|
System Simulation - PDSCH
For this note, is a comprehensive Python script for simulating 5G NR PDSCH (Physical Downlink Shared Channel) transmission through various radio channel models. The tool implements both frequency-domain and time-domain channel processing, supports multiple channel models (AWGN, TDL, CDL), and provides channel estimation, equalization, and BER analysis capabilities...

Followings are brief descriptions on each procedure with the focus on sionna api
- Inputs: Channel bandwidth, subcarrier spacing, RB allocation, channel type (AWGN/TDL/CDL), SNR, channel estimation/equalization methods, DMRS configuration
- Resource Grid Generation: Creates OFDM resource grid with DMRS and CORESET support
- Sionna API: ResourceGrid, ResourceGridMapper, PilotPattern
- Data Symbol Generation: Generates random bits and maps to constellation symbols (16QAM)
- Sionna API: BinarySource, Mapper
- Channel Processing (selectable via use_time_domain_channel option):
- Frequency Domain (default): TX Resource Grid → Channel (Freq Domain) → AWGN → RX Resource Grid (direct frequency-domain multiplication, no OFDM mod/demod)
- Custom implementation (NumPy-based channel coefficient generation)
- Optional: Sionna TDL/CDL models if available (falls back to custom)
- Time Domain: TX Resource Grid → IFFT → Channel (Time Domain) → AWGN → FFT → RX Resource Grid (full OFDM modulation/demodulation with time-domain channel convolution)
- Custom implementation (NumPy IFFT/FFT, custom channel IR generation)
- Optional: Sionna TDL/CDL models if available (falls back to custom)
- Channel Estimation: LS (Least Squares) or MMSE (Minimum Mean Square Error) estimation on DMRS REs extracted from TX and RX resource grids
- Custom implementation (NumPy-based LS/MMSE algorithms)
- Channel Coefficient Interpolation: Interpolates channel estimates from DMRS REs across frequency and time to cover all resource elements
- Sionna API: LinearInterpolator (if available, otherwise custom cubic spline interpolation)
- Equalization: ZF (Zero Forcing) or MMSE equalization using interpolated channel coefficients
- Custom implementation (NumPy-based ZF/MMSE algorithms)
- BER Analysis: Bit Error Rate calculation with constellation visualization comparing TX and recovered symbols
- Custom implementation (constellation matching and bit error counting)
- Visualization: Interactive Tkinter GUI with multiple tabs showing resource grids, channel coefficients, constellations, and spectrum
- Custom implementation (Tkinter/Matplotlib-based GUI)
Toolkits for the script
I didn't write the code myself. What I did was just prompting for AI tool and did basic check up for the output. Followings are the tool kits that I used for this script
- Scripting IDE : Cursor
- Version: 2.1.50 (system setup)
- VSCode Version: 1.105.1
- Commit: 56f0a83df8e9eb48585fcc4858a9440db4cc7770
- Date: 2025-12-06T23:39:52.834Z
- Electron: 37.7.0
- Chromium: 138.0.7204.251
- Node.js: 22.20.0
- V8: 13.8.258.32-electron.0
- OS: Windows_NT x64 10.0.26100
- AI Model : Auto (as of Dec 11, 2025)
- Python Info : Check out this note for the detailed installation proces that I went through
|
==== Python Version ====
3.12.3 (main, Nov 6 2025, 13:44:16) [GCC 13.3.0]
==== Python Executable ====
/home/jaeku/nvidia/venv-sionna/bin/python3
==== Platform Info ====
Linux-6.6.87.2-microsoft-standard-WSL2-x86_64-with-glibc2.39
('main', 'Nov 6 2025 13:44:16')
==== Site-Packages Directories ====
['/home/jaeku/nvidia/venv-sionna/lib/python3.12/site-packages', '/home/jaeku/nvidia/venv-sionna/local/lib/python3.12/dist-packages', '/home/jaeku/nvidia/venv-sionna/lib/python3/dist-packages', '/home/jaeku/nvidia/venv-sionna/lib/python3.12/dist-packages']
==== sys.path ====
/home/jaeku/nvidia
/usr/lib/python312.zip
/usr/lib/python3.12
/usr/lib/python3.12/lib-dynload
/home/jaeku/nvidia/venv-sionna/lib/python3.12/site-packages
==== Installed Packages ====
absl-py == 2.3.1
asttokens == 3.0.1
astunparse == 1.6.3
certifi == 2025.11.12
charset-normalizer == 3.4.4
comm == 0.2.3
contourpy == 1.3.3
cycler == 0.12.1
decorator == 5.2.1
drjit == 1.2.0
executing == 2.2.1
flatbuffers == 25.9.23
fonttools == 4.61.0
gast == 0.7.0
google-pasta == 0.2.0
grpcio == 1.76.0
h5py == 3.15.1
idna == 3.11
importlib_resources == 6.5.2
ipydatawidgets == 4.3.5
ipython == 9.8.0
ipython_pygments_lexers == 1.1.1
ipywidgets == 8.1.8
jedi == 0.19.2
jupyterlab_widgets == 3.0.16
keras == 3.12.0
kiwisolver == 1.4.9
libclang == 18.1.1
Markdown == 3.10
markdown-it-py == 4.0.0
MarkupSafe == 3.0.3
matplotlib == 3.10.7
matplotlib-inline == 0.2.1
mdurl == 0.1.2
mitsuba == 3.7.1
ml_dtypes == 0.5.4
namex == 0.1.0
numpy == 1.26.4
opt_einsum == 3.4.0
optree == 0.18.0
packaging == 25.0
parso == 0.8.5
pexpect == 4.9.0
pillow == 12.0.0
pip == 25.3
prompt_toolkit == 3.0.52
protobuf == 6.33.2
ptyprocess == 0.7.0
pure_eval == 0.2.3
Pygments == 2.19.2
pyparsing == 3.2.5
python-dateutil == 2.9.0.post0
pythreejs == 2.4.2
requests == 2.32.5
rich == 14.2.0
scipy == 1.16.3
setuptools == 80.9.0
sionna == 1.2.1
sionna-rt == 1.2.1
six == 1.17.0
stack-data == 0.6.3
tensorboard == 2.20.0
tensorboard-data-server == 0.7.2
tensorflow == 2.20.0
termcolor == 3.2.0
traitlets == 5.14.3
traittypes == 0.2.3
typing_extensions == 4.15.0
urllib3 == 2.6.0
wcwidth == 0.2.14
Werkzeug == 3.1.4
wheel == 0.45.1
widgetsnbextension == 4.0.15
wrapt == 2.0.1
|
Source Code and Output
You can get the source code for this note here. I would not describe / explain on every single line of the code since AI (e.g, chatGPT, Gemini etc) will explain better than I do.
Disclaimer !!!
I have checked and fixed some obvious issues of the script while I am prompting the script, but I haven't verified all the details of the implementation. So there likely to be bugs / errors that I missed. So take this purely as an educational purpose for getting some high level idea on how 3GPP NR Phy specification can be implemented
The script output some part of the result in text and some part in graphics.
Followings are the output of the code in text
|
2025-12-11 21:54:56.943016: I external/local_xla/xla/tsl/cuda/cudart_stub.cc:31] Could not find cuda drivers on your machine, GPU will not be used.
2025-12-11 21:54:56.966005: I tensorflow/core/util/port.cc:153] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable `TF_ENABLE_ONEDNN_OPTS=0`.
2025-12-11 21:54:57.535187: I tensorflow/core/platform/cpu_feature_guard.cc:210] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.
To enable the following instructions: AVX2 AVX_VNNI FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.
2025-12-11 21:54:59.703566: I tensorflow/core/util/port.cc:153] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable `TF_ENABLE_ONEDNN_OPTS=0`.
2025-12-11 21:54:59.706508: I external/local_xla/xla/tsl/cuda/cudart_stub.cc:31] Could not find cuda drivers on your machine, GPU will not be used.
[WARNING] Sionna LinearInterpolator not available, using custom interpolation
============================================================
NR Resource Grid Configuration
============================================================
Channel Bandwidth: 20.0 MHz
Subcarrier Spacing: 30 kHz
Max RBs (for BW/SCS): 51
START_RB: 0
NUM_RBS: 51
RB Range: RB 0 to RB 50
Allocation: 51/51 RBs (100.0%)
Allocated Subcarriers: 612
OFDM Symbols per Slot: 14
FFT Size: 2048
Guard Carriers: 718 (left), 718 (right)
------------------------------------------------------------
DMRS Configuration (3GPP TS 38.211)
------------------------------------------------------------
DMRS Config Type: Type 1 (comb-2)
DMRS Length: 1 symbol(s)
DMRS First Symbol (l0): 2
DMRS Additional Pos: 3
DMRS Symbol Positions: [2, 5, 8, 11]
DMRS CDM Group: 0
DMRS Ports: [0]
DMRS Delta Shift: 0
CDM Groups w/o Data: 1
------------------------------------------------------------
CORESET Configuration (3GPP TS 38.331)
------------------------------------------------------------
CORESET Enabled: Yes
CORESET ID: 1
Freq Domain Bitmap: 111100000000000... (45 bits)
CORESET RBs: 24 RBs (RB 0 to 23, contiguous)
CORESET Duration: 2 symbol(s)
CORESET Start Symbol: 0
CCE-REG Mapping: interleaved
REG Bundle Size: 6
Interleaver Size: 2
Shift Index: 0
CORESET REGs: 48
CORESET CCEs: 8
Overlap with PDSCH: 24 RBs
CORESET Data REs: 432
CORESET DMRS REs: 144
============================================================
Creating NR DMRS Type A pilot pattern (3GPP TS 38.211)...
2025-12-11 21:55:01.728104: E external/local_xla/xla/stream_executor/cuda/cuda_platform.cc:51] failed call to cuInit: INTERNAL: CUDA error: Failed call to cuInit: UNKNOWN ERROR (303)
DMRS mask shape: (1, 1, 14, 612)
DMRS pilots shape: (1, 1, 1224)
Number of DMRS REs: 1224
DMRS Info:
- CDM Groups Used: [0]
- Delta Shift: 0
- Ports: [0]
- DMRS Subcarriers/Symbol: 306
Incorporating CORESET into pilot pattern...
DMRS REs: 1224
CORESET REs: 576
Combined reserved REs: 1800
Creating Resource Grid...
Resource Grid created:
FFT Size: 2048
Num OFDM Symbols: 14
Num Subcarriers: 612
Num Data Symbols: 6768
Pilot Pattern: Custom NR DMRS Type A
Creating Resource Grid Mapper...
Number of data symbols to map: 6768
Data symbols shape: (1, 1, 1, 6768)
Resource grid shape: (1, 1, 1, 14, 2048)
============================================================
PDSCH RE Mapping Verification (3GPP TS 38.214)
============================================================
Reserved REs (PDSCH should NOT be mapped here):
Total REs in allocation: 8568
DMRS REs: 1224
CORESET REs: 576
PTRS REs: 0
Total Reserved REs: 1800
Available for PDSCH Data: 6768
Overhead: 21.01%
PDSCH Mapping Order Verification:
Number of Data REs mapped: 6768
Mapping order correct: YES ✓
CORESET data violations: 0 ✓
First data RE: Symbol 0, SC 288
Last data RE: Symbol 13, SC 611
3GPP Compliance Check (TS 38.214 Section 5.1.4):
[✓] Frequency-first mapping (lowest to highest SC, then next symbol)
[✓] DMRS REs excluded (via pilot pattern)
[✓] CORESET REs excluded (REs not available for PDSCH)
[✓] PTRS REs excluded (PTRS disabled)
[N/A] CSI-RS REs (not configured)
[INFO] VRB-to-PRB mapping: Non-interleaved (direct mapping)
============================================================
============================================================
System-level link simulation: Tx grid -> channel -> Rx -> EQ
============================================================
SNR: 30.0 dB
DMRS REs: 1224, PDSCH data REs: 6768
============================================================
Launching Resource Grid Viewer...
============================================================
============================================================
Running NR PDSCH Simulation
============================================================
Creating NR DMRS Type A pilot pattern (3GPP TS 38.211)...
DMRS mask shape: (1, 1, 14, 612)
Number of DMRS REs: 1224
Incorporating CORESET into pilot pattern...
Creating Resource Grid...
Creating Resource Grid Mapper...
Resource grid shape: (1, 1, 1, 14, 2048)
============================================================
System-level link simulation: Tx grid -> channel -> Rx -> EQ
============================================================
[CHANNEL] Channel Type: AWGN
[CHANNEL] Channel Profile: TDL-A
[CHANNEL] Velocity: 0.0 km/h
[CHANNEL] Carrier Frequency: 1.0 GHz
[CHANNEL] AWGN SNR: 30.0 dB
[CHANNEL] Processing in FREQUENCY DOMAIN (no OFDM mod/demod)
[CHANNEL] Applying channel to transmitted signal (frequency domain)...
[CHANNEL] Tx grid shape: (14, 612), power: 0.9257
[CHANNEL] Using AWGN-only channel (flat fading)
[CHANNEL] Channel coefficients shape: (14, 612)
[CHANNEL] Channel coefficients mean power: 1.0000
[CHANNEL] Channel coefficients min/max magnitude: 1.0000 / 1.0000
[CHANNEL] After channel: power: 0.9257
[CHANNEL] Adding AWGN noise (SNR: 30.0 dB)...
[CHANNEL] Noise power: 0.0010
[CHANNEL] Rx grid (no EQ) power: 0.9273
[CHANNEL] Channel simulation complete (frequency-domain processing)
[FREQ DOMAIN] Summary: Processed 14 symbols, 612 subcarriers
[FREQ DOMAIN] TX power: 0.925677
[FREQ DOMAIN] RX power (no EQ): 0.927255
[FREQ DOMAIN] Power ratio (RX/TX): 1.001704
[INTERPOLATION] Starting interpolation...
[INTERPOLATION] Grid shape: 14 symbols x 612 subcarriers
[INTERPOLATION] DMRS symbols: [2, 5, 8, 11]
[INTERPOLATION] Using Sionna LinearInterpolator: False
[INTERPOLATION] Symbol 2: 306 DMRS REs at subcarriers [ 0 2 4 6 8 10 12 14 16 18]...
[INTERPOLATION] Symbol 2: Channel estimate magnitude range: [0.940939, 1.055325]
[INTERPOLATION] Symbol 2: Channel estimate phase range: [-0.077572, 0.064904]
[INTERPOLATION] Symbol 2: Magnitude range: [0.940939, 1.055325]
[INTERPOLATION] Symbol 2: Phase range (unwrapped): [-0.077572, 0.064904]
[INTERPOLATION] Symbol 2: DMRS spacing: min=2, max=2, mean=2.00
[INTERPOLATION] Symbol 2: Using cubic spline interpolation
[INTERPOLATION] Symbol 2: Interpolated magnitude range: [0.940939, 1.056120]
[INTERPOLATION] Symbol 2: Interpolated phase range: [-0.077572, 0.073435]
[INTERPOLATION] Symbol 2: Phase change rate: min=0.000095, max=0.066557, mean=0.013459 rad/subcarrier
[INTERPOLATION] Symbol 5: 306 DMRS REs at subcarriers [ 0 2 4 6 8 10 12 14 16 18]...
[INTERPOLATION] Symbol 5: Channel estimate magnitude range: [0.937093, 1.087226]
[INTERPOLATION] Symbol 5: Channel estimate phase range: [-0.053265, 0.072214]
[INTERPOLATION] Symbol 5: Magnitude range: [0.937093, 1.087226]
[INTERPOLATION] Symbol 5: Phase range (unwrapped): [-0.053265, 0.072214]
[INTERPOLATION] Symbol 5: DMRS spacing: min=2, max=2, mean=2.00
[INTERPOLATION] Symbol 5: Using cubic spline interpolation
[INTERPOLATION] Symbol 5: Interpolated magnitude range: [0.935624, 1.087226]
[INTERPOLATION] Symbol 5: Interpolated phase range: [-0.053265, 0.072214]
[INTERPOLATION] Symbol 5: Phase change rate: min=0.000046, max=0.056383, mean=0.013382 rad/subcarrier
[INTERPOLATION] Symbol 8: 306 DMRS REs at subcarriers [ 0 2 4 6 8 10 12 14 16 18]...
[INTERPOLATION] Symbol 8: Channel estimate magnitude range: [0.937867, 1.053815]
[INTERPOLATION] Symbol 8: Channel estimate phase range: [-0.060445, 0.053296]
[INTERPOLATION] Symbol 8: Magnitude range: [0.937867, 1.053815]
[INTERPOLATION] Symbol 8: Phase range (unwrapped): [-0.060445, 0.053296]
[INTERPOLATION] Symbol 8: DMRS spacing: min=2, max=2, mean=2.00
[INTERPOLATION] Symbol 8: Using cubic spline interpolation
[INTERPOLATION] Symbol 8: Interpolated magnitude range: [0.935243, 1.053815]
[INTERPOLATION] Symbol 8: Interpolated phase range: [-0.060445, 0.059791]
[INTERPOLATION] Symbol 8: Phase change rate: min=0.000107, max=0.054314, mean=0.012571 rad/subcarrier
[INTERPOLATION] Symbol 11: 306 DMRS REs at subcarriers [ 0 2 4 6 8 10 12 14 16 18]...
[INTERPOLATION] Symbol 11: Channel estimate magnitude range: [0.941337, 1.053204]
[INTERPOLATION] Symbol 11: Channel estimate phase range: [-0.064835, 0.065215]
[INTERPOLATION] Symbol 11: Magnitude range: [0.941337, 1.053204]
[INTERPOLATION] Symbol 11: Phase range (unwrapped): [-0.064835, 0.065215]
[INTERPOLATION] Symbol 11: DMRS spacing: min=2, max=2, mean=2.00
[INTERPOLATION] Symbol 11: Using cubic spline interpolation
[INTERPOLATION] Symbol 11: Interpolated magnitude range: [0.941337, 1.053204]
[INTERPOLATION] Symbol 11: Interpolated phase range: [-0.064835, 0.065215]
[INTERPOLATION] Symbol 11: Phase change rate: min=0.000039, max=0.062268, mean=0.012399 rad/subcarrier
[CHANNEL EST] MSE at DMRS positions: 0.000998
[CHANNEL EST] Actual channel power at DMRS: 1.0000
[CHANNEL EST] Estimated channel power at DMRS: 1.0023
[CHANNEL EST] MSE at PDSCH positions (after interpolation): 0.000856
[CHANNEL EST] Actual channel power at PDSCH: 1.0000
[CHANNEL EST] Estimated channel power at PDSCH: 1.0024
[CHANNEL EST] Channel correlation (actual vs est) at PDSCH: 1.0008
[EQUALIZATION] SNR: 30.0 dB
[EQUALIZATION] DMRS REs: 1224, PDSCH data REs: 6768
[EQUALIZATION] Method: ZF
[EQUALIZATION] Rx grid (no EQ) power: 0.927255
[EQUALIZATION] Rx grid (EQ) power: 0.926183
[EQUALIZATION] Channel estimate power: 1.002142
[EQUALIZATION] Channel estimate min/max magnitude: 0.935243 / 1.087226
|
User Interface (GUI) Overview
Followings are the snapshots of graphical output (NOTE : If you want to get images for better resolution, I would suggest you to install the sionna on your PC and run the script that I shared).
The GUI provides a compact, real-time view of the 5G NR PDSCH simulation flow from data generation from RX and final recover on RX side.
It is designed to expose the key configuration knobs and signal observation points without overwhelming the user with internal implementation details.
Basic Configuration Panels
The top section of the UI is dedicated to simulation configuration.It is split into three logical groups that mirror the structure of the link model.

