A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
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#include <iterator>
35#include <sstream>
36
37namespace ns3
38{
39
40NS_LOG_COMPONENT_DEFINE("EmlsrManager");
41
42NS_OBJECT_ENSURE_REGISTERED(EmlsrManager);
43
44TypeId
46{
47 static TypeId tid =
48 TypeId("ns3::EmlsrManager")
50 .SetGroupName("Wifi")
51 .AddAttribute("EmlsrPaddingDelay",
52 "The EMLSR Paddind Delay (not used by AP MLDs). "
53 "Possible values are 0 us, 32 us, 64 us, 128 us or 256 us.",
57 .AddAttribute("EmlsrTransitionDelay",
58 "The EMLSR Transition Delay (not used by AP MLDs). "
59 "Possible values are 0 us, 16 us, 32 us, 64 us, 128 us or 256 us.",
63 .AddAttribute(
64 "MainPhyId",
65 "The ID of the main PHY (position in the vector of PHYs held by "
66 "WifiNetDevice). This attribute cannot be set after construction.",
67 TypeId::ATTR_GET | TypeId::ATTR_CONSTRUCT, // prevent setting after construction
70 MakeUintegerChecker<uint8_t>())
71 .AddAttribute("AuxPhyChannelWidth",
72 "The maximum channel width (MHz) supported by Aux PHYs. Note that the "
73 "maximum channel width is capped to the maximum channel width supported "
74 "by the configured maximum modulation class supported.",
76 TypeId::ATTR_CONSTRUCT, // prevent setting after construction
77 UintegerValue(20),
79 MakeUintegerChecker<uint16_t>(20, 160))
80 .AddAttribute("AuxPhyMaxModClass",
81 "The maximum modulation class supported by Aux PHYs. Use "
82 "WIFI_MOD_CLASS_OFDM for non-HT.",
84 TypeId::ATTR_CONSTRUCT, // prevent setting after construction
86 MakeEnumAccessor<WifiModulationClass>(&EmlsrManager::m_auxPhyMaxModClass),
88 "HR-DSSS",
90 "ERP-OFDM",
92 "OFDM",
94 "HT",
96 "VHT",
98 "HE",
100 "EHT"))
101 .AddAttribute("AuxPhyTxCapable",
102 "Whether Aux PHYs are capable of transmitting PPDUs.",
103 BooleanValue(true),
107 .AddAttribute(
108 "EmlsrLinkSet",
109 "IDs of the links on which EMLSR mode will be enabled. An empty set "
110 "indicates to disable EMLSR.",
112 MakeAttributeContainerAccessor<UintegerValue>(&EmlsrManager::SetEmlsrLinks),
113 MakeAttributeContainerChecker<UintegerValue>(MakeUintegerChecker<uint8_t>()))
114 .AddAttribute("ResetCamState",
115 "Whether to reset the state of the ChannelAccessManager associated with "
116 "the link on which the main PHY has just switched to.",
117 BooleanValue(false),
121 return tid;
122}
123
125 // The STA initializes dot11MSDTimerDuration to aPPDUMaxTime defined in Table 36-70
126 // (Sec. 35.3.16.8.1 of 802.11be D3.1)
127 : m_mediumSyncDuration(MicroSeconds(DEFAULT_MSD_DURATION_USEC)),
128 // The default value of dot11MSDOFDMEDthreshold is –72 dBm and the default value of
129 // dot11MSDTXOPMax is 1, respectively (Sec. 35.3.16.8.1 of 802.11be D3.1)
130 m_msdOfdmEdThreshold(DEFAULT_MSD_OFDM_ED_THRESH),
131 m_msdMaxNTxops(DEFAULT_MSD_MAX_N_TXOPS)
132{
133 NS_LOG_FUNCTION(this);
134}
135
137{
139}
140
141void
143{
144 NS_LOG_FUNCTION(this);
148 m_staMac = nullptr;
150 for (auto& [id, status] : m_mediumSyncDelayStatus)
151 {
152 status.timer.Cancel();
153 }
155}
156
157void
159{
160 NS_LOG_FUNCTION(this << mac);
161 NS_ASSERT(mac);
162 m_staMac = mac;
163
164 NS_ABORT_MSG_IF(!m_staMac->GetEhtConfiguration(), "EmlsrManager requires EHT support");
165 NS_ABORT_MSG_IF(m_staMac->GetNLinks() <= 1, "EmlsrManager can only be installed on MLDs");
167 "EmlsrManager can only be installed on non-AP MLDs");
168
172}
173
174void
176{
177 NS_LOG_FUNCTION(this << mainPhyId);
178 NS_ABORT_MSG_IF(IsInitialized(), "Cannot be called once this object has been initialized");
179 m_mainPhyId = mainPhyId;
180}
181
182uint8_t
184{
185 return m_mainPhyId;
186}
187
188void
190{
191 m_resetCamState = enable;
192}
193
194bool
196{
197 return m_resetCamState;
198}
199
200void
202{
203 m_auxPhyTxCapable = capable;
204}
205
206bool
208{
209 return m_auxPhyTxCapable;
210}
211
212const std::set<uint8_t>&
214{
215 return m_emlsrLinks;
216}
217
220{
221 return m_staMac;
222}
223
225EmlsrManager::GetEhtFem(uint8_t linkId) const
226{
227 return StaticCast<EhtFrameExchangeManager>(m_staMac->GetFrameExchangeManager(linkId));
228}
229
230std::optional<Time>
232{
233 if (const auto statusIt = m_mediumSyncDelayStatus.find(linkId);
234 statusIt != m_mediumSyncDelayStatus.cend() && statusIt->second.timer.IsPending())
235 {
236 return m_mediumSyncDuration - Simulator::GetDelayLeft(statusIt->second.timer);
237 }
238 return std::nullopt;
239}
240
241void
243{
246}
247
248std::optional<Time>
250{
252}
253
254void
256{
257 NS_LOG_FUNCTION(this << duration.As(Time::US));
258 m_mediumSyncDuration = duration;
259}
260
261Time
263{
265}
266
267void
269{
270 NS_LOG_FUNCTION(this << threshold);
271 m_msdOfdmEdThreshold = threshold;
272}
273
274int8_t
276{
278}
279
280void
281EmlsrManager::SetMediumSyncMaxNTxops(std::optional<uint8_t> nTxops)
282{
283 NS_LOG_FUNCTION(this << nTxops.has_value());
284 m_msdMaxNTxops = nTxops;
285}
286
287std::optional<uint8_t>
289{
290 return m_msdMaxNTxops;
291}
292
293void
294EmlsrManager::SetEmlsrLinks(const std::set<uint8_t>& linkIds)
295{
296 std::stringstream ss;
297 if (g_log.IsEnabled(ns3::LOG_FUNCTION))
298 {
299 std::copy(linkIds.cbegin(), linkIds.cend(), std::ostream_iterator<uint16_t>(ss, " "));
300 }
301 NS_LOG_FUNCTION(this << ss.str());
302 NS_ABORT_MSG_IF(linkIds.size() == 1, "Cannot enable EMLSR mode on a single link");
303
304 if (linkIds != m_emlsrLinks)
305 {
306 m_nextEmlsrLinks = linkIds;
307 }
308
309 if (GetStaMac() && GetStaMac()->IsAssociated() && GetTransitionTimeout() && m_nextEmlsrLinks)
310 {
311 // Request to enable EMLSR mode on the given links, provided that they have been setup
312 SendEmlOmn();
313 }
314}
315
316void
318{
319 NS_LOG_FUNCTION(this << *mpdu << linkId);
320
321 const auto& hdr = mpdu->GetHeader();
322
323 DoNotifyMgtFrameReceived(mpdu, linkId);
324
325 if (hdr.IsAssocResp() && GetStaMac()->IsAssociated() && GetTransitionTimeout())
326 {
327 // we just completed ML setup with an AP MLD that supports EMLSR
329
330 if (m_nextEmlsrLinks && !m_nextEmlsrLinks->empty())
331 {
332 // a non-empty set of EMLSR links have been configured, hence enable EMLSR mode
333 // on those links
334 SendEmlOmn();
335 }
336 }
337
338 if (hdr.IsAction() && hdr.GetAddr2() == m_staMac->GetBssid(linkId))
339 {
340 // this is an action frame sent by an AP of the AP MLD we are associated with
341 auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
342 if (category == WifiActionHeader::PROTECTED_EHT &&
343 action.protectedEhtAction ==
345 {
347 {
348 // no need to wait until the expiration of the transition timeout
351 }
352 }
353 }
354}
355
356void
358{
359 NS_LOG_FUNCTION(this << linkId);
360
362
363 // block transmissions and suspend medium access on all other EMLSR links
364 for (auto id : m_staMac->GetLinkIds())
365 {
366 if (id != linkId && m_staMac->IsEmlsrLink(id))
367 {
370 }
371 }
372
373 auto mainPhy = m_staMac->GetDevice()->GetPhy(m_mainPhyId);
374 auto auxPhy = m_staMac->GetWifiPhy(linkId);
375
376 if (m_staMac->GetWifiPhy(linkId) == mainPhy)
377 {
378 // nothing to do, we received an ICF from the main PHY
379 return;
380 }
381
382 Simulator::ScheduleNow([=, this]() {
383 SwitchMainPhy(linkId,
384 true, // channel switch should occur instantaneously
387
388 // aux PHY received the ICF but main PHY will send the response
389 auto uid = auxPhy->GetPreviouslyRxPpduUid();
390 mainPhy->SetPreviouslyRxPpduUid(uid);
391
392 DoNotifyIcfReceived(linkId);
393 });
394}
395
396void
397EmlsrManager::NotifyUlTxopStart(uint8_t linkId, std::optional<Time> timeToCtsEnd)
398{
399 NS_LOG_FUNCTION(this << linkId);
400
401 if (!m_staMac->IsEmlsrLink(linkId))
402 {
403 NS_LOG_DEBUG("EMLSR is not enabled on link " << +linkId);
404 return;
405 }
406
407 // block transmissions and suspend medium access on all other EMLSR links
408 for (auto id : m_staMac->GetLinkIds())
409 {
410 if (id != linkId && m_staMac->IsEmlsrLink(id))
411 {
414 }
415 }
416
417 // if this TXOP is being started by an aux PHY, schedule a channel switch for the main PHY
418 // such that the channel switch is completed by the time the CTS response is received. The
419 // delay has been passed by the FEM.
420 if (m_staMac->GetLinkForPhy(m_mainPhyId) != linkId)
421 {
422 auto stateHelper = m_staMac->GetWifiPhy(linkId)->GetState();
423 NS_ASSERT(stateHelper);
424 NS_ASSERT_MSG(stateHelper->GetState() == WifiPhyState::TX,
425 "Expecting the aux PHY to be transmitting (an RTS frame)");
426 NS_ASSERT_MSG(timeToCtsEnd.has_value(),
427 "Aux PHY is sending RTS, expected to get the time to CTS end");
428
429 auto mainPhy = m_staMac->GetDevice()->GetPhy(m_mainPhyId);
430
431 // the main PHY shall terminate the channel switch at the end of CTS reception;
432 // the time remaining to the end of CTS reception includes two propagation delays
433 const auto delay = *timeToCtsEnd - mainPhy->GetChannelSwitchDelay();
434
435 NS_ASSERT(delay.IsPositive());
436 NS_LOG_DEBUG("Schedule main Phy switch in " << delay.As(Time::US));
439 this,
440 linkId,
441 false,
444 }
445
446 DoNotifyUlTxopStart(linkId);
447}
448
449void
450EmlsrManager::NotifyTxopEnd(uint8_t linkId, bool ulTxopNotStarted, bool ongoingDlTxop)
451{
452 NS_LOG_FUNCTION(this << linkId << ulTxopNotStarted << ongoingDlTxop);
453
454 if (!m_staMac->IsEmlsrLink(linkId))
455 {
456 NS_LOG_DEBUG("EMLSR is not enabled on link " << +linkId);
457 return;
458 }
459
460 // If the main PHY has been scheduled to switch to this link, cancel the channel switch.
461 // This happens, e.g., when an aux PHY sent an RTS to start an UL TXOP but it did not
462 // receive a CTS response.
463 if (auto it = m_ulMainPhySwitch.find(linkId); it != m_ulMainPhySwitch.end())
464 {
465 if (it->second.IsPending())
466 {
467 NS_LOG_DEBUG("Cancelling main PHY channel switch event on link " << +linkId);
468 it->second.Cancel();
469 }
470 m_ulMainPhySwitch.erase(it);
471 }
472
473 // Unblock the other EMLSR links and start the MediumSyncDelay timer, provided that the TXOP
474 // included the transmission of at least a frame and there is no ongoing DL TXOP on this link.
475 // Indeed, the UL TXOP may have ended because the transmission of a frame failed and the
476 // corresponding TX timeout (leading to this call) may have occurred after the reception on
477 // this link of an ICF starting a DL TXOP. If the EMLSR Manager unblocked the other EMLSR
478 // links, another TXOP could be started on another EMLSR link (possibly leading to a crash)
479 // while the DL TXOP on this link is ongoing.
480 if (ongoingDlTxop)
481 {
482 NS_LOG_DEBUG("DL TXOP ongoing");
483 return;
484 }
485 if (ulTxopNotStarted)
486 {
487 NS_LOG_DEBUG("TXOP did not even start");
488 return;
489 }
490
491 DoNotifyTxopEnd(linkId);
492
493 Simulator::ScheduleNow([=, this]() {
494 // unblock transmissions and resume medium access on other EMLSR links
495 std::set<uint8_t> linkIds;
496 for (auto id : m_staMac->GetLinkIds())
497 {
498 if ((id != linkId) && m_staMac->IsEmlsrLink(id))
499 {
500 m_staMac->GetChannelAccessManager(id)->NotifyStopUsingOtherEmlsrLink();
501 linkIds.insert(id);
502 }
503 }
505
507 });
508}
509
510void
511EmlsrManager::SetCcaEdThresholdOnLinkSwitch(Ptr<WifiPhy> phy, uint8_t linkId)
512{
513 NS_LOG_FUNCTION(this << phy << linkId);
514
515 // if a MediumSyncDelay timer is running for the link on which the main PHY is going to
516 // operate, set the CCA ED threshold to the MediumSyncDelay OFDM ED threshold
517 if (auto statusIt = m_mediumSyncDelayStatus.find(linkId);
518 statusIt != m_mediumSyncDelayStatus.cend() && statusIt->second.timer.IsPending())
519 {
520 NS_LOG_DEBUG("Setting CCA ED threshold of PHY " << phy << " to " << +m_msdOfdmEdThreshold
521 << " on link " << +linkId);
522
523 // store the current CCA ED threshold in the m_prevCcaEdThreshold map, if not present
524 m_prevCcaEdThreshold.try_emplace(phy, phy->GetCcaEdThreshold());
525
526 phy->SetCcaEdThreshold(m_msdOfdmEdThreshold);
527 }
528 // otherwise, restore the previous value for the CCA ED threshold (if any)
529 else if (auto threshIt = m_prevCcaEdThreshold.find(phy);
530 threshIt != m_prevCcaEdThreshold.cend())
531 {
532 NS_LOG_DEBUG("Resetting CCA ED threshold of PHY " << phy << " to " << threshIt->second
533 << " on link " << +linkId);
534 phy->SetCcaEdThreshold(threshIt->second);
535 m_prevCcaEdThreshold.erase(threshIt);
536 }
537}
538
539void
540EmlsrManager::SwitchMainPhy(uint8_t linkId,
541 bool noSwitchDelay,
542 bool resetBackoff,
543 bool requestAccess)
544{
545 NS_LOG_FUNCTION(this << linkId << noSwitchDelay << resetBackoff << requestAccess);
546
547 auto mainPhy = m_staMac->GetDevice()->GetPhy(m_mainPhyId);
548
549 NS_ASSERT_MSG(mainPhy != m_staMac->GetWifiPhy(linkId),
550 "Main PHY is already operating on link " << +linkId);
551
552 if (mainPhy->IsStateSwitching())
553 {
554 NS_LOG_DEBUG("Main PHY is already switching, ignore new switching request");
555 return;
556 }
557
558 // find the link on which the main PHY is operating
559 auto currMainPhyLinkId = m_staMac->GetLinkForPhy(mainPhy);
560 NS_ASSERT_MSG(currMainPhyLinkId, "Current link ID for main PHY not found");
561
562 auto newMainPhyChannel = GetChannelForMainPhy(linkId);
563
564 NS_LOG_DEBUG("Main PHY (" << mainPhy << ") is about to switch to " << newMainPhyChannel
565 << " to operate on link " << +linkId);
566
567 // notify the channel access manager of the upcoming channel switch(es)
568 m_staMac->GetChannelAccessManager(*currMainPhyLinkId)
569 ->NotifySwitchingEmlsrLink(mainPhy, newMainPhyChannel, linkId);
570
571 // this assert also ensures that the actual channel switch is not delayed
572 NS_ASSERT_MSG(!mainPhy->GetState()->IsStateTx(),
573 "We should not ask the main PHY to switch channel while transmitting");
574
575 // request the main PHY to switch channel
576 const auto delay = mainPhy->GetChannelSwitchDelay();
577 const auto pifs = mainPhy->GetSifs() + mainPhy->GetSlot();
578 NS_ASSERT_MSG(noSwitchDelay || delay <= std::max(m_lastAdvTransitionDelay, pifs),
579 "Channel switch delay ("
580 << delay.As(Time::US)
581 << ") should be shorter than the maximum between the Transition delay ("
582 << m_lastAdvTransitionDelay.As(Time::US) << ") and a PIFS ("
583 << pifs.As(Time::US) << ")");
584 if (noSwitchDelay)
585 {
586 mainPhy->SetAttribute("ChannelSwitchDelay", TimeValue(Seconds(0)));
587 }
588 mainPhy->SetOperatingChannel(newMainPhyChannel);
589 // restore previous channel switch delay
590 if (noSwitchDelay)
591 {
592 mainPhy->SetAttribute("ChannelSwitchDelay", TimeValue(delay));
593 }
594 // re-enable short time slot, if needed
595 if (m_staMac->GetWifiRemoteStationManager(linkId)->GetShortSlotTimeEnabled())
596 {
597 mainPhy->SetSlot(MicroSeconds(9));
598 }
599
600 if (resetBackoff)
601 {
602 // reset the backoffs on the link left by the main PHY
603 m_staMac->GetChannelAccessManager(*currMainPhyLinkId)->ResetAllBackoffs();
604 }
605
606 const auto timeToSwitchEnd = noSwitchDelay ? Seconds(0) : mainPhy->GetChannelSwitchDelay();
607
608 if (requestAccess)
609 {
610 // schedule channel access request on the new link when switch is completed
611 Simulator::Schedule(timeToSwitchEnd, [=, this]() {
612 for (const auto& [acIndex, ac] : wifiAcList)
613 {
614 m_staMac->GetQosTxop(acIndex)->StartAccessAfterEvent(
615 linkId,
616 Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT,
617 Txop::CHECK_MEDIUM_BUSY);
618 }
619 });
620 }
621
622 SetCcaEdThresholdOnLinkSwitch(mainPhy, linkId);
623 NotifyMainPhySwitch(*currMainPhyLinkId, linkId);
624}
625
626void
627EmlsrManager::SwitchAuxPhy(uint8_t currLinkId, uint8_t nextLinkId)
628{
629 NS_LOG_FUNCTION(this << currLinkId << nextLinkId);
630
631 auto auxPhy = GetStaMac()->GetWifiPhy(currLinkId);
632
633 auto newAuxPhyChannel = GetChannelForAuxPhy(nextLinkId);
634
635 NS_LOG_DEBUG("Aux PHY (" << auxPhy << ") is about to switch to " << newAuxPhyChannel
636 << " to operate on link " << +nextLinkId);
637
638 GetStaMac()
639 ->GetChannelAccessManager(currLinkId)
640 ->NotifySwitchingEmlsrLink(auxPhy, newAuxPhyChannel, nextLinkId);
641
642 auxPhy->SetOperatingChannel(newAuxPhyChannel);
643 // re-enable short time slot, if needed
644 if (m_staMac->GetWifiRemoteStationManager(nextLinkId)->GetShortSlotTimeEnabled())
645 {
646 auxPhy->SetSlot(MicroSeconds(9));
647 }
648
649 // schedule channel access request on the new link when switch is completed
650 Simulator::Schedule(auxPhy->GetChannelSwitchDelay(), [=, this]() {
651 for (const auto& [acIndex, ac] : wifiAcList)
652 {
653 m_staMac->GetQosTxop(acIndex)->StartAccessAfterEvent(
654 nextLinkId,
655 Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT,
656 Txop::CHECK_MEDIUM_BUSY);
657 }
658 });
659
660 SetCcaEdThresholdOnLinkSwitch(auxPhy, nextLinkId);
661}
662
663void
664EmlsrManager::StartMediumSyncDelayTimer(uint8_t linkId)
665{
666 NS_LOG_FUNCTION(this << linkId);
667
668 // iterate over all the other EMLSR links
669 for (auto id : m_staMac->GetLinkIds())
670 {
671 if (id != linkId && m_staMac->IsEmlsrLink(id))
672 {
673 const auto [it, inserted] = m_mediumSyncDelayStatus.try_emplace(id);
674
675 // reset the max number of TXOP attempts
676 it->second.msdNTxopsLeft = m_msdMaxNTxops;
677
678 // there are cases in which no PHY is operating on a link; e.g., the main PHY starts
679 // switching to a link on which an aux PHY gained a TXOP and sent an RTS, but the CTS
680 // is not received and the UL TXOP ends before the main PHY channel switch is
681 // completed. The MSD timer is started on the link left "uncovered" by the main PHY
682 if (auto phy = m_staMac->GetWifiPhy(id); phy && !it->second.timer.IsPending())
683 {
684 NS_LOG_DEBUG("Setting CCA ED threshold on link "
685 << +id << " to " << +m_msdOfdmEdThreshold << " PHY " << phy);
686 m_prevCcaEdThreshold[phy] = phy->GetCcaEdThreshold();
687 phy->SetCcaEdThreshold(m_msdOfdmEdThreshold);
688 }
689
690 // (re)start the timer
691 it->second.timer.Cancel();
692 it->second.timer = Simulator::Schedule(m_mediumSyncDuration,
693 &EmlsrManager::MediumSyncDelayTimerExpired,
694 this,
695 id);
696 }
697 }
698}
699
700void
701EmlsrManager::CancelMediumSyncDelayTimer(uint8_t linkId)
702{
703 NS_LOG_FUNCTION(this << linkId);
704
705 auto timerIt = m_mediumSyncDelayStatus.find(linkId);
706
707 NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && timerIt->second.timer.IsPending());
708
709 timerIt->second.timer.Cancel();
710 MediumSyncDelayTimerExpired(linkId);
711}
712
713void
714EmlsrManager::MediumSyncDelayTimerExpired(uint8_t linkId)
715{
716 NS_LOG_FUNCTION(this << linkId);
717
718 auto timerIt = m_mediumSyncDelayStatus.find(linkId);
719
720 NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && !timerIt->second.timer.IsPending());
721
722 // reset the MSD OFDM ED threshold
723 auto phy = m_staMac->GetWifiPhy(linkId);
724
725 if (!phy)
726 {
727 // no PHY is operating on this link. This may happen when a MediumSyncDelay timer expires
728 // on the link left "uncovered" by the main PHY that is operating on another link (and the
729 // aux PHY of that link did not switch). In this case, do nothing, since the CCA ED
730 // threshold on the main PHY will be restored once the main PHY switches back to its link
731 return;
732 }
733
734 auto threshIt = m_prevCcaEdThreshold.find(phy);
735 NS_ASSERT_MSG(threshIt != m_prevCcaEdThreshold.cend(),
736 "No value to restore for CCA ED threshold on PHY " << phy);
737 NS_LOG_DEBUG("Resetting CCA ED threshold of PHY " << phy << " to " << threshIt->second
738 << " on link " << +linkId);
739 phy->SetCcaEdThreshold(threshIt->second);
740 m_prevCcaEdThreshold.erase(threshIt);
741}
742
743void
744EmlsrManager::DecrementMediumSyncDelayNTxops(uint8_t linkId)
745{
746 NS_LOG_FUNCTION(this << linkId);
747
748 const auto timerIt = m_mediumSyncDelayStatus.find(linkId);
749
750 NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && timerIt->second.timer.IsPending());
751 NS_ASSERT(timerIt->second.msdNTxopsLeft != 0);
752
753 if (timerIt->second.msdNTxopsLeft)
754 {
755 --timerIt->second.msdNTxopsLeft.value();
756 }
757}
758
759void
760EmlsrManager::ResetMediumSyncDelayNTxops(uint8_t linkId)
761{
762 NS_LOG_FUNCTION(this << linkId);
763
764 auto timerIt = m_mediumSyncDelayStatus.find(linkId);
765
766 NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && timerIt->second.timer.IsPending());
767 timerIt->second.msdNTxopsLeft.reset();
768}
769
770bool
771EmlsrManager::MediumSyncDelayNTxopsExceeded(uint8_t linkId)
772{
773 NS_LOG_FUNCTION(this << linkId);
774
775 auto timerIt = m_mediumSyncDelayStatus.find(linkId);
776
777 NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && timerIt->second.timer.IsPending());
778 return timerIt->second.msdNTxopsLeft == 0;
779}
780
782EmlsrManager::GetEmlOmn()
783{
784 MgtEmlOmn frame;
785
786 // Add the EMLSR Parameter Update field if needed
787 if (m_lastAdvPaddingDelay != m_emlsrPaddingDelay ||
788 m_lastAdvTransitionDelay != m_emlsrTransitionDelay)
789 {
790 m_lastAdvPaddingDelay = m_emlsrPaddingDelay;
791 m_lastAdvTransitionDelay = m_emlsrTransitionDelay;
794 frame.m_emlsrParamUpdate->paddingDelay =
795 CommonInfoBasicMle::EncodeEmlsrPaddingDelay(m_lastAdvPaddingDelay);
796 frame.m_emlsrParamUpdate->transitionDelay =
797 CommonInfoBasicMle::EncodeEmlsrTransitionDelay(m_lastAdvTransitionDelay);
798 }
799
800 // We must verify that the links included in the given EMLSR link set (if any) have been setup.
801 auto setupLinkIds = m_staMac->GetSetupLinkIds();
802
803 for (auto emlsrLinkIt = m_nextEmlsrLinks->begin(); emlsrLinkIt != m_nextEmlsrLinks->end();)
804 {
805 if (auto setupLinkIt = setupLinkIds.find(*emlsrLinkIt); setupLinkIt != setupLinkIds.cend())
806 {
807 setupLinkIds.erase(setupLinkIt);
808 frame.SetLinkIdInBitmap(*emlsrLinkIt);
809 emlsrLinkIt++;
810 }
811 else
812 {
813 NS_LOG_DEBUG("Link ID " << +(*emlsrLinkIt) << " has not been setup");
814 emlsrLinkIt = m_nextEmlsrLinks->erase(emlsrLinkIt);
815 }
816 }
817
818 // EMLSR Mode is enabled if and only if the set of EMLSR links is not empty
819 frame.m_emlControl.emlsrMode = m_nextEmlsrLinks->empty() ? 0 : 1;
820
821 return frame;
822}
823
824void
825EmlsrManager::SendEmlOmn()
826{
827 NS_LOG_FUNCTION(this);
828
829 NS_ABORT_MSG_IF(!m_emlsrTransitionTimeout,
830 "AP did not advertise a Transition Timeout, cannot send EML notification");
831 NS_ASSERT_MSG(m_nextEmlsrLinks, "Need to set EMLSR links before calling this method");
832
833 // TODO if this is a single radio non-AP MLD and not all setup links are in the EMLSR link
834 // set, we have to put setup links that are not included in the given EMLSR link set (i.e.,
835 // those remaining in setupLinkIds, if m_nextEmlsrLinks is not empty) in the sleep mode:
836 // For the EMLSR mode enabled in a single radio non-AP MLD, the STA(s) affiliated with
837 // the non-AP MLD that operates on the enabled link(s) that corresponds to the bit
838 // position(s) of the EMLSR Link Bitmap subfield set to 0 shall be in doze state if a
839 // non-AP STA affiliated with the non-AP MLD that operates on one of the EMLSR links is
840 // in awake state. (Sec. 35.3.17 of 802.11be D3.0)
841
842 auto frame = GetEmlOmn();
843 auto linkId = GetLinkToSendEmlOmn();
844 GetEhtFem(linkId)->SendEmlOmn(m_staMac->GetBssid(linkId), frame);
845}
846
847void
848EmlsrManager::TxOk(Ptr<const WifiMpdu> mpdu)
849{
850 NS_LOG_FUNCTION(this << *mpdu);
851
852 const auto& hdr = mpdu->GetHeader();
853
854 if (hdr.IsAssocReq())
855 {
856 // store padding delay and transition delay advertised in AssocReq
857 MgtAssocRequestHeader assocReq;
858 mpdu->GetPacket()->PeekHeader(assocReq);
859 auto& mle = assocReq.Get<MultiLinkElement>();
860 NS_ASSERT_MSG(mle, "AssocReq should contain a Multi-Link Element");
861 m_lastAdvPaddingDelay = mle->GetEmlsrPaddingDelay();
862 m_lastAdvTransitionDelay = mle->GetEmlsrTransitionDelay();
863 }
864
865 if (hdr.IsMgt() && hdr.IsAction())
866 {
867 if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
868 category == WifiActionHeader::PROTECTED_EHT &&
869 action.protectedEhtAction ==
870 WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
871 {
872 // the EML Operating Mode Notification frame that we sent has been acknowledged.
873 // Start the transition timeout to wait until the request can be made effective
874 NS_ASSERT_MSG(m_emlsrTransitionTimeout, "No transition timeout received from AP");
875 m_transitionTimeoutEvent = Simulator::Schedule(*m_emlsrTransitionTimeout,
876 &EmlsrManager::ChangeEmlsrMode,
877 this);
878 }
879 }
880}
881
882void
883EmlsrManager::TxDropped(WifiMacDropReason reason, Ptr<const WifiMpdu> mpdu)
884{
885 NS_LOG_FUNCTION(this << reason << *mpdu);
886
887 const auto& hdr = mpdu->GetHeader();
888
889 if (hdr.IsMgt() && hdr.IsAction())
890 {
891 auto pkt = mpdu->GetPacket()->Copy();
892 if (auto [category, action] = WifiActionHeader::Remove(pkt);
893 category == WifiActionHeader::PROTECTED_EHT &&
894 action.protectedEhtAction ==
895 WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
896 {
897 // the EML Operating Mode Notification frame has been dropped. Ask the subclass
898 // whether the frame needs to be resent
899 auto linkId = ResendNotification(mpdu);
900 if (linkId)
901 {
902 MgtEmlOmn frame;
903 pkt->RemoveHeader(frame);
904 GetEhtFem(*linkId)->SendEmlOmn(m_staMac->GetBssid(*linkId), frame);
905 }
906 else
907 {
908 m_nextEmlsrLinks.reset();
909 }
910 }
911 }
912}
913
914void
915EmlsrManager::ChangeEmlsrMode()
916{
917 NS_LOG_FUNCTION(this);
918
919 // After the successful transmission of the EML Operating Mode Notification frame by the
920 // non-AP STA affiliated with the non-AP MLD, the non-AP MLD shall operate in the EMLSR mode
921 // and the other non-AP STAs operating on the corresponding EMLSR links shall transition to
922 // active mode after the transition delay indicated in the Transition Timeout subfield in the
923 // EML Capabilities subfield of the Basic Multi-Link element or immediately after receiving an
924 // EML Operating Mode Notification frame from one of the APs operating on the EMLSR links and
925 // affiliated with the AP MLD. (Sec. 35.3.17 of 802.11be D3.0)
926 NS_ASSERT_MSG(m_nextEmlsrLinks, "No set of EMLSR links stored");
927 m_emlsrLinks.swap(*m_nextEmlsrLinks);
928 m_nextEmlsrLinks.reset();
929
930 // Make other non-AP STAs operating on the corresponding EMLSR links transition to
931 // active mode or passive mode (depending on whether EMLSR mode has been enabled or disabled)
932 m_staMac->NotifyEmlsrModeChanged(m_emlsrLinks);
933 // Enforce the limit on the max channel width supported by aux PHYs
934 ApplyMaxChannelWidthAndModClassOnAuxPhys();
935
936 NotifyEmlsrModeChanged();
937}
938
939void
940EmlsrManager::ApplyMaxChannelWidthAndModClassOnAuxPhys()
941{
942 NS_LOG_FUNCTION(this);
943 auto currMainPhyLinkId = m_staMac->GetLinkForPhy(m_mainPhyId);
944 NS_ASSERT(currMainPhyLinkId);
945
946 for (const auto linkId : m_staMac->GetLinkIds())
947 {
948 auto auxPhy = m_staMac->GetWifiPhy(linkId);
949 auto channel = GetChannelForAuxPhy(linkId);
950
951 if (linkId == currMainPhyLinkId || !m_staMac->IsEmlsrLink(linkId) ||
952 auxPhy->GetOperatingChannel() == channel)
953 {
954 continue;
955 }
956
957 auxPhy->SetMaxModulationClassSupported(m_auxPhyMaxModClass);
958
959 NS_LOG_DEBUG("Aux PHY (" << auxPhy << ") is about to switch to " << channel
960 << " to operate on link " << +linkId);
961 // We cannot simply set the new channel, because otherwise the MAC will disable
962 // the setup link. We need to inform the MAC (via the Channel Access Manager) that
963 // this channel switch must not have such a consequence. We already have a method
964 // for doing so, i.e., inform the MAC that the PHY is switching channel to operate
965 // on the "same" link.
966 auto cam = m_staMac->GetChannelAccessManager(linkId);
967 cam->NotifySwitchingEmlsrLink(auxPhy, channel, linkId);
968
969 auxPhy->SetOperatingChannel(channel);
970
971 // the way the ChannelAccessManager handles EMLSR link switch implies that a PHY listener
972 // is removed when the channel switch starts and another one is attached when the channel
973 // switch ends. In the meantime, no PHY is connected to the ChannelAccessManager. Thus,
974 // reset all backoffs (so that access timeout is also cancelled) when the channel switch
975 // starts and request channel access (if needed) when the channel switch ends.
976 cam->ResetAllBackoffs();
977 Simulator::Schedule(auxPhy->GetChannelSwitchDelay(), [=, this]() {
978 for (const auto& [acIndex, ac] : wifiAcList)
979 {
980 m_staMac->GetQosTxop(acIndex)->StartAccessAfterEvent(
981 linkId,
982 Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT,
983 Txop::CHECK_MEDIUM_BUSY);
984 }
985 });
986 }
987}
988
989void
990EmlsrManager::ComputeOperatingChannels()
991{
992 NS_LOG_FUNCTION(this);
993
994 m_mainPhyChannels.clear();
995 m_auxPhyChannels.clear();
996
997 auto linkIds = m_staMac->GetSetupLinkIds();
998
999 for (auto linkId : linkIds)
1000 {
1001 const auto& channel = m_staMac->GetWifiPhy(linkId)->GetOperatingChannel();
1002 m_mainPhyChannels.emplace(linkId, channel);
1003
1004 auto mainPhyChWidth = channel.GetWidth();
1005 auto auxPhyMaxWidth =
1006 std::min(m_auxPhyMaxWidth, GetMaximumChannelWidth(m_auxPhyMaxModClass));
1007 if (auxPhyMaxWidth >= mainPhyChWidth)
1008 {
1009 // same channel can be used by aux PHYs
1010 m_auxPhyChannels.emplace(linkId, channel);
1011 continue;
1012 }
1013 // aux PHYs will operate on a primary subchannel
1014 auto freq = channel.GetPrimaryChannelCenterFrequency(auxPhyMaxWidth);
1015 auto chIt = WifiPhyOperatingChannel::FindFirst(0,
1016 freq,
1017 auxPhyMaxWidth,
1019 channel.GetPhyBand());
1020 NS_ASSERT_MSG(chIt != WifiPhyOperatingChannel::m_frequencyChannels.end(),
1021 "Primary" << auxPhyMaxWidth << " channel not found");
1022 m_auxPhyChannels.emplace(linkId, chIt);
1023 // find the P20 index for the channel used by the aux PHYs
1024 auto p20Index = channel.GetPrimaryChannelIndex(20);
1025 while (mainPhyChWidth > auxPhyMaxWidth)
1026 {
1027 mainPhyChWidth /= 2;
1028 p20Index /= 2;
1029 }
1030 m_auxPhyChannels[linkId].SetPrimary20Index(p20Index);
1031 }
1032}
1033
1035EmlsrManager::GetChannelForMainPhy(uint8_t linkId) const
1036{
1037 auto it = m_mainPhyChannels.find(linkId);
1038 NS_ASSERT_MSG(it != m_mainPhyChannels.end(),
1039 "Channel for main PHY on link ID " << +linkId << " not found");
1040 return it->second;
1041}
1042
1044EmlsrManager::GetChannelForAuxPhy(uint8_t linkId) const
1045{
1046 auto it = m_auxPhyChannels.find(linkId);
1047 NS_ASSERT_MSG(it != m_auxPhyChannels.end(),
1048 "Channel for aux PHY on link ID " << +linkId << " not found");
1049 return it->second;
1050}
1051
1052} // namespace ns3
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 NotifyTxopEnd(uint8_t linkId, bool ulTxopNotStarted=false, bool ongoingDlTxop=false)
Notify the end of a TXOP on the given link.
std::map< uint8_t, EventId > m_ulMainPhySwitch
link ID-indexed map of timers started when an aux PHY gains an UL TXOP and schedules a channel switch...
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...
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
bool IsPending() const
This method is syntactic sugar for !IsExpired().
Definition: event-id.cc:76
EventImpl * PeekEventImpl() const
Definition: event-id.cc:89
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:162
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:322
bool TraceDisconnectWithoutContext(std::string name, const CallbackBase &cb)
Disconnect from a TraceSource a Callback previously connected without a context.
Definition: object-base.cc:352
A base class which provides memory management and object aggregation.
Definition: object.h:89
virtual void DoDispose()
Destructor implementation.
Definition: object.cc:444
bool IsInitialized() const
Check if the object has been initialized.
Definition: object.cc:251
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:77
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
void UnblockTxOnLink(std::set< uint8_t > linkIds, WifiQueueBlockedReason reason)
Unblock transmissions on the given links for the given reason.
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:932
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:981
Mac48Address GetBssid(uint8_t linkId) const
Definition: wifi-mac.cc:522
TypeOfStation GetTypeOfStation() const
Return the type of station.
Definition: wifi-mac.cc:466
uint8_t GetNLinks() const
Get the number of links (can be greater than 1 for 11be devices only).
Definition: wifi-mac.cc:1072
Ptr< WifiPhy > GetWifiPhy(uint8_t linkId=SINGLE_LINK_OP_ID) const
Definition: wifi-mac.cc:1315
Ptr< EhtConfiguration > GetEhtConfiguration() const
Definition: wifi-mac.cc:1897
Ptr< WifiNetDevice > GetDevice() const
Return the device this PHY is associated with.
Definition: wifi-mac.cc:483
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:1113
const std::set< uint8_t > & GetLinkIds() const
Definition: wifi-mac.cc:1078
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:1009
Ptr< WifiPhy > GetPhy() const
Time GetChannelSwitchDelay() const
Definition: wifi-phy.cc:723
Ptr< WifiPhyStateHelper > GetState() const
Return the WifiPhyStateHelper of this PHY.
Definition: wifi-phy.cc:461
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:1343
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1319
WifiMacDropReason
The reason why an MPDU was dropped.
Definition: wifi-mac.h:80
@ STA
Definition: wifi-mac.h:68
@ 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
static constexpr uint8_t DEFAULT_MSD_MAX_N_TXOPS
default MediumSyncDelay max number of TXOP attempts
@ TX
The PHY layer is sending a packet.
uint16_t GetMaximumChannelWidth(WifiModulationClass modulation)
Get the maximum channel width in MHz allowed for the given modulation class.
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Definition: nstime.h:1407
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:700
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
Definition: uinteger.h:46
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:189
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
@ LOG_FUNCTION
Function tracing for non-trivial function calls.
Definition: log.h:106
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Definition: boolean.h:81
static constexpr uint16_t DEFAULT_MSD_DURATION_USEC
default MediumSyncDelay timer duration (max PPDU TX time rounded to a multiple of 32 us)
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Definition: nstime.h:1427
ns3::Time timeout
uint8_t emlsrParamUpdateCtrl
EMLSR Parameter Update Control.
EMLSR Parameter Update field.