Difference between revisions of "Getting Started with UHD and C++"

From Ettus Knowledge Base
Jump to: navigation, search
(CMake)
 
(65 intermediate revisions by the same user not shown)
Line 8: Line 8:
 
!Details
 
!Details
 
|-
 
|-
|style="text-algin:center;"| 2016-05-01   
+
|style="text-align:center;"| 2016-05-01   
|style="text-algin:center;"| Neel Pandeya<br> Nate Temple  
+
|style="text-align:center;"| Neel Pandeya<br> Nate Temple  
|style="text-algin:center;"| Initial creation
+
|style="text-align:center;"| Initial creation
 
|}
 
|}
  
Line 17: Line 17:
  
 
==Overview==
 
==Overview==
This Application Note will walk through building a basic C++ program with UHD. This program will initialize, configure the USRP device, set the sample rate, frequency, gain, bandwidth, and select the antenna.  
+
This Application Note will walk through building a basic C++ program with UHD. This program will initialize, configure the USRP device, set the sample rate, frequency, gain, bandwidth, and select the antenna.
  
Example code:
+
==UHD Manual==
 +
The UHD Manual is hosted at [http://files.ettus.com/manual/index.html http://files.ettus.com/manual/index.html] and provides information on how to use the USRP devices and how to use the UHD API to connect to them through your own software. The manual is split into two parts: The device manual, and the UHD/API manual. The first part describes details of Ettus Research devices, motherboards and daughterboards, as well as aspects of using UHD. The second is meant for developers writing UHD-based applications, and includes descriptions of the API, sorted by namespaces, classes, and files.
  
 +
==Including Header Files==
 +
<syntaxhighlight lang="c++">
 
     #include <uhd/utils/thread_priority.hpp>
 
     #include <uhd/utils/thread_priority.hpp>
 
     #include <uhd/utils/safe_main.hpp>
 
     #include <uhd/utils/safe_main.hpp>
Line 30: Line 33:
 
     #include <boost/thread.hpp>
 
     #include <boost/thread.hpp>
 
     #include <iostream>
 
     #include <iostream>
   
+
</syntaxhighlight>
 +
 
 +
==UHD_SAFE_MAIN()==
 +
Defines a safe wrapper that places a catch-all around main. If an exception is thrown, it prints to stderr and returns.
 +
 
 +
<syntaxhighlight lang="c++">
 
     int UHD_SAFE_MAIN(int argc, char *argv[]) {
 
     int UHD_SAFE_MAIN(int argc, char *argv[]) {
         uhd::set_thread_priority_safe();
+
         ...
   
+
        std::string device_args("addr=192.168.10.2");
+
        std::string subdev("A:0");
+
        std::string ant("TX/RX");
+
        std::string ref("internal");
+
   
+
        double rate(1e6);
+
        double freq(915e6);
+
        double gain(10);
+
        double bw(1e6);
+
   
+
        //create a usrp device
+
        std::cout << std::endl;
+
        std::cout << boost::format("Creating the usrp device with: %s...") % device_args << std::endl;
+
        uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(device_args);
+
   
+
        // Lock mboard clocks
+
        std::cout << boost::format("Lock mboard clocks: %f") % ref << std::endl;
+
        usrp->set_clock_source(ref);
+
       
+
        //always select the subdevice first, the channel mapping affects the other settings
+
        std::cout << boost::format("subdev set to: %f") % subdev << std::endl;
+
        usrp->set_rx_subdev_spec(subdev);
+
        std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl;
+
   
+
        //set the sample rate
+
        if (rate <= 0.0) {
+
            std::cerr << "Please specify a valid sample rate" << std::endl;
+
            return ~0;
+
        }
+
   
+
        // set sample rate
+
        std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate / 1e6) << std::endl;
+
        usrp->set_rx_rate(rate);
+
        std::cout << boost::format("Actual RX Rate: %f Msps...") % (usrp->get_rx_rate() / 1e6) << std::endl << std::endl;
+
   
+
        // set freq
+
        std::cout << boost::format("Setting RX Freq: %f MHz...") % (freq / 1e6) << std::endl;
+
        uhd::tune_request_t tune_request(freq);
+
        usrp->set_rx_freq(tune_request);
+
        std::cout << boost::format("Actual RX Freq: %f MHz...") % (usrp->get_rx_freq() / 1e6) << std::endl << std::endl;
+
   
+
        // set the rf gain
+
        std::cout << boost::format("Setting RX Gain: %f dB...") % gain << std::endl;
+
        usrp->set_rx_gain(gain);
+
        std::cout << boost::format("Actual RX Gain: %f dB...") % usrp->get_rx_gain() << std::endl << std::endl;
+
   
+
        // set the IF filter bandwidth
+
        std::cout << boost::format("Setting RX Bandwidth: %f MHz...") % (bw / 1e6) << std::endl;
+
        usrp->set_rx_bandwidth(bw);
+
        std::cout << boost::format("Actual RX Bandwidth: %f MHz...") % (usrp->get_rx_bandwidth() / 1e6) << std::endl << std::endl;
+
   
+
        // set the antenna
+
        std::cout << boost::format("Setting RX Antenna: %s") % ant << std::endl;
+
        usrp->set_rx_antenna(ant);
+
        std::cout << boost::format("Actual RX Antenna: %s") % usrp->get_rx_antenna() << std::endl << std::endl;
+
   
+
        return EXIT_SUCCESS;
+
 
     }
 
     }
 +
</syntaxhighlight>
  
 +
