OFDM MIMO Channel Estimation and Detection
In this notebook, we will evaluate some of the OFDM channel estimation and MIMO detection algorithms available in Sionna PHY.
We will start by evaluating the mean square error (MSE) preformance of various channel estimation and interpolation methods.
Then, we will compare some of the MIMO detection algorithms under both perfect and imperfect channel state information (CSI) in terms of uncoded symbol error rate (SER) and coded bit error rate (BER).
The developed end-to-end models in this notebook are a great tool for benchmarking of MIMO receivers under realistic conditions. They can be easily extended to new channel estimation methods or MIMO detection algorithms.
For MSE evaluations, the block diagram of the system looks as follows:
where the channel estimation module is highlighted as it is the focus of this evaluation. The channel covariance matrices are required for linear minimum mean square error (LMMSE) channel interpolation.
For uncoded SER evaluations, the block diagram of the system looks as follows:
where the channel estimation and detection modules are highlighted as they are the focus of this evaluation.
Finally, for coded BER evaluations, the block diagram of the system looks as follows:
Table of Contents
GPU Configuration and Imports
[1]:
importosifos.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 Sionnatry:importsionna.phyexceptImportErrorase: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')sionna.phy.config.seed=42# Set seed for reproducible random number generation
[2]:
%matplotlib inlineimportmatplotlib.pyplotaspltimportnumpyasnpfromsionna.phyimportBlockfromsionna.phy.mimoimportStreamManagementfromsionna.phy.utilsimportsim_ber,ebnodb2nofromsionna.phy.mappingimportMapper,QAMSource,BinarySourcefromsionna.phy.ofdmimportResourceGrid,ResourceGridMapper,LSChannelEstimator, \LMMSEInterpolator,LinearDetector,KBestDetector, \EPDetector,MMSEPICDetectorfromsionna.phy.channelimportGenerateOFDMChannel,OFDMChannel,gen_single_sector_topologyfromsionna.phy.channel.tr38901importUMi,Antenna,PanelArrayfromsionna.phy.fec.ldpcimportLDPC5GEncoder,LDPC5GDecoder
Simulation parameters
The next cell defines the simulation parameters used throughout this notebook.
This includes the OFDM waveform parameters,antennas geometries and patterns, and the3GPP UMi channel model.
[3]:
NUM_OFDM_SYMBOLS=14FFT_SIZE=12*4# 4 PRBsSUBCARRIER_SPACING=30e3# HzCARRIER_FREQUENCY=3.5e9# HzSPEED=3.# m/s# The user terminals (UTs) are equipped with a single antenna# with vertial polarization.UT_ANTENNA=Antenna(polarization='single',polarization_type='V',antenna_pattern='omni',# Omnidirectional antenna patterncarrier_frequency=CARRIER_FREQUENCY)# The base station is equipped with an antenna# array of 8 cross-polarized antennas,# resulting in a total of 16 antenna elements.NUM_RX_ANT=16BS_ARRAY=PanelArray(num_rows_per_panel=4,num_cols_per_panel=2,polarization='dual',polarization_type='cross',antenna_pattern='38.901',# 3GPP 38.901 antenna patterncarrier_frequency=CARRIER_FREQUENCY)# 3GPP UMi channel model is consideredCHANNEL_MODEL=UMi(carrier_frequency=CARRIER_FREQUENCY,o2i_model='low',ut_array=UT_ANTENNA,bs_array=BS_ARRAY,direction='uplink',enable_shadow_fading=False,enable_pathloss=False)
Estimation of the channel time, frequency, and spatial covariance matrices
The linear minimum mean square (LMMSE) interpolation method requires knowledge of the time (i.e., across OFDM symbols), frequency (i.e., across sub-carriers), and spatial (i.e., across receive antennas) covariance matrices of the channel frequency response.
These are estimated in this section using Monte Carlo sampling.
We explain below how this is achieved for the frequency covariance matrix. The same approach is used for the time and spatial covariance matrices.
Let\(N\) be the number of sub-carriers. The first step for estimating the frequency covariance matrix is to sample the channel model in order to build a set of frequency-domain channel realizations\(\left\{ \mathbf{h}_k \right\}, 1 \leq k \leq K\), where\(K\) is the number of samples and\(\mathbf{h}_k \in \mathbb{C}^{N}\) are complex-valued samples of the channel frequency response.
The frequency covariance matrix\(\mathbf{R}^{(f)} \in \mathbb{C}^{N \times N}\) is then estimated by
\begin{equation}\mathbf{R}^{(f)} \approx \frac{1}{K} \sum_{k = 1}^K \mathbf{h}_k \mathbf{h}_k^{\mathrm{H}}\end{equation}
where we assume that the frequency-domain channel response has zero mean.
The following cells implement this process for all three dimensions (frequency, time, and space).
The next cell defines aresource grid and anOFDM channel generator for sampling the channel in the frequency domain.
[4]:
rg=ResourceGrid(num_ofdm_symbols=NUM_OFDM_SYMBOLS,fft_size=FFT_SIZE,subcarrier_spacing=SUBCARRIER_SPACING)channel_sampler=GenerateOFDMChannel(CHANNEL_MODEL,rg)
Then, a function that samples the channel is defined. It randomly samples a network topology for every batch and for every batch example using theappropriate utility function.
[5]:
defsample_channel(batch_size):# Sample random topologiestopology=gen_single_sector_topology(batch_size,1,'umi',min_ut_velocity=SPEED,max_ut_velocity=SPEED)CHANNEL_MODEL.set_topology(*topology)# Sample channel frequency responses# [batch size, 1, num_rx_ant, 1, 1, num_ofdm_symbols, fft_size]h_freq=channel_sampler(batch_size)# [batch size, num_rx_ant, num_ofdm_symbols, fft_size]h_freq=h_freq[:,0,:,0,0]returnh_freq
We now define a function that estimates the frequency, time, and spatial covariance matrcies using Monte Carlo sampling.
[6]:
@tf.function(jit_compile=True)# Use XLA for speed-updefestimate_covariance_matrices(num_it,batch_size):freq_cov_mat=tf.zeros([FFT_SIZE,FFT_SIZE],tf.complex64)time_cov_mat=tf.zeros([NUM_OFDM_SYMBOLS,NUM_OFDM_SYMBOLS],tf.complex64)space_cov_mat=tf.zeros([NUM_RX_ANT,NUM_RX_ANT],tf.complex64)for_intf.range(num_it):# [batch size, num_rx_ant, num_ofdm_symbols, fft_size]h_samples=sample_channel(batch_size)################################## Estimate frequency covariance################################## [batch size, num_rx_ant, fft_size, num_ofdm_symbols]h_samples_=tf.transpose(h_samples,[0,1,3,2])# [batch size, num_rx_ant, fft_size, fft_size]freq_cov_mat_=tf.matmul(h_samples_,h_samples_,adjoint_b=True)# [fft_size, fft_size]freq_cov_mat_=tf.reduce_mean(freq_cov_mat_,axis=(0,1))# [fft_size, fft_size]freq_cov_mat+=freq_cov_mat_################################# Estimate time covariance################################# [batch size, num_rx_ant, num_ofdm_symbols, fft_size]time_cov_mat_=tf.matmul(h_samples,h_samples,adjoint_b=True)# [num_ofdm_symbols, num_ofdm_symbols]time_cov_mat_=tf.reduce_mean(time_cov_mat_,axis=(0,1))# [num_ofdm_symbols, num_ofdm_symbols]time_cov_mat+=time_cov_mat_################################ Estimate spatial covariance################################ [batch size, num_ofdm_symbols, num_rx_ant, fft_size]h_samples_=tf.transpose(h_samples,[0,2,1,3])# [batch size, num_ofdm_symbols, num_rx_ant, num_rx_ant]space_cov_mat_=tf.matmul(h_samples_,h_samples_,adjoint_b=True)# [num_rx_ant, num_rx_ant]space_cov_mat_=tf.reduce_mean(space_cov_mat_,axis=(0,1))# [num_rx_ant, num_rx_ant]space_cov_mat+=space_cov_mat_freq_cov_mat/=tf.complex(tf.cast(NUM_OFDM_SYMBOLS*num_it,tf.float32),0.0)time_cov_mat/=tf.complex(tf.cast(FFT_SIZE*num_it,tf.float32),0.0)space_cov_mat/=tf.complex(tf.cast(FFT_SIZE*num_it,tf.float32),0.0)returnfreq_cov_mat,time_cov_mat,space_cov_mat
We then compute the estimates by executing the function defined in the previous cell.
The batch size and number of iterations determine the total number of samples, i.e.,
number of samples = batch_size x num_iterations
and hence control the tradeoff between the accuracy of the estimates and the time needed for their computation.
[7]:
batch_size=1000num_iterations=100FREQ_COV_MAT,TIME_COV_MAT,SPACE_COV_MAT=estimate_covariance_matrices(batch_size,num_iterations)
Finally, the estimated matrices are saved (as numpy arrays) for future use.
[8]:
# FREQ_COV_MAT : [fft_size, fft_size]# TIME_COV_MAT : [num_ofdm_symbols, num_ofdm_symbols]# SPACE_COV_MAT : [num_rx_ant, num_rx_ant]np.save('freq_cov_mat',FREQ_COV_MAT.numpy())np.save('time_cov_mat',TIME_COV_MAT.numpy())np.save('space_cov_mat',SPACE_COV_MAT.numpy())
Loading the channel covariance matrices
The next cell loads saved estimates of the time, frequency, and space covariance matrices.
[9]:
FREQ_COV_MAT=np.load('freq_cov_mat.npy')TIME_COV_MAT=np.load('time_cov_mat.npy')SPACE_COV_MAT=np.load('space_cov_mat.npy')
We then visualize the loaded matrices.
As one can see, the frequency correlation slowly decays with increasing spectral distance.
The time-correlation is much stronger as the mobility low. The covariance matrix is hence very badly conditioned with rank almost equal to one.
The spatial covariance matrix has a regular structure which is determined by the array geometry and polarization of its elements.
[10]:
fig,ax=plt.subplots(3,2,figsize=(10,12))fig.suptitle("Time and frequency channel covariance matrices")ax[0,0].set_title("Freq. cov. Real")im=ax[0,0].imshow(FREQ_COV_MAT.real,vmin=-0.3,vmax=1.8)ax[0,1].set_title("Freq. cov. Imag")im=ax[0,1].imshow(FREQ_COV_MAT.imag,vmin=-0.3,vmax=1.8)ax[1,0].set_title("Time cov. Real")im=ax[1,0].imshow(TIME_COV_MAT.real,vmin=-0.3,vmax=1.8)ax[1,1].set_title("Time cov. Imag")im=ax[1,1].imshow(TIME_COV_MAT.imag,vmin=-0.3,vmax=1.8)ax[2,0].set_title("Space cov. Real")im=ax[2,0].imshow(SPACE_COV_MAT.real,vmin=-0.3,vmax=1.8)ax[2,1].set_title("Space cov. Imag")im=ax[2,1].imshow(SPACE_COV_MAT.imag,vmin=-0.3,vmax=1.8)fig.subplots_adjust(right=0.8)cbar_ax=fig.add_axes([0.85,0.15,0.05,0.7])fig.colorbar(im,cax=cbar_ax);

