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