wifi: Extend EMLSR unit test to check single link EMLSR clients

This commit is contained in:
Stefano Avallone
2024-10-31 18:50:16 +01:00
parent 7a84cc1ac7
commit 5f52b08c3e
2 changed files with 309 additions and 9 deletions

View File

@@ -327,13 +327,12 @@ EmlsrOperationsTestBase::DoSetup()
SpectrumWifiPhyHelper phyHelper(3);
phyHelper.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
phyHelper.SetPcapCaptureType(WifiPhyHelper::PcapCaptureType::PCAP_PER_LINK);
phyHelper.Set(0, "ChannelSettings", StringValue("{2, 0, BAND_2_4GHZ, 0}"));
phyHelper.Set(1, "ChannelSettings", StringValue("{36, 0, BAND_5GHZ, 0}"));
phyHelper.Set(2, "ChannelSettings", StringValue("{1, 0, BAND_6GHZ, 0}"));
// Add three spectrum channels to use multi-RF interface
phyHelper.AddChannel(CreateObject<MultiModelSpectrumChannel>(), WIFI_SPECTRUM_2_4_GHZ);
phyHelper.AddChannel(CreateObject<MultiModelSpectrumChannel>(), WIFI_SPECTRUM_5_GHZ);
phyHelper.AddChannel(CreateObject<MultiModelSpectrumChannel>(), WIFI_SPECTRUM_6_GHZ);
for (std::size_t id = 0; id < m_channelsStr.size(); ++id)
{
phyHelper.Set(id, "ChannelSettings", StringValue(m_channelsStr[id]));
phyHelper.AddChannel(CreateObject<MultiModelSpectrumChannel>(), m_freqRanges[id]);
}
WifiMacHelper mac;
mac.SetType("ns3::ApWifiMac",
@@ -346,6 +345,7 @@ EmlsrOperationsTestBase::DoSetup()
BooleanValue(true));
NetDeviceContainer apDevice = wifi.Install(phyHelper, mac, wifiApNode);
m_apMac = DynamicCast<ApWifiMac>(DynamicCast<WifiNetDevice>(apDevice.Get(0))->GetMac());
mac.SetType("ns3::StaWifiMac",
"Ssid",
@@ -360,9 +360,25 @@ EmlsrOperationsTestBase::DoSetup()
"MainPhyId",
UintegerValue(m_mainPhyId));
NetDeviceContainer staDevices = wifi.Install(phyHelper, mac, wifiStaNodes);
if (m_nPhysPerEmlsrDevice < 3)
{
phyHelper = SpectrumWifiPhyHelper(m_nPhysPerEmlsrDevice);
phyHelper.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
phyHelper.SetPcapCaptureType(WifiPhyHelper::PcapCaptureType::PCAP_PER_LINK);
m_apMac = DynamicCast<ApWifiMac>(DynamicCast<WifiNetDevice>(apDevice.Get(0))->GetMac());
for (std::size_t id = 0; id < m_nPhysPerEmlsrDevice; ++id)
{
phyHelper.Set(id, "ChannelSettings", StringValue(m_channelsStr[id]));
auto channel =
DynamicCast<MultiModelSpectrumChannel>(m_apMac->GetWifiPhy(id)->GetChannel());
NS_TEST_ASSERT_MSG_NE(channel,
nullptr,
"Channel " << +id << " is not a spectrum channel");
phyHelper.AddChannel(channel, m_freqRanges[id]);
}
}
NetDeviceContainer staDevices = wifi.Install(phyHelper, mac, wifiStaNodes);
for (uint32_t i = 0; i < staDevices.GetN(); i++)
{
@@ -4956,6 +4972,202 @@ EmlsrCcaBusyTest::CheckPoint3()
"until end of transmission");
}
SingleLinkEmlsrTest::SingleLinkEmlsrTest(bool switchAuxPhy, bool auxPhyTxCapable)
: EmlsrOperationsTestBase(
"Check EMLSR single link operation (switchAuxPhy=" + std::to_string(switchAuxPhy) +
", auxPhyTxCapable=" + std::to_string(auxPhyTxCapable) + ")"),
m_switchAuxPhy(switchAuxPhy),
m_auxPhyTxCapable(auxPhyTxCapable)
{
m_mainPhyId = 0;
m_linksToEnableEmlsrOn = {m_mainPhyId};
m_nPhysPerEmlsrDevice = 1;
m_nEmlsrStations = 1;
m_nNonEmlsrStations = 0;
// channel switch delay will be also set to 64 us
m_paddingDelay = {MicroSeconds(64)};
m_transitionDelay = {MicroSeconds(64)};
m_establishBaDl = true;
m_establishBaUl = true;
m_duration = Seconds(0.5);
}
void
SingleLinkEmlsrTest::DoSetup()
{
Config::SetDefault("ns3::WifiPhy::ChannelSwitchDelay", TimeValue(MicroSeconds(64)));
Config::SetDefault("ns3::DefaultEmlsrManager::SwitchAuxPhy", BooleanValue(m_switchAuxPhy));
Config::SetDefault("ns3::EmlsrManager::AuxPhyTxCapable", BooleanValue(m_auxPhyTxCapable));
EmlsrOperationsTestBase::DoSetup();
}
void
SingleLinkEmlsrTest::Transmit(Ptr<WifiMac> mac,
uint8_t phyId,
WifiConstPsduMap psduMap,
WifiTxVector txVector,
double txPowerW)
{
EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
const auto psdu = psduMap.cbegin()->second;
const auto& hdr = psdu->GetHeader(0);
// nothing to do in case of Beacon and CF-End frames
if (hdr.IsBeacon() || hdr.IsCfEnd())
{
return;
}
auto linkId = mac->GetLinkForPhy(phyId);
NS_TEST_ASSERT_MSG_EQ(linkId.has_value(),
true,
"PHY " << +phyId << " is not operating on any link");
NS_TEST_EXPECT_MSG_EQ(+linkId.value(), 0, "TX occurred on unexpected link " << +linkId.value());
if (m_eventIt != m_events.cend())
{
// check that the expected frame is being transmitted
NS_TEST_EXPECT_MSG_EQ(m_eventIt->hdrType,
hdr.GetType(),
"Unexpected MAC header type for frame #"
<< std::distance(m_events.cbegin(), m_eventIt));
// perform actions/checks, if any
if (m_eventIt->func)
{
m_eventIt->func(psdu, txVector);
}
++m_eventIt;
}
}
void
SingleLinkEmlsrTest::DoRun()
{
// lambda to check that AP MLD started the transition delay timer after the TX/RX of given frame
auto checkTransDelay = [this](Ptr<const WifiPsdu> psdu,
const WifiTxVector& txVector,
bool testUnblockedForOtherReasons,
const std::string& frameStr) {
const auto txDuration = WifiPhy::CalculateTxDuration(psdu->GetSize(),
txVector,
m_apMac->GetWifiPhy(0)->GetPhyBand());
Simulator::Schedule(txDuration + MicroSeconds(1), /* to account for propagation delay */
&SingleLinkEmlsrTest::CheckBlockedLink,
this,
m_apMac,
m_staMacs[0]->GetAddress(),
0,
WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
true,
"Checking that AP MLD blocked transmissions to single link EMLSR "
"client after " +
frameStr,
testUnblockedForOtherReasons);
};
// expected sequence of transmitted frames
m_events.emplace_back(WIFI_MAC_MGT_ASSOCIATION_REQUEST);
m_events.emplace_back(WIFI_MAC_CTL_ACK);
m_events.emplace_back(WIFI_MAC_MGT_ASSOCIATION_RESPONSE);
m_events.emplace_back(WIFI_MAC_CTL_ACK);
// EML OMN sent by EMLSR client
m_events.emplace_back(WIFI_MAC_MGT_ACTION,
[=, this](Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) {
// check that the address of the EMLSR client is seen as an MLD
// address
NS_TEST_EXPECT_MSG_EQ(
m_apMac->GetWifiRemoteStationManager(0)
->GetMldAddress(m_staMacs[0]->GetAddress())
.has_value(),
true,
"Expected the EMLSR client address to be seen as an MLD address");
});
m_events.emplace_back(WIFI_MAC_CTL_ACK);
// EML OMN sent by AP MLD, protected by ICF
m_events.emplace_back(WIFI_MAC_CTL_TRIGGER);
m_events.emplace_back(WIFI_MAC_CTL_CTS);
m_events.emplace_back(WIFI_MAC_MGT_ACTION);
m_events.emplace_back(WIFI_MAC_CTL_ACK,
[=, this](Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) {
// check that EMLSR mode has been enabled on link 0 of EMLSR client
NS_TEST_EXPECT_MSG_EQ(
m_staMacs[0]->IsEmlsrLink(0),
true,
"Expected EMLSR mode to be enabled on link 0 of EMLSR client");
});
// Establishment of BA agreement for downlink direction
// ADDBA REQUEST sent by AP MLD (protected by ICF)
m_events.emplace_back(WIFI_MAC_CTL_TRIGGER);
m_events.emplace_back(WIFI_MAC_CTL_CTS);
m_events.emplace_back(WIFI_MAC_MGT_ACTION);
m_events.emplace_back(WIFI_MAC_CTL_ACK,
[=](Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) {
// check that transition delay is started after reception of Ack
checkTransDelay(psdu, txVector, false, "DL ADDBA REQUEST");
});
// ADDBA RESPONSE sent by EMLSR client (no RTS because it is sent by main PHY)
m_events.emplace_back(WIFI_MAC_MGT_ACTION);
m_events.emplace_back(WIFI_MAC_CTL_ACK,
[=](Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) {
// check that transition delay is started after reception of Ack
checkTransDelay(psdu, txVector, true, "DL ADDBA RESPONSE");
});
// Downlink QoS data frame that triggered BA agreement establishment
m_events.emplace_back(WIFI_MAC_CTL_TRIGGER);
m_events.emplace_back(WIFI_MAC_CTL_CTS);
m_events.emplace_back(WIFI_MAC_QOSDATA);
m_events.emplace_back(WIFI_MAC_CTL_BACKRESP,
[=](Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) {
// check that transition delay is started after reception of BlockAck
checkTransDelay(psdu, txVector, true, "DL QoS Data");
});
// Establishment of BA agreement for uplink direction
// ADDBA REQUEST sent by EMLSR client (no RTS because it is sent by main PHY)
m_events.emplace_back(WIFI_MAC_MGT_ACTION);
m_events.emplace_back(WIFI_MAC_CTL_ACK,
[=](Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) {
// check that transition delay is started after reception of Ack
checkTransDelay(psdu, txVector, false, "UL ADDBA REQUEST");
});
// ADDBA RESPONSE sent by AP MLD (protected by ICF)
m_events.emplace_back(WIFI_MAC_CTL_TRIGGER);
m_events.emplace_back(WIFI_MAC_CTL_CTS);
m_events.emplace_back(WIFI_MAC_MGT_ACTION);
m_events.emplace_back(WIFI_MAC_CTL_ACK,
[=](Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) {
// check that transition delay is started after reception of Ack
checkTransDelay(psdu, txVector, true, "UL ADDBA RESPONSE");
});
// Uplink QoS data frame that triggered BA agreement establishment
m_events.emplace_back(WIFI_MAC_QOSDATA);
m_events.emplace_back(WIFI_MAC_CTL_BACKRESP,
[=](Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) {
// check that transition delay is started after reception of BlockAck
checkTransDelay(psdu, txVector, true, "UL QoS Data");
});
m_eventIt = m_events.cbegin();
Simulator::Stop(m_duration);
Simulator::Run();
NS_TEST_EXPECT_MSG_EQ((m_eventIt == m_events.cend()), true, "Not all events took place");
Simulator::Destroy();
}
WifiEmlsrTestSuite::WifiEmlsrTestSuite()
: TestSuite("wifi-emlsr", Type::UNIT)
{
@@ -5039,6 +5251,15 @@ WifiEmlsrTestSuite::WifiEmlsrTestSuite()
AddTestCase(new EmlsrCcaBusyTest(MHz_u{20}), TestCase::Duration::QUICK);
AddTestCase(new EmlsrCcaBusyTest(MHz_u{80}), TestCase::Duration::QUICK);
for (const auto switchAuxPhy : {true, false})
{
for (const auto auxPhyTxCapable : {true, false})
{
AddTestCase(new SingleLinkEmlsrTest(switchAuxPhy, auxPhyTxCapable),
TestCase::Duration::QUICK);
}
}
}
static WifiEmlsrTestSuite g_wifiEmlsrTestSuite; ///< the test suite

