13#include "ns3/boolean.h"
15#include "ns3/wifi-net-device.h"
16#include "ns3/wifi-phy-listener.h"
17#include "ns3/wifi-phy.h"
76 const std::vector<Time>& )
override
111 TypeId(
"ns3::AdvancedEmlsrManager")
113 .SetGroupName(
"Wifi")
115 .AddAttribute(
"AllowUlTxopInRx",
116 "Whether a (main or aux) PHY is allowed to start an UL TXOP if "
117 "another PHY is receiving a PPDU (possibly starting a DL TXOP). "
118 "If this attribute is true, the PPDU may be dropped.",
122 .AddAttribute(
"InterruptSwitch",
123 "Whether the main PHY can be interrupted while switching to start "
124 "switching to another link.",
128 .AddAttribute(
"UseAuxPhyCca",
129 "Whether the CCA performed in the last PIFS interval by a non-TX "
130 "capable aux PHY should be used when the main PHY ends switching to "
131 "the aux PHY's link to determine whether TX can start or not (and what "
132 "bandwidth can be used for transmission) independently of whether the "
133 "aux PHY bandwidth is smaller than the main PHY bandwidth or not.",
137 .AddAttribute(
"SwitchMainPhyBackDelay",
138 "Duration of the timer started in case of non-TX capable aux PHY (that "
139 "does not switch link) when medium is sensed busy during the PIFS "
140 "interval preceding/following the main PHY switch end. When the timer "
141 "expires, the main PHY is switched back to the preferred link.",
163 for (
auto phy :
GetStaMac()->GetDevice()->GetPhys())
165 phy->TraceDisconnectWithoutContext(
183 for (
const auto& linkId :
GetStaMac()->GetLinkIds())
185 GetStaMac()->GetChannelAccessManager(linkId)->TraceDisconnectWithoutContext(
194 ->GetChannelAccessManager(emlsrLinkId)
195 ->TraceConnectWithoutContext(
208 for (
auto phy :
GetStaMac()->GetDevice()->GetPhys())
210 phy->TraceConnectWithoutContext(
226 for (
const auto id :
GetStaMac()->GetLinkIds())
244 return {
false, delay};
253 return {
false,
Time{0}};
256 return {
true,
Time{0}};
265 auto linkId =
GetStaMac()->GetLinkForPhy(phy);
266 if (!linkId.has_value())
272 auto& ongoingTxopEnd =
GetEhtFem(*linkId)->GetOngoingTxopEndEvent();
279 ongoingTxopEnd.Cancel();
298 phy->GetChannelSwitchDelay()))
340 "Aux PHY next link ID should have a value when interrupting a main PHY switch");
342 const auto delay = mainPhy->IsStateSwitching() ? mainPhy->GetDelayUntilIdle() :
Time{0};
352 const auto delay = mainPhy->GetDelayUntilIdle();
356 if (!
GetEhtFem(linkId)->UsingOtherEmlsrLink())
379 auto state = mainPhy->GetState()->GetState();
383 "Main PHY cannot be in state " << state);
386 auto switchingTime = mainPhy->GetChannelSwitchDelay();
388 if (switchingTime > timeToCtsEnd)
391 NS_LOG_DEBUG(
"Not enough time for main PHY to switch link (main PHY state: "
392 << mainPhy->GetState()->GetState() <<
")");
394 return {
false, timeToCtsEnd};
401 return {
true,
Time{0}};
407 NS_LOG_FUNCTION(
this << phy->GetPhyId() << linkId << edca->GetAccessCategory());
409 const auto caManager =
GetStaMac()->GetChannelAccessManager(linkId);
410 const auto pifs = phy->GetSifs() + phy->GetSlot();
412 const auto isBusy = caManager->IsBusy();
414 auto width = caManager->GetLargestIdlePrimaryChannel(pifs,
Simulator::Now());
416 if (!isBusy && width >
MHz_u{0})
442 NS_LOG_DEBUG(
"No AC is expected to get backoff soon, switch main PHY back");
444 !mainPhy->IsStateSwitching())
453 edca->NotifyChannelReleased(linkId);
454 edca->StartAccessAfterEvent(linkId,
483 for (
const auto id :
GetStaMac()->GetLinkIds())
487 if (!phy || !
GetStaMac()->IsEmlsrLink(
id))
492 if (!
GetEhtFem(
id)->VirtualCsMediumIdle() &&
495 NS_LOG_DEBUG(
"NAV is set and TXOP holder is not the associated AP MLD on link " << +
id);
503 extension =
Max(extension, delay);
505 else if (
id == linkId && phy->IsStateIdle())
512 extension =
Max(extension, phy->GetChannelSwitchDelay());
517 if (extension.IsStrictlyPositive())
546 if (!linkId.has_value())
584 const auto mainPhyLinkId =
GetStaMac()->GetLinkForPhy(mainPhy);
587 if (!mainPhyLinkId.has_value())
596 NS_LOG_DEBUG(
"Main PHY is trying to get access on another link");
601 auto minDelay = mainPhy->GetChannelSwitchDelay();
606 minDelay +=
GetStaMac()->GetWifiPhy(linkId)->GetPifs();
608 minDelay = std::max(delay, minDelay);
614 NS_LOG_DEBUG(
"No more TXOP attempts allowed on aux PHY link and MSD timer still running");
619 if (
const auto state = mainPhy->GetState()->GetState();
622 NS_LOG_DEBUG(
"Cannot request main PHY to switch when in state " << state);
633 auto requestSwitch =
false;
638 if (
auto edca =
GetStaMac()->GetQosTxop(acIndex);
639 acIndex >= aci && edca->HasFramesToTransmit(linkId))
641 requestSwitch =
true;
643 const auto backoffEnd =
644 GetStaMac()->GetChannelAccessManager(*mainPhyLinkId)->GetBackoffEndFor(edca);
646 <<
" on preferred link: " << backoffEnd.As(
Time::US));
648 if ((backoffEnd <= now + minDelay) && edca->HasFramesToTransmit(*mainPhyLinkId))
650 requestSwitch =
false;
656 return requestSwitch;
665 "This function should only be called if aux PHY is not TX capable");
675 NS_LOG_DEBUG(
"Main PHY is already switching to link " << +linkId);
681 const auto auxPhy =
GetStaMac()->GetWifiPhy(linkId);
682 const auto pifs = auxPhy->GetSifs() + auxPhy->GetSlot();
689 NS_LOG_DEBUG(
"Schedule CCA check at the end of main PHY switch");
700 NS_LOG_DEBUG(
"Schedule CCA check a PIFS after the end of main PHY switch");
711 if (
const auto mainPhyLinkId =
GetStaMac()->GetLinkForPhy(mainPhy))
713 auto mainPhyNavEnd =
GetStaMac()->GetChannelAccessManager(*mainPhyLinkId)->GetNavEnd();
749 else if (mainPhy->IsStateSwitching() || mainPhy->IsStateCcaBusy() || mainPhy->IsStateRx())
751 delay = mainPhy->GetDelayUntilIdle();
755 NS_LOG_DEBUG(
"Main PHY state is " << mainPhy->GetState()->GetState());
763 auto edca =
GetStaMac()->GetQosTxop(aci);
764 edca->NotifyChannelReleased(linkId);
769 edca->StartAccessAfterEvent(linkId,
790 NS_LOG_DEBUG(
"Do nothing if delay is not strictly positive");
794 if (
GetEhtFem(linkId)->UsingOtherEmlsrLink())
796 NS_LOG_DEBUG(
"Do nothing because another EMLSR link is being used");
802 NS_LOG_DEBUG(
"Do nothing because a frame is being received on another EMLSR link");
807 auto phy =
GetStaMac()->GetWifiPhy(linkId);
809 if (!phy || phy == mainPhy)
811 NS_LOG_DEBUG(
"No aux PHY is operating on link " << +linkId);
817 NS_LOG_DEBUG(
"Chosen not to request the main PHY to switch");
818 if (
const auto untilIdle = mainPhy->GetDelayUntilIdle();
819 untilIdle.IsStrictlyPositive() && untilIdle < delay)
836 const auto edca =
GetStaMac()->GetQosTxop(aci);
837 const auto cam =
GetStaMac()->GetChannelAccessManager(linkId);
838 const auto auxPhy =
GetStaMac()->GetWifiPhy(linkId);
839 const auto switchDelay = mainPhy->GetChannelSwitchDelay();
841 const auto auxPhyCcaCanBeUsed =
844 const auto backoffEndBeforeSwitch = (cam->GetBackoffEndFor(edca) - now) <= switchDelay;
846 if (auxPhyCcaCanBeUsed && backoffEndBeforeSwitch)
848 NS_LOG_DEBUG(
"Schedule CCA check at the end of main PHY switch");
853 CheckNavAndCcaLastPifs(auxPhy, linkId, edca);
859 if (
const auto mainPhyLinkId =
GetStaMac()->GetLinkForPhy(mainPhy))
861 auto mainPhyNavEnd =
GetStaMac()->GetChannelAccessManager(*mainPhyLinkId)->GetNavEnd();
862 remNav =
Max(remNav, mainPhyNavEnd - now);
873 const auto accessDelay = cam->GetBackoffEndFor(edca) -
Simulator::Now();
875 if (auxPhyCcaCanBeUsed && backoffEndBeforeSwitch && accessDelay.IsNegative())
881 const auto pifs =
GetStaMac()->GetWifiPhy(linkId)->GetPifs();
885 if (accessDelay <= pifs)
888 NS_LOG_DEBUG(
"Schedule CCA check a PIFS after the end of main PHY switch");
898 mainPhy->GetChannelSwitchDelay()))
900 NS_LOG_DEBUG(
"No AC is expected to get backoff soon, switch main PHY back");
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...
void InterruptSwitchMainPhyBackTimerIfNeeded()
This method is called by the PHY listener attached to the main PHY when a switch main PHY back timer ...
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...
~AdvancedEmlsrManager() override
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 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 ...
void DoNotifyIcfReceived(uint8_t linkId) override
Notify the subclass of the reception of an initial Control frame on the given link.
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...
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.
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 the main PHY while operating on the link of an aux PHY that is not TX capab...
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 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.
void SwitchMainPhyBackDelayExpired(uint8_t linkId)
This method is called when the switch main PHY back delay timer (which is started when the main PHY s...
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
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, Time > m_switchMainPhyOnRtsTx
link ID-indexed map of the time when an RTS that requires the main PHY to switch link is expected to ...
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
bool GetExpectedAccessWithinDelay(uint8_t linkId, const Time &delay) const
Get whether channel access is expected to be granted on the given link within the given delay to an A...
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.
void NotifyTxopEnd(uint8_t linkId, bool ulTxopNotStarted=false, bool ongoingDlTxop=false)
Notify the end of a TXOP on the given link.
uint8_t m_mainPhyId
ID of main PHY (position in the vector of PHYs held by WifiNetDevice)
bool GetAuxPhyTxCapable() const
const std::set< uint8_t > & GetEmlsrLinks() const
std::optional< Time > GetElapsedMediumSyncDelayTimer(uint8_t linkId) const
Check whether the MediumSyncDelay timer is running for the STA operating on the given link.
const WifiPhyOperatingChannel & GetChannelForMainPhy(uint8_t linkId) const
void SwitchMainPhy(uint8_t linkId, bool noSwitchDelay, bool resetBackoff, bool requestAccess, EmlsrMainPhySwitchTrace &&traceInfo)
Switch channel on the Main PHY so that it operates on the given link.
static constexpr bool RESET_BACKOFF
reset backoff on main PHY switch
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.
static constexpr bool DONT_RESET_BACKOFF
do not reset backoff on main PHY switch
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 NotifyRxEndError() override
We have received the last bit of a packet for which NotifyRxStart was invoked first and,...
void NotifyTxStart(Time, dBm_u) 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
void Cancel()
This method is syntactic sugar for the ns3::Simulator::Cancel method.
bool IsPending() const
This method is syntactic sugar for !IsExpired().
virtual bool StartTransmission(Ptr< Txop > dcf, MHz_u allowedWidth)
Request the FrameExchangeManager to start a frame exchange sequence.
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.
static Time GetDelayLeft(const EventId &id)
Get the remaining time until this event will execute.
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.
static constexpr bool DIDNT_HAVE_FRAMES_TO_TRANSMIT
no packet available for transmission was in the queue
static constexpr bool CHECK_MEDIUM_BUSY
generation of backoff (also) depends on the busy/idle state of the medium
a unique identifier for an interface.
TypeId SetParent(TypeId tid)
Set the parent TypeId.
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,...
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
#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.
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 ...
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Ptr< const AttributeChecker > MakeBooleanChecker()
@ 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.
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
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...
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Time end
end of channel switching
uint8_t from
ID of the link which the main PHY is/has been leaving.
uint8_t to
ID of the link which the main PHY is moving to.
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 when a (DL or UL) TXOP ended.
Struct to trace that main PHY switched to operate on a link on which an aux PHY that is not TX capabl...