wifi: Extend TXOP test to verify new NAV mechanism

This commit is contained in:
Sébastien Deronne
2024-09-07 12:52:30 +02:00
parent 7223f8b2aa
commit 0f3b78a78f

View File

@@ -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<FrameInfo> 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<ListErrorModel> m_apErrorModel; ///< error model to install on the AP
Ptr<ListErrorModel> 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<FrameInfo> 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<ListErrorModel> m_apErrorModel; ///< error model to install on the AP
Ptr<ListErrorModel> 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<PacketSocketAddress> 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<WifiNetDevice>(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<WifiNetDevice>(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<WifiNetDevice>(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<WifiNetDevice>(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<WifiNetDevice>(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);
}
}
}
}
}