hackrfone/
lib.rs

1//! HackRF One API.
2//!
3//! To get started take a look at [`HackRfOne::new`].
4#![doc(html_root_url = "https://docs.rs/hackrfone/0.2.3")]
5#![cfg_attr(docsrs, feature(doc_cfg))]
6#![deny(missing_docs)]
7
8pub use rusb;
9
10use rusb::{request_type, Direction, GlobalContext, Recipient, RequestType, UsbContext, Version};
11use std::{convert::TryFrom, time::Duration};
12
13#[cfg(feature = "num-complex")]
14#[cfg_attr(docsrs, doc(cfg(feature = "num-complex")))]
15pub use num_complex;
16
17/// HackRF USB vendor ID.
18const HACKRF_USB_VID: u16 = 0x1D50;
19/// HackRF One USB product ID.
20const HACKRF_ONE_USB_PID: u16 = 0x6089;
21
22#[allow(dead_code)]
23#[repr(u8)]
24enum Request {
25    SetTransceiverMode = 1,
26    Max2837Write = 2,
27    Max2837Read = 3,
28    Si5351CWrite = 4,
29    Si5351CRead = 5,
30    SampleRateSet = 6,
31    BasebandFilterBandwidthSet = 7,
32    Rffc5071Write = 8,
33    Rffc5071Read = 9,
34    SpiflashErase = 10,
35    SpiflashWrite = 11,
36    SpiflashRead = 12,
37    BoardIdRead = 14,
38    VersionStringRead = 15,
39    SetFreq = 16,
40    AmpEnable = 17,
41    BoardPartidSerialnoRead = 18,
42    SetLnaGain = 19,
43    SetVgaGain = 20,
44    SetTxvgaGain = 21,
45    AntennaEnable = 23,
46    SetFreqExplicit = 24,
47    UsbWcidVendorReq = 25,
48    InitSweep = 26,
49    OperacakeGetBoards = 27,
50    OperacakeSetPorts = 28,
51    SetHwSyncMode = 29,
52    Reset = 30,
53    OperacakeSetRanges = 31,
54    ClkoutEnable = 32,
55    SpiflashStatus = 33,
56    SpiflashClearStatus = 34,
57    OperacakeGpioTest = 35,
58    CpldChecksum = 36,
59    UiEnable = 37,
60}
61
62impl From<Request> for u8 {
63    fn from(r: Request) -> Self {
64        r as u8
65    }
66}
67
68#[allow(dead_code)]
69#[repr(u8)]
70enum TranscieverMode {
71    Off = 0,
72    Receive = 1,
73    Transmit = 2,
74    Ss = 3,
75    CpldUpdate = 4,
76    RxSweep = 5,
77}
78
79impl From<TranscieverMode> for u8 {
80    fn from(tm: TranscieverMode) -> Self {
81        tm as u8
82    }
83}
84
85impl From<TranscieverMode> for u16 {
86    fn from(tm: TranscieverMode) -> Self {
87        tm as u16
88    }
89}
90
91/// HackRF One errors.
92#[derive(Debug, Copy, Clone, PartialEq, Eq)]
93pub enum Error {
94    /// USB error.
95    Usb(rusb::Error),
96    /// Failed to transfer all bytes in a control transfer.
97    CtrlTransfer {
98        /// Control transfer direction.
99        dir: Direction,
100        /// Actual amount of bytes transferred.
101        actual: usize,
102        /// Excepted number of bytes transferred.
103        expected: usize,
104    },
105    /// An API call is not supported by your hardware.
106    ///
107    /// Try updating the firmware on your device.
108    Version {
109        /// Current device version.
110        device: Version,
111        /// Minimum version required.
112        min: Version,
113    },
114    /// A provided argument was out of range.
115    Argument,
116}
117
118impl From<rusb::Error> for Error {
119    fn from(e: rusb::Error) -> Self {
120        Error::Usb(e)
121    }
122}
123
124impl std::fmt::Display for Error {
125    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126        write!(f, "{:?}", self)
127    }
128}
129
130impl std::error::Error for Error {}
131
132/// Typestate for RX mode.
133#[derive(Debug)]
134pub struct RxMode;
135
136/// Typestate for an unknown mode.
137#[derive(Debug)]
138pub struct UnknownMode;
139
140/// HackRF One software defined radio.
141pub struct HackRfOne<MODE> {
142    dh: rusb::DeviceHandle<GlobalContext>,
143    desc: rusb::DeviceDescriptor,
144    #[allow(dead_code)]
145    mode: MODE,
146    to: Duration,
147}
148
149impl HackRfOne<UnknownMode> {
150    /// Open a new HackRF One.
151    ///
152    /// # Example
153    ///
154    /// ```no_run
155    /// use hackrfone::{HackRfOne, UnknownMode};
156    ///
157    /// let mut radio: HackRfOne<UnknownMode> = HackRfOne::new().unwrap();
158    /// ```
159    pub fn new() -> Option<HackRfOne<UnknownMode>> {
160        let ctx: GlobalContext = GlobalContext {};
161        let devices = match ctx.devices() {
162            Ok(d) => d,
163            Err(_) => return None,
164        };
165
166        for device in devices.iter() {
167            let desc = match device.device_descriptor() {
168                Ok(d) => d,
169                Err(_) => continue,
170            };
171
172            if desc.vendor_id() == HACKRF_USB_VID && desc.product_id() == HACKRF_ONE_USB_PID {
173                match device.open() {
174                    Ok(handle) => {
175                        return Some(HackRfOne {
176                            dh: handle,
177                            desc,
178                            mode: UnknownMode,
179                            to: Duration::from_secs(1),
180                        })
181                    }
182                    Err(_) => continue,
183                }
184            }
185        }
186
187        None
188    }
189}
190
191impl<MODE> HackRfOne<MODE> {
192    fn read_control<const N: usize>(
193        &self,
194        request: Request,
195        value: u16,
196        index: u16,
197    ) -> Result<[u8; N], Error> {
198        let mut buf: [u8; N] = [0; N];
199        let n: usize = self.dh.read_control(
200            request_type(Direction::In, RequestType::Vendor, Recipient::Device),
201            request.into(),
202            value,
203            index,
204            &mut buf,
205            self.to,
206        )?;
207        if n != buf.len() {
208            Err(Error::CtrlTransfer {
209                dir: Direction::In,
210                actual: n,
211                expected: buf.len(),
212            })
213        } else {
214            Ok(buf)
215        }
216    }
217
218    fn write_control(
219        &mut self,
220        request: Request,
221        value: u16,
222        index: u16,
223        buf: &[u8],
224    ) -> Result<(), Error> {
225        let n: usize = self.dh.write_control(
226            request_type(Direction::Out, RequestType::Vendor, Recipient::Device),
227            request.into(),
228            value,
229            index,
230            &buf,
231            self.to,
232        )?;
233        if n != buf.len() {
234            Err(Error::CtrlTransfer {
235                dir: Direction::Out,
236                actual: n,
237                expected: buf.len(),
238            })
239        } else {
240            Ok(())
241        }
242    }
243
244    fn check_api_version(&self, min: Version) -> Result<(), Error> {
245        fn version_to_u32(v: Version) -> u32 {
246            ((v.major() as u32) << 16) | ((v.minor() as u32) << 8) | (v.sub_minor() as u32)
247        }
248
249        let v: Version = self.device_version();
250        let v_cmp: u32 = version_to_u32(v);
251        let min_cmp: u32 = version_to_u32(min);
252
253        if v_cmp >= min_cmp {
254            Ok(())
255        } else {
256            Err(Error::Version { device: v, min })
257        }
258    }
259
260    /// Get the device version from the USB descriptor.
261    ///
262    /// The HackRF C API calls the equivalent of this function
263    /// `hackrf_usb_api_version_read`.
264    ///
265    /// # Example
266    ///
267    /// ```no_run
268    /// use hackrfone::{rusb, HackRfOne, UnknownMode};
269    ///
270    /// let mut radio: HackRfOne<UnknownMode> = HackRfOne::new().unwrap();
271    /// assert_eq!(radio.device_version(), rusb::Version(1, 0, 4));
272    /// ```
273    pub fn device_version(&self) -> Version {
274        self.desc.device_version()
275    }
276
277    /// Set the timeout for USB transfers.
278    ///
279    /// # Example
280    ///
281    /// Set a 100ms timeout.
282    ///
283    /// ```no_run
284    /// use hackrfone::{HackRfOne, UnknownMode};
285    /// use std::time::Duration;
286    ///
287    /// let mut radio: HackRfOne<UnknownMode> = HackRfOne::new().unwrap();
288    /// radio.set_timeout(Duration::from_millis(100))
289    /// ```
290    pub fn set_timeout(&mut self, duration: Duration) {
291        self.to = duration;
292    }
293
294    /// Read the board ID.
295    ///
296    /// # Example
297    ///
298    /// ```no_run
299    /// use hackrfone::{HackRfOne, UnknownMode};
300    ///
301    /// let mut radio: HackRfOne<UnknownMode> = HackRfOne::new().unwrap();
302    /// assert_eq!(radio.board_id()?, 0x02);
303    /// # Ok::<(), hackrfone::Error>(())
304    /// ```
305    pub fn board_id(&self) -> Result<u8, Error> {
306        let data: [u8; 1] = self.read_control(Request::BoardIdRead, 0, 0)?;
307        Ok(data[0])
308    }
309
310    /// Read the firmware version.
311    ///
312    /// # Example
313    ///
314    /// ```no_run
315    /// use hackrfone::{HackRfOne, UnknownMode};
316    ///
317    /// let mut radio: HackRfOne<UnknownMode> = HackRfOne::new().unwrap();
318    /// assert_eq!(radio.version()?, "2021.03.1");
319    /// # Ok::<(), hackrfone::Error>(())
320    /// ```
321    pub fn version(&self) -> Result<String, Error> {
322        let mut buf: [u8; 16] = [0; 16];
323        let n: usize = self.dh.read_control(
324            request_type(Direction::In, RequestType::Vendor, Recipient::Device),
325            Request::VersionStringRead.into(),
326            0,
327            0,
328            &mut buf,
329            self.to,
330        )?;
331        Ok(String::from_utf8_lossy(&buf[0..n]).into())
332    }
333
334    /// Set the center frequency.
335    ///
336    /// # Example
337    ///
338    /// Set the frequency to 915MHz.
339    ///
340    /// ```no_run
341    /// use hackrfone::{HackRfOne, UnknownMode};
342    ///
343    /// let mut radio: HackRfOne<UnknownMode> = HackRfOne::new().unwrap();
344    /// radio.set_freq(915_000_000)?;
345    /// # Ok::<(), hackrfone::Error>(())
346    /// ```
347    pub fn set_freq(&mut self, hz: u64) -> Result<(), Error> {
348        let buf: [u8; 8] = freq_params(hz);
349        self.write_control(Request::SetFreq, 0, 0, &buf)
350    }
351
352    /// Enable the RX/TX RF amplifier.
353    ///
354    /// In GNU radio this is used as the RF gain, where a value of 0 dB is off,
355    /// and a value of 14 dB is on.
356    ///
357    /// # Example
358    ///
359    /// Disable the amplifier.
360    ///
361    /// ```no_run
362    /// use hackrfone::{HackRfOne, UnknownMode};
363    ///
364    /// let mut radio: HackRfOne<UnknownMode> = HackRfOne::new().unwrap();
365    /// radio.set_amp_enable(false)?;
366    /// # Ok::<(), hackrfone::Error>(())
367    /// ```
368    pub fn set_amp_enable(&mut self, en: bool) -> Result<(), Error> {
369        self.write_control(Request::AmpEnable, en.into(), 0, &[])
370    }
371
372    /// Set the baseband filter bandwidth.
373    ///
374    /// This is automatically set when the sample rate is changed with
375    /// [`set_sample_rate`].
376    ///
377    /// # Example
378    ///
379    /// Set the filter bandwidth to 70% of the sample rate.
380    ///
381    /// ```no_run
382    /// use hackrfone::{HackRfOne, UnknownMode};
383    ///
384    /// const SAMPLE_HZ: u32 = 20_000_000;
385    /// const SAMPLE_DIV: u32 = 2;
386    /// const FILTER_BW: u32 = (0.7 * (SAMPLE_HZ as f32) / (SAMPLE_DIV as f32)) as u32;
387    ///
388    /// let mut radio: HackRfOne<UnknownMode> = HackRfOne::new().unwrap();
389    /// radio.set_sample_rate(SAMPLE_HZ, SAMPLE_DIV)?;
390    /// radio.set_baseband_filter_bandwidth(FILTER_BW)?;
391    /// # Ok::<(), hackrfone::Error>(())
392    /// ```
393    ///
394    /// [`set_sample_rate`]: crate::HackRfOne::set_sample_rate
395    pub fn set_baseband_filter_bandwidth(&mut self, hz: u32) -> Result<(), Error> {
396        self.write_control(
397            Request::BasebandFilterBandwidthSet,
398            (hz & 0xFFFF) as u16,
399            (hz >> 16) as u16,
400            &[],
401        )
402    }
403
404    /// Set the sample rate.
405    ///
406    /// For anti-aliasing, the baseband filter bandwidth is automatically set to
407    /// the widest available setting that is no more than 75% of the sample rate.
408    /// This happens every time the sample rate is set.
409    /// If you want to override the baseband filter selection, you must do so
410    /// after setting the sample rate.
411    ///
412    /// Limits are 8MHz - 20MHz.
413    /// Preferred rates are 8, 10, 12.5, 16, 20MHz due to less jitter.
414    ///
415    /// # Example
416    ///
417    /// Set the sample rate to 10 MHz.
418    ///
419    /// ```no_run
420    /// use hackrfone::{HackRfOne, UnknownMode};
421    ///
422    /// let mut radio: HackRfOne<UnknownMode> = HackRfOne::new().unwrap();
423    /// radio.set_sample_rate(20_000_000, 2)?;
424    /// # Ok::<(), hackrfone::Error>(())
425    /// ```
426    pub fn set_sample_rate(&mut self, hz: u32, div: u32) -> Result<(), Error> {
427        let hz: u32 = hz.to_le();
428        let div: u32 = div.to_le();
429        let buf: [u8; 8] = [
430            (hz & 0xFF) as u8,
431            ((hz >> 8) & 0xFF) as u8,
432            ((hz >> 16) & 0xFF) as u8,
433            ((hz >> 24) & 0xFF) as u8,
434            (div & 0xFF) as u8,
435            ((div >> 8) & 0xFF) as u8,
436            ((div >> 16) & 0xFF) as u8,
437            ((div >> 24) & 0xFF) as u8,
438        ];
439        self.write_control(Request::SampleRateSet, 0, 0, &buf)?;
440        self.set_baseband_filter_bandwidth((0.75 * (hz as f32) / (div as f32)) as u32)
441    }
442
443    /// Set the LNA (low noise amplifier) gain.
444    ///
445    /// Range 0 to 40dB in 8dB steps.
446    ///
447    /// This is also known as the IF gain.
448    ///
449    /// # Example
450    ///
451    /// Set the LNA gain to 16 dB (generally a reasonable gain to start with).
452    ///
453    /// ```no_run
454    /// use hackrfone::{HackRfOne, UnknownMode};
455    ///
456    /// let mut radio: HackRfOne<UnknownMode> = HackRfOne::new().unwrap();
457    /// radio.set_lna_gain(16)?;
458    /// # Ok::<(), hackrfone::Error>(())
459    /// ```
460    pub fn set_lna_gain(&mut self, gain: u16) -> Result<(), Error> {
461        if gain > 40 {
462            Err(Error::Argument)
463        } else {
464            let buf: [u8; 1] = self.read_control(Request::SetLnaGain, 0, gain & !0x07)?;
465            if buf[0] == 0 {
466                Err(Error::Argument)
467            } else {
468                Ok(())
469            }
470        }
471    }
472
473    /// Set the VGA (variable gain amplifier) gain.
474    ///
475    /// Range 0 to 62dB in 2dB steps.
476    ///
477    /// This is also known as the baseband (BB) gain.
478    ///
479    /// # Example
480    ///
481    /// Set the VGA gain to 16 dB (generally a reasonable gain to start with).
482    ///
483    ///
484    /// ```no_run
485    /// use hackrfone::{HackRfOne, UnknownMode};
486    ///
487    /// let mut radio: HackRfOne<UnknownMode> = HackRfOne::new().unwrap();
488    /// radio.set_vga_gain(16)?;
489    /// # Ok::<(), hackrfone::Error>(())
490    /// ```
491    pub fn set_vga_gain(&mut self, gain: u16) -> Result<(), Error> {
492        if gain > 62 {
493            Err(Error::Argument)
494        } else {
495            let buf: [u8; 1] = self.read_control(Request::SetVgaGain, 0, gain & !0b1)?;
496            if buf[0] == 0 {
497                Err(Error::Argument)
498            } else {
499                Ok(())
500            }
501        }
502    }
503
504    /// Set the transmit VGA gain.
505    ///
506    /// Range 0 to 47dB in 1db steps.
507    pub fn set_txvga_gain(&mut self, gain: u16) -> Result<(), Error> {
508        if gain > 47 {
509            Err(Error::Argument)
510        } else {
511            let buf: [u8; 1] = self.read_control(Request::SetTxvgaGain, 0, gain)?;
512            if buf[0] == 0 {
513                Err(Error::Argument)
514            } else {
515                Ok(())
516            }
517        }
518    }
519
520    /// Antenna power port control.
521    ///
522    /// The source docs are a little lacking in terms of explanations here.
523    pub fn set_antenna_enable(&mut self, value: u8) -> Result<(), Error> {
524        self.write_control(Request::AntennaEnable, value.into(), 0, &[])
525    }
526
527    /// CLKOUT enable.
528    ///
529    /// The source docs are a little lacking in terms of explanations here.
530    pub fn set_clkout_enable(&mut self, en: bool) -> Result<(), Error> {
531        self.check_api_version(Version::from_bcd(0x0103))?;
532        self.write_control(Request::ClkoutEnable, en.into(), 0, &[])
533    }
534
535    /// Reset the HackRF radio.
536    ///
537    /// # Example
538    ///
539    /// ```no_run
540    /// use hackrfone::{HackRfOne, UnknownMode};
541    ///
542    /// let mut radio: HackRfOne<UnknownMode> = HackRfOne::new().unwrap();
543    /// let mut radio: HackRfOne<UnknownMode> = radio.reset()?;
544    /// # Ok::<(), hackrfone::Error>(())
545    /// ```
546    pub fn reset(mut self) -> Result<HackRfOne<UnknownMode>, Error> {
547        self.check_api_version(Version::from_bcd(0x0102))?;
548        self.write_control(Request::Reset, 0, 0, &[])?;
549        Ok(HackRfOne {
550            dh: self.dh,
551            desc: self.desc,
552            mode: UnknownMode,
553            to: self.to,
554        })
555    }
556
557    fn set_transceiver_mode(&mut self, mode: TranscieverMode) -> Result<(), Error> {
558        self.write_control(Request::SetTransceiverMode, mode.into(), 0, &[])
559    }
560
561    /// Change the radio mode to RX.
562    ///
563    /// # Example
564    ///
565    /// ```no_run
566    /// use hackrfone::{HackRfOne, RxMode, UnknownMode};
567    ///
568    /// let mut radio: HackRfOne<UnknownMode> = HackRfOne::new().unwrap();
569    /// let mut radio: HackRfOne<RxMode> = radio.into_rx_mode()?;
570    /// # Ok::<(), hackrfone::Error>(())
571    /// ```
572    pub fn into_rx_mode(mut self) -> Result<HackRfOne<RxMode>, Error> {
573        self.set_transceiver_mode(TranscieverMode::Receive)?;
574        self.dh.claim_interface(0)?;
575        Ok(HackRfOne {
576            dh: self.dh,
577            desc: self.desc,
578            mode: RxMode,
579            to: self.to,
580        })
581    }
582}
583
584impl HackRfOne<RxMode> {
585    /// Receive data from the radio.
586    ///
587    /// This uses a bulk transfer to get one MTU (maximum transmission unit)
588    /// of data in a single shot.  The data format is pairs of signed 8-bit IQ.
589    /// Use the [`iq_to_cplx_i8`] or [`iq_to_cplx_f32`] helpers to convert the
590    /// data to a more manageable format.
591    ///
592    /// Unlike `libhackrf` this does not spawn a sampling thread.
593    ///
594    /// # Example
595    ///
596    /// ```no_run
597    /// use hackrfone::{HackRfOne, RxMode, UnknownMode};
598    ///
599    /// let mut radio: HackRfOne<UnknownMode> = HackRfOne::new().unwrap();
600    /// let mut radio: HackRfOne<RxMode> = radio.into_rx_mode()?;
601    /// let data: Vec<u8> = radio.rx()?;
602    /// radio.stop_rx()?;
603    /// # Ok::<(), hackrfone::Error>(())
604    /// ```
605    ///
606    /// [`iq_to_cplx_i8`]: crate::iq_to_cplx_i8
607    /// [`iq_to_cplx_f32`]: crate::iq_to_cplx_f32
608    pub fn rx(&mut self) -> Result<Vec<u8>, Error> {
609        const ENDPOINT: u8 = 0x81;
610        const MTU: usize = 128 * 1024;
611        let mut buf: Vec<u8> = vec![0; MTU];
612        let n: usize = self.dh.read_bulk(ENDPOINT, &mut buf, self.to)?;
613        buf.truncate(n);
614        Ok(buf)
615    }
616
617    /// Stop receiving.
618    ///
619    /// # Example
620    ///
621    /// ```no_run
622    /// use hackrfone::{HackRfOne, RxMode, UnknownMode};
623    ///
624    /// let mut radio: HackRfOne<UnknownMode> = HackRfOne::new().unwrap();
625    /// let mut radio: HackRfOne<RxMode> = radio.into_rx_mode()?;
626    /// let data: Vec<u8> = radio.rx()?;
627    /// radio.stop_rx()?;
628    /// # Ok::<(), hackrfone::Error>(())
629    /// ```
630    pub fn stop_rx(mut self) -> Result<HackRfOne<UnknownMode>, Error> {
631        self.dh.release_interface(0)?;
632        self.set_transceiver_mode(TranscieverMode::Off)?;
633        Ok(HackRfOne {
634            dh: self.dh,
635            desc: self.desc,
636            mode: UnknownMode,
637            to: self.to,
638        })
639    }
640}
641
642// Helper for set_freq
643fn freq_params(hz: u64) -> [u8; 8] {
644    const MHZ: u64 = 1_000_000;
645
646    let l_freq_mhz: u32 = u32::try_from(hz / MHZ).unwrap_or(u32::MAX).to_le();
647    let l_freq_hz: u32 = u32::try_from(hz - u64::from(l_freq_mhz) * MHZ)
648        .unwrap_or(u32::MAX)
649        .to_le();
650
651    [
652        (l_freq_mhz & 0xFF) as u8,
653        ((l_freq_mhz >> 8) & 0xFF) as u8,
654        ((l_freq_mhz >> 16) & 0xFF) as u8,
655        ((l_freq_mhz >> 24) & 0xFF) as u8,
656        (l_freq_hz & 0xFF) as u8,
657        ((l_freq_hz >> 8) & 0xFF) as u8,
658        ((l_freq_hz >> 16) & 0xFF) as u8,
659        ((l_freq_hz >> 24) & 0xFF) as u8,
660    ]
661}
662
663#[cfg(test)]
664mod freq_params {
665    use super::freq_params;
666
667    #[test]
668    fn nominal() {
669        assert_eq!(freq_params(915_000_000), [0x93, 0x03, 0, 0, 0, 0, 0, 0]);
670        assert_eq!(freq_params(915_000_001), [0x93, 0x03, 0, 0, 1, 0, 0, 0]);
671        assert_eq!(
672            freq_params(123456789),
673            [0x7B, 0, 0, 0, 0x55, 0xF8, 0x06, 0x00]
674        );
675    }
676
677    #[test]
678    fn min() {
679        assert_eq!(freq_params(0), [0; 8]);
680    }
681
682    #[test]
683    fn max() {
684        assert_eq!(freq_params(u64::MAX), [0xFF; 8]);
685    }
686}
687
688/// Convert an IQ sample pair to a complex number.
689///
690/// # Example
691///
692/// Post-processing sample data.
693///
694/// ```no_run
695/// use hackrfone::{iq_to_cplx_i8, HackRfOne, RxMode, UnknownMode};
696///
697/// let mut radio: HackRfOne<UnknownMode> = HackRfOne::new().unwrap();
698/// let mut radio: HackRfOne<RxMode> = radio.into_rx_mode()?;
699/// let data: Vec<u8> = radio.rx()?;
700/// radio.stop_rx()?;
701///
702/// for iq in data.chunks_exact(2) {
703///     let cplx: num_complex::Complex<i8> = iq_to_cplx_i8(iq[0], iq[1]);
704///     // .. do whatever you want with cplx here
705/// }
706///
707/// # Ok::<(), hackrfone::Error>(())
708/// ```
709///
710/// Guide level explanation.
711///
712/// ```
713/// use hackrfone::iq_to_cplx_i8;
714/// use num_complex::Complex;
715///
716/// assert_eq!(iq_to_cplx_i8(255, 1), Complex::new(-1, 1));
717/// ```
718#[cfg(feature = "num-complex")]
719#[cfg_attr(docsrs, doc(cfg(feature = "num-complex")))]
720pub fn iq_to_cplx_i8(i: u8, q: u8) -> num_complex::Complex<i8> {
721    num_complex::Complex::new(i as i8, q as i8)
722}
723
724/// Convert an IQ sample pair to a floating point complex number.
725///
726/// Generally you will want to use [`iq_to_cplx_i8`] for storing or transfering
727/// data because the samples are 2-bytes in the native i8, vs 8-bytes in f32.
728///
729/// Floats are easier to work with for running samples through digital signal
730/// processing algorithms (e.g. discrete fourier transform) where the i8 can
731/// easily saturate.
732///
733/// # Example
734///
735/// Post-processing sample data.
736///
737/// ```no_run
738/// use hackrfone::{iq_to_cplx_f32, HackRfOne, RxMode, UnknownMode};
739///
740/// let mut radio: HackRfOne<UnknownMode> = HackRfOne::new().unwrap();
741/// let mut radio: HackRfOne<RxMode> = radio.into_rx_mode()?;
742/// let data: Vec<u8> = radio.rx()?;
743/// radio.stop_rx()?;
744///
745/// for iq in data.chunks_exact(2) {
746///     let cplx: num_complex::Complex<f32> = iq_to_cplx_f32(iq[0], iq[1]);
747///     // .. do whatever you want with cplx here
748/// }
749///
750/// # Ok::<(), hackrfone::Error>(())
751/// ```
752///
753/// Guide level explanation.
754///
755/// ```
756/// use hackrfone::iq_to_cplx_f32;
757/// use num_complex::Complex;
758///
759/// assert_eq!(iq_to_cplx_f32(255, 1), Complex::new(-1.0, 1.0));
760/// ```
761#[cfg(feature = "num-complex")]
762#[cfg_attr(docsrs, doc(cfg(feature = "num-complex")))]
763pub fn iq_to_cplx_f32(i: u8, q: u8) -> num_complex::Complex<f32> {
764    num_complex::Complex::new(i as i8 as f32, q as i8 as f32)
765}