rtlsdr/
lib.rs

1mod sys;
2
3macro_rules! rtlsdr_result {
4    ($ret:expr) => {
5        unsafe {
6            if $ret < 0 {
7                Err($ret)
8            } else {
9                Ok($ret)
10            }
11        }
12    };
13}
14
15#[derive(Debug, Copy, Clone, PartialEq, Eq)]
16pub enum RtlsdrError {
17    LibusbError(LibusbError),
18    Unspecified(i32),
19}
20
21impl From<i32> for RtlsdrError {
22    fn from(err: i32) -> Self {
23        match LibusbError::try_from(err) {
24            Ok(e) => RtlsdrError::LibusbError(e),
25            Err(e) => RtlsdrError::Unspecified(e),
26        }
27    }
28}
29
30#[repr(i32)]
31#[derive(Debug, Copy, Clone, PartialEq, Eq)]
32pub enum LibusbError {
33    /// Input/output error
34    IoError = -1,
35    /// Invalid parameter
36    InvalidParam = -2,
37    /// Access denied (insufficient permissions)
38    AccessDenied = -3,
39    /// No such device (it may have been disconnected)
40    NoDevice = -4,
41    /// Entity not found
42    NoEntity = -5,
43    /// Resource busy
44    Busy = -6,
45    /// Operation timed out
46    Timeout = -7,
47    /// Overflow
48    Overflow = -8,
49    /// Pipe error
50    Pipe = -9,
51    /// System call interrupted (perhaps due to signal)
52    Interrupted = -10,
53    /// Insufficient memory
54    InsufficientMemory = -11,
55    /// Operation not supported or unimplemented on this platform
56    NotSupported = -12,
57    /// Other error
58    Other = -99,
59}
60
61impl TryFrom<i32> for LibusbError {
62    type Error = i32;
63
64    fn try_from(value: i32) -> Result<Self, Self::Error> {
65        match value {
66            -1 => Ok(LibusbError::IoError),
67            -2 => Ok(LibusbError::InvalidParam),
68            -3 => Ok(LibusbError::AccessDenied),
69            -4 => Ok(LibusbError::NoDevice),
70            -5 => Ok(LibusbError::NoEntity),
71            -6 => Ok(LibusbError::Busy),
72            -7 => Ok(LibusbError::Timeout),
73            -8 => Ok(LibusbError::Overflow),
74            -9 => Ok(LibusbError::Pipe),
75            -10 => Ok(LibusbError::Interrupted),
76            -11 => Ok(LibusbError::InsufficientMemory),
77            -12 => Ok(LibusbError::NotSupported),
78            -99 => Ok(LibusbError::Other),
79            e => Err(e),
80        }
81    }
82}
83
84#[repr(i32)]
85#[derive(Debug, Copy, Clone, PartialEq, Eq)]
86pub enum TunerType {
87    /// Unknown tuner type
88    Unknown = 0,
89    /// Elonics E4000 tuner
90    E4000 = 1,
91    /// FC0012 tuner
92    FC0012 = 2,
93    /// FC0013 tuner
94    FC0013 = 3,
95    /// FC2580 tuner
96    FC2580 = 4,
97    /// Realtek 820T tuner
98    R820T = 5,
99    /// Realtek 828D tuner
100    R828D = 6,
101}
102
103#[repr(i32)]
104#[derive(Debug, Copy, Clone, PartialEq, Eq)]
105pub enum Sideband {
106    Lower = 0,
107    Upper = 1,
108}
109
110#[repr(u32)]
111#[derive(Debug, Copy, Clone, PartialEq, Eq)]
112pub enum DirectSampling {
113    Disabled = 0,
114    I = 1,
115    Q = 2,
116}
117
118#[repr(u32)]
119#[derive(Debug, Copy, Clone, PartialEq, Eq)]
120pub enum DirectSamplingThreshold {
121    Disabled = 0,
122    I = 1,
123    Q = 2,
124    IBelow = 3,
125    QBelow = 4,
126}
127
128#[derive(Debug, Copy, Clone, PartialEq, Eq)]
129/// Device struct
130pub struct Device {
131    index: u32,
132    dev: *mut sys::rtlsdr_dev,
133}
134
135impl Device {
136    /// Open device
137    /// This may fail due to a libusb error or some other unspecified error
138    pub fn open(&mut self) -> Result<(), RtlsdrError> {
139        rtlsdr_result!(sys::rtlsdr_open(&mut self.dev, self.index))?;
140
141        Ok(())
142    }
143
144    // Close device
145    // This is called automatically when the Device is dropped
146    // This will return an error if the device is not open or already closed
147    pub fn close(&mut self) -> Result<(), String> {
148        rtlsdr_result!(sys::rtlsdr_close(self.dev)).map_err(|e| match e {
149            -1 => "Device was not opened or already closed".to_string(),
150            _ => format!("Failed to close device: {}", e),
151        })?;
152
153        self.dev = std::ptr::null_mut();
154
155        Ok(())
156    }
157
158    /// Get crystal oscillator frequencies used for the RTL2832 and the tuner IC
159    /// Usually both ICs use the same clock.
160    pub fn get_xtal_freq(&self) -> Result<(u32, u32), String> {
161        let mut rtl_freq = 0;
162        let mut tuner_freq = 0;
163
164        rtlsdr_result!(sys::rtlsdr_get_xtal_freq(
165            self.dev,
166            &mut rtl_freq,
167            &mut tuner_freq
168        ))
169        .map_err(|e| format!("Failed to get crystal frequency: {}", e))?;
170
171        Ok((rtl_freq, tuner_freq))
172    }
173
174    /// Set crystal oscillator frequencies used for the RTL2832 and the tuner IC.
175    /// Usually both ICs use the same clock.
176    /// Changing the clock may make sense if you are applying an external clock to the tuner
177    /// or to compensate the frequency (and samplerate) error caused by the original (cheap) crystal.
178    /// NOTE: Call this function only if you fully understand the implications.
179    pub fn set_xtal_freq(&mut self, rtl_freq: u32, tuner_freq: u32) -> Result<(), String> {
180        rtlsdr_result!(sys::rtlsdr_set_xtal_freq(self.dev, rtl_freq, tuner_freq))
181            .map_err(|e| format!("Failed to set crystal frequency: {}", e))?;
182
183        Ok(())
184    }
185
186    /// Get USB device strings.
187    /// @return (manufacturer, product, serial) strings
188    pub fn get_usb_device_strings(&self) -> Result<(String, String, String), String> {
189        let mut manufact = [0u8; 256];
190        let mut product = [0u8; 256];
191        let mut serial = [0u8; 256];
192
193        rtlsdr_result!(sys::rtlsdr_get_usb_strings(
194            self.dev,
195            manufact.as_mut_ptr() as *mut i8,
196            product.as_mut_ptr() as *mut i8,
197            serial.as_mut_ptr() as *mut i8
198        ))
199        .map_err(|e| format!("Failed to get usb device strings: {}", e))?;
200
201        let manufact = std::ffi::CStr::from_bytes_until_nul(&manufact)
202            .map_err(|e| format!("Failed to get usb device strings: {}", e))?
203            .to_str()
204            .expect("Failed to convert usb device string to str")
205            .to_string();
206        let product = std::ffi::CStr::from_bytes_until_nul(&product)
207            .map_err(|e| format!("Failed to get usb device strings: {}", e))?
208            .to_str()
209            .expect("Failed to convert usb device string to str")
210            .to_string();
211        let serial = std::ffi::CStr::from_bytes_until_nul(&serial)
212            .map_err(|e| format!("Failed to get usb device strings: {}", e))?
213            .to_str()
214            .expect("Failed to convert usb device string to str")
215            .to_owned();
216
217        Ok((manufact, product, serial))
218    }
219
220    /// Read the device EEPROM
221    pub fn read_eeprom(&self, offset: u8, len: u16) -> Result<Vec<u8>, String> {
222        let mut buf = vec![0u8; len as usize];
223
224        rtlsdr_result!(sys::rtlsdr_read_eeprom(
225            self.dev,
226            buf.as_mut_ptr(),
227            offset,
228            len
229        ))
230        .map_err(|e| match e {
231            -1 => "Invalid Device".to_string(),
232            -2 => "EEPROM size exceeded".to_string(),
233            -3 => "No EEPROM found".to_string(),
234            _ => format!("Failed to read EEPROM: {}", e),
235        })?;
236
237        Ok(buf)
238    }
239
240    /// Write the device EEPROM
241    pub fn write_eeprom(&mut self, offset: u8, buf: &mut [u8]) -> Result<(), String> {
242        rtlsdr_result!(sys::rtlsdr_write_eeprom(
243            self.dev,
244            buf.as_mut_ptr(),
245            offset,
246            buf.len() as u16
247        ))
248        .map_err(|e| match e {
249            -1 => "Invalid Device".to_string(),
250            -2 => "EEPROM size exceeded".to_string(),
251            -3 => "No EEPROM found".to_string(),
252            _ => format!("Failed to write EEPROM: {}", e),
253        })?;
254
255        Ok(())
256    }
257
258    /// Get actual frequency the device is tuned to in Hz
259    pub fn get_center_freq(&self) -> Result<u32, String> {
260        match unsafe { sys::rtlsdr_get_center_freq(self.dev) } {
261            0 => Err("Failed to get center frequency".to_string()),
262            freq => Ok(freq),
263        }
264    }
265
266    /// Set the frequency the device is tuned to in Hz
267    pub fn set_center_freq(&mut self, freq: u32) -> Result<(), String> {
268        rtlsdr_result!(sys::rtlsdr_set_center_freq(self.dev, freq))
269            .map_err(|e| format!("Failed to set center frequency: {}", e))?;
270
271        Ok(())
272    }
273
274    /// Get actual frequency correction value of the device.
275    /// @return correction value in parts per million (ppm)
276    pub fn get_freq_correction(&self) -> i32 {
277        unsafe { sys::rtlsdr_get_freq_correction(self.dev) }
278    }
279
280    /// Set frequency correction value for the device.
281    /// @param ppm correction value in parts per million (ppm)
282    pub fn set_freq_correction(&mut self, ppm: i32) -> Result<(), String> {
283        rtlsdr_result!(sys::rtlsdr_set_freq_correction(self.dev, ppm))
284            .map_err(|e| format!("Failed to set frequency correction: {}", e))?;
285
286        Ok(())
287    }
288
289    /// Get the tuner type
290    pub fn get_tuner_type(&self) -> TunerType {
291        let tuner_type = unsafe { sys::rtlsdr_get_tuner_type(self.dev) };
292
293        match tuner_type {
294            0 => TunerType::Unknown,
295            1 => TunerType::E4000,
296            2 => TunerType::FC0012,
297            3 => TunerType::FC0013,
298            4 => TunerType::FC2580,
299            5 => TunerType::R820T,
300            6 => TunerType::R828D,
301            _ => TunerType::Unknown,
302        }
303    }
304
305    /// Get a list of gains supported by the tuner.
306    /// Gain values in tenths of a dB, 115 means 11.5 dB
307    pub fn get_tuner_gains(&self) -> Vec<i32> {
308        let n = unsafe { sys::rtlsdr_get_tuner_gains(self.dev, std::ptr::null_mut()) };
309        let mut gains = vec![0i32; n as usize];
310        let n = unsafe { sys::rtlsdr_get_tuner_gains(self.dev, gains.as_mut_ptr()) };
311        gains.truncate(n as usize);
312        gains
313    }
314
315    /// Get actual (RF / HF) gain the device is configured to - excluding the IF gain.
316    /// Gain in tenths of a dB, 115 means 11.5 dB.
317    /// unfortunately it's impossible to distinguish error against 0 dB
318    pub fn get_tuner_gain(&self) -> i32 {
319        unsafe { sys::rtlsdr_get_tuner_gain(self.dev) }
320    }
321
322    /// Set the gain for the device.
323    /// Manual gain mode must be enabled for this to work.
324    /// Valid gain values may be queried with rtlsdr_get_tuner_gains function.
325    /// Gain in tenths of a dB, 115 means 11.5 dB
326    pub fn set_tuner_gain(&mut self, gain: i32) -> Result<(), String> {
327        rtlsdr_result!(sys::rtlsdr_set_tuner_gain(self.dev, gain))
328            .map_err(|e| format!("Failed to set tuner gain: {}", e))?;
329
330        Ok(())
331    }
332
333    /// Set the bandwidth for the device.
334    /// @param bw bandwidth in Hz. Zero means automatic BW selection.
335    pub fn set_tuner_bandwidth(&mut self, bw: u32) -> Result<(), String> {
336        rtlsdr_result!(sys::rtlsdr_set_tuner_bandwidth(self.dev, bw,))
337            .map_err(|e| format!("Failed to set bandwidth: {}", e))?;
338
339        Ok(())
340    }
341
342    /// Set the intermediate frequency gain for the device.
343    /// @param stage intermediate frequency gain stage number (1 to 6 for E4000)
344    /// @param gain in tenths of a dB, -30 means -3.0 dB.
345    pub fn set_tuner_if_gain(&mut self, stage: i32, gain: i32) -> Result<(), String> {
346        rtlsdr_result!(sys::rtlsdr_set_tuner_if_gain(self.dev, stage, gain))
347            .map_err(|e| format!("Failed to set IF gain: {}", e))?;
348
349        Ok(())
350    }
351
352    /// Set the gain mode (automatic/manual) for the device.
353    /// Manual gain mode must be enabled for the gain setter function to work.
354    pub fn set_tuner_gain_mode(&mut self, manual: bool) -> Result<(), String> {
355        rtlsdr_result!(sys::rtlsdr_set_tuner_gain_mode(self.dev, manual as i32))
356            .map_err(|e| format!("Failed to set tuner gain mode: {}", e))?;
357
358        Ok(())
359    }
360
361    /// Get actual sample rate the device is configured to.
362    /// @return sample rate in Hz
363    pub fn get_sample_rate(&self) -> Result<u32, String> {
364        match unsafe { sys::rtlsdr_get_sample_rate(self.dev) } {
365            0 => Err("Failed to get sample rate".to_string()),
366            rate => Ok(rate),
367        }
368    }
369
370    /// Set the sample rate for the device.
371    /// @param rate sample rate in Hz
372    pub fn set_sample_rate(&mut self, rate: u32) -> Result<(), String> {
373        rtlsdr_result!(sys::rtlsdr_set_sample_rate(self.dev, rate))
374            .map_err(|e| format!("Failed to set sample rate: {}", e))?;
375
376        Ok(())
377    }
378
379    /// Enable test mode that returns an 8 bit counter instead of the samples.
380    /// The counter is generated inside the RTL2832.
381    pub fn set_test_mode(&mut self, test_mode: bool) -> Result<(), String> {
382        rtlsdr_result!(sys::rtlsdr_set_testmode(self.dev, test_mode as i32))
383            .map_err(|e| format!("Failed to set test mode: {}", e))?;
384
385        Ok(())
386    }
387
388    /// Enable or disable the internal digital AGC of the RTL2832.
389    pub fn set_agc_mode(&mut self, enabled: bool) -> Result<(), String> {
390        rtlsdr_result!(sys::rtlsdr_set_agc_mode(self.dev, enabled as i32))
391            .map_err(|e| format!("Failed to set agc mode: {}", e))?;
392
393        Ok(())
394    }
395
396    /// Get state of the direct sampling mode
397    pub fn get_direct_sampling(&self) -> Result<DirectSampling, String> {
398        rtlsdr_result!(sys::rtlsdr_get_direct_sampling(self.dev,))
399            .map_err(|e| format!("Failed to get direct sampling mode: {}", e))
400            .map(|mode| match mode {
401                0 => DirectSampling::Disabled,
402                1 => DirectSampling::I,
403                2 => DirectSampling::Q,
404                _ => DirectSampling::Disabled,
405            })
406    }
407
408    /// Enable or disable the direct sampling mode.
409    /// When enabled, the IF mode of the RTL2832 is activated, and set_center_freq() will control the IF-frequency of the DDC,
410    /// which can be used to tune from 0 to 28.8 MHz (xtal frequency of the RTL2832).
411    pub fn set_direct_sampling(&mut self, mode: DirectSampling) -> Result<(), String> {
412        rtlsdr_result!(sys::rtlsdr_set_direct_sampling(self.dev, mode as i32))
413            .map_err(|e| format!("Failed to set direct sampling mode: {}", e))?;
414
415        Ok(())
416    }
417
418    /// Get state of the offset tuning mode
419    pub fn get_offset_tuning(&self) -> Result<bool, String> {
420        rtlsdr_result!(sys::rtlsdr_get_offset_tuning(self.dev))
421            .map_err(|e| format!("Failed to get offset tuning mode: {}", e))
422            .map(|mode| mode == 1)
423    }
424
425    /// Enable or disable offset tuning for zero-IF tuners, which allows to avoid problems caused by the DC offset of the ADCs and 1/f noise.
426    pub fn set_offset_tuning(&mut self, enabled: bool) -> Result<(), String> {
427        rtlsdr_result!(sys::rtlsdr_set_offset_tuning(self.dev, enabled as i32))
428            .map_err(|e| format!("Failed to set offset tuning mode: {}", e))?;
429
430        Ok(())
431    }
432
433    /// Reset buffer in RTL2832
434    pub fn reset_buffer(&mut self) -> Result<(), String> {
435        rtlsdr_result!(sys::rtlsdr_reset_buffer(self.dev))
436            .map_err(|e| format!("Failed to reset buffer: {}", e))?;
437
438        Ok(())
439    }
440
441    /// Read data synchronously
442    pub fn read_sync(&self, buf: &mut [u8]) -> Result<i32, RtlsdrError> {
443        let mut n_read = 0;
444
445        rtlsdr_result!(sys::rtlsdr_read_sync(
446            self.dev,
447            buf.as_mut_ptr() as *mut std::ffi::c_void,
448            buf.len() as i32,
449            &mut n_read
450        ))?;
451
452        Ok(n_read)
453    }
454
455    /// Read samples from the device asynchronously.
456    /// This function will block until it is being canceled using rtlsdr_cancel_async()
457    /// NOTE: This function is deprecated and is subject for removal.
458    /// @param cb callback function to return received samples
459    /// @param ctx user specific context to pass via the callback function
460    #[deprecated]
461    pub fn wait_async<F>(&self, cb: F) -> Result<(), String>
462    where
463        F: FnMut(Vec<u8>),
464    {
465        self.read_async(cb, 0, 0)
466    }
467
468    /// Read samples from the device asynchronously.
469    /// This function will block until it is being canceled using rtlsdr_cancel_async()
470    /// @param cb callback function to return received samples
471    /// @param buf_num optional buffer count, buf_num * buf_len = overall buffer size
472    /// set to 0 for default buffer count (15)
473    /// @param buf_len optional buffer length, must be multiple of 512,
474    /// should be a multiple of 16384 (URB size), set to 0 for default buffer length (16 * 32 * 512)
475    pub fn read_async<F>(&self, mut cb: F, buf_num: u32, buf_len: u32) -> Result<(), String>
476    where
477        F: FnMut(Vec<u8>),
478    {
479        unsafe extern "C" fn _cb<F>(buf: *mut u8, len: u32, ctx: *mut std::ffi::c_void)
480        where
481            F: FnMut(Vec<u8>),
482        {
483            let cb = &mut *(ctx as *mut F);
484            let mut vec = Vec::with_capacity(len as usize);
485            (0..len).for_each(|i| vec.push(*buf.offset(i as isize)));
486            cb(vec);
487        }
488
489        rtlsdr_result!(sys::rtlsdr_read_async(
490            self.dev,
491            Some(_cb::<F>),
492            &mut cb as *mut F as *mut std::ffi::c_void,
493            buf_num,
494            buf_len
495        ))
496        .map_err(|e| format!("Failed to wait async: {}", e))?;
497
498        Ok(())
499    }
500
501    /// Cancel all pending asynchronous operations on the device.
502    /// Due to incomplete concurrency implementation, this should only be called from within the callback function, so it is
503    /// in the correct thread.
504    pub fn cancel_async(&self) -> Result<(), String> {
505        rtlsdr_result!(sys::rtlsdr_cancel_async(self.dev))
506            .map_err(|e| format!("Failed to cancel async: {}", e))?;
507
508        Ok(())
509    }
510
511    /// Enable or disable (the bias tee on) GPIO PIN 0 - if not reconfigured.
512    /// See rtlsdr_set_opt_string() option 'T'.
513    /// This works for rtl-sdr.com v3 dongles, see http://www.rtl-sdr.com/rtl-sdr-blog-v-3-dongles-user-guide/
514    /// Note: rtlsdr_close() does not clear GPIO lines, so it leaves the (bias tee) line enabled if a client program
515    /// doesn't explictly disable it.
516    pub fn set_bias_tee(&mut self, on: bool) -> Result<(), String> {
517        rtlsdr_result!(sys::rtlsdr_set_bias_tee(self.dev, on as i32)).map_err(|e| match e {
518            -1 => "Device is not initialized".to_string(),
519            _ => format!("Failed to set bias tee: {}", e),
520        })?;
521
522        Ok(())
523    }
524
525    /// Enable or disable (the bias tee on) the given GPIO pin.
526    /// Note: rtlsdr_close() does not clear GPIO lines, so it leaves the (bias tee) lines enabled if a client program
527    /// doesn't explictly disable it.
528    /// @param gpio the gpio pin -- assuming this line is connected to Bias T.
529    /// gpio needs to be in 0 .. 7. BUT pin 4 is connected to Tuner RESET.
530    /// and for FC0012 is already connected/reserved pin 6 for switching V/U-HF.
531    /// @param on: 1 for Bias T on. 0 for Bias T off.
532    pub fn set_bias_tee_gpio(&mut self, gpio: i32, on: bool) -> Result<(), String> {
533        rtlsdr_result!(sys::rtlsdr_set_bias_tee_gpio(self.dev, gpio, on as i32)).map_err(|e| {
534            match e {
535                -1 => "Device is not initialized".to_string(),
536                _ => format!("Failed to set bias tee gpio: {}", e),
537            }
538        })?;
539
540        Ok(())
541    }
542}
543
544#[doc = "Get all available devices"]
545pub fn get_devices() -> Vec<Device> {
546    let n = unsafe { sys::rtlsdr_get_device_count() };
547
548    (0..n)
549        .map(|index| Device {
550            index,
551            dev: std::ptr::null_mut(),
552        })
553        .collect()
554}