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
impl Receive
Sourcepub async fn new(
rf: HackRf,
transfer_size: usize,
) -> Result<Self, StateChangeError>
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.
Sourcepub fn transfer_size(&self) -> usize
pub fn transfer_size(&self) -> usize
Get the chosen transfer size, in samples.
Sourcepub fn submit(&mut self)
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.
Sourcepub async fn next_complete(&mut self) -> Result<Buffer, Error>
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.
Sourcepub async fn stop(self) -> Result<HackRf, StateChangeError>
pub async fn stop(self) -> Result<HackRf, StateChangeError>
Halt receiving and return to idle mode.
This attempts to cancel all transfers and then complete whatever is left. Transfer errors are ignored.