]> code.octet-stream.net Git - m17rt/blob - m17core/src/reflector/convert.rs
add RfToVoice converter for reflector tx path
[m17rt] / m17core / src / reflector / convert.rs
1 //! Utilities for converting streams between UDP and RF representations
2
3 use crate::protocol::{LsfFrame, StreamFrame};
4
5 use super::packet::Voice;
6
7 /// Accepts `Voice` packets from a reflector and turns them into LSF and Stream frames.
8 ///
9 /// This is the format required for the voice data to cross the KISS protocol boundary.
10 pub struct VoiceToRf {
11 /// Link Setup most recently acquired
12 lsf: Option<LsfFrame>,
13 /// Which LICH part we are going to emit next, 0-5
14 lich_cnt: usize,
15 }
16
17 impl VoiceToRf {
18 pub fn new() -> Self {
19 Self {
20 lsf: None,
21 lich_cnt: 0,
22 }
23 }
24
25 /// For a Voice packet received from a reflector, return the frames that would be transmitted
26 /// on RF, including by reconstructing the LICH parts of the stream frame.
27 ///
28 /// If this is the start of a new or different stream transmission, this returns the Link Setup
29 /// Frame which comes first, then the first associated Stream frame.
30 ///
31 /// If this is a continuation of a transmission matching the previous LSF, then it returns only
32 /// the Stream frame.
33 pub fn next(&mut self, voice: &Voice) -> (Option<LsfFrame>, StreamFrame) {
34 let this_lsf = voice.link_setup_frame();
35 let emit_lsf = if Some(&this_lsf) != self.lsf.as_ref() {
36 self.lsf = Some(this_lsf.clone());
37 self.lich_cnt = 0;
38 true
39 } else {
40 false
41 };
42 let lsf = self.lsf.as_ref().unwrap();
43 let stream = StreamFrame {
44 lich_idx: self.lich_cnt as u8,
45 lich_part: (&lsf.0[self.lich_cnt * 5..(self.lich_cnt + 1) * 5])
46 .try_into()
47 .unwrap(),
48 frame_number: voice.frame_number(),
49 end_of_stream: voice.is_end_of_stream(),
50 stream_data: voice.payload().try_into().unwrap(),
51 };
52 let lsf = if emit_lsf { self.lsf.clone() } else { None };
53 if voice.is_end_of_stream() {
54 self.lsf = None;
55 }
56 (lsf, stream)
57 }
58 }
59
60 /// Accepts LSF and stream RF payloads and merges them into `Voice` packets for reflector use.
61 ///
62 /// For a series of transmissions this object should be re-used so that Stream ID is correctly
63 /// changed after each new LSF.
64 pub struct RfToVoice {
65 lsf: LsfFrame,
66 stream_id: u16,
67 }
68
69 impl RfToVoice {
70 pub fn new(lsf: LsfFrame) -> Self {
71 Self { lsf, stream_id: 0 }
72 }
73
74 pub fn process_lsf(&mut self, lsf: LsfFrame) {
75 self.lsf = lsf;
76 self.stream_id = self.stream_id.wrapping_add(1);
77 }
78
79 pub fn process_stream(&self, stream: &StreamFrame) -> Voice {
80 let mut v = Voice::new();
81 v.set_stream_id(self.stream_id);
82 v.set_frame_number(stream.frame_number);
83 v.set_end_of_stream(stream.end_of_stream);
84 v.set_payload(&stream.stream_data);
85 v.set_link_setup_frame(&self.lsf);
86 v
87 }
88 }
89
90 #[cfg(test)]
91 mod tests {
92 use crate::{
93 address::{Address, Callsign},
94 protocol::{LsfFrame, StreamFrame},
95 };
96
97 use super::{RfToVoice, VoiceToRf};
98
99 #[test]
100 fn convert_roundtrip() {
101 let lsf = LsfFrame::new_voice(
102 &Address::Callsign(Callsign(*b"VK7XT ")),
103 &Address::Broadcast,
104 );
105 let stream = StreamFrame {
106 lich_idx: 0,
107 lich_part: lsf.0[0..5].try_into().unwrap(),
108 frame_number: 0,
109 end_of_stream: false,
110 stream_data: [1u8; 16],
111 };
112 let rf_to_voice = RfToVoice::new(lsf.clone());
113 let voice = rf_to_voice.process_stream(&stream);
114
115 let mut voice_to_rf = VoiceToRf::new();
116 let (lsf2, stream2) = voice_to_rf.next(&voice);
117 assert_eq!(lsf2, Some(lsf));
118 assert_eq!(stream2, stream);
119 }
120 }