Skip to main content

rtl_sdr_rs/
lib.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! # rtlsdr Library
6//! Library for interfacing with an RTL-SDR device.
7
8mod device;
9pub mod error;
10mod rtlsdr;
11mod tuners;
12
13use device::Device;
14use error::{Result, RtlsdrError};
15use rtlsdr::RtlSdr as Sdr;
16use rusb::{Context, DeviceHandle, DeviceList, UsbContext};
17use tuners::r82xx::{R820T_TUNER_ID, R828D_TUNER_ID};
18
19pub struct TunerId;
20impl TunerId {
21    pub const R820T: &'static str = R820T_TUNER_ID;
22    pub const R828D: &'static str = R828D_TUNER_ID;
23}
24
25pub const DEFAULT_BUF_LENGTH: usize = 16 * 16384;
26
27pub struct DeviceDescriptors {
28    list: DeviceList<Context>,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct DeviceDescriptor {
33    pub index: usize,
34    pub vendor_id: u16,
35    pub product_id: u16,
36    pub manufacturer: String,
37    pub product: String,
38    pub serial: String,
39}
40
41impl DeviceDescriptors {
42    pub fn new() -> Result<Self> {
43        let context = Context::new()?;
44        let list = context.devices()?;
45        Ok(Self { list })
46    }
47
48    /// Returns an iterator over the found RTL-SDR devices.
49    pub fn iter(&self) -> impl Iterator<Item = DeviceDescriptor> + '_ {
50        self.list
51            .iter()
52            .filter_map(|device| {
53                let desc = device.device_descriptor().ok()?;
54                device::is_known_device(desc.vendor_id(), desc.product_id()).then_some(device)
55            })
56            .enumerate()
57            .filter_map(|(index, device)| {
58                let desc = device.device_descriptor().ok()?;
59                match device.open() {
60                    Ok(handle) => {
61                        let manufacturer = read_string(&handle, desc.manufacturer_string_index());
62                        let product = read_string(&handle, desc.product_string_index());
63                        let serial = read_string(&handle, desc.serial_number_string_index());
64
65                        Some(DeviceDescriptor {
66                            index,
67                            vendor_id: desc.vendor_id(),
68                            product_id: desc.product_id(),
69                            manufacturer,
70                            product,
71                            serial,
72                        })
73                    }
74                    Err(e) => {
75                        log::warn!("Could not open device at index {}: {}", index, e);
76                        None
77                    }
78                }
79            })
80    }
81}
82
83fn read_string<T: UsbContext>(handle: &DeviceHandle<T>, index: Option<u8>) -> String {
84    index
85        .and_then(|i| handle.read_string_descriptor_ascii(i).ok())
86        .unwrap_or_default()
87}
88
89#[derive(Debug, PartialEq, Eq, Clone)]
90pub enum DeviceId<'a> {
91    Index(usize),
92    Serial(&'a str),
93    Fd(i32),
94}
95
96#[derive(Debug)]
97pub enum TunerGain {
98    Auto,
99    Manual(i32),
100}
101#[derive(Debug)]
102pub enum DirectSampleMode {
103    Off,
104    On,
105    OnSwap, // Swap I and Q ADC, allowing to select between two inputs
106}
107
108#[derive(Debug, Copy, Clone, PartialEq, Eq)]
109pub enum Sensor {
110    TunerType,
111    TunerGainDb,
112    FrequencyCorrectionPpm,
113}
114
115#[derive(Debug, PartialEq)]
116pub enum SensorValue {
117    TunerType(String),
118    TunerGainDb(i32),
119    FrequencyCorrectionPpm(i32),
120}
121
122pub struct RtlSdr {
123    sdr: Sdr,
124}
125impl RtlSdr {
126    pub fn open(device_id: DeviceId) -> Result<RtlSdr> {
127        let dev = Device::new(device_id)?;
128        let mut sdr = Sdr::new(dev);
129        sdr.init()?;
130        Ok(RtlSdr { sdr })
131    }
132
133    pub fn open_with_serial(serial: &str) -> Result<RtlSdr> {
134        Self::open(DeviceId::Serial(serial))
135    }
136
137    /// Convenience function to open device by index (backward compatibility)
138    pub fn open_with_index(index: usize) -> Result<RtlSdr> {
139        Self::open(DeviceId::Index(index))
140    }
141
142    /// Convenience function to open device by file descriptor  
143    pub fn open_with_fd(fd: i32) -> Result<RtlSdr> {
144        Self::open(DeviceId::Fd(fd))
145    }
146    pub fn close(&mut self) -> Result<()> {
147        // TODO: wait until async is inactive
148        self.sdr.deinit_baseband()
149    }
150    pub fn reset_buffer(&self) -> Result<()> {
151        self.sdr.reset_buffer()
152    }
153    pub fn read_sync(&self, buf: &mut [u8]) -> Result<usize> {
154        self.sdr.read_sync(buf)
155    }
156    pub fn get_center_freq(&self) -> u32 {
157        self.sdr.get_center_freq()
158    }
159    pub fn set_center_freq(&mut self, freq: u32) -> Result<()> {
160        self.sdr.set_center_freq(freq)
161    }
162    pub fn get_tuner_gains(&self) -> Result<Vec<i32>> {
163        self.sdr.get_tuner_gains()
164    }
165    pub fn read_tuner_gain(&self) -> Result<i32> {
166        self.sdr.read_tuner_gain()
167    }
168    pub fn set_tuner_gain(&mut self, gain: TunerGain) -> Result<()> {
169        self.sdr.set_tuner_gain(gain)
170    }
171    pub fn get_freq_correction(&self) -> i32 {
172        self.sdr.get_freq_correction()
173    }
174    pub fn set_freq_correction(&mut self, ppm: i32) -> Result<()> {
175        self.sdr.set_freq_correction(ppm)
176    }
177    pub fn get_sample_rate(&self) -> u32 {
178        self.sdr.get_sample_rate()
179    }
180    pub fn set_sample_rate(&mut self, rate: u32) -> Result<()> {
181        self.sdr.set_sample_rate(rate)
182    }
183    pub fn set_tuner_bandwidth(&mut self, bw: u32) -> Result<()> {
184        self.sdr.set_tuner_bandwidth(bw)
185    }
186    pub fn set_testmode(&mut self, on: bool) -> Result<()> {
187        self.sdr.set_testmode(on)
188    }
189    pub fn set_direct_sampling(&mut self, mode: DirectSampleMode) -> Result<()> {
190        self.sdr.set_direct_sampling(mode)
191    }
192    pub fn set_bias_tee(&self, on: bool) -> Result<()> {
193        self.sdr.set_bias_tee(on)
194    }
195    pub fn get_tuner_id(&self) -> Result<&str> {
196        self.sdr.get_tuner_id()
197    }
198    pub fn list_sensors(&self) -> Result<Vec<Sensor>> {
199        Ok(vec![
200            Sensor::TunerType,
201            Sensor::TunerGainDb,
202            Sensor::FrequencyCorrectionPpm,
203        ])
204    }
205    pub fn read_sensor(&self, sensor: Sensor) -> Result<SensorValue> {
206        match sensor {
207            Sensor::TunerType => self
208                .get_tuner_id()
209                .map(|s| SensorValue::TunerType(s.to_string())),
210            Sensor::TunerGainDb => self.sdr.read_tuner_gain().map(SensorValue::TunerGainDb),
211            Sensor::FrequencyCorrectionPpm => Ok(SensorValue::FrequencyCorrectionPpm(
212                self.get_freq_correction(),
213            )),
214        }
215    }
216
217    /// Get the number of available RTL-SDR devices
218    pub fn get_device_count() -> Result<usize> {
219        let descriptors = DeviceDescriptors::new()?;
220        Ok(descriptors.iter().count())
221    }
222
223    /// List all available RTL-SDR devices
224    pub fn list_devices() -> Result<Vec<DeviceDescriptor>> {
225        let descriptors = DeviceDescriptors::new()?;
226        Ok(descriptors.iter().collect())
227    }
228
229    /// Open the first available RTL-SDR device
230    pub fn open_first_available() -> Result<RtlSdr> {
231        let descriptors = DeviceDescriptors::new()?;
232        let first_device = descriptors
233            .iter()
234            .next()
235            .ok_or_else(|| RtlsdrError::RtlsdrErr("No RTL-SDR devices found".to_string()))?;
236        Self::open_with_index(first_device.index)
237    }
238
239    /// Get device information for a specific device by index
240    pub fn get_device_info(index: usize) -> Result<DeviceDescriptor> {
241        let descriptors = DeviceDescriptors::new()?;
242        let devices: Vec<DeviceDescriptor> = descriptors.iter().collect();
243        devices
244            .into_iter()
245            .find(|d| d.index == index)
246            .ok_or_else(|| {
247                RtlsdrError::RtlsdrErr(format!("No device found at index {}", index))
248            })
249    }
250
251    /// Get the serial number for a specific device by index
252    pub fn get_device_serial(index: usize) -> Result<String> {
253        Self::get_device_info(index).map(|info| info.serial)
254    }
255}