13#include "ns3/boolean.h"
15#include "ns3/wifi-net-device.h"
16#include "ns3/wifi-phy.h"
31 TypeId(
"ns3::AdvancedEmlsrManager")
35 .AddAttribute(
"UseNotifiedMacHdr",
36 "Whether to use the information about the MAC header of the MPDU "
37 "being received, if notified by the PHY.",
41 .AddAttribute(
"AllowUlTxopInRx",
42 "Whether a (main or aux) PHY is allowed to start an UL TXOP if "
43 "another PHY is receiving a PPDU (possibly starting a DL TXOP). "
44 "If this attribute is true, the PPDU may be dropped.",
48 .AddAttribute(
"InterruptSwitch",
49 "Whether the main PHY can be interrupted while switching to start "
50 "switching to another link.",
54 .AddAttribute(
"UseAuxPhyCca",
55 "Whether the CCA performed in the last PIFS interval by a non-TX "
56 "capable aux PHY should be used when the main PHY ends switching to "
57 "the aux PHY's link to determine whether TX can start or not (and what "
58 "bandwidth can be used for transmission) independently of whether the "
59 "aux PHY bandwidth is smaller than the main PHY bandwidth or not.",
63 .AddAttribute(
"SwitchMainPhyBackDelay",
64 "Duration of the timer started in case of non-TX capable aux PHY (that "
65 "does not switch link) when medium is sensed busy during the PIFS "
66 "interval preceding/following the main PHY switch end. When the timer "
67 "expires, the main PHY is switched back to the preferred link.",
88 for (
auto phy :
GetStaMac()->GetDevice()->GetPhys())
90 phy->TraceDisconnectWithoutContext(
103 for (
const auto& linkId :
GetStaMac()->GetLinkIds())
105 GetStaMac()->GetChannelAccessManager(linkId)->TraceDisconnectWithoutContext(
114 ->GetChannelAccessManager(emlsrLinkId)
115 ->TraceConnectWithoutContext(
128 for (
auto phy :
GetStaMac()->GetDevice()->GetPhys())
130 phy->TraceConnectWithoutContext(
142 for (
const auto id :
GetStaMac()->GetLinkIds())
144 if (
id != linkId &&
GetStaMac()->IsEmlsrLink(
id))
151 phy->GetState()->GetLastTime({WifiPhyState::RX}) ==
Simulator::Now());
156 if (
const auto& hdr = macHdr->get();
158 (hdr.GetAddr1().IsBroadcast() || hdr.GetAddr1() ==
GetEhtFem(
id)->GetAddress()))
160 return {
false, phy->GetDelayUntilIdle()};
165 if (phy && phy->IsReceivingPhyHeader())
172 return {
false, phy->GetDelayUntilIdle()};
177 if (phy && phy->IsStateRx())
186 return {
false, phy->GetDelayUntilIdle()};
192 auto ongoingRxInfo =
GetEhtFem(
id)->GetOngoingRxInfo();
203 if (!ongoingRxInfo.has_value())
206 "Main PHY should have MAC header info when in RX state");
211 const auto& txVector = ongoingRxInfo->get().txVector;
217 auto macHdrDuration =
DataRate(txVector.GetMode().GetDataRate(txVector))
219 const auto timeSinceRxStart =
221 return {
false,
Max(macHdrDuration - timeSinceRxStart,
Time{0})};
233 return {
false,
Time{0}};
236 return {
true,
Time{0}};
245 auto linkId =
GetStaMac()->GetLinkForPhy(phy);
246 if (!linkId.has_value())
252 auto& ongoingTxopEnd =
GetEhtFem(*linkId)->GetOngoingTxopEndEvent();
259 ongoingTxopEnd.Cancel();
309 "Aux PHY next link ID should have a value when interrupting a main PHY switch");
319 if (!GetEhtFem(linkId)->UsingOtherEmlsrLink())
321 SwitchMainPhy(GetMainPhyId(), false, DONT_RESET_BACKOFF, REQUEST_ACCESS);
328AdvancedEmlsrManager::GetDelayUnlessMainPhyTakesOverUlTxop(uint8_t linkId)
332 if (!m_interruptSwitching)
334 return DefaultEmlsrManager::GetDelayUnlessMainPhyTakesOverUlTxop(linkId);
337 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
338 auto state = mainPhy->GetState()->GetState();
341 state == WifiPhyState::IDLE || state == WifiPhyState::CCA_BUSY,
342 "Main PHY cannot be in state " << state);
344 auto timeToCtsEnd = GetTimeToCtsEnd(linkId);
345 auto switchingTime = mainPhy->GetChannelSwitchDelay();
347 if (switchingTime > timeToCtsEnd)
350 NS_LOG_DEBUG(
"Not enough time for main PHY to switch link (main PHY state: "
351 << mainPhy->GetState()->GetState() <<
")");
353 return {
false, timeToCtsEnd};
358 const auto delay = timeToCtsEnd - switchingTime;
361 NS_LOG_DEBUG(
"Schedule main Phy switch in " << delay.As(Time::US));
362 m_ulMainPhySwitch[linkId] = Simulator::Schedule(delay,
363 &AdvancedEmlsrManager::SwitchMainPhy,
368 DONT_REQUEST_ACCESS);
370 return {
true,
Time{0}};
376 NS_LOG_FUNCTION(
this << phy->GetPhyId() << linkId << edca->GetAccessCategory());
378 const auto caManager = GetStaMac()->GetChannelAccessManager(linkId);
379 const auto pifs = phy->GetSifs() + phy->GetSlot();
381 const auto isBusy = caManager->IsBusy();
383 auto width = caManager->GetLargestIdlePrimaryChannel(pifs, Simulator::Now());
385 if (!isBusy && width > 0)
388 width = std::min(width, GetChannelForMainPhy(linkId).GetTotalWidth());
393 m_ccaLastPifs = Simulator::ScheduleNow([=,
this]() {
394 if (GetEhtFem(linkId)->HeFrameExchangeManager::StartTransmission(edca, width))
396 NotifyUlTxopStart(linkId);
398 else if (!m_switchAuxPhy)
401 SwitchMainPhyBackToPreferredLink(linkId);
409 edca->NotifyChannelReleased(linkId);
410 edca->StartAccessAfterEvent(linkId,
411 Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT,
412 Txop::CHECK_MEDIUM_BUSY);
417 m_switchMainPhyBackEvent.Cancel();
418 m_switchMainPhyBackEvent = Simulator::Schedule(m_switchMainPhyBackDelay, [
this, linkId]() {
421 SwitchMainPhyBackToPreferredLink(linkId);
428AdvancedEmlsrManager::DoNotifyIcfReceived(uint8_t linkId)
431 m_switchMainPhyBackEvent.Cancel();
432 m_ccaLastPifs.Cancel();
436AdvancedEmlsrManager::DoNotifyUlTxopStart(uint8_t linkId)
439 m_switchMainPhyBackEvent.Cancel();
440 m_ccaLastPifs.Cancel();
444AdvancedEmlsrManager::RequestMainPhyToSwitch(uint8_t linkId,
AcIndex aci,
const Time& delay)
449 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
450 const auto mainPhyLinkId = GetStaMac()->GetLinkForPhy(mainPhy);
453 if (!mainPhyLinkId.has_value())
460 if (m_ccaLastPifs.IsPending() || m_switchMainPhyBackEvent.IsPending())
462 NS_LOG_DEBUG(
"Main PHY is trying to get access on another link");
466 switch (mainPhy->GetState()->GetState())
468 case WifiPhyState::IDLE:
471 case WifiPhyState::CCA_BUSY:
474 if (mainPhy->IsReceivingPhyHeader() && !m_allowUlTxopInRx)
476 NS_LOG_DEBUG(
"Main PHY receiving PHY header and AllowUlTxopInRx is false");
480 case WifiPhyState::RX:
481 if (
auto macHdr = GetEhtFem(*mainPhyLinkId)->GetReceivedMacHdr())
485 if (
const auto& hdr = macHdr->get();
486 !m_useNotifiedMacHdr ||
487 (hdr.IsTrigger() && (hdr.GetAddr1().IsBroadcast() ||
488 hdr.GetAddr1() == GetEhtFem(*mainPhyLinkId)->GetAddress())))
490 NS_LOG_DEBUG(
"Receiving an ICF or cannot use MAC header information");
496 else if (!m_allowUlTxopInRx)
498 NS_LOG_DEBUG(
"Receiving PSDU, no MAC header information, AllowUlTxopInRx is false");
503 NS_LOG_DEBUG(
"Cannot request main PHY to switch when in state "
504 << mainPhy->GetState()->GetState());
514 auto requestSwitch =
false;
515 const auto now = Simulator::Now();
519 if (
auto edca = GetStaMac()->GetQosTxop(acIndex);
520 acIndex >= aci && edca->HasFramesToTransmit(linkId))
522 requestSwitch =
true;
524 const auto backoffEnd =
525 GetStaMac()->GetChannelAccessManager(*mainPhyLinkId)->GetBackoffEndFor(edca);
527 <<
" on preferred link: " << backoffEnd.As(Time::US));
529 if (
const auto minDelay = std::max(delay,
530 mainPhy->GetChannelSwitchDelay() +
531 GetStaMac()->GetWifiPhy(linkId)->GetPifs());
532 backoffEnd <= now + minDelay && edca->HasFramesToTransmit(*mainPhyLinkId))
534 requestSwitch =
false;
540 return requestSwitch;
544AdvancedEmlsrManager::SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId,
AcIndex aci)
549 "This function should only be called if aux PHY is not TX capable");
550 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
552 if (mainPhy->IsStateSwitching() && m_mainPhySwitchInfo.to == linkId)
559 NS_LOG_DEBUG(
"Main PHY is already switching to link " << +linkId);
563 if (RequestMainPhyToSwitch(linkId, aci,
Time{0}))
565 const auto auxPhy = GetStaMac()->GetWifiPhy(linkId);
566 const auto pifs = auxPhy->GetSifs() + auxPhy->GetSlot();
569 if (m_useAuxPhyCca || GetChannelForAuxPhy(linkId).GetTotalWidth() >=
570 GetChannelForMainPhy(linkId).GetTotalWidth())
573 NS_LOG_DEBUG(
"Schedule CCA check at the end of main PHY switch");
574 m_ccaLastPifs = Simulator::Schedule(mainPhy->GetChannelSwitchDelay(),
575 &AdvancedEmlsrManager::CheckNavAndCcaLastPifs,
579 GetStaMac()->GetQosTxop(aci));
584 NS_LOG_DEBUG(
"Schedule CCA check a PIFS after the end of main PHY switch");
585 m_ccaLastPifs = Simulator::Schedule(mainPhy->GetChannelSwitchDelay() + pifs,
586 &AdvancedEmlsrManager::CheckNavAndCcaLastPifs,
590 GetStaMac()->GetQosTxop(aci));
594 SwitchMainPhy(linkId,
false, RESET_BACKOFF, DONT_REQUEST_ACCESS);
617 if (m_ccaLastPifs.IsPending() || m_switchMainPhyBackEvent.IsPending())
619 delay = std::max(Simulator::GetDelayLeft(m_ccaLastPifs),
620 Simulator::GetDelayLeft(m_switchMainPhyBackEvent));
622 else if (mainPhy->IsStateSwitching() || mainPhy->IsStateCcaBusy() || mainPhy->IsStateRx())
624 delay = mainPhy->GetDelayUntilIdle();
628 NS_LOG_DEBUG(
"Main PHY state is " << mainPhy->GetState()->GetState());
636 auto edca = GetStaMac()->GetQosTxop(aci);
637 edca->NotifyChannelReleased(linkId);
640 << +linkId <<
" at time " << (Simulator::Now() + delay).As(Time::NS));
641 Simulator::Schedule(delay, [=]() {
642 edca->StartAccessAfterEvent(linkId,
643 Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT,
644 Txop::CHECK_MEDIUM_BUSY);
649AdvancedEmlsrManager::SwitchMainPhyIfTxopToBeGainedByAuxPhy(uint8_t linkId,
655 if (m_auxPhyTxCapable)
663 NS_LOG_DEBUG(
"Do nothing if delay is not strictly positive");
667 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
668 auto phy = GetStaMac()->GetWifiPhy(linkId);
670 if (!phy || phy == mainPhy)
672 NS_LOG_DEBUG(
"No aux PHY is operating on link " << +linkId);
676 if (!RequestMainPhyToSwitch(linkId, aci, delay))
678 NS_LOG_DEBUG(
"Chosen not to request the main PHY to switch");
679 if (
const auto untilIdle = mainPhy->GetDelayUntilIdle();
680 untilIdle.IsStrictlyPositive() && untilIdle < delay)
683 Simulator::Schedule(untilIdle,
684 &AdvancedEmlsrManager::SwitchMainPhyIfTxopToBeGainedByAuxPhy,
694 SwitchMainPhy(linkId,
false, RESET_BACKOFF, DONT_REQUEST_ACCESS);
698 Simulator::Schedule(mainPhy->GetChannelSwitchDelay(), [=,
this]() {
699 const auto edca = GetStaMac()->GetQosTxop(aci);
700 const auto pifs = GetStaMac()->GetWifiPhy(linkId)->GetPifs();
701 if (GetStaMac()->GetChannelAccessManager(linkId)->GetBackoffEndFor(edca) <=
702 Simulator::Now() + pifs)
705 NS_LOG_DEBUG(
"Schedule CCA check a PIFS after the end of main PHY switch");
706 m_ccaLastPifs = Simulator::Schedule(pifs,
707 &AdvancedEmlsrManager::CheckNavAndCcaLastPifs,
718 const auto minDelay =
720 mainPhy->GetChannelSwitchDelay() + GetStaMac()->GetWifiPhy(linkId)->GetPifs());
721 m_switchMainPhyBackEvent.Cancel();
722 m_switchMainPhyBackEvent =
723 Simulator::Schedule(minDelay + m_switchMainPhyBackDelay, [
this, linkId]() {
726 SwitchMainPhyBackToPreferredLink(linkId);
AdvancedEmlsrManager is an advanced EMLSR manager.
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...
bool m_useAuxPhyCca
whether the CCA performed in the last PIFS interval by a non-TX capable aux PHY should be used when t...
~AdvancedEmlsrManager() override
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 DoNotifyTxopEnd(uint8_t linkId) override
Notify the subclass of the end of a TXOP on the given link.
void NotifyEmlsrModeChanged() override
Notify subclass that EMLSR mode changed.
static TypeId GetTypeId()
Get the type ID.
bool m_useNotifiedMacHdr
whether to use the information about the MAC header of the MPDU being received (if notified by the PH...
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.
void DoSetWifiMac(Ptr< StaWifiMac > mac) override
Allow subclasses to take actions when the MAC is set.
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
AttributeValue implementation for Boolean.
Class for representing data rates.
Time CalculateBytesTxTime(uint32_t bytes) const
Calculate transmission time.
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...
EventId m_auxPhyToSleepEvent
the event scheduled to put an Aux PHY into sleep mode
MainPhySwitchInfo m_mainPhySwitchInfo
main PHY switch info
bool m_auxPhyTxCapable
whether Aux PHYs are capable of transmitting PPDUs
Ptr< EhtFrameExchangeManager > GetEhtFem(uint8_t linkId) const
void NotifyTxopEnd(uint8_t linkId, bool ulTxopNotStarted=false, bool ongoingDlTxop=false)
Notify the end of a TXOP on the given link.
void SwitchMainPhy(uint8_t linkId, bool noSwitchDelay, bool resetBackoff, bool requestAccess)
Switch channel on the Main PHY so that it operates on the given link.
uint8_t m_mainPhyId
ID of main PHY (position in the vector of PHYs held by WifiNetDevice)
const std::set< uint8_t > & GetEmlsrLinks() const
static constexpr bool REQUEST_ACCESS
request channel access when PHY switch ends
Ptr< StaWifiMac > GetStaMac() const
uint8_t GetMainPhyId() const
static constexpr bool DONT_RESET_BACKOFF
do not reset backoff on main PHY switch
void Cancel()
This method is syntactic sugar for the ns3::Simulator::Cancel method.
bool IsPending() const
This method is syntactic sugar for !IsExpired().
virtual void DoDispose()
Destructor implementation.
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.
static Time Now()
Return the current simulation virtual time.
static EventId ScheduleNow(FUNC f, Ts &&... args)
Schedule an event to expire Now.
Simulation virtual time values and global simulation resolution.
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
bool IsStrictlyPositive() const
Exactly equivalent to t > 0.
AttributeValue implementation for Time.
a unique identifier for an interface.
TypeId SetParent(TypeId tid)
Set the parent TypeId.
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,...
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Ptr< const AttributeChecker > MakeBooleanChecker()
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
#define NS_ABORT_MSG_UNLESS(cond, msg)
Abnormal program termination if a condition is false, with a message.
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
#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.
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
AcIndex
This enumeration defines the Access Categories as an enumeration with values corresponding to the AC ...
Every class exported by the ns3 library is enclosed in the ns3 namespace.
@ CCA_BUSY
The PHY layer has sense the medium busy through the CCA mechanism.
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...
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...
Time end
end of channel switching
uint8_t from
ID of the link which the main PHY is/has been leaving.