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