Link-level simulations with Sionna RT

In this notebook, you will use ray-traced channels for link-level simulations instead of stochastic channel models

Background Information

Ray tracing is a technique to simulate environment-specific and physically accurate channel realizations for a given scene and user position. Sionna RT is a ray tracing extension for radio propagation modeling which is built on top ofMitsuba 3. Like all of Sionna’s components, it is differentiable.

For an introduction about how to use Sionna RT, please see thecorresponding tutorials. TheEM Primer provides further details on the theoretical background of ray tracing of wireless channels.

In this notebook, we will use Sionna RT for site-specific link-level simulations. For this, we evaluate the BER performance for a MU-MIMO 5G NR system in the uplink direction based on ray traced CIRs for random user positions.

We use the 5G NR PUSCH transmitter and receiver from the5G NR PUSCH Tutorial notebook. Note that also the systems from the MIMOOFDM Transmissions over the CDL Channel Model or theNeural Receiver for OFDM SIMO Systems tutorials could be used instead.

There are different ways to implement uplink scenarios in Sionna RT. In this example, we configure the basestation as transmitter and the user equipments (UEs) as receivers which simplifies the ray tracing. Due to channel reciprocity, one canreverse the direction of the ray traced channels afterwards. For the ray tracer itself, the direction (uplink/downlink) does not change the simulated paths.

Imports

[1]:
importos# Configure which GPUifos.getenv("CUDA_VISIBLE_DEVICES")isNone:gpu_num=0# Use "" to use the CPUos.environ["CUDA_VISIBLE_DEVICES"]=f"{gpu_num}"os.environ['TF_CPP_MIN_LOG_LEVEL']='3'# Import or install Sionnatry:importsionna.phyimportsionna.rtexceptImportErrorase:importsysif'google.colab'insys.modules:# Install Sionna in Google Colabprint("Installing Sionna and restarting the runtime. Please run the cell again.")os.system("pip install sionna")os.kill(os.getpid(),5)else:raisee# Configure the notebook to use only a single GPU and allocate only as much memory as needed# For more details, see https://www.tensorflow.org/guide/gpuimporttensorflowastfgpus=tf.config.list_physical_devices('GPU')ifgpus:try:tf.config.experimental.set_memory_growth(gpus[0],True)exceptRuntimeErrorase:print(e)# Avoid warnings from TensorFlowtf.get_logger().setLevel('ERROR')importnumpyasnp# For link-level simulationsfromsionna.phy.channelimportOFDMChannel,CIRDatasetfromsionna.phy.nrimportPUSCHConfig,PUSCHTransmitter,PUSCHReceiverfromsionna.phy.utilsimportebnodb2no,PlotBERfromsionna.phy.ofdmimportKBestDetector,LinearDetectorfromsionna.phy.mimoimportStreamManagement# Import Sionna RT componentsfromsionna.rtimportload_scene,Camera,Transmitter,Receiver,PlanarArray,\PathSolver,RadioMapSolverno_preview=True# Toggle to False to use the preview widget# instead of rendering for scene visualization

Setting up the Ray Tracer

Let’s start by defining some constants that control the system we want to simulate.

[2]:
# System parameterssubcarrier_spacing=30e3# Hznum_time_steps=14# Total number of ofdm symbols per slotnum_tx=4# Number of usersnum_rx=1# Only one receiver considerednum_tx_ant=4# Each user has 4 antennasnum_rx_ant=16# The receiver is equipped with 16 antennas# batch_size for CIR generationbatch_size_cir=1000

We then set up the radio propagation environment. We start by loading a scene and then add a transmitter that acts as a base station. We will later use channel reciprocity to simulate the uplink direction.

