Effect of LDPC Decoding Iterations on PDSCH BLER
This notebook evaluates how the number of LDPC decoding iterations, controlled by numIter, affects the block error rate (BLER) of a 5G NR PDSCH link-level simulation.
For each value of numIter, the simulation runs an end-to-end PDSCH transmission over a CDL channel across a range of SNR values. The resulting BLER curves are then compared to show the trade-off between decoding performance and computational complexity.
[1]:
import numpy as np
import time
import matplotlib.pyplot as plt
from neoradium import BandwidthPart, PDSCH, CdlChannel, AntennaPanel, random, SnrScheduler
[2]:
numSlots = 200
snrScheduler = SnrScheduler(-12, 0.1, fastStep=1) # Start at -12 dB, use increments of 0.1 dB
bwp = BandwidthPart(numRbs=24, spacing=30) # Create the BandwidthPart object
# Create a PDSCH object
pdsch = PDSCH(bwp, numLayers=2, modulation="16QAM")
pdsch.setDMRS(additionalPos=1)
# Create an LDPC codec
ldpc = pdsch.getLdpcCodec(coderates=490/1024)
results = {}
for numIter in [3, 5, 10, 20]:
results[numIter] = {}
ldpc.cwCodecs[0].numIter = numIter
print(f"\nSimulating end-to-end with numIter = {numIter}")
print("SNR(dB) 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
bwp.slotNo = 0
# Create a CdlChannel object
channel = CdlChannel(bwp, 'C', delaySpread=300, carrierFreq=4e9, dopplerShift=5,
txAntenna = AntennaPanel([2,4], polarization="x"), # 16 TX antenna elements
rxAntenna = AntennaPanel([1,2], polarization="x")) # 4 RX antenna elements
blockErrors = 0
totalBlocks = 0
for slotNo in range(numSlots):
pdsch.initGrid() # Create and initialize the PDSCH grid
txBlock = random.bits(ldpc.txBlockSizes[0]) # Random transport block
numBits = pdsch.getBitCapacity() # Bit capacity of the PDSCH grid
rateMatchedCodeBlocks = ldpc.encode(txBlock, numBits[0]) # LDPC rate-matching and encoding
pdsch.setPdschData(rateMatchedCodeBlocks) # Map/modulate encoded code blocks
channelMatrix = channel.getChannelMatrix() # Get the channel matrix
precoder = pdsch.getPrecodingMatrix(channelMatrix) # Precoder matrix
txGrid = bwp.createGrid(len(channel.txAntenna)) # Create a transmitted grid
pdsch.precodeTo(txGrid, precoder) # Precode data into txGrid
rxGrid = txGrid.applyChannel(channelMatrix) # Apply the channel
rxGrid = rxGrid.addNoise(snrDb=snrDb) # Add noise
estChannelMatrix = channel.getEffChannel(channelMatrix, precoder) # Get effective channel
eqGrid, llrScales = pdsch.equalize(rxGrid, estChannelMatrix) # Equalization
llrs = pdsch.getLLRs(eqGrid, llrScales) # Demodulation (to LLRs)
decodedTxBlock, crcMatch = ldpc.decode(llrs) # LDPC rate-recovery and decoding
blockErrors += 0 if crcMatch[0][0] else 1 # Update transport block errors
totalBlocks += 1 # Update number of transport blocks
bler = blockErrors*100/totalBlocks # BLER in percent
print(f"\r{snrDb:^9.1f} {totalBlocks:^12d} {blockErrors:^12d} "
f"{bler:^7.2f} {time.monotonic()-t0:^10.3f}", end='')
channel.goNext()
snrScheduler.setData(bler)
print("")
results[numIter] = snrScheduler.getSnrsAndData()
Simulating end-to-end with numIter = 3
SNR(dB) Total Blocks Block Errors BLER(%) Time(Sec.)
--------- ------------ ------------ ------- ----------
-12.0 200 200 100.00 8.553
-11.0 200 200 100.00 8.578
-10.0 200 152 76.00 8.531
-10.1 200 171 85.50 8.631
-10.2 200 177 88.50 8.608
-10.3 200 185 92.50 8.755
-10.4 200 190 95.00 8.899
-10.5 200 199 99.50 8.703
-10.6 200 200 100.00 8.766
-10.7 200 200 100.00 8.779
-9.9 200 126 63.00 8.710
-9.8 200 112 56.00 8.762
-9.7 200 89 44.50 8.787
-9.6 200 69 34.50 8.811
-9.5 200 53 26.50 8.791
-9.4 200 43 21.50 8.806
-9.3 200 29 14.50 8.705
-9.2 200 16 8.00 8.690
-9.1 200 9 4.50 8.728
-9.0 200 6 3.00 8.849
-8.9 200 3 1.50 8.864
-8.8 200 3 1.50 8.672
-8.7 200 2 1.00 8.759
-8.6 200 1 0.50 8.727
-8.5 200 1 0.50 8.743
-8.4 200 0 0.00 8.775
-8.3 200 0 0.00 8.739
Simulating end-to-end with numIter = 5
SNR(dB) Total Blocks Block Errors BLER(%) Time(Sec.)
--------- ------------ ------------ ------- ----------
-12.0 200 126 63.00 9.306
-12.1 200 148 74.00 9.322
-12.2 200 172 86.00 9.290
-12.3 200 188 94.00 9.377
-12.4 200 194 97.00 9.354
-12.5 200 198 99.00 9.403
-12.6 200 199 99.50 9.285
-12.7 200 200 100.00 9.159
-12.8 200 200 100.00 9.221
-11.9 200 90 45.00 9.265
-11.8 200 61 30.50 9.263
-11.7 200 25 12.50 9.246
-11.6 200 15 7.50 9.256
-11.5 200 5 2.50 9.212
-11.4 200 2 1.00 9.262
-11.3 200 1 0.50 9.271
-11.2 200 1 0.50 9.341
-11.1 200 1 0.50 9.179
-11.0 200 0 0.00 9.141
-10.9 200 0 0.00 9.272
Simulating end-to-end with numIter = 10
SNR(dB) Total Blocks Block Errors BLER(%) Time(Sec.)
--------- ------------ ------------ ------- ----------
-12.0 200 0 0.00 10.386
-13.0 200 5 2.50 10.429
-13.1 200 23 11.50 10.918
-13.2 200 70 35.00 10.750
-13.3 200 116 58.00 10.579
-13.4 200 156 78.00 10.671
-13.5 200 177 88.50 10.476
-13.6 200 193 96.50 10.707
-13.7 200 198 99.00 10.714
-13.8 200 200 100.00 10.637
-13.9 200 200 100.00 10.699
-12.9 200 0 0.00 10.590
-12.8 200 0 0.00 10.448
Simulating end-to-end with numIter = 20
SNR(dB) Total Blocks Block Errors BLER(%) Time(Sec.)
--------- ------------ ------------ ------- ----------
-12.0 200 0 0.00 12.700
-13.0 200 0 0.00 12.701
-14.0 200 190 95.00 13.218
-14.1 200 199 99.50 13.276
-14.2 200 200 100.00 13.100
-14.3 200 200 100.00 13.172
-13.9 200 166 83.00 13.089
-13.8 200 137 68.50 13.080
-13.7 200 85 42.50 12.967
-13.6 200 35 17.50 12.851
-13.5 200 8 4.00 12.887
-13.4 200 1 0.50 12.770
-13.3 200 0 0.00 12.743
-13.2 200 0 0.00 12.665
[3]:
# Compare the results in a plot
for i,numIter in enumerate([3, 5, 10, 20]):
plt.plot(results[numIter][0], results[numIter][1], label=f"numIter={numIter}")
plt.legend()
plt.title("BLER for Different Numbers of LDPC Decoding Iterations")
plt.grid()
plt.xlabel("SNR (dB)")
plt.ylabel("BLER (%)")
plt.show()
[ ]:
[ ]: