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