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 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Author: Stefano Avallone <stavallo@unina.it>
7 */
8
9#include "wifi-emlsr-test.h"
10
11#include "ns3/attribute-container.h"
12#include "ns3/boolean.h"
13#include "ns3/config.h"
14#include "ns3/ctrl-headers.h"
15#include "ns3/eht-configuration.h"
16#include "ns3/emlsr-manager.h"
17#include "ns3/he-frame-exchange-manager.h"
18#include "ns3/log.h"
19#include "ns3/mgt-action-headers.h"
20#include "ns3/mobility-helper.h"
21#include "ns3/multi-model-spectrum-channel.h"
22#include "ns3/node-list.h"
23#include "ns3/packet-socket-helper.h"
24#include "ns3/packet-socket-server.h"
25#include "ns3/qos-txop.h"
26#include "ns3/rng-seed-manager.h"
27#include "ns3/rr-multi-user-scheduler.h"
28#include "ns3/simulator.h"
29#include "ns3/spectrum-wifi-helper.h"
30#include "ns3/spectrum-wifi-phy.h"
31#include "ns3/string.h"
32#include "ns3/wifi-net-device.h"
33
34#include <algorithm>
35#include <functional>
36#include <iomanip>
37#include <optional>
38
39using namespace ns3;
40
41NS_LOG_COMPONENT_DEFINE("WifiEmlsrTest");
42
45 "Check serialization and deserialization of the EML Operating Mode Notification frame")
46{
47}
48
49void
51{
52 MgtEmlOmn frame;
53
54 // Both EMLSR Mode and EMLMR Mode subfields set to 0 (no link bitmap);
56
57 frame.m_emlControl.emlsrMode = 1;
58 frame.SetLinkIdInBitmap(0);
59 frame.SetLinkIdInBitmap(5);
60 frame.SetLinkIdInBitmap(15);
61
62 // Adding Link Bitmap
64
65 NS_TEST_EXPECT_MSG_EQ((frame.GetLinkBitmap() == std::list<uint8_t>{0, 5, 15}),
66 true,
67 "Unexpected link bitmap");
68
69 auto padding = MicroSeconds(64);
70 auto transition = MicroSeconds(128);
71
75 frame.m_emlsrParamUpdate->transitionDelay =
77
78 // Adding the EMLSR Parameter Update field
80
83 padding,
84 "Unexpected EMLSR Padding Delay");
87 transition,
88 "Unexpected EMLSR Transition Delay");
89}
90
92 : TestCase(name)
93{
94}
95
96void
98 uint8_t phyId,
99 WifiConstPsduMap psduMap,
100 WifiTxVector txVector,
101 double txPowerW)
102{
103 auto linkId = mac->GetLinkForPhy(phyId);
104 NS_TEST_ASSERT_MSG_EQ(linkId.has_value(), true, "No link found for PHY ID " << +phyId);
105 m_txPsdus.push_back({Simulator::Now(), psduMap, txVector, *linkId, phyId});
106
107 auto txDuration =
108 WifiPhy::CalculateTxDuration(psduMap, txVector, mac->GetWifiPhy(*linkId)->GetPhyBand());
109
110 for (const auto& [aid, psdu] : psduMap)
111 {
112 std::stringstream ss;
113 ss << std::setprecision(10) << "PSDU #" << m_txPsdus.size() << " Link ID "
114 << +linkId.value() << " Phy ID " << +phyId << " " << psdu->GetHeader(0).GetTypeString();
115 if (psdu->GetHeader(0).IsAction())
116 {
117 ss << " ";
118 WifiActionHeader actionHdr;
119 psdu->GetPayload(0)->PeekHeader(actionHdr);
120 actionHdr.Print(ss);
121 }
122 ss << " #MPDUs " << psdu->GetNMpdus() << " duration/ID " << psdu->GetHeader(0).GetDuration()
123 << " RA = " << psdu->GetAddr1() << " TA = " << psdu->GetAddr2()
124 << " ADDR3 = " << psdu->GetHeader(0).GetAddr3()
125 << " ToDS = " << psdu->GetHeader(0).IsToDs()
126 << " FromDS = " << psdu->GetHeader(0).IsFromDs();
127 if (psdu->GetHeader(0).IsQosData())
128 {
129 ss << " seqNo = {";
130 for (auto& mpdu : *PeekPointer(psdu))
131 {
132 ss << mpdu->GetHeader().GetSequenceNumber() << ",";
133 }
134 ss << "} TID = " << +psdu->GetHeader(0).GetQosTid();
135 }
136 NS_LOG_INFO(ss.str());
137 }
138 NS_LOG_INFO("TX duration = " << txDuration.As(Time::MS) << " TXVECTOR = " << txVector << "\n");
139}
140
141void
143{
146 int64_t streamNumber = 100;
147
148 Config::SetDefault("ns3::WifiMac::MpduBufferSize", UintegerValue(64));
149
150 NodeContainer wifiApNode(1);
151 NodeContainer wifiStaNodes(m_nEmlsrStations);
152
153 WifiHelper wifi;
154 // wifi.EnableLogComponents ();
155 wifi.SetStandard(WIFI_STANDARD_80211be);
156 wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
157 "DataMode",
158 StringValue("EhtMcs0"),
159 "ControlMode",
160 StringValue("HtMcs0"));
161 wifi.ConfigEhtOptions("EmlsrActivated",
162 BooleanValue(true),
163 "TransitionTimeout",
165
166 // MLDs are configured with three links
167 SpectrumWifiPhyHelper phyHelper(3);
169 phyHelper.SetPcapCaptureType(WifiPhyHelper::PcapCaptureType::PCAP_PER_LINK);
170 phyHelper.Set(0, "ChannelSettings", StringValue("{2, 0, BAND_2_4GHZ, 0}"));
171 phyHelper.Set(1, "ChannelSettings", StringValue("{36, 0, BAND_5GHZ, 0}"));
172 phyHelper.Set(2, "ChannelSettings", StringValue("{1, 0, BAND_6GHZ, 0}"));
173 // Add three spectrum channels to use multi-RF interface
177
178 WifiMacHelper mac;
179 mac.SetType("ns3::ApWifiMac",
180 "Ssid",
181 SsidValue(Ssid("ns-3-ssid")),
182 "BeaconGeneration",
183 BooleanValue(true));
184
185 NetDeviceContainer apDevice = wifi.Install(phyHelper, mac, wifiApNode);
186
187 mac.SetType("ns3::StaWifiMac",
188 "Ssid",
189 SsidValue(Ssid("wrong-ssid")),
190 "MaxMissedBeacons",
191 UintegerValue(1e6), // do not deassociate
192 "ActiveProbing",
193 BooleanValue(false));
194 mac.SetEmlsrManager("ns3::AdvancedEmlsrManager",
195 "EmlsrLinkSet",
197 "MainPhyId",
199
200 NetDeviceContainer staDevices = wifi.Install(phyHelper, mac, wifiStaNodes);
201
203
204 for (uint32_t i = 0; i < staDevices.GetN(); i++)
205 {
206 auto device = DynamicCast<WifiNetDevice>(staDevices.Get(i));
207 auto staMac = DynamicCast<StaWifiMac>(device->GetMac());
208 NS_ASSERT_MSG(i < m_paddingDelay.size(), "Not enough padding delay values provided");
209 staMac->GetEmlsrManager()->SetAttribute("EmlsrPaddingDelay",
211 NS_ASSERT_MSG(i < m_transitionDelay.size(), "Not enough transition delay values provided");
212 staMac->GetEmlsrManager()->SetAttribute("EmlsrTransitionDelay",
214 }
215
216 if (m_nNonEmlsrStations > 0)
217 {
218 // create the other non-AP MLDs for which EMLSR is not activated
219 wifi.ConfigEhtOptions("EmlsrActivated", BooleanValue(false));
220 NodeContainer otherStaNodes(m_nNonEmlsrStations);
221 staDevices.Add(wifi.Install(phyHelper, mac, otherStaNodes));
222 wifiStaNodes.Add(otherStaNodes);
223 }
224
225 for (uint32_t i = 0; i < staDevices.GetN(); i++)
226 {
227 auto device = DynamicCast<WifiNetDevice>(staDevices.Get(i));
228 m_staMacs.push_back(DynamicCast<StaWifiMac>(device->GetMac()));
229 }
230
231 // Trace PSDUs passed to the PHY on AP MLD and non-AP MLDs
232 for (uint8_t phyId = 0; phyId < m_apMac->GetDevice()->GetNPhys(); phyId++)
233 {
235 "/NodeList/0/DeviceList/*/$ns3::WifiNetDevice/Phys/" + std::to_string(phyId) +
236 "/PhyTxPsduBegin",
238 }
239 for (std::size_t i = 0; i < m_nEmlsrStations + m_nNonEmlsrStations; i++)
240 {
241 for (uint8_t phyId = 0; phyId < m_staMacs[i]->GetDevice()->GetNPhys(); phyId++)
242 {
244 "/NodeList/" + std::to_string(i + 1) + "/DeviceList/*/$ns3::WifiNetDevice/Phys/" +
245 std::to_string(phyId) + "/PhyTxPsduBegin",
247 }
248 }
249
250 // Uncomment the lines below to write PCAP files
251 // phyHelper.EnablePcap("wifi-emlsr_AP", apDevice);
252 // phyHelper.EnablePcap("wifi-emlsr_STA", staDevices);
253
254 // Assign fixed streams to random variables in use
255 streamNumber += WifiHelper::AssignStreams(apDevice, streamNumber);
256 streamNumber += WifiHelper::AssignStreams(staDevices, streamNumber);
257
258 MobilityHelper mobility;
260
261 for (std::size_t id = 0; id <= m_nEmlsrStations + m_nNonEmlsrStations; id++)
262 {
263 // all non-AP MLDs are co-located
264 positionAlloc->Add(Vector(std::min<double>(id, 1), 0.0, 0.0));
265 }
266 mobility.SetPositionAllocator(positionAlloc);
267
268 mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
269 mobility.Install(wifiApNode);
270 mobility.Install(wifiStaNodes);
271
272 // install packet socket on all nodes
273 PacketSocketHelper packetSocket;
274 packetSocket.Install(wifiApNode);
275 packetSocket.Install(wifiStaNodes);
276
277 // install a packet socket server on all nodes
278 for (auto nodeIt = NodeList::Begin(); nodeIt != NodeList::End(); nodeIt++)
279 {
280 PacketSocketAddress srvAddr;
281 auto device = DynamicCast<WifiNetDevice>((*nodeIt)->GetDevice(0));
282 NS_TEST_ASSERT_MSG_NE(device, nullptr, "Expected a WifiNetDevice");
283 srvAddr.SetSingleDevice(device->GetIfIndex());
284 srvAddr.SetProtocol(1);
285
286 auto server = CreateObject<PacketSocketServer>();
287 server->SetLocal(srvAddr);
288 (*nodeIt)->AddApplication(server);
289 server->SetStartTime(Seconds(0)); // now
290 server->SetStopTime(m_duration);
291 }
292
293 // set DL and UL packet sockets
294 for (const auto& staMac : m_staMacs)
295 {
296 m_dlSockets.emplace_back();
297 m_dlSockets.back().SetSingleDevice(m_apMac->GetDevice()->GetIfIndex());
298 m_dlSockets.back().SetPhysicalAddress(staMac->GetDevice()->GetAddress());
299 m_dlSockets.back().SetProtocol(1);
300
301 m_ulSockets.emplace_back();
302 m_ulSockets.back().SetSingleDevice(staMac->GetDevice()->GetIfIndex());
303 m_ulSockets.back().SetPhysicalAddress(m_apMac->GetDevice()->GetAddress());
304 m_ulSockets.back().SetProtocol(1);
305 }
306
307 // schedule ML setup for one station at a time
308 m_apMac->TraceConnectWithoutContext("AssociatedSta",
310 Simulator::Schedule(Seconds(0), [&]() { m_staMacs[0]->SetSsid(Ssid("ns-3-ssid")); });
311}
312
315 std::size_t staId,
316 std::size_t count,
317 std::size_t pktSize) const
318{
319 auto client = CreateObject<PacketSocketClient>();
320 client->SetAttribute("PacketSize", UintegerValue(pktSize));
321 client->SetAttribute("MaxPackets", UintegerValue(count));
322 client->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
323 client->SetRemote(dir == DOWNLINK ? m_dlSockets.at(staId) : m_ulSockets.at(staId));
324 client->SetStartTime(Seconds(0)); // now
325 client->SetStopTime(m_duration - Simulator::Now());
326
327 return client;
328}
329
330void
332{
333 if (m_lastAid == aid)
334 {
335 // another STA of this non-AP MLD has already fired this callback
336 return;
337 }
338 m_lastAid = aid;
339
340 // wait some time (5ms) to allow the completion of association
341 auto delay = MilliSeconds(5);
342
343 if (m_establishBaDl)
344 {
345 // trigger establishment of BA agreement with AP as originator
346 Simulator::Schedule(delay, [=, this]() {
347 m_apMac->GetDevice()->GetNode()->AddApplication(
348 GetApplication(DOWNLINK, aid - 1, 4, 1000));
349 });
350
351 delay += MilliSeconds(5);
352 }
353
354 if (m_establishBaUl)
355 {
356 // trigger establishment of BA agreement with AP as recipient
357 Simulator::Schedule(delay, [=, this]() {
358 m_staMacs[aid - 1]->GetDevice()->GetNode()->AddApplication(
359 GetApplication(UPLINK, aid - 1, 4, 1000));
360 });
361
362 delay += MilliSeconds(5);
363 }
364
365 Simulator::Schedule(delay, [=, this]() {
367 {
368 // make the next STA start ML discovery & setup
369 m_staMacs[aid]->SetSsid(Ssid("ns-3-ssid"));
370 return;
371 }
372 // all stations associated; start traffic if needed
373 StartTraffic();
374 // stop generation of beacon frames in order to avoid interference
375 m_apMac->SetAttribute("BeaconGeneration", BooleanValue(false));
376 });
377}
378
379void
381 Mac48Address dest,
382 uint8_t linkId,
384 bool blocked,
385 std::string description,
386 bool testUnblockedForOtherReasons)
387{
389 auto mask = mac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, linkId);
390 NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
391 true,
392 description << ": Expected to find a mask for EMLSR link " << +linkId);
393 if (blocked)
394 {
395 NS_TEST_EXPECT_MSG_EQ(mask->test(static_cast<std::size_t>(reason)),
396 true,
397 description << ": Expected EMLSR link " << +linkId
398 << " to be blocked for reason " << reason);
399 if (testUnblockedForOtherReasons)
400 {
401 NS_TEST_EXPECT_MSG_EQ(mask->count(),
402 1,
403 description << ": Expected EMLSR link " << +linkId
404 << " to be blocked for one reason only");
405 }
406 }
407 else if (testUnblockedForOtherReasons)
408 {
409 NS_TEST_EXPECT_MSG_EQ(mask->none(),
410 true,
411 description << ": Expected EMLSR link " << +linkId
412 << " to be unblocked");
413 }
414 else
415 {
416 NS_TEST_EXPECT_MSG_EQ(mask->test(static_cast<std::size_t>(reason)),
417 false,
418 description << ": Expected EMLSR link " << +linkId
419 << " to be unblocked for reason " << reason);
420 }
421}
422
423EmlOmnExchangeTest::EmlOmnExchangeTest(const std::set<uint8_t>& linksToEnableEmlsrOn,
424 Time transitionTimeout)
425 : EmlsrOperationsTestBase("Check EML Notification exchange"),
426 m_checkEmlsrLinksCount(0),
427 m_emlNotificationDroppedCount(0)
428{
429 m_linksToEnableEmlsrOn = linksToEnableEmlsrOn;
432 m_transitionTimeout = transitionTimeout;
433 m_duration = Seconds(0.5);
434}
435
436void
438{
440
442 for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
443 {
444 m_apMac->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_errorModel);
445 }
446
447 m_staMacs[0]->TraceConnectWithoutContext("AckedMpdu",
449 m_staMacs[0]->TraceConnectWithoutContext("DroppedMpdu",
451}
452
453void
455 uint8_t phyId,
456 WifiConstPsduMap psduMap,
457 WifiTxVector txVector,
458 double txPowerW)
459{
460 EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
461 auto linkId = m_txPsdus.back().linkId;
462
463 auto psdu = psduMap.begin()->second;
464
465 switch (psdu->GetHeader(0).GetType())
466 {
468 NS_TEST_EXPECT_MSG_EQ(+linkId, +m_mainPhyId, "AssocReq not sent by the main PHY");
469 CheckEmlCapabilitiesInAssocReq(*psdu->begin(), txVector, linkId);
470 break;
471
473 CheckEmlCapabilitiesInAssocResp(*psdu->begin(), txVector, linkId);
474 break;
475
477 if (auto [category, action] = WifiActionHeader::Peek(psdu->GetPayload(0));
479 action.protectedEhtAction ==
481 {
482 CheckEmlNotification(psdu, txVector, linkId);
483
485 m_staMacs[0]->GetLinkIdByAddress(psdu->GetAddr2()) == linkId)
486 {
487 // transmitted by non-AP MLD, we need to corrupt it
488 m_uidList.push_front(psdu->GetPacket()->GetUid());
490 }
491 break;
492 }
493
494 default:;
495 }
496}
497
498void
500 const WifiTxVector& txVector,
501 uint8_t linkId)
502{
504 mpdu->GetPacket()->PeekHeader(frame);
505
506 const auto& mle = frame.Get<MultiLinkElement>();
507 NS_TEST_ASSERT_MSG_EQ(mle.has_value(), true, "Multi-Link Element must be present in AssocReq");
508
509 NS_TEST_ASSERT_MSG_EQ(mle->HasEmlCapabilities(),
510 true,
511 "Multi-Link Element in AssocReq must have EML Capabilities");
512 NS_TEST_ASSERT_MSG_EQ(mle->IsEmlsrSupported(),
513 true,
514 "EML Support subfield of EML Capabilities in AssocReq must be set to 1");
515 NS_TEST_ASSERT_MSG_EQ(mle->GetEmlsrPaddingDelay(),
516 m_paddingDelay.at(0),
517 "Unexpected Padding Delay in EML Capabilities included in AssocReq");
518 NS_TEST_ASSERT_MSG_EQ(mle->GetEmlsrTransitionDelay(),
519 m_transitionDelay.at(0),
520 "Unexpected Transition Delay in EML Capabilities included in AssocReq");
521}
522
523void
525 const WifiTxVector& txVector,
526 uint8_t linkId)
527{
528 bool sentToEmlsrClient =
529 (m_staMacs[0]->GetLinkIdByAddress(mpdu->GetHeader().GetAddr1()) == linkId);
530
531 if (!sentToEmlsrClient)
532 {
533 // nothing to check
534 return;
535 }
536
538 mpdu->GetPacket()->PeekHeader(frame);
539
540 const auto& mle = frame.Get<MultiLinkElement>();
541 NS_TEST_ASSERT_MSG_EQ(mle.has_value(), true, "Multi-Link Element must be present in AssocResp");
542
543 NS_TEST_ASSERT_MSG_EQ(mle->HasEmlCapabilities(),
544 true,
545 "Multi-Link Element in AssocResp must have EML Capabilities");
546 NS_TEST_ASSERT_MSG_EQ(mle->IsEmlsrSupported(),
547 true,
548 "EML Support subfield of EML Capabilities in AssocResp must be set to 1");
550 mle->GetTransitionTimeout(),
552 "Unexpected Transition Timeout in EML Capabilities included in AssocResp");
553}
554
555void
557 const WifiTxVector& txVector,
558 uint8_t linkId)
559{
560 MgtEmlOmn frame;
561 auto mpdu = *psdu->begin();
562 auto pkt = mpdu->GetPacket()->Copy();
564 pkt->RemoveHeader(frame);
565 NS_LOG_DEBUG(frame);
566
567 bool sentbyNonApMld = m_staMacs[0]->GetLinkIdByAddress(mpdu->GetHeader().GetAddr2()) == linkId;
568
570 1,
571 "EMLSR Mode subfield should be set to 1 (frame sent by non-AP MLD: "
572 << std::boolalpha << sentbyNonApMld << ")");
573
575 0,
576 "EMLMR Mode subfield should be set to 0 (frame sent by non-AP MLD: "
577 << std::boolalpha << sentbyNonApMld << ")");
578
580 true,
581 "Link Bitmap subfield should be present (frame sent by non-AP MLD: "
582 << std::boolalpha << sentbyNonApMld << ")");
583
584 auto setupLinks = m_staMacs[0]->GetSetupLinkIds();
585 std::list<uint8_t> expectedEmlsrLinks;
586 std::set_intersection(setupLinks.begin(),
587 setupLinks.end(),
590 std::back_inserter(expectedEmlsrLinks));
591
592 NS_TEST_EXPECT_MSG_EQ((expectedEmlsrLinks == frame.GetLinkBitmap()),
593 true,
594 "Unexpected Link Bitmap subfield (frame sent by non-AP MLD: "
595 << std::boolalpha << sentbyNonApMld << ")");
596
597 if (!sentbyNonApMld)
598 {
599 // the frame has been sent by the AP MLD
602 0,
603 "EMLSR Parameter Update Control should be set to 0 in frames sent by the AP MLD");
604
605 // as soon as the non-AP MLD receives this frame, it sets the EMLSR links
606 auto delay = WifiPhy::CalculateTxDuration(psdu,
607 txVector,
608 m_staMacs[0]->GetWifiPhy(linkId)->GetPhyBand()) +
609 MicroSeconds(1); // to account for propagation delay
611 }
612
614 +linkId,
615 "EML Notification received on unexpected link (frame sent by non-AP MLD: "
616 << std::boolalpha << sentbyNonApMld << ")");
617}
618
619void
621{
622 const auto& hdr = mpdu->GetHeader();
623
624 if (hdr.IsMgt() && hdr.IsAction())
625 {
626 if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
628 action.protectedEhtAction ==
630 {
631 // the EML Operating Mode Notification frame that the non-AP MLD sent has been
632 // acknowledged; after the transition timeout, the EMLSR links have been set
635 this);
636 }
637 }
638}
639
640void
642{
643 const auto& hdr = mpdu->GetHeader();
644
645 if (hdr.IsMgt() && hdr.IsAction())
646 {
647 if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
649 action.protectedEhtAction ==
651 {
652 // the EML Operating Mode Notification frame has been dropped. Don't
653 // corrupt it anymore
655 }
656 }
657}
658
659void
661{
663
664 auto setupLinks = m_staMacs[0]->GetSetupLinkIds();
665 std::set<uint8_t> expectedEmlsrLinks;
666 std::set_intersection(setupLinks.begin(),
667 setupLinks.end(),
670 std::inserter(expectedEmlsrLinks, expectedEmlsrLinks.end()));
671
672 NS_TEST_EXPECT_MSG_EQ((expectedEmlsrLinks == m_staMacs[0]->GetEmlsrManager()->GetEmlsrLinks()),
673 true,
674 "Unexpected set of EMLSR links)");
675}
676
677void
679{
682
684 2,
685 "Unexpected number of times CheckEmlsrLinks() is called");
688 1,
689 "Unexpected number of times the EML Notification frame is dropped due to max retry limit");
690
692}
693
695 : EmlsrOperationsTestBase("Check EML DL TXOP transmissions (" +
696 std::to_string(params.nEmlsrStations) + "," +
697 std::to_string(params.nNonEmlsrStations) + ")"),
698 m_emlsrLinks(params.linksToEnableEmlsrOn),
699 m_emlsrEnabledTime(0),
700 m_fe2to3delay(MilliSeconds(20)),
701 m_countQoSframes(0),
702 m_countBlockAck(0)
703{
704 m_nEmlsrStations = params.nEmlsrStations;
705 m_nNonEmlsrStations = params.nNonEmlsrStations;
706 m_linksToEnableEmlsrOn = {}; // do not enable EMLSR right after association
707 m_mainPhyId = 1;
708 m_paddingDelay = params.paddingDelay;
709 m_transitionDelay = params.transitionDelay;
710 m_transitionTimeout = params.transitionTimeout;
711 m_establishBaDl = true;
712 m_duration = Seconds(1.5);
713
714 NS_ABORT_MSG_IF(params.linksToEnableEmlsrOn.size() < 2,
715 "This test requires at least two links to be configured as EMLSR links");
716}
717
718void
720 uint8_t phyId,
721 WifiConstPsduMap psduMap,
722 WifiTxVector txVector,
723 double txPowerW)
724{
725 EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
726 auto linkId = m_txPsdus.back().linkId;
727
728 auto psdu = psduMap.begin()->second;
729 auto nodeId = mac->GetDevice()->GetNode()->GetId();
730
731 switch (psdu->GetHeader(0).GetType())
732 {
734 NS_ASSERT_MSG(nodeId > 0, "APs do not send AssocReq frames");
735 if (nodeId <= m_nEmlsrStations)
736 {
737 NS_TEST_EXPECT_MSG_EQ(+linkId, +m_mainPhyId, "AssocReq not sent by the main PHY");
738 // this AssocReq is being sent by an EMLSR client. The other EMLSR links should be
739 // in powersave mode after association; we let the non-EMLSR links transition to
740 // active mode (by sending data null frames) after association
741 for (const auto id : m_staMacs.at(nodeId - 1)->GetLinkIds())
742 {
743 if (id != linkId && m_emlsrLinks.count(id) == 1)
744 {
745 m_staMacs[nodeId - 1]->SetPowerSaveMode({true, id});
746 }
747 }
748 }
749 break;
750
751 case WIFI_MAC_MGT_ACTION: {
752 auto [category, action] = WifiActionHeader::Peek(psdu->GetPayload(0));
753
754 if ((category == WifiActionHeader::PROTECTED_EHT) &&
755 (action.protectedEhtAction ==
757 {
758 nodeId == 0 ? CheckApEmlNotificationFrame(*psdu->begin(), txVector, linkId)
759 : CheckStaEmlNotificationFrame(*psdu->begin(), txVector, linkId);
760 }
761 else if (category == WifiActionHeader::BLOCK_ACK &&
763 {
764 CheckPmModeAfterAssociation(psdu->GetAddr1());
765 }
766 }
767 break;
768
770 CheckInitialControlFrame(*psdu->begin(), txVector, linkId);
771 break;
772
773 case WIFI_MAC_QOSDATA:
774 CheckQosFrames(psduMap, txVector, linkId);
775 break;
776
778 CheckBlockAck(psduMap, txVector, phyId);
779 break;
780
781 default:;
782 }
783}
784
785void
787{
788 // Channel switch delay should be less than the ICF padding duration, otherwise
789 // DL TXOPs cannot be initiated on non-primary links
790 auto delay = std::min(MicroSeconds(100),
791 *std::min_element(m_paddingDelay.cbegin(), m_paddingDelay.cend()));
792 Config::SetDefault("ns3::WifiPhy::ChannelSwitchDelay", TimeValue(MicroSeconds(75)));
793
795
797 for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
798 {
799 m_apMac->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_errorModel);
800 }
801
802 m_apMac->GetQosTxop(AC_BE)->SetTxopLimits(
803 {MicroSeconds(3200), MicroSeconds(3200), MicroSeconds(3200)});
804
806 {
807 auto muScheduler =
809 m_apMac->AggregateObject(muScheduler);
810 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
811 {
812 m_apMac->GetFrameExchangeManager(linkId)->GetAckManager()->SetAttribute(
813 "DlMuAckSequenceType",
815 }
816 }
817}
818
819void
821{
823 {
824 // we are done with association and Block Ack agreement; we can now enable EMLSR mode
825 m_lastAid = 0;
827 return;
828 }
829
830 // we are done with sending EML Operating Mode Notification frames. We can now generate
831 // packets for all non-AP MLDs
832 for (std::size_t i = 0; i < m_nEmlsrStations + m_nNonEmlsrStations; i++)
833 {
834 // when multiple non-AP MLDs are present, MU transmission are used. Given that the
835 // available bandwidth decreases as the number of non-AP MLDs increases, compute the
836 // number of packets to generate so that we always have two A-MPDUs per non-AP MLD
837 std::size_t count = 8 / (m_nEmlsrStations + m_nNonEmlsrStations);
838 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, i, count, 450));
839 }
840
841 // in case of 2 EMLSR clients using no non-EMLSR link, generate one additional short
842 // packet to each EMLSR client to test transition delay
843 if (m_nEmlsrStations == 2 && m_apMac->GetNLinks() == m_emlsrLinks.size())
844 {
846 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, 0, 1, 40));
847 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, 1, 1, 40));
848 });
849 }
850
851 // schedule the transmission of EML Operating Mode Notification frames to disable EMLSR mode
852 // and the generation of other packets destined to the EMLSR clients
853 for (std::size_t id = 0; id < m_nEmlsrStations; id++)
854 {
855 Simulator::Schedule(m_fe2to3delay + MilliSeconds(5 * (id + 1)), [=, this]() {
856 m_staMacs.at(id)->GetEmlsrManager()->SetAttribute(
857 "EmlsrLinkSet",
859 });
860
862 m_apMac->GetDevice()->GetNode()->AddApplication(
864 });
865 }
866}
867
868void
870{
871 m_staMacs.at(m_lastAid)->GetEmlsrManager()->SetAttribute(
872 "EmlsrLinkSet",
874 m_lastAid++;
875 Simulator::Schedule(MilliSeconds(5), [=, this]() {
877 {
878 // make the next STA send EML Notification frame
880 return;
881 }
882 // all stations enabled EMLSR mode; start traffic
884 StartTraffic();
885 });
886}
887
888void
890{
891 auto psduIt = m_txPsdus.cbegin();
892
893 // lambda to jump to the next QoS data frame or MU-RTS Trigger Frame transmitted
894 // to an EMLSR client
895 auto jumpToQosDataOrMuRts = [&]() {
896 while (psduIt != m_txPsdus.cend() &&
897 !psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData())
898 {
899 auto psdu = psduIt->psduMap.cbegin()->second;
900 if (psdu->GetHeader(0).IsTrigger())
901 {
902 CtrlTriggerHeader trigger;
903 psdu->GetPayload(0)->PeekHeader(trigger);
904 if (trigger.IsMuRts())
905 {
906 break;
907 }
908 }
909 psduIt++;
910 }
911 };
912
913 /**
914 * Before enabling EMLSR mode, no MU-RTS TF should be sent. Four packets are generated
915 * after association to trigger the establishment of a Block Ack agreement. The TXOP Limit
916 * and the MCS are set such that two packets can be transmitted in a TXOP, hence we expect
917 * that the AP MLD sends two A-MPDUs to each non-AP MLD.
918 *
919 * EMLSR client with EMLSR mode to be enabled on all links: after ML setup, all other links
920 * stay in power save mode, hence BA establishment occurs on the same link.
921 *
922 * [link 0]
923 * ───────────────────────────────────────────────────────────────────────────
924 * | power save mode
925 *
926 * ┌─────┐ ┌─────┐ ┌───┬───┐ ┌───┬───┐
927 * ┌───┐ │Assoc│ │ADDBA│ ┌───┐ │QoS│QoS│ │QoS│QoS│
928 * [link 1] │ACK│ │Resp │ │ Req │ │ACK│ │ 0 │ 1 │ │ 2 │ 3 │
929 * ───┬─────┬┴───┴──┴─────┴┬───┬─┴─────┴┬───┬─┬─────┬┴───┴─┴───┴───┴┬──┬─┴───┴───┴┬──┬───
930 * │Assoc│ │ACK│ │ACK│ │ADDBA│ │BA│ │BA│
931 * │ Req │ └───┘ └───┘ │Resp │ └──┘ └──┘
932 * └─────┘ └─────┘
933 *
934 * [link 2]
935 * ───────────────────────────────────────────────────────────────────────────
936 * | power save mode
937 *
938 *
939 * EMLSR client with EMLSR mode to be enabled on not all the links: after ML setup,
940 * the other EMLSR links stay in power save mode, the non-EMLSR link (link 1) transitions
941 * to active mode.
942 *
943 * ┌─────┐ ┌───┬───┐
944 * ┌───┐ │ADDBA│ ┌───┐ │QoS│QoS│
945 * [link 0 - non EMLSR] │ACK│ │ Req │ │ACK│ │ 2 │ 3 │
946 * ──────────────────────────────┬────┬┴───┴──┴─────┴┬───┬─┬─────┬┴───┴─┴───┴───┴┬──┬─
947 * │Data│ │ACK│ │ADDBA│ │BA│
948 * │Null│ └───┘ │Resp │ └──┘
949 * └────┘ └─────┘
950 * ┌─────┐ ┌───┬───┐
951 * ┌───┐ │Assoc│ │QoS│QoS│
952 * [link 1] │ACK│ │Resp │ │ 0 │ 1 │
953 * ───┬─────┬┴───┴──┴─────┴┬───┬──────────────────────────────────┴───┴───┴┬──┬───────
954 * │Assoc│ │ACK│ │BA│
955 * │ Req │ └───┘ └──┘
956 * └─────┘
957 *
958 * [link 2]
959 * ───────────────────────────────────────────────────────────────────────────
960 * | power save mode
961 *
962 * Non-EMLSR client (not shown): after ML setup, all other links transition to active mode
963 * by sending a Data Null frame; QoS data frame exchanges occur on two links simultaneously.
964 */
965 for (std::size_t i = 0; i < m_nEmlsrStations + m_nNonEmlsrStations; i++)
966 {
967 std::set<uint8_t> linkIds;
968
969 jumpToQosDataOrMuRts();
970 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend() &&
971 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData()),
972 true,
973 "Expected at least one QoS data frame before enabling EMLSR mode");
974 linkIds.insert(psduIt->linkId);
975 const auto firstAmpduTxEnd =
976 psduIt->startTx +
977 WifiPhy::CalculateTxDuration(psduIt->psduMap,
978 psduIt->txVector,
979 m_staMacs[i]->GetWifiPhy(psduIt->linkId)->GetPhyBand());
980 psduIt++;
981
982 jumpToQosDataOrMuRts();
983 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend() &&
984 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData()),
985 true,
986 "Expected at least two QoS data frames before enabling EMLSR mode");
987 linkIds.insert(psduIt->linkId);
988 const auto secondAmpduTxStart = psduIt->startTx;
989 psduIt++;
990
991 /**
992 * If this is an EMLSR client and there is no setup link other than the one used to
993 * establish association that is not an EMLSR link, then the two A-MPDUs are sent one
994 * after another on the link used to establish association.
995 */
996 auto setupLinks = m_staMacs[i]->GetSetupLinkIds();
997 if (i < m_nEmlsrStations &&
998 std::none_of(setupLinks.begin(), setupLinks.end(), [&](auto&& linkId) {
999 return linkId != m_mainPhyId && m_emlsrLinks.count(linkId) == 0;
1000 }))
1001 {
1002 NS_TEST_EXPECT_MSG_EQ(linkIds.size(),
1003 1,
1004 "Expected both A-MPDUs to be sent on the same link");
1005 NS_TEST_EXPECT_MSG_EQ(*linkIds.begin(), +m_mainPhyId, "A-MPDUs sent on incorrect link");
1006 NS_TEST_EXPECT_MSG_LT(firstAmpduTxEnd,
1007 secondAmpduTxStart,
1008 "A-MPDUs are not sent one after another");
1009 }
1010 /**
1011 * Otherwise, the two A-MPDUs can be sent concurrently on two distinct links (may be
1012 * the link used to establish association and a non-EMLSR link).
1013 */
1014 else
1015 {
1016 NS_TEST_EXPECT_MSG_EQ(linkIds.size(),
1017 2,
1018 "Expected A-MPDUs to be sent on distinct links");
1019 NS_TEST_EXPECT_MSG_GT(firstAmpduTxEnd,
1020 secondAmpduTxStart,
1021 "A-MPDUs are not sent concurrently");
1022 }
1023 }
1024
1025 /**
1026 * After enabling EMLSR mode, MU-RTS TF should only be sent on EMLSR links. After the exchange
1027 * of EML Operating Mode Notification frames, a number of packets are generated at the AP MLD
1028 * to prepare two A-MPDUs for each non-AP MLD.
1029 *
1030 * EMLSR client with EMLSR mode to be enabled on all links (A is the EMLSR client, B is the
1031 * non-EMLSR client):
1032 * ┌─────┬─────┐
1033 * │QoS 4│QoS 5│
1034 * │ to A│ to A│
1035 * ┌───┐ ├─────┼─────┤
1036 * │MU │ │QoS 4│QoS 5│
1037 * [link 0] │RTS│ │ to B│ to B│
1038 * ──────────────────────────┴───┴┬───┬┴─────┴─────┴┬──┬────────────
1039 * │CTS│ │BA│
1040 * ├───┤ ├──┤
1041 * │CTS│ │BA│
1042 * └───┘ └──┘
1043 * ┌───┐ ┌─────┬─────┐
1044 * ┌───┐ │EML│ │QoS 6│QoS 7│
1045 * [link 1] │ACK│ │OM │ │ to B│ to B│
1046 * ────┬───┬┴───┴──┴───┴┬───┬─┴─────┴─────┴┬──┬────────────────────────────────────
1047 * │EML│ │ACK│ │BA│
1048 * │OM │ └───┘ └──┘
1049 * └───┘
1050 * ┌───┐ ┌─────┬─────┐
1051 * │MU │ │QoS 6│QoS 7│
1052 * [link 2] │RTS│ │ to A│ to A│
1053 * ─────────────────────────────────────────────────────────┴───┴┬───┬┴─────┴─────┴┬──┬─
1054 * │CTS│ │BA│
1055 * └───┘ └──┘
1056 *
1057 * EMLSR client with EMLSR mode to be enabled on not all the links (A is the EMLSR client,
1058 * B is the non-EMLSR client):
1059 * ┌─────┬─────┐
1060 * │QoS 4│QoS 5│
1061 * │ to A│ to A│
1062 * ├─────┼─────┤
1063 * │QoS 4│QoS 5│
1064 * [link 0 - non EMLSR] │ to B│ to B│
1065 * ───────────────────────────┴─────┴─────┴┬──┬───────────────────────────
1066 * │BA│
1067 * ├──┤
1068 * │BA│
1069 * └──┘
1070 * ┌─────┬─────┐
1071 * │QoS 6│QoS 7│
1072 * │ to A│ to A│
1073 * ┌───┐ ┌───┐ ├─────┼─────┤
1074 * ┌───┐ │EML│ │MU │ │QoS 6│QoS 7│
1075 * [link 1] │ACK│ │OM │ │RTS│ │ to B│ to B│
1076 * ────┬───┬┴───┴──┴───┴┬───┬─┴───┴┬───┬┴─────┴─────┴┬──┬────────────
1077 * │EML│ │ACK│ │CTS│ │BA│
1078 * │OM │ └───┘ ├───┤ ├──┤
1079 * └───┘ │CTS│ │BA│
1080 * └───┘ └──┘
1081 *
1082 * [link 2]
1083 * ────────────────────────────────────────────────────────────────────────────────
1084 */
1085
1086 /// Store a QoS data frame or an MU-RTS TF followed by a QoS data frame
1087 using FrameExchange = std::list<decltype(psduIt)>;
1088
1089 std::vector<std::list<FrameExchange>> frameExchanges(m_nEmlsrStations);
1090
1091 // compute all frame exchanges involving EMLSR clients
1092 while (psduIt != m_txPsdus.cend())
1093 {
1094 jumpToQosDataOrMuRts();
1095 if (psduIt == m_txPsdus.cend())
1096 {
1097 break;
1098 }
1099
1100 if (IsTrigger(psduIt->psduMap))
1101 {
1102 CtrlTriggerHeader trigger;
1103 psduIt->psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
1104 // this is an MU-RTS TF starting a new frame exchange sequence; add it to all
1105 // the addressed EMLSR clients
1107 true,
1108 "jumpToQosDataOrMuRts does not return TFs other than MU-RTS");
1109 for (const auto& userInfo : trigger)
1110 {
1111 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1112 {
1113 if (m_staMacs.at(i)->GetAssociationId() == userInfo.GetAid12())
1114 {
1115 frameExchanges.at(i).emplace_back(FrameExchange{psduIt});
1116 break;
1117 }
1118 }
1119 }
1120 psduIt++;
1121 continue;
1122 }
1123
1124 // we get here if psduIt points to a psduMap containing QoS data frame(s); find (if any)
1125 // the QoS data frame(s) addressed to EMLSR clients and add them to the appropriate
1126 // frame exchange sequence
1127 for (const auto& staIdPsduPair : psduIt->psduMap)
1128 {
1129 std::for_each_n(m_staMacs.cbegin(), m_nEmlsrStations, [&](auto&& staMac) {
1130 if (!staMac->GetLinkIdByAddress(staIdPsduPair.second->GetAddr1()))
1131 {
1132 // not addressed to this non-AP MLD
1133 return;
1134 }
1135 // a QoS data frame starts a new frame exchange sequence if there is no previous
1136 // MU-RTS TF that has been sent on the same link and is not already followed by
1137 // a QoS data frame
1138 std::size_t id = staMac->GetDevice()->GetNode()->GetId() - 1;
1139 for (auto& frameExchange : frameExchanges.at(id))
1140 {
1141 if (IsTrigger(frameExchange.front()->psduMap) &&
1142 frameExchange.front()->linkId == psduIt->linkId &&
1143 frameExchange.size() == 1)
1144 {
1145 auto it = std::next(frameExchange.front());
1146 while (it != m_txPsdus.end())
1147 {
1148 // stop at the first frame other than CTS sent on this link
1149 if (it->linkId == psduIt->linkId &&
1150 !it->psduMap.begin()->second->GetHeader(0).IsCts())
1151 {
1152 break;
1153 }
1154 ++it;
1155 }
1156 if (it == psduIt)
1157 {
1158 // the QoS data frame actually followed the MU-RTS TF
1159 frameExchange.emplace_back(psduIt);
1160 return;
1161 }
1162 }
1163 }
1164 frameExchanges.at(id).emplace_back(FrameExchange{psduIt});
1165 });
1166 }
1167 psduIt++;
1168 }
1169
1170 /**
1171 * Let's focus on the first two frame exchanges for each EMLSR clients. If all setup links are
1172 * EMLSR links, both frame exchanges are protected by MU-RTS TF and occur one after another.
1173 * Otherwise, one frame exchange occurs on the non-EMLSR link and is not protected by
1174 * MU-RTS TF; the other frame exchange occurs on an EMLSR link and is protected by MU-RTS TF.
1175 */
1176 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1177 {
1178 NS_TEST_EXPECT_MSG_GT_OR_EQ(frameExchanges.at(i).size(),
1179 2,
1180 "Expected at least 2 frame exchange sequences "
1181 << "involving EMLSR client " << i);
1182
1183 auto firstExchangeIt = frameExchanges.at(i).begin();
1184 auto secondExchangeIt = std::next(firstExchangeIt);
1185
1186 const auto firstAmpduTxEnd =
1187 firstExchangeIt->back()->startTx +
1189 firstExchangeIt->back()->psduMap,
1190 firstExchangeIt->back()->txVector,
1191 m_staMacs[i]->GetWifiPhy(firstExchangeIt->back()->linkId)->GetPhyBand());
1192 const auto secondAmpduTxStart = secondExchangeIt->front()->startTx;
1193
1194 if (m_staMacs[i]->GetNLinks() == m_emlsrLinks.size())
1195 {
1196 // all links are EMLSR links
1197 NS_TEST_EXPECT_MSG_EQ(IsTrigger(firstExchangeIt->front()->psduMap),
1198 true,
1199 "Expected an MU-RTS TF as ICF of first frame exchange sequence");
1201 firstExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1202 true,
1203 "Expected a QoS data frame in the first frame exchange sequence");
1204
1205 NS_TEST_EXPECT_MSG_EQ(IsTrigger(secondExchangeIt->front()->psduMap),
1206 true,
1207 "Expected an MU-RTS TF as ICF of second frame exchange sequence");
1209 secondExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1210 true,
1211 "Expected a QoS data frame in the second frame exchange sequence");
1212
1213 NS_TEST_EXPECT_MSG_LT(firstAmpduTxEnd,
1214 secondAmpduTxStart,
1215 "A-MPDUs are not sent one after another");
1216 }
1217 else
1218 {
1219 std::vector<uint8_t> nonEmlsrIds;
1220 auto setupLinks = m_staMacs[i]->GetSetupLinkIds();
1221 std::set_difference(setupLinks.begin(),
1222 setupLinks.end(),
1223 m_emlsrLinks.begin(),
1224 m_emlsrLinks.end(),
1225 std::back_inserter(nonEmlsrIds));
1226 NS_TEST_ASSERT_MSG_EQ(nonEmlsrIds.size(), 1, "Unexpected number of non-EMLSR links");
1227
1228 auto nonEmlsrLinkExchangeIt = firstExchangeIt->front()->linkId == nonEmlsrIds[0]
1229 ? firstExchangeIt
1230 : secondExchangeIt;
1231 NS_TEST_EXPECT_MSG_EQ(IsTrigger(nonEmlsrLinkExchangeIt->front()->psduMap),
1232 false,
1233 "Did not expect an MU-RTS TF as ICF on non-EMLSR link");
1235 nonEmlsrLinkExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1236 true,
1237 "Expected a QoS data frame on the non-EMLSR link");
1238
1239 auto emlsrLinkExchangeIt =
1240 nonEmlsrLinkExchangeIt == firstExchangeIt ? secondExchangeIt : firstExchangeIt;
1241 NS_TEST_EXPECT_MSG_NE(+emlsrLinkExchangeIt->front()->linkId,
1242 +nonEmlsrIds[0],
1243 "Expected this exchange not to occur on non-EMLSR link");
1244 NS_TEST_EXPECT_MSG_EQ(IsTrigger(emlsrLinkExchangeIt->front()->psduMap),
1245 true,
1246 "Expected an MU-RTS TF as ICF on the EMLSR link");
1248 emlsrLinkExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1249 true,
1250 "Expected a QoS data frame on the EMLSR link");
1251
1252 NS_TEST_EXPECT_MSG_GT(firstAmpduTxEnd,
1253 secondAmpduTxStart,
1254 "A-MPDUs are not sent concurrently");
1255 }
1256
1257 // we are done with processing the first two frame exchanges, remove them
1258 frameExchanges.at(i).erase(firstExchangeIt);
1259 frameExchanges.at(i).erase(secondExchangeIt);
1260 }
1261
1262 /**
1263 * A and B are two EMLSR clients. No ICF before the second QoS data frame because B
1264 * has not switched to listening mode. ICF is sent before the third QoS data frame because
1265 * A has switched to listening mode. C is a non-EMLSR client.
1266 *
1267 * ┌─────┐ A switches to listening
1268 * │QoS x│ after transition delay
1269 * │ to A│ |
1270 * ┌───┐ ├─────┤ ┌─────┐
1271 * │MU │ │QoS x│ │QoS y│
1272 * [link 0] │RTS│ │ to B│ │ to B│
1273 * ────────────┴───┴┬───┬┴─────┴┬──┬┴─────┴┬──┬────────────
1274 * │CTS│ │BA│ │BA│
1275 * ├───┤ ├──┤ └──┘
1276 * │CTS│ │BA│
1277 * └───┘ └──┘ AP continues the TXOP A switches to listening
1278 * after PIFS recovery after transition delay
1279 * │ │
1280 * ┌─────┐ ┌───┐ ┌─────┐ │┌───┐ ┌───┐
1281 * │QoS z│ │MU │ │QoS x│ ││MU │ ┌───┐ │CF-│
1282 * [link 1] │ to C│ │RTS│ │ to A│ ││RTS│ │BAR│ │End│
1283 * ───────────────────────────────┴─────┴┬──┬┴───┴┬───┬┴─────┴┬──┬┴───┴┬───┬┴───┴┬──┬┴───┴─
1284 * │BA│ │CTS│ │BA│ │CTS│ │BA│
1285 * └──┘ └───┘ └──x └───┘ └──┘
1286 */
1287 if (m_nEmlsrStations == 2 && m_apMac->GetNLinks() == m_emlsrLinks.size())
1288 {
1289 // the following checks are only done with 2 EMLSR clients having no non-EMLSR link
1290 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1291 {
1292 NS_TEST_EXPECT_MSG_GT_OR_EQ(frameExchanges.at(i).size(),
1293 2,
1294 "Expected at least 2 frame exchange sequences "
1295 << "involving EMLSR client " << i);
1296 // the first frame exchange must start with an ICF
1297 auto firstExchangeIt = frameExchanges.at(i).begin();
1298
1299 NS_TEST_EXPECT_MSG_EQ(IsTrigger(firstExchangeIt->front()->psduMap),
1300 true,
1301 "Expected an MU-RTS TF as ICF of first frame exchange sequence");
1303 firstExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1304 true,
1305 "Expected a QoS data frame in the first frame exchange sequence");
1306 }
1307
1308 // the second frame exchange is the one that starts first
1309 auto secondExchangeIt = std::next(frameExchanges.at(0).begin())->front()->startTx <
1310 std::next(frameExchanges.at(1).begin())->front()->startTx
1311 ? std::next(frameExchanges.at(0).begin())
1312 : std::next(frameExchanges.at(1).begin());
1313 decltype(secondExchangeIt) thirdExchangeIt;
1314 std::size_t thirdExchangeStaId;
1315
1316 if (secondExchangeIt == std::next(frameExchanges.at(0).begin()))
1317 {
1318 thirdExchangeIt = std::next(frameExchanges.at(1).begin());
1319 thirdExchangeStaId = 1;
1320 }
1321 else
1322 {
1323 thirdExchangeIt = std::next(frameExchanges.at(0).begin());
1324 thirdExchangeStaId = 0;
1325 }
1326
1327 // the second frame exchange is not protected by the ICF and starts a SIFS after the end
1328 // of the previous one
1329 NS_TEST_EXPECT_MSG_EQ(IsTrigger(secondExchangeIt->front()->psduMap),
1330 false,
1331 "Expected no ICF for the second frame exchange sequence");
1333 secondExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1334 true,
1335 "Expected a QoS data frame in the second frame exchange sequence");
1336
1337 // the first two frame exchanges occur on the same link
1338 NS_TEST_EXPECT_MSG_EQ(+secondExchangeIt->front()->linkId,
1339 +frameExchanges.at(0).begin()->front()->linkId,
1340 "Expected the first two frame exchanges to occur on the same link");
1341
1342 auto bAckRespIt = std::prev(secondExchangeIt->front());
1343 NS_TEST_EXPECT_MSG_EQ(bAckRespIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAck(),
1344 true,
1345 "Expected a BlockAck response before the second frame exchange");
1346 auto bAckRespTxEnd =
1347 bAckRespIt->startTx +
1348 WifiPhy::CalculateTxDuration(bAckRespIt->psduMap,
1349 bAckRespIt->txVector,
1350 m_apMac->GetWifiPhy(bAckRespIt->linkId)->GetPhyBand());
1351
1352 // the second frame exchange starts a SIFS after the previous one
1354 bAckRespTxEnd + m_apMac->GetWifiPhy(bAckRespIt->linkId)->GetSifs(),
1355 secondExchangeIt->front()->startTx,
1356 "Expected the second frame exchange to start a SIFS after the first one");
1357
1358 // the third frame exchange is protected by MU-RTS and occurs on a different link
1359 NS_TEST_EXPECT_MSG_EQ(IsTrigger(thirdExchangeIt->front()->psduMap),
1360 true,
1361 "Expected an MU-RTS as ICF for the third frame exchange sequence");
1363 thirdExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1364 true,
1365 "Expected a QoS data frame in the third frame exchange sequence");
1366
1368 +secondExchangeIt->front()->linkId,
1369 +thirdExchangeIt->front()->linkId,
1370 "Expected the second and third frame exchanges to occur on distinct links");
1371
1372 auto secondQosIt = secondExchangeIt->front();
1373 auto secondQosTxEnd =
1374 secondQosIt->startTx +
1375 WifiPhy::CalculateTxDuration(secondQosIt->psduMap,
1376 secondQosIt->txVector,
1377 m_apMac->GetWifiPhy(secondQosIt->linkId)->GetPhyBand());
1378
1379 NS_TEST_EXPECT_MSG_GT_OR_EQ(thirdExchangeIt->front()->startTx,
1380 secondQosTxEnd + m_transitionDelay.at(thirdExchangeStaId),
1381 "Transmission started before transition delay");
1382
1383 // the BlockAck of the third frame exchange is not received correctly, so there should be
1384 // another frame exchange
1385 NS_TEST_EXPECT_MSG_EQ((thirdExchangeIt != frameExchanges.at(thirdExchangeStaId).end()),
1386 true,
1387 "Expected a fourth frame exchange");
1388 auto fourthExchangeIt = std::next(thirdExchangeIt);
1389
1390 // the fourth frame exchange is protected by MU-RTS
1391 NS_TEST_EXPECT_MSG_EQ(IsTrigger(fourthExchangeIt->front()->psduMap),
1392 true,
1393 "Expected an MU-RTS as ICF for the fourth frame exchange sequence");
1394
1395 bAckRespIt = std::prev(fourthExchangeIt->front());
1396 NS_TEST_EXPECT_MSG_EQ(bAckRespIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAck(),
1397 true,
1398 "Expected a BlockAck response before the fourth frame exchange");
1399 auto phy = m_apMac->GetWifiPhy(bAckRespIt->linkId);
1400 bAckRespTxEnd = bAckRespIt->startTx + WifiPhy::CalculateTxDuration(bAckRespIt->psduMap,
1401 bAckRespIt->txVector,
1402 phy->GetPhyBand());
1403 auto timeout = phy->GetSifs() + phy->GetSlot() + MicroSeconds(20);
1404
1405 // the fourth frame exchange starts a PIFS after the previous one because the AP
1406 // performs PIFS recovery (the initial frame in the TXOP was successfully received by
1407 // a non-EMLSR client)
1408 NS_TEST_EXPECT_MSG_GT_OR_EQ(fourthExchangeIt->front()->startTx,
1409 bAckRespTxEnd + phy->GetPifs(),
1410 "Transmission started less than a PIFS after BlockAck");
1411 NS_TEST_EXPECT_MSG_LT(fourthExchangeIt->front()->startTx,
1412 bAckRespTxEnd + phy->GetPifs() +
1413 MicroSeconds(1) /* propagation delay upper bound */,
1414 "Transmission started too much time after BlockAck");
1415
1416 auto bAckReqIt = std::next(fourthExchangeIt->front(), 2);
1417 NS_TEST_EXPECT_MSG_EQ(bAckReqIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAckReq(),
1418 true,
1419 "Expected a BlockAck request in the fourth frame exchange");
1420
1421 // we are done with processing the frame exchanges, remove them (two frame exchanges
1422 // per EMLSR client, plus the last one)
1423 frameExchanges.at(0).pop_front();
1424 frameExchanges.at(0).pop_front();
1425 frameExchanges.at(1).pop_front();
1426 frameExchanges.at(1).pop_front();
1427 frameExchanges.at(thirdExchangeStaId).pop_front();
1428 }
1429
1430 /**
1431 * After disabling EMLSR mode, no MU-RTS TF should be sent. After the exchange of
1432 * EML Operating Mode Notification frames, a number of packets are generated at the AP MLD
1433 * to prepare two A-MPDUs for each EMLSR client.
1434 *
1435 * EMLSR client with EMLSR mode to be enabled on all links (A is the EMLSR client, B is the
1436 * non-EMLSR client):
1437 *
1438 * [link 0] | power save mode
1439 * ────────────────────────────────────────────────────────
1440 * ┌─────┬─────┐ ┌──────┬──────┐
1441 * │QoS 8│QoS 9│ │QoS 10│QoS 11│
1442 * │ to A│ to A│ │ to A │ to A │
1443 * ┌───┐ ┌───┐ ├─────┼─────┤ ├──────┼──────┤
1444 * ┌───┐ │MU │ │EML│ │QoS 8│QoS 9│ │QoS 10│QoS 11│
1445 * [link 1] │ACK│ │RTS│ │OM │ │ to B│ to B│ │ to B │ to B │
1446 * ────┬───┬┴───┴──┴───┴┬───┬┴───┴┬───┬──┴─────┴─────┴┬──┬────┴──────┴──────┴┬──┬─────
1447 * │EML│ │CTS│ │ACK│ │BA│ │BA│
1448 * │OM │ └───┘ └───┘ ├──┤ ├──┤
1449 * └───┘ │BA│ │BA│
1450 * └──┘ └──┘
1451 *
1452 * [link 2] | power save mode
1453 * ────────────────────────────────────────────────────────────────────────────
1454 *
1455 *
1456 * EMLSR client with EMLSR mode to be enabled on not all the links (A is the EMLSR client,
1457 * B is the non-EMLSR client):
1458 * ┌─────┬─────┐
1459 * │QoS 8│QoS 9│
1460 * │ to A│ to A│
1461 * ├─────┼─────┤
1462 * │QoS 8│QoS 9│
1463 * [link 0 - non EMLSR] │ to B│ to B│
1464 * ─────────────────────────────────────────┴─────┴─────┴┬──┬─────────────
1465 * │BA│
1466 * ├──┤
1467 * │BA│
1468 * └──┘
1469 * ┌──────┬──────┐
1470 * │QoS 10│QoS 11│
1471 * │ to A │ to A │
1472 * ┌───┐ ┌───┐ ├──────┼──────┤
1473 * ┌───┐ │MU │ │EML│ │QoS 10│QoS 11│
1474 * [link 1] │ACK│ │RTS│ │OM │ │ to B │ to B │
1475 * ────┬───┬┴───┴──┴───┴┬───┬┴───┴┬───┬──┴──────┴──────┴┬──┬─────
1476 * │EML│ │CTS│ │ACK│ │BA│
1477 * │OM │ └───┘ └───┘ ├──┤
1478 * └───┘ │BA│
1479 * └──┘
1480 *
1481 * [link 2] | power save mode
1482 * ────────────────────────────────────────────────────────────────────────────
1483 *
1484 */
1485
1486 // for each EMLSR client, there should be a frame exchange with ICF and no data frame
1487 // (ICF protects the EML Notification response) if the EML Notification response is sent
1488 // while EMLSR mode is still enabled and two frame exchanges with data frames
1489 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1490 {
1491 // the default EMLSR Manager requests to send EML Notification frames on the link where
1492 // the main PHY is operating; if EMLSR mode is still enabled on this link when the AP MLD
1493 // sends the EML Notification response, the latter is protected by an ICF
1494 auto exchangeIt = frameExchanges.at(i).cbegin();
1495
1496 auto linkIdOpt = m_staMacs[i]->GetLinkForPhy(m_mainPhyId);
1497 NS_TEST_ASSERT_MSG_EQ(linkIdOpt.has_value(),
1498 true,
1499 "Didn't find a link on which the main PHY is operating");
1500
1501 if (IsTrigger(exchangeIt->front()->psduMap))
1502 {
1503 NS_TEST_EXPECT_MSG_EQ(+exchangeIt->front()->linkId,
1504 +linkIdOpt.value(),
1505 "ICF was not sent on the expected link");
1506 NS_TEST_EXPECT_MSG_EQ(exchangeIt->size(),
1507 1,
1508 "Expected no data frame in the first frame exchange sequence");
1509 frameExchanges.at(i).pop_front();
1510 }
1511
1512 NS_TEST_EXPECT_MSG_GT_OR_EQ(frameExchanges.at(i).size(),
1513 2,
1514 "Expected at least 2 frame exchange sequences "
1515 << "involving EMLSR client " << i);
1516
1517 auto firstExchangeIt = frameExchanges.at(i).cbegin();
1518 auto secondExchangeIt = std::next(firstExchangeIt);
1519
1520 const auto firstAmpduTxEnd =
1521 firstExchangeIt->back()->startTx +
1523 firstExchangeIt->back()->psduMap,
1524 firstExchangeIt->back()->txVector,
1525 m_staMacs[i]->GetWifiPhy(firstExchangeIt->back()->linkId)->GetPhyBand());
1526 const auto secondAmpduTxStart = secondExchangeIt->front()->startTx;
1527
1529 firstExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1530 true,
1531 "Expected a QoS data frame in the first frame exchange sequence");
1532 NS_TEST_EXPECT_MSG_EQ(firstExchangeIt->size(),
1533 1,
1534 "Expected one frame only in the first frame exchange sequence");
1535
1537 secondExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1538 true,
1539 "Expected a QoS data frame in the second frame exchange sequence");
1540 NS_TEST_EXPECT_MSG_EQ(secondExchangeIt->size(),
1541 1,
1542 "Expected one frame only in the second frame exchange sequence");
1543
1544 if (m_staMacs[i]->GetNLinks() == m_emlsrLinks.size())
1545 {
1546 // all links are EMLSR links: the two QoS data frames are sent one after another on
1547 // the link used for sending EML OMN
1549 +firstExchangeIt->front()->linkId,
1550 +linkIdOpt.value(),
1551 "First frame exchange expected to occur on link used to send EML OMN");
1552
1554 +secondExchangeIt->front()->linkId,
1555 +linkIdOpt.value(),
1556 "Second frame exchange expected to occur on link used to send EML OMN");
1557
1558 NS_TEST_EXPECT_MSG_LT(firstAmpduTxEnd,
1559 secondAmpduTxStart,
1560 "A-MPDUs are not sent one after another");
1561 }
1562 else
1563 {
1564 // the two QoS data frames are sent concurrently on distinct links
1565 NS_TEST_EXPECT_MSG_NE(+firstExchangeIt->front()->linkId,
1566 +secondExchangeIt->front()->linkId,
1567 "Frame exchanges expected to occur on distinct links");
1568
1569 NS_TEST_EXPECT_MSG_GT(firstAmpduTxEnd,
1570 secondAmpduTxStart,
1571 "A-MPDUs are not sent concurrently");
1572 }
1573 }
1574}
1575
1576void
1578{
1579 std::optional<std::size_t> staId;
1580 for (std::size_t id = 0; id < m_nEmlsrStations + m_nNonEmlsrStations; id++)
1581 {
1582 if (m_staMacs.at(id)->GetLinkIdByAddress(address))
1583 {
1584 staId = id;
1585 break;
1586 }
1587 }
1588 NS_TEST_ASSERT_MSG_EQ(staId.has_value(), true, "Not an address of a non-AP MLD " << address);
1589
1590 // check that all EMLSR links (but the link used for ML setup) of the EMLSR clients
1591 // are considered to be in power save mode by the AP MLD; all the other links have
1592 // transitioned to active mode instead
1593 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
1594 {
1595 bool psModeExpected =
1596 *staId < m_nEmlsrStations && linkId != m_mainPhyId && m_emlsrLinks.count(linkId) == 1;
1597 auto addr = m_staMacs.at(*staId)->GetAddress();
1598 auto psMode = m_apMac->GetWifiRemoteStationManager(linkId)->IsInPsMode(addr);
1599 NS_TEST_EXPECT_MSG_EQ(psMode,
1600 psModeExpected,
1601 "EMLSR link " << +linkId << " of EMLSR client " << *staId
1602 << " not in " << (psModeExpected ? "PS" : "active")
1603 << " mode");
1604 // check that AP is blocking transmission of QoS data frames on this link
1606 addr,
1607 linkId,
1608 WifiQueueBlockedReason::POWER_SAVE_MODE,
1609 psModeExpected,
1610 "Checking PM mode after association on AP MLD for EMLSR client " +
1611 std::to_string(*staId),
1612 false);
1613 }
1614}
1615
1616void
1618 const WifiTxVector& txVector,
1619 uint8_t linkId)
1620{
1621 // the AP is replying to a received EMLSR Notification frame
1622 auto pkt = mpdu->GetPacket()->Copy();
1623 const auto& hdr = mpdu->GetHeader();
1625 MgtEmlOmn frame;
1626 pkt->RemoveHeader(frame);
1627
1628 std::optional<std::size_t> staId;
1629 for (std::size_t id = 0; id < m_nEmlsrStations; id++)
1630 {
1631 if (m_staMacs.at(id)->GetFrameExchangeManager(linkId)->GetAddress() == hdr.GetAddr1())
1632 {
1633 staId = id;
1634 break;
1635 }
1636 }
1637 NS_TEST_ASSERT_MSG_EQ(staId.has_value(),
1638 true,
1639 "Not an address of an EMLSR client " << hdr.GetAddr1());
1640
1641 // The EMLSR mode change occurs a Transition Timeout after the end of the PPDU carrying the Ack
1642 auto phy = m_apMac->GetWifiPhy(linkId);
1643 auto txDuration =
1644 WifiPhy::CalculateTxDuration(mpdu->GetSize() + 4, // A-MPDU Subframe header size
1645 txVector,
1646 phy->GetPhyBand());
1647 WifiTxVector ackTxVector =
1648 m_staMacs.at(*staId)->GetWifiRemoteStationManager(linkId)->GetAckTxVector(hdr.GetAddr2(),
1649 txVector);
1650 auto ackDuration = WifiPhy::CalculateTxDuration(GetAckSize() + 4, // A-MPDU Subframe header
1651 ackTxVector,
1652 phy->GetPhyBand());
1653
1654 Simulator::Schedule(txDuration + phy->GetSifs() + ackDuration, [=, this]() {
1655 if (frame.m_emlControl.emlsrMode == 1)
1656 {
1657 // EMLSR mode enabled. Check that all EMLSR links of the EMLSR clients are considered
1658 // to be in active mode by the AP MLD
1659 for (const auto linkId : m_emlsrLinks)
1660 {
1661 auto addr = m_staMacs.at(*staId)->GetAddress();
1662 auto psMode = m_apMac->GetWifiRemoteStationManager(linkId)->IsInPsMode(addr);
1663 NS_TEST_EXPECT_MSG_EQ(psMode,
1664 false,
1665 "EMLSR link " << +linkId << " of EMLSR client " << *staId
1666 << " not in active mode");
1667 // check that AP is not blocking transmission of QoS data frames on this link
1668 CheckBlockedLink(
1669 m_apMac,
1670 addr,
1671 linkId,
1672 WifiQueueBlockedReason::POWER_SAVE_MODE,
1673 false,
1674 "Checking EMLSR links on AP MLD after EMLSR mode is enabled on EMLSR client " +
1675 std::to_string(*staId),
1676 false);
1677 }
1678 }
1679 else
1680 {
1681 // EMLSR mode disabled. Check that all EMLSR links (but the link used to send the
1682 // EML Notification frame) of the EMLSR clients are considered to be in power save
1683 // mode by the AP MLD; the other links are in active mode
1684 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1685 {
1686 bool psModeExpected = id != linkId && m_emlsrLinks.count(id) == 1;
1687 auto addr = m_staMacs.at(*staId)->GetAddress();
1688 auto psMode = m_apMac->GetWifiRemoteStationManager(id)->IsInPsMode(addr);
1689 NS_TEST_EXPECT_MSG_EQ(psMode,
1690 psModeExpected,
1691 "EMLSR link "
1692 << +id << " of EMLSR client " << *staId << " not in "
1693 << (psModeExpected ? "PS" : "active") << " mode");
1694 // check that AP is blocking transmission of QoS data frames on this link
1695 CheckBlockedLink(
1696 m_apMac,
1697 addr,
1698 id,
1699 WifiQueueBlockedReason::POWER_SAVE_MODE,
1700 psModeExpected,
1701 "Checking links on AP MLD after EMLSR mode is disabled on EMLSR client " +
1702 std::to_string(*staId),
1703 false);
1704 }
1705 }
1706 });
1707}
1708
1709void
1711 const WifiTxVector& txVector,
1712 uint8_t linkId)
1713{
1714 // an EMLSR client is sending an EMLSR Notification frame
1715 auto pkt = mpdu->GetPacket()->Copy();
1716 const auto& hdr = mpdu->GetHeader();
1718 MgtEmlOmn frame;
1719 pkt->RemoveHeader(frame);
1720
1721 std::optional<std::size_t> staId;
1722 for (std::size_t id = 0; id < m_nEmlsrStations; id++)
1723 {
1724 if (m_staMacs.at(id)->GetFrameExchangeManager(linkId)->GetAddress() == hdr.GetAddr2())
1725 {
1726 staId = id;
1727 break;
1728 }
1729 }
1730 NS_TEST_ASSERT_MSG_EQ(staId.has_value(),
1731 true,
1732 "Not an address of an EMLSR client " << hdr.GetAddr1());
1733
1734 auto phy = m_staMacs.at(*staId)->GetWifiPhy(linkId);
1735 auto txDuration = WifiPhy::CalculateTxDuration(mpdu->GetSize(), txVector, phy->GetPhyBand());
1736 auto ackTxVector =
1737 m_apMac->GetWifiRemoteStationManager(linkId)->GetAckTxVector(hdr.GetAddr2(), txVector);
1738 auto ackDuration = WifiPhy::CalculateTxDuration(GetAckSize(), ackTxVector, phy->GetPhyBand());
1739 auto cfEndDuration = WifiPhy::CalculateTxDuration(
1741 m_staMacs.at(*staId)->GetWifiRemoteStationManager(linkId)->GetRtsTxVector(
1743 txVector.GetChannelWidth()),
1744 phy->GetPhyBand());
1745
1746 if (frame.m_emlControl.emlsrMode != 0)
1747 {
1748 return;
1749 }
1750
1751 // EMLSR mode disabled
1752 auto timeToCfEnd = txDuration + phy->GetSifs() + ackDuration + phy->GetSifs() + cfEndDuration;
1753
1754 // before the end of the CF-End frame, this link only is not blocked on both the
1755 // EMLSR client and the AP MLD
1756 Simulator::Schedule(timeToCfEnd - MicroSeconds(1), [=, this]() {
1757 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1758 {
1759 CheckBlockedLink(m_staMacs.at(*staId),
1760 m_apMac->GetAddress(),
1761 id,
1762 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1763 id != linkId && m_staMacs.at(*staId)->IsEmlsrLink(id),
1764 "Checking links on EMLSR client " + std::to_string(*staId) +
1765 " before the end of CF-End frame");
1767 m_staMacs.at(*staId)->GetAddress(),
1768 id,
1769 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1770 id != linkId && m_staMacs.at(*staId)->IsEmlsrLink(id),
1771 "Checking links of EMLSR client " + std::to_string(*staId) +
1772 " on the AP MLD before the end of CF-End frame");
1773 }
1774 });
1775 // after the end of the CF-End frame, all links for the EMLSR client are blocked on the
1776 // AP MLD
1777 Simulator::Schedule(timeToCfEnd + MicroSeconds(1), [=, this]() {
1778 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1779 {
1780 if (m_staMacs.at(*staId)->IsEmlsrLink(id))
1781 {
1782 CheckBlockedLink(
1783 m_apMac,
1784 m_staMacs.at(*staId)->GetAddress(),
1785 id && m_staMacs.at(*staId)->IsEmlsrLink(id),
1786 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
1787 true,
1788 "Checking links of EMLSR client " + std::to_string(*staId) +
1789 " are all blocked on the AP MLD right after the end of CF-End");
1790 }
1791 }
1792 });
1793 // before the end of the transition delay, all links for the EMLSR client are still
1794 // blocked on the AP MLD
1795 Simulator::Schedule(timeToCfEnd + m_transitionDelay.at(*staId) - MicroSeconds(1), [=, this]() {
1796 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1797 {
1798 if (m_staMacs.at(*staId)->IsEmlsrLink(id))
1799 {
1800 CheckBlockedLink(m_apMac,
1801 m_staMacs.at(*staId)->GetAddress(),
1802 id,
1803 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
1804 true,
1805 "Checking links of EMLSR client " + std::to_string(*staId) +
1806 " are all blocked on the AP MLD before the end of "
1807 "transition delay");
1808 }
1809 }
1810 });
1811 // immediately after the transition delay, all links for the EMLSR client are unblocked
1812 Simulator::Schedule(timeToCfEnd + m_transitionDelay.at(*staId) + MicroSeconds(1), [=, this]() {
1813 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1814 {
1815 if (m_staMacs.at(*staId)->IsEmlsrLink(id))
1816 {
1817 CheckBlockedLink(m_apMac,
1818 m_staMacs.at(*staId)->GetAddress(),
1819 id,
1820 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
1821 false,
1822 "Checking links of EMLSR client " + std::to_string(*staId) +
1823 " are all unblocked on the AP MLD after the transition delay");
1824 }
1825 }
1826 });
1827}
1828
1829void
1831 const WifiTxVector& txVector,
1832 uint8_t linkId)
1833{
1834 CtrlTriggerHeader trigger;
1835 mpdu->GetPacket()->PeekHeader(trigger);
1836 if (!trigger.IsMuRts())
1837 {
1838 return;
1839 }
1840
1842 true,
1843 "Did not expect an ICF before enabling EMLSR mode");
1844
1847 "Unexpected preamble type for the Initial Control frame");
1848 auto rate = txVector.GetMode().GetDataRate(txVector);
1849 NS_TEST_EXPECT_MSG_EQ((rate == 6e6 || rate == 12e6 || rate == 24e6),
1850 true,
1851 "Unexpected rate for the Initial Control frame: " << rate);
1852
1853 bool found = false;
1854 Time maxPaddingDelay{};
1855
1856 for (const auto& userInfo : trigger)
1857 {
1858 auto addr = m_apMac->GetMldOrLinkAddressByAid(userInfo.GetAid12());
1859 NS_TEST_ASSERT_MSG_EQ(addr.has_value(),
1860 true,
1861 "AID " << userInfo.GetAid12() << " not found");
1862
1863 if (m_apMac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*addr))
1864 {
1865 found = true;
1866
1867 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1868 {
1869 if (m_staMacs.at(i)->GetAddress() == *addr)
1870 {
1871 maxPaddingDelay = Max(maxPaddingDelay, m_paddingDelay.at(i));
1872 break;
1873 }
1874 }
1875
1876 // check that the AP has blocked transmission on all other EMLSR links
1877 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1878 {
1879 if (!m_apMac->GetWifiRemoteStationManager(id)->GetEmlsrEnabled(*addr))
1880 {
1881 continue;
1882 }
1883
1885 *addr,
1886 id,
1887 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1888 id != linkId,
1889 "Checking that AP blocked transmissions on all other EMLSR "
1890 "links after sending ICF to client with AID=" +
1891 std::to_string(userInfo.GetAid12()),
1892 false);
1893 }
1894 }
1895 }
1896
1897 NS_TEST_EXPECT_MSG_EQ(found, true, "Expected ICF to be addressed to at least an EMLSR client");
1898
1899 auto txDuration = WifiPhy::CalculateTxDuration(mpdu->GetSize(),
1900 txVector,
1901 m_apMac->GetWifiPhy(linkId)->GetPhyBand());
1902
1903 if (maxPaddingDelay.IsStrictlyPositive())
1904 {
1905 // compare the TX duration of this Trigger Frame to that of the Trigger Frame with no
1906 // padding added
1907 trigger.SetPaddingSize(0);
1908 auto pkt = Create<Packet>();
1909 pkt->AddHeader(trigger);
1910 auto txDurationWithout =
1911 WifiPhy::CalculateTxDuration(Create<WifiPsdu>(pkt, mpdu->GetHeader()),
1912 txVector,
1913 m_apMac->GetWifiPhy(linkId)->GetPhyBand());
1914
1915 NS_TEST_EXPECT_MSG_EQ(txDuration,
1916 txDurationWithout + maxPaddingDelay,
1917 "Unexpected TX duration of the MU-RTS TF with padding "
1918 << maxPaddingDelay.As(Time::US));
1919 }
1920
1921 // check that the EMLSR clients have blocked transmissions on other links after
1922 // receiving this ICF
1923 for (const auto& userInfo : trigger)
1924 {
1925 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1926 {
1927 if (m_staMacs[i]->GetAssociationId() != userInfo.GetAid12())
1928 {
1929 continue;
1930 }
1931
1932 Simulator::Schedule(txDuration + NanoSeconds(5), [=, this]() {
1933 for (uint8_t id = 0; id < m_staMacs[i]->GetNLinks(); id++)
1934 {
1935 // non-EMLSR links or links on which ICF is received are not blocked
1937 m_apMac->GetAddress(),
1938 id,
1939 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1940 id != linkId && m_staMacs[i]->IsEmlsrLink(id),
1941 "Checking EMLSR links on EMLSR client " + std::to_string(i) +
1942 " after receiving ICF");
1943 }
1944 });
1945
1946 break;
1947 }
1948 }
1949}
1950
1951void
1953 const WifiTxVector& txVector,
1954 uint8_t linkId)
1955{
1956 if (m_nEmlsrStations != 2 || m_apMac->GetNLinks() != m_emlsrLinks.size() ||
1958
1959 {
1960 // we are interested in frames sent to test transition delay
1961 return;
1962 }
1963
1964 std::size_t firstClientId = 0;
1965 std::size_t secondClientId = 1;
1966 auto addr = m_staMacs[secondClientId]->GetAddress();
1967 auto txDuration =
1968 WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
1969
1971
1972 switch (m_countQoSframes)
1973 {
1974 case 1:
1975 // generate another small packet addressed to the first EMLSR client only
1976 m_apMac->GetDevice()->GetNode()->AddApplication(
1977 GetApplication(DOWNLINK, firstClientId, 1, 40));
1978 // both EMLSR clients are about to receive a QoS data frame
1979 for (std::size_t clientId : {firstClientId, secondClientId})
1980 {
1981 Simulator::Schedule(txDuration, [=, this]() {
1982 for (uint8_t id = 0; id < m_staMacs[clientId]->GetNLinks(); id++)
1983 {
1984 // link on which QoS data is received is not blocked
1985 CheckBlockedLink(m_staMacs[clientId],
1986 m_apMac->GetAddress(),
1987 id,
1988 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1989 id != linkId,
1990 "Checking EMLSR links on EMLSR client " +
1991 std::to_string(clientId) +
1992 " after receiving the first QoS data frame");
1993 }
1994 });
1995 }
1996 break;
1997 case 2:
1998 // generate another small packet addressed to the second EMLSR client
1999 m_apMac->GetDevice()->GetNode()->AddApplication(
2000 GetApplication(DOWNLINK, secondClientId, 1, 40));
2001
2002 // when the transmission of the second QoS data frame starts, both EMLSR clients are
2003 // still blocking all the links but the one used to receive the QoS data frame
2004 for (std::size_t clientId : {firstClientId, secondClientId})
2005 {
2006 for (uint8_t id = 0; id < m_staMacs[clientId]->GetNLinks(); id++)
2007 {
2008 // link on which QoS data is received is not blocked
2009 CheckBlockedLink(m_staMacs[clientId],
2010 m_apMac->GetAddress(),
2011 id,
2012 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2013 id != linkId,
2014 "Checking EMLSR links on EMLSR client " +
2015 std::to_string(clientId) +
2016 " when starting the reception of the second QoS frame");
2017 }
2018 }
2019
2020 // the EMLSR client that is not the recipient of the QoS frame being transmitted will
2021 // switch back to listening mode after a transition delay starting from the end of
2022 // the PPDU carrying this QoS data frame
2023
2024 // immediately before the end of the PPDU, this link only is not blocked for the EMLSR
2025 // client on the AP MLD
2026 Simulator::Schedule(txDuration - NanoSeconds(1), [=, this]() {
2027 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2028 {
2030 addr,
2031 id,
2032 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2033 id != linkId,
2034 "Checking that links of EMLSR client " +
2035 std::to_string(secondClientId) +
2036 " are blocked on the AP MLD before the end of the PPDU");
2037 }
2038 });
2039 // immediately before the end of the PPDU, all the links on the EMLSR client that is not
2040 // the recipient of the second QoS frame are unblocked (they are unblocked when the
2041 // PHY-RXSTART.indication is not received)
2042 Simulator::Schedule(txDuration - NanoSeconds(1), [=, this]() {
2043 for (uint8_t id = 0; id < m_staMacs[secondClientId]->GetNLinks(); id++)
2044 {
2045 CheckBlockedLink(m_staMacs[secondClientId],
2046 m_apMac->GetAddress(),
2047 id,
2048 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2049 false,
2050 "Checking that links of EMLSR client " +
2051 std::to_string(secondClientId) +
2052 " are unblocked before the end of the second QoS frame");
2053 }
2054 });
2055 // immediately after the end of the PPDU, all links are blocked for the EMLSR client
2056 Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
2057 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2058 {
2060 addr,
2061 id,
2062 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2063 true,
2064 "Checking links of EMLSR client " +
2065 std::to_string(secondClientId) +
2066 " are all blocked on the AP MLD after the end of the PPDU");
2067 }
2068 });
2069 // immediately before the transition delay, all links are still blocked for the EMLSR client
2071 txDuration + m_transitionDelay.at(secondClientId) - NanoSeconds(1),
2072 [=, this]() {
2073 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2074 {
2076 m_apMac,
2077 addr,
2078 id,
2079 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2080 true,
2081 "Checking links of EMLSR client " + std::to_string(secondClientId) +
2082 " are all blocked on the AP MLD before the transition delay",
2083 false);
2084 }
2085 });
2086
2087 // 100 us before the transition delay expires, generate another small packet addressed
2088 // to a non-EMLSR client. The AP will start a TXOP to transmit this frame, while the
2089 // frame addressed to the EMLSR client is still queued because the transition delay has
2090 // not yet elapsed. The transition delay will expire while the AP is transmitting the
2091 // frame to the non-EMLSR client, so that the AP continues the TXOP to transmit the frame
2092 // to the EMLSR client and we can check that the AP performs PIFS recovery after missing
2093 // the BlockAck from the EMLSR client
2094 Simulator::Schedule(txDuration + m_transitionDelay.at(secondClientId) - MicroSeconds(100),
2095 [=, this]() {
2096 m_apMac->GetDevice()->GetNode()->AddApplication(
2098 });
2099
2100 break;
2101 case 3:
2102 // this is the frame addressed to a non-EMLSR client, which is transmitted before the
2103 // frame addressed to the EMLSR client, because the links of the latter are still blocked
2104 // at the AP because the transition delay has not yet elapsed
2106 psduMap.cbegin()->second->GetAddr1(),
2107 m_staMacs[m_nEmlsrStations]->GetFrameExchangeManager(linkId)->GetAddress(),
2108 "QoS frame not addressed to a non-EMLSR client");
2109
2110 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2111 {
2113 addr,
2114 id,
2115 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2116 true,
2117 "Checking links of EMLSR client " + std::to_string(secondClientId) +
2118 " are all blocked on the AP MLD before the transition delay");
2119 }
2120 // Block transmissions to the EMLSR client on all the links but the one on which this
2121 // frame is sent, so that the AP will continue this TXOP to send the queued frame to the
2122 // EMLSR client once the transition delay elapses
2123 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2124 {
2125 if (id != linkId)
2126 {
2127 m_apMac->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED, addr, {id});
2128 }
2129 }
2130 break;
2131 case 4:
2132 // the AP is continuing the TXOP, no need to block transmissions anymore
2133 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2134 {
2135 m_apMac->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED, addr, {id});
2136 }
2137 // at the end of the fourth QoS frame, this link only is not blocked on the EMLSR
2138 // client receiving the frame
2139 Simulator::Schedule(txDuration, [=, this]() {
2140 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2141 {
2142 CheckBlockedLink(m_staMacs[secondClientId],
2143 m_apMac->GetAddress(),
2144 id,
2145 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2146 id != linkId,
2147 "Checking EMLSR links on EMLSR client " +
2148 std::to_string(secondClientId) +
2149 " after receiving the fourth QoS data frame");
2150 }
2151 });
2152 break;
2153 }
2154}
2155
2156void
2158 const WifiTxVector& txVector,
2159 uint8_t phyId)
2160{
2161 if (m_nEmlsrStations != 2 || m_apMac->GetNLinks() != m_emlsrLinks.size() ||
2163 {
2164 // we are interested in frames sent to test transition delay
2165 return;
2166 }
2167
2168 if (++m_countBlockAck == 4)
2169 {
2170 // fourth BlockAck is sent by a non-EMLSR client
2171 return;
2172 }
2173
2174 auto taddr = psduMap.cbegin()->second->GetAddr2();
2175 std::size_t clientId;
2176 if (m_staMacs[0]->GetLinkIdByAddress(taddr))
2177 {
2178 clientId = 0;
2179 }
2180 else
2181 {
2182 NS_TEST_ASSERT_MSG_EQ(m_staMacs[1]->GetLinkIdByAddress(taddr).has_value(),
2183 true,
2184 "Unexpected TA for BlockAck: " << taddr);
2185 clientId = 1;
2186 }
2187
2188 // find the link on which the main PHY is operating
2189 auto currMainPhyLinkId = m_staMacs[clientId]->GetLinkForPhy(phyId);
2191 currMainPhyLinkId.has_value(),
2192 true,
2193 "Didn't find the link on which the PHY sending the BlockAck is operating");
2194 auto linkId = *currMainPhyLinkId;
2195
2196 // we need the MLD address to check the status of the container queues
2197 auto addr = m_apMac->GetWifiRemoteStationManager(linkId)->GetMldAddress(taddr);
2198 NS_TEST_ASSERT_MSG_EQ(addr.has_value(), true, "MLD address not found for " << taddr);
2199
2200 auto apPhy = m_apMac->GetWifiPhy(linkId);
2201 auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, apPhy->GetPhyBand());
2202 auto cfEndTxDuration = WifiPhy::CalculateTxDuration(
2204 m_apMac->GetWifiRemoteStationManager(linkId)->GetRtsTxVector(Mac48Address::GetBroadcast(),
2205 txVector.GetChannelWidth()),
2206 apPhy->GetPhyBand());
2207
2208 switch (m_countBlockAck)
2209 {
2210 case 5:
2211 // the PPDU carrying this BlockAck is corrupted, hence the AP MLD MAC receives the
2212 // PHY-RXSTART indication but it does not receive any frame from the PHY. Therefore,
2213 // at the end of the PPDU transmission, the AP MLD realizes that the EMLSR client has
2214 // not responded and makes an attempt at continuing the TXOP
2215
2216 // at the end of the PPDU, this link only is not blocked on both the EMLSR client and
2217 // the AP MLD
2218 Simulator::Schedule(txDuration, [=, this]() {
2219 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2220 {
2221 CheckBlockedLink(m_staMacs[clientId],
2222 m_apMac->GetAddress(),
2223 id,
2224 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2225 id != linkId,
2226 "Checking links on EMLSR client " + std::to_string(clientId) +
2227 " at the end of fourth BlockAck");
2229 *addr,
2230 id,
2231 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2232 id != linkId,
2233 "Checking links of EMLSR client " + std::to_string(clientId) +
2234 " on the AP MLD at the end of fourth BlockAck");
2235 }
2236 });
2237 // a SIFS after the end of the PPDU, still this link only is not blocked on both the
2238 // EMLSR client and the AP MLD
2239 Simulator::Schedule(txDuration + apPhy->GetSifs(), [=, this]() {
2240 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2241 {
2242 CheckBlockedLink(m_staMacs[clientId],
2243 m_apMac->GetAddress(),
2244 id,
2245 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2246 id != linkId,
2247 "Checking links on EMLSR client " + std::to_string(clientId) +
2248 " a SIFS after the end of fourth BlockAck");
2249 CheckBlockedLink(m_apMac,
2250 *addr,
2251 id,
2252 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2253 id != linkId,
2254 "Checking links of EMLSR client " + std::to_string(clientId) +
2255 " a SIFS after the end of fourth BlockAck");
2256 }
2257 });
2258 // corrupt this BlockAck so that the AP MLD sends a BlockAckReq later on
2259 {
2260 auto uid = psduMap.cbegin()->second->GetPacket()->GetUid();
2261 m_errorModel->SetList({uid});
2262 }
2263 break;
2264 case 6:
2265 // at the end of the PPDU, this link only is not blocked on both the EMLSR client and
2266 // the AP MLD
2267 Simulator::Schedule(txDuration, [=, this]() {
2268 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2269 {
2270 CheckBlockedLink(m_staMacs[clientId],
2271 m_apMac->GetAddress(),
2272 id,
2273 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2274 id != linkId,
2275 "Checking links on EMLSR client " + std::to_string(clientId) +
2276 " at the end of fifth BlockAck");
2278 *addr,
2279 id,
2280 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2281 id != linkId,
2282 "Checking links of EMLSR client " + std::to_string(clientId) +
2283 " on the AP MLD at the end of fifth BlockAck");
2284 }
2285 });
2286 // before the end of the CF-End frame, still this link only is not blocked on both the
2287 // EMLSR client and the AP MLD
2289 txDuration + apPhy->GetSifs() + cfEndTxDuration - MicroSeconds(1),
2290 [=, this]() {
2291 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2292 {
2293 CheckBlockedLink(m_staMacs[clientId],
2294 m_apMac->GetAddress(),
2295 id,
2296 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2297 id != linkId,
2298 "Checking links on EMLSR client " + std::to_string(clientId) +
2299 " before the end of CF-End frame");
2301 *addr,
2302 id,
2303 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2304 id != linkId,
2305 "Checking links of EMLSR client " + std::to_string(clientId) +
2306 " on the AP MLD before the end of CF-End frame");
2307 }
2308 });
2309 // after the end of the CF-End frame, all links for the EMLSR client are blocked on the
2310 // AP MLD
2312 txDuration + apPhy->GetSifs() + cfEndTxDuration + MicroSeconds(1),
2313 [=, this]() {
2314 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2315 {
2317 m_apMac,
2318 *addr,
2319 id,
2320 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2321 true,
2322 "Checking links of EMLSR client " + std::to_string(clientId) +
2323 " are all blocked on the AP MLD right after the end of CF-End");
2324 }
2325 });
2326 // before the end of the transition delay, all links for the EMLSR client are still
2327 // blocked on the AP MLD
2329 txDuration + apPhy->GetSifs() + cfEndTxDuration + m_transitionDelay.at(clientId) -
2330 MicroSeconds(1),
2331 [=, this]() {
2332 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2333 {
2335 m_apMac,
2336 *addr,
2337 id,
2338 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2339 true,
2340 "Checking links of EMLSR client " + std::to_string(clientId) +
2341 " are all blocked on the AP MLD before the end of transition delay");
2342 }
2343 });
2344 // immediately after the transition delay, all links for the EMLSR client are unblocked
2346 txDuration + apPhy->GetSifs() + cfEndTxDuration + m_transitionDelay.at(clientId) +
2347 MicroSeconds(1),
2348 [=, this]() {
2349 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2350 {
2352 m_apMac,
2353 *addr,
2354 id,
2355 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2356 false,
2357 "Checking links of EMLSR client " + std::to_string(clientId) +
2358 " are all unblocked on the AP MLD after the transition delay");
2359 }
2360 });
2361 break;
2362 }
2363}
2364
2365void
2375
2377 : EmlsrOperationsTestBase("Check EML UL TXOP transmissions"),
2378 m_emlsrLinks(params.linksToEnableEmlsrOn),
2379 m_channelWidth(params.channelWidth),
2380 m_auxPhyChannelWidth(params.auxPhyChannelWidth),
2381 m_mediumSyncDuration(params.mediumSyncDuration),
2382 m_msdMaxNTxops(params.msdMaxNTxops),
2383 m_emlsrEnabledTime(0),
2384 m_firstUlPktsGenTime(0),
2385 m_unblockMainPhyLinkDelay(MilliSeconds(20)),
2386 m_checkBackoffStarted(false),
2387 m_countQoSframes(0),
2388 m_countBlockAck(0),
2389 m_countRtsframes(0),
2390 m_genBackoffIfTxopWithoutTx(params.genBackoffAndUseAuxPhyCca),
2391 m_useAuxPhyCca(params.genBackoffAndUseAuxPhyCca)
2392{
2393 m_nEmlsrStations = 1;
2395 m_linksToEnableEmlsrOn = params.linksToEnableEmlsrOn;
2396 m_mainPhyId = 1;
2397
2398 // when aux PHYs do not switch link, the main PHY switches back to its previous link after
2399 // a TXOP, hence the transition delay must exceed the channel switch delay (default: 250us)
2401 m_establishBaDl = true;
2402 m_establishBaUl = true;
2403 m_duration = Seconds(1);
2404
2405 NS_ABORT_MSG_IF(params.linksToEnableEmlsrOn.size() < 2,
2406 "This test requires at least two links to be configured as EMLSR links");
2407 for (uint8_t id = 0; id < 3; id++)
2408 {
2409 if (!m_emlsrLinks.contains(id))
2410 {
2411 // non-EMLSR link found
2412 m_nonEmlsrLink = id;
2413 break;
2414 }
2415 }
2416}
2417
2418void
2420{
2421 Config::SetDefault("ns3::EmlsrManager::AuxPhyChannelWidth",
2423 Config::SetDefault("ns3::DefaultEmlsrManager::SwitchAuxPhy", BooleanValue(false));
2424 Config::SetDefault("ns3::AdvancedEmlsrManager::UseAuxPhyCca", BooleanValue(m_useAuxPhyCca));
2425 Config::SetDefault("ns3::EhtConfiguration::MediumSyncDuration",
2427 Config::SetDefault("ns3::EhtConfiguration::MsdMaxNTxops", UintegerValue(m_msdMaxNTxops));
2428 Config::SetDefault("ns3::ChannelAccessManager::GenerateBackoffIfTxopWithoutTx",
2430 // Channel switch delay should be less than RTS TX time + SIFS + CTS TX time, otherwise
2431 // UL TXOPs cannot be initiated by aux PHYs
2432 Config::SetDefault("ns3::WifiPhy::ChannelSwitchDelay", TimeValue(MicroSeconds(75)));
2433
2435
2436 m_staMacs[0]->GetQosTxop(AC_BE)->TraceConnectWithoutContext(
2437 "BackoffTrace",
2439
2440 uint8_t linkId = 0;
2441 // configure channels of the given width
2443 {
2444 MHz_u bw = 20;
2445 uint8_t number = band == WIFI_PHY_BAND_5GHZ ? 36 : 1;
2446
2447 auto width = std::min<MHz_u>(m_channelWidth, band == WIFI_PHY_BAND_2_4GHZ ? 40 : 160);
2448 while (bw < width)
2449 {
2450 bw *= 2;
2451 number += bw / 20;
2452 }
2453
2454 for (auto mac : std::initializer_list<Ptr<WifiMac>>{m_apMac, m_staMacs[0]})
2455 {
2456 mac->GetWifiPhy(linkId)->SetOperatingChannel(
2457 WifiPhy::ChannelTuple{number, width, band, 0});
2458 }
2459 linkId++;
2460 }
2461
2462 // install post reception error model on the AP affiliated with the AP MLD and operating on
2463 // the same link as the main PHY of the EMLSR client
2465 m_apMac->GetWifiPhy(m_mainPhyId)->SetPostReceptionErrorModel(m_errorModel);
2466}
2467
2468void
2470{
2471 NS_LOG_INFO("Backoff value " << backoff << " generated by EMLSR client on link " << +linkId
2472 << "\n");
2473 if (linkId != m_mainPhyId)
2474 {
2475 return; // we are only interested in backoff on main PHY link
2476 }
2477
2478 if (m_backoffEndTime)
2479 {
2481 {
2482 // another backoff value while checkBackoffStarted is true is generated only if
2483 // GenerateBackoffIfTxopWithoutTx is true
2486 true,
2487 "Another backoff value should not be generated while the main PHY link is blocked");
2488
2491 "Backoff generated at unexpected time");
2492 }
2493 else
2494 {
2495 // we are done checking the backoff
2496 m_backoffEndTime.reset();
2497 }
2498 }
2499
2501 {
2502 if (!m_backoffEndTime.has_value())
2503 {
2504 // this is the first time we set m_backoffEndTime, which is done right after receiving
2505 // a BlockAck, thus we have to wait an AIFS before invoking backoff
2507 m_staMacs[0]->GetChannelAccessManager(linkId)->GetSifs() +
2508 m_staMacs[0]->GetQosTxop(AC_BE)->GetAifsn(linkId) *
2509 m_staMacs[0]->GetChannelAccessManager(linkId)->GetSlot();
2510 }
2511 else
2512 {
2513 // we get here when the backoff expired but no transmission occurred, thus we have
2514 // generated a new backoff value and we will start decrementing the counter in a slot
2516 Simulator::Now() + m_staMacs[0]->GetChannelAccessManager(linkId)->GetSlot();
2517 }
2518 // add the time corresponding to the generated number of slots
2519 m_backoffEndTime.value() +=
2520 backoff * m_staMacs[0]->GetChannelAccessManager(linkId)->GetSlot();
2521 NS_LOG_DEBUG("Expected backoff end time = " << m_backoffEndTime->As(Time::US) << "\n");
2522 }
2523}
2524
2525void
2527 uint8_t phyId,
2528 WifiConstPsduMap psduMap,
2529 WifiTxVector txVector,
2530 double txPowerW)
2531{
2532 EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
2533 auto linkId = m_txPsdus.back().linkId;
2534
2535 auto psdu = psduMap.begin()->second;
2536 auto nodeId = mac->GetDevice()->GetNode()->GetId();
2537
2538 switch (psdu->GetHeader(0).GetType())
2539 {
2541 NS_ASSERT_MSG(nodeId > 0, "APs do not send AssocReq frames");
2542 NS_TEST_EXPECT_MSG_EQ(+linkId, +m_mainPhyId, "AssocReq not sent by the main PHY");
2543 break;
2544
2546 CheckInitialControlFrame(*psdu->begin(), txVector, linkId);
2547 break;
2548
2549 case WIFI_MAC_CTL_RTS:
2550 CheckRtsFrames(*psdu->begin(), txVector, linkId);
2551 break;
2552
2553 case WIFI_MAC_CTL_CTS:
2554 CheckCtsFrames(*psdu->begin(), txVector, linkId);
2555 break;
2556
2557 case WIFI_MAC_QOSDATA:
2558 CheckQosFrames(psduMap, txVector, linkId);
2559 break;
2560
2562 CheckBlockAck(psduMap, txVector, linkId);
2563 break;
2564
2565 default:;
2566 }
2567}
2568
2569void
2571{
2572 // initially, we prevent transmissions on aux PHY links
2573 auto auxPhyLinks = m_staMacs[0]->GetSetupLinkIds();
2574 auxPhyLinks.erase(m_mainPhyId);
2575 if (m_nonEmlsrLink)
2576 {
2577 auxPhyLinks.erase(*m_nonEmlsrLink);
2578 }
2579 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2580 m_apMac->GetAddress(),
2581 auxPhyLinks);
2582
2583 // Association, Block Ack agreement establishment and enabling EMLSR mode have been done.
2584 // After 50ms, schedule:
2585 // - block of transmissions on the link where the main PHY is operating and on the non-EMLSR
2586 // link (if any)
2587 // - the generation of two UL packets
2588 // - after m_unblockMainPhyLinkDelay, unblock transmissions on the link where the main PHY
2589 // is operating, so that the first data frame is transmitted on that link
2591 std::set<uint8_t> linkIds;
2592 linkIds.insert(*m_staMacs[0]->GetLinkForPhy(m_mainPhyId));
2593 if (m_nonEmlsrLink)
2594 {
2595 linkIds.insert(*m_nonEmlsrLink);
2596 }
2597 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2598 m_apMac->GetAddress(),
2599 linkIds);
2600
2601 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2602 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000));
2604
2606 m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2607 m_apMac->GetAddress(),
2608 {*m_staMacs[0]->GetLinkForPhy(m_mainPhyId)});
2609 });
2610 });
2611}
2612
2613void
2615 const WifiTxVector& txVector,
2616 uint8_t linkId)
2617{
2619
2620 auto txDuration =
2621 WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2622
2623 switch (m_countQoSframes)
2624 {
2625 case 1:
2626 case 2:
2627 // do nothing, these are the QoS data frames sent to establish BA agreements in DL and UL
2628 // direction
2629 break;
2630 case 3:
2631 // first UL data frame (transmitted by the main PHY)
2632 if (m_nonEmlsrLink)
2633 {
2634 // generate data packets for another UL data frame, which will be sent on the
2635 // non-EMLSR link
2636 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2637 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(
2638 GetApplication(UPLINK, 0, 2, 1000));
2639
2640 // unblock transmissions on the non-EMLSR link once the two packets are queued
2641 Simulator::ScheduleNow([=, this]() {
2642 m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2643 m_apMac->GetAddress(),
2644 {*m_nonEmlsrLink});
2645 });
2646 }
2647
2648 // check that other EMLSR links are now blocked on the EMLSR client and on the AP MLD
2649 // after this QoS data frame is received
2650 Simulator::ScheduleNow([=, this]() {
2651 for (auto id : m_staMacs[0]->GetLinkIds())
2652 {
2654 m_staMacs[0],
2655 m_apMac->GetAddress(),
2656 id,
2657 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2658 id != m_staMacs[0]->GetLinkForPhy(m_mainPhyId) && m_staMacs[0]->IsEmlsrLink(id),
2659 "Checking EMLSR links on EMLSR client while sending the first data frame",
2660 false);
2661
2664 [=, this]() {
2666 m_apMac,
2667 m_staMacs[0]->GetAddress(),
2668 id,
2669 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2670 id != m_staMacs[0]->GetLinkForPhy(m_mainPhyId) &&
2671 m_staMacs[0]->IsEmlsrLink(id),
2672 "Checking EMLSR links on AP MLD while sending the first data frame");
2673 });
2674 }
2675 });
2676
2677 if (m_nonEmlsrLink)
2678 {
2679 break;
2680 }
2681 m_countQoSframes++; // if all EMLSR links, next case is already executed now
2682 [[fallthrough]];
2683 case 4:
2684 // check that other EMLSR links are now blocked on the EMLSR client and on the AP MLD
2685 // after this QoS data frame is received
2686 Simulator::ScheduleNow([=, this]() {
2687 // make aux PHYs capable of transmitting frames
2688 auto auxPhyLinks = m_staMacs[0]->GetSetupLinkIds();
2689 auxPhyLinks.erase(m_mainPhyId);
2690 if (m_nonEmlsrLink)
2691 {
2692 auxPhyLinks.erase(*m_nonEmlsrLink);
2693 }
2694 m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2695 m_apMac->GetAddress(),
2696 auxPhyLinks);
2697
2698 // block transmissions on the link where the main PHY is operating
2699 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2700 m_apMac->GetAddress(),
2701 {*m_staMacs[0]->GetLinkForPhy(m_mainPhyId)});
2702
2703 // generate data packets for another UL data frame, which will be sent on a link on
2704 // which an aux PHY is operating
2705 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2706 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(
2707 GetApplication(UPLINK, 0, 2, 1000));
2708 });
2709 break;
2710 case 5:
2711 // check that other EMLSR links are now blocked on both the EMLSR client and the AP MLD
2712 Simulator::ScheduleNow([=, this]() {
2713 for (auto id : m_staMacs[0]->GetLinkIds())
2714 {
2716 m_staMacs[0],
2717 m_apMac->GetAddress(),
2718 id,
2719 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2720 id != linkId && m_staMacs[0]->IsEmlsrLink(id),
2721 "Checking EMLSR links on EMLSR client while sending the second data frame",
2722 false);
2723
2725 m_apMac,
2726 m_staMacs[0]->GetAddress(),
2727 id,
2728 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2729 id != linkId && m_staMacs[0]->IsEmlsrLink(id),
2730 "Checking EMLSR links on AP MLD while sending the second data frame",
2731 false);
2732 }
2733
2734 // unblock transmission on the link where the main PHY is operating
2735 m_staMacs[0]->GetMacQueueScheduler()->UnblockQueues(
2736 WifiQueueBlockedReason::TID_NOT_MAPPED,
2737 AC_BE,
2739 m_apMac->GetAddress(),
2740 m_staMacs[0]->GetAddress(),
2741 {0},
2742 {m_mainPhyId});
2743 });
2744 break;
2745 }
2746}
2747
2748void
2750 const WifiTxVector& txVector,
2751 uint8_t linkId)
2752{
2754
2755 auto auxPhyLinks = m_staMacs[0]->GetSetupLinkIds();
2756 auxPhyLinks.erase(m_mainPhyId);
2757 if (m_nonEmlsrLink)
2758 {
2759 auxPhyLinks.erase(*m_nonEmlsrLink);
2760 }
2761
2762 // lambda to check that the MediumSyncDelay timer is correctly running/not running and
2763 // the CCA ED threshold is set to the correct value on all the links
2764 auto checkMediumSyncDelayTimerActive = [=, this]() {
2765 for (auto id : m_staMacs[0]->GetLinkIds())
2766 {
2767 // timer only started on EMLSR links other than the link on which TXOP was carried
2768 // out
2769 auto isTimerActive = m_staMacs[0]->IsEmlsrLink(id) && id != linkId;
2770 auto time = m_staMacs[0]->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(id);
2771 NS_TEST_EXPECT_MSG_EQ(time.has_value(),
2772 isTimerActive,
2774 << " Unexpected status for MediumSyncDelay timer on link "
2775 << +id << " after terminating a TXOP on link " << +linkId);
2776 auto currThreshold = m_staMacs[0]->GetWifiPhy(id)->GetCcaEdThreshold();
2777 NS_TEST_EXPECT_MSG_EQ((static_cast<int8_t>(currThreshold) ==
2778 m_staMacs[0]->GetEmlsrManager()->GetMediumSyncOfdmEdThreshold()),
2779 isTimerActive,
2781 << " Unexpected value (" << currThreshold
2782 << ") for CCA ED threshold on link " << +id
2783 << " when MediumSyncDelay is "
2784 << (isTimerActive ? "active" : "inactive"));
2785 }
2786 };
2787
2788 auto txDuration =
2789 WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2790
2791 // in this test, BlockAck frames terminates TXOP. If a BlockAck frame is sent on an EMLSR link
2792 // other than the main PHY link, it means that the aux PHY associated with that link is in
2793 // sleep mode and it is resumed after the end of the BlockAck frame
2794 if (linkId != m_mainPhyId && linkId != m_nonEmlsrLink)
2795 {
2796 Simulator::Schedule(txDuration - NanoSeconds(1), [=, this]() {
2797 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetDevice()->GetPhy(linkId)->IsStateSleep(),
2798 true,
2799 "Aux PHY on link " << +linkId << " not in sleep mode");
2800 });
2801 Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
2802 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetDevice()->GetPhy(linkId)->IsStateSleep(),
2803 false,
2804 "Aux PHY on link " << +linkId << " in sleep mode");
2805 });
2806 }
2807
2808 switch (m_countBlockAck)
2809 {
2810 case 1:
2811 case 2:
2812 // do nothing, these are BlockAcks in response to the QoS data frames sent to establish
2813 // BA agreements in DL and UL direction
2814 break;
2815 case 3:
2816 if (linkId == m_nonEmlsrLink)
2817 {
2818 // this BlockAck has been sent on the non-EMLSR link, ignore it
2819 break;
2820 }
2821 m_checkBackoffStarted = true;
2822 if (!m_nonEmlsrLink)
2823 {
2824 m_countBlockAck++; // if all EMLSR links, next case is already executed now
2825 }
2826 [[fallthrough]];
2827 case 4:
2828 if (m_nonEmlsrLink && m_countBlockAck == 4)
2829 {
2830 // block transmissions on the non-EMLSR link
2831 Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
2832 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2833 m_apMac->GetAddress(),
2834 {*m_nonEmlsrLink});
2835 });
2836 }
2837 if (linkId == m_nonEmlsrLink)
2838 {
2839 // this BlockAck has been sent on the non-EMLSR link, ignore it
2840 break;
2841 }
2842 m_checkBackoffStarted = true;
2843 // check MediumSyncDelay timer on the EMLSR client after receiving BlockAck
2844 Simulator::Schedule(txDuration + NanoSeconds(1), checkMediumSyncDelayTimerActive);
2845 break;
2846 case 5:
2847 // Block Ack in response to the second data frame sent by the EMLSR client on EMLSR links.
2848 // Check that MediumSyncDelay timer is running on the link where the main PHY is operating
2849 // and that the number of backoff slots is not changed since the beginning of the TXOP
2850 Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
2851 checkMediumSyncDelayTimerActive();
2852 auto elapsed =
2853 m_staMacs[0]->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_mainPhyId);
2855 elapsed.has_value(),
2856 true,
2857 "MediumSyncDelay timer not running on link where main PHY is operating");
2859 m_staMacs[0]->GetEmlsrManager()->GetMediumSyncDuration() -
2860 *elapsed;
2861 });
2862
2863 Simulator::Schedule(txDuration, [=, this]() {
2864 m_checkBackoffStarted = false;
2866 true,
2867 "Backoff end time should have been calculated");
2868 // when this BlockAck is received, the TXOP ends and the main PHY link is unblocked,
2869 // which causes a new backoff timer to be generated if the backoff timer is not
2870 // already running
2872 });
2873
2874 // make aux PHYs not capable of transmitting frames
2875 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2876 m_apMac->GetAddress(),
2877 auxPhyLinks);
2878
2879 // generate data packets for another UL data frame, which will be sent on the link where
2880 // the main PHY is operating
2881 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2882 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000));
2883 break;
2884 case 6: {
2885 // block transmission on the main PHY link and on the non-EMLSR link (if any), so that
2886 // the next QoS frames are sent on a link where an aux PHY is operating
2887 std::set<uint8_t> linkIds{m_mainPhyId};
2888 if (m_nonEmlsrLink)
2889 {
2890 linkIds.insert(*m_nonEmlsrLink);
2891 }
2892 m_staMacs[0]->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
2893 AC_BE,
2895 m_apMac->GetAddress(),
2896 m_staMacs[0]->GetAddress(),
2897 {0},
2898 linkIds);
2899 }
2900 // make sure aux PHYs are capable of transmitting frames
2901 m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2902 m_apMac->GetAddress(),
2903 auxPhyLinks);
2904
2905 // generate data packets for another UL data frame
2906 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2907 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000));
2908 break;
2909 case 7:
2910 // make the aux PHY(s) not capable of transmitting frames
2911 m_staMacs[0]->GetEmlsrManager()->SetAuxPhyTxCapable(false);
2912 if (!m_nonEmlsrLink)
2913 {
2914 // if there are two non-primary links, set MediumSyncDuration to zero so that the
2915 // next UL QoS data frame is not protected also in case it is transmitted on the
2916 // non-primary link other than the one on which the last frame exchange occurred
2917 m_staMacs[0]->GetEmlsrManager()->SetMediumSyncDuration(Seconds(0));
2918 }
2919
2920 // generate a very large backoff for the primary link, so that when an aux PHY gains a
2921 // TXOP, it requests the main PHY to switch to its link to transmit the frames
2922 m_staMacs[0]->GetQosTxop(AC_BE)->StartBackoffNow(100, m_mainPhyId);
2923
2924 // events to be scheduled at the end of the BlockAck response
2925 Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
2926 // check that the main PHY switches to its primary link
2927 auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId);
2928 auto mainPhyLinkid = m_staMacs[0]->GetLinkForPhy(mainPhy);
2929
2930 NS_TEST_EXPECT_MSG_EQ(mainPhy->IsStateSwitching(),
2931 true,
2932 "Main PHY is not switching at time "
2933 << Simulator::Now().As(Time::NS));
2934 NS_TEST_ASSERT_MSG_EQ(mainPhyLinkid.has_value(),
2935 true,
2936 "Main PHY should be operating on a link");
2937 NS_TEST_EXPECT_MSG_EQ(+mainPhyLinkid.value(),
2938 +m_mainPhyId,
2939 "Main PHY expected to operate on the primary link");
2940
2941 // events to be scheduled when the first main PHY channel switch is completed
2942 Simulator::Schedule(mainPhy->GetChannelSwitchDelay(), [=, this]() {
2943 auto acBe = m_staMacs[0]->GetQosTxop(AC_BE);
2944
2945 // find the min remaining backoff time on non-primary links for AC BE
2946 auto minBackoff = Time::Max();
2947 for (uint8_t id = 0; id < m_staMacs[0]->GetNLinks(); id++)
2948 {
2949 // update backoff
2950 m_staMacs[0]->GetChannelAccessManager(id)->NeedBackoffUponAccess(acBe,
2951 true,
2952 true);
2953 if (auto backoff =
2954 m_staMacs[0]->GetChannelAccessManager(id)->GetBackoffEndFor(acBe);
2955 id != m_mainPhyId && m_staMacs[0]->IsEmlsrLink(id) && backoff < minBackoff)
2956 {
2957 minBackoff = backoff;
2958 }
2959 }
2960
2961 // if the backoff on a link has expired before the end of the main PHY channel
2962 // switch, the main PHY will be requested to switch again at the first slot
2963 // boundary after the end of the channel switch. Otherwise, it will be requested
2964 // to switch when the backoff expires.
2965 auto expected2ndSwitchDelay = (minBackoff <= Simulator::Now())
2966 ? mainPhy->GetSlot()
2967 : (minBackoff - Simulator::Now());
2968
2969 // check that the main PHY is requested to switch to a non-primary link after
2970 // the expected delay
2971 Simulator::Schedule(expected2ndSwitchDelay + NanoSeconds(1), [=, this]() {
2972 NS_TEST_EXPECT_MSG_EQ(mainPhy->IsStateSwitching(),
2973 true,
2974 "Main PHY is not switching at time "
2975 << Simulator::Now().As(Time::NS));
2976 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetLinkForPhy(mainPhy).has_value(),
2977 false,
2978 "Main PHY should not be operating on a link because it "
2979 "should be switching to a non-primary link");
2980
2981 // events to be scheduled when main PHY finishes switching to non-primary link
2982 Simulator::Schedule(mainPhy->GetDelayUntilIdle(), [=, this]() {
2983 auto nonPrimLinkId = m_staMacs[0]->GetLinkForPhy(mainPhy);
2984
2985 NS_TEST_ASSERT_MSG_EQ(nonPrimLinkId.has_value(),
2986 true,
2987 "Main PHY should have completed switching");
2988 // update backoff on the non-primary link on which main PHY is operating
2989 m_staMacs[0]
2990 ->GetChannelAccessManager(*nonPrimLinkId)
2991 ->NeedBackoffUponAccess(acBe, true, true);
2992 // record the time the transmission of the QoS data frames must have
2993 // started: (a PIFS after) end of channel switch, if the backoff counter
2994 // on the non-primary link is null and UseAuxPhyCca is true (false); when
2995 // the backoff expires, otherwise
2996 if (auto slots = acBe->GetBackoffSlots(*nonPrimLinkId); slots == 0)
2997 {
2998 m_5thQosFrameTxTime =
2999 Simulator::Now() + (m_useAuxPhyCca ? Time{0} : mainPhy->GetPifs());
3000 }
3001 else
3002 {
3003 m_5thQosFrameTxTime = m_staMacs[0]
3004 ->GetChannelAccessManager(*nonPrimLinkId)
3005 ->GetBackoffEndFor(acBe);
3006 }
3007 });
3008 });
3009 });
3010 });
3011
3012 // generate data packets for another UL data frame
3013 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
3014 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000));
3015
3016 break;
3017 }
3018}
3019
3020void
3022 const WifiTxVector& txVector,
3023 uint8_t linkId)
3024{
3025 CtrlTriggerHeader trigger;
3026 mpdu->GetPacket()->PeekHeader(trigger);
3027 if (!trigger.IsMuRts())
3028 {
3029 return;
3030 }
3031
3032 if (linkId != m_mainPhyId && linkId != m_nonEmlsrLink)
3033 {
3034 // this is an ICF sent on an aux PHY link to start a DL TXOP. Given that aux PHYs do not
3035 // switch channel, they are put in sleep mode while the main PHY is operating on their link
3036 auto auxPhy = m_staMacs[0]->GetWifiPhy(linkId);
3037 auto txDuration =
3038 WifiPhy::CalculateTxDuration(mpdu->GetSize(), txVector, auxPhy->GetPhyBand());
3039 Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
3040 NS_TEST_EXPECT_MSG_EQ(auxPhy->IsStateSleep(),
3041 true,
3042 "Aux PHY on link " << +linkId << " not in sleep mode");
3043 });
3044 }
3045}
3046
3047void
3049 const WifiTxVector& txVector,
3050 uint8_t linkId)
3051{
3053 {
3054 // this function only considers RTS frames sent after the first QoS data frame
3055 return;
3056 }
3057
3058 if (linkId != m_mainPhyId)
3059 {
3060 if (m_countRtsframes > 0 && !m_corruptCts.has_value())
3061 {
3062 // we get here for the frame exchange in which the CTS response must be corrupted.
3063 // Install post reception error model on the STA affiliated with the EMLSR client that
3064 // is transmitting this RTS frame
3066 m_staMacs[0]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_errorModel);
3067 m_corruptCts = true;
3068 }
3069
3070 return;
3071 }
3072
3073 // we get here for RTS frames sent by the main PHY while the MediumSyncDelay timer is running
3075
3077 m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId)->GetChannelWidth(),
3078 "RTS sent by main PHY on an unexpected width");
3079
3080 // corrupt reception at AP MLD
3081 NS_LOG_INFO("CORRUPTED");
3082 m_errorModel->SetList({mpdu->GetPacket()->GetUid()});
3083}
3084
3085void
3087 const WifiTxVector& txVector,
3088 uint8_t linkId)
3089{
3091 {
3092 // this function only considers CTS frames sent after the first QoS data frame
3093 return;
3094 }
3095
3096 auto txDuration = WifiPhy::CalculateTxDuration(mpdu->GetSize(),
3097 txVector,
3098 m_apMac->GetWifiPhy(linkId)->GetPhyBand());
3099
3100 if (linkId != m_mainPhyId && linkId != m_nonEmlsrLink &&
3101 mpdu->GetHeader().GetAddr1() == m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress())
3102 {
3103 // this is a CTS sent to an aux PHY starting an UL TXOP. Given that aux PHYs do not
3104 // switch channel, they are put in sleep mode when the main PHY starts operating on their
3105 // link, which coincides with the end of CTS plus two propagation delays
3106 auto auxPhy = m_staMacs[0]->GetWifiPhy(linkId);
3107 Simulator::Schedule(txDuration, [=, this]() {
3108 NS_TEST_EXPECT_MSG_EQ(auxPhy->IsStateSleep(),
3109 false,
3110 "Aux PHY on link " << +linkId << " already in sleep mode");
3111 });
3112 // if the CTS is corrupted, the TXOP ends and the aux PHY is not put to sleep
3113 auto isStateSleep = !(m_corruptCts.has_value() && *m_corruptCts);
3114
3117 [=, this]() {
3118 NS_TEST_EXPECT_MSG_EQ(auxPhy->IsStateSleep(),
3119 isStateSleep,
3120 "Aux PHY on link " << +linkId
3121 << (isStateSleep ? " not yet" : " already")
3122 << " in sleep mode");
3123 });
3124 }
3125
3126 if (m_corruptCts.has_value() && *m_corruptCts)
3127 {
3128 // corrupt reception at EMLSR client
3129 NS_LOG_INFO("CORRUPTED");
3130 m_errorModel->SetList({mpdu->GetPacket()->GetUid()});
3131 m_corruptCts = false;
3132
3133 // main PHY is about to complete channel switch when CTS ends
3134 Simulator::Schedule(txDuration, [=, this]() {
3135 const auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId);
3136 NS_TEST_EXPECT_MSG_EQ(mainPhy->IsStateSwitching(),
3137 true,
3138 "Expecting the main PHY to be switching link");
3139 });
3140 }
3141}
3142
3143void
3153
3154void
3156{
3157 if (m_msdMaxNTxops > 0)
3158 {
3162 "Unexpected number of RTS frames sent while the MediumSyncDelay timer is running");
3163 }
3164
3165 auto psduIt = m_txPsdus.cbegin();
3166
3167 // lambda to jump to the next QoS data frame or MU-RTS Trigger Frame or RTS transmitted
3168 // to/by an EMLSR client
3169 auto jumpToQosDataOrMuRts = [&]() {
3170 while (psduIt != m_txPsdus.cend() &&
3171 !psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData() &&
3172 !psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts())
3173 {
3174 auto psdu = psduIt->psduMap.cbegin()->second;
3175 if (psdu->GetHeader(0).IsTrigger())
3176 {
3177 CtrlTriggerHeader trigger;
3178 psdu->GetPayload(0)->PeekHeader(trigger);
3179 if (trigger.IsMuRts())
3180 {
3181 break;
3182 }
3183 }
3184 psduIt++;
3185 }
3186 };
3187
3188 /**
3189 * EMLSR client with EMLSR mode enabled on all links (main PHY ID = 1).
3190 *
3191 * main PHY│
3192 * blocked,│
3193 * aux PHYs││main PHY blocked│
3194 * cannot │
3195 * transmit│
3196 * │ ┌───┐ ┌──┐
3197 * [link 0] │CTS│ │BA│
3198 * ────────────────────────┬───┬┴───┴┬───┬───┬┴──┴─────────────────────────────────────────
3199 * │RTS│ │QoS│QoS│
3200 * └───┘ │ 6 │ 7 │
3201 * └───┴───┘
3202 * gen backoff gen backoff if MediumSyncDelay
3203 * ┌──┐ (also many times) not running timer expired ┌──┐
3204 * [link 1] │BA│ │ if allowed │ │ │BA│
3205 * ─────────┬───┬───┬┴──┴───────────────────────────┬───┬─────┬───┬────┬───┬───┬┴──┴───────
3206 * │QoS│QoS│ │RTS│ ... │RTS│ │QoS│QoS│
3207 * │ 4 │ 5 │ └───┘ └───┘ │ 8 │ 9 │
3208 * └───┴───┘ └───┴───┘
3209 *
3210 * [link 2]
3211 * ───────────────────────────────────────────────────────────────────────────
3212 *
3213 *
3214 *
3215 * EMLSR client with EMLSR mode enabled on links 0 and 1 (main PHY ID = 1).
3216 *
3217 * main PHY │
3218 * and │
3219 * non-EMLSR│
3220 * link │
3221 * blocked,│
3222 * aux PHYs││main PHY blocked│
3223 * cannot │
3224 * transmit│
3225 * │ ┌───┐ ┌──┐
3226 * [link 0] │CTS│ │BA│
3227 * ────────────────────────┬───┬┴───┴┬───┬───┬┴──┴─────────────────────────────────────────
3228 * │RTS│ │QoS│QoS│
3229 * └───┘ │ 8 │ 9 │
3230 * └───┴───┘
3231 * gen backoff gen backoff if MediumSyncDelay
3232 * ┌──┐ (also many times) not running timer expired ┌──┐
3233 * [link 1] │BA│ │ if allowed │ │ │BA│
3234 * ─────────┬───┬───┬┴──┴───────────────────────────┬───┬─────┬───┬────┬───┬───┬┴──┴───────
3235 * │QoS│QoS│ │RTS│ ... │RTS│ │QoS│QoS│
3236 * │ 4 │ 5 │ └───┘ └───┘ │ 10│ 11│
3237 * └───┴───┘ └───┴───┘
3238 * ┌──┐
3239 * [link 2] │BA│
3240 * ──────────┬───┬───┬┴──┴────────────────────────────────────────────────────────────
3241 * │QoS│QoS│
3242 * │ 6 │ 7 │
3243 * └───┴───┘
3244 *
3245 * For both scenarios, after the last frame exchange on the main PHY link, we have the
3246 * following frame exchanges on an EMLSR link where an aux PHY is operating on. After the
3247 * first frame exchange, aux PHYs are configured as non-TX capable. Note that the two frame
3248 * exchanges may occur on distinct non-primary EMLSR links.
3249 *
3250 * | main PHY || main PHY |
3251 * [ link ] ┌───┐ ┌───┐ ┌──┐| switches ||switches to| ┌──┐
3252 * [0 or 2] │CTS│ │CTS│ │BA│|to primary||non-primary|PIFS| │BA│
3253 * ──────┬───┬┴───X────┬───┬┴───┴┬───┬───┬┴──┴─────────────────────────────┬───┬───┬┴──┴────
3254 * │RTS│ │RTS│ │QoS│QoS│ │QoS│QoS│
3255 * └───┘ └───┘ │ X │ Y │ │ Z │ W │
3256 * └───┴───┘ └───┴───┘
3257 * For all EMLSR links scenario, X=10, Y=11, Z=12, W=13
3258 * For the scenario with a non-EMLSR link, X=12, Y=13, Z=14, W=15
3259 */
3260
3261 // jump to the first (non-Beacon) frame transmitted after establishing BA agreements and
3262 // enabling EMLSR mode
3263 while (psduIt != m_txPsdus.cend() &&
3264 (psduIt->startTx < m_firstUlPktsGenTime ||
3265 psduIt->psduMap.cbegin()->second->GetHeader(0).IsBeacon()))
3266 {
3267 ++psduIt;
3268 }
3269
3270 // the first QoS data frame is transmitted by the main PHY without RTS protection as soon
3271 // as transmissions on the link where the main PHY is operating are unblocked (at this
3272 // moment, aux PHYs cannot transmit)
3273 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3274 true,
3275 "First QoS data frame has not been transmitted");
3276 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
3277 true,
3278 "First QoS data frame should be transmitted without protection");
3279 NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
3280 +m_mainPhyId,
3281 "First QoS data frame should be transmitted by the main PHY");
3282 NS_TEST_EXPECT_MSG_GT_OR_EQ(psduIt->startTx,
3284 "First QoS data frame sent too early");
3285
3286 auto prevPsduIt = psduIt++;
3287 jumpToQosDataOrMuRts();
3288
3289 if (m_nonEmlsrLink)
3290 {
3291 // an additional data frame is sent concurrently on the non-EMLSR link
3293 (psduIt != m_txPsdus.cend()),
3294 true,
3295 "Expected another QoS data frame sent concurrently with the first frame");
3297 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
3298 true,
3299 "First data frame on non-EMLSR link should be transmitted without protection");
3300 NS_TEST_EXPECT_MSG_EQ(+psduIt->linkId,
3301 +m_nonEmlsrLink.value(),
3302 "First data frame expected to be transmitted on the non-EMLSR link");
3303 const auto txDuration =
3304 WifiPhy::CalculateTxDuration(prevPsduIt->psduMap,
3305 prevPsduIt->txVector,
3306 m_staMacs[0]->GetWifiPhy(prevPsduIt->phyId)->GetPhyBand());
3307 NS_TEST_EXPECT_MSG_LT(psduIt->startTx,
3308 prevPsduIt->startTx + txDuration,
3309 "First data frame on the non-EMLSR link not sent concurrently");
3310 psduIt++;
3311 jumpToQosDataOrMuRts();
3312 }
3313
3314 // the second QoS data frame is transmitted by the main PHY after that the aux PHY has
3315 // obtained a TXOP and sent an RTS
3316 // RTS
3317 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3318 true,
3319 "RTS before second QoS data frame has not been transmitted");
3320 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts(),
3321 true,
3322 "Second QoS data frame should be transmitted with protection");
3324 +psduIt->phyId,
3325 +m_mainPhyId,
3326 "RTS before second QoS data frame should not be transmitted by the main PHY");
3327 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3329 "RTS before second data frame transmitted on an unexpected width");
3330 psduIt++;
3331 // CTS
3332 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3333 true,
3334 "CTS before second QoS data frame has not been transmitted");
3335 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsCts(),
3336 true,
3337 "CTS before second QoS data frame has not been transmitted");
3338 psduIt++;
3339 // QoS Data
3340 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3341 true,
3342 "Second QoS data frame has not been transmitted");
3343 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
3344 true,
3345 "Second QoS data frame has not been transmitted");
3346 NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
3347 +m_mainPhyId,
3348 "Second QoS data frame should be transmitted by the main PHY");
3349 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3351 "Second data frame not transmitted on the same width as RTS");
3352
3353 bool moreQosDataFound = false;
3354
3355 while (++psduIt != m_txPsdus.cend())
3356 {
3357 jumpToQosDataOrMuRts();
3358 if (psduIt != m_txPsdus.cend() &&
3359 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData())
3360 {
3361 moreQosDataFound = true;
3362
3363 NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
3364 +m_mainPhyId,
3365 "Third QoS data frame should be transmitted by the main PHY");
3366 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3367 m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId)->GetChannelWidth(),
3368 "Expecting TX width of third data frame to equal the channel "
3369 "width used by the main PHY");
3371 psduIt->startTx,
3373 "Third QoS data frame sent before MediumSyncDelay timer expired");
3374
3375 break;
3376 }
3377 }
3378
3379 NS_TEST_EXPECT_MSG_EQ(moreQosDataFound,
3380 true,
3381 "Third QoS data frame transmitted by the main PHY not found");
3382
3383 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()), true, "Expected more frames");
3384 ++psduIt;
3385 jumpToQosDataOrMuRts();
3386
3387 // the first attempt at transmitting the fourth QoS data frame fails because CTS is corrupted
3388 // RTS
3389 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3390 true,
3391 "RTS before fourth QoS data frame has not been transmitted");
3392 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts(),
3393 true,
3394 "Fourth QoS data frame should be transmitted with protection");
3396 +psduIt->phyId,
3397 +m_mainPhyId,
3398 "RTS before fourth QoS data frame should not be transmitted by the main PHY");
3399 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3401 "RTS before fourth data frame transmitted on an unexpected width");
3402 psduIt++;
3403 // CTS
3404 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3405 true,
3406 "CTS before fourth QoS data frame has not been transmitted");
3407 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsCts(),
3408 true,
3409 "CTS before fourth QoS data frame has not been transmitted");
3410 psduIt++;
3411 jumpToQosDataOrMuRts();
3412
3413 // the fourth QoS data frame is transmitted by an aux PHY after that the aux PHY has
3414 // obtained a TXOP and sent an RTS
3415 // RTS
3416 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3417 true,
3418 "RTS before fourth QoS data frame has not been transmitted");
3419 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts(),
3420 true,
3421 "Fourth QoS data frame should be transmitted with protection");
3423 +psduIt->phyId,
3424 +m_mainPhyId,
3425 "RTS before fourth QoS data frame should not be transmitted by the main PHY");
3426 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3428 "RTS before fourth data frame transmitted on an unexpected width");
3429 psduIt++;
3430 // CTS
3431 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3432 true,
3433 "CTS before fourth QoS data frame has not been transmitted");
3434 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsCts(),
3435 true,
3436 "CTS before fourth QoS data frame has not been transmitted");
3437 psduIt++;
3438 // QoS Data
3439 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3440 true,
3441 "Fourth QoS data frame has not been transmitted");
3442 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
3443 true,
3444 "Fourth QoS data frame has not been transmitted");
3445 NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
3446 +m_mainPhyId,
3447 "Fourth QoS data frame should be transmitted by the main PHY");
3448 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3450 "Fourth data frame not transmitted on the same width as RTS");
3451
3452 psduIt++;
3453 jumpToQosDataOrMuRts();
3454
3455 // the fifth QoS data frame is transmitted by the main PHY on a non-primary link because
3456 // the aux PHY is not TX capable. The QoS data frame is not protected by RTS
3457 // QoS Data
3458 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3459 true,
3460 "Fifth QoS data frame has not been transmitted");
3461 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
3462 true,
3463 "Fifth QoS data frame has not been transmitted");
3464 NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
3465 +m_mainPhyId,
3466 "Fifth QoS data frame should be transmitted by the main PHY");
3467 NS_TEST_EXPECT_MSG_NE(+psduIt->linkId,
3468 +m_mainPhyId,
3469 "Fifth QoS data frame should be transmitted on a non-primary link");
3470 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3472 "Fifth data frame not transmitted on the correct channel width");
3473 // Do not check the start transmission time if a backoff is generated even when no
3474 // transmission is done (if the backoff expires while the main PHY is switching, a new
3475 // backoff is generated and, before this backoff expires, the main PHY may be requested
3476 // to switch to another non-primary link; this may happen multiple times...)
3478 {
3479 NS_TEST_EXPECT_MSG_LT_OR_EQ(psduIt->startTx,
3481 "Fifth data frame transmitted too late");
3482 }
3483}
3484
3487 std::string("Check EMLSR link switching (switchAuxPhy=") +
3488 std::to_string(params.switchAuxPhy) + ", resetCamStateAndInterruptSwitch=" +
3489 std::to_string(params.resetCamStateAndInterruptSwitch) +
3490 ", auxPhyMaxChWidth=" + std::to_string(params.auxPhyMaxChWidth) + "MHz )"),
3491 m_switchAuxPhy(params.switchAuxPhy),
3492 m_resetCamStateAndInterruptSwitch(params.resetCamStateAndInterruptSwitch),
3493 m_auxPhyMaxChWidth(params.auxPhyMaxChWidth),
3494 m_countQoSframes(0),
3495 m_countIcfFrames(0),
3496 m_countRtsFrames(0),
3497 m_txPsdusPos(0)
3498{
3499 m_nEmlsrStations = 1;
3501 m_linksToEnableEmlsrOn = {0, 1, 2}; // enable EMLSR on all links right after association
3502 m_mainPhyId = 1;
3503 m_establishBaDl = true;
3504 m_duration = Seconds(1.0);
3505 // when aux PHYs do not switch link, the main PHY switches back to its previous link after
3506 // a TXOP, hence the transition delay must exceed the channel switch delay (default: 250us)
3508}
3509
3510void
3512 uint8_t phyId,
3513 WifiConstPsduMap psduMap,
3514 WifiTxVector txVector,
3515 double txPowerW)
3516{
3517 EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
3518 auto linkId = m_txPsdus.back().linkId;
3519
3520 auto psdu = psduMap.begin()->second;
3521 auto nodeId = mac->GetDevice()->GetNode()->GetId();
3522
3523 switch (psdu->GetHeader(0).GetType())
3524 {
3526 NS_ASSERT_MSG(nodeId > 0, "APs do not send AssocReq frames");
3527 NS_TEST_EXPECT_MSG_EQ(+linkId, +m_mainPhyId, "AssocReq not sent by the main PHY");
3528 break;
3529
3530 case WIFI_MAC_MGT_ACTION: {
3531 auto [category, action] = WifiActionHeader::Peek(psdu->GetPayload(0));
3532
3533 if (nodeId == 1 && category == WifiActionHeader::PROTECTED_EHT &&
3534 action.protectedEhtAction ==
3536 {
3537 // the EMLSR client is starting the transmission of the EML OMN frame;
3538 // temporarily block transmissions of QoS data frames from the AP MLD to the
3539 // non-AP MLD on all the links but the one used for ML setup, so that we know
3540 // that the first QoS data frame is sent on the link of the main PHY
3541 std::set<uint8_t> linksToBlock;
3542 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
3543 {
3544 if (id != m_mainPhyId)
3545 {
3546 linksToBlock.insert(id);
3547 }
3548 }
3549 m_apMac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3550 AC_BE,
3552 m_staMacs[0]->GetAddress(),
3553 m_apMac->GetAddress(),
3554 {0},
3555 linksToBlock);
3556 }
3557 }
3558 break;
3559
3561 CheckInitialControlFrame(psduMap, txVector, linkId);
3562 break;
3563
3564 case WIFI_MAC_QOSDATA:
3565 CheckQosFrames(psduMap, txVector, linkId);
3566 break;
3567
3568 case WIFI_MAC_CTL_RTS:
3569 CheckRtsFrame(psduMap, txVector, linkId);
3570 break;
3571
3572 default:;
3573 }
3574}
3575
3576void
3578{
3579 Config::SetDefault("ns3::DefaultEmlsrManager::SwitchAuxPhy", BooleanValue(m_switchAuxPhy));
3580 Config::SetDefault("ns3::EmlsrManager::ResetCamState",
3582 Config::SetDefault("ns3::AdvancedEmlsrManager::InterruptSwitch",
3584 Config::SetDefault("ns3::EmlsrManager::AuxPhyChannelWidth", UintegerValue(m_auxPhyMaxChWidth));
3585 Config::SetDefault("ns3::WifiPhy::ChannelSwitchDelay", TimeValue(MicroSeconds(75)));
3586 Config::SetDefault("ns3::EhtConfiguration::MediumSyncDuration", TimeValue(Time{0}));
3587
3589
3591 for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
3592 {
3593 m_apMac->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_errorModel);
3594 }
3595
3596 // use channels of different widths
3597 for (auto mac : std::initializer_list<Ptr<WifiMac>>{m_apMac, m_staMacs[0]})
3598 {
3599 mac->GetWifiPhy(0)->SetOperatingChannel(
3601 mac->GetWifiPhy(1)->SetOperatingChannel(
3603 mac->GetWifiPhy(2)->SetOperatingChannel(
3605 }
3606}
3607
3608void
3618
3619void
3621 const WifiTxVector& txVector,
3622 uint8_t linkId)
3623{
3625
3626 switch (m_countQoSframes)
3627 {
3628 case 1:
3629 // unblock transmissions on all links
3630 m_apMac->GetMacQueueScheduler()->UnblockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3631 AC_BE,
3633 m_staMacs[0]->GetAddress(),
3634 m_apMac->GetAddress(),
3635 {0},
3636 {0, 1, 2});
3637 // block transmissions on the link used for ML setup
3638 m_apMac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3639 AC_BE,
3641 m_staMacs[0]->GetAddress(),
3642 m_apMac->GetAddress(),
3643 {0},
3644 {m_mainPhyId});
3645 // generate a new data packet, which will be sent on a link other than the one
3646 // used for ML setup, hence triggering a link switching on the EMLSR client
3647 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, 0, 2, 1000));
3648 break;
3649 case 2:
3650 // block transmission on the link used to send this QoS data frame
3651 m_apMac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3652 AC_BE,
3654 m_staMacs[0]->GetAddress(),
3655 m_apMac->GetAddress(),
3656 {0},
3657 {linkId});
3658 // generate a new data packet, which will be sent on the link that has not been used
3659 // so far, hence triggering another link switching on the EMLSR client
3660 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, 0, 2, 1000));
3661 break;
3662 case 3:
3663 // block transmission on the link used to send this QoS data frame
3664 m_apMac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3665 AC_BE,
3667 m_staMacs[0]->GetAddress(),
3668 m_apMac->GetAddress(),
3669 {0},
3670 {linkId});
3671 // unblock transmissions on the link used for ML setup
3672 m_apMac->GetMacQueueScheduler()->UnblockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3673 AC_BE,
3675 m_staMacs[0]->GetAddress(),
3676 m_apMac->GetAddress(),
3677 {0},
3678 {m_mainPhyId});
3679 // generate a new data packet, which will be sent again on the link used for ML setup,
3680 // hence triggering yet another link switching on the EMLSR client
3681 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, 0, 2, 1000));
3682 break;
3683 case 4:
3684 // block transmissions on all links at non-AP MLD side
3685 m_staMacs[0]->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3686 AC_BE,
3688 m_apMac->GetAddress(),
3689 m_staMacs[0]->GetAddress(),
3690 {0},
3691 {0, 1, 2});
3692 // unblock transmissions on the link used for ML setup (non-AP MLD side)
3693 m_staMacs[0]->GetMacQueueScheduler()->UnblockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3694 AC_BE,
3696 m_apMac->GetAddress(),
3697 m_staMacs[0]->GetAddress(),
3698 {0},
3699 {m_mainPhyId});
3700 // trigger establishment of BA agreement with AP as recipient
3701 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 4, 1000));
3702 break;
3703 case 5:
3704 // unblock transmissions on all links at non-AP MLD side
3705 m_staMacs[0]->GetMacQueueScheduler()->UnblockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3706 AC_BE,
3708 m_apMac->GetAddress(),
3709 m_staMacs[0]->GetAddress(),
3710 {0},
3711 {0, 1, 2});
3712 // block transmissions on the link used for ML setup (non-AP MLD side)
3713 m_staMacs[0]->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3714 AC_BE,
3716 m_apMac->GetAddress(),
3717 m_staMacs[0]->GetAddress(),
3718 {0},
3719 {m_mainPhyId});
3720 // generate a new data packet, which will be sent on a link other than the one
3721 // used for ML setup, hence triggering a link switching on the EMLSR client
3722 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000));
3723 break;
3724 }
3725}
3726
3727/**
3728 * AUX PHY switching enabled (X = channel switch delay)
3729 *
3730 * |--------- aux PHY A ---------|------ main PHY ------|-------------- aux PHY B -------------
3731 * ┌───┐ ┌───┐
3732 * │ICF│ │QoS│
3733 * ──────────────────────────┴───┴┬───┬┴───┴┬──┬────────────────────────────────────────────────
3734 * [link 0] │CTS│ │BA│
3735 * └───┘ └──┘
3736 *
3737 *
3738 * |--------- main PHY ----------|------------------ aux PHY A ----------------|--- main PHY ---
3739 * ┌───┐ ┌───┐ ┌───┐ ┌───┐
3740 * │ICF│ │QoS│ │ICF│ │QoS│
3741 * ───┴───┴┬───┬┴───┴┬──┬──────────────────────────────────────────────────┴───┴┬───┬┴───┴┬──┬──
3742 * [link 1]│CTS│ │BA│ │CTS│ │BA│
3743 * └───┘ └──┘ └───┘ └──┘
3744 *
3745 *
3746 * |--------------------- aux PHY B --------------------|------ main PHY ------|-- aux PHY A ---
3747 * ┌───┐ ┌───┐
3748 * │ICF│ │QoS│
3749 * ─────────────────────────────────────────────────┴───┴┬───┬┴───┴┬──┬─────────────────────────
3750 * [link 2] │CTS│ │BA│
3751 * └───┘ └──┘
3752 *
3753 * ... continued ...
3754 *
3755 * |----------------------------------------- aux PHY B ---------------------------------------
3756 * ─────────────────────────────────────────────────────────────────────────────────────────────
3757 * [link 0]
3758 *
3759 * |--------- main PHY ----------|X|X|------------------------ aux PHY A ----------------------
3760 * ┌───┐
3761 * │ACK│
3762 * ──────────┬───┬┴───┴────────────────────────────────────────────────────────────────────────
3763 * [link 1] │QoS│
3764 * └───┘
3765 *
3766 * |-------- aux PHY A ----------|X|---------------------- main PHY ---------------------------
3767 * ┌──┐
3768 * │BA│
3769 * ────────────────────────┬───X──────┬───┬┴──┴────────────────────────────────────────────────
3770 * [link 2] │RTS│ │QoS│
3771 * └───┘ └───┘
3772 ************************************************************************************************
3773 *
3774 * AUX PHY switching disabled (X = channel switch delay)
3775 *
3776 * |------------------------------------------ aux PHY A ---------------------------------------
3777 * |-- main PHY --|X|
3778 * ┌───┐ ┌───┐
3779 * │ICF│ │QoS│
3780 * ──────────────────────────┴───┴┬───┬┴───┴┬──┬────────────────────────────────────────────────
3781 * [link 0] │CTS│ │BA│
3782 * └───┘ └──┘
3783 *
3784 * |-main|
3785 * |--------- main PHY ----------| |-PHY-| |------ main PHY ------
3786 * ┌───┐ ┌───┐ ┌───┐ ┌───┐
3787 * │ICF│ │QoS│ │ICF│ │QoS│
3788 * ───┴───┴┬───┬┴───┴┬──┬──────────────────────────────────────────────────┴───┴┬───┬┴───┴┬──┬──
3789 * [link 1]│CTS│ │BA│ │CTS│ │BA│
3790 * └───┘ └──┘ └───┘ └──┘
3791 *
3792 *
3793 * |------------------------------------------ aux PHY B ---------------------------------------
3794 * |-- main PHY --|X|
3795 * ┌───┐ ┌───┐
3796 * │ICF│ │QoS│
3797 * ─────────────────────────────────────────────────┴───┴┬───┬┴───┴┬──┬─────────────────────────
3798 * [link 2] │CTS│ │BA│
3799 * └───┘ └──┘
3800 *
3801 * ... continued ...
3802 *
3803 * |----------------------------------------- aux PHY A ---------------------------------------
3804 * ─────────────────────────────────────────────────────────────────────────────────────────────
3805 * [link 0]
3806 *
3807 * |-------- main PHY --------| |--- main PHY ---|
3808 * ┌───┐
3809 * │ACK│
3810 * ──────────┬───┬┴───┴────────────────────────────────────────────────────────────────────────
3811 * [link 1] │QoS│
3812 * └───┘
3813 *
3814 * |------------------------------------------ aux PHY B --------------------------------------
3815 * |X||X| |X|-------------- main PHY --------------
3816 * ┌───┐ ┌──┐
3817 * │CTS│ │BA│
3818 * ────────────────────────┬───X───────────────┬───┬┴───┴┬───┬┴──┴─────────────────────────────
3819 * [link 2] │RTS│ │RTS│ │QoS│
3820 * └───┘ └───┘ └───┘
3821 *
3822 */
3823
3824void
3826 const WifiTxVector& txVector,
3827 uint8_t linkId)
3828{
3829 if (++m_countIcfFrames == 1)
3830 {
3831 m_txPsdusPos = m_txPsdus.size() - 1;
3832 }
3833
3834 // the first ICF is sent to protect ADDBA_REQ for DL BA agreement, then one ICF is sent before
3835 // each of the 4 DL QoS Data frames; finally, another ICF is sent before the ADDBA_RESP for UL
3836 // BA agreement. Hence, at any time the number of ICF sent is always greater than or equal to
3837 // the number of QoS data frames sent.
3838 NS_TEST_EXPECT_MSG_GT_OR_EQ(m_countIcfFrames, m_countQoSframes, "Unexpected number of ICFs");
3839
3840 auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId);
3841 auto phyRecvIcf = m_staMacs[0]->GetWifiPhy(linkId); // PHY receiving the ICF
3842
3843 auto currMainPhyLinkId = m_staMacs[0]->GetLinkForPhy(mainPhy);
3844 NS_TEST_ASSERT_MSG_EQ(currMainPhyLinkId.has_value(),
3845 true,
3846 "Didn't find the link on which the Main PHY is operating");
3847 NS_TEST_ASSERT_MSG_NE(phyRecvIcf,
3848 nullptr,
3849 "No PHY on the link where ICF " << m_countQoSframes << " was sent");
3850
3851 if (phyRecvIcf != mainPhy)
3852 {
3854 phyRecvIcf->GetChannelWidth(),
3856 "Aux PHY that received ICF "
3857 << m_countQoSframes << " is operating on a channel whose width exceeds the limit");
3858 }
3859
3860 // the first two ICFs (before ADDBA_REQ and before first DL QoS Data) and the ICF before the
3861 // ADDBA_RESP are received by the main PHY. If aux PHYs do not switch links, the ICF before
3862 // the last DL QoS Data is also received by the main PHY
3863 NS_TEST_EXPECT_MSG_EQ((phyRecvIcf == mainPhy),
3864 (m_countIcfFrames == 1 || m_countIcfFrames == 2 ||
3866 "Expecting that the ICF was received by the main PHY");
3867
3868 // if aux PHYs do not switch links, the main PHY is operating on its original link when
3869 // the transmission of an ICF starts
3870 NS_TEST_EXPECT_MSG_EQ(m_switchAuxPhy || currMainPhyLinkId == m_mainPhyId,
3871 true,
3872 "Main PHY is operating on an unexpected link ("
3873 << +currMainPhyLinkId.value() << ", expected " << +m_mainPhyId
3874 << ")");
3875
3876 auto txDuration =
3877 WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
3878
3879 // check that PHYs are operating on the expected link after the reception of the ICF
3880 Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
3881 // the main PHY must be operating on the link where ICF was sent
3882 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(linkId),
3883 mainPhy,
3884 "PHY operating on link where ICF was sent is not the main PHY");
3885
3886 // the behavior of Aux PHYs depends on whether they switch channel or not
3887 if (m_switchAuxPhy)
3888 {
3889 if (mainPhy != phyRecvIcf)
3890 {
3891 NS_TEST_EXPECT_MSG_EQ(phyRecvIcf->IsStateSwitching(),
3892 true,
3893 "Aux PHY expected to switch channel");
3894 }
3895 Simulator::Schedule(phyRecvIcf->GetChannelSwitchDelay(), [=, this]() {
3896 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(*currMainPhyLinkId),
3897 phyRecvIcf,
3898 "The Aux PHY that received the ICF is expected to operate "
3899 "on the link where Main PHY was before switching channel");
3900 });
3901 }
3902 else
3903 {
3904 NS_TEST_EXPECT_MSG_EQ(phyRecvIcf->IsStateSwitching(),
3905 false,
3906 "Aux PHY is not expected to switch channel");
3907 NS_TEST_EXPECT_MSG_EQ(phyRecvIcf->GetPhyBand(),
3908 mainPhy->GetPhyBand(),
3909 "The Aux PHY that received the ICF is expected to operate "
3910 "on the same band as the Main PHY");
3911 }
3912 });
3913}
3914
3915void
3917 const WifiTxVector& txVector,
3918 uint8_t linkId)
3919{
3920 // corrupt the first RTS frame (sent by the EMLSR client)
3921 if (++m_countRtsFrames == 1)
3922 {
3923 auto psdu = psduMap.begin()->second;
3924 m_errorModel->SetList({psdu->GetPacket()->GetUid()});
3925
3926 // check that when CTS timeout occurs, the main PHY is switching
3928 m_staMacs[0]->GetFrameExchangeManager(linkId)->GetWifiTxTimer().GetDelayLeft(),
3929 [=, this]() {
3930 auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId);
3931 NS_TEST_EXPECT_MSG_EQ(mainPhy->IsStateSwitching(),
3932 true,
3933 "Main PHY expected to be in SWITCHING state instead of "
3934 << mainPhy->GetState()->GetState());
3935
3936 // If main PHY channel switch can be interrupted, the main PHY should be back
3937 // operating on the primary link after a channel switch delay. Otherwise, it
3938 // will be operating on the primary link, if SwitchAuxPhy is false, or on the
3939 // link used to send the RTS, if SwitchAuxPhy is true, after the remaining
3940 // channel switching time plus the channel switch delay.
3941 auto newLinkId =
3943 auto delay = mainPhy->GetChannelSwitchDelay();
3945 {
3946 delay += mainPhy->GetDelayUntilIdle();
3947 }
3948 Simulator::Schedule(delay + TimeStep(1), [=, this]() {
3949 auto id = m_staMacs[0]->GetLinkForPhy(mainPhy);
3950 NS_TEST_EXPECT_MSG_EQ(id.has_value(),
3951 true,
3952 "Expected main PHY to operate on a link");
3954 newLinkId,
3955 "Main PHY is operating on an unexpected link");
3956 });
3957 });
3958 }
3959 // block transmissions on all other links at non-AP MLD side
3960 std::set<uint8_t> links{0, 1, 2};
3961 links.erase(linkId);
3962 m_staMacs[0]->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3963 AC_BE,
3965 m_apMac->GetAddress(),
3966 m_staMacs[0]->GetAddress(),
3967 {0},
3968 links);
3969}
3970
3971void
3973{
3974 NS_TEST_ASSERT_MSG_NE(m_txPsdusPos, 0, "BA agreement establishment not completed");
3975
3976 // Expected frame exchanges after ML setup and EML OMN exchange:
3977 // 1. (DL) ICF + CTS + ADDBA_REQ + ACK
3978 // 2. (UL) ADDBA_RESP + ACK
3979 // 3. (DL) ICF + CTS + DATA + BA
3980 // 4. (DL) ICF + CTS + DATA + BA
3981 // 5. (DL) ICF + CTS + DATA + BA
3982 // 6. (DL) ICF + CTS + DATA + BA
3983 // 7. (UL) ADDBA_REQ + ACK
3984 // 8. (DL) ICF + CTS + ADDBA_RESP + ACK
3985 // 9. (UL) DATA + BA
3986 // 10. (UL) RTS - CTS timeout
3987 // 11. (UL) (RTS + CTS + ) DATA + BA
3988
3989 // frame exchange 11 is protected if SwitchAuxPhy is false or (SwitchAuxPhy is true and) the
3990 // main PHY switch can be interrupted
3991 bool fe11protected = !m_switchAuxPhy || m_resetCamStateAndInterruptSwitch;
3992
3993 NS_TEST_EXPECT_MSG_EQ(m_countIcfFrames, 6, "Unexpected number of ICFs sent");
3994
3995 // frame exchanges without RTS because the EMLSR client sent the initial frame through main PHY
3996 const std::size_t nFrameExchNoRts = fe11protected ? 3 : 4;
3997
3998 const std::size_t nFrameExchWithRts = fe11protected ? 1 : 0;
3999
4001 m_txPsdusPos +
4002 m_countIcfFrames * 4 + /* frames in frame exchange with ICF */
4003 nFrameExchNoRts * 2 + /* frames in frame exchange without RTS */
4004 nFrameExchWithRts * 4 + /* frames in frame exchange with RTS */
4005 1, /* corrupted RTS */
4006 "Insufficient number of TX PSDUs");
4007
4008 // m_txPsdusPos points to the first ICF
4009 auto psduIt = std::next(m_txPsdus.cbegin(), m_txPsdusPos);
4010
4011 const std::size_t nFrameExchanges =
4012 m_countIcfFrames + nFrameExchNoRts + nFrameExchWithRts + 1 /* corrupted RTS */;
4013
4014 for (std::size_t i = 1; i <= nFrameExchanges; ++i)
4015 {
4016 if (i == 1 || (i >= 3 && i <= 6) || i == 8 || i == 10 || (i == 11 && fe11protected))
4017 {
4018 // frame exchanges with protection
4019 NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 &&
4020 (i < 9 ? psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsTrigger()
4021 : psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsRts())),
4022 true,
4023 "Expected a Trigger Frame (ICF)");
4024 psduIt++;
4025 if (i == 10)
4026 {
4027 continue; // corrupted RTS
4028 }
4029 NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 &&
4030 psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsCts()),
4031 true,
4032 "Expected a CTS");
4033 psduIt++;
4034 }
4035
4036 if (i == 1 || i == 2 || i == 7 || i == 8) // frame exchanges with ADDBA REQ/RESP frames
4037 {
4038 NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 &&
4039 psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsMgt()),
4040 true,
4041 "Expected a management frame");
4042 psduIt++;
4043 NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 &&
4044 psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsAck()),
4045 true,
4046 "Expected a Normal Ack");
4047 }
4048 else
4049 {
4050 NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 &&
4051 psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsQosData()),
4052 true,
4053 "Expected a QoS Data frame");
4054 psduIt++;
4055 NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 &&
4056 psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsBlockAck()),
4057 true,
4058 "Expected a BlockAck");
4059 }
4060 psduIt++;
4061 }
4062}
4063
4064EmlsrCcaBusyTest::EmlsrCcaBusyTest(uint16_t auxPhyMaxChWidth)
4065 : EmlsrOperationsTestBase(std::string("Check EMLSR link switching (auxPhyMaxChWidth=") +
4066 std::to_string(auxPhyMaxChWidth) + "MHz )"),
4067 m_auxPhyMaxChWidth(auxPhyMaxChWidth),
4068 m_channelSwitchDelay(MicroSeconds(75)),
4069 m_currMainPhyLinkId(0),
4070 m_nextMainPhyLinkId(0)
4071{
4072 m_nEmlsrStations = 1;
4074 m_linksToEnableEmlsrOn = {0, 1, 2}; // enable EMLSR on all links right after association
4075 m_mainPhyId = 1;
4076 m_establishBaUl = true;
4077 m_duration = Seconds(1.0);
4079}
4080
4081void
4089
4090void
4092{
4093 Config::SetDefault("ns3::DefaultEmlsrManager::SwitchAuxPhy", BooleanValue(true));
4094 Config::SetDefault("ns3::EmlsrManager::AuxPhyChannelWidth", UintegerValue(m_auxPhyMaxChWidth));
4095 Config::SetDefault("ns3::EmlsrManager::AuxPhyMaxModClass", StringValue("EHT"));
4096 Config::SetDefault("ns3::WifiPhy::ChannelSwitchDelay", TimeValue(m_channelSwitchDelay));
4097
4099
4100 // use channels of different widths
4101 for (auto mac : std::initializer_list<Ptr<WifiMac>>{m_apMac, m_staMacs[0], m_staMacs[1]})
4102 {
4103 mac->GetWifiPhy(0)->SetOperatingChannel(
4105 mac->GetWifiPhy(1)->SetOperatingChannel(
4107 mac->GetWifiPhy(2)->SetOperatingChannel(
4109 }
4110}
4111
4112void
4114{
4115 m_staMacs[1]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 1, 1, 2000));
4116
4117 // force the transmission of the packet to happen now on the given link.
4118 // Multiple ScheduleNow calls are needed because Node::AddApplication() schedules a call to
4119 // Application::Initialize(), which schedules a call to Application::StartApplication(), which
4120 // schedules a call to PacketSocketClient::Send(), which finally generates the packet
4121 Simulator::ScheduleNow([=, this]() {
4122 Simulator::ScheduleNow([=, this]() {
4123 Simulator::ScheduleNow([=, this]() {
4124 m_staMacs[1]->GetFrameExchangeManager(linkId)->StartTransmission(
4125 m_staMacs[1]->GetQosTxop(AC_BE),
4126 m_staMacs[1]->GetWifiPhy(linkId)->GetChannelWidth());
4127 });
4128 });
4129 });
4130
4131 // check that the other MLD started transmitting on the correct link
4132 Simulator::Schedule(TimeStep(1), [=, this]() {
4133 NS_TEST_EXPECT_MSG_EQ(m_staMacs[1]->GetWifiPhy(linkId)->IsStateTx(),
4134 true,
4135 "At time " << Simulator::Now().As(Time::NS)
4136 << ", other MLD did not start transmitting on link "
4137 << +linkId);
4138 });
4139}
4140
4141void
4143{
4144 auto currMainPhyLinkId = m_staMacs[0]->GetLinkForPhy(m_mainPhyId);
4145 NS_TEST_ASSERT_MSG_EQ(currMainPhyLinkId.has_value(),
4146 true,
4147 "Main PHY is not operating on any link");
4148 m_currMainPhyLinkId = *currMainPhyLinkId;
4150
4151 // request the main PHY to switch to another link
4152 m_staMacs[0]->GetEmlsrManager()->SwitchMainPhy(m_nextMainPhyLinkId,
4153 false,
4156
4157 // the other MLD transmits a packet to the AP
4159
4160 // schedule another packet transmission slightly (10 us) before the end of aux PHY switch
4163 this,
4165
4166 // first checkpoint is after that the preamble of the PPDU has been received
4168}
4169
4170/**
4171 * ┌───────────────┐
4172 * [link X] │ other to AP │CP3
4173 * ──────────────────────────────┴───────────────┴──────────────────────────────────────────────
4174 * |------ main PHY ------| |------------------- aux PHY ---------------------
4175 * .\_ _/
4176 * . \_ _/
4177 * . \_ _/
4178 * . \_ _/
4179 * [link Y] . CP1 \/ CP2
4180 * .┌───────────────┐
4181 * .│ other to AP │
4182 * ─────────────────────────┴───────────────┴────────────────────────────────────────────────────
4183 * |------------ aux PHY ----------|---------------------- main PHY ----------------------------
4184 *
4185 */
4186
4187void
4189{
4190 // first checkpoint is after that the preamble of the first PPDU has been received
4191 auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId);
4192
4193 // 1. Main PHY is switching
4194 NS_TEST_EXPECT_MSG_EQ(mainPhy->IsStateSwitching(), true, "Main PHY is not switching");
4195
4196 auto auxPhy = m_staMacs[0]->GetWifiPhy(m_nextMainPhyLinkId);
4197 NS_TEST_EXPECT_MSG_NE(mainPhy, auxPhy, "Main PHY is operating on an unexpected link");
4198
4199 // 2. Aux PHY is receiving the PHY header
4200 NS_TEST_EXPECT_MSG_EQ(auxPhy->IsReceivingPhyHeader(),
4201 true,
4202 "Aux PHY is not receiving a PHY header");
4203
4204 // 3. Main PHY dropped the preamble because it is switching
4205 NS_TEST_EXPECT_MSG_EQ(mainPhy->IsReceivingPhyHeader(),
4206 false,
4207 "Main PHY is receiving a PHY header");
4208
4209 // 4. Channel access manager on destination link (Y) has been notified of CCA busy, but not
4210 // until the end of transmission (main PHY dropped the preamble and notified CCA busy until
4211 // end of transmission but the channel access manager on link Y does not yet have a listener
4212 // attached to the main PHY; aux PHY notified CCA busy until the end of the PHY header field
4213 // being received)
4214 const auto caManager = m_staMacs[0]->GetChannelAccessManager(m_nextMainPhyLinkId);
4215 const auto endTxTime = m_staMacs[1]->GetChannelAccessManager(m_nextMainPhyLinkId)->m_lastTxEnd;
4216 NS_TEST_ASSERT_MSG_EQ(caManager->m_lastBusyEnd.contains(WIFI_CHANLIST_PRIMARY),
4217 true,
4218 "No CCA information for primary20 channel");
4220 caManager->m_lastBusyEnd[WIFI_CHANLIST_PRIMARY],
4222 "ChannelAccessManager on destination link not notified of CCA busy");
4224 caManager->m_lastBusyEnd[WIFI_CHANLIST_PRIMARY],
4225 endTxTime,
4226 "ChannelAccessManager on destination link notified of CCA busy until end of transmission");
4227
4228 // second checkpoint is after that the main PHY completed the link switch
4229 Simulator::Schedule(mainPhy->GetDelayUntilIdle() + TimeStep(1),
4231 this);
4232}
4233
4234void
4236{
4237 // second checkpoint is after that the main PHY completed the link switch. The channel access
4238 // manager on destination link (Y) is expected to be notified by the main PHY that medium is
4239 // busy until the end of the ongoing transmission
4240 const auto caManager = m_staMacs[0]->GetChannelAccessManager(m_nextMainPhyLinkId);
4241 const auto endTxTime = m_staMacs[1]->GetChannelAccessManager(m_nextMainPhyLinkId)->m_lastTxEnd;
4242 NS_TEST_ASSERT_MSG_EQ(caManager->m_lastBusyEnd.contains(WIFI_CHANLIST_PRIMARY),
4243 true,
4244 "No CCA information for primary20 channel");
4246 caManager->m_lastBusyEnd[WIFI_CHANLIST_PRIMARY],
4248 "ChannelAccessManager on destination link not notified of CCA busy");
4249 NS_TEST_EXPECT_MSG_GT_OR_EQ(caManager->m_lastBusyEnd[WIFI_CHANLIST_PRIMARY],
4250 endTxTime,
4251 "ChannelAccessManager on destination link not notified of CCA busy "
4252 "until end of transmission");
4253
4254 // third checkpoint is after that the aux PHY completed the link switch
4256}
4257
4258void
4260{
4261 // third checkpoint is after that the aux PHY completed the link switch. The channel access
4262 // manager on source link (X) is expected to be notified by the aux PHY that medium is
4263 // busy until the end of the ongoing transmission (even if the aux PHY was not listening to
4264 // link X when transmission started, its interface on link X recorded the transmission)
4265 const auto caManager = m_staMacs[0]->GetChannelAccessManager(m_currMainPhyLinkId);
4266 const auto endTxTime = m_staMacs[1]->GetChannelAccessManager(m_currMainPhyLinkId)->m_lastTxEnd;
4267 NS_TEST_ASSERT_MSG_EQ(caManager->m_lastBusyEnd.contains(WIFI_CHANLIST_PRIMARY),
4268 true,
4269 "No CCA information for primary20 channel");
4270 NS_TEST_EXPECT_MSG_GT_OR_EQ(caManager->m_lastBusyEnd[WIFI_CHANLIST_PRIMARY],
4272 "ChannelAccessManager on source link not notified of CCA busy");
4273 NS_TEST_EXPECT_MSG_GT_OR_EQ(caManager->m_lastBusyEnd[WIFI_CHANLIST_PRIMARY],
4274 endTxTime,
4275 "ChannelAccessManager on source link not notified of CCA busy "
4276 "until end of transmission");
4277}
4278
4280 : TestSuite("wifi-emlsr", Type::UNIT)
4281{
4282 AddTestCase(new EmlOperatingModeNotificationTest(), TestCase::Duration::QUICK);
4283 AddTestCase(new EmlOmnExchangeTest({1, 2}, MicroSeconds(0)), TestCase::Duration::QUICK);
4284 AddTestCase(new EmlOmnExchangeTest({1, 2}, MicroSeconds(2048)), TestCase::Duration::QUICK);
4285 AddTestCase(new EmlOmnExchangeTest({0, 1, 2, 3}, MicroSeconds(0)), TestCase::Duration::QUICK);
4286 AddTestCase(new EmlOmnExchangeTest({0, 1, 2, 3}, MicroSeconds(2048)),
4287 TestCase::Duration::QUICK);
4288 for (const auto& emlsrLinks :
4289 {std::set<uint8_t>{0, 1, 2}, std::set<uint8_t>{1, 2}, std::set<uint8_t>{0, 1}})
4290 {
4292 new EmlsrDlTxopTest(
4293 {1, 0, emlsrLinks, {MicroSeconds(32)}, {MicroSeconds(32)}, MicroSeconds(512)}),
4294 TestCase::Duration::QUICK);
4296 new EmlsrDlTxopTest(
4297 {1, 1, emlsrLinks, {MicroSeconds(64)}, {MicroSeconds(64)}, MicroSeconds(512)}),
4298 TestCase::Duration::QUICK);
4300 2,
4301 emlsrLinks,
4302 {MicroSeconds(128), MicroSeconds(256)},
4303 {MicroSeconds(128), MicroSeconds(256)},
4304 MicroSeconds(512)}),
4305 TestCase::Duration::QUICK);
4306 }
4307
4308 for (auto genBackoffAndUseAuxPhyCca : {true, false})
4309 {
4311 {{0, 1, 2}, 40, 20, MicroSeconds(5504), 3, genBackoffAndUseAuxPhyCca}),
4312 TestCase::Duration::QUICK);
4314 new EmlsrUlTxopTest({{0, 1}, 40, 20, MicroSeconds(5504), 1, genBackoffAndUseAuxPhyCca}),
4315 TestCase::Duration::QUICK);
4316 }
4317
4318 for (bool switchAuxPhy : {true, false})
4319 {
4320 for (bool resetCamStateAndInterruptSwitch : {true, false})
4321 {
4322 for (MHz_u auxPhyMaxChWidth : {20, 40, 80, 160})
4323 {
4325 {switchAuxPhy, resetCamStateAndInterruptSwitch, auxPhyMaxChWidth}),
4326 TestCase::Duration::QUICK);
4327 }
4328 }
4329 }
4330
4331 AddTestCase(new EmlsrCcaBusyTest(20), TestCase::Duration::QUICK);
4332 AddTestCase(new EmlsrCcaBusyTest(80), TestCase::Duration::QUICK);
4333}
4334
4335static 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.
void DoRun() override
Implementation to actually run this TestCase.
Test CCA busy notifications on EMLSR clients.
void DoSetup() override
Implementation to do any local setup required for this TestCase.
uint8_t m_nextMainPhyLinkId
the ID of the link the main PHY switches to
uint8_t m_currMainPhyLinkId
the ID of the link the main PHY switches from
void StartTraffic() override
Start the generation of traffic (needs to be overridden)
EmlsrCcaBusyTest(uint16_t auxPhyMaxChWidth)
Constructor.
void TransmitPacketToAp(uint8_t linkId)
Make the other MLD transmit a packet to the AP on the given link.
uint16_t m_auxPhyMaxChWidth
max channel width (MHz) supported by aux PHYs
void DoRun() override
Implementation to actually run this TestCase.
Time m_channelSwitchDelay
the PHY channel switch delay
void CheckPoint1()
Perform checks after that the preamble of the first PPDU has been received.
void CheckPoint3()
Perform checks after that the aux PHY completed the link switch.
void CheckPoint2()
Perform checks after that the main PHY completed the link switch.
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
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_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
bool m_useAuxPhyCca
whether CCA info from aux PHY is used when aux PHY is not TX capable
Time m_5thQosFrameTxTime
start transmission time of the 5th QoS data frame
MHz_u m_auxPhyChannelWidth
max width supported by aux PHYs
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
MHz_u m_channelWidth
width of the channels used by MLDs
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.
wifi EMLSR Test Suite
A container for one type of attribute.
Headers for Trigger frames.
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.
static constexpr bool DONT_REQUEST_ACCESS
do not request channel access when PHY switch ends
static constexpr bool DONT_RESET_BACKOFF
do not reset backoff on main PHY switch
Hold variables of type enum.
Definition enum.h:52
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)
an EUI-48 address
static Mac48Address GetBroadcast()
Implement the header for management frames of type association request.
Implement the header for management frames of type association and reassociation response.
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.
static Iterator Begin()
Definition node-list.cc:226
static Iterator End()
Definition node-list.cc:233
an address for a packet socket
void SetProtocol(uint16_t protocol)
Set the protocol.
void SetSingleDevice(uint32_t device)
Set the address to match only a specified NetDevice.
Give ns3::PacketSocket powers to ns3::Node.
void Install(Ptr< Node > node) const
Aggregate an instance of a ns3::PacketSocketFactory onto the provided node.
Smart pointer class similar to boost::intrusive_ptr.
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:560
static void Destroy()
Execute the events scheduled with ScheduleDestroy().
Definition simulator.cc:131
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:197
static void Run()
Run the simulation.
Definition simulator.cc:167
static EventId ScheduleNow(FUNC f, Ts &&... args)
Schedule an event to expire Now.
Definition simulator.h:594
static void Stop()
Tell the Simulator the calling event should be the last one executed.
Definition simulator.cc:175
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:25
Hold variables of type string.
Definition string.h:45
encapsulates test code
Definition test.h:1050
void AddTestCase(TestCase *testCase, Duration duration=Duration::QUICK)
Add an individual child TestCase to this test suite.
Definition test.cc:292
A suite of tests to run.
Definition test.h:1267
Type
Type of test.
Definition test.h:1274
Simulation virtual time values and global simulation resolution.
Definition nstime.h:94
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition time.cc:404
bool IsStrictlyPositive() const
Exactly equivalent to t > 0.
Definition nstime.h:340
@ US
microsecond
Definition nstime.h:107
@ MS
millisecond
Definition nstime.h:106
@ NS
nanosecond
Definition nstime.h:108
bool IsZero() const
Exactly equivalent to t == 0.
Definition nstime.h:304
Hold an unsigned integer type.
Definition uinteger.h:34
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
static int64_t AssignStreams(NetDeviceContainer c, int64_t stream)
Assign a fixed random variable stream number to the random variables used by the PHY and MAC aspects ...
Implements the IEEE 802.11 MAC header.
create MAC layers for a ns3::WifiNetDevice.
uint64_t GetDataRate(MHz_u channelWidth, Time guardInterval, uint8_t nss) const
Definition wifi-mode.cc:111
void SetPcapCaptureType(PcapCaptureType type)
Set the PCAP capture type to be used.
void SetPcapDataLinkType(SupportedPcapDataLinkTypes dlt)
Set the data link type of PCAP traces to be used.
void Set(std::string name, const AttributeValue &v)
@ DLT_IEEE802_11_RADIO
Include Radiotap link layer information.
std::tuple< uint8_t, MHz_u, WifiPhyBand, uint8_t > ChannelTuple
Tuple identifying a segment of an operating channel.
Definition wifi-phy.h:919
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition wifi-phy.cc:1572
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
MHz_u 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:75
void SetDefault(std::string name, const AttributeValue &value)
Definition config.cc:883
void ConnectWithoutContext(std::string path, const CallbackBase &cb)
Definition config.cc:943
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition abort.h:97
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition log.h:257
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition log.h:264
Ptr< T > CreateObject(Args &&... args)
Create an object by type, with varying number of constructor parameters.
Definition object.h:619
Ptr< T > CreateObjectWithAttributes(Args... args)
Allocate an Object on the heap and initialize with a set of attributes.
Ptr< T > Create(Ts &&... args)
Create class instances by constructors with varying numbers of arguments and return them by Ptr.
Definition ptr.h:436
#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:986
#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:134
#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:820
#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:780
#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:946
#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:656
#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:241
#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:554
#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:905
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1332
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1344
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1308
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1320
WifiMacDropReason
The reason why an MPDU was dropped.
Definition wifi-mac.h:70
WifiQueueBlockedReason
Enumeration of the reasons to block container queues.
@ WIFI_STANDARD_80211be
@ WIFI_PREAMBLE_HT_MF
@ WIFI_PHY_BAND_6GHZ
The 6 GHz band.
@ WIFI_PHY_BAND_2_4GHZ
The 2.4 GHz band.
@ WIFI_PHY_BAND_5GHZ
The 5 GHz band.
@ WIFI_CHANLIST_PRIMARY
@ AC_BE
Best Effort.
Definition qos-utils.h:64
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:443
std::unordered_map< uint16_t, Ptr< const WifiPsdu > > WifiConstPsduMap
Map of const PSDUs indexed by STA-ID.
std:: tuple< WifiContainerQueueType, WifiReceiverAddressType, Mac48Address, std::optional< uint8_t > > WifiContainerQueueId
Tuple (queue type, receiver address type, Address, TID) identifying a container queue.
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:684
bool IsTrigger(const WifiPsduMap &psduMap)
Ptr< T1 > DynamicCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:580
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:50
static constexpr uint16_t SU_STA_ID
STA_ID to identify a single user (SU)
Definition wifi-mode.h:24
static constexpr uint8_t MAX_PROPAGATION_DELAY_USEC
maximum propagation delay
constexpr FrequencyRange WIFI_SPECTRUM_2_4_GHZ
Identifier for the frequency range covering the wifi spectrum in the 2.4 GHz band.
STL namespace.
phy
Definition third.py:78
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