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