antenna: (merges !481) Update antenna module

- Introduce PhasedArrayModel
- Use the new PhasedArrayModel framework across modules (e.g., 3gpp channel model)
- Improve Angles class. Furthermore, Angles has been translated from elevation to inclination and from degrees to radians
- Update antenna module doc
- Fix random angle generation for the 3gpp channel model. Specifically, cluster and sub-cluster angles might be generated with inclination angles outside the inclination range [0, pi], and have now been fixed.
This commit is contained in:
Mattia Lecci
2021-04-10 20:01:38 +02:00
committed by Tom Henderson
parent 8d4e5772c8
commit 2cfcebe0d0
31 changed files with 2072 additions and 956 deletions

View File

@@ -41,7 +41,7 @@
#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include <fstream>
#include "ns3/three-gpp-antenna-array-model.h"
#include "ns3/uniform-planar-array.h"
#include "ns3/three-gpp-spectrum-propagation-loss-model.h"
#include "ns3/three-gpp-v2v-propagation-loss-model.h"
#include "ns3/three-gpp-channel-model.h"
@@ -61,9 +61,9 @@ static Ptr<ChannelConditionModel> m_condModel; //!< the ChannelConditionModel ob
* \param otherDevice the device towards which point the beam
*/
static void
DoBeamforming (Ptr<NetDevice> thisDevice, Ptr<ThreeGppAntennaArrayModel> thisAntenna, Ptr<NetDevice> otherDevice)
DoBeamforming (Ptr<NetDevice> thisDevice, Ptr<PhasedArrayModel> thisAntenna, Ptr<NetDevice> otherDevice)
{
ThreeGppAntennaArrayModel::ComplexVector antennaWeights;
PhasedArrayModel::ComplexVector antennaWeights;
// retrieve the position of the two devices
Vector aPos = thisDevice->GetNode ()->GetObject<MobilityModel> ()->GetPosition ();
@@ -72,31 +72,8 @@ DoBeamforming (Ptr<NetDevice> thisDevice, Ptr<ThreeGppAntennaArrayModel> thisAnt
// compute the azimuth and the elevation angles
Angles completeAngle (bPos,aPos);
double hAngleRadian = fmod (completeAngle.phi, 2.0 * M_PI); // the azimuth angle
if (hAngleRadian < 0)
{
hAngleRadian += 2.0 * M_PI;
}
double vAngleRadian = completeAngle.theta; // the elevation angle
// retrieve the number of antenna elements
int totNoArrayElements = thisAntenna->GetNumberOfElements ();
// the total power is divided equally among the antenna elements
double power = 1 / sqrt (totNoArrayElements);
// compute the antenna weights
for (int ind = 0; ind < totNoArrayElements; ind++)
{
Vector loc = thisAntenna->GetElementLocation (ind);
double phase = -2 * M_PI * (sin (vAngleRadian) * cos (hAngleRadian) * loc.x
+ sin (vAngleRadian) * sin (hAngleRadian) * loc.y
+ cos (vAngleRadian) * loc.z);
antennaWeights.push_back (exp (std::complex<double> (0, phase)) * power);
}
// store the antenna weights
thisAntenna->SetBeamformingVector (antennaWeights);
PhasedArrayModel::ComplexVector bf = thisAntenna->GetBeamformingVector (completeAngle);
thisAntenna->SetBeamformingVector (bf);
}
/**
@@ -212,8 +189,8 @@ main (int argc, char *argv[])
rxDev->SetNode (nodes.Get (1));
// create the antenna objects and set their dimensions
Ptr<ThreeGppAntennaArrayModel> txAntenna = CreateObjectWithAttributes<ThreeGppAntennaArrayModel> ("NumColumns", UintegerValue (2), "NumRows", UintegerValue (2), "BearingAngle", DoubleValue (-M_PI / 2));
Ptr<ThreeGppAntennaArrayModel> rxAntenna = CreateObjectWithAttributes<ThreeGppAntennaArrayModel> ("NumColumns", UintegerValue (2), "NumRows", UintegerValue (2), "BearingAngle", DoubleValue (M_PI / 2));
Ptr<PhasedArrayModel> txAntenna = CreateObjectWithAttributes<UniformPlanarArray> ("NumColumns", UintegerValue (2), "NumRows", UintegerValue (2), "BearingAngle", DoubleValue (-M_PI / 2));
Ptr<PhasedArrayModel> rxAntenna = CreateObjectWithAttributes<UniformPlanarArray> ("NumColumns", UintegerValue (2), "NumRows", UintegerValue (2), "BearingAngle", DoubleValue (M_PI / 2));
Ptr<MobilityModel> txMob;
Ptr<MobilityModel> rxMob;

View File

@@ -11,9 +11,27 @@ Overview
The Antenna module provides:
#. a new base class (AntennaModel) that provides an interface for the modeling of the radiation pattern of an antenna;
#. a class (Angles) and utility functions to deal with angles
#. a base class (AntennaModel) that provides an interface for the modeling of the radiation pattern of an antenna;
#. a set of classes derived from this base class that each models the radiation pattern of different types of antennas;
#. the class ThreeGppAntennaArrayModel, which implements the antenna model described in 3GPP TR 38.901
#. a base class (PhasedArrayModel) that provides a flexible interface for modeling a number of Phase Antenna Array (PAA) models
#. a class (UniformPlanarArray) derived from this base class, implementing a Uniform Planar Arraya (UPA) supporting both rectangular and linear lattices
------
Angles
------
The Angles class holds information about an angle in 3D space using spherical coordinates in radian units.
Specifically, it uses the azimuth-inclination convention, where
* Inclination is the angle between the zenith direction (positive z-axis) and the desired direction. It is included in the range [0, pi] radians.
* Azimuth is the signed angle measured from the positive x-axis, where a positive direction goes towards the positive y-axis. It is included in the range [-pi, pi) radians.
Multiple constructors are present, supporting the most common ways to encode information on a direction.
A static boolean variable allows the user to decide whether angles should be printed in radian or degree units.
A number of angle-related utilities are offered, such as radians/degree conversions, for both scalars and vectors, and angle wrapping.
------------
@@ -107,36 +125,53 @@ beamwidth, and :math:`A_{max}` is the maximum attenuation in dB of the
antenna. Note that this radiation pattern is independent of the inclination angle
:math:`\theta`.
.. _sec-3gpp-antenna-model:
-------------------------
ThreeGppAntennaArrayModel
-------------------------
The class ThreeGppAntennaArrayModel implements the antenna model described in
3GPP TR 38.901 [38901]_, which is used by the classes ThreeGppSpectrumPropagationLossModel
and ThreeGppChannelModel.
Each instance of this class models an isotropic rectangular antenna array composed
of a single panel with NxM elements, where N is the number of rows and M is the
number of columns, configurable through the attributes "NumRows" and "NumColumns".
The radiation pattern of the antenna elements follows the model specified in
Sec. 7.3 of 3GPP TR 38.901; only vertical polarization is considered (i.e.,
:math:`{\zeta = 0}`).
The directional gain of the antenna elements can be configured through the
attribute "ElementGain" (see formula 2.34 in [Mailloux]_ to choose a proper value).
ThreeGppAntennaModel
++++++++++++++++++++
This model implements the antenna element described in [38901]_.
Parameters are fixed from the technical report, thus no attributes nor setters are provided.
The model is largely based on the `ParabolicAntennaModel`_.
------------------
Phased Array Model
------------------
The class PhasedArrayModel has been created with flexibility in mind.
It abstracts the basic idea of a Phased Antenna Array (PAA) by removing any constraint on the
position of each element, and instead generalizes the concept of steering and beamforming vectors,
solely based on the generalized location of the antenna elements.
Derived classes must implement the following functions:
* GetNumberOfElements: returns the number of antenna elements
* GetElementLocation: returns the location of the antenna element with the specified index, normalized with respect to the wavelength
* GetElementFieldPattern: returns the horizontal and vertical components of the antenna element field pattern at the specified direction. Only vertical polarization is considered.
The class PhasedArrayModel also assumes that all antenna elements are equal, a typical key assumption which allows to model the PAA field pattern as the sum of the array factor, given by the geometry of the location of the antenna elements, and the element field pattern.
Any class derived from AntennaModel is a valid antenna element for the PhasedArrayModel, allowing for a great flexibility of the framework.
UniformPlanarArray
++++++++++++++++++
The class UniformPlanarArray is a generic implementation of Uniform Planar Arrays (UPAs),
supporting rectangular and linear regular lattices.
It loosesly 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`.
By default, the 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 spacing between the horizontal and vertical elements can be configured through
the attributes "AntennaHorizontalSpacing" and "AntennaVerticalSpacing".
The slant angle is instead fixed and assumed to be 0.
**Note:**
The number of antenna elements in the vertical and horizontal directions can be configured
through the attributes "NumRows" and "NumColumns", while the spacing between the horizontal
and vertical elements can be configured through the attributes "AntennaHorizontalSpacing"
and "AntennaVerticalSpacing".
* Currently, the model does not support multi-panel antennas, i.e.,
:math:`N_{g} = M_{g} = 1`.
* Currently, the model supports only single polarized (i.e., P = 1) antenna
panels with vertical polarization (i.e., :math:`{\zeta = 0}`)
Note: vertical polarization is assumed for each antenna element, as described in [38901]_ (i.e., :math:`{\zeta = 0}`).
.. [Balanis] C.A. Balanis, "Antenna Theory - Analysis and Design", Wiley, 2nd Ed.

View File

@@ -28,27 +28,139 @@ namespace ns3 {
NS_LOG_COMPONENT_DEFINE ("Angles");
double DegreesToRadians (double degrees)
bool Angles::m_printDeg = false;
const double DEG_TO_RAD = M_PI / 180.0;
const double RAD_TO_DEG = 180.0 / M_PI;
double
DegreesToRadians (double degrees)
{
return degrees * M_PI / 180.0;
return degrees * DEG_TO_RAD;
}
double
RadiansToDegrees (double radians)
{
return radians * RAD_TO_DEG;
}
std::vector<double>
DegreesToRadians (const std::vector<double> &degrees)
{
std::vector<double> radians;
radians.reserve (degrees.size ());
for (size_t i = 0; i < degrees.size (); i++)
{
radians.push_back (DegreesToRadians (degrees[i]));
}
return radians;
}
double RadiansToDegrees (double radians)
std::vector<double>
RadiansToDegrees (const std::vector<double> &radians)
{
return radians * 180.0 / M_PI;
std::vector<double> degrees;
degrees.reserve (radians.size ());
for (size_t i = 0; i < radians.size (); i++)
{
degrees.push_back (RadiansToDegrees (radians[i]));
}
return degrees;
}
std::ostream& operator<< (std::ostream& os, const Angles& a)
double
WrapTo360 (double a)
{
os << "(" << a.phi << ", " << a.theta << ")";
a = fmod (a, 360);
if (a < 0)
{
a += 360;
}
NS_ASSERT_MSG (0 <= a && a < 360, "Invalid wrap, a=" << a);
return a;
}
double
WrapTo180 (double a)
{
a = fmod (a + 180, 360);
if (a < 0)
{
a += 360;
}
a -= 180;
NS_ASSERT_MSG (-180 <= a && a < 180, "Invalid wrap, a=" << a);
return a;
}
double
WrapTo2Pi (double a)
{
a = fmod (a, 2 * M_PI);
if (a < 0)
{
a += 2 * M_PI;
}
NS_ASSERT_MSG (0 <= a && a < 2 * M_PI, "Invalid wrap, a=" << a);
return a;
}
double
WrapToPi (double a)
{
a = fmod (a + M_PI, 2 * M_PI);
if (a < 0)
{
a += 2 * M_PI;
}
a -= M_PI;
NS_ASSERT_MSG (-M_PI <= a && a < M_PI, "Invalid wrap, a=" << a);
return a;
}
std::ostream&
operator<< (std::ostream& os, const Angles& a)
{
double azim, incl;
std::string unit;
if (a.m_printDeg)
{
azim = RadiansToDegrees (a.m_azimuth);
incl = RadiansToDegrees (a.m_inclination);
unit = "deg";
}
else
{
azim = a.m_azimuth;
incl = a.m_inclination;
unit = "rad";
}
os << "(" << azim << ", " << incl << ") " << unit;
return os;
}
std::istream &operator >> (std::istream &is, Angles &a)
std::istream&
operator>> (std::istream& is, Angles& a)
{
char c;
is >> a.phi >> c >> a.theta;
is >> a.m_azimuth >> c >> a.m_inclination;
if (c != ':')
{
is.setstate (std::ios_base::failbit);
@@ -58,31 +170,98 @@ std::istream &operator >> (std::istream &is, Angles &a)
Angles::Angles ()
: phi (0),
theta (0)
{
}
: Angles (NAN, NAN)
{}
Angles::Angles (double p, double t)
: phi (p),
theta (t)
Angles::Angles (double azimuth, double inclination)
: m_azimuth (azimuth),
m_inclination (inclination)
{
NormalizeAngles ();
}
Angles::Angles (Vector v)
: phi (std::atan2 (v.y, v.x)),
theta (std::acos (v.z / v.GetLength ()))
: m_azimuth (std::atan2 (v.y, v.x)),
m_inclination (std::acos (v.z / v.GetLength ()))
{
// azimuth and inclination angles for zero-length vectors are not defined
if (v.x == 0.0 && v.y == 0.0 && v.z == 0.0)
{
m_azimuth = NAN;
m_inclination = NAN;
}
NormalizeAngles ();
}
Angles::Angles (Vector v, Vector o)
: phi (std::atan2 (v.y - o.y, v.x - o.x)),
theta (std::acos ((v.z - o.z) / CalculateDistance (v, o)))
: Angles (v - o)
{}
void
Angles::SetAzimuth (double azimuth)
{
m_azimuth = azimuth;
NormalizeAngles ();
}
void
Angles::SetInclination (double inclination)
{
m_inclination = inclination;
NormalizeAngles ();
}
double
Angles::GetAzimuth (void) const
{
return m_azimuth;
}
double
Angles::GetInclination (void) const
{
return m_inclination;
}
void
Angles::NormalizeAngles (void)
{
CheckIfValid ();
// Normalize azimuth angle
if (std::isnan (m_azimuth))
{
return;
}
m_azimuth = WrapToPi (m_azimuth);
}
void
Angles::CheckIfValid (void) const
{
if (std::isfinite (m_inclination) || std::isfinite (m_azimuth))
{
NS_ASSERT_MSG (0.0 <= m_inclination && m_inclination <= M_PI,
"m_inclination=" << m_inclination << " not valid, should be in [0, pi] rad");
}
else
{
// infinite or nan inclination or azimuth angle
NS_LOG_WARN ("Undefined angle: " << *this);
}
}
}

View File

@@ -23,6 +23,7 @@
#include <ns3/vector.h>
#include <vector>
namespace ns3 {
@@ -31,63 +32,107 @@ namespace ns3 {
* \brief converts degrees to radians
*
* \param degrees the angle in degrees
*
* \return the angle in radians
*/
double DegreesToRadians (double degrees);
/**
* \brief converts degrees to radians
*
* \param degrees the angles in degrees
* \return the angles in radians
*/
std::vector<double> DegreesToRadians (const std::vector<double> &degrees);
/**
* \brief converts radians to degrees
*
* \param radians the angle in radians
*
* \return the angle in degrees
*/
double RadiansToDegrees (double radians);
/**
* \brief converts radians to degrees
*
* struct holding the azimuth and inclination angles of spherical
* coordinates. The notation is the one used in "Antenna Theory - Analysis
* and Design", C.A. Balanis, Wiley, 2nd Ed., section 2.2 "Radiation
* pattern".
* This notation corresponds to the standard spherical coordinates, with phi
* \param radians the angles in radians
* \return the angles in degrees
*/
std::vector<double> RadiansToDegrees (const std::vector<double> &radians);
/**
* \brief Wrap angle in [0, 360)
*
* \param a the angle in degrees
* \return the wrapped angle in degrees
*/
double WrapTo360 (double a);
/**
* \brief Wrap angle in [-180, 180)
*
* \param a the angle in degrees
* \return the wrapped angle in degrees
*/
double WrapTo180 (double a);
/**
* \brief Wrap angle in [0, 2*M_PI)
*
* \param a the angle in radians
* \return the wrapped angle in radians
*/
double WrapTo2Pi (double a);
/**
* \brief Wrap angle in [-M_PI, M_PI)
*
* \param a the angle in radians
* \return the wrapped angle in radians
*/
double WrapToPi (double a);
/**
*
* Class holding the azimuth and inclination angles of spherical coordinates.
* The notation is the one used in "Antenna Theory - Analysis
* and Design", C.A. Balanis, Wiley, 2nd Ed., section 2.2 "Radiation pattern".
* This notation corresponds to the standard spherical coordinates, with azimuth
* measured counterclockwise in the x-y plane off the x-axis, and
* theta measured off the z-axis.
* inclination measured off the z-axis.
* Azimuth is consistently normalized to be in [-M_PI, M_PI).
*
* ^
* z |
* |_ theta
* |_ inclination
* | \
* | /|
* |/ | y
* +-------->
* / \|
* /___/
* x / phi
* x / azimuth
* |/
*
*/
struct Angles
class Angles
{
public:
/**
* default constructor, will initialize phi and theta to zero
* This constructor allows to specify azimuth and inclination.
* Inclination must be in [0, M_PI], while azimuth is
* automatically notmalized in [-M_PI, M_PI)
*
* \param azimuth the azimuth angle in radians
* \param inclination the inclination angle in radians
*/
Angles ();
Angles (double azimuth, double inclination);
/**
* this constructor allows to specify phi and theta
*
* \param phi the azimuth angle in radians
* \param theta the inclination angle in radians
*
*/
Angles (double phi, double theta);
/**
* this constructor will initialize phi and theta by converting the
* This constructor will initialize azimuth and inclination by converting the
* given 3D vector from cartesian coordinates to spherical coordinates
* Note: azimuth and inclination angles for a zero-length vector are not defined
* and are thus initialized to NAN
*
* \param v the 3D vector in cartesian coordinates
*
@@ -95,7 +140,7 @@ struct Angles
Angles (Vector v);
/**
* this constructor initializes an Angles instance with the angles
* This constructor initializes an Angles instance with the angles
* of the spherical coordinates of point v respect to point o
*
* \param v the point (in cartesian coordinates) for which the angles are determined
@@ -105,40 +150,69 @@ struct Angles
Angles (Vector v, Vector o);
/**
* the azimuth angle in radians
* Setter for azimuth angle
*
* \param azimuth angle in radians
*/
double phi;
void SetAzimuth (double azimuth);
/**
* the inclination angle in radians
* Setter for inclination angle
*
* \param inclination angle in radians. Must be in [0, M_PI]
*/
double theta;
void SetInclination (double inclination);
/**
* Getter for azimuth angle
*
* \return azimuth angle in radians
*/
double GetAzimuth (void) const;
/**
* Getter for inclination angle
*
* \return inclination angle in radians
*/
double GetInclination (void) const;
// friend methods
friend std::ostream& operator<< (std::ostream& os, const Angles& a);
friend std::istream& operator>> (std::istream& is, Angles& a);
static bool m_printDeg; //!< flag for printing in radians or degrees units
private:
/**
* Default constructor is disabled
*/
Angles ();
/**
* Normalize the angle azimuth angle range between in [-M_PI, M_PI)
* while checking if the angle is valid, i.e., finite and within
* the bounds.
*
* Note: while an arbitrary value for the azimuth angle is valid
* and can be wrapped in [-M_PI, M_PI), an inclination angle outside
* the [0, M_PI] range can be ambiguos and is thus not valid.
*/
void NormalizeAngles (void);
/**
* Check if Angle is valid or not
* Warns the user if the Angle is undefined (non-finite azimuth or inclination),
* throws an assert if the inclination angle is invalid (not in [0, M_PI])
*/
void CheckIfValid (void) const;
double m_azimuth; //!< the azimuth angle in radians
double m_inclination; //!< the inclination angle in radians
};
/**
* print a struct Angles to output
*
* \param os the output stream
* \param a the Angles struct
*
* \return a reference to the output stream
*/
std::ostream& operator<< ( std::ostream& os, const Angles& a);
/**
* initialize a struct Angles from input
*
* \param is the input stream
* \param a the Angles struct
*
* \return a reference to the input stream
*/
std::istream &operator >> (std::istream &is, Angles &a);
} // namespace ns3
#endif // ANGLES_H

