use alloc::vec;
use alloc::vec::Vec;
#[cfg(not(feature = "std"))]
use num_traits::Float;
use super::params::{Ldpc174_91Params, LdpcParams};
use super::{LDPC_K, LDPC_N};
const NCW: usize = 3;
#[inline]
fn platanh(x: f32) -> f32 {
if x.abs() > 0.999_999_9 {
x.signum() * 4.6
} else {
x.atanh()
}
}
pub fn crc14(data: &[u8]) -> u16 {
let mut crc: u16 = 0;
for &byte in data {
for i in (0..8).rev() {
let bit = (byte >> i) & 1;
let msb = (crc >> 13) & 1;
crc = ((crc << 1) | bit as u16) & 0x3FFF;
if msb != 0 {
crc ^= 0x2757;
}
}
}
crc
}
pub fn check_crc14(decoded: &[u8]) -> bool {
if decoded.len() != LDPC_K {
return false;
}
let mut bytes = [0u8; 12];
for (i, &bit) in decoded[..77].iter().enumerate() {
let byte_idx = i / 8;
let bit_pos = 7 - (i % 8);
bytes[byte_idx] |= (bit & 1) << bit_pos;
}
let computed = crc14(&bytes);
let mut received: u16 = 0;
for &bit in &decoded[77..91] {
received = (received << 1) | (bit as u16 & 1);
}
computed == received
}
pub struct BpResult {
pub message77: [u8; 77],
pub info: Vec<u8>,
pub codeword: Vec<u8>,
pub hard_errors: u32,
pub iterations: u32,
}
pub fn bp_decode_generic<P: LdpcParams>(
llr: &[f32],
ap_mask: Option<&[bool]>,
max_iter: u32,
verify: Option<fn(&[u8]) -> bool>,
) -> Option<BpResult> {
debug_assert_eq!(llr.len(), P::N, "llr length must equal P::N");
if let Some(m) = ap_mask {
debug_assert_eq!(m.len(), P::N, "ap_mask length must equal P::N");
}
let n = P::N;
let m_checks = P::M;
let k = P::K;
let max_row = P::MAX_ROW;
let mut tov = vec![0f32; n * NCW];
let mut toc = vec![0f32; m_checks * max_row];
let mut tanhtoc = vec![0f32; m_checks * max_row];
let mut zn = vec![0f32; n];
let mut cw = vec![0u8; n];
for j in 0..m_checks {
let nrw_j = P::nrw(j) as usize;
for i in 0..nrw_j {
let bit = P::nm(j, i) as usize;
toc[j * max_row + i] = llr[bit];
}
}
let mut ncnt = 0u32;
let mut nclast = 0u32;
for iter in 0..=max_iter {
for i in 0..n {
let ap = ap_mask.is_some_and(|mm| mm[i]);
if !ap {
let mut sum = 0.0f32;
for k_ in 0..NCW {
sum += tov[i * NCW + k_];
}
zn[i] = llr[i] + sum;
} else {
zn[i] = llr[i];
}
}
for i in 0..n {
cw[i] = if zn[i] > 0.0 { 1 } else { 0 };
}
let mut ncheck = 0u32;
for i in 0..m_checks {
let nrw_i = P::nrw(i) as usize;
let mut parity = 0u8;
for s in 0..nrw_i {
parity ^= cw[P::nm(i, s) as usize];
}
if parity != 0 {
ncheck += 1;
}
}
if ncheck == 0 {
let mut decoded = vec![0u8; k];
decoded.copy_from_slice(&cw[..k]);
let accept = match verify {
Some(f) => f(&decoded),
None => true,
};
if accept {
let mut hard_errors = 0u32;
for i in 0..n {
if (cw[i] == 1) != (llr[i] > 0.0) {
hard_errors += 1;
}
}
let mut message77 = [0u8; 77];
message77.copy_from_slice(&decoded[..77]);
return Some(BpResult {
message77,
info: decoded,
codeword: cw,
hard_errors,
iterations: iter,
});
}
}
if iter > 0 {
if ncheck < nclast {
ncnt = 0;
} else {
ncnt += 1;
}
if ncnt >= 5 && iter >= 10 && ncheck > 15 {
return None;
}
}
nclast = ncheck;
for j in 0..m_checks {
let nrw_j = P::nrw(j) as usize;
for i in 0..nrw_j {
let ibj = P::nm(j, i) as usize;
let mut msg = zn[ibj];
let mn_ibj = P::mn(ibj);
for kk in 0..NCW {
if mn_ibj[kk] as usize == j {
msg -= tov[ibj * NCW + kk];
}
}
toc[j * max_row + i] = msg;
}
}
for i in 0..m_checks {
let nrw_i = P::nrw(i) as usize;
for k_ in 0..nrw_i {
tanhtoc[i * max_row + k_] = (-toc[i * max_row + k_] / 2.0).tanh();
}
}
for j in 0..n {
let mn_j = P::mn(j);
for k_ in 0..NCW {
let ichk = mn_j[k_] as usize;
let nrw_ichk = P::nrw(ichk) as usize;
let mut tmn = 1.0f32;
for s in 0..nrw_ichk {
let bit = P::nm(ichk, s) as usize;
if bit != j {
tmn *= tanhtoc[ichk * max_row + s];
}
}
tov[j * NCW + k_] = 2.0 * platanh(-tmn);
}
}
}
None
}
pub fn bp_decode(
llr: &[f32; LDPC_N],
ap_mask: Option<&[bool; LDPC_N]>,
max_iter: u32,
verify: Option<fn(&[u8]) -> bool>,
) -> Option<BpResult> {
let ap_slice: Option<&[bool]> = ap_mask.map(|a| a.as_slice());
bp_decode_generic::<Ldpc174_91Params>(llr.as_slice(), ap_slice, max_iter, verify)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decode_perfect_llr_all_zeros() {
let llr = [10.0f32; 174];
let _result = bp_decode(&llr, None, 30, None);
}
#[test]
fn crc14_known_vector() {
assert_eq!(crc14(&[0u8; 12]), 0);
}
}