]> code.octet-stream.net Git - m17rt/blobdiff - m17core/src/tnc.rs
Start handling KISS frames in SoftTnc
[m17rt] / m17core / src / tnc.rs
index 26f8f4ab7e6764be7c374d31f8c8897022af7062..d4e07b4e8344dd6ad9709b146c9c8592d042f3f9 100644 (file)
@@ -1,5 +1,9 @@
-use crate::kiss::{KissBuffer, KissFrame};
-use crate::protocol::{Frame, LichCollection, LsfFrame, Mode, PacketFrameCounter};
+use crate::address::{Address, Callsign};
+use crate::kiss::{KissBuffer, KissFrame, PORT_PACKET_BASIC, PORT_PACKET_FULL, PORT_STREAM};
+use crate::modem::ModulatorFrame;
+use crate::protocol::{
+    Frame, LichCollection, LsfFrame, Mode, PacketFrame, PacketFrameCounter, StreamFrame,
+};
 
 /// Handles the KISS protocol and frame management for `SoftModulator` and `SoftDemodulator`.
 ///
 
 /// Handles the KISS protocol and frame management for `SoftModulator` and `SoftDemodulator`.
 ///
@@ -14,6 +18,51 @@ pub struct SoftTnc {
 
     /// Current RX or TX function of the TNC.
     state: State,
 
     /// Current RX or TX function of the TNC.
     state: State,
+
+    /// Latest state of data carrier detect from demodulator - controls whether we can go to TX
+    dcd: bool,
+
+    /// Current monotonic time, counted in samples
+    now: u64,
+
+    // TODO: use a static ring buffer crate of some sort?
+    /// Circular buffer of packets enqueued for transmission
+    packet_queue: [PendingPacket; 4],
+
+    /// Next slot to fill
+    packet_next: usize,
+
+    /// Current packet index, which is either partly transmitted or not transmitted at all.
+    packet_curr: usize,
+
+    /// If true, packet_next == packet_curr implies full queue. packet_next is invalid.
+    /// If false, it implies empty queue.
+    packet_full: bool,
+
+    /// The LSF for a stream we are going to start transmitting.
+    ///
+    /// This serves as a general indicator that we want to tx a stream.
+    stream_pending_lsf: Option<LsfFrame>,
+
+    /// Circular buffer of stream data enqueued for transmission.
+    ///
+    /// When the queue empties out, we hope that the last one has the end-of-stream flag set.
+    /// Otherwise a buffer underrun has occurred.
+    ///
+    /// Overruns are less troublesome - we can drop frames and receiving stations should cope.
+    stream_queue: [StreamFrame; 8],
+
+    /// Next slot to fill
+    stream_next: usize,
+
+    /// Current unsent stream frame index
+    stream_curr: usize,
+
+    /// True if stream_next == stream_curr because the queue is full. stream_next is invalid.
+    stream_full: bool,
+
+    /// Should PTT be on right now? Polled by external
+    ptt: bool,
 }
 
 impl SoftTnc {
 }
 
 impl SoftTnc {
@@ -22,11 +71,23 @@ impl SoftTnc {
             kiss_buffer: KissBuffer::new(),
             outgoing_kiss: None,
             state: State::Idle,
             kiss_buffer: KissBuffer::new(),
             outgoing_kiss: None,
             state: State::Idle,
+            dcd: false,
+            now: 0,
+            packet_queue: Default::default(),
+            packet_next: 0,
+            packet_curr: 0,
+            packet_full: false,
+            stream_pending_lsf: None,
+            stream_queue: Default::default(),
+            stream_next: 0,
+            stream_curr: 0,
+            stream_full: false,
+            ptt: false,
         }
     }
 
     /// Process an individual `Frame` that has been decoded by the modem.
         }
     }
 
     /// Process an individual `Frame` that has been decoded by the modem.
