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
57 TypeId(
"ns3::EhtFrameExchangeManager")
59 .AddConstructor<EhtFrameExchangeManager>()
61 .AddAttribute(
"EarlyTxopEndDetect",
62 "Whether the Duration/ID value of the frame being transmitted "
63 "or received can be used to early detect an ongoing TXOP end.",
102 protectionManager->SetLinkId(linkId);
106 ackManager->SetLinkId(linkId);
120 !mpdu->GetHeader().IsQosData() ||
122 mpdu->GetHeader().GetAddr1().IsGroup() ||
129 auto& hdr = mpdu->GetHeader();
133 hdr.SetAddr1(*address);
141 if (hdr.IsQosAmsdu())
143 if (hdr.IsToDs() && !hdr.IsFromDs())
146 hdr.SetAddr3(hdr.GetAddr1());
148 else if (!hdr.IsToDs() && hdr.IsFromDs())
151 hdr.SetAddr3(hdr.GetAddr2());
165 return m_staMac->GetMacQueueScheduler()->GetAllQueuesBlockedOnLink(
179 for (uint8_t linkId = 0; linkId <
m_apMac->GetNLinks(); linkId++)
187 std::set<Mac48Address> emlsrClients;
192 if (ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder &&
193 m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(
194 ehtFem->m_txopHolder.value()))
196 NS_LOG_DEBUG(
"Involved in UL TXOP: " << ehtFem->m_txopHolder.value());
197 emlsrClients.insert(ehtFem->m_txopHolder.value());
201 for (
const auto& address : ehtFem->m_protectedStas)
203 if (
m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(address))
206 emlsrClients.insert(address);
210 for (
const auto& address : emlsrClients)
213 m_mac->GetWifiRemoteStationManager(linkId)->GetMldAddress(address);
214 NS_ASSERT_MSG(mldAddress,
"MLD address not found for " << address);
230 "No mask for client " << *mldAddress <<
" on link " << +
m_linkId);
235 "Transmissions to " << *mldAddress <<
" on link " << +
m_linkId
236 <<
" are not blocked");
252 NS_LOG_DEBUG(
"StartTransmission called while another EMLSR link is being used");
257 auto emlsrManager =
m_staMac->GetEmlsrManager();
259 if (
auto elapsed = emlsrManager->GetElapsedMediumSyncDelayTimer(
m_linkId);
260 elapsed && emlsrManager->MediumSyncDelayNTxopsExceeded(
m_linkId))
262 NS_LOG_DEBUG(
"No new TXOP attempts allowed while MediumSyncDelay is running");
266 emlsrManager->GetMediumSyncDuration() - *elapsed,
285 if (
const auto [startTxop, delay] = emlsrManager->GetDelayUntilAccessRequest(
291 if (delay.IsStrictlyPositive())
347 auto sigBMode = phy->GetSigBMode(txVector);
353 if (
m_apMac && psdu->GetHeader(0).IsTrigger())
365 for (uint8_t linkId = 0; linkId <
m_apMac->GetNLinks(); ++linkId)
368 m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*clientMld))
379 psdu->GetHeader(0).IsRts())
390 auto delay =
m_apMac->GetApEmlsrManager()->GetDelayOnTxPsduNotForEmlsr(psdu,
413 m_staMac->GetEmlsrManager()->GetInDeviceInterference())
416 m_staMac->GetEmlsrManager()->NotifyInDeviceInterferenceStart(
m_linkId, txDuration);
437 psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
442 for (
const auto& client : recipients)
452 for (uint8_t linkId = 0; linkId <
m_apMac->GetNLinks(); ++linkId)
455 m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*clientMld))
457 m_mac->BlockUnicastTxOnLinks(
472 const auto psduMapIt = psduMap.find(aid);
473 const auto aidNotFoundAndNotTf = (psduMapIt == psduMap.cend()) && !
IsTrigger(psduMap);
475 const auto psdu = (psduMapIt != psduMap.cend() ? psduMapIt : psduMap.cbegin())->
second;
491 m_staMac->GetEmlsrManager()->GetInDeviceInterference())
494 m_staMac->GetEmlsrManager()->NotifyInDeviceInterferenceStart(
m_linkId, txDuration);
508 for (
const auto& phy :
m_staMac->GetDevice()->GetPhys())
514 if (
auto id =
m_staMac->GetLinkForPhy(phy);
517 const auto txPower = phy->GetPower(txVector.
GetTxPowerLevel()) + phy->GetTxGain();
541 for (
const auto& [range, interface] : rxPhy->GetSpectrumPhyInterfaces())
543 if (!interface->GetRxSpectrumModel())
555 spectrumSignalParams->duration = duration;
556 spectrumSignalParams->txPhy = txPhy->GetCurrentInterface();
557 spectrumSignalParams->txAntenna = txPhy->GetAntenna();
558 spectrumSignalParams->psd = psd;
560 rxPhy->StartRx(spectrumSignalParams, interface);
594 NS_ASSERT_MSG(mldAddress,
"MLD address not found for " << address);
596 std::set<uint8_t> linkIds{
m_linkId};
625 for (uint8_t linkId = 0; linkId <
m_apMac->GetNLinks(); ++linkId)
627 if (!
m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
634 if (ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder &&
635 m_mac->GetWifiRemoteStationManager(linkId)->GetMldAddress(*ehtFem->m_txopHolder) ==
638 NS_LOG_DEBUG(
"EMLSR client " << *mldAddress <<
" is the holder of an UL TXOP on link "
639 << +linkId <<
", do not unblock links");
643 if (linkId ==
m_linkId && !checkThisLink)
648 linkIds.insert(linkId);
651 m_apMac->GetWifiRemoteStationManager(linkId)->GetAffiliatedStaAddress(*mldAddress);
653 (ehtFem->m_sentRtsTo.contains(*linkAddr) || ehtFem->m_sentFrameTo.contains(*linkAddr) ||
654 ehtFem->m_protectedStas.contains(*linkAddr)))
657 <<
" has been sent an ICF, do not unblock links");
675 NS_ASSERT_MSG(mldAddress,
"MLD address not found for " << address);
678 auto blockLinks = [=,
this](
bool checkThisLink) {
681 NS_LOG_DEBUG(
"Could not unblock transmissions to " << address);
686 std::set<uint8_t> linkIds;
687 for (uint8_t linkId = 0; linkId <
m_mac->GetNLinks(); linkId++)
689 if (
m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
691 linkIds.insert(linkId);
700 auto unblockLinks = [=,
this]() {
710 emlCapabilities->get().emlsrTransitionDelay);
712 endDelay.IsZero() ? unblockLinks()
725 delay.
IsZero() ? blockLinks(
false)
749 m_staMac->NotifySwitchingEmlsrLink(phy, linkId, delay);
766 const auto sequence =
m_txMiddle->GetNextSequenceNumberFor(&hdr);
775 packet->AddHeader(frame);
776 packet->AddHeader(actionHdr);
800 for (uint8_t linkId = 0; linkId <
m_mac->GetNLinks(); linkId++)
802 std::optional<Mac48Address> linkAddress;
804 (linkAddress =
m_mac->GetWifiRemoteStationManager(linkId)->GetAffiliatedStaAddress(
806 (optRssi =
m_mac->GetWifiRemoteStationManager(linkId)->GetMostRecentRssi(*linkAddress)))
828 uint8_t maxPaddingDelay = 0;
829 bool isUnprotectedEmlsrDst =
false;
831 for (
const auto& address : recipients)
839 isUnprotectedEmlsrDst =
true;
842 maxPaddingDelay = std::max(maxPaddingDelay, emlCapabilities->get().emlsrPaddingDelay);
845 if (isUnprotectedEmlsrDst)
854 if (maxPaddingDelay > 0)
858 std::size_t nDbps = rate / 1e6 * 4;
883 auto mainPhy =
m_staMac->GetDevice()->GetPhy(
m_staMac->GetEmlsrManager()->GetMainPhyId());
892 if (mainPhy->IsStateSwitching() ||
m_mac->GetLinkForPhy(mainPhy) !=
m_linkId)
894 NS_LOG_DEBUG(
"Main PHY is switching or operating on another link, abort ICF response");
933 for (
const auto& address : clients)
961 if (
const auto apEmlsrManager =
m_apMac->GetApEmlsrManager();
964 return apEmlsrManager->UpdateCwAfterFailedIcf();
978 if (
const auto apEmlsrManager =
m_apMac->GetApEmlsrManager();
981 return apEmlsrManager->ReportFailedIcf();
996 if (staMissedTbPpduFrom.size() != nSolicitedStations)
1003 const auto apEmlsrManager =
m_apMac->GetApEmlsrManager();
1004 const auto updateFailedCw =
1005 crossLinkCollision && apEmlsrManager ? apEmlsrManager->UpdateCwAfterFailedIcf() :
true;
1011 std::size_t nSolicitedStations)
1017 if (staMissedTbPpduFrom.size() != nSolicitedStations)
1029 const std::set<Mac48Address>& staMissedResponseFrom)
const
1035 auto crossLinkCollision =
true;
1040 for (
const auto& address : staMissedResponseFrom)
1044 crossLinkCollision =
false;
1051 std::set<uint8_t> linkIds;
1052 for (uint8_t linkId = 0; linkId <
m_apMac->GetNLinks(); linkId++)
1054 if (
m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress) &&
1057 linkIds.insert(linkId);
1061 if (std::any_of(linkIds.cbegin(),
1064 [=,
this](uint8_t
id) {
1065 auto ehtFem = StaticCast<EhtFrameExchangeManager>(
1066 m_mac->GetFrameExchangeManager(id));
1067 return ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder &&
1068 m_mac->GetMldAddress(ehtFem->m_txopHolder.value()) == mldAddress;
1078 if (std::none_of(linkIds.cbegin(),
1082 [=,
this](uint8_t
id) {
1083 auto macHdr = m_mac->GetFrameExchangeManager(id)->GetReceivedMacHdr();
1084 if (!macHdr.has_value())
1088 auto addr2 = macHdr->get().GetAddr2();
1089 return m_mac->GetMldAddress(addr2) == mldAddress;
1092 crossLinkCollision =
false;
1096 return crossLinkCollision;
1108 if (m_apMac && GetWifiRemoteStationManager()->GetEmlsrEnabled(addr2))
1113 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(addr2);
1114 NS_ASSERT_MSG(mldAddress,
"MLD address not found for " << addr2);
1116 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
1118 if (linkId != m_linkId &&
1119 m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
1123 WifiRcvAddr::UNICAST,
1127 m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(
AC_BE, queueId, linkId);
1128 NS_ASSERT_MSG(mask,
"No mask for client " << *mldAddress <<
" on link " << +linkId);
1130 static_cast<std::size_t
>(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK)))
1133 "Transmissions to " << *mldAddress <<
" on link " << +linkId
1134 <<
" are not blocked");
1137 m_mac->BlockUnicastTxOnLinks(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1145 HeFrameExchangeManager::SendCtsAfterRts(rtsHdr, rtsTxVector, rtsSnr);
1161 if (psdu->GetAddr1() == address)
1170 if (mpdu->GetHeader().IsTrigger())
1173 mpdu->GetPacket()->PeekHeader(trigger);
1183 if (psdu->GetHeader(0).IsCts())
1185 if (m_apMac && psdu->GetAddr1() == m_self)
1189 if (m_staMac && psdu->GetAddr1() == m_bssid)
1197 if (psdu->GetHeader(0).IsBlockAck())
1200 psdu->GetPayload(0)->PeekHeader(blockAck);
1215EhtFrameExchangeManager::TransmissionSucceeded()
1219 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
1220 m_staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_linkId))
1222 NS_LOG_DEBUG(
"Reset the counter of TXOP attempts allowed while "
1223 "MediumSyncDelay is running");
1224 m_staMac->GetEmlsrManager()->ResetMediumSyncDelayNTxops(m_linkId);
1227 HeFrameExchangeManager::TransmissionSucceeded();
1231EhtFrameExchangeManager::TransmissionFailed(
bool forceCurrentCw)
1235 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
1236 m_staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_linkId))
1238 NS_LOG_DEBUG(
"Decrement the remaining number of TXOP attempts allowed while "
1239 "MediumSyncDelay is running");
1240 m_staMac->GetEmlsrManager()->DecrementMediumSyncDelayNTxops(m_linkId);
1243 HeFrameExchangeManager::TransmissionFailed(forceCurrentCw);
1247EhtFrameExchangeManager::NotifyChannelReleased(
Ptr<Txop> txop)
1258 if (
const auto remTxNav = m_txNav - Simulator::Now(); remTxNav.IsStrictlyPositive())
1263 for (
const auto& address : m_protectedStas)
1265 if (GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
1267 EmlsrSwitchToListening(address, delay);
1271 else if (m_staMac && m_staMac->IsEmlsrLink(m_linkId))
1274 auto edca = DynamicCast<QosTxop>(txop);
1278 m_staMac->GetEmlsrManager()->NotifyTxopEnd(m_linkId, edca);
1281 HeFrameExchangeManager::NotifyChannelReleased(txop);
1292 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
1293 m_staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_linkId))
1295 m_staMac->GetEmlsrManager()->CancelMediumSyncDelayTimer(m_linkId);
1307 for (
auto clientIt = m_protectedStas.begin(); clientIt != m_protectedStas.end();)
1316 if (GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt) && !txVector.
IsUlMu() &&
1317 !m_txTimer.GetStasExpectedToRespond().contains(*clientIt))
1319 EmlsrSwitchToListening(*clientIt,
Seconds(0));
1321 clientIt = m_protectedStas.erase(clientIt);
1330 HeFrameExchangeManager::PreProcessFrame(psdu, txVector);
1338 HeFrameExchangeManager::PostProcessFrame(psdu, txVector);
1340 if (m_apMac && m_apMac->GetApEmlsrManager())
1342 m_apMac->GetApEmlsrManager()->NotifyPsduRxOk(m_linkId, psdu);
1345 if (m_apMac && m_txopHolder == psdu->GetAddr2() &&
1346 GetWifiRemoteStationManager()->GetEmlsrEnabled(*m_txopHolder))
1348 const auto unrespondedRts = (psdu->GetHeader(0).IsRts() && !m_sendCtsEvent.IsPending());
1350 if (!m_ongoingTxopEnd.IsPending() && !unrespondedRts)
1355 Simulator::ScheduleNow(&EhtFrameExchangeManager::TxopEnd,
this, m_txopHolder);
1358 UpdateTxopEndOnRxEnd(psdu->GetDuration());
1361 if (m_staMac && m_ongoingTxopEnd.IsPending())
1363 if (GetEmlsrSwitchToListening(psdu, m_staMac->GetAssociationId(), m_self))
1366 m_ongoingTxopEnd.Cancel();
1367 m_staMac->GetEmlsrManager()->NotifyTxopEnd(m_linkId);
1371 UpdateTxopEndOnRxEnd(psdu->GetDuration());
1375 if (m_staMac && m_dlTxopStart)
1379 m_ongoingTxopEnd.Cancel();
1380 NS_LOG_DEBUG(
"Expected TXOP end=" << (Simulator::Now() + m_phy->GetSifs()).As(Time::S));
1381 m_ongoingTxopEnd = Simulator::Schedule(m_phy->GetSifs() + TimeStep(1),
1382 &EhtFrameExchangeManager::TxopEnd,
1386 m_staMac->GetEmlsrManager()->NotifyDlTxopStart(m_linkId);
1387 m_dlTxopStart =
false;
1399 if (m_ongoingTxopEnd.IsPending())
1405 if (
auto holder = FindTxopHolder(hdr, txVector); holder != sender)
1407 NS_LOG_DEBUG(
"Sender (" << sender <<
") differs from the TXOP holder ("
1412 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(sender))
1414 NS_LOG_DEBUG(
"Sender (" << sender <<
") is not an EMLSR client");
1418 NS_LOG_DEBUG(
"EMLSR client " << sender <<
" is starting a TXOP");
1421 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(sender);
1424 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
1426 if (linkId != m_linkId &&
1427 m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
1429 m_mac->BlockUnicastTxOnLinks(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1437 m_mac->GetWifiRemoteStationManager(linkId)->GetAffiliatedStaAddress(*mldAddress);
1440 StaticCast<EhtFrameExchangeManager>(m_mac->GetFrameExchangeManager(linkId));
1441 NS_LOG_DEBUG(
"Remove " << *linkAddr <<
" from protected STAs");
1442 ehtFem->m_protectedStas.erase(*linkAddr);
1443 ehtFem->m_sentRtsTo.erase(*linkAddr);
1444 ehtFem->m_sentFrameTo.erase(*linkAddr);
1451 m_mac->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1456 if (
auto it = m_transDelayTimer.find(*mldAddress);
1457 it != m_transDelayTimer.end() && it->second.IsPending())
1459 it->second.PeekEventImpl()->Invoke();
1460 it->second.Cancel();
1467EhtFrameExchangeManager::GetOngoingTxopEndEvent()
1469 return m_ongoingTxopEnd;
1477 if (m_apMac && m_apMac->GetApEmlsrManager())
1479 m_apMac->GetApEmlsrManager()->NotifyPsduRxError(m_linkId, psdu);
1489 NS_LOG_FUNCTION(
this << *mpdu << rxSignalInfo << txVector << inAmpdu);
1492 NS_ASSERT(mpdu->GetHeader().GetAddr1().IsGroup() || mpdu->GetHeader().GetAddr1() == m_self);
1494 const auto& hdr = mpdu->GetHeader();
1495 auto sender = hdr.GetAddr2();
1497 if (hdr.IsTrigger())
1505 mpdu->GetPacket()->PeekHeader(trigger);
1507 if (hdr.GetAddr1() != m_self &&
1508 (!hdr.GetAddr1().IsBroadcast() || !m_staMac->IsAssociated() ||
1515 if ((trigger.
IsMuRts() || trigger.
IsBsrp()) && !m_ongoingTxopEnd.IsPending() &&
1516 m_staMac->IsEmlsrLink(m_linkId))
1519 if (DropReceivedIcf(mpdu))
1524 m_dlTxopStart =
true;
1527 else if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) && !m_ongoingTxopEnd.IsPending() &&
1528 m_phy->GetPhyId() == m_staMac->GetEmlsrManager()->GetMainPhyId() &&
1529 (hdr.IsRts() || hdr.IsBlockAckReq() || hdr.IsData()) && hdr.GetAddr1() == m_self)
1533 if (DropReceivedIcf(mpdu))
1538 m_dlTxopStart =
true;
1544 for (
auto id : m_staMac->GetLinkIds())
1546 if (
id != m_linkId && m_staMac->IsEmlsrLink(
id))
1548 m_staMac->BlockTxOnLink(
id, WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK);
1554 if (!m_dlTxopStart && ShallDropReceivedMpdu(mpdu))
1560 HeFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
1562 if (m_apMac && GetWifiRemoteStationManager()->GetEmlsrEnabled(sender))
1564 if (hdr.IsRts() && !m_sendCtsEvent.IsPending())
1567 EmlsrSwitchToListening(sender,
Time{0});
1573 CheckEmlsrClientStartingTxop(hdr, txVector);
1583 if (!m_staMac || !m_staMac->IsEmlsrLink(m_linkId))
1600 if (m_sendCtsEvent.IsPending())
1602 NS_LOG_DEBUG(
"Dropping " << *mpdu <<
" received when CTS is scheduled for TX on link "
1607 const auto& hdr = mpdu->GetHeader();
1617 if (hdr.IsMgt() || hdr.IsCts() || hdr.IsCfEnd() || (hdr.IsData() && hdr.GetAddr1().IsGroup()))
1623 if (m_mac->GetLinkForPhy(m_staMac->GetEmlsrManager()->GetMainPhyId()) != m_linkId)
1625 NS_LOG_DEBUG(
"Dropping " << *mpdu <<
" received by an aux PHY on link " << +m_linkId);
1630 if (!m_ongoingTxopEnd.IsPending() &&
1632 return m_mac->GetQosTxop(aciAcPair.first)->GetTxopStartTime(m_linkId).has_value();
1635 NS_LOG_DEBUG(
"Dropping " << *mpdu <<
" received by main PHY on link " << +m_linkId
1636 <<
" while no TXOP is ongoing");
1649 auto emlsrManager = m_staMac->GetEmlsrManager();
1652 if (UsingOtherEmlsrLink())
1656 auto addr2 = icf->GetHeader().GetAddr2();
1657 const auto sender = GetWifiRemoteStationManager()->GetMldAddress(addr2).value_or(addr2);
1659 "If the ICF is not sent by an adhoc peer, it must be sent by an (AP) MLD");
1661 if (
auto it = std::find_if(
1662 m_staMac->GetLinkIds().cbegin(),
1663 m_staMac->GetLinkIds().cend(),
1665 [=,
this](uint8_t linkId) {
1667 StaticCast<EhtFrameExchangeManager>(m_mac->GetFrameExchangeManager(linkId));
1668 return linkId != m_linkId && m_staMac->IsEmlsrLink(linkId) &&
1669 ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder &&
1670 m_mac->GetWifiRemoteStationManager(linkId)
1671 ->GetMldAddress(*ehtFem->m_txopHolder)
1672 .value_or(*ehtFem->m_txopHolder) == sender;
1674 it != m_staMac->GetLinkIds().cend())
1679 StaticCast<EhtFrameExchangeManager>(m_mac->GetFrameExchangeManager(*it))
1680 ->m_ongoingTxopEnd.Cancel();
1683 m_staMac->UnblockTxOnLink({m_linkId}, WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK);
1693 NS_LOG_DEBUG(
"Drop ICF because another EMLSR link is being used");
1694 m_icfDropCallback({WifiIcfDrop::USING_OTHER_LINK, m_linkId, m_bssid});
1718 else if (
auto mainPhy = m_staMac->GetDevice()->GetPhy(emlsrManager->GetMainPhyId());
1721 auto reason = emlsrManager->CheckMainPhyTakesOverDlTxop(m_linkId);
1723 if (reason.has_value())
1726 "Drop ICF due to not enough time for the main PHY to switch link; reason = "
1728 m_icfDropCallback({*reason, m_linkId, m_bssid});
1736EhtFrameExchangeManager::TxopEnd(
const std::optional<Mac48Address>& txopHolder)
1740 if (m_phy && m_phy->GetInfoIfRxingPhyHeader())
1746 NS_LOG_DEBUG(
"PHY is decoding the PHY header of PPDU, postpone TXOP end");
1748 &EhtFrameExchangeManager::TxopEnd,
1754 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId))
1756 m_staMac->GetEmlsrManager()->NotifyTxopEnd(m_linkId);
1758 else if (m_apMac && txopHolder && GetWifiRemoteStationManager()->GetEmlsrEnabled(*txopHolder))
1761 EmlsrSwitchToListening(*txopHolder,
Seconds(0));
1766EhtFrameExchangeManager::UpdateTxopEndOnTxStart(
Time txDuration,
Time durationId)
1770 if (!m_ongoingTxopEnd.IsPending())
1776 m_ongoingTxopEnd.Cancel();
1779 if (m_txTimer.IsRunning())
1784 delay = m_txTimer.GetDelayLeft();
1786 else if (m_earlyTxopEndDetect && durationId <= m_phy->GetSifs())
1790 NS_LOG_DEBUG(
"Assume TXOP will end based on Duration/ID value");
1800 if (m_earlyTxopEndDetect)
1803 delay =
Min(delay, txDuration + durationId);
1807 NS_LOG_DEBUG(
"Expected TXOP end=" << (Simulator::Now() + delay).As(Time::S));
1809 Simulator::Schedule(delay, &EhtFrameExchangeManager::TxopEnd,
this, m_txopHolder);
1813EhtFrameExchangeManager::UpdateTxopEndOnRxStartIndication(
Time psduDuration)
1824 m_ongoingTxopEnd.Cancel();
1826 NS_LOG_DEBUG(
"Expected TXOP end=" << (Simulator::Now() + psduDuration).As(Time::S));
1827 m_ongoingTxopEnd = Simulator::Schedule(psduDuration +
NanoSeconds(1),
1828 &EhtFrameExchangeManager::TxopEnd,
1834EhtFrameExchangeManager::UpdateTxopEndOnRxEnd(
Time durationId)
1838 if (!m_ongoingTxopEnd.IsPending())
1844 m_ongoingTxopEnd.Cancel();
1848 if (m_earlyTxopEndDetect && durationId <= m_phy->GetSifs())
1850 NS_LOG_DEBUG(
"Assume TXOP ended based on Duration/ID value");
1851 TxopEnd(m_txopHolder);
1858 if (m_earlyTxopEndDetect)
1861 delay =
Min(delay, durationId);
1863 NS_LOG_DEBUG(
"Expected TXOP end=" << (Simulator::Now() + delay).As(Time::S));
1865 Simulator::Schedule(delay, &EhtFrameExchangeManager::TxopEnd,
this, m_txopHolder);
a polymophic address class
AttributeValue implementation for Boolean.
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 ...
bool m_earlyTxopEndDetect
whether the Duration/ID value of the frame being transmitted or received can be used to early detect ...
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...
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.
#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)
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.
std::tuple< WifiContainerQueueType, WifiRcvAddr, Mac48Address, std::optional< uint8_t > > WifiContainerQueueId
Tuple (queue type, receiver address type, Address, TID) identifying a container queue.
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.