wifi: Split EMLSR test in multiple test suites
This commit is contained in:
@@ -380,7 +380,10 @@ build_lib(
|
||||
test/wifi-co-trace-helper-test.cc
|
||||
test/wifi-dynamic-bw-op-test.cc
|
||||
test/wifi-eht-info-elems-test.cc
|
||||
test/wifi-emlsr-test.cc
|
||||
test/wifi-emlsr-basic-exchanges-test.cc
|
||||
test/wifi-emlsr-enabling-test.cc
|
||||
test/wifi-emlsr-link-switch-test.cc
|
||||
test/wifi-emlsr-test-base.cc
|
||||
test/wifi-error-rate-models-test.cc
|
||||
test/wifi-fils-frame-test.cc
|
||||
test/wifi-gcr-test.cc
|
||||
|
||||
3094
src/wifi/test/wifi-emlsr-basic-exchanges-test.cc
Normal file
3094
src/wifi/test/wifi-emlsr-basic-exchanges-test.cc
Normal file
File diff suppressed because it is too large
Load Diff
440
src/wifi/test/wifi-emlsr-basic-exchanges-test.h
Normal file
440
src/wifi/test/wifi-emlsr-basic-exchanges-test.h
Normal file
@@ -0,0 +1,440 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Universita' degli Studi di Napoli Federico II
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*
|
||||
* Author: Stefano Avallone <stavallo@unina.it>
|
||||
*/
|
||||
|
||||
#ifndef WIFI_EMLSR_BASIC_EXCHANGES_TEST_H
|
||||
#define WIFI_EMLSR_BASIC_EXCHANGES_TEST_H
|
||||
|
||||
#include "wifi-emlsr-test-base.h"
|
||||
|
||||
/**
|
||||
* @ingroup wifi-test
|
||||
* @ingroup tests
|
||||
*
|
||||
* @brief Test the transmission of DL frames to EMLSR clients.
|
||||
*
|
||||
* This test considers an AP MLD and a configurable number of non-AP MLDs that support EMLSR
|
||||
* and a configurable number of non-AP MLDs that do not support EMLSR. All MLDs have three
|
||||
* setup links, while the set of EMLSR links for the EMLSR clients is configurable.
|
||||
* Block ack agreements (for TID 0, with the AP MLD as originator) are established with all
|
||||
* the non-AP MLDs before that EMLSR clients send the EML Operating Mode Notification frame
|
||||
* to enable the EMLSR mode on their EMLSR links.
|
||||
*
|
||||
* Before enabling EMLSR mode, it is checked that:
|
||||
*
|
||||
* - all EMLSR links (but the link used for ML setup) of the EMLSR clients are considered to
|
||||
* be in power save mode and are blocked by the AP MLD; all the other links have transitioned
|
||||
* to active mode and are not blocked
|
||||
* - no MU-RTS Trigger Frame is sent as Initial control frame
|
||||
* - In case of EMLSR clients having no link that is not an EMLSR link and is different than
|
||||
* the link used for ML setup, the two A-MPDUs used to trigger BA establishment are
|
||||
* transmitted one after another on the link used for ML setup. Otherwise, the two A-MPDUs
|
||||
* are sent concurrently on two distinct links
|
||||
*
|
||||
* After enabling EMLSR mode, it is checked that:
|
||||
*
|
||||
* - all EMLSR links of the EMLSR clients are considered to be in active mode and are not
|
||||
* blocked by the AP MLD
|
||||
* - If all setup links are EMLSR links, the first two frame exchanges are both protected by
|
||||
* MU-RTS TF and occur one after another. Otherwise, one frame exchange occurs on the
|
||||
* non-EMLSR link and is not protected by MU-RTS TF; the other frame exchange occurs on an
|
||||
* EMLSR link and is protected by MU-RTS TF
|
||||
* - the AP MLD blocks transmission on all other EMLSR links when sending an ICF to an EMLSR client
|
||||
* - After completing a frame exchange with an EMLSR client, the AP MLD can start another frame
|
||||
* exchange with that EMLSR client within the same TXOP (after a SIFS) without sending an ICF
|
||||
* - During the transition delay, all EMLSR links are not used for DL transmissions
|
||||
* - The padding added to Initial Control frames is the largest among all the EMLSR clients
|
||||
* solicited by the ICF
|
||||
*
|
||||
* After disabling EMLSR mode, it is checked that:
|
||||
*
|
||||
* - all EMLSR links (but the link used to exchange EML Notification frames) of the EMLSR clients
|
||||
* are considered to be in power save mode and are blocked by the AP MLD
|
||||
* - an MU-RTS Trigger Frame is sent by the AP MLD as ICF for sending the EML Notification
|
||||
* response, unless the link used to exchange EML Notification frames is a non-EMLSR link
|
||||
* - no MU-RTS Trigger Frame is used as ICF for QoS data frames
|
||||
* - In case of EMLSR clients having no link that is not an EMLSR link and is different than
|
||||
* the link used to exchange EML Notification frames, the two A-MPDUs are transmitted one
|
||||
* after another on the link used to exchange EML Notification frames. Otherwise, the two
|
||||
* A-MPDUs are sent concurrently on two distinct links
|
||||
*
|
||||
* Also, if the PutAuxPhyToSleep attribute is set to true, it is checked that aux PHYs are in
|
||||
* sleep mode after receiving an ICF and are resumed from sleep after receiving the CF-End frame.
|
||||
*/
|
||||
class EmlsrDlTxopTest : public EmlsrOperationsTestBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Parameters for the EMLSR DL TXOP test
|
||||
*/
|
||||
struct Params
|
||||
{
|
||||
std::size_t nEmlsrStations; //!< number of non-AP MLDs that support EMLSR
|
||||
std::size_t nNonEmlsrStations; //!< number of non-AP MLDs that do not support EMLSR
|
||||
std::set<uint8_t>
|
||||
linksToEnableEmlsrOn; //!< IDs of links on which EMLSR mode should be enabled
|
||||
std::vector<Time> paddingDelay; //!< vector (whose size equals <i>nEmlsrStations</i>) of the
|
||||
//!< padding delay values advertised by non-AP MLDs
|
||||
std::vector<Time>
|
||||
transitionDelay; //!< vector (whose size equals <i>nEmlsrStations</i>) of
|
||||
//!< transition the delay values advertised by non-AP MLDs
|
||||
Time transitionTimeout; //!< the Transition Timeout advertised by the AP MLD
|
||||
bool putAuxPhyToSleep; //!< whether aux PHYs are put to sleep during DL/UL TXOPs
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param params parameters for the EMLSR DL TXOP test
|
||||
*/
|
||||
EmlsrDlTxopTest(const Params& params);
|
||||
~EmlsrDlTxopTest() override = default;
|
||||
|
||||
protected:
|
||||
void DoSetup() override;
|
||||
void DoRun() override;
|
||||
void Transmit(Ptr<WifiMac> mac,
|
||||
uint8_t phyId,
|
||||
WifiConstPsduMap psduMap,
|
||||
WifiTxVector txVector,
|
||||
double txPowerW) override;
|
||||
|
||||
/**
|
||||
* Check that the simulation produced the expected results.
|
||||
*/
|
||||
void CheckResults();
|
||||
|
||||
/**
|
||||
* Check that the AP MLD considers the correct Power Management mode for the links setup
|
||||
* with the given non-AP MLD. This method is intended to be called shortly after ML setup.
|
||||
*
|
||||
* @param address a link address of the given non-AP MLD
|
||||
*/
|
||||
void CheckPmModeAfterAssociation(const Mac48Address& address);
|
||||
|
||||
/**
|
||||
* Check that appropriate actions are taken when the AP MLD transmits an EML Operating Mode
|
||||
* Notification response frame to an EMLSR client on the given link.
|
||||
*
|
||||
* @param mpdu the MPDU carrying the EML Operating Mode Notification frame
|
||||
* @param txVector the TXVECTOR used to send the PPDU
|
||||
* @param linkId the ID of the given link
|
||||
*/
|
||||
void CheckApEmlNotificationFrame(Ptr<const WifiMpdu> mpdu,
|
||||
const WifiTxVector& txVector,
|
||||
uint8_t linkId);
|
||||
|
||||
/**
|
||||
* Check that appropriate actions are taken when an EMLSR client transmits an EML Operating
|
||||
* Mode Notification frame to the AP MLD on the given link.
|
||||
*
|
||||
* @param mpdu the MPDU carrying the EML Operating Mode Notification frame
|
||||
* @param txVector the TXVECTOR used to send the PPDU
|
||||
* @param linkId the ID of the given link
|
||||
*/
|
||||
void CheckStaEmlNotificationFrame(Ptr<const WifiMpdu> mpdu,
|
||||
const WifiTxVector& txVector,
|
||||
uint8_t linkId);
|
||||
|
||||
/**
|
||||
* Check that appropriate actions are taken by the AP MLD transmitting an initial
|
||||
* Control frame to an EMLSR client on the given link.
|
||||
*
|
||||
* @param mpdu the MPDU carrying the MU-RTS TF
|
||||
* @param txVector the TXVECTOR used to send the PPDU
|
||||
* @param linkId the ID of the given link
|
||||
*/
|
||||
void CheckInitialControlFrame(Ptr<const WifiMpdu> mpdu,
|
||||
const WifiTxVector& txVector,
|
||||
uint8_t linkId);
|
||||
|
||||
/**
|
||||
* Check that appropriate actions are taken by the AP MLD transmitting a PPDU containing
|
||||
* QoS data frames to EMLSR clients on the given link.
|
||||
*
|
||||
* @param psduMap the PSDU(s) carrying QoS data frames
|
||||
* @param txVector the TXVECTOR used to send the PPDU
|
||||
* @param linkId the ID of the given link
|
||||
*/
|
||||
void CheckQosFrames(const WifiConstPsduMap& psduMap,
|
||||
const WifiTxVector& txVector,
|
||||
uint8_t linkId);
|
||||
|
||||
/**
|
||||
* Check that appropriate actions are taken by the AP MLD receiving a PPDU containing
|
||||
* BlockAck frames from EMLSR clients on the given link.
|
||||
*
|
||||
* @param psduMap the PSDU carrying BlockAck frames
|
||||
* @param txVector the TXVECTOR used to send the PPDU
|
||||
* @param phyId the ID of the PHY transmitting the PSDU(s)
|
||||
*/
|
||||
void CheckBlockAck(const WifiConstPsduMap& psduMap,
|
||||
const WifiTxVector& txVector,
|
||||
uint8_t phyId);
|
||||
|
||||
private:
|
||||
void StartTraffic() override;
|
||||
|
||||
/**
|
||||
* Enable EMLSR mode on the next EMLSR client
|
||||
*/
|
||||
void EnableEmlsrMode();
|
||||
|
||||
std::set<uint8_t> m_emlsrLinks; /**< IDs of the links on which EMLSR mode has to be enabled */
|
||||
Time m_emlsrEnabledTime; //!< when EMLSR mode has been enabled on all EMLSR clients
|
||||
const Time m_fe2to3delay; /**< time interval between 2nd and 3rd frame exchange sequences
|
||||
after the enablement of EMLSR mode */
|
||||
std::size_t m_countQoSframes; //!< counter for QoS frames (transition delay test)
|
||||
std::size_t m_countBlockAck; //!< counter for BlockAck frames (transition delay test)
|
||||
Ptr<ListErrorModel> m_errorModel; ///< error rate model to corrupt BlockAck at AP MLD
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup wifi-test
|
||||
* @ingroup tests
|
||||
*
|
||||
* @brief Test the transmission of UL frames from EMLSR clients.
|
||||
*
|
||||
* This test considers an AP MLD and a non-AP MLD that support EMLSR. The non-AP MLD setups three
|
||||
* links, while the set of EMLSR links is configurable. Block ack agreements (for TID 0) for both
|
||||
* DL and UL directions are established after that the EML Operating Mode Notification frames are
|
||||
* exchanged to enable the EMLSR mode on the EMLSR links. Aux PHYs on the EMLSR client do not
|
||||
* switch link, hence the main PHY will switch link (if needed) when terminating a TXOP.
|
||||
*
|
||||
* It is checked that:
|
||||
*
|
||||
* - Initially, aux PHYs are configured so that they are unable to transmit frames. Before
|
||||
* generating the packets for the first UL data frame, transmissions on the link where the
|
||||
* main PHY is operating and on the non-EMLSR link (if any) are blocked. Thus, the first UL data
|
||||
* frame is held until transmissions on the link where the main PHY is operating are unblocked.
|
||||
* The first UL data frame is sent by the main PHY without RTS protection. When the data frame
|
||||
* exchange terminates, the MediumSyncDelay timer is started on the other EMLSR links and the
|
||||
* CCA ED threshold is set as expected
|
||||
* - If there is a non-EMLSR link, another data frame can be sent concurrently (without protection)
|
||||
* on the non-EMLSR link
|
||||
* - When the first UL data frame is transmitted, we make the aux PHYs on the EMLSR client capable
|
||||
* of transmitting, we block transmissions on the link where the main PHY is operating and
|
||||
* generate new UL packets, which will then be transmitted on a link where an aux PHY is
|
||||
* operating. Thus, the aux PHY transmits an RTS frame and the main PHY will take over and
|
||||
* transmit the second UL data frame. We check that, while the link on which the main PHY was
|
||||
* operating is blocked because another EMLSR link is being used, new backoff values for that
|
||||
* link are generated if and only if the QosTxop::GenerateBackoffIfTxopWithoutTx attribute is
|
||||
* true; otherwise, a new backoff value is generated when the link is unblocked.
|
||||
* - When the exchange of the second UL data frame terminates, we make the aux PHY unable to
|
||||
* transmit, block transmissions on the non-EMLSR link (if any) and generate some more UL
|
||||
* packets, which will then be transmitted by the main PHY. However, a MediumSyncDelay timer
|
||||
* is now running on the link where the main PHY is operating, hence transmissions are protected
|
||||
* by an RTS frame. We install a post reception error model on the AP MLD so that all RTS frames
|
||||
* sent by the EMLSR client are not received by the AP MLD. We check that the EMLSR client makes
|
||||
* at most the configured max number of transmission attempts and that another UL data frame is
|
||||
* sent once the MediumSyncDelay timer is expired. We also check that the TX width of the RTS
|
||||
* frames and the UL data frame equal the channel width used by the main PHY.
|
||||
* - We check that no issue arises in case an aux PHY sends an RTS frame but the CTS response is
|
||||
* not transmitted successfully. Specifically, we check that the main PHY is completing the
|
||||
* channel switch when the (unsuccessful) reception of the CTS ends and that a new RTS/CTS
|
||||
* exchange is carried out to protect the transmission of the last data frame.
|
||||
* - While the main PHY is operating on the same link as an aux PHY (which does not switch
|
||||
* channel), the aux PHY is put in sleep mode as soon as the main PHY starts operating on the
|
||||
* link, stays in sleep mode until the TXOP ends and is resumed from sleep mode right after the
|
||||
* end of the DL/UL TXOP.
|
||||
*
|
||||
* Also, if the PutAuxPhyToSleep attribute is set to true, it is checked that aux PHYs are in
|
||||
* sleep mode a SIFS after receiving the ICF and are still in sleep mode right before receiving
|
||||
* a Block Ack frame, and they are resumed from sleep after receiving the Block Ack frame.
|
||||
*/
|
||||
class EmlsrUlTxopTest : public EmlsrOperationsTestBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Parameters for the EMLSR UL TXOP test
|
||||
*/
|
||||
struct Params
|
||||
{
|
||||
std::set<uint8_t>
|
||||
linksToEnableEmlsrOn; //!< IDs of links on which EMLSR mode should be enabled
|
||||
MHz_u channelWidth; //!< width of the channels used by MLDs
|
||||
MHz_u auxPhyChannelWidth; //!< max width supported by aux PHYs
|
||||
Time mediumSyncDuration; //!< duration of the MediumSyncDelay timer
|
||||
uint8_t msdMaxNTxops; //!< Max number of TXOPs that an EMLSR client is allowed
|
||||
//!< to attempt to initiate while the MediumSyncDelay
|
||||
//!< timer is running (zero indicates no limit)
|
||||
bool genBackoffIfTxopWithoutTx; //!< whether the backoff should be invoked when the AC gains
|
||||
//!< a TXOP but it does not transmit any frame
|
||||
bool putAuxPhyToSleep; //!< whether aux PHYs are put to sleep during DL/UL TXOPs
|
||||
bool switchMainPhyBackDelayTimeout; //!< whether a SwitchMainPhyBackDelay timer expires
|
||||
//!< after that the main PHY moved to an aux PHY link
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param params parameters for the EMLSR UL TXOP test
|
||||
*/
|
||||
EmlsrUlTxopTest(const Params& params);
|
||||
~EmlsrUlTxopTest() override = default;
|
||||
|
||||
protected:
|
||||
void DoSetup() override;
|
||||
void DoRun() override;
|
||||
void Transmit(Ptr<WifiMac> mac,
|
||||
uint8_t phyId,
|
||||
WifiConstPsduMap psduMap,
|
||||
WifiTxVector txVector,
|
||||
double txPowerW) override;
|
||||
|
||||
/**
|
||||
* Check that the simulation produced the expected results.
|
||||
*/
|
||||
void CheckResults();
|
||||
|
||||
/**
|
||||
* Check that appropriate actions are taken by the EMLSR client when transmitting an RTS
|
||||
* frame on the given link.
|
||||
*
|
||||
* @param mpdu the MPDU carrying the RTS frame
|
||||
* @param txVector the TXVECTOR used to send the PPDU
|
||||
* @param linkId the ID of the given link
|
||||
*/
|
||||
void CheckRtsFrames(Ptr<const WifiMpdu> mpdu, const WifiTxVector& txVector, uint8_t linkId);
|
||||
|
||||
/**
|
||||
* Check that appropriate actions are taken by the EMLSR client when receiving a CTS
|
||||
* frame on the given link.
|
||||
*
|
||||
* @param mpdu the MPDU carrying the CTS frame
|
||||
* @param txVector the TXVECTOR used to send the PPDU
|
||||
* @param linkId the ID of the given link
|
||||
*/
|
||||
void CheckCtsFrames(Ptr<const WifiMpdu> mpdu, const WifiTxVector& txVector, uint8_t linkId);
|
||||
|
||||
/**
|
||||
* Check that appropriate actions are taken when an MLD transmits a PPDU containing
|
||||
* QoS data frames on the given link.
|
||||
*
|
||||
* @param psduMap the PSDU(s) carrying QoS data frames
|
||||
* @param txVector the TXVECTOR used to send the PPDU
|
||||
* @param linkId the ID of the given link
|
||||
*/
|
||||
void CheckQosFrames(const WifiConstPsduMap& psduMap,
|
||||
const WifiTxVector& txVector,
|
||||
uint8_t linkId);
|
||||
|
||||
/**
|
||||
* Check that appropriate actions are taken when an MLD transmits a PPDU containing
|
||||
* BlockAck frames on the given link.
|
||||
*
|
||||
* @param psduMap the PSDU carrying BlockAck frames
|
||||
* @param txVector the TXVECTOR used to send the PPDU
|
||||
* @param linkId the ID of the given link
|
||||
*/
|
||||
void CheckBlockAck(const WifiConstPsduMap& psduMap,
|
||||
const WifiTxVector& txVector,
|
||||
uint8_t linkId);
|
||||
|
||||
private:
|
||||
void StartTraffic() override;
|
||||
|
||||
/**
|
||||
* Callback invoked when a new backoff value is generated by the EMLSR client.
|
||||
*
|
||||
* @param backoff the generated backoff value
|
||||
* @param linkId the ID of the link for which the backoff value has been generated
|
||||
*/
|
||||
void BackoffGenerated(uint32_t backoff, uint8_t linkId);
|
||||
|
||||
std::set<uint8_t> m_emlsrLinks; /**< IDs of the links on which EMLSR mode has to be enabled */
|
||||
MHz_u m_channelWidth; //!< width of the channels used by MLDs
|
||||
MHz_u m_auxPhyChannelWidth; //!< max width supported by aux PHYs
|
||||
Time m_mediumSyncDuration; //!< duration of the MediumSyncDelay timer
|
||||
uint8_t m_msdMaxNTxops; //!< Max number of TXOPs that an EMLSR client is allowed
|
||||
//!< to attempt to initiate while the MediumSyncDelay
|
||||
//!< timer is running (zero indicates no limit)
|
||||
std::optional<uint8_t> m_nonEmlsrLink; //!< ID of the non-EMLSR link (if any)
|
||||
Time m_emlsrEnabledTime; //!< when EMLSR mode has been enabled on all EMLSR clients
|
||||
Time m_firstUlPktsGenTime; //!< generation time of the first two UL packets
|
||||
const Time m_unblockMainPhyLinkDelay; //!< delay between the time the first two UL packets are
|
||||
//!< generated and the time transmissions are unblocked
|
||||
//!< on the link where the main PHY is operating on
|
||||
Time m_lastMsdExpiryTime; //!< expiry time of the last MediumSyncDelay timer
|
||||
bool m_checkBackoffStarted; //!< whether we are checking the generated backoff values
|
||||
std::optional<Time> m_backoffEndTime; //!< expected backoff end time on main PHY link
|
||||
Ptr<ListErrorModel> m_errorModel; ///< error rate model to corrupt packets
|
||||
std::size_t m_countQoSframes; //!< counter for QoS frames
|
||||
std::size_t m_countBlockAck; //!< counter for BlockAck frames
|
||||
std::size_t m_countRtsframes; //!< counter for RTS frames
|
||||
bool m_genBackoffIfTxopWithoutTx; //!< whether the backoff should be invoked when the AC
|
||||
//!< gains the right to start a TXOP but it does not
|
||||
//!< transmit any frame
|
||||
std::optional<bool> m_corruptCts; //!< whether the transmitted CTS must be corrupted
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup wifi-test
|
||||
* @ingroup tests
|
||||
*
|
||||
* @brief Check UL OFDMA operations with EMLSR clients.
|
||||
*
|
||||
* This test considers an AP MLD and an EMLSR client and a non-AP MLD that setup three links with
|
||||
* the AP MLD. Once block ack agreements (for TID 0) are established for the UL direction, the
|
||||
* AP MLD starts requesting channel access (on all the links) through the Multi-User scheduler.
|
||||
* Given that links are idle, AP MLD accesses the channel on all the links and concurrently sends
|
||||
* Trigger Frames. When the transmission of the first Trigger Frame is over, a client application
|
||||
* on the EMLSR client generates two packets addressed to the AP MLD.
|
||||
*
|
||||
* It is checked that:
|
||||
* - when sending BSRP TF is disabled, the first Trigger Frame sent is an MU-RTS; otherwise, it is
|
||||
* a BSRP Trigger Frame. In both cases, such Trigger Frame acts as an ICF for the EMLSR client
|
||||
* - the other Trigger Frames sent concurrently with the ICF only solicit the non-EMLSR client
|
||||
* (AP MLD has blocked transmissions to the EMLSR client upon preparing the first Trigger Frame)
|
||||
* - the buffer status reported in QoS Null frames is as expected
|
||||
* - the EMLSR client sends a QoS Data frame in a TB PPDU
|
||||
*/
|
||||
class EmlsrUlOfdmaTest : public EmlsrOperationsTestBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param enableBsrp whether MU scheduler sends BSRP TFs
|
||||
*/
|
||||
EmlsrUlOfdmaTest(bool enableBsrp);
|
||||
|
||||
protected:
|
||||
void DoSetup() override;
|
||||
void DoRun() override;
|
||||
void Transmit(Ptr<WifiMac> mac,
|
||||
uint8_t phyId,
|
||||
WifiConstPsduMap psduMap,
|
||||
WifiTxVector txVector,
|
||||
double txPowerW) override;
|
||||
|
||||
/**
|
||||
* Check that the simulation produced the expected results.
|
||||
*/
|
||||
void CheckResults();
|
||||
|
||||
private:
|
||||
void StartTraffic() override;
|
||||
|
||||
bool m_enableBsrp; //!< whether MU scheduler sends BSRP TFs
|
||||
std::size_t m_txPsdusPos; //!< position in the vector of TX PSDUs of the first ICF
|
||||
Time m_startAccessReq; //!< start time of the first AP MLD access request via MU scheduler
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup wifi-test
|
||||
* @ingroup tests
|
||||
*
|
||||
* @brief wifi EMLSR suite to test basic frame exchanges.
|
||||
*/
|
||||
class WifiEmlsrBasicExchangesTestSuite : public TestSuite
|
||||
{
|
||||
public:
|
||||
WifiEmlsrBasicExchangesTestSuite();
|
||||
};
|
||||
|
||||
#endif /* WIFI_EMLSR_BASIC_EXCHANGES_TEST_H */
|
||||
361
src/wifi/test/wifi-emlsr-enabling-test.cc
Normal file
361
src/wifi/test/wifi-emlsr-enabling-test.cc
Normal file
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Universita' degli Studi di Napoli Federico II
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*
|
||||
* Author: Stefano Avallone <stavallo@unina.it>
|
||||
*/
|
||||
|
||||
#include "wifi-emlsr-enabling-test.h"
|
||||
|
||||
#include "ns3/advanced-emlsr-manager.h"
|
||||
#include "ns3/boolean.h"
|
||||
#include "ns3/config.h"
|
||||
#include "ns3/ctrl-headers.h"
|
||||
#include "ns3/log.h"
|
||||
#include "ns3/mgt-action-headers.h"
|
||||
#include "ns3/qos-txop.h"
|
||||
#include "ns3/simulator.h"
|
||||
#include "ns3/string.h"
|
||||
#include "ns3/wifi-net-device.h"
|
||||
#include "ns3/wifi-phy.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
|
||||
using namespace ns3;
|
||||
|
||||
NS_LOG_COMPONENT_DEFINE("WifiEmlsrEnablingTest");
|
||||
|
||||
EmlOperatingModeNotificationTest::EmlOperatingModeNotificationTest()
|
||||
: HeaderSerializationTestCase(
|
||||
"Check serialization and deserialization of the EML Operating Mode Notification frame")
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
EmlOperatingModeNotificationTest::DoRun()
|
||||
{
|
||||
MgtEmlOmn frame;
|
||||
|
||||
// Both EMLSR Mode and EMLMR Mode subfields set to 0 (no link bitmap);
|
||||
TestHeaderSerialization(frame);
|
||||
|
||||
frame.m_emlControl.emlsrMode = 1;
|
||||
frame.SetLinkIdInBitmap(0);
|
||||
frame.SetLinkIdInBitmap(5);
|
||||
frame.SetLinkIdInBitmap(15);
|
||||
|
||||
// Adding Link Bitmap
|
||||
TestHeaderSerialization(frame);
|
||||
|
||||
NS_TEST_EXPECT_MSG_EQ((frame.GetLinkBitmap() == std::list<uint8_t>{0, 5, 15}),
|
||||
true,
|
||||
"Unexpected link bitmap");
|
||||
|
||||
auto padding = MicroSeconds(64);
|
||||
auto transition = MicroSeconds(128);
|
||||
|
||||
frame.m_emlControl.emlsrParamUpdateCtrl = 1;
|
||||
frame.m_emlsrParamUpdate = MgtEmlOmn::EmlsrParamUpdate{};
|
||||
frame.m_emlsrParamUpdate->paddingDelay = CommonInfoBasicMle::EncodeEmlsrPaddingDelay(padding);
|
||||
frame.m_emlsrParamUpdate->transitionDelay =
|
||||
CommonInfoBasicMle::EncodeEmlsrTransitionDelay(transition);
|
||||
|
||||
// Adding the EMLSR Parameter Update field
|
||||
TestHeaderSerialization(frame);
|
||||
|
||||
NS_TEST_EXPECT_MSG_EQ(
|
||||
CommonInfoBasicMle::DecodeEmlsrPaddingDelay(frame.m_emlsrParamUpdate->paddingDelay),
|
||||
padding,
|
||||
"Unexpected EMLSR Padding Delay");
|
||||
NS_TEST_EXPECT_MSG_EQ(
|
||||
CommonInfoBasicMle::DecodeEmlsrTransitionDelay(frame.m_emlsrParamUpdate->transitionDelay),
|
||||
transition,
|
||||
"Unexpected EMLSR Transition Delay");
|
||||
}
|
||||
|
||||
EmlOmnExchangeTest::EmlOmnExchangeTest(const std::set<uint8_t>& linksToEnableEmlsrOn,
|
||||
Time transitionTimeout)
|
||||
: EmlsrOperationsTestBase("Check EML Notification exchange"),
|
||||
m_checkEmlsrLinksCount(0),
|
||||
m_emlNotificationDroppedCount(0)
|
||||
{
|
||||
m_linksToEnableEmlsrOn = linksToEnableEmlsrOn;
|
||||
m_nEmlsrStations = 1;
|
||||
m_nNonEmlsrStations = 0;
|
||||
m_transitionTimeout = transitionTimeout;
|
||||
m_duration = Seconds(0.5);
|
||||
}
|
||||
|
||||
void
|
||||
EmlOmnExchangeTest::DoSetup()
|
||||
{
|
||||
EmlsrOperationsTestBase::DoSetup();
|
||||
|
||||
m_errorModel = CreateObject<ListErrorModel>();
|
||||
for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
|
||||
{
|
||||
m_apMac->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_errorModel);
|
||||
}
|
||||
|
||||
m_staMacs[0]->TraceConnectWithoutContext("AckedMpdu",
|
||||
MakeCallback(&EmlOmnExchangeTest::TxOk, this));
|
||||
m_staMacs[0]->TraceConnectWithoutContext("DroppedMpdu",
|
||||
MakeCallback(&EmlOmnExchangeTest::TxDropped, this));
|
||||
}
|
||||
|
||||
void
|
||||
EmlOmnExchangeTest::Transmit(Ptr<WifiMac> mac,
|
||||
uint8_t phyId,
|
||||
WifiConstPsduMap psduMap,
|
||||
WifiTxVector txVector,
|
||||
double txPowerW)
|
||||
{
|
||||
EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
|
||||
auto linkId = m_txPsdus.back().linkId;
|
||||
|
||||
auto psdu = psduMap.begin()->second;
|
||||
|
||||
switch (psdu->GetHeader(0).GetType())
|
||||
{
|
||||
case WIFI_MAC_MGT_ASSOCIATION_REQUEST:
|
||||
NS_TEST_EXPECT_MSG_EQ(+linkId, +m_mainPhyId, "AssocReq not sent by the main PHY");
|
||||
CheckEmlCapabilitiesInAssocReq(*psdu->begin(), txVector, linkId);
|
||||
break;
|
||||
|
||||
case WIFI_MAC_MGT_ASSOCIATION_RESPONSE:
|
||||
CheckEmlCapabilitiesInAssocResp(*psdu->begin(), txVector, linkId);
|
||||
break;
|
||||
|
||||
case WIFI_MAC_MGT_ACTION:
|
||||
if (auto [category, action] = WifiActionHeader::Peek(psdu->GetPayload(0));
|
||||
category == WifiActionHeader::PROTECTED_EHT &&
|
||||
action.protectedEhtAction ==
|
||||
WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
|
||||
{
|
||||
CheckEmlNotification(psdu, txVector, linkId);
|
||||
|
||||
if (m_emlNotificationDroppedCount == 0 &&
|
||||
m_staMacs[0]->GetLinkIdByAddress(psdu->GetAddr2()) == linkId)
|
||||
{
|
||||
// transmitted by non-AP MLD, we need to corrupt it
|
||||
m_uidList.push_front(psdu->GetPacket()->GetUid());
|
||||
m_errorModel->SetList(m_uidList);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EmlOmnExchangeTest::CheckEmlCapabilitiesInAssocReq(Ptr<const WifiMpdu> mpdu,
|
||||
const WifiTxVector& txVector,
|
||||
uint8_t linkId)
|
||||
{
|
||||
MgtAssocRequestHeader frame;
|
||||
mpdu->GetPacket()->PeekHeader(frame);
|
||||
|
||||
const auto& mle = frame.Get<MultiLinkElement>();
|
||||
NS_TEST_ASSERT_MSG_EQ(mle.has_value(), true, "Multi-Link Element must be present in AssocReq");
|
||||
|
||||
NS_TEST_ASSERT_MSG_EQ(mle->HasEmlCapabilities(),
|
||||
true,
|
||||
"Multi-Link Element in AssocReq must have EML Capabilities");
|
||||
NS_TEST_ASSERT_MSG_EQ(mle->IsEmlsrSupported(),
|
||||
true,
|
||||
"EML Support subfield of EML Capabilities in AssocReq must be set to 1");
|
||||
NS_TEST_ASSERT_MSG_EQ(mle->GetEmlsrPaddingDelay(),
|
||||
m_paddingDelay.at(0),
|
||||
"Unexpected Padding Delay in EML Capabilities included in AssocReq");
|
||||
NS_TEST_ASSERT_MSG_EQ(mle->GetEmlsrTransitionDelay(),
|
||||
m_transitionDelay.at(0),
|
||||
"Unexpected Transition Delay in EML Capabilities included in AssocReq");
|
||||
}
|
||||
|
||||
void
|
||||
EmlOmnExchangeTest::CheckEmlCapabilitiesInAssocResp(Ptr<const WifiMpdu> mpdu,
|
||||
const WifiTxVector& txVector,
|
||||
uint8_t linkId)
|
||||
{
|
||||
bool sentToEmlsrClient =
|
||||
(m_staMacs[0]->GetLinkIdByAddress(mpdu->GetHeader().GetAddr1()) == linkId);
|
||||
|
||||
if (!sentToEmlsrClient)
|
||||
{
|
||||
// nothing to check
|
||||
return;
|
||||
}
|
||||
|
||||
MgtAssocResponseHeader frame;
|
||||
mpdu->GetPacket()->PeekHeader(frame);
|
||||
|
||||
const auto& mle = frame.Get<MultiLinkElement>();
|
||||
NS_TEST_ASSERT_MSG_EQ(mle.has_value(), true, "Multi-Link Element must be present in AssocResp");
|
||||
|
||||
NS_TEST_ASSERT_MSG_EQ(mle->HasEmlCapabilities(),
|
||||
true,
|
||||
"Multi-Link Element in AssocResp must have EML Capabilities");
|
||||
NS_TEST_ASSERT_MSG_EQ(mle->IsEmlsrSupported(),
|
||||
true,
|
||||
"EML Support subfield of EML Capabilities in AssocResp must be set to 1");
|
||||
NS_TEST_ASSERT_MSG_EQ(
|
||||
mle->GetTransitionTimeout(),
|
||||
m_transitionTimeout,
|
||||
"Unexpected Transition Timeout in EML Capabilities included in AssocResp");
|
||||
}
|
||||
|
||||
void
|
||||
EmlOmnExchangeTest::CheckEmlNotification(Ptr<const WifiPsdu> psdu,
|
||||
const WifiTxVector& txVector,
|
||||
uint8_t linkId)
|
||||
{
|
||||
MgtEmlOmn frame;
|
||||
auto mpdu = *psdu->begin();
|
||||
auto pkt = mpdu->GetPacket()->Copy();
|
||||
WifiActionHeader::Remove(pkt);
|
||||
pkt->RemoveHeader(frame);
|
||||
NS_LOG_DEBUG(frame);
|
||||
|
||||
bool sentbyNonApMld = m_staMacs[0]->GetLinkIdByAddress(mpdu->GetHeader().GetAddr2()) == linkId;
|
||||
|
||||
NS_TEST_EXPECT_MSG_EQ(+frame.m_emlControl.emlsrMode,
|
||||
1,
|
||||
"EMLSR Mode subfield should be set to 1 (frame sent by non-AP MLD: "
|
||||
<< std::boolalpha << sentbyNonApMld << ")");
|
||||
|
||||
NS_TEST_EXPECT_MSG_EQ(+frame.m_emlControl.emlmrMode,
|
||||
0,
|
||||
"EMLMR Mode subfield should be set to 0 (frame sent by non-AP MLD: "
|
||||
<< std::boolalpha << sentbyNonApMld << ")");
|
||||
|
||||
NS_TEST_ASSERT_MSG_EQ(frame.m_emlControl.linkBitmap.has_value(),
|
||||
true,
|
||||
"Link Bitmap subfield should be present (frame sent by non-AP MLD: "
|
||||
<< std::boolalpha << sentbyNonApMld << ")");
|
||||
|
||||
auto setupLinks = m_staMacs[0]->GetSetupLinkIds();
|
||||
std::list<uint8_t> expectedEmlsrLinks;
|
||||
std::set_intersection(setupLinks.begin(),
|
||||
setupLinks.end(),
|
||||
m_linksToEnableEmlsrOn.begin(),
|
||||
m_linksToEnableEmlsrOn.end(),
|
||||
std::back_inserter(expectedEmlsrLinks));
|
||||
|
||||
NS_TEST_EXPECT_MSG_EQ((expectedEmlsrLinks == frame.GetLinkBitmap()),
|
||||
true,
|
||||
"Unexpected Link Bitmap subfield (frame sent by non-AP MLD: "
|
||||
<< std::boolalpha << sentbyNonApMld << ")");
|
||||
|
||||
if (!sentbyNonApMld)
|
||||
{
|
||||
// the frame has been sent by the AP MLD
|
||||
NS_TEST_ASSERT_MSG_EQ(
|
||||
+frame.m_emlControl.emlsrParamUpdateCtrl,
|
||||
0,
|
||||
"EMLSR Parameter Update Control should be set to 0 in frames sent by the AP MLD");
|
||||
|
||||
// as soon as the non-AP MLD receives this frame, it sets the EMLSR links
|
||||
auto delay = WifiPhy::CalculateTxDuration(psdu,
|
||||
txVector,
|
||||
m_staMacs[0]->GetWifiPhy(linkId)->GetPhyBand()) +
|
||||
MicroSeconds(1); // to account for propagation delay
|
||||
Simulator::Schedule(delay, &EmlOmnExchangeTest::CheckEmlsrLinks, this);
|
||||
}
|
||||
|
||||
NS_TEST_EXPECT_MSG_EQ(+m_mainPhyId,
|
||||
+linkId,
|
||||
"EML Notification received on unexpected link (frame sent by non-AP MLD: "
|
||||
<< std::boolalpha << sentbyNonApMld << ")");
|
||||
}
|
||||
|
||||
void
|
||||
EmlOmnExchangeTest::TxOk(Ptr<const WifiMpdu> mpdu)
|
||||
{
|
||||
const auto& hdr = mpdu->GetHeader();
|
||||
|
||||
if (hdr.IsMgt() && hdr.IsAction())
|
||||
{
|
||||
if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
|
||||
category == WifiActionHeader::PROTECTED_EHT &&
|
||||
action.protectedEhtAction ==
|
||||
WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
|
||||
{
|
||||
// the EML Operating Mode Notification frame that the non-AP MLD sent has been
|
||||
// acknowledged; after the transition timeout, the EMLSR links have been set
|
||||
Simulator::Schedule(m_transitionTimeout + NanoSeconds(1),
|
||||
&EmlOmnExchangeTest::CheckEmlsrLinks,
|
||||
this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EmlOmnExchangeTest::TxDropped(WifiMacDropReason reason, Ptr<const WifiMpdu> mpdu)
|
||||
{
|
||||
const auto& hdr = mpdu->GetHeader();
|
||||
|
||||
if (hdr.IsMgt() && hdr.IsAction())
|
||||
{
|
||||
if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
|
||||
category == WifiActionHeader::PROTECTED_EHT &&
|
||||
action.protectedEhtAction ==
|
||||
WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
|
||||
{
|
||||
// the EML Operating Mode Notification frame has been dropped. Don't
|
||||
// corrupt it anymore
|
||||
m_emlNotificationDroppedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EmlOmnExchangeTest::CheckEmlsrLinks()
|
||||
{
|
||||
m_checkEmlsrLinksCount++;
|
||||
|
||||
auto setupLinks = m_staMacs[0]->GetSetupLinkIds();
|
||||
std::set<uint8_t> expectedEmlsrLinks;
|
||||
std::set_intersection(setupLinks.begin(),
|
||||
setupLinks.end(),
|
||||
m_linksToEnableEmlsrOn.begin(),
|
||||
m_linksToEnableEmlsrOn.end(),
|
||||
std::inserter(expectedEmlsrLinks, expectedEmlsrLinks.end()));
|
||||
|
||||
NS_TEST_EXPECT_MSG_EQ((expectedEmlsrLinks == m_staMacs[0]->GetEmlsrManager()->GetEmlsrLinks()),
|
||||
true,
|
||||
"Unexpected set of EMLSR links)");
|
||||
}
|
||||
|
||||
void
|
||||
EmlOmnExchangeTest::DoRun()
|
||||
{
|
||||
Simulator::Stop(m_duration);
|
||||
Simulator::Run();
|
||||
|
||||
NS_TEST_EXPECT_MSG_EQ(m_checkEmlsrLinksCount,
|
||||
2,
|
||||
"Unexpected number of times CheckEmlsrLinks() is called");
|
||||
NS_TEST_EXPECT_MSG_EQ(
|
||||
m_emlNotificationDroppedCount,
|
||||
1,
|
||||
"Unexpected number of times the EML Notification frame is dropped due to max retry limit");
|
||||
|
||||
Simulator::Destroy();
|
||||
}
|
||||
|
||||
WifiEmlsrEnablingTestSuite::WifiEmlsrEnablingTestSuite()
|
||||
: TestSuite("wifi-emlsr-enabling", Type::UNIT)
|
||||
{
|
||||
AddTestCase(new EmlOperatingModeNotificationTest(), TestCase::Duration::QUICK);
|
||||
AddTestCase(new EmlOmnExchangeTest({1, 2}, MicroSeconds(0)), TestCase::Duration::QUICK);
|
||||
AddTestCase(new EmlOmnExchangeTest({1, 2}, MicroSeconds(2048)), TestCase::Duration::QUICK);
|
||||
AddTestCase(new EmlOmnExchangeTest({0, 1, 2, 3}, MicroSeconds(0)), TestCase::Duration::QUICK);
|
||||
AddTestCase(new EmlOmnExchangeTest({0, 1, 2, 3}, MicroSeconds(2048)),
|
||||
TestCase::Duration::QUICK);
|
||||
}
|
||||
|
||||
static WifiEmlsrEnablingTestSuite g_wifiEmlsrEnablingTestSuite; ///< the test suite
|
||||
153
src/wifi/test/wifi-emlsr-enabling-test.h
Normal file
153
src/wifi/test/wifi-emlsr-enabling-test.h
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Universita' degli Studi di Napoli Federico II
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*
|
||||
* Author: Stefano Avallone <stavallo@unina.it>
|
||||
*/
|
||||
|
||||
#ifndef WIFI_EMLSR_ENABLING_TEST_H
|
||||
#define WIFI_EMLSR_ENABLING_TEST_H
|
||||
|
||||
#include "wifi-emlsr-test-base.h"
|
||||
|
||||
#include <list>
|
||||
|
||||
/**
|
||||
* @ingroup wifi-test
|
||||
* @ingroup tests
|
||||
*
|
||||
* @brief Test EML Operating Mode Notification frame serialization and deserialization
|
||||
*/
|
||||
class EmlOperatingModeNotificationTest : public HeaderSerializationTestCase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
EmlOperatingModeNotificationTest();
|
||||
~EmlOperatingModeNotificationTest() override = default;
|
||||
|
||||
private:
|
||||
void DoRun() override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup wifi-test
|
||||
* @ingroup tests
|
||||
*
|
||||
* @brief Test the exchange of EML Operating Mode Notification frames.
|
||||
*
|
||||
* This test considers an AP MLD and a non-AP MLD with EMLSR activated. Upon association,
|
||||
* the non-AP MLD sends an EML Operating Mode Notification frame, which is however corrupted
|
||||
* by using a post reception error model (installed on the AP MLD). We keep corrupting the
|
||||
* EML Notification frames transmitted by the non-AP MLD until the frame is dropped due to
|
||||
* exceeded max retry limit. It is checked that:
|
||||
*
|
||||
* - the Association Request contains a Multi-Link Element including an EML Capabilities field
|
||||
* that contains the expected values for Padding Delay and Transition Delay
|
||||
* - the Association Response contains a Multi-Link Element including an EML Capabilities field
|
||||
* that contains the expected value for Transition Timeout
|
||||
* - all EML Notification frames contain the expected values for EMLSR Mode, EMLMR Mode and
|
||||
* Link Bitmap fields and are transmitted on the link used for association
|
||||
* - the correct EMLSR link set is stored by the EMLSR Manager, both when the transition
|
||||
* timeout expires and when an EML Notification response is received from the AP MLD (thus,
|
||||
* the correct EMLSR link set is stored after whichever of the two events occur first)
|
||||
*/
|
||||
class EmlOmnExchangeTest : public EmlsrOperationsTestBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param linksToEnableEmlsrOn IDs of links on which EMLSR mode should be enabled
|
||||
* @param transitionTimeout the Transition Timeout advertised by the AP MLD
|
||||
*/
|
||||
EmlOmnExchangeTest(const std::set<uint8_t>& linksToEnableEmlsrOn, Time transitionTimeout);
|
||||
~EmlOmnExchangeTest() override = default;
|
||||
|
||||
protected:
|
||||
void DoSetup() override;
|
||||
void DoRun() override;
|
||||
void Transmit(Ptr<WifiMac> mac,
|
||||
uint8_t phyId,
|
||||
WifiConstPsduMap psduMap,
|
||||
WifiTxVector txVector,
|
||||
double txPowerW) override;
|
||||
|
||||
/**
|
||||
* Callback invoked when the non-AP MLD receives the acknowledgment for a transmitted MPDU.
|
||||
*
|
||||
* @param mpdu the acknowledged MPDU
|
||||
*/
|
||||
void TxOk(Ptr<const WifiMpdu> mpdu);
|
||||
/**
|
||||
* Callback invoked when the non-AP MLD drops the given MPDU for the given reason.
|
||||
*
|
||||
* @param reason the reason why the MPDU was dropped
|
||||
* @param mpdu the dropped MPDU
|
||||
*/
|
||||
void TxDropped(WifiMacDropReason reason, Ptr<const WifiMpdu> mpdu);
|
||||
|
||||
/**
|
||||
* Check the content of the EML Capabilities subfield of the Multi-Link Element included
|
||||
* in the Association Request frame sent by the non-AP MLD.
|
||||
*
|
||||
* @param mpdu the MPDU containing the Association Request frame
|
||||
* @param txVector the TXVECTOR used to transmit the frame
|
||||
* @param linkId the ID of the link on which the frame was transmitted
|
||||
*/
|
||||
void CheckEmlCapabilitiesInAssocReq(Ptr<const WifiMpdu> mpdu,
|
||||
const WifiTxVector& txVector,
|
||||
uint8_t linkId);
|
||||
/**
|
||||
* Check the content of the EML Capabilities subfield of the Multi-Link Element included
|
||||
* in the Association Response frame sent by the AP MLD to the EMLSR client.
|
||||
*
|
||||
* @param mpdu the MPDU containing the Association Response frame
|
||||
* @param txVector the TXVECTOR used to transmit the frame
|
||||
* @param linkId the ID of the link on which the frame was transmitted
|
||||
*/
|
||||
void CheckEmlCapabilitiesInAssocResp(Ptr<const WifiMpdu> mpdu,
|
||||
const WifiTxVector& txVector,
|
||||
uint8_t linkId);
|
||||
/**
|
||||
* Check the content of a received EML Operating Mode Notification frame.
|
||||
*
|
||||
* @param psdu the PSDU containing the EML Operating Mode Notification frame
|
||||
* @param txVector the TXVECTOR used to transmit the frame
|
||||
* @param linkId the ID of the link on which the frame was transmitted
|
||||
*/
|
||||
void CheckEmlNotification(Ptr<const WifiPsdu> psdu,
|
||||
const WifiTxVector& txVector,
|
||||
uint8_t linkId);
|
||||
/**
|
||||
* Check that the EMLSR mode has been enabled on the expected EMLSR links.
|
||||
*/
|
||||
void CheckEmlsrLinks();
|
||||
|
||||
private:
|
||||
std::size_t m_checkEmlsrLinksCount; /**< counter for the number of times CheckEmlsrLinks
|
||||
is called (should be two: when the transition
|
||||
timeout expires and when the EML Notification
|
||||
response from the AP MLD is received */
|
||||
std::size_t m_emlNotificationDroppedCount; /**< counter for the number of times the EML
|
||||
Notification frame sent by the non-AP MLD
|
||||
has been dropped due to max retry limit */
|
||||
Ptr<ListErrorModel> m_errorModel; ///< error rate model to corrupt packets at AP MLD
|
||||
std::list<uint64_t> m_uidList; ///< list of UIDs of packets to corrupt
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup wifi-test
|
||||
* @ingroup tests
|
||||
*
|
||||
* @brief wifi EMLSR suite to test the procedure for enabling EMLSR mode
|
||||
*/
|
||||
class WifiEmlsrEnablingTestSuite : public TestSuite
|
||||
{
|
||||
public:
|
||||
WifiEmlsrEnablingTestSuite();
|
||||
};
|
||||
|
||||
#endif /* WIFI_EMLSR_ENABLING_TEST_H */
|
||||
2502
src/wifi/test/wifi-emlsr-link-switch-test.cc
Normal file
2502
src/wifi/test/wifi-emlsr-link-switch-test.cc
Normal file
File diff suppressed because it is too large
Load Diff
691
src/wifi/test/wifi-emlsr-link-switch-test.h
Normal file
691
src/wifi/test/wifi-emlsr-link-switch-test.h
Normal file
@@ -0,0 +1,691 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Universita' degli Studi di Napoli Federico II
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*
|
||||
* Author: Stefano Avallone <stavallo@unina.it>
|
||||
*/
|
||||
|
||||
#ifndef WIFI_EMLSR_LINK_SWITCH_TEST_H
|
||||
#define WIFI_EMLSR_LINK_SWITCH_TEST_H
|
||||
|
||||
#include "wifi-emlsr-test-base.h"
|
||||
|
||||
#include <list>
|
||||
|
||||
using namespace ns3;
|
||||
using namespace std::string_literals;
|
||||
|
||||
// forward declaration
|
||||
namespace ns3
|
||||
{
|
||||
struct EmlsrMainPhySwitchTrace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup wifi-test
|
||||
* @ingroup tests
|
||||
*
|
||||
* @brief Test the switching of PHYs on EMLSR clients.
|
||||
*
|
||||
* An AP MLD and an EMLSR client setup 3 links, on which EMLSR mode is enabled. The AP MLD
|
||||
* transmits 4 QoS data frames (one after another, each protected by ICF):
|
||||
*
|
||||
* - 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
|
||||
*
|
||||
* 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
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Parameters for the EMLSR link switching test
|
||||
*/
|
||||
struct Params
|
||||
{
|
||||
bool
|
||||
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
|
||||
bool resetCamStateAndInterruptSwitch; //!< this variable controls two boolean values that
|
||||
//!< are either both set to true or both set to false;
|
||||
//!< the first value controls whether to reset the
|
||||
//!< state of the ChannelAccessManager associated
|
||||
//!< with the link on which the main PHY has just
|
||||
//!< switched to, the second value controls whether
|
||||
//!< a main PHY channel switch can be interrupted
|
||||
MHz_u auxPhyMaxChWidth; //!< max channel width supported by aux PHYs
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param params parameters for the EMLSR link switching test
|
||||
*/
|
||||
EmlsrLinkSwitchTest(const Params& params);
|
||||
|
||||
~EmlsrLinkSwitchTest() override = default;
|
||||
|
||||
protected:
|
||||
void DoSetup() override;
|
||||
void DoRun() override;
|
||||
void Transmit(Ptr<WifiMac> mac,
|
||||
uint8_t phyId,
|
||||
WifiConstPsduMap psduMap,
|
||||
WifiTxVector txVector,
|
||||
double txPowerW) override;
|
||||
|
||||
/**
|
||||
* Check that the simulation produced the expected results.
|
||||
*/
|
||||
void CheckResults();
|
||||
|
||||
/**
|
||||
* Check that the Main PHY (and possibly the Aux PHY) correctly switches channel when the
|
||||
* reception of an ICF ends.
|
||||
*
|
||||
* @param psduMap the PSDU carrying the MU-RTS TF
|
||||
* @param txVector the TXVECTOR used to send the PPDU
|
||||
* @param linkId the ID of the given link
|
||||
*/
|
||||
void CheckInitialControlFrame(const WifiConstPsduMap& psduMap,
|
||||
const WifiTxVector& txVector,
|
||||
uint8_t linkId);
|
||||
|
||||
/**
|
||||
* Check that appropriate actions are taken by the AP MLD transmitting a PPDU containing
|
||||
* QoS data frames to the EMLSR client on the given link.
|
||||
*
|
||||
* @param psduMap the PSDU(s) carrying QoS data frames
|
||||
* @param txVector the TXVECTOR used to send the PPDU
|
||||
* @param linkId the ID of the given link
|
||||
*/
|
||||
void CheckQosFrames(const WifiConstPsduMap& psduMap,
|
||||
const WifiTxVector& txVector,
|
||||
uint8_t linkId);
|
||||
|
||||
/**
|
||||
* Check that appropriate actions are taken by the EMLSR client transmitting a PPDU containing
|
||||
* an RTS frame to the AP MLD on the given link.
|
||||
*
|
||||
* @param psduMap the PSDU carrying RTS frame
|
||||
* @param txVector the TXVECTOR used to send the PPDU
|
||||
* @param linkId the ID of the given link
|
||||
*/
|
||||
void CheckRtsFrame(const WifiConstPsduMap& psduMap,
|
||||
const WifiTxVector& txVector,
|
||||
uint8_t linkId);
|
||||
|
||||
private:
|
||||
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 Aux PHY */
|
||||
bool
|
||||
m_resetCamStateAndInterruptSwitch; /**< whether to reset the state of the
|
||||
ChannelAccessManager associated with the link on which the main PHY
|
||||
has just switched to and whether main PHY switch can be interrupted */
|
||||
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<ListErrorModel> m_errorModel; ///< error rate model to corrupt packets at AP MLD
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup wifi-test
|
||||
* @ingroup tests
|
||||
*
|
||||
* @brief Test CCA busy notifications on EMLSR clients.
|
||||
*
|
||||
* SwitchAuxPhy is set to true, so that the aux PHY starts switching when the main PHY switch is
|
||||
* completed.
|
||||
*
|
||||
* - Main PHY switches to a link on which an aux PHY is operating. Right after the start of the
|
||||
* channel switch, the AP transmits a frame to another device on the aux PHY link. Verify that,
|
||||
* once the main PHY is operating on the new link, the channel access manager on that link is
|
||||
* notified of CCA busy until the end of the transmission
|
||||
* - When the main PHY switch is completed, the aux PHY switches to a link on which no PHY is
|
||||
* operating. Before the aux PHY starts switching, the AP starts transmitting a frame to another
|
||||
* device on the link on which no PHY is operating. Verify that, once the aux PHY is operating
|
||||
* on the new link, the channel access manager on that link is notified of CCA busy until the
|
||||
* end of the transmission
|
||||
*/
|
||||
class EmlsrCcaBusyTest : public EmlsrOperationsTestBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param auxPhyMaxChWidth max channel width supported by aux PHYs
|
||||
*/
|
||||
EmlsrCcaBusyTest(MHz_u auxPhyMaxChWidth);
|
||||
|
||||
~EmlsrCcaBusyTest() override = default;
|
||||
|
||||
protected:
|
||||
void DoSetup() override;
|
||||
void DoRun() override;
|
||||
|
||||
private:
|
||||
void StartTraffic() override;
|
||||
|
||||
/**
|
||||
* Make the other MLD transmit a packet to the AP on the given link.
|
||||
*
|
||||
* @param linkId the ID of the given link
|
||||
*/
|
||||
void TransmitPacketToAp(uint8_t linkId);
|
||||
|
||||
/**
|
||||
* Perform checks after that the preamble of the first PPDU has been received.
|
||||
*/
|
||||
void CheckPoint1();
|
||||
|
||||
/**
|
||||
* Perform checks after that the main PHY completed the link switch.
|
||||
*/
|
||||
void CheckPoint2();
|
||||
|
||||
/**
|
||||
* Perform checks after that the aux PHY completed the link switch.
|
||||
*/
|
||||
void CheckPoint3();
|
||||
|
||||
MHz_u m_auxPhyMaxChWidth; //!< max channel width supported by aux PHYs
|
||||
Time m_channelSwitchDelay; //!< the PHY channel switch delay
|
||||
uint8_t m_currMainPhyLinkId; //!< the ID of the link the main PHY switches from
|
||||
uint8_t m_nextMainPhyLinkId; //!< the ID of the link the main PHY switches to
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup wifi-test
|
||||
* @ingroup tests
|
||||
*
|
||||
* @brief Test ML setup and data exchange between an AP MLD and a single link EMLSR client.
|
||||
*
|
||||
* A single link EMLSR client performs ML setup with an AP MLD having three links and then enables
|
||||
* EMLSR mode on the unique link. A Block Ack agreement is established (for both the downlink and
|
||||
* uplink directions) and QoS data frames (aggregated in an A-MPDU) are transmitted by both the
|
||||
* AP MLD and the EMLSR client.
|
||||
*
|
||||
* It is checked that:
|
||||
* - the expected sequence of frames is transmitted, including ICFs before downlink transmissions
|
||||
* - EMLSR mode is enabled on the single EMLSR link
|
||||
* - the address of the EMLSR client is seen as an MLD address
|
||||
* - the AP MLD starts the transition delay timer at the end of each TXOP
|
||||
*/
|
||||
class SingleLinkEmlsrTest : public EmlsrOperationsTestBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param switchAuxPhy whether aux PHYs switch link
|
||||
* @param auxPhyTxCapable whether aux PHYs are TX capable
|
||||
*/
|
||||
SingleLinkEmlsrTest(bool switchAuxPhy, bool auxPhyTxCapable);
|
||||
|
||||
protected:
|
||||
void DoSetup() override;
|
||||
void DoRun() override;
|
||||
|
||||
void Transmit(Ptr<WifiMac> mac,
|
||||
uint8_t phyId,
|
||||
WifiConstPsduMap psduMap,
|
||||
WifiTxVector txVector,
|
||||
double txPowerW) override;
|
||||
|
||||
/// 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<void(Ptr<const WifiPsdu>, const WifiTxVector&)>&& f = {})
|
||||
: hdrType(type),
|
||||
func(f)
|
||||
{
|
||||
}
|
||||
|
||||
WifiMacType hdrType; ///< MAC header type of frame being transmitted
|
||||
std::function<void(Ptr<const WifiPsdu>, const WifiTxVector&)>
|
||||
func; ///< function to perform actions and checks
|
||||
};
|
||||
|
||||
private:
|
||||
bool m_switchAuxPhy; //!< whether aux PHYs switch link
|
||||
bool m_auxPhyTxCapable; //!< whether aux PHYs are TX capable
|
||||
std::list<Events> m_events; //!< list of events for a test run
|
||||
std::list<Events>::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:
|
||||
* during preamble detection period, before the PHY header end, before the MAC header end, before
|
||||
* the padding start and after the padding start.
|
||||
*
|
||||
\verbatim
|
||||
┌────────┬──────┬──────┬────────────────────┬───────┐
|
||||
│PREAMBLE│ PHY │ MAC │ MAC PAYLOAD │ │
|
||||
│ DETECT │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
|
||||
*
|
||||
* At the end of the DL TXOP, it is checked that:
|
||||
* - if the KeepMainPhyAfterDlTxop attribute of the AdvancedEmlsrManager is false, the main PHY
|
||||
* switches back to the preferred link
|
||||
* - if the KeepMainPhyAfterDlTxop attribute of the AdvancedEmlsrManager is true, the main PHY
|
||||
* stays on the current link to start an UL TXOP, if the UL frame can be sent on the same link
|
||||
* as the DL frame, or switches back to the preferred link, otherwise
|
||||
*
|
||||
* At the end of the UL TXOP, the main PHY returns to the preferred link.
|
||||
*
|
||||
* 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
|
||||
{
|
||||
DURING_PREAMBLE_DETECTION = 0,
|
||||
BEFORE_PHY_HDR_END,
|
||||
BEFORE_MAC_HDR_END,
|
||||
BEFORE_MAC_PAYLOAD_END,
|
||||
BEFORE_PADDING_END,
|
||||
CSD_COUNT
|
||||
};
|
||||
|
||||
protected:
|
||||
void DoSetup() override;
|
||||
void DoRun() override;
|
||||
void Transmit(Ptr<WifiMac> 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<WifiPhy> 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<WifiMac> 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<void(Ptr<const WifiPsdu>, const WifiTxVector&, uint8_t)>&& f = {})
|
||||
: hdrType(type),
|
||||
func(f)
|
||||
{
|
||||
}
|
||||
|
||||
WifiMacType hdrType; ///< MAC header type of frame being transmitted
|
||||
std::function<void(Ptr<const WifiPsdu>, 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<MainPhySwitchInfo> m_switchFrom; //!< info for main PHY leaving a link
|
||||
std::optional<MainPhySwitchInfo> m_switchTo; //!< info for main PHY connected to a link
|
||||
std::array<WifiSpectrumBandInfo, 3> m_bands; //!< bands of the 3 frequency channels
|
||||
std::list<Events> 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
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup wifi-test
|
||||
* @ingroup tests
|
||||
*
|
||||
* @brief Switch main PHY back timer test
|
||||
*
|
||||
* 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 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
|
||||
* MAC of the EMLSR client receives the RX start notification, which indicates that the modulation
|
||||
* is HT (hence the PPDU does not carry an ICF) and the PPDU duration exceeds the switch main PHY
|
||||
* back delay. The EMLSR client decides to switch the main PHY back to the preferred link (with
|
||||
* reason RX_END), but the actual main PHY switch is postponed until the ongoing channel switch
|
||||
* terminates.
|
||||
* - RXSTART_WHILE_SWITCH_INTERRUPT: same as previous scenario, except that the main PHY switch can
|
||||
* be interrupted, hence the main PHY switches back to the preferred link as soon as the reception
|
||||
* of the PHY header ends.
|
||||
* - RXSTART_AFTER_SWITCH_HT_PPDU: the AP MLD transmits an HT PPDU some time after the main PHY
|
||||
* starts switching to link 1; the delay is computed so that the RX START notification is sent
|
||||
* after that the main PHY has completed the channel switch. When the main PHY completes the
|
||||
* switch to link 1, it is determined that the PPDU being received (using HT modulation) cannot
|
||||
* be an ICF, hence the main PHY is connected to link 1. Connecting the main PHY to link 1
|
||||
* triggers a CCA busy notification until the end of the PPDU (we assume this information is
|
||||
* available from the PHY header decoded by the aux PHY), thus the main PHY switches back to the
|
||||
* preferred link (with reason BUSY_END).
|
||||
* - NON_HT_PPDU_DONT_USE_MAC_HDR: the AP MLD transmits a non-HT PPDU on link 1 (it does not really
|
||||
* matter if the RX START notification is sent before or after the end of main PHY switch). When
|
||||
* the main PHY completes the switch to link 1, it is detected that the aux PHY on link 1 is
|
||||
* receiving a PPDU which may be an ICF (the modulation is non-HT), hence the main PHY is not
|
||||
* connected to link 1 until the end of the PPDU reception (MAC header info is not used). At that
|
||||
* time, it is detected that the PPDU does not contain an ICF, but it is determined that channel
|
||||
* access can be gained before the end of the switch main PHY back timer, hence the main PHY stays
|
||||
* on link 1 and transmits its unicast data frame. The start of the UL TXOP cancels the main PHY
|
||||
* switch back timer and the main PHY switches back to the preferred link at the end of the TXOP.
|
||||
* - NON_HT_PPDU_USE_MAC_HDR: same as previous scenario, except that the MAC header info can be
|
||||
* used. After completing the channel switch, the main PHY is not connected to link 1 because the
|
||||
* non-HT PPDU being received may be an ICF. When the MAC header info is notified, it is detected
|
||||
* that the PPDU does not contain an ICF, channel access would not be gained before the end of the
|
||||
* switch main PHY back timer and therefore the main PHY switches back to the preferred link (with
|
||||
* reason RX_END).
|
||||
* - LONG_SWITCH_BACK_DELAY_DONT_USE_MAC_HDR: same as the NON_HT_PPDU_DONT_USE_MAC_HDR scenario,
|
||||
* except that the switch main PHY back delay is longer and exceeds the PPDU duration, but it is
|
||||
* does not exceed the PPDU duration plus AIFS and the backoff slots. Therefore, at the end of the
|
||||
* PPDU reception, it is determined that the backoff counter will not reach zero before the 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).
|
||||
* - LONG_SWITCH_BACK_DELAY_USE_MAC_HDR: same as the NON_HT_PPDU_USE_MAC_HDR scenario,
|
||||
* except that the switch main PHY back delay is longer and exceeds the PPDU duration, but it
|
||||
* does not exceed the PPDU duration plus AIFS and the backoff slots. Therefore, at the end of the
|
||||
* MAC header reception, it is determined that the backoff counter will not reach zero before the
|
||||
* 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).
|
||||
*
|
||||
* 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
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
EmlsrSwitchMainPhyBackTest();
|
||||
|
||||
/**
|
||||
* Enumeration indicating the tested scenario
|
||||
*/
|
||||
enum TestScenario : uint8_t
|
||||
{
|
||||
RXSTART_WHILE_SWITCH_NO_INTERRUPT = 0,
|
||||
RXSTART_WHILE_SWITCH_INTERRUPT,
|
||||
RXSTART_AFTER_SWITCH_HT_PPDU,
|
||||
NON_HT_PPDU_DONT_USE_MAC_HDR,
|
||||
NON_HT_PPDU_USE_MAC_HDR,
|
||||
LONG_SWITCH_BACK_DELAY_DONT_USE_MAC_HDR,
|
||||
LONG_SWITCH_BACK_DELAY_USE_MAC_HDR,
|
||||
COUNT
|
||||
};
|
||||
|
||||
protected:
|
||||
void DoSetup() override;
|
||||
void DoRun() override;
|
||||
void Transmit(Ptr<WifiMac> mac,
|
||||
uint8_t phyId,
|
||||
WifiConstPsduMap psduMap,
|
||||
WifiTxVector txVector,
|
||||
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();
|
||||
|
||||
/// 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<void(Ptr<const WifiPsdu>, const WifiTxVector&, uint8_t)>&& f = {})
|
||||
: hdrType(type),
|
||||
func(f)
|
||||
{
|
||||
}
|
||||
|
||||
WifiMacType hdrType; ///< MAC header type of frame being transmitted
|
||||
std::function<void(Ptr<const WifiPsdu>, const WifiTxVector&, uint8_t)>
|
||||
func; ///< function to perform actions and checks
|
||||
};
|
||||
|
||||
private:
|
||||
void StartTraffic() override;
|
||||
|
||||
uint8_t m_testIndex{0}; //!< index to iterate over test scenarios
|
||||
bool m_setupDone{false}; //!< whether association, BA, ... have been done
|
||||
bool m_dlPktDone{false}; //!< whether the DL packet has been generated
|
||||
std::list<Events> 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<WifiMpdu> 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
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup wifi-test
|
||||
* @ingroup tests
|
||||
*
|
||||
* @brief Check NAV and CCA in the last PIFS test
|
||||
*
|
||||
* 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 operate on 20 MHz channels; the main PHY operates on
|
||||
* 40 MHz channels and the preferred link is link 1. In order to control link switches, a
|
||||
* TID-to-Link mapping is configured so that TID 0 is mapped onto link 2 for both DL and UL. In this
|
||||
* test, the main PHY switches to link 2 to start an UL TXOP a predefined number of slots before
|
||||
* the backoff ends on link 2. We consider different durations of the channel switch delay to
|
||||
* verify the time the data frame is transmitted by the EMLSR client on link 2 and the data frame
|
||||
* TX width in various situations:
|
||||
*
|
||||
* AuxPhyCca = false AuxPhyCca = true
|
||||
* ┌────┐ ┌────┐
|
||||
* │QoS │40 │QoS │20
|
||||
* |--PIFS--│Data│MHz |--PIFS--│Data│MHz
|
||||
* ──────┬─────────┬────────┴────┴──── ──────┬─────────┼────┴─────────────
|
||||
* Backoff Switch Backoff Switch
|
||||
* end end end end
|
||||
*
|
||||
*
|
||||
* AuxPhyCca = false AuxPhyCca = true
|
||||
* ┌────┐ ┌────┐
|
||||
* │QoS │40 │QoS │20
|
||||
* |--PIFS--│Data│MHz |--PIFS--│Data│MHz
|
||||
* ─────────┬──────┬─┴────┴─────────── ──────────┬─────┼────┴─────────────
|
||||
* Switch Backoff Switch Backoff
|
||||
* end end end end
|
||||
*
|
||||
*
|
||||
* AuxPhyCca = false/true
|
||||
* ┌────┐
|
||||
* │QoS │40
|
||||
* |--PIFS--| │Data│MHz
|
||||
* ─────────┬───────────┼────┴────────
|
||||
* Switch Backoff
|
||||
* end end
|
||||
*
|
||||
* In all the cases, it is verified that the EMLSR client transmits the data frame, at the expected
|
||||
* time and on the expected channel width, and receives the acknowledgment.
|
||||
*/
|
||||
class EmlsrCheckNavAndCcaLastPifsTest : public EmlsrOperationsTestBase
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
EmlsrCheckNavAndCcaLastPifsTest();
|
||||
|
||||
/**
|
||||
* Enumeration indicating the tested scenario
|
||||
*/
|
||||
enum TestScenario : uint8_t
|
||||
{
|
||||
BACKOFF_END_BEFORE_SWITCH_END = 0,
|
||||
LESS_THAN_PIFS_UNTIL_BACKOFF_END,
|
||||
MORE_THAN_PIFS_UNTIL_BACKOFF_END,
|
||||
COUNT
|
||||
};
|
||||
|
||||
protected:
|
||||
void DoSetup() override;
|
||||
void DoRun() override;
|
||||
void Transmit(Ptr<WifiMac> 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();
|
||||
|
||||
/// 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<void(Ptr<const WifiPsdu>, const WifiTxVector&, uint8_t)>&& f = {})
|
||||
: hdrType(type),
|
||||
func(f)
|
||||
{
|
||||
}
|
||||
|
||||
WifiMacType hdrType; ///< MAC header type of frame being transmitted
|
||||
std::function<void(Ptr<const WifiPsdu>, const WifiTxVector&, uint8_t)>
|
||||
func; ///< function to perform actions and checks
|
||||
};
|
||||
|
||||
private:
|
||||
void StartTraffic() override;
|
||||
|
||||
std::size_t m_testIndex{0}; //!< index to iterate over test scenarios
|
||||
bool m_setupDone{false}; //!< whether association, BA, ... have been done
|
||||
std::list<Events> m_events; //!< list of events for a test run
|
||||
std::size_t m_processedEvents{0}; //!< number of processed events
|
||||
const uint8_t m_linkIdForTid0{2}; //!< ID of the link on which TID 0 is mapped
|
||||
const uint8_t m_nSlotsLeft{4}; //!< value for the CAM NSlotsLeft attribute
|
||||
const MHz_u m_mainPhyWidth{40}; //!< main PHY channel width
|
||||
const MHz_u m_auxPhyWidth{20}; //!< aux PHY channel width
|
||||
Time m_expectedTxStart; //!< expected start time for frame transmission
|
||||
MHz_u m_expectedWidth; //!< expected channel width for frame transmission
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup wifi-test
|
||||
* @ingroup tests
|
||||
*
|
||||
* @brief wifi EMLSR suite to test link switch operations
|
||||
*/
|
||||
class WifiEmlsrLinkSwitchTestSuite : public TestSuite
|
||||
{
|
||||
public:
|
||||
WifiEmlsrLinkSwitchTestSuite();
|
||||
};
|
||||
|
||||
#endif /* WIFI_EMLSR_LINK_SWITCH_TEST_H */
|
||||
663
src/wifi/test/wifi-emlsr-test-base.cc
Normal file
663
src/wifi/test/wifi-emlsr-test-base.cc
Normal file
@@ -0,0 +1,663 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Universita' degli Studi di Napoli Federico II
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*
|
||||
* Author: Stefano Avallone <stavallo@unina.it>
|
||||
*/
|
||||
|
||||
#include "wifi-emlsr-test-base.h"
|
||||
|
||||
#include "ns3/advanced-ap-emlsr-manager.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/interference-helper.h"
|
||||
#include "ns3/log.h"
|
||||
#include "ns3/mgt-action-headers.h"
|
||||
#include "ns3/mobility-helper.h"
|
||||
#include "ns3/multi-model-spectrum-channel.h"
|
||||
#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"
|
||||
#include "ns3/simulator.h"
|
||||
#include "ns3/spectrum-wifi-helper.h"
|
||||
#include "ns3/spectrum-wifi-phy.h"
|
||||
#include "ns3/string.h"
|
||||
#include "ns3/wifi-net-device.h"
|
||||
#include "ns3/wifi-spectrum-phy-interface.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
|
||||
using namespace ns3;
|
||||
|
||||
NS_LOG_COMPONENT_DEFINE("WifiEmlsrTest");
|
||||
|
||||
EmlsrOperationsTestBase::EmlsrOperationsTestBase(const std::string& name)
|
||||
: TestCase(name)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
EmlsrOperationsTestBase::Transmit(Ptr<WifiMac> mac,
|
||||
uint8_t phyId,
|
||||
WifiConstPsduMap psduMap,
|
||||
WifiTxVector txVector,
|
||||
double txPowerW)
|
||||
{
|
||||
auto linkId = mac->GetLinkForPhy(phyId);
|
||||
NS_TEST_ASSERT_MSG_EQ(linkId.has_value(), true, "No link found for PHY ID " << +phyId);
|
||||
m_txPsdus.push_back({Simulator::Now(), psduMap, txVector, *linkId, phyId});
|
||||
|
||||
auto txDuration =
|
||||
WifiPhy::CalculateTxDuration(psduMap, txVector, mac->GetWifiPhy(*linkId)->GetPhyBand());
|
||||
|
||||
for (const auto& [aid, psdu] : psduMap)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::setprecision(10) << "PSDU #" << m_txPsdus.size() << " Link ID "
|
||||
<< +linkId.value() << " Phy ID " << +phyId << " " << psdu->GetHeader(0).GetTypeString();
|
||||
if (psdu->GetHeader(0).IsAction())
|
||||
{
|
||||
ss << " ";
|
||||
WifiActionHeader actionHdr;
|
||||
psdu->GetPayload(0)->PeekHeader(actionHdr);
|
||||
actionHdr.Print(ss);
|
||||
}
|
||||
ss << " #MPDUs " << psdu->GetNMpdus() << " duration/ID " << psdu->GetHeader(0).GetDuration()
|
||||
<< " RA = " << psdu->GetAddr1() << " TA = " << psdu->GetAddr2()
|
||||
<< " ADDR3 = " << psdu->GetHeader(0).GetAddr3()
|
||||
<< " ToDS = " << psdu->GetHeader(0).IsToDs()
|
||||
<< " FromDS = " << psdu->GetHeader(0).IsFromDs();
|
||||
if (psdu->GetHeader(0).IsQosData())
|
||||
{
|
||||
ss << " seqNo = {";
|
||||
for (auto& mpdu : *PeekPointer(psdu))
|
||||
{
|
||||
ss << mpdu->GetHeader().GetSequenceNumber() << ",";
|
||||
}
|
||||
ss << "} TID = " << +psdu->GetHeader(0).GetQosTid();
|
||||
}
|
||||
NS_LOG_INFO(ss.str());
|
||||
|
||||
// if this frame is transmitted by an EMLSR client on an EMLSR links, in-device interference
|
||||
// is configured and the TX duration exceeds the threshold (72us), MediumSyncDelay timer is
|
||||
// (re)started at the end of the transmission
|
||||
if (auto staMac = DynamicCast<StaWifiMac>(mac);
|
||||
staMac && staMac->IsEmlsrLink(*linkId) &&
|
||||
staMac->GetEmlsrManager()->GetMediumSyncDuration().IsStrictlyPositive())
|
||||
{
|
||||
const auto mustStartMsd =
|
||||
staMac->GetEmlsrManager()->GetInDeviceInterference() &&
|
||||
txDuration > MicroSeconds(EmlsrManager::MEDIUM_SYNC_THRESHOLD_USEC);
|
||||
|
||||
for (auto id : staMac->GetLinkIds())
|
||||
{
|
||||
// timer started on EMLSR links other than the link on which TX is starting,
|
||||
// provided that a PHY is operating on the link and MediumSyncDuration is not null
|
||||
if (!staMac->IsEmlsrLink(id) || id == *linkId || staMac->GetWifiPhy(id) == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Simulator::Schedule(
|
||||
txDuration - TimeStep(1),
|
||||
[=, hdrType = psdu->GetHeader(0).GetTypeString(), this]() {
|
||||
// check if MSD timer was running on the link before completing transmission
|
||||
// and is expected to be running when check is performed (in 2 timesteps)
|
||||
const auto msdTimer =
|
||||
staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(id);
|
||||
const auto msdDuration =
|
||||
staMac->GetEhtConfiguration()->m_mediumSyncDuration;
|
||||
const auto msdWasRunning =
|
||||
(msdTimer.has_value() && (msdDuration - *msdTimer > TimeStep(2)));
|
||||
if (auto phy = staMac->GetWifiPhy(id);
|
||||
!msdWasRunning && !mustStartMsd && phy && phy->IsStateSleep())
|
||||
{
|
||||
// if the MSD timer was not running before the end of the TX, it is not
|
||||
// expected to be started and the PHY operating on this link is
|
||||
// sleeping, do not check that the MSD timer is not started after the
|
||||
// end of the TX, because it may be started because of the sleep period
|
||||
// of the aux PHY
|
||||
return;
|
||||
}
|
||||
Simulator::Schedule(TimeStep(2), [=, this]() {
|
||||
CheckMsdTimerRunning(staMac,
|
||||
id,
|
||||
(msdWasRunning || mustStartMsd),
|
||||
std::string("after transmitting ") + hdrType +
|
||||
" on link " + std::to_string(*linkId));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
NS_LOG_INFO("TX duration = " << txDuration.As(Time::MS) << " TXVECTOR = " << txVector << "\n");
|
||||
}
|
||||
|
||||
void
|
||||
EmlsrOperationsTestBase::CheckMsdTimerRunning(Ptr<StaWifiMac> staMac,
|
||||
uint8_t linkId,
|
||||
bool isRunning,
|
||||
const std::string& msg)
|
||||
{
|
||||
auto time = staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(linkId);
|
||||
NS_TEST_ASSERT_MSG_EQ(time.has_value(),
|
||||
isRunning,
|
||||
Simulator::Now().As(Time::MS)
|
||||
<< " Unexpected status for MediumSyncDelay timer on link " << +linkId
|
||||
<< " " << msg);
|
||||
if (auto phy = staMac->GetWifiPhy(linkId))
|
||||
{
|
||||
auto currThreshold = phy->GetCcaEdThreshold();
|
||||
NS_TEST_EXPECT_MSG_EQ((static_cast<int8_t>(currThreshold) ==
|
||||
staMac->GetEmlsrManager()->GetMediumSyncOfdmEdThreshold()),
|
||||
isRunning,
|
||||
Simulator::Now().As(Time::MS)
|
||||
<< " Unexpected value (" << currThreshold
|
||||
<< ") for CCA ED threshold on link " << +linkId << " " << msg);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EmlsrOperationsTestBase::CheckAuxPhysSleepMode(Ptr<StaWifiMac> staMac, bool sleep)
|
||||
{
|
||||
if (!m_putAuxPhyToSleep)
|
||||
{
|
||||
// if m_putAuxPhyToSleep is false, aux PHYs must not be put to sleep
|
||||
sleep = false;
|
||||
}
|
||||
|
||||
for (const auto& phy : staMac->GetDevice()->GetPhys())
|
||||
{
|
||||
if (phy->GetPhyId() == m_mainPhyId)
|
||||
{
|
||||
continue; // do not check the main PHY
|
||||
}
|
||||
|
||||
auto linkId = staMac->GetLinkForPhy(phy);
|
||||
|
||||
if (linkId.has_value() && !staMac->IsEmlsrLink(*linkId))
|
||||
{
|
||||
continue; // this PHY is not operating on an EMLSR link
|
||||
}
|
||||
|
||||
if (!sleep)
|
||||
{
|
||||
NS_TEST_EXPECT_MSG_EQ(phy->IsStateSleep(),
|
||||
false,
|
||||
Simulator::Now().GetTimeStep()
|
||||
<< " PHY " << +phy->GetPhyId() << " is in unexpected state "
|
||||
<< phy->GetState()->GetState());
|
||||
continue;
|
||||
}
|
||||
|
||||
// if the PHY is in state TX or switching, sleep is postponed until their end
|
||||
const auto delay =
|
||||
(phy->IsStateTx() || phy->IsStateSwitching()) ? phy->GetDelayUntilIdle() : Time{0};
|
||||
|
||||
Simulator::Schedule(delay, [=, this]() {
|
||||
NS_TEST_EXPECT_MSG_EQ(phy->IsStateSleep(),
|
||||
true,
|
||||
"PHY " << +phy->GetPhyId() << " is in unexpected state "
|
||||
<< phy->GetState()->GetState());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EmlsrOperationsTestBase::MainPhySwitchInfoCallback(std::size_t index,
|
||||
const EmlsrMainPhySwitchTrace& info)
|
||||
{
|
||||
m_traceInfo[index] = info.Clone();
|
||||
}
|
||||
|
||||
void
|
||||
EmlsrOperationsTestBase::CheckMainPhyTraceInfo(std::size_t index,
|
||||
std::string_view reason,
|
||||
const std::optional<uint8_t>& fromLinkId,
|
||||
uint8_t toLinkId,
|
||||
bool checkFromLinkId,
|
||||
bool checkToLinkId)
|
||||
{
|
||||
const auto traceInfoIt = m_traceInfo.find(index);
|
||||
NS_TEST_ASSERT_MSG_EQ((traceInfoIt != m_traceInfo.cend()),
|
||||
true,
|
||||
"Expected stored trace info: " << reason);
|
||||
const auto& traceInfo = traceInfoIt->second;
|
||||
|
||||
NS_TEST_EXPECT_MSG_EQ(traceInfo->GetName(), reason, "Unexpected reason");
|
||||
|
||||
if (checkFromLinkId)
|
||||
{
|
||||
NS_TEST_ASSERT_MSG_EQ(traceInfo->fromLinkId.has_value(),
|
||||
fromLinkId.has_value(),
|
||||
"Unexpected stored from_link ID");
|
||||
if (fromLinkId.has_value())
|
||||
{
|
||||
NS_TEST_EXPECT_MSG_EQ(+traceInfo->fromLinkId.value(),
|
||||
+fromLinkId.value(),
|
||||
"Unexpected from_link ID");
|
||||
}
|
||||
}
|
||||
|
||||
if (checkToLinkId)
|
||||
{
|
||||
NS_TEST_EXPECT_MSG_EQ(+traceInfo->toLinkId, +toLinkId, "Unexpected to_link ID");
|
||||
}
|
||||
|
||||
m_traceInfo.erase(traceInfoIt);
|
||||
}
|
||||
|
||||
void
|
||||
EmlsrOperationsTestBase::DoSetup()
|
||||
{
|
||||
RngSeedManager::SetSeed(m_rngSeed);
|
||||
RngSeedManager::SetRun(m_rngRun);
|
||||
int64_t streamNumber = m_streamNo;
|
||||
|
||||
Config::SetDefault("ns3::WifiMac::MpduBufferSize", UintegerValue(64));
|
||||
Config::SetDefault("ns3::EmlsrManager::InDeviceInterference", BooleanValue(true));
|
||||
Config::SetDefault("ns3::EmlsrManager::PutAuxPhyToSleep", BooleanValue(m_putAuxPhyToSleep));
|
||||
|
||||
NodeContainer wifiApNode(1);
|
||||
NodeContainer wifiStaNodes(m_nEmlsrStations);
|
||||
|
||||
WifiHelper wifi;
|
||||
// wifi.EnableLogComponents ();
|
||||
wifi.SetStandard(WIFI_STANDARD_80211be);
|
||||
wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
|
||||
"DataMode",
|
||||
StringValue("EhtMcs0"),
|
||||
"ControlMode",
|
||||
StringValue("HtMcs0"));
|
||||
wifi.ConfigEhtOptions("EmlsrActivated",
|
||||
BooleanValue(true),
|
||||
"TransitionTimeout",
|
||||
TimeValue(m_transitionTimeout));
|
||||
|
||||
// MLDs are configured with three links
|
||||
SpectrumWifiPhyHelper phyHelper(3);
|
||||
phyHelper.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
|
||||
phyHelper.SetPcapCaptureType(WifiPhyHelper::PcapCaptureType::PCAP_PER_LINK);
|
||||
|
||||
for (std::size_t id = 0; id < m_channelsStr.size(); ++id)
|
||||
{
|
||||
phyHelper.Set(id, "ChannelSettings", StringValue(m_channelsStr[id]));
|
||||
phyHelper.AddChannel(CreateObject<MultiModelSpectrumChannel>(), m_freqRanges[id]);
|
||||
}
|
||||
|
||||
WifiMacHelper mac;
|
||||
mac.SetType("ns3::ApWifiMac",
|
||||
"Ssid",
|
||||
SsidValue(Ssid("ns-3-ssid")),
|
||||
"BeaconGeneration",
|
||||
BooleanValue(true));
|
||||
mac.SetApEmlsrManager("ns3::AdvancedApEmlsrManager",
|
||||
"WaitTransDelayOnPsduRxError",
|
||||
BooleanValue(true));
|
||||
|
||||
NetDeviceContainer apDevice = wifi.Install(phyHelper, mac, wifiApNode);
|
||||
m_apMac = DynamicCast<ApWifiMac>(DynamicCast<WifiNetDevice>(apDevice.Get(0))->GetMac());
|
||||
|
||||
mac.SetType("ns3::StaWifiMac",
|
||||
"Ssid",
|
||||
SsidValue(Ssid("wrong-ssid")),
|
||||
"MaxMissedBeacons",
|
||||
UintegerValue(1e6), // do not deassociate
|
||||
"ActiveProbing",
|
||||
BooleanValue(false));
|
||||
mac.SetEmlsrManager("ns3::AdvancedEmlsrManager",
|
||||
"EmlsrLinkSet",
|
||||
AttributeContainerValue<UintegerValue>(m_linksToEnableEmlsrOn),
|
||||
"MainPhyId",
|
||||
UintegerValue(m_mainPhyId));
|
||||
|
||||
if (m_nPhysPerEmlsrDevice < 3)
|
||||
{
|
||||
phyHelper = SpectrumWifiPhyHelper(m_nPhysPerEmlsrDevice);
|
||||
phyHelper.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
|
||||
phyHelper.SetPcapCaptureType(WifiPhyHelper::PcapCaptureType::PCAP_PER_LINK);
|
||||
|
||||
for (std::size_t id = 0; id < m_nPhysPerEmlsrDevice; ++id)
|
||||
{
|
||||
phyHelper.Set(id, "ChannelSettings", StringValue(m_channelsStr[id]));
|
||||
auto channel =
|
||||
DynamicCast<MultiModelSpectrumChannel>(m_apMac->GetWifiPhy(id)->GetChannel());
|
||||
NS_TEST_ASSERT_MSG_NE(channel,
|
||||
nullptr,
|
||||
"Channel " << +id << " is not a spectrum channel");
|
||||
phyHelper.AddChannel(channel, m_freqRanges[id]);
|
||||
}
|
||||
}
|
||||
|
||||
NetDeviceContainer staDevices = wifi.Install(phyHelper, mac, wifiStaNodes);
|
||||
|
||||
for (uint32_t i = 0; i < staDevices.GetN(); i++)
|
||||
{
|
||||
auto device = DynamicCast<WifiNetDevice>(staDevices.Get(i));
|
||||
auto staMac = DynamicCast<StaWifiMac>(device->GetMac());
|
||||
auto emlsrManager = staMac->GetEmlsrManager();
|
||||
NS_ASSERT_MSG(i < m_paddingDelay.size(), "Not enough padding delay values provided");
|
||||
emlsrManager->SetAttribute("EmlsrPaddingDelay", TimeValue(m_paddingDelay.at(i)));
|
||||
NS_ASSERT_MSG(i < m_transitionDelay.size(), "Not enough transition delay values provided");
|
||||
emlsrManager->SetAttribute("EmlsrTransitionDelay", TimeValue(m_transitionDelay.at(i)));
|
||||
emlsrManager->TraceConnectWithoutContext(
|
||||
"MainPhySwitch",
|
||||
MakeCallback(&EmlsrOperationsTestBase::MainPhySwitchInfoCallback, this).Bind(i));
|
||||
}
|
||||
|
||||
if (m_nNonEmlsrStations > 0)
|
||||
{
|
||||
// create the other non-AP MLDs for which EMLSR is not activated
|
||||
wifi.ConfigEhtOptions("EmlsrActivated", BooleanValue(false));
|
||||
NodeContainer otherStaNodes(m_nNonEmlsrStations);
|
||||
staDevices.Add(wifi.Install(phyHelper, mac, otherStaNodes));
|
||||
wifiStaNodes.Add(otherStaNodes);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < staDevices.GetN(); i++)
|
||||
{
|
||||
auto device = DynamicCast<WifiNetDevice>(staDevices.Get(i));
|
||||
m_staMacs.push_back(DynamicCast<StaWifiMac>(device->GetMac()));
|
||||
}
|
||||
|
||||
// Trace PSDUs passed to the PHY on AP MLD and non-AP MLDs
|
||||
for (uint8_t phyId = 0; phyId < m_apMac->GetDevice()->GetNPhys(); phyId++)
|
||||
{
|
||||
Config::ConnectWithoutContext(
|
||||
"/NodeList/0/DeviceList/*/$ns3::WifiNetDevice/Phys/" + std::to_string(phyId) +
|
||||
"/PhyTxPsduBegin",
|
||||
MakeCallback(&EmlsrOperationsTestBase::Transmit, this).Bind(m_apMac, phyId));
|
||||
}
|
||||
for (std::size_t i = 0; i < m_nEmlsrStations + m_nNonEmlsrStations; i++)
|
||||
{
|
||||
for (uint8_t phyId = 0; phyId < m_staMacs[i]->GetDevice()->GetNPhys(); phyId++)
|
||||
{
|
||||
Config::ConnectWithoutContext(
|
||||
"/NodeList/" + std::to_string(i + 1) + "/DeviceList/*/$ns3::WifiNetDevice/Phys/" +
|
||||
std::to_string(phyId) + "/PhyTxPsduBegin",
|
||||
MakeCallback(&EmlsrOperationsTestBase::Transmit, this).Bind(m_staMacs[i], phyId));
|
||||
}
|
||||
}
|
||||
|
||||
// Uncomment the lines below to write PCAP files
|
||||
// phyHelper.EnablePcap("wifi-emlsr_AP", apDevice);
|
||||
// phyHelper.EnablePcap("wifi-emlsr_STA", staDevices);
|
||||
|
||||
// Assign fixed streams to random variables in use
|
||||
streamNumber += WifiHelper::AssignStreams(apDevice, streamNumber);
|
||||
streamNumber += WifiHelper::AssignStreams(staDevices, streamNumber);
|
||||
|
||||
MobilityHelper mobility;
|
||||
Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();
|
||||
|
||||
for (std::size_t id = 0; id <= m_nEmlsrStations + m_nNonEmlsrStations; id++)
|
||||
{
|
||||
// all non-AP MLDs are co-located
|
||||
positionAlloc->Add(Vector(std::min<double>(id, 1), 0.0, 0.0));
|
||||
}
|
||||
mobility.SetPositionAllocator(positionAlloc);
|
||||
|
||||
mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
|
||||
mobility.Install(wifiApNode);
|
||||
mobility.Install(wifiStaNodes);
|
||||
|
||||
// install packet socket on all nodes
|
||||
PacketSocketHelper packetSocket;
|
||||
packetSocket.Install(wifiApNode);
|
||||
packetSocket.Install(wifiStaNodes);
|
||||
|
||||
// install a packet socket server on all nodes
|
||||
for (auto nodeIt = NodeList::Begin(); nodeIt != NodeList::End(); nodeIt++)
|
||||
{
|
||||
PacketSocketAddress srvAddr;
|
||||
auto device = DynamicCast<WifiNetDevice>((*nodeIt)->GetDevice(0));
|
||||
NS_TEST_ASSERT_MSG_NE(device, nullptr, "Expected a WifiNetDevice");
|
||||
srvAddr.SetSingleDevice(device->GetIfIndex());
|
||||
srvAddr.SetProtocol(1);
|
||||
|
||||
auto server = CreateObject<PacketSocketServer>();
|
||||
server->SetLocal(srvAddr);
|
||||
(*nodeIt)->AddApplication(server);
|
||||
server->SetStartTime(Seconds(0)); // now
|
||||
server->SetStopTime(m_duration);
|
||||
}
|
||||
|
||||
// set DL and UL packet sockets
|
||||
for (const auto& staMac : m_staMacs)
|
||||
{
|
||||
m_dlSockets.emplace_back();
|
||||
m_dlSockets.back().SetSingleDevice(m_apMac->GetDevice()->GetIfIndex());
|
||||
m_dlSockets.back().SetPhysicalAddress(staMac->GetDevice()->GetAddress());
|
||||
m_dlSockets.back().SetProtocol(1);
|
||||
|
||||
m_ulSockets.emplace_back();
|
||||
m_ulSockets.back().SetSingleDevice(staMac->GetDevice()->GetIfIndex());
|
||||
m_ulSockets.back().SetPhysicalAddress(m_apMac->GetDevice()->GetAddress());
|
||||
m_ulSockets.back().SetProtocol(1);
|
||||
}
|
||||
|
||||
m_startAid = m_apMac->GetNextAssociationId();
|
||||
|
||||
// schedule ML setup for one station at a time
|
||||
m_apMac->TraceConnectWithoutContext(
|
||||
"AssociatedSta",
|
||||
MakeCallback(&EmlsrOperationsTestBase::StaAssociated, this));
|
||||
for (const auto& [aci, ac] : wifiAcList)
|
||||
{
|
||||
m_apMac->GetQosTxop(aci)->TraceConnectWithoutContext(
|
||||
"BaEstablished",
|
||||
MakeCallback(&EmlsrOperationsTestBase::BaEstablishedDl, this));
|
||||
for (std::size_t id = 0; id < m_nEmlsrStations + m_nNonEmlsrStations; ++id)
|
||||
{
|
||||
m_staMacs[id]->GetQosTxop(aci)->TraceConnectWithoutContext(
|
||||
"BaEstablished",
|
||||
MakeCallback(&EmlsrOperationsTestBase::BaEstablishedUl, this).Bind(id));
|
||||
}
|
||||
}
|
||||
Simulator::Schedule(Seconds(0), [&]() { m_staMacs[0]->SetSsid(Ssid("ns-3-ssid")); });
|
||||
}
|
||||
|
||||
Ptr<PacketSocketClient>
|
||||
EmlsrOperationsTestBase::GetApplication(TrafficDirection dir,
|
||||
std::size_t staId,
|
||||
std::size_t count,
|
||||
std::size_t pktSize,
|
||||
uint8_t priority) const
|
||||
{
|
||||
auto client = CreateObject<PacketSocketClient>();
|
||||
client->SetAttribute("PacketSize", UintegerValue(pktSize));
|
||||
client->SetAttribute("MaxPackets", UintegerValue(count));
|
||||
client->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
|
||||
client->SetAttribute("Priority", UintegerValue(priority));
|
||||
client->SetRemote(dir == DOWNLINK ? m_dlSockets.at(staId) : m_ulSockets.at(staId));
|
||||
client->SetStartTime(Seconds(0)); // now
|
||||
client->SetStopTime(m_duration - Simulator::Now());
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
void
|
||||
EmlsrOperationsTestBase::StaAssociated(uint16_t aid, Mac48Address /* addr */)
|
||||
{
|
||||
if (m_lastAid == aid)
|
||||
{
|
||||
// another STA of this non-AP MLD has already fired this callback
|
||||
return;
|
||||
}
|
||||
m_lastAid = aid;
|
||||
|
||||
// wait some time (10ms) to allow the completion of association
|
||||
const auto delay = MilliSeconds(10);
|
||||
|
||||
if (!m_establishBaDl.empty())
|
||||
{
|
||||
// trigger establishment of BA agreement with AP as originator
|
||||
Simulator::Schedule(delay, [=, this]() {
|
||||
m_apMac->GetDevice()->GetNode()->AddApplication(
|
||||
GetApplication(DOWNLINK, aid - m_startAid, 4, 1000, m_establishBaDl.front()));
|
||||
});
|
||||
}
|
||||
else if (!m_establishBaUl.empty())
|
||||
{
|
||||
// trigger establishment of BA agreement with AP as recipient
|
||||
Simulator::Schedule(delay, [=, this]() {
|
||||
m_staMacs[aid - m_startAid]->GetDevice()->GetNode()->AddApplication(
|
||||
GetApplication(UPLINK, aid - m_startAid, 4, 1000, m_establishBaUl.front()));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Simulator::Schedule(delay, [=, this]() { SetSsid(aid - m_startAid + 1); });
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EmlsrOperationsTestBase::BaEstablishedDl(Mac48Address recipient,
|
||||
uint8_t tid,
|
||||
std::optional<Mac48Address> /* gcrGroup */)
|
||||
{
|
||||
// wait some time (10ms) to allow the exchange of the data frame that triggered the Block Ack
|
||||
const auto delay = MilliSeconds(10);
|
||||
|
||||
auto linkId = m_apMac->IsAssociated(recipient);
|
||||
NS_TEST_ASSERT_MSG_EQ(linkId.has_value(), true, "No link for association of " << recipient);
|
||||
auto aid = m_apMac->GetWifiRemoteStationManager(*linkId)->GetAssociationId(recipient);
|
||||
|
||||
if (auto it = std::find(m_establishBaDl.cbegin(), m_establishBaDl.cend(), tid);
|
||||
it != m_establishBaDl.cend() && std::next(it) != m_establishBaDl.cend())
|
||||
{
|
||||
// trigger establishment of BA agreement with AP as originator
|
||||
Simulator::Schedule(delay, [=, this]() {
|
||||
m_apMac->GetDevice()->GetNode()->AddApplication(
|
||||
GetApplication(DOWNLINK, aid - m_startAid, 4, 1000, *std::next(it)));
|
||||
});
|
||||
}
|
||||
else if (!m_establishBaUl.empty())
|
||||
{
|
||||
// trigger establishment of BA agreement with AP as recipient
|
||||
Simulator::Schedule(delay, [=, this]() {
|
||||
m_staMacs[aid - m_startAid]->GetDevice()->GetNode()->AddApplication(
|
||||
GetApplication(UPLINK, aid - m_startAid, 4, 1000, m_establishBaUl.front()));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Simulator::Schedule(delay, [=, this]() { SetSsid(aid - m_startAid + 1); });
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EmlsrOperationsTestBase::BaEstablishedUl(std::size_t index,
|
||||
Mac48Address recipient,
|
||||
uint8_t tid,
|
||||
std::optional<Mac48Address> /* gcrGroup */)
|
||||
{
|
||||
// wait some time (10ms) to allow the exchange of the data frame that triggered the Block Ack
|
||||
const auto delay = MilliSeconds(10);
|
||||
|
||||
if (auto it = std::find(m_establishBaUl.cbegin(), m_establishBaUl.cend(), tid);
|
||||
it != m_establishBaUl.cend() && std::next(it) != m_establishBaUl.cend())
|
||||
{
|
||||
// trigger establishment of BA agreement with AP as recipient
|
||||
Simulator::Schedule(delay, [=, this]() {
|
||||
m_staMacs[index]->GetDevice()->GetNode()->AddApplication(
|
||||
GetApplication(UPLINK, index, 4, 1000, *std::next(it)));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Simulator::Schedule(delay, [=, this]() { SetSsid(index + 1); });
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EmlsrOperationsTestBase::SetSsid(std::size_t count)
|
||||
{
|
||||
if (count < m_nEmlsrStations + m_nNonEmlsrStations)
|
||||
{
|
||||
// make the next STA start ML discovery & setup
|
||||
m_staMacs[count]->SetSsid(Ssid("ns-3-ssid"));
|
||||
return;
|
||||
}
|
||||
// all stations associated; start traffic if needed
|
||||
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",
|
||||
MakeCallback(&EmlsrOperationsTestBase::StaAssociated, this));
|
||||
for (const auto& [aci, ac] : wifiAcList)
|
||||
{
|
||||
m_apMac->GetQosTxop(aci)->TraceDisconnectWithoutContext(
|
||||
"BaEstablished",
|
||||
MakeCallback(&EmlsrOperationsTestBase::BaEstablishedDl, this));
|
||||
for (std::size_t id = 0; id < m_nEmlsrStations + m_nNonEmlsrStations; ++id)
|
||||
{
|
||||
m_staMacs[id]->GetQosTxop(aci)->TraceDisconnectWithoutContext(
|
||||
"BaEstablished",
|
||||
MakeCallback(&EmlsrOperationsTestBase::BaEstablishedUl, this).Bind(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EmlsrOperationsTestBase::CheckBlockedLink(Ptr<WifiMac> mac,
|
||||
Mac48Address dest,
|
||||
uint8_t linkId,
|
||||
WifiQueueBlockedReason reason,
|
||||
bool blocked,
|
||||
std::string description,
|
||||
bool testUnblockedForOtherReasons)
|
||||
{
|
||||
WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, dest, 0);
|
||||
auto mask = mac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, linkId);
|
||||
NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
|
||||
true,
|
||||
description << ": Expected to find a mask for EMLSR link " << +linkId);
|
||||
if (blocked)
|
||||
{
|
||||
NS_TEST_EXPECT_MSG_EQ(mask->test(static_cast<std::size_t>(reason)),
|
||||
true,
|
||||
description << ": Expected EMLSR link " << +linkId
|
||||
<< " to be blocked for reason " << reason);
|
||||
if (testUnblockedForOtherReasons)
|
||||
{
|
||||
NS_TEST_EXPECT_MSG_EQ(mask->count(),
|
||||
1,
|
||||
description << ": Expected EMLSR link " << +linkId
|
||||
<< " to be blocked for one reason only");
|
||||
}
|
||||
}
|
||||
else if (testUnblockedForOtherReasons)
|
||||
{
|
||||
NS_TEST_EXPECT_MSG_EQ(mask->none(),
|
||||
true,
|
||||
description << ": Expected EMLSR link " << +linkId
|
||||
<< " to be unblocked");
|
||||
}
|
||||
else
|
||||
{
|
||||
NS_TEST_EXPECT_MSG_EQ(mask->test(static_cast<std::size_t>(reason)),
|
||||
false,
|
||||
description << ": Expected EMLSR link " << +linkId
|
||||
<< " to be unblocked for reason " << reason);
|
||||
}
|
||||
}
|
||||
272
src/wifi/test/wifi-emlsr-test-base.h
Normal file
272
src/wifi/test/wifi-emlsr-test-base.h
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Universita' degli Studi di Napoli Federico II
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*
|
||||
* Author: Stefano Avallone <stavallo@unina.it>
|
||||
*/
|
||||
|
||||
#ifndef WIFI_EMLSR_TEST_H
|
||||
#define WIFI_EMLSR_TEST_H
|
||||
|
||||
#include "ns3/adhoc-wifi-mac.h"
|
||||
#include "ns3/ap-wifi-mac.h"
|
||||
#include "ns3/error-model.h"
|
||||
#include "ns3/header-serialization-test.h"
|
||||
#include "ns3/packet-socket-address.h"
|
||||
#include "ns3/packet-socket-client.h"
|
||||
#include "ns3/sta-wifi-mac.h"
|
||||
#include "ns3/test.h"
|
||||
#include "ns3/wifi-mac-queue-scheduler.h"
|
||||
#include "ns3/wifi-mac.h"
|
||||
#include "ns3/wifi-ppdu.h"
|
||||
#include "ns3/wifi-psdu.h"
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
using namespace ns3;
|
||||
using namespace std::string_literals;
|
||||
|
||||
// forward declaration
|
||||
namespace ns3
|
||||
{
|
||||
struct EmlsrMainPhySwitchTrace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup wifi-test
|
||||
* @ingroup tests
|
||||
*
|
||||
* @brief Base class for EMLSR Operations tests
|
||||
*
|
||||
* This base class setups and configures one AP MLD, a variable number of non-AP MLDs with
|
||||
* EMLSR activated and a variable number of non-AP MLD with EMLSR deactivated. Every MLD has
|
||||
* three links, each operating on a distinct PHY band (2.4 GHz, 5 GHz and 6 GHz). Therefore,
|
||||
* it is expected that three links are setup by the non-AP MLD(s). The values for the Padding
|
||||
* Delay, the Transition Delay and the Transition Timeout are provided as argument to the
|
||||
* constructor of this class, along with the IDs of the links on which EMLSR mode must be
|
||||
* enabled for the non-AP MLDs (this information is used to set the EmlsrLinkSet attribute
|
||||
* of the DefaultEmlsrManager installed on the non-AP MLDs).
|
||||
*/
|
||||
class EmlsrOperationsTestBase : public TestCase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param name The name of the new TestCase created
|
||||
*/
|
||||
EmlsrOperationsTestBase(const std::string& name);
|
||||
~EmlsrOperationsTestBase() override = default;
|
||||
|
||||
/// Enumeration for traffic directions
|
||||
enum TrafficDirection : uint8_t
|
||||
{
|
||||
DOWNLINK = 0,
|
||||
UPLINK
|
||||
};
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Callback invoked when a FEM passes PSDUs to the PHY.
|
||||
*
|
||||
* @param mac the MAC transmitting the PSDUs
|
||||
* @param phyId the ID of the PHY transmitting the PSDUs
|
||||
* @param psduMap the PSDU map
|
||||
* @param txVector the TX vector
|
||||
* @param txPowerW the tx power in Watts
|
||||
*/
|
||||
virtual void Transmit(Ptr<WifiMac> mac,
|
||||
uint8_t phyId,
|
||||
WifiConstPsduMap psduMap,
|
||||
WifiTxVector txVector,
|
||||
double txPowerW);
|
||||
|
||||
/**
|
||||
* @param dir the traffic direction (downlink/uplink)
|
||||
* @param staId the index (starting at 0) of the non-AP MLD generating/receiving packets
|
||||
* @param count the number of packets to generate
|
||||
* @param pktSize the size of the packets to generate
|
||||
* @param priority user priority for generated packets
|
||||
* @return an application generating the given number packets of the given size from/to the
|
||||
* AP MLD to/from the given non-AP MLD
|
||||
*/
|
||||
Ptr<PacketSocketClient> GetApplication(TrafficDirection dir,
|
||||
std::size_t staId,
|
||||
std::size_t count,
|
||||
std::size_t pktSize,
|
||||
uint8_t priority = 0) const;
|
||||
|
||||
/**
|
||||
* Check whether QoS data unicast transmissions addressed to the given destination on the
|
||||
* given link are blocked or unblocked for the given reason on the given device.
|
||||
*
|
||||
* @param mac the MAC of the given device
|
||||
* @param dest the MAC address of the given destination
|
||||
* @param linkId the ID of the given link
|
||||
* @param reason the reason for blocking transmissions to test
|
||||
* @param blocked whether transmissions are blocked for the given reason
|
||||
* @param description text indicating when this check is performed
|
||||
* @param testUnblockedForOtherReasons whether to test if transmissions are unblocked for
|
||||
* all the reasons other than the one provided
|
||||
*/
|
||||
void CheckBlockedLink(Ptr<WifiMac> mac,
|
||||
Mac48Address dest,
|
||||
uint8_t linkId,
|
||||
WifiQueueBlockedReason reason,
|
||||
bool blocked,
|
||||
std::string description,
|
||||
bool testUnblockedForOtherReasons = true);
|
||||
|
||||
/**
|
||||
* Check whether the MediumSyncDelay timer is running on the given link of the given device.
|
||||
*
|
||||
* @param staMac the MAC of the given device
|
||||
* @param linkId the ID of the given link
|
||||
* @param isRunning whether the MediumSyncDelay timer is running
|
||||
* @param msg message to print in case the check failed
|
||||
*/
|
||||
void CheckMsdTimerRunning(Ptr<StaWifiMac> staMac,
|
||||
uint8_t linkId,
|
||||
bool isRunning,
|
||||
const std::string& msg);
|
||||
|
||||
/**
|
||||
* Check whether aux PHYs of the given device are in sleep mode/awake.
|
||||
*
|
||||
* @param staMac the MAC of the given device
|
||||
* @param sleep whether aux PHYs should be in sleep mode
|
||||
*/
|
||||
void CheckAuxPhysSleepMode(Ptr<StaWifiMac> staMac, bool sleep);
|
||||
|
||||
/**
|
||||
* Callback connected to the EMLSR Manager MainPhySwitch trace source.
|
||||
*
|
||||
* @param index the index of the EMLSR client whose main PHY switch event is logged
|
||||
* @param info the information associated with the main PHY switch event
|
||||
*/
|
||||
virtual void MainPhySwitchInfoCallback(std::size_t index, const EmlsrMainPhySwitchTrace& info);
|
||||
|
||||
/**
|
||||
* Check information provided by the EMLSR Manager MainPhySwitch trace.
|
||||
*
|
||||
* @param index the ID of the EMLSR client this check refers to
|
||||
* @param reason the reason for main PHY to switch
|
||||
* @param fromLinkId the ID of the link the main PHY is moving from (if any)
|
||||
* @param toLinkId the ID of the link the main PHY is moving to
|
||||
* @param checkFromLinkId whether to check the given fromLinkId value
|
||||
* @param checkToLinkId whether to check the given toLinkId value
|
||||
*/
|
||||
void CheckMainPhyTraceInfo(std::size_t index,
|
||||
std::string_view reason,
|
||||
const std::optional<uint8_t>& fromLinkId,
|
||||
uint8_t toLinkId,
|
||||
bool checkFromLinkId = true,
|
||||
bool checkToLinkId = true);
|
||||
|
||||
void DoSetup() override;
|
||||
|
||||
/// Information about transmitted frames
|
||||
struct FrameInfo
|
||||
{
|
||||
Time startTx; ///< TX start time
|
||||
WifiConstPsduMap psduMap; ///< transmitted PSDU map
|
||||
WifiTxVector txVector; ///< TXVECTOR
|
||||
uint8_t linkId; ///< link ID
|
||||
uint8_t phyId; ///< ID of the transmitting PHY
|
||||
};
|
||||
|
||||
/// array of strings defining the channels for the MLD links
|
||||
std::array<std::string, 3> m_channelsStr{"{2, 0, BAND_2_4GHZ, 0}"s,
|
||||
"{36, 0, BAND_5GHZ, 0}"s,
|
||||
"{1, 0, BAND_6GHZ, 0}"s};
|
||||
|
||||
/// array of frequency ranges for MLD links
|
||||
const std::array<FrequencyRange, 3> m_freqRanges{WIFI_SPECTRUM_2_4_GHZ,
|
||||
WIFI_SPECTRUM_5_GHZ,
|
||||
WIFI_SPECTRUM_6_GHZ};
|
||||
|
||||
uint32_t m_rngSeed{1}; //!< RNG seed value
|
||||
uint64_t m_rngRun{1}; //!< RNG run value
|
||||
int64_t m_streamNo{5}; //!< RNG stream number
|
||||
uint8_t m_mainPhyId{0}; //!< ID of the main PHY
|
||||
std::set<uint8_t> m_linksToEnableEmlsrOn; /**< IDs of the links on which EMLSR mode has to
|
||||
be enabled */
|
||||
std::size_t m_nPhysPerEmlsrDevice{3}; //!< number of PHYs per EMLSR client
|
||||
std::size_t m_nEmlsrStations{1}; ///< number of stations to create that activate EMLSR
|
||||
std::size_t m_nNonEmlsrStations{0}; /**< number of stations to create that do not
|
||||
activate EMLSR */
|
||||
Time m_transitionTimeout{MicroSeconds(128)}; ///< Transition Timeout advertised by the AP MLD
|
||||
std::vector<Time> m_paddingDelay{
|
||||
{MicroSeconds(32)}}; ///< Padding Delay advertised by the non-AP MLD
|
||||
std::vector<Time> m_transitionDelay{
|
||||
{MicroSeconds(16)}}; ///< Transition Delay advertised by the non-AP MLD
|
||||
std::vector<uint8_t> m_establishBaDl{}; /**< the TIDs for which BA needs to be established
|
||||
with the AP as originator */
|
||||
std::vector<uint8_t> m_establishBaUl{}; /**< the TIDs for which BA needs to be established
|
||||
with the AP as recipient */
|
||||
bool m_putAuxPhyToSleep{false}; //!< whether aux PHYs are put to sleep during DL/UL TXOPs
|
||||
std::vector<FrameInfo> m_txPsdus; ///< transmitted PSDUs
|
||||
Ptr<ApWifiMac> m_apMac; ///< AP wifi MAC
|
||||
std::vector<Ptr<StaWifiMac>> m_staMacs; ///< MACs of the non-AP MLDs
|
||||
std::vector<PacketSocketAddress> m_dlSockets; ///< packet socket address for DL traffic
|
||||
std::vector<PacketSocketAddress> m_ulSockets; ///< packet socket address for UL traffic
|
||||
uint16_t m_startAid{1}; ///< first AID to allocate to stations
|
||||
uint16_t m_lastAid{0}; ///< AID of last associated station
|
||||
Time m_duration{0}; ///< simulation duration
|
||||
std::map<std::size_t, std::shared_ptr<EmlsrMainPhySwitchTrace>>
|
||||
m_traceInfo; ///< EMLSR client ID-indexed map of trace info from last main PHY switch
|
||||
|
||||
private:
|
||||
/**
|
||||
* Callback connected to the ApWifiMac's AssociatedSta trace source.
|
||||
* Start generating traffic (if needed) when all stations are associated.
|
||||
*
|
||||
* @param aid the AID assigned to the previous associated STA
|
||||
*/
|
||||
void StaAssociated(uint16_t aid, Mac48Address /* addr */);
|
||||
|
||||
/**
|
||||
* Callback connected to the QosTxop's BaEstablished trace source of the AP's BE AC.
|
||||
*
|
||||
* @param recipient the recipient of the established Block Ack agreement
|
||||
* @param tid the TID
|
||||
*/
|
||||
void BaEstablishedDl(Mac48Address recipient,
|
||||
uint8_t tid,
|
||||
std::optional<Mac48Address> /* gcrGroup */);
|
||||
|
||||
/**
|
||||
* Callback connected to the QosTxop's BaEstablished trace source of a STA's BE AC.
|
||||
*
|
||||
* @param index the index of the STA which the callback is connected to
|
||||
* @param recipient the recipient of the established Block Ack agreement
|
||||
* @param tid the TID
|
||||
*/
|
||||
void BaEstablishedUl(std::size_t index,
|
||||
Mac48Address recipient,
|
||||
uint8_t tid,
|
||||
std::optional<Mac48Address> /* gcrGroup */);
|
||||
|
||||
/**
|
||||
* Set the SSID on the next station that needs to start the association procedure, or start
|
||||
* traffic if no other station left.
|
||||
*
|
||||
* @param count the number of STAs that completed the association procedure
|
||||
*/
|
||||
void SetSsid(std::size_t count);
|
||||
|
||||
/**
|
||||
* Start the generation of traffic (needs to be overridden)
|
||||
*/
|
||||
virtual void StartTraffic()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* WIFI_EMLSR_TEST_H */
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user