-    pub fn handle_frame(&mut self, frame: Frame) -> Result<(), SoftTncError> {
+    pub fn handle_frame(&mut self, frame: Frame) {
         match frame {
             Frame::Lsf(lsf) => {
                 // A new LSF implies a clean slate.
         match frame {
             Frame::Lsf(lsf) => {
                 // A new LSF implies a clean slate.
@@ -65,6 +126,7 @@ impl SoftTnc {
                                 let end = start + payload_len;
                                 rx.packet[start..(start + payload_len)]
                                     .copy_from_slice(&packet.payload);
                                 let end = start + payload_len;
                                 rx.packet[start..(start + payload_len)]
                                     .copy_from_slice(&packet.payload);
+                                // TODO: compatible packets should be sent on port 0 too
                                 let kiss =
                                     KissFrame::new_full_packet(&rx.lsf.0, &rx.packet[0..end])
                                         .unwrap();
                                 let kiss =
                                     KissFrame::new_full_packet(&rx.lsf.0, &rx.packet[0..end])
                                         .unwrap();
@@ -104,7 +166,7 @@ impl SoftTnc {
                             let lsf = LsfFrame(maybe_lsf);
                             // LICH can change mid-transmission so wait until the CRC is correct
                             // to ensure (to high probability) we haven't done a "torn read"
                             let lsf = LsfFrame(maybe_lsf);
                             // LICH can change mid-transmission so wait until the CRC is correct
                             // to ensure (to high probability) we haven't done a "torn read"
-                            if lsf.crc() == 0 {
+                            if lsf.check_crc() == 0 {
                                 let kiss = KissFrame::new_stream_setup(&lsf.0).unwrap();
                                 self.kiss_to_host(kiss);
                                 // TODO: avoid discarding the first data payload here
                                 let kiss = KissFrame::new_stream_setup(&lsf.0).unwrap();
                                 self.kiss_to_host(kiss);
                                 // TODO: avoid discarding the first data payload here
@@ -126,25 +188,108 @@ impl SoftTnc {
                 }
             }
         }
                 }
             }
         }
-        Ok(())
     }
 
     }
 
-    /// Update the number of samples that have been received by the incoming stream, as a form of timekeeping
-    pub fn advance_samples(&mut self, _samples: u64) {}
+    pub fn set_data_carrier_detect(&mut self, dcd: bool) {
+        self.dcd = dcd;
+    }
 
 
-    pub fn set_data_carrier_detect(&mut self, _dcd: bool) {}
+    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;
+                }
+            }
+            _ => (),
+        }
+    }
+
+    pub fn ptt(&self) -> bool {
+        self.ptt
+    }
 
 
-    pub fn read_tx_frame(&mut self) -> Result<Option<Frame>, SoftTncError> {
-        // yes we want to deal with Frames here
-        // it's important to establish successful decode that SoftDemodulator is aware of the frame innards
-        Ok(None)
+    pub fn set_tx_end_time(&mut self, in_samples: usize) {
+        match self.state {
+            State::TxEnding => {
+                self.state = State::TxEndingAtTime(self.now + in_samples as u64);
+            }
+            _ => (),
+        }
+    }
+
+    pub fn read_tx_frame(&mut self) -> Option<ModulatorFrame> {
+        match self.state {
+            State::Idle | State::RxAcquiringStream(_) | State::RxStream(_) | State::RxPacket(_) => {
+                // We will let CSMA decide whether to actually go ahead.
+                // That's not implemented yet, so let's just check DCD.
+                let channel_free = !self.dcd;
+                let stream_wants_to_tx = self.stream_pending_lsf.is_some();
+                let packet_wants_to_tx = self.packet_full || (self.packet_next != self.packet_curr);
+                if channel_free && stream_wants_to_tx {
+                    self.state = State::TxStream;
+                } else if channel_free && packet_wants_to_tx {
+                    self.state = State::TxPacket;
+                } else {
+                    return None;
+                }
+                self.ptt = true;
+                // TODO: true txdelay
+                Some(ModulatorFrame::Preamble { tx_delay: 0 })
+            }
+            State::TxStream => {
+                if !self.stream_full && self.stream_next == self.stream_curr {
+                    return None;
+                }
+                if let Some(lsf) = self.stream_pending_lsf.take() {
+                    return Some(ModulatorFrame::Lsf(lsf));
+                }
+                let frame = self.stream_queue[self.stream_curr].clone();
+                if self.stream_full {
+                    self.stream_full = false;
+                }
+                self.stream_curr = (self.stream_curr + 1) % 8;
+                if frame.end_of_stream {
+                    self.state = State::Idle;
+                }
+                Some(ModulatorFrame::Stream(frame))
+            }
+            State::TxStreamSentEndOfStream => {
+                self.state = State::TxEnding;
+                Some(ModulatorFrame::EndOfTransmission)
+            }
+            State::TxPacket => {
+                if !self.packet_full && self.packet_next == self.packet_curr {
+                    return None;
+                }
+                while self.packet_next != self.packet_curr {
+                    match self.packet_queue[self.packet_curr].next_frame() {
+                        Some(frame) => {
+                            return Some(frame);
+                        }
+                        None => {
+                            self.packet_curr = (self.packet_curr + 1) % 4;
+                        }
+                    }
+                }
+                self.state = State::TxEnding;
+                Some(ModulatorFrame::EndOfTransmission)
+            }
+            State::TxEnding | State::TxEndingAtTime(_) => {
+                // Once we have signalled EOT we withold any new frames until
+                // the channel fully clears and we are ready to TX again
+                None
+            }
+        }
     }
 
     /// Read KISS message to be sent to host.
     ///
     /// After each frame input, this should be consumed in a loop until length 0 is returned.
     /// This component will never block. Upstream interface can provide blocking `read()` if desired.
     }
 
     /// Read KISS message to be sent to host.
     ///
     /// After each frame input, this should be consumed in a loop until length 0 is returned.
     /// This component will never block. Upstream interface can provide blocking `read()` if desired.
