From d05d89952304dbbf6c099fbd651f01082fc78074 Mon Sep 17 00:00:00 2001 From: Stefano Avallone Date: Fri, 29 Mar 2024 19:12:38 +0100 Subject: [PATCH] wifi: Improve logic for main PHY to start TXOP with non-TX capable aux PHY --- src/wifi/model/eht/advanced-emlsr-manager.cc | 321 ++++++++++++++---- src/wifi/model/eht/advanced-emlsr-manager.h | 49 ++- .../model/eht/eht-frame-exchange-manager.cc | 38 +-- src/wifi/test/wifi-emlsr-test.cc | 30 +- src/wifi/test/wifi-emlsr-test.h | 12 +- 5 files changed, 317 insertions(+), 133 deletions(-) diff --git a/src/wifi/model/eht/advanced-emlsr-manager.cc b/src/wifi/model/eht/advanced-emlsr-manager.cc index ea863a80e..d206d0c54 100644 --- a/src/wifi/model/eht/advanced-emlsr-manager.cc +++ b/src/wifi/model/eht/advanced-emlsr-manager.cc @@ -15,6 +15,8 @@ #include "ns3/wifi-net-device.h" #include "ns3/wifi-phy.h" +#include + namespace ns3 { @@ -48,7 +50,24 @@ AdvancedEmlsrManager::GetTypeId() "switching to another link.", BooleanValue(false), MakeBooleanAccessor(&AdvancedEmlsrManager::m_interruptSwitching), - MakeBooleanChecker()); + MakeBooleanChecker()) + .AddAttribute("UseAuxPhyCca", + "Whether the CCA performed in the last PIFS interval by a non-TX " + "capable aux PHY should be used when the main PHY ends switching to " + "the aux PHY's link to determine whether TX can start or not (and what " + "bandwidth can be used for transmission) independently of whether the " + "aux PHY bandwidth is smaller than the main PHY bandwidth or not.", + BooleanValue(false), + MakeBooleanAccessor(&AdvancedEmlsrManager::m_useAuxPhyCca), + MakeBooleanChecker()) + .AddAttribute("SwitchMainPhyBackDelay", + "Duration of the timer started in case of non-TX capable aux PHY (that " + "does not switch link) when medium is sensed busy during the PIFS " + "interval preceding/following the main PHY switch end. When the timer " + "expires, the main PHY is switched back to the primary link.", + TimeValue(MilliSeconds(5)), + MakeTimeAccessor(&AdvancedEmlsrManager::m_switchMainPhyBackDelay), + MakeTimeChecker()); return tid; } @@ -317,6 +336,174 @@ AdvancedEmlsrManager::GetDelayUnlessMainPhyTakesOverUlTxop(uint8_t linkId) return Time{0}; } +void +AdvancedEmlsrManager::CheckNavAndCcaLastPifs(Ptr phy, uint8_t linkId, Ptr edca) +{ + NS_LOG_FUNCTION(this << phy->GetPhyId() << linkId << edca->GetAccessCategory()); + + const auto caManager = GetStaMac()->GetChannelAccessManager(linkId); + const auto pifs = phy->GetSifs() + phy->GetSlot(); + + const auto isBusy = caManager->IsBusy(); // check NAV and CCA on primary20 + // check CCA on the entire channel + auto width = caManager->GetLargestIdlePrimaryChannel(pifs, Simulator::Now()); + + if (!isBusy && width > 0) + { + // medium idle, start TXOP + width = std::min(width, GetChannelForMainPhy(linkId).GetTotalWidth()); + + // if this function is called at the end of the main PHY switch, it is executed before the + // main PHY is connected to this link in order to use the CCA information of the aux PHY. + // Schedule now the TXOP start so that we first connect the main PHY to this link. + m_ccaLastPifs = Simulator::ScheduleNow([=, this]() { + if (GetEhtFem(linkId)->HeFrameExchangeManager::StartTransmission(edca, width)) + { + NotifyUlTxopStart(linkId); + } + else if (!m_switchAuxPhy) + { + // switch main PHY back to primary link if SwitchAuxPhy is false + SwitchMainPhyBackToPrimaryLink(linkId); + } + }); + } + else + { + // medium busy, restart channel access + NS_LOG_DEBUG("Medium busy in the last PIFS interval"); + edca->NotifyChannelReleased(linkId); // to set access to NOT_REQUESTED + edca->StartAccessAfterEvent(linkId, + Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT, + Txop::CHECK_MEDIUM_BUSY); + + // the main PHY must stay for some time on this link to check if it gets channel access. + // The timer is stopped if a DL or UL TXOP is started. When the timer expires, the main PHY + // switches back to the preferred link if SwitchAuxPhy is false + m_switchMainPhyBackEvent = Simulator::Schedule(m_switchMainPhyBackDelay, [this, linkId]() { + if (!m_switchAuxPhy) + { + SwitchMainPhyBackToPrimaryLink(linkId); + } + }); + } +} + +void +AdvancedEmlsrManager::DoNotifyIcfReceived(uint8_t linkId) +{ + NS_LOG_FUNCTION(this << linkId); + m_switchMainPhyBackEvent.Cancel(); + m_ccaLastPifs.Cancel(); +} + +void +AdvancedEmlsrManager::DoNotifyUlTxopStart(uint8_t linkId) +{ + NS_LOG_FUNCTION(this << linkId); + m_switchMainPhyBackEvent.Cancel(); + m_ccaLastPifs.Cancel(); +} + +bool +AdvancedEmlsrManager::RequestMainPhyToSwitch(uint8_t linkId, AcIndex aci) +{ + NS_LOG_FUNCTION(this << linkId << aci); + + // the aux PHY is not TX capable; check if main PHY has to switch to the aux PHY's link + auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId); + const auto mainPhyLinkId = GetStaMac()->GetLinkForPhy(mainPhy); + + // if main PHY is not operating on a link, it is switching, hence do not request another switch + if (!mainPhyLinkId.has_value()) + { + NS_LOG_DEBUG("Main PHY is not operating on any link"); + return false; + } + + // if the main PHY is already trying to get access on a link, do not request another switch + if (m_ccaLastPifs.IsPending() || m_switchMainPhyBackEvent.IsPending()) + { + NS_LOG_DEBUG("Main PHY is trying to get access on another link"); + return false; + } + + switch (mainPhy->GetState()->GetState()) + { + case WifiPhyState::IDLE: + // proceed to try requesting main PHY to switch + break; + case WifiPhyState::CCA_BUSY: + // if the main PHY is receiving the PHY header of a PPDU, we decide to proceed or give up + // based on the AllowUlTxopInRx attribute + if (mainPhy->IsReceivingPhyHeader() && !m_allowUlTxopInRx) + { + NS_LOG_DEBUG("Main PHY receiving PHY header and AllowUlTxopInRx is false"); + return false; + } + break; + case WifiPhyState::RX: + if (auto macHdr = GetEhtFem(*mainPhyLinkId)->GetReceivedMacHdr()) + { + // information on the MAC header of the PSDU being received is available; if we cannot + // use it or the main PHY is receiving an ICF, give up requesting main PHY to switch + if (const auto& hdr = macHdr->get(); + !m_useNotifiedMacHdr || + (hdr.IsTrigger() && (hdr.GetAddr1().IsBroadcast() || + hdr.GetAddr1() == GetEhtFem(*mainPhyLinkId)->GetAddress()))) + { + NS_LOG_DEBUG("Receiving an ICF or cannot use MAC header information"); + return false; + } + } + // information on the MAC header of the PSDU being received is not available, we decide to + // proceed or give up based on the AllowUlTxopInRx attribute + else if (!m_allowUlTxopInRx) + { + NS_LOG_DEBUG("Receiving PSDU, no MAC header information, AllowUlTxopInRx is false"); + return false; + } + break; + default: + NS_LOG_DEBUG("Cannot request main PHY to switch when in state " + << mainPhy->GetState()->GetState()); + return false; + } + + // request to switch main PHY if we expect the main PHY to get channel access on this link + // more quickly, i.e., if ALL the ACs with queued frames (that can be transmitted on the link + // on which the main PHY is currently operating) and with priority higher than or equal to + // that of the AC for which Aux PHY gained TXOP have their backoff counter greater than the + // channel switch delay plus PIFS + + auto requestSwitch = false; + const auto now = Simulator::Now(); + + for (const auto& [acIndex, ac] : wifiAcList) + { + if (auto edca = GetStaMac()->GetQosTxop(acIndex); + acIndex >= aci && edca->HasFramesToTransmit(linkId)) + { + requestSwitch = true; + + const auto backoffEnd = + GetStaMac()->GetChannelAccessManager(*mainPhyLinkId)->GetBackoffEndFor(edca); + NS_LOG_DEBUG("Backoff end for " << acIndex + << " on primary link: " << backoffEnd.As(Time::US)); + + if (backoffEnd <= now + mainPhy->GetChannelSwitchDelay() + + GetStaMac()->GetWifiPhy(linkId)->GetPifs() && + edca->HasFramesToTransmit(*mainPhyLinkId)) + { + requestSwitch = false; + break; + } + } + } + + return requestSwitch; +} + void AdvancedEmlsrManager::SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId, AcIndex aci) { @@ -324,106 +511,90 @@ AdvancedEmlsrManager::SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId, AcIndex NS_ASSERT_MSG(!m_auxPhyTxCapable, "This function should only be called if aux PHY is not TX capable"); - - // the aux PHY is not TX capable; check if main PHY has to switch to the aux PHY's link auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId); - // if the main PHY is idle, switch main PHY if we expect the main PHY to get channel access on - // this link more quicky, i.e., if ALL the ACs with queued frames and with priority higher than - // or equal to that of the AC for which Aux PHY gained TXOP have their backoff counter greater - // than the channel switch delay plus PIFS - - auto requestSwitch = false; - - if (mainPhy->IsStateIdle()) + if (RequestMainPhyToSwitch(linkId, aci)) { - auto mainPhyLinkId = GetStaMac()->GetLinkForPhy(mainPhy); - NS_ASSERT(mainPhyLinkId.has_value()); + const auto auxPhy = GetStaMac()->GetWifiPhy(linkId); + const auto pifs = auxPhy->GetSifs() + auxPhy->GetSlot(); - // update backoff on main PHY link for all ACs - GetStaMac() - ->GetChannelAccessManager(*mainPhyLinkId) - ->NeedBackoffUponAccess(GetStaMac()->GetQosTxop(AC_BE), - Txop::HAD_FRAMES_TO_TRANSMIT, - Txop::CHECK_MEDIUM_BUSY); - - for (const auto& [acIndex, ac] : wifiAcList) + // schedule actions to take based on CCA sensing for a PIFS + if (m_useAuxPhyCca || GetChannelForAuxPhy(linkId).GetTotalWidth() >= + GetChannelForMainPhy(linkId).GetTotalWidth()) { - if (auto edca = GetStaMac()->GetQosTxop(acIndex); - acIndex >= aci && edca->HasFramesToTransmit(linkId)) - { - requestSwitch = true; - - auto backoffEnd = - GetStaMac()->GetChannelAccessManager(*mainPhyLinkId)->GetBackoffEndFor(edca); - NS_LOG_DEBUG("Backoff end for " << acIndex - << " on primary link: " << backoffEnd.As(Time::US)); - - if (backoffEnd <= Simulator::Now() + mainPhy->GetChannelSwitchDelay() + - GetStaMac()->GetWifiPhy(linkId)->GetPifs() && - edca->HasFramesToTransmit(*mainPhyLinkId)) - { - requestSwitch = false; - break; - } - } + // use aux PHY CCA in the last PIFS interval before main PHY switch end + NS_LOG_DEBUG("Schedule CCA check at the end of main PHY switch"); + m_ccaLastPifs = Simulator::Schedule(mainPhy->GetChannelSwitchDelay(), + &AdvancedEmlsrManager::CheckNavAndCcaLastPifs, + this, + auxPhy, + linkId, + GetStaMac()->GetQosTxop(aci)); + } + else + { + // use main PHY CCA in the last PIFS interval after main PHY switch end + NS_LOG_DEBUG("Schedule CCA check a PIFS after the end of main PHY switch"); + m_ccaLastPifs = Simulator::Schedule(mainPhy->GetChannelSwitchDelay() + pifs, + &AdvancedEmlsrManager::CheckNavAndCcaLastPifs, + this, + mainPhy, + linkId, + GetStaMac()->GetQosTxop(aci)); } - } - if ((mainPhy->IsStateCcaBusy() && !mainPhy->IsReceivingPhyHeader()) || - (mainPhy->IsStateIdle() && requestSwitch)) - { // switch main PHY - SwitchMainPhy(linkId, false, RESET_BACKOFF, REQUEST_ACCESS); - + SwitchMainPhy(linkId, false, RESET_BACKOFF, DONT_REQUEST_ACCESS); return; } // Determine if and when we need to request channel access again for the aux PHY based on // the main PHY state. // Note that, if we have requested the main PHY to switch (above), the function has returned - // and the EHT FEM will start a TXOP if the medium is idle for a PIFS interval following + // and the EHT FEM will start a TXOP if medium is idle for a PIFS interval preceding/following // the end of the main PHY channel switch. - // If the state is switching, but we have not requested the main PHY to switch, then we - // request channel access again for the aux PHY a PIFS after that the main PHY state is back - // to IDLE (to avoid stealing the main PHY from the non-primary link which the main PHY is - // switching to), and then we will determine if the main PHY has to switch link. - // If the state is CCA_BUSY, the medium is busy but the main PHY is not receiving a PPDU. - // In this case, we request channel access again for the aux PHY a PIFS after that the main - // PHY state is back to IDLE, and then we will determine if the main PHY has to switch link. - // If the state is TX or RX, it means that the main PHY is involved in a TXOP. In this - // case, do nothing because the channel access will be requested when unblocking links - // at the end of the TXOP. + // If the main PHY has been requested to switch by another aux PHY, this aux PHY will request + // channel access again when we have completed the CCA assessment on the other link. + // If the state is switching, CCA_BUSY or RX, then we request channel access again for the + // aux PHY when the main PHY state is back to IDLE. + // If the state is TX, it means that the main PHY is involved in a TXOP. Do nothing because + // the channel access will be requested when unblocking links at the end of the TXOP. // If the state is IDLE, then either no AC has traffic to send or the backoff on the link // of the main PHY is shorter than the channel switch delay. In the former case, do // nothing because channel access will be triggered when new packets arrive; in the latter // case, do nothing because the main PHY will start a TXOP and at the end of such TXOP // links will be unblocked and the channel access requested on all links - if (!mainPhy->IsStateSwitching() && !mainPhy->IsStateCcaBusy()) + Time delay{}; + + if (m_ccaLastPifs.IsPending() || m_switchMainPhyBackEvent.IsPending()) { - NS_LOG_DEBUG("Main PHY state is " << mainPhy->GetState()->GetState() << ". Do nothing"); + delay = std::max(Simulator::GetDelayLeft(m_ccaLastPifs), + Simulator::GetDelayLeft(m_switchMainPhyBackEvent)); + } + else if (mainPhy->IsStateSwitching() || mainPhy->IsStateCcaBusy() || mainPhy->IsStateRx()) + { + delay = mainPhy->GetDelayUntilIdle(); + NS_ASSERT(delay.IsStrictlyPositive()); + } + + NS_LOG_DEBUG("Main PHY state is " << mainPhy->GetState()->GetState()); + + if (delay.IsZero()) + { + NS_LOG_DEBUG("Do nothing"); return; } - auto delay = mainPhy->GetDelayUntilIdle(); - NS_ASSERT(delay.IsStrictlyPositive()); - delay += mainPhy->GetSifs() + mainPhy->GetSlot(); + auto edca = GetStaMac()->GetQosTxop(aci); + edca->NotifyChannelReleased(linkId); // to set access to NOT_REQUESTED - NS_LOG_DEBUG("Main PHY state is " << mainPhy->GetState()->GetState() - << ". Schedule channel access request on link " << +linkId - << " at time " << (Simulator::Now() + delay).As(Time::NS)); - Simulator::Schedule(delay, [=, this]() { - for (const auto& [aci, ac] : wifiAcList) - { - auto edca = GetStaMac()->GetQosTxop(aci); - if (edca->GetAccessStatus(linkId) != Txop::REQUESTED && - edca->HasFramesToTransmit(linkId)) - { - NS_LOG_DEBUG("Request channel access on link " << +linkId << " for " << aci); - GetStaMac()->GetChannelAccessManager(linkId)->RequestAccess(edca); - } - } + NS_LOG_DEBUG("Schedule channel access request on link " + << +linkId << " at time " << (Simulator::Now() + delay).As(Time::NS)); + Simulator::Schedule(delay, [=]() { + edca->StartAccessAfterEvent(linkId, + Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT, + Txop::CHECK_MEDIUM_BUSY); }); } diff --git a/src/wifi/model/eht/advanced-emlsr-manager.h b/src/wifi/model/eht/advanced-emlsr-manager.h index fbb8b29fa..180f9fca8 100644 --- a/src/wifi/model/eht/advanced-emlsr-manager.h +++ b/src/wifi/model/eht/advanced-emlsr-manager.h @@ -52,15 +52,52 @@ class AdvancedEmlsrManager : public DefaultEmlsrManager const WifiTxVector& txVector, Time psduDuration); + /** + * Use information from NAV and CCA performed by the given PHY on the given link in the last + * PIFS interval to determine whether the given EDCAF can start a TXOP. This function is + * intended to be used when the main PHY switches channel to start an UL TXOP on a link where + * channel access was obtained by a non-TX capable aux PHY. + * + * \param phy the PHY that performed CCA in the last PIFS interval + * \param linkId the ID of the given link + * \param edca the given EDCAF + */ + void CheckNavAndCcaLastPifs(Ptr phy, uint8_t linkId, Ptr edca); + + /** + * Determine whether the main PHY shall be requested to switch to the link of an aux PHY that + * has gained channel access through the given AC but it is not TX capable. + * + * \param linkId the ID of the link on which the aux PHY is operating + * \param aci the index of the given AC + * \return whether the main PHY shall be requested to switch to the link of the aux PHY + */ + bool RequestMainPhyToSwitch(uint8_t linkId, AcIndex aci); + private: void DoNotifyTxopEnd(uint8_t linkId) override; + void DoNotifyIcfReceived(uint8_t linkId) override; + void DoNotifyUlTxopStart(uint8_t linkId) override; - bool m_useNotifiedMacHdr; //!< whether to use the information about the MAC header of - //!< the MPDU being received (if notified by the PHY) - bool m_allowUlTxopInRx; //!< whether a (main or aux) PHY is allowed to start an UL - //!< TXOP if another PHY is receiving a PPDU - bool m_interruptSwitching; //!< whether a main PHY switching can be interrupted to start - //!< switching to another link + bool m_useNotifiedMacHdr; //!< whether to use the information about the MAC header of + //!< the MPDU being received (if notified by the PHY) + bool m_allowUlTxopInRx; //!< whether a (main or aux) PHY is allowed to start an UL + //!< TXOP if another PHY is receiving a PPDU + bool m_interruptSwitching; //!< whether a main PHY switching can be interrupted to start + //!< switching to another link + bool m_useAuxPhyCca; //!< whether the CCA performed in the last PIFS interval by a + //!< non-TX capable aux PHY should be used when the main PHY + //!< ends switching to the aux PHY's link to determine whether + //!< TX can start or not + Time m_switchMainPhyBackDelay; //!< duration of the timer started in case of non-TX capable aux + //!< PHY when medium is sensed busy during the PIFS interval + //!< preceding/following the main PHY switch end + EventId m_ccaLastPifs; //!< event scheduled in case of non-TX capable aux PHY to + //!< determine whether TX can be started based on whether + //!< the medium has been idle during the last PIFS interval + EventId m_switchMainPhyBackEvent; //!< event scheduled in case of non-TX capable aux PHY when + //!< medium is sensed busy during the PIFS interval + //!< preceding/following the main PHY switch end }; } // namespace ns3 diff --git a/src/wifi/model/eht/eht-frame-exchange-manager.cc b/src/wifi/model/eht/eht-frame-exchange-manager.cc index 4d364446f..2966abcd1 100644 --- a/src/wifi/model/eht/eht-frame-exchange-manager.cc +++ b/src/wifi/model/eht/eht-frame-exchange-manager.cc @@ -275,9 +275,6 @@ EhtFrameExchangeManager::StartTransmission(Ptr edca, MHz_u allowedWidth) return false; } - auto mainPhy = m_staMac->GetDevice()->GetPhy(emlsrManager->GetMainPhyId()); - const auto mainPhySwitching = mainPhy->IsStateSwitching(); - // let EMLSR manager decide whether to prevent or allow this UL TXOP if (auto delay = emlsrManager->GetDelayUntilAccessRequest( m_linkId, @@ -297,41 +294,10 @@ EhtFrameExchangeManager::StartTransmission(Ptr edca, MHz_u allowedWidth) // in case of aux PHY that is not TX capable, the main PHY can transmit if the medium is // sensed idle for a PIFS after the end of channel switch (assuming main PHY is switching) - if (m_phy != mainPhy && !emlsrManager->GetAuxPhyTxCapable()) + if (auto mainPhy = m_staMac->GetDevice()->GetPhy(emlsrManager->GetMainPhyId()); + m_phy != mainPhy && !emlsrManager->GetAuxPhyTxCapable()) { NS_LOG_DEBUG("Aux PHY is not capable of transmitting a PPDU"); - - if (!mainPhySwitching && mainPhy->IsStateSwitching()) - { - // main PHY switch has been requested by GetDelayUntilAccessRequest - const auto pifs = m_phy->GetSifs() + m_phy->GetSlot(); - auto checkMediumLastPifs = [=, this]() { - // check if the medium has been idle for the last PIFS interval - auto width = - m_staMac->GetChannelAccessManager(m_linkId)->GetLargestIdlePrimaryChannel( - pifs, - Simulator::Now()); - - if (width == 0) - { - NS_LOG_DEBUG("Medium busy in the last PIFS after channel switch end"); - edca->StartAccessAfterEvent(m_linkId, - Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT, - Txop::CHECK_MEDIUM_BUSY); - return; - } - - // medium idle, start a TXOP - if (HeFrameExchangeManager::StartTransmission(edca, width)) - { - // notify the EMLSR Manager of the UL TXOP start on an EMLSR link - emlsrManager->NotifyUlTxopStart(m_linkId); - } - }; - Simulator::Schedule(mainPhy->GetDelayUntilIdle() + pifs, checkMediumLastPifs); - } - - NotifyChannelReleased(edca); return false; } } diff --git a/src/wifi/test/wifi-emlsr-test.cc b/src/wifi/test/wifi-emlsr-test.cc index 36328eff7..ea5eee416 100644 --- a/src/wifi/test/wifi-emlsr-test.cc +++ b/src/wifi/test/wifi-emlsr-test.cc @@ -2386,7 +2386,8 @@ EmlsrUlTxopTest::EmlsrUlTxopTest(const Params& params) m_countQoSframes(0), m_countBlockAck(0), m_countRtsframes(0), - m_genBackoffIfTxopWithoutTx(params.genBackoffIfTxopWithoutTx) + m_genBackoffIfTxopWithoutTx(params.genBackoffAndUseAuxPhyCca), + m_useAuxPhyCca(params.genBackoffAndUseAuxPhyCca) { m_nEmlsrStations = 1; m_nNonEmlsrStations = 0; @@ -2419,6 +2420,7 @@ EmlsrUlTxopTest::DoSetup() Config::SetDefault("ns3::EmlsrManager::AuxPhyChannelWidth", UintegerValue(m_auxPhyChannelWidth)); Config::SetDefault("ns3::DefaultEmlsrManager::SwitchAuxPhy", BooleanValue(false)); + Config::SetDefault("ns3::AdvancedEmlsrManager::UseAuxPhyCca", BooleanValue(m_useAuxPhyCca)); Config::SetDefault("ns3::EhtConfiguration::MediumSyncDuration", TimeValue(m_mediumSyncDuration)); Config::SetDefault("ns3::EhtConfiguration::MsdMaxNTxops", UintegerValue(m_msdMaxNTxops)); @@ -2956,11 +2958,11 @@ EmlsrUlTxopTest::CheckBlockAck(const WifiConstPsduMap& psduMap, } // if the backoff on a link has expired before the end of the main PHY channel - // switch, the main PHY will be requested to switch again a PIFS after the end - // of the channel switch. Otherwise, it will be requested to switch when the - // backoff expires. + // switch, the main PHY will be requested to switch again at the first slot + // boundary after the end of the channel switch. Otherwise, it will be requested + // to switch when the backoff expires. auto expected2ndSwitchDelay = (minBackoff <= Simulator::Now()) - ? mainPhy->GetSifs() + mainPhy->GetSlot() + ? mainPhy->GetSlot() : (minBackoff - Simulator::Now()); // check that the main PHY is requested to switch to a non-primary link after @@ -2987,11 +2989,13 @@ EmlsrUlTxopTest::CheckBlockAck(const WifiConstPsduMap& psduMap, ->GetChannelAccessManager(*nonPrimLinkId) ->NeedBackoffUponAccess(acBe, true, true); // record the time the transmission of the QoS data frames must have - // started: a PIFS after end of channel switch, if the backoff counter - // on the non-primary link is null; when the backoff expires, otherwise + // started: (a PIFS after) end of channel switch, if the backoff counter + // on the non-primary link is null and UseAuxPhyCca is true (false); when + // the backoff expires, otherwise if (auto slots = acBe->GetBackoffSlots(*nonPrimLinkId); slots == 0) { - m_5thQosFrameTxTime = Simulator::Now() + mainPhy->GetPifs(); + m_5thQosFrameTxTime = + Simulator::Now() + (m_useAuxPhyCca ? Time{0} : mainPhy->GetPifs()); } else { @@ -3463,8 +3467,8 @@ EmlsrUlTxopTest::CheckResults() +m_mainPhyId, "Fifth QoS data frame should be transmitted on a non-primary link"); NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(), - m_channelWidth, - "Fifth data frame not transmitted on the channel width used by main PHY"); + (m_useAuxPhyCca ? m_auxPhyChannelWidth : m_channelWidth), + "Fifth data frame not transmitted on the correct channel width"); // Do not check the start transmission time if a backoff is generated even when no // transmission is done (if the backoff expires while the main PHY is switching, a new // backoff is generated and, before this backoff expires, the main PHY may be requested @@ -4300,13 +4304,13 @@ WifiEmlsrTestSuite::WifiEmlsrTestSuite() TestCase::Duration::QUICK); } - for (auto genBackoffIfTxopWithoutTx : {true, false}) + for (auto genBackoffAndUseAuxPhyCca : {true, false}) { AddTestCase(new EmlsrUlTxopTest( - {{0, 1, 2}, 40, 20, MicroSeconds(5504), 3, genBackoffIfTxopWithoutTx}), + {{0, 1, 2}, 40, 20, MicroSeconds(5504), 3, genBackoffAndUseAuxPhyCca}), TestCase::Duration::QUICK); AddTestCase( - new EmlsrUlTxopTest({{0, 1}, 40, 20, MicroSeconds(5504), 1, genBackoffIfTxopWithoutTx}), + new EmlsrUlTxopTest({{0, 1}, 40, 20, MicroSeconds(5504), 1, genBackoffAndUseAuxPhyCca}), TestCase::Duration::QUICK); } diff --git a/src/wifi/test/wifi-emlsr-test.h b/src/wifi/test/wifi-emlsr-test.h index d0fe5bf0a..92c3bed20 100644 --- a/src/wifi/test/wifi-emlsr-test.h +++ b/src/wifi/test/wifi-emlsr-test.h @@ -536,9 +536,13 @@ class EmlsrUlTxopTest : public EmlsrOperationsTestBase uint8_t msdMaxNTxops; //!< Max number of TXOPs that an EMLSR client is allowed //!< to attempt to initiate while the MediumSyncDelay //!< timer is running (zero indicates no limit) - bool genBackoffIfTxopWithoutTx; //!< whether the backoff should be invoked when the AC - //!< gains the right to start a TXOP but it does not - //!< transmit any frame + bool genBackoffAndUseAuxPhyCca; //!< this variable controls two boolean values that are + //!< either both set to true or both set to false; + //!< the first value controls whether the backoff should be + //!< invoked when the AC gains the right to start a TXOP + //!< but it does not transmit any frame, the second value + //!< controls whether CCA info from aux PHY is used when + //!< aux PHY is not TX capable }; /** @@ -653,6 +657,8 @@ class EmlsrUlTxopTest : public EmlsrOperationsTestBase bool m_genBackoffIfTxopWithoutTx; //!< whether the backoff should be invoked when the AC //!< gains the right to start a TXOP but it does not //!< transmit any frame + bool m_useAuxPhyCca; //!< whether CCA info from aux PHY is used when + //!< aux PHY is not TX capable std::optional m_corruptCts; //!< whether the transmitted CTS must be corrupted Time m_5thQosFrameTxTime; //!< start transmission time of the 5th QoS data frame };