Skip to main content
Ctrl+K

Sionna

  • Installation
  • Ray Tracing (RT)
  • Physical Layer (PHY)
  • System Level (SYS)
  • Research Kit (RK)
    • “Made with Sionna”
    • Citation
  • GitHub
  • PyPI
  • Installation
  • Ray Tracing (RT)
  • Physical Layer (PHY)
  • System Level (SYS)
  • Research Kit (RK)
  • “Made with Sionna”
  • Citation
  • GitHub
  • PyPI

Section Navigation

  • Tutorials
    • Beginners
      • “Hello, world!”
      • Part 1: Getting Started with Sionna
      • Part 2: Differentiable Communication Systems
      • Part 3: Advanced Link-level Simulations
      • Part 4: Toward Learned Receivers
      • Basic MIMO Simulations
      • Pulse-shaping Basics
      • Optical Channel with Lumped Amplification
    • Experts
      • 5G Channel Coding and Rate-Matching: Polar vs. LDPC Codes
      • 5G NR PUSCH Tutorial
      • Bit-Interleaved Coded Modulation (BICM)
      • MIMO OFDM Transmissions over the CDL Channel Model
      • Neural Receiver for OFDM SIMO Systems
      • Realistic Multiuser MIMO OFDM Simulations
      • OFDM MIMO Channel Estimation and Detection
      • Introduction to Iterative Detection and Decoding
      • End-to-end Learning with Autoencoders
      • Weighted Belief Propagation Decoding
      • OFDM-based Communications with Superimposed Pilots
      • Channel Models from Datasets
      • Link-level simulations with Sionna RT
  • API Documentation
    • Block
    • Configuration
    • Channel Models
      • Wireless
        • 3GPP 38.901
          • PanelArray
          • Antenna
          • AntennaArray
          • TDL
          • CDL
          • UMi
          • UMa
          • RMa
        • AWGN
        • CIRDataset
        • ChannelModel
        • Flat Fading
          • FlatFadingChannel
          • GenerateFlatFadingChannel
          • ApplyFlatFadingChannel
          • SpatialCorrelation
          • KroneckerModel
          • PerColumnModel
        • Frequency (OFDM) Domain
          • OFDMChannel
          • GenerateOFDMChannel
          • ApplyOFDMChannel
          • cir_to_ofdm_channel
        • RayleighBlockFading
        • Time Domain
          • TimeChannel
          • GenerateTimeChannel
          • ApplyTimeChannel
          • cir_to_time_channel
          • time_to_ofdm_channel
        • Utility Functions
          • subcarrier_frequencies
          • time_lag_discrete_time_channel
          • deg_2_rad
          • rad_2_deg
          • wrap_angle_0_360
          • drop_uts_in_sector
          • relocate_uts
          • set_3gpp_scenario_parameters
          • gen_single_sector_topology
          • gen_single_sector_topology_interferers
          • exp_corr_mat
          • one_ring_corr_mat
      • Optical
        • SSFM
        • EDFA
        • time_frequency_vector
      • Discrete
    • Forward Error Correction (FEC)
      • Linear Codes
        • LinearEncoder
        • OSDecoder
      • Low-Density Parity-Check (LDPC)
        • LDPC5GEncoder
        • LDPCBPDecoder
        • LDPC5GDecoder
        • Node Update Functions
          • vn_update_sum
          • cn_update_minsum
          • cn_update_offset_minsum
          • cn_update_phi
          • cn_update_tanh
        • Decoder Callbacks
          • DecoderStatisticsCallback
          • EXITCallback
          • WeightedBPCallback
      • Polar Codes
        • Polar5GEncoder
        • PolarEncoder
        • Polar5GDecoder
        • PolarSCDecoder
        • PolarSCLDecoder
        • PolarBPDecoder
        • Utility Functions
          • generate_5g_ranking
          • generate_polar_transform_mat
          • generate_rm_code
          • generate_dense_polar
      • Convolutional Codes
        • ConvEncoder
        • ViterbiDecoder
        • BCJRDecoder
        • Utility Functions
          • polynomial_selector
          • Trellis
      • Turbo Codes
        • TurboEncoder
        • TurboDecoder
        • Utility Functions
          • polynomial_selector
          • puncture_pattern
          • TurboTermination
      • Cyclic Redundancy Check (CRC)
        • CRCEncoder
        • CRCDecoder
      • Interleaving
        • RowColumnInterleaver
        • RandomInterleaver
        • Turbo3GPPInterleaver
        • Deinterleaver
      • Scrambling
        • Scrambler
        • Descrambler
        • TB5GScrambler
      • Utility Functions
        • (Binary) Linear Codes
          • load_parity_check_examples
          • alist2mat
          • load_alist
          • generate_reg_ldpc
          • make_systematic
          • gm2pcm
          • pcm2gm
          • verify_gm_pcm
        • EXIT Analysis
          • plot_exit_chart
          • get_exit_analytic
          • plot_trajectory
        • Miscellaneous
          • GaussianPriorSource
          • bin2int
          • int2bin
          • int_mod_2
          • llr2mi
          • j_fun
          • j_fun_inv
    • Mapping
      • Constellation
      • Mapper
      • Demapper
      • SymbolDemapper
      • Utility Functions
        • BinarySource
        • LLRs2SymbolLogits
        • pam
        • pam_gray
        • PAM2QAM
        • PAMSource
        • qam
        • QAM2PAM
        • QAMSource
        • SymbolInds2Bits
        • SymbolLogits2LLRs
        • SymbolLogits2Moments
        • SymbolSource
    • Multiple-Input Multiple-Output (MIMO)
      • StreamManagement
      • Precoding
        • cbf_precoding_matrix
        • rzf_precoding_matrix
        • rzf_precoder
        • grid_of_beams_dft_ula
        • grid_of_beams_dft
        • flatten_precoding_mat
        • normalize_precoding_power
      • Equalization
        • lmmse_matrix
        • lmmse_equalizer
        • zf_equalizer
        • mf_equalizer
      • Detection
        • LinearDetector
        • MaximumLikelihoodDetector
        • KBestDetector
        • EPDetector
        • MMSEPICDetector
      • Utility Functions
        • List2LLR
        • List2LLRSimple
        • complex2real_vector
        • real2complex_vector
        • complex2real_matrix
        • real2complex_matrix
        • complex2real_covariance
        • real2complex_covariance
        • complex2real_channel
        • real2complex_channel
        • whiten_channel
    • 5G NR
      • Carrier
        • CarrierConfig
      • Layer Mapping
        • LayerMapper
        • LayerDemapper
      • PUSCH
        • PUSCHConfig
        • PUSCHDMRSConfig
        • PUSCHLSChannelEstimator
        • PUSCHPilotPattern
        • PUSCHPrecoder
        • PUSCHReceiver
        • PUSCHTransmitter
      • Transport Block
        • TBConfig
        • TBEncoder
        • TBDecoder
      • Utils
        • calculate_tb_size
        • generate_prng_seq
        • decode_mcs_index
        • calculate_num_coded_bits
        • TransportBlockNR
        • CodedAWGNChannelNR
        • MCSDecoderNR
    • Object
    • Orthogonal Frequency-Division Multiplexing (OFDM)
      • Resource Grid
        • ResourceGrid
        • ResourceGridMapper
        • ResourceGridDemapper
        • RemoveNulledSubcarriers
      • Modulation & Demodulation
        • OFDMModulator
        • OFDMDemodulator
      • Pilot Pattern
        • PilotPattern
        • EmptyPilotPattern
        • KroneckerPilotPattern
      • Channel Estimation
        • BaseChannelEstimator
        • LSChannelEstimator
        • BaseChannelInterpolator
        • NearestNeighborInterpolator
        • LinearInterpolator
        • LMMSEInterpolator1D
        • LMMSEInterpolator
        • SpatialChannelFilter
        • tdl_freq_cov_mat
        • tdl_time_cov_mat
      • Precoding
        • RZFPrecoder
        • PrecodedChannel
        • CBFPrecodedChannel
        • EyePrecodedChannel
        • RZFPrecodedChannel
      • Equalization
        • OFDMEqualizer
        • LMMSEEqualizer
        • ZFEqualizer
        • MFEqualizer
        • PostEqualizationSINR
        • LMMSEPostEqualizationSINR
      • Detection
        • OFDMDetector
        • OFDMDetectorWithPrior
        • LinearDetector
        • MaximumLikelihoodDetector
        • MaximumLikelihoodDetectorWithPrior
        • KBestDetector
        • EPDetector
        • MMSEPICDetector
    • Signal
      • Filters
        • Filter
        • RaisedCosineFilter
        • RootRaisedCosineFilter
        • SincFilter
        • CustomFilter
      • Window functions
        • Window
        • HannWindow
        • HammingWindow
        • BlackmanWindow
        • CustomWindow
      • Utility functions
        • convolve
        • fft
        • ifft
        • empirical_psd
        • empirical_aclr
        • Upsampling
        • Downsampling
    • Utility Functions
      • Linear Algebra
        • inv_cholesky
        • matrix_pinv
      • Metrics
        • compute_ber
        • compute_bler
        • compute_ser
        • count_block_errors
        • count_errors
      • Misc
        • complex_normal
        • db_to_lin
        • dbm_to_watt
        • DeepUpdateDict
        • dict_keys_to_int
        • ebnodb2no
        • hard_decisions
        • Interpolate
        • lin_to_db
        • MCSDecoder
        • sample_bernoulli
        • scalar_to_shaped_tensor
        • sim_ber
        • SingleLinkChannel
        • SplineGriddataInterpolation
        • to_list
        • TransportBlock
        • watt_to_dbm
      • Numerics
        • bisection_method
        • expand_bound
      • Plotting
        • plot_ber
        • PlotBER
      • Random number generation
        • randint
        • rand
        • uniform
        • normal
      • Tensors
        • diag_part_axis
        • enumerate_indices
        • expand_to_rank
        • find_true_position
        • flatten_dims
        • flatten_last_dims
        • flatten_multi_index
        • gather_from_batched_indices
        • insert_dims
        • random_tensor_from_values
        • split_dim
        • tensor_values_are_in_set
  • Developer Guides
    • Matrix inversion
    • Random number generation
    • Sionna Block and Object
  • References
  • Physical Layer (PHY)
  • Tutorials
  • Beginners
  • Part 3: Advanced Link-level Simulations
