]> code.octet-stream.net Git - m17rt/blobdiff - m17core/src/modem.rs
fast demod bin
[m17rt] / m17core / src / modem.rs
index b333ef56ad992eaa261a086cab39783e4e560e7c..e7f249fe1b88c2b43402029cf551c22bdd4158ec 100644 (file)
@@ -1,5 +1,5 @@
 use crate::decode::{
 use crate::decode::{
-    parse_lsf, parse_packet, parse_stream, sync_burst_correlation, SyncBurst, SYNC_THRESHOLD,
+    SYNC_THRESHOLD, SyncBurst, parse_lsf, parse_packet, parse_stream, sync_burst_correlation,
 };
 use crate::encode::{
     encode_lsf, encode_packet, encode_stream, generate_end_of_transmission, generate_preamble,
 };
 use crate::encode::{
     encode_lsf, encode_packet, encode_stream, generate_end_of_transmission, generate_preamble,
@@ -9,7 +9,11 @@ use crate::shaping::RRC_48K;
 use log::debug;
 
 pub trait Demodulator {
 use log::debug;
 
 pub trait Demodulator {
-    fn demod(&mut self, sample: i16) -> Option<Frame>;
+    /// Handle the next sample.
+    ///
+    /// If a frame can be decoded, return it, along with an indication of many errors were fixed by FEC.
+    fn demod(&mut self, sample: i16) -> Option<(Frame, u8)>;
+    /// Does somebody else appear to be transmitting at the moment?
     fn data_carrier_detect(&self) -> bool;
 }
 
     fn data_carrier_detect(&self) -> bool;
 }
 
@@ -29,6 +33,8 @@ pub struct SoftDemodulator {
     sample: u64,
     /// Remaining samples to read in before attempting to decode the current candidate
     samples_until_decode: Option<u16>,
     sample: u64,
     /// Remaining samples to read in before attempting to decode the current candidate
     samples_until_decode: Option<u16>,
+    /// Do we think there is a data carrier, i.e., channel in use? If so, at what sample does it expire?
+    dcd: Option<u64>,
 }
 
 impl SoftDemodulator {
 }
 
 impl SoftDemodulator {
@@ -41,12 +47,31 @@ impl SoftDemodulator {
             candidate: None,
             sample: 0,
             samples_until_decode: None,
             candidate: None,
             sample: 0,
             samples_until_decode: None,
+            dcd: None,
+        }
+    }
+}
+
+impl SoftDemodulator {
+    fn dcd_until(&mut self, end_sample: u64) {
+        if self.dcd.is_none() {
+            debug!("SoftDemodulator DCD on");
+        }
+        self.dcd = Some(end_sample);
+    }
+
+    fn check_dcd(&mut self) {
+        if let Some(end_sample) = self.dcd {
+            if self.sample > end_sample {
+                self.dcd = None;
+                debug!("SoftDemodulator DCD off");
+            }
         }
     }
 }
 
 impl Demodulator for SoftDemodulator {
         }
     }
 }
 
 impl Demodulator for SoftDemodulator {
-    fn demod(&mut self, sample: i16) -> Option<Frame> {
+    fn demod(&mut self, sample: i16) -> Option<(Frame, u8)> {
         self.filter_win[self.filter_cursor] = sample;
         self.filter_cursor = (self.filter_cursor + 1) % 81;
         let mut out: f32 = 0.0;
         self.filter_win[self.filter_cursor] = sample;
         self.filter_cursor = (self.filter_cursor + 1) % 81;
         let mut out: f32 = 0.0;
@@ -59,6 +84,7 @@ impl Demodulator for SoftDemodulator {
         self.rx_cursor = (self.rx_cursor + 1) % 1920;
 
         self.sample += 1;
         self.rx_cursor = (self.rx_cursor + 1) % 1920;
 
         self.sample += 1;
+        self.check_dcd();
 
         if let Some(samples_until_decode) = self.samples_until_decode {
             let sud = samples_until_decode - 1;
 
         if let Some(samples_until_decode) = self.samples_until_decode {
             let sud = samples_until_decode - 1;
@@ -80,23 +106,26 @@ impl Demodulator for SoftDemodulator {
                 }
                 match c.burst {
                     SyncBurst::Lsf => {
                 }
                 match c.burst {
                     SyncBurst::Lsf => {
-                        if let Some(frame) = parse_lsf(&pkt_samples) {
-                            return Some(Frame::Lsf(frame));
+                        if let Some((frame, errors)) = parse_lsf(&pkt_samples) {
+                            return Some((Frame::Lsf(frame), errors));
                         }
                     }
                     SyncBurst::Bert => {
                         // TODO: BERT
                     }
                     SyncBurst::Stream => {
                         }
                     }
                     SyncBurst::Bert => {
                         // TODO: BERT
                     }
                     SyncBurst::Stream => {
-                        if let Some(frame) = parse_stream(&pkt_samples) {
-                            return Some(Frame::Stream(frame));
+                        if let Some((frame, errors)) = parse_stream(&pkt_samples) {
+                            return Some((Frame::Stream(frame), errors));
                         }
                     }
                     SyncBurst::Packet => {
                         }
                     }
                     SyncBurst::Packet => {
-                        if let Some(frame) = parse_packet(&pkt_samples) {
-                            return Some(Frame::Packet(frame));
+                        if let Some((frame, errors)) = parse_packet(&pkt_samples) {
+                            return Some((Frame::Packet(frame), errors));
                         }
                     }
                         }
                     }
+                    SyncBurst::Preamble | SyncBurst::EndOfTransmission => {
+                        // should never be chosen as a candidate
+                    }
                 }
             }
         }
                 }
             }
         }
@@ -107,6 +136,15 @@ impl Demodulator for SoftDemodulator {
             burst_window[i] = self.rx_win[c];
         }
 
             burst_window[i] = self.rx_win[c];
         }
 
