diff options
author | mattprost <matt.prost@ni.com> | 2020-08-06 18:10:16 -0500 |
---|---|---|
committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2020-08-11 16:08:16 -0500 |
commit | fa2d383ff539c40945d461176c0a3c64e033467c (patch) | |
tree | 3a9a09be5951201e43ac996b0d9afa262075beb9 /host | |
parent | f9ef6757b13ba972bdb658a292ae896cda64aef7 (diff) | |
download | uhd-fa2d383ff539c40945d461176c0a3c64e033467c.tar.gz uhd-fa2d383ff539c40945d461176c0a3c64e033467c.tar.bz2 uhd-fa2d383ff539c40945d461176c0a3c64e033467c.zip |
examples: replay samples from file
This example exercises the Replay Block RFNoC API. The Replay records
IQ data from a file and plays it back into a Radio for transmitting.
Signed-off-by: mattprost <matt.prost@ni.com>
Diffstat (limited to 'host')
-rw-r--r-- | host/examples/CMakeLists.txt | 2 | ||||
-rw-r--r-- | host/examples/replay_samples_from_file.cpp | 413 | ||||
-rw-r--r-- | host/examples/rfnoc_replay_samples_from_file.cpp | 423 |
3 files changed, 424 insertions, 414 deletions
diff --git a/host/examples/CMakeLists.txt b/host/examples/CMakeLists.txt index a001c81f3..40a2eb7ad 100644 --- a/host/examples/CMakeLists.txt +++ b/host/examples/CMakeLists.txt @@ -31,8 +31,8 @@ set(example_sources rfnoc_nullsource_ce_rx.cpp rfnoc_rx_to_file.cpp rfnoc_radio_loopback.cpp + rfnoc_replay_samples_from_file.cpp #benchmark_streamer.cpp - #replay_samples_from_file.cpp ) if(ENABLE_OCTOCLOCK) diff --git a/host/examples/replay_samples_from_file.cpp b/host/examples/replay_samples_from_file.cpp deleted file mode 100644 index a384f1131..000000000 --- a/host/examples/replay_samples_from_file.cpp +++ /dev/null @@ -1,413 +0,0 @@ -// -// Copyright 2018 Ettus Research, A National Instruments Company -// Copyright 2019 Ettus Research, A National Instruments Brand -// -// 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> -#include <uhd/utils/safe_main.hpp> -#include <boost/format.hpp> -#include <boost/program_options.hpp> -#include <csignal> -#include <fstream> -#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; -} - - -int UHD_SAFE_MAIN(int argc, char* argv[]) -{ - // 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 - 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 - - - /////////////////////////////////////////////////////////////////////////// - // 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"); - // clang-format off - 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") - ("nsamps", po::value<size_t>(&nsamps)->default_value(0), "number of samples to play (0 for infinite)") - ("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)") - ; - // clang-format on - 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; - cout << "This application uses the Replay block to playback data from a file to " - "a radio" - << endl - << endl; - 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); - std::cout << "Using replay block " << replay_id << ", channel " << replay_chan - << std::endl; - - - /////////////////////////////////////////////////////////////////////////// - // Configure radio - - // Lock clocks - if (vm.count("ref")) { - radio_ctrl->set_clock_source(ref); - } - - // Apply any radio arguments provided - radio_ctrl->set_args(radio_args); - - // Set the center frequency - if (not vm.count("freq")) { - std::cerr << "Please specify the center frequency with --freq" << std::endl; - return EXIT_FAILURE; - } - std::cout << boost::format("Setting TX Freq: %f MHz...") % (freq / 1e6) << std::endl; - radio_ctrl->set_tx_frequency(freq, radio_chan); - std::cout << boost::format("Actual TX Freq: %f MHz...") - % (radio_ctrl->get_tx_frequency(radio_chan) / 1e6) - << std::endl - << std::endl; - - // Set the sample rate - if (vm.count("rate")) { - std::cout << boost::format("Setting TX Rate: %f Msps...") % (rate / 1e6) - << std::endl; - radio_ctrl->set_rate(rate); - std::cout << boost::format("Actual TX Rate: %f Msps...") - % (radio_ctrl->get_rate() / 1e6) - << std::endl - << std::endl; - } - - // Set the RF gain - if (vm.count("gain")) { - std::cout << boost::format("Setting TX Gain: %f dB...") % gain << std::endl; - radio_ctrl->set_tx_gain(gain, radio_chan); - std::cout << boost::format("Actual TX Gain: %f dB...") - % radio_ctrl->get_tx_gain(radio_chan) - << std::endl - << std::endl; - } - - // Set the analog front-end filter bandwidth - if (vm.count("bw")) { - std::cout << boost::format("Setting TX Bandwidth: %f MHz...") % (bw / 1e6) - << std::endl; - radio_ctrl->set_tx_bandwidth(bw, radio_chan); - std::cout << boost::format("Actual TX Bandwidth: %f MHz...") - % (radio_ctrl->get_tx_bandwidth(radio_chan) / 1e6) - << std::endl - << std::endl; - } - - // 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)); - - - /////////////////////////////////////////////////////////////////////////// - // Connect Replay block to radio - - uhd::rfnoc::graph::sptr replay_graph = usrp->create_graph("rfnoc_replay"); - usrp->clear(); - 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); - - // 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; - - streamer_args["block_id"] = replay_ctrl->get_block_id().to_string(); - streamer_args["block_port"] = str(boost::format("%d") % replay_chan); - stream_args.args = streamer_args; - tx_stream = usrp->get_tx_stream(stream_args); - - // Make sure that streamer SPP is a multiple of the Replay block word size - size_t tx_spp = tx_stream->get_max_num_samps(); - if (tx_spp % samples_per_word != 0) { - // 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); - stream_args.args = streamer_args; - tx_stream = usrp->get_tx_stream(stream_args); - } - - 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 - size_t words_to_replay = file_size / replay_word_size; - 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 - cout << boost::format("Replay file size: %d bytes (%d qwords, %d samples)") - % (words_to_replay * replay_word_size) % words_to_replay - % samples_to_replay - << endl; - - 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; - - // 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); - if (fullness != 0) - break; - 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); - - - /////////////////////////////////////////////////////////////////////////// - // 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; - size_t num_tx_samps = tx_stream->send(tx_buf_ptr, samples_to_replay, tx_md); - - if (num_tx_samps != samples_to_replay) { - cout << boost::format("ERROR: Unable to send %d samples") % samples_to_replay - << endl; - return EXIT_FAILURE; - } - - - /////////////////////////////////////////////////////////////////////////// - // Wait for data to be stored in on-board memory - - cout << "Waiting for recording to complete..." << endl; - while (replay_ctrl->get_record_fullness(replay_chan) - < words_to_replay * replay_word_size) - ; - - - /////////////////////////////////////////////////////////////////////////// - // 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; - 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 { - // 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; - stream_cmd.num_samps = nsamps / samples_per_word; - cout << boost::format("Issuing replay command for %d words...") - % stream_cmd.num_samps - << endl; - } - 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; - - while (not stop_signal_called) - ; - - // 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; - replay_ctrl->issue_stream_cmd(stream_cmd, replay_chan); - - // 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); - - - /////////////////////////////////////////////////////////////////////////// - // Wait for any buffered replay data to finish playing out - - uint16_t prev_packet_count, packet_count; - - cout << "Waiting for replay data to flush... "; - prev_packet_count = - replay_ctrl->sr_read64(uhd::rfnoc::SR_READBACK_REG_GLOBAL_PARAMS, replay_chan) - >> 32; - while (true) { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - packet_count = - replay_ctrl->sr_read64(uhd::rfnoc::SR_READBACK_REG_GLOBAL_PARAMS, replay_chan) - >> 32; - if (packet_count == prev_packet_count) - break; - prev_packet_count = packet_count; - } - - cout << endl; - - return EXIT_SUCCESS; -} diff --git a/host/examples/rfnoc_replay_samples_from_file.cpp b/host/examples/rfnoc_replay_samples_from_file.cpp new file mode 100644 index 000000000..78a0d8894 --- /dev/null +++ b/host/examples/rfnoc_replay_samples_from_file.cpp @@ -0,0 +1,423 @@ +// +// Copyright 2020 Ettus Research, A National Instruments Brand +// +// 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/rfnoc/block_id.hpp> +#include <uhd/rfnoc/duc_block_control.hpp> +#include <uhd/rfnoc/mb_controller.hpp> +#include <uhd/rfnoc/radio_control.hpp> +#include <uhd/rfnoc/replay_block_control.hpp> +#include <uhd/rfnoc_graph.hpp> +#include <uhd/types/tune_request.hpp> +#include <uhd/utils/graph_utils.hpp> +#include <uhd/utils/math.hpp> +#include <uhd/utils/safe_main.hpp> +#include <boost/program_options.hpp> +#include <chrono> +#include <csignal> +#include <fstream> +#include <iostream> +#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; +} + + +int UHD_SAFE_MAIN(int argc, char* argv[]) +{ + // 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 + const size_t replay_word_size = 8; // Size of words used by replay block + const size_t sample_size = 4; // Complex signed 16-bit is 32 bits per sample + const size_t samples_per_word = 2; // Number of sc16 samples per word + + /************************************************************************ + * Set up the program options + ***********************************************************************/ + std::string args, tx_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"); + // clang-format off + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "multi uhd device address args") + ("tx_args", po::value<std::string>(&tx_args), "Block args for the transmit radio") + ("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") + ("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") + ("nsamps", po::value<size_t>(&nsamps)->default_value(0), "number of samples to play (0 for infinite)") + ("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)") + ; + // clang-format on + 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 << "UHD/RFNoC Replay samples from file " << desc << endl; + cout << "This application uses the Replay block to playback data from a file to " + "a radio" + << endl + << endl; + return EXIT_FAILURE; + } + + + /************************************************************************ + * Create device and block controls + ***********************************************************************/ + std::cout << std::endl; + std::cout << "Creating the RFNoC graph with args: " << args << "..." << std::endl; + uhd::rfnoc::rfnoc_graph::sptr graph = uhd::rfnoc::rfnoc_graph::make(args); + + // Create handle for radio object + uhd::rfnoc::block_id_t radio_ctrl_id(0, "Radio", radio_id); + uhd::rfnoc::radio_control::sptr radio_ctrl; + radio_ctrl = graph->get_block<uhd::rfnoc::radio_control>(radio_ctrl_id); + std::cout << "Using radio " << radio_ctrl_id << ", channel " << radio_chan + << std::endl; + + // Check for a duc connected to the radio + auto edges = graph->enumerate_static_connections(); + std::string dst_block = radio_ctrl->get_block_id(); + size_t dst_port = radio_chan; + uhd::rfnoc::duc_block_control::sptr duc_ctrl; + size_t duc_chan = 0; + for (auto& edge : edges) { + if (edge.dst_blockid == dst_block && edge.dst_port == dst_port) { + auto blockid = uhd::rfnoc::block_id_t(edge.src_blockid); + if (blockid.match("DUC")) { + duc_ctrl = graph->get_block<uhd::rfnoc::duc_block_control>(blockid); + duc_chan = edge.src_port; + } + break; + } + } + if (duc_ctrl) { + std::cout << "Using duc " << duc_ctrl->get_block_id() << ", channel " << duc_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_control::sptr replay_ctrl; + if (!graph->has_block(replay_ctrl_id)) { + cout << "Unable to find block \"" << replay_ctrl_id << "\"" << endl; + return EXIT_FAILURE; + } + replay_ctrl = graph->get_block<uhd::rfnoc::replay_block_control>(replay_ctrl_id); + std::cout << "Using replay " << replay_ctrl_id << ", channel " << replay_chan + << std::endl; + + /************************************************************************ + * Set up radio + ***********************************************************************/ + // Set clock reference + if (vm.count("ref")) { + // Lock mboard clocks + for (size_t i = 0; i < graph->get_num_mboards(); ++i) { + graph->get_mb_controller(i)->set_clock_source(ref); + } + } + + // Apply any radio arguments provided + if (vm.count("tx_args")) { + radio_ctrl->set_tx_tune_args(tx_args, radio_chan); + } + + // Set the center frequency + if (not vm.count("freq")) { + std::cerr << "Please specify the center frequency with --freq" << std::endl; + return EXIT_FAILURE; + } + std::cout << std::fixed; + std::cout << "Setting TX Freq: " << std::fixed << (freq / 1e6) << " MHz..." + << std::endl; + radio_ctrl->set_tx_frequency(freq, radio_chan); + std::cout << "Actual TX Freq: " << (radio_ctrl->get_tx_frequency(radio_chan) / 1e6) + << " MHz..." << std::endl + << std::endl; + std::cout << std::resetiosflags(std::ios::fixed); + + // Set the sample rate + if (vm.count("rate")) { + std::cout << std::fixed; + std::cout << "Setting TX Rate: " << (rate / 1e6) << " Msps..." << std::endl; + if (duc_ctrl) { + std::cout << "DUC block found." << std::endl; + duc_ctrl->set_input_rate(rate, duc_chan); + duc_ctrl->set_output_rate(radio_ctrl->get_rate(), duc_chan); + std::cout << "Interpolation value is " + << duc_ctrl->get_property<int>("interp", duc_chan) << std::endl; + rate = duc_ctrl->get_input_rate(duc_chan); + } else { + rate = radio_ctrl->set_rate(rate); + } + std::cout << "Actual TX Rate: " << (rate / 1e6) << " Msps..." << std::endl + << std::endl; + std::cout << std::resetiosflags(std::ios::fixed); + } + + // Set the RF gain + if (vm.count("gain")) { + std::cout << std::fixed; + std::cout << "Setting TX Gain: " << gain << " dB..." << std::endl; + radio_ctrl->set_tx_gain(gain, radio_chan); + std::cout << "Actual TX Gain: " << radio_ctrl->get_tx_gain(radio_chan) << " dB..." + << std::endl + << std::endl; + std::cout << std::resetiosflags(std::ios::fixed); + } + + // Set the analog front-end filter bandwidth + if (vm.count("bw")) { + std::cout << std::fixed; + std::cout << "Setting TX Bandwidth: " << (bw / 1e6) << " MHz..." << std::endl; + radio_ctrl->set_tx_bandwidth(bw, radio_chan); + std::cout << "Actual TX Bandwidth: " + << (radio_ctrl->get_tx_bandwidth(radio_chan) / 1e6) << " MHz..." + << std::endl + << std::endl; + std::cout << std::resetiosflags(std::ios::fixed); + } + + // 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)); + + + /************************************************************************ + * Connect Replay block to Radio + ***********************************************************************/ + UHD_LOG_INFO("TEST", "Creating graph..."); + if (duc_ctrl) { + graph->connect( + duc_ctrl->get_block_id(), duc_chan, radio_ctrl->get_block_id(), radio_chan); + graph->connect( + replay_ctrl->get_block_id(), replay_chan, duc_ctrl->get_block_id(), duc_chan); + } else { + graph->connect(replay_ctrl->get_block_id(), + replay_chan, + radio_ctrl->get_block_id(), + radio_chan); + } + UHD_LOG_INFO("TEST", "Committing graph..."); + graph->commit(); + UHD_LOG_INFO("TEST", "Commit complete."); + + + /************************************************************************ + * Set up 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; + + streamer_args["block_id"] = replay_ctrl->get_block_id().to_string(); + streamer_args["block_port"] = std::to_string(replay_chan); + stream_args.args = streamer_args; + stream_args.channels = {replay_chan}; + tx_stream = graph->create_tx_streamer(stream_args.channels.size(), stream_args); + graph->connect(tx_stream, 0, replay_ctrl->get_block_id(), replay_chan); + graph->commit(); + + // Make sure that streamer SPP is a multiple of the Replay block word size + size_t tx_spp = tx_stream->get_max_num_samps(); + if (tx_spp % samples_per_word != 0) { + // 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"] = std::to_string(tx_spp); + stream_args.args = streamer_args; + tx_stream = graph->create_tx_streamer(stream_args.channels.size(), stream_args); + graph->connect(tx_stream, replay_chan, replay_ctrl->get_block_id(), replay_chan); + graph->commit(); + } + + + /************************************************************************ + * 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 + size_t words_to_replay = file_size / replay_word_size; + size_t samples_to_replay = file_size / sample_size; + + // Create buffer + std::vector<char> tx_buffer(samples_to_replay * sample_size); + char* tx_buf_ptr = &tx_buffer[0]; + + // Read file into buffer, rounded down to number of words + infile.read(tx_buf_ptr, samples_to_replay * sample_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. + uint32_t replay_buff_addr = 0; + uint32_t replay_buff_size = samples_to_replay * sample_size; + replay_ctrl->record(replay_buff_addr, replay_buff_size, replay_chan); + + // Display replay configuration + cout << "Replay file size: " << replay_buff_size << " bytes (" << words_to_replay + << " qwords, " << samples_to_replay << " samples)" << endl; + + cout << "Record base address: 0x" << std::hex + << replay_ctrl->get_record_offset(replay_chan) << std::dec << endl; + cout << "Record buffer size: " << replay_ctrl->get_record_size(replay_chan) + << " bytes" << endl; + cout << "Record fullness: " << replay_ctrl->get_record_fullness(replay_chan) + << " bytes" << endl + << endl; + + // 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 << "Emptying 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); + if (fullness != 0) + break; + 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); + cout << "Record fullness: " << replay_ctrl->get_record_fullness(replay_chan) + << " bytes" << endl + << endl; + + /************************************************************************ + * 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; + size_t num_tx_samps = tx_stream->send(tx_buf_ptr, samples_to_replay, tx_md); + + if (num_tx_samps != samples_to_replay) { + cout << "ERROR: Unable to send " << samples_to_replay << " samples" << endl; + return EXIT_FAILURE; + } + + /************************************************************************ + * Wait for data to be stored in on-board memory + ***********************************************************************/ + cout << "Waiting for recording to complete..." << endl; + while (replay_ctrl->get_record_fullness(replay_chan) < replay_buff_size) + ; + cout << "Record fullness: " << replay_ctrl->get_record_fullness(replay_chan) + << " bytes" << endl + << endl; + + + /************************************************************************ + * Start replay of data + ***********************************************************************/ + if (nsamps <= 0) { + // Replay the entire buffer over and over + bool repeat = true; + cout << "Issuing replay command for " << samples_to_replay + << " samps in continuous mode..." << endl; + uhd::time_spec_t time_spec = uhd::time_spec_t(0.0); + replay_ctrl->play( + replay_buff_addr, replay_buff_size, replay_chan, time_spec, repeat); + } else { + // Replay nsamps, wrapping back to the start of the buffer if nsamps is + // larger than the buffer size. + replay_ctrl->config_play(replay_buff_addr, replay_buff_size, replay_chan); + uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); + stream_cmd.num_samps = nsamps; + cout << "Issuing replay command for " << nsamps << " samps..." << endl; + 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; + + while (not stop_signal_called) + ; + + // Remove SIGINT handler + std::signal(SIGINT, SIG_DFL); + + + /************************************************************************ + * Issue stop command + ***********************************************************************/ + cout << endl << "Stopping replay..." << endl; + replay_ctrl->stop(replay_chan); + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + return EXIT_SUCCESS; +} |