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
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 && !GetWifiRemoteStationManager()->GetHeSupported(mpdu->GetHeader().GetAddr1()))
154 {
155 return SU_TX;
156 }
157
158 if (m_enableUlOfdma && m_enableBsrp && (GetLastTxFormat() == DL_MU_TX || !mpdu))
159 {
160 TxFormat txFormat = TrySendingBsrpTf();
161
162 if (txFormat != DL_MU_TX)
163 {
164 return txFormat;
165 }
166 }
167 else if (m_enableUlOfdma && ((GetLastTxFormat() == 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
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 uint8_t tid = 0;
225 while (tid < 8)
226 {
227 // check that a BA agreement is established with the receiver for the
228 // considered TID, since ack sequences for UL MU require block ack
229 if (m_heFem->GetBaAgreementEstablished(staIt->address, tid))
230 {
231 break;
232 }
233 ++tid;
234 }
235 if (tid == 8)
236 {
237 NS_LOG_DEBUG("No Block Ack agreement established with " << staIt->address);
238 staIt++;
239 continue;
240 }
241
242 // prepare the MAC header of a frame that would be sent to the candidate station,
243 // just for the purpose of retrieving the TXVECTOR used to transmit to that station
245 hdr.SetAddr1(staIt->address);
247 WifiTxVector suTxVector =
249 txVector.SetHeMuUserInfo(staIt->aid,
250 {HeRu::RuSpec(), // assigned later by FinalizeTxVector
251 suTxVector.GetMode(),
252 suTxVector.GetNss()});
253 m_candidates.emplace_back(staIt, nullptr);
254
255 // move to the next station in the list
256 staIt++;
257 }
258
259 if (txVector.GetHeMuUserInfoMap().empty())
260 {
261 NS_LOG_DEBUG("No suitable station");
262 return txVector;
263 }
264
265 FinalizeTxVector(txVector);
266 return txVector;
267}
268
271{
272 NS_LOG_FUNCTION(this);
273
274 if (m_staListUl.empty())
275 {
276 NS_LOG_DEBUG("No HE stations associated: return SU_TX");
277 return TxFormat::SU_TX;
278 }
279
280 WifiTxVector txVector = GetTxVectorForUlMu([](const MasterInfo&) { return true; });
281
282 if (txVector.GetHeMuUserInfoMap().empty())
283 {
284 NS_LOG_DEBUG("No suitable station found");
285 return TxFormat::DL_MU_TX;
286 }
287
290
291 auto item = GetTriggerFrame(m_trigger);
292 m_triggerMacHdr = item->GetHeader();
293
295 // set the TXVECTOR used to send the Trigger Frame
298
299 if (!m_heFem->TryAddMpdu(item, m_txParams, m_availableTime))
300 {
301 // sending the BSRP Trigger Frame is not possible, hence return NO_TX. In
302 // this way, no transmission will occur now and the next time we will
303 // try again sending a BSRP Trigger Frame.
304 NS_LOG_DEBUG("Remaining TXOP duration is not enough for BSRP TF exchange");
305 return NO_TX;
306 }
307
308 // Compute the time taken by each station to transmit 8 QoS Null frames
309 Time qosNullTxDuration = Seconds(0);
310 for (const auto& userInfo : m_trigger)
311 {
313 txVector,
315 userInfo.GetAid12());
316 qosNullTxDuration = Max(qosNullTxDuration, duration);
317 }
318
319 if (m_availableTime != Time::Min())
320 {
321 // TryAddMpdu only considers the time to transmit the Trigger Frame
323 m_txParams.m_protection->protectionTime != Time::Min());
325 m_txParams.m_acknowledgment->acknowledgmentTime.IsZero());
327
328 if (m_txParams.m_protection->protectionTime + m_txParams.m_txDuration // BSRP TF tx time
329 + m_apMac->GetWifiPhy()->GetSifs() + qosNullTxDuration >
331 {
332 NS_LOG_DEBUG("Remaining TXOP duration is not enough for BSRP TF exchange");
333 return NO_TX;
334 }
335 }
336
337 uint16_t ulLength;
338 std::tie(ulLength, qosNullTxDuration) = HePhy::ConvertHeTbPpduDurationToLSigLength(
339 qosNullTxDuration,
342 NS_LOG_DEBUG("Duration of QoS Null frames: " << qosNullTxDuration.As(Time::MS));
343 m_trigger.SetUlLength(ulLength);
344
345 return UL_MU_TX;
346}
347
350{
351 NS_LOG_FUNCTION(this);
352
353 if (m_staListUl.empty())
354 {
355 NS_LOG_DEBUG("No HE stations associated: return SU_TX");
356 return TxFormat::SU_TX;
357 }
358
359 // check if an UL OFDMA transmission is possible after a DL OFDMA transmission
360 NS_ABORT_MSG_IF(m_ulPsduSize == 0, "The UlPsduSize attribute must be set to a non-null value");
361
362 // only consider stations that do not have reported a null queue size
364 [this](const MasterInfo& info) { return m_apMac->GetMaxBufferStatus(info.address) > 0; });
365
366 if (txVector.GetHeMuUserInfoMap().empty())
367 {
368 NS_LOG_DEBUG("No suitable station found");
369 return TxFormat::DL_MU_TX;
370 }
371
372 uint32_t maxBufferSize = 0;
373
374 for (const auto& candidate : txVector.GetHeMuUserInfoMap())
375 {
376 auto staIt = m_apMac->GetStaList().find(candidate.first);
377 NS_ASSERT(staIt != m_apMac->GetStaList().end());
378 uint8_t queueSize = m_apMac->GetMaxBufferStatus(staIt->second);
379 if (queueSize == 255)
380 {
381 NS_LOG_DEBUG("Buffer status of station " << staIt->second << " is unknown");
382 maxBufferSize = std::max(maxBufferSize, m_ulPsduSize);
383 }
384 else if (queueSize == 254)
385 {
386 NS_LOG_DEBUG("Buffer status of station " << staIt->second << " is not limited");
387 maxBufferSize = 0xffffffff;
388 }
389 else
390 {
391 NS_LOG_DEBUG("Buffer status of station " << staIt->second << " is " << +queueSize);
392 maxBufferSize = std::max(maxBufferSize, static_cast<uint32_t>(queueSize * 256));
393 }
394 }
395
396 if (maxBufferSize == 0)
397 {
398 return DL_MU_TX;
399 }
400
403
404 auto item = GetTriggerFrame(m_trigger);
405 m_triggerMacHdr = item->GetHeader();
406
407 // compute the maximum amount of time that can be granted to stations.
408 // This value is limited by the max PPDU duration
409 Time maxDuration = GetPpduMaxTime(txVector.GetPreambleType());
410
412 // set the TXVECTOR used to send the Trigger Frame
415
416 if (!m_heFem->TryAddMpdu(item, m_txParams, m_availableTime))
417 {
418 // an UL OFDMA transmission is not possible, hence return NO_TX. In
419 // this way, no transmission will occur now and the next time we will
420 // try again performing an UL OFDMA transmission.
421 NS_LOG_DEBUG("Remaining TXOP duration is not enough for UL MU exchange");
422 return NO_TX;
423 }
424
425 if (m_availableTime != Time::Min())
426 {
427 // TryAddMpdu only considers the time to transmit the Trigger Frame
429 m_txParams.m_protection->protectionTime != Time::Min());
431 m_txParams.m_acknowledgment->acknowledgmentTime != Time::Min());
433
434 maxDuration = Min(maxDuration,
435 m_availableTime - m_txParams.m_protection->protectionTime -
437 m_txParams.m_acknowledgment->acknowledgmentTime);
438 if (maxDuration.IsNegative())
439 {
440 NS_LOG_DEBUG("Remaining TXOP duration is not enough for UL MU exchange");
441 return NO_TX;
442 }
443 }
444
445 // Compute the time taken by each station to transmit a frame of maxBufferSize size
446 Time bufferTxTime = Seconds(0);
447 for (const auto& userInfo : m_trigger)
448 {
449 Time duration = WifiPhy::CalculateTxDuration(maxBufferSize,
450 txVector,
452 userInfo.GetAid12());
453 bufferTxTime = Max(bufferTxTime, duration);
454 }
455
456 if (bufferTxTime < maxDuration)
457 {
458 // the maximum buffer size can be transmitted within the allowed time
459 maxDuration = bufferTxTime;
460 }
461 else
462 {
463 // maxDuration may be a too short time. If it does not allow any station to
464 // transmit at least m_ulPsduSize bytes, give up the UL MU transmission for now
465 Time minDuration = Seconds(0);
466 for (const auto& userInfo : m_trigger)
467 {
469 txVector,
471 userInfo.GetAid12());
472 minDuration = (minDuration.IsZero() ? duration : Min(minDuration, duration));
473 }
474
475 if (maxDuration < minDuration)
476 {
477 // maxDuration is a too short time, hence return NO_TX. In this way,
478 // no transmission will occur now and the next time we will try again
479 // performing an UL OFDMA transmission.
480 NS_LOG_DEBUG("Available time " << maxDuration.As(Time::MS) << " is too short");
481 return NO_TX;
482 }
483 }
484
485 // maxDuration is the time to grant to the stations. Finalize the Trigger Frame
486 uint16_t ulLength;
487 std::tie(ulLength, maxDuration) =
489 txVector,
491 NS_LOG_DEBUG("TB PPDU duration: " << maxDuration.As(Time::MS));
492 m_trigger.SetUlLength(ulLength);
493 // set Preferred AC to the AC that gained channel access
494 for (auto& userInfo : m_trigger)
495 {
496 userInfo.SetBasicTriggerDepUserInfo(0, 0, m_edca->GetAccessCategory());
497 }
498
499 UpdateCredits(m_staListUl, maxDuration, txVector);
500
501 return UL_MU_TX;
502}
503
504void
506{
507 NS_LOG_FUNCTION(this << aid << address);
508
509 if (GetWifiRemoteStationManager()->GetHeSupported(address))
510 {
511 for (auto& staList : m_staListDl)
512 {
513 staList.second.push_back(MasterInfo{aid, address, 0.0});
514 }
515 m_staListUl.push_back(MasterInfo{aid, address, 0.0});
516 }
517}
518
519void
521{
522 NS_LOG_FUNCTION(this << aid << address);
523
524 if (GetWifiRemoteStationManager()->GetHeSupported(address))
525 {
526 for (auto& staList : m_staListDl)
527 {
528 staList.second.remove_if([&aid, &address](const MasterInfo& info) {
529 return info.aid == aid && info.address == address;
530 });
531 }
532 m_staListUl.remove_if([&aid, &address](const MasterInfo& info) {
533 return info.aid == aid && info.address == address;
534 });
535 }
536}
537
540{
541 NS_LOG_FUNCTION(this);
542
543 AcIndex primaryAc = m_edca->GetAccessCategory();
544
545 if (m_staListDl[primaryAc].empty())
546 {
547 NS_LOG_DEBUG("No HE stations associated: return SU_TX");
548 return TxFormat::SU_TX;
549 }
550
551 std::size_t count =
552 std::min(static_cast<std::size_t>(m_nStations), m_staListDl[primaryAc].size());
553 std::size_t nCentral26TonesRus;
554 HeRu::RuType ruType =
555 HeRu::GetEqualSizedRusForStations(m_allowedWidth, count, nCentral26TonesRus);
556 NS_ASSERT(count >= 1);
557
559 {
560 nCentral26TonesRus = 0;
561 }
562
563 uint8_t currTid = wifiAcList.at(primaryAc).GetHighTid();
564
566
567 if (mpdu && mpdu->GetHeader().IsQosData())
568 {
569 currTid = mpdu->GetHeader().GetQosTid();
570 }
571
572 // determine the list of TIDs to check
573 std::vector<uint8_t> tids;
574
576 {
577 for (auto acIt = wifiAcList.find(primaryAc); acIt != wifiAcList.end(); acIt++)
578 {
579 uint8_t firstTid = (acIt->first == primaryAc ? currTid : acIt->second.GetHighTid());
580 tids.push_back(firstTid);
581 tids.push_back(acIt->second.GetOtherTid(firstTid));
582 }
583 }
584 else
585 {
586 tids.push_back(currTid);
587 }
588
590 NS_ASSERT(heConfiguration);
591
595 m_txParams.m_txVector.SetGuardInterval(heConfiguration->GetGuardInterval().GetNanoSeconds());
596 m_txParams.m_txVector.SetBssColor(heConfiguration->GetBssColor());
597
598 // The TXOP limit can be exceeded by the TXOP holder if it does not transmit more
599 // than one Data or Management frame in the TXOP and the frame is not in an A-MPDU
600 // consisting of more than one MPDU (Sec. 10.22.2.8 of 802.11-2016).
601 // For the moment, we are considering just one MPDU per receiver.
602 Time actualAvailableTime = (m_initialFrame ? Time::Min() : m_availableTime);
603
604 // iterate over the associated stations until an enough number of stations is identified
605 auto staIt = m_staListDl[primaryAc].begin();
606 m_candidates.clear();
607
608 std::vector<uint8_t> ruAllocations;
609 auto numRuAllocs = m_txParams.m_txVector.GetChannelWidth() / 20;
610 ruAllocations.resize(numRuAllocs);
611 NS_ASSERT((m_candidates.size() % numRuAllocs) == 0);
612
613 while (staIt != m_staListDl[primaryAc].end() &&
614 m_candidates.size() <
615 std::min(static_cast<std::size_t>(m_nStations), count + nCentral26TonesRus))
616 {
617 NS_LOG_DEBUG("Next candidate STA (MAC=" << staIt->address << ", AID=" << staIt->aid << ")");
618
619 HeRu::RuType currRuType = (m_candidates.size() < count ? ruType : HeRu::RU_26_TONE);
620
621 // check if the AP has at least one frame to be sent to the current station
622 for (uint8_t tid : tids)
623 {
624 AcIndex ac = QosUtilsMapTidToAc(tid);
625 NS_ASSERT(ac >= primaryAc);
626 // check that a BA agreement is established with the receiver for the
627 // considered TID, since ack sequences for DL MU PPDUs require block ack
628 if (m_apMac->GetQosTxop(ac)->GetBaAgreementEstablished(staIt->address, tid))
629 {
630 mpdu =
631 m_apMac->GetQosTxop(ac)->PeekNextMpdu(SINGLE_LINK_OP_ID, tid, staIt->address);
632
633 // we only check if the first frame of the current TID meets the size
634 // and duration constraints. We do not explore the queues further.
635 if (mpdu)
636 {
637 // Use a temporary TX vector including only the STA-ID of the
638 // candidate station to check if the MPDU meets the size and time limits.
639 // An RU of the computed size is tentatively assigned to the candidate
640 // station, so that the TX duration can be correctly computed.
641 WifiTxVector suTxVector =
642 GetWifiRemoteStationManager()->GetDataTxVector(mpdu->GetHeader(),
644 WifiTxVector txVectorCopy = m_txParams.m_txVector;
645
647 staIt->aid,
648 {{currRuType, 1, true}, suTxVector.GetMode(), suTxVector.GetNss()});
649
650 if (!m_heFem->TryAddMpdu(mpdu, m_txParams, actualAvailableTime))
651 {
652 NS_LOG_DEBUG("Adding the peeked frame violates the time constraints");
653 m_txParams.m_txVector = txVectorCopy;
654 }
655 else
656 {
657 // the frame meets the constraints
658 NS_LOG_DEBUG("Adding candidate STA (MAC=" << staIt->address
659 << ", AID=" << staIt->aid
660 << ") TID=" << +tid);
661 m_candidates.emplace_back(staIt, mpdu);
662 break; // terminate the for loop
663 }
664 }
665 else
666 {
667 NS_LOG_DEBUG("No frames to send to " << staIt->address << " with TID=" << +tid);
668 }
669 }
670 }
671
672 // move to the next station in the list
673 staIt++;
674 }
675
676 if (m_candidates.empty())
677 {
678 if (m_forceDlOfdma)
679 {
680 NS_LOG_DEBUG("The AP does not have suitable frames to transmit: return NO_TX");
681 return NO_TX;
682 }
683 NS_LOG_DEBUG("The AP does not have suitable frames to transmit: return SU_TX");
684 return SU_TX;
685 }
686
687 return TxFormat::DL_MU_TX;
688}
689
690void
691RrMultiUserScheduler::FinalizeTxVector(WifiTxVector& txVector)
692{
693 // Do not log txVector because GetTxVectorForUlMu() left RUs undefined and
694 // printing them will crash the simulation
695 NS_LOG_FUNCTION(this);
696 NS_ASSERT(txVector.GetHeMuUserInfoMap().size() == m_candidates.size());
697
698 // compute how many stations can be granted an RU and the RU size
699 std::size_t nRusAssigned = m_candidates.size();
700 std::size_t nCentral26TonesRus;
701 HeRu::RuType ruType =
702 HeRu::GetEqualSizedRusForStations(m_allowedWidth, nRusAssigned, nCentral26TonesRus);
703
704 NS_LOG_DEBUG(nRusAssigned << " stations are being assigned a " << ruType << " RU");
705
706 if (!m_useCentral26TonesRus || m_candidates.size() == nRusAssigned)
707 {
708 nCentral26TonesRus = 0;
709 }
710 else
711 {
712 nCentral26TonesRus = std::min(m_candidates.size() - nRusAssigned, nCentral26TonesRus);
713 NS_LOG_DEBUG(nCentral26TonesRus << " stations are being assigned a 26-tones RU");
714 }
715
716 // re-allocate RUs based on the actual number of candidate stations
717 WifiTxVector::HeMuUserInfoMap heMuUserInfoMap;
718 std::swap(heMuUserInfoMap, txVector.GetHeMuUserInfoMap());
719
720 auto candidateIt = m_candidates.begin(); // iterator over the list of candidate receivers
721 auto ruSet = HeRu::GetRusOfType(m_allowedWidth, ruType);
722 auto ruSetIt = ruSet.begin();
723 auto central26TonesRus = HeRu::GetCentral26TonesRus(m_allowedWidth, ruType);
724 auto central26TonesRusIt = central26TonesRus.begin();
725
726 for (std::size_t i = 0; i < nRusAssigned + nCentral26TonesRus; i++)
727 {
728 NS_ASSERT(candidateIt != m_candidates.end());
729 auto mapIt = heMuUserInfoMap.find(candidateIt->first->aid);
730 NS_ASSERT(mapIt != heMuUserInfoMap.end());
731
732 txVector.SetHeMuUserInfo(mapIt->first,
733 {(i < nRusAssigned ? *ruSetIt++ : *central26TonesRusIt++),
734 mapIt->second.mcs,
735 mapIt->second.nss});
736 candidateIt++;
737 }
738
739 // remove candidates that will not be served
740 m_candidates.erase(candidateIt, m_candidates.end());
741}
742
743void
744RrMultiUserScheduler::UpdateCredits(std::list<MasterInfo>& staList,
745 Time txDuration,
746 const WifiTxVector& txVector)
747{
748 NS_LOG_FUNCTION(this << txDuration.As(Time::US) << txVector);
749
750 // find how many RUs have been allocated for each RU type
751 std::map<HeRu::RuType, std::size_t> ruMap;
752 for (const auto& userInfo : txVector.GetHeMuUserInfoMap())
753 {
754 ruMap.insert({userInfo.second.ru.GetRuType(), 0}).first->second++;
755 }
756
757 // The amount of credits received by each station equals the TX duration (in
758 // microseconds) divided by the number of stations.
759 double creditsPerSta = txDuration.ToDouble(Time::US) / staList.size();
760 // Transmitting stations have to pay a number of credits equal to the TX duration
761 // (in microseconds) times the allocated bandwidth share.
762 double debitsPerMhz =
763 txDuration.ToDouble(Time::US) /
764 std::accumulate(ruMap.begin(), ruMap.end(), 0, [](uint16_t sum, auto pair) {
765 return sum + pair.second * HeRu::GetBandwidth(pair.first);
766 });
767
768 // assign credits to all stations
769 for (auto& sta : staList)
770 {
771 sta.credits += creditsPerSta;
772 sta.credits = std::min(sta.credits, m_maxCredits.ToDouble(Time::US));
773 }
774
775 // subtract debits to the selected stations
776 for (auto& candidate : m_candidates)
777 {
778 auto mapIt = txVector.GetHeMuUserInfoMap().find(candidate.first->aid);
779 NS_ASSERT(mapIt != txVector.GetHeMuUserInfoMap().end());
780
781 candidate.first->credits -= debitsPerMhz * HeRu::GetBandwidth(mapIt->second.ru.GetRuType());
782 }
783
784 // sort the list in decreasing order of credits
785 staList.sort([](const MasterInfo& a, const MasterInfo& b) { return a.credits > b.credits; });
786}
787
789RrMultiUserScheduler::ComputeDlMuInfo()
790{
791 NS_LOG_FUNCTION(this);
792
793 if (m_candidates.empty())
794 {
795 return DlMuInfo();
796 }
797
798 DlMuInfo dlMuInfo;
799 std::swap(dlMuInfo.txParams.m_txVector, m_txParams.m_txVector);
800 FinalizeTxVector(dlMuInfo.txParams.m_txVector);
801
802 m_txParams.Clear();
803 Ptr<WifiMpdu> mpdu;
804
805 // Compute the TX params (again) by using the stored MPDUs and the final TXVECTOR
806 Time actualAvailableTime = (m_initialFrame ? Time::Min() : m_availableTime);
807
808 for (const auto& candidate : m_candidates)
809 {
810 mpdu = candidate.second;
811 NS_ASSERT(mpdu);
812
813 bool ret [[maybe_unused]] =
814 m_heFem->TryAddMpdu(mpdu, dlMuInfo.txParams, actualAvailableTime);
815 NS_ASSERT_MSG(ret,
816 "Weird that an MPDU does not meet constraints when "
817 "transmitted over a larger RU");
818 }
819
820 // We have to complete the PSDUs to send
821 Ptr<WifiMacQueue> queue;
822 Mac48Address receiver;
823
824 for (const auto& candidate : m_candidates)
825 {
826 // Let us try first A-MSDU aggregation if possible
827 mpdu = candidate.second;
828 NS_ASSERT(mpdu);
829 uint8_t tid = mpdu->GetHeader().GetQosTid();
830 receiver = mpdu->GetHeader().GetAddr1();
831 NS_ASSERT(receiver == candidate.first->address);
832
833 NS_ASSERT(mpdu->IsQueued());
834 Ptr<WifiMpdu> item = mpdu;
835
836 if (!mpdu->GetHeader().IsRetry())
837 {
838 // this MPDU must have been dequeued from the AC queue and we can try
839 // A-MSDU aggregation
840 item = m_heFem->GetMsduAggregator()->GetNextAmsdu(mpdu,
841 dlMuInfo.txParams,
842 m_availableTime);
843
844 if (!item)
845 {
846 // A-MSDU aggregation failed or disabled
847 item = mpdu;
848 }
849 m_apMac->GetQosTxop(QosUtilsMapTidToAc(tid))->AssignSequenceNumber(item);
850 }
851
852 // Now, let's try A-MPDU aggregation if possible
853 std::vector<Ptr<WifiMpdu>> mpduList =
854 m_heFem->GetMpduAggregator()->GetNextAmpdu(item, dlMuInfo.txParams, m_availableTime);
855
856 if (mpduList.size() > 1)
857 {
858 // A-MPDU aggregation succeeded, update psduMap
859 dlMuInfo.psduMap[candidate.first->aid] = Create<WifiPsdu>(std::move(mpduList));
860 }
861 else
862 {
863 dlMuInfo.psduMap[candidate.first->aid] = Create<WifiPsdu>(item, true);
864 }
865 }
866
867 AcIndex primaryAc = m_edca->GetAccessCategory();
868 UpdateCredits(m_staListDl[primaryAc],
869 dlMuInfo.txParams.m_txDuration,
870 dlMuInfo.txParams.m_txVector);
871
872 NS_LOG_DEBUG("Next station to serve has AID=" << m_staListDl[primaryAc].front().aid);
873
874 return dlMuInfo;
875}
876
878RrMultiUserScheduler::ComputeUlMuInfo()
879{
880 return UlMuInfo{m_trigger, m_triggerMacHdr, std::move(m_txParams)};
881}
882
883} // namespace ns3
#define min(a, b)
Definition: 80211b.c:42
#define max(a, b)
Definition: 80211b.c:43
#define Min(a, b)
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...
const std::map< uint16_t, Mac48Address > & GetStaList(uint8_t linkId=SINGLE_LINK_OP_ID) const
Get a const reference to the map of associated stations on the given link.
AttributeValue implementation for Boolean.
Definition: boolean.h:37
Headers for Trigger frames.
Definition: ctrl-headers.h:886
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:255
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:810
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
Ptr< WifiMpdu > GetTriggerFrame(const CtrlTriggerHeader &trigger) const
Get an MPDU containing the given Trigger Frame.
Ptr< WifiRemoteStationManager > GetWifiRemoteStationManager() const
Get the station manager attached to the AP.
void DoInitialize() override
Initialize() implementation.
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< HeFrameExchangeManager > m_heFem
HE Frame Exchange Manager.
TxFormat GetLastTxFormat() const
Get the format of the last transmission, as determined by the last call to NotifyAccessGranted that d...
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.
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:369
bool TraceDisconnectWithoutContext(std::string name, const CallbackBase &cb)
Disconnect from a TraceSource a Callback previously connected without a context.
Definition: object-base.cc:397
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:78
AcIndex GetAccessCategory() const
Get the access category of this object.
Definition: qos-txop.cc:747
bool GetBaAgreementEstablished(Mac48Address address, uint8_t tid) const
Definition: qos-txop.cc:261
Ptr< WifiMpdu > PeekNextMpdu(uint8_t linkId, uint8_t tid=8, Mac48Address recipient=Mac48Address::GetBroadcast(), Ptr< WifiMpdu > item=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:368
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
double ToDouble(enum Unit unit) const
Get the Time value expressed in a particular unit.
Definition: nstime.h:572
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
TimeWithUnit As(const enum Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition: time.cc:417
bool IsZero() const
Exactly equivalent to t == 0.
Definition: nstime.h:314
AttributeValue implementation for Time.
Definition: nstime.h:1425
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< HeConfiguration > GetHeConfiguration() const
Definition: wifi-mac.cc:1250
Ptr< WifiPhy > GetWifiPhy(uint8_t linkId=SINGLE_LINK_OP_ID) const
Definition: wifi-mac.cc:949
Ptr< WifiRemoteStationManager > GetWifiRemoteStationManager(uint8_t linkId=0) const
Definition: wifi-mac.cc:881
Mac48Address GetAddress() const
Definition: wifi-mac.cc:442
Ptr< QosTxop > GetQosTxop(AcIndex ac) const
Accessor for a specified EDCA object.
Definition: wifi-mac.cc:489
Time GetSifs() const
Return the Short Interframe Space (SIFS) for this PHY.
Definition: wifi-phy.cc:728
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition: wifi-phy.cc:1422
WifiPhyBand GetPhyBand() const
Get the configured Wi-Fi band.
Definition: wifi-phy.cc:950
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 SetChannelWidth(uint16_t channelWidth)
Sets the selected channelWidth (in MHz)
std::map< uint16_t, HeMuUserInfo > HeMuUserInfoMap
map of HE MU specific user info paramters 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.
Empty class, used as a default parent class for SimpleRefCount.
#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:86
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition: boolean.cc:124
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Definition: nstime.h:1426
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
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:45
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1338
AcIndex QosUtilsMapTidToAc(uint8_t tid)
Maps TID (Traffic ID) to Access classes.
Definition: qos-utils.cc:132
AcIndex
This enumeration defines the Access Categories as an enumeration with values corresponding to the AC ...
Definition: qos-utils.h:74
@ WIFI_PREAMBLE_HE_TB
@ WIFI_PREAMBLE_HE_MU
@ BASIC_TRIGGER
Definition: ctrl-headers.h:560
@ BSRP_TRIGGER
Definition: ctrl-headers.h:564
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:691
Ptr< const AttributeChecker > MakeTimeChecker(const Time min, const Time max)
Helper to make a Time checker with bounded range.
Definition: time.cc:535
static constexpr uint8_t SINGLE_LINK_OP_ID
Link ID for single link operations (helps tracking places where correct link ID is to be used to supp...
Definition: wifi-utils.h:140
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