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