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