]> code.octet-stream.net Git - m17rt/blob - m17core/src/fec.rs
Soundmodem rx works in realtime end-to-end using file source
[m17rt] / m17core / src / fec.rs
1 use crate::bits::{Bits, BitsMut};
2 use log::debug;
3
4 struct Transition {
5 input: u8,
6 output: [u8; 2],
7 source: usize,
8 }
9
10 static TRANSITIONS: [Transition; 32] = [
11 Transition {
12 input: 0,
13 output: [0, 0],
14 source: 0,
15 },
16 Transition {
17 input: 0,
18 output: [1, 1],
19 source: 1,
20 },
21 Transition {
22 input: 0,
23 output: [1, 0],
24 source: 2,
25 },
26 Transition {
27 input: 0,
28 output: [0, 1],
29 source: 3,
30 },
31 Transition {
32 input: 0,
33 output: [0, 1],
34 source: 4,
35 },
36 Transition {
37 input: 0,
38 output: [1, 0],
39 source: 5,
40 },
41 Transition {
42 input: 0,
43 output: [1, 1],
44 source: 6,
45 },
46 Transition {
47 input: 0,
48 output: [0, 0],
49 source: 7,
50 },
51 Transition {
52 input: 0,
53 output: [0, 1],
54 source: 8,
55 },
56 Transition {
57 input: 0,
58 output: [1, 0],
59 source: 9,
60 },
61 Transition {
62 input: 0,
63 output: [1, 1],
64 source: 10,
65 },
66 Transition {
67 input: 0,
68 output: [0, 0],
69 source: 11,
70 },
71 Transition {
72 input: 0,
73 output: [0, 0],
74 source: 12,
75 },
76 Transition {
77 input: 0,
78 output: [1, 1],
79 source: 13,
80 },
81 Transition {
82 input: 0,
83 output: [1, 0],
84 source: 14,
85 },
86 Transition {
87 input: 0,
88 output: [0, 1],
89 source: 15,
90 },
91 Transition {
92 input: 1,
93 output: [1, 1],
94 source: 0,
95 },
96 Transition {
97 input: 1,
98 output: [0, 0],
99 source: 1,
100 },
101 Transition {
102 input: 1,
103 output: [0, 1],
104 source: 2,
105 },
106 Transition {
107 input: 1,
108 output: [1, 0],
109 source: 3,
110 },
111 Transition {
112 input: 1,
113 output: [1, 0],
114 source: 4,
115 },
116 Transition {
117 input: 1,
118 output: [0, 1],
119 source: 5,
120 },
121 Transition {
122 input: 1,
123 output: [0, 0],
124 source: 6,
125 },
126 Transition {
127 input: 1,
128 output: [1, 1],
129 source: 7,
130 },
131 Transition {
132 input: 1,
133 output: [1, 0],
134 source: 8,
135 },
136 Transition {
137 input: 1,
138 output: [0, 1],
139 source: 9,
140 },
141 Transition {
142 input: 1,
143 output: [0, 0],
144 source: 10,
145 },
146 Transition {
147 input: 1,
148 output: [1, 1],
149 source: 11,
150 },
151 Transition {
152 input: 1,
153 output: [1, 1],
154 source: 12,
155 },
156 Transition {
157 input: 1,
158 output: [0, 0],
159 source: 13,
160 },
161 Transition {
162 input: 1,
163 output: [0, 1],
164 source: 14,
165 },
166 Transition {
167 input: 1,
168 output: [1, 0],
169 source: 15,
170 },
171 ];
172
173 pub(crate) fn p_1(step: usize) -> (bool, bool) {
174 let mod61 = step % 61;
175 let is_even = mod61 % 2 == 0;
176 (mod61 > 30 || is_even, mod61 < 30 || is_even)
177 }
178
179 pub(crate) fn p_2(step: usize) -> (bool, bool) {
180 let mod6 = step % 6;
181 (true, mod6 != 5)
182 }
183
184 fn best_previous(table: &[[u8; 32]; 244], step: usize, state: usize) -> u8 {
185 if step == 0 {
186 if state == 0 {
187 return 0;
188 } else {
189 return u8::MAX;
190 }
191 }
192 let prev1 = table[step - 1][state * 2];
193 let prev2 = table[step - 1][state * 2 + 1];
194 prev1.min(prev2)
195 }
196
197 fn hamming_distance(first: &[u8], second: &[u8]) -> u8 {
198 first
199 .iter()
200 .zip(second.iter())
201 .map(|(x, y)| if *x == *y { 0 } else { 1 })
202 .sum()
203 }
204
205 // maximum 368 type 3 bits, maximum 240 type 1 bits, 4 flush bits
206 pub(crate) fn decode(
207 type3: &[u8], // up to len 46
208 input_len: usize,
209 puncture: fn(usize) -> (bool, bool),
210 ) -> Option<[u8; 30]> {
211 let type3_bits = Bits::new(type3);
212 let mut type3_iter = type3_bits.iter();
213 let mut table = [[0u8; 32]; 244];
214 for step in 0..(input_len + 4) {
215 let (use_g1, use_g2) = puncture(step);
216 let split_idx = if use_g1 && use_g2 { 2 } else { 1 };
217 let mut input_bits = [0u8; 2];
218 input_bits[0] = type3_iter.next().unwrap();
219 let step_input = if split_idx == 1 {
220 &input_bits[0..1]
221 } else {
222 input_bits[1] = type3_iter.next().unwrap();
223 &input_bits[0..2]
224 };
225 for (t_idx, t) in TRANSITIONS.iter().enumerate() {
226 let t_offer = if use_g1 && use_g2 {
227 &t.output[..]
228 } else if use_g1 {
229 &t.output[0..1]
230 } else {
231 &t.output[1..2]
232 };
233 let step_dist = hamming_distance(step_input, t_offer);
234 table[step][t_idx] = best_previous(&table, step, t.source).saturating_add(step_dist);
235 }
236 }
237 let (mut best_idx, best) = table[input_len + 3]
238 .iter()
239 .enumerate()
240 .min_by_key(|(_, i)| *i)
241 .unwrap();
242 debug!("Best score is {best}, transition {best_idx}");
243 if *best > 6 {
244 None
245 } else {
246 let mut out = [0u8; 30];
247 let mut out_bits = BitsMut::new(&mut out);
248 for step in (0..(input_len + 4)).rev() {
249 let input = TRANSITIONS[best_idx].input;
250 if step < input_len {
251 out_bits.set_bit(step, input);
252 }
253 if step > 0 {
254 let state = TRANSITIONS[best_idx].source;
255 let prev1 = table[step - 1][state * 2];
256 let prev2 = table[step - 1][state * 2 + 1];
257 best_idx = if prev1 < prev2 {
258 state * 2
259 } else {
260 state * 2 + 1
261 };
262 }
263 }
264 Some(out)
265 }
266 }