16#include "ns3/ap-wifi-mac.h" 
   18#include "ns3/mgt-action-headers.h" 
   19#include "ns3/spectrum-signal-parameters.h" 
   20#include "ns3/sta-wifi-mac.h" 
   21#include "ns3/wifi-mac-queue.h" 
   22#include "ns3/wifi-net-device.h" 
   23#include "ns3/wifi-spectrum-phy-interface.h" 
   27#undef NS_LOG_APPEND_CONTEXT 
   28#define NS_LOG_APPEND_CONTEXT WIFI_FEM_NS_LOG_APPEND_CONTEXT 
   56    static TypeId tid = 
TypeId(
"ns3::EhtFrameExchangeManager")
 
   58                            .AddConstructor<EhtFrameExchangeManager>()
 
   59                            .SetGroupName(
"Wifi");
 
 
   95        protectionManager->SetLinkId(linkId);
 
   99        ackManager->SetLinkId(linkId);
 
 
  113        !mpdu->GetHeader().IsQosData() ||
 
  115        mpdu->GetHeader().GetAddr1().IsGroup() ||
 
  122    auto& hdr = mpdu->GetHeader();
 
  126    hdr.SetAddr1(*address);
 
  134    if (hdr.IsQosAmsdu())
 
  136        if (hdr.IsToDs() && !hdr.IsFromDs())
 
  139            hdr.SetAddr3(hdr.GetAddr1());
 
  141        else if (!hdr.IsToDs() && hdr.IsFromDs())
 
  144            hdr.SetAddr3(hdr.GetAddr2());
 
 
  158    return m_staMac->GetMacQueueScheduler()->GetAllQueuesBlockedOnLink(
 
 
  172        for (uint8_t linkId = 0; linkId < 
m_apMac->GetNLinks(); linkId++)
 
  180            std::set<Mac48Address> emlsrClients;
 
  185            if (ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder &&
 
  186                m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(
 
  187                    ehtFem->m_txopHolder.value()))
 
  189                NS_LOG_DEBUG(
"Involved in UL TXOP: " << ehtFem->m_txopHolder.value());
 
  190                emlsrClients.insert(ehtFem->m_txopHolder.value());
 
  194            for (
const auto& address : ehtFem->m_protectedStas)
 
  196                if (
m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(address))
 
  199                    emlsrClients.insert(address);
 
  203            for (
const auto& address : emlsrClients)
 
  206                    m_mac->GetWifiRemoteStationManager(linkId)->GetMldAddress(address);
 
  207                NS_ASSERT_MSG(mldAddress, 
"MLD address not found for " << address);
 
  220                              "No mask for client " << *mldAddress << 
" on link " << +
m_linkId);
 
  225                                  "Transmissions to " << *mldAddress << 
" on link " << +
m_linkId 
  226                                                      << 
" are not blocked");
 
  237    std::optional<Time> timeToCtsEnd;
 
  244            NS_LOG_DEBUG(
"StartTransmission called while another EMLSR link is being used");
 
  249        auto emlsrManager = 
m_staMac->GetEmlsrManager();
 
  251        if (
auto elapsed = emlsrManager->GetElapsedMediumSyncDelayTimer(
m_linkId);
 
  252            elapsed && emlsrManager->MediumSyncDelayNTxopsExceeded(
m_linkId))
 
  254            NS_LOG_DEBUG(
"No new TXOP attempts allowed while MediumSyncDelay is running");
 
  258                emlsrManager->GetMediumSyncDuration() - *elapsed,
 
  277        if (
const auto [startTxop, delay] = emlsrManager->GetDelayUntilAccessRequest(
 
  283            if (delay.IsStrictlyPositive())
 
 
  339        auto sigBMode = phy->GetSigBMode(txVector);
 
  345    if (
m_apMac && psdu->GetHeader(0).IsTrigger())
 
  357            for (uint8_t linkId = 0; linkId < 
m_apMac->GetNLinks(); ++linkId)
 
  360                    m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*clientMld))
 
  371        psdu->GetHeader(0).IsRts())
 
  382        auto delay = 
