Physical Channels
Physical Downlink Shared Channel
The module pdsch.py
implements the PDSCH
class which encapsulates
Physical Downlink Shared Channel. It is a downlink channel that deliver 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 for PDCCH and the remaining resources are available
for 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 MIMO (Multiple Input Multiple Output) 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 enables suppression of phase noise and common phase error, particularly
important at high carrier frequencies such as millimeter wave.
- class neoradium.pdsch.PDSCH(bwp, **kwargs)
This class encapsulates the configuration and functionality of a Physical Downlink Shared Channel (PDSCH) which delivers user data transmitted from gNB to UE.
- Parameters:
bwp (
BandwidthPart
) – TheBandwidthPart
object that represents the resources used by thisPDSCH
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 text string that can be either'A'
or'B'
. The default is'A'
.In mapping type
'A'
, the first DMRS OFDM symbol index is 2 or 3 and DMRS 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 DMRS OFDM symbol is the first OFDM symbol of the data allocation, that is, the DMRS 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 between 1 and 8, with 1 being the default.- modulation:
A text string or a tuple or list of 2 text strings specifying the modulation scheme used for data transmitted in this
PDSCH
based on 3GPP TR 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
)BPSK
1
QPSK
2
16QAM
4
64QAM
6
256QAM
8
1024QAM
10
If
modulation
is a text string, and there are two code-words in thisPDSCH
, the same modulation scheme is used for both code-words. If there are two code-words in thisPDSCH
, and you want to use different modulation schemes for the two code-words, then you can specify two different modulation schemes in a tuple or list of text strings. For example:# Using "QPSK" for the first code-word and "16QAM" for the second code-word modulation = ("QPSK", "16QAM")
The specified modulation scheme(s) are used to create one or two
Modem
objects.- reservedRbSets:
A list of
ReservedRbSet
objects that are used to reserve the specified resource blocks (RBs) at the specified OFDM symbols based on the patterns defined in theReservedRbSet
objects. The default is an empty list which means no reserved RBs.- reservedReMap:
The map of additional reserved resource elements (REs) as a 3D list of the form
portNo x symNo x reNo
. The default is an empty list which means no reserved REs. The following additional rules makes configuring reserved REs both easy and flexible.If
reservedReMap
has data for only one port, it means that the map is the same for all ports.If in a port, the reserved REs are given only for a single symbol, it is assumed the same REs are reserved in all symbols.
Here are some examples:
# REs 5, 17, and 29 are reserved on all ports and all symbols reservedReMap = [[[5,17,29]]] # REs 5, 17, and 29 are reserved on all ports on OFDM symbol 2 only reservedReMap = [[ [], [], [5,17,29], [], [], [], [], [], [], [], [], [], [], [] ]] # REs 5, 17, and 29 are reserved on all symbols of port index 1 (assuming we have 3 ports) reservedReMap = [[], [[5,17,29]], []]
- 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 isNone
. See Specifying the OFDM symbols below for more information.- symStart:
The index of the first OFDM symbol used for this
PDSCH
. The default isNone
. See Specifying the OFDM symbols below for more information.- symLen:
The number of consecutive OFDM symbols used by this
PDSCH
starting atsymStart
. The default isNone
. See Specifying the OFDM symbols below for more information.- symSet:
A list of OFDM symbol indexes 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 theBandwidthPart
objectbwp
- interleavingBundleSize:
The bundle size of interleaving process. It can be one of 0 (default), 2, or 4. The value 0 means interleaving is disabled (default). See 3GPP TS 38.211, Section 7.3.1.6 for more information.
- rnti:
The Radio Network Temporary Identifier. The default is 1. It is used with
nID
below to initialize a golden sequence used for the scrambling process. See 3GPP TS 38.211, Section 7.3.1.1 for more information.- nID:
The scrambling identity. The default is 1. It is used with
rnti
to initialize a golden sequence used for the scrambling process. See 3GPP TS 38.211, Section 7.3.1.1 for more information.- 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
. 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 thisPDSCH
based on 3GPP TS 38.214, Section 5.1.2.1. In this case, the parameterssymStart
,symLen
, andsymSet
are ignored.If
sliv
is not specified and bothsymStart
andsymLen
are specified, they are used to determine the OFDM symbols used by thisPDSCH
. In this case the parametersymSet
is ignored.If
sliv
,symStart
, andsymLen
are not specified butsymSet
is specified, it is used to determine the OFDM symbols used by thisPDSCH
.If neither of
sliv
,symStart
,symLen
, andsymSet
are specified, the OFDM symbols are automatically assigned based onmappingType
andcpType
parameter of theBandwidthPart
objectbwp
.
Other Properties:
- numCW:
The number of code-words 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 code-words.- dmrs:
The
DMRS
object associated with thisPDSCH
. You can usesetDMRS()
method to set theDMRS
object associated with thisPDSCH
.- portSet:
The list of ports used by this
PDSCH
and its associatedDMRS
object. By default, this is set to the number of layers specified bynumLayers
. This can be changed by theDMRS
configuration.- slotNo:
This returns the
slotNo
property of theCarrier
object containingbwp
.- frameNo:
This returns the
frameNo
property of theCarrier
object containingbwp
.- slotNoInFrame:
This returns the
slotNoInFrame
property of theCarrier
object containingbwp
.- slotMap:
This is the map of resource blocks used for each OFDM symbol in this
PDSCH
in the form of a 2D list (A list of lists). Each element in the list corresponds to one OFDM symbol. If an OFDM symbol has no resource blocks allocation in thisPDSCH
, its corresponding element is an empty list. Otherwise, it is a list of indexes of all resource blocks used in that OFDM symbol in the order they should be allocated (based on the interleaving process). Note that the reserved resource blocks specified byreservedRbSets
above are not included in theslotMap
.
The notebook PDSCH end-to-end communication shows how to create an end-to-end pipeline of PDSCH communication.
- print(indent=0, title='PDSCH Properties:', getStr=False)
Prints the properties of this
PDSCH
object.- Parameters:
indent (int (default: 0)) – The number of indentation characters.
title (str or None (default: None)) – If specified, it is used as a title for the printed information.
getStr (Boolean (default: False)) – If
True
, it returns the information in a text string instead of printing the information.
- Returns:
If the
getStr
parameter isTrue
, then this function returns the information in a text string. Otherwise, nothing is returned.- Return type:
None or str
- setDMRS(**kwargs)
Creates and initializes a
DMRS
object associated with thisPDSCH
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 DMRS.
- setPTRS(**kwargs)
Creates and initializes a
PTRS
object associated with thisPDSCH
object. Please note that you must first use thesetDMRS()
function to initialize theDMRS
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 PTRS.
- getGrid(useReDesc=False)
Creates a
Grid
object for thisPDSCH
and a populates it with the configuredDMRS
andPTRS
reference signals. It also marks all the resources corresponding to thereservedRbSets
andreservedReMap
parameters 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
populateGrid()
method).- Parameters:
useReDesc (Boolean (default: False)) – If
True
, the resource grid will also include additional fields that describe the content of each resource element (RE). This can be used during the debugging to make sure the resources are allocated correctly.- Returns:
A
Grid
object representing the resource grid for thisPDSCH
pre-populated with reference signals.- Return type:
- getReIndexes(grid, reTypeStr)
Returns the indexes of all resource elements of the specified resource grid that are allocated for this
PDSCH
with the content type specified by thereTypeStr
.This is similar to the
getReIndexes()
of theGrid
class with two main differences:This function considers only the resource elements in
grid
that are assigned to thisPDSCH
.The indexes are ordered based on the interleaving configuration given by
interleavingBundleSize
according to 3GPP TS 38.214, Section 5.1.4.1.
For example, the following code first gets the indexes of all DMRS values in
myPdsch
and uses the returned indexes to retrieve the DMRS values.myGrid = myPdsch.getGrid() dmrsIdx = myPdsch.getReIndexes(myGrid, "DMRS") # Get the indexes of all DMRS values dmrsValues = myGrid[indexes] # Get all DMRS values as a 1-D array.
- Parameters:
grid (
Grid
) – The resource grid associated with thisPDSCH
. This can be obtained using thegetGrid
function for example.reTypeStr (str) –
The content type of the desired resource elements in
grid
that are used by thisPDSCH
. Here is a list of values that can be used:- ”UNASSIGNED”:
The un-assigned resource elements.
- ”RESERVED”:
The reserved resource elements. This includes the REs reserved by
reservedRbSets
orreservedReMap
parameters of thisPDSCH
.- ”NO_DATA”:
The resource elements that should not contain any data. For example when the corresponding REs in a different layer is used for transmission of data for a different UE. (See
otherCdmGroups
parameter ofDMRS
class)- ”DMRS”:
The resource elements used for
DMRS
.- ”PTRS”:
The resource elements used for
PTRS
.- ”CSIRS_NZP”:
The resource elements used for Zero-Power (ZP) CSI-RS (See
csirs
).- ”CSIRS_ZP”:
The resource elements used for Non-Zero-Power (NZP) CSI-RS (See
csirs
).- ”PDSCH”:
The resource elements used for user data in a Physical Downlink Shared Channel (
PDSCH
)- ”PDCCH”:
The resource elements used for user data in a Physical Downlink Control Channel (
PDCCH
)- ”PUSCH”:
The resource elements used for user data in a Physical Uplink Shared Channel (
PUSCH
)- ”PUCCH”:
The resource elements used for user data in a Physical Uplink Control Channel (
PUCCH
)
- Returns:
A tuple of three 1-D numpy arrays specifying a list of locations in
grid
. This value can be used directly to access REs at the specified locations. (See the above example)- Return type:
3-tuple
- getNumREsFromIndexes(indexes)
Returns the number of resource elements included in
indexes
for each code-word. The returned value is a list of one or two integers depending on number of code-words (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 number of code-words (
numCW
), indicating the number of resource elements (REs) included inindexes
for each code-word.- Return type:
list
- getBitSizes(grid, reTypeStr='UNASSIGNED')
Returns total number of bits corresponding to the resource elements in
grid
assigned to thisPDSCH
with content type specified byreTypeStr
for each code-word. The returned value is a list of one or two integers depending on number of code-words (numCW
).The default value of
reTypeStr="UNASSIGNED"
is for a common use case where we want to get total number of bits available in thisPDSCH
for user data after setting aside the REs for DMRS, PTRS, and the reserved resources.- Parameters:
grid (
Grid()
) – The resource grid associated with thisPDSCH
. This can be obtained using thegetGrid()
function for example.reTypeStr (str (default: "UNASSIGNED")) – The content type of the desired resource elements used to count the returned number of bits. The default values of
"UNASSIGNED"
causes this function to return total number of (unassigned) bits that are available for user data. Please refer to thegetReIndexes()
function for a list of values that can used forreTypeStr
.
- Returns:
A list of one or two integers depending on number of code-words (
numCW
), indicating the number of bits allocated forreTypeStr
for each code-word.- Return type:
list
- populateGrid(grid, bits=None)
Populates the resource grid specified by
grid
with the user data provided inbits
.This function performs the following operations:
- Scrambling:
Scrambling of the specified
bits
using thernti
andnID
properties of thisPDSCH
. These properties are used to initialize a golden 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 code-word are scrambled separately.- Modulation:
Converting the scrambled binary data stream into complex symbols for each resource elements assigned for user data. The modulation process is performed by the
Modem
objects in themodems
list of thisPDSCH
. The modulation for each code-word is performed separately by its own dedicatedModem
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.- Interleaving:
Converting Virtual Resource Blocks (VRBs) to Physical Resource Blocks (PRBs). 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.
- Parameters:
grid (
Grid
) – TheGrid
object that gets populated with the user data bits.bits (list, tuple, numpy array, or None (default: None)) –
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 code-words (
numCW
), the tuple can have one or two 1D numpy arrays of bits each specifying the user data bits for each code-word.- numpy array:
A one or two dimensional numpy array. It is a 1D array, only if we have one code-word and the given numpy array is used for the single code-word. The 2D numpy array can be used for one or two code-word cases. The first dimension of the numpy array in this case should match the number of code-words (
numCW
).- list of numpy arrays:
Depending on the number of code-words (
numCW
), the list can have one or two 1D numpy arrays of bits each specifying the user data bits for each code-word.- None:
If this is None,
grid
data is not updated. This is used for the (rare) case where we only want to update the resource element descriptions in thegrid
object. See theuseReDesc
parameter of theGrid
class for more information.
- getLLRsFromGrid(rxGrid, pdschIndexes, llrScales=None, noiseVar=None, useMax=True)
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 thepopulateGrid()
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 so get the data in its original order.- Layer Demapping:
Extracting the modulated complex symbols for each code-word 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 themodems
list of thisPDSCH
. The demodulation for each code-word is performed separately by its own dedicatedModem
object. This produces one or two sets of LLRs for each code-word.- Descrambling:
The descrambling of the demodulated LLRs using the
rnti
andnID
properties of thisPDSCH
. These properties are used to initialize a golden sequence which is then used for the descrambling process according to 3GPP TS 38.211, Section 7.3.1.1. The LLRs for each code-word are descrambled separately.
This function returns a list of one or two numpy arrays representing the LLRs for each code-word.
- Parameters:
rxGrid (
Grid
) – The equalized received resource grid associated with thisPDSCH
. Usually this is theGrid
object obtained after equalization in the receiver pipeline (See theequalize()
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 functiongetReIndexes()
is usually used to obtain this.llrScales (3-D numpy array) – The Log-Likelihood Ratios (LLR) scaling factors which are used by demodulating 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 (default: 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 thenoiseVar
property of therxGrid
object.useMax (Boolean (default: True)) – If
True
, this implementation uses theMax
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. IfFalse
, the slower more accurate method is used.
- Returns:
A list of one or two numpy arrays each representing the LLRs for each code-word.
- Return type:
list
- getHardBitsFromGrid(rxGrid, pdschIndexes, llrScales=None, noiseVar=None, useMax=True)
This method first calls the
getLLRsFromGrid()
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 code-word.
- Parameters:
rxGrid (
Grid
) – The equalized received resource grid associated with thisPDSCH
. Usually this is theGrid
object obtained after equalization in the receiver pipeline (See theequalize()
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 functiongetReIndexes()
is usually used to obtain this.llrScales (3-D numpy array) – The Log-Likelihood Ratios (LLR) scaling factors which are used by demodulating 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 (default: 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 thenoiseVar
property of therxGrid
object.useMax (Boolean (default: True)) – If
True
, this implementation uses theMax
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. IfFalse
, the slower more accurate method is used.
- Returns:
A list of one or two numpy arrays of bits for each code-word.
- Return type:
list
- getDataSymbols(grid)
This is a helper function that returns the modulated complex symbols for all user data in
grid
for thisPDSCH
object. The following code shows 2 different ways to do this:# Getting the indexes 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
- 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 matrixes 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 ofPDSCH
determines what type of precoding matrix is returned by this function:- Wideband:
If
prgSize
is set to zero, a singleNt x Nl
, matrix is returned whereNt
is the number of transmitter antenna andNl
is the number of layers in thisPDSCH
. 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, theNt x Nl
precoding matrixgroupF
is applied to all subcarriers of the resource blocks listed ingroupRBs
.
Note
It is assumed that the
channelMatrix
is obtained based on the sameBandwidthPart
object as the one used by thisPDSCH
.- Parameters:
channelMatrix (Numpy array) – An
L x K x Nr x Nt
complex numpy array representing the channel matrix. It can be the actual channel matrix obtained directly from a channel model using thegetChannelMatrix()
method (perfect estimation), or an estimated channel matrix obtained using theestimateChannelLS()
method.- Returns:
Depending on the
prgSize
property of thisPDSCH
, the returned value can be:- Numpy Array:
If
prgSize
is set to zero, a single WidebandNt x Nl
, matrix is returned whereNt
is the number of transmitter antenna andNl
is the number of layers in thisPDSCH
. 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, theNt x Nl
precoding matrixgroupF
is applied to all subcarriers of the resource blocks listed ingroupRBs
.
- Return type:
Numpy array or list of tuples
- getTxBlockSize(codeRates, xOverhead=0, 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 code-word. 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 code-words. If it is a list, numpy array, or tuple, it should contain one or two code-rate values for each code-word. This is the value \(R\) in 3GPP TS 38.214, Section 5.1.3.2.xOverhead (int (default: 0)) – The number of additional overhead resource elements (REs) that should be considered when calculating the transport block size. This is the value \(N^{PRB}_{oh}\) in 3GPP TS 38.214, Section 5.1.3.2.
scaleFactor (float (default: 1.0)) – 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.
- Returns:
A list of one or two integers depending on number of code-words (
numCW
), indicating the transport block size for each code-word.- Return type:
list