5G-LENA nr-v3.3-49-g235218b1
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
78TypeId
80{
81 return NrAmc::GetTypeId();
82}
83
84uint8_t
85NrAmc::GetMcsFromCqi(uint8_t cqi) const
86{
87 NS_LOG_FUNCTION(cqi);
88 NS_ASSERT_MSG(cqi >= 0 && cqi <= 15, "CQI must be in [0..15] = " << cqi);
89
90 double spectralEfficiency = m_errorModel->GetSpectralEfficiencyForCqi(cqi);
91 uint8_t mcs = 0;
92
93 while ((mcs < m_errorModel->GetMaxMcs()) &&
94 (m_errorModel->GetSpectralEfficiencyForMcs(mcs + 1) <= spectralEfficiency))
95 {
96 ++mcs;
97 }
98
99 NS_LOG_LOGIC("mcs = " << mcs);
100
101 return mcs;
102}
103
104uint8_t
106{
107 NS_LOG_FUNCTION(this);
108 return m_numRefScPerRb;
109}
110
111void
113{
114 NS_LOG_FUNCTION(this);
115 m_numRefScPerRb = nref;
116}
117
118uint32_t
119NrAmc::CalculateTbSize(uint8_t mcs, uint8_t rank, uint32_t nprb) const
120{
121 NS_LOG_FUNCTION(this << static_cast<uint32_t>(mcs));
122
123 NS_ASSERT_MSG(mcs <= m_errorModel->GetMaxMcs(),
124 "MCS=" << static_cast<uint32_t>(mcs) << " while maximum MCS is "
125 << static_cast<uint32_t>(m_errorModel->GetMaxMcs()));
126
127 uint32_t payloadSize = GetPayloadSize(mcs, rank, nprb);
128 uint32_t tbSize = payloadSize;
129
130 if (m_errorModelType != LenaErrorModel::GetTypeId())
131 {
132 if (payloadSize >= m_crcLen)
133 {
134 tbSize =
135 payloadSize - m_crcLen; // subtract parity bits of m_crcLen used in transport block
136 }
137 uint32_t cbSize =
138 m_errorModel->GetMaxCbSize(payloadSize,
139 mcs); // max size of a code block (including m_crcLen)
140 if (tbSize > cbSize) // segmentation of the transport block occurs
141 {
142 double C = ceil(tbSize / cbSize);
143 tbSize = payloadSize - static_cast<uint32_t>(
144 C * m_crcLen); // subtract bits of m_crcLen used in code
145 // blocks, in case of code block segmentation
146 }
147 }
148
149 NS_LOG_INFO(" mcs:" << (unsigned)mcs << " TB size:" << tbSize);
150
151 return tbSize;
152}
153
154uint32_t
155NrAmc::GetPayloadSize(uint8_t mcs, uint8_t rank, uint32_t nprb) const
156{
157 return m_errorModel->GetPayloadSize(NrSpectrumValueHelper::SUBCARRIERS_PER_RB -
159 mcs,
160 rank,
161 nprb,
162 m_emMode);
163}
164
165uint8_t
166NrAmc::CreateCqiFeedbackWbTdma(const SpectrumValue& sinr, uint8_t& mcs) const
167{
168 NS_LOG_FUNCTION(this);
169
170 // produces a single CQI/MCS value
171
172 // std::vector<int> cqi;
173 uint8_t cqi = 0;
174 double seAvg = 0;
175
176 Values::const_iterator it;
177 if (m_amcModel == ShannonModel)
178 {
179 // use shannon model
180 double m_ber = GetBer(); // Shannon based model reference BER
181 uint32_t rbNum = 0;
182 for (it = sinr.ConstValuesBegin(); it != sinr.ConstValuesEnd(); it++)
183 {
184 double sinr_ = (*it);
185 if (sinr_ == 0.0)
186 {
187 // cqi.push_back (-1); // SINR == 0 (linear units) means no signal in this RB
188 }
189 else
190 {
191 /*
192 * Compute the spectral efficiency from the SINR
193 * SINR
194 * spectralEfficiency = log2 (1 + -------------------- )
195 * -ln(5*BER)/1.5
196 * NB: SINR must be expressed in linear units
197 */
198
199 double s = log2(1 + (sinr_ / ((-std::log(5.0 * m_ber)) / 1.5)));
200 seAvg += s;
201
202 int cqi_ = GetCqiFromSpectralEfficiency(s);
203 rbNum++;
204
205 NS_LOG_LOGIC(" PRB =" << sinr.GetSpectrumModel()->GetNumBands() << ", sinr = "
206 << sinr_ << " (=" << 10 * std::log10(sinr_) << " dB)"
207 << ", spectral efficiency =" << s << ", CQI = " << cqi_
208 << ", BER = " << m_ber);
209 // cqi.push_back (cqi_);
210 }
211 }
212 if (rbNum != 0)
213 {
214 seAvg /= rbNum;
215 }
216 cqi = GetCqiFromSpectralEfficiency(seAvg); // ceil (cqiAvg);
217 mcs = GetMcsFromSpectralEfficiency(seAvg); // ceil(mcsAvg);
218 }
219 else if (m_amcModel == ErrorModel)
220 {
221 std::vector<int> rbMap;
222 int rbId = 0;
223 for (it = sinr.ConstValuesBegin(); it != sinr.ConstValuesEnd(); it++)
224 {
225 if (*it != 0.0)
226 {
227 rbMap.push_back(rbId);
228 }
229 rbId += 1;
230 }
231
232 mcs = 0;
233 Ptr<NrErrorModelOutput> output;
234 while (mcs <= m_errorModel->GetMaxMcs())
235 {
236 uint8_t rank = 1; // This function is SISO only
237 auto tbSize = CalculateTbSize(mcs, rank, rbMap.size());
238 output = m_errorModel->GetTbDecodificationStats(sinr,
239 rbMap,
240 tbSize,
241 mcs,
243 if (output->m_tbler > 0.1)
244 {
245 break;
246 }
247 mcs++;
248 }
249
250 if (mcs > 0)
251 {
252 mcs--;
253 }
254
255 if ((output->m_tbler > 0.1) && (mcs == 0))
256 {
257 cqi = 0;
258 }
259 else if (mcs == m_errorModel->GetMaxMcs())
260 {
261 cqi = 15; // all MCSs can guarantee the 10 % of BER
262 }
263 else
264 {
265 double s = m_errorModel->GetSpectralEfficiencyForMcs(mcs);
266 cqi = 0;
267 while ((cqi < 15) && (m_errorModel->GetSpectralEfficiencyForCqi(cqi + 1) <= s))
268 {
269 ++cqi;
270 }
271 }
272 NS_LOG_DEBUG(this << "\t MCS " << (uint16_t)mcs << "-> CQI " << cqi);
273 }
274 return cqi;
275}
276
277uint8_t
279{
280 NS_LOG_FUNCTION(s);
281 NS_ASSERT_MSG(s >= 0.0, "negative spectral efficiency = " << s);
282 uint8_t cqi = 0;
283 while ((cqi < 15) && (m_errorModel->GetSpectralEfficiencyForCqi(cqi + 1) < s))
284 {
285 ++cqi;
286 }
287 NS_LOG_LOGIC("cqi = " << cqi);
288 return cqi;
289}
290
291uint8_t
293{
294 NS_LOG_FUNCTION(s);
295 NS_ASSERT_MSG(s >= 0.0, "negative spectral efficiency = " << s);
296 uint8_t mcs = 0;
297 while ((mcs < m_errorModel->GetMaxMcs()) &&
298 (m_errorModel->GetSpectralEfficiencyForMcs(mcs + 1) < s))
299 {
300 ++mcs;
301 }
302 NS_LOG_LOGIC("cqi = " << mcs);
303 return mcs;
304}
305
306uint32_t
308{
309 NS_LOG_FUNCTION(this);
310 return m_errorModel->GetMaxMcs();
311}
312
313void
315{
316 NS_LOG_FUNCTION(this);
317 m_amcModel = m;
318}
319
322{
323 NS_LOG_FUNCTION(this);
324 return m_amcModel;
325}
326
327void
328NrAmc::SetErrorModelType(const TypeId& type)
329{
330 NS_LOG_FUNCTION(this);
331 ObjectFactory factory;
332 m_errorModelType = type;
333
334 factory.SetTypeId(m_errorModelType);
335 m_errorModel = DynamicCast<NrErrorModel>(factory.Create());
336 NS_ASSERT(m_errorModel != nullptr);
337}
338
339TypeId
341{
342 NS_LOG_FUNCTION(this);
343 return m_errorModelType;
344}
345
346double
347NrAmc::GetBer() const
348{
349 NS_LOG_FUNCTION(this);
352 {
353 return 0.00005; // Value for LTE error model
354 }
355 else
356 {
357 return 0.00001; // Value for NR error model
358 }
359}
360
361NrAmc::McsParams
362NrAmc::GetMaxMcsParams(const NrSinrMatrix& sinrMat, size_t subbandSize) const
363{
364 auto mcs = uint8_t{0};
365 switch (m_amcModel)
366 {
367 case ShannonModel:
368 NS_ABORT_MSG("ShannonModel is not yet supported");
369 break;
370 case ErrorModel:
371 mcs = GetMaxMcsForErrorModel(sinrMat);
372 break;
373 default:
374 NS_ABORT_MSG("AMC model not supported");
375 break;
376 }
377 auto wbCqi = GetWbCqiFromMcs(mcs);
378 auto nRbs = sinrMat.GetNumRbs();
379 auto nSbs = (nRbs + subbandSize - 1) / subbandSize;
380
381 // Create subband CQI. Workaround: using WB-CQI as SB-CQI
382 // TODO: remove workaround and compute actual SB-CQI
383 auto sbCqis = std::vector<uint8_t>(nSbs, wbCqi);
384
385 auto tbSize = CalcTbSizeForMimoMatrix(mcs, sinrMat);
386 return McsParams{mcs, wbCqi, sbCqis, tbSize};
387}
388
389uint8_t
390NrAmc::GetMaxMcsForErrorModel(const NrSinrMatrix& sinrMat) const
391{
392 auto mcs = uint8_t{0};
393 while (mcs <= m_errorModel->GetMaxMcs())
394 {
395 auto tbler = CalcTblerForMimoMatrix(mcs, sinrMat);
396 // TODO: Change target TBLER from default 0.1 when using MCS table 3
397 if (tbler > 0.1)
398 {
399 break;
400 }
401 // The current configuration produces a sufficiently low TBLER, try next value
402 mcs++;
403 }
404 if (mcs > 0)
405 {
406 // The loop exited because the MCS exceeded max MCS or because of high TBLER. Reduce MCS
407 mcs--;
408 }
409
410 return mcs;
411}
412
413uint8_t
414NrAmc::GetWbCqiFromMcs(uint8_t mcs) const
415{
416 // Based on OSS CreateCqiFeedbackWbTdma
417 auto cqi = uint8_t{0};
418 if (mcs == 0)
419 {
420 cqi = 0;
421 }
422 else if (mcs == m_errorModel->GetMaxMcs())
423 {
424 // TODO: define constant for max CQI
425 cqi = 15;
426 }
427 else
428 {
429 auto s = m_errorModel->GetSpectralEfficiencyForMcs(mcs);
430 cqi = 0;
431 // TODO: define constant for max CQI
432 while ((cqi < 15) && (m_errorModel->GetSpectralEfficiencyForCqi(cqi + 1) <= s))
433 {
434 ++cqi;
435 }
436 }
437 return cqi;
438}
439
440double
441NrAmc::CalcTblerForMimoMatrix(uint8_t mcs, const NrSinrMatrix& sinrMat) const
442{
443 auto dummyRnti = uint16_t{0};
444 auto duration = Time{1.0}; // Use an arbitrary non-zero time as the chunk duration
445 auto mimoChunk = MimoSinrChunk{sinrMat, dummyRnti, duration};
446 auto mimoChunks = std::vector<MimoSinrChunk>{mimoChunk};
447 auto rank = sinrMat.GetRank();
448
449 // Create the RB map (indices of used RBs, i.e., indices of RBs where SINR is non-zero)
450 std::vector<int> rbMap{}; // TODO: change type of rbMap from int to size_t
451 int nRbs = static_cast<int>(sinrMat.GetNumRbs());
452 for (int rbIdx = 0; rbIdx < nRbs; rbIdx++)
453 {
454 if (sinrMat(0, rbIdx) != 0.0)
455 {
456 rbMap.push_back(rbIdx);
457 }
458 }
459
460 auto tbSize = CalcTbSizeForMimoMatrix(mcs, sinrMat);
461 auto dummyHistory = NrErrorModel::NrErrorModelHistory{}; // Create empty HARQ history
462 auto outputOfEm = m_errorModel->GetTbDecodificationStatsMimo(mimoChunks,
463 rbMap,
464 tbSize,
465 mcs,
466 rank,
467 dummyHistory);
468 return outputOfEm->m_tbler;
469}
470
471uint32_t
472NrAmc::CalcTbSizeForMimoMatrix(uint8_t mcs, const NrSinrMatrix& sinrMat) const
473{
474 auto nRbs = sinrMat.GetNumRbs();
475 auto nRbSyms = nRbs * NR_AMC_NUM_SYMBOLS_DEFAULT;
476 auto rank = sinrMat.GetRank();
477 return CalculateTbSize(mcs, rank, nRbSyms);
478}
479
480} // namespace ns3
static TypeId GetTypeId()
GetTypeId.
void SetErrorModelType(const TypeId &type)
Set Error model type.
Definition nr-amc.cc:328
void SetAmcModel(AmcModel m)
Set the AMC model type.
Definition nr-amc.cc:314
TypeId GetErrorModelType() const
Get the error model type.
Definition nr-amc.cc:340
uint8_t GetNumRefScPerRb() const
Definition nr-amc.cc:105
NrAmc()
NrAmc constructor.
Definition nr-amc.cc:25
void SetUlMode()
Set the object to be in "UL" mode.
Definition nr-amc.cc:42
static TypeId GetTypeId()
GetTypeId.
Definition nr-amc.cc:49
AmcModel GetAmcModel() const
Get the AMC model type.
Definition nr-amc.cc:321
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:155
static constexpr size_t NR_AMC_NUM_SYMBOLS_DEFAULT
Num OFDM syms for TB size.
Definition nr-amc.h:190
uint8_t CreateCqiFeedbackWbTdma(const SpectrumValue &sinr, uint8_t &mcsWb) const
Create a CQI/MCS wideband feedback from a SINR values.
Definition nr-amc.cc:166
McsParams GetMaxMcsParams(const NrSinrMatrix &sinrMat, size_t subbandSize) const
Find maximum MCS supported for this channel and obtain related parameters.
Definition nr-amc.cc:362
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:119
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:278
uint8_t GetMcsFromSpectralEfficiency(double s) const
Get MCS from a SpectralEfficiency value.
Definition nr-amc.cc:292
AmcModel
Valid types of the model used to create a cqi feedback.
Definition nr-amc.h:85
@ ErrorModel
Error Model version (can use different error models, see NrErrorModel)
Definition nr-amc.h:87
@ ShannonModel
Shannon based model (very conservative)
Definition nr-amc.h:86
void SetNumRefScPerRb(uint8_t nref)
Set the the number of subcarriers carrying reference signals per resource block.
Definition nr-amc.cc:112
uint8_t GetMcsFromCqi(uint8_t cqi) const
Get the MCS value from a CQI value.
Definition nr-amc.cc:85
uint32_t GetMaxMcs() const
Get the maximum MCS (depends on the underlying error model)
Definition nr-amc.cc:307
~NrAmc() override
~NrAmc deconstructor
Definition nr-amc.cc:30
TypeId GetInstanceTypeId() const override
GetInstanceTypeId.
Definition nr-amc.cc:79
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:194