A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
wifi-emlsr-test.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2023 Universita' degli Studi di Napoli Federico II
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation;
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 *
17 * Author: Stefano Avallone <stavallo@unina.it>
18 */
19
20#include "wifi-emlsr-test.h"
21
22#include "ns3/attribute-container.h"
23#include "ns3/boolean.h"
24#include "ns3/config.h"
25#include "ns3/ctrl-headers.h"
26#include "ns3/eht-configuration.h"
27#include "ns3/emlsr-manager.h"
28#include "ns3/he-frame-exchange-manager.h"
29#include "ns3/log.h"
30#include "ns3/mgt-action-headers.h"
31#include "ns3/mobility-helper.h"
32#include "ns3/multi-model-spectrum-channel.h"
33#include "ns3/node-list.h"
34#include "ns3/packet-socket-helper.h"
35#include "ns3/packet-socket-server.h"
36#include "ns3/qos-txop.h"
37#include "ns3/rng-seed-manager.h"
38#include "ns3/rr-multi-user-scheduler.h"
39#include "ns3/simulator.h"
40#include "ns3/spectrum-wifi-helper.h"
41#include "ns3/spectrum-wifi-phy.h"
42#include "ns3/string.h"
43#include "ns3/wifi-net-device.h"
44
45#include <algorithm>
46#include <functional>
47#include <iomanip>
48#include <optional>
49
50using namespace ns3;
51
52NS_LOG_COMPONENT_DEFINE("WifiEmlsrTest");
53
56 "Check serialization and deserialization of the EML Operating Mode Notification frame")
57{
58}
59
60void
62{
63 MgtEmlOmn frame;
64
65 // Both EMLSR Mode and EMLMR Mode subfields set to 0 (no link bitmap);
67
68 frame.m_emlControl.emlsrMode = 1;
69 frame.SetLinkIdInBitmap(0);
70 frame.SetLinkIdInBitmap(5);
71 frame.SetLinkIdInBitmap(15);
72
73 // Adding Link Bitmap
75
76 NS_TEST_EXPECT_MSG_EQ((frame.GetLinkBitmap() == std::list<uint8_t>{0, 5, 15}),
77 true,
78 "Unexpected link bitmap");
79
80 auto padding = MicroSeconds(64);
81 auto transition = MicroSeconds(128);
82
86 frame.m_emlsrParamUpdate->transitionDelay =
88
89 // Adding the EMLSR Parameter Update field
91
94 padding,
95 "Unexpected EMLSR Padding Delay");
98 transition,
99 "Unexpected EMLSR Transition Delay");
100}
101
103 : TestCase(name)
104{
105}
106
107void
109 uint8_t phyId,
110 WifiConstPsduMap psduMap,
111 WifiTxVector txVector,
112 double txPowerW)
113{
114 auto linkId = mac->GetLinkForPhy(phyId);
115 NS_TEST_ASSERT_MSG_EQ(linkId.has_value(), true, "No link found for PHY ID " << +phyId);
116 m_txPsdus.push_back({Simulator::Now(), psduMap, txVector, *linkId, phyId});
117
118 auto txDuration =
119 WifiPhy::CalculateTxDuration(psduMap, txVector, mac->GetWifiPhy(*linkId)->GetPhyBand());
120
121 for (const auto& [aid, psdu] : psduMap)
122 {
123 std::stringstream ss;
124 ss << std::setprecision(10) << "PSDU #" << m_txPsdus.size() << " Link ID "
125 << +linkId.value() << " Phy ID " << +phyId << " " << psdu->GetHeader(0).GetTypeString();
126 if (psdu->GetHeader(0).IsAction())
127 {
128 ss << " ";
129 WifiActionHeader actionHdr;
130 psdu->GetPayload(0)->PeekHeader(actionHdr);
131 actionHdr.Print(ss);
132 }
133 ss << " #MPDUs " << psdu->GetNMpdus() << " duration/ID " << psdu->GetHeader(0).GetDuration()
134 << " RA = " << psdu->GetAddr1() << " TA = " << psdu->GetAddr2()
135 << " ADDR3 = " << psdu->GetHeader(0).GetAddr3()
136 << " ToDS = " << psdu->GetHeader(0).IsToDs()
137 << " FromDS = " << psdu->GetHeader(0).IsFromDs();
138 if (psdu->GetHeader(0).IsQosData())
139 {
140 ss << " seqNo = {";
141 for (auto& mpdu : *PeekPointer(psdu))
142 {
143 ss << mpdu->GetHeader().GetSequenceNumber() << ",";
144 }
145 ss << "} TID = " << +psdu->GetHeader(0).GetQosTid();
146 }
147 NS_LOG_INFO(ss.str());
148 }
149 NS_LOG_INFO("TX duration = " << txDuration.As(Time::MS) << " TXVECTOR = " << txVector << "\n");
150}
151
152void
154{
157 int64_t streamNumber = 100;
158
159 Config::SetDefault("ns3::WifiMac::MpduBufferSize", UintegerValue(64));
160
161 NodeContainer wifiApNode(1);
162 NodeContainer wifiStaNodes(m_nEmlsrStations);
163
164 WifiHelper wifi;
165 // wifi.EnableLogComponents ();
166 wifi.SetStandard(WIFI_STANDARD_80211be);
167 wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
168 "DataMode",
169 StringValue("EhtMcs0"),
170 "ControlMode",
171 StringValue("HtMcs0"));
172 wifi.ConfigEhtOptions("EmlsrActivated",
173 BooleanValue(true),
174 "TransitionTimeout",
176
177 // MLDs are configured with three links
178 SpectrumWifiPhyHelper phyHelper(3);
180 phyHelper.Set(0, "ChannelSettings", StringValue("{2, 0, BAND_2_4GHZ, 0}"));
181 phyHelper.Set(1, "ChannelSettings", StringValue("{36, 0, BAND_5GHZ, 0}"));
182 phyHelper.Set(2, "ChannelSettings", StringValue("{1, 0, BAND_6GHZ, 0}"));
183 // Add three spectrum channels to use multi-RF interface
184 phyHelper.AddChannel(CreateObject<MultiModelSpectrumChannel>(), WIFI_SPECTRUM_2_4_GHZ);
185 phyHelper.AddChannel(CreateObject<MultiModelSpectrumChannel>(), WIFI_SPECTRUM_5_GHZ);
186 phyHelper.AddChannel(CreateObject<MultiModelSpectrumChannel>(), WIFI_SPECTRUM_6_GHZ);
187
188 WifiMacHelper mac;
189 mac.SetType("ns3::ApWifiMac",
190 "Ssid",
191 SsidValue(Ssid("ns-3-ssid")),
192 "BeaconGeneration",
193 BooleanValue(true));
194
195 NetDeviceContainer apDevice = wifi.Install(phyHelper, mac, wifiApNode);
196
197 mac.SetType("ns3::StaWifiMac",
198 "Ssid",
199 SsidValue(Ssid("wrong-ssid")),
200 "MaxMissedBeacons",
201 UintegerValue(1e6), // do not deassociate
202 "ActiveProbing",
203 BooleanValue(false));
204 mac.SetEmlsrManager("ns3::DefaultEmlsrManager",
205 "EmlsrLinkSet",
207 "MainPhyId",
209
210 NetDeviceContainer staDevices = wifi.Install(phyHelper, mac, wifiStaNodes);
211
212 m_apMac = DynamicCast<ApWifiMac>(DynamicCast<WifiNetDevice>(apDevice.Get(0))->GetMac());
213
214 for (uint32_t i = 0; i < staDevices.GetN(); i++)
215 {
216 auto device = DynamicCast<WifiNetDevice>(staDevices.Get(i));
217 auto staMac = DynamicCast<StaWifiMac>(device->GetMac());
218 NS_ASSERT_MSG(i < m_paddingDelay.size(), "Not enough padding delay values provided");
219 staMac->GetEmlsrManager()->SetAttribute("EmlsrPaddingDelay",
221 NS_ASSERT_MSG(i < m_transitionDelay.size(), "Not enough transition delay values provided");
222 staMac->GetEmlsrManager()->SetAttribute("EmlsrTransitionDelay",
224 }
225
226 if (m_nNonEmlsrStations > 0)
227 {
228 // create the other non-AP MLDs for which EMLSR is not activated
229 wifi.ConfigEhtOptions("EmlsrActivated", BooleanValue(false));
230 NodeContainer otherStaNodes(m_nNonEmlsrStations);
231 staDevices.Add(wifi.Install(phyHelper, mac, otherStaNodes));
232 wifiStaNodes.Add(otherStaNodes);
233 }
234
235 for (uint32_t i = 0; i < staDevices.GetN(); i++)
236 {
237 auto device = DynamicCast<WifiNetDevice>(staDevices.Get(i));
238 m_staMacs.push_back(DynamicCast<StaWifiMac>(device->GetMac()));
239 }
240
241 // Trace PSDUs passed to the PHY on AP MLD and non-AP MLDs
242 for (uint8_t phyId = 0; phyId < m_apMac->GetDevice()->GetNPhys(); phyId++)
243 {
245 "/NodeList/0/DeviceList/*/$ns3::WifiNetDevice/Phys/" + std::to_string(phyId) +
246 "/PhyTxPsduBegin",
248 }
249 for (std::size_t i = 0; i < m_nEmlsrStations + m_nNonEmlsrStations; i++)
250 {
251 for (uint8_t phyId = 0; phyId < m_staMacs[i]->GetDevice()->GetNPhys(); phyId++)
252 {
254 "/NodeList/" + std::to_string(i + 1) + "/DeviceList/*/$ns3::WifiNetDevice/Phys/" +
255 std::to_string(phyId) + "/PhyTxPsduBegin",
257 }
258 }
259
260 // Uncomment the lines below to write PCAP files
261 // phyHelper.EnablePcap("wifi-emlsr_AP", apDevice);
262 // phyHelper.EnablePcap("wifi-emlsr_STA", staDevices);
263
264 // Assign fixed streams to random variables in use
265 streamNumber += wifi.AssignStreams(apDevice, streamNumber);
266 streamNumber += wifi.AssignStreams(staDevices, streamNumber);
267
268 MobilityHelper mobility;
269 Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();
270
271 for (std::size_t id = 0; id <= m_nEmlsrStations + m_nNonEmlsrStations; id++)
272 {
273 // all non-AP MLDs are co-located
274 positionAlloc->Add(Vector(std::min<double>(id, 1), 0.0, 0.0));
275 }
276 mobility.SetPositionAllocator(positionAlloc);
277
278 mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
279 mobility.Install(wifiApNode);
280 mobility.Install(wifiStaNodes);
281
282 // install packet socket on all nodes
283 PacketSocketHelper packetSocket;
284 packetSocket.Install(wifiApNode);
285 packetSocket.Install(wifiStaNodes);
286
287 // install a packet socket server on all nodes
288 for (auto nodeIt = NodeList::Begin(); nodeIt != NodeList::End(); nodeIt++)
289 {
290 PacketSocketAddress srvAddr;
291 auto device = DynamicCast<WifiNetDevice>((*nodeIt)->GetDevice(0));
292 NS_TEST_ASSERT_MSG_NE(device, nullptr, "Expected a WifiNetDevice");
293 srvAddr.SetSingleDevice(device->GetIfIndex());
294 srvAddr.SetProtocol(1);
295
296 auto server = CreateObject<PacketSocketServer>();
297 server->SetLocal(srvAddr);
298 (*nodeIt)->AddApplication(server);
299 server->SetStartTime(Seconds(0)); // now
300 server->SetStopTime(m_duration);
301 }
302
303 // set DL and UL packet sockets
304 for (const auto& staMac : m_staMacs)
305 {
306 m_dlSockets.emplace_back();
307 m_dlSockets.back().SetSingleDevice(m_apMac->GetDevice()->GetIfIndex());
308 m_dlSockets.back().SetPhysicalAddress(staMac->GetDevice()->GetAddress());
309 m_dlSockets.back().SetProtocol(1);
310
311 m_ulSockets.emplace_back();
312 m_ulSockets.back().SetSingleDevice(staMac->GetDevice()->GetIfIndex());
313 m_ulSockets.back().SetPhysicalAddress(m_apMac->GetDevice()->GetAddress());
314 m_ulSockets.back().SetProtocol(1);
315 }
316
317 // schedule ML setup for one station at a time
318 m_apMac->TraceConnectWithoutContext("AssociatedSta",
320 Simulator::Schedule(Seconds(0), [&]() { m_staMacs[0]->SetSsid(Ssid("ns-3-ssid")); });
321}
322
325 std::size_t staId,
326 std::size_t count,
327 std::size_t pktSize) const
328{
329 auto client = CreateObject<PacketSocketClient>();
330 client->SetAttribute("PacketSize", UintegerValue(pktSize));
331 client->SetAttribute("MaxPackets", UintegerValue(count));
332 client->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
333 client->SetRemote(dir == DOWNLINK ? m_dlSockets.at(staId) : m_ulSockets.at(staId));
334 client->SetStartTime(Seconds(0)); // now
335 client->SetStopTime(m_duration - Simulator::Now());
336
337 return client;
338}
339
340void
342{
343 if (m_lastAid == aid)
344 {
345 // another STA of this non-AP MLD has already fired this callback
346 return;
347 }
348 m_lastAid = aid;
349
350 // wait some time (5ms) to allow the completion of association
351 auto delay = MilliSeconds(5);
352
353 if (m_establishBaDl)
354 {
355 // trigger establishment of BA agreement with AP as originator
356 Simulator::Schedule(delay, [=, this]() {
357 m_apMac->GetDevice()->GetNode()->AddApplication(
358 GetApplication(DOWNLINK, aid - 1, 4, 1000));
359 });
360
361 delay += MilliSeconds(5);
362 }
363
364 if (m_establishBaUl)
365 {
366 // trigger establishment of BA agreement with AP as recipient
367 Simulator::Schedule(delay, [=, this]() {
368 m_staMacs[aid - 1]->GetDevice()->GetNode()->AddApplication(
369 GetApplication(UPLINK, aid - 1, 4, 1000));
370 });
371
372 delay += MilliSeconds(5);
373 }
374
375 Simulator::Schedule(delay, [=, this]() {
377 {
378 // make the next STA start ML discovery & setup
379 m_staMacs[aid]->SetSsid(Ssid("ns-3-ssid"));
380 return;
381 }
382 // all stations associated; start traffic if needed
383 StartTraffic();
384 // stop generation of beacon frames in order to avoid interference
385 m_apMac->SetAttribute("BeaconGeneration", BooleanValue(false));
386 });
387}
388
389void
391 Mac48Address dest,
392 uint8_t linkId,
394 bool blocked,
395 std::string description,
396 bool testUnblockedForOtherReasons)
397{
399 auto mask = mac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, linkId);
400 NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
401 true,
402 description << ": Expected to find a mask for EMLSR link " << +linkId);
403 if (blocked)
404 {
405 NS_TEST_EXPECT_MSG_EQ(mask->test(static_cast<std::size_t>(reason)),
406 true,
407 description << ": Expected EMLSR link " << +linkId
408 << " to be blocked for reason " << reason);
409 if (testUnblockedForOtherReasons)
410 {
411 NS_TEST_EXPECT_MSG_EQ(mask->count(),
412 1,
413 description << ": Expected EMLSR link " << +linkId
414 << " to be blocked for one reason only");
415 }
416 }
417 else if (testUnblockedForOtherReasons)
418 {
419 NS_TEST_EXPECT_MSG_EQ(mask->none(),
420 true,
421 description << ": Expected EMLSR link " << +linkId
422 << " to be unblocked");
423 }
424 else
425 {
426 NS_TEST_EXPECT_MSG_EQ(mask->test(static_cast<std::size_t>(reason)),
427 false,
428 description << ": Expected EMLSR link " << +linkId
429 << " to be unblocked for reason " << reason);
430 }
431}
432
433EmlOmnExchangeTest::EmlOmnExchangeTest(const std::set<uint8_t>& linksToEnableEmlsrOn,
434 Time transitionTimeout)
435 : EmlsrOperationsTestBase("Check EML Notification exchange"),
436 m_checkEmlsrLinksCount(0),
437 m_emlNotificationDroppedCount(0)
438{
439 m_linksToEnableEmlsrOn = linksToEnableEmlsrOn;
442 m_transitionTimeout = transitionTimeout;
443 m_duration = Seconds(0.5);
444}
445
446void
448{
450
451 m_errorModel = CreateObject<ListErrorModel>();
452 for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
453 {
455 }
456
457 m_staMacs[0]->TraceConnectWithoutContext("AckedMpdu",
459 m_staMacs[0]->TraceConnectWithoutContext("DroppedMpdu",
461}
462
463void
465 uint8_t phyId,
466 WifiConstPsduMap psduMap,
467 WifiTxVector txVector,
468 double txPowerW)
469{
470 EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
471 auto linkId = m_txPsdus.back().linkId;
472
473 auto psdu = psduMap.begin()->second;
474
475 switch (psdu->GetHeader(0).GetType())
476 {
478 NS_TEST_EXPECT_MSG_EQ(+linkId, +m_mainPhyId, "AssocReq not sent by the main PHY");
479 CheckEmlCapabilitiesInAssocReq(*psdu->begin(), txVector, linkId);
480 break;
481
483 CheckEmlCapabilitiesInAssocResp(*psdu->begin(), txVector, linkId);
484 break;
485
487 if (auto [category, action] = WifiActionHeader::Peek(psdu->GetPayload(0));
489 action.protectedEhtAction ==
491 {
492 CheckEmlNotification(psdu, txVector, linkId);
493
495 m_staMacs[0]->GetLinkIdByAddress(psdu->GetAddr2()) == linkId)
496 {
497 // transmitted by non-AP MLD, we need to corrupt it
498 m_uidList.push_front(psdu->GetPacket()->GetUid());
500 }
501 break;
502 }
503
504 default:;
505 }
506}
507
508void
510 const WifiTxVector& txVector,
511 uint8_t linkId)
512{
514 mpdu->GetPacket()->PeekHeader(frame);
515
516 const auto& mle = frame.Get<MultiLinkElement>();
517 NS_TEST_ASSERT_MSG_EQ(mle.has_value(), true, "Multi-Link Element must be present in AssocReq");
518
519 NS_TEST_ASSERT_MSG_EQ(mle->HasEmlCapabilities(),
520 true,
521 "Multi-Link Element in AssocReq must have EML Capabilities");
522 NS_TEST_ASSERT_MSG_EQ(mle->IsEmlsrSupported(),
523 true,
524 "EML Support subfield of EML Capabilities in AssocReq must be set to 1");
525 NS_TEST_ASSERT_MSG_EQ(mle->GetEmlsrPaddingDelay(),
526 m_paddingDelay.at(0),
527 "Unexpected Padding Delay in EML Capabilities included in AssocReq");
528 NS_TEST_ASSERT_MSG_EQ(mle->GetEmlsrTransitionDelay(),
529 m_transitionDelay.at(0),
530 "Unexpected Transition Delay in EML Capabilities included in AssocReq");
531}
532
533void
535 const WifiTxVector& txVector,
536 uint8_t linkId)
537{
538 bool sentToEmlsrClient =
539 (m_staMacs[0]->GetLinkIdByAddress(mpdu->GetHeader().GetAddr1()) == linkId);
540
541 if (!sentToEmlsrClient)
542 {
543 // nothing to check
544 return;
545 }
546
548 mpdu->GetPacket()->PeekHeader(frame);
549
550 const auto& mle = frame.Get<MultiLinkElement>();
551 NS_TEST_ASSERT_MSG_EQ(mle.has_value(), true, "Multi-Link Element must be present in AssocResp");
552
553 NS_TEST_ASSERT_MSG_EQ(mle->HasEmlCapabilities(),
554 true,
555 "Multi-Link Element in AssocResp must have EML Capabilities");
556 NS_TEST_ASSERT_MSG_EQ(mle->IsEmlsrSupported(),
557 true,
558 "EML Support subfield of EML Capabilities in AssocResp must be set to 1");
560 mle->GetTransitionTimeout(),
562 "Unexpected Transition Timeout in EML Capabilities included in AssocResp");
563}
564
565void
567 const WifiTxVector& txVector,
568 uint8_t linkId)
569{
570 MgtEmlOmn frame;
571 auto mpdu = *psdu->begin();
572 auto pkt = mpdu->GetPacket()->Copy();
574 pkt->RemoveHeader(frame);
575 NS_LOG_DEBUG(frame);
576
577 bool sentbyNonApMld = m_staMacs[0]->GetLinkIdByAddress(mpdu->GetHeader().GetAddr2()) == linkId;
578
580 1,
581 "EMLSR Mode subfield should be set to 1 (frame sent by non-AP MLD: "
582 << std::boolalpha << sentbyNonApMld << ")");
583
585 0,
586 "EMLMR Mode subfield should be set to 0 (frame sent by non-AP MLD: "
587 << std::boolalpha << sentbyNonApMld << ")");
588
590 true,
591 "Link Bitmap subfield should be present (frame sent by non-AP MLD: "
592 << std::boolalpha << sentbyNonApMld << ")");
593
594 auto setupLinks = m_staMacs[0]->GetSetupLinkIds();
595 std::list<uint8_t> expectedEmlsrLinks;
596 std::set_intersection(setupLinks.begin(),
597 setupLinks.end(),
600 std::back_inserter(expectedEmlsrLinks));
601
602 NS_TEST_EXPECT_MSG_EQ((expectedEmlsrLinks == frame.GetLinkBitmap()),
603 true,
604 "Unexpected Link Bitmap subfield (frame sent by non-AP MLD: "
605 << std::boolalpha << sentbyNonApMld << ")");
606
607 if (!sentbyNonApMld)
608 {
609 // the frame has been sent by the AP MLD
612 0,
613 "EMLSR Parameter Update Control should be set to 0 in frames sent by the AP MLD");
614
615 // as soon as the non-AP MLD receives this frame, it sets the EMLSR links
616 auto delay = WifiPhy::CalculateTxDuration(psdu,
617 txVector,
618 m_staMacs[0]->GetWifiPhy(linkId)->GetPhyBand()) +
619 MicroSeconds(1); // to account for propagation delay
621 }
622
624 +linkId,
625 "EML Notification received on unexpected link (frame sent by non-AP MLD: "
626 << std::boolalpha << sentbyNonApMld << ")");
627}
628
629void
631{
632 const auto& hdr = mpdu->GetHeader();
633
634 if (hdr.IsMgt() && hdr.IsAction())
635 {
636 if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
638 action.protectedEhtAction ==
640 {
641 // the EML Operating Mode Notification frame that the non-AP MLD sent has been
642 // acknowledged; after the transition timeout, the EMLSR links have been set
645 this);
646 }
647 }
648}
649
650void
652{
653 const auto& hdr = mpdu->GetHeader();
654
655 if (hdr.IsMgt() && hdr.IsAction())
656 {
657 if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
659 action.protectedEhtAction ==
661 {
662 // the EML Operating Mode Notification frame has been dropped. Don't
663 // corrupt it anymore
665 }
666 }
667}
668
669void
671{
673
674 auto setupLinks = m_staMacs[0]->GetSetupLinkIds();
675 std::set<uint8_t> expectedEmlsrLinks;
676 std::set_intersection(setupLinks.begin(),
677 setupLinks.end(),
680 std::inserter(expectedEmlsrLinks, expectedEmlsrLinks.end()));
681
682 NS_TEST_EXPECT_MSG_EQ((expectedEmlsrLinks == m_staMacs[0]->GetEmlsrManager()->GetEmlsrLinks()),
683 true,
684 "Unexpected set of EMLSR links)");
685}
686
687void
689{
692
694 2,
695 "Unexpected number of times CheckEmlsrLinks() is called");
698 1,
699 "Unexpected number of times the EML Notification frame is dropped due to max retry limit");
700
702}
703
705 : EmlsrOperationsTestBase("Check EML DL TXOP transmissions (" +
706 std::to_string(params.nEmlsrStations) + "," +
707 std::to_string(params.nNonEmlsrStations) + ")"),
708 m_emlsrLinks(params.linksToEnableEmlsrOn),
709 m_emlsrEnabledTime(0),
710 m_fe2to3delay(MilliSeconds(20)),
711 m_countQoSframes(0),
712 m_countBlockAck(0)
713{
714 m_nEmlsrStations = params.nEmlsrStations;
715 m_nNonEmlsrStations = params.nNonEmlsrStations;
716 m_linksToEnableEmlsrOn = {}; // do not enable EMLSR right after association
717 m_mainPhyId = 1;
718 m_paddingDelay = params.paddingDelay;
719 m_transitionDelay = params.transitionDelay;
720 m_transitionTimeout = params.transitionTimeout;
721 m_establishBaDl = true;
722 m_duration = Seconds(1.5);
723
724 NS_ABORT_MSG_IF(params.linksToEnableEmlsrOn.size() < 2,
725 "This test requires at least two links to be configured as EMLSR links");
726}
727
728void
730 uint8_t phyId,
731 WifiConstPsduMap psduMap,
732 WifiTxVector txVector,
733 double txPowerW)
734{
735 EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
736 auto linkId = m_txPsdus.back().linkId;
737
738 auto psdu = psduMap.begin()->second;
739 auto nodeId = mac->GetDevice()->GetNode()->GetId();
740
741 switch (psdu->GetHeader(0).GetType())
742 {
744 NS_ASSERT_MSG(nodeId > 0, "APs do not send AssocReq frames");
745 if (nodeId <= m_nEmlsrStations)
746 {
747 NS_TEST_EXPECT_MSG_EQ(+linkId, +m_mainPhyId, "AssocReq not sent by the main PHY");
748 // this AssocReq is being sent by an EMLSR client. The other EMLSR links should be
749 // in powersave mode after association; we let the non-EMLSR links transition to
750 // active mode (by sending data null frames) after association
751 for (const auto id : m_staMacs.at(nodeId - 1)->GetLinkIds())
752 {
753 if (id != linkId && m_emlsrLinks.count(id) == 1)
754 {
755 m_staMacs[nodeId - 1]->SetPowerSaveMode({true, id});
756 }
757 }
758 }
759 break;
760
761 case WIFI_MAC_MGT_ACTION: {
762 auto [category, action] = WifiActionHeader::Peek(psdu->GetPayload(0));
763
764 if ((category == WifiActionHeader::PROTECTED_EHT) &&
765 (action.protectedEhtAction ==
767 {
768 nodeId == 0 ? CheckApEmlNotificationFrame(*psdu->begin(), txVector, linkId)
769 : CheckStaEmlNotificationFrame(*psdu->begin(), txVector, linkId);
770 }
771 else if (category == WifiActionHeader::BLOCK_ACK &&
773 {
774 CheckPmModeAfterAssociation(psdu->GetAddr1());
775 }
776 }
777 break;
778
780 CheckInitialControlFrame(*psdu->begin(), txVector, linkId);
781 break;
782
783 case WIFI_MAC_QOSDATA:
784 CheckQosFrames(psduMap, txVector, linkId);
785 break;
786
788 CheckBlockAck(psduMap, txVector, phyId);
789 break;
790
791 default:;
792 }
793}
794
795void
797{
798 // Channel switch delay should be less than the ICF padding duration, otherwise
799 // DL TXOPs cannot be initiated on non-primary links
800 auto delay = std::min(MicroSeconds(100),
801 *std::min_element(m_paddingDelay.cbegin(), m_paddingDelay.cend()));
802 Config::SetDefault("ns3::WifiPhy::ChannelSwitchDelay", TimeValue(MicroSeconds(75)));
803
805
806 m_errorModel = CreateObject<ListErrorModel>();
807 for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
808 {
810 }
811
813 {MicroSeconds(3200), MicroSeconds(3200), MicroSeconds(3200)});
814
816 {
817 auto muScheduler =
818 CreateObjectWithAttributes<RrMultiUserScheduler>("EnableUlOfdma", BooleanValue(false));
819 m_apMac->AggregateObject(muScheduler);
820 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
821 {
822 m_apMac->GetFrameExchangeManager(linkId)->GetAckManager()->SetAttribute(
823 "DlMuAckSequenceType",
825 }
826 }
827}
828
829void
831{
833 {
834 // we are done with association and Block Ack agreement; we can now enable EMLSR mode
835 m_lastAid = 0;
837 return;
838 }
839
840 // we are done with sending EML Operating Mode Notification frames. We can now generate
841 // packets for all non-AP MLDs
842 for (std::size_t i = 0; i < m_nEmlsrStations + m_nNonEmlsrStations; i++)
843 {
844 // when multiple non-AP MLDs are present, MU transmission are used. Given that the
845 // available bandwidth decreases as the number of non-AP MLDs increases, compute the
846 // number of packets to generate so that we always have two A-MPDUs per non-AP MLD
847 std::size_t count = 8 / (m_nEmlsrStations + m_nNonEmlsrStations);
849 }
850
851 // in case of 2 EMLSR clients using no non-EMLSR link, generate one additional short
852 // packet to each EMLSR client to test transition delay
853 if (m_nEmlsrStations == 2 && m_apMac->GetNLinks() == m_emlsrLinks.size())
854 {
856 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, 0, 1, 40));
857 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, 1, 1, 40));
858 });
859 }
860
861 // schedule the transmission of EML Operating Mode Notification frames to disable EMLSR mode
862 // and the generation of other packets destined to the EMLSR clients
863 for (std::size_t id = 0; id < m_nEmlsrStations; id++)
864 {
865 Simulator::Schedule(m_fe2to3delay + MilliSeconds(5 * (id + 1)), [=, this]() {
866 m_staMacs.at(id)->GetEmlsrManager()->SetAttribute(
867 "EmlsrLinkSet",
869 });
870
872 m_apMac->GetDevice()->GetNode()->AddApplication(
874 });
875 }
876}
877
878void
880{
881 m_staMacs.at(m_lastAid)->GetEmlsrManager()->SetAttribute(
882 "EmlsrLinkSet",
884 m_lastAid++;
885 Simulator::Schedule(MilliSeconds(5), [=, this]() {
887 {
888 // make the next STA send EML Notification frame
890 return;
891 }
892 // all stations enabled EMLSR mode; start traffic
894 StartTraffic();
895 });
896}
897
898void
900{
901 auto psduIt = m_txPsdus.cbegin();
902
903 // lambda to jump to the next QoS data frame or MU-RTS Trigger Frame transmitted
904 // to an EMLSR client
905 auto jumpToQosDataOrMuRts = [&]() {
906 while (psduIt != m_txPsdus.cend() &&
907 !psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData())
908 {
909 auto psdu = psduIt->psduMap.cbegin()->second;
910 if (psdu->GetHeader(0).IsTrigger())
911 {
912 CtrlTriggerHeader trigger;
913 psdu->GetPayload(0)->PeekHeader(trigger);
914 if (trigger.IsMuRts())
915 {
916 break;
917 }
918 }
919 psduIt++;
920 }
921 };
922
923 /**
924 * Before enabling EMLSR mode, no MU-RTS TF should be sent. Four packets are generated
925 * after association to trigger the establishment of a Block Ack agreement. The TXOP Limit
926 * and the MCS are set such that two packets can be transmitted in a TXOP, hence we expect
927 * that the AP MLD sends two A-MPDUs to each non-AP MLD.
928 *
929 * EMLSR client with EMLSR mode to be enabled on all links: after ML setup, all other links
930 * stay in power save mode, hence BA establishment occurs on the same link.
931 *
932 * [link 0]
933 * ───────────────────────────────────────────────────────────────────────────
934 * | power save mode
935 *
936 * ┌─────┐ ┌─────┐ ┌───┬───┐ ┌───┬───┐
937 * ┌───┐ │Assoc│ │ADDBA│ ┌───┐ │QoS│QoS│ │QoS│QoS│
938 * [link 1] │ACK│ │Resp │ │ Req │ │ACK│ │ 0 │ 1 │ │ 2 │ 3 │
939 * ───┬─────┬┴───┴──┴─────┴┬───┬─┴─────┴┬───┬─┬─────┬┴───┴─┴───┴───┴┬──┬─┴───┴───┴┬──┬───
940 * │Assoc│ │ACK│ │ACK│ │ADDBA│ │BA│ │BA│
941 * │ Req │ └───┘ └───┘ │Resp │ └──┘ └──┘
942 * └─────┘ └─────┘
943 *
944 * [link 2]
945 * ───────────────────────────────────────────────────────────────────────────
946 * | power save mode
947 *
948 *
949 * EMLSR client with EMLSR mode to be enabled on not all the links: after ML setup,
950 * the other EMLSR links stay in power save mode, the non-EMLSR link (link 1) transitions
951 * to active mode.
952 *
953 * ┌─────┐ ┌───┬───┐
954 * ┌───┐ │ADDBA│ ┌───┐ │QoS│QoS│
955 * [link 0 - non EMLSR] │ACK│ │ Req │ │ACK│ │ 2 │ 3 │
956 * ──────────────────────────────┬────┬┴───┴──┴─────┴┬───┬─┬─────┬┴───┴─┴───┴───┴┬──┬─
957 * │Data│ │ACK│ │ADDBA│ │BA│
958 * │Null│ └───┘ │Resp │ └──┘
959 * └────┘ └─────┘
960 * ┌─────┐ ┌───┬───┐
961 * ┌───┐ │Assoc│ │QoS│QoS│
962 * [link 1] │ACK│ │Resp │ │ 0 │ 1 │
963 * ───┬─────┬┴───┴──┴─────┴┬───┬──────────────────────────────────┴───┴───┴┬──┬───────
964 * │Assoc│ │ACK│ │BA│
965 * │ Req │ └───┘ └──┘
966 * └─────┘
967 *
968 * [link 2]
969 * ───────────────────────────────────────────────────────────────────────────
970 * | power save mode
971 *
972 * Non-EMLSR client (not shown): after ML setup, all other links transition to active mode
973 * by sending a Data Null frame; QoS data frame exchanges occur on two links simultaneously.
974 */
975 for (std::size_t i = 0; i < m_nEmlsrStations + m_nNonEmlsrStations; i++)
976 {
977 std::set<uint8_t> linkIds;
978
979 jumpToQosDataOrMuRts();
980 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend() &&
981 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData()),
982 true,
983 "Expected at least one QoS data frame before enabling EMLSR mode");
984 linkIds.insert(psduIt->linkId);
985 const auto firstAmpduTxEnd =
986 psduIt->startTx +
987 WifiPhy::CalculateTxDuration(psduIt->psduMap,
988 psduIt->txVector,
989 m_staMacs[i]->GetWifiPhy(psduIt->linkId)->GetPhyBand());
990 psduIt++;
991
992 jumpToQosDataOrMuRts();
993 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend() &&
994 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData()),
995 true,
996 "Expected at least two QoS data frames before enabling EMLSR mode");
997 linkIds.insert(psduIt->linkId);
998 const auto secondAmpduTxStart = psduIt->startTx;
999 psduIt++;
1000
1001 /**
1002 * If this is an EMLSR client and there is no setup link other than the one used to
1003 * establish association that is not an EMLSR link, then the two A-MPDUs are sent one
1004 * after another on the link used to establish association.
1005 */
1006 auto setupLinks = m_staMacs[i]->GetSetupLinkIds();
1007 if (i < m_nEmlsrStations &&
1008 std::none_of(setupLinks.begin(), setupLinks.end(), [&](auto&& linkId) {
1009 return linkId != m_mainPhyId && m_emlsrLinks.count(linkId) == 0;
1010 }))
1011 {
1012 NS_TEST_EXPECT_MSG_EQ(linkIds.size(),
1013 1,
1014 "Expected both A-MPDUs to be sent on the same link");
1015 NS_TEST_EXPECT_MSG_EQ(*linkIds.begin(), +m_mainPhyId, "A-MPDUs sent on incorrect link");
1016 NS_TEST_EXPECT_MSG_LT(firstAmpduTxEnd,
1017 secondAmpduTxStart,
1018 "A-MPDUs are not sent one after another");
1019 }
1020 /**
1021 * Otherwise, the two A-MPDUs can be sent concurrently on two distinct links (may be
1022 * the link used to establish association and a non-EMLSR link).
1023 */
1024 else
1025 {
1026 NS_TEST_EXPECT_MSG_EQ(linkIds.size(),
1027 2,
1028 "Expected A-MPDUs to be sent on distinct links");
1029 NS_TEST_EXPECT_MSG_GT(firstAmpduTxEnd,
1030 secondAmpduTxStart,
1031 "A-MPDUs are not sent concurrently");
1032 }
1033 }
1034
1035 /**
1036 * After enabling EMLSR mode, MU-RTS TF should only be sent on EMLSR links. After the exchange
1037 * of EML Operating Mode Notification frames, a number of packets are generated at the AP MLD
1038 * to prepare two A-MPDUs for each non-AP MLD.
1039 *
1040 * EMLSR client with EMLSR mode to be enabled on all links (A is the EMLSR client, B is the
1041 * non-EMLSR client):
1042 * ┌─────┬─────┐
1043 * │QoS 4│QoS 5│
1044 * │ to A│ to A│
1045 * ┌───┐ ├─────┼─────┤
1046 * │MU │ │QoS 4│QoS 5│
1047 * [link 0] │RTS│ │ to B│ to B│
1048 * ──────────────────────────┴───┴┬───┬┴─────┴─────┴┬──┬────────────
1049 * │CTS│ │BA│
1050 * ├───┤ ├──┤
1051 * │CTS│ │BA│
1052 * └───┘ └──┘
1053 * ┌───┐ ┌─────┬─────┐
1054 * ┌───┐ │EML│ │QoS 6│QoS 7│
1055 * [link 1] │ACK│ │OM │ │ to B│ to B│
1056 * ────┬───┬┴───┴──┴───┴┬───┬─┴─────┴─────┴┬──┬────────────────────────────────────
1057 * │EML│ │ACK│ │BA│
1058 * │OM │ └───┘ └──┘
1059 * └───┘
1060 * ┌───┐ ┌─────┬─────┐
1061 * │MU │ │QoS 6│QoS 7│
1062 * [link 2] │RTS│ │ to A│ to A│
1063 * ─────────────────────────────────────────────────────────┴───┴┬───┬┴─────┴─────┴┬──┬─
1064 * │CTS│ │BA│
1065 * └───┘ └──┘
1066 *
1067 * EMLSR client with EMLSR mode to be enabled on not all the links (A is the EMLSR client,
1068 * B is the non-EMLSR client):
1069 * ┌─────┬─────┐
1070 * │QoS 4│QoS 5│
1071 * │ to A│ to A│
1072 * ├─────┼─────┤
1073 * │QoS 4│QoS 5│
1074 * [link 0 - non EMLSR] │ to B│ to B│
1075 * ───────────────────────────┴─────┴─────┴┬──┬───────────────────────────
1076 * │BA│
1077 * ├──┤
1078 * │BA│
1079 * └──┘
1080 * ┌─────┬─────┐
1081 * │QoS 6│QoS 7│
1082 * │ to A│ to A│
1083 * ┌───┐ ┌───┐ ├─────┼─────┤
1084 * ┌───┐ │EML│ │MU │ │QoS 6│QoS 7│
1085 * [link 1] │ACK│ │OM │ │RTS│ │ to B│ to B│
1086 * ────┬───┬┴───┴──┴───┴┬───┬─┴───┴┬───┬┴─────┴─────┴┬──┬────────────
1087 * │EML│ │ACK│ │CTS│ │BA│
1088 * │OM │ └───┘ ├───┤ ├──┤
1089 * └───┘ │CTS│ │BA│
1090 * └───┘ └──┘
1091 *
1092 * [link 2]
1093 * ────────────────────────────────────────────────────────────────────────────────
1094 */
1095
1096 /// Store a QoS data frame or an MU-RTS TF followed by a QoS data frame
1097 using FrameExchange = std::list<decltype(psduIt)>;
1098
1099 std::vector<std::list<FrameExchange>> frameExchanges(m_nEmlsrStations);
1100
1101 // compute all frame exchanges involving EMLSR clients
1102 while (psduIt != m_txPsdus.cend())
1103 {
1104 jumpToQosDataOrMuRts();
1105 if (psduIt == m_txPsdus.cend())
1106 {
1107 break;
1108 }
1109
1110 if (IsTrigger(psduIt->psduMap))
1111 {
1112 CtrlTriggerHeader trigger;
1113 psduIt->psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
1114 // this is an MU-RTS TF starting a new frame exchange sequence; add it to all
1115 // the addressed EMLSR clients
1117 true,
1118 "jumpToQosDataOrMuRts does not return TFs other than MU-RTS");
1119 for (const auto& userInfo : trigger)
1120 {
1121 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1122 {
1123 if (m_staMacs.at(i)->GetAssociationId() == userInfo.GetAid12())
1124 {
1125 frameExchanges.at(i).emplace_back(FrameExchange{psduIt});
1126 break;
1127 }
1128 }
1129 }
1130 psduIt++;
1131 continue;
1132 }
1133
1134 // we get here if psduIt points to a psduMap containing QoS data frame(s); find (if any)
1135 // the QoS data frame(s) addressed to EMLSR clients and add them to the appropriate
1136 // frame exchange sequence
1137 for (const auto& staIdPsduPair : psduIt->psduMap)
1138 {
1139 std::for_each_n(m_staMacs.cbegin(), m_nEmlsrStations, [&](auto&& staMac) {
1140 if (!staMac->GetLinkIdByAddress(staIdPsduPair.second->GetAddr1()))
1141 {
1142 // not addressed to this non-AP MLD
1143 return;
1144 }
1145 // a QoS data frame starts a new frame exchange sequence if there is no previous
1146 // MU-RTS TF that has been sent on the same link and is not already followed by
1147 // a QoS data frame
1148 std::size_t id = staMac->GetDevice()->GetNode()->GetId() - 1;
1149 for (auto& frameExchange : frameExchanges.at(id))
1150 {
1151 if (IsTrigger(frameExchange.front()->psduMap) &&
1152 frameExchange.front()->linkId == psduIt->linkId &&
1153 frameExchange.size() == 1)
1154 {
1155 auto it = std::next(frameExchange.front());
1156 while (it != m_txPsdus.end())
1157 {
1158 // stop at the first frame other than CTS sent on this link
1159 if (it->linkId == psduIt->linkId &&
1160 !it->psduMap.begin()->second->GetHeader(0).IsCts())
1161 {
1162 break;
1163 }
1164 ++it;
1165 }
1166 if (it == psduIt)
1167 {
1168 // the QoS data frame actually followed the MU-RTS TF
1169 frameExchange.emplace_back(psduIt);
1170 return;
1171 }
1172 }
1173 }
1174 frameExchanges.at(id).emplace_back(FrameExchange{psduIt});
1175 });
1176 }
1177 psduIt++;
1178 }
1179
1180 /**
1181 * Let's focus on the first two frame exchanges for each EMLSR clients. If all setup links are
1182 * EMLSR links, both frame exchanges are protected by MU-RTS TF and occur one after another.
1183 * Otherwise, one frame exchange occurs on the non-EMLSR link and is not protected by
1184 * MU-RTS TF; the other frame exchange occurs on an EMLSR link and is protected by MU-RTS TF.
1185 */
1186 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1187 {
1188 NS_TEST_EXPECT_MSG_GT_OR_EQ(frameExchanges.at(i).size(),
1189 2,
1190 "Expected at least 2 frame exchange sequences "
1191 << "involving EMLSR client " << i);
1192
1193 auto firstExchangeIt = frameExchanges.at(i).begin();
1194 auto secondExchangeIt = std::next(firstExchangeIt);
1195
1196 const auto firstAmpduTxEnd =
1197 firstExchangeIt->back()->startTx +
1199 firstExchangeIt->back()->psduMap,
1200 firstExchangeIt->back()->txVector,
1201 m_staMacs[i]->GetWifiPhy(firstExchangeIt->back()->linkId)->GetPhyBand());
1202 const auto secondAmpduTxStart = secondExchangeIt->front()->startTx;
1203
1204 if (m_staMacs[i]->GetNLinks() == m_emlsrLinks.size())
1205 {
1206 // all links are EMLSR links
1207 NS_TEST_EXPECT_MSG_EQ(IsTrigger(firstExchangeIt->front()->psduMap),
1208 true,
1209 "Expected an MU-RTS TF as ICF of first frame exchange sequence");
1211 firstExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1212 true,
1213 "Expected a QoS data frame in the first frame exchange sequence");
1214
1215 NS_TEST_EXPECT_MSG_EQ(IsTrigger(secondExchangeIt->front()->psduMap),
1216 true,
1217 "Expected an MU-RTS TF as ICF of second frame exchange sequence");
1219 secondExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1220 true,
1221 "Expected a QoS data frame in the second frame exchange sequence");
1222
1223 NS_TEST_EXPECT_MSG_LT(firstAmpduTxEnd,
1224 secondAmpduTxStart,
1225 "A-MPDUs are not sent one after another");
1226 }
1227 else
1228 {
1229 std::vector<uint8_t> nonEmlsrIds;
1230 auto setupLinks = m_staMacs[i]->GetSetupLinkIds();
1231 std::set_difference(setupLinks.begin(),
1232 setupLinks.end(),
1233 m_emlsrLinks.begin(),
1234 m_emlsrLinks.end(),
1235 std::back_inserter(nonEmlsrIds));
1236 NS_TEST_ASSERT_MSG_EQ(nonEmlsrIds.size(), 1, "Unexpected number of non-EMLSR links");
1237
1238 auto nonEmlsrLinkExchangeIt = firstExchangeIt->front()->linkId == nonEmlsrIds[0]
1239 ? firstExchangeIt
1240 : secondExchangeIt;
1241 NS_TEST_EXPECT_MSG_EQ(IsTrigger(nonEmlsrLinkExchangeIt->front()->psduMap),
1242 false,
1243 "Did not expect an MU-RTS TF as ICF on non-EMLSR link");
1245 nonEmlsrLinkExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1246 true,
1247 "Expected a QoS data frame on the non-EMLSR link");
1248
1249 auto emlsrLinkExchangeIt =
1250 nonEmlsrLinkExchangeIt == firstExchangeIt ? secondExchangeIt : firstExchangeIt;
1251 NS_TEST_EXPECT_MSG_NE(+emlsrLinkExchangeIt->front()->linkId,
1252 +nonEmlsrIds[0],
1253 "Expected this exchange not to occur on non-EMLSR link");
1254 NS_TEST_EXPECT_MSG_EQ(IsTrigger(emlsrLinkExchangeIt->front()->psduMap),
1255 true,
1256 "Expected an MU-RTS TF as ICF on the EMLSR link");
1258 emlsrLinkExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1259 true,
1260 "Expected a QoS data frame on the EMLSR link");
1261
1262 NS_TEST_EXPECT_MSG_GT(firstAmpduTxEnd,
1263 secondAmpduTxStart,
1264 "A-MPDUs are not sent concurrently");
1265 }
1266
1267 // we are done with processing the first two frame exchanges, remove them
1268 frameExchanges.at(i).erase(firstExchangeIt);
1269 frameExchanges.at(i).erase(secondExchangeIt);
1270 }
1271
1272 /**
1273 * A and B are two EMLSR clients. No ICF before the second QoS data frame because B
1274 * has not switched to listening mode. ICF is sent before the third QoS data frame because
1275 * A has switched to listening mode. C is a non-EMLSR client.
1276 *
1277 * ┌─────┐ A switches to listening
1278 * │QoS x│ after transition delay
1279 * │ to A│ |
1280 * ┌───┐ ├─────┤ ┌─────┐
1281 * │MU │ │QoS x│ │QoS y│
1282 * [link 0] │RTS│ │ to B│ │ to B│
1283 * ────────────┴───┴┬───┬┴─────┴┬──┬┴─────┴┬──┬────────────
1284 * │CTS│ │BA│ │BA│
1285 * ├───┤ ├──┤ └──┘
1286 * │CTS│ │BA│
1287 * └───┘ └──┘ AP continues the TXOP A switches to listening
1288 * after PIFS recovery after transition delay
1289 * │ │
1290 * ┌─────┐ ┌───┐ ┌─────┐ │┌───┐ ┌───┐
1291 * │QoS z│ │MU │ │QoS x│ ││MU │ ┌───┐ │CF-│
1292 * [link 1] │ to C│ │RTS│ │ to A│ ││RTS│ │BAR│ │End│
1293 * ───────────────────────────────┴─────┴┬──┬┴───┴┬───┬┴─────┴┬──┬┴───┴┬───┬┴───┴┬──┬┴───┴─
1294 * │BA│ │CTS│ │BA│ │CTS│ │BA│
1295 * └──┘ └───┘ └──x └───┘ └──┘
1296 */
1297 if (m_nEmlsrStations == 2 && m_apMac->GetNLinks() == m_emlsrLinks.size())
1298 {
1299 // the following checks are only done with 2 EMLSR clients having no non-EMLSR link
1300 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1301 {
1302 NS_TEST_EXPECT_MSG_GT_OR_EQ(frameExchanges.at(i).size(),
1303 2,
1304 "Expected at least 2 frame exchange sequences "
1305 << "involving EMLSR client " << i);
1306 // the first frame exchange must start with an ICF
1307 auto firstExchangeIt = frameExchanges.at(i).begin();
1308
1309 NS_TEST_EXPECT_MSG_EQ(IsTrigger(firstExchangeIt->front()->psduMap),
1310 true,
1311 "Expected an MU-RTS TF as ICF of first frame exchange sequence");
1313 firstExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1314 true,
1315 "Expected a QoS data frame in the first frame exchange sequence");
1316 }
1317
1318 // the second frame exchange is the one that starts first
1319 auto secondExchangeIt = std::next(frameExchanges.at(0).begin())->front()->startTx <
1320 std::next(frameExchanges.at(1).begin())->front()->startTx
1321 ? std::next(frameExchanges.at(0).begin())
1322 : std::next(frameExchanges.at(1).begin());
1323 decltype(secondExchangeIt) thirdExchangeIt;
1324 std::size_t thirdExchangeStaId;
1325
1326 if (secondExchangeIt == std::next(frameExchanges.at(0).begin()))
1327 {
1328 thirdExchangeIt = std::next(frameExchanges.at(1).begin());
1329 thirdExchangeStaId = 1;
1330 }
1331 else
1332 {
1333 thirdExchangeIt = std::next(frameExchanges.at(0).begin());
1334 thirdExchangeStaId = 0;
1335 }
1336
1337 // the second frame exchange is not protected by the ICF and starts a SIFS after the end
1338 // of the previous one
1339 NS_TEST_EXPECT_MSG_EQ(IsTrigger(secondExchangeIt->front()->psduMap),
1340 false,
1341 "Expected no ICF for the second frame exchange sequence");
1343 secondExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1344 true,
1345 "Expected a QoS data frame in the second frame exchange sequence");
1346
1347 // the first two frame exchanges occur on the same link
1348 NS_TEST_EXPECT_MSG_EQ(+secondExchangeIt->front()->linkId,
1349 +frameExchanges.at(0).begin()->front()->linkId,
1350 "Expected the first two frame exchanges to occur on the same link");
1351
1352 auto bAckRespIt = std::prev(secondExchangeIt->front());
1353 NS_TEST_EXPECT_MSG_EQ(bAckRespIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAck(),
1354 true,
1355 "Expected a BlockAck response before the second frame exchange");
1356 auto bAckRespTxEnd =
1357 bAckRespIt->startTx +
1358 WifiPhy::CalculateTxDuration(bAckRespIt->psduMap,
1359 bAckRespIt->txVector,
1360 m_apMac->GetWifiPhy(bAckRespIt->linkId)->GetPhyBand());
1361
1362 // the second frame exchange starts a SIFS after the previous one
1364 bAckRespTxEnd + m_apMac->GetWifiPhy(bAckRespIt->linkId)->GetSifs(),
1365 secondExchangeIt->front()->startTx,
1366 "Expected the second frame exchange to start a SIFS after the first one");
1367
1368 // the third frame exchange is protected by MU-RTS and occurs on a different link
1369 NS_TEST_EXPECT_MSG_EQ(IsTrigger(thirdExchangeIt->front()->psduMap),
1370 true,
1371 "Expected an MU-RTS as ICF for the third frame exchange sequence");
1373 thirdExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1374 true,
1375 "Expected a QoS data frame in the third frame exchange sequence");
1376
1378 +secondExchangeIt->front()->linkId,
1379 +thirdExchangeIt->front()->linkId,
1380 "Expected the second and third frame exchanges to occur on distinct links");
1381
1382 auto secondQosIt = secondExchangeIt->front();
1383 auto secondQosTxEnd =
1384 secondQosIt->startTx +
1385 WifiPhy::CalculateTxDuration(secondQosIt->psduMap,
1386 secondQosIt->txVector,
1387 m_apMac->GetWifiPhy(secondQosIt->linkId)->GetPhyBand());
1388
1389 NS_TEST_EXPECT_MSG_GT_OR_EQ(thirdExchangeIt->front()->startTx,
1390 secondQosTxEnd + m_transitionDelay.at(thirdExchangeStaId),
1391 "Transmission started before transition delay");
1392
1393 // the BlockAck of the third frame exchange is not received correctly, so there should be
1394 // another frame exchange
1395 NS_TEST_EXPECT_MSG_EQ((thirdExchangeIt != frameExchanges.at(thirdExchangeStaId).end()),
1396 true,
1397 "Expected a fourth frame exchange");
1398 auto fourthExchangeIt = std::next(thirdExchangeIt);
1399
1400 // the fourth frame exchange is protected by MU-RTS
1401 NS_TEST_EXPECT_MSG_EQ(IsTrigger(fourthExchangeIt->front()->psduMap),
1402 true,
1403 "Expected an MU-RTS as ICF for the fourth frame exchange sequence");
1404
1405 bAckRespIt = std::prev(fourthExchangeIt->front());
1406 NS_TEST_EXPECT_MSG_EQ(bAckRespIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAck(),
1407 true,
1408 "Expected a BlockAck response before the fourth frame exchange");
1409 auto phy = m_apMac->GetWifiPhy(bAckRespIt->linkId);
1410 bAckRespTxEnd = bAckRespIt->startTx + WifiPhy::CalculateTxDuration(bAckRespIt->psduMap,
1411 bAckRespIt->txVector,
1412 phy->GetPhyBand());
1413 auto timeout = phy->GetSifs() + phy->GetSlot() + MicroSeconds(20);
1414
1415 // the fourth frame exchange starts a PIFS after the previous one because the AP
1416 // performs PIFS recovery (the initial frame in the TXOP was successfully received by
1417 // a non-EMLSR client)
1418 NS_TEST_EXPECT_MSG_GT_OR_EQ(fourthExchangeIt->front()->startTx,
1419 bAckRespTxEnd + phy->GetPifs(),
1420 "Transmission started less than a PIFS after BlockAck");
1421 NS_TEST_EXPECT_MSG_LT(fourthExchangeIt->front()->startTx,
1422 bAckRespTxEnd + phy->GetPifs() +
1423 MicroSeconds(1) /* propagation delay upper bound */,
1424 "Transmission started too much time after BlockAck");
1425
1426 auto bAckReqIt = std::next(fourthExchangeIt->front(), 2);
1427 NS_TEST_EXPECT_MSG_EQ(bAckReqIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAckReq(),
1428 true,
1429 "Expected a BlockAck request in the fourth frame exchange");
1430
1431 // we are done with processing the frame exchanges, remove them (two frame exchanges
1432 // per EMLSR client, plus the last one)
1433 frameExchanges.at(0).pop_front();
1434 frameExchanges.at(0).pop_front();
1435 frameExchanges.at(1).pop_front();
1436 frameExchanges.at(1).pop_front();
1437 frameExchanges.at(thirdExchangeStaId).pop_front();
1438 }
1439
1440 /**
1441 * After disabling EMLSR mode, no MU-RTS TF should be sent. After the exchange of
1442 * EML Operating Mode Notification frames, a number of packets are generated at the AP MLD
1443 * to prepare two A-MPDUs for each EMLSR client.
1444 *
1445 * EMLSR client with EMLSR mode to be enabled on all links (A is the EMLSR client, B is the
1446 * non-EMLSR client):
1447 *
1448 * [link 0] | power save mode
1449 * ────────────────────────────────────────────────────────
1450 * ┌─────┬─────┐ ┌──────┬──────┐
1451 * │QoS 8│QoS 9│ │QoS 10│QoS 11│
1452 * │ to A│ to A│ │ to A │ to A │
1453 * ┌───┐ ┌───┐ ├─────┼─────┤ ├──────┼──────┤
1454 * ┌───┐ │MU │ │EML│ │QoS 8│QoS 9│ │QoS 10│QoS 11│
1455 * [link 1] │ACK│ │RTS│ │OM │ │ to B│ to B│ │ to B │ to B │
1456 * ────┬───┬┴───┴──┴───┴┬───┬┴───┴┬───┬──┴─────┴─────┴┬──┬────┴──────┴──────┴┬──┬─────
1457 * │EML│ │CTS│ │ACK│ │BA│ │BA│
1458 * │OM │ └───┘ └───┘ ├──┤ ├──┤
1459 * └───┘ │BA│ │BA│
1460 * └──┘ └──┘
1461 *
1462 * [link 2] | power save mode
1463 * ────────────────────────────────────────────────────────────────────────────
1464 *
1465 *
1466 * EMLSR client with EMLSR mode to be enabled on not all the links (A is the EMLSR client,
1467 * B is the non-EMLSR client):
1468 * ┌─────┬─────┐
1469 * │QoS 8│QoS 9│
1470 * │ to A│ to A│
1471 * ├─────┼─────┤
1472 * │QoS 8│QoS 9│
1473 * [link 0 - non EMLSR] │ to B│ to B│
1474 * ─────────────────────────────────────────┴─────┴─────┴┬──┬─────────────
1475 * │BA│
1476 * ├──┤
1477 * │BA│
1478 * └──┘
1479 * ┌──────┬──────┐
1480 * │QoS 10│QoS 11│
1481 * │ to A │ to A │
1482 * ┌───┐ ┌───┐ ├──────┼──────┤
1483 * ┌───┐ │MU │ │EML│ │QoS 10│QoS 11│
1484 * [link 1] │ACK│ │RTS│ │OM │ │ to B │ to B │
1485 * ────┬───┬┴───┴──┴───┴┬───┬┴───┴┬───┬──┴──────┴──────┴┬──┬─────
1486 * │EML│ │CTS│ │ACK│ │BA│
1487 * │OM │ └───┘ └───┘ ├──┤
1488 * └───┘ │BA│
1489 * └──┘
1490 *
1491 * [link 2] | power save mode
1492 * ────────────────────────────────────────────────────────────────────────────
1493 *
1494 */
1495
1496 // for each EMLSR client, there should be a frame exchange with ICF and no data frame
1497 // (ICF protects the EML Notification response) if the EML Notification response is sent
1498 // while EMLSR mode is still enabled and two frame exchanges with data frames
1499 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1500 {
1501 // the default EMLSR Manager requests to send EML Notification frames on the link where
1502 // the main PHY is operating; if EMLSR mode is still enabled on this link when the AP MLD
1503 // sends the EML Notification response, the latter is protected by an ICF
1504 auto exchangeIt = frameExchanges.at(i).cbegin();
1505
1506 auto linkIdOpt = m_staMacs[i]->GetLinkForPhy(m_mainPhyId);
1507 NS_TEST_ASSERT_MSG_EQ(linkIdOpt.has_value(),
1508 true,
1509 "Didn't find a link on which the main PHY is operating");
1510
1511 if (IsTrigger(exchangeIt->front()->psduMap))
1512 {
1513 NS_TEST_EXPECT_MSG_EQ(+exchangeIt->front()->linkId,
1514 +linkIdOpt.value(),
1515 "ICF was not sent on the expected link");
1516 NS_TEST_EXPECT_MSG_EQ(exchangeIt->size(),
1517 1,
1518 "Expected no data frame in the first frame exchange sequence");
1519 frameExchanges.at(i).pop_front();
1520 }
1521
1522 NS_TEST_EXPECT_MSG_GT_OR_EQ(frameExchanges.at(i).size(),
1523 2,
1524 "Expected at least 2 frame exchange sequences "
1525 << "involving EMLSR client " << i);
1526
1527 auto firstExchangeIt = frameExchanges.at(i).cbegin();
1528 auto secondExchangeIt = std::next(firstExchangeIt);
1529
1530 const auto firstAmpduTxEnd =
1531 firstExchangeIt->back()->startTx +
1533 firstExchangeIt->back()->psduMap,
1534 firstExchangeIt->back()->txVector,
1535 m_staMacs[i]->GetWifiPhy(firstExchangeIt->back()->linkId)->GetPhyBand());
1536 const auto secondAmpduTxStart = secondExchangeIt->front()->startTx;
1537
1539 firstExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1540 true,
1541 "Expected a QoS data frame in the first frame exchange sequence");
1542 NS_TEST_EXPECT_MSG_EQ(firstExchangeIt->size(),
1543 1,
1544 "Expected one frame only in the first frame exchange sequence");
1545
1547 secondExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1548 true,
1549 "Expected a QoS data frame in the second frame exchange sequence");
1550 NS_TEST_EXPECT_MSG_EQ(secondExchangeIt->size(),
1551 1,
1552 "Expected one frame only in the second frame exchange sequence");
1553
1554 if (m_staMacs[i]->GetNLinks() == m_emlsrLinks.size())
1555 {
1556 // all links are EMLSR links: the two QoS data frames are sent one after another on
1557 // the link used for sending EML OMN
1559 +firstExchangeIt->front()->linkId,
1560 +linkIdOpt.value(),
1561 "First frame exchange expected to occur on link used to send EML OMN");
1562
1564 +secondExchangeIt->front()->linkId,
1565 +linkIdOpt.value(),
1566 "Second frame exchange expected to occur on link used to send EML OMN");
1567
1568 NS_TEST_EXPECT_MSG_LT(firstAmpduTxEnd,
1569 secondAmpduTxStart,
1570 "A-MPDUs are not sent one after another");
1571 }
1572 else
1573 {
1574 // the two QoS data frames are sent concurrently on distinct links
1575 NS_TEST_EXPECT_MSG_NE(+firstExchangeIt->front()->linkId,
1576 +secondExchangeIt->front()->linkId,
1577 "Frame exchanges expected to occur on distinct links");
1578
1579 NS_TEST_EXPECT_MSG_GT(firstAmpduTxEnd,
1580 secondAmpduTxStart,
1581 "A-MPDUs are not sent concurrently");
1582 }
1583 }
1584}
1585
1586void
1588{
1589 std::optional<std::size_t> staId;
1590 for (std::size_t id = 0; id < m_nEmlsrStations + m_nNonEmlsrStations; id++)
1591 {
1592 if (m_staMacs.at(id)->GetLinkIdByAddress(address))
1593 {
1594 staId = id;
1595 break;
1596 }
1597 }
1598 NS_TEST_ASSERT_MSG_EQ(staId.has_value(), true, "Not an address of a non-AP MLD " << address);
1599
1600 // check that all EMLSR links (but the link used for ML setup) of the EMLSR clients
1601 // are considered to be in power save mode by the AP MLD; all the other links have
1602 // transitioned to active mode instead
1603 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
1604 {
1605 bool psModeExpected =
1606 *staId < m_nEmlsrStations && linkId != m_mainPhyId && m_emlsrLinks.count(linkId) == 1;
1607 auto addr = m_staMacs.at(*staId)->GetAddress();
1608 auto psMode = m_apMac->GetWifiRemoteStationManager(linkId)->IsInPsMode(addr);
1609 NS_TEST_EXPECT_MSG_EQ(psMode,
1610 psModeExpected,
1611 "EMLSR link " << +linkId << " of EMLSR client " << *staId
1612 << " not in " << (psModeExpected ? "PS" : "active")
1613 << " mode");
1614 // check that AP is blocking transmission of QoS data frames on this link
1616 addr,
1617 linkId,
1618 WifiQueueBlockedReason::POWER_SAVE_MODE,
1619 psModeExpected,
1620 "Checking PM mode after association on AP MLD for EMLSR client " +
1621 std::to_string(*staId),
1622 false);
1623 }
1624}
1625
1626void
1628 const WifiTxVector& txVector,
1629 uint8_t linkId)
1630{
1631 // the AP is replying to a received EMLSR Notification frame
1632 auto pkt = mpdu->GetPacket()->Copy();
1633 const auto& hdr = mpdu->GetHeader();
1635 MgtEmlOmn frame;
1636 pkt->RemoveHeader(frame);
1637
1638 std::optional<std::size_t> staId;
1639 for (std::size_t id = 0; id < m_nEmlsrStations; id++)
1640 {
1641 if (m_staMacs.at(id)->GetFrameExchangeManager(linkId)->GetAddress() == hdr.GetAddr1())
1642 {
1643 staId = id;
1644 break;
1645 }
1646 }
1647 NS_TEST_ASSERT_MSG_EQ(staId.has_value(),
1648 true,
1649 "Not an address of an EMLSR client " << hdr.GetAddr1());
1650
1651 // The EMLSR mode change occurs a Transition Timeout after the end of the PPDU carrying the Ack
1652 auto phy = m_apMac->GetWifiPhy(linkId);
1653 auto txDuration =
1654 WifiPhy::CalculateTxDuration(mpdu->GetSize() + 4, // A-MPDU Subframe header size
1655 txVector,
1656 phy->GetPhyBand());
1657 WifiTxVector ackTxVector =
1658 m_staMacs.at(*staId)->GetWifiRemoteStationManager(linkId)->GetAckTxVector(hdr.GetAddr2(),
1659 txVector);
1660 auto ackDuration = WifiPhy::CalculateTxDuration(GetAckSize() + 4, // A-MPDU Subframe header
1661 ackTxVector,
1662 phy->GetPhyBand());
1663
1664 Simulator::Schedule(txDuration + phy->GetSifs() + ackDuration, [=, this]() {
1665 if (frame.m_emlControl.emlsrMode == 1)
1666 {
1667 // EMLSR mode enabled. Check that all EMLSR links of the EMLSR clients are considered
1668 // to be in active mode by the AP MLD
1669 for (const auto linkId : m_emlsrLinks)
1670 {
1671 auto addr = m_staMacs.at(*staId)->GetAddress();
1672 auto psMode = m_apMac->GetWifiRemoteStationManager(linkId)->IsInPsMode(addr);
1673 NS_TEST_EXPECT_MSG_EQ(psMode,
1674 false,
1675 "EMLSR link " << +linkId << " of EMLSR client " << *staId
1676 << " not in active mode");
1677 // check that AP is not blocking transmission of QoS data frames on this link
1678 CheckBlockedLink(
1679 m_apMac,
1680 addr,
1681 linkId,
1682 WifiQueueBlockedReason::POWER_SAVE_MODE,
1683 false,
1684 "Checking EMLSR links on AP MLD after EMLSR mode is enabled on EMLSR client " +
1685 std::to_string(*staId),
1686 false);
1687 }
1688 }
1689 else
1690 {
1691 // EMLSR mode disabled. Check that all EMLSR links (but the link used to send the
1692 // EML Notification frame) of the EMLSR clients are considered to be in power save
1693 // mode by the AP MLD; the other links are in active mode
1694 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1695 {
1696 bool psModeExpected = id != linkId && m_emlsrLinks.count(id) == 1;
1697 auto addr = m_staMacs.at(*staId)->GetAddress();
1698 auto psMode = m_apMac->GetWifiRemoteStationManager(id)->IsInPsMode(addr);
1699 NS_TEST_EXPECT_MSG_EQ(psMode,
1700 psModeExpected,
1701 "EMLSR link "
1702 << +id << " of EMLSR client " << *staId << " not in "
1703 << (psModeExpected ? "PS" : "active") << " mode");
1704 // check that AP is blocking transmission of QoS data frames on this link
1705 CheckBlockedLink(
1706 m_apMac,
1707 addr,
1708 id,
1709 WifiQueueBlockedReason::POWER_SAVE_MODE,
1710 psModeExpected,
1711 "Checking links on AP MLD after EMLSR mode is disabled on EMLSR client " +
1712 std::to_string(*staId),
1713 false);
1714 }
1715 }
1716 });
1717}
1718
1719void
1721 const WifiTxVector& txVector,
1722 uint8_t linkId)
1723{
1724 // an EMLSR client is sending an EMLSR Notification frame
1725 auto pkt = mpdu->GetPacket()->Copy();
1726 const auto& hdr = mpdu->GetHeader();
1728 MgtEmlOmn frame;
1729 pkt->RemoveHeader(frame);
1730
1731 std::optional<std::size_t> staId;
1732 for (std::size_t id = 0; id < m_nEmlsrStations; id++)
1733 {
1734 if (m_staMacs.at(id)->GetFrameExchangeManager(linkId)->GetAddress() == hdr.GetAddr2())
1735 {
1736 staId = id;
1737 break;
1738 }
1739 }
1740 NS_TEST_ASSERT_MSG_EQ(staId.has_value(),
1741 true,
1742 "Not an address of an EMLSR client " << hdr.GetAddr1());
1743
1744 auto phy = m_staMacs.at(*staId)->GetWifiPhy(linkId);
1745 auto txDuration = WifiPhy::CalculateTxDuration(mpdu->GetSize(), txVector, phy->GetPhyBand());
1746 auto ackTxVector =
1747 m_apMac->GetWifiRemoteStationManager(linkId)->GetAckTxVector(hdr.GetAddr2(), txVector);
1748 auto ackDuration = WifiPhy::CalculateTxDuration(GetAckSize(), ackTxVector, phy->GetPhyBand());
1749 auto cfEndDuration = WifiPhy::CalculateTxDuration(
1750 Create<WifiPsdu>(Create<Packet>(), WifiMacHeader(WIFI_MAC_CTL_END)),
1751 m_staMacs.at(*staId)->GetWifiRemoteStationManager(linkId)->GetRtsTxVector(
1753 txVector.GetChannelWidth()),
1754 phy->GetPhyBand());
1755
1756 if (frame.m_emlControl.emlsrMode != 0)
1757 {
1758 return;
1759 }
1760
1761 // EMLSR mode disabled
1762 auto timeToCfEnd = txDuration + phy->GetSifs() + ackDuration + phy->GetSifs() + cfEndDuration;
1763
1764 // before the end of the CF-End frame, this link only is not blocked on both the
1765 // EMLSR client and the AP MLD
1766 Simulator::Schedule(timeToCfEnd - MicroSeconds(1), [=, this]() {
1767 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1768 {
1769 CheckBlockedLink(m_staMacs.at(*staId),
1771 id,
1772 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1773 id != linkId && m_staMacs.at(*staId)->IsEmlsrLink(id),
1774 "Checking links on EMLSR client " + std::to_string(*staId) +
1775 " before the end of CF-End frame");
1777 m_staMacs.at(*staId)->GetAddress(),
1778 id,
1779 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1780 id != linkId && m_staMacs.at(*staId)->IsEmlsrLink(id),
1781 "Checking links of EMLSR client " + std::to_string(*staId) +
1782 " on the AP MLD before the end of CF-End frame");
1783 }
1784 });
1785 // after the end of the CF-End frame, all links for the EMLSR client are blocked on the
1786 // AP MLD
1787 Simulator::Schedule(timeToCfEnd + MicroSeconds(1), [=, this]() {
1788 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1789 {
1790 if (m_staMacs.at(*staId)->IsEmlsrLink(id))
1791 {
1792 CheckBlockedLink(
1793 m_apMac,
1794 m_staMacs.at(*staId)->GetAddress(),
1795 id && m_staMacs.at(*staId)->IsEmlsrLink(id),
1796 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
1797 true,
1798 "Checking links of EMLSR client " + std::to_string(*staId) +
1799 " are all blocked on the AP MLD right after the end of CF-End");
1800 }
1801 }
1802 });
1803 // before the end of the transition delay, all links for the EMLSR client are still
1804 // blocked on the AP MLD
1805 Simulator::Schedule(timeToCfEnd + m_transitionDelay.at(*staId) - MicroSeconds(1), [=, this]() {
1806 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1807 {
1808 if (m_staMacs.at(*staId)->IsEmlsrLink(id))
1809 {
1810 CheckBlockedLink(m_apMac,
1811 m_staMacs.at(*staId)->GetAddress(),
1812 id,
1813 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
1814 true,
1815 "Checking links of EMLSR client " + std::to_string(*staId) +
1816 " are all blocked on the AP MLD before the end of "
1817 "transition delay");
1818 }
1819 }
1820 });
1821 // immediately after the transition delay, all links for the EMLSR client are unblocked
1822 Simulator::Schedule(timeToCfEnd + m_transitionDelay.at(*staId) + MicroSeconds(1), [=, this]() {
1823 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1824 {
1825 if (m_staMacs.at(*staId)->IsEmlsrLink(id))
1826 {
1827 CheckBlockedLink(m_apMac,
1828 m_staMacs.at(*staId)->GetAddress(),
1829 id,
1830 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
1831 false,
1832 "Checking links of EMLSR client " + std::to_string(*staId) +
1833 " are all unblocked on the AP MLD after the transition delay");
1834 }
1835 }
1836 });
1837}
1838
1839void
1841 const WifiTxVector& txVector,
1842 uint8_t linkId)
1843{
1844 CtrlTriggerHeader trigger;
1845 mpdu->GetPacket()->PeekHeader(trigger);
1846 if (!trigger.IsMuRts())
1847 {
1848 return;
1849 }
1850
1852 true,
1853 "Did not expect an ICF before enabling EMLSR mode");
1854
1857 "Unexpected preamble type for the Initial Control frame");
1858 auto rate = txVector.GetMode().GetDataRate(txVector);
1859 NS_TEST_EXPECT_MSG_EQ((rate == 6e6 || rate == 12e6 || rate == 24e6),
1860 true,
1861 "Unexpected rate for the Initial Control frame: " << rate);
1862
1863 bool found = false;
1864 Time maxPaddingDelay{};
1865
1866 for (const auto& userInfo : trigger)
1867 {
1868 auto addr = m_apMac->GetMldOrLinkAddressByAid(userInfo.GetAid12());
1869 NS_TEST_ASSERT_MSG_EQ(addr.has_value(),
1870 true,
1871 "AID " << userInfo.GetAid12() << " not found");
1872
1873 if (m_apMac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*addr))
1874 {
1875 found = true;
1876
1877 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1878 {
1879 if (m_staMacs.at(i)->GetAddress() == *addr)
1880 {
1881 maxPaddingDelay = Max(maxPaddingDelay, m_paddingDelay.at(i));
1882 break;
1883 }
1884 }
1885
1886 // check that the AP has blocked transmission on all other EMLSR links
1887 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1888 {
1889 if (!m_apMac->GetWifiRemoteStationManager(id)->GetEmlsrEnabled(*addr))
1890 {
1891 continue;
1892 }
1893
1895 *addr,
1896 id,
1897 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1898 id != linkId,
1899 "Checking that AP blocked transmissions on all other EMLSR "
1900 "links after sending ICF to client with AID=" +
1901 std::to_string(userInfo.GetAid12()),
1902 false);
1903 }
1904 }
1905 }
1906
1907 NS_TEST_EXPECT_MSG_EQ(found, true, "Expected ICF to be addressed to at least an EMLSR client");
1908
1909 auto txDuration = WifiPhy::CalculateTxDuration(mpdu->GetSize(),
1910 txVector,
1911 m_apMac->GetWifiPhy(linkId)->GetPhyBand());
1912
1913 if (maxPaddingDelay.IsStrictlyPositive())
1914 {
1915 // compare the TX duration of this Trigger Frame to that of the Trigger Frame with no
1916 // padding added
1917 trigger.SetPaddingSize(0);
1918 auto pkt = Create<Packet>();
1919 pkt->AddHeader(trigger);
1920 auto txDurationWithout =
1921 WifiPhy::CalculateTxDuration(Create<WifiPsdu>(pkt, mpdu->GetHeader()),
1922 txVector,
1923 m_apMac->GetWifiPhy(linkId)->GetPhyBand());
1924
1925 NS_TEST_EXPECT_MSG_EQ(txDuration,
1926 txDurationWithout + maxPaddingDelay,
1927 "Unexpected TX duration of the MU-RTS TF with padding "
1928 << maxPaddingDelay.As(Time::US));
1929 }
1930
1931 // check that the EMLSR clients have blocked transmissions on other links after
1932 // receiving this ICF
1933 for (const auto& userInfo : trigger)
1934 {
1935 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1936 {
1937 if (m_staMacs[i]->GetAssociationId() != userInfo.GetAid12())
1938 {
1939 continue;
1940 }
1941
1942 Simulator::Schedule(txDuration + NanoSeconds(5), [=, this]() {
1943 for (uint8_t id = 0; id < m_staMacs[i]->GetNLinks(); id++)
1944 {
1945 // non-EMLSR links or links on which ICF is received are not blocked
1948 id,
1949 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1950 id != linkId && m_staMacs[i]->IsEmlsrLink(id),
1951 "Checking EMLSR links on EMLSR client " + std::to_string(i) +
1952 " after receiving ICF");
1953 }
1954 });
1955
1956 break;
1957 }
1958 }
1959}
1960
1961void
1963 const WifiTxVector& txVector,
1964 uint8_t linkId)
1965{
1966 if (m_nEmlsrStations != 2 || m_apMac->GetNLinks() != m_emlsrLinks.size() ||
1968
1969 {
1970 // we are interested in frames sent to test transition delay
1971 return;
1972 }
1973
1974 std::size_t firstClientId = 0;
1975 std::size_t secondClientId = 1;
1976 auto addr = m_staMacs[secondClientId]->GetAddress();
1977 auto txDuration =
1978 WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
1979
1981
1982 switch (m_countQoSframes)
1983 {
1984 case 1:
1985 // generate another small packet addressed to the first EMLSR client only
1987 GetApplication(DOWNLINK, firstClientId, 1, 40));
1988 // both EMLSR clients are about to receive a QoS data frame
1989 for (std::size_t clientId : {firstClientId, secondClientId})
1990 {
1991 Simulator::Schedule(txDuration, [=, this]() {
1992 for (uint8_t id = 0; id < m_staMacs[clientId]->GetNLinks(); id++)
1993 {
1994 // link on which QoS data is received is not blocked
1995 CheckBlockedLink(m_staMacs[clientId],
1997 id,
1998 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1999 id != linkId,
2000 "Checking EMLSR links on EMLSR client " +
2001 std::to_string(clientId) +
2002 " after receiving the first QoS data frame");
2003 }
2004 });
2005 }
2006 break;
2007 case 2:
2008 // generate another small packet addressed to the second EMLSR client
2010 GetApplication(DOWNLINK, secondClientId, 1, 40));
2011
2012 // when the transmission of the second QoS data frame starts, both EMLSR clients are
2013 // still blocking all the links but the one used to receive the QoS data frame
2014 for (std::size_t clientId : {firstClientId, secondClientId})
2015 {
2016 for (uint8_t id = 0; id < m_staMacs[clientId]->GetNLinks(); id++)
2017 {
2018 // link on which QoS data is received is not blocked
2019 CheckBlockedLink(m_staMacs[clientId],
2021 id,
2022 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2023 id != linkId,
2024 "Checking EMLSR links on EMLSR client " +
2025 std::to_string(clientId) +
2026 " when starting the reception of the second QoS frame");
2027 }
2028 }
2029
2030 // the EMLSR client that is not the recipient of the QoS frame being transmitted will
2031 // switch back to listening mode after a transition delay starting from the end of
2032 // the PPDU carrying this QoS data frame
2033
2034 // immediately before the end of the PPDU, this link only is not blocked for the EMLSR
2035 // client on the AP MLD
2036 Simulator::Schedule(txDuration - NanoSeconds(1), [=, this]() {
2037 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2038 {
2040 addr,
2041 id,
2042 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2043 id != linkId,
2044 "Checking that links of EMLSR client " +
2045 std::to_string(secondClientId) +
2046 " are blocked on the AP MLD before the end of the PPDU");
2047 }
2048 });
2049 // immediately before the end of the PPDU, all the links on the EMLSR client that is not
2050 // the recipient of the second QoS frame are unblocked (they are unblocked when the
2051 // PHY-RXSTART.indication is not received)
2052 Simulator::Schedule(txDuration - NanoSeconds(1), [=, this]() {
2053 for (uint8_t id = 0; id < m_staMacs[secondClientId]->GetNLinks(); id++)
2054 {
2055 CheckBlockedLink(m_staMacs[secondClientId],
2057 id,
2058 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2059 false,
2060 "Checking that links of EMLSR client " +
2061 std::to_string(secondClientId) +
2062 " are unblocked before the end of the second QoS frame");
2063 }
2064 });
2065 // immediately after the end of the PPDU, all links are blocked for the EMLSR client
2066 Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
2067 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2068 {
2070 addr,
2071 id,
2072 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2073 true,
2074 "Checking links of EMLSR client " +
2075 std::to_string(secondClientId) +
2076 " are all blocked on the AP MLD after the end of the PPDU");
2077 }
2078 });
2079 // immediately before the transition delay, all links are still blocked for the EMLSR client
2081 txDuration + m_transitionDelay.at(secondClientId) - NanoSeconds(1),
2082 [=, this]() {
2083 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2084 {
2086 m_apMac,
2087 addr,
2088 id,
2089 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2090 true,
2091 "Checking links of EMLSR client " + std::to_string(secondClientId) +
2092 " are all blocked on the AP MLD before the transition delay",
2093 false);
2094 }
2095 });
2096
2097 // 100 us before the transition delay expires, generate another small packet addressed
2098 // to a non-EMLSR client. The AP will start a TXOP to transmit this frame, while the
2099 // frame addressed to the EMLSR client is still queued because the transition delay has
2100 // not yet elapsed. The transition delay will expire while the AP is transmitting the
2101 // frame to the non-EMLSR client, so that the AP continues the TXOP to transmit the frame
2102 // to the EMLSR client and we can check that the AP performs PIFS recovery after missing
2103 // the BlockAck from the EMLSR client
2104 Simulator::Schedule(txDuration + m_transitionDelay.at(secondClientId) - MicroSeconds(100),
2105 [=, this]() {
2106 m_apMac->GetDevice()->GetNode()->AddApplication(
2108 });
2109
2110 break;
2111 case 3:
2112 // this is the frame addressed to a non-EMLSR client, which is transmitted before the
2113 // frame addressed to the EMLSR client, because the links of the latter are still blocked
2114 // at the AP because the transition delay has not yet elapsed
2116 psduMap.cbegin()->second->GetAddr1(),
2117 m_staMacs[m_nEmlsrStations]->GetFrameExchangeManager(linkId)->GetAddress(),
2118 "QoS frame not addressed to a non-EMLSR client");
2119
2120 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2121 {
2123 addr,
2124 id,
2125 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2126 true,
2127 "Checking links of EMLSR client " + std::to_string(secondClientId) +
2128 " are all blocked on the AP MLD before the transition delay");
2129 }
2130 // Block transmissions to the EMLSR client on all the links but the one on which this
2131 // frame is sent, so that the AP will continue this TXOP to send the queued frame to the
2132 // EMLSR client once the transition delay elapses
2133 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2134 {
2135 if (id != linkId)
2136 {
2137 m_apMac->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED, addr, {id});
2138 }
2139 }
2140 break;
2141 case 4:
2142 // the AP is continuing the TXOP, no need to block transmissions anymore
2143 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2144 {
2145 m_apMac->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED, addr, {id});
2146 }
2147 // at the end of the fourth QoS frame, this link only is not blocked on the EMLSR
2148 // client receiving the frame
2149 Simulator::Schedule(txDuration, [=, this]() {
2150 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2151 {
2152 CheckBlockedLink(m_staMacs[secondClientId],
2154 id,
2155 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2156 id != linkId,
2157 "Checking EMLSR links on EMLSR client " +
2158 std::to_string(secondClientId) +
2159 " after receiving the fourth QoS data frame");
2160 }
2161 });
2162 break;
2163 }
2164}
2165
2166void
2168 const WifiTxVector& txVector,
2169 uint8_t phyId)
2170{
2171 if (m_nEmlsrStations != 2 || m_apMac->GetNLinks() != m_emlsrLinks.size() ||
2173 {
2174 // we are interested in frames sent to test transition delay
2175 return;
2176 }
2177
2178 if (++m_countBlockAck == 4)
2179 {
2180 // fourth BlockAck is sent by a non-EMLSR client
2181 return;
2182 }
2183
2184 auto taddr = psduMap.cbegin()->second->GetAddr2();
2185 std::size_t clientId;
2186 if (m_staMacs[0]->GetLinkIdByAddress(taddr))
2187 {
2188 clientId = 0;
2189 }
2190 else
2191 {
2192 NS_TEST_ASSERT_MSG_EQ(m_staMacs[1]->GetLinkIdByAddress(taddr).has_value(),
2193 true,
2194 "Unexpected TA for BlockAck: " << taddr);
2195 clientId = 1;
2196 }
2197
2198 // find the link on which the main PHY is operating
2199 auto currMainPhyLinkId = m_staMacs[clientId]->GetLinkForPhy(phyId);
2201 currMainPhyLinkId.has_value(),
2202 true,
2203 "Didn't find the link on which the PHY sending the BlockAck is operating");
2204 auto linkId = *currMainPhyLinkId;
2205
2206 // we need the MLD address to check the status of the container queues
2207 auto addr = m_apMac->GetWifiRemoteStationManager(linkId)->GetMldAddress(taddr);
2208 NS_TEST_ASSERT_MSG_EQ(addr.has_value(), true, "MLD address not found for " << taddr);
2209
2210 auto apPhy = m_apMac->GetWifiPhy(linkId);
2211 auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, apPhy->GetPhyBand());
2212 auto cfEndTxDuration = WifiPhy::CalculateTxDuration(
2213 Create<WifiPsdu>(Create<Packet>(), WifiMacHeader(WIFI_MAC_CTL_END)),
2215 txVector.GetChannelWidth()),
2216 apPhy->GetPhyBand());
2217
2218 switch (m_countBlockAck)
2219 {
2220 case 5:
2221 // the PPDU carrying this BlockAck is corrupted, hence the AP MLD MAC receives the
2222 // PHY-RXSTART indication but it does not receive any frame from the PHY. Therefore,
2223 // at the end of the PPDU transmission, the AP MLD realizes that the EMLSR client has
2224 // not responded and makes an attempt at continuing the TXOP
2225
2226 // at the end of the PPDU, this link only is not blocked on both the EMLSR client and
2227 // the AP MLD
2228 Simulator::Schedule(txDuration, [=, this]() {
2229 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2230 {
2231 CheckBlockedLink(m_staMacs[clientId],
2233 id,
2234 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2235 id != linkId,
2236 "Checking links on EMLSR client " + std::to_string(clientId) +
2237 " at the end of fourth BlockAck");
2239 *addr,
2240 id,
2241 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2242 id != linkId,
2243 "Checking links of EMLSR client " + std::to_string(clientId) +
2244 " on the AP MLD at the end of fourth BlockAck");
2245 }
2246 });
2247 // a SIFS after the end of the PPDU, still this link only is not blocked on both the
2248 // EMLSR client and the AP MLD
2249 Simulator::Schedule(txDuration + apPhy->GetSifs(), [=, this]() {
2250 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2251 {
2252 CheckBlockedLink(m_staMacs[clientId],
2253 m_apMac->GetAddress(),
2254 id,
2255 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2256 id != linkId,
2257 "Checking links on EMLSR client " + std::to_string(clientId) +
2258 " a SIFS after the end of fourth BlockAck");
2259 CheckBlockedLink(m_apMac,
2260 *addr,
2261 id,
2262 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2263 id != linkId,
2264 "Checking links of EMLSR client " + std::to_string(clientId) +
2265 " a SIFS after the end of fourth BlockAck");
2266 }
2267 });
2268 // corrupt this BlockAck so that the AP MLD sends a BlockAckReq later on
2269 {
2270 auto uid = psduMap.cbegin()->second->GetPacket()->GetUid();
2271 m_errorModel->SetList({uid});
2272 }
2273 break;
2274 case 6:
2275 // at the end of the PPDU, this link only is not blocked on both the EMLSR client and
2276 // the AP MLD
2277 Simulator::Schedule(txDuration, [=, this]() {
2278 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2279 {
2280 CheckBlockedLink(m_staMacs[clientId],
2282 id,
2283 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2284 id != linkId,
2285 "Checking links on EMLSR client " + std::to_string(clientId) +
2286 " at the end of fifth BlockAck");
2288 *addr,
2289 id,
2290 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2291 id != linkId,
2292 "Checking links of EMLSR client " + std::to_string(clientId) +
2293 " on the AP MLD at the end of fifth BlockAck");
2294 }
2295 });
2296 // before the end of the CF-End frame, still this link only is not blocked on both the
2297 // EMLSR client and the AP MLD
2299 txDuration + apPhy->GetSifs() + cfEndTxDuration - MicroSeconds(1),
2300 [=, this]() {
2301 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2302 {
2303 CheckBlockedLink(m_staMacs[clientId],
2305 id,
2306 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2307 id != linkId,
2308 "Checking links on EMLSR client " + std::to_string(clientId) +
2309 " before the end of CF-End frame");
2311 *addr,
2312 id,
2313 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2314 id != linkId,
2315 "Checking links of EMLSR client " + std::to_string(clientId) +
2316 " on the AP MLD before the end of CF-End frame");
2317 }
2318 });
2319 // after the end of the CF-End frame, all links for the EMLSR client are blocked on the
2320 // AP MLD
2322 txDuration + apPhy->GetSifs() + cfEndTxDuration + MicroSeconds(1),
2323 [=, this]() {
2324 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2325 {
2327 m_apMac,
2328 *addr,
2329 id,
2330 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2331 true,
2332 "Checking links of EMLSR client " + std::to_string(clientId) +
2333 " are all blocked on the AP MLD right after the end of CF-End");
2334 }
2335 });
2336 // before the end of the transition delay, all links for the EMLSR client are still
2337 // blocked on the AP MLD
2339 txDuration + apPhy->GetSifs() + cfEndTxDuration + m_transitionDelay.at(clientId) -
2340 MicroSeconds(1),
2341 [=, this]() {
2342 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2343 {
2345 m_apMac,
2346 *addr,
2347 id,
2348 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2349 true,
2350 "Checking links of EMLSR client " + std::to_string(clientId) +
2351 " are all blocked on the AP MLD before the end of transition delay");
2352 }
2353 });
2354 // immediately after the transition delay, all links for the EMLSR client are unblocked
2356 txDuration + apPhy->GetSifs() + cfEndTxDuration + m_transitionDelay.at(clientId) +
2357 MicroSeconds(1),
2358 [=, this]() {
2359 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2360 {
2362 m_apMac,
2363 *addr,
2364 id,
2365 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2366 false,
2367 "Checking links of EMLSR client " + std::to_string(clientId) +
2368 " are all unblocked on the AP MLD after the transition delay");
2369 }
2370 });
2371 break;
2372 }
2373}
2374
2375void
2377{
2380
2381 CheckResults();
2382
2384}
2385
2387 : EmlsrOperationsTestBase("Check EML UL TXOP transmissions"),
2388 m_emlsrLinks(params.linksToEnableEmlsrOn),
2389 m_channelWidth(params.channelWidth),
2390 m_auxPhyChannelWidth(params.auxPhyChannelWidth),
2391 m_mediumSyncDuration(params.mediumSyncDuration),
2392 m_msdMaxNTxops(params.msdMaxNTxops),
2393 m_emlsrEnabledTime(0),
2394 m_firstUlPktsGenTime(0),
2395 m_unblockMainPhyLinkDelay(MilliSeconds(20)),
2396 m_checkBackoffStarted(false),
2397 m_countQoSframes(0),
2398 m_countBlockAck(0),
2399 m_countRtsframes(0),
2400 m_genBackoffIfTxopWithoutTx(params.genBackoffIfTxopWithoutTx)
2401{
2402 m_nEmlsrStations = 1;
2404 m_linksToEnableEmlsrOn = params.linksToEnableEmlsrOn;
2405 m_mainPhyId = 1;
2406
2407 // when aux PHYs do not switch link, the main PHY switches back to its previous link after
2408 // a TXOP, hence the transition delay must exceed the channel switch delay (default: 250us)
2410 m_establishBaDl = true;
2411 m_establishBaUl = true;
2412 m_duration = Seconds(1);
2413
2414 NS_ABORT_MSG_IF(params.linksToEnableEmlsrOn.size() < 2,
2415 "This test requires at least two links to be configured as EMLSR links");
2416 for (uint8_t id = 0; id < 3; id++)
2417 {
2418 if (!m_emlsrLinks.contains(id))
2419 {
2420 // non-EMLSR link found
2421 m_nonEmlsrLink = id;
2422 break;
2423 }
2424 }
2425}
2426
2427void
2429{
2430 Config::SetDefault("ns3::EmlsrManager::AuxPhyChannelWidth",
2432 Config::SetDefault("ns3::DefaultEmlsrManager::SwitchAuxPhy", BooleanValue(false));
2433 Config::SetDefault("ns3::EhtConfiguration::MediumSyncDuration",
2435 Config::SetDefault("ns3::EhtConfiguration::MsdMaxNTxops", UintegerValue(m_msdMaxNTxops));
2436 Config::SetDefault("ns3::ChannelAccessManager::GenerateBackoffIfTxopWithoutTx",
2438 // Channel switch delay should be less than RTS TX time + SIFS + CTS TX time, otherwise
2439 // UL TXOPs cannot be initiated by aux PHYs
2440 Config::SetDefault("ns3::WifiPhy::ChannelSwitchDelay", TimeValue(MicroSeconds(75)));
2441
2443
2444 m_staMacs[0]->GetQosTxop(AC_BE)->TraceConnectWithoutContext(
2445 "BackoffTrace",
2447
2448 uint8_t linkId = 0;
2449 // configure channels of the given width
2451 {
2452 uint16_t bw = 20;
2453 uint8_t number = band == WIFI_PHY_BAND_5GHZ ? 36 : 1;
2454
2455 auto width = std::min<uint16_t>(m_channelWidth, band == WIFI_PHY_BAND_2_4GHZ ? 40 : 160);
2456 while (bw < width)
2457 {
2458 bw *= 2;
2459 number += bw / 20;
2460 }
2461
2462 for (auto mac : std::initializer_list<Ptr<WifiMac>>{m_apMac, m_staMacs[0]})
2463 {
2464 mac->GetWifiPhy(linkId)->SetOperatingChannel(
2465 WifiPhy::ChannelTuple{number, width, band, 0});
2466 }
2467 linkId++;
2468 }
2469
2470 // install post reception error model on the AP affiliated with the AP MLD and operating on
2471 // the same link as the main PHY of the EMLSR client
2472 m_errorModel = CreateObject<ListErrorModel>();
2474}
2475
2476void
2478{
2479 NS_LOG_INFO("Backoff value " << backoff << " generated by EMLSR client on link " << +linkId
2480 << "\n");
2481 if (linkId != m_mainPhyId)
2482 {
2483 return; // we are only interested in backoff on main PHY link
2484 }
2485
2486 if (m_backoffEndTime)
2487 {
2489 {
2490 // another backoff value while checkBackoffStarted is true is generated only if
2491 // GenerateBackoffIfTxopWithoutTx is true
2494 true,
2495 "Another backoff value should not be generated while the main PHY link is blocked");
2496
2499 "Backoff generated at unexpected time");
2500 }
2501 else
2502 {
2503 // we are done checking the backoff
2504 m_backoffEndTime.reset();
2505 }
2506 }
2507
2509 {
2511 m_staMacs[0]->GetChannelAccessManager(linkId)->GetSifs() +
2512 (m_staMacs[0]->GetQosTxop(AC_BE)->GetAifsn(linkId) + backoff) *
2513 m_staMacs[0]->GetChannelAccessManager(linkId)->GetSlot();
2514 }
2515}
2516
2517void
2519 uint8_t phyId,
2520 WifiConstPsduMap psduMap,
2521 WifiTxVector txVector,
2522 double txPowerW)
2523{
2524 EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
2525 auto linkId = m_txPsdus.back().linkId;
2526
2527 auto psdu = psduMap.begin()->second;
2528 auto nodeId = mac->GetDevice()->GetNode()->GetId();
2529
2530 switch (psdu->GetHeader(0).GetType())
2531 {
2533 NS_ASSERT_MSG(nodeId > 0, "APs do not send AssocReq frames");
2534 NS_TEST_EXPECT_MSG_EQ(+linkId, +m_mainPhyId, "AssocReq not sent by the main PHY");
2535 break;
2536
2537 case WIFI_MAC_CTL_RTS:
2538 CheckRtsFrames(*psdu->begin(), txVector, linkId);
2539 break;
2540
2541 case WIFI_MAC_CTL_CTS:
2542 CheckCtsFrames(*psdu->begin(), txVector, linkId);
2543 break;
2544
2545 case WIFI_MAC_QOSDATA:
2546 CheckQosFrames(psduMap, txVector, linkId);
2547 break;
2548
2550 CheckBlockAck(psduMap, txVector, linkId);
2551 break;
2552
2553 default:;
2554 }
2555}
2556
2557void
2559{
2560 // initially, we prevent transmissions on aux PHY links
2561 auto auxPhyLinks = m_staMacs[0]->GetSetupLinkIds();
2562 auxPhyLinks.erase(m_mainPhyId);
2563 if (m_nonEmlsrLink)
2564 {
2565 auxPhyLinks.erase(*m_nonEmlsrLink);
2566 }
2567 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2569 auxPhyLinks);
2570
2571 // Association, Block Ack agreement establishment and enabling EMLSR mode have been done.
2572 // After 50ms, schedule:
2573 // - block of transmissions on the link where the main PHY is operating and on the non-EMLSR
2574 // link (if any)
2575 // - the generation of two UL packets
2576 // - after m_unblockMainPhyLinkDelay, unblock transmissions on the link where the main PHY
2577 // is operating, so that the first data frame is transmitted on that link
2579 std::set<uint8_t> linkIds;
2580 linkIds.insert(*m_staMacs[0]->GetLinkForPhy(m_mainPhyId));
2581 if (m_nonEmlsrLink)
2582 {
2583 linkIds.insert(*m_nonEmlsrLink);
2584 }
2585 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2587 linkIds);
2588
2589 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2590 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000));
2592
2594 m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2596 {*m_staMacs[0]->GetLinkForPhy(m_mainPhyId)});
2597 });
2598 });
2599}
2600
2601void
2603 const WifiTxVector& txVector,
2604 uint8_t linkId)
2605{
2607
2608 auto txDuration =
2609 WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2610
2611 switch (m_countQoSframes)
2612 {
2613 case 1:
2614 case 2:
2615 // do nothing, these are the QoS data frames sent to establish BA agreements in DL and UL
2616 // direction
2617 break;
2618 case 3:
2619 // first UL data frame (transmitted by the main PHY)
2620 if (m_nonEmlsrLink)
2621 {
2622 // generate data packets for another UL data frame, which will be sent on the
2623 // non-EMLSR link
2624 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2625 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(
2626 GetApplication(UPLINK, 0, 2, 1000));
2627
2628 // unblock transmissions on the non-EMLSR link once the two packets are queued
2629 Simulator::ScheduleNow([=, this]() {
2630 m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2632 {*m_nonEmlsrLink});
2633 });
2634 }
2635
2636 // check that other EMLSR links are now blocked on the EMLSR client and on the AP MLD
2637 // after this QoS data frame is received
2638 Simulator::ScheduleNow([=, this]() {
2639 for (auto id : m_staMacs[0]->GetLinkIds())
2640 {
2642 m_staMacs[0],
2644 id,
2645 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2646 id != m_staMacs[0]->GetLinkForPhy(m_mainPhyId) && m_staMacs[0]->IsEmlsrLink(id),
2647 "Checking EMLSR links on EMLSR client while sending the first data frame",
2648 false);
2649
2651 txDuration + MicroSeconds(1) /* propagation delay */,
2652 [=, this]() {
2654 m_apMac,
2655 m_staMacs[0]->GetAddress(),
2656 id,
2657 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2658 id != m_staMacs[0]->GetLinkForPhy(m_mainPhyId) &&
2659 m_staMacs[0]->IsEmlsrLink(id),
2660 "Checking EMLSR links on AP MLD while sending the first data frame");
2661 });
2662 }
2663 });
2664
2665 if (m_nonEmlsrLink)
2666 {
2667 break;
2668 }
2669 m_countQoSframes++; // if all EMLSR links, next case is already executed now
2670 [[fallthrough]];
2671 case 4:
2672 // check that other EMLSR links are now blocked on the EMLSR client and on the AP MLD
2673 // after this QoS data frame is received
2674 Simulator::ScheduleNow([=, this]() {
2675 // make aux PHYs capable of transmitting frames
2676 auto auxPhyLinks = m_staMacs[0]->GetSetupLinkIds();
2677 auxPhyLinks.erase(m_mainPhyId);
2678 if (m_nonEmlsrLink)
2679 {
2680 auxPhyLinks.erase(*m_nonEmlsrLink);
2681 }
2682 m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2684 auxPhyLinks);
2685
2686 // block transmissions on the link where the main PHY is operating
2687 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2689 {*m_staMacs[0]->GetLinkForPhy(m_mainPhyId)});
2690
2691 // generate data packets for another UL data frame, which will be sent on a link on
2692 // which an aux PHY is operating
2693 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2694 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(
2695 GetApplication(UPLINK, 0, 2, 1000));
2696 });
2697 break;
2698 case 5:
2699 // check that other EMLSR links are now blocked on both the EMLSR client and the AP MLD
2700 Simulator::ScheduleNow([=, this]() {
2701 for (auto id : m_staMacs[0]->GetLinkIds())
2702 {
2704 m_staMacs[0],
2706 id,
2707 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2708 id != linkId && m_staMacs[0]->IsEmlsrLink(id),
2709 "Checking EMLSR links on EMLSR client while sending the second data frame",
2710 false);
2711
2713 m_apMac,
2714 m_staMacs[0]->GetAddress(),
2715 id,
2716 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2717 id != linkId && m_staMacs[0]->IsEmlsrLink(id),
2718 "Checking EMLSR links on AP MLD while sending the second data frame",
2719 false);
2720 }
2721
2722 // unblock transmission on the link where the main PHY is operating
2723 m_staMacs[0]->GetMacQueueScheduler()->UnblockQueues(
2724 WifiQueueBlockedReason::TID_NOT_MAPPED,
2725 AC_BE,
2728 m_staMacs[0]->GetAddress(),
2729 {0},
2730 {m_mainPhyId});
2731 });
2732 break;
2733 }
2734}
2735
2736void
2738 const WifiTxVector& txVector,
2739 uint8_t linkId)
2740{
2742
2743 auto auxPhyLinks = m_staMacs[0]->GetSetupLinkIds();
2744 auxPhyLinks.erase(m_mainPhyId);
2745 if (m_nonEmlsrLink)
2746 {
2747 auxPhyLinks.erase(*m_nonEmlsrLink);
2748 }
2749
2750 // lambda to check that the MediumSyncDelay timer is correctly running/not running and
2751 // the CCA ED threshold is set to the correct value on all the links
2752 auto checkMediumSyncDelayTimerActive = [=, this]() {
2753 for (auto id : m_staMacs[0]->GetLinkIds())
2754 {
2755 // timer only started on EMLSR links other than the link on which TXOP was carried
2756 // out
2757 auto isTimerActive = m_staMacs[0]->IsEmlsrLink(id) && id != linkId;
2758 auto time = m_staMacs[0]->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(id);
2759 NS_TEST_EXPECT_MSG_EQ(time.has_value(),
2760 isTimerActive,
2762 << " Unexpected status for MediumSyncDelay timer on link "
2763 << +id << " after terminating a TXOP on link " << +linkId);
2764 auto currThreshold = m_staMacs[0]->GetWifiPhy(id)->GetCcaEdThreshold();
2765 NS_TEST_EXPECT_MSG_EQ((static_cast<int8_t>(currThreshold) ==
2766 m_staMacs[0]->GetEmlsrManager()->GetMediumSyncOfdmEdThreshold()),
2767 isTimerActive,
2769 << " Unexpected value (" << currThreshold
2770 << ") for CCA ED threshold on link " << +id
2771 << " when MediumSyncDelay is "
2772 << (isTimerActive ? "active" : "inactive"));
2773 }
2774 };
2775
2776 auto txDuration =
2777 WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2778
2779 switch (m_countBlockAck)
2780 {
2781 case 1:
2782 case 2:
2783 // do nothing, these are BlockAcks in response to the QoS data frames sent to establish
2784 // BA agreements in DL and UL direction
2785 break;
2786 case 3:
2787 if (linkId == m_nonEmlsrLink)
2788 {
2789 // this BlockAck has been sent on the non-EMLSR link, ignore it
2790 break;
2791 }
2792 m_checkBackoffStarted = true;
2793 if (!m_nonEmlsrLink)
2794 {
2795 m_countBlockAck++; // if all EMLSR links, next case is already executed now
2796 }
2797 [[fallthrough]];
2798 case 4:
2799 if (m_nonEmlsrLink && m_countBlockAck == 4)
2800 {
2801 // block transmissions on the non-EMLSR link
2802 Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
2803 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2805 {*m_nonEmlsrLink});
2806 });
2807 }
2808 if (linkId == m_nonEmlsrLink)
2809 {
2810 // this BlockAck has been sent on the non-EMLSR link, ignore it
2811 break;
2812 }
2813 m_checkBackoffStarted = true;
2814 // check MediumSyncDelay timer on the EMLSR client after receiving BlockAck
2815 Simulator::Schedule(txDuration + NanoSeconds(1), checkMediumSyncDelayTimerActive);
2816 break;
2817 case 5:
2818 // Block Ack in response to the second data frame sent by the EMLSR client on EMLSR links.
2819 // Check that MediumSyncDelay timer is running on the link where the main PHY is operating
2820 // and that the number of backoff slots is not changed since the beginning of the TXOP
2821 Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
2822 checkMediumSyncDelayTimerActive();
2823 auto elapsed =
2824 m_staMacs[0]->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_mainPhyId);
2826 elapsed.has_value(),
2827 true,
2828 "MediumSyncDelay timer not running on link where main PHY is operating");
2830 m_staMacs[0]->GetEmlsrManager()->GetMediumSyncDuration() -
2831 *elapsed;
2832 });
2833
2834 Simulator::Schedule(txDuration, [=, this]() {
2835 m_checkBackoffStarted = false;
2837 true,
2838 "Backoff end time should have been calculated");
2839 // when this BlockAck is received, the TXOP ends and the main PHY link is unblocked,
2840 // which causes a new backoff timer to be generated if the backoff timer is not
2841 // already running
2843 });
2844
2845 // make aux PHYs not capable of transmitting frames
2846 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2848 auxPhyLinks);
2849
2850 // generate data packets for another UL data frame, which will be sent on the link where
2851 // the main PHY is operating
2852 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2853 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000));
2854 break;
2855 case 6:
2856 // block transmission on the main PHY link and on the non-EMLSR link (if any), so that
2857 // the next QoS frames are sent on a link where an aux PHY is operating
2858 std::set<uint8_t> linkIds{m_mainPhyId};
2859 if (m_nonEmlsrLink)
2860 {
2861 linkIds.insert(*m_nonEmlsrLink);
2862 }
2863 m_staMacs[0]->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
2864 AC_BE,
2867 m_staMacs[0]->GetAddress(),
2868 {0},
2869 linkIds);
2870 // make sure aux PHYs are capable of transmitting frames
2871 m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2873 auxPhyLinks);
2874
2875 // generate data packets for another UL data frame
2876 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2877 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000));
2878
2879 break;
2880 }
2881}
2882
2883void
2885 const WifiTxVector& txVector,
2886 uint8_t linkId)
2887{
2889 {
2890 // this function only considers RTS frames sent after the first QoS data frame
2891 return;
2892 }
2893
2894 if (linkId != m_mainPhyId)
2895 {
2896 if (m_countRtsframes > 0 && !m_corruptCts.has_value())
2897 {
2898 // we get here for the frame exchange in which the CTS response must be corrupted.
2899 // Install post reception error model on the STA affiliated with the EMLSR client that
2900 // is transmitting this RTS frame
2901 m_errorModel = CreateObject<ListErrorModel>();
2902 m_staMacs[0]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_errorModel);
2903 m_corruptCts = true;
2904 }
2905
2906 return;
2907 }
2908
2909 // we get here for RTS frames sent by the main PHY while the MediumSyncDelay timer is running
2911
2913 m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId)->GetChannelWidth(),
2914 "RTS sent by main PHY on an unexpected width");
2915
2916 // corrupt reception at AP MLD
2917 NS_LOG_INFO("CORRUPTED");
2918 m_errorModel->SetList({mpdu->GetPacket()->GetUid()});
2919}
2920
2921void
2923 const WifiTxVector& txVector,
2924 uint8_t linkId)
2925{
2926 if (m_corruptCts.has_value() && *m_corruptCts)
2927 {
2928 // corrupt reception at EMLSR client
2929 NS_LOG_INFO("CORRUPTED");
2930 m_errorModel->SetList({mpdu->GetPacket()->GetUid()});
2931 m_corruptCts = false;
2932
2933 auto txDuration = WifiPhy::CalculateTxDuration(mpdu->GetSize(),
2934 txVector,
2935 m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2936 // main PHY is about to complete channel switch when CTS ends
2937 Simulator::Schedule(txDuration, [=, this]() {
2938 const auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId);
2939 NS_TEST_EXPECT_MSG_EQ(mainPhy->IsStateSwitching(),
2940 true,
2941 "Expecting the main PHY to be switching link");
2942 });
2943 }
2944}
2945
2946void
2948{
2951
2952 CheckResults();
2953
2955}
2956
2957void
2959{
2960 if (m_msdMaxNTxops > 0)
2961 {
2965 "Unexpected number of RTS frames sent while the MediumSyncDelay timer is running");
2966 }
2967
2968 auto psduIt = m_txPsdus.cbegin();
2969
2970 // lambda to jump to the next QoS data frame or MU-RTS Trigger Frame or RTS transmitted
2971 // to/by an EMLSR client
2972 auto jumpToQosDataOrMuRts = [&]() {
2973 while (psduIt != m_txPsdus.cend() &&
2974 !psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData() &&
2975 !psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts())
2976 {
2977 auto psdu = psduIt->psduMap.cbegin()->second;
2978 if (psdu->GetHeader(0).IsTrigger())
2979 {
2980 CtrlTriggerHeader trigger;
2981 psdu->GetPayload(0)->PeekHeader(trigger);
2982 if (trigger.IsMuRts())
2983 {
2984 break;
2985 }
2986 }
2987 psduIt++;
2988 }
2989 };
2990
2991 /**
2992 * EMLSR client with EMLSR mode enabled on all links (main PHY ID = 1).
2993 *
2994 * main PHY│
2995 * blocked,│
2996 * aux PHYs││main PHY blocked│
2997 * cannot │
2998 * transmit│
2999 * │ ┌───┐ ┌──┐
3000 * [link 0] │CTS│ │BA│
3001 * ────────────────────────┬───┬┴───┴┬───┬───┬┴──┴─────────────────────────────────────────
3002 * │RTS│ │QoS│QoS│
3003 * └───┘ │ 6 │ 7 │
3004 * └───┴───┘
3005 * gen backoff gen backoff if MediumSyncDelay
3006 * ┌──┐ (also many times) not running timer expired ┌──┐
3007 * [link 1] │BA│ │ if allowed │ │ │BA│
3008 * ─────────┬───┬───┬┴──┴───────────────────────────┬───┬─────┬───┬────┬───┬───┬┴──┴───────
3009 * │QoS│QoS│ │RTS│ ... │RTS│ │QoS│QoS│
3010 * │ 4 │ 5 │ └───┘ └───┘ │ 8 │ 9 │
3011 * └───┴───┘ └───┴───┘
3012 *
3013 * [link 2]
3014 * ───────────────────────────────────────────────────────────────────────────
3015 *
3016 *
3017 *
3018 * EMLSR client with EMLSR mode enabled on links 0 and 1 (main PHY ID = 1).
3019 *
3020 * main PHY │
3021 * and │
3022 * non-EMLSR│
3023 * link │
3024 * blocked,│
3025 * aux PHYs││main PHY blocked│
3026 * cannot │
3027 * transmit│
3028 * │ ┌───┐ ┌──┐
3029 * [link 0] │CTS│ │BA│
3030 * ────────────────────────┬───┬┴───┴┬───┬───┬┴──┴─────────────────────────────────────────
3031 * │RTS│ │QoS│QoS│
3032 * └───┘ │ 8 │ 9 │
3033 * └───┴───┘
3034 * gen backoff gen backoff if MediumSyncDelay
3035 * ┌──┐ (also many times) not running timer expired ┌──┐
3036 * [link 1] │BA│ │ if allowed │ │ │BA│
3037 * ─────────┬───┬───┬┴──┴───────────────────────────┬───┬─────┬───┬────┬───┬───┬┴──┴───────
3038 * │QoS│QoS│ │RTS│ ... │RTS│ │QoS│QoS│
3039 * │ 4 │ 5 │ └───┘ └───┘ │ 10│ 11│
3040 * └───┴───┘ └───┴───┘
3041 * ┌──┐
3042 * [link 2] │BA│
3043 * ──────────┬───┬───┬┴──┴────────────────────────────────────────────────────────────
3044 * │QoS│QoS│
3045 * │ 6 │ 7 │
3046 * └───┴───┘
3047 *
3048 * For both scenarios, after the last frame exchange on the main PHY link, we have the
3049 * following frame exchange on an EMLSR link where an aux PHY is operating on:
3050 *
3051 * ┌───┐ ┌───┐ ┌──┐
3052 * │CTS│ │CTS│ │BA│
3053 * ──────┬───┬┴───X─────────┬───┬┴───┴┬───┬───┬┴──┴─────────────────────────────────────
3054 * │RTS│ │RTS│ │QoS│QoS│
3055 * └───┘ └───┘ │ X │ Y │
3056 * └───┴───┘
3057 * For all EMLSR links scenario, X=10, Y=11; otherwise, X=12, Y=13.
3058 */
3059
3060 // jump to the first (non-Beacon) frame transmitted after establishing BA agreements and
3061 // enabling EMLSR mode
3062 while (psduIt != m_txPsdus.cend() &&
3063 (psduIt->startTx < m_firstUlPktsGenTime ||
3064 psduIt->psduMap.cbegin()->second->GetHeader(0).IsBeacon()))
3065 {
3066 ++psduIt;
3067 }
3068
3069 // the first QoS data frame is transmitted by the main PHY without RTS protection as soon
3070 // as transmissions on the link where the main PHY is operating are unblocked (at this
3071 // moment, aux PHYs cannot transmit)
3072 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3073 true,
3074 "First QoS data frame has not been transmitted");
3075 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
3076 true,
3077 "First QoS data frame should be transmitted without protection");
3078 NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
3079 +m_mainPhyId,
3080 "First QoS data frame should be transmitted by the main PHY");
3081 NS_TEST_EXPECT_MSG_GT_OR_EQ(psduIt->startTx,
3083 "First QoS data frame sent too early");
3084
3085 auto prevPsduIt = psduIt++;
3086 jumpToQosDataOrMuRts();
3087
3088 if (m_nonEmlsrLink)
3089 {
3090 // an additional data frame is sent concurrently on the non-EMLSR link
3092 (psduIt != m_txPsdus.cend()),
3093 true,
3094 "Expected another QoS data frame sent concurrently with the first frame");
3096 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
3097 true,
3098 "First data frame on non-EMLSR link should be transmitted without protection");
3099 NS_TEST_EXPECT_MSG_EQ(+psduIt->linkId,
3100 +m_nonEmlsrLink.value(),
3101 "First data frame expected to be transmitted on the non-EMLSR link");
3102 const auto txDuration =
3103 WifiPhy::CalculateTxDuration(prevPsduIt->psduMap,
3104 prevPsduIt->txVector,
3105 m_staMacs[0]->GetWifiPhy(prevPsduIt->phyId)->GetPhyBand());
3106 NS_TEST_EXPECT_MSG_LT(psduIt->startTx,
3107 prevPsduIt->startTx + txDuration,
3108 "First data frame on the non-EMLSR link not sent concurrently");
3109 psduIt++;
3110 jumpToQosDataOrMuRts();
3111 }
3112
3113 // the second QoS data frame is transmitted by the main PHY after that the aux PHY has
3114 // obtained a TXOP and sent an RTS
3115 // RTS
3116 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3117 true,
3118 "RTS before second QoS data frame has not been transmitted");
3119 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts(),
3120 true,
3121 "Second QoS data frame should be transmitted with protection");
3123 +psduIt->phyId,
3124 +m_mainPhyId,
3125 "RTS before second QoS data frame should not be transmitted by the main PHY");
3126 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3128 "RTS before second data frame transmitted on an unexpected width");
3129 psduIt++;
3130 // CTS
3131 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3132 true,
3133 "CTS before second QoS data frame has not been transmitted");
3134 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsCts(),
3135 true,
3136 "CTS before second QoS data frame has not been transmitted");
3137 psduIt++;
3138 // QoS Data
3139 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3140 true,
3141 "Second QoS data frame has not been transmitted");
3142 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
3143 true,
3144 "Second QoS data frame has not been transmitted");
3145 NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
3146 +m_mainPhyId,
3147 "Second QoS data frame should be transmitted by the main PHY");
3148 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3150 "Second data frame not transmitted on the same width as RTS");
3151
3152 bool moreQosDataFound = false;
3153
3154 while (++psduIt != m_txPsdus.cend())
3155 {
3156 jumpToQosDataOrMuRts();
3157 if (psduIt != m_txPsdus.cend() &&
3158 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData())
3159 {
3160 moreQosDataFound = true;
3161
3162 NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
3163 +m_mainPhyId,
3164 "Last QoS data frame should be transmitted by the main PHY");
3165 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3166 m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId)->GetChannelWidth(),
3167 "Expecting TX width of last data frame to equal the channel "
3168 "width used by the main PHY");
3170 psduIt->startTx,
3172 "Last QoS data frame sent before MediumSyncDelay timer expired");
3173
3174 break;
3175 }
3176 }
3177
3178 NS_TEST_EXPECT_MSG_EQ(moreQosDataFound,
3179 true,
3180 "Last QoS data frame transmitted by the main PHY not found");
3181
3182 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()), true, "Expected more frames");
3183 ++psduIt;
3184 jumpToQosDataOrMuRts();
3185
3186 // the first attempt at transmitting the last QoS data frame fails because CTS is corrupted
3187 // RTS
3188 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3189 true,
3190 "RTS before last QoS data frame has not been transmitted");
3191 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts(),
3192 true,
3193 "Last QoS data frame should be transmitted with protection");
3195 +psduIt->phyId,
3196 +m_mainPhyId,
3197 "RTS before last QoS data frame should not be transmitted by the main PHY");
3198 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3200 "RTS before last data frame transmitted on an unexpected width");
3201 psduIt++;
3202 // CTS
3203 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3204 true,
3205 "CTS before last QoS data frame has not been transmitted");
3206 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsCts(),
3207 true,
3208 "CTS before last QoS data frame has not been transmitted");
3209 psduIt++;
3210 jumpToQosDataOrMuRts();
3211
3212 // the last QoS data frame is transmitted by an aux PHY after that the aux PHY has
3213 // obtained a TXOP and sent an RTS
3214 // RTS
3215 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3216 true,
3217 "RTS before last QoS data frame has not been transmitted");
3218 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts(),
3219 true,
3220 "Last QoS data frame should be transmitted with protection");
3222 +psduIt->phyId,
3223 +m_mainPhyId,
3224 "RTS before last QoS data frame should not be transmitted by the main PHY");
3225 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3227 "RTS before last data frame transmitted on an unexpected width");
3228 psduIt++;
3229 // CTS
3230 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3231 true,
3232 "CTS before last QoS data frame has not been transmitted");
3233 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsCts(),
3234 true,
3235 "CTS before last QoS data frame has not been transmitted");
3236 psduIt++;
3237 // QoS Data
3238 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3239 true,
3240 "Last QoS data frame has not been transmitted");
3241 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
3242 true,
3243 "Last QoS data frame has not been transmitted");
3244 NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
3245 +m_mainPhyId,
3246 "Last QoS data frame should be transmitted by the main PHY");
3247 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3249 "Last data frame not transmitted on the same width as RTS");
3250}
3251
3253 : EmlsrOperationsTestBase(std::string("Check EMLSR link switching (switchAuxPhy=") +
3254 std::to_string(params.switchAuxPhy) +
3255 ", resetCamState=" + std::to_string(params.resetCamState) +
3256 ", auxPhyMaxChWidth=" + std::to_string(params.auxPhyMaxChWidth) +
3257 "MHz )"),
3258 m_switchAuxPhy(params.switchAuxPhy),
3259 m_resetCamState(params.resetCamState),
3260 m_auxPhyMaxChWidth(params.auxPhyMaxChWidth),
3261 m_countQoSframes(0),
3262 m_txPsdusPos(0)
3263{
3264 m_nEmlsrStations = 1;
3266 m_linksToEnableEmlsrOn = {0, 1, 2}; // enable EMLSR on all links right after association
3267 m_mainPhyId = 1;
3268 m_establishBaDl = true;
3269 m_duration = Seconds(1.0);
3270 // when aux PHYs do not switch link, the main PHY switches back to its previous link after
3271 // a TXOP, hence the transition delay must exceed the channel switch delay (default: 250us)
3273}
3274
3275void
3277 uint8_t phyId,
3278 WifiConstPsduMap psduMap,
3279 WifiTxVector txVector,
3280 double txPowerW)
3281{
3282 EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
3283 auto linkId = m_txPsdus.back().linkId;
3284
3285 auto psdu = psduMap.begin()->second;
3286 auto nodeId = mac->GetDevice()->GetNode()->GetId();
3287
3288 switch (psdu->GetHeader(0).GetType())
3289 {
3291 NS_ASSERT_MSG(nodeId > 0, "APs do not send AssocReq frames");
3292 NS_TEST_EXPECT_MSG_EQ(+linkId, +m_mainPhyId, "AssocReq not sent by the main PHY");
3293 break;
3294
3295 case WIFI_MAC_MGT_ACTION: {
3296 auto [category, action] = WifiActionHeader::Peek(psdu->GetPayload(0));
3297
3298 if (nodeId == 1 && category == WifiActionHeader::PROTECTED_EHT &&
3299 action.protectedEhtAction ==
3301 {
3302 // the EMLSR client is starting the transmission of the EML OMN frame;
3303 // temporarily block transmissions of QoS data frames from the AP MLD to the
3304 // non-AP MLD on all the links but the one used for ML setup, so that we know
3305 // that the first QoS data frame is sent on the link of the main PHY
3306 std::set<uint8_t> linksToBlock;
3307 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
3308 {
3309 if (id != m_mainPhyId)
3310 {
3311 linksToBlock.insert(id);
3312 }
3313 }
3314 m_apMac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3315 AC_BE,
3317 m_staMacs[0]->GetAddress(),
3319 {0},
3320 linksToBlock);
3321 }
3322 else if (category == WifiActionHeader::BLOCK_ACK &&
3324 {
3325 // store the current number of transmitted frame
3326 m_txPsdusPos = m_txPsdus.size() - 1;
3327 }
3328 }
3329 break;
3330
3332 CheckInitialControlFrame(psduMap, txVector, linkId);
3333 break;
3334
3335 case WIFI_MAC_QOSDATA:
3336 CheckQosFrames(psduMap, txVector, linkId);
3337 break;
3338
3339 default:;
3340 }
3341}
3342
3343void
3345{
3346 Config::SetDefault("ns3::DefaultEmlsrManager::SwitchAuxPhy", BooleanValue(m_switchAuxPhy));
3347 Config::SetDefault("ns3::EmlsrManager::ResetCamState", BooleanValue(m_resetCamState));
3348 Config::SetDefault("ns3::EmlsrManager::AuxPhyChannelWidth", UintegerValue(m_auxPhyMaxChWidth));
3349 Config::SetDefault("ns3::WifiPhy::ChannelSwitchDelay", TimeValue(MicroSeconds(75)));
3350
3352
3353 // use channels of different widths
3354 for (auto mac : std::initializer_list<Ptr<WifiMac>>{m_apMac, m_staMacs[0]})
3355 {
3356 mac->GetWifiPhy(0)->SetOperatingChannel(
3358 mac->GetWifiPhy(1)->SetOperatingChannel(
3360 mac->GetWifiPhy(2)->SetOperatingChannel(
3362 }
3363}
3364
3365void
3367{
3370
3371 CheckResults();
3372
3374}
3375
3376void
3378 const WifiTxVector& txVector,
3379 uint8_t linkId)
3380{
3382
3383 switch (m_countQoSframes)
3384 {
3385 case 1:
3386 // unblock transmissions on all links
3387 m_apMac->GetMacQueueScheduler()->UnblockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3388 AC_BE,
3390 m_staMacs[0]->GetAddress(),
3392 {0},
3393 {0, 1, 2});
3394 // block transmissions on the link used for ML setup
3395 m_apMac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3396 AC_BE,
3398 m_staMacs[0]->GetAddress(),
3400 {0},
3401 {m_mainPhyId});
3402 // generate a new data packet, which will be sent on a link other than the one
3403 // used for ML setup, hence triggering a link switching on the EMLSR client
3405 break;
3406 case 2:
3407 // block transmission on the link used to send this QoS data frame
3408 m_apMac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3409 AC_BE,
3411 m_staMacs[0]->GetAddress(),
3413 {0},
3414 {linkId});
3415 // generate a new data packet, which will be sent on the link that has not been used
3416 // so far, hence triggering another link switching on the EMLSR client
3418 break;
3419 case 3:
3420 // block transmission on the link used to send this QoS data frame
3421 m_apMac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3422 AC_BE,
3424 m_staMacs[0]->GetAddress(),
3426 {0},
3427 {linkId});
3428 // unblock transmissions on the link used for ML setup
3429 m_apMac->GetMacQueueScheduler()->UnblockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3430 AC_BE,
3432 m_staMacs[0]->GetAddress(),
3434 {0},
3435 {m_mainPhyId});
3436 // generate a new data packet, which will be sent again on the link used for ML setup,
3437 // hence triggering yet another link switching on the EMLSR client
3439 break;
3440 }
3441}
3442
3443/**
3444 * AUX PHY switching enabled
3445 *
3446 * |--------- aux PHY A ---------|------ main PHY ------|-------------- aux PHY B -------------
3447 * ┌───┐ ┌───┐
3448 * │ICF│ │QoS│
3449 * ──────────────────────────┴───┴┬───┬┴───┴┬──┬────────────────────────────────────────────────
3450 * [link 0] │CTS│ │BA│
3451 * └───┘ └──┘
3452 *
3453 *
3454 * |--------- main PHY ----------|------------------ aux PHY A ----------------|--- main PHY ---
3455 * ┌───┐ ┌───┐ ┌───┐ ┌───┐
3456 * │ICF│ │QoS│ │ICF│ │QoS│
3457 * ───┴───┴┬───┬┴───┴┬──┬──────────────────────────────────────────────────┴───┴┬───┬┴───┴┬──┬──
3458 * [link 1]│CTS│ │BA│ │CTS│ │BA│
3459 * └───┘ └──┘ └───┘ └──┘
3460 *
3461 *
3462 * |--------------------- aux PHY B --------------------|------ main PHY ------|-- aux PHY A ---
3463 * ┌───┐ ┌───┐
3464 * │ICF│ │QoS│
3465 * ─────────────────────────────────────────────────┴───┴┬───┬┴───┴┬──┬─────────────────────────
3466 * [link 2] │CTS│ │BA│
3467 * └───┘ └──┘
3468 *
3469 *
3470 * AUX PHY switching disabled (X = main PHY channel switch delay)
3471 *
3472 * |------------------------------------------ aux PHY A ---------------------------------------
3473 * |-- main PHY --|X|
3474 * ┌───┐ ┌───┐
3475 * │ICF│ │QoS│
3476 * ──────────────────────────┴───┴┬───┬┴───┴┬──┬────────────────────────────────────────────────
3477 * [link 0] │CTS│ │BA│
3478 * └───┘ └──┘
3479 *
3480 * |-main|
3481 * |--------- main PHY ----------| |-PHY-| |------ main PHY ------
3482 * ┌───┐ ┌───┐ ┌───┐ ┌───┐
3483 * │ICF│ │QoS│ │ICF│ │QoS│
3484 * ───┴───┴┬───┬┴───┴┬──┬──────────────────────────────────────────────────┴───┴┬───┬┴───┴┬──┬──
3485 * [link 1]│CTS│ │BA│ │CTS│ │BA│
3486 * └───┘ └──┘ └───┘ └──┘
3487 *
3488 *
3489 * |------------------------------------------ aux PHY B ---------------------------------------
3490 * |-- main PHY --|X|
3491 * ┌───┐ ┌───┐
3492 * │ICF│ │QoS│
3493 * ─────────────────────────────────────────────────┴───┴┬───┬┴───┴┬──┬─────────────────────────
3494 * [link 2] │CTS│ │BA│
3495 * └───┘ └──┘
3496 */
3497
3498void
3500 const WifiTxVector& txVector,
3501 uint8_t linkId)
3502{
3503 if (m_txPsdusPos == 0)
3504 {
3505 // the iterator has not been set yet, thus we are not done with the establishment of
3506 // the BA agreement
3507 return;
3508 }
3509
3510 NS_TEST_EXPECT_MSG_LT(m_countQoSframes, 4, "Unexpected number of ICFs");
3511
3512 auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId);
3513 auto phyRecvIcf = m_staMacs[0]->GetWifiPhy(linkId); // PHY receiving the ICF
3514
3515 auto currMainPhyLinkId = m_staMacs[0]->GetLinkForPhy(mainPhy);
3516 NS_TEST_ASSERT_MSG_EQ(currMainPhyLinkId.has_value(),
3517 true,
3518 "Didn't find the link on which the Main PHY is operating");
3519 NS_TEST_ASSERT_MSG_NE(phyRecvIcf,
3520 nullptr,
3521 "No PHY on the link where ICF " << m_countQoSframes << " was sent");
3522
3523 if (phyRecvIcf != mainPhy)
3524 {
3526 phyRecvIcf->GetChannelWidth(),
3528 "Aux PHY that received ICF "
3529 << m_countQoSframes << " is operating on a channel whose width exceeds the limit");
3530 }
3531
3532 // if aux PHYs switch links, only the first ICF is received by the main PHY; otherwise,
3533 // the first ICF and the last ICF are received by the main PHY
3534 NS_TEST_EXPECT_MSG_EQ((phyRecvIcf == mainPhy),
3536 "Expecting that the ICF was received by the main PHY");
3537
3538 // if aux PHYs do not switch links, the main PHY is operating on its original link when
3539 // the transmission of an ICF starts
3540 NS_TEST_EXPECT_MSG_EQ(m_switchAuxPhy || currMainPhyLinkId == m_mainPhyId,
3541 true,
3542 "Main PHY is operating on an unexpected link ("
3543 << +currMainPhyLinkId.value() << ", expected " << +m_mainPhyId
3544 << ")");
3545
3546 auto txDuration =
3547 WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
3548
3549 // check that PHYs are operating on the expected link after the reception of the ICF
3550 Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
3551 // the main PHY must be operating on the link where ICF was sent
3552 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(linkId),
3553 mainPhy,
3554 "PHY operating on link where ICF was sent is not the main PHY");
3555
3556 // the behavior of Aux PHYs depends on whether they switch channel or not
3557 if (m_switchAuxPhy)
3558 {
3559 if (mainPhy != phyRecvIcf)
3560 {
3561 NS_TEST_EXPECT_MSG_EQ(phyRecvIcf->IsStateSwitching(),
3562 true,
3563 "Aux PHY expected to switch channel");
3564 }
3565 Simulator::Schedule(phyRecvIcf->GetChannelSwitchDelay(), [=, this]() {
3566 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(*currMainPhyLinkId),
3567 phyRecvIcf,
3568 "The Aux PHY that received the ICF is expected to operate "
3569 "on the link where Main PHY was before switching channel");
3570 });
3571 }
3572 else
3573 {
3574 NS_TEST_EXPECT_MSG_EQ(phyRecvIcf->IsStateSwitching(),
3575 false,
3576 "Aux PHY is not expected to switch channel");
3577 NS_TEST_EXPECT_MSG_EQ(phyRecvIcf->GetPhyBand(),
3578 mainPhy->GetPhyBand(),
3579 "The Aux PHY that received the ICF is expected to operate "
3580 "on the same band as the Main PHY");
3581 }
3582 });
3583}
3584
3585void
3587{
3588 NS_TEST_ASSERT_MSG_NE(m_txPsdusPos, 0, "BA agreement establishment not completed");
3589
3590 const std::size_t nRxOk = 4; // successfully received ICFs
3591
3593 m_txPsdusPos + 2 + nRxOk * 4,
3594 "Insufficient number of TX PSDUs");
3595
3596 // m_txPsdusPos points to ADDBA_RESPONSE, then ACK and then ICF
3597 auto psduIt = std::next(m_txPsdus.cbegin(), m_txPsdusPos + 2);
3598
3599 for (std::size_t i = 0; i < nRxOk; i++)
3600 {
3601 NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 &&
3602 psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsTrigger()),
3603 true,
3604 "Expected a Trigger Frame (ICF)");
3605 psduIt++;
3607 (psduIt->psduMap.size() == 1 && psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsCts()),
3608 true,
3609 "Expected a CTS");
3610 psduIt++;
3611 NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 &&
3612 psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsQosData()),
3613 true,
3614 "Expected a QoS Data frame");
3615 psduIt++;
3616 NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 &&
3617 psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsBlockAck()),
3618 true,
3619 "Expected a BlockAck");
3620 psduIt++;
3621 }
3622}
3623
3625 : TestSuite("wifi-emlsr", Type::UNIT)
3626{
3627 AddTestCase(new EmlOperatingModeNotificationTest(), TestCase::Duration::QUICK);
3628 AddTestCase(new EmlOmnExchangeTest({1, 2}, MicroSeconds(0)), TestCase::Duration::QUICK);
3629 AddTestCase(new EmlOmnExchangeTest({1, 2}, MicroSeconds(2048)), TestCase::Duration::QUICK);
3630 AddTestCase(new EmlOmnExchangeTest({0, 1, 2, 3}, MicroSeconds(0)), TestCase::Duration::QUICK);
3631 AddTestCase(new EmlOmnExchangeTest({0, 1, 2, 3}, MicroSeconds(2048)),
3632 TestCase::Duration::QUICK);
3633 for (const auto& emlsrLinks :
3634 {std::set<uint8_t>{0, 1, 2}, std::set<uint8_t>{1, 2}, std::set<uint8_t>{0, 1}})
3635 {
3637 new EmlsrDlTxopTest(
3638 {1, 0, emlsrLinks, {MicroSeconds(32)}, {MicroSeconds(32)}, MicroSeconds(512)}),
3639 TestCase::Duration::QUICK);
3641 new EmlsrDlTxopTest(
3642 {1, 1, emlsrLinks, {MicroSeconds(64)}, {MicroSeconds(64)}, MicroSeconds(512)}),
3643 TestCase::Duration::QUICK);
3645 2,
3646 emlsrLinks,
3647 {MicroSeconds(128), MicroSeconds(256)},
3648 {MicroSeconds(128), MicroSeconds(256)},
3649 MicroSeconds(512)}),
3650 TestCase::Duration::QUICK);
3651 }
3652
3653 for (auto genBackoffIfTxopWithoutTx : {true, false})
3654 {
3656 {{0, 1, 2}, 40, 20, MicroSeconds(5504), 3, genBackoffIfTxopWithoutTx}),
3657 TestCase::Duration::QUICK);
3659 new EmlsrUlTxopTest({{0, 1}, 40, 20, MicroSeconds(5504), 1, genBackoffIfTxopWithoutTx}),
3660 TestCase::Duration::QUICK);
3661 }
3662
3663 for (bool switchAuxPhy : {true, false})
3664 {
3665 for (bool resetCamState : {true, false})
3666 {
3667 for (uint16_t auxPhyMaxChWidth : {20, 40, 80, 160})
3668 {
3670 new EmlsrLinkSwitchTest({switchAuxPhy, resetCamState, auxPhyMaxChWidth}),
3671 TestCase::Duration::QUICK);
3672 }
3673 }
3674 }
3675}
3676
3677static WifiEmlsrTestSuite g_wifiEmlsrTestSuite; ///< the test suite
#define Max(a, b)
Test the exchange of EML Operating Mode Notification frames.
void CheckEmlCapabilitiesInAssocResp(Ptr< const WifiMpdu > mpdu, const WifiTxVector &txVector, uint8_t linkId)
Check the content of the EML Capabilities subfield of the Multi-Link Element included in the Associat...
void TxOk(Ptr< const WifiMpdu > mpdu)
Callback invoked when the non-AP MLD receives the acknowledgment for a transmitted MPDU.
std::list< uint64_t > m_uidList
list of UIDs of packets to corrupt
std::size_t m_checkEmlsrLinksCount
counter for the number of times CheckEmlsrLinks is called (should be two: when the transition timeout...
Ptr< ListErrorModel > m_errorModel
error rate model to corrupt packets at AP MLD
void DoSetup() override
Implementation to do any local setup required for this TestCase.
EmlOmnExchangeTest(const std::set< uint8_t > &linksToEnableEmlsrOn, Time transitionTimeout)
Constructor.
std::size_t m_emlNotificationDroppedCount
counter for the number of times the EML Notification frame sent by the non-AP MLD has been dropped du...
void CheckEmlsrLinks()
Check that the EMLSR mode has been enabled on the expected EMLSR links.
void CheckEmlCapabilitiesInAssocReq(Ptr< const WifiMpdu > mpdu, const WifiTxVector &txVector, uint8_t linkId)
Check the content of the EML Capabilities subfield of the Multi-Link Element included in the Associat...
void TxDropped(WifiMacDropReason reason, Ptr< const WifiMpdu > mpdu)
Callback invoked when the non-AP MLD drops the given MPDU for the given reason.
void CheckEmlNotification(Ptr< const WifiPsdu > psdu, const WifiTxVector &txVector, uint8_t linkId)
Check the content of a received EML Operating Mode Notification frame.
void Transmit(Ptr< WifiMac > mac, uint8_t phyId, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW) override
Callback invoked when a FEM passes PSDUs to the PHY.
void DoRun() override
Implementation to actually run this TestCase.
Test EML Operating Mode Notification frame serialization and deserialization.
EmlOperatingModeNotificationTest()
Constructor.
void DoRun() override
Implementation to actually run this TestCase.
Test the transmission of DL frames to EMLSR clients.
void CheckInitialControlFrame(Ptr< const WifiMpdu > mpdu, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken by the AP MLD transmitting an initial Control frame to an EM...
const Time m_fe2to3delay
time interval between 2nd and 3rd frame exchange sequences after the enablement of EMLSR mode
void CheckResults()
Check that the simulation produced the expected results.
void CheckPmModeAfterAssociation(const Mac48Address &address)
Check that the AP MLD considers the correct Power Management mode for the links setup with the given ...
EmlsrDlTxopTest(const Params &params)
Constructor.
void StartTraffic() override
Start the generation of traffic (needs to be overridden)
Ptr< ListErrorModel > m_errorModel
error rate model to corrupt BlockAck at AP MLD
void CheckStaEmlNotificationFrame(Ptr< const WifiMpdu > mpdu, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken when an EMLSR client transmits an EML Operating Mode Notific...
std::size_t m_countQoSframes
counter for QoS frames (transition delay test)
void CheckApEmlNotificationFrame(Ptr< const WifiMpdu > mpdu, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken when the AP MLD transmits an EML Operating Mode Notification...
Time m_emlsrEnabledTime
when EMLSR mode has been enabled on all EMLSR clients
std::set< uint8_t > m_emlsrLinks
IDs of the links on which EMLSR mode has to be enabled.
void DoSetup() override
Implementation to do any local setup required for this TestCase.
void CheckQosFrames(const WifiConstPsduMap &psduMap, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken by the AP MLD transmitting a PPDU containing QoS data frames...
void Transmit(Ptr< WifiMac > mac, uint8_t phyId, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW) override
Callback invoked when a FEM passes PSDUs to the PHY.
void EnableEmlsrMode()
Enable EMLSR mode on the next EMLSR client.
void CheckBlockAck(const WifiConstPsduMap &psduMap, const WifiTxVector &txVector, uint8_t phyId)
Check that appropriate actions are taken by the AP MLD receiving a PPDU containing BlockAck frames fr...
void DoRun() override
Implementation to actually run this TestCase.
std::size_t m_countBlockAck
counter for BlockAck frames (transition delay test)
Base class for EMLSR Operations tests.
std::size_t m_nNonEmlsrStations
number of stations to create that do not activate EMLSR
void SetSsid(uint16_t aid, Mac48Address)
Set the SSID on the next station that needs to start the association procedure.
bool m_establishBaDl
whether BA needs to be established (for TID 0) with the AP as originator
void CheckBlockedLink(Ptr< WifiMac > mac, Mac48Address dest, uint8_t linkId, WifiQueueBlockedReason reason, bool blocked, std::string description, bool testUnblockedForOtherReasons=true)
Check whether QoS data unicast transmissions addressed to the given destination on the given link are...
std::size_t m_nEmlsrStations
number of stations to create that activate EMLSR
std::vector< PacketSocketAddress > m_dlSockets
packet socket address for DL traffic
std::vector< Time > m_paddingDelay
Padding Delay advertised by the non-AP MLD.
std::set< uint8_t > m_linksToEnableEmlsrOn
IDs of the links on which EMLSR mode has to be enabled.
Ptr< ApWifiMac > m_apMac
AP wifi MAC.
TrafficDirection
Enumeration for traffic directions.
void DoSetup() override
Implementation to do any local setup required for this TestCase.
uint8_t m_mainPhyId
ID of the main PHY.
Time m_duration
simulation duration
std::vector< FrameInfo > m_txPsdus
transmitted PSDUs
Ptr< PacketSocketClient > GetApplication(TrafficDirection dir, std::size_t staId, std::size_t count, std::size_t pktSize) const
virtual void Transmit(Ptr< WifiMac > mac, uint8_t phyId, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW)
Callback invoked when a FEM passes PSDUs to the PHY.
bool m_establishBaUl
whether BA needs to be established (for TID 0) with the AP as recipient
uint16_t m_lastAid
AID of last associated station.
std::vector< Time > m_transitionDelay
Transition Delay advertised by the non-AP MLD.
Time m_transitionTimeout
Transition Timeout advertised by the AP MLD.
std::vector< PacketSocketAddress > m_ulSockets
packet socket address for UL traffic
std::vector< Ptr< StaWifiMac > > m_staMacs
MACs of the non-AP MLDs.
virtual void StartTraffic()
Start the generation of traffic (needs to be overridden)
EmlsrOperationsTestBase(const std::string &name)
Constructor.
Test the transmission of UL frames from EMLSR clients.
std::size_t m_countQoSframes
counter for QoS frames
const Time m_unblockMainPhyLinkDelay
delay between the time the first two UL packets are generated and the time transmissions are unblocke...
Ptr< ListErrorModel > m_errorModel
error rate model to corrupt packets
uint16_t m_channelWidth
width (MHz) of the channels used by MLDs
void BackoffGenerated(uint32_t backoff, uint8_t linkId)
Callback invoked when a new backoff value is generated by the EMLSR client.
std::optional< uint8_t > m_nonEmlsrLink
ID of the non-EMLSR link (if any)
Time m_lastMsdExpiryTime
expiry time of the last MediumSyncDelay timer
void DoSetup() override
Implementation to do any local setup required for this TestCase.
std::size_t m_countRtsframes
counter for RTS frames
void CheckCtsFrames(Ptr< const WifiMpdu > mpdu, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken by the EMLSR client when receiving a CTS frame on the given ...
void DoRun() override
Implementation to actually run this TestCase.
Time m_firstUlPktsGenTime
generation time of the first two UL packets
std::optional< bool > m_corruptCts
whether the transmitted CTS must be corrupted
void CheckBlockAck(const WifiConstPsduMap &psduMap, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken when an MLD transmits a PPDU containing BlockAck frames on t...
void StartTraffic() override
Start the generation of traffic (needs to be overridden)
std::optional< Time > m_backoffEndTime
expected backoff end time on main PHY link
std::set< uint8_t > m_emlsrLinks
IDs of the links on which EMLSR mode has to be enabled.
Time m_mediumSyncDuration
duration of the MediumSyncDelay timer
void CheckRtsFrames(Ptr< const WifiMpdu > mpdu, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken by the EMLSR client when transmitting an RTS frame on the gi...
void CheckQosFrames(const WifiConstPsduMap &psduMap, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken when an MLD transmits a PPDU containing QoS data frames on t...
bool m_genBackoffIfTxopWithoutTx
whether the backoff should be invoked when the AC gains the right to start a TXOP but it does not tra...
void Transmit(Ptr< WifiMac > mac, uint8_t phyId, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW) override
Callback invoked when a FEM passes PSDUs to the PHY.
bool m_checkBackoffStarted
whether we are checking the generated backoff values
std::size_t m_countBlockAck
counter for BlockAck frames
void CheckResults()
Check that the simulation produced the expected results.
uint8_t m_msdMaxNTxops
Max number of TXOPs that an EMLSR client is allowed to attempt to initiate while the MediumSyncDelay ...
EmlsrUlTxopTest(const Params &params)
Constructor.
uint16_t m_auxPhyChannelWidth
max width (MHz) supported by aux PHYs
wifi EMLSR Test Suite
std::optional< Mac48Address > GetMldOrLinkAddressByAid(uint16_t aid) const
A container for one type of attribute.
Headers for Trigger frames.
Definition: ctrl-headers.h:942
bool IsMuRts() const
Check if this is a MU-RTS Trigger frame.
void SetPaddingSize(std::size_t size)
Set the size in bytes of the Padding field.
Hold variables of type enum.
Definition: enum.h:62
uint32_t GetUid() const
Definition: event-id.cc:110
Subclass of TestCase class adding the ability to test the serialization and deserialization of a Head...
void TestHeaderSerialization(const T &hdr, Args &&... args)
Serialize the given header in a buffer, then create a new header by deserializing from the buffer and...
void SetList(const std::list< uint64_t > &packetlist)
Definition: error-model.cc:456
an EUI-48 address
Definition: mac48-address.h:46
static Mac48Address GetBroadcast()
Implement the header for management frames of type association request.
Definition: mgt-headers.h:162
Implement the header for management frames of type association and reassociation response.
Definition: mgt-headers.h:339
Implement the header for Action frames of type EML Operating Mode Notification.
void SetLinkIdInBitmap(uint8_t linkId)
Set the bit position in the link bitmap corresponding to the given link.
EmlControl m_emlControl
EML Control field.
std::optional< EmlsrParamUpdate > m_emlsrParamUpdate
EMLSR Parameter Update field.
std::list< uint8_t > GetLinkBitmap() const
Helper class used to assign positions and mobility models to nodes.
holds a vector of ns3::NetDevice pointers
Ptr< NetDevice > Get(uint32_t i) const
Get the Ptr<NetDevice> stored in this container at a given index.
keep track of a set of node pointers.
uint32_t AddApplication(Ptr< Application > application)
Associate an Application to this Node.
Definition: node.cc:164
static Iterator Begin()
Definition: node-list.cc:237
static Iterator End()
Definition: node-list.cc:244
bool TraceConnectWithoutContext(std::string name, const CallbackBase &cb)
Connect a TraceSource to a Callback without a context.
Definition: object-base.cc:322
void SetAttribute(std::string name, const AttributeValue &value)
Set a single attribute, raising fatal errors if unsuccessful.
Definition: object-base.cc:211
void AggregateObject(Ptr< Object > other)
Aggregate two Objects together.
Definition: object.cc:309
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:77
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:571
static void Destroy()
Execute the events scheduled with ScheduleDestroy().
Definition: simulator.cc:142
static Time Now()
Return the current simulation virtual time.
Definition: simulator.cc:208
static void Run()
Run the simulation.
Definition: simulator.cc:178
static EventId ScheduleNow(FUNC f, Ts &&... args)
Schedule an event to expire Now.
Definition: simulator.h:605
static void Stop()
Tell the Simulator the calling event should be the last one executed.
Definition: simulator.cc:186
Make it easy to create and manage PHY objects for the spectrum model.
void AddChannel(const Ptr< SpectrumChannel > channel, const FrequencyRange &freqRange=WHOLE_WIFI_SPECTRUM)
The IEEE 802.11 SSID Information Element.
Definition: ssid.h:36
Hold variables of type string.
Definition: string.h:56
encapsulates test code
Definition: test.h:1061
void AddTestCase(TestCase *testCase, Duration duration=Duration::QUICK)
Add an individual child TestCase to this test suite.
Definition: test.cc:302
A suite of tests to run.
Definition: test.h:1273
Type
Type of test.
Definition: test.h:1280
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:105
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition: time.cc:415
bool IsStrictlyPositive() const
Exactly equivalent to t > 0.
Definition: nstime.h:351
@ US
microsecond
Definition: nstime.h:118
@ MS
millisecond
Definition: nstime.h:117
bool IsZero() const
Exactly equivalent to t == 0.
Definition: nstime.h:315
void SetTxopLimits(const std::vector< Time > &txopLimits)
Set the TXOP limit for each link.
Definition: txop.cc:478
Hold an unsigned integer type.
Definition: uinteger.h:45
See IEEE 802.11 chapter 7.3.1.11 Header format: | category: 1 | action value: 1 |.
static std::pair< CategoryValue, ActionValue > Peek(Ptr< const Packet > pkt)
Peek an Action header from the given packet.
void Print(std::ostream &os) const override
static std::pair< CategoryValue, ActionValue > Remove(Ptr< Packet > pkt)
Remove an Action header from the given packet.
helps to create WifiNetDevice objects
Definition: wifi-helper.h:324
Implements the IEEE 802.11 MAC header.
create MAC layers for a ns3::WifiNetDevice.
Ptr< FrameExchangeManager > GetFrameExchangeManager(uint8_t linkId=SINGLE_LINK_OP_ID) const
Get the Frame Exchange Manager associated with the given link.
Definition: wifi-mac.cc:981
Ptr< WifiMacQueueScheduler > GetMacQueueScheduler() const
Get the wifi MAC queue scheduler.
Definition: wifi-mac.cc:670
uint8_t GetNLinks() const
Get the number of links (can be greater than 1 for 11be devices only).
Definition: wifi-mac.cc:1072
void UnblockUnicastTxOnLinks(WifiQueueBlockedReason reason, const Mac48Address &address, const std::set< uint8_t > &linkIds)
Unblock the transmission on the given links of all unicast frames addressed to the station with the g...
Definition: wifi-mac.cc:1558
Ptr< WifiPhy > GetWifiPhy(uint8_t linkId=SINGLE_LINK_OP_ID) const
Definition: wifi-mac.cc:1315
void BlockUnicastTxOnLinks(WifiQueueBlockedReason reason, const Mac48Address &address, const std::set< uint8_t > &linkIds)
Block the transmission on the given links of all unicast frames addressed to the station with the giv...
Definition: wifi-mac.cc:1512
Ptr< WifiNetDevice > GetDevice() const
Return the device this PHY is associated with.
Definition: wifi-mac.cc:483
Ptr< WifiRemoteStationManager > GetWifiRemoteStationManager(uint8_t linkId=0) const
Definition: wifi-mac.cc:1045
Mac48Address GetAddress() const
Definition: wifi-mac.cc:496
Ptr< QosTxop > GetQosTxop(AcIndex ac) const
Accessor for a specified EDCA object.
Definition: wifi-mac.cc:593
uint64_t GetDataRate(uint16_t channelWidth, uint16_t guardInterval, uint8_t nss) const
Definition: wifi-mode.cc:122
uint32_t GetIfIndex() const override
Address GetAddress() const override
Ptr< Node > GetNode() const override
void SetPcapDataLinkType(SupportedPcapDataLinkTypes dlt)
Set the data link type of PCAP traces to be used.
Definition: wifi-helper.cc:543
void Set(std::string name, const AttributeValue &v)
Definition: wifi-helper.cc:163
@ DLT_IEEE802_11_RADIO
Include Radiotap link layer information.
Definition: wifi-helper.h:178
void SetPostReceptionErrorModel(const Ptr< ErrorModel > em)
Attach a receive ErrorModel to the WifiPhy.
Definition: wifi-phy.cc:679
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition: wifi-phy.cc:1539
WifiPhyBand GetPhyBand() const
Get the configured Wi-Fi band.
Definition: wifi-phy.cc:1053
std::tuple< uint8_t, uint16_t, WifiPhyBand, uint8_t > ChannelTuple
Tuple identifying an operating channel.
Definition: wifi-phy.h:926
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
WifiMode GetMode(uint16_t staId=SU_STA_ID) const
If this TX vector is associated with an SU PPDU, return the selected payload transmission mode.
WifiPreamble GetPreambleType() const
uint16_t GetChannelWidth() const
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition: assert.h:86
void SetDefault(std::string name, const AttributeValue &value)
Definition: config.cc:894
void ConnectWithoutContext(std::string path, const CallbackBase &cb)
Definition: config.cc:954
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition: abort.h:108
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:268
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:275
#define NS_TEST_EXPECT_MSG_GT_OR_EQ(actual, limit, msg)
Test that an actual value is greater than or equal to limit and report if not.
Definition: test.h:997
#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:145
#define NS_TEST_EXPECT_MSG_LT_OR_EQ(actual, limit, msg)
Test that an actual value is less than or equal to a limit and report if not.
Definition: test.h:831
#define NS_TEST_EXPECT_MSG_LT(actual, limit, msg)
Test that an actual value is less than a limit and report if not.
Definition: test.h:791
#define NS_TEST_EXPECT_MSG_GT(actual, limit, msg)
Test that an actual value is greater than a limit and report if not.
Definition: test.h:957
#define NS_TEST_EXPECT_MSG_NE(actual, limit, msg)
Test that an actual and expected (limit) value are not equal and report if not.
Definition: test.h:667
#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:252
#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:565
#define NS_TEST_ASSERT_MSG_GT_OR_EQ(actual, limit, msg)
Test that an actual value is greater than or equal to a limit and report and abort if not.
Definition: test.h:916
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1343
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1355
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1319
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1331
WifiMacDropReason
The reason why an MPDU was dropped.
Definition: wifi-mac.h:80
WifiQueueBlockedReason
Enumeration of the reasons to block container queues.
@ WIFI_STANDARD_80211be
@ WIFI_PREAMBLE_HT_MF
@ WIFI_PHY_BAND_6GHZ
The 6 GHz band.
Definition: wifi-phy-band.h:39
@ WIFI_PHY_BAND_2_4GHZ
The 2.4 GHz band.
Definition: wifi-phy-band.h:35
@ WIFI_PHY_BAND_5GHZ
The 5 GHz band.
Definition: wifi-phy-band.h:37
@ AC_BE
Best Effort.
Definition: qos-utils.h:75
Every class exported by the ns3 library is enclosed in the ns3 namespace.
constexpr FrequencyRange WIFI_SPECTRUM_6_GHZ
Identifier for the frequency range covering the wifi spectrum in the 6 GHz band.
U * PeekPointer(const Ptr< U > &p)
Definition: ptr.h:454
std::unordered_map< uint16_t, Ptr< const WifiPsdu > > WifiConstPsduMap
Map of const PSDUs indexed by STA-ID.
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:700
std::tuple< WifiContainerQueueType, WifiReceiverAddressType, Mac48Address, std::optional< uint8_t > > WifiContainerQueueId
Tuple (queue type, receiver address type, Address, TID) identifying a container queue.
bool IsTrigger(const WifiPsduMap &psduMap)
constexpr FrequencyRange WIFI_SPECTRUM_5_GHZ
Identifier for the frequency range covering the wifi spectrum in the 5 GHz band.
@ WIFI_MAC_CTL_TRIGGER
@ WIFI_MAC_CTL_RTS
@ WIFI_MAC_CTL_CTS
@ WIFI_MAC_MGT_ACTION
@ WIFI_MAC_MGT_ASSOCIATION_RESPONSE
@ WIFI_MAC_MGT_ASSOCIATION_REQUEST
@ WIFI_MAC_CTL_BACKRESP
@ WIFI_MAC_CTL_END
@ WIFI_MAC_QOSDATA
uint32_t GetAckSize()
Return the total Ack size (including FCS trailer).
Definition: wifi-utils.cc:60
static constexpr uint16_t SU_STA_ID
STA_ID to identify a single user (SU)
Definition: wifi-mode.h:35
constexpr FrequencyRange WIFI_SPECTRUM_2_4_GHZ
Identifier for the frequency range covering the wifi spectrum in the 2.4 GHz band.
STL namespace.
ns phy
Definition: third.py:89
ns3::Time timeout
Parameters for the EMLSR DL TXOP test.
Parameters for the EMLSR UL TXOP test.
static uint8_t EncodeEmlsrTransitionDelay(Time delay)
static Time DecodeEmlsrTransitionDelay(uint8_t value)
static Time DecodeEmlsrPaddingDelay(uint8_t value)
static uint8_t EncodeEmlsrPaddingDelay(Time delay)
uint8_t emlsrParamUpdateCtrl
EMLSR Parameter Update Control.
std::optional< uint16_t > linkBitmap
EMLSR/EMLMR Link Bitmap.
EMLSR Parameter Update field.
std::string dir
uint32_t pktSize
packet size used for the simulation (in bytes)
static WifiEmlsrTestSuite g_wifiEmlsrTestSuite
the test suite