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