## SPDX-FileCopyrightText: Copyright (c) 2021-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.# SPDX-License-Identifier: Apache-2.0#"""Implements classes and methods related to antenna arrays"""importmitsubaasmiimportdrjitasdrimportmatplotlib.pyplotaspltfrommatplotlib.markersimportMarkerStylefrom.utilsimportrotation_matrixfrom.antenna_patternimportAntennaPattern,antenna_pattern_registry
[docs]classAntennaArray:# pylint: disable=line-too-longr""" Class implementing an antenna array An antenna array is composed of antennas which are placed at different positions. All antennas share the same antenna pattern, which can be single- or dual-polarized. :param antenna_pattern: Antenna pattern to be used across the array :param normalized_positions: Array of relative positions of each antenna with respect to the position of the radio device, normalized by the wavelength. Dual-polarized antennas are counted as a single antenna and share the same position. """def__init__(self,antenna_pattern:AntennaPattern,normalized_positions:mi.Point3f):self.antenna_pattern=antenna_patternself.normalized_positions=normalized_positions@propertydefantenna_pattern(self):""" Get/set the antenna pattern :type: :class:`~sionna.rt.AntennaPattern` """returnself._antenna_pattern@antenna_pattern.setterdefantenna_pattern(self,v):ifnotisinstance(v,AntennaPattern):raiseTypeError("`antenna_pattern` must be an instance of type"f" AntennaPattern, found type '{type(v)}'.")self._antenna_pattern=v
[docs]defpositions(self,wavelength:float)->mi.Point3f:""" Get the relative positions of all antennas (dual-polarized antennas are counted as a single antenna and share the same position). Positions are computed by scaling the normalized positions of antennas by the ``wavelength``. :param wavelength: Wavelength [m] :returns: Relative antenna positions :math:`(x,y,z)` [m] """returnself._normalized_positions*wavelength
@propertydefnormalized_positions(self):r""" Get/set array of relative normalized positions :math:`(x,y,z)` [:math:`\lambda`] of each antenna. Dual-polarized antennas are counted as a single antenna and share the same position. :type: :py:class:`mi.Point3f` """returnself._normalized_positions@normalized_positions.setterdefnormalized_positions(self,normalized_positions):normalized_positions=mi.Point3f(normalized_positions)self._normalized_positions=normalized_positions@propertydefnum_ant(self):""" Number of linearly polarized antennas in the array. Dual-polarized antennas are counted as two linearly polarized antennas. :type: :py:class:`int` """returndr.shape(self._normalized_positions)[1]\
*len(self.antenna_pattern.patterns)@propertydefarray_size(self):""" Number of antennas in the array. Dual-polarized antennas are counted as a single antenna. :type: :py:class:`int` """returndr.shape(self.normalized_positions)[1]
[docs]defrotate(self,wavelength:float,orientation:mi.Point3f)->mi.Point3f:r""" Computes the relative positions of all antennas rotated according to the ``orientation`` Dual-polarized antennas are counted as a single antenna and share the same position. Positions are computed by scaling the normalized positions of antennas by the ``wavelength`` and rotating by ``orientation``. :param wavelength: Wavelength [m] :param orientation: Orientation [rad] specified through three angles corresponding to a 3D rotation as defined in :eq:`rotation` :returns: Rotated relative antenna positions :math:`(x,y,z)` [m] """rot_mat=rotation_matrix(orientation)p=self.positions(wavelength)rot_p=rot_mat@preturnrot_p
[docs]classPlanarArray(AntennaArray):# pylint: disable=line-too-longr""" Class implementing a planar antenna array The antennas of a planar array are regularly spaced, located in the y-z plane, and numbered column-first from the top-left to bottom-right corner. :param num_rows: Number of rows :param num_cols: Number of columns :param vertical_spacing: Vertical antenna spacing [multiples of wavelength] :param horizontal_spacing: Horizontal antenna spacing [multiples of wavelength] :param pattern: Name of a registered antenna pattern factory method :list-registry:`sionna.rt.antenna_pattern.antenna_pattern_registry` Keyword Arguments ----------------- polarization : :py:class:`str` Name of a registered polarization :list-registry:`sionna.rt.antenna_pattern.polarization_registry` polarization_model : :py:class:`str` Name of a registered polarization model :list-registry:`sionna.rt.antenna_pattern.polarization_model_registry`. Defaults to "tr38901_2". ** : :py:class:`Any` Depending on the chosen antenna pattern, other keyword arguments must be provided. See the :ref:`Developer Guide <dev_custom_antenna_patterns>` for more details. Example ------- .. code-block:: python from sionna.rt import PlanarArray array = PlanarArray(num_rows=8, num_cols=4, pattern="tr38901", polarization="VH") array.show() .. figure:: ../figures/antenna_array.png :align: center :scale: 100% """def__init__(self,*,num_rows:int,num_cols:int,vertical_spacing:float=0.5,horizontal_spacing:float=0.5,pattern:str,**kwargs):# Create list of antennasarray_size=num_rows*num_colsantenna_pattern=antenna_pattern_registry.get(pattern)(**kwargs)# Compute antenna positionsd_v=vertical_spacingd_h=horizontal_spacingnormalized_positions=dr.zeros(mi.Point3f,array_size)ii,jj=dr.meshgrid(dr.arange(mi.UInt,num_rows),dr.arange(mi.UInt,num_cols))# Set Y-Z positionsnormalized_positions.y=d_h*jj-(num_cols-1)*d_h/2normalized_positions.z=-d_v*ii+(num_rows-1)*d_v/2# Positionssuper().__init__(antenna_pattern,normalized_positions)
[docs]defshow(self):r"""show() Visualizes the planar antenna array Antennas are depicted by markers that are annotated with the antenna number. The marker is not related to the polarization of an antenna. Output ------ : :class:`matplotlib.pyplot.Figure` Figure depicting the antenna array """positions=self.normalized_positionsfig=plt.figure()plt.plot(positions.y.numpy(),positions.z.numpy(),marker=MarkerStyle("+").get_marker(),markeredgecolor='red',markerfacecolor='red',markersize="10",linestyle="None",markeredgewidth="1")foriinrange(self.array_size):fig.axes[0].annotate(i+1,(positions.y[i],positions.z[i]))plt.xlabel(r"y ($\lambda$)")plt.ylabel(r"z ($\lambda)$")plt.title("Planar Array Layout")returnfig