libhackrf
HackRF SDR platform library
Data Structures | Macros | Typedefs | Enumerations | Functions
Transmit & receive operation

RX and TX, callbacks. More...

Data Structures

struct  hackrf_transfer
 USB transfer information passed to RX or TX callback. More...
 

Macros

#define SAMPLES_PER_BLOCK   8192
 Number of samples per tuning when sweeping. More...
 
#define BYTES_PER_BLOCK   16384
 Number of bytes per tuning for sweeping. More...
 
#define MAX_SWEEP_RANGES   10
 Maximum number of sweep ranges to be specified for hackrf_init_sweep. More...
 

Typedefs

typedef int(* hackrf_sample_block_cb_fn) (hackrf_transfer *transfer)
 Sample block callback, used in RX and TX (set via hackrf_start_rx, hackrf_start_rx_sweep and hackrf_start_tx). More...
 
typedef void(* hackrf_tx_block_complete_cb_fn) (hackrf_transfer *transfer, int)
 Block complete callback. More...
 
typedef void(* hackrf_flush_cb_fn) (void *flush_ctx, int)
 Flush (end of transmission) callback. More...
 

Enumerations

enum  sweep_style {
  LINEAR = 0 ,
  INTERLEAVED = 1
}
 sweep mode enum More...
 

Functions

int hackrf_start_rx (hackrf_device *device, hackrf_sample_block_cb_fn callback, void *rx_ctx)
 Start receiving. More...
 
int hackrf_stop_rx (hackrf_device *device)
 Stop receiving. More...
 
int hackrf_start_tx (hackrf_device *device, hackrf_sample_block_cb_fn callback, void *tx_ctx)
 Start transmitting. More...
 
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. More...
 
int hackrf_enable_tx_flush (hackrf_device *device, hackrf_flush_cb_fn callback, void *flush_ctx)
 Setup flush (end-of-transmission) callback. More...
 
int hackrf_stop_tx (hackrf_device *device)
 Stop transmission. More...
 
int hackrf_set_tx_underrun_limit (hackrf_device *device, uint32_t value)
 Set transmit underrun limit. More...
 
int hackrf_set_rx_overrun_limit (hackrf_device *device, uint32_t value)
 Set receive overrun limit. More...
 
int hackrf_is_streaming (hackrf_device *device)
 Query device streaming status. More...
 
int hackrf_set_hw_sync_mode (hackrf_device *device, const uint8_t value)
 Set hardware sync mode (hardware triggering) More...
 
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. More...
 
int hackrf_start_rx_sweep (hackrf_device *device, hackrf_sample_block_cb_fn callback, void *rx_ctx)
 Start RX sweep. More...
 

Detailed Description

Streaming

There are 3 different streaming modes supported by HackRF:

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:

Steps for starting an RX or TX operation:

Data is transfered 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 adivsed 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 iteract 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.

// Transmit a 440Hz triangle wave through FM (144.5MHz) using the libhackrf API
// Copyright (c) 2022 László Baráth "Uncle Dino" HA7DN <https://github.com/Sasszem>
#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() {
// Generate an f_mod frequency triangle wave in the -1 - 1 region
// each call to this function generates a single sample
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; // we need to go from -1 to 1 in half the period
if (samples_generated < period_in_samples / 2 )
state += step;
else
state -= step;
// this way we don't need to modulo it
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;
int transfer_callback(hackrf_transfer *transfer) {
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());
// any IQ samples can be written here, now I'm doing FM modulation with a triangle wave
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(hackrf_transfer *transfer) {
pthread_mutex_lock(&mutex);
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
}
int main() {
hackrf_device *device = NULL;
hackrf_open(&device);
hackrf_set_freq(device, 144500000);
hackrf_set_sample_rate(device, 10000000);
hackrf_set_txvga_gain(device, 20);
// hackrf_set_tx_underrun_limit(device, 100000); // new-ish library function, not always available
hackrf_enable_tx_flush(device, flush_callback, NULL);
hackrf_start_tx(device, transfer_callback, NULL);
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex); // wait fo transfer to complete
hackrf_stop_tx(device);
hackrf_close(device);
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:858
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:867
int buffer_length
length of data buffer in bytes
Definition: hackrf.h:873
uint8_t * buffer
transfer data buffer (interleaved 8 bit I/Q samples)
Definition: hackrf.h:871
int valid_length
number of buffer bytes that were transferred
Definition: hackrf.h:875

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 faliure, 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 especialy 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 synchrnonize on an external trigger source.

