A Discrete-Event Network Simulator
API
rr-multi-user-scheduler.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"
23 #include "ns3/wifi-protection.h"
24 #include "ns3/wifi-acknowledgment.h"
25 #include "ns3/wifi-psdu.h"
27 #include "he-configuration.h"
28 #include "he-phy.h"
29 #include <algorithm>
30 
31 namespace ns3 {
32 
33 NS_LOG_COMPONENT_DEFINE ("RrMultiUserScheduler");
34 
35 NS_OBJECT_ENSURE_REGISTERED (RrMultiUserScheduler);
36 
37 TypeId
39 {
40  static TypeId tid = TypeId ("ns3::RrMultiUserScheduler")
42  .SetGroupName ("Wifi")
43  .AddConstructor<RrMultiUserScheduler> ()
44  .AddAttribute ("NStations",
45  "The maximum number of stations that can be granted an RU in a DL MU OFDMA transmission",
46  UintegerValue (4),
48  MakeUintegerChecker<uint8_t> (1, 74))
49  .AddAttribute ("EnableTxopSharing",
50  "If enabled, allow A-MPDUs of different TIDs in a DL MU PPDU.",
51  BooleanValue (true),
54  .AddAttribute ("ForceDlOfdma",
55  "If enabled, return DL_MU_TX even if no DL MU PPDU could be built.",
56  BooleanValue (false),
59  .AddAttribute ("EnableUlOfdma",
60  "If enabled, return UL_MU_TX if DL_MU_TX was returned the previous time.",
61  BooleanValue (true),
64  .AddAttribute ("EnableBsrp",
65  "If enabled, send a BSRP Trigger Frame before an UL MU transmission.",
66  BooleanValue (true),
69  .AddAttribute ("UlPsduSize",
70  "The default size in bytes of the solicited PSDU (to be sent in a TB PPDU)",
71  UintegerValue (500),
73  MakeUintegerChecker<uint32_t> ())
74  .AddAttribute ("UseCentral26TonesRus",
75  "If enabled, central 26-tone RUs are allocated, too, when the "
76  "selected RU type is at least 52 tones.",
77  BooleanValue (false),
80  .AddAttribute ("MaxCredits",
81  "Maximum amount of credits a station can have. When transmitting a DL MU PPDU, "
82  "the amount of credits received by each station equals the TX duration (in "
83  "microseconds) divided by the total number of stations. Stations that are the "
84  "recipient of the DL MU PPDU have to pay a number of credits equal to the TX "
85  "duration (in microseconds) times the allocated bandwidth share",
86  TimeValue (Seconds (1)),
88  MakeTimeChecker ())
89  ;
90  return tid;
91 }
92 
94 {
95  NS_LOG_FUNCTION (this);
96 }
97 
99 {
101 }
102 
103 void
105 {
106  NS_LOG_FUNCTION (this);
107  NS_ASSERT (m_apMac != nullptr);
108  m_apMac->TraceConnectWithoutContext ("AssociatedSta",
110  m_apMac->TraceConnectWithoutContext ("DeAssociatedSta",
112  for (const auto& ac : wifiAcList)
113  {
114  m_staList.insert ({ac.first, {}});
115  }
117 }
118 
119 void
121 {
122  NS_LOG_FUNCTION (this);
123  m_staList.clear ();
124  m_candidates.clear ();
125  m_txParams.Clear ();
126  m_apMac->TraceDisconnectWithoutContext ("AssociatedSta",
128  m_apMac->TraceDisconnectWithoutContext ("DeAssociatedSta",
131 }
132 
135 {
136  NS_LOG_FUNCTION (this);
137 
139 
140  if (mpdu != 0 && !GetWifiRemoteStationManager ()->GetHeSupported (mpdu->GetHeader ().GetAddr1 ()))
141  {
142  return SU_TX;
143  }
144 
146  {
147  return TrySendingBsrpTf ();
148  }
149 
152  {
153  TxFormat txFormat = TrySendingBasicTf ();
154 
155  if (txFormat != DL_MU_TX)
156  {
157  return txFormat;
158  }
159  }
160 
161  return TrySendingDlMuPpdu ();
162 }
163 
166 {
167  NS_LOG_FUNCTION (this);
168 
170 
173 
174  Ptr<Packet> packet = Create<Packet> ();
175  packet->AddHeader (m_trigger);
176 
178  if (m_trigger.GetNUserInfoFields () == 1)
179  {
180  NS_ASSERT (m_apMac->GetStaList ().find (m_trigger.begin ()->GetAid12 ()) != m_apMac->GetStaList ().end ());
181  receiver = m_apMac->GetStaList ().at (m_trigger.begin ()->GetAid12 ());
182  }
183 
185  m_triggerMacHdr.SetAddr1 (receiver);
186  m_triggerMacHdr.SetAddr2 (m_apMac->GetAddress ());
189 
190  Ptr<WifiMacQueueItem> item = Create<WifiMacQueueItem> (packet, m_triggerMacHdr);
191 
192  m_txParams.Clear ();
193  // set the TXVECTOR used to send the Trigger Frame
194  m_txParams.m_txVector = m_apMac->GetWifiRemoteStationManager ()->GetRtsTxVector (receiver);
195 
196  if (!m_heFem->TryAddMpdu (item, m_txParams, m_availableTime))
197  {
198  // sending the BSRP Trigger Frame is not possible, hence return NO_TX. In
199  // this way, no transmission will occur now and the next time we will
200  // try again sending a BSRP Trigger Frame.
201  NS_LOG_DEBUG ("Remaining TXOP duration is not enough for BSRP TF exchange");
202  return NO_TX;
203  }
204 
205  // Compute the time taken by each station to transmit 8 QoS Null frames
206  Time qosNullTxDuration = Seconds (0);
207  for (const auto& userInfo : m_trigger)
208  {
209  Time duration = WifiPhy::CalculateTxDuration (m_sizeOf8QosNull, txVector,
210  m_apMac->GetWifiPhy ()->GetPhyBand (),
211  userInfo.GetAid12 ());
212  qosNullTxDuration = Max (qosNullTxDuration, duration);
213  }
214 
215  if (m_availableTime != Time::Min ())
216  {
217  // TryAddMpdu only considers the time to transmit the Trigger Frame
219  NS_ASSERT (m_txParams.m_acknowledgment && m_txParams.m_acknowledgment->acknowledgmentTime.IsZero ());
221 
222  if (m_txParams.m_protection->protectionTime
223  + m_txParams.m_txDuration // BSRP TF tx time
224  + m_apMac->GetWifiPhy ()->GetSifs ()
225  + qosNullTxDuration
226  > m_availableTime)
227  {
228  NS_LOG_DEBUG ("Remaining TXOP duration is not enough for BSRP TF exchange");
229  return NO_TX;
230  }
231  }
232 
233  uint16_t ulLength;
234  std::tie (ulLength, qosNullTxDuration) = HePhy::ConvertHeTbPpduDurationToLSigLength (qosNullTxDuration,
235  m_trigger.GetHeTbTxVector (m_trigger.begin ()->GetAid12 ()),
236  m_apMac->GetWifiPhy ()->GetPhyBand ());
237  NS_LOG_DEBUG ("Duration of QoS Null frames: " << qosNullTxDuration.As (Time::MS));
238  m_trigger.SetUlLength (ulLength);
239 
240  return UL_MU_TX;
241 }
242 
245 {
246  NS_LOG_FUNCTION (this);
247 
248  // check if an UL OFDMA transmission is possible after a DL OFDMA transmission
249  NS_ABORT_MSG_IF (m_ulPsduSize == 0, "The UlPsduSize attribute must be set to a non-null value");
250 
251  // determine which of the stations served in DL have UL traffic
252  uint32_t maxBufferSize = 0;
253  // candidates sorted in decreasing order of queue size
254  std::multimap<uint8_t, CandidateInfo, std::greater<uint8_t>> ulCandidates;
255 
256  for (const auto& candidate : m_candidates)
257  {
258  uint8_t queueSize = m_apMac->GetMaxBufferStatus (candidate.first->address);
259  if (queueSize == 255)
260  {
261  NS_LOG_DEBUG ("Buffer status of station " << candidate.first->address << " is unknown");
262  maxBufferSize = std::max (maxBufferSize, m_ulPsduSize);
263  }
264  else if (queueSize == 254)
265  {
266  NS_LOG_DEBUG ("Buffer status of station " << candidate.first->address << " is not limited");
267  maxBufferSize = 0xffffffff;
268  }
269  else
270  {
271  NS_LOG_DEBUG ("Buffer status of station " << candidate.first->address << " is " << +queueSize);
272  maxBufferSize = std::max (maxBufferSize, static_cast<uint32_t> (queueSize * 256));
273  }
274  // serve the station if its queue size is not null
275  if (queueSize > 0)
276  {
277  ulCandidates.emplace (queueSize, candidate);
278  }
279  }
280 
281  // if the maximum buffer size is 0, skip UL OFDMA and proceed with trying DL OFDMA
282  if (maxBufferSize > 0)
283  {
284  NS_ASSERT (!ulCandidates.empty ());
285  std::size_t count = ulCandidates.size ();
286  std::size_t nCentral26TonesRus;
287  HeRu::RuType ruType = HeRu::GetEqualSizedRusForStations (m_apMac->GetWifiPhy ()->GetChannelWidth (),
288  count, nCentral26TonesRus);
289  if (!m_useCentral26TonesRus || ulCandidates.size () == count)
290  {
291  nCentral26TonesRus = 0;
292  }
293  else
294  {
295  nCentral26TonesRus = std::min (ulCandidates.size () - count, nCentral26TonesRus);
296  }
297 
298  WifiTxVector txVector;
300  auto candidateIt = ulCandidates.begin ();
301 
302  if (GetLastTxFormat () == DL_MU_TX)
303  {
304  txVector.SetChannelWidth (GetDlMuInfo ().txParams.m_txVector.GetChannelWidth ());
305  txVector.SetGuardInterval (CtrlTriggerHeader ().GetGuardInterval ());
306 
307  for (std::size_t i = 0; i < count + nCentral26TonesRus; i++)
308  {
309  NS_ASSERT (candidateIt != ulCandidates.end ());
310  uint16_t staId = candidateIt->second.first->aid;
311  // AssignRuIndices will be called below to set RuSpec
312  txVector.SetHeMuUserInfo (staId,
313  {{(i < count ? ruType : HeRu::RU_26_TONE), 1, false},
315  GetDlMuInfo ().txParams.m_txVector.GetNss (staId)});
316 
317  candidateIt++;
318  }
319  }
320  else
321  {
324 
325  for (std::size_t i = 0; i < count + nCentral26TonesRus; i++)
326  {
327  NS_ASSERT (candidateIt != ulCandidates.end ());
328  uint16_t staId = candidateIt->second.first->aid;
329  auto userInfoIt = GetUlMuInfo ().trigger.FindUserInfoWithAid (staId);
330  NS_ASSERT (userInfoIt != GetUlMuInfo ().trigger.end ());
331  // AssignRuIndices will be called below to set RuSpec
332  txVector.SetHeMuUserInfo (staId,
333  {{(i < count ? ruType : HeRu::RU_26_TONE), 1, false},
334  HePhy::GetHeMcs (userInfoIt->GetUlMcs ()),
335  userInfoIt->GetNss ()});
336 
337  candidateIt++;
338  }
339  }
340 
341  // remove candidates that will not be served
342  ulCandidates.erase (candidateIt, ulCandidates.end ());
343  AssignRuIndices (txVector);
344 
346  Ptr<Packet> packet = Create<Packet> ();
347  packet->AddHeader (m_trigger);
348 
350  if (ulCandidates.size () == 1)
351  {
352  receiver = ulCandidates.begin ()->second.first->address;
353  }
354 
356  m_triggerMacHdr.SetAddr1 (receiver);
357  m_triggerMacHdr.SetAddr2 (m_apMac->GetAddress ());
360 
361  Ptr<WifiMacQueueItem> item = Create<WifiMacQueueItem> (packet, m_triggerMacHdr);
362 
363  // compute the maximum amount of time that can be granted to stations.
364  // This value is limited by the max PPDU duration
365  Time maxDuration = GetPpduMaxTime (txVector.GetPreambleType ());
366 
367  m_txParams.Clear ();
368  // set the TXVECTOR used to send the Trigger Frame
369  m_txParams.m_txVector = m_apMac->GetWifiRemoteStationManager ()->GetRtsTxVector (receiver);
370 
371  if (!m_heFem->TryAddMpdu (item, m_txParams, m_availableTime))
372  {
373  // an UL OFDMA transmission is not possible, hence return NO_TX. In
374  // this way, no transmission will occur now and the next time we will
375  // try again performing an UL OFDMA transmission.
376  NS_LOG_DEBUG ("Remaining TXOP duration is not enough for UL MU exchange");
377  return NO_TX;
378  }
379 
380  if (m_availableTime != Time::Min ())
381  {
382  // TryAddMpdu only considers the time to transmit the Trigger Frame
386 
387  maxDuration = Min (maxDuration, m_availableTime
388  - m_txParams.m_protection->protectionTime
390  - m_apMac->GetWifiPhy ()->GetSifs ()
391  - m_txParams.m_acknowledgment->acknowledgmentTime);
392  if (maxDuration.IsNegative ())
393  {
394  NS_LOG_DEBUG ("Remaining TXOP duration is not enough for UL MU exchange");
395  return NO_TX;
396  }
397  }
398 
399  // Compute the time taken by each station to transmit a frame of maxBufferSize size
400  Time bufferTxTime = Seconds (0);
401  for (const auto& userInfo : m_trigger)
402  {
403  Time duration = WifiPhy::CalculateTxDuration (maxBufferSize, txVector,
404  m_apMac->GetWifiPhy ()->GetPhyBand (),
405  userInfo.GetAid12 ());
406  bufferTxTime = Max (bufferTxTime, duration);
407  }
408 
409  if (bufferTxTime < maxDuration)
410  {
411  // the maximum buffer size can be transmitted within the allowed time
412  maxDuration = bufferTxTime;
413  }
414  else
415  {
416  // maxDuration may be a too short time. If it does not allow any station to
417  // transmit at least m_ulPsduSize bytes, give up the UL MU transmission for now
418  Time minDuration = Seconds (0);
419  for (const auto& userInfo : m_trigger)
420  {
421  Time duration = WifiPhy::CalculateTxDuration (m_ulPsduSize, txVector,
422  m_apMac->GetWifiPhy ()->GetPhyBand (),
423  userInfo.GetAid12 ());
424  minDuration = (minDuration.IsZero () ? duration : Min (minDuration, duration));
425  }
426 
427  if (maxDuration < minDuration)
428  {
429  // maxDuration is a too short time, hence return NO_TX. In this way,
430  // no transmission will occur now and the next time we will try again
431  // performing an UL OFDMA transmission.
432  NS_LOG_DEBUG ("Available time " << maxDuration.As (Time::MS) << " is too short");
433  return NO_TX;
434  }
435  }
436 
437  // maxDuration is the time to grant to the stations. Finalize the Trigger Frame
438  uint16_t ulLength;
439  std::tie (ulLength, maxDuration) = HePhy::ConvertHeTbPpduDurationToLSigLength (maxDuration,
440  txVector,
441  m_apMac->GetWifiPhy ()->GetPhyBand ());
442  NS_LOG_DEBUG ("TB PPDU duration: " << maxDuration.As (Time::MS));
443  m_trigger.SetUlLength (ulLength);
444  // set Preferred AC to the AC that gained channel access
445  for (auto& userInfo : m_trigger)
446  {
447  userInfo.SetBasicTriggerDepUserInfo (0, 0, m_edca->GetAccessCategory ());
448  }
449 
450  return UL_MU_TX;
451  }
452  return DL_MU_TX;
453 }
454 
455 void
457 {
458  NS_LOG_FUNCTION (this << aid << address);
459 
460  if (GetWifiRemoteStationManager ()->GetHeSupported (address))
461  {
462  for (auto& staList : m_staList)
463  {
464  staList.second.push_back (MasterInfo {aid, address, 0.0});
465  }
466  }
467 }
468 
469 void
471 {
472  NS_LOG_FUNCTION (this << aid << address);
473 
474  if (GetWifiRemoteStationManager ()->GetHeSupported (address))
475  {
476  for (auto& staList : m_staList)
477  {
478  staList.second.remove_if ([&aid, &address] (const MasterInfo& info)
479  { return info.aid == aid && info.address == address; });
480  }
481  }
482 }
483 
486 {
487  NS_LOG_FUNCTION (this);
488 
489  AcIndex primaryAc = m_edca->GetAccessCategory ();
490 
491  if (m_staList[primaryAc].empty ())
492  {
493  NS_LOG_DEBUG ("No HE stations associated: return SU_TX");
494  return TxFormat::SU_TX;
495  }
496 
497  std::size_t count = std::min (static_cast<std::size_t> (m_nStations), m_staList[primaryAc].size ());
498  std::size_t nCentral26TonesRus;
499  HeRu::RuType ruType = HeRu::GetEqualSizedRusForStations (m_apMac->GetWifiPhy ()->GetChannelWidth (), count,
500  nCentral26TonesRus);
501  NS_ASSERT (count >= 1);
502 
504  {
505  nCentral26TonesRus = 0;
506  }
507 
508  uint8_t currTid = wifiAcList.at (primaryAc).GetHighTid ();
509 
511 
512  if (mpdu != nullptr && mpdu->GetHeader ().IsQosData ())
513  {
514  currTid = mpdu->GetHeader ().GetQosTid ();
515  }
516 
517  // determine the list of TIDs to check
518  std::vector<uint8_t> tids;
519 
521  {
522  for (auto acIt = wifiAcList.find (primaryAc); acIt != wifiAcList.end (); acIt++)
523  {
524  uint8_t firstTid = (acIt->first == primaryAc ? currTid : acIt->second.GetHighTid ());
525  tids.push_back (firstTid);
526  tids.push_back (acIt->second.GetOtherTid (firstTid));
527  }
528  }
529  else
530  {
531  tids.push_back (currTid);
532  }
533 
534  Ptr<HeConfiguration> heConfiguration = m_apMac->GetHeConfiguration ();
535  NS_ASSERT (heConfiguration != 0);
536 
537  m_txParams.Clear ();
539  m_txParams.m_txVector.SetChannelWidth (m_apMac->GetWifiPhy ()->GetChannelWidth ());
540  m_txParams.m_txVector.SetGuardInterval (heConfiguration->GetGuardInterval ().GetNanoSeconds ());
541  m_txParams.m_txVector.SetBssColor (heConfiguration->GetBssColor ());
542 
543  // The TXOP limit can be exceeded by the TXOP holder if it does not transmit more
544  // than one Data or Management frame in the TXOP and the frame is not in an A-MPDU
545  // consisting of more than one MPDU (Sec. 10.22.2.8 of 802.11-2016).
546  // For the moment, we are considering just one MPDU per receiver.
547  Time actualAvailableTime = (m_initialFrame ? Time::Min () : m_availableTime);
548 
549  // iterate over the associated stations until an enough number of stations is identified
550  auto staIt = m_staList[primaryAc].begin ();
551  m_candidates.clear ();
552 
553  while (staIt != m_staList[primaryAc].end ()
554  && m_candidates.size () < std::min (static_cast<std::size_t> (m_nStations), count + nCentral26TonesRus))
555  {
556  NS_LOG_DEBUG ("Next candidate STA (MAC=" << staIt->address << ", AID=" << staIt->aid << ")");
557 
558  HeRu::RuType currRuType = (m_candidates.size () < count ? ruType : HeRu::RU_26_TONE);
559 
560  // check if the AP has at least one frame to be sent to the current station
561  for (uint8_t tid : tids)
562  {
563  AcIndex ac = QosUtilsMapTidToAc (tid);
564  NS_ASSERT (ac >= primaryAc);
565  // check that a BA agreement is established with the receiver for the
566  // considered TID, since ack sequences for DL MU PPDUs require block ack
567  if (m_apMac->GetQosTxop (ac)->GetBaAgreementEstablished (staIt->address, tid))
568  {
569  mpdu = m_apMac->GetQosTxop (ac)->PeekNextMpdu (tid, staIt->address);
570 
571  // we only check if the first frame of the current TID meets the size
572  // and duration constraints. We do not explore the queues further.
573  if (mpdu != 0)
574  {
575  // Use a temporary TX vector including only the STA-ID of the
576  // candidate station to check if the MPDU meets the size and time limits.
577  // An RU of the computed size is tentatively assigned to the candidate
578  // station, so that the TX duration can be correctly computed.
579  WifiTxVector suTxVector = GetWifiRemoteStationManager ()->GetDataTxVector (mpdu->GetHeader ()),
580  txVectorCopy = m_txParams.m_txVector;
581 
583  {{currRuType, 1, false},
584  suTxVector.GetMode (),
585  suTxVector.GetNss ()});
586 
587  if (!m_heFem->TryAddMpdu (mpdu, m_txParams, actualAvailableTime))
588  {
589  NS_LOG_DEBUG ("Adding the peeked frame violates the time constraints");
590  m_txParams.m_txVector = txVectorCopy;
591  }
592  else
593  {
594  // the frame meets the constraints
595  NS_LOG_DEBUG ("Adding candidate STA (MAC=" << staIt->address << ", AID="
596  << staIt->aid << ") TID=" << +tid);
597  m_candidates.push_back ({staIt, mpdu});
598  break; // terminate the for loop
599  }
600  }
601  else
602  {
603  NS_LOG_DEBUG ("No frames to send to " << staIt->address << " with TID=" << +tid);
604  }
605  }
606  }
607 
608  // move to the next station in the list
609  staIt++;
610  }
611 
612  if (m_candidates.empty ())
613  {
614  if (m_forceDlOfdma)
615  {
616  NS_LOG_DEBUG ("The AP does not have suitable frames to transmit: return NO_TX");
617  return NO_TX;
618  }
619  NS_LOG_DEBUG ("The AP does not have suitable frames to transmit: return SU_TX");
620  return SU_TX;
621  }
622 
623  return TxFormat::DL_MU_TX;
624 }
625 
626 MultiUserScheduler::DlMuInfo
627 RrMultiUserScheduler::ComputeDlMuInfo (void)
628 {
629  NS_LOG_FUNCTION (this);
630 
631  if (m_candidates.empty ())
632  {
633  return DlMuInfo ();
634  }
635 
636  uint16_t bw = m_apMac->GetWifiPhy ()->GetChannelWidth ();
637 
638  // compute how many stations can be granted an RU and the RU size
639  std::size_t nRusAssigned = m_txParams.GetPsduInfoMap ().size ();
640  std::size_t nCentral26TonesRus;
641  HeRu::RuType ruType = HeRu::GetEqualSizedRusForStations (bw, nRusAssigned, nCentral26TonesRus);
642 
643  NS_LOG_DEBUG (nRusAssigned << " stations are being assigned a " << ruType << " RU");
644 
645  if (!m_useCentral26TonesRus || m_candidates.size () == nRusAssigned)
646  {
647  nCentral26TonesRus = 0;
648  }
649  else
650  {
651  nCentral26TonesRus = std::min (m_candidates.size () - nRusAssigned, nCentral26TonesRus);
652  NS_LOG_DEBUG (nCentral26TonesRus << " stations are being assigned a 26-tones RU");
653  }
654 
655  DlMuInfo dlMuInfo;
656 
657  // We have to update the TXVECTOR
658  dlMuInfo.txParams.m_txVector.SetPreambleType (m_txParams.m_txVector.GetPreambleType ());
659  dlMuInfo.txParams.m_txVector.SetChannelWidth (m_txParams.m_txVector.GetChannelWidth ());
660  dlMuInfo.txParams.m_txVector.SetGuardInterval (m_txParams.m_txVector.GetGuardInterval ());
661  dlMuInfo.txParams.m_txVector.SetBssColor (m_txParams.m_txVector.GetBssColor ());
662 
663  auto candidateIt = m_candidates.begin (); // iterator over the list of candidate receivers
664 
665  for (std::size_t i = 0; i < nRusAssigned + nCentral26TonesRus; i++)
666  {
667  NS_ASSERT (candidateIt != m_candidates.end ());
668 
669  uint16_t staId = candidateIt->first->aid;
670  // AssignRuIndices will be called below to set RuSpec
671  dlMuInfo.txParams.m_txVector.SetHeMuUserInfo (staId,
672  {{(i < nRusAssigned ? ruType : HeRu::RU_26_TONE), 1, false},
673  m_txParams.m_txVector.GetMode (staId),
674  m_txParams.m_txVector.GetNss (staId)});
675  candidateIt++;
676  }
677 
678  // remove candidates that will not be served
679  m_candidates.erase (candidateIt, m_candidates.end ());
680 
681  AssignRuIndices (dlMuInfo.txParams.m_txVector);
682  m_txParams.Clear ();
683 
685 
686  // Compute the TX params (again) by using the stored MPDUs and the final TXVECTOR
687  Time actualAvailableTime = (m_initialFrame ? Time::Min () : m_availableTime);
688 
689  for (const auto& candidate : m_candidates)
690  {
691  mpdu = candidate.second;
692  NS_ASSERT (mpdu != nullptr);
693 
694  [[maybe_unused]] bool ret = m_heFem->TryAddMpdu (mpdu, dlMuInfo.txParams, actualAvailableTime);
695  NS_ASSERT_MSG (ret, "Weird that an MPDU does not meet constraints when "
696  "transmitted over a larger RU");
697  }
698 
699  // We have to complete the PSDUs to send
700  Ptr<WifiMacQueue> queue;
701  Mac48Address receiver;
702 
703  for (const auto& candidate : m_candidates)
704  {
705  // Let us try first A-MSDU aggregation if possible
706  mpdu = candidate.second;
707  NS_ASSERT (mpdu != nullptr);
708  uint8_t tid = mpdu->GetHeader ().GetQosTid ();
709  receiver = mpdu->GetHeader ().GetAddr1 ();
710  NS_ASSERT (receiver == candidate.first->address);
711 
712  NS_ASSERT (mpdu->IsQueued ());
713  Ptr<WifiMacQueueItem> item = mpdu->GetItem ();
714 
715  if (!mpdu->GetHeader ().IsRetry ())
716  {
717  // this MPDU must have been dequeued from the AC queue and we can try
718  // A-MSDU aggregation
719  item = m_heFem->GetMsduAggregator ()->GetNextAmsdu (mpdu, dlMuInfo.txParams, m_availableTime);
720 
721  if (item == nullptr)
722  {
723  // A-MSDU aggregation failed or disabled
724  item = mpdu->GetItem ();
725  }
726  m_apMac->GetQosTxop (QosUtilsMapTidToAc (tid))->AssignSequenceNumber (item);
727  }
728 
729  // Now, let's try A-MPDU aggregation if possible
730  std::vector<Ptr<WifiMacQueueItem>> mpduList = m_heFem->GetMpduAggregator ()->GetNextAmpdu (item, dlMuInfo.txParams, m_availableTime);
731 
732  if (mpduList.size () > 1)
733  {
734  // A-MPDU aggregation succeeded, update psduMap
735  dlMuInfo.psduMap[candidate.first->aid] = Create<WifiPsdu> (std::move (mpduList));
736  }
737  else
738  {
739  dlMuInfo.psduMap[candidate.first->aid] = Create<WifiPsdu> (item, true);
740  }
741  }
742 
743  AcIndex primaryAc = m_edca->GetAccessCategory ();
744 
745  // The amount of credits received by each station equals the TX duration (in
746  // microseconds) divided by the number of stations.
747  double creditsPerSta = dlMuInfo.txParams.m_txDuration.ToDouble (Time::US)
748  / m_staList[primaryAc].size ();
749  // Transmitting stations have to pay a number of credits equal to the TX duration
750  // (in microseconds) times the allocated bandwidth share.
751  double debitsPerMhz = dlMuInfo.txParams.m_txDuration.ToDouble (Time::US)
752  / (nRusAssigned * HeRu::GetBandwidth (ruType)
753  + nCentral26TonesRus * HeRu::GetBandwidth (HeRu::RU_26_TONE));
754 
755  // assign credits to all stations
756  for (auto& sta : m_staList[primaryAc])
757  {
758  sta.credits += creditsPerSta;
759  sta.credits = std::min (sta.credits, m_maxCredits.ToDouble (Time::US));
760  }
761 
762  // subtract debits to the selected stations
763  candidateIt = m_candidates.begin ();
764 
765  for (std::size_t i = 0; i < nRusAssigned + nCentral26TonesRus; i++)
766  {
767  NS_ASSERT (candidateIt != m_candidates.end ());
768 
769  candidateIt->first->credits -= debitsPerMhz * HeRu::GetBandwidth (i < nRusAssigned ? ruType : HeRu::RU_26_TONE);
770 
771  candidateIt++;
772  }
773 
774  // sort the list in decreasing order of credits
775  m_staList[primaryAc].sort ([] (const MasterInfo& a, const MasterInfo& b)
776  { return a.credits > b.credits; });
777 
778  NS_LOG_DEBUG ("Next station to serve has AID=" << m_staList[primaryAc].front ().aid);
779 
780  return dlMuInfo;
781 }
782 
783 void
784 RrMultiUserScheduler::AssignRuIndices (WifiTxVector& txVector)
785 {
786  NS_LOG_FUNCTION (this << txVector);
787 
788  uint8_t bw = txVector.GetChannelWidth ();
789 
790  // find the RU types allocated in the TXVECTOR
791  std::set<HeRu::RuType> ruTypeSet;
792  for (const auto& userInfo : txVector.GetHeMuUserInfoMap ())
793  {
794  ruTypeSet.insert (userInfo.second.ru.GetRuType ());
795  }
796 
797  std::vector<HeRu::RuSpec> ruSet, central26TonesRus;
798 
799  // This scheduler allocates equal sized RUs and optionally the remaining 26-tone RUs
800  if (ruTypeSet.size () == 2)
801  {
802  // central 26-tone RUs have been allocated
803  NS_ASSERT (ruTypeSet.find (HeRu::RU_26_TONE) != ruTypeSet.end ());
804  ruTypeSet.erase (HeRu::RU_26_TONE);
805  NS_ASSERT (ruTypeSet.size () == 1);
806  central26TonesRus = HeRu::GetCentral26TonesRus (bw, *ruTypeSet.begin ());
807  }
808 
809  NS_ASSERT (ruTypeSet.size () == 1);
810  ruSet = HeRu::GetRusOfType (bw, *ruTypeSet.begin ());
811 
812  auto ruSetIt = ruSet.begin ();
813  auto central26TonesRusIt = central26TonesRus.begin ();
814 
815  for (const auto& userInfo : txVector.GetHeMuUserInfoMap ())
816  {
817  if (userInfo.second.ru.GetRuType () == *ruTypeSet.begin ())
818  {
819  NS_ASSERT (ruSetIt != ruSet.end ());
820  txVector.SetRu (*ruSetIt, userInfo.first);
821  ruSetIt++;
822  }
823  else
824  {
825  NS_ASSERT (central26TonesRusIt != central26TonesRus.end ());
826  txVector.SetRu (*central26TonesRusIt, userInfo.first);
827  central26TonesRusIt++;
828  }
829  }
830 }
831 
833 RrMultiUserScheduler::ComputeUlMuInfo (void)
834 {
835  return UlMuInfo {m_trigger, m_triggerMacHdr, std::move (m_txParams)};
836 }
837 
838 } //namespace ns3
#define min(a, b)
Definition: 80211b.c:42
#define max(a, b)
Definition: 80211b.c:43
#define Min(a, b)
AttributeValue implementation for Boolean.
Definition: boolean.h:37
Headers for Trigger frames.
Definition: ctrl-headers.h:886
ConstIterator begin(void) const
Get a const iterator pointing to the first User Info field in the list.
WifiTxVector GetHeTbTxVector(uint16_t staId) const
Get the TX vector that the station with the given STA-ID will use to send the HE TB PPDU solicited by...
uint16_t GetUlBandwidth(void) const
Get the bandwidth of the solicited HE TB PPDU.
std::size_t GetNUserInfoFields(void) const
Get the number of User Info fields in this Trigger Frame.
ConstIterator FindUserInfoWithAid(ConstIterator start, uint16_t aid12) const
Get a const iterator pointing to the first User Info field found (starting from the one pointed to by...
TriggerFrameType GetType(void) const
Get the Trigger Frame type.
void SetUlLength(uint16_t len)
Set the UL Length subfield of the Common Info field.
uint16_t GetGuardInterval(void) const
Get the guard interval duration (in nanoseconds) of the solicited HE TB PPDU.
static WifiMode GetHeMcs(uint8_t index)
Return the HE MCS corresponding to the provided index.
Definition: he-phy.cc:1015
static std::pair< uint16_t, Time > ConvertHeTbPpduDurationToLSigLength(Time ppduDuration, const WifiTxVector &txVector, WifiPhyBand band)
Compute the L-SIG length value corresponding to the given HE TB PPDU duration.
Definition: he-phy.cc:274
RuType
The different HE Resource Unit (RU) types.
Definition: he-ru.h:42
@ RU_26_TONE
Definition: he-ru.h:43
static RuType GetEqualSizedRusForStations(uint16_t bandwidth, std::size_t &nStations, std::size_t &nCentral26TonesRus)
Given the channel bandwidth and the number of stations candidate for being assigned an RU,...
Definition: he-ru.cc:537
an EUI-48 address
Definition: mac48-address.h:44
static Mac48Address GetBroadcast(void)
MultiUserScheduler is an abstract base class defining the API that APs supporting at least VHT can us...
bool m_initialFrame
true if a TXOP is being started
void DoInitialize(void) override
Initialize() implementation.
Ptr< ApWifiMac > m_apMac
the AP wifi MAC
Time m_availableTime
the time available for frame exchange
void DoDispose(void) override
Destructor implementation.
TxFormat GetLastTxFormat(void) const
Get the format of the last transmission, as determined by the last call to NotifyAccessGranted that d...
Ptr< HeFrameExchangeManager > m_heFem
HE Frame Exchange Manager.
Ptr< WifiRemoteStationManager > GetWifiRemoteStationManager(void) const
Get the station manager attached to the AP.
Ptr< QosTxop > m_edca
the AC that gained channel access
uint32_t m_sizeOf8QosNull
size in bytes of 8 QoS Null frames
UlMuInfo & GetUlMuInfo(void)
Get the information required to solicit an UL MU transmission.
DlMuInfo & GetDlMuInfo(void)
Get the information required to perform a DL MU transmission.
TxFormat
Enumeration of the possible transmission formats.
void AddHeader(const Header &header)
Add header to this packet.
Definition: packet.cc:256
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:74
AcIndex GetAccessCategory(void) const
Get the access category of this object.
Definition: qos-txop.cc:745
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
RrMultiUserScheduler is a simple OFDMA scheduler that indicates to perform a DL OFDMA transmission if...
virtual TxFormat TrySendingBsrpTf(void)
Check if it is possible to send a BSRP Trigger Frame given the current time limits.
void DoDispose(void) override
Destructor implementation.
bool m_enableBsrp
send a BSRP before an UL MU transmission
void NotifyStationAssociated(uint16_t aid, Mac48Address address)
Notify the scheduler that a station associated with the AP.
uint32_t m_ulPsduSize
the size in byte of the solicited PSDU
std::list< CandidateInfo > m_candidates
Candidate stations for MU TX.
bool m_useCentral26TonesRus
whether to allocate central 26-tone RUs
bool m_forceDlOfdma
return DL_OFDMA even if no DL MU PPDU was built
bool m_enableUlOfdma
enable the scheduler to also return UL_OFDMA
virtual TxFormat TrySendingDlMuPpdu(void)
Check if it is possible to send a DL MU PPDU given the current time limits.
WifiMacHeader m_triggerMacHdr
MAC header for Trigger Frame.
TxFormat SelectTxFormat(void) override
Select the format of the next transmission.
uint8_t m_nStations
Number of stations/slots to fill.
WifiTxParameters m_txParams
TX parameters.
virtual TxFormat TrySendingBasicTf(void)
Check if it is possible to send a Basic Trigger Frame given the current time limits.
void AssignRuIndices(WifiTxVector &txVector)
Assign an RU index to all the RUs allocated by the given TXVECTOR.
void NotifyStationDeassociated(uint16_t aid, Mac48Address address)
Notify the scheduler that a station deassociated with the AP.
Time m_maxCredits
Max amount of credits a station can have.
std::map< AcIndex, std::list< MasterInfo > > m_staList
Per-AC list of stations (next to serve first)
bool m_enableTxopSharing
allow A-MPDUs of different TIDs in a DL MU PPDU
CtrlTriggerHeader m_trigger
Trigger Frame to send.
void DoInitialize(void) override
Initialize() implementation.
static TypeId GetTypeId(void)
Get the type ID.
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:103
double ToDouble(enum Unit unit) const
Get the Time value expressed in a particular unit.
Definition: nstime.h:529
static Time Min()
Minimum representable Time Not to be confused with Min(Time,Time).
Definition: nstime.h:273
@ MS
millisecond
Definition: nstime.h:115
bool IsZero(void) const
Exactly equivalent to t == 0.
Definition: nstime.h:300
TimeWithUnit As(const enum Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition: time.cc:418
bool IsNegative(void) const
Exactly equivalent to t <= 0.
Definition: nstime.h:308
AttributeValue implementation for Time.
Definition: nstime.h:1308
a unique identifier for an interface.
Definition: type-id.h:59
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:922
Hold an unsigned integer type.
Definition: uinteger.h:44
Implements the IEEE 802.11 MAC header.
void SetDsNotFrom(void)
Un-set the From DS bit in the Frame Control field.
void SetAddr1(Mac48Address address)
Fill the Address 1 field with the given address.
void SetDsNotTo(void)
Un-set the To DS bit in the Frame Control field.
void SetAddr2(Mac48Address address)
Fill the Address 2 field with the given address.
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition: wifi-phy.cc:1327
WifiTxVector GetDataTxVector(const WifiMacHeader &header)
void Clear(void)
Reset the TX parameters.
std::unique_ptr< WifiProtection > m_protection
protection method
std::unique_ptr< WifiAcknowledgment > m_acknowledgment
acknowledgment method
Time m_txDuration
TX duration of the frame.
WifiTxVector m_txVector
TXVECTOR of the frame being prepared.
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
void SetChannelWidth(uint16_t channelWidth)
Sets the selected channelWidth (in MHz)
void SetGuardInterval(uint16_t guardInterval)
Sets the guard interval duration (in nanoseconds)
WifiMode GetMode(uint16_t staId=SU_STA_ID) const
If this TX vector is associated with an SU PPDU, return the selected payload transmission mode.
void SetHeMuUserInfo(uint16_t staId, HeMuUserInfo userInfo)
Set the HE MU user-specific transmission information for the given STA-ID.
WifiPreamble GetPreambleType(void) const
void SetRu(HeRu::RuSpec ru, uint16_t staId)
Set the RU specification for the STA-ID.
uint8_t GetNss(uint16_t staId=SU_STA_ID) const
If this TX vector is associated with an SU PPDU, return the number of spatial streams.
const HeMuUserInfoMap & GetHeMuUserInfoMap(void) const
Get a const reference to the map HE MU user-specific transmission information indexed by STA-ID.
void SetBssColor(uint8_t color)
Set the BSS color.
uint16_t GetChannelWidth(void) const
void SetPreambleType(WifiPreamble preamble)
Sets the preamble type.
make Callback use a separate empty type
Definition: empty.h:34
#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
#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
Ptr< const AttributeChecker > MakeBooleanChecker(void)
Definition: boolean.cc:121
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition: boolean.h:85
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition: nstime.h:1309
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition: uinteger.h:45
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition: abort.h:108
int64x64_t Max(const int64x64_t &a, const int64x64_t &b)
Maximum.
Definition: int64x64.h:230
int64x64_t Min(const int64x64_t &a, const int64x64_t &b)
Minimum.
Definition: int64x64.h:218
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:205
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:273
#define NS_LOG_FUNCTION_NOARGS()
Output the name of the function.
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:45
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1244
AcIndex QosUtilsMapTidToAc(uint8_t tid)
Maps TID (Traffic ID) to Access classes.
Definition: qos-utils.cc:126
AcIndex
This enumeration defines the Access Categories as an enumeration with values corresponding to the AC ...
Definition: qos-utils.h:71
@ WIFI_PREAMBLE_HE_TB
@ WIFI_PREAMBLE_HE_MU
@ BASIC_TRIGGER
Definition: ctrl-headers.h:562
@ BSRP_TRIGGER
Definition: ctrl-headers.h:566
Declaration of ns3::HePhy class and ns3::HeSigAParameters struct.
address
Definition: first.py:44
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Time GetPpduMaxTime(WifiPreamble preamble)
Get the maximum PPDU duration (see Section 10.14 of 802.11-2016) for the PHY layers defining the aPPD...
Ptr< const AttributeChecker > MakeTimeChecker(const Time min, const Time max)
Helper to make a Time checker with bounded range.
Definition: time.cc:522
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
@ WIFI_MAC_CTL_TRIGGER
Callback< R, Ts... > MakeCallback(R(T::*memPtr)(Ts...), OBJ objPtr)
Build Callbacks for class method members which take varying numbers of arguments and potentially retu...
Definition: callback.h:1648
Information to be provided in case of DL MU transmission.
WifiTxParameters txParams
the transmission parameters
WifiPsduMap psduMap
the DL MU PPDU to transmit
Information to be provided in case of UL MU transmission.
CtrlTriggerHeader trigger
the Trigger Frame used to solicit TB PPDUs
Information used to sort stations.
Mac48Address address
station's MAC Address
double credits
credits accumulated by the station