X-Git-Url: https://code.octet-stream.net/m17rt/blobdiff_plain/bb98d1726f35a41236b66bcf9fa0a8fa36633342..3903e719137aba15d30dd58b8d917965ec602400:/m17core/src/modem.rs?ds=inline diff --git a/m17core/src/modem.rs b/m17core/src/modem.rs index b333ef5..43ad5ac 100644 --- a/m17core/src/modem.rs +++ b/m17core/src/modem.rs @@ -29,6 +29,8 @@ pub struct SoftDemodulator { sample: u64, /// Remaining samples to read in before attempting to decode the current candidate samples_until_decode: Option, + /// Do we think there is a data carrier, i.e., channel in use? If so, at what sample does it expire? + dcd: Option, } impl SoftDemodulator { @@ -41,6 +43,25 @@ impl SoftDemodulator { 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"); + } } } } @@ -59,6 +80,7 @@ impl Demodulator for SoftDemodulator { 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; @@ -97,6 +119,9 @@ impl Demodulator for SoftDemodulator { return Some(Frame::Packet(frame)); } } + SyncBurst::Preamble | SyncBurst::EndOfTransmission => { + // should never be chosen as a candidate + } } } } @@ -107,6 +132,15 @@ impl Demodulator for SoftDemodulator { 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, @@ -148,6 +182,8 @@ impl Demodulator for SoftDemodulator { 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 +333,7 @@ impl SoftModulator { 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, @@ -348,7 +385,6 @@ impl Modulator for SoftModulator { 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; @@ -376,12 +412,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; - // 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 @@ -467,6 +500,12 @@ impl Modulator for SoftModulator { } } +impl Default for SoftModulator { + fn default() -> Self { + Self::new() + } +} + #[derive(Debug)] pub(crate) struct DecodeCandidate { burst: SyncBurst,