Resource Grid
The module grid.py implements the Grid class, which encapsulates the functionality of a Resource Grid,
including:
Keeping the Resource Element (RE) values for a specified resource grid size.
Providing easy access to a specific type of data in the resource grid (e.g., DMRS values, CSI-RS values, PDSCH data, etc.)
Providing statistics and visualizing the resource grid map.
Applying the precoding process which results in a new precoded
Gridobject.Applying OFDM modulation to the resource grid which results in a
Waveformobject.Applying a Channel Model to the resource grid in frequency domain.
Applying Additive White Gaussian Noise (AWGN) to the resource grid in frequency domain.
Performing Synchronization based on the correlation between the configured reference signals and a received
Waveform.Performing Channel Estimation based on a received resource grid and the configured reference signals.
Performing Equalization using the estimated channel.
- class neoradium.grid.Grid(bwp, numPlanes=1, contents='DATA', useReDesc=False, numSlots=1)
 This class implements the functionality of a resource grid. It stores the complex frequency-domain values of resource elements (REs) in the grid.
- Parameters:
 bwp (
BandwidthPart) – The bandwidth part object based on which this resource grid is created.numPlanes (int (default: 1)) – A resource grid can be considered as a 3-dimensional
P x L x Kcomplex tensor whereLis the number of OFDM symbols,Kis the number of subcarriers (based onbwp), andPis the number of planes. In different contexts,Pcan be equivalent to the number of layers, number of transmitter antennas, or number of receiver antennas. To avoid any confusion, the resource grid implementation in NeoRadium uses the term “Plane” for the first dimension of the resource grid.contents (str) –
The default content type of this resource grid. Each resource element (RE) in the resource grid has an associated content type. When some data is assigned to some REs in this resource grid without a specified content type, the default value is used. The following content types are currently defined:
- DATA:
 A Generic content type used when the type of data in the resource grid is unknown or not specified.
- PDSCH:
 The content type used for the data carried in a Physical Downlink Shared Channel (PDSCH)
- PDCCH:
 The content type used for the data carried in a Physical Downlink Control Channel (PDCCH)
- PUSCH:
 The content type used for the data carried in a Physical Uplink Shared Channel (PUSCH)
- PUCCH:
 The content type used for the data carried in a Physical Uplink Control Channel (PUCCH)
- RX_DATA:
 The content type used for the received resource grid. (Created by the OFDM demodulation process)
useReDesc (Boolean) – 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.
numSlots (int) – The number of time slots to include in the resource grid. The number of time symbols
L(the second dimension of the resource grid tensor) is equal tonumSlots * bwp.symbolsPerSlot.
Other Read-Only Properties:
Here is a list of additional properties:
- shape:
 Returns the shape of the 3-dimensional resource grid tensor.
- numPlanes:
 The number of antenna ports. (The same as
numPlanes)- numPorts:
 The number of antenna ports. (The same as
numPlanes)- numLayers:
 The number of layers. (The same as
numPlanes)- numSubcarriers:
 The number of subcarriers in this resource grid.
- numRBs:
 The number of resource blocks (RBs) in this resource grid. This is equal to
numSubcarriers/12. The number of subcarriers in a resource grid is always a multiple of 12.- numSymbols:
 The number of time symbols in this resource grid. This is equal to
numSlots*bwp.symbolsPerSlot.- size:
 The size of resource grid tensor.
- noiseVar:
 The variance of the AWGN noise present in this resource grid. This is usually initialized to zero. When an AWGN noise is applied to the grid using the
