X-Git-Url: https://code.octet-stream.net/m17rt/blobdiff_plain/bb98d1726f35a41236b66bcf9fa0a8fa36633342..99f4fcbee0b9774a24ef2428ddce71889e602e3b:/m17core/src/modem.rs?ds=sidebyside
diff --git a/m17core/src/modem.rs b/m17core/src/modem.rs
index b333ef5..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;
}
@@ -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,
+ /// 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,12 +47,31 @@ 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");
+ }
}
}
}
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;
@@ -59,6 +84,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;
@@ -80,23 +106,26 @@ 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 => {
+ // should never be chosen as a candidate
+ }
}
}
}
@@ -107,6 +136,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 +186,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 +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,
@@ -348,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;
@@ -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;
- // 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 +504,12 @@ impl Modulator for SoftModulator {
}
}
+impl Default for SoftModulator {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
#[derive(Debug)]
pub(crate) struct DecodeCandidate {
burst: SyncBurst,