diff --git a/src/wifi/test/wifi-emlsr-test.cc b/src/wifi/test/wifi-emlsr-test.cc index a86491901..3bf995ba7 100644 --- a/src/wifi/test/wifi-emlsr-test.cc +++ b/src/wifi/test/wifi-emlsr-test.cc @@ -3487,6 +3487,8 @@ EmlsrLinkSwitchTest::EmlsrLinkSwitchTest(const Params& params) m_resetCamState(params.resetCamState), m_auxPhyMaxChWidth(params.auxPhyMaxChWidth), m_countQoSframes(0), + m_countIcfFrames(0), + m_countRtsFrames(0), m_txPsdusPos(0) { m_nEmlsrStations = 1; @@ -3497,7 +3499,7 @@ EmlsrLinkSwitchTest::EmlsrLinkSwitchTest(const Params& params) 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)}; + m_transitionDelay = {MicroSeconds(128)}; } void @@ -3547,12 +3549,6 @@ EmlsrLinkSwitchTest::Transmit(Ptr mac, {0}, linksToBlock); } - else if (category == WifiActionHeader::BLOCK_ACK && - action.blockAck == WifiActionHeader::BLOCK_ACK_ADDBA_RESPONSE) - { - // store the current number of transmitted frame - m_txPsdusPos = m_txPsdus.size() - 1; - } } break; @@ -3564,6 +3560,27 @@ EmlsrLinkSwitchTest::Transmit(Ptr mac, CheckQosFrames(psduMap, txVector, linkId); break; + case WIFI_MAC_CTL_RTS: + // corrupt the first RTS frame (sent by the EMLSR client) + if (++m_countRtsFrames == 1) + { + m_errorModel->SetList({psdu->GetPacket()->GetUid()}); + } + // block transmissions on all other links at non-AP MLD side + { + std::set links{0, 1, 2}; + links.erase(linkId); + m_staMacs[0]->GetMacQueueScheduler()->BlockQueues( + WifiQueueBlockedReason::TID_NOT_MAPPED, + AC_BE, + {WIFI_QOSDATA_QUEUE}, + m_apMac->GetAddress(), + m_staMacs[0]->GetAddress(), + {0}, + links); + } + break; + default:; } } @@ -3575,9 +3592,16 @@ EmlsrLinkSwitchTest::DoSetup() Config::SetDefault("ns3::EmlsrManager::ResetCamState", BooleanValue(m_resetCamState)); Config::SetDefault("ns3::EmlsrManager::AuxPhyChannelWidth", UintegerValue(m_auxPhyMaxChWidth)); Config::SetDefault("ns3::WifiPhy::ChannelSwitchDelay", TimeValue(MicroSeconds(75))); + Config::SetDefault("ns3::EhtConfiguration::MediumSyncDuration", TimeValue(Time{0})); EmlsrOperationsTestBase::DoSetup(); + m_errorModel = CreateObject(); + for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId) + { + m_apMac->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_errorModel); + } + // use channels of different widths for (auto mac : std::initializer_list>{m_apMac, m_staMacs[0]}) { @@ -3665,18 +3689,59 @@ EmlsrLinkSwitchTest::CheckQosFrames(const WifiConstPsduMap& psduMap, // hence triggering yet another link switching on the EMLSR client m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, 0, 2, 1000)); break; + case 4: + // block transmissions on all links at non-AP MLD side + m_staMacs[0]->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED, + AC_BE, + {WIFI_QOSDATA_QUEUE}, + m_apMac->GetAddress(), + m_staMacs[0]->GetAddress(), + {0}, + {0, 1, 2}); + // unblock transmissions on the link used for ML setup (non-AP MLD side) + m_staMacs[0]->GetMacQueueScheduler()->UnblockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED, + AC_BE, + {WIFI_QOSDATA_QUEUE}, + m_apMac->GetAddress(), + m_staMacs[0]->GetAddress(), + {0}, + {m_mainPhyId}); + // trigger establishment of BA agreement with AP as recipient + m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 4, 1000)); + break; + case 5: + // unblock transmissions on all links at non-AP MLD side + m_staMacs[0]->GetMacQueueScheduler()->UnblockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED, + AC_BE, + {WIFI_QOSDATA_QUEUE}, + m_apMac->GetAddress(), + m_staMacs[0]->GetAddress(), + {0}, + {0, 1, 2}); + // block transmissions on the link used for ML setup (non-AP MLD side) + m_staMacs[0]->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED, + AC_BE, + {WIFI_QOSDATA_QUEUE}, + m_apMac->GetAddress(), + m_staMacs[0]->GetAddress(), + {0}, + {m_mainPhyId}); + // generate a new data packet, which will be sent on a link other than the one + // used for ML setup, hence triggering a link switching on the EMLSR client + m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000)); + break; } } /** - * AUX PHY switching enabled + * AUX PHY switching enabled (X = channel switch delay) * * |--------- aux PHY A ---------|------ main PHY ------|-------------- aux PHY B ------------- * ┌───┐ ┌───┐ * │ICF│ │QoS│ * ──────────────────────────┴───┴┬───┬┴───┴┬──┬──────────────────────────────────────────────── - * [link 0] │CTS│ │BA│ - * └───┘ └──┘ + * [link 0] │CTS│ │BA│ + * └───┘ └──┘ * * * |--------- main PHY ----------|------------------ aux PHY A ----------------|--- main PHY --- @@ -3694,8 +3759,28 @@ EmlsrLinkSwitchTest::CheckQosFrames(const WifiConstPsduMap& psduMap, * [link 2] │CTS│ │BA│ * └───┘ └──┘ * + * ... continued ... * - * AUX PHY switching disabled (X = main PHY channel switch delay) + * |----------------------------------------- aux PHY B --------------------------------------- + * ───────────────────────────────────────────────────────────────────────────────────────────── + * [link 0] + * + * |--------- main PHY ----------|X|X|------------------------ aux PHY A ---------------------- + * ┌───┐ + * │ACK│ + * ──────────┬───┬┴───┴──────────────────────────────────────────────────────────────────────── + * [link 1] │QoS│ + * └───┘ + * + * |-------- aux PHY A ----------|X|---------------------- main PHY --------------------------- + * ┌──┐ + * │BA│ + * ────────────────────────┬───X──────┬───┬┴──┴──────────────────────────────────────────────── + * [link 2] │RTS│ │QoS│ + * └───┘ └───┘ + ************************************************************************************************ + * + * AUX PHY switching disabled (X = channel switch delay) * * |------------------------------------------ aux PHY A --------------------------------------- * |-- main PHY --|X| @@ -3721,6 +3806,28 @@ EmlsrLinkSwitchTest::CheckQosFrames(const WifiConstPsduMap& psduMap, * ─────────────────────────────────────────────────┴───┴┬───┬┴───┴┬──┬───────────────────────── * [link 2] │CTS│ │BA│ * └───┘ └──┘ + * + * ... continued ... + * + * |----------------------------------------- aux PHY A --------------------------------------- + * ───────────────────────────────────────────────────────────────────────────────────────────── + * [link 0] + * + * |-------- main PHY --------| |--- main PHY ---| + * ┌───┐ + * │ACK│ + * ──────────┬───┬┴───┴──────────────────────────────────────────────────────────────────────── + * [link 1] │QoS│ + * └───┘ + * + * |------------------------------------------ aux PHY B -------------------------------------- + * |X||X| |X|-------------- main PHY -------------- + * ┌───┐ ┌──┐ + * │CTS│ │BA│ + * ────────────────────────┬───X───────────────┬───┬┴───┴┬───┬┴──┴───────────────────────────── + * [link 2] │RTS│ │RTS│ │QoS│ + * └───┘ └───┘ └───┘ + * */ void @@ -3728,14 +3835,16 @@ EmlsrLinkSwitchTest::CheckInitialControlFrame(const WifiConstPsduMap& psduMap, const WifiTxVector& txVector, uint8_t linkId) { - if (m_txPsdusPos == 0) + if (++m_countIcfFrames == 1) { - // the iterator has not been set yet, thus we are not done with the establishment of - // the BA agreement - return; + m_txPsdusPos = m_txPsdus.size() - 1; } - NS_TEST_EXPECT_MSG_LT(m_countQoSframes, 4, "Unexpected number of ICFs"); + // the first ICF is sent to protect ADDBA_REQ for DL BA agreement, then one ICF is sent before + // each of the 4 DL QoS Data frames; finally, another ICF is sent before the ADDBA_RESP for UL + // BA agreement. Hence, at any time the number of ICF sent is always greater than or equal to + // the number of QoS data frames sent. + NS_TEST_EXPECT_MSG_GT_OR_EQ(m_countIcfFrames, m_countQoSframes, "Unexpected number of ICFs"); auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId); auto phyRecvIcf = m_staMacs[0]->GetWifiPhy(linkId); // PHY receiving the ICF @@ -3757,10 +3866,12 @@ EmlsrLinkSwitchTest::CheckInitialControlFrame(const WifiConstPsduMap& psduMap, << 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 + // the first two ICFs (before ADDBA_REQ and before first DL QoS Data) and the ICF before the + // ADDBA_RESP are received by the main PHY. If aux PHYs do not switch links, the ICF before + // the last DL QoS Data is also received by the main PHY NS_TEST_EXPECT_MSG_EQ((phyRecvIcf == mainPhy), - (m_countQoSframes == 0 || (!m_switchAuxPhy && m_countQoSframes == 3)), + (m_countIcfFrames == 1 || m_countIcfFrames == 2 || + (!m_switchAuxPhy && m_countIcfFrames == 5) || m_countIcfFrames == 6), "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 @@ -3815,36 +3926,86 @@ EmlsrLinkSwitchTest::CheckResults() { NS_TEST_ASSERT_MSG_NE(m_txPsdusPos, 0, "BA agreement establishment not completed"); - const std::size_t nRxOk = 4; // successfully received ICFs + // Expected frame exchanges after ML setup and EML OMN exchange: + // 1. (DL) ICF + CTS + ADDBA_REQ + ACK + // 2. (UL) ADDBA_RESP + ACK + // 3. (DL) ICF + CTS + DATA + BA + // 4. (DL) ICF + CTS + DATA + BA + // 5. (DL) ICF + CTS + DATA + BA + // 6. (DL) ICF + CTS + DATA + BA + // 7. (UL) ADDBA_REQ + ACK + // 8. (DL) ICF + CTS + ADDBA_RESP + ACK + // 9. (UL) DATA + BA + // 10. (UL) RTS - CTS timeout + // 11. (UL) (RTS + CTS if SwitchAuxPhy is false + ) DATA + BA + + NS_TEST_EXPECT_MSG_EQ(m_countIcfFrames, 6, "Unexpected number of ICFs sent"); + + // frame exchanges without RTS because the EMLSR client sent the initial frame through main PHY + const std::size_t nFrameExchNoRts = m_switchAuxPhy ? 4 : 3; + + const std::size_t nFrameExchWithRts = m_switchAuxPhy ? 0 : 1; NS_TEST_ASSERT_MSG_GT_OR_EQ(m_txPsdus.size(), - m_txPsdusPos + 2 + nRxOk * 4, + m_txPsdusPos + + m_countIcfFrames * 4 + /* frames in frame exchange with ICF */ + nFrameExchNoRts * 2 + /* frames in frame exchange without RTS */ + nFrameExchWithRts * 4 + /* frames in frame exchange with RTS */ + 1, /* corrupted RTS */ "Insufficient number of TX PSDUs"); - // m_txPsdusPos points to ADDBA_RESPONSE, then ACK and then ICF - auto psduIt = std::next(m_txPsdus.cbegin(), m_txPsdusPos + 2); + // m_txPsdusPos points to the first ICF + auto psduIt = std::next(m_txPsdus.cbegin(), m_txPsdusPos); - for (std::size_t i = 0; i < nRxOk; i++) + const std::size_t nFrameExchanges = + m_countIcfFrames + nFrameExchNoRts + nFrameExchWithRts + 1 /* corrupted RTS */; + + for (std::size_t i = 1; i <= nFrameExchanges; ++i) { - NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 && - psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsTrigger()), - true, - "Expected a Trigger Frame (ICF)"); - psduIt++; - NS_TEST_EXPECT_MSG_EQ( - (psduIt->psduMap.size() == 1 && psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsCts()), - true, - "Expected a CTS"); - psduIt++; - NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 && - psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsQosData()), - true, - "Expected a QoS Data frame"); - psduIt++; - NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 && - psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsBlockAck()), - true, - "Expected a BlockAck"); + if (i == 1 || (i >= 3 && i <= 6) || i == 8 || i == 10 || (i == 11 && !m_switchAuxPhy)) + { + // frame exchanges with protection + NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 && + (i < 9 ? psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsTrigger() + : psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsRts())), + true, + "Expected a Trigger Frame (ICF)"); + psduIt++; + if (i == 10) + { + continue; // corrupted RTS + } + NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 && + psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsCts()), + true, + "Expected a CTS"); + psduIt++; + } + + if (i == 1 || i == 2 || i == 7 || i == 8) // frame exchanges with ADDBA REQ/RESP frames + { + NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 && + psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsMgt()), + true, + "Expected a management frame"); + psduIt++; + NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 && + psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsAck()), + true, + "Expected a Normal Ack"); + } + else + { + NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 && + psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsQosData()), + true, + "Expected a QoS Data frame"); + psduIt++; + NS_TEST_EXPECT_MSG_EQ((psduIt->psduMap.size() == 1 && + psduIt->psduMap.at(SU_STA_ID)->GetHeader(0).IsBlockAck()), + true, + "Expected a BlockAck"); + } psduIt++; } } diff --git a/src/wifi/test/wifi-emlsr-test.h b/src/wifi/test/wifi-emlsr-test.h index 5b4a2339b..ec608fff5 100644 --- a/src/wifi/test/wifi-emlsr-test.h +++ b/src/wifi/test/wifi-emlsr-test.h @@ -669,9 +669,10 @@ class EmlsrUlTxopTest : public EmlsrOperationsTestBase * - the first one on the link used for ML setup, hence no PHY switch occurs * - the second one on another link, thus causing the main PHY to switch link * - the third one on the remaining link, thus causing the main PHY to switch link again - * - the fourth one on the link used for ML setup; if the aux PHYs switches link, there is - * one aux PHY listening on such a link and the main PHY switches to this link, otherwise - * no PHY is listening on such a link and there is no response to the ICF sent by the AP MLD + * - the fourth one on the link used for ML setup + * + * Afterwards, the EMLSR client transmits 2 QoS data frames; the first one on the link used for + * ML setup (hence, no RTS is sent), the second one on another link. */ class EmlsrLinkSwitchTest : public EmlsrOperationsTestBase { @@ -741,9 +742,12 @@ class EmlsrLinkSwitchTest : public EmlsrOperationsTestBase the Main PHY was operating before moving to the link of Aux PHY */ bool m_resetCamState; /**< whether to reset the state of the ChannelAccessManager associated with the link on which the main PHY has just switched to */ - MHz_u m_auxPhyMaxChWidth; //!< max channel width supported by aux PHYs - std::size_t m_countQoSframes; //!< counter for QoS data frames - std::size_t m_txPsdusPos; //!< a position in the vector of TX PSDUs + MHz_u m_auxPhyMaxChWidth; //!< max channel width supported by aux PHYs + std::size_t m_countQoSframes; //!< counter for QoS data frames + std::size_t m_countIcfFrames; //!< counter for ICF frames + std::size_t m_countRtsFrames; //!< counter for RTS frames + std::size_t m_txPsdusPos; //!< position in the vector of TX PSDUs of the first ICF + Ptr m_errorModel; ///< error rate model to corrupt packets at AP MLD }; /**