[3]:
# Load an integrated scene.# You can try other scenes, such as `sionna.rt.scene.etoile`. Note that this would require# updating the position of the transmitter (see below in this cell).scene=load_scene(sionna.rt.scene.munich)# Transmitter (=basestation) has an antenna pattern from 3GPP 38.901scene.tx_array=PlanarArray(num_rows=1,num_cols=num_rx_ant//2,# We want to transmitter to be equiped with 16 antennasvertical_spacing=0.5,horizontal_spacing=0.5,pattern="tr38901",polarization="cross")# Create transmittertx=Transmitter(name="tx",position=[8.5,21,27],look_at=[45,90,1.5],# optional, defines view directiondisplay_radius=3.)# optinal, radius of the sphere for visualizing the devicescene.add(tx)# Create new camerabird_cam=Camera(position=[0,80,500],orientation=np.array([0,np.pi/2,-np.pi/2]))

We then compute a radio map for the instantiated transmitter.

[4]:
max_depth=5# Radio map solverrm_solver=RadioMapSolver()# Compute the radio maprm=rm_solver(scene,max_depth=5,cell_size=(1.,1.),samples_per_tx=10**7)

Let’s visualize the computed radio map.

[5]:
ifno_preview:# Render an imagescene.render(camera=bird_cam,radio_map=rm,rm_vmin=-110,clip_at=12.);# Clip the scene at rendering for visualizing the refracted fieldelse:# Show previewscene.preview(radio_map=rm,rm_vmin=-110,clip_at=12.);# Clip the scene at rendering for visualizing the refracted field
../../_images/phy_tutorials_Link_Level_Simulations_with_RT_14_0.png

The functionRadioMap.sample_positions() allows sampling of random user positions from a radio map. It ensures that only positions that have a path gain of at leastmin_gain_dB dB and atmostmax_gain_dB dB are sampled, i.e., it ignores positions without a connection to the transmitter. Further, one can set the distancesmin_dist andmax_dist to sample only points within a certain distance range from the transmitter.

[6]:
min_gain_db=-130# in dB; ignore any position with less than -130 dB path gainmax_gain_db=0# in dB; ignore strong paths# Sample points in a 5-400m range around the receivermin_dist=5# in mmax_dist=400# in m# Sample batch_size random user positions from the radio mapue_pos,_=rm.sample_positions(num_pos=batch_size_cir,metric="path_gain",min_val_db=min_gain_db,max_val_db=max_gain_db,min_dist=min_dist,max_dist=max_dist)

We now add new receivers (=UEs) at the sampled positions.

Remark: This is an example for 5G NR PUSCH (uplink direction), we will reverse the direction of the channel for later BER simulations.

[7]:
# Configure antenna array for all receivers (=UEs)scene.rx_array=PlanarArray(num_rows=1,num_cols=num_tx_ant//2,# Each receiver is equipped with 4 antennasvertical_spacing=0.5,horizontal_spacing=0.5,pattern="iso",polarization="cross")# Create batch_size receiversforiinrange(batch_size_cir):scene.remove(f"rx-{i}")# Remove old receiver if anyrx=Receiver(name=f"rx-{i}",position=ue_pos[0][i],# Position sampled from radio mapvelocity=(3.,3.,0),display_radius=1.,# optional, radius of the sphere for visualizing the devicecolor=(1,0,0)# optional, color for visualizing the device)scene.add(rx)# And visualize the sceneifno_preview:# Render an imagescene.render(camera=bird_cam,radio_map=rm,rm_vmin=-110,clip_at=12.);# Clip the scene at rendering for visualizing the refracted fieldelse:# Show previewscene.preview(radio_map=rm,rm_vmin=-110,clip_at=12.);# Clip the scene at rendering for visualizing the refracted field
../../_images/phy_tutorials_Link_Level_Simulations_with_RT_18_0.png

Each dot represents a receiver position drawn from the random sampling function of the radio map. This allows to efficiently sample batches of random channel realizations even in complex scenarios.

Creating a CIR Dataset

We can now simulate the CIRs for many different positions which will be used later on as a dataset for link-level simulations.

Remark: Running the cells below can take some time depending on the requested number of CIRs.

