wifi: Extend TXOP test to check protection based on responses

This commit is contained in:
Stefano Avallone
2024-05-17 19:01:36 +02:00
parent 5aedb84575
commit e0a109574f

View File

@@ -13,6 +13,7 @@
#include "ns3/log.h"
#include "ns3/mobility-helper.h"
#include "ns3/multi-model-spectrum-channel.h"
#include "ns3/node-list.h"
#include "ns3/packet-socket-client.h"
#include "ns3/packet-socket-helper.h"
#include "ns3/packet-socket-server.h"
@@ -47,7 +48,10 @@ NS_LOG_COMPONENT_DEFINE("WifiTxopTest");
* received, while the second frame to STA 2 is corrupted. It is checked that the AP performs
* PIFS recovery or invokes backoff depending on the value of the PifsRecovery attribute. All
* QoS data frames transmitted have a length/duration that does not exceed the length/duration
* based RTS/CTS threshold, hence RTS/CTS is never used.
* based RTS/CTS threshold, hence RTS/CTS is never used. After that a QoS data frame has been
* sent to every STA, the AP sends another QoS data frame to one of them; the size/duration of
* this frame exceeds the threshold, but, given that the STA has already sent a response to the
* AP in this TXOP, RTS/CTS is only used if the ProtectedIfResponded is false.
* - In the second TXOP, the AP sends a QoS data frame, in case of non-HT devices, or an A-MPDU
* consisting of 2 MPDUs, in case of HE devices, to each of the three STAs. All PSDUs transmitted
* have a length/duration that exceeds the length/duration based RTS/CTS threshold, hence RTS/CTS
@@ -65,6 +69,8 @@ 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
};
/**
@@ -80,6 +86,7 @@ class WifiTxopTest : public TestCase
* \param addr the address
*/
void L7Receive(std::string context, Ptr<const Packet> p, const Address& addr);
/**
* Callback invoked when PHY receives a PSDU to transmit
* \param context the context
@@ -91,6 +98,7 @@ class WifiTxopTest : public TestCase
WifiConstPsduMap psduMap,
WifiTxVector txVector,
double txPowerW);
/**
* Check correctness of transmitted frames
*/
@@ -99,6 +107,26 @@ class WifiTxopTest : public TestCase
private:
void DoRun() override;
/// Enumeration for traffic directions
enum TrafficDirection : uint8_t
{
DOWNLINK = 0,
UPLINK
};
/**
* \param dir the traffic direction (downlink/uplink)
* \param staId the index (starting at 0) of the non-AP STA generating/receiving packets
* \param count the number of packets to generate
* \param pktSize the size of the packets to generate
* \return an application generating the given number packets of the given size from/to the
* AP to/from the given non-AP STA
*/
Ptr<PacketSocketClient> GetApplication(TrafficDirection dir,
std::size_t staId,
std::size_t count,
std::size_t pktSize) const;
/// Information about transmitted frames
struct FrameInfo
{
@@ -127,10 +155,13 @@ class WifiTxopTest : public TestCase
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
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
std::vector<PacketSocketAddress> m_ulSockets; ///< packet socket address for UL traffic
};
WifiTxopTest::WifiTxopTest(const Params& params)
@@ -150,6 +181,7 @@ WifiTxopTest::WifiTxopTest(const Params& params)
m_pifsRecovery(params.pifsRecovery),
m_singleRtsPerTxop(params.singleRtsPerTxop),
m_lengthBasedRtsCtsThresh(params.lengthBasedRtsCtsThresh),
m_protectedIfResponded(params.protectedIfResponded),
m_apErrorModel(CreateObject<ListErrorModel>()),
m_staErrorModel(CreateObject<ListErrorModel>()),
m_apCorrupted(false),
@@ -204,6 +236,18 @@ WifiTxopTest::Transmit(std::string context,
}
}
// When the AP sends the first frame to the third station (which is not protected by RTS/CTS),
// we generate another frame addressed to the second station whose size/duration exceeds the
// threshold; however, RTS is not used because the second station has already responded to
// another frame in the same TXOP
if (const auto& hdr = psduMap.begin()->second->GetHeader(0);
hdr.IsQosData() && hdr.GetAddr1() == m_staDevices.Get(2)->GetAddress() &&
hdr.GetSequenceNumber() == (m_nonHt ? 0 : 1))
{
m_apDevices.Get(0)->GetNode()->AddApplication(
GetApplication(DOWNLINK, 1, m_nonHt ? 1 : 2, m_payloadSizeRtsOn));
}
// Log all transmitted frames that are not beacon frames and have been transmitted
// after the start time (so as to skip association requests/responses)
if (!psduMap.begin()->second->GetHeader(0).IsBeacon() && Simulator::Now() >= m_startTime)
@@ -262,6 +306,8 @@ WifiTxopTest::DoRun()
TimeValue(Seconds(m_payloadSizeRtsOn * (m_nonHt ? 1 : 2) * 8.0 /
m_mode.GetDataRate(20))));
}
Config::SetDefault("ns3::FrameExchangeManager::ProtectedIfResponded",
BooleanValue(m_protectedIfResponded));
WifiHelper wifi;
wifi.SetStandard(m_nonHt ? WIFI_STANDARD_80211a : WIFI_STANDARD_80211ax);
@@ -339,53 +385,60 @@ WifiTxopTest::DoRun()
packetSocket.Install(wifiApNode);
packetSocket.Install(wifiStaNodes);
// install a packet socket server on all nodes
for (auto nodeIt = NodeList::Begin(); nodeIt != NodeList::End(); ++nodeIt)
{
PacketSocketAddress srvAddr;
auto device = DynamicCast<WifiNetDevice>((*nodeIt)->GetDevice(0));
NS_TEST_ASSERT_MSG_NE(device, nullptr, "Expected a WifiNetDevice");
srvAddr.SetSingleDevice(device->GetIfIndex());
srvAddr.SetProtocol(1);
auto server = CreateObject<PacketSocketServer>();
server->SetLocal(srvAddr);
(*nodeIt)->AddApplication(server);
server->SetStartTime(Time{0}); // now
server->SetStopTime(Seconds(1.0));
}
// set DL and UL packet sockets
for (uint16_t i = 0; i < m_nStations; ++i)
{
m_dlSockets.emplace_back();
m_dlSockets.back().SetSingleDevice(m_apDevices.Get(0)->GetIfIndex());
m_dlSockets.back().SetPhysicalAddress(m_staDevices.Get(i)->GetAddress());
m_dlSockets.back().SetProtocol(1);
m_ulSockets.emplace_back();
m_ulSockets.back().SetSingleDevice(m_staDevices.Get(i)->GetIfIndex());
m_ulSockets.back().SetPhysicalAddress(m_apDevices.Get(0)->GetAddress());
m_ulSockets.back().SetProtocol(1);
}
// DL frames
for (uint16_t i = 0; i < m_nStations; i++)
{
PacketSocketAddress socket;
socket.SetSingleDevice(m_apDevices.Get(0)->GetIfIndex());
socket.SetPhysicalAddress(m_staDevices.Get(i)->GetAddress());
socket.SetProtocol(1);
if (!m_nonHt)
{
// Send one QoS data frame to establish Block Ack agreement (packet size is such that
// this packet is not counted as a received packet)
auto client = CreateObject<PacketSocketClient>();
client->SetAttribute("PacketSize", UintegerValue(m_payloadSizeRtsOff - 1));
client->SetAttribute("MaxPackets", UintegerValue(1));
client->SetAttribute("Interval", TimeValue(MicroSeconds(1)));
client->SetRemote(socket);
wifiApNode.Get(0)->AddApplication(client);
client->SetStartTime(m_startTime - MilliSeconds(110 - i * 25));
client->SetStopTime(Seconds(1.0));
Simulator::Schedule(m_startTime - MilliSeconds(110 - i * 25),
&Node::AddApplication,
wifiApNode.Get(0),
GetApplication(DOWNLINK, i, 1, m_payloadSizeRtsOff - 1));
}
// Send one QoS data frame (not protected by RTS/CTS) to each station
auto client1 = CreateObject<PacketSocketClient>();
client1->SetAttribute("PacketSize", UintegerValue(m_payloadSizeRtsOff));
client1->SetAttribute("MaxPackets", UintegerValue(1));
client1->SetAttribute("Interval", TimeValue(MicroSeconds(1)));
client1->SetRemote(socket);
wifiApNode.Get(0)->AddApplication(client1);
client1->SetStartTime(m_startTime);
client1->SetStopTime(Seconds(1.0));
Simulator::Schedule(m_startTime,
&Node::AddApplication,
wifiApNode.Get(0),
GetApplication(DOWNLINK, i, 1, m_payloadSizeRtsOff));
// Send one QoS data frame (protected by RTS/CTS) to each station
auto client2 = CreateObject<PacketSocketClient>();
client2->SetAttribute("PacketSize", UintegerValue(m_payloadSizeRtsOn));
client2->SetAttribute("MaxPackets", UintegerValue(m_nonHt ? 1 : 2));
client2->SetAttribute("Interval", TimeValue(Time{0}));
client2->SetRemote(socket);
wifiApNode.Get(0)->AddApplication(client2);
client2->SetStartTime(m_startTime + MilliSeconds(110));
client2->SetStopTime(Seconds(1.0));
auto server = CreateObject<PacketSocketServer>();
server->SetLocal(socket);
wifiStaNodes.Get(i)->AddApplication(server);
server->SetStartTime(Seconds(0.0));
server->SetStopTime(Seconds(1.0));
Simulator::Schedule(m_startTime + MilliSeconds(110),
&Node::AddApplication,
wifiApNode.Get(0),
GetApplication(DOWNLINK, i, m_nonHt ? 1 : 2, m_payloadSizeRtsOn));
}
// install the error model on the AP
@@ -398,39 +451,20 @@ WifiTxopTest::DoRun()
// UL Traffic (the first station sends one frame to the AP)
{
PacketSocketAddress socket;
socket.SetSingleDevice(m_staDevices.Get(0)->GetIfIndex());
socket.SetPhysicalAddress(m_apDevices.Get(0)->GetAddress());
socket.SetProtocol(1);
if (!m_nonHt)
{
// Send one QoS data frame to establish Block Ack agreement (packet size is such that
// this packet is not counted as a received packet)
auto client = CreateObject<PacketSocketClient>();
client->SetAttribute("PacketSize", UintegerValue(m_payloadSizeRtsOff - 1));
client->SetAttribute("MaxPackets", UintegerValue(1));
client->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
client->SetRemote(socket);
wifiStaNodes.Get(0)->AddApplication(client);
client->SetStartTime(m_startTime - MilliSeconds(35));
client->SetStopTime(Seconds(1.0));
Simulator::Schedule(m_startTime - MilliSeconds(35),
&Node::AddApplication,
wifiStaNodes.Get(0),
GetApplication(UPLINK, 0, 1, m_payloadSizeRtsOff - 1));
}
auto client = CreateObject<PacketSocketClient>();
client->SetAttribute("PacketSize", UintegerValue(m_payloadSizeRtsOff));
client->SetAttribute("MaxPackets", UintegerValue(1));
client->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
client->SetRemote(socket);
wifiStaNodes.Get(0)->AddApplication(client);
client->SetStartTime(m_startTime + MilliSeconds(2));
client->SetStopTime(Seconds(1.0));
auto server = CreateObject<PacketSocketServer>();
server->SetLocal(socket);
wifiApNode.Get(0)->AddApplication(server);
server->SetStartTime(Seconds(0.0));
server->SetStopTime(Seconds(1.0));
Simulator::Schedule(m_startTime + MilliSeconds(2),
&Node::AddApplication,
wifiStaNodes.Get(0),
GetApplication(UPLINK, 0, 1, m_payloadSizeRtsOff));
}
Config::Connect("/NodeList/*/ApplicationList/*/$ns3::PacketSocketServer/Rx",
@@ -447,6 +481,23 @@ WifiTxopTest::DoRun()
Simulator::Destroy();
}
Ptr<PacketSocketClient>
WifiTxopTest::GetApplication(TrafficDirection dir,
std::size_t staId,
std::size_t count,
std::size_t pktSize) const
{
auto client = CreateObject<PacketSocketClient>();
client->SetAttribute("PacketSize", UintegerValue(pktSize));
client->SetAttribute("MaxPackets", UintegerValue(count));
client->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
client->SetRemote(dir == DOWNLINK ? m_dlSockets.at(staId) : m_ulSockets.at(staId));
client->SetStartTime(Time{0}); // now
client->SetStopTime(Seconds(1.0));
return client;
}
void
WifiTxopTest::CheckResults()
{
@@ -501,24 +552,30 @@ WifiTxopTest::CheckResults()
*
* |--NAV----till end TXOP-------->|
* | |---NAV--till end TXOP--->|
* | | |-------------------------NAV------------------------------>|
* | | | |------------------------NAV------------------------->|
* | | | | |-----------------------NAV-------------------->|
* | | | | | |-----------NAV------------->|
* Start| | Start| | | | |--------NAV---------->|
* TXOP | | TXOP | | | Ack | | |------NAV------>|
* | | | | | | | Timeout | | | |---NAV--->|
* |---| |---|-backoff->|---| |---| |---| |-PIFS or->|---| |---| |---| |---| |-----|
* |QoS| |Ack| |QoS| |Ack| |QoS| |-backoff->|QoS| |Ack| |QoS| |Ack| |CFend|
* --------------------------------------------------------------------------------------------
* From: AP STA1 AP STA1 AP AP STA2 AP STA3 AP
* To: STA1 [AP] STA1 AP [STA2] STA2 AP STA3 AP all
* | | |-----------------------------NAV--------------------------------->|
* | | | |----------------------------NAV---------------------------->|
* | | | | |---------------------------NAV----------------------->|
* | | | | | |----------------NAV------------------>|
* | | | | | | |-------------NAV--------------->|
* | | | | | Ack | | |-----------NAV----------->|
* Start| | Start| | | Timeout | | | |--------NAV-------->|
* TXOP | | TXOP | | | |-PIFS-> | | | | |-----NAV----->|
* | | | back | | | | |- or -> | | | | | |--NAV-->|
* ┌───┐ ┌───┐-off->┌───┐ ┌───┐ ┌───┐ |-back->┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌─────┐
* │QoS│ │Ack│ │QoS│ │Ack│ │QoS│ |-off ->│QoS│ │Ack│ │QoS│ │Ack│ │QoS│ │Ack│ │CFend│
* ───┴───┴─┴───┴──────┴───┴─┴───┴─┴───┴───────────┴───┴─┴───┴─┴───┴─┴───┴─┴───┴─┴───┴─┴─────┴─
* TA: AP STA1 AP STA1 AP AP STA2 AP STA3 AP STA2 AP
* 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
*/
// We expect 25 frames to be transmitted if SingleRtsPerTxop is false and 22 frames (2 RTS
// less, 2 CTS less, 1 more CF-End)
// 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 ? 22 : 25),
(m_singleRtsPerTxop ? 24 + (m_protectedIfResponded ? 0 : 2)
: 27 + (m_protectedIfResponded ? 0 : 2)),
"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
@@ -762,20 +819,123 @@ WifiTxopTest::CheckResults()
RoundDurationId(m_txPsdus[7].header.GetDuration() - sifs - m_txPsdus[8].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
// the AP sends another frame to STA2, which is only protected if ProtectedIfResponded is true,
// because its size/duration exceeds the threshold but STA2 has already responded to the AP in
// this TXOP
if (!m_protectedIfResponded)
{
tEnd = m_txPsdus[8].txStart + m_txPsdus[8].txDuration;
tStart = m_txPsdus[9].txStart;
NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "RTS before fourth frame sent too early");
NS_TEST_EXPECT_MSG_LT(tStart,
tEnd + sifs + tolerance,
"RTS before fourth frame sent too late");
NS_TEST_EXPECT_MSG_EQ(m_txPsdus[9].header.IsRts(), true, "Expected an RTS frame");
NS_TEST_EXPECT_MSG_EQ(
m_txPsdus[9].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[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");
// a CTS is sent by STA2
tEnd = m_txPsdus[9].txStart + m_txPsdus[9].txDuration;
tStart = m_txPsdus[10].txStart;
NS_TEST_EXPECT_MSG_LT(tEnd + sifs,
tStart,
"CTS in response to RTS before the fourth frame sent too early");
NS_TEST_EXPECT_MSG_LT(tStart,
tEnd + sifs + tolerance,
"CTS in response to RTS before the fourth frame sent too late");
NS_TEST_EXPECT_MSG_EQ(m_txPsdus[10].header.IsCts(), true, "Expected a CTS");
NS_TEST_EXPECT_MSG_EQ(m_txPsdus[10].header.GetAddr1(),
apDev->GetMac()->GetAddress(),
"Expected a CTS frame sent to the AP");
NS_TEST_EXPECT_MSG_EQ(
m_txPsdus[10].header.GetDuration(),
RoundDurationId(m_txPsdus[9].header.GetDuration() - sifs - m_txPsdus[10].txDuration),
"Duration/ID of the CTS frame must be derived from that of the RTS frame");
// the AP sends another frame to STA2
tEnd = m_txPsdus[10].txStart + m_txPsdus[10].txDuration;
tStart = m_txPsdus[11].txStart;
// remove RTS and CTS so that indices are aligned with the ProtectedIfResponded true case
m_txPsdus.erase(std::next(m_txPsdus.cbegin(), 9), std::next(m_txPsdus.cbegin(), 11));
}
else
{
// the AP sends another frame to STA2
tEnd = m_txPsdus[8].txStart + m_txPsdus[8].txDuration;
tStart = m_txPsdus[9].txStart;
}
NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "Fourth frame sent too early");
NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "Fourth frame sent too late");
NS_TEST_EXPECT_MSG_EQ(m_txPsdus[9].header.IsQosData(), true, "Expected a QoS data frame");
NS_TEST_EXPECT_MSG_EQ(m_txPsdus[9].header.GetAddr1(),
DynamicCast<WifiNetDevice>(m_staDevices.Get(1))->GetMac()->GetAddress(),
"Expected a frame sent by the AP to the second station");
if (m_lengthBasedRtsCtsThresh)
{
NS_TEST_EXPECT_MSG_GT(m_txPsdus[9].size,
rtsCtsThreshold,
"PSDU size expected to exceed length based RTS/CTS threshold");
}
else
{
NS_TEST_EXPECT_MSG_GT(m_txPsdus[9].txDuration,
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");
std::string ack(m_nonHt ? "Normal Ack" : "Block Ack");
// a Normal/Block Ack is then sent by STA1
tEnd = m_txPsdus[9].txStart + m_txPsdus[9].txDuration;
tStart = m_txPsdus[10].txStart;
NS_TEST_EXPECT_MSG_LT(tEnd + sifs,
tStart,
ack << " in response to the fourth QoS data frame sent too early");
NS_TEST_EXPECT_MSG_LT(tStart,
tEnd + sifs + tolerance,
ack << " in response to the fourth QoS data frame sent too late");
NS_TEST_EXPECT_MSG_EQ(
(m_nonHt ? m_txPsdus[10].header.IsAck() : m_txPsdus[10].header.IsBlockAck()),
true,
"Expected a " << ack);
NS_TEST_EXPECT_MSG_EQ(m_txPsdus[10].header.GetAddr1(),
apDev->GetMac()->GetAddress(),
"Expected a " << ack << " sent to the AP");
NS_TEST_EXPECT_MSG_EQ(
m_txPsdus[10].header.GetDuration(),
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;
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[9].header.IsCfEnd(), true, "Expected a CF-End frame");
NS_TEST_EXPECT_MSG_EQ(m_txPsdus[9].header.GetDuration(),
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");
// the CF-End frame resets the NAV on STA1, which can now transmit
tEnd = m_txPsdus[9].txStart + m_txPsdus[9].txDuration;
tStart = m_txPsdus[10].txStart;
tEnd = m_txPsdus[11].txStart + m_txPsdus[11].txDuration;
tStart = m_txPsdus[12].txStart;
NS_TEST_EXPECT_MSG_GT_OR_EQ(tStart - tEnd,
sifs + m_staAifsn * slot,
@@ -783,51 +943,51 @@ 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[10].header.IsQosData(), true, "Expected a QoS data frame");
NS_TEST_EXPECT_MSG_EQ(m_txPsdus[10].header.GetAddr1(),
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(),
apDev->GetMac()->GetAddress(),
"Expected a frame sent by the first station to the AP");
if (m_lengthBasedRtsCtsThresh)
{
NS_TEST_EXPECT_MSG_LT(m_txPsdus[10].size,
NS_TEST_EXPECT_MSG_LT(m_txPsdus[12].size,
rtsCtsThreshold,
"PSDU size expected not to exceed length based RTS/CTS threshold");
}
else
{
NS_TEST_EXPECT_MSG_LT(
m_txPsdus[10].txDuration,
m_txPsdus[12].txDuration,
rtsCtsTxDurationThresh,
"PSDU duration expected not to exceed duration based RTS/CTS threshold");
}
NS_TEST_EXPECT_MSG_EQ(
m_txPsdus[10].header.GetDuration(),
RoundDurationId(m_staTxopLimit - m_txPsdus[10].txDuration),
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");
// a Normal Ack is then sent by the AP
tEnd = m_txPsdus[10].txStart + m_txPsdus[10].txDuration;
tStart = m_txPsdus[11].txStart;
tEnd = m_txPsdus[12].txStart + m_txPsdus[12].txDuration;
tStart = m_txPsdus[13].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[11].header.IsAck(), true, "Expected a Normal Ack");
NS_TEST_EXPECT_MSG_EQ(m_txPsdus[11].header.GetAddr1(),
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(),
DynamicCast<WifiNetDevice>(m_staDevices.Get(0))->GetMac()->GetAddress(),
"Expected a Normal Ack sent to the first station");
NS_TEST_EXPECT_MSG_EQ(
m_txPsdus[11].header.GetDuration(),
RoundDurationId(m_txPsdus[10].header.GetDuration() - sifs - m_txPsdus[11].txDuration),
m_txPsdus[13].header.GetDuration(),
RoundDurationId(m_txPsdus[12].header.GetDuration() - sifs - m_txPsdus[13].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[11].txStart + m_txPsdus[11].txDuration;
tStart = m_txPsdus[12].txStart;
tEnd = m_txPsdus[13].txStart + m_txPsdus[13].txDuration;
tStart = m_txPsdus[14].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[12].header.IsCfEnd(), true, "Expected a CF-End frame");
NS_TEST_EXPECT_MSG_EQ(m_txPsdus[12].header.GetDuration(),
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");
@@ -856,20 +1016,19 @@ WifiTxopTest::CheckResults()
*/
// the first frame is an RTS frame sent by the AP to STA1
txopStart = m_txPsdus[13].txStart;
std::string ack(m_nonHt ? "Normal Ack" : "Block Ack");
txopStart = m_txPsdus[15].txStart;
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(),
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(),
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[13].header.GetDuration(),
RoundDurationId(m_apTxopLimit - m_txPsdus[13].txDuration),
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");
// a CTS is sent by STA1
tEnd = m_txPsdus[13].txStart + m_txPsdus[13].txDuration;
tStart = m_txPsdus[14].txStart;
tEnd = m_txPsdus[15].txStart + m_txPsdus[15].txDuration;
tStart = m_txPsdus[16].txStart;
NS_TEST_EXPECT_MSG_LT(tEnd + sifs,
tStart,
@@ -877,46 +1036,46 @@ 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[14].header.IsCts(), true, "Expected a CTS");
NS_TEST_EXPECT_MSG_EQ(m_txPsdus[14].header.GetAddr1(),
NS_TEST_EXPECT_MSG_EQ(m_txPsdus[16].header.IsCts(), true, "Expected a CTS");
NS_TEST_EXPECT_MSG_EQ(m_txPsdus[16].header.GetAddr1(),
apDev->GetMac()->GetAddress(),
"Expected a CTS frame sent to the AP");
NS_TEST_EXPECT_MSG_EQ(
m_txPsdus[14].header.GetDuration(),
RoundDurationId(m_txPsdus[13].header.GetDuration() - sifs - m_txPsdus[14].txDuration),
m_txPsdus[16].header.GetDuration(),
RoundDurationId(m_txPsdus[15].header.GetDuration() - sifs - m_txPsdus[16].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[14].txStart + m_txPsdus[14].txDuration;
tStart = m_txPsdus[15].txStart;
tEnd = m_txPsdus[16].txStart + m_txPsdus[16].txDuration;
tStart = m_txPsdus[17].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[15].header.IsQosData(), true, "Expected a QoS data frame");
NS_TEST_EXPECT_MSG_EQ(m_txPsdus[15].header.GetAddr1(),
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(),
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[15].size,
NS_TEST_EXPECT_MSG_GT(m_txPsdus[17].size,
rtsCtsThreshold,
"PSDU size expected to exceed length based RTS/CTS threshold");
}
else
{
NS_TEST_EXPECT_MSG_GT(m_txPsdus[15].txDuration,
NS_TEST_EXPECT_MSG_GT(m_txPsdus[17].txDuration,
rtsCtsTxDurationThresh,
"PSDU duration expected to exceed duration based RTS/CTS threshold");
}
NS_TEST_EXPECT_MSG_EQ(
m_txPsdus[15].header.GetDuration(),
RoundDurationId(m_apTxopLimit - (m_txPsdus[15].txStart - txopStart) -
m_txPsdus[15].txDuration),
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");
// a Normal/Block Ack is then sent by STA1
tEnd = m_txPsdus[15].txStart + m_txPsdus[15].txDuration;
tStart = m_txPsdus[16].txStart;
tEnd = m_txPsdus[17].txStart + m_txPsdus[17].txDuration;
tStart = m_txPsdus[18].txStart;
NS_TEST_EXPECT_MSG_LT(tEnd + sifs,
tStart,
@@ -925,18 +1084,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[16].header.IsAck() : m_txPsdus[16].header.IsBlockAck()),
(m_nonHt ? m_txPsdus[18].header.IsAck() : m_txPsdus[18].header.IsBlockAck()),
true,
"Expected a " << ack);
NS_TEST_EXPECT_MSG_EQ(m_txPsdus[16].header.GetAddr1(),
NS_TEST_EXPECT_MSG_EQ(m_txPsdus[18].header.GetAddr1(),
apDev->GetMac()->GetAddress(),
"Expected a " << ack << " 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[18].header.GetDuration(),
RoundDurationId(m_txPsdus[17].header.GetDuration() - sifs - m_txPsdus[18].txDuration),
"Duration/ID of the " << ack << " must be derived from that of the previous frame");
std::size_t idx = 16;
std::size_t idx = 18;
if (!m_singleRtsPerTxop)
{
@@ -1142,10 +1301,10 @@ WifiTxopTest::CheckResults()
}
// Expected received packets:
// - 3 DL packets (without RTS/CTS)
// - 4 DL packets (without RTS/CTS) if non-HT, 5 DL packets (without RTS/CTS) if HE
// - 1 UL packet
// - 3 DL packets (with RTS/CTS) if non-HT, 6 DL packets (with RTS/CTS) if HE
NS_TEST_EXPECT_MSG_EQ(m_received, (m_nonHt ? 7 : 10), "Unexpected number of packets received");
NS_TEST_EXPECT_MSG_EQ(m_received, (m_nonHt ? 8 : 12), "Unexpected number of packets received");
}
/**
@@ -1168,17 +1327,20 @@ WifiTxopTestSuite::WifiTxopTestSuite()
AddTestCase(new WifiTxopTest({.nonHt = nonHt,
.pifsRecovery = true,
.singleRtsPerTxop = false,
.lengthBasedRtsCtsThresh = false}),
.lengthBasedRtsCtsThresh = false,
.protectedIfResponded = true}),
TestCase::Duration::QUICK);
AddTestCase(new WifiTxopTest({.nonHt = nonHt,
.pifsRecovery = false,
.singleRtsPerTxop = true,
.lengthBasedRtsCtsThresh = false}),
.lengthBasedRtsCtsThresh = false,
.protectedIfResponded = false}),
TestCase::Duration::QUICK);
AddTestCase(new WifiTxopTest({.nonHt = nonHt,
.pifsRecovery = true,
.singleRtsPerTxop = true,
.lengthBasedRtsCtsThresh = true}),
.lengthBasedRtsCtsThresh = true,
.protectedIfResponded = true}),
TestCase::Duration::QUICK);
}
}