Comparison of OFDM estimators
This section focuses on comparing the available OFDM channel estimators in Sionna for the considered setup.
OFDM channel estimation consists of two steps:
Channel estimation at pilot-carrying resource elements usingleast-squares (LS).
Interpolation for data-carrying resource elements, for which three methods are available in Sionna:
Nearest-neighbor, which uses the channel estimate of the nearest pilot
Linear, with optional averaging over the OFDM symbols (time dimension) for low mobility scenarios
LMMSE, which requires knowledge of the time and frequency covariance matrices
The LMMSE interpolator also features optional spatial smoothin, which requires the spatial covarance matrix. TheAPI documentation explains in more detail how this interpolator operates.
End-to-end model
In the next cell, we will create a Sionna Block which uses the interpolation method specified at initialization.
It computes the mean square error (MSE) for a specified batch size and signal-to-noise ratio (SNR) (in dB).
The following interpolation methods are available (set through theint_method parameter):
"nn": Nearest-neighbor interpolation"lin": Linear interpolation"lmmse": LMMSE interpolation
When LMMSE interpolation is used, it is required to specified the order in which interpolation and optional spatial smoothing is performed. This is achieved using thelmmse_order parameter. For example, setting this parameter to"f-t" leads to frequency interpolation being performed first followed by time interpolation, and no spatial smoothing. Setting it to"t-f-s" leads to time interpolation being performed first, followed by frequency interpolation, and finally spatial smoothing.
[11]:
classMIMOOFDMLink(Block):def__init__(self,int_method,lmmse_order=None,**kwargs):super().__init__(kwargs)assertint_methodin('nn','lin','lmmse')# Configure the resource gridrg=ResourceGrid(num_ofdm_symbols=NUM_OFDM_SYMBOLS,fft_size=FFT_SIZE,subcarrier_spacing=SUBCARRIER_SPACING,num_tx=1,pilot_pattern="kronecker",pilot_ofdm_symbol_indices=[2,11])self.rg=rg# Stream management# Only a sinlge UT is considered for channel estimationsm=StreamManagement([[1]],1)################################### Transmitter##################################self.qam_source=QAMSource(num_bits_per_symbol=2)# Modulation order does not impact the channel estimation. Set to QPSKself.rg_mapper=ResourceGridMapper(rg)################################### Channel##################################self.channel=OFDMChannel(CHANNEL_MODEL,rg,return_channel=True)#################################### Receiver#################################### Channel estimationfreq_cov_mat=tf.constant(FREQ_COV_MAT,tf.complex64)time_cov_mat=tf.constant(TIME_COV_MAT,tf.complex64)space_cov_mat=tf.constant(SPACE_COV_MAT,tf.complex64)ifint_method=='nn':self.channel_estimator=LSChannelEstimator(rg,interpolation_type='nn')elifint_method=='lin':self.channel_estimator=LSChannelEstimator(rg,interpolation_type='lin')elifint_method=='lmmse':lmmse_int_freq_first=LMMSEInterpolator(rg.pilot_pattern,time_cov_mat,freq_cov_mat,space_cov_mat,order=lmmse_order)self.channel_estimator=LSChannelEstimator(rg,interpolator=lmmse_int_freq_first)@tf.functiondefcall(self,batch_size,snr_db):################################### Transmitter##################################x=self.qam_source([batch_size,1,1,self.rg.num_data_symbols])x_rg=self.rg_mapper(x)################################### Channel##################################no=tf.pow(10.0,-snr_db/10.0)topology=gen_single_sector_topology(batch_size,1,'umi',min_ut_velocity=SPEED,max_ut_velocity=SPEED)CHANNEL_MODEL.set_topology(*topology)y_rg,h_freq=self.channel(x_rg,no)#################################### Channel estimation###################################h_hat,_=self.channel_estimator(y_rg,no)#################################### MSE###################################mse=tf.reduce_mean(tf.square(tf.abs(h_freq-h_hat)))returnmse
The next cell defines a function for evaluating the mean square error (MSE) of amodel over a range of SNRs (snr_dbs).
Thebatch_size andnum_it parameters control the number of samples used to compute the MSE for each SNR value.
[12]:
defevaluate_mse(model,snr_dbs,batch_size,num_it):# Casting model inputs to TensorFlow types to avoid# re-building of the graphsnr_dbs=tf.cast(snr_dbs,tf.float32)batch_size=tf.cast(batch_size,tf.int32)mses=[]forsnr_dbinsnr_dbs:mse_=0.0for_inrange(num_it):mse_+=model(batch_size,snr_db).numpy()# Averaging over the number of iterationsmse_/=float(num_it)mses.append(mse_)returnmses
The next cell defines the evaluation parameters.
[13]:
# Range of SNR (in dB)SNR_DBs=np.linspace(-10.0,20.0,20)# Number of iterations and batch size.# These parameters control the number of samples used to compute each SNR value.# The higher the number of samples is, the more accurate the MSE estimation is, at# the cost of longer compute time.BATCH_SIZE=512NUM_IT=10# Interpolation/filtering order for the LMMSE interpolator.# All valid configurations are listed.# Some are commented to speed-up simulations.# Uncomment configurations to evaluate them!ORDERS=['s-t-f',# Space - time - frequency#'s-f-t', # Space - frequency - time#'t-s-f', # Time - space - frequency't-f-s',# Time - frequency - space#'f-t-s', # Frequency - time - space#'f-s-t', # Frequency - space- time#'f-t', # Frequency - time (no spatial smoothing)'t-f'# Time - frequency (no spatial smoothing)]
The next cell evaluates the nearest-neighbor, linear, and LMMSE interpolator. For the LMMSE interpolator, we loop through the configuration listed inORDERS.
[14]:
MSES={}# Nearest-neighbor interpolatione2e=MIMOOFDMLink("nn")MSES['nn']=evaluate_mse(e2e,SNR_DBs,BATCH_SIZE,NUM_IT)# Linear interpolatione2e=MIMOOFDMLink("lin")MSES['lin']=evaluate_mse(e2e,SNR_DBs,BATCH_SIZE,NUM_IT)# LMMSEfororderinORDERS:e2e=MIMOOFDMLink("lmmse",order)MSES[f"lmmse:{order}"]=evaluate_mse(e2e,SNR_DBs,BATCH_SIZE,NUM_IT)
Finally, we plot the MSE.
[15]:
plt.figure(figsize=(8,6))forest_labelinMSES:plt.semilogy(SNR_DBs,MSES[est_label],label=est_label)plt.xlabel(r"SNR (dB)")plt.ylabel("MSE")plt.legend()plt.grid(True)

