libbladeRF  1.1.0
Nuand bladeRF library
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups
Functions
Synchronous data transmission and reception

Functions

API_EXPORT int CALL_CONV bladerf_sync_config (struct bladerf *dev, bladerf_module module, bladerf_format format, unsigned int num_buffers, unsigned int buffer_size, unsigned int num_transfers, unsigned int stream_timeout)
 
API_EXPORT int CALL_CONV bladerf_sync_tx (struct bladerf *dev, void *samples, unsigned int num_samples, struct bladerf_metadata *metadata, unsigned int timeout_ms)
 
API_EXPORT int CALL_CONV bladerf_sync_rx (struct bladerf *dev, void *samples, unsigned int num_samples, struct bladerf_metadata *metadata, unsigned int timeout_ms)
 

Detailed Description

This group of functions presents synchronous, blocking calls (with optional timeouts) for transmitting and receiving samples.

The synchronous interface is built atop the asynchronous interface, and is generally less complex and easier to work with. It alleviates the need to explicitly spawn threads (it is done under the hood) and manually manage sample buffers.

Under the hood, this interface spawns worker threads to handle an asynchronous stream and perform thread-safe buffer management.

These functions are thread-safe.

RX and TX without metadata

Below is the general process for using this interface to transmit and receive SC16 Q11 samples, without metadata.

int sync_rx_example(struct bladerf *dev)
{
int status, ret;
bool done = false;
/* "User" samples buffers and their associated sizes, in units of samples.
* Recall that one sample = two int16_t values. */
int16_t *rx_samples = NULL;
int16_t *tx_samples = NULL;
const unsigned int samples_len = 10000; /* May be any (reasonable) size */
/* These items configure the underlying asynch stream used by the sync
* interface. The "buffer" here refers to those used internally by worker
* threads, not the `samples` buffer above. */
const unsigned int num_buffers = 16;
const unsigned int buffer_size = 8192; /* Must be a multiple of 1024 */
const unsigned int num_transfers = 8;
const unsigned int timeout_ms = 3500;
/* Allocate a buffer to store received samples in */
rx_samples = malloc(samples_len * 2 * sizeof(int16_t));
if (rx_samples == NULL) {
perror("malloc");
}
/* Allocate a buffer to prepare transmit data in */
tx_samples = malloc(samples_len * 2 * sizeof(int16_t));
if (tx_samples == NULL) {
perror("malloc");
free(rx_samples);
}
/* Configure both the device's RX and TX modules for use with the synchronous
* interface. SC16 Q11 samples *without* metadata are used. */
status = bladerf_sync_config(dev,
num_buffers,
buffer_size,
num_transfers,
timeout_ms);
if (status != 0) {
fprintf(stderr, "Failed to configure RX sync interface: %s\n",
bladerf_strerror(status));
goto out;
}
status = bladerf_sync_config(dev,
num_buffers,
buffer_size,
num_transfers,
timeout_ms);
if (status != 0) {
fprintf(stderr, "Failed to configure TX sync interface: %s\n",
bladerf_strerror(status));
goto out;
}
/* We must always enable the modules *after* calling bladerf_sync_config(),
* and *before* attempting to RX or TX samples. */
if (status != 0) {
fprintf(stderr, "Failed to enable RX module: %s\n",
bladerf_strerror(status));
goto out;
}
if (status != 0) {
fprintf(stderr, "Failed to enable RX module: %s\n",
bladerf_strerror(status));
goto out;
}
/* Receive samples and do work on them and then transmit a response.
*
* Note we transmit more than `buffer_size` samples to ensure that our
* samples are written to the FPGA. (The samples are sent when the
* synchronous interface's internal buffer of `buffer_size` samples is
* filled.) This is generally not nececssary if you are continuously
* streaming TX data. Otherwise, you may need to zero-pad your TX data to
* achieve this.
*/
while (status == 0 && !done) {
status = bladerf_sync_rx(dev, rx_samples, samples_len, NULL, 5000);
if (status == 0) {
done = do_work(rx_samples, samples_len,
tx_samples, samples_len);
if (!done) {
status = bladerf_sync_tx(dev, tx_samples, samples_len,
NULL, 5000);
if (status != 0) {
fprintf(stderr, "Failed to TX samples: %s\n",
bladerf_strerror(status));
}
}
} else {
fprintf(stderr, "Failed to RX samples: %s\n",
bladerf_strerror(status));
}
}
if (status == 0) {
/* Wait a few seconds for any remaining TX samples to finish */
usleep(2000000);
}
out:
ret = status;
/* Disable RX module, shutting down our underlying RX stream */
if (status != 0) {
fprintf(stderr, "Failed to disable RX module: %s\n",
bladerf_strerror(status));
}
/* Disable TX module, shutting down our underlying TX stream */
if (status != 0) {
fprintf(stderr, "Failed to disable TX module: %s\n",
bladerf_strerror(status));
}
/* Free up our resources */
free(rx_samples);
free(tx_samples);
return ret;
}

To run in a half-duplex mode of operation, simply remove the RX or TX specific portions from the above example.

RX with metadata

By using the BLADERF_FORMAT_SC16_Q11_META format with the synchronous interface, one can read the timestamp associated with a received buffer of data, or schedule to read a specific number of samples at a desired timestamp. Additionally, the metadata indicates if an overrun was detected, and the number of samples actually copied into the buffer before the discontinuity caused by the overrun.

int sync_rx_meta_example(struct bladerf *dev, unsigned int samplerate)
{
int status, ret;
struct bladerf_metadata meta;
unsigned int i;
/* "User" buffer that we read samples into and do work on, and its
* associated size, in units of samples. Recall that one sample = two
* int16_t values. */
int16_t *samples;
const unsigned int samples_len = 4096;
/* These items configure the underlying asynch stream used by the the sync
* interface. The "buffer" here refers to those used internally by worker
* threads, not the `samples` buffer above. */
const unsigned int num_buffers = 16;
const unsigned int buffer_size = 16384;
const unsigned int num_transfers = 8;
const unsigned int timeout_ms = 5000;
memset(&meta, 0, sizeof(meta));
samples = malloc(samples_len * 2 * sizeof(int16_t));
if (samples == NULL) {
perror("malloc");
}
/* Configure the device's RX module for use with the sync interface.
* SC16 Q11 samples *with* metadata are used. */
status = bladerf_sync_config(dev,
num_buffers,
buffer_size,
num_transfers,
timeout_ms);
if (status != 0) {
fprintf(stderr, "Failed to configure RX sync interface: %s\n",
bladerf_strerror(status));
goto out;
}
/* We must always enable the RX module *after* calling
* bladerf_sync_config(), and *before* attempting to RX samples via
* bladerf_sync_rx(). */
if (status != 0) {
fprintf(stderr, "Failed to enable RX module: %s\n",
bladerf_strerror(status));
goto out;
}
/* Retrieve the current timestamp */
if (status != 0) {
fprintf(stderr, "Failed to get current RX timestamp: %s\n",
bladerf_strerror(status));
} else {
printf("Current RX timestamp: 0x%016"PRIx64"\n", meta.timestamp);
}
/* Schedule the first reception to be 2 seconds in the future */
meta.timestamp += 2 * samplerate;
/* Receive samples and do work on them */
for (i = 0; i < 5 && status == 0; i++) {
/* Perform a scheduled RX by having meta.timestamp set appropriately
* and ensuring the BLADERF_META_FLAG_RX_NOW flag is cleared. */
printf("Calling bladerf_sync_rx() for read @ t=0x%016"PRIx64"...\n",
meta.timestamp);
fflush(stdout);
status = bladerf_sync_rx(dev, samples, samples_len,
&meta, 2 * timeout_ms);
if (status != 0) {
fprintf(stderr, "Scheduled RX failed: %s\n\n",
bladerf_strerror(status));
} else if (meta.status & BLADERF_META_STATUS_OVERRUN) {
fprintf(stderr, "Overrun detected in scheduled RX. "
"%u valid samples were read.\n\n", meta.actual_count);
} else {
printf("Got %u samples at t=0x%016"PRIx64"\n\n",
meta.actual_count, meta.timestamp);
}
/* Perform a read immediately, and have the bladerf_sync_rx function
* provide the timestamp of the read samples */
printf("Calling bladerf_sync_rx() for an immediate read...\n");
status = bladerf_sync_rx(dev, samples, samples_len,
&meta, 2 * timeout_ms);
if (status != 0) {
fprintf(stderr, "Immediate RX failed: %s\n\n",
bladerf_strerror(status));
} else if (meta.status & BLADERF_META_STATUS_OVERRUN) {
fprintf(stderr, "Overrun detected in immediate RX. "
"%u valid samples were read.\n\n", meta.actual_count);
} else {
printf("Got %u samples at t=0x%016"PRIx64"\n\n",
meta.actual_count, meta.timestamp);
}
/* Schedule the next read 1.25 seconds into the future */
meta.timestamp += samplerate + samplerate / 4;
}
out:
ret = status;
/* Disable RX module, shutting down our underlying RX stream */
if (status != 0) {
fprintf(stderr, "Failed to disable RX module: %s\n",
bladerf_strerror(status));
}
/* Free up our resources */
free(samples);
return ret;
}

TX with metadata

Using the synchronous interface with the BLADERF_FORMAT_SC16_Q11_META format allows for bursts of samples to be scheduled for transmissions. Between these bursts, the device transmits I=0, Q=0.

To begin a burst, call bladerf_sync_tx() with the metadata's BLADERF_META_FLAG_TX_BURST_START flag set, the desired timestamp set in the bladerf_metadata structure, and any number of samples.

You may then continue calling bladerf_sync_tx(), without needing to update the timestamp field in the bladerf_metadata structure. For these calls, no flags should be set.

On the final bladerf_sync_tx() call for the burst, set the BLADERF_META_FLAG_TX_BURST_END flag, and ensure the final two samples provided to this function are 0.

It is valid to send the entire burst with a single function call, with both flags set.

int sync_tx_meta_example(struct bladerf *dev, bool use_tx_now,
unsigned int samplerate)
{
int status, ret;
struct bladerf_metadata meta;
unsigned int i, j;
int16_t zeros[] = { 0, 0, 0, 0 };
/* "User" buffer that we read samples into and do work on, and its
* associated size, in units of samples. Recall that one sample = two
* int16_t values. */
int16_t *samples;
const unsigned int samples_len = 60;
int16_t *to_tx;
unsigned int num_to_tx;
/* These items configure the underlying asynch stream used by the the sync
* interface. The "buffer" here refers to those used internally by worker
* threads, not the `samples` buffer above. */
const unsigned int num_buffers = 16;
const unsigned int buffer_size = 1024;
const unsigned int num_transfers = 8;
const unsigned int timeout_ms = 10000;
memset(&meta, 0, sizeof(meta));
samples = calloc(samples_len, 2 * sizeof(int16_t));
if (samples == NULL) {
perror("malloc");
}
/* Configure the device's TX module for use with the sync interface.
* SC16 Q11 samples *with* metadata are used. */
status = bladerf_sync_config(dev,
num_buffers,
buffer_size,
num_transfers,
timeout_ms);
if (status != 0) {
fprintf(stderr, "Failed to configure RX sync interface: %s\n",
bladerf_strerror(status));
goto out;
}
/* We must always enable the TX module *after* calling
* bladerf_sync_config(), and *before* attempting to TX samples via
* bladerf_sync_tx(). */
if (status != 0) {
fprintf(stderr, "Failed to enable RX module: %s\n",
bladerf_strerror(status));
goto out;
}
if (use_tx_now) {
usleep(250);
} else {
/* Retrieve the current timestamp */
if (status != 0) {
fprintf(stderr, "Failed to get current TX timestamp: %s\n",
bladerf_strerror(status));
goto out;
} else {
printf("\nCurrent TX timestamp: 0x%016"PRIx64"\n", meta.timestamp);
}
/* Schedule first burst 250 ms into the future */
meta.timestamp += samplerate / 4;
}
for (i = 0; i < 5; i++) {
/* The burst is broken up into multiple calls here simply to show that
* it is possible. The meta.timestamp value only needs to be provided
* to the call that starts the burst. The meta.timestamp field is not
* read when using the TX_NOW flag.
*
* Note that with a sufficiently large buffer, one could send the entire
* burst in a single function call, setting meta.flags to:
* BLADERF_META_FLAG_TX_BURST_START | BLADERF_META_FLAG_TX_BURST_END
*/
for (j = 0; j < 4; j++) {
switch (j) {
case 0:
get_samples(samples, samples_len);
if (use_tx_now) {
}
to_tx = samples;
num_to_tx = samples_len;
break;
case 1:
case 2:
get_samples(samples, samples_len);
meta.flags = 0;
to_tx = samples;
num_to_tx = samples_len;
break;
case 3:
/* Ensure the burst ends with 0-valued samples, as required
* by the API */
to_tx = zeros;
num_to_tx = sizeof(zeros) / sizeof(zeros[0]) / 2;
break;
}
status = bladerf_sync_tx(dev, to_tx, num_to_tx,
&meta, 2 * timeout_ms);
if (status != 0) {
fprintf(stderr, "Failed to TX sample chunk %u/4: %s\n", j + 1,
bladerf_strerror(status));
goto out;
}
}
/* Schedule next burst to be 1 ms after the end of this burst */
if (use_tx_now) {
usleep(1000);
} else {
meta.timestamp += (3 * samples_len) + 2 + samplerate / 1000;
}
}
/* Wait a couple seconds to ensure samples have been transmitted */
printf("Waiting for bursts to finish...\n");
usleep(2000000);
out:
ret = status;
/* Disable TX module, shutting down our underlying TX stream */
if (status != 0) {
fprintf(stderr, "Failed to disable TX module: %s\n",
bladerf_strerror(status));
}
free(samples);
return ret;
}