+        for burst in [SyncBurst::Preamble, SyncBurst::EndOfTransmission] {
+            let (diff, _, _) = sync_burst_correlation(burst.target(), &burst_window);
+            if diff < SYNC_THRESHOLD {
+                // arbitrary choice, 240 samples = 5ms
+                // these bursts keep repeating so it will keep pushing out the DCD end time
+                self.dcd_until(self.sample + 240);
+            }
+        }
+
         for burst in [
             SyncBurst::Lsf,
             SyncBurst::Bert,
         for burst in [
             SyncBurst::Lsf,
             SyncBurst::Bert,
@@ -148,6 +186,8 @@ impl Demodulator for SoftDemodulator {
                     self.sample - c.age as u64,
                     c.diff
                 );
                     self.sample - c.age as u64,
                     c.diff
                 );
+                // After any of these frame types you would expect to see a full EOT
+                self.dcd_until(self.sample + 1920 + 1920);
             }
         }
 
             }
         }
 
@@ -297,6 +337,7 @@ impl SoftModulator {
             next_len: 0,
             next_read: 0,
             tx_delay_padding: 0,
             next_len: 0,
             next_read: 0,
             tx_delay_padding: 0,
+            // TODO: actually set this to false when we are worried about underrun
             update_idle: true,
             idle: true,
             calculate_tx_end: false,
             update_idle: true,
             idle: true,
             calculate_tx_end: false,
@@ -348,7 +389,6 @@ impl Modulator for SoftModulator {
         capacity: usize,
         output_latency: usize,
     ) {
         capacity: usize,
         output_latency: usize,
     ) {
-        //log::debug!("modulator update_output_buffer {samples_to_play} {capacity} {output_latency}");
         self.output_latency = output_latency;
         self.buf_capacity = capacity;
         self.samples_in_buf = samples_to_play;
         self.output_latency = output_latency;
         self.buf_capacity = capacity;
         self.samples_in_buf = samples_to_play;
@@ -376,12 +416,9 @@ impl Modulator for SoftModulator {
             ModulatorFrame::Preamble { tx_delay } => {
                 // TODO: Stop assuming 48 kHz everywhere. 24 kHz should be fine too.
                 let tx_delay_samples = tx_delay as usize * 480;
             ModulatorFrame::Preamble { tx_delay } => {
                 // TODO: Stop assuming 48 kHz everywhere. 24 kHz should be fine too.
                 let tx_delay_samples = tx_delay as usize * 480;
-                // TxDelay and output latency have the same effect - account for whichever is bigger.
-                // We want our sound card DAC hitting preamble right when PTT fully engages.
-                // The modulator calls the shots here - TNC hands over Preamble and asserts PTT, then
-                // waits to be told when transmission will be complete. This estimate will not be
-                // made and delivered until we generate the EOT frame.
-                self.tx_delay_padding = tx_delay_samples.max(self.output_latency);
+                // Our output latency gives us a certain amount of unavoidable TxDelay
+                // So only introduce artificial delay if the requested TxDelay exceeds that
+                self.tx_delay_padding = tx_delay_samples.saturating_sub(self.output_latency);
 
                 // We should be starting from a filter_win of zeroes
                 // Transmission is effectively smeared by 80 taps and we'll capture that in EOT
 
                 // We should be starting from a filter_win of zeroes
                 // Transmission is effectively smeared by 80 taps and we'll capture that in EOT
@@ -467,6 +504,12 @@ impl Modulator for SoftModulator {
     }
 }
 
     }
 }
 
+impl Default for SoftModulator {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
 #[derive(Debug)]
 pub(crate) struct DecodeCandidate {
     burst: SyncBurst,
 #[derive(Debug)]
 pub(crate) struct DecodeCandidate {
     burst: SyncBurst,