Polar Coding
This notebook demonstrates how to use the PolarEncoder and PolarDecoder classes for Polar encoding and decoding.
[1]:
import numpy as np
import scipy.io
import time
from neoradium import PolarEncoder, PolarDecoder, random, Modem
[2]:
payloadLen = 30 # A (Must be no larger than 1706)
rateMatchedLen = 120
ebNo = 0.8
coderate = (payloadLen+24)/rateMatchedLen # Effective code rate
bps = 2; # bits per symbol = 2 for QPSK
esNo = ebNo + 10*np.log10(bps) # energy per symbol over noise
snrdB = esNo + 10*np.log10(coderate) # SNR in dB
noiseVar = 1/(10**(snrdB/10))
noiseStd = np.sqrt(noiseVar)
print(f"Coderate: {coderate}")
print(f"SNR: {snrdB:.2f} dB")
print(f"Noise Variance: {noiseVar:.2f}")
print(f"Noise STD: {noiseStd:.2f}")
Coderate: 0.45
SNR: 0.34 dB
Noise Variance: 0.92
Noise STD: 0.96
[3]:
# Create a Polar encoder
polarEncoder = PolarEncoder(payloadLen, rateMatchedLen, 'dci')
polarEncoder.print()
Polar Encoder Properties:
dataType: DCI
payloadSize (A): 30
rateMatchedLen (E): 120
codeBlockSize (K): 54
polarCodeSize (N): 128
Max Log2(N) (nMax): 9
Segmentation (iSeg): Disabled
Code Block CRC (crcPoly): 24C
Input Interleaving (iIL): Enabled
Coded bit Interleaving (iBIL): Disabled
Parity-check bits (nPC, nPCwm): 0,0
[4]:
random.setSeed(123) # Make results reproducible
txpBlock = random.bits(30) # Create random bit stream
print("Transport Block Shape: ", txpBlock.shape)
print("Transport Block: ", "".join(str(x) for x in txpBlock))
# Perform segmentation
codeBlocks = polarEncoder.doSegmentation(txpBlock)
print("Code block Shape: ", codeBlocks.shape)
print("Code block: ", "".join(str(x) for x in codeBlocks[0]))
Transport Block Shape: (30,)
Transport Block: 111011110011101011110100010011
Code block Shape: (1, 54)
Code block: 111011110011101011110100010011010100001100000101110011
[5]:
# Perform Polar encoding
codedBlocks = polarEncoder.encode(codeBlocks)
print("Coded block Shape: ", codedBlocks.shape)
print("Coded block first 10 bits:", "".join(str(x) for x in codedBlocks[0][:10]))
Coded block Shape: (1, 128)
Coded block first 10 bits: 1110101110
[6]:
# Perform rate matching
rateMatchedCodedBlocks = polarEncoder.rateMatch(codedBlocks)
print("Rate-Matched Shape: ", rateMatchedCodedBlocks.shape)
print("First 10 bits: ", "".join(str(x) for x in rateMatchedCodedBlocks[0][:10]))
Rate-Matched Shape: (1, 120)
First 10 bits: 1110101110
[7]:
# QPSK modulation
modulated = Modem('QPSK').modulate(rateMatchedCodedBlocks)
print("modulated Shape: ", modulated.shape)
modulated Shape: (1, 60)
[8]:
noise = random.awgn(modulated.shape, noiseStd)
# Add noise to the modulated signal
rxSymbols = modulated + noise
print("First 5 Rx Symbols:\n", rxSymbols[0,:5])
First 5 Rx Symbols:
[-0.0815589 -0.31480735j -1.13975742+1.07551113j -0.92231974+0.48795545j
-0.64105509-1.74439268j 0.10329524+0.25091831j]
[9]:
# Demodulation: calculate LLR values from symbols
llrs = Modem('QPSK').getLLRs(rxSymbols, noiseVar)
print("LLR Shape: ", llrs.shape)
print("First 10 LLRs: \n", llrs[0,:10])
LLR Shape: (1, 120)
First 10 LLRs:
[-0.2496082 -0.96345708 -3.48818838 3.29156482 -2.82272784 1.49337086
-1.96192704 -5.3386538 0.31613154 0.76792686]
[10]:
# Hard decision
hardCodeBlocks = 1*(llrs<0)
print("Hard Decision first 10 bits: ", "".join(str(x) for x in hardCodeBlocks[0][:10]))
print("Original Code block first 10 bits: ", "".join(str(x) for x in codeBlocks[0][:10]))
Hard Decision first 10 bits: 1110101100
Original Code block first 10 bits: 1110111100
[11]:
# Create a Polar decoder
polarDecoder = PolarDecoder(payloadLen, rateMatchedLen, 'dci', sclListSize=8, useMinsum=True)
polarDecoder.print()
Polar Decoder Properties:
dataType: DCI
payloadSize (A): 30
rateMatchedLen (E): 120
codeBlockSize (K): 54
polarCodeSize (N): 128
Max Log2(N) (nMax): 9
Segmentation (iSeg): Disabled
Code Block CRC (crcPoly): 24C
Input Interleaving (iIL): Enabled
Coded bit Interleaving (iBIL): Disabled
Parity-check bits (nPC, nPCwm): 0,0
SCL List Size: 8
Min-sum Approximation: Enabled
[12]:
# Perform rate recovery
rateRecoveredRxBlocks = polarDecoder.recoverRate(llrs)
print("Rate Recovered Shape: ", rateRecoveredRxBlocks.shape)
print("First 10 LLRs: \n", rateRecoveredRxBlocks[0,:10])
Rate Recovered Shape: (1, 128)
First 10 LLRs:
[-0.2496082 -0.96345708 -3.48818838 3.29156482 -2.82272784 1.49337086
-1.96192704 -5.3386538 0.31613154 0.76792686]
[13]:
# Decode the rate-recovered transport blocks
decTxBlock, numCrcErrors = polarDecoder.decode(rateRecoveredRxBlocks)
print("Number of CRC Errors: ", numCrcErrors)
print("Decoded Transport Block Shape: ", decTxBlock.shape)
print("Decoded Transport Block: ", "".join(str(x) for x in decTxBlock))
print("Original Transport Block: ", "".join(str(x) for x in txpBlock))
Number of CRC Errors: 0
Decoded Transport Block Shape: (30,)
Decoded Transport Block: 111011110011101011110100010011
Original Transport Block: 111011110011101011110100010011
[ ]:
[ ]: