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