A Discrete-Event Network Simulator
API
rr-multi-user-scheduler.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Universita' degli Studi di Napoli Federico II
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation;
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  *
17  * Author: Stefano Avallone <stavallo@unina.it>
18  */
19 
21 
22 #include "he-configuration.h"
24 #include "he-phy.h"
25 
26 #include "ns3/log.h"
27 #include "ns3/wifi-acknowledgment.h"
28 #include "ns3/wifi-mac-queue.h"
29 #include "ns3/wifi-protection.h"
30 #include "ns3/wifi-psdu.h"
31 
32 #include <algorithm>
33 #include <numeric>
34 
35 namespace ns3
36 {
37 
38 NS_LOG_COMPONENT_DEFINE("RrMultiUserScheduler");
39 
40 NS_OBJECT_ENSURE_REGISTERED(RrMultiUserScheduler);
41 
42 TypeId
44 {
45  static TypeId tid =
46  TypeId("ns3::RrMultiUserScheduler")
48  .SetGroupName("Wifi")
49  .AddConstructor<RrMultiUserScheduler>()
50  .AddAttribute("NStations",
51  "The maximum number of stations that can be granted an RU in a DL MU "
52  "OFDMA transmission",
53  UintegerValue(4),
55  MakeUintegerChecker<uint8_t>(1, 74))
56  .AddAttribute("EnableTxopSharing",
57  "If enabled, allow A-MPDUs of different TIDs in a DL MU PPDU.",
58  BooleanValue(true),
61  .AddAttribute("ForceDlOfdma",
62  "If enabled, return DL_MU_TX even if no DL MU PPDU could be built.",
63  BooleanValue(false),
66  .AddAttribute("EnableUlOfdma",
67  "If enabled, return UL_MU_TX if DL_MU_TX was returned the previous time.",
68  BooleanValue(true),
71  .AddAttribute("EnableBsrp",
72  "If enabled, send a BSRP Trigger Frame before an UL MU transmission.",
73  BooleanValue(true),
76  .AddAttribute(
77  "UlPsduSize",
78  "The default size in bytes of the solicited PSDU (to be sent in a TB PPDU)",
79  UintegerValue(500),
81  MakeUintegerChecker<uint32_t>())
82  .AddAttribute("UseCentral26TonesRus",
83  "If enabled, central 26-tone RUs are allocated, too, when the "
84  "selected RU type is at least 52 tones.",
85  BooleanValue(false),
88  .AddAttribute(
89  "MaxCredits",
90  "Maximum amount of credits a station can have. When transmitting a DL MU PPDU, "
91  "the amount of credits received by each station equals the TX duration (in "
92  "microseconds) divided by the total number of stations. Stations that are the "
93  "recipient of the DL MU PPDU have to pay a number of credits equal to the TX "
94  "duration (in microseconds) times the allocated bandwidth share",
95  TimeValue(Seconds(1)),
97  MakeTimeChecker());
98  return tid;
99 }
100 
102 {
103  NS_LOG_FUNCTION(this);
104 }
105 
107 {
109 }
110 
111 void
113 {
114  NS_LOG_FUNCTION(this);
117  "AssociatedSta",
120  "DeAssociatedSta",
122  for (const auto& ac : wifiAcList)
123  {
124  m_staListDl.insert({ac.first, {}});
125  }
127 }
128 
129 void
131 {
132  NS_LOG_FUNCTION(this);
133  m_staListDl.clear();
134  m_staListUl.clear();
135  m_candidates.clear();
136  m_txParams.Clear();
138  "AssociatedSta",
141  "DeAssociatedSta",
144 }
145 
148 {
149  NS_LOG_FUNCTION(this);
150 
152 
153  if (mpdu && !m_apMac->GetHeSupported(mpdu->GetHeader().GetAddr1()))
154  {
155  return SU_TX;
156  }
157 
159  {
160  TxFormat txFormat = TrySendingBsrpTf();
161 
162  if (txFormat != DL_MU_TX)
163  {
164  return txFormat;
165  }
166  }
167  else if (m_enableUlOfdma && ((GetLastTxFormat(m_linkId) == DL_MU_TX) ||
169  {
170  TxFormat txFormat = TrySendingBasicTf();
171 
172  if (txFormat != DL_MU_TX)
173  {
174  return txFormat;
175  }
176  }
177 
178  return TrySendingDlMuPpdu();
179 }
180 
181 template <class Func>
184 {
185  NS_LOG_FUNCTION(this);
186 
187  // determine RUs to allocate to stations
188  auto count = std::min<std::size_t>(m_nStations, m_staListUl.size());
189  std::size_t nCentral26TonesRus;
190  HeRu::GetEqualSizedRusForStations(m_allowedWidth, count, nCentral26TonesRus);
191  NS_ASSERT(count >= 1);
192 
194  {
195  nCentral26TonesRus = 0;
196  }
197 
198  Ptr<HeConfiguration> heConfiguration = m_apMac->GetHeConfiguration();
199  NS_ASSERT(heConfiguration);
200 
201  WifiTxVector txVector;
204  txVector.SetGuardInterval(heConfiguration->GetGuardInterval().GetNanoSeconds());
205  txVector.SetBssColor(heConfiguration->GetBssColor());
206 
207  // iterate over the associated stations until an enough number of stations is identified
208  auto staIt = m_staListUl.begin();
209  m_candidates.clear();
210 
211  while (staIt != m_staListUl.end() &&
212  txVector.GetHeMuUserInfoMap().size() <
213  std::min<std::size_t>(m_nStations, count + nCentral26TonesRus))
214  {
215  NS_LOG_DEBUG("Next candidate STA (MAC=" << staIt->address << ", AID=" << staIt->aid << ")");
216 
217  if (!canbeSolicited(*staIt))
218  {
219  NS_LOG_DEBUG("Skipping station based on provided function object");
220  staIt++;
221  continue;
222  }
223 
224  if (txVector.GetPreambleType() == WIFI_PREAMBLE_EHT_TB &&
225  !m_apMac->GetEhtSupported(staIt->address))
226  {
227  NS_LOG_DEBUG(
228  "Skipping non-EHT STA because this Trigger Frame is only soliciting EHT STAs");
229  staIt++;
230  continue;
231  }
232 
233  uint8_t tid = 0;
234  while (tid < 8)
235  {
236  // check that a BA agreement is established with the receiver for the
237  // considered TID, since ack sequences for UL MU require block ack
238  if (m_apMac->GetBaAgreementEstablishedAsRecipient(staIt->address, tid))
239  {
240  break;
241  }
242  ++tid;
243  }
244  if (tid == 8)
245  {
246  NS_LOG_DEBUG("No Block Ack agreement established with " << staIt->address);
247  staIt++;
248  continue;
249  }
250 
251  // if the first candidate STA is an EHT STA, we switch to soliciting EHT TB PPDUs
252  if (txVector.GetHeMuUserInfoMap().empty())
253  {
254  if (m_apMac->GetEhtSupported() && m_apMac->GetEhtSupported(staIt->address))
255  {
257  txVector.SetEhtPpduType(0);
258  }
259  // TODO otherwise, make sure the TX width does not exceed 160 MHz
260  }
261 
262  // prepare the MAC header of a frame that would be sent to the candidate station,
263  // just for the purpose of retrieving the TXVECTOR used to transmit to that station
266  ->GetAffiliatedStaAddress(staIt->address)
267  .value_or(staIt->address));
268  hdr.SetAddr2(m_apMac->GetFrameExchangeManager(m_linkId)->GetAddress());
269  WifiTxVector suTxVector =
271  txVector.SetHeMuUserInfo(staIt->aid,
272  {HeRu::RuSpec(), // assigned later by FinalizeTxVector
273  suTxVector.GetMode().GetMcsValue(),
274  suTxVector.GetNss()});
275  m_candidates.emplace_back(staIt, nullptr);
276 
277  // move to the next station in the list
278  staIt++;
279  }
280 
281  if (txVector.GetHeMuUserInfoMap().empty())
282  {
283  NS_LOG_DEBUG("No suitable station");
284  return txVector;
285  }
286 
287  FinalizeTxVector(txVector);
288  return txVector;
289 }
290 
293 {
294  NS_LOG_FUNCTION(this);
295 
296  if (m_staListUl.empty())
297  {
298  NS_LOG_DEBUG("No HE stations associated: return SU_TX");
299  return TxFormat::SU_TX;
300  }
301 
302  // only consider stations that have setup the current link
303  WifiTxVector txVector = GetTxVectorForUlMu([this](const MasterInfo& info) {
304  const auto& staList = m_apMac->GetStaList(m_linkId);
305  return staList.find(info.aid) != staList.cend();
306  });
307 
308  if (txVector.GetHeMuUserInfoMap().empty())
309  {
310  NS_LOG_DEBUG("No suitable station found");
311  return TxFormat::DL_MU_TX;
312  }
313 
316 
317  auto item = GetTriggerFrame(m_trigger, m_linkId);
318  m_triggerMacHdr = item->GetHeader();
319 
320  m_txParams.Clear();
321  // set the TXVECTOR used to send the Trigger Frame
324 
325  if (!GetHeFem(m_linkId)->TryAddMpdu(item, m_txParams, m_availableTime))
326  {
327  // sending the BSRP Trigger Frame is not possible, hence return NO_TX. In
328  // this way, no transmission will occur now and the next time we will
329  // try again sending a BSRP Trigger Frame.
330  NS_LOG_DEBUG("Remaining TXOP duration is not enough for BSRP TF exchange");
331  return NO_TX;
332  }
333 
334  // Compute the time taken by each station to transmit 8 QoS Null frames
335  Time qosNullTxDuration = Seconds(0);
336  for (const auto& userInfo : m_trigger)
337  {
339  txVector,
341  userInfo.GetAid12());
342  qosNullTxDuration = Max(qosNullTxDuration, duration);
343  }
344 
345  if (m_availableTime != Time::Min())
346  {
347  // TryAddMpdu only considers the time to transmit the Trigger Frame
349  m_txParams.m_protection->protectionTime != Time::Min());
351  m_txParams.m_acknowledgment->acknowledgmentTime.IsZero());
353 
354  if (m_txParams.m_protection->protectionTime + m_txParams.m_txDuration // BSRP TF tx time
355  + m_apMac->GetWifiPhy(m_linkId)->GetSifs() + qosNullTxDuration >
357  {
358  NS_LOG_DEBUG("Remaining TXOP duration is not enough for BSRP TF exchange");
359  return NO_TX;
360  }
361  }
362 
363  uint16_t ulLength;
364  std::tie(ulLength, qosNullTxDuration) = HePhy::ConvertHeTbPpduDurationToLSigLength(
365  qosNullTxDuration,
366  m_trigger.GetHeTbTxVector(m_trigger.begin()->GetAid12()),
368  NS_LOG_DEBUG("Duration of QoS Null frames: " << qosNullTxDuration.As(Time::MS));
369  m_trigger.SetUlLength(ulLength);
370 
371  return UL_MU_TX;
372 }
373 
376 {
377  NS_LOG_FUNCTION(this);
378 
379  if (m_staListUl.empty())
380  {
381  NS_LOG_DEBUG("No HE stations associated: return SU_TX");
382  return TxFormat::SU_TX;
383  }
384 
385  // check if an UL OFDMA transmission is possible after a DL OFDMA transmission
386  NS_ABORT_MSG_IF(m_ulPsduSize == 0, "The UlPsduSize attribute must be set to a non-null value");
387 
388  // only consider stations that have setup the current link and do not have
389  // reported a null queue size
390  WifiTxVector txVector = GetTxVectorForUlMu([this](const MasterInfo& info) {
391  const auto& staList = m_apMac->GetStaList(m_linkId);
392  return staList.find(info.aid) != staList.cend() &&
394  });
395 
396  if (txVector.GetHeMuUserInfoMap().empty())
397  {
398  NS_LOG_DEBUG("No suitable station found");
399  return TxFormat::DL_MU_TX;
400  }
401 
402  uint32_t maxBufferSize = 0;
403 
404  for (const auto& candidate : txVector.GetHeMuUserInfoMap())
405  {
406  auto address = m_apMac->GetMldOrLinkAddressByAid(candidate.first);
407  NS_ASSERT_MSG(address, "AID " << candidate.first << " not found");
408 
409  uint8_t queueSize = m_apMac->GetMaxBufferStatus(*address);
410  if (queueSize == 255)
411  {
412  NS_LOG_DEBUG("Buffer status of station " << *address << " is unknown");
413  maxBufferSize = std::max(maxBufferSize, m_ulPsduSize);
414  }
415  else if (queueSize == 254)
416  {
417  NS_LOG_DEBUG("Buffer status of station " << *address << " is not limited");
418  maxBufferSize = 0xffffffff;
419  }
420  else
421  {
422  NS_LOG_DEBUG("Buffer status of station " << *address << " is " << +queueSize);
423  maxBufferSize = std::max(maxBufferSize, static_cast<uint32_t>(queueSize * 256));
424  }
425  }
426 
427  if (maxBufferSize == 0)
428  {
429  return DL_MU_TX;
430  }
431 
434 
435  auto item = GetTriggerFrame(m_trigger, m_linkId);
436  m_triggerMacHdr = item->GetHeader();
437 
438  // compute the maximum amount of time that can be granted to stations.
439  // This value is limited by the max PPDU duration
440  Time maxDuration = GetPpduMaxTime(txVector.GetPreambleType());
441 
442  m_txParams.Clear();
443  // set the TXVECTOR used to send the Trigger Frame
446 
447  if (!GetHeFem(m_linkId)->TryAddMpdu(item, m_txParams, m_availableTime))
448  {
449  // an UL OFDMA transmission is not possible, hence return NO_TX. In
450  // this way, no transmission will occur now and the next time we will
451  // try again performing an UL OFDMA transmission.
452  NS_LOG_DEBUG("Remaining TXOP duration is not enough for UL MU exchange");
453  return NO_TX;
454  }
455 
456  if (m_availableTime != Time::Min())
457  {
458  // TryAddMpdu only considers the time to transmit the Trigger Frame
460  m_txParams.m_protection->protectionTime != Time::Min());
462  m_txParams.m_acknowledgment->acknowledgmentTime != Time::Min());
464 
465  maxDuration = Min(maxDuration,
466  m_availableTime - m_txParams.m_protection->protectionTime -
468  m_txParams.m_acknowledgment->acknowledgmentTime);
469  if (maxDuration.IsNegative())
470  {
471  NS_LOG_DEBUG("Remaining TXOP duration is not enough for UL MU exchange");
472  return NO_TX;
473  }
474  }
475 
476  // Compute the time taken by each station to transmit a frame of maxBufferSize size
477  Time bufferTxTime = Seconds(0);
478  for (const auto& userInfo : m_trigger)
479  {
480  Time duration = WifiPhy::CalculateTxDuration(maxBufferSize,
481  txVector,
483  userInfo.GetAid12());
484  bufferTxTime = Max(bufferTxTime, duration);
485  }
486 
487  if (bufferTxTime < maxDuration)
488  {
489  // the maximum buffer size can be transmitted within the allowed time
490  maxDuration = bufferTxTime;
491  }
492  else
493  {
494  // maxDuration may be a too short time. If it does not allow any station to
495  // transmit at least m_ulPsduSize bytes, give up the UL MU transmission for now
496  Time minDuration = Seconds(0);
497  for (const auto& userInfo : m_trigger)
498  {
499  Time duration =
501  txVector,
503  userInfo.GetAid12());
504  minDuration = (minDuration.IsZero() ? duration : Min(minDuration, duration));
505  }
506 
507  if (maxDuration < minDuration)
508  {
509  // maxDuration is a too short time, hence return NO_TX. In this way,
510  // no transmission will occur now and the next time we will try again
511  // performing an UL OFDMA transmission.
512  NS_LOG_DEBUG("Available time " << maxDuration.As(Time::MS) << " is too short");
513  return NO_TX;
514  }
515  }
516 
517  // maxDuration is the time to grant to the stations. Finalize the Trigger Frame
518  uint16_t ulLength;
519  std::tie(ulLength, maxDuration) =
521  txVector,
523  NS_LOG_DEBUG("TB PPDU duration: " << maxDuration.As(Time::MS));
524  m_trigger.SetUlLength(ulLength);
525  // set Preferred AC to the AC that gained channel access
526  for (auto& userInfo : m_trigger)
527  {
528  userInfo.SetBasicTriggerDepUserInfo(0, 0, m_edca->GetAccessCategory());
529  }
530 
531  UpdateCredits(m_staListUl, maxDuration, txVector);
532 
533  return UL_MU_TX;
534 }
535 
536 void
538 {
539  NS_LOG_FUNCTION(this << aid << address);
540 
542  {
543  return;
544  }
545 
546  auto mldOrLinkAddress = m_apMac->GetMldOrLinkAddressByAid(aid);
547  NS_ASSERT_MSG(mldOrLinkAddress, "AID " << aid << " not found");
548 
549  for (auto& staList : m_staListDl)
550  {
551  // if this is not the first STA of a non-AP MLD to be notified, an entry
552  // for this non-AP MLD already exists
553  const auto staIt = std::find_if(staList.second.cbegin(),
554  staList.second.cend(),
555  [aid](auto&& info) { return info.aid == aid; });
556  if (staIt == staList.second.cend())
557  {
558  staList.second.push_back(MasterInfo{aid, *mldOrLinkAddress, 0.0});
559  }
560  }
561 
562  const auto staIt = std::find_if(m_staListUl.cbegin(), m_staListUl.cend(), [aid](auto&& info) {
563  return info.aid == aid;
564  });
565  if (staIt == m_staListUl.cend())
566  {
567  m_staListUl.push_back(MasterInfo{aid, *mldOrLinkAddress, 0.0});
568  }
569 }
570 
571 void
573 {
574  NS_LOG_FUNCTION(this << aid << address);
575 
577  {
578  return;
579  }
580 
581  auto mldOrLinkAddress = m_apMac->GetMldOrLinkAddressByAid(aid);
582  NS_ASSERT_MSG(mldOrLinkAddress, "AID " << aid << " not found");
583 
584  if (m_apMac->IsAssociated(*mldOrLinkAddress))
585  {
586  // Another STA of the non-AP MLD is still associated
587  return;
588  }
589 
590  for (auto& staList : m_staListDl)
591  {
592  staList.second.remove_if([&aid](const MasterInfo& info) { return info.aid == aid; });
593  }
594  m_staListUl.remove_if([&aid](const MasterInfo& info) { return info.aid == aid; });
595 }
596 
599 {
600  NS_LOG_FUNCTION(this);
601 
602  AcIndex primaryAc = m_edca->GetAccessCategory();
603 
604  if (m_staListDl[primaryAc].empty())
605  {
606  NS_LOG_DEBUG("No HE stations associated: return SU_TX");
607  return TxFormat::SU_TX;
608  }
609 
610  std::size_t count =
611  std::min(static_cast<std::size_t>(m_nStations), m_staListDl[primaryAc].size());
612  std::size_t nCentral26TonesRus;
613  HeRu::RuType ruType =
614  HeRu::GetEqualSizedRusForStations(m_allowedWidth, count, nCentral26TonesRus);
615  NS_ASSERT(count >= 1);
616 
618  {
619  nCentral26TonesRus = 0;
620  }
621 
622  uint8_t currTid = wifiAcList.at(primaryAc).GetHighTid();
623 
625 
626  if (mpdu && mpdu->GetHeader().IsQosData())
627  {
628  currTid = mpdu->GetHeader().GetQosTid();
629  }
630 
631  // determine the list of TIDs to check
632  std::vector<uint8_t> tids;
633 
635  {
636  for (auto acIt = wifiAcList.find(primaryAc); acIt != wifiAcList.end(); acIt++)
637  {
638  uint8_t firstTid = (acIt->first == primaryAc ? currTid : acIt->second.GetHighTid());
639  tids.push_back(firstTid);
640  tids.push_back(acIt->second.GetOtherTid(firstTid));
641  }
642  }
643  else
644  {
645  tids.push_back(currTid);
646  }
647 
648  Ptr<HeConfiguration> heConfiguration = m_apMac->GetHeConfiguration();
649  NS_ASSERT(heConfiguration);
650 
651  m_txParams.Clear();
654  m_txParams.m_txVector.SetGuardInterval(heConfiguration->GetGuardInterval().GetNanoSeconds());
655  m_txParams.m_txVector.SetBssColor(heConfiguration->GetBssColor());
656 
657  // The TXOP limit can be exceeded by the TXOP holder if it does not transmit more
658  // than one Data or Management frame in the TXOP and the frame is not in an A-MPDU
659  // consisting of more than one MPDU (Sec. 10.22.2.8 of 802.11-2016).
660  // For the moment, we are considering just one MPDU per receiver.
661  Time actualAvailableTime = (m_initialFrame ? Time::Min() : m_availableTime);
662 
663  // iterate over the associated stations until an enough number of stations is identified
664  auto staIt = m_staListDl[primaryAc].begin();
665  m_candidates.clear();
666 
667  std::vector<uint8_t> ruAllocations;
668  auto numRuAllocs = m_txParams.m_txVector.GetChannelWidth() / 20;
669  ruAllocations.resize(numRuAllocs);
670  NS_ASSERT((m_candidates.size() % numRuAllocs) == 0);
671 
672  while (staIt != m_staListDl[primaryAc].end() &&
673  m_candidates.size() <
674  std::min(static_cast<std::size_t>(m_nStations), count + nCentral26TonesRus))
675  {
676  NS_LOG_DEBUG("Next candidate STA (MAC=" << staIt->address << ", AID=" << staIt->aid << ")");
677 
679  !m_apMac->GetEhtSupported(staIt->address))
680  {
681  NS_LOG_DEBUG("Skipping non-EHT STA because this DL MU PPDU is sent to EHT STAs only");
682  staIt++;
683  continue;
684  }
685 
686  HeRu::RuType currRuType = (m_candidates.size() < count ? ruType : HeRu::RU_26_TONE);
687 
688  // check if the AP has at least one frame to be sent to the current station
689  for (uint8_t tid : tids)
690  {
691  AcIndex ac = QosUtilsMapTidToAc(tid);
692  NS_ASSERT(ac >= primaryAc);
693  // check that a BA agreement is established with the receiver for the
694  // considered TID, since ack sequences for DL MU PPDUs require block ack
695  if (m_apMac->GetBaAgreementEstablishedAsOriginator(staIt->address, tid))
696  {
697  mpdu = m_apMac->GetQosTxop(ac)->PeekNextMpdu(m_linkId, tid, staIt->address);
698 
699  // we only check if the first frame of the current TID meets the size
700  // and duration constraints. We do not explore the queues further.
701  if (mpdu)
702  {
703  mpdu = GetHeFem(m_linkId)->CreateAliasIfNeeded(mpdu);
704  // Use a temporary TX vector including only the STA-ID of the
705  // candidate station to check if the MPDU meets the size and time limits.
706  // An RU of the computed size is tentatively assigned to the candidate
707  // station, so that the TX duration can be correctly computed.
708  WifiTxVector suTxVector =
711  WifiTxVector txVectorCopy = m_txParams.m_txVector;
712 
714  {{currRuType, 1, true},
715  suTxVector.GetMode().GetMcsValue(),
716  suTxVector.GetNss()});
717 
718  if (!GetHeFem(m_linkId)->TryAddMpdu(mpdu, m_txParams, actualAvailableTime))
719  {
720  NS_LOG_DEBUG("Adding the peeked frame violates the time constraints");
721  m_txParams.m_txVector = txVectorCopy;
722  }
723  else
724  {
725  // the frame meets the constraints
726  NS_LOG_DEBUG("Adding candidate STA (MAC=" << staIt->address
727  << ", AID=" << staIt->aid
728  << ") TID=" << +tid);
729  m_candidates.emplace_back(staIt, mpdu);
730  break; // terminate the for loop
731  }
732  }
733  else
734  {
735  NS_LOG_DEBUG("No frames to send to " << staIt->address << " with TID=" << +tid);
736  }
737  }
738  }
739 
740  // the first candidate STA determines the preamble type for the DL MU PPDU
741  if (m_candidates.size() == 1)
742  {
743  if (m_apMac->GetEhtSupported() && m_apMac->GetEhtSupported(staIt->address))
744  {
746  m_txParams.m_txVector.SetEhtPpduType(0); // indicates DL OFDMA transmission
747  }
748  // TODO otherwise, make sure the TX width does not exceed 160 MHz
749  }
750 
751  // move to the next station in the list
752  staIt++;
753  }
754 
755  if (m_candidates.empty())
756  {
757  if (m_forceDlOfdma)
758  {
759  NS_LOG_DEBUG("The AP does not have suitable frames to transmit: return NO_TX");
760  return NO_TX;
761  }
762  NS_LOG_DEBUG("The AP does not have suitable frames to transmit: return SU_TX");
763  return SU_TX;
764  }
765 
766  return TxFormat::DL_MU_TX;
767 }
768 
769 void
770 RrMultiUserScheduler::FinalizeTxVector(WifiTxVector& txVector)
771 {
772  // Do not log txVector because GetTxVectorForUlMu() left RUs undefined and
773  // printing them will crash the simulation
774  NS_LOG_FUNCTION(this);
775  NS_ASSERT(txVector.GetHeMuUserInfoMap().size() == m_candidates.size());
776 
777  // compute how many stations can be granted an RU and the RU size
778  std::size_t nRusAssigned = m_candidates.size();
779  std::size_t nCentral26TonesRus;
780  HeRu::RuType ruType =
781  HeRu::GetEqualSizedRusForStations(m_allowedWidth, nRusAssigned, nCentral26TonesRus);
782 
783  NS_LOG_DEBUG(nRusAssigned << " stations are being assigned a " << ruType << " RU");
784 
785  if (!m_useCentral26TonesRus || m_candidates.size() == nRusAssigned)
786  {
787  nCentral26TonesRus = 0;
788  }
789  else
790  {
791  nCentral26TonesRus = std::min(m_candidates.size() - nRusAssigned, nCentral26TonesRus);
792  NS_LOG_DEBUG(nCentral26TonesRus << " stations are being assigned a 26-tones RU");
793  }
794 
795  // re-allocate RUs based on the actual number of candidate stations
796  WifiTxVector::HeMuUserInfoMap heMuUserInfoMap;
797  std::swap(heMuUserInfoMap, txVector.GetHeMuUserInfoMap());
798 
799  auto candidateIt = m_candidates.begin(); // iterator over the list of candidate receivers
800  auto ruSet = HeRu::GetRusOfType(m_allowedWidth, ruType);
801  auto ruSetIt = ruSet.begin();
802  auto central26TonesRus = HeRu::GetCentral26TonesRus(m_allowedWidth, ruType);
803  auto central26TonesRusIt = central26TonesRus.begin();
804 
805  for (std::size_t i = 0; i < nRusAssigned + nCentral26TonesRus; i++)
806  {
807  NS_ASSERT(candidateIt != m_candidates.end());
808  auto mapIt = heMuUserInfoMap.find(candidateIt->first->aid);
809  NS_ASSERT(mapIt != heMuUserInfoMap.end());
810 
811  txVector.SetHeMuUserInfo(mapIt->first,
812  {(i < nRusAssigned ? *ruSetIt++ : *central26TonesRusIt++),
813  mapIt->second.mcs,
814  mapIt->second.nss});
815  candidateIt++;
816  }
817 
818  // remove candidates that will not be served
819  m_candidates.erase(candidateIt, m_candidates.end());
820 }
821 
822 void
823 RrMultiUserScheduler::UpdateCredits(std::list<MasterInfo>& staList,
824  Time txDuration,
825  const WifiTxVector& txVector)
826 {
827  NS_LOG_FUNCTION(this << txDuration.As(Time::US) << txVector);
828 
829  // find how many RUs have been allocated for each RU type
830  std::map<HeRu::RuType, std::size_t> ruMap;
831  for (const auto& userInfo : txVector.GetHeMuUserInfoMap())
832  {
833  ruMap.insert({userInfo.second.ru.GetRuType(), 0}).first->second++;
834  }
835 
836  // The amount of credits received by each station equals the TX duration (in
837  // microseconds) divided by the number of stations.
838  double creditsPerSta = txDuration.ToDouble(Time::US) / staList.size();
839  // Transmitting stations have to pay a number of credits equal to the TX duration
840  // (in microseconds) times the allocated bandwidth share.
841  double debitsPerMhz =
842  txDuration.ToDouble(Time::US) /
843  std::accumulate(ruMap.begin(), ruMap.end(), 0, [](uint16_t sum, auto pair) {
844  return sum + pair.second * HeRu::GetBandwidth(pair.first);
845  });
846 
847  // assign credits to all stations
848  for (auto& sta : staList)
849  {
850  sta.credits += creditsPerSta;
851  sta.credits = std::min(sta.credits, m_maxCredits.ToDouble(Time::US));
852  }
853 
854  // subtract debits to the selected stations
855  for (auto& candidate : m_candidates)
856  {
857  auto mapIt = txVector.GetHeMuUserInfoMap().find(candidate.first->aid);
858  NS_ASSERT(mapIt != txVector.GetHeMuUserInfoMap().end());
859 
860  candidate.first->credits -= debitsPerMhz * HeRu::GetBandwidth(mapIt->second.ru.GetRuType());
861  }
862 
863  // sort the list in decreasing order of credits
864  staList.sort([](const MasterInfo& a, const MasterInfo& b) { return a.credits > b.credits; });
865 }
866 
868 RrMultiUserScheduler::ComputeDlMuInfo()
869 {
870  NS_LOG_FUNCTION(this);
871 
872  if (m_candidates.empty())
873  {
874  return DlMuInfo();
875  }
876 
877  DlMuInfo dlMuInfo;
878  std::swap(dlMuInfo.txParams.m_txVector, m_txParams.m_txVector);
879  FinalizeTxVector(dlMuInfo.txParams.m_txVector);
880 
881  m_txParams.Clear();
882  Ptr<WifiMpdu> mpdu;
883 
884  // Compute the TX params (again) by using the stored MPDUs and the final TXVECTOR
885  Time actualAvailableTime = (m_initialFrame ? Time::Min() : m_availableTime);
886 
887  for (const auto& candidate : m_candidates)
888  {
889  mpdu = candidate.second;
890  NS_ASSERT(mpdu);
891 
892  bool ret [[maybe_unused]] =
893  GetHeFem(m_linkId)->TryAddMpdu(mpdu, dlMuInfo.txParams, actualAvailableTime);
894  NS_ASSERT_MSG(ret,
895  "Weird that an MPDU does not meet constraints when "
896  "transmitted over a larger RU");
897  }
898 
899  // We have to complete the PSDUs to send
900  Ptr<WifiMacQueue> queue;
901 
902  for (const auto& candidate : m_candidates)
903  {
904  // Let us try first A-MSDU aggregation if possible
905  mpdu = candidate.second;
906  NS_ASSERT(mpdu);
907  uint8_t tid = mpdu->GetHeader().GetQosTid();
908  NS_ASSERT_MSG(mpdu->GetOriginal()->GetHeader().GetAddr1() == candidate.first->address,
909  "RA of the stored MPDU must match the stored address");
910 
911  NS_ASSERT(mpdu->IsQueued());
912  Ptr<WifiMpdu> item = mpdu;
913 
914  if (!mpdu->GetHeader().IsRetry())
915  {
916  // this MPDU must have been dequeued from the AC queue and we can try
917  // A-MSDU aggregation
918  item = GetHeFem(m_linkId)->GetMsduAggregator()->GetNextAmsdu(mpdu,
919  dlMuInfo.txParams,
920  m_availableTime);
921 
922  if (!item)
923  {
924  // A-MSDU aggregation failed or disabled
925  item = mpdu;
926  }
927  m_apMac->GetQosTxop(QosUtilsMapTidToAc(tid))->AssignSequenceNumber(item);
928  }
929 
930  // Now, let's try A-MPDU aggregation if possible
931  std::vector<Ptr<WifiMpdu>> mpduList =
932  GetHeFem(m_linkId)->GetMpduAggregator()->GetNextAmpdu(item,
933  dlMuInfo.txParams,
934  m_availableTime);
935 
936  if (mpduList.size() > 1)
937  {
938  // A-MPDU aggregation succeeded, update psduMap
939  dlMuInfo.psduMap[candidate.first->aid] = Create<WifiPsdu>(std::move(mpduList));
940  }
941  else
942  {
943  dlMuInfo.psduMap[candidate.first->aid] = Create<WifiPsdu>(item, true);
944  }
945  }
946 
947  AcIndex primaryAc = m_edca->GetAccessCategory();
948  UpdateCredits(m_staListDl[primaryAc],
949  dlMuInfo.txParams.m_txDuration,
950  dlMuInfo.txParams.m_txVector);
951 
952  NS_LOG_DEBUG("Next station to serve has AID=" << m_staListDl[primaryAc].front().aid);
953 
954  return dlMuInfo;
955 }
956 
958 RrMultiUserScheduler::ComputeUlMuInfo()
959 {
960  return UlMuInfo{m_trigger, m_triggerMacHdr, std::move(m_txParams)};
961 }
962 
963 } // namespace ns3
#define min(a, b)
Definition: 80211b.c:42
#define max(a, b)
Definition: 80211b.c:43
#define Min(a, b)
const std::map< uint16_t, Mac48Address > & GetStaList(uint8_t linkId) const
Get a const reference to the map of associated stations on the given link.
std::optional< Mac48Address > GetMldOrLinkAddressByAid(uint16_t aid) const
uint8_t GetMaxBufferStatus(Mac48Address address) const
Return the maximum among the values of the Queue Size subfield of the last QoS Data or QoS Null frame...
std::optional< uint8_t > IsAssociated(const Mac48Address &address) const
Get the ID of a link (if any) that has been setup with the station having the given MAC address.
AttributeValue implementation for Boolean.
Definition: boolean.h:37
Headers for Trigger frames.
Definition: ctrl-headers.h:945
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 GetGuardInterval() const
Get the guard interval duration (in nanoseconds) of the solicited HE TB PPDU.
TriggerFrameType GetType() const
Get the Trigger Frame type.
ConstIterator begin() const
Get a const iterator pointing to the first User Info field in the list.
void SetUlLength(uint16_t len)
Set the UL Length subfield of the Common Info field.
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:256
RuType
The different HE Resource Unit (RU) types.
Definition: he-ru.h:41
@ RU_26_TONE
Definition: he-ru.h:42
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:794
an EUI-48 address
Definition: mac48-address.h:46
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() override
Initialize() implementation.
TxFormat GetLastTxFormat(uint8_t linkId)
Get the format of the last transmission on the given link, as determined by the last call to NotifyAc...
Ptr< ApWifiMac > m_apMac
the AP wifi MAC
uint16_t m_allowedWidth
the allowed width in MHz for the current transmission
Time m_availableTime
the time available for frame exchange
Ptr< WifiRemoteStationManager > GetWifiRemoteStationManager(uint8_t linkId) const
Get the station manager attached to the AP on the given link.
uint8_t m_linkId
the ID of the link over which channel access has been granted
uint32_t GetMaxSizeOfQosNullAmpdu(const CtrlTriggerHeader &trigger) const
Get the maximum size in bytes among the A-MPDUs containing QoS Null frames and solicited by the given...
Ptr< QosTxop > m_edca
the AC that gained channel access
void DoDispose() override
Destructor implementation.
Ptr< WifiMpdu > GetTriggerFrame(const CtrlTriggerHeader &trigger, uint8_t linkId) const
Get an MPDU containing the given Trigger Frame.
Ptr< HeFrameExchangeManager > GetHeFem(uint8_t linkId) const
Get the HE Frame Exchange Manager attached to the AP on the given link.
TxFormat
Enumeration of the possible transmission formats.
bool TraceConnectWithoutContext(std::string name, const CallbackBase &cb)
Connect a TraceSource to a Callback without a context.
Definition: object-base.cc:311
bool TraceDisconnectWithoutContext(std::string name, const CallbackBase &cb)
Disconnect from a TraceSource a Callback previously connected without a context.
Definition: object-base.cc:339
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:78
Ptr< WifiMpdu > PeekNextMpdu(uint8_t linkId, uint8_t tid=8, Mac48Address recipient=Mac48Address::GetBroadcast(), Ptr< const WifiMpdu > mpdu=nullptr)
Peek the next frame to transmit on the given link to the given receiver and of the given TID from the...
Definition: qos-txop.cc:359
AcIndex GetAccessCategory() const
Get the access category of this object.
Definition: qos-txop.cc:766
RrMultiUserScheduler is a simple OFDMA scheduler that indicates to perform a DL OFDMA transmission if...
TxFormat SelectTxFormat() override
Select the format of the next transmission.
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.
static TypeId GetTypeId()
Get the type ID.
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
void DoInitialize() override
Initialize() implementation.
void DoDispose() override
Destructor implementation.
void UpdateCredits(std::list< MasterInfo > &staList, Time txDuration, const WifiTxVector &txVector)
Update credits of the stations in the given list considering that a PPDU having the given duration is...
WifiMacHeader m_triggerMacHdr
MAC header for Trigger Frame.
uint8_t m_nStations
Number of stations/slots to fill.
WifiTxParameters m_txParams
TX parameters.
virtual TxFormat TrySendingDlMuPpdu()
Check if it is possible to send a DL MU PPDU given the current time limits.
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.
bool m_enableTxopSharing
allow A-MPDUs of different TIDs in a DL MU PPDU
CtrlTriggerHeader m_trigger
Trigger Frame to send.
std::map< AcIndex, std::list< MasterInfo > > m_staListDl
Per-AC list of stations (next to serve for DL first)
virtual TxFormat TrySendingBsrpTf()
Check if it is possible to send a BSRP Trigger Frame given the current time limits.
WifiTxVector GetTxVectorForUlMu(Func canBeSolicited)
Compute a TXVECTOR that can be used to construct a Trigger Frame to solicit transmissions from suitab...
virtual TxFormat TrySendingBasicTf()
Check if it is possible to send a Basic Trigger Frame given the current time limits.
void FinalizeTxVector(WifiTxVector &txVector)
Finalize the given TXVECTOR by only including the largest subset of the current set of candidate stat...
std::list< MasterInfo > m_staListUl
List of stations to serve for UL.
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:105
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition: time.cc:417
bool IsNegative() const
Exactly equivalent to t <= 0.
Definition: nstime.h:323
static Time Min()
Minimum representable Time Not to be confused with Min(Time,Time).
Definition: nstime.h:286
@ MS
millisecond
Definition: nstime.h:117
double ToDouble(Unit unit) const
Get the Time value expressed in a particular unit.
Definition: nstime.h:572
bool IsZero() const
Exactly equivalent to t == 0.
Definition: nstime.h:314
AttributeValue implementation for Time.
Definition: nstime.h:1423
a unique identifier for an interface.
Definition: type-id.h:60
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:935
Hold an unsigned integer type.
Definition: uinteger.h:45
Implements the IEEE 802.11 MAC header.
Mac48Address GetAddr1() const
Return the address in the Address 1 field.
void SetAddr1(Mac48Address address)
Fill the Address 1 field with the given address.
void SetAddr2(Mac48Address address)
Fill the Address 2 field with the given address.
Ptr< FrameExchangeManager > GetFrameExchangeManager(uint8_t linkId=SINGLE_LINK_OP_ID) const
Get the Frame Exchange Manager associated with the given link.
Definition: wifi-mac.cc:840
Ptr< HeConfiguration > GetHeConfiguration() const
Definition: wifi-mac.cc:1353
Ptr< WifiPhy > GetWifiPhy(uint8_t linkId=SINGLE_LINK_OP_ID) const
Definition: wifi-mac.cc:954
bool GetEhtSupported() const
Return whether the device supports EHT.
Definition: wifi-mac.cc:1384
bool GetHeSupported() const
Return whether the device supports HE.
Definition: wifi-mac.cc:1378
RecipientAgreementOptConstRef GetBaAgreementEstablishedAsRecipient(Mac48Address originator, uint8_t tid) const
Definition: wifi-mac.cc:1297
Ptr< WifiRemoteStationManager > GetWifiRemoteStationManager(uint8_t linkId=0) const
Definition: wifi-mac.cc:886
OriginatorAgreementOptConstRef GetBaAgreementEstablishedAsOriginator(Mac48Address recipient, uint8_t tid) const
Definition: wifi-mac.cc:1283
Ptr< QosTxop > GetQosTxop(AcIndex ac) const
Accessor for a specified EDCA object.
Definition: wifi-mac.cc:490
uint8_t GetMcsValue() const
Definition: wifi-mode.cc:163
Time GetSifs() const
Return the Short Interframe Space (SIFS) for this PHY.
Definition: wifi-phy.cc:774
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition: wifi-phy.cc:1480
WifiPhyBand GetPhyBand() const
Get the configured Wi-Fi band.
Definition: wifi-phy.cc:996
WifiTxVector GetDataTxVector(const WifiMacHeader &header, uint16_t allowedWidth)
WifiTxVector GetRtsTxVector(Mac48Address address)
std::unique_ptr< WifiProtection > m_protection
protection method
std::unique_ptr< WifiAcknowledgment > m_acknowledgment
acknowledgment method
Time m_txDuration
TX duration of the frame.
WifiTxVector m_txVector
TXVECTOR of the frame being prepared.
void Clear()
Reset the TX parameters.
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
void SetEhtPpduType(uint8_t type)
Set the EHT_PPDU_TYPE parameter.
void SetChannelWidth(uint16_t channelWidth)
Sets the selected channelWidth (in MHz)
std::map< uint16_t, HeMuUserInfo > HeMuUserInfoMap
map of HE MU specific user info parameters indexed by STA-ID
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() const
const HeMuUserInfoMap & GetHeMuUserInfoMap() const
Get a const reference to the map HE MU user-specific transmission information indexed by 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.
void SetBssColor(uint8_t color)
Set the BSS color.
uint16_t GetChannelWidth() const
void SetPreambleType(WifiPreamble preamble)
Sets the preamble type.
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition: assert.h:66
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition: assert.h:86
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:86
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition: boolean.cc:124
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:1424
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:46
#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:243
int64x64_t Min(const int64x64_t &a, const int64x64_t &b)
Minimum.
Definition: int64x64.h:229
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:268
#define NS_LOG_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:46
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1336
AcIndex QosUtilsMapTidToAc(uint8_t tid)
Maps TID (Traffic ID) to Access classes.
Definition: qos-utils.cc:134
AcIndex
This enumeration defines the Access Categories as an enumeration with values corresponding to the AC ...
Definition: qos-utils.h:72
@ WIFI_PREAMBLE_EHT_TB
@ WIFI_PREAMBLE_HE_TB
@ WIFI_PREAMBLE_EHT_MU
@ WIFI_PREAMBLE_HE_MU
Declaration of ns3::HePhy class and ns3::HeSigAParameters struct.
Definition: first.py:1
address
Definition: first.py:40
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...
Callback< R, Args... > MakeCallback(R(T::*memPtr)(Args...), OBJ objPtr)
Build Callbacks for class method members which take varying numbers of arguments and potentially retu...
Definition: callback.h:707
Ptr< const AttributeChecker > MakeTimeChecker(const Time min, const Time max)
Helper to make a Time checker with bounded range.
Definition: time.cc:535
const std::map< AcIndex, WifiAc > wifiAcList
Map containing the four ACs in increasing order of priority (according to Table 10-1 "UP-to-AC Mappin...
Definition: qos-utils.cc:126
@ WIFI_MAC_QOSDATA
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.
Information used to sort stations.
Mac48Address address
station's MAC Address
double credits
credits accumulated by the station