hackrfone/
lib.rs

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