==set_thread_priority_safe()==
 +
Set the scheduling priority on the current thread. Same as set_thread_priority but does not throw on failure.
  
 +
<syntaxhighlight lang="c++">
 +
    uhd::set_thread_priority_safe();
 +
</syntaxhighlight>
  
Use the uhd/host/examples/init_usrp/CMakeLists.txt file as template
+
==Create Variables==
- Add the names of your C++ source files to the add_executable(...) section
+
<syntaxhighlight lang="c++">
- Put both modified CMakeLists.txt file and C++ file into an empty folder
+
    std::string device_args("addr=192.168.10.2");
- Create a “build” folder and invoke CMake the usual way:  
+
    std::string subdev("A:0");
 +
    std::string ant("TX/RX");
 +
    std::string ref("internal");
  
     mkdir build
+
     double rate(1e6);
     cd build
+
     double freq(915e6);
     cmake ../
+
     double gain(10);
     make -j4
+
     double bw(1e6);
 +
</syntaxhighlight>
  
 +
==Creating a USRP Object==
 +
Make a new multi usrp from the device address. 
  
 +
<syntaxhighlight lang="c++">
 +
    uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(device_args);
 +
</syntaxhighlight>
  
 +
==Setting the Motherboard Clocks==
 +
Set the clock source for the usrp device. This sets the source for a 10 MHz reference clock. Typical options for source: internal, external, MIMO.
  
 +
<syntaxhighlight lang="c++">
 +
    usrp->set_clock_source(ref)
 +
</syntaxhighlight>
  
 +
==Selecting the Sub Device==
 +
Set the RX frontend specification. The subdev spec maps a physical part of a daughter-board to a channel number. Set the subdev spec before calling into any methods with a channel number. The subdev spec must be the same size across all motherboards. For more details on selecting the Sub Device, see the [http://files.ettus.com/manual/page_configuration.html#config_subdev Specifying the Subdevice] section of the UHD Manual.
  
 +
<syntaxhighlight lang="c++">
 +
    usrp->set_rx_subdev_spec(subdev)
 +
</syntaxhighlight>
  
<syntaxhighlight>
+
==Getting the selected Sub Device==
//
+
Get a printable summary for this USRP configuration.  
// Copyright 2011-2015 Ettus Research LLC
+
//
+
// This program is free software: you can redistribute it and/or modify
+
// it under the terms of the GNU General Public License as published by
+
// the Free Software Foundation, either version 3 of the License, or
+
// (at your option) any later version.
+
//
+
// This program is distributed in the hope that it will be useful,
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+
// GNU General Public License for more details.
+
//
+
// You should have received a copy of the GNU General Public License
+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
//
+
  
#include <uhd/utils/thread_priority.hpp>
+
<syntaxhighlight lang="c++">
#include <uhd/convert.hpp>
+
    usrp->get_pp_string()
#include <uhd/utils/safe_main.hpp>
+
</syntaxhighlight>
#include <uhd/usrp/multi_usrp.hpp>
+
#include <boost/program_options.hpp>
+
#include <boost/format.hpp>
+
#include <boost/thread/thread.hpp>
+
#include <boost/algorithm/string.hpp>
+
#include <boost/lexical_cast.hpp>
+
//#include <boost/atomic.hpp>
+
#include <iostream>
+
#include <complex>
+
#include <cstdlib>
+
  
namespace po = boost::program_options;
+
==Setting the Sample Rate==
 +
Set the RX sample rate. The rate is in Samples Per Second.
  
const double INIT_DELAY = 0.05;  // 50mS initial delay before transmit
+
<syntaxhighlight lang="c++">
//typedef boost::atomic<bool>   atomic_bool;
+
    usrp->set_rx_rate(rate)
// We'll fake atomic bools for now, for more backward compat.
+
</syntaxhighlight>
// This is just an example, after all.
+
typedef bool atomic_bool;
+
  
/***********************************************************************
+
==Getting the Sample Rate==
* Test result variables
+
Gets the RX sample rate. Returns the rate is in Samples Per Second.
**********************************************************************/
+
unsigned long long num_overflows = 0;
+
unsigned long long num_underflows = 0;
+
unsigned long long num_rx_samps = 0;
+
unsigned long long num_tx_samps = 0;
+
unsigned long long num_dropped_samps = 0;
+
unsigned long long num_seq_errors = 0;
+
unsigned long long num_timeouts = 0;
+
  
/***********************************************************************
+
<syntaxhighlight lang="c++">
* Benchmark RX Rate
+
    usrp->get_rx_rate()
**********************************************************************/
+
</syntaxhighlight>
void benchmark_rx_rate(
+
        uhd::usrp::multi_usrp::sptr usrp,
+
        const std::string &rx_cpu,
+
        uhd::rx_streamer::sptr rx_stream,
+
        bool random_nsamps,
+
        atomic_bool& burst_timer_elapsed
+
) {
+
    uhd::set_thread_priority_safe();
+
  
    //print pre-test summary
+
==Setting Center Frequency==
    std::cout << boost::format(
+
Create a tune request, with the RF frequency in Hz. Set the RX center frequency.
        "Testing receive rate %f Msps on %u channels"
+
    ) % (usrp->get_rx_rate()/1e6) % rx_stream->get_num_channels() << std::endl;
+
  
    //setup variables and allocate buffer
+
<syntaxhighlight lang="c++">
    uhd::rx_metadata_t md;
+
     uhd::tune_request_t tune_request(freq)
    const size_t max_samps_per_packet = rx_stream->get_max_num_samps();
+
     usrp->set_rx_freq(tune_request)
     std::vector<char> buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(rx_cpu));
+
</syntaxhighlight>
     std::vector<void *> buffs;
+
    for (size_t ch = 0; ch < rx_stream->get_num_channels(); ch++)
+
        buffs.push_back(&buff.front()); //same buffer for each channel
+
    bool had_an_overflow = false;
+
    uhd::time_spec_t last_time;
+
    const double rate = usrp->get_rx_rate();
+
  
    uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
+
==Getting the Center Frequency==
    cmd.time_spec = usrp->get_time_now() + uhd::time_spec_t(INIT_DELAY);
+
Get the RX center frequency. Returns the frequency in Hz.
    cmd.stream_now = (buffs.size() == 1);
+
    rx_stream->issue_stream_cmd(cmd);
+
  
     const float burst_pkt_time = std::max(0.100, (2 * max_samps_per_packet/rate));
+
<syntaxhighlight lang="c++">
    float recv_timeout = burst_pkt_time + INIT_DELAY;
+
     usrp->get_rx_freq()
 +
</syntaxhighlight>
  
    while (true) {
+
==Setting the RF Gain==
        //if (burst_timer_elapsed.load(boost::memory_order_relaxed)) {
+
Set the RX gain value for the specified gain element. For an empty name, distribute across all gain elements. Sets the gain in dB.
        if (burst_timer_elapsed) {
+
            rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS);
+
        }
+
        if (random_nsamps) {
+
            cmd.num_samps = rand() % max_samps_per_packet;
+
            rx_stream->issue_stream_cmd(cmd);
+
        }
+
        try {
+
            num_rx_samps += rx_stream->recv(buffs, max_samps_per_packet, md, recv_timeout)*rx_stream->get_num_channels();
+
            recv_timeout = burst_pkt_time;
+
        }
+
        catch (uhd::io_error &e) {
+
            std::cerr << "Caught an IO exception. " << std::endl;
+
            std::cerr << e.what() << std::endl;
+
            return;
+
        }
+
  
        //handle the error codes
+
<syntaxhighlight lang="c++">
        switch(md.error_code){
+
    usrp->set_rx_gain(gain)
        case uhd::rx_metadata_t::ERROR_CODE_NONE:
+
</syntaxhighlight>
            if (had_an_overflow){
+
                had_an_overflow = false;
+
                num_dropped_samps += (md.time_spec - last_time).to_ticks(rate);
+
            }
+
            break;
+
  
        // ERROR_CODE_OVERFLOW can indicate overflow or sequence error
+
==Reading the RF Gain==
        case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
+
Get the RX gain value for the specified gain element. For an empty name, sum across all gain elements. Returns the gain in dB.
            last_time = md.time_spec;
+
<syntaxhighlight lang="c++">
            had_an_overflow = true;
+
    usrp->get_rx_gain()
            // check out_of_sequence flag to see if it was a sequence error or overflow
+
</syntaxhighlight>
            if (!md.out_of_sequence)
+
                num_overflows++;
+
            break;
+
  
        case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
+
==Setting the IF Filter Bandwidth==
            // If we stopped the streamer, then we expect this at some point
+
Set the RX bandwidth on the frontend. Sets the bandwidth in Hz.
            //if (burst_timer_elapsed.load(boost::memory_order_relaxed)) {
+
            if (burst_timer_elapsed) {
+
                return;
+
            }
+
            std::cerr << "Receiver error: " << md.strerror() << ", continuing..." << std::endl;
+
            num_timeouts++;
+
            break;
+
  
            // Otherwise, it's an error
+
<syntaxhighlight lang="c++">
        default:
+
    usrp->set_rx_bandwidth(bw)
            std::cerr << "Receiver error: " << md.strerror() << std::endl;
+
</syntaxhighlight>
            std::cerr << "Unexpected error on recv, continuing..." << std::endl;
+
            break;
+
        }
+
    }
+
}
+
  
/***********************************************************************
+
==Getting the IF Filter Bandwidth==
* Benchmark TX Rate
+
Get the RX bandwidth on the frontend. Returns the bandwidth in Hz.
**********************************************************************/
+
void benchmark_tx_rate(
+
        uhd::usrp::multi_usrp::sptr usrp,
+
        const std::string &tx_cpu,
+
        uhd::tx_streamer::sptr tx_stream,
+
        atomic_bool& burst_timer_elapsed,
+
        bool random_nsamps=false
+
) {
+
    uhd::set_thread_priority_safe();
+
  
    //print pre-test summary
+
<syntaxhighlight lang="c++">
    std::cout << boost::format(
+
     usrp->get_rx_bandwidth()
        "Testing transmit rate %f Msps on %u channels"
+
</syntaxhighlight>
     ) % (usrp->get_tx_rate()/1e6) % tx_stream->get_num_channels() << std::endl;
+
  
    //setup variables and allocate buffer
+
==Selecting the Antenna==
    uhd::tx_metadata_t md;
+
Select the RX antenna on the frontend.  
    md.time_spec = usrp->get_time_now() + uhd::time_spec_t(INIT_DELAY);
+
    md.has_time_spec = (tx_stream->get_num_channels() > 1);
+
    const size_t max_samps_per_packet = tx_stream->get_max_num_samps();
+
    std::vector<char> buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(tx_cpu));
+
    std::vector<const void *> buffs;
+
    for (size_t ch = 0; ch < tx_stream->get_num_channels(); ch++)
+
        buffs.push_back(&buff.front()); //same buffer for each channel
+
    md.has_time_spec = (buffs.size() != 1);
+
  
     if (random_nsamps) {
+
<syntaxhighlight lang="c++">
        std::srand((unsigned int)time(NULL));
+
     usrp->set_rx_antenna(ant)
        //while (not burst_timer_elapsed.load(boost::memory_order_relaxed)) {
+
</syntaxhighlight>
        while (not burst_timer_elapsed) {
+
            size_t total_num_samps = rand() % max_samps_per_packet;
+
            size_t num_acc_samps = 0;
+
            const float timeout = 1;
+
  
            usrp->set_time_now(uhd::time_spec_t(0.0));
+
==Getting the Antenna==
            while(num_acc_samps < total_num_samps){
+
Get the selected RX antenna on the frontend. Returns the antenna name.
                //send a single packet
+
                num_tx_samps += tx_stream->send(buffs, max_samps_per_packet, md, timeout)*tx_stream->get_num_channels();
+
                num_acc_samps += std::min(total_num_samps-num_acc_samps, tx_stream->get_max_num_samps());
+
            }
+
        }
+
    } else {
+
        //while (not burst_timer_elapsed.load(boost::memory_order_relaxed)) {
+
        while (not burst_timer_elapsed) {
+
            num_tx_samps += tx_stream->send(buffs, max_samps_per_packet, md)*tx_stream->get_num_channels();
+
            md.has_time_spec = false;
+
        }
+
    }
+
  
    //send a mini EOB packet
+
<syntaxhighlight lang="c++">
    md.end_of_burst = true;
+
     usrp->get_rx_antenna()
     tx_stream->send(buffs, 0, md);
+
</syntaxhighlight>
}
+
  
