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