View File

@@ -41,12 +41,18 @@ CosineAntennaModel::GetTypeId ()
.SetParent<AntennaModel> ()
.SetGroupName ("Antenna")
.AddConstructor<CosineAntennaModel> ()
.AddAttribute ("Beamwidth",
"The 3dB beamwidth (degrees)",
DoubleValue (60),
MakeDoubleAccessor (&CosineAntennaModel::SetBeamwidth,
&CosineAntennaModel::GetBeamwidth),
MakeDoubleChecker<double> (0, 180))
.AddAttribute ("VerticalBeamwidth",
"The 3 dB vertical beamwidth (degrees). A beamwidth of 360 deg corresponds to constant gain",
DoubleValue (360),
MakeDoubleAccessor (&CosineAntennaModel::SetVerticalBeamwidth,
&CosineAntennaModel::GetVerticalBeamwidth),
MakeDoubleChecker<double> (0, 360))
.AddAttribute ("HorizontalBeamwidth",
"The 3 dB horizontal beamwidth (degrees). A beamwidth of 360 deg corresponds to constant gain",
DoubleValue (120),
MakeDoubleAccessor (&CosineAntennaModel::SetHorizontalBeamwidth,
&CosineAntennaModel::GetHorizontalBeamwidth),
MakeDoubleChecker<double> (0, 360))
.AddAttribute ("Orientation",
"The angle (degrees) that expresses the orientation of the antenna on the x-y plane relative to the x axis",
DoubleValue (0.0),
@@ -62,20 +68,73 @@ CosineAntennaModel::GetTypeId ()
return tid;
}
void
CosineAntennaModel::SetBeamwidth (double beamwidthDegrees)
{
NS_LOG_FUNCTION (this << beamwidthDegrees);
m_beamwidthRadians = DegreesToRadians (beamwidthDegrees);
m_exponent = -3.0 / (20 * std::log10 (std::cos (m_beamwidthRadians / 4.0)));
NS_LOG_LOGIC (this << " m_exponent = " << m_exponent);
}
double
CosineAntennaModel::GetBeamwidth () const
CosineAntennaModel::GetExponentFromBeamwidth (double beamwidthDegrees)
{
return RadiansToDegrees (m_beamwidthRadians);
NS_LOG_FUNCTION (beamwidthDegrees);
// The formula in obtained by inverting the power pattern P(alpha) in a single direction,
// while imposing that P(alpha0/2) = 0.5 = -3 dB, with respect to the exponent
// See CosineAntennaModel::GetGainDb for more information.
//
// The undetermined case of alpha0=360 is treated separately.
double exponent;
if (beamwidthDegrees == 360.0)
{
exponent = 0.0;
}
else
{
exponent = -3.0 / (20 * std::log10 (std::cos (DegreesToRadians (beamwidthDegrees / 4.0))));
}
return exponent;
}
double
CosineAntennaModel::GetBeamwidthFromExponent (double exponent)
{
NS_LOG_FUNCTION (exponent);
// The formula in obtained by inverting the power pattern P(alpha) in a single direction,
// while imposing that P(alpha0/2) = 0.5 = -3 dB, with respect to the beamwidth.
// See CosineAntennaModel::GetGainDb for more information.
double beamwidthRadians = 4 * std::acos (std::pow (0.5, 1 / (2 * exponent)));
return RadiansToDegrees (beamwidthRadians);
}
void
CosineAntennaModel::SetVerticalBeamwidth (double verticalBeamwidthDegrees)
{
NS_LOG_FUNCTION (this << verticalBeamwidthDegrees);
m_verticalExponent = GetExponentFromBeamwidth (verticalBeamwidthDegrees);
}
void
CosineAntennaModel::SetHorizontalBeamwidth (double horizontalBeamwidthDegrees)
{
NS_LOG_FUNCTION (this << horizontalBeamwidthDegrees);
m_horizontalExponent = GetExponentFromBeamwidth (horizontalBeamwidthDegrees);
}
double
CosineAntennaModel::GetVerticalBeamwidth () const
{
return GetBeamwidthFromExponent (m_verticalExponent);
}
double
CosineAntennaModel::GetHorizontalBeamwidth () const
{
return GetBeamwidthFromExponent (m_horizontalExponent);
}
void
CosineAntennaModel::SetOrientation (double orientationDegrees)
@@ -84,45 +143,35 @@ CosineAntennaModel::SetOrientation (double orientationDegrees)
m_orientationRadians = DegreesToRadians (orientationDegrees);
}
double
CosineAntennaModel::GetOrientation () const
{
return RadiansToDegrees (m_orientationRadians);
}
double
CosineAntennaModel::GetGainDb (Angles a)
{
NS_LOG_FUNCTION (this << a);
// azimuth angle w.r.t. the reference system of the antenna
double phi = a.phi - m_orientationRadians;
// make sure phi is in (-pi, pi]
while (phi <= -M_PI)
{
phi += M_PI+M_PI;
}
while (phi > M_PI)
{
phi -= M_PI+M_PI;
}
a.SetAzimuth (a.GetAzimuth () - m_orientationRadians);
NS_LOG_LOGIC ("phi = " << phi );
NS_LOG_LOGIC (a);
// element factor: amplitude gain of a single antenna element in linear units
double ef = std::pow (std::cos (phi / 2.0), m_exponent);
// The element power gain is computed as a product of cosine functions on the two axis
// The power pattern of the element is equal to:
// P(az,el) = cos(az/2)^2m * cos(pi/2 - incl/2)^2n,
// where az is the azimuth angle, and incl is the inclination angle.
double gain = (std::pow (std::cos (a.GetAzimuth () / 2), 2 * m_horizontalExponent)) *
(std::pow (std::cos ((M_PI / 2 - a.GetInclination ()) / 2), 2 * m_verticalExponent));
double gainDb = 10 * std::log10 (gain);
// the array factor is not considered. Note that if we did consider
// the array factor, the actual beamwidth would change, and in
// particular it would be different from the one specified by the
// user. Hence it is not desirable to use the array factor, for the
// ease of use of this model.
double gainDb = 20 * std::log10 (ef);
NS_LOG_LOGIC ("gain = " << gainDb << " + " << m_maxGain << " dB");
return gainDb + m_maxGain;
}
}

View File

