A Discrete-Event Network Simulator
API
emlsr-manager.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 "emlsr-manager.h"
21 
22 #include "eht-configuration.h"
24 
25 #include "ns3/abort.h"
26 #include "ns3/assert.h"
27 #include "ns3/attribute-container.h"
28 #include "ns3/log.h"
29 #include "ns3/mgt-action-headers.h"
30 #include "ns3/wifi-mpdu.h"
31 #include "ns3/wifi-net-device.h"
32 #include "ns3/wifi-phy-state-helper.h"
33 
34 namespace ns3
35 {
36 
37 NS_LOG_COMPONENT_DEFINE("EmlsrManager");
38 
39 NS_OBJECT_ENSURE_REGISTERED(EmlsrManager);
40 
41 TypeId
43 {
44  static TypeId tid =
45  TypeId("ns3::EmlsrManager")
46  .SetParent<Object>()
47  .SetGroupName("Wifi")
48  .AddAttribute("EmlsrPaddingDelay",
49  "The EMLSR Paddind Delay (not used by AP MLDs). "
50  "Possible values are 0 us, 32 us, 64 us, 128 us or 256 us.",
54  .AddAttribute("EmlsrTransitionDelay",
55  "The EMLSR Transition Delay (not used by AP MLDs). "
56  "Possible values are 0 us, 16 us, 32 us, 64 us, 128 us or 256 us.",
60  .AddAttribute(
61  "MainPhyId",
62  "The ID of the main PHY (position in the vector of PHYs held by "
63  "WifiNetDevice). This attribute cannot be set after construction.",
64  TypeId::ATTR_GET | TypeId::ATTR_CONSTRUCT, // prevent setting after construction
65  UintegerValue(0),
67  MakeUintegerChecker<uint8_t>())
68  .AddAttribute("AuxPhyChannelWidth",
69  "The maximum channel width (MHz) supported by Aux PHYs. Note that the "
70  "maximum channel width is capped to the maximum channel width supported "
71  "by the configured maximum modulation class supported.",
73  TypeId::ATTR_CONSTRUCT, // prevent setting after construction
74  UintegerValue(20),
76  MakeUintegerChecker<uint16_t>(20, 160))
77  .AddAttribute("AuxPhyMaxModClass",
78  "The maximum modulation class supported by Aux PHYs. Use "
79  "WIFI_MOD_CLASS_OFDM for non-HT.",
81  TypeId::ATTR_CONSTRUCT, // prevent setting after construction
83  MakeEnumAccessor<WifiModulationClass>(&EmlsrManager::m_auxPhyMaxModClass),
85  "HR-DSSS",
87  "ERP-OFDM",
89  "OFDM",
91  "HT",
93  "VHT",
95  "HE",
97  "EHT"))
98  .AddAttribute("AuxPhyTxCapable",
99  "Whether Aux PHYs are capable of transmitting PPDUs.",
100  BooleanValue(true),
104  .AddAttribute(
105  "EmlsrLinkSet",
106  "IDs of the links on which EMLSR mode will be enabled. An empty set "
107  "indicates to disable EMLSR.",
109  MakeAttributeContainerAccessor<UintegerValue>(&EmlsrManager::SetEmlsrLinks),
110  MakeAttributeContainerChecker<UintegerValue>(MakeUintegerChecker<uint8_t>()))
111  .AddAttribute("ResetCamState",
112  "Whether to reset the state of the ChannelAccessManager associated with "
113  "the link on which the main PHY has just switched to.",
114  BooleanValue(false),
118  return tid;
119 }
120 
122  // The STA initializes dot11MSDTimerDuration to aPPDUMaxTime defined in Table 36-70
123  // (Sec. 35.3.16.8.1 of 802.11be D3.1)
124  : m_mediumSyncDuration(MicroSeconds(DEFAULT_MSD_DURATION_USEC)),
125  // The default value of dot11MSDOFDMEDthreshold is –72 dBm and the default value of
126  // dot11MSDTXOPMax is 1, respectively (Sec. 35.3.16.8.1 of 802.11be D3.1)
127  m_msdOfdmEdThreshold(DEFAULT_MSD_OFDM_ED_THRESH),
128  m_msdMaxNTxops(DEFAULT_MSD_MAX_N_TXOPS)
129 {
130  NS_LOG_FUNCTION(this);
131 }
132 
134 {
136 }
137 
138 void
140 {
141  NS_LOG_FUNCTION(this);
145  m_staMac = nullptr;
147  for (auto& [id, status] : m_mediumSyncDelayStatus)
148  {
149  status.timer.Cancel();
150  }
152 }
153 
154 void
156 {
157  NS_LOG_FUNCTION(this << mac);
158  NS_ASSERT(mac);
159  m_staMac = mac;
160 
161  NS_ABORT_MSG_IF(!m_staMac->GetEhtConfiguration(), "EmlsrManager requires EHT support");
162  NS_ABORT_MSG_IF(m_staMac->GetNLinks() <= 1, "EmlsrManager can only be installed on MLDs");
164  "EmlsrManager can only be installed on non-AP MLDs");
165 
167  m_staMac->TraceConnectWithoutContext("DroppedMpdu",
169 }
170 
171 void
172 EmlsrManager::SetMainPhyId(uint8_t mainPhyId)
173 {
174  NS_LOG_FUNCTION(this << mainPhyId);
175  NS_ABORT_MSG_IF(IsInitialized(), "Cannot be called once this object has been initialized");
176  m_mainPhyId = mainPhyId;
177 }
178 
179 uint8_t
181 {
182  return m_mainPhyId;
183 }
184 
185 void
187 {
188  m_resetCamState = enable;
189 }
190 
191 bool
193 {
194  return m_resetCamState;
195 }
196 
197 void
199 {
200  m_auxPhyTxCapable = capable;
201 }
202 
203 bool
205 {
206  return m_auxPhyTxCapable;
207 }
208 
209 const std::set<uint8_t>&
211 {
212  return m_emlsrLinks;
213 }
214 
217 {
218  return m_staMac;
219 }
220 
222 EmlsrManager::GetEhtFem(uint8_t linkId) const
223 {
224  return StaticCast<EhtFrameExchangeManager>(m_staMac->GetFrameExchangeManager(linkId));
225 }
226 
227 std::optional<Time>
229 {
230  if (const auto statusIt = m_mediumSyncDelayStatus.find(linkId);
231  statusIt != m_mediumSyncDelayStatus.cend() && statusIt->second.timer.IsRunning())
232  {
233  return m_mediumSyncDuration - Simulator::GetDelayLeft(statusIt->second.timer);
234  }
235  return std::nullopt;
236 }
237 
238 void
240 {
243 }
244 
245 std::optional<Time>
247 {
249 }
250 
251 void
253 {
254  NS_LOG_FUNCTION(this << duration.As(Time::US));
255  m_mediumSyncDuration = duration;
256 }
257 
258 Time
260 {
261  return m_mediumSyncDuration;
262 }
263 
264 void
266 {
267  NS_LOG_FUNCTION(this << threshold);
268  m_msdOfdmEdThreshold = threshold;
269 }
270 
271 int8_t
273 {
274  return m_msdOfdmEdThreshold;
275 }
276 
277 void
278 EmlsrManager::SetMediumSyncMaxNTxops(std::optional<uint8_t> nTxops)
279 {
280  NS_LOG_FUNCTION(this << nTxops.has_value());
281  m_msdMaxNTxops = nTxops;
282 }
283 
284 std::optional<uint8_t>
286 {
287  return m_msdMaxNTxops;
288 }
289 
290 void
291 EmlsrManager::SetEmlsrLinks(const std::set<uint8_t>& linkIds)
292 {
293  NS_LOG_FUNCTION(this);
294  NS_ABORT_MSG_IF(linkIds.size() == 1, "Cannot enable EMLSR mode on a single link");
295 
296  if (linkIds != m_emlsrLinks)
297  {
298  m_nextEmlsrLinks = linkIds;
299  }
300 
301  if (GetStaMac() && GetStaMac()->IsAssociated() && GetTransitionTimeout() && m_nextEmlsrLinks)
302  {
303  // Request to enable EMLSR mode on the given links, provided that they have been setup
304  SendEmlOmn();
305  }
306 }
307 
308 void
310 {
311  NS_LOG_FUNCTION(this << *mpdu << linkId);
312 
313  const auto& hdr = mpdu->GetHeader();
314 
315  DoNotifyMgtFrameReceived(mpdu, linkId);
316 
317  if (hdr.IsAssocResp() && GetStaMac()->IsAssociated() && GetTransitionTimeout())
318  {
319  // we just completed ML setup with an AP MLD that supports EMLSR
321 
322  if (m_nextEmlsrLinks && !m_nextEmlsrLinks->empty())
323  {
324  // a non-empty set of EMLSR links have been configured, hence enable EMLSR mode
325  // on those links
326  SendEmlOmn();
327  }
328  }
329 
330  if (hdr.IsAction() && hdr.GetAddr2() == m_staMac->GetBssid(linkId))
331  {
332  // this is an action frame sent by an AP of the AP MLD we are associated with
333  auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
334  if (category == WifiActionHeader::PROTECTED_EHT &&
335  action.protectedEhtAction ==
337  {
339  {
340  // no need to wait until the expiration of the transition timeout
343  }
344  }
345  }
346 }
347 
348 void
350 {
351  NS_LOG_FUNCTION(this << linkId);
352 
353  NS_ASSERT(m_staMac->IsEmlsrLink(linkId));
354 
355  // block transmissions and suspend medium access on all other EMLSR links
356  for (auto id : m_staMac->GetLinkIds())
357  {
358  if (id != linkId && m_staMac->IsEmlsrLink(id))
359  {
362  }
363  }
364 
365  auto mainPhy = m_staMac->GetDevice()->GetPhy(m_mainPhyId);
366  auto auxPhy = m_staMac->GetWifiPhy(linkId);
367 
368  if (m_staMac->GetWifiPhy(linkId) == mainPhy)
369  {
370  // nothing to do, we received an ICF from the main PHY
371  return;
372  }
373 
374  Simulator::ScheduleNow([=, this]() {
375  SwitchMainPhy(linkId,
376  true, // channel switch should occur instantaneously
379 
380  // aux PHY received the ICF but main PHY will send the response
381  auto uid = auxPhy->GetPreviouslyRxPpduUid();
382  mainPhy->SetPreviouslyRxPpduUid(uid);
383 
384  DoNotifyIcfReceived(linkId);
385  });
386 }
387 
388 void
389 EmlsrManager::NotifyUlTxopStart(uint8_t linkId, std::optional<Time> timeToCtsEnd)
390 {
391  NS_LOG_FUNCTION(this << linkId);
392 
393  if (!m_staMac->IsEmlsrLink(linkId))
394  {
395  NS_LOG_DEBUG("EMLSR is not enabled on link " << +linkId);
396  return;
397  }
398 
399  // block transmissions and suspend medium access on all other EMLSR links
400  for (auto id : m_staMac->GetLinkIds())
401  {
402  if (id != linkId && m_staMac->IsEmlsrLink(id))
403  {
406  }
407  }
408 
409  // if this TXOP is being started by an aux PHY, schedule a channel switch for the main PHY
410  // such that the channel switch is completed by the time the CTS response is received. The
411  // delay has been passed by the FEM.
412  if (m_staMac->GetLinkForPhy(m_mainPhyId) != linkId)
413  {
414  auto stateHelper = m_staMac->GetWifiPhy(linkId)->GetState();
415  NS_ASSERT(stateHelper);
416  NS_ASSERT_MSG(stateHelper->GetState() == TX,
417  "Expecting the aux PHY to be transmitting (an RTS frame)");
418  NS_ASSERT_MSG(timeToCtsEnd.has_value(),
419  "Aux PHY is sending RTS, expected to get the time to CTS end");
420 
421  auto mainPhy = m_staMac->GetDevice()->GetPhy(m_mainPhyId);
422 
423  // the main PHY shall terminate the channel switch at the end of CTS reception;
424  // the time remaining to the end of CTS reception includes two propagation delays
425  const auto delay = *timeToCtsEnd - mainPhy->GetChannelSwitchDelay();
426 
427  NS_ASSERT(delay.IsPositive());
428  NS_LOG_DEBUG("Schedule main Phy switch in " << delay.As(Time::US));
429  Simulator::Schedule(delay,
431  this,
432  linkId,
433  false,
436  }
437 
438  DoNotifyUlTxopStart(linkId);
439 }
440 
441 void
443 {
444  NS_LOG_FUNCTION(this << linkId);
445 
446  if (!m_staMac->IsEmlsrLink(linkId))
447  {
448  NS_LOG_DEBUG("EMLSR is not enabled on link " << +linkId);
449  return;
450  }
451 
452  DoNotifyTxopEnd(linkId);
453 
454  Simulator::ScheduleNow([=, this]() {
455  // unblock transmissions and resume medium access on other EMLSR links
456  for (auto id : m_staMac->GetLinkIds())
457  {
458  if (m_staMac->IsEmlsrLink(id))
459  {
460  m_staMac->UnblockTxOnLink(id, WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK);
461  m_staMac->GetChannelAccessManager(id)->NotifyStopUsingOtherEmlsrLink();
462  }
463  }
464 
466  });
467 }
468 
469 void
470 EmlsrManager::SetCcaEdThresholdOnLinkSwitch(Ptr<WifiPhy> phy, uint8_t linkId)
471 {
472  NS_LOG_FUNCTION(this << phy << linkId);
473 
474  // if a MediumSyncDelay timer is running for the link on which the main PHY is going to
475  // operate, set the CCA ED threshold to the MediumSyncDelay OFDM ED threshold
476  if (auto statusIt = m_mediumSyncDelayStatus.find(linkId);
477  statusIt != m_mediumSyncDelayStatus.cend() && statusIt->second.timer.IsRunning())
478  {
479  NS_LOG_DEBUG("Setting CCA ED threshold of PHY " << phy << " to " << +m_msdOfdmEdThreshold
480  << " on link " << +linkId);
481 
482  // store the current CCA ED threshold in the m_prevCcaEdThreshold map, if not present
483  m_prevCcaEdThreshold.try_emplace(phy, phy->GetCcaEdThreshold());
484 
485  phy->SetCcaEdThreshold(m_msdOfdmEdThreshold);
486  }
487  // otherwise, restore the previous value for the CCA ED threshold (if any)
488  else if (auto threshIt = m_prevCcaEdThreshold.find(phy);
489  threshIt != m_prevCcaEdThreshold.cend())
490  {
491  NS_LOG_DEBUG("Resetting CCA ED threshold of PHY " << phy << " to " << threshIt->second
492  << " on link " << +linkId);
493  phy->SetCcaEdThreshold(threshIt->second);
494  m_prevCcaEdThreshold.erase(threshIt);
495  }
496 }
497 
498 void
499 EmlsrManager::SwitchMainPhy(uint8_t linkId,
500  bool noSwitchDelay,
501  bool resetBackoff,
502  bool requestAccess)
503 {
504  NS_LOG_FUNCTION(this << linkId << noSwitchDelay << resetBackoff << requestAccess);
505 
506  auto mainPhy = m_staMac->GetDevice()->GetPhy(m_mainPhyId);
507 
508  NS_ASSERT_MSG(mainPhy != m_staMac->GetWifiPhy(linkId),
509  "Main PHY is already operating on link " << +linkId);
510 
511  // find the link on which the main PHY is operating
512  auto currMainPhyLinkId = m_staMac->GetLinkForPhy(mainPhy);
513  NS_ASSERT_MSG(currMainPhyLinkId, "Current link ID for main PHY not found");
514 
515  auto newMainPhyChannel = GetChannelForMainPhy(linkId);
516 
517  NS_LOG_DEBUG("Main PHY (" << mainPhy << ") is about to switch to " << newMainPhyChannel
518  << " to operate on link " << +linkId);
519 
520  // notify the channel access manager of the upcoming channel switch(es)
521  m_staMac->GetChannelAccessManager(*currMainPhyLinkId)
522  ->NotifySwitchingEmlsrLink(mainPhy, newMainPhyChannel, linkId);
523 
524  // this assert also ensures that the actual channel switch is not delayed
525  NS_ASSERT_MSG(!mainPhy->GetState()->IsStateTx(),
526  "We should not ask the main PHY to switch channel while transmitting");
527 
528  // request the main PHY to switch channel
529  auto delay = mainPhy->GetChannelSwitchDelay();
530  NS_ASSERT_MSG(noSwitchDelay || delay <= m_lastAdvTransitionDelay,
531  "Transition delay (" << m_lastAdvTransitionDelay.As(Time::US)
532  << ") should exceed the channel switch delay ("
533  << delay.As(Time::US) << ")");
534  if (noSwitchDelay)
535  {
536  mainPhy->SetAttribute("ChannelSwitchDelay", TimeValue(Seconds(0)));
537  }
538  mainPhy->SetOperatingChannel(newMainPhyChannel);
539  // restore previous channel switch delay
540  if (noSwitchDelay)
541  {
542  mainPhy->SetAttribute("ChannelSwitchDelay", TimeValue(delay));
543  }
544  // re-enable short time slot, if needed
545  if (m_staMac->GetWifiRemoteStationManager(linkId)->GetShortSlotTimeEnabled())
546  {
547  mainPhy->SetSlot(MicroSeconds(9));
548  }
549 
550  if (resetBackoff)
551  {
552  // reset the backoffs on the link left by the main PHY
553  m_staMac->GetChannelAccessManager(*currMainPhyLinkId)->ResetAllBackoffs();
554  }
555 
556  const auto timeToSwitchEnd = noSwitchDelay ? Seconds(0) : mainPhy->GetChannelSwitchDelay();
557 
558  if (requestAccess)
559  {
560  // schedule channel access request on the new link when switch is completed
561  Simulator::Schedule(timeToSwitchEnd, [=, this]() {
562  for (const auto& [acIndex, ac] : wifiAcList)
563  {
564  m_staMac->GetQosTxop(acIndex)->StartAccessAfterEvent(
565  linkId,
566  Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT,
567  Txop::CHECK_MEDIUM_BUSY);
568  }
569  });
570  }
571 
572  SetCcaEdThresholdOnLinkSwitch(mainPhy, linkId);
573  NotifyMainPhySwitch(*currMainPhyLinkId, linkId);
574 }
575 
576 void
577 EmlsrManager::SwitchAuxPhy(uint8_t currLinkId, uint8_t nextLinkId)
578 {
579  NS_LOG_FUNCTION(this << currLinkId << nextLinkId);
580 
581  auto auxPhy = GetStaMac()->GetWifiPhy(currLinkId);
582 
583  auto newAuxPhyChannel = GetChannelForAuxPhy(nextLinkId);
584 
585  NS_LOG_DEBUG("Aux PHY (" << auxPhy << ") is about to switch to " << newAuxPhyChannel
586  << " to operate on link " << +nextLinkId);
587 
588  GetStaMac()
589  ->GetChannelAccessManager(currLinkId)
590  ->NotifySwitchingEmlsrLink(auxPhy, newAuxPhyChannel, nextLinkId);
591 
592  auxPhy->SetOperatingChannel(newAuxPhyChannel);
593  // re-enable short time slot, if needed
594  if (m_staMac->GetWifiRemoteStationManager(nextLinkId)->GetShortSlotTimeEnabled())
595  {
596  auxPhy->SetSlot(MicroSeconds(9));
597  }
598 
599  // schedule channel access request on the new link when switch is completed
600  Simulator::Schedule(auxPhy->GetChannelSwitchDelay(), [=, this]() {
601  for (const auto& [acIndex, ac] : wifiAcList)
602  {
603  m_staMac->GetQosTxop(acIndex)->StartAccessAfterEvent(
604  nextLinkId,
605  Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT,
606  Txop::CHECK_MEDIUM_BUSY);
607  }
608  });
609 
610  SetCcaEdThresholdOnLinkSwitch(auxPhy, nextLinkId);
611 }
612 
613 void
614 EmlsrManager::StartMediumSyncDelayTimer(uint8_t linkId)
615 {
616  NS_LOG_FUNCTION(this << linkId);
617 
618  // iterate over all the other EMLSR links
619  for (auto id : m_staMac->GetLinkIds())
620  {
621  if (id != linkId && m_staMac->IsEmlsrLink(id))
622  {
623  const auto [it, inserted] = m_mediumSyncDelayStatus.try_emplace(id);
624 
625  // reset the max number of TXOP attempts
626  it->second.msdNTxopsLeft = m_msdMaxNTxops;
627 
628  // there are cases in which no PHY is operating on a link; e.g., the main PHY starts
629  // switching to a link on which an aux PHY gained a TXOP and sent an RTS, but the CTS
630  // is not received and the UL TXOP ends before the main PHY channel switch is
631  // completed. The MSD timer is started on the link left "uncovered" by the main PHY
632  if (auto phy = m_staMac->GetWifiPhy(id); phy && !it->second.timer.IsRunning())
633  {
634  NS_LOG_DEBUG("Setting CCA ED threshold on link "
635  << +id << " to " << +m_msdOfdmEdThreshold << " PHY " << phy);
636  m_prevCcaEdThreshold[phy] = phy->GetCcaEdThreshold();
637  phy->SetCcaEdThreshold(m_msdOfdmEdThreshold);
638  }
639 
640  // (re)start the timer
641  it->second.timer.Cancel();
642  it->second.timer = Simulator::Schedule(m_mediumSyncDuration,
643  &EmlsrManager::MediumSyncDelayTimerExpired,
644  this,
645  id);
646  }
647  }
648 }
649 
650 void
651 EmlsrManager::CancelMediumSyncDelayTimer(uint8_t linkId)
652 {
653  NS_LOG_FUNCTION(this << linkId);
654 
655  auto timerIt = m_mediumSyncDelayStatus.find(linkId);
656 
657  NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && timerIt->second.timer.IsRunning());
658 
659  timerIt->second.timer.Cancel();
660  MediumSyncDelayTimerExpired(linkId);
661 }
662 
663 void
664 EmlsrManager::MediumSyncDelayTimerExpired(uint8_t linkId)
665 {
666  NS_LOG_FUNCTION(this << linkId);
667 
668  auto timerIt = m_mediumSyncDelayStatus.find(linkId);
669 
670  NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && !timerIt->second.timer.IsRunning());
671 
672  // reset the MSD OFDM ED threshold
673  auto phy = m_staMac->GetWifiPhy(linkId);
674 
675  if (!phy)
676  {
677  // no PHY is operating on this link. This may happen when a MediumSyncDelay timer expires
678  // on the link left "uncovered" by the main PHY that is operating on another link (and the
679  // aux PHY of that link did not switch). In this case, do nothing, since the CCA ED
680  // threshold on the main PHY will be restored once the main PHY switches back to its link
681  return;
682  }
683 
684  auto threshIt = m_prevCcaEdThreshold.find(phy);
685  NS_ASSERT_MSG(threshIt != m_prevCcaEdThreshold.cend(),
686  "No value to restore for CCA ED threshold on PHY " << phy);
687  NS_LOG_DEBUG("Resetting CCA ED threshold of PHY " << phy << " to " << threshIt->second
688  << " on link " << +linkId);
689  phy->SetCcaEdThreshold(threshIt->second);
690  m_prevCcaEdThreshold.erase(threshIt);
691 }
692 
693 void
694 EmlsrManager::DecrementMediumSyncDelayNTxops(uint8_t linkId)
695 {
696  NS_LOG_FUNCTION(this << linkId);
697 
698  const auto timerIt = m_mediumSyncDelayStatus.find(linkId);
699 
700  NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && timerIt->second.timer.IsRunning());
701  NS_ASSERT(timerIt->second.msdNTxopsLeft != 0);
702 
703  if (timerIt->second.msdNTxopsLeft)
704  {
705  --timerIt->second.msdNTxopsLeft.value();
706  }
707 }
708 
709 void
710 EmlsrManager::ResetMediumSyncDelayNTxops(uint8_t linkId)
711 {
712  NS_LOG_FUNCTION(this << linkId);
713 
714  auto timerIt = m_mediumSyncDelayStatus.find(linkId);
715 
716  NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && timerIt->second.timer.IsRunning());
717  timerIt->second.msdNTxopsLeft.reset();
718 }
719 
720 bool
721 EmlsrManager::MediumSyncDelayNTxopsExceeded(uint8_t linkId)
722 {
723  NS_LOG_FUNCTION(this << linkId);
724 
725  auto timerIt = m_mediumSyncDelayStatus.find(linkId);
726 
727  NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && timerIt->second.timer.IsRunning());
728  return timerIt->second.msdNTxopsLeft == 0;
729 }
730 
731 MgtEmlOmn
732 EmlsrManager::GetEmlOmn()
733 {
734  MgtEmlOmn frame;
735 
736  // Add the EMLSR Parameter Update field if needed
737  if (m_lastAdvPaddingDelay != m_emlsrPaddingDelay ||
738  m_lastAdvTransitionDelay != m_emlsrTransitionDelay)
739  {
740  m_lastAdvPaddingDelay = m_emlsrPaddingDelay;
741  m_lastAdvTransitionDelay = m_emlsrTransitionDelay;
744  frame.m_emlsrParamUpdate->paddingDelay =
745  CommonInfoBasicMle::EncodeEmlsrPaddingDelay(m_lastAdvPaddingDelay);
746  frame.m_emlsrParamUpdate->transitionDelay =
747  CommonInfoBasicMle::EncodeEmlsrTransitionDelay(m_lastAdvTransitionDelay);
748  }
749 
750  // We must verify that the links included in the given EMLSR link set (if any) have been setup.
751  auto setupLinkIds = m_staMac->GetSetupLinkIds();
752 
753  for (auto emlsrLinkIt = m_nextEmlsrLinks->begin(); emlsrLinkIt != m_nextEmlsrLinks->end();)
754  {
755  if (auto setupLinkIt = setupLinkIds.find(*emlsrLinkIt); setupLinkIt != setupLinkIds.cend())
756  {
757  setupLinkIds.erase(setupLinkIt);
758  frame.SetLinkIdInBitmap(*emlsrLinkIt);
759  emlsrLinkIt++;
760  }
761  else
762  {
763  NS_LOG_DEBUG("Link ID " << +(*emlsrLinkIt) << " has not been setup");
764  emlsrLinkIt = m_nextEmlsrLinks->erase(emlsrLinkIt);
765  }
766  }
767 
768  // EMLSR Mode is enabled if and only if the set of EMLSR links is not empty
769  frame.m_emlControl.emlsrMode = m_nextEmlsrLinks->empty() ? 0 : 1;
770 
771  return frame;
772 }
773 
774 void
775 EmlsrManager::SendEmlOmn()
776 {
777  NS_LOG_FUNCTION(this);
778 
779  NS_ABORT_MSG_IF(!m_emlsrTransitionTimeout,
780  "AP did not advertise a Transition Timeout, cannot send EML notification");
781  NS_ASSERT_MSG(m_nextEmlsrLinks, "Need to set EMLSR links before calling this method");
782 
783  // TODO if this is a single radio non-AP MLD and not all setup links are in the EMLSR link
784  // set, we have to put setup links that are not included in the given EMLSR link set (i.e.,
785  // those remaining in setupLinkIds, if m_nextEmlsrLinks is not empty) in the sleep mode:
786  // For the EMLSR mode enabled in a single radio non-AP MLD, the STA(s) affiliated with
787  // the non-AP MLD that operates on the enabled link(s) that corresponds to the bit
788  // position(s) of the EMLSR Link Bitmap subfield set to 0 shall be in doze state if a
789  // non-AP STA affiliated with the non-AP MLD that operates on one of the EMLSR links is
790  // in awake state. (Sec. 35.3.17 of 802.11be D3.0)
791 
792  auto frame = GetEmlOmn();
793  auto linkId = GetLinkToSendEmlOmn();
794  GetEhtFem(linkId)->SendEmlOmn(m_staMac->GetBssid(linkId), frame);
795 }
796 
797 void
798 EmlsrManager::TxOk(Ptr<const WifiMpdu> mpdu)
799 {
800  NS_LOG_FUNCTION(this << *mpdu);
801 
802  const auto& hdr = mpdu->GetHeader();
803 
804  if (hdr.IsAssocReq())
805  {
806  // store padding delay and transition delay advertised in AssocReq
807  MgtAssocRequestHeader assocReq;
808  mpdu->GetPacket()->PeekHeader(assocReq);
809  auto& mle = assocReq.Get<MultiLinkElement>();
810  NS_ASSERT_MSG(mle, "AssocReq should contain a Multi-Link Element");
811  m_lastAdvPaddingDelay = mle->GetEmlsrPaddingDelay();
812  m_lastAdvTransitionDelay = mle->GetEmlsrTransitionDelay();
813  }
814 
815  if (hdr.IsMgt() && hdr.IsAction())
816  {
817  if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
818  category == WifiActionHeader::PROTECTED_EHT &&
819  action.protectedEhtAction ==
820  WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
821  {
822  // the EML Operating Mode Notification frame that we sent has been acknowledged.
823  // Start the transition timeout to wait until the request can be made effective
824  NS_ASSERT_MSG(m_emlsrTransitionTimeout, "No transition timeout received from AP");
825  m_transitionTimeoutEvent = Simulator::Schedule(*m_emlsrTransitionTimeout,
826  &EmlsrManager::ChangeEmlsrMode,
827  this);
828  }
829  }
830 }
831 
832 void
833 EmlsrManager::TxDropped(WifiMacDropReason reason, Ptr<const WifiMpdu> mpdu)
834 {
835  NS_LOG_FUNCTION(this << reason << *mpdu);
836 
837  const auto& hdr = mpdu->GetHeader();
838 
839  if (hdr.IsMgt() && hdr.IsAction())
840  {
841  auto pkt = mpdu->GetPacket()->Copy();
842  if (auto [category, action] = WifiActionHeader::Remove(pkt);
843  category == WifiActionHeader::PROTECTED_EHT &&
844  action.protectedEhtAction ==
845  WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
846  {
847  // the EML Operating Mode Notification frame has been dropped. Ask the subclass
848  // whether the frame needs to be resent
849  auto linkId = ResendNotification(mpdu);
850  if (linkId)
851  {
852  MgtEmlOmn frame;
853  pkt->RemoveHeader(frame);
854  GetEhtFem(*linkId)->SendEmlOmn(m_staMac->GetBssid(*linkId), frame);
855  }
856  else
857  {
858  m_nextEmlsrLinks.reset();
859  }
860  }
861  }
862 }
863 
864 void
865 EmlsrManager::ChangeEmlsrMode()
866 {
867  NS_LOG_FUNCTION(this);
868 
869  // After the successful transmission of the EML Operating Mode Notification frame by the
870  // non-AP STA affiliated with the non-AP MLD, the non-AP MLD shall operate in the EMLSR mode
871  // and the other non-AP STAs operating on the corresponding EMLSR links shall transition to
872  // active mode after the transition delay indicated in the Transition Timeout subfield in the
873  // EML Capabilities subfield of the Basic Multi-Link element or immediately after receiving an
874  // EML Operating Mode Notification frame from one of the APs operating on the EMLSR links and
875  // affiliated with the AP MLD. (Sec. 35.3.17 of 802.11be D3.0)
876  NS_ASSERT_MSG(m_nextEmlsrLinks, "No set of EMLSR links stored");
877  m_emlsrLinks.swap(*m_nextEmlsrLinks);
878  m_nextEmlsrLinks.reset();
879 
880  // Make other non-AP STAs operating on the corresponding EMLSR links transition to
881  // active mode or passive mode (depending on whether EMLSR mode has been enabled or disabled)
882  m_staMac->NotifyEmlsrModeChanged(m_emlsrLinks);
883  // Enforce the limit on the max channel width supported by aux PHYs
884  ApplyMaxChannelWidthAndModClassOnAuxPhys();
885 
886  NotifyEmlsrModeChanged();
887 }
888 
889 void
890 EmlsrManager::ApplyMaxChannelWidthAndModClassOnAuxPhys()
891 {
892  NS_LOG_FUNCTION(this);
893  auto currMainPhyLinkId = m_staMac->GetLinkForPhy(m_mainPhyId);
894  NS_ASSERT(currMainPhyLinkId);
895 
896  for (const auto linkId : m_staMac->GetLinkIds())
897  {
898  auto auxPhy = m_staMac->GetWifiPhy(linkId);
899  auto channel = GetChannelForAuxPhy(linkId);
900 
901  if (linkId == currMainPhyLinkId || !m_staMac->IsEmlsrLink(linkId) ||
902  auxPhy->GetOperatingChannel() == channel)
903  {
904  continue;
905  }
906 
907  auxPhy->SetMaxModulationClassSupported(m_auxPhyMaxModClass);
908 
909  NS_LOG_DEBUG("Aux PHY (" << auxPhy << ") is about to switch to " << channel
910  << " to operate on link " << +linkId);
911  // We cannot simply set the new channel, because otherwise the MAC will disable
912  // the setup link. We need to inform the MAC (via the Channel Access Manager) that
913  // this channel switch must not have such a consequence. We already have a method
914  // for doing so, i.e., inform the MAC that the PHY is switching channel to operate
915  // on the "same" link.
916  auto cam = m_staMac->GetChannelAccessManager(linkId);
917  cam->NotifySwitchingEmlsrLink(auxPhy, channel, linkId);
918 
919  void (WifiPhy::*fp)(const WifiPhyOperatingChannel&) = &WifiPhy::SetOperatingChannel;
920  Simulator::ScheduleNow(fp, auxPhy, channel);
921 
922  // the way the ChannelAccessManager handles EMLSR link switch implies that a PHY listener
923  // is removed when the channel switch starts and another one is attached when the channel
924  // switch ends. In the meantime, no PHY is connected to the ChannelAccessManager. Inform
925  // the ChannelAccessManager that this channel switch is related to EMLSR operations, so
926  // that the ChannelAccessManager does not complain if events requiring access to the PHY
927  // occur during the channel switch.
928  cam->NotifyStartUsingOtherEmlsrLink();
929  Simulator::Schedule(auxPhy->GetChannelSwitchDelay(),
930  &ChannelAccessManager::NotifyStopUsingOtherEmlsrLink,
931  cam);
932  }
933 }
934 
935 void
936 EmlsrManager::ComputeOperatingChannels()
937 {
938  NS_LOG_FUNCTION(this);
939 
940  m_mainPhyChannels.clear();
941  m_auxPhyChannels.clear();
942 
943  auto linkIds = m_staMac->GetSetupLinkIds();
944 
945  for (auto linkId : linkIds)
946  {
947  const auto& channel = m_staMac->GetWifiPhy(linkId)->GetOperatingChannel();
948  m_mainPhyChannels.emplace(linkId, channel);
949 
950  auto mainPhyChWidth = channel.GetWidth();
951  auto auxPhyMaxWidth =
952  std::min(m_auxPhyMaxWidth, GetMaximumChannelWidth(m_auxPhyMaxModClass));
953  if (auxPhyMaxWidth >= mainPhyChWidth)
954  {
955  // same channel can be used by aux PHYs
956  m_auxPhyChannels.emplace(linkId, channel);
957  continue;
958  }
959  // aux PHYs will operate on a primary subchannel
960  auto freq = channel.GetPrimaryChannelCenterFrequency(auxPhyMaxWidth);
961  auto chIt = WifiPhyOperatingChannel::FindFirst(0,
962  freq,
963  auxPhyMaxWidth,
965  channel.GetPhyBand());
966  NS_ASSERT_MSG(chIt != WifiPhyOperatingChannel::m_frequencyChannels.end(),
967  "Primary" << auxPhyMaxWidth << " channel not found");
968  m_auxPhyChannels.emplace(linkId, chIt);
969  // find the P20 index for the channel used by the aux PHYs
970  auto p20Index = channel.GetPrimaryChannelIndex(20);
971  while (mainPhyChWidth > auxPhyMaxWidth)
972  {
973  mainPhyChWidth /= 2;
974  p20Index /= 2;
975  }
976  m_auxPhyChannels[linkId].SetPrimary20Index(p20Index);
977  }
978 }
979 
981 EmlsrManager::GetChannelForMainPhy(uint8_t linkId) const
982 {
983  auto it = m_mainPhyChannels.find(linkId);
984  NS_ASSERT_MSG(it != m_mainPhyChannels.end(),
985  "Channel for main PHY on link ID " << +linkId << " not found");
986  return it->second;
987 }
988 
990 EmlsrManager::GetChannelForAuxPhy(uint8_t linkId) const
991 {
992  auto it = m_auxPhyChannels.find(linkId);
993  NS_ASSERT_MSG(it != m_auxPhyChannels.end(),
994  "Channel for aux PHY on link ID " << +linkId << " not found");
995  return it->second;
996 }
997 
998 } // namespace ns3
#define min(a, b)
Definition: 80211b.c:41
A container for one type of attribute.
void NotifyStartUsingOtherEmlsrLink()
Notify that another EMLSR link is being used, hence medium access should be disabled.
void SendEmlOmn()
Send an EML Operating Mode Notification frame.
Time GetMediumSyncDuration() const
void ComputeOperatingChannels()
Compute the operating channels that the main PHY and the aux PHY(s) must switch to in order to operat...
void SetTransitionTimeout(Time timeout)
Set the Transition Timeout advertised by the associated AP with EMLSR activated.
bool m_auxPhyTxCapable
whether Aux PHYs are capable of transmitting PPDUs
std::optional< Time > GetTransitionTimeout() const
Ptr< EhtFrameExchangeManager > GetEhtFem(uint8_t linkId) const
void TxDropped(WifiMacDropReason reason, Ptr< const WifiMpdu > mpdu)
Notify that the given MPDU has been discarded for the given reason.
void TxOk(Ptr< const WifiMpdu > mpdu)
Notify the acknowledgment of the given MPDU.
void SwitchMainPhy(uint8_t linkId, bool noSwitchDelay, bool resetBackoff, bool requestAccess)
Switch channel on the Main PHY so that it operates on the given link.
bool GetCamStateReset() const
void NotifyUlTxopStart(uint8_t linkId, std::optional< Time > timeToCtsEnd)
Notify the start of an UL TXOP on the given link.
void SetEmlsrLinks(const std::set< uint8_t > &linkIds)
Take actions to enable EMLSR mode on the given set of links, if non-empty, or disable EMLSR mode,...
void SetMediumSyncOfdmEdThreshold(int8_t threshold)
Set the Medium Synchronization OFDM ED threshold (dBm) to use while the MediumSyncDelay timer is runn...
uint8_t m_mainPhyId
ID of main PHY (position in the vector of PHYs held by WifiNetDevice)
int8_t GetMediumSyncOfdmEdThreshold() const
void NotifyIcfReceived(uint8_t linkId)
Notify the reception of an initial Control frame on the given link.
std::map< uint8_t, MediumSyncDelayStatus > m_mediumSyncDelayStatus
the status of MediumSyncDelay timers (link ID-indexed)
void NotifyMgtFrameReceived(Ptr< const WifiMpdu > mpdu, uint8_t linkId)
Notify the reception of a management frame addressed to us.
virtual void DoNotifyUlTxopStart(uint8_t linkId)=0
Notify the subclass of the start of an UL TXOP on the given link.
Ptr< StaWifiMac > m_staMac
the MAC of the managed non-AP MLD
virtual void DoNotifyMgtFrameReceived(Ptr< const WifiMpdu > mpdu, uint8_t linkId)=0
Notify the subclass of the reception of a management frame addressed to us.
Time m_emlsrPaddingDelay
EMLSR Padding delay.
void SetMediumSyncMaxNTxops(std::optional< uint8_t > nTxops)
Set the maximum number of TXOPs a non-AP STA is allowed to attempt to initiate while the MediumSyncDe...
void NotifyTxopEnd(uint8_t linkId)
Notify the end of a TXOP on the given link.
Time m_emlsrTransitionDelay
EMLSR Transition delay.
void SetWifiMac(Ptr< StaWifiMac > mac)
Set the wifi MAC.
bool GetAuxPhyTxCapable() const
const std::set< uint8_t > & GetEmlsrLinks() const
Time m_mediumSyncDuration
duration of the MediumSyncDelay timer
std::optional< Time > m_emlsrTransitionTimeout
Transition timeout advertised by APs with EMLSR activated.
std::optional< Time > GetElapsedMediumSyncDelayTimer(uint8_t linkId) const
Check whether the MediumSyncDelay timer is running for the STA operating on the given link.
virtual void DoNotifyTxopEnd(uint8_t linkId)=0
Notify the subclass of the end of a TXOP on the given link.
void SetAuxPhyTxCapable(bool capable)
Set the member variable indicating whether Aux PHYs are capable of transmitting PPDUs.
std::optional< std::set< uint8_t > > m_nextEmlsrLinks
ID of the links that will become the EMLSR links when the pending notification frame is acknowledged.
void SetMainPhyId(uint8_t mainPhyId)
Set the ID of main PHY (position in the vector of PHYs held by WifiNetDevice).
~EmlsrManager() override
void SetMediumSyncDuration(Time duration)
Set the duration of the MediumSyncDelay timer.
static constexpr bool RESET_BACKOFF
reset backoff on main PHY switch
static constexpr bool DONT_REQUEST_ACCESS
do not request channel access when PHY switch ends
void DoDispose() override
Destructor implementation.
void StartMediumSyncDelayTimer(uint8_t linkId)
Start the MediumSyncDelay timer and take the appropriate actions, if the timer is not already running...
int8_t m_msdOfdmEdThreshold
MediumSyncDelay OFDM ED threshold.
std::optional< uint8_t > m_msdMaxNTxops
MediumSyncDelay max number of TXOPs.
Ptr< StaWifiMac > GetStaMac() const
WifiModulationClass m_auxPhyMaxModClass
max modulation class supported by aux PHYs
uint8_t GetMainPhyId() const
std::optional< uint8_t > GetMediumSyncMaxNTxops() const
void SetCamStateReset(bool enable)
Set the member variable indicating whether the state of the CAM should be reset when the main PHY swi...
EventId m_transitionTimeoutEvent
Timer started after the successful transmission of an EML Operating Mode Notification frame.
uint16_t m_auxPhyMaxWidth
max channel width (MHz) supported by aux PHYs
virtual void DoNotifyIcfReceived(uint8_t linkId)=0
Notify the subclass of the reception of an initial Control frame on the given link.
bool m_resetCamState
whether to reset the state of CAM when main PHY switches channel
static TypeId GetTypeId()
Get the type ID.
std::set< uint8_t > m_emlsrLinks
ID of the EMLSR links (empty if EMLSR mode is disabled)
Hold variables of type enum.
Definition: enum.h:62
void Cancel()
This method is syntactic sugar for the ns3::Simulator::Cancel method.
Definition: event-id.cc:55
EventImpl * PeekEventImpl() const
Definition: event-id.cc:83
bool IsRunning() const
This method is syntactic sugar for !IsExpired().
Definition: event-id.cc:76
void Invoke()
Called by the simulation engine to notify the event that it is time to execute.
Definition: event-impl.cc:47
Implement the header for management frames of type association request.
Definition: mgt-headers.h:157
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.
bool TraceConnectWithoutContext(std::string name, const CallbackBase &cb)
Connect a TraceSource to a Callback without a context.
Definition: object-base.cc:315
bool TraceDisconnectWithoutContext(std::string name, const CallbackBase &cb)
Disconnect from a TraceSource a Callback previously connected without a context.
Definition: object-base.cc:343
A base class which provides memory management and object aggregation.
Definition: object.h:89
virtual void DoDispose()
Destructor implementation.
Definition: object.cc:352
bool IsInitialized() const
Check if the object has been initialized.
Definition: object.cc:212
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition: simulator.h:571
static EventId ScheduleNow(FUNC f, Ts &&... args)
Schedule an event to expire Now.
Definition: simulator.h:605
static Time GetDelayLeft(const EventId &id)
Get the remaining time until this event will execute.
Definition: simulator.cc:217
void BlockTxOnLink(uint8_t linkId, WifiQueueBlockedReason reason)
Block transmissions on the given link for the given reason.
bool IsEmlsrLink(uint8_t linkId) const
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
@ US
microsecond
Definition: nstime.h:118
a unique identifier for an interface.
Definition: type-id.h:59
@ ATTR_GET
The attribute can be read.
Definition: type-id.h:64
@ ATTR_CONSTRUCT
The attribute can be written at construction-time.
Definition: type-id.h:66
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:931
Hold an unsigned integer type.
Definition: uinteger.h:45
static std::pair< CategoryValue, ActionValue > Peek(Ptr< const Packet > pkt)
Peek an Action header from the given packet.
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
Mac48Address GetBssid(uint8_t linkId) const
Definition: wifi-mac.cc:478
TypeOfStation GetTypeOfStation() const
Return the type of station.
Definition: wifi-mac.cc:422
uint8_t GetNLinks() const
Get the number of links (can be greater than 1 for 11be devices only).
Definition: wifi-mac.cc:933
Ptr< WifiPhy > GetWifiPhy(uint8_t linkId=SINGLE_LINK_OP_ID) const
Definition: wifi-mac.cc:1171
Ptr< EhtConfiguration > GetEhtConfiguration() const
Definition: wifi-mac.cc:1755
Ptr< WifiNetDevice > GetDevice() const
Return the device this PHY is associated with.
Definition: wifi-mac.cc:439
std::optional< uint8_t > GetLinkForPhy(Ptr< const WifiPhy > phy) const
Get the ID of the link (if any) on which the given PHY is operating.
Definition: wifi-mac.cc:974
const std::set< uint8_t > & GetLinkIds() const
Definition: wifi-mac.cc:939
Ptr< ChannelAccessManager > GetChannelAccessManager(uint8_t linkId=SINGLE_LINK_OP_ID) const
Get the Channel Access Manager associated with the given link.
Definition: wifi-mac.cc:870
Ptr< WifiPhy > GetPhy() const
802.11 PHY layer model
Definition: wifi-phy.h:53
Time GetChannelSwitchDelay() const
Definition: wifi-phy.cc:690
Ptr< WifiPhyStateHelper > GetState() const
Return the WifiPhyStateHelper of this PHY.
Definition: wifi-phy.cc:443
Class that keeps track of all information about the current PHY operating channel.
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition: assert.h:66
#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
#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_FUNCTION_NOARGS()
Output the name of the function.
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:46
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1350
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1326
WifiMacDropReason
The reason why an MPDU was dropped.
Definition: wifi-mac.h:77
@ STA
Definition: wifi-mac.h:65
@ WIFI_STANDARD_UNSPECIFIED
@ WIFI_MOD_CLASS_OFDM
OFDM (Clause 17)
@ WIFI_MOD_CLASS_HR_DSSS
HR/DSSS (Clause 16)
@ WIFI_MOD_CLASS_HT
HT (Clause 19)
@ WIFI_MOD_CLASS_EHT
EHT (Clause 36)
@ WIFI_MOD_CLASS_VHT
VHT (Clause 22)
@ WIFI_MOD_CLASS_HE
HE (Clause 27)
@ WIFI_MOD_CLASS_ERP_OFDM
ERP-OFDM (18.4)
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition: boolean.cc:124
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Definition: nstime.h:1414
static constexpr uint8_t DEFAULT_MSD_MAX_N_TXOPS
default MediumSyncDelay max number of TXOP attempts
uint16_t GetMaximumChannelWidth(WifiModulationClass modulation)
Get the maximum channel width in MHz allowed for the given modulation class.
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
Ptr< const AttributeChecker > MakeEnumChecker(T v, std::string n, Ts... args)
Make an EnumChecker pre-configured with a set of allowed values by name.
Definition: enum.h:194
Ptr< const AttributeChecker > MakeTimeChecker(const Time min, const Time max)
Helper to make a Time checker with bounded range.
Definition: time.cc:533
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Definition: boolean.h:86
const std::map< AcIndex, WifiAc > wifiAcList
Map containing the four ACs in increasing order of priority (according to Table 10-1 "UP-to-AC Mappin...
Definition: qos-utils.cc:126
static constexpr int8_t DEFAULT_MSD_OFDM_ED_THRESH
default MediumSyncDelay timer OFDM ED threshold
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
Definition: uinteger.h:46
static constexpr uint16_t DEFAULT_MSD_DURATION_USEC
default MediumSyncDelay timer duration (max PPDU TX time rounded to a multiple of 32 us)
channel
Definition: third.py:88
mac
Definition: third.py:92
phy
Definition: third.py:89
ns3::Time timeout
uint8_t emlsrParamUpdateCtrl
EMLSR Parameter Update Control.
EMLSR Parameter Update field.
@ TX
The PHY layer is sending a packet.