]> code.octet-stream.net Git - m17rt/blob - m17app/src/rtlsdr.rs
7829f96220357a644fb6934ec4cd00104753047c
[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, SoundmodemError},
9 soundmodem::{InputSource, 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>) -> Result<(), SoundmodemError> {
30 let mut cmd = 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 let mut stdout = cmd.stdout.take().unwrap();
44 let mut buf = [0u8; 1024];
45 let mut leftover: Option<u8> = None;
46 std::thread::spawn(move || {
47 while let Ok(n) = stdout.read(&mut buf) {
48 let mut start_idx = 0;
49 let mut samples = vec![];
50 if let Some(left) = leftover {
51 if n > 0 {
52 samples.push(i16::from_le_bytes([left, buf[0]]));
53 start_idx = 1;
54 leftover = None;
55 }
56 }
57 for sample in buf[start_idx..n].chunks(2) {
58 if sample.len() == 2 {
59 samples.push(i16::from_le_bytes([sample[0], sample[1]]))
60 } else {
61 leftover = Some(sample[0]);
62 }
63 }
64 if tx
65 .send(SoundmodemEvent::BasebandInput(samples.into()))
66 .is_err()
67 {
68 break;
69 }
70 }
71 });
72 *self.rtlfm.lock().unwrap() = Some(cmd);
73 Ok(())
74 }
75
76 fn close(&self) -> Result<(), SoundmodemError> {
77 if let Some(mut process) = self.rtlfm.lock().unwrap().take() {
78 let _ = process.kill();
79 }
80 Ok(())
81 }
82 }