[8]:
target_num_cirs=5000# Defines how many different CIRs are generated.# Remark: some path are removed if no path was found for this positionmax_depth=5min_gain_db=-130# in dB / ignore any position with less than -130 dB path gainmax_gain_db=0# in dB / ignore any position with more than 0 dB path gain# Sample points within a 10-400m radius around the transmittermin_dist=10# in mmax_dist=400# in m# List of channel impulse reponsesa_list=[]tau_list=[]# Maximum number of paths over all batches of CIRs.# This is used later to concatenate all CIRs.max_num_paths=0# Path solverp_solver=PathSolver()# Each simulation returns batch_size_cir resultsnum_runs=int(np.ceil(target_num_cirs/batch_size_cir))foridxinrange(num_runs):print(f"Progress:{idx+1}/{num_runs}",end="\r")# Sample random user positionsue_pos,_=rm.sample_positions(num_pos=batch_size_cir,metric="path_gain",min_val_db=min_gain_db,max_val_db=max_gain_db,min_dist=min_dist,max_dist=max_dist,seed=idx)# Change the seed from one run to the next to avoid sampling the same positions# Update all receiver positionsforrxinrange(batch_size_cir):scene.receivers[f"rx-{rx}"].position=ue_pos[0][rx]# Simulate CIRpaths=p_solver(scene,max_depth=max_depth,max_num_paths_per_src=10**7)# Transform paths into channel impulse responsesa,tau=paths.cir(sampling_frequency=subcarrier_spacing,num_time_steps=14,out_type='numpy')a_list.append(a)tau_list.append(tau)# Update maximum number of paths over all batches of CIRsnum_paths=a.shape[-2]ifnum_paths>max_num_paths:max_num_paths=num_paths# Concatenate all the CIRs into a single tensor along the num_rx dimension.# First, we need to pad the CIRs to ensure they all have the same number of paths.a=[]tau=[]fora_,tau_inzip(a_list,tau_list):num_paths=a_.shape[-2]a.append(np.pad(a_,[[0,0],[0,0],[0,0],[0,0],[0,max_num_paths-num_paths],[0,0]],constant_values=0))tau.append(np.pad(tau_,[[0,0],[0,0],[0,max_num_paths-num_paths]],constant_values=0))a=np.concatenate(a,axis=0)# Concatenate along the num_rx dimensiontau=np.concatenate(tau,axis=0)# Let's now convert to uplink direction, by switing the receiver and transmitter# dimensionsa=np.transpose(a,(2,3,0,1,4,5))tau=np.transpose(tau,(1,0,2))# Add a batch_size dimensiona=np.expand_dims(a,axis=0)tau=np.expand_dims(tau,axis=0)# Exchange the num_tx and batchsize dimensionsa=np.transpose(a,[3,1,2,0,4,5,6])tau=np.transpose(tau,[2,1,0,3])# Remove CIRs that have no active link (i.e., a is all-zero)p_link=np.sum(np.abs(a)**2,axis=(1,2,3,4,5,6))a=a[p_link>0.,...]tau=tau[p_link>0.,...]print("Shape of a:",a.shape)print("Shape of tau: ",tau.shape)
Shape of a: (4727, 1, 16, 1, 4, 39, 14)Shape of tau:  (4727, 1, 1, 39)

Note that transmitters and receivers have been reversed, i.e., the transmitter now denotes the UE (with 4 antennas each) and the receiver is the base station (with 16 antennas).

Remark: We have removed all positions for which the resulting CIR had zero gain, i.e., there was no path between the transmitter and the receiver. This comes from the fact that theRadioMap.sample_positions() function samples from a radio map subdivided into cells and randomizes the position within the cells. Therefore, randomly sampled positions may have no paths connecting them to the transmitter.

Let us now define a data generator that samples random UEs from the dataset and yields the previously simulated CIRs.

[9]:
classCIRGenerator:"""Creates a generator from a given dataset of channel impulse responses    The generator samples ``num_tx`` different transmitters from the given path    coefficients `a` and path delays `tau` and stacks the CIRs into a single tensor.    Note that the generator internally samples ``num_tx`` random transmitters    from the dataset. For this, the inputs ``a`` and ``tau`` must be given for    a single transmitter (i.e., ``num_tx`` =1) which will then be stacked    internally.    Parameters    ----------    a : [batch size, num_rx, num_rx_ant, 1, num_tx_ant, num_paths, num_time_steps], complex        Path coefficients per transmitter    tau : [batch size, num_rx, 1, num_paths], float        Path delays [s] per transmitter    num_tx : int        Number of transmitters    Output    -------    a : [batch size, num_rx, num_rx_ant, num_tx, num_tx_ant, num_paths, num_time_steps], tf.complex        Path coefficients    tau : [batch size, num_rx, num_tx, num_paths], tf.float        Path delays [s]    """def__init__(self,a,tau,num_tx):# Copy to tensorflowself._a=tf.constant(a,tf.complex64)self._tau=tf.constant(tau,tf.float32)self._dataset_size=self._a.shape[0]self._num_tx=num_txdef__call__(self):# Generator implements an infinite loop that yields new random sampleswhileTrue:# Sample 4 random users and stack them togetheridx,_,_=tf.random.uniform_candidate_sampler(tf.expand_dims(tf.range(self._dataset_size,dtype=tf.int64),axis=0),num_true=self._dataset_size,num_sampled=self._num_tx,unique=True,range_max=self._dataset_size)a=tf.gather(self._a,idx)tau=tf.gather(self._tau,idx)# Transpose to remove batch dimensiona=tf.transpose(a,(3,1,2,0,4,5,6))tau=tf.transpose(tau,(2,1,0,3))# And remove batch-dimensiona=tf.squeeze(a,axis=0)tau=tf.squeeze(tau,axis=0)yielda,tau

We use Sionna’s built-inCIRDataset to initialize a channel model that can be directly used in Sionna’sOFDMChannel layer.

[10]:
batch_size=20# Must be the same for the BER simulations as CIRDataset returns fixed batch_size# Init CIR generatorcir_generator=CIRGenerator(a,tau,num_tx)# Initialises a channel model that can be directly used by OFDMChannel layerchannel_model=CIRDataset(cir_generator,batch_size,num_rx,num_rx_ant,num_tx,num_tx_ant,max_num_paths,num_time_steps)

PUSCH Link-Level Simulations

Let’s now define an end-to-end model that simulates a PUSCH transmission.

