From 4fc5162f97266de1d072470b525df91458fd2cd2 Mon Sep 17 00:00:00 2001 From: Stefano Avallone Date: Wed, 14 Jun 2023 17:58:50 +0200 Subject: [PATCH] wifi: Default EMLSR Manager switches main PHY back to its previous link ...when a TXOP ends, if the aux PHYs do not switch link --- src/wifi/model/eht/default-emlsr-manager.cc | 20 +++- src/wifi/model/eht/default-emlsr-manager.h | 6 ++ src/wifi/model/eht/emlsr-manager.h | 16 +-- src/wifi/test/wifi-emlsr-test.cc | 105 +++++++++++--------- 4 files changed, 89 insertions(+), 58 deletions(-) diff --git a/src/wifi/model/eht/default-emlsr-manager.cc b/src/wifi/model/eht/default-emlsr-manager.cc index 9602336b8..1030ed4d6 100644 --- a/src/wifi/model/eht/default-emlsr-manager.cc +++ b/src/wifi/model/eht/default-emlsr-manager.cc @@ -42,7 +42,10 @@ DefaultEmlsrManager::GetTypeId() .AddConstructor() .AddAttribute("SwitchAuxPhy", "Whether Aux PHY should switch channel to operate on the link on which " - "the Main PHY was operating before moving to the link of the Aux PHY.", + "the Main PHY was operating before moving to the link of the Aux PHY. " + "Note that, if the Aux PHY does not switch channel, the main PHY will " + "switch back to its previous link once the TXOP terminates (otherwise, " + "no PHY will be listening on that EMLSR link).", BooleanValue(true), MakeBooleanAccessor(&DefaultEmlsrManager::m_switchAuxPhy), MakeBooleanChecker()); @@ -96,7 +99,10 @@ DefaultEmlsrManager::NotifyMainPhySwitch(uint8_t currLinkId, uint8_t nextLinkId) if (!m_switchAuxPhy) { - return; // nothing to do + // record that the main PHY will have to switch back to its current link + m_linkIdForMainPhyAfterTxop = currLinkId; + m_auxPhyToReconnect = GetStaMac()->GetWifiPhy(nextLinkId); + return; } // switch channel on Aux PHY so that it operates on the link on which the main PHY was operating @@ -119,6 +125,16 @@ void DefaultEmlsrManager::DoNotifyTxopEnd(uint8_t linkId) { NS_LOG_FUNCTION(this << linkId); + + // switch main PHY to the previous link, if needed + if (m_linkIdForMainPhyAfterTxop && linkId != m_linkIdForMainPhyAfterTxop) + { + auto phy = m_auxPhyToReconnect; + SwitchMainPhy(*m_linkIdForMainPhyAfterTxop, false); + // Reconnect the aux PHY to its original link + Simulator::ScheduleNow(&StaWifiMac::NotifySwitchingEmlsrLink, GetStaMac(), phy, linkId); + } + m_linkIdForMainPhyAfterTxop.reset(); } } // namespace ns3 diff --git a/src/wifi/model/eht/default-emlsr-manager.h b/src/wifi/model/eht/default-emlsr-manager.h index f5ea8ca75..0d989f1b4 100644 --- a/src/wifi/model/eht/default-emlsr-manager.h +++ b/src/wifi/model/eht/default-emlsr-manager.h @@ -58,6 +58,12 @@ class DefaultEmlsrManager : public EmlsrManager bool m_switchAuxPhy; /**< whether Aux PHY should switch channel to operate on the link on which the Main PHY was operating before moving to the link of the Aux PHY */ + std::optional + m_linkIdForMainPhyAfterTxop; //!< ID of the link the main PHY has to switch to once + //!< the current TXOP terminates + Ptr m_auxPhyToReconnect; //!< Aux PHY the ChannelAccessManager of the link on which + //!< the main PHY is operating has to connect a listener to + //!< when the main PHY is back operating on its previous link }; } // namespace ns3 diff --git a/src/wifi/model/eht/emlsr-manager.h b/src/wifi/model/eht/emlsr-manager.h index e027f90d3..f9d88ec4b 100644 --- a/src/wifi/model/eht/emlsr-manager.h +++ b/src/wifi/model/eht/emlsr-manager.h @@ -177,6 +177,14 @@ class EmlsrManager : public Object */ const WifiPhyOperatingChannel& GetChannelForAuxPhy(uint8_t linkId) const; + /** + * Switch channel on the Main PHY so that it operates on the given link. + * + * \param linkId the ID of the link on which the main PHY has to operate + * \param noSwitchDelay whether switching delay should be zero + */ + void SwitchMainPhy(uint8_t linkId, bool noSwitchDelay); + /** * Switch channel on the Aux PHY operating on the given current link so that it operates * on the given next link. @@ -269,14 +277,6 @@ class EmlsrManager : public Object */ void ChangeEmlsrMode(); - /** - * Switch channel on the Main PHY so that it operates on the given link. - * - * \param linkId the ID of the link on which the main PHY has to operate - * \param noSwitchDelay whether switching delay should be zero - */ - void SwitchMainPhy(uint8_t linkId, bool noSwitchDelay); - /** * Adjust the operating channel of all the aux PHYs to meet the constraint on the maximum * channel width supported by aux PHYs. diff --git a/src/wifi/test/wifi-emlsr-test.cc b/src/wifi/test/wifi-emlsr-test.cc index a30ef1e36..df9f07f6b 100644 --- a/src/wifi/test/wifi-emlsr-test.cc +++ b/src/wifi/test/wifi-emlsr-test.cc @@ -2217,6 +2217,9 @@ EmlsrLinkSwitchTest::EmlsrLinkSwitchTest(const Params& params) m_mainPhyId = 1; m_establishBaDl = true; m_duration = Seconds(1.0); + // when aux PHYs do not switch link, the main PHY switches back to its previous link after + // a TXOP, hence the transition delay must exceed the channel switch delay (default: 250us) + m_transitionDelay = {MicroSeconds(256)}; } void @@ -2413,25 +2416,27 @@ EmlsrLinkSwitchTest::CheckQosFrames(const WifiConstPsduMap& psduMap, * └───┘ └──┘ * * - * AUX PHY switching disabled + * AUX PHY switching disabled (X = main PHY channel switch delay) * - * |--------- aux PHY A ---------|------ main PHY ------| + * |------------------------------------------ aux PHY A --------------------------------------- + * |-- main PHY --|X| * ┌───┐ ┌───┐ * │ICF│ │QoS│ * ──────────────────────────┴───┴┬───┬┴───┴┬──┬──────────────────────────────────────────────── * [link 0] │CTS│ │BA│ * └───┘ └──┘ * - * - * |--------- main PHY ----------| - * ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ - * │ICF│ │QoS│ │ICF│ │ICF│ │ICF│ - * ───┴───┴┬───┬┴───┴┬──┬──────────────────────────────────────────────────┴───┴──┴───┴──┴───┴── - * [link 1]│CTS│ │BA│ - * └───┘ └──┘ + * |-main| + * |--------- main PHY ----------| |-PHY-| |------ main PHY ------ + * ┌───┐ ┌───┐ ┌───┐ ┌───┐ + * │ICF│ │QoS│ │ICF│ │QoS│ + * ───┴───┴┬───┬┴───┴┬──┬──────────────────────────────────────────────────┴───┴┬───┬┴───┴┬──┬── + * [link 1]│CTS│ │BA│ │CTS│ │BA│ + * └───┘ └──┘ └───┘ └──┘ * * - * |--------------------- aux PHY B --------------------|--------------- main PHY -------------- + * |------------------------------------------ aux PHY B --------------------------------------- + * |-- main PHY --|X| * ┌───┐ ┌───┐ * │ICF│ │QoS│ * ─────────────────────────────────────────────────┴───┴┬───┬┴───┴┬──┬───────────────────────── @@ -2451,6 +2456,8 @@ EmlsrLinkSwitchTest::CheckInitialControlFrame(const WifiConstPsduMap& psduMap, return; } + NS_TEST_EXPECT_MSG_LT(m_countQoSframes, 4, "Unexpected number of ICFs"); + auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId); auto phyRecvIcf = m_staMacs[0]->GetWifiPhy(linkId); // PHY receiving the ICF @@ -2458,66 +2465,68 @@ EmlsrLinkSwitchTest::CheckInitialControlFrame(const WifiConstPsduMap& psduMap, NS_TEST_ASSERT_MSG_EQ(currMainPhyLinkId.has_value(), true, "Didn't find the link on which the Main PHY is operating"); + NS_TEST_ASSERT_MSG_NE(phyRecvIcf, + nullptr, + "No PHY on the link where ICF " << m_countQoSframes << " was sent"); - if (phyRecvIcf && phyRecvIcf != mainPhy) + if (phyRecvIcf != mainPhy) { NS_TEST_EXPECT_MSG_LT_OR_EQ( phyRecvIcf->GetChannelWidth(), m_auxPhyMaxChWidth, - "Aux PHY that received ICF is operating on a channel whose width exceeds the limit " - << m_countQoSframes); + "Aux PHY that received ICF " + << m_countQoSframes << " is operating on a channel whose width exceeds the limit"); } + // if aux PHYs switch links, only the first ICF is received by the main PHY; otherwise, + // the first ICF and the last ICF are received by the main PHY + NS_TEST_EXPECT_MSG_EQ((phyRecvIcf == mainPhy), + (m_countQoSframes == 0 || (!m_switchAuxPhy && m_countQoSframes == 3)), + "Expecting that the ICF was received by the main PHY"); + + // if aux PHYs do not switch links, the main PHY is operating on its original link when + // the transmission of an ICF starts + NS_TEST_EXPECT_MSG_EQ(m_switchAuxPhy || currMainPhyLinkId == m_mainPhyId, + true, + "Main PHY is operating on an unexpected link (" + << +currMainPhyLinkId.value() << ", expected " << +m_mainPhyId + << ")"); + auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand()); // check that PHYs are operating on the expected link after the reception of the ICF Simulator::Schedule(txDuration + NanoSeconds(1), [=]() { - std::size_t nRxOk = m_switchAuxPhy ? 4 : 3; // successfully received ICFs - - if (m_countQoSframes < nRxOk) - { - // the main PHY must be operating on the link where ICF was sent - NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(linkId), - mainPhy, - "PHY operating on link where ICF was sent is not the main PHY"); - } - - // the first ICF is received by the main PHY and no channel switch occurs - if (m_countQoSframes == 0) - { - NS_TEST_EXPECT_MSG_EQ(phyRecvIcf, - mainPhy, - "PHY that received the ICF is not the main PHY"); - return; - } + // the main PHY must be operating on the link where ICF was sent + NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(linkId), + mainPhy, + "PHY operating on link where ICF was sent is not the main PHY"); // the behavior of Aux PHYs depends on whether they switch channel or not if (m_switchAuxPhy) { - NS_TEST_EXPECT_MSG_LT(m_countQoSframes, nRxOk, "Unexpected number of ICFs"); + if (mainPhy != phyRecvIcf) + { + NS_TEST_EXPECT_MSG_EQ(phyRecvIcf->IsStateSwitching(), + true, + "Aux PHY expected to switch channel"); + } Simulator::Schedule(phyRecvIcf->GetChannelSwitchDelay(), [=]() { NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(*currMainPhyLinkId), phyRecvIcf, - "PHY operating on link where Main PHY was before switching " - "channel is not the aux PHY that received the ICF"); + "The Aux PHY that received the ICF is expected to operate " + "on the link where Main PHY was before switching channel"); }); } - else if (m_countQoSframes < nRxOk) - { - // the first 3 ICFs are actually received by some PHY - NS_TEST_EXPECT_MSG_NE(phyRecvIcf, nullptr, "Expected some PHY to receive the ICF"); - NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(*currMainPhyLinkId), - nullptr, - "No PHY expected to operate on link where Main PHY was " - "before switching channel"); - } else { - // no PHY received the ICF - NS_TEST_EXPECT_MSG_EQ(phyRecvIcf, - nullptr, - "Expected no PHY to operate on link where ICF is sent"); + NS_TEST_EXPECT_MSG_EQ(phyRecvIcf->IsStateSwitching(), + false, + "Aux PHY is not expected to switch channel"); + NS_TEST_EXPECT_MSG_EQ(phyRecvIcf->GetPhyBand(), + mainPhy->GetPhyBand(), + "The Aux PHY that received the ICF is expected to operate " + "on the same band as the Main PHY"); } }); } @@ -2527,7 +2536,7 @@ EmlsrLinkSwitchTest::CheckResults() { NS_TEST_ASSERT_MSG_NE(m_txPsdusPos, 0, "BA agreement establishment not completed"); - std::size_t nRxOk = m_switchAuxPhy ? 4 : 3; // successfully received ICFs + const std::size_t nRxOk = 4; // successfully received ICFs NS_TEST_ASSERT_MSG_GT_OR_EQ(m_txPsdus.size(), m_txPsdusPos + 3 + nRxOk * 4,