From: Thomas Karpiniec Date: Tue, 28 Jan 2025 16:05:58 +0000 (+1100) Subject: RTLSDR input via rtl_fm X-Git-Tag: v0.1.0~1 X-Git-Url: https://code.octet-stream.net/m17rt/commitdiff_plain/608ca7e33ab51d812607ddcc3429bfa9aa3c34b0 RTLSDR input via rtl_fm --- diff --git a/m17app/src/error.rs b/m17app/src/error.rs index ee624f9..36d5ac0 100644 --- a/m17app/src/error.rs +++ b/m17app/src/error.rs @@ -13,4 +13,7 @@ pub enum M17Error { #[error("unable to locate sound card '{0}' - is it in use?")] SoundcardNotFound(String), + + #[error("unable to set up RTL-SDR receiver")] + RtlSdrInit, } diff --git a/m17app/src/lib.rs b/m17app/src/lib.rs index 543bdc5..06a6cfa 100755 --- a/m17app/src/lib.rs +++ b/m17app/src/lib.rs @@ -2,6 +2,7 @@ pub mod adapter; pub mod app; pub mod error; pub mod link_setup; +pub mod rtlsdr; pub mod serial; pub mod soundcard; pub mod soundmodem; diff --git a/m17app/src/rtlsdr.rs b/m17app/src/rtlsdr.rs new file mode 100644 index 0000000..33f8070 --- /dev/null +++ b/m17app/src/rtlsdr.rs @@ -0,0 +1,90 @@ +use std::{ + io::Read, + process::{Child, Command, Stdio}, + sync::{ + mpsc::{sync_channel, Receiver, SyncSender}, + Arc, Mutex, RwLock, + }, + time::{Duration, Instant}, +}; + +use cpal::{ + traits::{DeviceTrait, HostTrait, StreamTrait}, + SampleFormat, SampleRate, Stream, +}; + +use crate::{ + error::M17Error, + soundmodem::{InputSource, OutputBuffer, OutputSink, SoundmodemEvent}, +}; + +pub struct RtlSdr { + frequency_mhz: f32, + device_index: usize, + rtlfm: Mutex>, +} + +impl RtlSdr { + pub fn new(device_index: usize, frequency_mhz: f32) -> Result { + Ok(Self { + device_index, + frequency_mhz, + rtlfm: Mutex::new(None), + }) + } +} + +impl InputSource for RtlSdr { + fn start(&self, tx: SyncSender) { + // TODO: error handling + let mut cmd = Command::new("rtl_fm") + .args([ + "-E", + "offset", + "-f", + &format!("{:.6}M", self.frequency_mhz), + "-d", + &self.device_index.to_string(), + "-s", + "48k", + ]) + .stdout(Stdio::piped()) + .spawn() + .unwrap(); + let mut stdout = cmd.stdout.take().unwrap(); + let mut buf = [0u8; 1024]; + let mut leftover: Option = None; + std::thread::spawn(move || { + while let Ok(n) = stdout.read(&mut buf) { + let mut start_idx = 0; + let mut samples = vec![]; + if let Some(left) = leftover { + if n > 0 { + samples.push(i16::from_le_bytes([left, buf[0]])); + start_idx = 1; + leftover = None; + } + } + for sample in buf[start_idx..n].chunks(2) { + if sample.len() == 2 { + samples.push(i16::from_le_bytes([sample[0], sample[1]])) + } else { + leftover = Some(sample[0]); + } + } + if tx + .send(SoundmodemEvent::BasebandInput(samples.into())) + .is_err() + { + break; + } + } + }); + } + + fn close(&self) { + if let Some(mut process) = self.rtlfm.lock().unwrap().take() { + let _ = process.kill(); + } + } +} diff --git a/tools/m17rt-mod/src/main.rs b/tools/m17rt-mod/src/main.rs index 50358bd..3bd6727 100644 --- a/tools/m17rt-mod/src/main.rs +++ b/tools/m17rt-mod/src/main.rs @@ -8,6 +8,7 @@ use std::path::PathBuf; pub fn mod_test() { let soundcard = Soundcard::new("plughw:CARD=Device,DEV=0").unwrap(); + soundcard.set_tx_inverted(true); let ptt = SerialPtt::new("/dev/ttyUSB0", PttPin::Rts); let soundmodem = Soundmodem::new(soundcard.input(), soundcard.output(), ptt); let app = M17App::new(soundmodem);