diff options
Diffstat (limited to 'tools/kitchen_sink/kitchen_sink.cpp')
-rw-r--r-- | tools/kitchen_sink/kitchen_sink.cpp | 213 |
1 files changed, 183 insertions, 30 deletions
diff --git a/tools/kitchen_sink/kitchen_sink.cpp b/tools/kitchen_sink/kitchen_sink.cpp index c7265bea4..c73eafbe4 100644 --- a/tools/kitchen_sink/kitchen_sink.cpp +++ b/tools/kitchen_sink/kitchen_sink.cpp @@ -278,12 +278,16 @@ typedef struct RxParams { bool single_packets; bool size_map; size_t rx_sample_limit; - std::ofstream* capture_file; + std::vector<std::ofstream*> capture_files; bool set_rx_freq; double rx_freq; double rx_freq_delay; double rx_lo_offset; bool interleave_rx_file_samples; + bool ignore_late_start; + bool ignore_bad_packets; + bool ignore_timeout; + bool ignore_unexpected_error; } RX_PARAMS; static uint64_t recv_samp_count_progress = 0; @@ -465,25 +469,37 @@ void benchmark_rx_rate( recv_done.notify_one(); } - if (params.capture_file != NULL) + if (params.capture_files.empty() == false) { - if (params.interleave_rx_file_samples) + size_t channel_count = rx_stream->get_num_channels(); + + if ((channel_count == 1) || ((channel_count > 1) && (params.capture_files.size() == 1))) { - for (size_t i = 0; i < recv_samps; ++i) + if (params.interleave_rx_file_samples) + { + for (size_t i = 0; i < recv_samps; ++i) + { + for (size_t j = 0; j < channel_count; ++j) + { + params.capture_files[0]->write((const char*)buffs[j] + (bytes_per_samp * i), bytes_per_samp); + } + } + } + else { - size_t channel_count = rx_stream->get_num_channels(); - for (size_t j = 0; j < channel_count; ++j) + for (size_t i = 0; i < channel_count; ++i) { - params.capture_file->write((const char*)buffs[j] + (bytes_per_samp * i), bytes_per_samp); + size_t num_bytes = recv_samps * bytes_per_samp; + params.capture_files[0]->write((const char*)buffs[i], num_bytes); } } } else { - for (size_t i = 0; i < rx_stream->get_num_channels(); ++i) + for (size_t n = 0; n < channel_count; ++n) { size_t num_bytes = recv_samps * bytes_per_samp; - params.capture_file->write((const char*)buffs[i], num_bytes); + params.capture_files[n]->write((const char*)buffs[n], num_bytes); } } } @@ -498,13 +514,17 @@ void benchmark_rx_rate( //} //handle the error codes - switch(md.error_code) { + switch(md.error_code) + { case uhd::rx_metadata_t::ERROR_CODE_NONE: - if (had_an_overflow) { + { + if (had_an_overflow) + { had_an_overflow = false; num_dropped_samps += (md.time_spec - last_time).to_ticks(rate); // FIXME: Check this as 'num_dropped_samps' has come out -ve } break; + } // ERROR_CODE_OVERFLOW can indicate overflow or sequence error case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW: // 'recv_samps' should be 0 @@ -523,13 +543,43 @@ void benchmark_rx_rate( ss << HEADER_RX"(" << get_stringified_time() << ") "; ss << boost::format("Timeout") << std::endl; std::cout << ss.str(); + if (params.ignore_timeout == false) + sig_int_handler(-1); + break; + } + + case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND: + { + std::stringstream ss; + ss << HEADER_RX"(" << get_stringified_time() << ") "; + ss << boost::format("Late command") << std::endl; + std::cout << ss.str(); + if (params.ignore_late_start == false) + sig_int_handler(-1); + break; + } + + case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET: + { + std::stringstream ss; + ss << HEADER_RX"(" << get_stringified_time() << ") "; + ss << boost::format("Bad packet") << std::endl; + std::cout << ss.str(); + if (params.ignore_bad_packets == false) + sig_int_handler(-1); break; } default: - std::cerr << HEADER_RX"Error code: " << md.error_code << std::endl; - std::cerr << HEADER_RX"Unexpected error on recv, continuing..." << std::endl; + { + std::stringstream ss; + ss << HEADER_RX"(" << get_stringified_time() << ") "; + ss << (boost::format("Unexpected error (code: %d)") % md.error_code) << std::endl; + std::cout << ss.str(); + if (params.ignore_unexpected_error == false) + sig_int_handler(-1); break; + } } print_msgs(); @@ -550,11 +600,14 @@ void benchmark_rx_rate( rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); } - if (params.capture_file != NULL) + if (params.capture_files.empty() == false) { - std::cout << HEADER_RX"Closing capture file..." << std::endl; - delete params.capture_file; - params.capture_file = NULL; + std::cout << HEADER_RX"Closing capture files..." << std::endl; + + for (size_t n = 0; n < params.capture_files.size(); ++n) + delete params.capture_files[n]; + + params.capture_files.clear(); } l.lock(); @@ -628,7 +681,7 @@ void benchmark_tx_rate( size_t total_packet_count = (total_length / max_samps_per_packet) + ((total_length % max_samps_per_packet) ? 1 : 0); if ((params.use_tx_eob) && (params.tx_time_between_bursts > 0)) packet_time += uhd::time_spec_t(params.tx_time_between_bursts); - size_t max_late_count = (size_t)(rate / (double)packet_time.to_ticks(rate)) * total_packet_count; + size_t max_late_count = (size_t)(rate / (double)packet_time.to_ticks(rate)) * total_packet_count * tx_stream->get_num_channels(); // Also need to take into account number of radios // Will be much higher L values (e.g. 31K) on e.g. B200 when entire TX pipeline is full of late packets (large size due to total TX buffering throughout transport & DSP) @@ -1104,6 +1157,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::string time_source, clock_source; std::string tx_ant, rx_ant; std::string tx_subdev, rx_subdev; + std::string set_time_mode; //setup the program options po::options_description desc("Allowed options"); @@ -1139,7 +1193,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("progress-interval", po::value<double>(&progress_interval)->default_value(progress_interval), "seconds between bandwidth updates (0 disables)") ("rx-progress-interval", po::value<double>(&rx_progress_interval), "seconds between RX bandwidth updates (0 disables)") ("tx-progress-interval", po::value<double>(&tx_progress_interval), "seconds between TX bandwidth updates (0 disables)") - ("tx-offset", po::value<double>(&tx_time_offset), "seconds that TX should be in front of RX when following") + ("tx-offset", po::value<double>(&tx_time_offset)->default_value(0.0), "seconds that TX should be in front of RX when following") ("tx-length", po::value<size_t>(&tx_burst_length)->default_value(0), "TX burst length in samples (0: maximum packet size)") ("tx-flush", po::value<size_t>(&tx_flush_length)->default_value(0), "samples to flush TX with after burst") ("tx-burst-separation", po::value<double>(&tx_time_between_bursts), "seconds between TX bursts") @@ -1161,6 +1215,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("rx-sleep-delay", po::value<size_t>(&rx_sleep_delay)->default_value(1000), "RX sleep delay (us)") ("rx-sample-limit", po::value<size_t>(&rx_sample_limit)->default_value(0), "total number of samples to receive (0 implies continuous streaming)") ("rx-file", po::value<std::string>(&rx_file)->default_value(""), "RX capture file path") + ("set-time", po::value<std::string>(&set_time_mode)->default_value(""), "set mode (now, next_pps, unknown_pps)") //("allow-late", "allow late bursts") ("drop-late", "drop late bursts") ("still-set-rates", "still set rate on unused direction") @@ -1177,6 +1232,10 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("recover-late", "recover from excessive late TX packets") ("disable-async", "disable the async message thread") ("interleave-rx-file-samples", "interleave individual samples (default is interleaving buffers)") + ("ignore-late-start", "continue receiving even if stream command was late") + ("ignore-bad-packets", "continue receiving after a bad packet") + ("ignore-timeout", "continue receiving after timeout") + ("ignore-unexpected", "continue receiving after unexpected error") // record TX/RX times // Optional interruption // simulate u / o at random / pulses @@ -1207,7 +1266,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //print the help message if (vm.count("help") or ((rx_rate + tx_rate) == 0)){ - std::cout << boost::format("UHD Benchmark Rate %s") % desc << std::endl; + std::cout << boost::format("UHD Kitchen Sink %s") % desc << std::endl; std::cout << " By default, performs single-channel full-duplex test at 1 Msps with continuous streaming.\n" " Specify --channels to set RX & TX,\n" @@ -1234,6 +1293,10 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ bool recover_late = (vm.count("recover-late") > 0); bool enable_async = (vm.count("disable-async") == 0); bool interleave_rx_file_samples = (vm.count("interleave-rx-file-samples") > 0); + bool ignore_late_start = (vm.count("ignore-late-start") > 0); + bool ignore_bad_packets = (vm.count("ignore-bad-packets") > 0); + bool ignore_timeout = (vm.count("ignore-timeout") > 0); + bool ignore_unexpected_error = (vm.count("ignore-unexpected") > 0); boost::posix_time::time_duration interrupt_timeout_duration(boost::posix_time::seconds(long(interrupt_timeout)) + boost::posix_time::microseconds(long((interrupt_timeout - floor(interrupt_timeout))*1e6))); @@ -1273,6 +1336,36 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ return ~0; } + bool rx_filename_has_format = false; + if (rx_channel_nums.size() > 0) + { + std::string str0; + try + { + str0 = boost::str(boost::format(rx_file) % 0); + rx_filename_has_format = true; + } + catch (...) + { + } + + bool format_different = false; + try + { + std::string str1(boost::str(boost::format(rx_file) % 1)); + format_different = (str0 != str1); + } + catch (...) + { + } + + if ((rx_filename_has_format) && (format_different == false)) + { + std::cout << HEADER_ERROR "Multi-channel RX capture filename format did not produce unique names" << std::endl; + return ~0; + } + } + if ((tx_rx_sync) || (tx_follows_rx)) { if (tx_channel_nums.size() == 0) @@ -1305,16 +1398,40 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ } else { + if (clock_source.empty() == false) // Set clock first (stable clock for PPS registration) + { + usrp->set_clock_source(clock_source); + std::cout << boost::format(HEADER "Clock source set to: %s") % clock_source << std::endl; + } + if (time_source.empty() == false) { usrp->set_time_source(time_source); std::cout << boost::format(HEADER "Time source set to: %s") % time_source << std::endl; } - if (clock_source.empty() == false) + if (set_time_mode.empty() == false) { - usrp->set_clock_source(clock_source); - std::cout << boost::format(HEADER "Clock source set to: %s") % clock_source << std::endl; + if (set_time_mode == "now") + { + usrp->set_time_now(uhd::time_spec_t(0.0)); + std::cout << boost::format(HEADER "Time set now") << std::endl; + } + else if (set_time_mode == "next_pps") + { + usrp->set_time_next_pps(uhd::time_spec_t(0.0)); + sleep(1); + std::cout << boost::format(HEADER "Time set next PPS") << std::endl; + } + else if (set_time_mode == "unknown_pps") + { + usrp->set_time_unknown_pps(uhd::time_spec_t(0.0)); + std::cout << boost::format(HEADER "Time set unknown PPS") << std::endl; + } + else + { + std::cout << HEADER_WARN"Cannot set time with unknown mode: " << set_time_mode << std::endl; + } } } @@ -1462,11 +1579,33 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ } } - rx_params.capture_file = NULL; if (rx_file.empty() == false) { - std::cout << boost::format(HEADER_RX"Capturing to \"%s\"") % rx_file << std::endl; - rx_params.capture_file = new std::ofstream(rx_file.c_str(), std::ios::out); + if (rx_filename_has_format == false) + { + if (rx_stream->get_num_channels() == 1) + { + std::cout << boost::format(HEADER_RX"Capturing single channel to \"%s\"") % rx_file << std::endl; + } + else + { + if (interleave_rx_file_samples) + std::cout << boost::format(HEADER_RX"Capturing all %d channels as interleaved samples to \"%s\"") % rx_stream->get_num_channels() % rx_file << std::endl; + else + std::cout << boost::format(HEADER_RX"Capturing all %d channels as interleaved buffers to \"%s\"") % rx_stream->get_num_channels() % rx_file << std::endl; + } + + rx_params.capture_files.push_back(new std::ofstream(rx_file.c_str(), std::ios::out)); + } + else + { + for (size_t n = 0; n < rx_stream->get_num_channels(); ++n) + { + std::cout << boost::format(HEADER_RX"Capturing channel %d to \"%s\"") % n % (boost::str(boost::format(rx_file) % n)) << std::endl; + std::string rx_file_name(boost::str(boost::format(rx_file) % n)); + rx_params.capture_files.push_back(new std::ofstream(rx_file_name.c_str(), std::ios::out)); + } + } } std::cout << boost::format( @@ -1488,6 +1627,10 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ rx_params.rx_freq_delay = rx_freq_delay; rx_params.rx_lo_offset = rx_lo_offset; rx_params.interleave_rx_file_samples = interleave_rx_file_samples; + rx_params.ignore_late_start = ignore_late_start; + rx_params.ignore_bad_packets = ignore_bad_packets; + rx_params.ignore_timeout = ignore_timeout; + rx_params.ignore_unexpected_error = ignore_unexpected_error; thread_group.create_thread(boost::bind( &benchmark_rx_rate, @@ -1500,7 +1643,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ if (tx_channel_nums.size() > 0) { //create a transmit streamer size_t bytes_per_tx_sample = uhd::convert::get_bytes_per_item(tx_cpu); - std::cout << boost::format(HEADER_TX"CPU bytes per TX sample: %d for '%s'") % bytes_per_tx_sample % rx_cpu << std::endl; + std::cout << boost::format(HEADER_TX"CPU bytes per TX sample: %d for '%s'") % bytes_per_tx_sample % tx_cpu << std::endl; size_t wire_bytes_per_tx_sample = uhd::convert::get_bytes_per_item(tx_otw); std::cout << boost::format(HEADER_TX"OTW bytes per TX sample: %d for '%s'") % wire_bytes_per_tx_sample % tx_otw << std::endl; @@ -1554,6 +1697,14 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ } } + if (recover_late) + { + if (tx_time_offset <= 0) + { + std::cout << HEADER_WARN"TX late recovery will not work with no TX time offset" << std::endl; + } + } + if (use_tx_eob) std::cout << HEADER_TX"Will use EOB" << std::endl; else @@ -1635,11 +1786,11 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ else std::cout << HEADER "Waiting for Q..." << std::endl; - while (stop_signal_called == false) + do { // FIXME: Stop time - if (kbhit(interactive_sleep)) + if (kbhit(0)) { char c = fgetc(stdin); if (c == EOF) @@ -1669,7 +1820,9 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ } print_msgs(); - } + + abort_event.timed_wait(l_stop, boost::posix_time::milliseconds(interactive_sleep)); + } while (stop_signal_called == false); } else if (duration > 0) { |