use alloc::vec;
use crate::core::{FecCodec, FecOpts};
use crate::fec::ConvFano;
const N: usize = 162;
const K: usize = 50;
fn gen_matrix() -> &'static [[u8; N]; K] {
use std::sync::OnceLock;
static GEN: OnceLock<[[u8; N]; K]> = OnceLock::new();
GEN.get_or_init(|| {
let codec = ConvFano;
let mut g = [[0u8; N]; K];
let mut info = [0u8; K];
let mut codeword = vec![0u8; N];
for i in 0..K {
info.fill(0);
info[i] = 1;
codec.encode(&info, &mut codeword);
g[i].copy_from_slice(&codeword);
}
g
})
}
pub fn osd_decode(llrs: &[f32; N]) -> Option<([u8; K], u32)> {
let mut hard = [0u8; N];
let mut absllr = [0.0f32; N];
for i in 0..N {
hard[i] = if llrs[i] < 0.0 { 1 } else { 0 };
absllr[i] = llrs[i].abs();
}
let mut indices: [usize; N] = core::array::from_fn(|i| i);
indices.sort_by(|&a, &b| {
absllr[b]
.partial_cmp(&absllr[a])
.unwrap_or(core::cmp::Ordering::Equal)
});
let g0 = gen_matrix();
let mut g: [[u8; N]; K] = [[0u8; N]; K];
let mut hard_perm = [0u8; N];
let mut abs_perm = [0.0f32; N];
let mut perm = indices;
for (col_out, &col_src) in indices.iter().enumerate() {
for row in 0..K {
g[row][col_out] = g0[row][col_src];
}
hard_perm[col_out] = hard[col_src];
abs_perm[col_out] = absllr[col_src];
}
for id in 0..K {
let mut pivot_col = None;
for icol in id..(K + 20).min(N) {
if g[id][icol] == 1 {
pivot_col = Some(icol);
break;
}
}
let icol = pivot_col?;
if icol != id {
for row in 0..K {
g[row].swap(id, icol);
}
hard_perm.swap(id, icol);
abs_perm.swap(id, icol);
perm.swap(id, icol);
}
for row in 0..K {
if row != id && g[row][id] == 1 {
for c in 0..N {
g[row][c] ^= g[id][c];
}
}
}
}
let mut m0 = [0u8; K];
m0.copy_from_slice(&hard_perm[..K]);
let encode = |me: &[u8; K]| -> [u8; N] {
let mut cw = [0u8; N];
cw[..K].copy_from_slice(me);
for i in 0..K {
if me[i] == 1 {
for c in K..N {
cw[c] ^= g[i][c];
}
}
}
cw
};
let distance = |cw: &[u8; N]| -> f32 {
let mut d = 0.0f32;
for i in 0..N {
if cw[i] != hard_perm[i] {
d += abs_perm[i];
}
}
d
};
let c0 = encode(&m0);
let mut best_d = distance(&c0);
let mut best_cw = c0;
for n1 in 0..K {
let mut me = m0;
me[n1] ^= 1;
let cw = encode(&me);
let d = distance(&cw);
if d < best_d {
best_d = d;
best_cw = cw;
}
}
for n1 in 0..K {
for n2 in (n1 + 1)..K {
let mut me = m0;
me[n1] ^= 1;
me[n2] ^= 1;
let cw = encode(&me);
let d = distance(&cw);
if d < best_d {
best_d = d;
best_cw = cw;
}
}
}
let mut cw_natural = [0u8; N];
for (perm_i, &orig_i) in perm.iter().enumerate() {
cw_natural[orig_i] = best_cw[perm_i];
}
let mut hard_llrs = [0.0f32; N];
for i in 0..N {
hard_llrs[i] = if cw_natural[i] == 0 { 127.0 } else { -127.0 };
}
let codec = ConvFano;
let res = codec.decode_soft(&hard_llrs, &FecOpts::default())?;
if res.hard_errors > 0 {
return None;
}
let mut info = [0u8; K];
info.copy_from_slice(&res.info);
let mut nhardmin = 0u32;
for i in 0..N {
if cw_natural[i] != hard[i] {
nhardmin += 1;
}
}
Some((info, nhardmin))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn osd_runs_without_panic() {
let llrs = [1.0f32; N];
let _ = osd_decode(&llrs); }
}