addNoise()function, the variance of the noise stored in this property. Also, if a noisyWaveformis OFDM-demodulated using theofdmDemodulate()method, then the amount of noise is transferred to the newGridobject created.
Additionally, you can access (Read-Only) the following
BandwidthPartclass properties directly:startRb,numRbs,nFFT,symbolsPerSlot,slotsPerSubFrame,slotsPerFrame,symbolsPerSubFrame.Resource Grid Indexing:
a) Reading: You can directly access the contents of the resource grid using indices. Here are a few examples of accessing the RE values in the resource grid:
myREs = myGrid[0,2:5,:] # instead of using myGrid.grid[0,2:5,:] print(myREs.shape) # Assuming 612 subcarriers, this will print: "(3, 612)" indexes = myGrid.getReIndexes("DMRS") # Get the indices of all DMRS values dmrsValues = myGrid[indexes] # Get all DMRS values as a 1-D array.
Writing: You can assign different values to different REs in the resource grid. Here are a few examples:
# Set the RE at layer 1, symbol 2, and subcarrier 3 to the value # 0.707 - 0.707j and RE type "DMRS". myGrid[1,2,3] = (0.707 - 0.707j, "DMRS") # Mark all REs in the time symbol 5 as "RESERVED" for layer 1. The # RE values are set to 0 in this case. myGrid[1,5,:] = "RESERVED" # Update the 3 RE values at layer 0, subcarrier 5, and symbols [1, 4, 7] # and set their RE content type to the grid's default content type. myGrid[0,1:10:3,5] = [-0.948 - 0.948j, -0.316+0.316j, 0.316-0.948j]
- print(indent=0, title=None, getStr=False)
 Prints the properties of this resource grid object.
- Parameters:
 indent (int) – The number of indentation characters.
title (str) – If specified, it is used as a title for the printed information.
getStr (Boolean) – If True, returns a text string instead of printing it.
- Returns:
 If the
getStrparameter is True, then this function returns the information in a text string. Otherwise, nothing is returned.- Return type:
 None or str
- clone()
 Creates a copy of this resource grid object.
- Returns:
 A copy of this resource grid object.
- Return type:
 
- getStats()
 Returns some statistics about the allocation of resources in the resource grid.
- Returns:
 A dictionary of items containing the number of resource elements allocated for different types of data in this resource grid.
- Return type:
 dict
- reTypeAt(p, l, k)
 Returns the content type (as a string) of the resource element at the position specified by
p,l, andk.- Parameters:
 p (int) – The plane number. It can be the layer or antenna index depending on the context.
l (int) – The time symbol index.
k (int) – The subcarrier index.
- Returns:
 The content type of the resource element specified by
p,l, andk.- Return type:
 str
- getReIndexes(reTypeStr=None)
 Returns the indices of all resource elements in the resource grid with the content type specified by the
reTypeStr. For example, the code below gets the indices of all DMRS values in the resource grid and uses the returned indices to retrieve these values.dmrsIdx = myGrid.getReIndexes("DMRS") # Get the indices of all DMRS values dmrsValues = myGrid[dmrsIdx] # Get all DMRS values as a 1-D array.
- Parameters:
 reTypeStr (str or None) –
If
reTypeStris None, the default content type of this resource grid is used as the key. For example if this resource grid was created withcontents="PDSCH", then the indices of all resource elements with content type “PDSCH” are returned.Otherwise, this function returns the indices of all resource elements in the resource grid with the content type specified by
reTypeStr. 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
reservedRbSetsorreservedReMapparameters 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
otherCdmGroupsparameter ofDMRSclass)- ”DMRS”:
 The resource elements used for
DMRS.- ”PTRS”:
 The resource elements used for
PTRS.- ”CSIRS_NZP”:
 The resource elements used for Non-Zero-Power (NZP) CSI-RS (See
csirs).- ”CSIRS_ZP”:
 The resource elements used for Zero-Power (ZP) 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 the resource grid. This value can be used directly to access REs at the specified locations. (See the above example)
- Return type:
 3-tuple
- getReValues(reTypeStr=None)
 Returns the values of all resource elements in the resource grid with the content type specified by the
reTypeStr. This is a shortcut method that allows accessing all the values in one step. For example, the following two methods are equivalent.dmrsValues1 = myGrid[ myGrid.getReIndexes("DMRS") ] # Get indices, then access values dmrsValues2 = myGrid.getReValues("DMRS") # Using this method assert np.all(dmrsValues1==dmrsValues2) # The results are the same
- Parameters:
 reTypeStr (str or None) –
If
reTypeStris None, the default content type of this resource grid is used as the key. For example, if this resource grid was created withcontents="PDSCH", then the values of all resource elements with content type “PDSCH” are returned.Otherwise, this function returns the values of all resource elements in the resource grid with the content type specified by
reTypeStr. SeegetReIndexes()for a list of values that could be used forreTypeStr.- Returns:
 A 1-D complex NumPy array containing the values for all REs with the content type specified by
reTypeStr.- Return type:
 1-D NumPy array
