A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
three-gpp-channel-example.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2019 SIGNET Lab, Department of Information Engineering,
3 * University of Padova
4 *
5 * SPDX-License-Identifier: GPL-2.0-only
6 */
7
8/**
9 * @file
10 * This example shows how to configure the 3GPP channel model classes to
11 * compute the SNR between two nodes.
12 * The simulation involves two static nodes which are placed at a certain
13 * distance from each other and communicate through a wireless channel at
14 * 2 GHz with a bandwidth of 18 MHz. The default propagation environment is
15 * 3D-urban macro (UMa), and it can be configured changing the value of the
16 * string "scenario".
17 * Each node hosts has an antenna array with 4 antenna elements.
18 *
19 * The example writes its main results to the file ``snr-trace.txt``.
20 * Each row contains: (i) the simulation time (s), (ii) the SNR obtained with
21 * steering vectors (``DoBeamforming``), (iii) the SNR obtained with matched-filter
22 * combining, and (iv) the propagation gain.
23 */
24
25#include "ns3/channel-condition-model.h"
26#include "ns3/constant-position-mobility-model.h"
27#include "ns3/constant-velocity-mobility-model.h"
28#include "ns3/core-module.h"
29#include "ns3/lte-spectrum-value-helper.h"
30#include "ns3/mobility-model.h"
31#include "ns3/node-container.h"
32#include "ns3/node.h"
33#include "ns3/spectrum-signal-parameters.h"
34#include "ns3/three-gpp-channel-model.h"
35#include "ns3/three-gpp-propagation-loss-model.h"
36#include "ns3/three-gpp-spectrum-propagation-loss-model.h"
37#include "ns3/uniform-planar-array.h"
38
39#include <fstream>
40
41NS_LOG_COMPONENT_DEFINE("ThreeGppChannelExample");
42
43using namespace ns3;
44
46 m_propagationLossModel; //!< the PropagationLossModel object
48 m_spectrumLossModel; //!< the SpectrumPropagationLossModel object
49
50/**
51 * @brief A structure that holds the parameters for the
52 * ComputeSnr function. In this way the problem with the limited
53 * number of parameters of method Schedule is avoided.
54 */
56{
57 Ptr<MobilityModel> txMob; //!< the tx mobility model
58 Ptr<MobilityModel> rxMob; //!< the rx mobility model
59 double txPow; //!< the tx power in dBm
60 double noiseFigure; //!< the noise figure in dB
61 Ptr<PhasedArrayModel> txAntenna; //!< the tx antenna array
62 Ptr<PhasedArrayModel> rxAntenna; //!< the rx antenna array
63};
64
65/**
66 * Perform a simple (DFT/steering) beamforming toward the other node.
67 *
68 * This function builds a per-element complex weight vector based on the geometric
69 * direction between two nodes and the antenna element locations returned by
70 * `PhasedArrayModel::GetElementLocation()`. The weights are unit-norm (equal
71 * power across elements) and have the form:
72 *
73 * w[i] = exp(j * phase_i) / sqrt(N),
74 * phase_i = sign * 2*pi * ( u · r_i )
75 *
76 * where `u` is the unit direction corresponding to the azimuth/inclination
77 * angles of the line-of-sight vector between the nodes, `r_i` is the location of
78 * element `i` in the array coordinate system, and `N` is the number of antenna
79 * elements.
80 *
81 * Sign convention and how to use this function:
82 *
83 * The 3GPP channel coefficients are constructed using spatial phase terms of the
84 * form exp(+j*2*pi*(u·r)) (see `ThreeGppChannelModel::GetNewChannel()`), i.e., the
85 * array *response/steering vector* toward a direction is proportional to:
86 *
87 * a(theta)[i] = exp(+j*2*pi*(u·r_i)).
88 *
89 * In standard array processing, a transmit precoder that steers energy toward
90 * that direction uses the complex conjugate of the response, therefore
91 * sign = -1 (phase = -2*pi*(u·r_i)).
92 *
93 * For receive combining, the effective scalar channel is computed using the
94 * Hermitian inner product:
95 *
96 * h_eff = w_rx^H * H * w_tx .
97 *
98 * To obtain matched-filter (maximum-ratio) receive combining toward the same
99 * direction, the stored receive weight vector should be proportional to the
100 * response a(theta), so that `w_rx^H` applies the conjugation at combining time
101 * sign = +1 (phase = +2*pi*(u·r_i)).
102 *
103 * Therefore, when this function is used to set beamforming vectors explicitly in
104 * this example:
105 * - Use `sign = -1` when generating TX weights (precoding).
106 * - Use `sign = +1` when generating RX weights (so that Hermitian combining
107 * applies the conjugation).
108 *
109 * @param txMob The mobility model of the node for which the weights are generated.
110 * @param thisAntenna The antenna array on which the beamforming vector is set.
111 * @param rxMob The mobility model of the peer node toward/from which the beam is steered.
112 * @param sign Phase progression sign: typically -1 for TX weights and +1 for RX weights.
113 */
114static void
116 Ptr<PhasedArrayModel> thisAntenna,
117 Ptr<MobilityModel> rxMob,
118 double sign)
119{
120 // retrieve the position of the two nodes
121 Vector aPos = txMob->GetPosition();
122 Vector bPos = rxMob->GetPosition();
123
124 // compute the azimuth and the elevation angles
125 Angles completeAngle(bPos, aPos);
126 double hAngleRadian = completeAngle.GetAzimuth();
127
128 double vAngleRadian = completeAngle.GetInclination(); // the elevation angle
129
130 // retrieve the number of antenna elements and resize the vector
131 uint64_t totNoArrayElements = thisAntenna->GetNumElems();
132 PhasedArrayModel::ComplexVector antennaWeights(totNoArrayElements);
133
134 // the total power is divided equally among the antenna elements
135 double power = 1.0 / sqrt(totNoArrayElements);
136
137 // compute the antenna weights
138 const double sinVAngleRadian = sin(vAngleRadian);
139 const double cosVAngleRadian = cos(vAngleRadian);
140 const double sinHAngleRadian = sin(hAngleRadian);
141 const double cosHAngleRadian = cos(hAngleRadian);
142
143 for (uint64_t ind = 0; ind < totNoArrayElements; ind++)
144 {
145 Vector loc = thisAntenna->GetElementLocation(ind);
146 double phase = sign * 2 * M_PI *
147 (sinVAngleRadian * cosHAngleRadian * loc.x +
148 sinVAngleRadian * sinHAngleRadian * loc.y + cosVAngleRadian * loc.z);
149 antennaWeights[ind] = exp(std::complex<double>(0, phase)) * power;
150 }
151
152 // store the antenna weights
153 thisAntenna->SetBeamformingVector(antennaWeights);
154}
155
156/**
157 * Build the RX matched-filter (maximum-ratio) combining vector by aggregating
158 * the contributions of all clusters and normalizing the result:
159 * \f[
160 * \mathbf{g} = \sum_{c} \mathbf{H}_{c}\mathbf{w}_{\rm tx}, \qquad
161 * \mathbf{w}_{\rm rx} = \frac{\mathbf{g}}{\|\mathbf{g}\|}
162 * \f]
163 *
164 * The per-cluster channel matrix \f$\mathbf{H}_{c}\f$ is obtained from
165 * ``params->m_channel(u, s, c)`` (u: RX element index, s: TX element index, c: cluster index).
166 *
167 * @param params Channel parameters containing the per-cluster channel matrix.
168 * @param txAntenna TX phased-array model.
169 * @param rxAntenna RX phased-array model.
170 * @param wTx Unit-norm TX beamforming/precoding vector (size must match
171 * ``txAntenna->GetNumElems()``).
172 *
173 * @return The RX combining vector (size ``rxAntenna->GetNumElems()``), normalized to unit norm
174 * (unless the channel is all-zero, in which case a zero vector is returned).
175 */
176
183{
184 using C = std::complex<double>;
185
186 const uint64_t nTx = txAntenna->GetNumElems();
187 const uint64_t nRx = rxAntenna->GetNumElems();
188 const size_t numClusters = params->m_channel.GetNumPages();
189
190 NS_ASSERT_MSG(wTx.GetSize() == nTx, "wTx size does not match TX antenna elements");
191 NS_ASSERT_MSG(params->m_channel.GetNumRows() == nRx, "Channel rows != #RX elements");
192 NS_ASSERT_MSG(params->m_channel.GetNumCols() == nTx, "Channel cols != #TX elements");
193
195
196 // g = sum_c (H_c * wTx)
197 for (size_t c = 0; c < numClusters; ++c)
198 {
199 for (uint64_t u = 0; u < nRx; ++u)
200 {
201 C acc{0.0, 0.0};
202 for (uint64_t s = 0; s < nTx; ++s)
203 {
204 acc += params->m_channel(u, s, c) * wTx[s];
205 }
206 g[u] += acc;
207 }
208 }
209 // Normalize: wRx = g / ||g||
210 double norm2 = 0.0;
211 for (uint64_t u = 0; u < nRx; ++u)
212 {
213 norm2 += std::norm(g[u]);
214 }
215 if (norm2 > 0.0)
216 {
217 const double invNorm = 1.0 / std::sqrt(norm2);
218 for (uint64_t u = 0; u < nRx; ++u)
219 {
220 g[u] *= invNorm;
221 }
222 }
223 return g; // unit-norm (unless channel is all-zero)
224}
225
226/**
227 * Compute the average SNR
228 * @param params A structure that holds the parameters that are needed to perform calculations in
229 * ComputeSnr
230 */
231static void
233{
234 // Create the tx PSD using the LteSpectrumValueHelper
235 // 100 RBs corresponds to 18 MHz (1 RB = 180 kHz)
236 // EARFCN 100 corresponds to 2125.00 MHz
237 std::vector<int> activeRbs0(100);
238 for (int i = 0; i < 100; i++)
239 {
240 activeRbs0[i] = i;
241 }
242 auto txPsd =
243 LteSpectrumValueHelper::CreateTxPowerSpectralDensity(2100, 100, params.txPow, activeRbs0);
244 auto txParams = Create<SpectrumSignalParameters>();
245 txParams->psd = txPsd->Copy();
246 NS_LOG_DEBUG("Average tx power " << 10 * log10(Sum(*txPsd) * 180e3) << " dB");
247
248 // create the noise PSD
249 auto noisePsd =
250 LteSpectrumValueHelper::CreateNoisePowerSpectralDensity(2100, 100, params.noiseFigure);
251 NS_LOG_DEBUG("Average noise power " << 10 * log10(Sum(*noisePsd) * 180e3) << " dB");
252
253 // apply the pathloss
254 double propagationGainDb = m_propagationLossModel->CalcRxPower(0, params.txMob, params.rxMob);
255 NS_LOG_DEBUG("Pathloss " << -propagationGainDb << " dB");
256 double propagationGainLinear = std::pow(10.0, (propagationGainDb) / 10.0);
257 *(txParams->psd) *= propagationGainLinear;
258
259 NS_ASSERT_MSG(params.txAntenna, "params.txAntenna is nullptr!");
260 NS_ASSERT_MSG(params.rxAntenna, "params.rxAntenna is nullptr!");
261
262 // set/update the beamforming vectors on each call
263 double sign = -1.0;
264 DoBeamforming(params.txMob, params.txAntenna, params.rxMob, sign);
265 sign = +1.0;
266 DoBeamforming(params.rxMob, params.rxAntenna, params.txMob, sign);
267 // apply the fast fading and the beamforming gain
268 auto rxParams = m_spectrumLossModel->CalcRxPowerSpectralDensity(txParams,
269 params.txMob,
270 params.rxMob,
271 params.txAntenna,
272 params.rxAntenna);
273 auto rxPsdSteering = rxParams->psd;
274 NS_LOG_DEBUG("Average rx power " << 10 * log10(Sum(*rxPsdSteering) * 180e3) << " dB");
275 NS_LOG_DEBUG("Average SNR " << 10 * log10(Sum(*rxPsdSteering) / Sum(*noisePsd)) << " dB");
276
277 Ptr<ThreeGppChannelModel> channelModel =
280 channelModel->GetChannel(params.txMob, params.rxMob, params.txAntenna, params.rxAntenna);
281
282 const auto& wTx = params.txAntenna->GetBeamformingVectorRef();
283 auto wRx =
284 BuildRxMatchedFilterOverAllClusters(channelMatrix, params.txAntenna, params.rxAntenna, wTx);
285 params.rxAntenna->SetBeamformingVector(wRx); // Install RX matched-filter (MRC) combiner
286
287 auto rxParamsMatchedFilterComb =
288 m_spectrumLossModel->CalcRxPowerSpectralDensity(txParams,
289 params.txMob,
290 params.rxMob,
291 params.txAntenna,
292 params.rxAntenna);
293
294 auto rxPsdMatchedFilterComb = rxParamsMatchedFilterComb->psd;
295 NS_LOG_DEBUG("Average rx power when using matched filter combining "
296 << 10 * log10(Sum(*rxPsdMatchedFilterComb) * 180e3) << " dB");
297 NS_LOG_DEBUG("Average SNR when using matched filter combining "
298 << 10 * log10(Sum(*rxPsdMatchedFilterComb) / Sum(*noisePsd)) << " dB");
299
300 // print the SNR and pathloss values in the snr-trace.txt file
301 std::ofstream f;
302 f.open("snr-trace.txt", std::ios::out | std::ios::app);
303 f << Simulator::Now().GetSeconds() << " " << 10 * log10(Sum(*rxPsdSteering) / Sum(*noisePsd))
304 << " " << 10 * log10(Sum(*rxPsdMatchedFilterComb) / Sum(*noisePsd)) << " "
305 << propagationGainDb << std::endl;
306 f.close();
307}
308
309int
310main(int argc, char* argv[])
311{
312 double frequency = 2125.0e6; // operating frequency in Hz (corresponds to EARFCN 2100)
313 double txPow = 49.0; // tx power in dBm
314 double noiseFigure = 9.0; // noise figure in dB
315 double distance = 10.0; // distance between tx and rx nodes in meters
316 uint32_t simTime = 1000; // simulation time in milliseconds
317 uint32_t timeRes = 10; // time resolution in milliseconds
318 std::string scenario = "UMa"; // 3GPP propagation scenario
319
320 CommandLine cmd(__FILE__);
321 cmd.AddValue("frequency", "Operating frequency in Hz", frequency);
322 cmd.AddValue("txPow", "TX power in dBm", txPow);
323 cmd.AddValue("noiseFigure", "Noise figure in dB", noiseFigure);
324 cmd.AddValue("distance", "Distance between TX and RX nodes in meters", distance);
325 cmd.AddValue("simTime", "Simulation time in milliseconds", simTime);
326 cmd.AddValue("timeRes", "Time resolution in milliseconds", timeRes);
327 cmd.AddValue("scenario", "3GPP propagation scenario", scenario);
328 cmd.Parse(argc, argv);
329
330 Config::SetDefault("ns3::ThreeGppChannelModel::UpdatePeriod",
331 TimeValue(MilliSeconds(1))); // update the channel at each iteration
332 Config::SetDefault("ns3::ThreeGppChannelConditionModel::UpdatePeriod",
333 TimeValue(MilliSeconds(0))); // do not update the channel condition
334
337
338 // create and configure the factories for the channel condition and propagation loss models
339 ObjectFactory propagationLossModelFactory;
340 ObjectFactory channelConditionModelFactory;
341 if (scenario == "RMa")
342 {
343 propagationLossModelFactory.SetTypeId(ThreeGppRmaPropagationLossModel::GetTypeId());
344 channelConditionModelFactory.SetTypeId(ThreeGppRmaChannelConditionModel::GetTypeId());
345 }
346 else if (scenario == "UMa")
347 {
348 propagationLossModelFactory.SetTypeId(ThreeGppUmaPropagationLossModel::GetTypeId());
349 channelConditionModelFactory.SetTypeId(ThreeGppUmaChannelConditionModel::GetTypeId());
350 }
351 else if (scenario == "UMi-StreetCanyon")
352 {
353 propagationLossModelFactory.SetTypeId(
355 channelConditionModelFactory.SetTypeId(
357 }
358 else if (scenario == "InH-OfficeOpen")
359 {
360 propagationLossModelFactory.SetTypeId(
362 channelConditionModelFactory.SetTypeId(
364 }
365 else if (scenario == "InH-OfficeMixed")
366 {
367 propagationLossModelFactory.SetTypeId(
369 channelConditionModelFactory.SetTypeId(
371 }
372 else
373 {
374 NS_FATAL_ERROR("Unknown scenario");
375 }
376
377 // create the propagation loss model
378 m_propagationLossModel = propagationLossModelFactory.Create<ThreeGppPropagationLossModel>();
379 m_propagationLossModel->SetAttribute("Frequency", DoubleValue(frequency));
380 m_propagationLossModel->SetAttribute("ShadowingEnabled", BooleanValue(false));
381
382 // create the spectrum propagation loss model
384 m_spectrumLossModel->SetChannelModelAttribute("Frequency", DoubleValue(frequency));
385 m_spectrumLossModel->SetChannelModelAttribute("Scenario", StringValue(scenario));
386
387 // create the channel condition model and associate it with the spectrum and
388 // propagation loss model
390 channelConditionModelFactory.Create<ThreeGppChannelConditionModel>();
391 m_spectrumLossModel->SetChannelModelAttribute("ChannelConditionModel", PointerValue(condModel));
392 m_propagationLossModel->SetChannelConditionModel(condModel);
393
394 // create the tx and rx nodes
396 nodes.Create(2);
397
398 // create the tx and rx mobility models, set the positions
400 txMob->SetPosition(Vector(0.0, 0.0, 10.0));
402 rxMob->SetPosition(Vector(distance, 0.0, 1.6));
403 rxMob->SetVelocity(Vector(0.5, 0, 0)); // set small velocity to allow for channel updates
404
405 // assign the mobility models to the nodes
406 nodes.Get(0)->AggregateObject(txMob);
407 nodes.Get(1)->AggregateObject(rxMob);
408
409 // create the antenna objects and set their dimensions
410 Ptr<PhasedArrayModel> txAntenna =
412 UintegerValue(2),
413 "NumRows",
414 UintegerValue(2));
415 Ptr<PhasedArrayModel> rxAntenna =
417 UintegerValue(2),
418 "NumRows",
419 UintegerValue(2));
420
421 for (int i = 0; i < floor(simTime / timeRes); i++)
422 {
423 ComputeSnrParams params{txMob, rxMob, txPow, noiseFigure, txAntenna, rxAntenna};
424 Simulator::Schedule(MilliSeconds(timeRes * i), &ComputeSnr, params);
425 }
426
429 return 0;
430}
uint32_t u
Class holding the azimuth and inclination angles of spherical coordinates.
Definition angles.h:107
double GetInclination() const
Getter for inclination angle.
Definition angles.cc:236
double GetAzimuth() const
Getter for azimuth angle.
Definition angles.cc:230
AttributeValue implementation for Boolean.
Definition boolean.h:26
Parse command-line arguments.
This class can be used to hold variables of floating point type such as 'double' or 'float'.
Definition double.h:31
static Ptr< SpectrumValue > CreateNoisePowerSpectralDensity(uint32_t earfcn, uint16_t bandwidth, double noiseFigure)
create a SpectrumValue that models the power spectral density of AWGN
static Ptr< SpectrumValue > CreateTxPowerSpectralDensity(uint32_t earfcn, uint16_t bandwidth, double powerTx, std::vector< int > activeRbs)
create a spectrum value representing the power spectral density of a signal to be transmitted.
keep track of a set of node pointers.
Instantiate subclasses of ns3::Object.
Ptr< Object > Create() const
Create an Object instance of the configured TypeId.
void SetTypeId(TypeId tid)
Set the TypeId of the Objects to be created by this factory.
ComplexMatrixArray ComplexVector
the underlying Valarray
AttributeValue implementation for Pointer.
Definition pointer.h:37
Smart pointer class similar to boost::intrusive_ptr.
Definition ptr.h:70
static void SetRun(uint64_t run)
Set the run number of simulation.
static void SetSeed(uint32_t seed)
Set the seed.
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition simulator.h:580
static void Destroy()
Execute the events scheduled with ScheduleDestroy().
Definition simulator.cc:125
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:191
static void Run()
Run the simulation.
Definition simulator.cc:161
Hold variables of type string.
Definition string.h:45
Base class for the 3GPP channel condition models.
Base class for the 3GPP propagation models.
static TypeId GetTypeId()
Get the type ID.
static TypeId GetTypeId()
Get the type ID.
double GetSeconds() const
Get an approximation of the time stored in this instance in the indicated unit.
Definition nstime.h:398
AttributeValue implementation for Time.
Definition nstime.h:1375
Hold an unsigned integer type.
Definition uinteger.h:34
size_t GetSize() const
Definition val-array.h:394
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition assert.h:75
void SetDefault(std::string name, const AttributeValue &value)
Definition config.cc:886
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:194
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition log.h:260
Ptr< T > CreateObject(Args &&... args)
Create an object by type, with varying number of constructor parameters.
Definition object.h:627
Ptr< T > CreateObjectWithAttributes(Args... args)
Allocate an Object on the heap and initialize with a set of attributes.
Ptr< T > Create(Ts &&... args)
Create class instances by constructors with varying numbers of arguments and return them by Ptr.
Definition ptr.h:454
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1290
NodeContainer nodes
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Ptr< T1 > DynamicCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:605
double Sum(const SpectrumValue &x)
params
Fit Fluctuating Two Ray model to the 3GPP TR 38.901 using the Anderson-Darling goodness-of-fit ##.
A structure that holds the parameters for the ComputeSnr function.
Ptr< PhasedArrayModel > txAntenna
the tx antenna array
Ptr< MobilityModel > rxMob
the rx mobility model
double noiseFigure
the noise figure in dB
double txPow
the tx power in dBm
Ptr< PhasedArrayModel > rxAntenna
the rx antenna array
Ptr< MobilityModel > txMob
the tx mobility model
static void DoBeamforming(Ptr< MobilityModel > txMob, Ptr< PhasedArrayModel > thisAntenna, Ptr< MobilityModel > rxMob, double sign)
Perform a simple (DFT/steering) beamforming toward the other node.
static void ComputeSnr(const ComputeSnrParams &params)
Compute the average SNR.
static ns3::PhasedArrayModel::ComplexVector BuildRxMatchedFilterOverAllClusters(ns3::Ptr< const ns3::MatrixBasedChannelModel::ChannelMatrix > params, ns3::Ptr< const ns3::PhasedArrayModel > txAntenna, ns3::Ptr< const ns3::PhasedArrayModel > rxAntenna, const ns3::PhasedArrayModel::ComplexVector &wTx)
Build the RX matched-filter (maximum-ratio) combining vector by aggregating the contributions of all ...
static Ptr< ThreeGppPropagationLossModel > m_propagationLossModel
the log component
static void ComputeSnr(const ComputeSnrParams &params)
Compute the average SNR.
static Ptr< ThreeGppSpectrumPropagationLossModel > m_spectrumLossModel
the SpectrumPropagationLossModel object