wifi: Split EMLSR test in multiple test suites

This commit is contained in:
Stefano Avallone
2025-03-19 16:52:47 +01:00
parent 6ff7c27987
commit 4fcb82a119
11 changed files with 8180 additions and 8001 deletions

View File

@@ -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

File diff suppressed because it is too large Load Diff

View 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 */

View 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

View 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 */

File diff suppressed because it is too large Load Diff

View 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 */

View 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);
}
}

View 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