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