void benchmark_tx_rate_async_helper(
+
==Exiting==
        uhd::tx_streamer::sptr tx_stream,
+
<syntaxhighlight lang="c++">
        atomic_bool& burst_timer_elapsed
+
     return EXIT_SUCCESS;
) {
+
</syntaxhighlight>
    //setup variables and allocate buffer
+
     uhd::async_metadata_t async_md;
+
    bool exit_flag = false;
+
  
    while (true) {
+
==Full Example==
        //if (burst_timer_elapsed.load(boost::memory_order_relaxed)) {
+
<syntaxhighlight lang="c++">
        if (burst_timer_elapsed) {
+
#include <uhd/utils/thread_priority.hpp>
            exit_flag = true;
+
#include <uhd/utils/safe_main.hpp>
        }
+
#include <uhd/usrp/multi_usrp.hpp>
 +
#include <uhd/exception.hpp>
 +
#include <uhd/types/tune_request.hpp>
 +
#include <boost/program_options.hpp>
 +
#include <boost/format.hpp>
 +
#include <boost/thread.hpp>
 +
#include <iostream>
  
        if (not tx_stream->recv_async_msg(async_md)) {
+
int UHD_SAFE_MAIN(int argc, char *argv[]) {
            if (exit_flag == true)
+
    uhd::set_thread_priority_safe();
                return;
+
            continue;
+
        }
+
  
        //handle the error codes
+
    std::string device_args("addr=192.168.10.2");
        switch(async_md.event_code){
+
    std::string subdev("A:0");
        case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
+
    std::string ant("TX/RX");
            return;
+
    std::string ref("internal");
  
        case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
+
    double rate(1e6);
        case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
+
    double freq(915e6);
            num_underflows++;
+
    double gain(10);
            break;
+
    double bw(1e6);
  
        case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
+
    //create a usrp device
        case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
+
    std::cout << std::endl;
            num_seq_errors++;
+
    std::cout << boost::format("Creating the usrp device with: %s...") % device_args << std::endl;
            break;
+
    uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(device_args);
  
        default:
+
    // Lock mboard clocks
            std::cerr << "Event code: " << async_md.event_code << std::endl;
+
    std::cout << boost::format("Lock mboard clocks: %f") % ref << std::endl;
            std::cerr << "Unexpected event on async recv, continuing..." << std::endl;
+
    usrp->set_clock_source(ref);
            break;
+
   
        }
+
    //always select the subdevice first, the channel mapping affects the other settings
 +
    std::cout << boost::format("subdev set to: %f") % subdev << std::endl;
 +
    usrp->set_rx_subdev_spec(subdev);
 +
    std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl;
 +
 
 +
    //set the sample rate
 +
    if (rate <= 0.0) {
 +
        std::cerr << "Please specify a valid sample rate" << std::endl;
 +
        return ~0;
 
     }
 
     }
}
 
  
/***********************************************************************
+
    // set sample rate
* Main code + dispatcher
+
    std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate / 1e6) << std::endl;
**********************************************************************/
+
    usrp->set_rx_rate(rate);
int UHD_SAFE_MAIN(int argc, char *argv[]){
+
     std::cout << boost::format("Actual RX Rate: %f Msps...") % (usrp->get_rx_rate() / 1e6) << std::endl << std::endl;
     uhd::set_thread_priority_safe();
+
  
     //variables to be set by po
+
     // set freq
     std::string args;
+
     std::cout << boost::format("Setting RX Freq: %f MHz...") % (freq / 1e6) << std::endl;
     double duration;
+
     uhd::tune_request_t tune_request(freq);
     double rx_rate, tx_rate;
+
     usrp->set_rx_freq(tune_request);
     std::string rx_otw, tx_otw;
+
     std::cout << boost::format("Actual RX Freq: %f MHz...") % (usrp->get_rx_freq() / 1e6) << std::endl << std::endl;
    std::string rx_cpu, tx_cpu;
+
    std::string mode, ref, pps;
+
    std::string channel_list;
+
    bool random_nsamps = false;
+
    atomic_bool burst_timer_elapsed(false);
+
  
     //setup the program options
+
     // set the rf gain
     po::options_description desc("Allowed options");
+
     std::cout << boost::format("Setting RX Gain: %f dB...") % gain << std::endl;
    desc.add_options()
+
    usrp->set_rx_gain(gain);
        ("help", "help message")
+
    std::cout << boost::format("Actual RX Gain: %f dB...") % usrp->get_rx_gain() << std::endl << std::endl;
        ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args")
+
        ("duration", po::value<double>(&duration)->default_value(10.0), "duration for the test in seconds")
+
        ("rx_rate", po::value<double>(&rx_rate), "specify to perform a RX rate test (sps)")
+
        ("tx_rate", po::value<double>(&tx_rate), "specify to perform a TX rate test (sps)")
+
        ("rx_otw", po::value<std::string>(&rx_otw)->default_value("sc16"), "specify the over-the-wire sample mode for RX")
+
        ("tx_otw", po::value<std::string>(&tx_otw)->default_value("sc16"), "specify the over-the-wire sample mode for TX")
+
        ("rx_cpu", po::value<std::string>(&rx_cpu)->default_value("fc32"), "specify the host/cpu sample mode for RX")
+
        ("tx_cpu", po::value<std::string>(&tx_cpu)->default_value("fc32"), "specify the host/cpu sample mode for TX")
+
        ("ref", po::value<std::string>(&ref), "clock reference (internal, external, mimo, gpsdo)")
+
        ("pps", po::value<std::string>(&pps), "PPS source (internal, external, mimo, gpsdo)")
+
        ("mode", po::value<std::string>(&mode), "DEPRECATED - use \"ref\" and \"pps\" instead (none, mimo)")
+
        ("random", "Run with random values of samples in send() and recv() to stress-test the I/O.")
+
        ("channels", po::value<std::string>(&channel_list)->default_value("0"), "which channel(s) to use (specify \"0\", \"1\", \"0,1\", etc)")
+
    ;
+
    po::variables_map vm;
+
    po::store(po::parse_command_line(argc, argv, desc), vm);
+
    po::notify(vm);
+
  
     //print the help message
+
     // set the IF filter bandwidth
     if (vm.count("help") or (vm.count("rx_rate") + vm.count("tx_rate")) == 0){
+
     std::cout << boost::format("Setting RX Bandwidth: %f MHz...") % (bw / 1e6) << std::endl;
        std::cout << boost::format("UHD Benchmark Rate %s") % desc << std::endl;
+
    usrp->set_rx_bandwidth(bw);
        std::cout <<
+
    std::cout << boost::format("Actual RX Bandwidth: %f MHz...") % (usrp->get_rx_bandwidth() / 1e6) << std::endl << std::endl;
        "   Specify --rx_rate for a receive-only test.\n"
+
        "    Specify --tx_rate for a transmit-only test.\n"
+
        "    Specify both options for a full-duplex test.\n"
+
        << std::endl;
+
        return ~0;
+
    }
+
  
     // Random number of samples?
+
     // set the antenna
     if (vm.count("random")) {
+
     std::cout << boost::format("Setting RX Antenna: %s") % ant << std::endl;
        std::cout << "Using random number of samples in send() and recv() calls." << std::endl;
+
    usrp->set_rx_antenna(ant);
        random_nsamps = true;
+
    std::cout << boost::format("Actual RX Antenna: %s") % usrp->get_rx_antenna() << std::endl << std::endl;
    }
+
  
     if (vm.count("mode")) {
+
     return EXIT_SUCCESS;
        if (vm.count("pps") or vm.count("ref")) {
+
}
            std::cout << "ERROR: The \"mode\" parameter cannot be used with the \"ref\" and \"pps\" parameters.\n" << std::endl;
+
</syntaxhighlight>
            return -1;
+
        } else if (mode == "mimo") {
+
            ref = pps = "mimo";
+
            std::cout << "The use of the \"mode\" parameter is deprecated.  Please use \"ref\" and \"pps\" parameters instead\n" << std::endl;
+
        }
+
    }
+
  
    //create a usrp device
 
    std::cout << std::endl;
 
    uhd::device_addrs_t device_addrs = uhd::device::find(args, uhd::device::USRP);
 
    if (not device_addrs.empty() and device_addrs.at(0).get("type", "") == "usrp1"){
 
        std::cerr << "*** Warning! ***" << std::endl;
 
        std::cerr << "Benchmark results will be inaccurate on USRP1 due to insufficient features.\n" << std::endl;
 
    }
 
    std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl;
 
    uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args);
 
    std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl;
 
    int num_mboards = usrp->get_num_mboards();
 
  
    boost::thread_group thread_group;
+
==CMake==
 +
Use the <syntaxhighlight lang="c++" enclose="none">uhd/host/examples/init_usrp/CMakeLists.txt</syntaxhighlight> file as template
  
    if(vm.count("ref"))
+
* Add the names of your C++ source files to the add_executable(...) section
    {
+
* Put both modified CMakeLists.txt file and C++ file into an empty folder
        if (ref == "mimo")
+
        {
+
            if (num_mboards != 2) {
+
                std::cerr << "ERROR: ref = \"mimo\" implies 2 motherboards; your system has " << num_mboards << " boards" << std::endl;
+
                return -1;
+
            }
+
            usrp->set_clock_source("mimo",1);
+
        } else {
+
            usrp->set_clock_source(ref);
+
        }
+
  
        if(ref != "internal") {
+
[[File:uhd cpp makefile edits.png|650px]]
            std::cout << "Now confirming lock on clock signals..." << std::endl;
+
            bool is_locked = false;
+
            boost::system_time end_time = boost::get_system_time() + boost::posix_time::milliseconds(80);
+
            for (int i = 0; i < num_mboards; i++) {
+
                if (ref == "mimo" and i == 0) continue;
+
                while((is_locked = usrp->get_mboard_sensor("ref_locked",i).to_bool()) == false and
+
                            boost::get_system_time() < end_time )
+
                {
+
                    boost::this_thread::sleep(boost::posix_time::milliseconds(1));
+
                }
+
                if (is_locked == false) {
+
                    std::cerr << "ERROR: Unable to confirm clock signal locked on board:" << i <<  std::endl;
+
                    return -1;
+
                }
+
                is_locked = false;
+
            }
+
        }
+
    }
+
  
    if(vm.count("pps"))
+
==Compile and Install==
    {
+
* Create a “build” folder and invoke CMake the usual way:
      if(pps == "mimo")
+
      {
+
          if (num_mboards != 2) {
+
              std::cerr << "ERROR: ref = \"mimo\" implies 2 motherboards; your system has " << num_mboards << " boards" << std::endl;
+
              return -1;
+
          }
+
          //make mboard 1 a slave over the MIMO Cable
+
          usrp->set_time_source("mimo", 1);
+
      } else {
+
          usrp->set_time_source(pps);
+
      }
+
    }
+
  
     //detect which channels to use
+
     mkdir build
     std::vector<std::string> channel_strings;
+
     cd build
     std::vector<size_t> channel_nums;
+
     cmake ../
    boost::split(channel_strings, channel_list, boost::is_any_of("\"',"));
+
     make
    for(size_t ch = 0; ch < channel_strings.size(); ch++){
+
        size_t chan = boost::lexical_cast<int>(channel_strings[ch]);
+
        if(chan >= usrp->get_tx_num_channels() or chan >= usrp->get_rx_num_channels()){
+
            throw std::runtime_error("Invalid channel(s) specified.");
+
        }
+
        else channel_nums.push_back(boost::lexical_cast<int>(channel_strings[ch]));
+
     }
+
  
    std::cout << boost::format("Setting device timestamp to 0...") << std::endl;
+
==Running the Application==
    if (pps == "mimo" or ref == "mimo" or channel_nums.size() == 1) {
+
<pre>
      usrp->set_time_now(0.0);
+
$ ./usrp_basic
    } else {
+
linux; GNU C++ version 4.8.4; Boost_105400; UHD_003.010.git-202-g9e0861e1
      usrp->set_time_unknown_pps(uhd::time_spec_t(0.0));
+
    }
+
  
    //spawn the receive test thread
 
    if (vm.count("rx_rate")){
 
        usrp->set_rx_rate(rx_rate);
 
        //create a receive streamer
 
        uhd::stream_args_t stream_args(rx_cpu, rx_otw);
 
        stream_args.channels = channel_nums;
 
        uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args);
 
        thread_group.create_thread(boost::bind(&benchmark_rx_rate, usrp, rx_cpu, rx_stream, random_nsamps, boost::ref(burst_timer_elapsed)));
 
    }
 
  
    //spawn the transmit test thread
+
Creating the usrp device with: addr=192.168.10.2...
    if (vm.count("tx_rate")){
+
-- Opening a USRP2/N-Series device...
        usrp->set_tx_rate(tx_rate);
+
-- Current recv frame size: 1472 bytes
        //create a transmit streamer
+
-- Current send frame size: 1472 bytes
        uhd::stream_args_t stream_args(tx_cpu, tx_otw);
+
Lock mboard clocks: internal
        stream_args.channels = channel_nums;
+
subdev set to: A:0
        uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args);
+
Using Device: Single USRP:
        thread_group.create_thread(boost::bind(&benchmark_tx_rate, usrp, tx_cpu, tx_stream, boost::ref(burst_timer_elapsed), random_nsamps));
+
  Device: USRP2 / N-Series Device
        thread_group.create_thread(boost::bind(&benchmark_tx_rate_async_helper, tx_stream, boost::ref(burst_timer_elapsed)));
+
  Mboard 0: N210r4
     }
+
  RX Channel: 0
 +
    RX DSP: 0
 +
    RX Dboard: A
 +
    RX Subdev: WBXv2 RX+GDB
 +
  TX Channel: 0
 +
    TX DSP: 0
 +
    TX Dboard: A
 +
     TX Subdev: WBXv2 TX+GDB
  
    //sleep for the required duration
+
Setting RX Rate: 1.000000 Msps...
    const long secs = long(duration);
+
Actual RX Rate: 1.000000 Msps...
    const long usecs = long((duration - secs)*1e6);
+
    boost::this_thread::sleep(boost::posix_time::seconds(secs) + boost::posix_time::microseconds(usecs));
+
  
    //interrupt and join the threads
+
Setting RX Freq: 915.000000 MHz...
    //burst_timer_elapsed.store(true, boost::memory_order_relaxed);
