waverave_hackrf/
lib.rs

1/*!
2
3This is a complete, strongly-asynchronous host crate for the [HackRF][hackrf],
4made using the pure-rust [`nusb`] crate for USB interfacing. It reproduces *all*
5the functionality of the original [`libhackrf`][libhackrf] library.
6
7[hackrf]: https://greatscottgadgets.com/hackrf/one/
8[libhackrf]: https://github.com/greatscottgadgets/hackrf/tree/master/host
9
10The standard entry point for this library is [`open_hackrf()`], which will open
11the first available HackRF device.
12
13Getting started is easy: open up a HackRF peripheral, configure it as needed,
14and enter into transmit, receive, or RX sweep mode. Changing the operating mode
15also changes the struct used, i.e. it uses the typestate pattern. The different
16states and their corresponding structs are:
17
18- [`HackRf`] - The default, off, state.
19- [`Receive`] - Receiving RF signals.
20- [`Transmit`] - Transmitting RF signals.
21- [`Sweep`] - Running a receive sweep through multiple tuning frequencies.
22
23If a mode change error occurs, the [`HackRf`] struct is returned alongside the
24error, and it can potentially be reset back to the off state by running
25[`HackRf::turn_off`].
26
27As for what using this library looks like in practice, here's an example program
28that configures the system, enters receive mode, and processes samples to
29estimate the average received power relative to full scale:
30
31```no_run
32use anyhow::Result;
33#[tokio::main]
34async fn main() -> Result<()> {
35    let hackrf = waverave_hackrf::open_hackrf()?;
36
37    // Configure: 20MHz sample rate, turn on the RF amp, set IF & BB gains to 16 dB,
38    // and tune to 915 MHz.
39    hackrf.set_sample_rate(20e6).await?;
40    hackrf.set_amp_enable(true).await?;
41    hackrf.set_lna_gain(16).await?;
42    hackrf.set_vga_gain(16).await?;
43    hackrf.set_freq(915_000_000).await?;
44
45    // Start receiving, in bursts of 16384 samples
46    let mut hackrf_rx = hackrf.start_rx(16384).await.map_err(|e| e.err)?;
47
48    // Queue up 64 transfers, retrieve them, and measure average power.
49    for _ in 0..64 {
50        hackrf_rx.submit();
51    }
52    let mut count = 0;
53    let mut pow_sum = 0.0;
54    while hackrf_rx.pending() > 0 {
55        let buf = hackrf_rx.next_complete().await?;
56        for x in buf.samples() {
57            let re = x.re as f64;
58            let im = x.im as f64;
59            pow_sum += re * re + im * im;
60        }
61        count += buf.len();
62    }
63
64    // Stop receiving
65    hackrf_rx.stop().await?;
66
67    // Print out our measurement
68    let average_power = (pow_sum / (count as f64 * 127.0 * 127.0)).log10() * 10.;
69    println!("Average Power = {average_power} dbFS");
70    Ok(())
71}
72
73```
74
75*/
76
77#![warn(missing_docs)]
78
79mod consts;
80pub mod debug;
81mod error;
82pub mod info;
83mod rx;
84mod sweep;
85mod tx;
86use std::ops::Range;
87
88use bytemuck::Pod;
89use core::mem::size_of;
90use nusb::transfer::{ControlIn, ControlOut, ControlType, Recipient};
91
92use crate::consts::*;
93use crate::debug::Debug;
94use crate::info::Info;
95
96pub use crate::error::{Error, StateChangeError};
97pub use crate::rx::Receive;
98pub use crate::sweep::{Sweep, SweepBuf, SweepMode, SweepParams};
99pub use crate::tx::Transmit;
100
101/// Complex 8-bit signed data, as used by the HackRF.
102pub type ComplexI8 = num_complex::Complex<i8>;
103
104/// Operacake port A1
105pub const PORT_A1: u8 = 0;
106/// Operacake port A2
107pub const PORT_A2: u8 = 1;
108/// Operacake port A3
109pub const PORT_A3: u8 = 2;
110/// Operacake port A4
111pub const PORT_A4: u8 = 3;
112/// Operacake port B1
113pub const PORT_B1: u8 = 4;
114/// Operacake port B2
115pub const PORT_B2: u8 = 5;
116/// Operacake port B3
117pub const PORT_B3: u8 = 6;
118/// Operacake port B4
119pub const PORT_B4: u8 = 7;
120
121/// A Buffer holding HackRF transfer data.
122///
123/// Samples can be directly accessed as slices, and can be extended up to the
124/// length of the fixed-size underlying buffer.
125///
126/// When dropped, this buffer returns to the internal buffer pool it came from.
127/// It can either be backed by an allocation from the system allocator, or by
128/// some platform-specific way of allocating memory for zero-copy USB transfers.
129pub struct Buffer {
130    buf: Vec<u8>,
131    pool: crossbeam_channel::Sender<Vec<u8>>,
132}
133
134impl Buffer {
135    pub(crate) fn new(buf: Vec<u8>, pool: crossbeam_channel::Sender<Vec<u8>>) -> Self {
136        assert!(buf.len() & 0x1FF == 0);
137        Self { buf, pool }
138    }
139
140    pub(crate) fn into_vec(mut self) -> Vec<u8> {
141        core::mem::take(&mut self.buf)
142    }
143
144    /// Get how many samples this buffer can hold.
145    pub fn capacity(&self) -> usize {
146        // Force down to the nearest 512-byte boundary, which is the transfer
147        // size the HackRF requires.
148        (self.buf.capacity() & !0x1FF) / size_of::<ComplexI8>()
149    }
150
151    /// Clear out the buffer's samples.
152    pub fn clear(&mut self) {
153        self.buf.clear();
154    }
155
156    /// Size of the buffer, in samples.
157    pub fn len(&self) -> usize {
158        self.buf.len() / size_of::<ComplexI8>()
159    }
160
161    /// Returns true if there are no samples in the buffer.
162    pub fn is_empty(&self) -> bool {
163        self.buf.is_empty()
164    }
165
166    /// Remaining capacity in the buffer, in samples.
167    pub fn remaining_capacity(&self) -> usize {
168        self.capacity() - self.len()
169    }
170
171    /// Extend the buffer with some number of samples set to 0, and get a
172    /// mutable slice to the newly initialized samples.
173    ///
174    /// # Panics
175    /// - If there is not enough space left for the added samples.
176    pub fn extend_zeros(&mut self, len: usize) -> &mut [ComplexI8] {
177        assert!(self.remaining_capacity() >= len);
178        let old_len = self.buf.len();
179        let new_len = old_len + len * size_of::<ComplexI8>();
180        self.buf.resize(new_len, 0);
181        let buf: &mut [u8] = &mut self.buf;
182        // SAFETY: We only ever resize according to the size of a ComplexI8,
183        // the buffer always holds ComplexI8 internally, and ComplexI8 has an
184        // alignment of 1.
185        unsafe {
186            core::slice::from_raw_parts_mut(
187                buf.as_mut_ptr().add(old_len) as *mut ComplexI8,
188                len / size_of::<ComplexI8>(),
189            )
190        }
191    }
192
193    /// Extend the buffer with a slice of samples.
194    ///
195    /// # Panics
196    /// - If there is no space left in the buffer for the slice.
197    pub fn extend_from_slice(&mut self, slice: &[ComplexI8]) {
198        assert!(self.remaining_capacity() >= slice.len());
199        // SAFETY: We can always cast a ComplexI8 to bytes, as it meets all the
200        // "plain old data" requirements.
201        let slice = unsafe {
202            core::slice::from_raw_parts(slice.as_ptr() as *const u8, core::mem::size_of_val(slice))
203        };
204        self.buf.extend_from_slice(slice);
205    }
206
207    /// Push a value onto the buffer.
208    ///
209    /// # Panics
210    /// - If there is no space left in the buffer.
211    pub fn push(&mut self, val: ComplexI8) {
212        assert!(self.remaining_capacity() > 0);
213        let slice: &[u8; 2] = unsafe { &*((&val) as *const ComplexI8 as *const [u8; 2]) };
214        self.buf.extend_from_slice(slice);
215    }
216
217    /// Get the sample sequence as a slice of bytes instead of complex values.
218    pub fn bytes(&self) -> &[u8] {
219        &self.buf
220    }
221
222    /// Get the sample sequence as a mutable slice of bytes instead of complex values.
223    pub fn bytes_mut(&mut self) -> &mut [u8] {
224        &mut self.buf
225    }
226
227    /// Get the samples in the buffer.
228    pub fn samples(&self) -> &[ComplexI8] {
229        let buf: &[u8] = &self.buf;
230        // SAFETY: the buffer is aligned because `ComplexI8` has an alignment of
231        // 1, same as a byte buffer, the data is valid, and we truncate to only
232        // valid populated pairs. Also we shouldn't ever have a byte buffer that
233        // isn't an even number of bytes anyway...
234        unsafe {
235            core::slice::from_raw_parts(
236                buf.as_ptr() as *const ComplexI8,
237                self.buf.len() / size_of::<ComplexI8>(),
238            )
239        }
240    }
241
242    /// Mutably get the samples in the buffer.
243    pub fn samples_mut(&mut self) -> &mut [ComplexI8] {
244        let buf: &mut [u8] = &mut self.buf;
245        // SAFETY: the buffer is aligned because `ComplexI8` has an alignment of
246        // 1, same as a byte buffer, the data is valid, and we truncate to only
247        // valid populated pairs. Also we shouldn't ever have a byte buffer that
248        // isn't an even number of bytes anyway...
249        unsafe {
250            core::slice::from_raw_parts_mut(
251                buf.as_mut_ptr() as *mut ComplexI8,
252                self.buf.len() / size_of::<ComplexI8>(),
253            )
254        }
255    }
256}
257
258impl Drop for Buffer {
259    fn drop(&mut self) {
260        let inner = core::mem::take(&mut self.buf);
261        if inner.capacity() > 0 {
262            let _ = self.pool.send(inner);
263        }
264    }
265}
266
267/// Configuration settings for the Bias-T switch.
268///
269/// Used when calling [`HackRf::set_user_bias_t_opts`].
270#[derive(Clone, Debug)]
271pub struct BiasTSetting {
272    /// What mode change to apply when switching to transmit.
273    pub tx: BiasTMode,
274    /// What mode change to apply when switching to receive.
275    pub rx: BiasTMode,
276    /// What mode change to apply when switching off.
277    pub off: BiasTMode,
278}
279
280/// A Bias-T setting change to apply on a mode change.
281///
282/// See [`BiasTSetting`] for where to use this.
283#[allow(missing_docs)]
284#[derive(Clone, Copy, Debug, PartialEq, Eq)]
285pub enum BiasTMode {
286    NoChange,
287    Enable,
288    Disable,
289}
290
291impl BiasTMode {
292    fn as_u16(self) -> u16 {
293        match self {
294            Self::NoChange => 0x0,
295            Self::Disable => 0x2,
296            Self::Enable => 0x3,
297        }
298    }
299}
300
301/// RF Filter Setting Option.
302///
303/// Use when calling [`HackRf::set_freq_explicit`].
304#[repr(u8)]
305#[derive(Clone, Copy, Debug, PartialEq, Eq)]
306pub enum RfPathFilter {
307    /// No filter selected - mixer bypassed.
308    Bypass = 0,
309    /// Low pass filter, `f_c = f_IF - f_LO`
310    LowPass = 1,
311    /// High pass filter, `f_c = f_IF + f_LO`
312    HighPass = 2,
313}
314
315impl std::fmt::Display for RfPathFilter {
316    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
317        match self {
318            Self::Bypass => f.write_str("mixer bypass"),
319            Self::LowPass => f.write_str("low pass filter"),
320            Self::HighPass => f.write_str("high pass filter"),
321        }
322    }
323}
324
325/// Configuration for an Operacake board.
326///
327/// An Operacake board has three different operating modes:
328///
329/// - Manual: the switches are manually set and don't change until the next
330///   configuration operation.
331/// - Frequency: the switches change depending on the center frequency the board
332///   is tuned to.
333/// - Time: the switches change after some number of samples have been
334///   sent/received.
335///
336/// Use when calling [`HackRf::operacake_set_mode`].
337#[derive(Clone, Copy, Debug, PartialEq, Eq)]
338#[repr(u16)]
339#[allow(missing_docs)]
340pub enum OperacakeMode {
341    Manual = 0,
342    Frequency = 1,
343    Time = 2,
344}
345
346/// A Frequency band allocated to a specific port for all Operacakes operating
347/// in frequency mode.
348///
349/// This is used in [`HackRf::operacake_config_freq`].
350///
351/// Ports are zero-indexed, but can also be referred to with the top-level
352/// constants:
353/// - PORT_A1 = 0
354/// - PORT_A2 = 1
355/// - PORT_A3 = 2
356/// - PORT_A4 = 3
357/// - PORT_B1 = 4
358/// - PORT_B2 = 5
359/// - PORT_B3 = 6
360/// - PORT_B4 = 7
361#[derive(Clone, Copy, Debug)]
362pub struct OperacakeFreq {
363    /// Start frequency, in MHz.
364    pub min: u16,
365    /// Stop frequency, in MHz.
366    pub max: u16,
367    /// Port for A0 to use for the range. B0 will use the mirror image.
368    pub port: u8,
369}
370
371/// A dwell time allocated to a specific port for all Operacakes operating in
372/// dwell time mode.
373///
374/// This is used in [`HackRf::operacake_config_time`].
375///
376/// Ports are zero-indexed, but can also be referred to with the top-level
377/// constants:
378/// - PORT_A1 = 0
379/// - PORT_A2 = 1
380/// - PORT_A3 = 2
381/// - PORT_A4 = 3
382/// - PORT_B1 = 4
383/// - PORT_B2 = 5
384/// - PORT_B3 = 6
385/// - PORT_B4 = 7
386#[derive(Clone, Copy, Debug)]
387pub struct OperacakeDwell {
388    /// Dwell time, in number of samples
389    pub dwell: u32,
390    /// Port for A0 to use for the range. B0 will use the mirror image.
391    pub port: u8,
392}
393
394/// A HackRF device descriptor, which can be opened.
395///
396/// These are mostly returned from calling [`list_hackrf_devices`], but can also
397/// be formed by trying to convert a [`nusb::DeviceInfo`] into one.
398pub struct HackRfDescriptor {
399    info: nusb::DeviceInfo,
400}
401
402/// The type of HackRF device that was detected.
403#[allow(missing_docs)]
404#[derive(Clone, Copy, Debug, PartialEq, Eq)]
405pub enum HackRfType {
406    Jawbreaker,
407    One,
408    Rad1o,
409}
410
411impl std::fmt::Display for HackRfType {
412    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
413        match self {
414            Self::Jawbreaker => f.write_str("Jawbreaker"),
415            Self::One => f.write_str("HackRF One"),
416            Self::Rad1o => f.write_str("rad1o"),
417        }
418    }
419}
420
421impl HackRfDescriptor {
422    /// Get the serial number of this HackRF, as a string.
423    pub fn serial(&self) -> Option<&str> {
424        self.info.serial_number()
425    }
426
427    /// Get the [type][HackRfType] of HackRF radio this is.
428    pub fn radio_type(&self) -> HackRfType {
429        match self.info.product_id() {
430            HACKRF_JAWBREAKER_USB_PID => HackRfType::Jawbreaker,
431            HACKRF_ONE_USB_PID => HackRfType::One,
432            RAD1O_USB_PID => HackRfType::Rad1o,
433            _ => panic!("Created a HackRfDescriptor without using a known product ID"),
434        }
435    }
436
437    /// Try and open this HackRf device descriptor.
438    pub fn open(self) -> Result<HackRf, std::io::Error> {
439        let version = self.info.device_version();
440        let ty = self.radio_type();
441        let device = self.info.open()?;
442        #[cfg(not(target_os = "windows"))]
443        {
444            if device.active_configuration()?.configuration_value() != 1 {
445                device.detach_kernel_driver(0)?;
446                device.set_configuration(1)?;
447            }
448        }
449        let interface = device.detach_and_claim_interface(0)?;
450
451        let (buf_pool_send, buf_pool) = crossbeam_channel::unbounded();
452        let tx = TxEndpoint {
453            queue: interface.bulk_out_queue(TX_ENDPOINT_ADDRESS),
454            buf_pool,
455            buf_pool_send,
456        };
457        let (buf_pool_send, buf_pool) = crossbeam_channel::unbounded();
458        let rx = RxEndpoint {
459            queue: interface.bulk_in_queue(RX_ENDPOINT_ADDRESS),
460            buf_pool,
461            buf_pool_send,
462        };
463
464        Ok(HackRf {
465            interface,
466            version,
467            ty,
468            rx,
469            tx,
470        })
471    }
472}
473
474/// Try and turn any [`nusb::DeviceInfo`] descriptor into a HackRF, failing if
475/// the VID and PID don't match any known devices.
476impl TryFrom<nusb::DeviceInfo> for HackRfDescriptor {
477    type Error = &'static str;
478    fn try_from(value: nusb::DeviceInfo) -> Result<Self, Self::Error> {
479        if value.vendor_id() == HACKRF_USB_VID {
480            if matches!(
481                value.product_id(),
482                HACKRF_JAWBREAKER_USB_PID | HACKRF_ONE_USB_PID | RAD1O_USB_PID
483            ) {
484                Ok(HackRfDescriptor { info: value })
485            } else {
486                Err("VID recognized, PID not recognized")
487            }
488        } else {
489            Err("VID doesn't match for HackRF")
490        }
491    }
492}
493
494/// List all available HackRF devices.
495pub fn list_hackrf_devices() -> Result<Vec<HackRfDescriptor>, std::io::Error> {
496    Ok(nusb::list_devices()?
497        .filter(|d| {
498            d.vendor_id() == HACKRF_USB_VID
499                && matches!(
500                    d.product_id(),
501                    HACKRF_JAWBREAKER_USB_PID | HACKRF_ONE_USB_PID | RAD1O_USB_PID
502                )
503        })
504        .map(|d| HackRfDescriptor { info: d })
505        .collect::<Vec<HackRfDescriptor>>())
506}
507
508/// Open the first detected HackRF device in the system.
509///
510/// This is a shortcut for calling [`list_hackrf_devices`] and opening the first one.
511pub fn open_hackrf() -> Result<HackRf, std::io::Error> {
512    list_hackrf_devices()?
513        .into_iter()
514        .next()
515        .ok_or_else(|| std::io::Error::other("No HackRF devices"))?
516        .open()
517}
518
519/// A HackRF device. This is the main struct for talking to the HackRF.
520///
521/// This provides all the settings to actively configure the HackRF while it is
522/// off, as well as the ability to use debug or info fetching operations with
523/// the [`HackRf::info`] and [`HackRf::debug`] functions. Some of these
524/// operations are also exposed while receiving & transmitting, if it makes
525/// sense to do so.
526pub struct HackRf {
527    pub(crate) interface: nusb::Interface,
528    pub(crate) version: u16,
529    pub(crate) ty: HackRfType,
530    pub(crate) rx: RxEndpoint,
531    pub(crate) tx: TxEndpoint,
532}
533
534struct RxEndpoint {
535    queue: nusb::transfer::Queue<nusb::transfer::RequestBuffer>,
536    buf_pool: crossbeam_channel::Receiver<Vec<u8>>,
537    buf_pool_send: crossbeam_channel::Sender<Vec<u8>>,
538}
539
540struct TxEndpoint {
541    queue: nusb::transfer::Queue<Vec<u8>>,
542    buf_pool: crossbeam_channel::Receiver<Vec<u8>>,
543    buf_pool_send: crossbeam_channel::Sender<Vec<u8>>,
544}
545
546impl HackRf {
547    fn api_check(&self, needed: u16) -> Result<(), Error> {
548        if self.version < needed {
549            Err(Error::ApiVersion {
550                needed,
551                actual: self.version,
552            })
553        } else {
554            Ok(())
555        }
556    }
557
558    async fn write_u32(&self, req: ControlRequest, val: u32) -> Result<(), Error> {
559        Ok(self
560            .interface
561            .control_out(ControlOut {
562                control_type: ControlType::Vendor,
563                recipient: Recipient::Device,
564                request: req as u8,
565                value: (val & 0xffff) as u16,
566                index: (val >> 16) as u16,
567                data: &[],
568            })
569            .await
570            .status?)
571    }
572
573    async fn write_u16(&self, req: ControlRequest, idx: u16, val: u16) -> Result<(), Error> {
574        Ok(self
575            .interface
576            .control_out(ControlOut {
577                control_type: ControlType::Vendor,
578                recipient: Recipient::Device,
579                request: req as u8,
580                value: val,
581                index: idx,
582                data: &[],
583            })
584            .await
585            .status?)
586    }
587
588    async fn read_u16(&self, req: ControlRequest, idx: u16) -> Result<u16, Error> {
589        let ret = self
590            .interface
591            .control_in(ControlIn {
592                control_type: ControlType::Vendor,
593                recipient: Recipient::Device,
594                request: req as u8,
595                value: 0,
596                index: idx,
597                length: 2,
598            })
599            .await
600            .into_result()?;
601        let ret: [u8; 2] = ret.as_slice().try_into().map_err(|_| Error::ReturnData)?;
602        Ok(u16::from_le_bytes(ret))
603    }
604
605    async fn write_u8(&self, req: ControlRequest, idx: u16, val: u8) -> Result<(), Error> {
606        self.write_u16(req, idx, val as u16).await?;
607        Ok(())
608    }
609
610    async fn read_u8(&self, req: ControlRequest, idx: u16) -> Result<u8, Error> {
611        let ret = self
612            .interface
613            .control_in(ControlIn {
614                control_type: ControlType::Vendor,
615                recipient: Recipient::Device,
616                request: req as u8,
617                value: 0,
618                index: idx,
619                length: 1,
620            })
621            .await
622            .into_result()?;
623        ret.first().copied().ok_or(Error::ReturnData)
624    }
625
626    async fn write_bytes(&self, req: ControlRequest, data: &[u8]) -> Result<(), Error> {
627        self.interface
628            .control_out(ControlOut {
629                control_type: ControlType::Vendor,
630                recipient: Recipient::Device,
631                request: req as u8,
632                value: 0,
633                index: 0,
634                data,
635            })
636            .await
637            .into_result()?;
638        Ok(())
639    }
640
641    async fn read_bytes(&self, req: ControlRequest, len: usize) -> Result<Vec<u8>, Error> {
642        assert!(len < u16::MAX as usize);
643        Ok(self
644            .interface
645            .control_in(ControlIn {
646                control_type: ControlType::Vendor,
647                recipient: Recipient::Device,
648                request: req as u8,
649                value: 0,
650                index: 0,
651                length: len as u16,
652            })
653            .await
654            .into_result()?)
655    }
656
657    async fn read_struct<T>(&self, req: ControlRequest) -> Result<T, Error>
658    where
659        T: Pod,
660    {
661        let size = size_of::<T>();
662        let mut resp = self.read_bytes(req, size).await?;
663        if resp.len() < size {
664            return Err(Error::ReturnData);
665        }
666        resp.truncate(size);
667        Ok(bytemuck::pod_read_unaligned(&resp))
668    }
669
670    async fn set_transceiver_mode(&self, mode: TransceiverMode) -> Result<(), Error> {
671        self.write_u16(ControlRequest::SetTransceiverMode, 0, mode as u16)
672            .await
673    }
674
675    /// Set the baseband filter bandwidth.
676    ///
677    /// The possible settings are: 1.75, 2.5, 3.5, 5, 5.5, 6, 7, 8, 9, 10, 12,
678    /// 14, 15, 20, 24, and 28 MHz. This function will choose the nearest,
679    /// rounded down.
680    ///
681    /// The default is to set this to 3/4 of the sample rate, rounded down to
682    /// the nearest setting.
683    ///
684    /// Setting the sample rate with [`set_sample_rate`][Self::set_sample_rate]
685    /// will modify this setting.
686    pub async fn set_baseband_filter_bandwidth(&self, bandwidth_hz: u32) -> Result<(), Error> {
687        let bandwidth_hz = baseband_filter_bw(bandwidth_hz);
688        self.write_u32(ControlRequest::BasebandFilterBandwidthSet, bandwidth_hz)
689            .await
690    }
691
692    /// Set the transmit underrun limit. This will cause the HackRF to stop
693    /// operation if transmit runs out of samples to send. Set to 0 to disable.
694    ///
695    /// This will also cause all outstanding transmits to stall forever, so some
696    /// timeout will need to be added to the transmit completion futures.
697    pub async fn set_tx_underrun_limit(&self, val: u32) -> Result<(), Error> {
698        self.api_check(0x0106)?;
699        self.write_u32(ControlRequest::SetTxUnderrunLimit, val)
700            .await
701    }
702
703    /// Set the receive overrun limit. This will cause the HackRF to stop
704    /// operation if more than the specified amount of samples get lost. Set to
705    /// 0 to disable.
706    ///
707    /// This will also cause all outstanding receives to stall forever, so some
708    /// timeout will need to be added to the receive completion futures.
709    pub async fn set_rx_overrun_limit(&self, val: u32) -> Result<(), Error> {
710        self.api_check(0x0106)?;
711        self.write_u32(ControlRequest::SetRxOverrunLimit, val).await
712    }
713
714    /// Access the debug/programming commands for the HackRF.
715    pub fn debug(&mut self) -> Debug<'_> {
716        Debug::new(self)
717    }
718
719    /// Access the info commands for the HackRF.
720    pub fn info(&self) -> Info<'_> {
721        Info::new(self)
722    }
723
724    /// Set the operating frequency (recommended method).
725    ///
726    /// This uses the internal frequency tuning code onboard the HackRF, which
727    /// can differ between boards. It automatically sets the LO and IF
728    /// frequencies, as well as the RF path filter.
729    pub async fn set_freq(&self, freq_hz: u64) -> Result<(), Error> {
730        const ONE_MHZ: u64 = 1_000_000;
731        #[repr(C)]
732        #[derive(Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
733        struct FreqParams {
734            mhz: u32,
735            hz: u32,
736        }
737        let mhz = freq_hz / ONE_MHZ;
738        let hz = freq_hz % ONE_MHZ;
739        let params = FreqParams {
740            mhz: (mhz as u32).to_le(),
741            hz: (hz as u32).to_le(),
742        };
743
744        self.write_bytes(ControlRequest::SetFreq, bytemuck::bytes_of(&params))
745            .await
746    }
747
748    /// Set the IF & LO tuning frequencies, and the RF path filter.
749    ///
750    /// You may be looking for [`set_freq`][HackRf::set_freq] instead.
751    ///
752    /// This sets the center frequency to `f_c = f_IF + k * f_LO`, where k is
753    /// -1, 0, or 1 depending on the filter selected.
754    ///
755    /// IF frequency *must* be between 2-3 GHz, and it's strongly recommended to
756    /// be between 2170-2740 MHz.
757    ///
758    /// LO frequency must be between 84.375-5400 MHz. No effect if the filter is
759    /// set to bypass mode.
760    pub async fn set_freq_explicit(
761        &self,
762        if_freq_hz: u64,
763        lo_freq_hz: u64,
764        path: RfPathFilter,
765    ) -> Result<(), Error> {
766        #[repr(C)]
767        #[derive(Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
768        struct FreqParams {
769            if_freq_hz: u64,
770            lo_freq_hz: u64,
771            path: u8,
772            reserved: [u8; 7],
773        }
774
775        const IF_RANGE: Range<u64> = Range {
776            start: 2_000_000_000,
777            end: 3_000_000_001,
778        };
779        const LO_RANGE: Range<u64> = Range {
780            start: 84_375_000,
781            end: 5_400_000_001,
782        };
783
784        if !IF_RANGE.contains(&if_freq_hz) {
785            return Err(Error::TuningRange {
786                range: IF_RANGE,
787                val: if_freq_hz,
788            });
789        }
790        if path != RfPathFilter::Bypass && !LO_RANGE.contains(&lo_freq_hz) {
791            return Err(Error::TuningRange {
792                range: LO_RANGE,
793                val: lo_freq_hz,
794            });
795        }
796
797        let params = FreqParams {
798            if_freq_hz: if_freq_hz.to_le(),
799            lo_freq_hz: lo_freq_hz.to_le(),
800            path: path as u8,
801            reserved: [0u8; 7],
802        };
803
804        self.write_bytes(ControlRequest::SetFreqExplicit, bytemuck::bytes_of(&params))
805            .await
806    }
807
808    /// Set the sample rate using a clock frequency in Hz and a divider value.
809    ///
810    /// The resulting sample rate is `freq_hz/divider`. Divider value can be
811    /// 1-31, and the rate range should be 2-20MHz. Lower & higher values are
812    /// technically possible, but not recommended.
813    ///
814    /// This function will always call
815    /// [`set_baseband_filter_bandwidth`][Self::set_baseband_filter_bandwidth],
816    /// so any changes to the filter should be done *after* this function.
817    ///
818    /// You may want to just use [`set_sample_rate`][Self::set_sample_rate]
819    /// instead.
820    ///
821    pub async fn set_sample_rate_manual(&self, freq_hz: u32, divider: u32) -> Result<(), Error> {
822        #[repr(C)]
823        #[derive(Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
824        struct FracRateParams {
825            freq_hz: u32,
826            divider: u32,
827        }
828
829        const DIV_RANGE: Range<u32> = Range { start: 1, end: 32 };
830        if !DIV_RANGE.contains(&divider) {
831            return Err(Error::ValueRange {
832                range: DIV_RANGE,
833                val: divider,
834            });
835        }
836
837        let params = FracRateParams {
838            freq_hz: freq_hz.to_le(),
839            divider: divider.to_le(),
840        };
841
842        self.write_bytes(ControlRequest::SampleRateSet, bytemuck::bytes_of(&params))
843            .await?;
844
845        let filter_bw = baseband_filter_bw(freq_hz * 3 / (divider * 4));
846        self.set_baseband_filter_bandwidth(filter_bw).await?;
847        Ok(())
848    }
849
850    /// Set the sample rate, which should be between 2-20 MHz.
851    ///
852    /// Lower & higher rates are possible, but not recommended.
853    ///
854    /// This function will always call
855    /// [`set_baseband_filter_bandwidth`][Self::set_baseband_filter_bandwidth],
856    /// so any changes to the filter should be done *after* this function.
857    ///
858    /// This function is a convenience wrapper around
859    /// [`set_sample_rate_manual`][Self::set_sample_rate_manual].
860    ///
861    pub async fn set_sample_rate(&self, freq: f64) -> Result<(), Error> {
862        let freq = freq.clamp(2e6, 20e6);
863
864        let mut freq_hz = 0;
865        let mut divider = 1;
866        let mut diff = f64::MAX;
867
868        // Just blindly check the closest of all possible divider values,
869        // preferring the smaller divider value on ties
870        for i in 1u32..32 {
871            let new_freq_hz = (freq * (i as f64)).round() as u32;
872            let new_diff = ((freq_hz as f64) / (i as f64) - freq).abs();
873            if new_diff < diff {
874                freq_hz = new_freq_hz;
875                divider = i;
876                diff = new_diff;
877            }
878        }
879
880        self.set_sample_rate_manual(freq_hz, divider).await
881    }
882
883    /// Enable/disable the 14dB RF amplifiers.
884    ///
885    /// Enable/disable the RX/TX amplifiers U13/U25 via the controlling switches
886    /// U9 and U14.
887    pub async fn set_amp_enable(&self, enable: bool) -> Result<(), Error> {
888        self.write_u16(ControlRequest::AmpEnable, 0, enable as u16)
889            .await
890    }
891
892    /// Set the LNA gain.
893    ///
894    /// Sets the RF RX gain of the MAX2837 transceiver IC. Must be in the range
895    /// of 0-40 dB, and is forced to 8 dB steps. Intermediate values are rounded
896    /// down.
897    pub async fn set_lna_gain(&self, value: u16) -> Result<(), Error> {
898        if value > 40 {
899            return Err(Error::ValueRange {
900                range: Range { start: 0, end: 41 },
901                val: value as u32,
902            });
903        }
904
905        let ret = self
906            .read_u8(ControlRequest::SetLnaGain, value & (!0x07))
907            .await?;
908        if ret == 0 {
909            return Err(Error::ReturnData);
910        }
911        Ok(())
912    }
913
914    /// Set the VGA gain.
915    ///
916    /// Sets the baseband RX gain of the MAX2837 transceiver IC. Must be in the range
917    /// of 0-62 dB, and is forced to 2 dB steps. Intermediate values are rounded
918    /// down.
919    pub async fn set_vga_gain(&self, value: u16) -> Result<(), Error> {
920        if value > 62 {
921            return Err(Error::ValueRange {
922                range: Range { start: 0, end: 63 },
923                val: value as u32,
924            });
925        }
926
927        let ret = self
928            .read_u8(ControlRequest::SetVgaGain, value & (!0x01))
929            .await?;
930        if ret == 0 {
931            return Err(Error::ReturnData);
932        }
933        Ok(())
934    }
935
936    /// Set the RF TX gain.
937    ///
938    /// Sets the RF TX gain of the MAX2837 transceiver IC. Must be in the range
939    /// of 0-47 dB.
940    pub async fn set_txvga_gain(&self, value: u16) -> Result<(), Error> {
941        if value > 47 {
942            return Err(Error::ValueRange {
943                range: Range { start: 0, end: 48 },
944                val: value as u32,
945            });
946        }
947
948        let ret = self.read_u8(ControlRequest::SetTxvgaGain, value).await?;
949        if ret == 0 {
950            return Err(Error::ReturnData);
951        }
952        Ok(())
953    }
954
955    /// Temporarily enable/disable the bias-tee (antenna port power).
956    ///
957    /// Enable or disable the **3.3v (max 50 mA)** bias-tee. Defaults to
958    /// disabled on power-up.
959    ///
960    /// The firmware auto-disables this after returning to IDLE mode. Consider
961    /// using [`set_user_bias_t_opts`][Self::set_user_bias_t_opts] instead to
962    /// configure the bias to work exactly the way you want it to.
963    pub async fn set_antenna_enable(&self, enable: bool) -> Result<(), Error> {
964        self.write_u16(ControlRequest::AntennaEnable, 0, enable as u16)
965            .await
966    }
967
968    /// Set hardware sync mode (hardware triggering).
969    ///
970    /// See the documentation
971    /// [here](https://hackrf.readthedocs.io/en/latest/hardware_triggering.html).
972    ///
973    /// When enabled, the next operating mode (RX, TX, or Sweep) will not start
974    /// until the input hardware trigger occurs.
975    ///
976    /// Requires API version 0x0102 or higher.
977    pub async fn set_hw_sync_mode(&self, enable: bool) -> Result<(), Error> {
978        self.api_check(0x0102)?;
979        self.write_u16(ControlRequest::SetHwSyncMode, 0, enable as u16)
980            .await
981    }
982
983    /// Get a list of what operacake boards are attached (up to 8).
984    ///
985    /// Requires API version 0x0105 or higher.
986    pub async fn operacake_boards(&self) -> Result<Vec<u8>, Error> {
987        self.api_check(0x0105)?;
988        let mut resp = self
989            .read_bytes(ControlRequest::OperacakeGetBoards, 8)
990            .await?;
991        resp.retain(|&x| x != 0xFF);
992        Ok(resp)
993    }
994
995    /// Set an Operacake board to a specific operating mode.
996    ///
997    /// When set to frequency or dwell time mode, the settings are shared
998    /// between all operacakes in that operating mode.
999    ///
1000    /// Requires API version 0x0105 or higher.
1001    pub async fn operacake_set_mode(
1002        &self,
1003        address: u8,
1004        setting: OperacakeMode,
1005    ) -> Result<(), Error> {
1006        self.api_check(0x0105)?;
1007        if address > 7 {
1008            return Err(Error::InvalidParameter("Operacake address is out of range"));
1009        }
1010        self.write_u8(ControlRequest::OperacakeSetMode, setting as u16, address)
1011            .await
1012    }
1013
1014    /// Get the operating mode of an operacake board.
1015    ///
1016    /// Requires API version 0x0105 or higher.
1017    pub async fn operacake_get_mode(&self, address: u8) -> Result<OperacakeMode, Error> {
1018        self.api_check(0x0105)?;
1019        if address > 7 {
1020            return Err(Error::InvalidParameter("Operacake address is out of range"));
1021        }
1022        let ret = self
1023            .interface
1024            .control_in(ControlIn {
1025                control_type: ControlType::Vendor,
1026                recipient: Recipient::Device,
1027                request: ControlRequest::OperacakeGetMode as u8,
1028                value: address as u16,
1029                index: 0,
1030                length: 1,
1031            })
1032            .await
1033            .into_result()?;
1034        let ret = ret.first().ok_or(Error::ReturnData)?;
1035        match ret {
1036            0 => Ok(OperacakeMode::Manual),
1037            1 => Ok(OperacakeMode::Frequency),
1038            2 => Ok(OperacakeMode::Time),
1039            _ => Err(Error::ReturnData),
1040        }
1041    }
1042
1043    /// Set an operacake's switches manually.
1044    ///
1045    /// Should be called after setting manual mode with
1046    /// [`operacake_set_mode`][Self::operacake_set_mode].
1047    ///
1048    /// Requires API version 0x0102 or higher.
1049    pub async fn operacake_config_manual(&self, address: u8, a: u8, b: u8) -> Result<(), Error> {
1050        self.api_check(0x0102)?;
1051        if address > 7 {
1052            return Err(Error::InvalidParameter("Operacake address is out of range"));
1053        }
1054
1055        if a > 7 || b > 7 {
1056            return Err(Error::InvalidParameter(
1057                "One or more port numbers is out of range (0-7)",
1058            ));
1059        }
1060        if (a < 4 && b < 4) || (a >= 4 && b >= 4) {
1061            return Err(Error::InvalidParameter(
1062                "A0 & B0 ports are using same quad of multiplexed ports",
1063            ));
1064        }
1065
1066        let a = a as u16;
1067        let b = b as u16;
1068        self.write_u8(ControlRequest::OperacakeSetPorts, a | (b << 8), address)
1069            .await
1070    }
1071
1072    /// Match frequency bands to operacake ports.
1073    ///
1074    /// These frequency settings are used by any operacake operating in
1075    /// frequency mode.
1076    ///
1077    /// Requires API version 0x0103 or higher.
1078    pub async fn operacake_config_freq(&self, freqs: &[OperacakeFreq]) -> Result<(), Error> {
1079        self.api_check(0x0103)?;
1080        if freqs.len() > 8 {
1081            return Err(Error::InvalidParameter(
1082                "Operacake can only support 8 frequency bands max",
1083            ));
1084        }
1085        let mut data = Vec::with_capacity(5 * freqs.len());
1086        for f in freqs {
1087            if f.port > 7 {
1088                return Err(Error::InvalidParameter(
1089                    "Operacake frequency band port selection is out of range",
1090                ));
1091            }
1092            data.push((f.min >> 8) as u8);
1093            data.push((f.min & 0xFF) as u8);
1094            data.push((f.max >> 8) as u8);
1095            data.push((f.max & 0xFF) as u8);
1096            data.push(f.port);
1097        }
1098
1099        self.write_bytes(ControlRequest::OperacakeSetRanges, &data)
1100            .await
1101    }
1102
1103    /// Match dwell times to operacake ports.
1104    ///
1105    /// These dwell time settings are used by any operacake operating in
1106    /// time mode.
1107    ///
1108    /// Requires API version 0x0105 or higher.
1109    pub async fn operacake_config_time(&self, times: &[OperacakeDwell]) -> Result<(), Error> {
1110        self.api_check(0x0105)?;
1111        if times.len() > 16 {
1112            return Err(Error::InvalidParameter(
1113                "Operacake can only support 16 time slices max",
1114            ));
1115        }
1116        let mut data = Vec::with_capacity(5 * times.len());
1117        for t in times {
1118            if t.port > 7 {
1119                return Err(Error::InvalidParameter(
1120                    "Operacake time slice port selection is out of range",
1121                ));
1122            }
1123            data.extend_from_slice(&t.dwell.to_le_bytes());
1124            data.push(t.port);
1125        }
1126        self.write_bytes(ControlRequest::OperacakeSetDwellTimes, &data)
1127            .await
1128    }
1129
1130    /// Reset the HackRF.
1131    ///
1132    /// Requires API version 0x0102 or higher.
1133    pub async fn reset(&self) -> Result<(), Error> {
1134        self.api_check(0x0102)?;
1135        self.write_u16(ControlRequest::Reset, 0, 0).await
1136    }
1137
1138    /// Turn on the CLKOUT port.
1139    ///
1140    /// Requires API version 0x0103 or higher.
1141    pub async fn clkout_enable(&self, enable: bool) -> Result<(), Error> {
1142        self.api_check(0x0103)?;
1143        self.write_u16(ControlRequest::ClkoutEnable, 0, enable as u16)
1144            .await
1145    }
1146
1147    /// Check the CLKIN port status.
1148    ///
1149    /// Set to true if the CLKIN port is used as the reference clock.
1150    ///
1151    /// Requires API version 0x0106 or higher.
1152    pub async fn clkin_status(&self) -> Result<bool, Error> {
1153        self.api_check(0x0106)?;
1154        Ok(self.read_u8(ControlRequest::GetClkinStatus, 0).await? != 0)
1155    }
1156
1157    /// Perform a GPIO test of an Operacake board.
1158    ///
1159    /// Value 0xFFFF means "GPIO mode disabled" - remove additional add-on
1160    /// boards and retry.
1161    ///
1162    /// Value 0 means all tests passed.
1163    ///
1164    /// In any other values, a 1 bit signals an error. Bits are grouped in
1165    /// groups of 3. Encoding:
1166    ///
1167    /// ```text
1168    /// 0 - u1ctrl - u3ctrl0 - u3ctrl1 - u2ctrl0 - u2ctrl1
1169    /// ```
1170    ///
1171    /// Requires API version 0x0103 or higher.
1172    pub async fn operacake_gpio_test(&self, address: u8) -> Result<u16, Error> {
1173        self.api_check(0x0103)?;
1174        if address > 7 {
1175            return Err(Error::InvalidParameter("Operacake address is out of range"));
1176        }
1177        let ret = self
1178            .interface
1179            .control_in(ControlIn {
1180                control_type: ControlType::Vendor,
1181                recipient: Recipient::Device,
1182                request: ControlRequest::OperacakeGpioTest as u8,
1183                value: address as u16,
1184                index: 0,
1185                length: 2,
1186            })
1187            .await
1188            .into_result()?;
1189        let ret: [u8; 2] = ret.as_slice().try_into().map_err(|_| Error::ReturnData)?;
1190        Ok(u16::from_le_bytes(ret))
1191    }
1192
1193    /// Enable/disable the UI display on devices with one (Rad1o, PortaPack).
1194    ///
1195    /// Requires API version 0x0104 or higher.
1196    pub async fn set_ui_enable(&self, val: u8) -> Result<(), Error> {
1197        self.api_check(0x0104)?;
1198        self.write_u8(ControlRequest::UiEnable, 0, val).await
1199    }
1200
1201    /// Turn the LEDs on or off, overriding the default.
1202    ///
1203    /// There are normally 3 controllable LEDs: USB, RX, and TX. The Rad1o board
1204    /// has 4.  After setting them individually, they may get overridden later
1205    /// by other functions.
1206    ///
1207    /// | Bit | LED  |
1208    /// | --  | --   |
1209    /// | 0   | USB  |
1210    /// | 1   | RX   |
1211    /// | 2   | TX   |
1212    /// | 3   | User |
1213    ///
1214    /// Requires API version 0x0107 or higher.
1215    pub async fn set_leds(&self, state: u8) -> Result<(), Error> {
1216        self.api_check(0x0107)?;
1217        self.write_u8(ControlRequest::SetLeds, 0, state).await
1218    }
1219
1220    /// Set the Bias-Tee behavior.
1221    ///
1222    /// This function will configure what change, if any, to apply to the
1223    /// bias-tee circuit on a mode change. The default is for it to always be
1224    /// off, but with a custom config, it can turn on when switching to RX, TX,
1225    /// or even to always be on. The settings in `opts` are always applied when
1226    /// first changing to that mode, with [`BiasTMode::NoChange`] not changing
1227    /// from whatever it is set to before the transition.
1228    ///
1229    /// Requires API version 0x0108 or higher.
1230    pub async fn set_user_bias_t_opts(&self, opts: BiasTSetting) -> Result<(), Error> {
1231        self.api_check(0x0108)?;
1232        let state: u16 =
1233            0x124 | opts.off.as_u16() | (opts.rx.as_u16() << 3) | (opts.tx.as_u16() << 6);
1234        self.write_u16(ControlRequest::SetUserBiasTOpts, 0, state)
1235            .await
1236    }
1237
1238    /// Switch a HackRF into receive mode, getting `transfer_size` samples at a
1239    /// time. The transfer size is always rounded up to the nearest 256-sample
1240    /// block increment; it's recommended to be 8192 samples but can be smaller
1241    /// or larger as needed. If the same size is used repeatedly with
1242    /// `start_rx`, buffers won't need to be reallocated.
1243    pub async fn start_rx(self, transfer_size: usize) -> Result<Receive, StateChangeError> {
1244        Receive::new(self, transfer_size).await
1245    }
1246
1247    /// Start a RX sweep, which will also set the sample rate and baseband filter.
1248    ///
1249    /// Buffers are reused across sweep operations, provided that
1250    /// [`HackRf::start_rx`] isn't used, or is used with a 8192 sample buffer
1251    /// size.
1252    ///
1253    /// See [`Sweep`] for more details on the RX sweep mode.
1254    pub async fn start_rx_sweep(self, params: &SweepParams) -> Result<Sweep, StateChangeError> {
1255        Sweep::new(self, params).await
1256    }
1257
1258    /// Start a RX sweep, but don't set up the sample rate and baseband filter before starting.
1259    ///
1260    /// Buffers are reused across sweep operations, provided that
1261    /// [`HackRf::start_rx`] isn't used, or is used with a 8192 sample buffer
1262    /// size.
1263    ///
1264    /// See [`Sweep`] for more details on the RX sweep mode.
1265    pub async fn start_rx_sweep_custom_sample_rate(
1266        self,
1267        params: &SweepParams,
1268    ) -> Result<Sweep, StateChangeError> {
1269        Sweep::new_with_custom_sample_rate(self, params).await
1270    }
1271
1272    /// Switch a HackRF into transmit mode, with a set maximum number of samples
1273    /// per buffer block.
1274    ///
1275    /// Buffers are reused across transmit operations, provided that the
1276    /// `max_transfer_size` is always the same.
1277    pub async fn start_tx(self, max_transfer_size: usize) -> Result<Transmit, StateChangeError> {
1278        Transmit::new(self, max_transfer_size).await
1279    }
1280
1281    /// Try and turn the HackRF to the off state, regardless of what mode it is currently in.
1282    pub async fn turn_off(&self) -> Result<(), Error> {
1283        self.set_transceiver_mode(TransceiverMode::Off).await
1284    }
1285}
1286
1287fn baseband_filter_bw(freq: u32) -> u32 {
1288    const MAX2837_FT: &[u32] = &[
1289        1750000, 2500000, 3500000, 5000000, 5500000, 6000000, 7000000, 8000000, 9000000, 10000000,
1290        12000000, 14000000, 15000000, 20000000, 24000000, 28000000,
1291    ];
1292
1293    MAX2837_FT
1294        .iter()
1295        .rev()
1296        .find(|f| freq >= **f)
1297        .copied()
1298        .unwrap_or(MAX2837_FT[0])
1299}
1300
1301#[cfg(test)]
1302mod tests {
1303    use crate::baseband_filter_bw;
1304
1305    #[test]
1306    fn baseband_filter() {
1307        assert_eq!(baseband_filter_bw(1000), 1750000);
1308        assert_eq!(baseband_filter_bw(30_000_000), 28_000_000);
1309        assert_eq!(baseband_filter_bw(3_000_000), 2_500_000);
1310    }
1311}