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()
../../../../_images/source_Playground_Notebooks_ChanCode_NumIter_3_0.png
[ ]:

[ ]: