Modeling arrays

In array signal processing, sensor arrays consists a group of sensors (acoustic, electromagnetic, etc.) arranged in special geometry patterns. They find wide applications in radar, sonar, audio and speech processing, geophysics, and communications. In doatools, we focus on their application in direction-of-arrival (DOA) estimation.

import doatools.model as model
# Create a 10-element ULA with inter-element spacing 0.5.
ula = model.UniformLinearArray(10, 0.5)

API references

class doatools.model.arrays.ArrayDesign(locations, name, perturbations=[], element=<doatools.model.array_elements.IsotropicScalarSensor object>)[source]

Bases: object

Base class for all array designs.

Arrays can be 1D, 2D, or 3D. Consider the standard cartesian coordinate system. We define 1D, 2D, and 3D arrays as follows:

  • 1D arrays are linear arrays along the x-axis.
  • 2D arrays are planar arrays lying within the xy-plane.
  • 3D arrays are not restricted, whose element can exist anywhere in the 3D space.

We store the element locations with an m x d array, where m denotes the number of elements (size) of the array.

  • For 1D arrays, d equals one, and the i-th row of the m x 1 array stores the x coordinate of the i-th element.
  • For 2D arrays, d equals two, and the i-th row of the m x 2 array stores the x and y coordinates of the i-th element.
  • For 3D arrays, d equals three, and the i-th row of the m x 3 array stores the x, y, and z coordinates of the i-th element.

While this class is generally intended for internal use. You can also use this class to create custom arrays. Just to make sure that you do not modify locations after creating the array.

Parameters:
  • locations – A list or ndarray specifying the element locations. For 1D arrays, locations can be either a 1D list/ndarray, or an m x 1 list/ndarray, where m is the number of elements. For 2D or 3D arrays, locations must be a 2D list/ndarray of shape m x d, where d is 2 or 3. If the input is an ndarray, it will not be copied and should not be changed after creating the array design.
  • name (str) – Name of the array design.
  • perturbations (list or dict) –

    If a list is given, it should be a list of ArrayPerturbation.

    If a dictionary is given, it should be a dictionary containing the perturbation definitions. Correspinding ArrayPerturbation will be created automatically. The keys should be among the following:

    • 'location_errors'
    • 'gain_errors' (relative, -0.2 means 0.8 * original gain)
    • 'phase_errors' (in radians)
    • 'mutual_coupling'

    The values in the dictionary are two-element tuples. The first element is an ndarray representing the parameters, and the second element is a boolean specifying whether these parameters are known in prior.

  • element (ArrayElement) – Array element (sensor) used in this array. Default value is an instance of IsotropicScalarSensor.

Notes

Array designs are generally not changed after creation. Because array design objects are passed around when computing steering matrices, weight functions, etc., having a mutable internal state leads to more complexities and potential unexpected results. Although the internal states are generally accessible in Python, please refrain from modifying them unless you are aware of the side effects.

name

Retrieves the name of this array.

size

Retrieves the number of elements in the array.

output_size

Retrieves the output size of the array.

In generate, the output size should be equal to size. However, for vector sensor arrays, the output size is greater than the array size.

element_locations

Retrieves the nominal element locations.

Returns:An M x d matrix, where M is the number of elements and d is the number of dimensions of the nominal array.
actual_element_locations

Retrieves the actual element locations, considering location errors.

Returns:An M x d matrix, where M is the number of elements and d is the maximum of the following two:
  1. number of dimensions of the nominal array;
  2. number of dimensions of the sensor location errors.
element

Retrieves the array element.

is_perturbed

Returns if the array contains perturbations.

ndim

Retrieves the number of dimensions of the nominal array.

The number of dimensions is defined as the number of columns of the ndarray storing the nominal array element locations. It does not reflect the number of dimensions of the minimal subspace in which the nominal array lies. For instance, if the element locations are given by [[0, 0], [1, 1], [2, 2]], ndim equals to 2 instead of 1, despite the fact that this array is a linear array.

Perturbations do not affect this value.

actual_ndim

Retrieves the number of dimensions of the array, considering location errors.

has_perturbation(ptype)[source]

Checks if the array has the given type of perturbation.

is_perturbation_known(ptype)[source]

Checks if the specified perturbation is known in prior.

get_perturbation_params(ptype)[source]

Retrieves the parameters for the specified perturbation type.

perturbations

Retrieves a list of all perturbations.

get_perturbed_copy(perturbations, new_name=None)[source]

Returns a copy of this array design but with the specified perturbations.

The specified perturbations will replace the existing ones.

Notes

The default implementation performs a shallow copy of all existing fields using :meth:~copy.copy. Override this method if special operations are required.

Parameters:
  • perturbations (list or dict) –

    If a list is given, it should be a list of ArrayPerturbation.

    If a dictionary is given, it should be a dictionary containing the perturbation definitions. Correspinding ArrayPerturbation will be created automatically. The keys should be among the following:

    • 'location_errors'
    • 'gain_errors' (relative, -0.2 means 0.8 * original gain)
    • 'phase_errors' (in radians)
    • 'mutual_coupling'

    The values in the dictionary are two-element tuples. The first element is an ndarray representing the parameters, and the second element is a boolean specifying whether these parameters are known in prior.

  • new_name (str) – An optional new name for the resulting array design. If not provided, the name of the original array design will be used.
get_perturbation_free_copy(new_name=None)[source]

Returns a perturbation-free copy of this array design.

Notes

The default implementation performs a shallow copy of all existing fields using :meth:~copy.copy. Override this method if special operations are required.

Parameters:new_name (str) – An optional new name for the resulting array design. If not provided, the name of the original array design will be used.
steering_matrix(sources, wavelength, compute_derivatives=False, perturbations='all', flatten=True)[source]

Creates the steering matrix for the given DOAs.

Given \(K\) sources, denoted by \(\mathbf{\theta}\), and \(M\) sensors whose the actual sensor locations (after considering locations errors if exist) are denoted by \(\mathbf{d}\), the steering matrix is calculated as

\[\mathbf{A} = \mathbf{C} (\mathbf{F}(\mathbf{\theta}, \mathbf{d}) \odot \mathbf{A}_0(\mathbf{\theta}, \mathbf{d})).\]

Here \(\odot\) denotes the Hadamard product.

  • \(\mathbf{A}_0\) is an \(M \times K\) matrix calculated from the phase delays between the sourcess, \(\mathbf{\theta}\), and the sensor locations, \(\mathbf{d}\):

    \[\mathbf{A}_0 = \begin{bmatrix} \mathbf{a}_0(\mathbf{\theta}_1, \mathbf{d}) & \mathbf{a}_0(\mathbf{\theta}_2, \mathbf{d}) & \cdots & \mathbf{a}_0(\mathbf{\theta}_K, \mathbf{d}) \end{bmatrix}.\]
  • \(\mathbf{F}\) is the spatial response matrix. For isotropic scalar sensors, \(\mathbf{F}\) is an \(M \times K\) matrix of ones. For vectors sensor arrays, each sensor’s output is a vector of size \(L\). Consequently, \(\mathbf{F}\) is an \(L \times M \times K\) tensor and the broadcasting rule applies when computing the element-wise multiplication between \(\mathbf{F}\) and \(\mathbf{A}_0\).

  • \(\mathbf{C}(\cdot)\) is a matrix function that applies other perturbations such as gain errors, phase errors, and mutual coupling.

When compute_derivatives is True, this method also computes the derivative matrices associated with the source location parameters. The \(k\)-th column of the steering matrix, \(\mathbf{A}\), should depend only on the \(k\)-th source. Consequently, \(\mathbf{A}\) can be expressed as

\[\mathbf{A} = \begin{bmatrix} \mathbf{a}(\mathbf{\theta}_1) & \mathbf{a}(\mathbf{\theta}_2) & \cdots & \mathbf{a}(\mathbf{\theta}_K) \end{bmatrix}.\]

Then the \(i\)-th derivative matrix is computed as

\[\dot{\mathbf{A}}_i = \begin{bmatrix} \frac{\partial \mathbf{a}(\mathbf{\theta}_1)}{\partial \theta_{1i}} & \frac{\partial \mathbf{a}(\mathbf{\theta}_2)}{\partial \theta_{2i}} & \cdots & \frac{\partial \mathbf{a}(\mathbf{\theta}_K)}{\partial \theta_{Ki}} \end{bmatrix},\]

where \(\theta_{ki}\) is the \(i\)-th parameter of the \(k\)-th source location.

The current implementation cannot compute the derivative matrices when the array element is non-isotropic or non-scalar.

Parameters:
  • sources (SourcePlacement) –

    An instance of SourcePlacement.

    1. 1D arrays are placed along the x-axis. 2D arrays are placed within the xy-plane.
    2. If you pass in 1D DOAs for an 2D or 3D array, these DOAs will be assumed to be within the xy-plane. The azimuth angles are calculated as \(\pi/2\) minus the original 1D DOA values (broadside -> azimuth). The elevation angles are set to zeros (within the xy-plane).
  • wavelength (float) – Wavelength of the carrier wave.
  • compute_derivatives (bool) – If set to True, also outputs the derivative matrices with respect to the DOAs. The k-th column of the i-th derivative matrix contains the derivatives of the k-th column of A with respect to the i-th parameter associated with the k-th DOA. The derivative matrices are used when computing the CRBs. Not always available.
  • perturbations (str) –

    Specifies which perturbations are considered when constructing the steering matrix:

    • 'all' - All perturbations are considered. This is the default value.
    • 'known' - Only known perturbations (we have prior knowledge of the perturbation parameters) are considered. This option is used by DOA estimators when the exact knowledge of these perturbations are known in prior.
    • 'none' - None of the perturbations are considered.
  • flatten (bool) – Specifies whether the output should be flattend to matrices. This option does not have any effect if the array element has a scalar output. For an array element of non-scalar outputs (e.g., a vector sensor), the resulting steering matrix is actually a \(L \times M \times K\) tensor, where] \(L\) is the output size of each array element, \(M\) is the array size, and \(K\) is the number of sources. Setting flatten to True will flatten the tensor into a \(LM \times K\) matrix. Default value is True.

Notes

The steering matrix calculation is bound to array designs. This is a generic implementation, which can be overridden for special types of arrays.

class doatools.model.arrays.GridBasedArrayDesign(indices, d0=None, name=None, bases=None, **kwargs)[source]

Bases: doatools.model.arrays.ArrayDesign

Base class for all grid-based array designs.

For grid based arrays, each elements is placed on a predefined grid. A \(d\)-dimensional grid in a \(k\)-dimensional space (\(d \leq k\)) is generated by \(d\) \(k\)-dimensional basis vectors: \(\mathbf{v}_1, \mathbf{v}_2, \ldots, \mathbf{v}_d\). The location of the \((i_1, i_2, \ldots, i_d)\)-th element is given by

\[i_1 \mathbf{v}_1 + i_2 \mathbf{v}_2 + \cdots + i_d \mathbf{v}_d.\]
Parameters:
  • indices (ndarray) – m x d matrix denoting the grid indices of each element. The input ndarray is not copied and should never be changed after creating this array design.
  • d0 (float) – Grid size (or base inter-element spacing). For 2D and 3D arrays, d0 can either be a scalar (if the base inter-element spacing remains the same along all axes), or a list-like object such that d0[i] specifies the base inter-element spacing along the i-th axis. When using d0 to specify the grid size, the grid is assumed to be aligned with the x-, y-, and z-axis.
  • bases (ndarray) – Grid bases. Each row represents a basis vector. Given a d-dimensional grid, the element with the grid index (i1, i2,...,id) is located at i1 * bases[0,:] + ... + id * bases[d-1, :]. When bases is specified, d0 will be ignored. Use bases instead of d0 if the underlying grid is not aligned with the x-, y-, and z-axis.
  • name (str) – Name of the array design.
  • **kwargs – Other keyword arguments supported by ArrayDesign.
d0

Retrieves the base inter-element spacing(s) along each grid axis.

You are not supposed to modified the returned array.

Returns:A 1D vector containing the inter-element spacings along each grid axis.
Return type:ndarray
bases

Retrieves the basis vectors for the grid.

You are not supposed to modify the returned array.

Returns:A matrix where each row respresents a basis vector.
Return type:ndarray
element_indices

Retrieves the element indices.

You are not supposed to modify the returned array.

class doatools.model.arrays.UniformLinearArray(n, d0, name=None, **kwargs)[source]

Bases: doatools.model.arrays.GridBasedArrayDesign

Creates an n-element uniform linear array (ULA).

The ULA is placed along the x-axis, whose the first sensor is placed at the origin.

Parameters:
  • n (int) – Number of elements.
  • d0 (float) – Fundamental inter-element spacing (usually smallest).
  • name (str) – Name of the array design.
  • **kwargs – Other keyword arguments supported by ArrayDesign.
class doatools.model.arrays.NestedArray(n1, n2, d0, name=None, **kwargs)[source]

Bases: doatools.model.arrays.GridBasedArrayDesign

Creates a 1D nested array.

Parameters:
  • n1 (int) – Parameter N1.
  • n2 (int) – Parameter N2.
  • d0 (float) – Fundamental inter-element spacing (usually smallest).
  • name (str) – Name of the array design.
  • **kwargs – Other keyword arguments supported by ArrayDesign.

References

[1] P. Pal and P. P. Vaidyanathan, “Nested arrays: A novel approach to array processing with enhanced degrees of freedom,” IEEE Transactions on Signal Processing, vol. 58, no. 8, pp. 4167-4181, Aug. 2010.

n1

Retrieves the parameter, N1, used when creating this nested array.

n2

Retrieves the parameter, N2, used when creating this nested array.

class doatools.model.arrays.CoPrimeArray(m, n, d0, mode='2m', name=None, **kwargs)[source]

Bases: doatools.model.arrays.GridBasedArrayDesign

Creates a 1D co-prime array.

Parameters:
  • m (int) – The smaller number in the co-prime pair.
  • n (int) – The larger number in the co-prime pair.
  • d0 (float) – Fundamental inter-element spacing (usually smallest).
  • mode (str) – Either 'm' or '2m'.
  • name (str) – Name of the array design.
  • **kwargs – Other keyword arguments supported by ArrayDesign.

References

[1] P. Pal and P. P. Vaidyanathan, “Coprime sampling and the music algorithm,” in 2011 Digital Signal Processing and Signal Processing Education Meeting (DSP/SPE), 2011, pp. 289-294.

coprime_pair

Retrieves the co-prime pair used when creating this co-prime array.

mode

Retrieves the mode used when creating this co-prime array.

class doatools.model.arrays.MinimumRedundancyLinearArray(n, d0, name=None, **kwargs)[source]

Bases: doatools.model.arrays.GridBasedArrayDesign

Creates an n-element minimum redundancy linear array (MRLA).

Parameters:
  • n (int) – Number of elements. Up to 20.
  • d0 (float) – Fundamental inter-element spacing (usually smallest).
  • name (str) – Name of the array design.
  • **kwargs – Other keyword arguments supported by ArrayDesign.

References

[1] M. Ishiguro, “Minimum redundancy linear arrays for a large number of antennas,” Radio Sci., vol. 15, no. 6, pp. 1163-1170, Nov. 1980.

[2] A. Moffet, “Minimum-redundancy linear arrays,” IEEE Transactions on Antennas and Propagation, vol. 16, no. 2, pp. 172-175, Mar. 1968.

class doatools.model.arrays.UniformCircularArray(n, r, name=None, **kwargs)[source]

Bases: doatools.model.arrays.ArrayDesign

Creates a uniform circular array (UCA).

The UCA is centered at the origin, in the xy-plane.

Parameters:
  • n (int) – Number of elements.
  • r (float) – Radius of the circle.
  • name (str) – Name of the array design.
  • **kwargs – Other keyword arguments supported by ArrayDesign.
radius

Retrieves the radius of the uniform circular array.

class doatools.model.arrays.UniformRectangularArray(m, n, d0, name=None, **kwargs)[source]

Bases: doatools.model.arrays.GridBasedArrayDesign

Creates an m x n uniform rectangular array (URA).

The URA is placed on the xy-plane, and the (0,0)-th sensor is placed at the origin.

Parameters:
  • m (int) – Number of elements along the x-axis.
  • n (int) – Number of elements along the y-axis.
  • d0 (float) – Fundamental inter-element spacing. Can be either a scalar or a two-element list-like object.
  • name (str) – Name of the array design.
  • **kwargs – Other keyword arguments supported by ArrayDesign.
shape

Retrieves the shape of this uniform rectangular array.