A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
three-gpp-channel-consistency-example.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2020, University of Padova, Dep. of Information Engineering, SIGNET lab
3 * Copyright (c) 2026, Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
4 *
5 * SPDX-License-Identifier: GPL-2.0-only
6 *
7 */
8
9/*
10 * @file
11 * This example shows how to configure the channel consistent updates in a mobile environment.
12 * This example is using the channel consistency feature of the 3GPP channel model described in
13 * 3GPP TR 38.901, Sec. 7.6.3. This feature enables the generation of spatially
14 * correlated channel realizations for simulations with mobility. This feature is
15 * automatically enabled by setting up the UpdatePeriod attribute of ThreeGppChannelModel to be
16 * different than 0. The example is the following 38.901 7.8.5 scenario setup Config2 parameters
17 * for channel consistency evaluation of mobile users (Procedure A). The channel model is
18 * 3GPP UMi Street Canyon, while the channel condition model is set to be a deterministic
19 * channel condition model based on the buildings obstacles that are configured in the
20 * example, according to a typical 3GPP urban setup. See the illustration in the following figure:
21 *
22 * _______ _______
23 * | | | |
24 * | | | |
25 * | | | |
26 * | | | |
27 * | | | |
28 * |______| |______|
29 * < < < < <
30 * _______ ^ _______
31 * | | ^ | |
32 * | | ^ | |
33 * | | ^ | |
34 * | | ^ | |
35 * | | UE | |
36 * |______| |______|
37 * * gNB
38 *
39 * gNB is at the fixed position (shown by * in the previous illustration), and at the height of 10
40 * meters. The user is moving away from gNB as illustrated in the figure with ^ and <, and first, it
41 * is in the LOS state, and later, after turning into a perpendicular street, it is in the NLOS
42 * state.
43 *
44 * The UE moves with a speed of 30km/h, the frequency used is 30GHz, and the channel update is
45 * 0.5ms, configured based on the channel coherence time.
46 *
47 * The antenna array is set to quasiomni, and it is fixed during the whole simulation to eliminate
48 * possible effects of the impact of the beamforming update while the user is moving. The example
49 * tries to focus only on the changes in the SNR caused by the channel updates as the user moves.
50 * The bearing angels are set to 90 at gNB and -90 at UE.
51 *
52 * This example generates the output file '3gpp-channel-consistency-output.txt'. Each row of this
53 * output file is organized as follows:
54 * Time[s] TxPosX[m] TxPosY[m] RxPosX[m] RxPosY[m] ChannelState SNR[dB] Pathloss[dB]
55 * An additional python script three-gpp-channel-consistency-example.py reads this
56 * output file and generates two figures:
57 * (i) 3gpp-channel-consistency.gif, a GIF representing the simulation scenario and the UE mobility;
58 * it includes the "zoomed view" of the SNR, and also it includes the variation of the
59 * channel updates.
60 * (ii) 3gpp-channel-consistency-snr.png, which represents the behavior of the SNR over
61 * the time, the absolute value and the varying rate over time.
62 *
63 */
64
65#include "ns3/buildings-module.h"
66#include "ns3/core-module.h"
67#include "ns3/mobility-module.h"
68#include "ns3/network-module.h"
69#include "ns3/spectrum-signal-parameters.h"
70#include "ns3/three-gpp-channel-model.h"
71#include "ns3/three-gpp-propagation-loss-model.h"
72#include "ns3/three-gpp-spectrum-propagation-loss-model.h"
73#include "ns3/uniform-planar-array.h"
74
75#include <fstream>
76
77using namespace ns3;
78
79/// the log component
80NS_LOG_COMPONENT_DEFINE("ThreeGppChannelConsistencyExample");
81
82/// the PropagationLossModel object
84/// the SpectrumPropagationLossModel object
86/// the ChannelConditionModel object
88
89/**
90 * @brief A structure that holds the parameters for the ComputeSnr
91 * function.
92 */
94{
95 Ptr<MobilityModel> txMob; //!< the tx mobility model
96 Ptr<MobilityModel> rxMob; //!< the rx mobility model
97 Ptr<SpectrumSignalParameters> txParams; //!< the params of the tx signal
98 double noiseFigure; //!< the noise figure in dB
99 Ptr<PhasedArrayModel> txAntenna; //!< the tx antenna array
100 Ptr<PhasedArrayModel> rxAntenna; //!< the rx antenna array
101};
102
103/**
104 * Set QuasiOmni beamforming vector to the antenna array
105 * @param antenna the antenna array to which will be set quasi omni beamforming vector
106 */
107static void
109{
110 PhasedArrayModel::ComplexVector antennaWeights;
111
112 auto antennaRows = antenna->GetNumRows();
113 auto antennaColumns = antenna->GetNumColumns();
114 auto numElemsPerPort = antenna->GetNumElemsPerPort();
115
116 double power = 1 / sqrt(numElemsPerPort);
117 size_t numPolarizations = antenna->IsDualPol() ? 2 : 1;
118
119 PhasedArrayModel::ComplexVector omni(antennaRows * antennaColumns * numPolarizations);
120 uint16_t bfIndex = 0;
121 for (size_t pol = 0; pol < numPolarizations; pol++)
122 {
123 for (uint32_t ind = 0; ind < antennaRows; ind++)
124 {
125 std::complex<double> c = 0.0;
126 if (antennaRows % 2 == 0)
127 {
128 c = exp(std::complex<double>(0, M_PI * ind * ind / antennaRows));
129 }
130 else
131 {
132 c = exp(std::complex<double>(0, M_PI * ind * (ind + 1) / antennaRows));
133 }
134 for (uint32_t ind2 = 0; ind2 < antennaColumns; ind2++)
135 {
136 std::complex<double> d = 0.0;
137 if (antennaColumns % 2 == 0)
138 {
139 d = exp(std::complex<double>(0, M_PI * ind2 * ind2 / antennaColumns));
140 }
141 else
142 {
143 d = exp(std::complex<double>(0, M_PI * ind2 * (ind2 + 1) / antennaColumns));
144 }
145 omni[bfIndex] = (c * d * power);
146 bfIndex++;
147 }
148 }
149 }
150
151 antenna->SetBeamformingVector(omni);
152}
153
154/**
155 * Computes the SNR
156 * @param params A structure that holds a bunch of parameters needed by ComputeSnr function to
157 * calculate the average SNR
158 */
159static void
161{
162 // check the channel condition
163 Ptr<ChannelCondition> cond = m_condModel->GetChannelCondition(params.txMob, params.rxMob);
164 // apply the pathloss
165 double propagationGainDb = m_propagationLossModel->CalcRxPower(0, params.txMob, params.rxMob);
166 double propagationGainLinear = std::pow(10.0, (propagationGainDb) / 10.0);
167 *(params.txParams->psd) *= propagationGainLinear;
168 // apply the fast fading and the beamforming gain
169 auto rxParams = m_spectrumLossModel->CalcRxPowerSpectralDensity(params.txParams,
170 params.txMob,
171 params.rxMob,
172 params.txAntenna,
173 params.rxAntenna);
174 Ptr<SpectrumValue> rxPsd = rxParams->psd;
175 // create the noise psd
176 // taken from lte-spectrum-value-helper
177 const double kT_dBm_Hz = -174.0; // dBm/Hz
178 double kT_W_Hz = std::pow(10.0, (kT_dBm_Hz - 30) / 10.0);
179 double noiseFigureLinear = std::pow(10.0, params.noiseFigure / 10.0);
180 double noisePowerSpectralDensity = kT_W_Hz * noiseFigureLinear;
181 Ptr<SpectrumValue> noisePsd = Create<SpectrumValue>(params.txParams->psd->GetSpectrumModel());
182 (*noisePsd) = noisePowerSpectralDensity;
183 // print the SNR and pathloss values to the output file
184 std::ofstream f;
185 f.open("3gpp-channel-consistency-output.txt", std::ios::out | std::ios::app);
186 f << Simulator::Now().GetSeconds() << " " // time [s]
187 << params.txMob->GetPosition().x << " " << params.txMob->GetPosition().y << " "
188 << params.rxMob->GetPosition().x << " " << params.rxMob->GetPosition().y << " "
189 << cond->GetLosCondition() << " " // channel state
190 << 10 * log10(Sum(*rxPsd) / Sum(*noisePsd)) << " " // SNR [dB]
191 << -propagationGainDb << std::endl; // pathloss [dB]
192 f.close();
193}
194
195/**
196 * Generates a GNU-plottable file representing the buildings deployed in the
197 * scenario
198 * @param filename the name of the output file
199 */
200void
202{
203 std::ofstream outFile;
204 outFile.open(filename, std::ios_base::out | std::ios_base::trunc);
205 if (!outFile.is_open())
206 {
207 NS_LOG_ERROR("Can't open file " << filename);
208 return;
209 }
210 for (auto it = BuildingList::Begin(); it != BuildingList::End(); ++it)
211 {
212 Box box = (*it)->GetBoundaries();
213 outFile << box.xMin << " " << box.yMin << " " << box.xMax << " " << box.yMax << std::endl;
214 }
215}
216
217int
218main(int argc, char* argv[])
219{
220 double frequency = 30e9; // operating frequency in Hz
221 double txPow_dbm = 35.0; // tx power in dBm
222 double noiseFigure = 9.0; // noise figure in dB
223 Time simTime = Seconds(40); // simulation time
224 Time timeRes = MicroSeconds(500); // time resolution and the channel update time
225 double speed = 8.33; // speed of the mobile node in the scenario [m/s]
226 double rbWidthHz = 720e3; // RB width in Hz
227 uint32_t numRb = 275; // number of resource blocks
228 double gNBHeight = 10; // the height of the gNB
229 double ueHeight =
230 1.1; // the height of the UE (a bit higher than 1m because of the BP distance calculation)
231
232 // fix random parameters
235 uint64_t stream = 10e3;
236
237 // create the nodes
239 nodes.Create(2);
240
241 // create the tx and rx devices
244
245 // associate the nodes and the devices
246 nodes.Get(0)->AddDevice(txDev);
247 txDev->SetNode(nodes.Get(0));
248 nodes.Get(1)->AddDevice(rxDev);
249 rxDev->SetNode(nodes.Get(1));
250
251 // create the antenna objects and set their dimensions
252 Ptr<PhasedArrayModel> txAntenna =
254 UintegerValue(2),
255 "NumRows",
256 UintegerValue(2),
257 "BearingAngle",
258 DoubleValue(M_PI / 2));
259 Ptr<PhasedArrayModel> rxAntenna =
261 UintegerValue(2),
262 "NumRows",
263 UintegerValue(2),
264 "BearingAngle",
265 DoubleValue(-M_PI / 2));
266
267 Ptr<MobilityModel> txMob;
268 Ptr<MobilityModel> rxMob;
269
270 // create a grid of buildings
271 double buildingSizeX = 250 - 3.5 * 2 - 3; // m
272 double buildingSizeY = 433 - 3.5 * 2 - 3; // m
273 double streetWidth = 20; // m
274 double buildingHeight = 10; // m
275 uint32_t numBuildingsX = 2;
276 uint32_t numBuildingsY = 2;
277 double maxAxisX = (buildingSizeX + streetWidth) * numBuildingsX;
278 double maxAxisY = (buildingSizeY + streetWidth) * numBuildingsY;
279
280 std::vector<Ptr<Building>> buildingVector;
281 for (uint32_t buildingIdX = 0; buildingIdX < numBuildingsX; ++buildingIdX)
282 {
283 for (uint32_t buildingIdY = 0; buildingIdY < numBuildingsY; ++buildingIdY)
284 {
285 Ptr<Building> building;
286 building = CreateObject<Building>();
287
288 building->SetBoundaries(Box(buildingIdX * (buildingSizeX + streetWidth),
289 buildingIdX * (buildingSizeX + streetWidth) + buildingSizeX,
290 buildingIdY * (buildingSizeY + streetWidth),
291 buildingIdY * (buildingSizeY + streetWidth) + buildingSizeY,
292 0.0,
293 buildingHeight));
294 building->SetNRoomsX(1);
295 building->SetNRoomsY(1);
296 building->SetNFloors(1);
297 buildingVector.push_back(building);
298 }
299 }
300
301 // set the mobility models of the TX(gNB) and RX(mobile UE)
303 txMob->SetPosition(Vector(maxAxisX / 2 - streetWidth / 2, -20, gNBHeight));
304 nodes.Get(0)->AggregateObject(txMob);
305
306 Time nextWaypoint;
308 rxMob->GetObject<WaypointMobilityModel>()->AddWaypoint(
309 Waypoint(nextWaypoint, Vector(maxAxisX / 2 - streetWidth / 2, maxAxisY / 4, ueHeight)));
310 nextWaypoint += Seconds((maxAxisY - maxAxisY / 2 - streetWidth) / 2 / speed);
311 rxMob->GetObject<WaypointMobilityModel>()->AddWaypoint(
312 Waypoint(nextWaypoint,
313 Vector(maxAxisX / 2 - streetWidth / 2, maxAxisY / 2 - streetWidth / 2, ueHeight)));
314 nextWaypoint += Seconds((maxAxisX - streetWidth) / 2 / speed);
315 rxMob->GetObject<WaypointMobilityModel>()->AddWaypoint(
316 Waypoint(nextWaypoint, Vector(0.0, maxAxisY / 2 - streetWidth / 2, ueHeight)));
317 nextWaypoint = Seconds(0);
318 nodes.Get(1)->AggregateObject(rxMob);
319
320 // create the channel condition model
322
323 // create the propagation loss model
325
326 m_propagationLossModel->SetAttribute("Frequency", DoubleValue(frequency));
327 m_propagationLossModel->SetAttribute("ShadowingEnabled", BooleanValue(true));
328 m_propagationLossModel->SetAttribute("ChannelConditionModel", PointerValue(m_condModel));
329 stream += m_propagationLossModel->AssignStreams(stream);
330
331 // create the channel model
333 channelModel->SetAttribute("Scenario", StringValue("UMi-StreetCanyon"));
334 channelModel->SetAttribute("Frequency", DoubleValue(frequency));
335 channelModel->SetAttribute("ChannelConditionModel", PointerValue(m_condModel));
336 channelModel->SetAttribute("UpdatePeriod", TimeValue(timeRes));
337 stream += channelModel->AssignStreams(stream);
338
339 // create the spectrum propagation loss model
341 "ChannelModel",
342 PointerValue(channelModel));
343
345
346 // create the tx power spectral density
347 Bands rbs;
348 double freqSubBand = frequency;
349 for (uint32_t n = 0; n < numRb; ++n)
350 {
351 BandInfo rb;
352 rb.fl = freqSubBand;
353 freqSubBand += rbWidthHz / 2;
354 rb.fc = freqSubBand;
355 freqSubBand += rbWidthHz / 2;
356 rb.fh = freqSubBand;
357 rbs.push_back(rb);
358 }
359 Ptr<SpectrumModel> spectrumModel = Create<SpectrumModel>(rbs);
360 Ptr<SpectrumValue> txPsd = Create<SpectrumValue>(spectrumModel);
362 double txPow_w = std::pow(10., (txPow_dbm - 30) / 10);
363 double txPowDens = (txPow_w / (numRb * rbWidthHz));
364 (*txPsd) = txPowDens;
365 txParams->psd = txPsd->Copy();
366
367 CreateQuasiOmniBf(txAntenna);
368 CreateQuasiOmniBf(rxAntenna);
369
370 for (int i = 0; i < simTime / timeRes; i++)
371 {
372 ComputeSnrParams params{txMob, rxMob, txParams->Copy(), noiseFigure, txAntenna, rxAntenna};
373 Simulator::Schedule(timeRes * i, &ComputeSnr, params);
374 }
375
376 // initialize the output file
377 std::ofstream f;
378 f.open("3gpp-channel-consistency-output.txt", std::ios::out);
379 f << "Time[s] TxPosX[m] TxPosY[m] RxPosX[m] RxPosY[m] ChannelState SNR[dB] Pathloss[dB]"
380 << std::endl;
381 f.close();
382
383 // print the list of buildings to file
384 PrintGnuplottableBuildingListToFile("3gpp-channel-consistency-buildings.txt");
385
388 return 0;
389}
AttributeValue implementation for Boolean.
Definition boolean.h:26
a 3d box
Definition box.h:24
double yMax
The y coordinate of the top bound of the box.
Definition box.h:105
double xMin
The x coordinate of the left bound of the box.
Definition box.h:99
double yMin
The y coordinate of the bottom bound of the box.
Definition box.h:103
double xMax
The x coordinate of the right bound of the box.
Definition box.h:101
static Iterator End()
static Iterator Begin()
static void Install(Ptr< Node > node)
Install the MobilityBuildingInfo to a node.
This class can be used to hold variables of floating point type such as 'double' or 'float'.
Definition double.h:31
keep track of a set of node pointers.
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
Simulation virtual time values and global simulation resolution.
Definition nstime.h:95
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
a (time, location) pair.
Definition waypoint.h:25
Waypoint-based mobility model.
#define NS_LOG_ERROR(msg)
Use NS_LOG to output a message of level LOG_ERROR.
Definition log.h:246
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:194
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 MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1307
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1273
NodeContainer nodes
Every class exported by the ns3 library is enclosed in the ns3 namespace.
std::vector< BandInfo > Bands
Container of BandInfo.
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
Ptr< SpectrumSignalParameters > txParams
the params of the tx signal
double noiseFigure
the noise figure in dB
Ptr< PhasedArrayModel > rxAntenna
the rx antenna array
Ptr< MobilityModel > txMob
the tx mobility model
The building block of a SpectrumModel.
double fc
center frequency
double fl
lower limit of subband
double fh
upper limit of subband
static void ComputeSnr(const ComputeSnrParams &params)
Computes the SNR.
void PrintGnuplottableBuildingListToFile(std::string filename)
Generates a GNU-plottable file representing the buildings deployed in the scenario.
static void CreateQuasiOmniBf(Ptr< PhasedArrayModel > antenna)
Set QuasiOmni beamforming vector to the antenna array.
static Ptr< ChannelConditionModel > m_condModel
the ChannelConditionModel object
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