ドルフ・チェビシェフアレー

モジュールのインポートや初期設定など

import mpmath # Python の任意精度浮動小数点演算パッケージ
import numpy as np  
from scipy.special import factorial
from scipy.signal import find_peaks
from scipy import optimize
import matplotlib.pyplot as plt
np.set_printoptions(precision=4)
CCC = 299.79
qq = 180.0/np.pi
import scienceplots
#Warning : As of version 2.0.0, you need to add import scienceplots before setting the style (plt.style.use('science')).
plt.style.use(['science', 'notebook'])
#plt.rcParams['font.family'] = 'Times New Roman' # font familyの設定
plt.rcParams['font.family'] = 'serif' # font familyの設定
plt.rcParams['mathtext.fontset'] = 'cm' # math fontの設定

ユーザ関数

フォーマット付き出力をするため,
def table(*args):
    n1, n2 = np.shape(args)
    for i in range(n2):
        [print(f'{args[j][i]:11.8f}',end=' ') for j in range(n1)]
        print("")
    return
階乗の計算は,
def hfactorial(n):
    k = 1
    for i in range(1,n+1):
        k = k*i
    k = np.array(k,dtype=np.float128)
    return k
$ n!/m!$ の計算は,
def hfactorial3(n,m): # n!/m!
    k = 1
    if n>m:
        for i in range(m+1,n+1):
            k = k*i
        k = np.array(k,dtype=np.float128)
    elif n<m:
        for i in range(n+1,m+1):
            k = k*i
        k = np.array(k,dtype=np.float128)
        k = 1.0/k
    return k
ドルフ・チェビシェフアレーの一般式よりアレー素子の励振振幅係数は,
def dolph_tschebyscheff_array(n,R_db):
    R = 10**(R_db/20)
    R2 = np.sqrt(R**2-1)
    nm1 = n-1
    x0 = ((R+R2)**(1/nm1)+(R-R2)**(1/nm1))/2
    if n == 1:
        epn = 2
    else:
        epn = 1
    if np.mod(n,2) == 0:
        M = n//2 
        an = np.zeros(M)
        aa = np.zeros((M+1,M+1),dtype=np.float128)
        for n in range(1,M+1):
            for q in range(n,M+1):
                aa[n,q] = hfactorial3(q+M-2,q+n-1)/hfactorial(q-n)/hfactorial(M-q) * (-1)**(M-q) * x0**(2*q-1) * (2*M-1)
            an[n-1] = np.sum(aa[n,:])
    else:
        M = (n-1)//2
        an = np.zeros(M+1)
        aa = np.zeros((M+2,M+2),dtype=np.float128)
        for n in range(1,M+2):
            for q in range(n,M+2):
                aa[n,q] = hfactorial3(q+M-2,q+n-2)/epn/hfactorial(q-n)/hfactorial(M-q+1) * (-1)**(M-q+1) * x0**(2*(q-1)) * 2*M
            an[n-1] = np.sum(aa[n,:])
    return an,aa
