From: Thomas Karpiniec Date: Tue, 15 Jul 2025 11:13:55 +0000 (+1000) Subject: fast demod bin X-Git-Url: https://code.octet-stream.net/m17rt/commitdiff_plain/refs/heads/master?ds=inline fast demod bin --- diff --git a/Cargo.lock b/Cargo.lock index 4800984..51fa142 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -547,6 +547,16 @@ dependencies = [ "m17core", ] +[[package]] +name = "m17rt-fastdemod" +version = "0.1.0" +dependencies = [ + "clap", + "env_logger", + "log", + "m17core", +] + [[package]] name = "m17rt-mod" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 3ea3218..2883db3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,4 +2,4 @@ resolver = "2" members = [ "m17app", "m17codec2", "m17core", "tools/m17rt-demod", "tools/m17rt-mod", "tools/m17rt-txpacket", "tools/m17rt-rxpacket", "tools/m17rt-soundcards" -, "tools/m17rt-netclient"] +, "tools/m17rt-netclient", "tools/m17rt-fastdemod"] diff --git a/m17app/src/soundmodem.rs b/m17app/src/soundmodem.rs index c782d86..19a3dd5 100644 --- a/m17app/src/soundmodem.rs +++ b/m17app/src/soundmodem.rs @@ -234,7 +234,7 @@ fn spawn_soundmodem_worker( } SoundmodemEvent::BasebandInput(b) => { for sample in &*b { - if let Some(frame) = demodulator.demod(*sample) { + if let Some((frame, _)) = demodulator.demod(*sample) { tnc.handle_frame(frame); loop { let n = tnc.read_kiss(&mut buf); diff --git a/m17app/src/util/out_buffer.rs b/m17app/src/util/out_buffer.rs index 06d8e91..399ae71 100644 --- a/m17app/src/util/out_buffer.rs +++ b/m17app/src/util/out_buffer.rs @@ -1,7 +1,7 @@ //! Buffer between `read()` calls use std::{ - io::{self, ErrorKind, Read}, + io::{self, Read}, sync::{Arc, Mutex, mpsc::Receiver}, }; @@ -49,7 +49,7 @@ impl Read for OutBuffer { let output = { let rx = self.rx.lock().unwrap(); rx.recv() - .map_err(|s| io::Error::new(ErrorKind::Other, format!("{:?}", s)))? + .map_err(|s| io::Error::other(format!("{:?}", s)))? }; let to_write = output.len().min(buf.len()); buf[0..to_write].copy_from_slice(&output[0..to_write]); diff --git a/m17core/src/decode.rs b/m17core/src/decode.rs index 4bc6288..3195ed5 100644 --- a/m17core/src/decode.rs +++ b/m17core/src/decode.rs @@ -91,11 +91,11 @@ pub(crate) fn frame_initial_decode(frame: &[f32] /* length 192 */) -> [u8; 46] { interleave(&decoded[2..]) } -pub(crate) fn parse_lsf(frame: &[f32] /* length 192 */) -> Option { +pub(crate) fn parse_lsf(frame: &[f32] /* length 192 */) -> Option<(LsfFrame, u8)> { let deinterleaved = frame_initial_decode(frame); debug!("deinterleaved: {:?}", deinterleaved); - let lsf = match fec::decode(&deinterleaved, 240, p_1) { - Some(lsf) => LsfFrame(lsf), + let (lsf, errors) = match fec::decode(&deinterleaved, 240, p_1) { + Some((lsf, errors)) => (LsfFrame(lsf), errors), None => return None, }; debug!("full lsf: {:?}", lsf.0); @@ -108,13 +108,13 @@ pub(crate) fn parse_lsf(frame: &[f32] /* length 192 */) -> Option { debug!("encryption type: {:?}", lsf.encryption_type()); debug!("can: {}", lsf.channel_access_number()); debug!("meta: {:?}", lsf.meta()); - Some(lsf) + Some((lsf, errors)) } -pub(crate) fn parse_stream(frame: &[f32] /* length 192 */) -> Option { +pub(crate) fn parse_stream(frame: &[f32] /* length 192 */) -> Option<(StreamFrame, u8)> { let deinterleaved = frame_initial_decode(frame); let stream_part = &deinterleaved[12..]; - let stream = fec::decode(stream_part, 144, p_2)?; + let (stream, errors) = fec::decode(stream_part, 144, p_2)?; let frame_num = u16::from_be_bytes([stream[0], stream[1]]); let eos = (frame_num & 0x8000) > 0; let frame_num = frame_num & 0x7fff; // higher layer has to handle wraparound @@ -125,21 +125,24 @@ pub(crate) fn parse_stream(frame: &[f32] /* length 192 */) -> Option Option { +pub(crate) fn parse_packet(frame: &[f32] /* length 192 */) -> Option<(PacketFrame, u8)> { let deinterleaved = frame_initial_decode(frame); - let packet = fec::decode(&deinterleaved, 206, p_3)?; + let (packet, errors) = fec::decode(&deinterleaved, 206, p_3)?; let final_frame = (packet[25] & 0x80) > 0; let number = (packet[25] >> 2) & 0x1f; let counter = if final_frame { @@ -151,10 +154,13 @@ pub(crate) fn parse_packet(frame: &[f32] /* length 192 */) -> Option Option<(u8, [u8; 5])> { diff --git a/m17core/src/encode.rs b/m17core/src/encode.rs index 94d280b..0ba20d7 100644 --- a/m17core/src/encode.rs +++ b/m17core/src/encode.rs @@ -113,7 +113,7 @@ mod tests { ]); let encoded = encode_lsf(&lsf); let decoded = crate::decode::parse_lsf(&encoded); - assert_eq!(decoded, Some(lsf)); + assert!(matches!(decoded, Some((frame, _)) if frame == lsf)); } #[test] @@ -127,7 +127,7 @@ mod tests { }; let encoded = encode_stream(&stream); let decoded = crate::decode::parse_stream(&encoded); - assert_eq!(decoded, Some(stream)); + assert!(matches!(decoded, Some((frame, _)) if frame == stream)); } #[test] @@ -138,7 +138,7 @@ mod tests { }; let encoded = encode_packet(&packet); let decoded = crate::decode::parse_packet(&encoded); - assert_eq!(decoded, Some(packet)); + assert!(matches!(decoded, Some((frame, _)) if frame == packet)); let packet = PacketFrame { payload: [0u8; 25], @@ -146,7 +146,7 @@ mod tests { }; let encoded = encode_packet(&packet); let decoded = crate::decode::parse_packet(&encoded); - assert_eq!(decoded, Some(packet)); + assert!(matches!(decoded, Some((frame, _)) if frame == packet)); } #[test] diff --git a/m17core/src/fec.rs b/m17core/src/fec.rs index 5a49f87..3bf709d 100644 --- a/m17core/src/fec.rs +++ b/m17core/src/fec.rs @@ -212,7 +212,7 @@ pub(crate) fn decode( type3: &[u8], // up to len 46 input_len: usize, puncture: fn(usize) -> (bool, bool), -) -> Option<[u8; 30]> { +) -> Option<([u8; 30], u8)> { let type3_bits = Bits::new(type3); let mut type3_iter = type3_bits.iter(); let mut table = [[0u8; 32]; 244]; @@ -266,7 +266,7 @@ pub(crate) fn decode( }; } } - Some(out) + Some((out, *best)) } } @@ -332,7 +332,7 @@ mod tests { let encoded = encode(&lsf, 240, p_1); assert_eq!(encoded, expected_encoded); let decoded = decode(&encoded, 240, p_1); - assert_eq!(decoded, Some(lsf)); + assert_eq!(decoded, Some((lsf, 0))); } #[test] @@ -352,7 +352,7 @@ mod tests { if idx == 100 { assert_eq!(decoded, None); // 7 bits is too much damage } else { - assert_eq!(decoded, Some(lsf)); // recovered from errors + assert!(matches!(decoded, Some((frame, _)) if frame == lsf)); // recovered from errors } } } diff --git a/m17core/src/modem.rs b/m17core/src/modem.rs index 255678f..e7f249f 100644 --- a/m17core/src/modem.rs +++ b/m17core/src/modem.rs @@ -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 => { diff --git a/tools/m17rt-fastdemod/Cargo.toml b/tools/m17rt-fastdemod/Cargo.toml new file mode 100644 index 0000000..18f08a1 --- /dev/null +++ b/tools/m17rt-fastdemod/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "m17rt-fastdemod" +version = "0.1.0" +edition = "2024" +license = "MIT" +authors = ["Thomas Karpiniec Result<(), Box> { + env_logger::init(); + let args = Args::parse(); + + let mut file = File::open(&args.input)?; + let mut baseband = vec![]; + file.read_to_end(&mut baseband)?; + + let mut total = 0; + let mut demod = SoftDemodulator::new(); + for (idx, sample) in baseband + .chunks(2) + .map(|pair| i16::from_le_bytes([pair[0], pair[1]])) + .enumerate() + { + if let Some((frame, errors)) = demod.demod(sample) { + total += 1; + let frame_desc = match frame { + Frame::Lsf(_) => "lsf", + Frame::Stream(_) => "stream", + Frame::Packet(_) => "packet", + }; + println!("sample {}: {} with {} errors", idx, frame_desc, errors); + } + } + + println!("\ntotal successful decodes: {}", total); + + Ok(()) +}