Comparing the LDPC results with MATLAB

Applying LDPC encoding/decoding on random transport blocks and comparing the results with the equivalent MATLAB code “MatlabFiles/LDPC.mlx”. Here is the execution results of this code in MATLAB.

[1]:
import numpy as np
import scipy.io
import time

from neoradium import LdpcCodec

matlabFilesPath = "./MatlabFiles"
[2]:
# Read input bits from MATLAB-generated file
inBits = scipy.io.loadmat(matlabFilesPath+'/in.mat')['in'].reshape(-1)
print(f"First 10 bits: {inBits[:10]}")

# Create an LDPC encoder object
ldpc = LdpcCodec(modulations='QPSK', coderates=449/1024, txBlockSizes=len(inBits), numLayers=1)
print(ldpc)
First 10 bits: [0 0 1 1 0 0 1 1 0 1]

LDPC Encode/Decode Properties:
  Num layers:         1
  Num codewords:      1
  numIter:            5
  nRef:               0
  Modulation:         QPSK
  Coderate:           449/1024
  TBS:                10000
  numLayers:          1
  Base Graph:         1
  Code Block Size:    5280
  Num Code Blocks:    2
  Lifting Size:       240

[3]:
# Transport block padded with a 24-bit CRC
cwCodec = ldpc.cwCodecs[0]      # The LDPC codec for the first codeword
tbWithCrc = cwCodec.appendCrc(inBits,'24A')
tbWithCrc.shape
[3]:
(10024,)
[4]:
# Do the segmentation:
codeBlocksCrc = cwCodec.doSegmentation(tbWithCrc)

# NeoRadium does not set filler bits to -1. To match with MATLAB, we need to set these bits
# to -1. This is only needed when comparing segmentation results with MATLAB.
fillerStart = cwCodec.codeBlockSize-cwCodec.numFillerBits
codeBlocksCrc[:, fillerStart : fillerStart+cwCodec.numFillerBits] = -1

# Compare results with MATLAB:
codeBlocksCrcMatlab = scipy.io.loadmat(matlabFilesPath+'/cbsIn.mat')['cbsIn'].T
assert np.abs(codeBlocksCrc-codeBlocksCrcMatlab).sum()==0, "MISMATCH WITH MATLAB!!!"

print("CodeBlocks Shape (Including CRC):", codeBlocksCrc.shape)
print("liftingSize (Zc):                ", cwCodec.liftingSize)
print("setIndex (Zero-Based):           ", cwCodec.setIndex)
print("numFillerBits:                   ", cwCodec.numFillerBits)

CodeBlocks Shape (Including CRC): (2, 5280)
liftingSize (Zc):                 240
setIndex (Zero-Based):            7
numFillerBits:                    244
[5]:
print("Base Graph Shape:", cwCodec.baseGraph.shape)
print("8x8 sub-matrix at the \"Double Diagonal\" section:")
for r in cwCodec.baseGraph[0:8,22:30]: print("    " + "   ".join("%3d"%x for x in r))
Base Graph Shape: (46, 68)
8x8 sub-matrix at the "Double Diagonal" section:
      1     0    -1    -1    -1    -1    -1    -1
      0     0     0    -1    -1    -1    -1    -1
     -1    -1     0     0    -1    -1    -1    -1
      1    -1    -1     0    -1    -1    -1    -1
     -1    -1    -1    -1     0    -1    -1    -1
    180    -1    -1    -1    -1     0    -1    -1
     -1    -1    -1    -1    -1    -1     0    -1
     -1    -1    -1    -1    -1    -1    -1     0
[6]:
# Check the valid LDPC coded blocks:
# Do not puncture first 2 columns because we need the whole coded blocks for the
# "isValidCodedBlock" function below
testCodedBlocks = cwCodec.encodeCodeBlocks(codeBlocksCrc, puncture=False)

(cwCodec.isValidCodedBlock(testCodedBlocks[0]),
 cwCodec.isValidCodedBlock(testCodedBlocks[1]),
 cwCodec.isValidCodedBlock(np.zeros(68*cwCodec.liftingSize)),  # Always valid
 cwCodec.isValidCodedBlock(np.ones(68*cwCodec.liftingSize)))   # Intentionally Invalid

[6]:
(True, True, True, False)
[7]:
# Normal usage (1st 2 columns punctured, zero filler bits)
# Do segmentation
codeBlocksCrc = cwCodec.doSegmentation(tbWithCrc)
# Encoding:
codedBlocks = cwCodec.encodeCodeBlocks(codeBlocksCrc)
print("codedBlocks Shape:", codedBlocks.shape)

# NeoRadium does not set filler bits to -1. To match with MATLAB, we need to set these bits
# to -1. This is only needed when comparing encoder output (before rate matching) with MATLAB.
fillerStart = cwCodec.codeBlockSize-cwCodec.numFillerBits-2*cwCodec.liftingSize
codedBlocks[:, fillerStart : fillerStart+cwCodec.numFillerBits] = -1

# Compare results with MATLAB:
codedBlocksMatlab = scipy.io.loadmat(matlabFilesPath+'/enc.mat')['enc'].T
assert np.abs(codedBlocks-codedBlocksMatlab).sum()==0, "MISMATCH WITH MATLAB!!!"


codedBlocks Shape: (2, 15840)
[8]:
rateMatchedCodeBlocks = cwCodec.rateMatch(codedBlocks)
print("Rate-Matched coded blocks Shape:", rateMatchedCodeBlocks.shape)

# Compare results with MATLAB:
rateMatchedCodeBlocksMatlab = scipy.io.loadmat(matlabFilesPath+'/chIn.mat')['chIn'].T
assert np.abs(rateMatchedCodeBlocks-rateMatchedCodeBlocksMatlab).sum()==0, "MISMATCH WITH MATLAB!!!"


Rate-Matched coded blocks Shape: (22808,)
[9]:
# Do all of it with one call
rateMatchedCodeBlocks = cwCodec.encode(inBits)

# Compare results with MATLAB:
assert np.abs(rateMatchedCodeBlocks-rateMatchedCodeBlocksMatlab).sum()==0, "MISMATCH WITH MATLAB!!!"


[10]:
# Simple bipolar channel with no noise:
channelOutput = 1 - 2.0*rateMatchedCodeBlocks
[11]:
# Recover rate
rxCodedBlocks = cwCodec.recoverRate(channelOutput)

# Compare results with MATLAB:
rxCodedBlocksMatlab = scipy.io.loadmat(matlabFilesPath+'/raterec.mat')['raterec'].T
rxCodedBlocksMatlab[rxCodedBlocksMatlab==np.inf]=cwCodec.LARGE_LLR  # Replace inf with our LARGE_LLR
assert np.abs(rxCodedBlocks-rxCodedBlocksMatlab).sum()==0, "MISMATCH WITH MATLAB!!!"

rxCodedBlocks.shape

[11]:
(2, 15840)
[12]:
# Decode the rate-recovered message
rxCodeBlocks = cwCodec.decodeCodeBlocks(rxCodedBlocks)
rxCodeBlocks.shape
[12]:
(2, 5280)
[13]:
# Compare results with MATLAB:
rxCodeBlocksMatlab = scipy.io.loadmat(matlabFilesPath+'/decBits.mat')['decBits'].T
assert np.abs(rxCodeBlocks-rxCodeBlocksMatlab).sum()==0, "MISMATCH WITH MATLAB!!!"
[14]:
# Undo Segmentation and CRC checking
rxCodeBlocksWithoutCrc, crcMatch = cwCodec.checkCrcAndMerge(rxCodeBlocks)
print("CRC Matched:", crcMatch)

# Compare results with MATLAB:
rxCodeBlocksWithoutCrcMatlab = scipy.io.loadmat(matlabFilesPath+'/decBlk.mat')['decBlk'].T
assert np.abs(rxCodeBlocksWithoutCrc-rxCodeBlocksWithoutCrc).sum()==0, "MISMATCH WITH MATLAB!!!"
rxCodeBlocksWithoutCrc.shape
CRC Matched: [ True  True]
[14]:
(10024,)
[15]:
# The transport block CRC checking
print(cwCodec.checkCrc(rxCodeBlocksWithoutCrc,'24A'))
True
[16]:
# Compare with original input
assert np.abs(rxCodeBlocksWithoutCrc[:-24]-inBits).sum()==0, "MISMATCH WITH INPUT BITS!!!"
[17]:
# Do the whole decoding process in one call:
rxBlock, crcMatch = cwCodec.decode(channelOutput)
print("CRC Matched:", crcMatch)

# Compare results with MATLAB:
rxTxBlocksWithCrcMatlab = scipy.io.loadmat(matlabFilesPath+'/decBlk.mat')['decBlk'].T
assert np.abs(rxTxBlocksWithCrcMatlab[:,:-24]-rxBlock).sum()==0, "MISMATCH WITH MATLAB!!!"

# Compare with original input
assert np.abs(rxBlock-inBits).sum()==0, "MISMATCH WITH INPUT BITS!!!"
CRC Matched: [ True  True  True]
[ ]: