1use std::convert::TryInto;
2use std::io::Write;
3
4use Callsign;
5use DecodeError;
6use EncodeError;
7use Latitude;
8use Precision;
9
10use crate::Longitude;
11
12#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
13pub enum Message {
14 M0,
15 M1,
16 M2,
17 M3,
18 M4,
19 M5,
20 M6,
21 C0,
22 C1,
23 C2,
24 C3,
25 C4,
26 C5,
27 C6,
28 Emergency,
29 Unknown,
30}
31
32impl Message {
33 fn decode(a: MessageBit, b: MessageBit, c: MessageBit) -> Self {
34 use self::Message::*;
35 use self::MessageBit::{CustomOne, StandardOne, Zero};
36
37 match (a, b, c) {
38 (StandardOne, StandardOne, StandardOne) => M0,
39 (CustomOne, CustomOne, CustomOne) => C0,
40
41 (StandardOne, StandardOne, Zero) => M1,
42 (CustomOne, CustomOne, Zero) => C1,
43
44 (StandardOne, Zero, StandardOne) => M2,
45 (CustomOne, Zero, CustomOne) => C2,
46
47 (StandardOne, Zero, Zero) => M3,
48 (CustomOne, Zero, Zero) => C3,
49
50 (Zero, StandardOne, StandardOne) => M4,
51 (Zero, CustomOne, CustomOne) => C4,
52
53 (Zero, StandardOne, Zero) => M5,
54 (Zero, CustomOne, Zero) => C5,
55
56 (Zero, Zero, StandardOne) => M6,
57 (Zero, Zero, CustomOne) => C6,
58
59 (Zero, Zero, Zero) => Self::Emergency,
60 _ => Self::Unknown,
61 }
62 }
63
64 fn encode(&self) -> (MessageBit, MessageBit, MessageBit) {
65 use self::Message::*;
66 use self::MessageBit::{CustomOne, StandardOne, Zero};
67
68 match self {
69 M0 => (StandardOne, StandardOne, StandardOne),
70 C0 => (CustomOne, CustomOne, CustomOne),
71
72 M1 => (StandardOne, StandardOne, Zero),
73 C1 => (CustomOne, CustomOne, Zero),
74
75 M2 => (StandardOne, Zero, StandardOne),
76 C2 => (CustomOne, Zero, CustomOne),
77
78 M3 => (StandardOne, Zero, Zero),
79 C3 => (CustomOne, Zero, Zero),
80
81 M4 => (Zero, StandardOne, StandardOne),
82 C4 => (Zero, CustomOne, CustomOne),
83
84 M5 => (Zero, StandardOne, Zero),
85 C5 => (Zero, CustomOne, Zero),
86
87 M6 => (Zero, Zero, StandardOne),
88 C6 => (Zero, Zero, CustomOne),
89
90 Message::Emergency => (Zero, Zero, Zero),
91 Message::Unknown => (StandardOne, CustomOne, StandardOne),
93 }
94 }
95}
96
97#[derive(Copy, Clone, Debug, PartialEq, Eq)]
99pub struct Speed(u32);
100
101impl Speed {
102 pub fn new(knots: u32) -> Option<Self> {
104 if knots > 799 {
105 return None;
106 }
107
108 Some(Self(knots))
109 }
110
111 pub fn knots(&self) -> u32 {
112 self.0
113 }
114}
115
116#[derive(Copy, Clone, Debug, PartialEq, Eq)]
120pub struct Course(u32);
121
122impl Course {
123 pub const UNKNOWN: Self = Self(0);
124
125 pub fn new(degrees: u32) -> Option<Self> {
127 if degrees > 360 {
128 return None;
129 }
130
131 Some(Self(degrees))
132 }
133
134 pub fn degrees(&self) -> u32 {
135 self.0
136 }
137}
138
139#[derive(PartialEq, Debug, Clone)]
140pub struct AprsMicE {
141 pub latitude: Latitude,
142 pub longitude: Longitude,
143 pub precision: Precision,
144
145 pub message: Message,
146 pub speed: Speed,
147 pub course: Course,
148 pub symbol_table: u8,
149 pub symbol_code: u8,
150 pub comment: Vec<u8>,
151
152 pub current: bool,
153}
154
155impl AprsMicE {
156 pub fn decode(b: &[u8], to: Callsign, current: bool) -> Result<Self, DecodeError> {
157 let (latitude, precision, message, long_offset, long_dir) =
158 decode_callsign(&to).ok_or(DecodeError::InvalidMicEDestination(to))?;
159
160 let info = b
161 .get(0..8)
162 .ok_or_else(|| DecodeError::InvalidMicEInformation(b.to_vec()))?;
163 let comment = b.get(8..).unwrap_or(&[]).to_vec();
164
165 let longitude = decode_longitude(&info[0..3], long_offset, long_dir)
166 .ok_or_else(|| DecodeError::InvalidMicEInformation(b.to_vec()))?;
167 let (speed, course) = decode_speed_and_course(&info[3..6])
168 .ok_or_else(|| DecodeError::InvalidMicEInformation(b.to_vec()))?;
169 let symbol_code = info[6];
170 let symbol_table = info[7];
171
172 Ok(Self {
173 latitude,
174 longitude,
175 precision,
176
177 message,
178 speed,
179 course,
180 symbol_table,
181 symbol_code,
182 comment,
183
184 current,
185 })
186 }
187
188 pub fn encode<W: Write>(&self, buf: &mut W) -> Result<(), EncodeError> {
189 if self.current {
190 buf.write_all(&[b'`'])?;
191 } else {
192 buf.write_all(&[b'\''])?;
193 }
194
195 self.encode_longitude(buf)?;
196 self.encode_speed_and_course(buf)?;
197
198 buf.write_all(&[self.symbol_code, self.symbol_table])?;
199 buf.write_all(&self.comment)?;
200
201 Ok(())
202 }
203
204 pub fn encode_destination(&self) -> Callsign {
205 let mut encoded_lat = vec![];
206 self.latitude
210 .encode_uncompressed(&mut encoded_lat, self.precision)
211 .unwrap();
212 assert_eq!(8, encoded_lat.len());
213
214 let lat_dir = if *self.latitude >= 0.0 {
215 LatDir::North
216 } else {
217 LatDir::South
218 };
219
220 let (long_deg, _, _, is_east) = self.longitude.dmh();
221
222 let long_dir = if is_east {
223 LongDir::East
224 } else {
225 LongDir::West
226 };
227
228 let long_offset = if (0..=9).contains(&long_deg) || long_deg >= 100 {
229 LongOffset::Hundred
230 } else {
231 LongOffset::Zero
232 };
233
234 let (a, b, c) = self.message.encode();
235
236 let bytes = vec![
237 encode_bits_012(encoded_lat[0], a),
238 encode_bits_012(encoded_lat[1], b),
239 encode_bits_012(encoded_lat[2], c),
240 encode_bit_3(encoded_lat[3], lat_dir),
241 encode_bit_4(encoded_lat[5], long_offset),
242 encode_bit_5(encoded_lat[6], long_dir),
243 ];
244
245 Callsign::new_no_ssid(String::from_utf8(bytes).unwrap())
247 }
248
249 fn encode_longitude<W: Write>(&self, w: &mut W) -> Result<(), EncodeError> {
250 let (d, m, h, _) = self.longitude.dmh();
251
252 let d: u8 = d.try_into().unwrap();
254 let m: u8 = m.try_into().unwrap();
255 let h: u8 = h.try_into().unwrap();
256
257 let d = match d {
258 0..=9 => d + 90,
259 10..=99 => d,
260 100..=109 => d - 20,
261 _ => d - 100,
262 };
263
264 let m = match m {
265 0..=9 => m + 60,
266 _ => m,
267 };
268
269 w.write_all(&[d + 28, m + 28, h + 28])?;
270
271 Ok(())
272 }
273
274 fn encode_speed_and_course<W: Write>(&self, w: &mut W) -> Result<(), EncodeError> {
275 let tens_knots: u8 = (self.speed.knots() / 10).try_into().unwrap();
276 let units_knots = self.speed.knots() % 10;
277
278 let hundreds_course = self.course.degrees() / 100;
279 let units_course = self.course.degrees() % 100;
280
281 let sp: u8 = match tens_knots {
282 0..=19 => tens_knots + 80,
283 _ => tens_knots,
284 };
285 let dc: u8 = (units_knots * 10 + hundreds_course + 4).try_into().unwrap();
286 let se: u8 = (units_course).try_into().unwrap();
287
288 w.write_all(&[sp + 28, dc + 28, se + 28])?;
289
290 Ok(())
291 }
292}
293
294enum MessageBit {
295 Zero,
296 CustomOne,
297 StandardOne,
298}
299
300impl MessageBit {
301 fn decode(c: u8) -> Option<Self> {
302 match c {
303 b'0'..=b'9' | b'L' => Some(MessageBit::Zero),
304 b'A'..=b'K' => Some(MessageBit::CustomOne),
305 b'P'..=b'Z' => Some(MessageBit::StandardOne),
306 _ => None,
307 }
308 }
309}
310
311enum LatDir {
312 North,
313 South,
314}
315
316impl LatDir {
317 fn decode(c: u8) -> Option<Self> {
318 match c {
319 b'0'..=b'9' | b'L' => Some(LatDir::South),
320 b'P'..=b'Z' => Some(LatDir::North),
321 _ => None,
322 }
323 }
324
325 fn byte(self) -> u8 {
326 match self {
327 Self::North => b'N',
328 Self::South => b'S',
329 }
330 }
331}
332
333#[derive(Debug, PartialEq, Eq)]
334enum LongOffset {
335 Zero,
336 Hundred,
337}
338
339impl LongOffset {
340 fn decode(c: u8) -> Option<Self> {
341 match c {
342 b'0'..=b'9' | b'L' => Some(LongOffset::Zero),
343 b'P'..=b'Z' => Some(LongOffset::Hundred),
344 _ => None,
345 }
346 }
347}
348
349#[derive(Debug, PartialEq, Eq)]
350enum LongDir {
351 East,
352 West,
353}
354
355impl LongDir {
356 fn decode(c: u8) -> Option<Self> {
357 match c {
358 b'0'..=b'9' | b'L' => Some(Self::East),
359 b'P'..=b'Z' => Some(Self::West),
360 _ => None,
361 }
362 }
363}
364
365fn decode_latitude_digit(c: u8) -> Option<u8> {
367 match c {
368 b'0'..=b'9' => Some(c),
369 b'A'..=b'J' => Some(c - 17),
370 b'K' | b'L' | b'Z' => Some(b' '),
371 b'P'..=b'Y' => Some(c - 32),
372 _ => None,
373 }
374}
375
376fn decode_callsign(c: &Callsign) -> Option<(Latitude, Precision, Message, LongOffset, LongDir)> {
377 let data = c.call().as_bytes();
378 if data.len() != 6 {
379 return None;
380 }
381
382 let lat_bytes = [
383 decode_latitude_digit(data[0])?,
384 decode_latitude_digit(data[1])?,
385 decode_latitude_digit(data[2])?,
386 decode_latitude_digit(data[3])?,
387 b'.',
388 decode_latitude_digit(data[4])?,
389 decode_latitude_digit(data[5])?,
390 LatDir::decode(data[3])?.byte(),
391 ];
392
393 let (lat, precision) = Latitude::parse_uncompressed(&lat_bytes).ok()?;
394
395 let a = MessageBit::decode(data[0])?;
396 let b = MessageBit::decode(data[1])?;
397 let c = MessageBit::decode(data[2])?;
398
399 let msg = Message::decode(a, b, c);
400
401 let long_offset = LongOffset::decode(data[4])?;
402
403 let long_dir = LongDir::decode(data[5])?;
404
405 Some((lat, precision, msg, long_offset, long_dir))
406}
407
408fn decode_longitude(b: &[u8], offset: LongOffset, dir: LongDir) -> Option<Longitude> {
409 if b.len() != 3 {
410 return None;
411 }
412
413 let mut d = b[0].checked_sub(28)?;
414
415 if offset == LongOffset::Hundred {
416 d = d.checked_add(100)?;
417 }
418
419 if d >= 180 && d <= 189 {
420 d -= 80;
421 } else if d >= 190 && d <= 199 {
422 d -= 190;
423 }
424
425 let mut m = b[1].checked_sub(28)?;
426
427 if m >= 60 {
428 m -= 60;
429 }
430
431 let h = b[2].checked_sub(28)?;
432
433 Longitude::from_dmh(d.into(), m.into(), h.into(), dir == LongDir::East)
434}
435
436fn decode_speed_and_course(b: &[u8]) -> Option<(Speed, Course)> {
437 let sp = u32::from(b[0].checked_sub(28)?);
438
439 let tens_knots = sp * 10;
440
441 let dc = u32::from(b[1].checked_sub(28)?);
442
443 let units_knots = dc / 10;
444 let hundreds_course = (dc % 10) * 100;
445
446 let units_course = u32::from(b[2].checked_sub(28)?);
447
448 let mut speed_knots = tens_knots + units_knots;
449 if speed_knots >= 800 {
450 speed_knots -= 800;
451 }
452
453 let mut course_degrees = hundreds_course + units_course;
454 if course_degrees >= 400 {
455 course_degrees -= 400;
456 }
457
458 let speed = Speed::new(speed_knots)?;
459 let course = Course::new(course_degrees)?;
460
461 Some((speed, course))
462}
463
464fn encode_bits_012(lat_digit: u8, message_bit: MessageBit) -> u8 {
466 match (message_bit, lat_digit == b' ') {
467 (MessageBit::Zero, false) => lat_digit,
468 (MessageBit::Zero, true) => b'L',
469
470 (MessageBit::CustomOne, false) => lat_digit + 17,
471 (MessageBit::CustomOne, true) => b'K',
472
473 (MessageBit::StandardOne, false) => lat_digit + 32,
474 (MessageBit::StandardOne, true) => b'Z',
475 }
476}
477
478fn encode_bit_3(lat_digit: u8, lat_dir: LatDir) -> u8 {
479 match (lat_dir, lat_digit == b' ') {
480 (LatDir::North, false) => lat_digit + 32,
481 (LatDir::North, true) => b'Z',
482
483 (LatDir::South, false) => lat_digit,
484 (LatDir::South, true) => b'L',
485 }
486}
487
488fn encode_bit_4(lat_digit: u8, long_offset: LongOffset) -> u8 {
489 match (long_offset, lat_digit == b' ') {
490 (LongOffset::Zero, false) => lat_digit,
491 (LongOffset::Zero, true) => b'L',
492
493 (LongOffset::Hundred, false) => lat_digit + 32,
494 (LongOffset::Hundred, true) => b'Z',
495 }
496}
497
498fn encode_bit_5(lat_digit: u8, long_dir: LongDir) -> u8 {
499 match (long_dir, lat_digit == b' ') {
500 (LongDir::East, false) => lat_digit,
501 (LongDir::East, true) => b'L',
502
503 (LongDir::West, false) => lat_digit + 32,
504 (LongDir::West, true) => b'Z',
505 }
506}
507
508#[cfg(test)]
509mod tests {
510 use super::*;
511
512 #[test]
513 fn course_from_u32() {
514 let course = Course::new(123).unwrap();
515 assert_eq!(123, course.degrees());
516
517 let course = Course::new(360).unwrap();
518 assert_eq!(360, course.degrees());
519 }
520
521 #[test]
522 fn course_from_u32_smallest() {
523 let course = Course::new(0).unwrap();
524 assert_eq!(0, course.degrees());
525 }
526
527 #[test]
528 fn course_from_u32_too_big() {
529 assert_eq!(None, Course::new(361));
530 }
531
532 #[test]
533 fn speed_from_u32() {
534 let speed = Speed::new(545).unwrap();
535 assert_eq!(545, speed.knots());
536
537 let speed = Speed::new(799).unwrap();
538 assert_eq!(799, speed.knots());
539 }
540
541 #[test]
542 fn speed_from_u32_smallest() {
543 let speed = Speed::new(0).unwrap();
544 assert_eq!(0, speed.knots());
545 }
546
547 #[test]
548 fn speed_from_u32_too_big() {
549 assert_eq!(None, Speed::new(800));
550 assert_eq!(None, Speed::new(801));
551 assert_eq!(None, Speed::new(5237));
552 }
553
554 #[test]
555 fn decode_dest_test() {
556 let (latitude, precision, message, offset, dir) =
557 decode_callsign(&Callsign::new_no_ssid("S32U6T")).unwrap();
558
559 assert_eq!(Latitude::new(33.42733333333333).unwrap(), latitude);
560 assert_eq!(Precision::HundredthMinute, precision);
561 assert_eq!(Message::M3, message);
562 assert_eq!(LongOffset::Zero, offset);
563 assert_eq!(LongDir::West, dir);
564 }
565
566 #[test]
567 fn decode_test() {
568 let information = &br#"(_fn"Oj/Hello world!"#[..];
570 let to = Callsign::new_no_ssid("PPPPPP");
571
572 let data = AprsMicE::decode(information, to.clone(), true).unwrap();
573
574 assert_eq!(
575 AprsMicE {
576 latitude: Latitude::new(0.0).unwrap(),
577 longitude: Longitude::new(-112.12899999999999).unwrap(),
578 precision: Precision::HundredthMinute,
579 message: Message::M0,
580 speed: Speed::new(20).unwrap(),
581 course: Course::new(251).unwrap(),
582 symbol_table: b'/',
583 symbol_code: b'j',
584 comment: b"Hello world!".to_vec(),
585 current: true
586 },
587 data
588 );
589
590 let mut re_encoded = vec![];
591 data.encode(&mut re_encoded).unwrap();
592
593 assert_eq!(information, &re_encoded[1..]);
595 assert_eq!(to, data.encode_destination());
596 }
597
598 #[test]
599 fn encode_destination_test() {
600 let information = &br#"(_fn"Oj/Hello world!"#[..];
601 let to = Callsign::new_no_ssid("S5PPW4");
602
603 let data = AprsMicE::decode(information, to.clone(), true).unwrap();
604
605 assert_eq!(to, data.encode_destination());
606 }
607}