diff --git a/CHANGES.md b/CHANGES.md index afdab9ee0..cc7c41c88 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -16,6 +16,8 @@ This file is a best-effort approach to solving this issue; we will do our best b ### New API +* (wifi) Added a new `EarlyTxopEndDetect` attribute to `EhtFrameExchangeManager` to control whether the Duration/ID value of the frame being transmitted or received by a device shall be used to early detect the end of an ongoing TXOP (held by another device). + ### Changes to existing API * (antenna) Reformatted documentation diff --git a/src/wifi/model/eht/eht-frame-exchange-manager.cc b/src/wifi/model/eht/eht-frame-exchange-manager.cc index 53fac4168..810368628 100644 --- a/src/wifi/model/eht/eht-frame-exchange-manager.cc +++ b/src/wifi/model/eht/eht-frame-exchange-manager.cc @@ -53,10 +53,17 @@ NS_OBJECT_ENSURE_REGISTERED(EhtFrameExchangeManager); TypeId EhtFrameExchangeManager::GetTypeId() { - static TypeId tid = TypeId("ns3::EhtFrameExchangeManager") - .SetParent() - .AddConstructor() - .SetGroupName("Wifi"); + static TypeId tid = + TypeId("ns3::EhtFrameExchangeManager") + .SetParent() + .AddConstructor() + .SetGroupName("Wifi") + .AddAttribute("EarlyTxopEndDetect", + "Whether the Duration/ID value of the frame being transmitted " + "or received can be used to early detect an ongoing TXOP end.", + BooleanValue(true), + MakeBooleanAccessor(&EhtFrameExchangeManager::m_earlyTxopEndDetect), + MakeBooleanChecker()); return tid; } @@ -1780,7 +1787,7 @@ EhtFrameExchangeManager::UpdateTxopEndOnTxStart(Time txDuration, Time durationId // the response) delay = m_txTimer.GetDelayLeft(); } - else if (durationId <= m_phy->GetSifs()) + else if (m_earlyTxopEndDetect && durationId <= m_phy->GetSifs()) { // the TX timer is not running, hence no response is expected, and the Duration/ID value // is less than or equal to a SIFS; the TXOP will end after this transmission @@ -1794,8 +1801,11 @@ EhtFrameExchangeManager::UpdateTxopEndOnTxStart(Time txDuration, Time durationId // after the end of this PPDU, hence we need to postpone the TXOP end in order to // get the PHY-RXSTART.indication delay = txDuration + m_phy->GetSifs() + m_phy->GetSlot() + EMLSR_RX_PHY_START_DELAY; - // TXOP end cannot be beyond the period protected via Duration/ID - delay = Min(delay, txDuration + durationId); + if (m_earlyTxopEndDetect) + { + // TXOP end cannot be beyond the period protected via Duration/ID + delay = Min(delay, txDuration + durationId); + } } NS_LOG_DEBUG("Expected TXOP end=" << (Simulator::Now() + delay).As(Time::S)); @@ -1839,7 +1849,7 @@ EhtFrameExchangeManager::UpdateTxopEndOnRxEnd(Time durationId) // if the Duration/ID of the received frame is less than a SIFS, the TXOP // is terminated - if (durationId <= m_phy->GetSifs()) + if (m_earlyTxopEndDetect && durationId <= m_phy->GetSifs()) { NS_LOG_DEBUG("Assume TXOP ended based on Duration/ID value"); TxopEnd(m_txopHolder); @@ -1849,8 +1859,11 @@ EhtFrameExchangeManager::UpdateTxopEndOnRxEnd(Time durationId) // we may send a response after a SIFS or we may receive another frame after a SIFS. // Postpone the TXOP end by considering the latter (which takes longer) auto delay = m_phy->GetSifs() + m_phy->GetSlot() + EMLSR_RX_PHY_START_DELAY; - // TXOP end cannot be beyond the period protected via Duration/ID - delay = Min(delay, durationId); + if (m_earlyTxopEndDetect) + { + // TXOP end cannot be beyond the period protected via Duration/ID + delay = Min(delay, durationId); + } NS_LOG_DEBUG("Expected TXOP end=" << (Simulator::Now() + delay).As(Time::S)); m_ongoingTxopEnd = Simulator::Schedule(delay, &EhtFrameExchangeManager::TxopEnd, this, m_txopHolder); diff --git a/src/wifi/model/eht/eht-frame-exchange-manager.h b/src/wifi/model/eht/eht-frame-exchange-manager.h index 1e906e751..c14b2e424 100644 --- a/src/wifi/model/eht/eht-frame-exchange-manager.h +++ b/src/wifi/model/eht/eht-frame-exchange-manager.h @@ -247,6 +247,9 @@ class EhtFrameExchangeManager : public HeFrameExchangeManager */ bool UnblockEmlsrLinksIfAllowed(Mac48Address address, bool checkThisLink); + bool m_earlyTxopEndDetect; ///< whether the Duration/ID value of the frame being transmitted + ///< or received can be used to early detect an ongoing TXOP end + private: /** * @return whether the received ICF must be dropped because we are unable to process it diff --git a/src/wifi/test/wifi-emlsr-basic-exchanges-test.cc b/src/wifi/test/wifi-emlsr-basic-exchanges-test.cc index fa65201ce..50b64851e 100644 --- a/src/wifi/test/wifi-emlsr-basic-exchanges-test.cc +++ b/src/wifi/test/wifi-emlsr-basic-exchanges-test.cc @@ -2711,9 +2711,12 @@ EmlsrUlTxopTest::CheckResults() "Fourth data frame not transmitted on the same width as RTS"); } -EmlsrUlOfdmaTest::EmlsrUlOfdmaTest(bool enableBsrp) - : EmlsrOperationsTestBase("Check UL OFDMA operations with an EMLSR client"), +EmlsrUlOfdmaTest::EmlsrUlOfdmaTest(bool enableBsrp, bool protectSingleExchange) + : EmlsrOperationsTestBase("Check UL OFDMA operations with an EMLSR client (enableBsrp=" + + std::to_string(enableBsrp) + ", protectSingleExchange=" + + std::to_string(protectSingleExchange) + ")"), m_enableBsrp(enableBsrp), + m_protectSingleExchange(protectSingleExchange), m_txPsdusPos(0), m_startAccessReq(0) { @@ -2730,6 +2733,11 @@ void EmlsrUlOfdmaTest::DoSetup() { Config::SetDefault("ns3::WifiPhy::ChannelSwitchDelay", TimeValue(m_transitionDelay.at(0))); + Config::SetDefault("ns3::QosFrameExchangeManager::ProtectSingleExchange", + BooleanValue(m_protectSingleExchange)); + Config::SetDefault("ns3::DefaultEmlsrManager::SwitchAuxPhy", BooleanValue(false)); + Config::SetDefault("ns3::EhtFrameExchangeManager::EarlyTxopEndDetect", + BooleanValue(!(m_enableBsrp && m_protectSingleExchange))); EmlsrOperationsTestBase::DoSetup(); @@ -2762,6 +2770,7 @@ EmlsrUlOfdmaTest::Transmit(Ptr mac, { // this is the first Trigger Frame sent after the AP requested channel access // through the Multi-user scheduler and it is an ICF for the EMLSR client + m_1stTfLinkId = linkId; m_txPsdusPos = m_txPsdus.size() - 1; auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, @@ -2792,6 +2801,37 @@ EmlsrUlOfdmaTest::Transmit(Ptr mac, } break; + case WIFI_MAC_QOSDATA_NULL: + if (linkId == m_1stTfLinkId && + psdu->GetAddr2() == m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress()) + { + NS_TEST_EXPECT_MSG_EQ(m_enableBsrp, + true, + "EMLSR client is not expected to send a QoS null on the same " + "link as the first TF when BSRP is disabled"); + + // the Duration/ID of the QoS null frame sent by the EMLSR client after the first + // Trigger Frame on the same link is zero if and only if ProtectSingleExchange is true + NS_TEST_EXPECT_MSG_EQ(psdu->GetDuration().IsZero(), + m_protectSingleExchange, + "Unexpected Duration/ID (" << psdu->GetDuration() + << ") when ProtectSingleExchange=" + << m_protectSingleExchange); + + const auto txDuration = + WifiPhy::CalculateTxDuration(psduMap, + txVector, + mac->GetWifiPhy(linkId)->GetPhyBand()); + Simulator::Schedule(txDuration + MAX_PROPAGATION_DELAY, [=, this]() { + auto ehtFem = StaticCast( + m_staMacs[0]->GetFrameExchangeManager(linkId)); + NS_TEST_EXPECT_MSG_EQ(ehtFem->GetOngoingTxopEndEvent().IsPending(), + true, + "After QoS Null frame, the TXOP is not expected to be ended"); + }); + } + break; + case WIFI_MAC_CTL_BACKRESP: if (!m_startAccessReq.IsZero() && Simulator::Now() >= m_startAccessReq) { @@ -3080,8 +3120,14 @@ WifiEmlsrBasicExchangesTestSuite::WifiEmlsrBasicExchangesTestSuite() TestCase::Duration::QUICK); } - AddTestCase(new EmlsrUlOfdmaTest(false), TestCase::Duration::QUICK); - AddTestCase(new EmlsrUlOfdmaTest(true), TestCase::Duration::QUICK); + for (const auto enableBsrp : {true, false}) + { + for (const auto protectSingleExchange : {true, false}) + { + AddTestCase(new EmlsrUlOfdmaTest(enableBsrp, protectSingleExchange), + TestCase::Duration::QUICK); + } + } } static WifiEmlsrBasicExchangesTestSuite g_wifiEmlsrBasicExchangesTestSuite; ///< the test suite diff --git a/src/wifi/test/wifi-emlsr-basic-exchanges-test.h b/src/wifi/test/wifi-emlsr-basic-exchanges-test.h index 313d2735f..16c8c8961 100644 --- a/src/wifi/test/wifi-emlsr-basic-exchanges-test.h +++ b/src/wifi/test/wifi-emlsr-basic-exchanges-test.h @@ -392,6 +392,12 @@ class EmlsrUlTxopTest : public EmlsrOperationsTestBase * (AP MLD has blocked transmissions to the EMLSR client upon preparing the first Trigger Frame) * - the buffer status reported in QoS Null frames is as expected * - the EMLSR client sends a QoS Data frame in a TB PPDU + * + * It is also checked that, when sending BSRP TF is enabled and the single protection mechanism is + * used, the QoS Null frames sent in response to the BSRP TF acting as ICF have a Duration/ID of + * zero but the EMLSR client does not consider the TXOP ended when the transmission of the QoS Null + * frame ends (because in this case the ns3::EhtFrameExchangeManager::EarlyTxopEndDetect attribute + * is set to false) and therefore correctly receives the Basic Trigger Frame sent after a SIFS. */ class EmlsrUlOfdmaTest : public EmlsrOperationsTestBase { @@ -400,8 +406,9 @@ class EmlsrUlOfdmaTest : public EmlsrOperationsTestBase * Constructor * * @param enableBsrp whether MU scheduler sends BSRP TFs + * @param protectSingleExchange whether single protection mechanism is used */ - EmlsrUlOfdmaTest(bool enableBsrp); + EmlsrUlOfdmaTest(bool enableBsrp, bool protectSingleExchange); protected: void DoSetup() override; @@ -420,9 +427,11 @@ class EmlsrUlOfdmaTest : public EmlsrOperationsTestBase private: void StartTraffic() override; - bool m_enableBsrp; //!< whether MU scheduler sends BSRP TFs - std::size_t m_txPsdusPos; //!< position in the vector of TX PSDUs of the first ICF - Time m_startAccessReq; //!< start time of the first AP MLD access request via MU scheduler + bool m_enableBsrp; //!< whether MU scheduler sends BSRP TFs + bool m_protectSingleExchange; //!< whether single protection mechanism is used + std::size_t m_txPsdusPos; //!< position in the vector of TX PSDUs of the first ICF + Time m_startAccessReq; //!< start time of the first AP MLD access request via MU scheduler + std::optional m_1stTfLinkId; //!< ID of the link on which the first TF is sent }; /**