waverave_hackrf/
lib.rs

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