mod async_read;
mod device;
pub mod error;
mod rtlsdr;
mod tuners;
pub use async_read::{
AsyncReadConfigChange, AsyncReadControlHandle, AsyncReadEvent, AsyncReadHandle, CancelHandle,
};
use device::Device;
use error::{Result, RtlsdrError};
use nusb::MaybeFuture;
use rtlsdr::RtlSdr as Sdr;
use tuners::r82xx::{R820T_TUNER_ID, R828D_TUNER_ID};
pub struct TunerId;
impl TunerId {
pub const R820T: &'static str = R820T_TUNER_ID;
pub const R828D: &'static str = R828D_TUNER_ID;
}
pub const DEFAULT_BUF_LENGTH: usize = 16 * 16384;
pub const DEFAULT_ASYNC_BUF_NUMBER: usize = 15;
pub struct DeviceDescriptors {
list: Vec<nusb::DeviceInfo>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DeviceDescriptor {
pub index: usize,
pub vendor_id: u16,
pub product_id: u16,
pub manufacturer: String,
pub product: String,
pub serial: String,
}
impl DeviceDescriptors {
pub fn new() -> Result<Self> {
let list = nusb::list_devices()
.wait()
.map_err(RtlsdrError::from_usb)?
.collect();
Ok(Self { list })
}
pub fn iter(&self) -> impl Iterator<Item = DeviceDescriptor> + '_ {
self.list
.iter()
.filter(|device| device::is_known_device(device.vendor_id(), device.product_id()))
.enumerate()
.map(|(index, device)| {
let manufacturer = device.manufacturer_string().unwrap_or_default().to_string();
let product = device.product_string().unwrap_or_default().to_string();
let serial = device.serial_number().unwrap_or_default().to_string();
DeviceDescriptor {
index,
vendor_id: device.vendor_id(),
product_id: device.product_id(),
manufacturer,
product,
serial,
}
})
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum DeviceId<'a> {
Index(usize),
Serial(&'a str),
Fd(i32),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TunerGain {
Auto,
Manual(i32),
}
#[derive(Debug)]
pub enum DirectSampleMode {
Off,
On,
OnSwap, }
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Sensor {
TunerType,
TunerGainDb,
FrequencyCorrectionPpm,
}
#[derive(Debug, PartialEq)]
pub enum SensorValue {
TunerType(String),
TunerGainDb(i32),
FrequencyCorrectionPpm(i32),
}
pub struct RtlSdr {
sdr: Sdr,
closed: bool,
}
impl RtlSdr {
pub fn open(device_id: DeviceId) -> Result<RtlSdr> {
let dev = Device::new(device_id)?;
let mut sdr = Sdr::new(dev);
sdr.init()?;
Ok(RtlSdr { sdr, closed: false })
}
pub fn open_with_serial(serial: &str) -> Result<RtlSdr> {
Self::open(DeviceId::Serial(serial))
}
pub fn open_with_index(index: usize) -> Result<RtlSdr> {
Self::open(DeviceId::Index(index))
}
pub fn open_with_fd(fd: i32) -> Result<RtlSdr> {
Self::open(DeviceId::Fd(fd))
}
pub fn close(&mut self) -> Result<()> {
if self.closed {
return Ok(());
}
self.sdr.deinit_baseband()?;
self.closed = true;
Ok(())
}
pub fn reset_buffer(&self) -> Result<()> {
self.sdr.reset_buffer()
}
pub fn read_sync(&self, buf: &mut [u8]) -> Result<usize> {
self.sdr.read_sync(buf)
}
pub fn read_async<F>(
&self,
buf_num: usize,
buf_len: usize,
cancel: &CancelHandle,
callback: F,
) -> Result<()>
where
F: FnMut(&[u8]),
{
self.sdr.read_async(buf_num, buf_len, cancel, callback)
}
pub fn into_async_reader(self, buf_num: usize, buf_len: usize) -> Result<AsyncReadHandle> {
async_read::start_async_reader(self, buf_num, buf_len)
}
pub fn get_center_freq(&self) -> u32 {
self.sdr.get_center_freq()
}
pub fn set_center_freq(&mut self, freq: u32) -> Result<()> {
self.sdr.set_center_freq(freq)
}
pub fn get_tuner_gains(&self) -> Result<Vec<i32>> {
self.sdr.get_tuner_gains()
}
pub fn read_tuner_gain(&self) -> Result<i32> {
self.sdr.read_tuner_gain()
}
pub fn set_tuner_gain(&mut self, gain: TunerGain) -> Result<()> {
self.sdr.set_tuner_gain(gain)
}
pub fn get_freq_correction(&self) -> i32 {
self.sdr.get_freq_correction()
}
pub fn set_freq_correction(&mut self, ppm: i32) -> Result<()> {
self.sdr.set_freq_correction(ppm)
}
pub fn get_sample_rate(&self) -> u32 {
self.sdr.get_sample_rate()
}
pub fn set_sample_rate(&mut self, rate: u32) -> Result<()> {
self.sdr.set_sample_rate(rate)
}
pub fn set_tuner_bandwidth(&mut self, bw: u32) -> Result<()> {
self.sdr.set_tuner_bandwidth(bw)
}
pub fn set_testmode(&mut self, on: bool) -> Result<()> {
self.sdr.set_testmode(on)
}
pub fn set_direct_sampling(&mut self, mode: DirectSampleMode) -> Result<()> {
self.sdr.set_direct_sampling(mode)
}
pub fn set_bias_tee(&self, on: bool) -> Result<()> {
self.sdr.set_bias_tee(on)
}
pub fn get_tuner_id(&self) -> Result<&str> {
self.sdr.get_tuner_id()
}
pub fn list_sensors(&self) -> Result<Vec<Sensor>> {
Ok(vec![
Sensor::TunerType,
Sensor::TunerGainDb,
Sensor::FrequencyCorrectionPpm,
])
}
pub fn read_sensor(&self, sensor: Sensor) -> Result<SensorValue> {
match sensor {
Sensor::TunerType => self
.get_tuner_id()
.map(|s| SensorValue::TunerType(s.to_string())),
Sensor::TunerGainDb => self.sdr.read_tuner_gain().map(SensorValue::TunerGainDb),
Sensor::FrequencyCorrectionPpm => Ok(SensorValue::FrequencyCorrectionPpm(
self.get_freq_correction(),
)),
}
}
pub fn get_device_count() -> Result<usize> {
let descriptors = DeviceDescriptors::new()?;
Ok(descriptors.iter().count())
}
pub fn list_devices() -> Result<Vec<DeviceDescriptor>> {
let descriptors = DeviceDescriptors::new()?;
Ok(descriptors.iter().collect())
}
pub fn open_first_available() -> Result<RtlSdr> {
let descriptors = DeviceDescriptors::new()?;
let first_device = descriptors
.iter()
.next()
.ok_or_else(|| RtlsdrError::RtlsdrErr("No RTL-SDR devices found".to_string()))?;
Self::open_with_index(first_device.index)
}
pub fn get_device_info(index: usize) -> Result<DeviceDescriptor> {
let descriptors = DeviceDescriptors::new()?;
let devices: Vec<DeviceDescriptor> = descriptors.iter().collect();
devices
.into_iter()
.find(|d| d.index == index)
.ok_or_else(|| RtlsdrError::RtlsdrErr(format!("No device found at index {}", index)))
}
pub fn get_device_serial(index: usize) -> Result<String> {
Self::get_device_info(index).map(|info| info.serial)
}
}
impl Drop for RtlSdr {
fn drop(&mut self) {
if let Err(e) = self.close() {
log::error!("Failed to power down on close: {e}");
}
}
}
#[cfg(test)]
mod public_api_tests {
use super::{CancelHandle, Result, RtlSdr};
#[allow(dead_code)]
fn read_async_accepts_a_borrowed_callback(
sdr: &RtlSdr,
cancel: &CancelHandle,
byte_count: &mut usize,
) -> Result<()> {
sdr.read_async(1, 512, cancel, |buf| *byte_count += buf.len())
}
}