+
Actual RX Freq: 915.000000 MHz...
    burst_timer_elapsed = true;
+
 
    thread_group.join_all();
+
Setting RX Gain: 10.000000 dB...
 +
Actual RX Gain: 10.000000 dB...
 +
 
 +
Setting RX Bandwidth: 1.000000 MHz...
 +
Actual RX Bandwidth: 1.000000 MHz...
 +
 
 +
Setting RX Antenna: TX/RX
 +
Actual RX Antenna: TX/RX
 +
</pre>
 +
 
 +
==Additional Example Programs==
 +
Additional C++ example programs using the UHD API are provided within the [https://github.com/EttusResearch/uhd/ Ettus Research Github Repository], located in the
 +
<syntaxhighlight lang="c++" enclose="none"> host/examples/ </syntaxhighlight> directory. These examples are installed with UHD and will be located at <syntaxhighlight lang="c++" enclose="none">$prefix/lib/uhd/examples </syntaxhighlight> directory of your system.  
  
    //print summary
 
    std::cout << std::endl << boost::format(
 
        "Benchmark rate summary:\n"
 
        "  Num received samples:    %u\n"
 
        "  Num dropped samples:    %u\n"
 
        "  Num overflows detected:  %u\n"
 
        "  Num transmitted samples: %u\n"
 
        "  Num sequence errors:    %u\n"
 
        "  Num underflows detected: %u\n"
 
        "  Num timeouts:            %u\n"
 
    ) % num_rx_samps % num_dropped_samps
 
      % num_overflows % num_tx_samps
 
      % num_seq_errors % num_underflows
 
      % num_timeouts << std::endl;
 
  
    //finished
 
    std::cout << std::endl << "Done!" << std::endl << std::endl;
 
    return EXIT_SUCCESS;
 
}
 
  
</syntaxhighlight>
 
  
 
[[Category:Application Notes]]
 
[[Category:Application Notes]]

Latest revision as of 12:54, 10 August 2017

Application Note Number

AN-204

Revision History

Date Author Details
2016-05-01 Neel Pandeya
Nate Temple
Initial creation

Abstract

This AN explains how to write and build C++ programs that use the UHD API.

Overview

This Application Note will walk through building a basic C++ program with UHD. This program will initialize, configure the USRP device, set the sample rate, frequency, gain, bandwidth, and select the antenna.

UHD Manual

The UHD Manual is hosted at http://files.ettus.com/manual/index.html and provides information on how to use the USRP devices and how to use the UHD API to connect to them through your own software. The manual is split into two parts: The device manual, and the UHD/API manual. The first part describes details of Ettus Research devices, motherboards and daughterboards, as well as aspects of using UHD. The second is meant for developers writing UHD-based applications, and includes descriptions of the API, sorted by namespaces, classes, and files.

Including Header Files

    #include <uhd/utils/thread_priority.hpp>
    #include <uhd/utils/safe_main.hpp>
    #include <uhd/usrp/multi_usrp.hpp>
    #include <uhd/exception.hpp>
    #include <uhd/types/tune_request.hpp>
    #include <boost/program_options.hpp>
    #include <boost/format.hpp>
    #include <boost/thread.hpp>
    #include <iostream>

UHD_SAFE_MAIN()

Defines a safe wrapper that places a catch-all around main. If an exception is thrown, it prints to stderr and returns.

    int UHD_SAFE_MAIN(int argc, char *argv[]) {
        ...
    }

set_thread_priority_safe()

Set the scheduling priority on the current thread. Same as set_thread_priority but does not throw on failure.

    uhd::set_thread_priority_safe();

Create Variables

    std::string device_args("addr=192.168.10.2");
    std::string subdev("A:0");
    std::string ant("TX/RX");
    std::string ref("internal");

    double rate(1e6);
    double freq(915e6);
    double gain(10);
    double bw(1e6);

Creating a USRP Object

Make a new multi usrp from the device address.

    uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(device_args);

Setting the Motherboard Clocks

Set the clock source for the usrp device. This sets the source for a 10 MHz reference clock. Typical options for source: internal, external, MIMO.

    usrp->set_clock_source(ref)

Selecting the Sub Device

Set the RX frontend specification. The subdev spec maps a physical part of a daughter-board to a channel number. Set the subdev spec before calling into any methods with a channel number. The subdev spec must be the same size across all motherboards. For more details on selecting the Sub Device, see the Specifying the Subdevice section of the UHD Manual.

    usrp->set_rx_subdev_spec(subdev)

Getting the selected Sub Device

Get a printable summary for this USRP configuration.

    usrp->get_pp_string()

Setting the Sample Rate

Set the RX sample rate. The rate is in Samples Per Second.

    usrp->set_rx_rate(rate)

Getting the Sample Rate

Gets the RX sample rate. Returns the rate is in Samples Per Second.

    usrp->get_rx_rate()

Setting Center Frequency

Create a tune request, with the RF frequency in Hz. Set the RX center frequency.

    uhd::tune_request_t tune_request(freq)
    usrp->set_rx_freq(tune_request)

Getting the Center Frequency

Get the RX center frequency. Returns the frequency in Hz.

    usrp->get_rx_freq()

Setting the RF Gain

Set the RX gain value for the specified gain element. For an empty name, distribute across all gain elements. Sets the gain in dB.

    usrp->set_rx_gain(gain)

Reading the RF Gain

Get the RX gain value for the specified gain element. For an empty name, sum across all gain elements. Returns the gain in dB.

    usrp->get_rx_gain()

