Hi Kasper, There are several caveats/issues/topics to consider with regards to running at higher sample rates with the B2xx. Generally speaking, Linux will offer better performance than Windows.
What version of UHD are you using? If you're not using UHD 3.10.3.0, can you please try upgrading? UHD 3.10.3.0 includes a commit [1] and updated firmware, which optimizes the FX3 performance. The i5-3210M may be slightly under-powered for the task, however you can try all the following performance tuning adjustments, and it may be able to support 56 MS/s. It is worth noting that the recent KPTI patches and other related workarounds [2] for Intel CPUs to protect against Meltdown/Spectre attacks [3], may cause a considerable overhead. Here [4] are instructions on how to check to see if KPTI is enabled for Ubuntu. You may want to disable KPTI, if it is enabled on your system, then test to see how much additional overhead it creates, running your application. Adjust your CPU Governor to "performance". This can be done with the cpu-frequtils utility [5]. ( sudo cpufreq-set -g performance ) Ensure your CPU is not throttling due to overheating ( sudo cat /var/log/syslog | grep throttled ). This is very common in laptops, especially older devices where the thermal grease is in need of replacement. You can test using sc8 and sc12 OTW (over the wire) [6] sample sizes with the benchmark_rate [7] utility. Using sc12 will not drop any information as the ADC/DAC on the B2xx is 12bits. ./benchmark_rate --rx_otw sc12 --rx_rate 40e6 ./benchmark_rate --tx_otw sc8 --tx_rate 40e6 Some USB controllers can be problematic. Intel Series 7/8/9 USB controllers usually offer the best performance. Using Thinkpads (T430s, T470p) I've found that a recv/send frame size of 8192 tends to work the best at higher sample rates. As Marcus mentioned, the UHD examples are provided as an API reference and not tuned for performance. Case in point is rx_samples_to_file being single threaded. GNU Radio will by default offer a multi-threaded architecture, which can be useful to test. You may need to adjust the min buffer sizes to handle the higher sample rates however within the GR Blocks. I've attached an example of rx_samples_to_file.cpp which is multi-thread and has additional buffering. Without a SSD or NVMe hard drive, sustaining a high sample rate to disk can be difficult. Depending upon your system configuration, you may want to consider using a ram disk. I would recommend leaving at least 2-8 GB of ram for your host OS (this is dependent upon your application etc). This will however limit the length of time you can save to disk (as limited by the ram in the machine). Below is an example to create a 24GB ramdisk: mkdir -p ~/ramfs mount -t tmpfs -o size=24G tmpfs ~/ramfs [1] - https://github.com/EttusResearch/uhd/commit/d95613152da3e7c7f41c71acca65101ed0896893 [2] - https://en.wikipedia.org/wiki/Kernel_page-table_isolation [3] - https://en.wikipedia.org/wiki/Meltdown_(security_vulnerability) [4] - https://askubuntu.com/questions/992137/how-to-check-that-kpti-is-enabled-on-my-ubuntu [5] - http://www.thinkwiki.org/wiki/How_to_use_cpufrequtils [6] - https://files.ettus.com/manual/page_stream.html#stream_datatypes_otw [7] - https://github.com/EttusResearch/uhd/blob/maint/host/examples/benchmark_rate.cpp Regards, Nate Temple On Tue, Feb 13, 2018 at 11:59 AM, Marcus D. Leech via USRP-users < usrp-users@lists.ettus.com> wrote: > On 02/13/2018 04:52 AM, Kasper Føns via USRP-users wrote: > >> Hi. >> >> We have bought a B200 board and are having issues simply receiving the >> samples and would like some support in the matter. >> >> Running the command >> ./rx_samples_to_file --null --rate 56000000 >> on a Sony Vaio Z with an I5-3210M running Ubuntu Server 17.10 shows a >> high CPU usage of ~78%. >> >> Is such a high CPU usage expected? >> Switching terminal windows (ALT + F1 or ALT + F2) is enough to cause an >> overflow on the Linux host. >> >> >> There is also a high CPU usage on a Windows 10 machine (ThinkPad, >> i7-4810MQ). >> Running >> ./rx_samples_to_file --null --rate 56000000 >> results in a infinite stream of overflows. >> >> Running >> ./rx_samples_to_file --null --rate 32000000 >> utilizes 22% CPU and still overflows once in a while. Moving a >> calculator window around the screen results in overflows. >> >> We have tried increasing buffers using --args="recv_frame_size=X, >> num_recv_frames=Y" >> However, we haven't been able to increase X to higher values than ~16000 >> (16384 fails with lots of overflows). >> The same applies to Y, where 300 fails with an error. >> >> The software was compiled in release mode an ran over a USB 3 connection. >> >> Thus, for USB transfers using the B200: >> - On Vaio: Is ~78% CPU usage expected for 56 Msps ? >> - On Win10: Is it not possible to receive 56 Msps? >> - On Win10: Is 22% CPU usage expected for 32 Msps? >> - Is there some limit to recv_frame_size? A value of 16384 fails with >> infinite overflows. >> - Is there some way of tuning the framework for lower CPU? >> >> W_______________________________________________ >> USRP-users mailing list >> USRP-users@lists.ettus.com >> http://lists.ettus.com/mailman/listinfo/usrp-users_lists.ettus.com >> >> e hope you are able to help! >> > Let's do some first-order math. > > You're bringing in ~5e7 samples/second > If we optimistically assume a mean instructions-per-sample (including both > kernel and user-space code) of 100 instructions/sample, then we're talking a > requirement for 5e9 instructions/second. If your CPU is running at 3e9 > Hz, then it'll need to, on average, issue 1.6 instructions/cycle. > > You'll generally get more "mileage" out of num_recv_frames than the frame > size. On any given system, my understanding is that this is a shared > resource > (across a given USB controller, I think, but don't quote me). > > Now, the rx_samples_to_file application is single-threaded, so it's trying > to service the data-stream from the USRP at the same time as it's making > filesystem > calls to write the data (even if to /dev/null). That's a tall order for > a single-threaded application running at 5.6e7sps. These applications, > provided with > UHD, are generally intended as *coding examples*, and no guarantees > exist with respect to performance on any given system. Furthermore, some > USB3 controllers are better at handling bulk high-data-rate applications > than others, and the controller landscape changes so quickly that it's next > to > impossible to provide up-to-date recommendations in that department. > > If you install Gnu Radio, there's an application called "uhd_rx_cfile" > that takes advantage of the multi-threaded nature of Gnu Radio, and does > better. > > But keep *firmly in mind* that once you migrate from writing to /dev/null > to writing to real disk hardware, 5e7 samples/second is going to result in > a LOT of disk I/O--more than most ordinary single-disk, non-RAID disk > systems can usefully sustain. > > > > > _______________________________________________ > USRP-users mailing list > USRP-users@lists.ettus.com > http://lists.ettus.com/mailman/listinfo/usrp-users_lists.ettus.com >
cmake_minimum_required(VERSION 2.8) option(UHD_USE_STATIC_LIBS OFF) set(BOOST_REQUIRED_COMPONENTS program_options system thread ) if(MSVC) set(BOOST_ALL_DYN_LINK "${BOOST_ALL_DYN_LINK}" CACHE BOOL "boost enable dynamic linking") if(BOOST_ALL_DYN_LINK) add_definitions(-DBOOST_ALL_DYN_LINK) #setup boost auto-linking in msvc else(BOOST_ALL_DYN_LINK) set(BOOST_REQUIRED_COMPONENTS) #empty components list for static link endif(BOOST_ALL_DYN_LINK) endif(MSVC) find_package(Boost "1.46" REQUIRED ${BOOST_REQUIRED_COMPONENTS}) find_package(UHD "3.8.0" REQUIRED) IF(CMAKE_VERSION VERSION_LESS "3.1") IF(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") SET(CMAKE_CXX_FLAGS "--std=gnu++11 ${CMAKE_CXX_FLAGS}") ELSEIF(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") IF("${IS_APPLE}" STREQUAL "") SET(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}") ELSE() SET(CMAKE_CXX_FLAGS "-std=c++11 -stdlib=libc++ ${CMAKE_CXX_FLAGS}") ENDIF() ENDIF() ELSE() SET(CMAKE_CXX_STANDARD 11) ENDIF() IF(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" AND ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") SET(CMAKE_EXE_LINKER_FLAGS "-lthr ${CMAKE_EXE_LINKER_FLAGS}") SET(CMAKE_CXX_FLAGS "-stdlib=libc++ ${CMAKE_CXX_FLAGS}") ENDIF() include_directories( ${Boost_INCLUDE_DIRS} ${UHD_INCLUDE_DIRS} ) link_directories(${Boost_LIBRARY_DIRS}) add_executable(rx_samples_to_file rx_samples_to_file.cpp) SET(CMAKE_BUILD_TYPE "Release") if(NOT UHD_USE_STATIC_LIBS) message(STATUS "Linking against shared UHD library.") target_link_libraries(rx_samples_to_file ${UHD_LIBRARIES} ${Boost_LIBRARIES}) else(NOT UHD_USE_STATIC_LIBS) message(STATUS "Linking against static UHD library.") target_link_libraries(rx_samples_to_file ${UHD_STATIC_LIB_LINK_FLAG} ${UHD_STATIC_LIB_DEPS} ) endif(NOT UHD_USE_STATIC_LIBS)
// // Copyright 2010-2011,2014,2017 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/types/tune_request.hpp> #include <uhd/utils/thread_priority.hpp> #include <uhd/utils/safe_main.hpp> #include <uhd/usrp/multi_usrp.hpp> #include <uhd/exception.hpp> #include <uhd/transport/bounded_buffer.hpp> #include <boost/program_options.hpp> #include <boost/format.hpp> #include <boost/thread.hpp> #include <iostream> #include <fstream> #include <csignal> #include <complex> namespace po = boost::program_options; struct write_info_t { void *buff; size_t len; }; static const size_t NUM_BUFFERS = 1024; static bool stop_signal_called = false; void sig_int_handler(int){stop_signal_called = true;} uhd::transport::bounded_buffer<write_info_t *> g_empty_queue(NUM_BUFFERS); uhd::transport::bounded_buffer<write_info_t *> g_full_queue(NUM_BUFFERS); void write_to_file(std::string &filename) { write_info_t *write_info; std::ofstream outfile; outfile.open(filename.c_str(), std::ofstream::binary); while (not stop_signal_called) { if (g_full_queue.pop_with_timed_wait(write_info, 0.1)) { outfile.write((const char*)write_info->buff, write_info->len); g_empty_queue.push_with_wait(write_info); } } outfile.close(); } template<typename samp_type> void recv_to_file( uhd::usrp::multi_usrp::sptr usrp, const std::string &cpu_format, const std::string &wire_format, const std::string &channel, const std::string &file, size_t samps_per_buff, unsigned long long num_requested_samples, double time_requested = 0.0, bool bw_summary = false, bool stats = false, bool null = false, bool enable_size_map = false, bool continue_on_bad_packet = false ){ unsigned long long num_total_samps = 0; //create a receive streamer uhd::stream_args_t stream_args(cpu_format,wire_format); std::vector<size_t> channel_nums; channel_nums.push_back(boost::lexical_cast<size_t>(channel)); stream_args.channels = channel_nums; uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); uhd::rx_metadata_t md; std::vector< std::vector<samp_type> > buffs(NUM_BUFFERS, std::vector<samp_type>(samps_per_buff)); write_info_t descriptors[NUM_BUFFERS]; for (size_t i = 0; i < NUM_BUFFERS; i++) { descriptors[i].buff = &buffs[i].front(); descriptors[i].len = 0; g_empty_queue.push_with_haste(&descriptors[i]); } bool overflow_message = true; boost::thread_group thread_group; if (not null) { thread_group.create_thread(boost::bind(&write_to_file, file)); } boost::this_thread::sleep(boost::posix_time::milliseconds(100)); //setup streaming uhd::stream_cmd_t stream_cmd((num_requested_samples == 0)? uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS: uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE ); stream_cmd.num_samps = size_t(num_requested_samples); stream_cmd.stream_now = true; stream_cmd.time_spec = uhd::time_spec_t(); rx_stream->issue_stream_cmd(stream_cmd); boost::system_time start = boost::get_system_time(); unsigned long long ticks_requested = (long)(time_requested * (double)boost::posix_time::time_duration::ticks_per_second()); boost::posix_time::time_duration ticks_diff; boost::system_time last_update = start; unsigned long long last_update_samps = 0; typedef std::map<size_t,size_t> SizeMap; SizeMap mapSizes; while(not stop_signal_called and (num_requested_samples != num_total_samps or num_requested_samples == 0)) { boost::system_time now = boost::get_system_time(); write_info_t *info; if (not g_empty_queue.pop_with_haste(info)) { std::cout << "No available buffers" << std::endl; g_empty_queue.pop_with_wait(info); } size_t num_rx_samps = rx_stream->recv(info->buff, samps_per_buff, md, 3.0, enable_size_map); if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) { std::cout << boost::format("Timeout while streaming") << std::endl; break; } if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW){ if (overflow_message) { overflow_message = false; std::cerr << boost::format( "Got an overflow indication. Please consider the following:\n" " Your write medium must sustain a rate of %fMB/s.\n" " Dropped samples will not be written to the file.\n" " Please modify this example for your purposes.\n" " This message will not appear again.\n" ) % (usrp->get_rx_rate()*sizeof(samp_type)/1e6); } continue; } if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ std::string error = str(boost::format("Receiver error: %s") % md.strerror()); if (continue_on_bad_packet){ std::cerr << error << std::endl; continue; } else throw std::runtime_error(error); } if (enable_size_map) { SizeMap::iterator it = mapSizes.find(num_rx_samps); if (it == mapSizes.end()) mapSizes[num_rx_samps] = 0; mapSizes[num_rx_samps] += 1; } num_total_samps += num_rx_samps; if (null) { g_empty_queue.push_with_haste(info); } else { info->len = num_rx_samps * sizeof(samp_type); if (not g_full_queue.push_with_haste(info)) { std::cout << "Unable to push buffer into queue" << std::endl; g_full_queue.push_with_wait(info); } } if (bw_summary) { last_update_samps += num_rx_samps; boost::posix_time::time_duration update_diff = now - last_update; if (update_diff.ticks() > boost::posix_time::time_duration::ticks_per_second()) { double t = (double)update_diff.ticks() / (double)boost::posix_time::time_duration::ticks_per_second(); double r = (double)last_update_samps / t; std::cout << boost::format("\t%f Msps") % (r/1e6) << std::endl; last_update_samps = 0; last_update = now; } } ticks_diff = now - start; if (ticks_requested > 0){ if ((unsigned long long)ticks_diff.ticks() > ticks_requested) break; } } stop_signal_called = true; stream_cmd.stream_mode = uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS; rx_stream->issue_stream_cmd(stream_cmd); while (md.error_code != uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) { rx_stream->recv(&buffs[0].front(), samps_per_buff, md, 0.1, enable_size_map); } if (stats) { std::cout << std::endl; double t = (double)ticks_diff.ticks() / (double)boost::posix_time::time_duration::ticks_per_second(); std::cout << boost::format("Received %d samples in %f seconds") % num_total_samps % t << std::endl; double r = (double)num_total_samps / t; std::cout << boost::format("%f Msps") % (r/1e6) << std::endl; if (enable_size_map) { std::cout << std::endl; std::cout << "Packet size map (bytes: count)" << std::endl; for (SizeMap::iterator it = mapSizes.begin(); it != mapSizes.end(); it++) std::cout << it->first << ":\t" << it->second << std::endl; } } } typedef boost::function<uhd::sensor_value_t (const std::string&)> get_sensor_fn_t; bool check_locked_sensor(std::vector<std::string> sensor_names, const char* sensor_name, get_sensor_fn_t get_sensor_fn, double setup_time){ if (std::find(sensor_names.begin(), sensor_names.end(), sensor_name) == sensor_names.end()) return false; boost::system_time start = boost::get_system_time(); boost::system_time first_lock_time; std::cout << boost::format("Waiting for \"%s\": ") % sensor_name; std::cout.flush(); while (true) { if ((not first_lock_time.is_not_a_date_time()) and (boost::get_system_time() > (first_lock_time + boost::posix_time::seconds(setup_time)))) { std::cout << " locked." << std::endl; break; } if (get_sensor_fn(sensor_name).to_bool()){ if (first_lock_time.is_not_a_date_time()) first_lock_time = boost::get_system_time(); std::cout << "+"; std::cout.flush(); } else { first_lock_time = boost::system_time(); //reset to 'not a date time' if (boost::get_system_time() > (start + boost::posix_time::seconds(setup_time))){ std::cout << std::endl; throw std::runtime_error(str(boost::format("timed out waiting for consecutive locks on sensor \"%s\"") % sensor_name)); } std::cout << "_"; std::cout.flush(); } boost::this_thread::sleep(boost::posix_time::milliseconds(100)); } std::cout << std::endl; return true; } int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::set_thread_priority_safe(); //variables to be set by po std::string args, file, type, ant, subdev, ref, wirefmt, channel; size_t total_num_samps, spb; double rate, freq, gain, bw, total_time, setup_time; //setup the program options po::options_description desc("Allowed options"); desc.add_options() ("help", "help message") ("args", po::value<std::string>(&args)->default_value(""), "multi uhd device address args") ("file", po::value<std::string>(&file)->default_value("usrp_samples.dat"), "name of the file to write binary samples to") ("type", po::value<std::string>(&type)->default_value("short"), "sample type: double, float, or short") ("nsamps", po::value<size_t>(&total_num_samps)->default_value(0), "total number of samples to receive") ("duration", po::value<double>(&total_time)->default_value(0), "total number of seconds to receive") ("time", po::value<double>(&total_time), "(DEPRECATED) will go away soon! Use --duration instead") ("spb", po::value<size_t>(&spb)->default_value(10000), "samples per buffer") ("rate", po::value<double>(&rate)->default_value(1e6), "rate of incoming samples") ("freq", po::value<double>(&freq)->default_value(0.0), "RF center frequency in Hz") ("gain", po::value<double>(&gain), "gain for the RF chain") ("ant", po::value<std::string>(&ant), "antenna selection") ("subdev", po::value<std::string>(&subdev), "subdevice specification") ("channel", po::value<std::string>(&channel)->default_value("0"), "which channel to use") ("bw", po::value<double>(&bw), "analog frontend filter bandwidth in Hz") ("ref", po::value<std::string>(&ref)->default_value("internal"), "reference source (internal, external, mimo)") ("wirefmt", po::value<std::string>(&wirefmt)->default_value("sc16"), "wire format (sc8 or sc16)") ("setup", po::value<double>(&setup_time)->default_value(1.0), "seconds of setup time") ("progress", "periodically display short-term bandwidth") ("stats", "show average bandwidth on exit") ("sizemap", "track packet size and display breakdown on exit") ("null", "run without writing to file") ("continue", "don't abort on a bad packet") ("skip-lo", "skip checking LO lock status") ("int-n", "tune USRP with integer-N tuning") ; 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 RX samples to file %s") % desc << std::endl; std::cout << std::endl << "This application streams data from a single channel of a USRP device to a file.\n" << std::endl; return ~0; } bool bw_summary = vm.count("progress") > 0; bool stats = vm.count("stats") > 0; bool null = vm.count("null") > 0; bool enable_size_map = vm.count("sizemap") > 0; bool continue_on_bad_packet = vm.count("continue") > 0; if (enable_size_map) std::cout << "Packet size tracking enabled - will only recv one packet at a time!" << std::endl; //create a usrp device std::cout << 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); //Lock mboard clocks usrp->set_clock_source(ref); //always select the subdevice first, the channel mapping affects the other settings if (vm.count("subdev")) 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; } 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 the center frequency if (vm.count("freq")) { //with default of 0.0 this will always be true std::cout << boost::format("Setting RX Freq: %f MHz...") % (freq/1e6) << std::endl; uhd::tune_request_t tune_request(freq); if(vm.count("int-n")) tune_request.args = uhd::device_addr_t("mode_n=integer"); 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 if (vm.count("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 if (vm.count("bw")) { 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 if (vm.count("ant")) usrp->set_rx_antenna(ant); boost::this_thread::sleep(boost::posix_time::seconds(setup_time)); //allow for some setup time //check Ref and LO Lock detect if (not vm.count("skip-lo")){ check_locked_sensor(usrp->get_rx_sensor_names(0), "lo_locked", boost::bind(&uhd::usrp::multi_usrp::get_rx_sensor, usrp, _1, 0), setup_time); if (ref == "mimo") check_locked_sensor(usrp->get_mboard_sensor_names(0), "mimo_locked", boost::bind(&uhd::usrp::multi_usrp::get_mboard_sensor, usrp, _1, 0), setup_time); if (ref == "external") check_locked_sensor(usrp->get_mboard_sensor_names(0), "ref_locked", boost::bind(&uhd::usrp::multi_usrp::get_mboard_sensor, usrp, _1, 0), setup_time); } if (total_num_samps == 0){ std::signal(SIGINT, &sig_int_handler); std::cout << "Press Ctrl + C to stop streaming..." << std::endl; } #define recv_to_file_args(format) \ (usrp, format, wirefmt, channel, file, spb, total_num_samps, total_time, bw_summary, stats, null, enable_size_map, continue_on_bad_packet) //recv to file if (type == "double") recv_to_file<std::complex<double> >recv_to_file_args("fc64"); else if (type == "float") recv_to_file<std::complex<float> >recv_to_file_args("fc32"); else if (type == "short") recv_to_file<std::complex<short> >recv_to_file_args("sc16"); else throw std::runtime_error("Unknown type " + type); //finished std::cout << std::endl << "Done!" << std::endl << std::endl; return EXIT_SUCCESS; }
_______________________________________________ USRP-users mailing list USRP-users@lists.ettus.com http://lists.ettus.com/mailman/listinfo/usrp-users_lists.ettus.com