Physical Channels

Physical Downlink Shared Channel

The module pdsch.py implements the PDSCH class which encapsulates the Physical Downlink Shared Channel. It is a downlink channel that delivers user data from gNB to UE. PDSCH occupies a grid of Resource Blocks (RBs) within a slot. Usually, one or more OFDM symbols are used by the PDCCH, and the remaining resources are available for the PDSCH.

The gNB schedules PDSCH resources for UEs based on their channel quality, data requirements, and fairness considerations. PDSCH uses LDPC (Low-Density Parity-Check) coding to provide forward error correction, enhancing the robustness of data transmission over the wireless channel.

In Multiple Input, Multiple Output (MIMO) systems, a PDSCH is distributed among multiple layers. PDSCH includes Demodulation Reference Signals (DMRS) to assist the UE in channel estimation and demodulation, ensuring accurate data reception. It may also include Phase Tracking Reference Signals (PTRS) which enable suppression of phase noise and common phase error, particularly important at high carrier frequencies such as millimeter-wave bands.

class neoradium.pdsch.PDSCH(bwp, **kwargs)

This class encapsulates the configuration and functionality of a Physical Downlink Shared Channel (PDSCH) that delivers user data transmitted from gNB to UE.

Parameters:
  • bwp (BandwidthPart) – The BandwidthPart object that represents the resources used by this PDSCH for transmission of user data from gNB to UE.

  • kwargs (dict) –

    A set of optional arguments.

    mappingType:

    The mapping type used by this PDSCH and its associated DMRS object. It is a string that can be either 'A' or 'B'. The default is 'A'.

    In mapping type 'A', the first DM-RS OFDM symbol index is 2 or 3 and DM-RS is mapped relative to the start of slot boundary, regardless of where in the slot the actual data transmission starts. The user data in this case usually occupies most of the slot.

    In mapping type 'B', the first DM-RS OFDM symbol is the first OFDM symbol of the data allocation, that is, the DM-RS location is not given relative to the slot boundary but relative to where the user data is located. The user data in this case usually occupies a small fraction of the slot to support very low latency.

    numLayers:

    The number of transmission layers for this PDSCH. It must be an integer from 1 to 8, with 1 as the default.

    modulation:

    A string, or a tuple or list of two strings specifying the modulation scheme used for data transmitted in this PDSCH based on 3GPP TS 38.211, Table 7.3.1.2-1. The default is '16QAM'. Here is a list of supported modulation schemes:

    Modulation Scheme

    Modulation Order (qm)

    QPSK

    2

    16QAM

    4

    64QAM

    6

    256QAM

    8

    1024QAM

    10

    If modulation is a string and there are two codewords in this PDSCH, the same modulation scheme is used for both codewords. If there are two codewords in this PDSCH, and you want to use different modulation schemes for the two codewords, you can specify two different modulation schemes in a tuple or list of strings. For example:

    # Using "QPSK" for the first codeword and "16QAM" for the second codeword
    modulation = ("QPSK", "16QAM")
    

    The specified modulation scheme(s) are used to create one or two Modem objects.

    csiRsConfig:

    A CsiRsConfig object that contains CSI-RS configuration information. If specified, it is used to reserve CSI-RS resources in the grid so that they are not assigned to PDSCH, DM-RS, or PT-RS.

    reservedPrbSets:

    A list of ReservedPrbSet objects that are used to reserve the specified resource blocks (RBs) at the specified OFDM symbols based on the patterns defined in the ReservedPrbSet objects. The default is an empty list which means no reserved PRBs.

    portSet:

    A list of ports used by this PDSCH and its associated DMRS object. If not specified, by default, this is set based on the number of layers specified by numLayers. For example, for a 2-layer PDSCH, the portSet is set to {0, 1}, which corresponds to DM-RS port numbers {1000, 1001}.

    sliv:

    Start and Length Indicator Value. If specified, it is used to determine the start and length of consecutive OFDM symbols used by this PDSCH based on 3GPP TS 38.214, Section 5.1.2.1. The default is None. See Specifying the OFDM symbols below for more information.

    symStart:

    The index of the first OFDM symbol used for this PDSCH. The default is None. See Specifying the OFDM symbols below for more information.

    symLen:

    The number of consecutive OFDM symbols used by this PDSCH starting at symStart. The default is None. See Specifying the OFDM symbols below for more information.

    symSet:

    A list of OFDM symbol indices that are used by this PDSCH. See Specifying the OFDM symbols below for more information.

    prbSet:

    The list of physical resource blocks (PRBs) used by this PDSCH. The default is all the RBs in the BandwidthPart object bwp.

    interleavingBundleSize:

    This is for backward compatibility and will be removed in future versions. Set interleavingBundleSize in the BandwidthPart object.

    rnti:

    The Radio Network Temporary Identifier. The default is 1. It is used with nID below to initialize a Gold sequence used for the scrambling process. See 3GPP TS 38.211, Section 7.3.1.1 for more information.

    nID:

    The scrambling identity. It is used with rnti to initialize a Gold sequence used for the scrambling process. See 3GPP TS 38.211, Section 7.3.1.1 for more information. If not specified, it is set to bwp.cellId.

    prgSize:

    The size of Precoding RB Groups (PRGs). It can be one of 0 (default), 2, or 4. The value 0 means Wideband Precoding which means the same precoding is used for the whole bandwidth of this PDSCH. Subband values (2 or 4) enable a separate precoder per PRG; use a smaller prgSize when the channel is more frequency-selective (e.g., long delay spread, low antenna correlation across frequency), at the cost of higher PMI feedback overhead. prgSize=4 is the natural midpoint when subband precoding is needed without the full overhead of prgSize=2. See 3GPP TS 38.214, Section 5.1.2.3 for more information.

