Calculating BER/BLER for PDSCH Communication with LDPC
This notebook shows how to calculate the bit error rate of PDSCH communication with LDPC channel coding.
[1]:
import numpy as np
import scipy.io
import time
import matplotlib.pyplot as plt
from neoradium import Carrier, PDSCH, CdlChannel, AntennaPanel, LdpcEncoder, Grid, random
[2]:
numFrames = 4 # Number of time-domain frames
ebNoDbs = range(-2,4) # Eb/No values (in dB)
freqDomain = False # Set to True to apply channel in frequency domain
modulation = "16QAM"
carrier = Carrier(numRbs=51, spacing=30) # Create a carrier with 51 RBs and 30KHz subcarrier spacing
bwp = carrier.curBwp # The only bandwidth part in the carrier
# Create a PDSCH object
pdsch = PDSCH(bwp, interleavingBundleSize=0, numLayers=2, nID=carrier.cellId, modulation=modulation)
pdsch.setDMRS(prgSize=0, configType=2, additionalPos=2) # Specify the DMRS configuration
# Create the LDPC encoder
codeRate = 490/1024
ldpcEncoder = LdpcEncoder(baseGraphNo=1, modulation=pdsch.modems[0].modulation,
txLayers=pdsch.numLayers, targetRate=codeRate)
ldpcDecoder = ldpcEncoder.getDecoder()
numSlots = bwp.slotsPerFrame*numFrames
results = {}
for chanEstMethod in ["Perfect", "LS"]: # Two channel estimation methods
results[chanEstMethod] = {}
print("\nSimulating end-to-end for \"%s\", with \"%s\" channel estimation, in %s domain."%
(modulation, chanEstMethod, "frequency" if freqDomain else "time"))
print("EB/No(dB) Total Bits Bit Errors BER(%) Total Blocks Block Errors BLER(%) time(Sec.)")
print("--------- ---------- ---------- ------ ------------ ------------ ------- ----------")
for ebNoDb in ebNoDbs: # For each Eb/No value in ebNoDbs
snrDb = ebNoDb + 10*np.log10(pdsch.modems[0].qm * codeRate * bwp.dataTimeRatio) # Eb/No -> SNR
random.setSeed(123) # Making the results reproducible for each Eb/No
t0 = time.time() # Start time for each Eb/No
carrier.slotNo = 0
# Creating a CdlChannel object:
channel = CdlChannel(bwp, 'C', delaySpread=300, carrierFreq=4e9, dopplerShift=5,
txAntenna = AntennaPanel([2,4]), # 8 TX antenna
rxAntenna = AntennaPanel([1,2]), # 2 RX antenna
seed = 123)
blockErrors = 0
totalBlocks = 0
bitErrors = 0
totalBits = 0
for slotNo in range(numSlots):
grid = pdsch.getGrid() # Create a resource grid already populated with DMRS
txBlockSize = pdsch.getTxBlockSize(codeRate) # Calculate the Transport Block Size
txBlock = random.bits(txBlockSize[0]) # Create random binary data
numBits = pdsch.getBitSizes(grid) # Actual number of bits available in the resource grid
# Perform the segmentation, rate-matching, and encoding
rateMatchedCodeWords = ldpcEncoder.getRateMatchedCodeWords(txBlock, numBits[0])
pdsch.populateGrid(grid, rateMatchedCodeWords) # Map/modulate the data to the resource grid
# Store the indexes of the PDSCH data in pdschIndexes to be used later.
pdschIndexes = pdsch.getReIndexes(grid, "PDSCH")
# Getting the Precoding Matrix, and precoding the resource grid
channelMatrix = channel.getChannelMatrix() # Get the channel matrix
precoder = pdsch.getPrecodingMatrix(channelMatrix) # Get the precoder matrix from the PDSCH object
precodedGrid = grid.precode(precoder) # Perform the precoding
if freqDomain:
rxGrid = precodedGrid.applyChannel(channelMatrix) # Apply the channel in frequency domain
rxGrid = rxGrid.addNoise(snrDb=snrDb) # Add noise
else:
txWaveform = precodedGrid.ofdmModulate() # OFDM Modulation
maxDelay = channel.getMaxDelay() # Get the max. channel delay
txWaveform = txWaveform.pad(maxDelay) # Pad with zeros
rxWaveform = channel.applyToSignal(txWaveform) # Apply channel in time domain
noisyRxWaveform = rxWaveform.addNoise(snrDb=snrDb, nFFT=bwp.nFFT) # Add noise
offset = channel.getTimingOffset() # Get timing info for synchronization
syncedWaveform = noisyRxWaveform.sync(offset) # Synchronization
rxGrid = syncedWaveform.ofdmDemodulate(bwp) # OFDM demodulation
if chanEstMethod == "Perfect": # Perfect Channel Estimation
estChannelMatrix = channelMatrix @ precoder[None,...]
else: # LS + Interpolation Channel Estimation
estChannelMatrix, noiseEst = rxGrid.estimateChannelLS(pdsch.dmrs, polarInt=False,
kernel='linear')
eqGrid, llrScales = rxGrid.equalize(estChannelMatrix) # Equalization
llrs = pdsch.getLLRsFromGrid(eqGrid, pdschIndexes, llrScales) # Demodulation (to LLRs)
rxCodedBlocks = ldpcDecoder.recoverRate(llrs[0], txBlockSize[0]) # Recovering Rate
decodedBlocks = ldpcDecoder.decode(rxCodedBlocks, numIter=20) # LDPC Decoding
decodedTxBlockWithCRC, crcMatch = ldpcDecoder.checkCrcAndMerge(decodedBlocks) # Merge blocks
decodedTxBlock = decodedTxBlockWithCRC[:-24] # remove transport block CRC
blockErrors += len(crcMatch)-sum(crcMatch) # Number of Block errors
bitErrors += np.abs(decodedTxBlock-txBlock).sum() # Number of bit errors
totalBlocks += len(crcMatch)
totalBits += len(txBlock)
ber = bitErrors*100/totalBits
bler = blockErrors*100/totalBlocks
print("\r %3d %8d %8d %6.2f %8d %8d %6.2f %6.2f"
%(ebNoDb, totalBits, bitErrors, ber, totalBlocks, blockErrors, bler, time.time()-t0), end='')
carrier.goNext()
channel.goNext()
dt = time.time()-t0
results[chanEstMethod][snrDb] = {"totalBits": totalBits,
"bitErrors": bitErrors,
"BER": bitErrors*100/totalBits,
"totalBlocks":totalBlocks,
"blockErrors":blockErrors,
"BLER": blockErrors*100/totalBlocks,
"Time": dt}
print("\r %3d %8d %8d %6.2f %8d %8d %6.2f %6.2f"
%(ebNoDb, totalBits, bitErrors, ber, totalBlocks, blockErrors, bler, time.time()-t0))
Simulating end-to-end for "16QAM", with "Perfect" channel estimation, in time domain.
EB/No(dB) Total Bits Bit Errors BER(%) Total Blocks Block Errors BLER(%) time(Sec.)
--------- ---------- ---------- ------ ------------ ------------ ------- ----------
-2 2417280 230262 9.53 320 181 56.56 53.77
-1 2417280 114397 4.73 320 107 33.44 54.84
0 2417280 7617 0.32 320 13 4.06 54.15
1 2417280 0 0.00 320 0 0.00 55.53
2 2417280 0 0.00 320 0 0.00 54.87
3 2417280 0 0.00 320 0 0.00 54.94
Simulating end-to-end for "16QAM", with "LS" channel estimation, in time domain.
EB/No(dB) Total Bits Bit Errors BER(%) Total Blocks Block Errors BLER(%) time(Sec.)
--------- ---------- ---------- ------ ------------ ------------ ------- ----------
-2 2417280 447325 18.51 320 302 94.38 53.51
-1 2417280 350608 14.50 320 258 80.62 54.01
0 2417280 246521 10.20 320 196 61.25 54.24
1 2417280 120722 4.99 320 105 32.81 54.80
2 2417280 40487 1.67 320 40 12.50 55.53
3 2417280 2352 0.10 320 2 0.62 54.71
[3]:
# Compare the results in a couple of plots:
logGraph = False
def ebNo2Snr(ebNo): return ebNo + 10*np.log10(pdsch.modems[0].qm * codeRate * bwp.dataTimeRatio)
# Bit Error Rate:
for i,chanEstMethod in enumerate(['Perfect', 'LS']):
bers = [results[chanEstMethod][ebNo2Snr(ebNoDb)]["BER"] for ebNoDb in ebNoDbs]
plt.plot(ebNoDbs, bers, label=chanEstMethod)
plt.legend()
plt.title("Bit Error Rate for different mothods of Channel Estimation.");
plt.grid()
plt.xlabel("eb/No")
plt.xticks(ebNoDbs)
plt.ylabel("BER (%)")
if logGraph: plt.yscale('log')
plt.show()
# Block Error rate
for i,chanEstMethod in enumerate(['Perfect', 'LS']):
blers = [results[chanEstMethod][ebNo2Snr(ebNoDb)]["BLER"] for ebNoDb in ebNoDbs]
plt.plot(ebNoDbs, blers, label=chanEstMethod)
plt.legend()
plt.title("Block Error Rate for different mothods of Channel Estimation.");
plt.grid()
plt.xlabel("eb/No")
plt.xticks(ebNoDbs)
plt.ylabel("BLER (%)")
if logGraph: plt.yscale('log')
plt.show()


[ ]: