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 Grid object.
Applying OFDM modulation to the resource grid which results in a
Waveform
object.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 keeps the complex frequency-domain values of resource elements (REs) in a resource 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 K
complex tensor whereL
is the number of OFDM symbols,K
is the number of subcarriers (based onbwp
), andP
is the number of planes. In different contexts,P
can be equivalent to the number of layers, number of transmitter antenna, or number of receiver antenna. To avoid any confusion, the resource grid implementation in NeoRadium uses the term “Plane” for the first dimension of the resource gird.contents (str (default: "DATA")) –
The default content type of this resource grid. Each resource element (RE) in the resource grid has a an associated content type. When some data is assigned to some REs in this resource grid without a specified content type, this 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 (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.numSlots (int (default: 1)) – 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 noisyWaveform
is OFDM-demodulated using theofdmDemodulate()
method, then the amount of noise is transferred to the newGrid
object created.
Additionally you can access (Read-Only) these
BandwidthPart
class 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 indexes. 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 indexes of all DMRS values dmrsValues = myGrid[indexes] # Get all DMRS values as a 1-D array.
b) Writing: You can assign different values to different REs in the resource grid. Here are a few example:
# 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 mark 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 (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
- 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 indexes of all resource elements in the resource grid with the content type specified by the
reTypeStr
. For example, the following code first gets the indexes of all DMRS values in the resource grid and uses the returned indexes to retrieve the DMRS values.dmrsIdx = myGrid.getReIndexes("DMRS") # Get the indexes of all DMRS values dmrsValues = myGrid[indexes] # Get all DMRS values as a 1-D array.
- Parameters:
reTypeStr (str or None (default: None)) –
If
reTypeStr
isNone
, 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 indexes of all resource elements with content type “PDSCH” are returned.Otherwise, this function returns the indexes 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
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 differnt 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 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 short cut method that allows accessing all the values in one step. For example, the following two methods are equivalent.dmrsValues1 = myGrid[ myGrid.getReIndexes("DMRS") ] # Get indexes, 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 (default: None)) –
If
reTypeStr
isNone
, 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 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.
- Parameters:
f (numpy array or list of tuples) –
This function supports two types of precoding:
- Wideband:
f
is anNt x Nl
, matrix whereNt
is the number of transmitter antenna andNl
is 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:
f
is a list of tuples of the form (groupRBs
,groupF
). For each entry in the list, theNt x Nl
precoding matrixgroupF
is applied to all subcarriers of the resource blocks listed ingroupRBs
.
- Returns:
A new Grid object of shape
Nt x L x K
whereNt
is the number of transmitter antenna,L
is the number of OFDM symbols, andK
is the number of subcarriers.- Return type:
- ofdmModulate(f0=0, windowing='STD')
Applies OFDM Modulation to the resource grid which results in a
Waveform
object. This function is based on 3GPP TS 38.211, Section 5.3.1.- Parameters:
f0 (float (default: 0)) – 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 (default: "STD")) – 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 theWaveform
class.
- Returns:
A
Waveform
object 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 theWaveform
class to synchronize a received waveform.
- equalize(hf, noiseVar=None)
Equalizes a received resource grid using the estimated channel
hf
. The the estimated channel is assumed to include the effect of precoding matrix, therefore, its shape isL x K x Nr x Nl
whereL
is the number of OFDM symbols,K
is the number of subcarriers,Nr
is the number of receiver antenna, andNl
is the number of layers. The output of the equalization process is a newGrid
object of shapeNl x L x K
.This function also outputs Log-Likelihood Ratios (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 Nl
numpy array representing the estimated channel matrix, whereL
is the number of OFDM symbols,K
is the number of subcarriers,Nr
is the number of receiver antenna, andNl
is the number of layers.noiseVar (float or None (default: 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 thenoiseVar
property ofGrid
class).
- Returns:
eqGrid (
Grid
) – The equalized grid object of shapeNl x L x K
whereNl
is the number of layers,L
is the number of OFDM symbols, andK
is the number of subcarriers.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 is
Nl x L x K
which is similar toeqGrid
above.
- 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
Grid
object 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
meanCdm
isTrue
, 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
polarInt
andkernel
values.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 (
CsiRsConfig
orDMRS
) –This object contain reference signal information for the channel estimation. If it is a
CsiRsConfig
object, the channel matrix is estimated based on the CSI-RS signals which does not include the precoding effect.If this is a
DMRS
object, the channel matrix is estimated based on the demodulation reference signals which includes the precoding effect.meanCdm (Boolean (default: True)) – 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 (default: False)) –
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 bykernel
is 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 (default: 'linear')) –
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
kind
set 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
kind
set tonearest
.- quadratic:
A quadratic interpolation is applied to the values using extrapolation at both ends of the arrays. This uses the function interp1d with
kind
set toquadratic
.- thin_plate_spline:
An RBF interpolation is applied with a
thin_plate_spline
kernel. This uses the RBFInterpolator class.- multiquadric:
An RBF interpolation is applied with a
multiquadric
kernel. This uses the RBFInterpolator class.
- Returns:
hEst (a 4-D complex numpy array) – If
rsInfo
is aCsiRsConfig
object, anL x K x Nr x Nt
complex numpy array is returned whereL
is the number of OFDM symbols,K
is the number of subcarriers,Nr
is the number of receiver antenna, andNt
is the number of transmitter antenna.If
rsInfo
is aDMRS
object, anL x K x Nr x Nl
complex numpy array is returned whereL
is the number of OFDM symbols,K
is the number of subcarriers,Nr
is the number of receiver antenna, andNl
is 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
Grid
object. This function performs a matrix multiplication where this grid of shapeNt x L x K
is multiplied by the channel matrix of shapeL x K x Nr x Nt
and results in the received grid of shapeNr x L x K
, whereL
is the number of OFDM symbols,K
is the number of subcarriers,Nr
is the number of receiver antenna, andNt
is the number of transmitter antenna.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 Nt
numpy array representing the estimated channel matrix, whereL
is the number of OFDM symbols,K
is the number of subcarriers,Nr
is the number of receiver antenna, andNt
is the number of transmitter antenna.- Returns:
The received grid of shape
Nr x L x K
, whereNr
is the number of receiver antenna,L
is the number of OFDM symbols, andK
is the number of subcarriers.- Return type:
- addNoise(**kwargs)
Adds Additive White Gaussian Noise (AWGN) to this resource grid based on the given noise properties. The noisy grid is then returned in a new
Grid
object. ThenoiseVar
property of the returned grid contains the variance of the noise applied by this function.- Parameters:
kwargs (dict) –
One of the following parameters must be specified. They specify how the noise signal is generated.
- noise:
A numpy array with the same shape as this
Grid
object containing the noise information. If the noise information is provided bynoise
it is added directly to the grid. In this case all other parameters are ignored.- noiseStd:
The standard deviation of the noise. An AWGN complex noise signal is generated with zero-mean and the specified standard deviation. If
noiseStd
is specified,noiseVar
andsnrDb
values below are ignored.- noiseVar:
The variance of the noise. An AWGN complex noise signal is generated with zero-mean and the specified variance. If
noiseVar
is specified, the value ofsnrDb
is ignored.- snrDb:
The signal to noise ratio in dB. First the noise variance is calculated using the given SNR value and then an AWGN complex noise signal is generated with zero-mean and the calculated variance. This function uses the following formula to calculate the noise variance \(\sigma^2_{AWGN}\) from \(snrDb\):
\[\sigma^2_{AWGN} = \frac 1 {N_r.10^{\frac {snrDb} {10}}}\]where \(N_r\) is the number of receiver antenna.
- ranGen:
If provided, it is used as the random generator for the AWGN generation. Otherwise, if this is not specified, NeoRadium’s global random generator is used.
- Returns:
A new grid object containing the noisy version of this grid. The
noiseVar
property 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
plain
of the grid is drawn separately with subcarriers in horizontal direction and OFDM symbols in vertical direction.- Parameters:
ports (list (default: [0])) – Specifies the list of ports (or
plains
) to draw. Each port is drawn separately. By default this function draws only the first plain of the resource grid.reRange (tuple (default: (0,12))) – 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 of
(a, b)
means the first RE drawn is the one ata
and last one is atb-1
.title (str or None (default: 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.