-    pub fn read_kiss(&mut self, target_buf: &mut [u8]) -> Result<usize, SoftTncError> {
+    pub fn read_kiss(&mut self, target_buf: &mut [u8]) -> usize {
         match self.outgoing_kiss.as_mut() {
             Some(outgoing) => {
                 let n = (outgoing.kiss_frame.len - outgoing.sent).min(target_buf.len());
         match self.outgoing_kiss.as_mut() {
             Some(outgoing) => {
                 let n = (outgoing.kiss_frame.len - outgoing.sent).min(target_buf.len());
@@ -154,21 +299,75 @@ impl SoftTnc {
                 if outgoing.sent == outgoing.kiss_frame.len {
                     self.outgoing_kiss = None;
                 }
                 if outgoing.sent == outgoing.kiss_frame.len {
                     self.outgoing_kiss = None;
                 }
-                Ok(n)
+                n
             }
             }
-            None => Ok(0),
+            None => 0,
         }
     }
 
         }
     }
 
-    pub fn write_kiss(&mut self, buf: &[u8]) -> Result<usize, SoftTncError> {
+    /// Host sends in some KISS data.
+    pub fn write_kiss(&mut self, buf: &[u8]) -> usize {
         let target_buf = self.kiss_buffer.buf_remaining();
         let n = buf.len().min(target_buf.len());
         target_buf[0..n].copy_from_slice(&buf[0..n]);
         self.kiss_buffer.did_write(n);
         let target_buf = self.kiss_buffer.buf_remaining();
         let n = buf.len().min(target_buf.len());
         target_buf[0..n].copy_from_slice(&buf[0..n]);
         self.kiss_buffer.did_write(n);
-        while let Some(_kiss_frame) = self.kiss_buffer.next_frame() {
-            // TODO: handle host-to-TNC message
+        while let Some(kiss_frame) = self.kiss_buffer.next_frame() {
+            let Ok(port) = kiss_frame.port() else {
+                continue;
+            };
+            if port == PORT_PACKET_BASIC {
+                if self.packet_full {
+                    continue;
+                }
+                let mut pending = PendingPacket::new();
+                pending.app_data[0] = 0x00; // RAW
+                let Ok(mut len) = kiss_frame.decode_payload(&mut pending.app_data[1..]) else {
+                    continue;
+                };
+                len += 1; // for RAW prefix
+                let packet_crc = crate::crc::m17_crc(&pending.app_data[0..len]);
+                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::Broadcast,
+                ));
+                self.packet_queue[self.packet_next] = pending;
+                self.packet_next = (self.packet_next + 1) % 4;
+                if self.packet_next == self.packet_curr {
+                    self.packet_full = true;
+                }
+            } else if port == PORT_PACKET_FULL {
+                if self.packet_full {
+                    continue;
+                }
+                let mut pending = PendingPacket::new();
+                let mut payload = [0u8; 855];
+                let Ok(len) = kiss_frame.decode_payload(&mut payload) else {
+                    continue;
+                };
+                if len < 33 {
+                    continue;
+                }
+                let mut lsf = LsfFrame([0u8; 30]);
+                lsf.0.copy_from_slice(&payload[0..30]);
+                if lsf.check_crc() != 0 {
+                    continue;
+                }
+                pending.lsf = Some(lsf);
+                let app_data_len = len - 30;
+                pending.app_data[0..app_data_len].copy_from_slice(&payload[30..]);
+                pending.app_data_len = app_data_len;
+                self.packet_queue[self.packet_next] = pending;
+                self.packet_next = (self.packet_next + 1) % 4;
+                if self.packet_next == self.packet_curr {
+                    self.packet_full = true;
+                }
+            } else if port == PORT_STREAM {
+                // TODO: handle port 2
+            }
         }
         }
-        Ok(n)
+        n
     }
 
     fn kiss_to_host(&mut self, kiss_frame: KissFrame) {
     }
 
     fn kiss_to_host(&mut self, kiss_frame: KissFrame) {
@@ -191,7 +390,7 @@ struct OutgoingKiss {
 }
 
 enum State {
 }
 
 enum State {
-    /// Nothing happening.
+    /// Nothing happening. We may have TX data queued but we won't act on it until CSMA opens up.
     Idle,
 
     /// We received some stream data but missed the leading LSF so we are trying to assemble from LICH.
     Idle,
 
     /// We received some stream data but missed the leading LSF so we are trying to assemble from LICH.
@@ -202,7 +401,21 @@ enum State {
 
     /// We are receiving a packet. All is well so far, and there is more data to come before we tell the host.
     RxPacket(RxPacketState),
 
     /// We are receiving a packet. All is well so far, and there is more data to come before we tell the host.
     RxPacket(RxPacketState),
-    // TODO: TX
+
+    /// PTT is on and this is a stream-type transmission. New data may be added.
+    TxStream,
+
+    /// We have delivered the last frame in the current stream
+    TxStreamSentEndOfStream,
+
+    /// PTT is on and this is a packet-type transmission. New packets may be enqueued.
+    TxPacket,
+
+    /// We gave modulator an EndOfTransmission. PTT is still on, waiting for modulator to advise end time.
+    TxEnding,
+
+    /// Ending transmission, PTT remains on, but we know the timestamp at which we should disengage it.
+    TxEndingAtTime(u64),
 }
 
 struct RxAcquiringStreamState {
 }
 
 struct RxAcquiringStreamState {
@@ -230,6 +443,70 @@ struct RxPacketState {
     count: usize,
 }
 
     count: usize,
 }
 
+struct PendingPacket {
+    lsf: Option<LsfFrame>,
+
+    app_data: [u8; 825],
+    app_data_len: usize,
+    app_data_transmitted: usize,
+}
+
+impl PendingPacket {
+    fn new() -> Self {
+        Self {
+            lsf: None,
+            app_data: [0u8; 825],
+            app_data_len: 0,
+            app_data_transmitted: 0,
+        }
+    }
+
+    /// Returns next frame, not including preamble or EOT.
+    ///
+    /// False means all data frames have been sent.
+    fn next_frame(&mut self) -> Option<ModulatorFrame> {
+        if let Some(lsf) = self.lsf.take() {
+            return Some(ModulatorFrame::Lsf(lsf));
+        }
+        if self.app_data_len == self.app_data_transmitted {
+            return None;
+        }
+        let remaining = self.app_data_len - self.app_data_transmitted;
+        let (counter, data_len) = if remaining <= 25 {
+            (
+                PacketFrameCounter::FinalFrame {
+                    payload_len: remaining,
+                },
+                remaining,
+            )
+        } else {
+            (
+                PacketFrameCounter::Frame {
+                    index: self.app_data_transmitted / 25,
+                },
+                25,
+            )
+        };
+        let mut payload = [0u8; 25];
+        payload.copy_from_slice(
+            &self.app_data[self.app_data_transmitted..(self.app_data_transmitted + data_len)],
+        );
+        self.app_data_transmitted += data_len;
+        Some(ModulatorFrame::Packet(PacketFrame { payload, counter }))
+    }
+}
+
+impl Default for PendingPacket {
+    fn default() -> Self {
+        Self {
+            lsf: None,
+            app_data: [0u8; 825],
+            app_data_len: 0,
+            app_data_transmitted: 0,
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -271,10 +548,10 @@ mod tests {
         };
         let mut tnc = SoftTnc::new();
         let mut kiss = KissFrame::new_empty();
         };
         let mut tnc = SoftTnc::new();
         let mut kiss = KissFrame::new_empty();
-        assert_eq!(tnc.read_kiss(&mut kiss.data), Ok(0));
+        assert_eq!(tnc.read_kiss(&mut kiss.data), 0);
 
 
-        tnc.handle_frame(Frame::Lsf(lsf)).unwrap();
-        kiss.len = tnc.read_kiss(&mut kiss.data).unwrap();
+        tnc.handle_frame(Frame::Lsf(lsf));
+        kiss.len = tnc.read_kiss(&mut kiss.data);
         assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame);
         assert_eq!(kiss.port().unwrap(), PORT_STREAM);
 
         assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame);
         assert_eq!(kiss.port().unwrap(), PORT_STREAM);
 
@@ -282,16 +559,16 @@ mod tests {
         let n = kiss.decode_payload(&mut payload_buf).unwrap();
         assert_eq!(n, 30);
 
         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();
+        tnc.handle_frame(Frame::Stream(stream1));
+        kiss.len = tnc.read_kiss(&mut kiss.data);
         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);
 
         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();
+        tnc.handle_frame(Frame::Stream(stream2));
+        kiss.len = tnc.read_kiss(&mut kiss.data);
         assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame);
         assert_eq!(kiss.port().unwrap(), PORT_STREAM);
 
         assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame);
         assert_eq!(kiss.port().unwrap(), PORT_STREAM);
 
