]> code.octet-stream.net Git - m17rt/blobdiff - m17core/src/tnc.rs
More unit tests for TNC rx/assembly - packet mode
[m17rt] / m17core / src / tnc.rs
index 93a4363ae62f0939100ee51a04166fc548595edd..93f14203e956ee8ca3f517b281c646a98323082e 100644 (file)
@@ -102,6 +102,10 @@ impl SoftTnc {
 
     /// Process an individual `Frame` that has been decoded by the modem.
     pub fn handle_frame(&mut self, frame: Frame) {
+        if self.ptt {
+            // Ignore self-decodes
+            return;
+        }
         match frame {
             Frame::Lsf(lsf) => {
                 // A new LSF implies a clean slate.
@@ -213,15 +217,16 @@ impl SoftTnc {
 
     pub fn set_now(&mut self, now_samples: u64) {
         self.now = now_samples;
-        match self.state {
-            State::TxEndingAtTime(time) => {
-                if now_samples >= time {
-                    self.ptt = false;
-                    self.state = State::Idle;
-                }
+        // TODO: expose this to higher layer so we can schedule a precise delay
+        // rather than waiting for some soundcard I/O event
+        if let State::TxEndingAtTime(time) = self.state {
+            if now_samples >= time {
+                self.ptt = false;
+                self.state = State::Idle;
             }
-            _ => (),
         }
+        // TODO: should expire packet rx if we have not received a frame for a while
+        // otherwise we could pick up the LSF from one station and FinalFrame from another
     }
 
     pub fn ptt(&self) -> bool {
@@ -230,11 +235,8 @@ impl SoftTnc {
 
     pub fn set_tx_end_time(&mut self, in_samples: usize) {
         log::debug!("tnc has been told that tx will complete in {in_samples} samples");
-        match self.state {
-            State::TxEnding => {
-                self.state = State::TxEndingAtTime(self.now + in_samples as u64);
-            }
-            _ => (),
+        if let State::TxEnding = self.state {
+            self.state = State::TxEndingAtTime(self.now + in_samples as u64);
         }
     }
 
@@ -407,7 +409,7 @@ impl SoftTnc {
                 pending.app_data[len..len + 2].copy_from_slice(&packet_crc.to_be_bytes());
                 pending.app_data_len = len + 2;
                 pending.lsf = Some(LsfFrame::new_packet(
-                    &Address::Callsign(Callsign(b"M17RT-PKT".clone())),
+                    &Address::Callsign(Callsign(*b"M17RT-PKT")),
                     &Address::Broadcast,
                 ));
                 self.packet_queue[self.packet_next] = pending;
@@ -487,6 +489,12 @@ impl SoftTnc {
     }
 }
 
+impl Default for SoftTnc {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
 #[derive(Debug, PartialEq, Eq, Clone)]
 pub enum SoftTncError {
     General(&'static str),
@@ -498,6 +506,7 @@ struct OutgoingKiss {
     sent: usize,
 }
 
+#[allow(clippy::large_enum_variant)]
 enum State {
     /// Nothing happening. We may have TX data queued but we won't act on it until CSMA opens up.
     Idle,
@@ -620,16 +629,156 @@ impl Default for PendingPacket {
 mod tests {
     use super::*;
     use crate::kiss::{KissCommand, PORT_STREAM};
-    use crate::protocol::StreamFrame;
+    use crate::protocol::{PacketType, StreamFrame};
 
-    // TODO: finish all handle_frame tests as below
-    // this will be much more straightforward when we have a way to create LSFs programatically
+    #[test]
+    fn tnc_receive_single_frame_packet() {
+        let lsf = LsfFrame::new_packet(
+            &Address::Callsign(Callsign(*b"VK7XT    ")),
+            &Address::Broadcast,
+        );
+        let mut payload = [0u8; 25];
+        let (pt, pt_len) = PacketType::Sms.as_proto();
+        payload[0..pt_len].copy_from_slice(&pt[0..pt_len]);
+        payload[pt_len] = 0x41; // a message
+        let crc = crate::crc::m17_crc(&payload[0..=pt_len]).to_be_bytes();
+        payload[pt_len + 1] = crc[0];
+        payload[pt_len + 2] = crc[1];
+
+        let packet = PacketFrame {
+            payload,
+            counter: PacketFrameCounter::FinalFrame {
+                payload_len: pt_len + 3,
+            },
+        };
+        let mut tnc = SoftTnc::new();
+        let mut kiss = KissFrame::new_empty();
 
-    // receiving a single-frame packet
+        // TNC consumes LSF but has nothing to report yet
+        tnc.handle_frame(Frame::Lsf(lsf));
+        assert_eq!(tnc.read_kiss(&mut kiss.data), 0);
 
-    // receiving a multi-frame packet
+        // TODO: when support is added for the basic packet port they could arrive in either order
 
-    // part of one packet and then another
+        tnc.handle_frame(Frame::Packet(packet));
+        kiss.len = tnc.read_kiss(&mut kiss.data);
+        assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame);
+        assert_eq!(kiss.port().unwrap(), PORT_PACKET_FULL);
+
+        let mut payload_buf = [0u8; 2048];
+        let n = kiss.decode_payload(&mut payload_buf).unwrap();
+        assert_eq!(n, 30 + 1 + pt_len + 2);
+
+        // did we receive our message? (after the LSF)
+        assert_eq!(payload_buf[pt_len + 30], 0x41);
+    }
+
+    #[test]
+    fn tnc_receive_multiple_frame_packet() {
+        let lsf = LsfFrame::new_packet(
+            &Address::Callsign(Callsign(*b"VK7XT    ")),
+            &Address::Broadcast,
+        );
+        let mut payload = [0x41u8; 26]; // spans two frames
+        let (pt, pt_len) = PacketType::Sms.as_proto();
+        payload[0..pt_len].copy_from_slice(&pt[0..pt_len]);
+        let crc = crate::crc::m17_crc(&payload[0..24]).to_be_bytes();
+        payload[24] = crc[0];
+        payload[25] = crc[1];
+
+        let packet1 = PacketFrame {
+            payload: payload[0..25].try_into().unwrap(),
+            counter: PacketFrameCounter::Frame { index: 0 },
+        };
+        let mut payload2 = [0u8; 25];
+        payload2[0] = payload[25];
+        let packet2 = PacketFrame {
+            payload: payload2,
+            counter: PacketFrameCounter::FinalFrame { payload_len: 1 },
+        };
+
+        let mut tnc = SoftTnc::new();
+        let mut kiss = KissFrame::new_empty();
+
+        // Nothing to report until second final packet frame received
+        tnc.handle_frame(Frame::Lsf(lsf));
+        assert_eq!(tnc.read_kiss(&mut kiss.data), 0);
+        tnc.handle_frame(Frame::Packet(packet1));
+        assert_eq!(tnc.read_kiss(&mut kiss.data), 0);
+
+        tnc.handle_frame(Frame::Packet(packet2));
+        kiss.len = tnc.read_kiss(&mut kiss.data);
+        assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame);
+        assert_eq!(kiss.port().unwrap(), PORT_PACKET_FULL);
+
+        let mut payload_buf = [0u8; 2048];
+        let n = kiss.decode_payload(&mut payload_buf).unwrap();
+        assert_eq!(n, 30 + 26);
+
+        // did we receive our message? (after the LSF)
+        assert_eq!(payload_buf[pt_len + 30], 0x41);
+    }
+
+    #[test]
+    fn tnc_receive_partial_packet() {
+        let lsf = LsfFrame::new_packet(
+            &Address::Callsign(Callsign(*b"VK7XT    ")),
+            &Address::Broadcast,
+        );
+        let mut payload = [0x41u8; 26]; // spans two frames
+        let (pt, pt_len) = PacketType::Sms.as_proto();
+        payload[0..pt_len].copy_from_slice(&pt[0..pt_len]);
+        let crc = crate::crc::m17_crc(&payload[0..24]).to_be_bytes();
+        payload[24] = crc[0];
+        payload[25] = crc[1];
+
+        let packet1 = PacketFrame {
+            payload: payload[0..25].try_into().unwrap(),
+            counter: PacketFrameCounter::Frame { index: 0 },
+        };
+        // final frame of this transmission is dropped
+
+        let lsf2 = LsfFrame::new_packet(
+            &Address::Callsign(Callsign(*b"VK7XT    ")),
+            &Address::Broadcast,
+        );
+        let mut payload = [0u8; 25];
+        let (pt, pt_len) = PacketType::Sms.as_proto();
+        payload[0..pt_len].copy_from_slice(&pt[0..pt_len]);
+        payload[pt_len] = 0x42;
+        let crc = crate::crc::m17_crc(&payload[0..=pt_len]).to_be_bytes();
+        payload[pt_len + 1] = crc[0];
+        payload[pt_len + 2] = crc[1];
+
+        let packet2 = PacketFrame {
+            payload: payload[0..25].try_into().unwrap(),
+            counter: PacketFrameCounter::FinalFrame {
+                payload_len: pt_len + 3,
+            },
+        };
+
+        let mut tnc = SoftTnc::new();
+        let mut kiss = KissFrame::new_empty();
+
+        // Nothing to report until second packet received in its entirety
+        tnc.handle_frame(Frame::Lsf(lsf));
+        assert_eq!(tnc.read_kiss(&mut kiss.data), 0);
+        tnc.handle_frame(Frame::Packet(packet1));
+        assert_eq!(tnc.read_kiss(&mut kiss.data), 0);
+        tnc.handle_frame(Frame::Lsf(lsf2));
+        assert_eq!(tnc.read_kiss(&mut kiss.data), 0);
+        tnc.handle_frame(Frame::Packet(packet2));
+
+        kiss.len = tnc.read_kiss(&mut kiss.data);
+        assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame);
+        assert_eq!(kiss.port().unwrap(), PORT_PACKET_FULL);
+
+        let mut payload_buf = [0u8; 2048];
+        let n = kiss.decode_payload(&mut payload_buf).unwrap();
+        assert_eq!(n, 30 + 1 + pt_len + 2);
+        // we have received the second packet which has 0x42 in it
+        assert_eq!(payload_buf[pt_len + 30], 0x42);
+    }
 
     #[test]
     fn tnc_receive_stream() {