libbladeRF: Synchronous Interface: RX with Metadata

| libbladeRF 2.4.1-git Nuand bladeRF library

Synchronous Interface: RX with Metadata

This page describes how to use the bladeRF Synchronous Interface to receive samples with their associated timestamp, and scheduling reception at a specific timestamp.

The timestamp value is associated with free-running counter in the FPGA, incrementing at the sample rate specified by bladerf_set_sample_rate(), with each incoming sample.

## Configuring the Synchronous Interface

When timestamps are desired, one must enable metadata support. This is done through the `format` parameter of the bladerf_sync_config() function:

/* Configure the device's RX for use with the sync interface.
* SC16 Q11 samples *with* metadata are used. */
status = bladerf_sync_config(dev, BLADERF_RX_X1,
BLADERF_FORMAT_SC16_Q11_META, 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 error;
}
BLADERF_RX_X1
@ BLADERF_RX_X1
Definition: libbladeRF.h:715
bladerf_sync_config
API_EXPORT int CALL_CONV bladerf_sync_config(struct bladerf *dev, bladerf_channel_layout layout, bladerf_format format, unsigned int num_buffers, unsigned int buffer_size, unsigned int num_transfers, unsigned int stream_timeout)
bladerf_strerror
API_EXPORT const char *CALL_CONV bladerf_strerror(int error)
BLADERF_FORMAT_SC16_Q11_META
@ BLADERF_FORMAT_SC16_Q11_META
Definition: libbladeRF.h:2161

Descriptions of the other parameters may be found in the Synchronous Interface: Basic usage without metadata page.

Remember to enable the front end via bladerf_enable_module() after calling bladerf_sync_config(), and before attempting to call bladerf_sync_rx().

Also ensure that bladerf_sync_config() has been called before attempting to read timestamps via bladerf_get_timestamp().

When using the BLADERF_FORMAT_SC16_Q11_META format, a bladerf_metadata structure must be passed to bladerf_sync_rx(). One should zero the contents of this structure prior to using it.

## Receiving Samples Immediately with Timestamps

As shown in the following snippet, the bladerf_metadata structure (`meta`) is first zeroed out, and then the metadata flags (`meta.flags`) are set to BLADERF_META_FLAG_RX_NOW. This flag denotes that RX samples should returned immediately, rather than scheduled for a specific RX timestamp.

Once bladerf_sync_rx() returns, the bladerf_metadata.timestamp field may be read (`meta.timestamp`). Under normal conditions, this timestamp is associated with the first sample in the provided buffer, with the remaining samples in the buffer being contiguous (i.e., there is no timestamp gap between them). The bladerf_metadata.actual_count field should indicate that the requested number of samples have been read into the supplied buffer.

However, if an RX overrun has occurred, as indicated by the BLADERF_META_STATUS_OVERRUN bit being set in the bladerf_metadata.status field (`meta.status`), a sample discontinuity will have occurred. Generally, this overrun condition may occur if the time between calls to bladerf_sync_rx() is too long, or too few/small buffers were provided to bladerf_sync_config().

In the case of an overrun, the bladerf_metadata's `actual_count` value is set to the number of contiguous samples read into the supplied buffer. This value will be less than the requested number of samples, so be sure not to read past this number of samples.

int sync_rx_meta_now_example(struct bladerf *dev,
int16_t *samples,
unsigned int samples_len,
unsigned int rx_count,
unsigned int timeout_ms)
{
int status = 0;
struct bladerf_metadata meta;
unsigned int i;
/* Perform a read immediately, and have the bladerf_sync_rx function
* provide the timestamp of the read samples */
memset(&meta, 0, sizeof(meta));
meta.flags = BLADERF_META_FLAG_RX_NOW;
/* Receive samples and do work on them */
for (i = 0; i < rx_count && status == 0; i++) {
status = bladerf_sync_rx(dev, samples, samples_len, &meta, timeout_ms);
if (status != 0) {
fprintf(stderr, "RX \"now\" failed: %s\n\n",
bladerf_strerror(status));
} else if (meta.status & BLADERF_META_STATUS_OVERRUN) {
fprintf(stderr, "Overrun detected. %u valid samples were read.\n",
meta.actual_count);
} else {
printf("RX'd %u samples at t=0x%016" PRIx64 "\n", meta.actual_count,
meta.timestamp);
fflush(stdout);
/* ... Do work on samples here...
*
* status = process_samples(samples, samples_len);
*/
}
}
return status;
}
bladerf_sync_rx
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)
BLADERF_META_STATUS_OVERRUN
#define BLADERF_META_STATUS_OVERRUN
Definition: libbladeRF.h:2224
BLADERF_META_FLAG_RX_NOW
#define BLADERF_META_FLAG_RX_NOW
Definition: libbladeRF.h:2322
bladerf_metadata
Definition: libbladeRF.h:2349
bladerf_metadata::status
uint32_t status
Definition: libbladeRF.h:2377

## Receiving Samples at a Specified Timestamps

To schedule reception at a specific timestamp, set the bladerf_metadata.flags field to 0, and write the desired timestamp to the bladerf_metadata.timestamp field.

A call to bladerf_sync_rx() may then be used to read the specified number of samples at the timestamp indicated in metadata structure. Ensure that the timeout provided to bladerf_sync_rx() is sufficiently large, such that the function call does not time out before the desired timestamp occurs.

When scheduling the first RX, it is generally helpful to first read the stream's timestamp counter and advance it by a known duration. This is shown in the below example code.

Note that the caller is responsible for advancing the timestamp field each time bladerf_sync_rx() is called.

int sync_rx_meta_sched_example(struct bladerf *dev,
int16_t *samples,
unsigned int samples_len,
unsigned int rx_count,
unsigned int samplerate,
unsigned int timeout_ms)
{
int status;
struct bladerf_metadata meta;
unsigned int i;
/* 150 ms timestamp increment */
const uint64_t ts_inc_150ms = ((uint64_t)samplerate) * 150 / 1000;
/* 1 ms timestamp increment */
const uint64_t ts_inc_1ms = samplerate / 1000;
memset(&meta, 0, sizeof(meta));
/* Perform scheduled RXs by having meta.timestamp set appropriately
* and ensuring the BLADERF_META_FLAG_RX_NOW flag is cleared. */
meta.flags = 0;
/* Retrieve the current timestamp */
status = bladerf_get_timestamp(dev, BLADERF_RX, &meta.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 first RX to be 150 ms in the future */
meta.timestamp += ts_inc_150ms;
/* Receive samples and do work on them */
for (i = 0; i < rx_count && status == 0; i++) {
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("RX'd %u samples at t=0x%016" PRIx64 "\n", meta.actual_count,
meta.timestamp);
fflush(stdout);
/* ... Process samples here ... */
/* Schedule the next RX to be 1 ms after the end of the samples we
* just processed */
meta.timestamp += samples_len + ts_inc_1ms;
}
}
return status;
}
BLADERF_RX
@ BLADERF_RX
Definition: libbladeRF.h:707
bladerf_get_timestamp
API_EXPORT int CALL_CONV bladerf_get_timestamp(struct bladerf *dev, bladerf_direction dir, bladerf_timestamp *timestamp)

The description of the BLADERF_META_STATUS_OVERRUN flag and the the bladerf_metadata.actual_count field from the previous section applies here as well.