2018-07-16 12:39:30 +00:00
|
|
|
//
|
|
|
|
|
// Copyright 2018 Ettus Research, A National Instruments Company
|
2019-11-18 09:09:12 +00:00
|
|
|
// Copyright 2019 Ettus Research, A National Instruments Brand
|
2018-07-16 12:39:30 +00:00
|
|
|
//
|
|
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// Description:
|
|
|
|
|
//
|
|
|
|
|
// This example demonstrates using the Replay block to replay data from a file.
|
|
|
|
|
// It streams the file data to the Replay block, where it is recorded, then it
|
|
|
|
|
// is played back to the radio.
|
|
|
|
|
|
|
|
|
|
#include <uhd/device3.hpp>
|
|
|
|
|
#include <uhd/rfnoc/radio_ctrl.hpp>
|
|
|
|
|
#include <uhd/rfnoc/replay_block_ctrl.hpp>
|
2019-01-14 18:35:25 +00:00
|
|
|
#include <uhd/utils/safe_main.hpp>
|
2018-07-16 12:39:30 +00:00
|
|
|
#include <boost/format.hpp>
|
2019-01-14 18:35:25 +00:00
|
|
|
#include <boost/program_options.hpp>
|
2018-07-16 12:39:30 +00:00
|
|
|
#include <csignal>
|
2019-01-14 18:35:25 +00:00
|
|
|
#include <fstream>
|
2018-07-16 12:39:30 +00:00
|
|
|
#include <thread>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace po = boost::program_options;
|
|
|
|
|
|
|
|
|
|
using std::cout;
|
|
|
|
|
using std::endl;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
static volatile bool stop_signal_called = false;
|
|
|
|
|
|
|
|
|
|
// Ctrl+C handler
|
|
|
|
|
void sig_int_handler(int)
|
|
|
|
|
{
|
|
|
|
|
stop_signal_called = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-01-14 18:35:25 +00:00
|
|
|
int UHD_SAFE_MAIN(int argc, char* argv[])
|
2018-07-16 12:39:30 +00:00
|
|
|
{
|
|
|
|
|
// We use sc16 in this example, but the replay block only uses 64-bit words
|
|
|
|
|
// and is not aware of the CPU or wire format.
|
|
|
|
|
std::string wire_format("sc16");
|
|
|
|
|
std::string cpu_format("sc16");
|
|
|
|
|
|
|
|
|
|
// Constants related to the Replay block
|
2019-01-14 18:35:25 +00:00
|
|
|
const size_t replay_word_size = 8; // Size of words used by replay block
|
|
|
|
|
const size_t bytes_per_sample = 4; // Complex signed 16-bit is 32 bits per sample
|
|
|
|
|
const size_t samples_per_word = 2; // Number of sc16 samples per word
|
|
|
|
|
const size_t replay_spp = 2000; // SC16 Samples per packet generated by Replay block
|
2018-07-16 12:39:30 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Handle command line options
|
|
|
|
|
|
|
|
|
|
std::string args, radio_args, file, ant, ref;
|
|
|
|
|
double rate, freq, gain, bw;
|
|
|
|
|
size_t radio_id, radio_chan, replay_id, replay_chan, nsamps;
|
|
|
|
|
|
|
|
|
|
po::options_description desc("Allowed Options");
|
2019-01-10 23:19:48 +00:00
|
|
|
// clang-format off
|
2018-07-16 12:39:30 +00:00
|
|
|
desc.add_options()
|
|
|
|
|
("help", "help message")
|
|
|
|
|
("args", po::value<std::string>(&args)->default_value(""), "multi uhd device address args")
|
|
|
|
|
("radio-id", po::value<size_t>(&radio_id)->default_value(0), "radio block to use (e.g., 0 or 1).")
|
|
|
|
|
("radio-chan", po::value<size_t>(&radio_chan)->default_value(0), "radio channel to use")
|
|
|
|
|
("radio-args", po::value<std::string>(&radio_args), "radio arguments")
|
|
|
|
|
("replay-id", po::value<size_t>(&replay_id)->default_value(0), "replay block to use (e.g., 0 or 1)")
|
|
|
|
|
("replay_chan", po::value<size_t>(&replay_chan)->default_value(0), "replay channel to use")
|
2019-09-19 21:37:58 +00:00
|
|
|
("nsamps", po::value<size_t>(&nsamps)->default_value(0), "number of samples to play (0 for infinite)")
|
2018-07-16 12:39:30 +00:00
|
|
|
("file", po::value<std::string>(&file)->default_value("usrp_samples.dat"), "name of the file to read binary samples from")
|
|
|
|
|
("freq", po::value<double>(&freq), "RF center frequency in Hz")
|
|
|
|
|
("rate", po::value<double>(&rate), "rate of radio block")
|
|
|
|
|
("gain", po::value<double>(&gain), "gain for the RF chain")
|
|
|
|
|
("ant", po::value<std::string>(&ant), "antenna selection")
|
|
|
|
|
("bw", po::value<double>(&bw), "analog front-end filter bandwidth in Hz")
|
|
|
|
|
("ref", po::value<std::string>(&ref)->default_value("internal"), "reference source (internal, external, mimo)")
|
|
|
|
|
;
|
2019-01-10 23:19:48 +00:00
|
|
|
// clang-format on
|
2018-07-16 12:39:30 +00:00
|
|
|
po::variables_map vm;
|
|
|
|
|
po::store(po::parse_command_line(argc, argv, desc), vm);
|
|
|
|
|
po::notify(vm);
|
|
|
|
|
|
|
|
|
|
// Print help message
|
|
|
|
|
if (vm.count("help")) {
|
|
|
|
|
cout << boost::format("UHD/RFNoC Replay samples from file %s") % desc << endl;
|
2019-01-14 18:35:25 +00:00
|
|
|
cout << "This application uses the Replay block to playback data from a file to "
|
|
|
|
|
"a radio"
|
|
|
|
|
<< endl
|
|
|
|
|
<< endl;
|
2018-07-16 12:39:30 +00:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Create USRP device and block controls
|
|
|
|
|
|
|
|
|
|
cout << "Creating the USRP device with: " << args << ". . .\n" << endl;
|
|
|
|
|
uhd::device3::sptr usrp = uhd::device3::make(args);
|
|
|
|
|
|
|
|
|
|
// Create handle for radio object
|
|
|
|
|
uhd::rfnoc::block_id_t radio_ctrl_id(0, "Radio", radio_id);
|
|
|
|
|
uhd::rfnoc::radio_ctrl::sptr radio_ctrl;
|
|
|
|
|
radio_ctrl = usrp->get_block_ctrl<uhd::rfnoc::radio_ctrl>(radio_ctrl_id);
|
|
|
|
|
std::cout << "Using radio " << radio_id << ", channel " << radio_chan << std::endl;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check if the replay block exists on this device
|
|
|
|
|
uhd::rfnoc::block_id_t replay_ctrl_id(0, "Replay", replay_id);
|
|
|
|
|
uhd::rfnoc::replay_block_ctrl::sptr replay_ctrl;
|
|
|
|
|
if (!usrp->has_block(replay_ctrl_id)) {
|
|
|
|
|
cout << "Unable to find block \"" << replay_ctrl_id << "\"" << endl;
|
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
replay_ctrl = usrp->get_block_ctrl<uhd::rfnoc::replay_block_ctrl>(replay_ctrl_id);
|
2019-01-14 18:35:25 +00:00
|
|
|
std::cout << "Using replay block " << replay_id << ", channel " << replay_chan
|
|
|
|
|
<< std::endl;
|
2018-07-16 12:39:30 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Configure radio
|
|
|
|
|
|
|
|
|
|
// Lock clocks
|
2019-11-22 23:53:02 +00:00
|
|
|
if (vm.count("ref")) {
|
|
|
|
|
radio_ctrl->set_clock_source(ref);
|
|
|
|
|
}
|
2020-03-02 23:25:13 +00:00
|
|
|
|
2018-07-16 12:39:30 +00:00
|
|
|
// Apply any radio arguments provided
|
|
|
|
|
radio_ctrl->set_args(radio_args);
|
|
|
|
|
|
|
|
|
|
// Set the center frequency
|
2019-01-14 18:35:25 +00:00
|
|
|
if (not vm.count("freq")) {
|
2018-07-16 12:39:30 +00:00
|
|
|
std::cerr << "Please specify the center frequency with --freq" << std::endl;
|
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
}
|
2019-01-14 18:35:25 +00:00
|
|
|
std::cout << boost::format("Setting TX Freq: %f MHz...") % (freq / 1e6) << std::endl;
|
2018-07-16 12:39:30 +00:00
|
|
|
radio_ctrl->set_tx_frequency(freq, radio_chan);
|
2019-01-14 18:35:25 +00:00
|
|
|
std::cout << boost::format("Actual TX Freq: %f MHz...")
|
|
|
|
|
% (radio_ctrl->get_tx_frequency(radio_chan) / 1e6)
|
|
|
|
|
<< std::endl
|
|
|
|
|
<< std::endl;
|
2018-07-16 12:39:30 +00:00
|
|
|
|
|
|
|
|
// Set the sample rate
|
|
|
|
|
if (vm.count("rate")) {
|
2019-01-14 18:35:25 +00:00
|
|
|
std::cout << boost::format("Setting TX Rate: %f Msps...") % (rate / 1e6)
|
|
|
|
|
<< std::endl;
|
2018-07-16 12:39:30 +00:00
|
|
|
radio_ctrl->set_rate(rate);
|
2019-01-14 18:35:25 +00:00
|
|
|
std::cout << boost::format("Actual TX Rate: %f Msps...")
|
|
|
|
|
% (radio_ctrl->get_rate() / 1e6)
|
|
|
|
|
<< std::endl
|
|
|
|
|
<< std::endl;
|
2018-07-16 12:39:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set the RF gain
|
2019-01-14 18:35:25 +00:00
|
|
|
if (vm.count("gain")) {
|
2018-07-16 12:39:30 +00:00
|
|
|
std::cout << boost::format("Setting TX Gain: %f dB...") % gain << std::endl;
|
|
|
|
|
radio_ctrl->set_tx_gain(gain, radio_chan);
|
2019-01-14 18:35:25 +00:00
|
|
|
std::cout << boost::format("Actual TX Gain: %f dB...")
|
|
|
|
|
% radio_ctrl->get_tx_gain(radio_chan)
|
|
|
|
|
<< std::endl
|
|
|
|
|
<< std::endl;
|
2018-07-16 12:39:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set the analog front-end filter bandwidth
|
2019-01-14 18:35:25 +00:00
|
|
|
if (vm.count("bw")) {
|
|
|
|
|
std::cout << boost::format("Setting TX Bandwidth: %f MHz...") % (bw / 1e6)
|
2018-07-16 12:39:30 +00:00
|
|
|
<< std::endl;
|
|
|
|
|
radio_ctrl->set_tx_bandwidth(bw, radio_chan);
|
|
|
|
|
std::cout << boost::format("Actual TX Bandwidth: %f MHz...")
|
2019-01-14 18:35:25 +00:00
|
|
|
% (radio_ctrl->get_tx_bandwidth(radio_chan) / 1e6)
|
|
|
|
|
<< std::endl
|
|
|
|
|
<< std::endl;
|
2018-07-16 12:39:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set the antenna
|
|
|
|
|
if (vm.count("ant")) {
|
|
|
|
|
radio_ctrl->set_tx_antenna(ant, radio_chan);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Allow for some setup time
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
|
|
|
|
|
|
|
|
|
|
2019-01-14 18:35:25 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
2018-07-16 12:39:30 +00:00
|
|
|
// Connect Replay block to radio
|
|
|
|
|
|
|
|
|
|
uhd::rfnoc::graph::sptr replay_graph = usrp->create_graph("rfnoc_replay");
|
|
|
|
|
usrp->clear();
|
2019-01-14 18:35:25 +00:00
|
|
|
std::cout << "Connecting " << replay_ctrl->get_block_id() << " ==> "
|
|
|
|
|
<< radio_ctrl->get_block_id() << std::endl;
|
|
|
|
|
replay_graph->connect(replay_ctrl->get_block_id(),
|
|
|
|
|
replay_chan,
|
|
|
|
|
radio_ctrl->get_block_id(),
|
|
|
|
|
radio_chan,
|
|
|
|
|
replay_spp);
|
2018-07-16 12:39:30 +00:00
|
|
|
|
|
|
|
|
// Inform replay block that it has an RX streamer connected to it
|
|
|
|
|
replay_ctrl->set_rx_streamer(true, replay_chan);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Setup streamer to Replay block
|
|
|
|
|
|
|
|
|
|
uhd::device_addr_t streamer_args;
|
|
|
|
|
uhd::stream_args_t stream_args(cpu_format, wire_format);
|
|
|
|
|
uhd::tx_streamer::sptr tx_stream;
|
|
|
|
|
uhd::tx_metadata_t tx_md;
|
|
|
|
|
|
2019-01-14 18:35:25 +00:00
|
|
|
streamer_args["block_id"] = replay_ctrl->get_block_id().to_string();
|
2018-07-16 12:39:30 +00:00
|
|
|
streamer_args["block_port"] = str(boost::format("%d") % replay_chan);
|
2019-01-14 18:35:25 +00:00
|
|
|
stream_args.args = streamer_args;
|
|
|
|
|
tx_stream = usrp->get_tx_stream(stream_args);
|
2018-07-16 12:39:30 +00:00
|
|
|
|
|
|
|
|
// Make sure that streamer SPP is a multiple of the Replay block word size
|
|
|
|
|
size_t tx_spp = tx_stream->get_max_num_samps();
|
2019-01-14 18:35:25 +00:00
|
|
|
if (tx_spp % samples_per_word != 0) {
|
2018-07-16 12:39:30 +00:00
|
|
|
// Round SPP down to a multiple of the word size
|
|
|
|
|
tx_spp = (tx_spp / samples_per_word) * samples_per_word;
|
|
|
|
|
tx_stream.reset();
|
|
|
|
|
streamer_args["spp"] = boost::lexical_cast<std::string>(tx_spp);
|
2019-01-14 18:35:25 +00:00
|
|
|
stream_args.args = streamer_args;
|
|
|
|
|
tx_stream = usrp->get_tx_stream(stream_args);
|
2018-07-16 12:39:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cout << "Using streamer args: " << stream_args.args.to_string() << endl;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Read the data to replay
|
|
|
|
|
|
|
|
|
|
// Open the file
|
|
|
|
|
std::ifstream infile(file.c_str(), std::ifstream::binary);
|
|
|
|
|
if (!infile.is_open()) {
|
|
|
|
|
std::cerr << "Could not open specified file" << std::endl;
|
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get the file size
|
|
|
|
|
infile.seekg(0, std::ios::end);
|
|
|
|
|
size_t file_size = infile.tellg();
|
|
|
|
|
infile.seekg(0, std::ios::beg);
|
|
|
|
|
|
|
|
|
|
// Calculate the number of 64-bit words and samples to replay
|
2019-01-14 18:35:25 +00:00
|
|
|
size_t words_to_replay = file_size / replay_word_size;
|
2018-07-16 12:39:30 +00:00
|
|
|
size_t samples_to_replay = words_to_replay * replay_word_size / bytes_per_sample;
|
|
|
|
|
|
|
|
|
|
// Create buffer
|
|
|
|
|
std::vector<char> tx_buffer(words_to_replay * replay_word_size);
|
|
|
|
|
char* tx_buf_ptr = &tx_buffer[0];
|
|
|
|
|
|
|
|
|
|
// Read file into buffer, rounded down to number of words
|
|
|
|
|
infile.read(tx_buf_ptr, words_to_replay * replay_word_size);
|
|
|
|
|
infile.close();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Configure replay block
|
|
|
|
|
|
|
|
|
|
// Configure a buffer in the on-board memory at address 0 that's equal in
|
|
|
|
|
// size to the file we want to play back (rounded down to a multiple of
|
|
|
|
|
// 64-bit words). Note that it is allowed to playback a different size or
|
|
|
|
|
// location from what was recorded.
|
|
|
|
|
replay_ctrl->config_record(0, words_to_replay * replay_word_size, replay_chan);
|
|
|
|
|
replay_ctrl->config_play(0, words_to_replay * replay_word_size, replay_chan);
|
|
|
|
|
|
|
|
|
|
// Set samples per packet for Replay block playback
|
|
|
|
|
replay_ctrl->set_words_per_packet(replay_spp / samples_per_word, replay_chan);
|
|
|
|
|
|
|
|
|
|
// Display replay configuration
|
2019-01-14 18:35:25 +00:00
|
|
|
cout << boost::format("Replay file size: %d bytes (%d qwords, %d samples)")
|
|
|
|
|
% (words_to_replay * replay_word_size) % words_to_replay
|
|
|
|
|
% samples_to_replay
|
2018-07-16 12:39:30 +00:00
|
|
|
<< endl;
|
|
|
|
|
|
2019-01-14 18:35:25 +00:00
|
|
|
cout << boost::format("Record base address: 0x%X")
|
|
|
|
|
% replay_ctrl->get_record_addr(replay_chan)
|
|
|
|
|
<< endl;
|
|
|
|
|
cout << boost::format("Record buffer size: %d bytes")
|
|
|
|
|
% replay_ctrl->get_record_size(replay_chan)
|
|
|
|
|
<< endl;
|
|
|
|
|
cout << boost::format("Record fullness: %d")
|
|
|
|
|
% replay_ctrl->get_record_fullness(replay_chan)
|
|
|
|
|
<< endl;
|
|
|
|
|
cout << boost::format("Play base address: 0x%X")
|
|
|
|
|
% replay_ctrl->get_play_addr(replay_chan)
|
|
|
|
|
<< endl;
|
|
|
|
|
cout << boost::format("Play buffer size: %d bytes")
|
|
|
|
|
% replay_ctrl->get_play_size(replay_chan)
|
|
|
|
|
<< endl;
|
2018-07-16 12:39:30 +00:00
|
|
|
|
|
|
|
|
// Restart record buffer repeatedly until no new data appears on the Replay
|
|
|
|
|
// block's input. This will flush any data that was buffered on the input.
|
|
|
|
|
uint32_t fullness;
|
|
|
|
|
cout << boost::format("Restarting record buffer...") << endl;
|
|
|
|
|
do {
|
|
|
|
|
std::chrono::system_clock::time_point start_time;
|
|
|
|
|
std::chrono::system_clock::duration time_diff;
|
|
|
|
|
|
|
|
|
|
replay_ctrl->record_restart(replay_chan);
|
|
|
|
|
|
|
|
|
|
// Make sure the record buffer doesn't start to fill again
|
|
|
|
|
start_time = std::chrono::system_clock::now();
|
|
|
|
|
do {
|
|
|
|
|
fullness = replay_ctrl->get_record_fullness(replay_chan);
|
2019-01-14 18:35:25 +00:00
|
|
|
if (fullness != 0)
|
|
|
|
|
break;
|
2018-07-16 12:39:30 +00:00
|
|
|
time_diff = std::chrono::system_clock::now() - start_time;
|
|
|
|
|
time_diff = std::chrono::duration_cast<std::chrono::milliseconds>(time_diff);
|
|
|
|
|
} while (time_diff.count() < 250);
|
|
|
|
|
} while (fullness);
|
2019-01-14 18:35:25 +00:00
|
|
|
|
2018-07-16 12:39:30 +00:00
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Send data to replay (record the data)
|
|
|
|
|
|
|
|
|
|
cout << "Sending data to be recorded..." << endl;
|
|
|
|
|
tx_md.start_of_burst = true;
|
|
|
|
|
tx_md.end_of_burst = true;
|
2019-01-14 18:35:25 +00:00
|
|
|
size_t num_tx_samps = tx_stream->send(tx_buf_ptr, samples_to_replay, tx_md);
|
2018-07-16 12:39:30 +00:00
|
|
|
|
|
|
|
|
if (num_tx_samps != samples_to_replay) {
|
2019-01-14 18:35:25 +00:00
|
|
|
cout << boost::format("ERROR: Unable to send %d samples") % samples_to_replay
|
|
|
|
|
<< endl;
|
2018-07-16 12:39:30 +00:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Wait for data to be stored in on-board memory
|
|
|
|
|
|
|
|
|
|
cout << "Waiting for recording to complete..." << endl;
|
2019-01-14 18:35:25 +00:00
|
|
|
while (replay_ctrl->get_record_fullness(replay_chan)
|
|
|
|
|
< words_to_replay * replay_word_size)
|
|
|
|
|
;
|
2018-07-16 12:39:30 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Start replay of data
|
|
|
|
|
|
|
|
|
|
uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
|
|
|
|
|
|
|
|
|
|
if (nsamps <= 0) {
|
|
|
|
|
// Replay the entire buffer over and over
|
|
|
|
|
stream_cmd.stream_mode = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
|
2019-01-14 18:35:25 +00:00
|
|
|
stream_cmd.num_samps = words_to_replay;
|
|
|
|
|
cout << boost::format("Issuing replay command for %d words in continuous mode...")
|
|
|
|
|
% stream_cmd.num_samps
|
|
|
|
|
<< endl;
|
|
|
|
|
} else {
|
2018-07-16 12:39:30 +00:00
|
|
|
// Replay nsamps, wrapping back to the start of the buffer if nsamps is
|
|
|
|
|
// larger than the buffer size.
|
|
|
|
|
stream_cmd.stream_mode = uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE;
|
2019-01-14 18:35:25 +00:00
|
|
|
stream_cmd.num_samps = nsamps / samples_per_word;
|
|
|
|
|
cout << boost::format("Issuing replay command for %d words...")
|
|
|
|
|
% stream_cmd.num_samps
|
|
|
|
|
<< endl;
|
2018-07-16 12:39:30 +00:00
|
|
|
}
|
|
|
|
|
stream_cmd.stream_now = true;
|
|
|
|
|
replay_ctrl->issue_stream_cmd(stream_cmd, replay_chan);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Wait until user says to stop
|
|
|
|
|
|
|
|
|
|
// Setup SIGINT handler (Ctrl+C)
|
|
|
|
|
std::signal(SIGINT, &sig_int_handler);
|
|
|
|
|
cout << "Replaying data (Press Ctrl+C to stop)..." << endl;
|
|
|
|
|
|
2019-01-14 18:35:25 +00:00
|
|
|
while (not stop_signal_called)
|
|
|
|
|
;
|
2018-07-16 12:39:30 +00:00
|
|
|
|
|
|
|
|
// Remove SIGINT handler
|
|
|
|
|
std::signal(SIGINT, SIG_DFL);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Issue stop command
|
|
|
|
|
|
|
|
|
|
stream_cmd.stream_mode = uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
|
|
|
|
|
cout << endl << "Stopping replay..." << endl;
|
2018-11-06 12:48:28 +00:00
|
|
|
replay_ctrl->issue_stream_cmd(stream_cmd, replay_chan);
|
2018-07-16 12:39:30 +00:00
|
|
|
|
2018-11-08 17:40:07 +00:00
|
|
|
// The stop takes effect after the current command has completed, so use
|
|
|
|
|
// halt to stop the command in progress and clear any queued commands.
|
|
|
|
|
replay_ctrl->play_halt(replay_chan);
|
|
|
|
|
|
2018-07-16 12:39:30 +00:00
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
2018-11-08 17:40:07 +00:00
|
|
|
// Wait for any buffered replay data to finish playing out
|
2018-07-16 12:39:30 +00:00
|
|
|
|
|
|
|
|
uint16_t prev_packet_count, packet_count;
|
|
|
|
|
|
|
|
|
|
cout << "Waiting for replay data to flush... ";
|
2019-01-14 18:35:25 +00:00
|
|
|
prev_packet_count =
|
|
|
|
|
replay_ctrl->sr_read64(uhd::rfnoc::SR_READBACK_REG_GLOBAL_PARAMS, replay_chan)
|
|
|
|
|
>> 32;
|
|
|
|
|
while (true) {
|
2018-07-16 12:39:30 +00:00
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
2019-01-14 18:35:25 +00:00
|
|
|
packet_count =
|
|
|
|
|
replay_ctrl->sr_read64(uhd::rfnoc::SR_READBACK_REG_GLOBAL_PARAMS, replay_chan)
|
|
|
|
|
>> 32;
|
|
|
|
|
if (packet_count == prev_packet_count)
|
|
|
|
|
break;
|
2018-07-16 12:39:30 +00:00
|
|
|
prev_packet_count = packet_count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cout << endl;
|
|
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
2019-09-19 21:37:58 +00:00
|
|
|
}
|