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