@@ -31,45 +31,86 @@ namespace ns3 {
*
* \brief Cosine Antenna Model
*
* This class implements the cosine model as described in:
* This class implements the cosine model, similarly to what is described in:
* Cosine Antenna Element, Mathworks, Phased Array System Toolbox (Sep. 2020)
* Available online: https://www.mathworks.com/help/phased/ug/cosine-antenna-element.html
*
* Li Chunjian, "Efficient Antenna Patterns for Three-Sector WCDMA Systems"
* The power pattern of the element is equal to:
// P(az,el) = cos(az/2)^2m * cos(pi/2 - incl/2)^2n,
// where az is the azimuth angle, and incl is the inclination angle.
*
* Note that only the element factor of the above model is
* considered. Also, an additional constant gain is added to model the
* radiation pattern on the vertical plane (to account for the fact
* that the elevation angle is not included in the model).
* Differently from the source, the response is defined for azimuth and elevation angles
* between 180 and 180 degrees and is always positive.
* There is no response at the backside of a cosine antenna.
* The cosine response pattern achieves a maximum value of 1 (0 dB) at 0 degrees azimuth
* and 90 degrees inclination.
* An extra settable gain is added to the original model, to improve its generality.
*/
class CosineAntennaModel : public AntennaModel
{
public:
// inherited from Object
static TypeId GetTypeId ();
// inherited from AntennaModel
virtual double GetGainDb (Angles a);
/**
* Get the vertical 3 dB beamwidth of the cosine antenna model.
* \return the vertical beamwidth in degrees
*/
double GetVerticalBeamwidth (void) const;
// attribute getters/setters
void SetBeamwidth (double beamwidthDegrees);
double GetBeamwidth () const;
void SetOrientation (double orientationDegrees);
double GetOrientation () const;
/**
* Get the horizontal 3 dB beamwidth of the cosine antenna model.
* \return the horizontal beamwidth in degrees
*/
double GetHorizontalBeamwidth (void) const;
/**
* Get the horizontal orientation of the antenna element.
* \return the horizontal orientation in degrees
*/
double GetOrientation (void) const;
private:
/**
* this is the variable "n" in the paper by Chunjian
*
* Set the vertical 3 dB beamwidth (bilateral) of the cosine antenna model.
* \param verticalBeamwidthDegrees the vertical beamwidth in degrees
*/
double m_exponent;
void SetVerticalBeamwidth (double verticalBeamwidthDegrees);
double m_beamwidthRadians;
/**
* Set the horizontal 3 dB beamwidth (bilateral) of the cosine antenna model.
* \param horizontalBeamwidthDegrees the horizontal beamwidth in degrees
*/
void SetHorizontalBeamwidth (double horizontalBeamwidthDegrees);
double m_orientationRadians;
/**
* Set the horizontal orientation of the antenna element.
* \param orientationDegrees the horizontal orientation in degrees
*/
void SetOrientation (double orientationDegrees);
double m_maxGain;
/**
* Compute the exponent of the cosine antenna model from the beamwidth
* \param beamwidthRadians the beamwidth in degrees
* \return the exponent
*/
static double GetExponentFromBeamwidth (double beamwidthDegrees);
/**
* Compute the beamwidth of the cosine antenna model from the exponent
* \param exponent the exponent
* \return beamwidth in degrees
*/
static double GetBeamwidthFromExponent (double exponent);
double m_verticalExponent; //!< exponent of the vertical direction
double m_horizontalExponent; //!< exponent of the horizontal direction
double m_orientationRadians; //!< orientation in radians in the horizontal direction (bearing)
double m_maxGain; //!< antenna gain in dB towards the main orientation
};

View File

@@ -93,7 +93,7 @@ ParabolicAntennaModel::GetGainDb (Angles a)
{
NS_LOG_FUNCTION (this << a);
// azimuth angle w.r.t. the reference system of the antenna
double phi = a.phi - m_orientationRadians;
double phi = a.GetAzimuth () - m_orientationRadians;
// make sure phi is in (-pi, pi]
while (phi <= -M_PI)

View File

@@ -0,0 +1,169 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2020 University of Padova, Dep. of Information Engineering, SIGNET lab.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "phased-array-model.h"
#include <ns3/isotropic-antenna-model.h>
#include <ns3/log.h>
#include <ns3/double.h>
#include <ns3/uinteger.h>
#include <ns3/boolean.h>
#include <ns3/pointer.h>
namespace ns3 {
NS_LOG_COMPONENT_DEFINE ("PhasedArrayModel");
NS_OBJECT_ENSURE_REGISTERED (PhasedArrayModel);
std::ostream&
operator<< (std::ostream& os, const PhasedArrayModel::ComplexVector& cv)
{
size_t N = cv.size ();
// 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}
{}
PhasedArrayModel::~PhasedArrayModel ()
{
m_beamformingVector.clear ();
}
TypeId
PhasedArrayModel::GetTypeId ()
{
static TypeId tid = TypeId ("ns3::PhasedArrayModel")
.SetParent<Object> ()
.SetGroupName ("Antenna")
.AddAttribute ("AntennaElement",
"A pointer to the antenna element used by the phased array",
PointerValue (CreateObject<IsotropicAntennaModel> ()),
MakePointerAccessor (&PhasedArrayModel::m_antennaElement),
MakePointerChecker<AntennaModel> ())
;
return tid;
}
void
PhasedArrayModel::SetBeamformingVector (const ComplexVector &beamformingVector)
{
NS_LOG_FUNCTION (this << beamformingVector);
NS_ASSERT_MSG (beamformingVector.size () == GetNumberOfElements (),
beamformingVector.size () << " != " << GetNumberOfElements ());
m_beamformingVector = beamformingVector;
m_isBfVectorValid = true;
}
PhasedArrayModel::ComplexVector
PhasedArrayModel::GetBeamformingVector () 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;
}
double
PhasedArrayModel::ComputeNorm (const ComplexVector &vector)
{
double norm = 0;
for (uint64_t i = 0; i < vector.size (); i++)
{
norm += std::norm (vector[i]);
}
return std::sqrt (norm);
}
PhasedArrayModel::ComplexVector
PhasedArrayModel::GetBeamformingVector (Angles a) const
{
NS_LOG_FUNCTION (this << a);
ComplexVector beamformingVector = GetSteeringVector (a);
double norm = ComputeNorm (beamformingVector);
for (uint64_t i = 0; i < beamformingVector.size (); i++)
{
beamformingVector[i] = std::conj (beamformingVector[i]) / norm;
}
return beamformingVector;
}
PhasedArrayModel::ComplexVector
PhasedArrayModel::GetSteeringVector (Angles a) const
{
ComplexVector steeringVector;
steeringVector.resize (GetNumberOfElements ());
for (uint64_t i = 0; i < GetNumberOfElements (); i++)
{
Vector loc = GetElementLocation (i);
double phase = -2 * M_PI * (sin (a.GetInclination ()) * cos (a.GetAzimuth ()) * loc.x +
sin (a.GetInclination ()) * sin (a.GetAzimuth ()) * loc.y +
cos (a.GetInclination ()) * loc.z);
steeringVector[i] = std::polar<double> (1.0, phase);
}
return steeringVector;
}
void
PhasedArrayModel::SetAntennaElement (Ptr<AntennaModel> antennaElement)
{
NS_LOG_FUNCTION (this);
m_antennaElement = antennaElement;
}
Ptr<const AntennaModel>
PhasedArrayModel::GetAntennaElement () const
{
NS_LOG_FUNCTION (this);
return m_antennaElement;
}
} /* namespace ns3 */

View File

@@ -0,0 +1,145 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2020 University of Padova, Dep. of Information Engineering, SIGNET lab.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef PHASED_ARRAY_MODEL_H
#define PHASED_ARRAY_MODEL_H
#include <ns3/object.h>
#include <ns3/angles.h>
#include <complex>
#include <ns3/antenna-model.h>
namespace ns3 {
/**
* \ingroup antenna
*
* \brief Class implementing the phased array model virtual base class.
*/
class PhasedArrayModel : public Object
{
public:
/**
* Constructor
*/
PhasedArrayModel (void);
/**
* Destructor
*/
virtual ~PhasedArrayModel (void);
// inherited from Object
static TypeId GetTypeId (void);
typedef std::vector<std::complex<double> > ComplexVector; //!< type definition for complex vectors
/**
* Returns the horizontal and vertical components of the antenna element field
* pattern at the specified direction. Only vertical 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<double, double> GetElementFieldPattern (Angles a) const = 0;
/**
* Returns the location of the antenna element with the specified
* index, normalized with respect to the wavelength.
* \param idx index of the antenna element
* \return the 3D vector that represents the position of the element
*/
virtual Vector GetElementLocation (uint64_t index) const = 0;
/**
* Returns the number of antenna elements
* \return the number of antenna elements
*/
virtual uint64_t GetNumberOfElements (void) const = 0;
/**
* Sets the beamforming vector to be used
* \param beamformingVector the beamforming vector
*/
void SetBeamformingVector (const ComplexVector &beamformingVector);
/**
* Returns the beamforming vector that is currently being used
* \return the current beamforming vector
*/
ComplexVector GetBeamformingVector (void) const;
/**
* Returns the beamforming vector that points towards the specified position
* \param a the beamforming angle
* \return the beamforming vector
*/
ComplexVector GetBeamformingVector (Angles a) const;
/**
* Returns the steering vector that points toward the specified position
* \param a the steering angle
* \return the steering vector
*/
ComplexVector GetSteeringVector (Angles a) const;
/**
* Sets the antenna model to be used
* \param antennaElement the antenna model
*/
void SetAntennaElement (Ptr<AntennaModel> antennaElement);
/**
* Returns a pointer to the AntennaModel instance used to model the elements of the array
* \return pointer to the AntennaModel instance
*/
Ptr<const AntennaModel> GetAntennaElement (void) const;
protected:
/**
* Utility method to compute the euclidean norm of a ComplexVector
* \param vector the ComplexVector
* \return the euclidean norm
*/
static double ComputeNorm (const ComplexVector &vector);
ComplexVector m_beamformingVector; //!< the beamforming vector in use
Ptr<AntennaModel> m_antennaElement; //!< the model of the antenna element in use
bool m_isBfVectorValid; //!< ensures the validity of the beamforming vector
};
std::ostream& operator<< (std::ostream& os, const PhasedArrayModel::ComplexVector& cv);
} /* namespace ns3 */
#endif /* PHASED_ARRAY_MODEL_H */

View File

@@ -1,221 +0,0 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2019 SIGNET Lab, Department of Information Engineering,
* University of Padova
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "three-gpp-antenna-array-model.h"
#include "ns3/log.h"
#include "ns3/double.h"
#include "ns3/uinteger.h"
#include "ns3/boolean.h"
namespace ns3 {
NS_LOG_COMPONENT_DEFINE ("ThreeGppAntennaArrayModel");
NS_OBJECT_ENSURE_REGISTERED (ThreeGppAntennaArrayModel);
ThreeGppAntennaArrayModel::ThreeGppAntennaArrayModel (void)
{
NS_LOG_FUNCTION (this);
m_isOmniTx = false;
}
ThreeGppAntennaArrayModel::~ThreeGppAntennaArrayModel (void)
{
NS_LOG_FUNCTION (this);
}
TypeId
ThreeGppAntennaArrayModel::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::ThreeGppAntennaArrayModel")
.SetParent<Object> ()
.AddConstructor<ThreeGppAntennaArrayModel> ()
.AddAttribute ("AntennaHorizontalSpacing",
"Horizontal spacing between antenna elements, in multiples of wave length",
DoubleValue (0.5),
MakeDoubleAccessor (&ThreeGppAntennaArrayModel::m_disH),
MakeDoubleChecker<double> ())
.AddAttribute ("AntennaVerticalSpacing",
"Vertical spacing between antenna elements, in multiples of wave length",
DoubleValue (0.5),
MakeDoubleAccessor (&ThreeGppAntennaArrayModel::m_disV),
MakeDoubleChecker<double> ())
.AddAttribute ("NumColumns",
"Horizontal size of the array",
UintegerValue (4),
MakeUintegerAccessor (&ThreeGppAntennaArrayModel::m_numColumns),
MakeUintegerChecker<uint32_t> ())
.AddAttribute ("NumRows",
"Vertical size of the array",
UintegerValue (4),
MakeUintegerAccessor (&ThreeGppAntennaArrayModel::m_numRows),
MakeUintegerChecker<uint32_t> ())
.AddAttribute ("BearingAngle",
"The bearing angle in radians",
DoubleValue (0.0),
MakeDoubleAccessor (&ThreeGppAntennaArrayModel::m_alpha),
MakeDoubleChecker<double> (-M_PI, M_PI))
.AddAttribute ("DowntiltAngle",
"The downtilt angle in radians",
DoubleValue (0.0),
MakeDoubleAccessor (&ThreeGppAntennaArrayModel::m_beta),
MakeDoubleChecker<double> (0, M_PI))
.AddAttribute ("ElementGain",
"Directional gain of an antenna element in dBi",
DoubleValue (4.97),
MakeDoubleAccessor (&ThreeGppAntennaArrayModel::m_gE),
MakeDoubleChecker<double> (0, 8))
.AddAttribute ("IsotropicElements",
"If true, use an isotropic radiation pattern (for testing purposes)",
BooleanValue (false),
MakeBooleanAccessor (&ThreeGppAntennaArrayModel::m_isIsotropic),
MakeBooleanChecker ())
;
return tid;
}
bool
ThreeGppAntennaArrayModel::IsOmniTx (void) const
{
NS_LOG_FUNCTION (this);
return m_isOmniTx;
}
void
ThreeGppAntennaArrayModel::ChangeToOmniTx (void)
{
NS_LOG_FUNCTION (this);
m_isOmniTx = true;
}
void
ThreeGppAntennaArrayModel::SetBeamformingVector (const ComplexVector &beamformingVector)
{
NS_LOG_FUNCTION (this);
m_isOmniTx = false;
m_beamformingVector = beamformingVector;
}
const ThreeGppAntennaArrayModel::ComplexVector &
ThreeGppAntennaArrayModel::GetBeamformingVector(void) const
{
NS_LOG_FUNCTION (this);
return m_beamformingVector;
}
std::pair<double, double>
ThreeGppAntennaArrayModel::GetElementFieldPattern (Angles a) const
{
NS_LOG_FUNCTION (this);
// normalize phi (if needed)
a.phi = fmod (a.phi + M_PI, 2 * M_PI);
if (a.phi < 0)
a.phi += M_PI;
else
a.phi -= M_PI;
NS_ASSERT_MSG (a.theta >= 0 && a.theta <= M_PI, "The vertical angle should be between 0 and M_PI");
NS_ASSERT_MSG (a.phi >= -M_PI && a.phi <= M_PI, "The horizontal angle should be between -M_PI and M_PI");
// 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
double thetaPrime = std::acos (cos (m_beta)*cos (a.theta) + sin (m_beta)*cos (a.phi-m_alpha)*sin (a.theta));
double phiPrime = std::arg (std::complex<double> (cos (m_beta)*sin (a.theta)*cos (a.phi-m_alpha) - sin (m_beta)*cos (a.theta), sin (a.phi-m_alpha)*sin (a.theta)));
NS_LOG_DEBUG (a.theta << " " << thetaPrime << " " << a.phi << " " << phiPrime);
double aPrimeDb = GetRadiationPattern (thetaPrime, phiPrime);
double aPrime = pow (10, aPrimeDb / 10); // convert to linear
// compute psi using eq. 7.1-15 in 3GPP TR 38.901, assuming that the slant
// angle (gamma) is 0
double psi = std::arg (std::complex<double> (cos (m_beta) * sin (a.theta) - sin (m_beta) * cos (a.theta)* cos (a.phi - m_alpha), sin (m_beta)* sin (a.phi-m_alpha)));
NS_LOG_DEBUG ("psi " << psi);
// compute the antenna element field pattern in the vertical polarization using
// eq. 7.3-4 in 3GPP TR 38.901
// NOTE we assume vertical polarization, hence the field pattern in the
// horizontal polarization is 0
double fieldThetaPrime = std::sqrt (aPrime);
// convert the antenna element field pattern to GCS using eq. 7.1-11
// in 3GPP TR 38.901
double fieldTheta = cos (psi) * fieldThetaPrime;
double fieldPhi = sin (psi) * fieldThetaPrime;
NS_LOG_DEBUG (a.phi/M_PI*180 << " " << a.theta/M_PI*180 << " " << fieldTheta*fieldTheta + fieldPhi*fieldPhi);
return std::make_pair (fieldPhi, fieldTheta);
}
double
ThreeGppAntennaArrayModel::GetRadiationPattern (double thetaRadian, double phiRadian) const
{
if (m_isIsotropic)
{
return 0;
}
// convert the angles in degrees
double thetaDeg = thetaRadian * 180 / M_PI;
double phiDeg = phiRadian * 180 / M_PI;
NS_ASSERT_MSG (thetaDeg >= 0 && thetaDeg <= 180, "the vertical angle should be the range of [0,180]");
NS_ASSERT_MSG (phiDeg >= -180 && phiDeg <= 180, "the horizontal angle should be the range of [-180,180]");
// compute the radiation power pattern using equations in table 7.3-1 in
// 3GPP TR 38.901
double A_M = 30; // front-back ratio expressed in dB
double SLA = 30; // side-lobe level limit expressed in dB
double A_v = -1 * std::min (SLA,12 * pow ((thetaDeg - 90) / 65,2)); // vertical cut of the radiation power pattern (dB)
double A_h = -1 * std::min (A_M,12 * pow (phiDeg / 65,2)); // horizontal cut of the radiation power pattern (dB)
double A = m_gE - 1 * std::min (A_M,- A_v - A_h); // 3D radiation power pattern (dB)
return A; // 3D radiation power pattern in dB
}
Vector
ThreeGppAntennaArrayModel::GetElementLocation (uint64_t index) const
{
NS_LOG_FUNCTION (this);
// 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);
// convert the coordinates to the GCS using the rotation matrix 7.1-4 in 3GPP
// TR 38.901
Vector loc;
loc.x = cos(m_alpha)*cos (m_beta)*xPrime - sin (m_alpha)*yPrime + cos (m_alpha)*sin (m_beta)*zPrime;
loc.y = sin (m_alpha)*cos(m_beta)*xPrime + cos (m_alpha)*yPrime + sin (m_alpha)*sin (m_beta)*zPrime;
loc.z = -sin (m_beta)*xPrime+cos(m_beta)*zPrime;
return loc;
}
uint64_t
ThreeGppAntennaArrayModel::GetNumberOfElements (void) const
{
NS_LOG_FUNCTION (this);
return m_numRows * m_numColumns;
}
} /* namespace ns3 */

View File

@@ -1,135 +0,0 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2019 SIGNET Lab, Department of Information Engineering,
* University of Padova
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef THREE_GPP_ANTENNA_ARRAY_MODEL_H_
#define THREE_GPP_ANTENNA_ARRAY_MODEL_H_
#include <ns3/antenna-model.h>
#include <complex>
namespace ns3 {
/**
* \ingroup antenna
*
* \brief Class implementing the antenna model defined in 3GPP TR 38.901 V15.0.0
*
* \note the current implementation supports the modeling of antenna arrays
* composed of a single panel and with single (vertical) polarization.
*/
class ThreeGppAntennaArrayModel : public Object
{
public:
/**
* Constructor
*/
ThreeGppAntennaArrayModel (void);
/**
* Destructor
*/
virtual ~ThreeGppAntennaArrayModel (void);
// inherited from Object
static TypeId GetTypeId (void);
typedef std::vector<std::complex<double> > ComplexVector; //!< type definition for complex vectors
/**
* Returns the horizontal and vertical components of the antenna element field
* pattern at the specified direction. Only vertical 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
*/
std::pair<double, double> GetElementFieldPattern (Angles a) const;
/**
* Returns the location of the antenna element with the specified
* index assuming the left bottom corner is (0,0,0), normalized
* with respect to the wavelength.
* Antenna elements are scanned row by row, left to right and bottom to top.
* For example, an antenna with 2 rows and 3 columns will be ordered as follows:
* ^ z
* | 3 4 5
* | 0 1 2
* ----------> y
*
* \param index index of the antenna element
* \return the 3D vector that represents the position of the element
*/
virtual Vector GetElementLocation (uint64_t index) const;
/**
* Returns the number of antenna elements
* \return the number of antenna elements
*/
virtual uint64_t GetNumberOfElements (void) const;
/**
* Returns true if the antenna is configured for omnidirectional transmissions
* \return whether the transmission is set to omni
*/
bool IsOmniTx (void) const;
/**
* Change the antenna model to omnidirectional (ignoring the beams)
*/
void ChangeToOmniTx (void);
/**
* Sets the beamforming vector to be used
* \param beamformingVector the beamforming vector
*/
void SetBeamformingVector (const ComplexVector &beamformingVector);
/**
* Returns the beamforming vector that is currently being used
* \return the current beamforming vector
*/
const ComplexVector & GetBeamformingVector (void) const;
private:
/**
* Returns the radiation power pattern of a single antenna element in dB,
* generated according to Table 7.3-1 in 3GPP TR 38.901
* \param vAngleRadian the vertical angle in radians
* \param hAngleRadian the horizontal angle in radians
* \return the radiation power pattern in dB
*/
double GetRadiationPattern (double vAngleRadian, double hAngleRadian) const;
bool m_isOmniTx; //!< true if the antenna is configured for omni transmissions
ComplexVector m_beamformingVector; //!< the beamforming vector in use
uint32_t m_numColumns; //!< number of columns
uint32_t m_numRows; //!< number of rows
double m_disV; //!< antenna spacing in the vertical direction in multiples of wave length
double m_disH; //!< antenna spacing in the horizontal direction in multiples of wave length
double m_alpha; //!< the bearing angle in radians
double m_beta; //!< the downtilt angle in radians
double m_gE; //!< directional gain of a single antenna element (dBi)
bool m_isIsotropic; //!< if true, antenna elements are isotropic
};
} /* namespace ns3 */
#endif /* SRC_THREE_GPP_ANTENNA_ARRAY_MODEL_H_ */

View File

@@ -0,0 +1,118 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2020 University of Padova, Dep. of Information Engineering, SIGNET lab.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <ns3/log.h>
#include <ns3/double.h>
#include <cmath>
#include "antenna-model.h"
#include "three-gpp-antenna-model.h"
namespace ns3 {
NS_LOG_COMPONENT_DEFINE ("ThreeGppAntennaModel");
NS_OBJECT_ENSURE_REGISTERED (ThreeGppAntennaModel);
TypeId
ThreeGppAntennaModel::GetTypeId ()
{
static TypeId tid = TypeId ("ns3::ThreeGppAntennaModel")
.SetParent<AntennaModel> ()
.SetGroupName ("Antenna")
.AddConstructor<ThreeGppAntennaModel> ()
;
return tid;
}
ThreeGppAntennaModel::ThreeGppAntennaModel (void)
: m_verticalBeamwidthDegrees {65},
m_horizontalBeamwidthDegrees {65},
m_aMax {30},
m_slaV {30},
m_geMax {8.0}
{}
ThreeGppAntennaModel::~ThreeGppAntennaModel (void)
{}
double
ThreeGppAntennaModel::GetVerticalBeamwidth () const
{
return m_verticalBeamwidthDegrees;
}
double
ThreeGppAntennaModel::GetHorizontalBeamwidth () const
{
return m_horizontalBeamwidthDegrees;
}
double
ThreeGppAntennaModel::GetSlaV () const
{
return m_slaV;
}
double
ThreeGppAntennaModel::GetMaxAttenuation () const
{
return m_aMax;
}
double
ThreeGppAntennaModel::GetAntennaElementGain () const
{
return m_geMax;
}
double
ThreeGppAntennaModel::GetGainDb (Angles a)
{
NS_LOG_FUNCTION (this << a);
double phiDeg = RadiansToDegrees (a.GetAzimuth ());
double thetaDeg = RadiansToDegrees (a.GetInclination ());
NS_ASSERT_MSG (-180.0 <= phiDeg && phiDeg <= 180.0, "Out of boundaries: phiDeg=" << phiDeg);
NS_ASSERT_MSG (0.0 <= thetaDeg && thetaDeg <= 180.0, "Out of boundaries: thetaDeg=" << thetaDeg);
// compute the radiation power pattern using equations in table 7.3-1 in
// 3GPP TR 38.901
double vertGain = -std::min (m_slaV, 12 * pow ((thetaDeg - 90) / m_verticalBeamwidthDegrees, 2)); // vertical cut of the radiation power pattern (dB)
double horizGain = -std::min (m_aMax, 12 * pow (phiDeg / m_horizontalBeamwidthDegrees, 2)); // horizontal cut of the radiation power pattern (dB)
double gainDb = m_geMax - std::min (m_aMax, -(vertGain + horizGain)); // 3D radiation power pattern (dB)
NS_LOG_DEBUG ("gain=" << gainDb << " dB");
return gainDb;
}
}

View File

@@ -0,0 +1,90 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2020 University of Padova, Dep. of Information Engineering, SIGNET lab.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef THREE_GPP_ANTENNA_MODEL_H
#define THREE_GPP_ANTENNA_MODEL_H
#include <ns3/object.h>
#include <ns3/antenna-model.h>
namespace ns3 {
/**
*
* \brief Antenna model based on a parabolic approximation of the main lobe radiation pattern.
*
* This class implements the parabolic model as described in 3GPP TR 38.901 v15.0.0
*
*/
class ThreeGppAntennaModel : public AntennaModel
{
public:
ThreeGppAntennaModel (void);
virtual ~ThreeGppAntennaModel (void) override;
// inherited from Object
static TypeId GetTypeId ();
// inherited from AntennaModel
virtual double GetGainDb (Angles a) override;
/**
* Get the vertical beamwidth of the antenna element.
* \return the vertical beamwidth in degrees
*/
double GetVerticalBeamwidth () const;
/**
* Get the horizontal beamwidth of the antenna element.
* \return the horizontal beamwidth in degrees
*/
double GetHorizontalBeamwidth () const;
/**
* Get the side-lobe attenuation in the vertical direction of the antenna element.
* \return side-lobe attenuation in the vertical direction in dB
*/
double GetSlaV () const;
/**
* Get the naximum attenuation of the antenna element.
* \return the naximum attenuation in dB
*/
double GetMaxAttenuation () const;
/**
* Get the maximum directional gain of the antenna element.
* \return the maximum directional gain in dBi
*/
double GetAntennaElementGain () const;
private:
double m_verticalBeamwidthDegrees; //!< beamwidth in the vertical direction (\theta_{3dB}) [deg]
double m_horizontalBeamwidthDegrees; //!< beamwidth in the horizontal direction (\phi_{3dB}) [deg]
double m_aMax; //!< maximum attenuation (A_{max}) [dB]
double m_slaV; //!< side-lobe attenuation in the vertical direction (SLA_V) [dB]
double m_geMax; //!< maximum directional gain of the antenna element (G_{E,max}) [dBi]
};
} // namespace ns3
#endif // THREE_GPP_ANTENNA_MODEL_H

View File

@@ -0,0 +1,233 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2020 University of Padova, Dep. of Information Engineering, SIGNET lab.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "uniform-planar-array.h"
#include <ns3/log.h>
#include <ns3/double.h>
#include <ns3/uinteger.h>
#include <ns3/boolean.h>
namespace ns3 {
NS_LOG_COMPONENT_DEFINE ("UniformPlanarArray");
NS_OBJECT_ENSURE_REGISTERED (UniformPlanarArray);
UniformPlanarArray::UniformPlanarArray ()
: PhasedArrayModel (),
m_numColumns {1},
m_numRows {1},
m_disV {0.5},
m_disH {0.5},
m_alpha {0},
m_beta {0}
{}
UniformPlanarArray::~UniformPlanarArray ()
{}
TypeId
UniformPlanarArray::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::UniformPlanarArray")
.SetParent<PhasedArrayModel> ()
.AddConstructor<UniformPlanarArray> ()
.SetGroupName ("Antenna")
.AddAttribute ("AntennaHorizontalSpacing",
"Horizontal spacing between antenna elements, in multiples of wave length",
DoubleValue (0.5),
MakeDoubleAccessor (&UniformPlanarArray::SetAntennaHorizontalSpacing,
&UniformPlanarArray::GetAntennaHorizontalSpacing),
MakeDoubleChecker<double> (0.0))
.AddAttribute ("AntennaVerticalSpacing",
"Vertical spacing between antenna elements, in multiples of wave length",
DoubleValue (0.5),
MakeDoubleAccessor (&UniformPlanarArray::SetAntennaVerticalSpacing,
&UniformPlanarArray::GetAntennaVerticalSpacing),
MakeDoubleChecker<double> (0.0))
.AddAttribute ("NumColumns",
"Horizontal size of the array",
UintegerValue (4),
MakeUintegerAccessor (&UniformPlanarArray::SetNumColumns,
&UniformPlanarArray::GetNumColumns),
MakeUintegerChecker<uint32_t> (1))
.AddAttribute ("NumRows",
"Vertical size of the array",
UintegerValue (4),
MakeUintegerAccessor (&UniformPlanarArray::SetNumRows,
&UniformPlanarArray::GetNumRows),
MakeUintegerChecker<uint32_t> (1))
.AddAttribute ("BearingAngle",
"The bearing angle in radians",
DoubleValue (0.0),
MakeDoubleAccessor (&UniformPlanarArray::m_alpha),
MakeDoubleChecker<double> (-M_PI, M_PI))
.AddAttribute ("DowntiltAngle",
"The downtilt angle in radians",
DoubleValue (0.0),
MakeDoubleAccessor (&UniformPlanarArray::m_beta),
MakeDoubleChecker<double> (-M_PI, M_PI))
;
return tid;
}
void
UniformPlanarArray::SetNumColumns (uint32_t n)
{
NS_LOG_FUNCTION (this << n);
if (n != m_numColumns)
{
m_isBfVectorValid = false;
}
m_numColumns = n;
}
uint32_t
UniformPlanarArray::GetNumColumns (void) const
{
return m_numColumns;
}
void
UniformPlanarArray::SetNumRows (uint32_t n)
{
NS_LOG_FUNCTION (this << n);
if (n != m_numRows)
{
m_isBfVectorValid = false;
}
m_numRows = n;
}
uint32_t
UniformPlanarArray::GetNumRows (void) const
{
return m_numRows;
}
void
UniformPlanarArray::SetAntennaHorizontalSpacing (double s)
{
NS_LOG_FUNCTION (this << s);
NS_ABORT_MSG_IF (s <= 0, "Trying to set an invalid spacing: " << s);
if (s != m_disH)
{
m_isBfVectorValid = false;
}
m_disH = s;
}
double
UniformPlanarArray::GetAntennaHorizontalSpacing (void) const
{
return m_disH;
}
void
UniformPlanarArray::SetAntennaVerticalSpacing (double s)
{
NS_LOG_FUNCTION (this << s);
NS_ABORT_MSG_IF (s <= 0, "Trying to set an invalid spacing: " << s);
if (s != m_disV)
{
m_isBfVectorValid = false;
}
m_disV = s;
}
double
UniformPlanarArray::GetAntennaVerticalSpacing (void) const
{
return m_disV;
}
std::pair<double, double>
UniformPlanarArray::GetElementFieldPattern (Angles a) const
{
NS_LOG_FUNCTION (this << a);
// 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
double thetaPrime = std::acos (cos (m_beta) * cos (a.GetInclination ()) + sin (m_beta) * cos (a.GetAzimuth () - m_alpha) * sin (a.GetInclination ()));
double phiPrime = std::arg (std::complex<double> (cos (m_beta) * sin (a.GetInclination ()) * cos (a.GetAzimuth () - m_alpha) - sin (m_beta) * cos (a.GetInclination ()), sin (a.GetAzimuth () - m_alpha) * sin (a.GetInclination ())));
Angles aPrime (phiPrime, thetaPrime);
NS_LOG_DEBUG (a << " -> " << aPrime);
// compute the antenna element field pattern in the vertical polarization using
// eq. 7.3-4 in 3GPP TR 38.901
// NOTE we assume vertical polarization, hence the field pattern in the
// horizontal polarization is 0
double aPrimeDb = m_antennaElement->GetGainDb (aPrime);
double fieldThetaPrime = pow (10, aPrimeDb / 20); // convert to linear magnitude
// compute psi using eq. 7.1-15 in 3GPP TR 38.901, assuming that the slant
// angle (gamma) is 0
double psi = std::arg (std::complex<double> (cos (m_beta) * sin (a.GetInclination ()) - sin (m_beta) * cos (a.GetInclination ()) * cos (a.GetAzimuth () - m_alpha), sin (m_beta) * sin (a.GetAzimuth () - m_alpha)));
NS_LOG_DEBUG ("psi " << psi);
// convert the antenna element field pattern to GCS using eq. 7.1-11
// in 3GPP TR 38.901
double fieldTheta = cos (psi) * fieldThetaPrime;
double fieldPhi = sin (psi) * fieldThetaPrime;
NS_LOG_DEBUG (RadiansToDegrees (a.GetAzimuth ()) << " " << RadiansToDegrees (a.GetInclination ()) << " " << fieldTheta * fieldTheta + fieldPhi * fieldPhi);
return std::make_pair (fieldPhi, fieldTheta);
}
Vector
UniformPlanarArray::GetElementLocation (uint64_t index) const
{
NS_LOG_FUNCTION (this << index);
// 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);
// convert the coordinates to the GCS using the rotation matrix 7.1-4 in 3GPP
// TR 38.901
Vector loc;
loc.x = cos (m_alpha) * cos (m_beta) * xPrime - sin (m_alpha) * yPrime + cos (m_alpha) * sin (m_beta) * zPrime;
loc.y = sin (m_alpha) * cos (m_beta) * xPrime + cos (m_alpha) * yPrime + sin (m_alpha) * sin (m_beta) * zPrime;
loc.z = -sin (m_beta) * xPrime + cos (m_beta) * zPrime;
return loc;
}
uint64_t
UniformPlanarArray::GetNumberOfElements () const
{
return m_numRows * m_numColumns;
}
} /* namespace ns3 */

