RX and TX, callbacks.
More...
|
| int | hackrf_start_rx (hackrf_device *device, hackrf_sample_block_cb_fn callback, void *rx_ctx) |
| | Start receiving.
|
| |
| int | hackrf_stop_rx (hackrf_device *device) |
| | Stop receiving.
|
| |
| int | hackrf_start_tx (hackrf_device *device, hackrf_sample_block_cb_fn callback, void *tx_ctx) |
| | Start transmitting.
|
| |
| int | hackrf_set_tx_block_complete_callback (hackrf_device *device, hackrf_tx_block_complete_cb_fn callback) |
| | Setup callback to be called when an USB transfer is completed.
|
| |
| int | hackrf_enable_tx_flush (hackrf_device *device, hackrf_flush_cb_fn callback, void *flush_ctx) |
| | Setup flush (end-of-transmission) callback.
|
| |
| int | hackrf_stop_tx (hackrf_device *device) |
| | Stop transmission.
|
| |
| int | hackrf_set_tx_underrun_limit (hackrf_device *device, uint32_t value) |
| | Set transmit underrun limit.
|
| |
| int | hackrf_set_rx_overrun_limit (hackrf_device *device, uint32_t value) |
| | Set receive overrun limit.
|
| |
| int | hackrf_is_streaming (hackrf_device *device) |
| | Query device streaming status.
|
| |
| int | hackrf_set_hw_sync_mode (hackrf_device *device, const uint8_t value) |
| | Set hardware sync mode (hardware triggering)
|
| |
| int | hackrf_init_sweep (hackrf_device *device, const uint16_t *frequency_list, const int num_ranges, const uint32_t num_bytes, const uint32_t step_width, const uint32_t offset, const enum sweep_style style) |
| | Initialize sweep mode.
|
| |
| int | hackrf_start_rx_sweep (hackrf_device *device, hackrf_sample_block_cb_fn callback, void *rx_ctx) |
| | Start RX sweep.
|
| |
Streaming
There are 3 different streaming modes supported by HackRF:
- transmitting (TX)
- receiving (RX)
- swept receiving (SWEEP)
Each mode needs to be initialized before use, then the mode needs to be entered with the hackrf_start_* function. Data transfer happens through callbacks.
There are 3 types of callbacks in the library:
- transfer callback
- flush callback
- block complete callback
Steps for starting an RX or TX operation:
- initialize libhackrf
- open device
- setup device (frequency, samplerate, gain, etc.)
- setup callbacks, start operation (
hackrf_start_*)
- the main program should go to sleep
- when done, the transfer callback should return non-zero value, and signal the main thread to stop
- stop operation via
hackrf_stop_*
- close device, exit library, etc.
Data is transferred through the USB connection via setting up multiple async libusb transfers (hackrf_get_transfer_queue_depth). In TX mode, the transfers needs to be filled before submitting, and in RX mode, they need to be read out when they are done. This is done using the transfer callback - it receives a hackrf_transfer object and needs to transfer the data to/from it. As it's needed for all operations, this gets called whenever we need to move data, so every time a transfer is finished (and before the first transfer in TX mode). There's a "transfer complete callback" that only gets called when a transfer is completed. It does not need to do anything special tho, and is optional.
Streaming can be stopped via returning a non-zero value from the transfer callback, but that does NOT reset the device to IDLE mode, it only stops data transfers. In TX mode, when this happens, and the transmitter runs out of data to transmit, it will start transmitting all 0 values (but in older firmware versions, it started repeating the last buffer). To actually stop the operation, a call to hackrf_stop_* is needed. Since the callback operate in an async libusb context, such a call can't be made from there, only from the main thread, so it must be signaled through some means (for example, a global variable, or better, a pthread_cond) to stop. In RX mode, this signaling can be done from the transfer callback, but in TX mode, we must make sure that we only stop the operation when the last transfer is completed and the device transmitted it, or we might lose it. For this reason, the third flush callback exists, that gets called when this happens. It is advised to only signal the main thread to stop from this callback.
The function hackrf_is_streaming can be used to check if the device is streaming or not.
Transfer callback
Set when starting an operation with hackrf_start_tx, hackrf_start_rx or hackrf_start_rx_sweep. This callback supplies / receives data. This function takes a hackrf_transfer struct as a parameter, and fill/read data to/from its buffer. This function runs in an async libusb context, meaning it should not interact with the libhackrf library in other ways. The callback can return a boolean value, if its return value is non-zero then it won't be called again, meaning that no future transfers will take place, and (in TX case) the flush callback will be called shortly.
Block complete callback
This callback is optional, and only applicable in TX mode. It gets called whenever a data transfer is finished, and can read the data. It needs to do nothing at all. This callback can be set using hackrf_set_tx_block_complete_callback
Flush callback
This callback is optional, and only applicable in TX mode. It get called when the last transfer is completed, and it's advisable to only stop streaming via this callback. This callback can be set using hackrf_enable_tx_flush
Example TX code utilizing the transfer and flush callbacks.
#include <libhackrf/hackrf.h>
#include <math.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <complex.h>
#include <stdint.h>
const double f_mod = 440;
const uint64_t sample_rate = 10000000;
double triangle() {
static double state;
static uint64_t samples_generated;
const uint64_t period_in_samples = sample_rate / f_mod;
const double step = 4.0 / period_in_samples;
if (samples_generated < period_in_samples / 2 )
state += step;
else
state -= step;
if (samples_generated ++ == period_in_samples)
samples_generated = 0;
return state - 1.0;
}
volatile double complex phasor = 1.0;
int xfered_samples = 0;
int samples_to_xfer = 5*sample_rate;
volatile int should_stop = 0;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int8_t *signed_buffer = (int8_t*)transfer->
buffer;
for (int i = 0; i<transfer->buffer_length; i+=2) {
phasor *= cexp(I*6.28*3000 / sample_rate*triangle());
signed_buffer[i] = 128 * creal(phasor);
signed_buffer[i+1] = 128 * cimag(phasor);
}
transfer->valid_length = transfer->buffer_length;
xfered_samples += transfer->buffer_length;
if (xfered_samples >= samples_to_xfer) {
return 1;
}
return 0;
}
void flush_callback(void *ctx, int success) {
pthread_mutex_lock(&mutex);
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
}
int main() {
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
return 0;
}
int hackrf_set_sample_rate(hackrf_device *device, const double freq_hz)
Set sample rate.
int hackrf_set_amp_enable(hackrf_device *device, const uint8_t value)
Enable/disable 14dB RF amplifier.
int hackrf_set_freq(hackrf_device *device, const uint64_t freq_hz)
Set the center frequency.
int hackrf_set_txvga_gain(hackrf_device *device, uint32_t value)
Set RF TX gain of the MAX2837 transceiver IC ("IF" or "VGA" gain setting) in decibels.
int hackrf_close(hackrf_device *device)
Close a previously opened device.
int hackrf_open(hackrf_device **device)
Open first available HackRF device.
struct hackrf_device hackrf_device
Opaque struct for hackrf device info.
Definition hackrf.h:954
int hackrf_init()
Initialize libhackrf.
int hackrf_exit()
Exit libhackrf.
int hackrf_stop_tx(hackrf_device *device)
Stop transmission.
int hackrf_enable_tx_flush(hackrf_device *device, hackrf_flush_cb_fn callback, void *flush_ctx)
Setup flush (end-of-transmission) callback.
int hackrf_start_tx(hackrf_device *device, hackrf_sample_block_cb_fn callback, void *tx_ctx)
Start transmitting.
USB transfer information passed to RX or TX callback.
Definition hackrf.h:963
uint8_t * buffer
transfer data buffer (interleaved 8 bit I/Q samples)
Definition hackrf.h:967
This code can be compiled using gcc -o triangle triangle.c -lm -lhackrf. It generates and transmits a 440Hz triangle wave using FM modulation on the 2m HAM band (check your local laws and regulations on transmitting and only transmit on bands you have license to!).
For a more complete example, including error handling and more settings, see hackrf_transfer.c
Underrun and overrun
Underrun/overrun detection can be enabled using hackrf_set_tx_underrun_limit or hackrf_set_rx_overrun_limit limit. This causes the HackRF to stop operation if more than the specified amount of samples get lost, for example in case of your program crashing, USB connection failure, etc.
Sweeping
Sweeping mode is kind of special. In this mode, the device can be programmed to a list of frequencies to tune on, record set amount of samples and then tune to the next frequency and repeat. It can be setup via hackrf_init_sweep and started with hackrf_start_rx_sweep. In this mode, the callback does not receive raw samples, but blocks of samples prefixed with a frequency header specifying the tuned frequency.
See hackrf_sweep.c for a full example, and especially the start of the RX callback for parsing the frequency header.
HW sync mode
hackrf_set_hw_sync_mode can be used to setup HW sync mode (see the documentation on this mode). This mode allows multiple HackRF Ones to synchronize operations, or one HackRF One to synchronize on an external trigger source.
◆ BYTES_PER_BLOCK
| #define BYTES_PER_BLOCK 16384 |
◆ MAX_SWEEP_RANGES
| #define MAX_SWEEP_RANGES 10 |
◆ SAMPLES_PER_BLOCK
| #define SAMPLES_PER_BLOCK 8192 |
◆ hackrf_flush_cb_fn
| typedef void(* hackrf_flush_cb_fn) (void *flush_ctx, int) |
Will be called when the last samples are transmitted and stopping transmission will result in no samples getting lost. Should signal the main thread that it should stop transmission via hackrf_stop_tx
◆ hackrf_sample_block_cb_fn
In each mode, it is called when data needs to be handled, meaning filling samples in TX mode or reading them in RX modes.
In TX mode, it should refill the transfer buffer with new raw IQ data, and set hackrf_transfer::valid_length.
In RX mode, it should copy/process the contents of the transfer buffer's valid part.
In RX SWEEP mode, it receives multiple "blocks" of data, each with a 10-byte header containing the tuned frequency followed by the samples. See hackrf_init_sweep for more info.
The callback should return 0 if it wants to be called again, and any other value otherwise. Stopping the RX/TX/SWEEP is still done with hackrf_stop_rx and hackrf_stop_tx, and those should be called from the main thread, so this callback should signal the main thread that it should stop. Signaling the main thread to stop TX should be done from the flush callback in order to guarantee that no samples are discarded, see hackrf_flush_cb_fn
◆ hackrf_tx_block_complete_cb_fn
| typedef void(* hackrf_tx_block_complete_cb_fn) (hackrf_transfer *transfer, int) |
Set via hackrf_set_tx_block_complete_callback, called when a transfer is finished to the device's buffer, regardless if the transfer was successful or not. It can signal the main thread to stop on failure, can catch USB transfer errors and can also gather statistics about the transferred data.
◆ sweep_style
Used by hackrf_init_sweep, to set sweep parameters.
Linear mode is no longer used by the hackrf_sweep command line tool and in general the interleaved method is always preferable, but the linear mode remains available for backward compatibility and might be useful in some special circumstances.
| Enumerator |
|---|
| LINEAR | step_width is added to the current frequency at each step.
|
| INTERLEAVED | each step is divided into two interleaved sub-steps, allowing the host to select the best portions of the FFT of each sub-step and discard the rest.
|
◆ hackrf_enable_tx_flush()
This callback will be called when all the data was transmitted and all data transfers were completed. First parameter is supplied context, second parameter is success flag.
- Parameters
-
| device | device to configure |
| callback | callback to call when all transfers were completed |
| flush_ctx | context (1st parameter of callback) |
- Returns
- HACKRF_SUCCESS on success or hackrf_error variant
◆ hackrf_init_sweep()
| int hackrf_init_sweep |
( |
hackrf_device * |
device, |
|
|
const uint16_t * |
frequency_list, |
|
|
const int |
num_ranges, |
|
|
const uint32_t |
num_bytes, |
|
|
const uint32_t |
step_width, |
|
|
const uint32_t |
offset, |
|
|
const enum sweep_style |
style |
|
) |
| |
|
extern |
In this mode, in a single data transfer (single call to the RX transfer callback), multiple blocks of size num_bytes bytes are received with different center frequencies. At the beginning of each block, a 10-byte frequency header is present in 0x7F - 0x7F - uint64_t frequency (LSBFIRST, in Hz) format, followed by the actual samples.
Requires USB API version 0x0102 or above!
- Parameters
-
| device | device to configure |
| frequency_list | list of start-stop frequency pairs in MHz |
| num_ranges | length of array frequency_list (in pairs, so total array length / 2!). Must be less than MAX_SWEEP_RANGES |
| num_bytes | number of bytes to capture per tuning, must be a multiple of BYTES_PER_BLOCK |
| step_width | width of each tuning step in Hz |
| offset | frequency offset added to tuned frequencies. sample_rate / 2 is a good value |
| style | sweep style |
- Returns
- HACKRF_SUCCESS on success or hackrf_error variant
◆ hackrf_is_streaming()
◆ hackrf_set_hw_sync_mode()
| int hackrf_set_hw_sync_mode |
( |
hackrf_device * |
device, |
|
|
const uint8_t |
value |
|
) |
| |
|
extern |
See the documentation on hardware triggering for details
Requires USB API version 0x0102 or above!
- Parameters
-
| device | device to configure |
| value | enable (1) or disable (0) hardware triggering |
- Returns
- HACKRF_SUCCESS on success or hackrf_error variant
◆ hackrf_set_rx_overrun_limit()
| int hackrf_set_rx_overrun_limit |
( |
hackrf_device * |
device, |
|
|
uint32_t |
value |
|
) |
| |
|
extern |
When this limit is set, after the specified number of samples (bytes, not whole IQ pairs) missing the device will automatically return to IDLE mode, thus stopping operation. Useful for handling cases like program/computer crashes or other problems. The default value 0 means no limit.
Requires USB API version 0x0106 or above!
- Parameters
-
| device | device to configure |
| value | number of samples to wait before auto-stopping |
- Returns
- HACKRF_SUCCESS on success or hackrf_error variant
◆ hackrf_set_tx_block_complete_callback()
This callback will be called whenever an USB transfer to the device is completed, regardless if it was successful or not (indicated by the second parameter).
- Parameters
-
| device | device to configure |
| callback | callback to call when a transfer is completed |
- Returns
- HACKRF_SUCCESS on success or hackrf_error variant
◆ hackrf_set_tx_underrun_limit()
| int hackrf_set_tx_underrun_limit |
( |
hackrf_device * |
device, |
|
|
uint32_t |
value |
|
) |
| |
|
extern |
When this limit is set, after the specified number of samples (bytes, not whole IQ pairs) missing the device will automatically return to IDLE mode, thus stopping operation. Useful for handling cases like program/computer crashes or other problems. The default value 0 means no limit.
Requires USB API version 0x0106 or above!
- Parameters
-
| device | device to configure |
| value | number of samples to wait before auto-stopping |
- Returns
- HACKRF_SUCCESS on success or hackrf_error variant
◆ hackrf_start_rx()
Should be called after setting gains, frequency and sampling rate, as these values won't get reset but instead keep their last value, thus their state is unknown.
The callback is called with a hackrf_transfer object whenever the buffer is full. The callback is called in an async context so no libhackrf functions should be called from it. The callback should treat its argument as read-only.
- Parameters
-
| device | device to configure |
| callback | rx_callback |
| rx_ctx | User provided RX context. Not used by the library, but available to callback as hackrf_transfer::rx_ctx. |
- Returns
- HACKRF_SUCCESS on success or hackrf_error variant
◆ hackrf_start_rx_sweep()
See hackrf_init_sweep for more info
Requires USB API version 0x0104 or above!
- Parameters
-
| device | device to start sweeping |
| callback | rx callback processing the received data |
| rx_ctx | User provided RX context. Not used by the library, but available to callback as hackrf_transfer::rx_ctx. |
- Returns
- HACKRF_SUCCESS on success or hackrf_error variant
◆ hackrf_start_tx()
Should be called after setting gains, frequency and sampling rate, as these values won't get reset but instead keep their last value, thus their state is unknown. Setting flush function (using hackrf_enable_tx_flush) and/or setting block complete callback (using hackrf_set_tx_block_complete_callback) (if these features are used) should also be done before this.
The callback is called with a hackrf_transfer object whenever a transfer buffer is needed to be filled with samples. The callback is called in an async context so no libhackrf functions should be called from it. The callback should treat its argument as read-only, except the hackrf_transfer::buffer and hackrf_transfer::valid_length.
- Parameters
-
| device | device to configure |
| callback | tx_callback |
| tx_ctx | User provided TX context. Not used by the library, but available to callback as hackrf_transfer::tx_ctx. |
- Returns
- HACKRF_SUCCESS on success or hackrf_error variant
◆ hackrf_stop_rx()
◆ hackrf_stop_tx()