5G-LENA nr-v4.1
The 5G/NR module for the ns-3 simulator
Loading...
Searching...
No Matches
lena-lte-comparison.cc
1// Copyright (c) 2020 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
2//
3// SPDX-License-Identifier: GPL-2.0-only
4
5#include "lena-lte-comparison.h"
6
7#include "flow-monitor-output-stats.h"
8#include "lena-v1-utils.h"
9#include "lena-v2-utils.h"
10#include "power-output-stats.h"
11#include "rb-output-stats.h"
12#include "sinr-output-stats.h"
13#include "slot-output-stats.h"
14
15#include "ns3/antenna-module.h"
16#include "ns3/applications-module.h"
17#include "ns3/config-store-module.h"
18#include "ns3/config-store.h"
19#include "ns3/core-module.h"
20#include "ns3/flow-monitor-module.h"
21#include "ns3/internet-apps-module.h"
22#include "ns3/internet-module.h"
23#include "ns3/lte-module.h"
24#include "ns3/mobility-module.h"
25#include "ns3/network-module.h"
26#include "ns3/point-to-point-module.h"
27#include "ns3/radio-environment-map-helper.h"
28#include "ns3/sqlite-output.h"
29
30#include <iomanip>
31
32/*
33 * To be able to use LOG_* functions.
34 */
35#include "ns3/log.h"
36
37/*
38 * With this line, we will be able to see the logs of the file by enabling the
39 * component "LenaLteComparison", in this way:
40 *
41 * $ export NS_LOG="LenaLteComparison=level_info|prefix_func|prefix_time"
42 */
43NS_LOG_COMPONENT_DEFINE("LenaLteComparison");
44
45namespace ns3
46{
47
48const Time appStartWindow = MilliSeconds(50);
49
50template <typename T>
51Ptr<T>
52CreateLowLatTft(uint16_t start, uint16_t end, std::string dir)
53{
54 Ptr<T> lowLatTft;
55 lowLatTft = Create<T>();
56 typename T::PacketFilter dlpfLowLat;
57 if (dir == "DL")
58 {
59 dlpfLowLat.localPortStart = start;
60 dlpfLowLat.localPortEnd = end;
61 dlpfLowLat.direction = T::DOWNLINK;
62 }
63 else
64 {
65 dlpfLowLat.remotePortStart = start;
66 dlpfLowLat.remotePortEnd = end;
67 dlpfLowLat.direction = T::UPLINK;
68 }
69 lowLatTft->Add(dlpfLowLat);
70 return lowLatTft;
71}
72
73template Ptr<ns3::EpcTft> CreateLowLatTft<ns3::EpcTft>(uint16_t, uint16_t, std::string);
74template Ptr<ns3::NrEpcTft> CreateLowLatTft<ns3::NrEpcTft>(uint16_t, uint16_t, std::string);
75
76static std::pair<ApplicationContainer, Time>
77InstallApps(const Ptr<Node>& ue,
78 const Ptr<NetDevice>& ueDevice,
79 const Address& ueAddress,
80 const std::string& direction,
81 UdpClientHelper* dlClientLowLat,
82 const Ptr<Node>& remoteHost,
83 const Ipv4Address& remoteHostAddr,
84 Time udpAppStartTime,
85 uint16_t dlPortLowLat,
86 const Ptr<UniformRandomVariable>& x,
87 Time appGenerationTime,
88 const Ptr<LteHelper>& lteHelper,
89 const Ptr<NrHelper>& nrHelper)
90{
91 ApplicationContainer app;
92
93 // The bearer that will carry low latency traffic
94 EpsBearer lowLatBearer(EpsBearer::NGBR_VIDEO_TCP_DEFAULT);
95 NrEpsBearer nrLowLatBearer(NrEpsBearer::NGBR_VIDEO_TCP_DEFAULT);
96
97 // The filter for the low-latency traffic
98 Ptr<EpcTft> lowLatTft = CreateLowLatTft<EpcTft>(dlPortLowLat, dlPortLowLat, direction);
99 Ptr<NrEpcTft> nrLowLatTft = CreateLowLatTft<NrEpcTft>(dlPortLowLat, dlPortLowLat, direction);
100
101 // The client, who is transmitting, is installed in the remote host,
102 // with destination address set to the address of the UE
103 if (direction == "DL")
104 {
105 dlClientLowLat->SetAttribute(
106 "Remote",
107 AddressValue(addressUtils::ConvertToSocketAddress(ueAddress, dlPortLowLat)));
108 app = dlClientLowLat->Install(remoteHost);
109 }
110 else
111 {
112 dlClientLowLat->SetAttribute(
113 "Remote",
114 AddressValue(addressUtils::ConvertToSocketAddress(remoteHostAddr, dlPortLowLat)));
115 app = dlClientLowLat->Install(ue);
116 }
117
118 double start = x->GetValue(udpAppStartTime.GetMilliSeconds(),
119 (udpAppStartTime + appStartWindow).GetMilliSeconds());
120 Time startTime = MilliSeconds(start);
121 app.Start(startTime);
122 app.Stop(startTime + appGenerationTime);
123
124 std::cout << "\tStarts at time " << startTime.As(Time::MS) << " and ends at "
125 << (startTime + appGenerationTime).As(Time::MS) << std::endl;
126
127 // Activate a dedicated bearer for the traffic type
128 if (lteHelper != nullptr)
129 {
130 lteHelper->ActivateDedicatedEpsBearer(ueDevice, lowLatBearer, lowLatTft);
131 }
132 else if (nrHelper != nullptr)
133 {
134 nrHelper->ActivateDedicatedEpsBearer(ueDevice, nrLowLatBearer, nrLowLatTft);
135 }
136
137 return std::make_pair(app, startTime);
138}
139
140bool
141Parameters::Validate() const
142{
143 NS_ABORT_MSG_IF(bandwidthMHz != 20 && bandwidthMHz != 10 && bandwidthMHz != 5,
144 "Valid bandwidth values are 20, 10, 5, you set " << bandwidthMHz);
145
146 NS_ABORT_MSG_IF(trafficScenario > 3,
147 "Traffic scenario " << trafficScenario
148 << " not valid. Valid values are 0 1 2 3");
149
150 NS_ABORT_MSG_IF(numerologyBwp > 4, "At most 4 bandwidth parts supported.");
151
152 NS_ABORT_MSG_IF(direction != "DL" && direction != "UL",
153 "Flow direction can only be DL or UL: " << direction);
154 NS_ABORT_MSG_IF(operationMode != "TDD" && operationMode != "FDD",
155 "Operation mode can only be TDD or FDD: " << operationMode);
156 NS_ABORT_MSG_IF(radioNetwork != "LTE" && radioNetwork != "NR",
157 "Unrecognized radio network technology: " << radioNetwork);
158 NS_ABORT_MSG_IF(radioNetwork == "LTE" && operationMode != "FDD",
159 "Operation mode must be FDD in a 4G LTE network: " << operationMode);
160 NS_ABORT_MSG_IF(simulator != "LENA" && simulator != "5GLENA",
161 "Unrecognized simulator: " << simulator);
162 NS_ABORT_MSG_IF(scheduler != "PF" && scheduler != "RR",
163 "Unrecognized scheduler: " << scheduler);
164
165 if (dlRem || ulRem)
166 {
167 NS_ABORT_MSG_IF(simulator != "5GLENA",
168 "Cannot do the REM with the simulator " << simulator);
169 NS_ABORT_MSG_IF(dlRem && ulRem, "You selected both DL and UL REM, that is not supported");
170 NS_ABORT_MSG_IF(remSector > 3, "Only three sectors supported for REM");
171
172 NS_ABORT_MSG_IF(remSector == 0 && freqScenario != 1,
173 "RemSector == 0 makes sense only in a OVERLAPPING scenario");
174 }
175
176 return true;
177}
178
179void
180LenaLteComparison(const Parameters& params)
181{
182 params.Validate();
183
184 // Traffic parameters (that we will use inside this script:)
185 uint32_t udpPacketSize = 1000;
186 uint32_t lambda;
187 uint32_t packetCount;
188
189 std::cout << "\n----------------------------------------\n"
190 << "Configuring scenario" << std::endl;
191
192 std::cout << " traffic parameters\n";
193 switch (params.trafficScenario)
194 {
195 case 0: // let's put 80 Mbps with 20 MHz of bandwidth. Everything else is scaled
196 packetCount = 0xFFFFFFFF;
197 switch (params.bandwidthMHz)
198 {
199 case 20:
200 udpPacketSize = 1000;
201 break;
202 case 10:
203 udpPacketSize = 500;
204 break;
205 case 5:
206 udpPacketSize = 250;
207 break;
208 default:
209 udpPacketSize = 1000;
210 }
211 lambda = 10000 / params.ueNumPergNb;
212 break;
213 case 1:
214 packetCount = 1;
215 udpPacketSize = 12;
216 lambda = 1;
217 break;
218 case 2: // 1 Mbps == 0.125 MB/s in case of 20 MHz, everything else is scaled
219 packetCount = 0xFFFFFFFF;
220 switch (params.bandwidthMHz)
221 {
222 case 20:
223 udpPacketSize = 125;
224 break;
225 case 10:
226 udpPacketSize = 63;
227 break;
228 case 5:
229 udpPacketSize = 32;
230 break;
231 default:
232 udpPacketSize = 125;
233 }
234 lambda = 1000 / params.ueNumPergNb;
235 break;
236 case 3: // 20 Mbps == 2.5 MB/s in case of 20 MHz, everything else is scaled
237 packetCount = 0xFFFFFFFF;
238 switch (params.bandwidthMHz)
239 {
240 case 20:
241 udpPacketSize = 250;
242 break;
243 case 10:
244 udpPacketSize = 125;
245 break;
246 case 5:
247 udpPacketSize = 75;
248 break;
249 default:
250 udpPacketSize = 250;
251 }
252 lambda = 10000 / params.ueNumPergNb;
253 break;
254 default:
255 NS_FATAL_ERROR("Traffic scenario " << params.trafficScenario
256 << " not valid. Valid values are 0 1 2 3");
257 }
258
259 std::cout << " statistics\n";
260 SQLiteOutput db(params.outputDir + "/" + params.simTag + ".db");
261 SinrOutputStats sinrStats;
262 PowerOutputStats ueTxPowerStats;
263 PowerOutputStats gnbRxPowerStats;
264 SlotOutputStats slotStats;
265 RbOutputStats rbStats;
266
267 sinrStats.SetDb(&db);
268 ueTxPowerStats.SetDb(&db, "ueTxPower");
269 slotStats.SetDb(&db);
270 rbStats.SetDb(&db);
271 gnbRxPowerStats.SetDb(&db, "gnbRxPower");
272
273 /*
274 * Check if the frequency and numerology are in the allowed range.
275 * If you need to add other checks, here is the best position to put them.
276 */
277 std::cout << " checking frequency and numerology\n";
278
279 /*
280 * If the logging variable is set to true, enable the log of some components
281 * through the code. The same effect can be obtained through the use
282 * of the NS_LOG environment variable:
283 *
284 * export NS_LOG="UdpClient=level_info|prefix_time|prefix_func|prefix_node:UdpServer=..."
285 *
286 * Usually, the environment variable way is preferred, as it is more customizable,
287 * and more expressive.
288 */
289 std::cout << " logging\n";
290 if (params.logging)
291 {
292 LogComponentEnable("UdpClient", LOG_LEVEL_INFO);
293 LogComponentEnable("UdpServer", LOG_LEVEL_INFO);
294 LogComponentEnable("LtePdcp", LOG_LEVEL_INFO);
295 // LogComponentEnable ("NrMacSchedulerOfdma", LOG_LEVEL_ALL);
296 }
297
298 /*
299 * Default values for the simulation. We are progressively removing all
300 * the instances of SetDefault, but we need it for legacy code (LTE)
301 */
302 std::cout << " max tx buffer size\n";
303 Config::SetDefault("ns3::LteRlcUm::MaxTxBufferSize", UintegerValue(999999999));
304 Config::SetDefault("ns3::NrRlcUm::MaxTxBufferSize", UintegerValue(999999999));
305
306 /*
307 * Create the scenario. In our examples, we heavily use helpers that setup
308 * the gnbs and ue following a pre-defined pattern. Please have a look at the
309 * HexagonalGridScenarioHelper documentation to see how the nodes will be distributed.
310 */
311
312 ScenarioParameters scenarioParams;
313 scenarioParams.SetScenarioParameters(params.scenario);
314 // Customize parameters here
315 // scenarioParams.isd = ...
316
317 // The essentials describing a laydown
318 uint32_t gnbSites = 0;
319 NodeContainer gnbNodes;
320 NodeContainer ueNodes;
321 double sector0AngleRad = 0;
322 const uint32_t sectors = 3;
323
324 //
325 NodeDistributionScenarioInterface* scenario{nullptr};
326 FileScenarioHelper fileScenario;
327 HexagonalGridScenarioHelper gridScenario;
328
329 if (!params.baseStationFile.empty() and params.useSiteFile)
330 {
331 std::cout << " using tower positions from " << params.baseStationFile << std::endl;
332 std::cout << " setting sectorization" << std::endl;
333 fileScenario.SetSectorization(sectors);
334 std::cout << " adding site file" << std::endl;
335 fileScenario.Add(params.baseStationFile);
336 std::cout << " setting scenario" << std::endl;
337 fileScenario.SetScenarioParameters(params.scenario);
338 std::cout << " getting number of sites..." << std::flush;
339 gnbSites = fileScenario.GetNumSites();
340 std::cout << gnbSites << std::endl;
341 std::cout << " getting number of cells..." << std::flush;
342 uint32_t gnbNum = fileScenario.GetNumCells();
343 std::cout << gnbNum << std::endl;
344 uint32_t ueNum = params.ueNumPergNb * gnbNum;
345 std::cout << " setting number of UEs..." << ueNum << std::endl;
346 fileScenario.SetUtNumber(ueNum);
347 std::cout << " getting sector 0 angle..." << std::flush;
348 sector0AngleRad = fileScenario.GetAntennaOrientationRadians(0);
349 std::cout << sector0AngleRad << std::endl;
350
351 // Creates and plots the network deployment
352 std::cout << " creating scenario" << std::endl;
353 fileScenario.CreateScenario();
354 std::cout << " getting gnbNodes..." << std::flush;
355 gnbNodes = fileScenario.GetBaseStations();
356 std::cout << gnbNodes.GetN() << std::endl;
357 std::cout << " getting ueNodes..." << std::flush;
358 ueNodes = fileScenario.GetUserTerminals();
359 std::cout << ueNodes.GetN() << std::endl;
360 std::cout << " setting scenario pointer..." << std::flush;
361 scenario = &fileScenario;
362 std::cout << "0x" << std::hex << scenario << std::dec << std::endl;
363 }
364 else
365 {
366 std::cout << " hexagonal grid: ";
367 gridScenario.SetScenarioParameters(scenarioParams);
368 gridScenario.SetNumRings(params.numOuterRings);
369 gnbSites = gridScenario.GetNumSites();
370 uint32_t ueNum = params.ueNumPergNb * gnbSites * sectors;
371 gridScenario.SetUtNumber(ueNum);
372 sector0AngleRad = gridScenario.GetAntennaOrientationRadians(0);
373 std::cout << sector0AngleRad << std::endl;
374
375 // Creates and plots the network deployment
376 gridScenario.CreateScenario();
377 gnbNodes = gridScenario.GetBaseStations();
378 ueNodes = gridScenario.GetUserTerminals();
379 scenario = &gridScenario;
380 }
381
382 // Log the configuration
383 std::cout << "\n Topology configuration: " << gnbSites << " sites, " << sectors
384 << " sectors/site, " << gnbNodes.GetN() << " cells, " << ueNodes.GetN() << " UEs\n";
385
386 /*
387 * Create different gNB NodeContainer for the different sectors.
388 *
389 * Relationships between ueId, cellId, sectorId and siteId:
390 * ~~~{.cc}
391 * cellId = scenario->GetCellIndex (ueId);
392 * sector = scenario->GetSectorIndex (cellId);
393 * siteId = scenario->GetSiteIndex (cellId);
394 * ~~~{.cc}
395 *
396 * Iterate/index gnbNodes, gnbNetDevs by `cellId`.
397 * Iterate/index gnbSector<N>Container, gnbNodesBySector[sector],
398 * gnbSector<N>NetDev, gnbNdBySector[sector] by `siteId`
399 */
400 NodeContainer gnbSector1Container;
401 NodeContainer gnbSector2Container;
402 NodeContainer gnbSector3Container;
403 std::vector<NodeContainer*> gnbNodesBySector{&gnbSector1Container,
404 &gnbSector2Container,
405 &gnbSector3Container};
406 for (uint32_t cellId = 0; cellId < gnbNodes.GetN(); ++cellId)
407 {
408 Ptr<Node> gnb = gnbNodes.Get(cellId);
409 auto sector = scenario->GetSectorIndex(cellId);
410 gnbNodesBySector[sector]->Add(gnb);
411 }
412 std::cout << " gNb containers: " << gnbSector1Container.GetN() << ", "
413 << gnbSector2Container.GetN() << ", " << gnbSector3Container.GetN() << std::endl;
414
415 /*
416 * Create different UE NodeContainer for the different sectors.
417 *
418 * Multiple UEs per sector!
419 * Iterate/index ueNodes, ueNetDevs, ueIpIfaces by `ueId`.
420 * Iterate/Index ueSector<N>Container, ueNodesBySector[sector],
421 * ueSector<N>NetDev, ueNdBySector[sector] with i % gnbSites
422 */
423 NodeContainer ueSector1Container;
424 NodeContainer ueSector2Container;
425 NodeContainer ueSector3Container;
426 std::vector<NodeContainer*> ueNodesBySector{&ueSector1Container,
427 &ueSector2Container,
428 &ueSector3Container};
429 for (uint32_t ueId = 0; ueId < ueNodes.GetN(); ++ueId)
430 {
431 Ptr<Node> ue = ueNodes.Get(ueId);
432 auto cellId = scenario->GetCellIndex(ueId);
433 auto sector = scenario->GetSectorIndex(cellId);
434 ueNodesBySector[sector]->Add(ue);
435 }
436 std::cout << " UE containers: " << ueSector1Container.GetN() << ", "
437 << ueSector2Container.GetN() << ", " << ueSector3Container.GetN() << std::endl;
438
439 /*
440 * Setup the LTE or NR module. We create the various helpers needed inside
441 * their respective configuration functions
442 */
443 std::cout << " helpers\n";
444 Ptr<PointToPointEpcHelper> epcHelper;
445 Ptr<NrPointToPointEpcHelper> nrEpcHelper;
446
447 NetDeviceContainer gnbSector1NetDev;
448 NetDeviceContainer gnbSector2NetDev;
449 NetDeviceContainer gnbSector3NetDev;
450 std::vector<NetDeviceContainer*> gnbNdBySector{&gnbSector1NetDev,
451 &gnbSector2NetDev,
452 &gnbSector3NetDev};
453 NetDeviceContainer ueSector1NetDev;
454 NetDeviceContainer ueSector2NetDev;
455 NetDeviceContainer ueSector3NetDev;
456 std::vector<NetDeviceContainer*> ueNdBySector{&ueSector1NetDev,
457 &ueSector2NetDev,
458 &ueSector3NetDev};
459
460 Ptr<LteHelper> lteHelper = nullptr;
461 Ptr<NrHelper> nrHelper = nullptr;
462
463 if (params.simulator == "LENA")
464 {
465 epcHelper = CreateObject<PointToPointEpcHelper>();
466 LenaV1Utils::SetLenaV1SimulatorParameters(sector0AngleRad,
467 params.scenario,
468 gnbSector1Container,
469 gnbSector2Container,
470 gnbSector3Container,
471 ueSector1Container,
472 ueSector2Container,
473 ueSector3Container,
474 epcHelper,
475 lteHelper,
476 gnbSector1NetDev,
477 gnbSector2NetDev,
478 gnbSector3NetDev,
479 ueSector1NetDev,
480 ueSector2NetDev,
481 ueSector3NetDev,
482 params.calibration,
483 params.enableUlPc,
484 &sinrStats,
485 &ueTxPowerStats,
486 params.scheduler,
487 params.bandwidthMHz,
488 params.freqScenario,
489 params.downtiltAngle);
490 }
491 else if (params.simulator == "5GLENA")
492 {
493 nrEpcHelper = CreateObject<NrPointToPointEpcHelper>();
495 params.scenario,
496 params.radioNetwork,
497 params.errorModel,
498 params.operationMode,
499 params.direction,
500 params.numerologyBwp,
501 params.pattern,
502 gnbSector1Container,
503 gnbSector2Container,
504 gnbSector3Container,
505 ueSector1Container,
506 ueSector2Container,
507 ueSector3Container,
508 nrEpcHelper,
509 nrHelper,
510 gnbSector1NetDev,
511 gnbSector2NetDev,
512 gnbSector3NetDev,
513 ueSector1NetDev,
514 ueSector2NetDev,
515 ueSector3NetDev,
516 params.calibration,
517 params.enableUlPc,
518 params.powerAllocation,
519 &sinrStats,
520 &ueTxPowerStats,
521 &gnbRxPowerStats,
522 &slotStats,
523 &rbStats,
524 params.scheduler,
525 params.bandwidthMHz,
526 params.freqScenario,
527 params.downtiltAngle);
528 }
529
530 // Check we got one valid helper
531 if ((lteHelper == nullptr) && (nrHelper == nullptr))
532 {
533 NS_ABORT_MSG("Programming error: no valid helper");
534 }
535
536 // From here, it is standard NS3. In the future, we will create helpers
537 // for this part as well.
538
539 // create the internet and install the IP stack on the UEs
540 // get SGW/PGW and create a single RemoteHost
541 std::cout << " pgw and internet\n";
542 Ptr<Node> pgw;
543 if (params.simulator == "LENA")
544 {
545 pgw = epcHelper->GetPgwNode();
546 }
547 else
548 {
549 pgw = nrEpcHelper->GetPgwNode();
550 }
551 NodeContainer remoteHostContainer;
552 remoteHostContainer.Create(1);
553 Ptr<Node> remoteHost = remoteHostContainer.Get(0);
554 InternetStackHelper internet;
555 internet.Install(remoteHostContainer);
556
557 // connect a remoteHost to pgw. Setup routing too
558 PointToPointHelper p2ph;
559 p2ph.SetDeviceAttribute("DataRate", DataRateValue(DataRate("100Gb/s")));
560 p2ph.SetDeviceAttribute("Mtu", UintegerValue(2500));
561 p2ph.SetChannelAttribute("Delay", TimeValue(Seconds(0.000)));
562 NetDeviceContainer internetDevices = p2ph.Install(pgw, remoteHost);
563 Ipv4AddressHelper ipv4h;
564 Ipv4StaticRoutingHelper ipv4RoutingHelper;
565 ipv4h.SetBase("1.0.0.0", "255.0.0.0");
566 Ipv4InterfaceContainer internetIpIfaces = ipv4h.Assign(internetDevices);
567 Ptr<Ipv4StaticRouting> remoteHostStaticRouting =
568 ipv4RoutingHelper.GetStaticRouting(remoteHost->GetObject<Ipv4>());
569 remoteHostStaticRouting->AddNetworkRouteTo(Ipv4Address("7.0.0.0"), Ipv4Mask("255.0.0.0"), 1);
570 internet.Install(ueNodes);
571
572 NetDeviceContainer gnbNetDevs(gnbSector1NetDev, gnbSector2NetDev);
573 gnbNetDevs.Add(gnbSector3NetDev);
574 NetDeviceContainer ueNetDevs(ueSector1NetDev, ueSector2NetDev);
575 ueNetDevs.Add(ueSector3NetDev);
576
577 Ipv4InterfaceContainer ueIpIfaces;
578 Ipv4Address gatewayAddress;
579 if (params.simulator == "LENA")
580 {
581 ueIpIfaces = epcHelper->AssignUeIpv4Address(ueNetDevs);
582 gatewayAddress = epcHelper->GetUeDefaultGatewayAddress();
583 }
584 else
585 {
586 ueIpIfaces = nrEpcHelper->AssignUeIpv4Address(ueNetDevs);
587 gatewayAddress = nrEpcHelper->GetUeDefaultGatewayAddress();
588 }
589
590 Ipv4Address remoteHostAddr = internetIpIfaces.GetAddress(1);
591
592 // Set the default gateway for the UEs
593 std::cout << " default gateway\n";
594 for (auto ue = ueNodes.Begin(); ue != ueNodes.End(); ++ue)
595 {
596 Ptr<Ipv4StaticRouting> ueStaticRouting =
597 ipv4RoutingHelper.GetStaticRouting((*ue)->GetObject<Ipv4>());
598 ueStaticRouting->SetDefaultRoute(gatewayAddress, 1);
599 }
600
601 // attach UEs to their gNB. Try to attach them per cellId order
602 std::cout << " attach UEs to gNBs\n" << std::endl;
603 for (uint32_t ueId = 0; ueId < ueNodes.GetN(); ++ueId)
604 {
605 auto cellId = scenario->GetCellIndex(ueId);
606 Ptr<NetDevice> gnbNetDev = gnbNodes.Get(cellId)->GetDevice(0);
607 Ptr<NetDevice> ueNetDev = ueNodes.Get(ueId)->GetDevice(0);
608 if (lteHelper != nullptr)
609 {
610 lteHelper->Attach(ueNetDev, gnbNetDev);
611 }
612 else if (nrHelper != nullptr)
613 {
614 nrHelper->AttachToGnb(ueNetDev, gnbNetDev);
615 auto uePhyBwp0{nrHelper->GetUePhy(ueNetDev, 0)};
616 auto gnbPhyBwp0{nrHelper->GetGnbPhy(gnbNetDev, 0)};
617 Vector gnbpos = gnbNetDev->GetNode()->GetObject<MobilityModel>()->GetPosition();
618 Vector uepos = ueNetDev->GetNode()->GetObject<MobilityModel>()->GetPosition();
619 double distance = CalculateDistance(gnbpos, uepos);
620 std::cout << "ueId " << ueId << ", cellIndex " << cellId << ", ue freq "
621 << uePhyBwp0->GetCentralFrequency() / 1e9 << ", gnb freq "
622 << gnbPhyBwp0->GetCentralFrequency() / 1e9 << ", sector "
623 << scenario->GetSectorIndex(cellId) << ", distance " << distance
624 << ", azimuth gnb->ue:"
625 << RadiansToDegrees(Angles(gnbpos, uepos).GetAzimuth()) << std::endl;
626 }
627 }
628
629 /*
630 * Traffic part. Install two kind of traffic: low-latency and voice, each
631 * identified by a particular source port.
632 */
633 std::cout << " server factory\n";
634 uint16_t dlPortLowLat = 1234;
635
636 ApplicationContainer serverApps;
637
638 // The sink will always listen to the specified ports
639 UdpServerHelper dlPacketSinkLowLat(dlPortLowLat);
640
641 // The server, that is the application which is listening, is installed in the UE
642 if (params.direction == "DL")
643 {
644 serverApps.Add(dlPacketSinkLowLat.Install(ueNodes));
645 }
646 else
647 {
648 serverApps.Add(dlPacketSinkLowLat.Install(remoteHost));
649 }
650
651 // start UDP server
652 serverApps.Start(params.udpAppStartTime);
653
654 /*
655 * Configure attributes for the different generators, using user-provided
656 * parameters for generating a CBR traffic
657 *
658 * Low-Latency configuration and object creation:
659 */
660 Time interval = Seconds(1.0 / lambda);
661 std::cout << " client factory:"
662 << "\n packet size: " << udpPacketSize << "\n interval: " << interval
663 << "\n max packets: " << packetCount << std::endl;
664
665 UdpClientHelper dlClientLowLat;
666 dlClientLowLat.SetAttribute("MaxPackets", UintegerValue(packetCount));
667 dlClientLowLat.SetAttribute("PacketSize", UintegerValue(udpPacketSize));
668 dlClientLowLat.SetAttribute("Interval", TimeValue(interval));
669
670 /*
671 * Let's install the applications!
672 */
673 std::cout << " applications\n";
674 ApplicationContainer clientApps;
675 Ptr<UniformRandomVariable> startRng = CreateObject<UniformRandomVariable>();
676 startRng->SetStream(RngSeedManager::GetRun());
677 Time maxStartTime;
678
679 for (uint32_t ueId = 0; ueId < ueNodes.GetN(); ++ueId)
680 {
681 auto cellId = scenario->GetCellIndex(ueId);
682 auto sector = scenario->GetSectorIndex(cellId);
683 auto siteId = scenario->GetSiteIndex(cellId);
684 Ptr<Node> node = ueNodes.Get(ueId);
685 Ptr<NetDevice> dev = ueNetDevs.Get(ueId);
686 Address addr = ueIpIfaces.GetAddress(ueId);
687
688 std::cout << "app for ue " << ueId << ", cellId " << cellId << ", sector " << sector
689 << ", siteId " << siteId;
690 // << ":" << std::endl;
691
692 auto app = InstallApps(node,
693 dev,
694 addr,
695 params.direction,
696 &dlClientLowLat,
697 remoteHost,
698 remoteHostAddr,
699 params.udpAppStartTime,
700 dlPortLowLat,
701 startRng,
702 params.appGenerationTime,
703 lteHelper,
704 nrHelper);
705 maxStartTime = std::max(app.second, maxStartTime);
706 clientApps.Add(app.first);
707 }
708 std::cout << clientApps.GetN() << " apps\n";
709
710 // enable the traces provided by the nr module
711 std::cout << " tracing\n";
712 if (params.traces)
713 {
714 if (lteHelper != nullptr)
715 {
716 lteHelper->EnableTraces();
717 }
718 else if (nrHelper != nullptr)
719 {
720 nrHelper->EnableTraces();
721 }
722 }
723
724 std::cout << " flowmon\n";
725 FlowMonitorHelper flowmonHelper;
726 NodeContainer endpointNodes;
727 endpointNodes.Add(remoteHost);
728 endpointNodes.Add(ueNodes);
729
730 Ptr<FlowMonitor> monitor = flowmonHelper.Install(endpointNodes);
731 monitor->SetAttribute("DelayBinWidth", DoubleValue(0.001));
732 monitor->SetAttribute("JitterBinWidth", DoubleValue(0.001));
733 monitor->SetAttribute("PacketSizeBinWidth", DoubleValue(20));
734
735 std::string tableName = "e2e";
736
737 Ptr<NrRadioEnvironmentMapHelper>
738 remHelper; // Must be placed outside of block "if (generateRem)" because otherwise it gets
739 // destroyed, and when simulation starts the object does not exist anymore, but
740 // the scheduled REM events do (exist). So, REM events would be called with
741 // invalid pointer to remHelper ...
742
743 if (params.dlRem || params.ulRem)
744 {
745 std::cout << " rem helper\n";
746
747 uint16_t remPhyIndex = 0;
748 if (params.operationMode == "FDD" && params.direction == "UL")
749 {
750 remPhyIndex = 1;
751 }
752
753 NetDeviceContainer remNd;
754 Ptr<NetDevice> remDevice;
755
756 // params.ulRem:
757 std::vector<NetDeviceContainer*> remNdBySector{ueNdBySector};
758 std::vector<NetDeviceContainer*> remDevBySector{gnbNdBySector};
759
760 if (params.dlRem)
761 {
762 remNdBySector = gnbNdBySector;
763 remDevBySector = ueNdBySector;
764 }
765
766 uint32_t sectorIndex = 0;
767 // Reverse order so we get sector 1 for the remSector == 0 case
768 for (uint32_t sector = sectors; sector > 0; --sector)
769 {
770 if (params.remSector == sector || params.remSector == 0)
771 {
772 sectorIndex = sector - 1;
773 remNd.Add(*remNdBySector[sectorIndex]);
774 remDevice = remDevBySector[sectorIndex]->Get(0);
775 }
776 }
777
778 if (params.ulRem)
779 {
780 auto antArray =
781 DynamicCast<NrGnbNetDevice>(remDevice)->GetPhy(0)->GetSpectrumPhy()->GetAntenna();
782 auto antenna = DynamicCast<UniformPlanarArray>(antArray);
783 antenna->SetAttribute("AntennaElement",
784 PointerValue(CreateObject<IsotropicAntennaModel>()));
785 }
786
787 // Radio Environment Map Generation for ccId 0
788 remHelper = CreateObject<NrRadioEnvironmentMapHelper>();
789 remHelper->SetMinX(params.xMinRem);
790 remHelper->SetMaxX(params.xMaxRem);
791 remHelper->SetResX(params.xResRem);
792 remHelper->SetMinY(params.yMinRem);
793 remHelper->SetMaxY(params.yMaxRem);
794 remHelper->SetResY(params.yResRem);
795 remHelper->SetZ(params.zRem);
796
797 // save beamforming vectors, one per site (?)
798 for (uint32_t sector = sectors; sector > 0; --sector)
799 {
800 if ((params.remSector == sector) || (params.remSector == 0))
801 {
802 sectorIndex = sector - 1;
803 for (uint32_t siteId = 0; siteId < gnbSites; ++siteId)
804 {
805 gnbNdBySector[sectorIndex]
806 ->Get(siteId)
807 ->GetObject<NrGnbNetDevice>()
808 ->GetPhy(remPhyIndex)
809 ->ChangeBeamformingVector(
810 DynamicCast<NrUeNetDevice>(ueNdBySector[sectorIndex]->Get(siteId)));
811 }
812 }
813 }
814
815 remHelper->CreateRem(remNd, remDevice, remPhyIndex);
816 }
817
818 std::cout << "\n----------------------------------------\n"
819 << "Start simulation" << std::endl;
820 // Add some extra time for the last generated packets to be received
821 const Time appStopWindow = MilliSeconds(50);
822 Time stopTime = maxStartTime + params.appGenerationTime + appStopWindow;
823 Simulator::Stop(stopTime);
824 Simulator::Run();
825
826 sinrStats.EmptyCache();
827 ueTxPowerStats.EmptyCache();
828 gnbRxPowerStats.EmptyCache();
829 slotStats.EmptyCache();
830 rbStats.EmptyCache();
831
832 /*
833 * To check what was installed in the memory, i.e., BWPs of gNB Device, and its configuration.
834 * Example is: Node 1 -> Device 0 -> BandwidthPartMap -> {0,1} BWPs -> NrGnbPhy -> Numerology,
835 GtkConfigStore config;
836 config.ConfigureAttributes ();
837 */
838
839 FlowMonitorOutputStats flowMonStats;
840 flowMonStats.SetDb(&db, tableName);
841 flowMonStats.Save(monitor, flowmonHelper, params.outputDir + "/" + params.simTag);
842
843 std::cout << "\n----------------------------------------\n"
844 << "End simulation" << std::endl;
845
846 Simulator::Destroy();
847}
848
849std::ostream&
850operator<<(std::ostream& os, const Parameters& parameters)
851{
852 // Use p as shorthand for arg parametersx
853 auto p{parameters};
854
855#define MSG(m) \
856 os << "\n" << m << std::left << std::setw(40 - strlen(m)) << (strlen(m) > 0 ? ":" : "")
857
858 MSG("LENA LTE Scenario Parameters");
859 MSG("");
860 MSG("Model version") << p.simulator << (p.simulator == "LENA" ? " (v1)" : " (v2)");
861 if (p.simulator == "5GLENA")
862 {
863 MSG("LTE Standard") << p.radioNetwork << (p.radioNetwork == "LTE" ? " (4G)" : " (5G NR)");
864 MSG("4G-NR calibration mode") << (p.calibration ? "ON" : "off");
865 MSG("4G-NR ULPC mode") << (p.enableUlPc ? "Enabled" : "Disabled");
866 MSG("Operation mode") << p.operationMode;
867 if (p.operationMode == "TDD")
868 {
869 MSG("Numerology") << p.numerologyBwp;
870 MSG("TDD pattern") << p.pattern;
871 }
872 if (!p.errorModel.empty())
873 {
874 MSG("Error model") << p.errorModel;
875 }
876 else if (p.radioNetwork == "LTE")
877 {
878 MSG("Error model") << "ns3::LenaErrorModel";
879 }
880 else if (p.radioNetwork == "NR")
881 {
882 MSG("Error model") << "ns3::NrEesmCcT2";
883 }
884 }
885 else
886 {
887 // LENA v1
888 p.operationMode = "FDD";
889 MSG("LTE Standard") << "4G";
890 MSG("Calibration mode") << (p.calibration ? "ON" : "off");
891 MSG("LTE ULPC mode") << (p.enableUlPc ? "Enabled" : "Disabled");
892 MSG("Operation mode") << p.operationMode;
893 }
894
895 if (!p.baseStationFile.empty() and p.useSiteFile)
896 {
897 MSG("Base station positions") << "read from file " << p.baseStationFile;
898 }
899 else
900 {
901 MSG("Base station positions") << "regular hexaonal lay down";
902 MSG("Number of rings") << p.numOuterRings;
903 }
904
905 MSG("");
906 MSG("Channel bandwidth") << p.bandwidthMHz << " MHz";
907 MSG("Spectrum configuration") << (p.freqScenario == 0 ? "non-" : "") << "overlapping";
908 MSG("LTE Scheduler") << p.scheduler;
909
910 MSG("");
911 MSG("Basic scenario") << p.scenario;
912 if (p.scenario == "UMa")
913 {
914 os << "\n (ISD: 1.7 km, BS: 30 m, UE: 1.5 m, UE-BS min: 30.2 m)";
915 }
916 else if (p.scenario == "UMi")
917 {
918 os << "\n (ISD: 0.5 km, BS: 10 m, UE: 1.5 m, UE-BS min: 10 m)";
919 }
920 else if (p.scenario == "RMa")
921 {
922 os << "\n (ISD: 7.0 km, BS: 45 m, UE: 1.5 m, UE-BS min: 44.6 m)";
923 }
924 else
925 {
926 os << "\n (unknown configuration)";
927 }
928 if (p.baseStationFile.empty() and p.useSiteFile)
929 {
930 MSG("Number of outer rings") << p.numOuterRings;
931 }
932 MSG("Number of UEs per sector") << p.ueNumPergNb;
933 MSG("Antenna downtilt angle (deg)") << p.downtiltAngle;
934
935 MSG("");
936 MSG("Network loading") << p.trafficScenario;
937 switch (p.trafficScenario)
938 {
939 case 0:
940 MSG(" Max loading (80 Mbps/20 MHz)");
941 MSG(" Number of packets") << "infinite";
942 MSG(" Packet size");
943 switch (p.bandwidthMHz)
944 {
945 case 20:
946 os << "1000 bytes";
947 break;
948 case 10:
949 os << "500 bytes";
950 break;
951 case 5:
952 os << "250 bytes";
953 break;
954 default:
955 os << "1000 bytes";
956 }
957 // 1 s / (10000 / nUes)
958 MSG(" Inter-packet interval (per UE)") << p.ueNumPergNb / 10.0 << " ms";
959 break;
960
961 case 1:
962 MSG(" Latency");
963 MSG(" Number of packets") << 1;
964 MSG(" Packet size") << "12 bytes";
965 MSG(" Inter-packet interval (per UE)") << "1 s";
966 break;
967
968 case 2:
969 MSG(" Moderate loading");
970 MSG(" Number of packets") << "infinite";
971 MSG(" Packet size");
972 switch (p.bandwidthMHz)
973 {
974 case 20:
975 os << "125 bytes";
976 break;
977 case 10:
978 os << "63 bytes";
979 break;
980 case 5:
981 os << "32 bytes";
982 break;
983 default:
984 os << "125 bytes";
985 }
986 // 1 s / (1000 / nUes)
987 MSG(" Inter-packet interval (per UE)") << 1 / (1000 / p.ueNumPergNb) << " s";
988
989 break;
990
991 case 3:
992 MSG(" Moderate-high loading");
993 MSG(" Number of packets") << "infinite";
994 MSG(" Packet size");
995 switch (p.bandwidthMHz)
996 {
997 case 20:
998 os << "250 bytes";
999 break;
1000 case 10:
1001 os << "125 bytes";
1002 break;
1003 case 5:
1004 os << "75 bytes";
1005 break;
1006 default:
1007 os << "250 bytes";
1008 }
1009 // 1 s / (10000 / nUes)
1010 MSG(" Inter-packet interval (per UE)") << 1 / (10000.0 / p.ueNumPergNb) << " s";
1011
1012 break;
1013 default:
1014 os << "\n (Unknown configuration)";
1015 }
1016
1017 MSG("Application start window")
1018 << p.udpAppStartTime.As(Time::MS) << " + " << appStartWindow.As(Time::MS);
1019 MSG("Application on duration") << p.appGenerationTime.As(Time::MS);
1020 MSG("Traffic direction") << p.direction;
1021
1022 MSG("");
1023 MSG("Output file name") << p.simTag;
1024 MSG("Output directory") << p.outputDir;
1025 MSG("Logging") << (p.logging ? "ON" : "off");
1026 MSG("Trace file generation") << (p.traces ? "ON" : "off");
1027 MSG("");
1028 MSG("Radio environment map") << (p.dlRem ? "DL" : (p.ulRem ? "UL" : "off"));
1029 if (p.dlRem || p.ulRem)
1030 {
1031 MSG(" Sector to sample");
1032 if (p.remSector == 0)
1033 {
1034 os << "all";
1035 }
1036 else
1037 {
1038 os << p.remSector;
1039 }
1040 MSG(" X range") << p.xMinRem << " - " << p.xMaxRem << ", in " << p.xResRem << " m steps";
1041 MSG(" Y range") << p.yMinRem << " - " << p.yMaxRem << ", in " << p.yResRem << " m steps";
1042 MSG(" Altitude (Z)") << p.zRem << " m";
1043 }
1044
1045 os << std::endl;
1046 return os;
1047}
1048
1049} // namespace ns3
static void SetLenaV2SimulatorParameters(const double sector0AngleRad, const std::string &scenario, const std::string &confType, const std::string &radioNetwork, std::string errorModel, const std::string &operationMode, const std::string &direction, uint16_t numerology, const std::string &pattern, const NodeContainer &gnbSector1Container, const NodeContainer &gnbSector2Container, const NodeContainer &gnbSector3Container, const NodeContainer &ueSector1Container, const NodeContainer &ueSector2Container, const NodeContainer &ueSector3Container, const Ptr< NrPointToPointEpcHelper > &baseEpcHelper, Ptr< NrHelper > &nrHelper, NetDeviceContainer &gnbSector1NetDev, NetDeviceContainer &gnbSector2NetDev, NetDeviceContainer &gnbSector3NetDev, NetDeviceContainer &ueSector1NetDev, NetDeviceContainer &ueSector2NetDev, NetDeviceContainer &ueSector3NetDev, bool enableFading, bool enableUlPc, std::string powerAllocation, SinrOutputStats *sinrStats, PowerOutputStats *ueTxPowerStats, PowerOutputStats *gnbRxPowerStats, SlotOutputStats *slotStats, RbOutputStats *rbStats, const std::string &scheduler, uint32_t bandwidthMHz, double startingFreq, uint32_t freqScenario, double gnbTxPower, double ueTxPower, double downtiltAngle, const uint32_t gnbNumRows, const uint32_t gnbNumColumns, const uint32_t ueNumRows, const uint32_t ueNumColumns, bool gnbEnable3gppElement, bool ueEnable3gppElement, const double gnbHSpacing, const double gnbVSpacing, const double ueHSpacing, const double ueVSpacing, const double gnbNoiseFigure, const double ueNoiseFigure, bool enableRealBF, bool enableShadowing, double o2iThreshold, double o2iLowLossThreshold, bool linkO2iConditionToAntennaHeight, bool crossPolarizedGnb, bool crossPolarizedUe, double polSlantAngleGnb1, double polSlantAngleGnb2, double polSlantAngleUe1, double polSlantAngleUe2, std::string bfMethod, uint16_t beamConfSector, double beamConfElevation, double isd, bool ueBearingAngle, double PolSlantAngleGnb, double PolSlantAngleUe, bool dualPolarizedGnb, bool dualPolarizedUe, uint8_t numVPortsGnb, uint8_t numHPortsGnb, uint8_t numVPortsUe, uint8_t numHPortsUe, bool enableMimo, NrHelper::MimoPmiParams mimoPmiParams, bool enableSubbandScheluder, bool m_subbandCqiClamping, EnumValue< NrMacSchedulerUeInfo::McsCsiSource > m_mcsCsiSource)
@ NGBR_VIDEO_TCP_DEFAULT
Non-GBR TCP-based Video (Buffered Streaming, e.g., www, e-mail...)