@@ -361,9 +638,9 @@ mod tests {
         let mut tnc = SoftTnc::new();
         let mut kiss = KissFrame::new_empty();
         for f in frames {
         let mut tnc = SoftTnc::new();
         let mut kiss = KissFrame::new_empty();
         for f in frames {
-            tnc.handle_frame(Frame::Stream(f)).unwrap();
+            tnc.handle_frame(Frame::Stream(f));
         }
         }
-        kiss.len = tnc.read_kiss(&mut kiss.data).unwrap();
+        kiss.len = tnc.read_kiss(&mut kiss.data);
         let mut payload_buf = [0u8; 2048];
         let n = kiss.decode_payload(&mut payload_buf).unwrap();
         assert_eq!(n, 30);
         let mut payload_buf = [0u8; 2048];
         let n = kiss.decode_payload(&mut payload_buf).unwrap();
         assert_eq!(n, 30);
@@ -402,10 +679,10 @@ mod tests {
         };
         let mut tnc = SoftTnc::new();
         let mut kiss = KissFrame::new_empty();
         };
         let mut tnc = SoftTnc::new();
         let mut kiss = KissFrame::new_empty();
-        assert_eq!(tnc.read_kiss(&mut kiss.data), Ok(0));
+        assert_eq!(tnc.read_kiss(&mut kiss.data), 0);
 
 
-        tnc.handle_frame(Frame::Lsf(lsf)).unwrap();
-        kiss.len = tnc.read_kiss(&mut kiss.data).unwrap();
+        tnc.handle_frame(Frame::Lsf(lsf));
+        kiss.len = tnc.read_kiss(&mut kiss.data);
         assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame);
         assert_eq!(kiss.port().unwrap(), PORT_STREAM);
 
         assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame);
         assert_eq!(kiss.port().unwrap(), PORT_STREAM);
 
@@ -413,16 +690,16 @@ mod tests {
         let n = kiss.decode_payload(&mut payload_buf).unwrap();
         assert_eq!(n, 30);
 
         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();
+        tnc.handle_frame(Frame::Stream(stream1));
+        kiss.len = tnc.read_kiss(&mut kiss.data);
         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);
 
         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();
+        tnc.handle_frame(Frame::Stream(stream3));
+        kiss.len = tnc.read_kiss(&mut kiss.data);
         assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame);
         assert_eq!(kiss.port().unwrap(), PORT_STREAM);
 
         assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame);
         assert_eq!(kiss.port().unwrap(), PORT_STREAM);