]> code.octet-stream.net Git - m17rt/blob - m17app/src/rtlsdr.rs
Error handler for soundmodem components
[m17rt] / m17app / src / rtlsdr.rs
1 use std::{
2 io::Read,
3 process::{Child, Command, Stdio},
4 sync::{mpsc::SyncSender, Mutex},
5 };
6
7 use crate::{
8 error::M17Error,
9 soundmodem::{InputSource, SoundmodemErrorSender, SoundmodemEvent},
10 };
11
12 pub struct RtlSdr {
13 frequency_mhz: f32,
14 device_index: usize,
15 rtlfm: Mutex<Option<Child>>,
16 }
17
18 impl RtlSdr {
19 pub fn new(device_index: usize, frequency_mhz: f32) -> Result<Self, M17Error> {
20 Ok(Self {
21 device_index,
22 frequency_mhz,
23 rtlfm: Mutex::new(None),
24 })
25 }
26 }
27
28 impl InputSource for RtlSdr {
29 fn start(&self, tx: SyncSender<SoundmodemEvent>, errors: SoundmodemErrorSender) {
30 let mut cmd = match Command::new("rtl_fm")
31 .args([
32 "-E",
33 "offset",
34 "-f",
35 &format!("{:.6}M", self.frequency_mhz),
36 "-d",
37 &self.device_index.to_string(),
38 "-s",
39 "48k",
40 ])
41 .stdout(Stdio::piped())
42 .spawn()
43 {
44 Ok(c) => c,
45 Err(e) => {
46 errors.send_error(e);
47 return;
48 }
49 };
50 let mut stdout = cmd.stdout.take().unwrap();
51 let mut buf = [0u8; 1024];
52 let mut leftover: Option<u8> = None;
53 std::thread::spawn(move || {
54 while let Ok(n) = stdout.read(&mut buf) {
55 let mut start_idx = 0;
56 let mut samples = vec![];
57 if let Some(left) = leftover {
58 if n > 0 {
59 samples.push(i16::from_le_bytes([left, buf[0]]));
60 start_idx = 1;
61 leftover = None;
62 }
63 }
64 for sample in buf[start_idx..n].chunks(2) {
65 if sample.len() == 2 {
66 samples.push(i16::from_le_bytes([sample[0], sample[1]]))
67 } else {
68 leftover = Some(sample[0]);
69 }
70 }
71 if tx
72 .send(SoundmodemEvent::BasebandInput(samples.into()))
73 .is_err()
74 {
75 break;
76 }
77 }
78 });
79 *self.rtlfm.lock().unwrap() = Some(cmd);
80 }
81
82 fn close(&self) {
83 if let Some(mut process) = self.rtlfm.lock().unwrap().take() {
84 let _ = process.kill();
85 }
86 }
87 }