Specifying the OFDM symbols:

You can specify the OFDM symbols used by this PDSCH in different ways:

  • If sliv is specified, it is used to determine the start and length of consecutive OFDM symbols used by this PDSCH based on 3GPP TS 38.214, Section 5.1.2.1. In this case, the parameters symStart, symLen, and symSet are ignored.

  • If sliv is not specified and both symStart and symLen are specified, they are used to determine the OFDM symbols used by this PDSCH. In this case the parameter symSet is ignored.

  • If sliv, symStart, and symLen are not specified but symSet is specified, it is used to determine the OFDM symbols used by this PDSCH.

  • If neither of sliv, symStart, symLen, and symSet are specified, the OFDM symbols are automatically assigned based on mappingType and cpType parameter of the BandwidthPart object bwp.

Other Properties:

numCW:

The number of codewords derived from the numLayers parameter. It is either 1 or 2.

modems:

A list of one or two (depending on numCW) Modem object(s) used internally for modulation/demodulation of the codewords.

dmrs:

The DMRS object associated with this PDSCH. You can use setDMRS() method to set the DMRS object associated with this PDSCH.

slotNo:

This returns the slotNo property of the Carrier object containing bwp.

frameNo:

This returns the frameNo property of the Carrier object containing bwp.

slotNoInFrame:

This returns the slotNoInFrame property of the Carrier object containing bwp.

The notebook End-to-End PDSCH Communication Simulation shows how to create an end-to-end PDSCH communication pipeline.

print(indent=0, title='PDSCH Properties:', getStr=False)

Prints the properties of this PDSCH object.

Parameters:
  • indent (int) – The number of indentation characters.

  • title (str) – If specified, it is used as the title for the printed information.

  • getStr (bool) – If True, returns a string instead of printing it.

Returns:

If the getStr parameter is True, then this function returns the information in a string. Otherwise, nothing is returned.

Return type:

None or str

setDMRS(**kwargs)

Creates and initializes a DMRS object associated with this PDSCH object.

Parameters:

kwargs (dict) – A dictionary of parameters passed directly to the constructor of the DMRS class. Please refer to this class for a list of parameters that can be used to configure DM-RS.

setPTRS(**kwargs)

Creates and initializes a PTRS object associated with this PDSCH object. Please note that you must first use the setDMRS() function to initialize the DMRS object before calling this function.

Parameters:

kwargs (dict) – A dictionary of parameters passed directly to the constructor of the PTRS class. Please refer to this class for a list of parameters that can be used to configure PT-RS.

getGrid()

DEPRECATED: This method is deprecated and will be removed in future releases. Please use the initGrid() method instead.

initGrid()

Creates a Grid object for this PDSCH and populates it with the configured DMRS and PTRS reference signals.

If a CsiRsConfig object was provided when this PDSCH was created, it will be used to reserve the CSI-RS resources so that they are not assigned to PDSCH, DM-RS, or PT-RS.

This function also marks all resources corresponding to the reservedPrbSets parameter as “RESERVED” in the newly created resource grid.

The returned resource grid contains all reference signals and is ready to be populated with the user data (See setPdschData() method).

Returns:

A Grid object representing the resource grid for this PDSCH pre-populated with DM-RS and PT-RS.

Return type:

