11#include "ns3/advanced-emlsr-manager.h" 
   12#include "ns3/boolean.h" 
   13#include "ns3/config.h" 
   14#include "ns3/eht-configuration.h" 
   15#include "ns3/eht-frame-exchange-manager.h" 
   17#include "ns3/mgt-action-headers.h" 
   18#include "ns3/qos-txop.h" 
   19#include "ns3/rr-multi-user-scheduler.h" 
   20#include "ns3/simulator.h" 
   21#include "ns3/string.h" 
   22#include "ns3/wifi-net-device.h" 
   34                              std::to_string(params.nEmlsrStations) + 
"," +
 
   35                              std::to_string(params.nNonEmlsrStations) + 
")"),
 
   36      m_emlsrLinks(params.linksToEnableEmlsrOn),
 
   37      m_emlsrEnabledTime(0),
 
   54                    "This test requires at least two links to be configured as EMLSR links");
 
 
   67    auto psdu = psduMap.begin()->second;
 
   68    auto nodeId = mac->GetDevice()->GetNode()->GetId();
 
   70    switch (psdu->GetHeader(0).GetType())
 
   80            for (
const auto id : 
m_staMacs.at(nodeId - 1)->GetLinkIds())
 
   84                    m_staMacs[nodeId - 1]->SetPowerSaveMode({
true, 
id});
 
   94            (action.protectedEhtAction ==
 
  123            const auto txDuration =
 
  126                                             apMac->GetDevice()->GetPhy(phyId)->GetPhyBand());
 
 
  161    for (std::size_t linkId = 0; linkId < 
m_apMac->GetNLinks(); linkId++)
 
  173        m_apMac->AggregateObject(muScheduler);
 
  174        for (uint8_t linkId = 0; linkId < 
m_apMac->GetNLinks(); linkId++)
 
  176            m_apMac->GetFrameExchangeManager(linkId)->GetAckManager()->SetAttribute(
 
  177                "DlMuAckSequenceType",
 
 
  220            m_staMacs.at(
id)->GetEmlsrManager()->SetAttribute(
 
  226            m_apMac->GetDevice()->GetNode()->AddApplication(
 
 
  259    auto jumpToQosDataOrMuRts = [&]() {
 
  261               !psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData())
 
  263            auto psdu = psduIt->psduMap.cbegin()->second;
 
  264            if (psdu->GetHeader(0).IsTrigger())
 
  267                psdu->GetPayload(0)->PeekHeader(trigger);
 
  331        std::set<uint8_t> linkIds;
 
  333        jumpToQosDataOrMuRts();
 
  335                               psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData()),
 
  337                              "Expected at least one QoS data frame before enabling EMLSR mode");
 
  338        linkIds.insert(psduIt->linkId);
 
  339        const auto firstAmpduTxEnd =
 
  343                                         m_staMacs[i]->GetWifiPhy(psduIt->linkId)->GetPhyBand());
 
  344        auto firstQos = psduIt++;
 
  346        jumpToQosDataOrMuRts();
 
  348                               psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData()),
 
  350                              "Expected at least two QoS data frames before enabling EMLSR mode");
 
  351        linkIds.insert(psduIt->linkId);
 
  352        const auto secondAmpduTxStart = psduIt->startTx;
 
  354        auto beaconInBetween{
false};
 
  355        while (++firstQos != psduIt)
 
  357            if (firstQos->psduMap.cbegin()->second->GetHeader(0).IsBeacon())
 
  359                beaconInBetween = 
true;
 
  371        auto setupLinks = 
m_staMacs[i]->GetSetupLinkIds();
 
  373        bool areAllSetupLinksEmlsr =
 
  374            std::all_of(setupLinks.begin(), setupLinks.end(), [&](
auto&& linkId) {
 
  375                return linkId == m_mainPhyId || m_emlsrLinks.contains(linkId);
 
  382                                  "Expected both A-MPDUs to be sent on the same link");
 
  386                                  "A-MPDUs are not sent one after another");
 
  392        else if (!beaconInBetween)
 
  396                                  "Expected A-MPDUs to be sent on distinct links");
 
  399                                  "A-MPDUs are not sent concurrently");
 
  465    using FrameExchange = std::list<
decltype(psduIt)>;
 
  472        jumpToQosDataOrMuRts();
 
  481            psduIt->psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
 
  486                                  "jumpToQosDataOrMuRts does not return TFs other than MU-RTS");
 
  487            for (
const auto& userInfo : trigger)
 
  491                    if (
m_staMacs.at(i)->GetAssociationId() == userInfo.GetAid12())
 
  493                        frameExchanges.at(i).emplace_back(FrameExchange{psduIt});
 
  505        for (
const auto& staIdPsduPair : psduIt->psduMap)
 
  508                if (!staMac->GetLinkIdByAddress(staIdPsduPair.second->GetAddr1()))
 
  516                std::size_t 
id = staMac->GetDevice()->GetNode()->GetId() - 1;
 
  517                for (
auto& frameExchange : frameExchanges.at(
id))
 
  519                    if (
IsTrigger(frameExchange.front()->psduMap) &&
 
  520                        frameExchange.front()->linkId == psduIt->linkId &&
 
  521                        frameExchange.size() == 1)
 
  523                        auto it = std::next(frameExchange.front());
 
  527                            if (it->linkId == psduIt->linkId &&
 
  528                                !it->psduMap.begin()->second->GetHeader(0).IsCts())
 
  537                            frameExchange.emplace_back(psduIt);
 
  542                frameExchanges.at(
id).emplace_back(FrameExchange{psduIt});
 
  554    for (std::size_t i = 0; i < m_nEmlsrStations; i++)
 
  558                                    "Expected at least 2 frame exchange sequences " 
  559                                        << 