- precode(f)
 Applies the specified precoding matrix to this grid object and returns a new precoded 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.
- Parameters:
 f (NumPy array or list of tuples) –
This function supports two types of precoding:
- Wideband:
 fis anNt x Nlmatrix whereNtis the number of transmitter antennas andNlis the number of layers which must match the number of layers in the resource grid. In this case the same precoding is applied to all subcarriers of the resource grid.- Using PRGs:
 fis a list of tuples of the form (groupRBs,groupF). For each entry in the list, theNt x Nlprecoding matrixgroupFis applied to all subcarriers of the resource blocks listed ingroupRBs.
- Returns:
 A new Grid object of shape
Nt x L x KwhereNtis the number of transmitter antennas,Lis the number of OFDM symbols, andKis the number of subcarriers.- Return type:
 
- ofdmModulate(f0=0, windowing='STD')
 Applies OFDM Modulation to the resource grid which results in a
Waveformobject. This function is based on 3GPP TS 38.211, Section 5.3.1.- Parameters:
 f0 (float) – The carrier frequency of the generated waveform. If it is 0 (default), then a baseband waveform is generated and the up-conversion process explained in 3GPP TS 38.211, Section 5.4 is not applied.
windowing (str) – A text string indicating what type of windowing should be applied to the waveform after OFDM modulation. The default value
"STD"means the windowing should be applied based on 3GPP TS 38.104, Sections B.5.2 and C.5.2. For more information seeapplyWindowing()method of theWaveformclass.
- Returns:
 A
Waveformobject containing the OFDM-modulated waveform information.- Return type:
 
- estimateTimingOffset(rxWaveform)
 Estimates the timing offset of a received waveform. This method first applies OFDM modulation to the resource grid and then calculates the correlation of this waveform with the given
rxWaveform. The timing offset is the index of where the correlation is at its maximum. The output of this function can be used by thesync()method of theWaveformclass to synchronize a received waveform.
- equalize(hf, noiseVar=None)
 Equalizes a received resource grid using the estimated channel
hf. The estimated channel is assumed to include the effect of precoding matrix, therefore, its shape isL x K x Nr x NlwhereLis the number of OFDM symbols,Kis the number of subcarriers,Nris the number of receiver antennas, andNlis the number of layers. The output of the equalization process is a newGridobject of shapeNl x L x K.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:
 hf (4-D complex NumPy array) – This is an
L x K x Nr x NlNumPy array representing the estimated channel matrix, whereLis the number of OFDM symbols,Kis the number of subcarriers,Nris the number of receiver antennas, andNlis the number of layers.noiseVar (float or None) – The variance of the 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 thenoiseVarproperty ofGridclass).
- Returns:
 eqGrid (
Grid) – The equalized grid object of shapeNl x L x KwhereNlis the number of layers,Lis the number of OFDM symbols, andKis the number of subcarriers.llrScales (3-D NumPy array) – The Log-Likelihood Ratios (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 Kwhich is similar toeqGridabove.
- estimateChannelLS(rsInfo, meanCdm=True, polarInt=False, kernel='linear')
 Performs channel estimation based on this received grid and the reference signal information in the
rsInfo. Here is a list of steps taken by this function to calculated the estimated channel and noise variance:1) First the channel information is calculated at each pilot location using least squared method based on the following equations:
\[Y_p = h_p \odot P + n_p\]where \(Y_p\) is a vector of received values at the pilot locations which are the values in this
Gridobject at the pilot locations indicated inrsInfo, \(h_p\) is the vector of channel values at pilot locations, \(P\) is the vector of pilot values extracted fromrsInfo, and \(n_p\) is the noise at pilot locations. The least square estimate of the channel values at pilot locations \(h_p\) is then calculated by:\[h_p = \frac {Y_p} P \qquad \qquad \qquad \text{(element-wise division)}\]2) If
meanCdmis True, the \(h_p\) values in each CDM group are averaged which results in a new smaller set of \(h_p\) values located at centers of CDM groups.3) Frequency interpolation along subcarriers is applied to \(h_p\) values at all OFDM symbols containing pilots based on
polarIntandkernelvalues.4) A raise-cosine low-pass filter is applied to the Channel Impulse Response (CIR) values to get a de-noised version of CIRs. The noise variance is estimated using the difference between the noisy and de-noised versions of the CIRs.
5) Finally another interpolation is applied along OFDM symbols to estimate the channel information for the whole channel matrix.
- Parameters:
 rsInfo (
CsiRsConfigorDMRS) –This object contain reference signal information for the channel estimation. If it is a
CsiRsConfigobject, the channel matrix is estimated based on the CSI-RS signals which does not include the precoding effect.If this is a
DMRSobject, the channel matrix is estimated based on the demodulation reference signals which includes the precoding effect.meanCdm (Boolean) – If True, the \(h_p\) values at pilot locations for each CDM group are averaged before applying subcarrier interpolation. Otherwise interpolation is applied directly on the \(h_p\) values.
polarInt (Boolean) –
If True, the interpolation along the subcarriers is applied in polar coordinates. This means all \(h_p\) values are converted to the polar coordinates and then the type of interpolation specified by
kernelis applied to magnitudes and angles of these values. The results are then converted back to the cartesian coordinates. Otherwise (default), the interpolation is applied in the cartesian coordinates.Doing polar interpolation provides slightly better results at the cost of longer execution time.
kernel (str) –
The type of interpolation used for channel estimation process. The same type of 1-D interpolations are applied along subcarriers and then OFDM symbols. Here is a list of supported values:
- linear:
 A linear interpolation is applied to the values using extrapolation at both ends of the arrays. This uses the function interp1d with
