]> code.octet-stream.net Git - m17rt/commitdiff
Partial unit tests for stream frame handling by TNC
authorThomas Karpiniec <tom.karpiniec@outlook.com>
Thu, 2 Jan 2025 10:25:30 +0000 (21:25 +1100)
committerThomas Karpiniec <tom.karpiniec@outlook.com>
Thu, 2 Jan 2025 10:25:30 +0000 (21:25 +1100)
m17core/src/kiss.rs
m17core/src/tnc.rs

index b8c2f1fc76e261f66fa1d797031f51680445ebc4..ec30296a31ab5d9706529897e25b148d23b9aa31 100644 (file)
@@ -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<Self, KissError> {
-        if stream_data.len() != 26 {
-            return Err(KissError::StreamDataWrongSize);
-        }
+    pub fn new_stream_data(frame: &StreamFrame) -> Result<Self, KissError> {
         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 {
index a847683a548d360f3a25f11a912e6e8c2a663018..26f8f4ab7e6764be7c374d31f8c8897022af7062 100644 (file)
@@ -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);
+    }
+}