mfsk_core/fec/ldpc240_101/
mod.rs1pub mod tables;
14
15use crate::core::{FecCodec, FecOpts, FecResult};
16use crate::fec::ldpc::bp::bp_decode_generic;
17use crate::fec::ldpc::osd::{ldpc_encode_generic, osd_decode_generic};
18use crate::fec::ldpc::params::Ldpc240_101Params;
19
20pub const LDPC_N: usize = 240;
21pub const LDPC_K: usize = 101;
22pub const LDPC_M: usize = LDPC_N - LDPC_K; pub fn crc24(bits: &[u8]) -> u32 {
32 let mut r = [0u8; 25];
33 for (i, slot) in r.iter_mut().enumerate() {
34 *slot = if i < bits.len() { bits[i] & 1 } else { 0 };
35 }
36 const POLY: [u8; 25] = [
37 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1,
38 ];
39 let n = bits.len().saturating_sub(25);
40 for i in 0..=n {
41 if i + 25 <= bits.len() {
42 r[24] = bits[i + 24] & 1;
43 } else {
44 r[24] = 0;
45 }
46 let top = r[0];
47 if top != 0 {
48 for (rv, pv) in r.iter_mut().zip(POLY.iter()) {
49 *rv ^= *pv;
50 }
51 }
52 let first = r[0];
53 for k in 0..24 {
54 r[k] = r[k + 1];
55 }
56 r[24] = first;
57 }
58 let mut v = 0u32;
59 for &b in &r[..24] {
60 v = (v << 1) | (b as u32);
61 }
62 v
63}
64
65pub fn check_crc24(decoded: &[u8]) -> bool {
71 if decoded.len() != LDPC_K {
72 return false;
73 }
74 let mut with_zero = [0u8; LDPC_K];
75 with_zero[..77].copy_from_slice(&decoded[..77]);
76 let expected = crc24(&with_zero);
77
78 let mut got = 0u32;
79 for &b in &decoded[77..101] {
80 got = (got << 1) | (b as u32 & 1);
81 }
82 expected == got
83}
84
85#[derive(Copy, Clone, Debug, Default)]
92pub struct Ldpc240_101;
93
94impl FecCodec for Ldpc240_101 {
95 const N: usize = LDPC_N;
96 const K: usize = LDPC_K;
97
98 fn encode(&self, info: &[u8], codeword: &mut [u8]) {
99 assert_eq!(info.len(), LDPC_K, "info must be {} bits", LDPC_K);
100 assert_eq!(codeword.len(), LDPC_N, "codeword must be {} bits", LDPC_N);
101 ldpc_encode_generic::<Ldpc240_101Params>(info, codeword);
102 }
103
104 fn decode_soft(&self, llr: &[f32], opts: &FecOpts<'_>) -> Option<FecResult> {
105 assert_eq!(llr.len(), LDPC_N, "llr must be {} values", LDPC_N);
106 let mut llr_arr = vec![0f32; LDPC_N];
107 llr_arr.copy_from_slice(llr);
108
109 let ap_storage_holder;
114 let ap_slice: Option<&[bool]> = match opts.ap_mask {
115 Some((mask, values)) => {
116 assert_eq!(mask.len(), LDPC_N, "ap mask must be {} bits", LDPC_N);
117 assert_eq!(values.len(), LDPC_N, "ap values must be {} bits", LDPC_N);
118 let apmag = llr_arr.iter().map(|x| x.abs()).fold(0.0f32, f32::max) * 1.01;
119 let mut a = vec![false; LDPC_N];
120 for i in 0..LDPC_N {
121 if mask[i] != 0 {
122 a[i] = true;
123 llr_arr[i] = if values[i] != 0 { apmag } else { -apmag };
124 }
125 }
126 ap_storage_holder = a;
127 Some(ap_storage_holder.as_slice())
128 }
129 None => None,
130 };
131
132 if let Some(r) = bp_decode_generic::<Ldpc240_101Params>(
133 &llr_arr,
134 ap_slice,
135 opts.bp_max_iter,
136 opts.verify_info,
137 ) {
138 return Some(FecResult {
139 info: r.info,
140 hard_errors: r.hard_errors,
141 iterations: r.iterations,
142 });
143 }
144
145 if opts.osd_depth == 0 {
146 return None;
147 }
148
149 let r = osd_decode_generic::<Ldpc240_101Params>(
150 &llr_arr,
151 opts.osd_depth.min(3) as u8,
152 LDPC_K,
153 opts.verify_info,
154 )?;
155 Some(FecResult {
156 info: r.info,
157 hard_errors: r.hard_errors,
158 iterations: 0,
159 })
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166 use crate::fec::ldpc::bp::bp_decode_generic;
167 use crate::fec::ldpc::osd::ldpc_encode_generic;
168
169 #[test]
173 fn roundtrip_perfect_llr() {
174 let mut info = [0u8; LDPC_K];
175 for i in 0..77 {
176 info[i] = ((i * 7 + 3) & 1) as u8;
177 }
178 let crc = crc24(&info); for i in 0..24 {
180 info[77 + i] = ((crc >> (23 - i)) & 1) as u8;
181 }
182
183 let mut cw = [0u8; LDPC_N];
184 ldpc_encode_generic::<Ldpc240_101Params>(&info, &mut cw);
185 assert_eq!(&cw[..LDPC_K], &info[..]);
187
188 let mut llr = vec![0f32; LDPC_N];
190 for i in 0..LDPC_N {
191 llr[i] = if cw[i] == 1 { 8.0 } else { -8.0 };
192 }
193 let r = bp_decode_generic::<Ldpc240_101Params>(&llr, None, 30, Some(check_crc24))
194 .expect("BP converges on perfect LLR");
195 assert_eq!(&r.info[..77], &info[..77]);
196 }
197}