1use crate::decoder::TagDecoder;
9
10pub trait DecodingStrategy: Send + Sync + 'static {
12 type Code: Clone + std::fmt::Debug + Send + Sync;
14
15 fn from_intensities(intensities: &[f64], thresholds: &[f64]) -> Self::Code;
17
18 fn distance(code: &Self::Code, target: u64) -> u32;
23
24 fn decode(
26 code: &Self::Code,
27 decoder: &(impl TagDecoder + ?Sized),
28 max_error: u32,
29 ) -> Option<(u32, u32, u8)>;
30
31 fn to_debug_bits(code: &Self::Code) -> u64;
33}
34
35pub struct HardStrategy;
37
38impl DecodingStrategy for HardStrategy {
39 type Code = u64;
40
41 fn from_intensities(intensities: &[f64], thresholds: &[f64]) -> Self::Code {
42 let mut bits = 0u64;
43 for (i, (&val, &thresh)) in intensities.iter().zip(thresholds.iter()).enumerate() {
44 if val > thresh {
45 bits |= 1 << i;
46 }
47 }
48 bits
49 }
50
51 fn distance(code: &Self::Code, target: u64) -> u32 {
52 (*code ^ target).count_ones()
53 }
54
55 fn decode(
56 code: &Self::Code,
57 decoder: &(impl TagDecoder + ?Sized),
58 max_error: u32,
59 ) -> Option<(u32, u32, u8)> {
60 decoder.decode_full(*code, max_error)
61 }
62
63 fn to_debug_bits(code: &Self::Code) -> u64 {
64 *code
65 }
66}
67
68pub struct SoftStrategy;
70
71#[derive(Clone, Debug)]
73pub struct SoftCode {
74 pub llrs: [i16; 64],
76 pub len: usize,
78}
79
80impl SoftStrategy {
81 #[inline]
82 fn distance_with_limit(code: &SoftCode, target: u64, limit: u32) -> u32 {
83 let mut penalty = 0u32;
84 let n = code.len;
85
86 for i in 0..n {
87 let target_bit = (target >> i) & 1;
88 let llr = code.llrs[i];
89
90 if target_bit == 1 {
91 if llr < 0 {
92 penalty += u32::from(llr.unsigned_abs());
93 }
94 } else if llr > 0 {
95 penalty += u32::from(llr.unsigned_abs());
96 }
97
98 if penalty >= limit {
99 return limit;
100 }
101 }
102 penalty
103 }
104}
105
106impl DecodingStrategy for SoftStrategy {
107 type Code = SoftCode;
108
109 fn from_intensities(intensities: &[f64], thresholds: &[f64]) -> Self::Code {
110 let n = intensities.len().min(64);
111 let mut llrs = [0i16; 64];
112 for i in 0..n {
113 llrs[i] = (intensities[i] - thresholds[i]) as i16;
114 }
115 SoftCode { llrs, len: n }
116 }
117
118 fn distance(code: &Self::Code, target: u64) -> u32 {
119 Self::distance_with_limit(code, target, u32::MAX)
120 }
121
122 fn decode(
123 code: &Self::Code,
124 decoder: &(impl TagDecoder + ?Sized),
125 max_error: u32,
126 ) -> Option<(u32, u32, u8)> {
127 let bits = Self::to_debug_bits(code);
129 if let Some((id, hamming, rot)) = decoder.decode_full(bits, 0) {
130 return Some((id, hamming, rot));
131 }
132
133 let _codes_count = decoder.num_codes();
134 let soft_threshold = max_error.max(1) * 60;
135
136 let mut best_id = None;
137 let mut best_dist = soft_threshold;
138 let mut best_rot = 0;
139
140 for &(target_code, id, rot) in decoder.rotated_codes() {
141 let dist = Self::distance_with_limit(code, target_code, best_dist);
142 if dist < best_dist {
143 best_dist = dist;
144 best_id = Some(u32::from(id));
145 best_rot = rot;
146
147 if best_dist == 0 {
148 return Some((u32::from(id), 0, rot));
149 }
150 }
151 }
152
153 if best_dist < soft_threshold {
154 return best_id.map(|id| {
155 let equiv_hamming = best_dist / 60;
156 (id, equiv_hamming, best_rot)
157 });
158 }
159
160 None
161 }
162
163 fn to_debug_bits(code: &Self::Code) -> u64 {
164 let mut bits = 0u64;
165 for i in 0..code.len {
166 if code.llrs[i] > 0 {
167 bits |= 1 << i;
168 }
169 }
170 bits
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177
178 #[test]
179 fn test_hard_distance() {
180 let code = 0b1010;
181 let target = 0b1100;
182 assert_eq!(HardStrategy::distance(&code, target), 2);
184 }
185
186 #[test]
187 fn test_soft_from_intensities() {
188 let intensities = vec![100.0, 50.0, 200.0];
189 let thresholds = vec![80.0, 60.0, 150.0];
190 let code = SoftStrategy::from_intensities(&intensities, &thresholds);
192 assert_eq!(code.llrs[0], 20);
193 assert_eq!(code.llrs[1], -10);
194 assert_eq!(code.llrs[2], 50);
195 assert_eq!(code.len, 3);
196 }
197
198 #[test]
199 fn test_soft_distance() {
200 let code = SoftCode {
202 llrs: {
203 let mut l = [0i16; 64];
204 l[0] = 20;
205 l[1] = -10;
206 l[2] = 50;
207 l
208 },
209 len: 3,
210 };
211
212 assert_eq!(SoftStrategy::distance(&code, 0b101), 0);
217
218 assert_eq!(SoftStrategy::distance(&code, 0b010), 80);
224 }
225
226 #[test]
227 fn test_to_debug_bits() {
228 let code = SoftCode {
229 llrs: {
230 let mut l = [0i16; 64];
231 l[0] = 20;
232 l[1] = -10;
233 l[2] = 50;
234 l
235 },
236 len: 3,
237 };
238 assert_eq!(SoftStrategy::to_debug_bits(&code), 5);
241 }
242}