diff options
Diffstat (limited to 'host')
-rw-r--r-- | host/CMakeLists.txt | 4 | ||||
-rw-r--r-- | host/docs/vrt_chdr.dox | 24 | ||||
-rw-r--r-- | host/examples/CMakeLists.txt | 4 | ||||
-rw-r--r-- | host/examples/twinrx_freq_hopping.cpp | 280 | ||||
-rw-r--r-- | host/include/uhd/types/sid.hpp | 8 | ||||
-rw-r--r-- | host/include/uhd/usrp/multi_usrp.hpp | 2 | ||||
-rw-r--r-- | host/lib/transport/udp_common.hpp | 3 | ||||
-rw-r--r-- | host/lib/transport/udp_zero_copy.cpp | 16 | ||||
-rw-r--r-- | host/lib/usrp/common/max287x.hpp | 6 |
9 files changed, 324 insertions, 23 deletions
diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt index aab71d7e8..8def96811 100644 --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -238,8 +238,8 @@ IF(MSVC) -D_CRT_NONSTDC_NO_DEPRECATE -D_WINSOCK_DEPRECATED_NO_WARNINGS ) - ADD_DEFINITIONS(/MP) #multi-threaded build - ADD_DEFINITIONS(/bigobj) #Increases the number of addressable sections in an .obj file. + # multi-threaded build and increases the number of addressable sections in an .obj file. + SET (COMPILE_FLAGS ${COMPILE_FLAGS} /MP /bigobj) ENDIF(MSVC) IF(CYGWIN) diff --git a/host/docs/vrt_chdr.dox b/host/docs/vrt_chdr.dox index a2d36c93e..8675349f9 100644 --- a/host/docs/vrt_chdr.dox +++ b/host/docs/vrt_chdr.dox @@ -51,20 +51,20 @@ Bits | Meaning 60 | End-of-burst or error flag 59:48 | 12-bit sequence number 47:32 | Total packet length in Bytes -31:0 | Stream ID (SID) - +31:0 | Stream ID (SID). For the format of SID, see uhd::sid_t. The packet type is determined mainly by the first two bits, although -the EOB or error flag are also taken into consideration: - -Bit 63 | Bit 62 | Bit 60 | Packet Type --------|--------|--------|-------------- -0 | 0 | 0 | Data -0 | 0 | 1 | Data (End-of-burst) -0 | 1 | 0 | Flow Control -1 | 0 | 0 | Command Packet -1 | 1 | 0 | Command Response -1 | 1 | 1 | Command Response (Error) +the EOB or error flag are also taken into consideration (the third bit does not +affect the packet type): + +Bit 63 | Bit 62 | Bit 61 | Bit 60 | Packet Type +-------|--------|--------|--------|-------------- +0 | 0 | x | 0 | Data +0 | 0 | x | 1 | Data (End-of-burst) +0 | 1 | x | 0 | Flow Control +1 | 0 | x | 0 | Command Packet +1 | 1 | x | 0 | Command Response +1 | 1 | x | 1 | Command Response (Error) \section vrt_tools Tools diff --git a/host/examples/CMakeLists.txt b/host/examples/CMakeLists.txt index e61fd897f..e10c463f8 100644 --- a/host/examples/CMakeLists.txt +++ b/host/examples/CMakeLists.txt @@ -61,6 +61,10 @@ IF(CURSES_FOUND) ADD_EXECUTABLE(rx_ascii_art_dft rx_ascii_art_dft.cpp) TARGET_LINK_LIBRARIES(rx_ascii_art_dft uhd ${CURSES_LIBRARIES} ${Boost_LIBRARIES}) UHD_INSTALL(TARGETS rx_ascii_art_dft RUNTIME DESTINATION ${PKG_LIB_DIR}/examples COMPONENT examples) + + ADD_EXECUTABLE(twinrx_freq_hopping twinrx_freq_hopping.cpp) + TARGET_LINK_LIBRARIES(twinrx_freq_hopping uhd ${CURSES_LIBRARIES} ${Boost_LIBRARIES}) + UHD_INSTALL(TARGETS twinrx_freq_hopping RUNTIME DESTINATION ${PKG_LIB_DIR}/examples COMPONENT examples) ENDIF(CURSES_FOUND) ######################################################################## diff --git a/host/examples/twinrx_freq_hopping.cpp b/host/examples/twinrx_freq_hopping.cpp new file mode 100644 index 000000000..6d7018912 --- /dev/null +++ b/host/examples/twinrx_freq_hopping.cpp @@ -0,0 +1,280 @@ +// +// Copyright 2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +// FFT conversion +#include "ascii_art_dft.hpp" + +#include <uhd/utils/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/usrp/multi_usrp.hpp> + +#include <boost/program_options.hpp> +#include <boost/thread.hpp> + +#include <fstream> + +/* + * This example shows how to implement fast frequency hopping using an X-Series + * motherboard and a TwinRX daughterboard. + * + * The TwinRX daughterboard is different than previous daughterboards in that it has two + * RX channels, each with a set of Local Oscillators (LOs). Either channel can be configured + * to use either LO set, allowing for the two channels to share an LO source. + * + * The TwinRX can be used like any other daughterboard, as the multi_usrp::set_rx_freq() + * function will automatically calculate and set the two LO frequencies as needed. + * However, this adds to the overall tuning time. If the LO frequencies are manually set + * with the multi_usrp::set_rx_lo_freq() function, the TwinRX will will not perform the + * calculation itself, resulting in a faster tune time. This example shows how to take + * advantage of this as follows: + * + * 1. Tune across the given frequency range, storing the calculated LO frequencies along + * the way. + * 2. Use timed commands to tell the TwinRX to receive bursts of samples at given intervals. + * 3. For each frequency, tune the LOs for the inactive channel for the next frequency and + * receive at the current frequency. + * 4. If applicable, send the next timed command for streaming. + */ + +namespace pt = boost::posix_time; +namespace po = boost::program_options; + +typedef std::vector<std::complex<float> > recv_buff_t; +typedef std::vector<recv_buff_t> recv_buffs_t; + +// Global objects +static uhd::usrp::multi_usrp::sptr usrp; +static uhd::rx_streamer::sptr rx_stream; +static recv_buffs_t buffs; +static size_t recv_spb, spb; + +static std::vector<double> rf_freqs; + +static uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); + +double receive_interval; + +// Define the active channel (connected to antenna) and the unused channel +size_t ACTIVE_CHAN = 0; +size_t UNUSED_CHAN = 1; + +const int X300_COMMAND_FIFO_DEPTH = 16; + + +// This is a helper function for receiving samples from the USRP +static void twinrx_recv(recv_buff_t &buffer) { + + size_t num_acc_samps = 0; + uhd::rx_metadata_t md; + + // Repeatedly retrieve samples until the entire acquisition is received + while (num_acc_samps < spb) { + size_t num_to_recv = std::min<size_t>(recv_spb, (spb - num_acc_samps)); + + // recv call will block until samples are ready or the call times out + size_t num_recvd = rx_stream->recv(&buffer[num_acc_samps], num_to_recv, md, receive_interval); + + if(md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE) { + std::cout << md.strerror() << std::endl; + break; + } + num_acc_samps += num_recvd; + } +} + +// Function to write the acquisition FFT to a binary file +static void write_fft_to_file(const std::string &fft_path) { + std::cout << "Calculating FFTs (this may take a while)... " << std::flush; + std::ofstream ofile(fft_path.c_str(), std::ios::binary); + BOOST_FOREACH(const recv_buff_t &buff, buffs) { + std::vector<float> fft = acsii_art_dft::log_pwr_dft(&buff.front(), buff.size()); + ofile.write((char*)&fft[0], (sizeof(float)*fft.size())); + } + ofile.close(); + std::cout << "done." << std::endl; +} + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + uhd::set_thread_priority_safe(); + + // Program options + std::string args, fft_path, subdev, ant; + double rate, gain; + double start_freq, end_freq; + + // Set up the program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "Print this help message") + ("args", po::value<std::string>(&args)->default_value(""), "UHD device args") + ("subdev", po::value<std::string>(&subdev)->default_value("A:0 A:1"), "Subdevice specification") + ("ant", po::value<std::string>(&ant)->default_value("RX1"), "RX Antenna") + ("start-freq", po::value<double>(&start_freq), "Start frequency (defaults to lowest valid frequency)") + ("end-freq", po::value<double>(&end_freq), "End frequency (defaults to highest valid frequency)") + ("receive-interval", po::value<double>(&receive_interval)->default_value(5e-3), "Interval between scheduled receives") + ("rate", po::value<double>(&rate)->default_value(1e6), "Incoming sample rate") + ("gain", po::value<double>(&gain)->default_value(60), "RX gain") + ("spb", po::value<size_t>(&spb)->default_value(1024), "Samples per buffer") + ("fft-path", po::value<std::string>(&fft_path), "Output an FFT to this file (optional)") + ("repeat", "repeat sweep until Ctrl-C is pressed") + ; + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + if(vm.count("help")) { + std::cout << "TwinRX Frequency Hopping Example - " << desc << std::endl; + return EXIT_SUCCESS; + } + + // Create a USRP device + std::cout << boost::format("\nCreating the USRP device with args: \"%s\"...\n") % args; + usrp = uhd::usrp::multi_usrp::make(args); + + // Make sure the USRP is an X3xx with a TwinRX + uhd::dict<std::string, std::string> info = usrp->get_usrp_rx_info(); + if(info.get("mboard_id").find("X3") == std::string::npos) { + throw uhd::runtime_error("This example can only be used with an X-Series motherboard."); + } + if(info.get("rx_id").find("TwinRX") == std::string::npos) { + throw uhd::runtime_error("This example can only be used with a TwinRX daughterboard."); + } + + // Validate frequency range + uhd::freq_range_t rx_freq_range = usrp->get_rx_freq_range(); + if (!vm.count("start-freq")) { + start_freq = rx_freq_range.start(); + } + if (!vm.count("end-freq")) { + end_freq = rx_freq_range.stop(); + } + if (start_freq < rx_freq_range.start() or end_freq > rx_freq_range.stop()) { + throw uhd::runtime_error((boost::format("Start and stop frequencies must be between %d and %d MHz") + % ((rx_freq_range.start() / 1e6), (rx_freq_range.stop() / 1e6))).str()); + } + if (start_freq > end_freq) { + throw uhd::runtime_error("Start frequency must be less than end frequency."); + } + if ((end_freq - start_freq) > 0 and (end_freq - start_freq) < rate) { + throw uhd::runtime_error("The sample rate must be less than the range between the start and end frequencies."); + } + + // Set TwinRX settings + usrp->set_rx_subdev_spec(subdev); + + // Set the unused channel to not use any LOs. This allows the active channel to control them. + usrp->set_rx_lo_source("disabled", uhd::usrp::multi_usrp::ALL_LOS, UNUSED_CHAN); + + // Set user settings + std::cout << boost::format("Setting antenna to: %s\n") % ant; + usrp->set_rx_antenna(ant, ACTIVE_CHAN); + std::cout << boost::format("Actual antenna: %s\n") % usrp->get_rx_antenna(ACTIVE_CHAN); + + std::cout << boost::format("Setting sample rate to: %d\n") % rate; + usrp->set_rx_rate(rate); + std::cout << boost::format("Actual sample rate: %d\n") % usrp->get_rx_rate(); + + std::cout << boost::format("Setting gain to: %d\n") % gain; + usrp->set_rx_gain(gain); + std::cout << boost::format("Actual gain: %d\n") % usrp->get_rx_gain(); + + // Get an rx_streamer from the device + uhd::stream_args_t stream_args("fc32", "sc16"); + stream_args.channels.push_back(0); + rx_stream = usrp->get_rx_stream(stream_args); + recv_spb = rx_stream->get_max_num_samps(); + + // Calculate the frequency hops + for (double rx_freq = start_freq; rx_freq <= end_freq; rx_freq += rate) { + rf_freqs.push_back(rx_freq); + } + std::cout << boost::format("Total Hops: %d\n") % rf_freqs.size(); + + // Set up buffers + buffs = recv_buffs_t(rf_freqs.size(), recv_buff_t(spb)); + + // Tune the active channel to the first frequency and reset the USRP's time + usrp->set_rx_freq(rf_freqs[0], ACTIVE_CHAN); + usrp->set_time_now(uhd::time_spec_t(0.0)); + + // Configure the stream command which will be issued to acquire samples at each frequency + stream_cmd.num_samps = spb; + stream_cmd.stream_now = false; + stream_cmd.time_spec = uhd::time_spec_t(0.0); + + // Stream commands will be scheduled at regular intervals + uhd::time_spec_t receive_interval_ts = uhd::time_spec_t(receive_interval); + + // Issue stream commands to fill the command queue on the FPGA + size_t num_initial_cmds = std::min<size_t>(X300_COMMAND_FIFO_DEPTH, rf_freqs.size()); + size_t num_issued_commands; + + for (num_issued_commands = 0; num_issued_commands < num_initial_cmds; num_issued_commands++) { + stream_cmd.time_spec += receive_interval_ts; + rx_stream->issue_stream_cmd(stream_cmd); + } + + // Hop frequencies and acquire bursts of samples at each until done sweeping + while(1) { + + std::cout << "Scanning..." << std::endl; + uhd::time_spec_t start_time = uhd::time_spec_t::get_system_time(); + + for (size_t i = 0; i < rf_freqs.size(); i++) { + // Swap the mapping of synthesizers by setting the LO source + // The unused channel will always + std::string lo_src = (i % 2) ? "companion" : "internal"; + usrp->set_rx_lo_source(lo_src, uhd::usrp::multi_usrp::ALL_LOS, ACTIVE_CHAN); + + // Preconfigure the next frequency + usrp->set_rx_freq(rf_freqs[(i+1) % rf_freqs.size()], UNUSED_CHAN); + + // Program the current frequency + // This frequency was already pre-programmed in the previous iteration so the local oscillators + // are already tuned. This call will only configure front-end filter, amplifiers, etc + usrp->set_rx_freq(rf_freqs[i], ACTIVE_CHAN); + + // Receive one burst of samples + twinrx_recv(buffs[i]); + + // Schedule another acquisition if necessary + if (vm.count("repeat") or num_issued_commands < rf_freqs.size()) { + stream_cmd.time_spec += receive_interval_ts; + rx_stream->issue_stream_cmd(stream_cmd); + num_issued_commands++; + } + } + + uhd::time_spec_t end_time = uhd::time_spec_t::get_system_time(); + std::cout << boost::format("Sweep done in %d milliseconds.\n") % ((end_time - start_time).get_real_secs() * 1000); + + // Optionally convert received samples to FFT and write to file + if(vm.count("fft-path")) { + write_fft_to_file(fft_path); + } + + if (!vm.count("repeat")){ + break; + } + } + + std::cout << "Done!" << std::endl; + + usrp.reset(); + return EXIT_SUCCESS; +} + diff --git a/host/include/uhd/types/sid.hpp b/host/include/uhd/types/sid.hpp index f1471549e..f9fa40273 100644 --- a/host/include/uhd/types/sid.hpp +++ b/host/include/uhd/types/sid.hpp @@ -39,7 +39,13 @@ namespace uhd { * is required, we use the combination of the 8-bit address and the 8-bit * endpoint. * - * \section sid_str_repr String Representation + * <pre> + * +-------------+--------------+-------------+--------------+ + * | SRC address | SRC endpoint | DST address | DST endpoint | + * +-------------+--------------+-------------+--------------+ + * </pre> + * + * \section sid_str_repr String Representation (pretty printing) * * The string representation of a SID is of the form * diff --git a/host/include/uhd/usrp/multi_usrp.hpp b/host/include/uhd/usrp/multi_usrp.hpp index 1a64a8f3a..fee430fe0 100644 --- a/host/include/uhd/usrp/multi_usrp.hpp +++ b/host/include/uhd/usrp/multi_usrp.hpp @@ -120,6 +120,8 @@ public: * Make a new multi usrp from the device address. * \param dev_addr the device address * \return a new single usrp object + * \throws uhd::key_error no device found + * \throws uhd::index_error fewer devices found than expected */ static sptr make(const device_addr_t &dev_addr); diff --git a/host/lib/transport/udp_common.hpp b/host/lib/transport/udp_common.hpp index 82eee237b..d6e73cc5f 100644 --- a/host/lib/transport/udp_common.hpp +++ b/host/lib/transport/udp_common.hpp @@ -23,6 +23,9 @@ namespace uhd{ namespace transport{ + // Jumbo frames are limited to 9000; + static const size_t MAX_ETHERNET_MTU = 9000; + typedef boost::shared_ptr<boost::asio::ip::udp::socket> socket_sptr; /*! diff --git a/host/lib/transport/udp_zero_copy.cpp b/host/lib/transport/udp_zero_copy.cpp index 121bb9b64..68970f332 100644 --- a/host/lib/transport/udp_zero_copy.cpp +++ b/host/lib/transport/udp_zero_copy.cpp @@ -310,22 +310,22 @@ udp_zero_copy::sptr udp_zero_copy::make( xport_params.num_send_frames = size_t(hints.cast<double>("num_send_frames", default_buff_args.num_send_frames)); //extract buffer size hints from the device addr - size_t usr_recv_buff_size = size_t(hints.cast<double>("recv_buff_size", 0.0)); - size_t usr_send_buff_size = size_t(hints.cast<double>("send_buff_size", 0.0)); + size_t usr_recv_buff_size = size_t(hints.cast<double>("recv_buff_size", xport_params.num_recv_frames * MAX_ETHERNET_MTU)); + size_t usr_send_buff_size = size_t(hints.cast<double>("send_buff_size", xport_params.num_send_frames * MAX_ETHERNET_MTU)); if (hints.has_key("recv_buff_size")) { - if (usr_recv_buff_size < xport_params.recv_frame_size * xport_params.num_recv_frames) { + if (usr_recv_buff_size < xport_params.num_recv_frames * MAX_ETHERNET_MTU) { throw uhd::value_error((boost::format( - "recv_buff_size must be equal to or greater than (num_recv_frames * recv_frame_size) where num_recv_frames=%d, recv_frame_size=%d") - % xport_params.num_recv_frames % xport_params.recv_frame_size).str()); + "recv_buff_size must be equal to or greater than %d") + % (xport_params.num_recv_frames * MAX_ETHERNET_MTU)).str()); } } if (hints.has_key("send_buff_size")) { - if (usr_send_buff_size < xport_params.send_frame_size * xport_params.num_send_frames) { + if (usr_send_buff_size < xport_params.num_send_frames * MAX_ETHERNET_MTU) { throw uhd::value_error((boost::format( - "send_buff_size must be equal to or greater than (num_send_frames * send_frame_size) where num_send_frames=%d, send_frame_size=%d") - % xport_params.num_send_frames % xport_params.send_frame_size).str()); + "send_buff_size must be equal to or greater than %d") + % (xport_params.num_send_frames * MAX_ETHERNET_MTU)).str()); } } diff --git a/host/lib/usrp/common/max287x.hpp b/host/lib/usrp/common/max287x.hpp index 1209d194e..a0400f651 100644 --- a/host/lib/usrp/common/max287x.hpp +++ b/host/lib/usrp/common/max287x.hpp @@ -900,6 +900,12 @@ void max287x<max287x_regs_t>::commit() } else { try { changed_regs = _regs.template get_changed_addrs<uint32_t> (); + // register 0 must be written to apply double buffered fields + if (changed_regs.size() > 0) + { + changed_regs.insert(0); + } + for (int addr = 5; addr >= 0; addr--) { if (changed_regs.find(uint32_t(addr)) != changed_regs.end()) |