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