X-Git-Url: https://code.octet-stream.net/m17rt/blobdiff_plain/d5f46a2d5d294c5a4287780dd609475fa73df9a8..99f4fcbee0b9774a24ef2428ddce71889e602e3b:/m17core/src/modem.rs?ds=inline diff --git a/m17core/src/modem.rs b/m17core/src/modem.rs index 8cbd003..e7f249f 100644 --- a/m17core/src/modem.rs +++ b/m17core/src/modem.rs @@ -1,5 +1,5 @@ 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, @@ -9,7 +9,11 @@ use crate::shaping::RRC_48K; use log::debug; pub trait Demodulator { - fn demod(&mut self, sample: i16) -> Option; + /// 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; } @@ -67,7 +71,7 @@ impl SoftDemodulator { } impl Demodulator for SoftDemodulator { - fn demod(&mut self, sample: i16) -> Option { + 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; @@ -102,21 +106,21 @@ impl Demodulator for SoftDemodulator { } 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 => { - 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 => { - 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 => { @@ -333,6 +337,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, @@ -384,7 +389,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; @@ -412,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; - // 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 @@ -503,6 +504,12 @@ impl Modulator for SoftModulator { } } +impl Default for SoftModulator { + fn default() -> Self { + Self::new() + } +} + #[derive(Debug)] pub(crate) struct DecodeCandidate { burst: SyncBurst,