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 LdpcEncoder, LdpcDecoder

matlabFilesPath = "./MatlabFiles"
[2]:
# Create an LDPC encoder object
ldpcEncoder = LdpcEncoder(baseGraphNo=1, modulation='QPSK', rv=0, txLayers=1, nRef=0, targetRate=449/1024)
[3]:
# Read input bits from Matlab-generated file
inBits = scipy.io.loadmat(matlabFilesPath+'/in.mat')['in'].reshape(-1)
inBits[:10]
[3]:
array([0, 0, 1, 1, 0, 0, 1, 1, 0, 1], dtype=int8)
[4]:
# Transport block padded with a 24-bit CRC
tbWithCrc = ldpcEncoder.appendCrc(inBits,'24A')
tbWithCrc.shape
[4]:
(10024,)
[5]:
# Do the segmentation:
# To compare with Matlab, we need to use -1 for filler bits
codeBlocksCrc = ldpcEncoder.doSegmentation(tbWithCrc, fillerBit=-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):                ", ldpcEncoder.liftingSize)
print("setIndex (Zero-Based):           ", ldpcEncoder.setIndex)
print("numFillerBits:                   ", ldpcEncoder.numFillerBits)

CodeBlocks Shape (Including CRC): (2, 5280)
liftingSize (Zc):                 240
setIndex (Zero-Based):            7
numFillerBits:                    244
[6]:
print("Base Graph Shape:", ldpcEncoder.baseGraph.shape)
print("8x8 sub-matrix at the \"Double Diagonal\" section:")
for r in ldpcEncoder.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
[7]:
# Check the valid LDPC codewords:
# Set Filler bits to zero before encoding and do not puncture first 2 columns
codeBlocksCrc = ldpcEncoder.setFillerBits(codeBlocksCrc, 0)
testCodeWords = ldpcEncoder.encode(codeBlocksCrc, zeroFillerBits=True, puncture=False)

(ldpcEncoder.isValidCodeword(testCodeWords[0]),
 ldpcEncoder.isValidCodeword(testCodeWords[1]),
 ldpcEncoder.isValidCodeword(np.zeros(68*ldpcEncoder.liftingSize)),  # Always valid
 ldpcEncoder.isValidCodeword(np.ones(68*ldpcEncoder.liftingSize)))   # Intentionally Invalid

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

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


codeWords Shape: (2, 15840)
[9]:
rateMatchedCodeWords = ldpcEncoder.rateMatch(codeWords)
print("Rate-Matched codeWords Shape:", rateMatchedCodeWords.shape)

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


Rate-Matched codeWords Shape: (22808,)
[10]:
# Do all of it with one call end to end
rateMatchedCodeWords = ldpcEncoder.getRateMatchedCodeWords(inBits)

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

[11]:
# Simple bipolar channel with no noise:
channelOutput = 1 - 2.0*rateMatchedCodeWords
[12]:
# Create an LDPC decoder
ldpcDecoder = LdpcDecoder(baseGraphNo=1, modulation='QPSK', rv=0, txLayers=1, nRef=0);

# Recover rate
rxCodedBlocks = ldpcDecoder.recoverRate(channelOutput, len(inBits))

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

rxCodedBlocks.shape

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

# Compare results with matlab:
rxDecodedWordsWithoutCrcMatlab = scipy.io.loadmat(matlabFilesPath+'/decBlk.mat')['decBlk'].T
assert np.abs(rxDecodedWordsWithoutCrc-rxDecodedWordsWithoutCrcMatlab).sum()==0, "MISMATCH WITH MATLAB!!!"
rxDecodedWordsWithoutCrc.shape
CRC Matched: [ True  True]
[15]:
(10024,)
[16]:
# The transport block CRC checking
print(ldpcDecoder.checkCrc(rxDecodedWordsWithoutCrc,'24A'))
True
[17]:
# Compare with original input
assert np.abs(rxDecodedWordsWithoutCrc[:-24]-inBits).sum()==0, "MISMATCH WITH MATLAB!!!"
[ ]:

[ ]: