diff options
Diffstat (limited to 'host/utils')
-rw-r--r-- | host/utils/CMakeLists.txt | 31 | ||||
-rw-r--r-- | host/utils/cdecode.c | 80 | ||||
-rw-r--r-- | host/utils/cdecode.h | 28 | ||||
-rw-r--r-- | host/utils/converter_benchmark.cpp | 433 | ||||
-rw-r--r-- | host/utils/converter_benchmark.py | 193 | ||||
-rw-r--r-- | host/utils/fx2_init_eeprom.cpp | 31 | ||||
-rw-r--r-- | host/utils/query_gpsdo_sensors.cpp | 121 | ||||
-rw-r--r-- | host/utils/uhd_config_info.cpp | 89 |
8 files changed, 856 insertions, 150 deletions
diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt index 28fecc895..bf8d88799 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -19,6 +19,7 @@ # Utilities that get installed into the runtime path ######################################################################## SET(util_runtime_sources + uhd_config_info.cpp uhd_find_devices.cpp uhd_usrp_probe.cpp uhd_image_loader.cpp @@ -30,7 +31,7 @@ SET(util_runtime_sources SET(x3xx_burner_sources usrp_x3xx_fpga_burner.cpp - cdecode.c + ${CMAKE_CURRENT_SOURCE_DIR}/../lib/usrp/x300/cdecode.c ) find_package(UDev) @@ -48,18 +49,25 @@ FOREACH(util_source ${util_runtime_sources}) UHD_INSTALL(TARGETS ${util_name} RUNTIME DESTINATION ${RUNTIME_DIR} COMPONENT utilities) ENDFOREACH(util_source) -ADD_EXECUTABLE(usrp_x3xx_fpga_burner ${x3xx_burner_sources}) -TARGET_LINK_LIBRARIES(usrp_x3xx_fpga_burner uhd ${Boost_LIBRARIES}) -UHD_INSTALL(TARGETS usrp_x3xx_fpga_burner RUNTIME DESTINATION ${RUNTIME_DIR} COMPONENT utilities) +IF(ENABLE_X300) + INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/usrp/x300) + ADD_EXECUTABLE(usrp_x3xx_fpga_burner ${x3xx_burner_sources}) + TARGET_LINK_LIBRARIES(usrp_x3xx_fpga_burner uhd ${Boost_LIBRARIES}) + UHD_INSTALL(TARGETS usrp_x3xx_fpga_burner RUNTIME DESTINATION ${RUNTIME_DIR} COMPONENT utilities) +ENDIF(ENABLE_X300) ######################################################################## # Utilities that get installed into the share path ######################################################################## SET(util_share_sources + converter_benchmark.cpp query_gpsdo_sensors.cpp usrp_burn_db_eeprom.cpp usrp_burn_mb_eeprom.cpp ) +SET(util_share_sources_py + converter_benchmark.py +) IF(ENABLE_USB) LIST(APPEND util_share_sources fx2_init_eeprom.cpp @@ -108,9 +116,20 @@ FOREACH(util_source ${util_share_sources}) TARGET_LINK_LIBRARIES(${util_name} uhd ${Boost_LIBRARIES}) UHD_INSTALL(TARGETS ${util_name} RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) ENDFOREACH(util_source) +FOREACH(util_source ${util_share_sources_py}) + UHD_INSTALL(PROGRAMS + ${CMAKE_CURRENT_SOURCE_DIR}/${util_source} + DESTINATION ${PKG_LIB_DIR}/utils + COMPONENT utilities + ) +ENDFOREACH(util_source) -UHD_INSTALL(TARGETS usrp_n2xx_simple_net_burner RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) -UHD_INSTALL(TARGETS usrp_x3xx_fpga_burner RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) +IF(ENABLE_USRP2) + UHD_INSTALL(TARGETS usrp_n2xx_simple_net_burner RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) +ENDIF(ENABLE_USRP2) +IF(ENABLE_X300) + UHD_INSTALL(TARGETS usrp_x3xx_fpga_burner RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) +ENDIF(ENABLE_X300) #UHD images downloader configuration CONFIGURE_FILE( diff --git a/host/utils/cdecode.c b/host/utils/cdecode.c deleted file mode 100644 index 1d09cbe22..000000000 --- a/host/utils/cdecode.c +++ /dev/null @@ -1,80 +0,0 @@ -/* -cdecoder.c - c source to a base64 decoding algorithm implementation - -This is part of the libb64 project, and has been placed in the public domain. -For details, see http://sourceforge.net/projects/libb64 -*/ - -#include "cdecode.h" - -int base64_decode_value(char value_in){ - static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51}; - static const char decoding_size = sizeof(decoding); - value_in -= 43; - if ((signed char)value_in < 0 || value_in > decoding_size) return -1; - return decoding[(int)value_in]; -} - -void base64_init_decodestate(base64_decodestate* state_in){ - state_in->step = step_a; - state_in->plainchar = 0; -} - -size_t base64_decode_block(const char* code_in, const size_t length_in, char* plaintext_out, base64_decodestate* state_in){ - const char* codechar = code_in; - char* plainchar = plaintext_out; - char fragment; - - *plainchar = state_in->plainchar; - - switch (state_in->step){ - while (1){ - case step_a: - do{ - if (codechar == code_in+length_in){ - state_in->step = step_a; - state_in->plainchar = *plainchar; - return plainchar - plaintext_out; - } - fragment = (char)base64_decode_value(*codechar++); - } while ((signed char)fragment < 0); - *plainchar = (fragment & 0x03f) << 2; - - case step_b: - do{ - if (codechar == code_in+length_in){ - state_in->step = step_b; - state_in->plainchar = *plainchar; - return plainchar - plaintext_out; - } - fragment = (char)base64_decode_value(*codechar++); - } while ((signed char)fragment < 0); - *plainchar++ |= (fragment & 0x030) >> 4; - *plainchar = (fragment & 0x00f) << 4; - case step_c: - do{ - if (codechar == code_in+length_in) - { - state_in->step = step_c; - state_in->plainchar = *plainchar; - return plainchar - plaintext_out; - } - fragment = (char)base64_decode_value(*codechar++); - } while ((signed char)fragment < 0); - *plainchar++ |= (fragment & 0x03c) >> 2; - *plainchar = (fragment & 0x003) << 6; - case step_d: - do{ - if (codechar == code_in+length_in){ - state_in->step = step_d; - state_in->plainchar = *plainchar; - return plainchar - plaintext_out; - } - fragment = (char)base64_decode_value(*codechar++); - } while ((signed char)fragment < 0); - *plainchar++ |= (fragment & 0x03f); - } - } - /* control should not reach here */ - return plainchar - plaintext_out; -} diff --git a/host/utils/cdecode.h b/host/utils/cdecode.h deleted file mode 100644 index e1eee301f..000000000 --- a/host/utils/cdecode.h +++ /dev/null @@ -1,28 +0,0 @@ -/* -cdecode.h - c header for a base64 decoding algorithm - -This is part of the libb64 project, and has been placed in the public domain. -For details, see http://sourceforge.net/projects/libb64 -*/ - -#ifndef BASE64_CDECODE_H -#define BASE64_CDECODE_H - -#include <stddef.h> - -typedef enum{ - step_a, step_b, step_c, step_d -} base64_decodestep; - -typedef struct{ - base64_decodestep step; - char plainchar; -} base64_decodestate; - -void base64_init_decodestate(base64_decodestate* state_in); - -int base64_decode_value(char value_in); - -size_t base64_decode_block(const char* code_in, const size_t length_in, char* plaintext_out, base64_decodestate* state_in); - -#endif /* BASE64_CDECODE_H */ diff --git a/host/utils/converter_benchmark.cpp b/host/utils/converter_benchmark.cpp new file mode 100644 index 000000000..0f38e8518 --- /dev/null +++ b/host/utils/converter_benchmark.cpp @@ -0,0 +1,433 @@ +// +// Copyright 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/safe_main.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/convert.hpp> +#include <uhd/exception.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <boost/timer.hpp> +#include <boost/algorithm/string.hpp> +#include <iostream> +#include <iomanip> +#include <map> +#include <complex> + +namespace po = boost::program_options; +using namespace uhd::convert; + +enum buf_init_t { + RANDOM, INC +}; + +// Convert `sc16_item32_le' -> `sc16' +// Finds the first _ in format and returns the string +// until then. Returns the entire string if no _ is found. +std::string format_to_type(const std::string &format) +{ + std::string ret_val = ""; + for (size_t i = 0; i < format.length(); i++) { + if (format[i] == '_') { + return ret_val; + } + ret_val.append(1, format[i]); + } + + return ret_val; +} + +void configure_conv( + converter::sptr conv, + const std::string &in_type, + const std::string &out_type +) { + if (in_type == "sc16") { + if (out_type == "fc32") { + std::cout << "Setting scalar to 32767." << std::endl; + conv->set_scalar(32767.); + return; + } + } + + if (in_type == "fc32") { + if (out_type == "sc16") { + std::cout << "Setting scalar to 32767." << std::endl; + conv->set_scalar(32767.); + return; + } + } + + std::cout << "No configuration required." << std::endl; +} + +template <typename T> +void init_random_vector_complex_float(std::vector<char> &buf_ptr, const size_t n_items) +{ + std::complex<T> * const buf = reinterpret_cast<std::complex<T> * const>(&buf_ptr[0]); + for (size_t i = 0; i < n_items; i++) { + buf[i] = std::complex<T>( + T((std::rand()/double(RAND_MAX/2)) - 1), + T((std::rand()/double(RAND_MAX/2)) - 1) + ); + } +} + +template <typename T> +void init_random_vector_complex_int(std::vector<char> &buf_ptr, const size_t n_items) +{ + std::complex<T> * const buf = reinterpret_cast<std::complex<T> * const>(&buf_ptr[0]); + for (size_t i = 0; i < n_items; i++) { + buf[i] = std::complex<T>(T(std::rand()), T(std::rand())); + } +} + +template <typename T> +void init_random_vector_real_int(std::vector<char> &buf_ptr, size_t n_items) +{ + T * const buf = reinterpret_cast<T * const>(&buf_ptr[0]); + for (size_t i = 0; i < n_items; i++) { + buf[i] = T(std::rand()); + } +} + +// Fill a buffer with increasing numbers +template <typename T> +void init_inc_vector(std::vector<char> &buf_ptr, size_t n_items) +{ + T * const buf = reinterpret_cast<T * const>(&buf_ptr[0]); + for (size_t i = 0; i < n_items; i++) { + buf[i] = T(i); + } +} + +void init_buffers( + std::vector< std::vector<char> > &buf, + const std::string &type, + size_t bytes_per_item, + buf_init_t buf_seed_mode +) { + if (buf.empty()) { + return; + } + size_t n_items = buf[0].size() / bytes_per_item; + + /// Fill with incrementing integers + if (buf_seed_mode == INC) { + for (size_t i = 0; i < buf.size(); i++) { + if (type == "sc8") { + init_inc_vector< std::complex<boost::int8_t> >(buf[i], n_items); + } else if (type == "sc16") { + init_inc_vector< std::complex<boost::int16_t> >(buf[i], n_items); + } else if (type == "sc32") { + init_inc_vector< std::complex<boost::int32_t> >(buf[i], n_items); + } else if (type == "fc32") { + init_inc_vector< std::complex<float> >(buf[i], n_items); + } else if (type == "fc64") { + init_inc_vector< std::complex<double> >(buf[i], n_items); + } else if (type == "s8") { + init_inc_vector< boost::int8_t >(buf[i], n_items); + } else if (type == "s16") { + init_inc_vector< boost::int16_t >(buf[i], n_items); + } else if (type == "item32") { + init_inc_vector< boost::uint32_t >(buf[i], n_items); + init_random_vector_real_int<boost::uint32_t>(buf[i], n_items); + } else { + throw uhd::runtime_error(str( + boost::format("Cannot handle data type: %s") % type + )); + } + } + + return; + } + + assert(buf_seed_mode == RANDOM); + + /// Fill with random data + for (size_t i = 0; i < buf.size(); i++) { + if (type == "sc8") { + init_random_vector_complex_int<boost::int8_t>(buf[i], n_items); + } else if (type == "sc16") { + init_random_vector_complex_int<boost::int16_t>(buf[i], n_items); + } else if (type == "sc32") { + init_random_vector_complex_int<boost::int32_t>(buf[i], n_items); + } else if (type == "fc32") { + init_random_vector_complex_float<float>(buf[i], n_items); + } else if (type == "fc64") { + init_random_vector_complex_float<double>(buf[i], n_items); + } else if (type == "s8") { + init_random_vector_real_int<boost::int8_t>(buf[i], n_items); + } else if (type == "s16") { + init_random_vector_real_int<boost::int16_t>(buf[i], n_items); + } else if (type == "item32") { + init_random_vector_real_int<boost::uint32_t>(buf[i], n_items); + } else { + throw uhd::runtime_error(str( + boost::format("Cannot handle data type: %s") % type + )); + } + } +} + +// Returns time elapsed +double run_benchmark( + converter::sptr conv, + const std::vector<const void *> &input_buf_refs, + const std::vector<void *> &output_buf_refs, + size_t n_items, + size_t iterations +) { + boost::timer benchmark_timer; + for (size_t i = 0; i < iterations; i++) { + conv->conv(input_buf_refs, output_buf_refs, n_items); + } + return benchmark_timer.elapsed(); +} + +template <typename T> +std::string void_ptr_to_hexstring(const void *v_ptr, size_t index) +{ + const T *ptr = reinterpret_cast<const T *>(v_ptr); + return str(boost::format("%X") % ptr[index]); +} + +std::string item_to_hexstring( + const void *v_ptr, + size_t index, + const std::string &type +) { + if (type == "fc32") { + return void_ptr_to_hexstring<uint64_t>(v_ptr, index); + } + else if (type == "sc16" || type == "item32") { + return void_ptr_to_hexstring<uint32_t>(v_ptr, index); + } + else if (type == "sc8" || type == "s16") { + return void_ptr_to_hexstring<uint16_t>(v_ptr, index); + } + else if (type == "u8") { + return void_ptr_to_hexstring<uint8_t>(v_ptr, index); + } + else { + return str(boost::format("<unhandled data type: %s>") % type); + } +} + +std::string item_to_string( + const void *v_ptr, + size_t index, + const std::string &type, + const bool print_hex +) { + if (print_hex) { + return item_to_hexstring(v_ptr, index, type); + } + + if (type == "sc16") { + const std::complex<boost::int16_t> *ptr = reinterpret_cast<const std::complex<boost::int16_t> *>(v_ptr); + return boost::lexical_cast<std::string>(ptr[index]); + } + else if (type == "sc8") { + const std::complex<boost::int8_t> *ptr = reinterpret_cast<const std::complex<boost::int8_t> *>(v_ptr); + return boost::lexical_cast<std::string>(ptr[index]); + } + else if (type == "fc32") { + const std::complex<float> *ptr = reinterpret_cast<const std::complex<float> *>(v_ptr); + return boost::lexical_cast<std::string>(ptr[index]); + } + else if (type == "item32") { + const boost::uint32_t *ptr = reinterpret_cast<const boost::uint32_t *>(v_ptr); + return boost::lexical_cast<std::string>(ptr[index]); + } + else if (type == "s16") { + const boost::int16_t *ptr = reinterpret_cast<const boost::int16_t *>(v_ptr); + return boost::lexical_cast<std::string>(ptr[index]); + } + else { + return str(boost::format("<unhandled data type: %s>") % type); + } +} + +int UHD_SAFE_MAIN(int argc, char *argv[]) +{ + std::string in_format, out_format; + std::string priorities; + std::string seed_mode; + priority_type prio = -1, max_prio; + size_t iterations, n_samples; + size_t n_inputs, n_outputs; + buf_init_t buf_seed_mode = RANDOM; + + /// Command line arguments + po::options_description desc("Converter benchmark options:"); + desc.add_options() + ("help", "help message") + ("in", po::value<std::string>(&in_format), "Input format (e.g. 'sc16')") + ("out", po::value<std::string>(&out_format), "Output format (e.g. 'sc16')") + ("samples", po::value<size_t>(&n_samples)->default_value(1000000), "Number of samples per iteration") + ("iterations", po::value<size_t>(&iterations)->default_value(10000), "Number of iterations per benchmark") + ("priorities", po::value<std::string>(&priorities)->default_value("default"), "Converter priorities. Can be 'default', 'all', or a comma-separated list of priorities.") + ("max-prio", po::value<priority_type>(&max_prio)->default_value(4), "Largest available priority (advanced feature)") + ("n-inputs", po::value<size_t>(&n_inputs)->default_value(1), "Number of input vectors") + ("n-outputs", po::value<size_t>(&n_outputs)->default_value(1), "Number of output vectors") + ("debug-converter", "Skip benchmark and print conversion results. Implies iterations==1 and will only run on a single converter.") + ("seed-mode", po::value<std::string>(&seed_mode)->default_value("random"), "How to initialize the data: random, incremental") + ("hex", "When using debug mode, dump memory in hex") + ; + 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")){ + std::cout << boost::format("UHD Converter Benchmark Tool %s") % desc << std::endl << std::endl; + std::cout << " Use this to benchmark or debug converters." << std::endl + << " When using as a benchmark tool, it will output the execution time\n" + " for every conversion run in CSV format to stdout. Every line between\n" + " the output delimiters {{{ }}} is of the format: <PRIO>,<TIME IN MILLISECONDS>\n" + " When using for converter debugging, every line is formatted as\n" + " <INPUT_VALUE>,<OUTPUT_VALUE>\n" << std::endl; + return EXIT_FAILURE; + } + + // Parse more arguments + if (seed_mode == "incremental") { + buf_seed_mode = INC; + } else if (seed_mode == "random") { + buf_seed_mode = RANDOM; + } else { + std::cout << "Invalid argument: --seed-mode must be either 'incremental' or 'random'." << std::endl; + } + + bool debug_mode = bool(vm.count("debug-converter")); + if (debug_mode) { + iterations = 1; + } + + /// Create the converter(s) ////////////////////////////////////////////// + id_type converter_id; + converter_id.input_format = in_format; + converter_id.output_format = out_format; + converter_id.num_inputs = n_inputs; + converter_id.num_outputs = n_outputs; + std::cout << "Requested converter format: " << converter_id.to_string() + << std::endl; + uhd::dict<priority_type, converter::sptr> conv_list; + if (priorities == "default" or priorities.empty()) { + try { + conv_list[prio] = get_converter(converter_id, prio)(); // Can throw a uhd::key_error + } catch(const uhd::key_error &e) { + std::cout << "No converters found." << std::endl; + return EXIT_FAILURE; + } + } else if (priorities == "all") { + for (priority_type i = 0; i < max_prio; i++) { + try { + // get_converter() returns a factory function, execute that immediately: + converter::sptr conv_for_prio = get_converter(converter_id, i)(); // Can throw a uhd::key_error + conv_list[i] = conv_for_prio; + } catch (...) { + continue; + } + } + } else { // Assume that priorities contains a list of prios (e.g. 0,2,3) + std::vector<std::string> prios_in_list; + boost::split( + prios_in_list, + priorities, + boost::is_any_of(","), // Split at , + boost::token_compress_on // Avoid empty results + ); + BOOST_FOREACH(const std::string &this_prio, prios_in_list) { + size_t prio_index = boost::lexical_cast<size_t>(this_prio); + converter::sptr conv_for_prio = get_converter(converter_id, prio_index)(); // Can throw a uhd::key_error + conv_list[prio_index] = conv_for_prio; + } + } + std::cout << "Found " << conv_list.size() << " converter(s)." << std::endl; + + /// Create input and output buffers /////////////////////////////////////// + // First, convert the types to plain types (e.g. sc16_item32_le -> sc16) + const std::string in_type = format_to_type(in_format); + const std::string out_type = format_to_type(out_format); + const size_t in_size = get_bytes_per_item(in_type); + const size_t out_size = get_bytes_per_item(out_type); + // Create the buffers and fill them with random data & zeros, respectively + std::vector< std::vector<char> > input_buffers(n_inputs, std::vector<char>(in_size * n_samples, 0)); + std::vector< std::vector<char> > output_buffers(n_outputs, std::vector<char>(out_size * n_samples, 0)); + init_buffers(input_buffers, in_type, in_size, buf_seed_mode); + // Create ref vectors for the converter: + std::vector<const void *> input_buf_refs(n_inputs); + std::vector<void *> output_buf_refs(n_outputs); + for (size_t i = 0; i < n_inputs; i++) { + input_buf_refs[i] = reinterpret_cast<const void *>(&input_buffers[i][0]); + } + for (size_t i = 0; i < n_outputs; i++) { + output_buf_refs[i] = reinterpret_cast<void *>(&output_buffers[i][0]); + } + + /// Final configurations to the converter: + std::cout << "Configuring converters:" << std::endl; + BOOST_FOREACH(priority_type prio_i, conv_list.keys()) { + std::cout << "* [" << prio_i << "]: "; + configure_conv(conv_list[prio_i], in_type, out_type); + } + + /// Run the benchmark for every converter //////////////////////////////// + std::cout << "{{{" << std::endl; + if (not debug_mode) { + std::cout << "prio,duration_ms,avg_duration_ms,n_samples,iterations" << std::endl; + BOOST_FOREACH(priority_type prio_i, conv_list.keys()) { + double duration = run_benchmark( + conv_list[prio_i], + input_buf_refs, + output_buf_refs, + n_samples, + iterations + ); + std::cout << boost::format("%i,%d,%d,%d,%d") + % prio_i + % (duration * 1000) + % (duration * 1000.0 / iterations) + % n_samples + % iterations + << std::endl; + } + } + + /// Or run debug mode, which runs one conversion and prints the results //// + if (debug_mode) { + // Only run on the first converter: + run_benchmark( + conv_list[conv_list.keys().at(0)], + input_buf_refs, + output_buf_refs, + n_samples, + iterations + ); + for (size_t i = 0; i < n_samples; i++) { + std::cout << item_to_string(input_buf_refs[0], i, in_type, vm.count("hex")) + << ";" + << item_to_string(reinterpret_cast< const void * >(output_buf_refs[0]), i, out_type, vm.count("hex")) + << std::endl; + } + } + std::cout << "}}}" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/host/utils/converter_benchmark.py b/host/utils/converter_benchmark.py new file mode 100644 index 000000000..c3cab8753 --- /dev/null +++ b/host/utils/converter_benchmark.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python +# +# Copyright 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/>. +# +""" +Wrap the converter_benchmark tool and produce prettier results. +""" + +from __future__ import print_function +import argparse +import csv +import subprocess + +INTRO_SETUP = { + 'n_samples': { + 'title': 'Samples per iteration', + }, + 'iterations': { + 'title': 'Number of iterations' + }, +} + +TABLE_SETUP = { + 'prio': { + 'title': 'Priority', + }, + 'duration_ms': { + 'title': 'Total Duration (ms)', + }, + 'avg_duration_ms': { + 'title': 'Avg. Duration (ms)', + }, +} + +def run_benchmark(args): + """ Run the tool with the given arguments, return the section in the {{{ }}} brackets """ + call_args = ['./converter_benchmark',] + for k, v in args.__dict__.iteritems(): + k = k.replace('_', '-') + if v is None: + continue + if k in ('debug-converter', 'hex'): + if v: + call_args.append('--{0}'.format(k)) + continue + call_args.append('--{0}'.format(k)) + call_args.append(str(v)) + print(call_args) + try: + output = subprocess.check_output(call_args) + except subprocess.CalledProcessError as ex: + print(ex.output) + exit(ex.returncode) + header_out, csv_output = output.split('{{{', 1) + csv_output = csv_output.split('}}}', 1) + assert len(csv_output) == 2 and csv_output[1].strip() == '' + return header_out, csv_output[0] + +def print_stats_table(args, csv_output): + """ + Print stats. + """ + reader = csv.reader(csv_output.strip().split('\n'), delimiter=',') + title_row = reader.next() + row_widths = [0,] * len(TABLE_SETUP) + for idx, row in enumerate(reader): + if idx == 0: + # Print intro: + for k, v in INTRO_SETUP.iteritems(): + print("{title}: {value}".format( + title=v['title'], + value=row[title_row.index(k)], + )) + print("") + # Print table header + for idx, item in enumerate(TABLE_SETUP): + print(" {title} ".format(title=TABLE_SETUP[item]['title']), end='') + row_widths[idx] = len(TABLE_SETUP[item]['title']) + if idx < len(TABLE_SETUP) - 1: + print("|", end='') + print("") + for idx, item in enumerate(TABLE_SETUP): + print("-" * (row_widths[idx] + 2), end='') + if idx < len(TABLE_SETUP) - 1: + print("+", end='') + print("") + # Print actual row data + for idx, item in enumerate(TABLE_SETUP): + format_str = " {{item:>{n}}} ".format(n=row_widths[idx]) + print(format_str.format(item=row[title_row.index(item)]), end='') + if idx < len(TABLE_SETUP) - 1: + print("|", end='') + print("") + +def print_debug_table(args, csv_output): + """ + Print debug output. + """ + reader = csv.reader(csv_output.strip().split('\n'), delimiter=';') + print_widths_hex = { + 'u8': 2, + 'sc16': 8, + 'fc32': 16, + 's16': 4, + } + if args.hex: + format_str = "{{0[0]:0>{n_in}}} => {{0[1]:0>{n_out}}}".format( + n_in=print_widths_hex[getattr(args, 'in').split('_', 1)[0]], + n_out=print_widths_hex[args.out.split('_', 1)[0]] + ) + else: + format_str = "{0[0]}\t=>\t{0[1]}" + for row in reader: + print(format_str.format(row)) + +def setup_argparse(): + """ Configure arg parser. """ + parser = argparse.ArgumentParser( + description="UHD Converter Benchmark + Debugging Utility.", + ) + parser.add_argument( + "-i", "--in", required=True, + help="Input format (e.g. 'sc16')" + ) + parser.add_argument( + "-o", "--out", required=True, + help="Output format (e.g. 'sc16')" + ) + parser.add_argument( + "-s", "--samples", type=int, + help="Number of samples per iteration" + ) + parser.add_argument( + "-N", "--iterations", type=int, + help="Number of iterations per benchmark", + ) + parser.add_argument( + "-p", "--priorities", + help="Converter priorities. Can be 'default', 'all', or a comma-separated list of priorities.", + ) + parser.add_argument( + "--max-prio", type=int, + help="Largest available priority (advanced feature)", + ) + parser.add_argument( + "--n-inputs", type=int, + help="Number of input vectors", + ) + parser.add_argument( + "--n-outputs", type=int, + help="Number of output vectors", + ) + parser.add_argument( + "--seed-mode", choices=('random', 'incremental'), + help="How to initialize the data: random, incremental", + ) + parser.add_argument( + "--debug-converter", action='store_true', + help="Skip benchmark and print conversion results. Implies iterations==1 and will only run on a single converter.", + ) + parser.add_argument( + "--hex", action='store_true', + help="In debug mode, display data as hex values.", + ) + return parser + +def main(): + """ Go, go, go! """ + args = setup_argparse().parse_args() + print("Running converter benchmark...") + header_out, csv_output = run_benchmark(args) + print(header_out) + if args.debug_converter: + print_debug_table(args, csv_output) + else: + print_stats_table(args, csv_output) + +if __name__ == "__main__": + main() + diff --git a/host/utils/fx2_init_eeprom.cpp b/host/utils/fx2_init_eeprom.cpp index 5711b73e0..cf7fb2de2 100644 --- a/host/utils/fx2_init_eeprom.cpp +++ b/host/utils/fx2_init_eeprom.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010,2014 Ettus Research LLC +// Copyright 2010,2014,2016 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 @@ -20,8 +20,17 @@ #include <uhd/property_tree.hpp> #include <boost/program_options.hpp> #include <boost/format.hpp> +#include <boost/algorithm/string/predicate.hpp> #include <iostream> -#include <cstdlib> +//#include <cstdlib> +#ifdef UHD_PLATFORM_LINUX +#include <fstream> +#include <unistd.h> // syscall constants +#include <fcntl.h> // O_NONBLOCK +#include <sys/syscall.h> +#include <cerrno> +#include <cstring> // for std::strerror +#endif //UHD_PLATFORM_LINUX const std::string FX2_VENDOR_ID("0x04b4"); const std::string FX2_PRODUCT_ID("0x8613"); @@ -49,10 +58,22 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ return EXIT_FAILURE; } - //cant find a uninitialized usrp with this mystery module in the way... - if (std::system("/sbin/rmmod usbtest") != 0){ - std::cerr << "Did not rmmod usbtest, this may be ok..." << std::endl; +#ifdef UHD_PLATFORM_LINUX + //can't find an uninitialized usrp with this mystery usbtest in the way... + std::string module("usbtest"); + std::ifstream modules("/proc/modules"); + bool module_found = false; + std::string module_line; + while(std::getline(modules, module_line) && (!module_found)) { + module_found = boost::starts_with(module_line, module); } + if(module_found) { + std::cout << boost::format("Found the '%s' module. Unloading it.\n" ) % module; + int fail = syscall(__NR_delete_module, module.c_str(), O_NONBLOCK); + if(fail) + std::cerr << ( boost::format("Removing the '%s' module failed with error '%s'.\n") % module % std::strerror(errno) ); + } +#endif //UHD_PLATFORM_LINUX //load the options into the address uhd::device_addr_t device_addr; diff --git a/host/utils/query_gpsdo_sensors.cpp b/host/utils/query_gpsdo_sensors.cpp index 3b98a634c..4c17c6044 100644 --- a/host/utils/query_gpsdo_sensors.cpp +++ b/host/utils/query_gpsdo_sensors.cpp @@ -1,5 +1,5 @@ // -// Copyright 2012,2014 Ettus Research LLC +// Copyright 2012,2014,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 @@ -38,7 +38,6 @@ void print_notes(void) { std::cout << boost::format("**************************************Helpful Notes on Clock/PPS Selection**************************************\n"); std::cout << boost::format("As you can see, the default 10 MHz Reference and 1 PPS signals are now from the GPSDO.\n"); std::cout << boost::format("If you would like to use the internal reference(TCXO) in other applications, you must configure that explicitly.\n"); - std::cout << boost::format("You can no longer select the external SMAs for 10 MHz or 1 PPS signaling.\n"); std::cout << boost::format("****************************************************************************************************************\n"); } @@ -51,7 +50,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ po::options_description desc("Allowed options"); desc.add_options() ("help", "help message") - ("args", po::value<std::string>(&args)->default_value(""), "Specify a single USRP.") + ("args", po::value<std::string>(&args)->default_value(""), "Device address arguments specifying a single USRP") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); @@ -59,8 +58,11 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //Print the help message if (vm.count("help")) { - std::cout << boost::format("Query GPSDO Sensors %s") % desc << std::endl; - return EXIT_FAILURE; + std::cout << boost::format("Query GPSDO Sensors, try to lock the reference oscillator to the GPS disciplined clock, and set the device time to GPS time") + << std::endl + << std::endl + << desc; + return EXIT_FAILURE; } //Create a USRP device @@ -68,9 +70,6 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); std::cout << boost::format("Using Device: %s\n") % usrp->get_pp_string(); - print_notes(); - - //Verify GPS sensors are present (i.e. EEPROM has been burnt) std::vector<std::string> sensor_names = usrp->get_mboard_sensor_names(0); @@ -82,38 +81,96 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ exit(EXIT_FAILURE); } + // Explicitly set time source to gpsdo + boost::this_thread::sleep(boost::posix_time::seconds(1)); + try { + usrp->set_time_source("gpsdo"); + } catch (uhd::value_error &e) { + std::cout << "could not set the time source to \"gpsdo\"; error was:" <<std::endl; + std::cout << e.what() << std::endl; + std::cout << "trying \"external\"..." <<std::endl; + try { + usrp->set_time_source("external"); + } catch (uhd::value_error &e) { + std::cout << "\"external\" failed, too." << std::endl; + } + } + std::cout<< std::endl << "Time source is now " << usrp->get_time_source(0) << std::endl; + //Check for GPS lock uhd::sensor_value_t gps_locked = usrp->get_mboard_sensor("gps_locked",0); if(not gps_locked.to_bool()) { - std::cout << boost::format("\nGPS does not have lock. Wait a few minutes and try again.\n"); - std::cout << boost::format("NMEA strings and device time may not be accurate until lock is achieved.\n\n"); - } else - std::cout << boost::format("GPS Locked\n"); + std::cout << boost::format("\nGPS does not have lock. Wait a few minutes and try again.\n"); + std::cout << boost::format("NMEA strings and device time may not be accurate until lock is achieved.\n\n"); + } else { + std::cout << boost::format("GPS Locked"); + } + + std::cout << "\nSetting the reference clock source to \"gpsdo\"...\n"; + try { + usrp->set_clock_source("gpsdo"); + } catch (uhd::value_error &e) { + std::cout << "could not set the clock source to \"gpsdo\"; error was:" <<std::endl; + std::cout << e.what() << std::endl; + std::cout << "trying \"external\"..." <<std::endl; + try{ + usrp->set_clock_source("external"); + } catch (uhd::value_error &e) { + std::cout << "\"external\" failed, too." << std::endl; + } + } + std::cout<< std::endl << "Clock source is now " << usrp->get_clock_source(0) << std::endl; + + print_notes(); + //Check for 10 MHz lock if(std::find(sensor_names.begin(), sensor_names.end(), "ref_locked") != sensor_names.end()) { - uhd::sensor_value_t gps_locked = usrp->get_mboard_sensor("ref_locked",0); - if(not gps_locked.to_bool()) { - std::cout << boost::format("USRP NOT Locked to GPSDO 10 MHz Reference.\n"); - std::cout << boost::format("Double check installation instructions (N2X0/E1X0 only): https://www.ettus.com/content/files/gpsdo-kit_4.pdf\n\n"); - } else - std::cout << boost::format("USRP Locked to GPSDO 10 MHz Reference.\n"); - }else - std::cout << boost::format("ref_locked sensor not present on this board.\n"); - - // Explicitly set time source to gpsdo - usrp->set_time_source("gpsdo"); + uhd::sensor_value_t gps_locked = usrp->get_mboard_sensor("ref_locked",0); + if(not gps_locked.to_bool()) { + std::cout << boost::format("USRP NOT Locked to GPSDO 10 MHz Reference.\n"); + std::cout << boost::format("Double check installation instructions (N2X0/E1X0 only): https://www.ettus.com/content/files/gpsdo-kit_4.pdf\n\n"); + std::cout << boost::format("Locking the internal reference to the GPSDO might take a second to reach stability. Retrying in 10 s...\n\n"); + boost::this_thread::sleep(boost::posix_time::seconds(10)); + } + if(usrp->get_mboard_sensor("ref_locked",0).to_bool()) { + std::cout << boost::format("USRP Locked to GPSDO 10 MHz Reference.\n"); + } + } else { + std::cout << boost::format("ref_locked sensor not present on this board.\n"); + } //Check PPS and compare UHD device time to GPS time boost::this_thread::sleep(boost::posix_time::seconds(1)); uhd::sensor_value_t gps_time = usrp->get_mboard_sensor("gps_time"); - const time_t pc_clock_time = time(NULL); - const uhd::time_spec_t last_pps_time = usrp->get_time_last_pps(); + uhd::time_spec_t last_pps_time = usrp->get_time_last_pps(); + + //we only care about the full seconds + signed gps_seconds = gps_time.to_int(); + long long pps_seconds = last_pps_time.to_ticks(1.0); + + if(pps_seconds != gps_seconds) { + std::cout << boost::format("\nGPS and UHD Device time are NOT aligned;\nlast_pps: %ld vs gps: %ld. Trying to set the device time to GPS time...") + % pps_seconds % gps_seconds + << std::endl; + //full next after next second + uhd::time_spec_t next_pps_time(gps_seconds + 2.0); + //instruct the USRP to wait for the next PPS edge, then set the new time on the following PPS + usrp->set_time_unknown_pps(next_pps_time); + //allow some time to make sure the PPS has come… + boost::this_thread::sleep(boost::posix_time::seconds(1.1)); + //…then ask + gps_seconds = usrp->get_mboard_sensor("gps_time").to_int(); + pps_seconds = usrp->get_time_last_pps().to_ticks(1.0); + } - if (last_pps_time.to_ticks(1.0) == gps_time.to_int()) { + std::cout << boost::format("last_pps: %ld vs gps: %ld.") + % pps_seconds % gps_seconds + << std::endl; + if (pps_seconds == gps_seconds) { std::cout << boost::format("GPS and UHD Device time are aligned.\n"); } else { - std::cout << boost::format("\nGPS and UHD Device time are NOT aligned last_pps: %ld vs gps: %ld. Try re-running the program. Double check 1 PPS connection from GPSDO.\n\n") % last_pps_time.to_ticks(1.0) % gps_time.to_int() << std::endl; + std::cout << boost::format("Could not align UHD Device time to GPS time. Giving up.\n"); } //print NMEA strings @@ -121,12 +178,14 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::sensor_value_t gga_string = usrp->get_mboard_sensor("gps_gpgga"); uhd::sensor_value_t rmc_string = usrp->get_mboard_sensor("gps_gprmc"); std::cout << boost::format("Printing available NMEA strings:\n"); - std::cout << boost::format("%s\n%s\n%s\n") % gga_string.to_pp_string() % rmc_string.to_pp_string() % gps_time.to_pp_string(); - } catch (std::exception &) { + std::cout << boost::format("%s\n%s\n") % gga_string.to_pp_string() % rmc_string.to_pp_string(); + } catch (uhd::lookup_error &e) { std::cout << "NMEA strings not implemented for this device." << std::endl; } - std::cout << boost::format("UHD Device time: %.0f seconds\n") % (last_pps_time.get_real_secs()); - std::cout << boost::format("PC Clock time: %.0f seconds\n") % pc_clock_time; + std::cout << boost::format("GPS Epoch time at last PPS: %.5f seconds\n") % usrp->get_mboard_sensor("gps_time").to_real(); + std::cout << boost::format("UHD Device time last PPS: %.5f seconds\n") % (usrp->get_time_last_pps().get_real_secs()); + std::cout << boost::format("UHD Device time right now: %.5f seconds\n") % (usrp->get_time_now().get_real_secs()); + std::cout << boost::format("PC Clock time: %.5f seconds\n") % time(NULL); //finished std::cout << boost::format("\nDone!\n\n"); diff --git a/host/utils/uhd_config_info.cpp b/host/utils/uhd_config_info.cpp new file mode 100644 index 000000000..4279c325d --- /dev/null +++ b/host/utils/uhd_config_info.cpp @@ -0,0 +1,89 @@ +// +// Copyright 2015 National Instruments Corp. +// +// 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/build_info.hpp> +#include <uhd/version.hpp> +#include <uhd/utils/safe_main.hpp> + +#include <boost/format.hpp> +#include <boost/program_options.hpp> + +namespace po = boost::program_options; + +int UHD_SAFE_MAIN(int argc, char* argv[]) { + // Program Options + po::options_description desc("Allowed Options"); + desc.add_options() + ("build-date", "Print build date") + ("c-compiler", "Print C compiler") + ("cxx-compiler", "Print C++ compiler") + ("c-flags", "Print C compiler flags") + ("cxx-flags", "Print C++ compiler flags") + ("enabled-components", "Print built-time enabled components") + ("install-prefix", "Print install prefix") + ("libusb-version", "Print libusb version") + ("print-all", "Print everything") + ("version", "Print this UHD build's version") + ("help", "Print help message") + ; + + 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") > 0) { + std::cout << boost::format("UHD Config Info - %s") % desc << std::endl; + return EXIT_FAILURE; + } + + bool print_all = (vm.count("print-all") > 0); + + if(vm.count("version") > 0 or print_all) { + std::cout << "UHD " << uhd::get_version_string() << std::endl; + } + if(vm.count("build-date") > 0 or print_all) { + std::cout << "Build date: " << uhd::build_info::build_date() << std::endl; + } + if(vm.count("c-compiler") > 0 or print_all) { + std::cout << "C compiler: " << uhd::build_info::c_compiler() << std::endl; + } + if(vm.count("cxx-compiler") > 0 or print_all) { + std::cout << "C++ compiler: " << uhd::build_info::cxx_compiler() << std::endl; + } + if(vm.count("c-flags") > 0 or print_all) { + std::cout << "C flags: " << uhd::build_info::c_flags() << std::endl; + } + if(vm.count("cxx-flags") > 0 or print_all) { + std::cout << "C++ flags: " << uhd::build_info::cxx_flags() << std::endl; + } + if(vm.count("enabled-components") > 0 or print_all) { + std::cout << "Enabled components: " << uhd::build_info::enabled_components() << std::endl; + } + if(vm.count("install-prefix") > 0 or print_all) { + std::cout << "Install prefix: " << uhd::build_info::install_prefix() << std::endl; + } + if(vm.count("boost-version") > 0 or print_all) { + std::cout << "Boost version: " << uhd::build_info::boost_version() << std::endl; + } + if(vm.count("libusb-version") > 0 or print_all) { + std::string _libusb_version = uhd::build_info::libusb_version(); + std::cout << "Libusb version: " << (_libusb_version.empty() ? "N/A" : _libusb_version) << std::endl; + } + + return EXIT_SUCCESS; +} |