Setting the IF Filter Bandwidth

Set the RX bandwidth on the frontend. Sets the bandwidth in Hz.

    usrp->set_rx_bandwidth(bw)

Getting the IF Filter Bandwidth

Get the RX bandwidth on the frontend. Returns the bandwidth in Hz.

    usrp->get_rx_bandwidth()

Selecting the Antenna

Select the RX antenna on the frontend.

    usrp->set_rx_antenna(ant)

Getting the Antenna

Get the selected RX antenna on the frontend. Returns the antenna name.

    usrp->get_rx_antenna()

Exiting

    return EXIT_SUCCESS;

Full Example

#include <uhd/utils/thread_priority.hpp>
#include <uhd/utils/safe_main.hpp>
#include <uhd/usrp/multi_usrp.hpp>
#include <uhd/exception.hpp>
#include <uhd/types/tune_request.hpp>
#include <boost/program_options.hpp>
#include <boost/format.hpp>
#include <boost/thread.hpp>
#include <iostream>

int UHD_SAFE_MAIN(int argc, char *argv[]) {
    uhd::set_thread_priority_safe();

    std::string device_args("addr=192.168.10.2");
    std::string subdev("A:0");
    std::string ant("TX/RX");
    std::string ref("internal");

    double rate(1e6);
    double freq(915e6);
    double gain(10);
    double bw(1e6);

    //create a usrp device
    std::cout << std::endl;
    std::cout << boost::format("Creating the usrp device with: %s...") % device_args << std::endl;
    uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(device_args);

    // Lock mboard clocks
    std::cout << boost::format("Lock mboard clocks: %f") % ref << std::endl;
    usrp->set_clock_source(ref);
    
    //always select the subdevice first, the channel mapping affects the other settings
    std::cout << boost::format("subdev set to: %f") % subdev << std::endl;
    usrp->set_rx_subdev_spec(subdev);
    std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl;

    //set the sample rate
    if (rate <= 0.0) {
        std::cerr << "Please specify a valid sample rate" << std::endl;
        return ~0;
    }

    // set sample rate
    std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate / 1e6) << std::endl;
    usrp->set_rx_rate(rate);
    std::cout << boost::format("Actual RX Rate: %f Msps...") % (usrp->get_rx_rate() / 1e6) << std::endl << std::endl;

    // set freq
    std::cout << boost::format("Setting RX Freq: %f MHz...") % (freq / 1e6) << std::endl;
    uhd::tune_request_t tune_request(freq);
    usrp->set_rx_freq(tune_request);
    std::cout << boost::format("Actual RX Freq: %f MHz...") % (usrp->get_rx_freq() / 1e6) << std::endl << std::endl;

    // set the rf gain
    std::cout << boost::format("Setting RX Gain: %f dB...") % gain << std::endl;
    usrp->set_rx_gain(gain);
    std::cout << boost::format("Actual RX Gain: %f dB...") % usrp->get_rx_gain() << std::endl << std::endl;

    // set the IF filter bandwidth
    std::cout << boost::format("Setting RX Bandwidth: %f MHz...") % (bw / 1e6) << std::endl;
    usrp->set_rx_bandwidth(bw);
    std::cout << boost::format("Actual RX Bandwidth: %f MHz...") % (usrp->get_rx_bandwidth() / 1e6) << std::endl << std::endl;

    // set the antenna
    std::cout << boost::format("Setting RX Antenna: %s") % ant << std::endl;
    usrp->set_rx_antenna(ant);
    std::cout << boost::format("Actual RX Antenna: %s") % usrp->get_rx_antenna() << std::endl << std::endl;

    return EXIT_SUCCESS;
}


CMake

Use the uhd/host/examples/init_usrp/CMakeLists.txt file as template

  • Add the names of your C++ source files to the add_executable(...) section
  • Put both modified CMakeLists.txt file and C++ file into an empty folder

uhd cpp makefile edits.png

Compile and Install

  • Create a “build” folder and invoke CMake the usual way:
   mkdir build
   cd build
   cmake ../
   make

Running the Application

$ ./usrp_basic
linux; GNU C++ version 4.8.4; Boost_105400; UHD_003.010.git-202-g9e0861e1


Creating the usrp device with: addr=192.168.10.2...
-- Opening a USRP2/N-Series device...
-- Current recv frame size: 1472 bytes
-- Current send frame size: 1472 bytes
Lock mboard clocks: internal
subdev set to: A:0
Using Device: Single USRP:
  Device: USRP2 / N-Series Device
  Mboard 0: N210r4
  RX Channel: 0
    RX DSP: 0
    RX Dboard: A
    RX Subdev: WBXv2 RX+GDB
  TX Channel: 0
    TX DSP: 0
    TX Dboard: A
    TX Subdev: WBXv2 TX+GDB

Setting RX Rate: 1.000000 Msps...
Actual RX Rate: 1.000000 Msps...

Setting RX Freq: 915.000000 MHz...
Actual RX Freq: 915.000000 MHz...

Setting RX Gain: 10.000000 dB...
Actual RX Gain: 10.000000 dB...

Setting RX Bandwidth: 1.000000 MHz...
Actual RX Bandwidth: 1.000000 MHz...

Setting RX Antenna: TX/RX
Actual RX Antenna: TX/RX

Additional Example Programs

Additional C++ example programs using the UHD API are provided within the Ettus Research Github Repository, located in the host/examples/ directory. These examples are installed with UHD and will be located at $prefix/lib/uhd/examples directory of your system.