Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <uhd/usrp/multi_usrp.hpp>
- #include <uhd/utils/safe_main.hpp>
- #include <uhd/utils/thread.hpp>
- #include <boost/algorithm/string.hpp>
- #include <boost/format.hpp>
- #include <boost/program_options.hpp>
- #include <boost/filesystem.hpp>
- #include <chrono>
- #include <complex>
- #include <iostream>
- #include <thread>
- namespace po = boost::program_options;
- static bool stop_signal_called = false;
- void sig_int_handler(int)
- {
- stop_signal_called = true;
- }
- int UHD_SAFE_MAIN(int argc, char* argv[])
- {
- // variables to be set by po
- std::string args, sync, path, rx_filename, rx_subdev, rx_channel_list;
- double seconds_in_future;
- size_t rx_nsamps;
- double total_time, rx_rate, rx_freq, rx_gain, rx_bw;
- // 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(""), "single uhd device address args")
- ("duration", po::value<double>(&total_time)->default_value(60), "Total number of seconds to log data")
- ("delay", po::value<double>(&seconds_in_future)->default_value(3), "number of seconds in the future to receive")
- ("path", po::value<std::string>(&path)->default_value("/home/root/rx_mqp"), "File path for the log files")
- ("rx_file", po::value<std::string>(&rx_filename)->default_value("rx_data.dat"), "Name of RX log file")
- ("rx_nsamps", po::value<size_t>(&rx_nsamps)->default_value(0), "Total number of RX samples to receive. Set to 0 for continuous")
- ("rx_rate", po::value<double>(&rx_rate)->default_value(0.5e6), "Rate of incoming RX samples")
- ("rx_freq", po::value<double>(&rx_freq)->default_value(900e6), "RF center frequency of RX samples in Hz")
- ("rx_gain", po::value<double>(&rx_gain)->default_value(50), "RX gain for the RF chain")
- ("rx_bw", po::value<double>(&rx_bw)->default_value(200e3), "Analog frontend filter bandwidth of RX samples in Hz")
- ("sync", po::value<std::string>(&sync)->default_value("now"), "synchronization method: now, pps, mimo")
- ("rx_subdev", po::value<std::string>(&rx_subdev)->default_value("0:A"), "RX subdev spec (homogeneous across motherboards)")
- ("rx_channels", po::value<std::string>(&rx_channel_list)->default_value("0,1"), "which RX channel(s) to use (specify \"0\", \"1\", \"0,1\", etc)")
- ("skip-lo", "skip checking LO lock status")
- ("continue", "don't abort on a bad packet")
- ;
- // 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 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 continue_on_bad_packet = vm.count("continue") > 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(rx_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...") % (rx_rate / 1e6) << std::endl;
- _usrp->set_rx_rate(rx_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;
- if (sync == "now") {
- // This is not a true time lock, the devices will be off by a few RTT.
- // Rather, this is just to allow for demonstration of the code below.
- _usrp->set_time_now(uhd::time_spec_t(0.0));
- } else if (sync == "pps") {
- _usrp->set_time_source("external");
- _usrp->set_time_unknown_pps(uhd::time_spec_t(0.0));
- std::this_thread::sleep_for(std::chrono::seconds(1)); // wait for pps sync pulse
- } else if (sync == "mimo") {
- UHD_ASSERT_THROW(_usrp->get_num_mboards() == 2);
- // make mboard 1 a slave over the MIMO Cable
- _usrp->set_clock_source("mimo", 1);
- _usrp->set_time_source("mimo", 1);
- // set time on the master (mboard 0)
- _usrp->set_time_now(uhd::time_spec_t(0.0), 0);
- // sleep a bit while the slave locks its time to the master
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
- }
- // detect which channels to use
- std::vector<std::string> channel_strings;
- std::vector<size_t> channel_nums;
- boost::split(channel_strings, rx_channel_list, boost::is_any_of("\"',"));
- for (size_t ch = 0; ch < channel_strings.size(); ch++) {
- size_t chan = std::stoi(channel_strings[ch]);
- if (chan >= _usrp->get_rx_num_channels()) {
- throw std::runtime_error("Invalid channel(s) specified.");
- } else
- channel_nums.push_back(std::stoi(channel_strings[ch]));
- }
- for (size_t ch = 0; ch < channel_nums.size(); ch++)
- {
- // set the center frequency
- std::cout << boost::format("rx_init: Channel %d: Setting RX Freq: %f MHz...") % ch % (rx_freq / 1e6)
- << std::endl;
- uhd::tune_request_t tune_request(rx_freq);
- _usrp->set_rx_freq(tune_request, channel_nums[ch]);
- std::cout << boost::format("rx_init: Channel %d: Actual RX Freq: %f MHz...") % ch % (_usrp->get_rx_freq(channel_nums[ch]) / 1e6)
- << std::endl
- << std::endl;
- // set the analog frontend filter bandwidth
- std::cout << boost::format("rx_init: Channel %d: Setting RX Bandwidth: %f MHz...") % ch % (rx_bw / 1e6)
- << std::endl;
- _usrp->set_rx_bandwidth(rx_bw, channel_nums[ch]);
- std::cout << boost::format("rx_init: Channel %d: Actual RX Bandwidth: %f MHz...") % ch % (_usrp->get_rx_bandwidth(channel_nums[ch]) / 1e6)
- << std::endl
- << std::endl;
- // set the rf gain
- std::cout << boost::format("rx_init: Channel %d: Setting RX Gain: %f dB...") % 0 % rx_gain
- << std::endl;
- _usrp->set_rx_gain(rx_gain, channel_nums[0]);
- std::cout << boost::format("rx_init: Channel %d: Actual RX Gain: %f dB...") % 0 % _usrp->get_rx_gain(channel_nums[0])
- << std::endl
- << std::endl;
- // example also sets the antenna here
- }
- // create a receive streamer
- // linearly map channels (index0 = channel0, index1 = channel1, ...)
- uhd::stream_args_t stream_args("fc32"); // complex floats
- stream_args.channels = channel_nums;
- uhd::rx_streamer::sptr rx_stream = _usrp->get_rx_stream(stream_args);
- // Setup file path
- std::cout << "main: setup file path" << std::endl;
- boost::filesystem::path out_path;
- out_path = boost::filesystem::path(std::string(path));
- if (!boost::filesystem::exists(out_path))
- boost::filesystem::create_directory(out_path);
- boost::filesystem::path logfile = out_path / boost::filesystem::path(rx_filename);
- std::ofstream outfile(logfile.c_str(), std::ios_base::out);
- bool overflow_message = true;
- // setup streaming
- std::cout << std::endl;
- std::cout << boost::format("Begin streaming %u samples, %f seconds in the future...")
- % rx_nsamps % seconds_in_future
- << std::endl;
- uhd::stream_cmd_t stream_cmd((rx_nsamps == 0)
- ? uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS
- : uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE);
- stream_cmd.num_samps = rx_nsamps;
- stream_cmd.stream_now = false;
- stream_cmd.time_spec = uhd::time_spec_t(seconds_in_future);
- rx_stream->issue_stream_cmd(stream_cmd); // tells all channels to stream
- // meta-data will be filled in by recv()
- uhd::rx_metadata_t md;
- // 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<float>>> buffs(
- _usrp->get_rx_num_channels(), std::vector<std::complex<float>>(samps_per_buff));
- // create a vector of pointers to point to each of the channel buffers
- std::vector<std::complex<float>*> buff_ptrs;
- for (size_t i = 0; i < buffs.size(); i++)
- buff_ptrs.push_back(&buffs[i].front());
- // the first call to recv() will block this many seconds before receiving
- double timeout = seconds_in_future + 0.1; // timeout (delay before receive + padding)
- size_t num_acc_samps = 0; // number of accumulated samples
- const auto start_time = std::chrono::steady_clock::now();
- const auto stop_time =
- start_time + std::chrono::nanoseconds(int64_t(1e9 * total_time));
- while (not stop_signal_called
- and (rx_nsamps > num_acc_samps or rx_nsamps == 0)
- and (total_time == 0.0 or std::chrono::steady_clock::now() <= stop_time)) {
- // receive a single packet
- 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
- if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) {
- std::cerr << boost::format("Timeout while streaming") << std::endl;
- break;
- }
- if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW) {
- if (overflow_message) {
- overflow_message = false;
- std::cerr
- << boost::format(
- "Got an overflow indication. Please consider the following:\n"
- " Your write medium must sustain a rate of %fMB/s.\n"
- " Dropped samples will not be written to the file.\n"
- " Please modify this example for your purposes.\n"
- " This message will not appear again.\n")
- % (_usrp->get_rx_rate() * sizeof(float) / 1e6);
- }
- continue;
- }
- if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE) {
- std::string error = str(boost::format("Receiver error: %s") % md.strerror());
- if (continue_on_bad_packet) {
- std::cerr << error << std::endl;
- continue;
- } else
- throw std::runtime_error(error);
- }
- const auto now = std::chrono::steady_clock::now();
- const auto running_time = std::chrono::duration_cast<std::chrono::microseconds>(now - start_time);
- outfile << "\n" << running_time.count() << "\n";
- for (size_t n = 0; n < num_rx_samps; n++)
- {
- std::stringstream ss;
- ss << n;
- for (size_t ch = 0; ch < channel_nums.size(); ch++)
- {
- float i = buffs[ch][n].real();
- float q = buffs[ch][n].imag();
- ss << boost::format(",%f,%f") % i % q;
- }
- outfile << ss.str() << "\n";
- }
- num_acc_samps += num_rx_samps;
- }
- if (num_acc_samps < rx_nsamps)
- std::cerr << "Receive timeout before all samples received..." << std::endl;
- // finished
- std::cout << std::endl << "Done!" << std::endl << std::endl;
- return EXIT_SUCCESS;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement