From 6ff7c27987b67afb0619c1dae256d20cfc19278f Mon Sep 17 00:00:00 2001 From: Stefano Avallone Date: Wed, 19 Mar 2025 11:46:37 +0100 Subject: [PATCH] wifi: Extend EMLSR test case to check main PHY switch interruption when aux PHY gains channel access --- src/wifi/test/wifi-emlsr-test.cc | 141 +++++++++++++++++++++++++++---- src/wifi/test/wifi-emlsr-test.h | 25 ++++-- 2 files changed, 142 insertions(+), 24 deletions(-) diff --git a/src/wifi/test/wifi-emlsr-test.cc b/src/wifi/test/wifi-emlsr-test.cc index 3a5e6cf90..f5a32a968 100644 --- a/src/wifi/test/wifi-emlsr-test.cc +++ b/src/wifi/test/wifi-emlsr-test.cc @@ -641,6 +641,12 @@ EmlsrOperationsTestBase::SetSsid(std::size_t count) StartTraffic(); // stop generation of beacon frames in order to avoid interference m_apMac->SetAttribute("BeaconGeneration", BooleanValue(false)); + // Set the short slot time on the 2.4 GHz link because it is not updated automatically given + // that no more Beacon frames are sent + for (std::size_t id = 0; id < m_nEmlsrStations + m_nNonEmlsrStations; ++id) + { + m_staMacs[id]->GetDevice()->GetPhy(0)->SetSlot(MicroSeconds(9)); + } // disconnect callbacks m_apMac->TraceDisconnectWithoutContext( "AssociatedSta", @@ -5510,7 +5516,7 @@ EmlsrSwitchMainPhyBackTest::EmlsrSwitchMainPhyBackTest() m_paddingDelay = {MicroSeconds(64)}; m_transitionDelay = {MicroSeconds(64)}; m_establishBaDl = {0}; - m_establishBaUl = {0}; + m_establishBaUl = {0, 4}; m_duration = Seconds(0.5); } @@ -5522,7 +5528,8 @@ EmlsrSwitchMainPhyBackTest::DoSetup() Config::SetDefault("ns3::DefaultEmlsrManager::SwitchAuxPhy", BooleanValue(false)); Config::SetDefault("ns3::EmlsrManager::AuxPhyTxCapable", BooleanValue(false)); // Use only link 1 for DL and UL traffic - std::string mapping = "0 " + std::to_string(m_linkIdForTid0); + std::string mapping = + "0 " + std::to_string(m_linkIdForTid0) + "; 4 " + std::to_string(m_linkIdForTid4); Config::SetDefault("ns3::EhtConfiguration::TidToLinkMappingDl", StringValue(mapping)); Config::SetDefault("ns3::EhtConfiguration::TidToLinkMappingUl", StringValue(mapping)); Config::SetDefault("ns3::EmlsrManager::AuxPhyMaxModClass", StringValue("HT")); @@ -5621,6 +5628,33 @@ EmlsrSwitchMainPhyBackTest::MainPhySwitchInfoCallback(std::size_t index, return; } + // lambda to generate a frame with TID 4 and to handle the corresponding frames + auto genTid4Frame = [=, this]() { + m_dlPktDone = true; + + // in 5 microseconds, while still switching, generate a packet with TID 4, which causes a + // channel access request on link 0; if switching can be interrupted, the main PHY starts + // switching to link 0 as soon as channel access is gained on link 0 + Simulator::Schedule(MicroSeconds(5), [=, this]() { + m_staMacs[0]->GetDevice()->GetNode()->AddApplication( + GetApplication(UPLINK, 0, 1, 500, 4)); + // channel access can be obtained within a slot due to slot alignment + Simulator::Schedule(m_apMac->GetWifiPhy(m_linkIdForTid4)->GetSlot(), [=, this]() { + auto advEmlsrMgr = + DynamicCast(m_staMacs[0]->GetEmlsrManager()); + + NS_TEST_EXPECT_MSG_EQ(advEmlsrMgr->m_mainPhySwitchInfo.disconnected, + true, + "Expected the main PHY to be switching"); + NS_TEST_EXPECT_MSG_EQ( + +advEmlsrMgr->m_mainPhySwitchInfo.to, + +(advEmlsrMgr->m_interruptSwitching ? m_linkIdForTid4 : m_mainPhyId), + "Main PHY is switching to wrong link"); + }); + }); + InsertEventsForQosTid4(); + }; + if (m_expectedMainPhySwitchBackTime == Simulator::Now() && info.GetName() == "TxopNotGainedOnAuxPhyLink") { @@ -5704,7 +5738,8 @@ EmlsrSwitchMainPhyBackTest::MainPhySwitchInfoCallback(std::size_t index, default: NS_TEST_ASSERT_MSG_EQ(true, false, "Unexpected scenario: " << +m_testIndex); } - m_dlPktDone = true; + + genTid4Frame(); } if (m_expectedMainPhySwitchBackTime == Simulator::Now() && info.GetName() == "TxopEnded") @@ -5715,10 +5750,75 @@ EmlsrSwitchMainPhyBackTest::MainPhySwitchInfoCallback(std::size_t index, +static_cast(NON_HT_PPDU_DONT_USE_MAC_HDR), "Unexpected TxopEnded reason for switching main PHY back"); - m_dlPktDone = true; + genTid4Frame(); } } +void +EmlsrSwitchMainPhyBackTest::InsertEventsForQosTid4() +{ + const auto testIndex = static_cast(m_testIndex); + std::list events; + + events.emplace_back( + WIFI_MAC_QOSDATA, + [=, this](Ptr psdu, const WifiTxVector& txVector, uint8_t linkId) { + NS_TEST_EXPECT_MSG_EQ(+linkId, + +m_linkIdForTid4, + "Unicast frame with TID 4 transmitted on wrong link"); + NS_TEST_EXPECT_MSG_EQ(psdu->GetAddr1(), + m_apMac->GetFrameExchangeManager(linkId)->GetAddress(), + "Unexpected RA for the unicast frame with TID 4"); + NS_TEST_EXPECT_MSG_EQ(psdu->GetAddr2(), + m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress(), + "Unexpected TA for the unicast frame with TID 4"); + NS_TEST_EXPECT_MSG_EQ(+(*psdu->GetTids().cbegin()), + 4, + "Expected a unicast frame with TID 4"); + // if switching can be interrupted, the frame with TID 4 is transmitted as soon as + // the main PHY completes the switching to link 0 + if (auto advEmlsrMgr = + DynamicCast(m_staMacs[0]->GetEmlsrManager()); + advEmlsrMgr->m_interruptSwitching) + { + auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId); + NS_TEST_EXPECT_MSG_EQ(advEmlsrMgr->m_mainPhySwitchInfo.start + + mainPhy->GetChannelSwitchDelay(), + Simulator::Now(), + "Expected TX to start at main PHY switch end"); + } + }); + + events.emplace_back(WIFI_MAC_CTL_ACK); + + events.emplace_back( + WIFI_MAC_CTL_END, + [=, this](Ptr psdu, const WifiTxVector& txVector, uint8_t linkId) { + if (testIndex == NON_HT_PPDU_DONT_USE_MAC_HDR) + { + Simulator::Schedule(MilliSeconds(2), [=, this]() { + // check that trace infos have been received + NS_TEST_EXPECT_MSG_EQ(m_dlPktDone, + true, + "Did not receive the expected trace infos"); + NS_TEST_EXPECT_MSG_EQ(m_events.empty(), true, "Not all events took place"); + + if (++m_testIndex < static_cast(COUNT)) + { + RunOne(); + } + }); + } + }); + + // In the NON_HT_PPDU_DONT_USE_MAC_HDR scenario, the main PHY does not switch back to the + // preferred link after the transmission of the broadcast frame, so the QoS data frame with + // TID 0 is transmitted (on link 1) before the QoS data frame with TID 4 (on link 0) + const auto pos = + (testIndex == NON_HT_PPDU_DONT_USE_MAC_HDR ? m_events.cend() : m_events.cbegin()); + m_events.splice(pos, events); +} + void EmlsrSwitchMainPhyBackTest::RunOne() { @@ -5745,7 +5845,9 @@ EmlsrSwitchMainPhyBackTest::RunOne() m_switchMainPhyBackDelay -= MicroSeconds(250); } - const auto interruptSwitch = (testIndex == RXSTART_WHILE_SWITCH_INTERRUPT); + const auto interruptSwitch = + (testIndex == RXSTART_WHILE_SWITCH_INTERRUPT || testIndex == NON_HT_PPDU_DONT_USE_MAC_HDR || + testIndex == LONG_SWITCH_BACK_DELAY_USE_MAC_HDR); const auto useMacHeader = (testIndex == NON_HT_PPDU_USE_MAC_HDR || testIndex == LONG_SWITCH_BACK_DELAY_USE_MAC_HDR); @@ -5755,6 +5857,9 @@ EmlsrSwitchMainPhyBackTest::RunOne() TimeValue(m_switchMainPhyBackDelay)); m_staMacs[0]->GetEmlsrManager()->SetAttribute("InterruptSwitch", BooleanValue(interruptSwitch)); m_staMacs[0]->GetEmlsrManager()->SetAttribute("UseNotifiedMacHdr", BooleanValue(useMacHeader)); + m_staMacs[0]->GetEmlsrManager()->SetAttribute("CheckAccessOnMainPhyLink", BooleanValue(false)); + // no in-device interference, just to avoid starting MSD timer causing RTS-CTS exchanges + m_staMacs[0]->GetEmlsrManager()->SetAttribute("InDeviceInterference", BooleanValue(false)); NS_LOG_INFO("Starting test #" << +m_testIndex << "\n"); m_dlPktDone = false; @@ -6062,19 +6167,21 @@ EmlsrSwitchMainPhyBackTest::RunOne() // main PHY is expected to switch back when the UL TXOP ends m_expectedMainPhySwitchBackTime = Simulator::Now() + txDuration; } + else + { + Simulator::Schedule(MilliSeconds(2), [=, this]() { + // check that trace infos have been received + NS_TEST_EXPECT_MSG_EQ(m_dlPktDone, + true, + "Did not receive the expected trace infos"); + NS_TEST_EXPECT_MSG_EQ(m_events.empty(), true, "Not all events took place"); - Simulator::Schedule(MilliSeconds(2), [=, this]() { - // check that trace infos have been received - NS_TEST_EXPECT_MSG_EQ(m_dlPktDone, - true, - "Did not receive the expected trace infos"); - NS_TEST_EXPECT_MSG_EQ(m_events.empty(), true, "Not all events took place"); - - if (++m_testIndex < static_cast(COUNT)) - { - RunOne(); - } - }); + if (++m_testIndex < static_cast(COUNT)) + { + RunOne(); + } + }); + } }); } diff --git a/src/wifi/test/wifi-emlsr-test.h b/src/wifi/test/wifi-emlsr-test.h index 96c669a34..ae9a0a095 100644 --- a/src/wifi/test/wifi-emlsr-test.h +++ b/src/wifi/test/wifi-emlsr-test.h @@ -1230,11 +1230,11 @@ class EmlsrIcfSentDuringMainPhySwitchTest : public EmlsrOperationsTestBase * An AP MLD and an EMLSR client, both having 3 links, are considered in this test. Aux PHYs are * not TX capable, do not switch links and support up to the HT modulation class; the preferred link * is link 2. In order to control link switches, a TID-to-Link mapping is configured so that TID 0 - * is mapped onto link 1 for both DL and UL. In this test, the main PHY switches to link 1 to start - * an UL TXOP but, while the main PHY is switching or shortly after the channel switch ends, the AP - * MLD transmits a QoS Data broadcast frame on link 1 using a modulation supported by the aux PHYs. - * Different situations are tested and it is verified that the main PHY switches back to the - * preferred link as expected. Scenarios: + * is mapped onto link 1 and TID 4 is mapped onto link 0 (for both DL and UL). In this test, the + * main PHY switches to link 1 to start an UL TXOP but, while the main PHY is switching or shortly + * after the channel switch ends, the AP MLD transmits a QoS Data broadcast frame on link 1 using a + * modulation supported by the aux PHYs. Different situations are tested and it is verified that + * the main PHY switches back to the preferred link as expected. Scenarios: * * - RXSTART_WHILE_SWITCH_NO_INTERRUPT: the AP MLD transmits an HT PPDU while the main PHY is * switching; at the end of the PHY header reception (while the main PHY is still switching), the @@ -1282,8 +1282,15 @@ class EmlsrIcfSentDuringMainPhySwitchTest : public EmlsrOperationsTestBase * end of the switch main PHY back timer plus a channel switch delay and the main PHY switches * back to the preferred link (with reason BACKOFF_END). * - * In all the cases, it is verified that, after the reception of the broadcast data frame, the EMLSR - * client transmits the data frame and receives the acknowledgment. + * Except for the NON_HT_PPDU_DONT_USE_MAC_HDR case, in which the main PHY stays on link 1 and + * transmits a data frame, receives the Ack and switches back to the preferred link at the TXOP end, + * in all other cases the main PHY switches back to the preferred link without sending a frame on + * link 1. A few microseconds after starting the switch to the preferred link, a frame with TID 4 + * is queued. If interrupt switching is enabled, the switch to the preferred link is interrupted + * and the main PHY switches to link 0, where it transmits the data frame with TID 4 as soon as + * completing the switch. Afterwards, the main PHY switches back to the preferred link and, except + * for the NON_HT_PPDU_DONT_USE_MAC_HDR case, it switches to link 1 to transmit the queued frame + * with TID 0. */ class EmlsrSwitchMainPhyBackTest : public EmlsrOperationsTestBase { @@ -1316,6 +1323,9 @@ class EmlsrSwitchMainPhyBackTest : public EmlsrOperationsTestBase double txPowerW) override; void MainPhySwitchInfoCallback(std::size_t index, const EmlsrMainPhySwitchTrace& info) override; + /// Insert events corresponding to the UL TXOP to transmit the QoS Data frame with TID 4 + void InsertEventsForQosTid4(); + /// Runs a test case and invokes itself for the next test case void RunOne(); @@ -1349,6 +1359,7 @@ class EmlsrSwitchMainPhyBackTest : public EmlsrOperationsTestBase std::list m_events; //!< list of events for a test run std::size_t m_processedEvents{0}; //!< number of processed events const uint8_t m_linkIdForTid0{1}; //!< ID of the link on which TID 0 is mapped + const uint8_t m_linkIdForTid4{0}; //!< ID of the link on which TID 4 is mapped Ptr m_bcastFrame; //!< the broadcast frame sent by the AP MLD Time m_switchMainPhyBackDelay; //!< the switch main PHY back delay Time m_expectedMainPhySwitchBackTime; //!< expected main PHY switch back time