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/eht-frame-exchange-manager.h"
17#include "ns3/emlsr-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
38using namespace ns3;
39
40NS_LOG_COMPONENT_DEFINE("WifiEmlsrTest");
41
44 "Check serialization and deserialization of the EML Operating Mode Notification frame")
45{
46}
47
48void
50{
51 MgtEmlOmn frame;
52
53 // Both EMLSR Mode and EMLMR Mode subfields set to 0 (no link bitmap);
55
56 frame.m_emlControl.emlsrMode = 1;
57 frame.SetLinkIdInBitmap(0);
58 frame.SetLinkIdInBitmap(5);
59 frame.SetLinkIdInBitmap(15);
60
61 // Adding Link Bitmap
63
64 NS_TEST_EXPECT_MSG_EQ((frame.GetLinkBitmap() == std::list<uint8_t>{0, 5, 15}),
65 true,
66 "Unexpected link bitmap");
67
68 auto padding = MicroSeconds(64);
69 auto transition = MicroSeconds(128);
70
74 frame.m_emlsrParamUpdate->transitionDelay =
76
77 // Adding the EMLSR Parameter Update field
79
82 padding,
83 "Unexpected EMLSR Padding Delay");
86 transition,
87 "Unexpected EMLSR Transition Delay");
88}
89
91 : TestCase(name)
92{
93}
94
95void
97 uint8_t phyId,
98 WifiConstPsduMap psduMap,
99 WifiTxVector txVector,
100 double txPowerW)
101{
102 auto linkId = mac->GetLinkForPhy(phyId);
103 NS_TEST_ASSERT_MSG_EQ(linkId.has_value(), true, "No link found for PHY ID " << +phyId);
104 m_txPsdus.push_back({Simulator::Now(), psduMap, txVector, *linkId, phyId});
105
106 auto txDuration =
107 WifiPhy::CalculateTxDuration(psduMap, txVector, mac->GetWifiPhy(*linkId)->GetPhyBand());
108
109 for (const auto& [aid, psdu] : psduMap)
110 {
111 std::stringstream ss;
112 ss << std::setprecision(10) << "PSDU #" << m_txPsdus.size() << " Link ID "
113 << +linkId.value() << " Phy ID " << +phyId << " " << psdu->GetHeader(0).GetTypeString();
114 if (psdu->GetHeader(0).IsAction())
115 {
116 ss << " ";
117 WifiActionHeader actionHdr;
118 psdu->GetPayload(0)->PeekHeader(actionHdr);
119 actionHdr.Print(ss);
120 }
121 ss << " #MPDUs " << psdu->GetNMpdus() << " duration/ID " << psdu->GetHeader(0).GetDuration()
122 << " RA = " << psdu->GetAddr1() << " TA = " << psdu->GetAddr2()
123 << " ADDR3 = " << psdu->GetHeader(0).GetAddr3()
124 << " ToDS = " << psdu->GetHeader(0).IsToDs()
125 << " FromDS = " << psdu->GetHeader(0).IsFromDs();
126 if (psdu->GetHeader(0).IsQosData())
127 {
128 ss << " seqNo = {";
129 for (auto& mpdu : *PeekPointer(psdu))
130 {
131 ss << mpdu->GetHeader().GetSequenceNumber() << ",";
132 }
133 ss << "} TID = " << +psdu->GetHeader(0).GetQosTid();
134 }
135 NS_LOG_INFO(ss.str());
136
137 // if this frame is transmitted by an EMLSR client on an EMLSR links, in-device interference
138 // is configured and the TX duration exceeds the threshold (72us), MediumSyncDelay timer is
139 // (re)started at the end of the transmission
140 if (auto staMac = DynamicCast<StaWifiMac>(mac);
141 staMac && staMac->IsEmlsrLink(*linkId) &&
142 staMac->GetEmlsrManager()->GetMediumSyncDuration().IsStrictlyPositive())
143 {
144 const auto mustStartMsd =
145 staMac->GetEmlsrManager()->GetInDeviceInterference() &&
147
148 for (auto id : staMac->GetLinkIds())
149 {
150 // timer started on EMLSR links other than the link on which TX is starting,
151 // provided that a PHY is operating on the link and MediumSyncDuration is not null
152 if (!staMac->IsEmlsrLink(id) || id == *linkId || staMac->GetWifiPhy(id) == nullptr)
153 {
154 continue;
155 }
157 txDuration - TimeStep(1),
158 [=, hdrType = psdu->GetHeader(0).GetTypeString(), this]() {
159 // check if MSD timer was running on the link before completing transmission
160 bool msdWasRunning = staMac->GetEmlsrManager()
161 ->GetElapsedMediumSyncDelayTimer(id)
162 .has_value();
163 if (auto phy = staMac->GetWifiPhy(id);
164 !msdWasRunning && !mustStartMsd && phy && phy->IsStateSleep())
165 {
166 // if the MSD timer was not running before the end of the TX, it is not
167 // expected to be started and the PHY operating on this link is
168 // sleeping, do not check that the MSD timer is not started after the
169 // end of the TX, because it may be started because of the sleep period
170 // of the aux PHY
171 return;
172 }
173 Simulator::Schedule(TimeStep(2), [=, this]() {
175 id,
176 (msdWasRunning || mustStartMsd),
177 std::string("after transmitting ") + hdrType +
178 " on link " + std::to_string(*linkId));
179 });
180 });
181 }
182 }
183 }
184 NS_LOG_INFO("TX duration = " << txDuration.As(Time::MS) << " TXVECTOR = " << txVector << "\n");
185}
186
187void
189 uint8_t linkId,
190 bool isRunning,
191 const std::string& msg)
192{
193 auto time = staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(linkId);
194 NS_TEST_ASSERT_MSG_EQ(time.has_value(),
195 isRunning,
197 << " Unexpected status for MediumSyncDelay timer on link " << +linkId
198 << " " << msg);
199 if (auto phy = staMac->GetWifiPhy(linkId))
200 {
201 auto currThreshold = phy->GetCcaEdThreshold();
202 NS_TEST_EXPECT_MSG_EQ((static_cast<int8_t>(currThreshold) ==
203 staMac->GetEmlsrManager()->GetMediumSyncOfdmEdThreshold()),
204 isRunning,
206 << " Unexpected value (" << currThreshold
207 << ") for CCA ED threshold on link " << +linkId << " " << msg);
208 }
209}
210
211void
213{
215 {
216 // if m_putAuxPhyToSleep is false, aux PHYs must not be put to sleep
217 sleep = false;
218 }
219
220 for (const auto& phy : staMac->GetDevice()->GetPhys())
221 {
222 if (phy->GetPhyId() == m_mainPhyId)
223 {
224 continue; // do not check the main PHY
225 }
226
227 auto linkId = staMac->GetLinkForPhy(phy);
228
229 if (linkId.has_value() && !staMac->IsEmlsrLink(*linkId))
230 {
231 continue; // this PHY is not operating on an EMLSR link
232 }
233
234 if (!sleep)
235 {
236 NS_TEST_EXPECT_MSG_EQ(phy->IsStateSleep(),
237 false,
239 << " PHY " << +phy->GetPhyId() << " is in unexpected state "
240 << phy->GetState()->GetState());
241 continue;
242 }
243
244 // if the PHY is in state TX or switching, sleep is postponed until their end
245 const auto delay =
246 (phy->IsStateTx() || phy->IsStateSwitching()) ? phy->GetDelayUntilIdle() : Time{0};
247
248 Simulator::Schedule(delay, [=, this]() {
249 NS_TEST_EXPECT_MSG_EQ(phy->IsStateSleep(),
250 true,
251 "PHY " << +phy->GetPhyId() << " is in unexpected state "
252 << phy->GetState()->GetState());
253 });
254 }
255}
256
257void
259 const EmlsrMainPhySwitchTrace& info)
260{
261 m_traceInfo[index] = info.Clone();
262}
263
264void
266 std::string_view reason,
267 const std::optional<uint8_t>& fromLinkId,
268 uint8_t toLinkId,
269 bool checkFromLinkId,
270 bool checkToLinkId)
271{
272 const auto traceInfoIt = m_traceInfo.find(index);
273 NS_TEST_ASSERT_MSG_EQ((traceInfoIt != m_traceInfo.cend()), true, "Expected stored trace info");
274 const auto& traceInfo = traceInfoIt->second;
275
276 NS_TEST_EXPECT_MSG_EQ(traceInfo->GetName(), reason, "Unexpected reason");
277
278 if (checkFromLinkId)
279 {
280 NS_TEST_ASSERT_MSG_EQ(traceInfo->fromLinkId.has_value(),
281 fromLinkId.has_value(),
282 "Unexpected stored from_link ID");
283 if (fromLinkId.has_value())
284 {
285 NS_TEST_EXPECT_MSG_EQ(+traceInfo->fromLinkId.value(),
286 +fromLinkId.value(),
287 "Unexpected from_link ID");
288 }
289 }
290
291 if (checkToLinkId)
292 {
293 NS_TEST_EXPECT_MSG_EQ(+traceInfo->toLinkId, +toLinkId, "Unexpected to_link ID");
294 }
295
296 m_traceInfo.erase(traceInfoIt);
297}
298
299void
301{
304 int64_t streamNumber = 100;
305
306 Config::SetDefault("ns3::WifiMac::MpduBufferSize", UintegerValue(64));
307 Config::SetDefault("ns3::EmlsrManager::InDeviceInterference", BooleanValue(true));
308 Config::SetDefault("ns3::EmlsrManager::PutAuxPhyToSleep", BooleanValue(m_putAuxPhyToSleep));
309
310 NodeContainer wifiApNode(1);
311 NodeContainer wifiStaNodes(m_nEmlsrStations);
312
313 WifiHelper wifi;
314 // wifi.EnableLogComponents ();
315 wifi.SetStandard(WIFI_STANDARD_80211be);
316 wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
317 "DataMode",
318 StringValue("EhtMcs0"),
319 "ControlMode",
320 StringValue("HtMcs0"));
321 wifi.ConfigEhtOptions("EmlsrActivated",
322 BooleanValue(true),
323 "TransitionTimeout",
325
326 // MLDs are configured with three links
327 SpectrumWifiPhyHelper phyHelper(3);
329 phyHelper.SetPcapCaptureType(WifiPhyHelper::PcapCaptureType::PCAP_PER_LINK);
330
331 for (std::size_t id = 0; id < m_channelsStr.size(); ++id)
332 {
333 phyHelper.Set(id, "ChannelSettings", StringValue(m_channelsStr[id]));
335 }
336
337 WifiMacHelper mac;
338 mac.SetType("ns3::ApWifiMac",
339 "Ssid",
340 SsidValue(Ssid("ns-3-ssid")),
341 "BeaconGeneration",
342 BooleanValue(true));
343 mac.SetApEmlsrManager("ns3::AdvancedApEmlsrManager",
344 "WaitTransDelayOnPsduRxError",
345 BooleanValue(true));
346
347 NetDeviceContainer apDevice = wifi.Install(phyHelper, mac, wifiApNode);
349
350 mac.SetType("ns3::StaWifiMac",
351 "Ssid",
352 SsidValue(Ssid("wrong-ssid")),
353 "MaxMissedBeacons",
354 UintegerValue(1e6), // do not deassociate
355 "ActiveProbing",
356 BooleanValue(false));
357 mac.SetEmlsrManager("ns3::AdvancedEmlsrManager",
358 "EmlsrLinkSet",
360 "MainPhyId",
362
363 if (m_nPhysPerEmlsrDevice < 3)
364 {
367 phyHelper.SetPcapCaptureType(WifiPhyHelper::PcapCaptureType::PCAP_PER_LINK);
368
369 for (std::size_t id = 0; id < m_nPhysPerEmlsrDevice; ++id)
370 {
371 phyHelper.Set(id, "ChannelSettings", StringValue(m_channelsStr[id]));
372 auto channel =
373 DynamicCast<MultiModelSpectrumChannel>(m_apMac->GetWifiPhy(id)->GetChannel());
374 NS_TEST_ASSERT_MSG_NE(channel,
375 nullptr,
376 "Channel " << +id << " is not a spectrum channel");
377 phyHelper.AddChannel(channel, m_freqRanges[id]);
378 }
379 }
380
381 NetDeviceContainer staDevices = wifi.Install(phyHelper, mac, wifiStaNodes);
382
383 for (uint32_t i = 0; i < staDevices.GetN(); i++)
384 {
385 auto device = DynamicCast<WifiNetDevice>(staDevices.Get(i));
386 auto staMac = DynamicCast<StaWifiMac>(device->GetMac());
387 auto emlsrManager = staMac->GetEmlsrManager();
388 NS_ASSERT_MSG(i < m_paddingDelay.size(), "Not enough padding delay values provided");
389 emlsrManager->SetAttribute("EmlsrPaddingDelay", TimeValue(m_paddingDelay.at(i)));
390 NS_ASSERT_MSG(i < m_transitionDelay.size(), "Not enough transition delay values provided");
391 emlsrManager->SetAttribute("EmlsrTransitionDelay", TimeValue(m_transitionDelay.at(i)));
392 emlsrManager->TraceConnectWithoutContext(
393 "MainPhySwitch",
395 }
396
397 if (m_nNonEmlsrStations > 0)
398 {
399 // create the other non-AP MLDs for which EMLSR is not activated
400 wifi.ConfigEhtOptions("EmlsrActivated", BooleanValue(false));
401 NodeContainer otherStaNodes(m_nNonEmlsrStations);
402 staDevices.Add(wifi.Install(phyHelper, mac, otherStaNodes));
403 wifiStaNodes.Add(otherStaNodes);
404 }
405
406 for (uint32_t i = 0; i < staDevices.GetN(); i++)
407 {
408 auto device = DynamicCast<WifiNetDevice>(staDevices.Get(i));
409 m_staMacs.push_back(DynamicCast<StaWifiMac>(device->GetMac()));
410 }
411
412 // Trace PSDUs passed to the PHY on AP MLD and non-AP MLDs
413 for (uint8_t phyId = 0; phyId < m_apMac->GetDevice()->GetNPhys(); phyId++)
414 {
416 "/NodeList/0/DeviceList/*/$ns3::WifiNetDevice/Phys/" + std::to_string(phyId) +
417 "/PhyTxPsduBegin",
419 }
420 for (std::size_t i = 0; i < m_nEmlsrStations + m_nNonEmlsrStations; i++)
421 {
422 for (uint8_t phyId = 0; phyId < m_staMacs[i]->GetDevice()->GetNPhys(); phyId++)
423 {
425 "/NodeList/" + std::to_string(i + 1) + "/DeviceList/*/$ns3::WifiNetDevice/Phys/" +
426 std::to_string(phyId) + "/PhyTxPsduBegin",
428 }
429 }
430
431 // Uncomment the lines below to write PCAP files
432 // phyHelper.EnablePcap("wifi-emlsr_AP", apDevice);
433 // phyHelper.EnablePcap("wifi-emlsr_STA", staDevices);
434
435 // Assign fixed streams to random variables in use
436 streamNumber += WifiHelper::AssignStreams(apDevice, streamNumber);
437 streamNumber += WifiHelper::AssignStreams(staDevices, streamNumber);
438
439 MobilityHelper mobility;
441
442 for (std::size_t id = 0; id <= m_nEmlsrStations + m_nNonEmlsrStations; id++)
443 {
444 // all non-AP MLDs are co-located
445 positionAlloc->Add(Vector(std::min<double>(id, 1), 0.0, 0.0));
446 }
447 mobility.SetPositionAllocator(positionAlloc);
448
449 mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
450 mobility.Install(wifiApNode);
451 mobility.Install(wifiStaNodes);
452
453 // install packet socket on all nodes
454 PacketSocketHelper packetSocket;
455 packetSocket.Install(wifiApNode);
456 packetSocket.Install(wifiStaNodes);
457
458 // install a packet socket server on all nodes
459 for (auto nodeIt = NodeList::Begin(); nodeIt != NodeList::End(); nodeIt++)
460 {
461 PacketSocketAddress srvAddr;
462 auto device = DynamicCast<WifiNetDevice>((*nodeIt)->GetDevice(0));
463 NS_TEST_ASSERT_MSG_NE(device, nullptr, "Expected a WifiNetDevice");
464 srvAddr.SetSingleDevice(device->GetIfIndex());
465 srvAddr.SetProtocol(1);
466
467 auto server = CreateObject<PacketSocketServer>();
468 server->SetLocal(srvAddr);
469 (*nodeIt)->AddApplication(server);
470 server->SetStartTime(Seconds(0)); // now
471 server->SetStopTime(m_duration);
472 }
473
474 // set DL and UL packet sockets
475 for (const auto& staMac : m_staMacs)
476 {
477 m_dlSockets.emplace_back();
478 m_dlSockets.back().SetSingleDevice(m_apMac->GetDevice()->GetIfIndex());
479 m_dlSockets.back().SetPhysicalAddress(staMac->GetDevice()->GetAddress());
480 m_dlSockets.back().SetProtocol(1);
481
482 m_ulSockets.emplace_back();
483 m_ulSockets.back().SetSingleDevice(staMac->GetDevice()->GetIfIndex());
484 m_ulSockets.back().SetPhysicalAddress(m_apMac->GetDevice()->GetAddress());
485 m_ulSockets.back().SetProtocol(1);
486 }
487
488 // schedule ML setup for one station at a time
489 m_apMac->TraceConnectWithoutContext("AssociatedSta",
491 Simulator::Schedule(Seconds(0), [&]() { m_staMacs[0]->SetSsid(Ssid("ns-3-ssid")); });
492}
493
496 std::size_t staId,
497 std::size_t count,
498 std::size_t pktSize) const
499{
500 auto client = CreateObject<PacketSocketClient>();
501 client->SetAttribute("PacketSize", UintegerValue(pktSize));
502 client->SetAttribute("MaxPackets", UintegerValue(count));
503 client->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
504 client->SetRemote(dir == DOWNLINK ? m_dlSockets.at(staId) : m_ulSockets.at(staId));
505 client->SetStartTime(Seconds(0)); // now
506 client->SetStopTime(m_duration - Simulator::Now());
507
508 return client;
509}
510
511void
513{
514 if (m_lastAid == aid)
515 {
516 // another STA of this non-AP MLD has already fired this callback
517 return;
518 }
519 m_lastAid = aid;
520
521 // wait some time (5ms) to allow the completion of association
522 auto delay = MilliSeconds(5);
523
524 if (m_establishBaDl)
525 {
526 // trigger establishment of BA agreement with AP as originator
527 Simulator::Schedule(delay, [=, this]() {
528 m_apMac->GetDevice()->GetNode()->AddApplication(
529 GetApplication(DOWNLINK, aid - 1, 4, 1000));
530 });
531
532 delay += MilliSeconds(5);
533 }
534
535 if (m_establishBaUl)
536 {
537 // trigger establishment of BA agreement with AP as recipient
538 Simulator::Schedule(delay, [=, this]() {
539 m_staMacs[aid - 1]->GetDevice()->GetNode()->AddApplication(
540 GetApplication(UPLINK, aid - 1, 4, 1000));
541 });
542
543 delay += MilliSeconds(5);
544 }
545
546 Simulator::Schedule(delay, [=, this]() {
548 {
549 // make the next STA start ML discovery & setup
550 m_staMacs[aid]->SetSsid(Ssid("ns-3-ssid"));
551 return;
552 }
553 // all stations associated; start traffic if needed
554 StartTraffic();
555 // stop generation of beacon frames in order to avoid interference
556 m_apMac->SetAttribute("BeaconGeneration", BooleanValue(false));
557 });
558}
559
560void
562 Mac48Address dest,
563 uint8_t linkId,
565 bool blocked,
566 std::string description,
567 bool testUnblockedForOtherReasons)
568{
570 auto mask = mac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, linkId);
571 NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
572 true,
573 description << ": Expected to find a mask for EMLSR link " << +linkId);
574 if (blocked)
575 {
576 NS_TEST_EXPECT_MSG_EQ(mask->test(static_cast<std::size_t>(reason)),
577 true,
578 description << ": Expected EMLSR link " << +linkId
579 << " to be blocked for reason " << reason);
580 if (testUnblockedForOtherReasons)
581 {
582 NS_TEST_EXPECT_MSG_EQ(mask->count(),
583 1,
584 description << ": Expected EMLSR link " << +linkId
585 << " to be blocked for one reason only");
586 }
587 }
588 else if (testUnblockedForOtherReasons)
589 {
590 NS_TEST_EXPECT_MSG_EQ(mask->none(),
591 true,
592 description << ": Expected EMLSR link " << +linkId
593 << " to be unblocked");
594 }
595 else
596 {
597 NS_TEST_EXPECT_MSG_EQ(mask->test(static_cast<std::size_t>(reason)),
598 false,
599 description << ": Expected EMLSR link " << +linkId
600 << " to be unblocked for reason " << reason);
601 }
602}
603
604EmlOmnExchangeTest::EmlOmnExchangeTest(const std::set<uint8_t>& linksToEnableEmlsrOn,
605 Time transitionTimeout)
606 : EmlsrOperationsTestBase("Check EML Notification exchange"),
607 m_checkEmlsrLinksCount(0),
608 m_emlNotificationDroppedCount(0)
609{
610 m_linksToEnableEmlsrOn = linksToEnableEmlsrOn;
613 m_transitionTimeout = transitionTimeout;
614 m_duration = Seconds(0.5);
615}
616
617void
619{
621
623 for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
624 {
625 m_apMac->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_errorModel);
626 }
627
628 m_staMacs[0]->TraceConnectWithoutContext("AckedMpdu",
630 m_staMacs[0]->TraceConnectWithoutContext("DroppedMpdu",
632}
633
634void
636 uint8_t phyId,
637 WifiConstPsduMap psduMap,
638 WifiTxVector txVector,
639 double txPowerW)
640{
641 EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
642 auto linkId = m_txPsdus.back().linkId;
643
644 auto psdu = psduMap.begin()->second;
645
646 switch (psdu->GetHeader(0).GetType())
647 {
649 NS_TEST_EXPECT_MSG_EQ(+linkId, +m_mainPhyId, "AssocReq not sent by the main PHY");
650 CheckEmlCapabilitiesInAssocReq(*psdu->begin(), txVector, linkId);
651 break;
652
654 CheckEmlCapabilitiesInAssocResp(*psdu->begin(), txVector, linkId);
655 break;
656
658 if (auto [category, action] = WifiActionHeader::Peek(psdu->GetPayload(0));
660 action.protectedEhtAction ==
662 {
663 CheckEmlNotification(psdu, txVector, linkId);
664
666 m_staMacs[0]->GetLinkIdByAddress(psdu->GetAddr2()) == linkId)
667 {
668 // transmitted by non-AP MLD, we need to corrupt it
669 m_uidList.push_front(psdu->GetPacket()->GetUid());
671 }
672 break;
673 }
674
675 default:;
676 }
677}
678
679void
681 const WifiTxVector& txVector,
682 uint8_t linkId)
683{
685 mpdu->GetPacket()->PeekHeader(frame);
686
687 const auto& mle = frame.Get<MultiLinkElement>();
688 NS_TEST_ASSERT_MSG_EQ(mle.has_value(), true, "Multi-Link Element must be present in AssocReq");
689
690 NS_TEST_ASSERT_MSG_EQ(mle->HasEmlCapabilities(),
691 true,
692 "Multi-Link Element in AssocReq must have EML Capabilities");
693 NS_TEST_ASSERT_MSG_EQ(mle->IsEmlsrSupported(),
694 true,
695 "EML Support subfield of EML Capabilities in AssocReq must be set to 1");
696 NS_TEST_ASSERT_MSG_EQ(mle->GetEmlsrPaddingDelay(),
697 m_paddingDelay.at(0),
698 "Unexpected Padding Delay in EML Capabilities included in AssocReq");
699 NS_TEST_ASSERT_MSG_EQ(mle->GetEmlsrTransitionDelay(),
700 m_transitionDelay.at(0),
701 "Unexpected Transition Delay in EML Capabilities included in AssocReq");
702}
703
704void
706 const WifiTxVector& txVector,
707 uint8_t linkId)
708{
709 bool sentToEmlsrClient =
710 (m_staMacs[0]->GetLinkIdByAddress(mpdu->GetHeader().GetAddr1()) == linkId);
711
712 if (!sentToEmlsrClient)
713 {
714 // nothing to check
715 return;
716 }
717
719 mpdu->GetPacket()->PeekHeader(frame);
720
721 const auto& mle = frame.Get<MultiLinkElement>();
722 NS_TEST_ASSERT_MSG_EQ(mle.has_value(), true, "Multi-Link Element must be present in AssocResp");
723
724 NS_TEST_ASSERT_MSG_EQ(mle->HasEmlCapabilities(),
725 true,
726 "Multi-Link Element in AssocResp must have EML Capabilities");
727 NS_TEST_ASSERT_MSG_EQ(mle->IsEmlsrSupported(),
728 true,
729 "EML Support subfield of EML Capabilities in AssocResp must be set to 1");
731 mle->GetTransitionTimeout(),
733 "Unexpected Transition Timeout in EML Capabilities included in AssocResp");
734}
735
736void
738 const WifiTxVector& txVector,
739 uint8_t linkId)
740{
741 MgtEmlOmn frame;
742 auto mpdu = *psdu->begin();
743 auto pkt = mpdu->GetPacket()->Copy();
745 pkt->RemoveHeader(frame);
746 NS_LOG_DEBUG(frame);
747
748 bool sentbyNonApMld = m_staMacs[0]->GetLinkIdByAddress(mpdu->GetHeader().GetAddr2()) == linkId;
749
751 1,
752 "EMLSR Mode subfield should be set to 1 (frame sent by non-AP MLD: "
753 << std::boolalpha << sentbyNonApMld << ")");
754
756 0,
757 "EMLMR Mode subfield should be set to 0 (frame sent by non-AP MLD: "
758 << std::boolalpha << sentbyNonApMld << ")");
759
761 true,
762 "Link Bitmap subfield should be present (frame sent by non-AP MLD: "
763 << std::boolalpha << sentbyNonApMld << ")");
764
765 auto setupLinks = m_staMacs[0]->GetSetupLinkIds();
766 std::list<uint8_t> expectedEmlsrLinks;
767 std::set_intersection(setupLinks.begin(),
768 setupLinks.end(),
771 std::back_inserter(expectedEmlsrLinks));
772
773 NS_TEST_EXPECT_MSG_EQ((expectedEmlsrLinks == frame.GetLinkBitmap()),
774 true,
775 "Unexpected Link Bitmap subfield (frame sent by non-AP MLD: "
776 << std::boolalpha << sentbyNonApMld << ")");
777
778 if (!sentbyNonApMld)
779 {
780 // the frame has been sent by the AP MLD
783 0,
784 "EMLSR Parameter Update Control should be set to 0 in frames sent by the AP MLD");
785
786 // as soon as the non-AP MLD receives this frame, it sets the EMLSR links
787 auto delay = WifiPhy::CalculateTxDuration(psdu,
788 txVector,
789 m_staMacs[0]->GetWifiPhy(linkId)->GetPhyBand()) +
790 MicroSeconds(1); // to account for propagation delay
792 }
793
795 +linkId,
796 "EML Notification received on unexpected link (frame sent by non-AP MLD: "
797 << std::boolalpha << sentbyNonApMld << ")");
798}
799
800void
802{
803 const auto& hdr = mpdu->GetHeader();
804
805 if (hdr.IsMgt() && hdr.IsAction())
806 {
807 if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
809 action.protectedEhtAction ==
811 {
812 // the EML Operating Mode Notification frame that the non-AP MLD sent has been
813 // acknowledged; after the transition timeout, the EMLSR links have been set
816 this);
817 }
818 }
819}
820
821void
823{
824 const auto& hdr = mpdu->GetHeader();
825
826 if (hdr.IsMgt() && hdr.IsAction())
827 {
828 if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
830 action.protectedEhtAction ==
832 {
833 // the EML Operating Mode Notification frame has been dropped. Don't
834 // corrupt it anymore
836 }
837 }
838}
839
840void
842{
844
845 auto setupLinks = m_staMacs[0]->GetSetupLinkIds();
846 std::set<uint8_t> expectedEmlsrLinks;
847 std::set_intersection(setupLinks.begin(),
848 setupLinks.end(),
851 std::inserter(expectedEmlsrLinks, expectedEmlsrLinks.end()));
852
853 NS_TEST_EXPECT_MSG_EQ((expectedEmlsrLinks == m_staMacs[0]->GetEmlsrManager()->GetEmlsrLinks()),
854 true,
855 "Unexpected set of EMLSR links)");
856}
857
858void
860{
863
865 2,
866 "Unexpected number of times CheckEmlsrLinks() is called");
869 1,
870 "Unexpected number of times the EML Notification frame is dropped due to max retry limit");
871
873}
874
876 : EmlsrOperationsTestBase("Check EML DL TXOP transmissions (" +
877 std::to_string(params.nEmlsrStations) + "," +
878 std::to_string(params.nNonEmlsrStations) + ")"),
879 m_emlsrLinks(params.linksToEnableEmlsrOn),
880 m_emlsrEnabledTime(0),
881 m_fe2to3delay(MilliSeconds(20)),
882 m_countQoSframes(0),
883 m_countBlockAck(0)
884{
885 m_nEmlsrStations = params.nEmlsrStations;
886 m_nNonEmlsrStations = params.nNonEmlsrStations;
887 m_linksToEnableEmlsrOn = {}; // do not enable EMLSR right after association
888 m_mainPhyId = 1;
889 m_paddingDelay = params.paddingDelay;
890 m_transitionDelay = params.transitionDelay;
891 m_transitionTimeout = params.transitionTimeout;
892 m_establishBaDl = true;
893 m_putAuxPhyToSleep = params.putAuxPhyToSleep;
894 m_duration = Seconds(1.5);
895
896 NS_ABORT_MSG_IF(params.linksToEnableEmlsrOn.size() < 2,
897 "This test requires at least two links to be configured as EMLSR links");
898}
899
900void
902 uint8_t phyId,
903 WifiConstPsduMap psduMap,
904 WifiTxVector txVector,
905 double txPowerW)
906{
907 EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
908 auto linkId = m_txPsdus.back().linkId;
909
910 auto psdu = psduMap.begin()->second;
911 auto nodeId = mac->GetDevice()->GetNode()->GetId();
912
913 switch (psdu->GetHeader(0).GetType())
914 {
916 NS_ASSERT_MSG(nodeId > 0, "APs do not send AssocReq frames");
917 if (nodeId <= m_nEmlsrStations)
918 {
919 NS_TEST_EXPECT_MSG_EQ(+linkId, +m_mainPhyId, "AssocReq not sent by the main PHY");
920 // this AssocReq is being sent by an EMLSR client. The other EMLSR links should be
921 // in powersave mode after association; we let the non-EMLSR links transition to
922 // active mode (by sending data null frames) after association
923 for (const auto id : m_staMacs.at(nodeId - 1)->GetLinkIds())
924 {
925 if (id != linkId && m_emlsrLinks.count(id) == 1)
926 {
927 m_staMacs[nodeId - 1]->SetPowerSaveMode({true, id});
928 }
929 }
930 }
931 break;
932
933 case WIFI_MAC_MGT_ACTION: {
934 auto [category, action] = WifiActionHeader::Peek(psdu->GetPayload(0));
935
936 if ((category == WifiActionHeader::PROTECTED_EHT) &&
937 (action.protectedEhtAction ==
939 {
940 nodeId == 0 ? CheckApEmlNotificationFrame(*psdu->begin(), txVector, linkId)
941 : CheckStaEmlNotificationFrame(*psdu->begin(), txVector, linkId);
942 }
943 else if (category == WifiActionHeader::BLOCK_ACK &&
945 {
946 CheckPmModeAfterAssociation(psdu->GetAddr1());
947 }
948 }
949 break;
950
952 CheckInitialControlFrame(*psdu->begin(), txVector, linkId);
953 break;
954
955 case WIFI_MAC_QOSDATA:
956 CheckQosFrames(psduMap, txVector, linkId);
957 break;
958
960 CheckBlockAck(psduMap, txVector, phyId);
961 break;
962
963 case WIFI_MAC_CTL_END:
964 if (auto apMac = DynamicCast<ApWifiMac>(mac))
965 {
966 const auto txDuration =
968 txVector,
969 apMac->GetDevice()->GetPhy(phyId)->GetPhyBand());
970 for (std::size_t i = 0; i < m_nEmlsrStations; ++i)
971 {
972 if (m_staMacs[i]->IsEmlsrLink(linkId) &&
973 m_staMacs[i]->GetWifiPhy(linkId) ==
974 m_staMacs[i]->GetDevice()->GetPhy(m_mainPhyId))
975 {
976 // AP is terminating a TXOP on an EMLSR link on which the main PHY is operating,
977 // aux PHYs should resume from sleep
978 Simulator::Schedule(txDuration + TimeStep(1),
980 this,
981 m_staMacs[i],
982 false);
983 }
984 }
985 }
986 break;
987
988 default:;
989 }
990}
991
992void
994{
995 // Channel switch delay should be less than the ICF padding duration, otherwise
996 // DL TXOPs cannot be initiated on auxiliary links
997 auto delay = std::min(MicroSeconds(100),
998 *std::min_element(m_paddingDelay.cbegin(), m_paddingDelay.cend()));
999 Config::SetDefault("ns3::WifiPhy::ChannelSwitchDelay", TimeValue(MicroSeconds(75)));
1000
1002
1004 for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
1005 {
1006 m_apMac->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_errorModel);
1007 }
1008
1009 m_apMac->GetQosTxop(AC_BE)->SetTxopLimits(
1010 {MicroSeconds(3200), MicroSeconds(3200), MicroSeconds(3200)});
1011
1013 {
1014 auto muScheduler =
1016 m_apMac->AggregateObject(muScheduler);
1017 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
1018 {
1019 m_apMac->GetFrameExchangeManager(linkId)->GetAckManager()->SetAttribute(
1020 "DlMuAckSequenceType",
1022 }
1023 }
1024}
1025
1026void
1028{
1030 {
1031 // we are done with association and Block Ack agreement; we can now enable EMLSR mode
1032 m_lastAid = 0;
1034 return;
1035 }
1036
1037 // we are done with sending EML Operating Mode Notification frames. We can now generate
1038 // packets for all non-AP MLDs
1039 for (std::size_t i = 0; i < m_nEmlsrStations + m_nNonEmlsrStations; i++)
1040 {
1041 // when multiple non-AP MLDs are present, MU transmission are used. Given that the
1042 // available bandwidth decreases as the number of non-AP MLDs increases, compute the
1043 // number of packets to generate so that we always have two A-MPDUs per non-AP MLD
1044 std::size_t count = 8 / (m_nEmlsrStations + m_nNonEmlsrStations);
1045 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, i, count, 450));
1046 }
1047
1048 // in case of 2 EMLSR clients using no non-EMLSR link, generate one additional short
1049 // packet to each EMLSR client to test transition delay
1050 if (m_nEmlsrStations == 2 && m_apMac->GetNLinks() == m_emlsrLinks.size())
1051 {
1053 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, 0, 1, 40));
1054 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, 1, 1, 40));
1055 });
1056 }
1057
1058 // schedule the transmission of EML Operating Mode Notification frames to disable EMLSR mode
1059 // and the generation of other packets destined to the EMLSR clients
1060 for (std::size_t id = 0; id < m_nEmlsrStations; id++)
1061 {
1062 Simulator::Schedule(m_fe2to3delay + MilliSeconds(5 * (id + 1)), [=, this]() {
1063 m_staMacs.at(id)->GetEmlsrManager()->SetAttribute(
1064 "EmlsrLinkSet",
1066 });
1067
1069 m_apMac->GetDevice()->GetNode()->AddApplication(
1071 });
1072 }
1073}
1074
1075void
1077{
1078 m_staMacs.at(m_lastAid)->GetEmlsrManager()->SetAttribute(
1079 "EmlsrLinkSet",
1081 m_lastAid++;
1082 Simulator::Schedule(MilliSeconds(5), [=, this]() {
1084 {
1085 // make the next STA send EML Notification frame
1087 return;
1088 }
1089 // all stations enabled EMLSR mode; start traffic
1091 StartTraffic();
1092 });
1093}
1094
1095void
1097{
1098 auto psduIt = m_txPsdus.cbegin();
1099
1100 // lambda to jump to the next QoS data frame or MU-RTS Trigger Frame transmitted
1101 // to an EMLSR client
1102 auto jumpToQosDataOrMuRts = [&]() {
1103 while (psduIt != m_txPsdus.cend() &&
1104 !psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData())
1105 {
1106 auto psdu = psduIt->psduMap.cbegin()->second;
1107 if (psdu->GetHeader(0).IsTrigger())
1108 {
1109 CtrlTriggerHeader trigger;
1110 psdu->GetPayload(0)->PeekHeader(trigger);
1111 if (trigger.IsMuRts())
1112 {
1113 break;
1114 }
1115 }
1116 psduIt++;
1117 }
1118 };
1119
1120 /**
1121 * Before enabling EMLSR mode, no MU-RTS TF should be sent. Four packets are generated
1122 * after association to trigger the establishment of a Block Ack agreement. The TXOP Limit
1123 * and the MCS are set such that two packets can be transmitted in a TXOP, hence we expect
1124 * that the AP MLD sends two A-MPDUs to each non-AP MLD.
1125 *
1126 * EMLSR client with EMLSR mode to be enabled on all links: after ML setup, all other links
1127 * stay in power save mode, hence BA establishment occurs on the same link.
1128 *
1129 * [link 0]
1130 * ───────────────────────────────────────────────────────────────────────────
1131 * | power save mode
1132 *
1133 * ┌─────┐ ┌─────┐ ┌───┬───┐ ┌───┬───┐
1134 * ┌───┐ │Assoc│ │ADDBA│ ┌───┐ │QoS│QoS│ │QoS│QoS│
1135 * [link 1] │ACK│ │Resp │ │ Req │ │ACK│ │ 0 │ 1 │ │ 2 │ 3 │
1136 * ───┬─────┬┴───┴──┴─────┴┬───┬─┴─────┴┬───┬─┬─────┬┴───┴─┴───┴───┴┬──┬─┴───┴───┴┬──┬───
1137 * │Assoc│ │ACK│ │ACK│ │ADDBA│ │BA│ │BA│
1138 * │ Req │ └───┘ └───┘ │Resp │ └──┘ └──┘
1139 * └─────┘ └─────┘
1140 *
1141 * [link 2]
1142 * ───────────────────────────────────────────────────────────────────────────
1143 * | power save mode
1144 *
1145 *
1146 * EMLSR client with EMLSR mode to be enabled on not all the links: after ML setup,
1147 * the other EMLSR links stay in power save mode, the non-EMLSR link (link 1) transitions
1148 * to active mode.
1149 *
1150 * ┌─────┐ ┌───┬───┐
1151 * ┌───┐ │ADDBA│ ┌───┐ │QoS│QoS│
1152 * [link 0 - non EMLSR] │ACK│ │ Req │ │ACK│ │ 2 │ 3 │
1153 * ──────────────────────────────┬────┬┴───┴──┴─────┴┬───┬─┬─────┬┴───┴─┴───┴───┴┬──┬─
1154 * │Data│ │ACK│ │ADDBA│ │BA│
1155 * │Null│ └───┘ │Resp │ └──┘
1156 * └────┘ └─────┘
1157 * ┌─────┐ ┌───┬───┐
1158 * ┌───┐ │Assoc│ │QoS│QoS│
1159 * [link 1] │ACK│ │Resp │ │ 0 │ 1 │
1160 * ───┬─────┬┴───┴──┴─────┴┬───┬──────────────────────────────────┴───┴───┴┬──┬───────
1161 * │Assoc│ │ACK│ │BA│
1162 * │ Req │ └───┘ └──┘
1163 * └─────┘
1164 *
1165 * [link 2]
1166 * ───────────────────────────────────────────────────────────────────────────
1167 * | power save mode
1168 *
1169 * Non-EMLSR client (not shown): after ML setup, all other links transition to active mode
1170 * by sending a Data Null frame; QoS data frame exchanges occur on two links simultaneously.
1171 */
1172 for (std::size_t i = 0; i < m_nEmlsrStations + m_nNonEmlsrStations; i++)
1173 {
1174 std::set<uint8_t> linkIds;
1175
1176 jumpToQosDataOrMuRts();
1177 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend() &&
1178 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData()),
1179 true,
1180 "Expected at least one QoS data frame before enabling EMLSR mode");
1181 linkIds.insert(psduIt->linkId);
1182 const auto firstAmpduTxEnd =
1183 psduIt->startTx +
1184 WifiPhy::CalculateTxDuration(psduIt->psduMap,
1185 psduIt->txVector,
1186 m_staMacs[i]->GetWifiPhy(psduIt->linkId)->GetPhyBand());
1187 psduIt++;
1188
1189 jumpToQosDataOrMuRts();
1190 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend() &&
1191 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData()),
1192 true,
1193 "Expected at least two QoS data frames before enabling EMLSR mode");
1194 linkIds.insert(psduIt->linkId);
1195 const auto secondAmpduTxStart = psduIt->startTx;
1196 psduIt++;
1197
1198 /**
1199 * If this is an EMLSR client and there is no setup link other than the one used to
1200 * establish association that is not an EMLSR link, then the two A-MPDUs are sent one
1201 * after another on the link used to establish association.
1202 */
1203 auto setupLinks = m_staMacs[i]->GetSetupLinkIds();
1204
1205 bool areAllSetupLinksEmlsr =
1206 std::all_of(setupLinks.begin(), setupLinks.end(), [&](auto&& linkId) {
1207 return linkId == m_mainPhyId || m_emlsrLinks.contains(linkId);
1208 });
1209
1210 if (i < m_nEmlsrStations && areAllSetupLinksEmlsr)
1211 {
1212 NS_TEST_EXPECT_MSG_EQ(linkIds.size(),
1213 1,
1214 "Expected both A-MPDUs to be sent on the same link");
1215 NS_TEST_EXPECT_MSG_EQ(*linkIds.begin(), +m_mainPhyId, "A-MPDUs sent on incorrect link");
1216 NS_TEST_EXPECT_MSG_LT(firstAmpduTxEnd,
1217 secondAmpduTxStart,
1218 "A-MPDUs are not sent one after another");
1219 }
1220 /**
1221 * Otherwise, the two A-MPDUs can be sent concurrently on two distinct links (may be
1222 * the link used to establish association and a non-EMLSR link).
1223 */
1224 else
1225 {
1226 NS_TEST_EXPECT_MSG_EQ(linkIds.size(),
1227 2,
1228 "Expected A-MPDUs to be sent on distinct links");
1229 NS_TEST_EXPECT_MSG_GT(firstAmpduTxEnd,
1230 secondAmpduTxStart,
1231 "A-MPDUs are not sent concurrently");
1232 }
1233 }
1234
1235 /**
1236 * After enabling EMLSR mode, MU-RTS TF should only be sent on EMLSR links. After the exchange
1237 * of EML Operating Mode Notification frames, a number of packets are generated at the AP MLD
1238 * to prepare two A-MPDUs for each non-AP MLD.
1239 *
1240 * EMLSR client with EMLSR mode to be enabled on all links (A is the EMLSR client, B is the
1241 * non-EMLSR client):
1242 * ┌─────┬─────┐
1243 * │QoS 4│QoS 5│
1244 * │ to A│ to A│
1245 * ┌───┐ ├─────┼─────┤
1246 * │MU │ │QoS 4│QoS 5│
1247 * [link 0] │RTS│ │ to B│ to B│
1248 * ──────────────────────────┴───┴┬───┬┴─────┴─────┴┬──┬────────────
1249 * │CTS│ │BA│
1250 * ├───┤ ├──┤
1251 * │CTS│ │BA│
1252 * └───┘ └──┘
1253 * ┌───┐ ┌─────┬─────┐
1254 * ┌───┐ │EML│ │QoS 6│QoS 7│
1255 * [link 1] │ACK│ │OM │ │ to B│ to B│
1256 * ────┬───┬┴───┴──┴───┴┬───┬─┴─────┴─────┴┬──┬────────────────────────────────────
1257 * │EML│ │ACK│ │BA│
1258 * │OM │ └───┘ └──┘
1259 * └───┘
1260 * ┌───┐ ┌─────┬─────┐
1261 * │MU │ │QoS 6│QoS 7│
1262 * [link 2] │RTS│ │ to A│ to A│
1263 * ─────────────────────────────────────────────────────────┴───┴┬───┬┴─────┴─────┴┬──┬─
1264 * │CTS│ │BA│
1265 * └───┘ └──┘
1266 *
1267 * EMLSR client with EMLSR mode to be enabled on not all the links (A is the EMLSR client,
1268 * B is the non-EMLSR client):
1269 * ┌─────┬─────┐
1270 * │QoS 4│QoS 5│
1271 * │ to A│ to A│
1272 * ├─────┼─────┤
1273 * │QoS 4│QoS 5│
1274 * [link 0 - non EMLSR] │ to B│ to B│
1275 * ───────────────────────────┴─────┴─────┴┬──┬───────────────────────────
1276 * │BA│
1277 * ├──┤
1278 * │BA│
1279 * └──┘
1280 * ┌─────┬─────┐
1281 * │QoS 6│QoS 7│
1282 * │ to A│ to A│
1283 * ┌───┐ ┌───┐ ├─────┼─────┤
1284 * ┌───┐ │EML│ │MU │ │QoS 6│QoS 7│
1285 * [link 1] │ACK│ │OM │ │RTS│ │ to B│ to B│
1286 * ────┬───┬┴───┴──┴───┴┬───┬─┴───┴┬───┬┴─────┴─────┴┬──┬────────────
1287 * │EML│ │ACK│ │CTS│ │BA│
1288 * │OM │ └───┘ ├───┤ ├──┤
1289 * └───┘ │CTS│ │BA│
1290 * └───┘ └──┘
1291 *
1292 * [link 2]
1293 * ────────────────────────────────────────────────────────────────────────────────
1294 */
1295
1296 /// Store a QoS data frame or an MU-RTS TF followed by a QoS data frame
1297 using FrameExchange = std::list<decltype(psduIt)>;
1298
1299 std::vector<std::list<FrameExchange>> frameExchanges(m_nEmlsrStations);
1300
1301 // compute all frame exchanges involving EMLSR clients
1302 while (psduIt != m_txPsdus.cend())
1303 {
1304 jumpToQosDataOrMuRts();
1305 if (psduIt == m_txPsdus.cend())
1306 {
1307 break;
1308 }
1309
1310 if (IsTrigger(psduIt->psduMap))
1311 {
1312 CtrlTriggerHeader trigger;
1313 psduIt->psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
1314 // this is an MU-RTS TF starting a new frame exchange sequence; add it to all
1315 // the addressed EMLSR clients
1317 true,
1318 "jumpToQosDataOrMuRts does not return TFs other than MU-RTS");
1319 for (const auto& userInfo : trigger)
1320 {
1321 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1322 {
1323 if (m_staMacs.at(i)->GetAssociationId() == userInfo.GetAid12())
1324 {
1325 frameExchanges.at(i).emplace_back(FrameExchange{psduIt});
1326 break;
1327 }
1328 }
1329 }
1330 psduIt++;
1331 continue;
1332 }
1333
1334 // we get here if psduIt points to a psduMap containing QoS data frame(s); find (if any)
1335 // the QoS data frame(s) addressed to EMLSR clients and add them to the appropriate
1336 // frame exchange sequence
1337 for (const auto& staIdPsduPair : psduIt->psduMap)
1338 {
1339 std::for_each_n(m_staMacs.cbegin(), m_nEmlsrStations, [&](auto&& staMac) {
1340 if (!staMac->GetLinkIdByAddress(staIdPsduPair.second->GetAddr1()))
1341 {
1342 // not addressed to this non-AP MLD
1343 return;
1344 }
1345 // a QoS data frame starts a new frame exchange sequence if there is no previous
1346 // MU-RTS TF that has been sent on the same link and is not already followed by
1347 // a QoS data frame
1348 std::size_t id = staMac->GetDevice()->GetNode()->GetId() - 1;
1349 for (auto& frameExchange : frameExchanges.at(id))
1350 {
1351 if (IsTrigger(frameExchange.front()->psduMap) &&
1352 frameExchange.front()->linkId == psduIt->linkId &&
1353 frameExchange.size() == 1)
1354 {
1355 auto it = std::next(frameExchange.front());
1356 while (it != m_txPsdus.end())
1357 {
1358 // stop at the first frame other than CTS sent on this link
1359 if (it->linkId == psduIt->linkId &&
1360 !it->psduMap.begin()->second->GetHeader(0).IsCts())
1361 {
1362 break;
1363 }
1364 ++it;
1365 }
1366 if (it == psduIt)
1367 {
1368 // the QoS data frame actually followed the MU-RTS TF
1369 frameExchange.emplace_back(psduIt);
1370 return;
1371 }
1372 }
1373 }
1374 frameExchanges.at(id).emplace_back(FrameExchange{psduIt});
1375 });
1376 }
1377 psduIt++;
1378 }
1379
1380 /**
1381 * Let's focus on the first two frame exchanges for each EMLSR clients. If all setup links are
1382 * EMLSR links, both frame exchanges are protected by MU-RTS TF and occur one after another.
1383 * Otherwise, one frame exchange occurs on the non-EMLSR link and is not protected by
1384 * MU-RTS TF; the other frame exchange occurs on an EMLSR link and is protected by MU-RTS TF.
1385 */
1386 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1387 {
1388 NS_TEST_EXPECT_MSG_GT_OR_EQ(frameExchanges.at(i).size(),
1389 2,
1390 "Expected at least 2 frame exchange sequences "
1391 << "involving EMLSR client " << i);
1392
1393 auto firstExchangeIt = frameExchanges.at(i).begin();
1394 auto secondExchangeIt = std::next(firstExchangeIt);
1395
1396 const auto firstAmpduTxEnd =
1397 firstExchangeIt->back()->startTx +
1399 firstExchangeIt->back()->psduMap,
1400 firstExchangeIt->back()->txVector,
1401 m_staMacs[i]->GetWifiPhy(firstExchangeIt->back()->linkId)->GetPhyBand());
1402 const auto secondAmpduTxStart = secondExchangeIt->front()->startTx;
1403
1404 if (m_staMacs[i]->GetNLinks() == m_emlsrLinks.size())
1405 {
1406 // all links are EMLSR links
1407 NS_TEST_EXPECT_MSG_EQ(IsTrigger(firstExchangeIt->front()->psduMap),
1408 true,
1409 "Expected an MU-RTS TF as ICF of first frame exchange sequence");
1411 firstExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1412 true,
1413 "Expected a QoS data frame in the first frame exchange sequence");
1414
1415 NS_TEST_EXPECT_MSG_EQ(IsTrigger(secondExchangeIt->front()->psduMap),
1416 true,
1417 "Expected an MU-RTS TF as ICF of second frame exchange sequence");
1419 secondExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1420 true,
1421 "Expected a QoS data frame in the second frame exchange sequence");
1422
1423 NS_TEST_EXPECT_MSG_LT(firstAmpduTxEnd,
1424 secondAmpduTxStart,
1425 "A-MPDUs are not sent one after another");
1426 }
1427 else
1428 {
1429 std::vector<uint8_t> nonEmlsrIds;
1430 auto setupLinks = m_staMacs[i]->GetSetupLinkIds();
1431 std::set_difference(setupLinks.begin(),
1432 setupLinks.end(),
1433 m_emlsrLinks.begin(),
1434 m_emlsrLinks.end(),
1435 std::back_inserter(nonEmlsrIds));
1436 NS_TEST_ASSERT_MSG_EQ(nonEmlsrIds.size(), 1, "Unexpected number of non-EMLSR links");
1437
1438 auto nonEmlsrLinkExchangeIt = firstExchangeIt->front()->linkId == nonEmlsrIds[0]
1439 ? firstExchangeIt
1440 : secondExchangeIt;
1441 NS_TEST_EXPECT_MSG_EQ(IsTrigger(nonEmlsrLinkExchangeIt->front()->psduMap),
1442 false,
1443 "Did not expect an MU-RTS TF as ICF on non-EMLSR link");
1445 nonEmlsrLinkExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1446 true,
1447 "Expected a QoS data frame on the non-EMLSR link");
1448
1449 auto emlsrLinkExchangeIt =
1450 nonEmlsrLinkExchangeIt == firstExchangeIt ? secondExchangeIt : firstExchangeIt;
1451 NS_TEST_EXPECT_MSG_NE(+emlsrLinkExchangeIt->front()->linkId,
1452 +nonEmlsrIds[0],
1453 "Expected this exchange not to occur on non-EMLSR link");
1454 NS_TEST_EXPECT_MSG_EQ(IsTrigger(emlsrLinkExchangeIt->front()->psduMap),
1455 true,
1456 "Expected an MU-RTS TF as ICF on the EMLSR link");
1458 emlsrLinkExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1459 true,
1460 "Expected a QoS data frame on the EMLSR link");
1461
1462 NS_TEST_EXPECT_MSG_GT(firstAmpduTxEnd,
1463 secondAmpduTxStart,
1464 "A-MPDUs are not sent concurrently");
1465 }
1466
1467 // we are done with processing the first two frame exchanges, remove them
1468 frameExchanges.at(i).erase(firstExchangeIt);
1469 frameExchanges.at(i).erase(secondExchangeIt);
1470 }
1471
1472 /**
1473 * A and B are two EMLSR clients. No ICF before the second QoS data frame because B
1474 * has not switched to listening mode. ICF is sent before the third QoS data frame because
1475 * A has switched to listening mode. C is a non-EMLSR client.
1476 *
1477 * ┌─────┐ A switches to listening
1478 * │QoS x│ after transition delay
1479 * │ to A│ |
1480 * ┌───┐ ├─────┤ ┌─────┐
1481 * │MU │ │QoS x│ │QoS y│
1482 * [link 0] │RTS│ │ to B│ │ to B│
1483 * ────────────┴───┴┬───┬┴─────┴┬──┬┴─────┴┬──┬────────────
1484 * │CTS│ │BA│ │BA│
1485 * ├───┤ ├──┤ └──┘
1486 * │CTS│ │BA│
1487 * └───┘ └──┘ AP continues the TXOP A switches to listening
1488 * after PIFS recovery after transition delay
1489 * │ │
1490 * ┌─────┐ ┌───┐ ┌─────┐ │┌───┐ ┌───┐
1491 * │QoS z│ │MU │ │QoS x│ ││MU │ ┌───┐ │CF-│
1492 * [link 1] │ to C│ │RTS│ │ to A│ ││RTS│ │BAR│ │End│
1493 * ───────────────────────────────┴─────┴┬──┬┴───┴┬───┬┴─────┴┬──┬┴───┴┬───┬┴───┴┬──┬┴───┴─
1494 * │BA│ │CTS│ │BA│ │CTS│ │BA│
1495 * └──┘ └───┘ └──x └───┘ └──┘
1496 */
1497 if (m_nEmlsrStations == 2 && m_apMac->GetNLinks() == m_emlsrLinks.size())
1498 {
1499 // the following checks are only done with 2 EMLSR clients having no non-EMLSR link
1500 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1501 {
1502 NS_TEST_EXPECT_MSG_GT_OR_EQ(frameExchanges.at(i).size(),
1503 2,
1504 "Expected at least 2 frame exchange sequences "
1505 << "involving EMLSR client " << i);
1506 // the first frame exchange must start with an ICF
1507 auto firstExchangeIt = frameExchanges.at(i).begin();
1508
1509 NS_TEST_EXPECT_MSG_EQ(IsTrigger(firstExchangeIt->front()->psduMap),
1510 true,
1511 "Expected an MU-RTS TF as ICF of first frame exchange sequence");
1513 firstExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1514 true,
1515 "Expected a QoS data frame in the first frame exchange sequence");
1516 }
1517
1518 // the second frame exchange is the one that starts first
1519 auto secondExchangeIt = std::next(frameExchanges.at(0).begin())->front()->startTx <
1520 std::next(frameExchanges.at(1).begin())->front()->startTx
1521 ? std::next(frameExchanges.at(0).begin())
1522 : std::next(frameExchanges.at(1).begin());
1523 decltype(secondExchangeIt) thirdExchangeIt;
1524 std::size_t thirdExchangeStaId;
1525
1526 if (secondExchangeIt == std::next(frameExchanges.at(0).begin()))
1527 {
1528 thirdExchangeIt = std::next(frameExchanges.at(1).begin());
1529 thirdExchangeStaId = 1;
1530 }
1531 else
1532 {
1533 thirdExchangeIt = std::next(frameExchanges.at(0).begin());
1534 thirdExchangeStaId = 0;
1535 }
1536
1537 // the second frame exchange is not protected by the ICF and starts a SIFS after the end
1538 // of the previous one
1539 NS_TEST_EXPECT_MSG_EQ(IsTrigger(secondExchangeIt->front()->psduMap),
1540 false,
1541 "Expected no ICF for the second frame exchange sequence");
1543 secondExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1544 true,
1545 "Expected a QoS data frame in the second frame exchange sequence");
1546
1547 // the first two frame exchanges occur on the same link
1548 NS_TEST_EXPECT_MSG_EQ(+secondExchangeIt->front()->linkId,
1549 +frameExchanges.at(0).begin()->front()->linkId,
1550 "Expected the first two frame exchanges to occur on the same link");
1551
1552 auto bAckRespIt = std::prev(secondExchangeIt->front());
1553 NS_TEST_EXPECT_MSG_EQ(bAckRespIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAck(),
1554 true,
1555 "Expected a BlockAck response before the second frame exchange");
1556 auto bAckRespTxEnd =
1557 bAckRespIt->startTx +
1558 WifiPhy::CalculateTxDuration(bAckRespIt->psduMap,
1559 bAckRespIt->txVector,
1560 m_apMac->GetWifiPhy(bAckRespIt->linkId)->GetPhyBand());
1561
1562 // the second frame exchange starts a SIFS after the previous one
1564 bAckRespTxEnd + m_apMac->GetWifiPhy(bAckRespIt->linkId)->GetSifs(),
1565 secondExchangeIt->front()->startTx,
1566 "Expected the second frame exchange to start a SIFS after the first one");
1567
1568 // the third frame exchange is protected by MU-RTS and occurs on a different link
1569 NS_TEST_EXPECT_MSG_EQ(IsTrigger(thirdExchangeIt->front()->psduMap),
1570 true,
1571 "Expected an MU-RTS as ICF for the third frame exchange sequence");
1573 thirdExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1574 true,
1575 "Expected a QoS data frame in the third frame exchange sequence");
1576
1578 +secondExchangeIt->front()->linkId,
1579 +thirdExchangeIt->front()->linkId,
1580 "Expected the second and third frame exchanges to occur on distinct links");
1581
1582 auto secondQosIt = secondExchangeIt->front();
1583 auto secondQosTxEnd =
1584 secondQosIt->startTx +
1585 WifiPhy::CalculateTxDuration(secondQosIt->psduMap,
1586 secondQosIt->txVector,
1587 m_apMac->GetWifiPhy(secondQosIt->linkId)->GetPhyBand());
1588
1589 NS_TEST_EXPECT_MSG_GT_OR_EQ(thirdExchangeIt->front()->startTx,
1590 secondQosTxEnd + m_transitionDelay.at(thirdExchangeStaId),
1591 "Transmission started before transition delay");
1592
1593 // the BlockAck of the third frame exchange is not received correctly, so there should be
1594 // another frame exchange
1595 NS_TEST_EXPECT_MSG_EQ((thirdExchangeIt != frameExchanges.at(thirdExchangeStaId).end()),
1596 true,
1597 "Expected a fourth frame exchange");
1598 auto fourthExchangeIt = std::next(thirdExchangeIt);
1599
1600 // the fourth frame exchange is protected by MU-RTS
1601 NS_TEST_EXPECT_MSG_EQ(IsTrigger(fourthExchangeIt->front()->psduMap),
1602 true,
1603 "Expected an MU-RTS as ICF for the fourth frame exchange sequence");
1604
1605 bAckRespIt = std::prev(fourthExchangeIt->front());
1606 NS_TEST_EXPECT_MSG_EQ(bAckRespIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAck(),
1607 true,
1608 "Expected a BlockAck response before the fourth frame exchange");
1609 auto phy = m_apMac->GetWifiPhy(bAckRespIt->linkId);
1610 bAckRespTxEnd = bAckRespIt->startTx + WifiPhy::CalculateTxDuration(bAckRespIt->psduMap,
1611 bAckRespIt->txVector,
1612 phy->GetPhyBand());
1613 auto timeout = phy->GetSifs() + phy->GetSlot() + MicroSeconds(20);
1614
1615 // the fourth frame exchange starts a PIFS after the previous one because the AP
1616 // performs PIFS recovery (the initial frame in the TXOP was successfully received by
1617 // a non-EMLSR client)
1618 NS_TEST_EXPECT_MSG_GT_OR_EQ(fourthExchangeIt->front()->startTx,
1619 bAckRespTxEnd + phy->GetPifs(),
1620 "Transmission started less than a PIFS after BlockAck");
1621 NS_TEST_EXPECT_MSG_LT(fourthExchangeIt->front()->startTx,
1622 bAckRespTxEnd + phy->GetPifs() +
1623 MicroSeconds(1) /* propagation delay upper bound */,
1624 "Transmission started too much time after BlockAck");
1625
1626 auto bAckReqIt = std::next(fourthExchangeIt->front(), 2);
1627 NS_TEST_EXPECT_MSG_EQ(bAckReqIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAckReq(),
1628 true,
1629 "Expected a BlockAck request in the fourth frame exchange");
1630
1631 // we are done with processing the frame exchanges, remove them (two frame exchanges
1632 // per EMLSR client, plus the last one)
1633 frameExchanges.at(0).pop_front();
1634 frameExchanges.at(0).pop_front();
1635 frameExchanges.at(1).pop_front();
1636 frameExchanges.at(1).pop_front();
1637 frameExchanges.at(thirdExchangeStaId).pop_front();
1638 }
1639
1640 /**
1641 * After disabling EMLSR mode, no MU-RTS TF should be sent. After the exchange of
1642 * EML Operating Mode Notification frames, a number of packets are generated at the AP MLD
1643 * to prepare two A-MPDUs for each EMLSR client.
1644 *
1645 * EMLSR client with EMLSR mode to be enabled on all links (A is the EMLSR client, B is the
1646 * non-EMLSR client):
1647 *
1648 * [link 0] | power save mode
1649 * ────────────────────────────────────────────────────────
1650 * ┌─────┬─────┐ ┌──────┬──────┐
1651 * │QoS 8│QoS 9│ │QoS 10│QoS 11│
1652 * │ to A│ to A│ │ to A │ to A │
1653 * ┌───┐ ┌───┐ ├─────┼─────┤ ├──────┼──────┤
1654 * ┌───┐ │MU │ │EML│ │QoS 8│QoS 9│ │QoS 10│QoS 11│
1655 * [link 1] │ACK│ │RTS│ │OM │ │ to B│ to B│ │ to B │ to B │
1656 * ────┬───┬┴───┴──┴───┴┬───┬┴───┴┬───┬──┴─────┴─────┴┬──┬────┴──────┴──────┴┬──┬─────
1657 * │EML│ │CTS│ │ACK│ │BA│ │BA│
1658 * │OM │ └───┘ └───┘ ├──┤ ├──┤
1659 * └───┘ │BA│ │BA│
1660 * └──┘ └──┘
1661 *
1662 * [link 2] | power save mode
1663 * ────────────────────────────────────────────────────────────────────────────
1664 *
1665 *
1666 * EMLSR client with EMLSR mode to be enabled on not all the links (A is the EMLSR client,
1667 * B is the non-EMLSR client):
1668 * ┌─────┬─────┐
1669 * │QoS 8│QoS 9│
1670 * │ to A│ to A│
1671 * ├─────┼─────┤
1672 * │QoS 8│QoS 9│
1673 * [link 0 - non EMLSR] │ to B│ to B│
1674 * ─────────────────────────────────────────┴─────┴─────┴┬──┬─────────────
1675 * │BA│
1676 * ├──┤
1677 * │BA│
1678 * └──┘
1679 * ┌──────┬──────┐
1680 * │QoS 10│QoS 11│
1681 * │ to A │ to A │
1682 * ┌───┐ ┌───┐ ├──────┼──────┤
1683 * ┌───┐ │MU │ │EML│ │QoS 10│QoS 11│
1684 * [link 1] │ACK│ │RTS│ │OM │ │ to B │ to B │
1685 * ────┬───┬┴───┴──┴───┴┬───┬┴───┴┬───┬──┴──────┴──────┴┬──┬─────
1686 * │EML│ │CTS│ │ACK│ │BA│
1687 * │OM │ └───┘ └───┘ ├──┤
1688 * └───┘ │BA│
1689 * └──┘
1690 *
1691 * [link 2] | power save mode
1692 * ────────────────────────────────────────────────────────────────────────────
1693 *
1694 */
1695
1696 // for each EMLSR client, there should be a frame exchange with ICF and no data frame
1697 // (ICF protects the EML Notification response) if the EML Notification response is sent
1698 // while EMLSR mode is still enabled and two frame exchanges with data frames
1699 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1700 {
1701 // the default EMLSR Manager requests to send EML Notification frames on the link where
1702 // the main PHY is operating; if EMLSR mode is still enabled on this link when the AP MLD
1703 // sends the EML Notification response, the latter is protected by an ICF
1704 auto exchangeIt = frameExchanges.at(i).cbegin();
1705
1706 auto linkIdOpt = m_staMacs[i]->GetLinkForPhy(m_mainPhyId);
1707 NS_TEST_ASSERT_MSG_EQ(linkIdOpt.has_value(),
1708 true,
1709 "Didn't find a link on which the main PHY is operating");
1710
1711 if (IsTrigger(exchangeIt->front()->psduMap))
1712 {
1713 NS_TEST_EXPECT_MSG_EQ(+exchangeIt->front()->linkId,
1714 +linkIdOpt.value(),
1715 "ICF was not sent on the expected link");
1716 NS_TEST_EXPECT_MSG_EQ(exchangeIt->size(),
1717 1,
1718 "Expected no data frame in the first frame exchange sequence");
1719 frameExchanges.at(i).pop_front();
1720 }
1721
1722 NS_TEST_EXPECT_MSG_GT_OR_EQ(frameExchanges.at(i).size(),
1723 2,
1724 "Expected at least 2 frame exchange sequences "
1725 << "involving EMLSR client " << i);
1726
1727 auto firstExchangeIt = frameExchanges.at(i).cbegin();
1728 auto secondExchangeIt = std::next(firstExchangeIt);
1729
1730 const auto firstAmpduTxEnd =
1731 firstExchangeIt->back()->startTx +
1733 firstExchangeIt->back()->psduMap,
1734 firstExchangeIt->back()->txVector,
1735 m_staMacs[i]->GetWifiPhy(firstExchangeIt->back()->linkId)->GetPhyBand());
1736 const auto secondAmpduTxStart = secondExchangeIt->front()->startTx;
1737
1739 firstExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1740 true,
1741 "Expected a QoS data frame in the first frame exchange sequence");
1742 NS_TEST_EXPECT_MSG_EQ(firstExchangeIt->size(),
1743 1,
1744 "Expected one frame only in the first frame exchange sequence");
1745
1747 secondExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1748 true,
1749 "Expected a QoS data frame in the second frame exchange sequence");
1750 NS_TEST_EXPECT_MSG_EQ(secondExchangeIt->size(),
1751 1,
1752 "Expected one frame only in the second frame exchange sequence");
1753
1754 if (m_staMacs[i]->GetNLinks() == m_emlsrLinks.size())
1755 {
1756 // all links are EMLSR links: the two QoS data frames are sent one after another on
1757 // the link used for sending EML OMN
1759 +firstExchangeIt->front()->linkId,
1760 +linkIdOpt.value(),
1761 "First frame exchange expected to occur on link used to send EML OMN");
1762
1764 +secondExchangeIt->front()->linkId,
1765 +linkIdOpt.value(),
1766 "Second frame exchange expected to occur on link used to send EML OMN");
1767
1768 NS_TEST_EXPECT_MSG_LT(firstAmpduTxEnd,
1769 secondAmpduTxStart,
1770 "A-MPDUs are not sent one after another");
1771 }
1772 else
1773 {
1774 // the two QoS data frames are sent concurrently on distinct links
1775 NS_TEST_EXPECT_MSG_NE(+firstExchangeIt->front()->linkId,
1776 +secondExchangeIt->front()->linkId,
1777 "Frame exchanges expected to occur on distinct links");
1778
1779 NS_TEST_EXPECT_MSG_GT(firstAmpduTxEnd,
1780 secondAmpduTxStart,
1781 "A-MPDUs are not sent concurrently");
1782 }
1783 }
1784}
1785
1786void
1788{
1789 std::optional<std::size_t> staId;
1790 for (std::size_t id = 0; id < m_nEmlsrStations + m_nNonEmlsrStations; id++)
1791 {
1792 if (m_staMacs.at(id)->GetLinkIdByAddress(address))
1793 {
1794 staId = id;
1795 break;
1796 }
1797 }
1798 NS_TEST_ASSERT_MSG_EQ(staId.has_value(), true, "Not an address of a non-AP MLD " << address);
1799
1800 // check that all EMLSR links (but the link used for ML setup) of the EMLSR clients
1801 // are considered to be in power save mode by the AP MLD; all the other links have
1802 // transitioned to active mode instead
1803 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
1804 {
1805 bool psModeExpected =
1806 *staId < m_nEmlsrStations && linkId != m_mainPhyId && m_emlsrLinks.count(linkId) == 1;
1807 auto addr = m_staMacs.at(*staId)->GetAddress();
1808 auto psMode = m_apMac->GetWifiRemoteStationManager(linkId)->IsInPsMode(addr);
1809 NS_TEST_EXPECT_MSG_EQ(psMode,
1810 psModeExpected,
1811 "EMLSR link " << +linkId << " of EMLSR client " << *staId
1812 << " not in " << (psModeExpected ? "PS" : "active")
1813 << " mode");
1814 // check that AP is blocking transmission of QoS data frames on this link
1816 addr,
1817 linkId,
1818 WifiQueueBlockedReason::POWER_SAVE_MODE,
1819 psModeExpected,
1820 "Checking PM mode after association on AP MLD for EMLSR client " +
1821 std::to_string(*staId),
1822 false);
1823 }
1824}
1825
1826void
1828 const WifiTxVector& txVector,
1829 uint8_t linkId)
1830{
1831 // the AP is replying to a received EMLSR Notification frame
1832 auto pkt = mpdu->GetPacket()->Copy();
1833 const auto& hdr = mpdu->GetHeader();
1835 MgtEmlOmn frame;
1836 pkt->RemoveHeader(frame);
1837
1838 std::optional<std::size_t> staId;
1839 for (std::size_t id = 0; id < m_nEmlsrStations; id++)
1840 {
1841 if (m_staMacs.at(id)->GetFrameExchangeManager(linkId)->GetAddress() == hdr.GetAddr1())
1842 {
1843 staId = id;
1844 break;
1845 }
1846 }
1847 NS_TEST_ASSERT_MSG_EQ(staId.has_value(),
1848 true,
1849 "Not an address of an EMLSR client " << hdr.GetAddr1());
1850
1851 // The EMLSR mode change occurs a Transition Timeout after the end of the PPDU carrying the Ack
1852 auto phy = m_apMac->GetWifiPhy(linkId);
1853 auto txDuration =
1854 WifiPhy::CalculateTxDuration(mpdu->GetSize() + 4, // A-MPDU Subframe header size
1855 txVector,
1856 phy->GetPhyBand());
1857 WifiTxVector ackTxVector =
1858 m_staMacs.at(*staId)->GetWifiRemoteStationManager(linkId)->GetAckTxVector(hdr.GetAddr2(),
1859 txVector);
1860 auto ackDuration = WifiPhy::CalculateTxDuration(GetAckSize() + 4, // A-MPDU Subframe header
1861 ackTxVector,
1862 phy->GetPhyBand());
1863
1864 Simulator::Schedule(txDuration + phy->GetSifs() + ackDuration, [=, this]() {
1865 if (frame.m_emlControl.emlsrMode == 1)
1866 {
1867 // EMLSR mode enabled. Check that all EMLSR links of the EMLSR clients are considered
1868 // to be in active mode by the AP MLD
1869 for (const auto linkId : m_emlsrLinks)
1870 {
1871 auto addr = m_staMacs.at(*staId)->GetAddress();
1872 auto psMode = m_apMac->GetWifiRemoteStationManager(linkId)->IsInPsMode(addr);
1873 NS_TEST_EXPECT_MSG_EQ(psMode,
1874 false,
1875 "EMLSR link " << +linkId << " of EMLSR client " << *staId
1876 << " not in active mode");
1877 // check that AP is not blocking transmission of QoS data frames on this link
1878 CheckBlockedLink(
1879 m_apMac,
1880 addr,
1881 linkId,
1882 WifiQueueBlockedReason::POWER_SAVE_MODE,
1883 false,
1884 "Checking EMLSR links on AP MLD after EMLSR mode is enabled on EMLSR client " +
1885 std::to_string(*staId),
1886 false);
1887 }
1888 }
1889 else
1890 {
1891 // EMLSR mode disabled. Check that all EMLSR links (but the link used to send the
1892 // EML Notification frame) of the EMLSR clients are considered to be in power save
1893 // mode by the AP MLD; the other links are in active mode
1894 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1895 {
1896 bool psModeExpected = id != linkId && m_emlsrLinks.count(id) == 1;
1897 auto addr = m_staMacs.at(*staId)->GetAddress();
1898 auto psMode = m_apMac->GetWifiRemoteStationManager(id)->IsInPsMode(addr);
1899 NS_TEST_EXPECT_MSG_EQ(psMode,
1900 psModeExpected,
1901 "EMLSR link "
1902 << +id << " of EMLSR client " << *staId << " not in "
1903 << (psModeExpected ? "PS" : "active") << " mode");
1904 // check that AP is blocking transmission of QoS data frames on this link
1905 CheckBlockedLink(
1906 m_apMac,
1907 addr,
1908 id,
1909 WifiQueueBlockedReason::POWER_SAVE_MODE,
1910 psModeExpected,
1911 "Checking links on AP MLD after EMLSR mode is disabled on EMLSR client " +
1912 std::to_string(*staId),
1913 false);
1914 }
1915 }
1916 });
1917}
1918
1919void
1921 const WifiTxVector& txVector,
1922 uint8_t linkId)
1923{
1924 // an EMLSR client is sending an EMLSR Notification frame
1925 auto pkt = mpdu->GetPacket()->Copy();
1926 const auto& hdr = mpdu->GetHeader();
1928 MgtEmlOmn frame;
1929 pkt->RemoveHeader(frame);
1930
1931 std::optional<std::size_t> staId;
1932 for (std::size_t id = 0; id < m_nEmlsrStations; id++)
1933 {
1934 if (m_staMacs.at(id)->GetFrameExchangeManager(linkId)->GetAddress() == hdr.GetAddr2())
1935 {
1936 staId = id;
1937 break;
1938 }
1939 }
1940 NS_TEST_ASSERT_MSG_EQ(staId.has_value(),
1941 true,
1942 "Not an address of an EMLSR client " << hdr.GetAddr1());
1943
1944 auto phy = m_staMacs.at(*staId)->GetWifiPhy(linkId);
1945 auto txDuration = WifiPhy::CalculateTxDuration(mpdu->GetSize(), txVector, phy->GetPhyBand());
1946 auto ackTxVector =
1947 m_apMac->GetWifiRemoteStationManager(linkId)->GetAckTxVector(hdr.GetAddr2(), txVector);
1948 auto ackDuration = WifiPhy::CalculateTxDuration(GetAckSize(), ackTxVector, phy->GetPhyBand());
1949 auto cfEndDuration = WifiPhy::CalculateTxDuration(
1951 m_staMacs.at(*staId)->GetWifiRemoteStationManager(linkId)->GetRtsTxVector(
1953 txVector.GetChannelWidth()),
1954 phy->GetPhyBand());
1955
1956 if (frame.m_emlControl.emlsrMode != 0)
1957 {
1958 return;
1959 }
1960
1961 // EMLSR mode disabled
1962 auto timeToCfEnd = txDuration + phy->GetSifs() + ackDuration + phy->GetSifs() + cfEndDuration;
1963
1964 // before the end of the CF-End frame, this link only is not blocked on both the
1965 // EMLSR client and the AP MLD
1966 Simulator::Schedule(timeToCfEnd - MicroSeconds(1), [=, this]() {
1967 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1968 {
1969 CheckBlockedLink(m_staMacs.at(*staId),
1970 m_apMac->GetAddress(),
1971 id,
1972 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1973 id != linkId && m_staMacs.at(*staId)->IsEmlsrLink(id),
1974 "Checking links on EMLSR client " + std::to_string(*staId) +
1975 " before the end of CF-End frame");
1977 m_staMacs.at(*staId)->GetAddress(),
1978 id,
1979 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1980 id != linkId && m_staMacs.at(*staId)->IsEmlsrLink(id),
1981 "Checking links of EMLSR client " + std::to_string(*staId) +
1982 " on the AP MLD before the end of CF-End frame");
1983 }
1984 });
1985 // after the end of the CF-End frame, all links for the EMLSR client are blocked on the
1986 // AP MLD
1987 Simulator::Schedule(timeToCfEnd + MicroSeconds(1), [=, this]() {
1988 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1989 {
1990 if (m_staMacs.at(*staId)->IsEmlsrLink(id))
1991 {
1992 CheckBlockedLink(
1993 m_apMac,
1994 m_staMacs.at(*staId)->GetAddress(),
1995 id && m_staMacs.at(*staId)->IsEmlsrLink(id),
1996 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
1997 true,
1998 "Checking links of EMLSR client " + std::to_string(*staId) +
1999 " are all blocked on the AP MLD right after the end of CF-End");
2000 }
2001 }
2002 });
2003 // before the end of the transition delay, all links for the EMLSR client are still
2004 // blocked on the AP MLD
2005 Simulator::Schedule(timeToCfEnd + m_transitionDelay.at(*staId) - MicroSeconds(1), [=, this]() {
2006 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2007 {
2008 if (m_staMacs.at(*staId)->IsEmlsrLink(id))
2009 {
2010 CheckBlockedLink(m_apMac,
2011 m_staMacs.at(*staId)->GetAddress(),
2012 id,
2013 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2014 true,
2015 "Checking links of EMLSR client " + std::to_string(*staId) +
2016 " are all blocked on the AP MLD before the end of "
2017 "transition delay");
2018 }
2019 }
2020 });
2021 // immediately after the transition delay, all links for the EMLSR client are unblocked
2022 Simulator::Schedule(timeToCfEnd + m_transitionDelay.at(*staId) + MicroSeconds(1), [=, this]() {
2023 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2024 {
2025 if (m_staMacs.at(*staId)->IsEmlsrLink(id))
2026 {
2027 CheckBlockedLink(m_apMac,
2028 m_staMacs.at(*staId)->GetAddress(),
2029 id,
2030 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2031 false,
2032 "Checking links of EMLSR client " + std::to_string(*staId) +
2033 " are all unblocked on the AP MLD after the transition delay");
2034 }
2035 }
2036 });
2037}
2038
2039void
2041 const WifiTxVector& txVector,
2042 uint8_t linkId)
2043{
2044 CtrlTriggerHeader trigger;
2045 mpdu->GetPacket()->PeekHeader(trigger);
2046 if (!trigger.IsMuRts())
2047 {
2048 return;
2049 }
2050
2052 true,
2053 "Did not expect an ICF before enabling EMLSR mode");
2054
2057 "Unexpected preamble type for the Initial Control frame");
2058 auto rate = txVector.GetMode().GetDataRate(txVector);
2059 NS_TEST_EXPECT_MSG_EQ((rate == 6e6 || rate == 12e6 || rate == 24e6),
2060 true,
2061 "Unexpected rate for the Initial Control frame: " << rate);
2062
2063 bool found = false;
2064 Time maxPaddingDelay{};
2065
2066 for (const auto& userInfo : trigger)
2067 {
2068 auto addr = m_apMac->GetMldOrLinkAddressByAid(userInfo.GetAid12());
2069 NS_TEST_ASSERT_MSG_EQ(addr.has_value(),
2070 true,
2071 "AID " << userInfo.GetAid12() << " not found");
2072
2073 if (m_apMac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*addr))
2074 {
2075 found = true;
2076
2077 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
2078 {
2079 if (m_staMacs.at(i)->GetAddress() == *addr)
2080 {
2081 maxPaddingDelay = Max(maxPaddingDelay, m_paddingDelay.at(i));
2082 break;
2083 }
2084 }
2085
2086 // check that the AP has blocked transmission on all other EMLSR links
2087 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2088 {
2089 if (!m_apMac->GetWifiRemoteStationManager(id)->GetEmlsrEnabled(*addr))
2090 {
2091 continue;
2092 }
2093
2095 *addr,
2096 id,
2097 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2098 id != linkId,
2099 "Checking that AP blocked transmissions on all other EMLSR "
2100 "links after sending ICF to client with AID=" +
2101 std::to_string(userInfo.GetAid12()),
2102 false);
2103 }
2104 }
2105 }
2106
2107 NS_TEST_EXPECT_MSG_EQ(found, true, "Expected ICF to be addressed to at least an EMLSR client");
2108
2109 auto txDuration = WifiPhy::CalculateTxDuration(mpdu->GetSize(),
2110 txVector,
2111 m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2112
2113 if (maxPaddingDelay.IsStrictlyPositive())
2114 {
2115 // compare the TX duration of this Trigger Frame to that of the Trigger Frame with no
2116 // padding added
2117 trigger.SetPaddingSize(0);
2118 auto pkt = Create<Packet>();
2119 pkt->AddHeader(trigger);
2120 auto txDurationWithout =
2121 WifiPhy::CalculateTxDuration(Create<WifiPsdu>(pkt, mpdu->GetHeader()),
2122 txVector,
2123 m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2124
2125 NS_TEST_EXPECT_MSG_EQ(txDuration,
2126 txDurationWithout + maxPaddingDelay,
2127 "Unexpected TX duration of the MU-RTS TF with padding "
2128 << maxPaddingDelay.As(Time::US));
2129 }
2130
2131 // check that the EMLSR clients have blocked transmissions on other links, switched their main
2132 // PHY (if needed) and have put aux PHYs to sleep after receiving this ICF
2133 for (const auto& userInfo : trigger)
2134 {
2135 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
2136 {
2137 if (m_staMacs[i]->GetAssociationId() != userInfo.GetAid12())
2138 {
2139 continue;
2140 }
2141
2142 const auto mainPhyLinkId = m_staMacs[i]->GetLinkForPhy(m_mainPhyId);
2143
2144 Simulator::Schedule(txDuration + NanoSeconds(5), [=, this]() {
2145 for (uint8_t id = 0; id < m_staMacs[i]->GetNLinks(); id++)
2146 {
2147 // non-EMLSR links or links on which ICF is received are not blocked
2149 m_apMac->GetAddress(),
2150 id,
2151 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2152 id != linkId && m_staMacs[i]->IsEmlsrLink(id),
2153 "Checking EMLSR links on EMLSR client " + std::to_string(i) +
2154 " after receiving ICF");
2155 }
2156
2157 if (mainPhyLinkId != linkId)
2158 {
2159 CheckMainPhyTraceInfo(i, "DlTxopIcfReceivedByAuxPhy", mainPhyLinkId, linkId);
2160 }
2161
2163 });
2164
2165 break;
2166 }
2167 }
2168}
2169
2170void
2172 const WifiTxVector& txVector,
2173 uint8_t linkId)
2174{
2175 if (m_nEmlsrStations != 2 || m_apMac->GetNLinks() != m_emlsrLinks.size() ||
2177
2178 {
2179 // we are interested in frames sent to test transition delay
2180 return;
2181 }
2182
2183 std::size_t firstClientId = 0;
2184 std::size_t secondClientId = 1;
2185 auto addr = m_staMacs[secondClientId]->GetAddress();
2186 auto txDuration =
2187 WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2188
2190
2191 switch (m_countQoSframes)
2192 {
2193 case 1:
2194 // generate another small packet addressed to the first EMLSR client only
2195 m_apMac->GetDevice()->GetNode()->AddApplication(
2196 GetApplication(DOWNLINK, firstClientId, 1, 40));
2197 // both EMLSR clients are about to receive a QoS data frame
2198 for (std::size_t clientId : {firstClientId, secondClientId})
2199 {
2200 Simulator::Schedule(txDuration, [=, this]() {
2201 for (uint8_t id = 0; id < m_staMacs[clientId]->GetNLinks(); id++)
2202 {
2203 // link on which QoS data is received is not blocked
2204 CheckBlockedLink(m_staMacs[clientId],
2205 m_apMac->GetAddress(),
2206 id,
2207 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2208 id != linkId,
2209 "Checking EMLSR links on EMLSR client " +
2210 std::to_string(clientId) +
2211 " after receiving the first QoS data frame");
2212 }
2213 });
2214 }
2215 break;
2216 case 2:
2217 // generate another small packet addressed to the second EMLSR client
2218 m_apMac->GetDevice()->GetNode()->AddApplication(
2219 GetApplication(DOWNLINK, secondClientId, 1, 40));
2220
2221 // when the transmission of the second QoS data frame starts, both EMLSR clients are
2222 // still blocking all the links but the one used to receive the QoS data frame
2223 for (std::size_t clientId : {firstClientId, secondClientId})
2224 {
2225 for (uint8_t id = 0; id < m_staMacs[clientId]->GetNLinks(); id++)
2226 {
2227 // link on which QoS data is received is not blocked
2228 CheckBlockedLink(m_staMacs[clientId],
2229 m_apMac->GetAddress(),
2230 id,
2231 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2232 id != linkId,
2233 "Checking EMLSR links on EMLSR client " +
2234 std::to_string(clientId) +
2235 " when starting the reception of the second QoS frame");
2236 }
2237 }
2238
2239 // the EMLSR client that is not the recipient of the QoS frame being transmitted will
2240 // switch back to listening mode after a transition delay starting from the end of
2241 // the PPDU carrying this QoS data frame
2242
2243 // immediately before the end of the PPDU, this link only is not blocked for the EMLSR
2244 // client on the AP MLD
2245 Simulator::Schedule(txDuration - NanoSeconds(1), [=, this]() {
2246 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2247 {
2249 addr,
2250 id,
2251 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2252 id != linkId,
2253 "Checking that links of EMLSR client " +
2254 std::to_string(secondClientId) +
2255 " are blocked on the AP MLD before the end of the PPDU");
2256 }
2257 });
2258 // immediately before the end of the PPDU, all the links on the EMLSR client that is not
2259 // the recipient of the second QoS frame are unblocked (they are unblocked when the
2260 // PHY-RXSTART.indication is not received)
2261 Simulator::Schedule(txDuration - NanoSeconds(1), [=, this]() {
2262 for (uint8_t id = 0; id < m_staMacs[secondClientId]->GetNLinks(); id++)
2263 {
2264 CheckBlockedLink(m_staMacs[secondClientId],
2265 m_apMac->GetAddress(),
2266 id,
2267 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2268 false,
2269 "Checking that links of EMLSR client " +
2270 std::to_string(secondClientId) +
2271 " are unblocked before the end of the second QoS frame");
2272 }
2273 });
2274 // immediately after the end of the PPDU, all links are blocked for the EMLSR client
2275 Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
2276 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2277 {
2279 addr,
2280 id,
2281 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2282 true,
2283 "Checking links of EMLSR client " +
2284 std::to_string(secondClientId) +
2285 " are all blocked on the AP MLD after the end of the PPDU");
2286 }
2287 });
2288 // immediately before the transition delay, all links are still blocked for the EMLSR client
2290 txDuration + m_transitionDelay.at(secondClientId) - NanoSeconds(1),
2291 [=, this]() {
2292 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2293 {
2295 m_apMac,
2296 addr,
2297 id,
2298 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2299 true,
2300 "Checking links of EMLSR client " + std::to_string(secondClientId) +
2301 " are all blocked on the AP MLD before the transition delay",
2302 false);
2303 }
2304 });
2305
2306 // 100 us before the transition delay expires, generate another small packet addressed
2307 // to a non-EMLSR client. The AP will start a TXOP to transmit this frame, while the
2308 // frame addressed to the EMLSR client is still queued because the transition delay has
2309 // not yet elapsed. The transition delay will expire while the AP is transmitting the
2310 // frame to the non-EMLSR client, so that the AP continues the TXOP to transmit the frame
2311 // to the EMLSR client and we can check that the AP performs PIFS recovery after missing
2312 // the BlockAck from the EMLSR client
2313 Simulator::Schedule(txDuration + m_transitionDelay.at(secondClientId) - MicroSeconds(100),
2314 [=, this]() {
2315 m_apMac->GetDevice()->GetNode()->AddApplication(
2317 });
2318
2319 break;
2320 case 3:
2321 // this is the frame addressed to a non-EMLSR client, which is transmitted before the
2322 // frame addressed to the EMLSR client, because the links of the latter are still blocked
2323 // at the AP because the transition delay has not yet elapsed
2325 psduMap.cbegin()->second->GetAddr1(),
2326 m_staMacs[m_nEmlsrStations]->GetFrameExchangeManager(linkId)->GetAddress(),
2327 "QoS frame not addressed to a non-EMLSR client");
2328
2329 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2330 {
2332 addr,
2333 id,
2334 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2335 true,
2336 "Checking links of EMLSR client " + std::to_string(secondClientId) +
2337 " are all blocked on the AP MLD before the transition delay");
2338 }
2339 // Block transmissions to the EMLSR client on all the links but the one on which this
2340 // frame is sent, so that the AP will continue this TXOP to send the queued frame to the
2341 // EMLSR client once the transition delay elapses
2342 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2343 {
2344 if (id != linkId)
2345 {
2346 m_apMac->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED, addr, {id});
2347 }
2348 }
2349 break;
2350 case 4:
2351 // the AP is continuing the TXOP, no need to block transmissions anymore
2352 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2353 {
2354 m_apMac->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED, addr, {id});
2355 }
2356 // at the end of the fourth QoS frame, this link only is not blocked on the EMLSR
2357 // client receiving the frame
2358 Simulator::Schedule(txDuration, [=, this]() {
2359 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2360 {
2361 CheckBlockedLink(m_staMacs[secondClientId],
2362 m_apMac->GetAddress(),
2363 id,
2364 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2365 id != linkId,
2366 "Checking EMLSR links on EMLSR client " +
2367 std::to_string(secondClientId) +
2368 " after receiving the fourth QoS data frame");
2369 }
2370 });
2371 break;
2372 }
2373}
2374
2375void
2377 const WifiTxVector& txVector,
2378 uint8_t phyId)
2379{
2380 if (m_nEmlsrStations != 2 || m_apMac->GetNLinks() != m_emlsrLinks.size() ||
2382 {
2383 // we are interested in frames sent to test transition delay
2384 return;
2385 }
2386
2387 if (++m_countBlockAck == 4)
2388 {
2389 // fourth BlockAck is sent by a non-EMLSR client
2390 return;
2391 }
2392
2393 auto taddr = psduMap.cbegin()->second->GetAddr2();
2394 std::size_t clientId;
2395 if (m_staMacs[0]->GetLinkIdByAddress(taddr))
2396 {
2397 clientId = 0;
2398 }
2399 else
2400 {
2401 NS_TEST_ASSERT_MSG_EQ(m_staMacs[1]->GetLinkIdByAddress(taddr).has_value(),
2402 true,
2403 "Unexpected TA for BlockAck: " << taddr);
2404 clientId = 1;
2405 }
2406
2407 // find the link on which the main PHY is operating
2408 auto currMainPhyLinkId = m_staMacs[clientId]->GetLinkForPhy(phyId);
2410 currMainPhyLinkId.has_value(),
2411 true,
2412 "Didn't find the link on which the PHY sending the BlockAck is operating");
2413 auto linkId = *currMainPhyLinkId;
2414
2415 // we need the MLD address to check the status of the container queues
2416 auto addr = m_apMac->GetWifiRemoteStationManager(linkId)->GetMldAddress(taddr);
2417 NS_TEST_ASSERT_MSG_EQ(addr.has_value(), true, "MLD address not found for " << taddr);
2418
2419 auto apPhy = m_apMac->GetWifiPhy(linkId);
2420 auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, apPhy->GetPhyBand());
2421 auto cfEndTxDuration = WifiPhy::CalculateTxDuration(
2423 m_apMac->GetWifiRemoteStationManager(linkId)->GetRtsTxVector(Mac48Address::GetBroadcast(),
2424 txVector.GetChannelWidth()),
2425 apPhy->GetPhyBand());
2426
2427 switch (m_countBlockAck)
2428 {
2429 case 5:
2430 // the PPDU carrying this BlockAck is corrupted, hence the AP MLD MAC receives the
2431 // PHY-RXSTART indication but it does not receive any frame from the PHY. Therefore,
2432 // at the end of the PPDU transmission, the AP MLD realizes that the EMLSR client has
2433 // not responded and makes an attempt at continuing the TXOP
2434
2435 // at the end of the PPDU, this link only is not blocked on both the EMLSR client and
2436 // the AP MLD
2437 Simulator::Schedule(txDuration, [=, this]() {
2438 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2439 {
2440 CheckBlockedLink(m_staMacs[clientId],
2441 m_apMac->GetAddress(),
2442 id,
2443 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2444 id != linkId,
2445 "Checking links on EMLSR client " + std::to_string(clientId) +
2446 " at the end of fourth BlockAck");
2448 *addr,
2449 id,
2450 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2451 id != linkId,
2452 "Checking links of EMLSR client " + std::to_string(clientId) +
2453 " on the AP MLD at the end of fourth BlockAck");
2454 }
2455 });
2456 // a SIFS after the end of the PPDU, still this link only is not blocked on both the
2457 // EMLSR client and the AP MLD
2458 Simulator::Schedule(txDuration + apPhy->GetSifs(), [=, this]() {
2459 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2460 {
2461 CheckBlockedLink(m_staMacs[clientId],
2462 m_apMac->GetAddress(),
2463 id,
2464 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2465 id != linkId,
2466 "Checking links on EMLSR client " + std::to_string(clientId) +
2467 " a SIFS after the end of fourth BlockAck");
2468 CheckBlockedLink(m_apMac,
2469 *addr,
2470 id,
2471 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2472 id != linkId,
2473 "Checking links of EMLSR client " + std::to_string(clientId) +
2474 " a SIFS after the end of fourth BlockAck");
2475 }
2476 });
2477 // corrupt this BlockAck so that the AP MLD sends a BlockAckReq later on
2478 {
2479 auto uid = psduMap.cbegin()->second->GetPacket()->GetUid();
2480 m_errorModel->SetList({uid});
2481 }
2482 break;
2483 case 6:
2484 // at the end of the PPDU, this link only is not blocked on both the EMLSR client and
2485 // the AP MLD
2486 Simulator::Schedule(txDuration, [=, this]() {
2487 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2488 {
2489 CheckBlockedLink(m_staMacs[clientId],
2490 m_apMac->GetAddress(),
2491 id,
2492 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2493 id != linkId,
2494 "Checking links on EMLSR client " + std::to_string(clientId) +
2495 " at the end of fifth BlockAck");
2497 *addr,
2498 id,
2499 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2500 id != linkId,
2501 "Checking links of EMLSR client " + std::to_string(clientId) +
2502 " on the AP MLD at the end of fifth BlockAck");
2503 }
2504 });
2505 // before the end of the CF-End frame, still this link only is not blocked on both the
2506 // EMLSR client and the AP MLD
2508 txDuration + apPhy->GetSifs() + cfEndTxDuration - MicroSeconds(1),
2509 [=, this]() {
2510 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2511 {
2512 CheckBlockedLink(m_staMacs[clientId],
2513 m_apMac->GetAddress(),
2514 id,
2515 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2516 id != linkId,
2517 "Checking links on EMLSR client " + std::to_string(clientId) +
2518 " before the end of CF-End frame");
2520 *addr,
2521 id,
2522 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2523 id != linkId,
2524 "Checking links of EMLSR client " + std::to_string(clientId) +
2525 " on the AP MLD before the end of CF-End frame");
2526 }
2527 });
2528 // after the end of the CF-End frame, all links for the EMLSR client are blocked on the
2529 // AP MLD
2531 txDuration + apPhy->GetSifs() + cfEndTxDuration + MicroSeconds(1),
2532 [=, this]() {
2533 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2534 {
2536 m_apMac,
2537 *addr,
2538 id,
2539 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2540 true,
2541 "Checking links of EMLSR client " + std::to_string(clientId) +
2542 " are all blocked on the AP MLD right after the end of CF-End");
2543 }
2544 });
2545 // before the end of the transition delay, all links for the EMLSR client are still
2546 // blocked on the AP MLD
2548 txDuration + apPhy->GetSifs() + cfEndTxDuration + m_transitionDelay.at(clientId) -
2549 MicroSeconds(1),
2550 [=, this]() {
2551 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2552 {
2554 m_apMac,
2555 *addr,
2556 id,
2557 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2558 true,
2559 "Checking links of EMLSR client " + std::to_string(clientId) +
2560 " are all blocked on the AP MLD before the end of transition delay");
2561 }
2562 });
2563 // immediately after the transition delay, all links for the EMLSR client are unblocked
2565 txDuration + apPhy->GetSifs() + cfEndTxDuration + m_transitionDelay.at(clientId) +
2566 MicroSeconds(1),
2567 [=, this]() {
2568 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2569 {
2571 m_apMac,
2572 *addr,
2573 id,
2574 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2575 false,
2576 "Checking links of EMLSR client " + std::to_string(clientId) +
2577 " are all unblocked on the AP MLD after the transition delay");
2578 }
2579 });
2580 break;
2581 }
2582}
2583
2584void
2594
2596 : EmlsrOperationsTestBase("Check EML UL TXOP transmissions (genBackoffAndUseAuxPhyCca=" +
2597 std::to_string(params.genBackoffAndUseAuxPhyCca) +
2598 ", nSlotsLeftAlert=" + std::to_string(params.nSlotsLeftAlert)),
2599 m_emlsrLinks(params.linksToEnableEmlsrOn),
2600 m_channelWidth(params.channelWidth),
2601 m_auxPhyChannelWidth(params.auxPhyChannelWidth),
2602 m_mediumSyncDuration(params.mediumSyncDuration),
2603 m_msdMaxNTxops(params.msdMaxNTxops),
2604 m_emlsrEnabledTime(0),
2605 m_firstUlPktsGenTime(0),
2606 m_unblockMainPhyLinkDelay(MilliSeconds(20)),
2607 m_checkBackoffStarted(false),
2608 m_countQoSframes(0),
2609 m_countBlockAck(0),
2610 m_countRtsframes(0),
2611 m_genBackoffIfTxopWithoutTx(params.genBackoffAndUseAuxPhyCca),
2612 m_useAuxPhyCca(params.genBackoffAndUseAuxPhyCca),
2613 m_nSlotsLeftAlert(params.nSlotsLeftAlert),
2614 m_switchMainPhyBackDelayTimeout(params.switchMainPhyBackDelayTimeout),
2615 m_5thQosFrameExpWidth(0)
2616{
2617 m_nEmlsrStations = 1;
2619 m_linksToEnableEmlsrOn = params.linksToEnableEmlsrOn;
2620 m_mainPhyId = 1;
2621
2622 // when aux PHYs do not switch link, the main PHY switches back to its previous link after
2623 // a TXOP, hence the transition delay must exceed the channel switch delay (default: 250us)
2625 m_establishBaDl = true;
2626 m_establishBaUl = true;
2627 m_putAuxPhyToSleep = params.putAuxPhyToSleep;
2628 m_duration = Seconds(1);
2629
2630 NS_ABORT_MSG_IF(params.linksToEnableEmlsrOn.size() < 2,
2631 "This test requires at least two links to be configured as EMLSR links");
2632 for (uint8_t id = 0; id < 3; id++)
2633 {
2634 if (!m_emlsrLinks.contains(id))
2635 {
2636 // non-EMLSR link found
2637 m_nonEmlsrLink = id;
2638 break;
2639 }
2640 }
2641}
2642
2643void
2645{
2646 Config::SetDefault("ns3::EmlsrManager::AuxPhyChannelWidth",
2648 Config::SetDefault("ns3::DefaultEmlsrManager::SwitchAuxPhy", BooleanValue(false));
2649 Config::SetDefault("ns3::AdvancedEmlsrManager::UseAuxPhyCca", BooleanValue(m_useAuxPhyCca));
2650 Config::SetDefault("ns3::AdvancedEmlsrManager::SwitchMainPhyBackDelay",
2652 Config::SetDefault("ns3::EhtConfiguration::MediumSyncDuration",
2654 Config::SetDefault("ns3::EhtConfiguration::MsdMaxNTxops", UintegerValue(m_msdMaxNTxops));
2655 Config::SetDefault("ns3::ChannelAccessManager::GenerateBackoffIfTxopWithoutTx",
2657 Config::SetDefault("ns3::ChannelAccessManager::NSlotsLeft", UintegerValue(m_nSlotsLeftAlert));
2658 // Channel switch delay should be less than RTS TX time + SIFS + CTS TX time, otherwise
2659 // UL TXOPs cannot be initiated by aux PHYs
2660 Config::SetDefault("ns3::WifiPhy::ChannelSwitchDelay", TimeValue(MicroSeconds(75)));
2661 Config::SetDefault("ns3::WifiPhy::NotifyMacHdrRxEnd", BooleanValue(true));
2662
2664
2665 m_staMacs[0]->GetQosTxop(AC_BE)->TraceConnectWithoutContext(
2666 "BackoffTrace",
2668
2669 uint8_t linkId = 0;
2670 // configure channels of the given width
2672 {
2673 MHz_u bw{20};
2674 uint8_t number = band == WIFI_PHY_BAND_5GHZ ? 36 : 1;
2675
2676 auto width =
2677 std::min(m_channelWidth, band == WIFI_PHY_BAND_2_4GHZ ? MHz_u{40} : MHz_u{160});
2678 while (bw < width)
2679 {
2680 bw *= 2;
2681 number += Count20MHzSubchannels(bw);
2682 }
2683
2684 for (auto mac : std::initializer_list<Ptr<WifiMac>>{m_apMac, m_staMacs[0]})
2685 {
2686 mac->GetWifiPhy(linkId)->SetOperatingChannel(
2687 WifiPhy::ChannelTuple{number, width, band, 0});
2688 }
2689 linkId++;
2690 }
2691
2692 // install post reception error model on the AP affiliated with the AP MLD and operating on
2693 // the same link as the main PHY of the EMLSR client
2695 m_apMac->GetWifiPhy(m_mainPhyId)->SetPostReceptionErrorModel(m_errorModel);
2696}
2697
2698void
2700{
2701 NS_LOG_INFO("Backoff value " << backoff << " generated by EMLSR client on link " << +linkId
2702 << "\n");
2703 if (linkId != m_mainPhyId)
2704 {
2705 return; // we are only interested in backoff on main PHY link
2706 }
2707
2708 if (m_backoffEndTime)
2709 {
2711 {
2712 // another backoff value while checkBackoffStarted is true is generated only if
2713 // GenerateBackoffIfTxopWithoutTx is true
2716 true,
2717 "Another backoff value should not be generated while the main PHY link is blocked");
2718
2721 "Backoff generated at unexpected time");
2722 }
2723 else
2724 {
2725 // we are done checking the backoff
2726 m_backoffEndTime.reset();
2727 }
2728 }
2729
2731 {
2732 if (!m_backoffEndTime.has_value())
2733 {
2734 // this is the first time we set m_backoffEndTime, which is done right after receiving
2735 // a BlockAck, thus we have to wait an AIFS before invoking backoff
2737 m_staMacs[0]->GetChannelAccessManager(linkId)->GetSifs() +
2738 m_staMacs[0]->GetQosTxop(AC_BE)->GetAifsn(linkId) *
2739 m_staMacs[0]->GetChannelAccessManager(linkId)->GetSlot();
2740 }
2741 else
2742 {
2743 // we get here when the backoff expired but no transmission occurred, thus we have
2744 // generated a new backoff value and we will start decrementing the counter in a slot
2746 Simulator::Now() + m_staMacs[0]->GetChannelAccessManager(linkId)->GetSlot();
2747 }
2748 // add the time corresponding to the generated number of slots
2749 m_backoffEndTime.value() +=
2750 backoff * m_staMacs[0]->GetChannelAccessManager(linkId)->GetSlot();
2751 NS_LOG_DEBUG("Expected backoff end time = " << m_backoffEndTime->As(Time::US) << "\n");
2752 }
2753}
2754
2755void
2757 uint8_t phyId,
2758 WifiConstPsduMap psduMap,
2759 WifiTxVector txVector,
2760 double txPowerW)
2761{
2762 EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
2763 auto linkId = m_txPsdus.back().linkId;
2764
2765 auto psdu = psduMap.begin()->second;
2766 auto nodeId = mac->GetDevice()->GetNode()->GetId();
2767
2768 switch (psdu->GetHeader(0).GetType())
2769 {
2771 NS_ASSERT_MSG(nodeId > 0, "APs do not send AssocReq frames");
2772 NS_TEST_EXPECT_MSG_EQ(+linkId, +m_mainPhyId, "AssocReq not sent by the main PHY");
2773 break;
2774
2775 case WIFI_MAC_CTL_RTS:
2776 CheckRtsFrames(*psdu->begin(), txVector, linkId);
2777 break;
2778
2779 case WIFI_MAC_CTL_CTS:
2780 CheckCtsFrames(*psdu->begin(), txVector, linkId);
2781 break;
2782
2783 case WIFI_MAC_QOSDATA:
2784 CheckQosFrames(psduMap, txVector, linkId);
2785 break;
2786
2788 CheckBlockAck(psduMap, txVector, linkId);
2789 break;
2790
2791 default:;
2792 }
2793}
2794
2795void
2797{
2798 // initially, we prevent transmissions on aux PHY links
2799 auto auxPhyLinks = m_staMacs[0]->GetSetupLinkIds();
2800 auxPhyLinks.erase(m_mainPhyId);
2801 if (m_nonEmlsrLink)
2802 {
2803 auxPhyLinks.erase(*m_nonEmlsrLink);
2804 }
2805 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2806 m_apMac->GetAddress(),
2807 auxPhyLinks);
2808
2809 // Association, Block Ack agreement establishment and enabling EMLSR mode have been done.
2810 // After 50ms, schedule:
2811 // - block of transmissions on the link where the main PHY is operating and on the non-EMLSR
2812 // link (if any)
2813 // - the generation of two UL packets
2814 // - after m_unblockMainPhyLinkDelay, unblock transmissions on the link where the main PHY
2815 // is operating, so that the first data frame is transmitted on that link
2817 std::set<uint8_t> linkIds;
2818 linkIds.insert(*m_staMacs[0]->GetLinkForPhy(m_mainPhyId));
2819 if (m_nonEmlsrLink)
2820 {
2821 linkIds.insert(*m_nonEmlsrLink);
2822 }
2823 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2824 m_apMac->GetAddress(),
2825 linkIds);
2826
2827 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2828 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000));
2830
2832 m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2833 m_apMac->GetAddress(),
2834 {*m_staMacs[0]->GetLinkForPhy(m_mainPhyId)});
2835 });
2836 });
2837}
2838
2839void
2841 const WifiTxVector& txVector,
2842 uint8_t linkId)
2843{
2845
2846 auto txDuration =
2847 WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2848
2849 switch (m_countQoSframes)
2850 {
2851 case 1:
2852 case 2:
2853 // do nothing, these are the QoS data frames sent to establish BA agreements in DL and UL
2854 // direction
2855 break;
2856 case 3:
2857 // first UL data frame (transmitted by the main PHY)
2858 if (m_nonEmlsrLink)
2859 {
2860 // generate data packets for another UL data frame, which will be sent on the
2861 // non-EMLSR link
2862 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2863 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(
2864 GetApplication(UPLINK, 0, 2, 1000));
2865
2866 // unblock transmissions on the non-EMLSR link once the two packets are queued
2867 Simulator::ScheduleNow([=, this]() {
2868 m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2869 m_apMac->GetAddress(),
2870 {*m_nonEmlsrLink});
2871 });
2872 }
2873
2874 // check that other EMLSR links are now blocked on the EMLSR client and on the AP MLD
2875 // after this QoS data frame is received
2876 Simulator::ScheduleNow([=, this]() {
2877 auto phyHdrTxTime = WifiPhy::CalculatePhyPreambleAndHeaderDuration(txVector);
2878 auto macHdrSize = (*psduMap.at(SU_STA_ID)->begin())->GetHeader().GetSerializedSize() +
2879 4 /* A-MPDU subframe header size */;
2880 auto macHdrTxTime =
2881 DataRate(txVector.GetMode().GetDataRate(txVector)).CalculateBytesTxTime(macHdrSize);
2882
2883 for (auto id : m_staMacs[0]->GetLinkIds())
2884 {
2886 m_staMacs[0],
2887 m_apMac->GetAddress(),
2888 id,
2889 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2890 id != m_staMacs[0]->GetLinkForPhy(m_mainPhyId) && m_staMacs[0]->IsEmlsrLink(id),
2891 "Checking EMLSR links on EMLSR client while sending the first data frame",
2892 false);
2893
2894 Simulator::Schedule(phyHdrTxTime + macHdrTxTime + MicroSeconds(1), [=, this]() {
2896 m_staMacs[0]->GetAddress(),
2897 id,
2898 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2899 id != m_staMacs[0]->GetLinkForPhy(m_mainPhyId) &&
2900 m_staMacs[0]->IsEmlsrLink(id),
2901 "Checking EMLSR links on AP MLD right after receiving the MAC "
2902 "header of the first data frame");
2903 });
2904
2907 [=, this]() {
2909 m_apMac,
2910 m_staMacs[0]->GetAddress(),
2911 id,
2912 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2913 id != m_staMacs[0]->GetLinkForPhy(m_mainPhyId) &&
2914 m_staMacs[0]->IsEmlsrLink(id),
2915 "Checking EMLSR links on AP MLD after sending the first data frame");
2916 });
2917 }
2918 });
2919
2920 if (m_nonEmlsrLink)
2921 {
2922 break;
2923 }
2924 m_countQoSframes++; // if all EMLSR links, next case is already executed now
2925 [[fallthrough]];
2926 case 4:
2927 // check that other EMLSR links are now blocked on the EMLSR client and on the AP MLD
2928 // after this QoS data frame is received
2929 Simulator::ScheduleNow([=, this]() {
2930 // make aux PHYs capable of transmitting frames
2931 auto auxPhyLinks = m_staMacs[0]->GetSetupLinkIds();
2932 auxPhyLinks.erase(m_mainPhyId);
2933 if (m_nonEmlsrLink)
2934 {
2935 auxPhyLinks.erase(*m_nonEmlsrLink);
2936 }
2937 m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2938 m_apMac->GetAddress(),
2939 auxPhyLinks);
2940
2941 // block transmissions on the link where the main PHY is operating
2942 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2943 m_apMac->GetAddress(),
2944 {*m_staMacs[0]->GetLinkForPhy(m_mainPhyId)});
2945
2946 // generate data packets for another UL data frame, which will be sent on a link on
2947 // which an aux PHY is operating
2948 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2949 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(
2950 GetApplication(UPLINK, 0, 2, 1000));
2951 });
2952 break;
2953 case 5:
2954 // check that other EMLSR links are now blocked on both the EMLSR client and the AP MLD
2955 Simulator::ScheduleNow([=, this]() {
2956 for (auto id : m_staMacs[0]->GetLinkIds())
2957 {
2959 m_staMacs[0],
2960 m_apMac->GetAddress(),
2961 id,
2962 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2963 id != linkId && m_staMacs[0]->IsEmlsrLink(id),
2964 "Checking EMLSR links on EMLSR client while sending the second data frame",
2965 false);
2966
2968 m_apMac,
2969 m_staMacs[0]->GetAddress(),
2970 id,
2971 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2972 id != linkId && m_staMacs[0]->IsEmlsrLink(id),
2973 "Checking EMLSR links on AP MLD while sending the second data frame",
2974 false);
2975 }
2976
2977 // unblock transmission on the link where the main PHY is operating
2978 m_staMacs[0]->GetMacQueueScheduler()->UnblockQueues(
2979 WifiQueueBlockedReason::TID_NOT_MAPPED,
2980 AC_BE,
2982 m_apMac->GetAddress(),
2983 m_staMacs[0]->GetAddress(),
2984 {0},
2985 {m_mainPhyId});
2986 });
2987 break;
2988 }
2989}
2990
2991void
2993 const WifiTxVector& txVector,
2994 uint8_t linkId)
2995{
2997
2998 auto auxPhyLinks = m_staMacs[0]->GetSetupLinkIds();
2999 auxPhyLinks.erase(m_mainPhyId);
3000 if (m_nonEmlsrLink)
3001 {
3002 auxPhyLinks.erase(*m_nonEmlsrLink);
3003 }
3004
3005 auto txDuration =
3006 WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
3007
3008 // in this test, BlockAck frames terminates TXOP, thus aux PHYs shall be in sleep mode before
3009 // the end of BlockAck reception and awake right afterwards
3010 if (linkId != m_nonEmlsrLink)
3011 {
3012 Simulator::Schedule(txDuration - TimeStep(1),
3014 this,
3015 m_staMacs[0],
3016 true);
3017 Simulator::Schedule(txDuration + TimeStep(1),
3019 this,
3020 m_staMacs[0],
3021 false);
3022
3023 // if the TXOP has been carried out on a link other than the preferred link, the main PHY
3024 // switches back to the preferred link when the TXOP ends
3025 if (m_staMacs[0]->GetLinkForPhy(m_mainPhyId) != linkId)
3026 {
3027 Simulator::Schedule(txDuration + TimeStep(1), [=, this]() {
3028 // check the traced remaining time before calling CheckMainPhyTraceInfo
3029 if (const auto traceInfoIt = m_traceInfo.find(0);
3030 traceInfoIt != m_traceInfo.cend() &&
3031 traceInfoIt->second->GetName() == "TxopEnded")
3032 {
3033 const auto& traceInfo =
3034 static_cast<const EmlsrTxopEndedTrace&>(*traceInfoIt->second);
3036 traceInfo.remTime,
3037 Time{0},
3038 "Expected null remaining time because TXOP ended regularly");
3039 }
3040
3041 CheckMainPhyTraceInfo(0, "TxopEnded", linkId, m_mainPhyId);
3042 });
3043 }
3044 }
3045
3046 switch (m_countBlockAck)
3047 {
3048 case 1:
3049 case 2:
3050 // do nothing, these are BlockAcks in response to the QoS data frames sent to establish
3051 // BA agreements in DL and UL direction
3052 break;
3053 case 3:
3054 if (linkId == m_nonEmlsrLink)
3055 {
3056 // this BlockAck has been sent on the non-EMLSR link, ignore it
3057 break;
3058 }
3059 m_checkBackoffStarted = true;
3060 if (!m_nonEmlsrLink)
3061 {
3062 m_countBlockAck++; // if all EMLSR links, next case is already executed now
3063 }
3064 [[fallthrough]];
3065 case 4:
3066 if (m_nonEmlsrLink && m_countBlockAck == 4)
3067 {
3068 // block transmissions on the non-EMLSR link
3069 Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
3070 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
3071 m_apMac->GetAddress(),
3072 {*m_nonEmlsrLink});
3073 });
3074 }
3075 if (linkId == m_nonEmlsrLink)
3076 {
3077 // this BlockAck has been sent on the non-EMLSR link, ignore it
3078 break;
3079 }
3080 m_checkBackoffStarted = true;
3081 break;
3082 case 5:
3083 // Block Ack in response to the second data frame sent by the EMLSR client on EMLSR links.
3084 // Check that MediumSyncDelay timer starts running on the link where the main PHY switches
3085 // to when the channel switch is completed
3087 txDuration + m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId)->GetChannelSwitchDelay() +
3088 NanoSeconds(1),
3089 [=, this]() {
3090 auto elapsed =
3091 m_staMacs[0]->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_mainPhyId);
3093 elapsed.has_value(),
3094 true,
3095 "MediumSyncDelay timer not running on link where main PHY is operating");
3097 m_staMacs[0]->GetEmlsrManager()->GetMediumSyncDuration() -
3098 *elapsed;
3099 });
3100
3101 // Check that the number of backoff slots is not changed since the beginning of the TXOP
3102 Simulator::Schedule(txDuration, [=, this]() {
3103 m_checkBackoffStarted = false;
3105 true,
3106 "Backoff end time should have been calculated");
3107 // when this BlockAck is received, the TXOP ends and the main PHY link is unblocked,
3108 // which causes a new backoff timer to be generated if the backoff timer is not
3109 // already running
3111 });
3112
3113 // make aux PHYs not capable of transmitting frames
3114 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
3115 m_apMac->GetAddress(),
3116 auxPhyLinks);
3117
3118 // generate data packets for another UL data frame, which will be sent on the link where
3119 // the main PHY is operating
3120 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
3121 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000));
3122 break;
3123 case 6: {
3124 // block transmission on the main PHY link and on the non-EMLSR link (if any), so that
3125 // the next QoS frames are sent on a link where an aux PHY is operating
3126 std::set<uint8_t> linkIds{m_mainPhyId};
3127 if (m_nonEmlsrLink)
3128 {
3129 linkIds.insert(*m_nonEmlsrLink);
3130 }
3131 m_staMacs[0]->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3132 AC_BE,
3134 m_apMac->GetAddress(),
3135 m_staMacs[0]->GetAddress(),
3136 {0},
3137 linkIds);
3138 }
3139 // make sure aux PHYs are capable of transmitting frames
3140 m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
3141 m_apMac->GetAddress(),
3142 auxPhyLinks);
3143
3144 // generate data packets for another UL data frame
3145 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
3146 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000));
3147 break;
3148 case 7:
3149 // make the aux PHY(s) not capable of transmitting frames
3150 m_staMacs[0]->GetEmlsrManager()->SetAuxPhyTxCapable(false);
3151 if (!m_nonEmlsrLink)
3152 {
3153 // if there are two auxiliary links, set MediumSyncDuration to zero so that the
3154 // next UL QoS data frame is not protected also in case it is transmitted on the
3155 // auxiliary link other than the one on which the last frame exchange occurred
3156 m_staMacs[0]->GetEmlsrManager()->SetMediumSyncDuration(Seconds(0));
3157 }
3158
3159 // generate a very large backoff for the preferred link, so that when an aux PHY gains a
3160 // TXOP, it requests the main PHY to switch to its link to transmit the frames
3161 m_staMacs[0]->GetQosTxop(AC_BE)->StartBackoffNow(100, m_mainPhyId);
3162
3163 // events to be scheduled at the end of the BlockAck response
3164 Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
3165 // check that the main PHY switches to its preferred link
3166 auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId);
3167
3168 NS_TEST_EXPECT_MSG_EQ(mainPhy->IsStateSwitching(),
3169 true,
3170 "Main PHY is not switching at time "
3171 << Simulator::Now().As(Time::NS));
3172
3173 // events to be scheduled when the first main PHY channel switch is completed
3174 Simulator::Schedule(mainPhy->GetChannelSwitchDelay(), [=, this]() {
3175 // either the main PHY is operating on the preferred link or it is switching again
3176 auto mainPhyLinkid = m_staMacs[0]->GetLinkForPhy(mainPhy);
3177 if (mainPhyLinkid)
3178 {
3179 NS_TEST_EXPECT_MSG_EQ(+mainPhyLinkid.value(),
3180 +m_mainPhyId,
3181 "Main PHY expected to operate on the preferred link");
3182 }
3183 else
3184 {
3185 NS_TEST_EXPECT_MSG_EQ(
3186 mainPhy->IsStateSwitching(),
3187 true,
3188 "Main PHY is not operating on a link and it is not switching at time "
3189 << Simulator::Now().As(Time::NS));
3190 }
3191
3192 auto acBe = m_staMacs[0]->GetQosTxop(AC_BE);
3193
3194 // find the min remaining backoff time on auxiliary links for AC BE
3195 auto minBackoff = Time::Max();
3196 Time slot{0};
3197 for (uint8_t id = 0; id < m_staMacs[0]->GetNLinks(); id++)
3198 {
3199 if (!m_staMacs[0]->GetWifiPhy(id))
3200 {
3201 continue; // no PHY on this link
3202 }
3203
3204 if (auto backoff =
3205 m_staMacs[0]->GetChannelAccessManager(id)->GetBackoffEndFor(acBe);
3206 id != m_mainPhyId && m_staMacs[0]->IsEmlsrLink(id) && backoff < minBackoff)
3207 {
3208 minBackoff = backoff;
3209 slot = m_staMacs[0]->GetWifiPhy(id)->GetSlot();
3210 }
3211 }
3212
3213 // if the backoff on a link has expired before the end of the main PHY channel
3214 // switch, the main PHY will be requested to switch again no later than the first
3215 // slot boundary after the end of the channel switch. Otherwise, it will be
3216 // requested to switch when the backoff expires or when the backoff counter reaches
3217 // the configured number of slots
3218 auto expected2ndSwitchDelay =
3219 (minBackoff <= Simulator::Now()) ? mainPhy->GetSlot()
3220 : m_nSlotsLeftAlert > 0
3221 ? Max(minBackoff - m_nSlotsLeftAlert * slot - Simulator::Now(), Time{0})
3222 : (minBackoff - Simulator::Now());
3223
3224 // check that the main PHY is requested to switch to an auxiliary link after
3225 // the expected delay
3226 Simulator::Schedule(expected2ndSwitchDelay + NanoSeconds(1), [=, this]() {
3227 NS_TEST_EXPECT_MSG_EQ(mainPhy->IsStateSwitching(),
3228 true,
3229 "Main PHY is not switching at time "
3230 << Simulator::Now().As(Time::NS));
3231 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetLinkForPhy(mainPhy).has_value(),
3232 false,
3233 "Main PHY should not be operating on a link because it "
3234 "should be switching to an auxiliary link");
3235 // check that the appropriate trace info was received
3237 "UlTxopAuxPhyNotTxCapable",
3238 std::nullopt,
3239 0,
3240 false,
3241 false);
3242
3243 const auto delayUntilIdle = mainPhy->GetDelayUntilIdle();
3244 auto startTimerDelay = delayUntilIdle;
3245
3247 {
3248 TimeValue switchMainPhyBackDelay;
3249 m_staMacs[0]->GetEmlsrManager()->GetAttribute("SwitchMainPhyBackDelay",
3250 switchMainPhyBackDelay);
3251
3252 // If nSlotsAlert is 0, the decision whether to start the switch back timer
3253 // is taken at the end of the PIFS period during which we perform CCA and
3254 // NAV check, which coincides with the end of the channel switch or is a
3255 // PIFS afterwards, depending on whether aux PHY CCA is used. Therefore,
3256 // before the end of the CCA and NAV check period we have to make the medium
3257 // busy on the link the main PHY is switching to. Given that we do not know
3258 // which link it is, we set the NAV on all links.
3259 // If nSlotsAlert > 0, the decision whether to start the switch back timer
3260 // is taken at the end of the channel switch and it is needed that the time
3261 // until the backoff end is at least a PIFS to start the switch back timer.
3262 auto endCcaNavCheckDelay = delayUntilIdle;
3263
3264 for (uint8_t id = 0; id < m_staMacs[0]->GetNLinks(); ++id)
3265 {
3266 if (auto phy = m_staMacs[0]->GetWifiPhy(id))
3267 {
3268 if (!m_useAuxPhyCca && m_nSlotsLeftAlert == 0)
3269 {
3270 endCcaNavCheckDelay =
3271 Max(endCcaNavCheckDelay, delayUntilIdle + phy->GetPifs());
3272 }
3273
3274 m_staMacs[0]->GetChannelAccessManager(id)->NotifyNavStartNow(
3275 endCcaNavCheckDelay + TimeStep(1));
3276 }
3277 }
3278 startTimerDelay = endCcaNavCheckDelay;
3279
3280 // when the SwitchMainPhyBackDelay timer starts, extend the NAV on the
3281 // aux PHY link on which the main PHY is operating by the timer duration
3282 // plus a channel switch delay, so that the timer expires and the main PHY
3283 // returns to the preferred link. If nSlotsAlert > 0, the timer duration is
3284 // extended by the expected channel access when the main PHY switch ends.
3285 Simulator::Schedule(startTimerDelay, [=, this]() {
3286 auto auxLinkId = m_staMacs[0]->GetLinkForPhy(mainPhy);
3288 auxLinkId.has_value(),
3289 true,
3290 "Main PHY should be operating on a link before timer expires");
3291 auto timerDuration = switchMainPhyBackDelay.Get();
3292 if (m_nSlotsLeftAlert > 0)
3293 {
3294 timerDuration += (m_staMacs[0]
3295 ->GetChannelAccessManager(*auxLinkId)
3296 ->GetBackoffEndFor(acBe) -
3297 Simulator::Now());
3298 }
3299 m_staMacs[0]
3300 ->GetChannelAccessManager(*auxLinkId)
3301 ->NotifyNavStartNow(timerDuration +
3302 mainPhy->GetChannelSwitchDelay());
3303
3304 // check that the SwitchMainPhyBackDelay timer expires and the main PHY
3305 // returns to the preferred link
3306 Simulator::Schedule(timerDuration + TimeStep(1), [=, this]() {
3308 "TxopNotGainedOnAuxPhyLink",
3309 std::nullopt,
3311 false);
3312 });
3313 });
3314 }
3315
3316 // events to be scheduled when main PHY finishes switching to auxiliary link
3317 Simulator::Schedule(mainPhy->GetDelayUntilIdle(), [=, this]() {
3318 auto auxLinkId = m_staMacs[0]->GetLinkForPhy(mainPhy);
3319 NS_TEST_ASSERT_MSG_EQ(auxLinkId.has_value(),
3320 true,
3321 "Main PHY should have completed switching");
3322 // update backoff on the auxiliary link on which main PHY is operating
3323 auto cam = m_staMacs[0]->GetChannelAccessManager(*auxLinkId);
3324 cam->NeedBackoffUponAccess(acBe, true, true);
3325 const auto usedAuxPhyCca =
3326 (m_useAuxPhyCca || m_auxPhyChannelWidth >= m_channelWidth) &&
3327 (m_nSlotsLeftAlert == 0 ||
3328 cam->GetBackoffEndFor(acBe) <= Simulator::Now());
3329 m_5thQosFrameExpWidth =
3330 usedAuxPhyCca ? m_auxPhyChannelWidth : m_channelWidth;
3331 // record the time the transmission of the QoS data frames must have
3332 // started: (a PIFS after) end of channel switch, if the backoff counter
3333 // on the auxiliary link is null and UseAuxPhyCca is true (false); when
3334 // the backoff expires, otherwise
3335 if (auto slots = acBe->GetBackoffSlots(*auxLinkId); slots == 0)
3336 {
3337 m_5thQosFrameTxTime =
3338 Simulator::Now() + (m_useAuxPhyCca ? Time{0} : mainPhy->GetPifs());
3339 }
3340 else
3341 {
3342 m_5thQosFrameTxTime = cam->GetBackoffEndFor(acBe);
3343 }
3344 });
3345 });
3346 });
3347 });
3348
3349 // generate data packets for another UL data frame
3350 NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
3351 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000));
3352 break;
3353 }
3354}
3355
3356void
3358 const WifiTxVector& txVector,
3359 uint8_t linkId)
3360{
3362 {
3363 // this function only considers RTS frames sent after the first QoS data frame
3364 return;
3365 }
3366
3367 if (linkId != m_mainPhyId)
3368 {
3369 if (m_countRtsframes > 0 && !m_corruptCts.has_value())
3370 {
3371 // we get here for the frame exchange in which the CTS response must be corrupted.
3372 // Install post reception error model on the STA affiliated with the EMLSR client that
3373 // is transmitting this RTS frame
3375 m_staMacs[0]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_errorModel);
3376 m_corruptCts = true;
3377 }
3378
3379 return;
3380 }
3381
3382 // we get here for RTS frames sent by the main PHY while the MediumSyncDelay timer is running
3384
3386 m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId)->GetChannelWidth(),
3387 "RTS sent by main PHY on an unexpected width");
3388
3389 // corrupt reception at AP MLD
3390 NS_LOG_INFO("CORRUPTED");
3391 m_errorModel->SetList({mpdu->GetPacket()->GetUid()});
3392}
3393
3394void
3396 const WifiTxVector& txVector,
3397 uint8_t linkId)
3398{
3400 {
3401 // this function only considers CTS frames sent after the first QoS data frame
3402 return;
3403 }
3404
3405 auto txDuration = WifiPhy::CalculateTxDuration(mpdu->GetSize(),
3406 txVector,
3407 m_apMac->GetWifiPhy(linkId)->GetPhyBand());
3408 const auto doCorruptCts = m_corruptCts.has_value() && *m_corruptCts;
3409
3410 if (linkId != m_staMacs[0]->GetLinkForPhy(m_mainPhyId) && linkId != m_nonEmlsrLink &&
3411 mpdu->GetHeader().GetAddr1() == m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress())
3412 {
3413 // this is a CTS sent to an aux PHY starting an UL TXOP. Given that aux PHYs do not
3414 // switch channel, they are put in sleep mode when the main PHY starts operating on their
3415 // link, which coincides with the end of CTS plus two propagation delays
3416 const auto auxPhy = m_staMacs[0]->GetWifiPhy(linkId);
3417 const auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId);
3418 Simulator::Schedule(txDuration, [=, this]() {
3419 // when CTS ends, the main PHY is still switching and the aux PHY is not yet sleeping
3420 NS_TEST_EXPECT_MSG_EQ(mainPhy->IsStateSwitching(),
3421 true,
3422 "Expecting the main PHY to be switching link");
3423 NS_TEST_EXPECT_MSG_EQ(auxPhy->IsStateSleep(),
3424 false,
3425 "Aux PHY on link " << +linkId << " already in sleep mode");
3426 // when CTS is sent, the main PHY may have already started switching, thus we may not
3427 // know which link the main PHY is moving from
3428 CheckMainPhyTraceInfo(0, "UlTxopRtsSentByAuxPhy", std::nullopt, linkId, false);
3429 });
3431 txDuration + MicroSeconds(2 * MAX_PROPAGATION_DELAY_USEC) + TimeStep(1),
3432 [=, this]() {
3433 // aux PHYs are put to sleep if and only if CTS is not corrupted
3434 // (causing the end of the TXOP)
3435 CheckAuxPhysSleepMode(m_staMacs[0], !doCorruptCts);
3436 // if CTS is corrupted, TXOP ends and the main PHY switches back
3437 // to the preferred link
3438 if (doCorruptCts)
3439 {
3440 // check the traced remaining time before calling CheckMainPhyTraceInfo
3441 if (const auto traceInfoIt = m_traceInfo.find(0);
3442 traceInfoIt != m_traceInfo.cend() &&
3443 traceInfoIt->second->GetName() == "TxopEnded")
3444 {
3445 const auto& traceInfo =
3446 static_cast<const EmlsrTxopEndedTrace&>(*traceInfoIt->second);
3447 NS_TEST_EXPECT_MSG_GT(traceInfo.remTime,
3448 Time{0},
3449 "Expected non-zero remaining time because main PHY "
3450 "was switching when TXOP ended");
3451 }
3452
3453 CheckMainPhyTraceInfo(0, "TxopEnded", linkId, m_mainPhyId);
3454 }
3455 });
3456 }
3457
3458 if (doCorruptCts)
3459 {
3460 // corrupt reception at EMLSR client
3461 NS_LOG_INFO("CORRUPTED");
3462 m_errorModel->SetList({mpdu->GetPacket()->GetUid()});
3463 m_corruptCts = false;
3464 }
3465}
3466
3467void
3477
3478void
3480{
3481 if (m_msdMaxNTxops > 0)
3482 {
3486 "Unexpected number of RTS frames sent while the MediumSyncDelay timer is running");
3487 }
3488
3489 auto psduIt = m_txPsdus.cbegin();
3490
3491 // lambda to jump to the next QoS data frame or MU-RTS Trigger Frame or RTS transmitted
3492 // to/by an EMLSR client
3493 auto jumpToQosDataOrMuRts = [&]() {
3494 while (psduIt != m_txPsdus.cend() &&
3495 !psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData() &&
3496 !psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts())
3497 {
3498 auto psdu = psduIt->psduMap.cbegin()->second;
3499 if (psdu->GetHeader(0).IsTrigger())
3500 {
3501 CtrlTriggerHeader trigger;
3502 psdu->GetPayload(0)->PeekHeader(trigger);
3503 if (trigger.IsMuRts())
3504 {
3505 break;
3506 }
3507 }
3508 psduIt++;
3509 }
3510 };
3511
3512 /**
3513 * EMLSR client with EMLSR mode enabled on all links (main PHY ID = 1).
3514 *
3515 * main PHY│
3516 * blocked,│
3517 * aux PHYs││main PHY blocked│
3518 * cannot │
3519 * transmit│
3520 * │ ┌───┐ ┌──┐
3521 * [link 0] │CTS│ │BA│
3522 * ────────────────────────┬───┬┴───┴┬───┬───┬┴──┴─────────────────────────────────────────
3523 * │RTS│ │QoS│QoS│
3524 * └───┘ │ 6 │ 7 │
3525 * └───┴───┘
3526 * gen backoff gen backoff if MediumSyncDelay
3527 * ┌──┐ (also many times) not running timer expired ┌──┐
3528 * [link 1] │BA│ │ if allowed │ │ │BA│
3529 * ─────────┬───┬───┬┴──┴───────────────────────────┬───┬─────┬───┬────┬───┬───┬┴──┴───────
3530 * │QoS│QoS│ │RTS│ ... │RTS│ │QoS│QoS│
3531 * │ 4 │ 5 │ └───┘ └───┘ │ 8 │ 9 │
3532 * └───┴───┘ └───┴───┘
3533 *
3534 * [link 2]
3535 * ───────────────────────────────────────────────────────────────────────────
3536 *
3537 *
3538 *
3539 * EMLSR client with EMLSR mode enabled on links 0 and 1 (main PHY ID = 1).
3540 *
3541 * main PHY │
3542 * and │
3543 * non-EMLSR│
3544 * link │
3545 * blocked,│
3546 * aux PHYs││main PHY blocked│
3547 * cannot │
3548 * transmit│
3549 * │ ┌───┐ ┌──┐
3550 * [link 0] │CTS│ │BA│
3551 * ────────────────────────┬───┬┴───┴┬───┬───┬┴──┴─────────────────────────────────────────
3552 * │RTS│ │QoS│QoS│
3553 * └───┘ │ 8 │ 9 │
3554 * └───┴───┘
3555 * gen backoff gen backoff if MediumSyncDelay
3556 * ┌──┐ (also many times) not running timer expired ┌──┐
3557 * [link 1] │BA│ │ if allowed │ │ │BA│
3558 * ─────────┬───┬───┬┴──┴───────────────────────────┬───┬─────┬───┬────┬───┬───┬┴──┴───────
3559 * │QoS│QoS│ │RTS│ ... │RTS│ │QoS│QoS│
3560 * │ 4 │ 5 │ └───┘ └───┘ │ 10│ 11│
3561 * └───┴───┘ └───┴───┘
3562 * ┌──┐
3563 * [link 2] │BA│
3564 * ──────────┬───┬───┬┴──┴────────────────────────────────────────────────────────────
3565 * │QoS│QoS│
3566 * │ 6 │ 7 │
3567 * └───┴───┘
3568 *
3569 * For both scenarios, after the last frame exchange on the main PHY link, we have the
3570 * following frame exchanges on an EMLSR link where an aux PHY is operating on. After the
3571 * first frame exchange, aux PHYs are configured as non-TX capable. Note that the two frame
3572 * exchanges may occur on distinct auxiliary EMLSR links.
3573 *
3574 * | main PHY || main PHY |
3575 * [ link ] ┌───┐ ┌───┐ ┌──┐|switches to||switches to| ┌──┐
3576 * [0 or 2] │CTS│ │CTS│ │BA│| preferred ||auxiliary |PIFS| │BA│
3577 * ──────┬───┬┴───X────┬───┬┴───┴┬───┬───┬┴──┴──────────────────────────────┬───┬───┬┴──┴───
3578 * │RTS│ │RTS│ │QoS│QoS│ │QoS│QoS│
3579 * └───┘ └───┘ │ X │ Y │ │ Z │ W │
3580 * └───┴───┘ └───┴───┘
3581 * For all EMLSR links scenario, X=10, Y=11, Z=12, W=13
3582 * For the scenario with a non-EMLSR link, X=12, Y=13, Z=14, W=15
3583 */
3584
3585 // jump to the first (non-Beacon) frame transmitted after establishing BA agreements and
3586 // enabling EMLSR mode
3587 while (psduIt != m_txPsdus.cend() &&
3588 (psduIt->startTx < m_firstUlPktsGenTime ||
3589 psduIt->psduMap.cbegin()->second->GetHeader(0).IsBeacon()))
3590 {
3591 ++psduIt;
3592 }
3593
3594 // the first QoS data frame is transmitted by the main PHY without RTS protection as soon
3595 // as transmissions on the link where the main PHY is operating are unblocked (at this
3596 // moment, aux PHYs cannot transmit)
3597 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3598 true,
3599 "First QoS data frame has not been transmitted");
3600 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
3601 true,
3602 "First QoS data frame should be transmitted without protection");
3603 NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
3604 +m_mainPhyId,
3605 "First QoS data frame should be transmitted by the main PHY");
3606 NS_TEST_EXPECT_MSG_GT_OR_EQ(psduIt->startTx,
3608 "First QoS data frame sent too early");
3609
3610 auto prevPsduIt = psduIt++;
3611 jumpToQosDataOrMuRts();
3612
3613 if (m_nonEmlsrLink)
3614 {
3615 // an additional data frame is sent concurrently on the non-EMLSR link
3617 (psduIt != m_txPsdus.cend()),
3618 true,
3619 "Expected another QoS data frame sent concurrently with the first frame");
3621 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
3622 true,
3623 "First data frame on non-EMLSR link should be transmitted without protection");
3624 NS_TEST_EXPECT_MSG_EQ(+psduIt->linkId,
3625 +m_nonEmlsrLink.value(),
3626 "First data frame expected to be transmitted on the non-EMLSR link");
3627 const auto txDuration =
3628 WifiPhy::CalculateTxDuration(prevPsduIt->psduMap,
3629 prevPsduIt->txVector,
3630 m_staMacs[0]->GetWifiPhy(prevPsduIt->phyId)->GetPhyBand());
3631 NS_TEST_EXPECT_MSG_LT(psduIt->startTx,
3632 prevPsduIt->startTx + txDuration,
3633 "First data frame on the non-EMLSR link not sent concurrently");
3634 psduIt++;
3635 jumpToQosDataOrMuRts();
3636 }
3637
3638 // the second QoS data frame is transmitted by the main PHY after that the aux PHY has
3639 // obtained a TXOP and sent an RTS
3640 // RTS
3641 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3642 true,
3643 "RTS before second QoS data frame has not been transmitted");
3644 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts(),
3645 true,
3646 "Second QoS data frame should be transmitted with protection");
3648 +psduIt->phyId,
3649 +m_mainPhyId,
3650 "RTS before second QoS data frame should not be transmitted by the main PHY");
3651 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3653 "RTS before second data frame transmitted on an unexpected width");
3654 psduIt++;
3655 // CTS
3656 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3657 true,
3658 "CTS before second QoS data frame has not been transmitted");
3659 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsCts(),
3660 true,
3661 "CTS before second QoS data frame has not been transmitted");
3662 psduIt++;
3663 // QoS Data
3664 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3665 true,
3666 "Second QoS data frame has not been transmitted");
3667 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
3668 true,
3669 "Second QoS data frame has not been transmitted");
3670 NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
3671 +m_mainPhyId,
3672 "Second QoS data frame should be transmitted by the main PHY");
3673 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3675 "Second data frame not transmitted on the same width as RTS");
3676
3677 bool moreQosDataFound = false;
3678
3679 while (++psduIt != m_txPsdus.cend())
3680 {
3681 jumpToQosDataOrMuRts();
3682 if (psduIt != m_txPsdus.cend() &&
3683 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData())
3684 {
3685 moreQosDataFound = true;
3686
3687 NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
3688 +m_mainPhyId,
3689 "Third QoS data frame should be transmitted by the main PHY");
3690 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3691 m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId)->GetChannelWidth(),
3692 "Expecting TX width of third data frame to equal the channel "
3693 "width used by the main PHY");
3695 psduIt->startTx,
3697 "Third QoS data frame sent before MediumSyncDelay timer expired");
3698
3699 break;
3700 }
3701 }
3702
3703 NS_TEST_EXPECT_MSG_EQ(moreQosDataFound,
3704 true,
3705 "Third QoS data frame transmitted by the main PHY not found");
3706
3707 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()), true, "Expected more frames");
3708 ++psduIt;
3709 jumpToQosDataOrMuRts();
3710
3711 // the first attempt at transmitting the fourth QoS data frame fails because CTS is corrupted
3712 // RTS
3713 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3714 true,
3715 "RTS before fourth QoS data frame has not been transmitted");
3716 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts(),
3717 true,
3718 "Fourth QoS data frame should be transmitted with protection");
3720 +psduIt->phyId,
3721 +m_mainPhyId,
3722 "RTS before fourth QoS data frame should not be transmitted by the main PHY");
3723 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3725 "RTS before fourth data frame transmitted on an unexpected width");
3726 psduIt++;
3727 // CTS
3728 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3729 true,
3730 "CTS before fourth QoS data frame has not been transmitted");
3731 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsCts(),
3732 true,
3733 "CTS before fourth QoS data frame has not been transmitted");
3734 psduIt++;
3735 jumpToQosDataOrMuRts();
3736
3737 // the fourth QoS data frame is transmitted by an aux PHY after that the aux PHY has
3738 // obtained a TXOP and sent an RTS
3739 // RTS
3740 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3741 true,
3742 "RTS before fourth QoS data frame has not been transmitted");
3743 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts(),
3744 true,
3745 "Fourth QoS data frame should be transmitted with protection");
3747 +psduIt->phyId,
3748 +m_mainPhyId,
3749 "RTS before fourth QoS data frame should not be transmitted by the main PHY");
3750 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3752 "RTS before fourth data frame transmitted on an unexpected width");
3753 psduIt++;
3754 // CTS
3755 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3756 true,
3757 "CTS before fourth QoS data frame has not been transmitted");
3758 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsCts(),
3759 true,
3760 "CTS before fourth QoS data frame has not been transmitted");
3761 psduIt++;
3762 // QoS Data
3763 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3764 true,
3765 "Fourth QoS data frame has not been transmitted");
3766 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
3767 true,
3768 "Fourth QoS data frame has not been transmitted");
3769 NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
3770 +m_mainPhyId,
3771 "Fourth QoS data frame should be transmitted by the main PHY");
3772 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3774 "Fourth data frame not transmitted on the same width as RTS");
3775
3776 auto fourthLinkId = psduIt->linkId;
3777
3778 psduIt++;
3779 jumpToQosDataOrMuRts();
3780
3781 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()), true, "Expected more frames");
3782 // Do not check the start transmission time if a backoff is generated even when no
3783 // transmission is done (if the backoff expires while the main PHY is switching, a new
3784 // backoff is generated and, before this backoff expires, the main PHY may be requested
3785 // to switch to another auxiliary link; this may happen multiple times...)
3787 {
3788 NS_TEST_EXPECT_MSG_LT_OR_EQ(psduIt->startTx,
3790 "Fifth data frame transmitted too late");
3791 }
3792
3793 // the fifth QoS data frame is transmitted by the main PHY on an auxiliary link because
3794 // the aux PHY is not TX capable. The QoS data frame is protected by RTS if it is transmitted
3795 // on a different link than the previous one (because the MediumSyncDelay timer is running)
3796 if (psduIt->linkId != fourthLinkId)
3797 {
3798 // RTS
3799 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts(),
3800 true,
3801 "Fifth QoS data frame should be transmitted with protection");
3803 +psduIt->phyId,
3804 +m_mainPhyId,
3805 "RTS before fifth QoS data frame should be transmitted by the main PHY");
3806 psduIt++;
3807 // CTS
3808 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3809 true,
3810 "CTS before fifth QoS data frame has not been transmitted");
3811 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsCts(),
3812 true,
3813 "CTS before fifth QoS data frame has not been transmitted");
3814 psduIt++;
3815 }
3816
3817 // QoS Data
3818 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3819 true,
3820 "Fifth QoS data frame has not been transmitted");
3821 NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
3822 true,
3823 "Fifth QoS data frame has not been transmitted");
3824 NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
3825 +m_mainPhyId,
3826 "Fifth QoS data frame should be transmitted by the main PHY");
3827 NS_TEST_EXPECT_MSG_NE(+psduIt->linkId,
3828 +m_mainPhyId,
3829 "Fifth QoS data frame should be transmitted on an auxiliary link");
3830 NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3832 "Fifth data frame not transmitted on the correct channel width");
3833}
3834
3836 : EmlsrOperationsTestBase("Check UL OFDMA operations with an EMLSR client"),
3837 m_enableBsrp(enableBsrp),
3838 m_txPsdusPos(0),
3839 m_startAccessReq(0)
3840{
3841 m_linksToEnableEmlsrOn = {0, 1, 2};
3842 m_nEmlsrStations = 1;
3844 m_establishBaDl = false;
3845 m_establishBaUl = true;
3846 m_mainPhyId = 1;
3847 m_duration = Seconds(1);
3848}
3849
3850void
3852{
3853 Config::SetDefault("ns3::WifiPhy::ChannelSwitchDelay", TimeValue(m_transitionDelay.at(0)));
3854
3856
3857 m_apMac->GetQosTxop(AC_BE)->SetTxopLimits(
3858 {MicroSeconds(3200), MicroSeconds(3200), MicroSeconds(3200)});
3859
3860 auto muScheduler = CreateObjectWithAttributes<RrMultiUserScheduler>("EnableUlOfdma",
3861 BooleanValue(true),
3862 "EnableBsrp",
3864 m_apMac->AggregateObject(muScheduler);
3865}
3866
3867void
3869 uint8_t phyId,
3870 WifiConstPsduMap psduMap,
3871 WifiTxVector txVector,
3872 double txPowerW)
3873{
3874 EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
3875 auto linkId = m_txPsdus.back().linkId;
3876
3877 auto psdu = psduMap.begin()->second;
3878
3879 switch (psdu->GetHeader(0).GetType())
3880 {
3883 {
3884 // this is the first Trigger Frame sent after the AP requested channel access
3885 // through the Multi-user scheduler and it is an ICF for the EMLSR client
3886 m_txPsdusPos = m_txPsdus.size() - 1;
3887 auto txDuration = WifiPhy::CalculateTxDuration(psduMap,
3888 txVector,
3889 mac->GetWifiPhy(linkId)->GetPhyBand());
3890 NS_LOG_INFO("This is the first Trigger Frame\n");
3891 // once the Trigger Frame is received by the EMLSR client, make the client application
3892 // on the EMLSR client generate two packets. These packets will be sent via UL OFDMA
3893 // because the EMLSR client has blocked transmissions on other links when receiving
3894 // this Trigger Frame, hence it will not try to get access on other links via EDCA
3896 txDuration + MicroSeconds(1), // to account for propagation delay
3897 [=, this]() {
3898 for (const auto id : m_staMacs[0]->GetLinkIds())
3899 {
3901 m_staMacs[0]->GetFrameExchangeManager(id));
3903 ehtFem->UsingOtherEmlsrLink(),
3904 (id != linkId),
3905 "Link " << +id << " was" << (id == linkId ? " not" : "")
3906 << " expected to be blocked on EMLSR client at time "
3907 << Simulator::Now().As(Time::NS));
3908 }
3909 NS_LOG_INFO("Generate two packets\n");
3910 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(
3911 GetApplication(UPLINK, 0, 2, 100));
3912 });
3913 }
3914 break;
3915
3918 {
3919 CtrlBAckResponseHeader blockAck;
3920 psdu->GetPayload(0)->PeekHeader(blockAck);
3921 if (blockAck.IsMultiSta())
3922 {
3923 auto txDuration =
3925 txVector,
3926 mac->GetWifiPhy(linkId)->GetPhyBand());
3927 Simulator::Stop(txDuration + MicroSeconds(1));
3928 }
3929 }
3930 break;
3931
3932 default:;
3933 }
3934
3935 if (psdu->GetHeader(0).IsCfEnd())
3936 {
3937 // we do not check CF-End frames
3938 m_txPsdus.pop_back();
3939 }
3940}
3941
3942void
3952
3953void
3955{
3956 auto muScheduler = m_apMac->GetObject<MultiUserScheduler>();
3957 NS_TEST_ASSERT_MSG_NE(muScheduler, nullptr, "No MU scheduler installed on AP MLD");
3958
3959 NS_LOG_INFO("Setting Access Request interval");
3960
3961 const auto interval = MilliSeconds(50);
3962 muScheduler->SetAccessReqInterval(interval);
3963 m_startAccessReq = Simulator::Now() + interval;
3964}
3965
3966void
3968{
3969 /**
3970 * Sending BSRP TF disabled.
3971 *
3972 * The figure assumes that link 0 is used to send the first Trigger Frame after that the
3973 * AP MLD requests channel access through the Multi-user scheduler. The first Trigger Frame
3974 * is MU-RTS because EMLSR client needs an ICF; the other Trigger Frames are Basic TFs and
3975 * do not solicit the EMLSR client.
3976 * ┌─────┐ ┌─────┐ ┌──────┐
3977 * │ MU │ │Basic│ │Multi-│
3978 * [link 0] │ RTS │ │ TF │ │STA BA│
3979 * ───────────┴─────┴┬───┬┴─────┴┬────────┬─┴──────┴───────────────
3980 * │CTS│ │QoS Null│
3981 * ├───┤ ├────────┤
3982 * │CTS│ │QoS Data│
3983 * └───┘ └────────┘
3984 *
3985 * ┌─────┐
3986 * │Basic│
3987 * [link 1] │ TF │
3988 * ─────────────┴─────┴┬────┬──────────────────────────────────────
3989 * │QoS │
3990 * │Null│
3991 * └────┘
3992 *
3993 * ┌─────┐
3994 * │Basic│
3995 * [link 2] │ TF │
3996 * ─────────────┴─────┴┬────┬──────────────────────────────────────
3997 * │QoS │
3998 * │Null│
3999 * └────┘
4000 *
4001 * Sending BSRP TF enabled.
4002 *
4003 * The figure assumes that link 0 is used to send the first Trigger Frame after that the
4004 * AP MLD requests channel access through the Multi-user scheduler. The first Trigger Frames
4005 * are all BSRP Trigger Frames, but only the first one solicits the EMLSR client, too.
4006 * ┌─────┐ ┌─────┐ ┌──────┐
4007 * │BSRP │ │Basic│ │Multi-│
4008 * [link 0] │ TF │ │ TF │ │STA BA│
4009 * ───────────┴─────┴┬────────┬┴─────┴┬────────┬─┴──────┴──────────
4010 * │QoS Null│ │QoS Data│
4011 * ├────────┤ └────────┘
4012 * │QoS Null│
4013 * └────────┘
4014 *
4015 * ┌─────┐
4016 * │BSRP │
4017 * [link 1] │ TF │
4018 * ─────────────┴─────┴┬────┬──────────────────────────────────────
4019 * │QoS │
4020 * │Null│
4021 * └────┘
4022 *
4023 * ┌─────┐
4024 * │BSRP │
4025 * [link 2] │ TF │
4026 * ─────────────┴─────┴┬────┬──────────────────────────────────────
4027 * │QoS │
4028 * │Null│
4029 * └────┘
4030 */
4031
4032 NS_TEST_ASSERT_MSG_GT(m_txPsdusPos, 0, "First Trigger Frame not detected");
4033
4034 // Check the Trigger Frames (one per link) after requesting channel access
4035 auto index = m_txPsdusPos;
4036 const auto firstLinkId = m_txPsdus[m_txPsdusPos].linkId;
4037 for (; index < m_txPsdusPos + 3; ++index)
4038 {
4039 NS_TEST_ASSERT_MSG_EQ(m_txPsdus[index].psduMap.cbegin()->second->GetHeader(0).IsTrigger(),
4040 true,
4041 "Expected a Trigger Frame");
4042 CtrlTriggerHeader trigger;
4043 m_txPsdus[index].psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
4044
4045 TriggerFrameType triggerType =
4046 m_enableBsrp ? TriggerFrameType::BSRP_TRIGGER
4047 : (index == m_txPsdusPos ? TriggerFrameType::MU_RTS_TRIGGER
4048 : TriggerFrameType::BASIC_TRIGGER);
4049 NS_TEST_EXPECT_MSG_EQ(+static_cast<uint8_t>(trigger.GetType()),
4050 +static_cast<uint8_t>(triggerType),
4051 "Unexpected Trigger Frame type on link " << +m_txPsdus[index].linkId);
4052
4053 // only the first TF solicits the EMLSR client and the non-AP MLD
4055 trigger.GetNUserInfoFields(),
4056 (index == m_txPsdusPos ? 2 : 1),
4057 "Unexpected number of User Info fields for Trigger Frame, index=" << index);
4058 }
4059
4060 auto startIndex = index;
4061 std::size_t ctsCount = 0;
4062 std::size_t qosNullCount = 0;
4063 // Check responses to Trigger Frames
4064 for (; index < startIndex + 4; ++index)
4065 {
4066 const auto& hdr = m_txPsdus[index].psduMap.cbegin()->second->GetHeader(0);
4067
4068 if (hdr.IsCts())
4069 {
4070 ++ctsCount;
4071 continue;
4072 }
4073
4074 if (hdr.IsQosData() && !hdr.HasData())
4075 {
4076 ++qosNullCount;
4077 // if BSRP is enabled, the QoS Null frame sent by the EMLSR client in response to the
4078 // first BSRP TF reports a non-null buffer status
4079 if (m_enableBsrp &&
4080 hdr.GetAddr2() == m_staMacs[0]->GetFrameExchangeManager(firstLinkId)->GetAddress())
4081 {
4082 NS_TEST_EXPECT_MSG_GT(+hdr.GetQosQueueSize(), 0, "Unexpected buffer size");
4083 }
4084 else
4085 {
4086 NS_TEST_EXPECT_MSG_EQ(+hdr.GetQosQueueSize(), 0, "Unexpected buffer size");
4087 }
4088 continue;
4089 }
4090 }
4091 NS_TEST_EXPECT_MSG_EQ(ctsCount, (m_enableBsrp ? 0 : 2), "Unexpected number of CTS frames");
4092 NS_TEST_EXPECT_MSG_EQ(qosNullCount,
4093 (m_enableBsrp ? 4 : 2),
4094 "Unexpected number of QoS Null frames");
4095
4096 // we expect only one Basic Trigger Frame (sent on the same link as the first Trigger Frame),
4097 // because the buffer status reported on the other links by the non-EMLSR client is zero
4098 NS_TEST_ASSERT_MSG_EQ(m_txPsdus[index].psduMap.cbegin()->second->GetHeader(0).IsTrigger(),
4099 true,
4100 "Expected a Trigger Frame");
4101 NS_TEST_EXPECT_MSG_EQ(+m_txPsdus[index].linkId,
4102 +firstLinkId,
4103 "Unexpected link ID for Basic TF");
4104 CtrlTriggerHeader trigger;
4105 m_txPsdus[index].psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
4106
4107 NS_TEST_EXPECT_MSG_EQ(+static_cast<uint8_t>(trigger.GetType()),
4108 +static_cast<uint8_t>(TriggerFrameType::BASIC_TRIGGER),
4109 "Unexpected Trigger Frame type");
4110
4111 // when BSRP TF is enabled, the non-EMLSR client has already communicated a buffer status of
4112 // zero, so it is not solicited by the AP through the Basic Trigger Frame. Otherwise, it is
4113 // solicited because buffer status was not known when the BSRP TF was prepared (before sending
4114 // MU-RTS)
4116 (m_enableBsrp ? 1 : 2),
4117 "Unexpected number of User Info fields for Basic Trigger Frame");
4118
4119 // Response(s) to the Basic Trigger Frame
4120 startIndex = ++index;
4121 for (; index < startIndex + (m_enableBsrp ? 1 : 2); ++index)
4122 {
4123 const auto& hdr = m_txPsdus[index].psduMap.cbegin()->second->GetHeader(0);
4124
4125 NS_TEST_EXPECT_MSG_EQ(hdr.IsQosData(), true, "Expected a QoS frame");
4126
4127 // EMLSR client sends a QoS Data frame, non-EMLSR client sends a QoS Null frame
4129 hdr.HasData(),
4130 (hdr.GetAddr2() == m_staMacs[0]->GetFrameExchangeManager(firstLinkId)->GetAddress()),
4131 "Unexpected type of QoS data frame");
4132
4133 if (hdr.HasData())
4134 {
4135 NS_TEST_EXPECT_MSG_EQ(m_txPsdus[index].txVector.IsUlMu(),
4136 true,
4137 "QoS Data frame should be sent in a TB PPDU");
4138 }
4139 }
4140
4141 // Finally, the AP MLD sends a Multi-STA BlockAck
4142 NS_TEST_EXPECT_MSG_EQ(m_txPsdus[index].psduMap.cbegin()->second->GetHeader(0).IsBlockAck(),
4143 true,
4144 "Expected a BlockAck frame");
4145 CtrlBAckResponseHeader blockAck;
4146 m_txPsdus[index].psduMap.cbegin()->second->GetPayload(0)->PeekHeader(blockAck);
4147 NS_TEST_EXPECT_MSG_EQ(blockAck.IsMultiSta(), true, "Expected a Multi-STA BlockAck");
4148}
4149
4152 std::string("Check EMLSR link switching (switchAuxPhy=") +
4153 std::to_string(params.switchAuxPhy) + ", resetCamStateAndInterruptSwitch=" +
4154 std::to_string(params.resetCamStateAndInterruptSwitch) +
4155 ", auxPhyMaxChWidth=" + std::to_string(params.auxPhyMaxChWidth) + "MHz )"),
4156 m_switchAuxPhy(params.switchAuxPhy),
4157 m_resetCamStateAndInterruptSwitch(params.resetCamStateAndInterruptSwitch),
4158 m_auxPhyMaxChWidth(params.auxPhyMaxChWidth),
4159 m_countQoSframes(0),
4160 m_countIcfFrames(0),
4161 m_countRtsFrames(0),
4162 m_txPsdusPos(0)
4163{
4164 m_nEmlsrStations = 1;
4166 m_linksToEnableEmlsrOn = {0, 1, 2}; // enable EMLSR on all links right after association
4167 m_mainPhyId = 1;
4168 m_establishBaDl = true;
4169 m_duration = Seconds(1);
4170 // when aux PHYs do not switch link, the main PHY switches back to its previous link after
4171 // a TXOP, hence the transition delay must exceed the channel switch delay (default: 250us)
4173}
4174
4175void
4177 uint8_t phyId,
4178 WifiConstPsduMap psduMap,
4179 WifiTxVector txVector,
4180 double txPowerW)
4181{
4182 EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
4183 auto linkId = m_txPsdus.back().linkId;
4184
4185 auto psdu = psduMap.begin()->second;
4186 auto nodeId = mac->GetDevice()->GetNode()->GetId();
4187
4188 switch (psdu->GetHeader(0).GetType())
4189 {
4191 NS_ASSERT_MSG(nodeId > 0, "APs do not send AssocReq frames");
4192 NS_TEST_EXPECT_MSG_EQ(+linkId, +m_mainPhyId, "AssocReq not sent by the main PHY");
4193 break;
4194
4195 case WIFI_MAC_MGT_ACTION: {
4196 auto [category, action] = WifiActionHeader::Peek(psdu->GetPayload(0));
4197
4198 if (nodeId == 1 && category == WifiActionHeader::PROTECTED_EHT &&
4199 action.protectedEhtAction ==
4201 {
4202 // the EMLSR client is starting the transmission of the EML OMN frame;
4203 // temporarily block transmissions of QoS data frames from the AP MLD to the
4204 // non-AP MLD on all the links but the one used for ML setup, so that we know
4205 // that the first QoS data frame is sent on the link of the main PHY
4206 std::set<uint8_t> linksToBlock;
4207 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
4208 {
4209 if (id != m_mainPhyId)
4210 {
4211 linksToBlock.insert(id);
4212 }
4213 }
4214 m_apMac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
4215 AC_BE,
4217 m_staMacs[0]->GetAddress(),
4218 m_apMac->GetAddress(),
4219 {0},
4220 linksToBlock);
4221 }
4222 }
4223 break;
4224
4226 CheckInitialControlFrame(psduMap, txVector, linkId);
4227 break;
4228
4229 case WIFI_MAC_QOSDATA:
4230 CheckQosFrames(psduMap, txVector, linkId);
4231 break;
4232
4233 case WIFI_MAC_CTL_RTS:
4234 CheckRtsFrame(psduMap, txVector, linkId);
4235 break;
4236
4237 default:;
4238 }
4239}
4240
4241void
4243{
4244 Config::SetDefault("ns3::DefaultEmlsrManager::SwitchAuxPhy", BooleanValue(m_switchAuxPhy));
4245 Config::SetDefault("ns3::EmlsrManager::ResetCamState",
4247 Config::SetDefault("ns3::AdvancedEmlsrManager::InterruptSwitch",
4249 Config::SetDefault("ns3::EmlsrManager::AuxPhyChannelWidth", UintegerValue(m_auxPhyMaxChWidth));
4250 Config::SetDefault("ns3::WifiPhy::ChannelSwitchDelay", TimeValue(MicroSeconds(45)));
4251
4253
4255 for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
4256 {
4257 m_apMac->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_errorModel);
4258 }
4259
4260 // use channels of different widths
4261 for (auto mac : std::initializer_list<Ptr<WifiMac>>{m_apMac, m_staMacs[0]})
4262 {
4263 mac->GetWifiPhy(0)->SetOperatingChannel(
4265 mac->GetWifiPhy(1)->SetOperatingChannel(
4267 mac->GetWifiPhy(2)->SetOperatingChannel(
4269 }
4270}
4271
4272void
4282
4283void
4285 const WifiTxVector& txVector,
4286 uint8_t linkId)
4287{
4289
4290 switch (m_countQoSframes)
4291 {
4292 case 1:
4293 // unblock transmissions on all links
4294 m_apMac->GetMacQueueScheduler()->UnblockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
4295 AC_BE,
4297 m_staMacs[0]->GetAddress(),
4298 m_apMac->GetAddress(),
4299 {0},
4300 {0, 1, 2});
4301 // block transmissions on the link used for ML setup
4302 m_apMac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
4303 AC_BE,
4305 m_staMacs[0]->GetAddress(),
4306 m_apMac->GetAddress(),
4307 {0},
4308 {m_mainPhyId});
4309 // generate a new data packet, which will be sent on a link other than the one
4310 // used for ML setup, hence triggering a link switching on the EMLSR client
4311 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, 0, 2, 1000));
4312 break;
4313 case 2:
4314 // block transmission on the link used to send this QoS data frame
4315 m_apMac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
4316 AC_BE,
4318 m_staMacs[0]->GetAddress(),
4319 m_apMac->GetAddress(),
4320 {0},
4321 {linkId});
4322 // generate a new data packet, which will be sent on the link that has not been used
4323 // so far, hence triggering another link switching on the EMLSR client
4324 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, 0, 2, 1000));
4325 break;
4326 case 3:
4327 // block transmission on the link used to send this QoS data frame
4328 m_apMac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
4329 AC_BE,
4331 m_staMacs[0]->GetAddress(),
4332 m_apMac->GetAddress(),
4333 {0},
4334 {linkId});
4335 // unblock transmissions on the link used for ML setup
4336 m_apMac->GetMacQueueScheduler()->UnblockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
4337 AC_BE,
4339 m_staMacs[0]->GetAddress(),
4340 m_apMac->GetAddress(),
4341 {0},
4342 {m_mainPhyId});
4343 // generate a new data packet, which will be sent again on the link used for ML setup,
4344 // hence triggering yet another link switching on the EMLSR client
4345 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, 0, 2, 1000));
4346 break;
4347 case 4:
4348 // block transmissions on all links at non-AP MLD side
4349 m_staMacs[0]->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
4350 AC_BE,
4352 m_apMac->GetAddress(),
4353 m_staMacs[0]->GetAddress(),
4354 {0},
4355 {0, 1, 2});
4356 // unblock transmissions on the link used for ML setup (non-AP MLD side)
4357 m_staMacs[0]->GetMacQueueScheduler()->UnblockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
4358 AC_BE,
4360 m_apMac->GetAddress(),
4361 m_staMacs[0]->GetAddress(),
4362 {0},
4363 {m_mainPhyId});
4364 // trigger establishment of BA agreement with AP as recipient
4365 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 4, 1000));
4366 break;
4367 case 5:
4368 // unblock transmissions on all links at non-AP MLD side
4369 m_staMacs[0]->GetMacQueueScheduler()->UnblockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
4370 AC_BE,
4372 m_apMac->GetAddress(),
4373 m_staMacs[0]->GetAddress(),
4374 {0},
4375 {0, 1, 2});
4376 // block transmissions on the link used for ML setup (non-AP MLD side)
4377 m_staMacs[0]->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
4378 AC_BE,
4380 m_apMac->GetAddress(),
4381 m_staMacs[0]->GetAddress(),
4382 {0},
4383 {m_mainPhyId});
4384 // generate a new data packet, which will be sent on a link other than the one
4385 // used for ML setup, hence triggering a link switching on the EMLSR client
4386 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000));
4387 break;
4388 }
4389}
4390
4391/**
4392 * AUX PHY switching enabled (X = channel switch delay)
4393 *
4394 * |--------- aux PHY A ---------|------ main PHY ------|-------------- aux PHY B -------------
4395 * ┌───┐ ┌───┐
4396 * │ICF│ │QoS│
4397 * ──────────────────────────┴───┴┬───┬┴───┴┬──┬────────────────────────────────────────────────
4398 * [link 0] │CTS│ │BA│
4399 * └───┘ └──┘
4400 *
4401 *
4402 * |--------- main PHY ----------|------------------ aux PHY A ----------------|--- main PHY ---
4403 * ┌───┐ ┌───┐ ┌───┐ ┌───┐
4404 * │ICF│ │QoS│ │ICF│ │QoS│
4405 * ───┴───┴┬───┬┴───┴┬──┬──────────────────────────────────────────────────┴───┴┬───┬┴───┴┬──┬──
4406 * [link 1]│CTS│ │BA│ │CTS│ │BA│
4407 * └───┘ └──┘ └───┘ └──┘
4408 *
4409 *
4410 * |--------------------- aux PHY B --------------------|------ main PHY ------|-- aux PHY A ---
4411 * ┌───┐ ┌───┐
4412 * │ICF│ │QoS│
4413 * ─────────────────────────────────────────────────┴───┴┬───┬┴───┴┬──┬─────────────────────────
4414 * [link 2] │CTS│ │BA│
4415 * └───┘ └──┘
4416 *
4417 * ... continued ...
4418 *
4419 * |----------------------------------------- aux PHY B ---------------------------------------
4420 * ─────────────────────────────────────────────────────────────────────────────────────────────
4421 * [link 0]
4422 *
4423 * |--------- main PHY ----------|X|X|------------------------ aux PHY A ----------------------
4424 * ┌───┐
4425 * │ACK│
4426 * ──────────┬───┬┴───┴────────────────────────────────────────────────────────────────────────
4427 * [link 1] │QoS│
4428 * └───┘
4429 *
4430 * |-------- aux PHY A ----------|X|---------------------- main PHY ---------------------------
4431 * ┌──┐
4432 * │BA│
4433 * ────────────────────────┬───X──────┬───┬┴──┴────────────────────────────────────────────────
4434 * [link 2] │RTS│ │QoS│
4435 * └───┘ └───┘
4436 ************************************************************************************************
4437 *
4438 * AUX PHY switching disabled (X = channel switch delay)
4439 *
4440 * |------------------------------------------ aux PHY A ---------------------------------------
4441 * |-- main PHY --|X|
4442 * ┌───┐ ┌───┐
4443 * │ICF│ │QoS│
4444 * ──────────────────────────┴───┴┬───┬┴───┴┬──┬────────────────────────────────────────────────
4445 * [link 0] │CTS│ │BA│
4446 * └───┘ └──┘
4447 *
4448 * |-main|
4449 * |--------- main PHY ----------| |-PHY-| |------ main PHY ------
4450 * ┌───┐ ┌───┐ ┌───┐ ┌───┐
4451 * │ICF│ │QoS│ │ICF│ │QoS│
4452 * ───┴───┴┬───┬┴───┴┬──┬──────────────────────────────────────────────────┴───┴┬───┬┴───┴┬──┬──
4453 * [link 1]│CTS│ │BA│ │CTS│ │BA│
4454 * └───┘ └──┘ └───┘ └──┘
4455 *
4456 *
4457 * |------------------------------------------ aux PHY B ---------------------------------------
4458 * |-- main PHY --|X|
4459 * ┌───┐ ┌───┐
4460 * │ICF│ │QoS│
4461 * ─────────────────────────────────────────────────┴───┴┬───┬┴───┴┬──┬─────────────────────────
4462 * [link 2] │CTS│ │BA│
4463 * └───┘ └──┘
4464 *
4465 * ... continued ...
4466 *
4467 * |----------------------------------------- aux PHY A ---------------------------------------
4468 * ─────────────────────────────────────────────────────────────────────────────────────────────
4469 * [link 0]
4470 *
4471 * |-------- main PHY --------| |--- main PHY ---|
4472 * ┌───┐
4473 * │ACK│
4474 * ──────────┬───┬┴───┴────────────────────────────────────────────────────────────────────────
4475 * [link 1] │QoS│
4476 * └───┘
4477 *
4478 * |------------------------------------------ aux PHY B --------------------------------------
4479 * |X||X| |X|-------------- main PHY --------------
4480 * ┌───┐ ┌──┐
4481 * │CTS│ │BA│
4482 * ────────────────────────┬───X───────────────┬───┬┴───┴┬───┬┴──┴─────────────────────────────
4483 * [link 2] │RTS│ │RTS│ │QoS│
4484 * └───┘ └───┘ └───┘
4485 *
4486 */
4487
4488void
4490 const WifiTxVector& txVector,
4491 uint8_t linkId)
4492{
4493 if (++m_countIcfFrames == 1)
4494 {
4495 m_txPsdusPos = m_txPsdus.size() - 1;
4496 }
4497
4498 // the first ICF is sent to protect ADDBA_REQ for DL BA agreement, then one ICF is sent before
4499 // each of the 4 DL QoS Data frames; finally, another ICF is sent before the ADDBA_RESP for UL
4500 // BA agreement. Hence, at any time the number of ICF sent is always greater than or equal to
4501 // the number of QoS data frames sent.
4502 NS_TEST_EXPECT_MSG_GT_OR_EQ(m_countIcfFrames, m_countQoSframes, "Unexpected number of ICFs");
4503
4504 auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId);
4505 auto phyRecvIcf = m_staMacs[0]->GetWifiPhy(linkId); // PHY receiving the ICF
4506
4507 auto currMainPhyLinkId = m_staMacs[0]->GetLinkForPhy(mainPhy);
4508 NS_TEST_ASSERT_MSG_EQ(currMainPhyLinkId.has_value(),
4509 true,
4510 "Didn't find the link on which the Main PHY is operating");
4511 NS_TEST_ASSERT_MSG_NE(phyRecvIcf,
4512 nullptr,
4513 "No PHY on the link where ICF " << m_countQoSframes << " was sent");
4514
4515 if (phyRecvIcf != mainPhy)
4516 {
4518 phyRecvIcf->GetChannelWidth(),
4520 "Aux PHY that received ICF "
4521 << m_countQoSframes << " is operating on a channel whose width exceeds the limit");
4522 }
4523
4524 // the first two ICFs (before ADDBA_REQ and before first DL QoS Data) and the ICF before the
4525 // ADDBA_RESP are received by the main PHY. If aux PHYs do not switch links, the ICF before
4526 // the last DL QoS Data is also received by the main PHY
4527 NS_TEST_EXPECT_MSG_EQ((phyRecvIcf == mainPhy),
4528 (m_countIcfFrames == 1 || m_countIcfFrames == 2 ||
4530 "Expecting that the ICF was received by the main PHY");
4531
4532 // if aux PHYs do not switch links, the main PHY is operating on its original link when
4533 // the transmission of an ICF starts
4534 NS_TEST_EXPECT_MSG_EQ(m_switchAuxPhy || currMainPhyLinkId == m_mainPhyId,
4535 true,
4536 "Main PHY is operating on an unexpected link ("
4537 << +currMainPhyLinkId.value() << ", expected " << +m_mainPhyId
4538 << ")");
4539
4540 auto txDuration =
4541 WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
4542
4543 // check that PHYs are operating on the expected link after the reception of the ICF
4544 Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
4545 // the main PHY must be operating on the link where ICF was sent
4546 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(linkId),
4547 mainPhy,
4548 "PHY operating on link where ICF was sent is not the main PHY");
4549
4550 // the behavior of Aux PHYs depends on whether they switch channel or not
4551 if (m_switchAuxPhy)
4552 {
4553 if (mainPhy != phyRecvIcf)
4554 {
4555 NS_TEST_EXPECT_MSG_EQ(phyRecvIcf->IsStateSwitching(),
4556 true,
4557 "Aux PHY expected to switch channel");
4558 }
4559 Simulator::Schedule(phyRecvIcf->GetChannelSwitchDelay(), [=, this]() {
4560 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(*currMainPhyLinkId),
4561 phyRecvIcf,
4562 "The Aux PHY that received the ICF is expected to operate "
4563 "on the link where Main PHY was before switching channel");
4564 });
4565 }
4566 else
4567 {
4568 NS_TEST_EXPECT_MSG_EQ(phyRecvIcf->IsStateSwitching(),
4569 false,
4570 "Aux PHY is not expected to switch channel");
4571 NS_TEST_EXPECT_MSG_EQ(phyRecvIcf->GetPhyBand(),
4572 mainPhy->GetPhyBand(),
4573 "The Aux PHY that received the ICF is expected to operate "
4574 "on the same band as the Main PHY");
4575 }
4576 });
4577}
4578
4579void
4581 const WifiTxVector& txVector,
4582 uint8_t linkId)
4583{
4584 // corrupt the first RTS frame (sent by the EMLSR client)
4585 if (++m_countRtsFrames == 1)
4586 {
4587 auto psdu = psduMap.begin()->second;
4588 m_errorModel->SetList({psdu->GetPacket()->GetUid()});
4589
4590 // check that when CTS timeout occurs, the main PHY is switching
4592 m_staMacs[0]->GetFrameExchangeManager(linkId)->GetWifiTxTimer().GetDelayLeft() -
4593 TimeStep(1),
4594 [=, this]() {
4595 // store the time to complete the current channel switch at CTS timeout
4596 auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId);
4597 auto toCurrSwitchEnd = mainPhy->GetDelayUntilIdle() + TimeStep(1);
4598
4599 Simulator::Schedule(TimeStep(1), [=, this]() {
4600 NS_TEST_EXPECT_MSG_EQ(mainPhy->IsStateSwitching(),
4601 true,
4602 "Main PHY expected to be in SWITCHING state instead of "
4603 << mainPhy->GetState()->GetState());
4604
4605 // If main PHY channel switch can be interrupted, the main PHY should be back
4606 // operating on the preferred link after a channel switch delay. Otherwise, it
4607 // will be operating on the preferred link, if SwitchAuxPhy is false, or on the
4608 // link used to send the RTS, if SwitchAuxPhy is true, after the remaining
4609 // channel switching time plus the channel switch delay.
4611 ? m_mainPhyId
4612 : linkId;
4613 auto delayLeft = m_resetCamStateAndInterruptSwitch
4614 ? Time{0}
4615 : toCurrSwitchEnd; // time to complete current switch
4617 {
4618 // add the time to perform another channel switch
4619 delayLeft += mainPhy->GetChannelSwitchDelay();
4620 }
4621
4622 auto totalSwitchDelay =
4623 delayLeft + (mainPhy->GetChannelSwitchDelay() - toCurrSwitchEnd);
4624
4625 Simulator::Schedule(delayLeft - TimeStep(1), [=, this]() {
4626 // check if the MSD timer was running on the link left by the main PHY
4627 // before completing channel switch
4628 bool msdWasRunning = m_staMacs[0]
4629 ->GetEmlsrManager()
4630 ->GetElapsedMediumSyncDelayTimer(m_mainPhyId)
4631 .has_value();
4632
4633 Simulator::Schedule(TimeStep(2), [=, this]() {
4634 auto id = m_staMacs[0]->GetLinkForPhy(mainPhy);
4635 NS_TEST_EXPECT_MSG_EQ(id.has_value(),
4636 true,
4637 "Expected main PHY to operate on a link");
4639 newLinkId,
4640 "Main PHY is operating on an unexpected link");
4641 const auto startMsd =
4642 (totalSwitchDelay >
4644 const auto msdIsRunning = msdWasRunning || startMsd;
4646 m_staMacs[0],
4648 msdIsRunning,
4649 std::string("because total switch delay was ") +
4650 std::to_string(totalSwitchDelay.GetNanoSeconds()) + "ns");
4651 });
4652 });
4653 });
4654 });
4655 }
4656 // block transmissions on all other links at non-AP MLD side
4657 std::set<uint8_t> links{0, 1, 2};
4658 links.erase(linkId);
4659 m_staMacs[0]->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
4660 AC_BE,
4662 m_apMac->GetAddress(),
4663 m_staMacs[0]->GetAddress(),
4664 {0},
4665 links);
4666}
4667
4668void
4670{
4671 NS_TEST_ASSERT_MSG_NE(m_txPsdusPos, 0, "BA agreement establishment not completed");
4672
4673 // Expected frame exchanges after ML setup and EML OMN exchange:
4674 // 1. (DL) ICF + CTS + ADDBA_REQ + ACK
4675 // 2. (UL) ADDBA_RESP + ACK
4676 // 3. (DL) ICF + CTS + DATA + BA
4677 // 4. (DL) ICF + CTS + DATA + BA
4678 // 5. (DL) ICF + CTS + DATA + BA
4679 // 6. (DL) ICF + CTS + DATA + BA
4680 // 7. (UL) ADDBA_REQ + ACK
4681 // 8. (DL) ICF + CTS + ADDBA_RESP + ACK
4682 // 9. (UL) DATA + BA
4683 // 10. (UL) RTS - CTS timeout
4684 // 11. (UL) (RTS + CTS + ) DATA + BA
4685
4686 // frame exchange 11 is protected if SwitchAuxPhy is false or (SwitchAuxPhy is true and) the
4687 // main PHY switch can be interrupted
4688 bool fe11protected = !m_switchAuxPhy || m_resetCamStateAndInterruptSwitch;
4689
4690 NS_TEST_EXPECT_MSG_EQ(m_countIcfFrames, 6, "Unexpected number of ICFs sent");
4691
4692 // frame exchanges without RTS because the EMLSR client sent the initial frame through main PHY
4693 const std::size_t nFrameExchNoRts = fe11protected ? 3 : 4;
4694
4695 const std::size_t nFrameExchWithRts = fe11protected ? 1 : 0;
4696
4698 m_txPsdusPos +
4699 m_countIcfFrames * 4 + /* frames in frame exchange with ICF */
4700 nFrameExchNoRts * 2 + /* frames in frame exchange without RTS */
4701 nFrameExchWithRts * 4 + /* frames in frame exchange with RTS */
4702 1, /* corrupted RTS */
4703 "Insufficient number of TX PSDUs");
4704
4705 // m_txPsdusPos points to the first ICF
4706 auto psduIt = std::next(m_txPsdus.cbegin(), m_txPsdusPos);
4707
4708 const std::size_t nFrameExchanges =
4709 m_countIcfFrames + nFrameExchNoRts + nFrameExchWithRts + 1 /* corrupted RTS */;
4710
4711 for (std::size_t i = 1; i <= nFrameExchanges; ++i)
4712 {
4713 if (i == 1 || (i >= 3 && i <= 6) || i == 8 || i == 10 || (i == 11 && fe11protected))
4714 {
4715 // frame exchanges with protection
4716 NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 &&
4717 (i < 9 ? psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsTrigger()
4718 : psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsRts())),
4719 true,
4720 "Expected a Trigger Frame (ICF)");
4721 psduIt++;
4722 if (i == 10)
4723 {
4724 continue; // corrupted RTS
4725 }
4726 NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 &&
4727 psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsCts()),
4728 true,
4729 "Expected a CTS");
4730 psduIt++;
4731 }
4732
4733 if (i == 1 || i == 2 || i == 7 || i == 8) // frame exchanges with ADDBA REQ/RESP frames
4734 {
4735 NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 &&
4736 psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsMgt()),
4737 true,
4738 "Expected a management frame");
4739 psduIt++;
4740 NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 &&
4741 psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsAck()),
4742 true,
4743 "Expected a Normal Ack");
4744 }
4745 else
4746 {
4747 NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 &&
4748 psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsQosData()),
4749 true,
4750 "Expected a QoS Data frame");
4751 psduIt++;
4752 NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 &&
4753 psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsBlockAck()),
4754 true,
4755 "Expected a BlockAck");
4756 }
4757 psduIt++;
4758 }
4759}
4760
4762 : EmlsrOperationsTestBase(std::string("Check EMLSR link switching (auxPhyMaxChWidth=") +
4763 std::to_string(auxPhyMaxChWidth) + "MHz )"),
4764 m_auxPhyMaxChWidth(auxPhyMaxChWidth),
4765 m_channelSwitchDelay(MicroSeconds(75)),
4766 m_currMainPhyLinkId(0),
4767 m_nextMainPhyLinkId(0)
4768{
4769 m_nEmlsrStations = 1;
4771 m_linksToEnableEmlsrOn = {0, 1, 2}; // enable EMLSR on all links right after association
4772 m_mainPhyId = 1;
4773 m_establishBaUl = true;
4774 m_duration = Seconds(1);
4776}
4777
4778void
4786
4787void
4789{
4790 Config::SetDefault("ns3::DefaultEmlsrManager::SwitchAuxPhy", BooleanValue(true));
4791 Config::SetDefault("ns3::EmlsrManager::AuxPhyChannelWidth", UintegerValue(m_auxPhyMaxChWidth));
4792 Config::SetDefault("ns3::EmlsrManager::AuxPhyMaxModClass", StringValue("EHT"));
4793 Config::SetDefault("ns3::WifiPhy::ChannelSwitchDelay", TimeValue(m_channelSwitchDelay));
4794
4796
4797 // use channels of different widths
4798 for (auto mac : std::initializer_list<Ptr<WifiMac>>{m_apMac, m_staMacs[0], m_staMacs[1]})
4799 {
4800 mac->GetWifiPhy(0)->SetOperatingChannel(
4802 mac->GetWifiPhy(1)->SetOperatingChannel(
4804 mac->GetWifiPhy(2)->SetOperatingChannel(
4806 }
4807}
4808
4809void
4811{
4812 m_staMacs[1]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 1, 1, 2000));
4813
4814 // force the transmission of the packet to happen now on the given link.
4815 // Multiple ScheduleNow calls are needed because Node::AddApplication() schedules a call to
4816 // Application::Initialize(), which schedules a call to Application::StartApplication(), which
4817 // schedules a call to PacketSocketClient::Send(), which finally generates the packet
4818 Simulator::ScheduleNow([=, this]() {
4819 Simulator::ScheduleNow([=, this]() {
4820 Simulator::ScheduleNow([=, this]() {
4821 m_staMacs[1]->GetFrameExchangeManager(linkId)->StartTransmission(
4822 m_staMacs[1]->GetQosTxop(AC_BE),
4823 m_staMacs[1]->GetWifiPhy(linkId)->GetChannelWidth());
4824 });
4825 });
4826 });
4827
4828 // check that the other MLD started transmitting on the correct link
4829 Simulator::Schedule(TimeStep(1), [=, this]() {
4830 NS_TEST_EXPECT_MSG_EQ(m_staMacs[1]->GetWifiPhy(linkId)->IsStateTx(),
4831 true,
4832 "At time " << Simulator::Now().As(Time::NS)
4833 << ", other MLD did not start transmitting on link "
4834 << +linkId);
4835 });
4836}
4837
4838void
4840{
4841 auto currMainPhyLinkId = m_staMacs[0]->GetLinkForPhy(m_mainPhyId);
4842 NS_TEST_ASSERT_MSG_EQ(currMainPhyLinkId.has_value(),
4843 true,
4844 "Main PHY is not operating on any link");
4845 m_currMainPhyLinkId = *currMainPhyLinkId;
4847
4848 // request the main PHY to switch to another link
4849 m_staMacs[0]->GetEmlsrManager()->SwitchMainPhy(
4851 false,
4854 EmlsrDlTxopIcfReceivedByAuxPhyTrace{}); // trace info not used
4855
4856 // the other MLD transmits a packet to the AP
4858
4859 // schedule another packet transmission slightly (10 us) before the end of aux PHY switch
4862 this,
4864
4865 // first checkpoint is after that the preamble of the PPDU has been received
4867}
4868
4869/**
4870 * ┌───────────────┐
4871 * [link X] │ other to AP │CP3
4872 * ──────────────────────────────┴───────────────┴──────────────────────────────────────────────
4873 * |------ main PHY ------| |------------------- aux PHY ---------------------
4874 * .\_ _/
4875 * . \_ _/
4876 * . \_ _/
4877 * . \_ _/
4878 * [link Y] . CP1 \/ CP2
4879 * .┌───────────────┐
4880 * .│ other to AP │
4881 * ─────────────────────────┴───────────────┴────────────────────────────────────────────────────
4882 * |------------ aux PHY ----------|---------------------- main PHY ----------------------------
4883 *
4884 */
4885
4886void
4888{
4889 // first checkpoint is after that the preamble of the first PPDU has been received
4890 auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId);
4891
4892 // 1. Main PHY is switching
4893 NS_TEST_EXPECT_MSG_EQ(mainPhy->IsStateSwitching(), true, "Main PHY is not switching");
4894
4895 auto auxPhy = m_staMacs[0]->GetWifiPhy(m_nextMainPhyLinkId);
4896 NS_TEST_EXPECT_MSG_NE(mainPhy, auxPhy, "Main PHY is operating on an unexpected link");
4897
4898 // 2. Aux PHY is receiving the PHY header
4899 NS_TEST_EXPECT_MSG_EQ(auxPhy->GetInfoIfRxingPhyHeader().has_value(),
4900 true,
4901 "Aux PHY is not receiving a PHY header");
4902
4903 // 3. Main PHY dropped the preamble because it is switching
4904 NS_TEST_EXPECT_MSG_EQ(mainPhy->GetInfoIfRxingPhyHeader().has_value(),
4905 false,
4906 "Main PHY is receiving a PHY header");
4907
4908 // 4. Channel access manager on destination link (Y) has been notified of CCA busy, but not
4909 // until the end of transmission (main PHY dropped the preamble and notified CCA busy until
4910 // end of transmission but the channel access manager on link Y does not yet have a listener
4911 // attached to the main PHY; aux PHY notified CCA busy until the end of the PHY header field
4912 // being received)
4913 const auto caManager = m_staMacs[0]->GetChannelAccessManager(m_nextMainPhyLinkId);
4914 const auto endTxTime = m_staMacs[1]->GetChannelAccessManager(m_nextMainPhyLinkId)->m_lastTxEnd;
4915 NS_TEST_ASSERT_MSG_EQ(caManager->m_lastBusyEnd.contains(WIFI_CHANLIST_PRIMARY),
4916 true,
4917 "No CCA information for primary20 channel");
4919 caManager->m_lastBusyEnd[WIFI_CHANLIST_PRIMARY],
4921 "ChannelAccessManager on destination link not notified of CCA busy");
4923 caManager->m_lastBusyEnd[WIFI_CHANLIST_PRIMARY],
4924 endTxTime,
4925 "ChannelAccessManager on destination link notified of CCA busy until end of transmission");
4926
4927 // second checkpoint is after that the main PHY completed the link switch
4928 Simulator::Schedule(mainPhy->GetDelayUntilIdle() + TimeStep(1),
4930 this);
4931}
4932
4933void
4935{
4936 // second checkpoint is after that the main PHY completed the link switch. The channel access
4937 // manager on destination link (Y) is expected to be notified by the main PHY that medium is
4938 // busy until the end of the ongoing transmission
4939 const auto caManager = m_staMacs[0]->GetChannelAccessManager(m_nextMainPhyLinkId);
4940 const auto endTxTime = m_staMacs[1]->GetChannelAccessManager(m_nextMainPhyLinkId)->m_lastTxEnd;
4941 NS_TEST_ASSERT_MSG_EQ(caManager->m_lastBusyEnd.contains(WIFI_CHANLIST_PRIMARY),
4942 true,
4943 "No CCA information for primary20 channel");
4945 caManager->m_lastBusyEnd[WIFI_CHANLIST_PRIMARY],
4947 "ChannelAccessManager on destination link not notified of CCA busy");
4948 NS_TEST_EXPECT_MSG_GT_OR_EQ(caManager->m_lastBusyEnd[WIFI_CHANLIST_PRIMARY],
4949 endTxTime,
4950 "ChannelAccessManager on destination link not notified of CCA busy "
4951 "until end of transmission");
4952
4953 // third checkpoint is after that the aux PHY completed the link switch
4955}
4956
4957void
4959{
4960 // third checkpoint is after that the aux PHY completed the link switch. The channel access
4961 // manager on source link (X) is expected to be notified by the aux PHY that medium is
4962 // busy until the end of the ongoing transmission (even if the aux PHY was not listening to
4963 // link X when transmission started, its interface on link X recorded the transmission)
4964 const auto caManager = m_staMacs[0]->GetChannelAccessManager(m_currMainPhyLinkId);
4965 const auto endTxTime = m_staMacs[1]->GetChannelAccessManager(m_currMainPhyLinkId)->m_lastTxEnd;
4966 NS_TEST_ASSERT_MSG_EQ(caManager->m_lastBusyEnd.contains(WIFI_CHANLIST_PRIMARY),
4967 true,
4968 "No CCA information for primary20 channel");
4969 NS_TEST_EXPECT_MSG_GT_OR_EQ(caManager->m_lastBusyEnd[WIFI_CHANLIST_PRIMARY],
4971 "ChannelAccessManager on source link not notified of CCA busy");
4972 NS_TEST_EXPECT_MSG_GT_OR_EQ(caManager->m_lastBusyEnd[WIFI_CHANLIST_PRIMARY],
4973 endTxTime,
4974 "ChannelAccessManager on source link not notified of CCA busy "
4975 "until end of transmission");
4976}
4977
4978SingleLinkEmlsrTest::SingleLinkEmlsrTest(bool switchAuxPhy, bool auxPhyTxCapable)
4980 "Check EMLSR single link operation (switchAuxPhy=" + std::to_string(switchAuxPhy) +
4981 ", auxPhyTxCapable=" + std::to_string(auxPhyTxCapable) + ")"),
4982 m_switchAuxPhy(switchAuxPhy),
4983 m_auxPhyTxCapable(auxPhyTxCapable)
4984{
4985 m_mainPhyId = 0;
4988 m_nEmlsrStations = 1;
4990
4991 // channel switch delay will be also set to 64 us
4994 m_establishBaDl = true;
4995 m_establishBaUl = true;
4996 m_duration = Seconds(0.5);
4997}
4998
4999void
5001{
5002 Config::SetDefault("ns3::WifiPhy::ChannelSwitchDelay", TimeValue(MicroSeconds(64)));
5003 Config::SetDefault("ns3::DefaultEmlsrManager::SwitchAuxPhy", BooleanValue(m_switchAuxPhy));
5004 Config::SetDefault("ns3::EmlsrManager::AuxPhyTxCapable", BooleanValue(m_auxPhyTxCapable));
5005
5007}
5008
5009void
5011 uint8_t phyId,
5012 WifiConstPsduMap psduMap,
5013 WifiTxVector txVector,
5014 double txPowerW)
5015{
5016 EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
5017
5018 const auto psdu = psduMap.cbegin()->second;
5019 const auto& hdr = psdu->GetHeader(0);
5020
5021 // nothing to do in case of Beacon and CF-End frames
5022 if (hdr.IsBeacon() || hdr.IsCfEnd())
5023 {
5024 return;
5025 }
5026
5027 auto linkId = mac->GetLinkForPhy(phyId);
5028 NS_TEST_ASSERT_MSG_EQ(linkId.has_value(),
5029 true,
5030 "PHY " << +phyId << " is not operating on any link");
5031 NS_TEST_EXPECT_MSG_EQ(+linkId.value(), 0, "TX occurred on unexpected link " << +linkId.value());
5032
5033 if (m_eventIt != m_events.cend())
5034 {
5035 // check that the expected frame is being transmitted
5037 hdr.GetType(),
5038 "Unexpected MAC header type for frame #"
5039 << std::distance(m_events.cbegin(), m_eventIt));
5040 // perform actions/checks, if any
5041 if (m_eventIt->func)
5042 {
5043 m_eventIt->func(psdu, txVector);
5044 }
5045
5046 ++m_eventIt;
5047 }
5048}
5049
5050void
5052{
5053 // lambda to check that AP MLD started the transition delay timer after the TX/RX of given frame
5054 auto checkTransDelay = [this](Ptr<const WifiPsdu> psdu,
5055 const WifiTxVector& txVector,
5056 bool testUnblockedForOtherReasons,
5057 const std::string& frameStr) {
5058 const auto txDuration = WifiPhy::CalculateTxDuration(psdu->GetSize(),
5059 txVector,
5060 m_apMac->GetWifiPhy(0)->GetPhyBand());
5061 Simulator::Schedule(txDuration + MicroSeconds(1), /* to account for propagation delay */
5063 this,
5064 m_apMac,
5065 m_staMacs[0]->GetAddress(),
5066 0,
5067 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
5068 true,
5069 "Checking that AP MLD blocked transmissions to single link EMLSR "
5070 "client after " +
5071 frameStr,
5072 testUnblockedForOtherReasons);
5073 };
5074
5075 // expected sequence of transmitted frames
5077 m_events.emplace_back(WIFI_MAC_CTL_ACK);
5079 m_events.emplace_back(WIFI_MAC_CTL_ACK);
5080
5081 // EML OMN sent by EMLSR client
5082 m_events.emplace_back(WIFI_MAC_MGT_ACTION,
5083 [=, this](Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) {
5084 // check that the address of the EMLSR client is seen as an MLD
5085 // address
5087 m_apMac->GetWifiRemoteStationManager(0)
5088 ->GetMldAddress(m_staMacs[0]->GetAddress())
5089 .has_value(),
5090 true,
5091 "Expected the EMLSR client address to be seen as an MLD address");
5092 });
5093 m_events.emplace_back(WIFI_MAC_CTL_ACK);
5094 // EML OMN sent by AP MLD, protected by ICF
5095 m_events.emplace_back(WIFI_MAC_CTL_TRIGGER);
5096 m_events.emplace_back(WIFI_MAC_CTL_CTS);
5097 m_events.emplace_back(WIFI_MAC_MGT_ACTION);
5098 m_events.emplace_back(WIFI_MAC_CTL_ACK,
5099 [=, this](Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) {
5100 // check that EMLSR mode has been enabled on link 0 of EMLSR client
5102 m_staMacs[0]->IsEmlsrLink(0),
5103 true,
5104 "Expected EMLSR mode to be enabled on link 0 of EMLSR client");
5105 });
5106
5107 // Establishment of BA agreement for downlink direction
5108
5109 // ADDBA REQUEST sent by AP MLD (protected by ICF)
5110 m_events.emplace_back(WIFI_MAC_CTL_TRIGGER);
5111 m_events.emplace_back(WIFI_MAC_CTL_CTS);
5112 m_events.emplace_back(WIFI_MAC_MGT_ACTION);
5113 m_events.emplace_back(WIFI_MAC_CTL_ACK,
5114 [=](Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) {
5115 // check that transition delay is started after reception of Ack
5116 checkTransDelay(psdu, txVector, false, "DL ADDBA REQUEST");
5117 });
5118
5119 // ADDBA RESPONSE sent by EMLSR client (no RTS because it is sent by main PHY)
5120 m_events.emplace_back(WIFI_MAC_MGT_ACTION);
5121 m_events.emplace_back(WIFI_MAC_CTL_ACK,
5122 [=](Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) {
5123 // check that transition delay is started after reception of Ack
5124 checkTransDelay(psdu, txVector, true, "DL ADDBA RESPONSE");
5125 });
5126
5127 // Downlink QoS data frame that triggered BA agreement establishment
5128 m_events.emplace_back(WIFI_MAC_CTL_TRIGGER);
5129 m_events.emplace_back(WIFI_MAC_CTL_CTS);
5130 m_events.emplace_back(WIFI_MAC_QOSDATA);
5131 m_events.emplace_back(WIFI_MAC_CTL_BACKRESP,
5132 [=](Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) {
5133 // check that transition delay is started after reception of BlockAck
5134 checkTransDelay(psdu, txVector, true, "DL QoS Data");
5135 });
5136
5137 // Establishment of BA agreement for uplink direction
5138
5139 // ADDBA REQUEST sent by EMLSR client (no RTS because it is sent by main PHY)
5140 m_events.emplace_back(WIFI_MAC_MGT_ACTION);
5141 m_events.emplace_back(WIFI_MAC_CTL_ACK,
5142 [=](Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) {
5143 // check that transition delay is started after reception of Ack
5144 checkTransDelay(psdu, txVector, false, "UL ADDBA REQUEST");
5145 });
5146 // ADDBA RESPONSE sent by AP MLD (protected by ICF)
5147 m_events.emplace_back(WIFI_MAC_CTL_TRIGGER);
5148 m_events.emplace_back(WIFI_MAC_CTL_CTS);
5149 m_events.emplace_back(WIFI_MAC_MGT_ACTION);
5150 m_events.emplace_back(WIFI_MAC_CTL_ACK,
5151 [=](Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) {
5152 // check that transition delay is started after reception of Ack
5153 checkTransDelay(psdu, txVector, true, "UL ADDBA RESPONSE");
5154 });
5155
5156 // Uplink QoS data frame that triggered BA agreement establishment
5157 m_events.emplace_back(WIFI_MAC_QOSDATA);
5158 m_events.emplace_back(WIFI_MAC_CTL_BACKRESP,
5159 [=](Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) {
5160 // check that transition delay is started after reception of BlockAck
5161 checkTransDelay(psdu, txVector, true, "UL QoS Data");
5162 });
5163
5164 m_eventIt = m_events.cbegin();
5165
5168
5169 NS_TEST_EXPECT_MSG_EQ((m_eventIt == m_events.cend()), true, "Not all events took place");
5170
5172}
5173
5175 : TestSuite("wifi-emlsr", Type::UNIT)
5176{
5177 AddTestCase(new EmlOperatingModeNotificationTest(), TestCase::Duration::QUICK);
5178 AddTestCase(new EmlOmnExchangeTest({1, 2}, MicroSeconds(0)), TestCase::Duration::QUICK);
5179 AddTestCase(new EmlOmnExchangeTest({1, 2}, MicroSeconds(2048)), TestCase::Duration::QUICK);
5180 AddTestCase(new EmlOmnExchangeTest({0, 1, 2, 3}, MicroSeconds(0)), TestCase::Duration::QUICK);
5181 AddTestCase(new EmlOmnExchangeTest({0, 1, 2, 3}, MicroSeconds(2048)),
5182 TestCase::Duration::QUICK);
5183 for (const auto& emlsrLinks :
5184 {std::set<uint8_t>{0, 1, 2}, std::set<uint8_t>{1, 2}, std::set<uint8_t>{0, 1}})
5185 {
5187 0,
5188 emlsrLinks,
5189 {MicroSeconds(32)},
5190 {MicroSeconds(32)},
5191 MicroSeconds(512),
5192 true /* putAuxPhyToSleep */}),
5193 TestCase::Duration::QUICK);
5195 1,
5196 emlsrLinks,
5197 {MicroSeconds(64)},
5198 {MicroSeconds(64)},
5199 MicroSeconds(512),
5200 false /* putAuxPhyToSleep */}),
5201 TestCase::Duration::QUICK);
5203 2,
5204 emlsrLinks,
5205 {MicroSeconds(128), MicroSeconds(256)},
5206 {MicroSeconds(128), MicroSeconds(256)},
5207 MicroSeconds(512),
5208 true /* putAuxPhyToSleep */}),
5209 TestCase::Duration::QUICK);
5210 }
5211
5212 for (auto genBackoffAndUseAuxPhyCca : {true, false})
5213 {
5214 for (auto nSlotsLeft : std::initializer_list<uint8_t>{0, 4})
5215 {
5216 AddTestCase(new EmlsrUlTxopTest({{0, 1, 2},
5217 MHz_u{40},
5218 MHz_u{20},
5219 MicroSeconds(5504),
5220 3,
5221 genBackoffAndUseAuxPhyCca,
5222 nSlotsLeft,
5223 true, /* putAuxPhyToSleep */
5224 false /* switchMainPhyBackDelayTimeout */}),
5225 TestCase::Duration::QUICK);
5226 AddTestCase(new EmlsrUlTxopTest({{0, 1},
5227 MHz_u{40},
5228 MHz_u{20},
5229 MicroSeconds(5504),
5230 1,
5231 genBackoffAndUseAuxPhyCca,
5232 nSlotsLeft,
5233 false, /* putAuxPhyToSleep */
5234 true /* switchMainPhyBackDelayTimeout */}),
5235 TestCase::Duration::QUICK);
5236 }
5237 }
5238
5239 for (bool switchAuxPhy : {true, false})
5240 {
5241 for (bool resetCamStateAndInterruptSwitch : {true, false})
5242 {
5243 for (auto auxPhyMaxChWidth : {MHz_u{20}, MHz_u{40}, MHz_u{80}, MHz_u{160}})
5244 {
5246 {switchAuxPhy, resetCamStateAndInterruptSwitch, auxPhyMaxChWidth}),
5247 TestCase::Duration::QUICK);
5248 }
5249 }
5250 }
5251
5252 AddTestCase(new EmlsrUlOfdmaTest(false), TestCase::Duration::QUICK);
5253 AddTestCase(new EmlsrUlOfdmaTest(true), TestCase::Duration::QUICK);
5254
5255 AddTestCase(new EmlsrCcaBusyTest(MHz_u{20}), TestCase::Duration::QUICK);
5256 AddTestCase(new EmlsrCcaBusyTest(MHz_u{80}), TestCase::Duration::QUICK);
5257
5258 for (const auto switchAuxPhy : {true, false})
5259 {
5260 for (const auto auxPhyTxCapable : {true, false})
5261 {
5262 AddTestCase(new SingleLinkEmlsrTest(switchAuxPhy, auxPhyTxCapable),
5263 TestCase::Duration::QUICK);
5264 }
5265 }
5266}
5267
5268static 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)
MHz_u m_auxPhyMaxChWidth
max channel width supported by aux PHYs
void TransmitPacketToAp(uint8_t linkId)
Make the other MLD transmit a packet to the AP on the given link.
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.
EmlsrCcaBusyTest(MHz_u auxPhyMaxChWidth)
Constructor.
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.
void MainPhySwitchInfoCallback(std::size_t index, const EmlsrMainPhySwitchTrace &info)
Callback connected to the EMLSR Manager MainPhySwitch trace source.
const std::array< std::string, 3 > m_channelsStr
array of strings defining the channels for the MLD links
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.
bool m_putAuxPhyToSleep
whether aux PHYs are put to sleep during DL/UL TXOPs
TrafficDirection
Enumeration for traffic directions.
void CheckMsdTimerRunning(Ptr< StaWifiMac > staMac, uint8_t linkId, bool isRunning, const std::string &msg)
Check whether the MediumSyncDelay timer is running on the given link of the given device.
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.
void CheckMainPhyTraceInfo(std::size_t index, std::string_view reason, const std::optional< uint8_t > &fromLinkId, uint8_t toLinkId, bool checkFromLinkId=true, bool checkToLinkId=true)
Check information provided by the EMLSR Manager MainPhySwitch trace.
std::map< std::size_t, std::shared_ptr< EmlsrMainPhySwitchTrace > > m_traceInfo
EMLSR client ID-indexed map of trace info from last main PHY switch.
Time m_transitionTimeout
Transition Timeout advertised by the AP MLD.
const std::array< FrequencyRange, 3 > m_freqRanges
array of frequency ranges for MLD links
std::vector< PacketSocketAddress > m_ulSockets
packet socket address for UL traffic
void CheckAuxPhysSleepMode(Ptr< StaWifiMac > staMac, bool sleep)
Check whether aux PHYs of the given device are in sleep mode/awake.
std::vector< Ptr< StaWifiMac > > m_staMacs
MACs of the non-AP MLDs.
std::size_t m_nPhysPerEmlsrDevice
number of PHYs per EMLSR client
virtual void StartTraffic()
Start the generation of traffic (needs to be overridden)
EmlsrOperationsTestBase(const std::string &name)
Constructor.
Check UL OFDMA operations with EMLSR clients.
Time m_startAccessReq
start time of the first AP MLD access request via MU scheduler
void DoRun() override
Implementation to actually run this TestCase.
void CheckResults()
Check that the simulation produced the expected results.
EmlsrUlOfdmaTest(bool enableBsrp)
Constructor.
void StartTraffic() override
Start the generation of traffic (needs to be overridden)
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 DoSetup() override
Implementation to do any local setup required for this TestCase.
bool m_enableBsrp
whether MU scheduler sends BSRP TFs
std::size_t m_txPsdusPos
position in the vector of TX PSDUs of the first ICF
Test the transmission of UL frames from EMLSR clients.
std::size_t m_countQoSframes
counter for QoS frames
const Time m_unblockMainPhyLinkDelay
delay between the time the first two UL packets are generated and the time transmissions are unblocke...
Ptr< ListErrorModel > m_errorModel
error rate model to corrupt packets
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_5thQosFrameExpWidth
expected width 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.
bool m_switchMainPhyBackDelayTimeout
whether a SwitchMainPhyBackDelay timer expires after that the main PHY moved to an aux PHY link
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.
uint8_t m_nSlotsLeftAlert
value for ChannelAccessManager NSlotsLeft attribute
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.
AttributeValue implementation for Boolean.
Definition boolean.h:26
Headers for BlockAck response.
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.
TriggerFrameType GetType() const
Get the Trigger Frame type.
std::size_t GetNUserInfoFields() const
Get the number of User Info fields in this Trigger Frame.
Class for representing data rates.
Definition data-rate.h:78
Time CalculateBytesTxTime(uint32_t bytes) const
Calculate transmission time.
Definition data-rate.cc:220
static constexpr uint16_t MEDIUM_SYNC_THRESHOLD_USEC
The aMediumSyncThreshold defined by Sec. 35.3.16.18.1 of 802.11be D4.0.
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.
MultiUserScheduler is an abstract base class defining the API that APs supporting at least VHT can us...
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:561
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:595
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
AttributeValue implementation for Ssid.
Definition ssid.h:85
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:403
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
static Time Max()
Maximum representable Time Not to be confused with Max(Time,Time).
Definition nstime.h:286
bool IsZero() const
Exactly equivalent to t == 0.
Definition nstime.h:304
int64_t GetTimeStep() const
Get the raw time value, in the current resolution unit.
Definition nstime.h:434
AttributeValue implementation for Time.
Definition nstime.h:1432
Time Get() const
Definition time.cc:518
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:110
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:940
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition wifi-phy.cc:1588
static Time CalculatePhyPreambleAndHeaderDuration(const WifiTxVector &txVector)
Definition wifi-phy.cc:1581
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:886
void ConnectWithoutContext(std::string path, const CallbackBase &cb)
Definition config.cc:946
#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(actual, limit, msg)
Test that an actual value is greater than a limit and report and abort if not.
Definition test.h:864
#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:1369
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1381
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1345
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1357
WifiMacDropReason
The reason why an MPDU was dropped.
Definition wifi-mac.h:71
WifiQueueBlockedReason
Enumeration of the reasons to block container queues.
TriggerFrameType
The different Trigger frame types.
@ 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.
U * PeekPointer(const Ptr< U > &p)
Definition ptr.h:443
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
std::size_t Count20MHzSubchannels(MHz_u channelWidth)
Return the number of 20 MHz subchannels covering the channel width.
Definition wifi-utils.h:135
@ WIFI_MAC_CTL_TRIGGER
@ WIFI_MAC_CTL_RTS
@ WIFI_MAC_CTL_CTS
@ WIFI_MAC_MGT_ACTION
@ WIFI_MAC_MGT_ASSOCIATION_RESPONSE
@ WIFI_MAC_CTL_ACK
@ WIFI_MAC_MGT_ASSOCIATION_REQUEST
@ WIFI_MAC_CTL_BACKRESP
@ WIFI_MAC_CTL_END
@ WIFI_MAC_QOSDATA
Ptr< T1 > StaticCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:587
std::unordered_map< uint16_t, Ptr< const WifiPsdu > > WifiConstPsduMap
Map of const PSDUs indexed by STA-ID.
Definition wifi-ppdu.h:38
uint32_t GetAckSize()
Return the total Ack size (including FCS trailer).
Definition wifi-utils.cc:52
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
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)
Struct to trace that main PHY switched to start a DL TXOP after that an aux PHY received an ICF.
Base struct for EMLSR Main PHY switch traces.
virtual std::shared_ptr< EmlsrMainPhySwitchTrace > Clone() const =0
Struct to trace that main PHY switched when a (DL or UL) TXOP ended.
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