diff --git a/config.example.toml b/config.example.toml index 76ce98d821857eaaf68568e60293ccabcbeaf66f..4b1e964c480dfd761de57729da313f9897525a21 100644 --- a/config.example.toml +++ b/config.example.toml @@ -1,3 +1,18 @@ +# rtl_fm parameters +frequency = 430_500_000 # center frequency, Hz +device_id = 0 # ID of your RTL-SDR; 0 is default + +# if set to true: +# - expects 48k samples/second, 16-bit LE IQ. +# - will not start rtl_fm process +# - will ignore above settings +# probably don't want to touch this unless you're bypassing rtl_fm for some reason +use_stdin = false + +# max FSK deviation, Hz +# if you don't know what this means, leave it as-is +max_deviation = 15_000 + [felinet] address = "https://felinet.cats.radio" callsign = "CHANGEME" diff --git a/src/config.rs b/src/config.rs index af64d71550f0955cb5688a88c061183f49123721..15d9372f32d44780689b7e3647fe76d81ad28f21 100644 --- a/src/config.rs +++ b/src/config.rs @@ -30,6 +30,12 @@ pub struct FelinetConfig { #[derive(Deserialize, Clone)] pub struct Config { + pub frequency: u32, + #[serde(default)] + pub device_id: u32, + #[serde(default)] + pub use_stdin: bool, + pub max_deviation: i32, pub felinet: Option<FelinetConfig>, } diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 97554aef8158afb16a86c92da0c62a7af568fac3..57ebe6c58a09bfcdaaf444fc20e21d35685ba99f 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -6,7 +6,7 @@ use uuid::Uuid; use crate::{ codec2::{Complex, Fsk}, - config::FelinetConfig, + config::Config, decoder::rtl::RtlSdr, felinet::PacketIn as SemiPacketIn, util::{append_internet_to_packet_route, print_packet}, @@ -19,11 +19,7 @@ mod demod; mod packet; mod rtl; -pub fn decode_forever( - felinet_send: mpsc::Sender<SemiPacketIn>, - felinet_config: &Option<FelinetConfig>, - uuid: &Uuid, -) { +pub fn decode_forever(felinet_send: mpsc::Sender<SemiPacketIn>, config: &Config, uuid: &Uuid) { const USE_STDIN: bool = false; let bytes: Box<dyn Iterator<Item = u8>> = if USE_STDIN { @@ -31,7 +27,7 @@ pub fn decode_forever( Box::new(stdin.bytes().map(|x| x.expect("Could not read from stdin"))) } else { - let rtl = RtlSdr::new().unwrap(); + let rtl = RtlSdr::new(config.frequency, config.device_id).unwrap(); Box::new(rtl) }; @@ -51,7 +47,7 @@ pub fn decode_forever( demod.demod(|mut p| { print_packet(&p); - if let Some(fc) = felinet_config { + if let Some(fc) = &config.felinet { if append_internet_to_packet_route(&fc.callsign, fc.ssid, &mut p).is_none() { return; }; diff --git a/src/decoder/rtl.rs b/src/decoder/rtl.rs index 563d3b5bc673aa7f35a2949b2b62ef24345224fd..2285b05c8d229e51f27afd4f6deaa0d17b37f7e9 100644 --- a/src/decoder/rtl.rs +++ b/src/decoder/rtl.rs @@ -4,26 +4,19 @@ use anyhow::Context; use subprocess::{Communicator, Popen, PopenConfig, Redirection}; pub struct RtlSdr { + process: Popen, comms: Communicator, out_buf: VecDeque<u8>, err_buf: Vec<u8>, } impl RtlSdr { - pub fn new() -> anyhow::Result<Self> { - let mut p = Popen::create( + pub fn new(freq: u32, device_id: u32) -> anyhow::Result<Self> { + let freq = format!("{freq}"); + let device_id = format!("{device_id}"); + let mut process = Popen::create( &[ - "rtl_fm", - "-f", - "430500000", - "-M", - "raw", - "-F9", - "-p", - "0", - "-d", - "0", - "-s", + "rtl_fm", "-f", &freq, "-M", "raw", "-F9", "-p", "0", "-d", &device_id, "-s", "48000", ], PopenConfig { @@ -35,9 +28,10 @@ impl RtlSdr { ) .context("Error starting rtl-fm")?; - let comms = p.communicate_start(None).limit_size(1024); + let comms = process.communicate_start(None).limit_size(1024); Ok(Self { + process, comms, out_buf: VecDeque::new(), err_buf: vec![], @@ -49,28 +43,34 @@ impl Iterator for RtlSdr { type Item = u8; fn next(&mut self) -> Option<Self::Item> { - if let Some(b) = self.out_buf.pop_front() { - return Some(b); - } + while self.out_buf.is_empty() { + let (out, err) = self.comms.read().ok()?; + let out = out.expect("Missing stdout"); + let err = err.expect("Missing stderr"); - let (out, err) = self.comms.read().ok()?; - let out = out.expect("Missing stdout"); - let err = err.expect("Missing stderr"); + self.out_buf.extend(out.iter()); + for e in err { + if e == b'\n' { + handle_err(&self.err_buf); + self.err_buf.clear(); + } else { + self.err_buf.push(e); + } + } - self.out_buf.extend(out.iter()); - for e in err { - if e == b'\n' { + if self.process.poll().is_some() { handle_err(&self.err_buf); self.err_buf.clear(); - } else { - self.err_buf.push(e); + return None; } } - self.out_buf.pop_front() + Some(self.out_buf.pop_front().unwrap()) } } fn handle_err(err: &[u8]) { - eprintln!("[rtl_fm] {}", String::from_utf8_lossy(err)); + if !err.is_empty() { + eprintln!("[rtl_fm] {}", String::from_utf8_lossy(err)); + } } diff --git a/src/main.rs b/src/main.rs index 8d8a75489fb3327f51211a9be72c87996343ba9a..c54d67114adf3583b4536c7c2fe9efef95929d19 100644 --- a/src/main.rs +++ b/src/main.rs @@ -72,7 +72,7 @@ async fn gate_forever(config: Config) -> anyhow::Result<()> { } tokio::task::spawn_blocking(move || { - decoder::decode_forever(tx, &config.felinet, &uuid); + decoder::decode_forever(tx, &config, &uuid); }) .await?;