2016-08-28 19:42:00 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
2016-09-10 21:53:20 +00:00
|
|
|
Helical Antenna Tutorial
|
2016-08-28 19:42:00 +00:00
|
|
|
|
|
|
|
Tested with
|
2023-01-02 11:32:28 +00:00
|
|
|
- python 3.10
|
|
|
|
- openEMS v0.0.35+
|
2016-08-28 19:42:00 +00:00
|
|
|
|
2023-01-02 11:32:28 +00:00
|
|
|
(c) 2015-2023 Thorsten Liebig <thorsten.liebig@gmx.de>
|
2016-08-28 19:42:00 +00:00
|
|
|
|
|
|
|
"""
|
|
|
|
|
2016-09-10 21:53:20 +00:00
|
|
|
### Import Libraries
|
2016-08-28 19:42:00 +00:00
|
|
|
import os, tempfile
|
|
|
|
from pylab import *
|
|
|
|
|
|
|
|
from CSXCAD import CSXCAD
|
|
|
|
|
2016-09-10 21:53:20 +00:00
|
|
|
from openEMS import openEMS
|
2016-08-28 19:42:00 +00:00
|
|
|
from openEMS.physical_constants import *
|
|
|
|
|
|
|
|
|
2016-09-10 21:53:20 +00:00
|
|
|
### Setup the simulation
|
|
|
|
Sim_Path = os.path.join(tempfile.gettempdir(), 'Helical_Ant')
|
2016-08-28 19:42:00 +00:00
|
|
|
post_proc_only = False
|
|
|
|
|
|
|
|
unit = 1e-3 # all length in mm
|
|
|
|
|
|
|
|
f0 = 2.4e9 # center frequency, frequency of interest!
|
|
|
|
lambda0 = round(C0/f0/unit) # wavelength in mm
|
|
|
|
fc = 0.5e9 # 20 dB corner frequency
|
|
|
|
|
|
|
|
Helix_radius = 20 # --> diameter is ~ lambda/pi
|
|
|
|
Helix_turns = 10 # --> expected gain is G ~ 4 * 10 = 40 (16dBi)
|
|
|
|
Helix_pitch = 30 # --> pitch is ~ lambda/4
|
|
|
|
Helix_mesh_res = 3
|
|
|
|
|
|
|
|
gnd_radius = lambda0/2
|
|
|
|
|
|
|
|
# feeding
|
|
|
|
feed_heigth = 3
|
|
|
|
feed_R = 120 #feed impedance
|
|
|
|
|
|
|
|
# size of the simulation box
|
|
|
|
SimBox = array([1, 1, 1.5])*2.0*lambda0
|
|
|
|
|
2016-09-10 21:53:20 +00:00
|
|
|
### Setup FDTD parameter & excitation function
|
2016-08-28 19:42:00 +00:00
|
|
|
FDTD = openEMS(EndCriteria=1e-4)
|
|
|
|
FDTD.SetGaussExcite( f0, fc )
|
|
|
|
FDTD.SetBoundaryCond( ['MUR', 'MUR', 'MUR', 'MUR', 'MUR', 'PML_8'] )
|
|
|
|
|
2016-09-10 21:53:20 +00:00
|
|
|
### Setup Geometry & Mesh
|
2016-08-28 19:42:00 +00:00
|
|
|
CSX = CSXCAD.ContinuousStructure()
|
|
|
|
FDTD.SetCSX(CSX)
|
|
|
|
mesh = CSX.GetGrid()
|
|
|
|
mesh.SetDeltaUnit(unit)
|
|
|
|
|
|
|
|
max_res = floor(C0 / (f0+fc) / unit / 20) # cell size: lambda/20
|
|
|
|
|
|
|
|
# create helix mesh
|
|
|
|
mesh.AddLine('x', [-Helix_radius, 0, Helix_radius])
|
|
|
|
mesh.SmoothMeshLines('x', Helix_mesh_res)
|
|
|
|
# add the air-box
|
|
|
|
mesh.AddLine('x', [-SimBox[0]/2-gnd_radius, SimBox[0]/2+gnd_radius])
|
|
|
|
# create a smooth mesh between specified fixed mesh lines
|
|
|
|
mesh.SmoothMeshLines('x', max_res, ratio=1.4)
|
|
|
|
|
|
|
|
# copy x-mesh to y-direction
|
|
|
|
mesh.SetLines('y', mesh.GetLines('x'))
|
|
|
|
|
|
|
|
# create helix mesh in z-direction
|
|
|
|
mesh.AddLine('z', [0, feed_heigth, Helix_turns*Helix_pitch+feed_heigth])
|
|
|
|
mesh.SmoothMeshLines('z', Helix_mesh_res)
|
|
|
|
|
|
|
|
# add the air-box
|
|
|
|
mesh.AddLine('z', [-SimBox[2]/2, max(mesh.GetLines('z'))+SimBox[2]/2 ])
|
|
|
|
# create a smooth mesh between specified fixed mesh lines
|
|
|
|
mesh.SmoothMeshLines('z', max_res, ratio=1.4)
|
|
|
|
|
2016-09-10 21:53:20 +00:00
|
|
|
### Create the Geometry
|
|
|
|
## * Create the metal helix using the wire primitive.
|
|
|
|
## * Create a metal gorund plane as cylinder.
|
|
|
|
# create a perfect electric conductor (PEC)
|
|
|
|
helix_metal = CSX.AddMetal('helix' )
|
2016-08-28 19:42:00 +00:00
|
|
|
|
|
|
|
ang = linspace(0,2*pi,21)
|
|
|
|
coil_x = Helix_radius*cos(ang)
|
|
|
|
coil_y = Helix_radius*sin(ang)
|
|
|
|
coil_z = ang/2/pi*Helix_pitch
|
|
|
|
|
|
|
|
Helix_x=np.array([])
|
|
|
|
Helix_y=np.array([])
|
|
|
|
Helix_z=np.array([])
|
|
|
|
zpos = feed_heigth
|
|
|
|
for n in range(Helix_turns-1):
|
|
|
|
Helix_x = r_[Helix_x, coil_x]
|
|
|
|
Helix_y = r_[Helix_y, coil_y]
|
|
|
|
Helix_z = r_[Helix_z ,coil_z+zpos]
|
|
|
|
zpos = zpos + Helix_pitch
|
|
|
|
|
|
|
|
p = np.array([Helix_x, Helix_y, Helix_z])
|
2016-09-20 20:08:57 +00:00
|
|
|
helix_metal.AddCurve(p)
|
2016-08-28 19:42:00 +00:00
|
|
|
|
2016-09-10 21:53:20 +00:00
|
|
|
# create ground circular ground
|
2016-08-28 19:42:00 +00:00
|
|
|
gnd = CSX.AddMetal( 'gnd' ) # create a perfect electric conductor (PEC)
|
|
|
|
|
|
|
|
# add a box using cylindrical coordinates
|
|
|
|
start = [0, 0, -0.1]
|
|
|
|
stop = [0, 0, 0.1]
|
2016-09-20 20:08:57 +00:00
|
|
|
gnd.AddCylinder(start, stop, radius=gnd_radius)
|
2016-08-28 19:42:00 +00:00
|
|
|
|
2016-09-10 21:53:20 +00:00
|
|
|
# apply the excitation & resist as a current source
|
2016-08-28 19:42:00 +00:00
|
|
|
start = [Helix_radius, 0, 0]
|
|
|
|
stop = [Helix_radius, 0, feed_heigth]
|
|
|
|
port = FDTD.AddLumpedPort(1 ,feed_R, start, stop, 'z', 1.0, priority=5)
|
|
|
|
|
2016-09-10 21:53:20 +00:00
|
|
|
# nf2ff calc
|
2016-08-28 19:42:00 +00:00
|
|
|
nf2ff = FDTD.CreateNF2FFBox(opt_resolution=[lambda0/15]*3)
|
|
|
|
|
2016-09-10 21:53:20 +00:00
|
|
|
### Run the simulation
|
2016-08-28 19:42:00 +00:00
|
|
|
if 0: # debugging only
|
|
|
|
CSX_file = os.path.join(Sim_Path, 'helix.xml')
|
2016-09-06 21:12:59 +00:00
|
|
|
if not os.path.exists(Sim_Path):
|
|
|
|
os.mkdir(Sim_Path)
|
2016-08-28 19:42:00 +00:00
|
|
|
CSX.Write2XML(CSX_file)
|
2023-01-01 13:13:53 +00:00
|
|
|
from CSXCAD import AppCSXCAD_BIN
|
|
|
|
os.system(AppCSXCAD_BIN + ' "{}"'.format(CSX_file))
|
2016-08-28 19:42:00 +00:00
|
|
|
|
|
|
|
if not post_proc_only:
|
2023-01-02 11:32:28 +00:00
|
|
|
FDTD.Run(Sim_Path, cleanup=True)
|
2016-08-28 19:42:00 +00:00
|
|
|
|
2016-09-10 21:53:20 +00:00
|
|
|
### Postprocessing & plotting
|
2016-08-28 19:42:00 +00:00
|
|
|
freq = linspace( f0-fc, f0+fc, 501 )
|
|
|
|
port.CalcPort(Sim_Path, freq)
|
|
|
|
|
|
|
|
Zin = port.uf_tot / port.if_tot
|
|
|
|
s11 = port.uf_ref / port.uf_inc
|
|
|
|
|
2016-09-10 21:53:20 +00:00
|
|
|
## Plot the feed point impedance
|
2016-08-28 19:42:00 +00:00
|
|
|
figure()
|
|
|
|
plot( freq/1e6, real(Zin), 'k-', linewidth=2, label=r'$\Re(Z_{in})$' )
|
|
|
|
grid()
|
|
|
|
plot( freq/1e6, imag(Zin), 'r--', linewidth=2, label=r'$\Im(Z_{in})$' )
|
|
|
|
title( 'feed point impedance' )
|
|
|
|
xlabel( 'frequency (MHz)' )
|
|
|
|
ylabel( 'impedance ($\Omega$)' )
|
|
|
|
legend( )
|
|
|
|
|
2016-09-10 21:53:20 +00:00
|
|
|
## Plot reflection coefficient S11
|
2016-08-28 19:42:00 +00:00
|
|
|
figure()
|
|
|
|
plot( freq/1e6, 20*log10(abs(s11)), 'k-', linewidth=2 )
|
|
|
|
grid()
|
|
|
|
title( 'reflection coefficient $S_{11}$' )
|
|
|
|
xlabel( 'frequency (MHz)' )
|
|
|
|
ylabel( 'reflection coefficient $|S_{11}|$' )
|
|
|
|
|
2016-09-10 21:53:20 +00:00
|
|
|
### Create the NFFF contour
|
|
|
|
## * calculate the far field at phi=0 degrees and at phi=90 degrees
|
2016-08-28 19:42:00 +00:00
|
|
|
theta = arange(0.,180.,1.)
|
|
|
|
phi = arange(-180,180,2)
|
|
|
|
disp( 'calculating the 3D far field...' )
|
|
|
|
|
2016-09-06 21:12:59 +00:00
|
|
|
nf2ff_res = nf2ff.CalcNF2FF(Sim_Path, f0, theta, phi, read_cached=True, verbose=True )
|
2016-08-28 19:42:00 +00:00
|
|
|
|
2016-09-06 21:12:59 +00:00
|
|
|
Dmax_dB = 10*log10(nf2ff_res.Dmax[0])
|
|
|
|
E_norm = 20.0*log10(nf2ff_res.E_norm[0]/np.max(nf2ff_res.E_norm[0])) + 10*log10(nf2ff_res.Dmax[0])
|
2016-08-28 19:42:00 +00:00
|
|
|
|
|
|
|
theta_HPBW = theta[ np.where(squeeze(E_norm[:,phi==0])<Dmax_dB-3)[0][0] ]
|
2016-09-10 21:53:20 +00:00
|
|
|
|
|
|
|
## * Display power and directivity
|
2016-09-06 21:12:59 +00:00
|
|
|
print('radiated power: Prad = {} W'.format(nf2ff_res.Prad[0]))
|
2016-08-28 19:42:00 +00:00
|
|
|
print('directivity: Dmax = {} dBi'.format(Dmax_dB))
|
2016-09-06 21:12:59 +00:00
|
|
|
print('efficiency: nu_rad = {} %'.format(100*nf2ff_res.Prad[0]/interp(f0, freq, port.P_acc)))
|
2016-08-28 19:42:00 +00:00
|
|
|
print('theta_HPBW = {} °'.format(theta_HPBW))
|
|
|
|
|
2016-09-06 21:12:59 +00:00
|
|
|
E_norm = 20.0*log10(nf2ff_res.E_norm[0]/np.max(nf2ff_res.E_norm[0])) + 10*log10(nf2ff_res.Dmax[0])
|
|
|
|
E_CPRH = 20.0*log10(np.abs(nf2ff_res.E_cprh[0])/np.max(nf2ff_res.E_norm[0])) + 10*log10(nf2ff_res.Dmax[0])
|
|
|
|
E_CPLH = 20.0*log10(np.abs(nf2ff_res.E_cplh[0])/np.max(nf2ff_res.E_norm[0])) + 10*log10(nf2ff_res.Dmax[0])
|
2016-08-28 19:42:00 +00:00
|
|
|
|
2016-09-10 21:53:20 +00:00
|
|
|
## * Plot the pattern
|
2016-08-28 19:42:00 +00:00
|
|
|
figure()
|
|
|
|
plot(theta, E_norm[:,phi==0],'k-' , linewidth=2, label='$|E|$')
|
|
|
|
plot(theta, E_CPRH[:,phi==0],'g--', linewidth=2, label='$|E_{CPRH}|$')
|
|
|
|
plot(theta, E_CPLH[:,phi==0],'r-.', linewidth=2, label='$|E_{CPLH}|$')
|
|
|
|
grid()
|
|
|
|
xlabel('theta (deg)')
|
|
|
|
ylabel('directivity (dBi)')
|
2016-09-06 21:12:59 +00:00
|
|
|
title('Frequency: {} GHz'.format(nf2ff_res.freq[0]/1e9))
|
2016-08-28 19:42:00 +00:00
|
|
|
legend()
|
|
|
|
|
|
|
|
show()
|
|
|
|
|