mfsk_core/fec/ldpc_128_90/
mod.rs1pub mod tables;
16
17use alloc::vec;
18
19use crate::core::{FecCodec, FecOpts, FecResult};
20use crate::fec::ldpc::bp::bp_decode_generic_kind;
21use crate::fec::ldpc::osd::{ldpc_encode_generic, osd_decode_generic};
22use crate::fec::ldpc::params::Ldpc128_90Params;
23
24pub const LDPC_N: usize = 128;
25pub const LDPC_K: usize = 90;
26pub const LDPC_M: usize = LDPC_N - LDPC_K; pub fn crc13(data: &[u8]) -> u16 {
37 let mut crc: u16 = 0;
38 for &byte in data {
39 for i in (0..8).rev() {
40 let bit = (byte >> i) & 1;
41 let msb = (crc >> 12) & 1;
42 crc = ((crc << 1) | bit as u16) & 0x1FFF;
43 if msb != 0 {
44 crc ^= 0x15D7;
45 }
46 }
47 }
48 crc
49}
50
51pub fn check_crc13(decoded: &[u8]) -> bool {
61 if decoded.len() != LDPC_K {
62 return false;
63 }
64 let mut bytes = [0u8; 12];
65 for (i, &bit) in decoded[..77].iter().enumerate() {
66 let byte_idx = i / 8;
67 let bit_pos = 7 - (i % 8);
68 bytes[byte_idx] |= (bit & 1) << bit_pos;
69 }
70
71 let computed = crc13(&bytes);
72
73 let mut received: u16 = 0;
74 for &bit in &decoded[77..90] {
75 received = (received << 1) | (bit as u16 & 1);
76 }
77
78 computed == received
79}
80
81#[derive(Copy, Clone, Debug, Default)]
88pub struct Ldpc128_90;
89
90impl FecCodec for Ldpc128_90 {
91 const N: usize = LDPC_N;
92 const K: usize = LDPC_K;
93
94 fn encode(&self, info: &[u8], codeword: &mut [u8]) {
95 assert_eq!(info.len(), LDPC_K, "info must be {} bits", LDPC_K);
96 assert_eq!(codeword.len(), LDPC_N, "codeword must be {} bits", LDPC_N);
97 ldpc_encode_generic::<Ldpc128_90Params>(info, codeword);
98 }
99
100 fn decode_soft(&self, llr: &[f32], opts: &FecOpts<'_>) -> Option<FecResult> {
101 assert_eq!(llr.len(), LDPC_N, "llr must be {} values", LDPC_N);
102 let mut llr_arr = vec![0f32; LDPC_N];
103 llr_arr.copy_from_slice(llr);
104
105 let ap_storage_holder;
107 let ap_slice: Option<&[bool]> = match opts.ap_mask {
108 Some((mask, values)) => {
109 assert_eq!(mask.len(), LDPC_N, "ap mask must be {} bits", LDPC_N);
110 assert_eq!(values.len(), LDPC_N, "ap values must be {} bits", LDPC_N);
111 let apmag = llr_arr.iter().map(|x| x.abs()).fold(0.0f32, f32::max) * 1.01;
112 let mut a = vec![false; LDPC_N];
113 for i in 0..LDPC_N {
114 if mask[i] != 0 {
115 a[i] = true;
116 llr_arr[i] = if values[i] != 0 { apmag } else { -apmag };
117 }
118 }
119 ap_storage_holder = a;
120 Some(ap_storage_holder.as_slice())
121 }
122 None => None,
123 };
124
125 if let Some(r) = bp_decode_generic_kind::<Ldpc128_90Params>(
126 &llr_arr,
127 ap_slice,
128 opts.bp_max_iter,
129 opts.verify_info,
130 opts.bp_kind,
131 ) {
132 return Some(FecResult {
133 info: r.info,
134 hard_errors: r.hard_errors,
135 iterations: r.iterations,
136 });
137 }
138
139 if opts.osd_depth == 0 {
140 return None;
141 }
142
143 if let Some(r) = osd_decode_generic::<Ldpc128_90Params>(
144 &llr_arr,
145 opts.osd_depth.min(3) as u8,
146 LDPC_K,
147 opts.verify_info,
148 ) {
149 return Some(FecResult {
150 info: r.info,
151 hard_errors: r.hard_errors,
152 iterations: 0,
153 });
154 }
155
156 None
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163 use crate::fec::ldpc::bp::bp_decode_generic;
164 use crate::fec::ldpc::osd::ldpc_encode_generic;
165
166 #[test]
171 fn crc13_known_vectors() {
172 assert_eq!(crc13(&[0u8; 12]), 0);
173
174 let mut all_ones_77 = [0u8; 12];
175 for i in 0..77 {
176 let byte_idx = i / 8;
177 let bit_pos = 7 - (i % 8);
178 all_ones_77[byte_idx] |= 1 << bit_pos;
179 }
180 assert_eq!(crc13(&all_ones_77), 5559);
181
182 let mut pattern = [0u8; 12];
183 for i in 0..77 {
184 if (i * 7 + 3) & 1 != 0 {
185 let byte_idx = i / 8;
186 let bit_pos = 7 - (i % 8);
187 pattern[byte_idx] |= 1 << bit_pos;
188 }
189 }
190 assert_eq!(crc13(&pattern), 4105);
191
192 let mut single_bit0 = [0u8; 12];
193 single_bit0[0] |= 1 << 7;
194 assert_eq!(crc13(&single_bit0), 4822);
195
196 let mut single_bit76 = [0u8; 12];
197 single_bit76[9] |= 1 << 3; assert_eq!(crc13(&single_bit76), 7029);
199 }
200
201 #[test]
206 fn roundtrip_perfect_llr() {
207 let mut info = [0u8; LDPC_K];
208 for i in 0..77 {
209 info[i] = ((i * 7 + 3) & 1) as u8;
210 }
211 let crc = crc13(&{
212 let mut bytes = [0u8; 12];
213 for i in 0..77 {
214 let byte_idx = i / 8;
215 let bit_pos = 7 - (i % 8);
216 bytes[byte_idx] |= (info[i] & 1) << bit_pos;
217 }
218 bytes
219 });
220 for i in 0..13 {
221 info[77 + i] = ((crc >> (12 - i)) & 1) as u8;
222 }
223
224 let mut cw = [0u8; LDPC_N];
225 ldpc_encode_generic::<Ldpc128_90Params>(&info, &mut cw);
226 assert_eq!(&cw[..LDPC_K], &info[..]);
228 assert!(check_crc13(&info));
230
231 let mut llr = vec![0f32; LDPC_N];
232 for i in 0..LDPC_N {
233 llr[i] = if cw[i] == 1 { 8.0 } else { -8.0 };
234 }
235 let r = bp_decode_generic::<Ldpc128_90Params>(&llr, None, 30, Some(check_crc13))
236 .expect("BP converges on perfect LLR");
237 assert_eq!(&r.info[..77], &info[..77]);
238 }
239
240 #[test]
243 fn roundtrip_noisy_llr_recovers() {
244 let mut info = [0u8; LDPC_K];
245 for i in 0..77 {
246 info[i] = ((i * 3 + 1) & 1) as u8;
247 }
248 let crc = crc13(&{
249 let mut bytes = [0u8; 12];
250 for i in 0..77 {
251 let byte_idx = i / 8;
252 let bit_pos = 7 - (i % 8);
253 bytes[byte_idx] |= (info[i] & 1) << bit_pos;
254 }
255 bytes
256 });
257 for i in 0..13 {
258 info[77 + i] = ((crc >> (12 - i)) & 1) as u8;
259 }
260
261 let mut cw = [0u8; LDPC_N];
262 ldpc_encode_generic::<Ldpc128_90Params>(&info, &mut cw);
263
264 let mut llr = vec![0f32; LDPC_N];
265 for i in 0..LDPC_N {
266 let mag = if i % 11 == 0 { 0.3 } else { 3.0 };
269 let sign = if cw[i] == 1 { 1.0 } else { -1.0 };
270 llr[i] = sign * mag;
271 }
272 let r = bp_decode_generic::<Ldpc128_90Params>(&llr, None, 30, Some(check_crc13))
273 .expect("BP converges on noisy LLR");
274 assert_eq!(&r.info[..77], &info[..77]);
275 }
276}