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}