]> code.octet-stream.net Git - m17rt/blob - m17core/src/address.rs
Implementing soundmodem tx path
[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 pub fn encode_address(address: &Address) -> [u8; 6] {
49 let mut out: u64 = 0;
50 match address {
51 Address::Invalid => (),
52 Address::Callsign(call) => {
53 for c in call.0.iter().rev() {
54 let c = c.to_ascii_uppercase();
55 if let Some(pos) = ALPHABET.iter().position(|alpha| *alpha == c) {
56 out = out * 40 + pos as u64;
57 }
58 }
59 }
60 Address::Reserved(m) => out = *m,
61 Address::Broadcast => out = 0xFFFFFFFFFFFF,
62 }
63 out.to_be_bytes()[2..].try_into().unwrap()
64 }
65
66 #[cfg(test)]
67 mod tests {
68 use super::*;
69
70 #[test]
71 fn address_encode() {
72 let encoded = encode_address(&Address::Callsign(Callsign(
73 b"AB1CD ".as_slice().try_into().unwrap(),
74 )));
75 assert_eq!(encoded, [0x00, 0x00, 0x00, 0x9f, 0xdd, 0x51]);
76 }
77
78 #[test]
79 fn address_decode() {
80 let decoded = decode_address([0x00, 0x00, 0x00, 0x9f, 0xdd, 0x51]);
81 assert_eq!(
82 decoded,
83 Address::Callsign(Callsign(b"AB1CD ".as_slice().try_into().unwrap()))
84 );
85 }
86 }