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
65{
66 public:
72
73 private:
74 void DoRun() override;
75};
76
79 "Check serialization and deserialization of the EML Operating Mode Notification frame")
80{
81}
82
83void
85{
86 MgtEmlOmn frame;
87
88 // Both EMLSR Mode and EMLMR Mode subfields set to 0 (no link bitmap);
90
91 frame.m_emlControl.emlsrMode = 1;
92 frame.SetLinkIdInBitmap(0);
93 frame.SetLinkIdInBitmap(5);
94 frame.SetLinkIdInBitmap(15);
95
96 // Adding Link Bitmap
98
99 NS_TEST_EXPECT_MSG_EQ((frame.GetLinkBitmap() == std::list<uint8_t>{0, 5, 15}),
100 true,
101 "Unexpected link bitmap");
102
103 auto padding = MicroSeconds(64);
104 auto transition = MicroSeconds(128);
105
109 frame.m_emlsrParamUpdate->transitionDelay =
111
112 // Adding the EMLSR Parameter Update field
114
117 padding,
118 "Unexpected EMLSR Padding Delay");
121 transition,
122 "Unexpected EMLSR Transition Delay");
123}
124
141{
142 public:
148 EmlsrOperationsTestBase(const std::string& name);
149 ~EmlsrOperationsTestBase() override = default;
150
152 enum TrafficDirection : uint8_t
153 {
155 UPLINK
156 };
157
158 protected:
168 virtual void Transmit(Ptr<WifiMac> mac,
169 uint8_t phyId,
170 WifiConstPsduMap psduMap,
171 WifiTxVector txVector,
172 double txPowerW);
173
183 std::size_t staId,
184 std::size_t count,
185 std::size_t pktSize) const;
186
201 Mac48Address dest,
202 uint8_t linkId,
204 bool blocked,
205 std::string description,
206 bool testUnblockedForOtherReasons = true);
207
208 void DoSetup() override;
209
212 {
216 uint8_t linkId;
217 uint8_t phyId;
218 };
219
220 uint8_t m_mainPhyId{0};
221 std::set<uint8_t> m_linksToEnableEmlsrOn;
223 std::size_t m_nEmlsrStations{1};
224 std::size_t m_nNonEmlsrStations{0};
227 std::vector<Time> m_paddingDelay{
228 {MicroSeconds(32)}};
229 std::vector<Time> m_transitionDelay{
230 {MicroSeconds(16)}};
231 bool m_establishBaDl{false};
233 bool m_establishBaUl{false};
235 std::vector<FrameInfo> m_txPsdus;
237 std::vector<Ptr<StaWifiMac>> m_staMacs;
238 std::vector<PacketSocketAddress> m_dlSockets;
239 std::vector<PacketSocketAddress> m_ulSockets;
240 uint16_t m_lastAid{0};
242
243 private:
251 void SetSsid(uint16_t aid, Mac48Address /* addr */);
252
256 virtual void StartTraffic()
257 {
258 }
259};
260
262 : TestCase(name)
263{
264}
265
266void
268 uint8_t phyId,
269 WifiConstPsduMap psduMap,
270 WifiTxVector txVector,
271 double txPowerW)
272{
273 auto linkId = mac->GetLinkForPhy(phyId);
274 NS_TEST_ASSERT_MSG_EQ(linkId.has_value(), true, "No link found for PHY ID " << +phyId);
275 m_txPsdus.push_back({Simulator::Now(), psduMap, txVector, *linkId, phyId});
276
277 auto txDuration =
278 WifiPhy::CalculateTxDuration(psduMap, txVector, mac->GetWifiPhy(*linkId)->GetPhyBand());
279
280 for (const auto& [aid, psdu] : psduMap)
281 {
282 std::stringstream ss;
283 ss << std::setprecision(10) << "PSDU #" << m_txPsdus.size() << " Link ID "
284 << +linkId.value() << " Phy ID " << +phyId << " " << psdu->GetHeader(0).GetTypeString();
285 if (psdu->GetHeader(0).IsAction())
286 {
287 ss << " ";
288 WifiActionHeader actionHdr;
289 psdu->GetPayload(0)->PeekHeader(actionHdr);
290 actionHdr.Print(ss);
291 }
292 ss << " #MPDUs " << psdu->GetNMpdus() << " duration/ID " << psdu->GetHeader(0).GetDuration()
293 << " RA = " << psdu->GetAddr1() << " TA = " << psdu->GetAddr2()
294 << " ADDR3 = " << psdu->GetHeader(0).GetAddr3()
295 << " ToDS = " << psdu->GetHeader(0).IsToDs()
296 << " FromDS = " << psdu->GetHeader(0).IsFromDs();
297 if (psdu->GetHeader(0).IsQosData())
298 {
299 ss << " seqNo = {";
300 for (auto& mpdu : *PeekPointer(psdu))
301 {
302 ss << mpdu->GetHeader().GetSequenceNumber() << ",";
303 }
304 ss << "} TID = " << +psdu->GetHeader(0).GetQosTid();
305 }
306 NS_LOG_INFO(ss.str());
307 }
308 NS_LOG_INFO("TX duration = " << txDuration.As(Time::MS) << " TXVECTOR = " << txVector << "\n");
309}
310
311void
313{
316 int64_t streamNumber = 100;
317
318 NodeContainer wifiApNode(1);
319 NodeContainer wifiStaNodes(m_nEmlsrStations);
320
321 WifiHelper wifi;
322 // wifi.EnableLogComponents ();
323 wifi.SetStandard(WIFI_STANDARD_80211be);
324 wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
325 "DataMode",
326 StringValue("EhtMcs0"),
327 "ControlMode",
328 StringValue("HtMcs0"));
329 wifi.ConfigEhtOptions("EmlsrActivated",
330 BooleanValue(true),
331 "TransitionTimeout",
333
334 // MLDs are configured with three links
335 SpectrumWifiPhyHelper phyHelper(3);
337 phyHelper.Set(0, "ChannelSettings", StringValue("{2, 0, BAND_2_4GHZ, 0}"));
338 phyHelper.Set(1, "ChannelSettings", StringValue("{36, 0, BAND_5GHZ, 0}"));
339 phyHelper.Set(2, "ChannelSettings", StringValue("{1, 0, BAND_6GHZ, 0}"));
340 // Add three spectrum channels to use multi-RF interface
341 phyHelper.AddChannel(CreateObject<MultiModelSpectrumChannel>(), WIFI_SPECTRUM_2_4_GHZ);
342 phyHelper.AddChannel(CreateObject<MultiModelSpectrumChannel>(), WIFI_SPECTRUM_5_GHZ);
343 phyHelper.AddChannel(CreateObject<MultiModelSpectrumChannel>(), WIFI_SPECTRUM_6_GHZ);
344
345 WifiMacHelper mac;
346 mac.SetType("ns3::ApWifiMac",
347 "Ssid",
348 SsidValue(Ssid("ns-3-ssid")),
349 "BeaconGeneration",
350 BooleanValue(true));
351
352 NetDeviceContainer apDevice = wifi.Install(phyHelper, mac, wifiApNode);
353
354 mac.SetType("ns3::StaWifiMac",
355 "Ssid",
356 SsidValue(Ssid("wrong-ssid")),
357 "ActiveProbing",
358 BooleanValue(false));
359 mac.SetEmlsrManager("ns3::DefaultEmlsrManager",
360 "EmlsrLinkSet",
362 "MainPhyId",
364
365 NetDeviceContainer staDevices = wifi.Install(phyHelper, mac, wifiStaNodes);
366
367 m_apMac = DynamicCast<ApWifiMac>(DynamicCast<WifiNetDevice>(apDevice.Get(0))->GetMac());
368
369 for (uint32_t i = 0; i < staDevices.GetN(); i++)
370 {
371 auto device = DynamicCast<WifiNetDevice>(staDevices.Get(i));
372 auto staMac = DynamicCast<StaWifiMac>(device->GetMac());
373 NS_ASSERT_MSG(i < m_paddingDelay.size(), "Not enough padding delay values provided");
374 staMac->GetEmlsrManager()->SetAttribute("EmlsrPaddingDelay",
376 NS_ASSERT_MSG(i < m_transitionDelay.size(), "Not enough transition delay values provided");
377 staMac->GetEmlsrManager()->SetAttribute("EmlsrTransitionDelay",
379 }
380
381 if (m_nNonEmlsrStations > 0)
382 {
383 // create the other non-AP MLDs for which EMLSR is not activated
384 wifi.ConfigEhtOptions("EmlsrActivated", BooleanValue(false));
385 NodeContainer otherStaNodes(m_nNonEmlsrStations);
386 staDevices.Add(wifi.Install(phyHelper, mac, otherStaNodes));
387 wifiStaNodes.Add(otherStaNodes);
388 }
389
390 for (uint32_t i = 0; i < staDevices.GetN(); i++)
391 {
392 auto device = DynamicCast<WifiNetDevice>(staDevices.Get(i));
393 m_staMacs.push_back(DynamicCast<StaWifiMac>(device->GetMac()));
394 }
395
396 // Trace PSDUs passed to the PHY on AP MLD and non-AP MLDs
397 for (uint8_t phyId = 0; phyId < m_apMac->GetDevice()->GetNPhys(); phyId++)
398 {
400 "/NodeList/0/DeviceList/*/$ns3::WifiNetDevice/Phys/" + std::to_string(phyId) +
401 "/PhyTxPsduBegin",
403 }
404 for (std::size_t i = 0; i < m_nEmlsrStations + m_nNonEmlsrStations; i++)
405 {
406 for (uint8_t phyId = 0; phyId < m_staMacs[i]->GetDevice()->GetNPhys(); phyId++)
407 {
409 "/NodeList/" + std::to_string(i + 1) + "/DeviceList/*/$ns3::WifiNetDevice/Phys/" +
410 std::to_string(phyId) + "/PhyTxPsduBegin",
412 }
413 }
414
415 // Uncomment the lines below to write PCAP files
416 // phyHelper.EnablePcap("wifi-emlsr_AP", apDevice);
417 // phyHelper.EnablePcap("wifi-emlsr_STA", staDevices);
418
419 // Assign fixed streams to random variables in use
420 streamNumber += wifi.AssignStreams(apDevice, streamNumber);
421 streamNumber += wifi.AssignStreams(staDevices, streamNumber);
422
423 MobilityHelper mobility;
424 Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();
425
426 for (std::size_t id = 0; id <= m_nEmlsrStations + m_nNonEmlsrStations; id++)
427 {
428 // all non-AP MLDs are co-located
429 positionAlloc->Add(Vector(std::min<double>(id, 1), 0.0, 0.0));
430 }
431 mobility.SetPositionAllocator(positionAlloc);
432
433 mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
434 mobility.Install(wifiApNode);
435 mobility.Install(wifiStaNodes);
436
437 // install packet socket on all nodes
438 PacketSocketHelper packetSocket;
439 packetSocket.Install(wifiApNode);
440 packetSocket.Install(wifiStaNodes);
441
442 // install a packet socket server on all nodes
443 for (auto nodeIt = NodeList::Begin(); nodeIt != NodeList::End(); nodeIt++)
444 {
445 PacketSocketAddress srvAddr;
446 auto device = DynamicCast<WifiNetDevice>((*nodeIt)->GetDevice(0));
447 NS_TEST_ASSERT_MSG_NE(device, nullptr, "Expected a WifiNetDevice");
448 srvAddr.SetSingleDevice(device->GetIfIndex());
449 srvAddr.SetProtocol(1);
450
451 auto server = CreateObject<PacketSocketServer>();
452 server->SetLocal(srvAddr);
453 (*nodeIt)->AddApplication(server);
454 server->SetStartTime(Seconds(0)); // now
455 server->SetStopTime(m_duration);
456 }
457
458 // set DL and UL packet sockets
459 for (const auto& staMac : m_staMacs)
460 {
461 m_dlSockets.emplace_back();
462 m_dlSockets.back().SetSingleDevice(m_apMac->GetDevice()->GetIfIndex());
463 m_dlSockets.back().SetPhysicalAddress(staMac->GetDevice()->GetAddress());
464 m_dlSockets.back().SetProtocol(1);
465
466 m_ulSockets.emplace_back();
467 m_ulSockets.back().SetSingleDevice(staMac->GetDevice()->GetIfIndex());
468 m_ulSockets.back().SetPhysicalAddress(m_apMac->GetDevice()->GetAddress());
469 m_ulSockets.back().SetProtocol(1);
470 }
471
472 // schedule ML setup for one station at a time
473 m_apMac->TraceConnectWithoutContext("AssociatedSta",
475 Simulator::Schedule(Seconds(0), [&]() { m_staMacs[0]->SetSsid(Ssid("ns-3-ssid")); });
476}
477
480 std::size_t staId,
481 std::size_t count,
482 std::size_t pktSize) const
483{
484 auto client = CreateObject<PacketSocketClient>();
485 client->SetAttribute("PacketSize", UintegerValue(pktSize));
486 client->SetAttribute("MaxPackets", UintegerValue(count));
487 client->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
488 client->SetRemote(dir == DOWNLINK ? m_dlSockets.at(staId) : m_ulSockets.at(staId));
489 client->SetStartTime(Seconds(0)); // now
490 client->SetStopTime(m_duration - Simulator::Now());
491
492 return client;
493}
494
495void
497{
498 if (m_lastAid == aid)
499 {
500 // another STA of this non-AP MLD has already fired this callback
501 return;
502 }
503 m_lastAid = aid;
504
505 // wait some time (5ms) to allow the completion of association
506 auto delay = MilliSeconds(5);
507
508 if (m_establishBaDl)
509 {
510 // trigger establishment of BA agreement with AP as originator
511 Simulator::Schedule(delay, [=]() {
512 m_apMac->GetDevice()->GetNode()->AddApplication(
513 GetApplication(DOWNLINK, aid - 1, 4, 1000));
514 });
515
516 delay += MilliSeconds(5);
517 }
518
519 if (m_establishBaUl)
520 {
521 // trigger establishment of BA agreement with AP as recipient
522 Simulator::Schedule(delay, [=]() {
523 m_staMacs[aid - 1]->GetDevice()->GetNode()->AddApplication(
524 GetApplication(UPLINK, aid - 1, 4, 1000));
525 });
526
527 delay += MilliSeconds(5);
528 }
529
530 Simulator::Schedule(delay, [=]() {
532 {
533 // make the next STA start ML discovery & setup
534 m_staMacs[aid]->SetSsid(Ssid("ns-3-ssid"));
535 return;
536 }
537 // all stations associated; start traffic if needed
538 StartTraffic();
539 });
540}
541
542void
544 Mac48Address dest,
545 uint8_t linkId,
547 bool blocked,
548 std::string description,
549 bool testUnblockedForOtherReasons)
550{
552 auto mask = mac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, linkId);
553 NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
554 true,
555 description << ": Expected to find a mask for EMLSR link " << +linkId);
556 if (blocked)
557 {
558 NS_TEST_EXPECT_MSG_EQ(mask->test(static_cast<std::size_t>(reason)),
559 true,
560 description << ": Expected EMLSR link " << +linkId
561 << " to be blocked for reason " << reason);
562 if (testUnblockedForOtherReasons)
563 {
564 NS_TEST_EXPECT_MSG_EQ(mask->count(),
565 1,
566 description << ": Expected EMLSR link " << +linkId
567 << " to be blocked for one reason only");
568 }
569 }
570 else if (testUnblockedForOtherReasons)
571 {
572 NS_TEST_EXPECT_MSG_EQ(mask->none(),
573 true,
574 description << ": Expected EMLSR link " << +linkId
575 << " to be unblocked");
576 }
577 else
578 {
579 NS_TEST_EXPECT_MSG_EQ(mask->test(static_cast<std::size_t>(reason)),
580 false,
581 description << ": Expected EMLSR link " << +linkId
582 << " to be unblocked for reason " << reason);
583 }
584}
585
609{
610 public:
617 EmlOmnExchangeTest(const std::set<uint8_t>& linksToEnableEmlsrOn, Time transitionTimeout);
618 ~EmlOmnExchangeTest() override = default;
619
620 protected:
621 void DoSetup() override;
622 void DoRun() override;
623 void Transmit(Ptr<WifiMac> mac,
624 uint8_t phyId,
625 WifiConstPsduMap psduMap,
626 WifiTxVector txVector,
627 double txPowerW) override;
628
634 void TxOk(Ptr<const WifiMpdu> mpdu);
642
652 const WifiTxVector& txVector,
653 uint8_t linkId);
663 const WifiTxVector& txVector,
664 uint8_t linkId);
673 const WifiTxVector& txVector,
674 uint8_t linkId);
678 void CheckEmlsrLinks();
679
680 private:
689 std::list<uint64_t> m_uidList;
690};
691
692EmlOmnExchangeTest::EmlOmnExchangeTest(const std::set<uint8_t>& linksToEnableEmlsrOn,
693 Time transitionTimeout)
694 : EmlsrOperationsTestBase("Check EML Notification exchange"),
695 m_checkEmlsrLinksCount(0),
696 m_emlNotificationDroppedCount(0)
697{
698 m_linksToEnableEmlsrOn = linksToEnableEmlsrOn;
701 m_transitionTimeout = transitionTimeout;
702 m_duration = Seconds(0.5);
703}
704
705void
707{
709
710 m_errorModel = CreateObject<ListErrorModel>();
711 for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
712 {
714 }
715
716 m_staMacs[0]->TraceConnectWithoutContext("AckedMpdu",
718 m_staMacs[0]->TraceConnectWithoutContext("DroppedMpdu",
720}
721
722void
724 uint8_t phyId,
725 WifiConstPsduMap psduMap,
726 WifiTxVector txVector,
727 double txPowerW)
728{
729 EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
730 auto linkId = m_txPsdus.back().linkId;
731
732 auto psdu = psduMap.begin()->second;
733
734 switch (psdu->GetHeader(0).GetType())
735 {
737 NS_TEST_EXPECT_MSG_EQ(+linkId, +m_mainPhyId, "AssocReq not sent by the main PHY");
738 CheckEmlCapabilitiesInAssocReq(*psdu->begin(), txVector, linkId);
739 break;
740
742 CheckEmlCapabilitiesInAssocResp(*psdu->begin(), txVector, linkId);
743 break;
744
746 if (auto [category, action] = WifiActionHeader::Peek(psdu->GetPayload(0));
748 action.protectedEhtAction ==
750 {
751 CheckEmlNotification(psdu, txVector, linkId);
752
754 m_staMacs[0]->GetLinkIdByAddress(psdu->GetAddr2()) == linkId)
755 {
756 // transmitted by non-AP MLD, we need to corrupt it
757 m_uidList.push_front(psdu->GetPacket()->GetUid());
759 }
760 break;
761 }
762
763 default:;
764 }
765}
766
767void
769 const WifiTxVector& txVector,
770 uint8_t linkId)
771{
773 mpdu->GetPacket()->PeekHeader(frame);
774
775 const auto& mle = frame.Get<MultiLinkElement>();
776 NS_TEST_ASSERT_MSG_EQ(mle.has_value(), true, "Multi-Link Element must be present in AssocReq");
777
778 NS_TEST_ASSERT_MSG_EQ(mle->HasEmlCapabilities(),
779 true,
780 "Multi-Link Element in AssocReq must have EML Capabilities");
781 NS_TEST_ASSERT_MSG_EQ(mle->IsEmlsrSupported(),
782 true,
783 "EML Support subfield of EML Capabilities in AssocReq must be set to 1");
784 NS_TEST_ASSERT_MSG_EQ(mle->GetEmlsrPaddingDelay(),
785 m_paddingDelay.at(0),
786 "Unexpected Padding Delay in EML Capabilities included in AssocReq");
787 NS_TEST_ASSERT_MSG_EQ(mle->GetEmlsrTransitionDelay(),
788 m_transitionDelay.at(0),
789 "Unexpected Transition Delay in EML Capabilities included in AssocReq");
790}
791
792void
794 const WifiTxVector& txVector,
795 uint8_t linkId)
796{
797 bool sentToEmlsrClient =
798 (m_staMacs[0]->GetLinkIdByAddress(mpdu->GetHeader().GetAddr1()) == linkId);
799
800 if (!sentToEmlsrClient)
801 {
802 // nothing to check
803 return;
804 }
805
807 mpdu->GetPacket()->PeekHeader(frame);
808
809 const auto& mle = frame.Get<MultiLinkElement>();
810 NS_TEST_ASSERT_MSG_EQ(mle.has_value(), true, "Multi-Link Element must be present in AssocResp");
811
812 NS_TEST_ASSERT_MSG_EQ(mle->HasEmlCapabilities(),
813 true,
814 "Multi-Link Element in AssocResp must have EML Capabilities");
815 NS_TEST_ASSERT_MSG_EQ(mle->IsEmlsrSupported(),
816 true,
817 "EML Support subfield of EML Capabilities in AssocResp must be set to 1");
819 mle->GetTransitionTimeout(),
821 "Unexpected Transition Timeout in EML Capabilities included in AssocResp");
822}
823
824void
826 const WifiTxVector& txVector,
827 uint8_t linkId)
828{
829 MgtEmlOmn frame;
830 auto mpdu = *psdu->begin();
831 auto pkt = mpdu->GetPacket()->Copy();
833 pkt->RemoveHeader(frame);
834 NS_LOG_DEBUG(frame);
835
836 bool sentbyNonApMld = m_staMacs[0]->GetLinkIdByAddress(mpdu->GetHeader().GetAddr2()) == linkId;
837
839 1,
840 "EMLSR Mode subfield should be set to 1 (frame sent by non-AP MLD: "
841 << std::boolalpha << sentbyNonApMld << ")");
842
844 0,
845 "EMLMR Mode subfield should be set to 0 (frame sent by non-AP MLD: "
846 << std::boolalpha << sentbyNonApMld << ")");
847
849 true,
850 "Link Bitmap subfield should be present (frame sent by non-AP MLD: "
851 << std::boolalpha << sentbyNonApMld << ")");
852
853 auto setupLinks = m_staMacs[0]->GetSetupLinkIds();
854 std::list<uint8_t> expectedEmlsrLinks;
855 std::set_intersection(setupLinks.begin(),
856 setupLinks.end(),
859 std::back_inserter(expectedEmlsrLinks));
860
861 NS_TEST_EXPECT_MSG_EQ((expectedEmlsrLinks == frame.GetLinkBitmap()),
862 true,
863 "Unexpected Link Bitmap subfield (frame sent by non-AP MLD: "
864 << std::boolalpha << sentbyNonApMld << ")");
865
866 if (!sentbyNonApMld)
867 {
868 // the frame has been sent by the AP MLD
871 0,
872 "EMLSR Parameter Update Control should be set to 0 in frames sent by the AP MLD");
873
874 // as soon as the non-AP MLD receives this frame, it sets the EMLSR links
875 auto delay = WifiPhy::CalculateTxDuration(psdu,
876 txVector,
877 m_staMacs[0]->GetWifiPhy(linkId)->GetPhyBand()) +
878 MicroSeconds(1); // to account for propagation delay
880 }
881
883 +linkId,
884 "EML Notification received on unexpected link (frame sent by non-AP MLD: "
885 << std::boolalpha << sentbyNonApMld << ")");
886}
887
888void
890{
891 const auto& hdr = mpdu->GetHeader();
892
893 if (hdr.IsMgt() && hdr.IsAction())
894 {
895 if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
897 action.protectedEhtAction ==
899 {
900 // the EML Operating Mode Notification frame that the non-AP MLD sent has been
901 // acknowledged; after the transition timeout, the EMLSR links have been set
904 this);
905 }
906 }
907}
908
909void
911{
912 const auto& hdr = mpdu->GetHeader();
913
914 if (hdr.IsMgt() && hdr.IsAction())
915 {
916 if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
918 action.protectedEhtAction ==
920 {
921 // the EML Operating Mode Notification frame has been dropped. Don't
922 // corrupt it anymore
924 }
925 }
926}
927
928void
930{
932
933 auto setupLinks = m_staMacs[0]->GetSetupLinkIds();
934 std::set<uint8_t> expectedEmlsrLinks;
935 std::set_intersection(setupLinks.begin(),
936 setupLinks.end(),
939 std::inserter(expectedEmlsrLinks, expectedEmlsrLinks.end()));
940
941 NS_TEST_EXPECT_MSG_EQ((expectedEmlsrLinks == m_staMacs[0]->GetEmlsrManager()->GetEmlsrLinks()),
942 true,
943 "Unexpected set of EMLSR links)");
944}
945
946void
948{
951
953 2,
954 "Unexpected number of times CheckEmlsrLinks() is called");
957 1,
958 "Unexpected number of times the EML Notification frame is dropped due to max retry limit");
959
961}
962
1015{
1016 public:
1029 EmlsrDlTxopTest(std::size_t nEmlsrStations,
1030 std::size_t nNonEmlsrStations,
1031 const std::set<uint8_t>& linksToEnableEmlsrOn,
1032 const std::vector<Time>& paddingDelay,
1033 const std::vector<Time>& transitionDelay,
1034 Time transitionTimeout);
1035 ~EmlsrDlTxopTest() override = default;
1036
1037 protected:
1038 void DoSetup() override;
1039 void DoRun() override;
1040 void Transmit(Ptr<WifiMac> mac,
1041 uint8_t phyId,
1042 WifiConstPsduMap psduMap,
1043 WifiTxVector txVector,
1044 double txPowerW) override;
1045
1049 void CheckResults();
1050
1057 void CheckPmModeAfterAssociation(const Mac48Address& address);
1058
1068 const WifiTxVector& txVector,
1069 uint8_t linkId);
1070
1080 const WifiTxVector& txVector,
1081 uint8_t linkId);
1082
1091 void CheckQosFrames(const WifiConstPsduMap& psduMap,
1092 const WifiTxVector& txVector,
1093 uint8_t linkId);
1094
1103 void CheckBlockAck(const WifiConstPsduMap& psduMap,
1104 const WifiTxVector& txVector,
1105 uint8_t phyId);
1106
1107 private:
1108 void StartTraffic() override;
1109
1113 void EnableEmlsrMode();
1114
1115 std::set<uint8_t> m_emlsrLinks;
1119 std::size_t m_countQoSframes;
1120 std::size_t m_countBlockAck;
1122};
1123
1124EmlsrDlTxopTest::EmlsrDlTxopTest(std::size_t nEmlsrStations,
1125 std::size_t nNonEmlsrStations,
1126 const std::set<uint8_t>& linksToEnableEmlsrOn,
1127 const std::vector<Time>& paddingDelay,
1128 const std::vector<Time>& transitionDelay,
1129 Time transitionTimeout)
1130 : EmlsrOperationsTestBase("Check EML DL TXOP transmissions (" + std::to_string(nEmlsrStations) +
1131 "," + std::to_string(nNonEmlsrStations) + ")"),
1132 m_emlsrLinks(linksToEnableEmlsrOn),
1133 m_emlsrEnabledTime(0),
1134 m_fe2to3delay(MilliSeconds(20)),
1135 m_countQoSframes(0),
1136 m_countBlockAck(0)
1137{
1138 m_nEmlsrStations = nEmlsrStations;
1139 m_nNonEmlsrStations = nNonEmlsrStations;
1140 m_linksToEnableEmlsrOn = {}; // do not enable EMLSR right after association
1141 m_mainPhyId = 1;
1142 m_paddingDelay = paddingDelay;
1143 m_transitionDelay = transitionDelay;
1144 m_transitionTimeout = transitionTimeout;
1145 m_establishBaDl = true;
1146 m_duration = Seconds(1.5);
1147
1148 NS_ABORT_MSG_IF(linksToEnableEmlsrOn.size() < 2,
1149 "This test requires at least two links to be configured as EMLSR links");
1150}
1151
1152void
1154 uint8_t phyId,
1155 WifiConstPsduMap psduMap,
1156 WifiTxVector txVector,
1157 double txPowerW)
1158{
1159 EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
1160 auto linkId = m_txPsdus.back().linkId;
1161
1162 auto psdu = psduMap.begin()->second;
1163 auto nodeId = mac->GetDevice()->GetNode()->GetId();
1164
1165 switch (psdu->GetHeader(0).GetType())
1166 {
1168 NS_ASSERT_MSG(nodeId > 0, "APs do not send AssocReq frames");
1169 if (nodeId <= m_nEmlsrStations)
1170 {
1171 NS_TEST_EXPECT_MSG_EQ(+linkId, +m_mainPhyId, "AssocReq not sent by the main PHY");
1172 // this AssocReq is being sent by an EMLSR client. The other EMLSR links should be
1173 // in powersave mode after association; we let the non-EMLSR links transition to
1174 // active mode (by sending data null frames) after association
1175 for (const auto id : m_staMacs.at(nodeId - 1)->GetLinkIds())
1176 {
1177 if (id != linkId && m_emlsrLinks.count(id) == 1)
1178 {
1179 m_staMacs[nodeId - 1]->SetPowerSaveMode({true, id});
1180 }
1181 }
1182 }
1183 break;
1184
1185 case WIFI_MAC_MGT_ACTION: {
1186 auto [category, action] = WifiActionHeader::Peek(psdu->GetPayload(0));
1187
1188 if (nodeId == 0 && category == WifiActionHeader::PROTECTED_EHT &&
1189 action.protectedEhtAction ==
1191 {
1192 CheckEmlNotificationFrame(*psdu->begin(), txVector, linkId);
1193 }
1194 else if (category == WifiActionHeader::BLOCK_ACK &&
1196 {
1197 CheckPmModeAfterAssociation(psdu->GetAddr1());
1198 }
1199 }
1200 break;
1201
1203 CheckInitialControlFrame(*psdu->begin(), txVector, linkId);
1204 break;
1205
1206 case WIFI_MAC_QOSDATA:
1207 CheckQosFrames(psduMap, txVector, linkId);
1208 break;
1209
1211 CheckBlockAck(psduMap, txVector, phyId);
1212 break;
1213
1214 default:;
1215 }
1216}
1217
1218void
1220{
1222
1223 m_errorModel = CreateObject<ListErrorModel>();
1224 for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
1225 {
1227 }
1228
1230 {MicroSeconds(3200), MicroSeconds(3200), MicroSeconds(3200)});
1231
1233 {
1234 auto muScheduler =
1235 CreateObjectWithAttributes<RrMultiUserScheduler>("EnableUlOfdma", BooleanValue(false));
1236 m_apMac->AggregateObject(muScheduler);
1237 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
1238 {
1239 m_apMac->GetFrameExchangeManager(linkId)->GetAckManager()->SetAttribute(
1240 "DlMuAckSequenceType",
1242 }
1243 }
1244}
1245
1246void
1248{
1250 {
1251 // we are done with association and Block Ack agreement; we can now enable EMLSR mode
1252 m_lastAid = 0;
1254 return;
1255 }
1256
1257 // we are done with sending EML Operating Mode Notification frames. We can now generate
1258 // packets for all non-AP MLDs
1259 for (std::size_t i = 0; i < m_nEmlsrStations + m_nNonEmlsrStations; i++)
1260 {
1261 // when multiple non-AP MLDs are present, MU transmission are used. Given that the
1262 // available bandwidth decreases as the number of non-AP MLDs increases, compute the
1263 // number of packets to generate so that we always have two A-MPDUs per non-AP MLD
1264 std::size_t count = 8 / (m_nEmlsrStations + m_nNonEmlsrStations);
1266 }
1267
1268 // in case of 2 EMLSR clients using no non-EMLSR link, generate one additional short
1269 // packet to each EMLSR client to test transition delay
1270 if (m_nEmlsrStations == 2 && m_apMac->GetNLinks() == m_emlsrLinks.size())
1271 {
1273 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, 0, 1, 40));
1274 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, 1, 1, 40));
1275 });
1276 }
1277
1278 // schedule the transmission of EML Operating Mode Notification frames to disable EMLSR mode
1279 // and the generation of other packets destined to the EMLSR clients
1280 for (std::size_t id = 0; id < m_nEmlsrStations; id++)
1281 {
1282 Simulator::Schedule(m_fe2to3delay + MilliSeconds(5 * (id + 1)), [=]() {
1283 m_staMacs.at(id)->GetEmlsrManager()->SetAttribute(
1284 "EmlsrLinkSet",
1286 });
1287
1289 m_apMac->GetDevice()->GetNode()->AddApplication(
1291 });
1292 }
1293}
1294
1295void
1297{
1298 m_staMacs.at(m_lastAid)->GetEmlsrManager()->SetAttribute(
1299 "EmlsrLinkSet",
1301 m_lastAid++;
1304 {
1305 // make the next STA send EML Notification frame
1307 return;
1308 }
1309 // all stations enabled EMLSR mode; start traffic
1311 StartTraffic();
1312 });
1313}
1314
1315void
1317{
1318 auto psduIt = m_txPsdus.cbegin();
1319
1320 // lambda to jump to the next QoS data frame or MU-RTS Trigger Frame transmitted
1321 // to an EMLSR client
1322 auto jumpToQosDataOrMuRts = [&]() {
1323 while (psduIt != m_txPsdus.cend() &&
1324 !psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData())
1325 {
1326 auto psdu = psduIt->psduMap.cbegin()->second;
1327 if (psdu->GetHeader(0).IsTrigger())
1328 {
1329 CtrlTriggerHeader trigger;
1330 psdu->GetPayload(0)->PeekHeader(trigger);
1331 if (trigger.IsMuRts())
1332 {
1333 break;
1334 }
1335 }
1336 psduIt++;
1337 }
1338 };
1339
1392 for (std::size_t i = 0; i < m_nEmlsrStations + m_nNonEmlsrStations; i++)
1393 {
1394 std::set<uint8_t> linkIds;
1395
1396 jumpToQosDataOrMuRts();
1397 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend() &&
1398 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData()),
1399 true,
1400 "Expected at least one QoS data frame before enabling EMLSR mode");
1401 linkIds.insert(psduIt->linkId);
1402 const auto firstAmpduTxEnd =
1403 psduIt->startTx +
1404 WifiPhy::CalculateTxDuration(psduIt->psduMap,
1405 psduIt->txVector,
1406 m_staMacs[i]->GetWifiPhy(psduIt->linkId)->GetPhyBand());
1407 psduIt++;
1408
1409 jumpToQosDataOrMuRts();
1410 NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend() &&
1411 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData()),
1412 true,
1413 "Expected at least two QoS data frames before enabling EMLSR mode");
1414 linkIds.insert(psduIt->linkId);
1415 const auto secondAmpduTxStart = psduIt->startTx;
1416 psduIt++;
1417
1423 auto setupLinks = m_staMacs[i]->GetSetupLinkIds();
1424 if (i < m_nEmlsrStations &&
1425 std::none_of(setupLinks.begin(), setupLinks.end(), [&](auto&& linkId) {
1426 return linkId != m_mainPhyId && m_emlsrLinks.count(linkId) == 0;
1427 }))
1428 {
1429 NS_TEST_EXPECT_MSG_EQ(linkIds.size(),
1430 1,
1431 "Expected both A-MPDUs to be sent on the same link");
1432 NS_TEST_EXPECT_MSG_EQ(*linkIds.begin(), +m_mainPhyId, "A-MPDUs sent on incorrect link");
1433 NS_TEST_EXPECT_MSG_LT(firstAmpduTxEnd,
1434 secondAmpduTxStart,
1435 "A-MPDUs are not sent one after another");
1436 }
1441 else
1442 {
1443 NS_TEST_EXPECT_MSG_EQ(linkIds.size(),
1444 2,
1445 "Expected A-MPDUs to be sent on distinct links");
1446 NS_TEST_EXPECT_MSG_GT(firstAmpduTxEnd,
1447 secondAmpduTxStart,
1448 "A-MPDUs are not sent concurrently");
1449 }
1450 }
1451
1514 using FrameExchange = std::list<decltype(psduIt)>;
1515
1516 std::vector<std::list<FrameExchange>> frameExchanges(m_nEmlsrStations);
1517
1518 // compute all frame exchanges involving EMLSR clients
1519 while (psduIt != m_txPsdus.cend())
1520 {
1521 jumpToQosDataOrMuRts();
1522 if (psduIt == m_txPsdus.cend())
1523 {
1524 break;
1525 }
1526
1527 if (IsTrigger(psduIt->psduMap))
1528 {
1529 CtrlTriggerHeader trigger;
1530 psduIt->psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
1531 // this is an MU-RTS TF starting a new frame exchange sequence; add it to all
1532 // the addressed EMLSR clients
1534 true,
1535 "jumpToQosDataOrMuRts does not return TFs other than MU-RTS");
1536 for (const auto& userInfo : trigger)
1537 {
1538 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1539 {
1540 if (m_staMacs.at(i)->GetAssociationId() == userInfo.GetAid12())
1541 {
1542 frameExchanges.at(i).emplace_back(FrameExchange{psduIt});
1543 break;
1544 }
1545 }
1546 }
1547 psduIt++;
1548 continue;
1549 }
1550
1551 // we get here if psduIt points to a psduMap containing QoS data frame(s); find (if any)
1552 // the QoS data frame(s) addressed to EMLSR clients and add them to the appropriate
1553 // frame exchange sequence
1554 for (const auto& staIdPsduPair : psduIt->psduMap)
1555 {
1556 std::for_each_n(m_staMacs.cbegin(), m_nEmlsrStations, [&](auto&& staMac) {
1557 if (!staMac->GetLinkIdByAddress(staIdPsduPair.second->GetAddr1()))
1558 {
1559 // not addressed to this non-AP MLD
1560 return;
1561 }
1562 // a QoS data frame starts a new frame exchange sequence if there is no previous
1563 // MU-RTS TF that has been sent on the same link and is not already followed by
1564 // a QoS data frame
1565 std::size_t id = staMac->GetDevice()->GetNode()->GetId() - 1;
1566 for (auto& frameExchange : frameExchanges.at(id))
1567 {
1568 if (IsTrigger(frameExchange.front()->psduMap) &&
1569 frameExchange.front()->linkId == psduIt->linkId &&
1570 frameExchange.size() == 1)
1571 {
1572 auto it = std::next(frameExchange.front());
1573 while (it != m_txPsdus.end())
1574 {
1575 // stop at the first frame other than CTS sent on this link
1576 if (it->linkId == psduIt->linkId &&
1577 !it->psduMap.begin()->second->GetHeader(0).IsCts())
1578 {
1579 break;
1580 }
1581 ++it;
1582 }
1583 if (it == psduIt)
1584 {
1585 // the QoS data frame actually followed the MU-RTS TF
1586 frameExchange.emplace_back(psduIt);
1587 return;
1588 }
1589 }
1590 }
1591 frameExchanges.at(id).emplace_back(FrameExchange{psduIt});
1592 });
1593 }
1594 psduIt++;
1595 }
1596
1603 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1604 {
1605 NS_TEST_EXPECT_MSG_GT_OR_EQ(frameExchanges.at(i).size(),
1606 2,
1607 "Expected at least 2 frame exchange sequences "
1608 << "involving EMLSR client " << i);
1609
1610 auto firstExchangeIt = frameExchanges.at(i).begin();
1611 auto secondExchangeIt = std::next(firstExchangeIt);
1612
1613 const auto firstAmpduTxEnd =
1614 firstExchangeIt->back()->startTx +
1616 firstExchangeIt->back()->psduMap,
1617 firstExchangeIt->back()->txVector,
1618 m_staMacs[i]->GetWifiPhy(firstExchangeIt->back()->linkId)->GetPhyBand());
1619 const auto secondAmpduTxStart = secondExchangeIt->front()->startTx;
1620
1621 if (m_staMacs[i]->GetNLinks() == m_emlsrLinks.size())
1622 {
1623 // all links are EMLSR links
1624 NS_TEST_EXPECT_MSG_EQ(IsTrigger(firstExchangeIt->front()->psduMap),
1625 true,
1626 "Expected an MU-RTS TF as ICF of first frame exchange sequence");
1628 firstExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1629 true,
1630 "Expected a QoS data frame in the first frame exchange sequence");
1631
1632 NS_TEST_EXPECT_MSG_EQ(IsTrigger(secondExchangeIt->front()->psduMap),
1633 true,
1634 "Expected an MU-RTS TF as ICF of second frame exchange sequence");
1636 secondExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1637 true,
1638 "Expected a QoS data frame in the second frame exchange sequence");
1639
1640 NS_TEST_EXPECT_MSG_LT(firstAmpduTxEnd,
1641 secondAmpduTxStart,
1642 "A-MPDUs are not sent one after another");
1643 }
1644 else
1645 {
1646 std::vector<uint8_t> nonEmlsrIds;
1647 auto setupLinks = m_staMacs[i]->GetSetupLinkIds();
1648 std::set_difference(setupLinks.begin(),
1649 setupLinks.end(),
1650 m_emlsrLinks.begin(),
1651 m_emlsrLinks.end(),
1652 std::back_inserter(nonEmlsrIds));
1653 NS_TEST_ASSERT_MSG_EQ(nonEmlsrIds.size(), 1, "Unexpected number of non-EMLSR links");
1654
1655 auto nonEmlsrLinkExchangeIt = firstExchangeIt->front()->linkId == nonEmlsrIds[0]
1656 ? firstExchangeIt
1657 : secondExchangeIt;
1658 NS_TEST_EXPECT_MSG_EQ(IsTrigger(nonEmlsrLinkExchangeIt->front()->psduMap),
1659 false,
1660 "Did not expect an MU-RTS TF as ICF on non-EMLSR link");
1662 nonEmlsrLinkExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1663 true,
1664 "Expected a QoS data frame on the non-EMLSR link");
1665
1666 auto emlsrLinkExchangeIt =
1667 nonEmlsrLinkExchangeIt == firstExchangeIt ? secondExchangeIt : firstExchangeIt;
1668 NS_TEST_EXPECT_MSG_NE(+emlsrLinkExchangeIt->front()->linkId,
1669 +nonEmlsrIds[0],
1670 "Expected this exchange not to occur on non-EMLSR link");
1671 NS_TEST_EXPECT_MSG_EQ(IsTrigger(emlsrLinkExchangeIt->front()->psduMap),
1672 true,
1673 "Expected an MU-RTS TF as ICF on the EMLSR link");
1675 emlsrLinkExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1676 true,
1677 "Expected a QoS data frame on the EMLSR link");
1678
1679 NS_TEST_EXPECT_MSG_GT(firstAmpduTxEnd,
1680 secondAmpduTxStart,
1681 "A-MPDUs are not sent concurrently");
1682 }
1683
1684 // we are done with processing the first two frame exchanges, remove them
1685 frameExchanges.at(i).erase(firstExchangeIt);
1686 frameExchanges.at(i).erase(secondExchangeIt);
1687 }
1688
1714 if (m_nEmlsrStations == 2 && m_apMac->GetNLinks() == m_emlsrLinks.size())
1715 {
1716 // the following checks are only done with 2 EMLSR clients having no non-EMLSR link
1717 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1718 {
1719 NS_TEST_EXPECT_MSG_GT_OR_EQ(frameExchanges.at(i).size(),
1720 2,
1721 "Expected at least 2 frame exchange sequences "
1722 << "involving EMLSR client " << i);
1723 // the first frame exchange must start with an ICF
1724 auto firstExchangeIt = frameExchanges.at(i).begin();
1725
1726 NS_TEST_EXPECT_MSG_EQ(IsTrigger(firstExchangeIt->front()->psduMap),
1727 true,
1728 "Expected an MU-RTS TF as ICF of first frame exchange sequence");
1730 firstExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1731 true,
1732 "Expected a QoS data frame in the first frame exchange sequence");
1733 }
1734
1735 // the second frame exchange is the one that starts first
1736 auto secondExchangeIt = std::next(frameExchanges.at(0).begin())->front()->startTx <
1737 std::next(frameExchanges.at(1).begin())->front()->startTx
1738 ? std::next(frameExchanges.at(0).begin())
1739 : std::next(frameExchanges.at(1).begin());
1740 decltype(secondExchangeIt) thirdExchangeIt;
1741 std::size_t thirdExchangeStaId;
1742
1743 if (secondExchangeIt == std::next(frameExchanges.at(0).begin()))
1744 {
1745 thirdExchangeIt = std::next(frameExchanges.at(1).begin());
1746 thirdExchangeStaId = 1;
1747 }
1748 else
1749 {
1750 thirdExchangeIt = std::next(frameExchanges.at(0).begin());
1751 thirdExchangeStaId = 0;
1752 }
1753
1754 // the second frame exchange is not protected by the ICF and starts a SIFS after the end
1755 // of the previous one
1756 NS_TEST_EXPECT_MSG_EQ(IsTrigger(secondExchangeIt->front()->psduMap),
1757 false,
1758 "Expected no ICF for the second frame exchange sequence");
1760 secondExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1761 true,
1762 "Expected a QoS data frame in the second frame exchange sequence");
1763
1764 // the first two frame exchanges occur on the same link
1765 NS_TEST_EXPECT_MSG_EQ(+secondExchangeIt->front()->linkId,
1766 +frameExchanges.at(0).begin()->front()->linkId,
1767 "Expected the first two frame exchanges to occur on the same link");
1768
1769 auto bAckRespIt = std::prev(secondExchangeIt->front());
1770 NS_TEST_EXPECT_MSG_EQ(bAckRespIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAck(),
1771 true,
1772 "Expected a BlockAck response before the second frame exchange");
1773 auto bAckRespTxEnd =
1774 bAckRespIt->startTx +
1775 WifiPhy::CalculateTxDuration(bAckRespIt->psduMap,
1776 bAckRespIt->txVector,
1777 m_apMac->GetWifiPhy(bAckRespIt->linkId)->GetPhyBand());
1778
1779 // the second frame exchange starts a SIFS after the previous one
1781 bAckRespTxEnd + m_apMac->GetWifiPhy(bAckRespIt->linkId)->GetSifs(),
1782 secondExchangeIt->front()->startTx,
1783 "Expected the second frame exchange to start a SIFS after the first one");
1784
1785 // the third frame exchange is protected by MU-RTS and occurs on a different link
1786 NS_TEST_EXPECT_MSG_EQ(IsTrigger(thirdExchangeIt->front()->psduMap),
1787 true,
1788 "Expected an MU-RTS as ICF for the third frame exchange sequence");
1790 thirdExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1791 true,
1792 "Expected a QoS data frame in the third frame exchange sequence");
1793
1795 +secondExchangeIt->front()->linkId,
1796 +thirdExchangeIt->front()->linkId,
1797 "Expected the second and third frame exchanges to occur on distinct links");
1798
1799 auto secondQosIt = secondExchangeIt->front();
1800 auto secondQosTxEnd =
1801 secondQosIt->startTx +
1802 WifiPhy::CalculateTxDuration(secondQosIt->psduMap,
1803 secondQosIt->txVector,
1804 m_apMac->GetWifiPhy(secondQosIt->linkId)->GetPhyBand());
1805
1806 NS_TEST_EXPECT_MSG_GT_OR_EQ(thirdExchangeIt->front()->startTx,
1807 secondQosTxEnd + m_transitionDelay.at(thirdExchangeStaId),
1808 "Transmission started before transition delay");
1809
1810 // the BlockAck of the third frame exchange is not received correctly, so there should be
1811 // another frame exchange
1812 NS_TEST_EXPECT_MSG_EQ((thirdExchangeIt != frameExchanges.at(thirdExchangeStaId).end()),
1813 true,
1814 "Expected a fourth frame exchange");
1815 auto fourthExchangeIt = std::next(thirdExchangeIt);
1816
1817 // the fourth frame exchange is protected by MU-RTS
1818 NS_TEST_EXPECT_MSG_EQ(IsTrigger(fourthExchangeIt->front()->psduMap),
1819 true,
1820 "Expected an MU-RTS as ICF for the fourth frame exchange sequence");
1821
1822 bAckRespIt = std::prev(fourthExchangeIt->front());
1823 NS_TEST_EXPECT_MSG_EQ(bAckRespIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAck(),
1824 true,
1825 "Expected a BlockAck response before the fourth frame exchange");
1826 auto phy = m_apMac->GetWifiPhy(bAckRespIt->linkId);
1827 bAckRespTxEnd = bAckRespIt->startTx + WifiPhy::CalculateTxDuration(bAckRespIt->psduMap,
1828 bAckRespIt->txVector,
1829 phy->GetPhyBand());
1830 auto timeout = phy->GetSifs() + phy->GetSlot() + MicroSeconds(20);
1831
1832 // the fourth frame exchange starts a SIFS after the previous one because the AP can
1833 // continue the TXOP despite it does not receive the BlockAck (the AP received the
1834 // PHY-RXSTART.indication and the frame exchange involves an EMLSR client)
1835 NS_TEST_EXPECT_MSG_GT_OR_EQ(fourthExchangeIt->front()->startTx,
1836 bAckRespTxEnd + phy->GetSifs(),
1837 "Transmission started less than a SIFS after BlockAck");
1838 NS_TEST_EXPECT_MSG_LT(fourthExchangeIt->front()->startTx,
1839 bAckRespTxEnd + phy->GetSifs() +
1840 MicroSeconds(1) /* propagation delay upper bound */,
1841 "Transmission started too much time after BlockAck");
1842
1843 auto bAckReqIt = std::next(fourthExchangeIt->front(), 2);
1844 NS_TEST_EXPECT_MSG_EQ(bAckReqIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAckReq(),
1845 true,
1846 "Expected a BlockAck request in the fourth frame exchange");
1847
1848 // we are done with processing the frame exchanges, remove them (two frame exchanges
1849 // per EMLSR client, plus the last one)
1850 frameExchanges.at(0).pop_front();
1851 frameExchanges.at(0).pop_front();
1852 frameExchanges.at(1).pop_front();
1853 frameExchanges.at(1).pop_front();
1854 frameExchanges.at(thirdExchangeStaId).pop_front();
1855 }
1856
1913 // for each EMLSR client, there should be a frame exchange with ICF and no data frame
1914 // (ICF protects the EML Notification response) and two frame exchanges with data frames
1915 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1916 {
1917 // the default EMLSR Manager requests to send EML Notification frames on the link where
1918 // the main PHY is operating, hence this link is an EMLSR link and the EML Notification
1919 // frame is protected by an ICF
1920 auto exchangeIt = frameExchanges.at(i).cbegin();
1921
1922 auto linkIdOpt = m_staMacs[i]->GetLinkForPhy(m_mainPhyId);
1923 NS_TEST_ASSERT_MSG_EQ(linkIdOpt.has_value(),
1924 true,
1925 "Didn't find a link on which the main PHY is operating");
1926
1927 NS_TEST_EXPECT_MSG_EQ(IsTrigger(exchangeIt->front()->psduMap),
1928 true,
1929 "Expected an MU-RTS TF as ICF of first frame exchange sequence");
1930 NS_TEST_EXPECT_MSG_EQ(+exchangeIt->front()->linkId,
1931 +linkIdOpt.value(),
1932 "ICF was not sent on the expected link");
1933 NS_TEST_EXPECT_MSG_EQ(exchangeIt->size(),
1934 1,
1935 "Expected no data frame in the first frame exchange sequence");
1936
1937 frameExchanges.at(i).pop_front();
1938
1939 NS_TEST_EXPECT_MSG_GT_OR_EQ(frameExchanges.at(i).size(),
1940 2,
1941 "Expected at least 2 frame exchange sequences "
1942 << "involving EMLSR client " << i);
1943
1944 auto firstExchangeIt = frameExchanges.at(i).cbegin();
1945 auto secondExchangeIt = std::next(firstExchangeIt);
1946
1947 const auto firstAmpduTxEnd =
1948 firstExchangeIt->back()->startTx +
1950 firstExchangeIt->back()->psduMap,
1951 firstExchangeIt->back()->txVector,
1952 m_staMacs[i]->GetWifiPhy(firstExchangeIt->back()->linkId)->GetPhyBand());
1953 const auto secondAmpduTxStart = secondExchangeIt->front()->startTx;
1954
1956 firstExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1957 true,
1958 "Expected a QoS data frame in the first frame exchange sequence");
1959 NS_TEST_EXPECT_MSG_EQ(firstExchangeIt->size(),
1960 1,
1961 "Expected one frame only in the first frame exchange sequence");
1962
1964 secondExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1965 true,
1966 "Expected a QoS data frame in the second frame exchange sequence");
1967 NS_TEST_EXPECT_MSG_EQ(secondExchangeIt->size(),
1968 1,
1969 "Expected one frame only in the second frame exchange sequence");
1970
1971 if (m_staMacs[i]->GetNLinks() == m_emlsrLinks.size())
1972 {
1973 // all links are EMLSR links: the two QoS data frames are sent one after another on
1974 // the link used for sending EML OMN
1976 +firstExchangeIt->front()->linkId,
1977 +linkIdOpt.value(),
1978 "First frame exchange expected to occur on link used to send EML OMN");
1979
1981 +secondExchangeIt->front()->linkId,
1982 +linkIdOpt.value(),
1983 "Second frame exchange expected to occur on link used to send EML OMN");
1984
1985 NS_TEST_EXPECT_MSG_LT(firstAmpduTxEnd,
1986 secondAmpduTxStart,
1987 "A-MPDUs are not sent one after another");
1988 }
1989 else
1990 {
1991 // the two QoS data frames are sent concurrently on distinct links
1992 NS_TEST_EXPECT_MSG_NE(+firstExchangeIt->front()->linkId,
1993 +secondExchangeIt->front()->linkId,
1994 "Frame exchanges expected to occur on distinct links");
1995
1996 NS_TEST_EXPECT_MSG_GT(firstAmpduTxEnd,
1997 secondAmpduTxStart,
1998 "A-MPDUs are not sent concurrently");
1999 }
2000 }
2001}
2002
2003void
2005{
2006 std::optional<std::size_t> staId;
2007 for (std::size_t id = 0; id < m_nEmlsrStations + m_nNonEmlsrStations; id++)
2008 {
2009 if (m_staMacs.at(id)->GetLinkIdByAddress(address))
2010 {
2011 staId = id;
2012 break;
2013 }
2014 }
2015 NS_TEST_ASSERT_MSG_EQ(staId.has_value(), true, "Not an address of a non-AP MLD " << address);
2016
2017 // check that all EMLSR links (but the link used for ML setup) of the EMLSR clients
2018 // are considered to be in power save mode by the AP MLD; all the other links have
2019 // transitioned to active mode instead
2020 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
2021 {
2022 bool psModeExpected =
2023 *staId < m_nEmlsrStations && linkId != m_mainPhyId && m_emlsrLinks.count(linkId) == 1;
2024 auto addr = m_staMacs.at(*staId)->GetAddress();
2025 auto psMode = m_apMac->GetWifiRemoteStationManager(linkId)->IsInPsMode(addr);
2026 NS_TEST_EXPECT_MSG_EQ(psMode,
2027 psModeExpected,
2028 "EMLSR link " << +linkId << " of EMLSR client " << *staId
2029 << " not in " << (psModeExpected ? "PS" : "active")
2030 << " mode");
2031 // check that AP is blocking transmission of QoS data frames on this link
2033 addr,
2034 linkId,
2035 WifiQueueBlockedReason::POWER_SAVE_MODE,
2036 psModeExpected,
2037 "Checking PM mode after association on AP MLD for EMLSR client " +
2038 std::to_string(*staId),
2039 false);
2040 }
2041}
2042
2043void
2045 const WifiTxVector& txVector,
2046 uint8_t linkId)
2047{
2048 // the AP is replying to a received EMLSR Notification frame
2049 auto pkt = mpdu->GetPacket()->Copy();
2050 const auto& hdr = mpdu->GetHeader();
2052 MgtEmlOmn frame;
2053 pkt->RemoveHeader(frame);
2054
2055 std::optional<std::size_t> staId;
2056 for (std::size_t id = 0; id < m_nEmlsrStations; id++)
2057 {
2058 if (m_staMacs.at(id)->GetFrameExchangeManager(linkId)->GetAddress() == hdr.GetAddr1())
2059 {
2060 staId = id;
2061 break;
2062 }
2063 }
2064 NS_TEST_ASSERT_MSG_EQ(staId.has_value(),
2065 true,
2066 "Not an address of an EMLSR client " << hdr.GetAddr1());
2067
2068 // The EMLSR mode change occurs a Transition Timeout after the end of the PPDU carrying the Ack
2069 auto phy = m_apMac->GetWifiPhy(linkId);
2070 auto txDuration =
2071 WifiPhy::CalculateTxDuration(mpdu->GetSize() + 4, // A-MPDU Subframe header size
2072 txVector,
2073 phy->GetPhyBand());
2074 WifiTxVector ackTxVector =
2075 m_staMacs.at(*staId)->GetWifiRemoteStationManager(linkId)->GetAckTxVector(hdr.GetAddr2(),
2076 txVector);
2077 auto ackDuration = WifiPhy::CalculateTxDuration(GetAckSize() + 4, // A-MPDU Subframe header
2078 ackTxVector,
2079 phy->GetPhyBand());
2080
2081 Simulator::Schedule(txDuration + phy->GetSifs() + ackDuration, [=]() {
2082 if (frame.m_emlControl.emlsrMode == 1)
2083 {
2084 // EMLSR mode enabled. Check that all EMLSR links of the EMLSR clients are considered
2085 // to be in active mode by the AP MLD
2086 for (const auto linkId : m_emlsrLinks)
2087 {
2088 auto addr = m_staMacs.at(*staId)->GetAddress();
2089 auto psMode = m_apMac->GetWifiRemoteStationManager(linkId)->IsInPsMode(addr);
2090 NS_TEST_EXPECT_MSG_EQ(psMode,
2091 false,
2092 "EMLSR link " << +linkId << " of EMLSR client " << *staId
2093 << " not in active mode");
2094 // check that AP is not blocking transmission of QoS data frames on this link
2095 CheckBlockedLink(
2096 m_apMac,
2097 addr,
2098 linkId,
2099 WifiQueueBlockedReason::POWER_SAVE_MODE,
2100 false,
2101 "Checking EMLSR links on AP MLD after EMLSR mode is enabled on EMLSR client " +
2102 std::to_string(*staId),
2103 false);
2104 }
2105 }
2106 else
2107 {
2108 // EMLSR mode disabled. Check that all EMLSR links (but the link used to send the
2109 // EML Notification frame) of the EMLSR clients are considered to be in power save
2110 // mode by the AP MLD; the other links are in active mode
2111 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2112 {
2113 bool psModeExpected = id != linkId && m_emlsrLinks.count(id) == 1;
2114 auto addr = m_staMacs.at(*staId)->GetAddress();
2115 auto psMode = m_apMac->GetWifiRemoteStationManager(id)->IsInPsMode(addr);
2116 NS_TEST_EXPECT_MSG_EQ(psMode,
2117 psModeExpected,
2118 "EMLSR link "
2119 << +id << " of EMLSR client " << *staId << " not in "
2120 << (psModeExpected ? "PS" : "active") << " mode");
2121 // check that AP is blocking transmission of QoS data frames on this link
2122 CheckBlockedLink(
2123 m_apMac,
2124 addr,
2125 id,
2126 WifiQueueBlockedReason::POWER_SAVE_MODE,
2127 psModeExpected,
2128 "Checking links on AP MLD after EMLSR mode is disabled on EMLSR client " +
2129 std::to_string(*staId),
2130 false);
2131 }
2132 }
2133 });
2134}
2135
2136void
2138 const WifiTxVector& txVector,
2139 uint8_t linkId)
2140{
2141 CtrlTriggerHeader trigger;
2142 mpdu->GetPacket()->PeekHeader(trigger);
2143 if (!trigger.IsMuRts())
2144 {
2145 return;
2146 }
2147
2149 true,
2150 "Did not expect an ICF before enabling EMLSR mode");
2151
2154 "Unexpected preamble type for the Initial Control frame");
2155 auto rate = txVector.GetMode().GetDataRate(txVector);
2156 NS_TEST_EXPECT_MSG_EQ((rate == 6e6 || rate == 12e6 || rate == 24e6),
2157 true,
2158 "Unexpected rate for the Initial Control frame: " << rate);
2159
2160 bool found = false;
2161 Time maxPaddingDelay{};
2162
2163 for (const auto& userInfo : trigger)
2164 {
2165 auto addr = m_apMac->GetMldOrLinkAddressByAid(userInfo.GetAid12());
2166 NS_TEST_ASSERT_MSG_EQ(addr.has_value(),
2167 true,
2168 "AID " << userInfo.GetAid12() << " not found");
2169
2171 {
2172 found = true;
2173
2174 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
2175 {
2176 if (m_staMacs.at(i)->GetAddress() == *addr)
2177 {
2178 maxPaddingDelay = Max(maxPaddingDelay, m_paddingDelay.at(i));
2179 break;
2180 }
2181 }
2182
2183 // check that the AP has blocked transmission on all other EMLSR links
2184 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2185 {
2187 {
2188 continue;
2189 }
2190
2192 *addr,
2193 id,
2194 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2195 id != linkId,
2196 "Checking that AP blocked transmissions on all other EMLSR "
2197 "links after sending ICF to client with AID=" +
2198 std::to_string(userInfo.GetAid12()));
2199 }
2200 }
2201 }
2202
2203 NS_TEST_EXPECT_MSG_EQ(found, true, "Expected ICF to be addressed to at least an EMLSR client");
2204
2205 auto txDuration = WifiPhy::CalculateTxDuration(mpdu->GetSize(),
2206 txVector,
2207 m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2208
2209 if (maxPaddingDelay.IsStrictlyPositive())
2210 {
2211 // compare the TX duration of this Trigger Frame to that of the Trigger Frame with no
2212 // padding added
2213 trigger.SetPaddingSize(0);
2214 auto pkt = Create<Packet>();
2215 pkt->AddHeader(trigger);
2216 auto txDurationWithout =
2217 WifiPhy::CalculateTxDuration(Create<WifiPsdu>(pkt, mpdu->GetHeader()),
2218 txVector,
2219 m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2220
2221 NS_TEST_EXPECT_MSG_EQ(txDuration,
2222 txDurationWithout + maxPaddingDelay,
2223 "Unexpected TX duration of the MU-RTS TF with padding "
2224 << maxPaddingDelay.As(Time::US));
2225 }
2226
2227 // check that the EMLSR clients have blocked transmissions on other links after
2228 // receiving this ICF
2229 for (const auto& userInfo : trigger)
2230 {
2231 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
2232 {
2233 if (m_staMacs[i]->GetAssociationId() != userInfo.GetAid12())
2234 {
2235 continue;
2236 }
2237
2238 Simulator::Schedule(txDuration + NanoSeconds(5), [=]() {
2239 for (uint8_t id = 0; id < m_staMacs[i]->GetNLinks(); id++)
2240 {
2241 // non-EMLSR links or links on which ICF is received are not blocked
2244 id,
2245 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2246 id != linkId && m_staMacs[i]->IsEmlsrLink(id),
2247 "Checking EMLSR links on EMLSR client " + std::to_string(i) +
2248 " after receiving ICF");
2249 }
2250 });
2251
2252 break;
2253 }
2254 }
2255}
2256
2257void
2259 const WifiTxVector& txVector,
2260 uint8_t linkId)
2261{
2262 if (m_nEmlsrStations != 2 || m_apMac->GetNLinks() != m_emlsrLinks.size() ||
2264
2265 {
2266 // we are interested in frames sent to test transition delay
2267 return;
2268 }
2269
2270 std::size_t firstClientId = 0;
2271 std::size_t secondClientId = 1;
2272 auto addr = m_staMacs[secondClientId]->GetAddress();
2273 auto txDuration =
2274 WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2275
2277
2278 switch (m_countQoSframes)
2279 {
2280 case 1:
2281 // generate another small packet addressed to the first EMLSR client only
2283 GetApplication(DOWNLINK, firstClientId, 1, 40));
2284 // both EMLSR clients are about to receive a QoS data frame
2285 for (std::size_t clientId : {firstClientId, secondClientId})
2286 {
2287 Simulator::Schedule(txDuration, [=]() {
2288 for (uint8_t id = 0; id < m_staMacs[clientId]->GetNLinks(); id++)
2289 {
2290 // link on which QoS data is received is not blocked
2291 CheckBlockedLink(m_staMacs[clientId],
2293 id,
2294 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2295 id != linkId,
2296 "Checking EMLSR links on EMLSR client " +
2297 std::to_string(clientId) +
2298 " after receiving the first QoS data frame");
2299 }
2300 });
2301 }
2302 break;
2303 case 2:
2304 // generate another small packet addressed to the second EMLSR client
2306 GetApplication(DOWNLINK, secondClientId, 1, 40));
2307
2308 // when the transmission of the second QoS data frame starts, both EMLSR clients are
2309 // still blocking all the links but the one used to receive the QoS data frame
2310 for (std::size_t clientId : {firstClientId, secondClientId})
2311 {
2312 for (uint8_t id = 0; id < m_staMacs[clientId]->GetNLinks(); id++)
2313 {
2314 // link on which QoS data is received is not blocked
2315 CheckBlockedLink(m_staMacs[clientId],
2317 id,
2318 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2319 id != linkId,
2320 "Checking EMLSR links on EMLSR client " +
2321 std::to_string(clientId) +
2322 " when starting the reception of the second QoS frame");
2323 }
2324 }
2325
2326 // the EMLSR client that is not the recipient of the QoS frame being transmitted will
2327 // switch back to listening mode after a transition delay starting from the end of
2328 // the PPDU carrying this QoS data frame
2329
2330 // immediately before the end of the PPDU, this link only is not blocked for the EMLSR
2331 // client on the AP MLD
2332 Simulator::Schedule(txDuration - NanoSeconds(1), [=]() {
2333 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2334 {
2336 addr,
2337 id,
2338 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2339 id != linkId,
2340 "Checking that links of EMLSR client " +
2341 std::to_string(secondClientId) +
2342 " are blocked on the AP MLD before the end of the PPDU");
2343 }
2344 });
2345 // immediately before the end of the PPDU, all the links on the EMLSR client that is not
2346 // the recipient of the second QoS frame are unblocked (they are unblocked when the
2347 // PHY-RXSTART.indication is not received)
2348 Simulator::Schedule(txDuration - NanoSeconds(1), [=]() {
2349 for (uint8_t id = 0; id < m_staMacs[secondClientId]->GetNLinks(); id++)
2350 {
2351 CheckBlockedLink(m_staMacs[secondClientId],
2353 id,
2354 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2355 false,
2356 "Checking that links of EMLSR client " +
2357 std::to_string(secondClientId) +
2358 " are unblocked before the end of the second QoS frame");
2359 }
2360 });
2361 // immediately after the end of the PPDU, all links are blocked for the EMLSR client
2362 Simulator::Schedule(txDuration + NanoSeconds(1), [=]() {
2363 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2364 {
2366 addr,
2367 id,
2368 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2369 true,
2370 "Checking links of EMLSR client " +
2371 std::to_string(secondClientId) +
2372 " are all blocked on the AP MLD after the end of the PPDU");
2373 }
2374 });
2375 // immediately before the transition delay, all links are still blocked for the EMLSR client
2377 txDuration + m_transitionDelay.at(secondClientId) - NanoSeconds(1),
2378 [=]() {
2379 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2380 {
2382 m_apMac,
2383 addr,
2384 id,
2385 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2386 true,
2387 "Checking links of EMLSR client " + std::to_string(secondClientId) +
2388 " are all blocked on the AP MLD before the transition delay");
2389 }
2390 });
2391
2392 break;
2393 case 3:
2394 // at the end of the third QoS frame, this link only is not blocked on the EMLSR
2395 // client receiving the frame
2396 Simulator::Schedule(txDuration, [=]() {
2397 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2398 {
2399 CheckBlockedLink(m_staMacs[secondClientId],
2401 id,
2402 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2403 id != linkId,
2404 "Checking EMLSR links on EMLSR client " +
2405 std::to_string(secondClientId) +
2406 " after receiving the third QoS data frame");
2407 }
2408 });
2409 break;
2410 }
2411}
2412
2413void
2415 const WifiTxVector& txVector,
2416 uint8_t phyId)
2417{
2418 if (m_nEmlsrStations != 2 || m_apMac->GetNLinks() != m_emlsrLinks.size() ||
2420 {
2421 // we are interested in frames sent to test transition delay
2422 return;
2423 }
2424
2425 auto taddr = psduMap.cbegin()->second->GetAddr2();
2426 std::size_t clientId;
2427 if (m_staMacs[0]->GetLinkIdByAddress(taddr))
2428 {
2429 clientId = 0;
2430 }
2431 else
2432 {
2433 NS_TEST_ASSERT_MSG_EQ(m_staMacs[1]->GetLinkIdByAddress(taddr).has_value(),
2434 true,
2435 "Unexpected TA for BlockAck: " << taddr);
2436 clientId = 1;
2437 }
2438
2439 // find the link on which the main PHY is operating
2440 auto currMainPhyLinkId = m_staMacs[clientId]->GetLinkForPhy(phyId);
2442 currMainPhyLinkId.has_value(),
2443 true,
2444 "Didn't find the link on which the PHY sending the BlockAck is operating");
2445 auto linkId = *currMainPhyLinkId;
2446
2447 // we need the MLD address to check the status of the container queues
2448 auto addr = m_apMac->GetWifiRemoteStationManager(linkId)->GetMldAddress(taddr);
2449 NS_TEST_ASSERT_MSG_EQ(addr.has_value(), true, "MLD address not found for " << taddr);
2450
2451 auto apPhy = m_apMac->GetWifiPhy(linkId);
2452 auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, apPhy->GetPhyBand());
2453 auto cfEndTxDuration = WifiPhy::CalculateTxDuration(
2454 Create<WifiPsdu>(Create<Packet>(), WifiMacHeader(WIFI_MAC_CTL_END)),
2456 apPhy->GetPhyBand());
2457
2459
2460 switch (m_countBlockAck)
2461 {
2462 case 4:
2463 // the PPDU carrying this BlockAck is corrupted, hence the AP MLD MAC receives the
2464 // PHY-RXSTART indication but it does not receive any frame from the PHY. Therefore,
2465 // at the end of the PPDU transmission, the AP MLD realizes that the EMLSR client has
2466 // not responded and makes an attempt at continuing the TXOP
2467
2468 // at the end of the PPDU, this link only is not blocked on both the EMLSR client and
2469 // the AP MLD
2470 Simulator::Schedule(txDuration, [=]() {
2471 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2472 {
2473 CheckBlockedLink(m_staMacs[clientId],
2475 id,
2476 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2477 id != linkId,
2478 "Checking links on EMLSR client " + std::to_string(clientId) +
2479 " at the end of fourth BlockAck");
2481 *addr,
2482 id,
2483 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2484 id != linkId,
2485 "Checking links of EMLSR client " + std::to_string(clientId) +
2486 " on the AP MLD at the end of fourth BlockAck");
2487 }
2488 });
2489 // a SIFS after the end of the PPDU, still this link only is not blocked on both the
2490 // EMLSR client and the AP MLD
2491 Simulator::Schedule(txDuration + apPhy->GetSifs(), [=]() {
2492 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2493 {
2494 CheckBlockedLink(m_staMacs[clientId],
2495 m_apMac->GetAddress(),
2496 id,
2497 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2498 id != linkId,
2499 "Checking links on EMLSR client " + std::to_string(clientId) +
2500 " a SIFS after the end of fourth BlockAck");
2501 CheckBlockedLink(m_apMac,
2502 *addr,
2503 id,
2504 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2505 id != linkId,
2506 "Checking links of EMLSR client " + std::to_string(clientId) +
2507 " a SIFS after the end of fourth BlockAck");
2508 }
2509 });
2510 // corrupt this BlockAck so that the AP MLD sends a BlockAckReq later on
2511 {
2512 auto uid = psduMap.cbegin()->second->GetPacket()->GetUid();
2513 m_errorModel->SetList({uid});
2514 }
2515 break;
2516 case 5:
2517 // at the end of the PPDU, this link only is not blocked on both the EMLSR client and
2518 // the AP MLD
2519 Simulator::Schedule(txDuration, [=]() {
2520 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2521 {
2522 CheckBlockedLink(m_staMacs[clientId],
2524 id,
2525 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2526 id != linkId,
2527 "Checking links on EMLSR client " + std::to_string(clientId) +
2528 " at the end of fifth BlockAck");
2530 *addr,
2531 id,
2532 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2533 id != linkId,
2534 "Checking links of EMLSR client " + std::to_string(clientId) +
2535 " on the AP MLD at the end of fifth BlockAck");
2536 }
2537 });
2538 // before the end of the CF-End frame, still this link only is not blocked on both the
2539 // EMLSR client and the AP MLD
2541 txDuration + apPhy->GetSifs() + cfEndTxDuration - MicroSeconds(1),
2542 [=]() {
2543 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2544 {
2545 CheckBlockedLink(m_staMacs[clientId],
2547 id,
2548 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2549 id != linkId,
2550 "Checking links on EMLSR client " + std::to_string(clientId) +
2551 " before the end of CF-End frame");
2553 *addr,
2554 id,
2555 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2556 id != linkId,
2557 "Checking links of EMLSR client " + std::to_string(clientId) +
2558 " on the AP MLD before the end of CF-End frame");
2559 }
2560 });
2561 // after the end of the CF-End frame, all links for the EMLSR client are blocked on the
2562 // AP MLD
2564 txDuration + apPhy->GetSifs() + cfEndTxDuration + MicroSeconds(1),
2565 [=]() {
2566 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2567 {
2569 m_apMac,
2570 *addr,
2571 id,
2572 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2573 true,
2574 "Checking links of EMLSR client " + std::to_string(clientId) +
2575 " are all blocked on the AP MLD right after the end of CF-End");
2576 }
2577 });
2578 // before the end of the transition delay, all links for the EMLSR client are still
2579 // blocked on the AP MLD
2581 txDuration + apPhy->GetSifs() + cfEndTxDuration + m_transitionDelay.at(clientId) -
2582 MicroSeconds(1),
2583 [=]() {
2584 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2585 {
2587 m_apMac,
2588 *addr,
2589 id,
2590 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2591 true,
2592 "Checking links of EMLSR client " + std::to_string(clientId) +
2593 " are all blocked on the AP MLD before the end of transition delay");
2594 }
2595 });
2596 // immediately after the transition delay, all links for the EMLSR client are unblocked
2598 txDuration + apPhy->GetSifs() + cfEndTxDuration + m_transitionDelay.at(clientId) +
2599 MicroSeconds(1),
2600 [=]() {
2601 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2602 {
2604 m_apMac,
2605 *addr,
2606 id,
2607 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2608 false,
2609 "Checking links of EMLSR client " + std::to_string(clientId) +
2610 " are all unblocked on the AP MLD after the transition delay");
2611 }
2612 });
2613 break;
2614 }
2615}
2616
2617void
2619{
2622
2623 CheckResults();
2624
2626}
2627
2645{
2646 public:
2656 EmlsrLinkSwitchTest(bool switchAuxPhy, bool resetCamState, uint16_t auxPhyMaxChWidth);
2657
2658 ~EmlsrLinkSwitchTest() override = default;
2659
2660 protected:
2661 void DoSetup() override;
2662 void DoRun() override;
2663 void Transmit(Ptr<WifiMac> mac,
2664 uint8_t phyId,
2665 WifiConstPsduMap psduMap,
2666 WifiTxVector txVector,
2667 double txPowerW) override;
2668
2672 void CheckResults();
2673
2682 void CheckInitialControlFrame(const WifiConstPsduMap& psduMap,
2683 const WifiTxVector& txVector,
2684 uint8_t linkId);
2685
2694 void CheckQosFrames(const WifiConstPsduMap& psduMap,
2695 const WifiTxVector& txVector,
2696 uint8_t linkId);
2697
2698 private:
2704 std::size_t m_countQoSframes;
2705 std::size_t m_txPsdusPos;
2706};
2707
2709 bool resetCamState,
2710 uint16_t auxPhyMaxChWidth)
2711 : EmlsrOperationsTestBase(std::string("Check EMLSR link switching (switchAuxPhy=") +
2712 std::to_string(switchAuxPhy) +
2713 ", resetCamState=" + std::to_string(resetCamState) +
2714 ", auxPhyMaxChWidth=" + std::to_string(auxPhyMaxChWidth) + "MHz )"),
2715 m_switchAuxPhy(switchAuxPhy),
2716 m_resetCamState(resetCamState),
2717 m_auxPhyMaxChWidth(auxPhyMaxChWidth),
2718 m_countQoSframes(0),
2719 m_txPsdusPos(0)
2720{
2721 m_nEmlsrStations = 1;
2723 m_linksToEnableEmlsrOn = {0, 1, 2}; // enable EMLSR on all links right after association
2724 m_mainPhyId = 1;
2725 m_establishBaDl = true;
2726 m_duration = Seconds(1.0);
2727}
2728
2729void
2731 uint8_t phyId,
2732 WifiConstPsduMap psduMap,
2733 WifiTxVector txVector,
2734 double txPowerW)
2735{
2736 EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
2737 auto linkId = m_txPsdus.back().linkId;
2738
2739 auto psdu = psduMap.begin()->second;
2740 auto nodeId = mac->GetDevice()->GetNode()->GetId();
2741
2742 switch (psdu->GetHeader(0).GetType())
2743 {
2745 NS_ASSERT_MSG(nodeId > 0, "APs do not send AssocReq frames");
2746 NS_TEST_EXPECT_MSG_EQ(+linkId, +m_mainPhyId, "AssocReq not sent by the main PHY");
2747 break;
2748
2749 case WIFI_MAC_MGT_ACTION: {
2750 auto [category, action] = WifiActionHeader::Peek(psdu->GetPayload(0));
2751
2752 if (nodeId == 1 && category == WifiActionHeader::PROTECTED_EHT &&
2753 action.protectedEhtAction ==
2755 {
2756 // the EMLSR client is starting the transmission of the EML OMN frame;
2757 // temporarily block transmissions of QoS data frames from the AP MLD to the
2758 // non-AP MLD on all the links but the one used for ML setup, so that we know
2759 // that the first QoS data frame is sent on the link of the main PHY
2760 std::set<uint8_t> linksToBlock;
2761 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2762 {
2763 if (id != m_mainPhyId)
2764 {
2765 linksToBlock.insert(id);
2766 }
2767 }
2768 m_apMac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
2769 AC_BE,
2771 m_staMacs[0]->GetAddress(),
2773 {0},
2774 linksToBlock);
2775 }
2776 else if (category == WifiActionHeader::BLOCK_ACK &&
2778 {
2779 // store the current number of transmitted frame
2780 m_txPsdusPos = m_txPsdus.size() - 1;
2781 }
2782 }
2783 break;
2784
2786 CheckInitialControlFrame(psduMap, txVector, linkId);
2787 break;
2788
2789 case WIFI_MAC_QOSDATA:
2790 CheckQosFrames(psduMap, txVector, linkId);
2791 break;
2792
2793 default:;
2794 }
2795}
2796
2797void
2799{
2800 Config::SetDefault("ns3::DefaultEmlsrManager::SwitchAuxPhy", BooleanValue(m_switchAuxPhy));
2801 Config::SetDefault("ns3::EmlsrManager::ResetCamState", BooleanValue(m_resetCamState));
2802 Config::SetDefault("ns3::EmlsrManager::AuxPhyChannelWidth", UintegerValue(m_auxPhyMaxChWidth));
2803
2805
2806 // use channels of different widths
2807 for (auto mac : std::initializer_list<Ptr<WifiMac>>{m_apMac, m_staMacs[0]})
2808 {
2809 mac->GetWifiPhy(0)->SetOperatingChannel(
2811 mac->GetWifiPhy(1)->SetOperatingChannel(
2813 mac->GetWifiPhy(2)->SetOperatingChannel(
2815 }
2816}
2817
2818void
2820{
2823
2824 CheckResults();
2825
2827}
2828
2829void
2831 const WifiTxVector& txVector,
2832 uint8_t linkId)
2833{
2835
2836 switch (m_countQoSframes)
2837 {
2838 case 1:
2839 // unblock transmissions on all links
2840 m_apMac->GetMacQueueScheduler()->UnblockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
2841 AC_BE,
2843 m_staMacs[0]->GetAddress(),
2845 {0},
2846 {0, 1, 2});
2847 // block transmissions on the link used for ML setup
2848 m_apMac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
2849 AC_BE,
2851 m_staMacs[0]->GetAddress(),
2853 {0},
2854 {m_mainPhyId});
2855 // generate a new data packet, which will be sent on a link other than the one
2856 // used for ML setup, hence triggering a link switching on the EMLSR client
2858 break;
2859 case 2:
2860 // block transmission on the link used to send this QoS data frame
2861 m_apMac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
2862 AC_BE,
2864 m_staMacs[0]->GetAddress(),
2866 {0},
2867 {linkId});
2868 // generate a new data packet, which will be sent on the link that has not been used
2869 // so far, hence triggering another link switching on the EMLSR client
2871 break;
2872 case 3:
2873 // block transmission on the link used to send this QoS data frame
2874 m_apMac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
2875 AC_BE,
2877 m_staMacs[0]->GetAddress(),
2879 {0},
2880 {linkId});
2881 // unblock transmissions on the link used for ML setup
2882 m_apMac->GetMacQueueScheduler()->UnblockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
2883 AC_BE,
2885 m_staMacs[0]->GetAddress(),
2887 {0},
2888 {m_mainPhyId});
2889 // generate a new data packet, which will be sent again on the link used for ML setup,
2890 // hence triggering yet another link switching on the EMLSR client
2892 break;
2893 }
2894}
2895
2949void
2951 const WifiTxVector& txVector,
2952 uint8_t linkId)
2953{
2954 if (m_txPsdusPos == 0)
2955 {
2956 // the iterator has not been set yet, thus we are not done with the establishment of
2957 // the BA agreement
2958 return;
2959 }
2960
2961 auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId);
2962 auto phyRecvIcf = m_staMacs[0]->GetWifiPhy(linkId); // PHY receiving the ICF
2963
2964 auto currMainPhyLinkId = m_staMacs[0]->GetLinkForPhy(mainPhy);
2965 NS_TEST_ASSERT_MSG_EQ(currMainPhyLinkId.has_value(),
2966 true,
2967 "Didn't find the link on which the Main PHY is operating");
2968
2969 if (phyRecvIcf && phyRecvIcf != mainPhy)
2970 {
2972 phyRecvIcf->GetChannelWidth(),
2974 "Aux PHY that received ICF is operating on a channel whose width exceeds the limit "
2975 << m_countQoSframes);
2976 }
2977
2978 auto txDuration =
2979 WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2980
2981 // check that PHYs are operating on the expected link after the reception of the ICF
2982 Simulator::Schedule(txDuration + NanoSeconds(1), [=]() {
2983 std::size_t nRxOk = m_switchAuxPhy ? 4 : 3; // successfully received ICFs
2984
2985 if (m_countQoSframes < nRxOk)
2986 {
2987 // the main PHY must be operating on the link where ICF was sent
2988 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(linkId),
2989 mainPhy,
2990 "PHY operating on link where ICF was sent is not the main PHY");
2991 }
2992
2993 // the first ICF is received by the main PHY and no channel switch occurs
2994 if (m_countQoSframes == 0)
2995 {
2996 NS_TEST_EXPECT_MSG_EQ(phyRecvIcf,
2997 mainPhy,
2998 "PHY that received the ICF is not the main PHY");
2999 return;
3000 }
3001
3002 // the behavior of Aux PHYs depends on whether they switch channel or not
3003 if (m_switchAuxPhy)
3004 {
3005 NS_TEST_EXPECT_MSG_LT(m_countQoSframes, nRxOk, "Unexpected number of ICFs");
3006 Simulator::Schedule(phyRecvIcf->GetChannelSwitchDelay(), [=]() {
3007 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(*currMainPhyLinkId),
3008 phyRecvIcf,
3009 "PHY operating on link where Main PHY was before switching "
3010 "channel is not the aux PHY that received the ICF");
3011 });
3012 }
3013 else if (m_countQoSframes < nRxOk)
3014 {
3015 // the first 3 ICFs are actually received by some PHY
3016 NS_TEST_EXPECT_MSG_NE(phyRecvIcf, nullptr, "Expected some PHY to receive the ICF");
3017 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(*currMainPhyLinkId),
3018 nullptr,
3019 "No PHY expected to operate on link where Main PHY was "
3020 "before switching channel");
3021 }
3022 else
3023 {
3024 // no PHY received the ICF
3025 NS_TEST_EXPECT_MSG_EQ(phyRecvIcf,
3026 nullptr,
3027 "Expected no PHY to operate on link where ICF is sent");
3028 }
3029 });
3030}
3031
3032void
3034{
3035 NS_TEST_ASSERT_MSG_NE(m_txPsdusPos, 0, "BA agreement establishment not completed");
3036
3037 std::size_t nRxOk = m_switchAuxPhy ? 4 : 3; // successfully received ICFs
3038
3040 m_txPsdusPos + 3 + nRxOk * 4,
3041 "Insufficient number of TX PSDUs");
3042
3043 // m_txPsdusPos points to ADDBA_RESPONSE, then ACK and then ICF
3044 auto psduIt = std::next(m_txPsdus.cbegin(), m_txPsdusPos + 2);
3045
3046 for (std::size_t i = 0; i < nRxOk; i++)
3047 {
3048 NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 &&
3049 psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsTrigger()),
3050 true,
3051 "Expected a Trigger Frame (ICF)");
3052 psduIt++;
3054 (psduIt->psduMap.size() == 1 && psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsCts()),
3055 true,
3056 "Expected a CTS");
3057 psduIt++;
3058 NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 &&
3059 psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsQosData()),
3060 true,
3061 "Expected a QoS Data frame");
3062 psduIt++;
3063 NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 &&
3064 psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsBlockAck()),
3065 true,
3066 "Expected a BlockAck");
3067 psduIt++;
3068 }
3069}
3070
3078{
3079 public:
3081};
3082
3084 : TestSuite("wifi-emlsr", UNIT)
3085{
3091 for (const auto& emlsrLinks :
3092 {std::set<uint8_t>{0, 1, 2}, std::set<uint8_t>{1, 2}, std::set<uint8_t>{0, 1}})
3093 {
3095 0,
3096 emlsrLinks,
3097 {MicroSeconds(32)},
3098 {MicroSeconds(32)},
3099 MicroSeconds(512)),
3102 1,
3103 emlsrLinks,
3104 {MicroSeconds(64)},
3105 {MicroSeconds(64)},
3106 MicroSeconds(512)),
3109 2,
3110 emlsrLinks,
3111 {MicroSeconds(128), MicroSeconds(256)},
3112 {MicroSeconds(128), MicroSeconds(256)},
3113 MicroSeconds(512)),
3115 }
3116
3117 for (bool switchAuxPhy : {true, false})
3118 {
3119 for (bool resetCamState : {true, false})
3120 {
3121 for (uint16_t auxPhyMaxChWidth : {20, 40, 80, 160})
3122 {
3123 AddTestCase(new EmlsrLinkSwitchTest(switchAuxPhy, resetCamState, auxPhyMaxChWidth),
3125 }
3126 }
3127 }
3128}
3129
#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...
~EmlOmnExchangeTest() override=default
void TxDropped(WifiMacDropReason reason, Ptr< const WifiMpdu > mpdu)
Callback invoked when the non-AP MLD drops the given MPDU for the given reason.
void CheckEmlNotification(Ptr< const WifiPsdu > psdu, const WifiTxVector &txVector, uint8_t linkId)
Check the content of a received EML Operating Mode Notification frame.
void Transmit(Ptr< WifiMac > mac, uint8_t phyId, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW) override
Callback invoked when a FEM passes PSDUs to the PHY.
void DoRun() override
Implementation to actually run this TestCase.
Test EML Operating Mode Notification frame serialization and deserialization.
EmlOperatingModeNotificationTest()
Constructor.
void DoRun() override
Implementation to actually run this TestCase.
~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
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.
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::size_t m_countBlockAck
counter for BlockAck frames (transition delay test)
Base class for EMLSR Operations tests.
std::size_t m_nNonEmlsrStations
number of stations to create that do not activate EMLSR
void SetSsid(uint16_t aid, Mac48Address)
Set the SSID on the next station that needs to start the association procedure.
bool m_establishBaDl
whether BA needs to be established (for TID 0) with the AP as originator
void CheckBlockedLink(Ptr< WifiMac > mac, Mac48Address dest, uint8_t linkId, WifiQueueBlockedReason reason, bool blocked, std::string description, bool testUnblockedForOtherReasons=true)
Check whether QoS data unicast transmissions addressed to the given destination on the given link are...
std::size_t m_nEmlsrStations
number of stations to create that activate EMLSR
std::vector< PacketSocketAddress > m_dlSockets
packet socket address for DL traffic
std::vector< Time > m_paddingDelay
Padding Delay advertised by the non-AP MLD.
std::set< uint8_t > m_linksToEnableEmlsrOn
IDs of the links on which EMLSR mode has to be enabled.
Ptr< ApWifiMac > m_apMac
AP wifi MAC.
~EmlsrOperationsTestBase() override=default
TrafficDirection
Enumeration for traffic directions.
void DoSetup() override
Implementation to do any local setup required for this TestCase.
uint8_t m_mainPhyId
ID of the main PHY.
Time m_duration
simulation duration
std::vector< FrameInfo > m_txPsdus
transmitted PSDUs
Ptr< PacketSocketClient > GetApplication(TrafficDirection dir, std::size_t staId, std::size_t count, std::size_t pktSize) const
virtual void Transmit(Ptr< WifiMac > mac, uint8_t phyId, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW)
Callback invoked when a FEM passes PSDUs to the PHY.
bool m_establishBaUl
whether BA needs to be established (for TID 0) with the AP as recipient
uint16_t m_lastAid
AID of last associated station.
std::vector< Time > m_transitionDelay
Transition Delay advertised by the non-AP MLD.
Time m_transitionTimeout
Transition Timeout advertised by the AP MLD.
std::vector< PacketSocketAddress > m_ulSockets
packet socket address for UL traffic
std::vector< Ptr< StaWifiMac > > m_staMacs
MACs of the non-AP MLDs.
virtual void StartTraffic()
Start the generation of traffic (needs to be overridden)
EmlsrOperationsTestBase(const std::string &name)
Constructor.
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
static Mac48Address GetBroadcast()
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::optional< EmlsrParamUpdate > m_emlsrParamUpdate
EMLSR Parameter Update field.
Definition: mgt-headers.h:1164
std::list< uint8_t > GetLinkBitmap() const
Helper class used to assign positions and mobility models to nodes.
holds a vector of ns3::NetDevice pointers
Ptr< NetDevice > Get(uint32_t i) const
Get the Ptr<NetDevice> stored in this container at a given index.
keep track of a set of node pointers.
uint32_t AddApplication(Ptr< Application > application)
Associate an Application to this Node.
Definition: node.cc: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:315
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:558
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:1412
void SetTxopLimits(const std::vector< Time > &txopLimits)
Set the TXOP limit for each link.
Definition: txop.cc:396
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
Implements the IEEE 802.11 MAC header.
create MAC layers for a ns3::WifiNetDevice.
Ptr< FrameExchangeManager > GetFrameExchangeManager(uint8_t linkId=SINGLE_LINK_OP_ID) const
Get the Frame Exchange Manager associated with the given link.
Definition: wifi-mac.cc:866
Ptr< WifiMacQueueScheduler > GetMacQueueScheduler() const
Get the wifi MAC queue scheduler.
Definition: wifi-mac.cc:576
uint8_t GetNLinks() const
Get the number of links (can be greater than 1 for 11be devices only).
Definition: wifi-mac.cc:935
Ptr< WifiPhy > GetWifiPhy(uint8_t linkId=SINGLE_LINK_OP_ID) const
Definition: wifi-mac.cc:1173
Ptr< WifiNetDevice > GetDevice() const
Return the device this PHY is associated with.
Definition: wifi-mac.cc:439
Ptr< WifiRemoteStationManager > GetWifiRemoteStationManager(uint8_t linkId=0) const
Definition: wifi-mac.cc:908
Mac48Address GetAddress() const
Definition: wifi-mac.cc:452
Ptr< QosTxop > GetQosTxop(AcIndex ac) const
Accessor for a specified EDCA object.
Definition: wifi-mac.cc:499
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
std::tuple< uint8_t, uint16_t, int, uint8_t > ChannelTuple
Tuple identifying an operating channel.
Definition: wifi-phy.h:872
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.
WifiTxVector GetRtsTxVector(Mac48Address address)
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 SetDefault(std::string name, const AttributeValue &value)
Definition: config.cc:890
void ConnectWithoutContext(std::string path, const CallbackBase &cb)
Definition: config.cc:950
#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_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:830
#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
#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:915
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1349
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1361
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1325
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1337
WifiMacDropReason
The reason why an MPDU was dropped.
Definition: wifi-mac.h:77
WifiQueueBlockedReason
Enumeration of the reasons to block container queues.
@ WIFI_STANDARD_80211be
@ WIFI_PREAMBLE_HT_MF
@ WIFI_PHY_BAND_6GHZ
The 6 GHz band.
Definition: wifi-phy-band.h:39
@ WIFI_PHY_BAND_2_4GHZ
The 2.4 GHz band.
Definition: wifi-phy-band.h:35
@ WIFI_PHY_BAND_5GHZ
The 5 GHz band.
Definition: wifi-phy-band.h:37
@ AC_BE
Best Effort.
Definition: qos-utils.h: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:484
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:704
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_CTL_END
@ WIFI_MAC_QOSDATA
uint32_t GetAckSize()
Return the total Ack size (including FCS trailer).
Definition: wifi-utils.cc:58
static constexpr uint16_t SU_STA_ID
STA_ID to identify a single user (SU)
Definition: wifi-mode.h:35
constexpr FrequencyRange WIFI_SPECTRUM_2_4_GHZ
Identifier for the frequency range covering the wifi spectrum in the 2.4 GHz band.
STL namespace.
ns phy
Definition: third.py:82
ns3::Time timeout
Information about transmitted frames.
WifiConstPsduMap psduMap
transmitted PSDU map
uint8_t phyId
ID of the transmitting PHY.
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 emlsrMode
EMLSR Mode.
Definition: mgt-headers.h:1133
uint8_t emlsrParamUpdateCtrl
EMLSR Parameter Update Control.
Definition: mgt-headers.h:1135
uint8_t emlmrMode
EMLMR Mode.
Definition: mgt-headers.h:1134
std::optional< uint16_t > linkBitmap
EMLSR/EMLMR Link Bitmap.
Definition: mgt-headers.h:1137
EMLSR Parameter Update field.
Definition: mgt-headers.h:1146
std::string dir
uint32_t pktSize
packet size used for the simulation (in bytes)
static WifiEmlsrTestSuite g_wifiEmlsrTestSuite
the test suite