[11]:
classModel:"""Simulate PUSCH transmissions    This model runs BER simulations for a multi-user MIMO uplink channel    compliant with the 5G NR PUSCH specifications.    You can pick different scenarios, i.e., channel models, perfect or    estimated CSI, as well as different MIMO detectors (LMMSE or KBest).    Parameters    ----------    channel_model : :class:`~sionna.channel.ChannelModel` object        An instance of a :class:`~sionna.channel.ChannelModel` object, such as        :class:`~sionna.channel.RayleighBlockFading` or        :class:`~sionna.channel.tr38901.UMi` or        :class:`~sionna.channel.CIRDataset`.    perfect_csi : bool        Determines if perfect CSI is assumed or if the CSI is estimated    detector : str, one of ["lmmse", "kbest"]        MIMO detector to be used. Note that each detector has additional        parameters that can be configured in the source code of the _init_ call.    Input    -----    batch_size : int        Number of simultaneously simulated slots    ebno_db : float        Signal-to-noise-ratio    Output    ------    b : [batch_size, num_tx, tb_size], tf.float        Transmitted information bits    b_hat : [batch_size, num_tx, tb_size], tf.float        Decoded information bits    """def__init__(self,channel_model,perfect_csi,# booldetector,# "lmmse", "kbest"):super().__init__()self._channel_model=channel_modelself._perfect_csi=perfect_csi# System configurationself._num_prb=16self._mcs_index=14self._num_layers=1self._mcs_table=1self._domain="freq"# Below parameters must equal the Path2CIR parametersself._num_tx_ant=4self._num_tx=4self._subcarrier_spacing=30e3# must be the same as used for Path2CIR# PUSCHConfig for the first transmitterpusch_config=PUSCHConfig()pusch_config.carrier.subcarrier_spacing=self._subcarrier_spacing/1000pusch_config.carrier.n_size_grid=self._num_prbpusch_config.num_antenna_ports=self._num_tx_antpusch_config.num_layers=self._num_layerspusch_config.precoding="codebook"pusch_config.tpmi=1pusch_config.dmrs.dmrs_port_set=list(range(self._num_layers))pusch_config.dmrs.config_type=1pusch_config.dmrs.length=1pusch_config.dmrs.additional_position=1pusch_config.dmrs.num_cdm_groups_without_data=2pusch_config.tb.mcs_index=self._mcs_indexpusch_config.tb.mcs_table=self._mcs_table# Create PUSCHConfigs for the other transmitters by cloning of the first PUSCHConfig# and modifying the used DMRS ports.pusch_configs=[pusch_config]foriinrange(1,self._num_tx):pc=pusch_config.clone()pc.dmrs.dmrs_port_set=list(range(i*self._num_layers,(i+1)*self._num_layers))pusch_configs.append(pc)# Create PUSCHTransmitterself._pusch_transmitter=PUSCHTransmitter(pusch_configs,output_domain=self._domain)# Create PUSCHReceiverrx_tx_association=np.ones([1,self._num_tx],bool)stream_management=StreamManagement(rx_tx_association,self._num_layers)assertdetectorin["lmmse","kbest"],"Unsupported MIMO detector"ifdetector=="lmmse":detector=LinearDetector(equalizer="lmmse",output="bit",demapping_method="maxlog",resource_grid=self._pusch_transmitter.resource_grid,stream_management=stream_management,constellation_type="qam",num_bits_per_symbol=pusch_config.tb.num_bits_per_symbol)elifdetector=="kbest":detector=KBestDetector(output="bit",num_streams=self._num_tx*self._num_layers,k=64,resource_grid=self._pusch_transmitter.resource_grid,stream_management=stream_management,constellation_type="qam",num_bits_per_symbol=pusch_config.tb.num_bits_per_symbol)ifself._perfect_csi:self._pusch_receiver=PUSCHReceiver(self._pusch_transmitter,mimo_detector=detector,input_domain=self._domain,channel_estimator="perfect")else:self._pusch_receiver=PUSCHReceiver(self._pusch_transmitter,mimo_detector=detector,input_domain=self._domain)# Configure the actual channelself._channel=OFDMChannel(self._channel_model,self._pusch_transmitter.resource_grid,normalize_channel=True,return_channel=True)# XLA currently not supported by the CIRDataset function@tf.function(jit_compile=False)def__call__(self,batch_size,ebno_db):x,b=self._pusch_transmitter(batch_size)no=ebnodb2no(ebno_db,self._pusch_transmitter._num_bits_per_symbol,self._pusch_transmitter._target_coderate,self._pusch_transmitter.resource_grid)y,h=self._channel(x,no)ifself._perfect_csi:b_hat=self._pusch_receiver(y,no,h)else:b_hat=self._pusch_receiver(y,no)returnb,b_hat

We now initialize the end-to-end model that uses theCIRDataset.

[12]:
ebno_db=10.e2e_model=Model(channel_model,perfect_csi=False,# booldetector="lmmse")# "lmmse", "kbest"# We can draw samples from the end-2-end link-level simulationsb,b_hat=e2e_model(batch_size,ebno_db)

Now, let’s run the BER evaluation for different system configurations.

Remark: Running the cell below can take some time.

[13]:
ebno_db=np.arange(-3,18,2)# sim SNR rangeber_plot=PlotBER(f"Site-Specific MU-MIMO 5G NR PUSCH")fordetectorin["lmmse","kbest"]:forperf_csiin[True,False]:e2e_model=Model(channel_model,perfect_csi=perf_csi,detector=detector)# define legendcsi="Perf. CSI"ifperf_csielse"Imperf. CSI"det="K-Best"ifdetector=="kbest"else"LMMSE"l=det+" "+csiber_plot.simulate(e2e_model,ebno_dbs=ebno_db,# SNR to simulatelegend=l,# legend string for plottingmax_mc_iter=500,num_target_block_errors=2000,batch_size=batch_size,# batch-size per Monte Carlo runsoft_estimates=False,# the model returns hard-estimatesearly_stop=True,show_fig=False,add_bler=True,forward_keyboard_interrupt=True);
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status---------------------------------------------------------------------------------------------------------------------------------------     -3.0 | 1.3210e-01 | 9.5185e-01 |     1424408 |    10782720 |         2056 |        2160 |        23.8 |reached target block errors     -1.0 | 6.5100e-02 | 5.8488e-01 |     1117934 |    17172480 |         2012 |        3440 |         5.5 |reached target block errors      1.0 | 3.2190e-02 | 2.7486e-01 |     1169833 |    36341760 |         2001 |        7280 |        11.6 |reached target block errors      3.0 | 1.8199e-02 | 1.5847e-01 |     1148305 |    63098880 |         2003 |       12640 |        20.1 |reached target block errors      5.0 | 9.6161e-03 | 8.6979e-02 |     1106003 |   115015680 |         2004 |       23040 |        36.5 |reached target block errors      7.0 | 4.7417e-03 | 4.3675e-02 |      946815 |   199680000 |         1747 |       40000 |        63.4 |reached max iterations      9.0 | 2.7623e-03 | 2.4625e-02 |      551580 |   199680000 |          985 |       40000 |        63.3 |reached max iterations     11.0 | 1.2288e-03 | 1.1350e-02 |      245357 |   199680000 |          454 |       40000 |        63.2 |reached max iterations     13.0 | 7.0918e-04 | 6.4000e-03 |      141609 |   199680000 |          256 |       40000 |        63.3 |reached max iterations     15.0 | 4.6758e-04 | 3.9750e-03 |       93367 |   199680000 |          159 |       40000 |        63.3 |reached max iterations     17.0 | 2.0871e-04 | 1.6000e-03 |       41675 |   199680000 |           64 |       40000 |        63.3 |reached max iterationsEbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status---------------------------------------------------------------------------------------------------------------------------------------     -3.0 | 2.3236e-01 | 1.0000e+00 |     2319874 |     9984000 |         2000 |        2000 |        23.0 |reached target block errors     -1.0 | 1.8175e-01 | 9.9952e-01 |     1887149 |    10383360 |         2079 |        2080 |         3.3 |reached target block errors      1.0 | 1.2062e-01 | 9.2593e-01 |     1300640 |    10782720 |         2000 |        2160 |         3.4 |reached target block errors      3.0 | 6.7142e-02 | 5.3245e-01 |     1260249 |    18769920 |         2002 |        3760 |         5.9 |reached target block errors      5.0 | 3.7267e-02 | 2.8594e-01 |     1309694 |    35143680 |         2013 |        7040 |        11.1 |reached target block errors      7.0 | 2.1437e-02 | 1.6896e-01 |     1275606 |    59504640 |         2014 |       11920 |        18.9 |reached target block errors      9.0 | 1.0997e-02 | 8.9235e-02 |     1234137 |   112220160 |         2006 |       22480 |        35.6 |reached target block errors     11.0 | 6.3717e-03 | 5.3613e-02 |     1188324 |   186501120 |         2003 |       37360 |        59.1 |reached target block errors     13.0 | 3.1225e-03 | 2.6450e-02 |      623502 |   199680000 |         1058 |       40000 |        63.5 |reached max iterations     15.0 | 1.7128e-03 | 1.3050e-02 |      342016 |   199680000 |          522 |       40000 |        63.5 |reached max iterations     17.0 | 9.6857e-04 | 7.4750e-03 |      193404 |   199680000 |          299 |       40000 |        63.4 |reached max iterationsEbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status---------------------------------------------------------------------------------------------------------------------------------------     -3.0 | 1.4725e-01 | 9.9904e-01 |     1528976 |    10383360 |         2078 |        2080 |        52.4 |reached target block errors     -1.0 | 7.6806e-02 | 8.5125e-01 |      920195 |    11980800 |         2043 |        2400 |        33.2 |reached target block errors      1.0 | 3.3255e-02 | 3.3273e-01 |     1009341 |    30351360 |         2023 |        6080 |        83.7 |reached target block errors      3.0 | 1.5603e-02 | 1.4101e-01 |     1109188 |    71086080 |         2008 |       14240 |       196.6 |reached target block errors      5.0 | 6.1264e-03 | 5.6948e-02 |     1074081 |   175319040 |         2000 |       35120 |       484.8 |reached target block errors      7.0 | 1.8097e-03 | 1.9000e-02 |      361368 |   199680000 |          760 |       40000 |       552.2 |reached max iterations      9.0 | 4.2337e-04 | 4.6250e-03 |       84538 |   199680000 |          185 |       40000 |       552.6 |reached max iterations     11.0 | 1.4656e-04 | 1.3750e-03 |       29265 |   199680000 |           55 |       40000 |       552.5 |reached max iterations     13.0 | 1.0936e-04 | 6.7500e-04 |       21838 |   199680000 |           27 |       40000 |       553.1 |reached max iterations     15.0 | 4.9920e-05 | 3.2500e-04 |        9968 |   199680000 |           13 |       40000 |       552.6 |reached max iterations     17.0 | 5.0691e-05 | 2.0000e-04 |       10122 |   199680000 |            8 |       40000 |       552.9 |reached max iterationsEbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status---------------------------------------------------------------------------------------------------------------------------------------     -3.0 | 2.4172e-01 | 1.0000e+00 |     2413315 |     9984000 |         2000 |        2000 |        48.9 |reached target block errors     -1.0 | 1.9193e-01 | 1.0000e+00 |     1916234 |     9984000 |         2000 |        2000 |        27.7 |reached target block errors      1.0 | 1.4189e-01 | 9.8317e-01 |     1473333 |    10383360 |         2045 |        2080 |        28.8 |reached target block errors      3.0 | 7.5092e-02 | 7.0521e-01 |     1079597 |    14376960 |         2031 |        2880 |        40.0 |reached target block errors      5.0 | 3.4595e-02 | 2.9412e-01 |     1174339 |    33945600 |         2000 |        6800 |        94.0 |reached target block errors      7.0 | 1.7845e-02 | 1.4357e-01 |     1247159 |    69888000 |         2010 |       14000 |       193.9 |reached target block errors      9.0 | 7.9984e-03 | 6.4832e-02 |     1232980 |   154152960 |         2002 |       30880 |       427.4 |reached target block errors     11.0 | 2.7478e-03 | 2.4400e-02 |      548684 |   199680000 |          976 |       40000 |       554.0 |reached max iterations     13.0 | 8.9915e-04 | 7.7500e-03 |      179543 |   199680000 |          310 |       40000 |       554.0 |reached max iterations     15.0 | 2.8446e-04 | 2.1000e-03 |       56801 |   199680000 |           84 |       40000 |       554.4 |reached max iterations     17.0 | 1.0446e-04 | 7.2500e-04 |       20858 |   199680000 |           29 |       40000 |       554.0 |reached max iterations
[14]:
ber_plot(show_ber=False)
../../_images/phy_tutorials_Link_Level_Simulations_with_RT_31_0.png