View File

@@ -0,0 +1,166 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2020 University of Padova, Dep. of Information Engineering, SIGNET lab.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef UNIFORM_PLANAR_ARRAY_H
#define UNIFORM_PLANAR_ARRAY_H
#include <ns3/object.h>
#include <ns3/phased-array-model.h>
namespace ns3 {
/**
* \ingroup antenna
*
* \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 (vertical) polarization.
*/
class UniformPlanarArray : public PhasedArrayModel
{
public:
/**
* Constructor
*/
UniformPlanarArray (void);
/**
* Destructor
*/
virtual ~UniformPlanarArray (void);
// inherited from Object
static TypeId GetTypeId (void);
/**
* Returns the horizontal and vertical components of the antenna element field
* pattern at the specified direction. Only vertical 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
*/
std::pair<double, double> GetElementFieldPattern (Angles a) const override;
/**
* Returns the location of the antenna element with the specified
* index assuming the left bottom corner is (0,0,0), normalized
* with respect to the wavelength.
* Antenna elements are scanned row by row, left to right and bottom to top.
* For example, an antenna with 2 rows and 3 columns will be ordered as follows:
* ^ z
* | 3 4 5
* | 0 1 2
* ----------> y
*
* \param index index of the antenna 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
* \return the number of antenna elements
*/
uint64_t GetNumberOfElements (void) 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);
/**
* Get the number of columns of the phased array
* \return the number of columns
*/
uint32_t GetNumColumns (void) const;
/**
* Set the number of rows 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 rows
*/
void SetNumRows (uint32_t n);
/**
* Get the number of rows of the phased array
* \return the number of rows
*/
uint32_t GetNumRows (void) const;
/**
* Set the horizontal spacing for the antenna elements of the phased array
* This method resets the stored beamforming vector to a ComplexVector
* of the correct size, but zero-filled
* \param s the horizontal spacing in multiples of wavelength
*/
void SetAntennaHorizontalSpacing (double s);
/**
* Get the horizontal spacing for the antenna elements of the phased array
* \return the horizontal spacing in multiples of wavelength
*/
double GetAntennaHorizontalSpacing (void) const;
/**
* Set the vertical spacing for the antenna elements of the phased array
* This method resets the stored beamforming vector to a ComplexVector
* of the correct size, but zero-filled
* \param s the vertical spacing in multiples of wavelength
*/
void SetAntennaVerticalSpacing (double s);
/**
* Get the vertical spacing for the antenna elements of the phased array
* \return the vertical spacing in multiples of wavelength
*/
double GetAntennaVerticalSpacing (void) const;
uint32_t m_numColumns; //!< number of columns
uint32_t m_numRows; //!< number of rows
double m_disV; //!< antenna spacing in the vertical direction in multiples of wave length
double m_disH; //!< antenna spacing in the horizontal direction in multiples of wave length
double m_alpha; //!< the bearing angle in radians
double m_beta; //!< the downtilt angle in radians
};
} /* namespace ns3 */
#endif /* UNIFORM_PLANAR_ARRAY_H */

View File

@@ -53,15 +53,14 @@ OneVectorConstructorTestCase::OneVectorConstructorTestCase (Vector v, Angles a)
: TestCase (BuildNameString (v)),
m_v (v),
m_a (a)
{
}
{}
void
OneVectorConstructorTestCase::DoRun ()
{
Angles a (m_v);
NS_TEST_EXPECT_MSG_EQ_TOL ( a.phi, m_a.phi, 1e-10, "incorrect phi");
NS_TEST_EXPECT_MSG_EQ_TOL ( a.theta, m_a.theta, 1e-10, "incorrect theta");
NS_TEST_EXPECT_MSG_EQ_TOL ( a.GetAzimuth (), m_a.GetAzimuth (), 1e-10, "incorrect phi");
NS_TEST_EXPECT_MSG_EQ_TOL ( a.GetInclination (), m_a.GetInclination (), 1e-10, "incorrect theta");
}
@@ -95,15 +94,14 @@ TwoVectorsConstructorTestCase::TwoVectorsConstructorTestCase (Vector v, Vector o
m_v (v),
m_o (o),
m_a (a)
{
}
{}
void
TwoVectorsConstructorTestCase::DoRun ()
{
Angles a (m_v, m_o);
NS_TEST_EXPECT_MSG_EQ_TOL ( a.phi, m_a.phi, 1e-10, "incorrect phi");
NS_TEST_EXPECT_MSG_EQ_TOL ( a.theta, m_a.theta, 1e-10, "incorrect theta");
NS_TEST_EXPECT_MSG_EQ_TOL ( a.GetAzimuth (), m_a.GetAzimuth (), 1e-10, "incorrect phi");
NS_TEST_EXPECT_MSG_EQ_TOL ( a.GetInclination (), m_a.GetInclination (), 1e-10, "incorrect theta");
}
@@ -206,6 +204,6 @@ AnglesTestSuite::AnglesTestSuite ()
AddTestCase (new TwoVectorsConstructorTestCase (Vector (0.5, 11.45, std::sqrt (2) - 1), Vector (-0.5, 12.45, -1), Angles (-M_PI_4, M_PI_4)), TestCase::QUICK);
};
}
static AnglesTestSuite staticAnglesTestSuiteInstance;

View File

@@ -59,7 +59,7 @@ private:
std::string CosineAntennaModelTestCase::BuildNameString (Angles a, double b, double o, double g)
{
std::ostringstream oss;
oss << "theta=" << a.theta << " , phi=" << a.phi
oss << "theta=" << a.GetInclination () << " , phi=" << a.GetAzimuth ()
<< ", beamdwidth=" << b << "deg"
<< ", orientation=" << o
<< ", maxGain=" << g << " dB";
@@ -84,7 +84,8 @@ CosineAntennaModelTestCase::DoRun ()
NS_LOG_FUNCTION (this << BuildNameString (m_a, m_b, m_o, m_g));
Ptr<CosineAntennaModel> a = CreateObject<CosineAntennaModel> ();
a->SetAttribute ("Beamwidth", DoubleValue (m_b));
a->SetAttribute ("HorizontalBeamwidth", DoubleValue (m_b));
a->SetAttribute ("VerticalBeamwidth", DoubleValue (m_b));
a->SetAttribute ("Orientation", DoubleValue (m_o));
a->SetAttribute ("MaxGain", DoubleValue (m_g));
double actualGain = a->GetGainDb (m_a);
@@ -118,88 +119,95 @@ CosineAntennaModelTestSuite::CosineAntennaModelTestSuite ()
// e.g., with a 60 deg beamwidth, gain is -20dB at +- 74.945 degrees from boresight
// phi, theta, beamwidth, orientation, maxGain, expectedGain, condition
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (0), 0), 60, 0, 0, 0, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (30), 0), 60, 0, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-30), 0), 60, 0, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-90), 0), 60, 0, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (90), 0), 60, 0, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (100), 0), 60, 0, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (150), 0), 60, 0, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (180), 0), 60, 0, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-100), 0), 60, 0, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-150), 0), 60, 0, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-180), 0), 60, 0, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (0), DegreesToRadians (90)), 60, 0, 0, 0, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (30), DegreesToRadians (90)), 60, 0, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-30), DegreesToRadians (90)), 60, 0, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-90), DegreesToRadians (90)), 60, 0, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (90), DegreesToRadians (90)), 60, 0, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (100), DegreesToRadians (90)), 60, 0, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (150), DegreesToRadians (90)), 60, 0, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (180), DegreesToRadians (90)), 60, 0, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-100), DegreesToRadians (90)), 60, 0, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-150), DegreesToRadians (90)), 60, 0, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-180), DegreesToRadians (90)), 60, 0, 0, -20, LESSTHAN), TestCase::QUICK);
// test positive orientation
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (60), 0), 60, 60, 0, 0, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (90), 0), 60, 60, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (30), 0), 60, 60, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-30), 0), 60, 60, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (150), 0), 60, 60, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (160), 0), 60, 60, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (210), 0), 60, 60, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (240), 0), 60, 60, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-40), 0), 60, 60, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-90), 0), 60, 60, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-120), 0), 60, 60, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (60), DegreesToRadians (90)), 60, 60, 0, 0, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (90), DegreesToRadians (90)), 60, 60, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (30), DegreesToRadians (90)), 60, 60, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-30), DegreesToRadians (90)), 60, 60, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (150), DegreesToRadians (90)), 60, 60, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (160), DegreesToRadians (90)), 60, 60, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (210), DegreesToRadians (90)), 60, 60, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (240), DegreesToRadians (90)), 60, 60, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-40), DegreesToRadians (90)), 60, 60, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-90), DegreesToRadians (90)), 60, 60, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-120), DegreesToRadians (90)), 60, 60, 0, -20, LESSTHAN), TestCase::QUICK);
// test negative orientation and different beamwidths
// with a 100 deg beamwidth, gain is -20dB at +- 117.47 degrees from boresight
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-150), 0), 100, -150, 0, 0, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-100), 0), 100, -150, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-200), 0), 100, -150, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-32.531), 0), 100, -150, 0, -20, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (92.531), 0), 100, -150, 0, -20, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-30), 0), 100, -150, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (0), 0), 100, -150, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (60), 0), 100, -150, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (90), 0), 100, -150, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (30), 0), 100, -150, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-150), DegreesToRadians (90)), 100, -150, 0, 0, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-100), DegreesToRadians (90)), 100, -150, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-200), DegreesToRadians (90)), 100, -150, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-32.531),DegreesToRadians (90)), 100, -150, 0, -20, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (92.531), DegreesToRadians (90)), 100, -150, 0, -20, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-30), DegreesToRadians (90)), 100, -150, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (0), DegreesToRadians (90)), 100, -150, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (60), DegreesToRadians (90)), 100, -150, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (90), DegreesToRadians (90)), 100, -150, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (30), DegreesToRadians (90)), 100, -150, 0, -20, LESSTHAN), TestCase::QUICK);
// with a 150 deg beamwidth, gain is -10dB at +- 124.93 degrees from boresight, and -20dB at +- 155.32 degrees from boresight
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-150), 0), 150, -150, 0, 0, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (135), 0), 150, -150, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-75), 0), 150, -150, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (85.070), 0), 150, -150, 0, -10, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-25.070), 0), 150, -150, 0, -10, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (5.3230), 0), 150, -150, 0, -20, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (54.677), 0), 150, -150, 0, -20, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (30), 0), 150, -150, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (20), 0), 150, -150, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-150), DegreesToRadians (90)), 150, -150, 0, 0, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (135), DegreesToRadians (90)), 150, -150, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-75), DegreesToRadians (90)), 150, -150, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (85.070), DegreesToRadians (90)), 150, -150, 0, -10, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-25.070),DegreesToRadians (90)), 150, -150, 0, -10, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (5.3230), DegreesToRadians (90)), 150, -150, 0, -20, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (54.677), DegreesToRadians (90)), 150, -150, 0, -20, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (30), DegreesToRadians (90)), 150, -150, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (20), DegreesToRadians (90)), 150, -150, 0, -20, LESSTHAN), TestCase::QUICK);
// test flat beam, with beamwidth=360 deg
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (0), DegreesToRadians (90)), 360, 0, 0, 0, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (180), DegreesToRadians (90)), 360, 0, 0, 0, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-180), DegreesToRadians (90)), 360, 0, 0, 0, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (0), DegreesToRadians (0)), 360, 0, 0, 0, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (0), DegreesToRadians(180)), 360, 0, 0, 0, EQUAL), TestCase::QUICK);
// test maxGain
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (0), 0), 60, 0, 10, 10, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (30), 0), 60, 0, 22, 19, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-30), 0), 60, 0, -4, -7, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-90), 0), 60, 0, 10, -10, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (90), 0), 60, 0, -20, -40, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (100), 0), 60, 0, 40, 20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-150), 0), 100, -150, 2, 2, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-100), 0), 100, -150, 4, 1, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-200), 0), 100, -150, -1, -4, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (0), DegreesToRadians (90)), 60, 0, 10, 10, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (30), DegreesToRadians (90)), 60, 0, 22, 19, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-30), DegreesToRadians (90)), 60, 0, -4, -7, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-90), DegreesToRadians (90)), 60, 0, 10, -10, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (90), DegreesToRadians (90)), 60, 0, -20, -40, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (100), DegreesToRadians (90)), 60, 0, 40, 20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-150), DegreesToRadians (90)), 100, -150, 2, 2, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-100), DegreesToRadians (90)), 100, -150, 4, 1, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-200), DegreesToRadians (90)), 100, -150, -1, -4, EQUAL), TestCase::QUICK);
// test elevation angle
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (0), 2), 60, 0, 0, 0, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (30), 2), 60, 0, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-30), 2), 60, 0, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-90), 2), 60, 0, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-180), 2), 60, 0, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (60), -3), 60, 60, 0, 0, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (90), -3), 60, 60, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (30), -3), 60, 60, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-120), -3), 60, 60, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-150), -3), 100, -150, 0, 0, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-100), -3), 100, -150, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-200), -3), 100, -150, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-30), -3), 100, -150, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (90), 9.5), 100, -150, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (0), 9.5), 60, 0, 10, 10, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (30), 9.5), 60, 0, 22, 19, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-30), 9.5), 60, 0, -4, -7, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (100), 9.5), 60, 0, 40, 20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-150), 9.5), 100, -150, 2, 2, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-100), 9.5), 100, -150, 4, 1, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-200), 9.5), 100, -150, -1, -4, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (0), DegreesToRadians (60)), 60, 0, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (30), DegreesToRadians (60)), 60, 0, 0, -6, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-30), DegreesToRadians (60)), 60, 0, 0, -6, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-90), DegreesToRadians (60)), 60, 0, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-180), DegreesToRadians (60)), 60, 0, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (60), DegreesToRadians (120)), 60, 60, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (90), DegreesToRadians (120)), 60, 60, 0, -6, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (30), DegreesToRadians (120)), 60, 60, 0, -6, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-120), DegreesToRadians (120)), 60, 60, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-150), DegreesToRadians (140)), 100, -150, 0, -3, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-100), DegreesToRadians (140)), 100, -150, 0, -6, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-200), DegreesToRadians (140)), 100, -150, 0, -6, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-30), DegreesToRadians (140)), 100, -150, 0, -20, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (0), DegreesToRadians (60)), 60, 0, 10, 7, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (30), DegreesToRadians (60)), 60, 0, 22, 16, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-30), DegreesToRadians (60)), 60, 0, -4, -10, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-90), DegreesToRadians (60)), 60, 0, 10, -13, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (90), DegreesToRadians (60)), 60, 0, -20, -43, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (100), DegreesToRadians (60)), 60, 0, 40, 17, LESSTHAN), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-150), DegreesToRadians (40)), 100, -150, 2, -1, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-100), DegreesToRadians (40)), 100, -150, 4, -2, EQUAL), TestCase::QUICK);
AddTestCase (new CosineAntennaModelTestCase (Angles (DegreesToRadians (-200), DegreesToRadians (40)), 100, -150, -1, -7, EQUAL), TestCase::QUICK);
};

