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