フーリエ級数展開は,
def afactor_fourier(n,an,u):
    f = 0
    if np.mod(n,2)==0: # 偶数
        for i0 in range(n//2):
            i = i0+1
            f = f+an[i0]*np.cos((2*i-1)/2*u)
        f = 2.0*f
    else: # 奇数
        for i0 in range((n-1)//2):
            i = i0+1
            f = f+an[i0+1]*np.cos(i*u)
        f = an[0]+2*f
    return f
パラメータ$u$と角度$\theta$との関係は,
def theta_to_u(th,dd,th0):
    akd = 2*np.pi*dd
    u = akd * (np.cos(th)-np.cos(th0))
    return u
ビーム幅を求めるための関数を定義して,
def func_bw2(u,n,an,bw):
    return np.abs(afactor_fourier(n,an,u))-bw

波源分布

アレー素子の励振振幅は,
im = 1 # ピーク値およびナル点のプロットの有無, =0:なし,=1:あり
n = 30 # 36 # N elements
dd = 0.435 # Element spacing normalized by wave-length [mm]
R_db = 25.0 # サイドローブレベル(>0) [dB]
if np.mod(n,2) == 0:
    ii = n//2 
else:
    ii = (n+1)//2
an2, aa = dolph_tschebyscheff_array(n,R_db)
print('an2:',an2)
an_ = an2/an2[-1]# エッジ素子の振幅で規格化
print('an_:',an_)
if np.mod(n,2)==0:
    an = an_/(2*np.sum(an_))# u=0での指向性関数で規格化(素子数が偶数の場合)
else:
    an = an_/(2*np.sum(an_)-an_[0])# u=0での指向性関数で規格化(素子数が奇数の場合)
print('an:',an)
an2: [1.6226 1.6055 1.5719 1.5224 1.4583 1.3812 1.2928 1.1951 1.0905 0.9811
 0.8693 0.7574 0.6476 0.5419 1.2452]
an_: [1.3031 1.2894 1.2624 1.2227 1.1712 1.1093 1.0382 0.9598 0.8758 0.7879
 0.6982 0.6083 0.5201 0.4352 1.    ]
an: [0.0456 0.0451 0.0442 0.0428 0.041  0.0388 0.0363 0.0336 0.0307 0.0276
 0.0244 0.0213 0.0182 0.0152 0.035 ]
アレー素子の励振振幅を図示すると,
fig = plt.figure() # グラフ領域の作成
ne = np.linspace(0,len(an)-1,len(an),dtype='int')
plt.plot(ne,an/np.max(an),'o--',color='tab:blue')# 振幅の最大値で規格化
plt.plot(-ne,an/np.max(an),'o--',color='tab:blue')
plt.xlabel(r'$i$-th elements')
plt.ylabel(r'Amplitude')
fig.tight_layout()
fig.savefig('array_elements_N'+str(n)+'_d'+str(dd)+'wl.pdf')
plt.show()

放射パターン

放射パターンを求めると
th0deg = 60 # Beam direction [deg]
akd = 2*np.pi*dd
th0 = th0deg*np.pi/180
n_th = 180001
th = np.linspace(0, np.pi, n_th)
thdeg = th*qq
u = akd * (np.cos(th)-np.cos(th0))
nu = np.arange(n_th)
uoverpi = u/np.pi
nu_th0 = np.int64(np.round((n_th-1)*th0deg/180.0))
f = afactor_fourier(n,an,u)
p = 20.0*np.log10(np.abs(f))
ビーム幅を求めるため,
bw = np.sqrt(0.5)
fbw = func_bw2(u,n,an,bw)
nu_zero = nu[fbw==0.0]
fbw2 = fbw[:-1]*fbw[1:]
nu_zero = np.append(nu_zero,nu[:-1][fbw2<0.0])

if len(nu_zero[nu_zero<nu_th0])==0:
    nu_zero_m = -1
else:
    nu_zero_m = nu_zero[nu_zero<nu_th0][-1]
    
if len(nu_zero[nu_zero>nu_th0])==0:
    nu_zero_p = -1
else:
    nu_zero_p = nu_zero[nu_zero>nu_th0][0]
    
th_bw3_p, th_bw3_m = 0.0, 0.0
if nu_zero_p!=-1:
    up1 = np.min([u[nu_zero_p],u[nu_zero_p+1]])
    up2 = np.max([u[nu_zero_p],u[nu_zero_p+1]])
    u_zero_p = optimize.brentq(func_bw2, up1,up2, args=(n,an,bw))
#    u_zero_p = optimize.newton(func_bw2, up1, args=(n,an,bw))
    th_bw3_p = np.arccos(u_zero_p/akd +np.cos(th0))
if nu_zero_m!=-1:
    um1 = np.min([u[nu_zero_m],u[nu_zero_m+1]])   
    um2 = np.max([u[nu_zero_m],u[nu_zero_m+1]])
    u_zero_m = optimize.brentq(func_bw2, um1,um2, args=(n,an,bw))
#    u_zero_m = optimize.newton(func_bw2, um1, args=(n,an,bw))
    th_bw3_m = np.arccos(u_zero_m/akd +np.cos(th0))

if (nu_zero_p!=-1)&(nu_zero_m==-1):
    if th0deg<90.0:
        bw3_deg = 2*np.abs(th_bw3_p)*qq
    else:
        bw3_deg = 2*(180.0-np.abs(th_bw3_p)*qq)
elif (nu_zero_p==-1)&(nu_zero_m!=-1):
    if th0deg<90.0:
        bw3_deg = 2*np.abs(th_bw3_m)*qq
    else:
        bw3_deg = 2*(180.0-np.abs(th_bw3_m)*qq)
elif (nu_zero_p!=-1)&(nu_zero_m!=-1):
    bw3_deg = np.abs(th_bw3_m-th_bw3_p)*qq # 3-dBビーム幅
else:
    bw3_deg = 0.0
また,放射パターンのピーク値は,
p_ = np.where(p<-200.0, -800.0, p)# ピーク値
locs, _ = find_peaks(p_)
pp = p_[locs][p_[locs]<-0.0001]
peak_sl = 0.0
if len(pp)!=0:
    peak_sl = np.max(pp)
print(f"{'u/pi[rad]':>10s} {'th[deg]':>10s} {'f(u)':>10s} {'p(th)[dB]':>10s}")
for i in locs:
    print(f"{uoverpi[i]:10.5f} {thdeg[i]:10.5f} {f[i]:10.5f} {p[i]:10.5f}")
 u/pi[rad]    th[deg]       f(u)  p(th)[dB]
   0.42006   10.63300    0.05623  -25.00000
   0.35272   25.12000   -0.05623  -25.00000
   0.28605   34.02500    0.05623  -25.00000
   0.22069   41.09100   -0.05623  -25.00000
   0.15825   47.00800    0.05623  -25.00000
   0.10414   51.70600   -0.05623  -25.00000
  -0.00000   60.00000    1.00000    0.00000
  -0.10414   67.64800   -0.05623  -25.00000
  -0.15824   71.45100    0.05623  -25.00000
  -0.22068   75.73900   -0.05623  -25.00000
  -0.28605   80.14200    0.05623  -25.00000
  -0.35272   84.57300   -0.05623  -25.00000
  -0.42006   89.01600    0.05623  -25.00000
  -0.48779   93.47900   -0.05623  -25.00000
  -0.55578   97.98000    0.05623  -25.00000
  -0.62394  102.54300   -0.05623  -25.00000
  -0.69219  107.19500    0.05623  -25.00000
  -0.76053  111.97300   -0.05623  -25.00000
  -0.82892  116.92200    0.05623  -25.00000
  -0.89734  122.10200   -0.05623  -25.00000
  -0.96578  127.59600    0.05623  -25.00000
  -1.03422  133.53200   -0.05623  -25.00000
  -1.10266  140.12300    0.05623  -25.00000
  -1.17108  147.78700   -0.05623  -25.00000
  -1.23947  157.62000    0.05623  -25.00000
放射パターンのヌル点は,
locs_null, _ = find_peaks(-p_) # ヌル点
print(f"{'u/pi[rad]':>10s} {'th[deg]':>10s} {'f(u)':>10s} {'p(th)[dB]':>10s}")
for i in locs_null:
    print(f"{uoverpi[i]:10.5f} {thdeg[i]:10.5f} {f[i]:10.5f} {p[i]:10.5f}")
 u/pi[rad]    th[deg]       f(u)  p(th)[dB]
   0.38633   19.25600    0.00000 -107.14793
   0.31927   29.89100    0.00001 -101.11248
   0.25314   37.72400   -0.00000 -117.37981
   0.18891   44.18100    0.00001 -103.15207
   0.12947   49.54800    0.00001 -101.28047
   0.08542   53.26000   -0.00002  -92.65333
  -0.08542   66.30800    0.00000 -111.69691
  -0.12947   69.44000    0.00001  -98.53202
  -0.18891   73.56900    0.00001  -98.14287
  -0.25314   77.93400   -0.00001  -96.76007
  -0.31927   82.35600   -0.00001 -101.64076
  -0.38633   86.79300    0.00001 -100.90822
  -0.45389   91.24400    0.00001  -99.32778
  -0.52177   95.72400    0.00001 -103.89143
  -0.58984  100.25200    0.00001 -101.10842
  -0.65806  104.85600    0.00002  -95.89952
  -0.72636  109.56600   -0.00000 -106.37597
  -0.79472  114.42300    0.00000 -118.64153
  -0.86312  119.47800    0.00002  -95.45136
  -0.93156  124.80300    0.00001 -104.71929
  -1.00000  130.49800    0.00001 -101.41778
  -1.06844  136.72700   -0.00000 -115.61523
  -1.13688  143.78000   -0.00000 -121.49817
  -1.20528  152.29800   -0.00001 -101.53798
  -1.27365  164.57100   -0.00000 -116.77346
放射パターンを図示すると,
fig = plt.figure() # グラフ領域の作成
plt.ion()
plt.subplot(2,1,1)
if im == 1:
    plt.plot([0,0],[-1,1],'--',color='gray',alpha=0.5)
    plt.plot([-4,4],[0,0],'--',color='gray',alpha=0.5)
plt.plot(uoverpi,f)
if im == 1:
    plt.plot(uoverpi[locs],f[locs],'.')
    plt.plot(uoverpi[locs_null],np.zeros(len(locs_null)),'.')
plt.axis([-4,4,-1,1])
plt.xlabel(r'$u/\pi = 2d ( \cos \theta - \cos \theta_0)/\lambda$')
plt.ylabel('Array factor $f(u)$')
plt.text(0.8,-0.3,'$N=$'+str(n))
plt.text(0.8,-0.55,'Element spacing: $d = $'+str(dd)+r'$\lambda$')
plt.text(0.8,-0.8,r'Beam direction: $\theta_0 = $'+str(th0deg)+r'$^\circ$')

plt.subplot(2,1,2)
if im == 1:
    plt.plot(np.ones(2)*th0deg,[-50,0],'--',color='gray',alpha=0.5)
    plt.plot([0,180],np.ones(2)*(-3),'--',color='gray',alpha=0.5)
    if bw3_deg!=0.0:
        plt.plot(np.ones(2)*th_bw3_p*qq,[-50,0],'--',color='gray',alpha=0.5)
        plt.plot(np.ones(2)*th_bw3_m*qq,[-50,0],'--',color='gray',alpha=0.5)
plt.plot(thdeg,p)
if im == 1:
    plt.plot(thdeg[locs],p[locs],'o')
    plt.plot(thdeg[locs_null],np.ones(len(locs_null))*-40,'o')
plt.axis([0, 180, -40, 0])
plt.xlabel(r'Angle $\theta$ [deg]')
plt.ylabel(r'$20 \log_{10} |f(\theta)|$ [dB]')
plt.text(th0deg-7,2.5,r'$\theta_0 = $'+str(th0deg)+r'$^\circ$')
if bw3_deg!=0.0:
    plt.text(120,-7,f'3-dB beamwidth $={bw3_deg:.1f}^\circ$')
if (peak_sl>-80.0)&(peak_sl<0.0):
    plt.text(35,-10.0,f'Peak sidelobe level $={peak_sl:.2f}$ dB')
plt.xticks(np.linspace(0,180,5))

fig.tight_layout()
fig.savefig('array_factor_N'+str(n)+'_beam'+str(th0deg)+'deg_d'+str(dd)+'wl_im'+str(im)+'.pdf')
plt.show()

計算値の一部を出力すると,
print(f"{'u/pi[rad]':>10s} {'th[deg]':>10s}"\
        f"{'f(u)':>10s} {'f(th)[dB]':>10s}")
for i in range(0,len(th),5000):
    print(f"{uoverpi[i]:10.5f} {thdeg[i]:10.5f}"\
            f"{f[i]:10.5f} {p[i]:10.5f}")
 u/pi[rad]    th[deg]      f(u)  f(th)[dB]
   0.43500    0.00000   0.04322  -27.28589
   0.43169    5.00000   0.04822  -26.33520
   0.42178   10.00000   0.05605  -25.02787
   0.40536   15.00000   0.04358  -27.21403
   0.38253   20.00000  -0.00990  -40.08801
   0.35349   25.00000  -0.05620  -25.00568
   0.31844   30.00000   0.00220  -53.16318
   0.27766   35.00000   0.05182  -25.70974
   0.23146   40.00000  -0.04868  -26.25334
   0.18018   45.00000   0.02397  -32.40699
   0.12423   50.00000  -0.01682  -35.48172
   0.06401   55.00000   0.22208  -13.06962
  -0.00000   60.00000   1.00000    0.00000
  -0.06732   65.00000   0.17736  -15.02268
  -0.13744   70.00000   0.02445  -32.23370
  -0.20983   75.00000  -0.04845  -26.29341
  -0.28393   80.00000   0.05595  -25.04430
  -0.35917   85.00000  -0.05368  -25.40328
  -0.43500   90.00000   0.04322  -27.28589
  -0.51083   95.00000  -0.02724  -31.29471
  -0.58607  100.00000   0.00972  -40.24238
  -0.66017  105.00000   0.00548  -45.23199
  -0.73256  110.00000  -0.01582  -36.01650
  -0.80268  115.00000   0.02011  -33.93360
  -0.87000  120.00000  -0.01746  -35.15934
  -0.93401  125.00000   0.00633  -43.97869
  -0.99423  130.00000   0.01473  -36.63520
  -1.05018  135.00000  -0.04181  -27.57491
  -1.10146  140.00000   0.05615  -25.01327
  -1.14766  145.00000  -0.02673  -31.46098
  -1.18844  150.00000  -0.03929  -28.11476
  -1.22349  155.00000   0.04174  -27.58897
  -1.25253  160.00000   0.04640  -26.66929
  -1.27536  165.00000  -0.00442  -47.09860
  -1.29178  170.00000  -0.04165  -27.60864
  -1.30169  175.00000  -0.05402  -25.34826
  -1.30500  180.00000  -0.05577  -25.07250