View File

@@ -45,7 +45,7 @@ private:
std::string IsotropicAntennaModelTestCase::BuildNameString (Angles a)
{
std::ostringstream oss;
oss << "theta=" << a.theta << " , phi=" << a.phi;
oss << "theta=" << a.GetInclination () << " , phi=" << a.GetAzimuth ();
return oss.str ();
}

View File

@@ -59,7 +59,7 @@ private:
std::string ParabolicAntennaModelTestCase::BuildNameString (Angles a, double b, double o, double g)
{
std::ostringstream oss;
oss << "theta=" << a.theta << " , phi=" << a.phi
oss << "theta=" << a.GetInclination () << " , phi=" << a.GetAzimuth ()
<< ", beamdwidth=" << b << "deg"
<< ", orientation=" << o
<< ", maxAttenuation=" << g << " dB";
@@ -116,69 +116,69 @@ ParabolicAntennaModelTestSuite::ParabolicAntennaModelTestSuite ()
// with a 60 deg beamwidth, gain is -20dB at +-77.460 degrees from boresight
// phi, theta, beamwidth, orientation, maxAttn, expectedGain, condition
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (0), 0), 60, 0, 20, 0, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (30), 0), 60, 0, 20, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-30), 0), 60, 0, 20, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-90), 0), 60, 0, 20, -20, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (90), 0), 60, 0, 20, -20, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (100), 0), 60, 0, 20, -20, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (150), 0), 60, 0, 20, -20, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (180), 0), 60, 0, 20, -20, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-100), 0), 60, 0, 20, -20, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-150), 0), 60, 0, 20, -20, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-180), 0), 60, 0, 20, -20, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (0), DegreesToRadians (90)), 60, 0, 20, 0, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (30), DegreesToRadians (90)), 60, 0, 20, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-30), DegreesToRadians (90)), 60, 0, 20, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-90), DegreesToRadians (90)), 60, 0, 20, -20, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (90), DegreesToRadians (90)), 60, 0, 20, -20, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (100), DegreesToRadians (90)), 60, 0, 20, -20, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (150), DegreesToRadians (90)), 60, 0, 20, -20, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (180), DegreesToRadians (90)), 60, 0, 20, -20, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-100), DegreesToRadians (90)), 60, 0, 20, -20, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-150), DegreesToRadians (90)), 60, 0, 20, -20, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-180), DegreesToRadians (90)), 60, 0, 20, -20, EQUAL), TestCase::QUICK);
// with a 60 deg beamwidth, gain is -10dB at +-54.772 degrees from boresight
// test positive orientation
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (60), 0), 60, 60, 10, 0, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (90), 0), 60, 60, 10, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (30), 0), 60, 60, 10, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-30), 0), 60, 60, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (150), 0), 60, 60, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (160), 0), 60, 60, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (210), 0), 60, 60, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (240), 0), 60, 60, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-40), 0), 60, 60, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-90), 0), 60, 60, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-120), 0), 60, 60, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (60), DegreesToRadians (90)), 60, 60, 10, 0, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (90), DegreesToRadians (90)), 60, 60, 10, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (30), DegreesToRadians (90)), 60, 60, 10, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-30), DegreesToRadians (90)), 60, 60, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (150), DegreesToRadians (90)), 60, 60, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (160), DegreesToRadians (90)), 60, 60, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (210), DegreesToRadians (90)), 60, 60, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (240), DegreesToRadians (90)), 60, 60, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-40), DegreesToRadians (90)), 60, 60, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-90), DegreesToRadians (90)), 60, 60, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-120), DegreesToRadians (90)), 60, 60, 10, -10, EQUAL), TestCase::QUICK);
// test negative orientation and different beamwidths
// with a 80 deg beamwidth, gain is -20dB at +- 73.030 degrees from boresight
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-150), 0), 80, -150, 10, 0, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-110), 0), 80, -150, 10, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-190), 0), 80, -150, 10, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-70), 0), 80, -150, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (92), 0), 80, -150, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-30), 0), 80, -150, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (0), 0), 80, -150, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (60), 0), 80, -150, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (90), 0), 80, -150, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (30), 0), 80, -150, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-150), DegreesToRadians (90)), 80, -150, 10, 0, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-110), DegreesToRadians (90)), 80, -150, 10, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-190), DegreesToRadians (90)), 80, -150, 10, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-70), DegreesToRadians (90)), 80, -150, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (92), DegreesToRadians (90)), 80, -150, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-30), DegreesToRadians (90)), 80, -150, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (0), DegreesToRadians (90)), 80, -150, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (60), DegreesToRadians (90)), 80, -150, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (90), DegreesToRadians (90)), 80, -150, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (30), DegreesToRadians (90)), 80, -150, 10, -10, EQUAL), TestCase::QUICK);
// test elevation angle
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (0), 2), 60, 0, 20, 0, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (30), 2), 60, 0, 20, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-30), 2), 60, 0, 20, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-90), 2), 60, 0, 20, -20, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-180), 2), 60, 0, 20, -20, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (60), -3), 60, 60, 20, 0, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (90), -3), 60, 60, 20, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (30), -3), 60, 60, 20, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-120), -3), 60, 60, 20, -20, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-150), -3), 100, -150, 10, 0, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-100), -3), 100, -150, 10, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-200), -3), 100, -150, 10, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-30), -3), 100, -150, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (90), 9.5), 100, -150, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (0), 9.5), 60, 0, 20, 0, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (30), 9.5), 60, 0, 20, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-30), 9.5), 60, 0, 20, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (100), 9.5), 60, 0, 20, -20, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-150), 9.5), 100, -150, 30, 0, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-100), 9.5), 100, -150, 30, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-200), 9.5), 100, -150, 30, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (0), DegreesToRadians (88)), 60, 0, 20, 0, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (30), DegreesToRadians (88)), 60, 0, 20, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-30), DegreesToRadians (88)), 60, 0, 20, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-90), DegreesToRadians (88)), 60, 0, 20, -20, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-180), DegreesToRadians (88)), 60, 0, 20, -20, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (60), DegreesToRadians (93)), 60, 60, 20, 0, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (90), DegreesToRadians (93)), 60, 60, 20, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (30), DegreesToRadians (93)), 60, 60, 20, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-120), DegreesToRadians (93)), 60, 60, 20, -20, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-150), DegreesToRadians (93)), 100, -150, 10, 0, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-100), DegreesToRadians (93)), 100, -150, 10, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-200), DegreesToRadians (93)), 100, -150, 10, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-30), DegreesToRadians (93)), 100, -150, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (90), DegreesToRadians (80.5)), 100, -150, 10, -10, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (0), DegreesToRadians (80.5)), 60, 0, 20, 0, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (30), DegreesToRadians (80.5)), 60, 0, 20, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-30), DegreesToRadians (80.5)), 60, 0, 20, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (100), DegreesToRadians (80.5)), 60, 0, 20, -20, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-150), DegreesToRadians (80.5)), 100, -150, 30, 0, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-100), DegreesToRadians (80.5)), 100, -150, 30, -3, EQUAL), TestCase::QUICK);
AddTestCase (new ParabolicAntennaModelTestCase (Angles (DegreesToRadians (-200), DegreesToRadians (80.5)), 100, -150, 30, -3, EQUAL), TestCase::QUICK);
};

View File

@@ -0,0 +1,216 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2020 University of Padova, Dep. of Information Engineering, SIGNET lab.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ns3/log.h"
#include "ns3/test.h"
#include "ns3/double.h"
#include "ns3/uinteger.h"
#include "ns3/pointer.h"
#include "ns3/uniform-planar-array.h"
#include "ns3/isotropic-antenna-model.h"
#include "ns3/three-gpp-antenna-model.h"
#include "ns3/simulator.h"
#include "cmath"
#include "string"
#include "iostream"
#include "sstream"
using namespace ns3;
NS_LOG_COMPONENT_DEFINE ("TestUniformPlanarArray");
/**
* \ingroup tests
*
* \brief UniformPlanarArray Test Case
*/
class UniformPlanarArrayTestCase : public TestCase
{
public:
/**
* Generate a string containing all relevant parameters
* \param element the antenna element
* \param rows the number of rows
* \param cols the number of columns
* \param rowSpace the row spacing
* \param colSpace the column spacing
* \param alpha the bearing angle
* \param beta the tilting angle
* \param direction the direction
* \return the string containing all relevant parameters
*/
static std::string BuildNameString (Ptr<AntennaModel> element, uint32_t rows, uint32_t cols,double rowSpace, double colSpace,
double alpha, double beta, Angles direction);
/**
* The constructor of the test case
* \param element the antenna element
* \param rows the number of rows
* \param cols the number of columns
* \param rowSpace the row spacing
* \param colSpace the column spacing
* \param alpha the bearing angle
* \param beta the tilting angle
* \param direction the direction
* \param expectedGainDb the expected antenna gain [dB]
*/
UniformPlanarArrayTestCase (Ptr<AntennaModel> element, uint32_t rows, uint32_t cols, double rowSpace, double colSpace,
double alpha, double beta, Angles direction, double expectedGainDb);
private:
/**
* Run the test
*/
virtual void DoRun (void);
/**
* Compute the gain of the antenna array
* \param a the antenna array
* \return the gain of the antenna array [dB]
*/
double ComputeGain (Ptr<UniformPlanarArray> a);
Ptr<AntennaModel> m_element; //!< the antenna element
uint32_t m_rows; //!< the number of rows
uint32_t m_cols; //!< the number of columns
double m_rowSpace; //!< the row spacing
double m_colSpace; //!< the column spacing
double m_alpha; //!< the bearing angle [rad]
double m_beta; //!< the titling angle [rad]
Angles m_direction; //!< the testing direction
double m_expectedGain; //!< the expected antenna gain [dB]
};
std::string UniformPlanarArrayTestCase::BuildNameString (Ptr<AntennaModel> element, uint32_t rows, uint32_t cols, double rowSpace, double colSpace,
double alpha, double beta, Angles direction)
{
std::ostringstream oss;
oss << "UPA=" << rows << "x" << cols
<< ", row spacing=" << rowSpace << "*lambda"
<< ", col spacing=" << colSpace << "*lambda"
<< ", bearing=" << RadiansToDegrees (alpha) << " deg"
<< ", tilting=" << RadiansToDegrees (beta) << " deg"
<< ", element=" << element->GetInstanceTypeId ().GetName ()
<< ", direction=" << direction;
return oss.str ();
}
UniformPlanarArrayTestCase::UniformPlanarArrayTestCase (Ptr<AntennaModel> element, uint32_t rows, uint32_t cols, double rowSpace, double colSpace,
double alpha, double beta, Angles direction, double expectedGainDb)
: TestCase (BuildNameString (element, rows, cols, rowSpace, colSpace, alpha, beta, direction)),
m_element (element),
m_rows (rows),
m_cols (cols),
m_rowSpace (rowSpace),
m_colSpace (colSpace),
m_alpha (alpha),
m_beta (beta),
m_direction (direction),
m_expectedGain (expectedGainDb)
{}
double
UniformPlanarArrayTestCase::ComputeGain (Ptr<UniformPlanarArray> a)
{
// compute gain
PhasedArrayModel::ComplexVector sv = a->GetSteeringVector (m_direction);
NS_TEST_EXPECT_MSG_EQ (sv.size (), a->GetNumberOfElements (), "steering vector of wrong size");
PhasedArrayModel::ComplexVector bf = a->GetBeamformingVector (m_direction);
NS_TEST_EXPECT_MSG_EQ (bf.size (), a->GetNumberOfElements (), "beamforming vector of wrong size");
std::pair<double, double> fp = a->GetElementFieldPattern (m_direction);
// scalar product dot (sv, bf)
std::complex<double> prod {0};
for (size_t i = 0; i < sv.size (); i++)
{
prod += sv[i] * bf[i];
}
double bfGain = std::pow (std::abs (prod), 2);
double bfGainDb = 10 * std::log10 (bfGain);
// power gain from two polarizations
double elementPowerGain = std::pow (std::get<0> (fp), 2) + std::pow (std::get<1> (fp), 2);
double elementPowerGainDb = 10 * std::log10 (elementPowerGain);
// sum BF and element gains
return bfGainDb + elementPowerGainDb;
}
void
UniformPlanarArrayTestCase::DoRun ()
{
NS_LOG_FUNCTION (this << BuildNameString (m_element, m_rows, m_cols, m_rowSpace, m_colSpace, m_alpha, m_beta, m_direction));
Ptr<UniformPlanarArray> a = CreateObject<UniformPlanarArray> ();
a->SetAttribute ("AntennaElement", PointerValue (m_element));
a->SetAttribute ("NumRows", UintegerValue (m_rows));
a->SetAttribute ("NumColumns", UintegerValue (m_cols));
a->SetAttribute ("AntennaVerticalSpacing", DoubleValue (m_rowSpace));
a->SetAttribute ("AntennaHorizontalSpacing", DoubleValue (m_colSpace));
a->SetAttribute ("BearingAngle", DoubleValue (m_alpha));
a->SetAttribute ("DowntiltAngle", DoubleValue (m_beta));
double actualGainDb = ComputeGain (a);
NS_TEST_EXPECT_MSG_EQ_TOL (actualGainDb, m_expectedGain, 0.001, "wrong value of the radiation pattern");
}
/**
* \ingroup tests
*
* \brief UniformPlanarArray Test Suite
*/
class UniformPlanarArrayTestSuite : public TestSuite
{
public:
UniformPlanarArrayTestSuite ();
};
UniformPlanarArrayTestSuite::UniformPlanarArrayTestSuite ()
: TestSuite ("uniform-planar-array-test", UNIT)
{
Ptr<AntennaModel> isotropic = CreateObject<IsotropicAntennaModel> ();
Ptr<AntennaModel> tgpp = CreateObject<ThreeGppAntennaModel> ();
// element, rows, cols, rowSpace, colSpace, bearing, tilting, direction (azimuth, inclination), expectedGainDb
// Single element arrays: check if bearing/tilting works on antenna element
AddTestCase (new UniformPlanarArrayTestCase (isotropic, 1, 1, 0.5, 0.5, DegreesToRadians (0), DegreesToRadians (0), Angles (DegreesToRadians (0), DegreesToRadians (90)), 0.0), TestCase::QUICK);
AddTestCase (new UniformPlanarArrayTestCase ( tgpp, 1, 1, 0.5, 0.5, DegreesToRadians (0), DegreesToRadians (0), Angles (DegreesToRadians (0), DegreesToRadians (90)), 8.0), TestCase::QUICK);
AddTestCase (new UniformPlanarArrayTestCase ( tgpp, 1, 1, 0.5, 0.5, DegreesToRadians (90), DegreesToRadians (0), Angles (DegreesToRadians (90), DegreesToRadians (90)), 8.0), TestCase::QUICK);
AddTestCase (new UniformPlanarArrayTestCase ( tgpp, 1, 1, 0.5, 0.5, DegreesToRadians (-90), DegreesToRadians (0), Angles (DegreesToRadians (-90), DegreesToRadians (90)), 8.0), TestCase::QUICK);
AddTestCase (new UniformPlanarArrayTestCase ( tgpp, 1, 1, 0.5, 0.5, DegreesToRadians (180), DegreesToRadians (0), Angles (DegreesToRadians (180), DegreesToRadians (90)), 8.0), TestCase::QUICK);
AddTestCase (new UniformPlanarArrayTestCase ( tgpp, 1, 1, 0.5, 0.5, DegreesToRadians (-180), DegreesToRadians (0), Angles (DegreesToRadians (-180), DegreesToRadians (90)), 8.0), TestCase::QUICK);
AddTestCase (new UniformPlanarArrayTestCase ( tgpp, 1, 1, 0.5, 0.5, DegreesToRadians (0), DegreesToRadians (45), Angles (DegreesToRadians (0), DegreesToRadians (135)), 8.0), TestCase::QUICK);
AddTestCase (new UniformPlanarArrayTestCase ( tgpp, 1, 1, 0.5, 0.5, DegreesToRadians (0), DegreesToRadians (-45), Angles (DegreesToRadians (0), DegreesToRadians (45)), 8.0), TestCase::QUICK);
AddTestCase (new UniformPlanarArrayTestCase ( tgpp, 1, 1, 0.5, 0.5, DegreesToRadians (0), DegreesToRadians (90), Angles (DegreesToRadians (0), DegreesToRadians (180)), 8.0), TestCase::QUICK);
AddTestCase (new UniformPlanarArrayTestCase ( tgpp, 1, 1, 0.5, 0.5, DegreesToRadians (0), DegreesToRadians (-90), Angles (DegreesToRadians (0), DegreesToRadians (0)), 8.0), TestCase::QUICK);
// linear array
AddTestCase (new UniformPlanarArrayTestCase ( tgpp, 10, 1, 0.5, 0.5, DegreesToRadians (0), DegreesToRadians (0), Angles (DegreesToRadians (0), DegreesToRadians (90)), 18.0), TestCase::QUICK);
AddTestCase (new UniformPlanarArrayTestCase ( tgpp, 10, 1, 0.5, 0.5, DegreesToRadians (90), DegreesToRadians (0), Angles (DegreesToRadians (90), DegreesToRadians (90)), 18.0), TestCase::QUICK);
AddTestCase (new UniformPlanarArrayTestCase ( tgpp, 10, 1, 0.5, 0.5, DegreesToRadians (0), DegreesToRadians (45), Angles (DegreesToRadians (0), DegreesToRadians (135)), 18.0), TestCase::QUICK);
// planar array
AddTestCase (new UniformPlanarArrayTestCase ( tgpp, 10, 10, 0.5, 0.5, DegreesToRadians (0), DegreesToRadians (0), Angles (DegreesToRadians (0), DegreesToRadians (90)), 28.0), TestCase::QUICK);
AddTestCase (new UniformPlanarArrayTestCase ( tgpp, 10, 10, 0.5, 0.5, DegreesToRadians (90), DegreesToRadians (0), Angles (DegreesToRadians (90), DegreesToRadians (90)), 28.0), TestCase::QUICK);
AddTestCase (new UniformPlanarArrayTestCase ( tgpp, 10, 10, 0.5, 0.5, DegreesToRadians (0), DegreesToRadians (45), Angles (DegreesToRadians (0), DegreesToRadians (135)), 28.0), TestCase::QUICK);
}
static UniformPlanarArrayTestSuite staticUniformPlanarArrayTestSuiteInstance;

