11#include "ns3/attribute-container.h"
12#include "ns3/boolean.h"
13#include "ns3/config.h"
14#include "ns3/ctrl-headers.h"
15#include "ns3/eht-configuration.h"
16#include "ns3/eht-frame-exchange-manager.h"
17#include "ns3/emlsr-manager.h"
19#include "ns3/mgt-action-headers.h"
20#include "ns3/mobility-helper.h"
21#include "ns3/multi-model-spectrum-channel.h"
22#include "ns3/node-list.h"
23#include "ns3/packet-socket-helper.h"
24#include "ns3/packet-socket-server.h"
25#include "ns3/qos-txop.h"
26#include "ns3/rng-seed-manager.h"
27#include "ns3/rr-multi-user-scheduler.h"
28#include "ns3/simulator.h"
29#include "ns3/spectrum-wifi-helper.h"
30#include "ns3/spectrum-wifi-phy.h"
31#include "ns3/string.h"
32#include "ns3/wifi-net-device.h"
44 "Check serialization and deserialization of the EML Operating Mode Notification frame")
66 "Unexpected link bitmap");
83 "Unexpected EMLSR Padding Delay");
87 "Unexpected EMLSR Transition Delay");
102 auto linkId = mac->GetLinkForPhy(phyId);
109 for (
const auto& [aid, psdu] : psduMap)
111 std::stringstream ss;
112 ss << std::setprecision(10) <<
"PSDU #" <<
m_txPsdus.size() <<
" Link ID "
113 << +linkId.value() <<
" Phy ID " << +phyId <<
" " << psdu->GetHeader(0).GetTypeString();
114 if (psdu->GetHeader(0).IsAction())
118 psdu->GetPayload(0)->PeekHeader(actionHdr);
121 ss <<
" #MPDUs " << psdu->GetNMpdus() <<
" duration/ID " << psdu->GetHeader(0).GetDuration()
122 <<
" RA = " << psdu->GetAddr1() <<
" TA = " << psdu->GetAddr2()
123 <<
" ADDR3 = " << psdu->GetHeader(0).GetAddr3()
124 <<
" ToDS = " << psdu->GetHeader(0).IsToDs()
125 <<
" FromDS = " << psdu->GetHeader(0).IsFromDs();
126 if (psdu->GetHeader(0).IsQosData())
131 ss << mpdu->GetHeader().GetSequenceNumber() <<
",";
133 ss <<
"} TID = " << +psdu->GetHeader(0).GetQosTid();
141 staMac && staMac->IsEmlsrLink(*linkId) &&
142 staMac->GetEmlsrManager()->GetMediumSyncDuration().IsStrictlyPositive())
144 const auto mustStartMsd =
145 staMac->GetEmlsrManager()->GetInDeviceInterference() &&
148 for (
auto id : staMac->GetLinkIds())
152 if (!staMac->IsEmlsrLink(
id) ||
id == *linkId || staMac->GetWifiPhy(
id) ==
nullptr)
157 txDuration - TimeStep(1),
158 [=, hdrType = psdu->GetHeader(0).GetTypeString(),
this]() {
160 bool msdWasRunning = staMac->GetEmlsrManager()
161 ->GetElapsedMediumSyncDelayTimer(id)
163 if (auto phy = staMac->GetWifiPhy(id);
164 !msdWasRunning && !mustStartMsd && phy && phy->IsStateSleep())
176 (msdWasRunning || mustStartMsd),
177 std::string(
"after transmitting ") + hdrType +
178 " on link " + std::to_string(*linkId));
184 NS_LOG_INFO(
"TX duration = " << txDuration.As(
Time::MS) <<
" TXVECTOR = " << txVector <<
"\n");
191 const std::string& msg)
193 auto time = staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(linkId);
197 <<
" Unexpected status for MediumSyncDelay timer on link " << +linkId
199 if (
auto phy = staMac->GetWifiPhy(linkId))
201 auto currThreshold = phy->GetCcaEdThreshold();
203 staMac->GetEmlsrManager()->GetMediumSyncOfdmEdThreshold()),
206 <<
" Unexpected value (" << currThreshold
207 <<
") for CCA ED threshold on link " << +linkId <<
" " << msg);
220 for (
const auto& phy : staMac->GetDevice()->GetPhys())
227 auto linkId = staMac->GetLinkForPhy(phy);
229 if (linkId.has_value() && !staMac->IsEmlsrLink(*linkId))
239 <<
" PHY " << +phy->GetPhyId() <<
" is in unexpected state "
240 << phy->GetState()->GetState());
246 (phy->IsStateTx() || phy->IsStateSwitching()) ? phy->GetDelayUntilIdle() :
Time{0};
251 "PHY " << +phy->GetPhyId() <<
" is in unexpected state "
252 << phy->GetState()->GetState());
266 std::string_view reason,
267 const std::optional<uint8_t>& fromLinkId,
269 bool checkFromLinkId,
274 const auto& traceInfo = traceInfoIt->second;
281 fromLinkId.has_value(),
282 "Unexpected stored from_link ID");
283 if (fromLinkId.has_value())
287 "Unexpected from_link ID");
304 int64_t streamNumber = 100;
316 wifi.SetRemoteStationManager(
"ns3::ConstantRateWifiManager",
321 wifi.ConfigEhtOptions(
"EmlsrActivated",
330 phyHelper.
Set(0,
"ChannelSettings",
StringValue(
"{2, 0, BAND_2_4GHZ, 0}"));
331 phyHelper.
Set(1,
"ChannelSettings",
StringValue(
"{36, 0, BAND_5GHZ, 0}"));
332 phyHelper.
Set(2,
"ChannelSettings",
StringValue(
"{1, 0, BAND_6GHZ, 0}"));
339 mac.SetType(
"ns3::ApWifiMac",
344 mac.SetApEmlsrManager(
"ns3::AdvancedApEmlsrManager",
345 "WaitTransDelayOnPsduRxError",
350 mac.SetType(
"ns3::StaWifiMac",
357 mac.SetEmlsrManager(
"ns3::AdvancedEmlsrManager",
367 for (
uint32_t i = 0; i < staDevices.GetN(); i++)
371 auto emlsrManager = staMac->GetEmlsrManager();
376 emlsrManager->TraceConnectWithoutContext(
384 wifi.ConfigEhtOptions(
"EmlsrActivated",
BooleanValue(
false));
386 staDevices.Add(wifi.Install(phyHelper, mac, otherStaNodes));
387 wifiStaNodes.Add(otherStaNodes);
390 for (
uint32_t i = 0; i < staDevices.GetN(); i++)
397 for (uint8_t phyId = 0; phyId <
m_apMac->GetDevice()->GetNPhys(); phyId++)
400 "/NodeList/0/DeviceList/*/$ns3::WifiNetDevice/Phys/" + std::to_string(phyId) +
406 for (uint8_t phyId = 0; phyId <
m_staMacs[i]->GetDevice()->GetNPhys(); phyId++)
409 "/NodeList/" + std::to_string(i + 1) +
"/DeviceList/*/$ns3::WifiNetDevice/Phys/" +
410 std::to_string(phyId) +
"/PhyTxPsduBegin",
429 positionAlloc->Add(Vector(std::min<double>(
id, 1), 0.0, 0.0));
431 mobility.SetPositionAllocator(positionAlloc);
433 mobility.SetMobilityModel(
"ns3::ConstantPositionMobilityModel");
434 mobility.Install(wifiApNode);
435 mobility.Install(wifiStaNodes);
439 packetSocket.
Install(wifiApNode);
440 packetSocket.
Install(wifiStaNodes);
452 server->SetLocal(srvAddr);
453 (*nodeIt)->AddApplication(server);
454 server->SetStartTime(
Seconds(0));
463 m_dlSockets.back().SetPhysicalAddress(staMac->GetDevice()->GetAddress());
467 m_ulSockets.back().SetSingleDevice(staMac->GetDevice()->GetIfIndex());
473 m_apMac->TraceConnectWithoutContext(
"AssociatedSta",
489 client->SetStartTime(
Seconds(0));
512 m_apMac->GetDevice()->GetNode()->AddApplication(
523 m_staMacs[aid - 1]->GetDevice()->GetNode()->AddApplication(
550 std::string description,
551 bool testUnblockedForOtherReasons)
554 auto mask = mac->GetMacQueueScheduler()->GetQueueLinkMask(
AC_BE, queueId, linkId);
557 description <<
": Expected to find a mask for EMLSR link " << +linkId);
562 description <<
": Expected EMLSR link " << +linkId
563 <<
" to be blocked for reason " << reason);
564 if (testUnblockedForOtherReasons)
568 description <<
": Expected EMLSR link " << +linkId
569 <<
" to be blocked for one reason only");
572 else if (testUnblockedForOtherReasons)
576 description <<
": Expected EMLSR link " << +linkId
577 <<
" to be unblocked");
583 description <<
": Expected EMLSR link " << +linkId
584 <<
" to be unblocked for reason " << reason);
589 Time transitionTimeout)
591 m_checkEmlsrLinksCount(0),
592 m_emlNotificationDroppedCount(0)
607 for (std::size_t linkId = 0; linkId <
m_apMac->GetNLinks(); linkId++)
612 m_staMacs[0]->TraceConnectWithoutContext(
"AckedMpdu",
614 m_staMacs[0]->TraceConnectWithoutContext(
"DroppedMpdu",
628 auto psdu = psduMap.begin()->second;
630 switch (psdu->GetHeader(0).GetType())
644 action.protectedEhtAction ==
650 m_staMacs[0]->GetLinkIdByAddress(psdu->GetAddr2()) == linkId)
653 m_uidList.push_front(psdu->GetPacket()->GetUid());
669 mpdu->GetPacket()->PeekHeader(frame);
676 "Multi-Link Element in AssocReq must have EML Capabilities");
679 "EML Support subfield of EML Capabilities in AssocReq must be set to 1");
682 "Unexpected Padding Delay in EML Capabilities included in AssocReq");
685 "Unexpected Transition Delay in EML Capabilities included in AssocReq");
693 bool sentToEmlsrClient =
694 (
m_staMacs[0]->GetLinkIdByAddress(mpdu->GetHeader().GetAddr1()) == linkId);
696 if (!sentToEmlsrClient)
703 mpdu->GetPacket()->PeekHeader(frame);
710 "Multi-Link Element in AssocResp must have EML Capabilities");
713 "EML Support subfield of EML Capabilities in AssocResp must be set to 1");
715 mle->GetTransitionTimeout(),
717 "Unexpected Transition Timeout in EML Capabilities included in AssocResp");
726 auto mpdu = *psdu->begin();
727 auto pkt = mpdu->GetPacket()->Copy();
729 pkt->RemoveHeader(frame);
732 bool sentbyNonApMld =
m_staMacs[0]->GetLinkIdByAddress(mpdu->GetHeader().GetAddr2()) == linkId;
736 "EMLSR Mode subfield should be set to 1 (frame sent by non-AP MLD: "
737 << std::boolalpha << sentbyNonApMld <<
")");
741 "EMLMR Mode subfield should be set to 0 (frame sent by non-AP MLD: "
742 << std::boolalpha << sentbyNonApMld <<
")");
746 "Link Bitmap subfield should be present (frame sent by non-AP MLD: "
747 << std::boolalpha << sentbyNonApMld <<
")");
749 auto setupLinks =
m_staMacs[0]->GetSetupLinkIds();
750 std::list<uint8_t> expectedEmlsrLinks;
751 std::set_intersection(setupLinks.begin(),
755 std::back_inserter(expectedEmlsrLinks));
759 "Unexpected Link Bitmap subfield (frame sent by non-AP MLD: "
760 << std::boolalpha << sentbyNonApMld <<
")");
768 "EMLSR Parameter Update Control should be set to 0 in frames sent by the AP MLD");
773 m_staMacs[0]->GetWifiPhy(linkId)->GetPhyBand()) +
780 "EML Notification received on unexpected link (frame sent by non-AP MLD: "
781 << std::boolalpha << sentbyNonApMld <<
")");
787 const auto& hdr = mpdu->GetHeader();
789 if (hdr.IsMgt() && hdr.IsAction())
793 action.protectedEhtAction ==
808 const auto& hdr = mpdu->GetHeader();
810 if (hdr.IsMgt() && hdr.IsAction())
814 action.protectedEhtAction ==
829 auto setupLinks =
m_staMacs[0]->GetSetupLinkIds();
830 std::set<uint8_t> expectedEmlsrLinks;
831 std::set_intersection(setupLinks.begin(),
835 std::inserter(expectedEmlsrLinks, expectedEmlsrLinks.end()));
839 "Unexpected set of EMLSR links)");
850 "Unexpected number of times CheckEmlsrLinks() is called");
854 "Unexpected number of times the EML Notification frame is dropped due to max retry limit");
861 std::to_string(params.nEmlsrStations) +
"," +
862 std::to_string(params.nNonEmlsrStations) +
")"),
863 m_emlsrLinks(params.linksToEnableEmlsrOn),
864 m_emlsrEnabledTime(0),
881 "This test requires at least two links to be configured as EMLSR links");
894 auto psdu = psduMap.begin()->second;
895 auto nodeId = mac->GetDevice()->GetNode()->GetId();
897 switch (psdu->GetHeader(0).GetType())
900 NS_ASSERT_MSG(nodeId > 0,
"APs do not send AssocReq frames");
907 for (
const auto id :
m_staMacs.at(nodeId - 1)->GetLinkIds())
911 m_staMacs[nodeId - 1]->SetPowerSaveMode({
true,
id});
921 (action.protectedEhtAction ==
950 const auto txDuration =
953 apMac->GetDevice()->GetPhy(phyId)->GetPhyBand());
988 for (std::size_t linkId = 0; linkId <
m_apMac->GetNLinks(); linkId++)
1000 m_apMac->AggregateObject(muScheduler);
1001 for (uint8_t linkId = 0; linkId <
m_apMac->GetNLinks(); linkId++)
1003 m_apMac->GetFrameExchangeManager(linkId)->GetAckManager()->SetAttribute(
1004 "DlMuAckSequenceType",
1047 m_staMacs.at(
id)->GetEmlsrManager()->SetAttribute(
1053 m_apMac->GetDevice()->GetNode()->AddApplication(
1086 auto jumpToQosDataOrMuRts = [&]() {
1088 !psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData())
1090 auto psdu = psduIt->psduMap.cbegin()->second;
1091 if (psdu->GetHeader(0).IsTrigger())
1094 psdu->GetPayload(0)->PeekHeader(trigger);
1158 std::set<uint8_t> linkIds;
1160 jumpToQosDataOrMuRts();
1162 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData()),
1164 "Expected at least one QoS data frame before enabling EMLSR mode");
1165 linkIds.insert(psduIt->linkId);
1166 const auto firstAmpduTxEnd =
1170 m_staMacs[i]->GetWifiPhy(psduIt->linkId)->GetPhyBand());
1173 jumpToQosDataOrMuRts();
1175 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData()),
1177 "Expected at least two QoS data frames before enabling EMLSR mode");
1178 linkIds.insert(psduIt->linkId);
1179 const auto secondAmpduTxStart = psduIt->startTx;
1187 auto setupLinks =
m_staMacs[i]->GetSetupLinkIds();
1189 std::none_of(setupLinks.begin(), setupLinks.end(), [&](
auto&& linkId) {
1190 return linkId != m_mainPhyId && m_emlsrLinks.count(linkId) == 0;
1195 "Expected both A-MPDUs to be sent on the same link");
1199 "A-MPDUs are not sent one after another");
1209 "Expected A-MPDUs to be sent on distinct links");
1212 "A-MPDUs are not sent concurrently");
1278 using FrameExchange = std::list<
decltype(psduIt)>;
1285 jumpToQosDataOrMuRts();
1294 psduIt->psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
1299 "jumpToQosDataOrMuRts does not return TFs other than MU-RTS");
1300 for (
const auto& userInfo : trigger)
1304 if (
m_staMacs.at(i)->GetAssociationId() == userInfo.GetAid12())
1306 frameExchanges.at(i).emplace_back(FrameExchange{psduIt});
1318 for (
const auto& staIdPsduPair : psduIt->psduMap)
1321 if (!staMac->GetLinkIdByAddress(staIdPsduPair.second->GetAddr1()))
1329 std::size_t
id = staMac->GetDevice()->GetNode()->GetId() - 1;
1330 for (
auto& frameExchange : frameExchanges.at(
id))
1332 if (
IsTrigger(frameExchange.front()->psduMap) &&
1333 frameExchange.front()->linkId == psduIt->linkId &&
1334 frameExchange.size() == 1)
1336 auto it = std::next(frameExchange.front());
1340 if (it->linkId == psduIt->linkId &&
1341 !it->psduMap.begin()->second->GetHeader(0).IsCts())
1350 frameExchange.emplace_back(psduIt);
1355 frameExchanges.at(
id).emplace_back(FrameExchange{psduIt});
1367 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1371 "Expected at least 2 frame exchange sequences "
1372 <<
"involving EMLSR client " << i);
1374 auto firstExchangeIt = frameExchanges.at(i).begin();
1375 auto secondExchangeIt = std::next(firstExchangeIt);
1377 const auto firstAmpduTxEnd =
1378 firstExchangeIt->back()->startTx +
1380 firstExchangeIt->back()->psduMap,
1381 firstExchangeIt->back()->txVector,
1382 m_staMacs[i]->GetWifiPhy(firstExchangeIt->back()->linkId)->GetPhyBand());
1383 const auto secondAmpduTxStart = secondExchangeIt->front()->startTx;
1385 if (m_staMacs[i]->GetNLinks() == m_emlsrLinks.size())
1390 "Expected an MU-RTS TF as ICF of first frame exchange sequence");
1392 firstExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1394 "Expected a QoS data frame in the first frame exchange sequence");
1398 "Expected an MU-RTS TF as ICF of second frame exchange sequence");
1400 secondExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1402 "Expected a QoS data frame in the second frame exchange sequence");
1406 "A-MPDUs are not sent one after another");
1410 std::vector<uint8_t> nonEmlsrIds;
1411 auto setupLinks = m_staMacs[i]->GetSetupLinkIds();
1412 std::set_difference(setupLinks.begin(),
1414 m_emlsrLinks.begin(),
1416 std::back_inserter(nonEmlsrIds));
1419 auto nonEmlsrLinkExchangeIt = firstExchangeIt->front()->linkId == nonEmlsrIds[0]
1424 "Did not expect an MU-RTS TF as ICF on non-EMLSR link");
1426 nonEmlsrLinkExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1428 "Expected a QoS data frame on the non-EMLSR link");
1430 auto emlsrLinkExchangeIt =
1431 nonEmlsrLinkExchangeIt == firstExchangeIt ? secondExchangeIt : firstExchangeIt;
1434 "Expected this exchange not to occur on non-EMLSR link");
1437 "Expected an MU-RTS TF as ICF on the EMLSR link");
1439 emlsrLinkExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1441 "Expected a QoS data frame on the EMLSR link");
1445 "A-MPDUs are not sent concurrently");
1449 frameExchanges.at(i).erase(firstExchangeIt);
1450 frameExchanges.at(i).erase(secondExchangeIt);
1478 if (m_nEmlsrStations == 2 && m_apMac->GetNLinks() == m_emlsrLinks.size())
1481 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1485 "Expected at least 2 frame exchange sequences "
1486 <<
"involving EMLSR client " << i);
1488 auto firstExchangeIt = frameExchanges.at(i).begin();
1492 "Expected an MU-RTS TF as ICF of first frame exchange sequence");
1494 firstExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1496 "Expected a QoS data frame in the first frame exchange sequence");
1500 auto secondExchangeIt = std::next(frameExchanges.at(0).begin())->front()->startTx <
1501 std::next(frameExchanges.at(1).begin())->front()->startTx
1502 ? std::next(frameExchanges.at(0).begin())
1503 :
std::next(frameExchanges.at(1).begin());
1504 decltype(secondExchangeIt) thirdExchangeIt;
1505 std::size_t thirdExchangeStaId;
1507 if (secondExchangeIt == std::next(frameExchanges.at(0).begin()))
1509 thirdExchangeIt = std::next(frameExchanges.at(1).begin());
1510 thirdExchangeStaId = 1;
1514 thirdExchangeIt = std::next(frameExchanges.at(0).begin());
1515 thirdExchangeStaId = 0;
1522 "Expected no ICF for the second frame exchange sequence");
1524 secondExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1526 "Expected a QoS data frame in the second frame exchange sequence");
1530 +frameExchanges.at(0).begin()->front()->linkId,
1531 "Expected the first two frame exchanges to occur on the same link");
1533 auto bAckRespIt = std::prev(secondExchangeIt->front());
1536 "Expected a BlockAck response before the second frame exchange");
1537 auto bAckRespTxEnd =
1538 bAckRespIt->startTx +
1540 bAckRespIt->txVector,
1541 m_apMac->GetWifiPhy(bAckRespIt->linkId)->GetPhyBand());
1545 bAckRespTxEnd + m_apMac->GetWifiPhy(bAckRespIt->linkId)->GetSifs(),
1546 secondExchangeIt->front()->startTx,
1547 "Expected the second frame exchange to start a SIFS after the first one");
1552 "Expected an MU-RTS as ICF for the third frame exchange sequence");
1554 thirdExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1556 "Expected a QoS data frame in the third frame exchange sequence");
1559 +secondExchangeIt->front()->linkId,
1560 +thirdExchangeIt->front()->linkId,
1561 "Expected the second and third frame exchanges to occur on distinct links");
1563 auto secondQosIt = secondExchangeIt->front();
1564 auto secondQosTxEnd =
1565 secondQosIt->startTx +
1567 secondQosIt->txVector,
1568 m_apMac->GetWifiPhy(secondQosIt->linkId)->GetPhyBand());
1571 secondQosTxEnd + m_transitionDelay.at(thirdExchangeStaId),
1572 "Transmission started before transition delay");
1578 "Expected a fourth frame exchange");
1579 auto fourthExchangeIt = std::next(thirdExchangeIt);
1584 "Expected an MU-RTS as ICF for the fourth frame exchange sequence");
1586 bAckRespIt = std::prev(fourthExchangeIt->front());
1589 "Expected a BlockAck response before the fourth frame exchange");
1590 auto phy = m_apMac->GetWifiPhy(bAckRespIt->linkId);
1592 bAckRespIt->txVector,
1600 bAckRespTxEnd +
phy->GetPifs(),
1601 "Transmission started less than a PIFS after BlockAck");
1603 bAckRespTxEnd +
phy->GetPifs() +
1605 "Transmission started too much time after BlockAck");
1607 auto bAckReqIt = std::next(fourthExchangeIt->front(), 2);
1610 "Expected a BlockAck request in the fourth frame exchange");
1614 frameExchanges.at(0).pop_front();
1615 frameExchanges.at(0).pop_front();
1616 frameExchanges.at(1).pop_front();
1617 frameExchanges.at(1).pop_front();
1618 frameExchanges.at(thirdExchangeStaId).pop_front();
1680 for (std::size_t i = 0; i < m_nEmlsrStations; i++)
1685 auto exchangeIt = frameExchanges.at(i).cbegin();
1687 auto linkIdOpt = m_staMacs[i]->GetLinkForPhy(m_mainPhyId);
1690 "Didn't find a link on which the main PHY is operating");
1692 if (
IsTrigger(exchangeIt->front()->psduMap))
1696 "ICF was not sent on the expected link");
1699 "Expected no data frame in the first frame exchange sequence");
1700 frameExchanges.at(i).pop_front();
1705 "Expected at least 2 frame exchange sequences "
1706 <<
"involving EMLSR client " << i);
1708 auto firstExchangeIt = frameExchanges.at(i).cbegin();
1709 auto secondExchangeIt = std::next(firstExchangeIt);
1711 const auto firstAmpduTxEnd =
1712 firstExchangeIt->back()->startTx +
1714 firstExchangeIt->back()->psduMap,
1715 firstExchangeIt->back()->txVector,
1716 m_staMacs[i]->GetWifiPhy(firstExchangeIt->back()->linkId)->GetPhyBand());
1717 const auto secondAmpduTxStart = secondExchangeIt->front()->startTx;
1720 firstExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1722 "Expected a QoS data frame in the first frame exchange sequence");
1725 "Expected one frame only in the first frame exchange sequence");
1728 secondExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
1730 "Expected a QoS data frame in the second frame exchange sequence");
1733 "Expected one frame only in the second frame exchange sequence");
1735 if (m_staMacs[i]->GetNLinks() == m_emlsrLinks.size())
1740 +firstExchangeIt->front()->linkId,
1742 "First frame exchange expected to occur on link used to send EML OMN");
1745 +secondExchangeIt->front()->linkId,
1747 "Second frame exchange expected to occur on link used to send EML OMN");
1751 "A-MPDUs are not sent one after another");
1757 +secondExchangeIt->front()->linkId,
1758 "Frame exchanges expected to occur on distinct links");
1762 "A-MPDUs are not sent concurrently");
1770 std::optional<std::size_t> staId;
1773 if (
m_staMacs.at(
id)->GetLinkIdByAddress(address))
1784 for (uint8_t linkId = 0; linkId <
m_apMac->GetNLinks(); linkId++)
1786 bool psModeExpected =
1788 auto addr =
m_staMacs.at(*staId)->GetAddress();
1789 auto psMode =
m_apMac->GetWifiRemoteStationManager(linkId)->IsInPsMode(addr);
1792 "EMLSR link " << +linkId <<
" of EMLSR client " << *staId
1793 <<
" not in " << (psModeExpected ?
"PS" :
"active")
1799 WifiQueueBlockedReason::POWER_SAVE_MODE,
1801 "Checking PM mode after association on AP MLD for EMLSR client " +
1802 std::to_string(*staId),
1813 auto pkt = mpdu->GetPacket()->Copy();
1814 const auto& hdr = mpdu->GetHeader();
1817 pkt->RemoveHeader(frame);
1819 std::optional<std::size_t> staId;
1822 if (
m_staMacs.at(
id)->GetFrameExchangeManager(linkId)->GetAddress() == hdr.GetAddr1())
1830 "Not an address of an EMLSR client " << hdr.GetAddr1());
1833 auto phy =
m_apMac->GetWifiPhy(linkId);
1839 m_staMacs.at(*staId)->GetWifiRemoteStationManager(linkId)->GetAckTxVector(hdr.GetAddr2(),
1846 if (frame.m_emlControl.emlsrMode == 1)
1850 for (const auto linkId : m_emlsrLinks)
1852 auto addr = m_staMacs.at(*staId)->GetAddress();
1853 auto psMode = m_apMac->GetWifiRemoteStationManager(linkId)->IsInPsMode(addr);
1854 NS_TEST_EXPECT_MSG_EQ(psMode,
1856 "EMLSR link " << +linkId <<
" of EMLSR client " << *staId
1857 <<
" not in active mode");
1863 WifiQueueBlockedReason::POWER_SAVE_MODE,
1865 "Checking EMLSR links on AP MLD after EMLSR mode is enabled on EMLSR client " +
1866 std::to_string(*staId),
1875 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
1877 bool psModeExpected = id != linkId && m_emlsrLinks.count(id) == 1;
1878 auto addr = m_staMacs.at(*staId)->GetAddress();
1879 auto psMode = m_apMac->GetWifiRemoteStationManager(id)->IsInPsMode(addr);
1880 NS_TEST_EXPECT_MSG_EQ(psMode,
1883 << +id <<
" of EMLSR client " << *staId <<
" not in "
1884 << (psModeExpected ?
"PS" :
"active") <<
" mode");
1890 WifiQueueBlockedReason::POWER_SAVE_MODE,
1892 "Checking links on AP MLD after EMLSR mode is disabled on EMLSR client " +
1893 std::to_string(*staId),
1906 auto pkt = mpdu->GetPacket()->Copy();
1907 const auto& hdr = mpdu->GetHeader();
1910 pkt->RemoveHeader(frame);
1912 std::optional<std::size_t> staId;
1915 if (
m_staMacs.at(
id)->GetFrameExchangeManager(linkId)->GetAddress() == hdr.GetAddr2())
1923 "Not an address of an EMLSR client " << hdr.GetAddr1());
1925 auto phy =
m_staMacs.at(*staId)->GetWifiPhy(linkId);
1928 m_apMac->GetWifiRemoteStationManager(linkId)->GetAckTxVector(hdr.GetAddr2(), txVector);
1932 m_staMacs.at(*staId)->GetWifiRemoteStationManager(linkId)->GetRtsTxVector(
1943 auto timeToCfEnd = txDuration + phy->GetSifs() + ackDuration + phy->GetSifs() + cfEndDuration;
1948 for (uint8_t
id = 0;
id <
m_apMac->GetNLinks();
id++)
1953 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1954 id != linkId &&
m_staMacs.at(*staId)->IsEmlsrLink(
id),
1955 "Checking links on EMLSR client " + std::to_string(*staId) +
1956 " before the end of CF-End frame");
1960 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1961 id != linkId &&
m_staMacs.at(*staId)->IsEmlsrLink(
id),
1962 "Checking links of EMLSR client " + std::to_string(*staId) +
1963 " on the AP MLD before the end of CF-End frame");
1969 for (uint8_t
id = 0;
id <
m_apMac->GetNLinks();
id++)
1971 if (
m_staMacs.at(*staId)->IsEmlsrLink(
id))
1975 m_staMacs.at(*staId)->GetAddress(),
1976 id && m_staMacs.at(*staId)->IsEmlsrLink(id),
1977 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
1979 "Checking links of EMLSR client " + std::to_string(*staId) +
1980 " are all blocked on the AP MLD right after the end of CF-End");
1987 for (uint8_t
id = 0;
id <
m_apMac->GetNLinks();
id++)
1989 if (
m_staMacs.at(*staId)->IsEmlsrLink(
id))
1991 CheckBlockedLink(m_apMac,
1992 m_staMacs.at(*staId)->GetAddress(),
1994 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
1996 "Checking links of EMLSR client " + std::to_string(*staId) +
1997 " are all blocked on the AP MLD before the end of "
1998 "transition delay");
2004 for (uint8_t
id = 0;
id <
m_apMac->GetNLinks();
id++)
2006 if (
m_staMacs.at(*staId)->IsEmlsrLink(
id))
2008 CheckBlockedLink(m_apMac,
2009 m_staMacs.at(*staId)->GetAddress(),
2011 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2013 "Checking links of EMLSR client " + std::to_string(*staId) +
2014 " are all unblocked on the AP MLD after the transition delay");
2026 mpdu->GetPacket()->PeekHeader(trigger);
2034 "Did not expect an ICF before enabling EMLSR mode");
2038 "Unexpected preamble type for the Initial Control frame");
2042 "Unexpected rate for the Initial Control frame: " << rate);
2045 Time maxPaddingDelay{};
2047 for (
const auto& userInfo : trigger)
2049 auto addr =
m_apMac->GetMldOrLinkAddressByAid(userInfo.GetAid12());
2052 "AID " << userInfo.GetAid12() <<
" not found");
2054 if (
m_apMac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*addr))
2060 if (
m_staMacs.at(i)->GetAddress() == *addr)
2068 for (uint8_t
id = 0;
id <
m_apMac->GetNLinks();
id++)
2070 if (!
m_apMac->GetWifiRemoteStationManager(
id)->GetEmlsrEnabled(*addr))
2078 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2080 "Checking that AP blocked transmissions on all other EMLSR "
2081 "links after sending ICF to client with AID=" +
2082 std::to_string(userInfo.GetAid12()),
2092 m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2094 if (maxPaddingDelay.IsStrictlyPositive())
2100 pkt->AddHeader(trigger);
2101 auto txDurationWithout =
2104 m_apMac->GetWifiPhy(linkId)->GetPhyBand());
2107 txDurationWithout + maxPaddingDelay,
2108 "Unexpected TX duration of the MU-RTS TF with padding "
2114 for (
const auto& userInfo : trigger)
2118 if (
m_staMacs[i]->GetAssociationId() != userInfo.GetAid12())
2126 for (uint8_t
id = 0;
id <
m_staMacs[i]->GetNLinks();
id++)
2132 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2133 id != linkId &&
m_staMacs[i]->IsEmlsrLink(
id),
2134 "Checking EMLSR links on EMLSR client " + std::to_string(i) +
2135 " after receiving ICF");
2138 if (mainPhyLinkId != linkId)
2164 std::size_t firstClientId = 0;
2165 std::size_t secondClientId = 1;
2166 auto addr =
m_staMacs[secondClientId]->GetAddress();
2176 m_apMac->GetDevice()->GetNode()->AddApplication(
2179 for (std::size_t clientId : {firstClientId, secondClientId})
2182 for (uint8_t
id = 0;
id <
m_staMacs[clientId]->GetNLinks();
id++)
2188 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2190 "Checking EMLSR links on EMLSR client " +
2191 std::to_string(clientId) +
2192 " after receiving the first QoS data frame");
2199 m_apMac->GetDevice()->GetNode()->AddApplication(
2204 for (std::size_t clientId : {firstClientId, secondClientId})
2206 for (uint8_t
id = 0;
id <
m_staMacs[clientId]->GetNLinks();
id++)
2212 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2214 "Checking EMLSR links on EMLSR client " +
2215 std::to_string(clientId) +
2216 " when starting the reception of the second QoS frame");
2227 for (uint8_t
id = 0;
id <
m_apMac->GetNLinks();
id++)
2232 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2234 "Checking that links of EMLSR client " +
2235 std::to_string(secondClientId) +
2236 " are blocked on the AP MLD before the end of the PPDU");
2243 for (uint8_t
id = 0;
id <
m_staMacs[secondClientId]->GetNLinks();
id++)
2248 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2250 "Checking that links of EMLSR client " +
2251 std::to_string(secondClientId) +
2252 " are unblocked before the end of the second QoS frame");
2257 for (uint8_t
id = 0;
id <
m_apMac->GetNLinks();
id++)
2262 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2264 "Checking links of EMLSR client " +
2265 std::to_string(secondClientId) +
2266 " are all blocked on the AP MLD after the end of the PPDU");
2273 for (uint8_t
id = 0;
id <
m_apMac->GetNLinks();
id++)
2279 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2281 "Checking links of EMLSR client " + std::to_string(secondClientId) +
2282 " are all blocked on the AP MLD before the transition delay",
2296 m_apMac->GetDevice()->GetNode()->AddApplication(
2306 psduMap.cbegin()->second->GetAddr1(),
2308 "QoS frame not addressed to a non-EMLSR client");
2310 for (uint8_t
id = 0;
id <
m_apMac->GetNLinks();
id++)
2315 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2317 "Checking links of EMLSR client " + std::to_string(secondClientId) +
2318 " are all blocked on the AP MLD before the transition delay");
2323 for (uint8_t
id = 0;
id <
m_apMac->GetNLinks();
id++)
2327 m_apMac->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED, addr, {
id});
2333 for (uint8_t
id = 0;
id <
m_apMac->GetNLinks();
id++)
2335 m_apMac->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED, addr, {
id});
2340 for (uint8_t
id = 0;
id <
m_apMac->GetNLinks();
id++)
2345 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2347 "Checking EMLSR links on EMLSR client " +
2348 std::to_string(secondClientId) +
2349 " after receiving the fourth QoS data frame");
2374 auto taddr = psduMap.cbegin()->second->GetAddr2();
2375 std::size_t clientId;
2376 if (
m_staMacs[0]->GetLinkIdByAddress(taddr))
2384 "Unexpected TA for BlockAck: " << taddr);
2389 auto currMainPhyLinkId =
m_staMacs[clientId]->GetLinkForPhy(phyId);
2391 currMainPhyLinkId.has_value(),
2393 "Didn't find the link on which the PHY sending the BlockAck is operating");
2394 auto linkId = *currMainPhyLinkId;
2397 auto addr =
m_apMac->GetWifiRemoteStationManager(linkId)->GetMldAddress(taddr);
2400 auto apPhy =
m_apMac->GetWifiPhy(linkId);
2406 apPhy->GetPhyBand());
2419 for (uint8_t
id = 0;
id <
m_apMac->GetNLinks();
id++)
2424 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2426 "Checking links on EMLSR client " + std::to_string(clientId) +
2427 " at the end of fourth BlockAck");
2431 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2433 "Checking links of EMLSR client " + std::to_string(clientId) +
2434 " on the AP MLD at the end of fourth BlockAck");
2440 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
2442 CheckBlockedLink(m_staMacs[clientId],
2443 m_apMac->GetAddress(),
2445 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2447 "Checking links on EMLSR client " + std::to_string(clientId) +
2448 " a SIFS after the end of fourth BlockAck");
2449 CheckBlockedLink(m_apMac,
2452 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2454 "Checking links of EMLSR client " + std::to_string(clientId) +
2455 " a SIFS after the end of fourth BlockAck");
2460 auto uid = psduMap.cbegin()->second->GetPacket()->GetUid();
2468 for (uint8_t
id = 0;
id <
m_apMac->GetNLinks();
id++)
2473 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2475 "Checking links on EMLSR client " + std::to_string(clientId) +
2476 " at the end of fifth BlockAck");
2480 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2482 "Checking links of EMLSR client " + std::to_string(clientId) +
2483 " on the AP MLD at the end of fifth BlockAck");
2489 txDuration + apPhy->GetSifs() + cfEndTxDuration -
MicroSeconds(1),
2491 for (uint8_t
id = 0;
id <
m_apMac->GetNLinks();
id++)
2496 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2498 "Checking links on EMLSR client " + std::to_string(clientId) +
2499 " before the end of CF-End frame");
2503 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2505 "Checking links of EMLSR client " + std::to_string(clientId) +
2506 " on the AP MLD before the end of CF-End frame");
2512 txDuration + apPhy->GetSifs() + cfEndTxDuration +
MicroSeconds(1),
2514 for (uint8_t
id = 0;
id <
m_apMac->GetNLinks();
id++)
2520 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2522 "Checking links of EMLSR client " + std::to_string(clientId) +
2523 " are all blocked on the AP MLD right after the end of CF-End");
2529 txDuration + apPhy->GetSifs() + cfEndTxDuration +
m_transitionDelay.at(clientId) -
2532 for (uint8_t
id = 0;
id <
m_apMac->GetNLinks();
id++)
2538 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2540 "Checking links of EMLSR client " + std::to_string(clientId) +
2541 " are all blocked on the AP MLD before the end of transition delay");
2546 txDuration + apPhy->GetSifs() + cfEndTxDuration +
m_transitionDelay.at(clientId) +
2549 for (uint8_t
id = 0;
id <
m_apMac->GetNLinks();
id++)
2555 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
2557 "Checking links of EMLSR client " + std::to_string(clientId) +
2558 " are all unblocked on the AP MLD after the transition delay");
2578 std::to_string(params.genBackoffAndUseAuxPhyCca) +
2579 ", nSlotsLeftAlert=" +
std::to_string(params.nSlotsLeftAlert)),
2580 m_emlsrLinks(params.linksToEnableEmlsrOn),
2581 m_channelWidth(params.channelWidth),
2582 m_auxPhyChannelWidth(params.auxPhyChannelWidth),
2583 m_mediumSyncDuration(params.mediumSyncDuration),
2584 m_msdMaxNTxops(params.msdMaxNTxops),
2585 m_emlsrEnabledTime(0),
2586 m_firstUlPktsGenTime(0),
2588 m_checkBackoffStarted(false),
2589 m_countQoSframes(0),
2591 m_countRtsframes(0),
2592 m_genBackoffIfTxopWithoutTx(params.genBackoffAndUseAuxPhyCca),
2593 m_useAuxPhyCca(params.genBackoffAndUseAuxPhyCca),
2594 m_nSlotsLeftAlert(params.nSlotsLeftAlert),
2595 m_switchMainPhyBackDelayTimeout(params.switchMainPhyBackDelayTimeout),
2596 m_5thQosFrameExpWidth(0)
2612 "This test requires at least two links to be configured as EMLSR links");
2613 for (uint8_t
id = 0;
id < 3;
id++)
2665 for (
auto mac : std::initializer_list<Ptr<WifiMac>>{
m_apMac,
m_staMacs[0]})
2667 mac->GetWifiPhy(linkId)->SetOperatingChannel(
2682 NS_LOG_INFO(
"Backoff value " << backoff <<
" generated by EMLSR client on link " << +linkId
2698 "Another backoff value should not be generated while the main PHY link is blocked");
2702 "Backoff generated at unexpected time");
2718 m_staMacs[0]->GetChannelAccessManager(linkId)->GetSifs() +
2720 m_staMacs[0]->GetChannelAccessManager(linkId)->GetSlot();
2731 backoff *
m_staMacs[0]->GetChannelAccessManager(linkId)->GetSlot();
2746 auto psdu = psduMap.begin()->second;
2747 auto nodeId = mac->GetDevice()->GetNode()->GetId();
2749 switch (psdu->GetHeader(0).GetType())
2752 NS_ASSERT_MSG(nodeId > 0,
"APs do not send AssocReq frames");
2780 auto auxPhyLinks =
m_staMacs[0]->GetSetupLinkIds();
2786 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2798 std::set<uint8_t> linkIds;
2804 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2808 NS_LOG_INFO(
"Enqueuing two packets at the EMLSR client\n");
2813 m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2815 {*m_staMacs[0]->GetLinkForPhy(m_mainPhyId)});
2843 NS_LOG_INFO(
"Enqueuing two packets at the EMLSR client\n");
2844 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(
2849 m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2859 auto macHdrSize = (*psduMap.at(
SU_STA_ID)->begin())->GetHeader().GetSerializedSize() +
2864 for (
auto id :
m_staMacs[0]->GetLinkIds())
2870 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2872 "Checking EMLSR links on EMLSR client while sending the first data frame",
2879 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2882 "Checking EMLSR links on AP MLD right after receiving the MAC "
2883 "header of the first data frame");
2893 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2896 "Checking EMLSR links on AP MLD after sending the first data frame");
2912 auto auxPhyLinks =
m_staMacs[0]->GetSetupLinkIds();
2918 m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2923 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2925 {*m_staMacs[0]->GetLinkForPhy(m_mainPhyId)});
2929 NS_LOG_INFO(
"Enqueuing two packets at the EMLSR client\n");
2930 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(
2937 for (
auto id :
m_staMacs[0]->GetLinkIds())
2943 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2944 id != linkId &&
m_staMacs[0]->IsEmlsrLink(
id),
2945 "Checking EMLSR links on EMLSR client while sending the second data frame",
2952 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
2953 id != linkId &&
m_staMacs[0]->IsEmlsrLink(
id),
2954 "Checking EMLSR links on AP MLD while sending the second data frame",
2959 m_staMacs[0]->GetMacQueueScheduler()->UnblockQueues(
2960 WifiQueueBlockedReason::TID_NOT_MAPPED,
2979 auto auxPhyLinks =
m_staMacs[0]->GetSetupLinkIds();
3012 traceInfoIt->second->GetName() ==
"TxopEnded")
3014 const auto& traceInfo =
3019 "Expected null remaining time because TXOP ended regularly");
3051 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
3074 elapsed.has_value(),
3076 "MediumSyncDelay timer not running on link where main PHY is operating");
3078 m_staMacs[0]->GetEmlsrManager()->GetMediumSyncDuration() -
3087 "Backoff end time should have been calculated");
3095 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
3101 NS_LOG_INFO(
"Enqueuing two packets at the EMLSR client\n");
3112 m_staMacs[0]->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
3121 m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
3126 NS_LOG_INFO(
"Enqueuing two packets at the EMLSR client\n");
3131 m_staMacs[0]->GetEmlsrManager()->SetAuxPhyTxCapable(
false);
3151 "Main PHY is not switching at time "
3157 auto mainPhyLinkid = m_staMacs[0]->GetLinkForPhy(mainPhy);
3160 NS_TEST_EXPECT_MSG_EQ(+mainPhyLinkid.value(),
3162 "Main PHY expected to operate on the preferred link");
3166 NS_TEST_EXPECT_MSG_EQ(
3167 mainPhy->IsStateSwitching(),
3169 "Main PHY is not operating on a link and it is not switching at time "
3170 << Simulator::Now().As(Time::NS));
3178 for (uint8_t
id = 0;
id <
m_staMacs[0]->GetNLinks();
id++)
3186 m_staMacs[0]->GetChannelAccessManager(
id)->GetBackoffEndFor(acBe);
3189 minBackoff = backoff;
3190 slot =
m_staMacs[0]->GetWifiPhy(
id)->GetSlot();
3199 auto expected2ndSwitchDelay =
3210 "Main PHY is not switching at time "
3214 "Main PHY should not be operating on a link because it "
3215 "should be switching to an auxiliary link");
3218 "UlTxopAuxPhyNotTxCapable",
3224 const auto delayUntilIdle = mainPhy->GetDelayUntilIdle();
3225 auto startTimerDelay = delayUntilIdle;
3230 m_staMacs[0]->GetEmlsrManager()->GetAttribute(
"SwitchMainPhyBackDelay",
3231 switchMainPhyBackDelay);
3243 auto endCcaNavCheckDelay = delayUntilIdle;
3245 for (uint8_t
id = 0;
id <
m_staMacs[0]->GetNLinks(); ++id)
3247 if (
auto phy =
m_staMacs[0]->GetWifiPhy(
id))
3251 endCcaNavCheckDelay =
3252 Max(endCcaNavCheckDelay, delayUntilIdle + phy->GetPifs());
3255 m_staMacs[0]->GetChannelAccessManager(
id)->NotifyNavStartNow(
3256 endCcaNavCheckDelay + TimeStep(1));
3259 startTimerDelay = endCcaNavCheckDelay;
3267 auto auxLinkId =
m_staMacs[0]->GetLinkForPhy(mainPhy);
3269 auxLinkId.has_value(),
3271 "Main PHY should be operating on a link before timer expires");
3272 auto timerDuration = switchMainPhyBackDelay.
Get();
3276 ->GetChannelAccessManager(*auxLinkId)
3277 ->GetBackoffEndFor(acBe) -
3281 ->GetChannelAccessManager(*auxLinkId)
3282 ->NotifyNavStartNow(timerDuration +
3283 mainPhy->GetChannelSwitchDelay());
3289 "TxopNotGainedOnAuxPhyLink",
3299 auto auxLinkId = m_staMacs[0]->GetLinkForPhy(mainPhy);
3300 NS_TEST_ASSERT_MSG_EQ(auxLinkId.has_value(),
3302 "Main PHY should have completed switching");
3304 auto cam = m_staMacs[0]->GetChannelAccessManager(*auxLinkId);
3305 cam->NeedBackoffUponAccess(acBe, true, true);
3306 const auto usedAuxPhyCca =
3307 (m_useAuxPhyCca || m_auxPhyChannelWidth >= m_channelWidth) &&
3308 (m_nSlotsLeftAlert == 0 ||
3309 cam->GetBackoffEndFor(acBe) <= Simulator::Now());
3310 m_5thQosFrameExpWidth =
3311 usedAuxPhyCca ? m_auxPhyChannelWidth : m_channelWidth;
3316 if (auto slots = acBe->GetBackoffSlots(*auxLinkId); slots == 0)
3318 m_5thQosFrameTxTime =
3319 Simulator::Now() + (m_useAuxPhyCca ? Time{0} : mainPhy->GetPifs());
3323 m_5thQosFrameTxTime = cam->GetBackoffEndFor(acBe);
3331 NS_LOG_INFO(
"Enqueuing two packets at the EMLSR client\n");
3332 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(
UPLINK, 0, 2, 1000));
3368 "RTS sent by main PHY on an unexpected width");
3388 m_apMac->GetWifiPhy(linkId)->GetPhyBand());
3392 mpdu->GetHeader().GetAddr1() ==
m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress())
3397 const auto auxPhy =
m_staMacs[0]->GetWifiPhy(linkId);
3403 "Expecting the main PHY to be switching link");
3406 "Aux PHY on link " << +linkId <<
" already in sleep mode");
3424 traceInfoIt->second->GetName() ==
"TxopEnded")
3426 const auto& traceInfo =
3430 "Expected non-zero remaining time because main PHY "
3431 "was switching when TXOP ended");
3467 "Unexpected number of RTS frames sent while the MediumSyncDelay timer is running");
3474 auto jumpToQosDataOrMuRts = [&]() {
3476 !psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData() &&
3477 !psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts())
3479 auto psdu = psduIt->psduMap.cbegin()->second;
3480 if (psdu->GetHeader(0).IsTrigger())
3483 psdu->GetPayload(0)->PeekHeader(trigger);
3570 psduIt->psduMap.cbegin()->second->GetHeader(0).IsBeacon()))
3580 "First QoS data frame has not been transmitted");
3583 "First QoS data frame should be transmitted without protection");
3586 "First QoS data frame should be transmitted by the main PHY");
3589 "First QoS data frame sent too early");
3591 auto prevPsduIt = psduIt++;
3592 jumpToQosDataOrMuRts();
3600 "Expected another QoS data frame sent concurrently with the first frame");
3602 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
3604 "First data frame on non-EMLSR link should be transmitted without protection");
3607 "First data frame expected to be transmitted on the non-EMLSR link");
3608 const auto txDuration =
3610 prevPsduIt->txVector,
3611 m_staMacs[0]->GetWifiPhy(prevPsduIt->phyId)->GetPhyBand());
3613 prevPsduIt->startTx + txDuration,
3614 "First data frame on the non-EMLSR link not sent concurrently");
3616 jumpToQosDataOrMuRts();
3624 "RTS before second QoS data frame has not been transmitted");
3627 "Second QoS data frame should be transmitted with protection");
3631 "RTS before second QoS data frame should not be transmitted by the main PHY");
3634 "RTS before second data frame transmitted on an unexpected width");
3639 "CTS before second QoS data frame has not been transmitted");
3642 "CTS before second QoS data frame has not been transmitted");
3647 "Second QoS data frame has not been transmitted");
3650 "Second QoS data frame has not been transmitted");
3653 "Second QoS data frame should be transmitted by the main PHY");
3656 "Second data frame not transmitted on the same width as RTS");
3658 bool moreQosDataFound =
false;
3662 jumpToQosDataOrMuRts();
3664 psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData())
3666 moreQosDataFound =
true;
3670 "Third QoS data frame should be transmitted by the main PHY");
3673 "Expecting TX width of third data frame to equal the channel "
3674 "width used by the main PHY");
3678 "Third QoS data frame sent before MediumSyncDelay timer expired");
3686 "Third QoS data frame transmitted by the main PHY not found");
3690 jumpToQosDataOrMuRts();
3696 "RTS before fourth QoS data frame has not been transmitted");
3699 "Fourth QoS data frame should be transmitted with protection");
3703 "RTS before fourth QoS data frame should not be transmitted by the main PHY");
3706 "RTS before fourth data frame transmitted on an unexpected width");
3711 "CTS before fourth QoS data frame has not been transmitted");
3714 "CTS before fourth QoS data frame has not been transmitted");
3716 jumpToQosDataOrMuRts();
3723 "RTS before fourth QoS data frame has not been transmitted");
3726 "Fourth QoS data frame should be transmitted with protection");
3730 "RTS before fourth QoS data frame should not be transmitted by the main PHY");
3733 "RTS before fourth data frame transmitted on an unexpected width");
3738 "CTS before fourth QoS data frame has not been transmitted");
3741 "CTS before fourth QoS data frame has not been transmitted");
3746 "Fourth QoS data frame has not been transmitted");
3749 "Fourth QoS data frame has not been transmitted");
3752 "Fourth QoS data frame should be transmitted by the main PHY");
3755 "Fourth data frame not transmitted on the same width as RTS");
3757 auto fourthLinkId = psduIt->linkId;
3760 jumpToQosDataOrMuRts();
3771 "Fifth data frame transmitted too late");
3777 if (psduIt->linkId != fourthLinkId)
3782 "Fifth QoS data frame should be transmitted with protection");
3786 "RTS before fifth QoS data frame should be transmitted by the main PHY");
3791 "CTS before fifth QoS data frame has not been transmitted");
3794 "CTS before fifth QoS data frame has not been transmitted");
3801 "Fifth QoS data frame has not been transmitted");
3804 "Fifth QoS data frame has not been transmitted");
3807 "Fifth QoS data frame should be transmitted by the main PHY");
3810 "Fifth QoS data frame should be transmitted on an auxiliary link");
3813 "Fifth data frame not transmitted on the correct channel width");
3818 m_enableBsrp(enableBsrp),
3845 m_apMac->AggregateObject(muScheduler);
3858 auto psdu = psduMap.begin()->second;
3860 switch (psdu->GetHeader(0).GetType())
3870 mac->GetWifiPhy(linkId)->GetPhyBand());
3879 for (
const auto id :
m_staMacs[0]->GetLinkIds())
3882 m_staMacs[0]->GetFrameExchangeManager(
id));
3884 ehtFem->UsingOtherEmlsrLink(),
3886 "Link " << +
id <<
" was" << (
id == linkId ?
" not" :
"")
3887 <<
" expected to be blocked on EMLSR client at time "
3891 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(
3901 psdu->GetPayload(0)->PeekHeader(blockAck);
3907 mac->GetWifiPhy(linkId)->GetPhyBand());
3916 if (psdu->GetHeader(0).IsCfEnd())
3943 muScheduler->SetAccessReqInterval(interval);
4022 "Expected a Trigger Frame");
4024 m_txPsdus[index].psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
4028 : (index ==
m_txPsdusPos ? TriggerFrameType::MU_RTS_TRIGGER
4029 : TriggerFrameType::BASIC_TRIGGER);
4031 +
static_cast<uint8_t
>(triggerType),
4032 "Unexpected Trigger Frame type on link " << +
m_txPsdus[index].linkId);
4038 "Unexpected number of User Info fields for Trigger Frame, index=" << index);
4041 auto startIndex = index;
4042 std::size_t ctsCount = 0;
4043 std::size_t qosNullCount = 0;
4045 for (; index < startIndex + 4; ++index)
4047 const auto& hdr =
m_txPsdus[index].psduMap.cbegin()->second->GetHeader(0);
4055 if (hdr.IsQosData() && !hdr.HasData())
4061 hdr.GetAddr2() ==
m_staMacs[0]->GetFrameExchangeManager(firstLinkId)->GetAddress())
4075 "Unexpected number of QoS Null frames");
4081 "Expected a Trigger Frame");
4084 "Unexpected link ID for Basic TF");
4086 m_txPsdus[index].psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
4089 +
static_cast<uint8_t
>(TriggerFrameType::BASIC_TRIGGER),
4090 "Unexpected Trigger Frame type");
4098 "Unexpected number of User Info fields for Basic Trigger Frame");
4101 startIndex = ++index;
4102 for (; index < startIndex + (
m_enableBsrp ? 1 : 2); ++index)
4104 const auto& hdr =
m_txPsdus[index].psduMap.cbegin()->second->GetHeader(0);
4111 (hdr.GetAddr2() ==
m_staMacs[0]->GetFrameExchangeManager(firstLinkId)->GetAddress()),
4112 "Unexpected type of QoS data frame");
4118 "QoS Data frame should be sent in a TB PPDU");
4125 "Expected a BlockAck frame");
4127 m_txPsdus[index].psduMap.cbegin()->second->GetPayload(0)->PeekHeader(blockAck);
4133 std::string(
"Check EMLSR link switching (switchAuxPhy=") +
4134 std::to_string(params.switchAuxPhy) +
", resetCamStateAndInterruptSwitch=" +
4135 std::to_string(params.resetCamStateAndInterruptSwitch) +
4136 ", auxPhyMaxChWidth=" +
std::to_string(params.auxPhyMaxChWidth) +
"MHz )"),
4137 m_switchAuxPhy(params.switchAuxPhy),
4138 m_resetCamStateAndInterruptSwitch(params.resetCamStateAndInterruptSwitch),
4139 m_auxPhyMaxChWidth(params.auxPhyMaxChWidth),
4140 m_countQoSframes(0),
4141 m_countIcfFrames(0),
4142 m_countRtsFrames(0),
4166 auto psdu = psduMap.begin()->second;
4167 auto nodeId = mac->GetDevice()->GetNode()->GetId();
4169 switch (psdu->GetHeader(0).GetType())
4172 NS_ASSERT_MSG(nodeId > 0,
"APs do not send AssocReq frames");
4180 action.protectedEhtAction ==
4187 std::set<uint8_t> linksToBlock;
4188 for (uint8_t
id = 0;
id <
m_apMac->GetNLinks();
id++)
4192 linksToBlock.insert(
id);
4195 m_apMac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
4236 for (std::size_t linkId = 0; linkId <
m_apMac->GetNLinks(); ++linkId)
4242 for (
auto mac : std::initializer_list<Ptr<WifiMac>>{
m_apMac,
m_staMacs[0]})
4244 mac->GetWifiPhy(0)->SetOperatingChannel(
4246 mac->GetWifiPhy(1)->SetOperatingChannel(
4248 mac->GetWifiPhy(2)->SetOperatingChannel(
4275 m_apMac->GetMacQueueScheduler()->UnblockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
4283 m_apMac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
4296 m_apMac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
4309 m_apMac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
4317 m_apMac->GetMacQueueScheduler()->UnblockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
4330 m_staMacs[0]->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
4338 m_staMacs[0]->GetMacQueueScheduler()->UnblockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
4350 m_staMacs[0]->GetMacQueueScheduler()->UnblockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
4358 m_staMacs[0]->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
4486 auto phyRecvIcf =
m_staMacs[0]->GetWifiPhy(linkId);
4488 auto currMainPhyLinkId =
m_staMacs[0]->GetLinkForPhy(mainPhy);
4491 "Didn't find the link on which the Main PHY is operating");
4496 if (phyRecvIcf != mainPhy)
4499 phyRecvIcf->GetChannelWidth(),
4501 "Aux PHY that received ICF "
4502 <<
m_countQoSframes <<
" is operating on a channel whose width exceeds the limit");
4511 "Expecting that the ICF was received by the main PHY");
4517 "Main PHY is operating on an unexpected link ("
4518 << +currMainPhyLinkId.value() <<
", expected " << +
m_mainPhyId
4529 "PHY operating on link where ICF was sent is not the main PHY");
4534 if (mainPhy != phyRecvIcf)
4538 "Aux PHY expected to switch channel");
4541 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(*currMainPhyLinkId),
4543 "The Aux PHY that received the ICF is expected to operate "
4544 "on the link where Main PHY was before switching channel");
4551 "Aux PHY is not expected to switch channel");
4553 mainPhy->GetPhyBand(),
4554 "The Aux PHY that received the ICF is expected to operate "
4555 "on the same band as the Main PHY");
4568 auto psdu = psduMap.begin()->second;
4573 m_staMacs[0]->GetFrameExchangeManager(linkId)->GetWifiTxTimer().GetDelayLeft() -
4578 auto toCurrSwitchEnd = mainPhy->GetDelayUntilIdle() + TimeStep(1);
4583 "Main PHY expected to be in SWITCHING state instead of "
4584 << mainPhy->GetState()->GetState());
4600 delayLeft += mainPhy->GetChannelSwitchDelay();
4603 auto totalSwitchDelay =
4604 delayLeft + (mainPhy->GetChannelSwitchDelay() - toCurrSwitchEnd);
4615 auto id =
m_staMacs[0]->GetLinkForPhy(mainPhy);
4618 "Expected main PHY to operate on a link");
4621 "Main PHY is operating on an unexpected link");
4622 const auto startMsd =
4625 const auto msdIsRunning = msdWasRunning || startMsd;
4630 std::string(
"because total switch delay was ") +
4631 std::to_string(totalSwitchDelay.GetNanoSeconds()) +
"ns");
4638 std::set<uint8_t> links{0, 1, 2};
4639 links.erase(linkId);
4640 m_staMacs[0]->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
4674 const std::size_t nFrameExchNoRts = fe11protected ? 3 : 4;
4676 const std::size_t nFrameExchWithRts = fe11protected ? 1 : 0;
4681 nFrameExchNoRts * 2 +
4682 nFrameExchWithRts * 4 +
4684 "Insufficient number of TX PSDUs");
4689 const std::size_t nFrameExchanges =
4692 for (std::size_t i = 1; i <= nFrameExchanges; ++i)
4694 if (i == 1 || (i >= 3 && i <= 6) || i == 8 || i == 10 || (i == 11 && fe11protected))
4698 (i < 9 ? psduIt->psduMap.at(
SU_STA_ID)->GetHeader(0).IsTrigger()
4699 : psduIt->psduMap.at(
SU_STA_ID)->GetHeader(0).IsRts())),
4701 "Expected a Trigger Frame (ICF)");
4708 psduIt->psduMap.at(
SU_STA_ID)->GetHeader(0).IsCts()),
4714 if (i == 1 || i == 2 || i == 7 || i == 8)
4717 psduIt->psduMap.at(
SU_STA_ID)->GetHeader(0).IsMgt()),
4719 "Expected a management frame");
4722 psduIt->psduMap.at(
SU_STA_ID)->GetHeader(0).IsAck()),
4724 "Expected a Normal Ack");
4729 psduIt->psduMap.at(
SU_STA_ID)->GetHeader(0).IsQosData()),
4731 "Expected a QoS Data frame");
4734 psduIt->psduMap.at(
SU_STA_ID)->GetHeader(0).IsBlockAck()),
4736 "Expected a BlockAck");
4744 std::to_string(auxPhyMaxChWidth) +
"MHz )"),
4745 m_auxPhyMaxChWidth(auxPhyMaxChWidth),
4747 m_currMainPhyLinkId(0),
4748 m_nextMainPhyLinkId(0)
4781 mac->GetWifiPhy(0)->SetOperatingChannel(
4783 mac->GetWifiPhy(1)->SetOperatingChannel(
4785 mac->GetWifiPhy(2)->SetOperatingChannel(
4802 m_staMacs[1]->GetFrameExchangeManager(linkId)->StartTransmission(
4804 m_staMacs[1]->GetWifiPhy(linkId)->GetChannelWidth());
4814 <<
", other MLD did not start transmitting on link "
4825 "Main PHY is not operating on any link");
4830 m_staMacs[0]->GetEmlsrManager()->SwitchMainPhy(
4882 "Aux PHY is not receiving a PHY header");
4887 "Main PHY is receiving a PHY header");
4898 "No CCA information for primary20 channel");
4902 "ChannelAccessManager on destination link not notified of CCA busy");
4906 "ChannelAccessManager on destination link notified of CCA busy until end of transmission");
4924 "No CCA information for primary20 channel");
4928 "ChannelAccessManager on destination link not notified of CCA busy");
4931 "ChannelAccessManager on destination link not notified of CCA busy "
4932 "until end of transmission");
4949 "No CCA information for primary20 channel");
4952 "ChannelAccessManager on source link not notified of CCA busy");
4955 "ChannelAccessManager on source link not notified of CCA busy "
4956 "until end of transmission");
4967 TestCase::Duration::QUICK);
4968 for (
const auto& emlsrLinks :
4969 {std::set<uint8_t>{0, 1, 2}, std::set<uint8_t>{1, 2}, std::set<uint8_t>{0, 1}})
4978 TestCase::Duration::QUICK);
4986 TestCase::Duration::QUICK);
4994 TestCase::Duration::QUICK);
4997 for (
auto genBackoffAndUseAuxPhyCca : {
true,
false})
4999 for (
auto nSlotsLeft : std::initializer_list<uint8_t>{0, 4})
5006 genBackoffAndUseAuxPhyCca,
5010 TestCase::Duration::QUICK);
5016 genBackoffAndUseAuxPhyCca,
5020 TestCase::Duration::QUICK);
5024 for (
bool switchAuxPhy : {
true,
false})
5026 for (
bool resetCamStateAndInterruptSwitch : {
true,
false})
5031 {switchAuxPhy, resetCamStateAndInterruptSwitch, auxPhyMaxChWidth}),
5032 TestCase::Duration::QUICK);
Test the exchange of EML Operating Mode Notification frames.
void CheckEmlCapabilitiesInAssocResp(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 Associat...
void TxOk(Ptr< const WifiMpdu > mpdu)
Callback invoked when the non-AP MLD receives the acknowledgment for a transmitted MPDU.
std::list< uint64_t > m_uidList
list of UIDs of packets to corrupt
std::size_t m_checkEmlsrLinksCount
counter for the number of times CheckEmlsrLinks is called (should be two: when the transition timeout...
Ptr< ListErrorModel > m_errorModel
error rate model to corrupt packets at AP MLD
void DoSetup() override
Implementation to do any local setup required for this TestCase.
EmlOmnExchangeTest(const std::set< uint8_t > &linksToEnableEmlsrOn, Time transitionTimeout)
Constructor.
std::size_t m_emlNotificationDroppedCount
counter for the number of times the EML Notification frame sent by the non-AP MLD has been dropped du...
void CheckEmlsrLinks()
Check that the EMLSR mode has been enabled on the expected EMLSR links.
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 Associat...
void TxDropped(WifiMacDropReason reason, Ptr< const WifiMpdu > mpdu)
Callback invoked when the non-AP MLD drops the given MPDU for the given reason.
void CheckEmlNotification(Ptr< const WifiPsdu > psdu, const WifiTxVector &txVector, uint8_t linkId)
Check the content of a received EML Operating Mode Notification frame.
void Transmit(Ptr< WifiMac > mac, uint8_t phyId, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW) override
Callback invoked when a FEM passes PSDUs to the PHY.
void DoRun() override
Implementation to actually run this TestCase.
Test EML Operating Mode Notification frame serialization and deserialization.
EmlOperatingModeNotificationTest()
Constructor.
void DoRun() override
Implementation to actually run this TestCase.
Test CCA busy notifications on EMLSR clients.
void DoSetup() override
Implementation to do any local setup required for this TestCase.
uint8_t m_nextMainPhyLinkId
the ID of the link the main PHY switches to
uint8_t m_currMainPhyLinkId
the ID of the link the main PHY switches from
void StartTraffic() override
Start the generation of traffic (needs to be overridden)
MHz_u m_auxPhyMaxChWidth
max channel width supported by aux PHYs
void TransmitPacketToAp(uint8_t linkId)
Make the other MLD transmit a packet to the AP on the given link.
void DoRun() override
Implementation to actually run this TestCase.
Time m_channelSwitchDelay
the PHY channel switch delay
void CheckPoint1()
Perform checks after that the preamble of the first PPDU has been received.
void CheckPoint3()
Perform checks after that the aux PHY completed the link switch.
EmlsrCcaBusyTest(MHz_u auxPhyMaxChWidth)
Constructor.
void CheckPoint2()
Perform checks after that the main PHY completed the link switch.
Test the transmission of DL frames to EMLSR clients.
void CheckInitialControlFrame(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 EM...
const Time m_fe2to3delay
time interval between 2nd and 3rd frame exchange sequences after the enablement of EMLSR mode
void CheckResults()
Check that the simulation produced the expected results.
void CheckPmModeAfterAssociation(const Mac48Address &address)
Check that the AP MLD considers the correct Power Management mode for the links setup with the given ...
EmlsrDlTxopTest(const Params ¶ms)
Constructor.
void StartTraffic() override
Start the generation of traffic (needs to be overridden)
Ptr< ListErrorModel > m_errorModel
error rate model to corrupt BlockAck at AP MLD
void CheckStaEmlNotificationFrame(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 Notific...
std::size_t m_countQoSframes
counter for QoS frames (transition delay test)
void CheckApEmlNotificationFrame(Ptr< const WifiMpdu > mpdu, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken when the AP MLD transmits an EML Operating Mode Notification...
Time m_emlsrEnabledTime
when EMLSR mode has been enabled on all EMLSR clients
std::set< uint8_t > m_emlsrLinks
IDs of the links on which EMLSR mode has to be enabled.
void DoSetup() override
Implementation to do any local setup required for this TestCase.
void CheckQosFrames(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...
void Transmit(Ptr< WifiMac > mac, uint8_t phyId, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW) override
Callback invoked when a FEM passes PSDUs to the PHY.
void EnableEmlsrMode()
Enable EMLSR mode on the next EMLSR client.
void CheckBlockAck(const WifiConstPsduMap &psduMap, const WifiTxVector &txVector, uint8_t phyId)
Check that appropriate actions are taken by the AP MLD receiving a PPDU containing BlockAck frames fr...
void DoRun() override
Implementation to actually run this TestCase.
std::size_t m_countBlockAck
counter for BlockAck frames (transition delay test)
Test the switching of PHYs on EMLSR clients.
void Transmit(Ptr< WifiMac > mac, uint8_t phyId, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW) override
Callback invoked when a FEM passes PSDUs to the PHY.
bool m_resetCamStateAndInterruptSwitch
whether to reset the state of the ChannelAccessManager associated with the link on which the main PHY...
void DoRun() override
Implementation to actually run this TestCase.
void CheckResults()
Check that the simulation produced the expected results.
void CheckRtsFrame(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 fr...
void CheckInitialControlFrame(const WifiConstPsduMap &psduMap, const WifiTxVector &txVector, uint8_t linkId)
Check that the Main PHY (and possibly the Aux PHY) correctly switches channel when the reception of a...
void DoSetup() override
Implementation to do any local setup required for this TestCase.
std::size_t m_countRtsFrames
counter for RTS frames
std::size_t m_countIcfFrames
counter for ICF frames
Ptr< ListErrorModel > m_errorModel
error rate model to corrupt packets at AP MLD
std::size_t m_countQoSframes
counter for QoS data frames
std::size_t m_txPsdusPos
position in the vector of TX PSDUs of the first ICF
void CheckQosFrames(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...
EmlsrLinkSwitchTest(const Params ¶ms)
Constructor.
MHz_u m_auxPhyMaxChWidth
max channel width supported by aux PHYs
bool m_switchAuxPhy
whether AUX PHY should switch channel to operate on the link on which the Main PHY was operating befo...
Base class for EMLSR Operations tests.
void MainPhySwitchInfoCallback(std::size_t index, const EmlsrMainPhySwitchTrace &info)
Callback connected to the EMLSR Manager MainPhySwitch trace source.
std::size_t m_nNonEmlsrStations
number of stations to create that do not activate EMLSR
void SetSsid(uint16_t aid, Mac48Address)
Set the SSID on the next station that needs to start the association procedure.
bool m_establishBaDl
whether BA needs to be established (for TID 0) with the AP as originator
void CheckBlockedLink(Ptr< WifiMac > mac, Mac48Address dest, uint8_t linkId, WifiQueueBlockedReason reason, bool blocked, std::string description, bool testUnblockedForOtherReasons=true)
Check whether QoS data unicast transmissions addressed to the given destination on the given link are...
std::size_t m_nEmlsrStations
number of stations to create that activate EMLSR
std::vector< PacketSocketAddress > m_dlSockets
packet socket address for DL traffic
std::vector< Time > m_paddingDelay
Padding Delay advertised by the non-AP MLD.
std::set< uint8_t > m_linksToEnableEmlsrOn
IDs of the links on which EMLSR mode has to be enabled.
Ptr< ApWifiMac > m_apMac
AP wifi MAC.
bool m_putAuxPhyToSleep
whether aux PHYs are put to sleep during DL/UL TXOPs
TrafficDirection
Enumeration for traffic directions.
void CheckMsdTimerRunning(Ptr< StaWifiMac > staMac, uint8_t linkId, bool isRunning, const std::string &msg)
Check whether the MediumSyncDelay timer is running on the given link of the given device.
void DoSetup() override
Implementation to do any local setup required for this TestCase.
uint8_t m_mainPhyId
ID of the main PHY.
Time m_duration
simulation duration
std::vector< FrameInfo > m_txPsdus
transmitted PSDUs
Ptr< PacketSocketClient > GetApplication(TrafficDirection dir, std::size_t staId, std::size_t count, std::size_t pktSize) const
virtual void Transmit(Ptr< WifiMac > mac, uint8_t phyId, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW)
Callback invoked when a FEM passes PSDUs to the PHY.
bool m_establishBaUl
whether BA needs to be established (for TID 0) with the AP as recipient
uint16_t m_lastAid
AID of last associated station.
std::vector< Time > m_transitionDelay
Transition Delay advertised by the non-AP MLD.
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)
Check information provided by the EMLSR Manager MainPhySwitch trace.
std::map< std::size_t, std::shared_ptr< EmlsrMainPhySwitchTrace > > m_traceInfo
EMLSR client ID-indexed map of trace info from last main PHY switch.
Time m_transitionTimeout
Transition Timeout advertised by the AP MLD.
std::vector< PacketSocketAddress > m_ulSockets
packet socket address for UL traffic
void CheckAuxPhysSleepMode(Ptr< StaWifiMac > staMac, bool sleep)
Check whether aux PHYs of the given device are in sleep mode/awake.
std::vector< Ptr< StaWifiMac > > m_staMacs
MACs of the non-AP MLDs.
virtual void StartTraffic()
Start the generation of traffic (needs to be overridden)
EmlsrOperationsTestBase(const std::string &name)
Constructor.
Check UL OFDMA operations with EMLSR clients.
Time m_startAccessReq
start time of the first AP MLD access request via MU scheduler
void DoRun() override
Implementation to actually run this TestCase.
void CheckResults()
Check that the simulation produced the expected results.
EmlsrUlOfdmaTest(bool enableBsrp)
Constructor.
void StartTraffic() override
Start the generation of traffic (needs to be overridden)
void Transmit(Ptr< WifiMac > mac, uint8_t phyId, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW) override
Callback invoked when a FEM passes PSDUs to the PHY.
void DoSetup() override
Implementation to do any local setup required for this TestCase.
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
Test the transmission of UL frames from EMLSR clients.
std::size_t m_countQoSframes
counter for QoS frames
const Time m_unblockMainPhyLinkDelay
delay between the time the first two UL packets are generated and the time transmissions are unblocke...
Ptr< ListErrorModel > m_errorModel
error rate model to corrupt packets
bool m_useAuxPhyCca
whether CCA info from aux PHY is used when aux PHY is not TX capable
Time m_5thQosFrameTxTime
start transmission time of the 5th QoS data frame
MHz_u m_5thQosFrameExpWidth
expected width of the 5th QoS data frame
MHz_u m_auxPhyChannelWidth
max width supported by aux PHYs
void BackoffGenerated(uint32_t backoff, uint8_t linkId)
Callback invoked when a new backoff value is generated by the EMLSR client.
bool m_switchMainPhyBackDelayTimeout
whether a SwitchMainPhyBackDelay timer expires after that the main PHY moved to an aux PHY link
std::optional< uint8_t > m_nonEmlsrLink
ID of the non-EMLSR link (if any)
Time m_lastMsdExpiryTime
expiry time of the last MediumSyncDelay timer
void DoSetup() override
Implementation to do any local setup required for this TestCase.
std::size_t m_countRtsframes
counter for RTS frames
void CheckCtsFrames(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 ...
void DoRun() override
Implementation to actually run this TestCase.
Time m_firstUlPktsGenTime
generation time of the first two UL packets
std::optional< bool > m_corruptCts
whether the transmitted CTS must be corrupted
void CheckBlockAck(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 t...
void StartTraffic() override
Start the generation of traffic (needs to be overridden)
std::optional< Time > m_backoffEndTime
expected backoff end time on main PHY link
MHz_u m_channelWidth
width of the channels used by MLDs
std::set< uint8_t > m_emlsrLinks
IDs of the links on which EMLSR mode has to be enabled.
Time m_mediumSyncDuration
duration of the MediumSyncDelay timer
void CheckRtsFrames(Ptr< const WifiMpdu > mpdu, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken by the EMLSR client when transmitting an RTS frame on the gi...
void CheckQosFrames(const WifiConstPsduMap &psduMap, const WifiTxVector &txVector, uint8_t linkId)
Check that appropriate actions are taken when an MLD transmits a PPDU containing QoS data frames on t...
bool m_genBackoffIfTxopWithoutTx
whether the backoff should be invoked when the AC gains the right to start a TXOP but it does not tra...
void Transmit(Ptr< WifiMac > mac, uint8_t phyId, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW) override
Callback invoked when a FEM passes PSDUs to the PHY.
uint8_t m_nSlotsLeftAlert
value for ChannelAccessManager NSlotsLeft attribute
bool m_checkBackoffStarted
whether we are checking the generated backoff values
std::size_t m_countBlockAck
counter for BlockAck frames
void CheckResults()
Check that the simulation produced the expected results.
uint8_t m_msdMaxNTxops
Max number of TXOPs that an EMLSR client is allowed to attempt to initiate while the MediumSyncDelay ...
EmlsrUlTxopTest(const Params ¶ms)
Constructor.
A container for one type of attribute.
Class for representing data rates.
Time CalculateBytesTxTime(uint32_t bytes) const
Calculate transmission time.
static constexpr uint16_t MEDIUM_SYNC_THRESHOLD_USEC
The aMediumSyncThreshold defined by Sec. 35.3.16.18.1 of 802.11be D4.0.
static constexpr bool DONT_REQUEST_ACCESS
do not request channel access when PHY switch ends
static constexpr bool DONT_RESET_BACKOFF
do not reset backoff on main PHY switch
Hold variables of type enum.
void SetList(const std::list< uint64_t > &packetlist)
static Mac48Address GetBroadcast()
Implement the header for Action frames of type EML Operating Mode Notification.
void SetLinkIdInBitmap(uint8_t linkId)
Set the bit position in the link bitmap corresponding to the given link.
EmlControl m_emlControl
EML Control field.
std::optional< EmlsrParamUpdate > m_emlsrParamUpdate
EMLSR Parameter Update field.
std::list< uint8_t > GetLinkBitmap() const
Helper class used to assign positions and mobility models to nodes.
MultiUserScheduler is an abstract base class defining the API that APs supporting at least VHT can us...
holds a vector of ns3::NetDevice pointers
Ptr< NetDevice > Get(uint32_t i) const
Get the Ptr<NetDevice> stored in this container at a given index.
keep track of a set of node pointers.
an address for a packet socket
void SetProtocol(uint16_t protocol)
Set the protocol.
void SetSingleDevice(uint32_t device)
Set the address to match only a specified NetDevice.
Give ns3::PacketSocket powers to ns3::Node.
void Install(Ptr< Node > node) const
Aggregate an instance of a ns3::PacketSocketFactory onto the provided node.
Smart pointer class similar to boost::intrusive_ptr.
static void SetRun(uint64_t run)
Set the run number of simulation.
static void SetSeed(uint32_t seed)
Set the seed.
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
static void Destroy()
Execute the events scheduled with ScheduleDestroy().
static Time Now()
Return the current simulation virtual time.
static void Run()
Run the simulation.
static EventId ScheduleNow(FUNC f, Ts &&... args)
Schedule an event to expire Now.
static void Stop()
Tell the Simulator the calling event should be the last one executed.
Make it easy to create and manage PHY objects for the spectrum model.
void AddChannel(const Ptr< SpectrumChannel > channel, const FrequencyRange &freqRange=WHOLE_WIFI_SPECTRUM)
The IEEE 802.11 SSID Information Element.
Hold variables of type string.
void AddTestCase(TestCase *testCase, Duration duration=Duration::QUICK)
Add an individual child TestCase to this test suite.
Simulation virtual time values and global simulation resolution.
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
bool IsStrictlyPositive() const
Exactly equivalent to t > 0.
static Time Max()
Maximum representable Time Not to be confused with Max(Time,Time).
bool IsZero() const
Exactly equivalent to t == 0.
int64_t GetTimeStep() const
Get the raw time value, in the current resolution unit.
Hold an unsigned integer type.
helps to create WifiNetDevice objects
static int64_t AssignStreams(NetDeviceContainer c, int64_t stream)
Assign a fixed random variable stream number to the random variables used by the PHY and MAC aspects ...
create MAC layers for a ns3::WifiNetDevice.
uint64_t GetDataRate(MHz_u channelWidth, Time guardInterval, uint8_t nss) const
void SetPcapCaptureType(PcapCaptureType type)
Set the PCAP capture type to be used.
void SetPcapDataLinkType(SupportedPcapDataLinkTypes dlt)
Set the data link type of PCAP traces to be used.
void Set(std::string name, const AttributeValue &v)
@ DLT_IEEE802_11_RADIO
Include Radiotap link layer information.
std::tuple< uint8_t, MHz_u, WifiPhyBand, uint8_t > ChannelTuple
Tuple identifying a segment of an operating channel.
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
static Time CalculatePhyPreambleAndHeaderDuration(const WifiTxVector &txVector)
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
WifiMode GetMode(uint16_t staId=SU_STA_ID) const
If this TX vector is associated with an SU PPDU, return the selected payload transmission mode.
WifiPreamble GetPreambleType() const
MHz_u GetChannelWidth() const
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
void SetDefault(std::string name, const AttributeValue &value)
void ConnectWithoutContext(std::string path, const CallbackBase &cb)
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Ptr< T > CreateObject(Args &&... args)
Create an object by type, with varying number of constructor parameters.
Ptr< T > CreateObjectWithAttributes(Args... args)
Allocate an Object on the heap and initialize with a set of attributes.
Ptr< T > Create(Ts &&... args)
Create class instances by constructors with varying numbers of arguments and return them by Ptr.
#define NS_TEST_EXPECT_MSG_GT_OR_EQ(actual, limit, msg)
Test that an actual value is greater than or equal to limit and report if not.
#define NS_TEST_ASSERT_MSG_EQ(actual, limit, msg)
Test that an actual and expected (limit) value are equal and report and abort if not.
#define NS_TEST_EXPECT_MSG_LT_OR_EQ(actual, limit, msg)
Test that an actual value is less than or equal to a limit and report if not.
#define NS_TEST_EXPECT_MSG_LT(actual, limit, msg)
Test that an actual value is less than a limit and report if not.
#define NS_TEST_EXPECT_MSG_GT(actual, limit, msg)
Test that an actual value is greater than a limit and report if not.
#define NS_TEST_EXPECT_MSG_NE(actual, limit, msg)
Test that an actual and expected (limit) value are not equal and report if not.
#define NS_TEST_EXPECT_MSG_EQ(actual, limit, msg)
Test that an actual and expected (limit) value are equal and report if not.
#define NS_TEST_ASSERT_MSG_NE(actual, limit, msg)
Test that an actual and expected (limit) value are not equal and report and abort if not.
#define NS_TEST_ASSERT_MSG_GT(actual, limit, msg)
Test that an actual value is greater than a limit and report and abort if not.
#define NS_TEST_ASSERT_MSG_GT_OR_EQ(actual, limit, msg)
Test that an actual value is greater than or equal to a limit and report and abort if not.
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Time Seconds(double value)
Construct a Time in the indicated unit.
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
WifiMacDropReason
The reason why an MPDU was dropped.
WifiQueueBlockedReason
Enumeration of the reasons to block container queues.
TriggerFrameType
The different Trigger frame types.
@ WIFI_PHY_BAND_6GHZ
The 6 GHz band.
@ WIFI_PHY_BAND_2_4GHZ
The 2.4 GHz band.
@ WIFI_PHY_BAND_5GHZ
The 5 GHz band.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
constexpr FrequencyRange WIFI_SPECTRUM_6_GHZ
Identifier for the frequency range covering the wifi spectrum in the 6 GHz band.
U * PeekPointer(const Ptr< U > &p)
std:: tuple< WifiContainerQueueType, WifiReceiverAddressType, Mac48Address, std::optional< uint8_t > > WifiContainerQueueId
Tuple (queue type, receiver address type, Address, TID) identifying a container queue.
Callback< R, Args... > MakeCallback(R(T::*memPtr)(Args...), OBJ objPtr)
Build Callbacks for class method members which take varying numbers of arguments and potentially retu...
bool IsTrigger(const WifiPsduMap &psduMap)
Ptr< T1 > DynamicCast(const Ptr< T2 > &p)
Cast a Ptr.
std::size_t Count20MHzSubchannels(MHz_u channelWidth)
Return the number of 20 MHz subchannels covering the channel width.
constexpr FrequencyRange WIFI_SPECTRUM_5_GHZ
Identifier for the frequency range covering the wifi spectrum in the 5 GHz band.
@ WIFI_MAC_MGT_ASSOCIATION_RESPONSE
@ WIFI_MAC_MGT_ASSOCIATION_REQUEST
Ptr< T1 > StaticCast(const Ptr< T2 > &p)
Cast a Ptr.
std::unordered_map< uint16_t, Ptr< const WifiPsdu > > WifiConstPsduMap
Map of const PSDUs indexed by STA-ID.
uint32_t GetAckSize()
Return the total Ack size (including FCS trailer).
static constexpr uint16_t SU_STA_ID
STA_ID to identify a single user (SU)
static constexpr uint8_t MAX_PROPAGATION_DELAY_USEC
maximum propagation delay
constexpr FrequencyRange WIFI_SPECTRUM_2_4_GHZ
Identifier for the frequency range covering the wifi spectrum in the 2.4 GHz band.
Parameters for the EMLSR DL TXOP test.
Parameters for the EMLSR link switching test.
Parameters for the EMLSR UL TXOP test.
static uint8_t EncodeEmlsrTransitionDelay(Time delay)
static Time DecodeEmlsrTransitionDelay(uint8_t value)
static Time DecodeEmlsrPaddingDelay(uint8_t value)
static uint8_t EncodeEmlsrPaddingDelay(Time delay)
Struct to trace that main PHY switched to start a DL TXOP after that an aux PHY received an ICF.
Base struct for EMLSR Main PHY switch traces.
virtual std::shared_ptr< EmlsrMainPhySwitchTrace > Clone() const =0
Struct to trace that main PHY switched when a (DL or UL) TXOP ended.
uint8_t emlsrMode
EMLSR Mode.
uint8_t emlsrParamUpdateCtrl
EMLSR Parameter Update Control.
uint8_t emlmrMode
EMLMR Mode.
std::optional< uint16_t > linkBitmap
EMLSR/EMLMR Link Bitmap.
EMLSR Parameter Update field.
uint32_t pktSize
packet size used for the simulation (in bytes)
static WifiEmlsrTestSuite g_wifiEmlsrTestSuite
the test suite