Followings are brief descriptions of each part in this view :
-
Channel Configuration panelcontrols the physical layout of the NR resource grid.
-
Parameters such as channel bandwidth, subcarrier spacing, starting RB index, and number of allocated RBs define the frequency-domain structure of the OFDM grid.
-
DMRS Configuration panelcontrols how reference signals are embedded into the grid.
-
This includes the DMRS symbol index, additional positions, length, and configuration type.
-
These parameters directly affect channel estimation accuracy and interpolation behavior.
-
Radio Channel Configuration panel controls the impairments and receiver algorithms.
-
AWGN SNR sets the noise level.
-
Channel estimation and equalization methods determine how the receiver attempts to recover the transmitted symbols.
-
Any change in these fields updates the simulation logic.
-
Run regenerates the full transmit-channel-receive chain using the new settings.
Advanced Configuration Panels
In Advanced configuration following following parameters are available. The Advanced configuration exposes parameters that affect channel dynamics, waveform realism, and repeatability. These options are not required for basic link validation, but they become important when studying time variation, Doppler effects, and channel selectivity.

Followings are brief descriptions of each part in this view :
Visualization Tabs
The Visualization tabs present the signal at key observation points along the end-to-end data path, starting from symbol generation at the transmitter and ending at symbol recovery at the receiver.
-
Each tab corresponds to a specific processing stage.
-
Together, they allow the user to follow how the signal evolves through modulation, channel distortion, estimation, and equalization.