View File

@@ -10,7 +10,9 @@ def build(bld):
'model/isotropic-antenna-model.cc',
'model/cosine-antenna-model.cc',
'model/parabolic-antenna-model.cc',
'model/three-gpp-antenna-array-model.cc',
'model/three-gpp-antenna-model.cc',
'model/phased-array-model.cc',
'model/uniform-planar-array.cc',
]
module_test = bld.create_ns3_module_test_library('antenna')
@@ -20,6 +22,7 @@ def build(bld):
'test/test-isotropic-antenna.cc',
'test/test-cosine-antenna.cc',
'test/test-parabolic-antenna.cc',
'test/test-uniform-planar-array.cc',
]
# Tests encapsulating example programs should be listed here
@@ -36,7 +39,9 @@ def build(bld):
'model/isotropic-antenna-model.h',
'model/cosine-antenna-model.h',
'model/parabolic-antenna-model.h',
'model/three-gpp-antenna-array-model.h',
'model/three-gpp-antenna-model.h',
'model/phased-array-model.h',
'model/uniform-planar-array.h',
]
bld.ns3_python_bindings()

View File

@@ -37,7 +37,7 @@ using std::vector;
int
main (int argc, char *argv[])
{
CommandLine cmd (__FILE__);
CommandLine cmd;
cmd.Parse (argc, argv);
ConfigStore inputConfig;
@@ -178,19 +178,19 @@ main (int argc, char *argv[])
// Beam width is made quite narrow so sectors can be noticed in the REM
lteHelper->SetEnbAntennaModelType ("ns3::CosineAntennaModel");
lteHelper->SetEnbAntennaModelAttribute ("Orientation", DoubleValue (0));
lteHelper->SetEnbAntennaModelAttribute ("Beamwidth", DoubleValue (100));
lteHelper->SetEnbAntennaModelAttribute ("HorizontalBeamwidth", DoubleValue (100));
lteHelper->SetEnbAntennaModelAttribute ("MaxGain", DoubleValue (0.0));
enbDevs.Add ( lteHelper->InstallEnbDevice (threeSectorNodes.Get (0)));
lteHelper->SetEnbAntennaModelType ("ns3::CosineAntennaModel");
lteHelper->SetEnbAntennaModelAttribute ("Orientation", DoubleValue (360/3));
lteHelper->SetEnbAntennaModelAttribute ("Beamwidth", DoubleValue (100));
lteHelper->SetEnbAntennaModelAttribute ("HorizontalBeamwidth", DoubleValue (100));
lteHelper->SetEnbAntennaModelAttribute ("MaxGain", DoubleValue (0.0));
enbDevs.Add ( lteHelper->InstallEnbDevice (threeSectorNodes.Get (1)));
lteHelper->SetEnbAntennaModelType ("ns3::CosineAntennaModel");
lteHelper->SetEnbAntennaModelAttribute ("Orientation", DoubleValue (2*360/3));
lteHelper->SetEnbAntennaModelAttribute ("Beamwidth", DoubleValue (100));
lteHelper->SetEnbAntennaModelAttribute ("HorizontalBeamwidth", DoubleValue (100));
lteHelper->SetEnbAntennaModelAttribute ("MaxGain", DoubleValue (0.0));
enbDevs.Add ( lteHelper->InstallEnbDevice (threeSectorNodes.Get (2)));

View File

@@ -158,9 +158,13 @@ LteEnbAntennaTestCase::DoRun (void)
lteHelper->SetSchedulerAttribute ("UlCqiFilter", EnumValue (FfMacScheduler::PUSCH_UL_CQI));
lteHelper->SetEnbAntennaModelType ("ns3::CosineAntennaModel");
lteHelper->SetEnbAntennaModelAttribute ("Orientation", DoubleValue (m_orientationDegrees));
lteHelper->SetEnbAntennaModelAttribute ("Beamwidth", DoubleValue (m_beamwidthDegrees));
lteHelper->SetEnbAntennaModelAttribute ("HorizontalBeamwidth", DoubleValue (m_beamwidthDegrees));
lteHelper->SetEnbAntennaModelAttribute ("MaxGain", DoubleValue (0.0));
// set DL and UL bandwidth.
lteHelper->SetEnbDeviceAttribute ("DlBandwidth", UintegerValue (25));
lteHelper->SetEnbDeviceAttribute ("UlBandwidth", UintegerValue (25));
enbDevs = lteHelper->InstallEnbDevice (enbNodes);
ueDevs = lteHelper->InstallUeDevice (ueNodes);
@@ -220,6 +224,7 @@ LteEnbAntennaTestCase::DoRun (void)
}
// remember that propagation loss is 0dB
double calculatedAntennaGainDbDl = - (enbTxPowerDbm - calculatedSinrDbDl - noisePowerDbm - ueNoiseFigureDb);
NS_LOG_INFO ("expected " << m_antennaGainDb << " actual " << calculatedAntennaGainDbDl << " tol " << tolerance);
NS_TEST_ASSERT_MSG_EQ_TOL (calculatedAntennaGainDbDl, m_antennaGainDb, tolerance, "Wrong DL antenna gain!");
}
double expectedSinrUl = ueTxPowerDbm + m_antennaGainDb - noisePowerDbm + enbNoiseFigureDb;

View File

@@ -586,8 +586,6 @@ the given geographic origin point, and is tested in the ``rand-cart-around-geo``
test suite by verifying that the generated points do not exceed the given
maximum distance radius from the origin point.
.. _sec-3gpp-fast-fading-model:
3GPP TR 38.901 fast fading model
================================
The framework described by TR 38.901 [TR38901]_ is a 3D statistical Spatial
@@ -660,7 +658,7 @@ The method DoCalcRxPowerSpectralDensity uses m_deviceAntennaMap to obtain the
antenna objects associated to the transmitting and receiving devices, and calls
the method GetCurrentBeamformingVector to retrieve the beamforming vectors.
For each device using the channel, the m_deviceAntennaMap contains the associated
antenna object of type ThreeGppAntennaArrayModel. Since the mapping is one-to-one,
antenna object of type PhasedArrayModel. Since the mapping is one-to-one,
the model supports a single antenna object for each device.
The m_deviceAntennaMap has to be initialized by inserting the device-antenna
pairs using the method AddDevice.
@@ -698,6 +696,7 @@ distribution depends on the parameter :math:`v_{scatt}`.
The value of :math:`v_{scatt}` can be configured using the attribute "vScatt"
(by default it is set to 0, so that the scattering effect is not considered).
ThreeGppChannelModel
####################

View File

