USRP Hardware Driver and USRP Manual  Version: 003.010.002.000-0-tagged
UHD and USRP Manual
UHD - C API

Introduction

Alongside its C++ API, UHD provides a C API wrapper for the uhd::usrp::multi_usrp and uhd::usrp_clock::multi_usrp_clock classes, as well as their associated classes and structs. Other important UHD functions are also included in this API. To use this API, simply:

#include <uhd.h>

...and all UHD C-level structs and functions will be available to you. The sections below give more detail on the key features of the C API.

Keep in mind that the C-API is a wrapper around the C++ API, so performance can never exceed that of the C++ API.

API Philosophy

The C API was designed to mirror the C++ API as closely as possible. This means that most API calls are mapped 1:1 from C++ to C, and the documentation for the C++ API can still be useful and should be considered when using the C API. For example, the uhd_usrp_set_rx_antenna() and uhd::multi_usrp::set_rx_antenna() calls do the same thing under the hood, and thus, if the behaviour of the C API call is unclear, consulting the corresponding C++ API call documentation can be helpful.

There are some C++ concepts that don't map into C easily, though. Among those are object storage, which is solved by using handles (see C-Level Handles) and exceptions, which are translated into error codes (see Error Codes).

C-Level Handles

Most of the UHD classes that can be accessed on the C level are done so through handles, which internally store the C++ representation and allow access to internal values through helper functions.

All handles have associated *_make() and *_free() functions. After creating a handle, it must be passed through its make() function before it can be used in your program. Before the program terminates, you must pass the handle into its free() function, or your program will have a memory leak. The example below shows the proper usage of an RX metadata handle over the course of its lifetime, from instantiation to destruction.

// Streaming here puts useful information into metadata
time_t full_secs;
double frac_secs;
uhd_rx_metadata_time_spec(md, &full_secs, &frac_secs);

Again, make sure to pass your handle into a make() function before using it, or you will run into undefined behavior. Also be careful not to use the handle after passing it into a free() function, or your program will segfault.

Error Codes

As C cannot handle C++ runtime exceptions, UHD's C wrapper functions catch all exceptions and translate them into error codes, which are returned by each function. Any output variables are passed in as pointers into the function, which will set them internally.

Each uhd::runtime_error has a corresponding uhd_error value. Separate error codes indicate that a boost::exception or std::exception has been thrown, and any other exceptions are indicated by a catch-all UHD_ERROR_UNKNOWN code.

All UHD C-level handles store the string representation of the last C++ exception thrown internally. These handles have corresponding *_get_last_error() functions that will place the error string into a supplied string buffer.

For example, if a USRP device's handle throws an exception internally, the following code can access its error info:

char err_msg[256];
double gain;
// USRP configuration done here
uhd_error error_code = uhd_usrp_get_rx_gain(usrp, 0, "", &gain);
if(error_code){
uhd_usrp_get_last_error(usrp, err_msg, 256);
fprintf(stderr, "Error code %d: %s\n", error_code, err_msg);
}

All error codes can be found in <uhd/error.h>.

Example Code

UHD provides two examples that demonstrate the typical use case of the C API: RX and TX streaming. The rx_samples_c example is a simplified C version of rx_samples_to_file, and the tx_samples_c example is a simplified C version of tx_waveforms. These examples can be easily adapted or serve as a starting point for your own UHD C applications.