From 1f736373d028b40308d06a81cab3285c815a2850 Mon Sep 17 00:00:00 2001 From: Stefano Avallone Date: Wed, 29 Jan 2025 17:03:07 +0100 Subject: [PATCH] wifi: Extend EMLSR test to check latest fixes/improvements --- src/wifi/test/wifi-emlsr-test.cc | 453 ++++++++++++++++++++++++++++++- src/wifi/test/wifi-emlsr-test.h | 149 ++++++++++ 2 files changed, 601 insertions(+), 1 deletion(-) diff --git a/src/wifi/test/wifi-emlsr-test.cc b/src/wifi/test/wifi-emlsr-test.cc index ff4372534..902071b04 100644 --- a/src/wifi/test/wifi-emlsr-test.cc +++ b/src/wifi/test/wifi-emlsr-test.cc @@ -8,13 +8,14 @@ #include "wifi-emlsr-test.h" +#include "ns3/advanced-emlsr-manager.h" #include "ns3/attribute-container.h" #include "ns3/boolean.h" #include "ns3/config.h" #include "ns3/ctrl-headers.h" #include "ns3/eht-configuration.h" #include "ns3/eht-frame-exchange-manager.h" -#include "ns3/emlsr-manager.h" +#include "ns3/interference-helper.h" #include "ns3/log.h" #include "ns3/mgt-action-headers.h" #include "ns3/mobility-helper.h" @@ -22,6 +23,7 @@ #include "ns3/node-list.h" #include "ns3/packet-socket-helper.h" #include "ns3/packet-socket-server.h" +#include "ns3/pointer.h" #include "ns3/qos-txop.h" #include "ns3/rng-seed-manager.h" #include "ns3/rr-multi-user-scheduler.h" @@ -30,6 +32,7 @@ #include "ns3/spectrum-wifi-phy.h" #include "ns3/string.h" #include "ns3/wifi-net-device.h" +#include "ns3/wifi-spectrum-phy-interface.h" #include #include @@ -5263,6 +5266,452 @@ SingleLinkEmlsrTest::DoRun() Simulator::Destroy(); } +EmlsrIcfSentDuringMainPhySwitchTest::EmlsrIcfSentDuringMainPhySwitchTest() + : EmlsrOperationsTestBase("Check ICF reception while main PHY is switching") +{ + m_mainPhyId = 0; + m_linksToEnableEmlsrOn = {0, 1, 2}; + m_nEmlsrStations = 1; + m_nNonEmlsrStations = 0; + + // channel switch delay will be also set to 64 us + m_paddingDelay = {MicroSeconds(128)}; + m_transitionDelay = {MicroSeconds(64)}; + m_establishBaDl = {0, 3}; + m_establishBaUl = {0, 3}; + m_duration = Seconds(0.5); +} + +void +EmlsrIcfSentDuringMainPhySwitchTest::DoSetup() +{ + // channel switch delay will be modified during test scenarios + Config::SetDefault("ns3::WifiPhy::ChannelSwitchDelay", TimeValue(MicroSeconds(64))); + Config::SetDefault("ns3::WifiPhy::NotifyMacHdrRxEnd", BooleanValue(true)); + Config::SetDefault("ns3::DefaultEmlsrManager::SwitchAuxPhy", BooleanValue(false)); + Config::SetDefault("ns3::EmlsrManager::AuxPhyTxCapable", BooleanValue(false)); + // AP MLD transmits both TID 0 and TID 3 on link 1 + Config::SetDefault("ns3::EhtConfiguration::TidToLinkMappingDl", StringValue("0,3 1")); + // EMLSR client transmits TID 0 on link 1 and TID 3 on link 2 + Config::SetDefault("ns3::EhtConfiguration::TidToLinkMappingUl", + StringValue("0 1; 3 " + std::to_string(m_linkIdForTid3))); + + EmlsrOperationsTestBase::DoSetup(); + + m_staMacs[0]->TraceConnectWithoutContext( + "EmlsrLinkSwitch", + MakeCallback(&EmlsrIcfSentDuringMainPhySwitchTest::EmlsrLinkSwitchCb, this)); + + for (uint8_t i = 0; i < m_staMacs[0]->GetDevice()->GetNPhys(); ++i) + { + m_bands.at(i) = m_staMacs[0]->GetDevice()->GetPhy(i)->GetBand(MHz_u{20}, 0); + } +} + +void +EmlsrIcfSentDuringMainPhySwitchTest::GenerateNoiseOnAllLinks(Ptr mac, Time duration) +{ + for (const auto linkId : mac->GetLinkIds()) + { + auto phy = DynamicCast(mac->GetWifiPhy(linkId)); + NS_TEST_ASSERT_MSG_NE(phy, nullptr, "No PHY on link " << +linkId); + const auto txPower = phy->GetPower(1) + phy->GetTxGain(); + + auto psd = Create(phy->GetCurrentInterface()->GetRxSpectrumModel()); + *psd = txPower; + + auto spectrumSignalParams = Create(); + spectrumSignalParams->duration = duration; + spectrumSignalParams->txPhy = phy->GetCurrentInterface(); + spectrumSignalParams->txAntenna = phy->GetAntenna(); + spectrumSignalParams->psd = psd; + + phy->StartRx(spectrumSignalParams, phy->GetCurrentInterface()); + } +} + +void +EmlsrIcfSentDuringMainPhySwitchTest::CheckInDeviceInterference(const std::string& frameTypeStr, + uint8_t linkId, + Time duration) +{ + for (const auto& phy : m_staMacs[0]->GetDevice()->GetPhys()) + { + // ignore the PHY that is transmitting + if (m_staMacs[0]->GetLinkForPhy(phy) == linkId) + { + continue; + } + + PointerValue ptr; + phy->GetAttribute("InterferenceHelper", ptr); + auto interferenceHelper = ptr.Get(); + + // we need to check that all the PHY interfaces recorded the in-device interference, + // hence we consider a 20 MHz sub-band of the frequency channels of all the links + for (uint8_t i = 0; i < m_staMacs[0]->GetNLinks(); ++i) + { + auto energyDuration = + interferenceHelper->GetEnergyDuration(DbmToW(phy->GetCcaEdThreshold()), + m_bands.at(i)); + + NS_TEST_EXPECT_MSG_EQ( + energyDuration, + duration, + m_testStr << ", " << frameTypeStr << ": Unexpected energy duration for PHY " + << +phy->GetPhyId() << " in the band corresponding to link " << +i); + } + } +} + +void +EmlsrIcfSentDuringMainPhySwitchTest::Transmit(Ptr 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 before setup is completed + if (!m_setupDone) + { + return; + } + + auto linkId = mac->GetLinkForPhy(phyId); + NS_TEST_ASSERT_MSG_EQ(linkId.has_value(), + true, + "PHY " << +phyId << " is not operating on any link"); + + if (!m_events.empty()) + { + // check that the expected frame is being transmitted + NS_TEST_EXPECT_MSG_EQ(m_events.front().hdrType, + hdr.GetType(), + "Unexpected MAC header type for frame #" << ++m_processedEvents); + // perform actions/checks, if any + if (m_events.front().func) + { + m_events.front().func(psdu, txVector, linkId.value()); + } + + m_events.pop_front(); + } +} + +void +EmlsrIcfSentDuringMainPhySwitchTest::StartTraffic() +{ + m_setupDone = true; + RunOne(); +} + +void +EmlsrIcfSentDuringMainPhySwitchTest::DoRun() +{ + Simulator::Stop(m_duration); + Simulator::Run(); + + NS_TEST_EXPECT_MSG_EQ(m_events.empty(), true, "Not all events took place"); + + Simulator::Destroy(); +} + +void +EmlsrIcfSentDuringMainPhySwitchTest::EmlsrLinkSwitchCb(uint8_t linkId, + Ptr phy, + bool connected) +{ + if (!m_setupDone) + { + return; + } + + if (!connected) + { + const auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId); + NS_LOG_DEBUG("Main PHY leaving link " << +linkId << ", switch delay " + << mainPhy->GetChannelSwitchDelay().As(Time::US) + << "\n"); + m_switchFrom = MainPhySwitchInfo{Simulator::Now(), linkId}; + m_switchTo.reset(); + } + else + { + NS_LOG_DEBUG((phy->GetPhyId() == m_mainPhyId ? "Main" : "Aux") + << " PHY connected to link " << +linkId << "\n"); + if (phy->GetPhyId() == m_mainPhyId) + { + m_switchTo = MainPhySwitchInfo{Simulator::Now(), linkId}; + m_switchFrom.reset(); + } + } +} + +void +EmlsrIcfSentDuringMainPhySwitchTest::RunOne() +{ + const auto useMacHdrInfo = ((m_testIndex & 0b001) != 0); + const auto interruptSwitch = ((m_testIndex & 0b010) != 0); + const auto switchToOtherLink = ((m_testIndex & 0b100) != 0); + + m_staMacs[0]->GetEmlsrManager()->SetAttribute("UseNotifiedMacHdr", BooleanValue(useMacHdrInfo)); + auto advEmlsrMgr = DynamicCast(m_staMacs[0]->GetEmlsrManager()); + NS_TEST_ASSERT_MSG_NE(advEmlsrMgr, nullptr, "Advanced EMLSR Manager required"); + advEmlsrMgr->SetAttribute("InterruptSwitch", BooleanValue(interruptSwitch)); + + m_testStr = "SwitchToOtherLink=" + std::to_string(switchToOtherLink) + + ", InterruptSwitch=" + std::to_string(interruptSwitch) + + ", UseMacHdrInfo=" + std::to_string(useMacHdrInfo) + + ", ChannelSwitchDurationIdx=" + std::to_string(m_csdIndex); + NS_LOG_INFO("Starting test: " << m_testStr << "\n"); + + // generate noise on all the links of the AP MLD and the EMLSR client, so as to align the EDCA + // backoff boundaries + Simulator::Schedule(MilliSeconds(3), [=, this]() { + GenerateNoiseOnAllLinks(m_apMac, MicroSeconds(500)); + GenerateNoiseOnAllLinks(m_staMacs[0], MicroSeconds(500)); + }); + + // wait some more time to ensure that backoffs count down to zero and then generate a packet + // at the AP MLD and a packet at the EMLSR client. AP MLD and EMLSR client are expected to get + // access at the same time because backoff counter is zero and EDCA boundaries are aligned + Simulator::Schedule(MilliSeconds(5), [=, this]() { + uint8_t prio = (switchToOtherLink ? 3 : 0); + m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, 0, 1, 500, prio)); + m_staMacs[0]->GetDevice()->GetNode()->AddApplication( + GetApplication(UPLINK, 0, 1, 500, prio)); + }); + + m_switchFrom.reset(); + m_switchTo.reset(); + + m_events.emplace_back( + WIFI_MAC_CTL_TRIGGER, + [=, this](Ptr psdu, const WifiTxVector& txVector, uint8_t linkId) { + const auto phyHdrDuration = WifiPhy::CalculatePhyPreambleAndHeaderDuration(txVector); + const auto txDuration = + WifiPhy::CalculateTxDuration(psdu, + txVector, + m_apMac->GetWifiPhy(linkId)->GetPhyBand()); + auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId); + + // compute channel switch delay based on the scenario to test + Time channelSwitchDelay{0}; + const auto margin = MicroSeconds(2); + + switch (m_csdIndex) + { + case BEFORE_PHY_HDR_END: + channelSwitchDelay = phyHdrDuration - margin; + break; + case BEFORE_MAC_HDR_END: + channelSwitchDelay = phyHdrDuration + margin; + break; + case BEFORE_MAC_PAYLOAD_END: + channelSwitchDelay = txDuration - m_paddingDelay.at(0) - margin; + break; + case BEFORE_PADDING_END: + channelSwitchDelay = txDuration - m_paddingDelay.at(0) + margin; + break; + default:; + } + + NS_TEST_ASSERT_MSG_EQ(channelSwitchDelay.IsStrictlyPositive(), + true, + m_testStr << ": Channel switch delay is not strictly positive (" + << channelSwitchDelay.As(Time::US) << ")"); + NS_TEST_ASSERT_MSG_LT(channelSwitchDelay, + m_paddingDelay.at(0), + m_testStr + << ": Channel switch delay is greater than padding delay"); + // set channel switch delay + mainPhy->SetAttribute("ChannelSwitchDelay", TimeValue(channelSwitchDelay)); + + const auto startTx = Simulator::Now(); + + // check that main PHY has started switching + Simulator::ScheduleNow([=, this]() { + NS_TEST_ASSERT_MSG_EQ(m_switchFrom.has_value(), + true, + m_testStr << ": Main PHY did not start switching"); + NS_TEST_EXPECT_MSG_EQ(+m_switchFrom->linkId, + +m_mainPhyId, + m_testStr << ": Main PHY did not left the preferred link"); + NS_TEST_EXPECT_MSG_EQ(m_switchFrom->time, + startTx, + m_testStr + << ": Main PHY did not start switching at ICF TX start"); + }); + + // check what happens after channel switch is completed + Simulator::Schedule(channelSwitchDelay + TimeStep(1), [=, this]() { + // sanity check that the channel switch delay was computed correctly + auto auxPhy = m_staMacs[0]->GetWifiPhy(linkId); + auto fem = m_staMacs[0]->GetFrameExchangeManager(linkId); + switch (m_csdIndex) + { + case BEFORE_PADDING_END: + NS_TEST_EXPECT_MSG_GT( + Simulator::Now(), + startTx + txDuration - m_paddingDelay.at(0), + m_testStr << ": Channel switch terminated before padding start"); + [[fallthrough]]; + case BEFORE_MAC_PAYLOAD_END: + if (useMacHdrInfo) + { + NS_TEST_EXPECT_MSG_EQ(fem->GetReceivedMacHdr().has_value(), + true, + m_testStr << ": Channel switch terminated before " + "MAC header info is received"); + } + [[fallthrough]]; + case BEFORE_MAC_HDR_END: + NS_TEST_EXPECT_MSG_EQ(fem->GetOngoingRxInfo().has_value(), + true, + m_testStr << ": Channel switch terminated before " + "receiving RXSTART indication"); + break; + case BEFORE_PHY_HDR_END: + NS_TEST_EXPECT_MSG_EQ(auxPhy->GetInfoIfRxingPhyHeader().has_value(), + true, + m_testStr << ": Expected to be receiving the PHY header"); + break; + default: + NS_ABORT_MSG("Unexpected channel switch duration index"); + } + + // if the main PHY switched to the same link on which the ICF is being received, + // connecting the main PHY to the link is postponed until the end of the ICF, hence + // the main PHY is not operating on any link at this time; + // if the main PHY switched to another link, it was connected to that link but + // the UL TXOP did not start because, at the end of the NAV and CCA busy in the last + // PIFS check, it was detected that a frame which could be an ICF was being received + // on another link) + NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetLinkForPhy(m_mainPhyId).has_value(), + switchToOtherLink, + m_testStr + << ": Main PHY not expected to be connected to a link"); + + if (switchToOtherLink) + { + NS_TEST_EXPECT_MSG_EQ( + +m_staMacs[0]->GetLinkForPhy(m_mainPhyId).value(), + +m_linkIdForTid3, + m_testStr << ": Main PHY did not left the link on which TID 3 is mapped"); + } + }); + + // check what happens when the ICF ends + Simulator::Schedule(txDuration + TimeStep(1), [=, this]() { + // if the main PHY switched to the same link on which the ICF has been received, + // it has now been connected to that link; if the main PHY switched to another + // link and there was not enough time for the main PHY to start switching to the + // link on which the ICF has been received at the start of the padding, the ICF + // has been dropped and the main PHY stayed on the preferred link + + const auto id = m_staMacs[0]->GetLinkForPhy(m_mainPhyId); + NS_TEST_EXPECT_MSG_EQ(id.has_value(), + true, + m_testStr << ": Main PHY expected to be connected to a link"); + NS_TEST_EXPECT_MSG_EQ(+id.value(), + +linkId, + m_testStr << ": Main PHY connected to an unexpected link"); + + NS_TEST_ASSERT_MSG_EQ(m_switchTo.has_value(), + true, + m_testStr << ": Main PHY was not connected to a link"); + NS_TEST_EXPECT_MSG_EQ(+m_switchTo->linkId, + +linkId, + m_testStr + << ": Main PHY was not connected to the expected link"); + NS_TEST_EXPECT_MSG_EQ(m_switchTo->time, + Simulator::Now() - TimeStep(1), + m_testStr << ": Main PHY was not connected at ICF TX end"); + }); + }); + + m_events.emplace_back( + WIFI_MAC_CTL_CTS, + [=, this](Ptr psdu, const WifiTxVector& txVector, uint8_t linkId) { + const auto id = m_staMacs[0]->GetLinkForPhy(m_mainPhyId); + NS_TEST_EXPECT_MSG_EQ(id.has_value(), + true, + m_testStr << ": Main PHY expected to be connected to a link"); + NS_TEST_EXPECT_MSG_EQ(+id.value(), + +linkId, + m_testStr + << ": Main PHY expected to be connected to same link as ICF"); + Simulator::ScheduleNow([=, this]() { + NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId)->IsStateTx(), + true, + m_testStr << ": Main PHY expected to be transmitting"); + }); + + const auto txDuration = + WifiPhy::CalculateTxDuration(psdu, + txVector, + m_staMacs[0]->GetWifiPhy(linkId)->GetPhyBand()); + + Simulator::ScheduleNow([=, this]() { + CheckInDeviceInterference(m_testStr + ", CTS", linkId, txDuration); + }); + }); + + m_events.emplace_back(WIFI_MAC_QOSDATA); + m_events.emplace_back( + WIFI_MAC_CTL_ACK, + [=, this](Ptr psdu, const WifiTxVector& txVector, uint8_t linkId) { + const auto txDuration = + WifiPhy::CalculateTxDuration(psdu, + txVector, + m_staMacs[0]->GetWifiPhy(linkId)->GetPhyBand()); + + Simulator::ScheduleNow([=, this]() { + CheckInDeviceInterference(m_testStr + ", ACK", linkId, txDuration); + }); + }); + + // Uplink TXOP + m_events.emplace_back( + WIFI_MAC_QOSDATA, + [=, this](Ptr psdu, const WifiTxVector& txVector, uint8_t linkId) { + const auto txDuration = + WifiPhy::CalculateTxDuration(psdu, + txVector, + m_staMacs[0]->GetWifiPhy(linkId)->GetPhyBand()); + + Simulator::ScheduleNow([=, this]() { + CheckInDeviceInterference(m_testStr + ", QoS Data", linkId, txDuration); + }); + }); + + m_events.emplace_back( + WIFI_MAC_CTL_ACK, + [=, this](Ptr psdu, const WifiTxVector& txVector, uint8_t linkId) { + // Continue with the next test scenario + Simulator::Schedule(MilliSeconds(2), [=, this]() { + NS_TEST_EXPECT_MSG_EQ(m_events.empty(), true, "Not all events took place"); + + m_csdIndex = static_cast(static_cast(m_csdIndex) + 1); + if (m_csdIndex == CSD_COUNT) + { + ++m_testIndex; + m_csdIndex = BEFORE_PHY_HDR_END; + } + + if (m_testIndex < 8) + { + RunOne(); + } + }); + }); +} + WifiEmlsrTestSuite::WifiEmlsrTestSuite() : TestSuite("wifi-emlsr", Type::UNIT) { @@ -5369,6 +5818,8 @@ WifiEmlsrTestSuite::WifiEmlsrTestSuite() } } + AddTestCase(new EmlsrIcfSentDuringMainPhySwitchTest(), TestCase::Duration::QUICK); + AddTestCase(new EmlsrUlOfdmaTest(false), TestCase::Duration::QUICK); AddTestCase(new EmlsrUlOfdmaTest(true), TestCase::Duration::QUICK); diff --git a/src/wifi/test/wifi-emlsr-test.h b/src/wifi/test/wifi-emlsr-test.h index d5e87c5be..2109cc301 100644 --- a/src/wifi/test/wifi-emlsr-test.h +++ b/src/wifi/test/wifi-emlsr-test.h @@ -1091,4 +1091,153 @@ class SingleLinkEmlsrTest : public EmlsrOperationsTestBase std::list::const_iterator m_eventIt; //!< iterator over the list of events }; +/** + * @ingroup wifi-test + * @ingroup tests + * + * @brief Check ICF reception while main PHY is switching. + * + * An AP MLD and an EMLSR client, both having 3 links, are considered in this test. Aux PHYs are + * not TX capable and do not switch links; the preferred link is link 0. In order to control link + * switches, a TID-to-Link mapping is configured so that TIDs 0 and 3 are mapped onto link 1 in the + * DL direction, while TID 0 is mapped to link 1 and TID 3 is mapped to link 2 in the UL direction. + * In this way, the AP MLD always requests channel access on link 1, while the EMLSR client requests + * channel access on link 1 or link 2, depending on the TID. This test consists in having the AP MLD + * and the EMLSR client gain channel access simultaneously: the AP MLD starts transmitting an ICF, + * while the main PHY starts switching to the link on which the EMLSR client gained channel access, + * which could be either the link on which the ICF is being transmitted or another one, depending + * on the TID of the MPDU the EMLSR client has to transmit. + * + * The channel switch delay for the main PHY varies across test scenarios and is computed so that + * the channel switch terminates during one of the different steps of the reception of the ICF: + * before the PHY header end, before the MAC header end, before the padding start and after the + * padding start. + * + \verbatim + ┌──────┬──────┬────────────────────┬───────┐ + │ PHY │ MAC │ MAC PAYLOAD │ │ + │HEADER│HEADER│(COMMON & USER INFO)│PADDING│ + └──────┴──────┴────────────────────┴───────┘ + \endverbatim + * + * All the combinations of the following are tested: + * - main PHY switches to the same link as ICF or to another link + * - channel switch can be interrupted or not + * - MAC header reception information is available and can be used or not + * + * In all the cases, we check that the EMLSR client responds to the ICF: + * - if the main PHY switches to the same link as the ICF, connecting the main PHY to the link is + * postponed until the end of the ICF + * - if the main PHY switches to another link, the UL TXOP does not start because it is detected + * that a frame which could be an ICF is being received on another link + * + * The UL TXOP is started following the DL TXOP end. + * + * It is also checked that the in-device interference generated by every transmission of the EMLSR + * client is tracked by all the PHY interfaces of all the PHYs but the PHY that is transmitting + * for the entire duration of the transmission. + */ +class EmlsrIcfSentDuringMainPhySwitchTest : public EmlsrOperationsTestBase +{ + public: + /// Constructor. + EmlsrIcfSentDuringMainPhySwitchTest(); + + /** + * Enumeration indicating the duration of a main PHY channel switch compared to the ICF fields + */ + enum ChannelSwitchEnd : uint8_t + { + BEFORE_PHY_HDR_END = 0, + BEFORE_MAC_HDR_END, + BEFORE_MAC_PAYLOAD_END, + BEFORE_PADDING_END, + CSD_COUNT + }; + + protected: + void DoSetup() override; + void DoRun() override; + void Transmit(Ptr mac, + uint8_t phyId, + WifiConstPsduMap psduMap, + WifiTxVector txVector, + double txPowerW) override; + + /// Runs a test case and invokes itself for the next test case + void RunOne(); + + /** + * Callback connected to the EmlsrLinkSwitch trace source of the StaWifiMac of the EMLSR client. + * + * @param linkId the ID of the link which the PHY is connected to/disconnected from + * @param phy a pointer to the PHY that is connected to/disconnected from the given link + * @param connected true if the PHY is connected, false if the PHY is disconnected + */ + void EmlsrLinkSwitchCb(uint8_t linkId, Ptr phy, bool connected); + + /** + * Generate noise on all the links of the given MAC for the given time duration. This is used + * to align the EDCA backoff boundary on all the links for the given MAC. + * + * @param mac the given MAC + * @param duration the given duration + */ + void GenerateNoiseOnAllLinks(Ptr mac, Time duration); + + /** + * Check that the in-device interference generated by a transmission of the given duration + * on the given link is tracked by all the PHY interfaces of all the PHYs but the PHY that + * is transmitting. + * + * @param testStr the test string + * @param linkId the ID of the given link + * @param duration the duration of the transmission + */ + void CheckInDeviceInterference(const std::string& testStr, uint8_t linkId, Time duration); + + /// 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, const WifiTxVector&, uint8_t)>&& f = {}) + : hdrType(type), + func(f) + { + } + + WifiMacType hdrType; ///< MAC header type of frame being transmitted + std::function, const WifiTxVector&, uint8_t)> + func; ///< function to perform actions and checks + }; + + /// Store information about a main PHY switch + struct MainPhySwitchInfo + { + Time time; ///< the time the main PHY left/was connected to a link + uint8_t linkId; ///< the ID of the link the main PHY switched from/to + }; + + private: + void StartTraffic() override; + + ChannelSwitchEnd m_csdIndex{ + BEFORE_PHY_HDR_END}; //!< index to iterate over channel switch durations + uint8_t m_testIndex{0}; //!< index to iterate over test scenarios + std::string m_testStr; //!< test scenario description + bool m_setupDone{false}; //!< whether association, BA, ... have been done + std::optional m_switchFrom; //!< info for main PHY leaving a link + std::optional m_switchTo; //!< info for main PHY connected to a link + std::array m_bands; //!< bands of the 3 frequency channels + 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_linkIdForTid3{2}; //!< ID of the link on which TID 3 is mapped +}; + #endif /* WIFI_EMLSR_TEST_H */