m_apMac->GetApEmlsrManager()->GetDelayOnTxPsduNotForEmlsr(psdu,
 
  405             m_staMac->GetEmlsrManager()->GetInDeviceInterference())
 
  408        m_staMac->GetEmlsrManager()->NotifyInDeviceInterferenceStart(
m_linkId, txDuration);
 
 
  429            psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
 
  434                for (
const auto& client : recipients)
 
  444                    for (uint8_t linkId = 0; linkId < 
m_apMac->GetNLinks(); ++linkId)
 
  447                            m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*clientMld))
 
  449                            m_mac->BlockUnicastTxOnLinks(
 
  464            const auto psduMapIt = psduMap.find(aid);
 
  465            const auto aidNotFoundAndNotTf = (psduMapIt == psduMap.cend()) && !
IsTrigger(psduMap);
 
  467            const auto psdu = (psduMapIt != psduMap.cend() ? psduMapIt : psduMap.cbegin())->
second;
 
  483             m_staMac->GetEmlsrManager()->GetInDeviceInterference())
 
  486        m_staMac->GetEmlsrManager()->NotifyInDeviceInterferenceStart(
m_linkId, txDuration);
 
 
  500    for (
const auto& phy : 
m_staMac->GetDevice()->GetPhys())
 
  506        if (
auto id = 
m_staMac->GetLinkForPhy(phy);
 
  509            const auto txPower = phy->GetPower(txVector.
GetTxPowerLevel()) + phy->GetTxGain();
 
 
  533    for (
const auto& [range, interface] : rxPhy->GetSpectrumPhyInterfaces())
 
  535        if (!interface->GetRxSpectrumModel())
 
  547        spectrumSignalParams->duration = duration;
 
  548        spectrumSignalParams->txPhy = txPhy->GetCurrentInterface();
 
  549        spectrumSignalParams->txAntenna = txPhy->GetAntenna();
 
  550        spectrumSignalParams->psd = psd;
 
  552        rxPhy->StartRx(spectrumSignalParams, interface);
 
 
  586    NS_ASSERT_MSG(mldAddress, 
"MLD address not found for " << address);
 
  588    std::set<uint8_t> linkIds{
m_linkId};
 
  617    for (uint8_t linkId = 0; linkId < 
m_apMac->GetNLinks(); ++linkId)
 
  619        if (!
m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
 
  626        if (ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder &&
 
  627            m_mac->GetWifiRemoteStationManager(linkId)->GetMldAddress(*ehtFem->m_txopHolder) ==
 
  630            NS_LOG_DEBUG(
"EMLSR client " << *mldAddress << 
" is the holder of an UL TXOP on link " 
  631                                         << +linkId << 
", do not unblock links");
 
  635        if (linkId == 
m_linkId && !checkThisLink)
 
  640        linkIds.insert(linkId);
 
  643                m_apMac->GetWifiRemoteStationManager(linkId)->GetAffiliatedStaAddress(*mldAddress);
 
  645            (ehtFem->m_sentRtsTo.contains(*linkAddr) || ehtFem->m_sentFrameTo.contains(*linkAddr) ||
 
  646             ehtFem->m_protectedStas.contains(*linkAddr)))
 
  649                                         << 
" has been sent an ICF, do not unblock links");
 
 
  667    NS_ASSERT_MSG(mldAddress, 
"MLD address not found for " << address);
 
  670    auto blockLinks = [=, 
this](
bool checkThisLink) {
 
  673            NS_LOG_DEBUG(
"Could not unblock transmissions to " << address);
 
  678        std::set<uint8_t> linkIds;
 
  679        for (uint8_t linkId = 0; linkId < 
m_mac->GetNLinks(); linkId++)
 
  681            if (
m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
 
  683                linkIds.insert(linkId);
 
  692        auto unblockLinks = [=, 
this]() {
 
  702            emlCapabilities->get().emlsrTransitionDelay);
 
  704        endDelay.IsZero() ? unblockLinks()
 
  717    delay.
IsZero() ? blockLinks(
false)
 
 
  741    m_staMac->NotifySwitchingEmlsrLink(phy, linkId, delay);
 
 
  758    const auto sequence = 
m_txMiddle->GetNextSequenceNumberFor(&hdr);
 
  767    packet->AddHeader(frame);
 
  768    packet->AddHeader(actionHdr);
 
 
  792    for (uint8_t linkId = 0; linkId < 
m_mac->GetNLinks(); linkId++)
 
  794        std::optional<Mac48Address> linkAddress;
 
  796            (linkAddress = 
m_mac->GetWifiRemoteStationManager(linkId)->GetAffiliatedStaAddress(
 
  798            (optRssi = 
m_mac->GetWifiRemoteStationManager(linkId)->GetMostRecentRssi(*linkAddress)))
 
 
  820    uint8_t maxPaddingDelay = 0;
 
  821    bool isUnprotectedEmlsrDst = 
false;
 
  823    for (
const auto& address : recipients)
 
  831        isUnprotectedEmlsrDst = 
true;
 
  834        maxPaddingDelay = std::max(maxPaddingDelay, emlCapabilities->get().emlsrPaddingDelay);
 
  837    if (isUnprotectedEmlsrDst)
 
  846    if (maxPaddingDelay > 0)
 
  850        std::size_t nDbps = rate / 1e6 * 4; 
 
 
  875        auto mainPhy = 
m_staMac->GetDevice()->GetPhy(
m_staMac->GetEmlsrManager()->GetMainPhyId());
 
  884        if (mainPhy->IsStateSwitching() || 
m_mac->GetLinkForPhy(mainPhy) != 
m_linkId)
 
  886            NS_LOG_DEBUG(
"Main PHY is switching or operating on another link, abort ICF response");
 
 
  925    for (
const auto& address : clients)
 
 
  953        if (
const auto apEmlsrManager = 
m_apMac->GetApEmlsrManager();
 
  956            return apEmlsrManager->UpdateCwAfterFailedIcf();
 
 
  970        if (
const auto apEmlsrManager = 
m_apMac->GetApEmlsrManager();
 
  973            return apEmlsrManager->ReportFailedIcf();
 
 
  988    if (staMissedTbPpduFrom.size() != nSolicitedStations)
 
  995    const auto apEmlsrManager = 
m_apMac->GetApEmlsrManager();
 
  996    const auto updateFailedCw =
 
  997        crossLinkCollision && apEmlsrManager ? apEmlsrManager->UpdateCwAfterFailedIcf() : 
true;
 
 
 1003                                                  std::size_t nSolicitedStations)
 
 1009    if (staMissedTbPpduFrom.size() != nSolicitedStations)
 
 
 1021    const std::set<Mac48Address>& staMissedResponseFrom)
 const 
 1027    auto crossLinkCollision = 
true;
 
 1032    for (
const auto& address : staMissedResponseFrom)
 
 1036            crossLinkCollision = 
false;
 
 1043        std::set<uint8_t> linkIds; 
 
 1044        for (uint8_t linkId = 0; linkId < 
m_apMac->GetNLinks(); linkId++)
 
 1046            if (
m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress) &&
 
 1049                linkIds.insert(linkId);
 
 1053        if (std::any_of(linkIds.cbegin(),
 
 1056                        [=, 
this](uint8_t 
id) {
 
 1057                            auto ehtFem = StaticCast<EhtFrameExchangeManager>(
 
 1058                                m_mac->GetFrameExchangeManager(id));
 
 1059                            return ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder &&
 
 1060                                   m_mac->GetMldAddress(ehtFem->m_txopHolder.value()) == mldAddress;
 
 1070        if (std::none_of(linkIds.cbegin(),
 
 1074                         [=, 
this](uint8_t 
id) {
 
 1075                             auto macHdr = m_mac->GetFrameExchangeManager(id)->GetReceivedMacHdr();
 
 1076                             if (!macHdr.has_value())
 
 1080                             auto addr2 = macHdr->get().GetAddr2();
 
 1081                             return m_mac->GetMldAddress(addr2) == mldAddress;
 
 1084            crossLinkCollision = 
false;
 
 1088    return crossLinkCollision;
 
 
 1100    if (m_apMac && GetWifiRemoteStationManager()->GetEmlsrEnabled(addr2))
 
 1105        auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(addr2);
 
 1106        NS_ASSERT_MSG(mldAddress, 
"MLD address not found for " << addr2);
 
 1108        for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
 
 1110            if (linkId != m_linkId &&
 
 1111                m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
 
 1116                    m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(
AC_BE, queueId, linkId);
 
 1117                NS_ASSERT_MSG(mask, 
"No mask for client " << *mldAddress << 
" on link " << +linkId);
 
 1119                        static_cast<std::size_t
>(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK)))
 
 1122                                  "Transmissions to " << *mldAddress << 
" on link " << +linkId
 
 1123                                                      << 
" are not blocked");
 
 1126                    m_mac->BlockUnicastTxOnLinks(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 1134    HeFrameExchangeManager::SendCtsAfterRts(rtsHdr, rtsTxVector, rtsSnr);
 
 
 1150    if (psdu->GetAddr1() == address)
 
 1159        if (mpdu->GetHeader().IsTrigger())
 
 1162            mpdu->GetPacket()->PeekHeader(trigger);
 
 1172    if (psdu->GetHeader(0).IsCts())
 
 1174        if (m_apMac && psdu->GetAddr1() == m_self)
 
 1178        if (m_staMac && psdu->GetAddr1() == m_bssid)
 
 1186    if (psdu->GetHeader(0).IsBlockAck())
 
 1189        psdu->GetPayload(0)->PeekHeader(blockAck);
 
 
 1204EhtFrameExchangeManager::TransmissionSucceeded()
 
 1208    if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
 
 1209        m_staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_linkId))
 
 1211        NS_LOG_DEBUG(
"Reset the counter of TXOP attempts allowed while " 
 1212                     "MediumSyncDelay is running");
 
 1213        m_staMac->GetEmlsrManager()->ResetMediumSyncDelayNTxops(m_linkId);
 
 1216    HeFrameExchangeManager::TransmissionSucceeded();
 
 
 1220EhtFrameExchangeManager::TransmissionFailed(
bool forceCurrentCw)
 
 1224    if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
 
 1225        m_staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_linkId))
 
 1227        NS_LOG_DEBUG(
"Decrement the remaining number of TXOP attempts allowed while " 
 1228                     "MediumSyncDelay is running");
 
 1229        m_staMac->GetEmlsrManager()->DecrementMediumSyncDelayNTxops(m_linkId);
 
 1232    HeFrameExchangeManager::TransmissionFailed(forceCurrentCw);
 
 
 1236EhtFrameExchangeManager::NotifyChannelReleased(
Ptr<Txop> txop)
 
 1247        if (
const auto remTxNav = m_txNav - Simulator::Now(); remTxNav.IsStrictlyPositive())
 
 1252        for (
const auto& address : m_protectedStas)
 
 1254            if (GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
 
 1256                EmlsrSwitchToListening(address, delay);
 
 1260    else if (m_staMac && m_staMac->IsEmlsrLink(m_linkId))
 
 1263        auto edca = DynamicCast<QosTxop>(txop);
 
 1267        m_staMac->GetEmlsrManager()->NotifyTxopEnd(m_linkId, edca);
 
 1270    HeFrameExchangeManager::NotifyChannelReleased(txop);
 
 
 1281    if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
 
 1282        m_staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_linkId))
 
 1284        m_staMac->GetEmlsrManager()->CancelMediumSyncDelayTimer(m_linkId);
 
 1296        for (
auto clientIt = m_protectedStas.begin(); clientIt != m_protectedStas.end();)
 
 1305            if (GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt) && !txVector.
IsUlMu() &&
 
 1306                !m_txTimer.GetStasExpectedToRespond().contains(*clientIt))
 
 1308                EmlsrSwitchToListening(*clientIt, 
Seconds(0));
 
 1310                clientIt = m_protectedStas.erase(clientIt);
 
 1319    HeFrameExchangeManager::PreProcessFrame(psdu, txVector);
 
 
 1327    HeFrameExchangeManager::PostProcessFrame(psdu, txVector);
 
 1329    if (m_apMac && m_apMac->GetApEmlsrManager())
 
 1331        m_apMac->GetApEmlsrManager()->NotifyPsduRxOk(m_linkId, psdu);
 
 1334    if (m_apMac && m_txopHolder == psdu->GetAddr2() &&
 
 1335        GetWifiRemoteStationManager()->GetEmlsrEnabled(*m_txopHolder))
 
 1337        const auto unrespondedRts = (psdu->GetHeader(0).IsRts() && !m_sendCtsEvent.IsPending());
 
 1339        if (!m_ongoingTxopEnd.IsPending() && !unrespondedRts)
 
 1344                Simulator::ScheduleNow(&EhtFrameExchangeManager::TxopEnd, 
this, m_txopHolder);
 
 1347        UpdateTxopEndOnRxEnd(psdu->GetDuration());
 
 1350    if (m_staMac && m_ongoingTxopEnd.IsPending())
 
 1352        if (GetEmlsrSwitchToListening(psdu, m_staMac->GetAssociationId(), m_self))
 
 1355            m_ongoingTxopEnd.Cancel();
 
 1356            m_staMac->GetEmlsrManager()->NotifyTxopEnd(m_linkId);
 
 1360            UpdateTxopEndOnRxEnd(psdu->GetDuration());
 
 1364    if (m_staMac && m_dlTxopStart)
 
 1368        m_ongoingTxopEnd.Cancel();
 
 1369        NS_LOG_DEBUG(
"Expected TXOP end=" << (Simulator::Now() + m_phy->GetSifs()).As(Time::S));
 
 1370        m_ongoingTxopEnd = Simulator::Schedule(m_phy->GetSifs() + TimeStep(1),
 
 1371                                               &EhtFrameExchangeManager::TxopEnd,
 
 1375        m_staMac->GetEmlsrManager()->NotifyDlTxopStart(m_linkId);
 
 1376        m_dlTxopStart = 
false;
 
 
 1388    if (m_ongoingTxopEnd.IsPending())
 
 1394    if (
auto holder = FindTxopHolder(hdr, txVector); holder != sender)
 
 1396        NS_LOG_DEBUG(
"Sender (" << sender << 
") differs from the TXOP holder (" 
 1401    if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(sender))
 
 1403        NS_LOG_DEBUG(
"Sender (" << sender << 
") is not an EMLSR client");
 
 1407    NS_LOG_DEBUG(
"EMLSR client " << sender << 
" is starting a TXOP");
 
 1410    auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(sender);
 
 1413    for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
 
 1415        if (linkId != m_linkId &&
 
 1416            m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
 
 1418            m_mac->BlockUnicastTxOnLinks(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 1426                m_mac->GetWifiRemoteStationManager(linkId)->GetAffiliatedStaAddress(*mldAddress);
 
 1429                StaticCast<EhtFrameExchangeManager>(m_mac->GetFrameExchangeManager(linkId));
 
 1430            NS_LOG_DEBUG(
"Remove " << *linkAddr << 
" from protected STAs");
 
 1431            ehtFem->m_protectedStas.erase(*linkAddr);
 
 1432            ehtFem->m_sentRtsTo.erase(*linkAddr);
 
 1433            ehtFem->m_sentFrameTo.erase(*linkAddr);
 
 1440    m_mac->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
 
 1445    if (
auto it = m_transDelayTimer.find(*mldAddress);
 
 1446        it != m_transDelayTimer.end() && it->second.IsPending())
 
 1448        it->second.PeekEventImpl()->Invoke();
 
 1449        it->second.Cancel();
 
 
 1456EhtFrameExchangeManager::GetOngoingTxopEndEvent()
 
 1458    return m_ongoingTxopEnd;
 
 
 1466    if (m_apMac && m_apMac->GetApEmlsrManager())
 
 1468        m_apMac->GetApEmlsrManager()->NotifyPsduRxError(m_linkId, psdu);
 
 
 1478    NS_LOG_FUNCTION(
this << *mpdu << rxSignalInfo << txVector << inAmpdu);
 
 1481    NS_ASSERT(mpdu->GetHeader().GetAddr1().IsGroup() || mpdu->GetHeader().GetAddr1() == m_self);
 
 1483    const auto& hdr = mpdu->GetHeader();
 
 1484    auto sender = hdr.GetAddr2();
 
 1486    if (hdr.IsTrigger())
 
 1494        mpdu->GetPacket()->PeekHeader(trigger);
 
 1496        if (hdr.GetAddr1() != m_self &&
 
 1497            (!hdr.GetAddr1().IsBroadcast() || !m_staMac->IsAssociated() ||
 
 1504        if ((trigger.
IsMuRts() || trigger.
IsBsrp()) && !m_ongoingTxopEnd.IsPending() &&
 
 1505            m_staMac->IsEmlsrLink(m_linkId))
 
 1508            if (DropReceivedIcf())
 
 1513            m_dlTxopStart = 
true;
 
 1516    else if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) && !m_ongoingTxopEnd.IsPending() &&
 
 1517             m_phy->GetPhyId() == m_staMac->GetEmlsrManager()->GetMainPhyId() &&
 
 1518             (hdr.IsRts() || hdr.IsBlockAckReq() || (hdr.IsData() && hdr.GetAddr1() == m_self)))
 
 1521        m_dlTxopStart = 
true;
 
 1524    if (!m_dlTxopStart && ShallDropReceivedMpdu(mpdu))
 
 1530    HeFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
 
 1532    if (m_apMac && GetWifiRemoteStationManager()->GetEmlsrEnabled(sender))
 
 1534        if (hdr.IsRts() && !m_sendCtsEvent.IsPending())
 
 1537            EmlsrSwitchToListening(sender, 
Time{0});
 
 1543        CheckEmlsrClientStartingTxop(hdr, txVector);
 
 
 1551                                         const std::vector<bool>& perMpduStatus)
 
 1554        this << *psdu << rxSignalInfo << txVector << perMpduStatus.size()
 
 1555             << std::all_of(perMpduStatus.begin(), perMpduStatus.end(), [](
bool v) { return v; }));
 
 1557    const auto& hdr = psdu->GetHeader(0);
 
 1558    if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) && !m_ongoingTxopEnd.IsPending() &&
 
 1559        m_phy->GetPhyId() == m_staMac->GetEmlsrManager()->GetMainPhyId() &&
 
 1560        (hdr.IsData() && hdr.GetAddr1() == m_self))
 
 1563        m_dlTxopStart = 
true;
 
 1566    if (!m_dlTxopStart && ShallDropReceivedMpdu(*psdu->begin()))
 
 1571    HeFrameExchangeManager::EndReceiveAmpdu(psdu, rxSignalInfo, txVector, perMpduStatus);
 
 
 1580    if (!m_staMac || !m_staMac->IsEmlsrLink(m_linkId))
 
 1597    if (m_sendCtsEvent.IsPending())
 
 1599        NS_LOG_DEBUG(
"Dropping " << *mpdu << 
" received when CTS is scheduled for TX on link " 
 1604    const auto& hdr = mpdu->GetHeader();
 
 1614    if (hdr.IsMgt() || hdr.IsCts() || hdr.IsCfEnd() || (hdr.IsData() && hdr.GetAddr1().IsGroup()))
 
 1620    if (m_mac->GetLinkForPhy(m_staMac->GetEmlsrManager()->GetMainPhyId()) != m_linkId)
 
 1622        NS_LOG_DEBUG(
"Dropping " << *mpdu << 
" received by an aux PHY on link " << +m_linkId);
 
 1627    if (!m_ongoingTxopEnd.IsPending() &&
 
 1629            return m_mac->GetQosTxop(aciAcPair.first)->GetTxopStartTime(m_linkId).has_value();
 
 1632        NS_LOG_DEBUG(
"Dropping " << *mpdu << 
" received by main PHY on link " << +m_linkId
 
 1633                                 << 
" while no TXOP is ongoing");
 
 
 1642EhtFrameExchangeManager::DropReceivedIcf()
 
 1646    auto emlsrManager = m_staMac->GetEmlsrManager();
 
 1649    if (UsingOtherEmlsrLink())
 
 1653        auto apMldAddress = GetWifiRemoteStationManager()->GetMldAddress(m_bssid);
 
 1654        NS_ASSERT_MSG(apMldAddress, 
"MLD address not found for " << m_bssid);
 
 1656        if (
auto it = std::find_if(
 
 1657                m_staMac->GetLinkIds().cbegin(),
 
 1658                m_staMac->GetLinkIds().cend(),
 
 1660                [=, 
this](uint8_t linkId) {
 
 1662                        StaticCast<EhtFrameExchangeManager>(m_mac->GetFrameExchangeManager(linkId));
 
 1663                    return linkId != m_linkId && m_staMac->IsEmlsrLink(linkId) &&
 
 1664                           ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder &&
 
 1665                           m_mac->GetWifiRemoteStationManager(linkId)->GetMldAddress(
 
 1666                               *ehtFem->m_txopHolder) == apMldAddress;
 
 1668            it != m_staMac->GetLinkIds().cend())
 
 1674            StaticCast<EhtFrameExchangeManager>(m_mac->GetFrameExchangeManager(*it))
 
 1675                ->m_ongoingTxopEnd.Cancel();
 
 1679            m_staMac->UnblockTxOnLink({m_linkId}, WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK);
 
 1686            NS_LOG_DEBUG(
"Drop ICF because another EMLSR link is being used");
 
 1687            m_icfDropCallback({WifiIcfDrop::USING_OTHER_LINK, m_linkId, m_bssid});
 
 1711    else if (
auto mainPhy = m_staMac->GetDevice()->GetPhy(emlsrManager->GetMainPhyId());
 
 1714        auto reason = emlsrManager->CheckMainPhyTakesOverDlTxop(m_linkId);
 
 1716        if (reason.has_value())
 
 1719                "Drop ICF due to not enough time for the main PHY to switch link; reason = " 
 1721            m_icfDropCallback({*reason, m_linkId, m_bssid});
 
 
 1729EhtFrameExchangeManager::TxopEnd(
const std::optional<Mac48Address>& txopHolder)
 
 1733    if (m_phy && m_phy->GetInfoIfRxingPhyHeader())
 
 1739        NS_LOG_DEBUG(
"PHY is decoding the PHY header of PPDU, postpone TXOP end");
 
 1741                                               &EhtFrameExchangeManager::TxopEnd,
 
 1747    if (m_staMac && m_staMac->IsEmlsrLink(m_linkId))
 
 1749        m_staMac->GetEmlsrManager()->NotifyTxopEnd(m_linkId);
 
 1751    else if (m_apMac && txopHolder && GetWifiRemoteStationManager()->GetEmlsrEnabled(*txopHolder))
 
 1754        EmlsrSwitchToListening(*txopHolder, 
Seconds(0));
 
 
 1759EhtFrameExchangeManager::UpdateTxopEndOnTxStart(
Time txDuration, 
Time durationId)
 
 1763    if (!m_ongoingTxopEnd.IsPending())
 
 1769    m_ongoingTxopEnd.Cancel();
 
 1772    if (m_txTimer.IsRunning())
 
 1777        delay = m_txTimer.GetDelayLeft();
 
 1779    else if (durationId <= m_phy->GetSifs())
 
 1783        NS_LOG_DEBUG(
"Assume TXOP will end based on Duration/ID value");
 
 1794        delay = 
Min(delay, txDuration + durationId);
 
 1797    NS_LOG_DEBUG(
"Expected TXOP end=" << (Simulator::Now() + delay).As(Time::S));
 
 1799        Simulator::Schedule(delay, &EhtFrameExchangeManager::TxopEnd, 
this, m_txopHolder);
 
 
 1803EhtFrameExchangeManager::UpdateTxopEndOnRxStartIndication(
Time psduDuration)
 
 1814    m_ongoingTxopEnd.Cancel();
 
 1816    NS_LOG_DEBUG(
"Expected TXOP end=" << (Simulator::Now() + psduDuration).As(Time::S));
 
 1817    m_ongoingTxopEnd = Simulator::Schedule(psduDuration + 
NanoSeconds(1),
 
 1818                                           &EhtFrameExchangeManager::TxopEnd,
 
 
 1824EhtFrameExchangeManager::UpdateTxopEndOnRxEnd(
Time durationId)
 
 1828    if (!m_ongoingTxopEnd.IsPending())
 
 1834    m_ongoingTxopEnd.Cancel();
 
 1838    if (durationId <= m_phy->GetSifs())
 
 1840        NS_LOG_DEBUG(
"Assume TXOP ended based on Duration/ID value");
 
 1841        TxopEnd(m_txopHolder);
 
 1849    delay = 
Min(delay, durationId);
 
 1850    NS_LOG_DEBUG(
"Expected TXOP end=" << (Simulator::Now() + delay).As(Time::S));
 
 1852        Simulator::Schedule(delay, &EhtFrameExchangeManager::TxopEnd, 
this, m_txopHolder);
 
 
a polymophic address class
EhtFrameExchangeManager handles the frame exchange sequences for EHT stations.
void GenerateInDeviceInterferenceForAll(const Time &txDuration, const WifiTxVector &txVector)
Generate in-device interference caused by a transmission on this link for all the other PHYs of this ...
void ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVector &txVector) override
Forward a map of PSDUs down to the PHY layer.
void ReceivedQosNullAfterBsrpTf(Mac48Address sender) override
Perform the actions required when receiving QoS Null frame(s) from the given sender after a BSRP Trig...
void SetIcfPaddingAndTxVector(CtrlTriggerHeader &trigger, WifiTxVector &txVector) const
Set the padding and the TXVECTOR of the given Trigger Frame, in case it is an Initial Control Frame f...
bool UsingOtherEmlsrLink() const
void NavResetTimeout() override
Reset the NAV upon expiration of the NAV reset timer.
bool GetUpdateCwOnCtsTimeout() const override
void ForwardPsduDown(Ptr< const WifiPsdu > psdu, WifiTxVector &txVector) override
Forward a PSDU down to the PHY layer.
void EmlsrSwitchToListening(Mac48Address address, const Time &delay)
This method is intended to be called when an AP MLD detects that an EMLSR client previously involved ...
void BlockAcksInTbPpduTimeout(WifiPsduMap *psduMap, std::size_t nSolicitedStations) override
Take the necessary actions after that some BlockAck frames are missing in response to a DL MU PPDU.
void SendEmlOmn(const Mac48Address &dest, const MgtEmlOmn &frame)
Send an EML Operating Mode Notification frame to the given station.
Ptr< WifiMpdu > CreateAliasIfNeeded(Ptr< WifiMpdu > mpdu) const override
Create an alias of the given MPDU for transmission by this Frame Exchange Manager.
void ProtectionCompleted() override
Transmit prepared frame immediately, if no protection was used, or in a SIFS, if protection was compl...
void TbPpduTimeout(WifiPsduMap *psduMap, std::size_t nSolicitedStations) override
Take the necessary actions after that some TB PPDUs are missing in response to Trigger Frame.
bool GetReportRtsFailed() const override
void IntraBssNavResetTimeout() override
Reset the intra-BSS NAV upon expiration of the intra-BSS NAV reset timer.
void GenerateInDeviceInterference(Ptr< WifiPhy > phy, Time duration, Watt_u txPower)
Generate an in-device interference of the given power for the given duration for the given PHY.
void SendCtsAfterMuRts(const WifiMacHeader &muRtsHdr, const CtrlTriggerHeader &trigger, double muRtsSnr) override
Send CTS after receiving an MU-RTS.
void SwitchToListeningOrUnblockLinks(const std::set< Mac48Address > &clients)
For each EMLSR client in the given set of clients that did not respond to a frame requesting a respon...
EhtFrameExchangeManager()
bool IsCrossLinkCollision(const std::set< Mac48Address > &staMissedResponseFrom) const
Check whether all the stations that did not respond (to a certain frame) are EMLSR clients trying to ...
std::optional< dBm_u > GetMostRecentRssi(const Mac48Address &address) const override
Get the RSSI of the most recent packet received from the station having the given address.
bool EmlsrClientCannotRespondToIcf() const
bool GetEmlsrSwitchToListening(Ptr< const WifiPsdu > psdu, uint16_t aid, const Mac48Address &address) const
static TypeId GetTypeId()
Get the type ID.
void DoDispose() override
Destructor implementation.
std::unordered_map< Mac48Address, EventId, WifiAddressHash > m_transDelayTimer
MLD address-indexed map of transition delay timers.
void NotifyChannelReleased(Ptr< Txop > txop) override
Notify the given Txop that channel has been released.
EventId m_ongoingTxopEnd
event indicating the possible end of the current TXOP (of which we are not the holder)
void RxStartIndication(WifiTxVector txVector, Time psduDuration) override
void UpdateTxopEndOnTxStart(Time txDuration, Time durationId)
Update the TXOP end timer when starting a frame transmission.
void SendQosNullFramesInTbPpdu(const CtrlTriggerHeader &trigger, const WifiMacHeader &hdr) override
Send QoS Null frames in response to a Basic or BSRP Trigger Frame.
void UpdateTxopEndOnRxStartIndication(Time psduDuration)
Update the TXOP end timer when receiving a PHY-RXSTART.indication.
~EhtFrameExchangeManager() override
bool UnblockEmlsrLinksIfAllowed(Mac48Address address, bool checkThisLink)
Unblock transmissions on all the links of the given EMLSR client, provided that the latter is not inv...
void CtsAfterMuRtsTimeout(Ptr< WifiMpdu > muRts, const WifiTxVector &txVector) override
Called when no CTS frame is received after an MU-RTS.
void NotifySwitchingEmlsrLink(Ptr< WifiPhy > phy, uint8_t linkId, Time delay)
Notify that the given PHY will switch channel to operate on another EMLSR link after the given delay.
bool StartTransmission(Ptr< Txop > edca, MHz_u allowedWidth) override
Request the FrameExchangeManager to start a frame exchange sequence.
void SetLinkId(uint8_t linkId) override
Set the ID of the link this Frame Exchange Manager is associated with.
An identifier for simulation events.
void Cancel()
This method is syntactic sugar for the ns3::Simulator::Cancel method.
std::set< Mac48Address > m_sentRtsTo
the STA(s) which we sent an RTS to (waiting for CTS)
uint8_t m_linkId
the ID of the link this object is associated with
Ptr< WifiMac > m_mac
the MAC layer on this station
virtual void ResetPhy()
Remove WifiPhy associated with this FrameExchangeManager.
Ptr< WifiRemoteStationManager > GetWifiRemoteStationManager() const
Ptr< MacTxMiddle > m_txMiddle
the MAC TX Middle on this station
virtual bool GetReportRtsFailed() const
Mac48Address m_self
the MAC address of this device
WifiTxTimer m_txTimer
the timer set upon frame transmission
std::set< Mac48Address > m_protectedStas
STAs that have replied to an RTS in this TXOP.
Mac48Address GetAddress() const
Get the MAC address.
virtual void SetLinkId(uint8_t linkId)
Set the ID of the link this Frame Exchange Manager is associated with.
Ptr< WifiAckManager > GetAckManager() const
Get the Acknowledgment Manager used by this node.
Ptr< WifiProtectionManager > GetProtectionManager() const
Get the Protection Manager used by this node.
Ptr< WifiPhy > m_phy
the PHY layer on this station
Ptr< ApWifiMac > m_apMac
AP MAC layer pointer (null if not an AP)
Mac48Address m_bssid
BSSID address (Mac48Address)
virtual bool StartTransmission(Ptr< Txop > dcf, MHz_u allowedWidth)
Request the FrameExchangeManager to start a frame exchange sequence.
MHz_u m_allowedWidth
the allowed width for the current transmission
Ptr< StaWifiMac > m_staMac
STA MAC layer pointer (null if not a STA)
virtual bool GetUpdateCwOnCtsTimeout() const
HeFrameExchangeManager handles the frame exchange sequences for HE stations.
virtual void SendQosNullFramesInTbPpdu(const CtrlTriggerHeader &trigger, const WifiMacHeader &hdr)
Send QoS Null frames in response to a Basic or BSRP Trigger Frame.
void DoDispose() override
Destructor implementation.
virtual void IntraBssNavResetTimeout()
Reset the intra-BSS NAV upon expiration of the intra-BSS NAV reset timer.
virtual void ReceivedQosNullAfterBsrpTf(Mac48Address sender)
Perform the actions required when receiving QoS Null frame(s) from the given sender after a BSRP Trig...
void RxStartIndication(WifiTxVector txVector, Time psduDuration) override
virtual void CtsAfterMuRtsTimeout(Ptr< WifiMpdu > muRts, const WifiTxVector &txVector)
Called when no CTS frame is received after an MU-RTS.
void NavResetTimeout() override
Reset the NAV upon expiration of the NAV reset timer.
void ProtectionCompleted() override
Transmit prepared frame immediately, if no protection was used, or in a SIFS, if protection was compl...
virtual void SendCtsAfterMuRts(const WifiMacHeader &muRtsHdr, const CtrlTriggerHeader &trigger, double muRtsSnr)
Send CTS after receiving an MU-RTS.
virtual void BlockAcksInTbPpduTimeout(WifiPsduMap *psduMap, std::size_t nSolicitedStations)
Take the necessary actions after that some BlockAck frames are missing in response to a DL MU PPDU.
virtual void ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVector &txVector)
Forward a map of PSDUs down to the PHY layer.
std::set< Mac48Address > GetTfRecipients(const CtrlTriggerHeader &trigger) const
Get the (link) address of the non-AP stations solicited by the given Trigger Frame.
void DoTbPpduTimeout(WifiPsduMap *psduMap, std::size_t nSolicitedStations, bool updateFailedCw)
Take the necessary actions after that some TB PPDUs are missing in response to Trigger Frame.
virtual std::optional< dBm_u > GetMostRecentRssi(const Mac48Address &address) const
Get the RSSI of the most recent packet received from the station having the given address.
Ptr< MpduAggregator > m_mpduAggregator
A-MPDU aggregator.
virtual void ForwardPsduDown(Ptr< const WifiPsdu > psdu, WifiTxVector &txVector)
Forward a PSDU down to the PHY layer.
Ptr< MsduAggregator > m_msduAggregator
A-MSDU aggregator.
Implement the header for Action frames of type EML Operating Mode Notification.
Smart pointer class similar to boost::intrusive_ptr.
virtual Ptr< WifiMpdu > CreateAliasIfNeeded(Ptr< WifiMpdu > mpdu) const
Create an alias of the given MPDU for transmission by this Frame Exchange Manager.
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
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.
void StartAccessAfterEvent(uint8_t linkId, bool hadFramesToTransmit, bool checkMediumBusy)
Request channel access on the given link after the occurrence of an event that possibly requires to g...
static constexpr bool DIDNT_HAVE_FRAMES_TO_TRANSMIT
no packet available for transmission was in the queue
static constexpr bool CHECK_MEDIUM_BUSY
generation of backoff (also) depends on the busy/idle state of the medium
static constexpr bool DONT_CHECK_MEDIUM_BUSY
generation of backoff is independent of the busy/idle state of the medium
a unique identifier for an interface.
TypeId SetParent(TypeId tid)
Set the parent TypeId.
uint64_t GetDataRate(MHz_u channelWidth, Time guardInterval, uint8_t nss) const
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
WifiPhyBand GetPhyBand() const
Get the configured Wi-Fi band.
Ptr< PhyEntity > GetPhyEntity(WifiModulationClass modulation) const
Get the supported PHY entity corresponding to the modulation class.
const std::set< Mac48Address > & GetStasExpectedToRespond() const
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
void SetSigBMode(const WifiMode &mode)
Set the MCS used for SIG-B.
uint8_t GetTxPowerLevel() const
Declaration of ns3::EhtPhy class.
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
#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_FUNCTION_NOARGS()
Output the name of the function.
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Ptr< T > Create(Ts &&... args)
Create class instances by constructors with varying numbers of arguments and return them by Ptr.
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.
@ WIFI_MOD_CLASS_EHT
EHT (Clause 36)
@ WAITING_EMLSR_TRANSITION_DELAY
Every class exported by the ns3 library is enclosed in the ns3 namespace.
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.
static constexpr uint8_t WAIT_FOR_RXSTART_DELAY_USEC
Additional time (exceeding 20 us) to wait for a PHY-RXSTART.indication when the PHY is decoding a PHY...
bool IsTrigger(const WifiPsduMap &psduMap)
Ptr< T1 > DynamicCast(const Ptr< T2 > &p)
Cast a Ptr.
std::unordered_map< uint16_t, Ptr< WifiPsdu > > WifiPsduMap
Map of PSDUs indexed by STA-ID.
const std::map< AcIndex, WifiAc > wifiAcList
Map containing the four ACs in increasing order of priority (according to Table 10-1 "UP-to-AC Mappin...
Watt_u DbmToW(dBm_u val)
Convert from dBm to Watts.
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.
const Time EMLSR_RX_PHY_START_DELAY
aRxPHYStartDelay value to use when waiting for a new frame in the context of EMLSR operations (Sec.
static Time DecodeEmlsrTransitionDelay(uint8_t value)
RxSignalInfo structure containing info on the received signal.