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 * 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
35namespace ns3
36{
37
38NS_LOG_COMPONENT_DEFINE("RrMultiUserScheduler");
39
40NS_OBJECT_ENSURE_REGISTERED(RrMultiUserScheduler);
41
42TypeId
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",
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",
98 return tid;
99}
100
102{
103 NS_LOG_FUNCTION(this);
104}
105
107{
109}
110
111void
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
129void
131{
132 NS_LOG_FUNCTION(this);
133 m_staListDl.clear();
134 m_staListUl.clear();
135 m_candidates.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 }
169 {
170 TxFormat txFormat = TrySendingBasicTf();
171
172 if (txFormat != DL_MU_TX)
173 {
174 return txFormat;
175 }
176 }
177
178 return TrySendingDlMuPpdu();
179}
180
181template <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
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 {
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));
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.contains(info.aid);
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
321 // set the TXVECTOR used to send the Trigger Frame
325
326 if (!GetHeFem(m_linkId)->TryAddMpdu(item, m_txParams, m_availableTime))
327 {
328 // sending the BSRP Trigger Frame is not possible, hence return NO_TX. In
329 // this way, no transmission will occur now and the next time we will
330 // try again sending a BSRP Trigger Frame.
331 NS_LOG_DEBUG("Remaining TXOP duration is not enough for BSRP TF exchange");
332 return NO_TX;
333 }
334
335 // Compute the time taken by each station to transmit 8 QoS Null frames
336 Time qosNullTxDuration = Seconds(0);
337 for (const auto& userInfo : m_trigger)
338 {
340 txVector,
342 userInfo.GetAid12());
343 qosNullTxDuration = Max(qosNullTxDuration, duration);
344 }
345
346 if (m_availableTime != Time::Min())
347 {
348 // TryAddMpdu only considers the time to transmit the Trigger Frame
350 m_txParams.m_protection->protectionTime != Time::Min());
352 m_txParams.m_acknowledgment->acknowledgmentTime.IsZero());
354
355 if (m_txParams.m_protection->protectionTime + m_txParams.m_txDuration // BSRP TF tx time
356 + m_apMac->GetWifiPhy(m_linkId)->GetSifs() + qosNullTxDuration >
358 {
359 NS_LOG_DEBUG("Remaining TXOP duration is not enough for BSRP TF exchange");
360 return NO_TX;
361 }
362 }
363
364 uint16_t ulLength;
365 std::tie(ulLength, qosNullTxDuration) = HePhy::ConvertHeTbPpduDurationToLSigLength(
366 qosNullTxDuration,
369 NS_LOG_DEBUG("Duration of QoS Null frames: " << qosNullTxDuration.As(Time::MS));
370 m_trigger.SetUlLength(ulLength);
371
372 return UL_MU_TX;
373}
374
377{
378 NS_LOG_FUNCTION(this);
379
380 if (m_staListUl.empty())
381 {
382 NS_LOG_DEBUG("No HE stations associated: return SU_TX");
383 return TxFormat::SU_TX;
384 }
385
386 // check if an UL OFDMA transmission is possible after a DL OFDMA transmission
387 NS_ABORT_MSG_IF(m_ulPsduSize == 0, "The UlPsduSize attribute must be set to a non-null value");
388
389 // only consider stations that have setup the current link and do not have
390 // reported a null queue size
391 WifiTxVector txVector = GetTxVectorForUlMu([this](const MasterInfo& info) {
392 const auto& staList = m_apMac->GetStaList(m_linkId);
393 return staList.contains(info.aid) && m_apMac->GetMaxBufferStatus(info.address) > 0;
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
443 // set the TXVECTOR used to send the Trigger Frame
447
448 if (!GetHeFem(m_linkId)->TryAddMpdu(item, m_txParams, m_availableTime))
449 {
450 // an UL OFDMA transmission is not possible, hence return NO_TX. In
451 // this way, no transmission will occur now and the next time we will
452 // try again performing an UL OFDMA transmission.
453 NS_LOG_DEBUG("Remaining TXOP duration is not enough for UL MU exchange");
454 return NO_TX;
455 }
456
457 if (m_availableTime != Time::Min())
458 {
459 // TryAddMpdu only considers the time to transmit the Trigger Frame
461 m_txParams.m_protection->protectionTime != Time::Min());
463 m_txParams.m_acknowledgment->acknowledgmentTime != Time::Min());
465
466 maxDuration = Min(maxDuration,
467 m_availableTime - m_txParams.m_protection->protectionTime -
469 m_txParams.m_acknowledgment->acknowledgmentTime);
470 if (maxDuration.IsNegative())
471 {
472 NS_LOG_DEBUG("Remaining TXOP duration is not enough for UL MU exchange");
473 return NO_TX;
474 }
475 }
476
477 // Compute the time taken by each station to transmit a frame of maxBufferSize size
478 Time bufferTxTime = Seconds(0);
479 for (const auto& userInfo : m_trigger)
480 {
481 Time duration = WifiPhy::CalculateTxDuration(maxBufferSize,
482 txVector,
484 userInfo.GetAid12());
485 bufferTxTime = Max(bufferTxTime, duration);
486 }
487
488 if (bufferTxTime < maxDuration)
489 {
490 // the maximum buffer size can be transmitted within the allowed time
491 maxDuration = bufferTxTime;
492 }
493 else
494 {
495 // maxDuration may be a too short time. If it does not allow any station to
496 // transmit at least m_ulPsduSize bytes, give up the UL MU transmission for now
497 Time minDuration = Seconds(0);
498 for (const auto& userInfo : m_trigger)
499 {
500 Time duration =
502 txVector,
504 userInfo.GetAid12());
505 minDuration = (minDuration.IsZero() ? duration : Min(minDuration, duration));
506 }
507
508 if (maxDuration < minDuration)
509 {
510 // maxDuration is a too short time, hence return NO_TX. In this way,
511 // no transmission will occur now and the next time we will try again
512 // performing an UL OFDMA transmission.
513 NS_LOG_DEBUG("Available time " << maxDuration.As(Time::MS) << " is too short");
514 return NO_TX;
515 }
516 }
517
518 // maxDuration is the time to grant to the stations. Finalize the Trigger Frame
519 uint16_t ulLength;
520 std::tie(ulLength, maxDuration) =
522 txVector,
524 NS_LOG_DEBUG("TB PPDU duration: " << maxDuration.As(Time::MS));
525 m_trigger.SetUlLength(ulLength);
526 // set Preferred AC to the AC that gained channel access
527 for (auto& userInfo : m_trigger)
528 {
529 userInfo.SetBasicTriggerDepUserInfo(0, 0, m_edca->GetAccessCategory());
530 }
531
532 UpdateCredits(m_staListUl, maxDuration, txVector);
533
534 return UL_MU_TX;
535}
536
537void
539{
540 NS_LOG_FUNCTION(this << aid << address);
541
542 if (!m_apMac->GetHeSupported(address))
543 {
544 return;
545 }
546
547 auto mldOrLinkAddress = m_apMac->GetMldOrLinkAddressByAid(aid);
548 NS_ASSERT_MSG(mldOrLinkAddress, "AID " << aid << " not found");
549
550 for (auto& staList : m_staListDl)
551 {
552 // if this is not the first STA of a non-AP MLD to be notified, an entry
553 // for this non-AP MLD already exists
554 const auto staIt = std::find_if(staList.second.cbegin(),
555 staList.second.cend(),
556 [aid](auto&& info) { return info.aid == aid; });
557 if (staIt == staList.second.cend())
558 {
559 staList.second.push_back(MasterInfo{aid, *mldOrLinkAddress, 0.0});
560 }
561 }
562
563 const auto staIt = std::find_if(m_staListUl.cbegin(), m_staListUl.cend(), [aid](auto&& info) {
564 return info.aid == aid;
565 });
566 if (staIt == m_staListUl.cend())
567 {
568 m_staListUl.push_back(MasterInfo{aid, *mldOrLinkAddress, 0.0});
569 }
570}
571
572void
574{
575 NS_LOG_FUNCTION(this << aid << address);
576
577 if (!m_apMac->GetHeSupported(address))
578 {
579 return;
580 }
581
582 auto mldOrLinkAddress = m_apMac->GetMldOrLinkAddressByAid(aid);
583 NS_ASSERT_MSG(mldOrLinkAddress, "AID " << aid << " not found");
584
585 if (m_apMac->IsAssociated(*mldOrLinkAddress))
586 {
587 // Another STA of the non-AP MLD is still associated
588 return;
589 }
590
591 for (auto& staList : m_staListDl)
592 {
593 staList.second.remove_if([&aid](const MasterInfo& info) { return info.aid == aid; });
594 }
595 m_staListUl.remove_if([&aid](const MasterInfo& info) { return info.aid == aid; });
596}
597
600{
601 NS_LOG_FUNCTION(this);
602
603 AcIndex primaryAc = m_edca->GetAccessCategory();
604
605 if (m_staListDl[primaryAc].empty())
606 {
607 NS_LOG_DEBUG("No HE stations associated: return SU_TX");
608 return TxFormat::SU_TX;
609 }
610
611 std::size_t count =
612 std::min(static_cast<std::size_t>(m_nStations), m_staListDl[primaryAc].size());
613 std::size_t nCentral26TonesRus;
614 HeRu::RuType ruType =
615 HeRu::GetEqualSizedRusForStations(m_allowedWidth, count, nCentral26TonesRus);
616 NS_ASSERT(count >= 1);
617
619 {
620 nCentral26TonesRus = 0;
621 }
622
623 uint8_t currTid = wifiAcList.at(primaryAc).GetHighTid();
624
626
627 if (mpdu && mpdu->GetHeader().IsQosData())
628 {
629 currTid = mpdu->GetHeader().GetQosTid();
630 }
631
632 // determine the list of TIDs to check
633 std::vector<uint8_t> tids;
634
636 {
637 for (auto acIt = wifiAcList.find(primaryAc); acIt != wifiAcList.end(); acIt++)
638 {
639 uint8_t firstTid = (acIt->first == primaryAc ? currTid : acIt->second.GetHighTid());
640 tids.push_back(firstTid);
641 tids.push_back(acIt->second.GetOtherTid(firstTid));
642 }
643 }
644 else
645 {
646 tids.push_back(currTid);
647 }
648
650 NS_ASSERT(heConfiguration);
651
655 m_txParams.m_txVector.SetGuardInterval(heConfiguration->GetGuardInterval().GetNanoSeconds());
656 m_txParams.m_txVector.SetBssColor(heConfiguration->GetBssColor());
657
658 // The TXOP limit can be exceeded by the TXOP holder if it does not transmit more
659 // than one Data or Management frame in the TXOP and the frame is not in an A-MPDU
660 // consisting of more than one MPDU (Sec. 10.22.2.8 of 802.11-2016).
661 // For the moment, we are considering just one MPDU per receiver.
662 Time actualAvailableTime = (m_initialFrame ? Time::Min() : m_availableTime);
663
664 // iterate over the associated stations until an enough number of stations is identified
665 auto staIt = m_staListDl[primaryAc].begin();
666 m_candidates.clear();
667
668 std::vector<uint8_t> ruAllocations;
669 auto numRuAllocs = m_txParams.m_txVector.GetChannelWidth() / 20;
670 ruAllocations.resize(numRuAllocs);
671 NS_ASSERT((m_candidates.size() % numRuAllocs) == 0);
672
673 while (staIt != m_staListDl[primaryAc].end() &&
674 m_candidates.size() <
675 std::min(static_cast<std::size_t>(m_nStations), count + nCentral26TonesRus))
676 {
677 NS_LOG_DEBUG("Next candidate STA (MAC=" << staIt->address << ", AID=" << staIt->aid << ")");
678
680 !m_apMac->GetEhtSupported(staIt->address))
681 {
682 NS_LOG_DEBUG("Skipping non-EHT STA because this DL MU PPDU is sent to EHT STAs only");
683 staIt++;
684 continue;
685 }
686
687 HeRu::RuType currRuType = (m_candidates.size() < count ? ruType : HeRu::RU_26_TONE);
688
689 // check if the AP has at least one frame to be sent to the current station
690 for (uint8_t tid : tids)
691 {
692 AcIndex ac = QosUtilsMapTidToAc(tid);
693 NS_ASSERT(ac >= primaryAc);
694 // check that a BA agreement is established with the receiver for the
695 // considered TID, since ack sequences for DL MU PPDUs require block ack
696 if (m_apMac->GetBaAgreementEstablishedAsOriginator(staIt->address, tid))
697 {
698 mpdu = m_apMac->GetQosTxop(ac)->PeekNextMpdu(m_linkId, tid, staIt->address);
699
700 // we only check if the first frame of the current TID meets the size
701 // and duration constraints. We do not explore the queues further.
702 if (mpdu)
703 {
704 mpdu = GetHeFem(m_linkId)->CreateAliasIfNeeded(mpdu);
705 // Use a temporary TX vector including only the STA-ID of the
706 // candidate station to check if the MPDU meets the size and time limits.
707 // An RU of the computed size is tentatively assigned to the candidate
708 // station, so that the TX duration can be correctly computed.
709 WifiTxVector suTxVector =
710 GetWifiRemoteStationManager(m_linkId)->GetDataTxVector(mpdu->GetHeader(),
712
713 WifiTxVector txVectorCopy = m_txParams.m_txVector;
714
715 // the first candidate STA determines the preamble type for the DL MU PPDU
716 if (m_candidates.empty() &&
718 {
720 m_txParams.m_txVector.SetEhtPpduType(0); // indicates DL OFDMA transmission
721 }
722
724 {{currRuType, 1, true},
725 suTxVector.GetMode().GetMcsValue(),
726 suTxVector.GetNss()});
727
728 if (!GetHeFem(m_linkId)->TryAddMpdu(mpdu, m_txParams, actualAvailableTime))
729 {
730 NS_LOG_DEBUG("Adding the peeked frame violates the time constraints");
731 m_txParams.m_txVector = txVectorCopy;
732 }
733 else
734 {
735 // the frame meets the constraints
736 NS_LOG_DEBUG("Adding candidate STA (MAC=" << staIt->address
737 << ", AID=" << staIt->aid
738 << ") TID=" << +tid);
739 m_candidates.emplace_back(staIt, mpdu);
740 break; // terminate the for loop
741 }
742 }
743 else
744 {
745 NS_LOG_DEBUG("No frames to send to " << staIt->address << " with TID=" << +tid);
746 }
747 }
748 }
749
750 // move to the next station in the list
751 staIt++;
752 }
753
754 if (m_candidates.empty())
755 {
756 if (m_forceDlOfdma)
757 {
758 NS_LOG_DEBUG("The AP does not have suitable frames to transmit: return NO_TX");
759 return NO_TX;
760 }
761 NS_LOG_DEBUG("The AP does not have suitable frames to transmit: return SU_TX");
762 return SU_TX;
763 }
764
765 return TxFormat::DL_MU_TX;
766}
767
768void
769RrMultiUserScheduler::FinalizeTxVector(WifiTxVector& txVector)
770{
771 // Do not log txVector because GetTxVectorForUlMu() left RUs undefined and
772 // printing them will crash the simulation
773 NS_LOG_FUNCTION(this);
774 NS_ASSERT(txVector.GetHeMuUserInfoMap().size() == m_candidates.size());
775
776 // compute how many stations can be granted an RU and the RU size
777 std::size_t nRusAssigned = m_candidates.size();
778 std::size_t nCentral26TonesRus;
779 HeRu::RuType ruType =
780 HeRu::GetEqualSizedRusForStations(m_allowedWidth, nRusAssigned, nCentral26TonesRus);
781
782 NS_LOG_DEBUG(nRusAssigned << " stations are being assigned a " << ruType << " RU");
783
784 if (!m_useCentral26TonesRus || m_candidates.size() == nRusAssigned)
785 {
786 nCentral26TonesRus = 0;
787 }
788 else
789 {
790 nCentral26TonesRus = std::min(m_candidates.size() - nRusAssigned, nCentral26TonesRus);
791 NS_LOG_DEBUG(nCentral26TonesRus << " stations are being assigned a 26-tones RU");
792 }
793
794 // re-allocate RUs based on the actual number of candidate stations
795 WifiTxVector::HeMuUserInfoMap heMuUserInfoMap;
796 std::swap(heMuUserInfoMap, txVector.GetHeMuUserInfoMap());
797
798 auto candidateIt = m_candidates.begin(); // iterator over the list of candidate receivers
799 auto ruSet = HeRu::GetRusOfType(m_allowedWidth, ruType);
800 auto ruSetIt = ruSet.begin();
801 auto central26TonesRus = HeRu::GetCentral26TonesRus(m_allowedWidth, ruType);
802 auto central26TonesRusIt = central26TonesRus.begin();
803
804 for (std::size_t i = 0; i < nRusAssigned + nCentral26TonesRus; i++)
805 {
806 NS_ASSERT(candidateIt != m_candidates.end());
807 auto mapIt = heMuUserInfoMap.find(candidateIt->first->aid);
808 NS_ASSERT(mapIt != heMuUserInfoMap.end());
809
810 txVector.SetHeMuUserInfo(mapIt->first,
811 {(i < nRusAssigned ? *ruSetIt++ : *central26TonesRusIt++),
812 mapIt->second.mcs,
813 mapIt->second.nss});
814 candidateIt++;
815 }
816
817 // remove candidates that will not be served
818 m_candidates.erase(candidateIt, m_candidates.end());
819}
820
821void
822RrMultiUserScheduler::UpdateCredits(std::list<MasterInfo>& staList,
823 Time txDuration,
824 const WifiTxVector& txVector)
825{
826 NS_LOG_FUNCTION(this << txDuration.As(Time::US) << txVector);
827
828 // find how many RUs have been allocated for each RU type
829 std::map<HeRu::RuType, std::size_t> ruMap;
830 for (const auto& userInfo : txVector.GetHeMuUserInfoMap())
831 {
832 ruMap.insert({userInfo.second.ru.GetRuType(), 0}).first->second++;
833 }
834
835 // The amount of credits received by each station equals the TX duration (in
836 // microseconds) divided by the number of stations.
837 double creditsPerSta = txDuration.ToDouble(Time::US) / staList.size();
838 // Transmitting stations have to pay a number of credits equal to the TX duration
839 // (in microseconds) times the allocated bandwidth share.
840 double debitsPerMhz =
841 txDuration.ToDouble(Time::US) /
842 std::accumulate(ruMap.begin(), ruMap.end(), 0, [](uint16_t sum, auto pair) {
843 return sum + pair.second * HeRu::GetBandwidth(pair.first);
844 });
845
846 // assign credits to all stations
847 for (auto& sta : staList)
848 {
849 sta.credits += creditsPerSta;
850 sta.credits = std::min(sta.credits, m_maxCredits.ToDouble(Time::US));
851 }
852
853 // subtract debits to the selected stations
854 for (auto& candidate : m_candidates)
855 {
856 auto mapIt = txVector.GetHeMuUserInfoMap().find(candidate.first->aid);
857 NS_ASSERT(mapIt != txVector.GetHeMuUserInfoMap().end());
858
859 candidate.first->credits -= debitsPerMhz * HeRu::GetBandwidth(mapIt->second.ru.GetRuType());
860 }
861
862 // sort the list in decreasing order of credits
863 staList.sort([](const MasterInfo& a, const MasterInfo& b) { return a.credits > b.credits; });
864}
865
867RrMultiUserScheduler::ComputeDlMuInfo()
868{
869 NS_LOG_FUNCTION(this);
870
871 if (m_candidates.empty())
872 {
873 return DlMuInfo();
874 }
875
876 DlMuInfo dlMuInfo;
877 std::swap(dlMuInfo.txParams.m_txVector, m_txParams.m_txVector);
878 FinalizeTxVector(dlMuInfo.txParams.m_txVector);
879
880 m_txParams.Clear();
881 Ptr<WifiMpdu> mpdu;
882
883 // Compute the TX params (again) by using the stored MPDUs and the final TXVECTOR
884 Time actualAvailableTime = (m_initialFrame ? Time::Min() : m_availableTime);
885
886 for (const auto& candidate : m_candidates)
887 {
888 mpdu = candidate.second;
889 NS_ASSERT(mpdu);
890
891 bool ret [[maybe_unused]] =
892 GetHeFem(m_linkId)->TryAddMpdu(mpdu, dlMuInfo.txParams, actualAvailableTime);
893 NS_ASSERT_MSG(ret,
894 "Weird that an MPDU does not meet constraints when "
895 "transmitted over a larger RU");
896 }
897
898 // We have to complete the PSDUs to send
899 Ptr<WifiMacQueue> queue;
900
901 for (const auto& candidate : m_candidates)
902 {
903 // Let us try first A-MSDU aggregation if possible
904 mpdu = candidate.second;
905 NS_ASSERT(mpdu);
906 uint8_t tid = mpdu->GetHeader().GetQosTid();
907 NS_ASSERT_MSG(mpdu->GetOriginal()->GetHeader().GetAddr1() == candidate.first->address,
908 "RA of the stored MPDU must match the stored address");
909
910 NS_ASSERT(mpdu->IsQueued());
911 Ptr<WifiMpdu> item = mpdu;
912
913 if (!mpdu->GetHeader().IsRetry())
914 {
915 // this MPDU must have been dequeued from the AC queue and we can try
916 // A-MSDU aggregation
917 item = GetHeFem(m_linkId)->GetMsduAggregator()->GetNextAmsdu(mpdu,
918 dlMuInfo.txParams,
919 m_availableTime);
920
921 if (!item)
922 {
923 // A-MSDU aggregation failed or disabled
924 item = mpdu;
925 }
926 m_apMac->GetQosTxop(QosUtilsMapTidToAc(tid))->AssignSequenceNumber(item);
927 }
928
929 // Now, let's try A-MPDU aggregation if possible
930 std::vector<Ptr<WifiMpdu>> mpduList =
931 GetHeFem(m_linkId)->GetMpduAggregator()->GetNextAmpdu(item,
932 dlMuInfo.txParams,
933 m_availableTime);
934
935 if (mpduList.size() > 1)
936 {
937 // A-MPDU aggregation succeeded, update psduMap
938 dlMuInfo.psduMap[candidate.first->aid] = Create<WifiPsdu>(std::move(mpduList));
939 }
940 else
941 {
942 dlMuInfo.psduMap[candidate.first->aid] = Create<WifiPsdu>(item, true);
943 }
944 }
945
946 AcIndex primaryAc = m_edca->GetAccessCategory();
947 UpdateCredits(m_staListDl[primaryAc],
948 dlMuInfo.txParams.m_txDuration,
949 dlMuInfo.txParams.m_txVector);
950
951 NS_LOG_DEBUG("Next station to serve has AID=" << m_staListDl[primaryAc].front().aid);
952
953 return dlMuInfo;
954}
955
957RrMultiUserScheduler::ComputeUlMuInfo()
958{
959 return UlMuInfo{m_trigger, m_triggerMacHdr, std::move(m_txParams)};
960}
961
962} // namespace ns3
#define Max(a, b)
#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:942
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:272
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:817
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:322
bool TraceDisconnectWithoutContext(std::string name, const CallbackBase &cb)
Disconnect from a TraceSource a Callback previously connected without a context.
Definition: object-base.cc:352
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:77
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:378
AcIndex GetAccessCategory() const
Get the access category of this object.
Definition: qos-txop.cc:800
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:415
bool IsNegative() const
Exactly equivalent to t <= 0.
Definition: nstime.h:324
static Time Min()
Minimum representable Time Not to be confused with Min(Time,Time).
Definition: nstime.h:287
@ MS
millisecond
Definition: nstime.h:117
double ToDouble(Unit unit) const
Get the Time value expressed in a particular unit.
Definition: nstime.h:573
bool IsZero() const
Exactly equivalent to t == 0.
Definition: nstime.h:315
AttributeValue implementation for Time.
Definition: nstime.h:1406
a unique identifier for an interface.
Definition: type-id.h:59
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:932
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:878
Ptr< HeConfiguration > GetHeConfiguration() const
Definition: wifi-mac.cc:1780
Ptr< WifiPhy > GetWifiPhy(uint8_t linkId=SINGLE_LINK_OP_ID) const
Definition: wifi-mac.cc:1185
bool GetEhtSupported() const
Return whether the device supports EHT.
Definition: wifi-mac.cc:1811
bool GetHeSupported() const
Return whether the device supports HE.
Definition: wifi-mac.cc:1805
RecipientAgreementOptConstRef GetBaAgreementEstablishedAsRecipient(Mac48Address originator, uint8_t tid) const
Definition: wifi-mac.cc:1724
Ptr< WifiRemoteStationManager > GetWifiRemoteStationManager(uint8_t linkId=0) const
Definition: wifi-mac.cc:920
OriginatorAgreementOptConstRef GetBaAgreementEstablishedAsOriginator(Mac48Address recipient, uint8_t tid) const
Definition: wifi-mac.cc:1710
Ptr< QosTxop > GetQosTxop(AcIndex ac) const
Accessor for a specified EDCA object.
Definition: wifi-mac.cc:513
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:802
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition: wifi-phy.cc:1528
WifiPhyBand GetPhyBand() const
Get the configured Wi-Fi band.
Definition: wifi-phy.cc:1042
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)
Definition: boolean.h:81
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition: boolean.cc:124
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Definition: nstime.h:1427
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Definition: nstime.h:1407
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
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
#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:1319
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:73
@ 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
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:706
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
std::ofstream queueSize