15#include "ns3/assert.h" 
   16#include "ns3/attribute-container.h" 
   18#include "ns3/mgt-action-headers.h" 
   19#include "ns3/wifi-mpdu.h" 
   20#include "ns3/wifi-net-device.h" 
   21#include "ns3/wifi-phy-state-helper.h" 
   37        TypeId(
"ns3::EmlsrManager")
 
   40            .AddAttribute(
"EmlsrPaddingDelay",
 
   41                          "The EMLSR Paddind Delay (not used by AP MLDs). " 
   42                          "Possible values are 0 us, 32 us, 64 us, 128 us or 256 us.",
 
   46            .AddAttribute(
"EmlsrTransitionDelay",
 
   47                          "The EMLSR Transition Delay (not used by AP MLDs). " 
   48                          "Possible values are 0 us, 16 us, 32 us, 64 us, 128 us or 256 us.",
 
   54                "The ID of the main PHY (position in the vector of PHYs held by " 
   55                "WifiNetDevice). This attribute cannot be set after construction.",
 
   60            .AddAttribute(
"AuxPhyChannelWidth",
 
   61                          "The maximum channel width (MHz) supported by Aux PHYs.",
 
   67            .AddAttribute(
"AuxPhyMaxModClass",
 
   68                          "The maximum modulation class supported by Aux PHYs. Use " 
   69                          "WIFI_MOD_CLASS_OFDM for non-HT.",
 
   88            .AddAttribute(
"AuxPhyTxCapable",
 
   89                          "Whether Aux PHYs are capable of transmitting PPDUs.",
 
   94            .AddAttribute(
"InDeviceInterference",
 
   95                          "Whether in-device interference is such that a PHY cannot decode " 
   96                          "anything and cannot decrease the backoff counter when another PHY " 
   97                          "of the same device is transmitting.",
 
  102            .AddAttribute(
"PutAuxPhyToSleep",
 
  103                          "Whether Aux PHYs should be put into sleep mode while the Main PHY " 
  104                          "is carrying out a (DL or UL) TXOP. Specifically, for DL TXOPs, aux " 
  105                          "PHYs are put to sleep after receiving the ICF; for UL TXOPs, aux PHYs " 
  106                          "are put to sleep when the CTS frame is received, if RTS/CTS is used, " 
  107                          "or when the transmission of the data frame starts, otherwise. " 
  108                          "Aux PHYs are resumed from sleep when the TXOP ends.",
 
  112            .AddAttribute(
"UseNotifiedMacHdr",
 
  113                          "Whether to use the information about the MAC header of the MPDU " 
  114                          "being received, if notified by the PHY.",
 
  120                "IDs of the links on which EMLSR mode will be enabled. An empty set " 
  121                "indicates to disable EMLSR.",
 
  125            .AddAttribute(
"ResetCamState",
 
  126                          "Whether to reset the state of the ChannelAccessManager associated with " 
  127                          "the link on which the main PHY has just switched to.",
 
  132            .AddTraceSource(
"MainPhySwitch",
 
  133                            "This trace source is fired when the main PHY switches channel to " 
  134                            "operate on another link. Information associated with the main PHY " 
  135                            "switch is provided through a struct that is inherited from struct " 
  136                            "EmlsrMainPhySwitchTrace (use the GetName() method to get the type " 
  137                            "of the provided object).",
 
  139                            "ns3::EmlsrManager::MainPhySwitchCallback");
 
 
  165    m_staMac->TraceDisconnectWithoutContext(
"DroppedMpdu",
 
  171        status.timer.Cancel();
 
 
  185                    "EmlsrManager requires EMLSR to be activated");
 
  187                    "EmlsrManager can only be installed on non-AP MLDs");
 
  189                    "Main PHY ID (" << +
m_mainPhyId << 
") invalid given the number of PHY objects (" 
  190                                    << +
m_staMac->GetDevice()->GetNPhys() << 
")");
 
  193    m_staMac->TraceConnectWithoutContext(
"DroppedMpdu",
 
  195    m_staMac->TraceConnectWithoutContext(
 
 
  218                      "Main PHY only is expected to leave a link on which it is operating");
 
  219        NS_LOG_DEBUG(
"Record that no PHY is operating on link " << +linkId);
 
  221        if (phy->GetChannelSwitchDelay().IsZero())
 
  262        NS_ASSERT_MSG(duration.IsPositive(), 
"Interval duration should not be negative");
 
  264        NS_LOG_DEBUG(
"PHY " << +phy->GetPhyId() << 
" switched to link " << +linkId << 
" after " 
  266                            << 
" since last time a PHY was operating on this link");
 
 
  326const std::set<uint8_t>&
 
  401std::optional<uint8_t>
 
  410    std::stringstream ss;
 
  413        std::copy(linkIds.cbegin(), linkIds.cend(), std::ostream_iterator<uint16_t>(ss, 
" "));
 
 
  434    const auto& hdr = mpdu->GetHeader();
 
  451    if (hdr.IsAction() && hdr.GetAddr2() == 
m_staMac->GetBssid(linkId))
 
  456            action.protectedEhtAction ==
 
 
  477    for (
auto id : 
m_staMac->GetLinkIds())
 
  479        if (
id != linkId && 
m_staMac->IsEmlsrLink(
id))
 
  486    auto rxPhy = 
m_staMac->GetWifiPhy(linkId);
 
  488    const auto receivedByAuxPhy = (rxPhy != mainPhy);
 
  489    const auto mainPhyOnALink = (
m_staMac->GetLinkForPhy(mainPhy).has_value());
 
  490    const auto mainPhyIsSwitching =
 
  494    const auto mainPhyToConnect = (!mainPhyOnALink && !mainPhyIsSwitching);
 
  498    if (mainPhyToConnect)
 
  504        GetStaMac()->CancelEmlsrPhyConnectEvent(mainPhy->GetPhyId());
 
  511    if (receivedByAuxPhy && (!mainPhyToConnect || mainPhyToConnectToOtherLink))
 
  518    else if (mainPhyToConnect && !mainPhyToConnectToOtherLink)
 
  525        m_staMac->NotifySwitchingEmlsrLink(mainPhy, linkId, 
Time{0});
 
  528    if (receivedByAuxPhy)
 
  531        auto uid = rxPhy->GetPreviouslyRxPpduUid();
 
  532        mainPhy->SetPreviouslyRxPpduUid(uid);
 
 
  547    auto phy = 
m_staMac->GetWifiPhy(linkId);
 
  555        return {
false, delay};
 
  561        return {
true, 
Time{0}};
 
  573        NS_LOG_DEBUG(
"Aux PHY is not capable of transmitting a PPDU");
 
  574        return {
false, 
Time{0}};
 
 
  585    auto phy = 
GetStaMac()->GetWifiPhy(linkId);
 
  587    if (!phy || !
GetStaMac()->IsEmlsrLink(linkId))
 
  589        NS_LOG_DEBUG(
"No PHY (" << phy << 
") or not an EMLSR link (" << +linkId << 
")");
 
  590        return {
false, 
Time{0}};
 
  593    if (
auto endPreamble = phy->GetTimeToPreambleDetectionEnd())
 
  596        return {
true, endPreamble.value()};
 
  600        NS_LOG_DEBUG(
"Receiving the MAC payload of a PSDU and MAC header info can be used");
 
  603        if (
const auto& hdr = macHdr->get();
 
  605            (hdr.GetAddr1().IsBroadcast() || hdr.GetAddr1() == 
GetEhtFem(linkId)->GetAddress()))
 
  609            NS_LOG_DEBUG(
"Based on MAC header, may be an ICF, postpone by " 
  610                         << phy->GetDelayUntilIdle().As(
Time::US));
 
  611            return {
true, phy->GetDelayUntilIdle()};
 
  614    else if (
auto txVector = phy->GetInfoIfRxingPhyHeader())
 
  621            NS_LOG_DEBUG(
"PHY header of a non-HT PPDU, which may be an ICF, is being received");
 
  625    else if (phy->IsStateRx())
 
  639        if (
auto ongoingRxInfo = 
GetEhtFem(linkId)->GetOngoingRxInfo();
 
  640            ongoingRxInfo.has_value() &&
 
  643            if (
auto remTime = phy->GetTimeToMacHdrEnd(
SU_STA_ID);
 
  646                NS_LOG_DEBUG(
"Wait until the expected end of the MAC header reception: " 
  648                return {
true, *remTime};
 
  652                "MAC header info will not be available. Wait until the end of PSDU reception: " 
  653                << phy->GetDelayUntilIdle().As(
Time::US));
 
  654            return {
true, phy->GetDelayUntilIdle()};
 
  658    NS_LOG_DEBUG(
"No ICF being received, state: " << phy->GetState()->GetState());
 
  659    return {
false, 
Time{0}};
 
 
  662std::optional<WifiIcfDrop>
 
  667    const auto delay = mainPhy->GetChannelSwitchDelay();
 
  671    if (
auto lastSwitch = mainPhy->GetState()->GetLastTime({WifiPhyState::SWITCHING});
 
  674        lastTime = lastSwitch;
 
  678        lastSleep > lastTime)
 
  680        lastTime = lastSleep;
 
  687        NS_LOG_DEBUG(
"Not enough time for the main PHY to switch link; reason = " 
  688                     << reason << 
" lastTime = " << lastTime.As(
Time::US));
 
 
  701        NS_LOG_DEBUG(
"EMLSR is not enabled on link " << +linkId);
 
  706    for (
auto id : 
m_staMac->GetLinkIds())
 
  708        if (
id != linkId && 
m_staMac->IsEmlsrLink(
id))
 
 
  733        if (
auto mainPhy = 
m_staMac->GetDevice()->GetPhy(
m_mainPhyId); mainPhy->IsStateSwitching())
 
  735            delay = mainPhy->GetDelayUntilIdle();
 
  743        if (delay.IsStrictlyPositive())
 
 
  767        NS_LOG_DEBUG(
"EMLSR is not enabled on link " << +linkId);
 
  776        if (it->second.IsPending())
 
  778            NS_LOG_DEBUG(
"Cancelling main PHY channel switch event on link " << +linkId);
 
  791    if (
GetEhtFem(linkId)->GetOngoingTxopEndEvent().IsPending())
 
  798        if (
auto txopStart = edca->GetTxopStartTime(linkId);
 
  815    std::set<uint8_t> linkIds;
 
  816    for (
auto id : 
m_staMac->GetLinkIds())
 
  818        if ((
id != linkId) && 
m_staMac->IsEmlsrLink(
id))
 
 
  840    for (
auto id : 
m_staMac->GetLinkIds())
 
  842        if (
id != linkId && 
m_staMac->IsEmlsrLink(
id))
 
 
  859        NS_LOG_DEBUG(
"Setting CCA ED threshold of PHY " << +phy->GetPhyId() << 
" to " 
  873                     << +phy->GetPhyId() << 
" to " << threshIt->second << 
" on link " << +linkId);
 
  874        phy->SetCcaEdThreshold(threshIt->second);
 
 
  885    NS_LOG_FUNCTION(
this << linkId << noSwitchDelay << requestAccess << traceInfo.GetName());
 
  890                  "Main PHY is already operating on link " << +linkId);
 
  893    auto currMainPhyLinkId = 
m_staMac->GetLinkForPhy(mainPhy);
 
  894    traceInfo.fromLinkId = currMainPhyLinkId;
 
  895    traceInfo.toLinkId = linkId;
 
  906    NS_LOG_DEBUG(
"Main PHY (" << mainPhy << 
") is about to switch to " << newMainPhyChannel
 
  907                              << 
" to operate on link " << +linkId);
 
  911    if (currMainPhyLinkId.has_value())
 
  913        m_staMac->GetChannelAccessManager(*currMainPhyLinkId)
 
  914            ->NotifySwitchingEmlsrLink(mainPhy, newMainPhyChannel, linkId);
 
  919                  "We should not ask the main PHY to switch channel while transmitting");
 
  922    auto auxPhy = 
GetStaMac()->GetWifiPhy(linkId);
 
  925    const auto delay = mainPhy->GetChannelSwitchDelay();
 
  926    const auto pifs = mainPhy->GetSifs() + mainPhy->GetSlot();
 
  931                    << 
") should be shorter than the maximum between the Transition delay (" 
  939    mainPhy->SetOperatingChannel(newMainPhyChannel);
 
  943        mainPhy->SetAttribute(
"ChannelSwitchDelay", 
TimeValue(delay));
 
  946    if (
m_staMac->GetWifiRemoteStationManager(linkId)->GetShortSlotTimeEnabled())
 
  951    const auto timeToSwitchEnd = noSwitchDelay ? 
Seconds(0) : delay;
 
  955    if (!currMainPhyLinkId.has_value())
 
  957        m_staMac->NotifySwitchingEmlsrLink(mainPhy, linkId, timeToSwitchEnd);
 
  966                m_staMac->GetQosTxop(acIndex)->StartAccessAfterEvent(
 
 
  984    NS_LOG_DEBUG(
"Aux PHY (" << auxPhy << 
") is about to switch to " << newAuxPhyChannel
 
  985                             << 
" to operate on link " << +nextLinkId);
 
  988        ->GetChannelAccessManager(currLinkId)
 
  989        ->NotifySwitchingEmlsrLink(auxPhy, newAuxPhyChannel, nextLinkId);
 
  991    auxPhy->SetOperatingChannel(newAuxPhyChannel);
 
  993    if (
m_staMac->GetWifiRemoteStationManager(nextLinkId)->GetShortSlotTimeEnabled())
 
 1000        for (const auto& [acIndex, ac] : wifiAcList)
 
 1002            m_staMac->GetQosTxop(acIndex)->StartAccessAfterEvent(
 
 1004                Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT,
 
 1005                Txop::CHECK_MEDIUM_BUSY);
 
 
 1011EmlsrManager::StartMediumSyncDelayTimer(uint8_t linkId)
 
 1015    if (m_mediumSyncDuration.IsZero())
 
 1021    auto phy = m_staMac->GetWifiPhy(linkId);
 
 1030    const auto [it, inserted] = m_mediumSyncDelayStatus.try_emplace(linkId);
 
 1033    it->second.msdNTxopsLeft = m_msdMaxNTxops;
 
 1035    if (!it->second.timer.IsPending())
 
 1038                     << +linkId << 
" to " << +m_msdOfdmEdThreshold << 
" PHY " << +phy->GetPhyId());
 
 1039        m_prevCcaEdThreshold[phy] = phy->GetCcaEdThreshold();
 
 1040        phy->SetCcaEdThreshold(m_msdOfdmEdThreshold);
 
 1044    it->second.timer.Cancel();
 
 1045    NS_LOG_DEBUG(
"Starting MediumSyncDelay timer for " << m_mediumSyncDuration.As(Time::US)
 
 1046                                                       << 
" on link " << +linkId);
 
 1047    it->second.timer = Simulator::Schedule(m_mediumSyncDuration,
 
 1048                                           &EmlsrManager::MediumSyncDelayTimerExpired,
 
 
 1054EmlsrManager::CancelMediumSyncDelayTimer(uint8_t linkId)
 
 1058    auto timerIt = m_mediumSyncDelayStatus.find(linkId);
 
 1060    NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && timerIt->second.timer.IsPending());
 
 1062    timerIt->second.timer.Cancel();
 
 1063    MediumSyncDelayTimerExpired(linkId);
 
 
 1067EmlsrManager::MediumSyncDelayTimerExpired(uint8_t linkId)
 
 1071    auto timerIt = m_mediumSyncDelayStatus.find(linkId);
 
 1073    NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && !timerIt->second.timer.IsPending());
 
 1076    auto phy = m_staMac->GetWifiPhy(linkId);
 
 1087    auto threshIt = m_prevCcaEdThreshold.find(phy);
 
 1089                  "No value to restore for CCA ED threshold on PHY " << phy);
 
 1090    NS_LOG_DEBUG(
"Resetting CCA ED threshold of PHY " << phy << 
" to " << threshIt->second
 
 1091                                                      << 
" on link " << +linkId);
 
 1092    phy->SetCcaEdThreshold(threshIt->second);
 
 1093    m_prevCcaEdThreshold.erase(threshIt);
 
 
 1097EmlsrManager::DecrementMediumSyncDelayNTxops(uint8_t linkId)
 
 1101    const auto timerIt = m_mediumSyncDelayStatus.find(linkId);
 
 1103    NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && timerIt->second.timer.IsPending());
 
 1104    NS_ASSERT(timerIt->second.msdNTxopsLeft != 0);
 
 1106    if (timerIt->second.msdNTxopsLeft)
 
 1108        --timerIt->second.msdNTxopsLeft.value();
 
 
 1113EmlsrManager::ResetMediumSyncDelayNTxops(uint8_t linkId)
 
 1117    auto timerIt = m_mediumSyncDelayStatus.find(linkId);
 
 1119    NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && timerIt->second.timer.IsPending());
 
 1120    timerIt->second.msdNTxopsLeft.reset();
 
 
 1124EmlsrManager::MediumSyncDelayNTxopsExceeded(uint8_t linkId)
 
 1128    auto timerIt = m_mediumSyncDelayStatus.find(linkId);
 
 1130    NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && timerIt->second.timer.IsPending());
 
 1131    return timerIt->second.msdNTxopsLeft == 0;
 
 
 1135EmlsrManager::GetEmlOmn()
 
 1140    if (m_lastAdvPaddingDelay != m_emlsrPaddingDelay ||
 
 1141        m_lastAdvTransitionDelay != m_emlsrTransitionDelay)
 
 1143        m_lastAdvPaddingDelay = m_emlsrPaddingDelay;
 
 1144        m_lastAdvTransitionDelay = m_emlsrTransitionDelay;
 
 1148            CommonInfoBasicMle::EncodeEmlsrPaddingDelay(m_lastAdvPaddingDelay);
 
 1150            CommonInfoBasicMle::EncodeEmlsrTransitionDelay(m_lastAdvTransitionDelay);
 
 1154    auto setupLinkIds = m_staMac->GetSetupLinkIds();
 
 1156    for (
auto emlsrLinkIt = m_nextEmlsrLinks->begin(); emlsrLinkIt != m_nextEmlsrLinks->end();)
 
 1158        if (
auto setupLinkIt = setupLinkIds.find(*emlsrLinkIt); setupLinkIt != setupLinkIds.cend())
 
 1160            setupLinkIds.erase(setupLinkIt);
 
 1166            NS_LOG_DEBUG(
"Link ID " << +(*emlsrLinkIt) << 
" has not been setup");
 
 1167            emlsrLinkIt = m_nextEmlsrLinks->erase(emlsrLinkIt);
 
 
 1178EmlsrManager::SendEmlOmn()
 
 1183                    "AP did not advertise a Transition Timeout, cannot send EML notification");
 
 1184    NS_ASSERT_MSG(m_nextEmlsrLinks, 
"Need to set EMLSR links before calling this method");
 
 1195    auto frame = GetEmlOmn();
 
 1196    auto linkId = GetLinkToSendEmlOmn();
 
 1197    GetEhtFem(linkId)->SendEmlOmn(m_staMac->GetBssid(linkId), frame);
 
 
 1205    const auto& hdr = mpdu->GetHeader();
 
 1207    if (hdr.IsAssocReq())
 
 1211        mpdu->GetPacket()->PeekHeader(assocReq);
 
 1213        NS_ASSERT_MSG(mle, 
"AssocReq should contain a Multi-Link Element");
 
 1214        m_lastAdvPaddingDelay = mle->GetEmlsrPaddingDelay();
 
 1215        m_lastAdvTransitionDelay = mle->GetEmlsrTransitionDelay();
 
 1218    if (hdr.IsMgt() && hdr.IsAction())
 
 1220        if (
auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
 
 1221            category == WifiActionHeader::PROTECTED_EHT &&
 
 1222            action.protectedEhtAction ==
 
 1223                WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
 
 1227            NS_ASSERT_MSG(m_emlsrTransitionTimeout, 
"No transition timeout received from AP");
 
 1228            m_transitionTimeoutEvent = Simulator::Schedule(*m_emlsrTransitionTimeout,
 
 1229                                                           &EmlsrManager::ChangeEmlsrMode,
 
 
 1240    const auto& hdr = mpdu->GetHeader();
 
 1242    if (hdr.IsMgt() && hdr.IsAction())
 
 1244        auto pkt = mpdu->GetPacket()->Copy();
 
 1245        if (
auto [category, action] = WifiActionHeader::Remove(pkt);
 
 1246            category == WifiActionHeader::PROTECTED_EHT &&
 
 1247            action.protectedEhtAction ==
 
 1248                WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
 
 1252            auto linkId = ResendNotification(mpdu);
 
 1256                pkt->RemoveHeader(frame);
 
 1257                GetEhtFem(*linkId)->SendEmlOmn(m_staMac->GetBssid(*linkId), frame);
 
 1261                m_nextEmlsrLinks.reset();
 
 
 1268EmlsrManager::ChangeEmlsrMode()
 
 1279    NS_ASSERT_MSG(m_nextEmlsrLinks, 
"No set of EMLSR links stored");
 
 1280    m_emlsrLinks.swap(*m_nextEmlsrLinks);
 
 1281    m_nextEmlsrLinks.reset();
 
 1285    m_staMac->NotifyEmlsrModeChanged(m_emlsrLinks);
 
 1287    ApplyMaxChannelWidthAndModClassOnAuxPhys();
 
 1289    NotifyEmlsrModeChanged();
 
 
 1293EmlsrManager::ApplyMaxChannelWidthAndModClassOnAuxPhys()
 
 1296    auto currMainPhyLinkId = m_staMac->GetLinkForPhy(m_mainPhyId);
 
 1299    for (
const auto linkId : m_staMac->GetLinkIds())
 
 1301        auto auxPhy = m_staMac->GetWifiPhy(linkId);
 
 1302        auto channel = GetChannelForAuxPhy(linkId);
 
 1304        if (linkId == currMainPhyLinkId || !m_staMac->IsEmlsrLink(linkId))
 
 1309        auxPhy->SetMaxModulationClassSupported(m_auxPhyMaxModClass);
 
 1311        if (auxPhy->GetOperatingChannel() == channel)
 
 1316        NS_LOG_DEBUG(
"Aux PHY (" << auxPhy << 
") is about to switch to " << channel
 
 1317                                 << 
" to operate on link " << +linkId);
 
 1323        auto cam = m_staMac->GetChannelAccessManager(linkId);
 
 1324        cam->NotifySwitchingEmlsrLink(auxPhy, channel, linkId);
 
 1327        const auto delay = auxPhy->GetChannelSwitchDelay();
 
 1328        auxPhy->SetAttribute(
"ChannelSwitchDelay", 
TimeValue(
Time{0}));
 
 1329        auxPhy->SetOperatingChannel(channel);
 
 1330        auxPhy->SetAttribute(
"ChannelSwitchDelay", 
TimeValue(delay));
 
 
 1335EmlsrManager::ComputeOperatingChannels()
 
 1339    m_mainPhyChannels.clear();
 
 1340    m_auxPhyChannels.clear();
 
 1342    auto linkIds = m_staMac->GetSetupLinkIds();
 
 1344    for (
auto linkId : linkIds)
 
 1346        const auto& channel = m_staMac->GetWifiPhy(linkId)->GetOperatingChannel();
 
 1347        m_mainPhyChannels.emplace(linkId, channel);
 
 1349        auto mainPhyChWidth = channel.GetWidth();
 
 1350        if (m_auxPhyMaxWidth >= mainPhyChWidth)
 
 1353            m_auxPhyChannels.emplace(linkId, channel);
 
 1357        auto freq = channel.GetPrimaryChannelCenterFrequency(m_auxPhyMaxWidth);
 
 1358        auto chIt = WifiPhyOperatingChannel::FindFirst(0,
 
 1362                                                       channel.GetPhyBand());
 
 1363        NS_ASSERT_MSG(chIt != WifiPhyOperatingChannel::m_frequencyChannels.end(),
 
 1364                      "Primary" << m_auxPhyMaxWidth << 
" channel not found");
 
 1365        m_auxPhyChannels.emplace(linkId, chIt);
 
 1367        auto p20Index = channel.GetPrimaryChannelIndex(
MHz_u{20});
 
 1368        while (mainPhyChWidth > m_auxPhyMaxWidth)
 
 1370            mainPhyChWidth /= 2;
 
 1373        m_auxPhyChannels[linkId].SetPrimary20Index(p20Index);
 
 
 1378EmlsrManager::GetChannelForMainPhy(uint8_t linkId)
 const 
 1380    auto it = m_mainPhyChannels.find(linkId);
 
 1382                  "Channel for main PHY on link ID " << +linkId << 
" not found");
 
 
 1387EmlsrManager::GetChannelForAuxPhy(uint8_t linkId)
 const 
 1389    auto it = m_auxPhyChannels.find(linkId);
 
 1391                  "Channel for aux PHY on link ID " << +linkId << 
" not found");
 
 
 1396EmlsrManager::CancelAllSleepEvents()
 
 1400    for (
auto& [
id, event] : m_auxPhyToSleepEvents)
 
 1404    m_auxPhyToSleepEvents.clear();
 
 
 1408EmlsrManager::SetSleepStateForAllAuxPhys(
bool sleep)
 
 1412    CancelAllSleepEvents();
 
 1414    for (
const auto& phy : m_staMac->GetDevice()->GetPhys())
 
 1416        if (phy->GetPhyId() == m_mainPhyId)
 
 1421        auto linkId = m_staMac->GetLinkForPhy(phy);
 
 1423        if (linkId.has_value() && !m_staMac->IsEmlsrLink(*linkId))
 
 1430            if (!phy->IsStateSleep())
 
 1435            NS_LOG_DEBUG(
"PHY " << +phy->GetPhyId() << 
": Resuming from sleep");
 
 1436            phy->ResumeFromSleep();
 
 1439            if (linkId.has_value())
 
 1441                auto it = m_startSleep.find(phy->GetPhyId());
 
 1443                              "No start sleep info for PHY ID " << phy->GetPhyId());
 
 1444                const auto sleepDuration = Simulator::Now() - it->second;
 
 1445                m_startSleep.erase(it);
 
 1449                    StartMediumSyncDelayTimer(*linkId);
 
 1456        if (phy->IsStateSleep())
 
 1466        std::stringstream ss;
 
 1467        auto s = std::string(
"PHY ") + std::to_string(phy->GetPhyId()) + 
": Setting sleep mode";
 
 1468        if (phy->IsStateTx() || phy->IsStateSwitching())
 
 1470            const auto delay = phy->GetDelayUntilIdle();
 
 1472            m_auxPhyToSleepEvents[phy->GetPhyId()] = Simulator::Schedule(delay, [=, 
this]() {
 
 1473                phy->SetSleepMode(
true);
 
 1474                m_startSleep[phy->GetPhyId()] = Simulator::Now();
 
 1480            phy->SetSleepMode(
true);
 
 1481            m_startSleep[phy->GetPhyId()] = Simulator::Now();
 
 
A container for one type of attribute.
AttributeValue implementation for Boolean.
void SendEmlOmn()
Send an EML Operating Mode Notification frame.
Time GetMediumSyncDuration() const
void ComputeOperatingChannels()
Compute the operating channels that the main PHY and the aux PHY(s) must switch to in order to operat...
void SwitchMainPhy(uint8_t linkId, bool noSwitchDelay, bool requestAccess, EmlsrMainPhySwitchTrace &&traceInfo)
Switch channel on the Main PHY so that it operates on the given link.
void NotifyProtectionCompleted(uint8_t linkId)
Notify that protection (if required) is completed and data frame exchange can start on the given link...
void SetTransitionTimeout(Time timeout)
Set the Transition Timeout advertised by the associated AP with EMLSR activated.
bool m_useNotifiedMacHdr
whether to use the information about the MAC header of the MPDU being received (if notified by the PH...
bool m_auxPhyTxCapable
whether Aux PHYs are capable of transmitting PPDUs
virtual void DoNotifyTxopEnd(uint8_t linkId, Ptr< QosTxop > edca)=0
Notify the subclass of the end of a TXOP on the given link.
std::optional< Time > GetTransitionTimeout() const
std::pair< bool, Time > GetDelayUntilAccessRequest(uint8_t linkId, AcIndex aci)
Notify that an UL TXOP is gained on the given link by the given AC.
virtual std::pair< bool, Time > DoGetDelayUntilAccessRequest(uint8_t linkId)=0
Subclasses have to provide an implementation for this method, that is called by the base class when t...
Ptr< EhtFrameExchangeManager > GetEhtFem(uint8_t linkId) const
void TxDropped(WifiMacDropReason reason, Ptr< const WifiMpdu > mpdu)
Notify that the given MPDU has been discarded for the given reason.
void NotifyUlTxopStart(uint8_t linkId)
Notify the start of an UL TXOP on the given link.
void TxOk(Ptr< const WifiMpdu > mpdu)
Notify the acknowledgment of the given MPDU.
std::map< uint8_t, EventId > m_ulMainPhySwitch
link ID-indexed map of timers started when an aux PHY gains an UL TXOP and schedules a channel switch...
bool m_inDeviceInterference
whether in-device interference is such that a PHY cannot decode anything and cannot decrease the back...
bool GetCamStateReset() const
void SetEmlsrLinks(const std::set< uint8_t > &linkIds)
Take actions to enable EMLSR mode on the given set of links, if non-empty, or disable EMLSR mode,...
void SetMediumSyncOfdmEdThreshold(int8_t threshold)
Set the Medium Synchronization OFDM ED threshold (dBm) to use while the MediumSyncDelay timer is runn...
uint8_t m_mainPhyId
ID of main PHY (position in the vector of PHYs held by WifiNetDevice)
int8_t GetMediumSyncOfdmEdThreshold() const
std::map< uint8_t, MediumSyncDelayStatus > m_mediumSyncDelayStatus
the status of MediumSyncDelay timers (link ID-indexed)
bool m_auxPhyToSleep
whether Aux PHYs should be put into sleep mode while the Main PHY is carrying out a (DL or UL) TXOP
virtual std::pair< bool, Time > GetDelayUnlessMainPhyTakesOverUlTxop(uint8_t linkId)=0
Subclasses have to provide an implementation for this method, that is called by the base class when t...
virtual void SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId, AcIndex aci)=0
Subclasses have to provide an implementation for this method, that is called by the base class when t...
void NotifyDlTxopStart(uint8_t linkId)
Notify the start of a DL TXOP on the given link.
void NotifyMgtFrameReceived(Ptr< const WifiMpdu > mpdu, uint8_t linkId)
Notify the reception of a management frame addressed to us.
virtual void DoNotifyUlTxopStart(uint8_t linkId)=0
Notify the subclass of the start of an UL TXOP on the given link.
Ptr< StaWifiMac > m_staMac
the MAC of the managed non-AP MLD
virtual void DoNotifyMgtFrameReceived(Ptr< const WifiMpdu > mpdu, uint8_t linkId)=0
Notify the subclass of the reception of a management frame addressed to us.
Time m_emlsrPaddingDelay
EMLSR Padding delay.
bool GetInDeviceInterference() const
virtual void NotifyMainPhySwitch(std::optional< uint8_t > currLinkId, uint8_t nextLinkId, Ptr< WifiPhy > auxPhy, Time duration)=0
Notify subclass that the main PHY is switching channel to operate on another link.
MHz_u m_auxPhyMaxWidth
max channel width supported by aux PHYs
virtual void NotifyInDeviceInterferenceStart(uint8_t linkId, Time duration)
Notify that an STA affiliated with the EMLSR client is causing in-device interference for the given a...
void SetMediumSyncMaxNTxops(std::optional< uint8_t > nTxops)
Set the maximum number of TXOPs a non-AP STA is allowed to attempt to initiate while the MediumSyncDe...
Time m_emlsrTransitionDelay
EMLSR Transition delay.
void SetWifiMac(Ptr< StaWifiMac > mac)
Set the wifi MAC.
bool GetAuxPhyTxCapable() const
const std::set< uint8_t > & GetEmlsrLinks() const
MainPhySwitchTracedCallback m_mainPhySwitchTrace
main PHY switch trace source
Time m_mediumSyncDuration
duration of the MediumSyncDelay timer
void NotifyTxopEnd(uint8_t linkId, Ptr< QosTxop > edca=nullptr)
Notify the end of a TXOP on the given link.
std::optional< Time > m_emlsrTransitionTimeout
Transition timeout advertised by APs with EMLSR activated.
void SwitchAuxPhy(Ptr< WifiPhy > auxPhy, uint8_t currLinkId, uint8_t nextLinkId)
Switch channel on the Aux PHY operating on the given current link so that it operates on the given ne...
std::map< Ptr< WifiPhy >, dBm_u > m_prevCcaEdThreshold
the CCA sensitivity threshold to restore once the MediumSyncDelay timer expires or the PHY moves to a...
std::optional< Time > GetElapsedMediumSyncDelayTimer(uint8_t linkId) const
Check whether the MediumSyncDelay timer is running for the STA operating on the given link.
virtual void DoSetWifiMac(Ptr< StaWifiMac > mac)
Allow subclasses to take actions when the MAC is set.
std::map< uint8_t, Time > m_noPhySince
link ID-indexed map of the time since no PHY is operating on the link
void SetAuxPhyTxCapable(bool capable)
Set the member variable indicating whether Aux PHYs are capable of transmitting PPDUs.
virtual std::optional< WifiIcfDrop > CheckMainPhyTakesOverDlTxop(uint8_t linkId) const
This method is called when an aux PHY has completed reception of an ICF to determine whether there is...
std::optional< std::set< uint8_t > > m_nextEmlsrLinks
ID of the links that will become the EMLSR links when the pending notification frame is acknowledged.
void SetCcaEdThresholdOnLinkSwitch(Ptr< WifiPhy > phy, uint8_t linkId)
Set the CCA ED threshold (if needed) on the given PHY that is switching channel to operate on the giv...
void SetMainPhyId(uint8_t mainPhyId)
Set the ID of main PHY (position in the vector of PHYs held by WifiNetDevice).
virtual void EmlsrLinkSwitchCallback(uint8_t linkId, Ptr< WifiPhy > phy, bool connected)
Callback connected to the EmlsrLinkSwitch trace source of StaWifiMac.
const WifiPhyOperatingChannel & GetChannelForMainPhy(uint8_t linkId) const
void SetMediumSyncDuration(Time duration)
Set the duration of the MediumSyncDelay timer.
Time m_lastAdvTransitionDelay
last advertised transition delay
MainPhySwitchInfo m_mainPhySwitchInfo
main PHY switch info
virtual void DoNotifyDlTxopStart(uint8_t linkId)=0
Notify the subclass of the reception of an initial Control frame on the given link.
static constexpr bool DONT_REQUEST_ACCESS
do not request channel access when PHY switch ends
void DoDispose() override
Destructor implementation.
void StartMediumSyncDelayTimer(uint8_t linkId)
Start the MediumSyncDelay timer and take the appropriate actions.
int8_t m_msdOfdmEdThreshold
MediumSyncDelay OFDM ED threshold.
virtual void DoNotifyProtectionCompleted(uint8_t linkId)=0
Notify the subclass that protection (if required) is completed and data frame exchange can start on t...
std::optional< uint8_t > m_msdMaxNTxops
MediumSyncDelay max number of TXOPs.
Ptr< StaWifiMac > GetStaMac() const
WifiModulationClass m_auxPhyMaxModClass
max modulation class supported by aux PHYs
uint8_t GetMainPhyId() const
std::optional< uint8_t > GetMediumSyncMaxNTxops() const
void SetCamStateReset(bool enable)
Set the member variable indicating whether the state of the CAM should be reset when the main PHY swi...
virtual void NotifyRtsSent(uint8_t linkId, Ptr< const WifiPsdu > rts, const WifiTxVector &txVector)
Notify that RTS transmission is starting on the given link.
EventId m_transitionTimeoutEvent
Timer started after the successful transmission of an EML Operating Mode Notification frame.
void SetInDeviceInterference(bool enable)
Set the member variable indicating whether in-device interference is such that a PHY cannot decode an...
const WifiPhyOperatingChannel & GetChannelForAuxPhy(uint8_t linkId) const
bool m_resetCamState
whether to reset the state of CAM when main PHY switches channel
static TypeId GetTypeId()
Get the type ID.
std::pair< bool, Time > CheckPossiblyReceivingIcf(uint8_t linkId) const
Check whether a PPDU that may be an ICF is being received on the given link.
std::set< uint8_t > m_emlsrLinks
ID of the EMLSR links (empty if EMLSR mode is disabled)
void SetSleepStateForAllAuxPhys(bool sleep)
Set sleep state or awake state for all aux PHYs.
Hold variables of type enum.
void Cancel()
This method is syntactic sugar for the ns3::Simulator::Cancel method.
bool IsPending() const
This method is syntactic sugar for !IsExpired().
EventImpl * PeekEventImpl() const
void Invoke()
Called by the simulation engine to notify the event that it is time to execute.
Implement the header for Action frames of type EML Operating Mode Notification.
void SetLinkIdInBitmap(uint8_t linkId)
Set the bit position in the link bitmap corresponding to the given link.
EmlControl m_emlControl
EML Control field.
std::optional< EmlsrParamUpdate > m_emlsrParamUpdate
EMLSR Parameter Update field.
A base class which provides memory management and object aggregation.
virtual void DoDispose()
Destructor implementation.
bool IsInitialized() const
Check if the object has been initialized.
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 Time Now()
Return the current simulation virtual time.
static EventId ScheduleNow(FUNC f, Ts &&... args)
Schedule an event to expire Now.
static Time GetDelayLeft(const EventId &id)
Get the remaining time until this event will execute.
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.
AttributeValue implementation for Time.
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
a unique identifier for an interface.
@ ATTR_GET
The attribute can be read.
@ ATTR_CONSTRUCT
The attribute can be written at construction-time.
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Hold an unsigned integer type.
Class that keeps track of all information about the current PHY operating channel.
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
#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...
Ptr< AttributeChecker > MakeAttributeContainerChecker()
Make uninitialized AttributeContainerChecker using explicit types.
Ptr< const AttributeAccessor > MakeAttributeContainerAccessor(T1 a1)
Make AttributeContainerAccessor using explicit types.
Ptr< const AttributeChecker > MakeBooleanChecker()
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Ptr< const AttributeAccessor > MakeEnumAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Ptr< const AttributeChecker > MakeUintegerChecker()
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
#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_WARN(msg)
Use NS_LOG to output a message of level LOG_WARN.
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Time Seconds(double value)
Construct a Time in the indicated unit.
Ptr< const TraceSourceAccessor > MakeTraceSourceAccessor(T a)
Create a TraceSourceAccessor which will control access to the underlying trace source.
WifiMacDropReason
The reason why an MPDU was dropped.
AcIndex
This enumeration defines the Access Categories as an enumeration with values corresponding to the AC ...
@ WIFI_STANDARD_UNSPECIFIED
@ WIFI_MOD_CLASS_OFDM
OFDM (Clause 17)
@ WIFI_MOD_CLASS_HR_DSSS
HR/DSSS (Clause 16)
@ WIFI_MOD_CLASS_HT
HT (Clause 19)
@ WIFI_MOD_CLASS_EHT
EHT (Clause 36)
@ WIFI_MOD_CLASS_VHT
VHT (Clause 22)
@ WIFI_MOD_CLASS_HE
HE (Clause 27)
@ WIFI_MOD_CLASS_ERP_OFDM
ERP-OFDM (18.4)
Every class exported by the ns3 library is enclosed in the ns3 namespace.
const Time MEDIUM_SYNC_THRESHOLD
The aMediumSyncThreshold defined by Sec. 35.3.16.18.1 of 802.11be D4.0.
static constexpr uint8_t DEFAULT_MSD_MAX_N_TXOPS
default MediumSyncDelay max number of TXOP attempts
@ SWITCHING
The PHY layer is switching to other channel.
@ TX
The PHY layer is sending a packet.
@ SLEEP
The PHY layer is sleeping.
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...
Ptr< const AttributeChecker > MakeEnumChecker(T v, std::string n, Ts... args)
Make an EnumChecker pre-configured with a set of allowed values by name.
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...
static constexpr int8_t DEFAULT_MSD_OFDM_ED_THRESH
default MediumSyncDelay timer OFDM ED threshold
@ LOG_FUNCTION
Function tracing for non-trivial function calls.
Ptr< T1 > StaticCast(const Ptr< T2 > &p)
Cast a Ptr.
static constexpr uint16_t SU_STA_ID
STA_ID to identify a single user (SU)
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 constexpr uint16_t DEFAULT_MSD_DURATION_USEC
default MediumSyncDelay timer duration (max PPDU TX time rounded to a multiple of 32 us)
Struct to trace that main PHY switched to start a DL TXOP after that an aux PHY received an ICF.
Base struct for EMLSR Main PHY switch traces.
std::string reason
the reason for switching the main PHY
uint8_t from
ID of the link which the main PHY is/has been leaving.
Time start
start of channel switching
uint8_t to
ID of the link which the main PHY is moving to.
bool disconnected
true if the main PHY is not connected to any link, i.e., it is switching or waiting to be connected t...
uint8_t emlsrMode
EMLSR Mode.
uint8_t emlsrParamUpdateCtrl
EMLSR Parameter Update Control.
EMLSR Parameter Update field.