Visualization Tab - Tx Grid
The Tx Grid panel visualizes the transmitted NR OFDM resource grid before any channel impairment is applied. It provides a direct view of how frequency–time resources are populated based on the current configuration.

Followings are brief descriptions of each part in this view :
Visualization Tab - Tx Spectrum
The Tx Spectrum panel visualizes the spectral characteristics of the transmitted NR signal before it passes through the radio channel. It provides a frequency-domain view that complements the time–frequency view of the Tx Grid.

Followings are brief descriptions of each part in this view :
Visualization Tab - Rx Spectrum
The Rx Spectrum panel visualizes the spectral characteristics of the received signal after it has passed through the configured radio channel. It allows direct comparison with the Tx Spectrum to observe how the channel and noise affect the signal in the frequency domain.

Followings are brief descriptions of each part in this view :
Visualization Tab - Channel Coefficient (Raw)
The Channel Coef (raw) panel visualizes the direct output of the channel estimation stage.It shows channel coefficients estimated only at DMRS resource elements, without any interpolation across time or frequency.

Followings are brief descriptions of each part in this view :
Visualization Tab - Channel Coefficient (Inter p)
The Channel Coef (interp) panel visualizes the channel coefficients after interpolation across the full resource grid. It represents the channel knowledge actually used by the equalizer.