Grid

getReIndexes(grid, reTypeStr)

DEPRECATED: This method is deprecated and will be removed in future releases. Please use the getReIndexes() method instead.

getNumREsFromIndexes(indexes)

Returns the number of resource elements included in indexes for each codeword. The returned value is a list of one or two integers depending on the number of codewords (numCW).

Parameters:

indexes (3-tuple) – A tuple of 3 lists specifying locations of a set of resource elements in the resource grid. For example, this can be obtained using the getReIndexes() function.

Returns:

A list of one or two integers depending on the number of codewords (numCW), indicating the number of resource elements (REs) included in indexes for each codeword.

Return type:

list

getBitCapacity()

Returns the total number of bits corresponding to PDSCH resource elements. The returned value is a list of one or two integers depending on the number of codewords (numCW).

Returns:

A list of one or two integers depending on the number of codewords (numCW), indicating the number of PDSCH bits for each codeword.

Return type:

list

Note

This function replaces the deprecated function getBitSizes(). The following example shows how to migrate existing code to use this method:

# Old:
grid = pdsch.getGrid()
numBits = pdsch.getBitSizes(grid)

# New:
pdsch.initGrid()
numBits = pdsch.getBitCapacity()
getBitSizes(grid=None, reTypeStr='PDSCH')

DEPRECATED: This function is deprecated and will be removed in future releases. Please use the getBitCapacity() method instead.

setPdschData(dataBits)

Populates this PDSCH’s resource grid with the user data provided in dataBits.

This function performs the following operations:

Scrambling:

Scrambling of the specified dataBits using the rnti and nID properties of this PDSCH. These properties are used to initialize a Gold sequence which is then used for the scrambling process according to 3GPP TS 38.211, Section 7.3.1.1. The data bits for each codeword are scrambled separately.

Modulation:

Converting the scrambled binary data stream into complex symbols for each resource element assigned for user data. The modulation process is performed by the Modem objects in the modems list of this PDSCH. The modulation for each codeword is performed separately by its own dedicated Modem object.

Layer Mapping:

Distributing the modulated complex symbols across one or more transmission layers of this PDSCH according to 3GPP TS 38.211, Section 7.3.1.3.

Parameters:

dataBits (list, tuple, or NumPy array) –

Specifies the user data bits that are used to populate the specified resource grid. It can be one of the following:

tuple of NumPy arrays:

Depending on the number of codewords (numCW), the tuple can have one or two 1D NumPy arrays of bits each specifying the user data bits for each codeword.

NumPy array:

A one or two dimensional NumPy array. It is a 1D array, only if we have one codeword and the given NumPy array is used for the single codeword. The 2D NumPy array can be used for cases with one or two codewords. The first dimension of the NumPy array in this case should match the number of codewords (numCW).

list of NumPy arrays:

Depending on the number of codewords (numCW), the list can have one or two 1D NumPy arrays of bits each specifying the user data bits for each codeword.

Note

This function replaces the deprecated function populateGrid(). The following example shows how to migrate existing code to use this method:

# Old:
grid = pdsch.getGrid()                       # Create a resource grid already populated with DMRS
txBlockSize = pdsch.getTxBlockSize(codeRate) # Calculate the Transport Block Size
txBlock = random.bits(txBlockSize[0])        # Create random binary data
numBits = pdsch.getBitSizes(grid)            # Actual number of bits available in the resource grid
rateMatchedCodeBlocks = ldpcEncoder.getRateMatchedCodeBlocks(txBlock, numBits[0])
pdsch.populateGrid(grid, rateMatchedCodeBlocks)

# New:
pdsch.initGrid()                             # Create and initialize PDSCH's internal grid
txBlock = random.bits(ldpc.txBlockSizes[0])  # Create random binary data
numBits = pdsch.getBitCapacity()             # Actual number of bits available in the resource grid
rateMatchedCodeBlocks = ldpc.encode(txBlock, numBits[0])
pdsch.setPdschData(rateMatchedCodeBlocks)
populateGrid(grid, bits=None)

DEPRECATED: This function is deprecated and will be removed in future releases. Please use the setPdschData() method instead.

getLLRs(eqGrid, llrScales=None, noiseVar=None, useMax=True)

This method is used at the receiving side where the log-likelihood ratios (LLRs) are extracted from the equalized resource grid eqGrid. This is in some sense the opposite of the setPdschData() method since it does the following:

Deinterleaving:

