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