Followings are brief descriptions of each part in this view :
Visualization Tab - Rx Grid
The Rx Grid panel visualizes the received OFDM resource grid after channel equalization. It represents the frequency–time signal that is passed to symbol decision and demapping.

Followings are brief descriptions of each part in this view :
Visualization Tab - Constellation
The Constellations view provides a symbol-level summary of the entire transmission and reception process. It condenses all preceding processing stages into a small set of intuitive plots that directly reflect system performance.

Followings are brief descriptions of each part in this view :
-
Transmitter Reference Constellations
-
Receiver Constellations Before Equalization
-
Receiver Constellations After Equalization
-
The Rx PDSCH (EQ) plot shows recovered data symbols after channel estimation, interpolation, and equalization.
-
The Rx DMRS (EQ) plot shows equalized DMRS symbols.
-
Interpretation
-
A tight clustering of equalized PDSCH symbols around ideal constellation points indicates good channel estimation and equalization.
-
Residual spreading reflects:
-
BER provides a quantitative metric that complements the visual inspection.
Advanced Test - TDL
This example demonstrates the effect of a multipath fading channel using a TDL model. The test is executed by selecting TDL as the channel type in the Advanced configuration and pressing Run.

Followings are brief descriptions of each part in this view :
Advanced Test - User TDL
This example demonstrates the User TDL mode, where the multipath channel is constructed only from explicitly selected taps in the TDL Profile table. (NOTE : When User TDL is selected, 'Use Time Domain Channel' is automatically unchecked since this is designed to be applied only to frequency domain resource grid) The test is executed by selecting TDL User as the channel type and pressing Run.