Converting Physical Resource Blocks (PRBs) to Virtual Resource Blocks (VRBs). If enabled, the resources are re-ordered based on the interleaving configuration given by interleavingBundleSize according to 3GPP TS 38.214, Section 5.1.4.1 to get the data in its original order.

Layer Demapping:

Extracting the modulated complex symbols for each codeword from different layers of this PDSCH according to 3GPP TS 38.211, Section 7.3.1.3.

Demodulation:

Converting complex symbols to log-likelihood ratios (LLRs) using the Modem objects in the modems list of this PDSCH. The demodulation for each codeword is performed separately by its own dedicated Modem object. This produces one or two sets of LLRs for each codeword.

Descrambling:

The descrambling of the demodulated LLRs using the rnti and nID properties of this PDSCH. These properties are used to initialize a Gold sequence which is then used for the descrambling process according to 3GPP TS 38.211, Section 7.3.1.1. The LLRs for each codeword are descrambled separately.

This function returns a list of one or two NumPy arrays representing the LLRs for each codeword.

Parameters:
  • eqGrid (Grid) – The equalized received resource grid associated with this PDSCH. Usually this is the Grid object obtained after equalization in the receiver pipeline (See the equalize() function).

  • llrScales (3-D NumPy array or None) –

    The log-likelihood ratio (LLR) scaling factors which are used by demodulation process when extracting log-likelihood ratios (LLRs) from the equalized resource grid. The shape of this array must be the same shape as eqGrid. Typically obtained as the second return value of equalize():

    eqGrid, llrScales = pdsch.equalize(rxGrid, channelMatrix)
    llrs = pdsch.getLLRs(eqGrid, llrScales)
    

    If None, no per-RE scaling is applied.

  • noiseVar (float or None) – The variance of the Additive White Gaussian Noise (AWGN) present in the received resource grid. If this is not provided (noiseVar=None), This function uses the noiseVar property of the eqGrid object.

  • useMax (bool) – If True, this implementation uses the Max function in the calculation of the LLR values. This is faster but uses an approximation and is slightly less accurate than the actual log-likelihood method which uses logarithm and exponential functions. If False, the slower more accurate method is used.

Returns:

A list of one or two NumPy arrays each representing the LLRs for each codeword.

Return type:

list

Note

This function replaces the deprecated function getLLRsFromGrid(). The following example shows how to migrate existing code to use this method:

# Old:
llrs = pdsch.getLLRsFromGrid(eqGrid, pdschIndexes, llrScales)

# New:
llrs = pdsch.getLLRs(eqGrid, llrScales)
getLLRsFromGrid(rxGrid, pdschIndexes=None, llrScales=None, noiseVar=None, useMax=True)

DEPRECATED: This method is deprecated and will be removed in future releases. Please use the getLLRs() method instead.

This method is used at the receiving side where the log-likelihood ratios (LLRs) are extracted from the received resource grid rxGrid. This is in some sense the opposite of the populateGrid() method since it does the following:

Deinterleaving:

Converting Physical Resource Blocks (PRBs) to Virtual Resource Blocks (VRBs). If enabled, the resources are re-ordered based on the interleaving configuration given by interleavingBundleSize according to 3GPP TS 38.214, Section 5.1.4.1 to get the data in its original order.

Layer Demapping:

Extracting the modulated complex symbols for each codeword from different layers of this PDSCH according to 3GPP TS 38.211, Section 7.3.1.3.

Demodulation:

Converting complex symbols to log-likelihood ratios (LLRs) using the Modem objects in the modems list of this PDSCH. The demodulation for each codeword is performed separately by its own dedicated Modem object. This produces one or two sets of LLRs for each codeword.

Descrambling:

The descrambling of the demodulated LLRs using the rnti and nID properties of this PDSCH. These properties are used to initialize a Gold sequence which is then used for the descrambling process according to 3GPP TS 38.211, Section 7.3.1.1. The LLRs for each codeword are descrambled separately.

This function returns a list of one or two NumPy arrays representing the LLRs for each codeword.

Parameters:
  • rxGrid (Grid) – The equalized received resource grid associated with this PDSCH. Usually this is the Grid object obtained after equalization in the receiver pipeline (See the equalize() function).

  • pdschIndexes (3-tuple) – A tuple of 3 lists specifying locations of the set of resource elements in rxGrid that are assigned to the user data. The function getReIndexes() is typically used to obtain this. If not specified, the internal parameter dataIndices is used.

  • llrScales (3-D NumPy array) – The log-likelihood ratio (LLR) scaling factors which are used by demodulation process when extracting log-likelihood ratios (LLRs) from the equalized resource grid. The shape of this array must be the same shape as rxGrid.

  • noiseVar (float or None) – The variance of the Additive White Gaussian Noise (AWGN) present in the received resource grid. If this is not provided (noiseVar=None), This function uses the noiseVar property of the rxGrid object.

  • useMax (bool) – If True, this implementation uses the Max function in the calculation of the LLR values. This is faster but uses an approximation and is slightly less accurate than the actual log-likelihood method which uses logarithm and exponential functions. If False, the slower more accurate method is used.

