From 7f7286e371186e4ff08f6d70c06446f2b4700d4e Mon Sep 17 00:00:00 2001 From: Sharan Naribole Date: Thu, 10 Apr 2025 19:17:41 -0700 Subject: [PATCH] wifi: Add test for static EML OMN exchange --- src/wifi/CMakeLists.txt | 1 + src/wifi/test/wifi-static-emlsr-test.cc | 374 ++++++++++++++++++++++++ 2 files changed, 375 insertions(+) create mode 100644 src/wifi/test/wifi-static-emlsr-test.cc diff --git a/src/wifi/CMakeLists.txt b/src/wifi/CMakeLists.txt index cee2297b2..c626b5d68 100644 --- a/src/wifi/CMakeLists.txt +++ b/src/wifi/CMakeLists.txt @@ -407,6 +407,7 @@ build_lib( test/wifi-retransmit-test.cc test/wifi-ru-allocation-test.cc test/wifi-channel-switching-test.cc + test/wifi-static-emlsr-test.cc test/wifi-static-infra-bss-test.cc test/wifi-test.cc test/wifi-transmit-mask-test.cc diff --git a/src/wifi/test/wifi-static-emlsr-test.cc b/src/wifi/test/wifi-static-emlsr-test.cc new file mode 100644 index 000000000..823994e7b --- /dev/null +++ b/src/wifi/test/wifi-static-emlsr-test.cc @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2025 + * + * SPDX-License-Identifier: GPL-2.0-only + * + * Author: Sharan Naribole + */ + +#include "ns3/ap-wifi-mac.h" +#include "ns3/assert.h" +#include "ns3/attribute-container.h" +#include "ns3/boolean.h" +#include "ns3/emlsr-manager.h" +#include "ns3/frame-exchange-manager.h" +#include "ns3/log.h" +#include "ns3/mac48-address.h" +#include "ns3/multi-model-spectrum-channel.h" +#include "ns3/node-container.h" +#include "ns3/rng-seed-manager.h" +#include "ns3/simulator.h" +#include "ns3/spectrum-wifi-helper.h" +#include "ns3/sta-wifi-mac.h" +#include "ns3/string.h" +#include "ns3/test.h" +#include "ns3/wifi-mac-header.h" +#include "ns3/wifi-mac-helper.h" +#include "ns3/wifi-mac.h" +#include "ns3/wifi-net-device.h" +#include "ns3/wifi-static-setup-helper.h" +#include "ns3/wifi-utils.h" + +#include +#include +#include +#include + +/// @ingroup wifi-test +/// @ingroup tests +/// @brief WifiStaticSetupHelper EMLSR setup test suite +/// Test suite intended to test static EMLSR setup between AP MLD and client MLD. +/// The test prepares AP WifiNetDevice and client WifiNetDevice based on test vector input and +/// performs static EMLSR setup using WifiStaticSetupHelper. The test verifies if EMLSR state +/// machine at ApWifiMac and StaWifiMac has been updated correctly. + +using namespace ns3; +NS_LOG_COMPONENT_DEFINE("WifiStaticEmlsrTestSuite"); + +namespace WifiStaticEmlsrTestConstants +{ +const auto DEFAULT_RNG_SEED = 3; +const auto DEFAULT_RNG_RUN = 7; +const auto DEFAULT_STREAM_INDEX = 100; +const auto DEFAULT_SIM_STOP_TIME = NanoSeconds(1); +const auto DEFAULT_BEACON_GEN = false; +const auto DEFAULT_DATA_MODE = "HeMcs3"; +const auto DEFAULT_CONTROL_MODE = "OfdmRate24Mbps"; +const auto DEFAULT_SWITCH_DELAY = MicroSeconds(64); +const auto DEFAULT_AUX_PHY_CH_WIDTH = MHz_u{20}; +const auto DEFAULT_SWITCH_AUX_PHY = false; +const auto DEFAULT_WIFI_STANDARD = WifiStandard::WIFI_STANDARD_80211be; +const auto DEFAULT_SSID = Ssid("static-assoc-test"); +const std::string CHANNEL_0 = "{42, 80, BAND_5GHZ, 0}"; +const std::string CHANNEL_1 = "{23, 80, BAND_6GHZ, 0}"; +const std::string CHANNEL_2 = "{2, 0, BAND_2_4GHZ, 0}"; +const std::vector DEFAULT_AP_CHS = {CHANNEL_0, CHANNEL_1, CHANNEL_2}; +using ChannelMap = std::unordered_map>; +} // namespace WifiStaticEmlsrTestConstants + +/// @brief test case information +struct WifiStaticEmlsrTestVector +{ + std::string name; ///< Test case name + std::vector clientChs{}; ///< Channel settings for client device + std::set emlsrLinks{}; ///< EMLSR mode links + Time switchDelay{WifiStaticEmlsrTestConstants::DEFAULT_SWITCH_DELAY}; ///< Radio Switch Delay + MHz_u auxPhyWidth{ + WifiStaticEmlsrTestConstants::DEFAULT_AUX_PHY_CH_WIDTH}; ///< Aux PHY channel width + bool switchAuxPhy{WifiStaticEmlsrTestConstants::DEFAULT_SWITCH_AUX_PHY}; ///< Switch Aux PHY +}; + +/** + * Test static setup of the EMLSR mode. + * + * It is checked that: + * - EMLSR mode is enabled on the expected set of links, both at client side and AP MLD side + * - the channel switch delay is configured on the client links as expected + */ +class WifiStaticEmlsrTest : public TestCase +{ + public: + /** + * Constructor. + * + * @param testVec the test vector + */ + WifiStaticEmlsrTest(const WifiStaticEmlsrTestVector& testVec); + + private: + /// Construct WifiNetDevice + /// @param isAp true if AP, false otherwise + /// @param channelMap created spectrum channels + /// @return constructed WifiNetDevice + Ptr GetWifiNetDevice(bool isAp, + const WifiStaticEmlsrTestConstants::ChannelMap& channelMap); + + /// Construct PHY helper based on input operating channels + /// @param settings vector of strings specifying the operating channels to configure + /// @param channelMap created spectrum channels + /// @return PHY helper + SpectrumWifiPhyHelper GetPhyHelper( + const std::vector& settings, + const WifiStaticEmlsrTestConstants::ChannelMap& channelMap) const; + + /// @return the WifiHelper + WifiHelper GetWifiHelper() const; + /// @return the AP MAC helper + WifiMacHelper GetApMacHelper() const; + /// @return the Client MAC helper + WifiMacHelper GetClientMacHelper() const; + void ValidateEmlsr(); ///< Validate EMLSR setup + void DoRun() override; + void DoSetup() override; + + WifiStaticEmlsrTestVector m_testVec; ///< Test vector + Ptr m_apDev{nullptr}; ///< AP WiFi device + Ptr m_clientDev{nullptr}; ///< client WiFi device +}; + +WifiStaticEmlsrTest::WifiStaticEmlsrTest(const WifiStaticEmlsrTestVector& testVec) + : TestCase(testVec.name), + m_testVec(testVec) +{ +} + +WifiHelper +WifiStaticEmlsrTest::GetWifiHelper() const +{ + WifiHelper wifiHelper; + wifiHelper.SetStandard(WifiStaticEmlsrTestConstants::DEFAULT_WIFI_STANDARD); + wifiHelper.SetRemoteStationManager( + "ns3::ConstantRateWifiManager", + "DataMode", + StringValue(WifiStaticEmlsrTestConstants::DEFAULT_DATA_MODE), + "ControlMode", + StringValue(WifiStaticEmlsrTestConstants::DEFAULT_CONTROL_MODE)); + wifiHelper.ConfigEhtOptions("EmlsrActivated", BooleanValue(true)); + return wifiHelper; +} + +SpectrumWifiPhyHelper +WifiStaticEmlsrTest::GetPhyHelper(const std::vector& settings, + const WifiStaticEmlsrTestConstants::ChannelMap& channelMap) const +{ + NS_ASSERT(not settings.empty()); + SpectrumWifiPhyHelper helper(settings.size()); + + uint8_t linkId = 0; + for (const auto& str : settings) + { + helper.Set(linkId, "ChannelSettings", StringValue(str)); + helper.Set(linkId, "ChannelSwitchDelay", TimeValue(m_testVec.switchDelay)); + auto channelConfig = WifiChannelConfig::FromString(str); + auto phyBand = channelConfig.front().band; + auto freqRange = GetFrequencyRange(phyBand); + helper.AddPhyToFreqRangeMapping(linkId, freqRange); + helper.AddChannel(channelMap.at(phyBand), freqRange); + + ++linkId; + } + return helper; +} + +WifiMacHelper +WifiStaticEmlsrTest::GetApMacHelper() const +{ + WifiMacHelper macHelper; + auto ssid = Ssid(WifiStaticEmlsrTestConstants::DEFAULT_SSID); + + macHelper.SetType("ns3::ApWifiMac", + "Ssid", + SsidValue(ssid), + "BeaconGeneration", + BooleanValue(WifiStaticEmlsrTestConstants::DEFAULT_BEACON_GEN)); + return macHelper; +} + +WifiMacHelper +WifiStaticEmlsrTest::GetClientMacHelper() const +{ + WifiMacHelper macHelper; + Ssid ssid = Ssid(WifiStaticEmlsrTestConstants::DEFAULT_SSID); + macHelper.SetType("ns3::StaWifiMac", "Ssid", SsidValue(ssid)); + macHelper.SetEmlsrManager("ns3::DefaultEmlsrManager", + "EmlsrLinkSet", + AttributeContainerValue(m_testVec.emlsrLinks), + "AuxPhyChannelWidth", + UintegerValue(m_testVec.auxPhyWidth), + "SwitchAuxPhy", + BooleanValue(m_testVec.switchAuxPhy)); + return macHelper; +} + +Ptr +WifiStaticEmlsrTest::GetWifiNetDevice(bool isAp, + const WifiStaticEmlsrTestConstants::ChannelMap& channelMap) +{ + NodeContainer node(1); + auto wifiHelper = GetWifiHelper(); + auto settings = isAp ? WifiStaticEmlsrTestConstants::DEFAULT_AP_CHS : m_testVec.clientChs; + auto phyHelper = GetPhyHelper(settings, channelMap); + auto macHelper = isAp ? GetApMacHelper() : GetClientMacHelper(); + auto netDev = wifiHelper.Install(phyHelper, macHelper, node); + WifiHelper::AssignStreams(netDev, WifiStaticEmlsrTestConstants::DEFAULT_STREAM_INDEX); + return DynamicCast(netDev.Get(0)); +} + +void +WifiStaticEmlsrTest::DoSetup() +{ + RngSeedManager::SetSeed(WifiStaticEmlsrTestConstants::DEFAULT_RNG_SEED); + RngSeedManager::SetRun(WifiStaticEmlsrTestConstants::DEFAULT_RNG_RUN); + + WifiStaticEmlsrTestConstants::ChannelMap channelMap = { + {WIFI_PHY_BAND_2_4GHZ, CreateObject()}, + {WIFI_PHY_BAND_5GHZ, CreateObject()}, + {WIFI_PHY_BAND_6GHZ, CreateObject()}}; + + m_apDev = GetWifiNetDevice(true, channelMap); + NS_ASSERT(m_apDev); + m_clientDev = GetWifiNetDevice(false, channelMap); + NS_ASSERT(m_clientDev); + + WifiStaticSetupHelper::SetStaticAssociation(m_apDev, m_clientDev); + WifiStaticSetupHelper::SetStaticEmlsr(m_apDev, m_clientDev); +} + +void +WifiStaticEmlsrTest::ValidateEmlsr() +{ + auto clientMac = DynamicCast(m_clientDev->GetMac()); + NS_ASSERT_MSG(clientMac, "Expected StaWifiMac"); + NS_TEST_ASSERT_MSG_EQ(clientMac->IsAssociated(), true, "Expected non-AP MLD to be associated"); + auto setupLinks = clientMac->GetSetupLinkIds(); + auto isMldAssoc = (setupLinks.size() > 1); + NS_TEST_ASSERT_MSG_EQ(isMldAssoc, true, "EMLSR mode requires association on multiple links"); + auto emlsrManager = clientMac->GetEmlsrManager(); + NS_ASSERT_MSG(emlsrManager, "EMLSR Manager not set"); + auto clientEmlsrLinks = emlsrManager->GetEmlsrLinks(); + auto match = (clientEmlsrLinks == m_testVec.emlsrLinks); + NS_TEST_ASSERT_MSG_EQ(match, true, "Unexpected set of EMLSR links enabled"); + for (auto linkId : setupLinks) + { + auto expectedState = clientEmlsrLinks.contains(linkId); + auto clientLinkAddr = clientMac->GetFrameExchangeManager(linkId)->GetAddress(); + auto apManager = m_apDev->GetRemoteStationManager(linkId); + auto actualState = apManager->GetEmlsrEnabled(clientLinkAddr); + NS_TEST_ASSERT_MSG_EQ(actualState, + expectedState, + "EMLSR state mismatch on client link ID " << +linkId); + + // Validate Channel switch delay + auto actualDelay = clientMac->GetWifiPhy(linkId)->GetChannelSwitchDelay(); + NS_TEST_ASSERT_MSG_EQ(actualDelay, + m_testVec.switchDelay, + "Channel switch delay mismatch on client link ID " << +linkId); + } +} + +void +WifiStaticEmlsrTest::DoRun() +{ + Simulator::Stop(WifiStaticEmlsrTestConstants::DEFAULT_SIM_STOP_TIME); + Simulator::Run(); + ValidateEmlsr(); + Simulator::Destroy(); +} + +/** + * @ingroup wifi-test + * @ingroup tests + * + * @brief EMLSR static setup test suite + */ +class WifiStaticEmlsrTestSuite : public TestSuite +{ + public: + WifiStaticEmlsrTestSuite(); +}; + +WifiStaticEmlsrTestSuite::WifiStaticEmlsrTestSuite() + : TestSuite("wifi-static-emlsr-test", Type::UNIT) +{ + auto CHANNELS_2_LINKS = {WifiStaticEmlsrTestConstants::CHANNEL_0, + WifiStaticEmlsrTestConstants::CHANNEL_1}; + auto CHANNELS_3_LINKS = {WifiStaticEmlsrTestConstants::CHANNEL_0, + WifiStaticEmlsrTestConstants::CHANNEL_1, + WifiStaticEmlsrTestConstants::CHANNEL_2}; + auto CHANNELS_2_LINKS_ALT = {WifiStaticEmlsrTestConstants::CHANNEL_0, + WifiStaticEmlsrTestConstants::CHANNEL_2}; + + for (const std::vector inputs{ + {"Setup-2-link-EMLSR-2-link", + CHANNELS_2_LINKS, + {0, 1}, + WifiStaticEmlsrTestConstants::DEFAULT_SWITCH_DELAY, + WifiStaticEmlsrTestConstants::DEFAULT_AUX_PHY_CH_WIDTH, + WifiStaticEmlsrTestConstants::DEFAULT_SWITCH_AUX_PHY}, + {"Setup-3-link-EMLSR-2-link", + CHANNELS_3_LINKS, + {0, 1}, + WifiStaticEmlsrTestConstants::DEFAULT_SWITCH_DELAY, + WifiStaticEmlsrTestConstants::DEFAULT_AUX_PHY_CH_WIDTH, + WifiStaticEmlsrTestConstants::DEFAULT_SWITCH_AUX_PHY}, + {"Setup-3-link-EMLSR-2-link-Diff", + CHANNELS_3_LINKS, + {1, 2}, + WifiStaticEmlsrTestConstants::DEFAULT_SWITCH_DELAY, + WifiStaticEmlsrTestConstants::DEFAULT_AUX_PHY_CH_WIDTH, + WifiStaticEmlsrTestConstants::DEFAULT_SWITCH_AUX_PHY}, + {"Setup-3-link-EMLSR-2-link-Diff-2", + CHANNELS_3_LINKS, + {0, 2}, + WifiStaticEmlsrTestConstants::DEFAULT_SWITCH_DELAY, + WifiStaticEmlsrTestConstants::DEFAULT_AUX_PHY_CH_WIDTH, + WifiStaticEmlsrTestConstants::DEFAULT_SWITCH_AUX_PHY}, + {"Setup-3-link-EMLSR-3-link", + CHANNELS_3_LINKS, + {0, 1, 2}, + WifiStaticEmlsrTestConstants::DEFAULT_SWITCH_DELAY, + WifiStaticEmlsrTestConstants::DEFAULT_AUX_PHY_CH_WIDTH, + WifiStaticEmlsrTestConstants::DEFAULT_SWITCH_AUX_PHY}, + {"Setup-2-link-EMLSR-2-link-Diff-Set", + CHANNELS_2_LINKS_ALT, + {0, 2}, + WifiStaticEmlsrTestConstants::DEFAULT_SWITCH_DELAY, + WifiStaticEmlsrTestConstants::DEFAULT_AUX_PHY_CH_WIDTH, + WifiStaticEmlsrTestConstants::DEFAULT_SWITCH_AUX_PHY}, + {"EMLSR-2-link-16us-delay", + CHANNELS_2_LINKS, + {0, 1}, + MicroSeconds(16), + WifiStaticEmlsrTestConstants::DEFAULT_AUX_PHY_CH_WIDTH, + WifiStaticEmlsrTestConstants::DEFAULT_SWITCH_AUX_PHY}, + {"EMLSR-2-link-32us-delay", + CHANNELS_2_LINKS, + {0, 1}, + MicroSeconds(32), + WifiStaticEmlsrTestConstants::DEFAULT_AUX_PHY_CH_WIDTH, + WifiStaticEmlsrTestConstants::DEFAULT_SWITCH_AUX_PHY}, + {"EMLSR-2-link-80MHz-AuxPhy", + CHANNELS_2_LINKS, + {0, 1}, + MicroSeconds(32), + MHz_u{80}, + WifiStaticEmlsrTestConstants::DEFAULT_SWITCH_AUX_PHY}, + {"EMLSR-2-link-Switch-Aux-PHY", + CHANNELS_2_LINKS, + {0, 1}, + WifiStaticEmlsrTestConstants::DEFAULT_SWITCH_DELAY, + WifiStaticEmlsrTestConstants::DEFAULT_AUX_PHY_CH_WIDTH, + true}, + {"EMLSR-2-link-80MHz-AuxPhy-Switch", + CHANNELS_2_LINKS, + {0, 1}, + WifiStaticEmlsrTestConstants::DEFAULT_SWITCH_DELAY, + MHz_u{80}, + true}}; + const auto& input : inputs) + { + AddTestCase(new WifiStaticEmlsrTest(input), TestCase::Duration::QUICK); + } +} + +static WifiStaticEmlsrTestSuite g_wifiStaticEmlsrTestSuite;