A Discrete-Event Network Simulator
API
he-frame-exchange-manager.cc
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2020 Universita' degli Studi di Napoli Federico II
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation;
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  *
18  * Author: Stefano Avallone <stavallo@unina.it>
19  */
20 
21 #include "ns3/log.h"
22 #include "ns3/abort.h"
24 #include "he-configuration.h"
25 #include "ns3/recipient-block-ack-agreement.h"
26 #include "ns3/ap-wifi-mac.h"
27 #include "ns3/sta-wifi-mac.h"
28 #include "multi-user-scheduler.h"
29 #include "ns3/snr-tag.h"
30 #include "he-phy.h"
31 #include <algorithm>
32 #include <functional>
33 
34 #undef NS_LOG_APPEND_CONTEXT
35 #define NS_LOG_APPEND_CONTEXT std::clog << "[mac=" << m_self << "] "
36 
37 namespace ns3 {
38 
39 NS_LOG_COMPONENT_DEFINE ("HeFrameExchangeManager");
40 
41 NS_OBJECT_ENSURE_REGISTERED (HeFrameExchangeManager);
42 
43 TypeId
45 {
46  static TypeId tid = TypeId ("ns3::HeFrameExchangeManager")
48  .AddConstructor<HeFrameExchangeManager> ()
49  .SetGroupName ("Wifi")
50  ;
51  return tid;
52 }
53 
55  : m_triggerFrameInAmpdu (false)
56 {
57  NS_LOG_FUNCTION (this);
58 }
59 
61 {
63 }
64 
65 uint16_t
67 {
68  NS_ASSERT (m_mac->GetHeConfiguration () != 0);
69  if (m_mac->GetHeConfiguration ()->GetMpduBufferSize () > 64)
70  {
71  return 256;
72  }
73  return 64;
74 }
75 
76 void
78 {
79  m_apMac = DynamicCast<ApWifiMac> (mac);
80  m_staMac = DynamicCast<StaWifiMac> (mac);
82 }
83 
84 void
86 {
87  NS_LOG_FUNCTION (this);
88  m_apMac = 0;
89  m_staMac = 0;
90  m_psduMap.clear ();
91  m_txParams.Clear ();
92  m_muScheduler = 0;
95 }
96 
97 void
99 {
100  NS_ASSERT (m_mac);
101  NS_ABORT_MSG_IF (m_apMac == 0,
102  "A Multi-User Scheduler can only be aggregated to an AP");
103  NS_ABORT_MSG_IF (m_apMac->GetHeConfiguration () == 0,
104  "A Multi-User Scheduler can only be aggregated to an HE AP");
105  m_muScheduler = muScheduler;
106 }
107 
108 bool
109 HeFrameExchangeManager::StartFrameExchange (Ptr<QosTxop> edca, Time availableTime, bool initialFrame)
110 {
111  NS_LOG_FUNCTION (this << edca << availableTime << initialFrame);
112 
115 
116  /*
117  * We consult the Multi-user Scheduler (if available) to know the type of transmission to make if:
118  * - there is no pending BlockAckReq to transmit
119  * - either the AC queue is empty (the scheduler might select an UL MU transmission)
120  * or the next frame in the AC queue is a non-broadcast QoS data frame addressed to
121  * a receiver with which a BA agreement has been already established
122  */
123  if (m_muScheduler != 0
124  && edca->GetBaManager ()->GetBar (false) == nullptr
125  && (mpdu == 0
126  || (mpdu->GetHeader ().IsQosData ()
127  && !mpdu->GetHeader ().GetAddr1 ().IsGroup ()
128  && edca->GetBaAgreementEstablished (mpdu->GetHeader ().GetAddr1 (), mpdu->GetHeader ().GetQosTid ()))))
129  {
130  txFormat = m_muScheduler->NotifyAccessGranted (edca, availableTime, initialFrame);
131  }
132 
133  if (txFormat == MultiUserScheduler::SU_TX)
134  {
135  return VhtFrameExchangeManager::StartFrameExchange (edca, availableTime, initialFrame);
136  }
137 
138  if (txFormat == MultiUserScheduler::DL_MU_TX)
139  {
140  if (m_muScheduler->GetDlMuInfo ().psduMap.empty ())
141  {
142  NS_LOG_DEBUG ("The Multi-user Scheduler returned DL_MU_TX with empty psduMap, do not transmit");
143  return false;
144  }
145 
146  SendPsduMapWithProtection (m_muScheduler->GetDlMuInfo ().psduMap,
147  m_muScheduler->GetDlMuInfo ().txParams);
148  return true;
149  }
150 
151  if (txFormat == MultiUserScheduler::UL_MU_TX)
152  {
153  if (m_muScheduler->GetUlMuInfo ().trigger == nullptr)
154  {
155  NS_LOG_DEBUG ("The Multi-user Scheduler returned UL_MU_TX with empty Trigger Frame, do not transmit");
156  return false;
157  }
158 
159  NS_ASSERT (m_muScheduler->GetUlMuInfo ().trigger->GetHeader ().IsTrigger ());
161  m_muScheduler->GetUlMuInfo ().txParams.m_txVector)}},
162  m_muScheduler->GetUlMuInfo ().txParams);
163  return true;
164  }
165 
166  return false;
167 }
168 
169 bool
170 HeFrameExchangeManager::SendMpduFromBaManager (Ptr<QosTxop> edca, Time availableTime, bool initialFrame)
171 {
172  NS_LOG_FUNCTION (this << edca << availableTime << initialFrame);
173 
174  // First, check if there is a BAR to be transmitted
175  Ptr<const WifiMacQueueItem> peekedItem = edca->GetBaManager ()->GetBar (false);
176 
177  if (peekedItem == 0)
178  {
179  NS_LOG_DEBUG ("Block Ack Manager returned no frame to send");
180  return false;
181  }
182 
183  if (peekedItem->GetHeader ().IsBlockAckReq ())
184  {
185  // BlockAckReq are handled by the HT FEM
186  return HtFrameExchangeManager::SendMpduFromBaManager (edca, availableTime, initialFrame);
187  }
188 
189  NS_ASSERT (peekedItem->GetHeader ().IsTrigger ());
190  m_triggerFrame = Copy (edca->GetBaManager ()->GetBar ());
191 
192  SendPsduMap ();
193  return true;
194 }
195 
196 void
198 {
199  NS_LOG_FUNCTION (this << &txParams);
200 
201  m_psduMap = std::move (psduMap);
202  m_txParams = std::move (txParams);
203 
204 #ifdef NS3_BUILD_PROFILE_DEBUG
205  // If protection is required, the MPDUs must be stored in some queue because
206  // they are not put back in a queue if the MU-RTS/CTS exchange fails
208  {
209  for (const auto& psdu : psduMap)
210  {
211  for (const auto& mpdu : *PeekPointer (psdu.second))
212  {
213  NS_ASSERT (mpdu->GetHeader ().IsCtl () || !mpdu->GetHeader ().HasData () || mpdu->IsQueued ());
214  }
215  }
216  }
217 #endif
218 
219  // Make sure that the acknowledgment time has been computed, so that SendMuRts()
220  // can reuse this value.
222 
223  if (m_txParams.m_acknowledgment->acknowledgmentTime == Time::Min ())
224  {
226  }
227 
228  // Set QoS Ack policy
229  for (auto& psdu : m_psduMap)
230  {
232  }
233 
235  {
236  SendPsduMap ();
237  }
238  else
239  {
240  NS_ABORT_MSG ("Unknown or prohibited protection type: " << m_txParams.m_protection.get ());
241  }
242 }
243 
246 {
247  auto it = std::find_if (psduMap.begin (), psduMap.end (),
248  [&to] (std::pair<uint16_t, Ptr<WifiPsdu>> psdu)
249  { return psdu.second->GetAddr1 () == to; });
250  if (it != psduMap.end ())
251  {
252  return it->second;
253  }
254  return nullptr;
255 }
256 
257 void
259 {
260  NS_LOG_FUNCTION (this);
261 
264 
265  WifiTxTimer::Reason timerType = WifiTxTimer::NOT_RUNNING; // no timer
266  WifiTxVector* responseTxVector = nullptr;
267  Ptr<WifiMacQueueItem> mpdu = nullptr;
268  Ptr<WifiPsdu> psdu = nullptr;
269  WifiTxVector txVector;
270 
271  // Compute the type of TX timer to set depending on the acknowledgment method
272 
273  /*
274  * Acknowledgment via a sequence of BlockAckReq and BlockAck frames
275  */
277  {
278  WifiDlMuBarBaSequence* acknowledgment = static_cast<WifiDlMuBarBaSequence*> (m_txParams.m_acknowledgment.get ());
279 
280  // schedule the transmission of required BlockAckReq frames
281  for (const auto& psdu : m_psduMap)
282  {
283  if (acknowledgment->stationsSendBlockAckReqTo.find (psdu.second->GetAddr1 ())
284  != acknowledgment->stationsSendBlockAckReqTo.end ())
285  {
286  // the receiver of this PSDU will receive a BlockAckReq
287  std::set<uint8_t> tids = psdu.second->GetTids ();
288  NS_ABORT_MSG_IF (tids.size () > 1, "Acknowledgment method incompatible with a Multi-TID A-MPDU");
289  uint8_t tid = *tids.begin ();
290 
291  NS_ASSERT (m_edca != 0);
292  m_edca->ScheduleBar (m_mac->GetQosTxop (tid)->PrepareBlockAckRequest (psdu.second->GetAddr1 (), tid));
293  }
294  }
295 
296  if (!acknowledgment->stationsReplyingWithNormalAck.empty ())
297  {
298  // a station will reply immediately with a Normal Ack
300  responseTxVector = &acknowledgment->stationsReplyingWithNormalAck.begin ()->second.ackTxVector;
301  psdu = GetPsduTo (acknowledgment->stationsReplyingWithNormalAck.begin ()->first, m_psduMap);
302  NS_ASSERT (psdu->GetNMpdus () == 1);
303  mpdu = *psdu->begin ();
304  }
305  else if (!acknowledgment->stationsReplyingWithBlockAck.empty ())
306  {
307  // a station will reply immediately with a Block Ack
308  timerType = WifiTxTimer::WAIT_BLOCK_ACK;
309  responseTxVector = &acknowledgment->stationsReplyingWithBlockAck.begin ()->second.blockAckTxVector;
310  psdu = GetPsduTo (acknowledgment->stationsReplyingWithBlockAck.begin ()->first, m_psduMap);
311  }
312  // else no station will reply immediately
313  }
314  /*
315  * Acknowledgment via a MU-BAR Trigger Frame sent as single user frame
316  */
318  {
319  WifiDlMuTfMuBar* acknowledgment = static_cast<WifiDlMuTfMuBar*> (m_txParams.m_acknowledgment.get ());
320 
321  if (m_triggerFrame == nullptr)
322  {
323  // we are transmitting the DL MU PPDU and have to schedule the
324  // transmission of a MU-BAR Trigger Frame.
325  // Create a TXVECTOR by "merging" all the BlockAck TXVECTORs
326  std::map<uint16_t, CtrlBAckRequestHeader> recipients;
327 
328  NS_ASSERT (!acknowledgment->stationsReplyingWithBlockAck.empty ());
329  auto staIt = acknowledgment->stationsReplyingWithBlockAck.begin ();
330  WifiTxVector txVector = staIt->second.blockAckTxVector;
331  while (staIt != acknowledgment->stationsReplyingWithBlockAck.end ())
332  {
333  NS_ASSERT (m_apMac != 0);
334  uint16_t staId = m_apMac->GetAssociationId (staIt->first);
335 
336  txVector.SetHeMuUserInfo (staId, staIt->second.blockAckTxVector.GetHeMuUserInfo (staId));
337  recipients.emplace (staId, staIt->second.barHeader);
338 
339  staIt++;
340  }
341  // set the Length field of the response TXVECTOR, which is needed to correctly
342  // set the UL Length field of the MU-BAR Trigger Frame
343  txVector.SetLength (acknowledgment->ulLength);
344 
345  NS_ASSERT (m_edca != 0);
346  m_edca->ScheduleBar (PrepareMuBar (txVector, recipients));
347  }
348  else
349  {
350  // we are transmitting the MU-BAR following the DL MU PPDU after a SIFS.
351  // m_psduMap and m_txParams are still the same as when the DL MU PPDU was sent.
352  // record the set of stations expected to send a BlockAck frame
353  m_staExpectTbPpduFrom.clear ();
354  for (auto& station : acknowledgment->stationsReplyingWithBlockAck)
355  {
356  m_staExpectTbPpduFrom.insert (station.first);
357  }
358 
359  Ptr<WifiPsdu> triggerPsdu = GetWifiPsdu (m_triggerFrame, acknowledgment->muBarTxVector);
360  Time txDuration = m_phy->CalculateTxDuration (triggerPsdu->GetSize (),
361  acknowledgment->muBarTxVector,
362  m_phy->GetPhyBand ());
363  // update acknowledgmentTime to correctly set the Duration/ID
364  acknowledgment->acknowledgmentTime -= (m_phy->GetSifs () + txDuration);
366 
367  responseTxVector = &acknowledgment->stationsReplyingWithBlockAck.begin ()->second.blockAckTxVector;
368  Time timeout = txDuration + m_phy->GetSifs () + m_phy->GetSlot ()
369  + m_phy->CalculatePhyPreambleAndHeaderDuration (*responseTxVector);
370 
374  m_channelAccessManager->NotifyAckTimeoutStartNow (timeout);
375 
376  ForwardPsduDown (triggerPsdu, acknowledgment->muBarTxVector);
377  return;
378  }
379  }
380  /*
381  * Acknowledgment requested by MU-BAR TFs aggregated to PSDUs in the DL MU PPDU
382  */
384  {
385  WifiDlMuAggregateTf* acknowledgment = static_cast<WifiDlMuAggregateTf*> (m_txParams.m_acknowledgment.get ());
386 
387  // record the set of stations expected to send a BlockAck frame
388  m_staExpectTbPpduFrom.clear ();
389 
390  for (auto& station : acknowledgment->stationsReplyingWithBlockAck)
391  {
392  m_staExpectTbPpduFrom.insert (station.first);
393  // check that the station that is expected to send a BlockAck frame is
394  // actually the receiver of a PSDU
395  auto psduMapIt = std::find_if (m_psduMap.begin (), m_psduMap.end (),
396  [&station] (std::pair<uint16_t, Ptr<WifiPsdu>> psdu)
397  { return psdu.second->GetAddr1 () == station.first; });
398 
399  NS_ASSERT (psduMapIt != m_psduMap.end ());
400  // add a MU-BAR Trigger Frame to the PSDU
401  std::vector<Ptr<WifiMacQueueItem>> mpduList (psduMapIt->second->begin (), psduMapIt->second->end ());
402  NS_ASSERT (mpduList.size () == psduMapIt->second->GetNMpdus ());
403  // set the Length field of the response TXVECTOR, which is needed to correctly
404  // set the UL Length field of the MU-BAR Trigger Frame
405  station.second.blockAckTxVector.SetLength (acknowledgment->ulLength);
406  mpduList.push_back (PrepareMuBar (station.second.blockAckTxVector,
407  {{psduMapIt->first, station.second.barHeader}}));
408  psduMapIt->second = Create<WifiPsdu> (std::move (mpduList));
409  }
410 
412  responseTxVector = &acknowledgment->stationsReplyingWithBlockAck.begin ()->second.blockAckTxVector;
413  }
414  /*
415  * Basic Trigger Frame starting an UL MU transmission
416  */
417  else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA)
418  {
419  // the PSDU map being sent must contain a (Basic) Trigger Frame
420  NS_ASSERT (m_psduMap.size () == 1 && m_psduMap.begin ()->first == SU_STA_ID
421  && (mpdu = *m_psduMap.begin ()->second->begin ())->GetHeader ().IsTrigger ());
422 
423  WifiUlMuMultiStaBa* acknowledgment = static_cast<WifiUlMuMultiStaBa*> (m_txParams.m_acknowledgment.get ());
424 
425  // record the set of stations solicited by this Trigger Frame
426  m_staExpectTbPpduFrom.clear ();
427 
428  for (const auto& station : acknowledgment->stationsReceivingMultiStaBa)
429  {
430  m_staExpectTbPpduFrom.insert (station.first.first);
431  }
432 
433  // Reset stationsReceivingMultiStaBa, which will be filled as soon as
434  // TB PPDUs are received
435  acknowledgment->stationsReceivingMultiStaBa.clear ();
436  acknowledgment->baType.m_bitmapLen.clear ();
437 
438  // Add a SIFS and the TB PPDU duration to the acknowledgment time of the
439  // Trigger Frame, so that its Duration/ID is correctly computed
440  NS_ASSERT (m_muScheduler != 0);
441  acknowledgment->acknowledgmentTime += m_mac->GetWifiPhy ()->GetSifs ()
442  + m_muScheduler->GetUlMuInfo ().tbPpduDuration;
443 
445  responseTxVector = &acknowledgment->tbPpduTxVector;
446  }
447  /*
448  * BSRP Trigger Frame
449  */
450  else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::NONE
451  && !m_txParams.m_txVector.IsUlMu ()
452  && m_psduMap.size () == 1 && m_psduMap.begin ()->first == SU_STA_ID
453  && (mpdu = *m_psduMap.begin ()->second->begin ())->GetHeader ().IsTrigger ())
454  {
455  CtrlTriggerHeader trigger;
456  mpdu->GetPacket ()->PeekHeader (trigger);
457  NS_ASSERT (trigger.IsBsrp ());
458  NS_ASSERT (m_apMac != 0);
459 
460  // record the set of stations solicited by this Trigger Frame
461  m_staExpectTbPpduFrom.clear ();
462 
463  for (const auto& userInfo : trigger)
464  {
465  auto staIt = m_apMac->GetStaList ().find (userInfo.GetAid12 ());
466  NS_ASSERT (staIt != m_apMac->GetStaList ().end ());
467  m_staExpectTbPpduFrom.insert (staIt->second);
468  }
469 
470  // Add a SIFS and the TB PPDU duration to the acknowledgment time of the
471  // Trigger Frame, so that its Duration/ID is correctly computed
472  WifiNoAck* acknowledgment = static_cast<WifiNoAck*> (m_txParams.m_acknowledgment.get ());
473  txVector = trigger.GetHeTbTxVector (trigger.begin ()->GetAid12 ());
474  acknowledgment->acknowledgmentTime += m_mac->GetWifiPhy ()->GetSifs ()
475  + HePhy::ConvertLSigLengthToHeTbPpduDuration (trigger.GetUlLength (),
476  txVector,
477  m_phy->GetPhyBand ());
478 
480  responseTxVector = &txVector;
481  }
482  /*
483  * TB PPDU solicited by a Basic Trigger Frame
484  */
485  else if (m_txParams.m_txVector.IsUlMu ()
486  && m_txParams.m_acknowledgment->method == WifiAcknowledgment::ACK_AFTER_TB_PPDU)
487  {
488  NS_ASSERT (m_psduMap.size () == 1);
490  NS_ASSERT (m_staMac != 0 && m_staMac->IsAssociated ());
491  txVector = m_mac->GetWifiRemoteStationManager ()->GetBlockAckTxVector (m_psduMap.begin ()->second->GetAddr1 (),
492  m_txParams.m_txVector);
493  responseTxVector = &txVector;
494  }
495  /*
496  * QoS Null frames solicited by a BSRP Trigger Frame
497  */
498  else if (m_txParams.m_txVector.IsUlMu ()
499  && m_txParams.m_acknowledgment->method == WifiAcknowledgment::NONE)
500  {
501  // No response is expected, so do nothing.
502  }
503  else
504  {
505  NS_ABORT_MSG ("Unable to handle the selected acknowledgment method ("
506  << m_txParams.m_acknowledgment.get () << ")");
507  }
508 
509  // create a map of Ptr<const WifiPsdu>, as required by the PHY
510  WifiConstPsduMap psduMap;
511  for (const auto& psdu : m_psduMap)
512  {
513  psduMap.emplace (psdu.first, psdu.second);
514  }
515 
516  Time txDuration;
517  if (m_txParams.m_txVector.IsUlMu ())
518  {
519  txDuration = HePhy::ConvertLSigLengthToHeTbPpduDuration (m_txParams.m_txVector.GetLength (),
520  m_txParams.m_txVector,
521  m_phy->GetPhyBand ());
522  }
523  else
524  {
525  txDuration = m_phy->CalculateTxDuration (psduMap, m_txParams.m_txVector, m_phy->GetPhyBand ());
526 
527  // Set Duration/ID
528  Time durationId = GetPsduDurationId (txDuration, m_txParams);
529  for (auto& psdu : m_psduMap)
530  {
531  psdu.second->SetDuration (durationId);
532  }
533  }
534 
535  if (timerType == WifiTxTimer::NOT_RUNNING)
536  {
537  if (!m_txParams.m_txVector.IsUlMu ())
538  {
540  }
541  }
542  else
543  {
544  Time timeout = txDuration + m_phy->GetSifs () + m_phy->GetSlot ()
545  + m_phy->CalculatePhyPreambleAndHeaderDuration (*responseTxVector);
546  m_channelAccessManager->NotifyAckTimeoutStartNow (timeout);
547 
548  // start timer
549  switch (timerType)
550  {
552  NS_ASSERT (mpdu != nullptr);
553  m_txTimer.Set (timerType, timeout, &HeFrameExchangeManager::NormalAckTimeout,
554  this, mpdu, m_txParams.m_txVector);
555  break;
557  NS_ASSERT (psdu != nullptr);
558  m_txTimer.Set (timerType, timeout, &HeFrameExchangeManager::BlockAckTimeout,
559  this, psdu, m_txParams.m_txVector);
560  break;
562  m_txTimer.Set (timerType, timeout, &HeFrameExchangeManager::BlockAcksInTbPpduTimeout, this,
563  &m_psduMap, &m_staExpectTbPpduFrom, m_staExpectTbPpduFrom.size ());
564  break;
567  m_txTimer.Set (timerType, timeout, &HeFrameExchangeManager::TbPpduTimeout, this,
568  &m_psduMap, &m_staExpectTbPpduFrom, m_staExpectTbPpduFrom.size ());
569  break;
572  this, m_psduMap.begin ()->second, m_txParams.m_txVector);
573  break;
574  default:
575  NS_ABORT_MSG ("Unknown timer type: " << timerType);
576  break;
577  }
578  }
579 
580  // transmit the map of PSDUs
581  ForwardPsduMapDown (psduMap, m_txParams.m_txVector);
582 }
583 
584 void
585 HeFrameExchangeManager::ForwardPsduMapDown (WifiConstPsduMap psduMap, WifiTxVector& txVector)
586 {
587  NS_LOG_FUNCTION (this << psduMap << txVector);
588 
589  for (const auto& psdu : psduMap)
590  {
591  NS_LOG_DEBUG ("Transmitting: [STAID=" << psdu.first << ", " << *psdu.second << "]");
592  }
593  NS_LOG_DEBUG ("TXVECTOR: " << txVector);
594  for (const auto& psdu : psduMap)
595  {
596  NotifyTxToEdca (psdu.second);
597  }
598  if (psduMap.size () > 1 || psduMap.begin ()->second->IsAggregate () || psduMap.begin ()->second->IsSingle ())
599  {
600  txVector.SetAggregation (true);
601  }
602 
603  m_phy->Send (psduMap, txVector);
604 }
605 
607 HeFrameExchangeManager::PrepareMuBar (const WifiTxVector& responseTxVector,
608  std::map<uint16_t, CtrlBAckRequestHeader> recipients) const
609 {
610  NS_LOG_FUNCTION (this << responseTxVector);
611  NS_ASSERT (responseTxVector.GetHeMuUserInfoMap ().size () == recipients.size ());
612  NS_ASSERT (!recipients.empty ());
613 
614  CtrlTriggerHeader muBar (TriggerFrameType::MU_BAR_TRIGGER, responseTxVector);
615  SetTargetRssi (muBar);
616  Mac48Address rxAddress;
617 
618  // Add the Trigger Dependent User Info subfield to every User Info field
619  for (auto& userInfo : muBar)
620  {
621  auto recipientIt = recipients.find (userInfo.GetAid12 ());
622  NS_ASSERT (recipientIt != recipients.end ());
623 
624  // Store the BAR in the Trigger Dependent User Info subfield
625  userInfo.SetMuBarTriggerDepUserInfo (recipientIt->second);
626  }
627 
628  Ptr<Packet> bar = Create<Packet> ();
629  bar->AddHeader (muBar);
630  // "If the Trigger frame has one User Info field and the AID12 subfield of the
631  // User Info contains the AID of a STA, then the RA field is set to the address
632  // of that STA". Otherwise, it is set to the broadcast address (Sec. 9.3.1.23 -
633  // 802.11ax amendment draft 3.0)
634  if (muBar.GetNUserInfoFields () > 1)
635  {
636  rxAddress = Mac48Address::GetBroadcast ();
637  }
638  else
639  {
640  NS_ASSERT (m_apMac != 0);
641  rxAddress = m_apMac->GetStaList ().at (recipients.begin ()->first);
642  }
643 
644  WifiMacHeader hdr;
646  hdr.SetAddr1 (rxAddress);
647  hdr.SetAddr2 (m_self);
648  hdr.SetDsNotTo ();
649  hdr.SetDsNotFrom ();
650  hdr.SetNoRetry ();
651  hdr.SetNoMoreFragments ();
652 
653  return Create<WifiMacQueueItem> (bar, hdr);
654 }
655 
656 void
657 HeFrameExchangeManager::CalculateAcknowledgmentTime (WifiAcknowledgment* acknowledgment) const
658 {
659  NS_LOG_FUNCTION (this << acknowledgment);
660  NS_ASSERT (acknowledgment != nullptr);
661 
662  /*
663  * Acknowledgment via a sequence of BlockAckReq and BlockAck frames
664  */
665  if (acknowledgment->method == WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE)
666  {
667  WifiDlMuBarBaSequence* dlMuBarBaAcknowledgment = static_cast<WifiDlMuBarBaSequence*> (acknowledgment);
668 
669  Time duration = Seconds (0);
670 
671  // normal ack or implicit BAR policy can be used for (no more than) one receiver
672  NS_ABORT_IF (dlMuBarBaAcknowledgment->stationsReplyingWithNormalAck.size ()
673  + dlMuBarBaAcknowledgment->stationsReplyingWithBlockAck.size () > 1);
674 
675  if (!dlMuBarBaAcknowledgment->stationsReplyingWithNormalAck.empty ())
676  {
677  const auto& info = dlMuBarBaAcknowledgment->stationsReplyingWithNormalAck.begin ()->second;
678  duration += m_phy->GetSifs ()
679  + m_phy->CalculateTxDuration (GetAckSize (), info.ackTxVector, m_phy->GetPhyBand ());
680  }
681 
682  if (!dlMuBarBaAcknowledgment->stationsReplyingWithBlockAck.empty ())
683  {
684  const auto& info = dlMuBarBaAcknowledgment->stationsReplyingWithBlockAck.begin ()->second;
685  duration += m_phy->GetSifs ()
686  + m_phy->CalculateTxDuration (GetBlockAckSize (info.baType),
687  info.blockAckTxVector, m_phy->GetPhyBand ());
688  }
689 
690  for (const auto& stations : dlMuBarBaAcknowledgment->stationsSendBlockAckReqTo)
691  {
692  const auto& info = stations.second;
693  duration += m_phy->GetSifs ()
694  + m_phy->CalculateTxDuration (GetBlockAckRequestSize (info.barType),
695  info.blockAckReqTxVector, m_phy->GetPhyBand ())
696  + m_phy->GetSifs ()
697  + m_phy->CalculateTxDuration (GetBlockAckSize (info.baType),
698  info.blockAckTxVector, m_phy->GetPhyBand ());
699  }
700 
701  dlMuBarBaAcknowledgment->acknowledgmentTime = duration;
702  }
703  /*
704  * Acknowledgment via a MU-BAR Trigger Frame sent as single user frame
705  */
706  else if (acknowledgment->method == WifiAcknowledgment::DL_MU_TF_MU_BAR)
707  {
708  WifiDlMuTfMuBar* dlMuTfMuBarAcknowledgment = static_cast<WifiDlMuTfMuBar*> (acknowledgment);
709 
710  Time duration = Seconds (0);
711 
712  for (const auto& stations : dlMuTfMuBarAcknowledgment->stationsReplyingWithBlockAck)
713  {
714  // compute the TX duration of the BlockAck response from this receiver.
715  const auto& info = stations.second;
716  NS_ASSERT (info.blockAckTxVector.GetHeMuUserInfoMap ().size () == 1);
717  uint16_t staId = info.blockAckTxVector.GetHeMuUserInfoMap ().begin ()->first;
718  Time currBlockAckDuration = m_phy->CalculateTxDuration (GetBlockAckSize (info.baType),
719  info.blockAckTxVector,
720  m_phy->GetPhyBand (),
721  staId);
722  // update the max duration among all the Block Ack responses
723  if (currBlockAckDuration > duration)
724  {
725  duration = currBlockAckDuration;
726  }
727  }
728 
729  // The computed duration may not be coded exactly in the L-SIG length, hence determine
730  // the exact duration corresponding to the value that will be coded in this field.
731  dlMuTfMuBarAcknowledgment->ulLength = HePhy::ConvertHeTbPpduDurationToLSigLength (duration, m_phy->GetPhyBand ());
732  WifiTxVector& txVector = dlMuTfMuBarAcknowledgment->stationsReplyingWithBlockAck.begin ()->second.blockAckTxVector;
733  duration = HePhy::ConvertLSigLengthToHeTbPpduDuration (dlMuTfMuBarAcknowledgment->ulLength,
734  txVector, m_phy->GetPhyBand ());
735 
736  uint32_t muBarSize = GetMuBarSize (dlMuTfMuBarAcknowledgment->barTypes);
737  if (dlMuTfMuBarAcknowledgment->muBarTxVector.GetModulationClass () >= WIFI_MOD_CLASS_VHT)
738  {
739  // MU-BAR TF will be sent as an S-MPDU
740  muBarSize = MpduAggregator::GetSizeIfAggregated (muBarSize, 0);
741  }
742  dlMuTfMuBarAcknowledgment->acknowledgmentTime = m_phy->GetSifs ()
743  + m_phy->CalculateTxDuration (muBarSize,
744  dlMuTfMuBarAcknowledgment->muBarTxVector,
745  m_phy->GetPhyBand ())
746  + m_phy->GetSifs () + duration;
747  }
748  /*
749  * Acknowledgment requested by MU-BAR TFs aggregated to PSDUs in the DL MU PPDU
750  */
751  else if (acknowledgment->method == WifiAcknowledgment::DL_MU_AGGREGATE_TF)
752  {
753  WifiDlMuAggregateTf* dlMuAggrTfAcknowledgment = static_cast<WifiDlMuAggregateTf*> (acknowledgment);
754 
755  Time duration = Seconds (0);
756 
757  for (const auto& stations : dlMuAggrTfAcknowledgment->stationsReplyingWithBlockAck)
758  {
759  // compute the TX duration of the BlockAck response from this receiver.
760  const auto& info = stations.second;
761  NS_ASSERT (info.blockAckTxVector.GetHeMuUserInfoMap ().size () == 1);
762  uint16_t staId = info.blockAckTxVector.GetHeMuUserInfoMap ().begin ()->first;
763  Time currBlockAckDuration = m_phy->CalculateTxDuration (GetBlockAckSize (info.baType),
764  info.blockAckTxVector,
765  m_phy->GetPhyBand (),
766  staId);
767  // update the max duration among all the Block Ack responses
768  if (currBlockAckDuration > duration)
769  {
770  duration = currBlockAckDuration;
771  }
772  }
773 
774  // The computed duration may not be coded exactly in the L-SIG length, hence determine
775  // the exact duration corresponding to the value that will be coded in this field.
776  dlMuAggrTfAcknowledgment->ulLength = HePhy::ConvertHeTbPpduDurationToLSigLength (duration, m_phy->GetPhyBand ());
777  WifiTxVector& txVector = dlMuAggrTfAcknowledgment->stationsReplyingWithBlockAck.begin ()->second.blockAckTxVector;
778  duration = HePhy::ConvertLSigLengthToHeTbPpduDuration (dlMuAggrTfAcknowledgment->ulLength,
779  txVector, m_phy->GetPhyBand ());
780 
781  dlMuAggrTfAcknowledgment->acknowledgmentTime = m_phy->GetSifs () + duration;
782  }
783  /*
784  * Basic Trigger Frame starting an UL MU transmission
785  */
786  else if (acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA)
787  {
788  WifiUlMuMultiStaBa* ulMuMultiStaBa = static_cast<WifiUlMuMultiStaBa*> (acknowledgment);
789 
790  Time duration = m_phy->CalculateTxDuration (GetBlockAckSize (ulMuMultiStaBa->baType),
791  ulMuMultiStaBa->multiStaBaTxVector,
792  m_phy->GetPhyBand ());
793  ulMuMultiStaBa->acknowledgmentTime = m_phy->GetSifs () + duration;
794  }
795  /*
796  * TB PPDU solicired by a Basic or BSRP Trigger Frame
797  */
798  else if (acknowledgment->method == WifiAcknowledgment::ACK_AFTER_TB_PPDU)
799  {
800  // The station solicited by the Trigger Frame does not have to account
801  // for the actual acknowledgment time since it is given the PPDU duration
802  // through the Trigger Frame
803  acknowledgment->acknowledgmentTime = Seconds (0);
804  }
805  else
806  {
807  VhtFrameExchangeManager::CalculateAcknowledgmentTime (acknowledgment);
808  }
809 }
810 
811 Time
812 HeFrameExchangeManager::GetTxDuration (uint32_t ppduPayloadSize, Mac48Address receiver,
813  const WifiTxParameters& txParams) const
814 {
815  if (!txParams.m_txVector.IsMu ())
816  {
817  return VhtFrameExchangeManager::GetTxDuration (ppduPayloadSize, receiver, txParams);
818  }
819 
820  NS_ASSERT_MSG (!txParams.m_txVector.IsDlMu () || m_apMac != 0, "DL MU can be done by an AP");
821  NS_ASSERT_MSG (!txParams.m_txVector.IsUlMu () || m_staMac != 0, "UL MU can be done by a STA");
822 
823  if (txParams.m_acknowledgment
824  && txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_AGGREGATE_TF)
825  {
826  // we need to account for the size of the aggregated MU-BAR Trigger Frame
827  WifiDlMuAggregateTf* acknowledgment = static_cast<WifiDlMuAggregateTf*> (txParams.m_acknowledgment.get ());
828 
829  const auto& info = acknowledgment->stationsReplyingWithBlockAck.find (receiver);
830  NS_ASSERT (info != acknowledgment->stationsReplyingWithBlockAck.end ());
831 
832  ppduPayloadSize = MpduAggregator::GetSizeIfAggregated (info->second.muBarSize, ppduPayloadSize);
833  }
834 
835  uint16_t staId = (txParams.m_txVector.IsDlMu () ? m_apMac->GetAssociationId (receiver)
836  : m_staMac->GetAssociationId ());
837  Time psduDuration = m_phy->CalculateTxDuration (ppduPayloadSize, txParams.m_txVector,
838  m_phy->GetPhyBand (), staId);
839 
840  return std::max (psduDuration, txParams.m_txDuration);
841 }
842 
843 void
844 HeFrameExchangeManager::TbPpduTimeout (WifiPsduMap* psduMap,
845  const std::set<Mac48Address>* staMissedTbPpduFrom,
846  std::size_t nSolicitedStations)
847 {
848  NS_LOG_FUNCTION (this << psduMap << staMissedTbPpduFrom->size () << nSolicitedStations);
849 
850  NS_ASSERT (psduMap != nullptr);
851  NS_ASSERT (psduMap->size () == 1 && psduMap->begin ()->first == SU_STA_ID
852  && psduMap->begin ()->second->GetHeader (0).IsTrigger ());
853 
854  // This method is called if some station(s) did not send a TB PPDU
855  NS_ASSERT (!staMissedTbPpduFrom->empty ());
856  NS_ASSERT (m_edca != 0);
857 
858  if (staMissedTbPpduFrom->size () == nSolicitedStations)
859  {
860  // no station replied, the transmission failed
861  m_edca->UpdateFailedCw ();
862 
863  TransmissionFailed ();
864  }
865  else if (!m_multiStaBaEvent.IsRunning ())
866  {
867  m_edca->ResetCw ();
868  TransmissionSucceeded ();
869  }
870 
871  m_psduMap.clear ();
872 }
873 
874 void
875 HeFrameExchangeManager::BlockAcksInTbPpduTimeout (WifiPsduMap* psduMap,
876  const std::set<Mac48Address>* staMissedBlockAckFrom,
877  std::size_t nSolicitedStations)
878 {
879  NS_LOG_FUNCTION (this << psduMap << nSolicitedStations);
880 
881  NS_ASSERT (psduMap != nullptr);
882  NS_ASSERT (m_txParams.m_acknowledgment
883  && (m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_AGGREGATE_TF
884  || m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_TF_MU_BAR));
885 
886  // This method is called if some station(s) did not send a BlockAck frame in a TB PPDU
887  NS_ASSERT (!staMissedBlockAckFrom->empty ());
888 
889  bool resetCw;
890 
891  if (staMissedBlockAckFrom->size () == nSolicitedStations)
892  {
893  // no station replied, the transmission failed
894  // call ReportDataFailed to increase SRC/LRC
895  m_mac->GetWifiRemoteStationManager ()->ReportDataFailed (*psduMap->begin ()->second->begin ());
896  resetCw = false;
897  }
898  else
899  {
900  // the transmission succeeded
901  resetCw = true;
902  }
903 
904  m_triggerFrame = nullptr; // this is strictly needed for DL_MU_TF_MU_BAR only
905 
906  for (const auto& sta : *staMissedBlockAckFrom)
907  {
908  Ptr<WifiPsdu> psdu = GetPsduTo (sta, *psduMap);
909  NS_ASSERT (psdu != nullptr);
910  // If the QSRC[AC] or the QLRC[AC] has reached dot11ShortRetryLimit or dot11LongRetryLimit
911  // respectively, CW[AC] shall be reset to CWmin[AC] (sec. 10.22.2.2 of 802.11-2016).
912  // We should get that psduResetCw is the same for all PSDUs, but the handling of QSRC/QLRC
913  // needs to be aligned to the specifications.
914  bool psduResetCw;
915  MissedBlockAck (psdu, m_txParams.m_txVector, psduResetCw);
916  resetCw = resetCw || psduResetCw;
917  }
918 
919  NS_ASSERT (m_edca != 0);
920 
921  if (resetCw)
922  {
923  m_edca->ResetCw ();
924  }
925  else
926  {
927  m_edca->UpdateFailedCw ();
928  }
929 
930  if (staMissedBlockAckFrom->size () == nSolicitedStations)
931  {
932  // no station replied, the transmission failed
933  TransmissionFailed ();
934  }
935  else
936  {
937  TransmissionSucceeded ();
938  }
939  m_psduMap.clear ();
940 }
941 
942 void
943 HeFrameExchangeManager::BlockAckAfterTbPpduTimeout (Ptr<WifiPsdu> psdu, const WifiTxVector& txVector)
944 {
945  NS_LOG_FUNCTION (this << *psdu << txVector);
946 
947  bool resetCw;
948 
949  // call ReportDataFailed to increase SRC/LRC
950  m_mac->GetWifiRemoteStationManager ()->ReportDataFailed (*psdu->begin ());
951 
952  MissedBlockAck (psdu, m_txParams.m_txVector, resetCw);
953 
954  // This is a PSDU sent in a TB PPDU. An HE STA resumes the EDCA backoff procedure
955  // without modifying CW or the backoff counter for the associated EDCAF, after
956  // transmission of an MPDU in a TB PPDU regardless of whether the STA has received
957  // the corresponding acknowledgment frame in response to the MPDU sent in the TB PPDU
958  // (Sec. 10.22.2.2 of 11ax Draft 3.0)
959  m_psduMap.clear ();
960 }
961 
963 HeFrameExchangeManager::GetHeTbTxVector (CtrlTriggerHeader trigger, Mac48Address triggerSender) const
964 {
965  NS_ASSERT (triggerSender != m_self); //TxPower information is used only by STAs, it is useless for the sending AP (which can directly use CtrlTriggerHeader::GetHeTbTxVector)
966  NS_ASSERT (m_staMac != nullptr);
967  uint16_t staId = m_staMac->GetAssociationId ();
968  auto userInfoIt = trigger.FindUserInfoWithAid (staId);
969  NS_ASSERT (userInfoIt != trigger.end ());
970 
971  WifiTxVector v = trigger.GetHeTbTxVector (staId);
972 
973  Ptr<HeConfiguration> heConfiguration = m_mac->GetHeConfiguration ();
974  NS_ASSERT_MSG (heConfiguration != 0, "This STA has to be an HE station to send an HE TB PPDU");
975  v.SetBssColor (heConfiguration->GetBssColor ());
976 
977  uint8_t powerLevel = m_mac->GetWifiRemoteStationManager ()->GetDefaultTxPowerLevel ();
995  int8_t pathLossDb = trigger.GetApTxPower () - static_cast<int8_t> (m_mac->GetWifiRemoteStationManager ()->GetMostRecentRssi (triggerSender)); //cast RSSI to be on equal footing with AP Tx power information
996  double reqTxPowerDbm = static_cast<double> (userInfoIt->GetUlTargetRssi () + pathLossDb);
997 
998  //Convert the transmit power to a power level
999  uint8_t numPowerLevels = m_phy->GetNTxPower ();
1000  if (numPowerLevels > 1)
1001  {
1002  double stepDbm = (m_phy->GetTxPowerEnd () - m_phy->GetTxPowerStart ()) / (numPowerLevels - 1);
1003  powerLevel = static_cast<uint8_t> (ceil ((reqTxPowerDbm - m_phy->GetTxPowerStart ()) / stepDbm)); //better be slightly above so as to satisfy target UL RSSI
1004  if (powerLevel > numPowerLevels)
1005  {
1006  powerLevel = numPowerLevels; //capping will trigger warning below
1007  }
1008  }
1009  if (reqTxPowerDbm > m_phy->GetPowerDbm (powerLevel))
1010  {
1011  NS_LOG_WARN ("The requested power level (" << reqTxPowerDbm << "dBm) cannot be satisfied (max: " << m_phy->GetTxPowerEnd () << "dBm)");
1012  }
1013  v.SetTxPowerLevel (powerLevel);
1014  NS_LOG_LOGIC ("UL power control: "
1015  << "input {pathLoss=" << pathLossDb << "dB, reqTxPower=" << reqTxPowerDbm << "dBm}"
1016  << " output {powerLevel=" << +powerLevel << " -> " << m_phy->GetPowerDbm (powerLevel) << "dBm}"
1017  << " PHY power capa {min=" << m_phy->GetTxPowerStart () << "dBm, max=" << m_phy->GetTxPowerEnd () << "dBm, levels:" << +numPowerLevels << "}");
1018 
1019  return v;
1020 }
1021 
1022 void
1023 HeFrameExchangeManager::SetTargetRssi (CtrlTriggerHeader& trigger) const
1024 {
1025  NS_LOG_FUNCTION (this);
1026  NS_ASSERT (m_apMac != 0);
1027 
1028  trigger.SetApTxPower (static_cast<int8_t> (m_phy->GetPowerDbm (m_mac->GetWifiRemoteStationManager ()->GetDefaultTxPowerLevel ())));
1029  for (auto& userInfo : trigger)
1030  {
1031  const auto staList = m_apMac->GetStaList ();
1032  auto itAidAddr = staList.find (userInfo.GetAid12 ());
1033  NS_ASSERT (itAidAddr != staList.end ());
1034  int8_t rssi = static_cast<int8_t> (m_mac->GetWifiRemoteStationManager ()->GetMostRecentRssi (itAidAddr->second));
1035  rssi = (rssi >= -20) ? -20 : ((rssi <= -110) ? -110 : rssi); //cap so as to keep within [-110; -20] dBm
1036  userInfo.SetUlTargetRssi (rssi);
1037  }
1038 }
1039 
1040 void
1041 HeFrameExchangeManager::SendMultiStaBlockAck (const WifiTxParameters& txParams)
1042 {
1043  NS_LOG_FUNCTION (this << &txParams);
1044 
1045  NS_ASSERT (m_apMac != 0);
1046  NS_ASSERT (txParams.m_acknowledgment
1047  && txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA);
1048  WifiUlMuMultiStaBa* acknowledgment = static_cast<WifiUlMuMultiStaBa*> (txParams.m_acknowledgment.get ());
1049 
1050  NS_ASSERT (!acknowledgment->stationsReceivingMultiStaBa.empty ());
1051 
1052  CtrlBAckResponseHeader blockAck;
1053  blockAck.SetType (acknowledgment->baType);
1054 
1055  Mac48Address receiver;
1056 
1057  for (const auto& staInfo : acknowledgment->stationsReceivingMultiStaBa)
1058  {
1059  receiver = staInfo.first.first;
1060  uint8_t tid = staInfo.first.second;
1061  std::size_t index = staInfo.second;
1062 
1063  blockAck.SetAid11 (m_apMac->GetAssociationId (receiver), index);
1064  blockAck.SetTidInfo (tid, index);
1065 
1066  if (tid == 14)
1067  {
1068  // All-ack context
1069  NS_LOG_DEBUG ("Multi-STA Block Ack: Sending All-ack to=" << receiver);
1070  blockAck.SetAckType (true, index);
1071  continue;
1072  }
1073 
1074  if (acknowledgment->baType.m_bitmapLen.at (index) == 0)
1075  {
1076  // Acknowledgment context
1077  NS_LOG_DEBUG ("Multi-STA Block Ack: Sending Ack to=" << receiver);
1078  blockAck.SetAckType (true, index);
1079  }
1080  else
1081  {
1082  // Block acknowledgment context
1083  blockAck.SetAckType (false, index);
1084 
1085  auto addressTidPair = staInfo.first;
1086  auto agreementIt = m_agreements.find (addressTidPair);
1087  NS_ASSERT (agreementIt != m_agreements.end ());
1088  agreementIt->second.FillBlockAckBitmap (&blockAck, index);
1089  NS_LOG_DEBUG ("Multi-STA Block Ack: Sending Block Ack with seq=" << blockAck.GetStartingSequence (index)
1090  << " to=" << receiver << " tid=" << +tid);
1091  }
1092  }
1093 
1094  WifiMacHeader hdr;
1096  hdr.SetAddr1 (acknowledgment->stationsReceivingMultiStaBa.size () == 1 ? receiver : Mac48Address::GetBroadcast ());
1097  hdr.SetAddr2 (m_self);
1098  hdr.SetDsNotFrom ();
1099  hdr.SetDsNotTo ();
1100 
1101  Ptr<Packet> packet = Create<Packet> ();
1102  packet->AddHeader (blockAck);
1103  Ptr<WifiPsdu> psdu = GetWifiPsdu (Create<WifiMacQueueItem> (packet, hdr),
1104  acknowledgment->multiStaBaTxVector);
1105 
1106  // The Duration/ID field in a BlockAck frame transmitted in response to a frame
1107  // carried in HE TB PPDU is set according to the multiple protection settings
1108  // (Sec. 9.2.5.7 of 802.11ax D3.0)
1109  Time txDuration = m_phy->CalculateTxDuration (GetBlockAckSize (acknowledgment->baType),
1110  acknowledgment->multiStaBaTxVector,
1111  m_phy->GetPhyBand ());
1112  WifiTxParameters params;
1113  // if the TXOP limit is null, GetPsduDurationId returns the acknowledgment time,
1114  // hence we set an method with acknowledgment time equal to zero.
1115  params.m_acknowledgment = std::unique_ptr<WifiAcknowledgment> (new WifiNoAck);
1116  psdu->SetDuration (GetPsduDurationId (txDuration, params));
1117 
1118  psdu->GetPayload (0)->AddPacketTag (m_muSnrTag);
1119 
1120  ForwardPsduDown (psdu, acknowledgment->multiStaBaTxVector);
1121 
1122  // continue with the TXOP if time remains
1123  m_psduMap.clear ();
1124  m_edca->ResetCw ();
1125  m_muSnrTag.Reset ();
1126  Simulator::Schedule (txDuration, &HeFrameExchangeManager::TransmissionSucceeded, this);
1127 }
1128 
1129 void
1130 HeFrameExchangeManager::ReceiveBasicTrigger (const CtrlTriggerHeader& trigger, const WifiMacHeader& hdr)
1131 {
1132  NS_LOG_FUNCTION (this << trigger << hdr);
1133  NS_ASSERT (trigger.IsBasic ());
1134  NS_ASSERT (m_staMac != 0 && m_staMac->IsAssociated ());
1135 
1136  NS_LOG_DEBUG ("Received a Trigger Frame (basic variant) soliciting a transmission");
1137 
1138  if (trigger.GetCsRequired () && hdr.GetAddr2 () != m_txopHolder && m_navEnd > Simulator::Now ())
1139  {
1140  NS_LOG_DEBUG ("Carrier Sensing required and channel busy, do nothing");
1141  return;
1142  }
1143 
1144  // Starting from the Preferred AC indicated in the Trigger Frame, check if there
1145  // is either a pending BlockAckReq frame or a data frame that can be transmitted
1146  // in the allocated time and is addressed to a station with which a Block Ack
1147  // agreement has been established.
1148 
1149  // create the sequence of TIDs to check
1150  std::vector<uint8_t> tids;
1151  uint16_t staId = m_staMac->GetAssociationId ();
1152  AcIndex preferredAc = trigger.FindUserInfoWithAid (staId)->GetPreferredAc ();
1153  auto acIt = wifiAcList.find (preferredAc);
1154  for (uint8_t i = 0; i < 4; i++)
1155  {
1156  NS_ASSERT (acIt != wifiAcList.end ());
1157  tids.push_back (acIt->second.GetHighTid ());
1158  tids.push_back (acIt->second.GetLowTid ());
1159 
1160  acIt++;
1161  if (acIt == wifiAcList.end ())
1162  {
1163  acIt = wifiAcList.begin ();
1164  }
1165  }
1166 
1168  Ptr<WifiPsdu> psdu;
1169  WifiTxParameters txParams;
1170  WifiTxVector tbTxVector = GetHeTbTxVector (trigger, hdr.GetAddr2 ());
1171  Time ppduDuration = HePhy::ConvertLSigLengthToHeTbPpduDuration (trigger.GetUlLength (),
1172  tbTxVector,
1173  m_phy->GetPhyBand ());
1174 
1175  for (const auto& tid : tids)
1176  {
1177  Ptr<QosTxop> edca = m_mac->GetQosTxop (tid);
1178 
1179  if (!edca->GetBaAgreementEstablished (hdr.GetAddr2 (), tid))
1180  {
1181  // no Block Ack agreement established for this TID
1182  continue;
1183  }
1184 
1185  txParams.Clear ();
1186  txParams.m_txVector = tbTxVector;
1187 
1188  // first, check if there is a pending BlockAckReq frame
1189  if ((mpdu = edca->GetBaManager ()->GetBar (false, tid, hdr.GetAddr2 ())) != 0
1190  && TryAddMpdu (mpdu, txParams, ppduDuration))
1191  {
1192  NS_LOG_DEBUG ("Sending a BAR within a TB PPDU");
1193  psdu = Create<WifiPsdu> (edca->GetBaManager ()->GetBar (true, tid, hdr.GetAddr2 ()), true);
1194  break;
1195  }
1196 
1197  // otherwise, check if a suitable data frame is available
1198  if ((mpdu = edca->PeekNextMpdu (tid, hdr.GetAddr2 ())) != 0)
1199  {
1200  Ptr<WifiMacQueueItem> item = edca->GetNextMpdu (mpdu, txParams, ppduDuration, false);
1201 
1202  if (item != 0)
1203  {
1204  // try A-MPDU aggregation
1205  std::vector<Ptr<WifiMacQueueItem>> mpduList = m_mpduAggregator->GetNextAmpdu (item, txParams,
1206  ppduDuration);
1207  psdu = (mpduList.size () > 1 ? Create<WifiPsdu> (std::move (mpduList))
1208  : Create<WifiPsdu> (item, true));
1209  break;
1210  }
1211  }
1212  }
1213 
1214  if (psdu != 0)
1215  {
1216  psdu->SetDuration (hdr.GetDuration () - m_phy->GetSifs () - ppduDuration);
1217  SendPsduMapWithProtection (WifiPsduMap {{staId, psdu}}, txParams);
1218  }
1219  else
1220  {
1221  // send QoS Null frames
1222  SendQosNullFramesInTbPpdu (trigger, hdr);
1223  }
1224 }
1225 
1226 void
1227 HeFrameExchangeManager::SendQosNullFramesInTbPpdu (const CtrlTriggerHeader& trigger, const WifiMacHeader& hdr)
1228 {
1229  NS_LOG_FUNCTION (this << trigger << hdr);
1230  NS_ASSERT (trigger.IsBasic () || trigger.IsBsrp ());
1231  NS_ASSERT (m_staMac != 0 && m_staMac->IsAssociated ());
1232 
1233  NS_LOG_DEBUG ("Requested to send QoS Null frames");
1234 
1235  if (trigger.GetCsRequired () && hdr.GetAddr2 () != m_txopHolder && m_navEnd > Simulator::Now ())
1236  {
1237  NS_LOG_DEBUG ("Carrier Sensing required and channel busy, do nothing");
1238  return;
1239  }
1240 
1241  WifiMacHeader header;
1242  header.SetType (WIFI_MAC_QOSDATA_NULL);
1243  header.SetAddr1 (hdr.GetAddr2 ());
1244  header.SetAddr2 (m_self);
1245  header.SetAddr3 (hdr.GetAddr2 ());
1246  header.SetDsTo ();
1247  header.SetDsNotFrom ();
1248  // TR3: Sequence numbers for transmitted QoS (+)Null frames may be set
1249  // to any value. (Table 10-3 of 802.11-2016)
1250  header.SetSequenceNumber (0);
1251  // Set the EOSP bit so that NotifyTxToEdca will add the Queue Size
1252  header.SetQosEosp ();
1253 
1254  WifiTxParameters txParams;
1255  txParams.m_txVector = GetHeTbTxVector (trigger, hdr.GetAddr2 ());
1256  txParams.m_protection = std::unique_ptr<WifiProtection> (new WifiNoProtection);
1257  txParams.m_acknowledgment = std::unique_ptr<WifiAcknowledgment> (new WifiNoAck);
1258 
1259  Time ppduDuration = HePhy::ConvertLSigLengthToHeTbPpduDuration (trigger.GetUlLength (),
1260  txParams.m_txVector,
1261  m_phy->GetPhyBand ());
1262  header.SetDuration (hdr.GetDuration () - m_phy->GetSifs () - ppduDuration);
1263 
1264  Ptr<WifiMacQueueItem> mpdu;
1265  std::vector<Ptr<WifiMacQueueItem>> mpduList;
1266  uint8_t tid = 0;
1267  header.SetQosTid (tid);
1268 
1269  while (tid < 8
1270  && IsWithinSizeAndTimeLimits (txParams.GetSizeIfAddMpdu (mpdu = Create<WifiMacQueueItem> (Create<Packet> (),
1271  header)),
1272  hdr.GetAddr2 (), txParams, ppduDuration))
1273  {
1274  NS_LOG_DEBUG ("Aggregating a QoS Null frame with tid=" << +tid);
1275  // We could call TryAddMpdu instead of IsWithinSizeAndTimeLimits above in order to
1276  // get the TX parameters updated automatically. However, aggregating the QoS Null
1277  // frames might fail because MPDU aggregation is disabled by default for VO
1278  // and BK. Therefore, we skip the check on max A-MPDU size and only update the
1279  // TX parameters below.
1280  txParams.m_acknowledgment = GetAckManager ()->TryAddMpdu (mpdu, txParams);
1281  txParams.AddMpdu (mpdu);
1282  UpdateTxDuration (mpdu->GetHeader ().GetAddr1 (), txParams);
1283  mpduList.push_back (mpdu);
1284  header.SetQosTid (++tid);
1285  }
1286 
1287  if (mpduList.empty ())
1288  {
1289  NS_LOG_DEBUG ("Not enough time to send a QoS Null frame");
1290  return;
1291  }
1292 
1293  Ptr<WifiPsdu> psdu = (mpduList.size () > 1 ? Create<WifiPsdu> (std::move (mpduList))
1294  : Create<WifiPsdu> (mpduList.front (), true));
1295  uint16_t staId = m_staMac->GetAssociationId ();
1296  SendPsduMapWithProtection (WifiPsduMap {{staId, psdu}}, txParams);
1297 }
1298 
1299 void
1300 HeFrameExchangeManager::ReceiveMpdu (Ptr<WifiMacQueueItem> mpdu, RxSignalInfo rxSignalInfo,
1301  const WifiTxVector& txVector, bool inAmpdu)
1302 {
1303  // The received MPDU is either broadcast or addressed to this station
1304  NS_ASSERT (mpdu->GetHeader ().GetAddr1 ().IsBroadcast ()
1305  || mpdu->GetHeader ().GetAddr1 () == m_self);
1306 
1307  const WifiMacHeader& hdr = mpdu->GetHeader ();
1308 
1309  if (txVector.IsUlMu () && m_txTimer.IsRunning ()
1310  && m_txTimer.GetReason () == WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF)
1311  {
1312  Mac48Address sender = hdr.GetAddr2 ();
1313  NS_ASSERT (m_txParams.m_acknowledgment
1314  && m_txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA);
1315  WifiUlMuMultiStaBa* acknowledgment = static_cast<WifiUlMuMultiStaBa*> (m_txParams.m_acknowledgment.get ());
1316  std::size_t index = acknowledgment->baType.m_bitmapLen.size ();
1317 
1318  if (m_staExpectTbPpduFrom.find (sender) == m_staExpectTbPpduFrom.end ())
1319  {
1320  NS_LOG_WARN ("Received a TB PPDU from an unexpected station: " << sender);
1321  return;
1322  }
1323 
1324  if (hdr.IsBlockAckReq ())
1325  {
1326  NS_LOG_DEBUG ("Received a BlockAckReq in a TB PPDU from " << sender);
1327 
1328  CtrlBAckRequestHeader blockAckReq;
1329  mpdu->GetPacket ()->PeekHeader (blockAckReq);
1330  NS_ABORT_MSG_IF (blockAckReq.IsMultiTid (), "Multi-TID BlockAckReq not supported");
1331  uint8_t tid = blockAckReq.GetTidInfo ();
1332  auto agreementIt = m_agreements.find ({sender, tid});
1333  NS_ASSERT (agreementIt != m_agreements.end ());
1334  agreementIt->second.NotifyReceivedBar (blockAckReq.GetStartingSequence ());
1335 
1336  // Block Acknowledgment context
1337  acknowledgment->stationsReceivingMultiStaBa.emplace (std::make_pair (sender, tid), index);
1338  acknowledgment->baType.m_bitmapLen.push_back (GetBlockAckType (sender, tid).m_bitmapLen.at (0));
1339  uint16_t staId = txVector.GetHeMuUserInfoMap ().begin ()->first;
1340  m_muSnrTag.Set (staId, rxSignalInfo.snr);
1341  }
1342  else if (hdr.IsQosData () && !inAmpdu && hdr.GetQosAckPolicy () == WifiMacHeader::NORMAL_ACK)
1343  {
1344  NS_LOG_DEBUG ("Received an S-MPDU in a TB PPDU from " << sender << " (" << *mpdu << ")");
1345 
1346  uint8_t tid = hdr.GetQosTid ();
1347  auto agreementIt = m_agreements.find ({sender, tid});
1348  NS_ASSERT (agreementIt != m_agreements.end ());
1349  agreementIt->second.NotifyReceivedMpdu (mpdu);
1350 
1351  // Acknowledgment context of Multi-STA Block Acks
1352  acknowledgment->stationsReceivingMultiStaBa.emplace (std::make_pair (sender, tid), index);
1353  acknowledgment->baType.m_bitmapLen.push_back (0);
1354  uint16_t staId = txVector.GetHeMuUserInfoMap ().begin ()->first;
1355  m_muSnrTag.Set (staId, rxSignalInfo.snr);
1356  }
1357  else if (!(hdr.IsQosData () && !hdr.HasData () && !inAmpdu))
1358  {
1359  // The other case handled by this function is when we receive a QoS Null frame
1360  // that is not in an A-MPDU. For all other cases, the reception is handled by
1361  // parent classes. In particular, in case of a QoS data frame in A-MPDU, we
1362  // have to wait until the A-MPDU reception is completed, but we let the
1363  // parent classes notify the Block Ack agreement of the reception of this MPDU
1364  VhtFrameExchangeManager::ReceiveMpdu (mpdu, rxSignalInfo, txVector, inAmpdu);
1365  return;
1366  }
1367 
1368  // Schedule the transmission of a Multi-STA BlockAck frame if needed
1369  if (!acknowledgment->stationsReceivingMultiStaBa.empty () && !m_multiStaBaEvent.IsRunning ())
1370  {
1371  m_multiStaBaEvent = Simulator::Schedule (m_phy->GetSifs (),
1372  &HeFrameExchangeManager::SendMultiStaBlockAck,
1373  this, std::cref (m_txParams));
1374  }
1375 
1376  // remove the sender from the set of stations that are expected to send a TB PPDU
1377  m_staExpectTbPpduFrom.erase (sender);
1378 
1379  if (m_staExpectTbPpduFrom.empty ())
1380  {
1381  // we do not expect any other BlockAck frame
1382  m_txTimer.Cancel ();
1383  m_channelAccessManager->NotifyAckTimeoutResetNow ();
1384 
1385  if (!m_multiStaBaEvent.IsRunning ())
1386  {
1387  // all of the stations that replied with a TB PPDU sent QoS Null frames.
1388  NS_LOG_DEBUG ("Continue the TXOP");
1389  m_psduMap.clear ();
1390  m_edca->ResetCw ();
1391  TransmissionSucceeded ();
1392  }
1393  }
1394 
1395  // the received TB PPDU has been processed
1396  return;
1397  }
1398 
1399  if (hdr.IsCtl ())
1400  {
1401  if (hdr.IsAck () && m_txTimer.IsRunning ()
1402  && m_txTimer.GetReason () == WifiTxTimer::WAIT_NORMAL_ACK_AFTER_DL_MU_PPDU)
1403  {
1404  NS_ASSERT (hdr.GetAddr1 () == m_self);
1405  NS_ASSERT (m_txParams.m_acknowledgment);
1406  NS_ASSERT (m_txParams.m_acknowledgment->method == WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE);
1407 
1408  WifiDlMuBarBaSequence* acknowledgment = static_cast<WifiDlMuBarBaSequence*> (m_txParams.m_acknowledgment.get ());
1409  NS_ASSERT (acknowledgment->stationsReplyingWithNormalAck.size () == 1);
1410  NS_ASSERT (m_apMac != 0);
1411  uint16_t staId = m_apMac->GetAssociationId (acknowledgment->stationsReplyingWithNormalAck.begin ()->first);
1412  auto it = m_psduMap.find (staId);
1413  NS_ASSERT (it != m_psduMap.end ());
1414  NS_ASSERT (it->second->GetAddr1 () == acknowledgment->stationsReplyingWithNormalAck.begin ()->first);
1415  SnrTag tag;
1416  mpdu->GetPacket ()->PeekPacketTag (tag);
1417  ReceivedNormalAck (*it->second->begin (), m_txParams.m_txVector, txVector, rxSignalInfo, tag.Get ());
1418  }
1419  else if (hdr.IsBlockAck () && m_txTimer.IsRunning ()
1420  && m_txTimer.GetReason () == WifiTxTimer::WAIT_BLOCK_ACKS_IN_TB_PPDU)
1421  {
1422  Mac48Address sender = hdr.GetAddr2 ();
1423  NS_LOG_DEBUG ("Received BlockAck in TB PPDU from=" << sender);
1424 
1425  SnrTag tag;
1426  mpdu->GetPacket ()->PeekPacketTag (tag);
1427 
1428  // notify the Block Ack Manager
1429  CtrlBAckResponseHeader blockAck;
1430  mpdu->GetPacket ()->PeekHeader (blockAck);
1431  uint8_t tid = blockAck.GetTidInfo ();
1432  std::pair<uint16_t,uint16_t> ret = GetBaManager (tid)->NotifyGotBlockAck (blockAck, hdr.GetAddr2 (),
1433  {tid});
1434  m_mac->GetWifiRemoteStationManager ()->ReportAmpduTxStatus (hdr.GetAddr2 (), ret.first, ret.second,
1435  rxSignalInfo.snr, tag.Get (), m_txParams.m_txVector);
1436 
1437  // remove the sender from the set of stations that are expected to send a BlockAck
1438  if (m_staExpectTbPpduFrom.erase (sender) == 0)
1439  {
1440  NS_LOG_WARN ("Received a BlockAck from an unexpected stations: " << sender);
1441  return;
1442  }
1443 
1444  if (m_staExpectTbPpduFrom.empty ())
1445  {
1446  // we do not expect any other BlockAck frame
1447  m_txTimer.Cancel ();
1448  m_channelAccessManager->NotifyAckTimeoutResetNow ();
1449  m_triggerFrame = nullptr; // this is strictly needed for DL_MU_TF_MU_BAR only
1450 
1451  m_edca->ResetCw ();
1452  m_psduMap.clear ();
1453  TransmissionSucceeded ();
1454  }
1455  }
1456  else if (hdr.IsBlockAck () && m_txTimer.IsRunning ()
1457  && m_txTimer.GetReason () == WifiTxTimer::WAIT_BLOCK_ACK_AFTER_TB_PPDU)
1458  {
1459  CtrlBAckResponseHeader blockAck;
1460  mpdu->GetPacket ()->PeekHeader (blockAck);
1461 
1462  NS_ABORT_MSG_IF (!blockAck.IsMultiSta (),
1463  "A Multi-STA BlockAck is expected after a TB PPDU");
1464  NS_LOG_DEBUG ("Received a Multi-STA BlockAck from=" << hdr.GetAddr2 ());
1465 
1466  NS_ASSERT (m_staMac != nullptr && m_staMac->IsAssociated ());
1467  uint16_t staId = m_staMac->GetAssociationId ();
1468  std::vector<uint32_t> indices = blockAck.FindPerAidTidInfoWithAid (staId);
1469 
1470  if (indices.empty ())
1471  {
1472  NS_LOG_DEBUG ("No Per AID TID Info subfield intended for me");
1473  return;
1474  }
1475 
1476  MuSnrTag tag;
1477  mpdu->GetPacket ()->PeekPacketTag (tag);
1478 
1479  // notify the Block Ack Manager
1480  for (const auto& index : indices)
1481  {
1482  uint8_t tid = blockAck.GetTidInfo (index);
1483 
1484  if (blockAck.GetAckType (index) && tid < 8)
1485  {
1486  // Acknowledgment context
1487  NS_ABORT_IF (m_psduMap.empty () || m_psduMap.begin ()->first != staId);
1488  GetBaManager (tid)->NotifyGotAck (*m_psduMap.at (staId)->begin ());
1489  }
1490  else
1491  {
1492  // Block Acknowledgment or All-ack context
1493  if (blockAck.GetAckType (index) && tid == 14)
1494  {
1495  // All-ack context, we need to determine the actual TID(s) of the PSDU
1496  NS_ASSERT (indices.size () == 1);
1497  NS_ABORT_IF (m_psduMap.empty () || m_psduMap.begin ()->first != staId);
1498  std::set<uint8_t> tids = m_psduMap.at (staId)->GetTids ();
1499  NS_ABORT_MSG_IF (tids.size () > 1, "Multi-TID A-MPDUs not supported yet");
1500  tid = *tids.begin ();
1501  }
1502 
1503  std::pair<uint16_t,uint16_t> ret = GetBaManager (tid)->NotifyGotBlockAck (blockAck,
1504  hdr.GetAddr2 (),
1505  {tid}, index);
1506  m_mac->GetWifiRemoteStationManager ()->ReportAmpduTxStatus (hdr.GetAddr2 (), ret.first,
1507  ret.second, rxSignalInfo.snr,
1508  tag.Get (staId), m_txParams.m_txVector);
1509  }
1510 
1511  if (m_psduMap.at (staId)->GetHeader (0).IsQosData ()
1512  && (blockAck.GetAckType (index) // Ack or All-ack context
1513  || std::any_of (blockAck.GetBitmap (index).begin (),
1514  blockAck.GetBitmap (index).end (),
1515  [](uint8_t b) { return b != 0; })))
1516  {
1517  NS_ASSERT (m_psduMap.at (staId)->GetHeader (0).HasData ());
1518  NS_ASSERT (m_psduMap.at (staId)->GetHeader (0).GetQosTid () == tid);
1519  // the station has received a response from the AP for the HE TB PPDU
1520  // transmitted in response to a Basic Trigger Frame and at least one
1521  // MPDU was acknowledged. Therefore, it needs to update the access
1522  // parameters if it received an MU EDCA Parameter Set element.
1523  m_mac->GetQosTxop (tid)->StartMuEdcaTimerNow ();
1524  }
1525  }
1526 
1527  // cancel the timer
1528  m_txTimer.Cancel ();
1529  m_channelAccessManager->NotifyAckTimeoutResetNow ();
1530  m_psduMap.clear ();
1531  }
1532  else if (hdr.IsTrigger ())
1533  {
1534  // Trigger Frames are only processed by STAs
1535  if (m_staMac == nullptr)
1536  {
1537  return;
1538  }
1539 
1540  // A Trigger Frame in an A-MPDU is processed when the A-MPDU is fully received
1541  if (inAmpdu)
1542  {
1543  m_triggerFrameInAmpdu = true;
1544  return;
1545  }
1546 
1547  CtrlTriggerHeader trigger;
1548  mpdu->GetPacket ()->PeekHeader (trigger);
1549 
1550  if (hdr.GetAddr1 () != m_self
1551  && (!hdr.GetAddr1 ().IsBroadcast ()
1552  || !m_staMac->IsAssociated ()
1553  || hdr.GetAddr2 () != m_bssid // not sent by the AP this STA is associated with
1554  || trigger.FindUserInfoWithAid (m_staMac->GetAssociationId ()) == trigger.end ()))
1555  {
1556  // not addressed to us
1557  return;
1558  }
1559 
1560  uint16_t staId = m_staMac->GetAssociationId ();
1561 
1562  if (trigger.IsMuBar ())
1563  {
1564  Mac48Address sender = hdr.GetAddr2 ();
1565  NS_LOG_DEBUG ("Received MU-BAR Trigger Frame from=" << sender);
1566  m_mac->GetWifiRemoteStationManager ()->ReportRxOk (sender, rxSignalInfo, txVector);
1567 
1568  auto userInfoIt = trigger.FindUserInfoWithAid (staId);
1569  NS_ASSERT (userInfoIt != trigger.end ());
1570  CtrlBAckRequestHeader blockAckReq = userInfoIt->GetMuBarTriggerDepUserInfo ();
1571  NS_ABORT_MSG_IF (blockAckReq.IsMultiTid (), "Multi-TID BlockAckReq not supported");
1572  uint8_t tid = blockAckReq.GetTidInfo ();
1573 
1574  auto agreementIt = m_agreements.find ({sender, tid});
1575 
1576  if (agreementIt == m_agreements.end ())
1577  {
1578  NS_LOG_DEBUG ("There's not a valid agreement for this BlockAckReq");
1579  return;
1580  }
1581 
1582  agreementIt->second.NotifyReceivedBar (blockAckReq.GetStartingSequence ());
1583 
1584  NS_LOG_DEBUG ("Schedule Block Ack in TB PPDU");
1585  Simulator::Schedule (m_phy->GetSifs (), &HeFrameExchangeManager::SendBlockAck, this,
1586  agreementIt->second, hdr.GetDuration (),
1587  GetHeTbTxVector (trigger, hdr.GetAddr2 ()), rxSignalInfo.snr);
1588  }
1589  else if (trigger.IsBasic ())
1590  {
1591  Simulator::Schedule (m_phy->GetSifs (), &HeFrameExchangeManager::ReceiveBasicTrigger,
1592  this, trigger, hdr);
1593  }
1594  else if (trigger.IsBsrp ())
1595  {
1596  Simulator::Schedule (m_phy->GetSifs (), &HeFrameExchangeManager::SendQosNullFramesInTbPpdu,
1597  this, trigger, hdr);
1598  }
1599  }
1600  else
1601  {
1602  // the received control frame cannot be handled here
1603  VhtFrameExchangeManager::ReceiveMpdu (mpdu, rxSignalInfo, txVector, inAmpdu);
1604  }
1605 
1606  // the received control frame has been processed
1607  return;
1608  }
1609 
1610  // the received frame cannot be handled here
1611  VhtFrameExchangeManager::ReceiveMpdu (mpdu, rxSignalInfo, txVector, inAmpdu);;
1612 }
1613 
1614 void
1615 HeFrameExchangeManager::EndReceiveAmpdu (Ptr<const WifiPsdu> psdu, const RxSignalInfo& rxSignalInfo,
1616  const WifiTxVector& txVector, const std::vector<bool>& perMpduStatus)
1617 {
1618  std::set<uint8_t> tids = psdu->GetTids ();
1619 
1620  if (txVector.IsUlMu () && m_txTimer.IsRunning ()
1621  && m_txTimer.GetReason () == WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF)
1622  {
1623  Mac48Address sender = psdu->GetAddr2 ();
1624  NS_ASSERT (m_txParams.m_acknowledgment
1625  && m_txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA);
1626  WifiUlMuMultiStaBa* acknowledgment = static_cast<WifiUlMuMultiStaBa*> (m_txParams.m_acknowledgment.get ());
1627  std::size_t index = acknowledgment->baType.m_bitmapLen.size ();
1628 
1629  if (m_staExpectTbPpduFrom.find (sender) == m_staExpectTbPpduFrom.end ())
1630  {
1631  NS_LOG_WARN ("Received a TB PPDU from an unexpected station: " << sender);
1632  return;
1633  }
1634 
1635  NS_LOG_DEBUG ("Received an A-MPDU in a TB PPDU from " << sender << " (" << *psdu << ")");
1636 
1637  if (std::any_of (tids.begin (), tids.end (),
1638  [&psdu](uint8_t tid)
1639  { return psdu->GetAckPolicyForTid (tid) == WifiMacHeader::NORMAL_ACK; }))
1640  {
1641  if (std::all_of (perMpduStatus.cbegin (), perMpduStatus.cend (), [](bool v) { return v; }))
1642  {
1643  // All-ack context
1644  acknowledgment->stationsReceivingMultiStaBa.emplace (std::make_pair (sender, 14), index);
1645  acknowledgment->baType.m_bitmapLen.push_back (0);
1646  }
1647  else
1648  {
1649  // Block Acknowledgment context
1650  std::size_t i = 0;
1651  for (const auto& tid : tids)
1652  {
1653  acknowledgment->stationsReceivingMultiStaBa.emplace (std::make_pair (sender, tid), index + i++);
1654  acknowledgment->baType.m_bitmapLen.push_back (GetBlockAckType (sender, tid).m_bitmapLen.at (0));
1655  }
1656  }
1657  uint16_t staId = txVector.GetHeMuUserInfoMap ().begin ()->first;
1658  m_muSnrTag.Set (staId, rxSignalInfo.snr);
1659  }
1660 
1661  // Schedule the transmission of a Multi-STA BlockAck frame if needed
1662  if (!acknowledgment->stationsReceivingMultiStaBa.empty () && !m_multiStaBaEvent.IsRunning ())
1663  {
1664  m_multiStaBaEvent = Simulator::Schedule (m_phy->GetSifs (),
1665  &HeFrameExchangeManager::SendMultiStaBlockAck,
1666  this, std::cref (m_txParams));
1667  }
1668 
1669  // remove the sender from the set of stations that are expected to send a TB PPDU
1670  m_staExpectTbPpduFrom.erase (sender);
1671 
1672  if (m_staExpectTbPpduFrom.empty ())
1673  {
1674  // we do not expect any other BlockAck frame
1675  m_txTimer.Cancel ();
1676  m_channelAccessManager->NotifyAckTimeoutResetNow ();
1677 
1678  if (!m_multiStaBaEvent.IsRunning ())
1679  {
1680  // all of the stations that replied with a TB PPDU sent QoS Null frames.
1681  NS_LOG_DEBUG ("Continue the TXOP");
1682  m_psduMap.clear ();
1683  m_edca->ResetCw ();
1684  TransmissionSucceeded ();
1685  }
1686  }
1687 
1688  // the received TB PPDU has been processed
1689  return;
1690  }
1691 
1692  if (txVector.IsUlMu () && m_txTimer.IsRunning ()
1693  && m_txTimer.GetReason () == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF)
1694  {
1695  Mac48Address sender = psdu->GetAddr2 ();
1696 
1697  if (m_staExpectTbPpduFrom.find (sender) == m_staExpectTbPpduFrom.end ())
1698  {
1699  NS_LOG_WARN ("Received a TB PPDU from an unexpected station: " << sender);
1700  return;
1701  }
1702  if (std::none_of (psdu->begin (), psdu->end (), [](Ptr<WifiMacQueueItem> mpdu)
1703  { return mpdu->GetHeader ().IsQosData ()
1704  && !mpdu->GetHeader ().HasData ();
1705  }))
1706  {
1707  NS_LOG_WARN ("No QoS Null frame in the received PSDU");
1708  return;
1709  }
1710 
1711  NS_LOG_DEBUG ("Received QoS Null frames in a TB PPDU from " << sender);
1712 
1713  // remove the sender from the set of stations that are expected to send a TB PPDU
1714  m_staExpectTbPpduFrom.erase (sender);
1715 
1716  if (m_staExpectTbPpduFrom.empty ())
1717  {
1718  // we do not expect any other response
1719  m_txTimer.Cancel ();
1720  m_channelAccessManager->NotifyAckTimeoutResetNow ();
1721 
1722  NS_ASSERT (m_edca != 0);
1723  m_psduMap.clear ();
1724  m_edca->ResetCw ();
1725  TransmissionSucceeded ();
1726  }
1727 
1728  // the received TB PPDU has been processed
1729  return;
1730  }
1731 
1732  if (m_triggerFrameInAmpdu)
1733  {
1734  // the received A-MPDU contains a Trigger Frame. It is now time to handle it.
1735  auto psduIt = psdu->begin ();
1736  while (psduIt != psdu->end ())
1737  {
1738  if ((*psduIt)->GetHeader ().IsTrigger ())
1739  {
1740  ReceiveMpdu (*psduIt, rxSignalInfo, txVector, false);
1741  }
1742  psduIt++;
1743  }
1744 
1745  m_triggerFrameInAmpdu = false;
1746  return;
1747  }
1748 
1749  // the received frame cannot be handled here
1750  VhtFrameExchangeManager::EndReceiveAmpdu (psdu, rxSignalInfo, txVector, perMpduStatus);
1751 }
1752 
1753 } //namespace ns3
ns3::CtrlTriggerHeader::end
ConstIterator end(void) const
Get a const iterator indicating past-the-last User Info field in the list.
Definition: ctrl-headers.cc:2016
ns3::GetBlockAckSize
uint32_t GetBlockAckSize(BlockAckType type)
Return the total BlockAck size (including FCS trailer).
Definition: wifi-utils.cc:63
ns3::TypeId
a unique identifier for an interface.
Definition: type-id.h:59
NS_LOG_COMPONENT_DEFINE
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:205
ns3::MultiUserScheduler::DL_MU_TX
@ DL_MU_TX
Definition: multi-user-scheduler.h:66
ns3::WIFI_MAC_QOSDATA_NULL
@ WIFI_MAC_QOSDATA_NULL
Definition: wifi-mac-header.h:74
ns3::WifiUlMuMultiStaBa
WifiUlMuMultiStaBa specifies that a Basic Trigger Frame is being sent to solicit TB PPDUs that will b...
Definition: wifi-acknowledgment.h:306
ns3::HeFrameExchangeManager::m_psduMap
WifiPsduMap m_psduMap
the A-MPDU being transmitted
Definition: he-frame-exchange-manager.h:218
ns3::WifiAcknowledgment::acknowledgmentTime
Time acknowledgmentTime
time required by the acknowledgment method
Definition: wifi-acknowledgment.h:104
ns3::FrameExchangeManager::m_txTimer
WifiTxTimer m_txTimer
the timer set upon frame transmission
Definition: frame-exchange-manager.h:379
ns3::WifiTxVector::SetTxPowerLevel
void SetTxPowerLevel(uint8_t powerlevel)
Sets the selected transmission power level.
Definition: wifi-tx-vector.cc:242
NS_OBJECT_ENSURE_REGISTERED
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:45
NS_ASSERT
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition: assert.h:67
ns3::WifiNoProtection
WifiNoProtection specifies that no protection method is used.
Definition: wifi-protection.h:82
ns3::VhtFrameExchangeManager
VhtFrameExchangeManager handles the frame exchange sequences for VHT stations.
Definition: vht-frame-exchange-manager.h:35
ns3::WifiMacQueueItem::GetPacket
Ptr< const Packet > GetPacket(void) const
Get the packet stored in this item.
Definition: wifi-mac-queue-item.cc:66
ns3::WifiMacHeader::SetNoMoreFragments
void SetNoMoreFragments(void)
Un-set the More Fragment bit in the Frame Control Field.
Definition: wifi-mac-header.cc:322
ns3::FrameExchangeManager::m_phy
Ptr< WifiPhy > m_phy
the PHY layer on this station
Definition: frame-exchange-manager.h:385
ns3::WifiMacHeader::SetDuration
void SetDuration(Time duration)
Set the Duration/ID field with the given duration (Time object).
Definition: wifi-mac-header.cc:300
ns3::Packet::PeekHeader
uint32_t PeekHeader(Header &header) const
Deserialize but does not remove the header from the internal buffer.
Definition: packet.cc:290
ns3::WifiPsduMap
std::unordered_map< uint16_t, Ptr< WifiPsdu > > WifiPsduMap
Map of PSDUs indexed by STA-ID.
Definition: he-frame-exchange-manager.h:34
ns3::WifiMacHeader::IsAck
bool IsAck(void) const
Return true if the header is an Ack header.
Definition: wifi-mac-header.cc:663
ns3::WifiPsdu::GetAddr1
Mac48Address GetAddr1(void) const
Get the Receiver Address (RA), which is common to all the MPDUs.
Definition: wifi-psdu.cc:111
ns3::WifiAcknowledgment::NONE
@ NONE
Definition: wifi-acknowledgment.h:52
ns3::CtrlBAckResponseHeader::GetBitmap
const std::vector< uint8_t > & GetBitmap(std::size_t index=0) const
Return a const reference to the bitmap from the BlockAck response header.
Definition: ctrl-headers.cc:1052
ns3::WifiPsdu::GetPayload
Ptr< const Packet > GetPayload(std::size_t i) const
Get the payload of the i-th MPDU.
Definition: wifi-psdu.cc:278
ns3::WifiPhy::GetSlot
Time GetSlot(void) const
Return the slot duration for this PHY.
Definition: wifi-phy.cc:923
ns3::Packet::AddHeader
void AddHeader(const Header &header)
Add header to this packet.
Definition: packet.cc:256
ns3::WifiTxParameters::m_txDuration
Time m_txDuration
TX duration of the frame.
Definition: wifi-tx-parameters.h:65
ns3
Every class exported by the ns3 library is enclosed in the ns3 namespace.
ns3::HtFrameExchangeManager::StartFrameExchange
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...
Definition: ht-frame-exchange-manager.cc:334
ns3::HtFrameExchangeManager::SendMpduFromBaManager
virtual bool SendMpduFromBaManager(Ptr< QosTxop > edca, Time availableTime, bool initialFrame)
If the Block Ack Manager associated with the given EDCA has a BlockAckReq frame to transmit (the dura...
Definition: ht-frame-exchange-manager.cc:387
ns3::WifiPhy::CalculateTxDuration
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition: wifi-phy.cc:1611
ns3::WifiMacHeader::SetDsTo
void SetDsTo(void)
Set the To DS bit in the Frame Control field.
Definition: wifi-mac-header.cc:96
ns3::WifiDlMuBarBaSequence::stationsSendBlockAckReqTo
std::map< Mac48Address, BlockAckReqInfo > stationsSendBlockAckReqTo
Set of stations receiving a BlockAckReq frame and replying with a BlockAck frame.
Definition: wifi-acknowledgment.h:234
ns3::WifiMacHeader::SetSequenceNumber
void SetSequenceNumber(uint16_t seq)
Set the sequence number of the header.
Definition: wifi-mac-header.cc:312
ns3::QosTxop::GetBaAgreementEstablished
bool GetBaAgreementEstablished(Mac48Address address, uint8_t tid) const
Definition: qos-txop.cc:249
ns3::WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF
@ WAIT_TB_PPDU_AFTER_BASIC_TF
Definition: wifi-tx-timer.h:62
ns3::CtrlBAckRequestHeader
Headers for BlockAckRequest.
Definition: ctrl-headers.h:49
ns3::WifiPsdu::end
std::vector< Ptr< WifiMacQueueItem > >::const_iterator end(void) const
Return a const iterator to past-the-last MPDU.
Definition: wifi-psdu.cc:337
ns3::WifiMacHeader::IsTrigger
bool IsTrigger(void) const
Return true if the header is a Trigger header.
Definition: wifi-mac-header.cc:753
multi-user-scheduler.h
ns3::WifiAcknowledgment::ACK_AFTER_TB_PPDU
@ ACK_AFTER_TB_PPDU
Definition: wifi-acknowledgment.h:60
ns3::WifiTxTimer::IsRunning
bool IsRunning(void) const
Return true if the timer is running.
Definition: wifi-tx-timer.cc:121
ns3::HtFrameExchangeManager::DoDispose
void DoDispose() override
Destructor implementation.
Definition: ht-frame-exchange-manager.cc:66
ns3::WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF
@ WAIT_QOS_NULL_AFTER_BSRP_TF
Definition: wifi-tx-timer.h:63
ns3::WifiMacHeader::SetDsNotFrom
void SetDsNotFrom(void)
Un-set the From DS bit in the Frame Control field.
Definition: wifi-mac-header.cc:90
ns3::WifiDlMuTfMuBar::stationsReplyingWithBlockAck
std::map< Mac48Address, BlockAckInfo > stationsReplyingWithBlockAck
Set of stations replying with a BlockAck frame.
Definition: wifi-acknowledgment.h:262
ns3::WifiPhy::CalculatePhyPreambleAndHeaderDuration
static Time CalculatePhyPreambleAndHeaderDuration(const WifiTxVector &txVector)
Definition: wifi-phy.cc:1605
ns3::QosTxop::GetBaManager
Ptr< BlockAckManager > GetBaManager(void)
Get the Block Ack Manager associated with this QosTxop.
Definition: qos-txop.cc:243
ns3::CtrlTriggerHeader::IsMuBar
bool IsMuBar(void) const
Check if this is a MU-BAR Trigger frame.
Definition: ctrl-headers.cc:1780
NS_LOG_WARN
#define NS_LOG_WARN(msg)
Use NS_LOG to output a message of level LOG_WARN.
Definition: log.h:265
ns3::HeFrameExchangeManager::GetTypeId
static TypeId GetTypeId(void)
Get the type ID.
Definition: he-frame-exchange-manager.cc:44
ns3::CtrlBAckResponseHeader::GetStartingSequence
uint16_t GetStartingSequence(std::size_t index=0) const
For Block Ack variants other than Multi-STA Block Ack, get the starting sequence number.
Definition: ctrl-headers.cc:530
ns3::FrameExchangeManager::m_channelAccessManager
Ptr< ChannelAccessManager > m_channelAccessManager
the channel access manager
Definition: frame-exchange-manager.h:384
ns3::WifiTxVector::SetAggregation
void SetAggregation(bool aggregation)
Sets if PSDU contains A-MPDU.
Definition: wifi-tx-vector.cc:292
ns3::HtFrameExchangeManager::SetWifiMac
void SetWifiMac(const Ptr< RegularWifiMac > mac) override
Set the MAC layer to use.
Definition: ht-frame-exchange-manager.cc:78
ns3::WifiDlMuTfMuBar::barTypes
std::list< BlockAckReqType > barTypes
BAR types.
Definition: wifi-acknowledgment.h:263
ns3::WifiDlMuBarBaSequence::stationsReplyingWithBlockAck
std::map< Mac48Address, BlockAckInfo > stationsReplyingWithBlockAck
Set of stations replying with a BlockAck frame (no more than one)
Definition: wifi-acknowledgment.h:232
ns3::HeFrameExchangeManager::m_muScheduler
Ptr< MultiUserScheduler > m_muScheduler
Multi-user Scheduler (HE APs only)
Definition: he-frame-exchange-manager.h:220
ns3::Mac48Address
an EUI-48 address
Definition: mac48-address.h:44
ns3::HtFrameExchangeManager::ForwardPsduDown
virtual void ForwardPsduDown(Ptr< const WifiPsdu > psdu, WifiTxVector &txVector)
Forward a PSDU down to the PHY layer.
Definition: ht-frame-exchange-manager.cc:922
ns3::WifiDlMuAggregateTf
WifiDlMuAggregateTf specifies that a DL MU PPDU made of PSDUs including each a MU-BAR Trigger Frame i...
Definition: wifi-acknowledgment.h:277
ns3::WifiMacHeader::SetNoRetry
void SetNoRetry(void)
Un-set the Retry bit in the Frame Control field.
Definition: wifi-mac-header.cc:347
ns3::WifiTxVector
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
Definition: wifi-tx-vector.h:71
ns3::CtrlBAckRequestHeader::IsMultiTid
bool IsMultiTid(void) const
Check if the current Ack Policy has Multi-TID Block Ack.
Definition: ctrl-headers.cc:263
ns3::HeFrameExchangeManager::StartFrameExchange
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...
Definition: he-frame-exchange-manager.cc:109
ns3::WifiMacHeader::GetAddr1
Mac48Address GetAddr1(void) const
Return the address in the Address 1 field.
Definition: wifi-mac-header.cc:424
third.mac
mac
Definition: third.py:99
ns3::VhtFrameExchangeManager::GetWifiPsdu
Ptr< WifiPsdu > GetWifiPsdu(Ptr< WifiMacQueueItem > mpdu, const WifiTxVector &txVector) const override
Get a PSDU containing the given MPDU.
Definition: vht-frame-exchange-manager.cc:56
ns3::WifiMacHeader::SetAddr3
void SetAddr3(Mac48Address address)
Fill the Address 3 field with the given address.
Definition: wifi-mac-header.cc:120
ns3::WifiConstPsduMap
std::unordered_map< uint16_t, Ptr< const WifiPsdu > > WifiConstPsduMap
Map of const PSDUs indexed by STA-ID.
Definition: he-frame-exchange-manager.h:43
ns3::MuSnrTag
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:36
ns3::Packet::PeekPacketTag
bool PeekPacketTag(Tag &tag) const
Search a matching tag and call Tag::Deserialize if it is found.
Definition: packet.cc:978
ns3::WifiTxParameters
This class stores the TX parameters (TX vector, protection mechanism, acknowledgment mechanism,...
Definition: wifi-tx-parameters.h:45
ns3::Simulator::Schedule
static EventId Schedule(Time const &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition: simulator.h:556
he-phy.h
Declaration of ns3::HePhy class and ns3::HeSigAParameters struct.
ns3::HeFrameExchangeManager::GetSupportedBaBufferSize
uint16_t GetSupportedBaBufferSize(void) const override
Get the maximum supported buffer size for a Block Ack agreement.
Definition: he-frame-exchange-manager.cc:66
ns3::WifiMacQueueItem::GetHeader
const WifiMacHeader & GetHeader(void) const
Get the header stored in this item.
Definition: wifi-mac-queue-item.cc:72
ns3::CtrlBAckResponseHeader::SetTidInfo
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...
Definition: ctrl-headers.cc:477
ns3::WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE
@ DL_MU_BAR_BA_SEQUENCE
Definition: wifi-acknowledgment.h:56
ns3::PeekPointer
U * PeekPointer(const Ptr< U > &p)
Definition: ptr.h:415
ns3::TypeId::SetParent
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:923
ns3::MultiUserScheduler::SU_TX
@ SU_TX
Definition: multi-user-scheduler.h:65
ns3::WifiTxParameters::m_protection
std::unique_ptr< WifiProtection > m_protection
protection method
Definition: wifi-tx-parameters.h:63
ns3::MU_BAR_TRIGGER
@ MU_BAR_TRIGGER
Definition: ctrl-headers.h:564
ns3::WifiTxVector::IsUlMu
bool IsUlMu(void) const
Return true if this TX vector is used for an uplink multi-user transmission.
Definition: wifi-tx-vector.cc:382
ns3::HeFrameExchangeManager::m_apMac
Ptr< ApWifiMac > m_apMac
MAC pointer (null if not an AP)
Definition: he-frame-exchange-manager.h:201
ns3::WifiMacHeader::SetAddr1
void SetAddr1(Mac48Address address)
Fill the Address 1 field with the given address.
Definition: wifi-mac-header.cc:108
ns3::WifiTxTimer::WAIT_BLOCK_ACK_AFTER_TB_PPDU
@ WAIT_BLOCK_ACK_AFTER_TB_PPDU
Definition: wifi-tx-timer.h:64
ns3::GetAckSize
uint32_t GetAckSize(void)
Return the total Ack size (including FCS trailer).
Definition: wifi-utils.cc:55
ns3::WifiMacHeader
Implements the IEEE 802.11 MAC header.
Definition: wifi-mac-header.h:85
ns3::CtrlBAckResponseHeader::IsMultiSta
bool IsMultiSta(void) const
Check if the BlockAck frame variant is Multi-STA Block Ack.
Definition: ctrl-headers.cc:564
ns3::HeFrameExchangeManager::SendMpduFromBaManager
bool SendMpduFromBaManager(Ptr< QosTxop > edca, Time availableTime, bool initialFrame) override
If the Block Ack Manager associated with the given EDCA has a BlockAckReq frame to transmit (the dura...
Definition: he-frame-exchange-manager.cc:170
ns3::CtrlBAckResponseHeader::SetAid11
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...
Definition: ctrl-headers.cc:570
ns3::GetBlockAckRequestSize
uint32_t GetBlockAckRequestSize(BlockAckReqType type)
Return the total BlockAckRequest size (including FCS trailer).
Definition: wifi-utils.cc:73
ns3::HeFrameExchangeManager::PrepareMuBar
Ptr< WifiMacQueueItem > 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...
Definition: he-frame-exchange-manager.cc:607
ns3::Ptr
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:74
ns3::WifiTxVector::SetLength
void SetLength(uint16_t length)
Set the LENGTH field of the L-SIG.
Definition: wifi-tx-vector.cc:322
ns3::WifiMacHeader::IsBlockAckReq
bool IsBlockAckReq(void) const
Return true if the header is a BlockAckRequest header.
Definition: wifi-mac-header.cc:741
ns3::HeFrameExchangeManager::DoDispose
void DoDispose() override
Destructor implementation.
Definition: he-frame-exchange-manager.cc:85
ns3::WifiTxVector::GetModulationClass
WifiModulationClass GetModulationClass(void) const
Get the modulation class specified by this TXVECTOR.
Definition: wifi-tx-vector.cc:128
ns3::WifiAcknowledgment::UL_MU_MULTI_STA_BA
@ UL_MU_MULTI_STA_BA
Definition: wifi-acknowledgment.h:59
ns3::QosFrameExchangeManager::m_edca
Ptr< QosTxop > m_edca
the EDCAF that gained channel access
Definition: qos-frame-exchange-manager.h:159
ns3::HeFrameExchangeManager::SendPsduMap
void SendPsduMap(void)
Send the current PSDU map as a DL MU PPDU.
Definition: he-frame-exchange-manager.cc:258
ns3::WifiTxParameters::GetSizeIfAddMpdu
uint32_t GetSizeIfAddMpdu(Ptr< const WifiMacQueueItem > mpdu) const
Get the size in bytes of the frame in case the given MPDU is added.
Definition: wifi-tx-parameters.cc:147
ns3::Now
Time Now(void)
create an ns3::Time instance which contains the current simulation time.
Definition: simulator.cc:287
ns3::HeFrameExchangeManager::CalculateAcknowledgmentTime
void CalculateAcknowledgmentTime(WifiAcknowledgment *acknowledgment) const override
Calculate the time required to acknowledge a frame according to the given acknowledgment method.
Definition: he-frame-exchange-manager.cc:657
max
#define max(a, b)
Definition: 80211b.c:43
ns3::WifiProtection::NONE
@ NONE
Definition: wifi-protection.h:47
ns3::HeFrameExchangeManager::HeFrameExchangeManager
HeFrameExchangeManager()
Definition: he-frame-exchange-manager.cc:54
ns3::WifiTxTimer::WAIT_NORMAL_ACK_AFTER_DL_MU_PPDU
@ WAIT_NORMAL_ACK_AFTER_DL_MU_PPDU
Definition: wifi-tx-timer.h:60
ns3::CtrlBAckResponseHeader::SetAckType
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...
Definition: ctrl-headers.cc:586
ns3::WifiDlMuTfMuBar
WifiDlMuTfMuBar specifies that a DL MU PPDU is followed after a SIFS duration by a MU-BAR Trigger Fra...
Definition: wifi-acknowledgment.h:246
ns3::Mac48Address::IsBroadcast
bool IsBroadcast(void) const
Definition: mac48-address.cc:158
ns3::CtrlTriggerHeader::IsBasic
bool IsBasic(void) const
Check if this is a Basic Trigger frame.
Definition: ctrl-headers.cc:1768
ns3::HePhy::ConvertLSigLengthToHeTbPpduDuration
static Time ConvertLSigLengthToHeTbPpduDuration(uint16_t length, const WifiTxVector &txVector, WifiPhyBand band)
Definition: he-phy.cc:274
ns3::EventId::Cancel
void Cancel(void)
This method is syntactic sugar for the ns3::Simulator::Cancel method.
Definition: event-id.cc:53
ns3::WifiMacHeader::HasData
bool HasData(void) const
Return true if the header type is DATA and is not DATA_NULL.
Definition: wifi-mac-header.cc:632
ns3::CtrlTriggerHeader
Headers for Trigger frames.
Definition: ctrl-headers.h:886
ns3::MultiUserScheduler::UL_MU_TX
@ UL_MU_TX
Definition: multi-user-scheduler.h:67
ns3::WifiPsdu::GetSize
uint32_t GetSize(void) const
Return the size of the PSDU in bytes.
Definition: wifi-psdu.cc:260
ns3::WifiMacHeader::IsBlockAck
bool IsBlockAck(void) const
Return true if the header is a BlockAck header.
Definition: wifi-mac-header.cc:747
ns3::WifiDlMuTfMuBar::muBarTxVector
WifiTxVector muBarTxVector
TXVECTOR used to transmit the MU-BAR Trigger Frame.
Definition: wifi-acknowledgment.h:265
ns3::WifiTxVector::GetHeMuUserInfoMap
const HeMuUserInfoMap & GetHeMuUserInfoMap(void) const
Get a const reference to the map HE MU user-specific transmission information indexed by STA-ID.
Definition: wifi-tx-vector.cc:421
SU_STA_ID
#define SU_STA_ID
Definition: wifi-mode.h:32
ns3::WIFI_MAC_CTL_TRIGGER
@ WIFI_MAC_CTL_TRIGGER
Definition: wifi-mac-header.h:38
ns3::WifiTxParameters::Clear
void Clear(void)
Reset the TX parameters.
Definition: wifi-tx-parameters.cc:67
ns3::Copy
Ptr< T > Copy(Ptr< T > object)
Return a deep copy of a Ptr.
Definition: ptr.h:555
ns3::WifiPsdu::GetNMpdus
std::size_t GetNMpdus(void) const
Return the number of MPDUs constituting the PSDU.
Definition: wifi-psdu.cc:319
ns3::WifiMacHeader::IsQosData
bool IsQosData(void) const
Return true if the Type is DATA and Subtype is one of the possible values for QoS Data.
Definition: wifi-mac-header.cc:565
ns3::WifiPhy::GetPhyBand
WifiPhyBand GetPhyBand(void) const
Get the configured Wi-Fi band.
Definition: wifi-phy.cc:1125
ns3::WifiUlMuMultiStaBa::tbPpduTxVector
WifiTxVector tbPpduTxVector
TXVECTOR for a TB PPDU.
Definition: wifi-acknowledgment.h:316
ns3::WifiPsdu::GetTids
std::set< uint8_t > GetTids(void) const
Get the set of TIDs of the QoS Data frames included in the PSDU.
Definition: wifi-psdu.cc:166
ns3::Time
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:104
ns3::WifiDlMuTfMuBar::ulLength
uint16_t ulLength
the UL Length field of the MU-BAR Trigger Frame
Definition: wifi-acknowledgment.h:264
ns3::WifiTxTimer::WAIT_BLOCK_ACK
@ WAIT_BLOCK_ACK
Definition: wifi-tx-timer.h:59
ns3::WifiAcknowledgment::DL_MU_AGGREGATE_TF
@ DL_MU_AGGREGATE_TF
Definition: wifi-acknowledgment.h:58
NS_ABORT_MSG_IF
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition: abort.h:108
ns3::WifiPsdu::SetDuration
void SetDuration(Time duration)
Set the Duration/ID field on all the MPDUs.
Definition: wifi-psdu.cc:156
NS_ASSERT_MSG
#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:88
ns3::CtrlTriggerHeader::GetApTxPower
int8_t GetApTxPower(void) const
Get the power value (dBm) indicated by the AP TX Power subfield of the Common Info field.
Definition: ctrl-headers.cc:1966
ns3::CtrlTriggerHeader::GetUlLength
uint16_t GetUlLength(void) const
Get the UL Length subfield of the Common Info field.
Definition: ctrl-headers.cc:1822
ns3::BlockAckType::m_bitmapLen
std::vector< uint8_t > m_bitmapLen
Length (bytes) of included bitmaps.
Definition: block-ack-type.h:48
ns3::HeFrameExchangeManager::m_multiStaBaEvent
EventId m_multiStaBaEvent
Sending a Multi-STA BlockAck event.
Definition: he-frame-exchange-manager.h:223
ns3::WifiMacHeader::GetQosAckPolicy
QosAckPolicy GetQosAckPolicy(void) const
Return the QoS Ack policy in the QoS control field.
Definition: wifi-mac-header.cc:829
ns3::WifiTxTimer::Reason
Reason
The reason why the timer was started.
Definition: wifi-tx-timer.h:55
ns3::WifiTxVector::IsMu
bool IsMu(void) const
Return true if this TX vector is used for a multi-user transmission.
Definition: wifi-tx-vector.cc:370
ns3::MuSnrTag::Get
double Get(uint16_t staId) const
Return the SNR value for the given sender.
Definition: mu-snr-tag.cc:67
ns3::WifiTxParameters::m_acknowledgment
std::unique_ptr< WifiAcknowledgment > m_acknowledgment
acknowledgment method
Definition: wifi-tx-parameters.h:64
ns3::SnrTag
Introspection did not find any typical Config paths.
Definition: snr-tag.h:35
ns3::WifiDlMuAggregateTf::stationsReplyingWithBlockAck
std::map< Mac48Address, BlockAckInfo > stationsReplyingWithBlockAck
Set of stations replying with a BlockAck frame.
Definition: wifi-acknowledgment.h:294
ns3::wifiAcList
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:120
NS_LOG_LOGIC
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition: log.h:289
ns3::WifiDlMuBarBaSequence
WifiDlMuBarBaSequence specifies that a DL MU PPDU is acknowledged through a sequence of BlockAckReq a...
Definition: wifi-acknowledgment.h:202
NS_ABORT_IF
#define NS_ABORT_IF(cond)
Abnormal program termination if a condition is true.
Definition: abort.h:77
ns3::CtrlBAckResponseHeader::SetType
void SetType(BlockAckType type)
Set the block ack type.
Definition: ctrl-headers.cc:456
ns3::WifiAckManager::SetQosAckPolicy
static void SetQosAckPolicy(Ptr< WifiMacQueueItem > item, const WifiAcknowledgment *acknowledgment)
Set the QoS Ack policy for the given MPDU, which must be a QoS data frame.
Definition: wifi-ack-manager.cc:64
ns3::Packet::AddPacketTag
void AddPacketTag(const Tag &tag) const
Add a packet tag.
Definition: packet.cc:956
ns3::WifiMacHeader::SetAddr2
void SetAddr2(Mac48Address address)
Fill the Address 2 field with the given address.
Definition: wifi-mac-header.cc:114
ns3::WifiMacHeader::GetQosTid
uint8_t GetQosTid(void) const
Return the Traffic ID of a QoS header.
Definition: wifi-mac-header.cc:862
ns3::WifiUlMuMultiStaBa::baType
BlockAckType baType
BlockAck type.
Definition: wifi-acknowledgment.h:315
NS_LOG_FUNCTION_NOARGS
#define NS_LOG_FUNCTION_NOARGS()
Output the name of the function.
Definition: log-macros-enabled.h:209
ns3::CtrlBAckResponseHeader::GetAckType
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...
Definition: ctrl-headers.cc:597
ns3::HtFrameExchangeManager::TransmissionSucceeded
void TransmissionSucceeded(void) override
Take necessary actions upon a transmission success.
Definition: ht-frame-exchange-manager.cc:579
ns3::Time::Min
static Time Min()
Minimum representable Time Not to be confused with Min(Time,Time).
Definition: nstime.h:274
ns3::CtrlBAckResponseHeader::GetTidInfo
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...
Definition: ctrl-headers.cc:510
timeout
ns3::Time timeout
Definition: openflow-switch.cc:52
he-frame-exchange-manager.h
ns3::WifiPsdu::GetAddr2
Mac48Address GetAddr2(void) const
Get the Transmitter Address (TA), which is common to all the MPDUs.
Definition: wifi-psdu.cc:126
ns3::RxSignalInfo::snr
double snr
SNR in linear scale.
Definition: phy-entity.h:68
ns3::HeFrameExchangeManager::BlockAckAfterTbPpduTimeout
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...
Definition: he-frame-exchange-manager.cc:943
NS_LOG_DEBUG
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:273
ns3::WifiTxParameters::m_txVector
WifiTxVector m_txVector
TXVECTOR of the frame being prepared.
Definition: wifi-tx-parameters.h:62
ns3::HeFrameExchangeManager::SetWifiMac
void SetWifiMac(const Ptr< RegularWifiMac > mac) override
Set the MAC layer to use.
Definition: he-frame-exchange-manager.cc:77
ns3::CtrlTriggerHeader::GetCsRequired
bool GetCsRequired(void) const
Get the CS Required subfield of the Common Info field.
Definition: ctrl-headers.cc:1863
ns3::RxSignalInfo
RxSignalInfo structure containing info on the received signal.
Definition: phy-entity.h:67
ns3::Seconds
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1289
ns3::WifiAcknowledgment
WifiAcknowledgment is an abstract base struct.
Definition: wifi-acknowledgment.h:45
ns3::WifiTxVector::SetHeMuUserInfo
void SetHeMuUserInfo(uint16_t staId, HeMuUserInfo userInfo)
Set the HE MU user-specific transmission information for the given STA-ID.
Definition: wifi-tx-vector.cc:411
ns3::WifiNoAck
WifiNoAck specifies that no acknowledgment is required.
Definition: wifi-acknowledgment.h:130
ns3::FrameExchangeManager::m_mac
Ptr< RegularWifiMac > m_mac
the MAC layer on this station
Definition: frame-exchange-manager.h:381
ns3::WifiUlMuMultiStaBa::multiStaBaTxVector
WifiTxVector multiStaBaTxVector
TXVECTOR for the Multi-STA BlockAck.
Definition: wifi-acknowledgment.h:317
ns3::SnrTag::Get
double Get(void) const
Return the SNR value.
Definition: snr-tag.cc:89
ns3::AcIndex
AcIndex
This enumeration defines the Access Categories as an enumeration with values corresponding to the AC ...
Definition: qos-utils.h:71
NS_LOG_FUNCTION
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
Definition: log-macros-enabled.h:244
ns3::WifiDlMuBarBaSequence::stationsReplyingWithNormalAck
std::map< Mac48Address, AckInfo > stationsReplyingWithNormalAck
Set of stations replying with an Ack frame (no more than one)
Definition: wifi-acknowledgment.h:230
ns3::CtrlTriggerHeader::IsBsrp
bool IsBsrp(void) const
Check if this is a Buffer Status Report Poll Trigger frame.
Definition: ctrl-headers.cc:1792
ns3::WifiMacHeader::GetDuration
Time GetDuration(void) const
Return the duration from the Duration/ID field (Time object).
Definition: wifi-mac-header.cc:765
ns3::CtrlTriggerHeader::SetApTxPower
void SetApTxPower(int8_t power)
Set the AP TX Power subfield of the Common Info field.
Definition: ctrl-headers.cc:1957
ns3::QosTxop::ScheduleBar
void ScheduleBar(Ptr< const WifiMacQueueItem > bar, bool skipIfNoDataQueued=false)
Definition: qos-txop.cc:289
ns3::QosTxop::GetNextMpdu
Ptr< WifiMacQueueItem > GetNextMpdu(Ptr< const WifiMacQueueItem > peekedItem, WifiTxParameters &txParams, Time availableTime, bool initialFrame)
Prepare the frame to transmit starting from the MPDU that has been previously peeked by calling PeekN...
Definition: qos-txop.cc:437
ns3::HeFrameExchangeManager::m_txParams
WifiTxParameters m_txParams
the TX parameters for the current PPDU
Definition: he-frame-exchange-manager.h:219
ns3::WifiMacHeader::SetQosEosp
void SetQosEosp()
Set the end of service period (EOSP) bit in the QoS control field.
Definition: wifi-mac-header.cc:357
ns3::WifiMacHeader::IsCtl
bool IsCtl(void) const
Return true if the Type is Control.
Definition: wifi-mac-header.cc:571
ns3::WifiTxTimer::WAIT_BLOCK_ACKS_IN_TB_PPDU
@ WAIT_BLOCK_ACKS_IN_TB_PPDU
Definition: wifi-tx-timer.h:61
ns3::WifiMacHeader::SetType
void SetType(WifiMacType type, bool resetToDsFromDs=true)
Set Type/Subtype values with the correct values depending on the given type.
Definition: wifi-mac-header.cc:132
ns3::HeFrameExchangeManager::SendPsduMapWithProtection
void SendPsduMapWithProtection(WifiPsduMap psduMap, WifiTxParameters &txParams)
Send a map of PSDUs as a DL MU PPDU.
Definition: he-frame-exchange-manager.cc:197
ns3::QosTxop::PeekNextMpdu
Ptr< const WifiMacQueueItem > PeekNextMpdu(uint8_t tid=8, Mac48Address recipient=Mac48Address::GetBroadcast(), Ptr< const WifiMacQueueItem > item=nullptr)
Peek the next frame to transmit to the given receiver and of the given TID from the EDCA queue.
Definition: qos-txop.cc:357
ns3::TracedValueCallback::Time
void(* Time)(Time oldValue, Time newValue)
TracedValue callback signature for Time.
Definition: nstime.h:813
ns3::WifiUlMuMultiStaBa::stationsReceivingMultiStaBa
std::map< std::pair< Mac48Address, uint8_t >, std::size_t > stationsReceivingMultiStaBa
Map (originator, tid) pairs to the their index in baType.
Definition: wifi-acknowledgment.h:314
he-configuration.h
ns3::WifiTxTimer::NOT_RUNNING
@ NOT_RUNNING
Definition: wifi-tx-timer.h:56
ns3::WifiTxParameters::AddMpdu
void AddMpdu(Ptr< const WifiMacQueueItem > mpdu)
Record that an MPDU is being added to the current frame.
Definition: wifi-tx-parameters.cc:98
ns3::WifiMacHeader::SetDsNotTo
void SetDsNotTo(void)
Un-set the To DS bit in the Frame Control field.
Definition: wifi-mac-header.cc:102
ns3::WifiAcknowledgment::DL_MU_TF_MU_BAR
@ DL_MU_TF_MU_BAR
Definition: wifi-acknowledgment.h:57
ns3::MultiUserScheduler::TxFormat
TxFormat
Enumeration of the possible transmission formats.
Definition: multi-user-scheduler.h:63
ns3::WifiTxVector::SetBssColor
void SetBssColor(uint8_t color)
Set the BSS color.
Definition: wifi-tx-vector.cc:310
ns3::WIFI_MAC_CTL_BACKRESP
@ WIFI_MAC_CTL_BACKRESP
Definition: wifi-mac-header.h:44
ns3::WIFI_MOD_CLASS_VHT
@ WIFI_MOD_CLASS_VHT
VHT (Clause 22)
Definition: wifi-phy-common.h:131
ns3::WifiPsdu::begin
std::vector< Ptr< WifiMacQueueItem > >::const_iterator begin(void) const
Return a const iterator to the first MPDU.
Definition: wifi-psdu.cc:325
ns3::CtrlBAckRequestHeader::GetStartingSequence
uint16_t GetStartingSequence(void) const
Return the starting sequence number.
Definition: ctrl-headers.cc:239
ns3::HeFrameExchangeManager::m_staMac
Ptr< StaWifiMac > m_staMac
MAC pointer (null if not a STA)
Definition: he-frame-exchange-manager.h:202
ns3::WifiAcknowledgment::method
const Method method
acknowledgment method
Definition: wifi-acknowledgment.h:103
ns3::HeFrameExchangeManager::GetPsduTo
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,...
Definition: he-frame-exchange-manager.cc:245
ns3::HeFrameExchangeManager::~HeFrameExchangeManager
virtual ~HeFrameExchangeManager()
Definition: he-frame-exchange-manager.cc:60
ns3::WifiMacHeader::SetQosTid
void SetQosTid(uint8_t tid)
Set the TID for the QoS header.
Definition: wifi-mac-header.cc:352
ns3::GetMuBarSize
uint32_t GetMuBarSize(std::list< BlockAckReqType > types)
Return the total MU-BAR size (including FCS trailer).
Definition: wifi-utils.cc:83
ns3::CtrlBAckResponseHeader::FindPerAidTidInfoWithAid
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...
Definition: ctrl-headers.cc:628
ns3::WifiMacHeader::GetAddr2
Mac48Address GetAddr2(void) const
Return the address in the Address 2 field.
Definition: wifi-mac-header.cc:430
ns3::WifiPhy::GetSifs
Time GetSifs(void) const
Return the Short Interframe Space (SIFS) for this PHY.
Definition: wifi-phy.cc:911
ns3::WifiTxTimer::Set
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.
Definition: wifi-tx-timer.h:246
ns3::HeFrameExchangeManager::m_staExpectTbPpduFrom
std::set< Mac48Address > m_staExpectTbPpduFrom
set of stations expected to send a TB PPDU
Definition: he-frame-exchange-manager.h:222
ns3::HeFrameExchangeManager::TbPpduTimeout
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.
Definition: he-frame-exchange-manager.cc:844
ns3::CtrlTriggerHeader::FindUserInfoWithAid
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...
Definition: ctrl-headers.cc:2040
ns3::HeFrameExchangeManager::BlockAcksInTbPpduTimeout
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.
Definition: he-frame-exchange-manager.cc:875
ns3::WifiTxVector::IsDlMu
bool IsDlMu(void) const
Return true if this TX vector is used for a downlink multi-user transmission.
Definition: wifi-tx-vector.cc:376
ns3::HtFrameExchangeManager::GetPsduDurationId
virtual Time GetPsduDurationId(Time txDuration, const WifiTxParameters &txParams) const
Compute how to set the Duration/ID field of PSDUs that do not include fragments.
Definition: ht-frame-exchange-manager.cc:685
ns3::WifiDlMuAggregateTf::ulLength
uint16_t ulLength
the UL Length field of the MU-BAR Trigger Frames
Definition: wifi-acknowledgment.h:295
ns3::CtrlTriggerHeader::GetHeTbTxVector
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...
Definition: ctrl-headers.cc:1828
ns3::HeFrameExchangeManager::m_triggerFrame
Ptr< WifiMacQueueItem > m_triggerFrame
Trigger Frame being sent.
Definition: he-frame-exchange-manager.h:221
ns3::CtrlBAckRequestHeader::GetTidInfo
uint8_t GetTidInfo(void) const
Return the Traffic ID (TID).
Definition: ctrl-headers.cc:232
ns3::CtrlBAckResponseHeader
Headers for BlockAck response.
Definition: ctrl-headers.h:202
ns3::HtFrameExchangeManager::BlockAckTimeout
virtual void BlockAckTimeout(Ptr< WifiPsdu > psdu, const WifiTxVector &txVector)
Called when the BlockAck timeout expires.
Definition: ht-frame-exchange-manager.cc:1124
ns3::FrameExchangeManager::NormalAckTimeout
void NormalAckTimeout(Ptr< WifiMacQueueItem > mpdu, const WifiTxVector &txVector)
Called when the Ack timeout expires.
Definition: frame-exchange-manager.cc:767
ns3::HeFrameExchangeManager::SetMultiUserScheduler
void SetMultiUserScheduler(const Ptr< MultiUserScheduler > muScheduler)
Set the Multi-user Scheduler associated with this Frame Exchange Manager.
Definition: he-frame-exchange-manager.cc:98
NS_ABORT_MSG
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition: abort.h:50