A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
wifi-emlsr-test.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2023 Universita' degli Studi di Napoli Federico II
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation;
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 *
17 * Author: Stefano Avallone <stavallo@unina.it>
18 */
19
20#include "ns3/ap-wifi-mac.h"
21#include "ns3/attribute-container.h"
22#include "ns3/boolean.h"
23#include "ns3/config.h"
24#include "ns3/ctrl-headers.h"
25#include "ns3/eht-configuration.h"
26#include "ns3/emlsr-manager.h"
27#include "ns3/he-frame-exchange-manager.h"
28#include "ns3/header-serialization-test.h"
29#include "ns3/log.h"
30#include "ns3/mgt-headers.h"
31#include "ns3/mobility-helper.h"
32#include "ns3/multi-model-spectrum-channel.h"
33#include "ns3/node-list.h"
34#include "ns3/packet-socket-client.h"
35#include "ns3/packet-socket-helper.h"
36#include "ns3/packet-socket-server.h"
37#include "ns3/qos-txop.h"
38#include "ns3/rng-seed-manager.h"
39#include "ns3/rr-multi-user-scheduler.h"
40#include "ns3/simulator.h"
41#include "ns3/spectrum-wifi-helper.h"
42#include "ns3/spectrum-wifi-phy.h"
43#include "ns3/sta-wifi-mac.h"
44#include "ns3/string.h"
45#include "ns3/wifi-net-device.h"
46#include "ns3/wifi-ppdu.h"
47#include "ns3/wifi-psdu.h"
48
49#include <algorithm>
50#include <functional>
51#include <iomanip>
52#include <optional>
53
54using namespace ns3;
55
56NS_LOG_COMPONENT_DEFINE("WifiEmlsrTest");
57
58static uint32_t
59ContextToNodeId(std::string context)
60{
61 std::string sub = context.substr(10);
62 uint32_t pos = sub.find("/Device");
63 uint32_t nodeId = std::stoi(sub.substr(0, pos));
64 return nodeId;
65}
66
74{
75 public:
81
82 private:
83 void DoRun() override;
84};
85
88 "Check serialization and deserialization of the EML Operating Mode Notification frame")
89{
90}
91
92void
94{
96
97 // Both EMLSR Mode and EMLMR Mode subfields set to 0 (no link bitmap);
99
100 frame.m_emlControl.emlsrMode = 1;
101 frame.SetLinkIdInBitmap(0);
102 frame.SetLinkIdInBitmap(5);
103 frame.SetLinkIdInBitmap(15);
104
105 // Adding Link Bitmap
107
108 NS_TEST_EXPECT_MSG_EQ((frame.GetLinkBitmap() == std::list<uint8_t>{0, 5, 15}),
109 true,
110 "Unexpected link bitmap");
111
112 auto padding = MicroSeconds(64);
113 auto transition = MicroSeconds(128);
114
118 frame.m_emlsrParamUpdate->transitionDelay =
120
121 // Adding the EMLSR Parameter Update field
123
126 padding,
127 "Unexpected EMLSR Padding Delay");
130 transition,
131 "Unexpected EMLSR Transition Delay");
132}
133
150{
151 public:
157 EmlsrOperationsTestBase(const std::string& name);
158 ~EmlsrOperationsTestBase() override = default;
159
161 enum TrafficDirection : uint8_t
162 {
164 UPLINK
165 };
166
167 protected:
177 virtual void Transmit(uint8_t linkId,
178 std::string context,
179 WifiConstPsduMap psduMap,
180 WifiTxVector txVector,
181 double txPowerW);
182
192 std::size_t staId,
193 std::size_t count,
194 std::size_t pktSize) const;
195
196 void DoSetup() override;
197
200 {
204 uint8_t linkId;
205 };
206
207 std::set<uint8_t> m_linksToEnableEmlsrOn;
209 std::size_t m_nEmlsrStations{1};
210 std::size_t m_nNonEmlsrStations{0};
213 std::vector<Time> m_paddingDelay{
214 {MicroSeconds(32)}};
215 std::vector<Time> m_transitionDelay{
216 {MicroSeconds(16)}};
217 bool m_establishBaDl{false};
219 bool m_establishBaUl{false};
221 std::vector<FrameInfo> m_txPsdus;
223 std::vector<Ptr<StaWifiMac>> m_staMacs;
224 std::vector<PacketSocketAddress> m_dlSockets;
225 std::vector<PacketSocketAddress> m_ulSockets;
226 uint16_t m_lastAid{0};
228
229 private:
237 void SetSsid(uint16_t aid, Mac48Address /* addr */);
238
242 virtual void StartTraffic()
243 {
244 }
245};
246
248 : TestCase(name)
249{
250}
251
252void
254 std::string context,
255 WifiConstPsduMap psduMap,
256 WifiTxVector txVector,
257 double txPowerW)
258{
259 m_txPsdus.push_back({Simulator::Now(), psduMap, txVector, linkId});
260 auto txDuration =
261 WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
262
263 for (const auto& [aid, psdu] : psduMap)
264 {
265 std::stringstream ss;
266 ss << std::setprecision(10) << "PSDU #" << m_txPsdus.size() << " Link ID " << +linkId << " "
267 << psdu->GetHeader(0).GetTypeString();
268 if (psdu->GetHeader(0).IsAction())
269 {
270 ss << " ";
271 WifiActionHeader actionHdr;
272 psdu->GetPayload(0)->PeekHeader(actionHdr);
273 actionHdr.Print(ss);
274 }
275 ss << " #MPDUs " << psdu->GetNMpdus() << " duration/ID " << psdu->GetHeader(0).GetDuration()
276 << " RA = " << psdu->GetAddr1() << " TA = " << psdu->GetAddr2()
277 << " ADDR3 = " << psdu->GetHeader(0).GetAddr3()
278 << " ToDS = " << psdu->GetHeader(0).IsToDs()
279 << " FromDS = " << psdu->GetHeader(0).IsFromDs();
280 if (psdu->GetHeader(0).IsQosData())
281 {
282 ss << " seqNo = {";
283 for (auto& mpdu : *PeekPointer(psdu))
284 {
285 ss << mpdu->GetHeader().GetSequenceNumber() << ",";
286 }
287 ss << "} TID = " << +psdu->GetHeader(0).GetQosTid();
288 }
289 NS_LOG_INFO(ss.str());
290 }
291 NS_LOG_INFO("TX duration = " << txDuration.As(Time::MS) << " TXVECTOR = " << txVector << "\n");
292}
293
294void
296{
299 int64_t streamNumber = 100;
300
301 NodeContainer wifiApNode(1);
302 NodeContainer wifiStaNodes(m_nEmlsrStations);
303
304 WifiHelper wifi;
305 // wifi.EnableLogComponents ();
306 wifi.SetStandard(WIFI_STANDARD_80211be);
307 wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
308 "DataMode",
309 StringValue("EhtMcs0"),
310 "ControlMode",
311 StringValue("HtMcs0"));
312 wifi.ConfigEhtOptions("EmlsrActivated",
313 BooleanValue(true),
314 "TransitionTimeout",
316
317 // MLDs are configured with three links
318 SpectrumWifiPhyHelper phyHelper(3);
320 phyHelper.Set(0, "ChannelSettings", StringValue("{2, 0, BAND_2_4GHZ, 0}"));
321 phyHelper.Set(1, "ChannelSettings", StringValue("{36, 0, BAND_5GHZ, 0}"));
322 phyHelper.Set(2, "ChannelSettings", StringValue("{1, 0, BAND_6GHZ, 0}"));
323 // Add three spectrum channels to use multi-RF interface
324 phyHelper.AddChannel(CreateObject<MultiModelSpectrumChannel>(), WIFI_SPECTRUM_2_4_GHZ);
325 phyHelper.AddChannel(CreateObject<MultiModelSpectrumChannel>(), WIFI_SPECTRUM_5_GHZ);
326 phyHelper.AddChannel(CreateObject<MultiModelSpectrumChannel>(), WIFI_SPECTRUM_6_GHZ);
327
328 WifiMacHelper mac;
329 mac.SetType("ns3::ApWifiMac",
330 "Ssid",
331 SsidValue(Ssid("ns-3-ssid")),
332 "BeaconGeneration",
333 BooleanValue(true));
334
335 NetDeviceContainer apDevice = wifi.Install(phyHelper, mac, wifiApNode);
336
337 mac.SetType("ns3::StaWifiMac",
338 "Ssid",
339 SsidValue(Ssid("wrong-ssid")),
340 "ActiveProbing",
341 BooleanValue(false));
342 mac.SetEmlsrManager("ns3::DefaultEmlsrManager",
343 "EmlsrLinkSet",
345
346 NetDeviceContainer staDevices = wifi.Install(phyHelper, mac, wifiStaNodes);
347
348 m_apMac = DynamicCast<ApWifiMac>(DynamicCast<WifiNetDevice>(apDevice.Get(0))->GetMac());
349
350 for (uint32_t i = 0; i < staDevices.GetN(); i++)
351 {
352 auto device = DynamicCast<WifiNetDevice>(staDevices.Get(i));
353 auto staMac = DynamicCast<StaWifiMac>(device->GetMac());
354 NS_ASSERT_MSG(i < m_paddingDelay.size(), "Not enough padding delay values provided");
355 staMac->GetEmlsrManager()->SetAttribute("EmlsrPaddingDelay",
357 NS_ASSERT_MSG(i < m_transitionDelay.size(), "Not enough transition delay values provided");
358 staMac->GetEmlsrManager()->SetAttribute("EmlsrTransitionDelay",
360 }
361
362 if (m_nNonEmlsrStations > 0)
363 {
364 // create the other non-AP MLDs for which EMLSR is not activated
365 wifi.ConfigEhtOptions("EmlsrActivated", BooleanValue(false));
366 NodeContainer otherStaNodes(m_nNonEmlsrStations);
367 staDevices.Add(wifi.Install(phyHelper, mac, otherStaNodes));
368 wifiStaNodes.Add(otherStaNodes);
369 }
370
371 for (uint32_t i = 0; i < staDevices.GetN(); i++)
372 {
373 auto device = DynamicCast<WifiNetDevice>(staDevices.Get(i));
374 m_staMacs.push_back(DynamicCast<StaWifiMac>(device->GetMac()));
375 }
376
377 // Trace PSDUs passed to the PHY on AP MLD and non-AP MLDs
378 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
379 {
380 Config::Connect("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phys/" +
381 std::to_string(linkId) + "/PhyTxPsduBegin",
383 }
384
385 // Uncomment the lines below to write PCAP files
386 // phyHelper.EnablePcap("wifi-emlsr_AP", apDevice);
387 // phyHelper.EnablePcap("wifi-emlsr_STA", staDevices);
388
389 // Assign fixed streams to random variables in use
390 streamNumber += wifi.AssignStreams(apDevice, streamNumber);
391 streamNumber += wifi.AssignStreams(staDevices, streamNumber);
392
393 MobilityHelper mobility;
394 Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();
395
396 for (std::size_t id = 0; id <= m_nEmlsrStations + m_nNonEmlsrStations; id++)
397 {
398 // all non-AP MLDs are co-located
399 positionAlloc->Add(Vector(std::min<double>(id, 1), 0.0, 0.0));
400 }
401 mobility.SetPositionAllocator(positionAlloc);
402
403 mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
404 mobility.Install(wifiApNode);
405 mobility.Install(wifiStaNodes);
406
407 // install packet socket on all nodes
408 PacketSocketHelper packetSocket;
409 packetSocket.Install(wifiApNode);
410 packetSocket.Install(wifiStaNodes);
411
412 // install a packet socket server on all nodes
413 for (auto nodeIt = NodeList::Begin(); nodeIt != NodeList::End(); nodeIt++)
414 {
415 PacketSocketAddress srvAddr;
416 auto device = DynamicCast<WifiNetDevice>((*nodeIt)->GetDevice(0));
417 NS_TEST_ASSERT_MSG_NE(device, nullptr, "Expected a WifiNetDevice");
418 srvAddr.SetSingleDevice(device->GetIfIndex());
419 srvAddr.SetProtocol(1);
420
421 auto server = CreateObject<PacketSocketServer>();
422 server->SetLocal(srvAddr);
423 (*nodeIt)->AddApplication(server);
424 server->SetStartTime(Seconds(0)); // now
425 server->SetStopTime(m_duration);
426 }
427
428 // set DL and UL packet sockets
429 for (const auto& staMac : m_staMacs)
430 {
431 m_dlSockets.emplace_back();
432 m_dlSockets.back().SetSingleDevice(m_apMac->GetDevice()->GetIfIndex());
433 m_dlSockets.back().SetPhysicalAddress(staMac->GetDevice()->GetAddress());
434 m_dlSockets.back().SetProtocol(1);
435
436 m_ulSockets.emplace_back();
437 m_ulSockets.back().SetSingleDevice(staMac->GetDevice()->GetIfIndex());
438 m_ulSockets.back().SetPhysicalAddress(m_apMac->GetDevice()->GetAddress());
439 m_ulSockets.back().SetProtocol(1);
440 }
441
442 // schedule ML setup for one station at a time
443 m_apMac->TraceConnectWithoutContext("AssociatedSta",
445 Simulator::Schedule(Seconds(0), [&]() { m_staMacs[0]->SetSsid(Ssid("ns-3-ssid")); });
446}
447
450 std::size_t staId,
451 std::size_t count,
452 std::size_t pktSize) const
453{
454 auto client = CreateObject<PacketSocketClient>();
455 client->SetAttribute("PacketSize", UintegerValue(pktSize));
456 client->SetAttribute("MaxPackets", UintegerValue(count));
457 client->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
458 client->SetRemote(dir == DOWNLINK ? m_dlSockets.at(staId) : m_ulSockets.at(staId));
459 client->SetStartTime(Seconds(0)); // now
460 client->SetStopTime(m_duration - Simulator::Now());
461
462 return client;
463}
464
465void
467{
468 if (m_lastAid == aid)
469 {
470 // another STA of this non-AP MLD has already fired this callback
471 return;
472 }
473 m_lastAid = aid;
474
475 // wait some time (5ms) to allow the completion of association
476 auto delay = MilliSeconds(5);
477
478 if (m_establishBaDl)
479 {
480 // trigger establishment of BA agreement with AP as originator
481 Simulator::Schedule(delay, [=]() {
482 m_apMac->GetDevice()->GetNode()->AddApplication(
483 GetApplication(DOWNLINK, aid - 1, 4, 1000));
484 });
485
486 delay += MilliSeconds(5);
487 }
488
489 if (m_establishBaUl)
490 {
491 // trigger establishment of BA agreement with AP as recipient
492 Simulator::Schedule(delay, [=]() {
493 m_staMacs[aid - 1]->GetDevice()->GetNode()->AddApplication(
494 GetApplication(UPLINK, aid - 1, 4, 1000));
495 });
496
497 delay += MilliSeconds(5);
498 }
499
500 Simulator::Schedule(delay, [=]() {
502 {
503 // make the next STA start ML discovery & setup
504 m_staMacs[aid]->SetSsid(Ssid("ns-3-ssid"));
505 return;
506 }
507 // all stations associated; start traffic if needed
508 StartTraffic();
509 });
510}
511
535{
536 public:
543 EmlNotificationExchangeTest(const std::set<uint8_t>& linksToEnableEmlsrOn,
544 Time transitionTimeout);
545 ~EmlNotificationExchangeTest() override = default;
546
547 protected:
548 void DoSetup() override;
549 void DoRun() override;
550 void Transmit(uint8_t linkId,
551 std::string context,
552 WifiConstPsduMap psduMap,
553 WifiTxVector txVector,
554 double txPowerW) override;
555
561 void TxOk(Ptr<const WifiMpdu> mpdu);
569
579 const WifiTxVector& txVector,
580 uint8_t linkId);
590 const WifiTxVector& txVector,
591 uint8_t linkId);
600 const WifiTxVector& txVector,
601 uint8_t linkId);
605 void CheckEmlsrLinks();
606
607 private:
608 std::optional<uint8_t> m_assocLinkId;
617 std::list<uint64_t> m_uidList;
618};
619
621 const std::set<uint8_t>& linksToEnableEmlsrOn,
622 Time transitionTimeout)
623 : EmlsrOperationsTestBase("Check EML Notification exchange"),
624 m_checkEmlsrLinksCount(0),
625 m_emlNotificationDroppedCount(0)
626{
627 m_linksToEnableEmlsrOn = linksToEnableEmlsrOn;
630 m_transitionTimeout = transitionTimeout;
631 m_duration = Seconds(0.5);
632}
633
634void
636{
638
639 m_errorModel = CreateObject<ListErrorModel>();
640 for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
641 {
643 }
644
645 m_staMacs[0]->TraceConnectWithoutContext(
646 "AckedMpdu",
648 m_staMacs[0]->TraceConnectWithoutContext(
649 "DroppedMpdu",
651}
652
653void
655 std::string context,
656 WifiConstPsduMap psduMap,
657 WifiTxVector txVector,
658 double txPowerW)
659{
660 auto psdu = psduMap.begin()->second;
661
662 switch (psdu->GetHeader(0).GetType())
663 {
665 m_assocLinkId = linkId;
666 CheckEmlCapabilitiesInAssocReq(*psdu->begin(), txVector, linkId);
667 break;
668
670 CheckEmlCapabilitiesInAssocResp(*psdu->begin(), txVector, linkId);
671 break;
672
674 if (auto [category, action] = WifiActionHeader::Peek(psdu->GetPayload(0));
676 action.protectedEhtAction ==
678 {
679 CheckEmlNotification(psdu, txVector, linkId);
680
682 m_staMacs[0]->GetLinkIdByAddress(psdu->GetAddr2()) == linkId)
683 {
684 // transmitted by non-AP MLD, we need to corrupt it
685 m_uidList.push_front(psdu->GetPacket()->GetUid());
687 }
688 break;
689 }
690
691 default:;
692 }
693
694 EmlsrOperationsTestBase::Transmit(linkId, context, psduMap, txVector, txPowerW);
695}
696
697void
699 const WifiTxVector& txVector,
700 uint8_t linkId)
701{
703 mpdu->GetPacket()->PeekHeader(frame);
704
705 const auto& mle = frame.Get<MultiLinkElement>();
706 NS_TEST_ASSERT_MSG_EQ(mle.has_value(), true, "Multi-Link Element must be present in AssocReq");
707
708 NS_TEST_ASSERT_MSG_EQ(mle->HasEmlCapabilities(),
709 true,
710 "Multi-Link Element in AssocReq must have EML Capabilities");
711 NS_TEST_ASSERT_MSG_EQ(mle->IsEmlsrSupported(),
712 true,
713 "EML Support subfield of EML Capabilities in AssocReq must be set to 1");
714 NS_TEST_ASSERT_MSG_EQ(mle->GetEmlsrPaddingDelay(),
715 m_paddingDelay.at(0),
716 "Unexpected Padding Delay in EML Capabilities included in AssocReq");
717 NS_TEST_ASSERT_MSG_EQ(mle->GetEmlsrTransitionDelay(),
718 m_transitionDelay.at(0),
719 "Unexpected Transition Delay in EML Capabilities included in AssocReq");
720}
721
722void
724 const WifiTxVector& txVector,
725 uint8_t linkId)
726{
727 bool sentToEmlsrClient =
728 (m_staMacs[0]->GetLinkIdByAddress(mpdu->GetHeader().GetAddr1()) == linkId);
729
730 if (!sentToEmlsrClient)
731 {
732 // nothing to check
733 return;
734 }
735
737 mpdu->GetPacket()->PeekHeader(frame);
738
739 const auto& mle = frame.Get<MultiLinkElement>();
740 NS_TEST_ASSERT_MSG_EQ(mle.has_value(), true, "Multi-Link Element must be present in AssocResp");
741
742 NS_TEST_ASSERT_MSG_EQ(mle->HasEmlCapabilities(),
743 true,
744 "Multi-Link Element in AssocResp must have EML Capabilities");
745 NS_TEST_ASSERT_MSG_EQ(mle->IsEmlsrSupported(),
746 true,
747 "EML Support subfield of EML Capabilities in AssocResp must be set to 1");
749 mle->GetTransitionTimeout(),
751 "Unexpected Transition Timeout in EML Capabilities included in AssocResp");
752}
753
754void
756 const WifiTxVector& txVector,
757 uint8_t linkId)
758{
760 auto mpdu = *psdu->begin();
761 auto pkt = mpdu->GetPacket()->Copy();
763 pkt->RemoveHeader(frame);
764 NS_LOG_DEBUG(frame);
765
766 bool sentbyNonApMld = m_staMacs[0]->GetLinkIdByAddress(mpdu->GetHeader().GetAddr2()) == linkId;
767
769 1,
770 "EMLSR Mode subfield should be set to 1 (frame sent by non-AP MLD: "
771 << std::boolalpha << sentbyNonApMld << ")");
772
774 0,
775 "EMLMR Mode subfield should be set to 0 (frame sent by non-AP MLD: "
776 << std::boolalpha << sentbyNonApMld << ")");
777
779 true,
780 "Link Bitmap subfield should be present (frame sent by non-AP MLD: "
781 << std::boolalpha << sentbyNonApMld << ")");
782
783 auto setupLinks = m_staMacs[0]->GetSetupLinkIds();
784 std::list<uint8_t> expectedEmlsrLinks;
785 std::set_intersection(setupLinks.begin(),
786 setupLinks.end(),
789 std::back_inserter(expectedEmlsrLinks));
790
791 NS_TEST_EXPECT_MSG_EQ((expectedEmlsrLinks == frame.GetLinkBitmap()),
792 true,
793 "Unexpected Link Bitmap subfield (frame sent by non-AP MLD: "
794 << std::boolalpha << sentbyNonApMld << ")");
795
796 if (!sentbyNonApMld)
797 {
798 // the frame has been sent by the AP MLD
801 0,
802 "EMLSR Parameter Update Control should be set to 0 in frames sent by the AP MLD");
803
804 // as soon as the non-AP MLD receives this frame, it sets the EMLSR links
805 auto delay = WifiPhy::CalculateTxDuration(psdu,
806 txVector,
807 m_staMacs[0]->GetWifiPhy(linkId)->GetPhyBand()) +
808 MicroSeconds(1); // to account for propagation delay
810 }
811
813 true,
814 "No link ID indicating the link on which association was performed");
816 +linkId,
817 "EML Notification received on unexpected link (frame sent by non-AP MLD: "
818 << std::boolalpha << sentbyNonApMld << ")");
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 that the non-AP MLD sent has been
834 // acknowledged; after the transition timeout, the EMLSR links have been set
837 this);
838 }
839 }
840}
841
842void
844{
845 const auto& hdr = mpdu->GetHeader();
846
847 if (hdr.IsMgt() && hdr.IsAction())
848 {
849 if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
851 action.protectedEhtAction ==
853 {
854 // the EML Operating Mode Notification frame has been dropped. Don't
855 // corrupt it anymore
857 }
858 }
859}
860
861void
863{
865
866 auto setupLinks = m_staMacs[0]->GetSetupLinkIds();
867 std::set<uint8_t> expectedEmlsrLinks;
868 std::set_intersection(setupLinks.begin(),
869 setupLinks.end(),
872 std::inserter(expectedEmlsrLinks, expectedEmlsrLinks.end()));
873
874 NS_TEST_EXPECT_MSG_EQ((expectedEmlsrLinks == m_staMacs[0]->GetEmlsrManager()->GetEmlsrLinks()),
875 true,
876 "Unexpected set of EMLSR links)");
877}
878
879void
881{
884
886 2,
887 "Unexpected number of times CheckEmlsrLinks() is called");
890 1,
891 "Unexpected number of times the EML Notification frame is dropped due to max retry limit");
892
894}
895
948{
949 public:
962 EmlsrDlTxopTest(std::size_t nEmlsrStations,
963 std::size_t nNonEmlsrStations,
964 const std::set<uint8_t>& linksToEnableEmlsrOn,
965 const std::vector<Time>& paddingDelay,
966 const std::vector<Time>& transitionDelay,
967 Time transitionTimeout);
968 ~EmlsrDlTxopTest() override = default;
969
970 protected:
971 void DoSetup() override;
972 void DoRun() override;
973 void Transmit(uint8_t linkId,
974 std::string context,
975 WifiConstPsduMap psduMap,
976 WifiTxVector txVector,
977 double txPowerW) override;
978
982 void CheckResults();
983
990 void CheckPmModeAfterAssociation(const Mac48Address& address);
991
1001 const WifiTxVector& txVector,
1002 uint8_t linkId);
1003
1013 const WifiTxVector& txVector,
1014 uint8_t linkId);
1015
1024 void CheckQosFrames(const WifiConstPsduMap& psduMap,
1025 const WifiTxVector& txVector,
1026 uint8_t linkId);
1027
1036 void CheckBlockAck(const WifiConstPsduMap& psduMap,
1037 const WifiTxVector& txVector,
1038 uint8_t linkId);
1039
1040 private:
1041 void StartTraffic() override;
1042
1046 void EnableEmlsrMode();
1047
1048 std::set<uint8_t> m_emlsrLinks;
1049 std::vector<std::optional<uint8_t>>
1055 std::size_t m_countQoSframes;
1056 std::size_t m_countBlockAck;
1058};
1059
1060EmlsrDlTxopTest::EmlsrDlTxopTest(std::size_t nEmlsrStations,
1061 std::size_t nNonEmlsrStations,
1062 const std::set<uint8_t>& linksToEnableEmlsrOn,
1063 const std::vector<Time>& paddingDelay,
1064 const std::vector<Time>& transitionDelay,
1065 Time transitionTimeout)
1066 : EmlsrOperationsTestBase("Check EML DL TXOP transmissions (" + std::to_string(nEmlsrStations) +
1067 "," + std::to_string(nNonEmlsrStations) + ")"),
1068 m_emlsrLinks(linksToEnableEmlsrOn),
1069 m_assocLinkId(1 + nEmlsrStations + nNonEmlsrStations, std::nullopt),
1070 m_emlsrEnabledTime(0),
1071 m_fe2to3delay(MilliSeconds(20)),
1072 m_countQoSframes(0),
1073 m_countBlockAck(0)
1074{
1075 m_nEmlsrStations = nEmlsrStations;
1076 m_nNonEmlsrStations = nNonEmlsrStations;
1077 m_linksToEnableEmlsrOn = {}; // do not enable EMLSR right after association
1078 m_paddingDelay = paddingDelay;
1079 m_transitionDelay = transitionDelay;
1080 m_transitionTimeout = transitionTimeout;
1081 m_establishBaDl = true;
1082 m_duration = Seconds(1.5);
1083
1084 NS_ABORT_MSG_IF(linksToEnableEmlsrOn.size() < 2,
1085 "This test requires at least two links to be configured as EMLSR links");
1086}
1087
1088void
1090 std::string context,
1091 WifiConstPsduMap psduMap,
1092 WifiTxVector txVector,
1093 double txPowerW)
1094{
1095 auto psdu = psduMap.begin()->second;
1096 auto nodeId = ContextToNodeId(context);
1097
1098 switch (psdu->GetHeader(0).GetType())
1099 {
1101 NS_ASSERT_MSG(nodeId > 0, "APs do not send AssocReq frames");
1102 m_assocLinkId.at(nodeId) = linkId;
1103 if (nodeId <= m_nEmlsrStations)
1104 {
1105 // this AssocReq is being sent by an EMLSR client. The other EMLSR links should be
1106 // in powersave mode after association; we let the non-EMLSR links transition to
1107 // active mode (by sending data null frames) after association
1108 for (uint8_t id = 0; id < m_staMacs.at(nodeId - 1)->GetNLinks(); id++)
1109 {
1110 if (id != linkId && m_emlsrLinks.count(id) == 1)
1111 {
1112 m_staMacs[nodeId - 1]->SetPowerSaveMode({true, id});
1113 }
1114 }
1115 }
1116 break;
1117
1118 case WIFI_MAC_MGT_ACTION: {
1119 auto [category, action] = WifiActionHeader::Peek(psdu->GetPayload(0));
1120
1121 if (nodeId == 0 && category == WifiActionHeader::PROTECTED_EHT &&
1122 action.protectedEhtAction ==
1124 {
1125 CheckEmlNotificationFrame(*psdu->begin(), txVector, linkId);
1126 }
1127 else if (category == WifiActionHeader::BLOCK_ACK &&
1129 {
1130 CheckPmModeAfterAssociation(psdu->GetAddr1());
1131 }
1132 }
1133 break;
1134
1136 CheckInitialControlFrame(*psdu->begin(), txVector, linkId);
1137 break;
1138
1139 case WIFI_MAC_QOSDATA:
1140 CheckQosFrames(psduMap, txVector, linkId);
1141 break;
1142
1144 CheckBlockAck(psduMap, txVector, linkId);
1145 break;
1146
1147 default:;
1148 }
1149
1150 EmlsrOperationsTestBase::Transmit(linkId, context, psduMap, txVector, txPowerW);
1151}
1152
1153void
1155{
1157
1158 m_errorModel = CreateObject<ListErrorModel>();
1159 for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
1160 {
1162 }
1163
1165 {MicroSeconds(3200), MicroSeconds(3200), MicroSeconds(3200)});
1166
1168 {
1169 auto muScheduler =
1170 CreateObjectWithAttributes<RrMultiUserScheduler>("EnableUlOfdma", BooleanValue(false));
1171 m_apMac->AggregateObject(muScheduler);
1172 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
1173 {
1174 m_apMac->GetFrameExchangeManager(linkId)->GetAckManager()->SetAttribute(
1175 "DlMuAckSequenceType",
1177 }
1178 }
1179}
1180
1181void
1183{
1185 {
1186 // we are done with association and Block Ack agreement; we can now enable EMLSR mode
1187 m_lastAid = 0;
1189 return;
1190 }
1191
1192 // we are done with sending EML Operating Mode Notification frames. We can now generate
1193 // packets for all non-AP MLDs
1194 for (std::size_t i = 0; i < m_nEmlsrStations + m_nNonEmlsrStations; i++)
1195 {
1196 // when multiple non-AP MLDs are present, MU transmission are used. Given that the
1197 // available bandwidth decreases as the number of non-AP MLDs increases, compute the
1198 // number of packets to generate so that we always have two A-MPDUs per non-AP MLD
1199 std::size_t count = 8 / (m_nEmlsrStations + m_nNonEmlsrStations);
1201 }
1202
1203 // in case of 2 EMLSR clients using no non-EMLSR link, generate one additional short
1204 // packet to each EMLSR client to test transition delay
1205 if (m_nEmlsrStations == 2 && m_apMac->GetNLinks() == m_emlsrLinks.size())
1206 {
1208 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, 0, 1, 40));
1209 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, 1, 1, 40));
1210 });
1211 }
1212
1213 // schedule the transmission of EML Operating Mode Notification frames to disable EMLSR mode
1214 // and the generation of other packets destined to the EMLSR clients
1215 for (std::size_t id = 0; id < m_nEmlsrStations; id++)
1216 {
1217 Simulator::Schedule(m_fe2to3delay + MilliSeconds(5 * (id + 1)), [=]() {
1218 m_staMacs.at(id)->GetEmlsrManager()->SetAttribute(
1219 "EmlsrLinkSet",
1221 });
1222
1224 m_apMac->GetDevice()->GetNode()->AddApplication(
1226 });
1227 }
1228}
1229
1230void
1232{
1233 m_staMacs.at(m_lastAid)->GetEmlsrManager()->SetAttribute(
1234 "EmlsrLinkSet",
1236 m_lastAid++;
1239 {
1240 // make the next STA send EML Notification frame
1242 return;
1243 }
1244 // all stations enabled EMLSR mode; start traffic
1246 StartTraffic();
1247 });
1248}
1249
1250void
1252{
1253 auto psduIt = m_txPsdus.cbegin();
1254
1255 // lambda to jump to the next QoS data frame or MU-RTS Trigger Frame transmitted
1256 // to an EMLSR client
1257 auto jumpToQosDataOrMuRts = [&]() {
1258 while (psduIt != m_txPsdus.cend() &&
1259 !psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData())
1260 {
1261 auto psdu = psduIt->psduMap.cbegin()->second;
1262 if (psdu->GetHeader(0).IsTrigger())
1263 {
1264 CtrlTriggerHeader trigger;
1265 psdu->GetPayload(0)->PeekHeader(trigger);
1266 if (trigger.IsMuRts())
1267 {
1268 break;
1269 }
1270 }
1271 psduIt++;
1272 }
1273 };
1274
1327 for (std::size_t i = 0; i < m_nEmlsrStations + m_nNonEmlsrStations; i++)
1328 {
1329 std::set<uint8_t> linkIds;
1330
1331 jumpToQosDataOrMuRts();
1332 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend() &&
1333 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData()),
1334 true,
1335 "Expected at least one QoS data frame before enabling EMLSR mode");
1336 linkIds.insert(psduIt->linkId);
1337 const auto firstAmpduTxEnd =
1338 psduIt->startTx +
1339 WifiPhy::CalculateTxDuration(psduIt->psduMap,
1340 psduIt->txVector,
1341 m_staMacs[i]->GetWifiPhy(psduIt->linkId)->GetPhyBand());
1342 psduIt++;
1343
1344 jumpToQosDataOrMuRts();
1345 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend() &&
1346 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData()),
1347 true,
1348 "Expected at least two QoS data frames before enabling EMLSR mode");
1349 linkIds.insert(psduIt->linkId);
1350 const auto secondAmpduTxStart = psduIt->startTx;
1351 psduIt++;
1352
1358 auto setupLinks = m_staMacs[i]->GetSetupLinkIds();
1359 if (i < m_nEmlsrStations &&
1360 std::none_of(setupLinks.begin(), setupLinks.end(), [&](auto&& linkId) {
1361 return linkId != *m_assocLinkId[i + 1] && m_emlsrLinks.count(linkId) == 0;
1362 }))
1363 {
1364 NS_TEST_EXPECT_MSG_EQ(linkIds.size(),
1365 1,
1366 "Expected both A-MPDUs to be sent on the same link");
1367 NS_TEST_EXPECT_MSG_EQ(*linkIds.begin(),
1368 *m_assocLinkId[i + 1],
1369 "A-MPDUs sent on incorrect link");
1370 NS_TEST_EXPECT_MSG_LT(firstAmpduTxEnd,
1371 secondAmpduTxStart,
1372 "A-MPDUs are not sent one after another");
1373 }
1378 else
1379 {
1380 NS_TEST_EXPECT_MSG_EQ(linkIds.size(),
1381 2,
1382 "Expected A-MPDUs to be sent on distinct links");
1383 NS_TEST_EXPECT_MSG_GT(firstAmpduTxEnd,
1384 secondAmpduTxStart,
1385 "A-MPDUs are not sent concurrently");
1386 }
1387 }
1388
1451 using FrameExchange = std::list<decltype(psduIt)>;
1452
1453 std::vector<std::list<FrameExchange>> frameExchanges(m_nEmlsrStations);
1454
1455 // compute all frame exchanges involving EMLSR clients
1456 while (psduIt != m_txPsdus.cend())
1457 {
1458 jumpToQosDataOrMuRts();
1459 if (psduIt == m_txPsdus.cend())
1460 {
1461 break;
1462 }
1463
1464 if (IsTrigger(psduIt->psduMap))
1465 {
1466 CtrlTriggerHeader trigger;
1467 psduIt->psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
1468 // this is an MU-RTS TF starting a new frame exchange sequence; add it to all
1469 // the addressed EMLSR clients
1471 true,
1472 "jumpToQosDataOrMuRts does not return TFs other than MU-RTS");
1473 for (const auto& userInfo : trigger)
1474 {
1475 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1476 {
1477 if (m_staMacs.at(i)->GetAssociationId() == userInfo.GetAid12())
1478 {
1479 frameExchanges.at(i).emplace_back(FrameExchange{psduIt});
1480 break;
1481 }
1482 }
1483 }
1484 psduIt++;
1485 continue;
1486 }
1487
1488 // we get here if psduIt points to a psduMap containing QoS data frame(s); find (if any)
1489 // the QoS data frame(s) addressed to EMLSR clients and add them to the appropriate
1490 // frame exchange sequence
1491 for (const auto& staIdPsduPair : psduIt->psduMap)
1492 {
1493 std::for_each_n(m_staMacs.cbegin(), m_nEmlsrStations, [&](auto&& staMac) {
1494 if (!staMac->GetLinkIdByAddress(staIdPsduPair.second->GetAddr1()))
1495 {
1496 // not addressed to this non-AP MLD
1497 return;
1498 }
1499 // a QoS data frame starts a new frame exchange sequence if there is no previous
1500 // MU-RTS TF that has been sent on the same link and is not already followed by
1501 // a QoS data frame
1502 std::size_t id = staMac->GetDevice()->GetNode()->GetId() - 1;
1503 for (auto& frameExchange : frameExchanges.at(id))
1504 {
1505 if (IsTrigger(frameExchange.front()->psduMap) &&
1506 frameExchange.front()->linkId == psduIt->linkId &&
1507 frameExchange.size() == 1)
1508 {
1509 auto it = std::next(frameExchange.front());
1510 while (it != m_txPsdus.end())
1511 {
1512 // stop at the first frame other than CTS sent on this link
1513 if (it->linkId == psduIt->linkId &&
1514 !it->psduMap.begin()->second->GetHeader(0).IsCts())
1515 {
1516 break;
1517 }
1518 ++it;
1519 }
1520 if (it == psduIt)
1521 {
1522 // the QoS data frame actually followed the MU-RTS TF
1523 frameExchange.emplace_back(psduIt);
1524 return;
1525 }
1526 }
1527 }
1528 frameExchanges.at(id).emplace_back(FrameExchange{psduIt});
1529 });
1530 }
1531 psduIt++;
1532 }
1533
1540 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1541 {
1542 NS_TEST_EXPECT_MSG_GT_OR_EQ(frameExchanges.at(i).size(),
1543 2,
1544 "Expected at least 2 frame exchange sequences "
1545 << "involving EMLSR client " << i);
1546
1547 auto firstExchangeIt = frameExchanges.at(i).begin();
1548 auto secondExchangeIt = std::next(firstExchangeIt);
1549
1550 const auto firstAmpduTxEnd =
1551 firstExchangeIt->back()->startTx +
1553 firstExchangeIt->back()->psduMap,
1554 firstExchangeIt->back()->txVector,
1555 m_staMacs[i]->GetWifiPhy(firstExchangeIt->back()->linkId)->GetPhyBand());
1556 const auto secondAmpduTxStart = secondExchangeIt->front()->startTx;
1557
1558 if (m_staMacs[i]->GetNLinks() == m_emlsrLinks.size())
1559 {
1560 // all links are EMLSR links
1561 NS_TEST_EXPECT_MSG_EQ(IsTrigger(firstExchangeIt->front()->psduMap),
1562 true,
1563 "Expected an MU-RTS TF as ICF of first frame exchange sequence");
1565 firstExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1566 true,
1567 "Expected a QoS data frame in the first frame exchange sequence");
1568
1569 NS_TEST_EXPECT_MSG_EQ(IsTrigger(secondExchangeIt->front()->psduMap),
1570 true,
1571 "Expected an MU-RTS TF as ICF of second frame exchange sequence");
1573 secondExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1574 true,
1575 "Expected a QoS data frame in the second frame exchange sequence");
1576
1577 NS_TEST_EXPECT_MSG_LT(firstAmpduTxEnd,
1578 secondAmpduTxStart,
1579 "A-MPDUs are not sent one after another");
1580 }
1581 else
1582 {
1583 std::vector<uint8_t> nonEmlsrIds;
1584 auto setupLinks = m_staMacs[i]->GetSetupLinkIds();
1585 std::set_difference(setupLinks.begin(),
1586 setupLinks.end(),
1587 m_emlsrLinks.begin(),
1588 m_emlsrLinks.end(),
1589 std::back_inserter(nonEmlsrIds));
1590 NS_TEST_ASSERT_MSG_EQ(nonEmlsrIds.size(), 1, "Unexpected number of non-EMLSR links");
1591
1592 auto nonEmlsrLinkExchangeIt = firstExchangeIt->front()->linkId == nonEmlsrIds[0]
1593 ? firstExchangeIt
1594 : secondExchangeIt;
1595 NS_TEST_EXPECT_MSG_EQ(IsTrigger(nonEmlsrLinkExchangeIt->front()->psduMap),
1596 false,
1597 "Did not expect an MU-RTS TF as ICF on non-EMLSR link");
1599 nonEmlsrLinkExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1600 true,
1601 "Expected a QoS data frame on the non-EMLSR link");
1602
1603 auto emlsrLinkExchangeIt =
1604 nonEmlsrLinkExchangeIt == firstExchangeIt ? secondExchangeIt : firstExchangeIt;
1605 NS_TEST_EXPECT_MSG_NE(+emlsrLinkExchangeIt->front()->linkId,
1606 +nonEmlsrIds[0],
1607 "Expected this exchange not to occur on non-EMLSR link");
1608 NS_TEST_EXPECT_MSG_EQ(IsTrigger(emlsrLinkExchangeIt->front()->psduMap),
1609 true,
1610 "Expected an MU-RTS TF as ICF on the EMLSR link");
1612 emlsrLinkExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1613 true,
1614 "Expected a QoS data frame on the EMLSR link");
1615
1616 NS_TEST_EXPECT_MSG_GT(firstAmpduTxEnd,
1617 secondAmpduTxStart,
1618 "A-MPDUs are not sent concurrently");
1619 }
1620
1621 // we are done with processing the first two frame exchanges, remove them
1622 frameExchanges.at(i).erase(firstExchangeIt);
1623 frameExchanges.at(i).erase(secondExchangeIt);
1624 }
1625
1651 if (m_nEmlsrStations == 2 && m_apMac->GetNLinks() == m_emlsrLinks.size())
1652 {
1653 // the following checks are only done with 2 EMLSR clients having no non-EMLSR link
1654 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1655 {
1656 NS_TEST_EXPECT_MSG_GT_OR_EQ(frameExchanges.at(i).size(),
1657 2,
1658 "Expected at least 2 frame exchange sequences "
1659 << "involving EMLSR client " << i);
1660 // the first frame exchange must start with an ICF
1661 auto firstExchangeIt = frameExchanges.at(i).begin();
1662
1663 NS_TEST_EXPECT_MSG_EQ(IsTrigger(firstExchangeIt->front()->psduMap),
1664 true,
1665 "Expected an MU-RTS TF as ICF of first frame exchange sequence");
1667 firstExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1668 true,
1669 "Expected a QoS data frame in the first frame exchange sequence");
1670 }
1671
1672 // the second frame exchange is the one that starts first
1673 auto secondExchangeIt = std::next(frameExchanges.at(0).begin())->front()->startTx <
1674 std::next(frameExchanges.at(1).begin())->front()->startTx
1675 ? std::next(frameExchanges.at(0).begin())
1676 : std::next(frameExchanges.at(1).begin());
1677 decltype(secondExchangeIt) thirdExchangeIt;
1678 std::size_t thirdExchangeStaId;
1679
1680 if (secondExchangeIt == std::next(frameExchanges.at(0).begin()))
1681 {
1682 thirdExchangeIt = std::next(frameExchanges.at(1).begin());
1683 thirdExchangeStaId = 1;
1684 }
1685 else
1686 {
1687 thirdExchangeIt = std::next(frameExchanges.at(0).begin());
1688 thirdExchangeStaId = 0;
1689 }
1690
1691 // the second frame exchange is not protected by the ICF and starts a SIFS after the end
1692 // of the previous one
1693 NS_TEST_EXPECT_MSG_EQ(IsTrigger(secondExchangeIt->front()->psduMap),
1694 false,
1695 "Expected no ICF for the second frame exchange sequence");
1697 secondExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1698 true,
1699 "Expected a QoS data frame in the second frame exchange sequence");
1700
1701 // the first two frame exchanges occur on the same link
1702 NS_TEST_EXPECT_MSG_EQ(+secondExchangeIt->front()->linkId,
1703 +frameExchanges.at(0).begin()->front()->linkId,
1704 "Expected the first two frame exchanges to occur on the same link");
1705
1706 auto bAckRespIt = std::prev(secondExchangeIt->front());
1707 NS_TEST_EXPECT_MSG_EQ(bAckRespIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAck(),
1708 true,
1709 "Expected a BlockAck response before the second frame exchange");
1710 auto bAckRespTxEnd =
1711 bAckRespIt->startTx +
1712 WifiPhy::CalculateTxDuration(bAckRespIt->psduMap,
1713 bAckRespIt->txVector,
1714 m_apMac->GetWifiPhy(bAckRespIt->linkId)->GetPhyBand());
1715
1716 // the second frame exchange starts a SIFS after the previous one
1718 bAckRespTxEnd + m_apMac->GetWifiPhy(bAckRespIt->linkId)->GetSifs(),
1719 secondExchangeIt->front()->startTx,
1720 "Expected the second frame exchange to start a SIFS after the first one");
1721
1722 // the third frame exchange is protected by MU-RTS and occurs on a different link
1723 NS_TEST_EXPECT_MSG_EQ(IsTrigger(thirdExchangeIt->front()->psduMap),
1724 true,
1725 "Expected an MU-RTS as ICF for the third frame exchange sequence");
1727 thirdExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1728 true,
1729 "Expected a QoS data frame in the third frame exchange sequence");
1730
1732 +secondExchangeIt->front()->linkId,
1733 +thirdExchangeIt->front()->linkId,
1734 "Expected the second and third frame exchanges to occur on distinct links");
1735
1736 auto secondQosIt = secondExchangeIt->front();
1737 auto secondQosTxEnd =
1738 secondQosIt->startTx +
1739 WifiPhy::CalculateTxDuration(secondQosIt->psduMap,
1740 secondQosIt->txVector,
1741 m_apMac->GetWifiPhy(secondQosIt->linkId)->GetPhyBand());
1742
1743 NS_TEST_EXPECT_MSG_GT_OR_EQ(thirdExchangeIt->front()->startTx,
1744 secondQosTxEnd + m_transitionDelay.at(thirdExchangeStaId),
1745 "Transmission started before transition delay");
1746
1747 // the BlockAck of the third frame exchange is not received correctly, so there should be
1748 // another frame exchange
1749 NS_TEST_EXPECT_MSG_EQ((thirdExchangeIt != frameExchanges.at(thirdExchangeStaId).end()),
1750 true,
1751 "Expected a fourth frame exchange");
1752 auto fourthExchangeIt = std::next(thirdExchangeIt);
1753
1754 // the fourth frame exchange is protected by MU-RTS
1755 NS_TEST_EXPECT_MSG_EQ(IsTrigger(fourthExchangeIt->front()->psduMap),
1756 true,
1757 "Expected an MU-RTS as ICF for the fourth frame exchange sequence");
1758
1759 bAckRespIt = std::prev(fourthExchangeIt->front());
1760 NS_TEST_EXPECT_MSG_EQ(bAckRespIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAck(),
1761 true,
1762 "Expected a BlockAck response before the fourth frame exchange");
1763 auto phy = m_apMac->GetWifiPhy(bAckRespIt->linkId);
1764 bAckRespTxEnd = bAckRespIt->startTx + WifiPhy::CalculateTxDuration(bAckRespIt->psduMap,
1765 bAckRespIt->txVector,
1766 phy->GetPhyBand());
1767 auto timeout = phy->GetSifs() + phy->GetSlot() + MicroSeconds(20);
1768
1769 // the fourth frame exchange starts a SIFS after the previous one
1770 NS_TEST_EXPECT_MSG_GT_OR_EQ(fourthExchangeIt->front()->startTx,
1771 bAckRespTxEnd + timeout +
1772 m_transitionDelay.at(thirdExchangeStaId),
1773 "Transmission started before transition delay");
1774
1775 auto bAckReqIt = std::next(fourthExchangeIt->front(), 2);
1776 NS_TEST_EXPECT_MSG_EQ(bAckReqIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAckReq(),
1777 true,
1778 "Expected a BlockAck request in the fourth frame exchange");
1779
1780 // we are done with processing the frame exchanges, remove them (two frame exchanges
1781 // per EMLSR client, plus the last one)
1782 frameExchanges.at(0).pop_front();
1783 frameExchanges.at(0).pop_front();
1784 frameExchanges.at(1).pop_front();
1785 frameExchanges.at(1).pop_front();
1786 frameExchanges.at(thirdExchangeStaId).pop_front();
1787 }
1788
1845 // for each EMLSR client, there should be a frame exchange with ICF and no data frame
1846 // (ICF protects the EML Notification response) and two frame exchanges with data frames
1847 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1848 {
1849 // if the EML Notification frame disabling EMLSR mode is sent on an EMLSR link, it
1850 // is protected by an ICF; otherwise, it is not protected. Given that the default
1851 // EMLSR Manager sends EML Notification frames on the link used to establish association,
1852 // ICF is sent to protect the EML Notification frame if the link used to establish
1853 // association is an EMLSR link
1854 if (m_emlsrLinks.count(*m_assocLinkId.at(i + 1)) == 1)
1855 {
1856 auto firstExchangeIt = frameExchanges.at(i).begin();
1857
1858 NS_TEST_EXPECT_MSG_EQ(IsTrigger(firstExchangeIt->front()->psduMap),
1859 true,
1860 "Expected an MU-RTS TF as ICF of first frame exchange sequence");
1861 NS_TEST_EXPECT_MSG_EQ(firstExchangeIt->size(),
1862 1,
1863 "Expected no data frame in the first frame exchange sequence");
1864
1865 frameExchanges.at(i).pop_front();
1866 }
1867
1868 NS_TEST_EXPECT_MSG_GT_OR_EQ(frameExchanges.at(i).size(),
1869 2,
1870 "Expected at least 2 frame exchange sequences "
1871 << "involving EMLSR client " << i);
1872
1873 auto firstExchangeIt = frameExchanges.at(i).begin();
1874 auto secondExchangeIt = std::next(firstExchangeIt);
1875
1876 const auto firstAmpduTxEnd =
1877 firstExchangeIt->back()->startTx +
1879 firstExchangeIt->back()->psduMap,
1880 firstExchangeIt->back()->txVector,
1881 m_staMacs[i]->GetWifiPhy(firstExchangeIt->back()->linkId)->GetPhyBand());
1882 const auto secondAmpduTxStart = secondExchangeIt->front()->startTx;
1883
1885 firstExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1886 true,
1887 "Expected a QoS data frame in the first frame exchange sequence");
1888 NS_TEST_EXPECT_MSG_EQ(firstExchangeIt->size(),
1889 1,
1890 "Expected one frame only in the first frame exchange sequence");
1891
1893 secondExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1894 true,
1895 "Expected a QoS data frame in the second frame exchange sequence");
1896 NS_TEST_EXPECT_MSG_EQ(secondExchangeIt->size(),
1897 1,
1898 "Expected one frame only in the second frame exchange sequence");
1899
1900 if (m_staMacs[i]->GetNLinks() == m_emlsrLinks.size() ||
1901 m_emlsrLinks.count(*m_assocLinkId.at(i + 1)) == 0)
1902 {
1903 // all links are EMLSR links or the non-EMLSR link is the link used for association:
1904 // the two QoS data frames are sent one after another on the link used for association
1906 +firstExchangeIt->front()->linkId,
1907 +(*m_assocLinkId.at(i + 1)),
1908 "First frame exchange expected to occur on link used for association");
1909
1911 +secondExchangeIt->front()->linkId,
1912 +(*m_assocLinkId.at(i + 1)),
1913 "Second frame exchange expected to occur on link used for association");
1914
1915 NS_TEST_EXPECT_MSG_LT(firstAmpduTxEnd,
1916 secondAmpduTxStart,
1917 "A-MPDUs are not sent one after another");
1918 }
1919 else
1920 {
1921 // the two QoS data frames are sent concurrently on distinct links
1922 NS_TEST_EXPECT_MSG_NE(+firstExchangeIt->front()->linkId,
1923 +secondExchangeIt->front()->linkId,
1924 "Frame exchanges expected to occur on distinct links");
1925
1926 NS_TEST_EXPECT_MSG_GT(firstAmpduTxEnd,
1927 secondAmpduTxStart,
1928 "A-MPDUs are not sent concurrently");
1929 }
1930 }
1931}
1932
1933void
1935{
1936 std::optional<std::size_t> staId;
1937 for (std::size_t id = 0; id < m_nEmlsrStations + m_nNonEmlsrStations; id++)
1938 {
1939 if (m_staMacs.at(id)->GetLinkIdByAddress(address))
1940 {
1941 staId = id;
1942 break;
1943 }
1944 }
1945 NS_TEST_ASSERT_MSG_EQ(staId.has_value(), true, "Not an address of a non-AP MLD " << address);
1946
1947 // check that all EMLSR links (but the link used for ML setup) of the EMLSR clients
1948 // are considered to be in power save mode by the AP MLD; all the other links have
1949 // transitioned to active mode instead
1950 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
1951 {
1952 bool psModeExpected = *staId < m_nEmlsrStations && linkId != m_assocLinkId.at(*staId + 1) &&
1953 m_emlsrLinks.count(linkId) == 1;
1954 auto addr = m_staMacs.at(*staId)->GetAddress();
1955 auto psMode = m_apMac->GetWifiRemoteStationManager(linkId)->IsInPsMode(addr);
1956 NS_TEST_EXPECT_MSG_EQ(psMode,
1957 psModeExpected,
1958 "EMLSR link " << +linkId << " of EMLSR client " << *staId
1959 << " not in " << (psModeExpected ? "PS" : "active")
1960 << " mode");
1961 // check that AP is blocking transmission of QoS data frames on this link
1963 auto mask = m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, linkId);
1964 NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
1965 true,
1966 "Expected to find a mask for EMLSR link "
1967 << +linkId << " of EMLSR client " << *staId);
1968 auto reason = static_cast<std::size_t>(WifiQueueBlockedReason::POWER_SAVE_MODE);
1969 NS_TEST_EXPECT_MSG_EQ(mask->test(reason),
1970 psModeExpected,
1971 "Expected EMLSR link " << +linkId << " of EMLSR client " << *staId
1972 << " to be "
1973 << (psModeExpected ? "blocked" : "unblocked"));
1974 }
1975}
1976
1977void
1979 const WifiTxVector& txVector,
1980 uint8_t linkId)
1981{
1982 // the AP is replying to a received EMLSR Notification frame
1983 auto pkt = mpdu->GetPacket()->Copy();
1984 const auto& hdr = mpdu->GetHeader();
1987 pkt->RemoveHeader(frame);
1988
1989 std::optional<std::size_t> staId;
1990 for (std::size_t id = 0; id < m_nEmlsrStations; id++)
1991 {
1992 if (m_staMacs.at(id)->GetFrameExchangeManager(linkId)->GetAddress() == hdr.GetAddr1())
1993 {
1994 staId = id;
1995 break;
1996 }
1997 }
1998 NS_TEST_ASSERT_MSG_EQ(staId.has_value(),
1999 true,
2000 "Not an address of an EMLSR client " << hdr.GetAddr1());
2001
2002 // The EMLSR mode change occurs a Transition Timeout after the end of the PPDU carrying the Ack
2003 auto phy = m_apMac->GetWifiPhy(linkId);
2004 auto txDuration =
2005 WifiPhy::CalculateTxDuration(mpdu->GetSize() + 4, // A-MPDU Subframe header size
2006 txVector,
2007 phy->GetPhyBand());
2008 WifiTxVector ackTxVector =
2009 m_staMacs.at(*staId)->GetWifiRemoteStationManager(linkId)->GetAckTxVector(hdr.GetAddr2(),
2010 txVector);
2011 auto ackDuration = WifiPhy::CalculateTxDuration(GetAckSize() + 4, // A-MPDU Subframe header
2012 ackTxVector,
2013 phy->GetPhyBand());
2014
2015 Simulator::Schedule(txDuration + phy->GetSifs() + ackDuration, [=]() {
2016 if (frame.m_emlControl.emlsrMode == 1)
2017 {
2018 // EMLSR mode enabled. Check that all EMLSR links of the EMLSR clients are considered
2019 // to be in active mode by the AP MLD
2020 for (const auto linkId : m_emlsrLinks)
2021 {
2022 auto addr = m_staMacs.at(*staId)->GetAddress();
2023 auto psMode = m_apMac->GetWifiRemoteStationManager(linkId)->IsInPsMode(addr);
2024 NS_TEST_EXPECT_MSG_EQ(psMode,
2025 false,
2026 "EMLSR link " << +linkId << " of EMLSR client " << *staId
2027 << " not in active mode");
2028 // check that AP is not blocking transmission of QoS data frames on this link
2029 WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, addr, 0);
2030 auto mask =
2031 m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, linkId);
2032 NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
2033 true,
2034 "Expected to find a mask for EMLSR link "
2035 << +linkId << " of EMLSR client " << *staId);
2036 auto reason = static_cast<std::size_t>(WifiQueueBlockedReason::POWER_SAVE_MODE);
2037 NS_TEST_EXPECT_MSG_EQ(mask->test(reason),
2038 false,
2039 "Expected EMLSR link " << +linkId << " of EMLSR client "
2040 << *staId << " to be unblocked");
2041 }
2042 }
2043 else
2044 {
2045 // EMLSR mode disabled. Check that all EMLSR links (but the link used to send the
2046 // EML Notification frame) of the EMLSR clients are considered to be in power save
2047 // mode by the AP MLD; the other links are in active mode
2048 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2049 {
2050 bool psModeExpected = id != linkId && m_emlsrLinks.count(id) == 1;
2051 auto addr = m_staMacs.at(*staId)->GetAddress();
2052 auto psMode = m_apMac->GetWifiRemoteStationManager(id)->IsInPsMode(addr);
2053 NS_TEST_EXPECT_MSG_EQ(psMode,
2054 psModeExpected,
2055 "EMLSR link "
2056 << +id << " of EMLSR client " << *staId << " not in "
2057 << (psModeExpected ? "PS" : "active") << " mode");
2058 // check that AP is blocking transmission of QoS data frames on this link
2059 WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, addr, 0);
2060 auto mask = m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, id);
2061 NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
2062 true,
2063 "Expected to find a mask for EMLSR link "
2064 << +id << " of EMLSR client " << *staId);
2065 auto reason = static_cast<std::size_t>(WifiQueueBlockedReason::POWER_SAVE_MODE);
2066 NS_TEST_EXPECT_MSG_EQ(mask->test(reason),
2067 psModeExpected,
2068 "Expected EMLSR link "
2069 << +id << " of EMLSR client " << *staId << " to be "
2070 << (psModeExpected ? "blocked" : "unblocked"));
2071 }
2072 }
2073 });
2074}
2075
2076void
2078 const WifiTxVector& txVector,
2079 uint8_t linkId)
2080{
2081 CtrlTriggerHeader trigger;
2082 mpdu->GetPacket()->PeekHeader(trigger);
2083 if (!trigger.IsMuRts())
2084 {
2085 return;
2086 }
2087
2089 true,
2090 "Did not expect an ICF before enabling EMLSR mode");
2091
2094 "Unexpected preamble type for the Initial Control frame");
2095 auto rate = txVector.GetMode().GetDataRate(txVector);
2096 NS_TEST_EXPECT_MSG_EQ((rate == 6e6 || rate == 12e6 || rate == 24e6),
2097 true,
2098 "Unexpected rate for the Initial Control frame: " << rate);
2099
2100 bool found = false;
2101 Time maxPaddingDelay{};
2102
2103 for (const auto& userInfo : trigger)
2104 {
2105 auto addr = m_apMac->GetMldOrLinkAddressByAid(userInfo.GetAid12());
2106 NS_TEST_ASSERT_MSG_EQ(addr.has_value(),
2107 true,
2108 "AID " << userInfo.GetAid12() << " not found");
2109
2111 {
2112 found = true;
2113
2114 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
2115 {
2116 if (m_staMacs.at(i)->GetAddress() == *addr)
2117 {
2118 maxPaddingDelay = Max(maxPaddingDelay, m_paddingDelay.at(i));
2119 break;
2120 }
2121 }
2122
2123 // check that the AP has blocked transmission on all other EMLSR links
2124 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2125 {
2127 {
2128 continue;
2129 }
2130
2131 if (id == linkId)
2132 {
2134 auto mask =
2135 m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, id);
2136 NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
2137 true,
2138 "Expected to find a mask for EMLSR link "
2139 << +id << " of EMLSR client " << *addr);
2140 NS_TEST_EXPECT_MSG_EQ(mask->none(),
2141 true,
2142 "Expected EMLSR link " << +id << " of EMLSR client "
2143 << *addr << " to be unblocked");
2144 continue;
2145 }
2146
2148 auto mask = m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, id);
2149 NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
2150 true,
2151 "Expected to find a mask for EMLSR link "
2152 << +id << " of EMLSR client " << *addr);
2153 auto reason =
2154 static_cast<std::size_t>(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK);
2155 NS_TEST_EXPECT_MSG_EQ(mask->test(reason),
2156 true,
2157 "Expected EMLSR link " << +id << " of EMLSR client " << *addr
2158 << " to be blocked");
2159 NS_TEST_EXPECT_MSG_EQ(mask->count(),
2160 1,
2161 "Expected EMLSR link "
2162 << +id << " of EMLSR client " << *addr
2163 << " to be blocked for one reason only");
2164 }
2165 }
2166 }
2167
2168 NS_TEST_EXPECT_MSG_EQ(found, true, "Expected ICF to be addressed to at least an EMLSR client");
2169
2170 if (maxPaddingDelay.IsStrictlyPositive())
2171 {
2172 // compare the TX duration of this Trigger Frame to that of the Trigger Frame with no
2173 // padding added
2174 auto txDurationWith =
2175 WifiPhy::CalculateTxDuration(mpdu->GetSize(),
2176 txVector,
2177 m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2178 trigger.SetPaddingSize(0);
2179 auto pkt = Create<Packet>();
2180 pkt->AddHeader(trigger);
2181 auto txDurationWithout =
2182 WifiPhy::CalculateTxDuration(Create<WifiPsdu>(pkt, mpdu->GetHeader()),
2183 txVector,
2184 m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2185
2186 NS_TEST_EXPECT_MSG_EQ(txDurationWith,
2187 txDurationWithout + maxPaddingDelay,
2188 "Unexpected TX duration of the MU-RTS TF with padding "
2189 << maxPaddingDelay.As(Time::US));
2190 }
2191}
2192
2193void
2195 const WifiTxVector& txVector,
2196 uint8_t linkId)
2197{
2198 if (m_nEmlsrStations != 2 || m_apMac->GetNLinks() != m_emlsrLinks.size() ||
2200
2201 {
2202 // we are interested in frames sent to test transition delay
2203 return;
2204 }
2205
2206 std::size_t firstClientId = 0;
2207 std::size_t secondClientId = 1;
2208 auto addr = m_staMacs[secondClientId]->GetAddress();
2209 auto txDuration =
2210 WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2211
2213
2214 switch (m_countQoSframes)
2215 {
2216 case 1:
2217 // generate another small packet addressed to the first EMLSR client only
2219 GetApplication(DOWNLINK, firstClientId, 1, 40));
2220 break;
2221 case 2:
2222 // generate another small packet addressed to the second EMLSR client
2224 GetApplication(DOWNLINK, secondClientId, 1, 40));
2225
2226 // the EMLSR client that is not the recipient of the QoS frame being transmitted will
2227 // switch back to listening mode after a transition delay starting from the end of
2228 // the PPDU carrying this QoS data frame
2229
2230 // immediately before the end of the PPDU, this link is not blocked for the EMLSR client
2231 Simulator::Schedule(txDuration - NanoSeconds(1), [=]() {
2233 auto mask = m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, linkId);
2234 NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
2235 true,
2236 "Expected to find a mask for EMLSR link "
2237 << +linkId << " of EMLSR client " << secondClientId);
2238 NS_TEST_EXPECT_MSG_EQ(mask->none(),
2239 true,
2240 "Expected EMLSR link " << +linkId << " of EMLSR client "
2241 << secondClientId << " to be unblocked");
2242 });
2243 // immediately before the end of the PPDU, the other links are blocked for the EMLSR client
2244 Simulator::Schedule(txDuration - NanoSeconds(1), [=]() {
2245 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2246 {
2247 if (id == linkId)
2248 {
2249 continue;
2250 }
2251
2253 auto mask = m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, id);
2254 NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
2255 true,
2256 "Expected to find a mask for EMLSR link "
2257 << +id << " of EMLSR client " << secondClientId);
2258 auto reason =
2259 static_cast<std::size_t>(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK);
2260 NS_TEST_EXPECT_MSG_EQ(mask->test(reason),
2261 true,
2262 "Expected EMLSR link " << +id << " of EMLSR client "
2263 << secondClientId << " to be blocked");
2264 NS_TEST_EXPECT_MSG_EQ(mask->count(),
2265 1,
2266 "Expected EMLSR link " << +id << " of EMLSR client "
2267 << secondClientId
2268 << " to be blocked "
2269 " for one reason only");
2270 }
2271 });
2272 // immediately after the end of the PPDU, all links are blocked for the EMLSR client
2273 Simulator::Schedule(txDuration + NanoSeconds(1), [=]() {
2274 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2275 {
2277 auto mask = m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, id);
2278 NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
2279 true,
2280 "Expected to find a mask for EMLSR link "
2281 << +id << " of EMLSR client " << secondClientId);
2282 auto reason = static_cast<std::size_t>(
2283 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY);
2284 NS_TEST_EXPECT_MSG_EQ(mask->test(reason),
2285 true,
2286 "Expected EMLSR link " << +id << " of EMLSR client "
2287 << secondClientId << " to be blocked");
2288 NS_TEST_EXPECT_MSG_EQ(mask->count(),
2289 1,
2290 "Expected EMLSR link " << +id << " of EMLSR client "
2291 << secondClientId
2292 << " to be blocked "
2293 " for one reason only");
2294 }
2295 });
2296 // immediately before the transition delay, all links are still blocked for the EMLSR client
2298 txDuration + m_transitionDelay.at(secondClientId) - NanoSeconds(1),
2299 [=]() {
2300 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2301 {
2303 auto mask =
2304 m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, id);
2305 NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
2306 true,
2307 "Expected to find a mask for EMLSR link "
2308 << +id << " of EMLSR client " << secondClientId);
2309 auto reason = static_cast<std::size_t>(
2310 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY);
2311 NS_TEST_EXPECT_MSG_EQ(mask->test(reason),
2312 true,
2313 "Expected EMLSR link " << +id << " of EMLSR client "
2314 << secondClientId
2315 << " to be blocked");
2316 NS_TEST_EXPECT_MSG_EQ(mask->count(),
2317 1,
2318 "Expected EMLSR link " << +id << " of EMLSR client "
2319 << secondClientId
2320 << " to be blocked "
2321 " for one reason only");
2322 }
2323 });
2324
2325 break;
2326 }
2327}
2328
2329void
2331 const WifiTxVector& txVector,
2332 uint8_t linkId)
2333{
2334 if (m_nEmlsrStations != 2 || m_apMac->GetNLinks() != m_emlsrLinks.size() ||
2336 {
2337 // we are interested in frames sent to test transition delay
2338 return;
2339 }
2340
2341 auto taddr = psduMap.cbegin()->second->GetAddr2();
2342 std::size_t clientId;
2343 if (m_staMacs[0]->GetLinkIdByAddress(taddr))
2344 {
2345 clientId = 0;
2346 }
2347 else
2348 {
2349 NS_TEST_ASSERT_MSG_EQ(m_staMacs[1]->GetLinkIdByAddress(taddr).has_value(),
2350 true,
2351 "Unexpected TA for BlockAck: " << taddr);
2352 clientId = 1;
2353 }
2354 // we need the MLD address to check the status of the container queues
2355 auto addr = m_apMac->GetWifiRemoteStationManager(linkId)->GetMldAddress(taddr);
2356 NS_TEST_ASSERT_MSG_EQ(addr.has_value(), true, "MLD address not found for " << taddr);
2357
2358 auto apPhy = m_apMac->GetWifiPhy(linkId);
2359 auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, apPhy->GetPhyBand());
2360 auto timeout = apPhy->GetSifs() + apPhy->GetSlot() + MicroSeconds(20);
2361
2363
2364 switch (m_countBlockAck)
2365 {
2366 case 4:
2367 // at the end of the PPDU carrying this BlockAck, the EMLSR client sending this
2368 // frame will start a timeout interval, after which it will start the transition to
2369 // the listening mode (such transition lasting the transition delay)
2370
2371 // immediately before the end of the PPDU plus timeout, this link is not blocked
2372 // for the EMLSR client
2373 Simulator::Schedule(txDuration + timeout - NanoSeconds(1), [=]() {
2375 auto mask = m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, linkId);
2376 NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
2377 true,
2378 "Expected to find a mask for EMLSR link "
2379 << +linkId << " of EMLSR client " << clientId);
2380 NS_TEST_EXPECT_MSG_EQ(mask->none(),
2381 true,
2382 "Expected EMLSR link " << +linkId << " of EMLSR client "
2383 << clientId << " to be unblocked");
2384 });
2385 // immediately before the end of the PPDU plus timeout, the other links are blocked
2386 // for the EMLSR client
2387 Simulator::Schedule(txDuration + timeout - NanoSeconds(1), [=]() {
2388 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2389 {
2390 if (id == linkId)
2391 {
2392 continue;
2393 }
2394
2396 auto mask = m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, id);
2397 NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
2398 true,
2399 "Expected to find a mask for EMLSR link "
2400 << +id << " of EMLSR client " << clientId);
2401 auto reason =
2402 static_cast<std::size_t>(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK);
2403 NS_TEST_EXPECT_MSG_EQ(mask->test(reason),
2404 true,
2405 "Expected EMLSR link " << +id << " of EMLSR client "
2406 << clientId << " to be blocked");
2407 NS_TEST_EXPECT_MSG_EQ(mask->count(),
2408 1,
2409 "Expected EMLSR link " << +id << " of EMLSR client "
2410 << clientId
2411 << " to be blocked "
2412 " for one reason only");
2413 }
2414 });
2415 // immediately after the end of the PPDU plus timeout, all links are blocked for the EMLSR
2416 // client
2417 Simulator::Schedule(txDuration + timeout + MicroSeconds(1), [=]() {
2418 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2419 {
2421 auto mask = m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, id);
2422 NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
2423 true,
2424 "Expected to find a mask for EMLSR link "
2425 << +id << " of EMLSR client " << clientId);
2426 auto reason = static_cast<std::size_t>(
2427 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY);
2428 NS_TEST_EXPECT_MSG_EQ(mask->test(reason),
2429 true,
2430 "Expected EMLSR link " << +id << " of EMLSR client "
2431 << clientId << " to be blocked");
2432 NS_TEST_EXPECT_MSG_EQ(mask->count(),
2433 1,
2434 "Expected EMLSR link " << +id << " of EMLSR client "
2435 << clientId
2436 << " to be blocked "
2437 " for one reason only");
2438 }
2439 });
2440 // immediately before the transition delay, all links are still blocked for the EMLSR client
2442 txDuration + timeout + m_transitionDelay.at(clientId) - NanoSeconds(1),
2443 [=]() {
2444 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2445 {
2447 auto mask =
2448 m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, id);
2449 NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
2450 true,
2451 "Expected to find a mask for EMLSR link "
2452 << +id << " of EMLSR client " << clientId);
2453 auto reason = static_cast<std::size_t>(
2454 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY);
2455 NS_TEST_EXPECT_MSG_EQ(mask->test(reason),
2456 true,
2457 "Expected EMLSR link " << +id << " of EMLSR client "
2458 << clientId << " to be blocked");
2459 NS_TEST_EXPECT_MSG_EQ(mask->count(),
2460 1,
2461 "Expected EMLSR link " << +id << " of EMLSR client "
2462 << clientId
2463 << " to be blocked "
2464 " for one reason only");
2465 }
2466 });
2467 // immediately after the transition delay, all links are unblocked for the EMLSR client
2469 txDuration + timeout + m_transitionDelay.at(clientId) + MicroSeconds(1),
2470 [=]() {
2471 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2472 {
2474 auto mask =
2475 m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, id);
2476 NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
2477 true,
2478 "Expected to find a mask for EMLSR link "
2479 << +id << " of EMLSR client " << clientId);
2480 NS_TEST_EXPECT_MSG_EQ(mask->none(),
2481 true,
2482 "Expected EMLSR link " << +id << " of EMLSR client "
2483 << clientId << " to be unblocked");
2484 }
2485 });
2486
2487 // corrupt this BlockAck so that the AP MLD sends a BlockAckReq later on
2488 auto uid = psduMap.cbegin()->second->GetPacket()->GetUid();
2489 m_errorModel->SetList({uid});
2490 break;
2491 }
2492}
2493
2494void
2496{
2499
2500 CheckResults();
2501
2503}
2504
2512{
2513 public:
2515};
2516
2518 : TestSuite("wifi-emlsr", UNIT)
2519{
2525 for (const auto& emlsrLinks : {std::set<uint8_t>{0, 1, 2},
2526 std::set<uint8_t>{1, 2},
2527 std::set<uint8_t>{0, 2},
2528 std::set<uint8_t>{0, 1}})
2529 {
2531 0,
2532 emlsrLinks,
2533 {MicroSeconds(32)},
2534 {MicroSeconds(32)},
2535 MicroSeconds(512)),
2538 1,
2539 emlsrLinks,
2540 {MicroSeconds(64)},
2541 {MicroSeconds(64)},
2542 MicroSeconds(512)),
2545 2,
2546 emlsrLinks,
2547 {MicroSeconds(128), MicroSeconds(256)},
2548 {MicroSeconds(128), MicroSeconds(256)},
2549 MicroSeconds(512)),
2551 }
2552}
2553
#define Max(a, b)
Test the exchange of EML Operating Mode Notification frames.
Ptr< ListErrorModel > m_errorModel
error rate model to corrupt packets at AP MLD
void TxOk(Ptr< const WifiMpdu > mpdu)
Callback invoked when the non-AP MLD receives the acknowledgment for a transmitted MPDU.
void CheckEmlNotification(Ptr< const WifiPsdu > psdu, const WifiTxVector &txVector, uint8_t linkId)
Check the content of a received EML Operating Mode Notification frame.
std::optional< uint8_t > m_assocLinkId
ID of the link used to establish association.
std::list< uint64_t > m_uidList
list of UIDs of packets to corrupt
void DoRun() override
Implementation to actually run this TestCase.
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 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 DoSetup() override
Implementation to do any local setup required for this TestCase.
void CheckEmlsrLinks()
Check that the EMLSR mode has been enabled on the expected EMLSR links.
std::size_t m_checkEmlsrLinksCount
counter for the number of times CheckEmlsrLinks is called (should be two: when the transition timeout...
EmlNotificationExchangeTest(const std::set< uint8_t > &linksToEnableEmlsrOn, Time transitionTimeout)
Constructor.
void Transmit(uint8_t linkId, std::string context, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW) override
Callback invoked when a FEM passes PSDUs to the PHY.
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...
~EmlNotificationExchangeTest() override=default
Test EML Operating Mode Notification frame serialization and deserialization.
EmlOperatingModeNotificationTest()
Constructor.
void DoRun() override
Implementation to actually run this TestCase.
~EmlOperatingModeNotificationTest() override=default
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.
EmlsrDlTxopTest(std::size_t nEmlsrStations, std::size_t nNonEmlsrStations, const std::set< uint8_t > &linksToEnableEmlsrOn, const std::vector< Time > &paddingDelay, const std::vector< Time > &transitionDelay, Time transitionTimeout)
Constructor.
void CheckPmModeAfterAssociation(const Mac48Address &address)
Check that the AP MLD considers the correct Power Management mode for the links setup with the given ...
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
std::size_t m_countQoSframes
counter for QoS frames (transition delay test)
~EmlsrDlTxopTest() override=default
void Transmit(uint8_t linkId, std::string context, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW) override
Callback invoked when a FEM passes PSDUs to the PHY.
void CheckBlockAck(const WifiConstPsduMap &psduMap, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken by the AP MLD receiving a PPDU containing BlockAck frames fr...
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 EnableEmlsrMode()
Enable EMLSR mode on the next EMLSR client.
void DoRun() override
Implementation to actually run this TestCase.
void CheckEmlNotificationFrame(Ptr< const WifiMpdu > mpdu, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken by the AP MLD transmitting an EML Operating Mode Notificatio...
std::vector< std::optional< uint8_t > > m_assocLinkId
ID of the link used to establish association (vector index is the node ID)
std::size_t m_countBlockAck
counter for BlockAck frames (transition delay test)
Base class for EMLSR Operations tests.
std::size_t m_nNonEmlsrStations
number of stations to create that do not activate EMLSR
void SetSsid(uint16_t aid, Mac48Address)
Set the SSID on the next station that needs to start the association procedure.
bool m_establishBaDl
whether BA needs to be established (for TID 0) with the AP as originator
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.
~EmlsrOperationsTestBase() override=default
TrafficDirection
Enumeration for traffic directions.
void DoSetup() override
Implementation to do any local setup required for this TestCase.
virtual void Transmit(uint8_t linkId, std::string context, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW)
Callback invoked when a FEM passes PSDUs to the 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
bool m_establishBaUl
whether BA needs to be established (for TID 0) with the AP as recipient
uint16_t m_lastAid
AID of last associated station.
std::vector< Time > m_transitionDelay
Transition Delay advertised by the non-AP MLD.
Time m_transitionTimeout
Transition Timeout advertised by the AP MLD.
std::vector< PacketSocketAddress > m_ulSockets
packet socket address for UL traffic
std::vector< Ptr< StaWifiMac > > m_staMacs
MACs of the non-AP MLDs.
virtual void StartTraffic()
Start the generation of traffic (needs to be overridden)
EmlsrOperationsTestBase(const std::string &name)
Constructor.
wifi EMLSR Test Suite
std::optional< Mac48Address > GetMldOrLinkAddressByAid(uint16_t aid) const
A container for one type of attribute.
AttributeValue implementation for Boolean.
Definition: boolean.h:37
Headers for Trigger frames.
Definition: ctrl-headers.h:942
bool IsMuRts() const
Check if this is a MU-RTS Trigger frame.
void SetPaddingSize(std::size_t size)
Set the size in bytes of the Padding field.
Hold variables of type enum.
Definition: enum.h:56
uint32_t GetUid() const
Definition: event-id.cc:104
Subclass of TestCase class adding the ability to test the serialization and deserialization of a Head...
void TestHeaderSerialization(const T &hdr, Args &&... args)
Serialize the given header in a buffer, then create a new header by deserializing from the buffer and...
void SetList(const std::list< uint64_t > &packetlist)
Definition: error-model.cc:456
an EUI-48 address
Definition: mac48-address.h:46
Implement the header for management frames of type association request.
Definition: mgt-headers.h:161
Implement the header for management frames of type association and reassociation response.
Definition: mgt-headers.h:338
Implement the header for Action frames of type EML Operating Mode Notification.
Definition: mgt-headers.h:1113
void SetLinkIdInBitmap(uint8_t linkId)
Set the bit position in the link bitmap corresponding to the given link.
EmlControl m_emlControl
EML Control field.
Definition: mgt-headers.h:1163
std::list< uint8_t > GetLinkBitmap() const
std::optional< EmlsrParamUpdate > m_emlsrParamUpdate
EMLSR Parameter Update field.
Definition: mgt-headers.h:1164
Helper class used to assign positions and mobility models to nodes.
holds a vector of ns3::NetDevice pointers
Ptr< NetDevice > Get(uint32_t i) const
Get the Ptr<NetDevice> stored in this container at a given index.
keep track of a set of node pointers.
uint32_t AddApplication(Ptr< Application > application)
Associate an Application to this Node.
Definition: node.cc:169
static Iterator Begin()
Definition: node-list.cc:237
static Iterator End()
Definition: node-list.cc:244
bool TraceConnectWithoutContext(std::string name, const CallbackBase &cb)
Connect a TraceSource to a Callback without a context.
Definition: object-base.cc:311
void AggregateObject(Ptr< Object > other)
Aggregate two Objects together.
Definition: object.cc:259
an address for a packet socket
void SetProtocol(uint16_t protocol)
Set the protocol.
void SetSingleDevice(uint32_t device)
Set the address to match only a specified NetDevice.
Give ns3::PacketSocket powers to ns3::Node.
void Install(Ptr< Node > node) const
Aggregate an instance of a ns3::PacketSocketFactory onto the provided node.
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:78
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:568
static void Destroy()
Execute the events scheduled with ScheduleDestroy().
Definition: simulator.cc:140
static Time Now()
Return the current simulation virtual time.
Definition: simulator.cc:199
static void Run()
Run the simulation.
Definition: simulator.cc:176
static void Stop()
Tell the Simulator the calling event should be the last one executed.
Definition: simulator.cc:184
Make it easy to create and manage PHY objects for the spectrum model.
void AddChannel(const Ptr< SpectrumChannel > channel, const FrequencyRange &freqRange=WHOLE_WIFI_SPECTRUM)
The IEEE 802.11 SSID Information Element.
Definition: ssid.h:36
AttributeValue implementation for Ssid.
Hold variables of type string.
Definition: string.h:56
encapsulates test code
Definition: test.h:1060
@ QUICK
Fast test.
Definition: test.h:1065
void AddTestCase(TestCase *testCase, TestDuration duration=QUICK)
Add an individual child TestCase to this test suite.
Definition: test.cc:301
A suite of tests to run.
Definition: test.h:1256
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:105
bool IsStrictlyPositive() const
Exactly equivalent to t > 0.
Definition: nstime.h:350
@ US
microsecond
Definition: nstime.h:118
@ MS
millisecond
Definition: nstime.h:117
bool IsZero() const
Exactly equivalent to t == 0.
Definition: nstime.h:314
AttributeValue implementation for Time.
Definition: nstime.h:1423
void SetTxopLimits(const std::vector< Time > &txopLimits)
Set the TXOP limit for each link.
Definition: txop.cc:378
Hold an unsigned integer type.
Definition: uinteger.h:45
See IEEE 802.11 chapter 7.3.1.11 Header format: | category: 1 | action value: 1 |.
Definition: mgt-headers.h:539
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.
@ PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION
Definition: mgt-headers.h:714
helps to create WifiNetDevice objects
Definition: wifi-helper.h:324
create MAC layers for a ns3::WifiNetDevice.
Ptr< FrameExchangeManager > GetFrameExchangeManager(uint8_t linkId=SINGLE_LINK_OP_ID) const
Get the Frame Exchange Manager associated with the given link.
Definition: wifi-mac.cc:864
Ptr< WifiMacQueueScheduler > GetMacQueueScheduler() const
Get the wifi MAC queue scheduler.
Definition: wifi-mac.cc:574
uint8_t GetNLinks() const
Get the number of links (can be greater than 1 for 11be devices only).
Definition: wifi-mac.cc:930
Ptr< WifiPhy > GetWifiPhy(uint8_t linkId=SINGLE_LINK_OP_ID) const
Definition: wifi-mac.cc:978
Ptr< WifiNetDevice > GetDevice() const
Return the device this PHY is associated with.
Definition: wifi-mac.cc:437
Ptr< WifiRemoteStationManager > GetWifiRemoteStationManager(uint8_t linkId=0) const
Definition: wifi-mac.cc:910
Ptr< QosTxop > GetQosTxop(AcIndex ac) const
Accessor for a specified EDCA object.
Definition: wifi-mac.cc:497
uint64_t GetDataRate(uint16_t channelWidth, uint16_t guardInterval, uint8_t nss) const
Definition: wifi-mode.cc:122
uint32_t GetIfIndex() const override
Address GetAddress() const override
Ptr< Node > GetNode() const override
void SetPcapDataLinkType(SupportedPcapDataLinkTypes dlt)
Set the data link type of PCAP traces to be used.
Definition: wifi-helper.cc:543
void Set(std::string name, const AttributeValue &v)
Definition: wifi-helper.cc:163
@ DLT_IEEE802_11_RADIO
Include Radiotap link layer information.
Definition: wifi-helper.h:178
void SetPostReceptionErrorModel(const Ptr< ErrorModel > em)
Attach a receive ErrorModel to the WifiPhy.
Definition: wifi-phy.cc:648
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition: wifi-phy.cc:1496
WifiPhyBand GetPhyBand() const
Get the configured Wi-Fi band.
Definition: wifi-phy.cc:1005
bool GetEmlsrEnabled(const Mac48Address &address) const
bool IsInPsMode(const Mac48Address &address) const
Return whether the STA is currently in Power Save mode.
std::optional< Mac48Address > GetMldAddress(const Mac48Address &address) const
Get the address of the MLD the given station is affiliated with, if any.
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
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition: assert.h:86
void Connect(std::string path, const CallbackBase &cb)
Definition: config.cc:975
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition: abort.h:108
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:268
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:275
#define NS_TEST_EXPECT_MSG_GT_OR_EQ(actual, limit, msg)
Test that an actual value is greater than or equal to limit and report if not.
Definition: test.h:996
#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:144
#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:790
#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:956
#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:666
#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:251
#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:564
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1360
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1372
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1336
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1348
WifiMacDropReason
The reason why an MPDU was dropped.
Definition: wifi-mac.h:75
@ WIFI_STANDARD_80211be
@ WIFI_PREAMBLE_HT_MF
@ AC_BE
Best Effort.
Definition: qos-utils.h:74
Every class exported by the ns3 library is enclosed in the ns3 namespace.
constexpr FrequencyRange WIFI_SPECTRUM_6_GHZ
Identifier for the frequency range covering the wifi spectrum in the 6 GHz band.
U * PeekPointer(const Ptr< U > &p)
Definition: ptr.h:488
std::unordered_map< uint16_t, Ptr< const WifiPsdu > > WifiConstPsduMap
Map of const PSDUs indexed by STA-ID.
Callback< R, Args... > MakeCallback(R(T::*memPtr)(Args...), OBJ objPtr)
Build Callbacks for class method members which take varying numbers of arguments and potentially retu...
Definition: callback.h:702
std::tuple< WifiContainerQueueType, WifiReceiverAddressType, Mac48Address, std::optional< uint8_t > > WifiContainerQueueId
Tuple (queue type, receiver address type, Address, TID) identifying a container queue.
bool IsTrigger(const WifiPsduMap &psduMap)
constexpr FrequencyRange WIFI_SPECTRUM_5_GHZ
Identifier for the frequency range covering the wifi spectrum in the 5 GHz band.
@ WIFI_MAC_CTL_TRIGGER
@ WIFI_MAC_MGT_ACTION
@ WIFI_MAC_MGT_ASSOCIATION_RESPONSE
@ WIFI_MAC_MGT_ASSOCIATION_REQUEST
@ WIFI_MAC_CTL_BACKRESP
@ WIFI_MAC_QOSDATA
uint32_t GetAckSize()
Return the total Ack size (including FCS trailer).
Definition: wifi-utils.cc:58
constexpr FrequencyRange WIFI_SPECTRUM_2_4_GHZ
Identifier for the frequency range covering the wifi spectrum in the 2.4 GHz band.
STL namespace.
ns phy
Definition: third.py:82
ns3::Time timeout
Information about transmitted frames.
WifiConstPsduMap psduMap
transmitted PSDU map
static uint8_t EncodeEmlsrTransitionDelay(Time delay)
static Time DecodeEmlsrTransitionDelay(uint8_t value)
static Time DecodeEmlsrPaddingDelay(uint8_t value)
static uint8_t EncodeEmlsrPaddingDelay(Time delay)
uint8_t emlsrParamUpdateCtrl
EMLSR Parameter Update Control.
Definition: mgt-headers.h:1135
std::optional< uint16_t > linkBitmap
EMLSR/EMLMR Link Bitmap.
Definition: mgt-headers.h:1137
std::string dir
uint32_t pktSize
packet size used for the simulation (in bytes)
static WifiEmlsrTestSuite g_wifiEmlsrTestSuite
the test suite
static uint32_t ContextToNodeId(std::string context)