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();
306 "Aux PHY next link ID should have a value when interrupting a main PHY switch");
308 const auto delay = mainPhy->IsStateSwitching() ? mainPhy->GetDelayUntilIdle() :
Time{0};
318 const auto delay = mainPhy->GetDelayUntilIdle();
322 if (!
GetEhtFem(linkId)->UsingOtherEmlsrLink())
345 auto state = mainPhy->GetState()->GetState();
349 "Main PHY cannot be in state " << state);
352 auto switchingTime = mainPhy->GetChannelSwitchDelay();
354 if (switchingTime > timeToCtsEnd)
357 NS_LOG_DEBUG(
"Not enough time for main PHY to switch link (main PHY state: "
358 << mainPhy->GetState()->GetState() <<
")");
360 return {
false, timeToCtsEnd};
367 return {
true,
Time{0}};
373 NS_LOG_FUNCTION(
this << phy->GetPhyId() << linkId << edca->GetAccessCategory());
375 const auto caManager =
GetStaMac()->GetChannelAccessManager(linkId);
376 const auto pifs = phy->GetSifs() + phy->GetSlot();
378 const auto isBusy = caManager->IsBusy();
380 auto width = caManager->GetLargestIdlePrimaryChannel(pifs,
Simulator::Now());
382 if (!isBusy && width >
MHz_u{0})
406 edca->NotifyChannelReleased(linkId);
407 edca->StartAccessAfterEvent(linkId,
447 const auto mainPhyLinkId =
GetStaMac()->GetLinkForPhy(mainPhy);
450 if (!mainPhyLinkId.has_value())
459 NS_LOG_DEBUG(
"Main PHY is trying to get access on another link");
464 if (
const auto state = mainPhy->GetState()->GetState();
467 NS_LOG_DEBUG(
"Cannot request main PHY to switch when in state " << state);
477 auto requestSwitch =
false;
482 if (
auto edca =
GetStaMac()->GetQosTxop(acIndex);
483 acIndex >= aci && edca->HasFramesToTransmit(linkId))
485 requestSwitch =
true;
487 const auto backoffEnd =
488 GetStaMac()->GetChannelAccessManager(*mainPhyLinkId)->GetBackoffEndFor(edca);
490 <<
" on preferred link: " << backoffEnd.As(
Time::US));
492 if (
const auto minDelay = std::max(delay,
493 mainPhy->GetChannelSwitchDelay() +
494 GetStaMac()->GetWifiPhy(linkId)->GetPifs());
495 backoffEnd <= now + minDelay && edca->HasFramesToTransmit(*mainPhyLinkId))
497 requestSwitch =
false;
503 return requestSwitch;
512 "This function should only be called if aux PHY is not TX capable");
522 NS_LOG_DEBUG(
"Main PHY is already switching to link " << +linkId);
528 const auto auxPhy =
GetStaMac()->GetWifiPhy(linkId);
529 const auto pifs = auxPhy->GetSifs() + auxPhy->GetSlot();
536 NS_LOG_DEBUG(
"Schedule CCA check at the end of main PHY switch");
547 NS_LOG_DEBUG(
"Schedule CCA check a PIFS after the end of main PHY switch");
558 if (
const auto mainPhyLinkId =
GetStaMac()->GetLinkForPhy(mainPhy))
560 auto mainPhyNavEnd =
GetStaMac()->GetChannelAccessManager(*mainPhyLinkId)->GetNavEnd();
596 else if (mainPhy->IsStateSwitching() || mainPhy->IsStateCcaBusy() || mainPhy->IsStateRx())
598 delay = mainPhy->GetDelayUntilIdle();
602 NS_LOG_DEBUG(
"Main PHY state is " << mainPhy->GetState()->GetState());
610 auto edca =
GetStaMac()->GetQosTxop(aci);
611 edca->NotifyChannelReleased(linkId);
616 edca->StartAccessAfterEvent(linkId,
637 NS_LOG_DEBUG(
"Do nothing if delay is not strictly positive");
641 if (
GetEhtFem(linkId)->UsingOtherEmlsrLink())
643 NS_LOG_DEBUG(
"Do nothing because another EMLSR link is being used");
649 NS_LOG_DEBUG(
"Do nothing because a frame is being received on another EMLSR link");
654 auto phy =
GetStaMac()->GetWifiPhy(linkId);
656 if (!phy || phy == mainPhy)
658 NS_LOG_DEBUG(
"No aux PHY is operating on link " << +linkId);
664 NS_LOG_DEBUG(
"Chosen not to request the main PHY to switch");
665 if (
const auto untilIdle = mainPhy->GetDelayUntilIdle();
666 untilIdle.IsStrictlyPositive() && untilIdle < delay)
681 if (
const auto mainPhyLinkId =
GetStaMac()->GetLinkForPhy(mainPhy))
683 auto mainPhyNavEnd =
GetStaMac()->GetChannelAccessManager(*mainPhyLinkId)->GetNavEnd();
696 const auto edca = GetStaMac()->GetQosTxop(aci);
697 const auto pifs = GetStaMac()->GetWifiPhy(linkId)->GetPifs();
698 if (GetStaMac()->GetChannelAccessManager(linkId)->GetBackoffEndFor(edca) <=
699 Simulator::Now() + pifs)
702 NS_LOG_DEBUG(
"Schedule CCA check a PIFS after the end of main PHY switch");
703 m_ccaLastPifs = Simulator::Schedule(pifs,
704 &AdvancedEmlsrManager::CheckNavAndCcaLastPifs,
715 const auto minDelay =
717 mainPhy->GetChannelSwitchDelay() + GetStaMac()->GetWifiPhy(linkId)->GetPifs());
718 m_switchMainPhyBackEvent.Cancel();
719 m_switchMainPhyBackEvent =
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...
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.
bool m_useNotifiedMacHdr
whether to use the information about the MAC header of the MPDU being received (if notified by the PH...
void DoNotifyUlTxopStart(uint8_t linkId) override
Notify the subclass of the start of an UL TXOP on the given link.
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 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...
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.
MainPhySwitchInfo m_mainPhySwitchInfo
main PHY switch info
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_auxPhyTxCapable
whether Aux PHYs are capable of transmitting PPDUs
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)
const std::set< uint8_t > & GetEmlsrLinks() const
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
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
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 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.
AttributeValue implementation for Time.
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.
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.
@ 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...
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 to
ID of the link which the main PHY is moving to.
uint8_t from
ID of the link which the main PHY is/has been leaving.
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...