Returns:

A list of one or two NumPy arrays each representing the LLRs for each codeword.

Return type:

list

getHardBits(eqGrid, llrScales=None, noiseVar=None, useMax=True)

This method first calls the getLLRs() function above and then uses hard-decisions on the returned LLRs to get the output user bits.

This can be used when there is no channel coding in the communication pipeline. It returns a list of one or two NumPy arrays of bits for each codeword.

Parameters:
  • eqGrid (Grid) – The equalized received resource grid associated with this PDSCH. Usually this is the Grid object obtained after equalization in the receiver pipeline (See the equalize() function).

  • llrScales (3-D NumPy array) – The log-likelihood ratio (LLR) scaling factors which are used by demodulation process when extracting log-likelihood ratios (LLRs) from the equalized resource grid. The shape of this array must be the same shape as eqGrid.

  • noiseVar (float or None) – The variance of the Additive White Gaussian Noise (AWGN) present in the received resource grid. If this is not provided (noiseVar=None), This function uses the noiseVar property of the eqGrid object.

  • useMax (bool) – If True, this implementation uses the Max function in the calculation of the LLR values. This is faster but uses an approximation and is slightly less accurate than the actual log-likelihood method which uses logarithm and exponential functions. If False, the slower more accurate method is used.

Returns:

A list of one or two NumPy arrays of bits for each codeword.

Return type:

list

Returns:

A list of one or two NumPy arrays each representing the LLRs for each codeword.

Return type:

list

Note

This function replaces the deprecated function getHardBitsFromGrid(). The following example shows how to migrate existing code to use this method:

# Old:
llrs = pdsch.getHardBitsFromGrid(eqGrid, pdschIndexes, llrScales)

# New:
llrs = pdsch.getHardBits(eqGrid, llrScales)
getHardBitsFromGrid(rxGrid, pdschIndexes, llrScales=None, noiseVar=None, useMax=True)

DEPRECATED: This method is deprecated and will be removed in future releases. Please use the getHardBits() method instead.

getDataSymbols(grid=None)

This is a helper function that returns the modulated complex symbols for all user data in grid for this PDSCH object. The following code shows two different ways to do this:

# Getting the indices of user data in "grid" and then using them to get "dataSymbols1":
dataReIndexes = myPdsch.getReIndexes(grid, "PDSCH")
dataSymbols1 = grid[ dataReIndexes ]

# Using the "getDataSymbols" function:
dataSymbols2 = myPdsch.getDataSymbols(grid)

assert np.all(dataSymbols1==dataSymbols2)             # The results are the same
Parameters:

grid (Grid) – The resource grid associated with this PDSCH containing the user data.

Returns:

A 1D NumPy array of modulated complex symbols corresponding to the user data in grid.

Return type:

NumPy array

getPrecodingMatrix(channelMatrix)

This function calculates the precoding matrix that can be applied to a resource grid. This function supports Precoding RB groups (PRGs) which means different precoding matrices could be applied to different groups of subcarriers in the resource grid. See 3GPP TS 38.214, Section 5.1.2.3 for more details. The prgSize property of PDSCH determines what type of precoding matrix is returned by this function:

Wideband:

If prgSize is set to zero, a single Nt x Nl matrix is returned where Nt is the number of transmitter antennas and Nl is the number of layers in this PDSCH. In this case the same precoding is applied to all subcarriers of the resource grid.

Using PRGs:

If prgSize is set to 2 or 4, a list of tuples of the form (groupRBs, groupF) is returned. For each entry in the list, the Nt x Nl precoding matrix groupF is applied to all subcarriers of the resource blocks listed in groupRBs.

Note

It is assumed that the channelMatrix is obtained based on the same BandwidthPart object as the one used by this PDSCH.

Parameters:

channelMatrix (NumPy array) – An L x K x Nr x Nt complex NumPy array representing the channel matrix. It can be obtained directly from a channel model using the getChannelMatrix() method.

Returns:

Depending on the prgSize property of this PDSCH, the returned value can be:

