{ "cells": [ { "cell_type": "markdown", "id": "22bf3c88", "metadata": {}, "source": [ "# Comparing the LDPC results with MATLAB\n", "Applying LDPC encoding/decoding on random transport blocks and comparing the results with the equivalent MATLAB code \"MatlabFiles/LDPC.mlx\". [Here](MatlabFiles/LDPC.html) is the execution results of this code in MATLAB." ] }, { "cell_type": "code", "execution_count": 1, "id": "0fc98acd", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import scipy.io\n", "import time\n", "\n", "from neoradium import LdpcCodec\n", "\n", "matlabFilesPath = \"./MatlabFiles\"" ] }, { "cell_type": "code", "execution_count": 2, "id": "0d906bd2", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "First 10 bits: [0 0 1 1 0 0 1 1 0 1]\n", "\n", "LDPC Encode/Decode Properties:\n", " Num layers: 1\n", " Num codewords: 1\n", " numIter: 5\n", " nRef: 0\n", " Modulation: QPSK\n", " Coderate: 449/1024\n", " TBS: 10000\n", " numLayers: 1\n", " Base Graph: 1\n", " Code Block Size: 5280\n", " Num Code Blocks: 2\n", " Lifting Size: 240\n", "\n" ] } ], "source": [ "# Read input bits from MATLAB-generated file\n", "inBits = scipy.io.loadmat(matlabFilesPath+'/in.mat')['in'].reshape(-1)\n", "print(f\"First 10 bits: {inBits[:10]}\")\n", "\n", "# Create an LDPC encoder object\n", "ldpc = LdpcCodec(modulations='QPSK', coderates=449/1024, txBlockSizes=len(inBits), numLayers=1)\n", "print(ldpc)" ] }, { "cell_type": "code", "execution_count": 3, "id": "36d3e1de", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(10024,)" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Transport block padded with a 24-bit CRC\n", "cwCodec = ldpc.cwCodecs[0] # The LDPC codec for the first codeword\n", "tbWithCrc = cwCodec.appendCrc(inBits,'24A')\n", "tbWithCrc.shape" ] }, { "cell_type": "code", "execution_count": 4, "id": "762447be", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CodeBlocks Shape (Including CRC): (2, 5280)\n", "liftingSize (Zc): 240\n", "setIndex (Zero-Based): 7\n", "numFillerBits: 244\n" ] } ], "source": [ "# Do the segmentation:\n", "codeBlocksCrc = cwCodec.doSegmentation(tbWithCrc)\n", "\n", "# NeoRadium does not set filler bits to -1. To match with MATLAB, we need to set these bits \n", "# to -1. This is only needed when comparing segmentation results with MATLAB.\n", "fillerStart = cwCodec.codeBlockSize-cwCodec.numFillerBits\n", "codeBlocksCrc[:, fillerStart : fillerStart+cwCodec.numFillerBits] = -1\n", "\n", "# Compare results with MATLAB:\n", "codeBlocksCrcMatlab = scipy.io.loadmat(matlabFilesPath+'/cbsIn.mat')['cbsIn'].T\n", "assert np.abs(codeBlocksCrc-codeBlocksCrcMatlab).sum()==0, \"MISMATCH WITH MATLAB!!!\"\n", "\n", "print(\"CodeBlocks Shape (Including CRC):\", codeBlocksCrc.shape)\n", "print(\"liftingSize (Zc): \", cwCodec.liftingSize)\n", "print(\"setIndex (Zero-Based): \", cwCodec.setIndex)\n", "print(\"numFillerBits: \", cwCodec.numFillerBits)\n" ] }, { "cell_type": "code", "execution_count": 5, "id": "89a47c7b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Base Graph Shape: (46, 68)\n", "8x8 sub-matrix at the \"Double Diagonal\" section:\n", " 1 0 -1 -1 -1 -1 -1 -1\n", " 0 0 0 -1 -1 -1 -1 -1\n", " -1 -1 0 0 -1 -1 -1 -1\n", " 1 -1 -1 0 -1 -1 -1 -1\n", " -1 -1 -1 -1 0 -1 -1 -1\n", " 180 -1 -1 -1 -1 0 -1 -1\n", " -1 -1 -1 -1 -1 -1 0 -1\n", " -1 -1 -1 -1 -1 -1 -1 0\n" ] } ], "source": [ "print(\"Base Graph Shape:\", cwCodec.baseGraph.shape)\n", "print(\"8x8 sub-matrix at the \\\"Double Diagonal\\\" section:\")\n", "for r in cwCodec.baseGraph[0:8,22:30]: print(\" \" + \" \".join(\"%3d\"%x for x in r))" ] }, { "cell_type": "code", "execution_count": 6, "id": "81369392", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(True, True, True, False)" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Check the valid LDPC coded blocks:\n", "# Do not puncture first 2 columns because we need the whole coded blocks for the \n", "# \"isValidCodedBlock\" function below\n", "testCodedBlocks = cwCodec.encodeCodeBlocks(codeBlocksCrc, puncture=False)\n", "\n", "(cwCodec.isValidCodedBlock(testCodedBlocks[0]),\n", " cwCodec.isValidCodedBlock(testCodedBlocks[1]),\n", " cwCodec.isValidCodedBlock(np.zeros(68*cwCodec.liftingSize)), # Always valid\n", " cwCodec.isValidCodedBlock(np.ones(68*cwCodec.liftingSize))) # Intentionally Invalid\n" ] }, { "cell_type": "code", "execution_count": 7, "id": "ca7abd99", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "codedBlocks Shape: (2, 15840)\n" ] } ], "source": [ "# Normal usage (1st 2 columns punctured, zero filler bits)\n", "# Do segmentation\n", "codeBlocksCrc = cwCodec.doSegmentation(tbWithCrc)\n", "# Encoding:\n", "codedBlocks = cwCodec.encodeCodeBlocks(codeBlocksCrc)\n", "print(\"codedBlocks Shape:\", codedBlocks.shape)\n", "\n", "# NeoRadium does not set filler bits to -1. To match with MATLAB, we need to set these bits \n", "# to -1. This is only needed when comparing encoder output (before rate matching) with MATLAB.\n", "fillerStart = cwCodec.codeBlockSize-cwCodec.numFillerBits-2*cwCodec.liftingSize\n", "codedBlocks[:, fillerStart : fillerStart+cwCodec.numFillerBits] = -1\n", "\n", "# Compare results with MATLAB:\n", "codedBlocksMatlab = scipy.io.loadmat(matlabFilesPath+'/enc.mat')['enc'].T\n", "assert np.abs(codedBlocks-codedBlocksMatlab).sum()==0, \"MISMATCH WITH MATLAB!!!\" \n", "\n" ] }, { "cell_type": "code", "execution_count": 8, "id": "22f117e8", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Rate-Matched coded blocks Shape: (22808,)\n" ] } ], "source": [ "rateMatchedCodeBlocks = cwCodec.rateMatch(codedBlocks)\n", "print(\"Rate-Matched coded blocks Shape:\", rateMatchedCodeBlocks.shape)\n", "\n", "# Compare results with MATLAB:\n", "rateMatchedCodeBlocksMatlab = scipy.io.loadmat(matlabFilesPath+'/chIn.mat')['chIn'].T\n", "assert np.abs(rateMatchedCodeBlocks-rateMatchedCodeBlocksMatlab).sum()==0, \"MISMATCH WITH MATLAB!!!\" \n", "\n" ] }, { "cell_type": "code", "execution_count": 9, "id": "53fb09a0", "metadata": {}, "outputs": [], "source": [ "# Do all of it with one call\n", "rateMatchedCodeBlocks = cwCodec.encode(inBits)\n", "\n", "# Compare results with MATLAB:\n", "assert np.abs(rateMatchedCodeBlocks-rateMatchedCodeBlocksMatlab).sum()==0, \"MISMATCH WITH MATLAB!!!\" \n", "\n" ] }, { "cell_type": "code", "execution_count": 10, "id": "ad7c6371", "metadata": {}, "outputs": [], "source": [ "# Simple bipolar channel with no noise:\n", "channelOutput = 1 - 2.0*rateMatchedCodeBlocks" ] }, { "cell_type": "code", "execution_count": 11, "id": "622ee6fd", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(2, 15840)" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Recover rate\n", "rxCodedBlocks = cwCodec.recoverRate(channelOutput)\n", "\n", "# Compare results with MATLAB:\n", "rxCodedBlocksMatlab = scipy.io.loadmat(matlabFilesPath+'/raterec.mat')['raterec'].T\n", "rxCodedBlocksMatlab[rxCodedBlocksMatlab==np.inf]=cwCodec.LARGE_LLR # Replace inf with our LARGE_LLR\n", "assert np.abs(rxCodedBlocks-rxCodedBlocksMatlab).sum()==0, \"MISMATCH WITH MATLAB!!!\" \n", "\n", "rxCodedBlocks.shape\n" ] }, { "cell_type": "code", "execution_count": 12, "id": "04b77059", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(2, 5280)" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Decode the rate-recovered message\n", "rxCodeBlocks = cwCodec.decodeCodeBlocks(rxCodedBlocks)\n", "rxCodeBlocks.shape" ] }, { "cell_type": "code", "execution_count": 13, "id": "1d9893d6", "metadata": {}, "outputs": [], "source": [ "# Compare results with MATLAB:\n", "rxCodeBlocksMatlab = scipy.io.loadmat(matlabFilesPath+'/decBits.mat')['decBits'].T\n", "assert np.abs(rxCodeBlocks-rxCodeBlocksMatlab).sum()==0, \"MISMATCH WITH MATLAB!!!\" " ] }, { "cell_type": "code", "execution_count": 14, "id": "eebdd6c5", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CRC Matched: [ True True]\n" ] }, { "data": { "text/plain": [ "(10024,)" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Undo Segmentation and CRC checking\n", "rxCodeBlocksWithoutCrc, crcMatch = cwCodec.checkCrcAndMerge(rxCodeBlocks)\n", "print(\"CRC Matched:\", crcMatch)\n", "\n", "# Compare results with MATLAB:\n", "rxCodeBlocksWithoutCrcMatlab = scipy.io.loadmat(matlabFilesPath+'/decBlk.mat')['decBlk'].T\n", "assert np.abs(rxCodeBlocksWithoutCrc-rxCodeBlocksWithoutCrc).sum()==0, \"MISMATCH WITH MATLAB!!!\" \n", "rxCodeBlocksWithoutCrc.shape" ] }, { "cell_type": "code", "execution_count": 15, "id": "9dad40f6", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n" ] } ], "source": [ "# The transport block CRC checking\n", "print(cwCodec.checkCrc(rxCodeBlocksWithoutCrc,'24A'))" ] }, { "cell_type": "code", "execution_count": 16, "id": "9334c2c1", "metadata": {}, "outputs": [], "source": [ "# Compare with original input\n", "assert np.abs(rxCodeBlocksWithoutCrc[:-24]-inBits).sum()==0, \"MISMATCH WITH INPUT BITS!!!\"" ] }, { "cell_type": "code", "execution_count": 17, "id": "8f2a34b1", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CRC Matched: [ True True True]\n" ] } ], "source": [ "# Do the whole decoding process in one call:\n", "rxBlock, crcMatch = cwCodec.decode(channelOutput)\n", "print(\"CRC Matched:\", crcMatch)\n", "\n", "# Compare results with MATLAB:\n", "rxTxBlocksWithCrcMatlab = scipy.io.loadmat(matlabFilesPath+'/decBlk.mat')['decBlk'].T\n", "assert np.abs(rxTxBlocksWithCrcMatlab[:,:-24]-rxBlock).sum()==0, \"MISMATCH WITH MATLAB!!!\" \n", "\n", "# Compare with original input\n", "assert np.abs(rxBlock-inBits).sum()==0, \"MISMATCH WITH INPUT BITS!!!\"" ] }, { "cell_type": "code", "execution_count": null, "id": "c1791453-0717-49e3-bc28-e5803631ed68", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.10" } }, "nbformat": 4, "nbformat_minor": 5 }