use alloc::vec::Vec;
use super::Ft8;
use super::params::{LDPC_N, LLR_SCALE};
use crate::core::scalar::{Cmplx, LlrScalar};
pub use crate::core::llr::LlrSet as GenericLlrSet;
pub struct LlrSet<T: LlrScalar = f32> {
pub llra: [T; LDPC_N],
pub llrb: [T; LDPC_N],
pub llrc: [T; LDPC_N],
pub llrd: [T; LDPC_N],
}
#[inline]
fn flatten_cs(cs: &[[Cmplx<f32>; 8]; 79]) -> Vec<Cmplx<f32>> {
let mut out: Vec<Cmplx<f32>> = Vec::with_capacity(79 * 8);
for sym in cs.iter() {
out.extend_from_slice(sym);
}
out
}
#[inline]
fn inflate_llr<T: LlrScalar>(v: Vec<T>) -> [T; LDPC_N] {
let mut out = [T::ZERO; LDPC_N];
let n = v.len().min(LDPC_N);
out[..n].copy_from_slice(&v[..n]);
out
}
pub fn compute_llr<T: LlrScalar>(cs: &[[Cmplx<f32>; 8]; 79]) -> LlrSet<T> {
let flat = flatten_cs(cs);
let g = crate::core::llr::compute_llr_generic::<Ft8, f32, T>(&flat, 3);
debug_assert!((crate::core::llr::LLR_SCALE - LLR_SCALE).abs() < 1e-6);
LlrSet {
llra: inflate_llr(g.llra),
llrb: inflate_llr(g.llrb),
llrc: inflate_llr(g.llrc),
llrd: inflate_llr(g.llrd),
}
}
pub fn compute_llr_fast<T: LlrScalar>(cs: &[[Cmplx<f32>; 8]; 79]) -> LlrSet<T> {
let flat = flatten_cs(cs);
let g = crate::core::llr::compute_llr_generic::<Ft8, f32, T>(&flat, 1);
LlrSet {
llra: inflate_llr(g.llra),
llrb: inflate_llr(g.llrb),
llrc: inflate_llr(g.llrc),
llrd: inflate_llr(g.llrd),
}
}
pub fn compute_llr_partial<T: LlrScalar>(cs: &[[Cmplx<f32>; 8]; 79], nsym: usize) -> [T; LDPC_N] {
let flat = flatten_cs(cs);
let v = crate::core::llr::compute_llr_partial::<Ft8, f32, T>(&flat, nsym);
inflate_llr(v)
}
pub fn compute_snr_db(cs: &[[Cmplx<f32>; 8]; 79], itone: &[u8; 79]) -> f32 {
let flat = flatten_cs(cs);
crate::core::llr::compute_snr_db_generic::<Ft8, f32>(&flat, itone)
}
pub fn sync_quality(cs: &[[Cmplx<f32>; 8]; 79]) -> u32 {
let flat = flatten_cs(cs);
crate::core::llr::sync_quality_generic::<Ft8, f32>(&flat)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zero_spectra_zero_llr() {
let cs: Box<[[Cmplx<f32>; 8]; 79]> =
vec![[Cmplx::<f32>::default(); 8]; 79].try_into().unwrap();
let llr_set: LlrSet = compute_llr(&cs);
let any_large = llr_set.llra.iter().any(|&x| x.abs() > 1.0);
assert!(!any_large, "zero input should not produce large LLRs");
}
#[test]
fn llr_length_is_174() {
let cs: Box<[[Cmplx<f32>; 8]; 79]> =
vec![[Cmplx::<f32>::default(); 8]; 79].try_into().unwrap();
let llr_set: LlrSet = compute_llr(&cs);
assert_eq!(llr_set.llra.len(), 174);
assert_eq!(llr_set.llrd.len(), 174);
}
#[test]
fn sync_quality_costas_perfect() {
use super::super::params::COSTAS;
let mut cs = vec![[Cmplx::<f32>::default(); 8]; 79];
for &sym_offset in &[0usize, 36, 72] {
for t in 0..7 {
let sym = sym_offset + t;
cs[sym][COSTAS[t]] = Cmplx { re: 1.0, im: 0.0 };
}
}
let cs_box: Box<[[Cmplx<f32>; 8]; 79]> = cs.try_into().unwrap();
assert_eq!(sync_quality(&cs_box), 21);
}
}