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 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Author: Stefano Avallone <stavallo@unina.it>
7 */
8
9#include "emlsr-manager.h"
10
11#include "eht-configuration.h"
13
14#include "ns3/abort.h"
15#include "ns3/assert.h"
16#include "ns3/attribute-container.h"
17#include "ns3/log.h"
18#include "ns3/mgt-action-headers.h"
19#include "ns3/wifi-mpdu.h"
20#include "ns3/wifi-net-device.h"
21#include "ns3/wifi-phy-state-helper.h"
22
23#include <iterator>
24#include <sstream>
25
26namespace ns3
27{
28
29NS_LOG_COMPONENT_DEFINE("EmlsrManager");
30
31NS_OBJECT_ENSURE_REGISTERED(EmlsrManager);
32
33TypeId
35{
36 static TypeId tid =
37 TypeId("ns3::EmlsrManager")
39 .SetGroupName("Wifi")
40 .AddAttribute("EmlsrPaddingDelay",
41 "The EMLSR Paddind Delay (not used by AP MLDs). "
42 "Possible values are 0 us, 32 us, 64 us, 128 us or 256 us.",
46 .AddAttribute("EmlsrTransitionDelay",
47 "The EMLSR Transition Delay (not used by AP MLDs). "
48 "Possible values are 0 us, 16 us, 32 us, 64 us, 128 us or 256 us.",
52 .AddAttribute(
53 "MainPhyId",
54 "The ID of the main PHY (position in the vector of PHYs held by "
55 "WifiNetDevice). This attribute cannot be set after construction.",
56 TypeId::ATTR_GET | TypeId::ATTR_CONSTRUCT, // prevent setting after construction
60 .AddAttribute("AuxPhyChannelWidth",
61 "The maximum channel width (MHz) supported by Aux PHYs. Note that the "
62 "maximum channel width is capped to the maximum channel width supported "
63 "by the configured maximum modulation class supported.",
65 TypeId::ATTR_CONSTRUCT, // prevent setting after construction
66 UintegerValue(20),
69 .AddAttribute("AuxPhyMaxModClass",
70 "The maximum modulation class supported by Aux PHYs. Use "
71 "WIFI_MOD_CLASS_OFDM for non-HT.",
73 TypeId::ATTR_CONSTRUCT, // prevent setting after construction
77 "HR-DSSS",
79 "ERP-OFDM",
81 "OFDM",
83 "HT",
85 "VHT",
87 "HE",
89 "EHT"))
90 .AddAttribute("AuxPhyTxCapable",
91 "Whether Aux PHYs are capable of transmitting PPDUs.",
92 BooleanValue(true),
96 .AddAttribute("InDeviceInterference",
97 "Whether in-device interference is such that a PHY cannot decode "
98 "anything and cannot decrease the backoff counter when another PHY "
99 "of the same device is transmitting.",
100 BooleanValue(false),
104 .AddAttribute("PutAuxPhyToSleep",
105 "Whether Aux PHYs should be put into sleep mode while the Main PHY "
106 "is carrying out a (DL or UL) TXOP. Specifically, for DL TXOPs, aux "
107 "PHYs are put to sleep after receiving the ICF; for UL TXOPs, aux PHYs "
108 "are put to sleep when the CTS frame is received, if RTS/CTS is used, "
109 "or when the transmission of the data frame starts, otherwise. "
110 "Aux PHYs are resumed from sleep when the TXOP ends.",
111 BooleanValue(false),
114 .AddAttribute(
115 "EmlsrLinkSet",
116 "IDs of the links on which EMLSR mode will be enabled. An empty set "
117 "indicates to disable EMLSR.",
121 .AddAttribute("ResetCamState",
122 "Whether to reset the state of the ChannelAccessManager associated with "
123 "the link on which the main PHY has just switched to.",
124 BooleanValue(false),
128 .AddTraceSource("MainPhySwitch",
129 "This trace source is fired when the main PHY switches channel to "
130 "operate on another link. Information associated with the main PHY "
131 "switch is provided through a struct that is inherited from struct "
132 "EmlsrMainPhySwitchTrace (use the GetName() method to get the type "
133 "of the provided object).",
135 "ns3::EmlsrManager::MainPhySwitchCallback");
136 return tid;
137}
138
140 // The STA initializes dot11MSDTimerDuration to aPPDUMaxTime defined in Table 36-70
141 // (Sec. 35.3.16.8.1 of 802.11be D3.1)
142 : m_mediumSyncDuration(MicroSeconds(DEFAULT_MSD_DURATION_USEC)),
143 // The default value of dot11MSDOFDMEDthreshold is –72 dBm and the default value of
144 // dot11MSDTXOPMax is 1, respectively (Sec. 35.3.16.8.1 of 802.11be D3.1)
145 m_msdOfdmEdThreshold(DEFAULT_MSD_OFDM_ED_THRESH),
146 m_msdMaxNTxops(DEFAULT_MSD_MAX_N_TXOPS)
147{
148 NS_LOG_FUNCTION(this);
149}
150
155
156void
158{
159 NS_LOG_FUNCTION(this);
160 m_staMac->TraceDisconnectWithoutContext("AckedMpdu", MakeCallback(&EmlsrManager::TxOk, this));
161 m_staMac->TraceDisconnectWithoutContext("DroppedMpdu",
163 m_staMac = nullptr;
165 for (auto& [id, status] : m_mediumSyncDelayStatus)
166 {
167 status.timer.Cancel();
168 }
170}
171
172void
174{
175 NS_LOG_FUNCTION(this << mac);
176 NS_ASSERT(mac);
177 m_staMac = mac;
178
179 NS_ABORT_MSG_IF(!m_staMac->GetEhtConfiguration(), "EmlsrManager requires EHT support");
180 NS_ABORT_MSG_IF(m_staMac->GetNLinks() <= 1, "EmlsrManager can only be installed on MLDs");
181 NS_ABORT_MSG_IF(m_staMac->GetTypeOfStation() != STA,
182 "EmlsrManager can only be installed on non-AP MLDs");
183
184 m_staMac->TraceConnectWithoutContext("AckedMpdu", MakeCallback(&EmlsrManager::TxOk, this));
185 m_staMac->TraceConnectWithoutContext("DroppedMpdu",
187 m_staMac->TraceConnectWithoutContext(
188 "EmlsrLinkSwitch",
190 DoSetWifiMac(mac);
191}
192
193void
198
199void
201{
202 NS_LOG_FUNCTION(this << linkId << phy);
203
204 if (!phy)
205 {
206 NS_ASSERT(!m_noPhySince.contains(linkId));
207 NS_LOG_DEBUG("Record that no PHY is operating on link " << +linkId);
208 m_noPhySince[linkId] = Simulator::Now();
209 return;
210 }
211
212 // phy switched to operate on the link with ID equal to linkId
213 auto it = m_noPhySince.find(linkId);
214
215 if (it == m_noPhySince.end())
216 {
217 // phy switched to a link on which another PHY was operating, do nothing
218 return;
219 }
220
221 auto duration = Simulator::Now() - it->second;
222 NS_ASSERT_MSG(duration.IsPositive(), "Interval duration should not be negative");
223
224 NS_LOG_DEBUG("PHY " << +phy->GetPhyId() << " switched to link " << +linkId << " after "
225 << duration.As(Time::US)
226 << " since last time a PHY was operating on this link");
228 {
230 }
231
232 m_noPhySince.erase(it);
233}
234
235void
237{
238 NS_LOG_FUNCTION(this << mainPhyId);
239 NS_ABORT_MSG_IF(IsInitialized(), "Cannot be called once this object has been initialized");
240 m_mainPhyId = mainPhyId;
241}
242
243uint8_t
245{
246 return m_mainPhyId;
247}
248
249void
251{
252 m_resetCamState = enable;
253}
254
255bool
260
261void
263{
264 m_auxPhyTxCapable = capable;
265}
266
267bool
272
273void
278
279bool
284
285const std::set<uint8_t>&
287{
288 return m_emlsrLinks;
289}
290
293{
294 return m_staMac;
295}
296
298EmlsrManager::GetEhtFem(uint8_t linkId) const
299{
300 return StaticCast<EhtFrameExchangeManager>(m_staMac->GetFrameExchangeManager(linkId));
301}
302
303std::optional<Time>
305{
306 if (const auto statusIt = m_mediumSyncDelayStatus.find(linkId);
307 statusIt != m_mediumSyncDelayStatus.cend() && statusIt->second.timer.IsPending())
308 {
309 return m_mediumSyncDuration - Simulator::GetDelayLeft(statusIt->second.timer);
310 }
311 return std::nullopt;
312}
313
314void
320
321std::optional<Time>
326
327void
329{
330 NS_LOG_FUNCTION(this << duration.As(Time::US));
331 m_mediumSyncDuration = duration;
332}
333
334Time
339
340void
342{
343 NS_LOG_FUNCTION(this << threshold);
344 m_msdOfdmEdThreshold = threshold;
345}
346
347int8_t
352
353void
354EmlsrManager::SetMediumSyncMaxNTxops(std::optional<uint8_t> nTxops)
355{
356 NS_LOG_FUNCTION(this << nTxops.has_value());
357 m_msdMaxNTxops = nTxops;
358}
359
360std::optional<uint8_t>
365
366void
367EmlsrManager::SetEmlsrLinks(const std::set<uint8_t>& linkIds)
368{
369 std::stringstream ss;
370 if (g_log.IsEnabled(ns3::LOG_FUNCTION))
371 {
372 std::copy(linkIds.cbegin(), linkIds.cend(), std::ostream_iterator<uint16_t>(ss, " "));
373 }
374 NS_LOG_FUNCTION(this << ss.str());
375 NS_ABORT_MSG_IF(linkIds.size() == 1, "Cannot enable EMLSR mode on a single link");
376
377 if (linkIds != m_emlsrLinks)
378 {
379 m_nextEmlsrLinks = linkIds;
380 }
381
382 if (GetStaMac() && GetStaMac()->IsAssociated() && GetTransitionTimeout() && m_nextEmlsrLinks)
383 {
384 // Request to enable EMLSR mode on the given links, provided that they have been setup
385 SendEmlOmn();
386 }
387}
388
389void
391{
392 NS_LOG_FUNCTION(this << *mpdu << linkId);
393
394 const auto& hdr = mpdu->GetHeader();
395
396 DoNotifyMgtFrameReceived(mpdu, linkId);
397
398 if (hdr.IsAssocResp() && GetStaMac()->IsAssociated() && GetTransitionTimeout())
399 {
400 // we just completed ML setup with an AP MLD that supports EMLSR
402
403 if (m_nextEmlsrLinks && !m_nextEmlsrLinks->empty())
404 {
405 // a non-empty set of EMLSR links have been configured, hence enable EMLSR mode
406 // on those links
407 SendEmlOmn();
408 }
409 }
410
411 if (hdr.IsAction() && hdr.GetAddr2() == m_staMac->GetBssid(linkId))
412 {
413 // this is an action frame sent by an AP of the AP MLD we are associated with
414 auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
415 if (category == WifiActionHeader::PROTECTED_EHT &&
416 action.protectedEhtAction ==
418 {
420 {
421 // no need to wait until the expiration of the transition timeout
424 }
425 }
426 }
427}
428
429void
431{
432 NS_LOG_FUNCTION(this << linkId);
433
434 NS_ASSERT(m_staMac->IsEmlsrLink(linkId));
435
436 // block transmissions and suspend medium access on all other EMLSR links
437 for (auto id : m_staMac->GetLinkIds())
438 {
439 if (id != linkId && m_staMac->IsEmlsrLink(id))
440 {
442 }
443 }
444
445 auto mainPhy = m_staMac->GetDevice()->GetPhy(m_mainPhyId);
446 auto auxPhy = m_staMac->GetWifiPhy(linkId);
447
448 if (m_staMac->GetWifiPhy(linkId) != mainPhy)
449 {
450 // an aux PHY received the ICF
451 SwitchMainPhy(linkId,
452 true, // channel switch should occur instantaneously
456
457 // aux PHY received the ICF but main PHY will send the response
458 auto uid = auxPhy->GetPreviouslyRxPpduUid();
459 mainPhy->SetPreviouslyRxPpduUid(uid);
460 }
461
462 // a DL TXOP started, set all aux PHYs to sleep
463 if (m_auxPhyToSleep)
464 {
466 }
467
468 DoNotifyIcfReceived(linkId);
469}
470
471std::pair<bool, Time>
473{
474 auto phy = m_staMac->GetWifiPhy(linkId);
475 NS_ASSERT_MSG(phy, "No PHY operating on link " << +linkId);
476
477 auto mainPhy = m_staMac->GetDevice()->GetPhy(m_mainPhyId);
478
479 // check possible reasons to give up the TXOP that apply to both main PHY and aux PHYs
480 if (const auto [startTxop, delay] = DoGetDelayUntilAccessRequest(linkId); !startTxop)
481 {
482 return {false, delay};
483 }
484
485 if (phy == mainPhy)
486 {
487 // no more constraints to check if medium was gained by main PHY
488 return {true, Time{0}};
489 }
490
491 // an aux PHY is operating on the given link; call the appropriate method depending on
492 // whether the aux PHY is TX capable or not
494 {
496 // if the aux PHY is not TX capable, we don't have to request channel access: if the main
497 // PHY switches link, the UL TXOP will be started; if the main PHY does not switch, it is
498 // because it is going to start an UL TXOP on another link and this link will be restarted
499 // at the end of that UL TXOP when this link will be unblocked
500 NS_LOG_DEBUG("Aux PHY is not capable of transmitting a PPDU");
501 return {false, Time{0}};
502 }
503
505}
506
507void
509{
510 NS_LOG_FUNCTION(this << linkId);
511
512 if (!m_staMac->IsEmlsrLink(linkId))
513 {
514 NS_LOG_DEBUG("EMLSR is not enabled on link " << +linkId);
515 return;
516 }
517
518 // block transmissions and suspend medium access on all other EMLSR links
519 for (auto id : m_staMac->GetLinkIds())
520 {
521 if (id != linkId && m_staMac->IsEmlsrLink(id))
522 {
524 }
525 }
526
527 DoNotifyUlTxopStart(linkId);
528}
529
530void
532{
533 NS_LOG_FUNCTION(this << *rts << txVector);
534}
535
536void
538{
539 NS_LOG_FUNCTION(this << linkId);
540
541 if (m_auxPhyToSleep && m_staMac->IsEmlsrLink(linkId))
542 {
543 if (auto mainPhy = m_staMac->GetDevice()->GetPhy(m_mainPhyId); mainPhy->IsStateSwitching())
544 {
545 // main PHY is switching to this link to take over the UL TXOP. Postpone aux PHY
546 // sleeping until after the main PHY has completed switching
547 Simulator::Schedule(mainPhy->GetDelayUntilIdle() + TimeStep(1),
549 this,
550 true);
551 }
552 else
553 {
554 // put aux PHYs to sleep
556 }
557 }
558}
559
560void
561EmlsrManager::NotifyTxopEnd(uint8_t linkId, bool ulTxopNotStarted, bool ongoingDlTxop)
562{
563 NS_LOG_FUNCTION(this << linkId << ulTxopNotStarted << ongoingDlTxop);
564
565 if (!m_staMac->IsEmlsrLink(linkId))
566 {
567 NS_LOG_DEBUG("EMLSR is not enabled on link " << +linkId);
568 return;
569 }
570
571 // If the main PHY has been scheduled to switch to this link, cancel the channel switch.
572 // This happens, e.g., when an aux PHY sent an RTS to start an UL TXOP but it did not
573 // receive a CTS response.
574 if (auto it = m_ulMainPhySwitch.find(linkId); it != m_ulMainPhySwitch.end())
575 {
576 if (it->second.IsPending())
577 {
578 NS_LOG_DEBUG("Cancelling main PHY channel switch event on link " << +linkId);
579 it->second.Cancel();
580 }
581 m_ulMainPhySwitch.erase(it);
582 }
583
584 // Unblock the other EMLSR links and start the MediumSyncDelay timer, provided that the TXOP
585 // included the transmission of at least a frame and there is no ongoing DL TXOP on this link.
586 // Indeed, the UL TXOP may have ended because the transmission of a frame failed and the
587 // corresponding TX timeout (leading to this call) may have occurred after the reception on
588 // this link of an ICF starting a DL TXOP. If the EMLSR Manager unblocked the other EMLSR
589 // links, another TXOP could be started on another EMLSR link (possibly leading to a crash)
590 // while the DL TXOP on this link is ongoing.
591 if (ongoingDlTxop)
592 {
593 NS_LOG_DEBUG("DL TXOP ongoing");
594 return;
595 }
596 if (ulTxopNotStarted)
597 {
598 NS_LOG_DEBUG("TXOP did not even start");
599 return;
600 }
601
602 if (m_auxPhyToSleep)
603 {
604 // TXOP ended, resume all aux PHYs from sleep
606 }
607
608 DoNotifyTxopEnd(linkId);
609
610 Simulator::ScheduleNow([=, this]() {
611 // unblock transmissions and resume medium access on other EMLSR links
612 std::set<uint8_t> linkIds;
613 for (auto id : m_staMac->GetLinkIds())
614 {
615 if ((id != linkId) && m_staMac->IsEmlsrLink(id))
616 {
617 linkIds.insert(id);
618 }
619 }
621 });
622}
623
624void
626{
627 NS_LOG_FUNCTION(this << linkId << duration.As(Time::US));
629
630 // The STA may choose not to (re)start the MediumSyncDelay timer if the transmission duration
631 // is less than or equal to aMediumSyncThreshold. (Sec. 35.3.16.8.1 802.11be D5.1)
633 {
634 return;
635 }
636
637 // iterate over all the other EMLSR links
638 for (auto id : m_staMac->GetLinkIds())
639 {
640 if (id != linkId && m_staMac->IsEmlsrLink(id))
641 {
643 }
644 }
645}
646
647void
649{
650 NS_LOG_FUNCTION(this << phy << linkId);
651
652 // if a MediumSyncDelay timer is running for the link on which the main PHY is going to
653 // operate, set the CCA ED threshold to the MediumSyncDelay OFDM ED threshold
654 if (auto statusIt = m_mediumSyncDelayStatus.find(linkId);
655 statusIt != m_mediumSyncDelayStatus.cend() && statusIt->second.timer.IsPending())
656 {
657 NS_LOG_DEBUG("Setting CCA ED threshold of PHY " << phy << " to " << +m_msdOfdmEdThreshold
658 << " on link " << +linkId);
659
660 // store the current CCA ED threshold in the m_prevCcaEdThreshold map, if not present
661 m_prevCcaEdThreshold.try_emplace(phy, phy->GetCcaEdThreshold());
662
663 phy->SetCcaEdThreshold(m_msdOfdmEdThreshold);
664 }
665 // otherwise, restore the previous value for the CCA ED threshold (if any)
666 else if (auto threshIt = m_prevCcaEdThreshold.find(phy);
667 threshIt != m_prevCcaEdThreshold.cend())
668 {
669 NS_LOG_DEBUG("Resetting CCA ED threshold of PHY " << phy << " to " << threshIt->second
670 << " on link " << +linkId);
671 phy->SetCcaEdThreshold(threshIt->second);
672 m_prevCcaEdThreshold.erase(threshIt);
673 }
674}
675
676void
678 bool noSwitchDelay,
679 bool resetBackoff,
680 bool requestAccess,
681 EmlsrMainPhySwitchTrace&& traceInfo)
682{
683 NS_LOG_FUNCTION(this << linkId << noSwitchDelay << resetBackoff << requestAccess
684 << traceInfo.GetName());
685
686 auto mainPhy = m_staMac->GetDevice()->GetPhy(m_mainPhyId);
687
688 NS_ASSERT_MSG(mainPhy != m_staMac->GetWifiPhy(linkId),
689 "Main PHY is already operating on link " << +linkId);
690
691 // find the link on which the main PHY is operating
692 auto currMainPhyLinkId = m_staMac->GetLinkForPhy(mainPhy);
693 traceInfo.fromLinkId = currMainPhyLinkId;
694 traceInfo.toLinkId = linkId;
695 m_mainPhySwitchTrace(traceInfo);
696
697 NS_ASSERT_MSG(currMainPhyLinkId.has_value() || mainPhy->IsStateSwitching(),
698 "If the main PHY is not operating on a link, it must be switching");
699
700 const auto newMainPhyChannel = GetChannelForMainPhy(linkId);
701
702 NS_LOG_DEBUG("Main PHY (" << mainPhy << ") is about to switch to " << newMainPhyChannel
703 << " to operate on link " << +linkId);
704
705 // if the main PHY is operating on a link, notify the channel access manager of the upcoming
706 // channel switch
707 if (currMainPhyLinkId.has_value())
708 {
709 m_staMac->GetChannelAccessManager(*currMainPhyLinkId)
710 ->NotifySwitchingEmlsrLink(mainPhy, newMainPhyChannel, linkId);
711 }
712
713 // this assert also ensures that the actual channel switch is not delayed
714 NS_ASSERT_MSG(!mainPhy->GetState()->IsStateTx(),
715 "We should not ask the main PHY to switch channel while transmitting");
716
717 // record the aux PHY operating on the link the main PHY is switching to
718 auto auxPhy = GetStaMac()->GetWifiPhy(linkId);
719
720 // request the main PHY to switch channel
721 const auto delay = mainPhy->GetChannelSwitchDelay();
722 const auto pifs = mainPhy->GetSifs() + mainPhy->GetSlot();
723 NS_ASSERT_MSG(noSwitchDelay || delay <= std::max(m_lastAdvTransitionDelay, pifs),
724 "Channel switch delay ("
725 << delay.As(Time::US)
726 << ") should be shorter than the maximum between the Transition delay ("
727 << m_lastAdvTransitionDelay.As(Time::US) << ") and a PIFS ("
728 << pifs.As(Time::US) << ")");
729 if (noSwitchDelay)
730 {
731 mainPhy->SetAttribute("ChannelSwitchDelay", TimeValue(Seconds(0)));
732 }
733 mainPhy->SetOperatingChannel(newMainPhyChannel);
734 // restore previous channel switch delay
735 if (noSwitchDelay)
736 {
737 mainPhy->SetAttribute("ChannelSwitchDelay", TimeValue(delay));
738 }
739 // re-enable short time slot, if needed
740 if (m_staMac->GetWifiRemoteStationManager(linkId)->GetShortSlotTimeEnabled())
741 {
742 mainPhy->SetSlot(MicroSeconds(9));
743 }
744
745 const auto timeToSwitchEnd = noSwitchDelay ? Seconds(0) : delay;
746
747 // if the main PHY is not operating on any link (because it was switching), it is not connected
748 // to a channel access manager, hence we must notify the MAC of the new link switch
749 if (!currMainPhyLinkId.has_value())
750 {
751 m_staMac->NotifySwitchingEmlsrLink(mainPhy, linkId, timeToSwitchEnd);
752 }
753
754 if (resetBackoff && currMainPhyLinkId.has_value())
755 {
756 // reset the backoffs on the link left by the main PHY
757 m_staMac->GetChannelAccessManager(*currMainPhyLinkId)->ResetAllBackoffs();
758 }
759
760 if (requestAccess)
761 {
762 // schedule channel access request on the new link when switch is completed
763 Simulator::Schedule(timeToSwitchEnd, [=, this]() {
764 for (const auto& [acIndex, ac] : wifiAcList)
765 {
766 m_staMac->GetQosTxop(acIndex)->StartAccessAfterEvent(
767 linkId,
770 }
771 });
772 }
773
774 SetCcaEdThresholdOnLinkSwitch(mainPhy, linkId);
775 NotifyMainPhySwitch(currMainPhyLinkId, linkId, auxPhy, timeToSwitchEnd);
776}
777
778void
779EmlsrManager::SwitchAuxPhy(Ptr<WifiPhy> auxPhy, uint8_t currLinkId, uint8_t nextLinkId)
780{
781 NS_LOG_FUNCTION(this << auxPhy << currLinkId << nextLinkId);
782
783 auto newAuxPhyChannel = GetChannelForAuxPhy(nextLinkId);
784
785 NS_LOG_DEBUG("Aux PHY (" << auxPhy << ") is about to switch to " << newAuxPhyChannel
786 << " to operate on link " << +nextLinkId);
787
788 GetStaMac()
789 ->GetChannelAccessManager(currLinkId)
790 ->NotifySwitchingEmlsrLink(auxPhy, newAuxPhyChannel, nextLinkId);
791
792 auxPhy->SetOperatingChannel(newAuxPhyChannel);
793 // re-enable short time slot, if needed
794 if (m_staMac->GetWifiRemoteStationManager(nextLinkId)->GetShortSlotTimeEnabled())
795 {
796 auxPhy->SetSlot(MicroSeconds(9));
797 }
798
799 // schedule channel access request on the new link when switch is completed
800 Simulator::Schedule(auxPhy->GetChannelSwitchDelay(), [=, this]() {
801 for (const auto& [acIndex, ac] : wifiAcList)
802 {
803 m_staMac->GetQosTxop(acIndex)->StartAccessAfterEvent(
804 nextLinkId,
805 Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT,
806 Txop::CHECK_MEDIUM_BUSY);
807 }
808 });
809
810 SetCcaEdThresholdOnLinkSwitch(auxPhy, nextLinkId);
811}
812
813void
814EmlsrManager::StartMediumSyncDelayTimer(uint8_t linkId)
815{
816 NS_LOG_FUNCTION(this << linkId);
817
818 if (m_mediumSyncDuration.IsZero())
819 {
820 NS_LOG_DEBUG("MediumSyncDuration is zero");
821 return;
822 }
823
824 auto phy = m_staMac->GetWifiPhy(linkId);
825
826 if (!phy)
827 {
828 NS_LOG_DEBUG("No PHY operating on link " << +linkId);
829 // MSD timer will be started when a PHY will be operating on this link
830 return;
831 }
832
833 const auto [it, inserted] = m_mediumSyncDelayStatus.try_emplace(linkId);
834
835 // reset the max number of TXOP attempts
836 it->second.msdNTxopsLeft = m_msdMaxNTxops;
837
838 if (!it->second.timer.IsPending())
839 {
840 NS_LOG_DEBUG("Setting CCA ED threshold on link "
841 << +linkId << " to " << +m_msdOfdmEdThreshold << " PHY " << phy);
842 m_prevCcaEdThreshold[phy] = phy->GetCcaEdThreshold();
843 phy->SetCcaEdThreshold(m_msdOfdmEdThreshold);
844 }
845
846 // (re)start the timer
847 it->second.timer.Cancel();
848 NS_LOG_DEBUG("Starting MediumSyncDelay timer for " << m_mediumSyncDuration.As(Time::US)
849 << " on link " << +linkId);
850 it->second.timer = Simulator::Schedule(m_mediumSyncDuration,
851 &EmlsrManager::MediumSyncDelayTimerExpired,
852 this,
853 linkId);
854}
855
856void
857EmlsrManager::CancelMediumSyncDelayTimer(uint8_t linkId)
858{
859 NS_LOG_FUNCTION(this << linkId);
860
861 auto timerIt = m_mediumSyncDelayStatus.find(linkId);
862
863 NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && timerIt->second.timer.IsPending());
864
865 timerIt->second.timer.Cancel();
866 MediumSyncDelayTimerExpired(linkId);
867}
868
869void
870EmlsrManager::MediumSyncDelayTimerExpired(uint8_t linkId)
871{
872 NS_LOG_FUNCTION(this << linkId);
873
874 auto timerIt = m_mediumSyncDelayStatus.find(linkId);
875
876 NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && !timerIt->second.timer.IsPending());
877
878 // reset the MSD OFDM ED threshold
879 auto phy = m_staMac->GetWifiPhy(linkId);
880
881 if (!phy)
882 {
883 // no PHY is operating on this link. This may happen when a MediumSyncDelay timer expires
884 // on the link left "uncovered" by the main PHY that is operating on another link (and the
885 // aux PHY of that link did not switch). In this case, do nothing, since the CCA ED
886 // threshold on the main PHY will be restored once the main PHY switches back to its link
887 return;
888 }
889
890 auto threshIt = m_prevCcaEdThreshold.find(phy);
891 NS_ASSERT_MSG(threshIt != m_prevCcaEdThreshold.cend(),
892 "No value to restore for CCA ED threshold on PHY " << phy);
893 NS_LOG_DEBUG("Resetting CCA ED threshold of PHY " << phy << " to " << threshIt->second
894 << " on link " << +linkId);
895 phy->SetCcaEdThreshold(threshIt->second);
896 m_prevCcaEdThreshold.erase(threshIt);
897}
898
899void
900EmlsrManager::DecrementMediumSyncDelayNTxops(uint8_t linkId)
901{
902 NS_LOG_FUNCTION(this << linkId);
903
904 const auto timerIt = m_mediumSyncDelayStatus.find(linkId);
905
906 NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && timerIt->second.timer.IsPending());
907 NS_ASSERT(timerIt->second.msdNTxopsLeft != 0);
908
909 if (timerIt->second.msdNTxopsLeft)
910 {
911 --timerIt->second.msdNTxopsLeft.value();
912 }
913}
914
915void
916EmlsrManager::ResetMediumSyncDelayNTxops(uint8_t linkId)
917{
918 NS_LOG_FUNCTION(this << linkId);
919
920 auto timerIt = m_mediumSyncDelayStatus.find(linkId);
921
922 NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && timerIt->second.timer.IsPending());
923 timerIt->second.msdNTxopsLeft.reset();
924}
925
926bool
927EmlsrManager::MediumSyncDelayNTxopsExceeded(uint8_t linkId)
928{
929 NS_LOG_FUNCTION(this << linkId);
930
931 auto timerIt = m_mediumSyncDelayStatus.find(linkId);
932
933 NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && timerIt->second.timer.IsPending());
934 return timerIt->second.msdNTxopsLeft == 0;
935}
936
938EmlsrManager::GetEmlOmn()
939{
940 MgtEmlOmn frame;
941
942 // Add the EMLSR Parameter Update field if needed
943 if (m_lastAdvPaddingDelay != m_emlsrPaddingDelay ||
944 m_lastAdvTransitionDelay != m_emlsrTransitionDelay)
945 {
946 m_lastAdvPaddingDelay = m_emlsrPaddingDelay;
947 m_lastAdvTransitionDelay = m_emlsrTransitionDelay;
950 frame.m_emlsrParamUpdate->paddingDelay =
951 CommonInfoBasicMle::EncodeEmlsrPaddingDelay(m_lastAdvPaddingDelay);
952 frame.m_emlsrParamUpdate->transitionDelay =
953 CommonInfoBasicMle::EncodeEmlsrTransitionDelay(m_lastAdvTransitionDelay);
954 }
955
956 // We must verify that the links included in the given EMLSR link set (if any) have been setup.
957 auto setupLinkIds = m_staMac->GetSetupLinkIds();
958
959 for (auto emlsrLinkIt = m_nextEmlsrLinks->begin(); emlsrLinkIt != m_nextEmlsrLinks->end();)
960 {
961 if (auto setupLinkIt = setupLinkIds.find(*emlsrLinkIt); setupLinkIt != setupLinkIds.cend())
962 {
963 setupLinkIds.erase(setupLinkIt);
964 frame.SetLinkIdInBitmap(*emlsrLinkIt);
965 emlsrLinkIt++;
966 }
967 else
968 {
969 NS_LOG_DEBUG("Link ID " << +(*emlsrLinkIt) << " has not been setup");
970 emlsrLinkIt = m_nextEmlsrLinks->erase(emlsrLinkIt);
971 }
972 }
973
974 // EMLSR Mode is enabled if and only if the set of EMLSR links is not empty
975 frame.m_emlControl.emlsrMode = m_nextEmlsrLinks->empty() ? 0 : 1;
976
977 return frame;
978}
979
980void
981EmlsrManager::SendEmlOmn()
982{
983 NS_LOG_FUNCTION(this);
984
985 NS_ABORT_MSG_IF(!m_emlsrTransitionTimeout,
986 "AP did not advertise a Transition Timeout, cannot send EML notification");
987 NS_ASSERT_MSG(m_nextEmlsrLinks, "Need to set EMLSR links before calling this method");
988
989 // TODO if this is a single radio non-AP MLD and not all setup links are in the EMLSR link
990 // set, we have to put setup links that are not included in the given EMLSR link set (i.e.,
991 // those remaining in setupLinkIds, if m_nextEmlsrLinks is not empty) in the sleep mode:
992 // For the EMLSR mode enabled in a single radio non-AP MLD, the STA(s) affiliated with
993 // the non-AP MLD that operates on the enabled link(s) that corresponds to the bit
994 // position(s) of the EMLSR Link Bitmap subfield set to 0 shall be in doze state if a
995 // non-AP STA affiliated with the non-AP MLD that operates on one of the EMLSR links is
996 // in awake state. (Sec. 35.3.17 of 802.11be D3.0)
997
998 auto frame = GetEmlOmn();
999 auto linkId = GetLinkToSendEmlOmn();
1000 GetEhtFem(linkId)->SendEmlOmn(m_staMac->GetBssid(linkId), frame);
1001}
1002
1003void
1004EmlsrManager::TxOk(Ptr<const WifiMpdu> mpdu)
1005{
1006 NS_LOG_FUNCTION(this << *mpdu);
1007
1008 const auto& hdr = mpdu->GetHeader();
1009
1010 if (hdr.IsAssocReq())
1011 {
1012 // store padding delay and transition delay advertised in AssocReq
1013 MgtAssocRequestHeader assocReq;
1014 mpdu->GetPacket()->PeekHeader(assocReq);
1015 auto& mle = assocReq.Get<MultiLinkElement>();
1016 NS_ASSERT_MSG(mle, "AssocReq should contain a Multi-Link Element");
1017 m_lastAdvPaddingDelay = mle->GetEmlsrPaddingDelay();
1018 m_lastAdvTransitionDelay = mle->GetEmlsrTransitionDelay();
1019 }
1020
1021 if (hdr.IsMgt() && hdr.IsAction())
1022 {
1023 if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
1024 category == WifiActionHeader::PROTECTED_EHT &&
1025 action.protectedEhtAction ==
1026 WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
1027 {
1028 // the EML Operating Mode Notification frame that we sent has been acknowledged.
1029 // Start the transition timeout to wait until the request can be made effective
1030 NS_ASSERT_MSG(m_emlsrTransitionTimeout, "No transition timeout received from AP");
1031 m_transitionTimeoutEvent = Simulator::Schedule(*m_emlsrTransitionTimeout,
1032 &EmlsrManager::ChangeEmlsrMode,
1033 this);
1034 }
1035 }
1036}
1037
1038void
1039EmlsrManager::TxDropped(WifiMacDropReason reason, Ptr<const WifiMpdu> mpdu)
1040{
1041 NS_LOG_FUNCTION(this << reason << *mpdu);
1042
1043 const auto& hdr = mpdu->GetHeader();
1044
1045 if (hdr.IsMgt() && hdr.IsAction())
1046 {
1047 auto pkt = mpdu->GetPacket()->Copy();
1048 if (auto [category, action] = WifiActionHeader::Remove(pkt);
1049 category == WifiActionHeader::PROTECTED_EHT &&
1050 action.protectedEhtAction ==
1051 WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
1052 {
1053 // the EML Operating Mode Notification frame has been dropped. Ask the subclass
1054 // whether the frame needs to be resent
1055 auto linkId = ResendNotification(mpdu);
1056 if (linkId)
1057 {
1058 MgtEmlOmn frame;
1059 pkt->RemoveHeader(frame);
1060 GetEhtFem(*linkId)->SendEmlOmn(m_staMac->GetBssid(*linkId), frame);
1061 }
1062 else
1063 {
1064 m_nextEmlsrLinks.reset();
1065 }
1066 }
1067 }
1068}
1069
1070void
1071EmlsrManager::ChangeEmlsrMode()
1072{
1073 NS_LOG_FUNCTION(this);
1074
1075 // After the successful transmission of the EML Operating Mode Notification frame by the
1076 // non-AP STA affiliated with the non-AP MLD, the non-AP MLD shall operate in the EMLSR mode
1077 // and the other non-AP STAs operating on the corresponding EMLSR links shall transition to
1078 // active mode after the transition delay indicated in the Transition Timeout subfield in the
1079 // EML Capabilities subfield of the Basic Multi-Link element or immediately after receiving an
1080 // EML Operating Mode Notification frame from one of the APs operating on the EMLSR links and
1081 // affiliated with the AP MLD. (Sec. 35.3.17 of 802.11be D3.0)
1082 NS_ASSERT_MSG(m_nextEmlsrLinks, "No set of EMLSR links stored");
1083 m_emlsrLinks.swap(*m_nextEmlsrLinks);
1084 m_nextEmlsrLinks.reset();
1085
1086 // Make other non-AP STAs operating on the corresponding EMLSR links transition to
1087 // active mode or passive mode (depending on whether EMLSR mode has been enabled or disabled)
1088 m_staMac->NotifyEmlsrModeChanged(m_emlsrLinks);
1089 // Enforce the limit on the max channel width supported by aux PHYs
1090 ApplyMaxChannelWidthAndModClassOnAuxPhys();
1091
1092 NotifyEmlsrModeChanged();
1093}
1094
1095void
1096EmlsrManager::ApplyMaxChannelWidthAndModClassOnAuxPhys()
1097{
1098 NS_LOG_FUNCTION(this);
1099 auto currMainPhyLinkId = m_staMac->GetLinkForPhy(m_mainPhyId);
1100 NS_ASSERT(currMainPhyLinkId);
1101
1102 for (const auto linkId : m_staMac->GetLinkIds())
1103 {
1104 auto auxPhy = m_staMac->GetWifiPhy(linkId);
1105 auto channel = GetChannelForAuxPhy(linkId);
1106
1107 if (linkId == currMainPhyLinkId || !m_staMac->IsEmlsrLink(linkId) ||
1108 auxPhy->GetOperatingChannel() == channel)
1109 {
1110 continue;
1111 }
1112
1113 auxPhy->SetMaxModulationClassSupported(m_auxPhyMaxModClass);
1114
1115 NS_LOG_DEBUG("Aux PHY (" << auxPhy << ") is about to switch to " << channel
1116 << " to operate on link " << +linkId);
1117 // We cannot simply set the new channel, because otherwise the MAC will disable
1118 // the setup link. We need to inform the MAC (via the Channel Access Manager) that
1119 // this channel switch must not have such a consequence. We already have a method
1120 // for doing so, i.e., inform the MAC that the PHY is switching channel to operate
1121 // on the "same" link.
1122 auto cam = m_staMac->GetChannelAccessManager(linkId);
1123 cam->NotifySwitchingEmlsrLink(auxPhy, channel, linkId);
1124
1125 // apply channel width limitation assuming an instantaneous channel switch
1126 const auto delay = auxPhy->GetChannelSwitchDelay();
1127 auxPhy->SetAttribute("ChannelSwitchDelay", TimeValue(Time{0}));
1128 auxPhy->SetOperatingChannel(channel);
1129 auxPhy->SetAttribute("ChannelSwitchDelay", TimeValue(delay));
1130
1131 // the way the ChannelAccessManager handles EMLSR link switch implies that a PHY listener
1132 // is removed when the channel switch starts and another one is attached when the channel
1133 // switch ends. In the meantime, no PHY is connected to the ChannelAccessManager. Thus,
1134 // reset all backoffs (so that access timeout is also cancelled) when the channel switch
1135 // starts and request channel access (if needed) when the channel switch ends.
1136 cam->ResetAllBackoffs();
1137 Simulator::ScheduleNow([=, this]() {
1138 for (const auto& [acIndex, ac] : wifiAcList)
1139 {
1140 m_staMac->GetQosTxop(acIndex)->StartAccessAfterEvent(
1141 linkId,
1142 Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT,
1143 Txop::CHECK_MEDIUM_BUSY);
1144 }
1145 });
1146 }
1147}
1148
1149void
1150EmlsrManager::ComputeOperatingChannels()
1151{
1152 NS_LOG_FUNCTION(this);
1153
1154 m_mainPhyChannels.clear();
1155 m_auxPhyChannels.clear();
1156
1157 auto linkIds = m_staMac->GetSetupLinkIds();
1158
1159 for (auto linkId : linkIds)
1160 {
1161 const auto& channel = m_staMac->GetWifiPhy(linkId)->GetOperatingChannel();
1162 m_mainPhyChannels.emplace(linkId, channel);
1163
1164 auto mainPhyChWidth = channel.GetWidth();
1165 auto auxPhyMaxWidth =
1166 std::min(m_auxPhyMaxWidth, GetMaximumChannelWidth(m_auxPhyMaxModClass));
1167 if (auxPhyMaxWidth >= mainPhyChWidth)
1168 {
1169 // same channel can be used by aux PHYs
1170 m_auxPhyChannels.emplace(linkId, channel);
1171 continue;
1172 }
1173 // aux PHYs will operate on a primary subchannel
1174 auto freq = channel.GetPrimaryChannelCenterFrequency(auxPhyMaxWidth);
1175 auto chIt = WifiPhyOperatingChannel::FindFirst(0,
1176 freq,
1177 auxPhyMaxWidth,
1179 channel.GetPhyBand());
1180 NS_ASSERT_MSG(chIt != WifiPhyOperatingChannel::m_frequencyChannels.end(),
1181 "Primary" << auxPhyMaxWidth << " channel not found");
1182 m_auxPhyChannels.emplace(linkId, chIt);
1183 // find the P20 index for the channel used by the aux PHYs
1184 auto p20Index = channel.GetPrimaryChannelIndex(MHz_u{20});
1185 while (mainPhyChWidth > auxPhyMaxWidth)
1186 {
1187 mainPhyChWidth /= 2;
1188 p20Index /= 2;
1189 }
1190 m_auxPhyChannels[linkId].SetPrimary20Index(p20Index);
1191 }
1192}
1193
1195EmlsrManager::GetChannelForMainPhy(uint8_t linkId) const
1196{
1197 auto it = m_mainPhyChannels.find(linkId);
1198 NS_ASSERT_MSG(it != m_mainPhyChannels.end(),
1199 "Channel for main PHY on link ID " << +linkId << " not found");
1200 return it->second;
1201}
1202
1204EmlsrManager::GetChannelForAuxPhy(uint8_t linkId) const
1205{
1206 auto it = m_auxPhyChannels.find(linkId);
1207 NS_ASSERT_MSG(it != m_auxPhyChannels.end(),
1208 "Channel for aux PHY on link ID " << +linkId << " not found");
1209 return it->second;
1210}
1211
1212void
1213EmlsrManager::CancelAllSleepEvents()
1214{
1215 NS_LOG_FUNCTION(this);
1216
1217 for (auto& [id, event] : m_auxPhyToSleepEvents)
1218 {
1219 event.Cancel();
1220 }
1221 m_auxPhyToSleepEvents.clear();
1222}
1223
1224void
1225EmlsrManager::SetSleepStateForAllAuxPhys(bool sleep)
1226{
1227 NS_LOG_FUNCTION(this << sleep);
1228
1229 CancelAllSleepEvents();
1230
1231 for (const auto& phy : m_staMac->GetDevice()->GetPhys())
1232 {
1233 if (phy->GetPhyId() == m_mainPhyId)
1234 {
1235 continue; // do not set sleep mode/resume from sleep the main PHY
1236 }
1237
1238 auto linkId = m_staMac->GetLinkForPhy(phy);
1239
1240 if (linkId.has_value() && !m_staMac->IsEmlsrLink(*linkId))
1241 {
1242 continue; // this PHY is not operating on an EMLSR link
1243 }
1244
1245 if (!sleep)
1246 {
1247 if (!phy->IsStateSleep())
1248 {
1249 continue; // nothing to do
1250 }
1251
1252 NS_LOG_DEBUG("PHY " << +phy->GetPhyId() << ": Resuming from sleep");
1253 phy->ResumeFromSleep();
1254
1255 // if this aux PHY is operating on a link, check if it lost medium sync
1256 if (linkId.has_value())
1257 {
1258 auto it = m_startSleep.find(phy->GetPhyId());
1259 NS_ASSERT_MSG(it != m_startSleep.cend(),
1260 "No start sleep info for PHY ID " << phy->GetPhyId());
1261 const auto sleepDuration = Simulator::Now() - it->second;
1262 m_startSleep.erase(it);
1263
1264 if (sleepDuration > MicroSeconds(MEDIUM_SYNC_THRESHOLD_USEC))
1265 {
1266 StartMediumSyncDelayTimer(*linkId);
1267 }
1268 }
1269
1270 continue; // resuming the PHY from sleep has been handled
1271 }
1272
1273 if (phy->IsStateSleep())
1274 {
1275 continue; // nothing to do
1276 }
1277
1278 // we force WifiPhy::SetSleepMode() to abort RX and switch immediately to sleep mode in
1279 // case the state is RX. If the state is TX or SWITCHING, WifiPhy::SetSleepMode() postpones
1280 // setting sleep mode to end of TX or SWITCHING. This is fine, but we schedule events here
1281 // to correctly record the time an aux PHY was put to sleep and to be able to cancel them
1282 // later if needed
1283 std::stringstream ss;
1284 auto s = std::string("PHY ") + std::to_string(phy->GetPhyId()) + ": Setting sleep mode";
1285 if (phy->IsStateTx() || phy->IsStateSwitching())
1286 {
1287 const auto delay = phy->GetDelayUntilIdle();
1288 NS_LOG_DEBUG(s << " in " << delay.As(Time::US));
1289 m_auxPhyToSleepEvents[phy->GetPhyId()] = Simulator::Schedule(delay, [=, this]() {
1290 phy->SetSleepMode(true);
1291 m_startSleep[phy->GetPhyId()] = Simulator::Now();
1292 });
1293 }
1294 else
1295 {
1296 NS_LOG_DEBUG(s);
1297 phy->SetSleepMode(true);
1298 m_startSleep[phy->GetPhyId()] = Simulator::Now();
1299 }
1300 }
1301}
1302
1303} // namespace ns3
A container for one type of attribute.
AttributeValue implementation for Boolean.
Definition boolean.h:26
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 NotifyProtectionCompleted(uint8_t linkId)
Notify that protection (if required) is completed and data frame exchange can start on the given link...
void SetTransitionTimeout(Time timeout)
Set the Transition Timeout advertised by the associated AP with EMLSR activated.
static constexpr uint16_t MEDIUM_SYNC_THRESHOLD_USEC
The aMediumSyncThreshold defined by Sec. 35.3.16.18.1 of 802.11be D4.0.
bool m_auxPhyTxCapable
whether Aux PHYs are capable of transmitting PPDUs
std::optional< Time > GetTransitionTimeout() const
std::pair< bool, Time > GetDelayUntilAccessRequest(uint8_t linkId, AcIndex aci)
Notify that an UL TXOP is gained on the given link by the given AC.
virtual std::pair< bool, Time > DoGetDelayUntilAccessRequest(uint8_t linkId)=0
Subclasses have to provide an implementation for this method, that is called by the base class when t...
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 NotifyUlTxopStart(uint8_t linkId)
Notify the start of an UL TXOP on the given link.
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...
bool m_inDeviceInterference
whether in-device interference is such that a PHY cannot decode anything and cannot decrease the back...
bool GetCamStateReset() const
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)
bool m_auxPhyToSleep
whether Aux PHYs should be put into sleep mode while the Main PHY is carrying out a (DL or UL) TXOP
virtual std::pair< bool, Time > GetDelayUnlessMainPhyTakesOverUlTxop(uint8_t linkId)=0
Subclasses have to provide an implementation for this method, that is called by the base class when t...
virtual void SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId, AcIndex aci)=0
Subclasses have to provide an implementation for this method, that is called by the base class when t...
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.
bool GetInDeviceInterference() const
virtual void NotifyMainPhySwitch(std::optional< uint8_t > currLinkId, uint8_t nextLinkId, Ptr< WifiPhy > auxPhy, Time duration)=0
Notify subclass that the main PHY is switching channel to operate on another link.
MHz_u m_auxPhyMaxWidth
max channel width supported by aux PHYs
virtual void NotifyInDeviceInterferenceStart(uint8_t linkId, Time duration)
Notify that an STA affiliated with the EMLSR client is causing in-device interference for the given a...
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
MainPhySwitchTracedCallback m_mainPhySwitchTrace
main PHY switch trace source
virtual void EmlsrLinkSwitchCallback(uint8_t linkId, Ptr< WifiPhy > phy)
Callback connected to the EmlsrLinkSwitch trace source of StaWifiMac.
Time m_mediumSyncDuration
duration of the MediumSyncDelay timer
std::optional< Time > m_emlsrTransitionTimeout
Transition timeout advertised by APs with EMLSR activated.
void SwitchAuxPhy(Ptr< WifiPhy > auxPhy, uint8_t currLinkId, uint8_t nextLinkId)
Switch channel on the Aux PHY operating on the given current link so that it operates on the given ne...
std::map< Ptr< WifiPhy >, dBm_u > m_prevCcaEdThreshold
the CCA sensitivity threshold to restore once the MediumSyncDelay timer expires or the PHY moves to a...
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.
virtual void DoSetWifiMac(Ptr< StaWifiMac > mac)
Allow subclasses to take actions when the MAC is set.
std::map< uint8_t, Time > m_noPhySince
link ID-indexed map of the time since no PHY is operating on the 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 SetCcaEdThresholdOnLinkSwitch(Ptr< WifiPhy > phy, uint8_t linkId)
Set the CCA ED threshold (if needed) on the given PHY that is switching channel to operate on the giv...
void SetMainPhyId(uint8_t mainPhyId)
Set the ID of main PHY (position in the vector of PHYs held by WifiNetDevice).
const WifiPhyOperatingChannel & GetChannelForMainPhy(uint8_t linkId) const
~EmlsrManager() override
void SetMediumSyncDuration(Time duration)
Set the duration of the MediumSyncDelay timer.
void SwitchMainPhy(uint8_t linkId, bool noSwitchDelay, bool resetBackoff, bool requestAccess, EmlsrMainPhySwitchTrace &&traceInfo)
Switch channel on the Main PHY so that it operates on the given link.
static constexpr bool RESET_BACKOFF
reset backoff on main PHY switch
Time m_lastAdvTransitionDelay
last advertised transition delay
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.
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...
virtual void NotifyRtsSent(uint8_t linkId, Ptr< const WifiPsdu > rts, const WifiTxVector &txVector)
Notify that RTS transmission is starting on the given link.
EventId m_transitionTimeoutEvent
Timer started after the successful transmission of an EML Operating Mode Notification frame.
virtual void DoNotifyIcfReceived(uint8_t linkId)=0
Notify the subclass of the reception of an initial Control frame on the given link.
void SetInDeviceInterference(bool enable)
Set the member variable indicating whether in-device interference is such that a PHY cannot decode an...
const WifiPhyOperatingChannel & GetChannelForAuxPhy(uint8_t linkId) const
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)
void SetSleepStateForAllAuxPhys(bool sleep)
Set sleep state or awake state for all aux PHYs.
Hold variables of type enum.
Definition enum.h:52
void Cancel()
This method is syntactic sugar for the ns3::Simulator::Cancel method.
Definition event-id.cc:44
bool IsPending() const
This method is syntactic sugar for !IsExpired().
Definition event-id.cc:65
EventImpl * PeekEventImpl() const
Definition event-id.cc:78
void Invoke()
Called by the simulation engine to notify the event that it is time to execute.
Definition event-impl.cc:36
Implement the header for management frames of type association request.
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.
A base class which provides memory management and object aggregation.
Definition object.h:78
virtual void DoDispose()
Destructor implementation.
Definition object.cc:433
bool IsInitialized() const
Check if the object has been initialized.
Definition object.cc:240
Smart pointer class similar to boost::intrusive_ptr.
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition simulator.h:560
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:197
static EventId ScheduleNow(FUNC f, Ts &&... args)
Schedule an event to expire Now.
Definition simulator.h:594
static Time GetDelayLeft(const EventId &id)
Get the remaining time until this event will execute.
Definition simulator.cc:206
Simulation virtual time values and global simulation resolution.
Definition nstime.h:94
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition time.cc:404
@ US
microsecond
Definition nstime.h:107
AttributeValue implementation for Time.
Definition nstime.h:1431
static constexpr bool DIDNT_HAVE_FRAMES_TO_TRANSMIT
no packet available for transmission was in the queue
Definition txop.h:409
static constexpr bool CHECK_MEDIUM_BUSY
generation of backoff (also) depends on the busy/idle state of the medium
Definition txop.h:411
a unique identifier for an interface.
Definition type-id.h:48
@ ATTR_GET
The attribute can be read.
Definition type-id.h:53
@ ATTR_CONSTRUCT
The attribute can be written at construction-time.
Definition type-id.h:55
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition type-id.cc:1001
Hold an unsigned integer type.
Definition uinteger.h:34
static std::pair< CategoryValue, ActionValue > Peek(Ptr< const Packet > pkt)
Peek an Action header from the given packet.
Class that keeps track of all information about the current PHY operating channel.
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition assert.h:55
#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:75
Ptr< AttributeChecker > MakeAttributeContainerChecker()
Make uninitialized AttributeContainerChecker using explicit types.
Ptr< const AttributeAccessor > MakeAttributeContainerAccessor(T1 a1)
Make AttributeContainerAccessor using explicit types.
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition boolean.cc:113
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition boolean.h:70
Ptr< const AttributeAccessor > MakeEnumAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition enum.h:221
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition nstime.h:1432
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Definition nstime.h:1452
Ptr< const AttributeChecker > MakeUintegerChecker()
Definition uinteger.h:85
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition uinteger.h:35
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition abort.h:97
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition log.h:257
#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:35
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1368
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1344
Ptr< const TraceSourceAccessor > MakeTraceSourceAccessor(T a)
Create a TraceSourceAccessor which will control access to the underlying trace source.
WifiMacDropReason
The reason why an MPDU was dropped.
Definition wifi-mac.h:71
AcIndex
This enumeration defines the Access Categories as an enumeration with values corresponding to the AC ...
Definition qos-utils.h:62
@ STA
Definition wifi-mac.h:59
@ 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.
static constexpr uint8_t DEFAULT_MSD_MAX_N_TXOPS
default MediumSyncDelay max number of TXOP attempts
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:684
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:179
MHz_u GetMaximumChannelWidth(WifiModulationClass modulation)
Get the maximum channel width allowed for the given modulation class.
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:115
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:95
Ptr< T1 > StaticCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:587
static constexpr uint16_t DEFAULT_MSD_DURATION_USEC
default MediumSyncDelay timer duration (max PPDU TX time rounded to a multiple of 32 us)
ns3::Time timeout
Struct to trace that main PHY switched to start a DL TXOP after that an aux PHY received an ICF.
Base struct for EMLSR Main PHY switch traces.
uint8_t emlsrParamUpdateCtrl
EMLSR Parameter Update Control.
EMLSR Parameter Update field.