Формирование диаграмм направленности
Автор
import numpy as np
import cvxpy as cp
import matplotlib.pyplot as plt
DB_FLOOR = -80
def plot_pattern_cut(az_grid, el_grid, pat_db, el0=0.0, title=None):
idx = int(np.argmin(np.abs(el_grid - el0)))
plt.figure(figsize=(6,4.2))
plt.plot(az_grid, pat_db[idx, :])
plt.xlabel('Азимут'); plt.ylabel('Относительный уровень, дБ')
plt.title(title); plt.grid(True); plt.tight_layout(); plt.show()
def plot_pattern_2d(az_grid, el_grid, pat_db, db_floor=-60, title='Beam Pattern'):
pat_db = np.clip(pat_db, db_floor, 0)
AZ, EL = np.meshgrid(az_grid, el_grid)
plt.figure(figsize=(6.6,5.2))
cf = plt.contourf(AZ, EL, pat_db, levels=40)
plt.contour(AZ, EL, pat_db, levels=np.arange(db_floor, 1, 5), colors='k', linewidths=0.4, alpha=0.4)
plt.colorbar(cf, label='dB')
plt.xlabel('Azimuth (deg)'); plt.ylabel('Elevation (deg)')
plt.title(title); plt.tight_layout(); plt.show()
def plot_aperture(pos, r):
y, z = pos[:,1], pos[:,2]
plt.figure(figsize=(5,5))
plt.scatter(y, z, s=80)
circ = plt.Circle((0,0), r, fill=False, ls='--', lw=1.5)
plt.gca().add_artist(circ)
plt.gca().set_aspect('equal', 'box')
plt.xlabel('y (m)'); plt.ylabel('z (m)')
plt.grid(True, alpha=0.3)
plt.tight_layout(); plt.show()
def get_circular_planar_positions(r=3.0, delta=0.5, wavelength=1.0):
ys = np.arange(-r, r + 1e-9, delta)
zs = np.arange(-r, r + 1e-9, delta)
Y, Z = np.meshgrid(ys, zs, indexing='xy')
mask = (Y**2 + Z**2) <= r**2 + 1e-9
y = Y[mask].ravel()
z = Z[mask].ravel()
x = np.zeros_like(y)
pos = np.c_[x, y, z] / wavelength
return pos.astype(float), pos.shape[0]
def dir_unit_vector(az_deg, el_deg):
az = np.deg2rad(az_deg); el = np.deg2rad(el_deg)
c = np.cos(el)
return np.array([c*np.cos(az), c*np.sin(az), np.sin(el)])
def steering_vector(pos, az_deg, el_deg):
u = dir_unit_vector(az_deg, el_deg)
phase = 2*np.pi * (pos @ u)
return np.exp(1j * phase)
def steering_matrix(pos, az_list, el_list):
A = []
for az, el in zip(az_list, el_list):
A.append(steering_vector(pos, az, el).conj())
return np.vstack(A) if A else np.zeros((0, pos.shape[0]), dtype=complex)
def array_response_fast(pos, w, az_grid, el_grid):
az = np.deg2rad(np.asarray(az_grid))
el = np.deg2rad(np.asarray(el_grid))
ca, sa = np.cos(az), np.sin(az)
ce, se = np.cos(el), np.sin(el)
ux = np.outer(ce, ca)
uy = np.outer(ce, sa)
uz = np.outer(se, np.ones_like(az))
phase = 2*np.pi * (pos[:,0,None,None]*ux + pos[:,1,None,None]*uy + pos[:,2,None,None]*uz)
A = np.exp(1j*phase)
val = np.tensordot(np.conj(A), w, axes=(0,0))
pat = 20*np.log10(np.maximum(np.abs(val), 1e-12))
pat -= np.max(pat)
return pat
def make_side_region(az_span, el_span, step=0.5, exclude=set()):
azs = np.arange(az_span[0], az_span[1] + 1e-9, step)
els = np.arange(el_span[0], el_span[1] + 1e-9, step)
return [(az, el) for az in azs for el in els if (az, el) not in exclude]
def db2lin_amp(db): return 10**(db/20.0)
def synthesize_mask_l2(pos, main_dir, null_dirs=None, null_db=None,
side_dirs=None, side_db=None):
a_d = steering_vector(pos, *main_dir).conj()
A_i = steering_matrix(pos, *(zip(*null_dirs)) if null_dirs else ([], []))
A_s = steering_matrix(pos, *(zip(*side_dirs)) if side_dirs else ([], []))
N = pos.shape[0]
w = cp.Variable(N, complex=True)
cons = [a_d @ w == 1]
if A_i.size and null_db is not None:
r_i = np.atleast_1d(null_db)
r_i = np.full(A_i.shape[0], db2lin_amp(r_i[0])) if r_i.size==1 else db2lin_amp(r_i)
cons += [cp.abs(A_i @ w) <= r_i]
if A_s.size and side_db is not None:
r_s = np.atleast_1d(side_db)
r_s = np.full(A_s.shape[0], db2lin_amp(r_s[0])) if r_s.size==1 else db2lin_amp(r_s)
cons += [cp.abs(A_s @ w) <= r_s]
prob = cp.Problem(cp.Minimize(cp.norm(w, 2)), cons)
prob.solve(verbose=False)
if w.value is None: raise RuntimeError("Оптимизация не сошлась")
return np.asarray(w.value).ravel()
def normalize_weights(w, ref_idx=0):
w = w / (np.linalg.norm(w) + 1e-12)
w *= np.exp(-1j*np.angle(w[ref_idx]))
return w
def align_scale_phase(w_hat, w_true):
alpha = (np.vdot(w_hat, w_true) / (np.vdot(w_hat, w_hat) + 1e-12))
return alpha * w_hat
def db_to_uint8(pat_db, db_floor=DB_FLOOR):
pat = np.clip(pat_db, db_floor, 0.0)
img01 = (pat - db_floor) / (0.0 - db_floor)
return (img01 * 255.0).astype(np.uint8)