wifi: Add unit test to check TX window stalled with non-zero BA threshold

This commit is contained in:
Stefano Avallone
2024-07-01 13:51:45 +02:00
parent 75df5d004d
commit e9361ef6c8

View File

@@ -12,8 +12,10 @@
#include "ns3/config.h"
#include "ns3/ctrl-headers.h"
#include "ns3/double.h"
#include "ns3/frame-exchange-manager.h"
#include "ns3/mac-rx-middle.h"
#include "ns3/mobility-helper.h"
#include "ns3/multi-model-spectrum-channel.h"
#include "ns3/originator-block-ack-agreement.h"
#include "ns3/packet-socket-client.h"
#include "ns3/packet-socket-helper.h"
@@ -23,17 +25,23 @@
#include "ns3/qos-txop.h"
#include "ns3/qos-utils.h"
#include "ns3/recipient-block-ack-agreement.h"
#include "ns3/spectrum-wifi-helper.h"
#include "ns3/string.h"
#include "ns3/test.h"
#include "ns3/wifi-default-ack-manager.h"
#include "ns3/wifi-mac-header.h"
#include "ns3/wifi-mac-queue.h"
#include "ns3/wifi-mpdu.h"
#include "ns3/wifi-net-device.h"
#include "ns3/wifi-phy.h"
#include "ns3/yans-wifi-helper.h"
#include <list>
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("WifiBlockAckTest");
/**
* \ingroup wifi-test
* \ingroup tests
@@ -1999,6 +2007,239 @@ BlockAckAggregationDisabledTest::DoRun()
}
}
/**
* \ingroup wifi-test
* \ingroup tests
*
* \brief Test for Block Ack Policy with non-null BA threshold and TX window blocked.
*
* An EHT device establishes a Block Ack agreement (for TID 0) with the AP and uses a Block Ack
* threshold of 0.125 (i.e., Block Ack is requested if there are at least 64 * 0.125 = 8 MPDUs
* to be acknowledged, where 64 is the Block Ack buffer size). An application installed on the
* non-AP node generates 70 packets, hence an A-MPDU containing the first 64 MPDUs is transmitted.
* The first 5 MPDUs in that A-MPDU are corrupted. We check that:
*
* - the first A-MPDU contains MPDUs with sequence numbers from 0 to 63
* - the second A-MPDU contains the 5 retransmitted MPDUs (with sequence numbers from 0 to 4) only,
* because the TX window is blocked
* - the third A-MPDU contains the remaining 6 MPDUs (with sequence numbers from 64 to 69)
* - 3 A-MPDUs and 3 BlockAck frames are transmitted during the simulation
* - the MAC queue is empty at the end of simulation (meaning that all MPDUs were acknowledged)
*/
class OrigBlockAckWindowStalled : public TestCase
{
public:
/**
* \param mld whether the non-AP device is a multi-link device
*/
OrigBlockAckWindowStalled(bool mld);
/**
* Callback invoked when a FEM passes PSDUs to the PHY.
*
* \param psduMap the PSDU map
* \param txVector the TX vector
* \param txPowerW the tx power in Watts
*/
void Transmit(WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW);
private:
void DoSetup() override;
void DoRun() override;
const uint8_t m_nLinks; ///< number of links
const double m_baThreshold{0.125}; ///< BA threshold used by ack manager
const std::size_t m_nPkts{70}; ///< number of generated packets
Ptr<ListErrorModel> m_errorModel; ///< error rate model to corrupt packets
Ptr<WifiNetDevice> m_staDevice; ///< station WifiNetDevice
std::size_t m_qosCount{0}; ///< counter for transmitted QoS data frames
std::size_t m_baCount{0}; ///< counter for transmitted BlockAck frames
};
OrigBlockAckWindowStalled::OrigBlockAckWindowStalled(bool mld)
: TestCase("Test case for originator Block Ack window stalled"),
m_nLinks(mld ? 2 : 1)
{
}
void
OrigBlockAckWindowStalled::Transmit(WifiConstPsduMap psduMap,
WifiTxVector txVector,
double txPowerW)
{
for (const auto& [aid, psdu] : psduMap)
{
std::stringstream ss;
ss << " #MPDUs " << psdu->GetNMpdus();
for (const auto& mpdu : *PeekPointer(psdu))
{
ss << "\n" << *mpdu;
}
if (const auto& hdr = (*psdu->begin())->GetHeader(); hdr.IsQosData())
{
// check sequence numbers in the transmitted A-MPDU
uint16_t startSeqN{0};
std::size_t count{0};
switch (++m_qosCount)
{
case 1:
startSeqN = 0;
count = 64;
break;
case 2:
startSeqN = 0;
count = 5;
break;
case 3:
startSeqN = 64;
count = 6;
break;
default:;
}
NS_TEST_EXPECT_MSG_EQ(psdu->GetNMpdus(),
count,
"Unexpected number of MPDUs in A-MPDU #" << m_qosCount);
for (const auto& mpdu : *PeekPointer(psdu))
{
NS_TEST_EXPECT_MSG_GT_OR_EQ(mpdu->GetHeader().GetSequenceNumber(),
startSeqN,
"Unexpected SeqN in A-MPDU #" << m_qosCount);
NS_TEST_EXPECT_MSG_LT(mpdu->GetHeader().GetSequenceNumber(),
startSeqN + count,
"Unexpected SeqN in A-MPDU #" << m_qosCount);
}
// reset UIDs to corrupt
m_errorModel->SetList({});
// corrupt the first 5 MPDUs of the second QoS data frame
if (m_qosCount == 1)
{
auto mpduIt = psdu->begin();
std::list<uint64_t> uids;
ss << "\nCORRUPTED";
for (std::size_t i = 0; i < 5; ++i, ++mpduIt)
{
uids.push_back((*mpduIt)->GetPacket()->GetUid());
ss << " " << (*mpduIt)->GetHeader().GetSequenceNumber();
}
m_errorModel->SetList(uids);
}
}
else if (hdr.IsBlockAck())
{
++m_baCount;
}
NS_LOG_INFO(ss.str());
}
NS_LOG_INFO("TXVECTOR = " << txVector << "\n");
}
void
OrigBlockAckWindowStalled::DoSetup()
{
NodeContainer wifiStaNode(1);
NodeContainer wifiApNode(1);
auto channel = CreateObject<MultiModelSpectrumChannel>();
SpectrumWifiPhyHelper phy(m_nLinks);
phy.Set(0, "ChannelSettings", StringValue("{36, 0, BAND_5GHZ, 0}"));
if (m_nLinks > 1)
{
phy.Set(1, "ChannelSettings", StringValue("{100, 0, BAND_5GHZ, 0}"));
}
phy.SetChannel(channel);
WifiHelper wifi;
wifi.SetStandard(WIFI_STANDARD_80211be);
wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
"DataMode",
StringValue("EhtMcs7"),
"ControlMode",
StringValue("EhtMcs0"));
WifiMacHelper mac;
mac.SetAckManager("ns3::WifiDefaultAckManager", "BaThreshold", DoubleValue(m_baThreshold));
mac.SetType("ns3::StaWifiMac", "MpduBufferSize", UintegerValue(64));
auto staDevices = wifi.Install(phy, mac, wifiStaNode);
mac.SetType("ns3::ApWifiMac");
auto apDevices = wifi.Install(phy, mac, wifiApNode);
Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();
positionAlloc->Add(Vector(0.0, 0.0, 0.0));
positionAlloc->Add(Vector(1.0, 0.0, 0.0));
MobilityHelper mobility;
mobility.SetPositionAllocator(positionAlloc);
mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
mobility.Install(wifiApNode);
mobility.Install(wifiStaNode);
auto apDevice = DynamicCast<WifiNetDevice>(apDevices.Get(0));
m_staDevice = DynamicCast<WifiNetDevice>(staDevices.Get(0));
Config::ConnectWithoutContext(
"/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phys/*/PhyTxPsduBegin",
MakeCallback(&OrigBlockAckWindowStalled::Transmit, this));
// install post reception error model on AP
m_errorModel = CreateObject<ListErrorModel>();
apDevice->GetPhy(0)->SetPostReceptionErrorModel(m_errorModel);
if (m_nLinks > 1)
{
apDevice->GetPhy(1)->SetPostReceptionErrorModel(m_errorModel);
}
PacketSocketAddress socket;
socket.SetSingleDevice(m_staDevice->GetIfIndex());
socket.SetPhysicalAddress(apDevice->GetAddress());
socket.SetProtocol(1);
// give packet socket powers to nodes.
PacketSocketHelper packetSocket;
packetSocket.Install(wifiStaNode);
packetSocket.Install(wifiApNode);
auto client = CreateObject<PacketSocketClient>();
client->SetAttribute("PacketSize", UintegerValue(100));
client->SetAttribute("MaxPackets", UintegerValue(m_nPkts));
client->SetAttribute("Interval", TimeValue(Time{0}));
client->SetRemote(socket);
wifiStaNode.Get(0)->AddApplication(client);
client->SetStartTime(Seconds(0.5));
client->SetStopTime(Seconds(3.0));
auto server = CreateObject<PacketSocketServer>();
server->SetLocal(socket);
wifiApNode.Get(0)->AddApplication(server);
server->SetStartTime(Seconds(0.0));
server->SetStopTime(Seconds(4.0));
}
void
OrigBlockAckWindowStalled::DoRun()
{
Simulator::Stop(Seconds(3));
Simulator::Run();
NS_TEST_EXPECT_MSG_EQ(m_qosCount, 3, "Unexpected number of transmitted QoS data frames");
NS_TEST_EXPECT_MSG_EQ(m_baCount, 3, "Unexpected number of transmitted BlockAck frames");
NS_TEST_EXPECT_MSG_EQ(m_staDevice->GetMac()->GetTxopQueue(AC_BE)->IsEmpty(),
true,
"Expected no packet in STA queue");
Simulator::Destroy();
}
/**
* \ingroup wifi-test
* \ingroup tests
@@ -2023,6 +2264,8 @@ BlockAckTestSuite::BlockAckTestSuite()
AddTestCase(new MultiStaCtrlBAckResponseHeaderTest, TestCase::Duration::QUICK);
AddTestCase(new BlockAckAggregationDisabledTest(false), TestCase::Duration::QUICK);
AddTestCase(new BlockAckAggregationDisabledTest(true), TestCase::Duration::QUICK);
AddTestCase(new OrigBlockAckWindowStalled(false), TestCase::Duration::QUICK);
AddTestCase(new OrigBlockAckWindowStalled(true), TestCase::Duration::QUICK);
}
static BlockAckTestSuite g_blockAckTestSuite; ///< the test suite