5G-LENA nr-v3.3-159-ga6832aa7
The 5G/NR module for the ns-3 simulator
Loading...
Searching...
No Matches
nr-eesm-error-model.cc
1// Copyright (c) 2019 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
2// Copyright (c) 2019 Interdigital <kevin.wanuga@interdigital.com> tables
3//
4// SPDX-License-Identifier: GPL-2.0-only
5
6#include "nr-eesm-error-model.h"
7
8#include "fast-exp.h"
9#include "nr-phy-mac-common.h"
10
11#include "ns3/enum.h"
12#include "ns3/log.h"
13
14#include <algorithm>
15#include <cmath>
16
17namespace ns3
18{
19
20NS_LOG_COMPONENT_DEFINE("NrEesmErrorModel");
21NS_OBJECT_ENSURE_REGISTERED(NrEesmErrorModel);
22
26static const std::vector<uint16_t> LiftingSizeTableBG = {
27 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20,
28 22, 24, 26, 28, 30, 32, 36, 40, 44, 48, 52, 56, 60, 64, 72, 80, 88,
29 96, 104, 112, 120, 128, 144, 160, 176, 192, 208, 224, 240, 256, 288, 320, 352, 384};
30
31std::vector<std::string> NrEesmErrorModel::m_bgTypeName = {"BG1", "BG2"};
32
34 : NrErrorModel()
35{
36 NS_LOG_FUNCTION(this);
37}
38
40{
41 NS_LOG_FUNCTION(this);
42}
43
44TypeId
46{
47 static TypeId tid = TypeId("ns3::NrEesmErrorModel").SetParent<NrErrorModel>();
48 return tid;
49}
50
51double
52NrEesmErrorModel::SinrEff(const SpectrumValue& sinr,
53 const std::vector<int>& map,
54 uint8_t mcs,
55 double a,
56 double b) const
57{
58 // it follows: SINReff = - beta * ln [1/b * (sum (exp (-sinr/beta)) + a)]
59 // for HARQ-IR: b = sum (map.size()), a = sum_j(sum_n (exp (-sinr/beta))) (for previous retx,
60 // till j=q-1) for HARQ-CC: b = map.size(), a = 0.0 (SINRs are already combined in sinr input)
61
62 double sinrExpSum = SinrExp(sinr, map, mcs);
63 double beta = GetBetaTable()->at(mcs);
64 double SINR = -beta * log((a + sinrExpSum) / b);
65 SINR = std::max(SINR, 0.0);
66
67 NS_LOG_INFO(" Effective SINR = " << SINR);
68
69 return SINR;
70}
71
72double
73NrEesmErrorModel::SinrExp(const SpectrumValue& sinr, const std::vector<int>& map, uint8_t mcs) const
74{
75 // it returns sum_n (exp (-SINR/beta))
76 NS_LOG_FUNCTION(sinr << &map << (uint8_t)mcs);
77 NS_ABORT_MSG_IF(map.empty(),
78 " Error: number of allocated RBs cannot be 0 - EESM method - SinrEff function");
79
80 double SINRexp = 0.0;
81 double SINRsum = 0.0;
82 double beta = GetBetaTable()->at(mcs);
83 for (int i : map)
84 {
85 double sinrLin = sinr[i];
86 SINRexp = exp21d(-sinrLin / beta);
87 SINRsum += SINRexp;
88 }
89 return SINRsum;
90}
91
92const std::vector<double>&
93NrEesmErrorModel::GetSinrDbVectorFromSimulatedValues(NrEesmErrorModel::GraphType graphType,
94 uint8_t mcs,
95 uint32_t cbSizeIndex) const
96{
97 return std::get<0>(GetSimulatedBlerFromSINR()->at(graphType).at(mcs).at(cbSizeIndex));
98}
99
100const std::vector<double>&
101NrEesmErrorModel::GetBLERVectorFromSimulatedValues(NrEesmErrorModel::GraphType graphType,
102 uint8_t mcs,
103 uint32_t cbSizeIndex) const
104{
105 return std::get<1>(GetSimulatedBlerFromSINR()->at(graphType).at(mcs).at(cbSizeIndex));
106}
107
108double
109NrEesmErrorModel::MappingSinrBler(double sinr, uint8_t mcs, uint32_t cbSizeBit)
110{
111 NS_LOG_FUNCTION(sinr << (uint8_t)mcs << (uint32_t)cbSizeBit);
112 NS_ABORT_MSG_IF(mcs > GetMaxMcs(),
113 "MCS out of range [0..27/28]: " << static_cast<uint8_t>(mcs));
114
115 // use cbSize to obtain the index of CBSIZE in the map, jointly with mcs and sinr. take the
116 // lowest CBSIZE simulated including this CB for removing CB size quatization
117 // errors. sinr is also lower-bounded.
118 double bler = 0.0;
119 double sinr_db = 10 * log10(sinr);
120 GraphType bg_type = GetBaseGraphType(cbSizeBit, mcs);
121
122 // Get the index of CBSIZE in the map
123 NS_LOG_INFO("For sinr " << sinr << " and mcs " << +mcs << " CbSizebit " << cbSizeBit
124 << " we got bg type " << m_bgTypeName[bg_type]);
125 const auto& cbMap = GetSimulatedBlerFromSINR()->at(bg_type).at(mcs);
126 auto cbIt = cbMap.upper_bound(cbSizeBit);
127
128 if (cbIt != cbMap.begin())
129 {
130 cbIt--;
131 }
132
133 if (sinr_db < GetSinrDbVectorFromSimulatedValues(bg_type, mcs, cbIt->first).front())
134 {
135 bler = 1.0;
136 }
137 else if (sinr_db > GetSinrDbVectorFromSimulatedValues(bg_type, mcs, cbIt->first).back())
138 {
139 bler = 0.0;
140 }
141 else
142 {
143 // Get the index of SINR in the vector
144 auto sinrIt =
145 std::upper_bound(GetSinrDbVectorFromSimulatedValues(bg_type, mcs, cbIt->first).begin(),
146 GetSinrDbVectorFromSimulatedValues(bg_type, mcs, cbIt->first).end(),
147 sinr_db);
148
149 if (sinrIt != GetSinrDbVectorFromSimulatedValues(bg_type, mcs, cbIt->first).begin())
150 {
151 sinrIt--;
152 }
153
154 auto sinr_index =
155 std::distance(GetSinrDbVectorFromSimulatedValues(bg_type, mcs, cbIt->first).begin(),
156 sinrIt);
157 bler = GetBLERVectorFromSimulatedValues(bg_type, mcs, cbIt->first).at(sinr_index);
158 }
159
160 NS_LOG_LOGIC("SINR effective: " << sinr << " BLER:" << bler);
161 return bler;
162}
163
164NrEesmErrorModel::GraphType
165NrEesmErrorModel::GetBaseGraphType(uint32_t tbSizeBit, uint8_t mcs) const
166{
167 double ecr = GetMcsEcrTable()->at(mcs);
168
169 GraphType bg_type = FIRST;
170 if (tbSizeBit <= 292 || ecr <= 0.25 || (tbSizeBit <= 3824 && ecr <= 0.67))
171 {
172 bg_type = SECOND;
173 }
174 return bg_type;
175}
176
177std::pair<uint32_t, uint32_t>
178NrEesmErrorModel::CodeBlockSegmentation(uint32_t B, GraphType bg_type) const
179{
180 uint16_t Kcb;
181 uint8_t Kb;
182 if (bg_type == FIRST)
183 {
184 Kcb = 8448; // max size of a codeblock (including CRC) for BG1
185 Kb = 22;
186 }
187 else
188 {
189 NS_ASSERT(bg_type == SECOND);
190 Kcb = 3840; // max size of a codeblock (including CRC) for BG2
191 if (B >= 640)
192 {
193 Kb = 10;
194 }
195 else if (B >= 560)
196 {
197 Kb = 9;
198 }
199 else if (B >= 192)
200 {
201 Kb = 8;
202 }
203 else
204 {
205 Kb = 6;
206 }
207 }
208
209 uint32_t L = 0;
210 uint32_t C = 0; // no. of codeblocks
211 uint32_t B1 = 0;
212
213 if (B <= Kcb)
214 {
215 // only one codeblock
216 L = 0;
217 C = 1;
218 B1 = B;
219 }
220 else
221 {
222 L = 24;
223 C = ceil((double)B / ((double)(Kcb - L)));
224 B1 = B + C * L;
225 }
226
227 // Zc = minimum Z in all sets of lifting sizes table such that Kb * Z >= K1
228 uint32_t K1 = B1 / C;
229
230 // returns an iterator pointing to the first element in the range [first,last)
231 // which compares greater than the third parameter.
232 auto ZcIt = std::upper_bound(LiftingSizeTableBG.begin(),
233 --LiftingSizeTableBG.end(),
234 (static_cast<double>(K1) / Kb) + 0.001);
235 uint16_t Zc = *ZcIt;
236 uint32_t K;
237
238 if (bg_type == FIRST)
239 {
240 K = Zc * 22; // no. of bits in each code block
241 }
242 else // bg_type==2
243 {
244 NS_ASSERT(bg_type == SECOND);
245 K = Zc * 10; // no. of bits in each code block
246 }
247
248 NS_LOG_INFO("EESMErrorModel: TBS of " << B << " needs of " << B1 << " bits distributed in " << C
249 << " CBs of " << K << " bits");
250 return std::make_pair(K, C);
251}
252
253Ptr<NrErrorModelOutput>
255 const std::vector<int>& map,
256 uint32_t size,
257 uint8_t mcs,
258 const NrErrorModelHistory& sinrHistory)
259{
260 return GetTbBitDecodificationStats(sinr, map, size * 8, mcs, sinrHistory);
261}
262
263std::string
264NrEesmErrorModel::PrintMap(const std::vector<int>& map) const
265{
266 std::stringstream ss;
267
268 for (const auto& v : map)
269 {
270 ss << v << ", ";
271 }
272
273 return ss.str();
274}
275
276Ptr<NrErrorModelOutput>
277NrEesmErrorModel::GetTbBitDecodificationStats(const SpectrumValue& sinr,
278 const std::vector<int>& map,
279 uint32_t sizeBit,
280 uint8_t mcs,
281 const NrErrorModelHistory& sinrHistory)
282{
283 NS_LOG_FUNCTION(this);
284 NS_ABORT_IF(mcs > GetMaxMcs());
285
286 double tbSinr = SinrEff(sinr, map, mcs, 0, map.size()); // effective SINR for this tx
287 double SINR = tbSinr;
288 double sinrExpSum = SinrExp(sinr, map, mcs); // exponential sum of SINRs for this tx
289
290 NS_LOG_DEBUG(" mcs " << +mcs << " TBSize in bit " << sizeBit << " history elements: "
291 << sinrHistory.size() << " SINR of the tx: " << tbSinr << std::endl
292 << "MAP: " << PrintMap(map) << std::endl
293 << "SINR: " << sinr);
294
295 if (!sinrHistory.empty())
296 {
297 SINR = ComputeSINR(sinr, map, mcs, sizeBit, sinrHistory);
298 }
299
300 NS_LOG_DEBUG(" SINR after processing all retx (if any): " << SINR << " SINR last tx" << tbSinr);
301
302 // LDPC base graph type selection (1 or 2), as per TS 38.212, using the payload (A)
303 GraphType bg_type = GetBaseGraphType(sizeBit, mcs);
304 NS_LOG_INFO("BG type selection: " << bg_type);
305
306 // code block segmentation, as per TS 38.212, using payload + TB CRC attachment (B)
307 uint32_t B = sizeBit + 24; // input to code block segmentation, in bits
308 std::pair<uint32_t, uint32_t> cbSeg = CodeBlockSegmentation(B, bg_type);
309 uint32_t K = cbSeg.first;
310 uint32_t C = cbSeg.second;
311 NS_LOG_INFO("EESMErrorModel: TBS of " << B << " bits distributed in " << C << " CBs of " << K
312 << " bits");
313
314 uint8_t mcs_eq = mcs;
315 if ((!sinrHistory.empty()) && (mcs > 0))
316 {
317 mcs_eq = GetMcsEq(mcs);
318 }
319
320 NS_LOG_INFO(" MCS of tx " << +mcs << " Equivalent MCS for PHY abstraction (just for HARQ-IR) "
321 << +mcs_eq);
322
323 double errorRate = 1.0;
324 if (C != 1)
325 {
326 double cbler = MappingSinrBler(SINR, mcs_eq, K);
327 errorRate = 1.0 - pow(1.0 - cbler, C);
328 }
329 else
330 {
331 errorRate = MappingSinrBler(SINR, mcs_eq, K);
332 }
333
334 NS_LOG_DEBUG("Calculated Error rate " << errorRate);
335 NS_ASSERT(GetMcsEcrTable() != nullptr);
336
337 Ptr<NrEesmErrorModelOutput> ret = Create<NrEesmErrorModelOutput>(errorRate);
338 ret->m_sinrEff = SINR;
339 ret->m_sinr = sinr;
340 ret->m_map = map;
341 if (sinrHistory.empty())
342 {
343 ret->m_sinrExp = sinrExpSum; // it is first tx!
344 }
345 else
346 {
347 double m_sinrExpPrevious =
348 DynamicCast<NrEesmErrorModelOutput>(sinrHistory.back())->m_sinrExp;
349 ret->m_sinrExp = m_sinrExpPrevious + sinrExpSum; // it sums over previous tx (recursively)
350 }
351 ret->m_infoBits = sizeBit;
352 ret->m_codeBits = sizeBit / GetMcsEcrTable()->at(mcs);
353
354 return ret;
355}
356
357double
359{
360 NS_LOG_FUNCTION(this);
361 NS_ASSERT(GetSpectralEfficiencyForCqi() != nullptr);
362 NS_ABORT_MSG_UNLESS(cqi >= 0 && cqi <= 15, "CQI must be in [0..15] = " << cqi);
363
364 return GetSpectralEfficiencyForCqi()->at(cqi);
365}
366
367double
369{
370 NS_LOG_FUNCTION(this);
371 NS_ASSERT(GetSpectralEfficiencyForMcs() != nullptr);
372 NS_ABORT_IF(mcs > GetMaxMcs());
373
374 return GetSpectralEfficiencyForMcs()->at(mcs);
375}
376
377uint32_t
379 uint8_t mcs,
380 uint8_t rank,
381 uint32_t rbNum,
382 [[maybe_unused]] Mode mode) const
383{
384 NS_LOG_FUNCTION(this);
385 NS_ASSERT(GetMcsEcrTable() != nullptr);
386 const uint32_t rscElement = usefulSc * rbNum;
387 double Rcode = GetMcsEcrTable()->at(mcs);
388 uint8_t Qm = GetMcsMTable()->at(mcs);
389
390 const double spectralEfficiency = rscElement * Qm * Rcode * rank;
391
392 return static_cast<uint32_t>(std::floor(spectralEfficiency / 8));
393}
394
395uint32_t
396NrEesmErrorModel::GetMaxCbSize(uint32_t tbSize, uint8_t mcs) const
397{
398 GraphType bgType = GetBaseGraphType(tbSize * 8, mcs);
399 if (bgType == FIRST)
400 {
401 return LiftingSizeTableBG.back() * 22 / 8; // return CBsize in bytes
402 }
403 NS_ASSERT(bgType == SECOND);
404 return LiftingSizeTableBG.back() * 10 / 8; // return CBsize in bytes
405}
406
407uint8_t
409{
410 NS_LOG_FUNCTION(this);
411 NS_ASSERT(GetMcsEcrTable() != nullptr);
412 NS_LOG_INFO(" Max MCS: " << +(GetMcsEcrTable()->size() - 1));
413 return static_cast<uint8_t>(GetMcsEcrTable()->size() - 1);
414}
415
416} // namespace ns3
Ptr< NrErrorModelOutput > GetTbDecodificationStats(const SpectrumValue &sinr, const std::vector< int > &map, uint32_t size, uint8_t mcs, const NrErrorModelHistory &sinrHistory) override
Get an output for the decodification error probability of a given transport block,...
virtual double GetMcsEq(uint8_t mcsTx) const =0
Get the "Equivalent MCS" after retransmission combining.
uint32_t GetMaxCbSize(uint32_t tbSize, uint8_t mcs) const override
Get the maximum code block size in Bytes, as per NR. It depends on the LDPC base graph type.
double SinrEff(const SpectrumValue &sinr, const std::vector< int > &map, uint8_t mcs, double a, double b) const
compute the effective SINR for the specified MCS and SINR, according to the EESM method.
uint32_t GetPayloadSize(uint32_t usefulSc, uint8_t mcs, uint8_t rank, uint32_t rbNum, Mode mode) const override
Get the payload size in Bytes, following the MCSs in NR. It follows TS 38.214 Section 5....
virtual const std::vector< double > * GetBetaTable() const =0
static TypeId GetTypeId()
GetTypeId.
virtual const std::vector< double > * GetSpectralEfficiencyForMcs() const =0
virtual const std::vector< uint8_t > * GetMcsMTable() const =0
double SinrExp(const SpectrumValue &sinr, const std::vector< int > &map, uint8_t mcs) const
compute the sum of exponential SINRs for the specified MCS and SINR, according to the EESM method,...
uint8_t GetMaxMcs() const override
Get the maximum MCS. It depends on NR tables being used.
virtual const std::vector< double > * GetMcsEcrTable() const =0
~NrEesmErrorModel() override
~NrEesmErrorModel
std::string PrintMap(const std::vector< int > &map) const
function to print the RB map
virtual double ComputeSINR(const SpectrumValue &sinr, const std::vector< int > &map, uint8_t mcs, uint32_t sizeBit, const NrErrorModel::NrErrorModelHistory &sinrHistory) const =0
Compute the effective SINR after retransmission combining.
virtual const SimulatedBlerFromSINR * GetSimulatedBlerFromSINR() const =0
virtual const std::vector< double > * GetSpectralEfficiencyForCqi() const =0
NrEesmErrorModel()
NrEesmErrorModel constructor.
Interface for calculating the error probability for a transport block.
std::vector< Ptr< NrErrorModelOutput > > NrErrorModelHistory
Vector of previous output.
Mode
Indicate the mode (UL or DL)