Integration of the 3GPP TR 38.901 fast fading model (GSoC 2019) (2/2)

Close !90
This commit is contained in:
Tommaso Zugno
2019-07-02 16:21:21 +02:00
committed by Natale Patriciello
parent b98955f20a
commit b4f5695e05
14 changed files with 4468 additions and 475 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -12,7 +12,8 @@ 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 set of classes derived from this base class that each models the radiation pattern of different types of antennas.
#. 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
------------
@@ -25,8 +26,8 @@ is obtained by translating the Cartesian coordinate system used by the
ns-3 MobilityModel into the new origin :math:`o` which is the location
of the antenna, and then transforming the coordinates of every generic
point :math:`p` of the space from Cartesian coordinates
:math:`(x,y,z)` into spherical coordinates
:math:`(r, \theta,\phi)`.
:math:`(x,y,z)` into spherical coordinates
:math:`(r, \theta,\phi)`.
The antenna model neglects the radial component :math:`r`, and
only considers the angle components :math:`(\theta, \phi)`. An antenna
radiation pattern is then expressed as a mathematical function
@@ -36,15 +37,15 @@ transmission/reception. All angles are expressed in radians.
.. _fig-antenna-coordinate-system:
.. figure:: figures/antenna-coordinate-system.*
:align: center
Coordinate system of the AntennaModel
---------------
Provided models
---------------
---------------------
Single antenna models
---------------------
In this section we describe the antenna radiation pattern models that
are included within the antenna module.
@@ -65,19 +66,19 @@ This is the cosine model described in [Chunjian]_: the antenna gain is
determined as:
.. math::
g(\phi, \theta) = \cos^{n} \left(\frac{\phi - \phi_{0}}{2} \right)
where :math:`\phi_{0}` is the azimuthal orientation of the antenna
(i.e., its direction of maximum gain) and the exponential
.. math::
n = -\frac{3}{20 \log_{10} \left( \cos \frac{\phi_{3dB}}{4} \right)}
determines the desired 3dB beamwidth :math:`\phi_{3dB}`. Note that
this radiation pattern is independent of the inclination angle
:math:`\theta`.
:math:`\theta`.
A major difference between the model of [Chunjian]_ and the one
implemented in the class CosineAntennaModel is that only the element
@@ -97,18 +98,35 @@ ParabolicAntennaModel
This model is based on the parabolic approximation of the main lobe radiation pattern. It is often used in the context of cellular system to model the radiation pattern of a cell sector, see for instance [R4-092042a]_ and [Calcev]_. The antenna gain in dB is determined as:
.. math::
g_{dB}(\phi, \theta) = -\min \left( 12 \left(\frac{\phi - \phi_{0}}{\phi_{3dB}} \right)^2, A_{max} \right)
where :math:`\phi_{0}` is the azimuthal orientation of the antenna
(i.e., its direction of maximum gain), :math:`\phi_{3dB}` is its 3 dB
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`.
:math:`\theta`.
-------------------------
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 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).
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".
.. [Balanis] C.A. Balanis, "Antenna Theory - Analysis and Design", Wiley, 2nd Ed.
@@ -118,10 +136,11 @@ antenna. Note that this radiation pattern is independent of the inclination angl
.. [Calcev] George Calcev and Matt Dillon, "Antenna Tilt Control in
CDMA Networks", in Proc. of the 2nd Annual International Wireless
Internet Conference (WICON), 2006
Internet Conference (WICON), 2006
.. [R4-092042a] 3GPP TSG RAN WG4 (Radio) Meeting #51, R4-092042, Simulation
assumptions and parameters for FDD HeNB RF requirements.
.. [38901] 3GPP. 2018. TR 38.901, Study on channel model for frequencies from 0.5 to 100 GHz, V15.0.0. (2018-06).
.. [Mailloux] Robert J. Mailloux, "Phased Array Antenna Handbook", Artech House, 2nd Ed.

View File

@@ -0,0 +1,223 @@
/* -*- 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)
while (a.phi >= M_PI)
{
a.phi -= 2 * M_PI;
}
while (a.phi < -M_PI)
{
a.phi += 2 * 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
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
// vertical 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

@@ -0,0 +1,124 @@
/* -*- 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
*/
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
* \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 normalized location of the antenna element
* with specified index assuming the left bottom corner is (0,0,0).
* \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

@@ -10,8 +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',
]
module_test = bld.create_ns3_module_test_library('antenna')
module_test.source = [
'test/test-angles.cc',
@@ -20,7 +21,7 @@ def build(bld):
'test/test-cosine-antenna.cc',
'test/test-parabolic-antenna.cc',
]
headers = bld(features='ns3header')
headers.module = 'antenna'
headers.source = [
@@ -29,6 +30,7 @@ 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',
]
bld.ns3_python_bindings()

View File

@@ -17,15 +17,15 @@ Spectrum Module
The Spectrum module aims at providing support for modeling the frequency-dependent
aspects of communications in |ns3|.
The Spectrum module aims at providing support for modeling the frequency-dependent
aspects of communications in |ns3|.
The model was first introduced in
[Baldo2009Spectrum]_, and has been enhanced and refined over the years.
[Baldo2009Spectrum]_, and has been enhanced and refined over the years.
.. _fig-spectrum-analyzer-example:
.. figure:: figures/spectrum-analyzer-example.*
:align: center
@@ -39,7 +39,7 @@ Model Description
The module provides:
* a set of classes for modeling signals and
* a set of classes for modeling signals and
* a Channel/PHY interface based on a power spectral density
signal representation that is technology-independent
@@ -70,7 +70,7 @@ information for a signal being transmitted/received by PHY devices:
* the duration of the signal
* its Power Spectral Density (PSD) of the signal, which is assumed to be constant for
the duration of the signal.
the duration of the signal.
The PSD is represented as a set of discrete scalar values each
corresponding to a certain subband in frequency. The set of frequency subbands
@@ -95,7 +95,7 @@ that inherits from ``SpectrumSignalParameters`` and extends it with
any technology-specific information that is needed. This design
is intended to model the fact that in the real world we have signals
of different technologies being simultaneously transmitted and
received over the air.
received over the air.
@@ -108,7 +108,7 @@ reception of signals over the medium. The way this interaction works is depicted
.. _fig-spectrum-channel-phy-interface:
.. figure:: figures/spectrum-channel-phy-interface.*
:align: center
@@ -127,7 +127,7 @@ both provide this functionality:
- you can plug models based on ``PropagationLossModel`` on these
channels. Only linear models (where the loss value does not
depend on the transmission power) can be used.
depend on the transmission power) can be used.
These models are single-frequency in the sense that the loss value is
applied equally to all components of the power spectral density.
@@ -144,7 +144,7 @@ both provide this functionality:
``SingleModelSpectrumChannel`` and ``MultiModelSpectrumChannel`` are
quite similar, the main difference is that
``MultiModelSpectrumChannel`` allows to use different
``MultiModelSpectrumChannel`` allows to use different
``SpectrumModel`` instances with the same channel instance, by
automatically taking care of the conversion of PSDs among the
different models.
@@ -178,23 +178,23 @@ of the available implementations:
* ``AlohaNoackNetDevice``: a minimal NetDevice that allows to send
packets over ``HalfDuplexIdealPhy`` (or other PHY model based on
the ``GenericPhy`` interface).
the ``GenericPhy`` interface).
* ``SpectrumAnalyzer``, ``WaveformGenerator`` and ``MicrowaveOven`` are examples of PHY
models other than communication devices - the names should be
self-explaining.
self-explaining.
References
==========
.. [Baldo2009Spectrum] N. Baldo and M. Miozzo, "Spectrum-aware Channel and PHY layer modeling for ns3",
.. [Baldo2009Spectrum] N. Baldo and M. Miozzo, "Spectrum-aware Channel and PHY layer modeling for ns3",
Proceedings of ICST NSTools 2009, Pisa, Italy
@@ -205,11 +205,11 @@ Usage
The main use case of the spectrum model is for developers who want to
develop a new model for the PHY layer of some wireless technology to
be used within ns-3.
be used within ns-3.
Here are some notes on how the spectrum module is expected to be used.
* ``SpectrumPhy`` and ``SpectrumChannel`` are abstract base classes. Real
code will use classes that inherit from these classes.
code will use classes that inherit from these classes.
* If you are implementing a new model for some wireless
technology of your interest, and want to use the spectrum module,
@@ -221,7 +221,7 @@ Here are some notes on how the spectrum module is expected to be used.
instances of ``SpectrumModel`` are typically statically allocated,
in order to allow several ``SpectrumValue`` instances to reference
the same ``SpectrumModel`` instance.
- a child class of ``SpectrumPhy`` which will handle transmission and
reception of signals (including, if appropriate, interference
and error modeling).
@@ -238,21 +238,21 @@ Here are some notes on how the spectrum module is expected to be used.
are quite generic. Chances are you can use them as-is. Whether you
prefer one or the other it is just a matter of whether you will
have a single SpectrumModel or multiple ones in your
simulations.
simulations.
* Typically, there will be a single SpectrumChannel instance to which
several SpectrumPhy instances are plugged. The rule of thumb is
that all PHYs that are interfering with each other shall be plugged
on the same channel. Multiple SpectrumChannel instances are
expected to be used mainly when simulating completely orthogonal
channels; for example, when simulating the uplink and downlink
channels; for example, when simulating the uplink and downlink
of a Frequency Division Duplex system, it is a good choice to use
two SpectrumChannel instances in order to reduce computational
complexity.
complexity.
* Different types of SpectrumPhy (i.e., instances of different child
classes) can be plugged on the same SpectrumChannel instance. This
is one of the main features of the
is one of the main features of the
spectrum module, to support inter-technology interference. For
example, if you implement a WifiSpectrumPhy and a
BluetoohSpectrumPhy, and plug both on a SpectrumChannel, then you'll
@@ -265,7 +265,7 @@ Here are some notes on how the spectrum module is expected to be used.
interference. A PHY device model is expected to use the
``DynamicCast<>`` operator to determine if a signal is of a certain
type it can attempt to receive. If not, the signal is normally
expected to be considered as interference.
expected to be considered as interference.
@@ -275,7 +275,7 @@ Helpers
The helpers provided in ``src/spectrum/helpers`` are mainly intended
for the example implementations described in :ref:`sec-example-model-implementations`.
for the example implementations described in :ref:`sec-example-model-implementations`.
If you are developing your custom model based on the
spectrum framework, you will probably prefer to define your own
helpers.
@@ -293,7 +293,7 @@ Attributes
interference calculations. Just be careful to choose a value that
does not make the interference calculations inaccurate.
* The example implementations described in :ref:`sec-example-model-implementations` also have several attributes.
* The example implementations described in :ref:`sec-example-model-implementations` also have several attributes.
@@ -309,7 +309,7 @@ Output
calclulated. **Note**: only single-frequency path loss is accounted
for, see the attribute description.
* The example implementations described in :ref:`sec-example-model-implementations` also provide some trace sources.
* The example implementations described in :ref:`sec-example-model-implementations` also provide some trace sources.
* The helper class ``SpectrumAnalyzerHelper`` can be conveniently
used to generate an output text file containing the spectrogram
@@ -317,14 +317,14 @@ Output
be easily plotted with ``gnuplot``. For example, if your run the
example ``adhoc-aloha-ideal-phy-with-microwave-oven`` you will get
an output file called ``spectrum-analyzer-output-3-0.tr``. From
this output file, you can generate a figure similar to
this output file, you can generate a figure similar to
:ref:`fig-spectrum-analyzer-example` by executing the following
gnuplot commands:
.. sourcecode:: none
unset surface
set pm3d at s
set pm3d at s
set palette
set key off
set view 50,50
@@ -355,7 +355,7 @@ Troubleshooting
scenarios, depending on the actual waveforms involved, the number
of interferers, etc. Moreover, it is very important to use error
models that are consistent with the interference model. The
responsibility of ensuring that the models being used are correct
responsibility of ensuring that the models being used are correct
is left to the user.
@@ -394,7 +394,7 @@ numerical errors.
Describe how the model has been tested/validated. What tests run in the
test suite? How much API and code is covered by the tests? Again,
test suite? How much API and code is covered by the tests? Again,
references to outside published work may help here.
@@ -411,7 +411,7 @@ cases are created corresponding to different PSDs of the intended
signal and different amount of transmitted bytes. The test passes if
the output of the error model (successful or failed) coincides with
the expected one which was determine offline by manually calculating
the achievable rate using Shannon's formula.
the achievable rate using Shannon's formula.
IdealPhy test
@@ -434,7 +434,7 @@ following conditions are satisfied:
:math:`1\%` of the PHY rate;
* if the PHY rate is not achievable, the application throughput shall
be zero.
be zero.
@@ -446,57 +446,57 @@ TV Transmitter Model
====================
A TV Transmitter model is implemented by the ``TvSpectrumTransmitter`` class.
This model enables transmission of realistic TV signals to be simulated and can
be used for interference modeling. It provides a customizable power spectral
density (PSD) model, with configurable attributes including the type of
modulation (with models for analog, 8-VSB, and COFDM), signal bandwidth,
power spectral density level, frequency, and transmission duration. A helper
class, ``TvSpectrumTransmitterHelper``, is also provided to assist users in
This model enables transmission of realistic TV signals to be simulated and can
be used for interference modeling. It provides a customizable power spectral
density (PSD) model, with configurable attributes including the type of
modulation (with models for analog, 8-VSB, and COFDM), signal bandwidth,
power spectral density level, frequency, and transmission duration. A helper
class, ``TvSpectrumTransmitterHelper``, is also provided to assist users in
setting up simulations.
Main Model Class
################
The main TV Transmitter model class, ``TvSpectrumTransmitter``, provides a
user-configurable PSD model that can be transmitted on the ``SpectrumChannel``.
It inherits from ``SpectrumPhy`` and is comprised of attributes and methods to
The main TV Transmitter model class, ``TvSpectrumTransmitter``, provides a
user-configurable PSD model that can be transmitted on the ``SpectrumChannel``.
It inherits from ``SpectrumPhy`` and is comprised of attributes and methods to
create and transmit the signal on the channel.
.. _spectrum-tv-cofdm:
.. figure:: figures/spectrum-tv-cofdm.*
:align: center
8K COFDM signal spectrum generated from ``TvSpectrumTransmitter`` (Left) and
8K COFDM signal spectrum generated from ``TvSpectrumTransmitter`` (Left) and
theoretical COFDM signal spectrum [KoppCOFDM] (Right)
One of the user-configurable attributes is the type of modulation for the TV
transmitter to use. The options are 8-VSB (Eight-Level Vestigial Sideband
Modulation) which is notably used in the North America ATSC digital television
standard, COFDM (Coded Orthogonal Frequency Division Multiplexing) which is
notably used in the DVB-T and ISDB-T digital television standards adopted by
various countries around the world, and analog modulation which is a legacy
technology but is still being used by some countries today. To accomplish
realistic PSD models for these modulation types, the signals PSDs were
approximated from real standards and developed into models that are scalable by
frequency and power. The COFDM PSD is approximated from Figure 12 (8k mode) of
[KoppCOFDM], the 8-VSB PSD is approximated from Figure 3 of [Baron8VSB], and the
analog PSD is approximated from Figure 4 of [QualcommAnalog]. Note that the
analog model is approximated from the NTSC standard, but other analog modulation
standards such as PAL have similar signals. The approximated COFDM PSD model is
in 8K mode. The other configurable attributes are the start frequency,
signal/channel bandwidth, base PSD, antenna type, starting time,
One of the user-configurable attributes is the type of modulation for the TV
transmitter to use. The options are 8-VSB (Eight-Level Vestigial Sideband
Modulation) which is notably used in the North America ATSC digital television
standard, COFDM (Coded Orthogonal Frequency Division Multiplexing) which is
notably used in the DVB-T and ISDB-T digital television standards adopted by
various countries around the world, and analog modulation which is a legacy
technology but is still being used by some countries today. To accomplish
realistic PSD models for these modulation types, the signals PSDs were
approximated from real standards and developed into models that are scalable by
frequency and power. The COFDM PSD is approximated from Figure 12 (8k mode) of
[KoppCOFDM], the 8-VSB PSD is approximated from Figure 3 of [Baron8VSB], and the
analog PSD is approximated from Figure 4 of [QualcommAnalog]. Note that the
analog model is approximated from the NTSC standard, but other analog modulation
standards such as PAL have similar signals. The approximated COFDM PSD model is
in 8K mode. The other configurable attributes are the start frequency,
signal/channel bandwidth, base PSD, antenna type, starting time,
and transmit duration.
``TvSpectrumTransmitter`` uses ``IsotropicAntennaModel`` as its antenna model by
default, but any model that inherits from ``AntennaModel`` is selectable, so
directional antenna models can also be used. The propagation loss models used
in simulation are configured in the ``SpectrumChannel`` that the user chooses to
use. Terrain and spherical Earth/horizon effects may be supported in future ns-3
``TvSpectrumTransmitter`` uses ``IsotropicAntennaModel`` as its antenna model by
default, but any model that inherits from ``AntennaModel`` is selectable, so
directional antenna models can also be used. The propagation loss models used
in simulation are configured in the ``SpectrumChannel`` that the user chooses to
use. Terrain and spherical Earth/horizon effects may be supported in future ns-3
propagation loss models.
After the attributes are set, along with the ``SpectrumChannel``,
``MobilityModel``, and node locations, the PSD of the TV transmitter signal can
After the attributes are set, along with the ``SpectrumChannel``,
``MobilityModel``, and node locations, the PSD of the TV transmitter signal can
be created and transmitted on the channel.
.. _sec-tv-helper-class:
@@ -504,103 +504,284 @@ be created and transmitted on the channel.
Helper Class
############
The helper class, ``TvSpectrumTransmitterHelper``, consists of features to
assist users in setting up TV transmitters for their simulations. Functionality
is also provided to easily simulate real-world scenarios.
The helper class, ``TvSpectrumTransmitterHelper``, consists of features to
assist users in setting up TV transmitters for their simulations. Functionality
is also provided to easily simulate real-world scenarios.
.. _spectrum-tv-8vsb:
.. figure:: figures/spectrum-tv-8vsb.*
:align: center
North America ATSC channel 19 & 20 signals generated using
``TvSpectrumTransmitterHelper`` (Left) and theoretical 8-VSB signal
[Baron8VSB] (Right). Note that the theoretical signal is not shown in dB
North America ATSC channel 19 & 20 signals generated using
``TvSpectrumTransmitterHelper`` (Left) and theoretical 8-VSB signal
[Baron8VSB] (Right). Note that the theoretical signal is not shown in dB
while the ns-3 generated signals are.
Using this helper class, users can easily set up TV transmitters right after
configuring attributes. Multiple transmitters can be created at a time. Also
included are real characteristics of specific geographic regions that can be
used to run realistic simulations. The regions currently included are
North America, Europe, and Japan. The frequencies and bandwidth of each TV
Using this helper class, users can easily set up TV transmitters right after
configuring attributes. Multiple transmitters can be created at a time. Also
included are real characteristics of specific geographic regions that can be
used to run realistic simulations. The regions currently included are
North America, Europe, and Japan. The frequencies and bandwidth of each TV
channel for each these regions are provided.
.. _spectrum-tv-rand-geo-points:
.. figure:: figures/spectrum-tv-rand-geo-points.*
:align: center
Plot from MATLAB implementation of CreateRegionalTvTransmitters method in
``TvSpectrumTransmitterHelper``. Shows 100 random points on Earths surface
(with altitude 0) corresponding to TV transmitter locations within a 2000 km
Plot from MATLAB implementation of CreateRegionalTvTransmitters method in
``TvSpectrumTransmitterHelper``. Shows 100 random points on Earths surface
(with altitude 0) corresponding to TV transmitter locations within a 2000 km
radius of 35° latitude and -100° longitude.
A method (CreateRegionalTvTransmitters) is provided that enables users to
randomly generate multiple TV transmitters from a specified region with a given
density within a chosen radius around a point on Earths surface. The region,
which determines the channel frequencies of the generated TV transmitters, can
be specified to be one of the three provided, while the density determines the
amount of transmitters generated. The TV transmitters' antenna heights
(altitude) above Earth's surface can also be randomly generated to be within a
given maximum altitude. This method models Earth as a perfect sphere, and
generated location points are referenced accordingly in Earth-Centered
Earth-Fixed Cartesian coordinates. Note that bodies of water on Earth are not
considered in location point generation--TV transmitters can be generated
A method (CreateRegionalTvTransmitters) is provided that enables users to
randomly generate multiple TV transmitters from a specified region with a given
density within a chosen radius around a point on Earths surface. The region,
which determines the channel frequencies of the generated TV transmitters, can
be specified to be one of the three provided, while the density determines the
amount of transmitters generated. The TV transmitters' antenna heights
(altitude) above Earth's surface can also be randomly generated to be within a
given maximum altitude. This method models Earth as a perfect sphere, and
generated location points are referenced accordingly in Earth-Centered
Earth-Fixed Cartesian coordinates. Note that bodies of water on Earth are not
considered in location point generation--TV transmitters can be generated
anywhere on Earth around the origin point within the chosen maximum radius.
Examples
########
Two example simulations are provided that demonstrate the functionality of the
TV transmitter model. ``tv-trans-example`` simulates two 8-VSB TV transmitters
with adjacent channel frequencies. ``tv-trans-regional-example`` simulates
randomly generated COFDM TV transmitters (modeling the DVB-T standard)
located around the Paris, France area with channel frequencies and bandwidths
Two example simulations are provided that demonstrate the functionality of the
TV transmitter model. ``tv-trans-example`` simulates two 8-VSB TV transmitters
with adjacent channel frequencies. ``tv-trans-regional-example`` simulates
randomly generated COFDM TV transmitters (modeling the DVB-T standard)
located around the Paris, France area with channel frequencies and bandwidths
corresponding to the European television channel allocations.
Testing
#######
The ``tv-spectrum-transmitter`` test suite verifies the accuracy of the
spectrum/PSD model in ``TvSpectrumTransmitter`` by testing if the maximum power
spectral density, start frequency, and end frequency comply with expected values
The ``tv-spectrum-transmitter`` test suite verifies the accuracy of the
spectrum/PSD model in ``TvSpectrumTransmitter`` by testing if the maximum power
spectral density, start frequency, and end frequency comply with expected values
for various test cases.
The ``tv-helper-distribution`` test suite verifies the functionality of the
method in ``TvSpectrumTransmitterHelper`` that generates a random number of TV
transmitters based on the given density (low, medium, or high) and maximum
number of TV channels. It verifies that the number of TV transmitters generated
The ``tv-helper-distribution`` test suite verifies the functionality of the
method in ``TvSpectrumTransmitterHelper`` that generates a random number of TV
transmitters based on the given density (low, medium, or high) and maximum
number of TV channels. It verifies that the number of TV transmitters generated
does not exceed the expected bounds.
The CreateRegionalTvTransmitters method in ``TvSpectrumTransmitterHelper``
described in :ref:`sec-tv-helper-class` uses two methods from the
``GeographicPositions`` class in the Mobility module to generate the random
Cartesian points on or above earth's surface around an origin point which
correspond to TV transmitter positions. The first method converts Earth
geographic coordinates to Earth-Centered Earth-Fixed (ECEF) Cartesian
coordinates, and is tested in the ``geo-to-cartesian`` test suite by comparing
(with 10 meter tolerance) its output with the output of the geographic to ECEF
conversion function [MatlabGeo] of the MATLAB Mapping Toolbox for numerous
test cases. The other used method generates random ECEF Cartesian points around
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
The CreateRegionalTvTransmitters method in ``TvSpectrumTransmitterHelper``
described in :ref:`sec-tv-helper-class` uses two methods from the
``GeographicPositions`` class in the Mobility module to generate the random
Cartesian points on or above earth's surface around an origin point which
correspond to TV transmitter positions. The first method converts Earth
geographic coordinates to Earth-Centered Earth-Fixed (ECEF) Cartesian
coordinates, and is tested in the ``geo-to-cartesian`` test suite by comparing
(with 10 meter tolerance) its output with the output of the geographic to ECEF
conversion function [MatlabGeo] of the MATLAB Mapping Toolbox for numerous
test cases. The other used method generates random ECEF Cartesian points around
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.
3GPP TR 38.901 fast fading model
================================
The framework described by TR 38.901 [TR38901]_ is a 3D statistical Spatial
Channel Model supporting different propagation environments (e.g., urban,
rural, indoor), multi-antenna operations and the modeling of wireless channels
between 0.5 and 100 GHz.
The overall channel is represented by the matrix H(t,τ), in which each
entry H :sub:`u,s` (t,τ) corresponds to the impulse response of the channel between the
s-th element of the transmitting antenna and the u-th element of the receiving
antenna. H :sub:`u,s` (t,τ) is generated by the superposition of N different multi-path
components, called clusters, each of which composed of M different rays.
The channel matrix generation procedure accounts for large and small scale
propagation phenomena. The classes ThreeGppSpectrumPropagationLossModel and
ThreeGppChannelModel included in the spectrum module takes care of the generation
of the channel coefficients and the computation of the frequency-dependent
propagation loss.
Implementation
##############
Our implementation is described in [Zugno]_. It is based on the model described
in [Zhang]_, but the code has been refactored, extended, and aligned to TR 38.901
[TR38901]_.
The fundamental assumption behind this model is the channel reciprocity, i.e.,
the impulse response of the channel between node a and node b is the same as
between node b and node a.
To deal with the equivalence of the channel between a and b, no matter who is
the transmitter and who is the receiver, the model considers the pair of nodes
to be composed by one "s" and one "u" node. The channel matrix, as well as other
parameters, are saved and used under the assumption that, within a pair, the
definition of the "s" and "u" node will always be the same. For more details,
please have a look at the documentation of the classes
ThreeGppChannelModel and ThreeGppSpectrumPropagationLossModel.
**Note:**
* Currently, no error model is provided; a link-to-system campaign may be
needed to incorporate it in existing modules.
* The model does not include any spatial consistency update procedure
(see [TR38901]_, Sec. 7.6.1). The implementation of this feature is left
as future work.
* Issue regarding the blockage model: according to 3GPP TR 38.901 v15.0.0
(2018-06) section 7.6.4.1, the blocking region for self-blocking is provided
in LCS.
However, here, clusterAOA and clusterZOA are in GCS and blocking check is
performed for self-blocking similar to non-self blocking, that is in GCS.
One would expect the angles to be transposed to LCS before checking self-blockage.
ThreeGppSpectrumPropagationLossModel
####################################
The class ThreeGppSpectrumPropagationLossModel extends the SpectrumPropagationLossModel
interface and enables the modeling of frequency
dependent propagation phenomena. The main method is DoCalcRxPowerSpectralDensity,
which takes as input the power spectral density (PSD) of the transmitted signal,
the mobility models of the transmitting node and receiving node, and
returns the PSD of the received signal.
Procedure used to compute the PSD of to compute the PSD of the received signal:
1. Retrieve the beamforming vectors
To account for the beamforming, ThreeGppSpectrumPropagationLossModel has to
retrieve the beamforming vectors of the transmitting and receiving antennas.
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,
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.
2. Retrieve the channel matrix
The ThreeGppSpectrumPropagationLossModel relies on the ThreeGppChannelModel class
to obtain the channel matrix. In particular, it makes use of the method GetChannel,
which returns a ThreeGppChannelMatrix object containing the channel
matrix and other channel parameters.
The ThreeGppChannelModel instance is automatically
created in the the ThreeGppSpectrumPropagationLossModel constructor and it can
be configured using the method SetChannelModelAttribute ().
4. Compute the long term component
The method GetLongTerm returns the long term component obtained by multiplying
the channel matrix and the beamforming vectors. To reduce the computational
load, the long term components associated to the different channels are
stored in the m_longTermMap and recomputed only if the associated channel
matrix is updated or if the transmitting and/or receiving beamforming vectors
have changed. Given the channel reciprocity assumption, for each node pair a
single long term component is saved in the map.
5. Apply the small scale fading and compute the channel gain
The method CalcBeamformingGain computes the channel gain in each sub-band and
applies it to the PSD of the transmitted signal to obtain the received PSD.
To compute the sub-band gain, it accounts for the Doppler phenomenon and the
time dispersion effect on each cluster.
In order to reduce the computational load, the Doppler component of each
cluster is computed considering only the central ray.
ThreeGppChannelModel
####################
The class ThreeGppChannelModel implements the channel matrix generation procedure
described in Sec. of [TR38901]_.
The main method is GetChannel, which takes as input the mobility models of
the transmitter and receiver nodes, the associated antenna objects,
and returns a ThreeGppChannelMatrix object containing:
* the channel matrix of size UxSxN, where U is the number of receiving antenna elements, S is the number of transmitting antenna elements and N is the number of clusters
* the clusters delays, as an array of size N
* the clusters arrival and departure angles, as a 2D array in which each row corresponds to a direction (AOA, ZOA, AOD, ZOD) and each column corresponds to a different cluster
* a time stamp indicating the time at which the channel matrix was generated
* the node IDs
* other channel parameters
The ThreeGppChannelMatrix objects are saved
in the map m_channelMap and updated when the coherence time
expires, or in case the LOS/NLOS channel condition changes.
The coherence time can be configured through
the attribute "UpdatePeriod", and should be chosen by taking into account all the
factors that affects the channel variability, such as mobility, frequency,
propagation scenario, etc. By default, it is set to 0, which means that the
channel is recomputed only when the LOS/NLOS condition changes.
It is possible to configure the propagation scenario and the operating frequency
of interest through the attributes "Scenario" and "Frequency", respectively.
**Blockage model:** 3GPP TR 38.901 also provides an optional
feature that can be used to model the blockage effect due to the
presence of obstacles, such as trees, cars or humans, at the level
of a single cluster. This differs from a complete blockage, which
would result in an LOS to NLOS transition. Therefore, when this
feature is enabled, an additional attenuation is added to certain
clusters, depending on their angle of arrival. There are two possi-
ble methods for the computation of the additional attenuation, i.e.,
stochastic (Model A) and geometric (Model B). In this work, we
used the implementation provided by [Zhang]_, which
uses the stochastic method. In particular, the model is implemented by the
method CalcAttenuationOfBlockage, which computes the additional attenuation.
The blockage feature can be disable through the attribute "Blockage". Also, the
attributes "NumNonselfBlocking", "PortraitMode" and "BlockerSpeed" can be used
to configure the model.
Testing
#######
The test suite ThreeGppChannelTestSuite includes three test cases:
* ThreeGppChannelMatrixComputationTest checks if the channel matrix has the
correct dimensions and if it correctly normalized
* ThreeGppChannelMatrixUpdateTest, which checks if the channel matrix is correctly
updated when the coherence time exceeds
* ThreeGppSpectrumPropagationLossModelTest, which tests the functionalities of the
class ThreeGppSpectrumPropagationLossModel. It builds a simple network composed of two
nodes, computes the power spectral density received by the
receiving node, and (i) checks if the long term components for the direct and the reverse link
are the same, (ii) checks if the long term component is updated when changing the beamforming
vectors, (iii) checks if the long term is updated when changing the channel matrix
**Note:** TR 38.901 includes a calibration procedure that can be used to validate
the model, but it requires some additional features which are not currently
implemented, thus is left as future work.
References
##########
.. [Baron8VSB] Baron, Stanley. "First-Hand:Digital Television: The Digital
Terrestrial Television Broadcasting (DTTB) Standard." IEEE Global History
.. [Baron8VSB] Baron, Stanley. "First-Hand:Digital Television: The Digital
Terrestrial Television Broadcasting (DTTB) Standard." IEEE Global History
Network. <http://www.ieeeghn.org/wiki/index.php/First-Hand:Digital_Television:_The_Digital_Terrestrial_Television_Broadcasting_(DTTB)_Standard>.
.. [KoppCOFDM] Kopp, Carlo. "High Definition Television." High Definition
.. [KoppCOFDM] Kopp, Carlo. "High Definition Television." High Definition
Television. Air Power Australia. <http://www.ausairpower.net/AC-1100.html>.
.. [MatlabGeo] "Geodetic2ecef." Convert Geodetic to Geocentric (ECEF)
Coordinates. The MathWorks, Inc.
.. [MatlabGeo] "Geodetic2ecef." Convert Geodetic to Geocentric (ECEF)
Coordinates. The MathWorks, Inc.
<http://www.mathworks.com/help/map/ref/geodetic2ecef.html>.
.. [QualcommAnalog] Stephen Shellhammer, Ahmed Sadek, and Wenyi Zhang.
"Technical Challenges for Cognitive Radio in the TV White Space Spectrum."
.. [QualcommAnalog] Stephen Shellhammer, Ahmed Sadek, and Wenyi Zhang.
"Technical Challenges for Cognitive Radio in the TV White Space Spectrum."
Qualcomm Incorporated.
.. [TR38901] 3GPP. 2018. TR 38.901. Study on channel for frequencies from 0.5 to
100 GHz. V.15.0.0. (2018-06).
.. [Zhang] Menglei Zhang, Michele Polese, Marco Mezzavilla, Sundeep Rangan,
Michele Zorzi. "ns-3 Implementation of the 3GPP MIMO Channel Model for
Frequency Spectrum above 6 GHz". In Proceedings of the Workshop on ns-3
(WNS3 '17). 2017.
.. [Zugno] Tommaso Zugno, Michele Polese, Natale Patriciello, Biljana Bojovic,
Sandra Lagen, Michele Zorzi. "Implementation of a Spatial Channel Model for
ns-3". Submitted to the Workshop on ns-3 (WNS3 '20). 2020.
Available: https://arxiv.org/abs/2002.09341

View File

@@ -0,0 +1,262 @@
/* -*- 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
*/
/**
* This example shows how to configure the 3GPP channel model classes to
* compute the SNR between two nodes.
* The simulation involves two static nodes which are placed at a certain
* distance from each other and communicates through a wireless channel at
* 2 GHz with a bandwidth of 18 MHz. The default propagation environment is
* 3D-urban macro (UMa) and it can be configured changing the value of the
* string "scenario".
* Each node hosts a SimpleNetDevice and has an antenna array with 4 elements.
*/
#include "ns3/core-module.h"
#include "ns3/three-gpp-channel-model.h"
#include "ns3/three-gpp-antenna-array-model.h"
#include <fstream>
#include "ns3/three-gpp-spectrum-propagation-loss-model.h"
#include "ns3/net-device.h"
#include "ns3/simple-net-device.h"
#include "ns3/node.h"
#include "ns3/node-container.h"
#include "ns3/mobility-model.h"
#include "ns3/constant-position-mobility-model.h"
#include "ns3/lte-spectrum-value-helper.h"
#include "ns3/channel-condition-model.h"
#include "ns3/three-gpp-propagation-loss-model.h"
NS_LOG_COMPONENT_DEFINE ("ThreeGppChannelExample");
using namespace ns3;
static Ptr<ThreeGppPropagationLossModel> m_propagationLossModel; //!< the PropagationLossModel object
static Ptr<ThreeGppSpectrumPropagationLossModel> m_spectrumLossModel; //!< the SpectrumPropagationLossModel object
/**
* Perform the beamforming using the DFT beamforming method
* \param thisDevice the device performing the beamforming
* \param thisAntenna the antenna object associated to thisDevice
* \param otherDevice the device towards which point the beam
*/
static void
DoBeamforming (Ptr<NetDevice> thisDevice, Ptr<ThreeGppAntennaArrayModel> thisAntenna, Ptr<NetDevice> otherDevice)
{
ThreeGppAntennaArrayModel::ComplexVector antennaWeights;
// retrieve the position of the two devices
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 posX = bPos.x - aPos.x;
double phiAngle = atan ((bPos.y - aPos.y) / posX);
if (posX < 0)
{
phiAngle = phiAngle + M_PI;
}
if (phiAngle < 0)
{
phiAngle = phiAngle + 2 * M_PI;
}
double hAngleRadian = fmod ((phiAngle + M_PI),2 * M_PI - M_PI); // the azimuth angle
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);
}
/**
* Compute the average SNR
* \param txMob the tx mobility model
* \param rxMob the rx mobility model
* \param txPow the transmitting power in dBm
* \param noiseFigure the noise figure in dB
*/
static void
ComputeSnr (Ptr<MobilityModel> txMob, Ptr<MobilityModel> rxMob, double txPow, double noiseFigure)
{
// Create the tx PSD using the LteSpectrumValueHelper
// 100 RBs corresponds to 18 MHz (1 RB = 180 kHz)
// EARFCN 100 corresponds to 2125.00 MHz
std::vector<int> activeRbs0 (100);
for (int i = 0; i < 100 ; i++)
{
activeRbs0[i] = i;
}
Ptr<SpectrumValue> txPsd = LteSpectrumValueHelper::CreateTxPowerSpectralDensity (2100, 100, txPow, activeRbs0);
Ptr<SpectrumValue> rxPsd = txPsd->Copy ();
NS_LOG_DEBUG ("Average tx power " << 10*log10(Sum (*txPsd) * 180e3) << " dB");
// create the noise PSD
Ptr<SpectrumValue> noisePsd = LteSpectrumValueHelper::CreateNoisePowerSpectralDensity (2100, 100, noiseFigure);
NS_LOG_DEBUG ("Average noise power " << 10*log10 (Sum (*noisePsd) * 180e3) << " dB");
// apply the pathloss
double propagationGainDb = m_propagationLossModel->CalcRxPower (0, txMob, rxMob);
NS_LOG_DEBUG ("Pathloss " << -propagationGainDb << " dB");
double propagationGainLinear = std::pow (10.0, (propagationGainDb) / 10.0);
*(rxPsd) *= propagationGainLinear;
// apply the fast fading and the beamforming gain
rxPsd = m_spectrumLossModel->CalcRxPowerSpectralDensity (rxPsd, txMob, rxMob);
NS_LOG_DEBUG ("Average rx power " << 10*log10 (Sum (*rxPsd) * 180e3) << " dB");
// compute the SNR
NS_LOG_DEBUG ("Average SNR " << 10 * log10 (Sum (*rxPsd) / Sum (*noisePsd)) << " dB");
// print the SNR and pathloss values in the snr-trace.txt file
std::ofstream f;
f.open ("snr-trace.txt", std::ios::out | std::ios::app);
f << Simulator::Now ().GetSeconds () << " " << 10 * log10 (Sum (*rxPsd) / Sum (*noisePsd)) << " " << propagationGainDb << std::endl;
f.close ();
}
int
main (int argc, char *argv[])
{
double frequency = 2125.0e6; // operating frequency in Hz (corresponds to EARFCN 2100)
double txPow = 49.0; // tx power in dBm
double noiseFigure = 9.0; // noise figure in dB
double distance = 10.0; // distance between tx and rx nodes in meters
uint32_t simTime = 10000; // simulation time in milliseconds
uint32_t timeRes = 10; // time resolution in milliseconds
std::string scenario = "UMa"; // 3GPP propagation scenario
Config::SetDefault ("ns3::ThreeGppChannelModel::UpdatePeriod", TimeValue(MilliSeconds (1))); // update the channel at each iteration
Config::SetDefault ("ns3::ThreeGppChannelConditionModel::UpdatePeriod", TimeValue(MilliSeconds (0.0))); // do not update the channel condition
RngSeedManager::SetSeed(1);
RngSeedManager::SetRun(1);
// create and configure the factories for the channel condition and propagation loss models
ObjectFactory propagationLossModelFactory;
ObjectFactory channelConditionModelFactory;
if (scenario == "RMa")
{
propagationLossModelFactory.SetTypeId (ThreeGppRmaPropagationLossModel::GetTypeId ());
channelConditionModelFactory.SetTypeId (ThreeGppRmaChannelConditionModel::GetTypeId ());
}
else if (scenario == "UMa")
{
propagationLossModelFactory.SetTypeId (ThreeGppUmaPropagationLossModel::GetTypeId ());
channelConditionModelFactory.SetTypeId (ThreeGppUmaChannelConditionModel::GetTypeId ());
}
else if (scenario == "UMi-StreetCanyon")
{
propagationLossModelFactory.SetTypeId (ThreeGppUmiStreetCanyonPropagationLossModel::GetTypeId ());
channelConditionModelFactory.SetTypeId (ThreeGppUmiStreetCanyonChannelConditionModel::GetTypeId ());
}
else if (scenario == "InH-OfficeOpen")
{
propagationLossModelFactory.SetTypeId (ThreeGppIndoorOfficePropagationLossModel::GetTypeId ());
channelConditionModelFactory.SetTypeId (ThreeGppIndoorOpenOfficeChannelConditionModel::GetTypeId ());
}
else if (scenario == "InH-OfficeMixed")
{
propagationLossModelFactory.SetTypeId (ThreeGppIndoorOfficePropagationLossModel::GetTypeId ());
channelConditionModelFactory.SetTypeId (ThreeGppIndoorMixedOfficeChannelConditionModel::GetTypeId ());
}
else
{
NS_FATAL_ERROR ("Unknown scenario");
}
// create the propagation loss model
m_propagationLossModel = propagationLossModelFactory.Create<ThreeGppPropagationLossModel> ();
m_propagationLossModel->SetAttribute ("Frequency", DoubleValue (frequency));
m_propagationLossModel->SetAttribute ("ShadowingEnabled", BooleanValue (false));
// create the spectrum propagation loss model
m_spectrumLossModel = CreateObject<ThreeGppSpectrumPropagationLossModel> ();
m_spectrumLossModel->SetChannelModelAttribute ("Frequency", DoubleValue (frequency));
m_spectrumLossModel->SetChannelModelAttribute ("Scenario", StringValue (scenario));
// create the channel condition model and associate it with the spectrum and
// propagation loss model
Ptr<ChannelConditionModel> condModel = channelConditionModelFactory.Create<ThreeGppChannelConditionModel> ();
m_spectrumLossModel->SetChannelModelAttribute ("ChannelConditionModel", PointerValue (condModel));
m_propagationLossModel->SetChannelConditionModel (condModel);
// create the tx and rx nodes
NodeContainer nodes;
nodes.Create (2);
// create the tx and rx devices
Ptr<SimpleNetDevice> txDev = CreateObject<SimpleNetDevice> ();
Ptr<SimpleNetDevice> rxDev = CreateObject<SimpleNetDevice> ();
// associate the nodes and the devices
nodes.Get (0)->AddDevice (txDev);
txDev->SetNode (nodes.Get (0));
nodes.Get (1)->AddDevice (rxDev);
rxDev->SetNode (nodes.Get (1));
// create the tx and rx mobility models, set the positions
Ptr<MobilityModel> txMob = CreateObject<ConstantPositionMobilityModel> ();
txMob->SetPosition (Vector (0.0,0.0,10.0));
Ptr<MobilityModel> rxMob = CreateObject<ConstantPositionMobilityModel> ();
rxMob->SetPosition (Vector (distance,0.0,1.6));
// assign the mobility models to the nodes
nodes.Get (0)->AggregateObject (txMob);
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));
// initialize the devices in the ThreeGppSpectrumPropagationLossModel
m_spectrumLossModel->AddDevice (txDev, txAntenna);
m_spectrumLossModel->AddDevice (rxDev, rxAntenna);
// set the beamforming vectors
DoBeamforming (txDev, txAntenna, rxDev);
DoBeamforming (rxDev, rxAntenna, txDev);
for (int i = 0; i < floor (simTime / timeRes); i++)
{
Simulator::Schedule (MilliSeconds (timeRes*i), &ComputeSnr, txMob, rxMob, txPow, noiseFigure);
}
Simulator::Run ();
Simulator::Destroy ();
return 0;
}

View File

@@ -20,3 +20,7 @@ def build(bld):
obj = bld.create_ns3_program('tv-trans-regional-example',
['spectrum', 'mobility', 'core'])
obj.source = 'tv-trans-regional-example.cc'
obj = bld.create_ns3_program('three-gpp-channel-example',
['spectrum', 'mobility', 'core', 'lte'])
obj.source = 'three-gpp-channel-example.cc'

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,309 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2015, NYU WIRELESS, Tandon School of Engineering,
* New York University
* 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_CHANNEL_H
#define THREE_GPP_CHANNEL_H
#include <complex.h>
#include "ns3/angles.h"
#include <ns3/object.h>
#include <ns3/nstime.h>
#include <ns3/random-variable-stream.h>
#include <ns3/boolean.h>
#include <ns3/three-gpp-antenna-array-model.h>
#include <unordered_map>
#include <ns3/channel-condition-model.h>
namespace ns3 {
class MobilityModel;
/**
* \ingroup spectrum
* \brief Channel Matrix Generation following 3GPP TR 38.901
*
* The class implements the channel matrix generation procedure
* described in 3GPP TR 38.901.
*
* \see GetChannel
*/
class ThreeGppChannelModel : public Object
{
public:
/**
* Constructor
*/
ThreeGppChannelModel ();
/**
* Destructor
*/
~ThreeGppChannelModel ();
/**
* Get the type ID
* \return the object TypeId
*/
static TypeId GetTypeId ();
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<Complex2DVector> Complex3DVector; //!< type definition for complex 3D matrices
/**
* Data structure that stores a channel realization
*/
struct ThreeGppChannelMatrix : public SimpleRefCount<ThreeGppChannelMatrix>
{
Complex3DVector m_channel; //!< channel matrix H[u][s][n].
DoubleVector m_delay; //!< cluster delay.
Double2DVector m_angle; //!< cluster angle angle[direction][n], where direction = 0(aoa), 1(zoa), 2(aod), 3(zod) in degree.
Double2DVector m_nonSelfBlocking; //!< store the blockages
Time m_generatedTime; //!< generation time
std::pair<uint32_t, uint32_t> m_nodeIds; //!< the first element is the s-node ID, the second element is the u-node ID
bool m_los; //!< true if LOS, false if NLOS
// TODO these are not currently used, they have to be correctly set when including the spatial consistent update procedure
/*The following parameters are stored for spatial consistent updating*/
Vector m_preLocUT; //!< location of UT when generating the previous channel
Vector m_locUT; //!< location of UT
Double2DVector m_norRvAngles; //!< stores the normal variable for random angles angle[cluster][id] generated for equation (7.6-11)-(7.6-14), where id = 0(aoa),1(zoa),2(aod),3(zod)
double m_DS; //!< delay spread
double m_K; //!< K factor
uint8_t m_numCluster; //!< reduced cluster number;
Double3DVector m_clusterPhase; //!< the initial random phases
bool m_o2i; //!< true if O2I
Vector m_speed; //!< velocity
double m_dis2D; //!< 2D distance between tx and rx
double m_dis3D; //!< 3D distance between tx and rx
/**
* Returns true if the ThreeGppChannelMatrix object was generated
* considering node b as transmitter and node a as receiver.
* \param aid id of the a node
* \param bid id of the b node
* \return true if b is the rx and a is the tx, false otherwise
*/
bool IsReverse (const uint32_t aId, const uint32_t bId) const
{
uint32_t sId, uId;
std::tie (sId, uId) = m_nodeIds;
NS_ASSERT_MSG ((sId == aId && uId == bId) || (sId == bId && uId == aId),
"This matrix represents the channel between " << sId << " and " << uId);
return (sId == bId && uId == aId);
}
};
/**
* Set the channel condition model
* \param a pointer to the ChannelConditionModel object
*/
void SetChannelConditionModel (Ptr<ChannelConditionModel> model);
/**
* Get the associated channel condition model
* \return a pointer to the ChannelConditionModel object
*/
Ptr<ChannelConditionModel> GetChannelConditionModel () const;
/**
* Sets the center frequency of the model
* \param f the center frequency in Hz
*/
void SetFrequency (double f);
/**
* Returns the center frequency
* \return the center frequency in Hz
*/
double GetFrequency (void) const;
/**
* Sets the propagation scenario
* \param scenario the propagation scenario
*/
void SetScenario (const std::string &scenario);
/**
* Returns the propagation scenario
* \return the propagation scenario
*/
std::string GetScenario (void) const;
/**
* Looks for the channel matrix associated to the aMob and bMob pair in m_channelMap.
* If found, it checks if it has to be updated. If not found or if it has to
* be updated, it generates a new uncorrelated channel matrix using the
* method GetNewChannel and updates m_channelMap.
*
* We assume channel reciprocity between each node pair (i.e., H_ab = H_ba^T),
* therefore GetChannel (a, b) and GetChannel (b, a) will return the same
* ThreeGppChannelMatrix object.
* To understand if the channel matrix corresponds to H_ab or H_ba, we provide
* the method ThreeGppChannelMatrix::IsReverse. For instance, if the channel
* matrix corresponds to H_ab, a call to IsReverse (idA, idB) will return
* false, conversely, IsReverse (idB, idA) will return true.
*
* \param aMob mobility model of the a device
* \param bMob mobility model of the b device
* \param aAntenna antenna of the a device
* \param bAntenna antenna of the b device
* \return the channel matrix
*/
Ptr<const ThreeGppChannelMatrix> GetChannel (Ptr<const MobilityModel> aMob,
Ptr<const MobilityModel> bMob,
Ptr<const ThreeGppAntennaArrayModel> aAntenna,
Ptr<const ThreeGppAntennaArrayModel> bAntenna);
/**
* \brief Assign a fixed random variable stream number to the random variables
* used by this model.
*
* \param stream first stream index to use
* \return the number of stream indices assigned by this model
*/
int64_t AssignStreams (int64_t stream);
static const uint8_t AOA_INDEX = 0; //!< index of the AOA value in the m_angle array
static const uint8_t ZOA_INDEX = 1; //!< index of the ZOA value in the m_angle array
static const uint8_t AOD_INDEX = 2; //!< index of the AOD value in the m_angle array
static const uint8_t ZOD_INDEX = 3; //!< index of the ZOD value in the m_angle array
static const uint8_t PHI_INDEX = 0; //!< index of the PHI value in the m_nonSelfBlocking array
static const uint8_t X_INDEX = 1; //!< index of the X value in the m_nonSelfBlocking array
static const uint8_t THETA_INDEX = 2; //!< index of the THETA value in the m_nonSelfBlocking array
static const uint8_t Y_INDEX = 3; //!< index of the Y value in the m_nonSelfBlocking array
static const uint8_t R_INDEX = 4; //!< index of the R value in the m_nonSelfBlocking array
/**
* Calculate the channel key using the Cantor function
* \param x1 first value
* \param x2 second value
* \return \f$ (((x1 + x2) * (x1 + x2 + 1))/2) + x2; \f$
*/
static constexpr uint32_t GetKey (uint32_t x1, uint32_t x2)
{
return (((x1 + x2) * (x1 + x2 + 1)) / 2) + x2;
}
private:
/**
* Data structure that stores the parameters of 3GPP TR 38.901, Table 7.5-6,
* for a certain scenario
*/
struct ParamsTable : public SimpleRefCount<ParamsTable>
{
uint8_t m_numOfCluster = 0;
uint8_t m_raysPerCluster = 0;
double m_uLgDS = 0;
double m_sigLgDS = 0;
double m_uLgASD = 0;
double m_sigLgASD = 0;
double m_uLgASA = 0;
double m_sigLgASA = 0;
double m_uLgZSA = 0;
double m_sigLgZSA = 0;
double m_uLgZSD = 0;
double m_sigLgZSD = 0;
double m_offsetZOD = 0;
double m_cDS = 0;
double m_cASD = 0;
double m_cASA = 0;
double m_cZSA = 0;
double m_uK = 0;
double m_sigK = 0;
double m_rTau = 0;
double m_uXpr = 0;
double m_sigXpr = 0;
double m_perClusterShadowingStd = 0;
double m_sqrtC[7][7];
};
/**
* Get the parameters needed to apply the channel generation procedure
* \param los the LOS/NLOS condition
* \param o2i whether if it is an outdoor to indoor transmission
* \param hBS the height of the BS
* \param hUT the height of the UT
* \param distance2D the 2D distance between tx and rx
* \return the parameters table
*/
Ptr<const ParamsTable> GetThreeGppTable (bool los, bool o2i, double hBS, double hUT, double distance2D) const;
/**
* Compute the channel matrix between two devices using the procedure
* described in 3GPP TR 38.901
* \param locUT the location of the UT
* \param los the LOS/NLOS condition
* \param o2i whether if it is an outdoor to indoor transmission
* \param sAntenna the s node antenna array
* \param uAntenna the u node antenna array
* \param uAngle the u node angle
* \param sAngle the s node angle
* \param dis2D the 2D distance between tx and rx
* \param hBS the height of the BS
* \param hUT the height of the UT
* \return the channel realization
*/
Ptr<ThreeGppChannelMatrix> GetNewChannel (Vector locUT, bool los, bool o2i,
Ptr<const ThreeGppAntennaArrayModel> sAntenna,
Ptr<const ThreeGppAntennaArrayModel> uAntenna,
Angles &uAngle, Angles &sAngle,
double dis2D, double hBS, double hUT) const;
/**
* Applies the blockage model A described in 3GPP TR 38.901
* \param params the channel matrix
* \param clusterAOA vector containing the azimuth angle of arrival for each cluster
* \param clusterZOA vector containing the zenith angle of arrival for each cluster
* \return vector containing the power attenuation for each cluster
*/
DoubleVector CalcAttenuationOfBlockage (Ptr<ThreeGppChannelMatrix> params,
const DoubleVector &clusterAOA,
const DoubleVector &clusterZOA) const;
/**
* Check if the channel matrix has to be updated
* \param channelMatrix channel matrix
* \param isLos the current los condition
* \return true if the channel matrix has to be updated, false otherwise
*/
bool ChannelMatrixNeedsUpdate (Ptr<const ThreeGppChannelMatrix> channelMatrix, bool isLos) const;
std::unordered_map<uint32_t, Ptr<ThreeGppChannelMatrix> > m_channelMap; //!< map containing the channel realizations
Time m_updatePeriod; //!< the channel update period
double m_frequency; //!< the operating frequency
std::string m_scenario; //!< the 3GPP scenario
Ptr<ChannelConditionModel> m_channelConditionModel; //!< the channel condition model
Ptr<UniformRandomVariable> m_uniformRv; //!< uniform random variable
Ptr<NormalRandomVariable> m_normalRv; //!< normal random variable
// parameters for the blockage model
bool m_blockage; //!< enables the blockage model A
uint16_t m_numNonSelfBlocking; //!< number of non-self-blocking regions
bool m_portraitMode; //!< true if potrait mode, false if landscape
double m_blockerSpeed; //!< the blocker speed
};
} // namespace ns3
#endif /* THREE_GPP_CHANNEL_H */

View File

@@ -0,0 +1,293 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2015, NYU WIRELESS, Tandon School of Engineering,
* New York University
* 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 "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/node.h"
#include "ns3/channel-condition-model.h"
#include "ns3/double.h"
#include "ns3/string.h"
#include "ns3/simulator.h"
#include "ns3/pointer.h"
#include <map>
namespace ns3 {
NS_LOG_COMPONENT_DEFINE ("ThreeGppSpectrumPropagationLossModel");
NS_OBJECT_ENSURE_REGISTERED (ThreeGppSpectrumPropagationLossModel);
ThreeGppSpectrumPropagationLossModel::ThreeGppSpectrumPropagationLossModel ()
{
NS_LOG_FUNCTION (this);
m_channelModel = CreateObject<ThreeGppChannelModel> ();
}
ThreeGppSpectrumPropagationLossModel::~ThreeGppSpectrumPropagationLossModel ()
{
NS_LOG_FUNCTION (this);
}
TypeId
ThreeGppSpectrumPropagationLossModel::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::ThreeGppSpectrumPropagationLossModel")
.SetParent<SpectrumPropagationLossModel> ()
.SetGroupName ("Spectrum")
.AddConstructor<ThreeGppSpectrumPropagationLossModel> ()
;
return tid;
}
void
ThreeGppSpectrumPropagationLossModel::AddDevice (Ptr<NetDevice> n, Ptr<const ThreeGppAntennaArrayModel> 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));
}
double
ThreeGppSpectrumPropagationLossModel::GetFrequency () const
{
DoubleValue freq;
m_channelModel->GetAttribute ("Frequency", freq);
return freq.Get ();
}
void
ThreeGppSpectrumPropagationLossModel::SetChannelModelAttribute (const std::string &name, const AttributeValue &value)
{
m_channelModel->SetAttribute (name, value);
}
void
ThreeGppSpectrumPropagationLossModel::GetChannelModelAttribute (const std::string &name, AttributeValue &value) const
{
m_channelModel->GetAttribute (name, value);
}
ThreeGppAntennaArrayModel::ComplexVector
ThreeGppSpectrumPropagationLossModel::CalcLongTerm (Ptr<const ThreeGppChannelModel::ThreeGppChannelMatrix> params,
const ThreeGppAntennaArrayModel::ComplexVector &sW,
const ThreeGppAntennaArrayModel::ComplexVector &uW) const
{
NS_LOG_FUNCTION (this);
uint16_t sAntenna = static_cast<uint16_t> (sW.size ());
uint16_t uAntenna = static_cast<uint16_t> (uW.size ());
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;
uint8_t numCluster = static_cast<uint8_t> (params->m_channel[0][0].size ());
for (uint8_t cIndex = 0; cIndex < numCluster; cIndex++)
{
std::complex<double> txSum (0,0);
for (uint16_t sIndex = 0; sIndex < sAntenna; sIndex++)
{
std::complex<double> rxSum (0,0);
for (uint16_t uIndex = 0; uIndex < uAntenna; uIndex++)
{
rxSum = rxSum + uW[uIndex] * params->m_channel[uIndex][sIndex][cIndex];
}
txSum = txSum + sW[sIndex] * rxSum;
}
longTerm.push_back (txSum);
}
return longTerm;
}
Ptr<SpectrumValue>
ThreeGppSpectrumPropagationLossModel::CalcBeamformingGain (Ptr<SpectrumValue> txPsd,
ThreeGppAntennaArrayModel::ComplexVector longTerm,
Ptr<const ThreeGppChannelModel::ThreeGppChannelMatrix> params,
const ns3::Vector &sSpeed, const ns3::Vector &uSpeed) const
{
NS_LOG_FUNCTION (this);
Ptr<SpectrumValue> tempPsd = Copy<SpectrumValue> (txPsd);
//channel[rx][tx][cluster]
uint8_t numCluster = static_cast<uint8_t> (params->m_channel[0][0].size ());
// compute the doppler term
// 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;
for (uint8_t cIndex = 0; cIndex < numCluster; cIndex++)
{
//cluster angle angle[direction][n],where, direction = 0(aoa), 1(zoa).
// TODO should I include the "alfa" term for the Doppler of delayed paths?
double temp_doppler = 2 * M_PI * ((sin (params->m_angle[ThreeGppChannelModel::ZOA_INDEX][cIndex] * M_PI / 180) * cos (params->m_angle[ThreeGppChannelModel::AOA_INDEX][cIndex] * M_PI / 180) * uSpeed.x
+ sin (params->m_angle[ThreeGppChannelModel::ZOA_INDEX][cIndex] * M_PI / 180) * sin (params->m_angle[ThreeGppChannelModel::AOA_INDEX][cIndex] * M_PI / 180) * uSpeed.y
+ cos (params->m_angle[ThreeGppChannelModel::ZOA_INDEX][cIndex] * M_PI / 180) * uSpeed.z)
+ (sin (params->m_angle[ThreeGppChannelModel::ZOD_INDEX][cIndex] * M_PI / 180) * cos (params->m_angle[ThreeGppChannelModel::AOD_INDEX][cIndex] * M_PI / 180) * sSpeed.x
+ sin (params->m_angle[ThreeGppChannelModel::ZOD_INDEX][cIndex] * M_PI / 180) * sin (params->m_angle[ThreeGppChannelModel::AOD_INDEX][cIndex] * M_PI / 180) * sSpeed.y
+ cos (params->m_angle[ThreeGppChannelModel::ZOD_INDEX][cIndex] * M_PI / 180) * sSpeed.z))
* slotTime * GetFrequency () / 3e8;
doppler.push_back (exp (std::complex<double> (0, temp_doppler)));
}
// apply the doppler term and the propagation delay to the long term component
// to obtain the beamforming gain
auto vit = tempPsd->ValuesBegin (); // psd iterator
auto sbit = tempPsd->ConstBandsBegin(); // band iterator
while (vit != tempPsd->ValuesEnd ())
{
std::complex<double> subsbandGain (0.0,0.0);
if ((*vit) != 0.00)
{
double fsb = (*sbit).fc; // center frequency of the sub-band
for (uint8_t cIndex = 0; cIndex < numCluster; cIndex++)
{
double delay = -2 * M_PI * fsb * (params->m_delay[cIndex]);
subsbandGain = subsbandGain + longTerm[cIndex] * doppler[cIndex] * exp (std::complex<double> (0, delay));
}
*vit = (*vit) * (norm (subsbandGain));
}
vit++;
sbit++;
}
return tempPsd;
}
ThreeGppAntennaArrayModel::ComplexVector
ThreeGppSpectrumPropagationLossModel::GetLongTerm (uint32_t aId, uint32_t bId,
Ptr<const ThreeGppChannelModel::ThreeGppChannelMatrix> channelMatrix,
const ThreeGppAntennaArrayModel::ComplexVector &aW,
const ThreeGppAntennaArrayModel::ComplexVector &bW) const
{
ThreeGppAntennaArrayModel::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;
if (!channelMatrix->IsReverse (aId, bId))
{
sW = aW;
uW = bW;
}
else
{
sW = bW;
uW = aW;
}
// compute the long term key, the key is unique for each tx-rx pair
uint32_t x1 = std::min (aId, bId);
uint32_t x2 = std::max (aId, bId);
uint32_t longTermId = ThreeGppChannelModel::GetKey (x1, x2);
bool update = false; // indicates whether the long term has to be updated
bool notFound = false; // indicates if the long term has not been computed yet
// look for the long term in the map and check if it is valid
if (m_longTermMap.find (longTermId) != m_longTermMap.end ())
{
NS_LOG_DEBUG ("found the long term component in the map");
longTerm = m_longTermMap[longTermId]->m_longTerm;
// check if the channel matrix has been updated
// or the s beam has been changed
// or the u beam has been changed
update = (m_longTermMap[longTermId]->m_channel->m_generatedTime != channelMatrix->m_generatedTime
|| m_longTermMap[longTermId]->m_sW != sW
|| m_longTermMap[longTermId]->m_uW != uW);
}
else
{
NS_LOG_DEBUG ("long term component NOT found");
notFound = true;
}
if (update || notFound)
{
NS_LOG_DEBUG ("compute the long term");
// compute the long term component
longTerm = CalcLongTerm (channelMatrix, sW, uW);
// store the long term
Ptr<LongTerm> longTermItem = Create<LongTerm> ();
longTermItem->m_longTerm = longTerm;
longTermItem->m_channel = channelMatrix;
longTermItem->m_sW = sW;
longTermItem->m_uW = uW;
m_longTermMap[longTermId] = longTermItem;
}
return longTerm;
}
Ptr<SpectrumValue>
ThreeGppSpectrumPropagationLossModel::DoCalcRxPowerSpectralDensity (Ptr<const SpectrumValue> txPsd,
Ptr<const MobilityModel> a,
Ptr<const MobilityModel> b) const
{
NS_LOG_FUNCTION (this);
uint32_t aId = a->GetObject<Node> ()->GetId (); // id of the node a
uint32_t bId = b->GetObject<Node> ()->GetId (); // id of the node b
NS_ASSERT (aId != bId);
NS_ASSERT_MSG (a->GetDistanceFrom (b) > 0.0, "The position of a and b devices cannot be the same");
Ptr<SpectrumValue> rxPsd = Copy<SpectrumValue> (txPsd);
// 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);
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);
NS_LOG_DEBUG ("b node " << bId << " antenna " << bAntenna);
if (aAntenna->IsOmniTx () || bAntenna->IsOmniTx () )
{
NS_LOG_LOGIC ("Omni transmission, do nothing.");
return rxPsd;
}
Ptr<const ThreeGppChannelModel::ThreeGppChannelMatrix> channelMatrix = m_channelModel->GetChannel (a, b, aAntenna, bAntenna);
// get the precoding and combining vectors
ThreeGppAntennaArrayModel::ComplexVector aW = aAntenna->GetBeamformingVector ();
ThreeGppAntennaArrayModel::ComplexVector bW = bAntenna->GetBeamformingVector ();
// retrieve the long term component
ThreeGppAntennaArrayModel::ComplexVector longTerm = GetLongTerm (aId, bId, channelMatrix, aW, bW);
// apply the beamforming gain
rxPsd = CalcBeamformingGain (rxPsd, longTerm, channelMatrix, a->GetVelocity (), b->GetVelocity ());
return rxPsd;
}
} // namespace ns3

View File

@@ -0,0 +1,181 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2015, NYU WIRELESS, Tandon School of Engineering,
* New York University
* 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_SPECTRUM_PROPAGATION_LOSS_H
#define THREE_GPP_SPECTRUM_PROPAGATION_LOSS_H
#include "ns3/spectrum-propagation-loss-model.h"
#include <complex.h>
#include <map>
#include "ns3/three-gpp-channel-model.h"
namespace ns3 {
class NetDevice;
class ChannelConditionModel;
class ChannelCondition;
/**
* \ingroup spectrum
* \brief 3GPP Spectrum Propagation Loss Model
*
* This class models the frequency dependent propagation phenomena in the way
* described by 3GPP TR 38.901 document. The main method is DoCalcRxPowerSpectralDensity,
* which takes as input the power spectral density (PSD) of the transmitted signal,
* the mobility models of the transmitting node and receiving node, and
* returns the PSD of the received signal.
*
* \see ThreeGppChannelModel
* \see ThreeGppAntennaArrayModel
* \see ChannelCondition
*/
class ThreeGppSpectrumPropagationLossModel : public SpectrumPropagationLossModel
{
public:
/**
* Constructor
*/
ThreeGppSpectrumPropagationLossModel ();
/**
* Destructor
*/
~ThreeGppSpectrumPropagationLossModel ();
/**
* Get the type ID.
* \return the object TypeId
*/
static TypeId GetTypeId ();
/**
* Add a device-antenna pair
* \param n a pointer to the NetDevice
* \param a a pointer to the associated ThreeGppAntennaArrayModel
*/
void AddDevice (Ptr<NetDevice> n, Ptr<const ThreeGppAntennaArrayModel> a);
/**
* Sets the value of an attribute belonging to the associated
* ThreeGppChannelModel instance
* \param name name of the attribute
* \param value the attribute value
*/
void SetChannelModelAttribute (const std::string &name, const AttributeValue &value);
/**
* Returns the value of an attribute belonging to the associated
* ThreeGppChannelModel instance
* \param name name of the attribute
* \param where the result should be stored
*/
void GetChannelModelAttribute (const std::string &name, AttributeValue &value) const;
/**
* \brief Computes the received PSD.
*
* This function computes the received PSD by applying the 3GPP fast fading
* model and the beamforming gain.
* In particular, it retrieves the matrix representing the channel between
* node a and node b, computes the corresponding long term component, i.e.,
* the product between the cluster matrices and the TX and RX beamforming
* vectors (w_rx^T H^n_ab w_tx), and accounts for the Doppler component and
* the propagation delay.
* To reduce the computational load, the long term component associated with
* a certain channel is cached and recomputed only when the channel realization
* is updated, or when the beamforming vectors change.
*
* \param txPsd tx PSD
* \param a first node mobility model
* \param b second node mobility model
*
* \return the received PSD
*/
virtual Ptr<SpectrumValue> DoCalcRxPowerSpectralDensity (Ptr<const SpectrumValue> txPsd,
Ptr<const MobilityModel> a,
Ptr<const MobilityModel> b) const;
private:
/**
* Data structure that stores the long term component for a tx-rx pair
*/
struct LongTerm : public SimpleRefCount<LongTerm>
{
ThreeGppAntennaArrayModel::ComplexVector m_longTerm; //!< vector containing the long term component for each cluster
Ptr<const ThreeGppChannelModel::ThreeGppChannelMatrix> 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
};
/**
* Get the operating frequency
* \return the operating frequency in Hz
*/
double GetFrequency () const;
/**
* Looks for the long term component in m_longTermMap. If found, checks
* whether it has to be updated. If not found or if it has to be updated,
* calls the method CalcLongTerm to compute it.
* \param aId id of the first node
* \param bId id of the second node
* \param channelMatrix the channel matrix
* \param aW the beamforming vector of the first device
* \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,
Ptr<const ThreeGppChannelModel::ThreeGppChannelMatrix> channelMatrix,
const ThreeGppAntennaArrayModel::ComplexVector &aW,
const ThreeGppAntennaArrayModel::ComplexVector &bW) const;
/**
* Computes the long term component
* \param channelMatrix the channel matrix H
* \param sW the beamforming vector of the s device
* \param uW the beamforming vector of the u device
* \return the long term component
*/
ThreeGppAntennaArrayModel::ComplexVector CalcLongTerm (Ptr<const ThreeGppChannelModel::ThreeGppChannelMatrix> channelMatrix,
const ThreeGppAntennaArrayModel::ComplexVector &sW,
const ThreeGppAntennaArrayModel::ComplexVector &uW) const;
/**
* Computes the beamforming gain and applies it to the tx PSD
* \param txPsd the tx PSD
* \param longTerm the long term component
* \param params The channel matrix
* \param sSpeed speed of the first node
* \param uSpeed speed of the second node
* \return the rx PSD
*/
Ptr<SpectrumValue> CalcBeamformingGain (Ptr<SpectrumValue> txPsd,
ThreeGppAntennaArrayModel::ComplexVector longTerm,
Ptr<const ThreeGppChannelModel::ThreeGppChannelMatrix> params,
const Vector &sSpeed, const Vector &uSpeed) const;
std::unordered_map <uint32_t, Ptr<const ThreeGppAntennaArrayModel> > 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<ThreeGppChannelModel> m_channelModel; //!< the model to generate the channel matrix
};
} // namespace ns3
#endif /* THREE_GPP_SPECTRUM_PROPAGATION_LOSS_H */

View File

@@ -0,0 +1,577 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2019 SIGNET Lab, Department of Information Engineering,
*
* 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/abort.h"
#include "ns3/test.h"
#include "ns3/config.h"
#include "ns3/double.h"
#include "ns3/uinteger.h"
#include "ns3/string.h"
#include "ns3/angles.h"
#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/three-gpp-channel-model.h"
#include "ns3/simple-net-device.h"
#include "ns3/simulator.h"
#include "ns3/channel-condition-model.h"
#include "ns3/three-gpp-spectrum-propagation-loss-model.h"
#include "ns3/wifi-spectrum-value-helper.h"
#include "ns3/buildings-channel-condition-model.h"
using namespace ns3;
NS_LOG_COMPONENT_DEFINE ("ThreeGppChannelTestSuite");
/**
* Test case for the ThreeGppChannelModel class.
* 1) check if the channel matrix has the correct dimensions
* 2) check if the channel matrix is correctly normalized
*/
class ThreeGppChannelMatrixComputationTest : public TestCase
{
public:
/**
* Constructor
*/
ThreeGppChannelMatrixComputationTest ();
/**
* Destructor
*/
virtual ~ThreeGppChannelMatrixComputationTest ();
private:
/**
* Build the test scenario
*/
virtual void DoRun (void);
/**
* Compute the Frobenius norm of the channel matrix and stores it in m_normVector
* \param channelModel the ThreeGppChannelModel object used to generate the channel matrix
* \param txMob the mobility model of the first node
* \param rxMob the mobility model of the second node
* \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);
std::vector<double> m_normVector; //!< each element is the norm of a channel realization
};
ThreeGppChannelMatrixComputationTest::ThreeGppChannelMatrixComputationTest ()
: TestCase ("Check the dimensions and the norm of the channel matrix")
{
}
ThreeGppChannelMatrixComputationTest::~ThreeGppChannelMatrixComputationTest ()
{
}
void
ThreeGppChannelMatrixComputationTest::DoComputeNorm (Ptr<ThreeGppChannelModel> channelModel, Ptr<MobilityModel> txMob, Ptr<MobilityModel> rxMob, Ptr<ThreeGppAntennaArrayModel> txAntenna, Ptr<ThreeGppAntennaArrayModel> rxAntenna)
{
uint64_t txAntennaElements = txAntenna->GetNumberOfElements ();
uint64_t rxAntennaElements = rxAntenna->GetNumberOfElements ();
Ptr<const ThreeGppChannelModel::ThreeGppChannelMatrix> channelMatrix = channelModel->GetChannel (txMob, rxMob, txAntenna, rxAntenna);
double channelNorm = 0;
uint8_t numTotClusters = channelMatrix->m_channel.at (0).at (0).size ();
for (uint8_t cIndex = 0; cIndex < numTotClusters; cIndex++)
{
double clusterNorm = 0;
for (uint64_t sIndex = 0; sIndex < txAntennaElements; sIndex++)
{
for (uint32_t uIndex = 0; uIndex < rxAntennaElements; uIndex++)
{
clusterNorm += std::pow (std::abs (channelMatrix->m_channel.at (uIndex).at (sIndex).at (cIndex)), 2);
}
}
channelNorm += clusterNorm;
}
m_normVector.push_back (channelNorm);
}
void
ThreeGppChannelMatrixComputationTest::DoRun (void)
{
// Build the scenario for the test
uint8_t txAntennaElements[] {2, 2}; // tx antenna dimensions
uint8_t rxAntennaElements[] {2, 2}; // rx antenna dimensions
uint32_t updatePeriodMs = 100; // update period in ms
// create the channel condition model
Ptr<ChannelConditionModel> channelConditionModel = CreateObject<NeverLosChannelConditionModel> ();
// create the ThreeGppChannelModel object used to generate the channel matrix
Ptr<ThreeGppChannelModel> channelModel = CreateObject<ThreeGppChannelModel> ();
channelModel->SetAttribute ("Frequency", DoubleValue (60.0e9));
channelModel->SetAttribute ("Scenario", StringValue ("RMa"));
channelModel->SetAttribute ("ChannelConditionModel", PointerValue (channelConditionModel));
channelModel->SetAttribute ("UpdatePeriod", TimeValue (MilliSeconds (updatePeriodMs-1)));
// create the tx and rx nodes
NodeContainer nodes;
nodes.Create (2);
// create the tx and rx devices
Ptr<SimpleNetDevice> txDev = CreateObject<SimpleNetDevice> ();
Ptr<SimpleNetDevice> rxDev = CreateObject<SimpleNetDevice> ();
// associate the nodes and the devices
nodes.Get (0)->AddDevice (txDev);
txDev->SetNode (nodes.Get (0));
nodes.Get (1)->AddDevice (rxDev);
rxDev->SetNode (nodes.Get (1));
// create the tx and rx mobility models and set their positions
Ptr<MobilityModel> txMob = CreateObject<ConstantPositionMobilityModel> ();
txMob->SetPosition (Vector (0.0,0.0,10.0));
Ptr<MobilityModel> rxMob = CreateObject<ConstantPositionMobilityModel> ();
rxMob->SetPosition (Vector (100.0,0.0,10.0));
// associate the nodes and the mobility models
nodes.Get (0)->AggregateObject (txMob);
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));
// generate the channel matrix
Ptr<const ThreeGppChannelModel::ThreeGppChannelMatrix> channelMatrix = channelModel->GetChannel (txMob, rxMob, txAntenna, rxAntenna);
// check the channel matrix dimensions
NS_TEST_ASSERT_MSG_EQ (channelMatrix->m_channel.at (0).size (), txAntennaElements [0] * txAntennaElements [1], "The second dimension of H should be equal to the number of tx antenna elements");
NS_TEST_ASSERT_MSG_EQ (channelMatrix->m_channel.size (), rxAntennaElements [0] * rxAntennaElements [1], "The first dimension of H should be equal to the number of rx antenna elements");
// test if the channel matrix is correctly generated
uint16_t numIt = 1000;
for (uint16_t i = 0; i < numIt; i++)
{
Simulator::Schedule (MilliSeconds (updatePeriodMs * i), &ThreeGppChannelMatrixComputationTest::DoComputeNorm, this, channelModel, txMob, rxMob, txAntenna, rxAntenna);
}
Simulator::Run ();
// compute the sample mean
double sampleMean = 0;
for (auto i : m_normVector)
{
sampleMean += i;
}
sampleMean /= numIt;
// compute the sample standard deviation
double sampleStd = 0;
for (auto i : m_normVector)
{
sampleStd += ((i - sampleMean) * (i - sampleMean));
}
sampleStd = std::sqrt (sampleStd / (numIt - 1));
// perform the one sample t-test with a significance level of 0.05 to test
// the hypothesis "E [|H|^2] = M*N, where |H| indicates the Frobenius norm of
// H, M is the number of transmit antenna elements, and N is the number of
// the receive antenna elements"
double t = (sampleMean - txAntennaElements [0] * txAntennaElements [1] * rxAntennaElements [0] * rxAntennaElements [1]) / (sampleMean / std::sqrt (numIt));
// Using a significance level of 0.05, we reject the null hypothesis if |t| is
// greater than the critical value from a t-distribution with df = numIt-1
NS_TEST_ASSERT_MSG_EQ_TOL (std::abs (t), 0, 1.65, "We reject the hypothesis E[|H|^2] = M*N with a significance level of 0.05");
Simulator::Destroy ();
}
/**
* Test case for the ThreeGppChannelModel class.
* It checks if the channel realizations are correctly updated during the
* simulation.
*/
class ThreeGppChannelMatrixUpdateTest : public TestCase
{
public:
/**
* Constructor
*/
ThreeGppChannelMatrixUpdateTest ();
/**
* Destructor
*/
virtual ~ThreeGppChannelMatrixUpdateTest ();
private:
/**
* Build the test scenario
*/
virtual void DoRun (void);
/**
* This method is used to schedule the channel matrix computation at different
* time instants and to check if it correctly updated
* \param channelModel the ThreeGppChannelModel object used to generate the channel matrix
* \param txMob the mobility model of the first node
* \param rxMob the mobility model of the second node
* \param txAntenna the antenna object associated to the first node
* \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);
Ptr<const ThreeGppChannelModel::ThreeGppChannelMatrix> m_currentChannel; //!< used by DoGetChannel to store the current channel matrix
};
ThreeGppChannelMatrixUpdateTest::ThreeGppChannelMatrixUpdateTest ()
: TestCase ("Check if the channel realizations are correctly updated during the simulation")
{
}
ThreeGppChannelMatrixUpdateTest::~ThreeGppChannelMatrixUpdateTest ()
{
}
void
ThreeGppChannelMatrixUpdateTest::DoGetChannel (Ptr<ThreeGppChannelModel> channelModel, Ptr<MobilityModel> txMob, Ptr<MobilityModel> rxMob, Ptr<ThreeGppAntennaArrayModel> txAntenna, Ptr<ThreeGppAntennaArrayModel> rxAntenna, bool update)
{
// retrieve the channel matrix
Ptr<const ThreeGppChannelModel::ThreeGppChannelMatrix> channelMatrix = channelModel->GetChannel (txMob, rxMob, txAntenna, rxAntenna);
if (m_currentChannel == 0)
{
// this is the first time we compute the channel matrix, we initialize
// m_currentChannel
m_currentChannel = channelMatrix;
}
else
{
// compare the old and the new channel matrices
NS_TEST_ASSERT_MSG_EQ ((m_currentChannel != channelMatrix), update, Simulator::Now ().GetMilliSeconds () << " The channel matrix is not correctly updated");
}
}
void
ThreeGppChannelMatrixUpdateTest::DoRun (void)
{
// Build the scenario for the test
uint8_t txAntennaElements[] {2, 2}; // tx antenna dimensions
uint8_t rxAntennaElements[] {4, 4}; // rx antenna dimensions
uint32_t updatePeriodMs = 100; // update period in ms
// create the channel condition model
Ptr<ChannelConditionModel> channelConditionModel = CreateObject<AlwaysLosChannelConditionModel> ();
// create the ThreeGppChannelModel object used to generate the channel matrix
Ptr<ThreeGppChannelModel> channelModel = CreateObject<ThreeGppChannelModel> ();
channelModel->SetAttribute ("Frequency", DoubleValue (60.0e9));
channelModel->SetAttribute ("Scenario", StringValue ("UMa"));
channelModel->SetAttribute ("ChannelConditionModel", PointerValue (channelConditionModel));
channelModel->SetAttribute ("UpdatePeriod", TimeValue (MilliSeconds (updatePeriodMs)));
// create the tx and rx nodes
NodeContainer nodes;
nodes.Create (2);
// create the tx and rx devices
Ptr<SimpleNetDevice> txDev = CreateObject<SimpleNetDevice> ();
Ptr<SimpleNetDevice> rxDev = CreateObject<SimpleNetDevice> ();
// associate the nodes and the devices
nodes.Get (0)->AddDevice (txDev);
txDev->SetNode (nodes.Get (0));
nodes.Get (1)->AddDevice (rxDev);
rxDev->SetNode (nodes.Get (1));
// create the tx and rx mobility models and set their positions
Ptr<MobilityModel> txMob = CreateObject<ConstantPositionMobilityModel> ();
txMob->SetPosition (Vector (0.0,0.0,10.0));
Ptr<MobilityModel> rxMob = CreateObject<ConstantPositionMobilityModel> ();
rxMob->SetPosition (Vector (100.0,0.0,1.6));
// associate the nodes and the mobility models
nodes.Get (0)->AggregateObject (txMob);
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));
// 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);
// 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);
// 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::Run ();
Simulator::Destroy ();
}
/**
* Test case for the ThreeGppSpectrumPropagationLossModelTest class.
* 1) checks if the long term components for the direct and the reverse link
* are the same
* 2) checks if the long term component is updated when changing the beamforming
* vectors
* 3) checks if the long term is updated when changing the channel matrix
*/
class ThreeGppSpectrumPropagationLossModelTest : public TestCase
{
public:
/**
* Constructor
*/
ThreeGppSpectrumPropagationLossModelTest ();
/**
* Destructor
*/
virtual ~ThreeGppSpectrumPropagationLossModelTest ();
private:
/**
* Build the test scenario
*/
virtual void DoRun (void);
/**
* Points the beam of thisDevice towards otherDevice
* \param thisDevice the device to configure
* \param thisAntenna the antenna object associated to thisDevice
* \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);
/**
* Test of the long term component is correctly updated when the channel
* matrix is recomputed
* \param lossModel the ThreeGppSpectrumPropagationLossModel object used to
* compute the rx PSD
* \param txPsd the PSD of the transmitted signal
* \param txMob the mobility model of the tx device
* \param rxMob the mobility model of the rx device
* \param rxPsdOld the previously received PSD
*/
void CheckLongTermUpdate (Ptr<ThreeGppSpectrumPropagationLossModel> lossModel, Ptr<SpectrumValue> txPsd, Ptr<MobilityModel> txMob, Ptr<MobilityModel> rxMob, Ptr<SpectrumValue> rxPsdOld);
/**
* Checks if two PSDs are equal
* \param first the first PSD
* \param second the second PSD
* \return true if first and second are equal, false otherwise
*/
static bool ArePsdEqual (Ptr<SpectrumValue> first, Ptr<SpectrumValue> second);
};
ThreeGppSpectrumPropagationLossModelTest::ThreeGppSpectrumPropagationLossModelTest ()
: TestCase ("Test case for the ThreeGppSpectrumPropagationLossModel class")
{
}
ThreeGppSpectrumPropagationLossModelTest::~ThreeGppSpectrumPropagationLossModelTest ()
{
}
void
ThreeGppSpectrumPropagationLossModelTest::DoBeamforming (Ptr<NetDevice> thisDevice, Ptr<ThreeGppAntennaArrayModel> thisAntenna, Ptr<NetDevice> otherDevice, Ptr<ThreeGppAntennaArrayModel> otherAntenna)
{
uint8_t noPlane = 1;
ThreeGppAntennaArrayModel::ComplexVector antennaWeights;
Vector aPos = thisDevice->GetNode ()->GetObject<MobilityModel> ()->GetPosition ();
Vector bPos = otherDevice->GetNode ()->GetObject<MobilityModel> ()->GetPosition ();
Angles completeAngle (bPos,aPos);
double posX = bPos.x - aPos.x;
double phiAngle = atan ((bPos.y - aPos.y) / posX);
if (posX < 0)
{
phiAngle = phiAngle + M_PI;
}
if (phiAngle < 0)
{
phiAngle = phiAngle + 2 * M_PI;
}
double hAngleRadian = fmod ((phiAngle + (M_PI / noPlane)),2 * M_PI / noPlane) - (M_PI / noPlane);
double vAngleRadian = completeAngle.theta;
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);
}
thisAntenna->SetBeamformingVector (antennaWeights);
}
bool
ThreeGppSpectrumPropagationLossModelTest::ArePsdEqual (Ptr<SpectrumValue> first, Ptr<SpectrumValue> second)
{
bool ret = true;
for (uint8_t i = 0; i < first->GetSpectrumModel ()->GetNumBands (); i++)
{
if ((*first) [i] != (*second) [i])
{
ret = false;
continue;
}
}
return ret;
}
void
ThreeGppSpectrumPropagationLossModelTest::CheckLongTermUpdate (Ptr<ThreeGppSpectrumPropagationLossModel> lossModel, Ptr<SpectrumValue> txPsd, Ptr<MobilityModel> txMob, Ptr<MobilityModel> rxMob, Ptr<SpectrumValue> rxPsdOld)
{
Ptr<SpectrumValue> rxPsdNew = lossModel->DoCalcRxPowerSpectralDensity (txPsd, txMob, rxMob);
NS_TEST_ASSERT_MSG_EQ (ArePsdEqual (rxPsdOld, rxPsdNew), false, "The long term is not updated when the channel matrix is recomputed");
}
void
ThreeGppSpectrumPropagationLossModelTest::DoRun ()
{
// Build the scenario for the test
Config::SetDefault ("ns3::ThreeGppChannelModel::UpdatePeriod", TimeValue (MilliSeconds (100)));
uint8_t txAntennaElements[] {4, 4}; // tx antenna dimensions
uint8_t rxAntennaElements[] {4, 4}; // rx antenna dimensions
// create the ChannelConditionModel object to be used to retrieve the
// channel condition
Ptr<ChannelConditionModel> condModel = CreateObject<AlwaysLosChannelConditionModel> ();
// create the ThreeGppSpectrumPropagationLossModel object, set frequency,
// scenario and channel condition model to be used
Ptr<ThreeGppSpectrumPropagationLossModel> lossModel = CreateObject<ThreeGppSpectrumPropagationLossModel> ();
lossModel->SetChannelModelAttribute ("Frequency", DoubleValue(2.4e9));
lossModel->SetChannelModelAttribute ("Scenario", StringValue("UMa"));
lossModel->SetChannelModelAttribute ("ChannelConditionModel", PointerValue (condModel)); // create the ThreeGppChannelModel object used to generate the channel matrix
// create the tx and rx nodes
NodeContainer nodes;
nodes.Create (2);
// create the tx and rx devices
Ptr<SimpleNetDevice> txDev = CreateObject<SimpleNetDevice> ();
Ptr<SimpleNetDevice> rxDev = CreateObject<SimpleNetDevice> ();
// associate the nodes and the devices
nodes.Get (0)->AddDevice (txDev);
txDev->SetNode (nodes.Get (0));
nodes.Get (1)->AddDevice (rxDev);
rxDev->SetNode (nodes.Get (1));
// create the tx and rx mobility models and set their positions
Ptr<MobilityModel> txMob = CreateObject<ConstantPositionMobilityModel> ();
txMob->SetPosition (Vector (0.0,0.0,10.0));
Ptr<MobilityModel> rxMob = CreateObject<ConstantPositionMobilityModel> ();
rxMob->SetPosition (Vector (15.0,0.0,10.0)); // in this position the channel condition is always LOS
// associate the nodes and the mobility models
nodes.Get (0)->AggregateObject (txMob);
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]));
// initialize ThreeGppSpectrumPropagationLossModel
lossModel->AddDevice (txDev, txAntenna);
lossModel->AddDevice (rxDev, rxAntenna);
// set the beamforming vectors
DoBeamforming (txDev, txAntenna, rxDev, rxAntenna);
DoBeamforming (rxDev, rxAntenna, txDev, txAntenna);
// create the tx psd
WifiSpectrumValue5MhzFactory sf;
double txPower = 0.1; // Watts
uint32_t channelNumber = 1;
Ptr<SpectrumValue> txPsd = sf.CreateTxPowerSpectralDensity (txPower, channelNumber);
// compute the rx psd
Ptr<SpectrumValue> rxPsdOld = lossModel->DoCalcRxPowerSpectralDensity (txPsd, txMob, rxMob);
// 1) check that the rx PSD is equal for both the direct and the reverse channel
Ptr<SpectrumValue> rxPsdNew = lossModel->DoCalcRxPowerSpectralDensity (txPsd, rxMob, txMob);
NS_TEST_ASSERT_MSG_EQ (ArePsdEqual (rxPsdOld, rxPsdNew), true, "The long term for the direct and the reverse channel are different");
// 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 ();
txBfVector [0] = std::complex<double> (0.0, 0.0);
txAntenna->SetBeamformingVector (txBfVector);
rxPsdNew = lossModel->DoCalcRxPowerSpectralDensity (txPsd, rxMob, txMob);
NS_TEST_ASSERT_MSG_EQ (ArePsdEqual (rxPsdOld, rxPsdNew), false, "Changing the BF vectors the rx PSD does not change");
// update rxPsdOld
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::Run ();
Simulator::Destroy ();
}
/**
* \ingroup spectrum
*
* Test suite for the ThreeGppChannelModel class
*/
class ThreeGppChannelTestSuite : public TestSuite
{
public:
/**
* Constructor
*/
ThreeGppChannelTestSuite ();
};
ThreeGppChannelTestSuite::ThreeGppChannelTestSuite ()
: TestSuite ("three-gpp-channel", UNIT)
{
AddTestCase (new ThreeGppChannelMatrixComputationTest, TestCase::QUICK);
AddTestCase (new ThreeGppChannelMatrixUpdateTest, TestCase::QUICK);
AddTestCase (new ThreeGppSpectrumPropagationLossModelTest, TestCase::QUICK);
}
static ThreeGppChannelTestSuite myTestSuite;

View File

@@ -12,13 +12,13 @@ def build(bld):
'model/friis-spectrum-propagation-loss.cc',
'model/constant-spectrum-propagation-loss.cc',
'model/spectrum-phy.cc',
'model/spectrum-channel.cc',
'model/spectrum-channel.cc',
'model/single-model-spectrum-channel.cc',
'model/multi-model-spectrum-channel.cc',
'model/spectrum-interference.cc',
'model/spectrum-error-model.cc',
'model/spectrum-model-ism2400MHz-res1MHz.cc',
'model/spectrum-model-300kHz-300GHz-log.cc',
'model/spectrum-model-300kHz-300GHz-log.cc',
'model/wifi-spectrum-value-helper.cc',
'model/waveform-generator.cc',
'model/spectrum-analyzer.cc',
@@ -30,6 +30,8 @@ def build(bld):
'model/microwave-oven-spectrum-value-helper.cc',
'model/tv-spectrum-transmitter.cc',
'model/trace-fading-loss-model.cc',
'model/three-gpp-spectrum-propagation-loss-model.cc',
'model/three-gpp-channel-model.cc',
'helper/spectrum-helper.cc',
'helper/adhoc-aloha-noack-ideal-phy-helper.cc',
'helper/waveform-generator-helper.cc',
@@ -45,8 +47,9 @@ def build(bld):
'test/spectrum-waveform-generator-test.cc',
'test/tv-helper-distribution-test.cc',
'test/tv-spectrum-transmitter-test.cc',
'test/three-gpp-channel-test-suite.cc',
]
headers = bld(features='ns3header')
headers.module = 'spectrum'
headers.source = [
@@ -59,14 +62,14 @@ def build(bld):
'model/constant-spectrum-propagation-loss.h',
'model/spectrum-phy.h',
'model/spectrum-channel.h',
'model/single-model-spectrum-channel.h',
'model/single-model-spectrum-channel.h',
'model/multi-model-spectrum-channel.h',
'model/spectrum-interference.h',
'model/spectrum-error-model.h',
'model/spectrum-model-ism2400MHz-res1MHz.h',
'model/spectrum-model-300kHz-300GHz-log.h',
'model/wifi-spectrum-value-helper.h',
'model/waveform-generator.h',
'model/waveform-generator.h',
'model/spectrum-analyzer.h',
'model/aloha-noack-mac-header.h',
'model/aloha-noack-net-device.h',
@@ -76,6 +79,8 @@ def build(bld):
'model/microwave-oven-spectrum-value-helper.h',
'model/tv-spectrum-transmitter.h',
'model/trace-fading-loss-model.h',
'model/three-gpp-spectrum-propagation-loss-model.h',
'model/three-gpp-channel-model.h',
'helper/spectrum-helper.h',
'helper/adhoc-aloha-noack-ideal-phy-helper.h',
'helper/waveform-generator-helper.h',