"involving EMLSR client " << i);
 
  561        auto firstExchangeIt = frameExchanges.at(i).begin();
 
  562        auto secondExchangeIt = std::next(firstExchangeIt);
 
  564        const auto firstAmpduTxEnd =
 
  565            firstExchangeIt->back()->startTx +
 
  567                firstExchangeIt->back()->psduMap,
 
  568                firstExchangeIt->back()->txVector,
 
  569                m_staMacs[i]->GetWifiPhy(firstExchangeIt->back()->linkId)->GetPhyBand());
 
  570        const auto secondAmpduTxStart = secondExchangeIt->front()->startTx;
 
  572        if (m_staMacs[i]->GetNLinks() == m_emlsrLinks.size())
 
  577                                  "Expected an MU-RTS TF as ICF of first frame exchange sequence");
 
  579                firstExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
 
  581                "Expected a QoS data frame in the first frame exchange sequence");
 
  585                                  "Expected an MU-RTS TF as ICF of second frame exchange sequence");
 
  587                secondExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
 
  589                "Expected a QoS data frame in the second frame exchange sequence");
 
  593                                  "A-MPDUs are not sent one after another");
 
  597            std::vector<uint8_t> nonEmlsrIds;
 
  598            auto setupLinks = m_staMacs[i]->GetSetupLinkIds();
 
  599            std::set_difference(setupLinks.begin(),
 
  601                                m_emlsrLinks.begin(),
 
  603                                std::back_inserter(nonEmlsrIds));
 
  606            auto nonEmlsrLinkExchangeIt = firstExchangeIt->front()->linkId == nonEmlsrIds[0]
 
  611                                  "Did not expect an MU-RTS TF as ICF on non-EMLSR link");
 
  613                nonEmlsrLinkExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
 
  615                "Expected a QoS data frame on the non-EMLSR link");
 
  617            auto emlsrLinkExchangeIt =
 
  618                nonEmlsrLinkExchangeIt == firstExchangeIt ? secondExchangeIt : firstExchangeIt;
 
  621                                  "Expected this exchange not to occur on non-EMLSR link");
 
  624                                  "Expected an MU-RTS TF as ICF on the EMLSR link");
 
  626                emlsrLinkExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
 
  628                "Expected a QoS data frame on the EMLSR link");
 
  632                                  "A-MPDUs are not sent concurrently");
 
  636        frameExchanges.at(i).erase(firstExchangeIt);
 
  637        frameExchanges.at(i).erase(secondExchangeIt);
 
  665    if (m_nEmlsrStations == 2 && m_apMac->GetNLinks() == m_emlsrLinks.size())
 
  668        for (std::size_t i = 0; i < m_nEmlsrStations; i++)
 
  672                                        "Expected at least 2 frame exchange sequences " 
  673                                            << 
"involving EMLSR client " << i);
 
  675            auto firstExchangeIt = frameExchanges.at(i).begin();
 
  679                                  "Expected an MU-RTS TF as ICF of first frame exchange sequence");
 
  681                firstExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
 
  683                "Expected a QoS data frame in the first frame exchange sequence");
 
  687        auto secondExchangeIt = std::next(frameExchanges.at(0).begin())->front()->startTx <
 
  688                                        std::next(frameExchanges.at(1).begin())->front()->startTx
 
  689                                    ? std::next(frameExchanges.at(0).begin())
 
  690                                    : 
std::next(frameExchanges.at(1).begin());
 
  691        decltype(secondExchangeIt) thirdExchangeIt;
 
  692        std::size_t thirdExchangeStaId;
 
  694        if (secondExchangeIt == std::next(frameExchanges.at(0).begin()))
 
  696            thirdExchangeIt = std::next(frameExchanges.at(1).begin());
 
  697            thirdExchangeStaId = 1;
 
  701            thirdExchangeIt = std::next(frameExchanges.at(0).begin());
 
  702            thirdExchangeStaId = 0;
 
  709                              "Expected no ICF for the second frame exchange sequence");
 
  711            secondExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
 
  713            "Expected a QoS data frame in the second frame exchange sequence");
 
  717                              +frameExchanges.at(0).begin()->front()->linkId,
 
  718                              "Expected the first two frame exchanges to occur on the same link");
 
  720        auto bAckRespIt = std::prev(secondExchangeIt->front());
 
  723                              "Expected a BlockAck response before the second frame exchange");
 
  725            bAckRespIt->startTx +
 
  727                                         bAckRespIt->txVector,
 
  728                                         m_apMac->GetWifiPhy(bAckRespIt->linkId)->GetPhyBand());
 
  732            bAckRespTxEnd + m_apMac->GetWifiPhy(bAckRespIt->linkId)->GetSifs(),
 
  733            secondExchangeIt->front()->startTx,
 
  734            "Expected the second frame exchange to start a SIFS after the first one");
 
  739                              "Expected an MU-RTS as ICF for the third frame exchange sequence");
 
  741            thirdExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
 
  743            "Expected a QoS data frame in the third frame exchange sequence");
 
  746            +secondExchangeIt->front()->linkId,
 
  747            +thirdExchangeIt->front()->linkId,
 
  748            "Expected the second and third frame exchanges to occur on distinct links");
 
  750        auto secondQosIt = secondExchangeIt->front();
 
  751        auto secondQosTxEnd =
 
  752            secondQosIt->startTx +
 
  754                                         secondQosIt->txVector,
 
  755                                         m_apMac->GetWifiPhy(secondQosIt->linkId)->GetPhyBand());
 
  758                                    secondQosTxEnd + m_transitionDelay.at(thirdExchangeStaId),
 
  759                                    "Transmission started before transition delay");
 
  765                              "Expected a fourth frame exchange");
 
  766        auto fourthExchangeIt = std::next(thirdExchangeIt);
 
  771                              "Expected an MU-RTS as ICF for the fourth frame exchange sequence");
 
  773        bAckRespIt = std::prev(fourthExchangeIt->front());
 
  776                              "Expected a BlockAck response before the fourth frame exchange");
 
  777        auto phy = m_apMac->GetWifiPhy(bAckRespIt->linkId);
 
  779                                                                           bAckRespIt->txVector,
 
  787                                    bAckRespTxEnd + 
phy->GetPifs(),
 
  788                                    "Transmission started less than a PIFS after BlockAck");
 
  790                              bAckRespTxEnd + 
phy->GetPifs() +
 
  792                              "Transmission started too much time after BlockAck");
 
  794        auto bAckReqIt = std::next(fourthExchangeIt->front(), 2);
 
  797                              "Expected a BlockAck request in the fourth frame exchange");
 
  801        frameExchanges.at(0).pop_front();
 
  802        frameExchanges.at(0).pop_front();
 
  803        frameExchanges.at(1).pop_front();
 
  804        frameExchanges.at(1).pop_front();
 
  805        frameExchanges.at(thirdExchangeStaId).pop_front();
 
  867    for (std::size_t i = 0; i < m_nEmlsrStations; i++)
 
  872        auto exchangeIt = frameExchanges.at(i).cbegin();
 
  874        auto linkIdOpt = m_staMacs[i]->GetLinkForPhy(m_mainPhyId);
 
  877                              "Didn't find a link on which the main PHY is operating");
 
  879        if (
IsTrigger(exchangeIt->front()->psduMap))
 
  883                                  "ICF was not sent on the expected link");
 
  886                                  "Expected no data frame in the first frame exchange sequence");
 
  887            frameExchanges.at(i).pop_front();
 
  892                                    "Expected at least 2 frame exchange sequences " 
  893                                        << 
"involving EMLSR client " << i);
 
  895        auto firstExchangeIt = frameExchanges.at(i).cbegin();
 
  896        auto secondExchangeIt = std::next(firstExchangeIt);
 
  898        const auto firstAmpduTxEnd =
 
  899            firstExchangeIt->back()->startTx +
 
  901                firstExchangeIt->back()->psduMap,
 
  902                firstExchangeIt->back()->txVector,
 
  903                m_staMacs[i]->GetWifiPhy(firstExchangeIt->back()->linkId)->GetPhyBand());
 
  904        const auto secondAmpduTxStart = secondExchangeIt->front()->startTx;
 
  907            firstExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
 
  909            "Expected a QoS data frame in the first frame exchange sequence");
 
  912                              "Expected one frame only in the first frame exchange sequence");
 
  915            secondExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
 
  917            "Expected a QoS data frame in the second frame exchange sequence");
 
  920                              "Expected one frame only in the second frame exchange sequence");
 
  922        if (m_staMacs[i]->GetNLinks() == m_emlsrLinks.size())
 
  927                +firstExchangeIt->front()->linkId,
 
  929                "First frame exchange expected to occur on link used to send EML OMN");
 
  932                +secondExchangeIt->front()->linkId,
 
  934                "Second frame exchange expected to occur on link used to send EML OMN");
 
  938                                  "A-MPDUs are not sent one after another");
 
  944                                  +secondExchangeIt->front()->linkId,
 
  945                                  "Frame exchanges expected to occur on distinct links");
 
  949                                  "A-MPDUs are not sent concurrently");
 
 
  957    std::optional<std::size_t> staId;
 
  960        if (
m_staMacs.at(
id)->GetLinkIdByAddress(address))
 
  971    for (uint8_t linkId = 0; linkId < 
m_apMac->GetNLinks(); linkId++)
 
  973        bool psModeExpected =
 
  975        auto addr = 
m_staMacs.at(*staId)->GetAddress();
 
  976        auto psMode = 
m_apMac->GetWifiRemoteStationManager(linkId)->IsInPsMode(addr);
 
  979                              "EMLSR link " << +linkId << 
" of EMLSR client " << *staId
 
  980                                            << 
" not in " << (psModeExpected ? 
"PS" : 
"active")
 
  986                         WifiQueueBlockedReason::POWER_SAVE_MODE,
 
  988                         "Checking PM mode after association on AP MLD for EMLSR client " +
 
  989                             std::to_string(*staId),
 
 
 1000    auto pkt = mpdu->GetPacket()->Copy();
 
 1001    const auto& hdr = mpdu->GetHeader();
 
 1004    pkt->RemoveHeader(frame);
 
 1006    std::optional<std::size_t> staId;
 
 1009        if (
m_staMacs.at(
id)->GetFrameExchangeManager(linkId)->GetAddress() == hdr.GetAddr1())
 
 1017                          "Not an address of an EMLSR client " << hdr.GetAddr1());
 
 1020    auto phy = 
m_apMac->GetWifiPhy(linkId);
 
 1026        m_staMacs.at(*staId)->GetWifiRemoteStationManager(linkId)->GetAckTxVector(hdr.GetAddr2(),
 
 1033        if (frame.m_emlControl.emlsrMode == 1)
 
 1037            for (const auto linkId : m_emlsrLinks)
 
 1039                auto addr = m_staMacs.at(*staId)->GetAddress();
 
 1040                auto psMode = m_apMac->GetWifiRemoteStationManager(linkId)->IsInPsMode(addr);
 
 1041                NS_TEST_EXPECT_MSG_EQ(psMode,
 
 1043                                      "EMLSR link " << +linkId << 
" of EMLSR client " << *staId
 
 1044                                                    << 
" not in active mode");
 
 1050                    WifiQueueBlockedReason::POWER_SAVE_MODE,
 
 1052                    "Checking EMLSR links on AP MLD after EMLSR mode is enabled on EMLSR client " +
 
 1053                        std::to_string(*staId),
 
 1062            for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
 
 1064                bool psModeExpected = id != linkId && m_emlsrLinks.count(id) == 1;
 
 1065                auto addr = m_staMacs.at(*staId)->GetAddress();
 
 1066                auto psMode = m_apMac->GetWifiRemoteStationManager(id)->IsInPsMode(addr);
 
 1067                NS_TEST_EXPECT_MSG_EQ(psMode,
 
 1070                                          << +id << 
" of EMLSR client " << *staId << 
" not in " 
 1071                                          << (psModeExpected ? 
"PS" : 
"active") << 
" mode");
 
 1077                    WifiQueueBlockedReason::POWER_SAVE_MODE,
 
 1079                    "Checking links on AP MLD after EMLSR mode is disabled on EMLSR client " +
 
 1080                        std::to_string(*staId),
 
 
 1093    auto pkt = mpdu->GetPacket()->Copy();
 
 1094    const auto& hdr = mpdu->GetHeader();
 
 1097    pkt->RemoveHeader(frame);
 
 1099    std::optional<std::size_t> staId;
 
 1102        if (
m_staMacs.at(
id)->GetFrameExchangeManager(linkId)->GetAddress() == hdr.GetAddr2())
 
 1110                          "Not an address of an EMLSR client " << hdr.GetAddr1());
 
 1112    auto phy = 
m_staMacs.at(*staId)->GetWifiPhy(linkId);
 
 1115        m_apMac->GetWifiRemoteStationManager(linkId)->GetAckTxVector(hdr.GetAddr2(), txVector);
 
 1119        m_staMacs.at(*staId)->GetWifiRemoteStationManager(linkId)->GetRtsTxVector(
 
 1130    auto timeToCfEnd = txDuration + phy->GetSifs() + ackDuration + phy->GetSifs() + cfEndDuration;
 
 1135        for (uint8_t 
id = 0; 
id < 
m_apMac->GetNLinks(); 
id++)
 
 1140                             WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 1141                             id != linkId && 
m_staMacs.at(*staId)->IsEmlsrLink(
id),
 
 1142                             "Checking links on EMLSR client " + std::to_string(*staId) +
 
 1143                                 " before the end of CF-End frame");
 
 1147                             WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 1148                             id != linkId && 
m_staMacs.at(*staId)->IsEmlsrLink(
id),
 
 1149                             "Checking links of EMLSR client " + std::to_string(*staId) +
 
 1150                                 " on the AP MLD before the end of CF-End frame");
 
 1156        for (uint8_t 
id = 0; 
id < 
m_apMac->GetNLinks(); 
id++)
 
 1158            if (
m_staMacs.at(*staId)->IsEmlsrLink(
id))
 
 1162                    m_staMacs.at(*staId)->GetAddress(),
 
 1163                    id && m_staMacs.at(*staId)->IsEmlsrLink(id),
 
 1164                    WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
 
 1166                    "Checking links of EMLSR client " + std::to_string(*staId) +
 
 1167                        " are all blocked on the AP MLD right after the end of CF-End");
 
 1174        for (uint8_t 
id = 0; 
id < 
m_apMac->GetNLinks(); 
id++)
 
 1176            if (
m_staMacs.at(*staId)->IsEmlsrLink(
id))
 
 1178                CheckBlockedLink(m_apMac,
 
 1179                                 m_staMacs.at(*staId)->GetAddress(),
 
 1181                                 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
 
 1183                                 "Checking links of EMLSR client " + std::to_string(*staId) +
 
 1184                                     " are all blocked on the AP MLD before the end of " 
 1185                                     "transition delay");
 
 1191        for (uint8_t 
id = 0; 
id < 
m_apMac->GetNLinks(); 
id++)
 
 1193            if (
m_staMacs.at(*staId)->IsEmlsrLink(
id))
 
 1195                CheckBlockedLink(m_apMac,
 
 1196                                 m_staMacs.at(*staId)->GetAddress(),
 
 1198                                 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
 
 1200                                 "Checking links of EMLSR client " + std::to_string(*staId) +
 
 1201                                     " are all unblocked on the AP MLD after the transition delay");
 
 
 1213    mpdu->GetPacket()->PeekHeader(trigger);
 
 1221                          "Did not expect an ICF before enabling EMLSR mode");
 
 1225                          "Unexpected preamble type for the Initial Control frame");
 
 1229                          "Unexpected rate for the Initial Control frame: " << rate);
 
 1232    Time maxPaddingDelay{};
 
 1234    for (
const auto& userInfo : trigger)
 
 1236        auto addr = 
m_apMac->GetMldOrLinkAddressByAid(userInfo.GetAid12());
 
 1239                              "AID " << userInfo.GetAid12() << 
" not found");
 
 1241        if (
m_apMac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*addr))
 
 1247                if (
m_staMacs.at(i)->GetAddress() == *addr)
 
 1255            for (uint8_t 
id = 0; 
id < 
m_apMac->GetNLinks(); 
id++)
 
 1257                if (!
m_apMac->GetWifiRemoteStationManager(
id)->GetEmlsrEnabled(*addr))
 
 1265                                 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 1267                                 "Checking that AP blocked transmissions on all other EMLSR " 
 1268                                 "links after sending ICF to client with AID=" +
 
 1269                                     std::to_string(userInfo.GetAid12()),
 
 1279                                                   m_apMac->GetWifiPhy(linkId)->GetPhyBand());
 
 1281    if (maxPaddingDelay.IsStrictlyPositive())
 
 1287        pkt->AddHeader(trigger);
 
 1288        auto txDurationWithout =
 
 1291                                         m_apMac->GetWifiPhy(linkId)->GetPhyBand());
 
 1294                              txDurationWithout + maxPaddingDelay,
 
 1295                              "Unexpected TX duration of the MU-RTS TF with padding " 
 1301    for (
const auto& userInfo : trigger)
 
 1305            if (
m_staMacs[i]->GetAssociationId() != userInfo.GetAid12())
 
 1313                for (uint8_t 
id = 0; 
id < 
m_staMacs[i]->GetNLinks(); 
id++)
 
 1319                                     WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 1320                                     id != linkId && 
m_staMacs[i]->IsEmlsrLink(
id),
 
 1321                                     "Checking EMLSR links on EMLSR client " + std::to_string(i) +
 
 1322                                         " after receiving ICF");
 
 1325                if (mainPhyLinkId != linkId)
 
 
 1351    std::size_t firstClientId = 0;
 
 1352    std::size_t secondClientId = 1;
 
 1353    auto addr = 
m_staMacs[secondClientId]->GetAddress();
 
 1363        m_apMac->GetDevice()->GetNode()->AddApplication(
 
 1366        for (std::size_t clientId : {firstClientId, secondClientId})
 
 1369                for (uint8_t 
id = 0; 
id < 
m_staMacs[clientId]->GetNLinks(); 
id++)
 
 1375                                     WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 1377                                     "Checking EMLSR links on EMLSR client " +
 
 1378                                         std::to_string(clientId) +
 
 1379                                         " after receiving the first QoS data frame");
 
 1386        m_apMac->GetDevice()->GetNode()->AddApplication(
 
 1391        for (std::size_t clientId : {firstClientId, secondClientId})
 
 1393            for (uint8_t 
id = 0; 
id < 
m_staMacs[clientId]->GetNLinks(); 
id++)
 
 1399                                 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 1401                                 "Checking EMLSR links on EMLSR client " +
 
 1402                                     std::to_string(clientId) +
 
 1403                                     " when starting the reception of the second QoS frame");
 
 1414            for (uint8_t 
id = 0; 
id < 
m_apMac->GetNLinks(); 
id++)
 
 1419                                 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 1421                                 "Checking that links of EMLSR client " +
 
 1422                                     std::to_string(secondClientId) +
 
 1423                                     " are blocked on the AP MLD before the end of the PPDU");
 
 1430            for (uint8_t 
id = 0; 
id < 
m_staMacs[secondClientId]->GetNLinks(); 
id++)
 
 1435                                 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 1437                                 "Checking that links of EMLSR client " +
 
 1438                                     std::to_string(secondClientId) +
 
 1439                                     " are unblocked before the end of the second QoS frame");
 
 1444            for (uint8_t 
id = 0; 
id < 
m_apMac->GetNLinks(); 
id++)
 
 1449                                 WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
 
 1451                                 "Checking links of EMLSR client " +
 
 1452                                     std::to_string(secondClientId) +
 
 1453                                     " are all blocked on the AP MLD after the end of the PPDU");
 
 1460                for (uint8_t 
id = 0; 
id < 
m_apMac->GetNLinks(); 
id++)
 
 1466                        WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
 
 1468                        "Checking links of EMLSR client " + std::to_string(secondClientId) +
 
 1469                            " are all blocked on the AP MLD before the transition delay",
 
 1483                                m_apMac->GetDevice()->GetNode()->AddApplication(
 
 1493            psduMap.cbegin()->second->GetAddr1(),
 
 1495            "QoS frame not addressed to a non-EMLSR client");
 
 1497        for (uint8_t 
id = 0; 
id < 
m_apMac->GetNLinks(); 
id++)
 
 1502                             WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
 
 1504                             "Checking links of EMLSR client " + std::to_string(secondClientId) +
 
 1505                                 " are all blocked on the AP MLD before the transition delay");
 
 1510        for (uint8_t 
id = 0; 
id < 
m_apMac->GetNLinks(); 
id++)
 
 1514                m_apMac->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED, addr, {
id});
 
 1520        for (uint8_t 
id = 0; 
id < 
m_apMac->GetNLinks(); 
id++)
 
 1522            m_apMac->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED, addr, {
id});
 
 1527            for (uint8_t 
id = 0; 
id < 
m_apMac->GetNLinks(); 
id++)
 
 1532                                 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 1534                                 "Checking EMLSR links on EMLSR client " +
 
 1535                                     std::to_string(secondClientId) +
 
 1536                                     " after receiving the fourth QoS data frame");
 
 
 1561    auto taddr = psduMap.cbegin()->second->GetAddr2();
 
 1562    std::size_t clientId;
 
 1563    if (
m_staMacs[0]->GetLinkIdByAddress(taddr))
 
 1571                              "Unexpected TA for BlockAck: " << taddr);
 
 1576    auto currMainPhyLinkId = 
m_staMacs[clientId]->GetLinkForPhy(phyId);
 
 1578        currMainPhyLinkId.has_value(),
 
 1580        "Didn't find the link on which the PHY sending the BlockAck is operating");
 
 1581    auto linkId = *currMainPhyLinkId;
 
 1584    auto addr = 
m_apMac->GetWifiRemoteStationManager(linkId)->GetMldAddress(taddr);
 
 1587    auto apPhy = 
m_apMac->GetWifiPhy(linkId);
 
 1593        apPhy->GetPhyBand());
 
 1606            for (uint8_t 
id = 0; 
id < 
m_apMac->GetNLinks(); 
id++)
 
 1611                                 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 1613                                 "Checking links on EMLSR client " + std::to_string(clientId) +
 
 1614                                     " at the end of fourth BlockAck");
 
 1618                                 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 1620                                 "Checking links of EMLSR client " + std::to_string(clientId) +
 
 1621                                     " on the AP MLD at the end of fourth BlockAck");
 
 1627            for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
 
 1629                CheckBlockedLink(m_staMacs[clientId],
 
 1630                                 m_apMac->GetAddress(),
 
 1632                                 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 1634                                 "Checking links on EMLSR client " + std::to_string(clientId) +
 
 1635                                     " a SIFS after the end of fourth BlockAck");
 
 1636                CheckBlockedLink(m_apMac,
 
 1639                                 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 1641                                 "Checking links of EMLSR client " + std::to_string(clientId) +
 
 1642                                     " a SIFS after the end of fourth BlockAck");
 
 1647            auto uid = psduMap.cbegin()->second->GetPacket()->GetUid();
 
 1655            for (uint8_t 
id = 0; 
id < 
m_apMac->GetNLinks(); 
id++)
 
 1660                                 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 1662                                 "Checking links on EMLSR client " + std::to_string(clientId) +
 
 1663                                     " at the end of fifth BlockAck");
 
 1667                                 WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 1669                                 "Checking links of EMLSR client " + std::to_string(clientId) +
 
 1670                                     " on the AP MLD at the end of fifth BlockAck");
 
 1676            txDuration + apPhy->GetSifs() + cfEndTxDuration - 
MicroSeconds(1),
 
 1678                for (uint8_t 
id = 0; 
id < 
m_apMac->GetNLinks(); 
id++)
 
 1683                                     WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 1685                                     "Checking links on EMLSR client " + std::to_string(clientId) +
 
 1686                                         " before the end of CF-End frame");
 
 1690                                     WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 1692                                     "Checking links of EMLSR client " + std::to_string(clientId) +
 
 1693                                         " on the AP MLD before the end of CF-End frame");
 
 1699            txDuration + apPhy->GetSifs() + cfEndTxDuration + 
MicroSeconds(1),
 
 1701                for (uint8_t 
id = 0; 
id < 
m_apMac->GetNLinks(); 
id++)
 
 1707                        WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
 
 1709                        "Checking links of EMLSR client " + std::to_string(clientId) +
 
 1710                            " are all blocked on the AP MLD right after the end of CF-End");
 
 1716            txDuration + apPhy->GetSifs() + cfEndTxDuration + 
m_transitionDelay.at(clientId) -
 
 1719                for (uint8_t 
id = 0; 
id < 
m_apMac->GetNLinks(); 
id++)
 
 1725                        WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
 
 1727                        "Checking links of EMLSR client " + std::to_string(clientId) +
 
 1728                            " are all blocked on the AP MLD before the end of transition delay");
 
 1733            txDuration + apPhy->GetSifs() + cfEndTxDuration + 
m_transitionDelay.at(clientId) +
 
 1736                for (uint8_t 
id = 0; 
id < 
m_apMac->GetNLinks(); 
id++)
 
 1742                        WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
 
 1744                        "Checking links of EMLSR client " + std::to_string(clientId) +
 
 1745                            " are all unblocked on the AP MLD after the transition delay");
 
 
 1765                              std::to_string(params.genBackoffIfTxopWithoutTx)),
 
 1766      m_emlsrLinks(params.linksToEnableEmlsrOn),
 
 1767      m_channelWidth(params.channelWidth),
 
 1768      m_auxPhyChannelWidth(params.auxPhyChannelWidth),
 
 1769      m_mediumSyncDuration(params.mediumSyncDuration),
 
 1770      m_msdMaxNTxops(params.msdMaxNTxops),
 
 1771      m_emlsrEnabledTime(0),
 
 1772      m_firstUlPktsGenTime(0),
 
 1774      m_checkBackoffStarted(false),
 
 1775      m_countQoSframes(0),
 
 1777      m_countRtsframes(0),
 
 1778      m_genBackoffIfTxopWithoutTx(params.genBackoffIfTxopWithoutTx)
 
 1794                    "This test requires at least two links to be configured as EMLSR links");
 
 1795    for (uint8_t 
id = 0; 
id < 3; 
id++)
 
 
 1844        for (
auto mac : std::initializer_list<Ptr<WifiMac>>{
m_apMac, 
m_staMacs[0]})
 
 1846            mac->GetWifiPhy(linkId)->SetOperatingChannel(
 
 
 1861    NS_LOG_INFO(
"Backoff value " << backoff << 
" generated by EMLSR client on link " << +linkId
 
 1877                "Another backoff value should not be generated while the main PHY link is blocked");
 
 1881                                  "Backoff generated at unexpected time");
 
 1897                               m_staMacs[0]->GetChannelAccessManager(linkId)->GetSifs() +
 
 1899                                   m_staMacs[0]->GetChannelAccessManager(linkId)->GetSlot();
 
 1910            backoff * 
m_staMacs[0]->GetChannelAccessManager(linkId)->GetSlot();
 
 
 1925    auto psdu = psduMap.begin()->second;
 
 1926    auto nodeId = mac->GetDevice()->GetNode()->GetId();
 
 1928    switch (psdu->GetHeader(0).GetType())
 
 1931        NS_ASSERT_MSG(nodeId > 0, 
"APs do not send AssocReq frames");
 
 
 1959    auto auxPhyLinks = 
m_staMacs[0]->GetSetupLinkIds();
 
 1965    m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
 
 1977        std::set<uint8_t> linkIds;
 
 1983        m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
 
 1987        NS_LOG_INFO(
"Enqueuing two packets at the EMLSR client\n");
 
 1992            m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
 
 1994                                                  {*m_staMacs[0]->GetLinkForPhy(m_mainPhyId)});
 
 
 2022            NS_LOG_INFO(
"Enqueuing two packets at the EMLSR client\n");
 
 2023            m_staMacs[0]->GetDevice()->GetNode()->AddApplication(
 
 2028                m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
 
 2038            auto macHdrSize = (*psduMap.at(
SU_STA_ID)->begin())->GetHeader().GetSerializedSize() +
 
 2043            for (
auto id : 
m_staMacs[0]->GetLinkIds())
 
 2049                    WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 2051                    "Checking EMLSR links on EMLSR client while sending the first data frame",
 
 2058                                     WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 2061                                     "Checking EMLSR links on AP MLD right after receiving the MAC " 
 2062                                     "header of the first data frame");
 
 2070                        WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 2073                        "Checking EMLSR links on AP MLD after sending the first data frame");
 
 2089            auto auxPhyLinks = 
