5G-LENA nr-v3.1-69-g2dd513a7
The 5G/NR module for the ns-3 simulator
Loading...
Searching...
No Matches
nr-mac-scheduler-ns3.cc
1// Copyright (c) 2019 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
2//
3// SPDX-License-Identifier: GPL-2.0-only
4
5#define NS_LOG_APPEND_CONTEXT \
6 do \
7 { \
8 std::clog << " [ CellId " << GetCellId() << ", bwpId " << GetBwpId() << "] "; \
9 } while (false);
10
11#include "nr-mac-scheduler-ns3.h"
12
13#include "nr-mac-scheduler-harq-rr.h"
14#include "nr-mac-scheduler-lc-rr.h"
15#include "nr-mac-scheduler-srs-default.h"
16#include "nr-mac-short-bsr-ce.h"
17
18#include <ns3/boolean.h>
19#include <ns3/integer.h>
20#include <ns3/log.h>
21#include <ns3/pointer.h>
22#include <ns3/uinteger.h>
23
24#include <algorithm>
25#include <memory>
26#include <unordered_set>
27
28namespace ns3
29{
30
31NS_LOG_COMPONENT_DEFINE("NrMacSchedulerNs3");
32NS_OBJECT_ENSURE_REGISTERED(NrMacSchedulerNs3);
33
36{
37 NS_LOG_FUNCTION_NOARGS();
38
39 // Hardcoded, but the type can be a parameter if needed
40 m_schedHarq = std::make_unique<NrMacSchedulerHarqRr>();
41 m_schedHarq->InstallGetBwInRBG(std::bind(&NrMacSchedulerNs3::GetBandwidthInRbg, this));
42 m_schedHarq->InstallGetBwpIdFn(std::bind(&NrMacSchedulerNs3::GetBwpId, this));
43 m_schedHarq->InstallGetCellIdFn(std::bind(&NrMacSchedulerNs3::GetCellId, this));
44
45 m_cqiManagement.InstallGetBwpIdFn(std::bind(&NrMacSchedulerNs3::GetBwpId, this));
46 m_cqiManagement.InstallGetCellIdFn(std::bind(&NrMacSchedulerNs3::GetCellId, this));
47 m_cqiManagement.InstallGetNrAmcDlFn(std::bind([this]() { return m_dlAmc; }));
48 m_cqiManagement.InstallGetNrAmcUlFn(std::bind([this]() { return m_ulAmc; }));
49 m_cqiManagement.InstallGetStartMcsDlFn(std::bind([this]() { return m_startMcsDl; }));
50 m_cqiManagement.InstallGetStartMcsUlFn(std::bind([this]() { return m_startMcsUl; }));
51
52 // If more Srs allocators will be created, then we will add an attribute
53 m_schedulerSrs = CreateObject<NrMacSchedulerSrsDefault>();
54}
55
57{
58 m_ueMap.clear();
59}
60
61void
62NrMacSchedulerNs3::InstallDlAmc(const Ptr<NrAmc>& dlAmc)
63{
64 m_dlAmc = dlAmc;
65 m_dlAmc->SetDlMode();
66}
67
68void
69NrMacSchedulerNs3::InstallUlAmc(const Ptr<NrAmc>& ulAmc)
70{
71 m_ulAmc = ulAmc;
72 m_ulAmc->SetUlMode();
73}
74
75Ptr<const NrAmc>
77{
78 NS_LOG_FUNCTION(this);
79 return m_ulAmc;
80}
81
82Ptr<const NrAmc>
84{
85 NS_LOG_FUNCTION(this);
86 return m_dlAmc;
87}
88
89int64_t
91{
92 NS_LOG_FUNCTION(this << stream);
93 return m_schedulerSrs->AssignStreams(stream);
94}
95
96TypeId
98{
99 static TypeId tid =
100 TypeId("ns3::NrMacSchedulerNs3")
101 .SetParent<NrMacScheduler>()
102 .AddAttribute("CqiTimerThreshold",
103 "The time while a CQI is valid",
104 TimeValue(Seconds(1)),
107 MakeTimeChecker())
108 .AddAttribute("FixedMcsDl",
109 "Fix MCS to value set in StartingMcsDl",
110 BooleanValue(false),
111 MakeBooleanAccessor(&NrMacSchedulerNs3::SetFixedDlMcs,
113 MakeBooleanChecker())
114 .AddAttribute("FixedMcsUl",
115 "Fix MCS to value set in StartingMcsUl",
116 BooleanValue(false),
117 MakeBooleanAccessor(&NrMacSchedulerNs3::SetFixedUlMcs,
119 MakeBooleanChecker())
120 .AddAttribute("StartingMcsDl",
121 "Starting MCS for DL",
122 UintegerValue(0),
123 MakeUintegerAccessor(&NrMacSchedulerNs3::SetStartMcsDl,
125 MakeUintegerChecker<uint8_t>())
126 .AddAttribute("StartingMcsUl",
127 "Starting MCS for UL",
128 UintegerValue(0),
129 MakeUintegerAccessor(&NrMacSchedulerNs3::SetStartMcsUl,
131 MakeUintegerChecker<uint8_t>())
132 .AddAttribute("DlCtrlSymbols",
133 "Number of symbols allocated for DL CTRL",
134 UintegerValue(1),
135 MakeUintegerAccessor(&NrMacSchedulerNs3::SetDlCtrlSyms,
137 MakeUintegerChecker<uint8_t>())
138 .AddAttribute("UlCtrlSymbols",
139 "Number of symbols allocated for UL CTRL",
140 UintegerValue(1),
141 MakeUintegerAccessor(&NrMacSchedulerNs3::SetUlCtrlSyms,
143 MakeUintegerChecker<uint8_t>())
144 .AddAttribute("SrsSymbols",
145 "Number of symbols allocated for UL SRS",
146 UintegerValue(1),
147 MakeUintegerAccessor(&NrMacSchedulerNs3::SetSrsCtrlSyms,
149 MakeUintegerChecker<uint8_t>())
150 .AddAttribute("EnableSrsInUlSlots",
151 "Denotes whether the SRSs will be transmitted only in F slots"
152 "or both in F and UL slots. If False, SRS is transmitted only"
153 "in F slots, if True in both (F/UL)",
154 BooleanValue(true),
155 MakeBooleanAccessor(&NrMacSchedulerNs3::SetSrsInUlSlots,
157 MakeBooleanChecker())
158 .AddAttribute("EnableSrsInFSlots",
159 "Denotes whether the SRSs will be transmitted in F slots"
160 "If true, it can be transmitted in F slots, otherwise "
161 "it cannot.",
162 BooleanValue(true),
163 MakeBooleanAccessor(&NrMacSchedulerNs3::SetSrsInFSlots,
165 MakeBooleanChecker())
166 .AddAttribute("DlAmc",
167 "The DL AMC of this scheduler",
168 PointerValue(),
169 MakePointerAccessor(&NrMacSchedulerNs3::m_dlAmc),
170 MakePointerChecker<NrAmc>())
171 .AddAttribute("UlAmc",
172 "The UL AMC of this scheduler",
173 PointerValue(),
174 MakePointerAccessor(&NrMacSchedulerNs3::m_ulAmc),
175 MakePointerChecker<NrAmc>())
176 .AddAttribute("MaxDlMcs",
177 "Maximum MCS index for DL",
178 IntegerValue(-1),
179 MakeIntegerAccessor(&NrMacSchedulerNs3::SetMaxDlMcs,
181 MakeIntegerChecker<int8_t>(-1, 30))
182 .AddAttribute("EnableHarqReTx",
183 "If true, it would set the max HARQ ReTx to 3; otherwise it set it to 0",
184 BooleanValue(true),
185 MakeBooleanAccessor(&NrMacSchedulerNs3::EnableHarqReTx,
187 MakeBooleanChecker())
188 .AddAttribute(
189 "SchedLcAlgorithmType",
190 "Type of the scheduling algorithm that assigns bytes to the different LCs.",
191 TypeIdValue(NrMacSchedulerLcRR::GetTypeId()),
192 MakeTypeIdAccessor(&NrMacSchedulerNs3::SetLcSched),
193 //&NrMacSchedulerNs3::GetLcSched),
194 MakeTypeIdChecker())
195 .AddAttribute("RachUlGrantMcs",
196 "The MCS of the RACH UL grant, must be [0..15] (default 0)",
197 UintegerValue(0),
198 MakeUintegerAccessor(&NrMacSchedulerNs3::SetRachUlGrantMcs),
199 MakeUintegerChecker<uint8_t>());
200
201 return tid;
202}
203
211void
213{
214 NS_LOG_FUNCTION(this);
215 m_fixedMcsDl = true;
216 m_fixedMcsUl = true;
217 m_startMcsDl = static_cast<uint8_t>(mcs);
218 m_startMcsUl = static_cast<uint8_t>(mcs);
219}
220
221void
224{
225 NS_LOG_FUNCTION(this);
226
227 m_rachList = params.m_rachList;
228}
229
230void
232{
233 NS_LOG_FUNCTION(this);
234 m_cqiTimersThreshold = v;
235}
236
237Time
239{
240 NS_LOG_FUNCTION(this);
241 return m_cqiTimersThreshold;
242}
243
244void
246{
247 NS_LOG_FUNCTION(this);
248 m_fixedMcsDl = v;
249}
250
251bool
253{
254 NS_LOG_FUNCTION(this);
255 return m_fixedMcsDl;
256}
257
258void
260{
261 NS_LOG_FUNCTION(this);
262 m_fixedMcsUl = v;
263}
264
265bool
267{
268 NS_LOG_FUNCTION(this);
269 return m_fixedMcsUl;
270}
271
272void
274{
275 NS_LOG_FUNCTION(this);
276 m_startMcsDl = v;
277}
278
279uint8_t
281{
282 NS_LOG_FUNCTION(this);
283 return m_startMcsDl;
284}
285
286void
288{
289 NS_LOG_FUNCTION(this);
290 m_maxDlMcs = v;
291}
292
293int8_t
295{
296 NS_LOG_FUNCTION(this);
297 return m_maxDlMcs;
298}
299
300void
302{
303 m_rachUlGrantMcs = v;
304}
305
306void
308{
309 NS_LOG_FUNCTION(this);
310 ObjectFactory factory;
311 m_schedLcType = type;
312
313 factory.SetTypeId(m_schedLcType);
314 m_schedLc = DynamicCast<NrMacSchedulerLcAlgorithm>(factory.Create());
315 NS_ASSERT(m_schedLc != nullptr);
316}
317
318void
320{
321 NS_LOG_FUNCTION(this);
322 m_startMcsUl = v;
323}
324
325uint8_t
327{
328 NS_LOG_FUNCTION(this);
329 return m_startMcsUl;
330}
331
332void
334{
335 m_dlCtrlSymbols = v;
336}
337
338uint8_t
340{
341 return m_dlCtrlSymbols;
342}
343
344void
346{
347 m_ulCtrlSymbols = v;
348}
349
350void
351NrMacSchedulerNs3::SetDlNotchedRbgMask(const std::vector<uint8_t>& dlNotchedRbgsMask)
352{
353 NS_LOG_FUNCTION(this);
354 m_dlNotchedRbgsMask = dlNotchedRbgsMask;
355 std::stringstream ss;
356
357 // print the DL mask set (prefix + is added just for printing purposes)
358 for (const auto& x : m_dlNotchedRbgsMask)
359 {
360 ss << +x << " ";
361 }
362 NS_LOG_INFO("Set DL notched mask: " << ss.str());
363}
364
365std::vector<uint8_t>
367{
368 return m_dlNotchedRbgsMask;
369}
370
371void
372NrMacSchedulerNs3::SetUlNotchedRbgMask(const std::vector<uint8_t>& ulNotchedRbgsMask)
373{
374 NS_LOG_FUNCTION(this);
375 m_ulNotchedRbgsMask = ulNotchedRbgsMask;
376 std::stringstream ss;
377
378 // print the UL mask set (prefix + is added just for printing purposes)
379 for (const auto& x : m_ulNotchedRbgsMask)
380 {
381 ss << +x << " ";
382 }
383 NS_LOG_INFO("Set UL notched mask: " << ss.str());
384}
385
386std::vector<uint8_t>
388{
389 return m_ulNotchedRbgsMask;
390}
391
392void
394{
395 m_srsCtrlSymbols = v;
396}
397
398uint8_t
400{
401 return m_srsCtrlSymbols;
402}
403
404void
406{
407 m_enableSrsInUlSlots = v;
408}
409
410bool
412{
413 return m_enableSrsInUlSlots;
414}
415
416void
418{
419 m_enableSrsInFSlots = v;
420}
421
422bool
424{
425 return m_enableSrsInFSlots;
426}
427
428void
430{
431 m_enableHarqReTx = enableFlag;
432}
433
434bool
436{
437 return m_enableHarqReTx;
438}
439
440uint8_t
442 uint8_t symAvail,
443 const NrMacSchedulerNs3::ActiveHarqMap& activeDlHarq,
444 const std::unordered_map<uint16_t, UePtr>& ueMap,
445 std::vector<DlHarqInfo>* dlHarqToRetransmit,
446 const std::vector<DlHarqInfo>& dlHarqFeedback,
447 SlotAllocInfo* slotAlloc) const
448{
449 NS_LOG_FUNCTION(this);
450 return m_schedHarq->ScheduleDlHarq(startingPoint,
451 symAvail,
452 activeDlHarq,
453 ueMap,
454 dlHarqToRetransmit,
455 dlHarqFeedback,
456 slotAlloc);
457}
458
459uint8_t
461 uint8_t symAvail,
462 const std::unordered_map<uint16_t, UePtr>& ueMap,
463 std::vector<UlHarqInfo>* ulHarqToRetransmit,
464 const std::vector<UlHarqInfo>& ulHarqFeedback,
465 SlotAllocInfo* slotAlloc) const
466{
467 NS_LOG_FUNCTION(this);
468 return m_schedHarq->ScheduleUlHarq(startingPoint,
469 symAvail,
470 ueMap,
471 ulHarqToRetransmit,
472 ulHarqFeedback,
473 slotAlloc);
474}
475
476void
478{
479 NS_LOG_FUNCTION(this);
480 m_schedHarq->SortDlHarq(activeDlHarq);
481}
482
483void
485{
486 NS_LOG_FUNCTION(this);
487 m_schedHarq->SortDlHarq(activeUlHarq);
488}
489
490uint8_t
492{
493 return m_ulCtrlSymbols;
494}
495
502void
505{
506 NS_LOG_FUNCTION(this);
507
508 NS_ASSERT(params.m_ulBandwidth == params.m_dlBandwidth);
509 m_bandwidth = params.m_dlBandwidth;
510
512 cnf.m_result = NrMacCschedSapUser::Result_e::SUCCESS;
513 m_macCschedSapUser->CschedUeConfigCnf(cnf);
514}
515
525void
528{
529 NS_LOG_FUNCTION(this << " RNTI " << params.m_rnti << " txMode "
530 << static_cast<uint32_t>(params.m_transmissionMode));
531
532 auto itUe = m_ueMap.find(params.m_rnti);
533 GetSecond UeInfoOf;
534 if (itUe == m_ueMap.end())
535 {
536 itUe = m_ueMap.insert(std::make_pair(params.m_rnti, CreateUeRepresentation(params))).first;
537
538 UeInfoOf(*itUe)->m_dlHarq.SetMaxSize(
539 static_cast<uint8_t>(m_macSchedSapUser->GetNumHarqProcess()));
540 UeInfoOf(*itUe)->m_ulHarq.SetMaxSize(
541 static_cast<uint8_t>(m_macSchedSapUser->GetNumHarqProcess()));
542 UeInfoOf(*itUe)->m_dlMcs = m_startMcsDl;
543 UeInfoOf(*itUe)->m_startMcsDlUe = m_startMcsDl;
544 UeInfoOf(*itUe)->m_ulMcs = m_startMcsUl;
545
546 NrMacSchedulerSrs::SrsPeriodicityAndOffset srs = m_schedulerSrs->AddUe();
547
548 if (!srs.m_isValid)
549 {
550 bool ret = m_schedulerSrs->IncreasePeriodicity(
551 &m_ueMap); // The new UE will get the SRS offset/periodicity here
552 NS_ASSERT(ret);
553 }
554 else
555 {
556 UeInfoOf(*itUe)->m_srsPeriodicity =
557 srs.m_periodicity; // set the periodicity/offset based on the return value
558 UeInfoOf(*itUe)->m_srsOffset = srs.m_offset;
559 }
560
561 NS_LOG_INFO("Creating user, beam " << params.m_beamId << " and ue " << params.m_rnti
562 << " assigned SRS periodicity " << srs.m_periodicity
563 << " and offset " << srs.m_offset);
564 }
565 else
566 {
567 NS_LOG_LOGIC("Updating Beam for UE " << params.m_rnti << " beam " << params.m_beamId);
568 UeInfoOf(*itUe)->m_beamId = params.m_beamId;
569 }
570}
571
579void
582{
583 NS_LOG_FUNCTION(this << " Release RNTI " << params.m_rnti);
584
585 auto itUe = m_ueMap.find(params.m_rnti);
586 NS_ABORT_IF(itUe == m_ueMap.end());
587
588 m_schedulerSrs->RemoveUe(itUe->second->m_srsOffset);
589 m_ueMap.erase(itUe);
590
591 // When it will be the case of reducing the periodicity? Question for the
592 // future...
593
594 NS_LOG_INFO("Release RNTI " << params.m_rnti);
595}
596
597uint64_t
602
613LCPtr
615{
616 NS_LOG_FUNCTION(this);
617 return std::make_unique<NrMacSchedulerLC>(config);
618}
619
630LCGPtr
632{
633 NS_LOG_FUNCTION(this);
634 return std::make_unique<NrMacSchedulerLCG>(config.m_logicalChannelGroup);
635}
636
650void
653{
654 NS_LOG_FUNCTION(this << static_cast<uint32_t>(params.m_rnti));
655 auto itUe = m_ueMap.find(params.m_rnti);
656 GetSecond UeInfoOf;
657 NS_ABORT_IF(itUe == m_ueMap.end());
658
659 for (const auto& lcConfig : params.m_logicalChannelConfigList)
660 {
661 if (lcConfig.m_direction == nr::LogicalChannelConfigListElement_s::DIR_DL ||
662 lcConfig.m_direction == nr::LogicalChannelConfigListElement_s::DIR_BOTH)
663 {
664 auto itDl = UeInfoOf(*itUe)->m_dlLCG.find(lcConfig.m_logicalChannelGroup);
665 auto itDlEnd = UeInfoOf(*itUe)->m_dlLCG.end();
666 if (itDl == itDlEnd)
667 {
668 NS_LOG_DEBUG("Created DL LCG for UE "
669 << UeInfoOf(*itUe)->m_rnti
670 << " ID=" << static_cast<uint32_t>(lcConfig.m_logicalChannelGroup));
671 std::unique_ptr<NrMacSchedulerLCG> lcg = CreateLCG(lcConfig);
672 itDl = UeInfoOf(*itUe)
673 ->m_dlLCG.emplace(lcConfig.m_logicalChannelGroup, std::move(lcg))
674 .first;
675 }
676
677 itDl->second->Insert(CreateLC(lcConfig));
678 NS_LOG_DEBUG("Created DL LC for UE "
679 << UeInfoOf(*itUe)->m_rnti
680 << " ID=" << static_cast<uint32_t>(lcConfig.m_logicalChannelIdentity)
681 << " in LCG " << static_cast<uint32_t>(lcConfig.m_logicalChannelGroup));
682 }
683 if (lcConfig.m_direction == nr::LogicalChannelConfigListElement_s::DIR_UL ||
684 lcConfig.m_direction == nr::LogicalChannelConfigListElement_s::DIR_BOTH)
685 {
686 auto itUl = UeInfoOf(*itUe)->m_ulLCG.find(lcConfig.m_logicalChannelGroup);
687 auto itUlEnd = UeInfoOf(*itUe)->m_ulLCG.end();
688 if (itUl == itUlEnd)
689 {
690 NS_LOG_DEBUG("Created UL LCG for UE "
691 << UeInfoOf(*itUe)->m_rnti
692 << " ID=" << static_cast<uint32_t>(lcConfig.m_logicalChannelGroup));
693 std::unique_ptr<NrMacSchedulerLCG> lcg = CreateLCG(lcConfig);
694 itUl = UeInfoOf(*itUe)
695 ->m_ulLCG.emplace(lcConfig.m_logicalChannelGroup, std::move(lcg))
696 .first;
697 }
698
699 // Create a LC ID only if it is the first. For detail, see documentation
700 // of NrMacSchedulerLCG.
701 if (itUl->second->NumOfLC() == 0)
702 {
703 itUl->second->Insert(CreateLC(lcConfig));
704 NS_LOG_DEBUG("Created UL LC for UE "
705 << UeInfoOf(*itUe)->m_rnti
706 << " ID=" << static_cast<uint32_t>(lcConfig.m_logicalChannelIdentity)
707 << " in LCG "
708 << static_cast<uint32_t>(lcConfig.m_logicalChannelGroup));
709 }
710 }
711 }
712}
713
718void
721{
722 NS_LOG_FUNCTION(this);
723
724 for (const auto& lcId : params.m_logicalChannelIdentity)
725 {
726 auto itUe = m_ueMap.find(params.m_rnti);
727 NS_ABORT_IF(itUe == m_ueMap.end());
728 itUe->second->ReleaseLC(lcId);
729 }
730}
731
740void
743{
744 NS_LOG_FUNCTION(this << params.m_rnti
745 << static_cast<uint32_t>(params.m_logicalChannelIdentity));
746
747 GetSecond UeInfoOf;
748 auto itUe = m_ueMap.find(params.m_rnti);
749 NS_ABORT_IF(itUe == m_ueMap.end());
750
751 for (const auto& lcg : UeInfoOf(*itUe)->m_dlLCG)
752 {
753 if (lcg.second->Contains(params.m_logicalChannelIdentity))
754 {
755 NS_LOG_INFO("Updating DL LC Info: " << params
756 << " in LCG: " << static_cast<uint32_t>(lcg.first));
757 lcg.second->UpdateInfo(params);
758 return;
759 }
760 }
761 // Fail miserabily because we didn't found any LC
762 NS_FATAL_ERROR("The LC does not exist. Can't update");
763}
764
774void
775NrMacSchedulerNs3::BSRReceivedFromUe(const MacCeElement& bsr)
776{
777 NS_LOG_FUNCTION(this);
778 NS_ASSERT(bsr.m_macCeType == MacCeElement::BSR);
779 GetSecond UeInfoOf;
780 auto itUe = m_ueMap.find(bsr.m_rnti);
781 NS_ABORT_IF(itUe == m_ueMap.end());
782
783 // The UE only notifies the buf size as sum of all components.
784 // see nr-ue-mac.cc:395
785 for (uint8_t lcg = 0; lcg < 4; ++lcg)
786 {
787 uint8_t bsrId = bsr.m_macCeValue.m_bufferStatus.at(lcg);
788 uint32_t bufSize = NrMacShortBsrCe::FromLevelToBytes(bsrId);
789
790 auto itLcg = UeInfoOf(*itUe)->m_ulLCG.find(lcg);
791 if (itLcg == UeInfoOf(*itUe)->m_ulLCG.end())
792 {
793 // NS_ABORT_MSG_IF(bufSize > 0,
794 // "LCG " << static_cast<uint32_t>(lcg) << " not found for UE "
795 // << itUe->second->m_rnti);
796 NS_LOG_DEBUG("BSR does not match an established lcg");
797 continue;
798 }
799
800 if (itLcg->second->GetTotalSize() > 0 || bufSize > 0)
801 {
802 NS_LOG_INFO("Updating UL LCG " << static_cast<uint32_t>(lcg) << " for UE " << bsr.m_rnti
803 << " size " << bufSize);
804 }
805
806 itLcg->second->UpdateInfo(bufSize);
807 }
808}
809
817void
820{
821 NS_LOG_FUNCTION(this);
822
823 for (const auto& element : params.m_macCeList)
824 {
825 if (element.m_macCeType == MacCeElement::BSR)
826 {
827 BSRReceivedFromUe(element);
828 }
829 else
830 {
831 NS_LOG_INFO("Ignoring received CTRL message because it's not a BSR");
832 }
833 }
834}
835
846void
849{
850 NS_LOG_FUNCTION(this);
851
852 if (m_fixedMcsDl)
853 {
854 return;
855 }
856
857 NS_ASSERT(m_cqiTimersThreshold >= m_macSchedSapUser->GetSlotPeriod());
858
859 uint32_t expirationTime =
860 static_cast<uint32_t>(m_cqiTimersThreshold.GetNanoSeconds() /
861 m_macSchedSapUser->GetSlotPeriod().GetNanoSeconds());
862
863 for (const auto& cqi : params.m_cqiList)
864 {
865 NS_ASSERT(m_ueMap.find(cqi.m_rnti) != m_ueMap.end());
866 const std::shared_ptr<NrMacSchedulerUeInfo>& ue = m_ueMap.find(cqi.m_rnti)->second;
867
868 if (cqi.m_cqiType == DlCqiInfo::WB)
869 {
870 m_cqiManagement.DlWBCQIReported(cqi, ue, expirationTime, m_maxDlMcs);
871 }
872 else
873 {
874 m_cqiManagement.DlSBCQIReported(cqi, ue);
875 }
876 }
877}
878
892void
895{
896 NS_LOG_FUNCTION(this);
897
898 if (m_fixedMcsUl)
899 {
900 return;
901 }
902
903 GetSecond UeInfoOf;
904
905 uint32_t expirationTime =
906 static_cast<uint32_t>(m_cqiTimersThreshold.GetNanoSeconds() /
907 m_macSchedSapUser->GetSlotPeriod().GetNanoSeconds());
908
909 switch (params.m_ulCqi.m_type)
910 {
911 case UlCqiInfo::PUSCH: {
912 [[maybe_unused]] bool found = false;
913 uint8_t symStart = params.m_symStart;
914 SfnSf ulSfnSf = params.m_sfnSf;
915
916 NS_LOG_INFO("CQI for allocation: " << params.m_sfnSf << " started at sym: " << +symStart
917 << " modified allocation " << ulSfnSf << " sym Start "
918 << static_cast<uint32_t>(symStart));
919
920 auto itAlloc = m_ulAllocationMap.find(ulSfnSf.GetEncoding());
921 NS_ASSERT_MSG(itAlloc != m_ulAllocationMap.end(), "Can't find allocation for " << ulSfnSf);
922 std::vector<AllocElem>& ulAllocations = itAlloc->second.m_ulAllocations;
923
924 for (auto it = ulAllocations.cbegin(); it != ulAllocations.cend(); /* NO INC */)
925 {
926 const AllocElem& allocation = *(it);
927 if (allocation.m_symStart == symStart)
928 {
929 auto itUe = m_ueMap.find(allocation.m_rnti);
930 NS_ASSERT(itUe != m_ueMap.end());
931 NS_ASSERT(allocation.m_numSym > 0);
932 NS_ASSERT(allocation.m_tbs > 0);
933
934 m_cqiManagement.UlSBCQIReported(expirationTime,
935 allocation.m_tbs,
936 params,
937 UeInfoOf(*itUe),
938 allocation.m_rbgMask,
941 found = true;
942 it = ulAllocations.erase(it);
943 }
944 else
945 {
946 ++it;
947 }
948 }
949 NS_ASSERT(found);
950
951 if (ulAllocations.empty())
952 {
953 // remove obsolete info on allocation; we already processed all the CQI
954 NS_LOG_INFO("Removing allocation for " << ulSfnSf);
955 m_ulAllocationMap.erase(itAlloc);
956 }
957 }
958 break;
959 default:
960 NS_FATAL_ERROR("Unknown type of UL-CQI");
961 }
962}
963
977template <typename T>
978std::vector<T>
979NrMacSchedulerNs3::MergeHARQ(std::vector<T>* existingFeedbacks,
980 const std::vector<T>& inFeedbacks,
981 const std::string& mode) const
982{
983 NS_LOG_FUNCTION(this);
984 NS_LOG_INFO("To retransmit : " << existingFeedbacks->size() << " " << mode << " HARQ, received "
985 << inFeedbacks.size() << " " << mode << " HARQ Feedback");
986 uint64_t existingSize = existingFeedbacks->size();
987 uint64_t inSize = inFeedbacks.size();
988 existingFeedbacks->insert(existingFeedbacks->end(), inFeedbacks.begin(), inFeedbacks.end());
989 NS_ASSERT(existingFeedbacks->size() == existingSize + inSize);
990
991 auto ret = std::vector<T>(std::make_move_iterator(existingFeedbacks->begin()),
992 std::make_move_iterator(existingFeedbacks->end()));
993 existingFeedbacks->clear();
994
995 return ret;
996}
997
1015template <typename T>
1016void
1017NrMacSchedulerNs3::ProcessHARQFeedbacks(
1018 std::vector<T>* harqInfo,
1019 const NrMacSchedulerUeInfo::GetHarqVectorFn& GetHarqVectorFn,
1020 const std::string& direction) const
1021{
1022 NS_LOG_FUNCTION(this);
1023 uint32_t nackReceived = 0;
1024
1025 // Check the HARQ feedback, erase ACKed, updated NACKed
1026 for (auto harqFeedbackIt = harqInfo->begin(); harqFeedbackIt != harqInfo->end();
1027 /* nothing as increment */)
1028 {
1029 uint8_t harqId = harqFeedbackIt->m_harqProcessId;
1030 uint16_t rnti = harqFeedbackIt->m_rnti;
1031 NrMacHarqVector& ueHarqVector = GetHarqVectorFn(m_ueMap.find(rnti)->second);
1032 HarqProcess& ueProcess = ueHarqVector.Get(harqId);
1033
1034 NS_LOG_INFO("Evaluating feedback: " << *harqFeedbackIt);
1035 if (!ueProcess.m_active)
1036 {
1037 NS_LOG_INFO("UE " << rnti << " HARQ vector: " << ueHarqVector);
1038 NS_FATAL_ERROR("Received feedback for a process which is not active");
1039 }
1040 NS_ABORT_IF(ueProcess.m_dciElement == nullptr);
1041
1042 // RV number should not be greater than 3
1043 NS_ASSERT(ueProcess.m_dciElement->m_rv < 4);
1044 uint8_t maxHarqReTx = m_enableHarqReTx ? 3 : 0;
1045
1046 if (harqFeedbackIt->IsReceivedOk() || ueProcess.m_dciElement->m_rv == maxHarqReTx)
1047 {
1048 ueHarqVector.Erase(harqId);
1049 harqFeedbackIt = harqInfo->erase(harqFeedbackIt);
1050 NS_LOG_INFO("Erased processID " << static_cast<uint32_t>(harqId) << " of UE " << rnti
1051 << " direction " << direction);
1052 }
1053 else if (!harqFeedbackIt->IsReceivedOk())
1054 {
1055 ueProcess.m_status = HarqProcess::RECEIVED_FEEDBACK;
1056 nackReceived++;
1057 ++harqFeedbackIt;
1058 NS_LOG_INFO("NACK received for UE " << static_cast<uint32_t>(rnti) << " process "
1059 << static_cast<uint32_t>(harqId) << " direction "
1060 << direction);
1061 }
1062 }
1063
1064 NS_ASSERT(harqInfo->size() == nackReceived);
1065}
1066
1078void
1079NrMacSchedulerNs3::ResetExpiredHARQ(uint16_t rnti, NrMacHarqVector* harq)
1080{
1081 NS_LOG_FUNCTION(this << harq);
1082
1083 for (auto harqIt = harq->Begin(); harqIt != harq->End(); ++harqIt)
1084 {
1085 HarqProcess& process = harqIt->second;
1086 uint8_t processId = harqIt->first;
1087
1088 if (process.m_status == HarqProcess::INACTIVE)
1089 {
1090 continue;
1091 }
1092
1093 if (process.m_timer < m_macSchedSapUser->GetNumHarqProcess())
1094 {
1095 ++process.m_timer;
1096 NS_LOG_INFO("Updated process for UE " << rnti << " number "
1097 << static_cast<uint32_t>(processId)
1098 << ", resulting process: " << process);
1099 }
1100 else
1101 {
1102 harq->Erase(processId);
1103 NS_LOG_INFO("Erased process for UE " << rnti << " number "
1104 << static_cast<uint32_t>(processId)
1105 << " for time limits");
1106 }
1107 }
1108}
1109
1118uint8_t
1119NrMacSchedulerNs3::PrependCtrlSym(uint8_t symStart,
1120 uint8_t numSymToAllocate,
1122 std::deque<VarTtiAllocInfo>* allocations) const
1123{
1124 std::vector<uint8_t> rbgBitmask(GetBandwidthInRbg(), 1);
1125
1126 NS_ASSERT_MSG(rbgBitmask.size() == GetBandwidthInRbg(),
1127 "bitmask size " << rbgBitmask.size() << " conf " << GetBandwidthInRbg());
1128 if (mode == DciInfoElementTdma::DL)
1129 {
1130 NS_ASSERT(allocations->empty()); // no previous allocations
1131 NS_ASSERT(symStart == 0); // start from the symbol 0
1132 }
1133
1134 for (uint8_t sym = symStart; sym < symStart + numSymToAllocate; ++sym)
1135 {
1136 allocations->emplace_front(std::make_shared<DciInfoElementTdma>(sym,
1137 1,
1138 mode,
1140 rbgBitmask));
1141 NS_LOG_INFO("Allocating CTRL symbol, type"
1142 << mode << " in TDMA. numSym=1, symStart=" << static_cast<uint32_t>(sym)
1143 << " Remaining CTRL sym to allocate: " << sym - symStart);
1144 }
1145 return symStart + numSymToAllocate;
1146}
1147
1156uint8_t
1157NrMacSchedulerNs3::AppendCtrlSym(uint8_t symStart,
1158 uint8_t numSymToAllocate,
1160 std::deque<VarTtiAllocInfo>* allocations) const
1161{
1162 std::vector<uint8_t> rbgBitmask(GetBandwidthInRbg(), 1);
1163
1164 NS_ASSERT(rbgBitmask.size() == GetBandwidthInRbg());
1165 if (mode == DciInfoElementTdma::DL)
1166 {
1167 NS_ASSERT(allocations->empty()); // no previous allocations
1168 NS_ASSERT(symStart == 0); // start from the symbol 0
1169 }
1170
1171 for (uint8_t sym = symStart; sym < symStart + numSymToAllocate; ++sym)
1172 {
1173 allocations->emplace_back(std::make_shared<DciInfoElementTdma>(sym,
1174 1,
1175 mode,
1177 rbgBitmask));
1178 NS_LOG_INFO("Allocating CTRL symbol, type"
1179 << mode << " in TDMA. numSym=1, symStart=" << static_cast<uint32_t>(sym)
1180 << " Remaining CTRL sym to allocate: " << sym - symStart);
1181 }
1182 return symStart + numSymToAllocate;
1183}
1184
1195void
1196NrMacSchedulerNs3::ComputeActiveHarq(ActiveHarqMap* activeDlHarq,
1197 const std::vector<DlHarqInfo>& dlHarqFeedback) const
1198{
1199 NS_LOG_FUNCTION(this);
1200 NS_ASSERT(activeDlHarq->empty());
1201
1202 for (const auto& feedback : dlHarqFeedback)
1203 {
1204 uint16_t rnti = feedback.m_rnti;
1205 auto& schedInfo = m_ueMap.find(rnti)->second;
1206 auto beamIterator = activeDlHarq->find(schedInfo->m_beamId);
1207
1208 if (beamIterator == activeDlHarq->end())
1209 {
1210 std::vector<NrMacHarqVector::iterator> harqVector;
1211 NS_ASSERT(schedInfo->m_dlHarq.Find(feedback.m_harqProcessId)->second.m_active);
1212
1213 harqVector.emplace_back(schedInfo->m_dlHarq.Find(feedback.m_harqProcessId));
1214 activeDlHarq->emplace(std::make_pair(schedInfo->m_beamId, harqVector));
1215 }
1216 else
1217 {
1218 NS_ASSERT(schedInfo->m_dlHarq.Find(feedback.m_harqProcessId)->second.m_active);
1219 beamIterator->second.emplace_back(schedInfo->m_dlHarq.Find(feedback.m_harqProcessId));
1220 }
1221 NS_LOG_INFO("Received feedback for UE " << rnti << " ID "
1222 << static_cast<uint32_t>(feedback.m_harqProcessId)
1223 << " marked as active");
1224 NS_ASSERT(schedInfo->m_dlHarq.Find(feedback.m_harqProcessId)->second.m_status ==
1226 }
1227
1228 SortDlHarq(activeDlHarq);
1229}
1230
1241void
1242NrMacSchedulerNs3::ComputeActiveHarq(ActiveHarqMap* activeUlHarq,
1243 const std::vector<UlHarqInfo>& ulHarqFeedback) const
1244{
1245 NS_LOG_FUNCTION(this);
1246
1247 for (const auto& feedback : ulHarqFeedback)
1248 {
1249 uint16_t rnti = feedback.m_rnti;
1250 auto& schedInfo = m_ueMap.find(rnti)->second;
1251 auto beamIterator = activeUlHarq->find(schedInfo->m_beamId);
1252
1253 if (beamIterator == activeUlHarq->end())
1254 {
1255 std::vector<NrMacHarqVector::iterator> harqVector;
1256 NS_ASSERT(schedInfo->m_ulHarq.Find(feedback.m_harqProcessId)->second.m_active);
1257 harqVector.emplace_back(schedInfo->m_ulHarq.Find(feedback.m_harqProcessId));
1258 activeUlHarq->emplace(std::make_pair(schedInfo->m_beamId, harqVector));
1259 }
1260 else
1261 {
1262 NS_ASSERT(schedInfo->m_ulHarq.Find(feedback.m_harqProcessId)->second.m_active);
1263 beamIterator->second.emplace_back(schedInfo->m_ulHarq.Find(feedback.m_harqProcessId));
1264 }
1265 }
1266 SortUlHarq(activeUlHarq);
1267}
1268
1281void
1282NrMacSchedulerNs3::ComputeActiveUe(ActiveUeMap* activeUe,
1283 const NrMacSchedulerUeInfo::GetLCGFn& GetLCGFn,
1284 const NrMacSchedulerUeInfo::GetHarqVectorFn& GetHarqVector,
1285 const std::string& mode) const
1286{
1287 NS_LOG_FUNCTION(this);
1288 for (const auto& ueInfo : m_ueMap)
1289 {
1290 uint32_t totBuffer = 0;
1291 const auto& ue = ueInfo.second;
1292
1293 // compute total DL and UL bytes buffered
1294 for (const auto& lcgInfo : GetLCGFn(ue))
1295 {
1296 const auto& lcg = lcgInfo.second;
1297 if (lcg->GetTotalSize() > 0)
1298 {
1299 NS_LOG_INFO("UE " << ue->m_rnti << " " << mode << " LCG "
1300 << static_cast<uint32_t>(lcgInfo.first) << " bytes "
1301 << lcg->GetTotalSize());
1302 }
1303 totBuffer += lcg->GetTotalSize();
1304 }
1305
1306 auto harqV = GetHarqVector(ue);
1307
1308 if (totBuffer > 0 && harqV.CanInsert())
1309 {
1310 auto it = activeUe->find(ue->m_beamId);
1311 if (it == activeUe->end())
1312 {
1313 std::vector<std::pair<std::shared_ptr<NrMacSchedulerUeInfo>, uint32_t>> tmp;
1314 tmp.emplace_back(ue, totBuffer);
1315 activeUe->insert(std::make_pair(ue->m_beamId, tmp));
1316 }
1317 else
1318 {
1319 it->second.emplace_back(ue, totBuffer);
1320 }
1321 }
1322 }
1323}
1324
1356uint8_t
1357NrMacSchedulerNs3::DoScheduleDlData(PointInFTPlane* spoint,
1358 uint32_t symAvail,
1359 const ActiveUeMap& activeDl,
1360 SlotAllocInfo* slotAlloc) const
1361{
1362 NS_LOG_FUNCTION(this << symAvail);
1363 NS_ASSERT(spoint->m_rbg == 0);
1364 BeamSymbolMap symPerBeam = AssignDLRBG(symAvail, activeDl);
1365 GetFirst GetBeam;
1366 uint8_t usedSym = 0;
1367
1368 for (const auto& beam : activeDl)
1369 {
1370 uint32_t availableRBG =
1371 (GetBandwidthInRbg() - spoint->m_rbg) * symPerBeam.at(GetBeam(beam));
1372 bool assigned = false;
1373 std::unordered_set<uint8_t> symbStartDci;
1374 // allocSym is used to count the number of allocated symbols to the UEs of the beam
1375 // we are iterating over
1376 uint32_t allocSym = 0;
1377
1378 NS_LOG_DEBUG(activeDl.size()
1379 << " active DL beam, this beam has " << symPerBeam.at(GetBeam(beam))
1380 << " SYM, starts from RB " << spoint->m_rbg << " and symbol "
1381 << static_cast<uint32_t>(spoint->m_sym) << " for a total of " << availableRBG
1382 << " RBG. In one symbol we have " << GetBandwidthInRbg() << " RBG.");
1383
1384 if (symPerBeam.at(GetBeam(beam)) == 0)
1385 {
1386 NS_LOG_INFO("No available symbols for this beam, continue");
1387 continue;
1388 }
1389
1390 for (const auto& ue : beam.second)
1391 {
1392 if (ue.first->m_dlRBG == 0)
1393 {
1394 NS_LOG_INFO("UE " << ue.first->m_rnti << " does not have RBG assigned");
1395 continue;
1396 }
1397
1398 std::shared_ptr<DciInfoElementTdma> dci =
1399 CreateDlDci(spoint, ue.first, symPerBeam.at(GetBeam(beam)));
1400 if (dci == nullptr)
1401 {
1402 // By continuing to the next UE means that we are
1403 // wasting a resource assign to this UE. For a TDMA
1404 // scheduler this resource would be one or more
1405 // symbols, and for OFDMA scheduler it would be a
1406 // chunk of time + freq, i.e., one or more
1407 // symbols in time and one or more RBG in freq.
1408 // TODO To avoid this, a more accurate solution
1409 // is needed to assign resources. That is, a solution
1410 // that would not assign resources to a UE if the assigned resources
1411 // result a TB size of less than 7 bytes (3 mac header, 2 rlc header, 2 data).
1412 // Because if this happens CreateDlDci will not create DCI.
1413 NS_LOG_DEBUG("No DCI has been created, ignoring");
1414 ue.first->ResetDlMetric();
1415 continue;
1416 }
1417
1418 assigned = true;
1419
1420 if (symbStartDci.insert(dci->m_symStart).second)
1421 {
1422 allocSym += dci->m_numSym;
1423 }
1424
1425 NS_LOG_INFO("UE " << ue.first->m_rnti << " has " << ue.first->m_dlRBG
1426 << " RBG assigned");
1427 NS_ASSERT_MSG(dci->m_symStart + dci->m_numSym <= m_macSchedSapUser->GetSymbolsPerSlot(),
1428 "symStart: "
1429 << static_cast<uint32_t>(dci->m_symStart)
1430 << " symEnd: " << static_cast<uint32_t>(dci->m_numSym) << " symbols: "
1431 << static_cast<uint32_t>(m_macSchedSapUser->GetSymbolsPerSlot()));
1432
1433 HarqProcess harqProcess(true, HarqProcess::WAITING_FEEDBACK, 0, dci);
1434 uint8_t id;
1435
1436 if (!ue.first->m_dlHarq.CanInsert())
1437 {
1438 NS_LOG_INFO("Harq Vector condition for UE " << ue.first->m_rnti << std::endl
1439 << ue.first->m_dlHarq);
1440 NS_FATAL_ERROR("UE " << ue.first->m_rnti << " does not have DL HARQ space");
1441 }
1442
1443 ue.first->m_dlHarq.Insert(&id, harqProcess);
1444 ue.first->m_dlHarq.Get(id).m_dciElement->m_harqProcess = id;
1445
1446 // distribute tbsize among the LCs of the UE
1447 // distributedBytes size is equal to the number of LCs
1448 auto distributedBytes =
1449 m_schedLc->AssignBytesToDlLC(ue.first->m_dlLCG,
1450 dci->m_tbSize,
1452
1453 std::vector<std::vector<NrMacSchedulerLcAlgorithm::Assignation>> bytesPerLc(
1454 distributedBytes.size());
1455
1456 for (std::size_t numLc = 0; numLc < distributedBytes.size(); numLc++)
1457 {
1458 bytesPerLc.at(numLc).emplace_back(distributedBytes.at(numLc).m_lcg,
1459 distributedBytes.at(numLc).m_lcId,
1460 distributedBytes.at(numLc).m_bytes);
1461 }
1462
1463 VarTtiAllocInfo slotInfo(dci);
1464
1465 NS_LOG_INFO("Assigned process ID " << static_cast<uint32_t>(dci->m_harqProcess)
1466 << " to UE " << ue.first->m_rnti);
1467 NS_LOG_DEBUG(" UE" << dci->m_rnti << " gets DL symbols "
1468 << static_cast<uint32_t>(dci->m_symStart) << "-"
1469 << static_cast<uint32_t>(dci->m_symStart + dci->m_numSym) << " tbs "
1470 << dci->m_tbSize << " mcs " << static_cast<uint32_t>(dci->m_mcs)
1471 << " harqId " << static_cast<uint32_t>(id) << " rv "
1472 << static_cast<uint32_t>(dci->m_rv));
1473
1474 for (const auto& byteDistribution : distributedBytes)
1475 {
1476 NS_ASSERT(byteDistribution.m_bytes >= 3);
1477 uint8_t lcId = byteDistribution.m_lcId;
1478 uint8_t lcgId = byteDistribution.m_lcg;
1479 uint32_t bytes = byteDistribution.m_bytes - 3; // Consider the subPdu overhead
1480
1481 RlcPduInfo newRlcPdu(lcId, bytes);
1482 HarqProcess& process = ue.first->m_dlHarq.Get(dci->m_harqProcess);
1483
1484 slotInfo.m_rlcPduInfo.push_back(newRlcPdu);
1485 process.m_rlcPduInfo.push_back(newRlcPdu);
1486
1487 ue.first->m_dlLCG.at(lcgId)->AssignedData(lcId, bytes, "DL");
1488
1489 NS_LOG_DEBUG("DL LCG " << static_cast<uint32_t>(lcgId) << " LCID "
1490 << static_cast<uint32_t>(lcId) << " got bytes "
1491 << newRlcPdu.m_size);
1492 }
1493
1494 NS_ABORT_IF(slotInfo.m_rlcPduInfo.empty());
1495
1496 slotAlloc->m_varTtiAllocInfo.emplace_back(slotInfo);
1497 }
1498 if (assigned)
1499 {
1500 ChangeDlBeam(spoint, symPerBeam.at(GetBeam(beam)));
1501 usedSym += allocSym;
1502 slotAlloc->m_numSymAlloc += allocSym;
1503 }
1504 }
1505
1506 for (auto& beam : activeDl)
1507 {
1508 for (auto& ue : beam.second)
1509 {
1510 ue.first->ResetDlSchedInfo();
1511 }
1512 }
1513
1514 NS_ASSERT(spoint->m_rbg == 0);
1515
1516 return usedSym;
1517}
1518
1554uint8_t
1555NrMacSchedulerNs3::DoScheduleUlData(PointInFTPlane* spoint,
1556 uint32_t symAvail,
1557 const ActiveUeMap& activeUl,
1558 SlotAllocInfo* slotAlloc) const
1559{
1560 NS_LOG_FUNCTION(this);
1561 NS_ASSERT(symAvail > 0 && !activeUl.empty());
1562 NS_ASSERT(spoint->m_rbg == 0);
1563
1564 BeamSymbolMap symPerBeam = AssignULRBG(symAvail, activeUl);
1565 uint8_t usedSym = 0;
1566 GetFirst GetBeam;
1567
1568 for (const auto& beam : activeUl)
1569 {
1570 uint32_t availableRBG =
1571 (GetBandwidthInRbg() - spoint->m_rbg) * symPerBeam.at(GetBeam(beam));
1572 bool assigned = false;
1573 std::unordered_set<uint8_t> symbStartDci;
1574 // allocSym is used to count the number of allocated symbols to the UEs of the beam
1575 // we are iterating over
1576 uint32_t allocSym = 0;
1577
1578 NS_LOG_DEBUG(activeUl.size()
1579 << " active UL beam, this beam has " << symPerBeam.at(GetBeam(beam))
1580 << " SYM, starts from RBG " << spoint->m_rbg << " and symbol "
1581 << static_cast<uint32_t>(spoint->m_sym) << " (going backward) for a total of "
1582 << availableRBG << " RBG. In one symbol we have " << GetBandwidthInRbg()
1583 << " RBG.");
1584
1585 if (symPerBeam.at(GetBeam(beam)) == 0)
1586 {
1587 NS_LOG_INFO("No available symbols for this beam, continue");
1588 continue;
1589 }
1590
1591 for (const auto& ue : beam.second)
1592 {
1593 if (ue.first->m_ulRBG == 0)
1594 {
1595 NS_LOG_INFO("UE " << ue.first->m_rnti << " does not have RBG assigned");
1596 continue;
1597 }
1598
1599 std::shared_ptr<DciInfoElementTdma> dci =
1600 CreateUlDci(spoint, ue.first, symPerBeam.at(GetBeam(beam)));
1601
1602 if (dci == nullptr)
1603 {
1604 // By continuing to the next UE means that we are
1605 // wasting a resource assign to this UE. For a TDMA
1606 // scheduler this resource would be one or more
1607 // symbols, and for OFDMA scheduler it would be a
1608 // chunk of time + freq, i.e., one or more
1609 // symbols in time and one or more RBG in freq.
1610 // TODO To avoid this, a more accurate solution
1611 // is needed to assign resources. That is, a solution
1612 // that would not assign resources to a UE if the assigned resources
1613 // result a TB size of less than 7 bytes (3 mac header, 2 rlc header, 2
1614 // data). Because if this happens CreateUlDci will not create DCI.
1615 NS_LOG_DEBUG("No DCI has been created, ignoring");
1616 ue.first->ResetUlMetric();
1617 continue;
1618 }
1619
1620 assigned = true;
1621
1622 if (symbStartDci.insert(dci->m_symStart).second)
1623 {
1624 allocSym += dci->m_numSym;
1625 }
1626
1627 if (!ue.first->m_ulHarq.CanInsert())
1628 {
1629 NS_LOG_INFO("Harq Vector condition for UE " << ue.first->m_rnti << std::endl
1630 << ue.first->m_ulHarq);
1631 NS_FATAL_ERROR("UE " << ue.first->m_rnti << " does not have UL HARQ space");
1632 }
1633
1634 HarqProcess harqProcess(true, HarqProcess::WAITING_FEEDBACK, 0, dci);
1635 uint8_t id;
1636 ue.first->m_ulHarq.Insert(&id, harqProcess);
1637
1638 ue.first->m_ulHarq.Get(id).m_dciElement->m_harqProcess = id;
1639
1640 VarTtiAllocInfo slotInfo(dci);
1641
1642 NS_LOG_INFO("Assigned process ID " << static_cast<uint32_t>(dci->m_harqProcess)
1643 << " to UE " << ue.first->m_rnti);
1644 NS_LOG_DEBUG(" UE" << dci->m_rnti << " gets UL symbols "
1645 << static_cast<uint32_t>(dci->m_symStart) << "-"
1646 << static_cast<uint32_t>(dci->m_symStart + dci->m_numSym) << " tbs "
1647 << dci->m_tbSize << " mcs " << static_cast<uint32_t>(dci->m_mcs)
1648 << " harqId " << static_cast<uint32_t>(id) << " rv "
1649 << static_cast<uint32_t>(dci->m_rv));
1650
1651 auto distributedBytes = m_schedLc->AssignBytesToUlLC(ue.first->m_ulLCG, dci->m_tbSize);
1652 bool assignedToLC = false;
1653 for (const auto& byteDistribution : distributedBytes)
1654 {
1655 assignedToLC = true;
1656 ue.first->m_ulLCG.at(byteDistribution.m_lcg)
1657 ->AssignedData(byteDistribution.m_lcId, byteDistribution.m_bytes, "UL");
1658 NS_LOG_DEBUG("UL LCG " << static_cast<uint32_t>(byteDistribution.m_lcg)
1659 << " assigned bytes " << byteDistribution.m_bytes
1660 << " to LCID "
1661 << static_cast<uint32_t>(byteDistribution.m_lcId));
1662 }
1663 NS_ASSERT(assignedToLC);
1664 slotAlloc->m_varTtiAllocInfo.emplace_front(slotInfo);
1665 }
1666 if (assigned)
1667 {
1668 ChangeUlBeam(spoint, symPerBeam.at(GetBeam(beam)));
1669 usedSym += allocSym;
1670 slotAlloc->m_numSymAlloc += allocSym;
1671 }
1672 }
1673
1674 for (auto& beam : activeUl)
1675 {
1676 for (auto& ue : beam.second)
1677 {
1678 ue.first->ResetUlSchedInfo();
1679 }
1680 }
1681
1682 NS_ASSERT(spoint->m_rbg == 0);
1683
1684 return usedSym;
1685}
1686
1698void
1699NrMacSchedulerNs3::DoScheduleUlSr(PointInFTPlane* spoint, const std::list<uint16_t>& rntiList) const
1700{
1701 NS_LOG_FUNCTION(this);
1702 NS_ASSERT(spoint->m_rbg == 0);
1703
1704 for (const auto& v : rntiList)
1705 {
1706 for (auto& ulLcg : NrMacSchedulerUeInfo::GetUlLCG(m_ueMap.at(v)))
1707 {
1708 NS_LOG_DEBUG("Assigning 12 bytes to UE " << v << " because of a SR");
1709 ulLcg.second->UpdateInfo(12);
1710 }
1711 }
1712}
1713
1752void
1753NrMacSchedulerNs3::ScheduleDl(const NrMacSchedSapProvider::SchedDlTriggerReqParameters& params,
1754 const std::vector<DlHarqInfo>& dlHarqFeedback)
1755{
1756 NS_LOG_FUNCTION(this);
1757 NS_LOG_INFO("Scheduling invoked for slot " << params.m_snfSf << " of type "
1758 << params.m_slotType);
1759
1760 NrMacSchedSapUser::SchedConfigIndParameters dlSlot(params.m_snfSf);
1761 dlSlot.m_slotAllocInfo.m_sfnSf = params.m_snfSf;
1762 dlSlot.m_slotAllocInfo.m_type = SlotAllocInfo::DL;
1763 auto ulAllocationIt =
1764 m_ulAllocationMap.find(params.m_snfSf.GetEncoding()); // UL allocations for this slot
1765 if (ulAllocationIt == m_ulAllocationMap.end())
1766 {
1767 ulAllocationIt =
1768 m_ulAllocationMap.insert(std::make_pair(params.m_snfSf.GetEncoding(), SlotElem(0)))
1769 .first;
1770 }
1771 auto& ulAllocations = ulAllocationIt->second;
1772
1773 // add slot for DL control, at symbol 0
1774 PrependCtrlSym(0,
1775 m_dlCtrlSymbols,
1777 &dlSlot.m_slotAllocInfo.m_varTtiAllocInfo);
1778 dlSlot.m_slotAllocInfo.m_numSymAlloc += m_dlCtrlSymbols;
1779
1780 // In case of S slot, add UL CTRL and update the symbol used count
1781 if (params.m_slotType == LteNrTddSlotType::S)
1782 {
1783 NS_LOG_INFO("S slot, adding UL CTRL");
1784 AppendCtrlSym(static_cast<uint8_t>(m_macSchedSapUser->GetSymbolsPerSlot() - 1),
1785 m_ulCtrlSymbols,
1787 &dlSlot.m_slotAllocInfo.m_varTtiAllocInfo);
1788 ulAllocations.m_totUlSym += m_ulCtrlSymbols;
1789 dlSlot.m_slotAllocInfo.m_numSymAlloc += m_ulCtrlSymbols;
1790 }
1791
1792 // compute active ue in the current subframe, group them by BeamId
1793 ActiveHarqMap activeDlHarq;
1794 ComputeActiveHarq(&activeDlHarq, dlHarqFeedback);
1795
1796 ActiveUeMap activeDlUe;
1797 ComputeActiveUe(&activeDlUe,
1800 "DL");
1801
1802 DoScheduleDl(dlHarqFeedback,
1803 activeDlHarq,
1804 &activeDlUe,
1805 params.m_snfSf,
1806 ulAllocations,
1807 &dlSlot.m_slotAllocInfo);
1808
1809 // if the number of allocated symbols is greater than GetUlCtrlSymbols (), then don't
1810 // delete the allocation, as it will be removed when the CQI will be processed.
1811 // Otherwise, delete the allocation history for the slot.
1812 if (ulAllocations.m_totUlSym <= GetUlCtrlSyms())
1813 {
1814 NS_LOG_INFO("Removing UL allocation for slot " << params.m_snfSf << " size "
1815 << m_ulAllocationMap.size());
1816 m_ulAllocationMap.erase(ulAllocationIt);
1817 }
1818
1819 NS_LOG_INFO("Total DCI for DL : " << dlSlot.m_slotAllocInfo.m_varTtiAllocInfo.size()
1820 << " including DL CTRL");
1822}
1823
1860void
1861NrMacSchedulerNs3::ScheduleUl(const NrMacSchedSapProvider::SchedUlTriggerReqParameters& params,
1862 const std::vector<UlHarqInfo>& ulHarqFeedback)
1863{
1864 NS_LOG_FUNCTION(this);
1865 NS_LOG_INFO("Scheduling invoked for slot " << params.m_snfSf);
1866
1867 NrMacSchedSapUser::SchedConfigIndParameters ulSlot(params.m_snfSf);
1868 ulSlot.m_slotAllocInfo.m_sfnSf = params.m_snfSf;
1869 ulSlot.m_slotAllocInfo.m_type = SlotAllocInfo::UL;
1870
1871 // add slot for UL control, at last symbol, for slot type F and UL.
1872 AppendCtrlSym(static_cast<uint8_t>(m_macSchedSapUser->GetSymbolsPerSlot() - 1),
1873 m_ulCtrlSymbols,
1875 &ulSlot.m_slotAllocInfo.m_varTtiAllocInfo);
1876 ulSlot.m_slotAllocInfo.m_numSymAlloc += m_ulCtrlSymbols;
1877
1878 // Doing UL for slot ulSlot
1879 DoScheduleUl(ulHarqFeedback, params.m_snfSf, &ulSlot.m_slotAllocInfo, params.m_slotType);
1880
1881 NS_LOG_INFO("Total DCI for UL : " << ulSlot.m_slotAllocInfo.m_varTtiAllocInfo.size()
1882 << " including UL CTRL");
1883 m_macSchedSapUser->BuildRarList(ulSlot.m_slotAllocInfo);
1885}
1886
1924uint8_t
1925NrMacSchedulerNs3::DoScheduleUl(const std::vector<UlHarqInfo>& ulHarqFeedback,
1926 const SfnSf& ulSfn,
1927 SlotAllocInfo* allocInfo,
1928 LteNrTddSlotType type)
1929{
1930 NS_LOG_FUNCTION(this);
1931
1932 NS_ASSERT(allocInfo->m_varTtiAllocInfo.size() == 1); // Just the UL CTRL
1933
1934 uint8_t dataSymPerSlot = m_macSchedSapUser->GetSymbolsPerSlot() - m_ulCtrlSymbols;
1935 if (type == LteNrTddSlotType::F)
1936 { // if it's a type F, we have to consider DL CTRL symbols, otherwise, don't
1937 dataSymPerSlot -= m_dlCtrlSymbols;
1938 }
1939
1940 ActiveHarqMap activeUlHarq;
1941 ComputeActiveHarq(&activeUlHarq, ulHarqFeedback);
1942
1943 // Start the assignation from the last available data symbol, and like a shrimp
1944 // go backward.
1945 uint8_t lastSym = m_macSchedSapUser->GetSymbolsPerSlot() - m_ulCtrlSymbols;
1946 PointInFTPlane ulAssignationStartPoint(0, lastSym);
1947 uint8_t ulSymAvail = dataSymPerSlot;
1948
1949 // Create the UL allocation map entry
1950 m_ulAllocationMap.emplace(ulSfn.GetEncoding(), SlotElem(0));
1951
1952 if ((m_enableSrsInFSlots && type == LteNrTddSlotType::F) ||
1953 (m_enableSrsInUlSlots && type == LteNrTddSlotType::UL))
1954 { // SRS are included in F slots, and in UL slots if m_enableSrsInUlSlots=true
1955 m_srsSlotCounter++; // It's an uint, don't worry about wrap around
1956 NS_ASSERT(m_srsCtrlSymbols <= ulSymAvail);
1957 uint8_t srsSym = DoScheduleSrs(&ulAssignationStartPoint, allocInfo);
1958 ulSymAvail -= srsSym;
1959 }
1960
1961 NS_LOG_DEBUG("Scheduling UL " << ulSfn << " UL HARQ to retransmit: " << ulHarqFeedback.size()
1962 << " Active Beams UL HARQ: " << activeUlHarq.size()
1963 << " starting from (" << +ulAssignationStartPoint.m_rbg << ", "
1964 << +ulAssignationStartPoint.m_sym << ")");
1965
1966 // RACH
1967 uint8_t usedMsg3 = 0;
1968 if (!m_rachList.empty())
1969 {
1970 usedMsg3 = DoScheduleUlMsg3(&ulAssignationStartPoint, ulSymAvail, allocInfo);
1971 NS_ASSERT_MSG(ulSymAvail >= usedMsg3,
1972 "Available: " << +ulSymAvail << " used by UL MSG3: " << +usedMsg3);
1973 NS_LOG_INFO("For the slot " << ulSfn << " reserved " << static_cast<uint32_t>(usedMsg3)
1974 << " symbols for UL MSG3");
1975 ulSymAvail -= usedMsg3;
1976 allocInfo->m_numSymAlloc += usedMsg3;
1977 }
1978
1979 if (!activeUlHarq.empty())
1980 {
1981 uint8_t usedHarq = ScheduleUlHarq(&ulAssignationStartPoint,
1982 ulSymAvail,
1983 m_ueMap,
1984 &m_ulHarqToRetransmit,
1985 ulHarqFeedback,
1986 allocInfo);
1987 NS_ASSERT_MSG(ulSymAvail >= usedHarq,
1988 "Available: " << +ulSymAvail << " used by HARQ: " << +usedHarq);
1989 NS_LOG_INFO("For the slot " << ulSfn << " reserved " << static_cast<uint32_t>(usedHarq)
1990 << " symbols for UL HARQ retx");
1991 ulSymAvail -= usedHarq;
1992 }
1993
1994 NS_ASSERT(ulAssignationStartPoint.m_rbg == 0);
1995
1996 if (ulSymAvail > 0 && !m_srList.empty())
1997 {
1998 DoScheduleUlSr(&ulAssignationStartPoint, m_srList);
1999 m_srList.clear();
2000 }
2001
2002 ActiveUeMap activeUlUe;
2003 ComputeActiveUe(&activeUlUe,
2006 "UL");
2007
2008 GetSecond GetUeInfoList;
2009 for (const auto& alloc : allocInfo->m_varTtiAllocInfo)
2010 {
2011 for (auto it = activeUlUe.begin(); it != activeUlUe.end(); /* no incr */)
2012 {
2013 auto& ueInfos = GetUeInfoList(*it);
2014 for (auto ueIt = ueInfos.begin(); ueIt != ueInfos.end(); /* no incr */)
2015 {
2016 GetFirst GetUeInfoPtr;
2017 if (GetUeInfoPtr(*ueIt)->m_rnti == alloc.m_dci->m_rnti)
2018 {
2019 NS_LOG_INFO("Removed RNTI " << alloc.m_dci->m_rnti
2020 << " from active ue list "
2021 "because it has already an HARQ scheduled");
2022 ueInfos.erase(ueIt);
2023 break;
2024 }
2025 else
2026 {
2027 ++ueIt;
2028 }
2029 }
2030 if (!ueInfos.empty())
2031 {
2032 ++it;
2033 }
2034 else
2035 {
2036 activeUlUe.erase(it);
2037 break;
2038 }
2039 }
2040 }
2041
2042 if (ulSymAvail > 0 && !activeUlUe.empty())
2043 {
2044 uint8_t usedUl =
2045 DoScheduleUlData(&ulAssignationStartPoint, ulSymAvail, activeUlUe, allocInfo);
2046 NS_LOG_INFO("For the slot " << ulSfn << " reserved " << static_cast<uint32_t>(usedUl)
2047 << " symbols for UL data tx");
2048 ulSymAvail -= usedUl;
2049 }
2050
2051 std::vector<uint32_t> symToAl;
2052 symToAl.resize(15, 0);
2053
2054 auto& totUlSym = m_ulAllocationMap.at(ulSfn.GetEncoding()).m_totUlSym;
2055 auto& allocations = m_ulAllocationMap.at(ulSfn.GetEncoding()).m_ulAllocations;
2056 for (const auto& alloc : allocInfo->m_varTtiAllocInfo)
2057 {
2058 if (alloc.m_dci->m_format == DciInfoElementTdma::UL)
2059 {
2060 // Here we are assuming (with the assignment) that all the
2061 // allocations starting at a particular symbol will have the same
2062 // length.
2063 symToAl[alloc.m_dci->m_symStart] = alloc.m_dci->m_numSym;
2064 NS_LOG_INFO("UL Allocation. RNTI " << alloc.m_dci->m_rnti << ", symStart "
2065 << static_cast<uint32_t>(alloc.m_dci->m_symStart)
2066 << " numSym " << +alloc.m_dci->m_numSym);
2067
2068 if (alloc.m_dci->m_type == DciInfoElementTdma::DATA ||
2069 alloc.m_dci->m_type == DciInfoElementTdma::MSG3)
2070 {
2071 NS_LOG_INFO("Placed the above allocation in the CQI map");
2072 allocations.emplace_back(alloc.m_dci->m_rnti,
2073 alloc.m_dci->m_tbSize,
2074 alloc.m_dci->m_symStart,
2075 alloc.m_dci->m_numSym,
2076 alloc.m_dci->m_mcs,
2077 alloc.m_dci->m_rank,
2078 alloc.m_dci->m_rbgBitmask);
2079 }
2080 }
2081 }
2082
2083 for (const auto& v : symToAl)
2084 {
2085 totUlSym += v;
2086 }
2087
2088 NS_ASSERT_MSG((dataSymPerSlot + m_ulCtrlSymbols) - ulSymAvail == totUlSym,
2089 "UL symbols available: "
2090 << static_cast<uint32_t>(dataSymPerSlot + m_ulCtrlSymbols)
2091 << " UL symbols available at end of sched: "
2092 << static_cast<uint32_t>(ulSymAvail)
2093 << " total of symbols registered in the allocation: "
2094 << static_cast<uint32_t>(totUlSym) << " slot type " << type);
2095
2096 NS_LOG_INFO("For the slot " << ulSfn << " registered a total of "
2097 << static_cast<uint32_t>(totUlSym) << " symbols and "
2098 << allocations.size() << " data allocations, with a total of "
2099 << allocInfo->m_varTtiAllocInfo.size());
2100 NS_ASSERT(m_ulAllocationMap.at(ulSfn.GetEncoding()).m_totUlSym == totUlSym);
2101
2102 return dataSymPerSlot - ulSymAvail;
2103}
2104
2105uint8_t
2106NrMacSchedulerNs3::DoScheduleSrs(PointInFTPlane* spoint, SlotAllocInfo* allocInfo)
2107{
2108 NS_LOG_FUNCTION(this);
2109
2110 uint8_t used = 0;
2111
2112 // Without UE, don't schedule any SRS
2113 if (m_ueMap.empty())
2114 {
2115 return used;
2116 }
2117
2118 // Find the UE for which this is true:
2119 // absolute_slot_number % periodicity = offset_UEx
2120 // Assuming that all UEs share the same periodicity.
2121
2122 uint32_t offset_UEx = m_srsSlotCounter % m_ueMap.begin()->second->m_srsPeriodicity;
2123 uint16_t rnti = 0;
2124
2125 for (const auto& ue : m_ueMap)
2126 {
2127 if (ue.second->m_srsOffset == offset_UEx)
2128 {
2129 rnti = ue.second->m_rnti;
2130 }
2131 }
2132
2133 if (rnti == 0)
2134 {
2135 return used; // No SRS in this slot!
2136 }
2137
2138 // Schedule 4 allocation, of 1 symbol each, in TDMA mode, for the RNTI found.
2139
2140 for (uint32_t i = 0; i < m_srsCtrlSymbols; ++i)
2141 {
2142 std::vector<uint8_t> rbgAssigned(GetBandwidthInRbg(), 1);
2143
2144 NS_LOG_INFO("UE " << rnti << " assigned symbol " << +spoint->m_sym << " for SRS tx");
2145
2146 std::vector<uint8_t> rbgBitmask(GetBandwidthInRbg(), 1);
2147
2148 spoint->m_sym--;
2149
2150 uint8_t numSym{1};
2151 uint8_t mcs{0};
2152 uint8_t rank{1};
2153 Ptr<const ComplexMatrixArray> precMats{nullptr};
2154 uint32_t tbs{0};
2155 uint8_t ndi{1};
2156 uint8_t rv{0};
2157 auto dci = std::make_shared<DciInfoElementTdma>(rnti,
2159 spoint->m_sym,
2160 numSym,
2161 mcs,
2162 rank,
2163 precMats,
2164 tbs,
2165 ndi,
2166 rv,
2168 GetBwpId(),
2169 GetTpc());
2170 dci->m_rbgBitmask = rbgBitmask;
2171
2172 allocInfo->m_numSymAlloc += 1;
2173 allocInfo->m_varTtiAllocInfo.emplace_front(dci);
2174
2175 used++;
2176 }
2177
2178 return used;
2179}
2180
2181uint16_t
2183{
2185 {
2186 return m_macSchedSapUser->GetBwpId();
2187 }
2188 else
2189 {
2190 return UINT16_MAX;
2191 }
2192}
2193
2194uint16_t
2196{
2198 {
2199 return m_macSchedSapUser->GetCellId();
2200 }
2201 else
2202 {
2203 return UINT16_MAX;
2204 }
2205}
2206
2207uint16_t
2209{
2210 return m_bandwidth;
2211}
2212
2232uint8_t
2233NrMacSchedulerNs3::DoScheduleDl(const std::vector<DlHarqInfo>& dlHarqFeedback,
2234 const ActiveHarqMap& activeDlHarq,
2235 ActiveUeMap* activeDlUe,
2236 const SfnSf& dlSfnSf,
2237 const SlotElem& ulAllocations,
2238 SlotAllocInfo* allocInfo)
2239{
2240 NS_LOG_FUNCTION(this);
2241 NS_ASSERT(activeDlUe != nullptr);
2242
2243 uint8_t dataSymPerSlot = m_macSchedSapUser->GetSymbolsPerSlot() - m_dlCtrlSymbols;
2244
2245 uint8_t dlSymAvail = dataSymPerSlot - ulAllocations.m_totUlSym;
2246 PointInFTPlane dlAssignationStartPoint(0, m_dlCtrlSymbols);
2247
2248 NS_LOG_DEBUG("Scheduling DL for slot "
2249 << dlSfnSf << " DL HARQ to retransmit: " << dlHarqFeedback.size()
2250 << " Active Beams DL HARQ: " << activeDlHarq.size()
2251 << " sym available: " << static_cast<uint32_t>(dlSymAvail) << " starting from sym "
2252 << static_cast<uint32_t>(m_dlCtrlSymbols));
2253
2254 if (!activeDlHarq.empty())
2255 {
2256 uint8_t usedHarq = ScheduleDlHarq(&dlAssignationStartPoint,
2257 dlSymAvail,
2258 activeDlHarq,
2259 m_ueMap,
2260 &m_dlHarqToRetransmit,
2261 dlHarqFeedback,
2262 allocInfo);
2263 NS_ASSERT(dlSymAvail >= usedHarq);
2264 dlSymAvail -= usedHarq;
2265 }
2266
2267 GetSecond GetUeInfoList;
2268
2269 for (const auto& alloc : allocInfo->m_varTtiAllocInfo)
2270 {
2271 for (auto it = activeDlUe->begin(); it != activeDlUe->end(); /* no incr */)
2272 {
2273 auto& ueInfos = GetUeInfoList(*it);
2274 for (auto ueIt = ueInfos.begin(); ueIt != ueInfos.end(); /* no incr */)
2275 {
2276 GetFirst GetUeInfoPtr;
2277 if (GetUeInfoPtr(*ueIt)->m_rnti == alloc.m_dci->m_rnti)
2278 {
2279 NS_LOG_INFO("Removed RNTI " << alloc.m_dci->m_rnti
2280 << " from active ue list "
2281 "because it has already an HARQ scheduled");
2282 ueInfos.erase(ueIt);
2283 break;
2284 }
2285 else
2286 {
2287 ++ueIt;
2288 }
2289 }
2290 if (!ueInfos.empty())
2291 {
2292 ++it;
2293 }
2294 else
2295 {
2296 activeDlUe->erase(it);
2297 break;
2298 }
2299 }
2300 }
2301
2302 NS_ASSERT(dlAssignationStartPoint.m_rbg == 0);
2303
2304 if (dlSymAvail > 0 && !activeDlUe->empty())
2305 {
2306 uint8_t usedDl =
2307 DoScheduleDlData(&dlAssignationStartPoint, dlSymAvail, *activeDlUe, allocInfo);
2308 NS_ASSERT(dlSymAvail >= usedDl);
2309 dlSymAvail -= usedDl;
2310 }
2311
2312 return (dataSymPerSlot - ulAllocations.m_totUlSym) - dlSymAvail;
2313}
2314
2325void
2328{
2329 NS_LOG_FUNCTION(this);
2330
2331 // process received CQIs
2332 m_cqiManagement.RefreshDlCqiMaps(m_ueMap);
2333
2334 // reset expired HARQ
2335 for (const auto& itUe : m_ueMap)
2336 {
2337 ResetExpiredHARQ(itUe.second->m_rnti, &itUe.second->m_dlHarq);
2338 }
2339
2340 // Merge not-retransmitted and received feedback
2341 std::vector<DlHarqInfo> dlHarqFeedback;
2342
2343 if (!params.m_dlHarqInfoList.empty() || !m_dlHarqToRetransmit.empty())
2344 {
2345 // m_dlHarqToRetransmit will be cleared inside MergeHARQ
2346 uint64_t existingSize = m_dlHarqToRetransmit.size();
2347 uint64_t inSize = params.m_dlHarqInfoList.size();
2348
2349 dlHarqFeedback = MergeHARQ(&m_dlHarqToRetransmit, params.m_dlHarqInfoList, "DL");
2350
2351 NS_ASSERT(m_dlHarqToRetransmit.empty());
2352 NS_ASSERT_MSG(existingSize + inSize == dlHarqFeedback.size(),
2353 " existing: " << existingSize << " received: " << inSize
2354 << " calculated: " << dlHarqFeedback.size());
2355
2356 std::unordered_map<uint16_t, std::set<uint32_t>> feedbacksDup;
2357
2358 // Let's find out:
2359 // 1) Feedback that arrived late (i.e., their process has been marked inactive
2360 // due to timings
2361 // 2) Duplicated feedbacks (same UE, same process ID). I don't know why
2362 // these are generated.. but anyway..
2363 for (auto it = dlHarqFeedback.begin(); it != dlHarqFeedback.end(); /* no inc */)
2364 {
2365 auto& ueInfo = m_ueMap.find(it->m_rnti)->second;
2366 auto& process = ueInfo->m_dlHarq.Find(it->m_harqProcessId)->second;
2367 NS_LOG_INFO("Analyzing feedback for UE " << it->m_rnti << " process "
2368 << static_cast<uint32_t>(it->m_harqProcessId));
2369 if (!process.m_active)
2370 {
2371 NS_LOG_INFO("Feedback for UE " << it->m_rnti << " process "
2372 << static_cast<uint32_t>(it->m_harqProcessId)
2373 << " ignored because process is INACTIVE");
2374 it = dlHarqFeedback.erase(it); /* INC */
2375 }
2376 else
2377 {
2378 auto itDuplicated = feedbacksDup.find(it->m_rnti);
2379 if (itDuplicated == feedbacksDup.end())
2380 {
2381 feedbacksDup.insert(std::make_pair(it->m_rnti, std::set<uint32_t>()));
2382 feedbacksDup.at(it->m_rnti).insert(it->m_harqProcessId);
2383 ++it; /* INC */
2384 }
2385 else
2386 {
2387 if (itDuplicated->second.find(it->m_harqProcessId) ==
2388 itDuplicated->second.end())
2389 {
2390 itDuplicated->second.insert(it->m_harqProcessId);
2391 ++it; /* INC */
2392 }
2393 else
2394 {
2395 NS_LOG_INFO("Feedback for UE "
2396 << it->m_rnti << " process "
2397 << static_cast<uint32_t>(it->m_harqProcessId)
2398 << " ignored because is a duplicate of another feedback");
2399 it = dlHarqFeedback.erase(it); /* INC */
2400 }
2401 }
2402 }
2403 }
2404
2405 ProcessHARQFeedbacks(&dlHarqFeedback, NrMacSchedulerUeInfo::GetDlHarqVector, "DL");
2406 }
2407
2408 ScheduleDl(params, dlHarqFeedback);
2409}
2410
2421void
2424{
2425 NS_LOG_FUNCTION(this);
2426
2427 // process received CQIs
2428 m_cqiManagement.RefreshUlCqiMaps(m_ueMap);
2429
2430 // reset expired HARQ
2431 for (const auto& itUe : m_ueMap)
2432 {
2433 ResetExpiredHARQ(itUe.second->m_rnti, &itUe.second->m_ulHarq);
2434 }
2435
2436 // Merge not-retransmitted and received feedback
2437 std::vector<UlHarqInfo> ulHarqFeedback;
2438 if (!params.m_ulHarqInfoList.empty() || !m_ulHarqToRetransmit.empty())
2439 {
2440 // m_ulHarqToRetransmit will be cleared inside MergeHARQ
2441 uint64_t existingSize = m_ulHarqToRetransmit.size();
2442 uint64_t inSize = params.m_ulHarqInfoList.size();
2443
2444 ulHarqFeedback = MergeHARQ(&m_ulHarqToRetransmit, params.m_ulHarqInfoList, "UL");
2445
2446 NS_ASSERT(m_ulHarqToRetransmit.empty());
2447 NS_ASSERT_MSG(existingSize + inSize == ulHarqFeedback.size(),
2448 " existing: " << existingSize << " received: " << inSize
2449 << " calculated: " << ulHarqFeedback.size());
2450
2451 // if there are feedbacks for expired process, remove them
2452 for (auto it = ulHarqFeedback.begin(); it != ulHarqFeedback.end(); /* no inc */)
2453 {
2454 auto& ueInfo = m_ueMap.find(it->m_rnti)->second;
2455 auto& process = ueInfo->m_ulHarq.Find(it->m_harqProcessId)->second;
2456 if (!process.m_active)
2457 {
2458 NS_LOG_INFO("Feedback for UE " << it->m_rnti << " process "
2459 << static_cast<uint32_t>(it->m_harqProcessId)
2460 << " ignored because process is INACTIVE");
2461 it = ulHarqFeedback.erase(it);
2462 }
2463 else
2464 {
2465 ++it;
2466 }
2467 }
2468
2469 ProcessHARQFeedbacks(&ulHarqFeedback, NrMacSchedulerUeInfo::GetUlHarqVector, "UL");
2470 }
2471
2472 ScheduleUl(params, ulHarqFeedback);
2473}
2474
2481void
2484{
2485 NS_LOG_FUNCTION(this);
2486
2487 // Merge RNTI in our current list
2488 for (const auto& ue : params.m_srList)
2489 {
2490 NS_LOG_INFO("UE " << ue << " asked for a SR ");
2491
2492 auto it = std::find(m_srList.begin(), m_srList.end(), ue);
2493 if (it == m_srList.end())
2494 {
2495 m_srList.push_back(ue);
2496 }
2497 }
2498 NS_ASSERT(m_srList.size() >= params.m_srList.size());
2499}
2500
2516uint8_t
2517NrMacSchedulerNs3::DoScheduleUlMsg3(PointInFTPlane* sPoint,
2518 uint8_t symAvail,
2519 SlotAllocInfo* slotAlloc)
2520{
2521 NS_LOG_FUNCTION(this);
2522 NS_ASSERT(sPoint->m_rbg == 0);
2523
2524 uint8_t symAvailBeforeRach = symAvail;
2525
2526 for (const auto& rachReq : m_rachList)
2527 {
2528 uint8_t allocSymbols = 0;
2529 uint16_t tbSizeBits = 0;
2530
2531 // find the lowest TB size that fits UL grant estimated size
2532 while ((tbSizeBits < rachReq.m_estimatedSize) && (symAvail - allocSymbols > 0))
2533 {
2534 allocSymbols++;
2535 tbSizeBits =
2536 GetUlAmc()->CalculateTbSize(m_rachUlGrantMcs,
2537 1,
2538 GetBandwidthInRbg() * GetNumRbPerRbg() * allocSymbols) *
2539 8;
2540 }
2541
2542 if (tbSizeBits < rachReq.m_estimatedSize)
2543 {
2544 // no more allocation space: finish allocation
2545 break;
2546 }
2547
2548 sPoint->m_sym -= allocSymbols;
2549
2550 // DL-RACH Allocation
2551 // Ideal: no needs of configuring m_dci
2552 // UL-RACH Allocation
2553 uint8_t rank{1};
2554 Ptr<const ComplexMatrixArray> precMats{nullptr};
2555 std::shared_ptr<DciInfoElementTdma> ulMsg3Dci =
2556 std::make_shared<DciInfoElementTdma>(rachReq.m_rnti,
2558 sPoint->m_sym,
2559 allocSymbols,
2560 m_rachUlGrantMcs,
2561 rank,
2562 precMats,
2563 tbSizeBits / 8,
2564 1,
2565 0,
2567 GetBwpId(),
2568 GetTpc());
2569
2570 std::vector<uint8_t> rbgBitmask(GetBandwidthInRbg(), 1);
2571
2572 ulMsg3Dci->m_rbgBitmask = rbgBitmask;
2573 symAvail -= allocSymbols;
2574
2575 NS_LOG_INFO(" UL grant allocated to RNTI "
2576 << ulMsg3Dci->m_rnti << " in slot: " << slotAlloc->m_sfnSf << " symStart "
2577 << +ulMsg3Dci->m_symStart << " symEnd " << +ulMsg3Dci->m_numSym
2578 << " number of PRB" << GetBandwidthInRbg() * GetNumRbPerRbg() << " MCS "
2579 << (uint16_t) + ulMsg3Dci->m_mcs << " tbSize in bytes:" << +ulMsg3Dci->m_tbSize
2580 << " BWP index: " << +ulMsg3Dci->m_bwpIndex
2581 << " RBG bitmask:" << +ulMsg3Dci->m_rbgBitmask.size());
2582
2583 VarTtiAllocInfo slotInfo(ulMsg3Dci);
2584 slotAlloc->m_varTtiAllocInfo.emplace_front(slotInfo);
2585 }
2586
2587 m_rachList.clear();
2588 return (symAvailBeforeRach - symAvail);
2589}
2590
2591} // namespace ns3
virtual uint32_t GetNumRbPerRbg() const =0
Get the number of RB per RBG.
virtual Time GetSlotPeriod() const =0
Get the slot period.
virtual uint16_t GetBwpId() const =0
Get the BWP ID.
virtual uint32_t GetSymbolsPerSlot() const =0
Get the Symbol per slot.
virtual void SchedConfigInd(const struct SchedConfigIndParameters &params)=0
Install a scheduling decision.
virtual uint16_t GetCellId() const =0
Get the Cell ID.
virtual Ptr< const SpectrumModel > GetSpectrumModel() const =0
Get the SpectrumModel.
virtual void BuildRarList(SlotAllocInfo &slotAllocInfo)=0
Build RAR list from allocations and assign preamble IDs.
virtual uint8_t GetNumHarqProcess() const =0
Get the number of HARQ process.
void RefreshUlCqiMaps(const std::unordered_map< uint16_t, std::shared_ptr< NrMacSchedulerUeInfo > > &m_ueMap) const
Refresh the UL CQI for all the UE.
void InstallGetCellIdFn(const std::function< uint16_t()> &fn)
Install a function to retrieve the cell id.
void RefreshDlCqiMaps(const std::unordered_map< uint16_t, std::shared_ptr< NrMacSchedulerUeInfo > > &m_ueMap) const
Refresh the DL CQI for all the UE.
void UlSBCQIReported(uint32_t expirationTime, uint32_t tbs, const NrMacSchedSapProvider::SchedUlCqiInfoReqParameters &params, const std::shared_ptr< NrMacSchedulerUeInfo > &ueInfo, const std::vector< uint8_t > &rbgMask, uint32_t numRbPerRbg, const Ptr< const SpectrumModel > &model) const
An UL SB CQI has been reported for the specified UE.
void InstallGetBwpIdFn(const std::function< uint16_t()> &fn)
Install a function to retrieve the bwp id.
void DlSBCQIReported(const DlCqiInfo &info, const std::shared_ptr< NrMacSchedulerUeInfo > &ueInfo) const
SB CQI reported.
void DlWBCQIReported(const DlCqiInfo &info, const std::shared_ptr< NrMacSchedulerUeInfo > &ueInfo, uint32_t expirationTime, int8_t maxDlMcs) const
A wideband CQI has been reported for the specified UE.
Interface for all the nr schedulers.
NrMacSchedSapUser * m_macSchedSapUser
SAP user.
NrMacCschedSapUser * m_macCschedSapUser
SAP User.
static TypeId GetTypeId()
Get the type ID.
bool IsSrsInUlSlots() const
Check if the UL slots are allowed for SRS transmission.
void DoCschedUeConfigReq(const NrMacCschedSapProvider::CschedUeConfigReqParameters &params) override
Register an UE.
NrMacSchedulerNs3()
NrMacSchedulerNs3 default constructor.
uint8_t GetUlCtrlSyms() const override
Retrieve the number of UL ctrl symbols configured in the scheduler.
std::unordered_map< BeamId, HarqVectorIteratorList, BeamIdHash > ActiveHarqMap
Map between a beamID and the HARQ of that beam.
virtual std::shared_ptr< NrMacSchedulerUeInfo > CreateUeRepresentation(const NrMacCschedSapProvider::CschedUeConfigReqParameters &params) const =0
Create an UE representation for the scheduler.
Ptr< NrAmc > m_ulAmc
AMC pointer.
bool IsHarqReTxEnable() const
Is HARQ ReTx enable function.
uint8_t GetStartMcsDl() const
Get the DL MCS starting value.
int64_t AssignStreams(int64_t stream) override
Assign a fixed random variable stream number to the random variables used by this model....
void DoCschedUeReleaseReq(const NrMacCschedSapProvider::CschedUeReleaseReqParameters &params) override
Release an UE.
void DoSchedUlTriggerReq(const NrMacSchedSapProvider::SchedUlTriggerReqParameters &params) override
Decide how to fill the frequency/time of a UL slot.
void SetSrsInFSlots(bool v)
Set if the F slots are allowed for SRS transmission.
void SetDlNotchedRbgMask(const std::vector< uint8_t > &dlNotchedRbgsMask)
Set the notched (blank) RBGs Mask for the DL.
virtual void ChangeDlBeam(PointInFTPlane *spoint, uint32_t symOfBeam) const =0
Perform a custom operation on the starting point each time all the UE of a DL beam have been schedule...
void SetRachUlGrantMcs(uint8_t v)
Sets the default RACH UL grant MCS.
virtual BeamSymbolMap AssignDLRBG(uint32_t symAvail, const ActiveUeMap &activeDl) const =0
Assign the DL RBG to the active UE, and return the distribution of symbols per beam.
virtual void SortUlHarq(ActiveHarqMap *activeUlHarq) const
Sort the UL HARQ retransmission.
void SetSrsCtrlSyms(uint8_t v)
Set the number of UL SRS symbols.
void DoCschedLcReleaseReq(const NrMacCschedSapProvider::CschedLcReleaseReqParameters &params) override
Release a LC.
void SetFixedDlMcs(bool v)
Set if the MCS in DL is fixed (in case, it will take the starting value)
virtual void ChangeUlBeam(PointInFTPlane *spoint, uint32_t symOfBeam) const =0
Perform a custom operation on the starting point each time all the UE of an UL beam have been schedul...
Ptr< NrAmc > m_dlAmc
AMC pointer.
bool IsSrsInFSlots() const
Check if the F slots are allowed for SRS transmission.
void SetUlCtrlSyms(uint8_t v)
Set the number of UL ctrl symbols.
uint8_t GetSrsCtrlSyms() const
Get the configured value for the SRS symbols.
virtual uint8_t ScheduleUlHarq(NrMacSchedulerNs3::PointInFTPlane *startingPoint, uint8_t symAvail, const std::unordered_map< uint16_t, UePtr > &ueMap, std::vector< UlHarqInfo > *ulHarqToRetransmit, const std::vector< UlHarqInfo > &ulHarqFeedback, SlotAllocInfo *slotAlloc) const
Giving the input, append to slotAlloc the allocations for the DL HARQ retransmissions.
void InstallUlAmc(const Ptr< NrAmc > &ulAmc)
Install the AMC for the DL part.
Ptr< const NrAmc > GetDlAmc() const
Get the AMC for DL.
void SetSrsInUlSlots(bool v)
Set if the UL slots are allowed for SRS transmission (if True, UL and F slots may carry SRS,...
void SetStartMcsUl(uint8_t v)
Set the starting value for the UL MCS.
uint8_t GetStartMcsUl() const
Get the DL MCS starting value.
std::unordered_map< BeamId, uint32_t, BeamIdHash > BeamSymbolMap
Map between a BeamId and the symbol assigned to that beam.
void DoSchedSetMcs(uint32_t mcs) override
Set a fixed MCS.
void DoCschedLcConfigReq(const NrMacCschedSapProvider::CschedLcConfigReqParameters &params) override
Configure a logical channel for a UE.
void DoSchedDlCqiInfoReq(const NrMacSchedSapProvider::SchedDlCqiInfoReqParameters &params) override
Received a DL CQI message.
virtual std::shared_ptr< DciInfoElementTdma > CreateUlDci(PointInFTPlane *spoint, const std::shared_ptr< NrMacSchedulerUeInfo > &ueInfo, uint32_t maxSym) const =0
Create a DCI for the specified UE for UL data.
void DoSchedUlCqiInfoReq(const NrMacSchedSapProvider::SchedUlCqiInfoReqParameters &params) override
Received a UL CQI message.
uint16_t GetBwpId() const
Get the bwp id of this MAC.
void DoCschedCellConfigReq(const NrMacCschedSapProvider::CschedCellConfigReqParameters &params) override
Cell configuration.
void SetStartMcsDl(uint8_t v)
Set the starting value for the DL MCS.
void SetLcSched(const TypeId &type)
Set LC Scheduler Algorithm model type.
virtual BeamSymbolMap AssignULRBG(uint32_t symAvail, const ActiveUeMap &activeUl) const =0
Assign the UL RBG to the active UE, and return the distribution of symbols per beam.
static TypeId GetTypeId()
GetTypeId.
Time GetCqiTimerThreshold() const
Get the CqiTimerThreshold.
std::vector< uint8_t > GetDlNotchedRbgMask() const
Get the notched (blank) RBGs Mask for the DL.
std::unordered_map< BeamId, std::vector< UePtrAndBufferReq >, BeamIdHash > ActiveUeMap
Map between a BeamId and a vector of UE (the UE are in that beam)
bool IsUlMcsFixed() const
Check if the MCS in UL is fixed.
void InstallDlAmc(const Ptr< NrAmc > &dlAmc)
Install the AMC for the DL part.
~NrMacSchedulerNs3() override
NrMacSchedulerNs3 deconstructor.
virtual void SortDlHarq(ActiveHarqMap *activeDlHarq) const
Sort the DL HARQ retransmission.
void SetFixedUlMcs(bool v)
Set if the MCS in UL is fixed (in case, it will take the starting value)
virtual uint8_t GetTpc() const =0
Returns TPC command.
void SetUlNotchedRbgMask(const std::vector< uint8_t > &ulNotchedRbgsMask)
Set the notched (blank) RBGs Mask for the UL.
void DoSchedDlRlcBufferReq(const NrMacSchedSapProvider::SchedDlRlcBufferReqParameters &params) override
RLC informs of DL data.
void DoSchedDlRachInfoReq(const NrMacSchedSapProvider::SchedDlRachInfoReqParameters &params) override
RACH information.
void EnableHarqReTx(bool enableFlag)
Enable HARQ ReTx function.
virtual std::shared_ptr< DciInfoElementTdma > CreateDlDci(PointInFTPlane *spoint, const std::shared_ptr< NrMacSchedulerUeInfo > &ueInfo, uint32_t maxSym) const =0
Create a DCI for the specified UE for DL data.
void DoSchedDlTriggerReq(const NrMacSchedSapProvider::SchedDlTriggerReqParameters &params) override
Decide how to fill the frequency/time of a DL slot.
void DoSchedUlMacCtrlInfoReq(const NrMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters &params) override
Evaluate different types of control messages (only BSR for the moment)
std::vector< uint8_t > GetUlNotchedRbgMask() const
Get the notched (blank) RBGs Mask for the UL.
virtual uint8_t ScheduleDlHarq(NrMacSchedulerNs3::PointInFTPlane *startingPoint, uint8_t symAvail, const ActiveHarqMap &activeDlHarq, const std::unordered_map< uint16_t, UePtr > &ueMap, std::vector< DlHarqInfo > *dlHarqToRetransmit, const std::vector< DlHarqInfo > &dlHarqFeedback, SlotAllocInfo *slotAlloc) const
Giving the input, append to slotAlloc the allocations for the DL HARQ retransmissions.
virtual LCPtr CreateLC(const nr::LogicalChannelConfigListElement_s &config) const
Create a logical channel starting from a configuration.
bool IsDlMcsFixed() const
Check if the MCS in DL is fixed.
virtual LCGPtr CreateLCG(const nr::LogicalChannelConfigListElement_s &config) const
Create a logical channel group starting from a configuration.
void DoSchedUlSrInfoReq(const NrMacSchedSapProvider::SchedUlSrInfoReqParameters &params) override
Save the SR list into m_srList.
uint64_t GetNumRbPerRbg() const
Private function that is used to get the number of resource blocks per resource block group and also ...
uint16_t GetCellId() const
Get the cell id of this MAC.
void SetDlCtrlSyms(uint8_t v)
Set the number of DL ctrl symbols.
void SetMaxDlMcs(int8_t v)
Set the maximum index for the DL MCS.
Ptr< const NrAmc > GetUlAmc() const
Get the AMC for UL.
uint8_t GetDlCtrlSyms() const override
Retrieve the number of DL ctrl symbols configured in the scheduler.
int8_t GetMaxDlMcs() const
Get the maximum DL MCS index.
void SetCqiTimerThreshold(const Time &v)
Set the CqiTimerThreshold.
static std::unordered_map< uint8_t, LCGPtr > & GetUlLCG(const UePtr &ue)
GetUlLCG.
static NrMacHarqVector & GetUlHarqVector(const UePtr &ue)
GetUlHarqVector.
static std::unordered_map< uint8_t, LCGPtr > & GetDlLCG(const UePtr &ue)
GetDlLCG.
static NrMacHarqVector & GetDlHarqVector(const UePtr &ue)
GetDlHarqVector.
static uint64_t FromLevelToBytes(uint8_t bufferLevel)
Convert a buffer level into a buffer size.
The SfnSf class.
Definition sfnsf.h:32
uint64_t GetEncoding() const
Get encoding for this SfnSf.
Definition sfnsf.cc:24
std::unique_ptr< NrMacSchedulerLC > LCPtr
Unique pointer to an instance of NrMacSchedulerLC.
std::unique_ptr< NrMacSchedulerLCG > LCGPtr
LCGPtr unique pointer to a LCG.
LteNrTddSlotType
Available TDD slot types. Ordering is important.
@ F
DL CTRL + DL DATA + UL DATA + UL CTRL.
@ S
DL CTRL + DL DATA + UL CTRL.
@ UL
UL DATA + UL CTRL.
@ CTRL
Used for DL/UL CTRL.
@ DATA
Used for DL/UL DATA.
@ SRS
Used for SRS (it would be like DCI format 2_3)
DciFormat
Format of the DCI.
enum ns3::DlCqiInfo::DlCqiType WB
The type of the CQI.
@ RECEIVED_FEEDBACK
Received feedback (NACK)
@ WAITING_FEEDBACK
Data transmitted, waiting the feedback.
@ INACTIVE
Inactive process.
See section 4.3.14 macCEListElement.
uint16_t m_dlBandwidth
In number of RBG, created by MAC and passed to the scheduler.
uint16_t m_ulBandwidth
In number of RBG, created by MAC and passed to the scheduler.
std::vector< struct DlCqiInfo > m_cqiList
cqi list
std::vector< struct nr::RachListElement_s > m_rachList
RACH list.
uint8_t m_logicalChannelIdentity
The logical channel ID, range: 0..10.
DL HARQ information to be used when scheduling UL data.
std::vector< struct DlHarqInfo > m_dlHarqInfoList
DL HARQ info list.
uint8_t m_symStart
Sym start of the transmission to which this CQI refers to.
The SchedUlMacCtrlInfoReqParameters struct.
std::vector< struct MacCeElement > m_macCeList
MacCeElement list.
SR received from MAC, to pass to schedulers.
std::vector< uint16_t > m_srList
List of RNTI which asked for a SR.
UL HARQ information to be used when scheduling UL data.
std::vector< struct UlHarqInfo > m_ulHarqInfoList
UL HARQ info list.
Point in the Frequency/Time plane.
Struct to indicate to the scheduler the periodicity and the offset, in slots.
uint32_t m_periodicity
The periodicity requested (in slot).
uint32_t m_offset
The offset requested (in slot).
bool m_isValid
Indicates if the values are valid.
The SlotAllocInfo struct.
SfnSf m_sfnSf
SfnSf of this allocation.
@ UL
UL Allocations.
@ DL
DL Allocations.
std::deque< VarTtiAllocInfo > m_varTtiAllocInfo
queue of allocations
See section 4.3.4 logicalChannelConfigListElement.
uint8_t m_logicalChannelGroup
logical channel group