utils.py (18518B)
1 # 2 # SPDX-FileCopyrightText: Copyright (c) 2021-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 # SPDX-License-Identifier: Apache-2.0 4 # 5 """Utility functions for the nr (5G) sub-package of the Sionna library. 6 """ 7 8 import numpy as np 9 10 def generate_prng_seq(length, c_init): 11 r"""Implements pseudo-random sequence generator as defined in Sec. 5.2.1 12 in [3GPP38211]_ based on a length-31 Gold sequence. 13 14 Parameters 15 ---------- 16 length: int 17 Desired output sequence length. 18 19 c_init: int 20 Initialization sequence of the PRNG. Must be in the range of 0 to 21 :math:`2^{32}-1`. 22 23 Output 24 ------ 25 :[``length``], ndarray of 0s and 1s 26 Containing the scrambling sequence. 27 28 Note 29 ---- 30 The initialization sequence ``c_init`` is application specific and is 31 usually provided be higher layer protocols. 32 """ 33 34 # check inputs for consistency 35 assert(length%1==0), "length must be a positive integer." 36 length = int(length) 37 assert(length>0), "length must be a positive integer." 38 39 assert(c_init%1==0), "c_init must be integer." 40 c_init = int(c_init) 41 assert(c_init<2**32), "c_init must be in [0, 2^32-1]." 42 assert(c_init>=0), "c_init must be in [0, 2^32-1]." 43 44 # internal parameters 45 n_seq = 31 # length of gold sequence 46 n_c = 1600 # defined in 5.2.1 in 38.211 47 48 # init sequences 49 c = np.zeros(length) 50 x1 = np.zeros(length + n_c + n_seq) 51 x2 = np.zeros(length + n_c + n_seq) 52 53 #int2bin 54 bin_ = format(c_init, f'0{n_seq}b') 55 c_init = [int(x) for x in bin_[-n_seq:]] if n_seq else [] 56 c_init = np.flip(c_init) # reverse order 57 58 # init x1 and x2 59 x1[0] = 1 60 x2[0:n_seq] = c_init 61 62 # and run the generator 63 for idx in range(length + n_c): 64 x1[idx+31] = np.mod(x1[idx+3] + x1[idx], 2) 65 x2[idx+31] = np.mod(x2[idx+3] + x2[idx+2] + x2[idx+1] + x2[idx], 2) 66 67 # update output sequence 68 for idx in range(length): 69 c[idx] = np.mod(x1[idx+n_c] + x2[idx+n_c], 2) 70 71 return c 72 73 def select_mcs(mcs_index, 74 table_index=1, 75 channel_type="PUSCH", 76 transform_precoding=False, 77 pi2bpsk=False, 78 verbose=False): 79 # pylint: disable=line-too-long 80 r"""Selects modulation and coding scheme (MCS) as specified in TS 38.214 [3GPP38214]_. 81 82 Implements MCS tables as defined in [3GPP38214]_ for PUSCH and PDSCH. 83 84 Parameters 85 ---------- 86 mcs_index : int| [0,...,28] 87 MCS index (denoted as :math:`I_{MCS}` in [3GPP38214]_). 88 89 table_index : int, 1 (default) | 2 | 3 | 4 90 Indicates which MCS table from [3GPP38214]_ to use. Starts with index "1". 91 92 channel_type : str, "PUSCH" (default) | "PDSCH" 93 5G NR physical channel type. Valid choices are "PDSCH" and "PUSCH". 94 95 transform_precoding : bool, False (default) 96 If True, the MCS tables as described in Sec. 6.1.4.1 97 in [3GPP38214]_ are applied. Only relevant for "PUSCH". 98 99 pi2bpsk : bool, False (default) 100 If True, the higher-layer parameter `tp-pi2BPSK` as 101 described in Sec. 6.1.4.1 in [3GPP38214]_ is applied. Only relevant 102 for "PUSCH". 103 104 verbose : bool, False (default) 105 If True, additional information will be printed. 106 107 Returns 108 ------- 109 (modulation_order, target_rate) : 110 Tuple: 111 112 modulation_order : int 113 Modulation order, i.e., number of bits per symbol. 114 115 target_rate : float 116 Target coderate. 117 """ 118 119 # check inputs 120 assert isinstance(mcs_index, int), "mcs_index must be int." 121 assert (mcs_index>=0), "mcs_index cannot be negative." 122 assert isinstance(table_index, int), "table_index must be int." 123 assert (table_index>0), "table_index starts with 1." 124 assert isinstance(channel_type, str), "channel_type must be str." 125 assert (channel_type in ("PDSCH", "PUSCH")), \ 126 "channel_type must be either `PDSCH` or `PUSCH`." 127 assert isinstance(transform_precoding, bool), \ 128 "transform_precoding must be bool." 129 assert isinstance(pi2bpsk, bool), "pi2bpsk must be bool." 130 assert isinstance(verbose, bool), "verbose must be bool." 131 132 if verbose: 133 print(f"Selected MCS index {mcs_index} for {channel_type} channel " \ 134 f"and Table index {table_index}.") 135 136 # without pre-coding the Tables from 5.1.3.1 are used 137 if channel_type=="PDSCH" or transform_precoding is False: 138 139 if table_index==1: # Table 5.1.3.1-1 in 38.214 140 if verbose: 141 print("Applying Table 5.1.3.1-1 from TS 38.214.") 142 143 assert mcs_index<29, "mcs_index not supported." 144 mod_orders = [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 6, 145 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6] 146 target_rates = [120, 157, 193, 251, 308, 379, 449, 526, 602, 679, 147 340, 378, 434, 490, 553, 616, 658, 438, 466, 517, 148 567, 616, 666, 719, 772, 822, 873, 910, 948] 149 150 elif table_index==2: # Table 5.1.3.1-2 in 38.214 151 if verbose: 152 print("Applying Table 5.1.3.1-2 from TS 38.214.") 153 154 assert mcs_index<28, "mcs_index not supported." 155 mod_orders = [2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 156 6, 6, 8, 8, 8, 8, 8, 8, 8, 8] 157 target_rates = [120, 193, 308, 449, 602, 378, 434, 490, 553, 616, 158 658, 466, 517, 567, 616, 666, 719, 772, 822, 873, 159 682.5, 711, 754, 797, 841, 885, 916.5, 948] 160 161 elif table_index==3: # Table 5.1.3.1-3 in 38.214 162 if verbose: 163 print("Applying Table 5.1.3.1-3 from TS 38.214.") 164 165 assert mcs_index<29, "mcs_index not supported." 166 mod_orders = [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 167 4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6] 168 target_rates = [30, 40, 50, 64, 78, 99, 120, 157, 193, 251, 308, 169 379, 449, 526, 602, 340, 378, 434, 490, 553, 616, 170 438, 466, 517, 567, 616, 666, 719, 772] 171 172 elif table_index==4: # Table 5.1.3.1-4 in 38.214 173 if verbose: 174 print("Applying Table 5.1.3.1-4 from TS 38.214.") 175 176 assert mcs_index<27, "mcs_index not supported." 177 mod_orders = [2, 2, 2, 4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 8, 8, 178 8, 8, 8, 8, 8, 10, 10, 10, 10] 179 target_rates = [120, 193, 449, 378, 490, 616, 466, 517, 567, 616, 180 666, 719, 772, 822, 873, 682.5, 711, 754, 797, 841, 181 885, 916.5, 948, 805.5, 853, 900.5, 948] 182 else: 183 raise ValueError("Unsupported table_index.") 184 185 elif channel_type=="PUSCH": # only if pre-coding is true 186 187 if table_index==1: # Table 6.1.4.1-1 in 38.214 188 if verbose: 189 print("Applying Table 6.1.4.1-1 from TS 38.214.") 190 191 assert mcs_index<28, "mcs_index not supported." 192 # higher layer parameter as defined in 6.1.4.1 193 if pi2bpsk: 194 if verbose: 195 print("Assuming pi2BPSK modulation.") 196 q=1 197 else: 198 q=2 199 200 mod_orders = [q, q, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 6, 201 6, 6, 6, 6, 6, 6, 6, 6, 6, 6] 202 target_rates = [240/q, 314/q, 193, 251, 308, 379, 449, 526, 602, 203 679, 340, 378, 434, 490, 553, 616, 658, 466, 517, 204 567, 616, 666, 719, 772, 822, 873, 910, 948] 205 206 elif table_index==2: # Table 6.1.4.1-2 in 38.214 207 if verbose: 208 print("Applying Table 6.1.4.1-2 from TS 38.214.") 209 210 assert mcs_index<28, "mcs_index not supported." 211 # higher layer parameter as defined in 6.1.4.1 212 if pi2bpsk: 213 if verbose: 214 print("Assuming pi2BPSK modulation.") 215 q=1 216 else: 217 q=2 218 mod_orders = [q, q, q, q, q, q, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 219 4, 4, 4, 4, 4, 4, 6, 6, 6, 6] 220 target_rates = [60/q, 80/q, 100/q, 128/q, 156/q, 198/q, 120, 157, 221 193, 251, 308, 379, 449, 526, 602, 679, 378, 434, 222 490, 553, 616, 658, 699, 772, 567, 616, 666, 772] 223 else: 224 raise ValueError("Unsupported table_index.") 225 else: 226 raise ValueError("Unsupported channel_type.") 227 228 mod_order = mod_orders[mcs_index] 229 target_rate = target_rates[mcs_index] / 1024 # rate is given as r*1024 230 231 if verbose: 232 print("Modulation order: ", mod_order) 233 print("Target code rate: ", target_rate) 234 235 return mod_order, target_rate 236 237 def calculate_tb_size(modulation_order, 238 target_coderate, 239 target_tb_size=None, 240 num_coded_bits=None, 241 num_prbs=None, 242 num_ofdm_symbols=None, 243 num_dmrs_per_prb=None, 244 num_layers=1, 245 num_ov=0, 246 tb_scaling=1., 247 verbose=True): 248 # pylint: disable=line-too-long 249 r"""Calculates transport block (TB) size for given system parameters. 250 251 This function follows the basic procedure as defined in TS 38.214 Sec. 252 5.1.3.2 and Sec. 6.1.4.2 [3GPP38214]_. 253 254 Parameters 255 ---------- 256 modulation_order : int 257 Modulation order, i.e., number of bits per QAM symbol. 258 259 target_coderate : float 260 Target coderate. 261 262 target_tb_size: None (default) | int 263 Target transport block size, i.e., how many information bits can be 264 encoded into a slot for the given slot configuration. If provided, 265 ``num_prbs``, ``num_ofdm_symbols`` and ``num_dmrs_per_prb`` will be 266 ignored. 267 268 num_coded_bits: None (default) | int 269 How many coded bits can be fit into a given slot. If provided, 270 ``num_prbs``, ``num_ofdm_symbols`` and ``num_dmrs_per_prb`` will be 271 ignored. 272 273 num_prbs : None (default) | int 274 Total number of allocated PRBs per OFDM symbol where 1 PRB equals 12 275 subcarriers. 276 277 num_ofdm_symbols : None (default) | int 278 Number of OFDM symbols allocated for transmission. Cannot be larger 279 than 14. 280 281 num_dmrs_per_prb : None (default) | int 282 Number of DMRS (i.e., pilot) symbols per PRB that are NOT used for data 283 transmission. Sum over all ``num_ofdm_symbols`` OFDM symbols. 284 285 num_layers: int, 1 (default) 286 Number of MIMO layers. 287 288 num_ov : int, 0 (default) 289 Number of unused resource elements due to additional 290 overhead as specified by higher layer. 291 292 tb_scaling: float, 0.25 | 0.5 | 1 (default) 293 TB scaling factor for PDSCH as defined in TS 38.214 Tab. 5.1.3.2-2. 294 Valid choices are 0.25, 0.5 and 1.0. 295 296 verbose : bool, False (default) 297 If True, additional information will be printed. 298 299 Returns 300 ------- 301 (tb_size, cb_size, num_cbs, cw_length, tb_crc_length, cb_crc_length, cw_lengths) : 302 Tuple: 303 304 tb_size : int 305 Transport block size, i.e., how many information bits can be encoded 306 into a slot for the given slot configuration. 307 308 cb_size : int 309 Code block (CB) size. Determines the number of 310 information bits (including TB/CB CRC parity bits) per codeword. 311 312 num_cbs : int 313 Number of code blocks. Determines into how many CBs the TB is segmented. 314 315 cw_lengths : list of ints 316 Each list element defines the codeword length of each of the ``num_cbs`` 317 codewords after LDPC encoding and rate-matching. The total number of 318 coded bits is :math:`\sum` ``cw_lengths``. 319 320 tb_crc_length : int 321 Length of the TB CRC. 322 323 cb_crc_length : int 324 Length of each CB CRC. 325 326 Note 327 ---- 328 Due to rounding, ``cw_lengths`` (=length of each codeword after encoding), 329 can be slightly different within a transport block. Thus, 330 ``cw_lengths`` is given as a list of ints where each list elements denotes 331 the number of codeword bits of the corresponding codeword after 332 rate-matching. 333 """ 334 335 # supports two modi: 336 # a) target_tb_size and num_coded_bits given 337 # b) available res in slot given 338 339 # mode a) 340 if target_tb_size is not None: 341 342 if num_coded_bits is None: 343 raise ValueError("num_coded_bits cannot be None if " \ 344 "target_tb_size is provided.") 345 assert num_coded_bits%1==0, "num_coded_bits must be int." 346 num_coded_bits = int(num_coded_bits) 347 348 assert num_coded_bits%num_layers==0, \ 349 "num_coded_bits must be a multiple of num_layers." 350 351 assert num_coded_bits%modulation_order==0, \ 352 "num_coded_bits must be a multiple of modulation_order." 353 354 assert target_tb_size%1==0, "target_tb_size must be int." 355 n_info = int(target_tb_size) 356 357 assert target_tb_size<num_coded_bits, \ 358 "Invalid transport block parameters. target_tb_size must be less " \ 359 "than the requested num_coded_bits excluding the overhead for the "\ 360 "TB CRC." 361 362 else: 363 if num_coded_bits is not None: 364 print("num_coded_bits will be ignored if target_tb_size " \ 365 "is None.") 366 367 assert num_ofdm_symbols in range(1, 15),\ 368 "num_ofdm_symbols must be in the range from 1 to 14." 369 assert num_prbs in range(1, 276),\ 370 "num_prbs must be in the range from 1 to 276." 371 372 assert tb_scaling in (0.25, 0.5, 1.), \ 373 "tb_scaling must be in (0.25,0.5,1.)." 374 375 # compute number of data symbols per prb 376 n_re_per_prb = 12*num_ofdm_symbols - num_dmrs_per_prb - num_ov 377 378 # number of coded bits that fit into the given slot configuration 379 num_coded_bits = int(tb_scaling * n_re_per_prb \ 380 * modulation_order * num_layers * num_prbs) 381 382 # number of allocated REs 383 # the max. number of REs per PRB is limited to 156 in 38.214 384 n_re = min(156, n_re_per_prb) * num_prbs 385 386 # include tb_scaling as defined in Tab. 5.1.3.2-2 38.214 387 n_info = target_coderate * tb_scaling * n_re \ 388 * modulation_order * num_layers 389 390 if n_info <= 3824: 391 c=1 392 # go to step 3 in 38.214 5.1.3.2 393 n = max(3, np.floor(np.log2(n_info)) - 6) 394 n_info_q = max(24, 2**n * np.floor(n_info/2**n)) 395 396 # explicit lengths given in Tab 5.1.3.2-1 397 tab51321 = [24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128, 398 136, 144, 152, 160, 168, 176, 184, 192, 208, 224, 240, 256, 399 272, 288, 304, 320, 336, 352, 368, 384, 408, 432, 456, 480, 400 504, 528, 552, 576, 608, 640, 672, 704, 736, 768, 808, 848, 401 888, 928, 984, 1032, 1064, 1128, 1160, 1192, 1224, 1256, 402 1288, 1320, 1352, 1416, 1480, 1544, 1608, 1672, 1736, 1800, 403 1864, 1928, 2024, 2088, 2152, 2216, 2280, 2408, 2472, 2536, 404 2600, 2664, 2728, 2792, 2856, 2976, 3104, 3240, 3368, 3496, 405 3624, 3752, 3824] 406 407 # find closest TBS that is not less n_info 408 for tbs in tab51321: 409 if tbs>=n_info_q: 410 break 411 else: 412 # go to step 4 in 38.212 5.3.1.2 413 n = np.floor(np.log2(n_info-24)) - 5 414 # "ties in the round function are broken towards next largest integer" 415 n_info_q = max(3840, 2**n * np.round((n_info-24)/2**n)) 416 417 if target_coderate<=1/4: 418 c = np.ceil((n_info_q + 24) / 3816) 419 tbs = 8 * c * np.ceil((n_info_q + 24) / (8 * c)) - 24 420 else: 421 if n_info > 8424: 422 c = np.ceil((n_info_q + 24) / 8424) 423 tbs = 8 * c * np.ceil((n_info_q + 24) / (8*c)) - 24 424 else: 425 c = 1 426 tbs = 8 * np.ceil((n_info_q + 24) / 8) - 24 427 428 # TB CRC see 6.2.1 in 38.212 429 if tbs>3824: 430 tb_crc_length = 24 431 else: 432 tb_crc_length = 16 433 434 # if tbs > max CB length, CRC-24 is added; see 5.2.2 in 38.212 435 if c>1: # if multiple CBs exists, additional CRC is applied 436 cb_crc_length = 24 437 else: 438 cb_crc_length = 0 439 440 cb_size = (tbs + tb_crc_length)/c + cb_crc_length # bits per CW 441 # internal sanity check 442 assert (cb_size%1==0), "cb_size not an integer." 443 444 # c is the number of code blocks 445 num_cbs = int(c) 446 cb_size = int(cb_size) 447 tb_size = int(tbs) 448 449 # cb_length as specified in 5.4.2.1 38.212 450 # remark: the length can be different for multiple cws due to rounding 451 # thus a list of lengths is generated 452 cw_length = [] 453 454 for j in range(num_cbs): 455 # first blocks are floored 456 if j <= num_cbs \ 457 - np.mod(num_coded_bits/(num_layers*modulation_order),num_cbs)-1: 458 l = num_layers * modulation_order \ 459 * np.floor(num_coded_bits / (num_layers*modulation_order*num_cbs)) 460 cw_length += [int(l)] 461 else: # last blocks are ceiled 462 l = num_layers * modulation_order \ 463 * np.ceil(num_coded_bits / (num_layers*modulation_order*num_cbs)) 464 cw_length += [int(l)] 465 # sanity check that total length matches to total number of cws 466 assert num_coded_bits==np.sum(cw_length), \ 467 "Internal error: invalid codeword lengths." 468 469 effective_rate = tb_size / num_coded_bits 470 471 if verbose: 472 print("Modulation order:", modulation_order) 473 if target_coderate is not None: 474 print(f"Target coderate: {target_coderate:.3f}") 475 print(f"Effective coderate: {effective_rate:.3f}") 476 print("Number of layers:", num_layers) 477 print("------------------") 478 print("Info bits per TB: ", tb_size) 479 print("TB CRC length: ", tb_crc_length) 480 print("Total number of coded TB bits:", num_coded_bits) 481 print("------------------") 482 print("Info bits per CB:", cb_size) 483 print("Number of CBs:", num_cbs) 484 print("CB CRC length: ", cb_crc_length) 485 print("Output CB lengths:", cw_length) 486 487 return tb_size, cb_size, num_cbs, cw_length, tb_crc_length, cb_crc_length