A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
wifi-queue-scheduler-test.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2026 Universita' degli Studi di Napoli Federico II
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Author: Stefano Avallone <stavallo@unina.it>
7 */
8
9#include "ns3/ap-wifi-mac.h"
10#include "ns3/boolean.h"
11#include "ns3/log.h"
12#include "ns3/mobility-helper.h"
13#include "ns3/multi-model-spectrum-channel.h"
14#include "ns3/node-list.h"
15#include "ns3/nstime.h"
16#include "ns3/packet-socket-client.h"
17#include "ns3/packet-socket-helper.h"
18#include "ns3/packet-socket-server.h"
19#include "ns3/pointer.h"
20#include "ns3/rng-seed-manager.h"
21#include "ns3/spectrum-wifi-helper.h"
22#include "ns3/sta-wifi-mac.h"
23#include "ns3/test.h"
24#include "ns3/uinteger.h"
25#include "ns3/wifi-static-setup-helper.h"
26
27#include <algorithm>
28#include <iomanip>
29#include <list>
30#include <sstream>
31#include <vector>
32
33using namespace ns3;
34
35NS_LOG_COMPONENT_DEFINE("WifiQueueSchedulerTest");
36
37/**
38 * @ingroup wifi-test
39 * @ingroup tests
40 *
41 * A configurable number of non-AP STAs associate with an AP. All devices have the same number of
42 * links (1 or 2). The MAC queue scheduler to be installed on the devices is configurable, too. The
43 * test input is represented by a list of DL packets addressed to the non-AP STAs, along with the
44 * interval between the generation of consecutive packets. A-MPDU aggregation is disabled, so that
45 * one packet at a time is transmitted by the AP. The sequence of packets transmitted by the AP is
46 * compared to the expected sequence of packets based on the configured MAC queue scheduler.
47 */
49{
50 public:
51 /// @brief Input parameters
53 {
54 std::string schedulerTid; ///< scheduler TypeId
55 std::size_t nLinks; ///< number of links for each device
56 std::size_t nMaxInflights; ///< max number of links on which an MPDU can be
57 ///< simultaneously inflight
58 std::vector<std::pair<std::size_t, Time>> packets; ///< destination STA ID (starting at 0)
59 ///< and interval since the previous
60 ///< packet for all packets to enqueue
61 /// @return a string with the parameters' values
62 std::string ToStr() const;
63 };
64
65 /// @brief Expected results
67 {
68 std::vector<std::size_t> servedStas; ///< IDs of STAs in the order in which they are served
69 };
70
71 /**
72 * Constructor.
73 *
74 * @param params the input parameters
75 * @param results the expected parameters
76 */
77 WifiQueueSchedulerTest(const InputParams& params, const ExpectedResults& results);
78
79 private:
80 void DoSetup() override;
81 void DoRun() override;
82
83 /**
84 * Callback invoked when a FEM passes PSDUs to the PHY.
85 *
86 * @param mac the MAC transmitting the PSDUs
87 * @param phyId the ID of the PHY transmitting the PSDUs
88 * @param psduMap the PSDU map
89 * @param txVector the TX vector
90 * @param txPower the tx power
91 */
92 void Transmit(Ptr<WifiMac> mac,
93 uint8_t phyId,
94 WifiConstPsduMap psduMap,
95 WifiTxVector txVector,
96 Watt_u txPower);
97
98 /**
99 * Get an application generating packets according to given parameters.
100 *
101 * @param staId the ID of the STA the packets are addressed to
102 * @param count the number of packets to generate
103 * @param pktSize the size of the packets to generate
104 * @return an application generating the given number packets from the AP to the given STA
105 */
106 Ptr<PacketSocketClient> GetApplication(std::size_t staId,
107 std::size_t count,
108 std::size_t pktSize) const;
109
110 /**
111 * Enqueue packets according to input parameters.
112 */
113 void EnqueuePackets();
114
115 InputParams m_params; ///< input parameters
116 ExpectedResults m_expectedResults; ///< expected results
117 std::list<std::size_t> m_servedStas; ///< IDs of STAs in the order they are actually served
118 Ptr<ApWifiMac> m_apMac; ///< the AP wifi MAC
119 std::vector<Ptr<StaWifiMac>> m_staMacs; ///< the STA wifi MACs
120 std::vector<PacketSocketAddress> m_dlSockets; ///< packet socket addresses for DL traffic
121 const Time m_duration{MilliSeconds(100)}; ///< simulation duration
122};
123
124std::string
126{
127 std::stringstream ss;
128 ss << schedulerTid << ", nLinks=" << nLinks << ", nMaxInflights=" << nMaxInflights
129 << ", packets=";
130 for (const auto& [staId, interval] : packets)
131 {
132 ss << "(" << staId << ", " << interval << ")";
133 }
134 return ss.str();
135}
136
138 const ExpectedResults& results)
139 : TestCase("Test case for wifi queue scheduler " + params.ToStr()),
140 m_params(params),
141 m_expectedResults(results)
142{
143 NS_TEST_ASSERT_MSG_EQ(m_params.packets.empty(), false, "No packets to generate");
144 NS_TEST_ASSERT_MSG_EQ((m_params.nLinks == 1) || (m_params.nLinks == 2),
145 true,
146 "Unsupported number of links (" << +m_params.nLinks
147 << "), must be 1 or 2");
148}
149
150void
152{
155 int64_t streamNumber = 10;
156
157 const auto maxIt =
158 std::max_element(m_params.packets.cbegin(),
159 m_params.packets.cend(),
160 [](auto&& lhs, auto&& rhs) { return lhs.first < rhs.first; });
161 const std::size_t nStas = maxIt->first + 1;
162
163 NodeContainer wifiStaNodes(nStas);
164 NodeContainer wifiApNode(1);
165
166 WifiHelper wifi;
167 wifi.SetStandard(WIFI_STANDARD_80211be);
168 wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager");
169
172 // use default 40 MHz channel in 5 GHz band for link 0
173 phy.Set(0, "ChannelSettings", StringValue("{0, 40, BAND_5GHZ, 0}"));
174 if (m_params.nLinks == 2)
175 {
176 // use default 40 MHz channel in 6 GHz band for link 1
177 phy.Set(1, "ChannelSettings", StringValue("{0, 40, BAND_6GHZ, 0}"));
178 }
179
180 WifiMacHelper mac;
181 mac.SetMacQueueScheduler(m_params.schedulerTid);
182 mac.SetType("ns3::StaWifiMac", "Ssid", SsidValue(Ssid("scheduler-ssid")));
183
184 auto staDevices = wifi.Install(phy, mac, wifiStaNodes);
185 for (uint32_t i = 0; i < staDevices.GetN(); ++i)
186 {
187 m_staMacs.emplace_back(
188 StaticCast<StaWifiMac>(StaticCast<WifiNetDevice>(staDevices.Get(i))->GetMac()));
189 }
190
191 mac.SetType("ns3::ApWifiMac",
192 "BeaconGeneration",
193 BooleanValue(false),
194 "BE_MaxAmpduSize",
195 UintegerValue(0)); // disable A-MPDU aggregation
196 mac.SetEdca(AC_BE, "NMaxInflights", UintegerValue(m_params.nMaxInflights));
197
198 auto apDevice = wifi.Install(phy, mac, wifiApNode);
199 m_apMac = StaticCast<ApWifiMac>(StaticCast<WifiNetDevice>(apDevice.Get(0))->GetMac());
200
201 /* static setup of association and BA agreements */
202 auto apDev = DynamicCast<WifiNetDevice>(apDevice.Get(0));
203 NS_ASSERT(apDev);
205 WifiStaticSetupHelper::SetStaticBlockAck(apDev, staDevices, {0});
206
207 // Assign fixed streams to random variables in use
208 streamNumber += WifiHelper::AssignStreams(apDevice, streamNumber);
209 streamNumber += WifiHelper::AssignStreams(staDevices, streamNumber);
210
211 MobilityHelper mobility;
213
214 positionAlloc->Add(Vector(0.0, 0.0, 0.0));
215 positionAlloc->Add(Vector(1.0, 0.0, 0.0));
216 mobility.SetPositionAllocator(positionAlloc);
217
218 mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
219 mobility.Install(wifiApNode);
220 mobility.Install(wifiStaNodes);
221
222 PacketSocketHelper packetSocket;
223 packetSocket.Install(wifiApNode);
224 packetSocket.Install(wifiStaNodes);
225
226 // install a packet socket server on the STAs
227 for (uint32_t i = 0; i < wifiStaNodes.GetN(); ++i)
228 {
229 PacketSocketAddress srvAddr;
230 auto device = DynamicCast<WifiNetDevice>(wifiStaNodes.Get(i)->GetDevice(0));
231 NS_TEST_ASSERT_MSG_NE(device, nullptr, "Expected a WifiNetDevice");
232 srvAddr.SetSingleDevice(device->GetIfIndex());
233 srvAddr.SetProtocol(1);
234
235 auto server = CreateObject<PacketSocketServer>();
236 server->SetLocal(srvAddr);
237 wifiStaNodes.Get(i)->AddApplication(server);
238 server->SetStartTime(Time{0}); // now
239 server->SetStopTime(m_duration);
240 }
241
242 // set DL packet socket
243 for (const auto& staMac : m_staMacs)
244 {
245 m_dlSockets.emplace_back();
246 m_dlSockets.back().SetSingleDevice(m_apMac->GetDevice()->GetIfIndex());
247 m_dlSockets.back().SetPhysicalAddress(staMac->GetDevice()->GetAddress());
248 m_dlSockets.back().SetProtocol(1);
249 }
250
251 // Trace PSDUs passed to the PHY on all devices
252 for (auto nodeIt = NodeList::Begin(); nodeIt != NodeList::End(); ++nodeIt)
253 {
254 auto dev = DynamicCast<WifiNetDevice>((*nodeIt)->GetDevice(0));
255 for (uint8_t phyId = 0; phyId < dev->GetNPhys(); ++phyId)
256 {
257 dev->GetPhy(phyId)->TraceConnectWithoutContext(
258 "PhyTxPsduBegin",
259 MakeCallback(&WifiQueueSchedulerTest::Transmit, this).Bind(dev->GetMac(), phyId));
260 }
261 }
262}
263
264void
266 uint8_t phyId,
267 WifiConstPsduMap psduMap,
268 WifiTxVector txVector,
269 double txPowerW)
270{
271 auto linkId = mac->GetLinkForPhy(phyId);
272 NS_TEST_ASSERT_MSG_EQ(linkId.has_value(), true, "No link found for PHY ID " << +phyId);
273
274 for (const auto& [aid, psdu] : psduMap)
275 {
276 std::stringstream ss;
277 ss << std::setprecision(10) << " Link ID " << +linkId.value() << " Phy ID " << +phyId
278 << " #MPDUs " << psdu->GetNMpdus();
279 for (auto it = psdu->begin(); it != psdu->end(); ++it)
280 {
281 ss << "\n" << **it;
282 }
283 NS_LOG_INFO(ss.str());
284 }
285 NS_LOG_INFO("TXVECTOR = " << txVector << " " << mac << " " << m_apMac << "\n");
286
287 const auto psdu = psduMap.cbegin()->second;
288 const auto addr1 = psdu->GetAddr1();
289
290 if ((mac != m_apMac) || addr1.IsBroadcast())
291 {
292 return;
293 }
294
296 m_expectedResults.servedStas.size(),
297 "Served more STAs than expected");
298
299 auto expectedServedSta = m_expectedResults.servedStas.at(m_servedStas.size());
300
301 NS_TEST_ASSERT_MSG_LT(expectedServedSta,
302 m_staMacs.size(),
303 "Expected STA ID exceeds the number of STAs");
304
305 std::size_t actualServedSta{0};
306
307 for (std::size_t id = 0; id < m_staMacs.size(); ++id)
308 {
309 if (m_staMacs[id]->GetLinkIdByAddress(addr1))
310 {
311 actualServedSta = id;
312 break;
313 }
314 }
315
316 NS_TEST_ASSERT_MSG_NE(actualServedSta,
317 m_staMacs.size(),
318 "Receiver address (" << addr1 << ") does not belong to any STA");
319
320 NS_TEST_EXPECT_MSG_EQ(actualServedSta,
321 expectedServedSta,
322 "Incorrect STA served at time " << Simulator::Now());
323
324 m_servedStas.emplace_back(actualServedSta);
325}
326
329 std::size_t count,
330 std::size_t pktSize) const
331{
332 auto client = CreateObject<PacketSocketClient>();
333 client->SetAttribute("PacketSize", UintegerValue(pktSize));
334 client->SetAttribute("MaxPackets", UintegerValue(count));
335 client->SetAttribute("Interval", TimeValue(Time{0}));
336 client->SetRemote(m_dlSockets[staId]);
337 client->SetStartTime(Time{0}); // now
338 client->SetStopTime(m_duration - Simulator::Now());
339
340 return client;
341}
342
343void
345{
346 for (Time delayTot; const auto& [staId, delay] : m_params.packets)
347 {
348 delayTot += delay;
349 Simulator::Schedule(delayTot,
351 m_apMac->GetDevice()->GetNode(),
352 GetApplication(staId, 1, 1000));
353 }
354}
355
356void
358{
360
363
365 m_expectedResults.servedStas.size(),
366 "Not all the expected STAs were served");
367
369}
370
371/**
372 * @ingroup wifi-test
373 * @ingroup tests
374 *
375 * @brief wifi queue scheduler Test Suite
376 */
378{
379 public:
381};
382
384 : TestSuite("wifi-queue-scheduler", Type::UNIT)
385{
386 using ParamsResultsList = std::initializer_list<
387 std::pair<WifiQueueSchedulerTest::InputParams, WifiQueueSchedulerTest::ExpectedResults>>;
388
389 for (std::size_t nLinks = 1; nLinks <= 2; ++nLinks)
390 {
391 for (const auto& [params, results] : ParamsResultsList{
392 // with FCFS scheduler, packets are transmitted in the same order they are enqueued
393 {{.schedulerTid = "ns3::FcfsWifiQueueScheduler",
394 .nLinks = nLinks,
395 .nMaxInflights = 1,
396 .packets = {{0, Time{0}},
397 {0, Time{0}},
398 {1, Time{0}},
399 {1, Time{0}},
400 {2, Time{0}},
401 {2, Time{0}}}},
402 {.servedStas = {0, 0, 1, 1, 2, 2}}},
403 // packets are enqueued at intervals of 10us, same result
404 {{.schedulerTid = "ns3::FcfsWifiQueueScheduler",
405 .nLinks = nLinks,
406 .nMaxInflights = 1,
407 .packets = {{0, Time{0}},
408 {0, MicroSeconds(10)},
409 {1, MicroSeconds(10)},
410 {1, MicroSeconds(10)},
411 {2, MicroSeconds(10)},
412 {2, MicroSeconds(10)}}},
413 {.servedStas = {0, 0, 1, 1, 2, 2}}},
414 // with nLinks and nMaxInflights equal to 2, the FCFS scheduler transmits every
415 // packet twice because the STA sending a packet on the first link has the highest
416 // priority when transmitting on the second link
417 {{.schedulerTid = "ns3::FcfsWifiQueueScheduler",
418 .nLinks = nLinks,
419 .nMaxInflights = 2,
420 .packets = {{0, Time{0}}, {1, Time{0}}, {2, Time{0}}, {3, Time{0}}}},
421 {.servedStas = {0, 0, 1, 1, 2, 2, 3, 3}}},
422 // with RR scheduler, STAs are served in a round robin fashion
423 {{.schedulerTid = "ns3::RrWifiQueueScheduler",
424 .nLinks = nLinks,
425 .nMaxInflights = 1,
426 .packets = {{0, Time{0}},
427 {0, Time{0}},
428 {1, Time{0}},
429 {1, Time{0}},
430 {2, Time{0}},
431 {2, Time{0}}}},
432 {.servedStas = {0, 1, 2, 0, 1, 2}}},
433 // packets are enqueued at intervals of 10us, same result
434 {{.schedulerTid = "ns3::RrWifiQueueScheduler",
435 .nLinks = nLinks,
436 .nMaxInflights = 1,
437 .packets = {{0, Time{0}},
438 {0, MicroSeconds(10)},
439 {1, MicroSeconds(10)},
440 {1, MicroSeconds(10)},
441 {2, MicroSeconds(10)},
442 {2, MicroSeconds(10)}}},
443 {.servedStas = {0, 1, 2, 0, 1, 2}}},
444 // with nLinks and nMaxInflights equal to 2, the RR scheduler transmits every
445 // packet just once because the STA sending a packet on the first link has the
446 // lowest priority when transmitting on the second link
447 {{.schedulerTid = "ns3::RrWifiQueueScheduler",
448 .nLinks = nLinks,
449 .nMaxInflights = 2,
450 .packets = {{0, Time{0}}, {1, Time{0}}, {2, Time{0}}, {3, Time{0}}}},
451 {.servedStas = {0, 1, 2, 3}}},
452 })
453 {
454 if (params.nLinks < 2 && params.nMaxInflights > 1)
455 {
456 continue;
457 }
458
460 }
461 }
462}
463
A configurable number of non-AP STAs associate with an AP.
std::vector< PacketSocketAddress > m_dlSockets
packet socket addresses for DL traffic
void DoRun() override
Implementation to actually run this TestCase.
Ptr< ApWifiMac > m_apMac
the AP wifi MAC
InputParams m_params
input parameters
void DoSetup() override
Implementation to do any local setup required for this TestCase.
void EnqueuePackets()
Enqueue packets according to input parameters.
std::vector< Ptr< StaWifiMac > > m_staMacs
the STA wifi MACs
Ptr< PacketSocketClient > GetApplication(std::size_t staId, std::size_t count, std::size_t pktSize) const
Get an application generating packets according to given parameters.
ExpectedResults m_expectedResults
expected results
const Time m_duration
simulation duration
std::list< std::size_t > m_servedStas
IDs of STAs in the order they are actually served.
void Transmit(Ptr< WifiMac > mac, uint8_t phyId, WifiConstPsduMap psduMap, WifiTxVector txVector, Watt_u txPower)
Callback invoked when a FEM passes PSDUs to the PHY.
WifiQueueSchedulerTest(const InputParams &params, const ExpectedResults &results)
Constructor.
wifi queue scheduler Test Suite
Helper class used to assign positions and mobility models to nodes.
keep track of a set of node pointers.
uint32_t AddApplication(Ptr< Application > application)
Associate an Application to this Node.
Definition node.cc:153
static Iterator Begin()
Definition node-list.cc:226
static Iterator End()
Definition node-list.cc:233
an address for a packet socket
void SetProtocol(uint16_t protocol)
Set the protocol.
void SetSingleDevice(uint32_t device)
Set the address to match only a specified NetDevice.
Give ns3::PacketSocket powers to ns3::Node.
void Install(Ptr< Node > node) const
Aggregate an instance of a ns3::PacketSocketFactory onto the provided node.
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
static void Stop()
Tell the Simulator the calling event should be the last one executed.
Definition simulator.cc:169
Make it easy to create and manage PHY objects for the spectrum model.
The IEEE 802.11 SSID Information Element.
Definition ssid.h:25
Hold variables of type string.
Definition string.h:45
void AddTestCase(TestCase *testCase, Duration duration=Duration::QUICK)
Add an individual child TestCase to this test suite.
Definition test.cc:296
@ QUICK
Fast test.
Definition test.h:1057
TestCase(const TestCase &)=delete
Caller graph was not generated because of its size.
Type
Type of test.
Definition test.h:1271
TestSuite(std::string name, Type type=Type::UNIT)
Construct a new test suite.
Definition test.cc:494
Simulation virtual time values and global simulation resolution.
Definition nstime.h:95
Hold an unsigned integer type.
Definition uinteger.h:34
helps to create WifiNetDevice objects
static int64_t AssignStreams(NetDeviceContainer c, int64_t stream)
Assign a fixed random variable stream number to the random variables used by the PHY and MAC aspects ...
create MAC layers for a ns3::WifiNetDevice.
static void SetStaticAssociation(Ptr< WifiNetDevice > bssDev, const NetDeviceContainer &clientDevs)
Bypass static capabilities exchange for input devices.
static void SetStaticBlockAck(Ptr< WifiNetDevice > apDev, const NetDeviceContainer &clientDevs, const std::set< tid_t > &tids, std::optional< Mac48Address > gcrGroupAddr=std::nullopt)
Bypass ADDBA Request-Response exchange sequence between AP and STAs for given TIDs.
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition assert.h:55
Callback< R, Args... > MakeCallback(R(T::*memPtr)(Args...), OBJ objPtr)
Build Callbacks for class method members which take varying numbers of arguments and potentially retu...
Definition callback.h:690
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:194
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition log.h:267
Ptr< T > CreateObject(Args &&... args)
Create an object by type, with varying number of constructor parameters.
Definition object.h:627
#define NS_TEST_ASSERT_MSG_LT(actual, limit, msg)
Test that an actual value is less than a limit and report and abort if not.
Definition test.h:698
#define NS_TEST_ASSERT_MSG_EQ(actual, limit, msg)
Test that an actual and expected (limit) value are equal and report and abort if not.
Definition test.h:133
#define NS_TEST_EXPECT_MSG_EQ(actual, limit, msg)
Test that an actual and expected (limit) value are equal and report if not.
Definition test.h:240
#define NS_TEST_ASSERT_MSG_NE(actual, limit, msg)
Test that an actual and expected (limit) value are not equal and report and abort if not.
Definition test.h:553
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1307
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1290
@ WIFI_STANDARD_80211be
@ AC_BE
Best Effort.
Definition qos-utils.h:66
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:643
Ptr< T1 > StaticCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:650
std::unordered_map< uint16_t, Ptr< const WifiPsdu > > WifiConstPsduMap
Map of const PSDUs indexed by STA-ID.
Definition wifi-ppdu.h:38
double Watt_u
Watt weak type.
Definition wifi-units.h:25
std::vector< std::size_t > servedStas
IDs of STAs in the order in which they are served.
std::size_t nLinks
number of links for each device
std::size_t nMaxInflights
max number of links on which an MPDU can be simultaneously inflight
std::vector< std::pair< std::size_t, Time > > packets
destination STA ID (starting at 0) and interval since the previous packet for all packets to enqueue
static WifiQueueSchedulerTestSuite g_wifiQueueSchedulerTestSuite
the test suite