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->IsUlTargetRxPowerMaxTxPower())
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->GetUlTargetRxPower() + 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 if (!optRssi.has_value())
1600 {
1601 // This might happen after static setup where the AP has not received any
1602 // frame from the client yet.
1603 userInfo.SetUlTargetRxPowerMaxTxPower();
1604 continue;
1605 }
1606
1607 NS_ASSERT(optRssi);
1608 auto rssi = static_cast<int8_t>(*optRssi);
1609 rssi = (rssi >= -20)
1610 ? -20
1611 : ((rssi <= -110) ? -110 : rssi); // cap so as to keep within [-110; -20] dBm
1612 userInfo.SetUlTargetRxPower(rssi);
1613 }
1614}
1615
1616void
1618{
1619 NS_LOG_FUNCTION(this << psdu << txVector);
1620
1621 auto txVectorCopy = txVector;
1622
1623 if (psdu->GetNMpdus() == 1 && psdu->GetHeader(0).IsTrigger())
1624 {
1625 CtrlTriggerHeader trigger;
1626 psdu->GetPayload(0)->PeekHeader(trigger);
1627 if (trigger.IsMuRts())
1628 {
1629 const WifiMacHeader& muRts = psdu->GetHeader(0);
1630 // A station receiving an MU-RTS behaves just like as if it received an RTS.
1631 // Determine whether the MU-RTS is addressed to this station or not and
1632 // prepare an "equivalent" RTS frame so that we can reuse the UpdateNav()
1633 // and SetTxopHolder() methods of the parent classes
1634 WifiMacHeader rts;
1636 rts.SetDsNotFrom();
1637 rts.SetDsNotTo();
1638 rts.SetDuration(muRts.GetDuration());
1639 rts.SetAddr2(muRts.GetAddr2());
1640 if (m_staMac != nullptr && m_staMac->IsAssociated() &&
1641 muRts.GetAddr2() == m_bssid // sent by the AP this STA is associated with
1642 && trigger.FindUserInfoWithAid(m_staMac->GetAssociationId()) != trigger.end())
1643 {
1644 // the MU-RTS is addressed to this station
1645 rts.SetAddr1(m_self);
1646 }
1647 else
1648 {
1649 rts.SetAddr1(muRts.GetAddr2()); // an address different from that of this station
1650 }
1652 // The duration of the NAV reset timeout has to take into account that the CTS
1653 // response is sent using the 6 Mbps data rate
1654 txVectorCopy =
1656 }
1657 }
1659}
1660
1661void
1663 const CtrlTriggerHeader& trigger,
1664 double muRtsSnr)
1665{
1666 NS_LOG_FUNCTION(this << muRtsHdr << trigger << muRtsSnr);
1667
1668 if (!UlMuCsMediumIdle(trigger))
1669 {
1670 NS_LOG_DEBUG("UL MU CS indicated medium busy, cannot send CTS");
1671 return;
1672 }
1673
1674 NS_ASSERT(m_staMac != nullptr && m_staMac->IsAssociated());
1675 WifiTxVector ctsTxVector = GetCtsTxVectorAfterMuRts(trigger, m_staMac->GetAssociationId());
1676 ctsTxVector.SetTriggerResponding(true);
1677
1678 DoSendCtsAfterRts(muRtsHdr, ctsTxVector, muRtsSnr);
1679}
1680
1681void
1683{
1684 NS_LOG_FUNCTION(this << &txParams << durationId.As(Time::US));
1685
1687 NS_ASSERT(txParams.m_acknowledgment &&
1689 auto acknowledgment = static_cast<WifiUlMuMultiStaBa*>(txParams.m_acknowledgment.get());
1690
1691 NS_ASSERT(!acknowledgment->stationsReceivingMultiStaBa.empty());
1692
1693 CtrlBAckResponseHeader blockAck;
1694 blockAck.SetType(acknowledgment->baType);
1695
1696 Mac48Address receiver;
1697
1698 for (const auto& staInfo : acknowledgment->stationsReceivingMultiStaBa)
1699 {
1700 receiver = staInfo.first.first;
1701 uint8_t tid = staInfo.first.second;
1702 std::size_t index = staInfo.second;
1703
1704 blockAck.SetAid11(m_apMac->GetAssociationId(receiver, m_linkId), index);
1705 blockAck.SetTidInfo(tid, index);
1706
1707 if (tid == 14)
1708 {
1709 // All-ack context
1710 NS_LOG_DEBUG("Multi-STA Block Ack: Sending All-ack to=" << receiver);
1711 blockAck.SetAckType(true, index);
1712 continue;
1713 }
1714
1715 if (acknowledgment->baType.m_bitmapLen.at(index) == 0)
1716 {
1717 // Acknowledgment context
1718 NS_LOG_DEBUG("Multi-STA Block Ack: Sending Ack to=" << receiver);
1719 blockAck.SetAckType(true, index);
1720 }
1721 else
1722 {
1723 // Block acknowledgment context
1724 blockAck.SetAckType(false, index);
1725
1726 auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(receiver, tid);
1727 NS_ASSERT(agreement);
1728 agreement->get().FillBlockAckBitmap(blockAck, index);
1729 NS_LOG_DEBUG("Multi-STA Block Ack: Sending Block Ack with seq="
1730 << blockAck.GetStartingSequence(index) << " to=" << receiver
1731 << " tid=" << +tid);
1732 }
1733 }
1734
1735 WifiMacHeader hdr;
1737 hdr.SetAddr1(acknowledgment->stationsReceivingMultiStaBa.size() == 1
1738 ? receiver
1740 hdr.SetAddr2(m_self);
1741 hdr.SetDsNotFrom();
1742 hdr.SetDsNotTo();
1743
1744 Ptr<Packet> packet = Create<Packet>();
1745 packet->AddHeader(blockAck);
1746 Ptr<WifiPsdu> psdu =
1747 GetWifiPsdu(Create<WifiMpdu>(packet, hdr), acknowledgment->multiStaBaTxVector);
1748
1749 Time txDuration = WifiPhy::CalculateTxDuration(GetBlockAckSize(acknowledgment->baType),
1750 acknowledgment->multiStaBaTxVector,
1751 m_phy->GetPhyBand());
1752 /**
1753 * In a BlockAck frame transmitted in response to a frame carried in HE TB PPDU under
1754 * single protection settings, the Duration/ID field is set to the value obtained from
1755 * the Duration/ID field of the frame that elicited the response minus the time, in
1756 * microseconds between the end of the PPDU carrying the frame that elicited the response
1757 * and the end of the PPDU carrying the BlockAck frame.
1758 * Under multiple protection settings, the Duration/ID field in a BlockAck frame transmitted
1759 * in response to a frame carried in HE TB PPDU is set according to the multiple protection
1760 * settings defined in 9.2.5.2. (Sec. 9.2.5.7 of 802.11ax-2021)
1761 */
1763 const auto singleDurationId = Max(durationId - m_phy->GetSifs() - txDuration, Seconds(0));
1764 if (m_edca->GetTxopLimit(m_linkId).IsZero()) // single protection settings
1765 {
1766 psdu->SetDuration(singleDurationId);
1767 }
1768 else // multiple protection settings
1769 {
1770 auto duration = Max(m_edca->GetRemainingTxop(m_linkId) - txDuration, Seconds(0));
1772 {
1773 duration = std::min(duration, singleDurationId + m_singleExchangeProtectionSurplus);
1774 }
1775 psdu->SetDuration(duration);
1776 }
1777
1778 psdu->GetPayload(0)->AddPacketTag(m_muSnrTag);
1779
1780 ForwardPsduDown(psdu, acknowledgment->multiStaBaTxVector);
1781
1782 // continue with the TXOP if time remains
1783 m_psduMap.clear();
1784 m_edca->ResetCw(m_linkId);
1785 m_muSnrTag.Reset();
1787}
1788
1789void
1791 const WifiMacHeader& hdr)
1792{
1793 NS_LOG_FUNCTION(this << trigger << hdr);
1794 NS_ASSERT(trigger.IsBasic());
1795 NS_ASSERT(m_staMac && m_staMac->IsAssociated());
1796
1797 NS_LOG_DEBUG("Received a Trigger Frame (basic variant) soliciting a transmission");
1798
1799 if (!UlMuCsMediumIdle(trigger))
1800 {
1801 return;
1802 }
1803
1804 // Starting from the Preferred AC indicated in the Trigger Frame, check if there
1805 // is either a pending BlockAckReq frame or a data frame that can be transmitted
1806 // in the allocated time and is addressed to a station with which a Block Ack
1807 // agreement has been established.
1808
1809 // create the sequence of TIDs to check
1810 std::vector<uint8_t> tids;
1811 uint16_t staId = m_staMac->GetAssociationId();
1812 AcIndex preferredAc = trigger.FindUserInfoWithAid(staId)->GetPreferredAc();
1813 auto acIt = wifiAcList.find(preferredAc);
1814 for (uint8_t i = 0; i < 4; i++)
1815 {
1816 NS_ASSERT(acIt != wifiAcList.end());
1817 tids.push_back(acIt->second.GetHighTid());
1818 tids.push_back(acIt->second.GetLowTid());
1819
1820 acIt++;
1821 if (acIt == wifiAcList.end())
1822 {
1823 acIt = wifiAcList.begin();
1824 }
1825 }
1826
1827 Ptr<WifiPsdu> psdu;
1828 WifiTxParameters txParams;
1829 WifiTxVector tbTxVector = GetHeTbTxVector(trigger, hdr.GetAddr2());
1831 tbTxVector,
1832 m_phy->GetPhyBand());
1833
1834 for (const auto& tid : tids)
1835 {
1836 Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
1837
1838 if (!m_mac->GetBaAgreementEstablishedAsOriginator(hdr.GetAddr2(), tid))
1839 {
1840 // no Block Ack agreement established for this TID
1841 continue;
1842 }
1843
1844 txParams.Clear();
1845 txParams.m_txVector = tbTxVector;
1846
1847 // first, check if there is a pending BlockAckReq frame
1848 if (auto mpdu = GetBar(edca->GetAccessCategory(), tid, hdr.GetAddr2());
1849 mpdu && TryAddMpdu(mpdu, txParams, ppduDuration))
1850 {
1851 NS_LOG_DEBUG("Sending a BAR within a TB PPDU");
1852 psdu = Create<WifiPsdu>(mpdu, true);
1853 break;
1854 }
1855
1856 // otherwise, check if a suitable data frame is available
1857 auto receiver =
1858 GetWifiRemoteStationManager()->GetMldAddress(hdr.GetAddr2()).value_or(hdr.GetAddr2());
1859 if (auto mpdu = edca->PeekNextMpdu(m_linkId, tid, receiver))
1860 {
1861 mpdu = CreateAliasIfNeeded(mpdu);
1862 if (auto item = edca->GetNextMpdu(m_linkId, mpdu, txParams, ppduDuration, false))
1863 {
1864 // try A-MPDU aggregation
1865 std::vector<Ptr<WifiMpdu>> mpduList =
1866 m_mpduAggregator->GetNextAmpdu(item, txParams, ppduDuration);
1867 psdu = (mpduList.size() > 1 ? Create<WifiPsdu>(std::move(mpduList))
1868 : Create<WifiPsdu>(item, true));
1869 break;
1870 }
1871 }
1872 }
1873
1874 if (psdu)
1875 {
1876 psdu->SetDuration(hdr.GetDuration() - m_phy->GetSifs() - ppduDuration);
1877 SendPsduMapWithProtection(WifiPsduMap{{staId, psdu}}, txParams);
1878 }
1879 else
1880 {
1881 // send QoS Null frames
1882 SendQosNullFramesInTbPpdu(trigger, hdr);
1883 }
1884}
1885
1886void
1888 const WifiMacHeader& hdr)
1889{
1890 NS_LOG_FUNCTION(this << trigger << hdr);
1891 NS_ASSERT(trigger.IsBasic() || trigger.IsBsrp());
1892 NS_ASSERT(m_staMac && m_staMac->IsAssociated());
1893
1894 NS_LOG_DEBUG("Requested to send QoS Null frames");
1895
1896 if (!UlMuCsMediumIdle(trigger))
1897 {
1898 return;
1899 }
1900
1901 const auto addr1 =
1902 GetWifiRemoteStationManager()->GetMldAddress(hdr.GetAddr2()).value_or(hdr.GetAddr2());
1904 header.SetAddr1(addr1);
1905 header.SetAddr2(m_mac->GetAddress());
1906 header.SetAddr3(hdr.GetAddr2());
1907 header.SetDsTo();
1908 header.SetDsNotFrom();
1909 // TR3: Sequence numbers for transmitted QoS (+)Null frames may be set
1910 // to any value. (Table 10-3 of 802.11-2016)
1911 header.SetSequenceNumber(0);
1912 // Set the EOSP bit so that HtFEM::FinalizeMacHeader will add the Queue Size
1913 header.SetQosEosp();
1914
1915 WifiTxParameters txParams;
1916 txParams.m_txVector = GetHeTbTxVector(trigger, hdr.GetAddr2());
1917 txParams.m_protection = std::unique_ptr<WifiProtection>(new WifiNoProtection);
1918 txParams.m_acknowledgment = std::unique_ptr<WifiAcknowledgment>(new WifiNoAck);
1919
1921 txParams.m_txVector,
1922 m_phy->GetPhyBand());
1923 header.SetDuration(hdr.GetDuration() - m_phy->GetSifs() - ppduDuration);
1924
1925 std::vector<Ptr<WifiMpdu>> mpduList;
1926
1927 for (uint8_t tid = 0; tid < 8; ++tid)
1928 {
1929 if (!m_mac->GetBaAgreementEstablishedAsOriginator(hdr.GetAddr2(), tid))
1930 {
1931 NS_LOG_DEBUG("Skipping tid=" << +tid << " because no agreement established");
1932 continue;
1933 }
1934
1935 // We could call TryAddMpdu instead of IsWithinSizeAndTimeLimits below in order to
1936 // get the TX parameters updated automatically. However, aggregating the QoS Null
1937 // frames might fail because MPDU aggregation is disabled by default for VO
1938 // and BK. Therefore, we skip the check on max A-MPDU size and only update the
1939 // TX parameters below.
1940 header.SetQosTid(tid);
1941 auto mpdu = Create<WifiMpdu>(Create<Packet>(), header);
1942 mpdu = CreateAliasIfNeeded(mpdu);
1943 txParams.AddMpdu(mpdu);
1944 UpdateTxDuration(header.GetAddr1(), txParams);
1945
1946 if (!IsWithinSizeAndTimeLimits(txParams.GetSize(header.GetAddr1()),
1947 hdr.GetAddr2(),
1948 txParams,
1949 ppduDuration))
1950 {
1951 txParams.UndoAddMpdu();
1952 break;
1953 }
1954
1955 NS_LOG_DEBUG("Aggregating a QoS Null frame with tid=" << +tid);
1956 txParams.m_acknowledgment = GetAckManager()->TryAddMpdu(mpdu, txParams);
1957 mpduList.push_back(mpdu);
1958 }
1959
1960 if (mpduList.empty())
1961 {
1962 NS_LOG_DEBUG("Not enough time to send a QoS Null frame");
1963 return;
1964 }
1965
1966 Ptr<WifiPsdu> psdu = (mpduList.size() > 1 ? Create<WifiPsdu>(std::move(mpduList))
1967 : Create<WifiPsdu>(mpduList.front(), true));
1968 uint16_t staId = m_staMac->GetAssociationId();
1969 SendPsduMapWithProtection(WifiPsduMap{{staId, psdu}}, txParams);
1970}
1971
1972void
1974 uint8_t tid,
1975 Time durationId,
1976 double snr)
1977{
1978 NS_LOG_FUNCTION(this << trigger << tid << durationId.As(Time::US) << snr);
1979
1980 auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(m_bssid, tid);
1981
1982 if (!agreement)
1983 {
1984 NS_LOG_DEBUG("There's not a valid agreement for this BlockAckReq");
1985 return;
1986 }
1987
1988 if (!UlMuCsMediumIdle(trigger))
1989 {
1990 return;
1991 }
1992
1993 NS_LOG_DEBUG("Send Block Ack in TB PPDU");
1994 auto txVector = GetHeTbTxVector(trigger, m_bssid);
1995 SendBlockAck(*agreement, durationId, txVector, snr);
1996}
1997
1998bool
2000{
2001 NS_LOG_FUNCTION(this << hdr << txVector);
2002
2003 // "If, based on the MAC address information of a frame carried in a received PPDU, the
2004 // received PPDU satisfies both intra-BSS and inter-BSS conditions, then the received PPDU is
2005 // classified as an intra-BSS PPDU." (Sec. 26.2.2 of 802.11ax-2021)
2006 // Hence, check first if the intra-BSS conditions using MAC address information are satisfied:
2007 // 1. "The PPDU carries a frame that has an RA, TA, or BSSID field value that is equal to
2008 // the BSSID of the BSS in which the STA is associated"
2009 const auto ra = hdr.GetAddr1();
2010 auto ta = hdr.GetAddr2();
2011 const auto bssid = hdr.GetAddr3();
2012 const auto empty = Mac48Address();
2013
2014 if (ra == m_bssid || ta == m_bssid || bssid == m_bssid)
2015 {
2016 return true;
2017 }
2018
2019 // 2. "The PPDU carries a Control frame that does not have a TA field and that has an
2020 // RA field value that matches the saved TXOP holder address of the BSS in which
2021 // the STA is associated"
2022 if (hdr.IsCtl() && ta == empty && ra == m_txopHolder)
2023 {
2024 return true;
2025 }
2026
2027 // If we get here, the intra-BSS conditions using MAC address information are not satisfied.
2028 // "If the received PPDU satisfies the intra-BSS conditions using the RXVECTOR parameter
2029 // BSS_COLOR and also satisfies the inter-BSS conditions using MAC address information of a
2030 // frame carried in the PPDU, then the classification made using the MAC address information
2031 // takes precedence."
2032 // Hence, if the inter-BSS conditions using MAC address information are satisfied, the frame
2033 // is classified as inter-BSS
2034 // 1. "The PPDU carries a frame that has a BSSID field, the value of which is not the BSSID
2035 // of the BSS in which the STA is associated"
2036 if (bssid != empty && bssid != m_bssid)
2037 {
2038 return false;
2039 }
2040
2041 // 2. The PPDU carries a frame that does not have a BSSID field but has both an RA field and
2042 // TA field, neither value of which is equal to the BSSID of the BSS in which the STA is
2043 // associated
2044 if (bssid == empty && ta != empty && ra != empty && ta != m_bssid && ra != m_bssid)
2045 {
2046 return false;
2047 }
2048
2049 // If we get here, both intra-BSS and inter-bss conditions using MAC address information
2050 // are not satisfied. Hence, the frame is classified as intra-BSS if the intra-BSS conditions
2051 // using the RXVECTOR parameters are satisfied:
2052 // 1. The RXVECTOR parameter BSS_COLOR of the PPDU carrying the frame is the BSS color of the
2053 // BSS of which the STA is a member
2054 // This condition is used if the BSS is not disabled ("If a STA determines that the BSS color
2055 // is disabled (see 26.17.3.3), then the RXVECTOR parameter BSS_COLOR of a PPDU shall not be
2056 // used to classify the PPDU")
2057 const auto bssColor = m_mac->GetHeConfiguration()->m_bssColor;
2058
2059 // the other two conditions using the RXVECTOR parameter PARTIAL_AID are not implemented
2060 return bssColor != 0 && bssColor == txVector.GetBssColor();
2061}
2062
2063void
2065 const WifiTxVector& txVector,
2066 const Time& surplus)
2067{
2068 NS_LOG_FUNCTION(this << hdr << txVector << surplus.As(Time::US));
2069
2070 if (!hdr.HasNav())
2071 {
2072 return;
2073 }
2074
2075 if (hdr.GetAddr1() == m_self)
2076 {
2077 // When the received frame's RA is equal to the STA's own MAC address, the STA
2078 // shall not update its NAV (IEEE 802.11-2020, sec. 10.3.2.4)
2079 return;
2080 }
2081
2082 // The intra-BSS NAV is updated by an intra-BSS PPDU. The basic NAV is updated by an
2083 // inter-BSS PPDU or a PPDU that cannot be classified as intra-BSS or inter-BSS.
2084 // (Section 26.2.4 of 802.11ax-2021)
2085 if (!IsIntraBssPpdu(hdr, txVector))
2086 {
2087 NS_LOG_DEBUG("PPDU not classified as intra-BSS, update the basic NAV");
2088 VhtFrameExchangeManager::UpdateNav(hdr, txVector, surplus);
2089 return;
2090 }
2091
2092 NS_LOG_DEBUG("PPDU classified as intra-BSS, update the intra-BSS NAV");
2093 Time duration = hdr.GetDuration();
2094 NS_LOG_DEBUG("Duration/ID=" << duration);
2095 duration += surplus;
2096
2097 if (hdr.IsCfEnd())
2098 {
2099 // An HE STA that maintains two NAVs (see 26.2.4) and receives a CF-End frame should reset
2100 // the basic NAV if the received CF-End frame is carried in an inter-BSS PPDU and reset the
2101 // intra-BSS NAV if the received CF-End frame is carried in an intra-BSS PPDU. (Sec. 26.2.5
2102 // of 802.11ax-2021)
2103 NS_LOG_DEBUG("Received CF-End, resetting the intra-BSS NAV");
2105 return;
2106 }
2107
2108 // For all other received frames the STA shall update its NAV when the received
2109 // Duration is greater than the STA's current NAV value (IEEE 802.11-2020 sec. 10.3.2.4)
2110 auto intraBssNavEnd = Simulator::Now() + duration;
2111 if (intraBssNavEnd > m_intraBssNavEnd)
2112 {
2113 m_intraBssNavEnd = intraBssNavEnd;
2114 NS_LOG_DEBUG("Updated intra-BSS NAV=" << m_intraBssNavEnd);
2115
2116 // A STA that used information from an RTS frame as the most recent basis to update
2117 // its NAV setting is permitted to reset its NAV if no PHY-RXSTART.indication
2118 // primitive is received from the PHY during a NAVTimeout period starting when the
2119 // MAC receives a PHY-RXEND.indication primitive corresponding to the detection of
2120 // the RTS frame. NAVTimeout period is equal to:
2121 // (2 x aSIFSTime) + (CTS_Time) + aRxPHYStartDelay + (2 x aSlotTime)
2122 // The “CTS_Time” shall be calculated using the length of the CTS frame and the data
2123 // rate at which the RTS frame used for the most recent NAV update was received
2124 // (IEEE 802.11-2016 sec. 10.3.2.4)
2125 if (hdr.IsRts())
2126 {
2127 auto addr2 = hdr.GetAddr2();
2128 WifiTxVector ctsTxVector =
2129 GetWifiRemoteStationManager()->GetCtsTxVector(addr2, txVector.GetMode());
2130 auto navResetDelay =
2131 2 * m_phy->GetSifs() +
2132 WifiPhy::CalculateTxDuration(GetCtsSize(), ctsTxVector, m_phy->GetPhyBand()) +
2133 WifiPhy::CalculatePhyPreambleAndHeaderDuration(ctsTxVector) + 2 * m_phy->GetSlot();
2134 m_intraBssNavResetEvent.Cancel();
2136 Simulator::Schedule(navResetDelay,
2138 this);
2139 }
2140 }
2141 NS_LOG_DEBUG("Current intra-BSS NAV=" << m_intraBssNavEnd);
2142
2143 m_channelAccessManager->NotifyNavStartNow(duration);
2144}
2145
2146void
2148{
2149 NS_LOG_FUNCTION(this);
2151 {
2152 m_txopHolder.reset();
2153 }
2154}
2155
2156void
2158{
2159 NS_LOG_FUNCTION(this);
2161 // Do not reset the TXOP holder because the basic NAV is updated by inter-BSS frames
2162 // The NAV seen by the ChannelAccessManager is now the intra-BSS NAV only
2164 m_channelAccessManager->NotifyNavResetNow(intraBssNav);
2165}
2166
2167void
2169{
2170 NS_LOG_FUNCTION(this);
2173 // The NAV seen by the ChannelAccessManager is now the basic NAV only
2175 m_channelAccessManager->NotifyNavResetNow(basicNav);
2176}
2177
2178std::optional<Mac48Address>
2180{
2181 NS_LOG_FUNCTION(this << hdr << txVector);
2182
2183 if (hdr.IsTrigger() && hdr.GetAddr2() == m_bssid)
2184 {
2185 return m_bssid;
2186 }
2187 if (!txVector.IsUlMu()) // the sender of a TB PPDU is not the TXOP holder
2188 {
2189 return VhtFrameExchangeManager::FindTxopHolder(hdr, txVector);
2190 }
2191 return std::nullopt;
2192}
2193
2194bool
2196{
2197 // For an HE STA maintaining two NAVs, if both the NAV timers are 0, the virtual CS indication
2198 // is that the medium is idle; if at least one of the two NAV timers is nonzero, the virtual CS
2199 // indication is that the medium is busy. (Sec. 26.2.4 of 802.11ax-2021)
2201}
2202
2203bool
2205{
2206 if (!trigger.GetCsRequired())
2207 {
2208 NS_LOG_DEBUG("CS not required");
2209 return true;
2210 }
2211
2212 // A non-AP STA does not consider the intra-BSS NAV in determining whether to respond to a
2213 // Trigger frame sent by the AP with which the non-AP STA is associated.
2214 // A non-AP STA considers the basic NAV in determining whether to respond to a Trigger frame
2215 // sent by the AP with which the non-AP STA is associated. (Sec. 26.5.2.5 of 802.11ax-2021)
2216 const Time now = Simulator::Now();
2217 if (m_navEnd > now)
2218 {
2219 NS_LOG_DEBUG("Basic NAV indicates medium busy");
2220 return false;
2221 }
2222
2223 NS_ASSERT_MSG(m_staMac, "UL MU CS is only performed by non-AP STAs");
2224 const auto userInfoIt = trigger.FindUserInfoWithAid(m_staMac->GetAssociationId());
2225 NS_ASSERT_MSG(userInfoIt != trigger.end(),
2226 "No User Info field for STA (" << m_self
2227 << ") AID=" << m_staMac->GetAssociationId());
2228
2229 std::set<uint8_t> indices;
2230
2231 if (trigger.IsMuRts())
2232 {
2233 auto ctsTxVector = GetCtsTxVectorAfterMuRts(trigger, m_staMac->GetAssociationId());
2234 auto bw = ctsTxVector.GetChannelWidth();
2235 indices = m_phy->GetOperatingChannel().GetAll20MHzChannelIndicesInPrimary(bw);
2236 }
2237 else
2238 {
2239 indices =
2240 m_phy->GetOperatingChannel().Get20MHzIndicesCoveringRu(userInfoIt->GetRuAllocation(),
2241 trigger.GetUlBandwidth());
2242 }
2243 return !m_channelAccessManager->GetPer20MHzBusy(indices);
2244}
2245
2246void
2248 RxSignalInfo rxSignalInfo,
2249 const WifiTxVector& txVector,
2250 bool inAmpdu)
2251{
2252 NS_LOG_FUNCTION(this << *mpdu << rxSignalInfo << txVector << inAmpdu);
2253
2254 // The received MPDU is either broadcast or addressed to this station
2255 NS_ASSERT(mpdu->GetHeader().GetAddr1().IsGroup() || mpdu->GetHeader().GetAddr1() == m_self);
2256
2257 const WifiMacHeader& hdr = mpdu->GetHeader();
2258
2259 if (txVector.IsUlMu() && m_txTimer.IsRunning() &&
2261 {
2262 Mac48Address sender = hdr.GetAddr2();
2263 NS_ASSERT(m_txParams.m_acknowledgment &&
2264 m_txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA);
2265 auto acknowledgment = static_cast<WifiUlMuMultiStaBa*>(m_txParams.m_acknowledgment.get());
2266 std::size_t index = acknowledgment->baType.m_bitmapLen.size();
2267
2268 if (!m_txTimer.GetStasExpectedToRespond().contains(sender))
2269 {
2270 NS_LOG_WARN("Received a TB PPDU from an unexpected station: " << sender);
2271 return;
2272 }
2273
2274 if (hdr.IsBlockAckReq())
2275 {
2276 NS_LOG_DEBUG("Received a BlockAckReq in a TB PPDU from " << sender);
2277
2278 CtrlBAckRequestHeader blockAckReq;
2279 mpdu->GetPacket()->PeekHeader(blockAckReq);
2280 NS_ABORT_MSG_IF(blockAckReq.IsMultiTid(), "Multi-TID BlockAckReq not supported");
2281 uint8_t tid = blockAckReq.GetTidInfo();
2282 GetBaManager(tid)->NotifyGotBlockAckRequest(
2283 m_mac->GetMldAddress(sender).value_or(sender),
2284 tid,
2285 blockAckReq.GetStartingSequence());
2286
2287 // Block Acknowledgment context
2288 acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, tid), index);
2289 acknowledgment->baType.m_bitmapLen.push_back(
2290 m_mac->GetBaTypeAsRecipient(sender, tid).m_bitmapLen.at(0));
2291 uint16_t staId = txVector.GetHeMuUserInfoMap().begin()->first;
2292 m_muSnrTag.Set(staId, rxSignalInfo.snr);
2293 }
2294 else if (hdr.IsQosData() && !inAmpdu && hdr.GetQosAckPolicy() == WifiMacHeader::NORMAL_ACK)
2295 {
2296 NS_LOG_DEBUG("Received an S-MPDU in a TB PPDU from " << sender << " (" << *mpdu << ")");
2297
2298 uint8_t tid = hdr.GetQosTid();
2299 GetBaManager(tid)->NotifyGotMpdu(mpdu);
2300
2301 // Acknowledgment context of Multi-STA Block Acks
2302 acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, tid), index);
2303 acknowledgment->baType.m_bitmapLen.push_back(0);
2304 uint16_t staId = txVector.GetHeMuUserInfoMap().begin()->first;
2305 m_muSnrTag.Set(staId, rxSignalInfo.snr);
2306 }
2307 else if (!(hdr.IsQosData() && !hdr.HasData() && !inAmpdu))
2308 {
2309 // The other case handled by this function is when we receive a QoS Null frame
2310 // that is not in an A-MPDU. For all other cases, the reception is handled by
2311 // parent classes. In particular, in case of a QoS data frame in A-MPDU, we
2312 // have to wait until the A-MPDU reception is completed, but we let the
2313 // parent classes notify the Block Ack agreement of the reception of this MPDU
2314 VhtFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
2315 return;
2316 }
2317
2318 // Schedule the transmission of a Multi-STA BlockAck frame if needed
2319 if (!acknowledgment->stationsReceivingMultiStaBa.empty() && !m_multiStaBaEvent.IsPending())
2320 {
2323 this,
2324 std::cref(m_txParams),
2325 mpdu->GetHeader().GetDuration());
2326 }
2327
2328 // remove the sender from the set of stations that are expected to send a TB PPDU
2329 m_txTimer.GotResponseFrom(sender);
2330
2331 if (m_txTimer.GetStasExpectedToRespond().empty())
2332 {
2333 // we do not expect any other BlockAck frame
2334 m_txTimer.Cancel();
2335 m_channelAccessManager->NotifyAckTimeoutResetNow();
2336
2337 if (!m_multiStaBaEvent.IsPending())
2338 {
2339 // all of the stations that replied with a TB PPDU sent QoS Null frames.
2340 NS_LOG_DEBUG("Continue the TXOP");
2341 m_psduMap.clear();
2342 m_edca->ResetCw(m_linkId);
2344 }
2345 }
2346
2347 // the received TB PPDU has been processed
2348 return;
2349 }
2350
2351 if (txVector.IsUlMu() && m_txTimer.IsRunning() &&
2353 !inAmpdu) // if in A-MPDU, processing is done at the end of A-MPDU reception
2354 {
2355 const auto& sender = hdr.GetAddr2();
2356
2357 if (!m_txTimer.GetStasExpectedToRespond().contains(sender))
2358 {
2359 NS_LOG_WARN("Received a TB PPDU from an unexpected station: " << sender);
2360 return;
2361 }
2362 if (!(hdr.IsQosData() && !hdr.HasData()))
2363 {
2364 NS_LOG_WARN("No QoS Null frame in the received MPDU");
2365 return;
2366 }
2367
2368 NS_LOG_DEBUG("Received a QoS Null frame in a TB PPDU from " << sender);
2370
2371 // the received TB PPDU has been processed
2372 return;
2373 }
2374
2375 if (hdr.IsCtl())
2376 {
2377 if (hdr.IsCts() && m_txTimer.IsRunning() &&
2378 m_txTimer.GetReason() == WifiTxTimer::WAIT_CTS && m_psduMap.size() == 1)
2379 {
2380 NS_ABORT_MSG_IF(inAmpdu, "Received CTS as part of an A-MPDU");
2381 NS_ASSERT(hdr.GetAddr1() == m_self);
2382
2383 Mac48Address sender = m_psduMap.begin()->second->GetAddr1();
2384 NS_LOG_DEBUG("Received CTS from=" << sender);
2385
2386 SnrTag tag;
2387 mpdu->GetPacket()->PeekPacketTag(tag);
2388 GetWifiRemoteStationManager()->ReportRxOk(sender, rxSignalInfo, txVector);
2389 GetWifiRemoteStationManager()->ReportRtsOk(m_psduMap.begin()->second->GetHeader(0),
2390 rxSignalInfo.snr,
2391 txVector.GetMode(),
2392 tag.Get());
2393
2394 m_txTimer.Cancel();
2395 m_channelAccessManager->NotifyCtsTimeoutResetNow();
2397 }
2398 else if (hdr.IsCts() && m_txTimer.IsRunning() &&
2400 {
2401 NS_ABORT_MSG_IF(inAmpdu, "Received CTS as part of an A-MPDU");
2402 NS_ASSERT(hdr.GetAddr1() == m_self);
2403
2404 NS_LOG_DEBUG("Received a CTS frame in response to an MU-RTS");
2405
2406 m_txTimer.Cancel();
2407 m_channelAccessManager->NotifyCtsTimeoutResetNow();
2409 }
2410 else if (hdr.IsAck() && m_txTimer.IsRunning() &&
2412 {
2413 NS_ASSERT(hdr.GetAddr1() == m_self);
2414 NS_ASSERT(m_txParams.m_acknowledgment);
2415 NS_ASSERT(m_txParams.m_acknowledgment->method ==
2417
2418 auto acknowledgment =
2419 static_cast<WifiDlMuBarBaSequence*>(m_txParams.m_acknowledgment.get());
2420 NS_ASSERT(acknowledgment->stationsReplyingWithNormalAck.size() == 1);
2422 uint16_t staId = m_apMac->GetAssociationId(
2423 acknowledgment->stationsReplyingWithNormalAck.begin()->first,
2424 m_linkId);
2425 auto it = m_psduMap.find(staId);
2426 NS_ASSERT(it != m_psduMap.end());
2427 NS_ASSERT(it->second->GetAddr1() ==
2428 acknowledgment->stationsReplyingWithNormalAck.begin()->first);
2429 SnrTag tag;
2430 mpdu->GetPacket()->PeekPacketTag(tag);
2431 ReceivedNormalAck(*it->second->begin(),
2432 m_txParams.m_txVector,
2433 txVector,
2434 rxSignalInfo,
2435 tag.Get());
2436 m_psduMap.clear();
2437 }
2438 // TODO the PHY should not pass us a non-TB PPDU if we are waiting for a
2439 // TB PPDU. However, processing the PHY header is done by the PHY entity
2440 // corresponding to the modulation class of the PPDU being received, hence
2441 // it is not possible to check if a valid TRIGVECTOR is stored when receiving
2442 // PPDUs of older modulation classes. Therefore, we check here that we are
2443 // actually receiving a TB PPDU.
2444 else if (hdr.IsBlockAck() && txVector.IsUlMu() && m_txTimer.IsRunning() &&
2446 {
2447 Mac48Address sender = hdr.GetAddr2();
2448 NS_LOG_DEBUG("Received BlockAck in TB PPDU from=" << sender);
2449
2450 SnrTag tag;
2451 mpdu->GetPacket()->PeekPacketTag(tag);
2452
2453 // notify the Block Ack Manager
2454 CtrlBAckResponseHeader blockAck;
2455 mpdu->GetPacket()->PeekHeader(blockAck);
2456 uint8_t tid = blockAck.GetTidInfo();
2457 std::pair<uint16_t, uint16_t> ret =
2458 GetBaManager(tid)->NotifyGotBlockAck(m_linkId,
2459 blockAck,
2460 m_mac->GetMldAddress(sender).value_or(sender),
2461 {tid});
2462 GetWifiRemoteStationManager()->ReportAmpduTxStatus(sender,
2463 ret.first,
2464 ret.second,
2465 rxSignalInfo.snr,
2466 tag.Get(),
2467 m_txParams.m_txVector);
2468
2469 // remove the sender from the set of stations that are expected to send a BlockAck
2470 if (!m_txTimer.GetStasExpectedToRespond().contains(sender))
2471 {
2472 NS_LOG_WARN("Received a BlockAck from an unexpected stations: " << sender);
2473 return;
2474 }
2475
2476 m_txTimer.GotResponseFrom(sender);
2477
2478 if (m_txTimer.GetStasExpectedToRespond().empty())
2479 {
2480 // we do not expect any other BlockAck frame
2481 m_txTimer.Cancel();
2482 m_channelAccessManager->NotifyAckTimeoutResetNow();
2483 if (m_triggerFrame)
2484 {
2485 // this is strictly needed for DL_MU_TF_MU_BAR only
2486 m_triggerFrame = nullptr;
2487 }
2488
2489 m_edca->ResetCw(m_linkId);
2490 m_psduMap.clear();
2492 }
2493 }
2494 else if (hdr.IsBlockAck() && m_txTimer.IsRunning() &&
2496 {
2497 CtrlBAckResponseHeader blockAck;
2498 mpdu->GetPacket()->PeekHeader(blockAck);
2499
2500 NS_ABORT_MSG_IF(!blockAck.IsMultiSta(),
2501 "A Multi-STA BlockAck is expected after a TB PPDU");
2502 NS_LOG_DEBUG("Received a Multi-STA BlockAck from=" << hdr.GetAddr2());
2503 m_txTimer.GotResponseFrom(hdr.GetAddr2());
2504
2505 NS_ASSERT(m_staMac && m_staMac->IsAssociated());
2506 if (hdr.GetAddr2() != m_bssid)
2507 {
2508 NS_LOG_DEBUG("The sender is not the AP we are associated with");
2509 return;
2510 }
2511
2512 uint16_t staId = m_staMac->GetAssociationId();
2513 std::vector<uint32_t> indices = blockAck.FindPerAidTidInfoWithAid(staId);
2514
2515 if (indices.empty())
2516 {
2517 NS_LOG_DEBUG("No Per AID TID Info subfield intended for me");
2518 return;
2519 }
2520
2521 MuSnrTag tag;
2522 mpdu->GetPacket()->PeekPacketTag(tag);
2523
2524 // notify the Block Ack Manager
2525 for (const auto& index : indices)
2526 {
2527 uint8_t tid = blockAck.GetTidInfo(index);
2528
2529 if (blockAck.GetAckType(index) && tid < 8)
2530 {
2531 // Acknowledgment context
2532 NS_ABORT_IF(m_psduMap.empty() || m_psduMap.begin()->first != staId);
2533 GetBaManager(tid)->NotifyGotAck(m_linkId, *m_psduMap.at(staId)->begin());
2534 }
2535 else
2536 {
2537 // Block Acknowledgment or All-ack context
2538 if (blockAck.GetAckType(index) && tid == 14)
2539 {
2540 // All-ack context, we need to determine the actual TID(s) of the PSDU
2541 NS_ASSERT(indices.size() == 1);
2542 NS_ABORT_IF(m_psduMap.empty() || m_psduMap.begin()->first != staId);
2543 std::set<uint8_t> tids = m_psduMap.at(staId)->GetTids();
2544 NS_ABORT_MSG_IF(tids.size() > 1, "Multi-TID A-MPDUs not supported yet");
2545 tid = *tids.begin();
2546 }
2547
2548 std::pair<uint16_t, uint16_t> ret = GetBaManager(tid)->NotifyGotBlockAck(
2549 m_linkId,
2550 blockAck,
2551 m_mac->GetMldAddress(hdr.GetAddr2()).value_or(hdr.GetAddr2()),
2552 {tid},
2553 index);
2554 GetWifiRemoteStationManager()->ReportAmpduTxStatus(hdr.GetAddr2(),
2555 ret.first,
2556 ret.second,
2557 rxSignalInfo.snr,
2558 tag.Get(staId),
2559 m_txParams.m_txVector);
2560 }
2561
2562 if (m_psduMap.at(staId)->GetHeader(0).IsQosData() &&
2563 (blockAck.GetAckType(index) // Ack or All-ack context
2564 || std::any_of(blockAck.GetBitmap(index).begin(),
2565 blockAck.GetBitmap(index).end(),
2566 [](uint8_t b) { return b != 0; })))
2567 {
2568 NS_ASSERT(m_psduMap.at(staId)->GetHeader(0).HasData());
2569 NS_ASSERT(m_psduMap.at(staId)->GetHeader(0).GetQosTid() == tid);
2570 // the station has received a response from the AP for the HE TB PPDU
2571 // transmitted in response to a Basic Trigger Frame and at least one
2572 // MPDU was acknowledged. Therefore, it needs to update the access
2573 // parameters if it received an MU EDCA Parameter Set element.
2574 m_mac->GetQosTxop(tid)->StartMuEdcaTimerNow(m_linkId);
2575 }
2576 }
2577
2578 // cancel the timer
2579 m_txTimer.Cancel();
2580 m_channelAccessManager->NotifyAckTimeoutResetNow();
2581 // dequeue BlockAckReq frames included in acknowledged TB PPDUs (if any)
2582 for (const auto& [staId, psdu] : m_psduMap)
2583 {
2584 if (psdu->GetNMpdus() == 1 && psdu->GetHeader(0).IsBlockAckReq())
2585 {
2586 DequeuePsdu(psdu);
2587 }
2588 }
2589 m_psduMap.clear();
2590 }
2591 else if (hdr.IsBlockAck() && m_txTimer.IsRunning() &&
2593 {
2594 // this BlockAck frame may have been sent in response to a DL MU PPDU with
2595 // acknowledgment in SU format or one of the consequent BlockAckReq frames.
2596 // We clear the PSDU map and let parent classes continue processing this frame.
2597 m_psduMap.clear();
2598 VhtFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
2599 }
2600 else if (hdr.IsTrigger())
2601 {
2602 // Trigger Frames are only processed by STAs
2603 if (!m_staMac)
2604 {
2605 return;
2606 }
2607
2608 // A Trigger Frame in an A-MPDU is processed when the A-MPDU is fully received
2609 if (inAmpdu)
2610 {
2611 m_triggerFrameInAmpdu = true;
2612 return;
2613 }
2614
2615 CtrlTriggerHeader trigger;
2616 mpdu->GetPacket()->PeekHeader(trigger);
2617
2618 if (hdr.GetAddr1() != m_self &&
2619 (!hdr.GetAddr1().IsBroadcast() || !m_staMac->IsAssociated() ||
2620 hdr.GetAddr2() != m_bssid // not sent by the AP this STA is associated with
2621 || trigger.FindUserInfoWithAid(m_staMac->GetAssociationId()) == trigger.end()))
2622 {
2623 // not addressed to us
2624 return;
2625 }
2626
2627 uint16_t staId = m_staMac->GetAssociationId();
2628
2629 if (trigger.IsMuRts())
2630 {
2631 Mac48Address sender = hdr.GetAddr2();
2632 NS_LOG_DEBUG("Received MU-RTS Trigger Frame from=" << sender);
2633 GetWifiRemoteStationManager()->ReportRxOk(sender, rxSignalInfo, txVector);
2634
2635 // If a non-AP STA receives an MU-RTS Trigger frame, the non-AP STA shall commence
2636 // the transmission of a CTS frame response at the SIFS time boundary after
2637 // the end of a received PPDU when all the following conditions are met:
2638 // - The MU-RTS Trigger frame has one of the User Info fields addressed to
2639 // the non-AP STA (this is guaranteed if we get here)
2640 // - The UL MU CS condition indicates that the medium is idle
2641 // (Sec. 26.2.6.3 of 802.11ax-2021)
2642 NS_LOG_DEBUG("Schedule CTS");
2645 this,
2646 hdr,
2647 trigger,
2648 rxSignalInfo.snr);
2649 }
2650 else if (trigger.IsMuBar())
2651 {
2652 Mac48Address sender = hdr.GetAddr2();
2653 NS_LOG_DEBUG("Received MU-BAR Trigger Frame from=" << sender);
2654 GetWifiRemoteStationManager()->ReportRxOk(sender, rxSignalInfo, txVector);
2655
2656 auto userInfoIt = trigger.FindUserInfoWithAid(staId);
2657 NS_ASSERT(userInfoIt != trigger.end());
2658 CtrlBAckRequestHeader blockAckReq = userInfoIt->GetMuBarTriggerDepUserInfo();
2659 NS_ABORT_MSG_IF(blockAckReq.IsMultiTid(), "Multi-TID BlockAckReq not supported");
2660 uint8_t tid = blockAckReq.GetTidInfo();
2661
2662 GetBaManager(tid)->NotifyGotBlockAckRequest(
2663 m_mac->GetMldAddress(sender).value_or(sender),
2664 tid,
2665 blockAckReq.GetStartingSequence());
2666
2667 Simulator::Schedule(m_phy->GetSifs(),
2669 this,
2670 trigger,
2671 tid,
2672 hdr.GetDuration(),
2673 rxSignalInfo.snr);
2674 }
2675 else if (trigger.IsBasic())
2676 {
2677 Simulator::Schedule(m_phy->GetSifs(),
2679 this,
2680 trigger,
2681 hdr);
2682 }
2683 else if (trigger.IsBsrp())
2684 {
2685 Simulator::Schedule(m_phy->GetSifs(),
2687 this,
2688 trigger,
2689 hdr);
2690 }
2691 }
2692 else
2693 {
2694 // the received control frame cannot be handled here
2695 VhtFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
2696 }
2697
2698 // the received control frame has been processed
2699 return;
2700 }
2701
2702 // the received frame cannot be handled here
2703 VhtFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
2704 ;
2705}
2706
2707void
2709 const RxSignalInfo& rxSignalInfo,
2710 const WifiTxVector& txVector,
2711 const std::vector<bool>& perMpduStatus)
2712{
2713 std::set<uint8_t> tids = psdu->GetTids();
2714
2715 if (txVector.IsUlMu() && m_txTimer.IsRunning() &&
2717 {
2718 Mac48Address sender = psdu->GetAddr2();
2719 NS_ASSERT(m_txParams.m_acknowledgment &&
2720 m_txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA);
2721 auto acknowledgment = static_cast<WifiUlMuMultiStaBa*>(m_txParams.m_acknowledgment.get());
2722 std::size_t index = acknowledgment->baType.m_bitmapLen.size();
2723
2724 if (!m_txTimer.GetStasExpectedToRespond().contains(sender))
2725 {
2726 NS_LOG_WARN("Received a TB PPDU from an unexpected station: " << sender);
2727 return;
2728 }
2729
2730 NS_LOG_DEBUG("Received an A-MPDU in a TB PPDU from " << sender << " (" << *psdu << ")");
2731
2732 if (std::any_of(tids.begin(), tids.end(), [&psdu](uint8_t tid) {
2733 return psdu->GetAckPolicyForTid(tid) == WifiMacHeader::NORMAL_ACK;
2734 }))
2735 {
2736 if (std::all_of(perMpduStatus.cbegin(), perMpduStatus.cend(), [](bool v) { return v; }))
2737 {
2738 // All-ack context
2739 acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, 14),
2740 index);
2741 acknowledgment->baType.m_bitmapLen.push_back(0);
2742 }
2743 else
2744 {
2745 // Block Acknowledgment context
2746 std::size_t i = 0;
2747 for (const auto& tid : tids)
2748 {
2749 acknowledgment->stationsReceivingMultiStaBa.emplace(std::make_pair(sender, tid),
2750 index + i++);
2751 acknowledgment->baType.m_bitmapLen.push_back(
2752 m_mac->GetBaTypeAsRecipient(sender, tid).m_bitmapLen.at(0));
2753 }
2754 }
2755 uint16_t staId = txVector.GetHeMuUserInfoMap().begin()->first;
2756 m_muSnrTag.Set(staId, rxSignalInfo.snr);
2757 }
2758
2759 // Schedule the transmission of a Multi-STA BlockAck frame if needed
2760 if (!acknowledgment->stationsReceivingMultiStaBa.empty() && !m_multiStaBaEvent.IsPending())
2761 {
2764 this,
2765 std::cref(m_txParams),
2766 psdu->GetDuration());
2767 }
2768
2769 // remove the sender from the set of stations that are expected to send a TB PPDU
2770 m_txTimer.GotResponseFrom(sender);
2771
2772 if (m_txTimer.GetStasExpectedToRespond().empty())
2773 {
2774 // we do not expect any other BlockAck frame
2775 m_txTimer.Cancel();
2776 m_channelAccessManager->NotifyAckTimeoutResetNow();
2777
2778 if (!m_multiStaBaEvent.IsPending())
2779 {
2780 // all of the stations that replied with a TB PPDU sent QoS Null frames.
2781 NS_LOG_DEBUG("Continue the TXOP");
2782 m_psduMap.clear();
2783 m_edca->ResetCw(m_linkId);
2785 }
2786 }
2787
2788 // the received TB PPDU has been processed
2789 return;
2790 }
2791
2792 if (txVector.IsUlMu() && m_txTimer.IsRunning() &&
2794 {
2795 Mac48Address sender = psdu->GetAddr2();
2796
2797 if (!m_txTimer.GetStasExpectedToRespond().contains(sender))
2798 {
2799 NS_LOG_WARN("Received a TB PPDU from an unexpected station: " << sender);
2800 return;
2801 }
2802 if (std::none_of(psdu->begin(), psdu->end(), [](Ptr<WifiMpdu> mpdu) {
2803 return mpdu->GetHeader().IsQosData() && !mpdu->GetHeader().HasData();
2804 }))
2805 {
2806 NS_LOG_WARN("No QoS Null frame in the received PSDU");
2807 return;
2808 }
2809
2810 NS_LOG_DEBUG("Received QoS Null frames in a TB PPDU from " << sender);
2812
2813 // the received TB PPDU has been processed
2814 return;
2815 }
2816
2818 {
2819 // the received A-MPDU contains a Trigger Frame. It is now time to handle it.
2820 auto psduIt = psdu->begin();
2821 while (psduIt != psdu->end())
2822 {
2823 if ((*psduIt)->GetHeader().IsTrigger())
2824 {
2825 ReceiveMpdu(*psduIt, rxSignalInfo, txVector, false);
2826 }
2827 psduIt++;
2828 }
2829
2830 m_triggerFrameInAmpdu = false;
2831 return;
2832 }
2833
2834 // the received frame cannot be handled here
2835 VhtFrameExchangeManager::EndReceiveAmpdu(psdu, rxSignalInfo, txVector, perMpduStatus);
2836}
2837
2838void
2840{
2841 NS_LOG_FUNCTION(this << sender);
2842
2843 NS_ASSERT(m_txTimer.IsRunning() &&
2845
2846 // remove the sender from the set of stations that are expected to send a TB PPDU
2847 m_txTimer.GotResponseFrom(sender);
2848
2849 if (m_txTimer.GetStasExpectedToRespond().empty())
2850 {
2851 // we do not expect any other response
2852 m_channelAccessManager->NotifyAckTimeoutResetNow();
2853
2855 m_psduMap.clear();
2856 m_edca->ResetCw(m_linkId);
2858 // we reset the TX timer after calling TransmissionSucceeded, so that the latter can
2859 // check whether the reason for the last timer is WAIT_QOS_NULL_AFTER_BSRP_TF
2860 m_txTimer.Cancel();
2861 }
2862}
2863
2864} // 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:1568
static Time CalculatePhyPreambleAndHeaderDuration(const WifiTxVector &txVector)
Definition wifi-phy.cc:1561
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:114
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:84
double snr
SNR in linear scale.
Definition wifi-types.h:85
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.