kindset tolinear.- nearest:
 A nearest neighbor interpolation is applied to the values using extrapolation at both ends of the arrays. This uses the function interp1d with
kindset tonearest.- quadratic:
 A quadratic interpolation is applied to the values using extrapolation at both ends of the arrays. This uses the function interp1d with
kindset toquadratic.- thin_plate_spline:
 An RBF interpolation is applied with a
thin_plate_splinekernel. This uses the RBFInterpolator class.- multiquadric:
 An RBF interpolation is applied with a
multiquadrickernel. This uses the RBFInterpolator class.
- Returns:
 hEst (a 4-D complex NumPy array) – If
rsInfois aCsiRsConfigobject, anL x K x Nr x Ntcomplex NumPy array is returned whereLis the number of OFDM symbols,Kis the number of subcarriers,Nris the number of receiver antennas, andNtis the number of transmitter antennas.If
rsInfois aDMRSobject, anL x K x Nr x Nlcomplex NumPy array is returned whereLis the number of OFDM symbols,Kis the number of subcarriers,Nris the number of receiver antennas, andNlis the number of layers.estNoiseVar (float) – The estimated noise variance.
- applyChannel(channelMatrix)
 Applies a channel to this grid in frequency domain which results in a new received
Gridobject. This function performs a matrix multiplication where this grid of shapeNt x L x Kis multiplied by the channel matrix of shapeL x K x Nr x Ntand results in the received grid of shapeNr x L x K, whereLis the number of OFDM symbols,Kis the number of subcarriers,Nris the number of receiver antennas, andNtis the number of transmitter antennas.This method can be used as a shortcut method to get the received resource grid faster compared to time domain process of doing OFDM modulation, applying the channel, performing synchronization, and doing OFDM demodulation.
Please note that the results are slightly different when a channel is applied in time domain vs frequency domain.
- Parameters:
 channelMatrix (4-D complex NumPy array) – This is an
L x K x Nr x NtNumPy array representing the estimated channel matrix, whereLis the number of OFDM symbols,Kis the number of subcarriers,Nris the number of receiver antennas, andNtis the number of transmitter antennas.- Returns:
 The received grid of shape
Nr x L x K, whereNris the number of receiver antennas,Lis the number of OFDM symbols, andKis the number of subcarriers.- Return type:
 
- getNoiseStd(snr)
 This function calculates the noise standard deviation for the given signal-to-noise ratio (SNR). It first calculates the average received signal power per resource element (RE) and then uses it, along with the given SNR, to calculate the noise power. The returned standard deviation can be used directly by the
addNoise()method using thenoiseStdargument.- Parameters:
 snr (float) – The signal-to-noise ratio in linear scale (not in dB).
- Returns:
 The noise standard deviation.
- Return type:
 float
- addNoise(**kwargs)
 Adds Additive White Gaussian Noise (AWGN) to this resource grid based on the specified noise properties. The noisy grid is returned as a new
