|
|
Line 20: |
Line 20: |
| | | |
| Example code: | | Example code: |
− |
| |
− | #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;
| |
− | }
| |
− |
| |
− |
| |
− |
| |
− | 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
| |
− | - Create a “build” folder and invoke CMake the usual way:
| |
− |
| |
− | mkdir build
| |
− | cd build
| |
− | cmake ../
| |
− | make -j4
| |
− |
| |
− |
| |
− |
| |
− |
| |
− |
| |
− |
| |
| | | |
| <syntaxhighlight lang="c++"> | | <syntaxhighlight lang="c++"> |
− | //
| |
− | // 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> | | #include <uhd/utils/thread_priority.hpp> |
− | #include <uhd/convert.hpp>
| |
| #include <uhd/utils/safe_main.hpp> | | #include <uhd/utils/safe_main.hpp> |
| #include <uhd/usrp/multi_usrp.hpp> | | #include <uhd/usrp/multi_usrp.hpp> |
| + | #include <uhd/exception.hpp> |
| + | #include <uhd/types/tune_request.hpp> |
| #include <boost/program_options.hpp> | | #include <boost/program_options.hpp> |
| #include <boost/format.hpp> | | #include <boost/format.hpp> |
− | #include <boost/thread/thread.hpp> | + | #include <boost/thread.hpp> |
− | #include <boost/algorithm/string.hpp>
| + | |
− | #include <boost/lexical_cast.hpp>
| + | |
− | //#include <boost/atomic.hpp>
| + | |
| #include <iostream> | | #include <iostream> |
− | #include <complex>
| |
− | #include <cstdlib>
| |
| | | |
− | namespace po = boost::program_options;
| + | int UHD_SAFE_MAIN(int argc, char *argv[]) { |
− | | + | |
− | const double INIT_DELAY = 0.05; // 50mS initial delay before transmit
| + | |
− | //typedef boost::atomic<bool> atomic_bool;
| + | |
− | // We'll fake atomic bools for now, for more backward compat.
| + | |
− | // This is just an example, after all.
| + | |
− | typedef bool atomic_bool;
| + | |
− | | + | |
− | /***********************************************************************
| + | |
− | * Test result variables
| + | |
− | **********************************************************************/
| + | |
− | 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;
| + | |
− | | + | |
− | /***********************************************************************
| + | |
− | * Benchmark RX Rate
| + | |
− | **********************************************************************/
| + | |
− | 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(); | | uhd::set_thread_priority_safe(); |
| | | |
− | //print pre-test summary | + | std::string device_args("addr=192.168.10.2"); |
− | std::cout << boost::format( | + | std::string subdev("A:0"); |
− | "Testing receive rate %f Msps on %u channels"
| + | std::string ant("TX/RX"); |
− | ) % (usrp->get_rx_rate()/1e6) % rx_stream->get_num_channels() << std::endl; | + | std::string ref("internal"); |
| | | |
− | //setup variables and allocate buffer | + | double rate(1e6); |
− | uhd::rx_metadata_t md;
| + | double freq(915e6); |
− | const size_t max_samps_per_packet = rx_stream->get_max_num_samps();
| + | double gain(10); |
− | std::vector<char> buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(rx_cpu)); | + | double bw(1e6); |
− | 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); | + | //create a usrp device |
− | cmd.time_spec = usrp->get_time_now() + uhd::time_spec_t(INIT_DELAY); | + | std::cout << std::endl; |
− | cmd.stream_now = (buffs.size() == 1);
| + | std::cout << boost::format("Creating the usrp device with: %s...") % device_args << std::endl; |
− | rx_stream->issue_stream_cmd(cmd); | + | uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(device_args); |
| | | |
− | const float burst_pkt_time = std::max(0.100, (2 * max_samps_per_packet/rate)); | + | // Lock mboard clocks |
− | float recv_timeout = burst_pkt_time + INIT_DELAY; | + | 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; |
| | | |
− | while (true) { | + | //set the sample rate |
− | //if (burst_timer_elapsed.load(boost::memory_order_relaxed)) {
| + | if (rate <= 0.0) { |
− | if (burst_timer_elapsed) {
| + | std::cerr << "Please specify a valid sample rate" << std::endl; |
− | rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS);
| + | return ~0; |
− | }
| + | |
− | 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
| + | |
− | switch(md.error_code){
| + | |
− | case uhd::rx_metadata_t::ERROR_CODE_NONE:
| + | |
− | 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
| + | |
− | case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
| + | |
− | last_time = md.time_spec;
| + | |
− | had_an_overflow = true;
| + | |
− | // check out_of_sequence flag to see if it was a sequence error or overflow
| + | |
− | if (!md.out_of_sequence)
| + | |
− | num_overflows++;
| + | |
− | break;
| + | |
− | | + | |
− | case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT: | + | |
− | // If we stopped the streamer, then we expect this at some point
| + | |
− | //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
| + | |
− | default: | + | |
− | std::cerr << "Receiver error: " << md.strerror() << std::endl;
| + | |
− | std::cerr << "Unexpected error on recv, continuing..." << std::endl;
| + | |
− | break;
| + | |
− | }
| + | |
| } | | } |
− | }
| |
| | | |
− | /*********************************************************************** | + | // set sample rate |
− | * Benchmark TX Rate
| + | std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate / 1e6) << std::endl; |
− | **********************************************************************/
| + | usrp->set_rx_rate(rate); |
− | void benchmark_tx_rate(
| + | std::cout << boost::format("Actual RX Rate: %f Msps...") % (usrp->get_rx_rate() / 1e6) << std::endl << std::endl; |
− | 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 | + | // set freq |
− | std::cout << boost::format( | + | std::cout << boost::format("Setting RX Freq: %f MHz...") % (freq / 1e6) << std::endl; |
− | "Testing transmit rate %f Msps on %u channels"
| + | uhd::tune_request_t tune_request(freq); |
− | ) % (usrp->get_tx_rate()/1e6) % tx_stream->get_num_channels() << std::endl;
| + | usrp->set_rx_freq(tune_request); |
| + | std::cout << boost::format("Actual RX Freq: %f MHz...") % (usrp->get_rx_freq() / 1e6) << std::endl << std::endl; |
| | | |
− | //setup variables and allocate buffer | + | // set the rf gain |
− | uhd::tx_metadata_t md; | + | std::cout << boost::format("Setting RX Gain: %f dB...") % gain << std::endl; |
− | md.time_spec = usrp->get_time_now() + uhd::time_spec_t(INIT_DELAY);
| + | usrp->set_rx_gain(gain); |
− | md.has_time_spec = (tx_stream->get_num_channels() > 1);
| + | std::cout << boost::format("Actual RX Gain: %f dB...") % usrp->get_rx_gain() << std::endl << std::endl; |
− | 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) { | + | // set the IF filter bandwidth |
− | std::srand((unsigned int)time(NULL));
| + | std::cout << boost::format("Setting RX Bandwidth: %f MHz...") % (bw / 1e6) << std::endl; |
− | //while (not burst_timer_elapsed.load(boost::memory_order_relaxed)) {
| + | usrp->set_rx_bandwidth(bw); |
− | while (not burst_timer_elapsed) {
| + | std::cout << boost::format("Actual RX Bandwidth: %f MHz...") % (usrp->get_rx_bandwidth() / 1e6) << std::endl << std::endl; |
− | 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));
| + | // set the antenna |
− | while(num_acc_samps < total_num_samps){
| + | std::cout << boost::format("Setting RX Antenna: %s") % ant << std::endl; |
− | //send a single packet
| + | usrp->set_rx_antenna(ant); |
− | num_tx_samps += tx_stream->send(buffs, max_samps_per_packet, md, timeout)*tx_stream->get_num_channels();
| + | std::cout << boost::format("Actual RX Antenna: %s") % usrp->get_rx_antenna() << std::endl << std::endl; |
− | 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 | + | return EXIT_SUCCESS; |
− | md.end_of_burst = true;
| + | |
− | tx_stream->send(buffs, 0, md);
| + | |
| } | | } |
| + | </syntaxhighlight> |
| | | |
− | void benchmark_tx_rate_async_helper(
| |
− | uhd::tx_streamer::sptr tx_stream,
| |
− | atomic_bool& burst_timer_elapsed
| |
− | ) {
| |
− | //setup variables and allocate buffer
| |
− | uhd::async_metadata_t async_md;
| |
− | bool exit_flag = false;
| |
| | | |
− | while (true) {
| + | Use the uhd/host/examples/init_usrp/CMakeLists.txt file as template |
− | //if (burst_timer_elapsed.load(boost::memory_order_relaxed)) {
| + | - Add the names of your C++ source files to the add_executable(...) section |
− | if (burst_timer_elapsed) {
| + | - Put both modified CMakeLists.txt file and C++ file into an empty folder |
− | exit_flag = true;
| + | - Create a “build” folder and invoke CMake the usual way: |
− | }
| + | |
| | | |
− | if (not tx_stream->recv_async_msg(async_md)) {
| + | mkdir build |
− | if (exit_flag == true)
| + | cd build |
− | return;
| + | cmake ../ |
− | continue;
| + | make -j4 |
− | }
| + | |
| | | |
− | //handle the error codes
| |
− | switch(async_md.event_code){
| |
− | case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
| |
− | return;
| |
| | | |
− | case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
| |
− | case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
| |
− | num_underflows++;
| |
− | break;
| |
| | | |
− | case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
| |
− | case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
| |
− | num_seq_errors++;
| |
− | break;
| |
| | | |
− | default:
| |
− | std::cerr << "Event code: " << async_md.event_code << std::endl;
| |
− | std::cerr << "Unexpected event on async recv, continuing..." << std::endl;
| |
− | break;
| |
− | }
| |
− | }
| |
− | }
| |
− |
| |
− | /***********************************************************************
| |
− | * Main code + dispatcher
| |
− | **********************************************************************/
| |
− | int UHD_SAFE_MAIN(int argc, char *argv[]){
| |
− | uhd::set_thread_priority_safe();
| |
− |
| |
− | //variables to be set by po
| |
− | std::string args;
| |
− | double duration;
| |
− | double rx_rate, tx_rate;
| |
− | std::string rx_otw, tx_otw;
| |
− | 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
| |
− | po::options_description desc("Allowed options");
| |
− | desc.add_options()
| |
− | ("help", "help message")
| |
− | ("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
| |
− | if (vm.count("help") or (vm.count("rx_rate") + vm.count("tx_rate")) == 0){
| |
− | std::cout << boost::format("UHD Benchmark Rate %s") % desc << std::endl;
| |
− | std::cout <<
| |
− | " 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?
| |
− | if (vm.count("random")) {
| |
− | std::cout << "Using random number of samples in send() and recv() calls." << std::endl;
| |
− | random_nsamps = true;
| |
− | }
| |
− |
| |
− | if (vm.count("mode")) {
| |
− | 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;
| |
− | 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;
| |
− |
| |
− | if(vm.count("ref"))
| |
− | {
| |
− | 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") {
| |
− | 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"))
| |
− | {
| |
− | 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
| |
− | std::vector<std::string> channel_strings;
| |
− | std::vector<size_t> channel_nums;
| |
− | boost::split(channel_strings, channel_list, boost::is_any_of("\"',"));
| |
− | 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;
| |
− | if (pps == "mimo" or ref == "mimo" or channel_nums.size() == 1) {
| |
− | usrp->set_time_now(0.0);
| |
− | } else {
| |
− | 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
| |
− | if (vm.count("tx_rate")){
| |
− | usrp->set_tx_rate(tx_rate);
| |
− | //create a transmit streamer
| |
− | uhd::stream_args_t stream_args(tx_cpu, tx_otw);
| |
− | stream_args.channels = channel_nums;
| |
− | uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args);
| |
− | thread_group.create_thread(boost::bind(&benchmark_tx_rate, usrp, tx_cpu, tx_stream, boost::ref(burst_timer_elapsed), random_nsamps));
| |
− | thread_group.create_thread(boost::bind(&benchmark_tx_rate_async_helper, tx_stream, boost::ref(burst_timer_elapsed)));
| |
− | }
| |
− |
| |
− | //sleep for the required duration
| |
− | const long secs = long(duration);
| |
− | 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
| |
− | //burst_timer_elapsed.store(true, boost::memory_order_relaxed);
| |
− | burst_timer_elapsed = true;
| |
− | thread_group.join_all();
| |
− |
| |
− | //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]] |
This AN explains how to write and build C++ programs that use the UHD API.
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.