From 13b0675a96fd8fc05b7a269348bd9d6bc03ad3c0 Mon Sep 17 00:00:00 2001 From: Stefano Avallone Date: Wed, 15 May 2024 18:12:52 +0200 Subject: [PATCH] wifi: Allow to continue a TXOP after a BSRP Trigger Frame --- .../model/he/he-frame-exchange-manager.cc | 102 ++++++++++++------ src/wifi/model/he/he-frame-exchange-manager.h | 10 ++ src/wifi/model/he/multi-user-scheduler.cc | 20 +++- src/wifi/model/he/multi-user-scheduler.h | 30 ++++-- src/wifi/model/he/rr-multi-user-scheduler.cc | 51 ++++++++- src/wifi/model/he/rr-multi-user-scheduler.h | 3 + 6 files changed, 172 insertions(+), 44 deletions(-) diff --git a/src/wifi/model/he/he-frame-exchange-manager.cc b/src/wifi/model/he/he-frame-exchange-manager.cc index 8213a6ac5..4b975f221 100644 --- a/src/wifi/model/he/he-frame-exchange-manager.cc +++ b/src/wifi/model/he/he-frame-exchange-manager.cc @@ -54,10 +54,17 @@ IsTrigger(const WifiConstPsduMap& psduMap) TypeId HeFrameExchangeManager::GetTypeId() { - static TypeId tid = TypeId("ns3::HeFrameExchangeManager") - .SetParent() - .AddConstructor() - .SetGroupName("Wifi"); + static TypeId tid = + TypeId("ns3::HeFrameExchangeManager") + .SetParent() + .AddConstructor() + .SetGroupName("Wifi") + .AddAttribute("ContinueTxopAfterBsrp", + "Whether to continue a TXOP a SIFS after the reception of responses " + "to a BSRP Trigger Frame when TXOP limit is zero.", + BooleanValue(false), + MakeBooleanAccessor(&HeFrameExchangeManager::m_continueTxopAfterBsrpTf), + MakeBooleanChecker()); return tid; } @@ -508,7 +515,25 @@ HeFrameExchangeManager::TransmissionSucceeded() m_sentFrameTo.erase(address); } - VhtFrameExchangeManager::TransmissionSucceeded(); + if (m_continueTxopAfterBsrpTf && m_edca && m_edca->GetTxopLimit(m_linkId).IsZero() && + m_txTimer.IsRunning() && m_txTimer.GetReason() == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF) + { + NS_LOG_DEBUG("Schedule another transmission in a SIFS after successful BSRP TF"); + bool (HeFrameExchangeManager::*fp)(Ptr, Time) = + &HeFrameExchangeManager::StartTransmission; + + // TXOP limit is null, hence the txopDuration parameter is unused + Simulator::Schedule(m_phy->GetSifs(), fp, this, m_edca, Seconds(0)); + if (m_protectedIfResponded) + { + m_protectedStas.merge(m_sentFrameTo); + } + m_sentFrameTo.clear(); + } + else + { + VhtFrameExchangeManager::TransmissionSucceeded(); + } } void @@ -795,6 +820,15 @@ HeFrameExchangeManager::SendPsduMap() // Set Duration/ID Time durationId = GetPsduDurationId(txDuration, m_txParams); + + if (m_continueTxopAfterBsrpTf && m_edca && m_edca->GetTxopLimit(m_linkId).IsZero() && + timerType == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF) + { + // add the duration of the following frame exchange to extend the NAV beyond the + // responses to the BSRP TF + durationId += m_muScheduler->GetExtraTimeForBsrpTfDurationId(m_linkId); + } + for (auto& psdu : m_psduMap) { psdu.second->SetDuration(durationId); @@ -2310,21 +2344,7 @@ HeFrameExchangeManager::ReceiveMpdu(Ptr mpdu, } NS_LOG_DEBUG("Received a QoS Null frame in a TB PPDU from " << sender); - - // remove the sender from the set of stations that are expected to send a TB PPDU - m_txTimer.GotResponseFrom(sender); - - if (m_txTimer.GetStasExpectedToRespond().empty()) - { - // we do not expect any other response - m_txTimer.Cancel(); - m_channelAccessManager->NotifyAckTimeoutResetNow(); - - NS_ASSERT(m_edca); - m_psduMap.clear(); - m_edca->ResetCw(m_linkId); - TransmissionSucceeded(); - } + ReceivedQosNullAfterBsrpTf(sender); // the received TB PPDU has been processed return; @@ -2770,21 +2790,7 @@ HeFrameExchangeManager::EndReceiveAmpdu(Ptr psdu, } NS_LOG_DEBUG("Received QoS Null frames in a TB PPDU from " << sender); - - // remove the sender from the set of stations that are expected to send a TB PPDU - m_txTimer.GotResponseFrom(sender); - - if (m_txTimer.GetStasExpectedToRespond().empty()) - { - // we do not expect any other response - m_txTimer.Cancel(); - m_channelAccessManager->NotifyAckTimeoutResetNow(); - - NS_ASSERT(m_edca); - m_psduMap.clear(); - m_edca->ResetCw(m_linkId); - TransmissionSucceeded(); - } + ReceivedQosNullAfterBsrpTf(sender); // the received TB PPDU has been processed return; @@ -2811,4 +2817,30 @@ HeFrameExchangeManager::EndReceiveAmpdu(Ptr psdu, VhtFrameExchangeManager::EndReceiveAmpdu(psdu, rxSignalInfo, txVector, perMpduStatus); } +void +HeFrameExchangeManager::ReceivedQosNullAfterBsrpTf(Mac48Address sender) +{ + NS_LOG_FUNCTION(this << sender); + + NS_ASSERT(m_txTimer.IsRunning() && + m_txTimer.GetReason() == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF); + + // remove the sender from the set of stations that are expected to send a TB PPDU + m_txTimer.GotResponseFrom(sender); + + if (m_txTimer.GetStasExpectedToRespond().empty()) + { + // we do not expect any other response + m_channelAccessManager->NotifyAckTimeoutResetNow(); + + NS_ASSERT(m_edca); + m_psduMap.clear(); + m_edca->ResetCw(m_linkId); + TransmissionSucceeded(); + // we reset the TX timer after calling TransmissionSucceeded, so that the latter can + // check whether the reason for the last timer is WAIT_QOS_NULL_AFTER_BSRP_TF + m_txTimer.Cancel(); + } +} + } // namespace ns3 diff --git a/src/wifi/model/he/he-frame-exchange-manager.h b/src/wifi/model/he/he-frame-exchange-manager.h index c1f5b3d24..b21f68053 100644 --- a/src/wifi/model/he/he-frame-exchange-manager.h +++ b/src/wifi/model/he/he-frame-exchange-manager.h @@ -341,6 +341,14 @@ class HeFrameExchangeManager : public VhtFrameExchangeManager */ void SendQosNullFramesInTbPpdu(const CtrlTriggerHeader& trigger, const WifiMacHeader& hdr); + /** + * Perform the actions required when receiving QoS Null frame(s) from the given sender after + * a BSRP Trigger Frame. + * + * \param sender the MAC address of the given sender + */ + void ReceivedQosNullAfterBsrpTf(Mac48Address sender); + Ptr m_apMac; //!< MAC pointer (null if not an AP) Ptr m_staMac; //!< MAC pointer (null if not a STA) WifiTxVector m_trigVector; //!< the TRIGVECTOR @@ -381,6 +389,8 @@ class HeFrameExchangeManager : public VhtFrameExchangeManager EventId m_multiStaBaEvent; //!< Sending a Multi-STA BlockAck event MuSnrTag m_muSnrTag; //!< Tag to attach to Multi-STA BlockAck frames bool m_triggerFrameInAmpdu; //!< True if the received A-MPDU contains an MU-BAR + bool m_continueTxopAfterBsrpTf; //!< whether to continue a TXOP a SIFS after the reception of + //!< responses to a BSRP TF when TXOP limit is zero }; } // namespace ns3 diff --git a/src/wifi/model/he/multi-user-scheduler.cc b/src/wifi/model/he/multi-user-scheduler.cc index 328a18b5d..f9095c6dd 100644 --- a/src/wifi/model/he/multi-user-scheduler.cc +++ b/src/wifi/model/he/multi-user-scheduler.cc @@ -64,7 +64,19 @@ MultiUserScheduler::GetTypeId() "channel access.", BooleanValue(true), MakeBooleanAccessor(&MultiUserScheduler::m_restartTimerUponAccess), - MakeBooleanChecker()); + MakeBooleanChecker()) + .AddAttribute("DefaultTbPpduDuration", + "Default duration of TB PPDUs solicited via a Basic Trigger Frame. " + "This value is used to compute the Duration/ID field of BSRP Trigger " + "Frames sent when the TXOP Limit is zero and the FrameExchangeManager " + "continues the TXOP a SIFS after receiving response to the BSRP TF. " + "This value shall also be used by subclasses when they have no other " + "information available to determine the TX duration of solicited PPDUs. " + "The default value roughly corresponds to half the maximum PPDU TX " + "duration.", + TimeValue(MilliSeconds(2)), + MakeTimeAccessor(&MultiUserScheduler::m_defaultTbPpduDuration), + MakeTimeChecker()); return tid; } @@ -186,6 +198,12 @@ MultiUserScheduler::GetHeFem(uint8_t linkId) const return StaticCast(m_apMac->GetFrameExchangeManager(linkId)); } +Time +MultiUserScheduler::GetExtraTimeForBsrpTfDurationId(uint8_t linkId) const +{ + return m_defaultTbPpduDuration; +} + void MultiUserScheduler::AccessReqTimeout(uint8_t linkId) { diff --git a/src/wifi/model/he/multi-user-scheduler.h b/src/wifi/model/he/multi-user-scheduler.h index 28cc7519c..a61de7009 100644 --- a/src/wifi/model/he/multi-user-scheduler.h +++ b/src/wifi/model/he/multi-user-scheduler.h @@ -111,6 +111,23 @@ class MultiUserScheduler : public Object */ UlMuInfo& GetUlMuInfo(uint8_t linkId); + /** + * When the TXOP limit is zero and the TXOP continues a SIFS after receiving a response to a + * BSRP TF, the Duration/ID field of the BSRP TF should be extended to reserve the medium + * for the frame exchange following the BSRP TF. This method is intended to return the estimated + * duration of the frame exchange following the BSRP TF (including the SIFS after the responses + * to the BSRP TF). Specifically, the base class method simply returns the default duration of + * TB PPDUs solicited via a Basic Trigger Frame. Subclasses can override this method to return + * a more accurate estimate of the time required by the following frame exchange. + * + * This method should only be called when the MU scheduler has determined that a BSRP TF has + * to be sent on the given link. + * + * \param linkId the ID of the given link + * \return the estimated duration of the frame exchange following the BSRP TF + */ + virtual Time GetExtraTimeForBsrpTfDurationId(uint8_t linkId) const; + /** * Set the duration of the interval between two consecutive requests for channel * access made by the MultiUserScheduler. @@ -177,12 +194,13 @@ class MultiUserScheduler : public Object void NotifyNewAggregate() override; void DoInitialize() override; - Ptr m_apMac; //!< the AP wifi MAC - Ptr m_edca; //!< the AC that gained channel access - Time m_availableTime; //!< the time available for frame exchange - bool m_initialFrame; //!< true if a TXOP is being started - MHz_u m_allowedWidth; //!< the allowed width for the current transmission - uint8_t m_linkId; //!< the ID of the link over which channel access has been granted + Ptr m_apMac; //!< the AP wifi MAC + Ptr m_edca; //!< the AC that gained channel access + Time m_availableTime; //!< the time available for frame exchange + bool m_initialFrame; //!< true if a TXOP is being started + MHz_u m_allowedWidth; //!< the allowed width for the current transmission + uint8_t m_linkId; //!< the ID of the link over which channel access has been granted + Time m_defaultTbPpduDuration; //!< the default duration of TB PPDUs solicited by Basic TFs private: /** diff --git a/src/wifi/model/he/rr-multi-user-scheduler.cc b/src/wifi/model/he/rr-multi-user-scheduler.cc index 66b3c4fda..392a3b0a6 100644 --- a/src/wifi/model/he/rr-multi-user-scheduler.cc +++ b/src/wifi/model/he/rr-multi-user-scheduler.cc @@ -383,6 +383,9 @@ RrMultiUserScheduler::TrySendingBsrpTf() qosNullTxDuration = Max(qosNullTxDuration, duration); } + NS_ASSERT(m_txParams.m_txDuration.has_value()); + m_triggerTxDuration = m_txParams.m_txDuration.value(); + if (m_availableTime != Time::Min()) { // TryAddMpdu only considers the time to transmit the Trigger Frame @@ -390,7 +393,6 @@ RrMultiUserScheduler::TrySendingBsrpTf() NS_ASSERT(m_txParams.m_acknowledgment && m_txParams.m_acknowledgment->acknowledgmentTime.has_value() && m_txParams.m_acknowledgment->acknowledgmentTime->IsZero()); - NS_ASSERT(m_txParams.m_txDuration.has_value()); if (*m_txParams.m_protection->protectionTime + *m_txParams.m_txDuration // BSRP TF tx time + m_apMac->GetWifiPhy(m_linkId)->GetSifs() + qosNullTxDuration > @@ -503,13 +505,15 @@ RrMultiUserScheduler::TrySendingBasicTf() return NO_TX; } + NS_ASSERT(m_txParams.m_txDuration.has_value()); + m_triggerTxDuration = m_txParams.m_txDuration.value(); + if (m_availableTime != Time::Min()) { // TryAddMpdu only considers the time to transmit the Trigger Frame NS_ASSERT(m_txParams.m_protection && m_txParams.m_protection->protectionTime.has_value()); NS_ASSERT(m_txParams.m_acknowledgment && m_txParams.m_acknowledgment->acknowledgmentTime.has_value()); - NS_ASSERT(m_txParams.m_txDuration.has_value()); maxDuration = Min(maxDuration, m_availableTime - *m_txParams.m_protection->protectionTime - @@ -582,6 +586,49 @@ RrMultiUserScheduler::TrySendingBasicTf() return UL_MU_TX; } +Time +RrMultiUserScheduler::GetExtraTimeForBsrpTfDurationId(uint8_t linkId) const +{ + auto phy = m_apMac->GetWifiPhy(linkId); + BlockAckType baType(BlockAckType::MULTI_STA); + uint16_t aid{0}; + Mac48Address staAddress; + + // we assume that a Basic Trigger Frame is sent after a BSRP Trigger Frame. In order to + // compute the TX duration of the Multi-STA BlockAck, we need to find the bitmap length + // for each STA solicited by the Trigger Frame + for (const auto& userInfo : m_trigger) + { + aid = userInfo.GetAid12(); + auto it = m_apMac->GetStaList(linkId).find(aid); + NS_ASSERT(it != m_apMac->GetStaList(linkId).cend()); + staAddress = it->second; + + // find a TID for which a BA agreement exists with the given originator + uint8_t tid = 0; + while (tid < 8 && !m_apMac->GetBaAgreementEstablishedAsRecipient(staAddress, tid)) + { + ++tid; + } + NS_ASSERT_MSG(tid < 8, "No Block Ack agreement established with originator " << staAddress); + + baType.m_bitmapLen.push_back( + m_apMac->GetBaTypeAsRecipient(staAddress, tid).m_bitmapLen.at(0)); + } + + NS_ASSERT_MSG(aid != 0, "No User Info field in the Trigger Frame"); + + auto multiStaBaTxVector = + GetWifiRemoteStationManager(linkId)->GetBlockAckTxVector(staAddress, + m_trigger.GetHeTbTxVector(aid)); + + auto multiStaBaDuration = WifiPhy::CalculateTxDuration(GetBlockAckSize(baType), + multiStaBaTxVector, + phy->GetPhyBand()); + + return m_triggerTxDuration + m_defaultTbPpduDuration + multiStaBaDuration + 3 * phy->GetSifs(); +} + void RrMultiUserScheduler::NotifyStationAssociated(uint16_t aid, Mac48Address address) { diff --git a/src/wifi/model/he/rr-multi-user-scheduler.h b/src/wifi/model/he/rr-multi-user-scheduler.h index e41606e9e..a397c9028 100644 --- a/src/wifi/model/he/rr-multi-user-scheduler.h +++ b/src/wifi/model/he/rr-multi-user-scheduler.h @@ -42,6 +42,8 @@ class RrMultiUserScheduler : public MultiUserScheduler RrMultiUserScheduler(); ~RrMultiUserScheduler() override; + Time GetExtraTimeForBsrpTfDurationId(uint8_t linkId) const override; + protected: void DoDispose() override; void DoInitialize() override; @@ -173,6 +175,7 @@ class RrMultiUserScheduler : public MultiUserScheduler Time m_maxCredits; //!< Max amount of credits a station can have CtrlTriggerHeader m_trigger; //!< Trigger Frame to send WifiMacHeader m_triggerMacHdr; //!< MAC header for Trigger Frame + Time m_triggerTxDuration{0}; //!< Trigger Frame TX duration WifiTxParameters m_txParams; //!< TX parameters };