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