From 0f3b78a78f0ecc1720b0b7411d451cf073658b68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Deronne?= Date: Sat, 7 Sep 2024 12:52:30 +0200 Subject: [PATCH] wifi: Extend TXOP test to verify new NAV mechanism --- src/wifi/test/wifi-txop-test.cc | 415 +++++++++++++++++++++----------- 1 file changed, 277 insertions(+), 138 deletions(-) diff --git a/src/wifi/test/wifi-txop-test.cc b/src/wifi/test/wifi-txop-test.cc index 552f6b0a3..c8d254391 100644 --- a/src/wifi/test/wifi-txop-test.cc +++ b/src/wifi/test/wifi-txop-test.cc @@ -69,8 +69,15 @@ class WifiTxopTest : public TestCase bool singleRtsPerTxop; //!< whether protection mechanism is used no more than once per TXOP bool lengthBasedRtsCtsThresh; //!< if true, use length based RTS/CTS threshold; if false, //!< use TX duration based RTS/CTS threshold - bool protectedIfResponded; //!< whether a station is assumed to be protected if replied to - //!< a frame requiring acknowledgment + bool protectedIfResponded; //!< whether a station is assumed to be protected if replied to + //!< a frame requiring acknowledgment + bool protectSingleExchange; //!< whether the Duration/ID field in frames establishing + //!< protection only covers the immediate frame exchange instead + //!< of rest of the TXOP limit + Time singleExchangeProtectionBuffer; //!< whether the NAV duration should be extended by a + //!< PIFS after the frame exchange when protection only + //!< covers the immediate frame exchange instead of + //!< rest of the TXOP limit }; /** @@ -137,27 +144,29 @@ class WifiTxopTest : public TestCase WifiTxVector txVector; ///< TX vector used to transmit the frame }; - uint16_t m_nStations; ///< number of stations - NetDeviceContainer m_staDevices; ///< container for stations' NetDevices - NetDeviceContainer m_apDevices; ///< container for AP's NetDevice - std::vector m_txPsdus; ///< transmitted PSDUs - Time m_apTxopLimit; ///< TXOP limit for AP (AC BE) - uint8_t m_staAifsn; ///< AIFSN for STAs (AC BE) - uint32_t m_staCwMin; ///< CWmin for STAs (AC BE) - uint32_t m_staCwMax; ///< CWmax for STAs (AC BE) - Time m_staTxopLimit; ///< TXOP limit for STAs (AC BE) - uint16_t m_received; ///< number of packets received by the stations - bool m_nonHt; ///< whether to use 802.11a or 802.11ax - std::size_t m_payloadSizeRtsOn; ///< size in bytes of packets protected by RTS - std::size_t m_payloadSizeRtsOff; ///< size in bytes of packets not protected by RTS - Time m_startTime; ///< time when data frame exchanges start - WifiMode m_mode; ///< wifi mode used to transmit data frames - bool m_pifsRecovery; ///< whether to use PIFS recovery - bool m_singleRtsPerTxop; ///< whether to use single RTS per TXOP - bool m_lengthBasedRtsCtsThresh; ///< whether to use length based RTS/CTS threshold - bool m_protectedIfResponded; ///< whether a STA is protected if it responds to the AP - Ptr m_apErrorModel; ///< error model to install on the AP - Ptr m_staErrorModel; ///< error model to install on a STA + uint16_t m_nStations; ///< number of stations + NetDeviceContainer m_staDevices; ///< container for stations' NetDevices + NetDeviceContainer m_apDevices; ///< container for AP's NetDevice + std::vector m_txPsdus; ///< transmitted PSDUs + Time m_apTxopLimit; ///< TXOP limit for AP (AC BE) + uint8_t m_staAifsn; ///< AIFSN for STAs (AC BE) + uint32_t m_staCwMin; ///< CWmin for STAs (AC BE) + uint32_t m_staCwMax; ///< CWmax for STAs (AC BE) + Time m_staTxopLimit; ///< TXOP limit for STAs (AC BE) + uint16_t m_received; ///< number of packets received by the stations + bool m_nonHt; ///< @copydoc Params::nonHt + std::size_t m_payloadSizeRtsOn; ///< size in bytes of packets protected by RTS + std::size_t m_payloadSizeRtsOff; ///< size in bytes of packets not protected by RTS + Time m_startTime; ///< time when data frame exchanges start + WifiMode m_mode; ///< wifi mode used to transmit data frames + bool m_pifsRecovery; ///< @copydoc Params::pifsRecovery + bool m_singleRtsPerTxop; ///< @copydoc Params::singleRtsPerTxop + bool m_lengthBasedRtsCtsThresh; ///< @copydoc Params::lengthBasedRtsCtsThresh + bool m_protectedIfResponded; ///< @copydoc Params::protectedIfResponded + bool m_protectSingleExchange; ///< @copydoc Params::protectSingleExchange + Time m_singleExchangeProtectionSurplus; ///< @copydoc Params::singleExchangeProtectionBuffer + Ptr m_apErrorModel; ///< error model to install on the AP + Ptr m_staErrorModel; ///< error model to install on a STA bool m_apCorrupted; ///< whether the frame to be corrupted by the AP has been corrupted bool m_staCorrupted; ///< whether the frame to be corrupted by the STA has been corrupted std::vector m_dlSockets; ///< packet socket address for DL traffic @@ -170,7 +179,10 @@ WifiTxopTest::WifiTxopTest(const WifiTxopTest::Params& params) " pifsRecovery=" + std::to_string(params.pifsRecovery) + " singleRtsPerTxop=" + std::to_string(params.singleRtsPerTxop) + " lengthBasedRtsCtsThresh=" + std::to_string(params.lengthBasedRtsCtsThresh) + - " protectedIfResponded=" + std::to_string(params.protectedIfResponded)), + " protectedIfResponded=" + std::to_string(params.protectedIfResponded) + + " protectSingleExchange=" + std::to_string(params.protectSingleExchange) + + " singleExchangeProtectionBuffer=" + + std::to_string(params.singleExchangeProtectionBuffer.GetMicroSeconds()) + "us"), m_nStations(3), m_apTxopLimit(MicroSeconds(4768)), m_staAifsn(4), @@ -276,6 +288,9 @@ WifiTxopTest::Transmit(std::string context, void WifiTxopTest::DoRun() { + // LogComponentEnable("WifiTxopTest", LOG_LEVEL_ALL); + NS_LOG_FUNCTION(this); + RngSeedManager::SetSeed(1); RngSeedManager::SetRun(40); int64_t streamNumber = 100; @@ -298,6 +313,10 @@ WifiTxopTest::DoRun() phy.Set("ChannelSettings", StringValue("{0, 20, BAND_5GHZ, 0}")); Config::SetDefault("ns3::QosFrameExchangeManager::PifsRecovery", BooleanValue(m_pifsRecovery)); + Config::SetDefault("ns3::QosFrameExchangeManager::ProtectSingleExchange", + BooleanValue(m_protectSingleExchange)); + Config::SetDefault("ns3::QosFrameExchangeManager::SingleExchangeProtectionSurplus", + TimeValue(m_singleExchangeProtectionSurplus)); Config::SetDefault("ns3::WifiDefaultProtectionManager::SingleRtsPerTxop", BooleanValue(m_singleRtsPerTxop)); if (m_lengthBasedRtsCtsThresh) @@ -572,15 +591,20 @@ WifiTxopTest::CheckResults() * RA: STA1 [AP] STA1 AP [STA2] STA2 AP STA3 AP STA2 AP all * * NOTE: If ProtectedIfResponded is false, the last QoS data frame is protected by RTS/CTS + * NOTE: If ProtectSingleExchange is true, CF-END frames are not transmitted + * and NAV ends once DATA+Ack is completed */ // We expect 25 frames to be transmitted if SingleRtsPerTxop is false and 22 frames (2 RTS // less, 2 CTS less, 1 more CF-End). If ProtectedIfResponded is false, there are 2 frames - // (an RTS and a CTS) more. - NS_TEST_ASSERT_MSG_EQ(m_txPsdus.size(), - (m_singleRtsPerTxop ? 24 + (m_protectedIfResponded ? 0 : 2) - : 27 + (m_protectedIfResponded ? 0 : 2)), - "Unexpected number of transmitted frames"); + // (an RTS and a CTS) more. If ProtectSingleExchange is true, there are 2 (if + // SingleRtsPerTxop is false) or 3 (if SingleRtsPerTxop is true) less frames (CF-END) + NS_TEST_ASSERT_MSG_EQ( + m_txPsdus.size(), + (m_singleRtsPerTxop + ? 21U + (m_protectedIfResponded ? 0U : 2U) + (m_protectSingleExchange ? 0U : 3U) + : 25U + (m_protectedIfResponded ? 0U : 2U) + (m_protectSingleExchange ? 0U : 2U)), + "Unexpected number of transmitted frames"); // the first frame sent after 400ms is a QoS data frame sent by the AP to STA1 without RTS/CTS txopStart = m_txPsdus[0].txStart; @@ -602,9 +626,15 @@ WifiTxopTest::CheckResults() rtsCtsTxDurationThresh, "PSDU duration expected not to exceed duration based RTS/CTS threshold"); } - NS_TEST_EXPECT_MSG_EQ(m_txPsdus[0].header.GetDuration(), - RoundDurationId(m_apTxopLimit - m_txPsdus[0].txDuration), - "Duration/ID of the first frame must cover the whole TXOP"); + NS_TEST_EXPECT_MSG_EQ( + m_txPsdus[0].header.GetDuration(), + (!m_protectSingleExchange + ? RoundDurationId(m_apTxopLimit - m_txPsdus[0].txDuration) + : sifs + m_txPsdus[1].txDuration + m_singleExchangeProtectionSurplus), + (!m_protectSingleExchange + ? "Duration/ID of the first frame must cover the whole TXOP" + : "Duration/ID of the first frame must be set to remaining time to " + "complete DATA+ACK sequence")); // a Normal Ack is sent by STA1 tEnd = m_txPsdus[0].txStart + m_txPsdus[0].txDuration; @@ -661,9 +691,15 @@ WifiTxopTest::CheckResults() rtsCtsTxDurationThresh, "PSDU duration expected not to exceed duration based RTS/CTS threshold"); } - NS_TEST_EXPECT_MSG_EQ(m_txPsdus[2].header.GetDuration(), - RoundDurationId(m_apTxopLimit - m_txPsdus[2].txDuration), - "Duration/ID of the retransmitted frame must cover the whole TXOP"); + NS_TEST_EXPECT_MSG_EQ( + m_txPsdus[2].header.GetDuration(), + (!m_protectSingleExchange + ? RoundDurationId(m_apTxopLimit - m_txPsdus[2].txDuration) + : sifs + m_txPsdus[3].txDuration + m_singleExchangeProtectionSurplus), + (!m_protectSingleExchange + ? "Duration/ID of the retransmitted frame must cover the whole TXOP" + : "Duration/ID of the retransmitted frame must be set to remaining time to " + "complete DATA+ACK sequence")); // a Normal Ack is then sent by STA1 tEnd = m_txPsdus[2].txStart + m_txPsdus[2].txDuration; @@ -706,9 +742,16 @@ WifiTxopTest::CheckResults() "PSDU duration expected not to exceed duration based RTS/CTS threshold"); } NS_TEST_EXPECT_MSG_EQ(m_txPsdus[4].header.GetDuration(), - RoundDurationId(m_apTxopLimit - (m_txPsdus[4].txStart - txopStart) - - m_txPsdus[4].txDuration), - "Duration/ID of the second frame does not cover the remaining TXOP"); + (!m_protectSingleExchange + ? RoundDurationId(m_apTxopLimit - + (m_txPsdus[4].txStart - txopStart) - + m_txPsdus[4].txDuration) + : sifs + + m_txPsdus[6].txDuration + m_singleExchangeProtectionSurplus /* ACK is 2 frames later since this DATA is first corrupted */), + (!m_protectSingleExchange + ? "Duration/ID of the second frame does not cover the remaining TXOP" + : "Duration/ID of the second frame must be set to remaining time to " + "complete DATA+ACK sequence")); // STA2 receives a corrupted frame and hence it does not send the Ack. When the AckTimeout // expires, the AP performs PIFS recovery or invoke backoff, without terminating the TXOP, @@ -754,10 +797,16 @@ WifiTxopTest::CheckResults() rtsCtsTxDurationThresh, "PSDU duration expected not to exceed duration based RTS/CTS threshold"); } - NS_TEST_EXPECT_MSG_EQ(m_txPsdus[5].header.GetDuration(), - RoundDurationId(m_apTxopLimit - (m_txPsdus[5].txStart - txopStart) - - m_txPsdus[5].txDuration), - "Duration/ID of the second frame does not cover the remaining TXOP"); + NS_TEST_EXPECT_MSG_EQ( + m_txPsdus[5].header.GetDuration(), + (!m_protectSingleExchange + ? RoundDurationId(m_apTxopLimit - (m_txPsdus[5].txStart - txopStart) - + m_txPsdus[5].txDuration) + : sifs + m_txPsdus[6].txDuration + m_singleExchangeProtectionSurplus), + (!m_protectSingleExchange + ? "Duration/ID of the second frame does not cover the remaining TXOP" + : "Duration/ID of the second frame must be set to remaining time to " + "complete DATA+ACK sequence")); // a Normal Ack is then sent by STA2 tEnd = m_txPsdus[5].txStart + m_txPsdus[5].txDuration; @@ -801,10 +850,16 @@ WifiTxopTest::CheckResults() rtsCtsTxDurationThresh, "PSDU duration expected not to exceed duration based RTS/CTS threshold"); } - NS_TEST_EXPECT_MSG_EQ(m_txPsdus[7].header.GetDuration(), - RoundDurationId(m_apTxopLimit - (m_txPsdus[7].txStart - txopStart) - - m_txPsdus[7].txDuration), - "Duration/ID of the third frame does not cover the remaining TXOP"); + NS_TEST_EXPECT_MSG_EQ( + m_txPsdus[7].header.GetDuration(), + (!m_protectSingleExchange + ? RoundDurationId(m_apTxopLimit - (m_txPsdus[7].txStart - txopStart) - + m_txPsdus[7].txDuration) + : sifs + m_txPsdus[8].txDuration + m_singleExchangeProtectionSurplus), + (!m_protectSingleExchange + ? "Duration/ID of the third frame does not cover the remaining TXOP" + : "Duration/ID of the third frame must be set to remaining time to " + "complete DATA+ACK sequence")); // a Normal Ack is then sent by STA3 tEnd = m_txPsdus[7].txStart + m_txPsdus[7].txDuration; @@ -842,9 +897,16 @@ WifiTxopTest::CheckResults() "Expected an RTS frame sent by the AP to the second station"); NS_TEST_EXPECT_MSG_EQ( m_txPsdus[9].header.GetDuration(), - RoundDurationId(m_apTxopLimit - (m_txPsdus[9].txStart - txopStart) - - m_txPsdus[9].txDuration), - "Duration/ID of the RTS before the fourth frame does not cover the remaining TXOP"); + (!m_protectSingleExchange + ? RoundDurationId(m_apTxopLimit - (m_txPsdus[9].txStart - txopStart) - + m_txPsdus[9].txDuration) + : sifs + m_txPsdus[10].txDuration + sifs + m_txPsdus[11].txDuration + sifs + + m_txPsdus[12].txDuration + m_singleExchangeProtectionSurplus), + (!m_protectSingleExchange + ? "Duration/ID of the RTS before the fourth frame does not cover the remaining " + "TXOP" + : "Duration/ID of the RTS before the fourth frame must be set to remaining time " + "to complete CTS+DATA+ACK sequence")); // a CTS is sent by STA2 tEnd = m_txPsdus[9].txStart + m_txPsdus[9].txDuration; @@ -897,10 +959,16 @@ WifiTxopTest::CheckResults() rtsCtsTxDurationThresh, "PSDU duration expected to exceed duration based RTS/CTS threshold"); } - NS_TEST_EXPECT_MSG_EQ(m_txPsdus[9].header.GetDuration(), - RoundDurationId(m_apTxopLimit - (m_txPsdus[9].txStart - txopStart) - - m_txPsdus[9].txDuration), - "Duration/ID of the fourth frame does not cover the remaining TXOP"); + NS_TEST_EXPECT_MSG_EQ( + m_txPsdus[9].header.GetDuration(), + (!m_protectSingleExchange + ? RoundDurationId(m_apTxopLimit - (m_txPsdus[9].txStart - txopStart) - + m_txPsdus[9].txDuration) + : sifs + m_txPsdus[10].txDuration + m_singleExchangeProtectionSurplus), + (!m_protectSingleExchange + ? "Duration/ID of the fourth frame does not cover the remaining TXOP" + : "Duration/ID of the fourth frame must be set to remaining time to " + "complete DATA+(Block)ACK sequence")); std::string ack(m_nonHt ? "Normal Ack" : "Block Ack"); @@ -926,20 +994,26 @@ WifiTxopTest::CheckResults() RoundDurationId(m_txPsdus[9].header.GetDuration() - sifs - m_txPsdus[10].txDuration), "Duration/ID of the " << ack << " must be derived from that of the previous frame"); - // the TXOP limit is such that enough time for sending a CF-End frame remains - tEnd = m_txPsdus[10].txStart + m_txPsdus[10].txDuration; - tStart = m_txPsdus[11].txStart; + if (!m_protectSingleExchange) + { + // the TXOP limit is such that enough time for sending a CF-End frame remains + tEnd = m_txPsdus[10].txStart + m_txPsdus[10].txDuration; + tStart = m_txPsdus[11].txStart; - NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "CF-End sent too early"); - NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "CF-End sent too late"); - NS_TEST_EXPECT_MSG_EQ(m_txPsdus[11].header.IsCfEnd(), true, "Expected a CF-End frame"); - NS_TEST_EXPECT_MSG_EQ(m_txPsdus[11].header.GetDuration(), - Seconds(0), - "Duration/ID must be set to 0 for CF-End frames"); + NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "CF-End sent too early"); + NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "CF-End sent too late"); + NS_TEST_EXPECT_MSG_EQ(m_txPsdus[11].header.IsCfEnd(), true, "Expected a CF-End frame"); + NS_TEST_EXPECT_MSG_EQ(m_txPsdus[11].header.GetDuration(), + MicroSeconds(0), + "Duration/ID must be set to 0 for CF-End frames"); + + // remove CF-END so that indices are aligned with the protectSingleExchange false case + m_txPsdus.erase(std::next(m_txPsdus.cbegin(), 11), std::next(m_txPsdus.cbegin(), 12)); + } // the CF-End frame resets the NAV on STA1, which can now transmit - tEnd = m_txPsdus[11].txStart + m_txPsdus[11].txDuration; - tStart = m_txPsdus[12].txStart; + tEnd = m_txPsdus[10].txStart + m_txPsdus[10].txDuration; + tStart = m_txPsdus[11].txStart; NS_TEST_EXPECT_MSG_GT_OR_EQ(tStart - tEnd, sifs + m_staAifsn * slot, @@ -947,53 +1021,66 @@ WifiTxopTest::CheckResults() NS_TEST_EXPECT_MSG_LT_OR_EQ(tStart - tEnd, sifs + m_staAifsn * slot + m_staCwMin * slot + tolerance, "More than AIFS+BO elapsed between two TXOPs"); - NS_TEST_EXPECT_MSG_EQ(m_txPsdus[12].header.IsQosData(), true, "Expected a QoS data frame"); - NS_TEST_EXPECT_MSG_EQ(m_txPsdus[12].header.GetAddr1(), + NS_TEST_EXPECT_MSG_EQ(m_txPsdus[11].header.IsQosData(), true, "Expected a QoS data frame"); + NS_TEST_EXPECT_MSG_EQ(m_txPsdus[11].header.GetAddr1(), apDev->GetMac()->GetAddress(), "Expected a frame sent by the first station to the AP"); if (m_lengthBasedRtsCtsThresh) { - NS_TEST_EXPECT_MSG_LT(m_txPsdus[12].size, + NS_TEST_EXPECT_MSG_LT(m_txPsdus[11].size, rtsCtsThreshold, "PSDU size expected not to exceed length based RTS/CTS threshold"); } else { NS_TEST_EXPECT_MSG_LT( - m_txPsdus[12].txDuration, + m_txPsdus[11].txDuration, rtsCtsTxDurationThresh, "PSDU duration expected not to exceed duration based RTS/CTS threshold"); } NS_TEST_EXPECT_MSG_EQ( - m_txPsdus[12].header.GetDuration(), - RoundDurationId(m_staTxopLimit - m_txPsdus[12].txDuration), - "Duration/ID of the frame sent by the first station does not cover the remaining TXOP"); + m_txPsdus[11].header.GetDuration(), + (!m_protectSingleExchange + ? RoundDurationId(m_staTxopLimit - m_txPsdus[11].txDuration) + : sifs + m_txPsdus[12].txDuration + m_singleExchangeProtectionSurplus), + (!m_protectSingleExchange + ? "Duration/ID of the frame sent by the first station does not " + "cover the remaining TXOP" + : "Duration/ID of the frame sent by the first station must be set " + "to remaining time to " + "complete DATA+ACK sequence")); // a Normal Ack is then sent by the AP - tEnd = m_txPsdus[12].txStart + m_txPsdus[12].txDuration; - tStart = m_txPsdus[13].txStart; + tEnd = m_txPsdus[11].txStart + m_txPsdus[11].txDuration; + tStart = m_txPsdus[12].txStart; NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "Ack sent too early"); NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "Ack sent too late"); - NS_TEST_EXPECT_MSG_EQ(m_txPsdus[13].header.IsAck(), true, "Expected a Normal Ack"); - NS_TEST_EXPECT_MSG_EQ(m_txPsdus[13].header.GetAddr1(), + NS_TEST_EXPECT_MSG_EQ(m_txPsdus[12].header.IsAck(), true, "Expected a Normal Ack"); + NS_TEST_EXPECT_MSG_EQ(m_txPsdus[12].header.GetAddr1(), DynamicCast(m_staDevices.Get(0))->GetMac()->GetAddress(), "Expected a Normal Ack sent to the first station"); NS_TEST_EXPECT_MSG_EQ( - m_txPsdus[13].header.GetDuration(), - RoundDurationId(m_txPsdus[12].header.GetDuration() - sifs - m_txPsdus[13].txDuration), + m_txPsdus[12].header.GetDuration(), + RoundDurationId(m_txPsdus[11].header.GetDuration() - sifs - m_txPsdus[12].txDuration), "Duration/ID of the Ack must be derived from that of the previous frame"); - // the TXOP limit is such that enough time for sending a CF-End frame remains - tEnd = m_txPsdus[13].txStart + m_txPsdus[13].txDuration; - tStart = m_txPsdus[14].txStart; + if (!m_protectSingleExchange) + { + // the TXOP limit is such that enough time for sending a CF-End frame remains + tEnd = m_txPsdus[12].txStart + m_txPsdus[12].txDuration; + tStart = m_txPsdus[13].txStart; - NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "CF-End sent too early"); - NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "CF-End sent too late"); - NS_TEST_EXPECT_MSG_EQ(m_txPsdus[14].header.IsCfEnd(), true, "Expected a CF-End frame"); - NS_TEST_EXPECT_MSG_EQ(m_txPsdus[14].header.GetDuration(), - Seconds(0), - "Duration/ID must be set to 0 for CF-End frames"); + NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "CF-End sent too early"); + NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "CF-End sent too late"); + NS_TEST_EXPECT_MSG_EQ(m_txPsdus[13].header.IsCfEnd(), true, "Expected a CF-End frame"); + NS_TEST_EXPECT_MSG_EQ(m_txPsdus[13].header.GetDuration(), + MicroSeconds(0), + "Duration/ID must be set to 0 for CF-End frames"); + + // remove CF-END so that indices are aligned with the protectSingleExchange false case + m_txPsdus.erase(std::next(m_txPsdus.cbegin(), 13), std::next(m_txPsdus.cbegin(), 14)); + } /* * Verify that the Duration/ID of RTS/CTS frames is set correctly, that the TXOP holder is @@ -1020,19 +1107,26 @@ WifiTxopTest::CheckResults() */ // the first frame is an RTS frame sent by the AP to STA1 - txopStart = m_txPsdus[15].txStart; + txopStart = m_txPsdus[13].txStart; - NS_TEST_EXPECT_MSG_EQ(m_txPsdus[15].header.IsRts(), true, "Expected an RTS frame"); - NS_TEST_EXPECT_MSG_EQ(m_txPsdus[15].header.GetAddr1(), + NS_TEST_EXPECT_MSG_EQ(m_txPsdus[13].header.IsRts(), true, "Expected an RTS frame"); + NS_TEST_EXPECT_MSG_EQ(m_txPsdus[13].header.GetAddr1(), DynamicCast(m_staDevices.Get(0))->GetMac()->GetAddress(), "Expected an RTS frame sent by the AP to the first station"); - NS_TEST_EXPECT_MSG_EQ(m_txPsdus[15].header.GetDuration(), - RoundDurationId(m_apTxopLimit - m_txPsdus[15].txDuration), - "Duration/ID of the first RTS frame must cover the whole TXOP"); + NS_TEST_EXPECT_MSG_EQ(m_txPsdus[13].header.GetDuration(), + (!m_protectSingleExchange + ? RoundDurationId(m_apTxopLimit - m_txPsdus[13].txDuration) + : sifs + m_txPsdus[14].txDuration + sifs + m_txPsdus[15].txDuration + + sifs + m_txPsdus[16].txDuration + + m_singleExchangeProtectionSurplus), + (!m_protectSingleExchange + ? "Duration/ID of the first RTS frame must cover the whole TXOP" + : "Duration/ID of the first RTS frame must be set to remaining time " + "to complete CTS+DATA+(Block)ACK sequence")); // a CTS is sent by STA1 - tEnd = m_txPsdus[15].txStart + m_txPsdus[15].txDuration; - tStart = m_txPsdus[16].txStart; + tEnd = m_txPsdus[13].txStart + m_txPsdus[13].txDuration; + tStart = m_txPsdus[14].txStart; NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, @@ -1040,46 +1134,51 @@ WifiTxopTest::CheckResults() NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "CTS in response to the first RTS frame sent too late"); - NS_TEST_EXPECT_MSG_EQ(m_txPsdus[16].header.IsCts(), true, "Expected a CTS"); - NS_TEST_EXPECT_MSG_EQ(m_txPsdus[16].header.GetAddr1(), + NS_TEST_EXPECT_MSG_EQ(m_txPsdus[14].header.IsCts(), true, "Expected a CTS"); + NS_TEST_EXPECT_MSG_EQ(m_txPsdus[14].header.GetAddr1(), apDev->GetMac()->GetAddress(), "Expected a CTS frame sent to the AP"); NS_TEST_EXPECT_MSG_EQ( - m_txPsdus[16].header.GetDuration(), - RoundDurationId(m_txPsdus[15].header.GetDuration() - sifs - m_txPsdus[16].txDuration), + m_txPsdus[14].header.GetDuration(), + RoundDurationId(m_txPsdus[13].header.GetDuration() - sifs - m_txPsdus[14].txDuration), "Duration/ID of the CTS frame must be derived from that of the RTS frame"); // the AP sends a frame to STA1 - tEnd = m_txPsdus[16].txStart + m_txPsdus[16].txDuration; - tStart = m_txPsdus[17].txStart; + tEnd = m_txPsdus[14].txStart + m_txPsdus[14].txDuration; + tStart = m_txPsdus[15].txStart; NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "First QoS data frame sent too early"); NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "First QoS data frame sent too late"); - NS_TEST_EXPECT_MSG_EQ(m_txPsdus[17].header.IsQosData(), true, "Expected a QoS data frame"); - NS_TEST_EXPECT_MSG_EQ(m_txPsdus[17].header.GetAddr1(), + NS_TEST_EXPECT_MSG_EQ(m_txPsdus[15].header.IsQosData(), true, "Expected a QoS data frame"); + NS_TEST_EXPECT_MSG_EQ(m_txPsdus[15].header.GetAddr1(), DynamicCast(m_staDevices.Get(0))->GetMac()->GetAddress(), "Expected a frame sent by the AP to the first station"); if (m_lengthBasedRtsCtsThresh) { - NS_TEST_EXPECT_MSG_GT(m_txPsdus[17].size, + NS_TEST_EXPECT_MSG_GT(m_txPsdus[15].size, rtsCtsThreshold, "PSDU size expected to exceed length based RTS/CTS threshold"); } else { - NS_TEST_EXPECT_MSG_GT(m_txPsdus[17].txDuration, + NS_TEST_EXPECT_MSG_GT(m_txPsdus[15].txDuration, rtsCtsTxDurationThresh, "PSDU duration expected to exceed duration based RTS/CTS threshold"); } NS_TEST_EXPECT_MSG_EQ( - m_txPsdus[17].header.GetDuration(), - RoundDurationId(m_apTxopLimit - (m_txPsdus[17].txStart - txopStart) - - m_txPsdus[17].txDuration), - "Duration/ID of the first QoS data frame does not cover the remaining TXOP"); + m_txPsdus[15].header.GetDuration(), + (!m_protectSingleExchange + ? RoundDurationId(m_apTxopLimit - (m_txPsdus[15].txStart - txopStart) - + m_txPsdus[15].txDuration) + : sifs + m_txPsdus[16].txDuration + m_singleExchangeProtectionSurplus), + (!m_protectSingleExchange + ? "Duration/ID of the first QoS data frame does not cover the remaining TXOP" + : "Duration/ID of the first QoS data frame must be set to remaining time to " + "complete DATA+(Block)ACK sequence")); // a Normal/Block Ack is then sent by STA1 - tEnd = m_txPsdus[17].txStart + m_txPsdus[17].txDuration; - tStart = m_txPsdus[18].txStart; + tEnd = m_txPsdus[15].txStart + m_txPsdus[15].txDuration; + tStart = m_txPsdus[16].txStart; NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, @@ -1088,18 +1187,18 @@ WifiTxopTest::CheckResults() tEnd + sifs + tolerance, ack << " in response to the first QoS data frame sent too late"); NS_TEST_EXPECT_MSG_EQ( - (m_nonHt ? m_txPsdus[18].header.IsAck() : m_txPsdus[18].header.IsBlockAck()), + (m_nonHt ? m_txPsdus[16].header.IsAck() : m_txPsdus[16].header.IsBlockAck()), true, "Expected a " << ack); - NS_TEST_EXPECT_MSG_EQ(m_txPsdus[18].header.GetAddr1(), + NS_TEST_EXPECT_MSG_EQ(m_txPsdus[16].header.GetAddr1(), apDev->GetMac()->GetAddress(), "Expected a " << ack << " sent to the AP"); NS_TEST_EXPECT_MSG_EQ( - m_txPsdus[18].header.GetDuration(), - RoundDurationId(m_txPsdus[17].header.GetDuration() - sifs - m_txPsdus[18].txDuration), + m_txPsdus[16].header.GetDuration(), + RoundDurationId(m_txPsdus[15].header.GetDuration() - sifs - m_txPsdus[16].txDuration), "Duration/ID of the " << ack << " must be derived from that of the previous frame"); - std::size_t idx = 18; + std::size_t idx = 16; if (!m_singleRtsPerTxop) { @@ -1115,10 +1214,17 @@ WifiTxopTest::CheckResults() m_txPsdus[idx].header.GetAddr1(), DynamicCast(m_staDevices.Get(1))->GetMac()->GetAddress(), "Expected an RTS frame sent by the AP to the second station"); - NS_TEST_EXPECT_MSG_EQ(m_txPsdus[idx].header.GetDuration(), - RoundDurationId(m_apTxopLimit - (m_txPsdus[idx].txStart - txopStart) - - m_txPsdus[idx].txDuration), - "Duration/ID of the second RTS frame must cover the whole TXOP"); + NS_TEST_EXPECT_MSG_EQ( + m_txPsdus[idx].header.GetDuration(), + (!m_protectSingleExchange + ? RoundDurationId(m_apTxopLimit - (m_txPsdus[idx].txStart - txopStart) - + m_txPsdus[idx].txDuration) + : sifs + m_txPsdus[idx + 1].txDuration + sifs + m_txPsdus[idx + 2].txDuration + + sifs + m_txPsdus[idx + 3].txDuration + m_singleExchangeProtectionSurplus), + (!m_protectSingleExchange + ? "Duration/ID of the second RTS frame must cover the whole TXOP" + : "Duration/ID of the second RTS frame must be set to remaining time to complete " + "CTS+DATA+(Block)ACK sequence")); // a CTS is sent by STA2 (which ignores the NAV) tEnd = m_txPsdus[idx].txStart + m_txPsdus[idx].txDuration; @@ -1168,9 +1274,14 @@ WifiTxopTest::CheckResults() } NS_TEST_EXPECT_MSG_EQ( m_txPsdus[idx].header.GetDuration(), - RoundDurationId(m_apTxopLimit - (m_txPsdus[idx].txStart - txopStart) - - m_txPsdus[idx].txDuration), - "Duration/ID of the second QoS data frame does not cover the remaining TXOP"); + (!m_protectSingleExchange + ? RoundDurationId(m_apTxopLimit - (m_txPsdus[idx].txStart - txopStart) - + m_txPsdus[idx].txDuration) + : sifs + m_txPsdus[idx + 1].txDuration + m_singleExchangeProtectionSurplus), + (!m_protectSingleExchange + ? "Duration/ID of the second QoS data frame does not cover the remaining TXOP" + : "Duration/ID of the second QoS data frame must be set to remaining time to " + "complete DATA+(Block)ACK sequence")); // a Normal/Block Ack is then sent by STA2 tEnd = m_txPsdus[idx].txStart + m_txPsdus[idx].txDuration; @@ -1209,10 +1320,17 @@ WifiTxopTest::CheckResults() m_txPsdus[idx].header.GetAddr1(), DynamicCast(m_staDevices.Get(2))->GetMac()->GetAddress(), "Expected an RTS frame sent by the AP to the third station"); - NS_TEST_EXPECT_MSG_EQ(m_txPsdus[idx].header.GetDuration(), - RoundDurationId(m_apTxopLimit - (m_txPsdus[idx].txStart - txopStart) - - m_txPsdus[idx].txDuration), - "Duration/ID of the third RTS frame must cover the whole TXOP"); + NS_TEST_EXPECT_MSG_EQ( + m_txPsdus[idx].header.GetDuration(), + (!m_protectSingleExchange + ? RoundDurationId(m_apTxopLimit - (m_txPsdus[idx].txStart - txopStart) - + m_txPsdus[idx].txDuration) + : sifs + m_txPsdus[idx + 1].txDuration + sifs + m_txPsdus[idx + 2].txDuration + + sifs + m_txPsdus[idx + 3].txDuration + m_singleExchangeProtectionSurplus), + (!m_protectSingleExchange + ? "Duration/ID of the third RTS frame must cover the whole TXOP" + : "Duration/ID of the third RTS frame must be set to remaining time to complete " + "CTS+DATA+(Block)ACK sequence")); // a CTS is sent by STA3 (which ignores the NAV) tEnd = m_txPsdus[idx].txStart + m_txPsdus[idx].txDuration; @@ -1261,9 +1379,14 @@ WifiTxopTest::CheckResults() } NS_TEST_EXPECT_MSG_EQ( m_txPsdus[idx].header.GetDuration(), - RoundDurationId(m_apTxopLimit - (m_txPsdus[idx].txStart - txopStart) - - m_txPsdus[idx].txDuration), - "Duration/ID of the third QoS data frame does not cover the remaining TXOP"); + (!m_protectSingleExchange + ? RoundDurationId(m_apTxopLimit - (m_txPsdus[idx].txStart - txopStart) - + m_txPsdus[idx].txDuration) + : sifs + m_txPsdus[idx + 1].txDuration + m_singleExchangeProtectionSurplus), + (!m_protectSingleExchange + ? "Duration/ID of the third QoS data frame does not cover the remaining TXOP" + : "Duration/ID of the third QoS data frame must be set to remaining time to " + "complete DATA+(Block)ACK sequence")); // a Normal/Block Ack is then sent by STA3 tEnd = m_txPsdus[idx].txStart + m_txPsdus[idx].txDuration; @@ -1290,7 +1413,7 @@ WifiTxopTest::CheckResults() // there is no time remaining for sending a CF-End frame if SingleRtsPerTxop is false. This is // verified by checking that 25 frames are transmitted (done at the beginning of this method) - if (m_singleRtsPerTxop) + if (m_singleRtsPerTxop && !m_protectSingleExchange) { tEnd = m_txPsdus[idx].txStart + m_txPsdus[idx].txDuration; ++idx; @@ -1300,7 +1423,7 @@ WifiTxopTest::CheckResults() NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "CF-End sent too late"); NS_TEST_EXPECT_MSG_EQ(m_txPsdus[idx].header.IsCfEnd(), true, "Expected a CF-End frame"); NS_TEST_EXPECT_MSG_EQ(m_txPsdus[idx].header.GetDuration(), - Seconds(0), + MicroSeconds(0), "Duration/ID must be set to 0 for CF-End frames"); } @@ -1336,13 +1459,29 @@ WifiTxopTestSuite::WifiTxopTestSuite() { for (const auto protectedIfResponded : {true, false}) { - AddTestCase( - new WifiTxopTest({.nonHt = nonHt, - .pifsRecovery = pifsRecovery, - .singleRtsPerTxop = singleRtsPerTxop, - .lengthBasedRtsCtsThresh = lengthBasedRtsCtsThresh, - .protectedIfResponded = protectedIfResponded}), - TestCase::Duration::QUICK); + for (const auto protectSingleExchange : {false, true}) + { + for (const auto& singleExchangeProtectionBuffer : + {Time(), MicroSeconds(25) /* PIFS */}) + { + if (!protectSingleExchange && + singleExchangeProtectionBuffer.IsStrictlyPositive()) + { + continue; + } + AddTestCase(new WifiTxopTest({ + .nonHt = nonHt, + .pifsRecovery = pifsRecovery, + .singleRtsPerTxop = singleRtsPerTxop, + .lengthBasedRtsCtsThresh = lengthBasedRtsCtsThresh, + .protectedIfResponded = protectedIfResponded, + .protectSingleExchange = protectSingleExchange, + .singleExchangeProtectionBuffer = + singleExchangeProtectionBuffer, + }), + TestCase::Duration::QUICK); + } + } } } }