NumPy Array:

If prgSize is set to zero, a single Wideband Nt x Nl, matrix is returned where Nt is the number of transmitter antennas and Nl is the number of layers in this PDSCH. In this case the same precoding is applied to all subcarriers of the resource grid.

list of tuples:

If prgSize is set to 2 or 4, a list of tuples of the form (groupRBs, groupF) is returned. For each entry in the list, the Nt x Nl precoding matrix groupF is applied to all subcarriers of the resource blocks listed in groupRBs.

Note

The returned precoding matrix (or each groupF in the subband case) is normalized by \(1/\sqrt{N_l}\) per 3GPP TS 38.211, Section 6.3.1.5 so that the total transmit power is preserved across layers. If you compose this output with your own additional precoder factors, do not re-apply this normalization.

Return type:

NumPy array or list of tuples

precodeTo(txGrid, precoder, w=None)

Applies the specified precoding matrix to this grid object and returns a new precoded grid. Optionally, a steering vector can be applied before the precoder. This function supports Precoding RB Groups (PRGs), which means different precoding matrices can be applied to different groups of subcarriers in the resource grid. See 3GPP TS 38.214, Section 5.1.2.3 for more details.

Parameters:
  • txGrid (Grid) – The transmitted resource grid of shape Nt x L x K where Nt is the number of transmitter antennas, L is the number of OFDM symbols, and K is the number of subcarriers. The precoded information is placed in this resource grid.

  • precoder (NumPy array or list of tuples) –

    This function supports two types of precoding:

    Wideband:

    precoder is an Nt x Nl matrix where Nt is the number of transmitter antennas and Nl is the number of layers which must match the number of layers in this PDSCH. In this case the same precoding is applied to all subcarriers of this PDSCH.

    Using PRGs:

    precoder is a list of tuples of the form (groupRBs, groupF). For each entry in the list, the Nt x Nl precoding matrix groupF is applied to all subcarriers of the resource blocks listed in groupRBs.

  • w (NumPy array, optional) – An optional steering vector of shape Nt x 1 applied before the precoder, where Nt is the number of transmitter antennas. When provided, each row of the precoding matrix is multiplied by the corresponding element of w. This allows a directional steering vector to be combined with a PMI-based precoder. If omitted, only the specified precoder is applied.

Note

This function replaces the deprecated function precode(). The following example shows how to migrate existing code to use this method:

# Old:
precodedGrid = grid.precode(precoder)

# New:
txGrid = bwp.createGrid(channel.txAntenna.numEl)
pdsch.precodeTo(txGrid, precoder)
estimateChannel(rxGrid, **kwargs)

Estimate the effective PDSCH channel from DMRS and return an associated residual-error variance.

This function estimates the effective channel between the PDSCH layers and the receive antennas using the DMRS REs. A local channel estimate is first computed for each CDM group by dividing the received DMRS values by the transmitted DMRS reference values and averaging over the REs in the CDM group.

The per-CDM-group channel estimates are then interpolated:

  • across subcarriers, using the center subcarrier of each CDM group, and

  • across OFDM symbols, using the center OFDM symbol of each DMRS symbol position or DMRS symbol pair.

The returned channel estimate is always in physical PRB order. Please refer to the notebook Channel/Noise Estimation for examples of using this function.

Parameters:
  • rxGrid (Grid or numpy.ndarray) – The received resource grid used for channel estimation.

  • **kwargs (dict, optional) –

    Optional keyword arguments.

    extrapolatebool or None

    If True, linear extrapolation is used outside the DMRS-supported frequency and time ranges. If False, the channel estimate is clipped to the boundary values at both ends during interpolation. If not specified, linear extrapolation is used when the DMRS’s additionalPos is non-zero.

    estimateNoiseVarbool

    If True, the function also returns the estimated noise variance, in addition to the estimated channel and residual error variance. The noise variance is computed using a calibration table. Note that the accuracy of the noise variance estimate degrades at high SNR values.

Returns:

  • chanEst (numpy.ndarray) – The estimated effective channel as a NumPy complex array of shape (L, K, nr, numLayers), where:

    • L is the number of OFDM symbols in the received grid,

    • K is the number of subcarriers in the full bandwidth part (i.e., bwp.numRbs * 12), not just the PDSCH allocation — the channel estimate is returned over the entire BWP so it can be fed directly to equalize() / getPrecodingMatrix(),

    • nr is the number of receive antennas, and

    • numLayers is the number of PDSCH layers.

  • errVar (float) – The variance of the effective residual uncertainty associated with the estimated channel. This quantity is computed from the residual between the received CDM-group DMRS values and their reconstruction from the local channel estimates.

    This is not the AWGN noise variance applied to the received signal. It is intended for use as the noiseVar input of equalize() when the channel is estimated using this function.

  • estNoiseVar (float) – The estimated noise variance. This is returned only if estimateNoiseVar is set to True in kwargs. The estimate may be less accurate at high SNR.