Colab logo Run in Google Colab View on GitHub ⬇ Download notebook

Part 3: Advanced Link-level Simulations#

This tutorial will guide you through Sionna, from its basic principles to the implementation of a point-to-point link with a 5G NR compliant code and a 3GPP channel model. You will also learn how to write custom trainable blocks by implementing a state of the art neural receiver, and how to train and evaluate end-to-end communication systems.

The tutorial is structured in four notebooks:

  • Part I: Getting started with Sionna

  • Part II: Differentiable Communication Systems

  • Part III: Advanced Link-level Simulations

  • Part IV: Toward Learned Receivers

The official documentation provides key material on how to use Sionna and how its components are implemented.

  • Imports

  • OFDM Resource Grid and Stream Management

  • Antenna Arrays

  • Channel Model

  • Uplink Transmission in the Frequency Domain

Imports#

[1]:
# Import Sionna
try:
    import sionna as sn
    import sionna.phy
except ImportError as e:
    import os
    import sys
    if 'google.colab' in sys.modules:
       # Install Sionna in Google Colab
       print("Installing Sionna and restarting the runtime. Please run the cell again.")
       os.system("pip install sionna")
       os.kill(os.getpid(), 5)
    else:
       raise e

import torch
import numpy as np

# For plotting
%matplotlib inline
import matplotlib.pyplot as plt

