A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
advanced-emlsr-manager.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2024 Universita' di Napoli Federico II
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Author: Stefano Avallone <stavallo@unina.it>
7 */
8
10
12
13#include "ns3/boolean.h"
14#include "ns3/log.h"
15#include "ns3/wifi-net-device.h"
16#include "ns3/wifi-phy-listener.h"
17#include "ns3/wifi-phy.h"
18
19#include <algorithm>
20#include <sstream>
21
22namespace ns3
23{
24
25NS_LOG_COMPONENT_DEFINE("AdvancedEmlsrManager");
26
27NS_OBJECT_ENSURE_REGISTERED(AdvancedEmlsrManager);
28
29/**
30 * PHY listener connected to the main PHY while operating on the link of an aux PHY that is
31 * not TX capable.
32 *
33 * PHY notifications are forwarded to this EMLSR manager one timestep later because this EMLSR
34 * manager may then decide to switch the main PHY back to the preferred link. Given that notifying
35 * a PHY listener is only one of the actions that are performed when handling events such as RX end
36 * or CCA busy start, it is not a good idea to request a main PHY switch while performing other
37 * actions. Forwarding notifications a timestep later allows to first complete the handling of the
38 * given event and then (possibly) starting a main PHY switch.
39 */
41{
42 public:
43 /**
44 * Constructor
45 *
46 * @param emlsrManager the EMLSR manager
47 */
49 : m_emlsrManager(emlsrManager)
50 {
51 }
52
59
66
67 void NotifyRxEndError(const WifiTxVector& /* txVector */) override
68 {
69 }
70
71 void NotifyTxStart(Time /* duration */, dBm_u /* txPower */) override
72 {
73 }
74
75 void NotifyCcaBusyStart(Time /* duration */,
76 WifiChannelListType /* channelType */,
77 const std::vector<Time>& /* per20MhzDurations */) override
78 {
79 Simulator::Schedule(TimeStep(1),
82 }
83
84 void NotifySwitchingStart(Time /* duration */) override
85 {
86 }
87
88 void NotifySleep() override
89 {
90 }
91
92 void NotifyOff() override
93 {
94 }
95
96 void NotifyWakeup() override
97 {
98 }
99
100 void NotifyOn() override
101 {
102 }
103
104 private:
106};
107
108TypeId
110{
111 static TypeId tid =
112 TypeId("ns3::AdvancedEmlsrManager")
114 .SetGroupName("Wifi")
115 .AddConstructor<AdvancedEmlsrManager>()
116 .AddAttribute("AllowUlTxopInRx",
117 "Whether a (main or aux) PHY is allowed to start an UL TXOP if "
118 "another PHY is receiving a PPDU (possibly starting a DL TXOP). "
119 "If this attribute is true, the PPDU may be dropped.",
120 BooleanValue(false),
123 .AddAttribute("InterruptSwitch",
124 "Whether the main PHY can be interrupted while switching to start "
125 "switching to another link.",
126 BooleanValue(false),
129 .AddAttribute("UseAuxPhyCca",
130 "Whether the CCA performed in the last PIFS interval by a non-TX "
131 "capable aux PHY should be used when the main PHY ends switching to "
132 "the aux PHY's link to determine whether TX can start or not (and what "
133 "bandwidth can be used for transmission) independently of whether the "
134 "aux PHY bandwidth is smaller than the main PHY bandwidth or not.",
135 BooleanValue(false),
138 .AddAttribute("SwitchMainPhyBackDelay",
139 "Duration of the timer started in case of non-TX capable aux PHY (that "
140 "does not switch link) when medium is sensed busy during the PIFS "
141 "interval preceding/following the main PHY switch end. When the timer "
142 "expires, the main PHY is switched back to the preferred link.",
146 .AddAttribute("KeepMainPhyAfterDlTxop",
147 "In case aux PHYs are not TX capable and do not switch link, after the "
148 "end of a DL TXOP carried out on an aux PHY link, the main PHY stays on "
149 "that link for a switch main PHY back delay, if this attribute is true, "
150 "or it returns to the preferred link, otherwise.",
151 BooleanValue(false),
154 .AddAttribute("CheckAccessOnMainPhyLink",
155 "In case aux PHYs are not TX capable and an Access Category, say it AC "
156 "X, is about to gain channel access on an aux PHY link, determine "
157 "whether the time the ACs with priority higher than or equal to AC X and "
158 "with frames to send on the main PHY link are expected to gain access on "
159 "the main PHY link should be taken into account when taking the decision "
160 "to switch the main PHY to the aux PHY link.",
161 BooleanValue(true),
164 .AddAttribute(
165 "MinAcToSkipCheckAccess",
166 "If the CheckAccessOnMainPhyLink attribute is set to false, indicate the "
167 "minimum priority AC for which it is allowed to skip the check related "
168 "to the expected channel access time on the main PHY link.",
172 "AC_BE",
174 "AC_VI",
176 "AC_VO",
178 "AC_BK"));
179 return tid;
180}
181
183{
184 NS_LOG_FUNCTION(this);
185 m_phyListener = std::make_shared<EmlsrPhyListener>(this);
186}
187
192
193void
195{
196 NS_LOG_FUNCTION(this);
197 for (auto phy : GetStaMac()->GetDevice()->GetPhys())
198 {
199 phy->TraceDisconnectWithoutContext(
200 "PhyRxMacHeaderEnd",
202 }
204 m_phyListener.reset();
206}
207
208void
210{
211 NS_LOG_FUNCTION(this);
212
213 // disconnect callbacks on all links
214 for (const auto& linkId : GetStaMac()->GetLinkIds())
215 {
216 GetStaMac()->GetChannelAccessManager(linkId)->TraceDisconnectWithoutContext(
217 "NSlotsLeftAlert",
219 }
220
221 // connect callbacks on EMLSR links
222 for (const auto& emlsrLinkId : GetEmlsrLinks())
223 {
224 GetStaMac()
225 ->GetChannelAccessManager(emlsrLinkId)
226 ->TraceConnectWithoutContext(
227 "NSlotsLeftAlert",
229 }
230
232}
233
234void
236{
237 NS_LOG_FUNCTION(this << mac);
238
239 for (auto phy : GetStaMac()->GetDevice()->GetPhys())
240 {
241 phy->TraceConnectWithoutContext(
242 "PhyRxMacHeaderEnd",
244 }
245}
246
247void
249{
250 NS_LOG_FUNCTION(this << phy->GetPhyId());
251
253 "PHY listener is still connected to PHY " << +m_auxPhyWithListener->GetPhyId());
254 phy->RegisterListener(m_phyListener);
256}
257
258void
270
271std::pair<bool, Time>
273{
274 NS_LOG_FUNCTION(this << linkId);
275
276 // prevent or allow an UL TXOP depending on whether another PHY is receiving a PPDU
277 for (const auto id : GetStaMac()->GetLinkIds())
278 {
279 if (id == linkId)
280 {
281 continue;
282 }
283
284 const auto [maybeIcf, delay] = CheckPossiblyReceivingIcf(id);
285
286 if (!maybeIcf)
287 {
288 // not receiving anything or receiving something that is certainly not an ICF
289 continue;
290 }
291
292 // a PPDU that may be an ICF is being received
294 {
295 return {false, delay};
296 }
297 }
298
299 auto phy = GetStaMac()->GetWifiPhy(linkId);
300 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
301
302 if (phy == mainPhy)
303 {
305 {
306 // main PHY has got access on the link it switched to (because the aux PHY is not TX
307 // capable) before a PIFS interval was elapsed: do not start the TXOP now
308 return {false, Time{0}};
309 }
310
311 // UL TXOP is going to start
312 m_rtsStartingUlTxop[linkId] = {Simulator::Now(), false};
313 }
314
315 return {true, Time{0}};
316}
317
318void
320 const WifiMacHeader& macHdr,
321 const WifiTxVector& txVector,
322 Time psduDuration)
323{
324 auto linkId = GetStaMac()->GetLinkForPhy(phy);
325 if (!linkId.has_value() || !m_useNotifiedMacHdr)
326 {
327 return;
328 }
329 NS_LOG_FUNCTION(this << *linkId << macHdr << txVector << psduDuration.As(Time::MS));
330
331 auto& ongoingTxopEnd = GetEhtFem(*linkId)->GetOngoingTxopEndEvent();
332
333 if (ongoingTxopEnd.IsPending() && macHdr.GetAddr1() != GetEhtFem(*linkId)->GetAddress() &&
334 !macHdr.GetAddr1().IsBroadcast() &&
335 !(macHdr.IsCts() && macHdr.GetAddr1() == GetEhtFem(*linkId)->GetBssid() /* CTS-to-self */))
336 {
337 // the EMLSR client is no longer involved in the TXOP and switching to listening mode
338 ongoingTxopEnd.Cancel();
339 // this method is a callback connected to the PhyRxMacHeaderEnd trace source of WifiPhy
340 // and is called within a for loop that executes all the callbacks. The call to NotifyTxop
341 // below leads the main PHY to be connected back to the preferred link, thus
342 // the ResetPhy() method of the FEM on the auxiliary link is called, which disconnects
343 // another callback (FEM::ReceivedMacHdr) from the PhyRxMacHeaderEnd trace source of
344 // the main PHY, thus invalidating the list of callbacks on which the for loop iterates.
345 // Hence, schedule the call to NotifyTxopEnd to execute it outside such for loop.
347 }
348
349 // if the MAC header has been received on the link on which the main PHY is operating (or on
350 // the link the main PHY is switching to), the switch main PHY back timer is running and channel
351 // access is not expected to be gained by the main PHY before the switch main PHY back timer
352 // expires (plus a channel switch delay), try to switch the main PHY back to the preferred link
353 const auto mainPhyInvolved =
354 (phy->GetPhyId() == GetMainPhyId()) ||
356 const auto delay =
357 Simulator::GetDelayLeft(m_switchMainPhyBackEvent) + phy->GetChannelSwitchDelay();
358
359 Simulator::ScheduleNow([=, this]() {
360 if (WifiExpectedAccessReason reason;
361 m_switchMainPhyBackEvent.IsPending() && mainPhyInvolved &&
362 (reason = GetStaMac()->GetChannelAccessManager(*linkId)->GetExpectedAccessWithin(
364 {
365 SwitchMainPhyBackDelayExpired(*linkId, reason);
366 }
367 });
368}
369
370void
372{
373 NS_LOG_FUNCTION(this << linkId << edca);
374
375 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
376
377 if (m_switchAuxPhy && (!mainPhy->IsStateSwitching() || !m_interruptSwitching))
378 {
379 NS_LOG_DEBUG("SwitchAuxPhy true, nothing to do");
380 return;
381 }
382
384 {
385 NS_LOG_DEBUG("SwitchAuxPhy false, nothing to do");
386 return;
387 }
388
389 // we get here if:
390 // - SwitchAuxPhy is true, the main PHY is switching and switching can be interrupted
391 // or
392 // - SwitchAuxPhy is false and there is an aux PHY to reconnect
393
395 {
396 // DL TXOP ended, check if the main PHY must be kept on this link to try to gain an UL TXOP
398 "Switch main PHY back timer should not be running at the end of a DL TXOP");
399 NS_ASSERT_MSG(!mainPhy->IsStateSwitching(),
400 "Main PHY should not be switching at the end of a DL TXOP");
401
402 if (GetStaMac()->GetChannelAccessManager(linkId)->GetExpectedAccessWithin(
404 {
405 NS_LOG_DEBUG("Keep main PHY on link " << +linkId << " to try to gain an UL TXOP");
409 this,
410 linkId,
411 std::nullopt);
412 // start checking PHY activity on the link the main PHY is operating
413 RegisterListener(GetStaMac()->GetWifiPhy(linkId));
414 return;
415 }
416 }
417
418 std::shared_ptr<EmlsrMainPhySwitchTrace> traceInfo;
419
420 if (const auto it = m_rtsStartingUlTxop.find(linkId);
421 it != m_rtsStartingUlTxop.cend() && it->second.second)
422 {
423 // TXOP ended due to a CTS timeout following the RTS that started a TXOP
424 traceInfo = std::make_shared<EmlsrCtsAfterRtsTimeoutTrace>(Time{0});
425 m_rtsStartingUlTxop.erase(it);
426 }
427 else
428 {
429 traceInfo = std::make_shared<EmlsrTxopEndedTrace>();
430 }
431
432 // Note that the main PHY may be switching at the end of a TXOP when, e.g., the main PHY
433 // starts switching to a link on which an aux PHY gained a TXOP and sent an RTS, but the CTS
434 // is not received and the UL TXOP ends before the main PHY channel switch is completed.
435 // In such cases, wait until the main PHY channel switch is completed (unless the channel
436 // switching can be interrupted) before requesting a new channel switch.
437 // Backoff shall not be reset on the link left by the main PHY because a TXOP ended and
438 // a new backoff value must be generated.
439 if (m_switchAuxPhy || !mainPhy->IsStateSwitching() || m_interruptSwitching)
440 {
443 "Aux PHY next link ID should have a value when interrupting a main PHY switch");
444 uint8_t nextLinkId = m_switchAuxPhy ? m_mainPhySwitchInfo.from : GetMainPhyId();
445 SwitchMainPhy(nextLinkId, false, REQUEST_ACCESS, std::move(*traceInfo));
446 }
447 else
448 {
449 // delay link switch until current channel switching is completed
450 const auto delay = mainPhy->GetDelayUntilIdle();
451
452 if (auto info = std::dynamic_pointer_cast<EmlsrCtsAfterRtsTimeoutTrace>(traceInfo))
453 {
454 info->sinceCtsTimeout = delay;
455 }
456
457 Simulator::Schedule(delay, [=, this]() {
458 // request the main PHY to switch back to the preferred link only if in the meantime
459 // no TXOP started on another link (which will require the main PHY to switch link)
460 if (!GetEhtFem(linkId)->UsingOtherEmlsrLink())
461 {
462 SwitchMainPhy(GetMainPhyId(), false, REQUEST_ACCESS, std::move(*traceInfo));
463 }
464 });
465 }
466}
467
468std::optional<WifiIcfDrop>
470{
472
473 // if the switching can be interrupted, do not drop an ICF due to not enough time for switching
475 {
476 return std::nullopt;
477 }
478 return reason;
479}
480
481std::pair<bool, Time>
483{
484 NS_LOG_FUNCTION(this << linkId);
485
487 {
489 }
490
491 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
492 auto state = mainPhy->GetState()->GetState();
493
495 state == WifiPhyState::IDLE || state == WifiPhyState::CCA_BUSY,
496 "Main PHY cannot be in state " << state);
497
498 auto timeToCtsEnd = GetTimeToCtsEnd(linkId);
499 auto switchingTime = mainPhy->GetChannelSwitchDelay();
500
501 if (switchingTime > timeToCtsEnd)
502 {
503 // switching takes longer than RTS/CTS exchange, release channel
504 NS_LOG_DEBUG("Not enough time for main PHY to switch link (main PHY state: "
505 << mainPhy->GetState()->GetState() << ")");
506 // retry channel access when the CTS was expected to be received
507 return {false, timeToCtsEnd};
508 }
509
510 // TXOP can be started, main PHY will be scheduled to switch by NotifyRtsSent as soon as the
511 // transmission of the RTS is notified
512 m_rtsStartingUlTxop[linkId] = {Simulator::Now(), false};
513
514 return {true, Time{0}};
515}
516
517void
519{
520 NS_LOG_FUNCTION(this << phy->GetPhyId() << linkId << edca->GetAccessCategory());
521
522 const auto caManager = GetStaMac()->GetChannelAccessManager(linkId);
523 const auto pifs = phy->GetSifs() + phy->GetSlot();
524
525 const auto isBusy = caManager->IsBusy(); // check NAV and CCA on primary20
526 // check CCA on the entire channel
527 auto width = caManager->GetLargestIdlePrimaryChannel(pifs, Simulator::Now());
528
529 // lambda to perform the actions needed when a TXOP is not started
530 auto txopNotStarted = [=, this]() {
531 // check when access may be granted to determine whether to switch the main PHY back
532 // to the preferred link (if aux PHYs do not switch link)
533 const auto mainPhy = GetStaMac()->GetDevice()->GetPhy(GetMainPhyId());
534 const auto delay =
535 Simulator::GetDelayLeft(m_switchMainPhyBackEvent) + mainPhy->GetChannelSwitchDelay();
536
537 if (WifiExpectedAccessReason reason;
539 (reason = GetStaMac()->GetChannelAccessManager(linkId)->GetExpectedAccessWithin(
541 {
542 NS_LOG_DEBUG("No AC is expected to get backoff soon, switch main PHY back");
543 SwitchMainPhyBackDelayExpired(linkId, reason);
544 }
545
546 // restart channel access
547 edca->NotifyChannelReleased(linkId); // to set access to NOT_REQUESTED
548 edca->StartAccessAfterEvent(linkId,
551 };
552
554 {
555 NS_LOG_DEBUG("Main PHY switched back (or scheduled to switch back) before PIFS check");
556 txopNotStarted();
557 }
558 else if (!isBusy && width > MHz_u{0})
559 {
560 // medium idle, start TXOP
561 width = std::min(width, GetChannelForMainPhy(linkId).GetTotalWidth());
562
563 // if this function is called at the end of the main PHY switch, it is executed before the
564 // main PHY is connected to this link in order to use the CCA information of the aux PHY.
565 // Schedule now the TXOP start so that we first connect the main PHY to this link.
567 if (GetEhtFem(linkId)->StartTransmission(edca, width))
568 {
569 NotifyUlTxopStart(linkId);
570 }
571 else
572 {
573 txopNotStarted();
574 }
575 });
576 }
577 else
578 {
579 NS_LOG_DEBUG("Medium busy in the last PIFS interval");
580 txopNotStarted();
581 }
582}
583
584void
586 uint8_t linkId,
587 std::optional<WifiExpectedAccessReason> stopReason)
588{
589 if (g_log.IsEnabled(ns3::LOG_FUNCTION))
590 {
591 std::stringstream ss;
592 if (stopReason.has_value())
593 {
594 ss << stopReason.value();
595 }
596 NS_LOG_FUNCTION(this << linkId << ss.str());
597 }
598
600
601 NS_ASSERT_MSG(!m_switchAuxPhy, "Don't expect this to be called when aux PHYs switch link");
602 Time extension{0};
603
604 // check if the timer must be restarted because a frame is being received on any link
605 for (const auto id : GetStaMac()->GetLinkIds())
606 {
607 auto phy = GetStaMac()->GetWifiPhy(id);
608
609 if (!phy || !GetStaMac()->IsEmlsrLink(id))
610 {
611 continue;
612 }
613
614 if (!GetEhtFem(id)->VirtualCsMediumIdle() &&
615 GetEhtFem(id)->GetTxopHolder() != GetEhtFem(id)->GetBssid())
616 {
617 NS_LOG_DEBUG("NAV is set and TXOP holder is not the associated AP MLD on link " << +id);
618 continue;
619 }
620
621 const auto [maybeIcf, delay] = CheckPossiblyReceivingIcf(id);
622
623 if (maybeIcf)
624 {
625 extension = Max(extension, delay);
626 }
627 else if (id == linkId && phy->IsStateIdle())
628 {
629 // this is the link on which the main PHY is operating. If an AC with traffic is
630 // expected to get channel access soon (within a channel switch delay), restart
631 // the timer to have the main PHY stay a bit longer on this link
632 if (GetStaMac()->GetChannelAccessManager(linkId)->GetExpectedAccessWithin(
633 phy->GetChannelSwitchDelay()) == WifiExpectedAccessReason::ACCESS_EXPECTED)
634 {
635 extension = Max(extension, phy->GetChannelSwitchDelay());
636 }
637 }
638 }
639
640 if (extension.IsStrictlyPositive())
641 {
642 NS_LOG_DEBUG("Restarting the timer, check again in " << extension.As(Time::US));
644 Simulator::Schedule(extension,
646 this,
647 linkId,
648 stopReason);
649 return;
650 }
651
652 // no need to wait further, switch the main PHY back to the preferred link and unregister
653 // the PHY listener from the aux PHY
654 const auto elapsed = Simulator::Now() - m_mainPhySwitchInfo.start;
655 const auto isSwitching = GetStaMac()->GetDevice()->GetPhy(GetMainPhyId())->IsStateSwitching();
657 EmlsrSwitchMainPhyBackTrace(elapsed, stopReason, isSwitching));
658 // if scheduled, invoke CheckNavAndCcaLastPifs(), which will just restart channel access
660 {
663 }
665}
666
667void
669 EmlsrMainPhySwitchTrace&& traceInfo)
670{
672 {
674 linkId,
675 std::forward<EmlsrMainPhySwitchTrace>(traceInfo));
676 return;
677 }
678
679 NS_LOG_FUNCTION(this << linkId << traceInfo.GetName());
680
681 NS_ABORT_MSG_IF(m_switchAuxPhy, "This method can only be called when SwitchAuxPhy is false");
682
684 {
685 return;
686 }
687
689 false,
691 std::forward<EmlsrMainPhySwitchTrace>(traceInfo));
692}
693
694void
696{
697 NS_LOG_FUNCTION(this);
698
700 {
701 return; // nothing to do
702 }
703
704 // a busy event occurred, check if the main PHY has to switch back to the preferred link
705 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(GetMainPhyId());
706 auto linkId = GetStaMac()->GetLinkForPhy(GetMainPhyId());
707
708 if (!linkId.has_value())
709 {
711 linkId = m_mainPhySwitchInfo.to;
712 NS_LOG_DEBUG("Main PHY is switching to link " << +linkId.value());
713 }
714
715 const auto delay =
716 Simulator::GetDelayLeft(m_switchMainPhyBackEvent) + mainPhy->GetChannelSwitchDelay();
717 if (auto reason = GetStaMac()->GetChannelAccessManager(*linkId)->GetExpectedAccessWithin(delay);
719 {
720 SwitchMainPhyBackDelayExpired(*linkId, reason);
721 }
722}
723
724void
732
733void
741
742bool
744{
745 NS_LOG_FUNCTION(this << linkId << aci << delay.As(Time::US));
746
747 // the aux PHY is not TX capable; check if main PHY has to switch to the aux PHY's link
748 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
749
750 // if main PHY is not operating on a link and is trying to start a (DL or UL) TXOP, then do
751 // not request another switch
753 (!m_interruptSwitching || m_mainPhySwitchInfo.reason == "DlTxopIcfReceivedByAuxPhy" ||
754 m_mainPhySwitchInfo.reason == "UlTxopAuxPhyNotTxCapable"))
755 {
756 NS_LOG_DEBUG("Main PHY is not operating on any link and cannot switch to another link");
757 return false;
758 }
759
760 // if the main PHY is already trying to get access on a link, do not request another switch
762 {
763 NS_LOG_DEBUG("Main PHY is trying to get access on another link");
764 return false;
765 }
766
767 // delay until the earliest time the main PHY can access medium on the aux PHY link
768 auto minDelay = mainPhy->GetChannelSwitchDelay();
769 if (!m_useAuxPhyCca && (GetChannelForAuxPhy(linkId).GetTotalWidth() <
770 GetChannelForMainPhy(linkId).GetTotalWidth()))
771 {
772 // cannot use aux PHY CCA
773 const auto pifs = GetStaMac()->GetWifiPhy(linkId)->GetPifs();
774 if (m_switchMainPhyBackDelay < pifs)
775 {
777 "Main PHY has to perform CCA but switch main PHY back delay is less than PIFS");
778 return false;
779 }
780 minDelay += pifs;
781 }
782 minDelay = std::max(delay, minDelay);
783
784 if (const auto elapsed = GetElapsedMediumSyncDelayTimer(linkId);
785 elapsed && MediumSyncDelayNTxopsExceeded(linkId) &&
786 (GetMediumSyncDuration() - *elapsed > minDelay))
787 {
788 NS_LOG_DEBUG("No more TXOP attempts allowed on aux PHY link and MSD timer still running");
789 return false;
790 }
791
792 // DoGetDelayUntilAccessRequest has already checked if the main PHY is receiving an ICF and
793 // above it is checked whether we can request another switch while already switching
794 if (const auto state = mainPhy->GetState()->GetState();
795 state != WifiPhyState::IDLE && state != WifiPhyState::CCA_BUSY &&
796 state != WifiPhyState::RX && state != WifiPhyState::SWITCHING)
797 {
798 NS_LOG_DEBUG("Cannot request main PHY to switch when in state " << state);
799 return false;
800 }
801
802 // if the AC that is about to get channel access on the aux PHY link has no frames to send on
803 // that link, do not request the main PHY to switch
804 if (!GetStaMac()->GetQosTxop(aci)->HasFramesToTransmit(linkId))
805 {
806 NS_LOG_DEBUG("No frames of " << aci << " to send on link " << +linkId);
807 return false;
808 }
809
810 // if user has configured to skip the check related to the expected channel access time on
811 // the main PHY link and the AC that is about to gain access on the aux PHY link has a priority
812 // greater than or equal to the minimum priority that has been configured, switch the main PHY
814 {
815 NS_LOG_DEBUG("Skipping check related to the expected channel access time on main PHY link");
816 return true;
817 }
818
819 const auto mainPhyLinkId = GetStaMac()->GetLinkForPhy(mainPhy);
820 if (!mainPhyLinkId.has_value())
821 {
823 NS_LOG_DEBUG("The main PHY is not connected to any link");
824 // we don't know when the main PHY will be connected to the link it is switching to, nor
825 // which backoff value it will possibly generate; therefore, request it to switch to the
826 // aux PHY link
827 return true;
828 }
829
830 // let AC X be the AC that is about to gain channel access on the aux PHY link, request to
831 // switch the main PHY if we do not expect any AC, with priority higher than or equal to that
832 // of AC X and with frames to send on the main PHY link, to gain channel access on the main PHY
833 // link before AC X is able to start transmitting on the aux PHY link.
834
835 const auto now = Simulator::Now();
836
837 for (const auto& [acIndex, ac] : wifiAcList)
838 {
839 // ignore ACs with lower priority than the AC that is about to get access on aux PHY link
840 if (acIndex < aci)
841 {
842 continue;
843 }
844
845 const auto edca = GetStaMac()->GetQosTxop(acIndex);
846 const auto backoffEnd =
847 GetStaMac()->GetChannelAccessManager(*mainPhyLinkId)->GetBackoffEndFor(edca);
848 NS_LOG_DEBUG("Backoff end for " << acIndex
849 << " on main PHY link: " << backoffEnd.As(Time::US));
850
851 if ((backoffEnd <= now + minDelay) && edca->HasFramesToTransmit(*mainPhyLinkId))
852 {
853 NS_LOG_DEBUG(acIndex << " is expected to gain access on link " << +mainPhyLinkId.value()
854 << " sooner than " << aci << " on link " << +linkId);
855 return false;
856 }
857 }
858
859 return true;
860}
861
862void
864{
865 NS_LOG_FUNCTION(this << linkId << aci);
866
868 "This function should only be called if aux PHY is not TX capable");
869 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
870
871 if (mainPhy->IsStateSwitching() && m_mainPhySwitchInfo.to == linkId)
872 {
873 // the main PHY is switching to the link on which the aux PHY gained a TXOP. This can
874 // happen, e.g., if the main PHY was requested to switch to that link before the backoff
875 // counter reached zero. Or, this can happen in case of internal collision: the first AC
876 // requests the main PHY to switch and the second one finds the main PHY to be switching.
877 // In both cases, we do nothing because we have already scheduled the necessary actions
878 NS_LOG_DEBUG("Main PHY is already switching to link " << +linkId);
879 return;
880 }
881
882 if (RequestMainPhyToSwitch(linkId, aci, Time{0}))
883 {
884 const auto auxPhy = GetStaMac()->GetWifiPhy(linkId);
885 const auto pifs = auxPhy->GetSifs() + auxPhy->GetSlot();
886
887 // schedule actions to take based on CCA sensing for a PIFS
888 if (m_useAuxPhyCca || GetChannelForAuxPhy(linkId).GetTotalWidth() >=
889 GetChannelForMainPhy(linkId).GetTotalWidth())
890 {
891 // use aux PHY CCA in the last PIFS interval before main PHY switch end
892 NS_LOG_DEBUG("Schedule CCA check at the end of main PHY switch");
893 m_ccaLastPifs = Simulator::Schedule(mainPhy->GetChannelSwitchDelay(),
895 this,
896 auxPhy,
897 linkId,
898 GetStaMac()->GetQosTxop(aci));
899 }
900 else
901 {
902 // use main PHY CCA in the last PIFS interval after main PHY switch end
903 NS_LOG_DEBUG("Schedule CCA check a PIFS after the end of main PHY switch");
904 m_ccaLastPifs = Simulator::Schedule(mainPhy->GetChannelSwitchDelay() + pifs,
906 this,
907 mainPhy,
908 linkId,
909 GetStaMac()->GetQosTxop(aci));
910 }
911
912 // switch main PHY
913 Time remNav{0};
914 if (const auto mainPhyLinkId = GetStaMac()->GetLinkForPhy(mainPhy))
915 {
916 auto mainPhyNavEnd = GetStaMac()->GetChannelAccessManager(*mainPhyLinkId)->GetNavEnd();
917 remNav = Max(remNav, mainPhyNavEnd - Simulator::Now());
918 }
919
920 SwitchMainPhy(linkId,
921 false,
924
925 // if SwitchAuxPhy is false, the main PHY must stay for some time on this link to check if
926 // it gets channel access. The timer is stopped if a DL or UL TXOP is started. When the
927 // timer expires, the main PHY switches back to the preferred link
928 if (!m_switchAuxPhy)
929 {
932 Simulator::Schedule(mainPhy->GetChannelSwitchDelay() + m_switchMainPhyBackDelay,
934 this,
935 linkId,
936 std::nullopt);
937 // start checking PHY activity on the link the main PHY is switching to
938 RegisterListener(auxPhy);
939 }
940 return;
941 }
942
943 // Determine if and when we need to request channel access again for the aux PHY based on
944 // the main PHY state.
945 // Note that, if we have requested the main PHY to switch (above), the function has returned
946 // and the EHT FEM will start a TXOP if medium is idle for a PIFS interval preceding/following
947 // the end of the main PHY channel switch.
948 // If the main PHY has been requested to switch by another aux PHY, this aux PHY will request
949 // channel access again when we have completed the CCA assessment on the other link.
950 // If the state is switching, CCA_BUSY or RX, then we request channel access again for the
951 // aux PHY when the main PHY state is back to IDLE.
952 // If the state is TX, it means that the main PHY is involved in a TXOP. Do nothing because
953 // the channel access will be requested when unblocking links at the end of the TXOP.
954 // If the state is IDLE, then either no AC has traffic to send or the backoff on the link
955 // of the main PHY is shorter than the channel switch delay. In the former case, do
956 // nothing because channel access will be triggered when new packets arrive; in the latter
957 // case, do nothing because the main PHY will start a TXOP and at the end of such TXOP
958 // links will be unblocked and the channel access requested on all links
959
960 std::optional<Time> delay;
961
963 {
964 delay = std::max(Simulator::GetDelayLeft(m_ccaLastPifs),
966 }
967 else if (mainPhy->GetState()->GetLastTime(
968 {WifiPhyState::SWITCHING, WifiPhyState::CCA_BUSY, WifiPhyState::RX}) ==
970 {
971 delay = mainPhy->GetDelayUntilIdle();
972 }
973
974 NS_LOG_DEBUG("Main PHY state is " << mainPhy->GetState()->GetState());
975 auto edca = GetStaMac()->GetQosTxop(aci);
976 edca->NotifyChannelReleased(linkId); // to set access to NOT_REQUESTED
977
978 if (!delay.has_value())
979 {
980 NS_LOG_DEBUG("Do nothing");
981 return;
982 }
983
984 NS_LOG_DEBUG("Schedule channel access request on link "
985 << +linkId << " at time " << (Simulator::Now() + *delay).As(Time::NS));
986 Simulator::Schedule(*delay, [=]() {
987 edca->StartAccessAfterEvent(linkId,
990 });
991}
992
993void
995 AcIndex aci,
996 const Time& delay)
997{
998 NS_LOG_FUNCTION(this << linkId << aci << delay.As(Time::US));
999
1001 {
1002 NS_LOG_DEBUG("Nothing to do if aux PHY is TX capable");
1003 return;
1004 }
1005
1006 if (!delay.IsStrictlyPositive())
1007 {
1008 NS_LOG_DEBUG("Do nothing if delay is not strictly positive");
1009 return;
1010 }
1011
1012 if (GetEhtFem(linkId)->UsingOtherEmlsrLink())
1013 {
1014 NS_LOG_DEBUG("Do nothing because another EMLSR link is being used");
1015 return;
1016 }
1017
1019 {
1020 NS_LOG_DEBUG("Do nothing because a frame is being received on another EMLSR link");
1021 return;
1022 }
1023
1024 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
1025 auto phy = GetStaMac()->GetWifiPhy(linkId);
1026
1027 if (!phy || phy == mainPhy)
1028 {
1029 NS_LOG_DEBUG("No aux PHY is operating on link " << +linkId);
1030 return;
1031 }
1032
1033 if (!RequestMainPhyToSwitch(linkId, aci, delay))
1034 {
1035 NS_LOG_DEBUG("Chosen not to request the main PHY to switch");
1036 if (const auto untilIdle = mainPhy->GetDelayUntilIdle();
1037 untilIdle.IsStrictlyPositive() && untilIdle < delay)
1038 {
1039 NS_LOG_DEBUG("Retrying in " << untilIdle.As(Time::US));
1040 Simulator::Schedule(untilIdle,
1042 this,
1043 linkId,
1044 aci,
1045 delay - untilIdle);
1046 }
1047 return;
1048 }
1049
1050 // switch main PHY
1051
1052 // use aux PHY CCA (if allowed) if the backoff has already counted down to zero on the aux PHY
1053 // link when the main PHY completes the switch
1054 const auto edca = GetStaMac()->GetQosTxop(aci);
1055 const auto auxPhy = GetStaMac()->GetWifiPhy(linkId);
1056 const auto switchDelay = mainPhy->GetChannelSwitchDelay();
1057 const auto auxPhyCcaCanBeUsed =
1060
1061 // check expected channel access delay when switch is completed
1062 Simulator::Schedule(switchDelay, [=, this]() {
1063 // this is scheduled before starting the main PHY switch, hence it is executed before the
1064 // main PHY is connected to the aux PHY link
1065
1067 {
1068 // if SwitchAuxPhy is false and the switch main PHY back timer is not running, it means
1069 // that the channel switch was interrupted, hence there is nothing to check
1070 return;
1071 }
1072
1073 const auto backoffEnd =
1074 GetStaMac()->GetChannelAccessManager(linkId)->GetBackoffEndFor(edca);
1075 const auto pifs = GetStaMac()->GetWifiPhy(linkId)->GetPifs();
1076 const auto now = Simulator::Now();
1077
1078 // In case aux PHY CCA can be used and the backoff has not yet reached zero, no NAV and CCA
1079 // check is needed. The channel width that will be used is the width of the aux PHY if less
1080 // than a PIFS remains until the backoff reaches zero, and the width of the main PHY,
1081 // otherwise. If aux PHY CCA can be used and the backoff has already reached zero, a NAV and
1082 // CCA check is needed.
1083
1084 if (auxPhyCcaCanBeUsed && backoffEnd < now)
1085 {
1086 /**
1087 * use aux PHY CCA in the last PIFS interval before main PHY switch end
1088 *
1089 * Backoff Switch
1090 * end end (now)
1091 * ──────────┴─────────┴──────────
1092 * |---- PIFS ----|
1093 */
1094 CheckNavAndCcaLastPifs(auxPhy, linkId, edca);
1095 }
1096 else if (!auxPhyCcaCanBeUsed && (backoffEnd - now <= pifs))
1097 {
1098 /**
1099 * the remaining backoff time (if any) when the main PHY completes the switch is shorter
1100 * than or equal to a PIFS, thus the main PHY performs CCA in the last PIFS interval
1101 * after switch end
1102 *
1103 * Switch Backoff Backoff Switch
1104 * end (now) end end end (now)
1105 * ──────────┴─────────┴────────── ──────────┴─────────┴──────────
1106 * |---- PIFS ----| |---- PIFS ----|
1107 */
1108 NS_LOG_DEBUG("Schedule CCA check a PIFS after the end of main PHY switch");
1111 this,
1112 mainPhy,
1113 linkId,
1114 edca);
1115 }
1116 else if (WifiExpectedAccessReason reason;
1117 !m_switchAuxPhy &&
1118 (reason = GetStaMac()->GetChannelAccessManager(linkId)->GetExpectedAccessWithin(
1120 mainPhy->GetChannelSwitchDelay())) !=
1122 {
1123 NS_LOG_DEBUG("No AC is expected to get backoff soon, switch main PHY back");
1124 SwitchMainPhyBackDelayExpired(linkId, reason);
1125 }
1126 });
1127
1128 Time remNav{0};
1129 if (const auto mainPhyLinkId = GetStaMac()->GetLinkForPhy(mainPhy))
1130 {
1131 auto mainPhyNavEnd = GetStaMac()->GetChannelAccessManager(*mainPhyLinkId)->GetNavEnd();
1132 remNav = Max(remNav, mainPhyNavEnd - Simulator::Now());
1133 }
1134
1135 SwitchMainPhy(linkId,
1136 false,
1138 EmlsrUlTxopAuxPhyNotTxCapableTrace(aci, delay, remNav));
1139
1140 // if SwitchAuxPhy is false, the main PHY must stay for some time on this link to check if it
1141 // gets channel access. The timer is stopped if a DL or UL TXOP is started. When the timer
1142 // expires, the main PHY switches back to the preferred link
1143 if (!m_switchAuxPhy)
1144 {
1149 this,
1150 linkId,
1151 std::nullopt);
1152 // start checking PHY activity on the link the main PHY is switching to
1153 RegisterListener(auxPhy);
1154 }
1155}
1156
1157} // namespace ns3
#define Max(a, b)
AdvancedEmlsrManager is an advanced EMLSR manager.
void SwitchMainPhyBackDelayExpired(uint8_t linkId, std::optional< WifiExpectedAccessReason > stopReason)
This method is called when the switch main PHY back timer (which is started when the main PHY switche...
std::pair< bool, Time > DoGetDelayUntilAccessRequest(uint8_t linkId) override
Subclasses have to provide an implementation for this method, that is called by the base class when t...
void InterruptSwitchMainPhyBackTimerIfNeeded()
This method is called by the PHY listener attached to the main PHY when a switch main PHY back timer ...
void DoNotifyDlTxopStart(uint8_t linkId) override
Notify the subclass of the reception of an initial Control frame on the given link.
std::pair< bool, Time > GetDelayUnlessMainPhyTakesOverUlTxop(uint8_t linkId) override
Subclasses have to provide an implementation for this method, that is called by the base class when t...
bool m_useAuxPhyCca
whether the CCA performed in the last PIFS interval by a non-TX capable aux PHY should be used when t...
bool RequestMainPhyToSwitch(uint8_t linkId, AcIndex aci, const Time &delay)
Determine whether the main PHY shall be requested to switch to the link of an aux PHY that is expecte...
void UnregisterListener()
Disconnect the PHY listener from the PHY it is connected to (if any)
void RegisterListener(Ptr< WifiPhy > phy)
Register a PHY listener so that this EMLSR Manager is notified of PHY events generated by the given P...
void SwitchMainPhyIfTxopToBeGainedByAuxPhy(uint8_t linkId, AcIndex aci, const Time &delay)
This method is called when the given AC of the EMLSR client is expected to get channel access in the ...
bool m_allowUlTxopInRx
whether a (main or aux) PHY is allowed to start an UL TXOP if another PHY is receiving a PPDU
void CheckNavAndCcaLastPifs(Ptr< WifiPhy > phy, uint8_t linkId, Ptr< QosTxop > edca)
Use information from NAV and CCA performed by the given PHY on the given link in the last PIFS interv...
Ptr< WifiPhy > m_auxPhyWithListener
aux PHY which a PHY listener is connected to
std::optional< WifiIcfDrop > CheckMainPhyTakesOverDlTxop(uint8_t linkId) const override
This method is called when an aux PHY has completed reception of an ICF to determine whether there is...
void NotifyEmlsrModeChanged() override
Notify subclass that EMLSR mode changed.
static TypeId GetTypeId()
Get the type ID.
void SwitchMainPhyBackToPreferredLink(uint8_t linkId, EmlsrMainPhySwitchTrace &&traceInfo) override
This method can only be called when aux PHYs do not switch link.
void DoNotifyUlTxopStart(uint8_t linkId) override
Notify the subclass of the start of an UL TXOP on the given link.
std::shared_ptr< WifiPhyListener > m_phyListener
PHY listener connected to an aux PHY (that is not TX capable and does not switch link) while the main...
Time m_switchMainPhyBackDelay
duration of the timer started in case of non-TX capable aux PHY when medium is sensed busy during the...
void DoDispose() override
Destructor implementation.
EventId m_switchMainPhyBackEvent
event scheduled in case of non-TX capable aux PHY when medium is sensed busy during the PIFS interval...
void DoNotifyTxopEnd(uint8_t linkId, Ptr< QosTxop > edca) override
Notify the subclass of the end of a TXOP on the given link.
AcIndex m_minAcToSkipCheckAccess
if m_checkAccessOnMainPhyLink is set to false, indicate the minimum priority AC for which it is allow...
void SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId, AcIndex aci) override
Subclasses have to provide an implementation for this method, that is called by the base class when t...
void DoSetWifiMac(Ptr< StaWifiMac > mac) override
Allow subclasses to take actions when the MAC is set.
bool m_keepMainPhyAfterDlTxop
whether the main PHY must stay, for a switch main PHY back delay, on an aux PHY link after a DL TXOP,...
void ReceivedMacHdr(Ptr< WifiPhy > phy, const WifiMacHeader &macHdr, const WifiTxVector &txVector, Time psduDuration)
Possibly take actions when notified of the MAC header of the MPDU being received by the given PHY.
EventId m_ccaLastPifs
event scheduled in case of non-TX capable aux PHY to determine whether TX can be started based on whe...
bool m_interruptSwitching
whether a main PHY switching can be interrupted to start switching to another link
bool m_checkAccessOnMainPhyLink
in case aux PHYs are not TX capable and an Access Category, say it AC X, is about to gain channel acc...
AttributeValue implementation for Boolean.
Definition boolean.h:26
DefaultEmlsrManager is the default EMLSR manager.
void NotifyEmlsrModeChanged() override
Notify subclass that EMLSR mode changed.
Ptr< WifiPhy > m_auxPhyToReconnect
Aux PHY the ChannelAccessManager of the link on which the main PHY is operating has to connect a list...
bool m_switchAuxPhy
whether Aux PHY should switch channel to operate on the link on which the Main PHY was operating befo...
Time GetTimeToCtsEnd(uint8_t linkId) const
This function is intended to be called when an aux PHY is about to transmit an RTS on the given link ...
std::map< uint8_t, std::pair< Time, bool > > m_rtsStartingUlTxop
link ID-indexed map indicating the time when an UL TXOP is going to start and whether it is starting ...
virtual void SwitchMainPhyBackToPreferredLink(uint8_t linkId, EmlsrMainPhySwitchTrace &&traceInfo)
This method can only be called when aux PHYs do not switch link.
std::pair< bool, Time > GetDelayUnlessMainPhyTakesOverUlTxop(uint8_t linkId) override
Subclasses have to provide an implementation for this method, that is called by the base class when t...
Time GetMediumSyncDuration() const
void SwitchMainPhy(uint8_t linkId, bool noSwitchDelay, bool requestAccess, EmlsrMainPhySwitchTrace &&traceInfo)
Switch channel on the Main PHY so that it operates on the given link.
bool m_useNotifiedMacHdr
whether to use the information about the MAC header of the MPDU being received (if notified by the PH...
bool m_auxPhyTxCapable
whether Aux PHYs are capable of transmitting PPDUs
bool MediumSyncDelayNTxopsExceeded(uint8_t linkId)
Return whether no more TXOP attempt is allowed on the given link.
Ptr< EhtFrameExchangeManager > GetEhtFem(uint8_t linkId) const
void NotifyUlTxopStart(uint8_t linkId)
Notify the start of an UL TXOP on the given link.
uint8_t m_mainPhyId
ID of main PHY (position in the vector of PHYs held by WifiNetDevice)
const std::set< uint8_t > & GetEmlsrLinks() const
void NotifyTxopEnd(uint8_t linkId, Ptr< QosTxop > edca=nullptr)
Notify the end of a TXOP on the given link.
std::optional< Time > GetElapsedMediumSyncDelayTimer(uint8_t linkId) const
Check whether the MediumSyncDelay timer is running for the STA operating on the given link.
virtual std::optional< WifiIcfDrop > CheckMainPhyTakesOverDlTxop(uint8_t linkId) const
This method is called when an aux PHY has completed reception of an ICF to determine whether there is...
const WifiPhyOperatingChannel & GetChannelForMainPhy(uint8_t linkId) const
MainPhySwitchInfo m_mainPhySwitchInfo
main PHY switch info
static constexpr bool REQUEST_ACCESS
request channel access when PHY switch ends
static constexpr bool DONT_REQUEST_ACCESS
do not request channel access when PHY switch ends
Ptr< StaWifiMac > GetStaMac() const
uint8_t GetMainPhyId() const
const WifiPhyOperatingChannel & GetChannelForAuxPhy(uint8_t linkId) const
std::pair< bool, Time > CheckPossiblyReceivingIcf(uint8_t linkId) const
Check whether a PPDU that may be an ICF is being received on the given link.
PHY listener connected to the main PHY while operating on the link of an aux PHY that is not TX capab...
EmlsrPhyListener(Ptr< AdvancedEmlsrManager > emlsrManager)
Constructor.
void NotifyCcaBusyStart(Time, WifiChannelListType, const std::vector< Time > &) override
void NotifyWakeup() override
Notify listeners that we woke up.
void NotifyOff() override
Notify listeners that we went to switch off.
void NotifyTxStart(Time, dBm_u) override
void NotifyRxEndError(const WifiTxVector &) override
void NotifySwitchingStart(Time) override
void NotifyRxStart(Time) override
void NotifyRxEndOk() override
We have received the last bit of a packet for which NotifyRxStart was invoked first and,...
void NotifyOn() override
Notify listeners that we went to switch on.
void NotifySleep() override
Notify listeners that we went to sleep.
Ptr< AdvancedEmlsrManager > m_emlsrManager
the EMLSR manager
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
bool IsBroadcast() const
virtual void DoDispose()
Destructor implementation.
Definition object.cc:433
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:561
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:595
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:403
bool IsStrictlyPositive() const
Exactly equivalent to t > 0.
Definition nstime.h:340
@ US
microsecond
Definition nstime.h:107
@ MS
millisecond
Definition nstime.h:106
@ NS
nanosecond
Definition nstime.h:108
AttributeValue implementation for Time.
Definition nstime.h:1432
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:49
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition type-id.cc:1001
Implements the IEEE 802.11 MAC header.
bool IsCts() const
Return true if the header is a CTS header.
Mac48Address GetAddr1() const
Return the address in the Address 1 field.
uint8_t GetPhyId() const
Get the index allocated to this PHY.
Definition wifi-phy.cc:680
void UnregisterListener(const std::shared_ptr< WifiPhyListener > &listener)
Definition wifi-phy.cc:509
receive notifications about PHY events.
MHz_u GetTotalWidth() const
Return the width of the whole 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< 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:1433
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Definition nstime.h:1453
#define NS_ABORT_MSG_UNLESS(cond, msg)
Abnormal program termination if a condition is false, with a message.
Definition abort.h:133
#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 MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1357
WifiExpectedAccessReason
Enumeration values for the outcome of the check whether channel access is expected to be gained withi...
WifiChannelListType
Enumeration of the possible channel-list parameter elements defined in Table 8-5 of IEEE 802....
AcIndex
This enumeration defines the Access Categories as an enumeration with values corresponding to the AC ...
Definition qos-utils.h:62
@ AC_BE
Best Effort.
Definition qos-utils.h:64
@ AC_VO
Voice.
Definition qos-utils.h:70
@ AC_VI
Video.
Definition qos-utils.h:68
@ AC_BK
Background.
Definition qos-utils.h:66
Definition first.py:1
Every class exported by the ns3 library is enclosed in the ns3 namespace.
@ SWITCHING
The PHY layer is switching to other channel.
@ IDLE
The PHY layer is IDLE.
@ CCA_BUSY
The PHY layer has sense the medium busy through the CCA mechanism.
@ RX
The PHY layer is receiving a packet.
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
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
@ LOG_FUNCTION
Function tracing for non-trivial function calls.
Definition log.h:95
Base struct for EMLSR Main PHY switch traces.
std::string reason
the reason for switching the main PHY
uint8_t from
ID of the link which the main PHY is/has been leaving.
Time start
start of channel switching
uint8_t to
ID of the link which the main PHY is moving to.
bool disconnected
true if the main PHY is not connected to any link, i.e., it is switching or waiting to be connected t...
Struct to trace that main PHY switched to leave a link on which an aux PHY was expected to gain a TXO...
Struct to trace that main PHY switched to operate on a link on which an aux PHY that is not TX capabl...