Note

  • The local CDM-group channel estimate is computed as the average of cdmY / cdmX over the REs of each CDM group.

  • For double-symbol DMRS configurations, each adjacent DMRS symbol pair is represented by a single time anchor located at the center of the pair.

  • If only one DMRS symbol position is present, the frequency-interpolated channel estimate is copied to all OFDM symbols.

  • The residual-based errVar reflects the uncertainty associated with the estimated channel and may include contributions from AWGN, channel estimation error, interpolation error, and model mismatch.

  • This function replaces the deprecated function estimateChannelLS(). The following example shows how to migrate existing code to use this method:

    # Old:
    estChannelMatrix, noiseVar = rxGrid.estimateChannelLS(pdsch.dmrs)
    
    # New:
    estChannelMatrix, errVar = pdsch.estimateChannel(rxGrid)
    
equalize(rxGrid, h, noiseVar=None)

Equalizes the received resource grid rxGrid using the effective channel h. The effective channel is assumed to include the effect of the precoding matrix, therefore, its shape is L x K x Nr x Nl where L is the number of OFDM symbols, K is the number of subcarriers in the whole bandwidth part, Nr is the number of receiver antennas, and Nl is the number of layers. The output of the equalization process is a new Grid object of shape Nl x L x Kp, where Kp is the number of subcarriers used by this PDSCH (Kp <= K).

The process of de-interleaving resource blocks and mapping from PRBs to VRBs to GRBs (the RBs in the returned resource grid) is performed in this function. Both rxGrid and h are in PRBs. The returned equalized resource grid is in GRBs.

This function also outputs log-likelihood ratio (LLR) scaling factors which are used by the demodulation process when extracting log-likelihood ratios (LLRs) from the equalized resource grid.

This method uses the Minimum Mean Squared Error (MMSE) algorithm for the equalization.

Parameters:
  • rxGrid (Grid) – The received resource grid. It is an Nr x L x K resource grid where Nr is the number of receiver antennas, L is the number of OFDM symbols, and K is the number of subcarriers in the whole bandwidth part.

  • h (4-D complex NumPy array) – This is an L x K x Nr x Nl NumPy array representing the estimated channel matrix, where L is the number of OFDM symbols, K is the number of subcarriers in the whole bandwidth part, Nr is the number of receiver antennas, and Nl is the number of layers.

  • noiseVar (float or None) –

    The variance of noise applied to the received resource grid. If this is not provided, this method tries to use the noise variance of the resource grid obtained by the OFDM demodulation process for the time-domain case or the variance of the noise applied to the received resource grid by the addNoise() method for the frequency domain case (See the noiseVar property of Grid class).

    Note

    When the function estimateChannel() is used to estimate the channel, the errVar returned by that function must be passed to this function through the noiseVar argument. Although errVar is not exactly the AWGN noise variance applied to the received signal, it should be used for equalization whenever an estimated channel is used. The following example shows equalization with perfect channel knowledge versus equalization with an estimated channel:

    # Using the perfect channel from a channel model 'channel'
    channelMatrix = channel.getChannelMatrix()
    precoder = pdsch.getPrecodingMatrix(channelMatrix)
    effChannelMatrix = CdlChannel.getEffChannel(channelMatrix, precoder)
    eqGrid, llrScales = pdsch.equalize(rxGrid, effChannelMatrix)    # 'noiseVar' is stored in 'rxGrid'
    
    # Using an estimated channel
    estChannelMatrix, errVar = pdsch.estimateChannel(rxGrid)
    eqGrid, llrScales = pdsch.equalize(rxGrid, estChannelMatrix, errVar)
    

Returns:

  • eqGrid (Grid) – The equalized grid object of shape Nl x L x Kp where Nl is the number of layers, L is the number of OFDM symbols, and Kp is the number of subcarriers used by this PDSCH.

  • llrScales (3-D NumPy array) – The log-likelihood ratio (LLR) scaling factors which are used by the demodulation process when extracting log-likelihood ratios (LLRs) from the equalized resource grid. The shape of this array is Nl x L x Kp which is similar to eqGrid above.