Unsurprisingly, the LMMSE interpolator leads to more accurate estimates compared to the two other methods, as it leverages knowledge of the the channel statistics. Moreover, the order in which the LMMSE interpolation steps are performed strongly impacts the accuracy of the estimator. This is because the LMMSE interpolation operates in one dimension at a time which is not equivalent to full-blown LMMSE estimation across all dimensions at one.
Also note that the order that leads to the best accuracy depends on the channel statistics. As a rule of thumb, it might be good to start with the dimension that is most strongly correlated (i.e., time in our example).
Comparison of MIMO detectors
An OFDM MIMO receiver consists of two stages:OFDM channel estimation andMIMO detection.
While the previous section focused on OFDM channel estimation, this section focuses now on MIMO detection.
The following MIMO detection algorithms, all available out-of-the-box in Sionna, are considered:
Both perfect and imperfect channel state information is considered in the simulations. LS estimation combined with LMMSE interpolation is used, with time-frequency-space smoothing (in this order, i.e.,order='t-f-s').
End-to-end model
An end-to-end model is created in the next cell as a Sionna Block, which uses the detection method specified at initialization.
It computes either the coded bit error rate (BER) or the uncoded symbol error rate (SER), for a specified batch size,\(E_b/N_0\) (in dB), and QAM modulation with a specified modulation order. When computing the BER, a 5G LDPC code is used with the specified coderate.
The following MIMO detection methods are considered (set through thedet_param parameter):
"lmmse": No parameter needed"k-best": List sizek, defaults to 64"ep": Number of iterationsl, defaults to 10"mmse-pic": Number of self-iterationsnum_it, defaults to 4
Thedet_param parameter corresponds to eitherk,l, ornum_it, for K-Best, EP, or MMSE-PIC, respectively. If set toNone, a default value is used according to the selected detector.
Theperf_csi parameter controls whether perfect CSI is assumed or not. If set toFalse, then LS combined with LMMSE interpolation is used to estimate the channel.
You can easily add your own MIMO detector and channel estimator to this model for a fair and realistic benchmark.
[16]:
classMIMOOFDMLink(Block):def__init__(self,output,det_method,perf_csi,num_tx,num_bits_per_symbol,det_param=None,coderate=0.5,**kwargs):super().__init__(kwargs)assertdet_methodin('lmmse','k-best','ep','mmse-pic'),"Unknown detection method"self._output=outputself.num_tx=num_txself.num_bits_per_symbol=num_bits_per_symbolself.coderate=coderateself.det_method=det_methodself.perf_csi=perf_csi# Configure the resource gridrg=ResourceGrid(num_ofdm_symbols=NUM_OFDM_SYMBOLS,fft_size=FFT_SIZE,subcarrier_spacing=SUBCARRIER_SPACING,num_tx=num_tx,pilot_pattern="kronecker",pilot_ofdm_symbol_indices=[2,11])self.rg=rg# Stream managementsm=StreamManagement(np.ones([1,num_tx],int),1)# Codeword length and number of information bits per codewordn=int(rg.num_data_symbols*num_bits_per_symbol)k=int(coderate*n)self.n=nself.k=k# If output is symbol, then no FEC is used and hard decision are outputhard_out=(output=="symbol")coded=(output=="bit")self.hard_out=hard_outself.coded=coded################################### Transmitter##################################self.binary_source=BinarySource()self.mapper=Mapper(constellation_type="qam",num_bits_per_symbol=num_bits_per_symbol,return_indices=True)self.rg_mapper=ResourceGridMapper(rg)ifcoded:self.encoder=LDPC5GEncoder(k,n,num_bits_per_symbol=num_bits_per_symbol)################################### Channel##################################self.channel=OFDMChannel(CHANNEL_MODEL,rg,return_channel=True)#################################### Receiver#################################### Channel estimationifnotself.perf_csi:freq_cov_mat=tf.constant(FREQ_COV_MAT,tf.complex64)time_cov_mat=tf.constant(TIME_COV_MAT,tf.complex64)space_cov_mat=tf.constant(SPACE_COV_MAT,tf.complex64)lmmse_int_time_first=LMMSEInterpolator(rg.pilot_pattern,time_cov_mat,freq_cov_mat,space_cov_mat,order='t-f-s')self.channel_estimator=LSChannelEstimator(rg,interpolator=lmmse_int_time_first)# Detectionifdet_method=="lmmse":self.detector=LinearDetector("lmmse",output,"app",rg,sm,constellation_type="qam",num_bits_per_symbol=num_bits_per_symbol,hard_out=hard_out)elifdet_method=='k-best':ifdet_paramisNone:k=64else:k=det_paramself.detector=KBestDetector(output,num_tx,k,rg,sm,constellation_type="qam",num_bits_per_symbol=num_bits_per_symbol,hard_out=hard_out)elifdet_method=="ep":ifdet_paramisNone:l=10else:l=det_paramself.detector=EPDetector(output,rg,sm,num_bits_per_symbol,l=l,hard_out=hard_out)elifdet_method=='mmse-pic':ifdet_paramisNone:l=4else:l=det_paramself.detector=MMSEPICDetector(output,'app',rg,sm,num_iter=l,constellation_type="qam",num_bits_per_symbol=num_bits_per_symbol,hard_out=hard_out)ifcoded:self.decoder=LDPC5GDecoder(self.encoder,hard_out=False)@tf.functiondefcall(self,batch_size,ebno_db):################################### Transmitter##################################ifself.coded:b=self.binary_source([batch_size,self.num_tx,1,self.k])c=self.encoder(b)else:c=self.binary_source([batch_size,self.num_tx,1,self.n])bits_shape=tf.shape(c)x,x_ind=self.mapper(c)x_rg=self.rg_mapper(x)################################### Channel##################################no=ebnodb2no(ebno_db,self.num_bits_per_symbol,self.coderate,resource_grid=self.rg)topology=gen_single_sector_topology(batch_size,self.num_tx,'umi',min_ut_velocity=SPEED,max_ut_velocity=SPEED)CHANNEL_MODEL.set_topology(*topology)y_rg,h_freq=self.channel(x_rg,no)#################################### Receiver#################################### Channel estimationifself.perf_csi:h_hat=h_freqerr_var=0.0else:h_hat,err_var=self.channel_estimator(y_rg,no)# Detectionifself.det_method=="mmse-pic":ifself._output=="bit":prior_shape=bits_shapeelifself._output=="symbol":prior_shape=tf.concat([tf.shape(x),[self.num_bits_per_symbol]],axis=0)prior=tf.zeros(prior_shape)det_out=self.detector(y_rg,h_hat,prior,err_var,no)else:det_out=self.detector(y_rg,h_hat,err_var,no)# (Decoding) and outputifself._output=="bit":llr=tf.reshape(det_out,bits_shape)b_hat=self.decoder(llr)returnb,b_hatelifself._output=="symbol":x_hat=tf.reshape(det_out,tf.shape(x_ind))returnx_ind,x_hat
The following function is used to evaluate all of the considered detectors for a given setup: It instantiates the end-to-end systems, runs the simulations, and returns the BER or SER.
[17]:
defrun_sim(num_tx,num_bits_per_symbol,output,ebno_dbs,perf_csi,det_param=None):lmmse=MIMOOFDMLink(output,"lmmse",perf_csi,num_tx,num_bits_per_symbol,det_param)k_best=MIMOOFDMLink(output,"k-best",perf_csi,num_tx,num_bits_per_symbol,det_param)ep=MIMOOFDMLink(output,"ep",perf_csi,num_tx,num_bits_per_symbol,det_param)mmse_pic=MIMOOFDMLink(output,"mmse-pic",perf_csi,num_tx,num_bits_per_symbol,det_param)ifoutput=="symbol":soft_estimates=Falseylabel="Uncoded SER"else:soft_estimates=Trueylabel="Coded BER"er_lmmse,_=sim_ber(lmmse,ebno_dbs,batch_size=64,max_mc_iter=200,num_target_block_errors=200,soft_estimates=soft_estimates);er_ep,_=sim_ber(ep,ebno_dbs,batch_size=64,max_mc_iter=200,num_target_block_errors=200,soft_estimates=soft_estimates);er_kbest,_=sim_ber(k_best,ebno_dbs,batch_size=64,max_mc_iter=200,num_target_block_errors=200,soft_estimates=soft_estimates);er_mmse_pic,_=sim_ber(mmse_pic,ebno_dbs,batch_size=64,max_mc_iter=200,num_target_block_errors=200,soft_estimates=soft_estimates);returner_lmmse,er_ep,er_kbest,er_mmse_pic
The next cell defines the simulation parameters.
[18]:
# Range of SNR (dB)EBN0_DBs=np.linspace(-10.,20.0,10)# Number of transmittersNUM_TX=4# Modulation order (number of bits per symbol)NUM_BITS_PER_SYMBOL=4# 16-QAM
We start by evaluating the uncoded SER. The next cell runs the simulations with perfect CSI and channel estimation. Results are stored in theSER dictionary.
[19]:
SER={}# Store the results# Perfect CSIser_lmmse,ser_ep,ser_kbest,ser_mmse_pic=run_sim(NUM_TX,NUM_BITS_PER_SYMBOL,"symbol",EBN0_DBs,True)SER['Perf. CSI / LMMSE']=ser_lmmseSER['Perf. CSI / EP']=ser_epSER['Perf. CSI / K-Best']=ser_kbestSER['Perf. CSI / MMSE-PIC']=ser_mmse_pic# Imperfect CSIser_lmmse,ser_ep,ser_kbest,ser_mmse_pic=run_sim(NUM_TX,NUM_BITS_PER_SYMBOL,"symbol",EBN0_DBs,False)SER['Ch. Est. / LMMSE']=ser_lmmseSER['Ch. Est. / EP']=ser_epSER['Ch. Est. / K-Best']=ser_kbestSER['Ch. Est. / MMSE-PIC']=ser_mmse_pic
EbNo [dB] | BER | BLER | bit errors | num bits | block errors | num blocks | runtime [s] | status--------------------------------------------------------------------------------------------------------------------------------------- -10.0 | 6.2838e-01 | 1.0000e+00 | 92658 | 147456 | 256 | 256 | 6.4 |reached target block errors -6.667 | 5.3079e-01 | 1.0000e+00 | 78268 | 147456 | 256 | 256 | 0.1 |reached target block errors -3.333 | 3.6810e-01 | 1.0000e+00 | 54278 | 147456 | 256 | 256 | 0.1 |reached target block errors 0.0 | 2.5828e-01 | 9.9219e-01 | 38085 | 147456 | 254 | 256 | 0.1 |reached target block errors 3.333 | 1.4242e-01 | 8.9844e-01 | 21001 | 147456 | 230 | 256 | 0.1 |reached target block errors 6.667 | 6.2571e-02 | 7.4023e-01 | 18453 | 294912 | 379 | 512 | 0.1 |reached target block errors 10.0 | 2.6004e-02 | 4.9219e-01 | 7669 | 294912 | 252 | 512 | 0.1 |reached target block errors 13.333 | 7.5005e-03 | 2.8776e-01 | 3318 | 442368 | 221 | 768 | 0.2 |reached target block errors 16.667 | 1.9531e-03 | 1.2165e-01 | 2016 | 1032192 | 218 | 1792 | 0.5 |reached target block errors 20.0 | 2.6747e-04 | 3.1250e-02 | 986 | 3686400 | 200 | 6400 | 1.7 |reached target block errorsEbNo [dB] | BER | BLER | bit errors | num bits | block errors | num blocks | runtime [s] | status--------------------------------------------------------------------------------------------------------------------------------------- -10.0 | 6.0986e-01 | 1.0000e+00 | 89927 | 147456 | 256 | 256 | 6.9 |reached target block errors -6.667 | 4.7891e-01 | 9.9609e-01 | 70618 | 147456 | 255 | 256 | 0.1 |reached target block errors -3.333 | 3.2503e-01 | 9.9219e-01 | 47927 | 147456 | 254 | 256 | 0.1 |reached target block errors 0.0 | 1.6372e-01 | 9.7266e-01 | 24141 | 147456 | 249 | 256 | 0.1 |reached target block errors 3.333 | 5.8302e-02 | 8.5938e-01 | 8597 | 147456 | 220 | 256 | 0.1 |reached target block errors 6.667 | 1.0718e-02 | 4.4922e-01 | 3161 | 294912 | 230 | 512 | 0.2 |reached target block errors 10.0 | 1.8365e-03 | 1.7891e-01 | 1354 | 737280 | 229 | 1280 | 0.5 |reached target block errors 13.333 | 2.5627e-04 | 2.4858e-02 | 1247 | 4866048 | 210 | 8448 | 3.0 |reached target block errors 16.667 | 3.8724e-05 | 4.6228e-03 | 965 | 24920064 | 200 | 43264 | 15.5 |reached target block errors 20.0 | 9.0535e-06 | 6.8359e-04 | 267 | 29491200 | 35 | 51200 | 18.3 |reached max iterationsEbNo [dB] | BER | BLER | bit errors | num bits | block errors | num blocks | runtime [s] | status--------------------------------------------------------------------------------------------------------------------------------------- -10.0 | 6.3044e-01 | 1.0000e+00 | 92962 | 147456 | 256 | 256 | 7.5 |reached target block errors -6.667 | 4.7668e-01 | 1.0000e+00 | 70289 | 147456 | 256 | 256 | 0.7 |reached target block errors -3.333 | 2.9218e-01 | 1.0000e+00 | 43083 | 147456 | 256 | 256 | 0.7 |reached target block errors 0.0 | 1.1167e-01 | 9.7266e-01 | 16466 | 147456 | 249 | 256 | 0.7 |reached target block errors 3.333 | 3.5139e-02 | 7.3633e-01 | 10363 | 294912 | 377 | 512 | 1.5 |reached target block errors 6.667 | 5.8345e-03 | 2.9036e-01 | 2581 | 442368 | 223 | 768 | 2.2 |reached target block errors 10.0 | 1.2373e-03 | 7.8835e-02 | 2007 | 1622016 | 222 | 2816 | 8.0 |reached target block errors 13.333 | 1.6483e-04 | 1.3440e-02 | 1434 | 8699904 | 203 | 15104 | 42.7 |reached target block errors 16.667 | 6.8393e-05 | 2.9492e-03 | 2017 | 29491200 | 151 | 51200 | 144.9 |reached max iterations 20.0 | 8.5449e-06 | 5.0781e-04 | 252 | 29491200 | 26 | 51200 | 144.4 |reached max iterationsEbNo [dB] | BER | BLER | bit errors | num bits | block errors | num blocks | runtime [s] | status--------------------------------------------------------------------------------------------------------------------------------------- -10.0 | 6.1721e-01 | 1.0000e+00 | 91011 | 147456 | 256 | 256 | 6.8 |reached target block errors -6.667 | 4.6697e-01 | 1.0000e+00 | 68858 | 147456 | 256 | 256 | 0.1 |reached target block errors -3.333 | 3.1925e-01 | 1.0000e+00 | 47076 | 147456 | 256 | 256 | 0.1 |reached target block errors 0.0 | 1.5159e-01 | 9.7266e-01 | 22353 | 147456 | 249 | 256 | 0.1 |reached target block errors 3.333 | 5.4552e-02 | 8.5156e-01 | 8044 | 147456 | 218 | 256 | 0.1 |reached target block errors 6.667 | 1.0474e-02 | 5.7031e-01 | 3089 | 294912 | 292 | 512 | 0.2 |reached target block errors 10.0 | 2.7172e-03 | 2.6042e-01 | 1202 | 442368 | 200 | 768 | 0.3 |reached target block errors 13.333 | 5.8209e-04 | 6.9336e-02 | 1030 | 1769472 | 213 | 3072 | 1.2 |reached target block errors 16.667 | 7.0752e-05 | 1.2010e-02 | 699 | 9879552 | 206 | 17152 | 6.5 |reached target block errors 20.0 | 1.5801e-05 | 1.7773e-03 | 466 | 29491200 | 91 | 51200 | 19.3 |reached max iterationsEbNo [dB] | BER | BLER | bit errors | num bits | block errors | num blocks | runtime [s] | status--------------------------------------------------------------------------------------------------------------------------------------- -10.0 | 6.4981e-01 | 1.0000e+00 | 95819 | 147456 | 256 | 256 | 6.6 |reached target block errors -6.667 | 5.4840e-01 | 1.0000e+00 | 80865 | 147456 | 256 | 256 | 0.8 |reached target block errors -3.333 | 4.1493e-01 | 1.0000e+00 | 61184 | 147456 | 256 | 256 | 0.8 |reached target block errors 0.0 | 2.8806e-01 | 9.8438e-01 | 42476 | 147456 | 252 | 256 | 0.8 |reached target block errors 3.333 | 1.4888e-01 | 9.2578e-01 | 21953 | 147456 | 237 | 256 | 0.8 |reached target block errors 6.667 | 1.0667e-01 | 9.0234e-01 | 15729 | 147456 | 231 | 256 | 0.8 |reached target block errors 10.0 | 4.7740e-02 | 6.5039e-01 | 14079 | 294912 | 333 | 512 | 1.6 |reached target block errors 13.333 | 2.2447e-02 | 4.6484e-01 | 6620 | 294912 | 238 | 512 | 1.6 |reached target block errors 16.667 | 7.3197e-03 | 2.7083e-01 | 3238 | 442368 | 208 | 768 | 2.4 |reached target block errors 20.0 | 1.8876e-03 | 1.3411e-01 | 1670 | 884736 | 206 | 1536 | 4.8 |reached target block errorsEbNo [dB] | BER | BLER | bit errors | num bits | block errors | num blocks | runtime [s] | status--------------------------------------------------------------------------------------------------------------------------------------- -10.0 | 6.4125e-01 | 1.0000e+00 | 94556 | 147456 | 256 | 256 | 7.6 |reached target block errors -6.667 | 5.3082e-01 | 1.0000e+00 | 78273 | 147456 | 256 | 256 | 0.8 |reached target block errors -3.333 | 3.7705e-01 | 1.0000e+00 | 55598 | 147456 | 256 | 256 | 0.8 |reached target block errors 0.0 | 1.9615e-01 | 9.8828e-01 | 28923 | 147456 | 253 | 256 | 0.8 |reached target block errors 3.333 | 8.8460e-02 | 9.1406e-01 | 13044 | 147456 | 234 | 256 | 0.8 |reached target block errors 6.667 | 2.3407e-02 | 6.4844e-01 | 6903 | 294912 | 332 | 512 | 1.6 |reached target block errors 10.0 | 9.1847e-03 | 3.8021e-01 | 4063 | 442368 | 292 | 768 | 2.4 |reached target block errors 13.333 | 3.2945e-03 | 1.6328e-01 | 2429 | 737280 | 209 | 1280 | 4.1 |reached target block errors 16.667 | 8.6873e-04 | 8.1250e-02 | 1281 | 1474560 | 208 | 2560 | 8.1 |reached target block errors 20.0 | 1.1822e-03 | 4.1324e-02 | 3312 | 2801664 | 201 | 4864 | 15.4 |reached target block errorsEbNo [dB] | BER | BLER | bit errors | num bits | block errors | num blocks | runtime [s] | status--------------------------------------------------------------------------------------------------------------------------------------- -10.0 | 6.8111e-01 | 1.0000e+00 | 100434 | 147456 | 256 | 256 | 9.1 |reached target block errors -6.667 | 5.4485e-01 | 1.0000e+00 | 80342 | 147456 | 256 | 256 | 1.5 |reached target block errors -3.333 | 3.7080e-01 | 1.0000e+00 | 54677 | 147456 | 256 | 256 | 1.4 |reached target block errors 0.0 | 1.8909e-01 | 9.7266e-01 | 27883 | 147456 | 249 | 256 | 1.5 |reached target block errors 3.333 | 5.6878e-02 | 8.0078e-01 | 8387 | 147456 | 205 | 256 | 1.5 |reached target block errors 6.667 | 2.1844e-02 | 5.4297e-01 | 6442 | 294912 | 278 | 512 | 2.9 |reached target block errors 10.0 | 6.0238e-03 | 2.5098e-01 | 3553 | 589824 | 257 | 1024 | 5.8 |reached target block errors 13.333 | 2.9781e-03 | 1.2723e-01 | 3074 | 1032192 | 228 | 1792 | 10.2 |reached target block errors 16.667 | 1.3725e-03 | 6.2200e-02 | 2631 | 1916928 | 207 | 3328 | 19.0 |reached target block errors 20.0 | 8.2143e-04 | 5.3467e-02 | 1938 | 2359296 | 219 | 4096 | 23.4 |reached target block errorsEbNo [dB] | BER | BLER | bit errors | num bits | block errors | num blocks | runtime [s] | status--------------------------------------------------------------------------------------------------------------------------------------- -10.0 | 6.4833e-01 | 1.0000e+00 | 95600 | 147456 | 256 | 256 | 7.3 |reached target block errors -6.667 | 5.2430e-01 | 1.0000e+00 | 77311 | 147456 | 256 | 256 | 0.8 |reached target block errors -3.333 | 3.9441e-01 | 1.0000e+00 | 58158 | 147456 | 256 | 256 | 0.8 |reached target block errors 0.0 | 2.1968e-01 | 9.9219e-01 | 32393 | 147456 | 254 | 256 | 0.8 |reached target block errors 3.333 | 1.1182e-01 | 9.2188e-01 | 16488 | 147456 | 236 | 256 | 0.8 |reached target block errors 6.667 | 3.1779e-02 | 7.4609e-01 | 9372 | 294912 | 382 | 512 | 1.7 |reached target block errors 10.0 | 1.4431e-02 | 4.7461e-01 | 4256 | 294912 | 243 | 512 | 1.7 |reached target block errors 13.333 | 2.0752e-03 | 2.2461e-01 | 1224 | 589824 | 230 | 1024 | 3.3 |reached target block errors 16.667 | 2.2210e-03 | 1.0010e-01 | 2620 | 1179648 | 205 | 2048 | 6.5 |reached target block errors 20.0 | 2.0950e-03 | 6.7057e-02 | 3707 | 1769472 | 206 | 3072 | 9.8 |reached target block errors
Next, we evaluate the coded BER. The cell below runs the simulations with perfect CSI and channel estimation. Results are stored in theBER dictionary.
[20]:
BER={}# Store the results# Perfect CSIber_lmmse,ber_ep,ber_kbest,ber_mmse_pic=run_sim(NUM_TX,NUM_BITS_PER_SYMBOL,"bit",EBN0_DBs,True)BER['Perf. CSI / LMMSE']=ber_lmmseBER['Perf. CSI / EP']=ber_epBER['Perf. CSI / K-Best']=ber_kbestBER['Perf. CSI / MMSE-PIC']=ber_mmse_pic# Imperfect CSIber_lmmse,ber_ep,ber_kbest,ber_mmse_pic=run_sim(NUM_TX,NUM_BITS_PER_SYMBOL,"bit",EBN0_DBs,False)BER['Ch. Est. / LMMSE']=ber_lmmseBER['Ch. Est. / EP']=ber_epBER['Ch. Est. / K-Best']=ber_kbestBER['Ch. Est. / MMSE-PIC']=ber_mmse_pic
EbNo [dB] | BER | BLER | bit errors | num bits | block errors | num blocks | runtime [s] | status--------------------------------------------------------------------------------------------------------------------------------------- -10.0 | 1.8211e-01 | 8.6719e-01 | 53707 | 294912 | 222 | 256 | 8.0 |reached target block errors -6.667 | 1.0835e-01 | 5.7422e-01 | 63905 | 589824 | 294 | 512 | 0.2 |reached target block errors -3.333 | 5.6606e-02 | 3.5807e-01 | 50081 | 884736 | 275 | 768 | 0.3 |reached target block errors 0.0 | 1.8911e-02 | 1.3151e-01 | 33462 | 1769472 | 202 | 1536 | 0.6 |reached target block errors 3.333 | 7.1384e-03 | 4.9316e-02 | 33683 | 4718592 | 202 | 4096 | 1.6 |reached target block errors 6.667 | 1.5862e-03 | 1.2079e-02 | 30406 | 19169280 | 201 | 16640 | 6.4 |reached target block errors 10.0 | 3.4153e-04 | 2.6953e-03 | 20144 | 58982400 | 138 | 51200 | 19.8 |reached max iterations 13.333 | 4.8811e-05 | 3.7109e-04 | 2879 | 58982400 | 19 | 51200 | 19.8 |reached max iterations 16.667 | 0.0000e+00 | 0.0000e+00 | 0 | 58982400 | 0 | 51200 | 19.8 |reached max iterationsSimulation stopped as no error occurred @ EbNo = 16.7 dB.EbNo [dB] | BER | BLER | bit errors | num bits | block errors | num blocks | runtime [s] | status--------------------------------------------------------------------------------------------------------------------------------------- -10.0 | 1.8772e-01 | 8.7109e-01 | 55361 | 294912 | 223 | 256 | 7.1 |reached target block errors -6.667 | 9.8562e-02 | 5.4102e-01 | 58134 | 589824 | 277 | 512 | 0.2 |reached target block errors -3.333 | 3.8445e-02 | 2.3828e-01 | 45351 | 1179648 | 244 | 1024 | 0.5 |reached target block errors 0.0 | 7.8825e-03 | 5.8036e-02 | 32545 | 4128768 | 208 | 3584 | 1.7 |reached target block errors 3.333 | 7.8278e-04 | 6.1035e-03 | 29549 | 37748736 | 200 | 32768 | 15.1 |reached target block errors 6.667 | 1.1863e-04 | 9.5703e-04 | 6997 | 58982400 | 49 | 51200 | 23.6 |reached max iterations 10.0 | 1.0630e-05 | 7.8125e-05 | 627 | 58982400 | 4 | 51200 | 23.6 |reached max iterations 13.333 | 0.0000e+00 | 0.0000e+00 | 0 | 58982400 | 0 | 51200 | 23.7 |reached max iterationsSimulation stopped as no error occurred @ EbNo = 13.3 dB.EbNo [dB] | BER | BLER | bit errors | num bits | block errors | num blocks | runtime [s] | status--------------------------------------------------------------------------------------------------------------------------------------- -10.0 | 2.1242e-01 | 9.1406e-01 | 62646 | 294912 | 234 | 256 | 7.7 |reached target block errors -6.667 | 1.1749e-01 | 6.6797e-01 | 69299 | 589824 | 342 | 512 | 1.8 |reached target block errors -3.333 | 3.3664e-02 | 2.4121e-01 | 39712 | 1179648 | 247 | 1024 | 3.5 |reached target block errors 0.0 | 6.0211e-03 | 4.8713e-02 | 30187 | 5013504 | 212 | 4352 | 15.0 |reached target block errors 3.333 | 6.5579e-04 | 5.1398e-03 | 29397 | 44826624 | 200 | 38912 | 133.7 |reached target block errors 6.667 | 8.7501e-05 | 7.0312e-04 | 5161 | 58982400 | 36 | 51200 | 175.9 |reached max iterations 10.0 | 1.3224e-05 | 1.5625e-04 | 780 | 58982400 | 8 | 51200 | 175.8 |reached max iterations 13.333 | 1.5259e-06 | 1.9531e-05 | 90 | 58982400 | 1 | 51200 | 176.0 |reached max iterations 16.667 | 0.0000e+00 | 0.0000e+00 | 0 | 58982400 | 0 | 51200 | 175.6 |reached max iterationsSimulation stopped as no error occurred @ EbNo = 16.7 dB.EbNo [dB] | BER | BLER | bit errors | num bits | block errors | num blocks | runtime [s] | status--------------------------------------------------------------------------------------------------------------------------------------- -10.0 | 1.8271e-01 | 8.0469e-01 | 53884 | 294912 | 206 | 256 | 6.2 |reached target block errors -6.667 | 1.1947e-01 | 6.3867e-01 | 70465 | 589824 | 327 | 512 | 0.2 |reached target block errors -3.333 | 5.4596e-02 | 3.3984e-01 | 48303 | 884736 | 261 | 768 | 0.4 |reached target block errors 0.0 | 1.1698e-02 | 9.2014e-02 | 31049 | 2654208 | 212 | 2304 | 1.1 |reached target block errors 3.333 | 1.2643e-03 | 1.2979e-02 | 23118 | 18284544 | 206 | 15872 | 7.6 |reached target block errors 6.667 | 1.9104e-04 | 2.0898e-03 | 11268 | 58982400 | 107 | 51200 | 24.8 |reached max iterations 10.0 | 1.9972e-05 | 1.3672e-04 | 1178 | 58982400 | 7 | 51200 | 24.8 |reached max iterations 13.333 | 3.8147e-06 | 3.9063e-05 | 225 | 58982400 | 2 | 51200 | 24.8 |reached max iterations 16.667 | 0.0000e+00 | 0.0000e+00 | 0 | 58982400 | 0 | 51200 | 24.8 |reached max iterationsSimulation stopped as no error occurred @ EbNo = 16.7 dB.EbNo [dB] | BER | BLER | bit errors | num bits | block errors | num blocks | runtime [s] | status--------------------------------------------------------------------------------------------------------------------------------------- -10.0 | 2.1006e-01 | 9.0234e-01 | 61948 | 294912 | 231 | 256 | 8.0 |reached target block errors -6.667 | 1.4451e-01 | 7.1289e-01 | 85235 | 589824 | 365 | 512 | 1.6 |reached target block errors -3.333 | 8.1896e-02 | 4.6289e-01 | 48304 | 589824 | 237 | 512 | 1.6 |reached target block errors 0.0 | 3.3626e-02 | 2.1680e-01 | 39667 | 1179648 | 222 | 1024 | 3.3 |reached target block errors 3.333 | 1.4133e-02 | 9.3750e-02 | 37513 | 2654208 | 216 | 2304 | 7.4 |reached target block errors 6.667 | 4.2246e-03 | 2.8878e-02 | 34885 | 8257536 | 207 | 7168 | 23.0 |reached target block errors 10.0 | 1.5778e-03 | 9.8145e-03 | 37224 | 23592960 | 201 | 20480 | 65.6 |reached target block errors 13.333 | 7.0814e-04 | 4.3403e-03 | 37591 | 53084160 | 200 | 46080 | 147.6 |reached target block errors 16.667 | 3.8495e-04 | 2.4805e-03 | 22705 | 58982400 | 127 | 51200 | 164.0 |reached max iterations 20.0 | 2.2702e-04 | 1.7578e-03 | 13390 | 58982400 | 90 | 51200 | 163.8 |reached max iterationsEbNo [dB] | BER | BLER | bit errors | num bits | block errors | num blocks | runtime [s] | status--------------------------------------------------------------------------------------------------------------------------------------- -10.0 | 2.0131e-01 | 8.8281e-01 | 59369 | 294912 | 226 | 256 | 8.6 |reached target block errors -6.667 | 1.2891e-01 | 6.6016e-01 | 76037 | 589824 | 338 | 512 | 1.7 |reached target block errors -3.333 | 6.4141e-02 | 3.7500e-01 | 56748 | 884736 | 288 | 768 | 2.5 |reached target block errors 0.0 | 1.9562e-02 | 1.3411e-01 | 34614 | 1769472 | 206 | 1536 | 5.0 |reached target block errors 3.333 | 4.1952e-03 | 2.9225e-02 | 33405 | 7962624 | 202 | 6912 | 22.7 |reached target block errors 6.667 | 1.1184e-03 | 8.3527e-03 | 31004 | 27721728 | 201 | 24064 | 78.6 |reached target block errors 10.0 | 6.2521e-04 | 4.7201e-03 | 30976 | 49545216 | 203 | 43008 | 140.7 |reached target block errors 13.333 | 4.1604e-04 | 3.1641e-03 | 24539 | 58982400 | 162 | 51200 | 167.7 |reached max iterations 16.667 | 1.4374e-04 | 1.5039e-03 | 8478 | 58982400 | 77 | 51200 | 167.5 |reached max iterations 20.0 | 2.5279e-04 | 1.8555e-03 | 14910 | 58982400 | 95 | 51200 | 167.7 |reached max iterationsEbNo [dB] | BER | BLER | bit errors | num bits | block errors | num blocks | runtime [s] | status--------------------------------------------------------------------------------------------------------------------------------------- -10.0 | 2.2560e-01 | 9.5703e-01 | 66531 | 294912 | 245 | 256 | 9.1 |reached target block errors -6.667 | 1.4490e-01 | 7.1094e-01 | 85466 | 589824 | 364 | 512 | 3.2 |reached target block errors -3.333 | 6.8112e-02 | 4.1602e-01 | 40174 | 589824 | 213 | 512 | 3.2 |reached target block errors 0.0 | 1.5723e-02 | 1.0693e-01 | 37095 | 2359296 | 219 | 2048 | 12.8 |reached target block errors 3.333 | 3.1675e-03 | 1.9345e-02 | 39234 | 12386304 | 208 | 10752 | 67.3 |reached target block errors 6.667 | 9.6538e-04 | 6.1516e-03 | 36157 | 37453824 | 200 | 32512 | 203.6 |reached target block errors 10.0 | 4.2582e-04 | 2.8711e-03 | 25116 | 58982400 | 147 | 51200 | 320.9 |reached max iterations 13.333 | 2.4751e-04 | 1.8555e-03 | 14599 | 58982400 | 95 | 51200 | 321.1 |reached max iterations 16.667 | 1.3477e-04 | 1.2500e-03 | 7949 | 58982400 | 64 | 51200 | 320.6 |reached max iterations 20.0 | 1.8600e-04 | 1.5039e-03 | 10971 | 58982400 | 77 | 51200 | 320.8 |reached max iterationsEbNo [dB] | BER | BLER | bit errors | num bits | block errors | num blocks | runtime [s] | status--------------------------------------------------------------------------------------------------------------------------------------- -10.0 | 2.1925e-01 | 9.1406e-01 | 64660 | 294912 | 234 | 256 | 8.1 |reached target block errors -6.667 | 1.4956e-01 | 7.1484e-01 | 88215 | 589824 | 366 | 512 | 1.7 |reached target block errors -3.333 | 8.9325e-02 | 4.8828e-01 | 52686 | 589824 | 250 | 512 | 1.7 |reached target block errors 0.0 | 3.0121e-02 | 1.9922e-01 | 35532 | 1179648 | 204 | 1024 | 3.4 |reached target block errors 3.333 | 5.7904e-03 | 4.5956e-02 | 29030 | 5013504 | 200 | 4352 | 14.4 |reached target block errors 6.667 | 1.6986e-03 | 1.3706e-02 | 28554 | 16809984 | 200 | 14592 | 48.1 |reached target block errors 10.0 | 6.5654e-04 | 5.3879e-03 | 28075 | 42762240 | 200 | 37120 | 122.4 |reached target block errors 13.333 | 3.2079e-04 | 2.8906e-03 | 18921 | 58982400 | 148 | 51200 | 169.0 |reached max iterations 16.667 | 2.3003e-04 | 1.8555e-03 | 13568 | 58982400 | 95 | 51200 | 169.0 |reached max iterations 20.0 | 2.2305e-04 | 1.8555e-03 | 13156 | 58982400 | 95 | 51200 | 168.7 |reached max iterations
Finally, we plot the results.
[21]:
fig,ax=plt.subplots(1,2,figsize=(16,7))fig.suptitle(f"{NUM_TX}x{NUM_RX_ANT} UMi |{2**NUM_BITS_PER_SYMBOL}-QAM")## SERax[0].set_title("Symbol error rate")# Perfect CSIax[0].semilogy(EBN0_DBs,SER['Perf. CSI / LMMSE'],'x-',label='Perf. CSI / LMMSE',c='C0')ax[0].semilogy(EBN0_DBs,SER['Perf. CSI / EP'],'o--',label='Perf. CSI / EP',c='C0')ax[0].semilogy(EBN0_DBs,SER['Perf. CSI / K-Best'],'s-.',label='Perf. CSI / K-Best',c='C0')ax[0].semilogy(EBN0_DBs,SER['Perf. CSI / MMSE-PIC'],'d:',label='Perf. CSI / MMSE-PIC',c='C0')# Imperfect CSIax[0].semilogy(EBN0_DBs,SER['Ch. Est. / LMMSE'],'x-',label='Ch. Est. / LMMSE',c='C1')ax[0].semilogy(EBN0_DBs,SER['Ch. Est. / EP'],'o--',label='Ch. Est. / EP',c='C1')ax[0].semilogy(EBN0_DBs,SER['Ch. Est. / K-Best'],'s-.',label='Ch. Est. / K-Best',c='C1')ax[0].semilogy(EBN0_DBs,SER['Ch. Est. / MMSE-PIC'],'d:',label='Ch. Est. / MMSE-PIC',c='C1')ax[0].set_xlabel(r"$E_b/N0$")ax[0].set_ylabel("SER")ax[0].set_ylim((1e-4,1.0))ax[0].legend()ax[0].grid(True)## SERax[1].set_title("Bit error rate")# Perfect CSIax[1].semilogy(EBN0_DBs,BER['Perf. CSI / LMMSE'],'x-',label='Perf. CSI / LMMSE',c='C0')ax[1].semilogy(EBN0_DBs,BER['Perf. CSI / EP'],'o--',label='Perf. CSI / EP',c='C0')ax[1].semilogy(EBN0_DBs,BER['Perf. CSI / K-Best'],'s-.',label='Perf. CSI / K-Best',c='C0')ax[1].semilogy(EBN0_DBs,BER['Perf. CSI / MMSE-PIC'],'d:',label='Perf. CSI / MMSE-PIC',c='C0')# Imperfect CSIax[1].semilogy(EBN0_DBs,BER['Ch. Est. / LMMSE'],'x-',label='Ch. Est. / LMMSE',c='C1')ax[1].semilogy(EBN0_DBs,BER['Ch. Est. / EP'],'o--',label='Ch. Est. / EP',c='C1')ax[1].semilogy(EBN0_DBs,BER['Ch. Est. / K-Best'],'s-.',label='Ch. Est. / K-Best',c='C1')ax[1].semilogy(EBN0_DBs,BER['Ch. Est. / MMSE-PIC'],'d:',label='Ch. Est. / MMSE-PIC',c='C1')ax[1].set_xlabel(r"$E_b/N0$")ax[1].set_ylabel("BER")ax[1].set_ylim((1e-4,1.0))ax[1].legend()ax[1].grid(True)

For this setup, the non-linear detection algorithms K-Best, EP, and MMSE-PIC, outperform the linear MMSE detection method. It is remarkable that K-Best and EP with imperfect CSI achieve lower BER than LMMSE detection with perfect CSI.
However, one should keep in mind that:
EP is prone to numerical imprecision and could therefore achieve better BER/SER with double precision (
dtype=tf.complex128). The number of iterationslas well as the update smoothing parameterbetaimpact performance.For K-Best, there is not a unique way to compute soft information and better performance could be achieved with improved methods for computing soft information from a list of candidates (seelist2llr). Increasing the list size
kresults in improved accuracy at the cost of higher complexity.MMSE-PIC can be easily combined with a decoder to implement iterative detection and decoding, as it takes as input soft prior information on the bits/symbols.