Struct Receive

Source
pub struct Receive { /* private fields */ }
Expand description

A HackRF operating in receive mode.

To receive data, first take a HackRF peripheral and call HackRf::start_rx, or use Receive::new with it.

Next, call submit to queue up requests, stopping when there are enough pending requests. libhackrf queues up 1 MiB of data, or 524288 samples. You’ll probably want something similar, with the number of pending requests informed by your chosen transfer block size.

Actual reception is done with next_complete, which will panic if there are no pending requests. The number of pending requests can always be checked with pending.

When finished receiving, call stop to cancel all remaining transactions and switch the HackRF off again.

Putting it all together, here’s an example receive program that writes all data to a file:

use std::sync::{Arc, atomic};

use anyhow::Result;
use tokio::io::AsyncWriteExt;
use waverave_hackrf::Buffer;
#[tokio::main]
async fn main() -> Result<()> {
    // Set up the ctrl-c handler
    let ctrlc_rx = Arc::new(atomic::AtomicBool::new(false));
    let ctrlc_tx = ctrlc_rx.clone();
    tokio::spawn(async move {
        tokio::signal::ctrl_c().await.unwrap();
        ctrlc_tx.store(true, atomic::Ordering::Release);
    });

    // Open up a file for buffered writing.
    let mut args = std::env::args();
    args.next();
    let file_name = args.next().unwrap_or_else(|| String::from("./rx.bin"));
    let mut file = tokio::fs::File::create(&file_name).await?;

    // Open up the HackRF
    let hackrf = waverave_hackrf::open_hackrf()?;

    // Configure: 20MHz sample rate, turn on the RF amp, set IF & BB gains to 16 dB,
    // and tune to 915 MHz.
    hackrf.set_amp_enable(true).await?;
    hackrf.set_sample_rate(20e6).await?;
    hackrf.set_lna_gain(16).await?;
    hackrf.set_vga_gain(16).await?;
    hackrf.set_freq(915_000_000).await?;

    // Start receiving, in bursts of 8192 samples
    let mut hackrf_rx = hackrf.start_rx(8192).await.map_err(|e| e.err)?;

    // Separate the file writer from the sample reader with a separate task
    let (data_send, mut data_recv) = tokio::sync::mpsc::unbounded_channel::<Buffer>();
    let file_writer = tokio::spawn(async move {
        loop {
            let Some(buf) = data_recv.recv().await else {
                break;
            };
            file.write_all_buf(&mut buf.bytes()).await?;
        }
        file.flush().await?;
        Ok::<(), anyhow::Error>(())
    });

    // Queue up 64 transfers immediately, then start retrieving them until we
    // get a ctrl-c.
    for _ in 0..64 {
        hackrf_rx.submit();
    }
    loop {
        if ctrlc_rx.load(atomic::Ordering::Acquire) {
            break;
        }
        if data_send.send(hackrf_rx.next_complete().await?).is_err() {
            break;
        }
        hackrf_rx.submit();
    }

    // Stop receiving
    hackrf_rx.stop().await?;
    drop(data_send);

    // Wait for file writer task to close up shop
    file_writer.await??;

    Ok(())
}

Implementations§

Source§

impl Receive

Source

pub async fn new( rf: HackRf, transfer_size: usize, ) -> Result<Self, StateChangeError>

Switch a HackRF into receive mode, getting transfer_size samples at a time. The transfer size is always rounded up to the nearest 256-sample block increment; it’s recommended to be 8192 samples but can be smaller or larger as needed.

Source

pub fn transfer_size(&self) -> usize

Get the chosen transfer size, in samples.

Source

pub fn submit(&mut self)

Queue up a receive transfer.

This will pull from a reusable buffer pool first, and allocate a new buffer if none are available in the pool.

The buffer pool will grow so long as completed buffers aren’t dropped.

Source

pub async fn next_complete(&mut self) -> Result<Buffer, Error>

Retrieve the next chunk of receive data.

This future is cancel-safe, so feel free to use it alongside a timeout or a select!-type pattern.

Source

pub fn pending(&self) -> usize

Get the number of pending requests.

Source

pub async fn stop(self) -> Result<HackRf, Error>

Halt receiving and return to idle mode.

This attempts to cancel all transfers and then complete whatever is left. Transfer errors are ignored.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.