1#![doc = include_str!("../readme.md")]
2
3use std::{
4 pin::Pin,
5 task::{Context, Poll},
6};
7
8use futures::Stream;
9use num_complex::Complex;
10
11pub mod iqread;
12#[cfg(feature = "rtlsdr")]
13pub mod rtlsdr;
14
15#[derive(Debug, Copy, Clone)]
19pub enum IqFormat {
20 Cu8,
22 Cs8,
24 Cs16,
26 Cf32,
28}
29
30pub enum IqSource {
34 IqFile(iqread::IqRead<std::io::BufReader<std::fs::File>>),
36 #[cfg(feature = "rtlsdr")]
37 RtlSdr(rtlsdr::RtlSdrReader),
39}
40
41impl Iterator for IqSource {
42 type Item = Result<Vec<Complex<f32>>, std::io::Error>;
43
44 fn next(&mut self) -> Option<Self::Item> {
45 match self {
46 IqSource::IqFile(source) => source.next(),
47 #[cfg(feature = "rtlsdr")]
48 IqSource::RtlSdr(_source) => todo!(), }
50 }
51}
52impl IqSource {
53 pub fn from_file<P: AsRef<std::path::Path>>(
55 path: P,
56 center_freq: u32,
57 sample_rate: u32,
58 chunk_size: usize,
59 iq_format: IqFormat,
60 ) -> Result<Self, std::io::Error> {
61 let source =
62 iqread::IqRead::from_file(path, center_freq, sample_rate, chunk_size, iq_format)?;
63 Ok(IqSource::IqFile(source))
64 }
65
66 #[cfg(feature = "rtlsdr")]
67 pub fn from_rtlsdr(
69 device_index: usize,
70 center_freq: u32,
71 sample_rate: u32,
72 gain: Option<i32>,
73 ) -> Result<Self, rtl_sdr_rs::error::RtlsdrError> {
74 let config = rtlsdr::RtlSdrConfig {
75 device_index,
76 center_freq,
77 sample_rate,
78 gain,
79 };
80 let source = rtlsdr::RtlSdrReader::new(&config)?;
81 Ok(IqSource::RtlSdr(source))
82 }
83}
84
85pub enum IqAsyncSource {
89 IqAsyncFile(iqread::IqAsyncRead<tokio::io::BufReader<tokio::fs::File>>),
91 IqAsyncStdin(iqread::IqAsyncRead<tokio::io::BufReader<tokio::io::Stdin>>),
93 IqAsyncTcp(iqread::IqAsyncRead<tokio::io::BufReader<tokio::net::TcpStream>>),
95 #[cfg(feature = "rtlsdr")]
96 RtlSdr(rtlsdr::AsyncRtlSdrReader),
98}
99
100impl IqAsyncSource {
101 pub async fn from_file<P: AsRef<std::path::Path>>(
103 path: P,
104 center_freq: u32,
105 sample_rate: u32,
106 chunk_size: usize,
107 iq_format: IqFormat,
108 ) -> Result<Self, std::io::Error> {
109 let source =
110 iqread::IqAsyncRead::from_file(path, center_freq, sample_rate, chunk_size, iq_format)
111 .await?;
112 Ok(IqAsyncSource::IqAsyncFile(source))
113 }
114
115 pub fn from_stdin(
117 center_freq: u32,
118 sample_rate: u32,
119 chunk_size: usize,
120 iq_format: IqFormat,
121 ) -> Self {
122 let source =
123 iqread::IqAsyncRead::from_stdin(center_freq, sample_rate, chunk_size, iq_format);
124 IqAsyncSource::IqAsyncStdin(source)
125 }
126
127 pub async fn from_tcp(
129 addr: &str,
130 port: u16,
131 center_freq: u32,
132 sample_rate: u32,
133 chunk_size: usize,
134 iq_format: IqFormat,
135 ) -> Result<Self, std::io::Error> {
136 let source = iqread::IqAsyncRead::from_tcp(
137 addr,
138 port,
139 center_freq,
140 sample_rate,
141 chunk_size,
142 iq_format,
143 )
144 .await?;
145 Ok(IqAsyncSource::IqAsyncTcp(source))
146 }
147
148 #[cfg(feature = "rtlsdr")]
149 pub async fn from_rtlsdr(
151 device_index: usize,
152 center_freq: u32,
153 sample_rate: u32,
154 gain: Option<i32>,
155 ) -> Result<Self, rtl_sdr_rs::error::RtlsdrError> {
156 let config = rtlsdr::RtlSdrConfig {
157 device_index,
158 center_freq,
159 sample_rate,
160 gain,
161 };
162 let async_reader = rtlsdr::AsyncRtlSdrReader::new(&config)?;
163 Ok(IqAsyncSource::RtlSdr(async_reader))
164 }
165}
166
167impl Stream for IqAsyncSource {
168 type Item = Result<Vec<Complex<f32>>, std::io::Error>;
169
170 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
171 match self.get_mut() {
172 IqAsyncSource::IqAsyncFile(source) => Pin::new(source).poll_next(cx),
173 IqAsyncSource::IqAsyncStdin(source) => Pin::new(source).poll_next(cx),
174 IqAsyncSource::IqAsyncTcp(source) => Pin::new(source).poll_next(cx),
175 #[cfg(feature = "rtlsdr")]
176 IqAsyncSource::RtlSdr(_source) => todo!(), }
178 }
179}
180
181fn convert_bytes_to_complex(format: IqFormat, buffer: &[u8]) -> Vec<Complex<f32>> {
182 match format {
183 IqFormat::Cu8 => buffer
184 .chunks_exact(2)
185 .map(|c| Complex::new((c[0] as f32 - 127.5) / 128.0, (c[1] as f32 - 127.5) / 128.0))
186 .collect(),
187 IqFormat::Cs8 => buffer
188 .chunks_exact(2)
189 .map(|c| Complex::new((c[0] as i8) as f32 / 128.0, (c[1] as i8) as f32 / 128.0))
190 .collect(),
191 IqFormat::Cs16 => buffer
192 .chunks_exact(4)
193 .map(|c| {
194 Complex::new(
195 i16::from_le_bytes([c[0], c[1]]) as f32 / 32768.0,
196 i16::from_le_bytes([c[2], c[3]]) as f32 / 32768.0,
197 )
198 })
199 .collect(),
200 IqFormat::Cf32 => buffer
201 .chunks_exact(8)
202 .map(|c| {
203 Complex::new(
204 f32::from_le_bytes([c[0], c[1], c[2], c[3]]),
205 f32::from_le_bytes([c[4], c[5], c[6], c[7]]),
206 )
207 })
208 .collect(),
209 }
210}