# Set seed for reproducible results
sn.phy.config.seed = 42

OFDM Resource Grid and Stream Management#

We will setup a realistic SIMO point-to-point link between a mobile user terminal (UT) and a base station (BS). The system we will setup is shown in the figure below.

SIMO

Stream Management#

For any type of MIMO simulations, it is required to setup a StreamManagement object. It determines which transmitters and receivers communicate data streams with each other. In our scenario, we will configure a single UT equipped with a single antenna and a single BS equipped with multiple antennas. Whether the UT or BS is considered as a transmitter depends on the link direction, which can be either uplink or downlink. The StreamManagement has many properties that are used by other components, such as precoding and equalization.

We will configure the system here such that the number of streams per transmitter is equal to the number of UT antennas.

[2]:
# Define the number of UT and BS antennas
NUM_UT = 1
NUM_BS = 1
NUM_UT_ANT = 1
NUM_BS_ANT = 4

# The number of transmitted streams is equal to the number of UT antennas
# in both uplink and downlink
NUM_STREAMS_PER_TX = NUM_UT_ANT

# Create an RX-TX association matrix.
# RX_TX_ASSOCIATION[i,j]=1 means that receiver i gets at least one stream
# from transmitter j. Depending on the transmission direction (uplink or downlink),
# the role of UT and BS can change.
# For example, considering a system with 2 RX and 4 TX, the RX-TX
# association matrix could be
# [ [1 , 1, 0, 0],
#   [0 , 0, 1, 1] ]
# which indicates that the RX 0 receives from TX 0 and 1, and RX 1 receives from
# TX 2 and 3.
#
# In this notebook, as we have only a single transmitter and receiver,
# the RX-TX association matrix is simply:
RX_TX_ASSOCIATION = np.array([[1]])

