A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
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 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Author: Stefano Avallone <stavallo@unina.it>
7 */
8
10
11#include "he-configuration.h"
13#include "he-phy.h"
14
15#include "ns3/log.h"
16#include "ns3/wifi-acknowledgment.h"
17#include "ns3/wifi-mac-queue.h"
18#include "ns3/wifi-protection.h"
19#include "ns3/wifi-psdu.h"
20
21#include <algorithm>
22#include <numeric>
23
24namespace ns3
25{
26
27NS_LOG_COMPONENT_DEFINE("RrMultiUserScheduler");
28
29NS_OBJECT_ENSURE_REGISTERED(RrMultiUserScheduler);
30
31TypeId
33{
34 static TypeId tid =
35 TypeId("ns3::RrMultiUserScheduler")
37 .SetGroupName("Wifi")
38 .AddConstructor<RrMultiUserScheduler>()
39 .AddAttribute("NStations",
40 "The maximum number of stations that can be granted an RU in a DL MU "
41 "OFDMA transmission",
45 .AddAttribute("EnableTxopSharing",
46 "If enabled, allow A-MPDUs of different TIDs in a DL MU PPDU.",
47 BooleanValue(true),
50 .AddAttribute("ForceDlOfdma",
51 "If enabled, return DL_MU_TX even if no DL MU PPDU could be built.",
52 BooleanValue(false),
55 .AddAttribute("EnableUlOfdma",
56 "If enabled, return UL_MU_TX if DL_MU_TX was returned the previous time.",
57 BooleanValue(true),
60 .AddAttribute("EnableBsrp",
61 "If enabled, send a BSRP Trigger Frame before an UL MU transmission.",
62 BooleanValue(true),
65 .AddAttribute(
66 "UlPsduSize",
67 "The default size in bytes of the solicited PSDU (to be sent in a TB PPDU)",
68 UintegerValue(500),
71 .AddAttribute("UseCentral26TonesRus",
72 "If enabled, central 26-tone RUs are allocated, too, when the "
73 "selected RU type is at least 52 tones.",
74 BooleanValue(false),
77 .AddAttribute(
78 "MaxCredits",
79 "Maximum amount of credits a station can have. When transmitting a DL MU PPDU, "
80 "the amount of credits received by each station equals the TX duration (in "
81 "microseconds) divided by the total number of stations. Stations that are the "
82 "recipient of the DL MU PPDU have to pay a number of credits equal to the TX "
83 "duration (in microseconds) times the allocated bandwidth share",
87 return tid;
88}
89
94
99
100void
102{
103 NS_LOG_FUNCTION(this);
105 m_apMac->TraceConnectWithoutContext(
106 "AssociatedSta",
108 m_apMac->TraceConnectWithoutContext(
109 "DeAssociatedSta",
111 for (const auto& ac : wifiAcList)
112 {
113 m_staListDl.insert({ac.first, {}});
114 }
116}
117
118void
120{
121 NS_LOG_FUNCTION(this);
122 m_staListDl.clear();
123 m_staListUl.clear();
124 m_candidates.clear();
126 m_apMac->TraceDisconnectWithoutContext(
127 "AssociatedSta",
129 m_apMac->TraceDisconnectWithoutContext(
130 "DeAssociatedSta",
133}
134
137{
138 NS_LOG_FUNCTION(this);
139
141
142 if (mpdu && !m_apMac->GetHeSupported(mpdu->GetHeader().GetAddr1()))
143 {
144 return SU_TX;
145 }
146
150 {
151 TxFormat txFormat = TrySendingBsrpTf();
152
153 if (txFormat != DL_MU_TX)
154 {
155 return txFormat;
156 }
157 }
160 {
161 TxFormat txFormat = TrySendingBasicTf();
162
163 if (txFormat != DL_MU_TX)
164 {
165 return txFormat;
166 }
167 }
168
169 return TrySendingDlMuPpdu();
170}
171
173RrMultiUserScheduler::GetTxVectorForUlMu(std::function<bool(const MasterInfo&)> canBeSolicited)
174{
175 NS_LOG_FUNCTION(this);
176
177 // determine RUs to allocate to stations
178 auto count = std::min<std::size_t>(m_nStations, m_staListUl.size());
179 std::size_t nCentral26TonesRus;
180 HeRu::GetEqualSizedRusForStations(m_allowedWidth, count, nCentral26TonesRus);
181 NS_ASSERT(count >= 1);
182
184 {
185 nCentral26TonesRus = 0;
186 }
187
188 Ptr<HeConfiguration> heConfiguration = m_apMac->GetHeConfiguration();
189 NS_ASSERT(heConfiguration);
190
191 WifiTxVector txVector;
194 txVector.SetGuardInterval(heConfiguration->GetGuardInterval());
195 txVector.SetBssColor(heConfiguration->GetBssColor());
196
197 // iterate over the associated stations until an enough number of stations is identified
198 auto staIt = m_staListUl.begin();
199 m_candidates.clear();
200
201 while (staIt != m_staListUl.end() &&
202 txVector.GetHeMuUserInfoMap().size() <
203 std::min<std::size_t>(m_nStations, count + nCentral26TonesRus))
204 {
205 NS_LOG_DEBUG("Next candidate STA (MAC=" << staIt->address << ", AID=" << staIt->aid << ")");
206
207 if (!canBeSolicited(*staIt))
208 {
209 NS_LOG_DEBUG("Skipping station based on provided function object");
210 staIt++;
211 continue;
212 }
213
214 if (txVector.GetPreambleType() == WIFI_PREAMBLE_EHT_TB &&
215 !m_apMac->GetEhtSupported(staIt->address))
216 {
218 "Skipping non-EHT STA because this Trigger Frame is only soliciting EHT STAs");
219 staIt++;
220 continue;
221 }
222
223 uint8_t tid = 0;
224 while (tid < 8)
225 {
226 // check that a BA agreement is established with the receiver for the
227 // considered TID, since ack sequences for UL MU require block ack
228 if (m_apMac->GetBaAgreementEstablishedAsRecipient(staIt->address, tid))
229 {
230 break;
231 }
232 ++tid;
233 }
234 if (tid == 8)
235 {
236 NS_LOG_DEBUG("No Block Ack agreement established with " << staIt->address);
237 staIt++;
238 continue;
239 }
240
241 // if the first candidate STA is an EHT STA, we switch to soliciting EHT TB PPDUs
242 if (txVector.GetHeMuUserInfoMap().empty())
243 {
244 if (m_apMac->GetEhtSupported() && m_apMac->GetEhtSupported(staIt->address))
245 {
247 txVector.SetEhtPpduType(0);
248 }
249 // TODO otherwise, make sure the TX width does not exceed 160 MHz
250 }
251
252 // prepare the MAC header of a frame that would be sent to the candidate station,
253 // just for the purpose of retrieving the TXVECTOR used to transmit to that station
256 ->GetAffiliatedStaAddress(staIt->address)
257 .value_or(staIt->address));
258 hdr.SetAddr2(m_apMac->GetFrameExchangeManager(m_linkId)->GetAddress());
259 WifiTxVector suTxVector =
261 txVector.SetHeMuUserInfo(staIt->aid,
262 {HeRu::RuSpec(), // assigned later by FinalizeTxVector
263 suTxVector.GetMode().GetMcsValue(),
264 suTxVector.GetNss()});
265 m_candidates.emplace_back(staIt, nullptr);
266
267 // move to the next station in the list
268 staIt++;
269 }
270
271 if (txVector.GetHeMuUserInfoMap().empty())
272 {
273 NS_LOG_DEBUG("No suitable station");
274 return txVector;
275 }
276
277 FinalizeTxVector(txVector);
278 return txVector;
279}
280
281bool
283{
284 // check if station has setup the current link
285 if (!m_apMac->GetStaList(m_linkId).contains(info.aid))
286 {
287 NS_LOG_INFO("STA with AID " << info.aid << " has not setup link " << +m_linkId);
288 return false;
289 }
290
291 auto mldAddr = GetWifiRemoteStationManager(m_linkId)->GetMldAddress(info.address);
292
293 // remaining checks are for MLDs only
294 if (!mldAddr)
295 {
296 return true;
297 }
298
299 // check if at least one TID is mapped on the current link in the UL direction
300 bool mapped = false;
301 for (uint8_t tid = 0; tid < 8; ++tid)
302 {
303 if (m_apMac->TidMappedOnLink(*mldAddr, WifiDirection::UPLINK, tid, m_linkId))
304 {
305 mapped = true;
306 break;
307 }
308 }
309
310 if (!mapped)
311 {
312 NS_LOG_DEBUG("MLD " << *mldAddr << " has not mapped any TID on link " << +m_linkId);
313 return false;
314 }
315
316 // check if the station is an EMLSR client that is using another link
317 if (GetWifiRemoteStationManager(m_linkId)->GetEmlsrEnabled(info.address) &&
318 (m_apMac->GetTxBlockedOnLink(AC_BE,
319 {WIFI_QOSDATA_QUEUE, WIFI_UNICAST, *mldAddr, 0},
320 m_linkId,
322 m_apMac->GetTxBlockedOnLink(AC_BE,
323 {WIFI_QOSDATA_QUEUE, WIFI_UNICAST, *mldAddr, 0},
324 m_linkId,
326 {
327 NS_LOG_INFO("EMLSR client " << *mldAddr << " is using another link");
328 return false;
329 }
330
331 return true;
332}
333
336{
337 NS_LOG_FUNCTION(this);
338
339 if (m_staListUl.empty())
340 {
341 NS_LOG_DEBUG("No HE stations associated: return SU_TX");
342 return TxFormat::SU_TX;
343 }
344
345 auto txVector = GetTxVectorForUlMu(
346 std::bind(&RrMultiUserScheduler::CanSolicitStaInBsrpTf, this, std::placeholders::_1));
347
348 if (txVector.GetHeMuUserInfoMap().empty())
349 {
350 NS_LOG_DEBUG("No suitable station found");
351 return TxFormat::DL_MU_TX;
352 }
353
355 txVector.SetGuardInterval(m_trigger.GetGuardInterval());
356
357 auto item = GetTriggerFrame(m_trigger, m_linkId);
358 m_triggerMacHdr = item->GetHeader();
359
361 // set the TXVECTOR used to send the Trigger Frame
363 m_apMac->GetWifiRemoteStationManager(m_linkId)->GetRtsTxVector(m_triggerMacHdr.GetAddr1(),
365
366 if (!GetHeFem(m_linkId)->TryAddMpdu(item, m_txParams, m_availableTime))
367 {
368 // sending the BSRP Trigger Frame is not possible, hence return NO_TX. In
369 // this way, no transmission will occur now and the next time we will
370 // try again sending a BSRP Trigger Frame.
371 NS_LOG_DEBUG("Remaining TXOP duration is not enough for BSRP TF exchange");
372 return NO_TX;
373 }
374
375 // Compute the time taken by each station to transmit 8 QoS Null frames
376 Time qosNullTxDuration = Seconds(0);
377 for (const auto& userInfo : m_trigger)
378 {
380 txVector,
381 m_apMac->GetWifiPhy(m_linkId)->GetPhyBand(),
382 userInfo.GetAid12());
383 qosNullTxDuration = Max(qosNullTxDuration, duration);
384 }
385
386 if (m_availableTime != Time::Min())
387 {
388 // TryAddMpdu only considers the time to transmit the Trigger Frame
389 NS_ASSERT(m_txParams.m_protection && m_txParams.m_protection->protectionTime.has_value());
391 m_txParams.m_acknowledgment->acknowledgmentTime.has_value() &&
392 m_txParams.m_acknowledgment->acknowledgmentTime->IsZero());
393 NS_ASSERT(m_txParams.m_txDuration.has_value());
394
395 if (*m_txParams.m_protection->protectionTime + *m_txParams.m_txDuration // BSRP TF tx time
396 + m_apMac->GetWifiPhy(m_linkId)->GetSifs() + qosNullTxDuration >
398 {
399 NS_LOG_DEBUG("Remaining TXOP duration is not enough for BSRP TF exchange");
400 return NO_TX;
401 }
402 }
403
404 uint16_t ulLength;
405 std::tie(ulLength, qosNullTxDuration) = HePhy::ConvertHeTbPpduDurationToLSigLength(
406 qosNullTxDuration,
408 m_apMac->GetWifiPhy(m_linkId)->GetPhyBand());
409 NS_LOG_DEBUG("Duration of QoS Null frames: " << qosNullTxDuration.As(Time::MS));
410 m_trigger.SetUlLength(ulLength);
411
412 return UL_MU_TX;
413}
414
415bool
417{
418 // in addition to the checks performed when sending a BSRP TF, also check if the station
419 // has reported a null queue size
420 if (!CanSolicitStaInBsrpTf(info))
421 {
422 return false;
423 }
424
425 return m_apMac->GetMaxBufferStatus(info.address) > 0;
426}
427
430{
431 NS_LOG_FUNCTION(this);
432
433 if (m_staListUl.empty())
434 {
435 NS_LOG_DEBUG("No HE stations associated: return SU_TX");
436 return TxFormat::SU_TX;
437 }
438
439 // check if an UL OFDMA transmission is possible after a DL OFDMA transmission
440 NS_ABORT_MSG_IF(m_ulPsduSize == 0, "The UlPsduSize attribute must be set to a non-null value");
441
442 auto txVector = GetTxVectorForUlMu(
443 std::bind(&RrMultiUserScheduler::CanSolicitStaInBasicTf, this, std::placeholders::_1));
444
445 if (txVector.GetHeMuUserInfoMap().empty())
446 {
447 NS_LOG_DEBUG("No suitable station found");
448 return TxFormat::DL_MU_TX;
449 }
450
451 uint32_t maxBufferSize = 0;
452
453 for (const auto& candidate : txVector.GetHeMuUserInfoMap())
454 {
455 auto address = m_apMac->GetMldOrLinkAddressByAid(candidate.first);
456 NS_ASSERT_MSG(address, "AID " << candidate.first << " not found");
457
458 uint8_t queueSize = m_apMac->GetMaxBufferStatus(*address);
459 if (queueSize == 255)
460 {
461 NS_LOG_DEBUG("Buffer status of station " << *address << " is unknown");
462 maxBufferSize = std::max(maxBufferSize, m_ulPsduSize);
463 }
464 else if (queueSize == 254)
465 {
466 NS_LOG_DEBUG("Buffer status of station " << *address << " is not limited");
467 maxBufferSize = 0xffffffff;
468 }
469 else
470 {
471 NS_LOG_DEBUG("Buffer status of station " << *address << " is " << +queueSize);
472 maxBufferSize = std::max(maxBufferSize, static_cast<uint32_t>(queueSize * 256));
473 }
474 }
475
476 if (maxBufferSize == 0)
477 {
478 return DL_MU_TX;
479 }
480
482 txVector.SetGuardInterval(m_trigger.GetGuardInterval());
483
484 auto item = GetTriggerFrame(m_trigger, m_linkId);
485 m_triggerMacHdr = item->GetHeader();
486
487 // compute the maximum amount of time that can be granted to stations.
488 // This value is limited by the max PPDU duration
489 Time maxDuration = GetPpduMaxTime(txVector.GetPreambleType());
490
492 // set the TXVECTOR used to send the Trigger Frame
494 m_apMac->GetWifiRemoteStationManager(m_linkId)->GetRtsTxVector(m_triggerMacHdr.GetAddr1(),
496
497 if (!GetHeFem(m_linkId)->TryAddMpdu(item, m_txParams, m_availableTime))
498 {
499 // an UL OFDMA transmission is not possible, hence return NO_TX. In
500 // this way, no transmission will occur now and the next time we will
501 // try again performing an UL OFDMA transmission.
502 NS_LOG_DEBUG("Remaining TXOP duration is not enough for UL MU exchange");
503 return NO_TX;
504 }
505
506 if (m_availableTime != Time::Min())
507 {
508 // TryAddMpdu only considers the time to transmit the Trigger Frame
509 NS_ASSERT(m_txParams.m_protection && m_txParams.m_protection->protectionTime.has_value());
511 m_txParams.m_acknowledgment->acknowledgmentTime.has_value());
512 NS_ASSERT(m_txParams.m_txDuration.has_value());
513
514 maxDuration = Min(maxDuration,
515 m_availableTime - *m_txParams.m_protection->protectionTime -
516 *m_txParams.m_txDuration - m_apMac->GetWifiPhy(m_linkId)->GetSifs() -
517 *m_txParams.m_acknowledgment->acknowledgmentTime);
518 if (maxDuration.IsNegative())
519 {
520 NS_LOG_DEBUG("Remaining TXOP duration is not enough for UL MU exchange");
521 return NO_TX;
522 }
523 }
524
525 // Compute the time taken by each station to transmit a frame of maxBufferSize size
526 Time bufferTxTime = Seconds(0);
527 for (const auto& userInfo : m_trigger)
528 {
529 Time duration = WifiPhy::CalculateTxDuration(maxBufferSize,
530 txVector,
531 m_apMac->GetWifiPhy(m_linkId)->GetPhyBand(),
532 userInfo.GetAid12());
533 bufferTxTime = Max(bufferTxTime, duration);
534 }
535
536 if (bufferTxTime < maxDuration)
537 {
538 // the maximum buffer size can be transmitted within the allowed time
539 maxDuration = bufferTxTime;
540 }
541 else
542 {
543 // maxDuration may be a too short time. If it does not allow any station to
544 // transmit at least m_ulPsduSize bytes, give up the UL MU transmission for now
545 Time minDuration = Seconds(0);
546 for (const auto& userInfo : m_trigger)
547 {
548 Time duration =
550 txVector,
551 m_apMac->GetWifiPhy(m_linkId)->GetPhyBand(),
552 userInfo.GetAid12());
553 minDuration = (minDuration.IsZero() ? duration : Min(minDuration, duration));
554 }
555
556 if (maxDuration < minDuration)
557 {
558 // maxDuration is a too short time, hence return NO_TX. In this way,
559 // no transmission will occur now and the next time we will try again
560 // performing an UL OFDMA transmission.
561 NS_LOG_DEBUG("Available time " << maxDuration.As(Time::MS) << " is too short");
562 return NO_TX;
563 }
564 }
565
566 // maxDuration is the time to grant to the stations. Finalize the Trigger Frame
567 uint16_t ulLength;
568 std::tie(ulLength, maxDuration) =
570 txVector,
571 m_apMac->GetWifiPhy(m_linkId)->GetPhyBand());
572 NS_LOG_DEBUG("TB PPDU duration: " << maxDuration.As(Time::MS));
573 m_trigger.SetUlLength(ulLength);
574 // set Preferred AC to the AC that gained channel access
575 for (auto& userInfo : m_trigger)
576 {
577 userInfo.SetBasicTriggerDepUserInfo(0, 0, m_edca->GetAccessCategory());
578 }
579
580 UpdateCredits(m_staListUl, maxDuration, txVector);
581
582 return UL_MU_TX;
583}
584
585void
587{
588 NS_LOG_FUNCTION(this << aid << address);
589
590 if (!m_apMac->GetHeSupported(address))
591 {
592 return;
593 }
594
595 auto mldOrLinkAddress = m_apMac->GetMldOrLinkAddressByAid(aid);
596 NS_ASSERT_MSG(mldOrLinkAddress, "AID " << aid << " not found");
597
598 for (auto& staList : m_staListDl)
599 {
600 // if this is not the first STA of a non-AP MLD to be notified, an entry
601 // for this non-AP MLD already exists
602 const auto staIt = std::find_if(staList.second.cbegin(),
603 staList.second.cend(),
604 [aid](auto&& info) { return info.aid == aid; });
605 if (staIt == staList.second.cend())
606 {
607 staList.second.push_back(MasterInfo{aid, *mldOrLinkAddress, 0.0});
608 }
609 }
610
611 const auto staIt = std::find_if(m_staListUl.cbegin(), m_staListUl.cend(), [aid](auto&& info) {
612 return info.aid == aid;
613 });
614 if (staIt == m_staListUl.cend())
615 {
616 m_staListUl.push_back(MasterInfo{aid, *mldOrLinkAddress, 0.0});
617 }
618}
619
620void
622{
623 NS_LOG_FUNCTION(this << aid << address);
624
625 if (!m_apMac->GetHeSupported(address))
626 {
627 return;
628 }
629
630 auto mldOrLinkAddress = m_apMac->GetMldOrLinkAddressByAid(aid);
631 NS_ASSERT_MSG(mldOrLinkAddress, "AID " << aid << " not found");
632
633 if (m_apMac->IsAssociated(*mldOrLinkAddress))
634 {
635 // Another STA of the non-AP MLD is still associated
636 return;
637 }
638
639 for (auto& staList : m_staListDl)
640 {
641 staList.second.remove_if([&aid](const MasterInfo& info) { return info.aid == aid; });
642 }
643 m_staListUl.remove_if([&aid](const MasterInfo& info) { return info.aid == aid; });
644}
645
648{
649 NS_LOG_FUNCTION(this);
650
651 AcIndex primaryAc = m_edca->GetAccessCategory();
652
653 if (m_staListDl[primaryAc].empty())
654 {
655 NS_LOG_DEBUG("No HE stations associated: return SU_TX");
656 return TxFormat::SU_TX;
657 }
658
659 std::size_t count =
660 std::min(static_cast<std::size_t>(m_nStations), m_staListDl[primaryAc].size());
661 std::size_t nCentral26TonesRus;
662 HeRu::RuType ruType =
663 HeRu::GetEqualSizedRusForStations(m_allowedWidth, count, nCentral26TonesRus);
664 NS_ASSERT(count >= 1);
665
667 {
668 nCentral26TonesRus = 0;
669 }
670
671 uint8_t currTid = wifiAcList.at(primaryAc).GetHighTid();
672
674
675 if (mpdu && mpdu->GetHeader().IsQosData())
676 {
677 currTid = mpdu->GetHeader().GetQosTid();
678 }
679
680 // determine the list of TIDs to check
681 std::vector<uint8_t> tids;
682
684 {
685 for (auto acIt = wifiAcList.find(primaryAc); acIt != wifiAcList.end(); acIt++)
686 {
687 uint8_t firstTid = (acIt->first == primaryAc ? currTid : acIt->second.GetHighTid());
688 tids.push_back(firstTid);
689 tids.push_back(acIt->second.GetOtherTid(firstTid));
690 }
691 }
692 else
693 {
694 tids.push_back(currTid);
695 }
696
697 Ptr<HeConfiguration> heConfiguration = m_apMac->GetHeConfiguration();
698 NS_ASSERT(heConfiguration);
699
703 m_txParams.m_txVector.SetGuardInterval(heConfiguration->GetGuardInterval());
704 m_txParams.m_txVector.SetBssColor(heConfiguration->GetBssColor());
705
706 // The TXOP limit can be exceeded by the TXOP holder if it does not transmit more
707 // than one Data or Management frame in the TXOP and the frame is not in an A-MPDU
708 // consisting of more than one MPDU (Sec. 10.22.2.8 of 802.11-2016).
709 // For the moment, we are considering just one MPDU per receiver.
710 Time actualAvailableTime = (m_initialFrame ? Time::Min() : m_availableTime);
711
712 // iterate over the associated stations until an enough number of stations is identified
713 auto staIt = m_staListDl[primaryAc].begin();
714 m_candidates.clear();
715
716 std::vector<uint8_t> ruAllocations;
717 const std::size_t numRuAllocs = m_txParams.m_txVector.GetChannelWidth() / 20;
718 ruAllocations.resize(numRuAllocs);
719 NS_ASSERT((m_candidates.size() % numRuAllocs) == 0);
720
721 while (staIt != m_staListDl[primaryAc].end() &&
722 m_candidates.size() <
723 std::min(static_cast<std::size_t>(m_nStations), count + nCentral26TonesRus))
724 {
725 NS_LOG_DEBUG("Next candidate STA (MAC=" << staIt->address << ", AID=" << staIt->aid << ")");
726
728 !m_apMac->GetEhtSupported(staIt->address))
729 {
730 NS_LOG_DEBUG("Skipping non-EHT STA because this DL MU PPDU is sent to EHT STAs only");
731 staIt++;
732 continue;
733 }
734
735 HeRu::RuType currRuType = (m_candidates.size() < count ? ruType : HeRu::RU_26_TONE);
736
737 // check if the AP has at least one frame to be sent to the current station
738 for (uint8_t tid : tids)
739 {
740 AcIndex ac = QosUtilsMapTidToAc(tid);
741 NS_ASSERT(ac >= primaryAc);
742 // check that a BA agreement is established with the receiver for the
743 // considered TID, since ack sequences for DL MU PPDUs require block ack
744 if (m_apMac->GetBaAgreementEstablishedAsOriginator(staIt->address, tid))
745 {
746 mpdu = m_apMac->GetQosTxop(ac)->PeekNextMpdu(m_linkId, tid, staIt->address);
747
748 // we only check if the first frame of the current TID meets the size
749 // and duration constraints. We do not explore the queues further.
750 if (mpdu)
751 {
752 mpdu = GetHeFem(m_linkId)->CreateAliasIfNeeded(mpdu);
753 // Use a temporary TX vector including only the STA-ID of the
754 // candidate station to check if the MPDU meets the size and time limits.
755 // An RU of the computed size is tentatively assigned to the candidate
756 // station, so that the TX duration can be correctly computed.
757 WifiTxVector suTxVector =
758 GetWifiRemoteStationManager(m_linkId)->GetDataTxVector(mpdu->GetHeader(),
760
761 WifiTxVector txVectorCopy = m_txParams.m_txVector;
762
763 // the first candidate STA determines the preamble type for the DL MU PPDU
764 if (m_candidates.empty() &&
766 {
768 m_txParams.m_txVector.SetEhtPpduType(0); // indicates DL OFDMA transmission
769 }
770
772 {{currRuType, 1, true},
773 suTxVector.GetMode().GetMcsValue(),
774 suTxVector.GetNss()});
775
776 if (!GetHeFem(m_linkId)->TryAddMpdu(mpdu, m_txParams, actualAvailableTime))
777 {
778 NS_LOG_DEBUG("Adding the peeked frame violates the time constraints");
779 m_txParams.m_txVector = txVectorCopy;
780 }
781 else
782 {
783 // the frame meets the constraints
784 NS_LOG_DEBUG("Adding candidate STA (MAC=" << staIt->address
785 << ", AID=" << staIt->aid
786 << ") TID=" << +tid);
787 m_candidates.emplace_back(staIt, mpdu);
788 break; // terminate the for loop
789 }
790 }
791 else
792 {
793 NS_LOG_DEBUG("No frames to send to " << staIt->address << " with TID=" << +tid);
794 }
795 }
796 }
797
798 // move to the next station in the list
799 staIt++;
800 }
801
802 if (m_candidates.empty())
803 {
804 if (m_forceDlOfdma)
805 {
806 NS_LOG_DEBUG("The AP does not have suitable frames to transmit: return NO_TX");
807 return NO_TX;
808 }
809 NS_LOG_DEBUG("The AP does not have suitable frames to transmit: return SU_TX");
810 return SU_TX;
811 }
812
813 return TxFormat::DL_MU_TX;
814}
815
816void
817RrMultiUserScheduler::FinalizeTxVector(WifiTxVector& txVector)
818{
819 // Do not log txVector because GetTxVectorForUlMu() left RUs undefined and
820 // printing them will crash the simulation
821 NS_LOG_FUNCTION(this);
822 NS_ASSERT(txVector.GetHeMuUserInfoMap().size() == m_candidates.size());
823
824 // compute how many stations can be granted an RU and the RU size
825 std::size_t nRusAssigned = m_candidates.size();
826 std::size_t nCentral26TonesRus;
827 HeRu::RuType ruType =
828 HeRu::GetEqualSizedRusForStations(m_allowedWidth, nRusAssigned, nCentral26TonesRus);
829
830 NS_LOG_DEBUG(nRusAssigned << " stations are being assigned a " << ruType << " RU");
831
832 if (!m_useCentral26TonesRus || m_candidates.size() == nRusAssigned)
833 {
834 nCentral26TonesRus = 0;
835 }
836 else
837 {
838 nCentral26TonesRus = std::min(m_candidates.size() - nRusAssigned, nCentral26TonesRus);
839 NS_LOG_DEBUG(nCentral26TonesRus << " stations are being assigned a 26-tones RU");
840 }
841
842 // re-allocate RUs based on the actual number of candidate stations
843 WifiTxVector::HeMuUserInfoMap heMuUserInfoMap;
844 std::swap(heMuUserInfoMap, txVector.GetHeMuUserInfoMap());
845
846 auto candidateIt = m_candidates.begin(); // iterator over the list of candidate receivers
847 auto ruSet = HeRu::GetRusOfType(m_allowedWidth, ruType);
848 auto ruSetIt = ruSet.begin();
849 auto central26TonesRus = HeRu::GetCentral26TonesRus(m_allowedWidth, ruType);
850 auto central26TonesRusIt = central26TonesRus.begin();
851
852 for (std::size_t i = 0; i < nRusAssigned + nCentral26TonesRus; i++)
853 {
854 NS_ASSERT(candidateIt != m_candidates.end());
855 auto mapIt = heMuUserInfoMap.find(candidateIt->first->aid);
856 NS_ASSERT(mapIt != heMuUserInfoMap.end());
857
858 txVector.SetHeMuUserInfo(mapIt->first,
859 {(i < nRusAssigned ? *ruSetIt++ : *central26TonesRusIt++),
860 mapIt->second.mcs,
861 mapIt->second.nss});
862 candidateIt++;
863 }
864
865 // remove candidates that will not be served
866 m_candidates.erase(candidateIt, m_candidates.end());
867}
868
869void
870RrMultiUserScheduler::UpdateCredits(std::list<MasterInfo>& staList,
871 Time txDuration,
872 const WifiTxVector& txVector)
873{
874 NS_LOG_FUNCTION(this << txDuration.As(Time::US) << txVector);
875
876 // find how many RUs have been allocated for each RU type
877 std::map<HeRu::RuType, std::size_t> ruMap;
878 for (const auto& userInfo : txVector.GetHeMuUserInfoMap())
879 {
880 ruMap.insert({userInfo.second.ru.GetRuType(), 0}).first->second++;
881 }
882
883 // The amount of credits received by each station equals the TX duration (in
884 // microseconds) divided by the number of stations.
885 double creditsPerSta = txDuration.ToDouble(Time::US) / staList.size();
886 // Transmitting stations have to pay a number of credits equal to the TX duration
887 // (in microseconds) times the allocated bandwidth share.
888 double debitsPerMhz =
889 txDuration.ToDouble(Time::US) /
890 std::accumulate(ruMap.begin(), ruMap.end(), 0, [](uint16_t sum, auto pair) {
891 return sum + pair.second * HeRu::GetBandwidth(pair.first);
892 });
893
894 // assign credits to all stations
895 for (auto& sta : staList)
896 {
897 sta.credits += creditsPerSta;
898 sta.credits = std::min(sta.credits, m_maxCredits.ToDouble(Time::US));
899 }
900
901 // subtract debits to the selected stations
902 for (auto& candidate : m_candidates)
903 {
904 auto mapIt = txVector.GetHeMuUserInfoMap().find(candidate.first->aid);
905 NS_ASSERT(mapIt != txVector.GetHeMuUserInfoMap().end());
906
907 candidate.first->credits -= debitsPerMhz * HeRu::GetBandwidth(mapIt->second.ru.GetRuType());
908 }
909
910 // sort the list in decreasing order of credits
911 staList.sort([](const MasterInfo& a, const MasterInfo& b) { return a.credits > b.credits; });
912}
913
915RrMultiUserScheduler::ComputeDlMuInfo()
916{
917 NS_LOG_FUNCTION(this);
918
919 if (m_candidates.empty())
920 {
921 return DlMuInfo();
922 }
923
924 DlMuInfo dlMuInfo;
925 std::swap(dlMuInfo.txParams.m_txVector, m_txParams.m_txVector);
926 FinalizeTxVector(dlMuInfo.txParams.m_txVector);
927
928 m_txParams.Clear();
929 Ptr<WifiMpdu> mpdu;
930
931 // Compute the TX params (again) by using the stored MPDUs and the final TXVECTOR
932 Time actualAvailableTime = (m_initialFrame ? Time::Min() : m_availableTime);
933
934 for (const auto& candidate : m_candidates)
935 {
936 mpdu = candidate.second;
937 NS_ASSERT(mpdu);
938
939 bool ret [[maybe_unused]] =
940 GetHeFem(m_linkId)->TryAddMpdu(mpdu, dlMuInfo.txParams, actualAvailableTime);
941 NS_ASSERT_MSG(ret,
942 "Weird that an MPDU does not meet constraints when "
943 "transmitted over a larger RU");
944 }
945
946 // We have to complete the PSDUs to send
947 Ptr<WifiMacQueue> queue;
948
949 for (const auto& candidate : m_candidates)
950 {
951 // Let us try first A-MSDU aggregation if possible
952 mpdu = candidate.second;
953 NS_ASSERT(mpdu);
954 uint8_t tid = mpdu->GetHeader().GetQosTid();
955 NS_ASSERT_MSG(mpdu->GetOriginal()->GetHeader().GetAddr1() == candidate.first->address,
956 "RA of the stored MPDU must match the stored address");
957
958 NS_ASSERT(mpdu->IsQueued());
959 Ptr<WifiMpdu> item = mpdu;
960
961 if (!mpdu->GetHeader().IsRetry())
962 {
963 // this MPDU must have been dequeued from the AC queue and we can try
964 // A-MSDU aggregation
965 item = GetHeFem(m_linkId)->GetMsduAggregator()->GetNextAmsdu(mpdu,
966 dlMuInfo.txParams,
967 m_availableTime);
968
969 if (!item)
970 {
971 // A-MSDU aggregation failed or disabled
972 item = mpdu;
973 }
974 m_apMac->GetQosTxop(QosUtilsMapTidToAc(tid))->AssignSequenceNumber(item);
975 }
976
977 // Now, let's try A-MPDU aggregation if possible
978 std::vector<Ptr<WifiMpdu>> mpduList =
979 GetHeFem(m_linkId)->GetMpduAggregator()->GetNextAmpdu(item,
980 dlMuInfo.txParams,
981 m_availableTime);
982
983 if (mpduList.size() > 1)
984 {
985 // A-MPDU aggregation succeeded, update psduMap
986 dlMuInfo.psduMap[candidate.first->aid] = Create<WifiPsdu>(std::move(mpduList));
987 }
988 else
989 {
990 dlMuInfo.psduMap[candidate.first->aid] = Create<WifiPsdu>(item, true);
991 }
992 }
993
994 NS_ASSERT(dlMuInfo.txParams.m_txDuration.has_value());
995 AcIndex primaryAc = m_edca->GetAccessCategory();
996 UpdateCredits(m_staListDl[primaryAc],
997 *dlMuInfo.txParams.m_txDuration,
998 dlMuInfo.txParams.m_txVector);
999
1000 NS_LOG_DEBUG("Next station to serve has AID=" << m_staListDl[primaryAc].front().aid);
1001
1002 return dlMuInfo;
1003}
1004
1006RrMultiUserScheduler::ComputeUlMuInfo()
1007{
1008 return UlMuInfo{m_trigger, m_triggerMacHdr, std::move(m_txParams)};
1009}
1010
1011} // namespace ns3
#define Max(a, b)
#define Min(a, b)
Headers for Trigger frames.
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...
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.
Time GetGuardInterval() const
Get the guard interval duration of the solicited HE TB PPDU.
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:261
static RuType GetEqualSizedRusForStations(MHz_u 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:806
RuType
The different HE Resource Unit (RU) types.
Definition he-ru.h:32
@ RU_26_TONE
Definition he-ru.h:33
an EUI-48 address
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
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.
MHz_u m_allowedWidth
the allowed width for the current transmission
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.
Smart pointer class similar to boost::intrusive_ptr.
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:374
AcIndex GetAccessCategory() const
Get the access category of this object.
Definition qos-txop.cc:796
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.
virtual WifiTxVector GetTxVectorForUlMu(std::function< bool(const MasterInfo &)> canBeSolicited)
Compute a TXVECTOR that can be used to construct a Trigger Frame to solicit transmissions from suitab...
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 bool CanSolicitStaInBasicTf(const MasterInfo &info) const
Determine whether the given STA can be solicited via a Basic Trigger Frame.
virtual TxFormat TrySendingBsrpTf()
Check if it is possible to send a BSRP Trigger Frame given the current time limits.
virtual bool CanSolicitStaInBsrpTf(const MasterInfo &info) const
Determine whether the given STA can be solicited via a BSRP Trigger Frame.
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:94
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition time.cc:404
bool IsNegative() const
Exactly equivalent to t <= 0.
Definition nstime.h:313
static Time Min()
Minimum representable Time Not to be confused with Min(Time,Time).
Definition nstime.h:276
@ MS
millisecond
Definition nstime.h:106
double ToDouble(Unit unit) const
Get the Time value expressed in a particular unit.
Definition nstime.h:562
bool IsZero() const
Exactly equivalent to t == 0.
Definition nstime.h:304
a unique identifier for an interface.
Definition type-id.h:48
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition type-id.cc:1001
Hold an unsigned integer type.
Definition uinteger.h:34
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.
uint8_t GetMcsValue() const
Definition wifi-mode.cc:152
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition wifi-phy.cc:1572
std::optional< Time > m_txDuration
TX duration of the frame.
std::unique_ptr< WifiProtection > m_protection
protection method
std::unique_ptr< WifiAcknowledgment > m_acknowledgment
acknowledgment method
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 SetGuardInterval(Time guardInterval)
Sets the guard interval duration (in nanoseconds)
std::map< uint16_t, HeMuUserInfo > HeMuUserInfoMap
map of HE MU specific user info parameters indexed by STA-ID
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
void SetChannelWidth(MHz_u channelWidth)
Sets the selected channelWidth.
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.
MHz_u GetChannelWidth() const
void SetBssColor(uint8_t color)
Set the BSS color.
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:55
#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:75
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition abort.h:97
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition log.h:257
#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_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition log.h:264
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition object-base.h:35
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1308
AcIndex QosUtilsMapTidToAc(uint8_t tid)
Maps TID (Traffic ID) to Access classes.
Definition qos-utils.cc:123
AcIndex
This enumeration defines the Access Categories as an enumeration with values corresponding to the AC ...
Definition qos-utils.h:62
@ WIFI_PREAMBLE_EHT_TB
@ WIFI_PREAMBLE_HE_TB
@ WIFI_PREAMBLE_EHT_MU
@ WIFI_PREAMBLE_HE_MU
@ AC_BE
Best Effort.
Definition qos-utils.h:64
Declaration of ns3::HePhy class and ns3::HeSigAParameters struct.
Definition first.py:1
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 > MakeBooleanChecker()
Definition boolean.cc:113
Ptr< const AttributeChecker > MakeUintegerChecker()
Definition uinteger.h:85
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Definition nstime.h:1396
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:684
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
Definition uinteger.h:35
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:115
@ WIFI_MAC_QOSDATA
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Definition boolean.h:70
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Definition nstime.h:1416
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
std::ofstream queueSize