m_staMacs[0]->GetSetupLinkIds();
 
 2095            m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
 
 2100            m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
 
 2102                                                {*m_staMacs[0]->GetLinkForPhy(m_mainPhyId)});
 
 2106            NS_LOG_INFO(
"Enqueuing two packets at the EMLSR client\n");
 
 2107            m_staMacs[0]->GetDevice()->GetNode()->AddApplication(
 
 2114            for (
auto id : 
m_staMacs[0]->GetLinkIds())
 
 2120                    WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 2121                    id != linkId && 
m_staMacs[0]->IsEmlsrLink(
id),
 
 2122                    "Checking EMLSR links on EMLSR client while sending the second data frame",
 
 2129                    WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 2130                    id != linkId && 
m_staMacs[0]->IsEmlsrLink(
id),
 
 2131                    "Checking EMLSR links on AP MLD while sending the second data frame",
 
 2136            m_staMacs[0]->GetMacQueueScheduler()->UnblockQueues(
 
 2137                WifiQueueBlockedReason::TID_NOT_MAPPED,
 
 
 2156    auto auxPhyLinks = 
m_staMacs[0]->GetSetupLinkIds();
 
 2215                m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
 
 2238                    elapsed.has_value(),
 
 2240                    "MediumSyncDelay timer not running on link where main PHY is operating");
 
 2242                                      m_staMacs[0]->GetEmlsrManager()->GetMediumSyncDuration() -
 
 2251                                  "Backoff end time should have been calculated");
 
 2259        m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
 
 2265        NS_LOG_INFO(
"Enqueuing two packets at the EMLSR client\n");
 
 2276        m_staMacs[0]->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
 
 2285        m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
 
 2290        NS_LOG_INFO(
"Enqueuing two packets at the EMLSR client\n");
 
 
 2327                          "RTS sent by main PHY on an unexpected width");
 
 
 2347                                                   m_apMac->GetWifiPhy(linkId)->GetPhyBand());
 
 2351        mpdu->GetHeader().GetAddr1() == 
m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress())
 
 2356        const auto auxPhy = 
m_staMacs[0]->GetWifiPhy(linkId);
 
 2362                                  "Expecting the main PHY to be switching link");
 
 2365                                  "Aux PHY on link " << +linkId << 
" already in sleep mode");
 
 2378                    m_staMacs[0]->GetFrameExchangeManager(linkId));
 
 2379                doCorruptCts && !ehtFem->UsingOtherEmlsrLink())
 
 2385                    traceInfoIt->second->GetName() == 
"CtsAfterRtsTimeout")
 
 2387                    const auto& traceInfo =
 
 2391                                          "Expected non-zero remaining time because main PHY " 
 2392                                          "was switching when CTS timeout occurred");
 
 
 2428            "Unexpected number of RTS frames sent while the MediumSyncDelay timer is running");
 
 2435    auto jumpToQosDataOrMuRts = [&]() {
 
 2437               !psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData() &&
 
 2438               !psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts())
 
 2440            auto psdu = psduIt->psduMap.cbegin()->second;
 
 2441            if (psdu->GetHeader(0).IsTrigger())
 
 2444                psdu->GetPayload(0)->PeekHeader(trigger);
 
 2529            psduIt->psduMap.cbegin()->second->GetHeader(0).IsBeacon()))
 
 2539                          "First QoS data frame has not been transmitted");
 
 2542                          "First QoS data frame should be transmitted without protection");
 
 2545                          "First QoS data frame should be transmitted by the main PHY");
 
 2548                                "First QoS data frame sent too early");
 
 2550    auto prevPsduIt = psduIt++;
 
 2551    jumpToQosDataOrMuRts();
 
 2559            "Expected another QoS data frame sent concurrently with the first frame");
 
 2561            psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
 
 2563            "First data frame on non-EMLSR link should be transmitted without protection");
 
 2566                              "First data frame expected to be transmitted on the non-EMLSR link");
 
 2567        const auto txDuration =
 
 2569                                         prevPsduIt->txVector,
 
 2570                                         m_staMacs[0]->GetWifiPhy(prevPsduIt->phyId)->GetPhyBand());
 
 2572                              prevPsduIt->startTx + txDuration,
 
 2573                              "First data frame on the non-EMLSR link not sent concurrently");
 
 2575        jumpToQosDataOrMuRts();
 
 2583                          "RTS before second QoS data frame has not been transmitted");
 
 2586                          "Second QoS data frame should be transmitted with protection");
 
 2590        "RTS before second QoS data frame should not be transmitted by the main PHY");
 
 2593                          "RTS before second data frame transmitted on an unexpected width");
 
 2598                          "CTS before second QoS data frame has not been transmitted");
 
 2601                          "CTS before second QoS data frame has not been transmitted");
 
 2606                          "Second QoS data frame has not been transmitted");
 
 2609                          "Second QoS data frame has not been transmitted");
 
 2612                          "Second QoS data frame should be transmitted by the main PHY");
 
 2615                          "Second data frame not transmitted on the same width as RTS");
 
 2617    bool moreQosDataFound = 
false;
 
 2621        jumpToQosDataOrMuRts();
 
 2623            psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData())
 
 2625            moreQosDataFound = 
true;
 
 2629                                  "Third QoS data frame should be transmitted by the main PHY");
 
 2632                                  "Expecting TX width of third data frame to equal the channel " 
 2633                                  "width used by the main PHY");
 
 2637                "Third QoS data frame sent before MediumSyncDelay timer expired");
 
 2645                          "Third QoS data frame transmitted by the main PHY not found");
 
 2649    jumpToQosDataOrMuRts();
 
 2655                          "RTS before fourth QoS data frame has not been transmitted");
 
 2658                          "Fourth QoS data frame should be transmitted with protection");
 
 2662        "RTS before fourth QoS data frame should not be transmitted by the main PHY");
 
 2665                          "RTS before fourth data frame transmitted on an unexpected width");
 
 2670                          "CTS before fourth QoS data frame has not been transmitted");
 
 2673                          "CTS before fourth QoS data frame has not been transmitted");
 
 2675    jumpToQosDataOrMuRts();
 
 2682                          "RTS before fourth QoS data frame has not been transmitted");
 
 2685                          "Fourth QoS data frame should be transmitted with protection");
 
 2689        "RTS before fourth QoS data frame should not be transmitted by the main PHY");
 
 2692                          "RTS before fourth data frame transmitted on an unexpected width");
 
 2697                          "CTS before fourth QoS data frame has not been transmitted");
 
 2700                          "CTS before fourth QoS data frame has not been transmitted");
 
 2705                          "Fourth QoS data frame has not been transmitted");
 
 2708                          "Fourth QoS data frame has not been transmitted");
 
 2711                          "Fourth QoS data frame should be transmitted by the main PHY");
 
 2714                          "Fourth data frame not transmitted on the same width as RTS");
 
 
 2719      m_enableBsrp(enableBsrp),
 
 
 2746    m_apMac->AggregateObject(muScheduler);
 
 
 2759    auto psdu = psduMap.begin()->second;
 
 2761    switch (psdu->GetHeader(0).GetType())
 
 2771                                                           mac->GetWifiPhy(linkId)->GetPhyBand());
 
 2780                    for (
const auto id : 
m_staMacs[0]->GetLinkIds())
 
 2783                            m_staMacs[0]->GetFrameExchangeManager(
id));
 
 2785                            ehtFem->UsingOtherEmlsrLink(),
 
 2787                            "Link " << +