Function Documentation

API_EXPORT int CALL_CONV bladerf_sync_config ( struct bladerf *  dev,
bladerf_module  module,
bladerf_format  format,
unsigned int  num_buffers,
unsigned int  buffer_size,
unsigned int  num_transfers,
unsigned int  stream_timeout 
)

(Re)Configure a device for synchronous transmission or reception

This function sets up the device for the specified format and initializes the underlying asynchronous stream parameters

This function does not call bladerf_enable_module(). The API user is responsible for enabling/disable modules when desired.

Note that (re)configuring BLADERF_MODULE_TX does not affect the BLADERF_MODULE_RX modules, and vice versa. This call configures each module independently.

Memory allocated by this function will be deallocated when bladerf_close() is called.

See the bladerf_init_stream() documentation for information on determining appropriate values for buffers_size, num_transfers, and stream_timeout. The num_buffers parameter should generally be increased as the amount of work done between bladerf_sync_rx() or bladerf_sync_tx() calls increases.

Parameters
devDevice to configure
moduleModule to use with synchronous interface
formatFormat to use in synchronous data transfers
num_buffersThe number of buffers to use in the underlying data stream. This must be greater than the num_xfers parameter.
buffer_sizeThe size of the underlying stream buffers, in samples. This value must be a multiple of 1024. Note that samples are only transferred when a buffer of this size is filled.
num_transfersThe number of active USB transfers that may be in-flight at any given time. If unsure of what to use here, try values of 4, 8, or 16.
stream_timeoutTimeout (milliseconds) for transfers in the underlying data stream.
Returns
0 on success, BLADERF_ERR_UNSUPPORTED if libbladeRF is not built with support for this functionality, or a value from Error codes list on failures.
API_EXPORT int CALL_CONV bladerf_sync_rx ( struct bladerf *  dev,
void *  samples,
unsigned int  num_samples,
struct bladerf_metadata metadata,
unsigned int  timeout_ms 
)

Receive IQ samples.

Under the hood, this call starts up an underlying asynchronous stream as needed. This stream can be stopped by disabling the RX module. (See bladerf_enable_module for more details.)

Parameters
[in]devDevice handle
[out]samplesBuffer to store samples in. The caller is responsible for ensuring this buffer is sufficiently large for the number of samples requested, considering the size of the sample format being used.
[in]num_samplesNumber of samples to read
[out]metadataSample metadata. This must be provided when using the BLADERF_FORMAT_SC16_Q11_META format, but may be NULL when the interface is configured for the BLADERF_FORMAT_SC16_Q11 format.
[in]timeout_msTimeout (milliseconds) for this call to complete. Zero implies "infinite."
Precondition
A bladerf_sync_config() call has been to configure the device for synchronous data transfer.
A call to bladerf_enable_module() should be made before attempting to receive samples. Failing to do this may result in timeouts and other errors.
Returns
0 on success, BLADERF_ERR_UNSUPPORTED if libbladeRF is not built with support for this functionality, or a value from Error codes list on failures.
API_EXPORT int CALL_CONV bladerf_sync_tx ( struct bladerf *  dev,
void *  samples,
unsigned int  num_samples,
struct bladerf_metadata metadata,
unsigned int  timeout_ms 
)

Transmit IQ samples.

Under the hood, this call starts up an underlying asynchronous stream as needed. This stream can be stopped by disabling the TX module. (See bladerf_enable_module for more details.)

Samples will only be sent to the FPGA when a buffer have been filled. The number of samples required to fill a buffer corresponds to the buffer_size parameter passed to bladerf_sync_config().

Parameters
[in]devDevice handle
[in]samplesArray of samples
[in]num_samplesNumber of samples to write
[in]metadataSample metadata. This must be provided when using the BLADERF_FORMAT_SC16_Q11_META format, but may be NULL when the interface is configured for the BLADERF_FORMAT_SC16_Q11 format.
[in]timeout_msTimeout (milliseconds) for this call to complete. Zero implies "infinite."
Precondition
A bladerf_sync_config() call has been to configure the device for synchronous data transfer.
A call to bladerf_enable_module() should be made before attempting to transmit samples. Failing to do this may result in timeouts and other errors.
Returns
0 on success, BLADERF_ERR_UNSUPPORTED if libbladeRF is not built with support for this functionality, or a value from Error codes list on failures.