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