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