While working to get coherent streams working, I ran into an issue using an x310 with two TwinRX daughterboards. The issue starts with a series of "ERROR_CODE_OVERFLOW (Out of sequence error)" errors. In an attempt to recover from that, the rx streamer is thrown out and recreated. The next stream errors change to "ERROR_CODE_TIMEOUT". Once in this state, all future streams end with this error. The x310 is connected over 10G ethernet.
I managed to reproduce this error with an example program based off of "rx_multi_samples.cpp". I had to make the following changes: 1. STREAM_MODE_START_CONTINUOUS is now used, ending the stream with STREAM_MODE_STOP_CONTINUOUS 2. The stream delay was set to .01 (mostly to speed up the rate the error would occur) 3. Multi stream commands (and stop commands) are issued in repetition (start, stop, start, stop, etc.) rather than just one long stream 4. Each stream uses a different sampling rate (alternates between 25Msps and 50Msps) 5. A small loop was added to the collect loop to slow down the thread enough to get overflow errors (but only sometimes, nothing crazy) 6. Once the out of sequence error is encountered 10 times in a row, the rx streamer is destroyed and re-created 7. Every stream command after step 5 ends in a timeout error It is also worth pointing out that this does not happen if the sample rate does not change. The out of sequence errors will still happen until the rx streamer is re-created, but the timeout errors do not occur after that... I attached the entire example program (with modifications) to this email. I started it with the args: rx_multi_samples --args addr=192.168.30.2 --subdev "A:0 A:1 B:0 B:1" --channels "0,1,2,3" --dilv --rate 50000000 --nsamps 8000000 Is there something wrong with how we are using the interface? Is there steps we can take to either avoid or recover from this state? I appreciate any help we can get... Will
// // Copyright 2011 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/safe_main.hpp> #include <uhd/usrp/multi_usrp.hpp> #include <boost/program_options.hpp> #include <boost/format.hpp> #include <boost/thread.hpp> #include <boost/lexical_cast.hpp> #include <boost/algorithm/string.hpp> #include <iostream> #include <complex> #include <thread> #include <chrono> #include <unistd.h> namespace po = boost::program_options; const size_t MAX_OVERFLOW_ERRORS = 5; // the maximum number of overflows at zero samples received before the capture errors out int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::set_thread_priority_safe(); //variables to be set by po std::string args, sync, subdev, channel_list; double seconds_in_future; size_t total_num_samps; double rate; //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") ("secs", po::value<double>(&seconds_in_future)->default_value(1.5), "number of seconds in the future to receive") ("nsamps", po::value<size_t>(&total_num_samps)->default_value(10000), "total number of samples to receive") ("rate", po::value<double>(&rate)->default_value(100e6/16), "rate of incoming samples") ("sync", po::value<std::string>(&sync)->default_value("now"), "synchronization method: now, pps, mimo") ("subdev", po::value<std::string>(&subdev), "subdev spec (homogeneous across motherboards)") ("dilv", "specify to disable inner-loop verbose") ("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")){ std::cout << boost::format("UHD RX Multi Samples %s") % desc << std::endl; std::cout << " This is a demonstration of how to receive aligned data from multiple channels.\n" " This example can receive from multiple DSPs, multiple motherboards, or both.\n" " The MIMO cable or PPS can be used to synchronize the configuration. See --sync\n" "\n" " Specify --subdev to select multiple channels per motherboard.\n" " Ex: --subdev=\"0:A 0:B\" to get 2 channels on a Basic RX.\n" "\n" " Specify --args to select multiple motherboards in a configuration.\n" " Ex: --args=\"addr0=192.168.10.2, addr1=192.168.10.3\"\n" << std::endl; return ~0; } bool verbose = vm.count("dilv") == 0; //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); //always select the subdevice first, the channel mapping affects the other settings if (vm.count("subdev")) usrp->set_rx_subdev_spec(subdev); //sets across all mboards std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; //set the rx sample rate (sets across all channels) 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; std::cout << boost::format("Setting device timestamp to 0...") << std::endl; //Set the time based on GPS time { std::cout << "Setting to GPS time" << std::endl; uhd::time_spec_t last = usrp->get_time_last_pps(); uhd::time_spec_t next = usrp->get_time_last_pps(); while(next == last) { std::this_thread::sleep_for(std::chrono::milliseconds(50)); last = next; next = usrp->get_time_last_pps(); } // sleep 200 ms to allow new time data from the GPSDO to propagate through the system std::this_thread::sleep_for(std::chrono::milliseconds(200)); // set time usrp->set_time_next_pps(uhd::time_spec_t(time_t(usrp->get_mboard_sensor("gps_time").to_int()+1))); // wait for PPS again last = usrp->get_time_last_pps(); next = usrp->get_time_last_pps(); while(next == last) { std::this_thread::sleep_for(std::chrono::milliseconds(50)); last = next; next = usrp->get_time_last_pps(); } // sleep 200 ms to allow new time data from the GPSDO to propagate through the system std::this_thread::sleep_for(std::chrono::milliseconds(200)); usrp->set_clock_source("gpsdo"); } //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_rx_num_channels()){ throw std::runtime_error("Invalid channel(s) specified."); } else channel_nums.push_back(boost::lexical_cast<int>(channel_strings[ch])); } //create a receive streamer //linearly map channels (index0 = channel0, index1 = channel1, ...) uhd::stream_args_t stream_args("sc16", ""); //16 bit complex integers stream_args.channels = channel_nums; uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); //allocate buffers to receive with samples (one buffer per channel) const size_t samps_per_buff = rx_stream->get_max_num_samps(); std::vector<std::vector<std::complex<int16_t> > > buffs( usrp->get_rx_num_channels(), std::vector<std::complex<int16_t> >(samps_per_buff) ); //create a vector of pointers to point to each of the channel buffers std::vector<std::complex<int16_t> *> buff_ptrs; for (size_t i = 0; i < buffs.size(); i++) buff_ptrs.push_back(&buffs[i].front()); //will switch each time between 50Msps and 25Msps uint64_t seqErrs = 0; bool rateFlag = false; while(true) { try{ //set the sampling rate to a new value if(rateFlag) rate = 50000000.0; else rate = 25000000.0; rateFlag = !rateFlag; usrp->set_rx_rate(rate); //wait for LO lock for(size_t channel : channel_nums) { const size_t MAX_LOCK_WAIT = 500; // number of 10 millisecond sleep periods to wait for LO lock size_t count = 0; for(; count < MAX_LOCK_WAIT; count++) { if(usrp->get_rx_sensor("lo_locked", channel).to_bool()) { break; } usleep(10000); // 10 milliseconds } } //meta-data will be filled in by recv() uhd::rx_metadata_t md; //the first call to recv() will block this many seconds before receiving double timeout = 0.5; //timeout (delay before receive + padding) size_t num_acc_samps = 0; //number of accumulated samples //clear out any old packets left from previous captures while(true) { uhd::rx_metadata_t sampleMetadata; size_t samplesRecieved = rx_stream->recv(buff_ptrs, samps_per_buff, md, 0, true); if(samplesRecieved == 0) { break; } } //Stream continuously until told to stop... uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); uhd::stream_cmd_t stop_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); stream_cmd.stream_now = false; //delay the command by 10ms uhd::time_spec_t stream_delay = 0.01; //required to support coherent collects for multi-channel USRPs stream_cmd.time_spec = usrp->get_time_now(); stream_cmd.time_spec += stream_delay.get_real_secs(); rx_stream->issue_stream_cmd(stream_cmd); //tells all channels to stream size_t overflowErrors = 0; while(num_acc_samps < total_num_samps){ //receive a single packet (or slightly less depending on how much is needed) size_t samps_to_recv = std::min(total_num_samps-num_acc_samps, samps_per_buff); size_t num_rx_samps = rx_stream->recv( buff_ptrs, samps_per_buff, md, timeout ); //use a small timeout for subsequent packets timeout = 0.1; //handle the error code // ignore overflow if no samples have been received yet if(md.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW && num_acc_samps == 0 && overflowErrors < MAX_OVERFLOW_ERRORS) { overflowErrors++; continue; } if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) break; if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ std::cerr << "got error!" << md.strerror() << std::endl; break; } if(verbose) std::cout << boost::format( "Received packet: %u samples, %u full secs, %f frac secs" ) % num_rx_samps % md.time_spec.get_full_secs() % md.time_spec.get_frac_secs() << std::endl; num_acc_samps += num_rx_samps; //some garbage code to slow this thread down just enough to cause overflows for(auto& buf : buffs) { for(auto& num : buf) num.real(num.real() - 1); } } if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT && num_acc_samps < total_num_samps) std::cerr << "Receive timeout before all samples received..." << std::endl; rx_stream->issue_stream_cmd(stop_cmd); //tells all channels to stop //If there was an out of sequence error AND we have seen a few in a row, reset and re-create the rx streamer... if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ if(md.out_of_sequence) { seqErrs++; if(seqErrs > 10) { seqErrs = 0; std::cout << "Reseting RX stream!" << std::endl; rx_stream.reset(); rx_stream = usrp->get_rx_stream(stream_args); } } } } catch (...) { std::cout << "Caught exception" << 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