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}