16#include "ns3/ap-wifi-mac.h"
17#include "ns3/erp-ofdm-phy.h"
19#include "ns3/recipient-block-ack-agreement.h"
20#include "ns3/snr-tag.h"
21#include "ns3/sta-wifi-mac.h"
22#include "ns3/wifi-mac-queue.h"
23#include "ns3/wifi-mac-trailer.h"
28#undef NS_LOG_APPEND_CONTEXT
29#define NS_LOG_APPEND_CONTEXT WIFI_FEM_NS_LOG_APPEND_CONTEXT
41 return psduMap.size() == 1 && psduMap.cbegin()->first ==
SU_STA_ID &&
42 psduMap.cbegin()->second->GetNMpdus() == 1 &&
43 psduMap.cbegin()->second->GetHeader(0).IsTrigger();
49 return psduMap.size() == 1 && psduMap.cbegin()->first ==
SU_STA_ID &&
50 psduMap.cbegin()->second->GetNMpdus() == 1 &&
51 psduMap.cbegin()->second->GetHeader(0).IsTrigger();
58 TypeId(
"ns3::HeFrameExchangeManager")
60 .AddConstructor<HeFrameExchangeManager>()
62 .AddAttribute(
"ContinueTxopAfterBsrp",
63 "Whether to continue a TXOP a SIFS after the reception of responses "
64 "to a BSRP Trigger Frame when TXOP limit is zero.",
72 : m_intraBssNavEnd(0),
73 m_triggerFrameInAmpdu(false)
122 "A Multi-User Scheduler can only be aggregated to an HE AP");
143 (!(mpdu = edca->PeekNextMpdu(
m_linkId)) ||
144 (mpdu->GetHeader().IsQosData() && !mpdu->GetHeader().GetAddr1().IsGroup() &&
145 m_mac->GetBaAgreementEstablishedAsOriginator(mpdu->GetHeader().GetAddr1(),
146 mpdu->GetHeader().GetQosTid()))))
165 "The Multi-user Scheduler returned DL_MU_TX with empty psduMap, do not transmit");
216 <<
") incompatible with Basic Trigger Frame");
220 <<
") incompatible with BSRP Trigger Frame");
222 auto txVector = trigger.GetHeTbTxVector(trigger.begin()->GetAid12());
239 if (mpdu->IsQueued())
256 "Cannot use RTS/CTS with MU PPDUs");
273std::set<Mac48Address>
276 std::set<Mac48Address> recipients;
280 for (
const auto& userInfo : trigger)
282 const auto addressIt = aidAddrMap.find(userInfo.GetAid12());
283 NS_ASSERT_MSG(addressIt != aidAddrMap.end(),
"AID not found");
284 recipients.insert(addressIt->second);
304 NS_LOG_INFO(
"Multi-user scheduler aborted the transmission");
335 NS_LOG_FUNCTION(
this << muRtsSize << muRtsTxVector << txDuration << response);
339 const auto singleDurationId =
344 return singleDurationId;
381 protection->muRts.SetCsRequired(
true);
383 payload->AddHeader(protection->muRts);
389 mpdu->GetHeader().SetDuration(
391 protection->muRtsTxVector,
404 protection->muRtsTxVector,
416 protection->muRtsTxVector);
442 auto it = std::find_if(
445 [&to](std::pair<uint16_t,
Ptr<WifiPsdu>> psdu) { return psdu.second->GetAddr1() == to; });
446 if (it != psduMap.end())
481 NS_LOG_DEBUG(address <<
" did not respond, hence it is no longer protected");
491 NS_LOG_DEBUG(
"Schedule another transmission in a SIFS after successful BSRP TF");
494 if (!StartTransmission(m_edca, Seconds(0)))
512HeFrameExchangeManager::SendPsduMap()
524 std::set<Mac48Address> staExpectResponseFrom;
531 if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE)
533 auto acknowledgment =
537 for (
const auto& psdu : m_psduMap)
539 if (acknowledgment->stationsSendBlockAckReqTo.contains(psdu.second->GetAddr1()))
542 std::set<uint8_t> tids = psdu.second->GetTids();
544 "Acknowledgment method incompatible with a Multi-TID A-MPDU");
545 uint8_t tid = *tids.begin();
549 m_mac->GetQosTxop(tid)->PrepareBlockAckRequest(psdu.second->GetAddr1(), tid);
550 m_edca->GetBaManager()->ScheduleBar(reqHdr, hdr);
554 if (!acknowledgment->stationsReplyingWithNormalAck.empty())
557 timerType = WifiTxTimer::WAIT_NORMAL_ACK_AFTER_DL_MU_PPDU;
559 &acknowledgment->stationsReplyingWithNormalAck.begin()->second.ackTxVector;
560 auto from = acknowledgment->stationsReplyingWithNormalAck.begin()->first;
561 psdu = GetPsduTo(from, m_psduMap);
563 mpdu = *psdu->begin();
564 staExpectResponseFrom.insert(from);
566 else if (!acknowledgment->stationsReplyingWithBlockAck.empty())
569 timerType = WifiTxTimer::WAIT_BLOCK_ACK;
571 &acknowledgment->stationsReplyingWithBlockAck.begin()->second.blockAckTxVector;
572 auto from = acknowledgment->stationsReplyingWithBlockAck.begin()->first;
573 psdu = GetPsduTo(from, m_psduMap);
574 staExpectResponseFrom.insert(from);
581 else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_TF_MU_BAR)
583 auto acknowledgment =
static_cast<WifiDlMuTfMuBar*
>(m_txParams.m_acknowledgment.get());
590 std::map<uint16_t, CtrlBAckRequestHeader> recipients;
592 NS_ASSERT(!acknowledgment->stationsReplyingWithBlockAck.empty());
593 auto staIt = acknowledgment->stationsReplyingWithBlockAck.begin();
594 m_trigVector = staIt->second.blockAckTxVector;
595 while (staIt != acknowledgment->stationsReplyingWithBlockAck.end())
598 uint16_t staId = m_apMac->GetAssociationId(staIt->first, m_linkId);
600 m_trigVector.SetHeMuUserInfo(staId,
601 staIt->second.blockAckTxVector.GetHeMuUserInfo(staId));
602 recipients.emplace(staId, staIt->second.barHeader);
608 m_trigVector.SetLength(acknowledgment->ulLength);
610 m_triggerFrame = PrepareMuBar(m_trigVector, recipients);
617 for (
auto& station : acknowledgment->stationsReplyingWithBlockAck)
619 staExpectResponseFrom.insert(station.first);
622 Ptr<WifiPsdu> triggerPsdu = GetWifiPsdu(m_triggerFrame, acknowledgment->muBarTxVector);
623 Time txDuration = WifiPhy::CalculateTxDuration(triggerPsdu->GetSize(),
624 acknowledgment->muBarTxVector,
625 m_phy->GetPhyBand());
627 *acknowledgment->acknowledgmentTime -= (m_phy->GetSifs() + txDuration);
628 m_triggerFrame->GetHeader().SetDuration(GetPsduDurationId(txDuration, m_txParams));
631 &acknowledgment->stationsReplyingWithBlockAck.begin()->second.blockAckTxVector;
632 Time timeout = txDuration + m_phy->GetSifs() + m_phy->GetSlot() +
633 WifiPhy::CalculatePhyPreambleAndHeaderDuration(*responseTxVector);
635 m_txTimer.Set(WifiTxTimer::WAIT_BLOCK_ACKS_IN_TB_PPDU,
637 staExpectResponseFrom,
638 &HeFrameExchangeManager::BlockAcksInTbPpduTimeout,
641 staExpectResponseFrom.size());
642 m_channelAccessManager->NotifyAckTimeoutStartNow(
timeout);
644 ForwardPsduDown(triggerPsdu, acknowledgment->muBarTxVector);
649 hePhy->SetTrigVector(m_trigVector,
timeout);
657 else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_AGGREGATE_TF)
659 auto acknowledgment =
static_cast<WifiDlMuAggregateTf*
>(m_txParams.m_acknowledgment.get());
665 for (
auto& station : acknowledgment->stationsReplyingWithBlockAck)
667 staExpectResponseFrom.insert(station.first);
670 auto psduMapIt = std::find_if(m_psduMap.begin(),
673 return psdu.second->GetAddr1() == station.first;
678 std::vector<Ptr<WifiMpdu>> mpduList(psduMapIt->second->begin(),
679 psduMapIt->second->end());
680 NS_ASSERT(mpduList.size() == psduMapIt->second->GetNMpdus());
683 station.second.blockAckTxVector.SetLength(acknowledgment->ulLength);
684 mpduList.push_back(PrepareMuBar(station.second.blockAckTxVector,
685 {{psduMapIt->first, station.second.barHeader}}));
686 psduMapIt->second = Create<WifiPsdu>(std::move(mpduList));
687 m_trigVector.SetHeMuUserInfo(
689 station.second.blockAckTxVector.GetHeMuUserInfo(psduMapIt->first));
692 timerType = WifiTxTimer::WAIT_BLOCK_ACKS_IN_TB_PPDU;
694 &acknowledgment->stationsReplyingWithBlockAck.begin()->second.blockAckTxVector;
695 m_trigVector.
SetLength(acknowledgment->ulLength);
700 else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA)
704 mpdu = *m_psduMap.begin()->second->begin();
706 auto acknowledgment =
static_cast<WifiUlMuMultiStaBa*
>(m_txParams.m_acknowledgment.get());
709 for (
const auto& station : acknowledgment->stationsReceivingMultiStaBa)
711 staExpectResponseFrom.insert(station.first.first);
717 acknowledgment->baType.m_bitmapLen.clear();
719 timerType = WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF;
720 responseTxVector = &acknowledgment->tbPpduTxVector;
721 m_trigVector = GetTrigVector(m_muScheduler->GetUlMuInfo(m_linkId).trigger);
726 else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::NONE &&
727 !m_txParams.m_txVector.IsUlMu() &&
IsTrigger(m_psduMap))
729 CtrlTriggerHeader& trigger = m_muScheduler->GetUlMuInfo(m_linkId).trigger;
734 for (
const auto& userInfo : trigger)
736 auto staIt = m_apMac->GetStaList(m_linkId).find(userInfo.GetAid12());
737 NS_ASSERT(staIt != m_apMac->GetStaList(m_linkId).end());
738 staExpectResponseFrom.insert(staIt->second);
741 timerType = WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF;
742 txVector = trigger.GetHeTbTxVector(trigger.begin()->GetAid12());
743 responseTxVector = &txVector;
744 m_trigVector = GetTrigVector(m_muScheduler->GetUlMuInfo(m_linkId).trigger);
749 else if (m_txParams.m_txVector.IsUlMu() &&
750 m_txParams.m_acknowledgment->method == WifiAcknowledgment::ACK_AFTER_TB_PPDU)
753 timerType = WifiTxTimer::WAIT_BLOCK_ACK_AFTER_TB_PPDU;
754 NS_ASSERT(m_staMac && m_staMac->IsAssociated());
755 auto recv = m_psduMap.begin()->second->GetAddr1();
756 txVector = GetWifiRemoteStationManager()->GetBlockAckTxVector(recv, m_txParams.m_txVector);
757 responseTxVector = &txVector;
758 staExpectResponseFrom.insert(recv);
763 else if (m_txParams.m_txVector.IsUlMu() &&
764 m_txParams.m_acknowledgment->method == WifiAcknowledgment::NONE)
770 NS_ABORT_MSG(
"Unable to handle the selected acknowledgment method ("
771 << m_txParams.m_acknowledgment.get() <<
")");
776 for (
const auto& psdu : m_psduMap)
778 psduMap.emplace(psdu.first, psdu.second);
782 if (m_txParams.m_txVector.IsUlMu())
784 txDuration = HePhy::ConvertLSigLengthToHeTbPpduDuration(m_txParams.m_txVector.GetLength(),
785 m_txParams.m_txVector,
786 m_phy->GetPhyBand());
791 WifiPhy::CalculateTxDuration(psduMap, m_txParams.m_txVector, m_phy->GetPhyBand());
794 Time durationId = GetPsduDurationId(txDuration, m_txParams);
796 if (m_continueTxopAfterBsrpTf && m_edca && m_edca->GetTxopLimit(m_linkId).IsZero() &&
797 timerType == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF)
801 durationId += m_muScheduler->GetExtraTimeForBsrpTfDurationId(m_linkId);
804 for (
auto& psdu : m_psduMap)
806 psdu.second->SetDuration(durationId);
810 if (timerType == WifiTxTimer::NOT_RUNNING)
815 Simulator::Schedule(txDuration + m_phy->GetSifs(),
816 &HeFrameExchangeManager::SendPsduMap,
819 else if (!m_txParams.m_txVector.IsUlMu())
821 Simulator::Schedule(txDuration, &HeFrameExchangeManager::TransmissionSucceeded,
this);
826 Time timeout = txDuration + m_phy->GetSifs() + m_phy->GetSlot() +
827 WifiPhy::CalculatePhyPreambleAndHeaderDuration(*responseTxVector);
828 m_channelAccessManager->NotifyAckTimeoutStartNow(
timeout);
833 case WifiTxTimer::WAIT_NORMAL_ACK_AFTER_DL_MU_PPDU:
835 m_txTimer.Set(timerType,
837 staExpectResponseFrom,
838 &HeFrameExchangeManager::NormalAckTimeout,
841 m_txParams.m_txVector);
843 case WifiTxTimer::WAIT_BLOCK_ACK:
845 m_txTimer.Set(timerType,
847 staExpectResponseFrom,
848 &HeFrameExchangeManager::BlockAckTimeout,
851 m_txParams.m_txVector);
853 case WifiTxTimer::WAIT_BLOCK_ACKS_IN_TB_PPDU:
854 m_txTimer.Set(timerType,
856 staExpectResponseFrom,
857 &HeFrameExchangeManager::BlockAcksInTbPpduTimeout,
860 staExpectResponseFrom.size());
862 case WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF:
863 case WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF:
864 m_txTimer.Set(timerType,
866 staExpectResponseFrom,
867 &HeFrameExchangeManager::TbPpduTimeout,
870 staExpectResponseFrom.size());
872 case WifiTxTimer::WAIT_BLOCK_ACK_AFTER_TB_PPDU:
873 m_txTimer.Set(timerType,
875 staExpectResponseFrom,
876 &HeFrameExchangeManager::BlockAckAfterTbPpduTimeout,
878 m_psduMap.begin()->second,
879 m_txParams.m_txVector);
888 ForwardPsduMapDown(psduMap, m_txParams.m_txVector);
890 if (timerType == WifiTxTimer::WAIT_BLOCK_ACKS_IN_TB_PPDU ||
891 timerType == WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF ||
892 timerType == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF)
895 auto hePhy = StaticCast<HePhy>(m_phy->GetPhyEntity(responseTxVector->GetModulationClass()));
896 hePhy->SetTrigVector(m_trigVector, m_txTimer.GetDelayLeft());
898 else if (timerType == WifiTxTimer::NOT_RUNNING &&
899 (m_txParams.m_txVector.IsUlMu() ||
900 m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE))
904 Simulator::Schedule(txDuration, &WifiPsduMap::clear, &m_psduMap);
907 if (m_txTimer.IsRunning() && timerType != WifiTxTimer::WAIT_BLOCK_ACK_AFTER_TB_PPDU)
913 for (
const auto& address : staExpectResponseFrom)
915 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(address) ||
916 timerType == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF ||
917 m_protectedStas.contains(address))
919 m_sentFrameTo.insert(address);
933 auto sigBMode = hePhy->GetSigBMode(txVector);
937 for (
const auto& psdu : psduMap)
939 NS_LOG_DEBUG(
"Transmitting: [STAID=" << psdu.first <<
", " << *psdu.second <<
"]");
942 for (
const auto& [staId, psdu] : psduMap)
944 FinalizeMacHeader(psdu);
945 NotifyTxToEdca(psdu);
949 if (psduMap.size() > 1 || psduMap.begin()->second->IsAggregate() ||
950 psduMap.begin()->second->IsSingle())
955 const auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, m_phy->GetPhyBand());
956 SetTxNav(*psduMap.cbegin()->second->begin(), txDuration);
958 m_phy->Send(psduMap, txVector);
962HeFrameExchangeManager::PrepareMuBar(
const WifiTxVector& responseTxVector,
963 std::map<uint16_t, CtrlBAckRequestHeader> recipients)
const
970 SetTargetRssi(muBar);
976 for (
auto& userInfo : muBar)
978 auto recipientIt = recipients.find(userInfo.GetAid12());
979 NS_ASSERT(recipientIt != recipients.end());
982 userInfo.SetMuBarTriggerDepUserInfo(recipientIt->second);
986 bar->AddHeader(muBar);
994 rxAddress = Mac48Address::GetBroadcast();
999 rxAddress = m_apMac->GetStaList(m_linkId).at(recipients.begin()->first);
1011 return Create<WifiMpdu>(bar, hdr);
1020 if (protection->
method == WifiProtection::MU_RTS_CTS)
1027 GetCtsTxVectorAfterMuRts(muRtsCtsProtection->muRts,
1028 muRtsCtsProtection->muRts.begin()->GetAid12());
1031 muRtsCtsProtection->muRts.GetSerializedSize() + WIFI_MAC_FCS_LENGTH;
1032 muRtsCtsProtection->protectionTime =
1033 WifiPhy::CalculateTxDuration(muRtsSize,
1034 muRtsCtsProtection->muRtsTxVector,
1035 m_phy->GetPhyBand()) +
1036 WifiPhy::CalculateTxDuration(
GetCtsSize(), ctsTxVector, m_phy->GetPhyBand()) +
1037 2 * m_phy->GetSifs();
1041 VhtFrameExchangeManager::CalculateProtectionTime(protection);
1054 if (acknowledgment->
method == WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE)
1061 NS_ABORT_IF(dlMuBarBaAcknowledgment->stationsReplyingWithNormalAck.size() +
1062 dlMuBarBaAcknowledgment->stationsReplyingWithBlockAck.size() >
1065 if (!dlMuBarBaAcknowledgment->stationsReplyingWithNormalAck.empty())
1068 dlMuBarBaAcknowledgment->stationsReplyingWithNormalAck.begin()->second;
1071 WifiPhy::CalculateTxDuration(
GetAckSize(), info.ackTxVector, m_phy->GetPhyBand());
1074 if (!dlMuBarBaAcknowledgment->stationsReplyingWithBlockAck.empty())
1077 dlMuBarBaAcknowledgment->stationsReplyingWithBlockAck.begin()->second;
1079 m_phy->GetSifs() + WifiPhy::CalculateTxDuration(
GetBlockAckSize(info.baType),
1080 info.blockAckTxVector,
1081 m_phy->GetPhyBand());
1084 for (
const auto& stations : dlMuBarBaAcknowledgment->stationsSendBlockAckReqTo)
1086 const auto& info = stations.second;
1087 duration += m_phy->GetSifs() +
1089 info.blockAckReqTxVector,
1090 m_phy->GetPhyBand()) +
1093 info.blockAckTxVector,
1094 m_phy->GetPhyBand());
1097 dlMuBarBaAcknowledgment->acknowledgmentTime = duration;
1102 else if (acknowledgment->
method == WifiAcknowledgment::DL_MU_TF_MU_BAR)
1104 auto dlMuTfMuBarAcknowledgment =
static_cast<WifiDlMuTfMuBar*
>(acknowledgment);
1108 for (
const auto& stations : dlMuTfMuBarAcknowledgment->stationsReplyingWithBlockAck)
1111 const auto& info = stations.second;
1112 NS_ASSERT(info.blockAckTxVector.GetHeMuUserInfoMap().size() == 1);
1113 uint16_t staId = info.blockAckTxVector.GetHeMuUserInfoMap().begin()->first;
1115 info.blockAckTxVector,
1116 m_phy->GetPhyBand(),
1119 if (currBlockAckDuration > duration)
1121 duration = currBlockAckDuration;
1127 WifiTxVector& txVector = dlMuTfMuBarAcknowledgment->stationsReplyingWithBlockAck.begin()
1128 ->second.blockAckTxVector;
1129 std::tie(dlMuTfMuBarAcknowledgment->ulLength, duration) =
1130 HePhy::ConvertHeTbPpduDurationToLSigLength(duration, txVector, m_phy->GetPhyBand());
1133 if (dlMuTfMuBarAcknowledgment->muBarTxVector.GetModulationClass() >=
WIFI_MOD_CLASS_VHT)
1136 muBarSize = MpduAggregator::GetSizeIfAggregated(muBarSize, 0);
1138 dlMuTfMuBarAcknowledgment->acknowledgmentTime =
1140 WifiPhy::CalculateTxDuration(muBarSize,
1141 dlMuTfMuBarAcknowledgment->muBarTxVector,
1142 m_phy->GetPhyBand()) +
1143 m_phy->GetSifs() + duration;
1148 else if (acknowledgment->
method == WifiAcknowledgment::DL_MU_AGGREGATE_TF)
1154 for (
const auto& stations : dlMuAggrTfAcknowledgment->stationsReplyingWithBlockAck)
1157 const auto& info = stations.second;
1158 NS_ASSERT(info.blockAckTxVector.GetHeMuUserInfoMap().size() == 1);
1159 uint16_t staId = info.blockAckTxVector.GetHeMuUserInfoMap().begin()->first;
1161 info.blockAckTxVector,
1162 m_phy->GetPhyBand(),
1165 if (currBlockAckDuration > duration)
1167 duration = currBlockAckDuration;
1174 dlMuAggrTfAcknowledgment->stationsReplyingWithBlockAck.begin()->second.blockAckTxVector;
1175 std::tie(dlMuAggrTfAcknowledgment->ulLength, duration) =
1176 HePhy::ConvertHeTbPpduDurationToLSigLength(duration, txVector, m_phy->GetPhyBand());
1177 dlMuAggrTfAcknowledgment->acknowledgmentTime = m_phy->GetSifs() + duration;
1182 else if (acknowledgment->
method == WifiAcknowledgment::UL_MU_MULTI_STA_BA)
1187 ulMuMultiStaBa->multiStaBaTxVector,
1188 m_phy->GetPhyBand());
1189 ulMuMultiStaBa->acknowledgmentTime = m_phy->GetSifs() + duration;
1194 else if (acknowledgment->
method == WifiAcknowledgment::ACK_AFTER_TB_PPDU)
1203 VhtFrameExchangeManager::CalculateAcknowledgmentTime(acknowledgment);
1208HeFrameExchangeManager::GetCtsModeAfterMuRts()
const
1213 : OfdmPhy::GetOfdmRate6Mbps();
1218 uint16_t staId)
const
1223 NS_ASSERT_MSG(userInfoIt != trigger.
end(),
"User Info field for AID=" << staId <<
" not found");
1226 if (uint8_t ru = userInfoIt->GetMuRtsRuAllocation(); ru < 65)
1244 auto txVector = GetWifiRemoteStationManager()->GetCtsTxVector(m_bssid, GetCtsModeAfterMuRts());
1246 txVector.SetChannelWidth(bw);
1252HeFrameExchangeManager::GetTxDuration(
uint32_t ppduPayloadSize,
1258 return VhtFrameExchangeManager::GetTxDuration(ppduPayloadSize, receiver, txParams);
1265 txParams.
m_acknowledgment->method == WifiAcknowledgment::DL_MU_AGGREGATE_TF)
1269 NS_ASSERT_MSG(psduInfo,
"No information for " << receiver <<
" in TX params");
1270 NS_ASSERT_MSG(!psduInfo->seqNumbers.empty(),
"No sequence number for " << receiver);
1271 const auto tid = psduInfo->seqNumbers.cbegin()->first;
1273 ppduPayloadSize = MpduAggregator::GetSizeIfAggregated(
1274 GetMuBarSize({m_mac->GetBarTypeAsOriginator(receiver, tid)}),
1278 uint16_t staId = (txParams.
m_txVector.
IsDlMu() ? m_apMac->GetAssociationId(receiver, m_linkId)
1279 : m_staMac->GetAssociationId());
1280 Time psduDuration = WifiPhy::CalculateTxDuration(ppduPayloadSize,
1282 m_phy->GetPhyBand(),
1289HeFrameExchangeManager::TbPpduTimeout(
WifiPsduMap* psduMap, std::size_t nSolicitedStations)
1292 DoTbPpduTimeout(psduMap, nSolicitedStations,
true);
1297 std::size_t nSolicitedStations,
1298 bool updateFailedCw)
1300 const auto& staMissedTbPpduFrom = m_txTimer.GetStasExpectedToRespond();
1301 NS_LOG_FUNCTION(
this << psduMap << staMissedTbPpduFrom.size() << nSolicitedStations
1308 NS_ASSERT(!staMissedTbPpduFrom.empty());
1311 if (staMissedTbPpduFrom.size() == nSolicitedStations)
1315 psduMap->cbegin()->second->GetPayload(0)->PeekHeader(trigger);
1317 if (m_continueTxopAfterBsrpTf && m_edca->GetTxopLimit(m_linkId).IsZero() &&
1320 SendCfEndIfNeeded();
1323 TransmissionFailed(!updateFailedCw);
1325 else if (!m_multiStaBaEvent.IsPending())
1327 m_edca->ResetCw(m_linkId);
1328 TransmissionSucceeded();
1334 for (
const auto& address : staMissedTbPpduFrom)
1336 NS_LOG_DEBUG(address <<
" did not respond, hence it is no longer protected");
1337 m_protectedStas.erase(address);
1338 m_sentFrameTo.erase(address);
1340 if (m_protectedIfResponded)
1342 m_protectedStas.merge(m_sentFrameTo);
1344 m_sentFrameTo.clear();
1352 std::size_t nSolicitedStations)
1357 NS_ASSERT(m_txParams.m_acknowledgment &&
1358 (m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_AGGREGATE_TF ||
1359 m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_TF_MU_BAR));
1362 const auto& staMissedBlockAckFrom = m_txTimer.GetStasExpectedToRespond();
1363 NS_ASSERT(!staMissedBlockAckFrom.empty());
1365 if (staMissedBlockAckFrom.size() == nSolicitedStations)
1368 GetWifiRemoteStationManager()->ReportDataFailed(*psduMap->begin()->second->begin());
1374 m_triggerFrame =
nullptr;
1377 for (
const auto& sta : staMissedBlockAckFrom)
1379 auto psdu = GetPsduTo(sta, *psduMap);
1381 MissedBlockAck(psdu, m_txParams.m_txVector);
1386 if (staMissedBlockAckFrom.size() == nSolicitedStations)
1389 TransmissionFailed();
1393 m_edca->ResetCw(m_linkId);
1394 TransmissionSucceeded();
1404 GetWifiRemoteStationManager()->ReportDataFailed(*psdu->begin());
1406 MissedBlockAck(psdu, m_txParams.m_txVector);
1421 VhtFrameExchangeManager::NormalAckTimeout(mpdu, txVector);
1426 for (
auto& psdu : m_psduMap)
1430 if (mpdu->IsQueued())
1432 m_mac->GetTxopQueue(mpdu->GetQueueAc())->GetOriginal(mpdu)->GetHeader().SetRetry();
1433 mpdu->ResetInFlight(m_linkId);
1445 VhtFrameExchangeManager::BlockAckTimeout(psdu, txVector);
1450 for (
auto& psdu : m_psduMap)
1454 if (mpdu->IsQueued())
1456 mpdu->GetHeader().SetRetry();
1472 for (
const auto& userInfoField : trigger)
1475 userInfoField.GetAid12(),
1476 {userInfoField.GetRuAllocation(), userInfoField.GetUlMcs(), userInfoField.GetNss()});
1488 uint16_t staId = m_staMac->GetAssociationId();
1495 NS_ASSERT_MSG(heConfiguration,
"This STA has to be an HE station to send an HE TB PPDU");
1498 if (userInfoIt->IsUlTargetRssiMaxTxPower())
1500 NS_LOG_LOGIC(
"AP requested using the max transmit power (" << m_phy->GetTxPowerEnd()
1506 uint8_t powerLevel = GetWifiRemoteStationManager()->GetDefaultTxPowerLevel();
1524 auto optRssi = GetMostRecentRssi(triggerSender);
1530 auto reqTxPower =
dBm_u{
static_cast<double>(userInfoIt->GetUlTargetRssi() + pathLossDb)};
1533 uint8_t numPowerLevels = m_phy->GetNTxPower();
1534 if (numPowerLevels > 1)
1536 dBm_u step = (m_phy->GetTxPowerEnd() - m_phy->GetTxPowerStart()) / (numPowerLevels - 1);
1537 powerLevel =
static_cast<uint8_t
>(
1538 ceil((reqTxPower - m_phy->GetTxPowerStart()) /
1540 if (powerLevel > numPowerLevels)
1542 powerLevel = numPowerLevels;
1545 if (reqTxPower > m_phy->GetPower(powerLevel))
1547 NS_LOG_WARN(
"The requested power level (" << reqTxPower <<
"dBm) cannot be satisfied (max: "
1548 << m_phy->GetTxPowerEnd() <<
"dBm)");
1552 <<
"{pathLoss=" << pathLossDb <<
"dB, reqTxPower=" << reqTxPower <<
"dBm}"
1554 <<
"{powerLevel=" << +powerLevel <<
" -> " << m_phy->GetPower(powerLevel) <<
"dBm}"
1555 <<
" PHY power capa "
1556 <<
"{min=" << m_phy->GetTxPowerStart() <<
"dBm, max=" << m_phy->GetTxPowerEnd()
1557 <<
"dBm, levels:" << +numPowerLevels <<
"}");
1563HeFrameExchangeManager::GetMostRecentRssi(
const Mac48Address& address)
const
1565 return GetWifiRemoteStationManager()->GetMostRecentRssi(address);
1575 m_phy->GetPower(GetWifiRemoteStationManager()->GetDefaultTxPowerLevel())));
1576 for (
auto& userInfo : trigger)
1578 const auto staList = m_apMac->GetStaList(m_linkId);
1579 auto itAidAddr = staList.find(userInfo.GetAid12());
1581 auto optRssi = GetMostRecentRssi(itAidAddr->second);
1583 auto rssi =
static_cast<int8_t>(*optRssi);
1584 rssi = (rssi >= -20)
1586 : ((rssi <= -110) ? -110 : rssi);
1587 userInfo.SetUlTargetRssi(rssi);
1596 auto txVectorCopy = txVector;
1598 if (psdu->GetNMpdus() == 1 && psdu->GetHeader(0).IsTrigger())
1601 psdu->GetPayload(0)->PeekHeader(trigger);
1615 if (m_staMac !=
nullptr && m_staMac->IsAssociated() &&
1626 psdu = Create<const WifiPsdu>(Create<Packet>(), rts);
1630 GetWifiRemoteStationManager()->GetCtsTxVector(m_bssid, GetCtsModeAfterMuRts());
1633 VhtFrameExchangeManager::PostProcessFrame(psdu, txVectorCopy);
1643 if (!UlMuCsMediumIdle(trigger))
1645 NS_LOG_DEBUG(
"UL MU CS indicated medium busy, cannot send CTS");
1649 NS_ASSERT(m_staMac !=
nullptr && m_staMac->IsAssociated());
1650 WifiTxVector ctsTxVector = GetCtsTxVectorAfterMuRts(trigger, m_staMac->GetAssociationId());
1653 DoSendCtsAfterRts(muRtsHdr, ctsTxVector, muRtsSnr);
1663 txParams.
m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA);
1666 NS_ASSERT(!acknowledgment->stationsReceivingMultiStaBa.empty());
1669 blockAck.
SetType(acknowledgment->baType);
1673 for (
const auto& staInfo : acknowledgment->stationsReceivingMultiStaBa)
1675 receiver = staInfo.first.first;
1676 uint8_t tid = staInfo.first.second;
1677 std::size_t index = staInfo.second;
1679 blockAck.
SetAid11(m_apMac->GetAssociationId(receiver, m_linkId), index);
1685 NS_LOG_DEBUG(
"Multi-STA Block Ack: Sending All-ack to=" << receiver);
1690 if (acknowledgment->baType.m_bitmapLen.at(index) == 0)
1693 NS_LOG_DEBUG(
"Multi-STA Block Ack: Sending Ack to=" << receiver);
1701 auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(receiver, tid);
1703 agreement->get().FillBlockAckBitmap(blockAck, index);
1704 NS_LOG_DEBUG(
"Multi-STA Block Ack: Sending Block Ack with seq="
1706 <<
" tid=" << +tid);
1712 hdr.
SetAddr1(acknowledgment->stationsReceivingMultiStaBa.size() == 1
1714 : Mac48Address::GetBroadcast());
1720 packet->AddHeader(blockAck);
1722 GetWifiPsdu(Create<WifiMpdu>(packet, hdr), acknowledgment->multiStaBaTxVector);
1725 acknowledgment->multiStaBaTxVector,
1726 m_phy->GetPhyBand());
1738 const auto singleDurationId =
Max(durationId - m_phy->GetSifs() - txDuration,
Seconds(0));
1739 if (m_edca->GetTxopLimit(m_linkId).IsZero())
1741 psdu->SetDuration(singleDurationId);
1745 auto duration =
Max(m_edca->GetRemainingTxop(m_linkId) - txDuration,
Seconds(0));
1746 if (m_protectSingleExchange)
1748 duration = std::min(duration, singleDurationId + m_singleExchangeProtectionSurplus);
1750 psdu->SetDuration(duration);
1753 psdu->GetPayload(0)->AddPacketTag(m_muSnrTag);
1755 ForwardPsduDown(psdu, acknowledgment->multiStaBaTxVector);
1759 m_edca->ResetCw(m_linkId);
1761 Simulator::Schedule(txDuration, &HeFrameExchangeManager::TransmissionSucceeded,
this);
1770 NS_ASSERT(m_staMac && m_staMac->IsAssociated());
1772 NS_LOG_DEBUG(
"Received a Trigger Frame (basic variant) soliciting a transmission");
1774 if (!UlMuCsMediumIdle(trigger))
1785 std::vector<uint8_t> tids;
1786 uint16_t staId = m_staMac->GetAssociationId();
1789 for (uint8_t i = 0; i < 4; i++)
1792 tids.push_back(acIt->second.GetHighTid());
1793 tids.push_back(acIt->second.GetLowTid());
1805 Time ppduDuration = HePhy::ConvertLSigLengthToHeTbPpduDuration(trigger.
GetUlLength(),
1807 m_phy->GetPhyBand());
1809 for (
const auto& tid : tids)
1813 if (!m_mac->GetBaAgreementEstablishedAsOriginator(hdr.
GetAddr2(), tid))
1823 if (
auto mpdu = GetBar(edca->GetAccessCategory(), tid, hdr.
GetAddr2());
1824 mpdu && TryAddMpdu(mpdu, txParams, ppduDuration))
1827 psdu = Create<WifiPsdu>(mpdu,
true);
1833 GetWifiRemoteStationManager()->GetMldAddress(hdr.
GetAddr2()).value_or(hdr.
GetAddr2());
1834 if (
auto mpdu = edca->PeekNextMpdu(m_linkId, tid, receiver))
1836 mpdu = CreateAliasIfNeeded(mpdu);
1837 if (
auto item = edca->GetNextMpdu(m_linkId, mpdu, txParams, ppduDuration,
false))
1840 std::vector<Ptr<WifiMpdu>> mpduList =
1841 m_mpduAggregator->GetNextAmpdu(item, txParams, ppduDuration);
1842 psdu = (mpduList.size() > 1 ? Create<WifiPsdu>(std::move(mpduList))
1843 : Create<WifiPsdu>(item,
true));
1851 psdu->SetDuration(hdr.
GetDuration() - m_phy->GetSifs() - ppduDuration);
1852 SendPsduMapWithProtection(
WifiPsduMap{{staId, psdu}}, txParams);
1857 SendQosNullFramesInTbPpdu(trigger, hdr);
1867 NS_ASSERT(m_staMac && m_staMac->IsAssociated());
1871 if (!UlMuCsMediumIdle(trigger))
1877 GetWifiRemoteStationManager()->GetMldAddress(hdr.
GetAddr2()).value_or(hdr.
GetAddr2());
1880 header.
SetAddr2(m_mac->GetAddress());
1895 Time ppduDuration = HePhy::ConvertLSigLengthToHeTbPpduDuration(trigger.
GetUlLength(),
1897 m_phy->GetPhyBand());
1900 std::vector<Ptr<WifiMpdu>> mpduList;
1902 for (uint8_t tid = 0; tid < 8; ++tid)
1904 if (!m_mac->GetBaAgreementEstablishedAsOriginator(hdr.
GetAddr2(), tid))
1906 NS_LOG_DEBUG(
"Skipping tid=" << +tid <<
" because no agreement established");
1916 auto mpdu = Create<WifiMpdu>(Create<Packet>(), header);
1917 mpdu = CreateAliasIfNeeded(mpdu);
1919 UpdateTxDuration(header.
GetAddr1(), txParams);
1930 NS_LOG_DEBUG(
"Aggregating a QoS Null frame with tid=" << +tid);
1932 mpduList.push_back(mpdu);
1935 if (mpduList.empty())
1937 NS_LOG_DEBUG(
"Not enough time to send a QoS Null frame");
1941 Ptr<WifiPsdu> psdu = (mpduList.size() > 1 ? Create<WifiPsdu>(std::move(mpduList))
1942 : Create<WifiPsdu>(mpduList.front(),
true));
1943 uint16_t staId = m_staMac->GetAssociationId();
1944 SendPsduMapWithProtection(
WifiPsduMap{{staId, psdu}}, txParams);
1955 auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(m_bssid, tid);
1959 NS_LOG_DEBUG(
"There's not a valid agreement for this BlockAckReq");
1963 if (!UlMuCsMediumIdle(trigger))
1969 auto txVector = GetHeTbTxVector(trigger, m_bssid);
1970 SendBlockAck(*agreement, durationId, txVector, snr);
1989 if (ra == m_bssid || ta == m_bssid || bssid == m_bssid)
1997 if (hdr.
IsCtl() && ta == empty && ra == m_txopHolder)
2011 if (bssid != empty && bssid != m_bssid)
2019 if (bssid == empty && ta != empty && ra != empty && ta != m_bssid && ra != m_bssid)
2032 const auto bssColor = m_mac->GetHeConfiguration()->m_bssColor;
2035 return bssColor != 0 && bssColor == txVector.
GetBssColor();
2041 const Time& surplus)
2060 if (!IsIntraBssPpdu(hdr, txVector))
2062 NS_LOG_DEBUG(
"PPDU not classified as intra-BSS, update the basic NAV");
2063 VhtFrameExchangeManager::UpdateNav(hdr, txVector, surplus);
2067 NS_LOG_DEBUG(
"PPDU classified as intra-BSS, update the intra-BSS NAV");
2070 duration += surplus;
2078 NS_LOG_DEBUG(
"Received CF-End, resetting the intra-BSS NAV");
2079 IntraBssNavResetTimeout();
2085 auto intraBssNavEnd = Simulator::Now() + duration;
2086 if (intraBssNavEnd > m_intraBssNavEnd)
2088 m_intraBssNavEnd = intraBssNavEnd;
2089 NS_LOG_DEBUG(
"Updated intra-BSS NAV=" << m_intraBssNavEnd);
2104 GetWifiRemoteStationManager()->GetCtsTxVector(addr2, txVector.
GetMode());
2105 auto navResetDelay =
2106 2 * m_phy->GetSifs() +
2107 WifiPhy::CalculateTxDuration(
GetCtsSize(), ctsTxVector, m_phy->GetPhyBand()) +
2108 WifiPhy::CalculatePhyPreambleAndHeaderDuration(ctsTxVector) + 2 * m_phy->GetSlot();
2109 m_intraBssNavResetEvent.Cancel();
2110 m_intraBssNavResetEvent =
2111 Simulator::Schedule(navResetDelay,
2112 &HeFrameExchangeManager::IntraBssNavResetTimeout,
2116 NS_LOG_DEBUG(
"Current intra-BSS NAV=" << m_intraBssNavEnd);
2118 m_channelAccessManager->NotifyNavStartNow(duration);
2122HeFrameExchangeManager::ClearTxopHolderIfNeeded()
2125 if (m_intraBssNavEnd <= Simulator::Now())
2127 m_txopHolder.reset();
2132HeFrameExchangeManager::NavResetTimeout()
2135 m_navEnd = Simulator::Now();
2138 Time intraBssNav = Simulator::GetDelayLeft(m_intraBssNavResetEvent);
2139 m_channelAccessManager->NotifyNavResetNow(intraBssNav);
2143HeFrameExchangeManager::IntraBssNavResetTimeout()
2146 m_intraBssNavEnd = Simulator::Now();
2147 ClearTxopHolderIfNeeded();
2149 Time basicNav = Simulator::GetDelayLeft(m_navResetEvent);
2150 m_channelAccessManager->NotifyNavResetNow(basicNav);
2153std::optional<Mac48Address>
2164 return VhtFrameExchangeManager::FindTxopHolder(hdr, txVector);
2166 return std::nullopt;
2170HeFrameExchangeManager::VirtualCsMediumIdle()
const
2175 return m_navEnd <= Simulator::Now() && m_intraBssNavEnd <= Simulator::Now();
2191 const Time now = Simulator::Now();
2198 NS_ASSERT_MSG(m_staMac,
"UL MU CS is only performed by non-AP STAs");
2201 "No User Info field for STA (" << m_self
2202 <<
") AID=" << m_staMac->GetAssociationId());
2204 std::set<uint8_t> indices;
2208 auto ctsTxVector = GetCtsTxVectorAfterMuRts(trigger, m_staMac->GetAssociationId());
2209 auto bw = ctsTxVector.GetChannelWidth();
2210 indices = m_phy->GetOperatingChannel().GetAll20MHzChannelIndicesInPrimary(bw);
2215 m_phy->GetOperatingChannel().Get20MHzIndicesCoveringRu(userInfoIt->GetRuAllocation(),
2218 return !m_channelAccessManager->GetPer20MHzBusy(indices);
2227 NS_LOG_FUNCTION(
this << *mpdu << rxSignalInfo << txVector << inAmpdu);
2230 NS_ASSERT(mpdu->GetHeader().GetAddr1().IsGroup() || mpdu->GetHeader().GetAddr1() == m_self);
2234 if (txVector.
IsUlMu() && m_txTimer.IsRunning() &&
2235 m_txTimer.GetReason() == WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF)
2238 NS_ASSERT(m_txParams.m_acknowledgment &&
2239 m_txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA);
2240 auto acknowledgment =
static_cast<WifiUlMuMultiStaBa*
>(m_txParams.m_acknowledgment.get());
2243 if (!m_txTimer.GetStasExpectedToRespond().contains(sender))
2245 NS_LOG_WARN(
"Received a TB PPDU from an unexpected station: " << sender);
2251 NS_LOG_DEBUG(
"Received a BlockAckReq in a TB PPDU from " << sender);
2254 mpdu->GetPacket()->PeekHeader(blockAckReq);
2257 GetBaManager(tid)->NotifyGotBlockAckRequest(
2258 m_mac->GetMldAddress(sender).value_or(sender),
2263 acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, tid), index);
2264 acknowledgment->baType.m_bitmapLen.push_back(
2265 m_mac->GetBaTypeAsRecipient(sender, tid).m_bitmapLen.at(0));
2267 m_muSnrTag.Set(staId, rxSignalInfo.
snr);
2271 NS_LOG_DEBUG(
"Received an S-MPDU in a TB PPDU from " << sender <<
" (" << *mpdu <<
")");
2274 GetBaManager(tid)->NotifyGotMpdu(mpdu);
2277 acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, tid), index);
2278 acknowledgment->baType.m_bitmapLen.push_back(0);
2280 m_muSnrTag.Set(staId, rxSignalInfo.
snr);
2289 VhtFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
2294 if (!acknowledgment->stationsReceivingMultiStaBa.empty() && !m_multiStaBaEvent.IsPending())
2296 m_multiStaBaEvent = Simulator::Schedule(m_phy->GetSifs(),
2297 &HeFrameExchangeManager::SendMultiStaBlockAck,
2299 std::cref(m_txParams),
2300 mpdu->GetHeader().GetDuration());
2304 m_txTimer.GotResponseFrom(sender);
2306 if (m_txTimer.GetStasExpectedToRespond().empty())
2310 m_channelAccessManager->NotifyAckTimeoutResetNow();
2312 if (!m_multiStaBaEvent.IsPending())
2317 m_edca->ResetCw(m_linkId);
2318 TransmissionSucceeded();
2326 if (txVector.
IsUlMu() && m_txTimer.IsRunning() &&
2327 m_txTimer.GetReason() == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF &&
2330 const auto& sender = hdr.
GetAddr2();
2332 if (!m_txTimer.GetStasExpectedToRespond().contains(sender))
2334 NS_LOG_WARN(
"Received a TB PPDU from an unexpected station: " << sender);
2339 NS_LOG_WARN(
"No QoS Null frame in the received MPDU");
2343 NS_LOG_DEBUG(
"Received a QoS Null frame in a TB PPDU from " << sender);
2344 ReceivedQosNullAfterBsrpTf(sender);
2352 if (hdr.
IsCts() && m_txTimer.IsRunning() &&
2353 m_txTimer.GetReason() == WifiTxTimer::WAIT_CTS && m_psduMap.size() == 1)
2358 Mac48Address sender = m_psduMap.begin()->second->GetAddr1();
2362 mpdu->GetPacket()->PeekPacketTag(tag);
2363 GetWifiRemoteStationManager()->ReportRxOk(sender, rxSignalInfo, txVector);
2364 GetWifiRemoteStationManager()->ReportRtsOk(m_psduMap.begin()->second->GetHeader(0),
2370 m_channelAccessManager->NotifyCtsTimeoutResetNow();
2371 ProtectionCompleted();
2373 else if (hdr.
IsCts() && m_txTimer.IsRunning() &&
2374 m_txTimer.GetReason() == WifiTxTimer::WAIT_CTS_AFTER_MU_RTS)
2379 NS_LOG_DEBUG(
"Received a CTS frame in response to an MU-RTS");
2382 m_channelAccessManager->NotifyCtsTimeoutResetNow();
2383 ProtectionCompleted();
2385 else if (hdr.
IsAck() && m_txTimer.IsRunning() &&
2386 m_txTimer.GetReason() == WifiTxTimer::WAIT_NORMAL_ACK_AFTER_DL_MU_PPDU)
2390 NS_ASSERT(m_txParams.m_acknowledgment->method ==
2391 WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE);
2393 auto acknowledgment =
2395 NS_ASSERT(acknowledgment->stationsReplyingWithNormalAck.size() == 1);
2397 uint16_t staId = m_apMac->GetAssociationId(
2398 acknowledgment->stationsReplyingWithNormalAck.begin()->first,
2400 auto it = m_psduMap.find(staId);
2403 acknowledgment->stationsReplyingWithNormalAck.begin()->first);
2405 mpdu->GetPacket()->PeekPacketTag(tag);
2406 ReceivedNormalAck(*it->second->begin(),
2407 m_txParams.m_txVector,
2420 m_txTimer.GetReason() == WifiTxTimer::WAIT_BLOCK_ACKS_IN_TB_PPDU)
2423 NS_LOG_DEBUG(
"Received BlockAck in TB PPDU from=" << sender);
2426 mpdu->GetPacket()->PeekPacketTag(tag);
2430 mpdu->GetPacket()->PeekHeader(blockAck);
2432 std::pair<uint16_t, uint16_t> ret =
2433 GetBaManager(tid)->NotifyGotBlockAck(m_linkId,
2435 m_mac->GetMldAddress(sender).value_or(sender),
2437 GetWifiRemoteStationManager()->ReportAmpduTxStatus(sender,
2442 m_txParams.m_txVector);
2445 if (!m_txTimer.GetStasExpectedToRespond().contains(sender))
2447 NS_LOG_WARN(
"Received a BlockAck from an unexpected stations: " << sender);
2451 m_txTimer.GotResponseFrom(sender);
2453 if (m_txTimer.GetStasExpectedToRespond().empty())
2457 m_channelAccessManager->NotifyAckTimeoutResetNow();
2461 m_triggerFrame =
nullptr;
2464 m_edca->ResetCw(m_linkId);
2466 TransmissionSucceeded();
2469 else if (hdr.
IsBlockAck() && m_txTimer.IsRunning() &&
2470 m_txTimer.GetReason() == WifiTxTimer::WAIT_BLOCK_ACK_AFTER_TB_PPDU)
2473 mpdu->GetPacket()->PeekHeader(blockAck);
2476 "A Multi-STA BlockAck is expected after a TB PPDU");
2478 m_txTimer.GotResponseFrom(hdr.
GetAddr2());
2480 NS_ASSERT(m_staMac && m_staMac->IsAssociated());
2483 NS_LOG_DEBUG(
"The sender is not the AP we are associated with");
2487 uint16_t staId = m_staMac->GetAssociationId();
2490 if (indices.empty())
2492 NS_LOG_DEBUG(
"No Per AID TID Info subfield intended for me");
2497 mpdu->GetPacket()->PeekPacketTag(tag);
2500 for (
const auto& index : indices)
2507 NS_ABORT_IF(m_psduMap.empty() || m_psduMap.begin()->first != staId);
2508 GetBaManager(tid)->NotifyGotAck(m_linkId, *m_psduMap.at(staId)->begin());
2517 NS_ABORT_IF(m_psduMap.empty() || m_psduMap.begin()->first != staId);
2518 std::set<uint8_t> tids = m_psduMap.at(staId)->GetTids();
2519 NS_ABORT_MSG_IF(tids.size() > 1,
"Multi-TID A-MPDUs not supported yet");
2520 tid = *tids.begin();
2523 std::pair<uint16_t, uint16_t> ret = GetBaManager(tid)->NotifyGotBlockAck(
2529 GetWifiRemoteStationManager()->ReportAmpduTxStatus(hdr.
GetAddr2(),
2534 m_txParams.m_txVector);
2537 if (m_psduMap.at(staId)->GetHeader(0).IsQosData() &&
2539 || std::any_of(blockAck.
GetBitmap(index).begin(),
2541 [](uint8_t b) { return b != 0; })))
2543 NS_ASSERT(m_psduMap.at(staId)->GetHeader(0).HasData());
2544 NS_ASSERT(m_psduMap.at(staId)->GetHeader(0).GetQosTid() == tid);
2549 m_mac->GetQosTxop(tid)->StartMuEdcaTimerNow(m_linkId);
2555 m_channelAccessManager->NotifyAckTimeoutResetNow();
2557 for (
const auto& [staId, psdu] : m_psduMap)
2559 if (psdu->GetNMpdus() == 1 && psdu->GetHeader(0).IsBlockAckReq())
2566 else if (hdr.
IsBlockAck() && m_txTimer.IsRunning() &&
2567 m_txTimer.GetReason() == WifiTxTimer::WAIT_BLOCK_ACK)
2573 VhtFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
2586 m_triggerFrameInAmpdu =
true;
2591 mpdu->GetPacket()->PeekHeader(trigger);
2602 uint16_t staId = m_staMac->GetAssociationId();
2607 NS_LOG_DEBUG(
"Received MU-RTS Trigger Frame from=" << sender);
2608 GetWifiRemoteStationManager()->ReportRxOk(sender, rxSignalInfo, txVector);
2618 m_sendCtsEvent = Simulator::Schedule(m_phy->GetSifs(),
2619 &HeFrameExchangeManager::SendCtsAfterMuRts,
2628 NS_LOG_DEBUG(
"Received MU-BAR Trigger Frame from=" << sender);
2629 GetWifiRemoteStationManager()->ReportRxOk(sender, rxSignalInfo, txVector);
2637 GetBaManager(tid)->NotifyGotBlockAckRequest(
2638 m_mac->GetMldAddress(sender).value_or(sender),
2642 Simulator::Schedule(m_phy->GetSifs(),
2643 &HeFrameExchangeManager::ReceiveMuBarTrigger,
2652 Simulator::Schedule(m_phy->GetSifs(),
2653 &HeFrameExchangeManager::ReceiveBasicTrigger,
2658 else if (trigger.
IsBsrp())
2660 Simulator::Schedule(m_phy->GetSifs(),
2661 &HeFrameExchangeManager::SendQosNullFramesInTbPpdu,
2670 VhtFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
2678 VhtFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
2686 const std::vector<bool>& perMpduStatus)
2688 std::set<uint8_t> tids = psdu->GetTids();
2690 if (txVector.
IsUlMu() && m_txTimer.IsRunning() &&
2691 m_txTimer.GetReason() == WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF)
2694 NS_ASSERT(m_txParams.m_acknowledgment &&
2695 m_txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA);
2696 auto acknowledgment =
static_cast<WifiUlMuMultiStaBa*
>(m_txParams.m_acknowledgment.get());
2699 if (!m_txTimer.GetStasExpectedToRespond().contains(sender))
2701 NS_LOG_WARN(
"Received a TB PPDU from an unexpected station: " << sender);
2705 NS_LOG_DEBUG(
"Received an A-MPDU in a TB PPDU from " << sender <<
" (" << *psdu <<
")");
2707 if (std::any_of(tids.begin(), tids.end(), [&psdu](uint8_t tid) {
2708 return psdu->GetAckPolicyForTid(tid) == WifiMacHeader::NORMAL_ACK;
2711 if (std::all_of(perMpduStatus.cbegin(), perMpduStatus.cend(), [](
bool v) { return v; }))
2714 acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, 14),
2716 acknowledgment->baType.m_bitmapLen.push_back(0);
2722 for (
const auto& tid : tids)
2724 acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, tid),
2726 acknowledgment->baType.m_bitmapLen.push_back(
2727 m_mac->GetBaTypeAsRecipient(sender, tid).m_bitmapLen.at(0));
2731 m_muSnrTag.Set(staId, rxSignalInfo.
snr);
2735 if (!acknowledgment->stationsReceivingMultiStaBa.empty() && !m_multiStaBaEvent.IsPending())
2737 m_multiStaBaEvent = Simulator::Schedule(m_phy->GetSifs(),
2738 &HeFrameExchangeManager::SendMultiStaBlockAck,
2740 std::cref(m_txParams),
2741 psdu->GetDuration());
2745 m_txTimer.GotResponseFrom(sender);
2747 if (m_txTimer.GetStasExpectedToRespond().empty())
2751 m_channelAccessManager->NotifyAckTimeoutResetNow();
2753 if (!m_multiStaBaEvent.IsPending())
2758 m_edca->ResetCw(m_linkId);
2759 TransmissionSucceeded();
2767 if (txVector.
IsUlMu() && m_txTimer.IsRunning() &&
2768 m_txTimer.GetReason() == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF)
2772 if (!m_txTimer.GetStasExpectedToRespond().contains(sender))
2774 NS_LOG_WARN(
"Received a TB PPDU from an unexpected station: " << sender);
2777 if (std::none_of(psdu->begin(), psdu->end(), [](
Ptr<WifiMpdu> mpdu) {
2778 return mpdu->GetHeader().IsQosData() && !mpdu->GetHeader().HasData();
2781 NS_LOG_WARN(
"No QoS Null frame in the received PSDU");
2785 NS_LOG_DEBUG(
"Received QoS Null frames in a TB PPDU from " << sender);
2786 ReceivedQosNullAfterBsrpTf(sender);
2792 if (m_triggerFrameInAmpdu)
2795 auto psduIt = psdu->begin();
2796 while (psduIt != psdu->end())
2798 if ((*psduIt)->GetHeader().IsTrigger())
2800 ReceiveMpdu(*psduIt, rxSignalInfo, txVector,
false);
2805 m_triggerFrameInAmpdu =
false;
2810 VhtFrameExchangeManager::EndReceiveAmpdu(psdu, rxSignalInfo, txVector, perMpduStatus);
2819 m_txTimer.GetReason() == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF);
2822 m_txTimer.GotResponseFrom(sender);
2824 if (m_txTimer.GetStasExpectedToRespond().empty())
2827 m_channelAccessManager->NotifyAckTimeoutResetNow();
2831 m_edca->ResetCw(m_linkId);
2832 TransmissionSucceeded();
AttributeValue implementation for Boolean.
void Cancel()
This method is syntactic sugar for the ns3::Simulator::Cancel method.
bool IsPending() const
This method is syntactic sugar for !IsExpired().
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
bool m_protectedIfResponded
whether a STA is assumed to be protected if replied to a frame requiring acknowledgment
virtual void Reset()
Reset this frame exchange manager.
Mac48Address m_self
the MAC address of this device
virtual void StartProtection(const WifiTxParameters &txParams)
Start the protection mechanism indicated by the given TX parameters.
WifiTxTimer m_txTimer
the timer set upon frame transmission
std::set< Mac48Address > m_protectedStas
STAs that have replied to an RTS in this TXOP.
virtual Time GetRtsDurationId(const WifiTxVector &rtsTxVector, Time txDuration, Time response) const
Compute how to set the Duration/ID field of an RTS frame to send to protect a frame transmitted with ...
virtual void ProtectionCompleted()
Transmit prepared frame immediately, if no protection was used, or in a SIFS, if protection was compl...
virtual void NotifyChannelReleased(Ptr< Txop > txop)
Notify the given Txop that channel has been released.
virtual void CtsTimeout(Ptr< WifiMpdu > rts, const WifiTxVector &txVector)
Called when the CTS timeout expires.
void DoCtsTimeout(const WifiPsduMap &psduMap)
Take required actions when the CTS timer fired after sending an (MU-)RTS to protect the given PSDU ma...
Time m_txNav
the TXNAV timer
virtual void TransmissionSucceeded()
Take necessary actions upon a transmission success.
Ptr< WifiPhy > m_phy
the PHY layer on this station
std::set< Mac48Address > m_sentFrameTo
the STA(s) to which we sent a frame requesting a response
Ptr< ApWifiMac > m_apMac
AP MAC layer pointer (null if not an AP)
Ptr< ChannelAccessManager > m_channelAccessManager
the channel access manager
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 void RxStartIndication(WifiTxVector txVector, Time psduDuration)
virtual Time GetMuRtsDurationId(uint32_t muRtsSize, const WifiTxVector &muRtsTxVector, Time txDuration, Time response) const
Compute how to set the Duration/ID field of an MU-RTS Trigger Frame to send to protect a frame transm...
void DoDispose() override
Destructor implementation.
void Reset() override
Reset this frame exchange manager.
WifiMode GetCtsModeAfterMuRts() const
virtual void SendMuRts(const WifiTxParameters &txParams)
Send an MU-RTS to begin an MU-RTS/CTS frame exchange protecting an MU PPDU.
WifiTxParameters m_txParams
the TX parameters for the current PPDU
void RxStartIndication(WifiTxVector txVector, Time psduDuration) override
void CalculateAcknowledgmentTime(WifiAcknowledgment *acknowledgment) const override
Calculate the time required to acknowledge a frame according to the given acknowledgment method.
void SetMultiUserScheduler(const Ptr< MultiUserScheduler > muScheduler)
Set the Multi-user Scheduler associated with this Frame Exchange Manager.
WifiTxVector GetCtsTxVectorAfterMuRts(const CtrlTriggerHeader &trigger, uint16_t staId) const
Get the TXVECTOR that the station having the given station ID has to use to send a CTS frame after re...
bool m_continueTxopAfterBsrpTf
whether to continue a TXOP a SIFS after the reception of responses to a BSRP TF when TXOP limit is ze...
Ptr< MultiUserScheduler > m_muScheduler
Multi-user Scheduler (HE APs only)
virtual void CtsAfterMuRtsTimeout(Ptr< WifiMpdu > muRts, const WifiTxVector &txVector)
Called when no CTS frame is received after an MU-RTS.
void ProtectionCompleted() override
Transmit prepared frame immediately, if no protection was used, or in a SIFS, if protection was compl...
static Ptr< WifiPsdu > GetPsduTo(Mac48Address to, const WifiPsduMap &psduMap)
Get the PSDU in the given PSDU map that is addressed to the given MAC address, if any,...
void CtsTimeout(Ptr< WifiMpdu > rts, const WifiTxVector &txVector) override
Called when the CTS timeout expires.
EventId m_intraBssNavResetEvent
the event to reset the intra-BSS NAV after an RTS
bool StartFrameExchange(Ptr< QosTxop > edca, Time availableTime, bool initialFrame) override
Start a frame exchange (including protection frames and acknowledgment frames as needed) that fits wi...
void SendPsduMap()
Send the current PSDU map as a DL MU PPDU.
WifiPsduMap m_psduMap
the A-MPDU being transmitted
static TypeId GetTypeId()
Get the type ID.
Time m_intraBssNavEnd
intra-BSS NAV expiration time
std::set< Mac48Address > GetTfRecipients(const CtrlTriggerHeader &trigger) const
Get the (link) address of the non-AP stations solicited by the given Trigger Frame.
EventId m_multiStaBaEvent
Sending a Multi-STA BlockAck event.
void SendPsduMapWithProtection(WifiPsduMap psduMap, WifiTxParameters &txParams)
Send a map of PSDUs as a DL MU PPDU.
void StartProtection(const WifiTxParameters &txParams) override
Start the protection mechanism indicated by the given TX parameters.
~HeFrameExchangeManager() override
void TransmissionSucceeded() override
Take necessary actions upon a transmission success.
static Time ConvertLSigLengthToHeTbPpduDuration(uint16_t length, const WifiTxVector &txVector, WifiPhyBand band)
Ptr< WifiMpdu > GetBar(AcIndex ac, std::optional< uint8_t > optTid=std::nullopt, std::optional< Mac48Address > optAddress=std::nullopt)
Get the next BlockAckRequest or MU-BAR Trigger Frame to send, if any.
void ForwardMpduDown(Ptr< WifiMpdu > mpdu, WifiTxVector &txVector) override
Forward an MPDU down to the PHY layer.
bool StartFrameExchange(Ptr< QosTxop > edca, Time availableTime, bool initialFrame) override
Start a frame exchange (including protection frames and acknowledgment frames as needed) that fits wi...
static Mac48Address GetBroadcast()
A tag to be attached to a response to a multi-user UL frame, that carries the SNR values with which t...
double Get(uint16_t staId) const
Return the SNR value for the given sender.
TxFormat
Enumeration of the possible transmission formats.
virtual void DoDispose()
Destructor implementation.
Smart pointer class similar to boost::intrusive_ptr.
Ptr< QosTxop > m_edca
the EDCAF that gained channel access
virtual bool SendCfEndIfNeeded()
Send a CF-End frame to indicate the completion of the TXOP, provided that the remaining duration is l...
Time m_singleExchangeProtectionSurplus
additional time to protect beyond end of the immediate frame exchange in case of non-zero TXOP limit ...
bool m_protectSingleExchange
true if the Duration/ID field in frames establishing protection only covers the immediate frame excha...
virtual Time GetRemainingTxop(uint8_t linkId) const
Return the remaining duration in the current TXOP on the given link.
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.
Introspection did not find any typical Config paths.
double Get() const
Return the SNR value.
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.
Time GetTxopLimit() const
Return the TXOP limit.
a unique identifier for an interface.
TypeId SetParent(TypeId tid)
Set the parent TypeId.
VhtFrameExchangeManager handles the frame exchange sequences for VHT stations.
Ptr< WifiPsdu > GetWifiPsdu(Ptr< WifiMpdu > mpdu, const WifiTxVector &txVector) const override
Get a PSDU containing the given MPDU.
static void SetQosAckPolicy(Ptr< WifiMpdu > item, const WifiAcknowledgment *acknowledgment)
Set the QoS Ack policy for the given MPDU, which must be a QoS data frame.
represent a single transmission mode
Time GetSlot() const
Return the slot duration for this PHY.
Time GetSifs() const
Return the Short Interframe Space (SIFS) for this PHY.
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.
static Time CalculatePhyPreambleAndHeaderDuration(const WifiTxVector &txVector)
This class stores the TX parameters (TX vector, protection mechanism, acknowledgment mechanism,...
std::optional< Time > m_txDuration
TX duration of the frame.
std::unique_ptr< WifiProtection > m_protection
protection method
uint32_t GetSize(Mac48Address receiver) const
Get the size in bytes of the (A-)MPDU addressed to the given receiver.
std::unique_ptr< WifiAcknowledgment > m_acknowledgment
acknowledgment method
const PsduInfo * GetPsduInfo(Mac48Address receiver) const
Get a pointer to the information about the PSDU addressed to the given receiver, if present,...
void UndoAddMpdu()
Undo the addition of the last MPDU added by calling AddMpdu().
WifiTxVector m_txVector
TXVECTOR of the frame being prepared.
void AddMpdu(Ptr< const WifiMpdu > mpdu)
Record that an MPDU is being added to the current frame.
void Clear()
Reset the TX parameters.
bool IsRunning() const
Return true if the timer is running.
Reason
The reason why the timer was started.
@ WAIT_QOS_NULL_AFTER_BSRP_TF
const std::set< Mac48Address > & GetStasExpectedToRespond() const
void Set(Reason reason, const Time &delay, const std::set< Mac48Address > &from, MEM mem_ptr, OBJ obj, Args... args)
This method is called when a frame soliciting a response is transmitted.
Reason GetReason() const
Get the reason why the timer was started.
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
void SetTxPowerLevel(uint8_t powerlevel)
Sets the selected transmission power level.
uint8_t GetBssColor() const
Get the BSS color.
void SetGuardInterval(Time guardInterval)
Sets the guard interval duration (in nanoseconds)
void SetTriggerResponding(bool triggerResponding)
Set the Trigger Responding parameter to the given value.
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.
void SetHeMuUserInfo(uint16_t staId, HeMuUserInfo userInfo)
Set the HE MU user-specific transmission information for the given STA-ID.
WifiPreamble GetPreambleType() const
void SetAggregation(bool aggregation)
Sets if PSDU contains A-MPDU.
void SetChannelWidth(MHz_u channelWidth)
Sets the selected channelWidth.
const HeMuUserInfoMap & GetHeMuUserInfoMap() const
Get a const reference to the map HE MU user-specific transmission information indexed by STA-ID.
WifiModulationClass GetModulationClass() const
Get the modulation class specified by this TXVECTOR.
void SetLength(uint16_t length)
Set the LENGTH field of the L-SIG.
MHz_u GetChannelWidth() const
void SetSigBMode(const WifiMode &mode)
Set the MCS used for SIG-B.
void SetBssColor(uint8_t color)
Set the BSS color.
void SetMode(WifiMode mode)
Sets the selected payload transmission mode.
void SetPreambleType(WifiPreamble preamble)
Sets the preamble type.
#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(msg)
Unconditional abnormal program termination with a message.
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
#define NS_ABORT_IF(cond)
Abnormal program termination if a condition is true.
#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_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
#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_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 Seconds(double value)
Construct a Time in the indicated unit.
AcIndex
This enumeration defines the Access Categories as an enumeration with values corresponding to the AC ...
@ WIFI_PHY_BAND_2_4GHZ
The 2.4 GHz band.
@ WIFI_MOD_CLASS_VHT
VHT (Clause 22)
Declaration of ns3::HePhy class and ns3::HeSigAParameters struct.
void(* Time)(Time oldValue, Time newValue)
TracedValue callback signature for Time.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
U * PeekPointer(const Ptr< U > &p)
bool IsTrigger(const WifiPsduMap &psduMap)
std::unordered_map< uint16_t, Ptr< WifiPsdu > > WifiPsduMap
Map of PSDUs indexed by STA-ID.
uint32_t GetBlockAckRequestSize(BlockAckReqType type)
Return the total BlockAckRequest size (including FCS trailer).
uint32_t GetMuBarSize(std::list< BlockAckReqType > types)
Return the total MU-BAR size (including FCS trailer).
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...
uint32_t GetBlockAckSize(BlockAckType type)
Return the total BlockAck size (including FCS trailer).
bool IsDlMu(WifiPreamble preamble)
Return true if a preamble corresponds to a downlink multi-user transmission.
std::unordered_map< uint16_t, Ptr< const WifiPsdu > > WifiConstPsduMap
Map of const PSDUs indexed by STA-ID.
uint32_t GetAckSize()
Return the total Ack size (including FCS trailer).
static constexpr uint16_t SU_STA_ID
STA_ID to identify a single user (SU)
uint32_t GetCtsSize()
Return the total CTS size (including FCS trailer).
std::vector< uint8_t > m_bitmapLen
Length (bytes) of included bitmaps.
RxSignalInfo structure containing info on the received signal.
double snr
SNR in linear scale.
WifiAcknowledgment is an abstract base struct.
const Method method
acknowledgment method
std::optional< Time > acknowledgmentTime
time required by the acknowledgment method
WifiDlMuAggregateTf specifies that a DL MU PPDU made of PSDUs including each a MU-BAR Trigger Frame i...
std::map< Mac48Address, BlockAckInfo > stationsReplyingWithBlockAck
Set of stations replying with a BlockAck frame.
WifiDlMuBarBaSequence specifies that a DL MU PPDU is acknowledged through a sequence of BlockAckReq a...
WifiDlMuTfMuBar specifies that a DL MU PPDU is followed after a SIFS duration by a MU-BAR Trigger Fra...
WifiMuRtsCtsProtection specifies that MU-RTS/CTS protection method is used.
WifiNoAck specifies that no acknowledgment is required.
WifiNoProtection specifies that no protection method is used.
WifiProtection is an abstract base struct.
const Method method
protection method
WifiUlMuMultiStaBa specifies that a Basic Trigger Frame is being sent to solicit TB PPDUs that will b...
BlockAckType baType
BlockAck type.
std::map< std::pair< Mac48Address, uint8_t >, std::size_t > stationsReceivingMultiStaBa
Map (originator, tid) pairs to the their index in baType.