A Discrete-Event Network Simulator
API
uniform-planar-array.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 University of Padova, Dep. of Information Engineering, SIGNET lab.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation;
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  */
17 
18 #include "uniform-planar-array.h"
19 
20 #include <ns3/boolean.h>
21 #include <ns3/double.h>
22 #include <ns3/log.h>
23 #include <ns3/uinteger.h>
24 
25 namespace ns3
26 {
27 
28 NS_LOG_COMPONENT_DEFINE("UniformPlanarArray");
29 
30 NS_OBJECT_ENSURE_REGISTERED(UniformPlanarArray);
31 
34 {
35 }
36 
38 {
39 }
40 
41 TypeId
43 {
44  static TypeId tid =
45  TypeId("ns3::UniformPlanarArray")
47  .AddConstructor<UniformPlanarArray>()
48  .SetGroupName("Antenna")
49  .AddAttribute(
50  "AntennaHorizontalSpacing",
51  "Horizontal spacing between antenna elements, in multiples of wave length",
52  DoubleValue(0.5),
55  MakeDoubleChecker<double>(0.0))
56  .AddAttribute("AntennaVerticalSpacing",
57  "Vertical spacing between antenna elements, in multiples of wave length",
58  DoubleValue(0.5),
61  MakeDoubleChecker<double>(0.0))
62  .AddAttribute("NumColumns",
63  "Horizontal size of the array",
64  UintegerValue(4),
67  MakeUintegerChecker<uint32_t>(1))
68  .AddAttribute("NumRows",
69  "Vertical size of the array",
70  UintegerValue(4),
73  MakeUintegerChecker<uint32_t>(1))
74  .AddAttribute("BearingAngle",
75  "The bearing angle in radians",
76  DoubleValue(0.0),
78  MakeDoubleChecker<double>(-M_PI, M_PI))
79  .AddAttribute("DowntiltAngle",
80  "The downtilt angle in radians",
81  DoubleValue(0.0),
83  MakeDoubleChecker<double>(-M_PI, M_PI))
84  .AddAttribute("PolSlantAngle",
85  "The polarization slant angle in radians",
86  DoubleValue(0.0),
89  MakeDoubleChecker<double>(-M_PI, M_PI))
90  .AddAttribute("NumVerticalPorts",
91  "Vertical number of ports",
92  UintegerValue(1),
95  MakeUintegerChecker<uint32_t>())
96  .AddAttribute("NumHorizontalPorts",
97  "Horizontal number of ports",
98  UintegerValue(1),
101  MakeUintegerChecker<uint32_t>())
102  .AddAttribute("IsDualPolarized",
103  "If true, dual polarized antenna",
104  BooleanValue(false),
108  return tid;
109 }
110 
111 void
113 {
114  NS_LOG_FUNCTION(this << n);
115  if (n != m_numColumns)
116  {
117  m_isBfVectorValid = false;
118  }
119  m_numColumns = n;
120 }
121 
122 uint32_t
124 {
125  return m_numColumns;
126 }
127 
128 void
130 {
131  NS_LOG_FUNCTION(this << n);
132  if (n != m_numRows)
133  {
134  m_isBfVectorValid = false;
135  }
136  m_numRows = n;
137 }
138 
139 uint32_t
141 {
142  return m_numRows;
143 }
144 
145 void
147 {
148  m_alpha = alpha;
149  m_cosAlpha = cos(m_alpha);
150  m_sinAlpha = sin(m_alpha);
151 }
152 
153 void
155 {
156  m_beta = beta;
157  m_cosBeta = cos(m_beta);
158  m_sinBeta = sin(m_beta);
159 }
160 
161 void
163 {
164  m_polSlant = polSlant;
165  m_cosPolSlant[0] = cos(m_polSlant);
166  m_sinPolSlant[0] = sin(m_polSlant);
167 }
168 
169 void
171 {
172  NS_LOG_FUNCTION(this << s);
173  NS_ABORT_MSG_IF(s <= 0, "Trying to set an invalid spacing: " << s);
174 
175  if (s != m_disH)
176  {
177  m_isBfVectorValid = false;
178  }
179  m_disH = s;
180 }
181 
182 double
184 {
185  return m_disH;
186 }
187 
188 void
190 {
191  NS_LOG_FUNCTION(this << s);
192  NS_ABORT_MSG_IF(s <= 0, "Trying to set an invalid spacing: " << s);
193 
194  if (s != m_disV)
195  {
196  m_isBfVectorValid = false;
197  }
198  m_disV = s;
199 }
200 
201 double
203 {
204  return m_disV;
205 }
206 
207 std::pair<double, double>
209 {
210  NS_LOG_FUNCTION(this << a);
211  NS_ASSERT_MSG(polIndex < GetNumPols(), "Polarization index can be 0 or 1.");
212 
213  // convert the theta and phi angles from GCS to LCS using eq. 7.1-7 and 7.1-8 in 3GPP TR 38.901
214  // NOTE we assume a fixed slant angle of 0 degrees
215  double cosIncl = cos(a.GetInclination());
216  double sinIncl = sin(a.GetInclination());
217  double cosAzim = cos(a.GetAzimuth() - m_alpha);
218  double sinAzim = sin(a.GetAzimuth() - m_alpha);
219  double thetaPrime = std::acos(m_cosBeta * cosIncl + m_sinBeta * cosAzim * sinIncl);
220  double phiPrime =
221  std::arg(std::complex<double>(m_cosBeta * sinIncl * cosAzim - m_sinBeta * cosIncl,
222  sinAzim * sinIncl));
223  Angles aPrime(phiPrime, thetaPrime);
224  NS_LOG_DEBUG(a << " -> " << aPrime);
225 
226  // compute the antenna element field patterns using eq. 7.3-4 and 7.3-5 in 3GPP TR 38.901,
227  // using the configured polarization slant angle (m_polSlant)
228  // NOTE: the slant angle (assumed to be 0) differs from the polarization slant angle
229  // (m_polSlant, given by the attribute), in 3GPP TR 38.901
230  double aPrimeDb = m_antennaElement->GetGainDb(aPrime);
231  double fieldThetaPrime =
232  pow(10, aPrimeDb / 20) * m_cosPolSlant[polIndex]; // convert to linear magnitude
233  double fieldPhiPrime =
234  pow(10, aPrimeDb / 20) * m_sinPolSlant[polIndex]; // convert to linear magnitude
235 
236  // compute psi using eq. 7.1-15 in 3GPP TR 38.901, assuming that the slant
237  // angle (gamma) is 0
238  double psi = std::arg(std::complex<double>(m_cosBeta * sinIncl - m_sinBeta * cosIncl * cosAzim,
239  m_sinBeta * sinAzim));
240  NS_LOG_DEBUG("psi " << psi);
241 
242  // convert the antenna element field pattern to GCS using eq. 7.1-11
243  // in 3GPP TR 38.901
244  double fieldTheta = cos(psi) * fieldThetaPrime - sin(psi) * fieldPhiPrime;
245  double fieldPhi = sin(psi) * fieldThetaPrime + cos(psi) * fieldPhiPrime;
247  << " " << RadiansToDegrees(a.GetInclination()) << " "
248  << fieldTheta * fieldTheta + fieldPhi * fieldPhi);
249 
250  return std::make_pair(fieldPhi, fieldTheta);
251 }
252 
253 Vector
255 {
256  NS_LOG_FUNCTION(this << index);
257  uint64_t tmpIndex = index;
258  // for dual polarization, the top half corresponds to one polarization and
259  // lower half corresponds to the other polarization
260  if (m_isDualPolarized && tmpIndex >= m_numRows * m_numColumns)
261  {
262  tmpIndex -= m_numRows * m_numColumns;
263  }
264  // compute the element coordinates in the LCS
265  // assume the left bottom corner is (0,0,0), and the rectangular antenna array is on the y-z
266  // plane.
267  double xPrime = 0;
268  double yPrime = m_disH * (tmpIndex % m_numColumns);
269  double zPrime = m_disV * floor(tmpIndex / m_numColumns);
270 
271  // convert the coordinates to the GCS using the rotation matrix 7.1-4 in 3GPP
272  // TR 38.901
273  Vector loc;
274  loc.x = m_cosAlpha * m_cosBeta * xPrime - m_sinAlpha * yPrime + m_cosAlpha * m_sinBeta * zPrime;
275  loc.y = m_sinAlpha * m_cosBeta * xPrime + m_cosAlpha * yPrime + m_sinAlpha * m_sinBeta * zPrime;
276  loc.z = -m_sinBeta * xPrime + m_cosBeta * zPrime;
277  return loc;
278 }
279 
280 uint8_t
282 {
283  return m_isDualPolarized ? 2 : 1;
284 }
285 
286 size_t
288 {
289  // From 38.901 [M, N, P, Mg, Ng] = [m_numRows, m_numColumns, 2, 1, 1]
290  return GetNumPols() * m_numRows * m_numColumns;
291  // with dual polarization, the number of antenna elements double up
292 }
293 
294 void
296 {
297  NS_LOG_FUNCTION(this);
298  NS_ASSERT_MSG(nPorts > 0, "Ports should be greater than 0");
299  NS_ASSERT_MSG(((m_numRows % nPorts) == 0),
300  "The number of vertical ports must divide number of rows");
301  m_numVPorts = nPorts;
302 }
303 
304 void
306 {
307  NS_ASSERT_MSG(nPorts > 0, "Ports should be greater than 0");
308  NS_ASSERT_MSG(((m_numColumns % nPorts) == 0),
309  "The number of horizontal ports must divide number of columns");
310  m_numHPorts = nPorts;
311 }
312 
313 uint16_t
315 {
316  return m_numVPorts;
317 }
318 
319 uint16_t
321 {
322  return m_numHPorts;
323 }
324 
325 uint16_t
327 {
328  return GetNumPols() * m_numVPorts * m_numHPorts;
329 }
330 
331 size_t
333 {
334  return m_numRows / m_numVPorts;
335 }
336 
337 size_t
339 {
340  return m_numColumns / m_numHPorts;
341 }
342 
343 size_t
345 {
346  // Multiply the number of rows and number of columns belonging to one antenna port.
347  // This also holds for dual polarization, where each polarization belongs to a separate port.
348  return GetVElemsPerPort() * GetHElemsPerPort();
349 }
350 
351 uint16_t
352 UniformPlanarArray::ArrayIndexFromPortIndex(uint16_t portIndex, uint16_t subElementIndex) const
353 {
354  NS_ASSERT_MSG(portIndex < GetNumPorts(), "Port should be less than total Ports");
355  NS_ASSERT(subElementIndex < (GetHElemsPerPort() * GetVElemsPerPort()));
356 
357  // In case the array is dual-polarized, change to the index that belongs to the first
358  // polarization
359  auto firstPolPortIdx = portIndex;
360  auto polarizationOffset = 0;
361  auto arraySize = GetNumHorizontalPorts() * GetNumVerticalPorts();
362  if (firstPolPortIdx >= arraySize)
363  {
364  firstPolPortIdx = portIndex - arraySize;
365  polarizationOffset = GetNumColumns() * GetNumRows();
366  }
367  // column-major indexing
368  auto hPortIdx = firstPolPortIdx / GetNumVerticalPorts();
369  auto vPortIdx = firstPolPortIdx % GetNumVerticalPorts();
370  auto hElemIdx = (hPortIdx * GetHElemsPerPort()) + (subElementIndex % GetHElemsPerPort());
371  auto vElemIdx = (vPortIdx * GetVElemsPerPort()) + (subElementIndex / GetHElemsPerPort());
372  return (vElemIdx * GetNumColumns() + hElemIdx + polarizationOffset);
373 }
374 
375 bool
377 {
378  return m_isDualPolarized;
379 }
380 
381 void
383 {
384  m_isDualPolarized = isDualPol;
385  if (isDualPol)
386  {
387  m_cosPolSlant[1] = cos(m_polSlant - M_PI / 2);
388  m_sinPolSlant[1] = sin(m_polSlant - M_PI / 2);
389  }
390 }
391 
392 double
394 {
395  return m_polSlant;
396 }
397 
398 uint8_t
399 UniformPlanarArray::GetElemPol(size_t elemIndex) const
400 {
401  NS_ASSERT(elemIndex < GetNumElems());
402  return (elemIndex < GetNumRows() * GetNumColumns()) ? 0 : 1;
403 }
404 
405 } /* namespace ns3 */
Class holding the azimuth and inclination angles of spherical coordinates.
Definition: angles.h:118
double GetInclination() const
Getter for inclination angle.
Definition: angles.cc:246
double GetAzimuth() const
Getter for azimuth angle.
Definition: angles.cc:240
virtual double GetGainDb(Angles a)=0
this method is expected to be re-implemented by each antenna model
This class can be used to hold variables of floating point type such as 'double' or 'float'.
Definition: double.h:42
Class implementing the phased array model virtual base class.
Ptr< AntennaModel > m_antennaElement
the model of the antenna element in use
bool m_isBfVectorValid
ensures the validity of the beamforming vector
a unique identifier for an interface.
Definition: type-id.h:59
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:931
Hold an unsigned integer type.
Definition: uinteger.h:45
double m_disV
antenna spacing in the vertical direction in multiples of wave length
double m_sinAlpha
the sine of alpha
void SetPolSlant(double polSlant)
Set the polarization slant angle This method sets the polarization slant angle and computes its cosin...
uint16_t GetNumPorts() const override
Get the total number of antenna ports.
double m_disH
antenna spacing in the horizontal direction in multiples of wave length
void SetBeta(double beta)
Set the downtilt angle This method sets the downtilt angle and computes its cosine and sine.
bool m_isDualPolarized
if true antenna elements are dual-polarized
uint16_t ArrayIndexFromPortIndex(uint16_t portIndex, uint16_t subElementIndex) const override
Maps element within a port to an index of element within the antenna array.
void SetNumRows(uint32_t n) override
Set the number of rows of the phased array This method resets the stored beamforming vector to a Comp...
bool IsDualPol() const override
Check if an antenna array contains dual-polarized elements.
static TypeId GetTypeId()
Get the type ID.
void SetAlpha(double alpha)
Set the bearing angle This method sets the bearing angle and computes its cosine and sine.
void SetNumColumns(uint32_t n) override
Set the number of columns of the phased array This method resets the stored beamforming vector to a C...
double GetAntennaVerticalSpacing() const
Get the vertical spacing for the antenna elements of the phased array.
uint16_t GetNumVerticalPorts() const override
Get the number of vertical antenna ports.
uint16_t m_numVPorts
Number of vertical ports.
std::pair< double, double > GetElementFieldPattern(Angles a, uint8_t polIndex=0) const override
Returns the horizontal and vertical components of the antenna element field pattern at the specified ...
double m_polSlant
the polarization slant angle in radians
uint8_t GetElemPol(size_t elemIndex) const override
Returns the index of polarization to which belongs the antenna element with a specific index.
uint32_t GetNumRows() const override
Get the number of rows of the phased array.
double GetPolSlant() const override
Returns polarization angle of first polarization.
double m_cosBeta
the cosine of Beta
void SetAntennaVerticalSpacing(double s)
Set the vertical spacing for the antenna elements of the phased array This method resets the stored b...
double GetAntennaHorizontalSpacing() const
Get the horizontal spacing for the antenna elements of the phased array.
~UniformPlanarArray() override
Destructor.
uint32_t m_numRows
number of rows
Vector GetElementLocation(uint64_t index) const override
Returns the location of the antenna element with the specified index assuming the left bottom corner ...
uint32_t m_numColumns
number of columns
double m_alpha
the bearing angle in radians
uint16_t GetNumHorizontalPorts() const override
Get the number of horizontal antenna ports.
uint16_t m_numHPorts
Number of horizontal ports.
void SetDualPol(bool isDualPol)
Set the polarization.
size_t GetHElemsPerPort() const override
Get the number of horizontal elements belonging to each port.
void SetNumVerticalPorts(uint16_t nPorts) override
Set the number of vertical antenna ports.
size_t GetVElemsPerPort() const override
Get the number of vertical elements belonging to each port.
uint32_t GetNumColumns() const override
Get the number of columns of the phased array.
void SetNumHorizontalPorts(uint16_t nPorts) override
Set the number of horizontal antenna ports.
std::vector< double > m_cosPolSlant
the cosine of polarization slant angle
double m_sinBeta
the sine of Beta
size_t GetNumElems() const override
Returns the number of total antenna elements.
void SetAntennaHorizontalSpacing(double s)
Set the horizontal spacing for the antenna elements of the phased array This method resets the stored...
double m_beta
the downtilt angle in radians
double m_cosAlpha
the cosine of alpha
size_t GetNumElemsPerPort() const override
Get the total number of elements belonging to each port.
uint8_t GetNumPols() const override
Returns the number of polarizations, 2 in the case that the antenna is dual-polarized,...
std::vector< double > m_sinPolSlant
the sine polarization slant angle
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition: assert.h:66
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition: assert.h:86
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition: abort.h:108
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:268
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:46
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition: boolean.cc:124
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Definition: boolean.h:86
Ptr< const AttributeAccessor > MakeDoubleAccessor(T1 a1)
Definition: double.h:43
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
Definition: uinteger.h:46
double RadiansToDegrees(double radians)
converts radians to degrees
Definition: angles.cc:45