1#![no_std]
4
5#[cfg(feature = "defmt-03")]
6use defmt;
7
8#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
9#[derive(Debug, PartialEq)]
10pub enum Error {
11 InvalidPower,
12 InvalidGrid,
13 InvalidCallsign,
14}
15
16struct ShiftRegister {
20 value: u32,
21 and_const: u32,
22}
23
24impl ShiftRegister {
25 fn new(and_const: u32) -> Self {
26 Self {
27 value: 0,
28 and_const,
29 }
30 }
31
32 fn shift(&mut self, bit: u32) -> u8 {
33 self.value = (self.value << 1) | bit;
34 let ones = (self.value & self.and_const).count_ones();
35 let parity = ones & 0x01; parity as u8
37 }
38}
39
40struct Buffer {
43 buffer: [u8; 162],
44 index: usize,
45}
46
47impl Buffer {
48 fn new() -> Self {
49 Self {
50 buffer: [0u8; 162],
51 index: 0,
52 }
53 }
54
55 fn push(&mut self, bit: u8) {
56 self.buffer[self.index] = bit;
57 self.index += 1;
58 }
59
60 fn interleave(&mut self) {
61 let mut interleaved = [0u8; 162];
62 let mut p = 0;
63 for i in 0u8..255 {
64 let j = i.reverse_bits() as usize;
65 if j < 162 {
66 interleaved[j] = self.buffer[p];
67 p += 1;
68 if p == 162 {
69 break;
70 }
71 }
72 }
73
74 self.buffer = interleaved;
75 }
76
77 fn sync(&mut self) {
78 const SYNC: [u8; 162] = [
79 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1,
80 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0,
81 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0,
82 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0,
83 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0,
84 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
85 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1,
86 0, 0, 0, 1, 1, 0, 0, 0,
87 ];
88
89 for i in 0..162 {
90 self.buffer[i] = SYNC[i] + 2 * self.buffer[i];
91 }
92 }
93
94 fn release(self) -> [u8; 162] {
95 self.buffer
96 }
97}
98
99fn encode_callsign_char(c: u8) -> Result<u32, Error> {
102 let c = c as char;
103 if c == ' ' {
104 Ok(36)
105 } else {
106 match c.to_digit(36) {
107 Some(d) => Ok(d),
108 None => return Err(Error::InvalidCallsign),
109 }
110 }
111}
112
113fn encode_callsign(callsign: &str) -> Result<u32, Error> {
114 let callsign = callsign.as_bytes();
115
116 let length = callsign.len();
118 if !(3..=6).contains(&length) {
119 return Err(Error::InvalidCallsign);
120 }
121
122 let start = match length {
134 0..=4 => {
135 if (callsign[2] as char).is_digit(10) {
136 0
137 } else {
138 1
139 }
140 }
141 5 => {
142 if (callsign[1] as char).is_digit(10) {
143 1
144 } else {
145 0
146 }
147 }
148 _ => 0,
149 };
150
151 let stop = start + length;
154 let mut padded = [b' '; 6];
155 padded[start..stop].copy_from_slice(&callsign);
156 let callsign = padded;
157
158 if !(callsign[2] as char).is_digit(10) {
160 return Err(Error::InvalidCallsign);
161 }
162
163 let scalars = [0u32, 36, 10, 27, 27, 27];
175 let subtracts = [0u32, 0, 0, 10, 10, 10];
176
177 let mut n = 0;
178 for (index, &c) in callsign.iter().enumerate() {
179 n = n * scalars[index] + encode_callsign_char(c)? as u32
180 - subtracts[index];
181 }
182
183 Ok(n)
184}
185
186fn encode_grid_char(c: u8) -> Result<u8, Error> {
187 return match (c as char).to_ascii_uppercase() {
188 'A'..='R' => Ok((c as u8) - b'A'),
189 '0'..='9' => Ok((c as u8) - b'0'),
190 _ => Err(Error::InvalidGrid),
191 };
192}
193
194fn encode_grid(grid: &str) -> Result<u16, Error> {
195 let grid = grid.as_bytes();
196
197 if grid.len() != 4 {
198 return Err(Error::InvalidGrid);
199 }
200
201 let first = encode_grid_char(grid[0])? as u16;
202 let second = encode_grid_char(grid[1])? as u16;
203 let third = encode_grid_char(grid[2])? as u16;
204 let fourth = encode_grid_char(grid[3])? as u16;
205
206 let result = (179 - 10 * first - third) * 180 + 10 * second + fourth;
207 Ok(result)
208}
209
210fn encode_power(power: u8) -> Result<u8, Error> {
211 let rem = power % 10;
217
218 if (0..=60).contains(&power) && (rem == 0 || rem == 3 || rem == 7) {
219 Ok(power + 64)
220 } else {
221 Err(Error::InvalidPower)
222 }
223}
224
225pub fn encode(
231 callsign: &str,
232 grid: &str,
233 power: u8,
234) -> Result<[u8; 162], Error> {
235 let callsign = encode_callsign(callsign)?;
236 let grid = encode_grid(grid)?;
237 let power = encode_power(power)?;
238
239 let mut reg0 = ShiftRegister::new(0xF2D05351);
240 let mut reg1 = ShiftRegister::new(0xE4613C47);
241
242 let mut buffer = Buffer::new();
243
244 for i in (0..28).rev() {
245 let bit = (callsign >> i) & 0x01;
246 buffer.push(reg0.shift(bit));
247 buffer.push(reg1.shift(bit));
248 }
249
250 for i in (0..15).rev() {
251 let bit = (grid as u32 >> i) & 0x01;
252 buffer.push(reg0.shift(bit));
253 buffer.push(reg1.shift(bit));
254 }
255
256 for i in (0..7).rev() {
257 let bit = (power as u32 >> i) & 0x01;
258 buffer.push(reg0.shift(bit));
259 buffer.push(reg1.shift(bit));
260 }
261
262 for _ in (0..31).rev() {
263 let bit = 0;
264 buffer.push(reg0.shift(bit));
265 buffer.push(reg1.shift(bit));
266 }
267
268 buffer.interleave();
269 buffer.sync();
270 Ok(buffer.release())
271}
272
273#[cfg(test)]
274mod tests {
275 use super::*;
276
277 #[test]
278 fn test_encode_callsign() {
279 assert_eq!(encode_callsign(" 9 "), Ok(262374389));
280 assert_eq!(encode_callsign("KA1BCD"), Ok(143706369));
281 }
282
283 #[test]
284 fn test_encode_grid() {
285 assert_eq!(encode_grid("AA00"), Ok(32220));
286 assert_eq!(encode_grid("RR99"), Ok(179));
287
288 assert_eq!(encode_grid("Z"), Err(Error::InvalidGrid));
289 assert_eq!(encode_grid("ZZ"), Err(Error::InvalidGrid));
290 assert_eq!(encode_grid("ZZ11"), Err(Error::InvalidGrid));
291 }
292
293 #[test]
294 fn test_encode_power() {
295 assert_eq!(encode_power(61), Err(Error::InvalidPower));
297
298 for power in 0..=60 {
299 let rem = power % 10;
300 let result = encode_power(power);
301 if rem == 0 || rem == 3 || rem == 7 {
302 assert_eq!(result, Ok(power + 64));
303 } else {
304 assert_eq!(result, Err(Error::InvalidPower));
306 }
307 }
308 }
309
310 #[test]
311 fn test_encode_wspr() {
312 assert_eq!(
313 encode("K1A", "FN34", 33),
314 Ok([
315 3, 3, 0, 0, 2, 2, 0, 0, 1, 2, 0, 0, 1, 1, 1, 0, 2, 0, 3, 0, 0,
316 3, 0, 1, 1, 3, 3, 0, 0, 0, 0, 2, 0, 2, 1, 0, 0, 3, 2, 1, 2, 0,
317 2, 0, 0, 0, 3, 0, 1, 3, 0, 2, 3, 1, 2, 3, 0, 2, 0, 3, 3, 0, 1,
318 2, 2, 0, 0, 1, 3, 2, 1, 0, 3, 2, 3, 2, 1, 0, 0, 3, 2, 2, 1, 2,
319 1, 1, 0, 0, 0, 1, 1, 0, 3, 2, 3, 2, 2, 2, 3, 0, 2, 2, 0, 0, 1,
320 2, 2, 1, 2, 0, 1, 3, 1, 2, 3, 3, 0, 0, 1, 1, 2, 3, 2, 2, 0, 3,
321 1, 3, 2, 2, 0, 2, 0, 3, 0, 3, 2, 0, 1, 1, 2, 2, 0, 0, 2, 2, 2,
322 1, 3, 2, 3, 2, 3, 1, 2, 0, 0, 3, 1, 2, 2, 2
323 ])
324 );
325
326 assert_eq!(
327 encode("N6AB", "CM87", 0),
328 Ok([
329 3, 1, 0, 0, 2, 2, 0, 2, 1, 0, 2, 0, 1, 3, 3, 0, 2, 0, 3, 0, 0,
330 1, 2, 1, 3, 1, 1, 2, 0, 2, 0, 0, 0, 2, 3, 2, 0, 1, 0, 3, 2, 0,
331 0, 0, 0, 2, 1, 0, 1, 3, 2, 0, 3, 1, 2, 1, 2, 0, 2, 3, 3, 0, 3,
332 0, 2, 2, 2, 3, 3, 0, 3, 2, 3, 2, 3, 2, 3, 0, 2, 1, 2, 2, 1, 0,
333 1, 3, 2, 2, 0, 1, 1, 0, 1, 2, 1, 0, 2, 2, 1, 0, 0, 2, 2, 0, 1,
334 0, 2, 3, 0, 2, 1, 1, 1, 0, 3, 3, 2, 0, 3, 1, 0, 3, 2, 0, 0, 3,
335 3, 1, 2, 2, 2, 2, 2, 3, 0, 1, 2, 0, 1, 1, 0, 2, 2, 0, 2, 2, 2,
336 3, 3, 2, 1, 2, 1, 3, 0, 0, 0, 3, 1, 0, 2, 2
337 ])
338 );
339
340 assert_eq!(
341 encode("G1ABC", "IO83", 37),
342 Ok([
343 3, 3, 0, 0, 0, 2, 0, 0, 1, 0, 2, 0, 1, 1, 3, 2, 2, 2, 3, 2, 2,
344 1, 0, 1, 1, 3, 1, 2, 2, 2, 0, 0, 0, 0, 3, 0, 0, 1, 0, 3, 0, 2,
345 2, 2, 0, 2, 3, 2, 1, 3, 2, 2, 3, 3, 0, 1, 0, 0, 0, 1, 3, 2, 3,
346 2, 2, 2, 0, 1, 1, 2, 3, 0, 3, 0, 1, 0, 3, 0, 0, 1, 2, 2, 3, 2,
347 3, 3, 0, 0, 2, 3, 1, 2, 1, 0, 1, 2, 2, 2, 1, 0, 2, 0, 2, 2, 3,
348 2, 0, 1, 0, 0, 3, 1, 1, 2, 3, 3, 2, 2, 1, 1, 2, 1, 2, 0, 0, 1,
349 3, 3, 2, 0, 0, 2, 2, 1, 2, 3, 2, 0, 1, 1, 2, 2, 2, 2, 2, 0, 2,
350 3, 3, 2, 1, 2, 1, 3, 0, 2, 2, 3, 3, 2, 2, 0
351 ])
352 );
353
354 assert_eq!(
355 encode("KA1BCD", "AA00", 33),
356 Ok([
357 3, 3, 2, 2, 0, 2, 0, 2, 3, 2, 0, 2, 1, 1, 1, 0, 0, 2, 1, 0, 2,
358 3, 2, 1, 1, 1, 1, 0, 0, 2, 0, 2, 2, 0, 3, 2, 2, 3, 2, 3, 2, 2,
359 2, 0, 2, 0, 3, 0, 3, 1, 0, 2, 3, 1, 0, 3, 2, 2, 0, 1, 3, 2, 1,
360 2, 0, 2, 0, 3, 3, 0, 3, 2, 1, 2, 1, 0, 3, 0, 2, 3, 0, 0, 3, 0,
361 3, 3, 2, 0, 2, 1, 1, 0, 3, 0, 3, 2, 2, 0, 3, 2, 0, 0, 2, 0, 3,
362 2, 0, 1, 2, 2, 1, 3, 1, 2, 1, 3, 2, 0, 1, 1, 2, 3, 0, 0, 2, 1,
363 3, 3, 2, 0, 2, 2, 2, 3, 0, 1, 2, 2, 1, 1, 0, 2, 0, 0, 0, 0, 2,
364 3, 1, 2, 1, 2, 3, 3, 2, 2, 2, 3, 1, 2, 0, 2
365 ])
366 );
367 }
368}