5G-LENA nr-v3.3-159-ga6832aa7
The 5G/NR module for the ns-3 simulator
Loading...
Searching...
No Matches
nr-test-ri-pmi.cc
Go to the documentation of this file.
1// Copyright (c) 2024 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
2//
3// SPDX-License-Identifier: GPL-2.0-only
4
5#include "ns3/basic-data-calculators.h"
6#include "ns3/boolean.h"
7#include "ns3/config.h"
8#include "ns3/double.h"
9#include "ns3/flow-monitor-helper.h"
10#include "ns3/internet-stack-helper.h"
11#include "ns3/ipv4-flow-classifier.h"
12#include "ns3/ipv4-static-routing-helper.h"
13#include "ns3/mobility-helper.h"
14#include "ns3/nr-amc.h"
15#include "ns3/nr-channel-helper.h"
16#include "ns3/nr-eps-bearer.h"
17#include "ns3/nr-gnb-net-device.h"
18#include "ns3/nr-helper.h"
19#include "ns3/nr-point-to-point-epc-helper.h"
20#include "ns3/nr-ue-net-device.h"
21#include "ns3/nr-ue-phy.h"
22#include "ns3/point-to-point-helper.h"
23#include "ns3/test.h"
24#include "ns3/udp-client-server-helper.h"
25#include "ns3/uinteger.h"
26
27#include <cmath>
28#include <iostream>
29#include <map>
30#include <string>
31
39namespace ns3
40{
41struct CqiFeedbackTraceStats
42{
43 Ptr<MinMaxAvgTotalCalculator<uint8_t>> m_ri;
44 Ptr<MinMaxAvgTotalCalculator<uint8_t>> m_mcs;
45
46 CqiFeedbackTraceStats()
47 {
48 m_ri = CreateObject<MinMaxAvgTotalCalculator<uint8_t>>();
49 m_mcs = CreateObject<MinMaxAvgTotalCalculator<uint8_t>>();
50 }
51
52 CqiFeedbackTraceStats(uint8_t rank, uint8_t mcs)
53 {
54 m_ri = CreateObject<MinMaxAvgTotalCalculator<uint8_t>>();
55 m_ri->Update(rank);
56 m_mcs = CreateObject<MinMaxAvgTotalCalculator<uint8_t>>();
57 m_mcs->Update(mcs);
58 }
59};
60
61void
62CqiFeedbackTracedCallback(std::map<uint16_t, CqiFeedbackTraceStats>* stats,
63 uint16_t rnti,
64 [[maybe_unused]] uint8_t cqi,
65 uint8_t mcs,
66 uint8_t rank)
67{
68 auto it = stats->find(rnti);
69 if (it != stats->end())
70 {
71 it->second.m_ri->Update(rank);
72 it->second.m_mcs->Update(mcs);
73 }
74 else
75 {
76 (*stats)[rnti] = CqiFeedbackTraceStats(rank, mcs);
77 }
78}
79
80std::string
81GetRiPmiTestCaseName(double distanceGnbUe,
82 std::string riSelectionTechnique,
83 double riThreshold,
84 std::string pmiSelectionTechnique)
85{
86 std::stringstream ss;
87 ss << distanceGnbUe << "-" << riSelectionTechnique << "-" << riThreshold << "-"
88 << pmiSelectionTechnique;
89 return ss.str();
90}
91
92/* Beginning of TestCttcNrMimoDemoTestCase */
93class RiPmiTestCase : public TestCase
94{
95 public:
96 RiPmiTestCase(double distanceGnbUe,
97 std::string riSelectionTechnique,
98 double riThreshold,
99 std::string pmiSelectionTechnique,
100 double throughput,
101 double latency,
102 double meanRank,
103 double meanMcs)
104 : TestCase(GetRiPmiTestCaseName(distanceGnbUe,
105 riSelectionTechnique,
106 riThreshold,
107 pmiSelectionTechnique)),
108 m_distanceGnbUe(distanceGnbUe),
109 m_riSelectionTechnique(riSelectionTechnique),
110 m_riThreshold(riThreshold),
111 m_pmiSelectionTechnique(pmiSelectionTechnique),
112 m_targetThroughput(throughput),
113 m_targetLatency(latency),
114 m_targetMeanRank(meanRank),
115 m_targetMeanMcs(meanMcs)
116 {
117 }
118
119 private:
120 void DoRun() override;
121 double m_distanceGnbUe;
122 std::string m_riSelectionTechnique;
123 double m_riThreshold;
124 std::string m_pmiSelectionTechnique;
125 double m_targetThroughput;
126 double m_targetLatency;
127 double m_targetMeanRank;
128 double m_targetMeanMcs;
129};
130
131void
132RiPmiTestCase::DoRun()
133{
134 NrHelper::AntennaParams apUe;
135 NrHelper::AntennaParams apGnb;
136 apUe.antennaElem = "ns3::ThreeGppAntennaModel";
137 apUe.nAntCols = 2;
138 apUe.nAntRows = 2;
139 apUe.nHorizPorts = 2;
140 apUe.nVertPorts = 1;
141 apUe.isDualPolarized = true;
142 apGnb.antennaElem = "ns3::ThreeGppAntennaModel";
143 apGnb.nAntCols = 4;
144 apGnb.nAntRows = 2;
145 apGnb.nHorizPorts = 2;
146 apGnb.nVertPorts = 1;
147 apGnb.isDualPolarized = true;
148 double downtiltAngleGnb = 10;
149
150 // The polarization slant angle in degrees in case of x-polarized
151 double polSlantAngleGnb = 0.0;
152 double polSlantAngleUe = 0.0;
153 // The bearing angles in degrees
154 double bearingAngleGnb = 0.0;
155 double bearingAngleUe = 180.0;
156
157 // Traffic parameters to fully saturate channel
158 uint32_t udpPacketSize = 1000;
159 Time packetInterval = NanoSeconds(40000);
160 Time udpAppStartTime = MilliSeconds(400);
161
162 // Other simulation scenario parameters
163 Time simTime = MilliSeconds(1400);
164 uint16_t numerology = 0;
165 double centralFrequency = 3.5e9;
166 double bandwidth = 10e6;
167 double txPowerGnb = 23; // dBm
168 double txPowerUe = 23; // dBm
169 uint16_t updatePeriodMs = 100;
170 std::string errorModel = "ns3::NrEesmIrT2";
171 std::string scheduler = "ns3::NrMacSchedulerTdmaRR";
172 std::string beamformingMethod = "ns3::DirectPathBeamforming";
173
174 uint32_t wbPmiUpdateIntervalMs = 10; // Wideband PMI update interval in ms
175 uint32_t sbPmiUpdateIntervalMs = 2; // Subband PMI update interval in ms
176
177 NrHelper::MimoPmiParams mimoPmiParams;
178 mimoPmiParams.rankLimit = 4;
179 mimoPmiParams.subbandSize = 4;
180 mimoPmiParams.fullSearchCb = "ns3::NrCbTypeOneSp";
181 mimoPmiParams.pmSearchMethod = m_pmiSelectionTechnique;
182 if (!m_riSelectionTechnique.empty())
183 {
184 mimoPmiParams.rankTechnique = m_riSelectionTechnique;
185 mimoPmiParams.rankThreshold = m_riThreshold;
186 }
187
188 // convert angle values into radians
189 apUe.bearingAngle = bearingAngleUe * (M_PI / 180);
190 apUe.polSlantAngle = polSlantAngleUe * (M_PI / 180);
191 apGnb.bearingAngle = bearingAngleGnb * (M_PI / 180);
192 apGnb.polSlantAngle = polSlantAngleGnb * (M_PI / 180);
193
194 Config::SetDefault("ns3::NrRlcUm::MaxTxBufferSize", UintegerValue(999999999));
195 Config::SetDefault("ns3::ThreeGppChannelModel::UpdatePeriod",
196 TimeValue(MilliSeconds(updatePeriodMs)));
197
198 uint16_t pairsToCreate = 1;
199 NodeContainer gnbContainer;
200 gnbContainer.Create(pairsToCreate);
201 NodeContainer ueContainer;
202 ueContainer.Create(pairsToCreate);
203
212 MobilityHelper mobility;
213 mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
214 Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();
215 positionAlloc->Add(Vector(0.0, 0.0, 25.0));
216 positionAlloc->Add(Vector(m_distanceGnbUe, 0.0, 1.5));
217 mobility.SetPositionAllocator(positionAlloc);
218 mobility.Install(gnbContainer.Get(0));
219 mobility.Install(ueContainer.Get(0));
220
224 Ptr<NrPointToPointEpcHelper> epcHelper = CreateObject<NrPointToPointEpcHelper>();
225 Ptr<IdealBeamformingHelper> idealBeamformingHelper = CreateObject<IdealBeamformingHelper>();
226 Ptr<NrHelper> nrHelper = CreateObject<NrHelper>();
227 nrHelper->SetBeamformingHelper(idealBeamformingHelper);
228 nrHelper->SetEpcHelper(epcHelper);
229
230 // Set the channel using the scenario, condition and channel model
231 // then enable shadowing
232 Ptr<NrChannelHelper> channelHelper = CreateObject<NrChannelHelper>();
233 channelHelper->ConfigureFactories("UMa", "LOS", "ThreeGpp");
234 channelHelper->SetPathlossAttribute("ShadowingEnabled", BooleanValue(false));
235
236 // Create and set the channel with the band
237 CcBwpCreator ccBwpCreator;
238 const uint8_t numCcPerBand = 1;
239 CcBwpCreator::SimpleOperationBandConf bandConf(centralFrequency, bandwidth, numCcPerBand);
240 OperationBandInfo band = ccBwpCreator.CreateOperationBandContiguousCc(bandConf);
241 channelHelper->AssignChannelsToBands({band});
245 nrHelper->SetDlErrorModel(errorModel);
246 nrHelper->SetUlErrorModel(errorModel);
247 nrHelper->SetGnbDlAmcAttribute("AmcModel", EnumValue(NrAmc::ErrorModel));
248 nrHelper->SetGnbUlAmcAttribute("AmcModel", EnumValue(NrAmc::ErrorModel));
249 nrHelper->SetSchedulerTypeId(TypeId::LookupByName(scheduler));
250 idealBeamformingHelper->SetAttribute("BeamformingMethod",
251 TypeIdValue(TypeId::LookupByName(beamformingMethod)));
252 // Core latency
253 epcHelper->SetAttribute("S1uLinkDelay", TimeValue(MilliSeconds(0)));
254
255 nrHelper->SetupMimoPmi(mimoPmiParams);
256 nrHelper->SetupGnbAntennas(apGnb);
257 nrHelper->SetGnbAntennaAttribute("DowntiltAngle", DoubleValue(downtiltAngleGnb * M_PI / 180.0));
258 nrHelper->SetupUeAntennas(apUe);
259
260 nrHelper->SetGnbPhyAttribute("Numerology", UintegerValue(numerology));
261 nrHelper->SetGnbPhyAttribute("TxPower", DoubleValue(txPowerGnb));
262 nrHelper->SetUePhyAttribute("TxPower", DoubleValue(txPowerUe));
263 nrHelper->SetUePhyAttribute("WbPmiUpdateInterval",
264 TimeValue(MilliSeconds(wbPmiUpdateIntervalMs)));
265 nrHelper->SetUePhyAttribute("SbPmiUpdateInterval",
266 TimeValue(MilliSeconds(sbPmiUpdateIntervalMs)));
267
268 uint32_t bwpId = 0;
269 // gNb routing between bearer type and bandwidth part
270 nrHelper->SetGnbBwpManagerAlgorithmAttribute("NGBR_LOW_LAT_EMBB", UintegerValue(bwpId));
271 // UE routing between bearer type and bandwidth part
272 nrHelper->SetUeBwpManagerAlgorithmAttribute("NGBR_LOW_LAT_EMBB", UintegerValue(bwpId));
277 allBwps = CcBwpCreator::GetAllBwps({band});
278
282 NetDeviceContainer enbNetDev = nrHelper->InstallGnbDevice(gnbContainer, allBwps);
283 NetDeviceContainer ueNetDev = nrHelper->InstallUeDevice(ueContainer, allBwps);
284
285 int64_t randomStream = 1;
286 randomStream += nrHelper->AssignStreams(enbNetDev, randomStream);
287 randomStream += nrHelper->AssignStreams(ueNetDev, randomStream);
288
289 std::map<uint16_t, CqiFeedbackTraceStats> cqiTraces;
290 for (auto it = ueNetDev.Begin(); it != ueNetDev.End(); ++it)
291 {
292 auto cqiCb = MakeBoundCallback(&CqiFeedbackTracedCallback, &cqiTraces);
293 nrHelper->GetUePhy(*it, 0)->TraceConnectWithoutContext("CqiFeedbackTrace", cqiCb);
294 }
295
296 // create the Internet and install the IP stack on the UEs
297 // get SGW/PGW and create a single RemoteHost
298 auto [remoteHost, remoteHostIpv4Address] =
299 epcHelper->SetupRemoteHost("100Gb/s", 2500, Seconds(0.000));
300
301 InternetStackHelper internet;
302 Ipv4InterfaceContainer ueIpIface;
303 internet.Install(ueContainer);
304 ueIpIface = epcHelper->AssignUeIpv4Address(NetDeviceContainer(ueNetDev));
305
306 // attach each UE to its gNB according to desired scenario
307 nrHelper->AttachToGnb(ueNetDev.Get(0), enbNetDev.Get(0));
311 uint16_t dlPort = 1234;
312 ApplicationContainer serverApps;
313 // The sink will always listen to the specified ports
314 UdpServerHelper dlPacketSink(dlPort);
315 // The server, that is the application which is listening, is installed in the UE
316 serverApps.Add(dlPacketSink.Install(ueContainer));
321 UdpClientHelper dlClient;
322 dlClient.SetAttribute("MaxPackets", UintegerValue(0xFFFFFFFF));
323 dlClient.SetAttribute("PacketSize", UintegerValue(udpPacketSize));
324 dlClient.SetAttribute("Interval", TimeValue(packetInterval));
325
326 // The bearer that will carry the traffic
327 NrEpsBearer epsBearer(NrEpsBearer::NGBR_LOW_LAT_EMBB);
328
329 // The filter for the traffic
330 Ptr<NrEpcTft> dlTft = Create<NrEpcTft>();
331 NrEpcTft::PacketFilter dlPktFilter;
332 dlPktFilter.localPortStart = dlPort;
333 dlPktFilter.localPortEnd = dlPort;
334 dlTft->Add(dlPktFilter);
335
339 ApplicationContainer clientApps;
340
341 for (uint32_t i = 0; i < ueContainer.GetN(); ++i)
342 {
343 Ptr<Node> ue = ueContainer.Get(i);
344 Ptr<NetDevice> ueDevice = ueNetDev.Get(i);
345 Address ueAddress = ueIpIface.GetAddress(i);
346
347 // The client, who is transmitting, is installed in the remote host,
348 // with destination address set to the address of the UE
349 dlClient.SetAttribute(
350 "Remote",
351 AddressValue(
352 InetSocketAddress(Ipv4Address::ConvertFrom(ueAddress), dlPort).ConvertTo()));
353 clientApps.Add(dlClient.Install(remoteHost));
354
355 // Activate a dedicated bearer for the traffic
356 nrHelper->ActivateDedicatedEpsBearer(ueDevice, epsBearer, dlTft);
357 }
358
359 // start UDP server and client apps
360 serverApps.Start(udpAppStartTime);
361 clientApps.Start(udpAppStartTime);
362 serverApps.Stop(simTime);
363 clientApps.Stop(simTime);
364
365 // enable the traces provided by the nr module
366 nrHelper->EnableTraces();
367
368 FlowMonitorHelper flowmonHelper;
369 NodeContainer endpointNodes;
370 endpointNodes.Add(remoteHost);
371 endpointNodes.Add(ueContainer);
372
373 Ptr<ns3::FlowMonitor> monitor = flowmonHelper.Install(endpointNodes);
374 monitor->SetAttribute("DelayBinWidth", DoubleValue(0.001));
375 monitor->SetAttribute("JitterBinWidth", DoubleValue(0.001));
376 monitor->SetAttribute("PacketSizeBinWidth", DoubleValue(20));
377
378 Simulator::Stop(simTime);
379 Simulator::Run();
380
381 // Print per-flow statistics
382 monitor->CheckForLostPackets();
383 Ptr<Ipv4FlowClassifier> classifier =
384 DynamicCast<Ipv4FlowClassifier>(flowmonHelper.GetClassifier());
385 FlowMonitor::FlowStatsContainer stats = monitor->GetFlowStats();
386
387 double averageFlowThroughput = 0.0;
388 double averageFlowDelay = 0.0;
389 double averageMcsForAllUes = 0.0;
390 double averageRiForAllUes = 0.0;
391 for (const auto& ue : cqiTraces)
392 {
393 averageRiForAllUes += ue.second.m_ri->getMean();
394 averageMcsForAllUes += ue.second.m_mcs->getMean();
395 }
396
397 NS_TEST_ASSERT_MSG_EQ(ueNetDev.GetN(),
398 cqiTraces.size(),
399 "Not all UEs have generated CQI feedback.");
400
401 double flowDuration = (simTime - udpAppStartTime).GetSeconds();
402 for (std::map<FlowId, FlowMonitor::FlowStats>::const_iterator i = stats.begin();
403 i != stats.end();
404 ++i)
405 {
406 if (i->second.rxPackets > 0)
407 {
408 // Measure the duration of the flow from receiver's perspective
409 averageFlowThroughput += i->second.rxBytes * 8.0 / flowDuration / 1000 / 1000;
410 averageFlowDelay += 1000 * i->second.delaySum.GetSeconds() / i->second.rxPackets;
411 }
412 }
413
414 // Tolerate results with a 1% tolerance
415 NS_TEST_EXPECT_MSG_EQ_TOL(averageFlowThroughput,
416 m_targetThroughput,
417 m_targetThroughput * 0.05,
418 "Throughput is out of the expected range");
419 NS_TEST_EXPECT_MSG_EQ_TOL(averageFlowDelay,
420 m_targetLatency,
421 m_targetLatency * 0.05,
422 "Delay is out of the expected range");
423 NS_TEST_EXPECT_MSG_EQ_TOL(averageRiForAllUes,
424 m_targetMeanRank,
425 m_targetMeanRank * 0.05,
426 "Rank is out of the expected range");
427 NS_TEST_EXPECT_MSG_EQ_TOL(averageMcsForAllUes,
428 m_targetMeanMcs,
429 m_targetMeanMcs * 0.05,
430 "MCS is out of the expected range");
431
432 Simulator::Destroy();
433}
434
435/* End of TestCttcNrMimoDemoTestCase */
436class TestRiPmiSystem : public TestSuite
437{
438 public:
439 TestRiPmiSystem()
440 : TestSuite("nr-test-ri-pmi-system", Type::SYSTEM)
441 {
442 // Fully saturated channel with 200Mbps traffic
443 // Parameters (gNB-UE distance, RI selection technique, RI threshold, PMI selection
444 // technique, expected throughput, latency, mean RI and mean MCS)
445 // clang-format off
446 AddTestCase(new RiPmiTestCase( 20, "", 0.0, "ns3::NrPmSearchFull", 133.0, 150.0, 3.1, 25.0), Duration::QUICK);
447 AddTestCase(new RiPmiTestCase(500, "", 0.0, "ns3::NrPmSearchFull", 104.0, 243.7, 2.3, 26.6), Duration::QUICK);
448 AddTestCase(new RiPmiTestCase( 20, "", 0.0, "ns3::NrPmSearchIdeal", 154.0, 71.4, 3.5, 25.3), Duration::QUICK);
449 AddTestCase(new RiPmiTestCase(500, "", 0.0, "ns3::NrPmSearchIdeal", 106.2, 205.4, 2.9, 24.0), Duration::QUICK);
450 AddTestCase(new RiPmiTestCase( 20, "SVD", 0.0, "ns3::NrPmSearchFast", 114.4, 165.9, 4.0, 17.0), Duration::QUICK);
451 AddTestCase(new RiPmiTestCase( 20, "SVD", 0.5, "ns3::NrPmSearchFast", 86.1, 291.1, 1.7, 27.0), Duration::EXTENSIVE);
452 AddTestCase(new RiPmiTestCase( 20, "SVD", 0.9, "ns3::NrPmSearchFast", 51.0, 376.5, 1.0, 27.0), Duration::EXTENSIVE);
453 AddTestCase(new RiPmiTestCase(500, "SVD", 0.0, "ns3::NrPmSearchFast", 62.4, 284.4, 4.0, 9.6), Duration::QUICK);
454 AddTestCase(new RiPmiTestCase(500, "SVD", 0.5, "ns3::NrPmSearchFast", 96.9, 250.8, 1.9, 27.0), Duration::EXTENSIVE);
455 AddTestCase(new RiPmiTestCase(500, "SVD", 0.9, "ns3::NrPmSearchFast", 53.0, 400.3, 1.1, 27.0), Duration::EXTENSIVE);
456 AddTestCase(new RiPmiTestCase( 20, "WaterFilling", 10.0, "ns3::NrPmSearchFast", 126.4, 157.6, 3.6, 20.5), Duration::QUICK);
457 AddTestCase(new RiPmiTestCase( 20, "WaterFilling", 50.0, "ns3::NrPmSearchFast", 128.2, 156.9, 3.3, 23.0), Duration::EXTENSIVE);
458 AddTestCase(new RiPmiTestCase( 20, "WaterFilling", 75.0, "ns3::NrPmSearchFast", 129.5, 155.7, 3.1, 24.5), Duration::EXTENSIVE);
459 AddTestCase(new RiPmiTestCase( 20, "WaterFilling", 90.0, "ns3::NrPmSearchFast", 129.5, 155.7, 3.1, 24.5), Duration::EXTENSIVE);
460 AddTestCase(new RiPmiTestCase(500, "WaterFilling", 10.0, "ns3::NrPmSearchFast", 92.0, 282.9, 3.1, 18.5), Duration::QUICK);
461 AddTestCase(new RiPmiTestCase(500, "WaterFilling", 50.0, "ns3::NrPmSearchFast", 99.8, 268.1, 2.3, 24.7), Duration::QUICK);
462 AddTestCase(new RiPmiTestCase(500, "WaterFilling", 75.0, "ns3::NrPmSearchFast", 101.8, 260.8, 2.2, 27.0), Duration::EXTENSIVE);
463 AddTestCase(new RiPmiTestCase(500, "WaterFilling", 90.0, "ns3::NrPmSearchFast", 101.8, 260.8, 2.2, 27.0), Duration::EXTENSIVE);
464 AddTestCase(new RiPmiTestCase( 20, "Sasaoka", 0.0, "ns3::NrPmSearchFast", 124.3, 170.3, 3.1, 23.0), Duration::QUICK);
465 AddTestCase(new RiPmiTestCase( 20, "Sasaoka", 0.0, "ns3::NrPmSearchSasaoka", 117.6, 153.9, 3.1, 23.0), Duration::QUICK);
466 AddTestCase(new RiPmiTestCase(500, "Sasaoka", 0.0, "ns3::NrPmSearchFast", 75.9, 299.2, 3.1, 15.0), Duration::QUICK);
467 AddTestCase(new RiPmiTestCase(500, "Sasaoka", 0.0, "ns3::NrPmSearchSasaoka", 76.5, 301.5, 3.1, 15.0), Duration::QUICK);
468#ifdef PMI_MALEKI
469 // Maleki's PMI test is enabled via a conditional compilation flag due to external
470 // dependencies such as Pybind11+Pyttb presence
471 AddTestCase(new RiPmiTestCase( 20, "", 0.0, "ns3::NrPmSearchMaleki", 120.2, 169.8, 2.6, 27.0), Duration::QUICK);
472 AddTestCase(new RiPmiTestCase(500, "", 0.0, "ns3::NrPmSearchMaleki", 104.9, 242.7, 2.2, 27.0), Duration::QUICK);
473#endif
474 // clang-format on
475 }
476};
477
478static TestRiPmiSystem g_testRiPmiSystem;
479
480} // namespace ns3
static BandwidthPartInfoPtrVector GetAllBwps(const std::vector< std::reference_wrapper< OperationBandInfo > > &operationBands)
Get all the BWP pointers from the specified vector of operation bands.
@ ErrorModel
Error Model version (can use different error models, see NrErrorModel)
Definition nr-amc.h:81
@ NGBR_LOW_LAT_EMBB
Non-GBR Low Latency eMBB applications.
std::vector< std::reference_wrapper< BandwidthPartInfoPtr > > BandwidthPartInfoPtrVector
vector of unique_ptr of BandwidthPartInfo
static TestRiPmiSystem g_testRiPmiSystem
RI/PMI system tests.