5G-LENA nr-v3.3-120-gdac69c56
The 5G/NR module for the ns-3 simulator
Loading...
Searching...
No Matches
nr-fh-control.cc
1// Copyright (c) 2023 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
2//
3// SPDX-License-Identifier: GPL-2.0-only
4
5#include "nr-fh-control.h"
6
7#include "ns3/core-module.h"
8
9namespace ns3
10{
11
12NS_LOG_COMPONENT_DEFINE("NrFhControl");
13NS_OBJECT_ENSURE_REGISTERED(NrFhControl);
14
15static constexpr uint32_t
16Cantor(uint16_t x1, uint16_t x2)
17{
18 return (((x1 + x2) * (x1 + x2 + 1)) / 2) + x2;
19}
20
21TypeId
23{
24 static TypeId tid =
25 TypeId("ns3::NrFhControl")
26 .SetParent<Object>()
27 .AddConstructor<NrFhControl>()
28 .SetGroupName("Nr")
29 .AddAttribute(
30 "FhControlMethod",
31 "The FH Control method defines the model that the fhControl will use"
32 "to limit the capacity. There are four FH Control methods: "
33 "a) Dropping. When CTRL channels are sent, PHY asks the FhControl whether"
34 "the allocation fits. If not, it drops the DCI + data."
35 "b) Postponing. When tdma/ofdma have allocated the RBs/symbols to all the"
36 "UEs, it iterates through all the UEs and asks the FhControl whether the"
37 "allocation fits. If not, it sets the assigned RBGs to 0 and therefore the"
38 "sending of the data is postponed (DCI is not created – data stays in RLC queue)"
39 "c) Optimize MCS. When tdma/ofdma have allocated the RBs/symbols to all the UEs,"
40 "it iterates through all the UEs (with data in their queues and resources"
41 "allocated during the scheduling process) and asks fhControl for the max MCS."
42 "It assigns the min among the allocated one and the max MCS."
43 "d) Optimize RBs. When tdma/ofdma are allocating the RBs/symbols to a UE,"
44 "it calls the fhControl to provide the max RBs that can be assigned.",
45 EnumValue(NrFhControl::Dropping),
46 MakeEnumAccessor<FhControlMethod>(&NrFhControl::SetFhControlMethod,
47 &NrFhControl::GetFhControlMethod),
48 MakeEnumChecker(NrFhControl::Dropping,
49 "Dropping",
51 "Postponing",
53 "OptimizeMcs",
55 "OptimizeRBs"))
56 .AddAttribute("FhCapacity",
57 "The available fronthaul capacity (in Mbps)."
58 "The capacity is shared among the active BWPs"
59 "of a cell.",
60 UintegerValue(1000),
61 MakeUintegerAccessor(&NrFhControl::SetCellFhCapacity),
62 MakeUintegerChecker<uint32_t>(0, 150000))
63 .AddAttribute("OverheadDyn",
64 "The overhead for dynamic adaptation (in bits)",
65 UintegerValue(32),
66 MakeUintegerAccessor(&NrFhControl::SetOverheadDyn),
67 MakeUintegerChecker<uint8_t>(0, 100))
68 .AddTraceSource(
69 "RequiredFhDlThroughput",
70 "Report required fronthaul throughput in DL per BWP (Sfnfn, bwpId, reqFhThr)",
71 MakeTraceSourceAccessor(&NrFhControl::m_reqFhDlThrTrace),
72 "ns3::ReqFhDlThr::TracedCallback")
73 .AddTraceSource(
74 "UsedAirRbs",
75 "Report the employed RBs of the air interface in DL per BWP (Sfnfn, bwpId, rbsAir)",
76 MakeTraceSourceAccessor(&NrFhControl::m_rbsAirTrace),
77 "ns3::rbsAir::TracedCallback");
78 return tid;
79}
80
82 : m_physicalCellId(0),
83 m_fhPhySapUser{},
84 m_fhSchedSapUser{}
85{
86 NS_LOG_FUNCTION(this);
87 m_fhPhySapProvider = new MemberNrFhPhySapProvider<NrFhControl>(this);
88 m_fhSchedSapProvider = new MemberNrFhSchedSapProvider<NrFhControl>(this);
89}
90
92{
93 delete m_fhPhySapProvider;
94 delete m_fhSchedSapProvider;
95}
96
97void
99{
100 NS_LOG_FUNCTION(this << s);
101 std::map<uint16_t, NrFhPhySapUser*>::iterator it = m_fhPhySapUser.find(bwpId);
102
103 if (it != m_fhPhySapUser.end())
104 {
105 NS_FATAL_ERROR("Tried to allocated an existing bwpId");
106 }
107 if (it == m_fhPhySapUser.end())
108 {
109 m_fhPhySapUser.insert(std::pair<uint8_t, NrFhPhySapUser*>(bwpId, s));
110 }
111}
112
115{
116 NS_LOG_FUNCTION(this);
117
118 return m_fhPhySapProvider;
119}
120
121void
123{
124 NS_LOG_FUNCTION(this << s);
125
126 std::map<uint16_t, NrFhSchedSapUser*>::iterator it = m_fhSchedSapUser.find(bwpId);
127
128 if (it != m_fhSchedSapUser.end())
129 {
130 NS_FATAL_ERROR("Tried to allocated an existing bwpId");
131 }
132 if (it == m_fhSchedSapUser.end())
133 {
134 m_fhSchedSapUser.insert(std::pair<uint8_t, NrFhSchedSapUser*>(bwpId, s));
135 }
136}
137
140{
141 NS_LOG_FUNCTION(this);
142 return m_fhSchedSapProvider;
143}
144
145void
147{
148 NS_LOG_FUNCTION(this);
149 NS_LOG_DEBUG("Set the Fh Control Method to: " << model);
150 m_fhControlMethod = model;
151}
152
154NrFhControl::GetFhControlMethod() const
155{
156 NS_LOG_FUNCTION(this);
157 return m_fhControlMethod;
158}
159
160uint8_t
161NrFhControl::DoGetFhControlMethod() const
162{
163 return m_fhControlMethod;
164}
165
166void
168{
169 NS_LOG_FUNCTION(this);
170 m_fhCapacity = capacity;
171}
172
173void
175{
176 NS_LOG_FUNCTION(this);
177 m_overheadDyn = overhead;
178}
179
180void
181NrFhControl::SetErrorModelType(std::string errorModelType)
182{
183 m_errorModelType = errorModelType;
184
185 if (m_errorModelType == "ns3::NrEesmIrT1" || m_errorModelType == "ns3::NrEesmCcT1")
186 {
187 m_mcsTable = 1;
188 }
189 else if (m_errorModelType == "ns3::NrEesmIrT2" || m_errorModelType == "ns3::NrEesmCcT2")
190 {
191 m_mcsTable = 2;
192 }
193 else
194 {
195 NS_ABORT_MSG(
196 "Wrong error model type. To use NrFhControl, one of the Nr error models should be set."
197 "Please select among: ns3::NrEesmIrT1, ns3::NrEesmCcT1 for MCS Table 1 and"
198 "ns3::NrEesmIrT2 and ns3::NrEesmCcT2 for MCS Table 2");
199 }
200}
201
202void
203NrFhControl::SetPhysicalCellId(uint16_t physicalCellId)
204{
205 NS_LOG_FUNCTION(this);
206 m_physicalCellId = physicalCellId;
207 NS_LOG_DEBUG("NrFhControl initialized for cell Id: " << m_physicalCellId);
208}
209
210uint16_t
211NrFhControl::DoGetPhysicalCellId() const
212{
213 return m_physicalCellId;
214}
215
216void
217NrFhControl::SetFhNumerology(uint16_t bwpId, uint16_t num)
218{
219 if (m_numerologyPerBwp.find(bwpId) == m_numerologyPerBwp.end()) // bwpId not in the map
220 {
221 m_numerologyPerBwp.insert(std::make_pair(bwpId, num));
222 SfnSf waitingSlot = {0, 0, 0, static_cast<uint8_t>(num)};
223 m_waitingSlotPerBwp.insert(std::make_pair(bwpId, waitingSlot));
224 NS_LOG_DEBUG("Cell: " << m_physicalCellId << " BWP: " << bwpId << " num: " << num);
225 }
226 else
227 {
228 NS_ABORT_MSG("Configure NrFhControl should be called only once");
229 }
230}
231
232void
233NrFhControl::DoSetActiveUe(uint16_t bwpId, uint16_t rnti, uint32_t bytes)
234{
235 if (m_activeUesPerBwp.find(bwpId) == m_activeUesPerBwp.end())
236 {
237 NS_LOG_DEBUG("Creating m_activeUesPerBwp entry for bwpId: " << bwpId);
238 m_activeUesPerBwp[bwpId] = {};
239 }
240 NS_LOG_DEBUG("Creating m_activeUesPerBwp entry for bwpId: " << bwpId << " and rnti: " << rnti);
241 m_activeUesPerBwp.at(bwpId).emplace(rnti);
242
243 uint32_t c1 = Cantor(bwpId, rnti);
244 if (m_rntiQueueSize.find(c1) == m_rntiQueueSize.end()) // UE not in the map
245 {
246 NS_LOG_DEBUG("Cell: " << m_physicalCellId << " Creating pair " << c1 << " for bwpId: "
247 << bwpId << " and rnti: " << rnti << " with bytes: " << bytes);
248
249 m_rntiQueueSize.insert(std::make_pair(c1, bytes));
250 }
251 else
252 {
253 NS_LOG_DEBUG("Cell: " << m_physicalCellId << " Updating pair " << c1 << " for bwpId: "
254 << bwpId << " and rnti: " << rnti << " with bytes: " << bytes);
255 m_rntiQueueSize.at(c1) = bytes;
256 }
257}
258
259void
260NrFhControl::DoSetActiveHarqUes(uint16_t bwpId, uint16_t rnti)
261{
262 if (m_activeHarqUesPerBwp.find(bwpId) == m_activeHarqUesPerBwp.end()) // UE not in the map
263 {
264 NS_LOG_DEBUG("Creating m_activeHarqUesPerBwp entry for bwpId: " << bwpId);
265 m_activeHarqUesPerBwp[bwpId] = {};
266 }
267 NS_LOG_DEBUG("Creating m_activeHarqUesPerBwp entry for bwpId: " << bwpId
268 << " and rnti: " << rnti);
269 m_activeHarqUesPerBwp.at(bwpId).emplace(rnti);
270}
271
272void
273NrFhControl::DoUpdateActiveUesMap(
274 uint16_t bwpId,
275 const std::deque<VarTtiAllocInfo>& allocation,
276 const std::unordered_map<uint16_t, std::shared_ptr<NrMacSchedulerUeInfo>>& ueMap)
277{
278 for (const auto& alloc : allocation)
279 {
280 if (alloc.m_dci->m_type != DciInfoElementTdma::DATA ||
281 alloc.m_dci->m_format == DciInfoElementTdma::UL)
282 {
283 continue;
284 }
285
286 uint16_t rnti = alloc.m_dci->m_rnti;
287 uint32_t c1 = Cantor(bwpId, rnti);
288 uint32_t numRbs =
289 static_cast<uint32_t>(
290 std::count(alloc.m_dci->m_rbgBitmask.begin(), alloc.m_dci->m_rbgBitmask.end(), 1)) *
291 static_cast<uint32_t>(m_fhSchedSapUser.at(bwpId)->GetNumRbPerRbgFromSched());
292
293 NS_LOG_INFO("Cell: " << m_physicalCellId << " We got called for Update for bwpId: " << bwpId
294 << " RNTI: " << rnti);
295
296 // Create/Update FH DL Throughput per BWP
297 uint64_t fhDlThr = GetFhThr(bwpId,
298 static_cast<uint32_t>(alloc.m_dci->m_mcs),
299 static_cast<uint32_t>(alloc.m_dci->m_numSym) * numRbs,
300 alloc.m_dci->m_rank);
301 if (m_reqFhDlThrTracedValuePerBwp.find(bwpId) == m_reqFhDlThrTracedValuePerBwp.end())
302 {
303 NS_LOG_DEBUG("Create pair for m_reqFhDlThrTracedValuePerBwp.at(" << bwpId
304 << "): " << fhDlThr);
305 }
306 m_reqFhDlThrTracedValuePerBwp[bwpId] += fhDlThr;
307 NS_LOG_DEBUG("Update m_reqFhDlThrTracedValuePerBwp.at("
308 << bwpId << "): " << m_reqFhDlThrTracedValuePerBwp.at(bwpId));
309
310 // Create/Update used RBs of the air of a specific bwpId (AI Trace)
311 if (m_rbsAirTracedValue.find(bwpId) == m_rbsAirTracedValue.end()) // bwp not in the map
312 {
313 NS_LOG_DEBUG("Create pair for m_rbsAirTracedValue.at(" << bwpId << "): " << numRbs
314 << " RBs");
315 }
316 m_rbsAirTracedValue[bwpId] += numRbs;
317 NS_LOG_DEBUG("Update m_rbsAirTracedValue.at(" << bwpId << ")" << m_rbsAirTracedValue[bwpId]
318 << " RBs");
319
320 if (alloc.m_dci->m_ndi == 0) // retx
321 {
322 NS_LOG_DEBUG("Retransmission, update only m_activeHarqUesPerBwp");
323 if (m_activeHarqUesPerBwp.find(rnti) != m_activeHarqUesPerBwp.end())
324 {
325 m_activeHarqUesPerBwp.at(bwpId).erase(rnti);
326 NS_LOG_DEBUG("Update m_activeHarqBwps map for bwpId: "
327 << bwpId << " with: " << m_activeHarqUesPerBwp.at(bwpId).size()
328 << " UEs");
329 if (m_activeHarqUesPerBwp.at(bwpId).empty())
330 {
331 NS_LOG_DEBUG(
332 "Remove BWP from m_activeHarqBwps because we served all its HARQ UEs");
333 m_activeHarqUesPerBwp.erase(bwpId);
334 }
335 }
336 continue;
337 }
338
339 // update stored maps
340 if (m_rntiQueueSize.empty())
341 {
342 NS_LOG_DEBUG("empty MAP");
343 NS_ABORT_MSG_IF(!m_activeUesPerBwp.at(bwpId).empty(),
344 "No UE in map, but something in activeUes map");
345 continue;
346 }
347
348 if (ueMap.find(rnti) != ueMap.end())
349 {
350 uint32_t totBuffer = ueMap.at(rnti)->GetTotalDlBuffer();
351 if (totBuffer > 0)
352 {
353 m_rntiQueueSize.at(c1) = totBuffer;
354 NS_LOG_DEBUG("Updating queue size for bwpId: " << bwpId << " RNTI: " << rnti
355 << " to " << m_rntiQueueSize.at(c1)
356 << " bytes");
357 }
358 else
359 {
360 NS_LOG_INFO(
361 "Removing UE because we served it. RLC queue size: " << m_rntiQueueSize.at(c1));
362 m_rntiQueueSize.erase(c1);
363 m_activeUesPerBwp.at(bwpId).erase(rnti);
364 NS_LOG_DEBUG("Update ActiveBwps map for bwpId: "
365 << bwpId << " with: " << m_activeUesPerBwp.at(bwpId).size() << " UEs");
366
367 if (m_activeUesPerBwp.at(bwpId).empty())
368 {
369 NS_LOG_DEBUG("Remove BWP from Active BWPs because we served all its UEs");
370 m_activeUesPerBwp.erase(bwpId);
371 }
372 }
373 }
374 else
375 {
376 NS_ABORT_MSG("UE not in the map, but has an allocation");
377 }
378 }
379}
380
381uint16_t
382NrFhControl::GetNumberActiveUes(uint16_t bwpId) const
383{
384 return m_activeUesPerBwp.at(bwpId).size();
385}
386
387uint16_t
388NrFhControl::GetNumberActiveBwps() const
389{
390 // BWPs with active UEs with new data
391 uint16_t numActiveBwps = m_activeUesPerBwp.size();
392 for (auto& it : m_activeHarqUesPerBwp)
393 {
394 // If there is an active BWP with active HARQ UE(s)
395 // and it is not included in the active BWPs list
396 if (m_activeUesPerBwp.find(it.first) == m_activeUesPerBwp.end())
397 {
398 numActiveBwps++; // increment the number of active BWPs
399 }
400 }
401 NS_LOG_DEBUG("Number of active BWPs calculated: " << numActiveBwps);
402 return numActiveBwps;
403}
404
405bool
406NrFhControl::DoGetDoesAllocationFit(uint16_t bwpId, uint32_t mcs, uint32_t nRegs, uint8_t dlRank)
407{
408 NS_LOG_INFO("NrFhControl::DoGetDoesAllocationFit for cell: " << m_physicalCellId << " bwpId: "
409 << bwpId << " mcs: " << mcs
410 << " nRegs: " << nRegs);
411 uint16_t numOfActiveBwps =
412 GetNumberActiveBwps(); // considers only active BWPs with data in queue
413
414 NS_LOG_DEBUG("Number of active BWPs in DoesAllocFit: " << numOfActiveBwps);
415
416 if (numOfActiveBwps == 0) // if we are at this point with numBWPs=0, it means there are
417 // BWPs with just remaining HARQ allocations
418 {
419 numOfActiveBwps++;
420 }
421 uint64_t thr = GetFhThr(
422 bwpId,
423 mcs,
424 nRegs * static_cast<uint32_t>(m_fhSchedSapUser.at(bwpId)->GetNumRbPerRbgFromSched()),
425 dlRank);
426
427 if (m_allocThrPerBwp.find(bwpId) == m_allocThrPerBwp.end()) // bwpId not in the map
428 {
429 if (thr < (m_fhCapacity / static_cast<uint32_t>(numOfActiveBwps) * 1e6))
430 {
431 m_allocThrPerBwp.insert(std::make_pair(bwpId, thr));
432 NS_LOG_DEBUG("BWP not in the map, Allocation can be included. BWP Thr: "
433 << m_allocThrPerBwp.at(bwpId));
434 return true;
435 }
436 NS_LOG_DEBUG("BWP not in the map, Allocation cannot be included");
437 return false;
438 } // bwp in the map & we can store the allocation
439 if ((m_allocThrPerBwp[bwpId] + thr) <
440 (m_fhCapacity / static_cast<uint32_t>(numOfActiveBwps) * 1e6))
441 {
442 m_allocThrPerBwp[bwpId] += thr;
443 NS_LOG_DEBUG(
444 "BWP in the map, Allocation can be included. BWP Thr: " << m_allocThrPerBwp.at(bwpId));
445 return true;
446 }
447 NS_LOG_INFO("BWP in the map, Allocation cannot be included");
448 return false;
449}
450
451uint8_t
452NrFhControl::DoGetMaxMcsAssignable(uint16_t bwpId, uint32_t reg, uint32_t rnti, uint8_t dlRank)
453{
454 uint16_t numOfActiveBwps =
455 GetNumberActiveBwps(); // considers only active BWPs with data in queue
456 NS_ASSERT_MSG(numOfActiveBwps > 0, "No Active BWPs, sth is wrong");
457 uint32_t availableCapacity = m_fhCapacity / static_cast<uint32_t>(numOfActiveBwps);
458
459 uint16_t numActiveUes = GetNumberActiveUes(bwpId);
460 NS_LOG_INFO("BwpId: " << bwpId << " Number of Active UEs: " << numActiveUes);
461 uint16_t Kp = numActiveUes;
462
463 Time slotLength = MicroSeconds(
464 static_cast<uint16_t>(1000 / std::pow(2, m_numerologyPerBwp.at(bwpId)))); // slot length
465 uint32_t overheadMac = static_cast<uint32_t>(
466 10e6 * 1e-3 /
467 std::pow(2, m_numerologyPerBwp.at(bwpId))); // bits (10e6 (bps) x slot length (in s))
468
469 if (availableCapacity * 1e6 * slotLength.GetSeconds() <=
470 numActiveUes * m_overheadDyn + numActiveUes * overheadMac + numActiveUes * 12 * 2 * 10)
471 {
472 while (availableCapacity * 1e6 * slotLength.GetSeconds() <=
473 Kp * m_overheadDyn + Kp * overheadMac + Kp * 12 * 2 * 10)
474 {
475 Kp--;
476 }
477 }
478 NS_ABORT_MSG_IF(availableCapacity * 1e6 * slotLength.GetSeconds() <=
479 Kp * m_overheadDyn + Kp * overheadMac + Kp * 12 * 2 * 10,
480 "Not enough fronthaul capacity to send intra-PHY split overhead");
481
482 uint32_t num = static_cast<uint32_t>(availableCapacity * 1e6 * slotLength.GetSeconds() -
483 Kp * m_overheadDyn - Kp * overheadMac - Kp * 12 * 2 * 10);
484 if (Kp == 0)
485 {
486 return 0;
487 }
488 uint16_t modOrderMax =
489 num / (12 * Kp * reg * dlRank) /
490 static_cast<uint32_t>(
491 m_fhSchedSapUser.at(bwpId)
492 ->GetNumRbPerRbgFromSched()); // REGs, otherwise, should divide by nSymb
493 uint8_t mcsMax = GetMaxMcs(m_mcsTable, modOrderMax); // MCS max
494
495 NS_ABORT_MSG_IF(mcsMax == 0, "could not compute correctly the maxMCS");
496
497 return mcsMax;
498}
499
500uint32_t
501NrFhControl::DoGetMaxRegAssignable(uint16_t bwpId, uint32_t mcs, uint32_t rnti, uint8_t dlRank)
502{
503 uint32_t modulationOrder =
504 m_mcsTable == 1 ? GetModulationOrderTable1(mcs) : GetModulationOrderTable2(mcs);
505
506 uint16_t numOfActiveBwps =
507 GetNumberActiveBwps(); // considers only active BWPs with data in queue
508 NS_ASSERT_MSG(numOfActiveBwps > 0, "No Active BWPs, sth is wrong");
509 uint32_t availableCapacity = m_fhCapacity / static_cast<uint32_t>(numOfActiveBwps);
510
511 uint16_t numActiveUes = GetNumberActiveUes(bwpId);
512 NS_LOG_INFO("BwpId: " << bwpId << " Number of Active UEs: " << numActiveUes);
513 uint16_t Kp = numActiveUes;
514
515 Time slotLength = MicroSeconds(
516 static_cast<uint16_t>(1000 / std::pow(2, m_numerologyPerBwp.at(bwpId)))); // slot length
517 uint32_t overheadMac = static_cast<uint32_t>(
518 10e6 * 1e-3 /
519 std::pow(2, m_numerologyPerBwp.at(bwpId))); // bits (10e6 (bps) x slot length (in s))
520
521 if (availableCapacity * 1e6 * slotLength.GetSeconds() <=
522 numActiveUes * m_overheadDyn + numActiveUes * overheadMac + numActiveUes * 12 * 2 * 10)
523 {
524 while (availableCapacity * 1e6 * slotLength.GetSeconds() <=
525 Kp * m_overheadDyn + Kp * overheadMac + Kp * 12 * 2 * 10)
526 {
527 Kp--;
528 }
529 }
530 NS_ABORT_MSG_IF(availableCapacity * 1e6 * slotLength.GetSeconds() <=
531 Kp * m_overheadDyn + Kp * overheadMac + Kp * 12 * 2 * 10,
532 "Not enough fronthaul capacity to send intra-PHY split overhead");
533
534 uint32_t num = static_cast<uint32_t>(availableCapacity * 1e6 * slotLength.GetSeconds() -
535 Kp * m_overheadDyn - Kp * overheadMac - Kp * 12 * 2 * 10);
536 if (Kp == 0)
537 {
538 return 0;
539 }
540 uint32_t nMax =
541 num / (12 * Kp * modulationOrder * dlRank) /
542 static_cast<uint32_t>(
543 m_fhSchedSapUser.at(bwpId)
544 ->GetNumRbPerRbgFromSched()); // in REGs, otherwise, should divide by nSymb
545
546 NS_LOG_DEBUG("Scheduler GetMaxRegAssignable " << nMax << " for UE " << rnti << " with mcs "
547 << mcs);
548
549 return nMax;
550}
551
552void
553NrFhControl::DoUpdateTracesBasedOnDroppedData(uint16_t bwpId,
554 uint32_t mcs,
555 uint32_t nRbgs,
556 uint32_t nSymb,
557 uint8_t dlRank)
558{
559 // in Dropping, the trace is computed from PHY layer
560 uint32_t numRbs =
561 nRbgs * static_cast<uint32_t>(m_fhSchedSapUser.at(bwpId)->GetNumRbPerRbgFromSched());
562
563 // update FH trace and AI trace
564 NS_LOG_DEBUG("Update Traces based on Dropped Data");
565 // bwpId not in the map
566 if (m_reqFhDlThrTracedValuePerBwp.find(bwpId) == m_reqFhDlThrTracedValuePerBwp.end())
567 {
568 NS_LOG_DEBUG("Create pair for"
569 << " m_reqFhDlThrTracedValuePerBwp.at(" << bwpId
570 << "): " << GetFhThr(bwpId, mcs, (numRbs * nSymb), dlRank));
571 }
572 m_reqFhDlThrTracedValuePerBwp[bwpId] += GetFhThr(bwpId, mcs, (numRbs * nSymb), dlRank);
573 NS_LOG_DEBUG("Update m_reqFhDlThrTracedValuePerBwp.at("
574 << bwpId << "): " << m_reqFhDlThrTracedValuePerBwp.at(bwpId));
575
576 if (m_rbsAirTracedValue.find(bwpId) == m_rbsAirTracedValue.end()) // bwpId not in the map
577 {
578 NS_LOG_DEBUG("Create pair for m_rbsAirTracedValue.at(" << bwpId << "): " << numRbs
579 << " RBs");
580 }
581 m_rbsAirTracedValue[bwpId] += numRbs;
582 NS_LOG_DEBUG("Update m_rbsAirTracedValue.at(" << bwpId << "): " << m_rbsAirTracedValue.at(bwpId)
583 << " RBs");
584}
585
586void
587NrFhControl::DoNotifyEndSlot(uint16_t bwpId, SfnSf currentSlot)
588{
589 // we only want to save it once (per slot) in the trace. Note that every cell that calls EndSlot
590 if (currentSlot == m_waitingSlotPerBwp.at(bwpId))
591 {
592 NS_LOG_INFO(currentSlot);
593
594 // store SfnSf and required FH thr (in DL)
595 if (m_reqFhDlThrTracedValuePerBwp.find(bwpId) == m_reqFhDlThrTracedValuePerBwp.end())
596 {
597 m_reqFhDlThrTrace(currentSlot, m_physicalCellId, bwpId, 0);
598 NS_LOG_DEBUG("Size 0, bwpId: " << bwpId << " FH DL Throughput 0");
599 }
600 else
601 {
602 NS_LOG_INFO("Req FH DL thr at end slot for m_reqFhDlThrTracedValuePerBwp.at("
603 << bwpId << "): " << m_reqFhDlThrTracedValuePerBwp.at(bwpId));
604 m_reqFhDlThrTrace(currentSlot,
605 m_physicalCellId,
606 bwpId,
607 m_reqFhDlThrTracedValuePerBwp.at(
608 bwpId)); // store SfnSf, bwpId and required FH thr (in DL)
609 }
610
611 uint32_t rbSum = 0;
612 if (m_rbsAirTracedValue.empty())
613 {
614 m_rbsAirTrace(currentSlot,
615 m_physicalCellId,
616 bwpId,
617 rbSum); // store SfnSf, bwpId and 0 used RBs
618 NS_LOG_DEBUG("Size 0, bwpId: " << bwpId
619 << " Average RBs used at the end of slot: " << rbSum);
620 }
621 else
622 {
623 for (std::pair<uint16_t, uint32_t> element : m_rbsAirTracedValue)
624 {
625 rbSum += element.second;
626 }
627 rbSum = rbSum / m_rbsAirTracedValue.size();
628 m_rbsAirTrace(currentSlot,
629 m_physicalCellId,
630 bwpId,
631 rbSum); // store SfnSf, bwpId and AVERAGE used RBs
632 NS_LOG_DEBUG("Average RBs used at the end of slot: " << rbSum);
633 }
634
635 NS_LOG_DEBUG("Reset traces for next slot");
636 m_reqFhDlThrTracedValuePerBwp.erase(bwpId);
637 m_rbsAirTracedValue.erase(bwpId);
638 m_allocThrPerCell = 0;
639 m_allocThrPerBwp.erase(bwpId);
640 m_waitingSlotPerBwp.at(bwpId).Add(1);
641 }
642}
643
644uint64_t
645NrFhControl::GetFhThr(uint16_t bwpId, uint32_t mcs, uint32_t nRegs, uint8_t dlRank) const
646{
647 uint64_t thr;
648 uint32_t modulationOrder =
649 m_mcsTable == 1 ? GetModulationOrderTable1(mcs) : GetModulationOrderTable2(mcs);
650
651 uint16_t numerology = m_fhPhySapUser.at(bwpId)->GetNumerology();
652 NS_ASSERT_MSG(numerology == m_numerologyPerBwp.at(bwpId),
653 " Numerology has not been configured properly for bwpId: " << bwpId);
654 Time slotLength =
655 MicroSeconds(static_cast<uint16_t>(1000 / std::pow(2, numerology))); // slot length
656 uint32_t overheadMac = static_cast<uint32_t>(
657 10e6 * 1e-3 / std::pow(2, numerology)); // bits (10e6 (bps) x slot length (in s))
658
659 thr = (12 * modulationOrder * nRegs * dlRank + m_overheadDyn + overheadMac + 12 * 2 * 10) /
660 slotLength.GetSeconds(); // added 10 RBs of DCI overhead over 1 symbol, encoded with QPSK
661
662 return thr;
663}
664
665uint8_t
666NrFhControl::GetMaxMcs(uint8_t mcsTable, uint16_t modOrder) const
667{
668 uint8_t mcsMax = 0;
669 if (mcsTable == 1)
670 {
671 if (modOrder < 4)
672 {
673 mcsMax = GetMcsTable1(0);
674 }
675 else if (modOrder >= 4 && modOrder < 6)
676 {
677 mcsMax = GetMcsTable1(1);
678 }
679 else if (modOrder >= 6)
680 {
681 mcsMax = GetMcsTable1(2);
682 }
683 }
684 else if (m_mcsTable == 2)
685 {
686 if (modOrder < 4)
687 {
688 mcsMax = GetMcsTable2(0);
689 }
690 else if (modOrder >= 4 && modOrder < 6)
691 {
692 mcsMax = GetMcsTable2(1);
693 }
694 else if (modOrder >= 6 && modOrder < 8)
695 {
696 mcsMax = GetMcsTable2(2);
697 }
698 else if (modOrder >= 8)
699 {
700 mcsMax = GetMcsTable2(3);
701 }
702 }
703 return mcsMax;
704}
705
706uint32_t
707NrFhControl::GetModulationOrderTable1(const uint32_t mcs) const
708{
709 std::vector<uint8_t> McsMTable1 = {// QPSK (modulationOrder = 2)
710 2,
711 2,
712 2,
713 2,
714 2,
715 2,
716 2,
717 2,
718 2,
719 2,
720 // 16QAM (modulationOrder = 4)
721 4,
722 4,
723 4,
724 4,
725 4,
726 4,
727 4,
728 // 64QAM (modulationOrder = 6)
729 6,
730 6,
731 6,
732 6,
733 6,
734 6,
735 6,
736 6,
737 6,
738 6,
739 6,
740 6};
741 return McsMTable1.at(mcs);
742}
743
744uint32_t
745NrFhControl::GetModulationOrderTable2(const uint32_t mcs) const
746{
747 NS_ASSERT_MSG(mcs <= 27, "MCS must be up to 27");
748 std::vector<uint8_t> McsMTable2 = {// QPSK (modulationOrder = 2)
749 2,
750 2,
751 2,
752 2,
753 2,
754 // 16QAM (modulationOrder = 4)
755 4,
756 4,
757 4,
758 4,
759 4,
760 4,
761 // 64QAM (modulationOrder = 6)
762 6,
763 6,
764 6,
765 6,
766 6,
767 6,
768 6,
769 6,
770 6,
771 // 256QAM (modulationOrder = 8)
772 8,
773 8,
774 8,
775 8,
776 8,
777 8,
778 8,
779 8};
780 return McsMTable2.at(mcs);
781}
782
783uint8_t
784NrFhControl::GetMcsTable1(const uint8_t modOrd) const
785{
786 std::vector<uint8_t> McsTable1 = {// QPSK (modulationOrder = 2)
787 9,
788 // 16QAM (modulationOrder = 4)
789 16,
790 // 64QAM (modulationOrder = 6)
791 28};
792 return McsTable1.at(modOrd);
793}
794
795uint8_t
796NrFhControl::GetMcsTable2(const uint8_t modOrd) const
797{
798 std::vector<uint8_t> McsTable2 = {// QPSK (modulationOrder = 2)
799 4,
800 // 16QAM (modulationOrder = 4)
801 10,
802 // 64QAM (modulationOrder = 6)
803 19,
804 // 256QAM (modulationOrder = 8)
805 27};
806 return McsTable2.at(modOrd);
807}
808
809} // namespace ns3
friend class MemberNrFhSchedSapProvider< NrFhControl >
let the forwarder class access the protected and private members
void SetNrFhPhySapUser(uint16_t bwpId, NrFhPhySapUser *s)
Set the Fh control - PHY SAP User PHY is per bwp as such we store in a map the bwpId and the correspo...
NrFhPhySapProvider * GetNrFhPhySapProvider()
Get the Fh control - PHY SAP User ptr.
void SetCellFhCapacity(uint32_t capacity)
Set the available fronthaul capacity of the cell. Notice that throughout the code,...
void SetErrorModelType(std::string errorModelType)
Set the ErrorModelType based on which the MCS Table (1 or 2) will be set." "ns3::NrEesmIrT1 an...
~NrFhControl() override
~NrFhControl deconstructor
NrFhControl()
NrFhControl constructor.
friend class MemberNrFhPhySapProvider< NrFhControl >
let the forwarder class access the protected and private members
NrFhSchedSapProvider * GetNrFhSchedSapProvider()
Get the Fh control - Sched SAP User ptr.
void SetOverheadDyn(uint8_t overhead)
Set the overhead for dynamic modulation compression.
FhControlMethod
The optimization models (FH Control method) of the NrFhControl.
@ Postponing
Postpone sending data (MAC Layer)
@ OptimizeRBs
Optimize RBs allocated.
@ Dropping
Drop DCI + DATA at the PHY Layer.
@ OptimizeMcs
Optimize MCS.
static TypeId GetTypeId()
GetTypeId.
void SetPhysicalCellId(uint16_t physCellId)
Set the physical cell Id of the cell to which this NrFhControl instance belongs to.
void SetFhNumerology(uint16_t bwpId, uint16_t num)
Set the numerology.
void SetFhControlMethod(FhControlMethod model)
Set the FH Control method type.
void SetNrFhSchedSapUser(uint16_t bwpId, NrFhSchedSapUser *s)
Set the Fh control - Sched SAP User Sched is per bwp as such we store in a map the bwpId and the corr...
Service Access Point (SAP) offered by the FhControl instance to the gnb PHY instance.
Service Access Point (SAP) offered by the gnb PHY instance to the FhControl instance.
Service Access Point (SAP) offered by the FhControl instance to the MAC Scheduler instance.
Service Access Point (SAP) offered by the MAC Scheduler instance to the FhControl instance.
The SfnSf class.
Definition sfnsf.h:32
@ DATA
Used for DL/UL DATA.