Gridobject. ThenoiseVarproperty of the returned grid contains the variance of the noise applied by this function.If you already have a noise signal in a NumPy array, you can use the
noiseparameter of this function to apply it directly to this resource grid:ExamplemyNoise = random.awgn(rxGrid.shape, 0.1) # Create AWGN with σ = 0.1 rxGrid.addNoise(noise=myNoise)
If you know the variance or standard deviation of the noise, you can use them directly by setting the arguments
noiseStdandnoiseVarrespectively:ExamplerxGrid.addNoise(noiseStd=0.1) # Same result as above rxGrid.addNoise(noiseVar=0.01) # Same result as above
If you have a signal-to-noise ratio (SNR), there are two approaches for adding noise to the received resource grid:
Matlab Approach:
In this case, it is assumed that the received signal power is normalized to \(\frac{1}{N_r}\), where \(N_r\) is the number of receiver antennas. Please note that when channel models such as CDL, TDL, or trajectory-based models are used in the communication pipeline, this assumption is not always valid. Support for this approach is included only to allow comparison with Matlab.
\[\sigma^2_{AWGN} = \frac{1}{N_r \cdot 10^{\frac{SNR_{dB}}{10}}}\]ExamplerxWaveform.addNoise(snrDb=mySnrDb, useRxPower=False)
Using RX Power:
In this case, the function first calculates the average received signal power per resource element (RE) and uses it, along with the given signal-to-noise ratio, to calculate the noise power.
\[\sigma^2_{AWGN} = \frac{\sigma^2_{RX}}{10^{\frac{SNR_{dB}}{10}}}\]ExamplerxWaveform.addNoise(snrDb=mySnrDb, useRxPower=True)
Please refer to the notebook SNR Calculations in NeoRadium for a complete analysis of how NeoRadium calculates and applies noise power for a given signal-to-noise ratio.
- Parameters:
 kwargs (dict) –
The amount of noise must be specified by one of the parameters
noise,noiseStd,noiseVar, orsnrDb.- noise:
 NumPy array with the same shape as this
Gridobject containing the noise values. Ifnoiseis provided, it is added directly to the grid and all other parameters are ignored.- noiseStd:
 The standard deviation of the noise. AWGN complex noise is generated with zero mean and the specified standard deviation. If
noiseStdis specified,noiseVarandsnrDbare ignored.- noiseVar:
 The variance of the noise. AWGN complex noise is generated with zero mean and the specified variance. If
noiseVaris specified, thesnrDbvalue is ignored.- snrDb:
 The signal-to-noise ratio in decibels (dB). The noise standard deviation is calculated using the given SNR value and the
useRxPowerparameter. Then, AWGN complex noise is generated with zero mean and the calculated standard deviation.- useRxPower:
 If True, this function first calculates the average received signal power per resource element (RE) and uses it, along with the given signal-to-noise ratio, to compute the noise power. Otherwise, it is assumed that the received signal power is normalized to \(\frac{1}{N_r}\) (Matlab approach), where \(N_r\) is the number of receiver antennas.
Note
Currently, the default value of
useRxPoweris False (Matlab approach) for backward compatibility. However, in future releases, this may change to True. To ensure forward-compatible code, explicitly set this parameter instead of relying on the default.- ranGen:
 If provided, this random generator is used for AWGN generation. Otherwise, NeoRadium’s global random generator is used.
- Returns:
 A new grid object containing the noisy version of this grid. The
noiseVarproperty of the returned grid contains the variance of the noise applied by this function.- Return type:
 
- drawMap(ports=[0], reRange=(0, 12), title=None)
 Draws a color-coded map of this grid object. Each
planeof the grid is drawn separately with subcarriers in horizontal direction and OFDM symbols in vertical direction.- Parameters:
 ports (list) – Specifies the list of ports (or
planes) to draw. Each port is drawn separately. By default this function draws only the first plane of the resource grid.reRange (tuple) – Specifies the range of subcarriers (REs) to draw. By default this function only draws the first resource block of the grid (subcarriers 0 to 12). The tuple
(a, b)means the first RE drawn is the one ataand last one is atb-1.title (str or None) – If specified, it is used as the title for the drawn grid. Otherwise, this function automatically creates a title based on the given parameters.