From adeecb9abd0f0e673ad80519b05b391e94f2dc6a Mon Sep 17 00:00:00 2001 From: Biljana Bojovic Date: Tue, 10 Jan 2023 15:41:57 +0100 Subject: [PATCH] antenna: Extend antenna array to support multiple ports and dual pol. of antenna elements antenna: Fix PhasedArrayAntennaModel Doxygen antenna: Fix UniformPlanarArray Doxygen antenna: Rename Get horizontal vertical number of elements in the port antenna: fix phased-array-model.cc antenna: Fix (remove 3gpp comment) antenna: Add units to GetPolSlant antenna: Fix IsPolDual -> IsDualPol, add function SetDualPol, Remove SetPolarization antenna: Remove GetNumPolarizations function to reduce the dependency between spectrum and antenna antenna: Simplify the code in GetBeamformingVector in PhasedArrayModel antenna: Rename GetPolAngle->GetPolSlant antenna: Fix GetBeamformingVector normalization description, add 3GPP reference antenna: Expand explanation of why is done the normalization of the BF weights in GetBeamformingVector antenna: Enable access to public Get and Set functions antenna: Refactor and move the code related to polarization to antenna model fix variable name in antenna antenna fix doxygen antenna: Doc. for dual-polarization and multiple ports in UniformPlannarArray remote trailing whitespaces antenna: Return back cos and sin optimization of pol slant angle antenna: Fix documentation regarding the polarization slant angle antenna: UniformPlannarArray Initialize cos/sin of -90 antenna: Change parameter names Remove new line antenna: Suggestions proposed by Eduardo Almeida antenna: Add a 3GPP reference for a multi-port antenna array model --- src/antenna/doc/source/antenna-design.rst | 29 +++- src/antenna/model/phased-array-model.cc | 41 +++--- src/antenna/model/phased-array-model.h | 147 ++++++++++++++++--- src/antenna/model/uniform-planar-array.cc | 169 ++++++++++++++++++++-- src/antenna/model/uniform-planar-array.h | 137 +++++++++++++++--- 5 files changed, 444 insertions(+), 79 deletions(-) diff --git a/src/antenna/doc/source/antenna-design.rst b/src/antenna/doc/source/antenna-design.rst index 0caeb6c6f..feebd635e 100644 --- a/src/antenna/doc/source/antenna-design.rst +++ b/src/antenna/doc/source/antenna-design.rst @@ -160,10 +160,10 @@ UniformPlanarArray The class UniformPlanarArray is a generic implementation of Uniform Planar Arrays (UPAs), supporting rectangular and linear regular lattices. -It loosely follows the implementation described in the 3GPP TR 38.901 [38901]_, -considering only a single a single panel, i.e., :math:`N_{g} = M_{g} = 1`. +It closely follows the implementation described in the 3GPP TR 38.901 [38901]_, +considering only a single panel, i.e., :math:`N_{g} = M_{g} = 1`. -By default, the array is orthogonal to the x-axis, pointing towards the positive +By default, the antenna array is orthogonal to the x-axis, pointing towards the positive direction, but the orientation can be changed through the attributes "BearingAngle", which adjusts the azimuth angle, and "DowntiltAngle", which adjusts the elevation angle. The slant angle is instead fixed and assumed to be 0. @@ -173,8 +173,24 @@ through the attributes "NumRows" and "NumColumns", while the spacing between the and vertical elements can be configured through the attributes "AntennaHorizontalSpacing" and "AntennaVerticalSpacing". -The polarization of each antenna element in the array is determined by the polarization -slant angle through the attribute "PolSlantAngle", as described in [38901]_ (i.e., :math:`{\zeta}`). +UniformPlannarArray supports the concept of antenna ports following the sub-array partition +model for TXRU virtualization, as described in Section 5.2.2 of 3GPP TR 36.897 [36897]_. +The number of antenna ports in vertical and horizontal directions can be configured through +the attributes "NumVerticalPorts" and "NumHorizontalPorts", respectively. For example, +if "NumRows" and "NumColumns" are configured to 2 and 4, and the number of +"NumVerticalPorts" and "NumHorizontalPorts" to 1 and 2, then the antenna elements belonging +to the first two columns of the antenna array will belong to the first antenna port, +and the third and the fourth columns will belong to the second antenna port. Note that +"NumRows" and "NumColumns" must be a multiple of "NumVerticalPorts" and "NumHorizontalPorts", +respectively. + +Whether the antenna is dual-polarized or not is configured through the attribute +"IsDualPolarized". In case the antenna array is dual polarized, the total number +of antenna elements is doubled and the two polarizations are overlapped in space. +The polarization slant angle of the antenna elements belonging to the first polarization +are configured through the attribute "PolSlantAngle"; while the antenna elements of +the second polarization have the polarization slant angle minus 90 degrees, +as described in [38901]_ (i.e., :math:`{\zeta}`). .. [Balanis] C.A. Balanis, "Antenna Theory - Analysis and Design", Wiley, 2nd Ed. @@ -193,3 +209,6 @@ slant angle through the attribute "PolSlantAngle", as described in [38901]_ (i.e .. [38901] 3GPP. 2018. TR 38.901, Study on channel model for frequencies from 0.5 to 100 GHz, V15.0.0. (2018-06). .. [Mailloux] Robert J. Mailloux, "Phased Array Antenna Handbook", Artech House, 2nd Ed. + +.. [TR36897] 3GPP. 2015. TR 36.897. Study on elevation beamforming / Full-Dimension (FD) + Multiple Input Multiple Output (MIMO) for LTE. V13.0.0. (2015-06) diff --git a/src/antenna/model/phased-array-model.cc b/src/antenna/model/phased-array-model.cc index 81477811d..05f117b49 100644 --- a/src/antenna/model/phased-array-model.cc +++ b/src/antenna/model/phased-array-model.cc @@ -34,28 +34,6 @@ NS_LOG_COMPONENT_DEFINE("PhasedArrayModel"); NS_OBJECT_ENSURE_REGISTERED(PhasedArrayModel); -std::ostream& -operator<<(std::ostream& os, const PhasedArrayModel::ComplexVector& cv) -{ - size_t N = cv.GetSize(); - - // empty - if (N == 0) - { - os << "[]"; - return os; - } - - // non-empty - os << "["; - for (std::size_t i = 0; i < N - 1; ++i) - { - os << cv[i] << ", "; - } - os << cv[N - 1] << "]"; - return os; -} - PhasedArrayModel::PhasedArrayModel() : m_isBfVectorValid{false} { @@ -101,13 +79,30 @@ PhasedArrayModel::GetBeamformingVector() const return m_beamformingVector; } +const PhasedArrayModel::ComplexVector& +PhasedArrayModel::GetBeamformingVectorRef() const +{ + NS_LOG_FUNCTION(this); + NS_ASSERT_MSG(m_isBfVectorValid, + "The beamforming vector should be Set before it's Get, and should refer to the " + "current array configuration"); + return m_beamformingVector; +} + PhasedArrayModel::ComplexVector PhasedArrayModel::GetBeamformingVector(Angles a) const { NS_LOG_FUNCTION(this << a); ComplexVector beamformingVector = GetSteeringVector(a); - double normRes = norm(beamformingVector); + // The normalization takes into account the total number of ports as only a + // portion (K,L) of beam weights associated with a specific port are non-zero. + // See 3GPP Section 5.2.2 36.897. This normalization corresponds to + // a sub-array partition model (which is different from the full-connection + // model). Note that the total number of ports used to perform normalization + // is the ratio between the total number of antenna elements and the + // number of antenna elements per port. + double normRes = norm(beamformingVector) / sqrt(GetNumPorts()); for (size_t i = 0; i < GetNumberOfElements(); i++) { diff --git a/src/antenna/model/phased-array-model.h b/src/antenna/model/phased-array-model.h index e1129a80a..c26bbc104 100644 --- a/src/antenna/model/phased-array-model.h +++ b/src/antenna/model/phased-array-model.h @@ -73,17 +73,7 @@ class PhasedArrayModel : public Object } /** - * Returns the horizontal and vertical components of the antenna element field - * pattern at the specified direction. Single polarization is considered. - * \param a the angle indicating the interested direction - * \return a pair in which the first element is the horizontal component - * of the field pattern and the second element is the vertical - * component of the field pattern - */ - virtual std::pair GetElementFieldPattern(Angles a) const = 0; - - /** - * Returns the location of the antenna element with the specified + * \brief Returns the location of the antenna element with the specified * index, normalized with respect to the wavelength. * \param index the index of the antenna element * \return the 3D vector that represents the position of the element @@ -91,11 +81,129 @@ class PhasedArrayModel : public Object virtual Vector GetElementLocation(uint64_t index) const = 0; /** - * Returns the number of antenna elements + * \brief Returns the number of antenna elements * \return the number of antenna elements */ virtual size_t GetNumberOfElements() const = 0; + /** + * \brief Returns the horizontal and vertical components of the antenna element field + * pattern at the specified direction. Single polarization is considered. + * \param a the angle indicating the interested direction + * \param polIndex the index of the polarization for which will be retrieved the field + * pattern + * \return a pair in which the first element is the horizontal component of the field + * pattern and the second element is the vertical component of the field pattern + */ + virtual std::pair GetElementFieldPattern(Angles a, + uint8_t polIndex = 0) const = 0; + + /** + * \brief Set the vertical number of ports + * \param nPorts the vertical number of ports + */ + virtual void SetNumVerticalPorts(uint16_t nPorts) = 0; + + /** + * \brief Set the horizontal number of ports + * \param nPorts the horizontal number of ports + */ + virtual void SetNumHorizontalPorts(uint16_t nPorts) = 0; + + /** + * \brief Get the vertical number of ports + * \return the vertical number of ports + */ + virtual uint16_t GetNumVerticalPorts() const = 0; + + /** + * \brief Get the horizontal number of ports + * \return the horizontal number of ports + */ + virtual uint16_t GetNumHorizontalPorts() const = 0; + + /** + * \brief Get the number of ports + * \return the number of ports + */ + virtual uint16_t GetNumPorts() const = 0; + + /** + * \brief Get the number of polarizations + * \return the number of polarizations + */ + virtual uint8_t GetNumPols() const = 0; + + /** + * \brief Get the vertical number of antenna elements per port + * \return the vertical number of antenna elements per port + */ + virtual size_t GetVElemsPerPort() const = 0; + /** + * \brief Get the horizontal number of antenna elements per port + * \return the horizontal number of antenna elements per port + */ + virtual size_t GetHElemsPerPort() const = 0; + + /** + * \brief Get the number of elements per port + * \return the number of elements per port + */ + virtual size_t GetNumElemsPerPort() const = 0; + + /** + * \brief Set the number of columns + * \param nColumns the number of columns to be set + */ + virtual void SetNumColumns(uint32_t nColumns) = 0; + + /** + * \brief Set the number of rows + * \param nRows the number of rows to be set + */ + virtual void SetNumRows(uint32_t nRows) = 0; + + /** + * \brief Get the number of columns + * \return the number of columns in the antenna array + */ + virtual uint32_t GetNumColumns() const = 0; + + /** + * \brief Get the number of rows + * \return the number of rows in the antenna array + */ + virtual uint32_t GetNumRows() const = 0; + + /** + * \brief Get the polarization slant angle + * \return the polarization slant angle + */ + virtual double GetPolSlant() const = 0; + + /** + * \brief Get the indication whether the antenna array is dual polarized + * \return Returns true if the antenna array is dual polarized + */ + virtual bool IsDualPol() const = 0; + + /** + * \brief Calculate the index in the antenna array from the port index and the element in the + * port + * \param portIndex the port index + * \param subElementIndex the element index in the port + * \return the antenna element index in the antenna array + */ + virtual uint16_t ArrayIndexFromPortIndex(uint16_t portIndex, + uint16_t subElementIndex) const = 0; + + /** + * Returns the index of the polarization to which belongs that antenna element + * \param elementIndex the antenna element for which will be returned the polarization index + * \return the polarization index + */ + virtual uint8_t GetElemPol(size_t elementIndex) const = 0; + /** * Sets the beamforming vector to be used * \param beamformingVector the beamforming vector @@ -108,6 +216,12 @@ class PhasedArrayModel : public Object */ ComplexVector GetBeamformingVector() const; + /** + * Returns the const reference of the beamforming vector that is currently being used + * \return the const reference of the current beamforming vector + */ + const PhasedArrayModel::ComplexVector& GetBeamformingVectorRef() const; + /** * Returns the beamforming vector that points towards the specified position * \param a the beamforming angle @@ -149,15 +263,6 @@ class PhasedArrayModel : public Object uint32_t m_id{0}; //!< the ID of this antenna array instance }; -/** - * \brief Stream insertion operator. - * - * \param [in] os The reference to the output stream. - * \param [in] cv A vector of complex values. - * \returns The reference to the output stream. - */ -std::ostream& operator<<(std::ostream& os, const PhasedArrayModel::ComplexVector& cv); - } /* namespace ns3 */ #endif /* PHASED_ARRAY_MODEL_H */ diff --git a/src/antenna/model/uniform-planar-array.cc b/src/antenna/model/uniform-planar-array.cc index 0f5eafa6d..7997d256c 100644 --- a/src/antenna/model/uniform-planar-array.cc +++ b/src/antenna/model/uniform-planar-array.cc @@ -84,8 +84,27 @@ UniformPlanarArray::GetTypeId() .AddAttribute("PolSlantAngle", "The polarization slant angle in radians", DoubleValue(0.0), - MakeDoubleAccessor(&UniformPlanarArray::SetPolSlant), - MakeDoubleChecker(-M_PI, M_PI)); + MakeDoubleAccessor(&UniformPlanarArray::SetPolSlant, + &UniformPlanarArray::GetPolSlant), + MakeDoubleChecker(-M_PI, M_PI)) + .AddAttribute("NumVerticalPorts", + "Vertical number of ports", + UintegerValue(1), + MakeUintegerAccessor(&UniformPlanarArray::GetNumVerticalPorts, + &UniformPlanarArray::SetNumVerticalPorts), + MakeUintegerChecker()) + .AddAttribute("NumHorizontalPorts", + "Horizontal number of ports", + UintegerValue(1), + MakeUintegerAccessor(&UniformPlanarArray::GetNumHorizontalPorts, + &UniformPlanarArray::SetNumHorizontalPorts), + MakeUintegerChecker()) + .AddAttribute("IsDualPolarized", + "If true, dual polarized antenna", + BooleanValue(false), + MakeBooleanAccessor(&UniformPlanarArray::SetDualPol, + &UniformPlanarArray::IsDualPol), + MakeBooleanChecker()); return tid; } @@ -143,8 +162,8 @@ void UniformPlanarArray::SetPolSlant(double polSlant) { m_polSlant = polSlant; - m_cosPolSlant = cos(m_polSlant); - m_sinPolSlant = sin(m_polSlant); + m_cosPolSlant[0] = cos(m_polSlant); + m_sinPolSlant[0] = sin(m_polSlant); } void @@ -186,9 +205,10 @@ UniformPlanarArray::GetAntennaVerticalSpacing() const } std::pair -UniformPlanarArray::GetElementFieldPattern(Angles a) const +UniformPlanarArray::GetElementFieldPattern(Angles a, uint8_t polIndex) const { NS_LOG_FUNCTION(this << a); + NS_ASSERT_MSG(polIndex < GetNumPols(), "Polarization index can be 0 or 1."); // convert the theta and phi angles from GCS to LCS using eq. 7.1-7 and 7.1-8 in 3GPP TR 38.901 // NOTE we assume a fixed slant angle of 0 degrees @@ -208,8 +228,10 @@ UniformPlanarArray::GetElementFieldPattern(Angles a) const // NOTE: the slant angle (assumed to be 0) differs from the polarization slant angle // (m_polSlant, given by the attribute), in 3GPP TR 38.901 double aPrimeDb = m_antennaElement->GetGainDb(aPrime); - double fieldThetaPrime = pow(10, aPrimeDb / 20) * m_cosPolSlant; // convert to linear magnitude - double fieldPhiPrime = pow(10, aPrimeDb / 20) * m_sinPolSlant; // convert to linear magnitude + double fieldThetaPrime = + pow(10, aPrimeDb / 20) * m_cosPolSlant[polIndex]; // convert to linear magnitude + double fieldPhiPrime = + pow(10, aPrimeDb / 20) * m_sinPolSlant[polIndex]; // convert to linear magnitude // compute psi using eq. 7.1-15 in 3GPP TR 38.901, assuming that the slant // angle (gamma) is 0 @@ -232,13 +254,19 @@ Vector UniformPlanarArray::GetElementLocation(uint64_t index) const { NS_LOG_FUNCTION(this << index); - + uint64_t tmpIndex = index; + // for dual polarization, the top half corresponds to one polarization and + // lower half corresponds to the other polarization + if (m_isDualPolarized && tmpIndex >= m_numRows * m_numColumns) + { + tmpIndex -= m_numRows * m_numColumns; + } // compute the element coordinates in the LCS // assume the left bottom corner is (0,0,0), and the rectangular antenna array is on the y-z // plane. double xPrime = 0; - double yPrime = m_disH * (index % m_numColumns); - double zPrime = m_disV * floor(index / m_numColumns); + double yPrime = m_disH * (tmpIndex % m_numColumns); + double zPrime = m_disV * floor(tmpIndex / m_numColumns); // convert the coordinates to the GCS using the rotation matrix 7.1-4 in 3GPP // TR 38.901 @@ -249,10 +277,129 @@ UniformPlanarArray::GetElementLocation(uint64_t index) const return loc; } +uint8_t +UniformPlanarArray::GetNumPols() const +{ + return m_isDualPolarized ? 2 : 1; +} + size_t UniformPlanarArray::GetNumberOfElements() const { - return m_numRows * m_numColumns; + // From 38.901 [M, N, P, Mg, Ng] = [m_numRows, m_numColumns, 2, 1, 1] + return GetNumPols() * m_numRows * m_numColumns; + // with dual polarization, the number of antenna elements double up +} + +void +UniformPlanarArray::SetNumVerticalPorts(uint16_t nPorts) +{ + NS_LOG_FUNCTION(this); + NS_ASSERT_MSG(nPorts > 0, "Ports should be greater than 0"); + NS_ASSERT_MSG(((m_numRows % nPorts) == 0), + "The number of vertical ports must divide number of rows"); + m_numVPorts = nPorts; +} + +void +UniformPlanarArray::SetNumHorizontalPorts(uint16_t nPorts) +{ + NS_ASSERT_MSG(nPorts > 0, "Ports should be greater than 0"); + NS_ASSERT_MSG(((m_numColumns % nPorts) == 0), + "The number of horizontal ports must divide number of columns"); + m_numHPorts = nPorts; +} + +uint16_t +UniformPlanarArray::GetNumVerticalPorts() const +{ + return m_numVPorts; +} + +uint16_t +UniformPlanarArray::GetNumHorizontalPorts() const +{ + return m_numHPorts; +} + +uint16_t +UniformPlanarArray::GetNumPorts() const +{ + return GetNumPols() * m_numVPorts * m_numHPorts; +} + +size_t +UniformPlanarArray::GetVElemsPerPort() const +{ + return m_numRows / m_numVPorts; +} + +size_t +UniformPlanarArray::GetHElemsPerPort() const +{ + return m_numColumns / m_numHPorts; +} + +size_t +UniformPlanarArray::GetNumElemsPerPort() const +{ + // Multiply the number of rows and number of columns belonging to one antenna port. + // This also holds for dual polarization, where each polarization belongs to a separate port. + return GetVElemsPerPort() * GetHElemsPerPort(); +} + +uint16_t +UniformPlanarArray::ArrayIndexFromPortIndex(uint16_t portIndex, uint16_t subElementIndex) const +{ + NS_ASSERT_MSG(portIndex < GetNumPorts(), "Port should be less than total Ports"); + NS_ASSERT(subElementIndex < (GetHElemsPerPort() * GetVElemsPerPort())); + + // In case the array is dual-polarized, change to the index that belongs to the first + // polarization + auto firstPolPortIdx = portIndex; + auto polarizationOffset = 0; + auto arraySize = GetNumHorizontalPorts() * GetNumVerticalPorts(); + if (firstPolPortIdx > arraySize) + { + firstPolPortIdx = portIndex - arraySize; + polarizationOffset = GetNumColumns() * GetNumRows(); + } + // column-major indexing + auto hPortIdx = firstPolPortIdx / GetNumVerticalPorts(); + auto vPortIdx = firstPolPortIdx % GetNumVerticalPorts(); + auto hElemIdx = (hPortIdx * GetHElemsPerPort()) + (subElementIndex % GetHElemsPerPort()); + auto vElemIdx = (vPortIdx * GetVElemsPerPort()) + (subElementIndex / GetHElemsPerPort()); + return (vElemIdx * GetNumColumns() + hElemIdx + polarizationOffset); +} + +bool +UniformPlanarArray::IsDualPol() const +{ + return m_isDualPolarized; +} + +void +UniformPlanarArray::SetDualPol(bool isDualPol) +{ + m_isDualPolarized = isDualPol; + if (isDualPol) + { + m_cosPolSlant[1] = cos(m_polSlant - M_PI / 2); + m_sinPolSlant[1] = sin(m_polSlant - M_PI / 2); + } +} + +double +UniformPlanarArray::GetPolSlant() const +{ + return m_polSlant; +} + +uint8_t +UniformPlanarArray::GetElemPol(size_t elemIndex) const +{ + NS_ASSERT(elemIndex < GetNumElems()); + return (elemIndex < GetNumRows() * GetNumColumns()) ? 0 : 1; } } /* namespace ns3 */ diff --git a/src/antenna/model/uniform-planar-array.h b/src/antenna/model/uniform-planar-array.h index 82d23c513..73de013a8 100644 --- a/src/antenna/model/uniform-planar-array.h +++ b/src/antenna/model/uniform-planar-array.h @@ -31,7 +31,7 @@ namespace ns3 * \brief Class implementing Uniform Planar Array (UPA) model. * * \note the current implementation supports the modeling of antenna arrays - * composed of a single panel and with single (configured) polarization. + * composed of a single panel and with single or dual polarization. */ class UniformPlanarArray : public PhasedArrayModel { @@ -54,13 +54,15 @@ class UniformPlanarArray : public PhasedArrayModel /** * Returns the horizontal and vertical components of the antenna element field - * pattern at the specified direction. Single polarization is considered. + * pattern at the specified direction and for the specified polarization. * \param a the angle indicating the interested direction + * \param polIndex the index of the polarization for which will be retrieved the field + * pattern * \return a pair in which the first element is the horizontal component * of the field pattern and the second element is the vertical * component of the field pattern */ - std::pair GetElementFieldPattern(Angles a) const override; + std::pair GetElementFieldPattern(Angles a, uint8_t polIndex = 0) const override; /** * Returns the location of the antenna element with the specified @@ -72,32 +74,55 @@ class UniformPlanarArray : public PhasedArrayModel * | 3 4 5 * | 0 1 2 * ----------> y + * In case of dual-polarized antennas, the antennas of the first and the second polarization + * are overlapped in space. * * \param index index of the antenna element - * \return the 3D vector that represents the position of the element + * \return the 3D vector that + * represents the position of the element */ Vector GetElementLocation(uint64_t index) const override; /** - * Returns the number of antenna elements + * Check if an antenna array contains dual-polarized elements + * + * \return true if antenna has two polarization otherwise false + */ + bool IsDualPol() const override; + + /** + * Returns polarization angle of first polarization + * \return polarization angle in radians + */ + double GetPolSlant() const override; + + /** + * Returns the number of polarizations, 2 in the case that + * the antenna is dual-polarized, otherwise 1 + * \return + */ + uint8_t GetNumPols() const override; + + /** + * Returns the number of total antenna elements. Note that if the antenna + * is dual-polarized the number of total antenna elements is doubled. * \return the number of antenna elements */ size_t GetNumberOfElements() const override; - private: /** * Set the number of columns of the phased array * This method resets the stored beamforming vector to a ComplexVector * of the correct size, but zero-filled * \param n the number of columns */ - void SetNumColumns(uint32_t n); + void SetNumColumns(uint32_t n) override; /** * Get the number of columns of the phased array * \return the number of columns */ - uint32_t GetNumColumns() const; + uint32_t GetNumColumns() const override; /** * Set the number of rows of the phased array @@ -105,13 +130,69 @@ class UniformPlanarArray : public PhasedArrayModel * of the correct size, but zero-filled * \param n the number of rows */ - void SetNumRows(uint32_t n); + void SetNumRows(uint32_t n) override; /** * Get the number of rows of the phased array * \return the number of rows */ - uint32_t GetNumRows() const; + uint32_t GetNumRows() const override; + + /** + * Set the number of vertical antenna ports + * \param nPorts The number of vertical ports to be configured + */ + void SetNumVerticalPorts(uint16_t nPorts) override; + + /** + * Set the number of horizontal antenna ports + * \param nPorts + */ + void SetNumHorizontalPorts(uint16_t nPorts) override; + + /** + * Get the number of vertical antenna ports + * \return the number of vertical antenna ports + */ + uint16_t GetNumVerticalPorts() const override; + + /** + * Get the number of horizontal antenna ports + * \return the number of horizontal antenna ports + */ + uint16_t GetNumHorizontalPorts() const override; + + /** + * Get the total number of antenna ports + * \return the number of antenna ports + */ + uint16_t GetNumPorts() const override; + + /** + * Get the number of vertical elements belonging to each port + * \return the number of vertical elements belonging of each port + */ + size_t GetVElemsPerPort() const override; + + /** + * Get the number of horizontal elements belonging to each port + * \return the number of horizontal elements belonging to each port + */ + size_t GetHElemsPerPort() const override; + + /** + * Get the total number of elements belonging to each port + * \return the number of elements per port + */ + size_t GetNumElemsPerPort() const override; + + /** + * Maps element within a port to an index of element within the antenna array + * \param portIndex the port index + * \param subElementIndex the element index within the port + * \return the element index in the antenna array + */ + uint16_t ArrayIndexFromPortIndex(uint16_t portIndex, uint16_t subElementIndex) const override; /** * \brief Set the bearing angle @@ -165,19 +246,37 @@ class UniformPlanarArray : public PhasedArrayModel */ double GetAntennaVerticalSpacing() const; + /** + * Set the polarization + * \param isDualPol whether to set the antenna array to be dual-polarized, + * if true, antenna will be dual-polarized + */ + void SetDualPol(bool isDualPol); + + /** + * Returns the index of polarization to which belongs the antenna element with a specific index + * \param elemIndex the antenna element index + * \return the polarization index + */ + uint8_t GetElemPol(size_t elemIndex) const override; + + private: uint32_t m_numColumns{1}; //!< number of columns uint32_t m_numRows{1}; //!< number of rows double m_disV{0.5}; //!< antenna spacing in the vertical direction in multiples of wave length double m_disH{0.5}; //!< antenna spacing in the horizontal direction in multiples of wave length - double m_alpha{0.0}; //!< the bearing angle in radians - double m_cosAlpha{1.0}; //!< the cosine of alpha - double m_sinAlpha{0.0}; //!< the sine of alpha - double m_beta{0.0}; //!< the downtilt angle in radians - double m_cosBeta{1.0}; //!< the cosine of Beta - double m_sinBeta{0.0}; //!< the sine of Beta - double m_polSlant{0.0}; //!< the polarization slant angle in radians - double m_cosPolSlant{1.0}; //!< the cosine of polarization slant angle - double m_sinPolSlant{0.0}; //!< the sine polarization slant angle + double m_alpha{0.0}; //!< the bearing angle in radians + double m_cosAlpha{1.0}; //!< the cosine of alpha + double m_sinAlpha{0.0}; //!< the sine of alpha + double m_beta{0.0}; //!< the downtilt angle in radians + double m_cosBeta{1.0}; //!< the cosine of Beta + double m_sinBeta{0.0}; //!< the sine of Beta + double m_polSlant{0.0}; //!< the polarization slant angle in radians + bool m_isDualPolarized{false}; //!< if true antenna elements are dual-polarized + uint16_t m_numVPorts{1}; //!< Number of vertical ports + uint16_t m_numHPorts{1}; //!< Number of horizontal ports + std::vector m_cosPolSlant{1.0, 0.0}; //!< the cosine of polarization slant angle + std::vector m_sinPolSlant{0.0, -1.0}; //!< the sine polarization slant angle }; } /* namespace ns3 */