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);
370 protection->muRts.SetCsRequired(
true);
372 payload->AddHeader(protection->muRts);
378 mpdu->GetHeader().SetDuration(
380 protection->muRtsTxVector,
393 protection->muRtsTxVector,
405 protection->muRtsTxVector);
431 auto it = std::find_if(
434 [&to](std::pair<uint16_t,
Ptr<WifiPsdu>> psdu) { return psdu.second->GetAddr1() == to; });
435 if (it != psduMap.end())
470 NS_LOG_DEBUG(address <<
" did not respond, hence it is no longer protected");
480 NS_LOG_DEBUG(
"Schedule another transmission in a SIFS after successful BSRP TF");
483 if (!StartTransmission(m_edca, Seconds(0)))
501HeFrameExchangeManager::SendPsduMap()
513 std::set<Mac48Address> staExpectResponseFrom;
520 if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE)
522 auto acknowledgment =
526 for (
const auto& psdu : m_psduMap)
528 if (acknowledgment->stationsSendBlockAckReqTo.contains(psdu.second->GetAddr1()))
531 std::set<uint8_t> tids = psdu.second->GetTids();
533 "Acknowledgment method incompatible with a Multi-TID A-MPDU");
534 uint8_t tid = *tids.begin();
538 m_mac->GetQosTxop(tid)->PrepareBlockAckRequest(psdu.second->GetAddr1(), tid);
539 m_edca->GetBaManager()->ScheduleBar(reqHdr, hdr);
543 if (!acknowledgment->stationsReplyingWithNormalAck.empty())
546 timerType = WifiTxTimer::WAIT_NORMAL_ACK_AFTER_DL_MU_PPDU;
548 &acknowledgment->stationsReplyingWithNormalAck.begin()->second.ackTxVector;
549 auto from = acknowledgment->stationsReplyingWithNormalAck.begin()->first;
550 psdu = GetPsduTo(from, m_psduMap);
552 mpdu = *psdu->begin();
553 staExpectResponseFrom.insert(from);
555 else if (!acknowledgment->stationsReplyingWithBlockAck.empty())
558 timerType = WifiTxTimer::WAIT_BLOCK_ACK;
560 &acknowledgment->stationsReplyingWithBlockAck.begin()->second.blockAckTxVector;
561 auto from = acknowledgment->stationsReplyingWithBlockAck.begin()->first;
562 psdu = GetPsduTo(from, m_psduMap);
563 staExpectResponseFrom.insert(from);
570 else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_TF_MU_BAR)
572 auto acknowledgment =
static_cast<WifiDlMuTfMuBar*
>(m_txParams.m_acknowledgment.get());
579 std::map<uint16_t, CtrlBAckRequestHeader> recipients;
581 NS_ASSERT(!acknowledgment->stationsReplyingWithBlockAck.empty());
582 auto staIt = acknowledgment->stationsReplyingWithBlockAck.begin();
583 m_trigVector = staIt->second.blockAckTxVector;
584 while (staIt != acknowledgment->stationsReplyingWithBlockAck.end())
587 uint16_t staId = m_apMac->GetAssociationId(staIt->first, m_linkId);
589 m_trigVector.SetHeMuUserInfo(staId,
590 staIt->second.blockAckTxVector.GetHeMuUserInfo(staId));
591 recipients.emplace(staId, staIt->second.barHeader);
597 m_trigVector.SetLength(acknowledgment->ulLength);
599 m_triggerFrame = PrepareMuBar(m_trigVector, recipients);
606 for (
auto& station : acknowledgment->stationsReplyingWithBlockAck)
608 staExpectResponseFrom.insert(station.first);
611 Ptr<WifiPsdu> triggerPsdu = GetWifiPsdu(m_triggerFrame, acknowledgment->muBarTxVector);
612 Time txDuration = WifiPhy::CalculateTxDuration(triggerPsdu->GetSize(),
613 acknowledgment->muBarTxVector,
614 m_phy->GetPhyBand());
616 *acknowledgment->acknowledgmentTime -= (m_phy->GetSifs() + txDuration);
617 m_triggerFrame->GetHeader().SetDuration(GetPsduDurationId(txDuration, m_txParams));
620 &acknowledgment->stationsReplyingWithBlockAck.begin()->second.blockAckTxVector;
621 Time timeout = txDuration + m_phy->GetSifs() + m_phy->GetSlot() +
622 WifiPhy::CalculatePhyPreambleAndHeaderDuration(*responseTxVector);
624 m_txTimer.Set(WifiTxTimer::WAIT_BLOCK_ACKS_IN_TB_PPDU,
626 staExpectResponseFrom,
627 &HeFrameExchangeManager::BlockAcksInTbPpduTimeout,
630 staExpectResponseFrom.size());
631 m_channelAccessManager->NotifyAckTimeoutStartNow(
timeout);
633 ForwardPsduDown(triggerPsdu, acknowledgment->muBarTxVector);
638 hePhy->SetTrigVector(m_trigVector,
timeout);
646 else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_AGGREGATE_TF)
648 auto acknowledgment =
static_cast<WifiDlMuAggregateTf*
>(m_txParams.m_acknowledgment.get());
654 for (
auto& station : acknowledgment->stationsReplyingWithBlockAck)
656 staExpectResponseFrom.insert(station.first);
659 auto psduMapIt = std::find_if(m_psduMap.begin(),
662 return psdu.second->GetAddr1() == station.first;
667 std::vector<Ptr<WifiMpdu>> mpduList(psduMapIt->second->begin(),
668 psduMapIt->second->end());
669 NS_ASSERT(mpduList.size() == psduMapIt->second->GetNMpdus());
672 station.second.blockAckTxVector.SetLength(acknowledgment->ulLength);
673 mpduList.push_back(PrepareMuBar(station.second.blockAckTxVector,
674 {{psduMapIt->first, station.second.barHeader}}));
675 psduMapIt->second = Create<WifiPsdu>(std::move(mpduList));
676 m_trigVector.SetHeMuUserInfo(
678 station.second.blockAckTxVector.GetHeMuUserInfo(psduMapIt->first));
681 timerType = WifiTxTimer::WAIT_BLOCK_ACKS_IN_TB_PPDU;
683 &acknowledgment->stationsReplyingWithBlockAck.begin()->second.blockAckTxVector;
684 m_trigVector.
SetLength(acknowledgment->ulLength);
689 else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA)
693 mpdu = *m_psduMap.begin()->second->begin();
695 auto acknowledgment =
static_cast<WifiUlMuMultiStaBa*
>(m_txParams.m_acknowledgment.get());
698 for (
const auto& station : acknowledgment->stationsReceivingMultiStaBa)
700 staExpectResponseFrom.insert(station.first.first);
706 acknowledgment->baType.m_bitmapLen.clear();
708 timerType = WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF;
709 responseTxVector = &acknowledgment->tbPpduTxVector;
710 m_trigVector = GetTrigVector(m_muScheduler->GetUlMuInfo(m_linkId).trigger);
715 else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::NONE &&
716 !m_txParams.m_txVector.IsUlMu() &&
IsTrigger(m_psduMap))
718 CtrlTriggerHeader& trigger = m_muScheduler->GetUlMuInfo(m_linkId).trigger;
723 for (
const auto& userInfo : trigger)
725 auto staIt = m_apMac->GetStaList(m_linkId).find(userInfo.GetAid12());
726 NS_ASSERT(staIt != m_apMac->GetStaList(m_linkId).end());
727 staExpectResponseFrom.insert(staIt->second);
730 timerType = WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF;
731 txVector = trigger.GetHeTbTxVector(trigger.begin()->GetAid12());
732 responseTxVector = &txVector;
733 m_trigVector = GetTrigVector(m_muScheduler->GetUlMuInfo(m_linkId).trigger);
738 else if (m_txParams.m_txVector.IsUlMu() &&
739 m_txParams.m_acknowledgment->method == WifiAcknowledgment::ACK_AFTER_TB_PPDU)
742 timerType = WifiTxTimer::WAIT_BLOCK_ACK_AFTER_TB_PPDU;
743 NS_ASSERT(m_staMac && m_staMac->IsAssociated());
744 auto recv = m_psduMap.begin()->second->GetAddr1();
745 txVector = GetWifiRemoteStationManager()->GetBlockAckTxVector(recv, m_txParams.m_txVector);
746 responseTxVector = &txVector;
747 staExpectResponseFrom.insert(recv);
752 else if (m_txParams.m_txVector.IsUlMu() &&
753 m_txParams.m_acknowledgment->method == WifiAcknowledgment::NONE)
759 NS_ABORT_MSG(
"Unable to handle the selected acknowledgment method ("
760 << m_txParams.m_acknowledgment.get() <<
")");
765 for (
const auto& psdu : m_psduMap)
767 psduMap.emplace(psdu.first, psdu.second);
771 if (m_txParams.m_txVector.IsUlMu())
773 txDuration = HePhy::ConvertLSigLengthToHeTbPpduDuration(m_txParams.m_txVector.GetLength(),
774 m_txParams.m_txVector,
775 m_phy->GetPhyBand());
780 WifiPhy::CalculateTxDuration(psduMap, m_txParams.m_txVector, m_phy->GetPhyBand());
783 Time durationId = GetPsduDurationId(txDuration, m_txParams);
785 if (m_continueTxopAfterBsrpTf && m_edca && m_edca->GetTxopLimit(m_linkId).IsZero() &&
786 timerType == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF)
790 durationId += m_muScheduler->GetExtraTimeForBsrpTfDurationId(m_linkId);
793 for (
auto& psdu : m_psduMap)
795 psdu.second->SetDuration(durationId);
799 if (timerType == WifiTxTimer::NOT_RUNNING)
804 Simulator::Schedule(txDuration + m_phy->GetSifs(),
805 &HeFrameExchangeManager::SendPsduMap,
808 else if (!m_txParams.m_txVector.IsUlMu())
810 Simulator::Schedule(txDuration, &HeFrameExchangeManager::TransmissionSucceeded,
this);
815 Time timeout = txDuration + m_phy->GetSifs() + m_phy->GetSlot() +
816 WifiPhy::CalculatePhyPreambleAndHeaderDuration(*responseTxVector);
817 m_channelAccessManager->NotifyAckTimeoutStartNow(
timeout);
822 case WifiTxTimer::WAIT_NORMAL_ACK_AFTER_DL_MU_PPDU:
824 m_txTimer.Set(timerType,
826 staExpectResponseFrom,
827 &HeFrameExchangeManager::NormalAckTimeout,
830 m_txParams.m_txVector);
832 case WifiTxTimer::WAIT_BLOCK_ACK:
834 m_txTimer.Set(timerType,
836 staExpectResponseFrom,
837 &HeFrameExchangeManager::BlockAckTimeout,
840 m_txParams.m_txVector);
842 case WifiTxTimer::WAIT_BLOCK_ACKS_IN_TB_PPDU:
843 m_txTimer.Set(timerType,
845 staExpectResponseFrom,
846 &HeFrameExchangeManager::BlockAcksInTbPpduTimeout,
849 staExpectResponseFrom.size());
851 case WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF:
852 case WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF:
853 m_txTimer.Set(timerType,
855 staExpectResponseFrom,
856 &HeFrameExchangeManager::TbPpduTimeout,
859 staExpectResponseFrom.size());
861 case WifiTxTimer::WAIT_BLOCK_ACK_AFTER_TB_PPDU:
862 m_txTimer.Set(timerType,
864 staExpectResponseFrom,
865 &HeFrameExchangeManager::BlockAckAfterTbPpduTimeout,
867 m_psduMap.begin()->second,
868 m_txParams.m_txVector);
877 ForwardPsduMapDown(psduMap, m_txParams.m_txVector);
879 if (timerType == WifiTxTimer::WAIT_BLOCK_ACKS_IN_TB_PPDU ||
880 timerType == WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF ||
881 timerType == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF)
884 auto hePhy = StaticCast<HePhy>(m_phy->GetPhyEntity(responseTxVector->GetModulationClass()));
885 hePhy->SetTrigVector(m_trigVector, m_txTimer.GetDelayLeft());
887 else if (timerType == WifiTxTimer::NOT_RUNNING &&
888 (m_txParams.m_txVector.IsUlMu() ||
889 m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE))
893 Simulator::Schedule(txDuration, &WifiPsduMap::clear, &m_psduMap);
896 if (m_txTimer.IsRunning() && timerType != WifiTxTimer::WAIT_BLOCK_ACK_AFTER_TB_PPDU)
902 for (
const auto& address : staExpectResponseFrom)
904 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(address) ||
905 timerType == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF ||
906 m_protectedStas.contains(address))
908 m_sentFrameTo.insert(address);
922 auto sigBMode = hePhy->GetSigBMode(txVector);
926 for (
const auto& psdu : psduMap)
928 NS_LOG_DEBUG(
"Transmitting: [STAID=" << psdu.first <<
", " << *psdu.second <<
"]");
931 for (
const auto& [staId, psdu] : psduMap)
933 FinalizeMacHeader(psdu);
934 NotifyTxToEdca(psdu);
938 if (psduMap.size() > 1 || psduMap.begin()->second->IsAggregate() ||
939 psduMap.begin()->second->IsSingle())
944 auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, m_phy->GetPhyBand());
948 m_txNav =
Max(m_txNav, Simulator::Now() + txDuration + psduMap.cbegin()->second->GetDuration());
950 m_phy->Send(psduMap, txVector);
954HeFrameExchangeManager::PrepareMuBar(
const WifiTxVector& responseTxVector,
955 std::map<uint16_t, CtrlBAckRequestHeader> recipients)
const
962 SetTargetRssi(muBar);
968 for (
auto& userInfo : muBar)
970 auto recipientIt = recipients.find(userInfo.GetAid12());
971 NS_ASSERT(recipientIt != recipients.end());
974 userInfo.SetMuBarTriggerDepUserInfo(recipientIt->second);
978 bar->AddHeader(muBar);
986 rxAddress = Mac48Address::GetBroadcast();
991 rxAddress = m_apMac->GetStaList(m_linkId).at(recipients.begin()->first);
1003 return Create<WifiMpdu>(bar, hdr);
1012 if (protection->
method == WifiProtection::MU_RTS_CTS)
1019 GetCtsTxVectorAfterMuRts(muRtsCtsProtection->muRts,
1020 muRtsCtsProtection->muRts.begin()->GetAid12());
1023 muRtsCtsProtection->muRts.GetSerializedSize() + WIFI_MAC_FCS_LENGTH;
1024 muRtsCtsProtection->protectionTime =
1025 WifiPhy::CalculateTxDuration(muRtsSize,
1026 muRtsCtsProtection->muRtsTxVector,
1027 m_phy->GetPhyBand()) +
1028 WifiPhy::CalculateTxDuration(
GetCtsSize(), ctsTxVector, m_phy->GetPhyBand()) +
1029 2 * m_phy->GetSifs();
1033 VhtFrameExchangeManager::CalculateProtectionTime(protection);
1046 if (acknowledgment->
method == WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE)
1053 NS_ABORT_IF(dlMuBarBaAcknowledgment->stationsReplyingWithNormalAck.size() +
1054 dlMuBarBaAcknowledgment->stationsReplyingWithBlockAck.size() >
1057 if (!dlMuBarBaAcknowledgment->stationsReplyingWithNormalAck.empty())
1060 dlMuBarBaAcknowledgment->stationsReplyingWithNormalAck.begin()->second;
1063 WifiPhy::CalculateTxDuration(
GetAckSize(), info.ackTxVector, m_phy->GetPhyBand());
1066 if (!dlMuBarBaAcknowledgment->stationsReplyingWithBlockAck.empty())
1069 dlMuBarBaAcknowledgment->stationsReplyingWithBlockAck.begin()->second;
1071 m_phy->GetSifs() + WifiPhy::CalculateTxDuration(
GetBlockAckSize(info.baType),
1072 info.blockAckTxVector,
1073 m_phy->GetPhyBand());
1076 for (
const auto& stations : dlMuBarBaAcknowledgment->stationsSendBlockAckReqTo)
1078 const auto& info = stations.second;
1079 duration += m_phy->GetSifs() +
1081 info.blockAckReqTxVector,
1082 m_phy->GetPhyBand()) +
1085 info.blockAckTxVector,
1086 m_phy->GetPhyBand());
1089 dlMuBarBaAcknowledgment->acknowledgmentTime = duration;
1094 else if (acknowledgment->
method == WifiAcknowledgment::DL_MU_TF_MU_BAR)
1096 auto dlMuTfMuBarAcknowledgment =
static_cast<WifiDlMuTfMuBar*
>(acknowledgment);
1100 for (
const auto& stations : dlMuTfMuBarAcknowledgment->stationsReplyingWithBlockAck)
1103 const auto& info = stations.second;
1104 NS_ASSERT(info.blockAckTxVector.GetHeMuUserInfoMap().size() == 1);
1105 uint16_t staId = info.blockAckTxVector.GetHeMuUserInfoMap().begin()->first;
1107 info.blockAckTxVector,
1108 m_phy->GetPhyBand(),
1111 if (currBlockAckDuration > duration)
1113 duration = currBlockAckDuration;
1119 WifiTxVector& txVector = dlMuTfMuBarAcknowledgment->stationsReplyingWithBlockAck.begin()
1120 ->second.blockAckTxVector;
1121 std::tie(dlMuTfMuBarAcknowledgment->ulLength, duration) =
1122 HePhy::ConvertHeTbPpduDurationToLSigLength(duration, txVector, m_phy->GetPhyBand());
1125 if (dlMuTfMuBarAcknowledgment->muBarTxVector.GetModulationClass() >=
WIFI_MOD_CLASS_VHT)
1128 muBarSize = MpduAggregator::GetSizeIfAggregated(muBarSize, 0);
1130 dlMuTfMuBarAcknowledgment->acknowledgmentTime =
1132 WifiPhy::CalculateTxDuration(muBarSize,
1133 dlMuTfMuBarAcknowledgment->muBarTxVector,
1134 m_phy->GetPhyBand()) +
1135 m_phy->GetSifs() + duration;
1140 else if (acknowledgment->
method == WifiAcknowledgment::DL_MU_AGGREGATE_TF)
1146 for (
const auto& stations : dlMuAggrTfAcknowledgment->stationsReplyingWithBlockAck)
1149 const auto& info = stations.second;
1150 NS_ASSERT(info.blockAckTxVector.GetHeMuUserInfoMap().size() == 1);
1151 uint16_t staId = info.blockAckTxVector.GetHeMuUserInfoMap().begin()->first;
1153 info.blockAckTxVector,
1154 m_phy->GetPhyBand(),
1157 if (currBlockAckDuration > duration)
1159 duration = currBlockAckDuration;
1166 dlMuAggrTfAcknowledgment->stationsReplyingWithBlockAck.begin()->second.blockAckTxVector;
1167 std::tie(dlMuAggrTfAcknowledgment->ulLength, duration) =
1168 HePhy::ConvertHeTbPpduDurationToLSigLength(duration, txVector, m_phy->GetPhyBand());
1169 dlMuAggrTfAcknowledgment->acknowledgmentTime = m_phy->GetSifs() + duration;
1174 else if (acknowledgment->
method == WifiAcknowledgment::UL_MU_MULTI_STA_BA)
1179 ulMuMultiStaBa->multiStaBaTxVector,
1180 m_phy->GetPhyBand());
1181 ulMuMultiStaBa->acknowledgmentTime = m_phy->GetSifs() + duration;
1186 else if (acknowledgment->
method == WifiAcknowledgment::ACK_AFTER_TB_PPDU)
1195 VhtFrameExchangeManager::CalculateAcknowledgmentTime(acknowledgment);
1200HeFrameExchangeManager::GetCtsModeAfterMuRts()
const
1205 : OfdmPhy::GetOfdmRate6Mbps();
1210 uint16_t staId)
const
1215 NS_ASSERT_MSG(userInfoIt != trigger.
end(),
"User Info field for AID=" << staId <<
" not found");
1218 if (uint8_t ru = userInfoIt->GetMuRtsRuAllocation(); ru < 65)
1236 auto txVector = GetWifiRemoteStationManager()->GetCtsTxVector(m_bssid, GetCtsModeAfterMuRts());
1238 txVector.SetChannelWidth(bw);
1244HeFrameExchangeManager::GetTxDuration(
uint32_t ppduPayloadSize,
1250 return VhtFrameExchangeManager::GetTxDuration(ppduPayloadSize, receiver, txParams);
1257 txParams.
m_acknowledgment->method == WifiAcknowledgment::DL_MU_AGGREGATE_TF)
1261 NS_ASSERT_MSG(psduInfo,
"No information for " << receiver <<
" in TX params");
1262 NS_ASSERT_MSG(!psduInfo->seqNumbers.empty(),
"No sequence number for " << receiver);
1263 const auto tid = psduInfo->seqNumbers.cbegin()->first;
1265 ppduPayloadSize = MpduAggregator::GetSizeIfAggregated(
1266 GetMuBarSize({m_mac->GetBarTypeAsOriginator(receiver, tid)}),
1270 uint16_t staId = (txParams.
m_txVector.
IsDlMu() ? m_apMac->GetAssociationId(receiver, m_linkId)
1271 : m_staMac->GetAssociationId());
1272 Time psduDuration = WifiPhy::CalculateTxDuration(ppduPayloadSize,
1274 m_phy->GetPhyBand(),
1281HeFrameExchangeManager::TbPpduTimeout(
WifiPsduMap* psduMap, std::size_t nSolicitedStations)
1284 DoTbPpduTimeout(psduMap, nSolicitedStations,
true);
1289 std::size_t nSolicitedStations,
1290 bool updateFailedCw)
1292 const auto& staMissedTbPpduFrom = m_txTimer.GetStasExpectedToRespond();
1293 NS_LOG_FUNCTION(
this << psduMap << staMissedTbPpduFrom.size() << nSolicitedStations
1300 NS_ASSERT(!staMissedTbPpduFrom.empty());
1303 if (staMissedTbPpduFrom.size() == nSolicitedStations)
1307 psduMap->cbegin()->second->GetPayload(0)->PeekHeader(trigger);
1309 if (m_continueTxopAfterBsrpTf && m_edca->GetTxopLimit(m_linkId).IsZero() &&
1312 SendCfEndIfNeeded();
1315 TransmissionFailed(!updateFailedCw);
1317 else if (!m_multiStaBaEvent.IsPending())
1319 m_edca->ResetCw(m_linkId);
1320 TransmissionSucceeded();
1326 for (
const auto& address : staMissedTbPpduFrom)
1328 NS_LOG_DEBUG(address <<
" did not respond, hence it is no longer protected");
1329 m_protectedStas.erase(address);
1330 m_sentFrameTo.erase(address);
1332 if (m_protectedIfResponded)
1334 m_protectedStas.merge(m_sentFrameTo);
1336 m_sentFrameTo.clear();
1344 std::size_t nSolicitedStations)
1349 NS_ASSERT(m_txParams.m_acknowledgment &&
1350 (m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_AGGREGATE_TF ||
1351 m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_TF_MU_BAR));
1354 const auto& staMissedBlockAckFrom = m_txTimer.GetStasExpectedToRespond();
1355 NS_ASSERT(!staMissedBlockAckFrom.empty());
1357 if (staMissedBlockAckFrom.size() == nSolicitedStations)
1360 GetWifiRemoteStationManager()->ReportDataFailed(*psduMap->begin()->second->begin());
1366 m_triggerFrame =
nullptr;
1369 for (
const auto& sta : staMissedBlockAckFrom)
1371 auto psdu = GetPsduTo(sta, *psduMap);
1373 MissedBlockAck(psdu, m_txParams.m_txVector);
1378 if (staMissedBlockAckFrom.size() == nSolicitedStations)
1381 TransmissionFailed();
1385 m_edca->ResetCw(m_linkId);
1386 TransmissionSucceeded();
1396 GetWifiRemoteStationManager()->ReportDataFailed(*psdu->begin());
1398 MissedBlockAck(psdu, m_txParams.m_txVector);
1413 VhtFrameExchangeManager::NormalAckTimeout(mpdu, txVector);
1418 for (
auto& psdu : m_psduMap)
1422 if (mpdu->IsQueued())
1424 m_mac->GetTxopQueue(mpdu->GetQueueAc())->GetOriginal(mpdu)->GetHeader().SetRetry();
1425 mpdu->ResetInFlight(m_linkId);
1437 VhtFrameExchangeManager::BlockAckTimeout(psdu, txVector);
1442 for (
auto& psdu : m_psduMap)
1446 if (mpdu->IsQueued())
1448 mpdu->GetHeader().SetRetry();
1464 for (
const auto& userInfoField : trigger)
1467 userInfoField.GetAid12(),
1468 {userInfoField.GetRuAllocation(), userInfoField.GetUlMcs(), userInfoField.GetNss()});
1480 uint16_t staId = m_staMac->GetAssociationId();
1487 NS_ASSERT_MSG(heConfiguration,
"This STA has to be an HE station to send an HE TB PPDU");
1490 if (userInfoIt->IsUlTargetRssiMaxTxPower())
1492 NS_LOG_LOGIC(
"AP requested using the max transmit power (" << m_phy->GetTxPowerEnd()
1498 uint8_t powerLevel = GetWifiRemoteStationManager()->GetDefaultTxPowerLevel();
1516 auto optRssi = GetMostRecentRssi(triggerSender);
1522 auto reqTxPower =
dBm_u{
static_cast<double>(userInfoIt->GetUlTargetRssi() + pathLossDb)};
1525 uint8_t numPowerLevels = m_phy->GetNTxPower();
1526 if (numPowerLevels > 1)
1528 dBm_u step = (m_phy->GetTxPowerEnd() - m_phy->GetTxPowerStart()) / (numPowerLevels - 1);
1529 powerLevel =
static_cast<uint8_t
>(
1530 ceil((reqTxPower - m_phy->GetTxPowerStart()) /
1532 if (powerLevel > numPowerLevels)
1534 powerLevel = numPowerLevels;
1537 if (reqTxPower > m_phy->GetPower(powerLevel))
1539 NS_LOG_WARN(
"The requested power level (" << reqTxPower <<
"dBm) cannot be satisfied (max: "
1540 << m_phy->GetTxPowerEnd() <<
"dBm)");
1544 <<
"{pathLoss=" << pathLossDb <<
"dB, reqTxPower=" << reqTxPower <<
"dBm}"
1546 <<
"{powerLevel=" << +powerLevel <<
" -> " << m_phy->GetPower(powerLevel) <<
"dBm}"
1547 <<
" PHY power capa "
1548 <<
"{min=" << m_phy->GetTxPowerStart() <<
"dBm, max=" << m_phy->GetTxPowerEnd()
1549 <<
"dBm, levels:" << +numPowerLevels <<
"}");
1555HeFrameExchangeManager::GetMostRecentRssi(
const Mac48Address& address)
const
1557 return GetWifiRemoteStationManager()->GetMostRecentRssi(address);
1567 m_phy->GetPower(GetWifiRemoteStationManager()->GetDefaultTxPowerLevel())));
1568 for (
auto& userInfo : trigger)
1570 const auto staList = m_apMac->GetStaList(m_linkId);
1571 auto itAidAddr = staList.find(userInfo.GetAid12());
1573 auto optRssi = GetMostRecentRssi(itAidAddr->second);
1575 auto rssi =
static_cast<int8_t>(*optRssi);
1576 rssi = (rssi >= -20)
1578 : ((rssi <= -110) ? -110 : rssi);
1579 userInfo.SetUlTargetRssi(rssi);
1588 auto txVectorCopy = txVector;
1590 if (psdu->GetNMpdus() == 1 && psdu->GetHeader(0).IsTrigger())
1593 psdu->GetPayload(0)->PeekHeader(trigger);
1607 if (m_staMac !=
nullptr && m_staMac->IsAssociated() &&
1618 psdu = Create<const WifiPsdu>(Create<Packet>(), rts);
1622 GetWifiRemoteStationManager()->GetCtsTxVector(m_bssid, GetCtsModeAfterMuRts());
1625 VhtFrameExchangeManager::PostProcessFrame(psdu, txVectorCopy);
1635 if (!UlMuCsMediumIdle(trigger))
1637 NS_LOG_DEBUG(
"UL MU CS indicated medium busy, cannot send CTS");
1641 NS_ASSERT(m_staMac !=
nullptr && m_staMac->IsAssociated());
1642 WifiTxVector ctsTxVector = GetCtsTxVectorAfterMuRts(trigger, m_staMac->GetAssociationId());
1645 DoSendCtsAfterRts(muRtsHdr, ctsTxVector, muRtsSnr);
1655 txParams.
m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA);
1658 NS_ASSERT(!acknowledgment->stationsReceivingMultiStaBa.empty());
1661 blockAck.
SetType(acknowledgment->baType);
1665 for (
const auto& staInfo : acknowledgment->stationsReceivingMultiStaBa)
1667 receiver = staInfo.first.first;
1668 uint8_t tid = staInfo.first.second;
1669 std::size_t index = staInfo.second;
1671 blockAck.
SetAid11(m_apMac->GetAssociationId(receiver, m_linkId), index);
1677 NS_LOG_DEBUG(
"Multi-STA Block Ack: Sending All-ack to=" << receiver);
1682 if (acknowledgment->baType.m_bitmapLen.at(index) == 0)
1685 NS_LOG_DEBUG(
"Multi-STA Block Ack: Sending Ack to=" << receiver);
1693 auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(receiver, tid);
1695 agreement->get().FillBlockAckBitmap(blockAck, index);
1696 NS_LOG_DEBUG(
"Multi-STA Block Ack: Sending Block Ack with seq="
1698 <<
" tid=" << +tid);
1704 hdr.
SetAddr1(acknowledgment->stationsReceivingMultiStaBa.size() == 1
1706 : Mac48Address::GetBroadcast());
1712 packet->AddHeader(blockAck);
1714 GetWifiPsdu(Create<WifiMpdu>(packet, hdr), acknowledgment->multiStaBaTxVector);
1717 acknowledgment->multiStaBaTxVector,
1718 m_phy->GetPhyBand());
1730 if (m_edca->GetTxopLimit(m_linkId).IsZero())
1733 psdu->SetDuration(
Max(durationId - m_phy->GetSifs() - txDuration,
Seconds(0)));
1738 psdu->SetDuration(
Max(m_edca->GetRemainingTxop(m_linkId) - txDuration,
Seconds(0)));
1741 psdu->GetPayload(0)->AddPacketTag(m_muSnrTag);
1743 ForwardPsduDown(psdu, acknowledgment->multiStaBaTxVector);
1747 m_edca->ResetCw(m_linkId);
1749 Simulator::Schedule(txDuration, &HeFrameExchangeManager::TransmissionSucceeded,
this);
1758 NS_ASSERT(m_staMac && m_staMac->IsAssociated());
1760 NS_LOG_DEBUG(
"Received a Trigger Frame (basic variant) soliciting a transmission");
1762 if (!UlMuCsMediumIdle(trigger))
1773 std::vector<uint8_t> tids;
1774 uint16_t staId = m_staMac->GetAssociationId();
1777 for (uint8_t i = 0; i < 4; i++)
1780 tids.push_back(acIt->second.GetHighTid());
1781 tids.push_back(acIt->second.GetLowTid());
1793 Time ppduDuration = HePhy::ConvertLSigLengthToHeTbPpduDuration(trigger.
GetUlLength(),
1795 m_phy->GetPhyBand());
1797 for (
const auto& tid : tids)
1801 if (!m_mac->GetBaAgreementEstablishedAsOriginator(hdr.
GetAddr2(), tid))
1811 if (
auto mpdu = GetBar(edca->GetAccessCategory(), tid, hdr.
GetAddr2());
1812 mpdu && TryAddMpdu(mpdu, txParams, ppduDuration))
1815 psdu = Create<WifiPsdu>(mpdu,
true);
1821 GetWifiRemoteStationManager()->GetMldAddress(hdr.
GetAddr2()).value_or(hdr.
GetAddr2());
1822 if (
auto mpdu = edca->PeekNextMpdu(m_linkId, tid, receiver))
1824 mpdu = CreateAliasIfNeeded(mpdu);
1825 if (
auto item = edca->GetNextMpdu(m_linkId, mpdu, txParams, ppduDuration,
false))
1828 std::vector<Ptr<WifiMpdu>> mpduList =
1829 m_mpduAggregator->GetNextAmpdu(item, txParams, ppduDuration);
1830 psdu = (mpduList.size() > 1 ? Create<WifiPsdu>(std::move(mpduList))
1831 : Create<WifiPsdu>(item,
true));
1839 psdu->SetDuration(hdr.
GetDuration() - m_phy->GetSifs() - ppduDuration);
1840 SendPsduMapWithProtection(
WifiPsduMap{{staId, psdu}}, txParams);
1845 SendQosNullFramesInTbPpdu(trigger, hdr);
1855 NS_ASSERT(m_staMac && m_staMac->IsAssociated());
1859 if (!UlMuCsMediumIdle(trigger))
1865 GetWifiRemoteStationManager()->GetMldAddress(hdr.
GetAddr2()).value_or(hdr.
GetAddr2());
1868 header.
SetAddr2(m_mac->GetAddress());
1883 Time ppduDuration = HePhy::ConvertLSigLengthToHeTbPpduDuration(trigger.
GetUlLength(),
1885 m_phy->GetPhyBand());
1888 std::vector<Ptr<WifiMpdu>> mpduList;
1890 for (uint8_t tid = 0; tid < 8; ++tid)
1892 if (!m_mac->GetBaAgreementEstablishedAsOriginator(hdr.
GetAddr2(), tid))
1894 NS_LOG_DEBUG(
"Skipping tid=" << +tid <<
" because no agreement established");
1904 auto mpdu = Create<WifiMpdu>(Create<Packet>(), header);
1905 mpdu = CreateAliasIfNeeded(mpdu);
1907 UpdateTxDuration(header.
GetAddr1(), txParams);
1918 NS_LOG_DEBUG(
"Aggregating a QoS Null frame with tid=" << +tid);
1920 mpduList.push_back(mpdu);
1923 if (mpduList.empty())
1925 NS_LOG_DEBUG(
"Not enough time to send a QoS Null frame");
1929 Ptr<WifiPsdu> psdu = (mpduList.size() > 1 ? Create<WifiPsdu>(std::move(mpduList))
1930 : Create<WifiPsdu>(mpduList.front(),
true));
1931 uint16_t staId = m_staMac->GetAssociationId();
1932 SendPsduMapWithProtection(
WifiPsduMap{{staId, psdu}}, txParams);
1943 auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(m_bssid, tid);
1947 NS_LOG_DEBUG(
"There's not a valid agreement for this BlockAckReq");
1951 if (!UlMuCsMediumIdle(trigger))
1957 auto txVector = GetHeTbTxVector(trigger, m_bssid);
1958 SendBlockAck(*agreement, durationId, txVector, snr);
1972 const auto ra = psdu->GetAddr1();
1973 const auto ta = psdu->GetAddr2();
1974 const auto bssid = psdu->GetHeader(0).GetAddr3();
1977 if (ra == m_bssid || ta == m_bssid || bssid == m_bssid)
1985 if (psdu->GetHeader(0).IsCtl() && ta == empty && ra == m_txopHolder)
1999 if (bssid != empty && bssid != m_bssid)
2007 if (bssid == empty && ta != empty && ra != empty && ta != m_bssid && ra != m_bssid)
2020 const auto bssColor = m_mac->GetHeConfiguration()->m_bssColor;
2023 return bssColor != 0 && bssColor == txVector.
GetBssColor();
2031 if (!psdu->HasNav())
2036 if (psdu->GetAddr1() == m_self)
2046 if (!IsIntraBssPpdu(psdu, txVector))
2048 NS_LOG_DEBUG(
"PPDU not classified as intra-BSS, update the basic NAV");
2049 VhtFrameExchangeManager::UpdateNav(psdu, txVector);
2053 NS_LOG_DEBUG(
"PPDU classified as intra-BSS, update the intra-BSS NAV");
2054 Time duration = psdu->GetDuration();
2057 if (psdu->GetHeader(0).IsCfEnd())
2063 NS_LOG_DEBUG(
"Received CF-End, resetting the intra-BSS NAV");
2064 IntraBssNavResetTimeout();
2070 auto intraBssNavEnd = Simulator::Now() + duration;
2071 if (intraBssNavEnd > m_intraBssNavEnd)
2073 m_intraBssNavEnd = intraBssNavEnd;
2074 NS_LOG_DEBUG(
"Updated intra-BSS NAV=" << m_intraBssNavEnd);
2085 if (psdu->GetHeader(0).IsRts())
2088 GetWifiRemoteStationManager()->GetCtsTxVector(psdu->GetAddr2(), txVector.
GetMode());
2089 auto navResetDelay =
2090 2 * m_phy->GetSifs() +
2091 WifiPhy::CalculateTxDuration(
GetCtsSize(), ctsTxVector, m_phy->GetPhyBand()) +
2092 WifiPhy::CalculatePhyPreambleAndHeaderDuration(ctsTxVector) + 2 * m_phy->GetSlot();
2093 m_intraBssNavResetEvent =
2094 Simulator::Schedule(navResetDelay,
2095 &HeFrameExchangeManager::IntraBssNavResetTimeout,
2099 NS_LOG_DEBUG(
"Current intra-BSS NAV=" << m_intraBssNavEnd);
2101 m_channelAccessManager->NotifyNavStartNow(duration);
2105HeFrameExchangeManager::ClearTxopHolderIfNeeded()
2108 if (m_intraBssNavEnd <= Simulator::Now())
2110 m_txopHolder.reset();
2115HeFrameExchangeManager::NavResetTimeout()
2118 m_navEnd = Simulator::Now();
2121 Time intraBssNav = Simulator::GetDelayLeft(m_intraBssNavResetEvent);
2122 m_channelAccessManager->NotifyNavResetNow(intraBssNav);
2126HeFrameExchangeManager::IntraBssNavResetTimeout()
2129 m_intraBssNavEnd = Simulator::Now();
2130 ClearTxopHolderIfNeeded();
2132 Time basicNav = Simulator::GetDelayLeft(m_navResetEvent);
2133 m_channelAccessManager->NotifyNavResetNow(basicNav);
2136std::optional<Mac48Address>
2147 return VhtFrameExchangeManager::FindTxopHolder(hdr, txVector);
2149 return std::nullopt;
2153HeFrameExchangeManager::VirtualCsMediumIdle()
const
2158 return m_navEnd <= Simulator::Now() && m_intraBssNavEnd <= Simulator::Now();
2174 const Time now = Simulator::Now();
2181 NS_ASSERT_MSG(m_staMac,
"UL MU CS is only performed by non-AP STAs");
2184 "No User Info field for STA (" << m_self
2185 <<
") AID=" << m_staMac->GetAssociationId());
2187 std::set<uint8_t> indices;
2191 auto ctsTxVector = GetCtsTxVectorAfterMuRts(trigger, m_staMac->GetAssociationId());
2192 auto bw = ctsTxVector.GetChannelWidth();
2193 indices = m_phy->GetOperatingChannel().GetAll20MHzChannelIndicesInPrimary(bw);
2198 m_phy->GetOperatingChannel().Get20MHzIndicesCoveringRu(userInfoIt->GetRuAllocation(),
2201 return !m_channelAccessManager->GetPer20MHzBusy(indices);
2210 NS_LOG_FUNCTION(
this << *mpdu << rxSignalInfo << txVector << inAmpdu);
2213 NS_ASSERT(mpdu->GetHeader().GetAddr1().IsGroup() || mpdu->GetHeader().GetAddr1() == m_self);
2217 if (txVector.
IsUlMu() && m_txTimer.IsRunning() &&
2218 m_txTimer.GetReason() == WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF)
2221 NS_ASSERT(m_txParams.m_acknowledgment &&
2222 m_txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA);
2223 auto acknowledgment =
static_cast<WifiUlMuMultiStaBa*
>(m_txParams.m_acknowledgment.get());
2226 if (!m_txTimer.GetStasExpectedToRespond().contains(sender))
2228 NS_LOG_WARN(
"Received a TB PPDU from an unexpected station: " << sender);
2234 NS_LOG_DEBUG(
"Received a BlockAckReq in a TB PPDU from " << sender);
2237 mpdu->GetPacket()->PeekHeader(blockAckReq);
2240 GetBaManager(tid)->NotifyGotBlockAckRequest(
2241 m_mac->GetMldAddress(sender).value_or(sender),
2246 acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, tid), index);
2247 acknowledgment->baType.m_bitmapLen.push_back(
2248 m_mac->GetBaTypeAsRecipient(sender, tid).m_bitmapLen.at(0));
2250 m_muSnrTag.Set(staId, rxSignalInfo.
snr);
2254 NS_LOG_DEBUG(
"Received an S-MPDU in a TB PPDU from " << sender <<
" (" << *mpdu <<
")");
2257 GetBaManager(tid)->NotifyGotMpdu(mpdu);
2260 acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, tid), index);
2261 acknowledgment->baType.m_bitmapLen.push_back(0);
2263 m_muSnrTag.Set(staId, rxSignalInfo.
snr);
2272 VhtFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
2277 if (!acknowledgment->stationsReceivingMultiStaBa.empty() && !m_multiStaBaEvent.IsPending())
2279 m_multiStaBaEvent = Simulator::Schedule(m_phy->GetSifs(),
2280 &HeFrameExchangeManager::SendMultiStaBlockAck,
2282 std::cref(m_txParams),
2283 mpdu->GetHeader().GetDuration());
2287 m_txTimer.GotResponseFrom(sender);
2289 if (m_txTimer.GetStasExpectedToRespond().empty())
2293 m_channelAccessManager->NotifyAckTimeoutResetNow();
2295 if (!m_multiStaBaEvent.IsPending())
2300 m_edca->ResetCw(m_linkId);
2301 TransmissionSucceeded();
2309 if (txVector.
IsUlMu() && m_txTimer.IsRunning() &&
2310 m_txTimer.GetReason() == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF &&
2313 const auto& sender = hdr.
GetAddr2();
2315 if (!m_txTimer.GetStasExpectedToRespond().contains(sender))
2317 NS_LOG_WARN(
"Received a TB PPDU from an unexpected station: " << sender);
2322 NS_LOG_WARN(
"No QoS Null frame in the received MPDU");
2326 NS_LOG_DEBUG(
"Received a QoS Null frame in a TB PPDU from " << sender);
2327 ReceivedQosNullAfterBsrpTf(sender);
2335 if (hdr.
IsCts() && m_txTimer.IsRunning() &&
2336 m_txTimer.GetReason() == WifiTxTimer::WAIT_CTS && m_psduMap.size() == 1)
2341 Mac48Address sender = m_psduMap.begin()->second->GetAddr1();
2345 mpdu->GetPacket()->PeekPacketTag(tag);
2346 GetWifiRemoteStationManager()->ReportRxOk(sender, rxSignalInfo, txVector);
2347 GetWifiRemoteStationManager()->ReportRtsOk(m_psduMap.begin()->second->GetHeader(0),
2353 m_channelAccessManager->NotifyCtsTimeoutResetNow();
2354 ProtectionCompleted();
2356 else if (hdr.
IsCts() && m_txTimer.IsRunning() &&
2357 m_txTimer.GetReason() == WifiTxTimer::WAIT_CTS_AFTER_MU_RTS)
2362 NS_LOG_DEBUG(
"Received a CTS frame in response to an MU-RTS");
2365 m_channelAccessManager->NotifyCtsTimeoutResetNow();
2366 ProtectionCompleted();
2368 else if (hdr.
IsAck() && m_txTimer.IsRunning() &&
2369 m_txTimer.GetReason() == WifiTxTimer::WAIT_NORMAL_ACK_AFTER_DL_MU_PPDU)
2373 NS_ASSERT(m_txParams.m_acknowledgment->method ==
2374 WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE);
2376 auto acknowledgment =
2378 NS_ASSERT(acknowledgment->stationsReplyingWithNormalAck.size() == 1);
2380 uint16_t staId = m_apMac->GetAssociationId(
2381 acknowledgment->stationsReplyingWithNormalAck.begin()->first,
2383 auto it = m_psduMap.find(staId);
2386 acknowledgment->stationsReplyingWithNormalAck.begin()->first);
2388 mpdu->GetPacket()->PeekPacketTag(tag);
2389 ReceivedNormalAck(*it->second->begin(),
2390 m_txParams.m_txVector,
2403 m_txTimer.GetReason() == WifiTxTimer::WAIT_BLOCK_ACKS_IN_TB_PPDU)
2406 NS_LOG_DEBUG(
"Received BlockAck in TB PPDU from=" << sender);
2409 mpdu->GetPacket()->PeekPacketTag(tag);
2413 mpdu->GetPacket()->PeekHeader(blockAck);
2415 std::pair<uint16_t, uint16_t> ret =
2416 GetBaManager(tid)->NotifyGotBlockAck(m_linkId,
2418 m_mac->GetMldAddress(sender).value_or(sender),
2420 GetWifiRemoteStationManager()->ReportAmpduTxStatus(sender,
2425 m_txParams.m_txVector);
2428 if (!m_txTimer.GetStasExpectedToRespond().contains(sender))
2430 NS_LOG_WARN(
"Received a BlockAck from an unexpected stations: " << sender);
2434 m_txTimer.GotResponseFrom(sender);
2436 if (m_txTimer.GetStasExpectedToRespond().empty())
2440 m_channelAccessManager->NotifyAckTimeoutResetNow();
2444 m_triggerFrame =
nullptr;
2447 m_edca->ResetCw(m_linkId);
2449 TransmissionSucceeded();
2452 else if (hdr.
IsBlockAck() && m_txTimer.IsRunning() &&
2453 m_txTimer.GetReason() == WifiTxTimer::WAIT_BLOCK_ACK_AFTER_TB_PPDU)
2456 mpdu->GetPacket()->PeekHeader(blockAck);
2459 "A Multi-STA BlockAck is expected after a TB PPDU");
2461 m_txTimer.GotResponseFrom(hdr.
GetAddr2());
2463 NS_ASSERT(m_staMac && m_staMac->IsAssociated());
2466 NS_LOG_DEBUG(
"The sender is not the AP we are associated with");
2470 uint16_t staId = m_staMac->GetAssociationId();
2473 if (indices.empty())
2475 NS_LOG_DEBUG(
"No Per AID TID Info subfield intended for me");
2480 mpdu->GetPacket()->PeekPacketTag(tag);
2483 for (
const auto& index : indices)
2490 NS_ABORT_IF(m_psduMap.empty() || m_psduMap.begin()->first != staId);
2491 GetBaManager(tid)->NotifyGotAck(m_linkId, *m_psduMap.at(staId)->begin());
2500 NS_ABORT_IF(m_psduMap.empty() || m_psduMap.begin()->first != staId);
2501 std::set<uint8_t> tids = m_psduMap.at(staId)->GetTids();
2502 NS_ABORT_MSG_IF(tids.size() > 1,
"Multi-TID A-MPDUs not supported yet");
2503 tid = *tids.begin();
2506 std::pair<uint16_t, uint16_t> ret = GetBaManager(tid)->NotifyGotBlockAck(
2512 GetWifiRemoteStationManager()->ReportAmpduTxStatus(hdr.
GetAddr2(),
2517 m_txParams.m_txVector);
2520 if (m_psduMap.at(staId)->GetHeader(0).IsQosData() &&
2522 || std::any_of(blockAck.
GetBitmap(index).begin(),
2524 [](uint8_t b) { return b != 0; })))
2526 NS_ASSERT(m_psduMap.at(staId)->GetHeader(0).HasData());
2527 NS_ASSERT(m_psduMap.at(staId)->GetHeader(0).GetQosTid() == tid);
2532 m_mac->GetQosTxop(tid)->StartMuEdcaTimerNow(m_linkId);
2538 m_channelAccessManager->NotifyAckTimeoutResetNow();
2540 for (
const auto& [staId, psdu] : m_psduMap)
2542 if (psdu->GetNMpdus() == 1 && psdu->GetHeader(0).IsBlockAckReq())
2549 else if (hdr.
IsBlockAck() && m_txTimer.IsRunning() &&
2550 m_txTimer.GetReason() == WifiTxTimer::WAIT_BLOCK_ACK)
2556 VhtFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
2569 m_triggerFrameInAmpdu =
true;
2574 mpdu->GetPacket()->PeekHeader(trigger);
2585 uint16_t staId = m_staMac->GetAssociationId();
2590 NS_LOG_DEBUG(
"Received MU-RTS Trigger Frame from=" << sender);
2591 GetWifiRemoteStationManager()->ReportRxOk(sender, rxSignalInfo, txVector);
2601 m_sendCtsEvent = Simulator::Schedule(m_phy->GetSifs(),
2602 &HeFrameExchangeManager::SendCtsAfterMuRts,
2611 NS_LOG_DEBUG(
"Received MU-BAR Trigger Frame from=" << sender);
2612 GetWifiRemoteStationManager()->ReportRxOk(sender, rxSignalInfo, txVector);
2620 GetBaManager(tid)->NotifyGotBlockAckRequest(
2621 m_mac->GetMldAddress(sender).value_or(sender),
2625 Simulator::Schedule(m_phy->GetSifs(),
2626 &HeFrameExchangeManager::ReceiveMuBarTrigger,
2635 Simulator::Schedule(m_phy->GetSifs(),
2636 &HeFrameExchangeManager::ReceiveBasicTrigger,
2641 else if (trigger.
IsBsrp())
2643 Simulator::Schedule(m_phy->GetSifs(),
2644 &HeFrameExchangeManager::SendQosNullFramesInTbPpdu,
2653 VhtFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
2661 VhtFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
2669 const std::vector<bool>& perMpduStatus)
2671 std::set<uint8_t> tids = psdu->GetTids();
2673 if (txVector.
IsUlMu() && m_txTimer.IsRunning() &&
2674 m_txTimer.GetReason() == WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF)
2677 NS_ASSERT(m_txParams.m_acknowledgment &&
2678 m_txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA);
2679 auto acknowledgment =
static_cast<WifiUlMuMultiStaBa*
>(m_txParams.m_acknowledgment.get());
2682 if (!m_txTimer.GetStasExpectedToRespond().contains(sender))
2684 NS_LOG_WARN(
"Received a TB PPDU from an unexpected station: " << sender);
2688 NS_LOG_DEBUG(
"Received an A-MPDU in a TB PPDU from " << sender <<
" (" << *psdu <<
")");
2690 if (std::any_of(tids.begin(), tids.end(), [&psdu](uint8_t tid) {
2691 return psdu->GetAckPolicyForTid(tid) == WifiMacHeader::NORMAL_ACK;
2694 if (std::all_of(perMpduStatus.cbegin(), perMpduStatus.cend(), [](
bool v) { return v; }))
2697 acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, 14),
2699 acknowledgment->baType.m_bitmapLen.push_back(0);
2705 for (
const auto& tid : tids)
2707 acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, tid),
2709 acknowledgment->baType.m_bitmapLen.push_back(
2710 m_mac->GetBaTypeAsRecipient(sender, tid).m_bitmapLen.at(0));
2714 m_muSnrTag.Set(staId, rxSignalInfo.
snr);
2718 if (!acknowledgment->stationsReceivingMultiStaBa.empty() && !m_multiStaBaEvent.IsPending())
2720 m_multiStaBaEvent = Simulator::Schedule(m_phy->GetSifs(),
2721 &HeFrameExchangeManager::SendMultiStaBlockAck,
2723 std::cref(m_txParams),
2724 psdu->GetDuration());
2728 m_txTimer.GotResponseFrom(sender);
2730 if (m_txTimer.GetStasExpectedToRespond().empty())
2734 m_channelAccessManager->NotifyAckTimeoutResetNow();
2736 if (!m_multiStaBaEvent.IsPending())
2741 m_edca->ResetCw(m_linkId);
2742 TransmissionSucceeded();
2750 if (txVector.
IsUlMu() && m_txTimer.IsRunning() &&
2751 m_txTimer.GetReason() == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF)
2755 if (!m_txTimer.GetStasExpectedToRespond().contains(sender))
2757 NS_LOG_WARN(
"Received a TB PPDU from an unexpected station: " << sender);
2760 if (std::none_of(psdu->begin(), psdu->end(), [](
Ptr<WifiMpdu> mpdu) {
2761 return mpdu->GetHeader().IsQosData() && !mpdu->GetHeader().HasData();
2764 NS_LOG_WARN(
"No QoS Null frame in the received PSDU");
2768 NS_LOG_DEBUG(
"Received QoS Null frames in a TB PPDU from " << sender);
2769 ReceivedQosNullAfterBsrpTf(sender);
2775 if (m_triggerFrameInAmpdu)
2778 auto psduIt = psdu->begin();
2779 while (psduIt != psdu->end())
2781 if ((*psduIt)->GetHeader().IsTrigger())
2783 ReceiveMpdu(*psduIt, rxSignalInfo, txVector,
false);
2788 m_triggerFrameInAmpdu =
false;
2793 VhtFrameExchangeManager::EndReceiveAmpdu(psdu, rxSignalInfo, txVector, perMpduStatus);
2802 m_txTimer.GetReason() == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF);
2805 m_txTimer.GotResponseFrom(sender);
2807 if (m_txTimer.GetStasExpectedToRespond().empty())
2810 m_channelAccessManager->NotifyAckTimeoutResetNow();
2814 m_edca->ResetCw(m_linkId);
2815 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...
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.