5G-LENA nr-v3.3-161-gad18933f
The 5G/NR module for the ns-3 simulator
Loading...
Searching...
No Matches
nr-amc.cc
1// Copyright (c) 2019 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
2//
3// SPDX-License-Identifier: GPL-2.0-only
4
5#include "nr-amc.h"
6
7#include "lena-error-model.h"
8#include "nr-error-model.h"
9#include "nr-lte-mi-error-model.h"
10
11#include "ns3/double.h"
12#include "ns3/enum.h"
13#include "ns3/log.h"
14#include "ns3/math.h"
15#include "ns3/nr-spectrum-value-helper.h"
16#include "ns3/object-factory.h"
17#include "ns3/uinteger.h"
18
19namespace ns3
20{
21
22NS_LOG_COMPONENT_DEFINE("NrAmc");
23NS_OBJECT_ENSURE_REGISTERED(NrAmc);
24
26{
27 NS_LOG_INFO("Initialize AMC module");
28}
29
31{
32}
33
34void
36{
37 NS_LOG_FUNCTION(this);
38 m_emMode = NrErrorModel::DL;
39}
40
41void
43{
44 NS_LOG_FUNCTION(this);
45 m_emMode = NrErrorModel::UL;
46}
47
48TypeId
50{
51 static TypeId tid =
52 TypeId("ns3::NrAmc")
53 .SetParent<Object>()
54 .AddAttribute("NumRefScPerRb",
55 "Number of Subcarriers carrying Reference Signals per RB",
56 UintegerValue(1),
57 MakeUintegerAccessor(&NrAmc::SetNumRefScPerRb, &NrAmc::GetNumRefScPerRb),
58 MakeUintegerChecker<uint8_t>(0, 12))
59 .AddAttribute("AmcModel",
60 "AMC model used to assign CQI",
61 EnumValue(NrAmc::ErrorModel),
62 MakeEnumAccessor<AmcModel>(&NrAmc::SetAmcModel, &NrAmc::GetAmcModel),
63 MakeEnumChecker(NrAmc::ErrorModel,
64 "ErrorModel",
66 "ShannonModel"))
67 .AddAttribute("ErrorModelType",
68 "Type of the Error Model to use when AmcModel is set to ErrorModel. "
69 "This parameter has to match the ErrorModelType in nr-spectrum-model,"
70 "because they need to refer to same MCS tables and indexes",
71 TypeIdValue(NrLteMiErrorModel::GetTypeId()),
73 MakeTypeIdChecker())
74 .AddConstructor<NrAmc>();
75 return tid;
76}
77
78uint8_t
79NrAmc::GetMcsFromCqi(uint8_t cqi) const
80{
81 NS_LOG_FUNCTION(cqi);
82 NS_ASSERT_MSG(cqi >= 0 && cqi <= 15, "CQI must be in [0..15] = " << cqi);
83
84 if (m_cachedCqiToMcsMap.find(cqi) == m_cachedCqiToMcsMap.end())
85 {
86 double spectralEfficiency = m_errorModel->GetSpectralEfficiencyForCqi(cqi);
87 uint8_t mcs = 0;
88
89 while ((mcs < m_errorModel->GetMaxMcs()) &&
90 (m_errorModel->GetSpectralEfficiencyForMcs(mcs + 1) <= spectralEfficiency))
91 {
92 ++mcs;
93 }
94 NS_LOG_LOGIC("mcs = " << mcs);
95 m_cachedCqiToMcsMap[cqi] = mcs;
96 }
97
98 return m_cachedCqiToMcsMap.at(cqi);
99}
100
101uint8_t
103{
104 NS_LOG_FUNCTION(this);
105 return m_numRefScPerRb;
106}
107
108void
110{
111 NS_LOG_FUNCTION(this);
112 m_numRefScPerRb = nref;
113}
114
115uint32_t
116NrAmc::CalculateTbSize(uint8_t mcs, uint8_t rank, uint32_t nprb) const
117{
118 NS_LOG_FUNCTION(this << static_cast<uint32_t>(mcs));
119
120 NS_ASSERT_MSG(mcs <= m_errorModel->GetMaxMcs(),
121 "MCS=" << static_cast<uint32_t>(mcs) << " while maximum MCS is "
122 << static_cast<uint32_t>(m_errorModel->GetMaxMcs()));
123
124 uint32_t payloadSize = GetPayloadSize(mcs, rank, nprb);
125 uint32_t tbSize = payloadSize;
126
127 if (m_errorModelType != LenaErrorModel::GetTypeId())
128 {
129 if (payloadSize >= m_crcLen)
130 {
131 tbSize =
132 payloadSize - m_crcLen; // subtract parity bits of m_crcLen used in transport block
133 }
134 uint32_t cbSize =
135 m_errorModel->GetMaxCbSize(payloadSize,
136 mcs); // max size of a code block (including m_crcLen)
137 if (tbSize > cbSize) // segmentation of the transport block occurs
138 {
139 double C = ceil(tbSize / cbSize);
140 tbSize = payloadSize - static_cast<uint32_t>(
141 C * m_crcLen); // subtract bits of m_crcLen used in code
142 // blocks, in case of code block segmentation
143 }
144 }
145
146 NS_LOG_INFO(" mcs:" << (unsigned)mcs << " TB size:" << tbSize);
147
148 return tbSize;
149}
150
151uint32_t
152NrAmc::GetPayloadSize(uint8_t mcs, uint8_t rank, uint32_t nprb) const
153{
154 return m_errorModel->GetPayloadSize(NrSpectrumValueHelper::SUBCARRIERS_PER_RB -
156 mcs,
157 rank,
158 nprb,
159 m_emMode);
160}
161
162uint8_t
163NrAmc::CreateCqiFeedbackSiso(const SpectrumValue& sinr, uint8_t& mcs) const
164{
165 NS_LOG_FUNCTION(this);
166
167 // produces a single CQI/MCS value
168
169 // std::vector<int> cqi;
170 uint8_t cqi = 0;
171 double seAvg = 0;
172
173 Values::const_iterator it;
174 if (m_amcModel == ShannonModel)
175 {
176 // use shannon model
177 double m_ber = GetBer(); // Shannon based model reference BER
178 uint32_t rbNum = 0;
179 for (it = sinr.ConstValuesBegin(); it != sinr.ConstValuesEnd(); it++)
180 {
181 double sinr_ = (*it);
182 if (sinr_ == 0.0)
183 {
184 // cqi.push_back (-1); // SINR == 0 (linear units) means no signal in this RB
185 }
186 else
187 {
188 auto s = GetSpectralEfficiencyForSinr(sinr_);
189 seAvg += s;
190
191 int cqi_ = GetCqiFromSpectralEfficiency(s);
192 rbNum++;
193
194 NS_LOG_LOGIC(" PRB =" << sinr.GetSpectrumModel()->GetNumBands() << ", sinr = "
195 << sinr_ << " (=" << 10 * std::log10(sinr_) << " dB)"
196 << ", spectral efficiency =" << s << ", CQI = " << cqi_
197 << ", BER = " << m_ber);
198 // cqi.push_back (cqi_);
199 }
200 }
201 if (rbNum != 0)
202 {
203 seAvg /= rbNum;
204 }
205 cqi = GetCqiFromSpectralEfficiency(seAvg); // ceil (cqiAvg);
206 mcs = GetMcsFromSpectralEfficiency(seAvg); // ceil(mcsAvg);
207 }
208 else if (m_amcModel == ErrorModel)
209 {
210 std::vector<int> rbMap;
211 int rbId = 0;
212 for (it = sinr.ConstValuesBegin(); it != sinr.ConstValuesEnd(); it++)
213 {
214 if (*it != 0.0)
215 {
216 rbMap.push_back(rbId);
217 }
218 rbId += 1;
219 }
220
221 mcs = 0;
222 Ptr<NrErrorModelOutput> output;
223 while (mcs <= m_errorModel->GetMaxMcs())
224 {
225 uint8_t rank = 1; // This function is SISO only
226 auto tbSize = CalculateTbSize(mcs, rank, rbMap.size());
227 output = m_errorModel->GetTbDecodificationStats(sinr,
228 rbMap,
229 tbSize,
230 mcs,
232 if (output->m_tbler > 0.1)
233 {
234 break;
235 }
236 mcs++;
237 }
238
239 if (mcs > 0)
240 {
241 mcs--;
242 }
243
244 if ((output->m_tbler > 0.1) && (mcs == 0))
245 {
246 cqi = 0;
247 }
248 else if (mcs == m_errorModel->GetMaxMcs())
249 {
250 cqi = 15; // all MCSs can guarantee the 10 % of BER
251 }
252 else
253 {
254 double s = m_errorModel->GetSpectralEfficiencyForMcs(mcs);
255 cqi = 0;
256 while ((cqi < 15) && (m_errorModel->GetSpectralEfficiencyForCqi(cqi + 1) <= s))
257 {
258 ++cqi;
259 }
260 }
261 NS_LOG_DEBUG(this << "\t MCS " << (uint16_t)mcs << "-> CQI " << cqi);
262 }
263 return cqi;
264}
265
266uint8_t
268{
269 NS_LOG_FUNCTION(s);
270 NS_ASSERT_MSG(s >= 0.0, "negative spectral efficiency = " << s);
271 uint8_t cqi = 0;
272 while ((cqi < 15) && (m_errorModel->GetSpectralEfficiencyForCqi(cqi + 1) < s))
273 {
274 ++cqi;
275 }
276 NS_LOG_LOGIC("cqi = " << cqi);
277 return cqi;
278}
279
280uint8_t
282{
283 NS_LOG_FUNCTION(s);
284 NS_ASSERT_MSG(s >= 0.0, "negative spectral efficiency = " << s);
285 uint8_t mcs = 0;
286 while ((mcs < m_errorModel->GetMaxMcs()) &&
287 (m_errorModel->GetSpectralEfficiencyForMcs(mcs + 1) < s))
288 {
289 ++mcs;
290 }
291 NS_LOG_LOGIC("cqi = " << mcs);
292 return mcs;
293}
294
295uint32_t
297{
298 NS_LOG_FUNCTION(this);
299 return m_errorModel->GetMaxMcs();
300}
301
302void
304{
305 NS_LOG_FUNCTION(this);
306 m_amcModel = m;
307}
308
311{
312 NS_LOG_FUNCTION(this);
313 return m_amcModel;
314}
315
316void
317NrAmc::SetErrorModelType(const TypeId& type)
318{
319 NS_LOG_FUNCTION(this);
320 ObjectFactory factory;
321 m_errorModelType = type;
322
323 factory.SetTypeId(m_errorModelType);
324 m_errorModel = DynamicCast<NrErrorModel>(factory.Create());
325 NS_ASSERT(m_errorModel != nullptr);
326 m_cachedCqiToMcsMap.clear(); // clear stale cache
327}
328
329TypeId
331{
332 NS_LOG_FUNCTION(this);
333 return m_errorModelType;
334}
335
336double
337NrAmc::GetBer() const
338{
339 NS_LOG_FUNCTION(this);
342 {
343 return 0.00005; // Value for LTE error model
344 }
345 else
346 {
347 return 0.00001; // Value for NR error model
348 }
349}
350
351NrAmc::McsParams
352NrAmc::GetMaxMcsParams(const NrSinrMatrix& sinrMat, size_t subbandSize) const
353{
354 auto wbMcs = GetMcs(sinrMat);
355 auto wbCqi = GetWbCqiFromMcs(wbMcs);
356 auto sbMcs = GetSbMcs(subbandSize, sinrMat);
357
358 std::vector<uint8_t> sbCqis;
359 sbCqis.resize(sbMcs.size());
360
361 std::transform(sbMcs.begin(), sbMcs.end(), sbCqis.begin(), [&](uint8_t mcs) {
362 return GetWbCqiFromMcs(mcs);
363 });
364
365 auto tbSize = CalcTbSizeForMimoMatrix(wbMcs, sinrMat);
366 return McsParams{wbMcs, wbCqi, sbCqis, tbSize};
367}
368
369std::vector<uint8_t>
370NrAmc::GetSbMcs(const size_t subbandSize, const NrSinrMatrix& sinrMat) const
371{
372 auto nRbs = sinrMat.GetNumRbs();
373 auto nSbs = (nRbs + subbandSize - 1) / subbandSize;
374 auto sbMcs = std::vector<uint8_t>(nSbs, 0);
375
376 size_t lastSubbandSize = sinrMat.GetNumCols() % subbandSize;
377 for (size_t i = 0; i < sbMcs.size(); i++)
378 {
379 auto sinrSb = ExtractSbFromMat(i, i != (nSbs - 1) ? subbandSize : lastSubbandSize, sinrMat);
380 sbMcs[i] = GetMcs(sinrSb);
381 }
382 return sbMcs;
383}
384
386NrAmc::ExtractSbFromMat(const uint8_t sbIndex,
387 const size_t subbandSize,
388 const NrSinrMatrix& sinrMat) const
389{
390 const auto rank = sinrMat.GetNumRows();
391 DoubleMatrixArray sinrSb(rank, sinrMat.GetNumCols());
392 for (uint8_t r = 0; r < rank; r++)
393 {
394 for (size_t iRb = sbIndex * subbandSize; iRb < (sbIndex + 1) * subbandSize; iRb++)
395 {
396 sinrSb(r, iRb) = sinrMat(r, iRb);
397 }
398 }
399 return sinrSb;
400}
401
403NrAmc::CreateSinrMatForSb(const NrSinrMatrix& avgSinrSb, const NrSinrMatrix& sinrMat) const
404{
405 auto sbSinrMat = DoubleMatrixArray{sinrMat.GetNumRows(), sinrMat.GetNumCols()};
406 for (size_t iRb = 0; iRb < sinrMat.GetNumCols(); iRb++)
407 {
408 for (size_t layer = 0; layer < sinrMat.GetNumRows(); layer++)
409 {
410 sbSinrMat(layer, iRb) = avgSinrSb(layer, 0);
411 }
412 }
413
414 return NrSinrMatrix{sbSinrMat};
415}
416
417uint8_t
418NrAmc::GetMcs(const NrSinrMatrix& sinrMat) const
419{
420 auto mcs = uint8_t{0};
421 switch (m_amcModel)
422 {
423 case ShannonModel:
424 NS_ABORT_MSG("ShannonModel is not yet supported");
425 break;
426 case ErrorModel:
427 mcs = GetMaxMcsForErrorModel(sinrMat);
428 break;
429 default:
430 NS_ABORT_MSG("AMC model not supported");
431 break;
432 }
433 return mcs;
434}
435
436uint8_t
437NrAmc::GetMaxMcsForErrorModel(const NrSinrMatrix& sinrMat) const
438{
439 auto mcs = uint8_t{0};
440 while (mcs <= m_errorModel->GetMaxMcs())
441 {
442 auto tbler = CalcTblerForMimoMatrix(mcs, sinrMat);
443 // TODO: Change target TBLER from default 0.1 when using MCS table 3
444 if (tbler > 0.1)
445 {
446 break;
447 }
448 // The current configuration produces a sufficiently low TBLER, try next value
449 mcs++;
450 }
451 if (mcs > 0)
452 {
453 // The loop exited because the MCS exceeded max MCS or because of high TBLER. Reduce MCS
454 mcs--;
455 }
456
457 return mcs;
458}
459
460uint8_t
461NrAmc::GetWbCqiFromMcs(uint8_t mcs) const
462{
463 // Based on OSS CreateCqiFeedbackSiso
464 auto cqi = uint8_t{0};
465 if (mcs == 0)
466 {
467 cqi = 0;
468 }
469 else if (mcs == m_errorModel->GetMaxMcs())
470 {
471 // TODO: define constant for max CQI
472 cqi = 15;
473 }
474 else
475 {
476 auto s = m_errorModel->GetSpectralEfficiencyForMcs(mcs);
477 cqi = 0;
478 // TODO: define constant for max CQI
479 while ((cqi < 15) && (m_errorModel->GetSpectralEfficiencyForCqi(cqi + 1) <= s))
480 {
481 ++cqi;
482 }
483 }
484 return cqi;
485}
486
487double
488NrAmc::CalcTblerForMimoMatrix(uint8_t mcs, const NrSinrMatrix& sinrMat) const
489{
490 auto dummyRnti = uint16_t{0};
491 auto duration = Time{1.0}; // Use an arbitrary non-zero time as the chunk duration
492 auto mimoChunk = MimoSinrChunk{sinrMat, dummyRnti, duration};
493 auto mimoChunks = std::vector<MimoSinrChunk>{mimoChunk};
494 auto rank = sinrMat.GetRank();
495
496 // Create the RB map (indices of used RBs, i.e., indices of RBs where SINR is non-zero)
497 std::vector<int> rbMap{}; // TODO: change type of rbMap from int to size_t
498 int nRbs = static_cast<int>(sinrMat.GetNumRbs());
499 for (int rbIdx = 0; rbIdx < nRbs; rbIdx++)
500 {
501 if (sinrMat(0, rbIdx) != 0.0)
502 {
503 rbMap.push_back(rbIdx);
504 }
505 }
506
507 if (rbMap.empty())
508 {
509 return 1.0;
510 }
511
512 auto tbSize = CalcTbSizeForMimoMatrix(mcs, sinrMat);
513 auto dummyHistory = NrErrorModel::NrErrorModelHistory{}; // Create empty HARQ history
514 auto outputOfEm = m_errorModel->GetTbDecodificationStatsMimo(mimoChunks,
515 rbMap,
516 tbSize,
517 mcs,
518 rank,
519 dummyHistory);
520 return outputOfEm->m_tbler;
521}
522
523uint32_t
524NrAmc::CalcTbSizeForMimoMatrix(uint8_t mcs, const NrSinrMatrix& sinrMat) const
525{
526 auto nRbs = sinrMat.GetNumRbs();
527 auto nRbSyms = nRbs * NR_AMC_NUM_SYMBOLS_DEFAULT;
528 auto rank = sinrMat.GetRank();
529 return CalculateTbSize(mcs, rank, nRbSyms);
530}
531
532double
534{
535 /*
536 * Compute the spectral efficiency from the SINR
537 * SINR
538 * spectralEfficiency = log2 (1 + -------------------- )
539 * -ln(5*BER)/1.5
540 * NB: SINR must be expressed in linear units
541 */
542 double s = log2(1 + (sinr / ((-std::log(5.0 * GetBer())) / 1.5)));
543 return s;
544}
545
546double
548{
549 /*
550 * Compute the spectral efficiency from the SINR
551 * SINR = ((2^spectralEff)-1) * -ln(5*BER)/1.5
552 * NB: SINR must be expressed in linear units
553 */
554 double sinr = (pow(2, spectralEff) - 1) * (-std::log(5.0 * GetBer())) / 1.5;
555 return sinr;
556}
557
558} // namespace ns3
static TypeId GetTypeId()
GetTypeId.
void SetErrorModelType(const TypeId &type)
Set Error model type.
Definition nr-amc.cc:317
void SetAmcModel(AmcModel m)
Set the AMC model type.
Definition nr-amc.cc:303
std::vector< uint8_t > GetSbMcs(const size_t subbandSize, const NrSinrMatrix &sinrMat) const
Find Sb MCS supported for this channel.
Definition nr-amc.cc:370
TypeId GetErrorModelType() const
Get the error model type.
Definition nr-amc.cc:330
uint8_t GetNumRefScPerRb() const
Definition nr-amc.cc:102
NrAmc()
NrAmc constructor.
Definition nr-amc.cc:25
void SetUlMode()
Set the object to be in "UL" mode.
Definition nr-amc.cc:42
double GetSpectralEfficiencyForSinr(double sinr) const
Compute spectral efficient for a given SINR according to Shannon's capacity.
Definition nr-amc.cc:533
static TypeId GetTypeId()
GetTypeId.
Definition nr-amc.cc:49
AmcModel GetAmcModel() const
Get the AMC model type.
Definition nr-amc.cc:310
uint32_t GetPayloadSize(uint8_t mcs, uint8_t rank, uint32_t nprb) const
Calculate the Payload Size (in bytes) from MCS and the number of RB.
Definition nr-amc.cc:152
NrSinrMatrix ExtractSbFromMat(const uint8_t sbIndex, const size_t subbandSize, const NrSinrMatrix &sinrMat) const
Extract sub-band sinr value from the sinrMatrix.
Definition nr-amc.cc:386
NrSinrMatrix CreateSinrMatForSb(const NrSinrMatrix &avgSinrSb, const NrSinrMatrix &sinrMat) const
Create wide-band matrix based on average sinr of particular sub-band.
Definition nr-amc.cc:403
double GetSinrFromSpectralEfficiency(double spectralEff) const
Compute SINR for a given spectral efficiency according to Shannon's capacity.
Definition nr-amc.cc:547
static constexpr size_t NR_AMC_NUM_SYMBOLS_DEFAULT
Num OFDM syms for TB size.
Definition nr-amc.h:191
McsParams GetMaxMcsParams(const NrSinrMatrix &sinrMat, size_t subbandSize) const
Find maximum MCS supported for this channel and obtain related parameters.
Definition nr-amc.cc:352
uint32_t CalculateTbSize(uint8_t mcs, uint8_t rank, uint32_t nprb) const
Calculate the TransportBlock size (in bytes) giving the MCS and the number of RB assigned.
Definition nr-amc.cc:116
void SetDlMode()
Set the object to be in "DL" mode.
Definition nr-amc.cc:35
uint8_t GetCqiFromSpectralEfficiency(double s) const
Get CQI from a SpectralEfficiency value.
Definition nr-amc.cc:267
uint8_t GetMcsFromSpectralEfficiency(double s) const
Get MCS from a SpectralEfficiency value.
Definition nr-amc.cc:281
uint8_t CreateCqiFeedbackSiso(const SpectrumValue &sinr, uint8_t &mcsWb) const
Create a CQI/MCS wideband feedback from SINR values.
Definition nr-amc.cc:163
AmcModel
Valid types of the model used to create a cqi feedback.
Definition nr-amc.h:79
@ ErrorModel
Error Model version (can use different error models, see NrErrorModel)
Definition nr-amc.h:81
@ ShannonModel
Shannon based model (very conservative)
Definition nr-amc.h:80
void SetNumRefScPerRb(uint8_t nref)
Set the the number of subcarriers carrying reference signals per resource block.
Definition nr-amc.cc:109
uint8_t GetMcsFromCqi(uint8_t cqi) const
Get the MCS value from a CQI value.
Definition nr-amc.cc:79
uint8_t GetMcs(const NrSinrMatrix &sinrMat) const
Find MCS supported for this channel.
Definition nr-amc.cc:418
uint32_t GetMaxMcs() const
Get the maximum MCS (depends on the underlying error model)
Definition nr-amc.cc:296
~NrAmc() override
~NrAmc deconstructor
Definition nr-amc.cc:30
std::vector< Ptr< NrErrorModelOutput > > NrErrorModelHistory
Vector of previous output.
static TypeId GetTypeId()
GetTypeId.
NrSinrMatrix stores the MIMO SINR matrix, with dimension rank x nRbs.
static const uint8_t SUBCARRIERS_PER_RB
subcarriers per resource block
Parameters related to MCS selection.
Definition nr-amc.h:195