BLER Evaluation for PDSCH Communication with LDPC
This notebook demonstrates an end-to-end PDSCH communication pipeline for evaluating block error rate (BLER).
It compares two channel-estimation methods:
perfect channel knowledge
LS-based channel estimation
The notebook uses LDPC coding and measures BLER over multiple slots across a range of SNR values.
[1]:
import numpy as np
import time
import matplotlib.pyplot as plt
from neoradium import Carrier, PDSCH, CdlChannel, AntennaPanel, random, SnrScheduler
[2]:
numSlots = 200
# Using SnrScheduler class to automatically find the right SNR ranges
snrScheduler = SnrScheduler(0, 0.2, fastStep=5) # Start at 0 dB, use increments of 0.2 dB
freqDomain = True # Set to True to apply channel in frequency domain
modulation = "16QAM"
carrier = Carrier(numRbs=24, spacing=30) # Create a carrier with 24 RBs and 30 kHz subcarrier spacing
bwp = carrier.curBwp # The only bandwidth part in the carrier
# Create a PDSCH object
pdsch = PDSCH(bwp, numLayers=2, modulation=modulation)
pdsch.setDMRS(configType=2, additionalPos=1, symbols=2) # DMRS configuration
# Create an LDPC codec
ldpc = pdsch.getLdpcCodec(coderates=490/1024)
results = {}
for chanEstMethod in ["Perfect channel knowledge", "LS channel estimation"]: # Two channel estimation methods
results[chanEstMethod] = {}
print("\nSimulating end-to-end for %s using %s in the %s domain"%
(modulation, chanEstMethod, "frequency" if freqDomain else "time"))
print("SNR(dB) Total Bits Bit Errors BER(%) Total Blocks Block Errors BLER(%) time(Sec.)")
print("--------- ---------- ---------- ------ ------------ ------------ ------- ----------")
snrScheduler.reset()
for snrDb in snrScheduler:
random.setSeed(123) # Make the results reproducible for each SNR
t0 = time.monotonic() # Start time for each SNR
carrier.slotNo = 0
# Create a CdlChannel object
channel = CdlChannel(bwp, 'C', delaySpread=300, carrierFreq=4e9, dopplerShift=5,
txAntenna = AntennaPanel([2,4], polarization="x"), # 16 TX antennas
rxAntenna = AntennaPanel([1,2], polarization="x")) # 4 RX antennas
blockErrors = 0
totalBlocks = 0
bitErrors = 0
totalBits = 0
for slotNo in range(numSlots):
pdsch.initGrid() # Create and initialize PDSCH's internal grid
txBlock = random.bits(ldpc.txBlockSizes[0]) # Create random binary data
numBits = pdsch.getBitCapacity() # Number of bits available in the resource grid
# Perform the segmentation, rate-matching, and encoding
rateMatchedCodeBlocks = ldpc.encode(txBlock, numBits[0])
pdsch.setPdschData(rateMatchedCodeBlocks) # Map/modulate the data to the resource grid
channelMatrix = channel.getChannelMatrix() # Get the channel matrix
precoder = pdsch.getPrecodingMatrix(channelMatrix) # Get the precoding matrix
txGrid = bwp.createGrid(len(channel.txAntenna)) # Create the transmitted resource grid
pdsch.precodeTo(txGrid, precoder) # Perform the precoding
if freqDomain:
rxGrid = txGrid.applyChannel(channelMatrix) # Apply the channel in the frequency domain
rxGrid = rxGrid.addNoise(snrDb=snrDb) # Add noise
else:
txWaveform = txGrid.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 the time domain
noisyRxWaveform = rxWaveform.addNoise(snrDb=snrDb, bwp=bwp) # Add noise
offset = channel.getTimingOffset() # Get timing info for synchronization
syncedWaveform = noisyRxWaveform.sync(offset) # Synchronization
rxGrid = syncedWaveform.ofdmDemodulate(bwp) # OFDM demodulation
if "Perfect" in chanEstMethod:
estChannelMatrix = channel.getEffChannel(channelMatrix, precoder) # Perfect channel knowledge
eqGrid, llrScales = pdsch.equalize(rxGrid, estChannelMatrix) # Equalization
else:
estChannelMatrix, errVar = pdsch.estimateChannel(rxGrid) # LS channel estimation
eqGrid, llrScales = pdsch.equalize(rxGrid, estChannelMatrix, errVar)# Equalization
llrs = pdsch.getLLRs(eqGrid, llrScales) # Demodulation (to LLRs)
decodedTxBlock, crcMatch = ldpc.decode(llrs[0]) # LDPC decoding
blockErrors += 0 if crcMatch[0] else 1 # Number of transport-block errors
bitErrors += np.abs(decodedTxBlock-txBlock).sum() # Count the number of bit errors
totalBlocks += 1
totalBits += len(txBlock)
ber = bitErrors*100/totalBits
bler = blockErrors*100/totalBlocks
print("\r %5.1f %8d %8d %6.2f %8d %8d %6.2f %6.2f"
%(snrDb, totalBits, bitErrors, ber, totalBlocks, blockErrors, bler, time.monotonic()-t0), end='')
channel.goNext()
dt = time.monotonic()-t0
snrScheduler.setData(bler, ber)
print("")
results[chanEstMethod] = snrScheduler.getSnrsAndData()
# Compare the results
logGraph = False
for i,chanEstMethod in enumerate(["Perfect channel knowledge", "LS channel estimation"]):
plt.plot(results[chanEstMethod][0], results[chanEstMethod][1], label=chanEstMethod)
plt.legend()
plt.title("Block error rate (BLER) for different channel-estimation methods");
plt.grid()
plt.xlabel("SNR (dB)")
# plt.xticks(results[chanEstMethod][0])
plt.ylabel("BLER (%)")
if logGraph: plt.yscale('log')
plt.show()
Simulating end-to-end for 16QAM using Perfect channel knowledge in the frequency domain
SNR(dB) Total Bits Bit Errors BER(%) Total Blocks Block Errors BLER(%) time(Sec.)
--------- ---------- ---------- ------ ------------ ------------ ------- ----------
0.0 2766400 0 0.00 200 0 0.00 51.98
-5.0 2766400 0 0.00 200 0 0.00 51.49
-10.0 2766400 0 0.00 200 0 0.00 51.57
-15.0 2766400 447996 16.19 200 200 100.00 51.67
-12.4 2766400 3840 0.14 200 189 94.50 51.34
-12.6 2766400 9633 0.35 200 199 99.50 50.98
-12.8 2766400 22497 0.81 200 200 100.00 51.51
-13.0 2766400 46315 1.67 200 200 100.00 50.99
-12.2 2766400 1455 0.05 200 164 82.00 50.97
-12.0 2766400 435 0.02 200 111 55.50 50.76
-11.8 2766400 107 0.00 200 53 26.50 50.86
-11.6 2766400 19 0.00 200 14 7.00 50.66
-11.4 2766400 1 0.00 200 1 0.50 50.85
-11.2 2766400 0 0.00 200 0 0.00 50.88
-11.0 2766400 0 0.00 200 0 0.00 50.85
Simulating end-to-end for 16QAM using LS channel estimation in the frequency domain
SNR(dB) Total Bits Bit Errors BER(%) Total Blocks Block Errors BLER(%) time(Sec.)
--------- ---------- ---------- ------ ------------ ------------ ------- ----------
0.0 2766400 0 0.00 200 0 0.00 50.81
-5.0 2766400 0 0.00 200 0 0.00 51.16
-10.0 2766400 7 0.00 200 5 2.50 50.58
-10.2 2766400 23 0.00 200 14 7.00 51.12
-10.4 2766400 105 0.00 200 49 24.50 51.40
-10.6 2766400 432 0.02 200 110 55.00 50.92
-10.8 2766400 1350 0.05 200 159 79.50 51.08
-11.0 2766400 3475 0.13 200 186 93.00 50.83
-11.2 2766400 8502 0.31 200 198 99.00 50.86
-11.4 2766400 19039 0.69 200 200 100.00 50.79
-11.6 2766400 39436 1.43 200 200 100.00 51.23
-9.8 2766400 3 0.00 200 3 1.50 50.60
-9.6 2766400 1 0.00 200 1 0.50 50.57
-9.4 2766400 0 0.00 200 0 0.00 50.52
-9.2 2766400 0 0.00 200 0 0.00 50.75
[ ]: