mirror of
https://github.com/saymrwulf/uhd.git
synced 2026-05-15 21:01:26 +00:00
The example assumed that there was always at least one TX and on RX channel. Since that is not always true, this change checks for TX and RX channels and only exucutes tests for what exists on the device. Signed-off-by: Michael West <michael.west@ettus.com>
388 lines
13 KiB
C++
388 lines
13 KiB
C++
//
|
|
// Copyright 2010-2013 Ettus Research LLC
|
|
// Copyright 2018 Ettus Research, a National Instruments Company
|
|
// Copyright 2019 Ettus Research, a National Instruments Brand
|
|
//
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
//
|
|
|
|
#include <uhd/config.hpp>
|
|
#include <uhd/types/stream_cmd.hpp>
|
|
#include <uhd/usrp/multi_usrp.hpp>
|
|
#include <uhd/utils/safe_main.hpp>
|
|
#include <uhd/utils/static.hpp>
|
|
#include <uhd/utils/thread.hpp>
|
|
#include <boost/assign/list_of.hpp>
|
|
#include <boost/format.hpp>
|
|
#include <boost/program_options.hpp>
|
|
#include <complex>
|
|
#include <cstdlib>
|
|
#include <ctime>
|
|
#include <functional>
|
|
#include <iostream>
|
|
|
|
namespace po = boost::program_options;
|
|
|
|
/*!
|
|
* Test the late command message:
|
|
* Issue a stream command with a time that is in the past.
|
|
* We expect to get an inline late command message.
|
|
*/
|
|
bool test_late_command_message(uhd::usrp::multi_usrp::sptr usrp,
|
|
uhd::rx_streamer::sptr rx_stream,
|
|
uhd::tx_streamer::sptr)
|
|
{
|
|
std::cout << "Test late command message... " << std::flush;
|
|
|
|
usrp->set_time_now(uhd::time_spec_t(200.0)); // set time
|
|
|
|
uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE);
|
|
stream_cmd.num_samps = rx_stream->get_max_num_samps();
|
|
stream_cmd.stream_now = false;
|
|
stream_cmd.time_spec = uhd::time_spec_t(100.0); // time in the past
|
|
rx_stream->issue_stream_cmd(stream_cmd);
|
|
|
|
std::vector<std::complex<float>> buff(rx_stream->get_max_num_samps());
|
|
uhd::rx_metadata_t md;
|
|
|
|
const size_t nsamps = rx_stream->recv(&buff.front(), buff.size(), md);
|
|
|
|
switch (md.error_code) {
|
|
case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
|
|
std::cout << "success:\n"
|
|
<< " Got error code late command message.\n"
|
|
<< std::endl;
|
|
return true;
|
|
|
|
case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
|
|
std::cout << "failed:\n"
|
|
<< " Inline message recv timed out.\n"
|
|
<< std::endl;
|
|
return false;
|
|
|
|
default:
|
|
std::cout << boost::format(
|
|
"failed:\n"
|
|
" Got unexpected error code 0x%x (%s), nsamps %u.\n")
|
|
% md.error_code % md.strerror() % nsamps
|
|
<< std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Test the broken chain message:
|
|
* Issue a stream command with num samps and more.
|
|
* We expect to get an inline broken chain message.
|
|
*/
|
|
bool test_broken_chain_message(UHD_UNUSED(uhd::usrp::multi_usrp::sptr usrp),
|
|
uhd::rx_streamer::sptr rx_stream,
|
|
uhd::tx_streamer::sptr)
|
|
{
|
|
std::cout << "Test broken chain message... " << std::flush;
|
|
|
|
uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE);
|
|
stream_cmd.stream_now = true;
|
|
stream_cmd.num_samps = rx_stream->get_max_num_samps();
|
|
rx_stream->issue_stream_cmd(stream_cmd);
|
|
|
|
std::vector<std::complex<float>> buff(rx_stream->get_max_num_samps());
|
|
uhd::rx_metadata_t md;
|
|
|
|
rx_stream->recv( // once for the requested samples
|
|
&buff.front(),
|
|
buff.size(),
|
|
md);
|
|
|
|
rx_stream->recv( // again for the inline message
|
|
&buff.front(),
|
|
buff.size(),
|
|
md);
|
|
|
|
switch (md.error_code) {
|
|
case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
|
|
std::cout << boost::format("success:\n"
|
|
" Got error code broken chain message.\n")
|
|
<< std::endl;
|
|
return true;
|
|
|
|
case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
|
|
std::cout << boost::format("failed:\n"
|
|
" Inline message recv timed out.\n")
|
|
<< std::endl;
|
|
return false;
|
|
|
|
default:
|
|
std::cout << boost::format("failed:\n"
|
|
" Got unexpected error code 0x%x (%s).\n")
|
|
% md.error_code % md.strerror()
|
|
<< std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Test the burst ack message:
|
|
* Send a burst of many samples that will fragment internally.
|
|
* We expect to get an burst ack async message.
|
|
*/
|
|
bool test_burst_ack_message(
|
|
uhd::usrp::multi_usrp::sptr, uhd::rx_streamer::sptr, uhd::tx_streamer::sptr tx_stream)
|
|
{
|
|
std::cout << "Test burst ack message... " << std::flush;
|
|
|
|
uhd::tx_metadata_t md;
|
|
md.start_of_burst = true;
|
|
md.end_of_burst = true;
|
|
md.has_time_spec = false;
|
|
|
|
std::vector<std::complex<float>> buff(tx_stream->get_max_num_samps());
|
|
tx_stream->send(&buff.front(), buff.size(), md);
|
|
|
|
uhd::async_metadata_t async_md;
|
|
if (not tx_stream->recv_async_msg(async_md)) {
|
|
std::cout << "failed:\n"
|
|
<< " Async message recv timed out.\n"
|
|
<< std::endl;
|
|
return false;
|
|
}
|
|
|
|
switch (async_md.event_code) {
|
|
case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
|
|
std::cout << "success:\n"
|
|
<< " Got event code burst ack message.\n"
|
|
<< std::endl;
|
|
return true;
|
|
|
|
default:
|
|
std::cout << boost::format("failed:\n"
|
|
" Got unexpected event code 0x%x.\n")
|
|
% async_md.event_code
|
|
<< std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Test the underflow message:
|
|
* Send a start of burst packet with no following end of burst.
|
|
* We expect to get an underflow(within a burst) async message.
|
|
*/
|
|
bool test_underflow_message(
|
|
uhd::usrp::multi_usrp::sptr, uhd::rx_streamer::sptr, uhd::tx_streamer::sptr tx_stream)
|
|
{
|
|
std::cout << "Test underflow message... " << std::flush;
|
|
|
|
uhd::tx_metadata_t md;
|
|
md.start_of_burst = true;
|
|
md.end_of_burst = false;
|
|
md.has_time_spec = false;
|
|
|
|
std::vector<std::complex<float>> buff(tx_stream->get_max_num_samps());
|
|
tx_stream->send(&buff.front(), buff.size(), md);
|
|
|
|
uhd::async_metadata_t async_md;
|
|
bool result = false;
|
|
if (not tx_stream->recv_async_msg(async_md, 1)) {
|
|
std::cout << boost::format("failed:\n"
|
|
" Async message recv timed out.\n")
|
|
<< std::endl;
|
|
} else {
|
|
switch (async_md.event_code) {
|
|
case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
|
|
std::cout << boost::format("success:\n"
|
|
" Got event code underflow message.\n")
|
|
<< std::endl;
|
|
result = true;
|
|
break;
|
|
|
|
default:
|
|
std::cout << boost::format("failed:\n"
|
|
" Got unexpected event code 0x%x.\n")
|
|
% async_md.event_code
|
|
<< std::endl;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Finish the burst to avoid affecting other tests. The DUC only considers
|
|
// time specs from the beginning of a burst, if other tests need a time
|
|
// spec they would be affected by an unfinished burst.
|
|
md.end_of_burst = true;
|
|
tx_stream->send(&buff.front(), buff.size(), md);
|
|
return result;
|
|
}
|
|
|
|
/*!
|
|
* Test the time error message:
|
|
* Send a burst packet that occurs at a time in the past.
|
|
* We expect to get a time error async message.
|
|
*/
|
|
bool test_time_error_message(uhd::usrp::multi_usrp::sptr usrp,
|
|
uhd::rx_streamer::sptr,
|
|
uhd::tx_streamer::sptr tx_stream)
|
|
{
|
|
std::cout << "Test time error message... " << std::flush;
|
|
|
|
uhd::tx_metadata_t md;
|
|
md.start_of_burst = true;
|
|
md.end_of_burst = true;
|
|
md.has_time_spec = true;
|
|
md.time_spec = uhd::time_spec_t(100.0); // send at 100s
|
|
|
|
usrp->set_time_now(uhd::time_spec_t(200.0)); // time at 200s
|
|
|
|
std::vector<std::complex<float>> buff(tx_stream->get_max_num_samps());
|
|
tx_stream->send(&buff.front(), buff.size(), md);
|
|
|
|
uhd::async_metadata_t async_md;
|
|
if (not tx_stream->recv_async_msg(async_md)) {
|
|
std::cout << boost::format("failed:\n"
|
|
" Async message recv timed out.\n")
|
|
<< std::endl;
|
|
return false;
|
|
}
|
|
|
|
switch (async_md.event_code) {
|
|
case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
|
|
std::cout << boost::format("success:\n"
|
|
" Got event code time error message.\n")
|
|
<< std::endl;
|
|
return true;
|
|
|
|
default:
|
|
std::cout << boost::format("failed:\n"
|
|
" Got unexpected event code 0x%x.\n")
|
|
% async_md.event_code
|
|
<< std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void flush_async(uhd::tx_streamer::sptr tx_stream)
|
|
{
|
|
uhd::async_metadata_t async_md;
|
|
while (tx_stream->recv_async_msg(async_md)) {
|
|
}
|
|
}
|
|
|
|
void flush_recv(uhd::rx_streamer::sptr rx_stream)
|
|
{
|
|
uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE);
|
|
stream_cmd.num_samps = rx_stream->get_max_num_samps() * 3;
|
|
stream_cmd.stream_now = true;
|
|
rx_stream->issue_stream_cmd(stream_cmd);
|
|
|
|
std::vector<std::complex<float>> buff(stream_cmd.num_samps);
|
|
uhd::rx_metadata_t md;
|
|
|
|
do {
|
|
rx_stream->recv(&buff.front(), buff.size(), md);
|
|
} while (md.error_code != uhd::rx_metadata_t::ERROR_CODE_TIMEOUT);
|
|
}
|
|
|
|
int UHD_SAFE_MAIN(int argc, char* argv[])
|
|
{
|
|
// variables to be set by po
|
|
std::string args;
|
|
size_t ntests;
|
|
|
|
// setup the program options
|
|
po::options_description desc("Allowed options");
|
|
// clang-format off
|
|
desc.add_options()
|
|
("help", "help message")
|
|
("args", po::value<std::string>(&args)->default_value(""), "multi uhd device address args")
|
|
("ntests", po::value<size_t>(&ntests)->default_value(50), "number of tests to run")
|
|
("test-chain", "Run broken chain tests")
|
|
;
|
|
// clang-format on
|
|
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 Test Messages %s") % desc << std::endl;
|
|
return ~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);
|
|
std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl;
|
|
|
|
// create RX and TX streamers
|
|
uhd::stream_args_t stream_args("fc32"); // complex floats
|
|
uhd::rx_streamer::sptr rx_stream;
|
|
uhd::tx_streamer::sptr tx_stream;
|
|
if (usrp->get_rx_num_channels()) {
|
|
rx_stream = usrp->get_rx_stream(stream_args);
|
|
}
|
|
if (usrp->get_tx_num_channels()) {
|
|
tx_stream = usrp->get_tx_stream(stream_args);
|
|
}
|
|
|
|
//------------------------------------------------------------------
|
|
// begin messages test
|
|
//------------------------------------------------------------------
|
|
using test_executor_fn = std::function<bool(
|
|
uhd::usrp::multi_usrp::sptr, uhd::rx_streamer::sptr, uhd::tx_streamer::sptr)>;
|
|
uhd::dict<std::string, test_executor_fn> tests;
|
|
if (tx_stream) {
|
|
tests["Test Burst ACK "] = &test_burst_ack_message;
|
|
tests["Test Underflow "] = &test_underflow_message;
|
|
tests["Test Time Error"] = &test_time_error_message;
|
|
}
|
|
if (rx_stream) {
|
|
tests["Test Late Command"] = &test_late_command_message;
|
|
}
|
|
|
|
if (vm.count("test-chain")) {
|
|
tests["Test Broken Chain"] = &test_broken_chain_message;
|
|
}
|
|
|
|
// init result counts
|
|
uhd::dict<std::string, size_t> failures, successes;
|
|
for (const std::string& key : tests.keys()) {
|
|
failures[key] = 0;
|
|
successes[key] = 0;
|
|
}
|
|
|
|
// run the tests, pick at random
|
|
std::srand((unsigned int)time(NULL));
|
|
for (size_t n = 0; n < ntests; n++) {
|
|
std::string key = tests.keys()[std::rand() % tests.size()];
|
|
bool pass = tests[key](usrp, rx_stream, tx_stream);
|
|
|
|
if (rx_stream) {
|
|
flush_recv(rx_stream);
|
|
}
|
|
if (tx_stream) {
|
|
flush_async(tx_stream);
|
|
}
|
|
|
|
// store result
|
|
if (pass)
|
|
successes[key]++;
|
|
else
|
|
failures[key]++;
|
|
}
|
|
|
|
// print the result summary
|
|
bool any_failure = false;
|
|
std::cout << std::endl << "Summary:" << std::endl << std::endl;
|
|
for (const std::string& key : tests.keys()) {
|
|
std::cout << boost::format("%s -> %3u successes, %3u failures") % key
|
|
% successes[key] % failures[key]
|
|
<< std::endl;
|
|
any_failure = any_failure or (failures[key] > 0);
|
|
}
|
|
|
|
// finished
|
|
std::cout << std::endl << "Done!" << std::endl << std::endl;
|
|
|
|
return any_failure ? EXIT_FAILURE : EXIT_SUCCESS;
|
|
}
|