# Instantiate a StreamManagement object
# This determines which data streams are determined for which receiver.
# In this simple setup, this is fairly straightforward. However,
# in setups with multiple transmitters and receivers, the stream management
# is more involved.
STREAM_MANAGEMENT = sn.phy.mimo.StreamManagement(RX_TX_ASSOCIATION, NUM_STREAMS_PER_TX)

Resource Grid#

We will also configure a ResourceGrid object. It is used to allocate data symbols and pilot symbols on the resource grid. In this notebook, we use a kronecker pilot pattern, as described in the OFDM-MIMO Detection tutorial.

Please have a look at the API documentation for all parameters.

[3]:
RESOURCE_GRID = sn.phy.ofdm.ResourceGrid(num_ofdm_symbols=14,
                                         fft_size=76,
                                         subcarrier_spacing=30e3,
                                         num_tx=NUM_UT,
                                         num_streams_per_tx=NUM_STREAMS_PER_TX,
                                         cyclic_prefix_length=6,
                                         pilot_pattern="kronecker",
                                         pilot_ofdm_symbol_indices=[2, 11])
[4]:
RESOURCE_GRID.pilot_pattern.show();
../../../../build/doctrees/nbsphinx/phy_tutorials_notebooks_Sionna_tutorial_part3_13_0.png

Antenna Arrays#

We use the Antenna and AntennaArray classes from the 3GPP TR38.901 channel model to configure our antenna elements and arrays. The Antenna class consists of a single antenna element, whereas the AntennaArray class consists of multiple antenna elements.

Please have a look at the API documentation for more information.

[5]:
CARRIER_FREQUENCY = 2.6e9 # Carrier frequency in Hz.
                          # This is needed here to define the antenna element spacing.

UT_ARRAY = sn.phy.channel.tr38901.Antenna(polarization="single",
                                          polarization_type="V",
                                          antenna_pattern="38.901",
                                          carrier_frequency=CARRIER_FREQUENCY)
UT_ARRAY.show();

BS_ARRAY = sn.phy.channel.tr38901.AntennaArray(num_rows=1,
                                               num_cols=int(NUM_BS_ANT/2),
                                               polarization="dual",
                                               polarization_type="cross",
                                               antenna_pattern="38.901",
                                               carrier_frequency=CARRIER_FREQUENCY)
../../../../build/doctrees/nbsphinx/phy_tutorials_notebooks_Sionna_tutorial_part3_16_0.png
[6]:
BS_ARRAY.show_element_radiation_pattern();
../../../../build/doctrees/nbsphinx/phy_tutorials_notebooks_Sionna_tutorial_part3_17_0.png
../../../../build/doctrees/nbsphinx/phy_tutorials_notebooks_Sionna_tutorial_part3_17_1.png
../../../../build/doctrees/nbsphinx/phy_tutorials_notebooks_Sionna_tutorial_part3_17_2.png

Channel Model#

The 3GPP 38.901 channel model offers five different delay profiles, called “CDL-A”, “CDL-B”, “CDL-C”, “CDL-D”, and “CDL-E”. The first three model non line-of-sight (NLoS) channels, whereas the last two model line-of-sight (LoS) channels.

Please have a look at the API documentation for more information.

[7]:
DELAY_SPREAD = 100e-9 # Nominal delay spread in [s]. Please see the CDL documentation
                      # about how to choose this value.

DIRECTION = "uplink"  # The `direction` determines if the UT or BS is transmitting.
                      # In the `uplink`, the UT is transmitting.

CDL_MODEL = "C"       # Suitable values are ["A", "B", "C", "D", "E"]

SPEED = 10.0          # UT speed [m/s]. BSs are always assumed to be fixed.
                      # The direction of travel will chosen randomly.

CDL = sn.phy.channel.tr38901.CDL(CDL_MODEL,
                                 DELAY_SPREAD,
                                 CARRIER_FREQUENCY,
                                 UT_ARRAY,
                                 BS_ARRAY,
                                 DIRECTION,
                                 min_speed=SPEED)

