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