airspy/
lib.rs

1// Rust binding to libairspy
2// Copyright 2015 Adam Greig <adam@adamgreig.com>
3// Licensed under the MIT License.
4
5extern crate num;
6
7use std::ffi::CStr;
8use std::result;
9use std::str;
10use std::sync::mpsc::Sender;
11use num::Complex;
12
13mod ffi;
14
15/// Store a reference to an opened Airspy device.
16pub struct Airspy {
17    ptr: *mut ffi::airspy_device
18}
19
20/// Ensure whenever the Airspy goes out of scope it is closed.
21impl Drop for Airspy {
22    #[inline(never)]
23    fn drop(&mut self) {
24        unsafe { ffi::airspy_close(self.ptr); }
25    }
26}
27
28/// Hold major, minor and revision version numbers of libairspy C library.
29pub struct LibVersion {
30    pub major: u32,
31    pub minor: u32,
32    pub revision: u32
33}
34
35/// Hold an Airspy's part ID and serial number.
36pub struct PartID {
37    pub part_id: [u32; 2],
38    pub serial_no: u64
39}
40
41/// Represent IQ data, either f32 or i16
42pub type IQ<T> = Complex<T>;
43
44/// Represent real-only data, either f32 or i16 or u16
45pub type Real<T> = T;
46
47/// Trait for all allowable sample types
48// TODO: update to use associated constants when that hits stable
49pub trait SampleType: Clone { fn get_type() -> ffi::airspy_sample_type; }
50impl SampleType for IQ<f32> { fn get_type() -> ffi::airspy_sample_type {
51    ffi::airspy_sample_type::AIRSPY_SAMPLE_FLOAT32_IQ } }
52impl SampleType for IQ<i16> { fn get_type() -> ffi::airspy_sample_type {
53    ffi::airspy_sample_type::AIRSPY_SAMPLE_INT16_IQ } }
54impl SampleType for Real<f32> { fn get_type() -> ffi::airspy_sample_type {
55    ffi::airspy_sample_type::AIRSPY_SAMPLE_FLOAT32_REAL } }
56impl SampleType for Real<i16> { fn get_type() -> ffi::airspy_sample_type {
57    ffi::airspy_sample_type::AIRSPY_SAMPLE_INT16_REAL } }
58impl SampleType for Real<u16> { fn get_type() -> ffi::airspy_sample_type {
59    ffi::airspy_sample_type::AIRSPY_SAMPLE_UINT16_REAL } }
60
61/// Choice of GPIO port
62pub enum GPIOPort {
63    Port0, Port1, Port2, Port3, Port4, Port5, Port6, Port7
64}
65
66/// Choice of GPIO pin
67pub enum GPIOPin {
68    Pin0,  Pin1,  Pin2,  Pin3,  Pin4,  Pin5,  Pin6,  Pin7,
69    Pin8,  Pin9,  Pin10, Pin11, Pin12, Pin13, Pin14, Pin15,
70    Pin16, Pin17, Pin18, Pin19, Pin20, Pin21, Pin22, Pin23,
71    Pin24, Pin25, Pin26, Pin27, Pin28, Pin29, Pin30, Pin31
72}
73
74/// GPIO Direction
75pub enum GPIODirection {
76    Input, Output
77}
78
79/// Error type for libairspy functions.
80#[derive(Debug)]
81pub struct FFIError {
82    errno: ffi::airspy_error
83}
84
85impl FFIError {
86    fn new(err: ffi::airspy_error) -> FFIError {
87        FFIError { errno: err }
88    }
89
90    fn errstr(&self) -> &str {
91        let cstr = unsafe {
92            CStr::from_ptr(ffi::airspy_error_name(self.errno))
93        };
94        str::from_utf8(cstr.to_bytes()).unwrap()
95    }
96}
97
98impl std::fmt::Display for FFIError {
99    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
100        write!(f, "Airspy Error: {}", self.errstr())
101    }
102}
103
104impl std::error::Error for FFIError {
105    fn description(&self) -> &str {
106        self.errstr()
107    }
108}
109
110/// Result type for libairspy functions.
111pub type Result<T> = result::Result<T, FFIError>;
112
113/// Fetch the current version of libairspy C library.
114pub fn lib_version() -> LibVersion {
115    let mut v = ffi::airspy_lib_version_t {
116        major_version: 0, minor_version: 0, revision: 0};
117    unsafe { ffi::airspy_lib_version(&mut v); }
118
119    LibVersion {
120        major: v.major_version, minor: v.minor_version, revision: v.revision}
121}
122
123macro_rules! ffifn {
124    ($f:expr, $r:expr) => (
125        match unsafe { $f } {
126            ffi::airspy_error::AIRSPY_SUCCESS => Ok($r),
127            err => Err(FFIError::new(err))
128        }
129    );
130    ($f:expr) => (
131        ffifn!($f, ())
132    );
133}
134
135/// Callback in Rust to send to the libairspy C library that sends buffers
136/// through to a user channel, quitting streaming when the channel hangs up.
137extern fn rx_cb<T>(transfer: *mut ffi::airspy_transfer_t) -> ffi::c_int
138    where T: SampleType
139{
140    let transfer = unsafe { &*transfer };
141    let sample_count = transfer.sample_count as usize;
142    let buffer = unsafe {
143        std::slice::from_raw_parts(transfer.samples as *const T, sample_count)
144    }.to_vec();
145
146    // Turn the ctx into a &Sender and send the buffer along it.
147    // If it works, keep asking for more samples, but if not, we'll quit.
148    let sender: &Sender<Vec<T>> = unsafe { &*(transfer.ctx as *const _)};
149    match sender.send(buffer) {
150        Ok(_) => 0,
151        Err(_) => {
152            // Drop the Sender to prevent leaks,
153            // then tell libairspy to stop streaming.
154            let boxed: Box<Sender<Vec<T>>> = unsafe {
155                std::mem::transmute(transfer.ctx as *const _) };
156            std::mem::drop(boxed);
157            1
158        }
159    }
160}
161
162
163/// Initialise the Airspy library. Call once at application startup.
164pub fn init() -> Result<()> {
165    ffifn!(ffi::airspy_init())
166}
167
168/// Deinitialise the Airspy library. Call once at application end.
169pub fn exit() -> Result<()> {
170    ffifn!(ffi::airspy_exit())
171}
172
173impl Airspy {
174    /// Try to open the next available Airspy device.
175    pub fn new() -> Result<Airspy> {
176        let mut device: Airspy = Airspy{ ptr: unsafe { std::mem::zeroed() }};
177        ffifn!(ffi::airspy_open(&mut device.ptr), device)
178    }
179
180    /// Try to open a specific Airspy device by serial number.
181    pub fn from_serial(serial_number: u64) -> Result<Airspy> {
182        let mut device: Airspy = Airspy{ ptr: unsafe { std::mem::zeroed() }};
183        ffifn!(ffi::airspy_open_sn(&mut device.ptr, serial_number), device)
184    }
185
186    /// Get available sample rates for this Airspy.
187    pub fn get_sample_rates(&mut self) -> Result<Vec<u32>> {
188        let mut len: u32 = 0;
189        let lenp = &mut len as *mut u32;
190        try!(ffifn!(ffi::airspy_get_samplerates(self.ptr, lenp, 0)));
191        let mut rates: Vec<u32> = Vec::with_capacity(len as usize);
192        let ratesp = rates.as_mut_ptr();
193        try!(ffifn!(ffi::airspy_get_samplerates(self.ptr, ratesp, len)));
194        unsafe { rates.set_len(len as usize); }
195        Ok(rates)
196    }
197
198    /// Set this Airspy's sample rate to a specific rate.
199    ///
200    /// This rate must be in the available rates or an error is returned.
201    pub fn set_sample_rate(&mut self, target_rate: u32) -> Result<()> {
202        let rates = try!(self.get_sample_rates());
203        for (idx, rate) in rates.iter().enumerate() {
204            if *rate == target_rate {
205                return ffifn!(ffi::airspy_set_samplerate(
206                    self.ptr, idx as u32));
207            }
208        }
209        Err(FFIError::new(ffi::airspy_error::AIRSPY_ERROR_INVALID_PARAM))
210    }
211
212    /// Start RX streaming from the Airspy.
213    ///
214    /// The given channel will be sent Vec<T> when Airspy callbacks occur. When
215    /// the remote channel hangs up, libairspy is told to stop streaming and
216    /// the Sender is dropped.
217    ///
218    /// T is used to set the sample type and may be IQ<f32>, IQ<i16>,
219    /// Real<f32>, Real<i16> or Real<u16>.
220    pub fn start_rx<T>(&mut self, sender: Sender<Vec<T>>) -> Result<()>
221        where T: SampleType
222    {
223        // Set the Airspy to the correct sample type
224        try!(ffifn!(ffi::airspy_set_sample_type(self.ptr, T::get_type())));
225
226        // Box the Sender to move it onto the heap, then get a void* to it.
227        let boxed_sender = Box::new(sender);
228        let ctx = &*boxed_sender as *const _ as *mut ffi::c_void;
229
230        // Forget the heap Sender so it is not immediately destroyed.
231        std::mem::forget(boxed_sender);
232
233        // Start data reception
234        ffifn!(ffi::airspy_start_rx(self.ptr, rx_cb::<T>, ctx))
235    }
236
237    /// Check if the Airspy is currently streaming.
238    pub fn is_streaming(&mut self) -> bool {
239        unsafe { ffi::airspy_is_streaming(self.ptr) == 1 }
240    }
241
242    /// Write a register on the Si5351C
243    pub fn si5351c_write(&mut self, register: u8, value: u8) -> Result<()> {
244        ffifn!(ffi::airspy_si5351c_write(self.ptr, register, value))
245    }
246
247    /// Read a register on the Si5351C
248    pub fn si5351c_read(&mut self, register: u8) -> Result<u8> {
249        let mut val: u8 = 0;
250        try!(ffifn!(ffi::airspy_si5351c_read(
251            self.ptr, register, &mut val as *mut u8)));
252        Ok(val)
253    }
254
255    /// Write a register on the R820t
256    pub fn r820t_write(&mut self, register: u8, value: u8) -> Result<()> {
257        ffifn!(ffi::airspy_r820t_write(self.ptr, register, value))
258    }
259
260    /// Read a register on the R820t
261    pub fn r820t_read(&mut self, register: u8) -> Result<u8> {
262        let mut val: u8 = 0;
263        try!(ffifn!(ffi::airspy_r820t_read(
264            self.ptr, register, &mut val as *mut u8)));
265        Ok(val)
266    }
267
268    fn map_gpio_port_pin(port: GPIOPort, pin: GPIOPin)
269            -> (ffi::airspy_gpio_port_t, ffi::airspy_gpio_pin_t) {
270        (match port {
271            GPIOPort::Port0 => ffi::airspy_gpio_port_t::GPIO_PORT0,
272            GPIOPort::Port1 => ffi::airspy_gpio_port_t::GPIO_PORT1,
273            GPIOPort::Port2 => ffi::airspy_gpio_port_t::GPIO_PORT2,
274            GPIOPort::Port3 => ffi::airspy_gpio_port_t::GPIO_PORT3,
275            GPIOPort::Port4 => ffi::airspy_gpio_port_t::GPIO_PORT4,
276            GPIOPort::Port5 => ffi::airspy_gpio_port_t::GPIO_PORT5,
277            GPIOPort::Port6 => ffi::airspy_gpio_port_t::GPIO_PORT6,
278            GPIOPort::Port7 => ffi::airspy_gpio_port_t::GPIO_PORT7,
279        }, match pin {
280            GPIOPin::Pin0  => ffi::airspy_gpio_pin_t::GPIO_PIN0,
281            GPIOPin::Pin1  => ffi::airspy_gpio_pin_t::GPIO_PIN1,
282            GPIOPin::Pin2  => ffi::airspy_gpio_pin_t::GPIO_PIN2,
283            GPIOPin::Pin3  => ffi::airspy_gpio_pin_t::GPIO_PIN3,
284            GPIOPin::Pin4  => ffi::airspy_gpio_pin_t::GPIO_PIN4,
285            GPIOPin::Pin5  => ffi::airspy_gpio_pin_t::GPIO_PIN5,
286            GPIOPin::Pin6  => ffi::airspy_gpio_pin_t::GPIO_PIN6,
287            GPIOPin::Pin7  => ffi::airspy_gpio_pin_t::GPIO_PIN7,
288            GPIOPin::Pin8  => ffi::airspy_gpio_pin_t::GPIO_PIN8,
289            GPIOPin::Pin9  => ffi::airspy_gpio_pin_t::GPIO_PIN9,
290            GPIOPin::Pin10 => ffi::airspy_gpio_pin_t::GPIO_PIN10,
291            GPIOPin::Pin11 => ffi::airspy_gpio_pin_t::GPIO_PIN11,
292            GPIOPin::Pin12 => ffi::airspy_gpio_pin_t::GPIO_PIN12,
293            GPIOPin::Pin13 => ffi::airspy_gpio_pin_t::GPIO_PIN13,
294            GPIOPin::Pin14 => ffi::airspy_gpio_pin_t::GPIO_PIN14,
295            GPIOPin::Pin15 => ffi::airspy_gpio_pin_t::GPIO_PIN15,
296            GPIOPin::Pin16 => ffi::airspy_gpio_pin_t::GPIO_PIN16,
297            GPIOPin::Pin17 => ffi::airspy_gpio_pin_t::GPIO_PIN17,
298            GPIOPin::Pin18 => ffi::airspy_gpio_pin_t::GPIO_PIN18,
299            GPIOPin::Pin19 => ffi::airspy_gpio_pin_t::GPIO_PIN19,
300            GPIOPin::Pin20 => ffi::airspy_gpio_pin_t::GPIO_PIN20,
301            GPIOPin::Pin21 => ffi::airspy_gpio_pin_t::GPIO_PIN21,
302            GPIOPin::Pin22 => ffi::airspy_gpio_pin_t::GPIO_PIN22,
303            GPIOPin::Pin23 => ffi::airspy_gpio_pin_t::GPIO_PIN23,
304            GPIOPin::Pin24 => ffi::airspy_gpio_pin_t::GPIO_PIN24,
305            GPIOPin::Pin25 => ffi::airspy_gpio_pin_t::GPIO_PIN25,
306            GPIOPin::Pin26 => ffi::airspy_gpio_pin_t::GPIO_PIN26,
307            GPIOPin::Pin27 => ffi::airspy_gpio_pin_t::GPIO_PIN27,
308            GPIOPin::Pin28 => ffi::airspy_gpio_pin_t::GPIO_PIN28,
309            GPIOPin::Pin29 => ffi::airspy_gpio_pin_t::GPIO_PIN29,
310            GPIOPin::Pin30 => ffi::airspy_gpio_pin_t::GPIO_PIN30,
311            GPIOPin::Pin31 => ffi::airspy_gpio_pin_t::GPIO_PIN31,
312        })
313    }
314
315    /// Write a GPIO port:pin to `val`, false to clear or true to set.
316    pub fn gpio_write(&mut self, port: GPIOPort, pin: GPIOPin, val: bool)
317        -> Result<()>
318    {
319        let (port, pin) = Airspy::map_gpio_port_pin(port, pin);
320        ffifn!(ffi::airspy_gpio_write(self.ptr, port, pin, val as u8))
321    }
322
323    /// Read a GPIO port:pin
324    pub fn gpio_read(&mut self, port: GPIOPort, pin: GPIOPin) -> Result<bool> {
325        let mut val: u8 = 0;
326        let (port, pin) = Airspy::map_gpio_port_pin(port, pin);
327        try!(ffifn!(ffi::airspy_gpio_read(
328            self.ptr, port, pin, &mut val as *mut u8)));
329        Ok(match val {
330            0 => false,
331            1 => true,
332            _ => unreachable!()
333        })
334    }
335
336    /// Set a GPIO port:pin direction.
337    pub fn gpio_set_direction(&mut self, port: GPIOPort, pin: GPIOPin,
338        dir: GPIODirection) -> Result<()>
339    {
340        let (port, pin) = Airspy::map_gpio_port_pin(port, pin);
341        let dir: u8 = match dir {
342            GPIODirection::Input => 0,
343            GPIODirection::Output => 1
344        };
345        ffifn!(ffi::airspy_gpiodir_write(self.ptr, port, pin, dir))
346    }
347
348    /// Get a GPIO port:pin direction.
349    pub fn gpio_get_direction(&mut self, port: GPIOPort, pin: GPIOPin)
350        -> Result<GPIODirection>
351    {
352        let mut dir: u8 = 0;
353        let (port, pin) = Airspy::map_gpio_port_pin(port, pin);
354        try!(ffifn!(ffi::airspy_gpiodir_read(
355            self.ptr, port, pin, &mut dir as *mut u8)));
356        Ok(match dir {
357            0 => GPIODirection::Input,
358            1 => GPIODirection::Output,
359            _ => unreachable!()
360        })
361    }
362
363    /// Get the Airspy board type.
364    pub fn get_board_id(&mut self) -> Result<&str> {
365        let mut id: u8 = 0;
366        try!(ffifn!(ffi::airspy_board_id_read(self.ptr, &mut id as *mut u8)));
367        let cstr = unsafe {
368            CStr::from_ptr(ffi::airspy_board_id_name(id))
369        };
370        Ok(str::from_utf8(cstr.to_bytes()).unwrap())
371    }
372
373    /// Get the Airspy firmware version number.
374    pub fn get_version(&mut self) -> Result<String> {
375        let mut buf: Vec<i8> = Vec::with_capacity(255);
376        let bufp = buf.as_mut_ptr();
377        try!(ffifn!(ffi::airspy_version_string_read(self.ptr, bufp, 255)));
378        let cstr = unsafe { CStr::from_ptr(buf.as_ptr()) };
379        Ok(String::from(str::from_utf8(cstr.to_bytes()).unwrap()))
380    }
381
382    /// Get the Airspy part ID and serial number.
383    pub fn get_partid_serial(&mut self) -> Result<PartID> {
384        let mut v = ffi::airspy_read_partid_serialno_t {
385            part_id: [0u32; 2],
386            serial_no: [0u32; 4]
387        };
388        try!(ffifn!(ffi::airspy_board_partid_serialno_read(self.ptr, &mut v)));
389        Ok(PartID {
390            part_id: v.part_id,
391            serial_no: (v.serial_no[2] as u64) << 32 | v.serial_no[3] as u64
392        })
393    }
394
395    /// Set Airspy centre frequency, `freq` 24000000 to 1750000000 (in Hz)
396    pub fn set_freq(&mut self, freq: u32) -> Result<()> {
397        ffifn!(ffi::airspy_set_freq(self.ptr, freq))
398    }
399
400    /// Set LNA gain, 0 to 15dB
401    pub fn set_lna_gain(&mut self, gain: u8) -> Result<()> {
402        ffifn!(ffi::airspy_set_lna_gain(self.ptr, gain))
403    }
404
405    /// Set mixer gain, 0 to 15dB
406    pub fn set_mixer_gain(&mut self, gain: u8) -> Result<()> {
407        ffifn!(ffi::airspy_set_mixer_gain(self.ptr, gain))
408    }
409
410    /// Set VGA gain, 0 to 15dB
411    pub fn set_vga_gain(&mut self, gain: u8) -> Result<()> {
412        ffifn!(ffi::airspy_set_vga_gain(self.ptr, gain))
413    }
414
415    /// Enable/disable LNA AGC
416    pub fn set_lna_agc(&mut self, enable: bool) -> Result<()> {
417        ffifn!(ffi::airspy_set_lna_agc(self.ptr, enable as u8))
418    }
419
420    /// Enable/disable mixer AGC
421    pub fn set_mixer_agc(&mut self, enable: bool) -> Result<()> {
422        ffifn!(ffi::airspy_set_mixer_agc(self.ptr, enable as u8))
423    }
424
425    /// Enable/disable RF bias voltage
426    pub fn set_rf_bias(&mut self, enable: bool) -> Result<()> {
427        ffifn!(ffi::airspy_set_rf_bias(self.ptr, enable as u8))
428    }
429}
430
431#[cfg(test)]
432mod tests {
433    use super::*;
434
435    #[test]
436    fn test_lib_version() {
437        let v = lib_version();
438        assert!(v.major == 1);
439        assert!(v.minor == 0);
440        assert!(v.revision >= 6);
441    }
442
443    #[test]
444    fn test_init() {
445        assert!(init().is_ok());
446        let _ = exit();
447    }
448
449    #[test]
450    fn test_exit() {
451        let _ = init();
452        assert!(exit().is_ok());
453    }
454
455    #[test]
456    fn test_new() {
457        let _ = init();
458        let airspy = Airspy::new();
459        assert!(airspy.is_ok());
460        let _ = exit();
461    }
462
463    #[test]
464    fn test_from_serial() {
465        let _ = init();
466        let mut serial: u64;
467        {
468            let mut airspy = Airspy::new().unwrap();
469            let v = airspy.get_partid_serial().unwrap();
470            serial = v.serial_no;
471        }
472        let airspy = Airspy::from_serial(serial);
473        assert!(airspy.is_ok());
474        let _ = exit();
475    }
476
477    #[test]
478    fn test_get_sample_rates() {
479        let _ = init();
480        let mut airspy = Airspy::new().unwrap();
481        let rates = airspy.get_sample_rates();
482        assert!(rates.is_ok());
483        let rates = rates.unwrap();
484        assert!(rates.len() == 2);
485        assert!(rates.contains(&2500000));
486        assert!(rates.contains(&10000000));
487        let _ = exit();
488    }
489
490    #[test]
491    fn test_set_sample_rate() {
492        let _ = init();
493        let mut airspy = Airspy::new().unwrap();
494        assert!(airspy.set_sample_rate(10_000_000).is_ok());
495    }
496
497    #[test]
498    fn test_start_rx() {
499        let _ = init();
500        let mut airspy = Airspy::new().unwrap();
501        let (tx, _) = ::std::sync::mpsc::channel();
502        assert!(airspy.start_rx::<IQ<f32>>(tx).is_ok());
503    }
504
505    #[test]
506    fn test_is_streaming() {
507        let _ = init();
508        let mut airspy = Airspy::new().unwrap();
509        assert!(airspy.is_streaming() == false);
510    }
511
512    #[test]
513    fn test_si5351c_read() {
514        let mut airspy = Airspy::new().unwrap();
515        assert!(airspy.si5351c_read(16).is_ok());
516    }
517
518    #[test]
519    fn test_si5351c_write() {
520        let mut airspy = Airspy::new().unwrap();
521        let val = airspy.si5351c_read(16).unwrap();
522        assert!(airspy.si5351c_write(16, val).is_ok());
523    }
524
525    #[test]
526    fn test_r820t_read() {
527        let mut airspy = Airspy::new().unwrap();
528        assert!(airspy.r820t_read(0x0F).is_ok());
529    }
530
531    #[test]
532    fn test_r820t_write() {
533        let mut airspy = Airspy::new().unwrap();
534        let val = airspy.r820t_read(0x0F).unwrap();
535        assert!(airspy.r820t_write(0x0F, val).is_ok());
536    }
537
538    #[test]
539    fn test_gpio_read() {
540        let mut airspy = Airspy::new().unwrap();
541        assert!(airspy.gpio_read(GPIOPort::Port0, GPIOPin::Pin0).is_ok());
542    }
543
544    #[test]
545    fn test_gpio_write() {
546        let mut airspy = Airspy::new().unwrap();
547        let val = airspy.gpio_read(GPIOPort::Port0, GPIOPin::Pin0).unwrap();
548        assert!(airspy.gpio_write(
549            GPIOPort::Port0, GPIOPin::Pin0, val).is_ok());
550    }
551
552    #[test]
553    fn test_gpio_get_dir() {
554        let mut airspy = Airspy::new().unwrap();
555        assert!(airspy.gpio_get_direction(
556            GPIOPort::Port0, GPIOPin::Pin0).is_ok());
557    }
558
559    #[test]
560    fn test_gpio_set_dir() {
561        let mut airspy = Airspy::new().unwrap();
562        let dir = airspy.gpio_get_direction(
563            GPIOPort::Port0, GPIOPin::Pin0).unwrap();
564        assert!(airspy.gpio_set_direction(
565            GPIOPort::Port0, GPIOPin::Pin0, dir).is_ok());
566    }
567
568    #[test]
569    fn test_get_board() {
570        let _ = init();
571        let mut airspy = Airspy::new().unwrap();
572        assert!(airspy.get_board_id().unwrap() == "AIRSPY");
573    }
574
575    #[test]
576    fn test_get_version() {
577        let _ = init();
578        let mut airspy = Airspy::new().unwrap();
579        assert!(airspy.get_version().is_ok());
580        // Don't check this value as it is hardware-dependent.
581    }
582
583    #[test]
584    fn test_get_partid_serial() {
585        let _ = init();
586        let mut airspy = Airspy::new().unwrap();
587        assert!(airspy.get_partid_serial().is_ok());
588        // Don't check these values as they are hardware-dependent.
589    }
590
591    #[test]
592    fn test_set_freq() {
593        let _ = init();
594        let mut airspy = Airspy::new().unwrap();
595        assert!(airspy.set_freq(434000000).is_ok());
596    }
597
598    #[test]
599    fn test_set_lna_gain() {
600        let _ = init();
601        let mut airspy = Airspy::new().unwrap();
602        assert!(airspy.set_lna_gain(7).is_ok());
603    }
604
605    #[test]
606    fn test_set_mixer_gain() {
607        let _ = init();
608        let mut airspy = Airspy::new().unwrap();
609        assert!(airspy.set_mixer_gain(7).is_ok());
610    }
611
612    #[test]
613    fn test_set_vga_gain() {
614        let _ = init();
615        let mut airspy = Airspy::new().unwrap();
616        assert!(airspy.set_vga_gain(7).is_ok());
617    }
618
619    #[test]
620    fn test_set_lna_agc() {
621        let _ = init();
622        let mut airspy = Airspy::new().unwrap();
623        assert!(airspy.set_lna_agc(true).is_ok());
624    }
625
626    #[test]
627    fn test_set_mixer_agc() {
628        let _ = init();
629        let mut airspy = Airspy::new().unwrap();
630        assert!(airspy.set_mixer_agc(true).is_ok());
631    }
632
633    #[test]
634    fn test_set_rf_bias() {
635        let _ = init();
636        let mut airspy = Airspy::new().unwrap();
637        assert!(airspy.set_rf_bias(true).is_ok());
638    }
639}