The instance CDL of the CDL model can be used to generate batches of random realizations of continuous-time channel impulse responses, consisting of complex gains a and delays tau for each path. To account for time-varying channels, a channel impulse responses is sampled at the sampling_frequency for num_time_samples samples. For more details on this, please have a look at the API documentation of the channel models.

In order to model the channel in the frequency domain, we need num_ofdm_symbols samples that are taken once per ofdm_symbol_duration, which corresponds to the length of an OFDM symbol plus the cyclic prefix.

[8]:
BATCH_SIZE = 128 # How many examples are processed by Sionna in parallel

a, tau = CDL(batch_size=BATCH_SIZE,
             num_time_steps=RESOURCE_GRID.num_ofdm_symbols,
             sampling_frequency=1/RESOURCE_GRID.ofdm_symbol_duration)
The path gains a have shape
[batch size, num_rx, num_rx_ant, num_tx, num_tx_ant, num_paths, num_time_steps]
and the delays tau have shape
[batch_size, num_rx, num_tx, num_paths].
[9]:
print("Shape of the path gains: ", a.shape)
print("Shape of the delays:", tau.shape)
Shape of the path gains:  torch.Size([128, 1, 4, 1, 1, 24, 14])
Shape of the delays: torch.Size([128, 1, 1, 24])

The delays are assumed to be static within the time-window of interest. Only the complex path gains change over time. The following two figures depict the channel impulse response at a particular time instant and the time-evolution of the gain of one path, respectively.

[10]:
a = a.cpu().numpy()
tau = tau.cpu().numpy()

plt.figure()
plt.title("Channel impulse response realization")
plt.stem(tau[0,0,0,:]/1e-9, np.abs(a)[0,0,0,0,0,:,0])
plt.xlabel(r"$\tau$ [ns]")
plt.ylabel(r"$|a|$")

plt.figure()
plt.title("Time evolution of path gain")
plt.plot(np.arange(RESOURCE_GRID.num_ofdm_symbols)*RESOURCE_GRID.ofdm_symbol_duration/1e-6, np.real(a)[0,0,0,0,0,0,:])
plt.plot(np.arange(RESOURCE_GRID.num_ofdm_symbols)*RESOURCE_GRID.ofdm_symbol_duration/1e-6, np.imag(a)[0,0,0,0,0,0,:])
plt.legend(["Real part", "Imaginary part"])

plt.xlabel(r"$t$ [us]")
plt.ylabel(r"$a$");
../../../../build/doctrees/nbsphinx/phy_tutorials_notebooks_Sionna_tutorial_part3_26_0.png
../../../../build/doctrees/nbsphinx/phy_tutorials_notebooks_Sionna_tutorial_part3_26_1.png

See the notebook Realistic Multiuser MIMO Simulations for more advanced examples.

Uplink Transmission in the Frequency Domain#

We are now ready to simulate a transmission.

In the following, the channel is simulated in the frequency domain. Therefore, the channel is assumed to be constant over the duration of an OFDM symbol, which leads to not simulating the intercarrier interference (ICI) that could occur due to channel aging over the duration of OFDM symbols.

The OFDMChannel layer is used to simulate the channel in the frequency domain and takes care of sampling channel impulse responses, computing the frequency responses, and applying the channel transfer function to the channel inputs (including AWGN).

Note that it is also possible to simulate the channel in time domain using the TimeChannel layer, which enables simulation of ICI. For more information, please have a look at the API documentation.

[11]:
NUM_BITS_PER_SYMBOL = 2 # QPSK
CODERATE = 0.5

# Number of coded bits in a resource grid
n = int(RESOURCE_GRID.num_data_symbols*NUM_BITS_PER_SYMBOL)
# Number of information bits in a resource grid
k = int(n*CODERATE)

# The binary source will create batches of information bits
binary_source = sn.phy.mapping.BinarySource()

# The encoder maps information bits to coded bits
encoder = sn.phy.fec.ldpc.LDPC5GEncoder(k, n)

# The mapper maps blocks of information bits to constellation symbols
mapper = sn.phy.mapping.Mapper("qam", NUM_BITS_PER_SYMBOL)

# The resource grid mapper maps symbols onto an OFDM resource grid
rg_mapper = sn.phy.ofdm.ResourceGridMapper(RESOURCE_GRID)

# Frequency domain channel
channel = sn.phy.channel.OFDMChannel(CDL, RESOURCE_GRID, add_awgn=True, normalize_channel=True, return_channel=True)

# The LS channel estimator will provide channel estimates and error variances
ls_est = sn.phy.ofdm.LSChannelEstimator(RESOURCE_GRID, interpolation_type="nn")

# The LMMSE equalizer will provide soft symbols together with noise variance estimates
lmmse_equ = sn.phy.ofdm.LMMSEEqualizer(RESOURCE_GRID, STREAM_MANAGEMENT)

# The demapper produces LLR for all coded bits
demapper = sn.phy.mapping.Demapper("app", "qam", NUM_BITS_PER_SYMBOL)

# The decoder provides hard-decisions on the information bits
decoder = sn.phy.fec.ldpc.LDPC5GDecoder(encoder, hard_out=True)

Let’s now simulate the transmission, and look at the shape of the layers outputs at each stage.

The utility function ebnodb2no takes as additional input the resource grid to account for the pilots when computing the noise power spectral density ratio \(N_0\) from the energy per bit to noise power spectral density ratio \(E_b/N_0\) (in dB).

[12]:
no = sn.phy.utils.ebnodb2no(ebno_db=10.0,
                            num_bits_per_symbol=NUM_BITS_PER_SYMBOL,
                            coderate=CODERATE,
                            resource_grid=RESOURCE_GRID)

# Transmitter
bits = binary_source([BATCH_SIZE, NUM_UT, RESOURCE_GRID.num_streams_per_tx, k])
print("Shape of bits: ", bits.shape)
codewords = encoder(bits)
print("Shape of codewords: ", codewords.shape)
x = mapper(codewords)
print("Shape of x: ", x.shape)
x_rg = rg_mapper(x)
print("Shape of x_rg: ", x_rg.shape)

# Channel
y, h_freq = channel(x_rg, no)
print("Shape of y_rg: ", y.shape)
print("Shape of h_freq: ", h_freq.shape)

# Receiver
h_hat, err_var = ls_est (y, no)
print("Shape of h_hat: ", h_hat.shape)
print("Shape of err_var: ", err_var.shape)
x_hat, no_eff = lmmse_equ(y, h_hat, err_var, no)
print("Shape of x_hat: ", x_hat.shape)
print("Shape of no_eff: ", no_eff.shape)
llr = demapper(x_hat, no_eff)
print("Shape of llr: ", llr.shape)
bits_hat = decoder(llr)
print("Shape of bits_hat: ", bits_hat.shape)
Shape of bits:  torch.Size([128, 1, 1, 912])
Shape of codewords:  torch.Size([128, 1, 1, 1824])
Shape of x:  torch.Size([128, 1, 1, 912])
Shape of x_rg:  torch.Size([128, 1, 1, 14, 76])
Shape of y_rg:  torch.Size([128, 1, 4, 14, 76])
Shape of h_freq:  torch.Size([128, 1, 4, 1, 1, 14, 76])
Shape of h_hat:  torch.Size([128, 1, 4, 1, 1, 14, 76])
Shape of err_var:  torch.Size([128, 1, 4, 1, 1, 14, 76])
Shape of x_hat:  torch.Size([128, 1, 1, 912])
Shape of no_eff:  torch.Size([128, 1, 1, 912])
Shape of llr:  torch.Size([128, 1, 1, 1824])
Shape of bits_hat:  torch.Size([128, 1, 1, 912])

Creating an End-to-End System#

We can now combine all of the above components into a single Block that implements a complete transmission chain. In Sionna, the Block class is the fundamental building block for creating differentiable communication systems. It inherits from torch.nn.Module and provides additional functionality for managing device placement, precision, and random number generation.

[13]:
class OFDMSystem(sn.phy.Block):
    """
    Complete OFDM system for link-level simulations.

    This class encapsulates the entire transmitter-channel-receiver chain
    for an OFDM system with LDPC coding over a CDL channel.
    """

    def __init__(self, perfect_csi):
        super().__init__()

        self.perfect_csi = perfect_csi

        n = int(RESOURCE_GRID.num_data_symbols*NUM_BITS_PER_SYMBOL) # Number of coded bits
        k = int(n*CODERATE) # Number of information bits
        self.k = k

        # The binary source will create batches of information bits
        self.binary_source = sn.phy.mapping.BinarySource()

        # The encoder maps information bits to coded bits
        self.encoder = sn.phy.fec.ldpc.LDPC5GEncoder(k, n)

        # The mapper maps blocks of information bits to constellation symbols
        self.mapper = sn.phy.mapping.Mapper("qam", NUM_BITS_PER_SYMBOL)

        # The resource grid mapper maps symbols onto an OFDM resource grid
        self.rg_mapper = sn.phy.ofdm.ResourceGridMapper(RESOURCE_GRID)

        # Frequency domain channel
        self.channel = sn.phy.channel.OFDMChannel(CDL, RESOURCE_GRID, add_awgn=True, normalize_channel=True, return_channel=True)

        # The LS channel estimator will provide channel estimates and error variances
        self.ls_est = sn.phy.ofdm.LSChannelEstimator(RESOURCE_GRID, interpolation_type="nn")

        # The LMMSE equalizer will provide soft symbols together with noise variance estimates
        self.lmmse_equ = sn.phy.ofdm.LMMSEEqualizer(RESOURCE_GRID, STREAM_MANAGEMENT)

        # The demapper produces LLR for all coded bits
        self.demapper = sn.phy.mapping.Demapper("app", "qam", NUM_BITS_PER_SYMBOL)

        # The decoder provides hard-decisions on the information bits
        self.decoder = sn.phy.fec.ldpc.LDPC5GDecoder(self.encoder, hard_out=True)

    def call(self, batch_size, ebno_db):
        """Forward pass through the OFDM system."""
        no = sn.phy.utils.ebnodb2no(ebno_db, num_bits_per_symbol=NUM_BITS_PER_SYMBOL, coderate=CODERATE, resource_grid=RESOURCE_GRID)

        # Transmitter
        bits = self.binary_source([batch_size, NUM_UT, RESOURCE_GRID.num_streams_per_tx, self.k])
        codewords = self.encoder(bits)
        x = self.mapper(codewords)
        x_rg = self.rg_mapper(x)

        # Channel
        y, h_freq = self.channel(x_rg, no)

        # Receiver
        if self.perfect_csi:
            h_hat, err_var = h_freq, 0.
        else:
            h_hat, err_var = self.ls_est(y, no)
        x_hat, no_eff = self.lmmse_equ(y, h_hat, err_var, no)
        llr = self.demapper(x_hat, no_eff)
        bits_hat = self.decoder(llr)

        return bits, bits_hat

Running the Simulation#

Now we run BER simulations for both LS channel estimation and perfect CSI. The PlotBER utility makes it easy to run Monte Carlo simulations and plot the results.

[14]:
EBN0_DB_MIN = -8.0 # Minimum value of Eb/N0 [dB] for simulations
EBN0_DB_MAX = 3.0 # Maximum value of Eb/N0 [dB] for simulations

ber_plots = sn.phy.utils.PlotBER("OFDM over 3GPP CDL")

model_ls = OFDMSystem(False)
ber_plots.simulate(model_ls,
                  ebno_dbs=np.linspace(EBN0_DB_MIN, EBN0_DB_MAX, 20),
                  batch_size=BATCH_SIZE,
                  num_target_block_errors=100, # simulate until 100 block errors occurred
                  legend="LS Estimation",
                  soft_estimates=True,
                  max_mc_iter=100, # run 100 Monte-Carlo simulations (each with batch_size samples)
                  show_fig=False);

model_pcsi = OFDMSystem(True)
ber_plots.simulate(model_pcsi,
                  ebno_dbs=np.linspace(EBN0_DB_MIN, EBN0_DB_MAX, 20),
                  batch_size=BATCH_SIZE,
                  num_target_block_errors=100, # simulate until 100 block errors occurred
                  legend="Perfect CSI",
                  soft_estimates=True,
                  max_mc_iter=100, # run 100 Monte-Carlo simulations (each with batch_size samples)
                  show_fig=False);

