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 primary link.",
88 for (
auto phy :
GetStaMac()->GetDevice()->GetPhys())
90 phy->TraceDisconnectWithoutContext(
102 for (
auto phy :
GetStaMac()->GetDevice()->GetPhys())
104 phy->TraceConnectWithoutContext(
116 for (
const auto id :
GetStaMac()->GetLinkIds())
118 if (
id != linkId &&
GetStaMac()->IsEmlsrLink(
id))
125 phy->GetState()->GetLastTime({WifiPhyState::RX}) ==
Simulator::Now());
130 if (
const auto& hdr = macHdr->get();
132 (hdr.GetAddr1().IsBroadcast() || hdr.GetAddr1() ==
GetEhtFem(
id)->GetAddress()))
134 return {
false, phy->GetDelayUntilIdle()};
139 if (phy && phy->IsReceivingPhyHeader())
146 return {
false, phy->GetDelayUntilIdle()};
151 if (phy && phy->IsStateRx())
160 return {
false, phy->GetDelayUntilIdle()};
166 auto ongoingRxInfo =
GetEhtFem(
id)->GetOngoingRxInfo();
177 if (!ongoingRxInfo.has_value())
180 "Main PHY should have MAC header info when in RX state");
185 const auto& txVector = ongoingRxInfo->get().txVector;
191 auto macHdrDuration =
DataRate(txVector.GetMode().GetDataRate(txVector))
193 const auto timeSinceRxStart =
195 return {
false,
Max(macHdrDuration - timeSinceRxStart,
Time{0})};
202 return {
true,
Time{0}};
211 auto linkId =
GetStaMac()->GetLinkForPhy(phy);
212 if (!linkId.has_value())
218 auto& ongoingTxopEnd =
GetEhtFem(*linkId)->GetOngoingTxopEndEvent();
225 ongoingTxopEnd.Cancel();
275 "Aux PHY next link ID should have a value when interrupting a main PHY switch");
285 if (!GetEhtFem(linkId)->UsingOtherEmlsrLink())
287 SwitchMainPhy(GetMainPhyId(), false, DONT_RESET_BACKOFF, REQUEST_ACCESS);
294AdvancedEmlsrManager::GetDelayUnlessMainPhyTakesOverUlTxop(uint8_t linkId)
298 if (!m_interruptSwitching)
300 return DefaultEmlsrManager::GetDelayUnlessMainPhyTakesOverUlTxop(linkId);
303 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
304 auto state = mainPhy->GetState()->GetState();
307 state == WifiPhyState::IDLE || state == WifiPhyState::CCA_BUSY,
308 "Main PHY cannot be in state " << state);
310 auto timeToCtsEnd = GetTimeToCtsEnd(linkId);
311 auto switchingTime = mainPhy->GetChannelSwitchDelay();
313 if (switchingTime > timeToCtsEnd)
316 NS_LOG_DEBUG(
"Not enough time for main PHY to switch link (main PHY state: "
317 << mainPhy->GetState()->GetState() <<
")");
319 return {
false, timeToCtsEnd};
324 const auto delay = timeToCtsEnd - switchingTime;
327 NS_LOG_DEBUG(
"Schedule main Phy switch in " << delay.As(Time::US));
328 m_ulMainPhySwitch[linkId] = Simulator::Schedule(delay,
329 &AdvancedEmlsrManager::SwitchMainPhy,
334 DONT_REQUEST_ACCESS);
336 return {
true,
Time{0}};
342 NS_LOG_FUNCTION(
this << phy->GetPhyId() << linkId << edca->GetAccessCategory());
344 const auto caManager = GetStaMac()->GetChannelAccessManager(linkId);
345 const auto pifs = phy->GetSifs() + phy->GetSlot();
347 const auto isBusy = caManager->IsBusy();
349 auto width = caManager->GetLargestIdlePrimaryChannel(pifs, Simulator::Now());
351 if (!isBusy && width > 0)
354 width = std::min(width, GetChannelForMainPhy(linkId).GetTotalWidth());
359 m_ccaLastPifs = Simulator::ScheduleNow([=,
this]() {
360 if (GetEhtFem(linkId)->HeFrameExchangeManager::StartTransmission(edca, width))
362 NotifyUlTxopStart(linkId);
364 else if (!m_switchAuxPhy)
367 SwitchMainPhyBackToPrimaryLink(linkId);
375 edca->NotifyChannelReleased(linkId);
376 edca->StartAccessAfterEvent(linkId,
377 Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT,
378 Txop::CHECK_MEDIUM_BUSY);
383 m_switchMainPhyBackEvent = Simulator::Schedule(m_switchMainPhyBackDelay, [
this, linkId]() {
386 SwitchMainPhyBackToPrimaryLink(linkId);
393AdvancedEmlsrManager::DoNotifyIcfReceived(uint8_t linkId)
396 m_switchMainPhyBackEvent.Cancel();
397 m_ccaLastPifs.Cancel();
401AdvancedEmlsrManager::DoNotifyUlTxopStart(uint8_t linkId)
404 m_switchMainPhyBackEvent.Cancel();
405 m_ccaLastPifs.Cancel();
409AdvancedEmlsrManager::RequestMainPhyToSwitch(uint8_t linkId,
AcIndex aci)
414 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
415 const auto mainPhyLinkId = GetStaMac()->GetLinkForPhy(mainPhy);
418 if (!mainPhyLinkId.has_value())
425 if (m_ccaLastPifs.IsPending() || m_switchMainPhyBackEvent.IsPending())
427 NS_LOG_DEBUG(
"Main PHY is trying to get access on another link");
431 switch (mainPhy->GetState()->GetState())
433 case WifiPhyState::IDLE:
436 case WifiPhyState::CCA_BUSY:
439 if (mainPhy->IsReceivingPhyHeader() && !m_allowUlTxopInRx)
441 NS_LOG_DEBUG(
"Main PHY receiving PHY header and AllowUlTxopInRx is false");
445 case WifiPhyState::RX:
446 if (
auto macHdr = GetEhtFem(*mainPhyLinkId)->GetReceivedMacHdr())
450 if (
const auto& hdr = macHdr->get();
451 !m_useNotifiedMacHdr ||
452 (hdr.IsTrigger() && (hdr.GetAddr1().IsBroadcast() ||
453 hdr.GetAddr1() == GetEhtFem(*mainPhyLinkId)->GetAddress())))
455 NS_LOG_DEBUG(
"Receiving an ICF or cannot use MAC header information");
461 else if (!m_allowUlTxopInRx)
463 NS_LOG_DEBUG(
"Receiving PSDU, no MAC header information, AllowUlTxopInRx is false");
468 NS_LOG_DEBUG(
"Cannot request main PHY to switch when in state "
469 << mainPhy->GetState()->GetState());
479 auto requestSwitch =
false;
480 const auto now = Simulator::Now();
484 if (
auto edca = GetStaMac()->GetQosTxop(acIndex);
485 acIndex >= aci && edca->HasFramesToTransmit(linkId))
487 requestSwitch =
true;
489 const auto backoffEnd =
490 GetStaMac()->GetChannelAccessManager(*mainPhyLinkId)->GetBackoffEndFor(edca);
492 <<
" on primary link: " << backoffEnd.As(Time::US));
494 if (backoffEnd <= now + mainPhy->GetChannelSwitchDelay() +
495 GetStaMac()->GetWifiPhy(linkId)->GetPifs() &&
496 edca->HasFramesToTransmit(*mainPhyLinkId))
498 requestSwitch =
false;
504 return requestSwitch;
508AdvancedEmlsrManager::SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId,
AcIndex aci)
513 "This function should only be called if aux PHY is not TX capable");
514 auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
516 if (RequestMainPhyToSwitch(linkId, aci))
518 const auto auxPhy = GetStaMac()->GetWifiPhy(linkId);
519 const auto pifs = auxPhy->GetSifs() + auxPhy->GetSlot();
522 if (m_useAuxPhyCca || GetChannelForAuxPhy(linkId).GetTotalWidth() >=
523 GetChannelForMainPhy(linkId).GetTotalWidth())
526 NS_LOG_DEBUG(
"Schedule CCA check at the end of main PHY switch");
527 m_ccaLastPifs = Simulator::Schedule(mainPhy->GetChannelSwitchDelay(),
528 &AdvancedEmlsrManager::CheckNavAndCcaLastPifs,
532 GetStaMac()->GetQosTxop(aci));
537 NS_LOG_DEBUG(
"Schedule CCA check a PIFS after the end of main PHY switch");
538 m_ccaLastPifs = Simulator::Schedule(mainPhy->GetChannelSwitchDelay() + pifs,
539 &AdvancedEmlsrManager::CheckNavAndCcaLastPifs,
543 GetStaMac()->GetQosTxop(aci));
547 SwitchMainPhy(linkId,
false, RESET_BACKOFF, DONT_REQUEST_ACCESS);
570 if (m_ccaLastPifs.IsPending() || m_switchMainPhyBackEvent.IsPending())
572 delay = std::max(Simulator::GetDelayLeft(m_ccaLastPifs),
573 Simulator::GetDelayLeft(m_switchMainPhyBackEvent));
575 else if (mainPhy->IsStateSwitching() || mainPhy->IsStateCcaBusy() || mainPhy->IsStateRx())
577 delay = mainPhy->GetDelayUntilIdle();
581 NS_LOG_DEBUG(
"Main PHY state is " << mainPhy->GetState()->GetState());
589 auto edca = GetStaMac()->GetQosTxop(aci);
590 edca->NotifyChannelReleased(linkId);
593 << +linkId <<
" at time " << (Simulator::Now() + delay).As(Time::NS));
594 Simulator::Schedule(delay, [=]() {
595 edca->StartAccessAfterEvent(linkId,
596 Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT,
597 Txop::CHECK_MEDIUM_BUSY);
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
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.
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.
bool m_interruptSwitching
whether a main PHY switching can be interrupted to start switching to another link
Class for representing data rates.
Time CalculateBytesTxTime(uint32_t bytes) const
Calculate transmission time.
DefaultEmlsrManager is the default EMLSR manager.
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
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)
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.
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.
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...
#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.
Ptr< const AttributeChecker > MakeBooleanChecker()
@ CCA_BUSY
The PHY layer has sense the medium busy through the CCA mechanism.
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.