A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
he-frame-exchange-manager.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2020 Universita' degli Studi di Napoli Federico II
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Author: Stefano Avallone <stavallo@unina.it>
7 */
8
10
11#include "he-configuration.h"
12#include "he-phy.h"
14
15#include "ns3/abort.h"
16#include "ns3/ap-wifi-mac.h"
17#include "ns3/erp-ofdm-phy.h"
18#include "ns3/log.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"
24
25#include <algorithm>
26#include <functional>
27
28#undef NS_LOG_APPEND_CONTEXT
29#define NS_LOG_APPEND_CONTEXT WIFI_FEM_NS_LOG_APPEND_CONTEXT
30
31namespace ns3
32{
33
34NS_LOG_COMPONENT_DEFINE("HeFrameExchangeManager");
35
36NS_OBJECT_ENSURE_REGISTERED(HeFrameExchangeManager);
37
38bool
39IsTrigger(const WifiPsduMap& psduMap)
40{
41 return psduMap.size() == 1 && psduMap.cbegin()->first == SU_STA_ID &&
42 psduMap.cbegin()->second->GetNMpdus() == 1 &&
43 psduMap.cbegin()->second->GetHeader(0).IsTrigger();
44}
45
46bool
48{
49 return psduMap.size() == 1 && psduMap.cbegin()->first == SU_STA_ID &&
50 psduMap.cbegin()->second->GetNMpdus() == 1 &&
51 psduMap.cbegin()->second->GetHeader(0).IsTrigger();
52}
53
54TypeId
56{
57 static TypeId tid =
58 TypeId("ns3::HeFrameExchangeManager")
60 .AddConstructor<HeFrameExchangeManager>()
61 .SetGroupName("Wifi")
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.",
65 BooleanValue(false),
68 return tid;
69}
70
72 : m_intraBssNavEnd(0),
73 m_triggerFrameInAmpdu(false)
74{
75 NS_LOG_FUNCTION(this);
76}
77
82
83void
94
95void
97{
98 NS_LOG_FUNCTION(this << txVector << psduDuration.As(Time::MS));
99 VhtFrameExchangeManager::RxStartIndication(txVector, psduDuration);
100 // Cancel intra-BSS NAV reset timer when receiving a frame from the PHY
102}
103
104void
115
116void
118{
120 NS_ABORT_MSG_IF(!m_apMac, "A Multi-User Scheduler can only be aggregated to an AP");
121 NS_ABORT_MSG_IF(!m_apMac->GetHeConfiguration(),
122 "A Multi-User Scheduler can only be aggregated to an HE AP");
123 m_muScheduler = muScheduler;
124}
125
126bool
127HeFrameExchangeManager::StartFrameExchange(Ptr<QosTxop> edca, Time availableTime, bool initialFrame)
128{
129 NS_LOG_FUNCTION(this << edca << availableTime << initialFrame);
130
133
134 /*
135 * We consult the Multi-user Scheduler (if available) to know the type of transmission to make
136 * if:
137 * - there is no pending BlockAckReq to transmit
138 * - either the AC queue is empty (the scheduler might select an UL MU transmission)
139 * or the next frame in the AC queue is a non-broadcast QoS data frame addressed to
140 * a receiver with which a BA agreement has been already established
141 */
142 if (m_muScheduler && !GetBar(edca->GetAccessCategory()) &&
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()))))
147 {
148 txFormat = m_muScheduler->NotifyAccessGranted(edca,
149 availableTime,
150 initialFrame,
152 m_linkId);
153 }
154
155 if (txFormat == MultiUserScheduler::SU_TX)
156 {
157 return VhtFrameExchangeManager::StartFrameExchange(edca, availableTime, initialFrame);
158 }
159
160 if (txFormat == MultiUserScheduler::DL_MU_TX)
161 {
162 if (m_muScheduler->GetDlMuInfo(m_linkId).psduMap.empty())
163 {
165 "The Multi-user Scheduler returned DL_MU_TX with empty psduMap, do not transmit");
166 return false;
167 }
168
170 m_muScheduler->GetDlMuInfo(m_linkId).txParams);
171 return true;
172 }
173
174 if (txFormat == MultiUserScheduler::UL_MU_TX)
175 {
176 auto packet = Create<Packet>();
177 packet->AddHeader(m_muScheduler->GetUlMuInfo(m_linkId).trigger);
178 auto trigger = Create<WifiMpdu>(packet, m_muScheduler->GetUlMuInfo(m_linkId).macHdr);
181 {SU_STA_ID,
182 GetWifiPsdu(trigger, m_muScheduler->GetUlMuInfo(m_linkId).txParams.m_txVector)}},
183 m_muScheduler->GetUlMuInfo(m_linkId).txParams);
184 return true;
185 }
186
187 return false;
188}
189
190void
192{
193 NS_LOG_FUNCTION(this << &txParams);
194
195 m_psduMap = std::move(psduMap);
196 m_txParams = std::move(txParams);
197
198 // Make sure that the acknowledgment time has been computed, so that SendMuRts()
199 // can reuse this value.
201
202 if (!m_txParams.m_acknowledgment->acknowledgmentTime.has_value())
203 {
205 }
206
207 // in case we are sending a Trigger Frame, update the acknowledgment time so that
208 // the Duration/ID of the MU-RTS is correctly computed
210 {
212 const auto& trigger = m_muScheduler->GetUlMuInfo(m_linkId).trigger;
213 NS_ASSERT_MSG(!trigger.IsBasic() || m_txParams.m_acknowledgment->method ==
215 "Acknowledgment (" << m_txParams.m_acknowledgment.get()
216 << ") incompatible with Basic Trigger Frame");
217 NS_ASSERT_MSG(!trigger.IsBsrp() ||
219 "Acknowledgment (" << m_txParams.m_acknowledgment.get()
220 << ") incompatible with BSRP Trigger Frame");
221 // Add a SIFS and the TB PPDU duration to the acknowledgment time of the Trigger Frame
222 auto txVector = trigger.GetHeTbTxVector(trigger.begin()->GetAid12());
223 *m_txParams.m_acknowledgment->acknowledgmentTime +=
225 txVector,
226 m_phy->GetPhyBand());
227 }
228
229 // Set QoS Ack policy
230 for (auto& psdu : m_psduMap)
231 {
233 }
234
235 for (const auto& psdu : m_psduMap)
236 {
237 for (const auto& mpdu : *PeekPointer(psdu.second))
238 {
239 if (mpdu->IsQueued())
240 {
241 mpdu->SetInFlight(m_linkId);
242 }
243 }
244 }
245
247}
248
249void
251{
252 NS_LOG_FUNCTION(this << &txParams);
253
254 NS_ABORT_MSG_IF(m_psduMap.size() > 1 &&
255 txParams.m_protection->method == WifiProtection::RTS_CTS,
256 "Cannot use RTS/CTS with MU PPDUs");
257 if (txParams.m_protection->method == WifiProtection::MU_RTS_CTS)
258 {
259 auto protection = static_cast<WifiMuRtsCtsProtection*>(txParams.m_protection.get());
260
261 NS_ASSERT(protection->muRts.IsMuRts());
262 NS_ASSERT(m_sentRtsTo.empty());
263 m_sentRtsTo = GetTfRecipients(protection->muRts);
264
265 SendMuRts(txParams);
266 }
267 else
268 {
270 }
271}
272
273std::set<Mac48Address>
275{
276 std::set<Mac48Address> recipients;
277 NS_ASSERT_MSG(m_apMac, "APs only can send Trigger Frames");
278 const auto& aidAddrMap = m_apMac->GetStaList(m_linkId);
279
280 for (const auto& userInfo : trigger)
281 {
282 const auto addressIt = aidAddrMap.find(userInfo.GetAid12());
283 NS_ASSERT_MSG(addressIt != aidAddrMap.end(), "AID not found");
284 recipients.insert(addressIt->second);
285 }
286
287 return recipients;
288}
289
290void
292{
293 NS_LOG_FUNCTION(this);
294 if (!m_psduMap.empty())
295 {
297 m_sentRtsTo.clear();
298 if (m_muScheduler)
299 {
300 m_muScheduler->NotifyProtectionCompleted(m_linkId, m_psduMap, m_txParams);
301
302 if (m_psduMap.empty())
303 {
304 NS_LOG_INFO("Multi-user scheduler aborted the transmission");
307 {
309 return;
310 }
312 m_edca = nullptr;
313 return;
314 }
315 }
317 {
318 SendPsduMap();
319 }
320 else
321 {
323 }
324 return;
325 }
327}
328
329Time
331 const WifiTxVector& muRtsTxVector,
332 Time txDuration,
333 Time response) const
334{
335 NS_LOG_FUNCTION(this << muRtsSize << muRtsTxVector << txDuration << response);
336
338 {
339 WifiTxVector txVector;
340 txVector.SetMode(GetCtsModeAfterMuRts());
341 return VhtFrameExchangeManager::GetRtsDurationId(txVector, txDuration, response);
342 }
343
344 // under multiple protection settings, if the TXOP limit is not null, Duration/ID
345 // is set to cover the remaining TXOP time (Sec. 9.2.5.2 of 802.11-2016).
346 // The TXOP holder may exceed the TXOP limit in some situations (Sec. 10.22.2.8
347 // of 802.11-2016)
348 return std::max(m_edca->GetRemainingTxop(m_linkId) -
349 WifiPhy::CalculateTxDuration(muRtsSize, muRtsTxVector, m_phy->GetPhyBand()),
350 Seconds(0));
351}
352
353void
355{
356 NS_LOG_FUNCTION(this << &txParams);
357 WifiMacHeader hdr;
360 hdr.SetAddr2(m_self);
361 hdr.SetDsNotTo();
362 hdr.SetDsNotFrom();
363 hdr.SetNoRetry();
364 hdr.SetNoMoreFragments();
365
366 NS_ASSERT(txParams.m_protection && txParams.m_protection->method == WifiProtection::MU_RTS_CTS);
367 auto protection = static_cast<WifiMuRtsCtsProtection*>(txParams.m_protection.get());
368
369 NS_ASSERT(protection->muRts.IsMuRts());
370 protection->muRts.SetCsRequired(true);
371 Ptr<Packet> payload = Create<Packet>();
372 payload->AddHeader(protection->muRts);
373
374 auto mpdu = Create<WifiMpdu>(payload, hdr);
375
376 NS_ASSERT(txParams.m_txDuration.has_value());
377 NS_ASSERT(txParams.m_acknowledgment->acknowledgmentTime.has_value());
378 mpdu->GetHeader().SetDuration(
379 GetMuRtsDurationId(mpdu->GetSize(),
380 protection->muRtsTxVector,
381 *txParams.m_txDuration,
382 *txParams.m_acknowledgment->acknowledgmentTime));
383
384 // Get the TXVECTOR used by one station to send the CTS response. This is used
385 // to compute the preamble duration, so it does not matter which station we choose
386 WifiTxVector ctsTxVector =
387 GetCtsTxVectorAfterMuRts(protection->muRts, protection->muRts.begin()->GetAid12());
388
389 // After transmitting an MU-RTS frame, the STA shall wait for a CTSTimeout interval of
390 // aSIFSTime + aSlotTime + aRxPHYStartDelay (Sec. 27.2.5.2 of 802.11ax D3.0).
391 // aRxPHYStartDelay equals the time to transmit the PHY header.
392 Time timeout = WifiPhy::CalculateTxDuration(mpdu->GetSize(),
393 protection->muRtsTxVector,
394 m_phy->GetPhyBand()) +
395 m_phy->GetSifs() + m_phy->GetSlot() +
397
400 timeout,
403 this,
404 mpdu,
405 protection->muRtsTxVector);
406 m_channelAccessManager->NotifyCtsTimeoutStartNow(timeout);
407
408 ForwardMpduDown(mpdu, protection->muRtsTxVector);
409}
410
411void
413{
414 NS_LOG_FUNCTION(this << *muRts << txVector);
415
416 if (m_psduMap.empty())
417 {
418 // A CTS Timeout occurred when protecting a single PSDU that is not included
419 // in a DL MU PPDU is handled by the parent classes
421 return;
422 }
423
425 m_psduMap.clear();
426}
427
428void
430{
431 NS_LOG_FUNCTION(this);
432
433 // GetUpdateCwOnCtsTimeout() needs to be called before resetting m_sentRtsTo
434 const auto updateCw = GetUpdateCwOnCtsTimeout();
435
436 m_sentRtsTo.clear();
437 for (const auto& psdu : psduMap)
438 {
439 for (const auto& mpdu : *PeekPointer(psdu.second))
440 {
441 if (mpdu->IsQueued())
442 {
443 mpdu->ResetInFlight(m_linkId);
444 }
445 }
446 }
447
448 if (const auto& hdr = psduMap.cbegin()->second->GetHeader(0); !hdr.GetAddr1().IsGroup())
449 {
450 GetWifiRemoteStationManager()->ReportRtsFailed(hdr);
451 }
452
453 for (const auto& [staId, psdu] : psduMap)
454 {
455 if (psdu->GetAddr1().IsGroup())
456 {
457 continue;
458 }
459 if (auto droppedMpdu = DropMpduIfRetryLimitReached(psdu))
460 {
461 GetWifiRemoteStationManager()->ReportFinalRtsFailed(droppedMpdu->GetHeader());
462 }
463 // Make the sequence numbers of the MPDUs available again if the MPDUs have never
464 // been transmitted, both in case the MPDUs have been discarded and in case the
465 // MPDUs have to be transmitted (because a new sequence number is assigned to
466 // MPDUs that have never been transmitted and are selected for transmission)
468 }
469
470 TransmissionFailed(!updateCw);
471}
472
475{
476 auto it = std::find_if(
477 psduMap.begin(),
478 psduMap.end(),
479 [&to](std::pair<uint16_t, Ptr<WifiPsdu>> psdu) { return psdu.second->GetAddr1() == to; });
480 if (it != psduMap.end())
481 {
482 return it->second;
483 }
484 return nullptr;
485}
486
487void
489{
490 NS_LOG_FUNCTION(this << *rts << txVector);
491
492 if (m_psduMap.empty())
493 {
494 // A CTS Timeout occurred when protecting a single PSDU that is not included
495 // in a DL MU PPDU is handled by the parent classes
497 return;
498 }
499
500 NS_ABORT_MSG_IF(m_psduMap.size() > 1, "RTS/CTS cannot be used to protect an MU PPDU");
501 DoCtsTimeout(m_psduMap.begin()->second);
502 m_psduMap.clear();
503}
504
505void
507{
508 NS_LOG_FUNCTION(this);
509
510 // A multi-user transmission may succeed even if some stations did not respond.
511 // Remove such stations from the set of stations for which protection is not needed
512 // in the current TXOP.
513 for (const auto& address : m_txTimer.GetStasExpectedToRespond())
514 {
515 NS_LOG_DEBUG(address << " did not respond, hence it is no longer protected");
516 m_protectedStas.erase(address);
517 m_sentFrameTo.erase(address);
518 }
519
524 {
525 NS_LOG_DEBUG("Schedule another transmission in a SIFS after successful BSRP TF");
526 Simulator::Schedule(m_phy->GetSifs(), [=, this]() {
527 // TXOP limit is null, hence the txopDuration parameter is unused
528 if (!StartTransmission(m_edca, Seconds(0)))
529 {
530 SendCfEndIfNeeded();
531 }
532 });
534 {
536 }
537 m_sentFrameTo.clear();
538 }
539 else
540 {
542 }
543}
544
545void
546HeFrameExchangeManager::SendPsduMap()
547{
548 NS_LOG_FUNCTION(this);
549
550 NS_ASSERT(m_txParams.m_acknowledgment);
551 NS_ASSERT(!m_txTimer.IsRunning());
552
553 WifiTxTimer::Reason timerType = WifiTxTimer::NOT_RUNNING; // no timer
554 WifiTxVector* responseTxVector = nullptr;
555 Ptr<WifiMpdu> mpdu = nullptr;
556 Ptr<WifiPsdu> psdu = nullptr;
557 WifiTxVector txVector;
558 std::set<Mac48Address> staExpectResponseFrom;
559
560 // Compute the type of TX timer to set depending on the acknowledgment method
561
562 /*
563 * Acknowledgment via a sequence of BlockAckReq and BlockAck frames
564 */
565 if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE)
566 {
567 auto acknowledgment =
568 static_cast<WifiDlMuBarBaSequence*>(m_txParams.m_acknowledgment.get());
569
570 // schedule the transmission of required BlockAckReq frames
571 for (const auto& psdu : m_psduMap)
572 {
573 if (acknowledgment->stationsSendBlockAckReqTo.contains(psdu.second->GetAddr1()))
574 {
575 // the receiver of this PSDU will receive a BlockAckReq
576 std::set<uint8_t> tids = psdu.second->GetTids();
577 NS_ABORT_MSG_IF(tids.size() > 1,
578 "Acknowledgment method incompatible with a Multi-TID A-MPDU");
579 uint8_t tid = *tids.begin();
580
581 NS_ASSERT(m_edca);
582 auto [reqHdr, hdr] =
583 m_mac->GetQosTxop(tid)->PrepareBlockAckRequest(psdu.second->GetAddr1(), tid);
584 m_edca->GetBaManager()->ScheduleBar(reqHdr, hdr);
585 }
586 }
587
588 if (!acknowledgment->stationsReplyingWithNormalAck.empty())
589 {
590 // a station will reply immediately with a Normal Ack
591 timerType = WifiTxTimer::WAIT_NORMAL_ACK_AFTER_DL_MU_PPDU;
592 responseTxVector =
593 &acknowledgment->stationsReplyingWithNormalAck.begin()->second.ackTxVector;
594 auto from = acknowledgment->stationsReplyingWithNormalAck.begin()->first;
595 psdu = GetPsduTo(from, m_psduMap);
596 NS_ASSERT(psdu->GetNMpdus() == 1);
597 mpdu = *psdu->begin();
598 staExpectResponseFrom.insert(from);
599 }
600 else if (!acknowledgment->stationsReplyingWithBlockAck.empty())
601 {
602 // a station will reply immediately with a Block Ack
603 timerType = WifiTxTimer::WAIT_BLOCK_ACK;
604 responseTxVector =
605 &acknowledgment->stationsReplyingWithBlockAck.begin()->second.blockAckTxVector;
606 auto from = acknowledgment->stationsReplyingWithBlockAck.begin()->first;
607 psdu = GetPsduTo(from, m_psduMap);
608 staExpectResponseFrom.insert(from);
609 }
610 // else no station will reply immediately
611 }
612 /*
613 * Acknowledgment via a MU-BAR Trigger Frame sent as single user frame
614 */
615 else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_TF_MU_BAR)
616 {
617 auto acknowledgment = static_cast<WifiDlMuTfMuBar*>(m_txParams.m_acknowledgment.get());
618
619 if (!m_triggerFrame)
620 {
621 // we are transmitting the DL MU PPDU and have to schedule the
622 // transmission of a MU-BAR Trigger Frame.
623 // Create a TRIGVECTOR by "merging" all the BlockAck TXVECTORs
624 std::map<uint16_t, CtrlBAckRequestHeader> recipients;
625
626 NS_ASSERT(!acknowledgment->stationsReplyingWithBlockAck.empty());
627 auto staIt = acknowledgment->stationsReplyingWithBlockAck.begin();
628 m_trigVector = staIt->second.blockAckTxVector;
629 while (staIt != acknowledgment->stationsReplyingWithBlockAck.end())
630 {
631 NS_ASSERT(m_apMac);
632 uint16_t staId = m_apMac->GetAssociationId(staIt->first, m_linkId);
633
634 m_trigVector.SetHeMuUserInfo(staId,
635 staIt->second.blockAckTxVector.GetHeMuUserInfo(staId));
636 recipients.emplace(staId, staIt->second.barHeader);
637
638 staIt++;
639 }
640 // set the Length field of the response TXVECTOR, which is needed to correctly
641 // set the UL Length field of the MU-BAR Trigger Frame
642 m_trigVector.SetLength(acknowledgment->ulLength);
643
644 m_triggerFrame = PrepareMuBar(m_trigVector, recipients);
645 }
646 else
647 {
648 // we are transmitting the MU-BAR following the DL MU PPDU after a SIFS.
649 // m_psduMap and m_txParams are still the same as when the DL MU PPDU was sent.
650 // record the set of stations expected to send a BlockAck frame
651 for (auto& station : acknowledgment->stationsReplyingWithBlockAck)
652 {
653 staExpectResponseFrom.insert(station.first);
654 }
655
656 Ptr<WifiPsdu> triggerPsdu = GetWifiPsdu(m_triggerFrame, acknowledgment->muBarTxVector);
657 Time txDuration = WifiPhy::CalculateTxDuration(triggerPsdu->GetSize(),
658 acknowledgment->muBarTxVector,
659 m_phy->GetPhyBand());
660 // update acknowledgmentTime to correctly set the Duration/ID
661 *acknowledgment->acknowledgmentTime -= (m_phy->GetSifs() + txDuration);
662 m_triggerFrame->GetHeader().SetDuration(GetPsduDurationId(txDuration, m_txParams));
663
664 responseTxVector =
665 &acknowledgment->stationsReplyingWithBlockAck.begin()->second.blockAckTxVector;
666 Time timeout = txDuration + m_phy->GetSifs() + m_phy->GetSlot() +
667 WifiPhy::CalculatePhyPreambleAndHeaderDuration(*responseTxVector);
668
669 m_txTimer.Set(WifiTxTimer::WAIT_BLOCK_ACKS_IN_TB_PPDU,
670 timeout,
671 staExpectResponseFrom,
672 &HeFrameExchangeManager::BlockAcksInTbPpduTimeout,
673 this,
674 &m_psduMap,
675 staExpectResponseFrom.size());
676 m_channelAccessManager->NotifyAckTimeoutStartNow(timeout);
677
678 ForwardPsduDown(triggerPsdu, acknowledgment->muBarTxVector);
679
680 // Pass TRIGVECTOR to HE PHY (equivalent to PHY-TRIGGER.request primitive)
681 auto hePhy =
682 StaticCast<HePhy>(m_phy->GetPhyEntity(responseTxVector->GetModulationClass()));
683 hePhy->SetTrigVector(m_trigVector, timeout);
684
685 return;
686 }
687 }
688 /*
689 * Acknowledgment requested by MU-BAR TFs aggregated to PSDUs in the DL MU PPDU
690 */
691 else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_AGGREGATE_TF)
692 {
693 auto acknowledgment = static_cast<WifiDlMuAggregateTf*>(m_txParams.m_acknowledgment.get());
694
695 m_trigVector =
696 acknowledgment->stationsReplyingWithBlockAck.begin()->second.blockAckTxVector;
697
698 // record the set of stations expected to send a BlockAck frame
699 for (auto& station : acknowledgment->stationsReplyingWithBlockAck)
700 {
701 staExpectResponseFrom.insert(station.first);
702 // check that the station that is expected to send a BlockAck frame is
703 // actually the receiver of a PSDU
704 auto psduMapIt = std::find_if(m_psduMap.begin(),
705 m_psduMap.end(),
706 [&station](std::pair<uint16_t, Ptr<WifiPsdu>> psdu) {
707 return psdu.second->GetAddr1() == station.first;
708 });
709
710 NS_ASSERT(psduMapIt != m_psduMap.end());
711 // add a MU-BAR Trigger Frame to the PSDU
712 std::vector<Ptr<WifiMpdu>> mpduList(psduMapIt->second->begin(),
713 psduMapIt->second->end());
714 NS_ASSERT(mpduList.size() == psduMapIt->second->GetNMpdus());
715 // set the Length field of the response TXVECTOR, which is needed to correctly
716 // set the UL Length field of the MU-BAR Trigger Frame
717 station.second.blockAckTxVector.SetLength(acknowledgment->ulLength);
718 mpduList.push_back(PrepareMuBar(station.second.blockAckTxVector,
719 {{psduMapIt->first, station.second.barHeader}}));
720 psduMapIt->second = Create<WifiPsdu>(std::move(mpduList));
721 m_trigVector.SetHeMuUserInfo(
722 psduMapIt->first,
723 station.second.blockAckTxVector.GetHeMuUserInfo(psduMapIt->first));
724 }
725
726 timerType = WifiTxTimer::WAIT_BLOCK_ACKS_IN_TB_PPDU;
727 responseTxVector =
728 &acknowledgment->stationsReplyingWithBlockAck.begin()->second.blockAckTxVector;
729 m_trigVector.SetLength(acknowledgment->ulLength);
730 }
731 /*
732 * Basic Trigger Frame starting an UL MU transmission
733 */
734 else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA)
735 {
736 // the PSDU map being sent must contain a (Basic) Trigger Frame
737 NS_ASSERT(IsTrigger(m_psduMap));
738 mpdu = *m_psduMap.begin()->second->begin();
739
740 auto acknowledgment = static_cast<WifiUlMuMultiStaBa*>(m_txParams.m_acknowledgment.get());
741
742 // record the set of stations solicited by this Trigger Frame
743 for (const auto& station : acknowledgment->stationsReceivingMultiStaBa)
744 {
745 staExpectResponseFrom.insert(station.first.first);
746 }
747
748 // Reset stationsReceivingMultiStaBa, which will be filled as soon as
749 // TB PPDUs are received
750 acknowledgment->stationsReceivingMultiStaBa.clear();
751 acknowledgment->baType.m_bitmapLen.clear();
752
753 timerType = WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF;
754 responseTxVector = &acknowledgment->tbPpduTxVector;
755 m_trigVector = GetTrigVector(m_muScheduler->GetUlMuInfo(m_linkId).trigger);
756 }
757 /*
758 * BSRP Trigger Frame
759 */
760 else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::NONE &&
761 !m_txParams.m_txVector.IsUlMu() && IsTrigger(m_psduMap))
762 {
763 CtrlTriggerHeader& trigger = m_muScheduler->GetUlMuInfo(m_linkId).trigger;
764 NS_ASSERT(trigger.IsBsrp());
765 NS_ASSERT(m_apMac);
766
767 // record the set of stations solicited by this Trigger Frame
768 for (const auto& userInfo : trigger)
769 {
770 auto staIt = m_apMac->GetStaList(m_linkId).find(userInfo.GetAid12());
771 NS_ASSERT(staIt != m_apMac->GetStaList(m_linkId).end());
772 staExpectResponseFrom.insert(staIt->second);
773 }
774
775 timerType = WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF;
776 txVector = trigger.GetHeTbTxVector(trigger.begin()->GetAid12());
777 responseTxVector = &txVector;
778 m_trigVector = GetTrigVector(m_muScheduler->GetUlMuInfo(m_linkId).trigger);
779 }
780 /*
781 * TB PPDU solicited by a Basic Trigger Frame
782 */
783 else if (m_txParams.m_txVector.IsUlMu() &&
784 m_txParams.m_acknowledgment->method == WifiAcknowledgment::ACK_AFTER_TB_PPDU)
785 {
786 NS_ASSERT(m_psduMap.size() == 1);
787 timerType = WifiTxTimer::WAIT_BLOCK_ACK_AFTER_TB_PPDU;
788 NS_ASSERT(m_staMac && m_staMac->IsAssociated());
789 auto recv = m_psduMap.begin()->second->GetAddr1();
790 txVector = GetWifiRemoteStationManager()->GetBlockAckTxVector(recv, m_txParams.m_txVector);
791 responseTxVector = &txVector;
792 staExpectResponseFrom.insert(recv);
793 }
794 /*
795 * QoS Null frames solicited by a BSRP Trigger Frame
796 */
797 else if (m_txParams.m_txVector.IsUlMu() &&
798 m_txParams.m_acknowledgment->method == WifiAcknowledgment::NONE)
799 {
800 // No response is expected, so do nothing.
801 }
802 else
803 {
804 NS_ABORT_MSG("Unable to handle the selected acknowledgment method ("
805 << m_txParams.m_acknowledgment.get() << ")");
806 }
807
808 // create a map of Ptr<const WifiPsdu>, as required by the PHY
809 WifiConstPsduMap psduMap;
810 for (const auto& psdu : m_psduMap)
811 {
812 psduMap.emplace(psdu.first, psdu.second);
813 }
814
815 Time txDuration;
816 if (m_txParams.m_txVector.IsUlMu())
817 {
818 txDuration = HePhy::ConvertLSigLengthToHeTbPpduDuration(m_txParams.m_txVector.GetLength(),
819 m_txParams.m_txVector,
820 m_phy->GetPhyBand());
821 }
822 else
823 {
824 txDuration =
825 WifiPhy::CalculateTxDuration(psduMap, m_txParams.m_txVector, m_phy->GetPhyBand());
826
827 // Set Duration/ID
828 Time durationId = GetPsduDurationId(txDuration, m_txParams);
829
830 if (m_continueTxopAfterBsrpTf && m_edca && m_edca->GetTxopLimit(m_linkId).IsZero() &&
831 timerType == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF)
832 {
833 // add the duration of the following frame exchange to extend the NAV beyond the
834 // responses to the BSRP TF
835 durationId += m_muScheduler->GetExtraTimeForBsrpTfDurationId(m_linkId);
836 }
837
838 for (auto& psdu : m_psduMap)
839 {
840 psdu.second->SetDuration(durationId);
841 }
842 }
843
844 if (timerType == WifiTxTimer::NOT_RUNNING)
845 {
846 if (m_triggerFrame)
847 {
848 NS_LOG_DEBUG("Scheduling MU-BAR " << *m_triggerFrame);
849 Simulator::Schedule(txDuration + m_phy->GetSifs(),
850 &HeFrameExchangeManager::SendPsduMap,
851 this);
852 }
853 else if (!m_txParams.m_txVector.IsUlMu())
854 {
855 Simulator::Schedule(txDuration, &HeFrameExchangeManager::TransmissionSucceeded, this);
856 }
857 }
858 else
859 {
860 Time timeout = txDuration + m_phy->GetSifs() + m_phy->GetSlot() +
861 WifiPhy::CalculatePhyPreambleAndHeaderDuration(*responseTxVector);
862 m_channelAccessManager->NotifyAckTimeoutStartNow(timeout);
863
864 // start timer
865 switch (timerType)
866 {
867 case WifiTxTimer::WAIT_NORMAL_ACK_AFTER_DL_MU_PPDU:
868 NS_ASSERT(mpdu);
869 m_txTimer.Set(timerType,
870 timeout,
871 staExpectResponseFrom,
872 &HeFrameExchangeManager::NormalAckTimeout,
873 this,
874 mpdu,
875 m_txParams.m_txVector);
876 break;
877 case WifiTxTimer::WAIT_BLOCK_ACK:
878 NS_ASSERT(psdu);
879 m_txTimer.Set(timerType,
880 timeout,
881 staExpectResponseFrom,
882 &HeFrameExchangeManager::BlockAckTimeout,
883 this,
884 psdu,
885 m_txParams.m_txVector);
886 break;
887 case WifiTxTimer::WAIT_BLOCK_ACKS_IN_TB_PPDU:
888 m_txTimer.Set(timerType,
889 timeout,
890 staExpectResponseFrom,
891 &HeFrameExchangeManager::BlockAcksInTbPpduTimeout,
892 this,
893 &m_psduMap,
894 staExpectResponseFrom.size());
895 break;
896 case WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF:
897 case WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF:
898 m_txTimer.Set(timerType,
899 timeout,
900 staExpectResponseFrom,
901 &HeFrameExchangeManager::TbPpduTimeout,
902 this,
903 &m_psduMap,
904 staExpectResponseFrom.size());
905 break;
906 case WifiTxTimer::WAIT_BLOCK_ACK_AFTER_TB_PPDU:
907 m_txTimer.Set(timerType,
908 timeout,
909 staExpectResponseFrom,
910 &HeFrameExchangeManager::BlockAckAfterTbPpduTimeout,
911 this,
912 m_psduMap.begin()->second,
913 m_txParams.m_txVector);
914 break;
915 default:
916 NS_ABORT_MSG("Unknown timer type: " << timerType);
917 break;
918 }
919 }
920
921 // transmit the map of PSDUs
922 ForwardPsduMapDown(psduMap, m_txParams.m_txVector);
923
924 if (timerType == WifiTxTimer::WAIT_BLOCK_ACKS_IN_TB_PPDU ||
925 timerType == WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF ||
926 timerType == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF)
927 {
928 // Pass TRIGVECTOR to HE PHY (equivalent to PHY-TRIGGER.request primitive)
929 auto hePhy = StaticCast<HePhy>(m_phy->GetPhyEntity(responseTxVector->GetModulationClass()));
930 hePhy->SetTrigVector(m_trigVector, m_txTimer.GetDelayLeft());
931 }
932 else if (timerType == WifiTxTimer::NOT_RUNNING &&
933 (m_txParams.m_txVector.IsUlMu() ||
934 m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE))
935 {
936 // clear m_psduMap after sending QoS Null frames following a BSRP Trigger Frame or after
937 // sending a DL MU PPDU with BAR-BA ack sequence and no immediate response is expected
938 Simulator::Schedule(txDuration, &WifiPsduMap::clear, &m_psduMap);
939 }
940
941 if (m_txTimer.IsRunning() && timerType != WifiTxTimer::WAIT_BLOCK_ACK_AFTER_TB_PPDU)
942 {
943 NS_ASSERT(m_sentFrameTo.empty());
944
945 // do not record that a frame is being sent to an EMLSR client unless the AP is sending a
946 // BSRP TF or the EMLSR client is already protected
947 for (const auto& address : staExpectResponseFrom)
948 {
949 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(address) ||
950 timerType == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF ||
951 m_protectedStas.contains(address))
952 {
953 m_sentFrameTo.insert(address);
954 }
955 }
956 }
957}
958
959void
960HeFrameExchangeManager::ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVector& txVector)
961{
962 NS_LOG_FUNCTION(this << psduMap << txVector);
963
964 if (ns3::IsDlMu(txVector.GetPreambleType()))
965 {
966 auto hePhy = StaticCast<HePhy>(m_phy->GetPhyEntity(txVector.GetModulationClass()));
967 auto sigBMode = hePhy->GetSigBMode(txVector);
968 txVector.SetSigBMode(sigBMode);
969 }
970
971 for (const auto& psdu : psduMap)
972 {
973 NS_LOG_DEBUG("Transmitting: [STAID=" << psdu.first << ", " << *psdu.second << "]");
974 }
975 NS_LOG_DEBUG("TXVECTOR: " << txVector);
976 for (const auto& [staId, psdu] : psduMap)
977 {
978 FinalizeMacHeader(psdu);
979 NotifyTxToEdca(psdu);
980 }
981 m_allowedWidth = std::min(m_allowedWidth, txVector.GetChannelWidth());
982
983 if (psduMap.size() > 1 || psduMap.begin()->second->IsAggregate() ||
984 psduMap.begin()->second->IsSingle())
985 {
986 txVector.SetAggregation(true);
987 }
988
989 auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, m_phy->GetPhyBand());
990 // The TXNAV timer is a single timer, shared by the EDCAFs within a STA, that is initialized
991 // with the duration from the Duration/ID field in the frame most recently successfully
992 // transmitted by the TXOP holder, except for PS-Poll frames. (Sec.10.23.2.2 IEEE 802.11-2020)
993 m_txNav = Max(m_txNav, Simulator::Now() + txDuration + psduMap.cbegin()->second->GetDuration());
994
995 m_phy->Send(psduMap, txVector);
996}
997
999HeFrameExchangeManager::PrepareMuBar(const WifiTxVector& responseTxVector,
1000 std::map<uint16_t, CtrlBAckRequestHeader> recipients) const
1001{
1002 NS_LOG_FUNCTION(this << responseTxVector);
1003 NS_ASSERT(responseTxVector.GetHeMuUserInfoMap().size() == recipients.size());
1004 NS_ASSERT(!recipients.empty());
1005
1006 CtrlTriggerHeader muBar(TriggerFrameType::MU_BAR_TRIGGER, responseTxVector);
1007 SetTargetRssi(muBar);
1008 // Set the CS Required subfield to true, unless the UL Length subfield is less
1009 // than or equal to 418 (see Section 26.5.2.5 of 802.11ax-2021)
1010 muBar.SetCsRequired(muBar.GetUlLength() > 418);
1011
1012 // Add the Trigger Dependent User Info subfield to every User Info field
1013 for (auto& userInfo : muBar)
1014 {
1015 auto recipientIt = recipients.find(userInfo.GetAid12());
1016 NS_ASSERT(recipientIt != recipients.end());
1017
1018 // Store the BAR in the Trigger Dependent User Info subfield
1019 userInfo.SetMuBarTriggerDepUserInfo(recipientIt->second);
1020 }
1021
1022 Ptr<Packet> bar = Create<Packet>();
1023 bar->AddHeader(muBar);
1024 Mac48Address rxAddress;
1025 // "If the Trigger frame has one User Info field and the AID12 subfield of the
1026 // User Info contains the AID of a STA, then the RA field is set to the address
1027 // of that STA". Otherwise, it is set to the broadcast address (Sec. 9.3.1.23 -
1028 // 802.11ax amendment draft 3.0)
1029 if (muBar.GetNUserInfoFields() > 1)
1030 {
1031 rxAddress = Mac48Address::GetBroadcast();
1032 }
1033 else
1034 {
1035 NS_ASSERT(m_apMac);
1036 rxAddress = m_apMac->GetStaList(m_linkId).at(recipients.begin()->first);
1037 }
1038
1039 WifiMacHeader hdr;
1041 hdr.SetAddr1(rxAddress);
1042 hdr.SetAddr2(m_self);
1043 hdr.SetDsNotTo();
1044 hdr.SetDsNotFrom();
1045 hdr.SetNoRetry();
1046 hdr.SetNoMoreFragments();
1047
1048 return Create<WifiMpdu>(bar, hdr);
1049}
1050
1051void
1052HeFrameExchangeManager::CalculateProtectionTime(WifiProtection* protection) const
1053{
1054 NS_LOG_FUNCTION(this << protection);
1055 NS_ASSERT(protection != nullptr);
1056
1057 if (protection->method == WifiProtection::MU_RTS_CTS)
1058 {
1059 auto muRtsCtsProtection = static_cast<WifiMuRtsCtsProtection*>(protection);
1060
1061 // Get the TXVECTOR used by one station to send the CTS response. This is used
1062 // to compute the TX duration, so it does not matter which station we choose
1063 WifiTxVector ctsTxVector =
1064 GetCtsTxVectorAfterMuRts(muRtsCtsProtection->muRts,
1065 muRtsCtsProtection->muRts.begin()->GetAid12());
1066
1068 muRtsCtsProtection->muRts.GetSerializedSize() + WIFI_MAC_FCS_LENGTH;
1069 muRtsCtsProtection->protectionTime =
1070 WifiPhy::CalculateTxDuration(muRtsSize,
1071 muRtsCtsProtection->muRtsTxVector,
1072 m_phy->GetPhyBand()) +
1073 WifiPhy::CalculateTxDuration(GetCtsSize(), ctsTxVector, m_phy->GetPhyBand()) +
1074 2 * m_phy->GetSifs();
1075 }
1076 else
1077 {
1078 VhtFrameExchangeManager::CalculateProtectionTime(protection);
1079 }
1080}
1081
1082void
1083HeFrameExchangeManager::CalculateAcknowledgmentTime(WifiAcknowledgment* acknowledgment) const
1084{
1085 NS_LOG_FUNCTION(this << acknowledgment);
1086 NS_ASSERT(acknowledgment);
1087
1088 /*
1089 * Acknowledgment via a sequence of BlockAckReq and BlockAck frames
1090 */
1091 if (acknowledgment->method == WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE)
1092 {
1093 auto dlMuBarBaAcknowledgment = static_cast<WifiDlMuBarBaSequence*>(acknowledgment);
1094
1095 Time duration;
1096
1097 // normal ack or implicit BAR policy can be used for (no more than) one receiver
1098 NS_ABORT_IF(dlMuBarBaAcknowledgment->stationsReplyingWithNormalAck.size() +
1099 dlMuBarBaAcknowledgment->stationsReplyingWithBlockAck.size() >
1100 1);
1101
1102 if (!dlMuBarBaAcknowledgment->stationsReplyingWithNormalAck.empty())
1103 {
1104 const auto& info =
1105 dlMuBarBaAcknowledgment->stationsReplyingWithNormalAck.begin()->second;
1106 duration +=
1107 m_phy->GetSifs() +
1108 WifiPhy::CalculateTxDuration(GetAckSize(), info.ackTxVector, m_phy->GetPhyBand());
1109 }
1110
1111 if (!dlMuBarBaAcknowledgment->stationsReplyingWithBlockAck.empty())
1112 {
1113 const auto& info =
1114 dlMuBarBaAcknowledgment->stationsReplyingWithBlockAck.begin()->second;
1115 duration +=
1116 m_phy->GetSifs() + WifiPhy::CalculateTxDuration(GetBlockAckSize(info.baType),
1117 info.blockAckTxVector,
1118 m_phy->GetPhyBand());
1119 }
1120
1121 for (const auto& stations : dlMuBarBaAcknowledgment->stationsSendBlockAckReqTo)
1122 {
1123 const auto& info = stations.second;
1124 duration += m_phy->GetSifs() +
1125 WifiPhy::CalculateTxDuration(GetBlockAckRequestSize(info.barType),
1126 info.blockAckReqTxVector,
1127 m_phy->GetPhyBand()) +
1128 m_phy->GetSifs() +
1129 WifiPhy::CalculateTxDuration(GetBlockAckSize(info.baType),
1130 info.blockAckTxVector,
1131 m_phy->GetPhyBand());
1132 }
1133
1134 dlMuBarBaAcknowledgment->acknowledgmentTime = duration;
1135 }
1136 /*
1137 * Acknowledgment via a MU-BAR Trigger Frame sent as single user frame
1138 */
1139 else if (acknowledgment->method == WifiAcknowledgment::DL_MU_TF_MU_BAR)
1140 {
1141 auto dlMuTfMuBarAcknowledgment = static_cast<WifiDlMuTfMuBar*>(acknowledgment);
1142
1143 Time duration;
1144
1145 for (const auto& stations : dlMuTfMuBarAcknowledgment->stationsReplyingWithBlockAck)
1146 {
1147 // compute the TX duration of the BlockAck response from this receiver.
1148 const auto& info = stations.second;
1149 NS_ASSERT(info.blockAckTxVector.GetHeMuUserInfoMap().size() == 1);
1150 uint16_t staId = info.blockAckTxVector.GetHeMuUserInfoMap().begin()->first;
1151 Time currBlockAckDuration = WifiPhy::CalculateTxDuration(GetBlockAckSize(info.baType),
1152 info.blockAckTxVector,
1153 m_phy->GetPhyBand(),
1154 staId);
1155 // update the max duration among all the Block Ack responses
1156 if (currBlockAckDuration > duration)
1157 {
1158 duration = currBlockAckDuration;
1159 }
1160 }
1161
1162 // The computed duration may not be coded exactly in the L-SIG length, hence determine
1163 // the exact duration corresponding to the value that will be coded in this field.
1164 WifiTxVector& txVector = dlMuTfMuBarAcknowledgment->stationsReplyingWithBlockAck.begin()
1165 ->second.blockAckTxVector;
1166 std::tie(dlMuTfMuBarAcknowledgment->ulLength, duration) =
1167 HePhy::ConvertHeTbPpduDurationToLSigLength(duration, txVector, m_phy->GetPhyBand());
1168
1169 uint32_t muBarSize = GetMuBarSize(dlMuTfMuBarAcknowledgment->barTypes);
1170 if (dlMuTfMuBarAcknowledgment->muBarTxVector.GetModulationClass() >= WIFI_MOD_CLASS_VHT)
1171 {
1172 // MU-BAR TF will be sent as an S-MPDU
1173 muBarSize = MpduAggregator::GetSizeIfAggregated(muBarSize, 0);
1174 }
1175 dlMuTfMuBarAcknowledgment->acknowledgmentTime =
1176 m_phy->GetSifs() +
1177 WifiPhy::CalculateTxDuration(muBarSize,
1178 dlMuTfMuBarAcknowledgment->muBarTxVector,
1179 m_phy->GetPhyBand()) +
1180 m_phy->GetSifs() + duration;
1181 }
1182 /*
1183 * Acknowledgment requested by MU-BAR TFs aggregated to PSDUs in the DL MU PPDU
1184 */
1185 else if (acknowledgment->method == WifiAcknowledgment::DL_MU_AGGREGATE_TF)
1186 {
1187 auto dlMuAggrTfAcknowledgment = static_cast<WifiDlMuAggregateTf*>(acknowledgment);
1188
1189 Time duration;
1190
1191 for (const auto& stations : dlMuAggrTfAcknowledgment->stationsReplyingWithBlockAck)
1192 {
1193 // compute the TX duration of the BlockAck response from this receiver.
1194 const auto& info = stations.second;
1195 NS_ASSERT(info.blockAckTxVector.GetHeMuUserInfoMap().size() == 1);
1196 uint16_t staId = info.blockAckTxVector.GetHeMuUserInfoMap().begin()->first;
1197 Time currBlockAckDuration = WifiPhy::CalculateTxDuration(GetBlockAckSize(info.baType),
1198 info.blockAckTxVector,
1199 m_phy->GetPhyBand(),
1200 staId);
1201 // update the max duration among all the Block Ack responses
1202 if (currBlockAckDuration > duration)
1203 {
1204 duration = currBlockAckDuration;
1205 }
1206 }
1207
1208 // The computed duration may not be coded exactly in the L-SIG length, hence determine
1209 // the exact duration corresponding to the value that will be coded in this field.
1210 WifiTxVector& txVector =
1211 dlMuAggrTfAcknowledgment->stationsReplyingWithBlockAck.begin()->second.blockAckTxVector;
1212 std::tie(dlMuAggrTfAcknowledgment->ulLength, duration) =
1213 HePhy::ConvertHeTbPpduDurationToLSigLength(duration, txVector, m_phy->GetPhyBand());
1214 dlMuAggrTfAcknowledgment->acknowledgmentTime = m_phy->GetSifs() + duration;
1215 }
1216 /*
1217 * Basic Trigger Frame starting an UL MU transmission
1218 */
1219 else if (acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA)
1220 {
1221 auto ulMuMultiStaBa = static_cast<WifiUlMuMultiStaBa*>(acknowledgment);
1222
1223 Time duration = WifiPhy::CalculateTxDuration(GetBlockAckSize(ulMuMultiStaBa->baType),
1224 ulMuMultiStaBa->multiStaBaTxVector,
1225 m_phy->GetPhyBand());
1226 ulMuMultiStaBa->acknowledgmentTime = m_phy->GetSifs() + duration;
1227 }
1228 /*
1229 * TB PPDU solicired by a Basic or BSRP Trigger Frame
1230 */
1231 else if (acknowledgment->method == WifiAcknowledgment::ACK_AFTER_TB_PPDU)
1232 {
1233 // The station solicited by the Trigger Frame does not have to account
1234 // for the actual acknowledgment time since it is given the PPDU duration
1235 // through the Trigger Frame
1236 acknowledgment->acknowledgmentTime = Seconds(0);
1237 }
1238 else
1239 {
1240 VhtFrameExchangeManager::CalculateAcknowledgmentTime(acknowledgment);
1241 }
1242}
1243
1245HeFrameExchangeManager::GetCtsModeAfterMuRts() const
1246{
1247 // The CTS frame sent in response to an MU-RTS Trigger frame shall be carried in a non-HT or
1248 // non-HT duplicate PPDU (see Clause 17) with a 6 Mb/s rate (Sec. 26.2.6.3 of 802.11ax-2021)
1249 return m_phy->GetPhyBand() == WIFI_PHY_BAND_2_4GHZ ? ErpOfdmPhy::GetErpOfdmRate6Mbps()
1250 : OfdmPhy::GetOfdmRate6Mbps();
1251}
1252
1254HeFrameExchangeManager::GetCtsTxVectorAfterMuRts(const CtrlTriggerHeader& trigger,
1255 uint16_t staId) const
1256{
1257 NS_LOG_FUNCTION(this << trigger << staId);
1258
1259 auto userInfoIt = trigger.FindUserInfoWithAid(staId);
1260 NS_ASSERT_MSG(userInfoIt != trigger.end(), "User Info field for AID=" << staId << " not found");
1261 MHz_u bw{0};
1262
1263 if (uint8_t ru = userInfoIt->GetMuRtsRuAllocation(); ru < 65)
1264 {
1265 bw = MHz_u{20};
1266 }
1267 else if (ru < 67)
1268 {
1269 bw = MHz_u{40};
1270 }
1271 else if (ru == 67)
1272 {
1273 bw = MHz_u{80};
1274 }
1275 else
1276 {
1277 NS_ASSERT(ru == 68);
1278 bw = MHz_u{160};
1279 }
1280
1281 auto txVector = GetWifiRemoteStationManager()->GetCtsTxVector(m_bssid, GetCtsModeAfterMuRts());
1282 // set the channel width of the CTS TXVECTOR according to the allocated RU
1283 txVector.SetChannelWidth(bw);
1284
1285 return txVector;
1286}
1287
1288Time
1289HeFrameExchangeManager::GetTxDuration(uint32_t ppduPayloadSize,
1290 Mac48Address receiver,
1291 const WifiTxParameters& txParams) const
1292{
1293 if (!txParams.m_txVector.IsMu())
1294 {
1295 return VhtFrameExchangeManager::GetTxDuration(ppduPayloadSize, receiver, txParams);
1296 }
1297
1298 NS_ASSERT_MSG(!txParams.m_txVector.IsDlMu() || m_apMac, "DL MU can be done by an AP");
1299 NS_ASSERT_MSG(!txParams.m_txVector.IsUlMu() || m_staMac, "UL MU can be done by a STA");
1300
1301 if (txParams.m_acknowledgment &&
1302 txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_AGGREGATE_TF)
1303 {
1304 // we need to account for the size of the aggregated MU-BAR Trigger Frame
1305 auto psduInfo = txParams.GetPsduInfo(receiver);
1306 NS_ASSERT_MSG(psduInfo, "No information for " << receiver << " in TX params");
1307 NS_ASSERT_MSG(!psduInfo->seqNumbers.empty(), "No sequence number for " << receiver);
1308 const auto tid = psduInfo->seqNumbers.cbegin()->first;
1309
1310 ppduPayloadSize = MpduAggregator::GetSizeIfAggregated(
1311 GetMuBarSize({m_mac->GetBarTypeAsOriginator(receiver, tid)}),
1312 ppduPayloadSize);
1313 }
1314
1315 uint16_t staId = (txParams.m_txVector.IsDlMu() ? m_apMac->GetAssociationId(receiver, m_linkId)
1316 : m_staMac->GetAssociationId());
1317 Time psduDuration = WifiPhy::CalculateTxDuration(ppduPayloadSize,
1318 txParams.m_txVector,
1319 m_phy->GetPhyBand(),
1320 staId);
1321
1322 return txParams.m_txDuration ? std::max(psduDuration, *txParams.m_txDuration) : psduDuration;
1323}
1324
1325void
1326HeFrameExchangeManager::TbPpduTimeout(WifiPsduMap* psduMap, std::size_t nSolicitedStations)
1327{
1328 NS_LOG_FUNCTION(this << psduMap << nSolicitedStations);
1329 DoTbPpduTimeout(psduMap, nSolicitedStations, true);
1330}
1331
1332void
1333HeFrameExchangeManager::DoTbPpduTimeout(WifiPsduMap* psduMap,
1334 std::size_t nSolicitedStations,
1335 bool updateFailedCw)
1336{
1337 const auto& staMissedTbPpduFrom = m_txTimer.GetStasExpectedToRespond();
1338 NS_LOG_FUNCTION(this << psduMap << staMissedTbPpduFrom.size() << nSolicitedStations
1339 << updateFailedCw);
1340
1341 NS_ASSERT(psduMap);
1342 NS_ASSERT(IsTrigger(*psduMap));
1343
1344 // This method is called if some station(s) did not send a TB PPDU
1345 NS_ASSERT(!staMissedTbPpduFrom.empty());
1346 NS_ASSERT(m_edca);
1347
1348 if (staMissedTbPpduFrom.size() == nSolicitedStations)
1349 {
1350 // no station replied, the transmission failed
1351 CtrlTriggerHeader trigger;
1352 psduMap->cbegin()->second->GetPayload(0)->PeekHeader(trigger);
1353
1354 if (m_continueTxopAfterBsrpTf && m_edca->GetTxopLimit(m_linkId).IsZero() &&
1355 trigger.IsBsrp())
1356 {
1357 SendCfEndIfNeeded();
1358 }
1359
1360 TransmissionFailed(!updateFailedCw);
1361 }
1362 else if (!m_multiStaBaEvent.IsPending())
1363 {
1364 m_edca->ResetCw(m_linkId);
1365 TransmissionSucceeded();
1366 }
1367 else
1368 {
1369 // Stations that did not respond must be removed from the set of stations for which
1370 // protection is not needed in the current TXOP.
1371 for (const auto& address : staMissedTbPpduFrom)
1372 {
1373 NS_LOG_DEBUG(address << " did not respond, hence it is no longer protected");
1374 m_protectedStas.erase(address);
1375 m_sentFrameTo.erase(address);
1376 }
1377 if (m_protectedIfResponded)
1378 {
1379 m_protectedStas.merge(m_sentFrameTo);
1380 }
1381 m_sentFrameTo.clear();
1382 }
1383
1384 m_psduMap.clear();
1385}
1386
1387void
1388HeFrameExchangeManager::BlockAcksInTbPpduTimeout(WifiPsduMap* psduMap,
1389 std::size_t nSolicitedStations)
1390{
1391 NS_LOG_FUNCTION(this << psduMap << nSolicitedStations);
1392
1393 NS_ASSERT(psduMap);
1394 NS_ASSERT(m_txParams.m_acknowledgment &&
1395 (m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_AGGREGATE_TF ||
1396 m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_TF_MU_BAR));
1397
1398 // This method is called if some station(s) did not send a BlockAck frame in a TB PPDU
1399 const auto& staMissedBlockAckFrom = m_txTimer.GetStasExpectedToRespond();
1400 NS_ASSERT(!staMissedBlockAckFrom.empty());
1401
1402 if (staMissedBlockAckFrom.size() == nSolicitedStations)
1403 {
1404 // no station replied, the transmission failed
1405 GetWifiRemoteStationManager()->ReportDataFailed(*psduMap->begin()->second->begin());
1406 }
1407
1408 if (m_triggerFrame)
1409 {
1410 // this is strictly needed for DL_MU_TF_MU_BAR only
1411 m_triggerFrame = nullptr;
1412 }
1413
1414 for (const auto& sta : staMissedBlockAckFrom)
1415 {
1416 auto psdu = GetPsduTo(sta, *psduMap);
1417 NS_ASSERT(psdu);
1418 MissedBlockAck(psdu, m_txParams.m_txVector);
1419 }
1420
1421 NS_ASSERT(m_edca);
1422
1423 if (staMissedBlockAckFrom.size() == nSolicitedStations)
1424 {
1425 // no station replied, the transmission failed
1426 TransmissionFailed();
1427 }
1428 else
1429 {
1430 m_edca->ResetCw(m_linkId);
1431 TransmissionSucceeded();
1432 }
1433 m_psduMap.clear();
1434}
1435
1436void
1437HeFrameExchangeManager::BlockAckAfterTbPpduTimeout(Ptr<WifiPsdu> psdu, const WifiTxVector& txVector)
1438{
1439 NS_LOG_FUNCTION(this << *psdu << txVector);
1440
1441 GetWifiRemoteStationManager()->ReportDataFailed(*psdu->begin());
1442
1443 MissedBlockAck(psdu, m_txParams.m_txVector);
1444
1445 // This is a PSDU sent in a TB PPDU. An HE STA resumes the EDCA backoff procedure
1446 // without modifying CW or the backoff counter for the associated EDCAF, after
1447 // transmission of an MPDU in a TB PPDU regardless of whether the STA has received
1448 // the corresponding acknowledgment frame in response to the MPDU sent in the TB PPDU
1449 // (Sec. 10.22.2.2 of 11ax Draft 3.0)
1450 m_psduMap.clear();
1451}
1452
1453void
1454HeFrameExchangeManager::NormalAckTimeout(Ptr<WifiMpdu> mpdu, const WifiTxVector& txVector)
1455{
1456 NS_LOG_FUNCTION(this << *mpdu << txVector);
1457
1458 VhtFrameExchangeManager::NormalAckTimeout(mpdu, txVector);
1459
1460 // If a Normal Ack is missed in response to a DL MU PPDU requiring acknowledgment
1461 // in SU format, we have to set the Retry flag for all transmitted MPDUs that have
1462 // not been acknowledged nor discarded and clear m_psduMap since the transmission failed.
1463 for (auto& psdu : m_psduMap)
1464 {
1465 for (auto& mpdu : *PeekPointer(psdu.second))
1466 {
1467 if (mpdu->IsQueued())
1468 {
1469 m_mac->GetTxopQueue(mpdu->GetQueueAc())->GetOriginal(mpdu)->GetHeader().SetRetry();
1470 mpdu->ResetInFlight(m_linkId);
1471 }
1472 }
1473 }
1474 m_psduMap.clear();
1475}
1476
1477void
1478HeFrameExchangeManager::BlockAckTimeout(Ptr<WifiPsdu> psdu, const WifiTxVector& txVector)
1479{
1480 NS_LOG_FUNCTION(this << *psdu << txVector);
1481
1482 VhtFrameExchangeManager::BlockAckTimeout(psdu, txVector);
1483
1484 // If a Block Ack is missed in response to a DL MU PPDU requiring acknowledgment
1485 // in SU format, we have to set the Retry flag for all transmitted MPDUs that have
1486 // not been acknowledged nor discarded and clear m_psduMap since the transmission failed.
1487 for (auto& psdu : m_psduMap)
1488 {
1489 for (auto& mpdu : *PeekPointer(psdu.second))
1490 {
1491 if (mpdu->IsQueued())
1492 {
1493 mpdu->GetHeader().SetRetry();
1494 }
1495 }
1496 }
1497 m_psduMap.clear();
1498}
1499
1501HeFrameExchangeManager::GetTrigVector(const CtrlTriggerHeader& trigger) const
1502{
1503 WifiTxVector v;
1504 v.SetPreambleType(trigger.GetVariant() == TriggerFrameVariant::HE ? WIFI_PREAMBLE_HE_TB
1506 v.SetChannelWidth(trigger.GetUlBandwidth());
1508 v.SetLength(trigger.GetUlLength());
1509 for (const auto& userInfoField : trigger)
1510 {
1512 userInfoField.GetAid12(),
1513 {userInfoField.GetRuAllocation(), userInfoField.GetUlMcs(), userInfoField.GetNss()});
1514 }
1515 return v;
1516}
1517
1519HeFrameExchangeManager::GetHeTbTxVector(CtrlTriggerHeader trigger, Mac48Address triggerSender) const
1520{
1521 NS_ASSERT(triggerSender !=
1522 m_self); // TxPower information is used only by STAs, it is useless for the sending AP
1523 // (which can directly use CtrlTriggerHeader::GetHeTbTxVector)
1524 NS_ASSERT(m_staMac);
1525 uint16_t staId = m_staMac->GetAssociationId();
1526 auto userInfoIt = trigger.FindUserInfoWithAid(staId);
1527 NS_ASSERT(userInfoIt != trigger.end());
1528
1529 WifiTxVector v = trigger.GetHeTbTxVector(staId);
1530
1531 Ptr<HeConfiguration> heConfiguration = m_mac->GetHeConfiguration();
1532 NS_ASSERT_MSG(heConfiguration, "This STA has to be an HE station to send an HE TB PPDU");
1533 v.SetBssColor(heConfiguration->m_bssColor);
1534
1535 if (userInfoIt->IsUlTargetRssiMaxTxPower())
1536 {
1537 NS_LOG_LOGIC("AP requested using the max transmit power (" << m_phy->GetTxPowerEnd()
1538 << " dBm)");
1539 v.SetTxPowerLevel(m_phy->GetNTxPower());
1540 return v;
1541 }
1542
1543 uint8_t powerLevel = GetWifiRemoteStationManager()->GetDefaultTxPowerLevel();
1544 /**
1545 * Get the transmit power to use for an HE TB PPDU
1546 * considering:
1547 * - the transmit power used by the AP to send the Trigger Frame (TF),
1548 * obtained from the AP TX Power subfield of the Common Info field
1549 * of the TF.
1550 * - the target uplink RSSI expected by the AP for the triggered HE TB PPDU,
1551 * obtained from the UL Target RSSI subfield of the User Info field
1552 * of the TF.
1553 * - the RSSI of the PPDU containing the TF, typically logged by the
1554 * WifiRemoteStationManager upon reception of the TF from the AP.
1555 *
1556 * It is assumed that path loss is symmetric (i.e. uplink path loss is
1557 * equivalent to the measured downlink path loss);
1558 *
1559 * Refer to section 27.3.14.2 (Power pre-correction) of 802.11ax Draft 4.0 for more details.
1560 */
1561 auto optRssi = GetMostRecentRssi(triggerSender);
1562 NS_ASSERT(optRssi);
1563 int8_t pathLossDb =
1564 trigger.GetApTxPower() -
1565 static_cast<int8_t>(
1566 *optRssi); // cast RSSI to be on equal footing with AP Tx power information
1567 auto reqTxPower = dBm_u{static_cast<double>(userInfoIt->GetUlTargetRssi() + pathLossDb)};
1568
1569 // Convert the transmit power to a power level
1570 uint8_t numPowerLevels = m_phy->GetNTxPower();
1571 if (numPowerLevels > 1)
1572 {
1573 dBm_u step = (m_phy->GetTxPowerEnd() - m_phy->GetTxPowerStart()) / (numPowerLevels - 1);
1574 powerLevel = static_cast<uint8_t>(
1575 ceil((reqTxPower - m_phy->GetTxPowerStart()) /
1576 step)); // better be slightly above so as to satisfy target UL RSSI
1577 if (powerLevel > numPowerLevels)
1578 {
1579 powerLevel = numPowerLevels; // capping will trigger warning below
1580 }
1581 }
1582 if (reqTxPower > m_phy->GetPower(powerLevel))
1583 {
1584 NS_LOG_WARN("The requested power level (" << reqTxPower << "dBm) cannot be satisfied (max: "
1585 << m_phy->GetTxPowerEnd() << "dBm)");
1586 }
1587 v.SetTxPowerLevel(powerLevel);
1588 NS_LOG_LOGIC("UL power control: input "
1589 << "{pathLoss=" << pathLossDb << "dB, reqTxPower=" << reqTxPower << "dBm}"
1590 << " output "
1591 << "{powerLevel=" << +powerLevel << " -> " << m_phy->GetPower(powerLevel) << "dBm}"
1592 << " PHY power capa "
1593 << "{min=" << m_phy->GetTxPowerStart() << "dBm, max=" << m_phy->GetTxPowerEnd()
1594 << "dBm, levels:" << +numPowerLevels << "}");
1595
1596 return v;
1597}
1598
1599std::optional<dBm_u>
1600HeFrameExchangeManager::GetMostRecentRssi(const Mac48Address& address) const
1601{
1602 return GetWifiRemoteStationManager()->GetMostRecentRssi(address);
1603}
1604
1605void
1606HeFrameExchangeManager::SetTargetRssi(CtrlTriggerHeader& trigger) const
1607{
1608 NS_LOG_FUNCTION(this);
1609 NS_ASSERT(m_apMac);
1610
1611 trigger.SetApTxPower(static_cast<int8_t>(
1612 m_phy->GetPower(GetWifiRemoteStationManager()->GetDefaultTxPowerLevel())));
1613 for (auto& userInfo : trigger)
1614 {
1615 const auto staList = m_apMac->GetStaList(m_linkId);
1616 auto itAidAddr = staList.find(userInfo.GetAid12());
1617 NS_ASSERT(itAidAddr != staList.end());
1618 auto optRssi = GetMostRecentRssi(itAidAddr->second);
1619 NS_ASSERT(optRssi);
1620 auto rssi = static_cast<int8_t>(*optRssi);
1621 rssi = (rssi >= -20)
1622 ? -20
1623 : ((rssi <= -110) ? -110 : rssi); // cap so as to keep within [-110; -20] dBm
1624 userInfo.SetUlTargetRssi(rssi);
1625 }
1626}
1627
1628void
1629HeFrameExchangeManager::PostProcessFrame(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector)
1630{
1631 NS_LOG_FUNCTION(this << psdu << txVector);
1632
1633 auto txVectorCopy = txVector;
1634
1635 if (psdu->GetNMpdus() == 1 && psdu->GetHeader(0).IsTrigger())
1636 {
1637 CtrlTriggerHeader trigger;
1638 psdu->GetPayload(0)->PeekHeader(trigger);
1639 if (trigger.IsMuRts())
1640 {
1641 const WifiMacHeader& muRts = psdu->GetHeader(0);
1642 // A station receiving an MU-RTS behaves just like as if it received an RTS.
1643 // Determine whether the MU-RTS is addressed to this station or not and
1644 // prepare an "equivalent" RTS frame so that we can reuse the UpdateNav()
1645 // and SetTxopHolder() methods of the parent classes
1646 WifiMacHeader rts;
1648 rts.SetDsNotFrom();
1649 rts.SetDsNotTo();
1650 rts.SetDuration(muRts.GetDuration());
1651 rts.SetAddr2(muRts.GetAddr2());
1652 if (m_staMac != nullptr && m_staMac->IsAssociated() &&
1653 muRts.GetAddr2() == m_bssid // sent by the AP this STA is associated with
1654 && trigger.FindUserInfoWithAid(m_staMac->GetAssociationId()) != trigger.end())
1655 {
1656 // the MU-RTS is addressed to this station
1657 rts.SetAddr1(m_self);
1658 }
1659 else
1660 {
1661 rts.SetAddr1(muRts.GetAddr2()); // an address different from that of this station
1662 }
1663 psdu = Create<const WifiPsdu>(Create<Packet>(), rts);
1664 // The duration of the NAV reset timeout has to take into account that the CTS
1665 // response is sent using the 6 Mbps data rate
1666 txVectorCopy =
1667 GetWifiRemoteStationManager()->GetCtsTxVector(m_bssid, GetCtsModeAfterMuRts());
1668 }
1669 }
1670 VhtFrameExchangeManager::PostProcessFrame(psdu, txVectorCopy);
1671}
1672
1673void
1674HeFrameExchangeManager::SendCtsAfterMuRts(const WifiMacHeader& muRtsHdr,
1675 const CtrlTriggerHeader& trigger,
1676 double muRtsSnr)
1677{
1678 NS_LOG_FUNCTION(this << muRtsHdr << trigger << muRtsSnr);
1679
1680 if (!UlMuCsMediumIdle(trigger))
1681 {
1682 NS_LOG_DEBUG("UL MU CS indicated medium busy, cannot send CTS");
1683 return;
1684 }
1685
1686 NS_ASSERT(m_staMac != nullptr && m_staMac->IsAssociated());
1687 WifiTxVector ctsTxVector = GetCtsTxVectorAfterMuRts(trigger, m_staMac->GetAssociationId());
1688 ctsTxVector.SetTriggerResponding(true);
1689
1690 DoSendCtsAfterRts(muRtsHdr, ctsTxVector, muRtsSnr);
1691}
1692
1693void
1694HeFrameExchangeManager::SendMultiStaBlockAck(const WifiTxParameters& txParams, Time durationId)
1695{
1696 NS_LOG_FUNCTION(this << &txParams << durationId.As(Time::US));
1697
1698 NS_ASSERT(m_apMac);
1699 NS_ASSERT(txParams.m_acknowledgment &&
1700 txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA);
1701 auto acknowledgment = static_cast<WifiUlMuMultiStaBa*>(txParams.m_acknowledgment.get());
1702
1703 NS_ASSERT(!acknowledgment->stationsReceivingMultiStaBa.empty());
1704
1705 CtrlBAckResponseHeader blockAck;
1706 blockAck.SetType(acknowledgment->baType);
1707
1708 Mac48Address receiver;
1709
1710 for (const auto& staInfo : acknowledgment->stationsReceivingMultiStaBa)
1711 {
1712 receiver = staInfo.first.first;
1713 uint8_t tid = staInfo.first.second;
1714 std::size_t index = staInfo.second;
1715
1716 blockAck.SetAid11(m_apMac->GetAssociationId(receiver, m_linkId), index);
1717 blockAck.SetTidInfo(tid, index);
1718
1719 if (tid == 14)
1720 {
1721 // All-ack context
1722 NS_LOG_DEBUG("Multi-STA Block Ack: Sending All-ack to=" << receiver);
1723 blockAck.SetAckType(true, index);
1724 continue;
1725 }
1726
1727 if (acknowledgment->baType.m_bitmapLen.at(index) == 0)
1728 {
1729 // Acknowledgment context
1730 NS_LOG_DEBUG("Multi-STA Block Ack: Sending Ack to=" << receiver);
1731 blockAck.SetAckType(true, index);
1732 }
1733 else
1734 {
1735 // Block acknowledgment context
1736 blockAck.SetAckType(false, index);
1737
1738 auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(receiver, tid);
1739 NS_ASSERT(agreement);
1740 agreement->get().FillBlockAckBitmap(blockAck, index);
1741 NS_LOG_DEBUG("Multi-STA Block Ack: Sending Block Ack with seq="
1742 << blockAck.GetStartingSequence(index) << " to=" << receiver
1743 << " tid=" << +tid);
1744 }
1745 }
1746
1747 WifiMacHeader hdr;
1749 hdr.SetAddr1(acknowledgment->stationsReceivingMultiStaBa.size() == 1
1750 ? receiver
1751 : Mac48Address::GetBroadcast());
1752 hdr.SetAddr2(m_self);
1753 hdr.SetDsNotFrom();
1754 hdr.SetDsNotTo();
1755
1756 Ptr<Packet> packet = Create<Packet>();
1757 packet->AddHeader(blockAck);
1758 Ptr<WifiPsdu> psdu =
1759 GetWifiPsdu(Create<WifiMpdu>(packet, hdr), acknowledgment->multiStaBaTxVector);
1760
1761 Time txDuration = WifiPhy::CalculateTxDuration(GetBlockAckSize(acknowledgment->baType),
1762 acknowledgment->multiStaBaTxVector,
1763 m_phy->GetPhyBand());
1764 /**
1765 * In a BlockAck frame transmitted in response to a frame carried in HE TB PPDU under
1766 * single protection settings, the Duration/ID field is set to the value obtained from
1767 * the Duration/ID field of the frame that elicited the response minus the time, in
1768 * microseconds between the end of the PPDU carrying the frame that elicited the response
1769 * and the end of the PPDU carrying the BlockAck frame.
1770 * Under multiple protection settings, the Duration/ID field in a BlockAck frame transmitted
1771 * in response to a frame carried in HE TB PPDU is set according to the multiple protection
1772 * settings defined in 9.2.5.2. (Sec. 9.2.5.7 of 802.11ax-2021)
1773 */
1774 NS_ASSERT(m_edca);
1775 if (m_edca->GetTxopLimit(m_linkId).IsZero())
1776 {
1777 // single protection settings
1778 psdu->SetDuration(Max(durationId - m_phy->GetSifs() - txDuration, Seconds(0)));
1779 }
1780 else
1781 {
1782 // multiple protection settings
1783 psdu->SetDuration(Max(m_edca->GetRemainingTxop(m_linkId) - txDuration, Seconds(0)));
1784 }
1785
1786 psdu->GetPayload(0)->AddPacketTag(m_muSnrTag);
1787
1788 ForwardPsduDown(psdu, acknowledgment->multiStaBaTxVector);
1789
1790 // continue with the TXOP if time remains
1791 m_psduMap.clear();
1792 m_edca->ResetCw(m_linkId);
1793 m_muSnrTag.Reset();
1794 Simulator::Schedule(txDuration, &HeFrameExchangeManager::TransmissionSucceeded, this);
1795}
1796
1797void
1798HeFrameExchangeManager::ReceiveBasicTrigger(const CtrlTriggerHeader& trigger,
1799 const WifiMacHeader& hdr)
1800{
1801 NS_LOG_FUNCTION(this << trigger << hdr);
1802 NS_ASSERT(trigger.IsBasic());
1803 NS_ASSERT(m_staMac && m_staMac->IsAssociated());
1804
1805 NS_LOG_DEBUG("Received a Trigger Frame (basic variant) soliciting a transmission");
1806
1807 if (!UlMuCsMediumIdle(trigger))
1808 {
1809 return;
1810 }
1811
1812 // Starting from the Preferred AC indicated in the Trigger Frame, check if there
1813 // is either a pending BlockAckReq frame or a data frame that can be transmitted
1814 // in the allocated time and is addressed to a station with which a Block Ack
1815 // agreement has been established.
1816
1817 // create the sequence of TIDs to check
1818 std::vector<uint8_t> tids;
1819 uint16_t staId = m_staMac->GetAssociationId();
1820 AcIndex preferredAc = trigger.FindUserInfoWithAid(staId)->GetPreferredAc();
1821 auto acIt = wifiAcList.find(preferredAc);
1822 for (uint8_t i = 0; i < 4; i++)
1823 {
1824 NS_ASSERT(acIt != wifiAcList.end());
1825 tids.push_back(acIt->second.GetHighTid());
1826 tids.push_back(acIt->second.GetLowTid());
1827
1828 acIt++;
1829 if (acIt == wifiAcList.end())
1830 {
1831 acIt = wifiAcList.begin();
1832 }
1833 }
1834
1835 Ptr<WifiPsdu> psdu;
1836 WifiTxParameters txParams;
1837 WifiTxVector tbTxVector = GetHeTbTxVector(trigger, hdr.GetAddr2());
1838 Time ppduDuration = HePhy::ConvertLSigLengthToHeTbPpduDuration(trigger.GetUlLength(),
1839 tbTxVector,
1840 m_phy->GetPhyBand());
1841
1842 for (const auto& tid : tids)
1843 {
1844 Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
1845
1846 if (!m_mac->GetBaAgreementEstablishedAsOriginator(hdr.GetAddr2(), tid))
1847 {
1848 // no Block Ack agreement established for this TID
1849 continue;
1850 }
1851
1852 txParams.Clear();
1853 txParams.m_txVector = tbTxVector;
1854
1855 // first, check if there is a pending BlockAckReq frame
1856 if (auto mpdu = GetBar(edca->GetAccessCategory(), tid, hdr.GetAddr2());
1857 mpdu && TryAddMpdu(mpdu, txParams, ppduDuration))
1858 {
1859 NS_LOG_DEBUG("Sending a BAR within a TB PPDU");
1860 psdu = Create<WifiPsdu>(mpdu, true);
1861 break;
1862 }
1863
1864 // otherwise, check if a suitable data frame is available
1865 auto receiver =
1866 GetWifiRemoteStationManager()->GetMldAddress(hdr.GetAddr2()).value_or(hdr.GetAddr2());
1867 if (auto mpdu = edca->PeekNextMpdu(m_linkId, tid, receiver))
1868 {
1869 mpdu = CreateAliasIfNeeded(mpdu);
1870 if (auto item = edca->GetNextMpdu(m_linkId, mpdu, txParams, ppduDuration, false))
1871 {
1872 // try A-MPDU aggregation
1873 std::vector<Ptr<WifiMpdu>> mpduList =
1874 m_mpduAggregator->GetNextAmpdu(item, txParams, ppduDuration);
1875 psdu = (mpduList.size() > 1 ? Create<WifiPsdu>(std::move(mpduList))
1876 : Create<WifiPsdu>(item, true));
1877 break;
1878 }
1879 }
1880 }
1881
1882 if (psdu)
1883 {
1884 psdu->SetDuration(hdr.GetDuration() - m_phy->GetSifs() - ppduDuration);
1885 SendPsduMapWithProtection(WifiPsduMap{{staId, psdu}}, txParams);
1886 }
1887 else
1888 {
1889 // send QoS Null frames
1890 SendQosNullFramesInTbPpdu(trigger, hdr);
1891 }
1892}
1893
1894void
1895HeFrameExchangeManager::SendQosNullFramesInTbPpdu(const CtrlTriggerHeader& trigger,
1896 const WifiMacHeader& hdr)
1897{
1898 NS_LOG_FUNCTION(this << trigger << hdr);
1899 NS_ASSERT(trigger.IsBasic() || trigger.IsBsrp());
1900 NS_ASSERT(m_staMac && m_staMac->IsAssociated());
1901
1902 NS_LOG_DEBUG("Requested to send QoS Null frames");
1903
1904 if (!UlMuCsMediumIdle(trigger))
1905 {
1906 return;
1907 }
1908
1909 const auto addr1 =
1910 GetWifiRemoteStationManager()->GetMldAddress(hdr.GetAddr2()).value_or(hdr.GetAddr2());
1912 header.SetAddr1(addr1);
1913 header.SetAddr2(m_mac->GetAddress());
1914 header.SetAddr3(hdr.GetAddr2());
1915 header.SetDsTo();
1916 header.SetDsNotFrom();
1917 // TR3: Sequence numbers for transmitted QoS (+)Null frames may be set
1918 // to any value. (Table 10-3 of 802.11-2016)
1919 header.SetSequenceNumber(0);
1920 // Set the EOSP bit so that NotifyTxToEdca will add the Queue Size
1921 header.SetQosEosp();
1922
1923 WifiTxParameters txParams;
1924 txParams.m_txVector = GetHeTbTxVector(trigger, hdr.GetAddr2());
1925 txParams.m_protection = std::unique_ptr<WifiProtection>(new WifiNoProtection);
1926 txParams.m_acknowledgment = std::unique_ptr<WifiAcknowledgment>(new WifiNoAck);
1927
1928 Time ppduDuration = HePhy::ConvertLSigLengthToHeTbPpduDuration(trigger.GetUlLength(),
1929 txParams.m_txVector,
1930 m_phy->GetPhyBand());
1931 header.SetDuration(hdr.GetDuration() - m_phy->GetSifs() - ppduDuration);
1932
1933 std::vector<Ptr<WifiMpdu>> mpduList;
1934
1935 for (uint8_t tid = 0; tid < 8; ++tid)
1936 {
1937 if (!m_mac->GetBaAgreementEstablishedAsOriginator(hdr.GetAddr2(), tid))
1938 {
1939 NS_LOG_DEBUG("Skipping tid=" << +tid << " because no agreement established");
1940 continue;
1941 }
1942
1943 // We could call TryAddMpdu instead of IsWithinSizeAndTimeLimits below in order to
1944 // get the TX parameters updated automatically. However, aggregating the QoS Null
1945 // frames might fail because MPDU aggregation is disabled by default for VO
1946 // and BK. Therefore, we skip the check on max A-MPDU size and only update the
1947 // TX parameters below.
1948 header.SetQosTid(tid);
1949 auto mpdu = Create<WifiMpdu>(Create<Packet>(), header);
1950 mpdu = CreateAliasIfNeeded(mpdu);
1951 txParams.AddMpdu(mpdu);
1952 UpdateTxDuration(header.GetAddr1(), txParams);
1953
1954 if (!IsWithinSizeAndTimeLimits(txParams.GetSize(header.GetAddr1()),
1955 hdr.GetAddr2(),
1956 txParams,
1957 ppduDuration))
1958 {
1959 txParams.UndoAddMpdu();
1960 break;
1961 }
1962
1963 NS_LOG_DEBUG("Aggregating a QoS Null frame with tid=" << +tid);
1964 txParams.m_acknowledgment = GetAckManager()->TryAddMpdu(mpdu, txParams);
1965 mpduList.push_back(mpdu);
1966 }
1967
1968 if (mpduList.empty())
1969 {
1970 NS_LOG_DEBUG("Not enough time to send a QoS Null frame");
1971 return;
1972 }
1973
1974 Ptr<WifiPsdu> psdu = (mpduList.size() > 1 ? Create<WifiPsdu>(std::move(mpduList))
1975 : Create<WifiPsdu>(mpduList.front(), true));
1976 uint16_t staId = m_staMac->GetAssociationId();
1977 SendPsduMapWithProtection(WifiPsduMap{{staId, psdu}}, txParams);
1978}
1979
1980void
1981HeFrameExchangeManager::ReceiveMuBarTrigger(const CtrlTriggerHeader& trigger,
1982 uint8_t tid,
1983 Time durationId,
1984 double snr)
1985{
1986 NS_LOG_FUNCTION(this << trigger << tid << durationId.As(Time::US) << snr);
1987
1988 auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(m_bssid, tid);
1989
1990 if (!agreement)
1991 {
1992 NS_LOG_DEBUG("There's not a valid agreement for this BlockAckReq");
1993 return;
1994 }
1995
1996 if (!UlMuCsMediumIdle(trigger))
1997 {
1998 return;
1999 }
2000
2001 NS_LOG_DEBUG("Send Block Ack in TB PPDU");
2002 auto txVector = GetHeTbTxVector(trigger, m_bssid);
2003 SendBlockAck(*agreement, durationId, txVector, snr);
2004}
2005
2006bool
2007HeFrameExchangeManager::IsIntraBssPpdu(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) const
2008{
2009 NS_LOG_FUNCTION(this << psdu << txVector);
2010
2011 // "If, based on the MAC address information of a frame carried in a received PPDU, the
2012 // received PPDU satisfies both intra-BSS and inter-BSS conditions, then the received PPDU is
2013 // classified as an intra-BSS PPDU." (Sec. 26.2.2 of 802.11ax-2021)
2014 // Hence, check first if the intra-BSS conditions using MAC address information are satisfied:
2015 // 1. "The PPDU carries a frame that has an RA, TA, or BSSID field value that is equal to
2016 // the BSSID of the BSS in which the STA is associated"
2017 const auto ra = psdu->GetAddr1();
2018 const auto ta = psdu->GetAddr2();
2019 const auto bssid = psdu->GetHeader(0).GetAddr3();
2020 const auto empty = Mac48Address();
2021
2022 if (ra == m_bssid || ta == m_bssid || bssid == m_bssid)
2023 {
2024 return true;
2025 }
2026
2027 // 2. "The PPDU carries a Control frame that does not have a TA field and that has an
2028 // RA field value that matches the saved TXOP holder address of the BSS in which
2029 // the STA is associated"
2030 if (psdu->GetHeader(0).IsCtl() && ta == empty && ra == m_txopHolder)
2031 {
2032 return true;
2033 }
2034
2035 // If we get here, the intra-BSS conditions using MAC address information are not satisfied.
2036 // "If the received PPDU satisfies the intra-BSS conditions using the RXVECTOR parameter
2037 // BSS_COLOR and also satisfies the inter-BSS conditions using MAC address information of a
2038 // frame carried in the PPDU, then the classification made using the MAC address information
2039 // takes precedence."
2040 // Hence, if the inter-BSS conditions using MAC address information are satisfied, the frame
2041 // is classified as inter-BSS
2042 // 1. "The PPDU carries a frame that has a BSSID field, the value of which is not the BSSID
2043 // of the BSS in which the STA is associated"
2044 if (bssid != empty && bssid != m_bssid)
2045 {
2046 return false;
2047 }
2048
2049 // 2. The PPDU carries a frame that does not have a BSSID field but has both an RA field and
2050 // TA field, neither value of which is equal to the BSSID of the BSS in which the STA is
2051 // associated
2052 if (bssid == empty && ta != empty && ra != empty && ta != m_bssid && ra != m_bssid)
2053 {
2054 return false;
2055 }
2056
2057 // If we get here, both intra-BSS and inter-bss conditions using MAC address information
2058 // are not satisfied. Hence, the frame is classified as intra-BSS if the intra-BSS conditions
2059 // using the RXVECTOR parameters are satisfied:
2060 // 1. The RXVECTOR parameter BSS_COLOR of the PPDU carrying the frame is the BSS color of the
2061 // BSS of which the STA is a member
2062 // This condition is used if the BSS is not disabled ("If a STA determines that the BSS color
2063 // is disabled (see 26.17.3.3), then the RXVECTOR parameter BSS_COLOR of a PPDU shall not be
2064 // used to classify the PPDU")
2065 const auto bssColor = m_mac->GetHeConfiguration()->m_bssColor;
2066
2067 // the other two conditions using the RXVECTOR parameter PARTIAL_AID are not implemented
2068 return bssColor != 0 && bssColor == txVector.GetBssColor();
2069}
2070
2071void
2072HeFrameExchangeManager::UpdateNav(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector)
2073{
2074 NS_LOG_FUNCTION(this << psdu << txVector);
2075
2076 if (!psdu->HasNav())
2077 {
2078 return;
2079 }
2080
2081 if (psdu->GetAddr1() == m_self)
2082 {
2083 // When the received frame's RA is equal to the STA's own MAC address, the STA
2084 // shall not update its NAV (IEEE 802.11-2020, sec. 10.3.2.4)
2085 return;
2086 }
2087
2088 // The intra-BSS NAV is updated by an intra-BSS PPDU. The basic NAV is updated by an
2089 // inter-BSS PPDU or a PPDU that cannot be classified as intra-BSS or inter-BSS.
2090 // (Section 26.2.4 of 802.11ax-2021)
2091 if (!IsIntraBssPpdu(psdu, txVector))
2092 {
2093 NS_LOG_DEBUG("PPDU not classified as intra-BSS, update the basic NAV");
2094 VhtFrameExchangeManager::UpdateNav(psdu, txVector);
2095 return;
2096 }
2097
2098 NS_LOG_DEBUG("PPDU classified as intra-BSS, update the intra-BSS NAV");
2099 Time duration = psdu->GetDuration();
2100 NS_LOG_DEBUG("Duration/ID=" << duration);
2101
2102 if (psdu->GetHeader(0).IsCfEnd())
2103 {
2104 // An HE STA that maintains two NAVs (see 26.2.4) and receives a CF-End frame should reset
2105 // the basic NAV if the received CF-End frame is carried in an inter-BSS PPDU and reset the
2106 // intra-BSS NAV if the received CF-End frame is carried in an intra-BSS PPDU. (Sec. 26.2.5
2107 // of 802.11ax-2021)
2108 NS_LOG_DEBUG("Received CF-End, resetting the intra-BSS NAV");
2109 IntraBssNavResetTimeout();
2110 return;
2111 }
2112
2113 // For all other received frames the STA shall update its NAV when the received
2114 // Duration is greater than the STA's current NAV value (IEEE 802.11-2020 sec. 10.3.2.4)
2115 auto intraBssNavEnd = Simulator::Now() + duration;
2116 if (intraBssNavEnd > m_intraBssNavEnd)
2117 {
2118 m_intraBssNavEnd = intraBssNavEnd;
2119 NS_LOG_DEBUG("Updated intra-BSS NAV=" << m_intraBssNavEnd);
2120
2121 // A STA that used information from an RTS frame as the most recent basis to update
2122 // its NAV setting is permitted to reset its NAV if no PHY-RXSTART.indication
2123 // primitive is received from the PHY during a NAVTimeout period starting when the
2124 // MAC receives a PHY-RXEND.indication primitive corresponding to the detection of
2125 // the RTS frame. NAVTimeout period is equal to:
2126 // (2 x aSIFSTime) + (CTS_Time) + aRxPHYStartDelay + (2 x aSlotTime)
2127 // The “CTS_Time” shall be calculated using the length of the CTS frame and the data
2128 // rate at which the RTS frame used for the most recent NAV update was received
2129 // (IEEE 802.11-2016 sec. 10.3.2.4)
2130 if (psdu->GetHeader(0).IsRts())
2131 {
2132 WifiTxVector ctsTxVector =
2133 GetWifiRemoteStationManager()->GetCtsTxVector(psdu->GetAddr2(), txVector.GetMode());
2134 auto navResetDelay =
2135 2 * m_phy->GetSifs() +
2136 WifiPhy::CalculateTxDuration(GetCtsSize(), ctsTxVector, m_phy->GetPhyBand()) +
2137 WifiPhy::CalculatePhyPreambleAndHeaderDuration(ctsTxVector) + 2 * m_phy->GetSlot();
2138 m_intraBssNavResetEvent =
2139 Simulator::Schedule(navResetDelay,
2140 &HeFrameExchangeManager::IntraBssNavResetTimeout,
2141 this);
2142 }
2143 }
2144 NS_LOG_DEBUG("Current intra-BSS NAV=" << m_intraBssNavEnd);
2145
2146 m_channelAccessManager->NotifyNavStartNow(duration);
2147}
2148
2149void
2150HeFrameExchangeManager::ClearTxopHolderIfNeeded()
2151{
2152 NS_LOG_FUNCTION(this);
2153 if (m_intraBssNavEnd <= Simulator::Now())
2154 {
2155 m_txopHolder.reset();
2156 }
2157}
2158
2159void
2160HeFrameExchangeManager::NavResetTimeout()
2161{
2162 NS_LOG_FUNCTION(this);
2163 m_navEnd = Simulator::Now();
2164 // Do not reset the TXOP holder because the basic NAV is updated by inter-BSS frames
2165 // The NAV seen by the ChannelAccessManager is now the intra-BSS NAV only
2166 Time intraBssNav = Simulator::GetDelayLeft(m_intraBssNavResetEvent);
2167 m_channelAccessManager->NotifyNavResetNow(intraBssNav);
2168}
2169
2170void
2171HeFrameExchangeManager::IntraBssNavResetTimeout()
2172{
2173 NS_LOG_FUNCTION(this);
2174 m_intraBssNavEnd = Simulator::Now();
2175 ClearTxopHolderIfNeeded();
2176 // The NAV seen by the ChannelAccessManager is now the basic NAV only
2177 Time basicNav = Simulator::GetDelayLeft(m_navResetEvent);
2178 m_channelAccessManager->NotifyNavResetNow(basicNav);
2179}
2180
2181std::optional<Mac48Address>
2182HeFrameExchangeManager::FindTxopHolder(const WifiMacHeader& hdr, const WifiTxVector& txVector)
2183{
2184 NS_LOG_FUNCTION(this << hdr << txVector);
2185
2186 if (hdr.IsTrigger() && hdr.GetAddr2() == m_bssid)
2187 {
2188 return m_bssid;
2189 }
2190 if (!txVector.IsUlMu()) // the sender of a TB PPDU is not the TXOP holder
2191 {
2192 return VhtFrameExchangeManager::FindTxopHolder(hdr, txVector);
2193 }
2194 return std::nullopt;
2195}
2196
2197bool
2198HeFrameExchangeManager::VirtualCsMediumIdle() const
2199{
2200 // For an HE STA maintaining two NAVs, if both the NAV timers are 0, the virtual CS indication
2201 // is that the medium is idle; if at least one of the two NAV timers is nonzero, the virtual CS
2202 // indication is that the medium is busy. (Sec. 26.2.4 of 802.11ax-2021)
2203 return m_navEnd <= Simulator::Now() && m_intraBssNavEnd <= Simulator::Now();
2204}
2205
2206bool
2207HeFrameExchangeManager::UlMuCsMediumIdle(const CtrlTriggerHeader& trigger) const
2208{
2209 if (!trigger.GetCsRequired())
2210 {
2211 NS_LOG_DEBUG("CS not required");
2212 return true;
2213 }
2214
2215 // A non-AP STA does not consider the intra-BSS NAV in determining whether to respond to a
2216 // Trigger frame sent by the AP with which the non-AP STA is associated.
2217 // A non-AP STA considers the basic NAV in determining whether to respond to a Trigger frame
2218 // sent by the AP with which the non-AP STA is associated. (Sec. 26.5.2.5 of 802.11ax-2021)
2219 const Time now = Simulator::Now();
2220 if (m_navEnd > now)
2221 {
2222 NS_LOG_DEBUG("Basic NAV indicates medium busy");
2223 return false;
2224 }
2225
2226 NS_ASSERT_MSG(m_staMac, "UL MU CS is only performed by non-AP STAs");
2227 const auto userInfoIt = trigger.FindUserInfoWithAid(m_staMac->GetAssociationId());
2228 NS_ASSERT_MSG(userInfoIt != trigger.end(),
2229 "No User Info field for STA (" << m_self
2230 << ") AID=" << m_staMac->GetAssociationId());
2231
2232 std::set<uint8_t> indices;
2233
2234 if (trigger.IsMuRts())
2235 {
2236 auto ctsTxVector = GetCtsTxVectorAfterMuRts(trigger, m_staMac->GetAssociationId());
2237 auto bw = ctsTxVector.GetChannelWidth();
2238 indices = m_phy->GetOperatingChannel().GetAll20MHzChannelIndicesInPrimary(bw);
2239 }
2240 else
2241 {
2242 indices =
2243 m_phy->GetOperatingChannel().Get20MHzIndicesCoveringRu(userInfoIt->GetRuAllocation(),
2244 trigger.GetUlBandwidth());
2245 }
2246 return !m_channelAccessManager->GetPer20MHzBusy(indices);
2247}
2248
2249void
2250HeFrameExchangeManager::ReceiveMpdu(Ptr<const WifiMpdu> mpdu,
2251 RxSignalInfo rxSignalInfo,
2252 const WifiTxVector& txVector,
2253 bool inAmpdu)
2254{
2255 NS_LOG_FUNCTION(this << *mpdu << rxSignalInfo << txVector << inAmpdu);
2256
2257 // The received MPDU is either broadcast or addressed to this station
2258 NS_ASSERT(mpdu->GetHeader().GetAddr1().IsGroup() || mpdu->GetHeader().GetAddr1() == m_self);
2259
2260 const WifiMacHeader& hdr = mpdu->GetHeader();
2261
2262 if (txVector.IsUlMu() && m_txTimer.IsRunning() &&
2263 m_txTimer.GetReason() == WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF)
2264 {
2265 Mac48Address sender = hdr.GetAddr2();
2266 NS_ASSERT(m_txParams.m_acknowledgment &&
2267 m_txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA);
2268 auto acknowledgment = static_cast<WifiUlMuMultiStaBa*>(m_txParams.m_acknowledgment.get());
2269 std::size_t index = acknowledgment->baType.m_bitmapLen.size();
2270
2271 if (!m_txTimer.GetStasExpectedToRespond().contains(sender))
2272 {
2273 NS_LOG_WARN("Received a TB PPDU from an unexpected station: " << sender);
2274 return;
2275 }
2276
2277 if (hdr.IsBlockAckReq())
2278 {
2279 NS_LOG_DEBUG("Received a BlockAckReq in a TB PPDU from " << sender);
2280
2281 CtrlBAckRequestHeader blockAckReq;
2282 mpdu->GetPacket()->PeekHeader(blockAckReq);
2283 NS_ABORT_MSG_IF(blockAckReq.IsMultiTid(), "Multi-TID BlockAckReq not supported");
2284 uint8_t tid = blockAckReq.GetTidInfo();
2285 GetBaManager(tid)->NotifyGotBlockAckRequest(
2286 m_mac->GetMldAddress(sender).value_or(sender),
2287 tid,
2288 blockAckReq.GetStartingSequence());
2289
2290 // Block Acknowledgment context
2291 acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, tid), index);
2292 acknowledgment->baType.m_bitmapLen.push_back(
2293 m_mac->GetBaTypeAsRecipient(sender, tid).m_bitmapLen.at(0));
2294 uint16_t staId = txVector.GetHeMuUserInfoMap().begin()->first;
2295 m_muSnrTag.Set(staId, rxSignalInfo.snr);
2296 }
2297 else if (hdr.IsQosData() && !inAmpdu && hdr.GetQosAckPolicy() == WifiMacHeader::NORMAL_ACK)
2298 {
2299 NS_LOG_DEBUG("Received an S-MPDU in a TB PPDU from " << sender << " (" << *mpdu << ")");
2300
2301 uint8_t tid = hdr.GetQosTid();
2302 GetBaManager(tid)->NotifyGotMpdu(mpdu);
2303
2304 // Acknowledgment context of Multi-STA Block Acks
2305 acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, tid), index);
2306 acknowledgment->baType.m_bitmapLen.push_back(0);
2307 uint16_t staId = txVector.GetHeMuUserInfoMap().begin()->first;
2308 m_muSnrTag.Set(staId, rxSignalInfo.snr);
2309 }
2310 else if (!(hdr.IsQosData() && !hdr.HasData() && !inAmpdu))
2311 {
2312 // The other case handled by this function is when we receive a QoS Null frame
2313 // that is not in an A-MPDU. For all other cases, the reception is handled by
2314 // parent classes. In particular, in case of a QoS data frame in A-MPDU, we
2315 // have to wait until the A-MPDU reception is completed, but we let the
2316 // parent classes notify the Block Ack agreement of the reception of this MPDU
2317 VhtFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
2318 return;
2319 }
2320
2321 // Schedule the transmission of a Multi-STA BlockAck frame if needed
2322 if (!acknowledgment->stationsReceivingMultiStaBa.empty() && !m_multiStaBaEvent.IsPending())
2323 {
2324 m_multiStaBaEvent = Simulator::Schedule(m_phy->GetSifs(),
2325 &HeFrameExchangeManager::SendMultiStaBlockAck,
2326 this,
2327 std::cref(m_txParams),
2328 mpdu->GetHeader().GetDuration());
2329 }
2330
2331 // remove the sender from the set of stations that are expected to send a TB PPDU
2332 m_txTimer.GotResponseFrom(sender);
2333
2334 if (m_txTimer.GetStasExpectedToRespond().empty())
2335 {
2336 // we do not expect any other BlockAck frame
2337 m_txTimer.Cancel();
2338 m_channelAccessManager->NotifyAckTimeoutResetNow();
2339
2340 if (!m_multiStaBaEvent.IsPending())
2341 {
2342 // all of the stations that replied with a TB PPDU sent QoS Null frames.
2343 NS_LOG_DEBUG("Continue the TXOP");
2344 m_psduMap.clear();
2345 m_edca->ResetCw(m_linkId);
2346 TransmissionSucceeded();
2347 }
2348 }
2349
2350 // the received TB PPDU has been processed
2351 return;
2352 }
2353
2354 if (txVector.IsUlMu() && m_txTimer.IsRunning() &&
2355 m_txTimer.GetReason() == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF &&
2356 !inAmpdu) // if in A-MPDU, processing is done at the end of A-MPDU reception
2357 {
2358 const auto& sender = hdr.GetAddr2();
2359
2360 if (!m_txTimer.GetStasExpectedToRespond().contains(sender))
2361 {
2362 NS_LOG_WARN("Received a TB PPDU from an unexpected station: " << sender);
2363 return;
2364 }
2365 if (!(hdr.IsQosData() && !hdr.HasData()))
2366 {
2367 NS_LOG_WARN("No QoS Null frame in the received MPDU");
2368 return;
2369 }
2370
2371 NS_LOG_DEBUG("Received a QoS Null frame in a TB PPDU from " << sender);
2372 ReceivedQosNullAfterBsrpTf(sender);
2373
2374 // the received TB PPDU has been processed
2375 return;
2376 }
2377
2378 if (hdr.IsCtl())
2379 {
2380 if (hdr.IsCts() && m_txTimer.IsRunning() &&
2381 m_txTimer.GetReason() == WifiTxTimer::WAIT_CTS && m_psduMap.size() == 1)
2382 {
2383 NS_ABORT_MSG_IF(inAmpdu, "Received CTS as part of an A-MPDU");
2384 NS_ASSERT(hdr.GetAddr1() == m_self);
2385
2386 Mac48Address sender = m_psduMap.begin()->second->GetAddr1();
2387 NS_LOG_DEBUG("Received CTS from=" << sender);
2388
2389 SnrTag tag;
2390 mpdu->GetPacket()->PeekPacketTag(tag);
2391 GetWifiRemoteStationManager()->ReportRxOk(sender, rxSignalInfo, txVector);
2392 GetWifiRemoteStationManager()->ReportRtsOk(m_psduMap.begin()->second->GetHeader(0),
2393 rxSignalInfo.snr,
2394 txVector.GetMode(),
2395 tag.Get());
2396
2397 m_txTimer.Cancel();
2398 m_channelAccessManager->NotifyCtsTimeoutResetNow();
2399 ProtectionCompleted();
2400 }
2401 else if (hdr.IsCts() && m_txTimer.IsRunning() &&
2402 m_txTimer.GetReason() == WifiTxTimer::WAIT_CTS_AFTER_MU_RTS)
2403 {
2404 NS_ABORT_MSG_IF(inAmpdu, "Received CTS as part of an A-MPDU");
2405 NS_ASSERT(hdr.GetAddr1() == m_self);
2406
2407 NS_LOG_DEBUG("Received a CTS frame in response to an MU-RTS");
2408
2409 m_txTimer.Cancel();
2410 m_channelAccessManager->NotifyCtsTimeoutResetNow();
2411 ProtectionCompleted();
2412 }
2413 else if (hdr.IsAck() && m_txTimer.IsRunning() &&
2414 m_txTimer.GetReason() == WifiTxTimer::WAIT_NORMAL_ACK_AFTER_DL_MU_PPDU)
2415 {
2416 NS_ASSERT(hdr.GetAddr1() == m_self);
2417 NS_ASSERT(m_txParams.m_acknowledgment);
2418 NS_ASSERT(m_txParams.m_acknowledgment->method ==
2419 WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE);
2420
2421 auto acknowledgment =
2422 static_cast<WifiDlMuBarBaSequence*>(m_txParams.m_acknowledgment.get());
2423 NS_ASSERT(acknowledgment->stationsReplyingWithNormalAck.size() == 1);
2424 NS_ASSERT(m_apMac);
2425 uint16_t staId = m_apMac->GetAssociationId(
2426 acknowledgment->stationsReplyingWithNormalAck.begin()->first,
2427 m_linkId);
2428 auto it = m_psduMap.find(staId);
2429 NS_ASSERT(it != m_psduMap.end());
2430 NS_ASSERT(it->second->GetAddr1() ==
2431 acknowledgment->stationsReplyingWithNormalAck.begin()->first);
2432 SnrTag tag;
2433 mpdu->GetPacket()->PeekPacketTag(tag);
2434 ReceivedNormalAck(*it->second->begin(),
2435 m_txParams.m_txVector,
2436 txVector,
2437 rxSignalInfo,
2438 tag.Get());
2439 m_psduMap.clear();
2440 }
2441 // TODO the PHY should not pass us a non-TB PPDU if we are waiting for a
2442 // TB PPDU. However, processing the PHY header is done by the PHY entity
2443 // corresponding to the modulation class of the PPDU being received, hence
2444 // it is not possible to check if a valid TRIGVECTOR is stored when receiving
2445 // PPDUs of older modulation classes. Therefore, we check here that we are
2446 // actually receiving a TB PPDU.
2447 else if (hdr.IsBlockAck() && txVector.IsUlMu() && m_txTimer.IsRunning() &&
2448 m_txTimer.GetReason() == WifiTxTimer::WAIT_BLOCK_ACKS_IN_TB_PPDU)
2449 {
2450 Mac48Address sender = hdr.GetAddr2();
2451 NS_LOG_DEBUG("Received BlockAck in TB PPDU from=" << sender);
2452
2453 SnrTag tag;
2454 mpdu->GetPacket()->PeekPacketTag(tag);
2455
2456 // notify the Block Ack Manager
2457 CtrlBAckResponseHeader blockAck;
2458 mpdu->GetPacket()->PeekHeader(blockAck);
2459 uint8_t tid = blockAck.GetTidInfo();
2460 std::pair<uint16_t, uint16_t> ret =
2461 GetBaManager(tid)->NotifyGotBlockAck(m_linkId,
2462 blockAck,
2463 m_mac->GetMldAddress(sender).value_or(sender),
2464 {tid});
2465 GetWifiRemoteStationManager()->ReportAmpduTxStatus(sender,
2466 ret.first,
2467 ret.second,
2468 rxSignalInfo.snr,
2469 tag.Get(),
2470 m_txParams.m_txVector);
2471
2472 // remove the sender from the set of stations that are expected to send a BlockAck
2473 if (!m_txTimer.GetStasExpectedToRespond().contains(sender))
2474 {
2475 NS_LOG_WARN("Received a BlockAck from an unexpected stations: " << sender);
2476 return;
2477 }
2478
2479 m_txTimer.GotResponseFrom(sender);
2480
2481 if (m_txTimer.GetStasExpectedToRespond().empty())
2482 {
2483 // we do not expect any other BlockAck frame
2484 m_txTimer.Cancel();
2485 m_channelAccessManager->NotifyAckTimeoutResetNow();
2486 if (m_triggerFrame)
2487 {
2488 // this is strictly needed for DL_MU_TF_MU_BAR only
2489 m_triggerFrame = nullptr;
2490 }
2491
2492 m_edca->ResetCw(m_linkId);
2493 m_psduMap.clear();
2494 TransmissionSucceeded();
2495 }
2496 }
2497 else if (hdr.IsBlockAck() && m_txTimer.IsRunning() &&
2498 m_txTimer.GetReason() == WifiTxTimer::WAIT_BLOCK_ACK_AFTER_TB_PPDU)
2499 {
2500 CtrlBAckResponseHeader blockAck;
2501 mpdu->GetPacket()->PeekHeader(blockAck);
2502
2503 NS_ABORT_MSG_IF(!blockAck.IsMultiSta(),
2504 "A Multi-STA BlockAck is expected after a TB PPDU");
2505 NS_LOG_DEBUG("Received a Multi-STA BlockAck from=" << hdr.GetAddr2());
2506 m_txTimer.GotResponseFrom(hdr.GetAddr2());
2507
2508 NS_ASSERT(m_staMac && m_staMac->IsAssociated());
2509 if (hdr.GetAddr2() != m_bssid)
2510 {
2511 NS_LOG_DEBUG("The sender is not the AP we are associated with");
2512 return;
2513 }
2514
2515 uint16_t staId = m_staMac->GetAssociationId();
2516 std::vector<uint32_t> indices = blockAck.FindPerAidTidInfoWithAid(staId);
2517
2518 if (indices.empty())
2519 {
2520 NS_LOG_DEBUG("No Per AID TID Info subfield intended for me");
2521 return;
2522 }
2523
2524 MuSnrTag tag;
2525 mpdu->GetPacket()->PeekPacketTag(tag);
2526
2527 // notify the Block Ack Manager
2528 for (const auto& index : indices)
2529 {
2530 uint8_t tid = blockAck.GetTidInfo(index);
2531
2532 if (blockAck.GetAckType(index) && tid < 8)
2533 {
2534 // Acknowledgment context
2535 NS_ABORT_IF(m_psduMap.empty() || m_psduMap.begin()->first != staId);
2536 GetBaManager(tid)->NotifyGotAck(m_linkId, *m_psduMap.at(staId)->begin());
2537 }
2538 else
2539 {
2540 // Block Acknowledgment or All-ack context
2541 if (blockAck.GetAckType(index) && tid == 14)
2542 {
2543 // All-ack context, we need to determine the actual TID(s) of the PSDU
2544 NS_ASSERT(indices.size() == 1);
2545 NS_ABORT_IF(m_psduMap.empty() || m_psduMap.begin()->first != staId);
2546 std::set<uint8_t> tids = m_psduMap.at(staId)->GetTids();
2547 NS_ABORT_MSG_IF(tids.size() > 1, "Multi-TID A-MPDUs not supported yet");
2548 tid = *tids.begin();
2549 }
2550
2551 std::pair<uint16_t, uint16_t> ret = GetBaManager(tid)->NotifyGotBlockAck(
2552 m_linkId,
2553 blockAck,
2554 m_mac->GetMldAddress(hdr.GetAddr2()).value_or(hdr.GetAddr2()),
2555 {tid},
2556 index);
2557 GetWifiRemoteStationManager()->ReportAmpduTxStatus(hdr.GetAddr2(),
2558 ret.first,
2559 ret.second,
2560 rxSignalInfo.snr,
2561 tag.Get(staId),
2562 m_txParams.m_txVector);
2563 }
2564
2565 if (m_psduMap.at(staId)->GetHeader(0).IsQosData() &&
2566 (blockAck.GetAckType(index) // Ack or All-ack context
2567 || std::any_of(blockAck.GetBitmap(index).begin(),
2568 blockAck.GetBitmap(index).end(),
2569 [](uint8_t b) { return b != 0; })))
2570 {
2571 NS_ASSERT(m_psduMap.at(staId)->GetHeader(0).HasData());
2572 NS_ASSERT(m_psduMap.at(staId)->GetHeader(0).GetQosTid() == tid);
2573 // the station has received a response from the AP for the HE TB PPDU
2574 // transmitted in response to a Basic Trigger Frame and at least one
2575 // MPDU was acknowledged. Therefore, it needs to update the access
2576 // parameters if it received an MU EDCA Parameter Set element.
2577 m_mac->GetQosTxop(tid)->StartMuEdcaTimerNow(m_linkId);
2578 }
2579 }
2580
2581 // cancel the timer
2582 m_txTimer.Cancel();
2583 m_channelAccessManager->NotifyAckTimeoutResetNow();
2584 // dequeue BlockAckReq frames included in acknowledged TB PPDUs (if any)
2585 for (const auto& [staId, psdu] : m_psduMap)
2586 {
2587 if (psdu->GetNMpdus() == 1 && psdu->GetHeader(0).IsBlockAckReq())
2588 {
2589 DequeuePsdu(psdu);
2590 }
2591 }
2592 m_psduMap.clear();
2593 }
2594 else if (hdr.IsBlockAck() && m_txTimer.IsRunning() &&
2595 m_txTimer.GetReason() == WifiTxTimer::WAIT_BLOCK_ACK)
2596 {
2597 // this BlockAck frame may have been sent in response to a DL MU PPDU with
2598 // acknowledgment in SU format or one of the consequent BlockAckReq frames.
2599 // We clear the PSDU map and let parent classes continue processing this frame.
2600 m_psduMap.clear();
2601 VhtFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
2602 }
2603 else if (hdr.IsTrigger())
2604 {
2605 // Trigger Frames are only processed by STAs
2606 if (!m_staMac)
2607 {
2608 return;
2609 }
2610
2611 // A Trigger Frame in an A-MPDU is processed when the A-MPDU is fully received
2612 if (inAmpdu)
2613 {
2614 m_triggerFrameInAmpdu = true;
2615 return;
2616 }
2617
2618 CtrlTriggerHeader trigger;
2619 mpdu->GetPacket()->PeekHeader(trigger);
2620
2621 if (hdr.GetAddr1() != m_self &&
2622 (!hdr.GetAddr1().IsBroadcast() || !m_staMac->IsAssociated() ||
2623 hdr.GetAddr2() != m_bssid // not sent by the AP this STA is associated with
2624 || trigger.FindUserInfoWithAid(m_staMac->GetAssociationId()) == trigger.end()))
2625 {
2626 // not addressed to us
2627 return;
2628 }
2629
2630 uint16_t staId = m_staMac->GetAssociationId();
2631
2632 if (trigger.IsMuRts())
2633 {
2634 Mac48Address sender = hdr.GetAddr2();
2635 NS_LOG_DEBUG("Received MU-RTS Trigger Frame from=" << sender);
2636 GetWifiRemoteStationManager()->ReportRxOk(sender, rxSignalInfo, txVector);
2637
2638 // If a non-AP STA receives an MU-RTS Trigger frame, the non-AP STA shall commence
2639 // the transmission of a CTS frame response at the SIFS time boundary after
2640 // the end of a received PPDU when all the following conditions are met:
2641 // - The MU-RTS Trigger frame has one of the User Info fields addressed to
2642 // the non-AP STA (this is guaranteed if we get here)
2643 // - The UL MU CS condition indicates that the medium is idle
2644 // (Sec. 26.2.6.3 of 802.11ax-2021)
2645 NS_LOG_DEBUG("Schedule CTS");
2646 m_sendCtsEvent = Simulator::Schedule(m_phy->GetSifs(),
2647 &HeFrameExchangeManager::SendCtsAfterMuRts,
2648 this,
2649 hdr,
2650 trigger,
2651 rxSignalInfo.snr);
2652 }
2653 else if (trigger.IsMuBar())
2654 {
2655 Mac48Address sender = hdr.GetAddr2();
2656 NS_LOG_DEBUG("Received MU-BAR Trigger Frame from=" << sender);
2657 GetWifiRemoteStationManager()->ReportRxOk(sender, rxSignalInfo, txVector);
2658
2659 auto userInfoIt = trigger.FindUserInfoWithAid(staId);
2660 NS_ASSERT(userInfoIt != trigger.end());
2661 CtrlBAckRequestHeader blockAckReq = userInfoIt->GetMuBarTriggerDepUserInfo();
2662 NS_ABORT_MSG_IF(blockAckReq.IsMultiTid(), "Multi-TID BlockAckReq not supported");
2663 uint8_t tid = blockAckReq.GetTidInfo();
2664
2665 GetBaManager(tid)->NotifyGotBlockAckRequest(
2666 m_mac->GetMldAddress(sender).value_or(sender),
2667 tid,
2668 blockAckReq.GetStartingSequence());
2669
2670 Simulator::Schedule(m_phy->GetSifs(),
2671 &HeFrameExchangeManager::ReceiveMuBarTrigger,
2672 this,
2673 trigger,
2674 tid,
2675 hdr.GetDuration(),
2676 rxSignalInfo.snr);
2677 }
2678 else if (trigger.IsBasic())
2679 {
2680 Simulator::Schedule(m_phy->GetSifs(),
2681 &HeFrameExchangeManager::ReceiveBasicTrigger,
2682 this,
2683 trigger,
2684 hdr);
2685 }
2686 else if (trigger.IsBsrp())
2687 {
2688 Simulator::Schedule(m_phy->GetSifs(),
2689 &HeFrameExchangeManager::SendQosNullFramesInTbPpdu,
2690 this,
2691 trigger,
2692 hdr);
2693 }
2694 }
2695 else
2696 {
2697 // the received control frame cannot be handled here
2698 VhtFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
2699 }
2700
2701 // the received control frame has been processed
2702 return;
2703 }
2704
2705 // the received frame cannot be handled here
2706 VhtFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
2707 ;
2708}
2709
2710void
2711HeFrameExchangeManager::EndReceiveAmpdu(Ptr<const WifiPsdu> psdu,
2712 const RxSignalInfo& rxSignalInfo,
2713 const WifiTxVector& txVector,
2714 const std::vector<bool>& perMpduStatus)
2715{
2716 std::set<uint8_t> tids = psdu->GetTids();
2717
2718 if (txVector.IsUlMu() && m_txTimer.IsRunning() &&
2719 m_txTimer.GetReason() == WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF)
2720 {
2721 Mac48Address sender = psdu->GetAddr2();
2722 NS_ASSERT(m_txParams.m_acknowledgment &&
2723 m_txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA);
2724 auto acknowledgment = static_cast<WifiUlMuMultiStaBa*>(m_txParams.m_acknowledgment.get());
2725 std::size_t index = acknowledgment->baType.m_bitmapLen.size();
2726
2727 if (!m_txTimer.GetStasExpectedToRespond().contains(sender))
2728 {
2729 NS_LOG_WARN("Received a TB PPDU from an unexpected station: " << sender);
2730 return;
2731 }
2732
2733 NS_LOG_DEBUG("Received an A-MPDU in a TB PPDU from " << sender << " (" << *psdu << ")");
2734
2735 if (std::any_of(tids.begin(), tids.end(), [&psdu](uint8_t tid) {
2736 return psdu->GetAckPolicyForTid(tid) == WifiMacHeader::NORMAL_ACK;
2737 }))
2738 {
2739 if (std::all_of(perMpduStatus.cbegin(), perMpduStatus.cend(), [](bool v) { return v; }))
2740 {
2741 // All-ack context
2742 acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, 14),
2743 index);
2744 acknowledgment->baType.m_bitmapLen.push_back(0);
2745 }
2746 else
2747 {
2748 // Block Acknowledgment context
2749 std::size_t i = 0;
2750 for (const auto& tid : tids)
2751 {
2752 acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, tid),
2753 index + i++);
2754 acknowledgment->baType.m_bitmapLen.push_back(
2755 m_mac->GetBaTypeAsRecipient(sender, tid).m_bitmapLen.at(0));
2756 }
2757 }
2758 uint16_t staId = txVector.GetHeMuUserInfoMap().begin()->first;
2759 m_muSnrTag.Set(staId, rxSignalInfo.snr);
2760 }
2761
2762 // Schedule the transmission of a Multi-STA BlockAck frame if needed
2763 if (!acknowledgment->stationsReceivingMultiStaBa.empty() && !m_multiStaBaEvent.IsPending())
2764 {
2765 m_multiStaBaEvent = Simulator::Schedule(m_phy->GetSifs(),
2766 &HeFrameExchangeManager::SendMultiStaBlockAck,
2767 this,
2768 std::cref(m_txParams),
2769 psdu->GetDuration());
2770 }
2771
2772 // remove the sender from the set of stations that are expected to send a TB PPDU
2773 m_txTimer.GotResponseFrom(sender);
2774
2775 if (m_txTimer.GetStasExpectedToRespond().empty())
2776 {
2777 // we do not expect any other BlockAck frame
2778 m_txTimer.Cancel();
2779 m_channelAccessManager->NotifyAckTimeoutResetNow();
2780
2781 if (!m_multiStaBaEvent.IsPending())
2782 {
2783 // all of the stations that replied with a TB PPDU sent QoS Null frames.
2784 NS_LOG_DEBUG("Continue the TXOP");
2785 m_psduMap.clear();
2786 m_edca->ResetCw(m_linkId);
2787 TransmissionSucceeded();
2788 }
2789 }
2790
2791 // the received TB PPDU has been processed
2792 return;
2793 }
2794
2795 if (txVector.IsUlMu() && m_txTimer.IsRunning() &&
2796 m_txTimer.GetReason() == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF)
2797 {
2798 Mac48Address sender = psdu->GetAddr2();
2799
2800 if (!m_txTimer.GetStasExpectedToRespond().contains(sender))
2801 {
2802 NS_LOG_WARN("Received a TB PPDU from an unexpected station: " << sender);
2803 return;
2804 }
2805 if (std::none_of(psdu->begin(), psdu->end(), [](Ptr<WifiMpdu> mpdu) {
2806 return mpdu->GetHeader().IsQosData() && !mpdu->GetHeader().HasData();
2807 }))
2808 {
2809 NS_LOG_WARN("No QoS Null frame in the received PSDU");
2810 return;
2811 }
2812
2813 NS_LOG_DEBUG("Received QoS Null frames in a TB PPDU from " << sender);
2814 ReceivedQosNullAfterBsrpTf(sender);
2815
2816 // the received TB PPDU has been processed
2817 return;
2818 }
2819
2820 if (m_triggerFrameInAmpdu)
2821 {
2822 // the received A-MPDU contains a Trigger Frame. It is now time to handle it.
2823 auto psduIt = psdu->begin();
2824 while (psduIt != psdu->end())
2825 {
2826 if ((*psduIt)->GetHeader().IsTrigger())
2827 {
2828 ReceiveMpdu(*psduIt, rxSignalInfo, txVector, false);
2829 }
2830 psduIt++;
2831 }
2832
2833 m_triggerFrameInAmpdu = false;
2834 return;
2835 }
2836
2837 // the received frame cannot be handled here
2838 VhtFrameExchangeManager::EndReceiveAmpdu(psdu, rxSignalInfo, txVector, perMpduStatus);
2839}
2840
2841void
2842HeFrameExchangeManager::ReceivedQosNullAfterBsrpTf(Mac48Address sender)
2843{
2844 NS_LOG_FUNCTION(this << sender);
2845
2846 NS_ASSERT(m_txTimer.IsRunning() &&
2847 m_txTimer.GetReason() == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF);
2848
2849 // remove the sender from the set of stations that are expected to send a TB PPDU
2850 m_txTimer.GotResponseFrom(sender);
2851
2852 if (m_txTimer.GetStasExpectedToRespond().empty())
2853 {
2854 // we do not expect any other response
2855 m_channelAccessManager->NotifyAckTimeoutResetNow();
2856
2857 NS_ASSERT(m_edca);
2858 m_psduMap.clear();
2859 m_edca->ResetCw(m_linkId);
2860 TransmissionSucceeded();
2861 // we reset the TX timer after calling TransmissionSucceeded, so that the latter can
2862 // check whether the reason for the last timer is WAIT_QOS_NULL_AFTER_BSRP_TF
2863 m_txTimer.Cancel();
2864 }
2865}
2866
2867} // namespace ns3
#define Max(a, b)
AttributeValue implementation for Boolean.
Definition boolean.h:26
Headers for BlockAckRequest.
uint16_t GetStartingSequence() const
Return the starting sequence number.
uint8_t GetTidInfo() const
Return the Traffic ID (TID).
Headers for BlockAck response.
std::vector< uint32_t > FindPerAidTidInfoWithAid(uint16_t aid) const
For Multi-STA Block Acks, get the indices of the Per AID TID Info subfields carrying the given AID in...
uint16_t GetStartingSequence(std::size_t index=0) const
For Block Ack variants other than Multi-STA Block Ack, get the starting sequence number.
uint8_t GetTidInfo(std::size_t index=0) const
For Block Ack variants other than Multi-STA Block Ack, get the TID_INFO subfield of the BA Control fi...
const std::vector< uint8_t > & GetBitmap(std::size_t index=0) const
Return a const reference to the bitmap from the BlockAck response header.
void SetAckType(bool type, std::size_t index)
For Multi-STA Block Acks, set the Ack Type subfield of the Per AID TID Info subfield identified by th...
void SetTidInfo(uint8_t tid, std::size_t index=0)
For Block Ack variants other than Multi-STA Block Ack, set the TID_INFO subfield of the BA Control fi...
void SetType(BlockAckType type)
Set the block ack type.
void SetAid11(uint16_t aid, std::size_t index)
For Multi-STA Block Acks, set the AID11 subfield of the Per AID TID Info subfield identified by the g...
bool GetAckType(std::size_t index) const
For Multi-STA Block Acks, get the Ack Type subfield of the Per AID TID Info subfield identified by th...
Headers for Trigger frames.
bool IsBasic() const
Check if this is a Basic Trigger frame.
void SetApTxPower(int8_t power)
Set the AP TX Power subfield of the Common Info field.
ConstIterator end() const
Get a const iterator indicating past-the-last User Info field in the list.
WifiTxVector GetHeTbTxVector(uint16_t staId) const
Get the TX vector that the station with the given STA-ID will use to send the HE TB PPDU solicited by...
bool IsMuRts() const
Check if this is a MU-RTS Trigger frame.
bool IsBsrp() const
Check if this is a Buffer Status Report Poll Trigger frame.
bool IsMuBar() const
Check if this is a MU-BAR Trigger frame.
std::size_t GetNUserInfoFields() const
Get the number of User Info fields in this Trigger Frame.
ConstIterator FindUserInfoWithAid(ConstIterator start, uint16_t aid12) const
Get a const iterator pointing to the first User Info field found (starting from the one pointed to by...
void SetCsRequired(bool cs)
Set the CS Required subfield of the Common Info field.
uint16_t GetUlLength() const
Get the UL Length subfield of the Common Info field.
MHz_u GetUlBandwidth() const
Get the bandwidth of the solicited HE TB PPDU.
TriggerFrameVariant GetVariant() const
Get the Common Info field variant.
Time GetGuardInterval() const
Get the guard interval duration of the solicited HE TB PPDU.
bool GetCsRequired() const
Get the CS Required subfield of the Common Info field.
int8_t GetApTxPower() const
Get the power value (dBm) indicated by the AP TX Power subfield of the Common Info field.
void Cancel()
This method is syntactic sugar for the ns3::Simulator::Cancel method.
Definition event-id.cc:44
bool IsPending() const
This method is syntactic sugar for !IsExpired().
Definition event-id.cc:65
std::set< Mac48Address > m_sentRtsTo
the STA(s) which we sent an RTS to (waiting for CTS)
void DoCtsTimeout(Ptr< WifiPsdu > psdu)
Take required actions when the CTS timer fired after sending an RTS to protect the given PSDU expires...
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
Ptr< WifiRemoteStationManager > GetWifiRemoteStationManager() const
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.
virtual void TransmissionSucceeded()
Take necessary actions upon a transmission success.
Ptr< WifiPhy > m_phy
the PHY layer on this station
Ptr< WifiMpdu > DropMpduIfRetryLimitReached(Ptr< WifiPsdu > psdu)
Wrapper for the GetMpdusToDropOnTxFailure function of the remote station manager that additionally dr...
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 bool GetUpdateCwOnCtsTimeout() const
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.
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 DoCtsAfterMuRtsTimeout(const WifiPsduMap &psduMap)
Called when no CTS frame is received after an MU-RTS.
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.
void TransmissionSucceeded() override
Take necessary actions upon a transmission success.
static Time ConvertLSigLengthToHeTbPpduDuration(uint16_t length, const WifiTxVector &txVector, WifiPhyBand band)
Definition he-phy.cc:281
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...
void ReleaseSequenceNumbers(Ptr< const WifiPsdu > psdu) const override
Make the sequence numbers of MPDUs included in the given PSDU available again if the MPDUs have never...
an EUI-48 address
static Mac48Address GetBroadcast()
bool IsBroadcast() const
A tag to be attached to a response to a multi-user UL frame, that carries the SNR values with which t...
Definition mu-snr-tag.h:26
double Get(uint16_t staId) const
Return the SNR value for the given sender.
Definition mu-snr-tag.cc:53
TxFormat
Enumeration of the possible transmission formats.
virtual void DoDispose()
Destructor implementation.
Definition object.cc:433
Smart pointer class similar to boost::intrusive_ptr.
Ptr< QosTxop > m_edca
the EDCAF that gained channel access
void TransmissionFailed(bool forceCurrentCw=false) override
Take necessary actions upon a transmission failure.
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.
Definition qos-txop.cc:665
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition simulator.h:561
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:197
Introspection did not find any typical Config paths.
Definition snr-tag.h:24
double Get() const
Return the SNR value.
Definition snr-tag.cc:79
Simulation virtual time values and global simulation resolution.
Definition nstime.h:94
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition time.cc:403
bool IsStrictlyPositive() const
Exactly equivalent to t > 0.
Definition nstime.h:340
@ MS
millisecond
Definition nstime.h:106
bool IsZero() const
Exactly equivalent to t == 0.
Definition nstime.h:304
Time GetTxopLimit() const
Return the TXOP limit.
Definition txop.cc:606
a unique identifier for an interface.
Definition type-id.h:49
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition type-id.cc:1001
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.
Implements the IEEE 802.11 MAC header.
uint8_t GetQosTid() const
Return the Traffic ID of a QoS header.
bool IsAck() const
Return true if the header is an Ack header.
bool IsBlockAckReq() const
Return true if the header is a BlockAckRequest header.
bool IsCts() const
Return true if the header is a CTS header.
Mac48Address GetAddr1() const
Return the address in the Address 1 field.
bool IsTrigger() const
Return true if the header is a Trigger header.
void SetNoMoreFragments()
Un-set the More Fragment bit in the Frame Control Field.
bool IsCtl() const
Return true if the Type is Control.
Time GetDuration() const
Return the duration from the Duration/ID field (Time object).
void SetSequenceNumber(uint16_t seq)
Set the sequence number of the header.
virtual uint32_t GetSize() const
Return the size of the WifiMacHeader in octets.
void SetDsNotFrom()
Un-set the From DS bit in the Frame Control field.
void SetAddr1(Mac48Address address)
Fill the Address 1 field with the given address.
bool IsBlockAck() const
Return true if the header is a BlockAck header.
virtual void SetType(WifiMacType type, bool resetToDsFromDs=true)
Set Type/Subtype values with the correct values depending on the given type.
Mac48Address GetAddr2() const
Return the address in the Address 2 field.
bool HasData() const
Return true if the header type is DATA and is not DATA_NULL.
void SetQosTid(uint8_t tid)
Set the TID for the QoS header.
QosAckPolicy GetQosAckPolicy() const
Return the QoS Ack policy in the QoS control field.
void SetDuration(Time duration)
Set the Duration/ID field with the given duration (Time object).
void SetDsTo()
Set the To DS bit in the Frame Control field.
void SetAddr2(Mac48Address address)
Fill the Address 2 field with the given address.
bool IsQosData() const
Return true if the Type is DATA and Subtype is one of the possible values for QoS Data.
void SetQosEosp()
Set the end of service period (EOSP) bit in the QoS control field.
void SetAddr3(Mac48Address address)
Fill the Address 3 field with the given address.
void SetDsNotTo()
Un-set the To DS bit in the Frame Control field.
void SetNoRetry()
Un-set the Retry bit in the Frame Control field.
represent a single transmission mode
Definition wifi-mode.h:40
Time GetSlot() const
Return the slot duration for this PHY.
Definition wifi-phy.cc:841
Time GetSifs() const
Return the Short Interframe Space (SIFS) for this PHY.
Definition wifi-phy.cc:829
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition wifi-phy.cc:1587
WifiPhyBand GetPhyBand() const
Get the configured Wi-Fi band.
Definition wifi-phy.cc:1069
static Time CalculatePhyPreambleAndHeaderDuration(const WifiTxVector &txVector)
Definition wifi-phy.cc:1580
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.
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,...
Definition assert.h:55
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition assert.h:75
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition boolean.cc:113
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition boolean.h:70
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition abort.h:38
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition abort.h:97
#define NS_ABORT_IF(cond)
Abnormal program termination if a condition is true.
Definition abort.h:65
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition log.h:257
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition log.h:271
#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.
Definition log.h:250
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition log.h:264
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition object-base.h:35
Ptr< T > Create(Ts &&... args)
Create class instances by constructors with varying numbers of arguments and return them by Ptr.
Definition ptr.h:436
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1345
AcIndex
This enumeration defines the Access Categories as an enumeration with values corresponding to the AC ...
Definition qos-utils.h:62
@ WIFI_PREAMBLE_EHT_TB
@ WIFI_PREAMBLE_HE_TB
@ 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.
Definition nstime.h:865
Every class exported by the ns3 library is enclosed in the ns3 namespace.
U * PeekPointer(const Ptr< U > &p)
Definition ptr.h:443
bool IsTrigger(const WifiPsduMap &psduMap)
std::unordered_map< uint16_t, Ptr< WifiPsdu > > WifiPsduMap
Map of PSDUs indexed by STA-ID.
Definition wifi-mac.h:78
uint32_t GetBlockAckRequestSize(BlockAckReqType type)
Return the total BlockAckRequest size (including FCS trailer).
Definition wifi-utils.cc:70
uint32_t GetMuBarSize(std::list< BlockAckReqType > types)
Return the total MU-BAR size (including FCS trailer).
Definition wifi-utils.cc:80
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...
Definition qos-utils.cc:115
@ WIFI_MAC_CTL_TRIGGER
@ WIFI_MAC_CTL_RTS
@ WIFI_MAC_CTL_BACKRESP
@ WIFI_MAC_QOSDATA_NULL
uint32_t GetBlockAckSize(BlockAckType type)
Return the total BlockAck size (including FCS trailer).
Definition wifi-utils.cc:60
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.
Definition wifi-ppdu.h:38
uint32_t GetAckSize()
Return the total Ack size (including FCS trailer).
Definition wifi-utils.cc:52
static constexpr uint16_t SU_STA_ID
STA_ID to identify a single user (SU)
Definition wifi-mode.h:24
uint32_t GetCtsSize()
Return the total CTS size (including FCS trailer).
ns3::Time timeout
std::vector< uint8_t > m_bitmapLen
Length (bytes) of included bitmaps.
RxSignalInfo structure containing info on the received signal.
Definition wifi-types.h:72
double snr
SNR in linear scale.
Definition wifi-types.h:73
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.