]> code.octet-stream.net Git - m17rt/blob - m17app/src/link_setup.rs
Spruce up the high-level API for specifying addresses for transmission
[m17rt] / m17app / src / link_setup.rs
1 use std::fmt::Display;
2
3 use m17core::{
4 address::{Address, Callsign, ALPHABET},
5 protocol::LsfFrame,
6 };
7
8 use crate::error::M17Error;
9
10 pub struct LinkSetup {
11 pub(crate) raw: LsfFrame,
12 }
13
14 impl LinkSetup {
15 /// Provide a completed LsfFrame.
16 pub fn new_raw(frame: LsfFrame) -> Self {
17 Self { raw: frame }
18 }
19
20 /// Set up an unencrypted voice stream with channel access number 0 and the given source and destination.
21 pub fn new_voice(source: &M17Address, destination: &M17Address) -> Self {
22 Self {
23 raw: LsfFrame::new_voice(source.address(), destination.address()),
24 }
25 }
26
27 /// Set up an unencrypted packet data transmission with channel access number 0 and the given source and destination.
28 pub fn new_packet(source: &M17Address, destination: &M17Address) -> Self {
29 Self {
30 raw: LsfFrame::new_packet(source.address(), destination.address()),
31 }
32 }
33
34 /// Configure the channel access number for this transmission, which may be from 0 to 15 inclusive.
35 pub fn set_channel_access_number(&mut self, channel_access_number: u8) {
36 self.raw.set_channel_access_number(channel_access_number);
37 }
38
39 pub fn lich_part(&self, counter: u8) -> [u8; 5] {
40 let idx = counter as usize;
41 self.raw.0[idx * 5..(idx + 1) * 5].try_into().unwrap()
42 }
43 }
44
45 /// Station address. High level version of `Address` from core.
46
47 #[derive(Debug, Clone)]
48 pub struct M17Address(Address);
49
50 impl M17Address {
51 pub fn new_broadcast() -> Self {
52 Self(Address::Broadcast)
53 }
54
55 pub fn from_callsign(callsign: &str) -> Result<Self, M17Error> {
56 let trimmed = callsign.trim().to_uppercase();
57 let len = trimmed.len();
58 if len > 9 {
59 return Err(M17Error::CallsignTooLong(len));
60 }
61 let mut address = [b' '; 9];
62 for (i, c) in trimmed.chars().enumerate() {
63 if !c.is_ascii() {
64 return Err(M17Error::InvalidCallsignCharacters(c));
65 }
66 if !ALPHABET.contains(&(c as u8)) {
67 return Err(M17Error::InvalidCallsignCharacters(c));
68 }
69 address[i] = c as u8;
70 }
71 Ok(Self(Address::Callsign(Callsign(address))))
72 }
73
74 pub(crate) fn address(&self) -> &Address {
75 &self.0
76 }
77 }
78
79 impl Display for M17Address {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 match self.0 {
82 Address::Invalid => unreachable!(),
83 Address::Callsign(ref callsign) => {
84 write!(
85 f,
86 "{}",
87 callsign
88 .0
89 .iter()
90 .map(|c| *c as char)
91 .collect::<String>()
92 .trim()
93 )
94 }
95 Address::Reserved(_) => unreachable!(),
96 Address::Broadcast => {
97 write!(f, "<BROADCAST>")
98 }
99 }
100 }
101 }