id << 
" was" << (
id == linkId ? 
" not" : 
"")
 
 2788                                    << 
" expected to be blocked on EMLSR client at time " 
 2792                    m_staMacs[0]->GetDevice()->GetNode()->AddApplication(
 
 2802            psdu->GetPayload(0)->PeekHeader(blockAck);
 
 2808                                                 mac->GetWifiPhy(linkId)->GetPhyBand());
 
 2817    if (psdu->GetHeader(0).IsCfEnd())
 
 
 2844    muScheduler->SetAccessReqInterval(interval);
 
 
 2923                              "Expected a Trigger Frame");
 
 2925        m_txPsdus[index].psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
 
 2929                         : (index == 
m_txPsdusPos ? TriggerFrameType::MU_RTS_TRIGGER
 
 2930                                                  : TriggerFrameType::BASIC_TRIGGER);
 
 2932                              +
static_cast<uint8_t
>(triggerType),
 
 2933                              "Unexpected Trigger Frame type on link " << +
m_txPsdus[index].linkId);
 
 2939            "Unexpected number of User Info fields for Trigger Frame, index=" << index);
 
 2942    auto startIndex = index;
 
 2943    std::size_t ctsCount = 0;
 
 2944    std::size_t qosNullCount = 0;
 
 2946    for (; index < startIndex + 4; ++index)
 
 2948        const auto& hdr = 
m_txPsdus[index].psduMap.cbegin()->second->GetHeader(0);
 
 2956        if (hdr.IsQosData() && !hdr.HasData())
 
 2962                hdr.GetAddr2() == 
m_staMacs[0]->GetFrameExchangeManager(firstLinkId)->GetAddress())
 
 2976                          "Unexpected number of QoS Null frames");
 
 2982                          "Expected a Trigger Frame");
 
 2985                          "Unexpected link ID for Basic TF");
 
 2987    m_txPsdus[index].psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
 
 2990                          +
static_cast<uint8_t
>(TriggerFrameType::BASIC_TRIGGER),
 
 2991                          "Unexpected Trigger Frame type");
 
 2999                          "Unexpected number of User Info fields for Basic Trigger Frame");
 
 3002    startIndex = ++index;
 
 3003    for (; index < startIndex + (
m_enableBsrp ? 1 : 2); ++index)
 
 3005        const auto& hdr = 
m_txPsdus[index].psduMap.cbegin()->second->GetHeader(0);
 
 3012            (hdr.GetAddr2() == 
m_staMacs[0]->GetFrameExchangeManager(firstLinkId)->GetAddress()),
 
 3013            "Unexpected type of QoS data frame");
 
 3019                                  "QoS Data frame should be sent in a TB PPDU");
 
 3026                          "Expected a BlockAck frame");
 
 3028    m_txPsdus[index].psduMap.cbegin()->second->GetPayload(0)->PeekHeader(blockAck);
 
 
 3035    for (
const auto& emlsrLinks :
 
 3036         {std::set<uint8_t>{0, 1, 2}, std::set<uint8_t>{1, 2}, std::set<uint8_t>{0, 1}})
 
 3045                    TestCase::Duration::QUICK);
 
 3053                    TestCase::Duration::QUICK);
 
 3061                    TestCase::Duration::QUICK);
 
 3064    for (
auto genBackoffIfTxopWithoutTx : {
true, 
false})
 
 3071                                         genBackoffIfTxopWithoutTx,
 
 3074                    TestCase::Duration::QUICK);
 
 3080                                         genBackoffIfTxopWithoutTx,
 
 3083                    TestCase::Duration::QUICK);
 
 
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)
Base class for EMLSR Operations tests.
std::size_t m_nNonEmlsrStations
number of stations to create that do not activate EMLSR
std::vector< uint8_t > m_establishBaDl
the TIDs for which BA needs to be established 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< 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
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
Ptr< PacketSocketClient > GetApplication(TrafficDirection dir, std::size_t staId, std::size_t count, std::size_t pktSize, uint8_t priority=0) const
std::vector< FrameInfo > m_txPsdus
transmitted PSDUs
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.
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.
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.
std::vector< uint8_t > m_establishBaUl
the TIDs for which BA needs to be established with the AP as recipient
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
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.
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.
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.
wifi EMLSR suite to test basic frame exchanges.
WifiEmlsrBasicExchangesTestSuite()
A container for one type of attribute.
AttributeValue implementation for Boolean.
Class for representing data rates.
Time CalculateBytesTxTime(uint32_t bytes) const
Calculate transmission time.
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.
EmlControl m_emlControl
EML Control field.
MultiUserScheduler is an abstract base class defining the API that APs supporting at least VHT can us...
Smart pointer class similar to boost::intrusive_ptr.
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.
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.
bool IsZero() const
Exactly equivalent to t == 0.
AttributeValue implementation for Time.
Hold an unsigned integer type.
uint64_t GetDataRate(MHz_u channelWidth, Time guardInterval, uint8_t nss) const
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)
#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.
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.
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.
const Time MAX_PROPAGATION_DELAY
maximum propagation delay
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.
@ 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)
Parameters for the EMLSR DL TXOP test.
Parameters for the EMLSR UL TXOP test.
Struct to trace that main PHY started switching after a CTS timeout occurred on the link on which an ...
uint8_t emlsrMode
EMLSR Mode.
static WifiEmlsrBasicExchangesTestSuite g_wifiEmlsrBasicExchangesTestSuite
the test suite