pub struct Transmit { /* private fields */ }Expand description
A HackRF operating in transmit mode.
To send data, first take a HackRF peripheral and call
HackRf::start_tx, or use Transmit::new with it. Provide the maximum
block transfer size, in samples, that will be used for the duration of the
transmit.
Next, call get_buffer to allocate a buffer, and
then fill that buffer with the next block of samples to transmit, up to the
maximum block transfer size chosen. Set up around half a million samples
worth of buffers before submitting any, ideally, or gaps in the transmit
sequence may occur.
Next, start calling submit to submit the blocks.
Continue the buffer filling and submitting process as long as needed,
preferrably pausing with next_complete when
there a more than half a million samples worth of buffers queued up. The
number of queued buffers can be checked with pending.
Once buffers are queued up, the process of completing, grabbing a buffer,
and submitting it in a loop until finished.
When all buffers have been submitted, call flush to
queue up a final set of zero-filled buffers to flush out any remaining data
in the HackRF’s internal buffers. Then continue calling
next_complete until
pending returns 0. Finally, exit transmit mode with
stop. Transmit mode can be exited without doing this
sequence, but not all samples queued up will necessarily be sent otherwise.
Putting it all together, here’s an example program that transmits all the data in a file:
use anyhow::Result;
use tokio::io::AsyncReadExt;
use waverave_hackrf::Buffer;
#[tokio::main]
async fn main() -> Result<()> {
let hackrf = waverave_hackrf::open_hackrf()?;
// Configure: 20MHz sample rate, turn on RF amp, set TX IF gain to 16 dB,
// and tune to 915 MHz.
hackrf.set_sample_rate(20e6).await?;
hackrf.set_txvga_gain(16).await?;
hackrf.set_freq(915_000_000).await?;
hackrf.set_amp_enable(true).await?;
// Open up a file for buffered reading.
let mut args = std::env::args();
args.next();
let file_name = args.next().unwrap_or_else(|| String::from("./tx.bin"));
let mut file = tokio::fs::File::open(&file_name).await?;
// Start transmitting, in bursts of 8192 samples
let mut hackrf_tx = hackrf.start_tx(8192).await.map_err(|e| e.err)?;
// Set up an asynchronous process that fills buffers and sends them on to
// the transmitter.
let (buf_send, mut buf_recv) = tokio::sync::mpsc::channel::<Buffer>(4);
let (data_send, mut data_recv) = tokio::sync::mpsc::channel::<Buffer>(4);
tokio::spawn(async move {
loop {
let Some(mut buf) = buf_recv.recv().await else {
break;
};
buf.extend_zeros(buf.remaining_capacity());
file.read_exact(buf.bytes_mut()).await?;
let Ok(_) = data_send.send(buf).await else {
break;
};
}
Ok::<(), anyhow::Error>(())
});
// Start filling up queue
let mut start = Vec::with_capacity(64);
while start.len() < 64 {
while buf_send.try_send(hackrf_tx.get_buffer()).is_ok() {}
let Some(buf) = data_recv.recv().await else {
break;
};
start.push(buf);
}
// Submit the whole starting queue in one go
for buf in start {
hackrf_tx.submit(buf);
}
// Continue filling the queue and submitting samples as often as possible
loop {
while buf_send.try_send(hackrf_tx.get_buffer()).is_ok() {}
hackrf_tx.next_complete().await?;
let Some(buf) = data_recv.recv().await else {
break;
};
hackrf_tx.submit(buf);
}
// Flush the remainder
hackrf_tx.flush();
while hackrf_tx.pending() > 0 {
hackrf_tx.next_complete().await?;
}
// Stop transmitting
hackrf_tx.stop().await?;
Ok(())
}Implementations§
Source§impl Transmit
impl Transmit
Sourcepub async fn new(
rf: HackRf,
max_transfer_size: usize,
) -> Result<Self, StateChangeError>
pub async fn new( rf: HackRf, max_transfer_size: usize, ) -> Result<Self, StateChangeError>
Switch a HackRF into transmit mode, with a set maximum number of samples per buffer block.
Buffers are reused across transmit operations, provided that the
max_transfer_size is always the same.
Sourcepub fn get_buffer(&self) -> Buffer
pub fn get_buffer(&self) -> Buffer
Get a buffer for holding transmit data.
All buffers have the same capacity, set when transmit is initialized. Actual transmitted data is rounded up to the nearest 256 samples, zero-filling as needed.
Sourcepub fn max_transfer_size(&self) -> usize
pub fn max_transfer_size(&self) -> usize
The maximum number of samples that can be queued within a single buffer.
Sourcepub fn submit(&mut self, tx: Buffer)
pub fn submit(&mut self, tx: Buffer)
Queue up a transmit 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 fn flush(&mut self)
pub fn flush(&mut self)
Flush whatever remaining samples are in the HackRF internal buffer.
This will generate additional pending operations.
Additional pending operations go up by 8192.div_ceil(max_transfer_size).
Sourcepub async fn next_complete(&mut self) -> Result<(), Error>
pub async fn next_complete(&mut self) -> Result<(), Error>
Wait for a transmit operation to complete.
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.