]> code.octet-stream.net Git - m17rt/blob - m17core/src/address.rs
Fix packet encoding
[m17rt] / m17core / src / address.rs
1 #[derive(Debug, Clone, PartialEq, Eq)]
2 pub enum Address {
3 Invalid,
4 Callsign(Callsign),
5 Reserved(u64),
6 Broadcast,
7 }
8
9 /// ASCII representation of a callsign address.
10 ///
11 /// May be up to 9 characters long - if it shorter then remaining space is filled with
12 /// space characters.
13 #[derive(Debug, Clone, PartialEq, Eq)]
14 pub struct Callsign(pub [u8; 9]);
15
16 static ALPHABET: [u8; 40] = [
17 b' ', b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O',
18 b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', b'0', b'1', b'2', b'3', b'4',
19 b'5', b'6', b'7', b'8', b'9', b'-', b'/', b'.',
20 ];
21
22 // should be len 6
23 pub fn decode_address(encoded: [u8; 6]) -> Address {
24 let full = u64::from_be_bytes([
25 0, 0, encoded[0], encoded[1], encoded[2], encoded[3], encoded[4], encoded[5],
26 ]);
27 match full {
28 m @ 1..=0xEE6B27FFFFFF => Address::Callsign(decode_base_40(m)),
29 m @ 0xEE6B28000000..=0xFFFFFFFFFFFE => Address::Reserved(m),
30 0xFFFFFFFFFFFF => Address::Broadcast,
31 _ => Address::Invalid,
32 }
33 }
34
35 fn decode_base_40(mut encoded: u64) -> Callsign {
36 let mut callsign = Callsign([b' '; 9]);
37 let mut pos = 0;
38 while encoded > 0 {
39 callsign.0[pos] = ALPHABET[(encoded % 40) as usize];
40 encoded /= 40;
41 pos += 1;
42 }
43 callsign
44 }
45
46 pub fn encode_address(address: &Address) -> [u8; 6] {
47 let mut out: u64 = 0;
48 match address {
49 Address::Invalid => (),
50 Address::Callsign(call) => {
51 for c in call.0.iter().rev() {
52 let c = c.to_ascii_uppercase();
53 if let Some(pos) = ALPHABET.iter().position(|alpha| *alpha == c) {
54 out = out * 40 + pos as u64;
55 }
56 }
57 }
58 Address::Reserved(m) => out = *m,
59 Address::Broadcast => out = 0xFFFFFFFFFFFF,
60 }
61 out.to_be_bytes()[2..].try_into().unwrap()
62 }
63
64 #[cfg(test)]
65 mod tests {
66 use super::*;
67
68 #[test]
69 fn address_encode() {
70 let encoded = encode_address(&Address::Callsign(Callsign(
71 b"AB1CD ".as_slice().try_into().unwrap(),
72 )));
73 assert_eq!(encoded, [0x00, 0x00, 0x00, 0x9f, 0xdd, 0x51]);
74 }
75
76 #[test]
77 fn address_decode() {
78 let decoded = decode_address([0x00, 0x00, 0x00, 0x9f, 0xdd, 0x51]);
79 assert_eq!(
80 decoded,
81 Address::Callsign(Callsign(b"AB1CD ".as_slice().try_into().unwrap()))
82 );
83 }
84 }