ber_plots();
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
     -8.0 | 4.3150e-01 | 1.0000e+00 |       50372 |      116736 |          128 |         128 |         0.0 |reached target block errors
   -7.421 | 4.2313e-01 | 1.0000e+00 |       49395 |      116736 |          128 |         128 |         0.0 |reached target block errors
   -6.842 | 4.0915e-01 | 1.0000e+00 |       47762 |      116736 |          128 |         128 |         0.0 |reached target block errors
   -6.263 | 4.0056e-01 | 1.0000e+00 |       46760 |      116736 |          128 |         128 |         0.0 |reached target block errors
   -5.684 | 3.8882e-01 | 1.0000e+00 |       45389 |      116736 |          128 |         128 |         0.0 |reached target block errors
   -5.105 | 3.7701e-01 | 1.0000e+00 |       44011 |      116736 |          128 |         128 |         0.0 |reached target block errors
   -4.526 | 3.6298e-01 | 1.0000e+00 |       42373 |      116736 |          128 |         128 |         0.0 |reached target block errors
   -3.947 | 3.4417e-01 | 1.0000e+00 |       40177 |      116736 |          128 |         128 |         0.0 |reached target block errors
   -3.368 | 3.2861e-01 | 1.0000e+00 |       38361 |      116736 |          128 |         128 |         0.0 |reached target block errors
   -2.789 | 3.0912e-01 | 1.0000e+00 |       36086 |      116736 |          128 |         128 |         0.0 |reached target block errors
   -2.211 | 2.9042e-01 | 1.0000e+00 |       33903 |      116736 |          128 |         128 |         0.0 |reached target block errors
   -1.632 | 2.6795e-01 | 1.0000e+00 |       31279 |      116736 |          128 |         128 |         0.0 |reached target block errors
   -1.053 | 2.4026e-01 | 1.0000e+00 |       28047 |      116736 |          128 |         128 |         0.0 |reached target block errors
   -0.474 | 2.0492e-01 | 1.0000e+00 |       23921 |      116736 |          128 |         128 |         0.0 |reached target block errors
    0.105 | 1.4728e-01 | 9.8438e-01 |       17193 |      116736 |          126 |         128 |         0.0 |reached target block errors
    0.684 | 3.0796e-02 | 4.4531e-01 |        7190 |      233472 |          114 |         256 |         0.1 |reached target block errors
    1.263 | 4.7638e-04 | 1.0959e-02 |        4004 |     8404992 |          101 |        9216 |         2.2 |reached target block errors
    1.842 | 0.0000e+00 | 0.0000e+00 |           0 |    11673600 |            0 |       12800 |         3.0 |reached max iterations

Simulation stopped as no error occurred @ EbNo = 1.8 dB.

EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
     -8.0 | 2.8361e-01 | 1.0000e+00 |       33108 |      116736 |          128 |         128 |         0.0 |reached target block errors
   -7.421 | 2.6357e-01 | 1.0000e+00 |       30768 |      116736 |          128 |         128 |         0.0 |reached target block errors
   -6.842 | 2.4973e-01 | 1.0000e+00 |       29153 |      116736 |          128 |         128 |         0.0 |reached target block errors
   -6.263 | 2.2761e-01 | 1.0000e+00 |       26570 |      116736 |          128 |         128 |         0.0 |reached target block errors
   -5.684 | 2.0400e-01 | 1.0000e+00 |       23814 |      116736 |          128 |         128 |         0.0 |reached target block errors
   -5.105 | 1.7256e-01 | 1.0000e+00 |       20144 |      116736 |          128 |         128 |         0.0 |reached target block errors
   -4.526 | 9.7425e-02 | 9.7656e-01 |       11373 |      116736 |          125 |         128 |         0.0 |reached target block errors
   -3.947 | 1.5525e-02 | 3.8281e-01 |        5437 |      350208 |          147 |         384 |         0.1 |reached target block errors
   -3.368 | 1.6076e-04 | 8.6806e-03 |        1689 |    10506240 |          100 |       11520 |         2.7 |reached target block errors
   -2.789 | 0.0000e+00 | 0.0000e+00 |           0 |    11673600 |            0 |       12800 |         3.0 |reached max iterations

Simulation stopped as no error occurred @ EbNo = -2.8 dB.

../../../../build/doctrees/nbsphinx/phy_tutorials_notebooks_Sionna_tutorial_part3_36_1.png

previous

Part 2: Differentiable Communication Systems

next

Part 4: Toward Learned Receivers

On this page
  • Imports
  • OFDM Resource Grid and Stream Management
    • Stream Management
    • Resource Grid
  • Antenna Arrays
  • Channel Model
  • Uplink Transmission in the Frequency Domain
    • Creating an End-to-End System
    • Running the Simulation

This Page

  • Show Source

© Copyright 2021-2026 NVIDIA CORPORATION.

Created using Sphinx 9.1.0.

Built with the PyData Sphinx Theme 0.16.1.