View File

@@ -21,6 +21,7 @@
#include "ns3/wifi-ppdu.h"
#include "ns3/wifi-psdu.h"
#include <array>
#include <list>
#include <map>
#include <memory>
@@ -29,6 +30,7 @@
#include <vector>
using namespace ns3;
using namespace std::string_literals;
// forward declaration
namespace ns3
@@ -196,9 +198,20 @@ class EmlsrOperationsTestBase : public TestCase
uint8_t phyId; ///< ID of the transmitting PHY
};
/// array of strings defining the channels for the MLD links
const std::array<std::string, 3> m_channelsStr{"{2, 0, BAND_2_4GHZ, 0}"s,
"{36, 0, BAND_5GHZ, 0}"s,
"{1, 0, BAND_6GHZ, 0}"s};
/// array of frequency ranges for MLD links
const std::array<FrequencyRange, 3> m_freqRanges{WIFI_SPECTRUM_2_4_GHZ,
WIFI_SPECTRUM_5_GHZ,
WIFI_SPECTRUM_6_GHZ};
uint8_t m_mainPhyId{0}; //!< ID of the main PHY
std::set<uint8_t> m_linksToEnableEmlsrOn; /**< IDs of the links on which EMLSR mode has to
be enabled */
std::size_t m_nPhysPerEmlsrDevice{3}; //!< number of PHYs per EMLSR client
std::size_t m_nEmlsrStations{1}; ///< number of stations to create that activate EMLSR
std::size_t m_nNonEmlsrStations{0}; /**< number of stations to create that do not
activate EMLSR */
@@ -973,4 +986,70 @@ class EmlsrCcaBusyTest : public EmlsrOperationsTestBase
uint8_t m_nextMainPhyLinkId; //!< the ID of the link the main PHY switches to
};
/**
* @ingroup wifi-test
* @ingroup tests
*
* @brief Test ML setup and data exchange between an AP MLD and a single link EMLSR client.
*
* A single link EMLSR client performs ML setup with an AP MLD having three links and then enables
* EMLSR mode on the unique link. A Block Ack agreement is established (for both the downlink and
* uplink directions) and QoS data frames (aggregated in an A-MPDU) are transmitted by both the
* AP MLD and the EMLSR client.
*
* It is checked that:
* - the expected sequence of frames is transmitted, including ICFs before downlink transmissions
* - EMLSR mode is enabled on the single EMLSR link
* - the address of the EMLSR client is seen as an MLD address
* - the AP MLD starts the transition delay timer at the end of each TXOP
*/
class SingleLinkEmlsrTest : public EmlsrOperationsTestBase
{
public:
/**
* Constructor.
*
* @param switchAuxPhy whether aux PHYs switch link
* @param auxPhyTxCapable whether aux PHYs are TX capable
*/
SingleLinkEmlsrTest(bool switchAuxPhy, bool auxPhyTxCapable);
protected:
void DoSetup() override;
void DoRun() override;
void Transmit(Ptr<WifiMac> mac,
uint8_t phyId,
WifiConstPsduMap psduMap,
WifiTxVector txVector,
double txPowerW) override;
/// Actions and checks to perform upon the transmission of each frame
struct Events
{
/**
* Constructor.
*
* @param type the frame MAC header type
* @param f function to perform actions and checks
*/
Events(WifiMacType type,
std::function<void(Ptr<const WifiPsdu>, const WifiTxVector&)>&& f = {})
: hdrType(type),
func(f)
{
}
WifiMacType hdrType; ///< MAC header type of frame being transmitted
std::function<void(Ptr<const WifiPsdu>, const WifiTxVector&)>
func; ///< function to perform actions and checks
};
private:
bool m_switchAuxPhy; //!< whether aux PHYs switch link
bool m_auxPhyTxCapable; //!< whether aux PHYs are TX capable
std::list<Events> m_events; //!< list of events for a test run
std::list<Events>::const_iterator m_eventIt; //!< iterator over the list of events
};
#endif /* WIFI_EMLSR_TEST_H */