mfsk_core/fec/ldpc240_101/
mod.rs1pub mod bp;
11pub mod osd;
12pub mod tables;
13
14pub use bp::{BpResult, bp_decode, check_crc24, crc24};
15pub use osd::{OsdResult, ldpc_encode, osd_decode, osd_decode_deep};
16
17use crate::core::{FecCodec, FecOpts, FecResult};
18
19pub const LDPC_N: usize = 240;
20pub const LDPC_K: usize = 101;
21pub const LDPC_M: usize = LDPC_N - LDPC_K; #[derive(Copy, Clone, Debug, Default)]
25pub struct Ldpc240_101;
26
27impl FecCodec for Ldpc240_101 {
28 const N: usize = LDPC_N;
29 const K: usize = LDPC_K;
30
31 fn encode(&self, info: &[u8], codeword: &mut [u8]) {
32 assert_eq!(info.len(), LDPC_K, "info must be {} bits", LDPC_K);
33 assert_eq!(codeword.len(), LDPC_N, "codeword must be {} bits", LDPC_N);
34 let mut arr = [0u8; LDPC_K];
35 arr.copy_from_slice(info);
36 let cw = ldpc_encode(&arr);
37 codeword.copy_from_slice(&cw);
38 }
39
40 fn decode_soft(&self, llr: &[f32], opts: &FecOpts<'_>) -> Option<FecResult> {
41 assert_eq!(llr.len(), LDPC_N, "llr must be {} values", LDPC_N);
42 let mut llr_arr = [0f32; LDPC_N];
43 llr_arr.copy_from_slice(llr);
44
45 let ap_storage;
47 let ap_mask: Option<&[bool; LDPC_N]> = match opts.ap_mask {
48 Some((mask, values)) => {
49 assert_eq!(mask.len(), LDPC_N, "ap mask must be {} bits", LDPC_N);
50 assert_eq!(values.len(), LDPC_N, "ap values must be {} bits", LDPC_N);
51 let apmag = llr_arr.iter().map(|x| x.abs()).fold(0.0f32, f32::max) * 1.01;
52 let mut a = [false; LDPC_N];
53 for i in 0..LDPC_N {
54 if mask[i] != 0 {
55 a[i] = true;
56 llr_arr[i] = if values[i] != 0 { apmag } else { -apmag };
57 }
58 }
59 ap_storage = a;
60 Some(&ap_storage)
61 }
62 None => None,
63 };
64
65 if let Some(r) = bp_decode(&llr_arr, ap_mask, opts.bp_max_iter) {
66 let mut info = vec![0u8; LDPC_K];
67 info[..77].copy_from_slice(&r.message77);
68 info[77..].copy_from_slice(&r.codeword[77..LDPC_K]);
69 return Some(FecResult {
70 info,
71 hard_errors: r.hard_errors,
72 iterations: r.iterations,
73 });
74 }
75
76 if opts.osd_depth == 0 {
77 return None;
78 }
79
80 let r = osd_decode_deep(&llr_arr, opts.osd_depth.min(3) as u8)?;
81 let mut info = vec![0u8; LDPC_K];
82 info[..77].copy_from_slice(&r.message77);
83 info[77..].copy_from_slice(&r.codeword[77..LDPC_K]);
84 Some(FecResult {
85 info,
86 hard_errors: r.hard_errors,
87 iterations: 0,
88 })
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
100 fn roundtrip_perfect_llr() {
101 let mut info = [0u8; LDPC_K];
103 for i in 0..77 {
104 info[i] = ((i * 7 + 3) & 1) as u8;
105 }
106 let crc = crc24(&info); for i in 0..24 {
108 info[77 + i] = ((crc >> (23 - i)) & 1) as u8;
109 }
110
111 let cw = ldpc_encode(&info);
112 assert_eq!(&cw[..LDPC_K], &info[..]);
114
115 let mut llr = [0f32; LDPC_N];
117 for i in 0..LDPC_N {
118 llr[i] = if cw[i] == 1 { 8.0 } else { -8.0 };
119 }
120 let r = bp_decode(&llr, None, 30).expect("BP converges on perfect LLR");
121 assert_eq!(&r.message77[..], &info[..77]);
122 }
123}