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