Modelling an antenna array

An antenna array can be used to direct radio waves in a particular direction by adjusting their number, geometrical arrangement, and relative amplitudes and phases (see e.g. S. J. Orfanidis, Electromagnetic Waves and Antennas, Rutgers University, 2016.)

Consider an array of $n$ isotropic antennas at positions, $\boldsymbol{d}_i$, evenly spaced by $d$ along the $x$-axis from the origin:

\begin{align*} \boldsymbol{d}_0 = 0, \boldsymbol{d}_1 = d\boldsymbol{\hat{x}}, \ldots,\boldsymbol{d}_{n-1} = (n-1)d\boldsymbol{\hat{x}}. \end{align*}

If a single antenna produces a radiation vector, $\boldsymbol{F}(\boldsymbol{k})$, where $\boldsymbol{k} = k\boldsymbol{\hat{r}} = (2\pi/\lambda)\boldsymbol{\hat{r}}$, the total radiation vector due to all $n$ antennas is

\begin{align*} \boldsymbol{F}_\mathrm{tot}(\boldsymbol{k}) = \sum_{j=0}^{n-1}w_j\mathrm{e}^{ji\boldsymbol{k}\cdot\boldsymbol{d}_j}\boldsymbol{F}(\boldsymbol{k}) = A(\boldsymbol{k})F(\boldsymbol{k}), \end{align*}

where $w_j$ is the feed coefficient of the $j$th antenna, representing its amplitude and phase, and $A(\boldsymbol{k})$ is known as the array factor. We can choose $w_0 = 1$ to specify the feed coefficients relative to the antenna at the origin. If we further choose to look only at the azimuthal ($\phi$) contribution to the radiation in the $xy$ plane, setting the polar angle $\theta=\pi/2$, we have: $$ A(\phi) = \sum_{j=0}^{n-1}w_j\mathrm{e}^{jikd\cos\phi}. $$

The relative radiation power pattern ("gain") is the square of this quantity. For two identical antennas, $$ g(\phi) = |A(\phi)|^2 = |w_0 + w_1\mathrm{e}^{ikd\cos\phi}|^2. $$ In the code below, the related quantity, the directive gain, $10\log_{10}(g/g_\mathrm{max})$, is plotted below as a function of $\phi$ for the two-antenna case on a polar plot for $d=\lambda$ and $w_0 =1, w_1 = -i$.

import numpy as np
import matplotlib.pyplot as plt

def gain(d, w):
    """Return the power as a function of azimuthal angle, phi."""
    phi = np.linspace(0, 2*np.pi, 1000)
    psi = 2*np.pi * d / lam * np.cos(phi)
    A = w[0] + w[1]*np.exp(1j*psi)
    g = np.abs(A)**2
    return phi, g

def get_directive_gain(g, minDdBi=-20):
    """Return the "directive gain" of the antenna array producing gain g."""
    DdBi = 10 * np.log10(g / np.max(g))
    return np.clip(DdBi, minDdBi, None)

# Wavelength, antenna spacing, feed coefficients.
lam = 1
d = lam
w = np.array([1, -1j])
# Calculate gain and directive gain; plot on a polar chart.
phi, g = gain(d, w)
DdBi = get_directive_gain(g)

plt.polar(phi, DdBi)
ax = plt.gca()
ax.set_rticks([-20, -15, -10, -5])
ax.set_rlabel_position(45)
plt.show()

Notes:

  • To better show the interesting region of the plot, where the power is highest, we "clip" the values less than minDdBi to that value.

  • To customize the plot we need the Axes object in the current plot context; this is returned by plt.gca() ("get current axes").

  • set_rticks sets the position of the radial tick marks.

  • set_rlabel_position defines the angular position of the radial ticks.

The directive gain of a two-antenna array with d = lambda, w0 = 1;w1 = -i

NumPy's broadcasting methods provide a natural way to extend this code to an arbitrary number of antennas; in the following example the figure method add_subplot is called with the argument projection='polar' and returns a corresponding Axes object, ax.

import numpy as np
import matplotlib.pyplot as plt

def gain(d, w):
    """Return the power as a function of azimuthal angle, phi."""
    phi = np.linspace(0, 2*np.pi, 1000)
    psi = 2*np.pi * d / lam * np.cos(phi)
    j = np.arange(len(w))
    A = np.sum(w[j] * np.exp(j * 1j * psi[:, None]), axis=1)
    g = np.abs(A)**2
    return phi, g

def get_directive_gain(g, minDdBi=-20):
    """Return the "directive gain" of the antenna array producing gain g."""
    DdBi = 10 * np.log10(g / np.max(g))
    return np.clip(DdBi, minDdBi, None)

# Wavelength, antenna spacing, feed coefficients.
lam = 1
d = lam / 2
w = np.array([1, -1, 1])
# Calculate gain and directive gain; plot on a polar chart.
phi, g = gain(d, w)
DdBi = get_directive_gain(g)

fig = plt.figure()
ax = fig.add_subplot(projection='polar')
ax.plot(phi, DdBi)
ax.set_rticks([-20, -15, -10, -5])
ax.set_rlabel_position(45)
plt.show()

The sum to calculate $A$ is over the terms in the array factor expression: adding an axis to thepsi array calculates this sum for each of the angular positions, $\phi$.

Note that with the projection already defined, we need ax.plot here, not ax.polar to plot the data.

The directive gain of a many-antenna array with d = lambda/2, w0 = 1;w1 = -1;w2 = 1