Source code for sionna.nr.utils

#
# SPDX-FileCopyrightText: Copyright (c) 2021-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
"""Utility functions for the nr (5G) sub-package of the Sionna library.
"""

import numpy as np

[docs] def generate_prng_seq(length, c_init): r"""Implements pseudo-random sequence generator as defined in Sec. 5.2.1 in [3GPP38211]_ based on a length-31 Gold sequence. Parameters ---------- length: int Desired output sequence length. c_init: int Initialization sequence of the PRNG. Must be in the range of 0 to :math:`2^{32}-1`. Output ------ :[``length``], ndarray of 0s and 1s Containing the scrambling sequence. Note ---- The initialization sequence ``c_init`` is application specific and is usually provided be higher layer protocols. """ # check inputs for consistency assert(length%1==0), "length must be a positive integer." length = int(length) assert(length>0), "length must be a positive integer." assert(c_init%1==0), "c_init must be integer." c_init = int(c_init) assert(c_init<2**32), "c_init must be in [0, 2^32-1]." assert(c_init>=0), "c_init must be in [0, 2^32-1]." # internal parameters n_seq = 31 # length of gold sequence n_c = 1600 # defined in 5.2.1 in 38.211 # init sequences c = np.zeros(length) x1 = np.zeros(length + n_c + n_seq) x2 = np.zeros(length + n_c + n_seq) #int2bin bin_ = format(c_init, f'0{n_seq}b') c_init = [int(x) for x in bin_[-n_seq:]] if n_seq else [] c_init = np.flip(c_init) # reverse order # init x1 and x2 x1[0] = 1 x2[0:n_seq] = c_init # and run the generator for idx in range(length + n_c): x1[idx+31] = np.mod(x1[idx+3] + x1[idx], 2) x2[idx+31] = np.mod(x2[idx+3] + x2[idx+2] + x2[idx+1] + x2[idx], 2) # update output sequence for idx in range(length): c[idx] = np.mod(x1[idx+n_c] + x2[idx+n_c], 2) return c
[docs] def select_mcs(mcs_index, table_index=1, channel_type="PUSCH", transform_precoding=False, pi2bpsk=False, verbose=False): # pylint: disable=line-too-long r"""Selects modulation and coding scheme (MCS) as specified in TS 38.214 [3GPP38214]_. Implements MCS tables as defined in [3GPP38214]_ for PUSCH and PDSCH. Parameters ---------- mcs_index : int| [0,...,28] MCS index (denoted as :math:`I_{MCS}` in [3GPP38214]_). table_index : int, 1 (default) | 2 | 3 | 4 Indicates which MCS table from [3GPP38214]_ to use. Starts with index "1". channel_type : str, "PUSCH" (default) | "PDSCH" 5G NR physical channel type. Valid choices are "PDSCH" and "PUSCH". transform_precoding : bool, False (default) If True, the MCS tables as described in Sec. 6.1.4.1 in [3GPP38214]_ are applied. Only relevant for "PUSCH". pi2bpsk : bool, False (default) If True, the higher-layer parameter `tp-pi2BPSK` as described in Sec. 6.1.4.1 in [3GPP38214]_ is applied. Only relevant for "PUSCH". verbose : bool, False (default) If True, additional information will be printed. Returns ------- (modulation_order, target_rate) : Tuple: modulation_order : int Modulation order, i.e., number of bits per symbol. target_rate : float Target coderate. """ # check inputs assert isinstance(mcs_index, int), "mcs_index must be int." assert (mcs_index>=0), "mcs_index cannot be negative." assert isinstance(table_index, int), "table_index must be int." assert (table_index>0), "table_index starts with 1." assert isinstance(channel_type, str), "channel_type must be str." assert (channel_type in ("PDSCH", "PUSCH")), \ "channel_type must be either `PDSCH` or `PUSCH`." assert isinstance(transform_precoding, bool), \ "transform_precoding must be bool." assert isinstance(pi2bpsk, bool), "pi2bpsk must be bool." assert isinstance(verbose, bool), "verbose must be bool." if verbose: print(f"Selected MCS index {mcs_index} for {channel_type} channel " \ f"and Table index {table_index}.") # without pre-coding the Tables from 5.1.3.1 are used if channel_type=="PDSCH" or transform_precoding is False: if table_index==1: # Table 5.1.3.1-1 in 38.214 if verbose: print("Applying Table 5.1.3.1-1 from TS 38.214.") assert mcs_index<29, "mcs_index not supported." mod_orders = [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6] target_rates = [120, 157, 193, 251, 308, 379, 449, 526, 602, 679, 340, 378, 434, 490, 553, 616, 658, 438, 466, 517, 567, 616, 666, 719, 772, 822, 873, 910, 948] elif table_index==2: # Table 5.1.3.1-2 in 38.214 if verbose: print("Applying Table 5.1.3.1-2 from TS 38.214.") assert mcs_index<28, "mcs_index not supported." mod_orders = [2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 8, 8, 8, 8, 8, 8, 8] target_rates = [120, 193, 308, 449, 602, 378, 434, 490, 553, 616, 658, 466, 517, 567, 616, 666, 719, 772, 822, 873, 682.5, 711, 754, 797, 841, 885, 916.5, 948] elif table_index==3: # Table 5.1.3.1-3 in 38.214 if verbose: print("Applying Table 5.1.3.1-3 from TS 38.214.") assert mcs_index<29, "mcs_index not supported." mod_orders = [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6] target_rates = [30, 40, 50, 64, 78, 99, 120, 157, 193, 251, 308, 379, 449, 526, 602, 340, 378, 434, 490, 553, 616, 438, 466, 517, 567, 616, 666, 719, 772] elif table_index==4: # Table 5.1.3.1-4 in 38.214 if verbose: print("Applying Table 5.1.3.1-4 from TS 38.214.") assert mcs_index<27, "mcs_index not supported." mod_orders = [2, 2, 2, 4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 8, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10] target_rates = [120, 193, 449, 378, 490, 616, 466, 517, 567, 616, 666, 719, 772, 822, 873, 682.5, 711, 754, 797, 841, 885, 916.5, 948, 805.5, 853, 900.5, 948] else: raise ValueError("Unsupported table_index.") elif channel_type=="PUSCH": # only if pre-coding is true if table_index==1: # Table 6.1.4.1-1 in 38.214 if verbose: print("Applying Table 6.1.4.1-1 from TS 38.214.") assert mcs_index<28, "mcs_index not supported." # higher layer parameter as defined in 6.1.4.1 if pi2bpsk: if verbose: print("Assuming pi2BPSK modulation.") q=1 else: q=2 mod_orders = [q, q, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6] target_rates = [240/q, 314/q, 193, 251, 308, 379, 449, 526, 602, 679, 340, 378, 434, 490, 553, 616, 658, 466, 517, 567, 616, 666, 719, 772, 822, 873, 910, 948] elif table_index==2: # Table 6.1.4.1-2 in 38.214 if verbose: print("Applying Table 6.1.4.1-2 from TS 38.214.") assert mcs_index<28, "mcs_index not supported." # higher layer parameter as defined in 6.1.4.1 if pi2bpsk: if verbose: print("Assuming pi2BPSK modulation.") q=1 else: q=2 mod_orders = [q, q, q, q, q, q, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 6] target_rates = [60/q, 80/q, 100/q, 128/q, 156/q, 198/q, 120, 157, 193, 251, 308, 379, 449, 526, 602, 679, 378, 434, 490, 553, 616, 658, 699, 772, 567, 616, 666, 772] else: raise ValueError("Unsupported table_index.") else: raise ValueError("Unsupported channel_type.") mod_order = mod_orders[mcs_index] target_rate = target_rates[mcs_index] / 1024 # rate is given as r*1024 if verbose: print("Modulation order: ", mod_order) print("Target code rate: ", target_rate) return mod_order, target_rate
[docs] def calculate_tb_size(modulation_order, target_coderate, target_tb_size=None, num_coded_bits=None, num_prbs=None, num_ofdm_symbols=None, num_dmrs_per_prb=None, num_layers=1, num_ov=0, tb_scaling=1., verbose=True): # pylint: disable=line-too-long r"""Calculates transport block (TB) size for given system parameters. This function follows the basic procedure as defined in TS 38.214 Sec. 5.1.3.2 and Sec. 6.1.4.2 [3GPP38214]_. Parameters ---------- modulation_order : int Modulation order, i.e., number of bits per QAM symbol. target_coderate : float Target coderate. target_tb_size: None (default) | int Target transport block size, i.e., how many information bits can be encoded into a slot for the given slot configuration. If provided, ``num_prbs``, ``num_ofdm_symbols`` and ``num_dmrs_per_prb`` will be ignored. num_coded_bits: None (default) | int How many coded bits can be fit into a given slot. If provided, ``num_prbs``, ``num_ofdm_symbols`` and ``num_dmrs_per_prb`` will be ignored. num_prbs : None (default) | int Total number of allocated PRBs per OFDM symbol where 1 PRB equals 12 subcarriers. num_ofdm_symbols : None (default) | int Number of OFDM symbols allocated for transmission. Cannot be larger than 14. num_dmrs_per_prb : None (default) | int Number of DMRS (i.e., pilot) symbols per PRB that are NOT used for data transmission. Sum over all ``num_ofdm_symbols`` OFDM symbols. num_layers: int, 1 (default) Number of MIMO layers. num_ov : int, 0 (default) Number of unused resource elements due to additional overhead as specified by higher layer. tb_scaling: float, 0.25 | 0.5 | 1 (default) TB scaling factor for PDSCH as defined in TS 38.214 Tab. 5.1.3.2-2. Valid choices are 0.25, 0.5 and 1.0. verbose : bool, False (default) If True, additional information will be printed. Returns ------- (tb_size, cb_size, num_cbs, cw_length, tb_crc_length, cb_crc_length, cw_lengths) : Tuple: tb_size : int Transport block size, i.e., how many information bits can be encoded into a slot for the given slot configuration. cb_size : int Code block (CB) size. Determines the number of information bits (including TB/CB CRC parity bits) per codeword. num_cbs : int Number of code blocks. Determines into how many CBs the TB is segmented. cw_lengths : list of ints Each list element defines the codeword length of each of the ``num_cbs`` codewords after LDPC encoding and rate-matching. The total number of coded bits is :math:`\sum` ``cw_lengths``. tb_crc_length : int Length of the TB CRC. cb_crc_length : int Length of each CB CRC. Note ---- Due to rounding, ``cw_lengths`` (=length of each codeword after encoding), can be slightly different within a transport block. Thus, ``cw_lengths`` is given as a list of ints where each list elements denotes the number of codeword bits of the corresponding codeword after rate-matching. """ # supports two modi: # a) target_tb_size and num_coded_bits given # b) available res in slot given # mode a) if target_tb_size is not None: if num_coded_bits is None: raise ValueError("num_coded_bits cannot be None if " \ "target_tb_size is provided.") assert num_coded_bits%1==0, "num_coded_bits must be int." num_coded_bits = int(num_coded_bits) assert num_coded_bits%num_layers==0, \ "num_coded_bits must be a multiple of num_layers." assert num_coded_bits%modulation_order==0, \ "num_coded_bits must be a multiple of modulation_order." assert target_tb_size%1==0, "target_tb_size must be int." n_info = int(target_tb_size) assert target_tb_size<num_coded_bits, \ "Invalid transport block parameters. target_tb_size must be less " \ "than the requested num_coded_bits excluding the overhead for the "\ "TB CRC." else: if num_coded_bits is not None: print("num_coded_bits will be ignored if target_tb_size " \ "is None.") assert num_ofdm_symbols in range(1, 15),\ "num_ofdm_symbols must be in the range from 1 to 14." assert num_prbs in range(1, 276),\ "num_prbs must be in the range from 1 to 276." assert tb_scaling in (0.25, 0.5, 1.), \ "tb_scaling must be in (0.25,0.5,1.)." # compute number of data symbols per prb n_re_per_prb = 12*num_ofdm_symbols - num_dmrs_per_prb - num_ov # number of coded bits that fit into the given slot configuration num_coded_bits = int(tb_scaling * n_re_per_prb \ * modulation_order * num_layers * num_prbs) # number of allocated REs # the max. number of REs per PRB is limited to 156 in 38.214 n_re = min(156, n_re_per_prb) * num_prbs # include tb_scaling as defined in Tab. 5.1.3.2-2 38.214 n_info = target_coderate * tb_scaling * n_re \ * modulation_order * num_layers if n_info <= 3824: c=1 # go to step 3 in 38.214 5.1.3.2 n = max(3, np.floor(np.log2(n_info)) - 6) n_info_q = max(24, 2**n * np.floor(n_info/2**n)) # explicit lengths given in Tab 5.1.3.2-1 tab51321 = [24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128, 136, 144, 152, 160, 168, 176, 184, 192, 208, 224, 240, 256, 272, 288, 304, 320, 336, 352, 368, 384, 408, 432, 456, 480, 504, 528, 552, 576, 608, 640, 672, 704, 736, 768, 808, 848, 888, 928, 984, 1032, 1064, 1128, 1160, 1192, 1224, 1256, 1288, 1320, 1352, 1416, 1480, 1544, 1608, 1672, 1736, 1800, 1864, 1928, 2024, 2088, 2152, 2216, 2280, 2408, 2472, 2536, 2600, 2664, 2728, 2792, 2856, 2976, 3104, 3240, 3368, 3496, 3624, 3752, 3824] # find closest TBS that is not less n_info for tbs in tab51321: if tbs>=n_info_q: break else: # go to step 4 in 38.212 5.3.1.2 n = np.floor(np.log2(n_info-24)) - 5 # "ties in the round function are broken towards next largest integer" n_info_q = max(3840, 2**n * np.round((n_info-24)/2**n)) if target_coderate<=1/4: c = np.ceil((n_info_q + 24) / 3816) tbs = 8 * c * np.ceil((n_info_q + 24) / (8 * c)) - 24 else: if n_info > 8424: c = np.ceil((n_info_q + 24) / 8424) tbs = 8 * c * np.ceil((n_info_q + 24) / (8*c)) - 24 else: c = 1 tbs = 8 * np.ceil((n_info_q + 24) / 8) - 24 # TB CRC see 6.2.1 in 38.212 if tbs>3824: tb_crc_length = 24 else: tb_crc_length = 16 # if tbs > max CB length, CRC-24 is added; see 5.2.2 in 38.212 if c>1: # if multiple CBs exists, additional CRC is applied cb_crc_length = 24 else: cb_crc_length = 0 cb_size = (tbs + tb_crc_length)/c + cb_crc_length # bits per CW # internal sanity check assert (cb_size%1==0), "cb_size not an integer." # c is the number of code blocks num_cbs = int(c) cb_size = int(cb_size) tb_size = int(tbs) # cb_length as specified in 5.4.2.1 38.212 # remark: the length can be different for multiple cws due to rounding # thus a list of lengths is generated cw_length = [] for j in range(num_cbs): # first blocks are floored if j <= num_cbs \ - np.mod(num_coded_bits/(num_layers*modulation_order),num_cbs)-1: l = num_layers * modulation_order \ * np.floor(num_coded_bits / (num_layers*modulation_order*num_cbs)) cw_length += [int(l)] else: # last blocks are ceiled l = num_layers * modulation_order \ * np.ceil(num_coded_bits / (num_layers*modulation_order*num_cbs)) cw_length += [int(l)] # sanity check that total length matches to total number of cws assert num_coded_bits==np.sum(cw_length), \ "Internal error: invalid codeword lengths." effective_rate = tb_size / num_coded_bits if verbose: print("Modulation order:", modulation_order) if target_coderate is not None: print(f"Target coderate: {target_coderate:.3f}") print(f"Effective coderate: {effective_rate:.3f}") print("Number of layers:", num_layers) print("------------------") print("Info bits per TB: ", tb_size) print("TB CRC length: ", tb_crc_length) print("Total number of coded TB bits:", num_coded_bits) print("------------------") print("Info bits per CB:", cb_size) print("Number of CBs:", num_cbs) print("CB CRC length: ", cb_crc_length) print("Output CB lengths:", cw_length) return tb_size, cb_size, num_cbs, cw_length, tb_crc_length, cb_crc_length