@@ -30,7 +30,7 @@
#include "ns3/core-module.h"
#include "ns3/three-gpp-channel-model.h"
#include "ns3/three-gpp-antenna-array-model.h"
#include "ns3/uniform-planar-array.h"
#include <fstream>
#include "ns3/three-gpp-spectrum-propagation-loss-model.h"
#include "ns3/net-device.h"
@@ -57,9 +57,9 @@ static Ptr<ThreeGppSpectrumPropagationLossModel> m_spectrumLossModel; //!< the S
* \param otherDevice the device towards which point the beam
*/
static void
DoBeamforming (Ptr<NetDevice> thisDevice, Ptr<ThreeGppAntennaArrayModel> thisAntenna, Ptr<NetDevice> otherDevice)
DoBeamforming (Ptr<NetDevice> thisDevice, Ptr<PhasedArrayModel> thisAntenna, Ptr<NetDevice> otherDevice)
{
ThreeGppAntennaArrayModel::ComplexVector antennaWeights;
PhasedArrayModel::ComplexVector antennaWeights;
// retrieve the position of the two devices
Vector aPos = thisDevice->GetNode ()->GetObject<MobilityModel> ()->GetPosition ();
@@ -67,13 +67,9 @@ DoBeamforming (Ptr<NetDevice> thisDevice, Ptr<ThreeGppAntennaArrayModel> thisAnt
// compute the azimuth and the elevation angles
Angles completeAngle (bPos,aPos);
double hAngleRadian = completeAngle.GetAzimuth ();
double hAngleRadian = fmod (completeAngle.phi, 2.0 * M_PI); // the azimuth angle
if (hAngleRadian < 0)
{
hAngleRadian += 2.0 * M_PI;
}
double vAngleRadian = completeAngle.theta; // the elevation angle
double vAngleRadian = completeAngle.GetInclination (); // the elevation angle
// retrieve the number of antenna elements
int totNoArrayElements = thisAntenna->GetNumberOfElements ();
@@ -232,8 +228,8 @@ main (int argc, char *argv[])
nodes.Get (1)->AggregateObject (rxMob);
// create the antenna objects and set their dimensions
Ptr<ThreeGppAntennaArrayModel> txAntenna = CreateObjectWithAttributes<ThreeGppAntennaArrayModel> ("NumColumns", UintegerValue (2), "NumRows", UintegerValue (2));
Ptr<ThreeGppAntennaArrayModel> rxAntenna = CreateObjectWithAttributes<ThreeGppAntennaArrayModel> ("NumColumns", UintegerValue (2), "NumRows", UintegerValue (2));
Ptr<PhasedArrayModel> txAntenna = CreateObjectWithAttributes<UniformPlanarArray> ("NumColumns", UintegerValue (2), "NumRows", UintegerValue (2));
Ptr<PhasedArrayModel> rxAntenna = CreateObjectWithAttributes<UniformPlanarArray> ("NumColumns", UintegerValue (2), "NumRows", UintegerValue (2));
// initialize the devices in the ThreeGppSpectrumPropagationLossModel
m_spectrumLossModel->AddDevice (txDev, txAntenna);

View File

@@ -26,7 +26,7 @@
#include <ns3/object.h>
#include <ns3/nstime.h>
#include <ns3/vector.h>
#include <ns3/three-gpp-antenna-array-model.h>
#include <ns3/phased-array-model.h>
#include <tuple>
namespace ns3 {
@@ -51,7 +51,7 @@ public:
typedef std::vector<double> DoubleVector; //!< type definition for vectors of doubles
typedef std::vector<DoubleVector> Double2DVector; //!< type definition for matrices of doubles
typedef std::vector<Double2DVector> Double3DVector; //!< type definition for 3D matrices of doubles
typedef std::vector<ThreeGppAntennaArrayModel::ComplexVector> Complex2DVector; //!< type definition for complex matrices
typedef std::vector<PhasedArrayModel::ComplexVector> Complex2DVector; //!< type definition for complex matrices
typedef std::vector<Complex2DVector> Complex3DVector; //!< type definition for complex 3D matrices
@@ -108,8 +108,8 @@ public:
*/
virtual Ptr<const ChannelMatrix> GetChannel (Ptr<const MobilityModel> aMob,
Ptr<const MobilityModel> bMob,
Ptr<const ThreeGppAntennaArrayModel> aAntenna,
Ptr<const ThreeGppAntennaArrayModel> bAntenna) = 0;
Ptr<const PhasedArrayModel> aAntenna,
Ptr<const PhasedArrayModel> bAntenna) = 0;
/**
* Calculate the channel key using the Cantor function

View File

@@ -22,7 +22,7 @@
#include "three-gpp-channel-model.h"
#include "ns3/log.h"
#include "ns3/three-gpp-antenna-array-model.h"
#include "ns3/phased-array-model.h"
#include "ns3/node.h"
#include "ns3/double.h"
#include "ns3/string.h"
@@ -970,8 +970,8 @@ ThreeGppChannelModel::ChannelMatrixNeedsUpdate (Ptr<const ThreeGppChannelMatrix>
Ptr<const MatrixBasedChannelModel::ChannelMatrix>
ThreeGppChannelModel::GetChannel (Ptr<const MobilityModel> aMob,
Ptr<const MobilityModel> bMob,
Ptr<const ThreeGppAntennaArrayModel> aAntenna,
Ptr<const ThreeGppAntennaArrayModel> bAntenna)
Ptr<const PhasedArrayModel> aAntenna,
Ptr<const PhasedArrayModel> bAntenna)
{
NS_LOG_FUNCTION (this);
@@ -1038,8 +1038,8 @@ ThreeGppChannelModel::GetChannel (Ptr<const MobilityModel> aMob,
Ptr<ThreeGppChannelModel::ThreeGppChannelMatrix>
ThreeGppChannelModel::GetNewChannel (Vector locUT, Ptr<const ChannelCondition> channelCondition,
Ptr<const ThreeGppAntennaArrayModel> sAntenna,
Ptr<const ThreeGppAntennaArrayModel> uAntenna,
Ptr<const PhasedArrayModel> sAntenna,
Ptr<const PhasedArrayModel> uAntenna,
Angles &uAngle, Angles &sAngle,
double dis2D, double hBS, double hUT) const
{
@@ -1333,17 +1333,17 @@ ThreeGppChannelModel::GetNewChannel (Vector locUT, Ptr<const ChannelCondition> c
{
Xn = -1;
}
clusterAoa[cIndex] = clusterAoa[cIndex] * Xn + (m_normalRv->GetValue () * ASA / 7) + uAngle.phi * 180 / M_PI; //(7.5-11)
clusterAod[cIndex] = clusterAod[cIndex] * Xn + (m_normalRv->GetValue () * ASD / 7) + sAngle.phi * 180 / M_PI;
clusterAoa[cIndex] = clusterAoa[cIndex] * Xn + (m_normalRv->GetValue () * ASA / 7) + RadiansToDegrees (uAngle.GetAzimuth ()); //(7.5-11)
clusterAod[cIndex] = clusterAod[cIndex] * Xn + (m_normalRv->GetValue () * ASD / 7) + RadiansToDegrees (sAngle.GetAzimuth ());
if (o2i)
{
clusterZoa[cIndex] = clusterZoa[cIndex] * Xn + (m_normalRv->GetValue () * ZSA / 7) + 90; //(7.5-16)
}
else
{
clusterZoa[cIndex] = clusterZoa[cIndex] * Xn + (m_normalRv->GetValue () * ZSA / 7) + uAngle.theta * 180 / M_PI; //(7.5-16)
clusterZoa[cIndex] = clusterZoa[cIndex] * Xn + (m_normalRv->GetValue () * ZSA / 7) + RadiansToDegrees (uAngle.GetInclination ()); //(7.5-16)
}
clusterZod[cIndex] = clusterZod[cIndex] * Xn + (m_normalRv->GetValue () * ZSD / 7) + sAngle.theta * 180 / M_PI + table3gpp->m_offsetZOD; //(7.5-19)
clusterZod[cIndex] = clusterZod[cIndex] * Xn + (m_normalRv->GetValue () * ZSD / 7) + RadiansToDegrees (sAngle.GetInclination ()) + table3gpp->m_offsetZOD; //(7.5-19)
}
@@ -1351,10 +1351,10 @@ ThreeGppChannelModel::GetNewChannel (Vector locUT, Ptr<const ChannelCondition> c
{
//The 7.5-12 can be rewrite as Theta_n,ZOA = Theta_n,ZOA - (Theta_1,ZOA - Theta_LOS,ZOA) = Theta_n,ZOA - diffZOA,
//Similar as AOD, ZSA and ZSD.
double diffAoa = clusterAoa[0] - uAngle.phi * 180 / M_PI;
double diffAod = clusterAod[0] - sAngle.phi * 180 / M_PI;
double diffZsa = clusterZoa[0] - uAngle.theta * 180 / M_PI;
double diffZsd = clusterZod[0] - sAngle.theta * 180 / M_PI;
double diffAoa = clusterAoa[0] - RadiansToDegrees (uAngle.GetAzimuth ());
double diffAod = clusterAod[0] - RadiansToDegrees (sAngle.GetAzimuth ());
double diffZsa = clusterZoa[0] - RadiansToDegrees (uAngle.GetInclination ());
double diffZsd = clusterZod[0] - RadiansToDegrees (sAngle.GetInclination ());
for (uint8_t cIndex = 0; cIndex < numReducedCluster; cIndex++)
{
@@ -1376,69 +1376,12 @@ ThreeGppChannelModel::GetNewChannel (Vector locUT, Ptr<const ChannelCondition> c
for (uint8_t mInd = 0; mInd < raysPerCluster; mInd++)
{
double tempAoa = clusterAoa[nInd] + table3gpp->m_cASA * offSetAlpha[mInd]; //(7.5-13)
while (tempAoa > 360)
{
tempAoa -= 360;
}
while (tempAoa < 0)
{
tempAoa += 360;
}
NS_ASSERT_MSG (tempAoa >= 0 && tempAoa <= 360, "the AOA should be the range of [0,360]");
rayAoa_radian[nInd][mInd] = tempAoa * M_PI / 180;
double tempAod = clusterAod[nInd] + table3gpp->m_cASD * offSetAlpha[mInd];
while (tempAod > 360)
{
tempAod -= 360;
}
while (tempAod < 0)
{
tempAod += 360;
}
NS_ASSERT_MSG (tempAod >= 0 && tempAod <= 360, "the AOD should be the range of [0,360]");
rayAod_radian[nInd][mInd] = tempAod * M_PI / 180;
double tempZoa = clusterZoa[nInd] + table3gpp->m_cZSA * offSetAlpha[mInd]; //(7.5-18)
std::tie (rayAoa_radian[nInd][mInd], rayZoa_radian[nInd][mInd]) = WrapAngles (DegreesToRadians (tempAoa), DegreesToRadians (tempZoa));
while (tempZoa > 360)
{
tempZoa -= 360;
}
while (tempZoa < 0)
{
tempZoa += 360;
}
if (tempZoa > 180)
{
tempZoa = 360 - tempZoa;
}
NS_ASSERT_MSG (tempZoa >= 0&&tempZoa <= 180, "the ZOA should be the range of [0,180]");
rayZoa_radian[nInd][mInd] = tempZoa * M_PI / 180;
double tempAod = clusterAod[nInd] + table3gpp->m_cASD * offSetAlpha[mInd]; //(7.5-13)
double tempZod = clusterZod[nInd] + 0.375 * pow (10,table3gpp->m_uLgZSD) * offSetAlpha[mInd]; //(7.5-20)
while (tempZod > 360)
{
tempZod -= 360;
}
while (tempZod < 0)
{
tempZod += 360;
}
if (tempZod > 180)
{
tempZod = 360 - tempZod;
}
NS_ASSERT_MSG (tempZod >= 0&&tempZod <= 180, "the ZOD should be the range of [0,180]");
rayZod_radian[nInd][mInd] = tempZod * M_PI / 180;
std::tie (rayAod_radian[nInd][mInd], rayZod_radian[nInd][mInd]) = WrapAngles (DegreesToRadians (tempAod), DegreesToRadians (tempZod));
}
}
DoubleVector angle_degree;
@@ -1712,16 +1655,16 @@ ThreeGppChannelModel::GetNewChannel (Vector locUT, Ptr<const ChannelCondition> c
if (los) //(7.5-29) && (7.5-30)
{
std::complex<double> ray (0,0);
double rxPhaseDiff = 2 * M_PI * (sin (uAngle.theta) * cos (uAngle.phi) * uLoc.x
+ sin (uAngle.theta) * sin (uAngle.phi) * uLoc.y
+ cos (uAngle.theta) * uLoc.z);
double txPhaseDiff = 2 * M_PI * (sin (sAngle.theta) * cos (sAngle.phi) * sLoc.x
+ sin (sAngle.theta) * sin (sAngle.phi) * sLoc.y
+ cos (sAngle.theta) * sLoc.z);
double rxPhaseDiff = 2 * M_PI * (sin (uAngle.GetInclination ()) * cos (uAngle.GetAzimuth ()) * uLoc.x
+ sin (uAngle.GetInclination ()) * sin (uAngle.GetAzimuth ()) * uLoc.y
+ cos (uAngle.GetInclination ()) * uLoc.z);
double txPhaseDiff = 2 * M_PI * (sin (sAngle.GetInclination ()) * cos (sAngle.GetAzimuth ()) * sLoc.x
+ sin (sAngle.GetInclination ()) * sin (sAngle.GetAzimuth ()) * sLoc.y
+ cos (sAngle.GetInclination ()) * sLoc.z);
double rxFieldPatternPhi, rxFieldPatternTheta, txFieldPatternPhi, txFieldPatternTheta;
std::tie (rxFieldPatternPhi, rxFieldPatternTheta) = uAntenna->GetElementFieldPattern (Angles (uAngle.phi, uAngle.theta));
std::tie (txFieldPatternPhi, txFieldPatternTheta) = sAntenna->GetElementFieldPattern (Angles (sAngle.phi, sAngle.theta));
std::tie (rxFieldPatternPhi, rxFieldPatternTheta) = uAntenna->GetElementFieldPattern (Angles (uAngle.GetAzimuth (), uAngle.GetInclination ()));
std::tie (txFieldPatternPhi, txFieldPatternTheta) = sAntenna->GetElementFieldPattern (Angles (sAngle.GetAzimuth (), sAngle.GetInclination ()));
double lambda = 3e8 / m_frequency; // the wavelength of the carrier frequency
@@ -1816,6 +1759,27 @@ ThreeGppChannelModel::GetNewChannel (Vector locUT, Ptr<const ChannelCondition> c
return channelParams;
}
std::pair<double, double>
ThreeGppChannelModel::WrapAngles (double azimuthRad, double inclinationRad)
{
inclinationRad = WrapTo2Pi (inclinationRad);
if (inclinationRad > M_PI)
{
// inclination must be in [0, M_PI]
inclinationRad -= M_PI;
azimuthRad += M_PI;
}
azimuthRad = WrapTo2Pi (azimuthRad);
NS_ASSERT_MSG (0 <= inclinationRad && inclinationRad <= M_PI,
"inclinationRad=" << inclinationRad << " not valid, should be in [0, pi]");
NS_ASSERT_MSG (0 <= azimuthRad && azimuthRad <= 2 * M_PI,
"azimuthRad=" << azimuthRad << " not valid, should be in [0, 2*pi]");
return std::make_pair (azimuthRad, inclinationRad);
}
MatrixBasedChannelModel::DoubleVector
ThreeGppChannelModel::CalcAttenuationOfBlockage (Ptr<ThreeGppChannelModel::ThreeGppChannelMatrix> params,
const DoubleVector &clusterAOA,
@@ -2018,13 +1982,13 @@ ThreeGppChannelModel::CalcAttenuationOfBlockage (Ptr<ThreeGppChannelModel::Three
}
double lambda = 3e8 / m_frequency;
double F_A1 = atan (signA1 * M_PI / 2 * sqrt (M_PI / lambda *
params->m_nonSelfBlocking[blockInd][R_INDEX] * (1 / cos (A1 * M_PI / 180) - 1))) / M_PI; //(7.6-23)
params->m_nonSelfBlocking[blockInd][R_INDEX] * (1 / cos (DegreesToRadians (A1)) - 1))) / M_PI; //(7.6-23)
double F_A2 = atan (signA2 * M_PI / 2 * sqrt (M_PI / lambda *
params->m_nonSelfBlocking[blockInd][R_INDEX] * (1 / cos (A2 * M_PI / 180) - 1))) / M_PI;
params->m_nonSelfBlocking[blockInd][R_INDEX] * (1 / cos (DegreesToRadians (A2)) - 1))) / M_PI;
double F_Z1 = atan (signZ1 * M_PI / 2 * sqrt (M_PI / lambda *
params->m_nonSelfBlocking[blockInd][R_INDEX] * (1 / cos (Z1 * M_PI / 180) - 1))) / M_PI;
params->m_nonSelfBlocking[blockInd][R_INDEX] * (1 / cos (DegreesToRadians (Z1)) - 1))) / M_PI;
double F_Z2 = atan (signZ2 * M_PI / 2 * sqrt (M_PI / lambda *
params->m_nonSelfBlocking[blockInd][R_INDEX] * (1 / cos (Z2 * M_PI / 180) - 1))) / M_PI;
params->m_nonSelfBlocking[blockInd][R_INDEX] * (1 / cos (DegreesToRadians (Z2)) - 1))) / M_PI;
double L_dB = -20 * log10 (1 - (F_A1 + F_A2) * (F_Z1 + F_Z2)); //(7.6-22)
powerAttenuation[cInd] += L_dB;
NS_LOG_INFO ("Cluster[" << (int)cInd << "] is blocked by no-self blocking, "

View File

@@ -117,8 +117,8 @@ public:
*/
Ptr<const ChannelMatrix> GetChannel (Ptr<const MobilityModel> aMob,
Ptr<const MobilityModel> bMob,
Ptr<const ThreeGppAntennaArrayModel> aAntenna,
Ptr<const ThreeGppAntennaArrayModel> bAntenna) override;
Ptr<const PhasedArrayModel> aAntenna,
Ptr<const PhasedArrayModel> bAntenna) override;
/**
* \brief Assign a fixed random variable stream number to the random variables
* used by this model.
@@ -129,6 +129,20 @@ public:
int64_t AssignStreams (int64_t stream);
private:
/**
* Wrap an (azimuth, inclination) angle pair in a valid range.
* Specifically, inclination must be in [0, M_PI] and azimuth in [0, 2*M_PI).
* If the inclination angle is outside of its range, the azimuth angle is
* rotated by M_PI.
* This methods aims specifically at solving the problem of generating angles at
* the boundaries of the angle domain, specifically, generating angle distributions
* close to inclinationRad=0 and inclinationRad=M_PI.
*
* \param azimuthRad the azimuth angle in radians
* \param inclinationRad the inclination angle in radians
* \return the wrapped (azimuth, inclination) angle pair in radians
*/
static std::pair<double, double> WrapAngles (double azimuthRad, double inclinationRad);
/**
* \brief Shuffle the elements of a simple sequence container of type double
* \param first Pointer to the first element among the elements to be shuffled
@@ -217,8 +231,8 @@ private:
* \return the channel realization
*/
Ptr<ThreeGppChannelMatrix> GetNewChannel (Vector locUT, Ptr<const ChannelCondition> channelCondition,
Ptr<const ThreeGppAntennaArrayModel> sAntenna,
Ptr<const ThreeGppAntennaArrayModel> uAntenna,
Ptr<const PhasedArrayModel> sAntenna,
Ptr<const PhasedArrayModel> uAntenna,
Angles &uAngle, Angles &sAngle,
double dis2D, double hBS, double hUT) const;

View File

@@ -23,7 +23,7 @@
#include "ns3/log.h"
#include "three-gpp-spectrum-propagation-loss-model.h"
#include "ns3/net-device.h"
#include "ns3/three-gpp-antenna-array-model.h"
#include "ns3/phased-array-model.h"
#include "ns3/node.h"
#include "ns3/channel-condition-model.h"
#include "ns3/double.h"
@@ -95,7 +95,7 @@ ThreeGppSpectrumPropagationLossModel::GetChannelModel () const
}
void
ThreeGppSpectrumPropagationLossModel::AddDevice (Ptr<NetDevice> n, Ptr<const ThreeGppAntennaArrayModel> a)
ThreeGppSpectrumPropagationLossModel::AddDevice (Ptr<NetDevice> n, Ptr<const PhasedArrayModel> a)
{
NS_ASSERT_MSG (m_deviceAntennaMap.find (n->GetNode ()->GetId ()) == m_deviceAntennaMap.end (), "Device is already present in the map");
m_deviceAntennaMap.insert (std::make_pair (n->GetNode ()->GetId (), a));
@@ -121,10 +121,10 @@ ThreeGppSpectrumPropagationLossModel::GetChannelModelAttribute (const std::strin
m_channelModel->GetAttribute (name, value);
}
ThreeGppAntennaArrayModel::ComplexVector
PhasedArrayModel::ComplexVector
ThreeGppSpectrumPropagationLossModel::CalcLongTerm (Ptr<const MatrixBasedChannelModel::ChannelMatrix> params,
const ThreeGppAntennaArrayModel::ComplexVector &sW,
const ThreeGppAntennaArrayModel::ComplexVector &uW) const
const PhasedArrayModel::ComplexVector &sW,
const PhasedArrayModel::ComplexVector &uW) const
{
NS_LOG_FUNCTION (this);
@@ -134,7 +134,7 @@ ThreeGppSpectrumPropagationLossModel::CalcLongTerm (Ptr<const MatrixBasedChannel
NS_LOG_DEBUG ("CalcLongTerm with sAntenna " << sAntenna << " uAntenna " << uAntenna);
//store the long term part to reduce computation load
//only the small scale fading needs to be updated if the large scale parameters and antenna weights remain unchanged.
ThreeGppAntennaArrayModel::ComplexVector longTerm;
PhasedArrayModel::ComplexVector longTerm;
uint8_t numCluster = static_cast<uint8_t> (params->m_channel[0][0].size ());
for (uint8_t cIndex = 0; cIndex < numCluster; cIndex++)
@@ -156,7 +156,7 @@ ThreeGppSpectrumPropagationLossModel::CalcLongTerm (Ptr<const MatrixBasedChannel
Ptr<SpectrumValue>
ThreeGppSpectrumPropagationLossModel::CalcBeamformingGain (Ptr<SpectrumValue> txPsd,
ThreeGppAntennaArrayModel::ComplexVector longTerm,
PhasedArrayModel::ComplexVector longTerm,
Ptr<const MatrixBasedChannelModel::ChannelMatrix> params,
const ns3::Vector &sSpeed, const ns3::Vector &uSpeed) const
{
@@ -171,7 +171,7 @@ ThreeGppSpectrumPropagationLossModel::CalcBeamformingGain (Ptr<SpectrumValue> tx
// NOTE the update of Doppler is simplified by only taking the center angle of
// each cluster in to consideration.
double slotTime = Simulator::Now ().GetSeconds ();
ThreeGppAntennaArrayModel::ComplexVector doppler;
PhasedArrayModel::ComplexVector doppler;
for (uint8_t cIndex = 0; cIndex < numCluster; cIndex++)
{
// Compute alpha and D as described in 3GPP TR 37.885 v15.3.0, Sec. 6.2.3
@@ -226,17 +226,17 @@ ThreeGppSpectrumPropagationLossModel::CalcBeamformingGain (Ptr<SpectrumValue> tx
return tempPsd;
}
ThreeGppAntennaArrayModel::ComplexVector
PhasedArrayModel::ComplexVector
ThreeGppSpectrumPropagationLossModel::GetLongTerm (uint32_t aId, uint32_t bId,
Ptr<const MatrixBasedChannelModel::ChannelMatrix> channelMatrix,
const ThreeGppAntennaArrayModel::ComplexVector &aW,
const ThreeGppAntennaArrayModel::ComplexVector &bW) const
const PhasedArrayModel::ComplexVector &aW,
const PhasedArrayModel::ComplexVector &bW) const
{
ThreeGppAntennaArrayModel::ComplexVector longTerm; // vector containing the long term component for each cluster
PhasedArrayModel::ComplexVector longTerm; // vector containing the long term component for each cluster
// check if the channel matrix was generated considering a as the s-node and
// b as the u-node or viceversa
ThreeGppAntennaArrayModel::ComplexVector sW, uW;
PhasedArrayModel::ComplexVector sW, uW;
if (!channelMatrix->IsReverse (aId, bId))
{
sW = aW;
@@ -311,28 +311,22 @@ ThreeGppSpectrumPropagationLossModel::DoCalcRxPowerSpectralDensity (Ptr<const Sp
// retrieve the antenna of device a
NS_ASSERT_MSG (m_deviceAntennaMap.find (aId) != m_deviceAntennaMap.end (), "Antenna not found for node " << aId);
Ptr<const ThreeGppAntennaArrayModel> aAntenna = m_deviceAntennaMap.at (aId);
Ptr<const PhasedArrayModel> aAntenna = m_deviceAntennaMap.at (aId);
NS_LOG_DEBUG ("a node " << a->GetObject<Node> () << " antenna " << aAntenna);
// retrieve the antenna of the device b
NS_ASSERT_MSG (m_deviceAntennaMap.find (bId) != m_deviceAntennaMap.end (), "Antenna not found for device " << bId);
Ptr<const ThreeGppAntennaArrayModel> bAntenna = m_deviceAntennaMap.at (bId);
Ptr<const PhasedArrayModel> bAntenna = m_deviceAntennaMap.at (bId);
NS_LOG_DEBUG ("b node " << bId << " antenna " << bAntenna);
if (aAntenna->IsOmniTx () || bAntenna->IsOmniTx () )
{
NS_LOG_LOGIC ("Omni transmission, do nothing.");
return rxPsd;
}
Ptr<const MatrixBasedChannelModel::ChannelMatrix> channelMatrix = m_channelModel->GetChannel (a, b, aAntenna, bAntenna);
// get the precoding and combining vectors
ThreeGppAntennaArrayModel::ComplexVector aW = aAntenna->GetBeamformingVector ();
ThreeGppAntennaArrayModel::ComplexVector bW = bAntenna->GetBeamformingVector ();
PhasedArrayModel::ComplexVector aW = aAntenna->GetBeamformingVector ();
PhasedArrayModel::ComplexVector bW = bAntenna->GetBeamformingVector ();
// retrieve the long term component
ThreeGppAntennaArrayModel::ComplexVector longTerm = GetLongTerm (aId, bId, channelMatrix, aW, bW);
PhasedArrayModel::ComplexVector longTerm = GetLongTerm (aId, bId, channelMatrix, aW, bW);
// apply the beamforming gain
rxPsd = CalcBeamformingGain (rxPsd, longTerm, channelMatrix, a->GetVelocity (), b->GetVelocity ());

View File

@@ -46,7 +46,7 @@ class ChannelCondition;
* returns the PSD of the received signal.
*
* \see MatrixBasedChannelModel
* \see ThreeGppAntennaArrayModel
* \see PhasedArrayModel
* \see ChannelCondition
*/
class ThreeGppSpectrumPropagationLossModel : public SpectrumPropagationLossModel
@@ -85,9 +85,9 @@ public:
/**
* Add a device-antenna pair
* \param n a pointer to the NetDevice
* \param a a pointer to the associated ThreeGppAntennaArrayModel
* \param a a pointer to the associated PhasedArrayModel
*/
void AddDevice (Ptr<NetDevice> n, Ptr<const ThreeGppAntennaArrayModel> a);
void AddDevice (Ptr<NetDevice> n, Ptr<const PhasedArrayModel> a);
/**
@@ -136,10 +136,10 @@ private:
*/
struct LongTerm : public SimpleRefCount<LongTerm>
{
ThreeGppAntennaArrayModel::ComplexVector m_longTerm; //!< vector containing the long term component for each cluster
PhasedArrayModel::ComplexVector m_longTerm; //!< vector containing the long term component for each cluster
Ptr<const MatrixBasedChannelModel::ChannelMatrix> m_channel; //!< pointer to the channel matrix used to compute the long term
ThreeGppAntennaArrayModel::ComplexVector m_sW; //!< the beamforming vector for the node s used to compute the long term
ThreeGppAntennaArrayModel::ComplexVector m_uW; //!< the beamforming vector for the node u used to compute the long term
PhasedArrayModel::ComplexVector m_sW; //!< the beamforming vector for the node s used to compute the long term
PhasedArrayModel::ComplexVector m_uW; //!< the beamforming vector for the node u used to compute the long term
};
/**
@@ -159,10 +159,10 @@ private:
* \param bW the beamforming vector of the second device
* \return vector containing the long term compoenent for each cluster
*/
ThreeGppAntennaArrayModel::ComplexVector GetLongTerm (uint32_t aId, uint32_t bId,
PhasedArrayModel::ComplexVector GetLongTerm (uint32_t aId, uint32_t bId,
Ptr<const MatrixBasedChannelModel::ChannelMatrix> channelMatrix,
const ThreeGppAntennaArrayModel::ComplexVector &aW,
const ThreeGppAntennaArrayModel::ComplexVector &bW) const;
const PhasedArrayModel::ComplexVector &aW,
const PhasedArrayModel::ComplexVector &bW) const;
/**
* Computes the long term component
* \param channelMatrix the channel matrix H
@@ -170,9 +170,9 @@ private:
* \param uW the beamforming vector of the u device
* \return the long term component
*/
ThreeGppAntennaArrayModel::ComplexVector CalcLongTerm (Ptr<const MatrixBasedChannelModel::ChannelMatrix> channelMatrix,
const ThreeGppAntennaArrayModel::ComplexVector &sW,
const ThreeGppAntennaArrayModel::ComplexVector &uW) const;
PhasedArrayModel::ComplexVector CalcLongTerm (Ptr<const MatrixBasedChannelModel::ChannelMatrix> channelMatrix,
const PhasedArrayModel::ComplexVector &sW,
const PhasedArrayModel::ComplexVector &uW) const;
/**
* Computes the beamforming gain and applies it to the tx PSD
@@ -184,11 +184,11 @@ private:
* \return the rx PSD
*/
Ptr<SpectrumValue> CalcBeamformingGain (Ptr<SpectrumValue> txPsd,
ThreeGppAntennaArrayModel::ComplexVector longTerm,
PhasedArrayModel::ComplexVector longTerm,
Ptr<const MatrixBasedChannelModel::ChannelMatrix> params,
const Vector &sSpeed, const Vector &uSpeed) const;
std::unordered_map <uint32_t, Ptr<const ThreeGppAntennaArrayModel> > m_deviceAntennaMap; //!< map containig the <node, antenna> associations
std::unordered_map <uint32_t, Ptr<const PhasedArrayModel> > m_deviceAntennaMap; //!< map containig the <node, antenna> associations
mutable std::unordered_map < uint32_t, Ptr<const LongTerm> > m_longTermMap; //!< map containing the long term components
Ptr<MatrixBasedChannelModel> m_channelModel; //!< the model to generate the channel matrix

View File

@@ -27,7 +27,8 @@
#include "ns3/pointer.h"
#include "ns3/node-container.h"
#include "ns3/constant-position-mobility-model.h"
#include "ns3/three-gpp-antenna-array-model.h"
#include "ns3/uniform-planar-array.h"
#include "ns3/isotropic-antenna-model.h"
#include "ns3/three-gpp-channel-model.h"
#include "ns3/simple-net-device.h"
#include "ns3/simulator.h"
@@ -71,7 +72,7 @@ private:
* \param txAntenna the antenna object associated to the first node
* \param rxAntenna the antenna object associated to the second node
*/
void DoComputeNorm (Ptr<ThreeGppChannelModel> channelModel, Ptr<MobilityModel> txMob, Ptr<MobilityModel> rxMob, Ptr<ThreeGppAntennaArrayModel> txAntenna, Ptr<ThreeGppAntennaArrayModel> rxAntenna);
void DoComputeNorm (Ptr<ThreeGppChannelModel> channelModel, Ptr<MobilityModel> txMob, Ptr<MobilityModel> rxMob, Ptr<PhasedArrayModel> txAntenna, Ptr<PhasedArrayModel> rxAntenna);
std::vector<double> m_normVector; //!< each element is the norm of a channel realization
};
@@ -86,7 +87,7 @@ ThreeGppChannelMatrixComputationTest::~ThreeGppChannelMatrixComputationTest ()
}
void
ThreeGppChannelMatrixComputationTest::DoComputeNorm (Ptr<ThreeGppChannelModel> channelModel, Ptr<MobilityModel> txMob, Ptr<MobilityModel> rxMob, Ptr<ThreeGppAntennaArrayModel> txAntenna, Ptr<ThreeGppAntennaArrayModel> rxAntenna)
ThreeGppChannelMatrixComputationTest::DoComputeNorm (Ptr<ThreeGppChannelModel> channelModel, Ptr<MobilityModel> txMob, Ptr<MobilityModel> rxMob, Ptr<PhasedArrayModel> txAntenna, Ptr<PhasedArrayModel> rxAntenna)
{
uint64_t txAntennaElements = txAntenna->GetNumberOfElements ();
uint64_t rxAntennaElements = rxAntenna->GetNumberOfElements ();
@@ -153,8 +154,12 @@ ThreeGppChannelMatrixComputationTest::DoRun (void)
nodes.Get (1)->AggregateObject (rxMob);
// create the tx and rx antennas and set the their dimensions
Ptr<ThreeGppAntennaArrayModel> txAntenna = CreateObjectWithAttributes<ThreeGppAntennaArrayModel> ("NumColumns", UintegerValue (txAntennaElements [0]), "NumRows", UintegerValue (txAntennaElements [1]), "IsotropicElements", BooleanValue (true));
Ptr<ThreeGppAntennaArrayModel> rxAntenna = CreateObjectWithAttributes<ThreeGppAntennaArrayModel> ("NumColumns", UintegerValue (rxAntennaElements [0]), "NumRows", UintegerValue (rxAntennaElements [1]), "IsotropicElements", BooleanValue (true));
Ptr<PhasedArrayModel> txAntenna = CreateObjectWithAttributes<UniformPlanarArray> ("NumColumns", UintegerValue (txAntennaElements [0]),
"NumRows", UintegerValue (txAntennaElements [1]),
"AntennaElement", PointerValue(CreateObject<IsotropicAntennaModel> ()));
Ptr<PhasedArrayModel> rxAntenna = CreateObjectWithAttributes<UniformPlanarArray> ("NumColumns", UintegerValue (rxAntennaElements [0]),
"NumRows", UintegerValue (rxAntennaElements [1]),
"AntennaElement", PointerValue(CreateObject<IsotropicAntennaModel> ()));
// generate the channel matrix
Ptr<const ThreeGppChannelModel::ChannelMatrix> channelMatrix = channelModel->GetChannel (txMob, rxMob, txAntenna, rxAntenna);
@@ -235,7 +240,7 @@ private:
* \param rxAntenna the antenna object associated to the second node
* \param update whether if the channel matrix should be updated or not
*/
void DoGetChannel (Ptr<ThreeGppChannelModel> channelModel, Ptr<MobilityModel> txMob, Ptr<MobilityModel> rxMob, Ptr<ThreeGppAntennaArrayModel> txAntenna, Ptr<ThreeGppAntennaArrayModel> rxAntenna, bool update);
void DoGetChannel (Ptr<ThreeGppChannelModel> channelModel, Ptr<MobilityModel> txMob, Ptr<MobilityModel> rxMob, Ptr<PhasedArrayModel> txAntenna, Ptr<PhasedArrayModel> rxAntenna, bool update);
Ptr<const ThreeGppChannelModel::ChannelMatrix> m_currentChannel; //!< used by DoGetChannel to store the current channel matrix
};
@@ -250,7 +255,7 @@ ThreeGppChannelMatrixUpdateTest::~ThreeGppChannelMatrixUpdateTest ()
}
void
ThreeGppChannelMatrixUpdateTest::DoGetChannel (Ptr<ThreeGppChannelModel> channelModel, Ptr<MobilityModel> txMob, Ptr<MobilityModel> rxMob, Ptr<ThreeGppAntennaArrayModel> txAntenna, Ptr<ThreeGppAntennaArrayModel> rxAntenna, bool update)
ThreeGppChannelMatrixUpdateTest::DoGetChannel (Ptr<ThreeGppChannelModel> channelModel, Ptr<MobilityModel> txMob, Ptr<MobilityModel> rxMob, Ptr<PhasedArrayModel> txAntenna, Ptr<PhasedArrayModel> rxAntenna, bool update)
{
// retrieve the channel matrix
Ptr<const ThreeGppChannelModel::ChannelMatrix> channelMatrix = channelModel->GetChannel (txMob, rxMob, txAntenna, rxAntenna);
@@ -312,22 +317,29 @@ ThreeGppChannelMatrixUpdateTest::DoRun (void)
nodes.Get (1)->AggregateObject (rxMob);
// create the tx and rx antennas and set the their dimensions
Ptr<ThreeGppAntennaArrayModel> txAntenna = CreateObjectWithAttributes<ThreeGppAntennaArrayModel> ("NumColumns", UintegerValue (txAntennaElements [0]), "NumRows", UintegerValue (txAntennaElements [1]), "IsotropicElements", BooleanValue (true));
Ptr<ThreeGppAntennaArrayModel> rxAntenna = CreateObjectWithAttributes<ThreeGppAntennaArrayModel> ("NumColumns", UintegerValue (rxAntennaElements [0]), "NumRows", UintegerValue (rxAntennaElements [1]), "IsotropicElements", BooleanValue (true));
Ptr<PhasedArrayModel> txAntenna = CreateObjectWithAttributes<UniformPlanarArray> ("NumColumns", UintegerValue (txAntennaElements [0]),
"NumRows", UintegerValue (txAntennaElements [1]),
"AntennaElement", PointerValue(CreateObject<IsotropicAntennaModel> ()));
Ptr<PhasedArrayModel> rxAntenna = CreateObjectWithAttributes<UniformPlanarArray> ("NumColumns", UintegerValue (rxAntennaElements [0]),
"NumRows", UintegerValue (rxAntennaElements [1]),
"AntennaElement", PointerValue(CreateObject<IsotropicAntennaModel> ()));
// check if the channel matrix is correctly updated
// compute the channel matrix for the first time
uint32_t firstTimeMs = 1; // time instant at which the channel matrix is generated for the first time
Simulator::Schedule (MilliSeconds (firstTimeMs), &ThreeGppChannelMatrixUpdateTest::DoGetChannel, this, channelModel, txMob, rxMob, txAntenna, rxAntenna, true);
Simulator::Schedule (MilliSeconds (firstTimeMs), &ThreeGppChannelMatrixUpdateTest::DoGetChannel,
this, channelModel, txMob, rxMob, txAntenna, rxAntenna, true);
// call GetChannel before the update period is exceeded, the channel matrix
// should not be updated
Simulator::Schedule (MilliSeconds (firstTimeMs + updatePeriodMs / 2), &ThreeGppChannelMatrixUpdateTest::DoGetChannel, this, channelModel, txMob, rxMob, txAntenna, rxAntenna, false);
Simulator::Schedule (MilliSeconds (firstTimeMs + updatePeriodMs / 2), &ThreeGppChannelMatrixUpdateTest::DoGetChannel,
this, channelModel, txMob, rxMob, txAntenna, rxAntenna, false);
// call GetChannel when the update period is exceeded, the channel matrix
// should be recomputed
Simulator::Schedule (MilliSeconds (firstTimeMs + updatePeriodMs + 1), &ThreeGppChannelMatrixUpdateTest::DoGetChannel, this, channelModel, txMob, rxMob, txAntenna, rxAntenna, true);
Simulator::Schedule (MilliSeconds (firstTimeMs + updatePeriodMs + 1), &ThreeGppChannelMatrixUpdateTest::DoGetChannel,
this, channelModel, txMob, rxMob, txAntenna, rxAntenna, true);
Simulator::Run ();
Simulator::Destroy ();
@@ -367,7 +379,7 @@ private:
* \param otherDevice the device to communicate with
* \param otherAntenna the antenna object associated to otherDevice
*/
void DoBeamforming (Ptr<NetDevice> thisDevice, Ptr<ThreeGppAntennaArrayModel> thisAntenna, Ptr<NetDevice> otherDevice, Ptr<ThreeGppAntennaArrayModel> otherAntenna);
void DoBeamforming (Ptr<NetDevice> thisDevice, Ptr<PhasedArrayModel> thisAntenna, Ptr<NetDevice> otherDevice, Ptr<PhasedArrayModel> otherAntenna);
/**
* Test of the long term component is correctly updated when the channel
@@ -400,35 +412,15 @@ ThreeGppSpectrumPropagationLossModelTest::~ThreeGppSpectrumPropagationLossModelT
}
void
ThreeGppSpectrumPropagationLossModelTest::DoBeamforming (Ptr<NetDevice> thisDevice, Ptr<ThreeGppAntennaArrayModel> thisAntenna, Ptr<NetDevice> otherDevice, Ptr<ThreeGppAntennaArrayModel> otherAntenna)
ThreeGppSpectrumPropagationLossModelTest::DoBeamforming (Ptr<NetDevice> thisDevice, Ptr<PhasedArrayModel> thisAntenna, Ptr<NetDevice> otherDevice, Ptr<PhasedArrayModel> otherAntenna)
{
ThreeGppAntennaArrayModel::ComplexVector antennaWeights;
Vector aPos = thisDevice->GetNode ()->GetObject<MobilityModel> ()->GetPosition ();
Vector bPos = otherDevice->GetNode ()->GetObject<MobilityModel> ()->GetPosition ();
// compute the azimuth and the elevation angles
Angles completeAngle (bPos,aPos);
double hAngleRadian = fmod (completeAngle.phi, 2.0 * M_PI); // the azimuth angle
if (hAngleRadian < 0)
{
hAngleRadian += 2.0 * M_PI;
}
double vAngleRadian = completeAngle.theta; // the elevation angle
int totNoArrayElements = thisAntenna->GetNumberOfElements ();
double power = 1 / sqrt (totNoArrayElements);
for (int ind = 0; ind < totNoArrayElements; ind++)
{
Vector loc = thisAntenna->GetElementLocation (ind);
double phase = -2 * M_PI * (sin (vAngleRadian) * cos (hAngleRadian) * loc.x
+ sin (vAngleRadian) * sin (hAngleRadian) * loc.y
+ cos (vAngleRadian) * loc.z);
antennaWeights.push_back (exp (std::complex<double> (0, phase)) * power);
}
PhasedArrayModel::ComplexVector antennaWeights = thisAntenna->GetBeamformingVector (completeAngle);
thisAntenna->SetBeamformingVector (antennaWeights);
}
@@ -499,8 +491,12 @@ ThreeGppSpectrumPropagationLossModelTest::DoRun ()
nodes.Get (1)->AggregateObject (rxMob);
// create the tx and rx antennas and set the their dimensions
Ptr<ThreeGppAntennaArrayModel> txAntenna = CreateObjectWithAttributes<ThreeGppAntennaArrayModel> ("NumColumns", UintegerValue (txAntennaElements [0]), "NumRows", UintegerValue (txAntennaElements [1]));
Ptr<ThreeGppAntennaArrayModel> rxAntenna = CreateObjectWithAttributes<ThreeGppAntennaArrayModel> ("NumColumns", UintegerValue (rxAntennaElements [0]), "NumRows", UintegerValue (rxAntennaElements [1]));
Ptr<PhasedArrayModel> txAntenna = CreateObjectWithAttributes<UniformPlanarArray> ("NumColumns", UintegerValue (txAntennaElements [0]),
"NumRows", UintegerValue (txAntennaElements [1]),
"AntennaElement", PointerValue(CreateObject<IsotropicAntennaModel> ()));
Ptr<PhasedArrayModel> rxAntenna = CreateObjectWithAttributes<UniformPlanarArray> ("NumColumns", UintegerValue (rxAntennaElements [0]),
"NumRows", UintegerValue (rxAntennaElements [1]),
"AntennaElement", PointerValue(CreateObject<IsotropicAntennaModel> ()));
// initialize ThreeGppSpectrumPropagationLossModel
lossModel->AddDevice (txDev, txAntenna);
@@ -526,7 +522,7 @@ ThreeGppSpectrumPropagationLossModelTest::DoRun ()
// 2) check if the long term is updated when changing the BF vector
// change the position of the rx device and recompute the beamforming vectors
rxMob->SetPosition (Vector (10.0, 5.0, 10.0));
ThreeGppAntennaArrayModel::ComplexVector txBfVector = txAntenna->GetBeamformingVector ();
PhasedArrayModel::ComplexVector txBfVector = txAntenna->GetBeamformingVector ();
txBfVector [0] = std::complex<double> (0.0, 0.0);
txAntenna->SetBeamformingVector (txBfVector);
@@ -537,7 +533,8 @@ ThreeGppSpectrumPropagationLossModelTest::DoRun ()
rxPsdOld = rxPsdNew;
// 3) check if the long term is updated when the channel matrix is recomputed
Simulator::Schedule (MilliSeconds (101), &ThreeGppSpectrumPropagationLossModelTest::CheckLongTermUpdate, this, lossModel, txPsd, txMob, rxMob, rxPsdOld);
Simulator::Schedule (MilliSeconds (101), &ThreeGppSpectrumPropagationLossModelTest::CheckLongTermUpdate,
this, lossModel, txPsd, txMob, rxMob, rxPsdOld);
Simulator::Run ();
Simulator::Destroy ();