diff --git a/src/wifi/model/eht/advanced-emlsr-manager.cc b/src/wifi/model/eht/advanced-emlsr-manager.cc index 4f6fc4f22..7d8884174 100644 --- a/src/wifi/model/eht/advanced-emlsr-manager.cc +++ b/src/wifi/model/eht/advanced-emlsr-manager.cc @@ -89,7 +89,7 @@ AdvancedEmlsrManager::DoSetWifiMac(Ptr mac) } Time -AdvancedEmlsrManager::GetDelayUntilAccessRequest(uint8_t linkId) +AdvancedEmlsrManager::DoGetDelayUntilAccessRequest(uint8_t linkId) { NS_LOG_FUNCTION(this << linkId); diff --git a/src/wifi/model/eht/advanced-emlsr-manager.h b/src/wifi/model/eht/advanced-emlsr-manager.h index 0eb47e3a5..e6f3315a4 100644 --- a/src/wifi/model/eht/advanced-emlsr-manager.h +++ b/src/wifi/model/eht/advanced-emlsr-manager.h @@ -31,11 +31,10 @@ class AdvancedEmlsrManager : public DefaultEmlsrManager AdvancedEmlsrManager(); ~AdvancedEmlsrManager() override; - Time GetDelayUntilAccessRequest(uint8_t linkId) override; - protected: void DoDispose() override; void DoSetWifiMac(Ptr mac) override; + Time DoGetDelayUntilAccessRequest(uint8_t linkId) override; /** * Possibly take actions when notified of the MAC header of the MPDU being received by the diff --git a/src/wifi/model/eht/default-emlsr-manager.cc b/src/wifi/model/eht/default-emlsr-manager.cc index 57a98372a..1e3f59af5 100644 --- a/src/wifi/model/eht/default-emlsr-manager.cc +++ b/src/wifi/model/eht/default-emlsr-manager.cc @@ -172,7 +172,7 @@ DefaultEmlsrManager::NotifyMainPhySwitch(std::optional currLinkId, } Time -DefaultEmlsrManager::GetDelayUntilAccessRequest(uint8_t linkId) +DefaultEmlsrManager::DoGetDelayUntilAccessRequest(uint8_t linkId) { NS_LOG_FUNCTION(this << linkId); return Time{0}; // start the TXOP @@ -228,7 +228,7 @@ DefaultEmlsrManager::DoNotifyTxopEnd(uint8_t linkId) } } -bool +void DefaultEmlsrManager::SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId) { NS_LOG_FUNCTION(this << linkId); @@ -280,7 +280,7 @@ DefaultEmlsrManager::SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId) // switch main PHY SwitchMainPhy(linkId, false, RESET_BACKOFF, REQUEST_ACCESS); - return true; + return; } // Determine if and when we need to request channel access again for the aux PHY based on @@ -307,7 +307,7 @@ DefaultEmlsrManager::SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId) if (!mainPhy->IsStateSwitching() && !mainPhy->IsStateCcaBusy()) { NS_LOG_DEBUG("Main PHY state is " << mainPhy->GetState()->GetState() << ". Do nothing"); - return false; + return; } auto delay = mainPhy->GetDelayUntilIdle(); @@ -329,8 +329,78 @@ DefaultEmlsrManager::SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId) } } }); +} - return false; +Time +DefaultEmlsrManager::GetTimeToCtsEnd(uint8_t linkId) const +{ + NS_LOG_FUNCTION(this << linkId); + + auto phy = GetStaMac()->GetWifiPhy(linkId); + NS_ASSERT_MSG(phy, "No PHY operating on link " << +linkId); + + // we have to check whether the main PHY can switch to take over the UL TXOP + const auto stationManager = GetStaMac()->GetWifiRemoteStationManager(linkId); + const auto bssid = GetEhtFem(linkId)->GetBssid(); + const auto allowedWidth = GetEhtFem(linkId)->GetAllowedWidth(); + + const auto rtsTxVector = stationManager->GetRtsTxVector(bssid, allowedWidth); + const auto rtsTxTime = phy->CalculateTxDuration(GetRtsSize(), rtsTxVector, phy->GetPhyBand()); + const auto ctsTxVector = stationManager->GetCtsTxVector(bssid, rtsTxVector.GetMode()); + const auto ctsTxTime = phy->CalculateTxDuration(GetCtsSize(), ctsTxVector, phy->GetPhyBand()); + + // the main PHY shall terminate the channel switch at the end of CTS reception; + // the time remaining to the end of CTS reception includes two propagation delays + return rtsTxTime + phy->GetSifs() + ctsTxTime + MicroSeconds(2 * MAX_PROPAGATION_DELAY_USEC); +} + +Time +DefaultEmlsrManager::GetDelayUnlessMainPhyTakesOverUlTxop(uint8_t linkId) +{ + NS_LOG_FUNCTION(this << linkId); + + auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId); + auto timeToCtsEnd = GetTimeToCtsEnd(linkId); + auto switchingTime = mainPhy->GetChannelSwitchDelay(); + + switch (mainPhy->GetState()->GetState()) + { + case WifiPhyState::SWITCHING: + // the main PHY is switching (to another link), hence the remaining time to + // the end of the current channel switch needs to be added up + switchingTime += mainPhy->GetDelayUntilIdle(); + [[fallthrough]]; + case WifiPhyState::RX: + case WifiPhyState::IDLE: + case WifiPhyState::CCA_BUSY: + if (switchingTime > timeToCtsEnd) + { + // switching takes longer than RTS/CTS exchange, release channel + NS_LOG_DEBUG("Not enough time for main PHY to switch link (main PHY state: " + << mainPhy->GetState()->GetState() << ")"); + // retry channel access when the CTS was expected to be received + return timeToCtsEnd; + } + break; + default: + NS_ABORT_MSG("Main PHY cannot be in state " << mainPhy->GetState()->GetState()); + } + + // TXOP can be started, schedule main PHY switch. Main PHY shall terminate the channel switch + // at the end of CTS reception + const auto delay = timeToCtsEnd - mainPhy->GetChannelSwitchDelay(); + + NS_ASSERT(delay.IsPositive()); + NS_LOG_DEBUG("Schedule main Phy switch in " << delay.As(Time::US)); + m_ulMainPhySwitch[linkId] = Simulator::Schedule(delay, + &DefaultEmlsrManager::SwitchMainPhy, + this, + linkId, + false, + RESET_BACKOFF, + DONT_REQUEST_ACCESS); + + return Time{0}; } } // namespace ns3 diff --git a/src/wifi/model/eht/default-emlsr-manager.h b/src/wifi/model/eht/default-emlsr-manager.h index ed95f7ddf..6ae1c2c06 100644 --- a/src/wifi/model/eht/default-emlsr-manager.h +++ b/src/wifi/model/eht/default-emlsr-manager.h @@ -33,17 +33,21 @@ class DefaultEmlsrManager : public EmlsrManager DefaultEmlsrManager(); ~DefaultEmlsrManager() override; - bool SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId) override; - - /** - * \param linkId the ID of the link on which TXOP is gained - * \return zero, indicating that the TXOP can be started - */ - Time GetDelayUntilAccessRequest(uint8_t linkId) override; - protected: uint8_t GetLinkToSendEmlOmn() override; std::optional ResendNotification(Ptr mpdu) override; + Time DoGetDelayUntilAccessRequest(uint8_t linkId) override; + void SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId) override; + Time GetDelayUnlessMainPhyTakesOverUlTxop(uint8_t linkId) override; + + /** + * This function is intended to be called when an aux PHY is about to transmit an RTS on + * the given link to calculate the time remaining to the end of the CTS reception. + * + * \param linkId the ID of the given link + * \return the time remaining to the end of the CTS reception + */ + Time GetTimeToCtsEnd(uint8_t linkId) const; /// Store information about a main PHY switch. struct MainPhySwitchInfo diff --git a/src/wifi/model/eht/eht-frame-exchange-manager.cc b/src/wifi/model/eht/eht-frame-exchange-manager.cc index f85e92ca3..9c71b0b8b 100644 --- a/src/wifi/model/eht/eht-frame-exchange-manager.cc +++ b/src/wifi/model/eht/eht-frame-exchange-manager.cc @@ -169,6 +169,8 @@ EhtFrameExchangeManager::StartTransmission(Ptr edca, MHz_u allowedWidth) { NS_LOG_FUNCTION(this << edca << allowedWidth); + m_allowedWidth = allowedWidth; + if (m_apMac) { for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++) @@ -273,6 +275,9 @@ 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); delay.IsStrictlyPositive()) @@ -288,98 +293,44 @@ EhtFrameExchangeManager::StartTransmission(Ptr edca, MHz_u allowedWidth) return false; } - if (auto mainPhy = m_staMac->GetDevice()->GetPhy(emlsrManager->GetMainPhyId()); - mainPhy != m_phy) + // 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()) { - // an aux PHY is operating on this link + NS_LOG_DEBUG("Aux PHY is not capable of transmitting a PPDU"); - if (!emlsrManager->GetAuxPhyTxCapable()) + if (!mainPhySwitching && mainPhy->IsStateSwitching()) { - NS_LOG_DEBUG("Aux PHY is not capable of transmitting a PPDU"); + // 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 (emlsrManager->SwitchMainPhyIfTxopGainedByAuxPhy(m_linkId)) - { - NS_ASSERT_MSG(mainPhy->IsStateSwitching(), - "SwitchMainPhyIfTxopGainedByAuxPhy returned true but main PHY is " - "not switching"); + 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; + } - 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, std::nullopt); - } - }; - Simulator::Schedule(mainPhy->GetDelayUntilIdle() + pifs, checkMediumLastPifs); - } - - NotifyChannelReleased(edca); - return false; + // 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); } - // we have to check whether the main PHY can switch to take over the UL TXOP - - const auto rtsTxVector = - GetWifiRemoteStationManager()->GetRtsTxVector(m_bssid, allowedWidth); - const auto rtsTxTime = - m_phy->CalculateTxDuration(GetRtsSize(), rtsTxVector, m_phy->GetPhyBand()); - const auto ctsTxVector = - GetWifiRemoteStationManager()->GetCtsTxVector(m_bssid, rtsTxVector.GetMode()); - const auto ctsTxTime = - m_phy->CalculateTxDuration(GetCtsSize(), ctsTxVector, m_phy->GetPhyBand()); - - // the main PHY shall terminate the channel switch at the end of CTS reception; - // the time remaining to the end of CTS reception includes two propagation delays - timeToCtsEnd = rtsTxTime + m_phy->GetSifs() + ctsTxTime + - MicroSeconds(2 * MAX_PROPAGATION_DELAY_USEC); - - auto switchingTime = mainPhy->GetChannelSwitchDelay(); - - switch (mainPhy->GetState()->GetState()) - { - case WifiPhyState::SWITCHING: - // the main PHY is switching (to another link), hence the remaining time to - // the end of the current channel switch needs to be added up - switchingTime += mainPhy->GetDelayUntilIdle(); - [[fallthrough]]; - case WifiPhyState::RX: - case WifiPhyState::IDLE: - case WifiPhyState::CCA_BUSY: - if (switchingTime <= timeToCtsEnd) - { - break; // start TXOP - } - // switching takes longer than RTS/CTS exchange, release channel - NS_LOG_DEBUG("Not enough time for main PHY to switch link (main PHY state: " - << mainPhy->GetState() << ")"); - // retry channel access when the CTS was expected to be received - NotifyChannelReleased(edca); - Simulator::Schedule(*timeToCtsEnd, - &Txop::StartAccessAfterEvent, - edca, - m_linkId, - Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT, // queued frames cannot be - // transmitted now - Txop::CHECK_MEDIUM_BUSY); // generate backoff if medium busy - return false; - default: - NS_ABORT_MSG("Main PHY cannot be in state " << mainPhy->GetState()); - } + NotifyChannelReleased(edca); + return false; } } @@ -389,7 +340,7 @@ EhtFrameExchangeManager::StartTransmission(Ptr edca, MHz_u allowedWidth) { // notify the EMLSR Manager of the UL TXOP start on an EMLSR link NS_ASSERT(m_staMac->GetEmlsrManager()); - m_staMac->GetEmlsrManager()->NotifyUlTxopStart(m_linkId, timeToCtsEnd); + m_staMac->GetEmlsrManager()->NotifyUlTxopStart(m_linkId); } if (started) diff --git a/src/wifi/model/eht/emlsr-manager.cc b/src/wifi/model/eht/emlsr-manager.cc index 91dbed60d..f468dd631 100644 --- a/src/wifi/model/eht/emlsr-manager.cc +++ b/src/wifi/model/eht/emlsr-manager.cc @@ -408,8 +408,43 @@ EmlsrManager::NotifyIcfReceived(uint8_t linkId) }); } +Time +EmlsrManager::GetDelayUntilAccessRequest(uint8_t linkId) +{ + auto phy = m_staMac->GetWifiPhy(linkId); + NS_ASSERT_MSG(phy, "No PHY operating on link " << +linkId); + + auto mainPhy = m_staMac->GetDevice()->GetPhy(m_mainPhyId); + + // check possible reasons to give up the TXOP that apply to both main PHY and aux PHYs + if (auto delay = DoGetDelayUntilAccessRequest(linkId); delay.IsStrictlyPositive()) + { + return delay; + } + + if (phy == mainPhy) + { + // no more constraints to check if medium was gained by main PHY + return Time{0}; + } + + // an aux PHY is operating on the given link; call the appropriate method depending on + // whether the aux PHY is TX capable or not + if (!m_auxPhyTxCapable) + { + SwitchMainPhyIfTxopGainedByAuxPhy(linkId); + // if the aux PHY is not TX capable, we don't have to request channel access: if the main + // PHY switches link, the UL TXOP will be started; if the main PHY does not switch, it is + // because it is going to start an UL TXOP on another link and this link will be restarted + // at the end of that UL TXOP when this link will be unblocked + return Time{0}; + } + + return GetDelayUnlessMainPhyTakesOverUlTxop(linkId); +} + void -EmlsrManager::NotifyUlTxopStart(uint8_t linkId, std::optional