Followings are brief descriptions of each part in this view :
Advanced Test - User CDL
This example demonstrates the use of a CDL (Clustered Delay Line) channel model. The test is executed by selecting CDL as the channel type in the Advanced configuration and pressing Run.

Followings are brief descriptions of each part in this view :
Source Code Overview
Even though I no longer need to describe every single line of code in the age of AI, I still think it is valuable to write down some key points. This is not to show off the code itself. It is to record the intention behind the script. I want to clarify what I was trying to do, what kind of idea I had in mind, and what kind of problems this script is designed to solve. This allows the reader to connect the final result with the original design goal. It also helps me later when I come back to
this code after a long time and forget my own thinking process.
You may not need all of these details if your only goal is to understand the output of the script. For simple usage, you can often treat the script as a black box. You run it. You look at the result. You move on. In many cases this is enough. However, if you want to modify, revise, or extend the script for your own purpose, the situation changes. You would need to know which part of the code is doing what. You would need to know which parameters control which behavior. You would also need to
know what kind of assumptions I made when I first wrote the code. In that case, a clear description of the code is not a luxury. It becomes a kind of map.
One of the best ways to learn anything related to programming is still very old-fashioned. You break it and then you fix it. You change a small part of the script and see what goes wrong. You check the error. You adjust. You run it again. This loop may look inefficient at first, but it forces your brain to connect cause and effect inside the code. So my intention with these explanations is not just to document the script. It is to give you enough context so that you can safely break it, understand
why it broke, and then fix it in a way that matches your own idea. This process is painful sometimes, but it is also the part that makes the knowledge stick to your brain.
Key Features
- Dual Channel Processing Modes: Frequency-domain (fast) and time-domain (realistic) channel application
- Multiple Channel Models: AWGN, TDL (Tapped Delay Line), and CDL (Clustered Delay Line) with various profiles
- Channel Estimation: LS and MMSE methods with frequency/time interpolation
- Equalization: ZF and MMSE equalizers
- Doppler Support: Time-varying channels with velocity-based Doppler shift
- 3GPP-Compliant DMRS: DMRS Type A with configurable positions, CDM groups, and OCC
- CORESET Support: Configurable CORESET regions with bitmap-based allocation
- Interactive GUI: Real-time configuration and visualization with tabbed interface
- BER Analysis: Comprehensive BER calculation and constellation visualization
Fully Implemented
|
Feature
|
Description
|
Reference
|
|
Resource Grid Creation
|
OFDM resource grid with configurable FFT size, guard carriers, and subcarrier spacing
|
TS 38.211
|
|
DMRS Type A
|
Symbol positions based on l0 and additional position (0–3)
|
TS 38.211 Table 7.4.1.1.2-3
|
|
DMRS Type 1 Frequency Pattern
|
Comb-2 pattern with even/odd subcarrier allocation
|
TS 38.211 Section 7.4.1.1.2
|
|
DMRS CDM Groups
|
Support for CDM groups 0 and 1 (Type 1)
|
TS 38.211 Table 7.4.1.1.2-1
|
|
DMRS OCC
|
Orthogonal Cover Codes for ports 0–3 (single-symbol) and 0–7 (double-symbol)
|
TS 38.211
|
|
CORESET Configuration
|
Full CORESET setup with bitmap, duration, start symbol
|
TS 38.331
|
|
CORESET Bitmap
|
45-bit frequency domain bitmap (each bit = 6 RBs)
|
TS 38.331
|
|
Frequency-Domain Channel
|
Direct channel multiplication in frequency domain (default)
|
-
|
|
Time-Domain Channel
|
Full OFDM modulation/demodulation with IFFT/FFT and CP
|
-
|
|
AWGN Channel
|
Additive White Gaussian Noise channel model
|
-
|
|
TDL Channel
|
Tapped Delay Line channel with configurable profiles (A/B/C/D)
|
TS 38.901
|
|
CDL Channel
|
Clustered Delay Line channel with configurable profiles (A/B/C/D/E)
|
TS 38.901
|
|
Doppler Shift
|
Time-varying phase rotation based on velocity and carrier frequency
|
-
|
|
Channel Estimation (LS)
|
Least Squares channel estimation on DMRS REs
|
-
|
|
Channel Estimation (MMSE)
|
Minimum Mean Square Error channel estimation
|
-
|
|
Channel Interpolation
|
Frequency and time interpolation of channel estimates
|
-
|
|
ZF Equalization
|
Zero Forcing equalization
|
-
|
|
MMSE Equalization
|
Minimum Mean Square Error equalization
|
-
|
|
BER Calculation
|
Bit Error Rate calculation with constellation matching
|
-
|
|
Interactive GUI
|
Tkinter-based GUI with real-time configuration and visualization
|
-
|
|
Channel Coefficient Visualization
|
Magnitude and phase plots of channel estimates
|
-
|
|
Constellation Visualization
|
Scatter plots of TX/RX symbols (pre/post equalization)
|
-
|
|
Spectrum Analysis
|
Welch PSD and spectrogram visualization
|
-
|
Partially Implemented
|
Feature
|
Status
|
Notes
|
|
Sionna TDL/CDL Models
|
Optional
|
Uses Sionna built-in models if available, otherwise falls back to custom implementation
|
|
Channel Interpolation
|
Conditional
|
Uses Sionna LinearInterpolator if available, otherwise uses custom cubic spline interpolation
|
|
PTRS (Phase Tracking Reference Signal)
|
Partial
|
Time/frequency density supported; actual PTRS sequence not generated
|
Not Implemented
|
Feature
|
Description
|
Reference
|
|
CSI-RS
|
Channel State Information Reference Signal
|
TS 38.211 Section 7.4.1.5
|
|
PTRS Sequence Generation
|
Gold sequence for PTRS values
|
TS 38.211 Section 7.4.1.2.2
|
|
DMRS Gold Sequence
|
Uses random QPSK instead of proper Gold sequence
|
TS 38.211 Section 7.4.1.1.1
|
|
LDPC/Polar Encoding
|
Channel coding
|
TS 38.212
|
|
Rate Matching
|
Rate matching around reserved resources
|
TS 38.214
|
|
Scrambling
|
PDSCH scrambling sequence
|
TS 38.211
|
|
Modulation Order Adaptation
|
Fixed 16QAM (configurable in code but not in GUI)
|
TS 38.214
|
|
Multi-Antenna Processing
|
MIMO precoding, spatial multiplexing
|
TS 38.211
|
|
HARQ
|
Hybrid Automatic Repeat Request
|
TS 38.214
|
|
Channel Coding
|
LDPC/Polar encoding and decoding
|
TS 38.212
|
Description of Parameters (Global Variables)
Channel Bandwidth Configuration
|
Parameter
|
Type
|
Default
|
Description
|
|
channel_bw_mhz
|
float
|
20.0
|
Channel bandwidth in MHz (5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 60, 70, 80, 90, 100)
|
|
subcarrier_spacing_hz
|
float
|
30e3
|
Subcarrier spacing in Hz (15e3, 30e3, 60e3, 120e3)
|
|
start_rb
|
int
|
0
|
Starting RB index (0 to MAX_RBS − NUM_RBS)
|
|
num_rbs
|
int
|
51
|
Number of RBs to allocate
|
Antenna Configuration
|
Parameter
|
Type
|
Default
|
Description
|
|
num_tx
|
int
|
1
|
Number of transmitters
|
|
num_streams_per_tx
|
int
|
1
|
Number of streams per transmitter
|
Channel Configuration
|
Parameter
|
Type
|
Default
|
Description
|
|
awgn_snr_db
|
float
|
30.0
|
AWGN signal-to-noise ratio in dB
|
|
channel_type
|
str
|
AWGN
|
Channel type: AWGN, TDL, TDL User or CDL
|
|
channel_profile
|
str
|
TDL-A
|
Channel profile: TDL-A/B/C/D or CDL-A/B/C/D/E
|
|
velocity_kmh
|
float
|
0.0
|
Velocity in km/h (for Doppler shift)
|
|
carrier_frequency_ghz
|
float
|
1.0
|
Carrier frequency in GHz (for Doppler shift)
|
|
use_time_domain_channel
|
bool
|
False
|
True: Apply channel in time domain (with OFDM mod/demod)
False: Frequency-domain channel application (default)
|
Channel Estimation and Equalization
|
Parameter
|
Type
|
Default
|
Description
|
|
channel_est_method
|
str
|
LS
|
Channel estimation method: LS or MMSE
|
|
equalization_method
|
str
|
ZF
|
Equalization method: ZF or MMSE
|
DMRS Configuration
|
Parameter
|
Type
|
Default
|
Description
|
|
dmrs_symbol_index
|
int
|
2
|
First DMRS symbol position l0 (2 or 3 for Type A)
|
|
dmrs_add_pos
|
int
|
3
|
Additional position (0–3 → 1 to 4 DMRS symbols)
|
|
dmrs_length
|
int
|
1
|
DMRS length (1: single-symbol, 2: double-symbol)
|
|
dmrs_config_type
|
int
|
1
|
Configuration type (1: comb-2, 2: comb-3)
|
Data Generator Configuration
|
Parameter
|
Type
|
Default
|
Description
|
|
random_seed_mode
|
str
|
Random
|
Random seed mode: Random or Fixed
|
|
fixed_seed
|
int
|
42
|
Seed value when mode is Fixed
|
Description of Functions
Main Simulation Function
run_sim(cfg: Config, verbose: bool = True) -> SimResult
Runs the complete NR PDSCH simulation pipeline.
Parameters:
|
Parameter
|
Type
|
Description
|
|
cfg
|
Config
|
Configuration parameters (see Config class)
|
|
verbose
|
bool
|
Whether to print progress messages (default: True)
|
Returns: SimResult - Dataclass containing:
- x_rg: Transmitted resource grid tensor
- dmrs_mask_original: Original DMRS mask
- reserved_mask: Reserved RE mask
- rx_grid_no_eq: Received grid before equalization
- rx_grid_eq: Received grid after equalization
- constellations: Dictionary of constellation data for visualization
- ber_pdsch_no_eq: BER without equalization
- ber_pdsch_eq: BER with equalization
- h_est_full: Full channel estimate (interpolated)
- h_est_dmrs: Raw channel estimate at DMRS positions only
- Derived parameters (fft_size, num_subcarriers, etc.)
Processing Steps:
- Compute derived parameters (FFT size, guard carriers, etc.)
- Create DMRS pilot pattern
- Create resource grid and mapper
- Generate and map data symbols (16QAM)
- Apply channel (frequency or time domain based on use_time_domain_channel)
- Channel estimation on DMRS REs
- Channel interpolation across all REs
- Equalization (ZF or MMSE)
- BER calculation
- Return results for visualization
Channel Estimation Functions
estimate_channel_ls(rx_pilots, tx_pilots)
Least Squares (LS) channel estimation.
Parameters:
|
Parameter
|
Type
|
Description
|
|
rx_pilots
|
np.ndarray
|
Received pilot symbols
|
|
tx_pilots
|
np.ndarray
|
Transmitted pilot symbols
|
Returns: np.ndarray - Channel estimates: rx_pilots / tx_pilots
estimate_channel_mmse(rx_pilots, tx_pilots, snr_db, channel_power=1.0)
Minimum Mean Square Error (MMSE) channel estimation.
Parameters:
|
Parameter
|
Type
|
Description
|
|
rx_pilots
|
np.ndarray
|
Received pilot symbols
|
|
tx_pilots
|
np.ndarray
|
Transmitted pilot symbols
|
|
snr_db
|
float
|
Signal-to-noise ratio in dB
|
|
channel_power
|
float
|
Average channel power (default: 1.0)
|
Returns: np.ndarray - MMSE channel estimates
Algorithm:
- First computes LS estimate: h_ls = rx_pilots / tx_pilots
- Applies MMSE smoothing: h_mmse = h_ls * (snr / (snr + 1))
interpolate_channel_response(h_est_dmrs, dmrs_mask, dmrs_symbols, verbose=False)
Interpolates channel estimates across frequency (within DMRS symbols) and time (copy nearest DMRS symbol for data symbols).
Parameters:
|
Parameter
|
Type
|
Description
|
|
h_est_dmrs
|
np.ndarray
|
Partial channel estimate with values on DMRS REs. Shape: (num_symbols, num_subcarriers)
|
|
dmrs_mask
|
np.ndarray
|
Boolean mask of DMRS REs. Shape: (num_symbols, num_subcarriers)
|
|
dmrs_symbols
|
list<int>
|
OFDM symbol indices carrying DMRS
|
|
verbose
|
bool
|
If True, print detailed debug information
|
Returns: np.ndarray - Interpolated channel estimate covering all REs
Algorithm:
- Uses Sionna LinearInterpolator if available, otherwise falls back to custom implementation
- For each DMRS symbol: interpolates across frequency using cubic spline (magnitude and phase separately)
- For data symbols: copies channel from nearest DMRS symbol in time
Equalization Functions
equalize_zf(rx_signal, h_est)
Zero Forcing (ZF) equalization.
Parameters:
|
Parameter
|
Type
|
Description
|
|
rx_signal
|
np.ndarray
|
Received signal
|
|
h_est
|
np.ndarray
|
Channel estimate
|
Returns: np.ndarray - Equalized signal: rx_signal / h_est
equalize_mmse(rx_signal, h_est, snr_db)
Minimum Mean Square Error (MMSE) equalization.
Parameters:
|
Parameter
|
Type
|
Description
|
|
rx_signal
|
np.ndarray
|
Received signal
|
|
h_est
|
np.ndarray
|
Channel estimate
|
|
snr_db
|
float
|
Signal-to-noise ratio in dB
|
Returns: np.ndarray - MMSE equalized signal
Algorithm:
- MMSE equalizer: h_mmse = conj(h_est) / (|h_est|^2 + 1/snr)
- Equalized signal: rx_eq = rx_signal * h_mmse
BER Calculation
calculate_ber(tx_symbols, rx_symbols, constellation_order=16, verbose=False)
Calculates Bit Error Rate by mapping received symbols to nearest constellation points and comparing bits.
Parameters:
|
Parameter
|
Type
|
Description
|
|
tx_symbols
|
np.ndarray
|
Transmitted symbols
|
|
rx_symbols
|
np.ndarray
|
Received symbols
|
|
constellation_order
|
int
|
Constellation order (4: QPSK, 16: 16QAM, 64: 64QAM, 256: 256QAM)
|
|
verbose
|
bool
|
If True, print debug information
|
Returns: float - Bit Error Rate (BER)
Time-Domain Processing Functions
resource_grid_to_time_domain(tx_grid_full, fft_size, cp_length, guard_left, guard_right, verbose=False)
Converts frequency-domain resource grid to time-domain OFDM symbols with cyclic prefix.
Parameters:
|
Parameter
|
Type
|
Description
|
|
tx_grid_full
|
np.ndarray
|
Full frequency-domain resource grid. Shape: (num_ofdm_symbols, fft_size)
|
|
fft_size
|
int
|
FFT size
|
|
cp_length
|
int
|
Cyclic prefix length in samples
|
|
guard_left
|
int
|
Number of left guard carriers
|
|
guard_right
|
int
|
Number of right guard carriers
|
|
verbose
|
bool
|
If True, print debug information
|
Returns: np.ndarray - Time-domain OFDM symbols with CP. Shape: (num_ofdm_symbols, fft_size + cp_length)
Algorithm:
- For each OFDM symbol:
- Apply ifftshift to move DC to index 0
- IFFT: time_symbol = ifft(freq_symbol) * fft_size
- Add cyclic prefix: copy last cp_length samples to the beginning
time_domain_to_resource_grid(time_symbols, fft_size, cp_length, guard_left, num_subcarriers, verbose=False)
Converts time-domain OFDM symbols (with CP) back to frequency-domain resource grid.
Parameters:
|
Parameter
|
Type
|
Description
|
|
time_symbols
|
np.ndarray
|
Time-domain OFDM symbols with CP. Shape: (num_ofdm_symbols, fft_size + cp_length)
|
|
fft_size
|
int
|
FFT size
|
|
cp_length
|
int
|
Cyclic prefix length in samples
|
|
guard_left
|
int
|
Number of left guard carriers
|
|
num_subcarriers
|
int
|
Number of allocated subcarriers
|
|
verbose
|
bool
|
If True, print debug information
|
Returns: np.ndarray - Frequency-domain resource grid. Shape: (num_ofdm_symbols, num_subcarriers)
Algorithm:
- For each OFDM symbol:
- Remove cyclic prefix: time_symbol_no_cp = time_symbols[cp_length:]
- FFT: freq_symbol = fft(time_symbol_no_cp) / fft_size
- Apply fftshift to restore original subcarrier arrangement
- Extract allocated subcarriers (skip guard carriers)
generate_time_domain_channel_ir(channel_type, channel_profile, tx_grid_shape, fft_size, subcarrier_spacing_hz, carrier_frequency_ghz, velocity_kmh, rng, verbose=False)
Generates time-domain channel impulse response for TDL/CDL channels.
Parameters:
|
Parameter
|
Type
|
Description
|
|
channel_type
|
str
|
Channel type: AWGN, TDL, or CDL
|
|
channel_profile
|
str
|
Channel profile (e.g., TDL-A, CDL-A)
|
|
tx_grid_shape
|
tuple
|
Shape of TX grid (num_symbols, num_subcarriers)
|
|
fft_size
|
int
|
FFT size
|
|
subcarrier_spacing_hz
|
float
|
Subcarrier spacing in Hz
|
|
carrier_frequency_ghz
|
float
|
Carrier frequency in GHz
|
|
velocity_kmh
|
float
|
Velocity in km/h
|
|
rng
|
np.random.Generator
|
Random number generator
|
|
verbose
|
bool
|
If True, print debug information
|
Returns: np.ndarray or None - Time-domain channel impulse response. Shape: (num_symbols, max_delay_taps). Returns None for AWGN.
Algorithm:
- TDL: generates multipath taps with exponential power delay profile
- CDL: generates clustered multipath taps with exponential power delay profile
- Applies time variation (Doppler) if velocity > 0
- Normalizes channel power
apply_time_domain_channel(time_symbols, h_ir, cp_length, verbose=False)
Applies time-domain channel (convolution) to OFDM symbols.
Parameters:
|
Parameter
|
Type
|
Description
|
|
time_symbols
|
np.ndarray
|
Time-domain OFDM symbols with CP. Shape: (num_symbols, fft_size + cp_length)
|
|
h_ir
|
np.ndarray or None
|
Time-domain channel impulse response. Shape: (num_symbols, max_delay_taps). If None, no channel is applied (AWGN only)
|
|
cp_length
|
int
|
Cyclic prefix length
|
|
verbose
|
bool
|
If True, print debug information
|
Returns: np.ndarray - Time-domain symbols after channel. Shape: same as time_symbols
Algorithm:
- For each symbol: convolves channel IR with time-domain symbol using np.convolve(..., mode='same')
- Returns unchanged if h_ir is None (AWGN only)
Validation and Utility Functions
validate_rb_allocation(start_rb, num_rbs, max_rbs, channel_bw, scs_khz)
Validates RB allocation parameters and adjusts invalid values.
Returns: tuple(valid_start_rb, valid_num_rbs, error_messages)
compute_derived_params(cfg: Config) -> Dict[str, Any]
Computes derived parameters from configuration (FFT size, guard carriers, etc.).
Returns: dictionary with keys: MAX_RBS, FFT_SIZE, NUM_SUBCARRIERS, GUARD_LEFT, GUARD_RIGHT, etc.
get_dmrs_symbols(l0=2, add_pos=0, num_symbols=14)
Gets DMRS symbol positions based on 3GPP TS 38.211 Table 7.4.1.1.2-3.
Returns: list<int> - DMRS symbol indices
create_nr_dmrs_type_a_pilot_pattern(...)
Creates a complete NR DMRS Type A pilot pattern (3GPP TS 38.211 compliant).
Returns: tuple(pilot_pattern, mask, pilots, dmrs_info)
- pilot_pattern: Sionna PilotPattern object
- mask: Boolean array [num_tx, num_streams, num_symbols, num_sc]
- pilots: Complex array [num_tx, num_streams, num_pilots]
- dmrs_info: Dictionary with configuration summary
Visualization Class
ResourceGridViewer
A Tkinter-based GUI class for visualizing NR PDSCH channel simulation results.
Constructor:
viewer = ResourceGridViewer(cfg, result)
Parameters:
|
Parameter
|
Type
|
Description
|
|
cfg
|
Config
|
Configuration object
|
|
result
|
SimResult
|
Simulation results
|
Methods:
|
Method
|
Description
|
|
create_tab1_full_grid()
|
Full resource grid view (TX) with magnitude and phase
|
|
create_tab4_spectrum()
|
TX spectrum: Welch PSD and spectrogram
|
|
create_tab_rx_spectrum()
|
RX spectrum: Welch PSD and spectrogram (if time-domain processing)
|
|
create_tab_channel_coef()
|
Channel coefficients: magnitude and phase (interpolated)
|
|
create_tab_channel_coef_raw()
|
Raw channel coefficients: DMRS positions only
|
|
create_tab_rx_grid()
|
RX resource grid: magnitude and phase (pre/post equalization)
|
|
create_tab_constellations()
|
Constellation scatter plots: TX, RX (no EQ), RX (EQ), DMRS
|
Configuration Tab:
- Basic Tab: Channel BW, SCS, RB allocation, SNR, channel estimation and equalization methods
- Advanced Tab: Channel type and profile, velocity, carrier frequency, time-domain option, random seed mode, DMRS configuration
Run Button: Re-runs simulation with updated configuration and refreshes all tabs.
VI. Channel Processing Modes
Frequency-Domain Processing (Default)
Mode: use_time_domain_channel = False
Processing Flow:
- Generate frequency-domain channel coefficients based on channel type
- Apply channel: rx_grid = channel_coeffs * tx_grid
- Add AWGN noise in frequency domain
- Channel estimation and equalization in frequency domain
Advantages:
- Fast computation (no IFFT/FFT overhead)
- Suitable for system-level simulations
- Mathematically equivalent for flat channels
Limitations:
- Does not model actual OFDM modulation and demodulation
- May not capture all time-domain effects (ISI, CP handling)
Time-Domain Processing
Mode: use_time_domain_channel = True
Processing Flow:
- Step 1: Convert TX resource grid to time domain (IFFT + CP)
- Step 2: Generate time-domain channel impulse response
- Step 3: Apply channel in time domain (convolution)
- Step 4: Add AWGN noise in time domain (with CP overhead compensation)
- Step 5: Convert back to frequency domain (FFT, remove CP)
- Step 6: Compute frequency-domain channel coefficients for estimation
- Channel estimation and equalization in frequency domain
Advantages:
- More realistic simulation (includes actual OFDM mod/demod)
- Properly models cyclic prefix effects
- Captures time-domain channel characteristics accurately
Limitations:
- Slower computation (IFFT/FFT overhead)
- Slight numerical precision differences may cause minor BER degradation (< 0.5 dB)
Noise Power Normalization:
- Accounts for CP overhead: noise_var_time = noise_var_per_re * fft_size / (fft_size + cp_length)
- Ensures noise power per RE matches frequency-domain approach after FFT
|