Macro Definition Documentation

◆ BYTES_PER_BLOCK

#define BYTES_PER_BLOCK   16384

◆ MAX_SWEEP_RANGES

#define MAX_SWEEP_RANGES   10

◆ SAMPLES_PER_BLOCK

#define SAMPLES_PER_BLOCK   8192

Typedef Documentation

◆ 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

typedef int(* hackrf_sample_block_cb_fn) (hackrf_transfer *transfer)

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 transfered data.

Enumeration Type Documentation

◆ 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.

Function Documentation

◆ hackrf_enable_tx_flush()

int hackrf_enable_tx_flush ( hackrf_device device,
hackrf_flush_cb_fn  callback,
void *  flush_ctx 
)

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
devicedevice to configure
callbackcallback to call when all transfers were completed
flush_ctxcontext (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 
)

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
devicedevice to configure
frequency_listlist of start-stop frequency pairs in MHz
num_rangeslength of array frequency_list (in pairs, so total array length / 2!). Must be less than MAX_SWEEP_RANGES
num_bytesnumber of bytes to capture per tuning, must be a multiple of BYTES_PER_BLOCK
step_widthwidth of each tuning step in Hz
offsetfrequency offset added to tuned frequencies. sample_rate / 2 is a good value
stylesweep style
Returns
HACKRF_SUCCESS on success or hackrf_error variant

◆ hackrf_is_streaming()

int hackrf_is_streaming ( hackrf_device device)
Parameters
devicedevice to query
Returns
HACKRF_TRUE if the device is streaming, else one of HACKRF_ERROR_STREAMING_THREAD_ERR, HACKRF_ERROR_STREAMING_STOPPED or HACKRF_ERROR_STREAMING_EXIT_CALLED

◆ hackrf_set_hw_sync_mode()

int hackrf_set_hw_sync_mode ( hackrf_device device,
const uint8_t  value 
)

See the documentation on hardware triggering for details

Requires USB API version 0x0102 or above!

Parameters
devicedevice to configure
valueenable (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 
)

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
devicedevice to configure
valuenumber of samples to wait before auto-stopping
Returns
HACKRF_SUCCESS on success or hackrf_error variant

◆ hackrf_set_tx_block_complete_callback()

int hackrf_set_tx_block_complete_callback ( hackrf_device device,
hackrf_tx_block_complete_cb_fn  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
devicedevice to configure
callbackcallback 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 
)

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
devicedevice to configure
valuenumber of samples to wait before auto-stopping
Returns
HACKRF_SUCCESS on success or hackrf_error variant

◆ hackrf_start_rx()

int hackrf_start_rx ( hackrf_device device,
hackrf_sample_block_cb_fn  callback,
void *  rx_ctx 
)

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
devicedevice to configure
callbackrx_callback
rx_ctxUser 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()

int hackrf_start_rx_sweep ( hackrf_device device,
hackrf_sample_block_cb_fn  callback,
void *  rx_ctx 
)

See hackrf_init_sweep for more info

Requires USB API version 0x0104 or above!

Parameters
devicedevice to start sweeping
callbackrx callback processing the received data
rx_ctxUser 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()

int hackrf_start_tx ( hackrf_device device,
hackrf_sample_block_cb_fn  callback,
void *  tx_ctx 
)

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
devicedevice to configure
callbacktx_callback
tx_ctxUser 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()

int hackrf_stop_rx ( hackrf_device device)
Parameters
devicedevice to stop RX on
Returns
HACKRF_SUCCESS on success or hackrf_error variant

◆ hackrf_stop_tx()

int hackrf_stop_tx ( hackrf_device device)
Parameters
devicedevice to stop TX on
Returns
HACKRF_SUCCESS on success or hackrf_error variant