Note

This function replaces the deprecated function equalize(). The following example shows how to migrate existing code to use this method:

# Old:
eqGrid, llrScales = rxGrid.equalize(effectiveChannelMatrix)

# New:
eqGrid, llrScales = pdsch.equalize(rxGrid, effectiveChannelMatrix)
getTxBlockSize(coderates, xOverhead=None, scaleFactor=1.0)

This function calculates the transport block size based on the desired code rate (coderates), the number of additional overhead resource elements (xOverhead), and the scaling factor (scaleFactor). It returns a list of one or two integer values specifying the size of transport blocks for each codeword. This implementation is based on 3GPP TS 38.214, Section 5.1.3.2.

Parameters:
  • coderates (float, list, NumPy array, or tuple) – If coderates is a float value, it specifies the same code rate for all codewords. If it is a list, NumPy array, or tuple, it should contain one or two code rate values for each codeword. This is the value \(R\) in 3GPP TS 38.214, Section 5.1.3.2.

  • xOverhead (int or None, optional) –

    The number of additional overhead resource elements per PRB to consider when calculating the transport block size. This corresponds to \(N^{PRB}_{oh}\) in 3GPP TS 38.214, Section 5.1.3.2 and, when explicitly specified, must be one of 0, 6, 12, or 18 according to 3GPP TS 38.331.

    If set to None (the default), NeoRadium automatically selects a conservative value based on the configured DM-RS overhead, the maximum CSI-RS overhead that may occur in a slot, and the requested code rate. This helps prevent the effective code rate from becoming excessively large when reference-signal overhead reduces the number of REs available for PDSCH. The automatically selected value remains fixed for the TBS calculation and is chosen from 0, 6, 12, and 18.

    The automatic selection is a NeoRadium implementation convenience based on conservative empirical thresholds. It is not a procedure specified or recommended by 3GPP. To disable the automatic behavior, explicitly provide the desired xOverhead value.

  • scaleFactor (float) – The scaling factor, which must be one of: 0.25, 0.5, or 1.0. This is the value \(S\) in 3GPP TS 38.214, Table 5.1.3.2-2 and reduces the effective TBS by the same proportion. The value is signaled by higher layers; 1.0 is the normal case (full capacity), while 0.5 and 0.25 are used for more robust/fallback transmissions (e.g., certain DCI Format 1_0 cases).

Returns:

A list of one or two integers depending on the number of codewords (numCW), indicating the transport block size for each codeword.

Return type:

list

getLdpcCodec(coderates, numIter=5, nRef=0)

Creates and returns an LdpcCodec object configured based on the current PDSCH settings.

This method derives the required LDPC coding parameters (modulation schemes and transport block sizes) from the PDSCH instance and combines them with the specified coderates to initialize an LdpcCodec object. The resulting codec can be used for encoding and decoding the transport block(s) associated with this PDSCH.

Parameters:
  • coderates (float, list, tuple, or NumPy array) – One or two code rate values corresponding to the codeword(s) of this PDSCH. If a single value is provided and two codewords are present (i.e., numLayers > 4), the same code rate is used for both codewords.

  • numIter (int) – The number of iterations used in the LDPC decoder (Layered Belief Propagation). Higher values may improve decoding performance at the cost of increased complexity. The default is 5.

  • nRef (int) – The reference buffer size used for Low-Buffer Rate Matching (LBRM). This corresponds to \(N_{ref}\) in 3GPP TS 38.212, Section 5.4.2.1. The default is 0 (LBRM disabled).

Returns:

An LDPC codec object configured for the current PDSCH, supporting one or two codewords depending on numLayers.

Return type:

LdpcCodec

Notes

  • The modulation schemes are automatically extracted from the internal modems of this PDSCH.

  • The transport block size(s) are computed using getTxBlockSize() based on the provided coderates.

  • The returned LdpcCodec object is fully configured and ready for encoding and decoding operations.

getHarq(coderates, numIter=5, nRef=0, harqType='CC', numProc=8, rvSequence=[0, 2, 3, 1], maxTries=4, eventCallback=None)

Creates and returns a HarqEntity object configured based on the current PDSCH settings.

This method first creates an LdpcCodec object using the getLdpcCodec() method and then uses it to create a HarqEntity object.

  • For more details about coderates, numIter, and nRef refer to getLdpcCodec().

  • For more details about harqType, numProc, rvSequence, maxTries, eventCallback refer to HarqEntity.

Returns:

A HARQ entity object created based on the given parameters.

Return type:

HarqEntity