From 7b7b572052951018da4d6d0f5753d627836236ad Mon Sep 17 00:00:00 2001 From: Thomas Karpiniec Date: Thu, 2 Jan 2025 21:25:30 +1100 Subject: [PATCH 1/1] Partial unit tests for stream frame handling by TNC --- m17core/src/kiss.rs | 32 ++++--- m17core/src/tnc.rs | 208 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 226 insertions(+), 14 deletions(-) diff --git a/m17core/src/kiss.rs b/m17core/src/kiss.rs index b8c2f1f..ec30296 100644 --- a/m17core/src/kiss.rs +++ b/m17core/src/kiss.rs @@ -1,3 +1,5 @@ +use crate::protocol::StreamFrame; + // Note FEND and FESC both have the top two bits set. In the header byte this corresponds // to high port numbers which are never used by M17 so we needn't bother (un)escaping it. @@ -117,14 +119,7 @@ impl KissFrame { } /// Transmit a segment of data in a stream transfer (e.g. voice). - /// - /// A data payload of 26 bytes including metadata must be provided. This must follow - /// exactly the prescribed format (H.5.2 in the spec). The TNC will be watching for - /// the EOS flag to know that this transmission has ended. - pub fn new_stream_data(stream_data: &[u8]) -> Result { - if stream_data.len() != 26 { - return Err(KissError::StreamDataWrongSize); - } + pub fn new_stream_data(frame: &StreamFrame) -> Result { let mut data = [0u8; MAX_FRAME_LEN]; let mut i = 0; push(&mut data, &mut i, FEND); @@ -133,9 +128,25 @@ impl KissFrame { &mut i, kiss_header(PORT_STREAM, KissCommand::DataFrame.proto_value()), ); - i += escape(stream_data, &mut data[i..]); - push(&mut data, &mut i, FEND); + // 5 bytes LICH content + i += escape(&frame.lich_part, &mut data[i..]); + // 1 byte LICH metadata + i += escape(&[frame.lich_idx << 5], &mut data[i..]); + + // 2 bytes frame number/EOS + 16 bytes payload + 2 bytes CRC + let mut inner_data = [0u8; 20]; + let frame_num = frame.frame_number.to_be_bytes(); + inner_data[0] = frame_num[0] | if frame.end_of_stream { 0x80 } else { 0 }; + inner_data[1] = frame_num[1]; + inner_data[2..18].copy_from_slice(&frame.stream_data); + let crc = crate::crc::m17_crc(&inner_data[0..18]); + let crc_be = crc.to_be_bytes(); + inner_data[18] = crc_be[0]; + inner_data[19] = crc_be[1]; + i += escape(&inner_data, &mut data[i..]); + + push(&mut data, &mut i, FEND); Ok(KissFrame { data, len: i }) } @@ -388,7 +399,6 @@ pub enum KissError { UnsupportedKissCommand, PayloadTooBig, LsfWrongSize, - StreamDataWrongSize, } fn escape(src: &[u8], dst: &mut [u8]) -> usize { diff --git a/m17core/src/tnc.rs b/m17core/src/tnc.rs index a847683..26f8f4a 100644 --- a/m17core/src/tnc.rs +++ b/m17core/src/tnc.rs @@ -40,6 +40,8 @@ impl SoftTnc { }) } Mode::Stream => { + let kiss = KissFrame::new_stream_setup(&lsf.0).unwrap(); + self.kiss_to_host(kiss); self.state = State::RxStream(RxStreamState { lsf, index: 0 }); } } @@ -87,9 +89,8 @@ impl SoftTnc { self.state = State::RxAcquiringStream(RxAcquiringStreamState { lich }); } else { rx.index = stream.frame_number + 1; - let kiss = KissFrame::new_stream_data(&stream.stream_data).unwrap(); + let kiss = KissFrame::new_stream_data(&stream).unwrap(); self.kiss_to_host(kiss); - // TODO: handle LICH contents to track META content // TODO: end stream if LICH updates indicate non-META part has changed // (this implies a new station) if stream.end_of_stream { @@ -178,7 +179,7 @@ impl SoftTnc { } } -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq, Clone)] pub enum SoftTncError { General(&'static str), InvalidState, @@ -228,3 +229,204 @@ struct RxPacketState { /// this will be between 0 and 32 inclusive. count: usize, } + +#[cfg(test)] +mod tests { + use super::*; + use crate::kiss::{KissCommand, PORT_STREAM}; + use crate::protocol::StreamFrame; + + // TODO: finish all handle_frame tests as below + // this will be much more straightforward when we have a way to create LSFs programatically + + // receiving a single-frame packet + + // receiving a multi-frame packet + + // part of one packet and then another + + #[test] + fn tnc_receive_stream() { + let lsf = LsfFrame([ + 255, 255, 255, 255, 255, 255, 0, 0, 0, 159, 221, 81, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 131, 53, + ]); + let stream1 = StreamFrame { + lich_idx: 0, + lich_part: [255, 255, 255, 255, 255], + frame_number: 0, + end_of_stream: false, + stream_data: [ + 128, 0, 119, 115, 220, 252, 41, 235, 8, 0, 116, 195, 94, 244, 45, 75, + ], + }; + let stream2 = StreamFrame { + lich_idx: 1, + lich_part: [255, 0, 0, 0, 159], + frame_number: 1, + end_of_stream: true, + stream_data: [ + 17, 0, 94, 82, 216, 135, 181, 15, 30, 0, 125, 195, 152, 183, 41, 57, + ], + }; + let mut tnc = SoftTnc::new(); + let mut kiss = KissFrame::new_empty(); + assert_eq!(tnc.read_kiss(&mut kiss.data), Ok(0)); + + tnc.handle_frame(Frame::Lsf(lsf)).unwrap(); + kiss.len = tnc.read_kiss(&mut kiss.data).unwrap(); + assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame); + assert_eq!(kiss.port().unwrap(), PORT_STREAM); + + let mut payload_buf = [0u8; 2048]; + let n = kiss.decode_payload(&mut payload_buf).unwrap(); + assert_eq!(n, 30); + + tnc.handle_frame(Frame::Stream(stream1)).unwrap(); + kiss.len = tnc.read_kiss(&mut kiss.data).unwrap(); + assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame); + assert_eq!(kiss.port().unwrap(), PORT_STREAM); + + let n = kiss.decode_payload(&mut payload_buf).unwrap(); + assert_eq!(n, 26); + + tnc.handle_frame(Frame::Stream(stream2)).unwrap(); + kiss.len = tnc.read_kiss(&mut kiss.data).unwrap(); + assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame); + assert_eq!(kiss.port().unwrap(), PORT_STREAM); + + let n = kiss.decode_payload(&mut payload_buf).unwrap(); + assert_eq!(n, 26); + } + + #[test] + fn tnc_acquire_stream() { + let frames = [ + StreamFrame { + lich_idx: 0, + lich_part: [255, 255, 255, 255, 255], + frame_number: 0, + end_of_stream: false, + stream_data: [ + 128, 0, 119, 115, 220, 252, 41, 235, 8, 0, 116, 195, 94, 244, 45, 75, + ], + }, + StreamFrame { + lich_idx: 1, + lich_part: [255, 0, 0, 0, 159], + frame_number: 1, + end_of_stream: false, + stream_data: [ + 17, 0, 94, 82, 216, 135, 181, 15, 30, 0, 125, 195, 152, 183, 41, 57, + ], + }, + StreamFrame { + lich_idx: 2, + lich_part: [221, 81, 5, 5, 0], + frame_number: 2, + end_of_stream: false, + stream_data: [ + 17, 128, 93, 74, 154, 167, 169, 11, 20, 0, 116, 91, 158, 220, 45, 111, + ], + }, + StreamFrame { + lich_idx: 3, + lich_part: [0, 0, 0, 0, 0], + frame_number: 3, + end_of_stream: false, + stream_data: [ + 15, 128, 114, 83, 218, 252, 59, 111, 31, 128, 116, 91, 84, 231, 45, 105, + ], + }, + StreamFrame { + lich_idx: 4, + lich_part: [0, 0, 0, 0, 0], + frame_number: 4, + end_of_stream: false, + stream_data: [ + 9, 128, 119, 115, 220, 220, 57, 15, 48, 128, 124, 83, 158, 236, 181, 91, + ], + }, + StreamFrame { + lich_idx: 5, + lich_part: [0, 0, 0, 131, 53], + frame_number: 5, + end_of_stream: false, + stream_data: [ + 52, 0, 116, 90, 152, 167, 225, 216, 32, 0, 116, 83, 156, 212, 33, 216, + ], + }, + ]; + + let mut tnc = SoftTnc::new(); + let mut kiss = KissFrame::new_empty(); + for f in frames { + tnc.handle_frame(Frame::Stream(f)).unwrap(); + } + kiss.len = tnc.read_kiss(&mut kiss.data).unwrap(); + let mut payload_buf = [0u8; 2048]; + let n = kiss.decode_payload(&mut payload_buf).unwrap(); + assert_eq!(n, 30); + assert_eq!( + &payload_buf[0..30], + [ + 255, 255, 255, 255, 255, 255, 0, 0, 0, 159, 221, 81, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 131, 53, + ] + ); + } + + #[test] + fn tnc_handle_skipped_stream_frame() { + let lsf = LsfFrame([ + 255, 255, 255, 255, 255, 255, 0, 0, 0, 159, 221, 81, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 131, 53, + ]); + let stream1 = StreamFrame { + lich_idx: 0, + lich_part: [255, 255, 255, 255, 255], + frame_number: 0, + end_of_stream: false, + stream_data: [ + 128, 0, 119, 115, 220, 252, 41, 235, 8, 0, 116, 195, 94, 244, 45, 75, + ], + }; + let stream3 = StreamFrame { + lich_idx: 2, + lich_part: [221, 81, 5, 5, 0], + frame_number: 2, + end_of_stream: false, + stream_data: [ + 17, 128, 93, 74, 154, 167, 169, 11, 20, 0, 116, 91, 158, 220, 45, 111, + ], + }; + let mut tnc = SoftTnc::new(); + let mut kiss = KissFrame::new_empty(); + assert_eq!(tnc.read_kiss(&mut kiss.data), Ok(0)); + + tnc.handle_frame(Frame::Lsf(lsf)).unwrap(); + kiss.len = tnc.read_kiss(&mut kiss.data).unwrap(); + assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame); + assert_eq!(kiss.port().unwrap(), PORT_STREAM); + + let mut payload_buf = [0u8; 2048]; + let n = kiss.decode_payload(&mut payload_buf).unwrap(); + assert_eq!(n, 30); + + tnc.handle_frame(Frame::Stream(stream1)).unwrap(); + kiss.len = tnc.read_kiss(&mut kiss.data).unwrap(); + assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame); + assert_eq!(kiss.port().unwrap(), PORT_STREAM); + + let n = kiss.decode_payload(&mut payload_buf).unwrap(); + assert_eq!(n, 26); + + tnc.handle_frame(Frame::Stream(stream3)).unwrap(); + kiss.len = tnc.read_kiss(&mut kiss.data).unwrap(); + assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame); + assert_eq!(kiss.port().unwrap(), PORT_STREAM); + + let n = kiss.decode_payload(&mut payload_buf).unwrap(); + assert_eq!(n, 26); + } +} -- 2.39.5