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 const auto isMainPhy = (phy->GetPhyId() == GetMainPhyId());
333
334 if (ongoingTxopEnd.IsPending() && macHdr.GetAddr1() != GetEhtFem(*linkId)->GetAddress() &&
335 !macHdr.IsTrigger() && !macHdr.IsBlockAck() &&
336 !(macHdr.IsCts() && macHdr.GetAddr1() == GetEhtFem(*linkId)->GetBssid() /* CTS-to-self */))
337 {
338 // the EMLSR client is no longer involved in the TXOP and switching to listening mode
339 ongoingTxopEnd.Cancel();
340 // this method is a callback connected to the PhyRxMacHeaderEnd trace source of WifiPhy
341 // and is called within a for loop that executes all the callbacks. The call to NotifyTxop
342 // below leads the main PHY to be connected back to the preferred link, thus
343 // the ResetPhy() method of the FEM on the auxiliary link is called, which disconnects
344 // another callback (FEM::ReceivedMacHdr) from the PhyRxMacHeaderEnd trace source of
345 // the main PHY, thus invalidating the list of callbacks on which the for loop iterates.
346 // Hence, schedule the call to NotifyTxopEnd to execute it outside such for loop.
348 }
349
350 if (!ongoingTxopEnd.IsPending() && GetStaMac()->IsEmlsrLink(*linkId) && isMainPhy &&
351 !GetEhtFem(*linkId)->UsingOtherEmlsrLink() &&
352 (macHdr.IsRts() || macHdr.IsBlockAckReq() || macHdr.IsData()) &&
353 (macHdr.GetAddr1() == GetEhtFem(*linkId)->GetAddress()))
354 {
355 // a frame that is starting a DL TXOP is being received by the main PHY; start blocking
356 // transmission on other links (which is normally done later on by PostProcessFrame()) to
357 // avoid starting an UL TXOP before the end of the MPDU
358 for (auto id : GetStaMac()->GetLinkIds())
359 {
360 if (id != *linkId && GetStaMac()->IsEmlsrLink(id))
361 {
363 }
364 }
365 return;
366 }
367
368 // if the MAC header has been received on the link on which the main PHY is operating (or on
369 // the link the main PHY is switching to), the switch main PHY back timer is running and channel
370 // access is not expected to be gained by the main PHY before the switch main PHY back timer
371 // expires (plus a channel switch delay), try to switch the main PHY back to the preferred link
372 const auto mainPhyInvolved =
373 isMainPhy || (m_mainPhySwitchInfo.disconnected && m_mainPhySwitchInfo.to == *linkId);
374 const auto delay =
375 Simulator::GetDelayLeft(m_switchMainPhyBackEvent) + phy->GetChannelSwitchDelay();
376
377 Simulator::ScheduleNow([=, this]() {
378 if (WifiExpectedAccessReason reason;
379 m_switchMainPhyBackEvent.IsPending() && mainPhyInvolved &&
380 (reason = GetStaMac()->GetChannelAccessManager(*linkId)->GetExpectedAccessWithin(
382 {
383 SwitchMainPhyBackDelayExpired(*linkId, reason);
384 }
385 });
386}
387
388void
390{
391 NS_LOG_FUNCTION(this << linkId << edca);
392
393 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
394
395 if (m_switchAuxPhy && (!mainPhy->IsStateSwitching() || !m_interruptSwitching))
396 {
397 NS_LOG_DEBUG("SwitchAuxPhy true, nothing to do");
398 return;
399 }
400
402 {
403 NS_LOG_DEBUG("SwitchAuxPhy false, nothing to do");
404 return;
405 }
406
407 // we get here if:
408 // - SwitchAuxPhy is true, the main PHY is switching and switching can be interrupted
409 // or
410 // - SwitchAuxPhy is false and there is an aux PHY to reconnect
411
413 {
414 // DL TXOP ended, check if the main PHY must be kept on this link to try to gain an UL TXOP
416 "Switch main PHY back timer should not be running at the end of a DL TXOP");
417 NS_ASSERT_MSG(!mainPhy->IsStateSwitching(),
418 "Main PHY should not be switching at the end of a DL TXOP");
419
420 if (GetStaMac()->GetChannelAccessManager(linkId)->GetExpectedAccessWithin(
422 {
423 NS_LOG_DEBUG("Keep main PHY on link " << +linkId << " to try to gain an UL TXOP");
427 this,
428 linkId,
429 std::nullopt);
430 // start checking PHY activity on the link the main PHY is operating
431 RegisterListener(GetStaMac()->GetWifiPhy(linkId));
432 return;
433 }
434 }
435
436 std::shared_ptr<EmlsrMainPhySwitchTrace> traceInfo;
437
438 if (const auto it = m_rtsStartingUlTxop.find(linkId);
439 it != m_rtsStartingUlTxop.cend() && it->second.second)
440 {
441 // TXOP ended due to a CTS timeout following the RTS that started a TXOP
442 traceInfo = std::make_shared<EmlsrCtsAfterRtsTimeoutTrace>(Time{0});
443 m_rtsStartingUlTxop.erase(it);
444 }
445 else
446 {
447 traceInfo = std::make_shared<EmlsrTxopEndedTrace>();
448 }
449
450 // Note that the main PHY may be switching at the end of a TXOP when, e.g., the main PHY
451 // starts switching to a link on which an aux PHY gained a TXOP and sent an RTS, but the CTS
452 // is not received and the UL TXOP ends before the main PHY channel switch is completed.
453 // In such cases, wait until the main PHY channel switch is completed (unless the channel
454 // switching can be interrupted) before requesting a new channel switch.
455 // Backoff shall not be reset on the link left by the main PHY because a TXOP ended and
456 // a new backoff value must be generated.
457 if (m_switchAuxPhy || !mainPhy->IsStateSwitching() || m_interruptSwitching)
458 {
461 "Aux PHY next link ID should have a value when interrupting a main PHY switch");
462 uint8_t nextLinkId = m_switchAuxPhy ? m_mainPhySwitchInfo.from : GetMainPhyId();
463 SwitchMainPhy(nextLinkId, false, REQUEST_ACCESS, std::move(*traceInfo));
464 }
465 else
466 {
467 // delay link switch until current channel switching is completed
468 const auto delay = mainPhy->GetDelayUntilIdle();
469
470 if (auto info = std::dynamic_pointer_cast<EmlsrCtsAfterRtsTimeoutTrace>(traceInfo))
471 {
472 info->sinceCtsTimeout = delay;
473 }
474
475 Simulator::Schedule(delay, [=, this]() {
476 // request the main PHY to switch back to the preferred link only if in the meantime
477 // no TXOP started on another link (which will require the main PHY to switch link)
478 if (!GetEhtFem(linkId)->UsingOtherEmlsrLink())
479 {
480 SwitchMainPhy(GetMainPhyId(), false, REQUEST_ACCESS, std::move(*traceInfo));
481 }
482 });
483 }
484}
485
486std::optional<WifiIcfDrop>
488{
490
491 // if the switching can be interrupted, do not drop an ICF due to not enough time for switching
493 {
494 return std::nullopt;
495 }
496 return reason;
497}
498
499std::pair<bool, Time>
501{
502 NS_LOG_FUNCTION(this << linkId);
503
505 {
507 }
508
509 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
510 auto state = mainPhy->GetState()->GetState();
511
513 state == WifiPhyState::IDLE || state == WifiPhyState::CCA_BUSY,
514 "Main PHY cannot be in state " << state);
515
516 auto timeToCtsEnd = GetTimeToCtsEnd(linkId);
517 auto switchingTime = mainPhy->GetChannelSwitchDelay();
518
519 if (switchingTime > timeToCtsEnd)
520 {
521 // switching takes longer than RTS/CTS exchange, release channel
522 NS_LOG_DEBUG("Not enough time for main PHY to switch link (main PHY state: "
523 << mainPhy->GetState()->GetState() << ")");
524 // retry channel access when the CTS was expected to be received
525 return {false, timeToCtsEnd};
526 }
527
528 // TXOP can be started, main PHY will be scheduled to switch by NotifyRtsSent as soon as the
529 // transmission of the RTS is notified
530 m_rtsStartingUlTxop[linkId] = {Simulator::Now(), false};
531
532 return {true, Time{0}};
533}
534
535void
537{
538 NS_LOG_FUNCTION(this << phy->GetPhyId() << linkId << edca->GetAccessCategory());
539
540 const auto caManager = GetStaMac()->GetChannelAccessManager(linkId);
541 const auto pifs = phy->GetSifs() + phy->GetSlot();
542
543 const auto isBusy = caManager->IsBusy(); // check NAV and CCA on primary20
544 // check CCA on the entire channel
545 auto width = caManager->GetLargestIdlePrimaryChannel(pifs, Simulator::Now());
546
547 // lambda to perform the actions needed when a TXOP is not started
548 auto txopNotStarted = [=, this]() {
549 // check when access may be granted to determine whether to switch the main PHY back
550 // to the preferred link (if aux PHYs do not switch link)
551 const auto mainPhy = GetStaMac()->GetDevice()->GetPhy(GetMainPhyId());
552 const auto delay =
553 Simulator::GetDelayLeft(m_switchMainPhyBackEvent) + mainPhy->GetChannelSwitchDelay();
554
555 if (WifiExpectedAccessReason reason;
557 (reason = GetStaMac()->GetChannelAccessManager(linkId)->GetExpectedAccessWithin(
559 {
560 NS_LOG_DEBUG("No AC is expected to get backoff soon, switch main PHY back");
561 SwitchMainPhyBackDelayExpired(linkId, reason);
562 }
563
564 // restart channel access
565 edca->NotifyChannelReleased(linkId); // to set access to NOT_REQUESTED
566 edca->StartAccessAfterEvent(linkId,
569 };
570
572 {
573 NS_LOG_DEBUG("Main PHY switched back (or scheduled to switch back) before PIFS check");
574 txopNotStarted();
575 }
576 else if (!isBusy && width > MHz_u{0})
577 {
578 // medium idle, start TXOP
579 width = std::min(width, GetChannelForMainPhy(linkId).GetTotalWidth());
580
581 // if this function is called at the end of the main PHY switch, it is executed before the
582 // main PHY is connected to this link in order to use the CCA information of the aux PHY.
583 // Schedule now the TXOP start so that we first connect the main PHY to this link.
585 if (GetEhtFem(linkId)->StartTransmission(edca, width))
586 {
587 NotifyUlTxopStart(linkId);
588 }
589 else
590 {
591 txopNotStarted();
592 }
593 });
594 }
595 else
596 {
597 NS_LOG_DEBUG("Medium busy in the last PIFS interval");
598 txopNotStarted();
599 }
600}
601
602void
604 uint8_t linkId,
605 std::optional<WifiExpectedAccessReason> stopReason)
606{
607 if (g_log.IsEnabled(ns3::LOG_FUNCTION))
608 {
609 std::stringstream ss;
610 if (stopReason.has_value())
611 {
612 ss << stopReason.value();
613 }
614 NS_LOG_FUNCTION(this << linkId << ss.str());
615 }
616
618
619 NS_ASSERT_MSG(!m_switchAuxPhy, "Don't expect this to be called when aux PHYs switch link");
620 Time extension{0};
621
622 // check if the timer must be restarted because a frame is being received on any link
623 for (const auto id : GetStaMac()->GetLinkIds())
624 {
625 auto phy = GetStaMac()->GetWifiPhy(id);
626
627 if (!phy || !GetStaMac()->IsEmlsrLink(id))
628 {
629 continue;
630 }
631
632 if (!GetEhtFem(id)->VirtualCsMediumIdle() &&
633 GetEhtFem(id)->GetTxopHolder() != GetEhtFem(id)->GetBssid())
634 {
635 NS_LOG_DEBUG("NAV is set and TXOP holder is not the associated AP MLD on link " << +id);
636 continue;
637 }
638
639 const auto [maybeIcf, delay] = CheckPossiblyReceivingIcf(id);
640
641 if (maybeIcf)
642 {
643 extension = Max(extension, delay);
644 }
645 else if (id == linkId && phy->IsStateIdle())
646 {
647 // this is the link on which the main PHY is operating. If an AC with traffic is
648 // expected to get channel access soon (within a channel switch delay), restart
649 // the timer to have the main PHY stay a bit longer on this link
650 if (GetStaMac()->GetChannelAccessManager(linkId)->GetExpectedAccessWithin(
651 phy->GetChannelSwitchDelay()) == WifiExpectedAccessReason::ACCESS_EXPECTED)
652 {
653 extension = Max(extension, phy->GetChannelSwitchDelay());
654 }
655 }
656 }
657
658 if (extension.IsStrictlyPositive())
659 {
660 NS_LOG_DEBUG("Restarting the timer, check again in " << extension.As(Time::US));
662 Simulator::Schedule(extension,
664 this,
665 linkId,
666 stopReason);
667 return;
668 }
669
670 // no need to wait further, switch the main PHY back to the preferred link and unregister
671 // the PHY listener from the aux PHY
672 const auto elapsed = Simulator::Now() - m_mainPhySwitchInfo.start;
673 const auto isSwitching = GetStaMac()->GetDevice()->GetPhy(GetMainPhyId())->IsStateSwitching();
675 EmlsrSwitchMainPhyBackTrace(elapsed, stopReason, isSwitching));
676 // if scheduled, invoke CheckNavAndCcaLastPifs(), which will just restart channel access
678 {
681 }
683}
684
685void
687 EmlsrMainPhySwitchTrace&& traceInfo)
688{
690 {
692 linkId,
693 std::forward<EmlsrMainPhySwitchTrace>(traceInfo));
694 return;
695 }
696
697 NS_LOG_FUNCTION(this << linkId << traceInfo.GetName());
698
699 NS_ABORT_MSG_IF(m_switchAuxPhy, "This method can only be called when SwitchAuxPhy is false");
700
702 {
703 return;
704 }
705
707 false,
709 std::forward<EmlsrMainPhySwitchTrace>(traceInfo));
710}
711
712void
714{
715 NS_LOG_FUNCTION(this);
716
718 {
719 return; // nothing to do
720 }
721
722 // a busy event occurred, check if the main PHY has to switch back to the preferred link
723 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(GetMainPhyId());
724 auto linkId = GetStaMac()->GetLinkForPhy(GetMainPhyId());
725
726 if (!linkId.has_value())
727 {
729 linkId = m_mainPhySwitchInfo.to;
730 NS_LOG_DEBUG("Main PHY is switching to link " << +linkId.value());
731 }
732
733 const auto delay =
734 Simulator::GetDelayLeft(m_switchMainPhyBackEvent) + mainPhy->GetChannelSwitchDelay();
735 if (auto reason = GetStaMac()->GetChannelAccessManager(*linkId)->GetExpectedAccessWithin(delay);
737 {
738 SwitchMainPhyBackDelayExpired(*linkId, reason);
739 }
740}
741
742void
750
751void
759
760bool
762{
763 NS_LOG_FUNCTION(this << linkId << aci << delay.As(Time::US));
764
765 // the aux PHY is not TX capable; check if main PHY has to switch to the aux PHY's link
766 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
767
768 // if main PHY is not operating on a link and is trying to start a (DL or UL) TXOP, then do
769 // not request another switch
771 (!m_interruptSwitching || m_mainPhySwitchInfo.reason == "DlTxopIcfReceivedByAuxPhy" ||
772 m_mainPhySwitchInfo.reason == "UlTxopAuxPhyNotTxCapable"))
773 {
774 NS_LOG_DEBUG("Main PHY is not operating on any link and cannot switch to another link");
775 return false;
776 }
777
778 // if the main PHY is already trying to get access on a link, do not request another switch
780 {
781 NS_LOG_DEBUG("Main PHY is trying to get access on another link");
782 return false;
783 }
784
785 // delay until the earliest time the main PHY can access medium on the aux PHY link
786 auto minDelay = mainPhy->GetChannelSwitchDelay();
787 if (!m_useAuxPhyCca && (GetChannelForAuxPhy(linkId).GetTotalWidth() <
788 GetChannelForMainPhy(linkId).GetTotalWidth()))
789 {
790 // cannot use aux PHY CCA
791 const auto pifs = GetStaMac()->GetWifiPhy(linkId)->GetPifs();
792 if (m_switchMainPhyBackDelay < pifs)
793 {
795 "Main PHY has to perform CCA but switch main PHY back delay is less than PIFS");
796 return false;
797 }
798 minDelay += pifs;
799 }
800 minDelay = std::max(delay, minDelay);
801
802 if (const auto elapsed = GetElapsedMediumSyncDelayTimer(linkId);
803 elapsed && MediumSyncDelayNTxopsExceeded(linkId) &&
804 (GetMediumSyncDuration() - *elapsed > minDelay))
805 {
806 NS_LOG_DEBUG("No more TXOP attempts allowed on aux PHY link and MSD timer still running");
807 return false;
808 }
809
810 // DoGetDelayUntilAccessRequest has already checked if the main PHY is receiving an ICF and
811 // above it is checked whether we can request another switch while already switching
812 if (const auto state = mainPhy->GetState()->GetState();
813 state != WifiPhyState::IDLE && state != WifiPhyState::CCA_BUSY &&
814 state != WifiPhyState::RX && state != WifiPhyState::SWITCHING)
815 {
816 NS_LOG_DEBUG("Cannot request main PHY to switch when in state " << state);
817 return false;
818 }
819
820 // if the AC that is about to get channel access on the aux PHY link has no frames to send on
821 // that link, do not request the main PHY to switch
822 if (!GetStaMac()->GetQosTxop(aci)->HasFramesToTransmit(linkId))
823 {
824 NS_LOG_DEBUG("No frames of " << aci << " to send on link " << +linkId);
825 return false;
826 }
827
828 // if user has configured to skip the check related to the expected channel access time on
829 // the main PHY link and the AC that is about to gain access on the aux PHY link has a priority
830 // greater than or equal to the minimum priority that has been configured, switch the main PHY
832 {
833 NS_LOG_DEBUG("Skipping check related to the expected channel access time on main PHY link");
834 return true;
835 }
836
837 const auto mainPhyLinkId = GetStaMac()->GetLinkForPhy(mainPhy);
838 if (!mainPhyLinkId.has_value())
839 {
841 NS_LOG_DEBUG("The main PHY is not connected to any link");
842 // we don't know when the main PHY will be connected to the link it is switching to, nor
843 // which backoff value it will possibly generate; therefore, request it to switch to the
844 // aux PHY link
845 return true;
846 }
847
848 // let AC X be the AC that is about to gain channel access on the aux PHY link, request to
849 // switch the main PHY if we do not expect any AC, with priority higher than or equal to that
850 // of AC X and with frames to send on the main PHY link, to gain channel access on the main PHY
851 // link before AC X is able to start transmitting on the aux PHY link.
852
853 const auto now = Simulator::Now();
854
855 for (const auto& [acIndex, ac] : wifiAcList)
856 {
857 // ignore ACs with lower priority than the AC that is about to get access on aux PHY link
858 if (acIndex < aci)
859 {
860 continue;
861 }
862
863 const auto edca = GetStaMac()->GetQosTxop(acIndex);
864 const auto backoffEnd =
865 GetStaMac()->GetChannelAccessManager(*mainPhyLinkId)->GetBackoffEndFor(edca);
866 NS_LOG_DEBUG("Backoff end for " << acIndex
867 << " on main PHY link: " << backoffEnd.As(Time::US));
868
869 if ((backoffEnd <= now + minDelay) && edca->HasFramesToTransmit(*mainPhyLinkId))
870 {
871 NS_LOG_DEBUG(acIndex << " is expected to gain access on link " << +mainPhyLinkId.value()
872 << " sooner than " << aci << " on link " << +linkId);
873 return false;
874 }
875 }
876
877 return true;
878}
879
880void
882{
883 NS_LOG_FUNCTION(this << linkId << aci);
884
886 "This function should only be called if aux PHY is not TX capable");
887 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
888
889 if (mainPhy->IsStateSwitching() && m_mainPhySwitchInfo.to == linkId)
890 {
891 // the main PHY is switching to the link on which the aux PHY gained a TXOP. This can
892 // happen, e.g., if the main PHY was requested to switch to that link before the backoff
893 // counter reached zero. Or, this can happen in case of internal collision: the first AC
894 // requests the main PHY to switch and the second one finds the main PHY to be switching.
895 // In both cases, we do nothing because we have already scheduled the necessary actions
896 NS_LOG_DEBUG("Main PHY is already switching to link " << +linkId);
897 return;
898 }
899
900 if (RequestMainPhyToSwitch(linkId, aci, Time{0}))
901 {
902 const auto auxPhy = GetStaMac()->GetWifiPhy(linkId);
903 const auto pifs = auxPhy->GetSifs() + auxPhy->GetSlot();
904
905 // schedule actions to take based on CCA sensing for a PIFS
906 if (m_useAuxPhyCca || GetChannelForAuxPhy(linkId).GetTotalWidth() >=
907 GetChannelForMainPhy(linkId).GetTotalWidth())
908 {
909 // use aux PHY CCA in the last PIFS interval before main PHY switch end
910 NS_LOG_DEBUG("Schedule CCA check at the end of main PHY switch");
911 m_ccaLastPifs = Simulator::Schedule(mainPhy->GetChannelSwitchDelay(),
913 this,
914 auxPhy,
915 linkId,
916 GetStaMac()->GetQosTxop(aci));
917 }
918 else
919 {
920 // use main PHY CCA in the last PIFS interval after main PHY switch end
921 NS_LOG_DEBUG("Schedule CCA check a PIFS after the end of main PHY switch");
922 m_ccaLastPifs = Simulator::Schedule(mainPhy->GetChannelSwitchDelay() + pifs,
924 this,
925 mainPhy,
926 linkId,
927 GetStaMac()->GetQosTxop(aci));
928 }
929
930 // switch main PHY
931 Time remNav{0};
932 if (const auto mainPhyLinkId = GetStaMac()->GetLinkForPhy(mainPhy))
933 {
934 auto mainPhyNavEnd = GetStaMac()->GetChannelAccessManager(*mainPhyLinkId)->GetNavEnd();
935 remNav = Max(remNav, mainPhyNavEnd - Simulator::Now());
936 }
937
938 SwitchMainPhy(linkId,
939 false,
942
943 // if SwitchAuxPhy is false, the main PHY must stay for some time on this link to check if
944 // it gets channel access. The timer is stopped if a DL or UL TXOP is started. When the
945 // timer expires, the main PHY switches back to the preferred link
946 if (!m_switchAuxPhy)
947 {
950 Simulator::Schedule(mainPhy->GetChannelSwitchDelay() + m_switchMainPhyBackDelay,
952 this,
953 linkId,
954 std::nullopt);
955 // start checking PHY activity on the link the main PHY is switching to
956 RegisterListener(auxPhy);
957 }
958 return;
959 }
960
961 // Determine if and when we need to request channel access again for the aux PHY based on
962 // the main PHY state.
963 // Note that, if we have requested the main PHY to switch (above), the function has returned
964 // and the EHT FEM will start a TXOP if medium is idle for a PIFS interval preceding/following
965 // the end of the main PHY channel switch.
966 // If the main PHY has been requested to switch by another aux PHY, this aux PHY will request
967 // channel access again when we have completed the CCA assessment on the other link.
968 // If the state is switching, CCA_BUSY or RX, then we request channel access again for the
969 // aux PHY when the main PHY state is back to IDLE.
970 // If the state is TX, it means that the main PHY is involved in a TXOP. Do nothing because
971 // the channel access will be requested when unblocking links at the end of the TXOP.
972 // If the state is IDLE, then either no AC has traffic to send or the backoff on the link
973 // of the main PHY is shorter than the channel switch delay. In the former case, do
974 // nothing because channel access will be triggered when new packets arrive; in the latter
975 // case, do nothing because the main PHY will start a TXOP and at the end of such TXOP
976 // links will be unblocked and the channel access requested on all links
977
978 std::optional<Time> delay;
979
981 {
982 delay = std::max(Simulator::GetDelayLeft(m_ccaLastPifs),
984 }
985 else if (mainPhy->GetState()->GetLastTime(
986 {WifiPhyState::SWITCHING, WifiPhyState::CCA_BUSY, WifiPhyState::RX}) ==
988 {
989 delay = mainPhy->GetDelayUntilIdle();
990 }
991
992 NS_LOG_DEBUG("Main PHY state is " << mainPhy->GetState()->GetState());
993 auto edca = GetStaMac()->GetQosTxop(aci);
994 edca->NotifyChannelReleased(linkId); // to set access to NOT_REQUESTED
995
996 if (!delay.has_value())
997 {
998 NS_LOG_DEBUG("Do nothing");
999 return;
1000 }
1001
1002 NS_LOG_DEBUG("Schedule channel access request on link "
1003 << +linkId << " at time " << (Simulator::Now() + *delay).As(Time::NS));
1004 Simulator::Schedule(*delay, [=]() {
1005 edca->StartAccessAfterEvent(linkId,
1008 });
1009}
1010
1011void
1013 AcIndex aci,
1014 const Time& delay)
1015{
1016 NS_LOG_FUNCTION(this << linkId << aci << delay.As(Time::US));
1017
1019 {
1020 NS_LOG_DEBUG("Nothing to do if aux PHY is TX capable");
1021 return;
1022 }
1023
1024 if (!delay.IsStrictlyPositive())
1025 {
1026 NS_LOG_DEBUG("Do nothing if delay is not strictly positive");
1027 return;
1028 }
1029
1030 if (GetEhtFem(linkId)->UsingOtherEmlsrLink())
1031 {
1032 NS_LOG_DEBUG("Do nothing because another EMLSR link is being used");
1033 return;
1034 }
1035
1037 {
1038 NS_LOG_DEBUG("Do nothing because a frame is being received on another EMLSR link");
1039 return;
1040 }
1041
1042 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
1043 auto phy = GetStaMac()->GetWifiPhy(linkId);
1044
1045 if (!phy || phy == mainPhy)
1046 {
1047 NS_LOG_DEBUG("No aux PHY is operating on link " << +linkId);
1048 return;
1049 }
1050
1051 if (!RequestMainPhyToSwitch(linkId, aci, delay))
1052 {
1053 NS_LOG_DEBUG("Chosen not to request the main PHY to switch");
1054 if (const auto untilIdle = mainPhy->GetDelayUntilIdle();
1055 untilIdle.IsStrictlyPositive() && untilIdle < delay)
1056 {
1057 NS_LOG_DEBUG("Retrying in " << untilIdle.As(Time::US));
1058 Simulator::Schedule(untilIdle,
1060 this,
1061 linkId,
1062 aci,
1063 delay - untilIdle);
1064 }
1065 return;
1066 }
1067
1068 // switch main PHY
1069
1070 // use aux PHY CCA (if allowed) if the backoff has already counted down to zero on the aux PHY
1071 // link when the main PHY completes the switch
1072 const auto edca = GetStaMac()->GetQosTxop(aci);
1073 const auto auxPhy = GetStaMac()->GetWifiPhy(linkId);
1074 const auto switchDelay = mainPhy->GetChannelSwitchDelay();
1075 const auto auxPhyCcaCanBeUsed =
1078
1079 // check expected channel access delay when switch is completed
1080 Simulator::Schedule(switchDelay, [=, this]() {
1081 // this is scheduled before starting the main PHY switch, hence it is executed before the
1082 // main PHY is connected to the aux PHY link
1083
1085 {
1086 // if SwitchAuxPhy is false and the switch main PHY back timer is not running, it means
1087 // that the channel switch was interrupted, hence there is nothing to check
1088 return;
1089 }
1090
1091 const auto backoffEnd =
1092 GetStaMac()->GetChannelAccessManager(linkId)->GetBackoffEndFor(edca);
1093 const auto pifs = GetStaMac()->GetWifiPhy(linkId)->GetPifs();
1094 const auto now = Simulator::Now();
1095
1096 // In case aux PHY CCA can be used and the backoff has not yet reached zero, no NAV and CCA
1097 // check is needed. The channel width that will be used is the width of the aux PHY if less
1098 // than a PIFS remains until the backoff reaches zero, and the width of the main PHY,
1099 // otherwise. If aux PHY CCA can be used and the backoff has already reached zero, a NAV and
1100 // CCA check is needed.
1101
1102 if (auxPhyCcaCanBeUsed && backoffEnd < now)
1103 {
1104 /**
1105 * use aux PHY CCA in the last PIFS interval before main PHY switch end
1106 *
1107 * Backoff Switch
1108 * end end (now)
1109 * ──────────┴─────────┴──────────
1110 * |---- PIFS ----|
1111 */
1112 CheckNavAndCcaLastPifs(auxPhy, linkId, edca);
1113 }
1114 else if (!auxPhyCcaCanBeUsed && (backoffEnd - now <= pifs))
1115 {
1116 /**
1117 * the remaining backoff time (if any) when the main PHY completes the switch is shorter
1118 * than or equal to a PIFS, thus the main PHY performs CCA in the last PIFS interval
1119 * after switch end
1120 *
1121 * Switch Backoff Backoff Switch
1122 * end (now) end end end (now)
1123 * ──────────┴─────────┴────────── ──────────┴─────────┴──────────
1124 * |---- PIFS ----| |---- PIFS ----|
1125 */
1126 NS_LOG_DEBUG("Schedule CCA check a PIFS after the end of main PHY switch");
1129 this,
1130 mainPhy,
1131 linkId,
1132 edca);
1133 }
1134 else if (WifiExpectedAccessReason reason;
1135 !m_switchAuxPhy &&
1136 (reason = GetStaMac()->GetChannelAccessManager(linkId)->GetExpectedAccessWithin(
1138 mainPhy->GetChannelSwitchDelay())) !=
1140 {
1141 NS_LOG_DEBUG("No AC is expected to get backoff soon, switch main PHY back");
1142 SwitchMainPhyBackDelayExpired(linkId, reason);
1143 }
1144 });
1145
1146 Time remNav{0};
1147 if (const auto mainPhyLinkId = GetStaMac()->GetLinkForPhy(mainPhy))
1148 {
1149 auto mainPhyNavEnd = GetStaMac()->GetChannelAccessManager(*mainPhyLinkId)->GetNavEnd();
1150 remNav = Max(remNav, mainPhyNavEnd - Simulator::Now());
1151 }
1152
1153 SwitchMainPhy(linkId,
1154 false,
1156 EmlsrUlTxopAuxPhyNotTxCapableTrace(aci, delay, remNav));
1157
1158 // if SwitchAuxPhy is false, the main PHY must stay for some time on this link to check if it
1159 // gets channel access. The timer is stopped if a DL or UL TXOP is started. When the timer
1160 // expires, the main PHY switches back to the preferred link
1161 if (!m_switchAuxPhy)
1162 {
1167 this,
1168 linkId,
1169 std::nullopt);
1170 // start checking PHY activity on the link the main PHY is switching to
1171 RegisterListener(auxPhy);
1172 }
1173}
1174
1175} // 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
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:96
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition time.cc:409
bool IsStrictlyPositive() const
Exactly equivalent to t > 0.
Definition nstime.h:342
@ US
microsecond
Definition nstime.h:109
@ MS
millisecond
Definition nstime.h:108
@ NS
nanosecond
Definition nstime.h:110
AttributeValue implementation for Time.
Definition nstime.h:1456
static constexpr bool DIDNT_HAVE_FRAMES_TO_TRANSMIT
no packet available for transmission was in the queue
Definition txop.h:411
static constexpr bool CHECK_MEDIUM_BUSY
generation of backoff (also) depends on the busy/idle state of the medium
Definition txop.h:413
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 IsBlockAckReq() const
Return true if the header is a BlockAckRequest header.
bool IsCts() const
Return true if the header is a CTS header.
Mac48Address GetAddr1() const
Return the address in the Address 1 field.
bool IsTrigger() const
Return true if the header is a Trigger header.
bool IsBlockAck() const
Return true if the header is a BlockAck header.
bool IsData() const
Return true if the Type is DATA.
bool IsRts() const
Return true if the header is a RTS header.
uint8_t GetPhyId() const
Get the index allocated to this PHY.
Definition wifi-phy.cc:681
void UnregisterListener(const std::shared_ptr< WifiPhyListener > &listener)
Definition wifi-phy.cc:510
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:1457
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Definition nstime.h:1477
#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:1381
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...