A Discrete-Event Network Simulator
API
wifi-emlsr-test.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2023 Universita' degli Studi di Napoli Federico II
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation;
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  *
17  * Author: Stefano Avallone <stavallo@unina.it>
18  */
19 
20 #include "wifi-emlsr-test.h"
21 
22 #include "ns3/attribute-container.h"
23 #include "ns3/boolean.h"
24 #include "ns3/config.h"
25 #include "ns3/ctrl-headers.h"
26 #include "ns3/eht-configuration.h"
27 #include "ns3/emlsr-manager.h"
28 #include "ns3/he-frame-exchange-manager.h"
29 #include "ns3/log.h"
30 #include "ns3/mgt-action-headers.h"
31 #include "ns3/mobility-helper.h"
32 #include "ns3/multi-model-spectrum-channel.h"
33 #include "ns3/node-list.h"
34 #include "ns3/packet-socket-helper.h"
35 #include "ns3/packet-socket-server.h"
36 #include "ns3/qos-txop.h"
37 #include "ns3/rng-seed-manager.h"
38 #include "ns3/rr-multi-user-scheduler.h"
39 #include "ns3/simulator.h"
40 #include "ns3/spectrum-wifi-helper.h"
41 #include "ns3/spectrum-wifi-phy.h"
42 #include "ns3/string.h"
43 #include "ns3/wifi-net-device.h"
44 
45 #include <algorithm>
46 #include <functional>
47 #include <iomanip>
48 #include <optional>
49 
50 using namespace ns3;
51 
52 NS_LOG_COMPONENT_DEFINE("WifiEmlsrTest");
53 
56  "Check serialization and deserialization of the EML Operating Mode Notification frame")
57 {
58 }
59 
60 void
62 {
63  MgtEmlOmn frame;
64 
65  // Both EMLSR Mode and EMLMR Mode subfields set to 0 (no link bitmap);
67 
68  frame.m_emlControl.emlsrMode = 1;
69  frame.SetLinkIdInBitmap(0);
70  frame.SetLinkIdInBitmap(5);
71  frame.SetLinkIdInBitmap(15);
72 
73  // Adding Link Bitmap
75 
76  NS_TEST_EXPECT_MSG_EQ((frame.GetLinkBitmap() == std::list<uint8_t>{0, 5, 15}),
77  true,
78  "Unexpected link bitmap");
79 
80  auto padding = MicroSeconds(64);
81  auto transition = MicroSeconds(128);
82 
85  frame.m_emlsrParamUpdate->paddingDelay = CommonInfoBasicMle::EncodeEmlsrPaddingDelay(padding);
86  frame.m_emlsrParamUpdate->transitionDelay =
87  CommonInfoBasicMle::EncodeEmlsrTransitionDelay(transition);
88 
89  // Adding the EMLSR Parameter Update field
91 
93  CommonInfoBasicMle::DecodeEmlsrPaddingDelay(frame.m_emlsrParamUpdate->paddingDelay),
94  padding,
95  "Unexpected EMLSR Padding Delay");
97  CommonInfoBasicMle::DecodeEmlsrTransitionDelay(frame.m_emlsrParamUpdate->transitionDelay),
98  transition,
99  "Unexpected EMLSR Transition Delay");
100 }
101 
103  : TestCase(name)
104 {
105 }
106 
107 void
109  uint8_t phyId,
110  WifiConstPsduMap psduMap,
111  WifiTxVector txVector,
112  double txPowerW)
113 {
114  auto linkId = mac->GetLinkForPhy(phyId);
115  NS_TEST_ASSERT_MSG_EQ(linkId.has_value(), true, "No link found for PHY ID " << +phyId);
116  m_txPsdus.push_back({Simulator::Now(), psduMap, txVector, *linkId, phyId});
117 
118  auto txDuration =
119  WifiPhy::CalculateTxDuration(psduMap, txVector, mac->GetWifiPhy(*linkId)->GetPhyBand());
120 
121  for (const auto& [aid, psdu] : psduMap)
122  {
123  std::stringstream ss;
124  ss << std::setprecision(10) << "PSDU #" << m_txPsdus.size() << " Link ID "
125  << +linkId.value() << " Phy ID " << +phyId << " " << psdu->GetHeader(0).GetTypeString();
126  if (psdu->GetHeader(0).IsAction())
127  {
128  ss << " ";
129  WifiActionHeader actionHdr;
130  psdu->GetPayload(0)->PeekHeader(actionHdr);
131  actionHdr.Print(ss);
132  }
133  ss << " #MPDUs " << psdu->GetNMpdus() << " duration/ID " << psdu->GetHeader(0).GetDuration()
134  << " RA = " << psdu->GetAddr1() << " TA = " << psdu->GetAddr2()
135  << " ADDR3 = " << psdu->GetHeader(0).GetAddr3()
136  << " ToDS = " << psdu->GetHeader(0).IsToDs()
137  << " FromDS = " << psdu->GetHeader(0).IsFromDs();
138  if (psdu->GetHeader(0).IsQosData())
139  {
140  ss << " seqNo = {";
141  for (auto& mpdu : *PeekPointer(psdu))
142  {
143  ss << mpdu->GetHeader().GetSequenceNumber() << ",";
144  }
145  ss << "} TID = " << +psdu->GetHeader(0).GetQosTid();
146  }
147  NS_LOG_INFO(ss.str());
148  }
149  NS_LOG_INFO("TX duration = " << txDuration.As(Time::MS) << " TXVECTOR = " << txVector << "\n");
150 }
151 
152 void
154 {
155  RngSeedManager::SetSeed(1);
156  RngSeedManager::SetRun(2);
157  int64_t streamNumber = 100;
158 
159  Config::SetDefault("ns3::WifiMac::MpduBufferSize", UintegerValue(64));
160 
163 
165  // wifi.EnableLogComponents ();
166  wifi.SetStandard(WIFI_STANDARD_80211be);
167  wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
168  "DataMode",
169  StringValue("EhtMcs0"),
170  "ControlMode",
171  StringValue("HtMcs0"));
172  wifi.ConfigEhtOptions("EmlsrActivated",
173  BooleanValue(true),
174  "TransitionTimeout",
176 
177  // MLDs are configured with three links
178  SpectrumWifiPhyHelper phyHelper(3);
179  phyHelper.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
180  phyHelper.Set(0, "ChannelSettings", StringValue("{2, 0, BAND_2_4GHZ, 0}"));
181  phyHelper.Set(1, "ChannelSettings", StringValue("{36, 0, BAND_5GHZ, 0}"));
182  phyHelper.Set(2, "ChannelSettings", StringValue("{1, 0, BAND_6GHZ, 0}"));
183  // Add three spectrum channels to use multi-RF interface
184  phyHelper.AddChannel(CreateObject<MultiModelSpectrumChannel>(), WIFI_SPECTRUM_2_4_GHZ);
185  phyHelper.AddChannel(CreateObject<MultiModelSpectrumChannel>(), WIFI_SPECTRUM_5_GHZ);
186  phyHelper.AddChannel(CreateObject<MultiModelSpectrumChannel>(), WIFI_SPECTRUM_6_GHZ);
187 
189  mac.SetType("ns3::ApWifiMac",
190  "Ssid",
191  SsidValue(Ssid("ns-3-ssid")),
192  "BeaconGeneration",
193  BooleanValue(true));
194 
195  NetDeviceContainer apDevice = wifi.Install(phyHelper, mac, wifiApNode);
196 
197  mac.SetType("ns3::StaWifiMac",
198  "Ssid",
199  SsidValue(Ssid("wrong-ssid")),
200  "ActiveProbing",
201  BooleanValue(false));
202  mac.SetEmlsrManager("ns3::DefaultEmlsrManager",
203  "EmlsrLinkSet",
205  "MainPhyId",
207 
208  NetDeviceContainer staDevices = wifi.Install(phyHelper, mac, wifiStaNodes);
209 
210  m_apMac = DynamicCast<ApWifiMac>(DynamicCast<WifiNetDevice>(apDevice.Get(0))->GetMac());
211 
212  for (uint32_t i = 0; i < staDevices.GetN(); i++)
213  {
214  auto device = DynamicCast<WifiNetDevice>(staDevices.Get(i));
215  auto staMac = DynamicCast<StaWifiMac>(device->GetMac());
216  NS_ASSERT_MSG(i < m_paddingDelay.size(), "Not enough padding delay values provided");
217  staMac->GetEmlsrManager()->SetAttribute("EmlsrPaddingDelay",
218  TimeValue(m_paddingDelay.at(i)));
219  NS_ASSERT_MSG(i < m_transitionDelay.size(), "Not enough transition delay values provided");
220  staMac->GetEmlsrManager()->SetAttribute("EmlsrTransitionDelay",
222  }
223 
224  if (m_nNonEmlsrStations > 0)
225  {
226  // create the other non-AP MLDs for which EMLSR is not activated
227  wifi.ConfigEhtOptions("EmlsrActivated", BooleanValue(false));
228  NodeContainer otherStaNodes(m_nNonEmlsrStations);
229  staDevices.Add(wifi.Install(phyHelper, mac, otherStaNodes));
230  wifiStaNodes.Add(otherStaNodes);
231  }
232 
233  for (uint32_t i = 0; i < staDevices.GetN(); i++)
234  {
235  auto device = DynamicCast<WifiNetDevice>(staDevices.Get(i));
236  m_staMacs.push_back(DynamicCast<StaWifiMac>(device->GetMac()));
237  }
238 
239  // Trace PSDUs passed to the PHY on AP MLD and non-AP MLDs
240  for (uint8_t phyId = 0; phyId < m_apMac->GetDevice()->GetNPhys(); phyId++)
241  {
243  "/NodeList/0/DeviceList/*/$ns3::WifiNetDevice/Phys/" + std::to_string(phyId) +
244  "/PhyTxPsduBegin",
246  }
247  for (std::size_t i = 0; i < m_nEmlsrStations + m_nNonEmlsrStations; i++)
248  {
249  for (uint8_t phyId = 0; phyId < m_staMacs[i]->GetDevice()->GetNPhys(); phyId++)
250  {
252  "/NodeList/" + std::to_string(i + 1) + "/DeviceList/*/$ns3::WifiNetDevice/Phys/" +
253  std::to_string(phyId) + "/PhyTxPsduBegin",
255  }
256  }
257 
258  // Uncomment the lines below to write PCAP files
259  // phyHelper.EnablePcap("wifi-emlsr_AP", apDevice);
260  // phyHelper.EnablePcap("wifi-emlsr_STA", staDevices);
261 
262  // Assign fixed streams to random variables in use
263  streamNumber += wifi.AssignStreams(apDevice, streamNumber);
264  streamNumber += wifi.AssignStreams(staDevices, streamNumber);
265 
267  Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();
268 
269  for (std::size_t id = 0; id <= m_nEmlsrStations + m_nNonEmlsrStations; id++)
270  {
271  // all non-AP MLDs are co-located
272  positionAlloc->Add(Vector(std::min<double>(id, 1), 0.0, 0.0));
273  }
274  mobility.SetPositionAllocator(positionAlloc);
275 
276  mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
277  mobility.Install(wifiApNode);
278  mobility.Install(wifiStaNodes);
279 
280  // install packet socket on all nodes
281  PacketSocketHelper packetSocket;
282  packetSocket.Install(wifiApNode);
283  packetSocket.Install(wifiStaNodes);
284 
285  // install a packet socket server on all nodes
286  for (auto nodeIt = NodeList::Begin(); nodeIt != NodeList::End(); nodeIt++)
287  {
288  PacketSocketAddress srvAddr;
289  auto device = DynamicCast<WifiNetDevice>((*nodeIt)->GetDevice(0));
290  NS_TEST_ASSERT_MSG_NE(device, nullptr, "Expected a WifiNetDevice");
291  srvAddr.SetSingleDevice(device->GetIfIndex());
292  srvAddr.SetProtocol(1);
293 
294  auto server = CreateObject<PacketSocketServer>();
295  server->SetLocal(srvAddr);
296  (*nodeIt)->AddApplication(server);
297  server->SetStartTime(Seconds(0)); // now
298  server->SetStopTime(m_duration);
299  }
300 
301  // set DL and UL packet sockets
302  for (const auto& staMac : m_staMacs)
303  {
304  m_dlSockets.emplace_back();
305  m_dlSockets.back().SetSingleDevice(m_apMac->GetDevice()->GetIfIndex());
306  m_dlSockets.back().SetPhysicalAddress(staMac->GetDevice()->GetAddress());
307  m_dlSockets.back().SetProtocol(1);
308 
309  m_ulSockets.emplace_back();
310  m_ulSockets.back().SetSingleDevice(staMac->GetDevice()->GetIfIndex());
311  m_ulSockets.back().SetPhysicalAddress(m_apMac->GetDevice()->GetAddress());
312  m_ulSockets.back().SetProtocol(1);
313  }
314 
315  // schedule ML setup for one station at a time
316  m_apMac->TraceConnectWithoutContext("AssociatedSta",
318  Simulator::Schedule(Seconds(0), [&]() { m_staMacs[0]->SetSsid(Ssid("ns-3-ssid")); });
319 }
320 
323  std::size_t staId,
324  std::size_t count,
325  std::size_t pktSize) const
326 {
327  auto client = CreateObject<PacketSocketClient>();
328  client->SetAttribute("PacketSize", UintegerValue(pktSize));
329  client->SetAttribute("MaxPackets", UintegerValue(count));
330  client->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
331  client->SetRemote(dir == DOWNLINK ? m_dlSockets.at(staId) : m_ulSockets.at(staId));
332  client->SetStartTime(Seconds(0)); // now
333  client->SetStopTime(m_duration - Simulator::Now());
334 
335  return client;
336 }
337 
338 void
340 {
341  if (m_lastAid == aid)
342  {
343  // another STA of this non-AP MLD has already fired this callback
344  return;
345  }
346  m_lastAid = aid;
347 
348  // wait some time (5ms) to allow the completion of association
349  auto delay = MilliSeconds(5);
350 
351  if (m_establishBaDl)
352  {
353  // trigger establishment of BA agreement with AP as originator
354  Simulator::Schedule(delay, [=, this]() {
355  m_apMac->GetDevice()->GetNode()->AddApplication(
356  GetApplication(DOWNLINK, aid - 1, 4, 1000));
357  });
358 
359  delay += MilliSeconds(5);
360  }
361 
362  if (m_establishBaUl)
363  {
364  // trigger establishment of BA agreement with AP as recipient
365  Simulator::Schedule(delay, [=, this]() {
366  m_staMacs[aid - 1]->GetDevice()->GetNode()->AddApplication(
367  GetApplication(UPLINK, aid - 1, 4, 1000));
368  });
369 
370  delay += MilliSeconds(5);
371  }
372 
373  Simulator::Schedule(delay, [=, this]() {
375  {
376  // make the next STA start ML discovery & setup
377  m_staMacs[aid]->SetSsid(Ssid("ns-3-ssid"));
378  return;
379  }
380  // all stations associated; start traffic if needed
381  StartTraffic();
382  });
383 }
384 
385 void
387  Mac48Address dest,
388  uint8_t linkId,
389  WifiQueueBlockedReason reason,
390  bool blocked,
391  std::string description,
392  bool testUnblockedForOtherReasons)
393 {
395  auto mask = mac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, linkId);
396  NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
397  true,
398  description << ": Expected to find a mask for EMLSR link " << +linkId);
399  if (blocked)
400  {
401  NS_TEST_EXPECT_MSG_EQ(mask->test(static_cast<std::size_t>(reason)),
402  true,
403  description << ": Expected EMLSR link " << +linkId
404  << " to be blocked for reason " << reason);
405  if (testUnblockedForOtherReasons)
406  {
407  NS_TEST_EXPECT_MSG_EQ(mask->count(),
408  1,
409  description << ": Expected EMLSR link " << +linkId
410  << " to be blocked for one reason only");
411  }
412  }
413  else if (testUnblockedForOtherReasons)
414  {
415  NS_TEST_EXPECT_MSG_EQ(mask->none(),
416  true,
417  description << ": Expected EMLSR link " << +linkId
418  << " to be unblocked");
419  }
420  else
421  {
422  NS_TEST_EXPECT_MSG_EQ(mask->test(static_cast<std::size_t>(reason)),
423  false,
424  description << ": Expected EMLSR link " << +linkId
425  << " to be unblocked for reason " << reason);
426  }
427 }
428 
429 EmlOmnExchangeTest::EmlOmnExchangeTest(const std::set<uint8_t>& linksToEnableEmlsrOn,
430  Time transitionTimeout)
431  : EmlsrOperationsTestBase("Check EML Notification exchange"),
432  m_checkEmlsrLinksCount(0),
433  m_emlNotificationDroppedCount(0)
434 {
435  m_linksToEnableEmlsrOn = linksToEnableEmlsrOn;
436  m_nEmlsrStations = 1;
438  m_transitionTimeout = transitionTimeout;
439  m_duration = Seconds(0.5);
440 }
441 
442 void
444 {
446 
447  m_errorModel = CreateObject<ListErrorModel>();
448  for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
449  {
451  }
452 
453  m_staMacs[0]->TraceConnectWithoutContext("AckedMpdu",
455  m_staMacs[0]->TraceConnectWithoutContext("DroppedMpdu",
457 }
458 
459 void
461  uint8_t phyId,
462  WifiConstPsduMap psduMap,
463  WifiTxVector txVector,
464  double txPowerW)
465 {
466  EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
467  auto linkId = m_txPsdus.back().linkId;
468 
469  auto psdu = psduMap.begin()->second;
470 
471  switch (psdu->GetHeader(0).GetType())
472  {
474  NS_TEST_EXPECT_MSG_EQ(+linkId, +m_mainPhyId, "AssocReq not sent by the main PHY");
475  CheckEmlCapabilitiesInAssocReq(*psdu->begin(), txVector, linkId);
476  break;
477 
479  CheckEmlCapabilitiesInAssocResp(*psdu->begin(), txVector, linkId);
480  break;
481 
482  case WIFI_MAC_MGT_ACTION:
483  if (auto [category, action] = WifiActionHeader::Peek(psdu->GetPayload(0));
484  category == WifiActionHeader::PROTECTED_EHT &&
485  action.protectedEhtAction ==
486  WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
487  {
488  CheckEmlNotification(psdu, txVector, linkId);
489 
491  m_staMacs[0]->GetLinkIdByAddress(psdu->GetAddr2()) == linkId)
492  {
493  // transmitted by non-AP MLD, we need to corrupt it
494  m_uidList.push_front(psdu->GetPacket()->GetUid());
496  }
497  break;
498  }
499 
500  default:;
501  }
502 }
503 
504 void
506  const WifiTxVector& txVector,
507  uint8_t linkId)
508 {
509  MgtAssocRequestHeader frame;
510  mpdu->GetPacket()->PeekHeader(frame);
511 
512  const auto& mle = frame.Get<MultiLinkElement>();
513  NS_TEST_ASSERT_MSG_EQ(mle.has_value(), true, "Multi-Link Element must be present in AssocReq");
514 
515  NS_TEST_ASSERT_MSG_EQ(mle->HasEmlCapabilities(),
516  true,
517  "Multi-Link Element in AssocReq must have EML Capabilities");
518  NS_TEST_ASSERT_MSG_EQ(mle->IsEmlsrSupported(),
519  true,
520  "EML Support subfield of EML Capabilities in AssocReq must be set to 1");
521  NS_TEST_ASSERT_MSG_EQ(mle->GetEmlsrPaddingDelay(),
522  m_paddingDelay.at(0),
523  "Unexpected Padding Delay in EML Capabilities included in AssocReq");
524  NS_TEST_ASSERT_MSG_EQ(mle->GetEmlsrTransitionDelay(),
525  m_transitionDelay.at(0),
526  "Unexpected Transition Delay in EML Capabilities included in AssocReq");
527 }
528 
529 void
531  const WifiTxVector& txVector,
532  uint8_t linkId)
533 {
534  bool sentToEmlsrClient =
535  (m_staMacs[0]->GetLinkIdByAddress(mpdu->GetHeader().GetAddr1()) == linkId);
536 
537  if (!sentToEmlsrClient)
538  {
539  // nothing to check
540  return;
541  }
542 
544  mpdu->GetPacket()->PeekHeader(frame);
545 
546  const auto& mle = frame.Get<MultiLinkElement>();
547  NS_TEST_ASSERT_MSG_EQ(mle.has_value(), true, "Multi-Link Element must be present in AssocResp");
548 
549  NS_TEST_ASSERT_MSG_EQ(mle->HasEmlCapabilities(),
550  true,
551  "Multi-Link Element in AssocResp must have EML Capabilities");
552  NS_TEST_ASSERT_MSG_EQ(mle->IsEmlsrSupported(),
553  true,
554  "EML Support subfield of EML Capabilities in AssocResp must be set to 1");
556  mle->GetTransitionTimeout(),
558  "Unexpected Transition Timeout in EML Capabilities included in AssocResp");
559 }
560 
561 void
563  const WifiTxVector& txVector,
564  uint8_t linkId)
565 {
566  MgtEmlOmn frame;
567  auto mpdu = *psdu->begin();
568  auto pkt = mpdu->GetPacket()->Copy();
569  WifiActionHeader::Remove(pkt);
570  pkt->RemoveHeader(frame);
571  NS_LOG_DEBUG(frame);
572 
573  bool sentbyNonApMld = m_staMacs[0]->GetLinkIdByAddress(mpdu->GetHeader().GetAddr2()) == linkId;
574 
576  1,
577  "EMLSR Mode subfield should be set to 1 (frame sent by non-AP MLD: "
578  << std::boolalpha << sentbyNonApMld << ")");
579 
581  0,
582  "EMLMR Mode subfield should be set to 0 (frame sent by non-AP MLD: "
583  << std::boolalpha << sentbyNonApMld << ")");
584 
586  true,
587  "Link Bitmap subfield should be present (frame sent by non-AP MLD: "
588  << std::boolalpha << sentbyNonApMld << ")");
589 
590  auto setupLinks = m_staMacs[0]->GetSetupLinkIds();
591  std::list<uint8_t> expectedEmlsrLinks;
592  std::set_intersection(setupLinks.begin(),
593  setupLinks.end(),
594  m_linksToEnableEmlsrOn.begin(),
596  std::back_inserter(expectedEmlsrLinks));
597 
598  NS_TEST_EXPECT_MSG_EQ((expectedEmlsrLinks == frame.GetLinkBitmap()),
599  true,
600  "Unexpected Link Bitmap subfield (frame sent by non-AP MLD: "
601  << std::boolalpha << sentbyNonApMld << ")");
602 
603  if (!sentbyNonApMld)
604  {
605  // the frame has been sent by the AP MLD
608  0,
609  "EMLSR Parameter Update Control should be set to 0 in frames sent by the AP MLD");
610 
611  // as soon as the non-AP MLD receives this frame, it sets the EMLSR links
612  auto delay = WifiPhy::CalculateTxDuration(psdu,
613  txVector,
614  m_staMacs[0]->GetWifiPhy(linkId)->GetPhyBand()) +
615  MicroSeconds(1); // to account for propagation delay
616  Simulator::Schedule(delay, &EmlOmnExchangeTest::CheckEmlsrLinks, this);
617  }
618 
620  +linkId,
621  "EML Notification received on unexpected link (frame sent by non-AP MLD: "
622  << std::boolalpha << sentbyNonApMld << ")");
623 }
624 
625 void
627 {
628  const auto& hdr = mpdu->GetHeader();
629 
630  if (hdr.IsMgt() && hdr.IsAction())
631  {
632  if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
633  category == WifiActionHeader::PROTECTED_EHT &&
634  action.protectedEhtAction ==
635  WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
636  {
637  // the EML Operating Mode Notification frame that the non-AP MLD sent has been
638  // acknowledged; after the transition timeout, the EMLSR links have been set
639  Simulator::Schedule(m_transitionTimeout + NanoSeconds(1),
641  this);
642  }
643  }
644 }
645 
646 void
648 {
649  const auto& hdr = mpdu->GetHeader();
650 
651  if (hdr.IsMgt() && hdr.IsAction())
652  {
653  if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
654  category == WifiActionHeader::PROTECTED_EHT &&
655  action.protectedEhtAction ==
656  WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
657  {
658  // the EML Operating Mode Notification frame has been dropped. Don't
659  // corrupt it anymore
661  }
662  }
663 }
664 
665 void
667 {
669 
670  auto setupLinks = m_staMacs[0]->GetSetupLinkIds();
671  std::set<uint8_t> expectedEmlsrLinks;
672  std::set_intersection(setupLinks.begin(),
673  setupLinks.end(),
674  m_linksToEnableEmlsrOn.begin(),
676  std::inserter(expectedEmlsrLinks, expectedEmlsrLinks.end()));
677 
678  NS_TEST_EXPECT_MSG_EQ((expectedEmlsrLinks == m_staMacs[0]->GetEmlsrManager()->GetEmlsrLinks()),
679  true,
680  "Unexpected set of EMLSR links)");
681 }
682 
683 void
685 {
686  Simulator::Stop(m_duration);
687  Simulator::Run();
688 
690  2,
691  "Unexpected number of times CheckEmlsrLinks() is called");
694  1,
695  "Unexpected number of times the EML Notification frame is dropped due to max retry limit");
696 
697  Simulator::Destroy();
698 }
699 
701  : EmlsrOperationsTestBase("Check EML DL TXOP transmissions (" +
702  std::to_string(params.nEmlsrStations) + "," +
703  std::to_string(params.nNonEmlsrStations) + ")"),
704  m_emlsrLinks(params.linksToEnableEmlsrOn),
705  m_emlsrEnabledTime(0),
706  m_fe2to3delay(MilliSeconds(20)),
707  m_countQoSframes(0),
708  m_countBlockAck(0)
709 {
710  m_nEmlsrStations = params.nEmlsrStations;
711  m_nNonEmlsrStations = params.nNonEmlsrStations;
712  m_linksToEnableEmlsrOn = {}; // do not enable EMLSR right after association
713  m_mainPhyId = 1;
714  m_paddingDelay = params.paddingDelay;
715  m_transitionDelay = params.transitionDelay;
716  m_transitionTimeout = params.transitionTimeout;
717  m_establishBaDl = true;
718  m_duration = Seconds(1.5);
719 
720  NS_ABORT_MSG_IF(params.linksToEnableEmlsrOn.size() < 2,
721  "This test requires at least two links to be configured as EMLSR links");
722 }
723 
724 void
726  uint8_t phyId,
727  WifiConstPsduMap psduMap,
728  WifiTxVector txVector,
729  double txPowerW)
730 {
731  EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
732  auto linkId = m_txPsdus.back().linkId;
733 
734  auto psdu = psduMap.begin()->second;
735  auto nodeId = mac->GetDevice()->GetNode()->GetId();
736 
737  switch (psdu->GetHeader(0).GetType())
738  {
740  NS_ASSERT_MSG(nodeId > 0, "APs do not send AssocReq frames");
741  if (nodeId <= m_nEmlsrStations)
742  {
743  NS_TEST_EXPECT_MSG_EQ(+linkId, +m_mainPhyId, "AssocReq not sent by the main PHY");
744  // this AssocReq is being sent by an EMLSR client. The other EMLSR links should be
745  // in powersave mode after association; we let the non-EMLSR links transition to
746  // active mode (by sending data null frames) after association
747  for (const auto id : m_staMacs.at(nodeId - 1)->GetLinkIds())
748  {
749  if (id != linkId && m_emlsrLinks.count(id) == 1)
750  {
751  m_staMacs[nodeId - 1]->SetPowerSaveMode({true, id});
752  }
753  }
754  }
755  break;
756 
757  case WIFI_MAC_MGT_ACTION: {
758  auto [category, action] = WifiActionHeader::Peek(psdu->GetPayload(0));
759 
760  if (nodeId == 0 && category == WifiActionHeader::PROTECTED_EHT &&
761  action.protectedEhtAction ==
762  WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
763  {
764  CheckEmlNotificationFrame(*psdu->begin(), txVector, linkId);
765  }
766  else if (category == WifiActionHeader::BLOCK_ACK &&
767  action.blockAck == WifiActionHeader::BLOCK_ACK_ADDBA_REQUEST)
768  {
769  CheckPmModeAfterAssociation(psdu->GetAddr1());
770  }
771  }
772  break;
773 
775  CheckInitialControlFrame(*psdu->begin(), txVector, linkId);
776  break;
777 
778  case WIFI_MAC_QOSDATA:
779  CheckQosFrames(psduMap, txVector, linkId);
780  break;
781 
783  CheckBlockAck(psduMap, txVector, phyId);
784  break;
785 
786  default:;
787  }
788 }
789 
790 void
792 {
794 
795  m_errorModel = CreateObject<ListErrorModel>();
796  for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
797  {
799  }
800 
802  {MicroSeconds(3200), MicroSeconds(3200), MicroSeconds(3200)});
803 
805  {
806  auto muScheduler =
807  CreateObjectWithAttributes<RrMultiUserScheduler>("EnableUlOfdma", BooleanValue(false));
808  m_apMac->AggregateObject(muScheduler);
809  for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
810  {
811  m_apMac->GetFrameExchangeManager(linkId)->GetAckManager()->SetAttribute(
812  "DlMuAckSequenceType",
813  EnumValue(WifiAcknowledgment::DL_MU_AGGREGATE_TF));
814  }
815  }
816 }
817 
818 void
820 {
822  {
823  // we are done with association and Block Ack agreement; we can now enable EMLSR mode
824  m_lastAid = 0;
825  EnableEmlsrMode();
826  return;
827  }
828 
829  // we are done with sending EML Operating Mode Notification frames. We can now generate
830  // packets for all non-AP MLDs
831  for (std::size_t i = 0; i < m_nEmlsrStations + m_nNonEmlsrStations; i++)
832  {
833  // when multiple non-AP MLDs are present, MU transmission are used. Given that the
834  // available bandwidth decreases as the number of non-AP MLDs increases, compute the
835  // number of packets to generate so that we always have two A-MPDUs per non-AP MLD
836  std::size_t count = 8 / (m_nEmlsrStations + m_nNonEmlsrStations);
838  }
839 
840  // in case of 2 EMLSR clients using no non-EMLSR link, generate one additional short
841  // packet to each EMLSR client to test transition delay
842  if (m_nEmlsrStations == 2 && m_apMac->GetNLinks() == m_emlsrLinks.size())
843  {
844  Simulator::Schedule(m_fe2to3delay, [&]() {
845  m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, 0, 1, 40));
846  m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, 1, 1, 40));
847  });
848  }
849 
850  // schedule the transmission of EML Operating Mode Notification frames to disable EMLSR mode
851  // and the generation of other packets destined to the EMLSR clients
852  for (std::size_t id = 0; id < m_nEmlsrStations; id++)
853  {
854  Simulator::Schedule(m_fe2to3delay + MilliSeconds(5 * (id + 1)), [=, this]() {
855  m_staMacs.at(id)->GetEmlsrManager()->SetAttribute(
856  "EmlsrLinkSet",
858  });
859 
860  Simulator::Schedule(m_fe2to3delay + MilliSeconds(5 * (m_nEmlsrStations + 1)), [=, this]() {
861  m_apMac->GetDevice()->GetNode()->AddApplication(
862  GetApplication(DOWNLINK, id, 8 / m_nEmlsrStations, 650));
863  });
864  }
865 }
866 
867 void
869 {
870  m_staMacs.at(m_lastAid)->GetEmlsrManager()->SetAttribute(
871  "EmlsrLinkSet",
873  m_lastAid++;
874  Simulator::Schedule(MilliSeconds(5), [=, this]() {
876  {
877  // make the next STA send EML Notification frame
878  EnableEmlsrMode();
879  return;
880  }
881  // all stations enabled EMLSR mode; start traffic
883  StartTraffic();
884  });
885 }
886 
887 void
889 {
890  auto psduIt = m_txPsdus.cbegin();
891 
892  // lambda to jump to the next QoS data frame or MU-RTS Trigger Frame transmitted
893  // to an EMLSR client
894  auto jumpToQosDataOrMuRts = [&]() {
895  while (psduIt != m_txPsdus.cend() &&
896  !psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData())
897  {
898  auto psdu = psduIt->psduMap.cbegin()->second;
899  if (psdu->GetHeader(0).IsTrigger())
900  {
901  CtrlTriggerHeader trigger;
902  psdu->GetPayload(0)->PeekHeader(trigger);
903  if (trigger.IsMuRts())
904  {
905  break;
906  }
907  }
908  psduIt++;
909  }
910  };
911 
964  for (std::size_t i = 0; i < m_nEmlsrStations + m_nNonEmlsrStations; i++)
965  {
966  std::set<uint8_t> linkIds;
967 
968  jumpToQosDataOrMuRts();
969  NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend() &&
970  psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData()),
971  true,
972  "Expected at least one QoS data frame before enabling EMLSR mode");
973  linkIds.insert(psduIt->linkId);
974  const auto firstAmpduTxEnd =
975  psduIt->startTx +
976  WifiPhy::CalculateTxDuration(psduIt->psduMap,
977  psduIt->txVector,
978  m_staMacs[i]->GetWifiPhy(psduIt->linkId)->GetPhyBand());
979  psduIt++;
980 
981  jumpToQosDataOrMuRts();
982  NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend() &&
983  psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData()),
984  true,
985  "Expected at least two QoS data frames before enabling EMLSR mode");
986  linkIds.insert(psduIt->linkId);
987  const auto secondAmpduTxStart = psduIt->startTx;
988  psduIt++;
989 
995  auto setupLinks = m_staMacs[i]->GetSetupLinkIds();
996  if (i < m_nEmlsrStations &&
997  std::none_of(setupLinks.begin(), setupLinks.end(), [&](auto&& linkId) {
998  return linkId != m_mainPhyId && m_emlsrLinks.count(linkId) == 0;
999  }))
1000  {
1001  NS_TEST_EXPECT_MSG_EQ(linkIds.size(),
1002  1,
1003  "Expected both A-MPDUs to be sent on the same link");
1004  NS_TEST_EXPECT_MSG_EQ(*linkIds.begin(), +m_mainPhyId, "A-MPDUs sent on incorrect link");
1005  NS_TEST_EXPECT_MSG_LT(firstAmpduTxEnd,
1006  secondAmpduTxStart,
1007  "A-MPDUs are not sent one after another");
1008  }
1013  else
1014  {
1015  NS_TEST_EXPECT_MSG_EQ(linkIds.size(),
1016  2,
1017  "Expected A-MPDUs to be sent on distinct links");
1018  NS_TEST_EXPECT_MSG_GT(firstAmpduTxEnd,
1019  secondAmpduTxStart,
1020  "A-MPDUs are not sent concurrently");
1021  }
1022  }
1023 
1086  using FrameExchange = std::list<decltype(psduIt)>;
1087 
1088  std::vector<std::list<FrameExchange>> frameExchanges(m_nEmlsrStations);
1089 
1090  // compute all frame exchanges involving EMLSR clients
1091  while (psduIt != m_txPsdus.cend())
1092  {
1093  jumpToQosDataOrMuRts();
1094  if (psduIt == m_txPsdus.cend())
1095  {
1096  break;
1097  }
1098 
1099  if (IsTrigger(psduIt->psduMap))
1100  {
1101  CtrlTriggerHeader trigger;
1102  psduIt->psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
1103  // this is an MU-RTS TF starting a new frame exchange sequence; add it to all
1104  // the addressed EMLSR clients
1105  NS_TEST_ASSERT_MSG_EQ(trigger.IsMuRts(),
1106  true,
1107  "jumpToQosDataOrMuRts does not return TFs other than MU-RTS");
1108  for (const auto& userInfo : trigger)
1109  {
1110  for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1111  {
1112  if (m_staMacs.at(i)->GetAssociationId() == userInfo.GetAid12())
1113  {
1114  frameExchanges.at(i).emplace_back(FrameExchange{psduIt});
1115  break;
1116  }
1117  }
1118  }
1119  psduIt++;
1120  continue;
1121  }
1122 
1123  // we get here if psduIt points to a psduMap containing QoS data frame(s); find (if any)
1124  // the QoS data frame(s) addressed to EMLSR clients and add them to the appropriate
1125  // frame exchange sequence
1126  for (const auto& staIdPsduPair : psduIt->psduMap)
1127  {
1128  std::for_each_n(m_staMacs.cbegin(), m_nEmlsrStations, [&](auto&& staMac) {
1129  if (!staMac->GetLinkIdByAddress(staIdPsduPair.second->GetAddr1()))
1130  {
1131  // not addressed to this non-AP MLD
1132  return;
1133  }
1134  // a QoS data frame starts a new frame exchange sequence if there is no previous
1135  // MU-RTS TF that has been sent on the same link and is not already followed by
1136  // a QoS data frame
1137  std::size_t id = staMac->GetDevice()->GetNode()->GetId() - 1;
1138  for (auto& frameExchange : frameExchanges.at(id))
1139  {
1140  if (IsTrigger(frameExchange.front()->psduMap) &&
1141  frameExchange.front()->linkId == psduIt->linkId &&
1142  frameExchange.size() == 1)
1143  {
1144  auto it = std::next(frameExchange.front());
1145  while (it != m_txPsdus.end())
1146  {
1147  // stop at the first frame other than CTS sent on this link
1148  if (it->linkId == psduIt->linkId &&
1149  !it->psduMap.begin()->second->GetHeader(0).IsCts())
1150  {
1151  break;
1152  }
1153  ++it;
1154  }
1155  if (it == psduIt)
1156  {
1157  // the QoS data frame actually followed the MU-RTS TF
1158  frameExchange.emplace_back(psduIt);
1159  return;
1160  }
1161  }
1162  }
1163  frameExchanges.at(id).emplace_back(FrameExchange{psduIt});
1164  });
1165  }
1166  psduIt++;
1167  }
1168 
1175  for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1176  {
1177  NS_TEST_EXPECT_MSG_GT_OR_EQ(frameExchanges.at(i).size(),
1178  2,
1179  "Expected at least 2 frame exchange sequences "
1180  << "involving EMLSR client " << i);
1181 
1182  auto firstExchangeIt = frameExchanges.at(i).begin();
1183  auto secondExchangeIt = std::next(firstExchangeIt);
1184 
1185  const auto firstAmpduTxEnd =
1186  firstExchangeIt->back()->startTx +
1187  WifiPhy::CalculateTxDuration(
1188  firstExchangeIt->back()->psduMap,
1189  firstExchangeIt->back()->txVector,
1190  m_staMacs[i]->GetWifiPhy(firstExchangeIt->back()->linkId)->GetPhyBand());
1191  const auto secondAmpduTxStart = secondExchangeIt->front()->startTx;
1192 
1193  if (m_staMacs[i]->GetNLinks() == m_emlsrLinks.size())
1194  {
1195  // all links are EMLSR links
1196  NS_TEST_EXPECT_MSG_EQ(IsTrigger(firstExchangeIt->front()->psduMap),
1197  true,
1198  "Expected an MU-RTS TF as ICF of first frame exchange sequence");
1200  firstExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1201  true,
1202  "Expected a QoS data frame in the first frame exchange sequence");
1203 
1204  NS_TEST_EXPECT_MSG_EQ(IsTrigger(secondExchangeIt->front()->psduMap),
1205  true,
1206  "Expected an MU-RTS TF as ICF of second frame exchange sequence");
1208  secondExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1209  true,
1210  "Expected a QoS data frame in the second frame exchange sequence");
1211 
1212  NS_TEST_EXPECT_MSG_LT(firstAmpduTxEnd,
1213  secondAmpduTxStart,
1214  "A-MPDUs are not sent one after another");
1215  }
1216  else
1217  {
1218  std::vector<uint8_t> nonEmlsrIds;
1219  auto setupLinks = m_staMacs[i]->GetSetupLinkIds();
1220  std::set_difference(setupLinks.begin(),
1221  setupLinks.end(),
1222  m_emlsrLinks.begin(),
1223  m_emlsrLinks.end(),
1224  std::back_inserter(nonEmlsrIds));
1225  NS_TEST_ASSERT_MSG_EQ(nonEmlsrIds.size(), 1, "Unexpected number of non-EMLSR links");
1226 
1227  auto nonEmlsrLinkExchangeIt = firstExchangeIt->front()->linkId == nonEmlsrIds[0]
1228  ? firstExchangeIt
1229  : secondExchangeIt;
1230  NS_TEST_EXPECT_MSG_EQ(IsTrigger(nonEmlsrLinkExchangeIt->front()->psduMap),
1231  false,
1232  "Did not expect an MU-RTS TF as ICF on non-EMLSR link");
1234  nonEmlsrLinkExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1235  true,
1236  "Expected a QoS data frame on the non-EMLSR link");
1237 
1238  auto emlsrLinkExchangeIt =
1239  nonEmlsrLinkExchangeIt == firstExchangeIt ? secondExchangeIt : firstExchangeIt;
1240  NS_TEST_EXPECT_MSG_NE(+emlsrLinkExchangeIt->front()->linkId,
1241  +nonEmlsrIds[0],
1242  "Expected this exchange not to occur on non-EMLSR link");
1243  NS_TEST_EXPECT_MSG_EQ(IsTrigger(emlsrLinkExchangeIt->front()->psduMap),
1244  true,
1245  "Expected an MU-RTS TF as ICF on the EMLSR link");
1247  emlsrLinkExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1248  true,
1249  "Expected a QoS data frame on the EMLSR link");
1250 
1251  NS_TEST_EXPECT_MSG_GT(firstAmpduTxEnd,
1252  secondAmpduTxStart,
1253  "A-MPDUs are not sent concurrently");
1254  }
1255 
1256  // we are done with processing the first two frame exchanges, remove them
1257  frameExchanges.at(i).erase(firstExchangeIt);
1258  frameExchanges.at(i).erase(secondExchangeIt);
1259  }
1260 
1286  if (m_nEmlsrStations == 2 && m_apMac->GetNLinks() == m_emlsrLinks.size())
1287  {
1288  // the following checks are only done with 2 EMLSR clients having no non-EMLSR link
1289  for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1290  {
1291  NS_TEST_EXPECT_MSG_GT_OR_EQ(frameExchanges.at(i).size(),
1292  2,
1293  "Expected at least 2 frame exchange sequences "
1294  << "involving EMLSR client " << i);
1295  // the first frame exchange must start with an ICF
1296  auto firstExchangeIt = frameExchanges.at(i).begin();
1297 
1298  NS_TEST_EXPECT_MSG_EQ(IsTrigger(firstExchangeIt->front()->psduMap),
1299  true,
1300  "Expected an MU-RTS TF as ICF of first frame exchange sequence");
1302  firstExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1303  true,
1304  "Expected a QoS data frame in the first frame exchange sequence");
1305  }
1306 
1307  // the second frame exchange is the one that starts first
1308  auto secondExchangeIt = std::next(frameExchanges.at(0).begin())->front()->startTx <
1309  std::next(frameExchanges.at(1).begin())->front()->startTx
1310  ? std::next(frameExchanges.at(0).begin())
1311  : std::next(frameExchanges.at(1).begin());
1312  decltype(secondExchangeIt) thirdExchangeIt;
1313  std::size_t thirdExchangeStaId;
1314 
1315  if (secondExchangeIt == std::next(frameExchanges.at(0).begin()))
1316  {
1317  thirdExchangeIt = std::next(frameExchanges.at(1).begin());
1318  thirdExchangeStaId = 1;
1319  }
1320  else
1321  {
1322  thirdExchangeIt = std::next(frameExchanges.at(0).begin());
1323  thirdExchangeStaId = 0;
1324  }
1325 
1326  // the second frame exchange is not protected by the ICF and starts a SIFS after the end
1327  // of the previous one
1328  NS_TEST_EXPECT_MSG_EQ(IsTrigger(secondExchangeIt->front()->psduMap),
1329  false,
1330  "Expected no ICF for the second frame exchange sequence");
1332  secondExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1333  true,
1334  "Expected a QoS data frame in the second frame exchange sequence");
1335 
1336  // the first two frame exchanges occur on the same link
1337  NS_TEST_EXPECT_MSG_EQ(+secondExchangeIt->front()->linkId,
1338  +frameExchanges.at(0).begin()->front()->linkId,
1339  "Expected the first two frame exchanges to occur on the same link");
1340 
1341  auto bAckRespIt = std::prev(secondExchangeIt->front());
1342  NS_TEST_EXPECT_MSG_EQ(bAckRespIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAck(),
1343  true,
1344  "Expected a BlockAck response before the second frame exchange");
1345  auto bAckRespTxEnd =
1346  bAckRespIt->startTx +
1347  WifiPhy::CalculateTxDuration(bAckRespIt->psduMap,
1348  bAckRespIt->txVector,
1349  m_apMac->GetWifiPhy(bAckRespIt->linkId)->GetPhyBand());
1350 
1351  // the second frame exchange starts a SIFS after the previous one
1353  bAckRespTxEnd + m_apMac->GetWifiPhy(bAckRespIt->linkId)->GetSifs(),
1354  secondExchangeIt->front()->startTx,
1355  "Expected the second frame exchange to start a SIFS after the first one");
1356 
1357  // the third frame exchange is protected by MU-RTS and occurs on a different link
1358  NS_TEST_EXPECT_MSG_EQ(IsTrigger(thirdExchangeIt->front()->psduMap),
1359  true,
1360  "Expected an MU-RTS as ICF for the third frame exchange sequence");
1362  thirdExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1363  true,
1364  "Expected a QoS data frame in the third frame exchange sequence");
1365 
1367  +secondExchangeIt->front()->linkId,
1368  +thirdExchangeIt->front()->linkId,
1369  "Expected the second and third frame exchanges to occur on distinct links");
1370 
1371  auto secondQosIt = secondExchangeIt->front();
1372  auto secondQosTxEnd =
1373  secondQosIt->startTx +
1374  WifiPhy::CalculateTxDuration(secondQosIt->psduMap,
1375  secondQosIt->txVector,
1376  m_apMac->GetWifiPhy(secondQosIt->linkId)->GetPhyBand());
1377 
1378  NS_TEST_EXPECT_MSG_GT_OR_EQ(thirdExchangeIt->front()->startTx,
1379  secondQosTxEnd + m_transitionDelay.at(thirdExchangeStaId),
1380  "Transmission started before transition delay");
1381 
1382  // the BlockAck of the third frame exchange is not received correctly, so there should be
1383  // another frame exchange
1384  NS_TEST_EXPECT_MSG_EQ((thirdExchangeIt != frameExchanges.at(thirdExchangeStaId).end()),
1385  true,
1386  "Expected a fourth frame exchange");
1387  auto fourthExchangeIt = std::next(thirdExchangeIt);
1388 
1389  // the fourth frame exchange is protected by MU-RTS
1390  NS_TEST_EXPECT_MSG_EQ(IsTrigger(fourthExchangeIt->front()->psduMap),
1391  true,
1392  "Expected an MU-RTS as ICF for the fourth frame exchange sequence");
1393 
1394  bAckRespIt = std::prev(fourthExchangeIt->front());
1395  NS_TEST_EXPECT_MSG_EQ(bAckRespIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAck(),
1396  true,
1397  "Expected a BlockAck response before the fourth frame exchange");
1398  auto phy = m_apMac->GetWifiPhy(bAckRespIt->linkId);
1399  bAckRespTxEnd = bAckRespIt->startTx + WifiPhy::CalculateTxDuration(bAckRespIt->psduMap,
1400  bAckRespIt->txVector,
1401  phy->GetPhyBand());
1402  auto timeout = phy->GetSifs() + phy->GetSlot() + MicroSeconds(20);
1403 
1404  // the fourth frame exchange starts a PIFS after the previous one because the AP
1405  // performs PIFS recovery (the initial frame in the TXOP was successfully received by
1406  // a non-EMLSR client)
1407  NS_TEST_EXPECT_MSG_GT_OR_EQ(fourthExchangeIt->front()->startTx,
1408  bAckRespTxEnd + phy->GetPifs(),
1409  "Transmission started less than a PIFS after BlockAck");
1410  NS_TEST_EXPECT_MSG_LT(fourthExchangeIt->front()->startTx,
1411  bAckRespTxEnd + phy->GetPifs() +
1412  MicroSeconds(1) /* propagation delay upper bound */,
1413  "Transmission started too much time after BlockAck");
1414 
1415  auto bAckReqIt = std::next(fourthExchangeIt->front(), 2);
1416  NS_TEST_EXPECT_MSG_EQ(bAckReqIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAckReq(),
1417  true,
1418  "Expected a BlockAck request in the fourth frame exchange");
1419 
1420  // we are done with processing the frame exchanges, remove them (two frame exchanges
1421  // per EMLSR client, plus the last one)
1422  frameExchanges.at(0).pop_front();
1423  frameExchanges.at(0).pop_front();
1424  frameExchanges.at(1).pop_front();
1425  frameExchanges.at(1).pop_front();
1426  frameExchanges.at(thirdExchangeStaId).pop_front();
1427  }
1428 
1485  // for each EMLSR client, there should be a frame exchange with ICF and no data frame
1486  // (ICF protects the EML Notification response) and two frame exchanges with data frames
1487  for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1488  {
1489  // the default EMLSR Manager requests to send EML Notification frames on the link where
1490  // the main PHY is operating, hence this link is an EMLSR link and the EML Notification
1491  // frame is protected by an ICF
1492  auto exchangeIt = frameExchanges.at(i).cbegin();
1493 
1494  auto linkIdOpt = m_staMacs[i]->GetLinkForPhy(m_mainPhyId);
1495  NS_TEST_ASSERT_MSG_EQ(linkIdOpt.has_value(),
1496  true,
1497  "Didn't find a link on which the main PHY is operating");
1498 
1499  NS_TEST_EXPECT_MSG_EQ(IsTrigger(exchangeIt->front()->psduMap),
1500  true,
1501  "Expected an MU-RTS TF as ICF of first frame exchange sequence");
1502  NS_TEST_EXPECT_MSG_EQ(+exchangeIt->front()->linkId,
1503  +linkIdOpt.value(),
1504  "ICF was not sent on the expected link");
1505  NS_TEST_EXPECT_MSG_EQ(exchangeIt->size(),
1506  1,
1507  "Expected no data frame in the first frame exchange sequence");
1508 
1509  frameExchanges.at(i).pop_front();
1510 
1511  NS_TEST_EXPECT_MSG_GT_OR_EQ(frameExchanges.at(i).size(),
1512  2,
1513  "Expected at least 2 frame exchange sequences "
1514  << "involving EMLSR client " << i);
1515 
1516  auto firstExchangeIt = frameExchanges.at(i).cbegin();
1517  auto secondExchangeIt = std::next(firstExchangeIt);
1518 
1519  const auto firstAmpduTxEnd =
1520  firstExchangeIt->back()->startTx +
1521  WifiPhy::CalculateTxDuration(
1522  firstExchangeIt->back()->psduMap,
1523  firstExchangeIt->back()->txVector,
1524  m_staMacs[i]->GetWifiPhy(firstExchangeIt->back()->linkId)->GetPhyBand());
1525  const auto secondAmpduTxStart = secondExchangeIt->front()->startTx;
1526 
1528  firstExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1529  true,
1530  "Expected a QoS data frame in the first frame exchange sequence");
1531  NS_TEST_EXPECT_MSG_EQ(firstExchangeIt->size(),
1532  1,
1533  "Expected one frame only in the first frame exchange sequence");
1534 
1536  secondExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1537  true,
1538  "Expected a QoS data frame in the second frame exchange sequence");
1539  NS_TEST_EXPECT_MSG_EQ(secondExchangeIt->size(),
1540  1,
1541  "Expected one frame only in the second frame exchange sequence");
1542 
1543  if (m_staMacs[i]->GetNLinks() == m_emlsrLinks.size())
1544  {
1545  // all links are EMLSR links: the two QoS data frames are sent one after another on
1546  // the link used for sending EML OMN
1548  +firstExchangeIt->front()->linkId,
1549  +linkIdOpt.value(),
1550  "First frame exchange expected to occur on link used to send EML OMN");
1551 
1553  +secondExchangeIt->front()->linkId,
1554  +linkIdOpt.value(),
1555  "Second frame exchange expected to occur on link used to send EML OMN");
1556 
1557  NS_TEST_EXPECT_MSG_LT(firstAmpduTxEnd,
1558  secondAmpduTxStart,
1559  "A-MPDUs are not sent one after another");
1560  }
1561  else
1562  {
1563  // the two QoS data frames are sent concurrently on distinct links
1564  NS_TEST_EXPECT_MSG_NE(+firstExchangeIt->front()->linkId,
1565  +secondExchangeIt->front()->linkId,
1566  "Frame exchanges expected to occur on distinct links");
1567 
1568  NS_TEST_EXPECT_MSG_GT(firstAmpduTxEnd,
1569  secondAmpduTxStart,
1570  "A-MPDUs are not sent concurrently");
1571  }
1572  }
1573 }
1574 
1575 void
1577 {
1578  std::optional<std::size_t> staId;
1579  for (std::size_t id = 0; id < m_nEmlsrStations + m_nNonEmlsrStations; id++)
1580  {
1581  if (m_staMacs.at(id)->GetLinkIdByAddress(address))
1582  {
1583  staId = id;
1584  break;
1585  }
1586  }
1587  NS_TEST_ASSERT_MSG_EQ(staId.has_value(), true, "Not an address of a non-AP MLD " << address);
1588 
1589  // check that all EMLSR links (but the link used for ML setup) of the EMLSR clients
1590  // are considered to be in power save mode by the AP MLD; all the other links have
1591  // transitioned to active mode instead
1592  for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
1593  {
1594  bool psModeExpected =
1595  *staId < m_nEmlsrStations && linkId != m_mainPhyId && m_emlsrLinks.count(linkId) == 1;
1596  auto addr = m_staMacs.at(*staId)->GetAddress();
1597  auto psMode = m_apMac->GetWifiRemoteStationManager(linkId)->IsInPsMode(addr);
1598  NS_TEST_EXPECT_MSG_EQ(psMode,
1599  psModeExpected,
1600  "EMLSR link " << +linkId << " of EMLSR client " << *staId
1601  << " not in " << (psModeExpected ? "PS" : "active")
1602  << " mode");
1603  // check that AP is blocking transmission of QoS data frames on this link
1605  addr,
1606  linkId,
1607  WifiQueueBlockedReason::POWER_SAVE_MODE,
1608  psModeExpected,
1609  "Checking PM mode after association on AP MLD for EMLSR client " +
1610  std::to_string(*staId),
1611  false);
1612  }
1613 }
1614 
1615 void
1617  const WifiTxVector& txVector,
1618  uint8_t linkId)
1619 {
1620  // the AP is replying to a received EMLSR Notification frame
1621  auto pkt = mpdu->GetPacket()->Copy();
1622  const auto& hdr = mpdu->GetHeader();
1623  WifiActionHeader::Remove(pkt);
1624  MgtEmlOmn frame;
1625  pkt->RemoveHeader(frame);
1626 
1627  std::optional<std::size_t> staId;
1628  for (std::size_t id = 0; id < m_nEmlsrStations; id++)
1629  {
1630  if (m_staMacs.at(id)->GetFrameExchangeManager(linkId)->GetAddress() == hdr.GetAddr1())
1631  {
1632  staId = id;
1633  break;
1634  }
1635  }
1636  NS_TEST_ASSERT_MSG_EQ(staId.has_value(),
1637  true,
1638  "Not an address of an EMLSR client " << hdr.GetAddr1());
1639 
1640  // The EMLSR mode change occurs a Transition Timeout after the end of the PPDU carrying the Ack
1641  auto phy = m_apMac->GetWifiPhy(linkId);
1642  auto txDuration =
1643  WifiPhy::CalculateTxDuration(mpdu->GetSize() + 4, // A-MPDU Subframe header size
1644  txVector,
1645  phy->GetPhyBand());
1646  WifiTxVector ackTxVector =
1647  m_staMacs.at(*staId)->GetWifiRemoteStationManager(linkId)->GetAckTxVector(hdr.GetAddr2(),
1648  txVector);
1649  auto ackDuration = WifiPhy::CalculateTxDuration(GetAckSize() + 4, // A-MPDU Subframe header
1650  ackTxVector,
1651  phy->GetPhyBand());
1652 
1653  Simulator::Schedule(txDuration + phy->GetSifs() + ackDuration, [=, this]() {
1654  if (frame.m_emlControl.emlsrMode == 1)
1655  {
1656  // EMLSR mode enabled. Check that all EMLSR links of the EMLSR clients are considered
1657  // to be in active mode by the AP MLD
1658  for (const auto linkId : m_emlsrLinks)
1659  {
1660  auto addr = m_staMacs.at(*staId)->GetAddress();
1661  auto psMode = m_apMac->GetWifiRemoteStationManager(linkId)->IsInPsMode(addr);
1662  NS_TEST_EXPECT_MSG_EQ(psMode,
1663  false,
1664  "EMLSR link " << +linkId << " of EMLSR client " << *staId
1665  << " not in active mode");
1666  // check that AP is not blocking transmission of QoS data frames on this link
1667  CheckBlockedLink(
1668  m_apMac,
1669  addr,
1670  linkId,
1671  WifiQueueBlockedReason::POWER_SAVE_MODE,
1672  false,
1673  "Checking EMLSR links on AP MLD after EMLSR mode is enabled on EMLSR client " +
1674  std::to_string(*staId),
1675  false);
1676  }
1677  }
1678  else
1679  {
1680  // EMLSR mode disabled. Check that all EMLSR links (but the link used to send the
1681  // EML Notification frame) of the EMLSR clients are considered to be in power save
1682  // mode by the AP MLD; the other links are in active mode
1683  for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1684  {
1685  bool psModeExpected = id != linkId && m_emlsrLinks.count(id) == 1;
1686  auto addr = m_staMacs.at(*staId)->GetAddress();
1687  auto psMode = m_apMac->GetWifiRemoteStationManager(id)->IsInPsMode(addr);
1688  NS_TEST_EXPECT_MSG_EQ(psMode,
1689  psModeExpected,
1690  "EMLSR link "
1691  << +id << " of EMLSR client " << *staId << " not in "
1692  << (psModeExpected ? "PS" : "active") << " mode");
1693  // check that AP is blocking transmission of QoS data frames on this link
1694  CheckBlockedLink(
1695  m_apMac,
1696  addr,
1697  id,
1698  WifiQueueBlockedReason::POWER_SAVE_MODE,
1699  psModeExpected,
1700  "Checking links on AP MLD after EMLSR mode is disabled on EMLSR client " +
1701  std::to_string(*staId),
1702  false);
1703  }
1704  }
1705  });
1706 }
1707 
1708 void
1710  const WifiTxVector& txVector,
1711  uint8_t linkId)
1712 {
1713  CtrlTriggerHeader trigger;
1714  mpdu->GetPacket()->PeekHeader(trigger);
1715  if (!trigger.IsMuRts())
1716  {
1717  return;
1718  }
1719 
1721  true,
1722  "Did not expect an ICF before enabling EMLSR mode");
1723 
1726  "Unexpected preamble type for the Initial Control frame");
1727  auto rate = txVector.GetMode().GetDataRate(txVector);
1728  NS_TEST_EXPECT_MSG_EQ((rate == 6e6 || rate == 12e6 || rate == 24e6),
1729  true,
1730  "Unexpected rate for the Initial Control frame: " << rate);
1731 
1732  bool found = false;
1733  Time maxPaddingDelay{};
1734 
1735  for (const auto& userInfo : trigger)
1736  {
1737  auto addr = m_apMac->GetMldOrLinkAddressByAid(userInfo.GetAid12());
1738  NS_TEST_ASSERT_MSG_EQ(addr.has_value(),
1739  true,
1740  "AID " << userInfo.GetAid12() << " not found");
1741 
1742  if (m_apMac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*addr))
1743  {
1744  found = true;
1745 
1746  for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1747  {
1748  if (m_staMacs.at(i)->GetAddress() == *addr)
1749  {
1750  maxPaddingDelay = Max(maxPaddingDelay, m_paddingDelay.at(i));
1751  break;
1752  }
1753  }
1754 
1755  // check that the AP has blocked transmission on all other EMLSR links
1756  for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1757  {
1758  if (!m_apMac->GetWifiRemoteStationManager(id)->GetEmlsrEnabled(*addr))
1759  {
1760  continue;
1761  }
1762 
1764  *addr,
1765  id,
1766  WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1767  id != linkId,
1768  "Checking that AP blocked transmissions on all other EMLSR "
1769  "links after sending ICF to client with AID=" +
1770  std::to_string(userInfo.GetAid12()),
1771  false);
1772  }
1773  }
1774  }
1775 
1776  NS_TEST_EXPECT_MSG_EQ(found, true, "Expected ICF to be addressed to at least an EMLSR client");
1777 
1778  auto txDuration = WifiPhy::CalculateTxDuration(mpdu->GetSize(),
1779  txVector,
1780  m_apMac->GetWifiPhy(linkId)->GetPhyBand());
1781 
1782  if (maxPaddingDelay.IsStrictlyPositive())
1783  {
1784  // compare the TX duration of this Trigger Frame to that of the Trigger Frame with no
1785  // padding added
1786  trigger.SetPaddingSize(0);
1787  auto pkt = Create<Packet>();
1788  pkt->AddHeader(trigger);
1789  auto txDurationWithout =
1790  WifiPhy::CalculateTxDuration(Create<WifiPsdu>(pkt, mpdu->GetHeader()),
1791  txVector,
1792  m_apMac->GetWifiPhy(linkId)->GetPhyBand());
1793 
1794  NS_TEST_EXPECT_MSG_EQ(txDuration,
1795  txDurationWithout + maxPaddingDelay,
1796  "Unexpected TX duration of the MU-RTS TF with padding "
1797  << maxPaddingDelay.As(Time::US));
1798  }
1799 
1800  // check that the EMLSR clients have blocked transmissions on other links after
1801  // receiving this ICF
1802  for (const auto& userInfo : trigger)
1803  {
1804  for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1805  {
1806  if (m_staMacs[i]->GetAssociationId() != userInfo.GetAid12())
1807  {
1808  continue;
1809  }
1810 
1811  Simulator::Schedule(txDuration + NanoSeconds(5), [=, this]() {
1812  for (uint8_t id = 0; id < m_staMacs[i]->GetNLinks(); id++)
1813  {
1814  // non-EMLSR links or links on which ICF is received are not blocked
1816  m_apMac->GetAddress(),
1817  id,
1818  WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1819  id != linkId && m_staMacs[i]->IsEmlsrLink(id),
1820  "Checking EMLSR links on EMLSR client " + std::to_string(i) +
1821  " after receiving ICF");
1822  }
1823  });
1824 
1825  break;
1826  }
1827  }
1828 }
1829 
1830 void
1832  const WifiTxVector& txVector,
1833  uint8_t linkId)
1834 {
1835  if (m_nEmlsrStations != 2 || m_apMac->GetNLinks() != m_emlsrLinks.size() ||
1837 
1838  {
1839  // we are interested in frames sent to test transition delay
1840  return;
1841  }
1842 
1843  std::size_t firstClientId = 0;
1844  std::size_t secondClientId = 1;
1845  auto addr = m_staMacs[secondClientId]->GetAddress();
1846  auto txDuration =
1847  WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
1848 
1849  m_countQoSframes++;
1850 
1851  switch (m_countQoSframes)
1852  {
1853  case 1:
1854  // generate another small packet addressed to the first EMLSR client only
1856  GetApplication(DOWNLINK, firstClientId, 1, 40));
1857  // both EMLSR clients are about to receive a QoS data frame
1858  for (std::size_t clientId : {firstClientId, secondClientId})
1859  {
1860  Simulator::Schedule(txDuration, [=, this]() {
1861  for (uint8_t id = 0; id < m_staMacs[clientId]->GetNLinks(); id++)
1862  {
1863  // link on which QoS data is received is not blocked
1864  CheckBlockedLink(m_staMacs[clientId],
1865  m_apMac->GetAddress(),
1866  id,
1867  WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1868  id != linkId,
1869  "Checking EMLSR links on EMLSR client " +
1870  std::to_string(clientId) +
1871  " after receiving the first QoS data frame");
1872  }
1873  });
1874  }
1875  break;
1876  case 2:
1877  // generate another small packet addressed to the second EMLSR client
1879  GetApplication(DOWNLINK, secondClientId, 1, 40));
1880 
1881  // when the transmission of the second QoS data frame starts, both EMLSR clients are
1882  // still blocking all the links but the one used to receive the QoS data frame
1883  for (std::size_t clientId : {firstClientId, secondClientId})
1884  {
1885  for (uint8_t id = 0; id < m_staMacs[clientId]->GetNLinks(); id++)
1886  {
1887  // link on which QoS data is received is not blocked
1888  CheckBlockedLink(m_staMacs[clientId],
1889  m_apMac->GetAddress(),
1890  id,
1891  WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1892  id != linkId,
1893  "Checking EMLSR links on EMLSR client " +
1894  std::to_string(clientId) +
1895  " when starting the reception of the second QoS frame");
1896  }
1897  }
1898 
1899  // the EMLSR client that is not the recipient of the QoS frame being transmitted will
1900  // switch back to listening mode after a transition delay starting from the end of
1901  // the PPDU carrying this QoS data frame
1902 
1903  // immediately before the end of the PPDU, this link only is not blocked for the EMLSR
1904  // client on the AP MLD
1905  Simulator::Schedule(txDuration - NanoSeconds(1), [=, this]() {
1906  for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1907  {
1909  addr,
1910  id,
1911  WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1912  id != linkId,
1913  "Checking that links of EMLSR client " +
1914  std::to_string(secondClientId) +
1915  " are blocked on the AP MLD before the end of the PPDU");
1916  }
1917  });
1918  // immediately before the end of the PPDU, all the links on the EMLSR client that is not
1919  // the recipient of the second QoS frame are unblocked (they are unblocked when the
1920  // PHY-RXSTART.indication is not received)
1921  Simulator::Schedule(txDuration - NanoSeconds(1), [=, this]() {
1922  for (uint8_t id = 0; id < m_staMacs[secondClientId]->GetNLinks(); id++)
1923  {
1924  CheckBlockedLink(m_staMacs[secondClientId],
1925  m_apMac->GetAddress(),
1926  id,
1927  WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1928  false,
1929  "Checking that links of EMLSR client " +
1930  std::to_string(secondClientId) +
1931  " are unblocked before the end of the second QoS frame");
1932  }
1933  });
1934  // immediately after the end of the PPDU, all links are blocked for the EMLSR client
1935  Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
1936  for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1937  {
1939  addr,
1940  id,
1941  WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
1942  true,
1943  "Checking links of EMLSR client " +
1944  std::to_string(secondClientId) +
1945  " are all blocked on the AP MLD after the end of the PPDU");
1946  }
1947  });
1948  // immediately before the transition delay, all links are still blocked for the EMLSR client
1949  Simulator::Schedule(
1950  txDuration + m_transitionDelay.at(secondClientId) - NanoSeconds(1),
1951  [=, this]() {
1952  for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1953  {
1955  m_apMac,
1956  addr,
1957  id,
1958  WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
1959  true,
1960  "Checking links of EMLSR client " + std::to_string(secondClientId) +
1961  " are all blocked on the AP MLD before the transition delay",
1962  false);
1963  }
1964  });
1965 
1966  // 100 us before the transition delay expires, generate another small packet addressed
1967  // to a non-EMLSR client. The AP will start a TXOP to transmit this frame, while the
1968  // frame addressed to the EMLSR client is still queued because the transition delay has
1969  // not yet elapsed. The transition delay will expire while the AP is transmitting the
1970  // frame to the non-EMLSR client, so that the AP continues the TXOP to transmit the frame
1971  // to the EMLSR client and we can check that the AP performs PIFS recovery after missing
1972  // the BlockAck from the EMLSR client
1973  Simulator::Schedule(txDuration + m_transitionDelay.at(secondClientId) - MicroSeconds(100),
1974  [=, this]() {
1975  m_apMac->GetDevice()->GetNode()->AddApplication(
1977  });
1978 
1979  break;
1980  case 3:
1981  // this is the frame addressed to a non-EMLSR client, which is transmitted before the
1982  // frame addressed to the EMLSR client, because the links of the latter are still blocked
1983  // at the AP because the transition delay has not yet elapsed
1985  psduMap.cbegin()->second->GetAddr1(),
1986  m_staMacs[m_nEmlsrStations]->GetFrameExchangeManager(linkId)->GetAddress(),
1987  "QoS frame not addressed to a non-EMLSR client");
1988 
1989  for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1990  {
1992  addr,
1993  id,
1994  WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
1995  true,
1996  "Checking links of EMLSR client " + std::to_string(secondClientId) +
1997  " are all blocked on the AP MLD before the transition delay");
1998  }
1999  // Block transmissions to the EMLSR client on all the links but the one on which this
2000  // frame is sent, so that the AP will continue this TXOP to send the queued frame to the
2001  // EMLSR client once the transition delay elapses
2002  for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2003  {
2004  if (id != linkId)
2005  {
2006  m_apMac->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED, addr, {id});
2007  }
2008  }
2009  break;
2010  case 4:
2011  // the AP is continuing the TXOP, no need to block transmissions anymore
2012  for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2013  {
2014  m_apMac->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED, addr, {id});
2015  }
2016  // at the end of the fourth QoS frame, this link only is not blocked on the EMLSR
2017  // client receiving the frame
2018  Simulator::Schedule(txDuration, [=, this]() {
2019  for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2020  {
2021  CheckBlockedLink(m_staMacs[secondClientId],
2022  m_apMac->GetAddress(),
2023  id,
2024  WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2025  id != linkId,
2026  "Checking EMLSR links on EMLSR client " +
2027  std::to_string(secondClientId) +
2028  " after receiving the fourth QoS data frame");
2029  }
2030  });
2031  break;
2032  }
2033 }
2034 
2035 void
2037  const WifiTxVector& txVector,
2038  uint8_t phyId)
2039 {
2040  if (m_nEmlsrStations != 2 || m_apMac->GetNLinks() != m_emlsrLinks.size() ||
2042  {
2043  // we are interested in frames sent to test transition delay
2044  return;
2045  }
2046 
2047  if (++m_countBlockAck == 4)
2048  {
2049  // fourth BlockAck is sent by a non-EMLSR client
2050  return;
2051  }
2052 
2053  auto taddr = psduMap.cbegin()->second->GetAddr2();
2054  std::size_t clientId;
2055  if (m_staMacs[0]->GetLinkIdByAddress(taddr))
2056  {
2057  clientId = 0;
2058  }
2059  else
2060  {
2061  NS_TEST_ASSERT_MSG_EQ(m_staMacs[1]->GetLinkIdByAddress(taddr).has_value(),
2062  true,
2063  "Unexpected TA for BlockAck: " << taddr);
2064  clientId = 1;
2065  }
2066 
2067  // find the link on which the main PHY is operating
2068  auto currMainPhyLinkId = m_staMacs[clientId]->GetLinkForPhy(phyId);
2070  currMainPhyLinkId.has_value(),
2071  true,
2072  "Didn't find the link on which the PHY sending the BlockAck is operating");
2073  auto linkId = *currMainPhyLinkId;
2074 
2075  // we need the MLD address to check the status of the container queues
2076  auto addr = m_apMac->GetWifiRemoteStationManager(linkId)->GetMldAddress(taddr);
2077  NS_TEST_ASSERT_MSG_EQ(addr.has_value(), true, "MLD address not found for " << taddr);
2078 
2079  auto apPhy = m_apMac->GetWifiPhy(linkId);
2080  auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, apPhy->GetPhyBand());
2081  auto cfEndTxDuration = WifiPhy::CalculateTxDuration(
2082  Create<WifiPsdu>(Create<Packet>(), WifiMacHeader(WIFI_MAC_CTL_END)),
2083  m_apMac->GetWifiRemoteStationManager(linkId)->GetRtsTxVector(Mac48Address::GetBroadcast(),
2084  txVector.GetChannelWidth()),
2085  apPhy->GetPhyBand());
2086 
2087  switch (m_countBlockAck)
2088  {
2089  case 5:
2090  // the PPDU carrying this BlockAck is corrupted, hence the AP MLD MAC receives the
2091  // PHY-RXSTART indication but it does not receive any frame from the PHY. Therefore,
2092  // at the end of the PPDU transmission, the AP MLD realizes that the EMLSR client has
2093  // not responded and makes an attempt at continuing the TXOP
2094 
2095  // at the end of the PPDU, this link only is not blocked on both the EMLSR client and
2096  // the AP MLD
2097  Simulator::Schedule(txDuration, [=, this]() {
2098  for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2099  {
2100  CheckBlockedLink(m_staMacs[clientId],
2101  m_apMac->GetAddress(),
2102  id,
2103  WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2104  id != linkId,
2105  "Checking links on EMLSR client " + std::to_string(clientId) +
2106  " at the end of fourth BlockAck");
2108  *addr,
2109  id,
2110  WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2111  id != linkId,
2112  "Checking links of EMLSR client " + std::to_string(clientId) +
2113  " on the AP MLD at the end of fourth BlockAck");
2114  }
2115  });
2116  // a SIFS after the end of the PPDU, still this link only is not blocked on both the
2117  // EMLSR client and the AP MLD
2118  Simulator::Schedule(txDuration + apPhy->GetSifs(), [=, this]() {
2119  for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2120  {
2121  CheckBlockedLink(m_staMacs[clientId],
2122  m_apMac->GetAddress(),
2123  id,
2124  WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2125  id != linkId,
2126  "Checking links on EMLSR client " + std::to_string(clientId) +
2127  " a SIFS after the end of fourth BlockAck");
2128  CheckBlockedLink(m_apMac,
2129  *addr,
2130  id,
2131  WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2132  id != linkId,
2133  "Checking links of EMLSR client " + std::to_string(clientId) +
2134  " a SIFS after the end of fourth BlockAck");
2135  }
2136  });
2137  // corrupt this BlockAck so that the AP MLD sends a BlockAckReq later on
2138  {
2139  auto uid = psduMap.cbegin()->second->GetPacket()->GetUid();
2140  m_errorModel->SetList({uid});
2141  }
2142  break;
2143  case 6:
2144  // at the end of the PPDU, this link only is not blocked on both the EMLSR client and
2145  // the AP MLD
2146  Simulator::Schedule(txDuration, [=, this]() {
2147  for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2148  {
2149  CheckBlockedLink(m_staMacs[clientId],
2150  m_apMac->GetAddress(),
2151  id,
2152  WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2153  id != linkId,
2154  "Checking links on EMLSR client " + std::to_string(clientId) +
2155  " at the end of fifth BlockAck");
2157  *addr,
2158  id,
2159  WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2160  id != linkId,
2161  "Checking links of EMLSR client " + std::to_string(clientId) +
2162  " on the AP MLD at the end of fifth BlockAck");
2163  }
2164  });
2165  // before the end of the CF-End frame, still this link only is not blocked on both the
2166  // EMLSR client and the AP MLD
2167  Simulator::Schedule(
2168  txDuration + apPhy->GetSifs() + cfEndTxDuration - MicroSeconds(1),
2169  [=, this]() {
2170  for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2171  {
2172  CheckBlockedLink(m_staMacs[clientId],
2173  m_apMac->GetAddress(),
2174  id,
2175  WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2176  id != linkId,
2177  "Checking links on EMLSR client " + std::to_string(clientId) +
2178  " before the end of CF-End frame");
2180  *addr,
2181  id,
2182  WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2183  id != linkId,
2184  "Checking links of EMLSR client " + std::to_string(clientId) +
2185  " on the AP MLD before the end of CF-End frame");
2186  }
2187  });
2188  // after the end of the CF-End frame, all links for the EMLSR client are blocked on the
2189  // AP MLD
2190  Simulator::Schedule(
2191  txDuration + apPhy->GetSifs() + cfEndTxDuration + MicroSeconds(1),
2192  [=, this]() {
2193  for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2194  {
2196  m_apMac,
2197  *addr,
2198  id,
2199  WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2200  true,
2201  "Checking links of EMLSR client " + std::to_string(clientId) +
2202  " are all blocked on the AP MLD right after the end of CF-End");
2203  }
2204  });
2205  // before the end of the transition delay, all links for the EMLSR client are still
2206  // blocked on the AP MLD
2207  Simulator::Schedule(
2208  txDuration + apPhy->GetSifs() + cfEndTxDuration + m_transitionDelay.at(clientId) -
2209  MicroSeconds(1),
2210  [=, this]() {
2211  for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2212  {
2214  m_apMac,
2215  *addr,
2216  id,
2217  WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2218  true,
2219  "Checking links of EMLSR client " + std::to_string(clientId) +
2220  " are all blocked on the AP MLD before the end of transition delay");
2221  }
2222  });
2223  // immediately after the transition delay, all links for the EMLSR client are unblocked
2224  Simulator::Schedule(
2225  txDuration + apPhy->GetSifs() + cfEndTxDuration + m_transitionDelay.at(clientId) +
2226  MicroSeconds(1),
2227  [=, this]() {
2228  for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2229  {
2231  m_apMac,
2232  *addr,
2233  id,
2234  WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2235  false,
2236  "Checking links of EMLSR client " + std::to_string(clientId) +
2237  " are all unblocked on the AP MLD after the transition delay");
2238  }
2239  });
2240  break;
2241  }
2242 }
2243 
2244 void
2246 {
2247  Simulator::Stop(m_duration);
2248  Simulator::Run();
2249 
2250  CheckResults();
2251 
2252  Simulator::Destroy();
2253 }
2254 
2256  : EmlsrOperationsTestBase("Check EML UL TXOP transmissions"),
2257  m_emlsrLinks(params.linksToEnableEmlsrOn),
2258  m_channelWidth(params.channelWidth),
2259  m_auxPhyChannelWidth(params.auxPhyChannelWidth),
2260  m_mediumSyncDuration(params.mediumSyncDuration),
2261  m_msdMaxNTxops(params.msdMaxNTxops),
2262  m_emlsrEnabledTime(0),
2263  m_firstUlPktsGenTime(0),
2264  m_unblockMainPhyLinkDelay(MilliSeconds(20)),
2265  m_checkBackoffStarted(false),
2266  m_countQoSframes(0),
2267  m_countBlockAck(0),
2268  m_countRtsframes(0),
2269  m_genBackoffIfTxopWithoutTx(params.genBackoffIfTxopWithoutTx)
2270 {
2271  m_nEmlsrStations = 1;
2272  m_nNonEmlsrStations = 0;
2273  m_linksToEnableEmlsrOn = params.linksToEnableEmlsrOn;
2274  m_mainPhyId = 1;
2275 
2276  // when aux PHYs do not switch link, the main PHY switches back to its previous link after
2277  // a TXOP, hence the transition delay must exceed the channel switch delay (default: 250us)
2279  m_establishBaDl = true;
2280  m_establishBaUl = true;
2281  m_duration = Seconds(1);
2282 
2283  NS_ABORT_MSG_IF(params.linksToEnableEmlsrOn.size() < 2,
2284  "This test requires at least two links to be configured as EMLSR links");
2285  for (uint8_t id = 0; id < 3; id++)
2286  {
2287  if (m_emlsrLinks.count(id) == 0)
2288  {
2289  // non-EMLSR link found
2290  m_nonEmlsrLink = id;
2291  break;
2292  }
2293  }
2294 }
2295 
2296 void
2298 {
2299  Config::SetDefault("ns3::EmlsrManager::AuxPhyChannelWidth",
2301  Config::SetDefault("ns3::DefaultEmlsrManager::SwitchAuxPhy", BooleanValue(false));
2302  Config::SetDefault("ns3::EhtConfiguration::MediumSyncDuration",
2304  Config::SetDefault("ns3::EhtConfiguration::MsdMaxNTxops", UintegerValue(m_msdMaxNTxops));
2305  Config::SetDefault("ns3::ChannelAccessManager::GenerateBackoffIfTxopWithoutTx",
2307  // Channel switch delay should be less than RTS TX time + SIFS + CTS TX time, otherwise
2308  // UL TXOPs cannot be initiated by aux PHYs
2309  Config::SetDefault("ns3::WifiPhy::ChannelSwitchDelay", TimeValue(MicroSeconds(75)));
2310 
2312 
2313  m_staMacs[0]->GetQosTxop(AC_BE)->TraceConnectWithoutContext(
2314  "BackoffTrace",
2316 
2317  uint8_t linkId = 0;
2318  // configure channels of the given width
2320  {
2321  uint16_t bw = 20;
2322  uint8_t number = band == WIFI_PHY_BAND_5GHZ ? 36 : 1;
2323 
2324  auto width = std::min<uint16_t>(m_channelWidth, band == WIFI_PHY_BAND_2_4GHZ ? 40 : 160);
2325  while (bw < width)
2326  {
2327  bw *= 2;
2328  number += bw / 20;
2329  }
2330 
2331  for (auto mac : std::initializer_list<Ptr<WifiMac>>{m_apMac, m_staMacs[0]})
2332  {
2333  mac->GetWifiPhy(linkId)->SetOperatingChannel(
2334  WifiPhy::ChannelTuple{number, width, band, 0});
2335  }
2336  linkId++;
2337  }
2338 
2339  // install post reception error model on the AP affiliated with the AP MLD and operating on
2340  // the same link as the main PHY of the EMLSR client
2341  m_errorModel = CreateObject<ListErrorModel>();
2343 }
2344 
2345 void
2346 EmlsrUlTxopTest::BackoffGenerated(uint32_t backoff, uint8_t linkId)
2347 {
2348  NS_LOG_INFO("Backoff value " << backoff << " generated by EMLSR client on link " << +linkId
2349  << "\n");
2350  if (linkId != m_mainPhyId)
2351  {
2352  return; // we are only interested in backoff on main PHY link
2353  }
2354 
2355  if (m_backoffEndTime)
2356  {
2358  {
2359  // another backoff value while checkBackoffStarted is true is generated only if
2360  // GenerateBackoffIfTxopWithoutTx is true
2363  true,
2364  "Another backoff value should not be generated while the main PHY link is blocked");
2365 
2367  Simulator::Now(),
2368  "Backoff generated at unexpected time");
2369  }
2370  else
2371  {
2372  // we are done checking the backoff
2373  m_backoffEndTime.reset();
2374  }
2375  }
2376 
2378  {
2380  m_staMacs[0]->GetChannelAccessManager(linkId)->GetSifs() +
2381  (m_staMacs[0]->GetQosTxop(AC_BE)->GetAifsn(linkId) + backoff) *
2382  m_staMacs[0]->GetChannelAccessManager(linkId)->GetSlot();
2383  }
2384 }
2385 
2386 void
2388  uint8_t phyId,
2389  WifiConstPsduMap psduMap,
2390  WifiTxVector txVector,
2391  double txPowerW)
2392 {
2393  EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
2394  auto linkId = m_txPsdus.back().linkId;
2395 
2396  auto psdu = psduMap.begin()->second;
2397  auto nodeId = mac->GetDevice()->GetNode()->GetId();
2398 
2399  switch (psdu->GetHeader(0).GetType())
2400  {
2402  NS_ASSERT_MSG(nodeId > 0, "APs do not send AssocReq frames");
2403  NS_TEST_EXPECT_MSG_EQ(+linkId, +m_mainPhyId, "AssocReq not sent by the main PHY");
2404  break;
2405 
2406  case WIFI_MAC_CTL_RTS:
2407  CheckRtsFrames(*psdu->begin(), txVector, linkId);
2408  break;
2409 
2410  case WIFI_MAC_CTL_CTS:
2411  CheckCtsFrames(*psdu->begin(), txVector, linkId);
2412  break;
2413 
2414  case WIFI_MAC_QOSDATA:
2415  CheckQosFrames(psduMap, txVector, linkId);
2416  break;
2417 
2418  case WIFI_MAC_CTL_BACKRESP:
2419  CheckBlockAck(psduMap, txVector, linkId);
2420  break;
2421 
2422  default:;
2423  }
2424 }
2425 
2426 void
2428 {
2429  // initially, we prevent transmissions on aux PHY links
2430  auto auxPhyLinks = m_staMacs[0]->GetSetupLinkIds();
2431  auxPhyLinks.erase(m_mainPhyId);
2432  if (m_nonEmlsrLink)
2433  {
2434  auxPhyLinks.erase(*m_nonEmlsrLink);
2435  }
2436  m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2437  m_apMac->GetAddress(),
2438  auxPhyLinks);
2439 
2440  // Association, Block Ack agreement establishment and enabling EMLSR mode have been done.
2441  // After 50ms, schedule:
2442  // - block of transmissions on the link where the main PHY is operating and on the non-EMLSR
2443  // link (if any)
2444  // - the generation of two UL packets
2445  // - after m_unblockMainPhyLinkDelay, unblock transmissions on the link where the main PHY
2446  // is operating, so that the first data frame is transmitted on that link
2447  Simulator::Schedule(MilliSeconds(50), [&]() {
2448  std::set<uint8_t> linkIds;
2449  linkIds.insert(*m_staMacs[0]->GetLinkForPhy(m_mainPhyId));
2450  if (m_nonEmlsrLink)
2451  {
2452  linkIds.insert(*m_nonEmlsrLink);
2453  }
2454  m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2455  m_apMac->GetAddress(),
2456  linkIds);
2457 
2458  NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2459  m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000));
2461 
2462  Simulator::Schedule(m_unblockMainPhyLinkDelay, [&]() {
2463  m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2464  m_apMac->GetAddress(),
2465  {*m_staMacs[0]->GetLinkForPhy(m_mainPhyId)});
2466  });
2467  });
2468 }
2469 
2470 void
2472  const WifiTxVector& txVector,
2473  uint8_t linkId)
2474 {
2475  m_countQoSframes++;
2476 
2477  auto txDuration =
2478  WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2479 
2480  switch (m_countQoSframes)
2481  {
2482  case 1:
2483  case 2:
2484  // do nothing, these are the QoS data frames sent to establish BA agreements in DL and UL
2485  // direction
2486  break;
2487  case 3:
2488  // first UL data frame (transmitted by the main PHY)
2489  if (m_nonEmlsrLink)
2490  {
2491  // generate data packets for another UL data frame, which will be sent on the
2492  // non-EMLSR link
2493  NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2494  m_staMacs[0]->GetDevice()->GetNode()->AddApplication(
2495  GetApplication(UPLINK, 0, 2, 1000));
2496 
2497  // unblock transmissions on the non-EMLSR link once the two packets are queued
2498  Simulator::ScheduleNow([=, this]() {
2499  m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2500  m_apMac->GetAddress(),
2501  {*m_nonEmlsrLink});
2502  });
2503  }
2504 
2505  // check that other EMLSR links are now blocked on the EMLSR client and on the AP MLD
2506  // after this QoS data frame is received
2507  Simulator::ScheduleNow([=, this]() {
2508  for (auto id : m_staMacs[0]->GetLinkIds())
2509  {
2511  m_staMacs[0],
2512  m_apMac->GetAddress(),
2513  id,
2514  WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2515  id != m_staMacs[0]->GetLinkForPhy(m_mainPhyId) && m_staMacs[0]->IsEmlsrLink(id),
2516  "Checking EMLSR links on EMLSR client while sending the first data frame",
2517  false);
2518 
2519  Simulator::Schedule(
2520  txDuration + MicroSeconds(1) /* propagation delay */,
2521  [=, this]() {
2523  m_apMac,
2524  m_staMacs[0]->GetAddress(),
2525  id,
2526  WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2527  id != m_staMacs[0]->GetLinkForPhy(m_mainPhyId) &&
2528  m_staMacs[0]->IsEmlsrLink(id),
2529  "Checking EMLSR links on AP MLD while sending the first data frame");
2530  });
2531  }
2532  });
2533 
2534  if (m_nonEmlsrLink)
2535  {
2536  break;
2537  }
2538  m_countQoSframes++; // if all EMLSR links, next case is already executed now
2539  [[fallthrough]];
2540  case 4:
2541  // check that other EMLSR links are now blocked on the EMLSR client and on the AP MLD
2542  // after this QoS data frame is received
2543  Simulator::ScheduleNow([=, this]() {
2544  // make aux PHYs capable of transmitting frames
2545  auto auxPhyLinks = m_staMacs[0]->GetSetupLinkIds();
2546  auxPhyLinks.erase(m_mainPhyId);
2547  if (m_nonEmlsrLink)
2548  {
2549  auxPhyLinks.erase(*m_nonEmlsrLink);
2550  }
2551  m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2552  m_apMac->GetAddress(),
2553  auxPhyLinks);
2554 
2555  // block transmissions on the link where the main PHY is operating
2556  m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2557  m_apMac->GetAddress(),
2558  {*m_staMacs[0]->GetLinkForPhy(m_mainPhyId)});
2559 
2560  // generate data packets for another UL data frame, which will be sent on a link on
2561  // which an aux PHY is operating
2562  NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2563  m_staMacs[0]->GetDevice()->GetNode()->AddApplication(
2564  GetApplication(UPLINK, 0, 2, 1000));
2565  });
2566  break;
2567  case 5:
2568  // check that other EMLSR links are now blocked on both the EMLSR client and the AP MLD
2569  Simulator::ScheduleNow([=, this]() {
2570  for (auto id : m_staMacs[0]->GetLinkIds())
2571  {
2573  m_staMacs[0],
2574  m_apMac->GetAddress(),
2575  id,
2576  WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2577  id != linkId && m_staMacs[0]->IsEmlsrLink(id),
2578  "Checking EMLSR links on EMLSR client while sending the second data frame",
2579  false);
2580 
2582  m_apMac,
2583  m_staMacs[0]->GetAddress(),
2584  id,
2585  WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2586  id != linkId && m_staMacs[0]->IsEmlsrLink(id),
2587  "Checking EMLSR links on AP MLD while sending the second data frame",
2588  false);
2589  }
2590 
2591  // unblock transmission on the link where the main PHY is operating
2592  m_staMacs[0]->GetMacQueueScheduler()->UnblockQueues(
2593  WifiQueueBlockedReason::TID_NOT_MAPPED,
2594  AC_BE,
2596  m_apMac->GetAddress(),
2597  m_staMacs[0]->GetAddress(),
2598  {0},
2599  {m_mainPhyId});
2600  });
2601  break;
2602  }
2603 }
2604 
2605 void
2607  const WifiTxVector& txVector,
2608  uint8_t linkId)
2609 {
2610  m_countBlockAck++;
2611 
2612  auto auxPhyLinks = m_staMacs[0]->GetSetupLinkIds();
2613  auxPhyLinks.erase(m_mainPhyId);
2614  if (m_nonEmlsrLink)
2615  {
2616  auxPhyLinks.erase(*m_nonEmlsrLink);
2617  }
2618 
2619  // lambda to check that the MediumSyncDelay timer is correctly running/not running and
2620  // the CCA ED threshold is set to the correct value on all the links
2621  auto checkMediumSyncDelayTimerActive = [=, this]() {
2622  for (auto id : m_staMacs[0]->GetLinkIds())
2623  {
2624  // timer only started on EMLSR links other than the link on which TXOP was carried
2625  // out
2626  auto isTimerActive = m_staMacs[0]->IsEmlsrLink(id) && id != linkId;
2627  auto time = m_staMacs[0]->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(id);
2628  NS_TEST_EXPECT_MSG_EQ(time.has_value(),
2629  isTimerActive,
2630  Simulator::Now().As(Time::MS)
2631  << " Unexpected status for MediumSyncDelay timer on link "
2632  << +id << " after terminating a TXOP on link " << +linkId);
2633  auto currThreshold = m_staMacs[0]->GetWifiPhy(id)->GetCcaEdThreshold();
2634  NS_TEST_EXPECT_MSG_EQ((static_cast<int8_t>(currThreshold) ==
2635  m_staMacs[0]->GetEmlsrManager()->GetMediumSyncOfdmEdThreshold()),
2636  isTimerActive,
2637  Simulator::Now().As(Time::MS)
2638  << " Unexpected value (" << currThreshold
2639  << ") for CCA ED threshold on link " << +id
2640  << " when MediumSyncDelay is "
2641  << (isTimerActive ? "active" : "inactive"));
2642  }
2643  };
2644 
2645  auto txDuration =
2646  WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2647 
2648  switch (m_countBlockAck)
2649  {
2650  case 1:
2651  case 2:
2652  // do nothing, these are BlockAcks in response to the QoS data frames sent to establish
2653  // BA agreements in DL and UL direction
2654  break;
2655  case 3:
2656  if (linkId == m_nonEmlsrLink)
2657  {
2658  // this BlockAck has been sent on the non-EMLSR link, ignore it
2659  break;
2660  }
2661  m_checkBackoffStarted = true;
2662  if (!m_nonEmlsrLink)
2663  {
2664  m_countBlockAck++; // if all EMLSR links, next case is already executed now
2665  }
2666  [[fallthrough]];
2667  case 4:
2668  if (m_nonEmlsrLink && m_countBlockAck == 4)
2669  {
2670  // block transmissions on the non-EMLSR link
2671  Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
2672  m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2673  m_apMac->GetAddress(),
2674  {*m_nonEmlsrLink});
2675  });
2676  }
2677  if (linkId == m_nonEmlsrLink)
2678  {
2679  // this BlockAck has been sent on the non-EMLSR link, ignore it
2680  break;
2681  }
2682  m_checkBackoffStarted = true;
2683  // check MediumSyncDelay timer on the EMLSR client after receiving BlockAck
2684  Simulator::Schedule(txDuration + NanoSeconds(1), checkMediumSyncDelayTimerActive);
2685  break;
2686  case 5:
2687  // Block Ack in response to the second data frame sent by the EMLSR client on EMLSR links.
2688  // Check that MediumSyncDelay timer is running on the link where the main PHY is operating
2689  // and that the number of backoff slots is not changed since the beginning of the TXOP
2690  Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
2691  checkMediumSyncDelayTimerActive();
2692  auto elapsed =
2693  m_staMacs[0]->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_mainPhyId);
2695  elapsed.has_value(),
2696  true,
2697  "MediumSyncDelay timer not running on link where main PHY is operating");
2699  m_staMacs[0]->GetEmlsrManager()->GetMediumSyncDuration() -
2700  *elapsed;
2701  });
2702 
2703  Simulator::Schedule(txDuration, [=, this]() {
2704  m_checkBackoffStarted = false;
2706  true,
2707  "Backoff end time should have been calculated");
2708  // when this BlockAck is received, the TXOP ends and the main PHY link is unblocked,
2709  // which causes a new backoff timer to be generated if the backoff timer is not
2710  // already running
2712  });
2713 
2714  // make aux PHYs not capable of transmitting frames
2715  m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2716  m_apMac->GetAddress(),
2717  auxPhyLinks);
2718 
2719  // generate data packets for another UL data frame, which will be sent on the link where
2720  // the main PHY is operating
2721  NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2722  m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000));
2723  break;
2724  case 6:
2725  // block transmission on the main PHY link and on the non-EMLSR link (if any), so that
2726  // the next QoS frames are sent on a link where an aux PHY is operating
2727  std::set<uint8_t> linkIds{m_mainPhyId};
2728  if (m_nonEmlsrLink)
2729  {
2730  linkIds.insert(*m_nonEmlsrLink);
2731  }
2732  m_staMacs[0]->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
2733  AC_BE,
2735  m_apMac->GetAddress(),
2736  m_staMacs[0]->GetAddress(),
2737  {0},
2738  linkIds);
2739  // make sure aux PHYs are capable of transmitting frames
2740  m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2741  m_apMac->GetAddress(),
2742  auxPhyLinks);
2743 
2744  // generate data packets for another UL data frame
2745  NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
2746  m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000));
2747 
2748  break;
2749  }
2750 }
2751 
2752 void
2754  const WifiTxVector& txVector,
2755  uint8_t linkId)
2756 {
2758  {
2759  // this function only considers RTS frames sent after the first QoS data frame
2760  return;
2761  }
2762 
2763  if (linkId != m_mainPhyId)
2764  {
2765  if (m_countRtsframes > 0 && !m_corruptCts.has_value())
2766  {
2767  // we get here for the frame exchange in which the CTS response must be corrupted.
2768  // Install post reception error model on the STA affiliated with the EMLSR client that
2769  // is transmitting this RTS frame
2770  m_errorModel = CreateObject<ListErrorModel>();
2771  m_staMacs[0]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_errorModel);
2772  m_corruptCts = true;
2773  }
2774 
2775  return;
2776  }
2777 
2778  // we get here for RTS frames sent by the main PHY while the MediumSyncDelay timer is running
2779  m_countRtsframes++;
2780 
2782  m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId)->GetChannelWidth(),
2783  "RTS sent by main PHY on an unexpected width");
2784 
2785  // corrupt reception at AP MLD
2786  NS_LOG_INFO("CORRUPTED");
2787  m_errorModel->SetList({mpdu->GetPacket()->GetUid()});
2788 }
2789 
2790 void
2792  const WifiTxVector& txVector,
2793  uint8_t linkId)
2794 {
2795  if (m_corruptCts.has_value() && *m_corruptCts)
2796  {
2797  // corrupt reception at EMLSR client
2798  NS_LOG_INFO("CORRUPTED");
2799  m_errorModel->SetList({mpdu->GetPacket()->GetUid()});
2800  m_corruptCts = false;
2801 
2802  auto txDuration = WifiPhy::CalculateTxDuration(mpdu->GetSize(),
2803  txVector,
2804  m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2805  // main PHY is about to complete channel switch when CTS ends
2806  Simulator::Schedule(txDuration, [=, this]() {
2807  const auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId);
2808  NS_TEST_EXPECT_MSG_EQ(mainPhy->IsStateSwitching(),
2809  true,
2810  "Expecting the main PHY to be switching link");
2811  });
2812  }
2813 }
2814 
2815 void
2817 {
2818  Simulator::Stop(m_duration);
2819  Simulator::Run();
2820 
2821  CheckResults();
2822 
2823  Simulator::Destroy();
2824 }
2825 
2826 void
2828 {
2829  if (m_msdMaxNTxops > 0)
2830  {
2834  "Unexpected number of RTS frames sent while the MediumSyncDelay timer is running");
2835  }
2836 
2837  auto psduIt = m_txPsdus.cbegin();
2838 
2839  // lambda to jump to the next QoS data frame or MU-RTS Trigger Frame or RTS transmitted
2840  // to/by an EMLSR client
2841  auto jumpToQosDataOrMuRts = [&]() {
2842  while (psduIt != m_txPsdus.cend() &&
2843  !psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData() &&
2844  !psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts())
2845  {
2846  auto psdu = psduIt->psduMap.cbegin()->second;
2847  if (psdu->GetHeader(0).IsTrigger())
2848  {
2849  CtrlTriggerHeader trigger;
2850  psdu->GetPayload(0)->PeekHeader(trigger);
2851  if (trigger.IsMuRts())
2852  {
2853  break;
2854  }
2855  }
2856  psduIt++;
2857  }
2858  };
2859 
2929  // jump to the first (non-Beacon) frame transmitted after establishing BA agreements and
2930  // enabling EMLSR mode
2931  while (psduIt != m_txPsdus.cend() &&
2932  (psduIt->startTx < m_firstUlPktsGenTime ||
2933  psduIt->psduMap.cbegin()->second->GetHeader(0).IsBeacon()))
2934  {
2935  ++psduIt;
2936  }
2937 
2938  // the first QoS data frame is transmitted by the main PHY without RTS protection as soon
2939  // as transmissions on the link where the main PHY is operating are unblocked (at this
2940  // moment, aux PHYs cannot transmit)
2941  NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
2942  true,
2943  "First QoS data frame has not been transmitted");
2944  NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
2945  true,
2946  "First QoS data frame should be transmitted without protection");
2947  NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
2948  +m_mainPhyId,
2949  "First QoS data frame should be transmitted by the main PHY");
2950  NS_TEST_EXPECT_MSG_GT_OR_EQ(psduIt->startTx,
2952  "First QoS data frame sent too early");
2953 
2954  auto prevPsduIt = psduIt++;
2955  jumpToQosDataOrMuRts();
2956 
2957  if (m_nonEmlsrLink)
2958  {
2959  // an additional data frame is sent concurrently on the non-EMLSR link
2961  (psduIt != m_txPsdus.cend()),
2962  true,
2963  "Expected another QoS data frame sent concurrently with the first frame");
2965  psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
2966  true,
2967  "First data frame on non-EMLSR link should be transmitted without protection");
2968  NS_TEST_EXPECT_MSG_EQ(+psduIt->linkId,
2969  +m_nonEmlsrLink.value(),
2970  "First data frame expected to be transmitted on the non-EMLSR link");
2971  const auto txDuration =
2972  WifiPhy::CalculateTxDuration(prevPsduIt->psduMap,
2973  prevPsduIt->txVector,
2974  m_staMacs[0]->GetWifiPhy(prevPsduIt->phyId)->GetPhyBand());
2975  NS_TEST_EXPECT_MSG_LT(psduIt->startTx,
2976  prevPsduIt->startTx + txDuration,
2977  "First data frame on the non-EMLSR link not sent concurrently");
2978  psduIt++;
2979  jumpToQosDataOrMuRts();
2980  }
2981 
2982  // the second QoS data frame is transmitted by the main PHY after that the aux PHY has
2983  // obtained a TXOP and sent an RTS
2984  // RTS
2985  NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
2986  true,
2987  "RTS before second QoS data frame has not been transmitted");
2988  NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts(),
2989  true,
2990  "Second QoS data frame should be transmitted with protection");
2992  +psduIt->phyId,
2993  +m_mainPhyId,
2994  "RTS before second QoS data frame should not be transmitted by the main PHY");
2995  NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
2997  "RTS before second data frame transmitted on an unexpected width");
2998  psduIt++;
2999  // CTS
3000  NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3001  true,
3002  "CTS before second QoS data frame has not been transmitted");
3003  NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsCts(),
3004  true,
3005  "CTS before second QoS data frame has not been transmitted");
3006  psduIt++;
3007  // QoS Data
3008  NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3009  true,
3010  "Second QoS data frame has not been transmitted");
3011  NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
3012  true,
3013  "Second QoS data frame has not been transmitted");
3014  NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
3015  +m_mainPhyId,
3016  "Second QoS data frame should be transmitted by the main PHY");
3017  NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3019  "Second data frame not transmitted on the same width as RTS");
3020 
3021  bool moreQosDataFound = false;
3022 
3023  while (++psduIt != m_txPsdus.cend())
3024  {
3025  jumpToQosDataOrMuRts();
3026  if (psduIt != m_txPsdus.cend() &&
3027  psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData())
3028  {
3029  moreQosDataFound = true;
3030 
3031  NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
3032  +m_mainPhyId,
3033  "Last QoS data frame should be transmitted by the main PHY");
3034  NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3035  m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId)->GetChannelWidth(),
3036  "Expecting TX width of last data frame to equal the channel "
3037  "width used by the main PHY");
3039  psduIt->startTx,
3041  "Last QoS data frame sent before MediumSyncDelay timer expired");
3042 
3043  break;
3044  }
3045  }
3046 
3047  NS_TEST_EXPECT_MSG_EQ(moreQosDataFound,
3048  true,
3049  "Last QoS data frame transmitted by the main PHY not found");
3050 
3051  NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()), true, "Expected more frames");
3052  ++psduIt;
3053  jumpToQosDataOrMuRts();
3054 
3055  // the first attempt at transmitting the last QoS data frame fails because CTS is corrupted
3056  // RTS
3057  NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3058  true,
3059  "RTS before last QoS data frame has not been transmitted");
3060  NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts(),
3061  true,
3062  "Last QoS data frame should be transmitted with protection");
3064  +psduIt->phyId,
3065  +m_mainPhyId,
3066  "RTS before last QoS data frame should not be transmitted by the main PHY");
3067  NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3069  "RTS before last data frame transmitted on an unexpected width");
3070  psduIt++;
3071  // CTS
3072  NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3073  true,
3074  "CTS before last QoS data frame has not been transmitted");
3075  NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsCts(),
3076  true,
3077  "CTS before last QoS data frame has not been transmitted");
3078  psduIt++;
3079  jumpToQosDataOrMuRts();
3080 
3081  // the last QoS data frame is transmitted by an aux PHY after that the aux PHY has
3082  // obtained a TXOP and sent an RTS
3083  // RTS
3084  NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3085  true,
3086  "RTS before last QoS data frame has not been transmitted");
3087  NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts(),
3088  true,
3089  "Last QoS data frame should be transmitted with protection");
3091  +psduIt->phyId,
3092  +m_mainPhyId,
3093  "RTS before last QoS data frame should not be transmitted by the main PHY");
3094  NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3096  "RTS before last data frame transmitted on an unexpected width");
3097  psduIt++;
3098  // CTS
3099  NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3100  true,
3101  "CTS before last QoS data frame has not been transmitted");
3102  NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsCts(),
3103  true,
3104  "CTS before last QoS data frame has not been transmitted");
3105  psduIt++;
3106  // QoS Data
3107  NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
3108  true,
3109  "Last QoS data frame has not been transmitted");
3110  NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
3111  true,
3112  "Last QoS data frame has not been transmitted");
3113  NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
3114  +m_mainPhyId,
3115  "Last QoS data frame should be transmitted by the main PHY");
3116  NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
3118  "Last data frame not transmitted on the same width as RTS");
3119 }
3120 
3122  : EmlsrOperationsTestBase(std::string("Check EMLSR link switching (switchAuxPhy=") +
3123  std::to_string(params.switchAuxPhy) +
3124  ", resetCamState=" + std::to_string(params.resetCamState) +
3125  ", auxPhyMaxChWidth=" + std::to_string(params.auxPhyMaxChWidth) +
3126  "MHz )"),
3127  m_switchAuxPhy(params.switchAuxPhy),
3128  m_resetCamState(params.resetCamState),
3129  m_auxPhyMaxChWidth(params.auxPhyMaxChWidth),
3130  m_countQoSframes(0),
3131  m_txPsdusPos(0)
3132 {
3133  m_nEmlsrStations = 1;
3134  m_nNonEmlsrStations = 0;
3135  m_linksToEnableEmlsrOn = {0, 1, 2}; // enable EMLSR on all links right after association
3136  m_mainPhyId = 1;
3137  m_establishBaDl = true;
3138  m_duration = Seconds(1.0);
3139  // when aux PHYs do not switch link, the main PHY switches back to its previous link after
3140  // a TXOP, hence the transition delay must exceed the channel switch delay (default: 250us)
3142 }
3143 
3144 void
3146  uint8_t phyId,
3147  WifiConstPsduMap psduMap,
3148  WifiTxVector txVector,
3149  double txPowerW)
3150 {
3151  EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
3152  auto linkId = m_txPsdus.back().linkId;
3153 
3154  auto psdu = psduMap.begin()->second;
3155  auto nodeId = mac->GetDevice()->GetNode()->GetId();
3156 
3157  switch (psdu->GetHeader(0).GetType())
3158  {
3160  NS_ASSERT_MSG(nodeId > 0, "APs do not send AssocReq frames");
3161  NS_TEST_EXPECT_MSG_EQ(+linkId, +m_mainPhyId, "AssocReq not sent by the main PHY");
3162  break;
3163 
3164  case WIFI_MAC_MGT_ACTION: {
3165  auto [category, action] = WifiActionHeader::Peek(psdu->GetPayload(0));
3166 
3167  if (nodeId == 1 && category == WifiActionHeader::PROTECTED_EHT &&
3168  action.protectedEhtAction ==
3169  WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
3170  {
3171  // the EMLSR client is starting the transmission of the EML OMN frame;
3172  // temporarily block transmissions of QoS data frames from the AP MLD to the
3173  // non-AP MLD on all the links but the one used for ML setup, so that we know
3174  // that the first QoS data frame is sent on the link of the main PHY
3175  std::set<uint8_t> linksToBlock;
3176  for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
3177  {
3178  if (id != m_mainPhyId)
3179  {
3180  linksToBlock.insert(id);
3181  }
3182  }
3183  m_apMac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3184  AC_BE,
3186  m_staMacs[0]->GetAddress(),
3187  m_apMac->GetAddress(),
3188  {0},
3189  linksToBlock);
3190  }
3191  else if (category == WifiActionHeader::BLOCK_ACK &&
3192  action.blockAck == WifiActionHeader::BLOCK_ACK_ADDBA_RESPONSE)
3193  {
3194  // store the current number of transmitted frame
3195  m_txPsdusPos = m_txPsdus.size() - 1;
3196  }
3197  }
3198  break;
3199 
3200  case WIFI_MAC_CTL_TRIGGER:
3201  CheckInitialControlFrame(psduMap, txVector, linkId);
3202  break;
3203 
3204  case WIFI_MAC_QOSDATA:
3205  CheckQosFrames(psduMap, txVector, linkId);
3206  break;
3207 
3208  default:;
3209  }
3210 }
3211 
3212 void
3214 {
3215  Config::SetDefault("ns3::DefaultEmlsrManager::SwitchAuxPhy", BooleanValue(m_switchAuxPhy));
3216  Config::SetDefault("ns3::EmlsrManager::ResetCamState", BooleanValue(m_resetCamState));
3217  Config::SetDefault("ns3::EmlsrManager::AuxPhyChannelWidth", UintegerValue(m_auxPhyMaxChWidth));
3218 
3220 
3221  // use channels of different widths
3222  for (auto mac : std::initializer_list<Ptr<WifiMac>>{m_apMac, m_staMacs[0]})
3223  {
3224  mac->GetWifiPhy(0)->SetOperatingChannel(
3226  mac->GetWifiPhy(1)->SetOperatingChannel(
3228  mac->GetWifiPhy(2)->SetOperatingChannel(
3230  }
3231 }
3232 
3233 void
3235 {
3236  Simulator::Stop(m_duration);
3237  Simulator::Run();
3238 
3239  CheckResults();
3240 
3241  Simulator::Destroy();
3242 }
3243 
3244 void
3246  const WifiTxVector& txVector,
3247  uint8_t linkId)
3248 {
3249  m_countQoSframes++;
3250 
3251  switch (m_countQoSframes)
3252  {
3253  case 1:
3254  // unblock transmissions on all links
3255  m_apMac->GetMacQueueScheduler()->UnblockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3256  AC_BE,
3258  m_staMacs[0]->GetAddress(),
3259  m_apMac->GetAddress(),
3260  {0},
3261  {0, 1, 2});
3262  // block transmissions on the link used for ML setup
3263  m_apMac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3264  AC_BE,
3266  m_staMacs[0]->GetAddress(),
3267  m_apMac->GetAddress(),
3268  {0},
3269  {m_mainPhyId});
3270  // generate a new data packet, which will be sent on a link other than the one
3271  // used for ML setup, hence triggering a link switching on the EMLSR client
3273  break;
3274  case 2:
3275  // block transmission on the link used to send this QoS data frame
3276  m_apMac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3277  AC_BE,
3279  m_staMacs[0]->GetAddress(),
3280  m_apMac->GetAddress(),
3281  {0},
3282  {linkId});
3283  // generate a new data packet, which will be sent on the link that has not been used
3284  // so far, hence triggering another link switching on the EMLSR client
3286  break;
3287  case 3:
3288  // block transmission on the link used to send this QoS data frame
3289  m_apMac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3290  AC_BE,
3292  m_staMacs[0]->GetAddress(),
3293  m_apMac->GetAddress(),
3294  {0},
3295  {linkId});
3296  // unblock transmissions on the link used for ML setup
3297  m_apMac->GetMacQueueScheduler()->UnblockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3298  AC_BE,
3300  m_staMacs[0]->GetAddress(),
3301  m_apMac->GetAddress(),
3302  {0},
3303  {m_mainPhyId});
3304  // generate a new data packet, which will be sent again on the link used for ML setup,
3305  // hence triggering yet another link switching on the EMLSR client
3307  break;
3308  }
3309 }
3310 
3366 void
3368  const WifiTxVector& txVector,
3369  uint8_t linkId)
3370 {
3371  if (m_txPsdusPos == 0)
3372  {
3373  // the iterator has not been set yet, thus we are not done with the establishment of
3374  // the BA agreement
3375  return;
3376  }
3377 
3378  NS_TEST_EXPECT_MSG_LT(m_countQoSframes, 4, "Unexpected number of ICFs");
3379 
3380  auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId);
3381  auto phyRecvIcf = m_staMacs[0]->GetWifiPhy(linkId); // PHY receiving the ICF
3382 
3383  auto currMainPhyLinkId = m_staMacs[0]->GetLinkForPhy(mainPhy);
3384  NS_TEST_ASSERT_MSG_EQ(currMainPhyLinkId.has_value(),
3385  true,
3386  "Didn't find the link on which the Main PHY is operating");
3387  NS_TEST_ASSERT_MSG_NE(phyRecvIcf,
3388  nullptr,
3389  "No PHY on the link where ICF " << m_countQoSframes << " was sent");
3390 
3391  if (phyRecvIcf != mainPhy)
3392  {
3394  phyRecvIcf->GetChannelWidth(),
3396  "Aux PHY that received ICF "
3397  << m_countQoSframes << " is operating on a channel whose width exceeds the limit");
3398  }
3399 
3400  // if aux PHYs switch links, only the first ICF is received by the main PHY; otherwise,
3401  // the first ICF and the last ICF are received by the main PHY
3402  NS_TEST_EXPECT_MSG_EQ((phyRecvIcf == mainPhy),
3403  (m_countQoSframes == 0 || (!m_switchAuxPhy && m_countQoSframes == 3)),
3404  "Expecting that the ICF was received by the main PHY");
3405 
3406  // if aux PHYs do not switch links, the main PHY is operating on its original link when
3407  // the transmission of an ICF starts
3408  NS_TEST_EXPECT_MSG_EQ(m_switchAuxPhy || currMainPhyLinkId == m_mainPhyId,
3409  true,
3410  "Main PHY is operating on an unexpected link ("
3411  << +currMainPhyLinkId.value() << ", expected " << +m_mainPhyId
3412  << ")");
3413 
3414  auto txDuration =
3415  WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
3416 
3417  // check that PHYs are operating on the expected link after the reception of the ICF
3418  Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
3419  // the main PHY must be operating on the link where ICF was sent
3420  NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(linkId),
3421  mainPhy,
3422  "PHY operating on link where ICF was sent is not the main PHY");
3423 
3424  // the behavior of Aux PHYs depends on whether they switch channel or not
3425  if (m_switchAuxPhy)
3426  {
3427  if (mainPhy != phyRecvIcf)
3428  {
3429  NS_TEST_EXPECT_MSG_EQ(phyRecvIcf->IsStateSwitching(),
3430  true,
3431  "Aux PHY expected to switch channel");
3432  }
3433  Simulator::Schedule(phyRecvIcf->GetChannelSwitchDelay(), [=, this]() {
3434  NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(*currMainPhyLinkId),
3435  phyRecvIcf,
3436  "The Aux PHY that received the ICF is expected to operate "
3437  "on the link where Main PHY was before switching channel");
3438  });
3439  }
3440  else
3441  {
3442  NS_TEST_EXPECT_MSG_EQ(phyRecvIcf->IsStateSwitching(),
3443  false,
3444  "Aux PHY is not expected to switch channel");
3445  NS_TEST_EXPECT_MSG_EQ(phyRecvIcf->GetPhyBand(),
3446  mainPhy->GetPhyBand(),
3447  "The Aux PHY that received the ICF is expected to operate "
3448  "on the same band as the Main PHY");
3449  }
3450  });
3451 }
3452 
3453 void
3455 {
3456  NS_TEST_ASSERT_MSG_NE(m_txPsdusPos, 0, "BA agreement establishment not completed");
3457 
3458  const std::size_t nRxOk = 4; // successfully received ICFs
3459 
3461  m_txPsdusPos + 3 + nRxOk * 4,
3462  "Insufficient number of TX PSDUs");
3463 
3464  // m_txPsdusPos points to ADDBA_RESPONSE, then ACK and then ICF
3465  auto psduIt = std::next(m_txPsdus.cbegin(), m_txPsdusPos + 2);
3466 
3467  for (std::size_t i = 0; i < nRxOk; i++)
3468  {
3469  NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 &&
3470  psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsTrigger()),
3471  true,
3472  "Expected a Trigger Frame (ICF)");
3473  psduIt++;
3475  (psduIt->psduMap.size() == 1 && psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsCts()),
3476  true,
3477  "Expected a CTS");
3478  psduIt++;
3479  NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 &&
3480  psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsQosData()),
3481  true,
3482  "Expected a QoS Data frame");
3483  psduIt++;
3484  NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 &&
3485  psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsBlockAck()),
3486  true,
3487  "Expected a BlockAck");
3488  psduIt++;
3489  }
3490 }
3491 
3493  : TestSuite("wifi-emlsr", UNIT)
3494 {
3495  AddTestCase(new EmlOperatingModeNotificationTest(), TestCase::QUICK);
3496  AddTestCase(new EmlOmnExchangeTest({1, 2}, MicroSeconds(0)), TestCase::QUICK);
3497  AddTestCase(new EmlOmnExchangeTest({1, 2}, MicroSeconds(2048)), TestCase::QUICK);
3498  AddTestCase(new EmlOmnExchangeTest({0, 1, 2, 3}, MicroSeconds(0)), TestCase::QUICK);
3499  AddTestCase(new EmlOmnExchangeTest({0, 1, 2, 3}, MicroSeconds(2048)), TestCase::QUICK);
3500  for (const auto& emlsrLinks :
3501  {std::set<uint8_t>{0, 1, 2}, std::set<uint8_t>{1, 2}, std::set<uint8_t>{0, 1}})
3502  {
3503  AddTestCase(
3504  new EmlsrDlTxopTest(
3505  {1, 0, emlsrLinks, {MicroSeconds(32)}, {MicroSeconds(32)}, MicroSeconds(512)}),
3506  TestCase::QUICK);
3507  AddTestCase(
3508  new EmlsrDlTxopTest(
3509  {1, 1, emlsrLinks, {MicroSeconds(64)}, {MicroSeconds(64)}, MicroSeconds(512)}),
3510  TestCase::QUICK);
3512  2,
3513  emlsrLinks,
3514  {MicroSeconds(128), MicroSeconds(256)},
3515  {MicroSeconds(128), MicroSeconds(256)},
3516  MicroSeconds(512)}),
3517  TestCase::QUICK);
3518  }
3519 
3520  for (auto genBackoffIfTxopWithoutTx : {true, false})
3521  {
3523  {{0, 1, 2}, 40, 20, MicroSeconds(5504), 3, genBackoffIfTxopWithoutTx}),
3524  TestCase::QUICK);
3525  AddTestCase(
3526  new EmlsrUlTxopTest({{0, 1}, 40, 20, MicroSeconds(5504), 1, genBackoffIfTxopWithoutTx}),
3527  TestCase::QUICK);
3528  }
3529 
3530  for (bool switchAuxPhy : {true, false})
3531  {
3532  for (bool resetCamState : {true, false})
3533  {
3534  for (uint16_t auxPhyMaxChWidth : {20, 40, 80, 160})
3535  {
3536  AddTestCase(
3537  new EmlsrLinkSwitchTest({switchAuxPhy, resetCamState, auxPhyMaxChWidth}),
3538  TestCase::QUICK);
3539  }
3540  }
3541  }
3542 }
3543 
#define Max(a, b)
Test the exchange of EML Operating Mode Notification frames.
void CheckEmlCapabilitiesInAssocResp(Ptr< const WifiMpdu > mpdu, const WifiTxVector &txVector, uint8_t linkId)
Check the content of the EML Capabilities subfield of the Multi-Link Element included in the Associat...
void TxOk(Ptr< const WifiMpdu > mpdu)
Callback invoked when the non-AP MLD receives the acknowledgment for a transmitted MPDU.
std::list< uint64_t > m_uidList
list of UIDs of packets to corrupt
std::size_t m_checkEmlsrLinksCount
counter for the number of times CheckEmlsrLinks is called (should be two: when the transition timeout...
Ptr< ListErrorModel > m_errorModel
error rate model to corrupt packets at AP MLD
void DoSetup() override
Implementation to do any local setup required for this TestCase.
EmlOmnExchangeTest(const std::set< uint8_t > &linksToEnableEmlsrOn, Time transitionTimeout)
Constructor.
std::size_t m_emlNotificationDroppedCount
counter for the number of times the EML Notification frame sent by the non-AP MLD has been dropped du...
void CheckEmlsrLinks()
Check that the EMLSR mode has been enabled on the expected EMLSR links.
void CheckEmlCapabilitiesInAssocReq(Ptr< const WifiMpdu > mpdu, const WifiTxVector &txVector, uint8_t linkId)
Check the content of the EML Capabilities subfield of the Multi-Link Element included in the Associat...
void TxDropped(WifiMacDropReason reason, Ptr< const WifiMpdu > mpdu)
Callback invoked when the non-AP MLD drops the given MPDU for the given reason.
void CheckEmlNotification(Ptr< const WifiPsdu > psdu, const WifiTxVector &txVector, uint8_t linkId)
Check the content of a received EML Operating Mode Notification frame.
void Transmit(Ptr< WifiMac > mac, uint8_t phyId, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW) override
Callback invoked when a FEM passes PSDUs to the PHY.
void DoRun() override
Implementation to actually run this TestCase.
Test EML Operating Mode Notification frame serialization and deserialization.
EmlOperatingModeNotificationTest()
Constructor.
void DoRun() override
Implementation to actually run this TestCase.
Test the transmission of DL frames to EMLSR clients.
void CheckInitialControlFrame(Ptr< const WifiMpdu > mpdu, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken by the AP MLD transmitting an initial Control frame to an EM...
const Time m_fe2to3delay
time interval between 2nd and 3rd frame exchange sequences after the enablement of EMLSR mode
void CheckResults()
Check that the simulation produced the expected results.
void CheckPmModeAfterAssociation(const Mac48Address &address)
Check that the AP MLD considers the correct Power Management mode for the links setup with the given ...
EmlsrDlTxopTest(const Params &params)
Constructor.
void StartTraffic() override
Start the generation of traffic (needs to be overridden)
Ptr< ListErrorModel > m_errorModel
error rate model to corrupt BlockAck at AP MLD
std::size_t m_countQoSframes
counter for QoS frames (transition delay test)
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.
TrafficDirection
Enumeration for traffic directions.
void DoSetup() override
Implementation to do any local setup required for this TestCase.
uint8_t m_mainPhyId
ID of the main PHY.
Time m_duration
simulation duration
std::vector< FrameInfo > m_txPsdus
transmitted PSDUs
Ptr< PacketSocketClient > GetApplication(TrafficDirection dir, std::size_t staId, std::size_t count, std::size_t pktSize) const
virtual void Transmit(Ptr< WifiMac > mac, uint8_t phyId, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW)
Callback invoked when a FEM passes PSDUs to the PHY.
bool m_establishBaUl
whether BA needs to be established (for TID 0) with the AP as recipient
uint16_t m_lastAid
AID of last associated station.
std::vector< Time > m_transitionDelay
Transition Delay advertised by the non-AP MLD.
Time m_transitionTimeout
Transition Timeout advertised by the AP MLD.
std::vector< PacketSocketAddress > m_ulSockets
packet socket address for UL traffic
std::vector< Ptr< StaWifiMac > > m_staMacs
MACs of the non-AP MLDs.
virtual void StartTraffic()
Start the generation of traffic (needs to be overridden)
EmlsrOperationsTestBase(const std::string &name)
Constructor.
Test the transmission of UL frames from EMLSR clients.
std::size_t m_countQoSframes
counter for QoS frames
const Time m_unblockMainPhyLinkDelay
delay between the time the first two UL packets are generated and the time transmissions are unblocke...
Ptr< ListErrorModel > m_errorModel
error rate model to corrupt packets
uint16_t m_channelWidth
width (MHz) of the channels used by MLDs
void BackoffGenerated(uint32_t backoff, uint8_t linkId)
Callback invoked when a new backoff value is generated by the EMLSR client.
std::optional< uint8_t > m_nonEmlsrLink
ID of the non-EMLSR link (if any)
Time m_lastMsdExpiryTime
expiry time of the last MediumSyncDelay timer
void DoSetup() override
Implementation to do any local setup required for this TestCase.
std::size_t m_countRtsframes
counter for RTS frames
void CheckCtsFrames(Ptr< const WifiMpdu > mpdu, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken by the EMLSR client when receiving a CTS frame on the given ...
void DoRun() override
Implementation to actually run this TestCase.
Time m_firstUlPktsGenTime
generation time of the first two UL packets
std::optional< bool > m_corruptCts
whether the transmitted CTS must be corrupted
void CheckBlockAck(const WifiConstPsduMap &psduMap, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken when an MLD transmits a PPDU containing BlockAck frames on t...
void StartTraffic() override
Start the generation of traffic (needs to be overridden)
std::optional< Time > m_backoffEndTime
expected backoff end time on main PHY link
std::set< uint8_t > m_emlsrLinks
IDs of the links on which EMLSR mode has to be enabled.
Time m_mediumSyncDuration
duration of the MediumSyncDelay timer
void CheckRtsFrames(Ptr< const WifiMpdu > mpdu, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken by the EMLSR client when transmitting an RTS frame on the gi...
void CheckQosFrames(const WifiConstPsduMap &psduMap, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken when an MLD transmits a PPDU containing QoS data frames on t...
bool m_genBackoffIfTxopWithoutTx
whether the backoff should be invoked when the AC gains the right to start a TXOP but it does not tra...
void Transmit(Ptr< WifiMac > mac, uint8_t phyId, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW) override
Callback invoked when a FEM passes PSDUs to the PHY.
bool m_checkBackoffStarted
whether we are checking the generated backoff values
std::size_t m_countBlockAck
counter for BlockAck frames
void CheckResults()
Check that the simulation produced the expected results.
uint8_t m_msdMaxNTxops
Max number of TXOPs that an EMLSR client is allowed to attempt to initiate while the MediumSyncDelay ...
EmlsrUlTxopTest(const Params &params)
Constructor.
uint16_t m_auxPhyChannelWidth
max width (MHz) supported by aux PHYs
wifi EMLSR Test Suite
std::optional< Mac48Address > GetMldOrLinkAddressByAid(uint16_t aid) const
A container for one type of attribute.
Headers for Trigger frames.
Definition: ctrl-headers.h:942
bool IsMuRts() const
Check if this is a MU-RTS Trigger frame.
void SetPaddingSize(std::size_t size)
Set the size in bytes of the Padding field.
Hold variables of type enum.
Definition: enum.h:62
Subclass of TestCase class adding the ability to test the serialization and deserialization of a Head...
void TestHeaderSerialization(const T &hdr, Args &&... args)
Serialize the given header in a buffer, then create a new header by deserializing from the buffer and...
void SetList(const std::list< uint64_t > &packetlist)
Definition: error-model.cc:456
an EUI-48 address
Definition: mac48-address.h:46
Implement the header for management frames of type association request.
Definition: mgt-headers.h:157
Implement the header for management frames of type association and reassociation response.
Definition: mgt-headers.h:334
Implement the header for Action frames of type EML Operating Mode Notification.
void SetLinkIdInBitmap(uint8_t linkId)
Set the bit position in the link bitmap corresponding to the given link.
EmlControl m_emlControl
EML Control field.
std::optional< EmlsrParamUpdate > m_emlsrParamUpdate
EMLSR Parameter Update field.
std::list< uint8_t > GetLinkBitmap() const
Helper class used to assign positions and mobility models to nodes.
holds a vector of ns3::NetDevice pointers
Ptr< NetDevice > Get(uint32_t i) const
Get the Ptr<NetDevice> stored in this container at a given index.
keep track of a set of node pointers.
uint32_t AddApplication(Ptr< Application > application)
Associate an Application to this Node.
Definition: node.cc:169
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.
Make it easy to create and manage PHY objects for the spectrum model.
void AddChannel(const Ptr< SpectrumChannel > channel, const FrequencyRange &freqRange=WHOLE_WIFI_SPECTRUM)
The IEEE 802.11 SSID Information Element.
Definition: ssid.h:36
Hold variables of type string.
Definition: string.h:56
encapsulates test code
Definition: test.h:1060
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
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition: time.cc:415
bool IsStrictlyPositive() const
Exactly equivalent to t > 0.
Definition: nstime.h:351
bool IsZero() const
Exactly equivalent to t == 0.
Definition: nstime.h:315
void SetTxopLimits(const std::vector< Time > &txopLimits)
Set the TXOP limit for each link.
Definition: txop.cc:395
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 |.
void Print(std::ostream &os) const override
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:864
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:933
void UnblockUnicastTxOnLinks(WifiQueueBlockedReason reason, const Mac48Address &address, const std::set< uint8_t > &linkIds)
Unblock the transmission on the given links of all unicast frames addressed to the station with the g...
Definition: wifi-mac.cc:1428
Ptr< WifiPhy > GetWifiPhy(uint8_t linkId=SINGLE_LINK_OP_ID) const
Definition: wifi-mac.cc:1171
void BlockUnicastTxOnLinks(WifiQueueBlockedReason reason, const Mac48Address &address, const std::set< uint8_t > &linkIds)
Block the transmission on the given links of all unicast frames addressed to the station with the giv...
Definition: wifi-mac.cc:1387
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:906
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
void SetPostReceptionErrorModel(const Ptr< ErrorModel > em)
Attach a receive ErrorModel to the WifiPhy.
Definition: wifi-phy.cc:646
WifiPhyBand GetPhyBand() const
Get the configured Wi-Fi band.
Definition: wifi-phy.cc:1021
std::tuple< uint8_t, uint16_t, WifiPhyBand, uint8_t > ChannelTuple
Tuple identifying an operating channel.
Definition: wifi-phy.h:891
std::vector< Ptr< WifiMpdu > >::const_iterator begin() const
Return a const iterator to the first MPDU.
Definition: wifi-psdu.cc:333
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
WifiMode GetMode(uint16_t staId=SU_STA_ID) const
If this TX vector is associated with an SU PPDU, return the selected payload transmission mode.
WifiPreamble GetPreambleType() const
uint16_t GetChannelWidth() const
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition: assert.h:86
void SetDefault(std::string name, const AttributeValue &value)
Definition: config.cc: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
Time Now()
create an ns3::Time instance which contains the current simulation time.
Definition: simulator.cc:305
#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:1350
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1362
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1326
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1338
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:75
address
Definition: first.py:47
NLOHMANN_BASIC_JSON_TPL_DECLARATION std::string to_string(const NLOHMANN_BASIC_JSON_TPL &j)
user-defined to_string function for JSON values
Definition: json.h:25255
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.
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_CTL_RTS
@ WIFI_MAC_CTL_CTS
@ WIFI_MAC_MGT_ACTION
@ WIFI_MAC_MGT_ASSOCIATION_RESPONSE
@ WIFI_MAC_MGT_ASSOCIATION_REQUEST
@ WIFI_MAC_CTL_BACKRESP
@ WIFI_MAC_CTL_END
@ WIFI_MAC_QOSDATA
uint32_t GetAckSize()
Return the total Ack size (including FCS trailer).
Definition: wifi-utils.cc: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.
U * PeekPointer(const Ptr< U > &p)
Definition: ptr.h:449
staDevices
Definition: third.py:100
mac
Definition: third.py:92
wifi
Definition: third.py:95
wifiApNode
Definition: third.py:86
mobility
Definition: third.py:105
wifiStaNodes
Definition: third.py:84
phy
Definition: third.py:89
params
Fit Fluctuating Two Ray model to the 3GPP TR 38.901 using the Anderson-Darling goodness-of-fit ##.
#define list
ns3::Time timeout
Parameters for the EMLSR DL TXOP test.
Parameters for the EMLSR UL TXOP test.
uint8_t emlsrParamUpdateCtrl
EMLSR Parameter Update Control.
std::optional< uint16_t > linkBitmap
EMLSR/EMLMR Link Bitmap.
EMLSR Parameter Update field.
uint32_t prev
std::string dir
uint32_t pktSize
packet size used for the simulation (in bytes)
static WifiEmlsrTestSuite g_wifiEmlsrTestSuite
the test suite