mirror of
https://github.com/jhshi/openofdm.git
synced 2024-12-27 17:19:23 +00:00
238 lines
8.5 KiB
Python
238 lines
8.5 KiB
Python
|
|
|
|
# Authors: Veeresh Taranalli <veeresht@gmail.com>
|
|
# License: BSD 3-Clause
|
|
|
|
""" LDPC Codes """
|
|
import numpy as np
|
|
|
|
__all__ = ['get_ldpc_code_params, ldpc_bp_decode']
|
|
|
|
MAX_POS_LLR = 38.0
|
|
MIN_NEG_LLR = -38.0
|
|
|
|
def get_ldpc_code_params(ldpc_design_filename):
|
|
"""
|
|
Extract parameters from LDPC code design file.
|
|
|
|
Parameters
|
|
----------
|
|
ldpc_design_filename : string
|
|
Filename of the LDPC code design file.
|
|
|
|
Returns
|
|
-------
|
|
ldpc_code_params : dictionary
|
|
Parameters of the LDPC code.
|
|
"""
|
|
|
|
ldpc_design_file = open(ldpc_design_filename)
|
|
|
|
ldpc_code_params = {}
|
|
|
|
[n_vnodes, n_cnodes] = [int(x) for x in ldpc_design_file.readline().split(' ')]
|
|
[max_vnode_deg, max_cnode_deg] = [int(x) for x in ldpc_design_file.readline().split(' ')]
|
|
vnode_deg_list = np.array([int(x) for x in ldpc_design_file.readline().split(' ')[:-1]], np.int32)
|
|
cnode_deg_list = np.array([int(x) for x in ldpc_design_file.readline().split(' ')[:-1]], np.int32)
|
|
|
|
cnode_adj_list = -np.ones([n_cnodes, max_cnode_deg], int)
|
|
vnode_adj_list = -np.ones([n_vnodes, max_vnode_deg], int)
|
|
|
|
for vnode_idx in range(n_vnodes):
|
|
vnode_adj_list[vnode_idx, 0:vnode_deg_list[vnode_idx]] = \
|
|
np.array([int(x)-1 for x in ldpc_design_file.readline().split('\t')])
|
|
|
|
for cnode_idx in range(n_cnodes):
|
|
cnode_adj_list[cnode_idx, 0:cnode_deg_list[cnode_idx]] = \
|
|
np.array([int(x)-1 for x in ldpc_design_file.readline().split('\t')])
|
|
|
|
cnode_vnode_map = -np.ones([n_cnodes, max_cnode_deg], int)
|
|
vnode_cnode_map = -np.ones([n_vnodes, max_vnode_deg], int)
|
|
cnode_list = np.arange(n_cnodes)
|
|
vnode_list = np.arange(n_vnodes)
|
|
|
|
for cnode in range(n_cnodes):
|
|
for i, vnode in enumerate(cnode_adj_list[cnode, 0:cnode_deg_list[cnode]]):
|
|
cnode_vnode_map[cnode, i] = cnode_list[np.where(vnode_adj_list[vnode, :] == cnode)]
|
|
|
|
for vnode in range(n_vnodes):
|
|
for i, cnode in enumerate(vnode_adj_list[vnode, 0:vnode_deg_list[vnode]]):
|
|
vnode_cnode_map[vnode, i] = vnode_list[np.where(cnode_adj_list[cnode, :] == vnode)]
|
|
|
|
|
|
cnode_adj_list_1d = cnode_adj_list.flatten().astype(np.int32)
|
|
vnode_adj_list_1d = vnode_adj_list.flatten().astype(np.int32)
|
|
cnode_vnode_map_1d = cnode_vnode_map.flatten().astype(np.int32)
|
|
vnode_cnode_map_1d = vnode_cnode_map.flatten().astype(np.int32)
|
|
|
|
pmat = np.zeros([n_cnodes, n_vnodes], int)
|
|
for cnode_idx in range(n_cnodes):
|
|
pmat[cnode_idx, cnode_adj_list[cnode_idx, :]] = 1
|
|
|
|
ldpc_code_params['n_vnodes'] = n_vnodes
|
|
ldpc_code_params['n_cnodes'] = n_cnodes
|
|
ldpc_code_params['max_cnode_deg'] = max_cnode_deg
|
|
ldpc_code_params['max_vnode_deg'] = max_vnode_deg
|
|
ldpc_code_params['cnode_adj_list'] = cnode_adj_list_1d
|
|
ldpc_code_params['cnode_vnode_map'] = cnode_vnode_map_1d
|
|
ldpc_code_params['vnode_adj_list'] = vnode_adj_list_1d
|
|
ldpc_code_params['vnode_cnode_map'] = vnode_cnode_map_1d
|
|
ldpc_code_params['cnode_deg_list'] = cnode_deg_list
|
|
ldpc_code_params['vnode_deg_list'] = vnode_deg_list
|
|
|
|
ldpc_design_file.close()
|
|
|
|
return ldpc_code_params
|
|
|
|
def _limit_llr(in_llr):
|
|
|
|
out_llr = in_llr
|
|
|
|
if in_llr > MAX_POS_LLR:
|
|
out_llr = MAX_POS_LLR
|
|
|
|
if in_llr < MIN_NEG_LLR:
|
|
out_llr = MIN_NEG_LLR
|
|
|
|
return out_llr
|
|
|
|
def sum_product_update(cnode_idx, cnode_adj_list, cnode_deg_list, cnode_msgs,
|
|
vnode_msgs, cnode_vnode_map, max_cnode_deg, max_vnode_deg):
|
|
|
|
start_idx = cnode_idx*max_cnode_deg
|
|
offset = cnode_deg_list[cnode_idx]
|
|
vnode_list = cnode_adj_list[start_idx:start_idx+offset]
|
|
vnode_list_msgs_tanh = np.tanh(vnode_msgs[vnode_list*max_vnode_deg +
|
|
cnode_vnode_map[start_idx:start_idx+offset]]/2.0)
|
|
msg_prod = np.prod(vnode_list_msgs_tanh)
|
|
|
|
# Compute messages on outgoing edges using the incoming message product
|
|
cnode_msgs[start_idx:start_idx+offset]= 2.0*np.arctanh(msg_prod/vnode_list_msgs_tanh)
|
|
|
|
|
|
def min_sum_update(cnode_idx, cnode_adj_list, cnode_deg_list, cnode_msgs,
|
|
vnode_msgs, cnode_vnode_map, max_cnode_deg, max_vnode_deg):
|
|
|
|
start_idx = cnode_idx*max_cnode_deg
|
|
offset = cnode_deg_list[cnode_idx]
|
|
vnode_list = cnode_adj_list[start_idx:start_idx+offset]
|
|
vnode_list_msgs = vnode_msgs[vnode_list*max_vnode_deg +
|
|
cnode_vnode_map[start_idx:start_idx+offset]]
|
|
vnode_list_msgs = np.ma.array(vnode_list_msgs, mask=False)
|
|
|
|
# Compute messages on outgoing edges using the incoming messages
|
|
for i in range(start_idx, start_idx+offset):
|
|
vnode_list_msgs.mask[i-start_idx] = True
|
|
cnode_msgs[i] = np.prod(np.sign(vnode_list_msgs))*np.min(np.abs(vnode_list_msgs))
|
|
vnode_list_msgs.mask[i-start_idx] = False
|
|
#print cnode_msgs[i]
|
|
|
|
def ldpc_bp_decode(llr_vec, ldpc_code_params, decoder_algorithm, n_iters):
|
|
"""
|
|
LDPC Decoder using Belief Propagation (BP).
|
|
|
|
Parameters
|
|
----------
|
|
llr_vec : 1D array of float
|
|
Received codeword LLR values from the channel.
|
|
|
|
ldpc_code_params : dictionary
|
|
Parameters of the LDPC code.
|
|
|
|
decoder_algorithm: string
|
|
Specify the decoder algorithm type.
|
|
SPA for Sum-Product Algorithm
|
|
MSA for Min-Sum Algorithm
|
|
|
|
n_iters : int
|
|
Max. number of iterations of decoding to be done.
|
|
|
|
Returns
|
|
-------
|
|
dec_word : 1D array of 0's and 1's
|
|
The codeword after decoding.
|
|
|
|
out_llrs : 1D array of float
|
|
LLR values corresponding to the decoded output.
|
|
"""
|
|
|
|
n_cnodes = ldpc_code_params['n_cnodes']
|
|
n_vnodes = ldpc_code_params['n_vnodes']
|
|
max_cnode_deg = ldpc_code_params['max_cnode_deg']
|
|
max_vnode_deg = ldpc_code_params['max_vnode_deg']
|
|
cnode_adj_list = ldpc_code_params['cnode_adj_list']
|
|
cnode_vnode_map = ldpc_code_params['cnode_vnode_map']
|
|
vnode_adj_list = ldpc_code_params['vnode_adj_list']
|
|
vnode_cnode_map = ldpc_code_params['vnode_cnode_map']
|
|
cnode_deg_list = ldpc_code_params['cnode_deg_list']
|
|
vnode_deg_list = ldpc_code_params['vnode_deg_list']
|
|
|
|
dec_word = np.zeros(n_vnodes, int)
|
|
out_llrs = np.zeros(n_vnodes, int)
|
|
|
|
cnode_msgs = np.zeros(n_cnodes*max_cnode_deg)
|
|
vnode_msgs = np.zeros(n_vnodes*max_vnode_deg)
|
|
|
|
_limit_llr_v = np.vectorize(_limit_llr)
|
|
|
|
if decoder_algorithm == 'SPA':
|
|
check_node_update = sum_product_update
|
|
elif decoder_algorithm == 'MSA':
|
|
check_node_update = min_sum_update
|
|
else:
|
|
raise NameError('Please input a valid decoder_algorithm string.')
|
|
|
|
# Initialize vnode messages with the LLR values received
|
|
for vnode_idx in range(n_vnodes):
|
|
start_idx = vnode_idx*max_vnode_deg
|
|
offset = vnode_deg_list[vnode_idx]
|
|
vnode_msgs[start_idx : start_idx+offset] = llr_vec[vnode_idx]
|
|
|
|
# Main loop of Belief Propagation (BP) decoding iterations
|
|
for iter_cnt in range(n_iters):
|
|
|
|
continue_flag = 0
|
|
|
|
# Check Node Update
|
|
for cnode_idx in range(n_cnodes):
|
|
|
|
check_node_update(cnode_idx, cnode_adj_list, cnode_deg_list, cnode_msgs,
|
|
vnode_msgs, cnode_vnode_map, max_cnode_deg, max_vnode_deg)
|
|
|
|
# Variable Node Update
|
|
for vnode_idx in range(n_vnodes):
|
|
|
|
# Compute sum of all incoming messages at the variable node
|
|
start_idx = vnode_idx*max_vnode_deg
|
|
offset = vnode_deg_list[vnode_idx]
|
|
cnode_list = vnode_adj_list[start_idx:start_idx+offset]
|
|
cnode_list_msgs = cnode_msgs[cnode_list*max_cnode_deg + vnode_cnode_map[start_idx:start_idx+offset]]
|
|
msg_sum = np.sum(cnode_list_msgs)
|
|
|
|
# Compute messages on outgoing edges using the incoming message sum
|
|
vnode_msgs[start_idx:start_idx+offset] = _limit_llr_v(llr_vec[vnode_idx] + msg_sum -
|
|
cnode_list_msgs)
|
|
|
|
# Update output LLRs and decoded word
|
|
out_llrs[vnode_idx] = llr_vec[vnode_idx] + msg_sum
|
|
if out_llrs[vnode_idx] > 0:
|
|
dec_word[vnode_idx] = 0
|
|
else:
|
|
dec_word[vnode_idx] = 1
|
|
|
|
# Compute if early termination using parity check matrix
|
|
for cnode_idx in range(n_cnodes):
|
|
p_sum = 0
|
|
for i in range(cnode_deg_list[cnode_idx]):
|
|
p_sum ^= dec_word[cnode_adj_list[cnode_idx*max_cnode_deg + i]]
|
|
|
|
if p_sum != 0:
|
|
continue_flag = 1
|
|
break
|
|
|
|
# Stop iterations
|
|
if continue_flag == 0:
|
|
break
|
|
|
|
return dec_word, out_llrs
|