]> code.octet-stream.net Git - m17rt/blob - m17core/src/address.rs
Begin adding support for sound card output via codec2 adapter
[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 ///
14 /// If the "std" feature is enabled then callsigns be converted to or created from strings.
15 #[derive(Debug, Clone, PartialEq, Eq)]
16 pub struct Callsign([u8; 9]);
17
18 static ALPHABET: [u8; 40] = [
19 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',
20 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',
21 b'5', b'6', b'7', b'8', b'9', b'-', b'/', b'.',
22 ];
23
24 // should be len 6
25 pub fn decode_address(encoded: [u8; 6]) -> Address {
26 let full = u64::from_be_bytes([
27 0, 0, encoded[0], encoded[1], encoded[2], encoded[3], encoded[4], encoded[5],
28 ]);
29 match full {
30 m @ 1..=0xEE6B27FFFFFF => Address::Callsign(decode_base_40(m)),
31 m @ 0xEE6B28000000..=0xFFFFFFFFFFFE => Address::Reserved(m),
32 0xFFFFFFFFFFFF => Address::Broadcast,
33 _ => Address::Invalid,
34 }
35 }
36
37 fn decode_base_40(mut encoded: u64) -> Callsign {
38 let mut callsign = Callsign([b' '; 9]);
39 let mut pos = 0;
40 while encoded > 0 {
41 callsign.0[pos] = ALPHABET[(encoded % 40) as usize];
42 encoded /= 40;
43 pos += 1;
44 }
45 callsign
46 }
47
48 #[allow(dead_code)]
49 pub fn encode_address(address: &Address) -> [u8; 6] {
50 let mut out: u64 = 0;
51 match address {
52 Address::Invalid => (),
53 Address::Callsign(call) => {
54 for c in call.0.iter().rev() {
55 let c = c.to_ascii_uppercase();
56 if let Some(pos) = ALPHABET.iter().position(|alpha| *alpha == c) {
57 out = out * 40 + pos as u64;
58 }
59 }
60 }
61 Address::Reserved(m) => out = *m,
62 Address::Broadcast => out = 0xFFFFFFFFFFFF,
63 }
64 out.to_be_bytes()[2..].try_into().unwrap()
65 }
66
67 #[cfg(test)]
68 mod tests {
69 use super::*;
70
71 #[test]
72 fn address_encode() {
73 let encoded = encode_address(&Address::Callsign(Callsign(
74 b"AB1CD ".as_slice().try_into().unwrap(),
75 )));
76 assert_eq!(encoded, [0x00, 0x00, 0x00, 0x9f, 0xdd, 0x51]);
77 }
78
79 #[test]
80 fn address_decode() {
81 let decoded = decode_address([0x00, 0x00, 0x00, 0x9f, 0xdd, 0x51]);
82 assert_eq!(
83 decoded,
84 Address::Callsign(Callsign(b"AB1CD ".as_slice().try_into().unwrap()))
85 );
86 }
87 }