Assume a plane wave propagating in the +z direction. It's phasor is
$$\begin{aligned} \\ \vec{E} = \vec{E}(z) &= (E_x e^{j\delta_x} \hat{x} + E_y e^{j\delta_y} \hat{y})e^{-jkz} \\ &= E_x e^{j\delta_x}(\hat{x} + \alpha \hat{y})e^{-jkz} \\ \end{aligned}$$with
$$\begin{aligned} \\ \alpha &= \frac{E_y}{E_x} e^{j(\delta_y - \delta_x)} \\ &= \tan(\psi)e^{j\delta} \\ \end{aligned}$$and
$$\begin{aligned} \\ &\delta = \delta_y - \delta_x \\ &\tan(\psi) = \frac{E_y}{E_x}. \\ \end{aligned}$$Note that $E_x$ and $E_y$ are real and $\geq 0$. The range of $\psi$ is $0 \leq \psi \leq \pi/2$. Also note that $\delta$ is simply the phase difference between the y and x components of the electric field. $\alpha$ is therefore a complex number with magnitude $E_y / E_x = \tan(\psi)$ and phase $\delta$. The range of $\delta$ is $-\pi < \delta \leq \pi$.
If we are only interested in the polarization state, we can normalize the phasor. First we note that
$$\begin{aligned} \\ |\vec{E}| &= \sqrt{\vec{E} \cdot \vec{E}^*} \\ &= \sqrt{E_x e^{j\delta_x}(\hat{x} + \tan(\psi)e^{j\delta}\hat{y})e^{-jkz} \cdot E_x e^{-j\delta_x}(\hat{x} + \tan(\psi)e^{-j\delta} \hat{y})e^{jkz}} \\ &= E_x \sqrt{1 + \tan^2(\psi)} \\ &= E_x \sqrt{\sec^2(\psi)} \\ &= E_x \sec(\psi) \\ &= \frac{E_x}{\cos(\psi)} \\ \end{aligned}$$With no loss of generality let's set $\delta_x = 0$, which corresponds to shifting the origin of our coordinate system. The normalized phasor then becomes
$$\begin{aligned} \\ \vec{E}_n &= \frac{\vec{E}}{|\vec{E}|} \\ &= \frac{\cos(\psi)\vec{E}}{E_x} \\ &= (\hat{x}\cos(\psi) + \hat{y}\alpha\cos(\psi) )e^{-jkz} \\ \end{aligned}$$Note that
$$\begin{aligned} \\ \alpha\cos(\psi) &= \tan(\psi)e^{j\delta}\cos(\psi) \\ &= \sin(\psi)e^{j\delta} \\ \end{aligned}$$The normalized complex amplitude vector is therefore
$$\vec{E}_n = (\hat{x}\cos(\psi) + \hat{y}\sin(\psi)e^{j\delta} )e^{-jkz}$$We now calculate the (normalized) real electric field:
$$\begin{aligned} \\ \vec{\mathscr{E}}(z,t) &= \Re(\vec{E}_n(z) e^{j\omega t} ) \\ &= \hat{x}\cos(\psi)\cos(\omega t - k z) + \hat{y}\sin(\psi)\cos(\omega t - k z + \delta). \\ \end{aligned}$$To visualize the polarization ellipse, we choose the $z=0$ plane:
$$\begin{aligned} \\ \vec{\mathscr{E}}(0,t) &= \hat{x}\cos(\psi)\cos(\omega t) + \hat{y}\sin(\psi)\cos(\omega t + \delta) \\ &= \hat{x}\cos(\psi)\cos(2\pi \frac{t}{T}) + \hat{y}\sin(\psi)\cos(2\pi \frac{t}{T} + \delta) \\ \end{aligned}$$where we have used
$$\begin{aligned} \\ \omega t &= 2\pi f t \\ &= 2\pi \frac{t}{T} \\ \end{aligned}$$and $T = 1/f$.
In [8]:
#from pyqtgraph import QtGui, QtCore
#import pyqtgraph as pg
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from IPython.html.widgets import interact, fixed
from matplotlib import animation
from JSAnimation import IPython_display
In [4]:
%%javascript
IPython.load_extensions('calico-document-tools');
In [3]:
def normalized_electricfield_in_xyplane(t_norm,delta_rad,psi_rad):
x_component = np.cos(psi_rad) * np.cos(2*np.pi*t_norm)
y_component = np.sin(psi_rad) * np.cos(2*np.pi*t_norm + delta_rad)
#print ' ', x_component, y_component
return (x_component, y_component)
# test normalized_electricfield_in_xyplane
for index,t_normalized in enumerate(np.linspace(0,1,5)):
temp = normalized_electricfield_in_xyplane(t_normalized,np.radians(180),np.radians(45))
print index, t_normalized, 'x =', temp[0], 'y =', temp[1]
In [5]:
def approx_tangent_to_curve(index,curve_x,curve_y):
maxindex = len(curve_x) - 1
if index < maxindex:
i1 = index+1
i2 = index
elif index == maxindex: # handle end-of-array index
i1 = index
i2 = index-1
else:
print ' OUT OF RANGE INDEX in approx_tangent_to_curve():'
print ' %d is greater than the index of the end of the array, which is %d' % (index, maxindex)
return
#print index, maxindex, i1, i2
dx = (curve_x[i1] - curve_x[i2]) / 100.0
dy = (curve_y[i1] - curve_y[i2]) / 100.0
return dx, dy
In [7]:
plotlimits = 1.1
npts_in_ellipse = 101
indexforarrowhead = 25
offsetindex = 2
arrowheadwidth = 0.08
arrowheadlength = 0.16
def plot_normalized_polarization(delta_deg,psi_deg):
t_normalized = np.linspace(0,1,npts_in_ellipse)
efield2 = normalized_electricfield_in_xyplane(t_normalized,np.radians(delta_deg),np.radians(psi_deg))
dx, dy = approx_tangent_to_curve(indexforarrowhead,efield2[0],efield2[1])
fig = plt.figure()
ax = plt.axes(xlim=(-plotlimits, plotlimits), ylim=(-plotlimits,plotlimits))
ax.plot(efield2[0],efield2[1])
ax.set_aspect('equal')
ax.arrow(efield2[0][indexforarrowhead],efield2[1][indexforarrowhead],
dx, dy, head_width=arrowheadwidth, head_length=arrowheadlength)
if delta_deg % 180 == 0:
ax.arrow(efield2[0][indexforarrowhead-offsetindex],efield2[1][indexforarrowhead-offsetindex],
-dx, -dy, head_width=arrowheadwidth, head_length=arrowheadlength)
ax.set_xlabel('x')
ax.set_ylabel('y')
labelstring = '$\delta = %.1f\degree$\n' % delta_deg
labelstring += '$\psi = %.1f\degree$' % psi_deg
ax.text(-0.8, 0.6, labelstring, fontsize = '14')
interact(plot_normalized_polarization,delta_deg=(-180,180),psi_deg=(0,90));
In [53]:
delta_deg = 60
psi_deg = 45
temp_index = 0
plotlimits = 1.1
npts_in_ellipse = 51
arrowheadwidth = 0.08
arrowheadlength = 0.16
def arrowlengthandangle(x,y,arrowheadlength):
fulllength = np.sqrt(x**2 + y**2)
arrowlength_net = fulllength - arrowheadlength
theta = np.arctan2(y, x)
return arrowlength_net, theta
def make_2Dpolarization_animation():
# Set up more animation & plot parameters
numframes = npts_in_ellipse
plotfontsize = 14
t_normalized = np.linspace(0,1,numframes)
xx, yy = normalized_electricfield_in_xyplane(t_normalized,np.radians(delta_deg),np.radians(psi_deg))
fig = plt.figure(figsize=(8,8))
ax = plt.axes(xlim=(-plotlimits, plotlimits), ylim=(-plotlimits,plotlimits))
ax.plot(xx,yy)
ax.set_aspect('equal')
ax.set_xlabel('x', fontsize=plotfontsize)
ax.set_ylabel('y', fontsize=plotfontsize)
labelstring = '$\delta = %.1f\degree$\n' % delta_deg
labelstring += '$\psi = %.1f\degree$' % psi_deg
ax.text(-1.0, 0.9, labelstring, fontsize=plotfontsize+2)
arrowlength_net, theta = arrowlengthandangle(xx[temp_index],yy[temp_index],arrowheadlength)
'''a1 = plt.arrow(0.0, 0.0, arrowlength_net*np.cos(theta), arrowlength_net*np.sin(theta),
head_width=arrowheadwidth, head_length=arrowheadlength, fc="k", ec="k")'''
line, = ax.plot([], [], lw=2)
textfortime = ax.text(0.6, 0.9, '', fontsize=plotfontsize)
# initialization function: plot the background of each frame
def init():
line.set_data([], [])
#ax.add_patch(a1)
textfortime.set_text('')
return line, textfortime,
# animation function. This is called sequentially
def plot_efieldvector(i):
t_normalized = float(i)/numframes
'''tempv = calc_voltage_on_tline(t_normalized, v, loadreflcoeff, genreflcoeff, delta_voltages)'''
line.set_data([0,xx[i]], [0,yy[i]])
#a1.set_data(0,0,1,1)
text4 = 't = %.2f T' % t_normalized
textfortime.set_text(text4)
return line, textfortime,
# Create animation and return it
return animation.FuncAnimation(fig, plot_efieldvector, init_func=init, frames=numframes, interval=20, blit=True)
make_2Dpolarization_animation()
Out[53]:
In [ ]:
In [ ]:
In [24]:
numrows = 5
numcols = 5
delta_deg = np.linspace(-180,180,numcols)
psi_deg = np.linspace(90,0,numrows)
npts_in_ellipse = 101
indexforarrowhead = 25
offsetindex = 2
arrowheadwidth = 0.08
arrowheadlength = 0.16
xlimit = 1.2
ylimit = 1.2
t_normalized = np.linspace(0,1,npts_in_ellipse)
# row and column sharing
f, ax = plt.subplots(numrows, numcols, sharex='col', sharey='row',figsize=(12,12))
for i in range(numrows):
for j in range(numcols):
xx,yy = normalized_electricfield_in_xyplane(t_normalized,np.radians(delta_deg[j]),np.radians(psi_deg[i]))
ax[i][j].plot(xx,yy)
dx, dy = approx_tangent_to_curve(indexforarrowhead,xx,yy)
ax[i][j].arrow(xx[indexforarrowhead],yy[indexforarrowhead],
dx, dy, head_width=arrowheadwidth, head_length=arrowheadlength)
if delta_deg[j] % 180 == 0:
ax[i][j].arrow(xx[indexforarrowhead-offsetindex],yy[indexforarrowhead-offsetindex],
-dx, -dy, head_width=arrowheadwidth, head_length=arrowheadlength)
ax[0][0].set_title('test title')
# Set y limits
for i in range(numrows):
ax[i][0].set_ylim(-ylimit,ylimit)
ax[i][0].set_ylabel('$\psi = %.1f\degree$' % psi_deg[i], fontsize='18')
# Set x limits
for j in range(numcols):
ax[numrows-1][j].set_xlim(-xlimit,xlimit)
ax[numrows-1][j].set_xlabel('$\delta = %d\degree$' % delta_deg[j], fontsize='18')
# Fine-tune figure; make subplots close to each other.
f.subplots_adjust(hspace=0,wspace=0)
In [41]:
numrows = 13
numcols = 13
delta_deg = np.linspace(-180,180,numcols)
psi_deg = np.linspace(90,0,numrows)
npts_in_ellipse = 101
indexforarrowhead = 75
offsetindex = 2
arrowheadwidth = 0.16
arrowheadlength = 0.32
xlimit = 1.2
ylimit = 1.2
t_normalized = np.linspace(0,1,npts_in_ellipse)
# row and column sharing
f, ax = plt.subplots(numrows, numcols, sharex='col', sharey='row',figsize=(21,21))
for i in range(numrows):
for j in range(numcols):
xx,yy = normalized_electricfield_in_xyplane(t_normalized,np.radians(delta_deg[j]),np.radians(psi_deg[i]))
ax[i][j].plot(xx,yy)
if (delta_deg[j] >= 0) or (delta_deg[j] == -180):
index = indexforarrowhead
else:
index = (npts_in_ellipse-1) - indexforarrowhead
dx, dy = approx_tangent_to_curve(index,xx,yy)
if (delta_deg[j] in (-180,0,180)) or (psi_deg[i] in (0,90)):
pass
else:
ax[i][j].arrow(xx[index],yy[index],
dx, dy, head_width=arrowheadwidth, head_length=arrowheadlength)
'''if (delta_deg[j] % 180 == 0) or (psi_deg[i] == 0):
ax[i][j].arrow(xx[indexforarrowhead-offsetindex],yy[indexforarrowhead-offsetindex],
-dx, -dy, head_width=arrowheadwidth, head_length=arrowheadlength)'''
ax[0][0].set_title('Polarization as a Function of $\delta$ and $\psi$', fontsize='24',horizontalalignment='left')
# Set y limits
for i in range(numrows):
ax[i][0].set_ylim(-ylimit,ylimit)
ax[i][0].set_ylabel('$\psi = %.1f\degree$' % psi_deg[i], fontsize='18')
# Set x limits
for j in range(numcols):
ax[numrows-1][j].set_xlim(-xlimit,xlimit)
ax[numrows-1][j].set_xlabel('$\delta = %d\degree$' % delta_deg[j], fontsize='18')
# Fine-tune figure; make subplots close to each other.
f.subplots_adjust(hspace=0,wspace=0)
f.savefig('Polarization-large array of subplots.png')
In [ ]: