diff options
Diffstat (limited to 'host/examples')
-rw-r--r-- | host/examples/CMakeLists.txt | 50 | ||||
-rw-r--r-- | host/examples/ascii_art_dft.hpp | 320 | ||||
-rw-r--r-- | host/examples/benchmark_rx_rate.cpp | 160 | ||||
-rw-r--r-- | host/examples/rx_ascii_art_dft.cpp | 143 | ||||
-rw-r--r-- | host/examples/rx_samples_to_file.cpp | 135 | ||||
-rw-r--r-- | host/examples/rx_samples_to_udp.cpp | 135 | ||||
-rw-r--r-- | host/examples/rx_timed_samples.cpp | 132 | ||||
-rw-r--r-- | host/examples/test_async_messages.cpp | 260 | ||||
-rw-r--r-- | host/examples/test_pps_input.cpp | 86 | ||||
-rw-r--r-- | host/examples/tx_from_file.cpp | 125 | ||||
-rw-r--r-- | host/examples/tx_timed_samples.cpp | 115 | ||||
-rw-r--r-- | host/examples/tx_waveforms.cpp | 184 |
12 files changed, 1845 insertions, 0 deletions
diff --git a/host/examples/CMakeLists.txt b/host/examples/CMakeLists.txt new file mode 100644 index 000000000..ce2ca9640 --- /dev/null +++ b/host/examples/CMakeLists.txt @@ -0,0 +1,50 @@ +# +# Copyright 2010 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/>. +# + +######################################################################## +# example applications +######################################################################## +SET(example_sources + benchmark_rx_rate.cpp + rx_samples_to_file.cpp + rx_samples_to_udp.cpp + rx_timed_samples.cpp + test_async_messages.cpp + test_pps_input.cpp + tx_timed_samples.cpp + tx_waveforms.cpp +) + +#for each source: build an executable and install +FOREACH(example_source ${example_sources}) + GET_FILENAME_COMPONENT(example_name ${example_source} NAME_WE) + ADD_EXECUTABLE(${example_name} ${example_source}) + TARGET_LINK_LIBRARIES(${example_name} uhd) + INSTALL(TARGETS ${example_name} RUNTIME DESTINATION ${PKG_DATA_DIR}/examples) +ENDFOREACH(example_source) + +######################################################################## +# ASCII Art DFT - requires curses, so this part is optional +######################################################################## +INCLUDE(FindCurses) + +IF(CURSES_FOUND) + INCLUDE_DIRECTORIES(${CURSES_INCLUDE_DIR}) + ADD_EXECUTABLE(rx_ascii_art_dft rx_ascii_art_dft.cpp) + TARGET_LINK_LIBRARIES(rx_ascii_art_dft uhd ${CURSES_LIBRARIES}) + INSTALL(TARGETS rx_ascii_art_dft RUNTIME DESTINATION ${PKG_DATA_DIR}/examples) +ENDIF(CURSES_FOUND) diff --git a/host/examples/ascii_art_dft.hpp b/host/examples/ascii_art_dft.hpp new file mode 100644 index 000000000..ee2267c2d --- /dev/null +++ b/host/examples/ascii_art_dft.hpp @@ -0,0 +1,320 @@ +// +// ASCII Art DFT Plotter - Josh Blum +// + +#ifndef ASCII_ART_DFT_HPP +#define ASCII_ART_DFT_HPP + +#include <string> +#include <cstddef> +#include <vector> +#include <complex> +#include <stdexcept> + +namespace acsii_art_dft{ + + //! Type produced by the log power DFT function + typedef std::vector<float> log_pwr_dft_type; + + /*! + * Get a logarithmic power DFT of the input samples. + * Samples are expected to be in the range [-1.0, 1.0]. + * \param samps a pointer to an array of complex samples + * \param nsamps the number of samples in the array + * \return a real range of DFT bins in units of dB + */ + template <typename T> log_pwr_dft_type log_pwr_dft( + const std::complex<T> *samps, size_t nsamps + ); + + /*! + * Convert a DFT to a piroundable ascii plot. + * \param dft the log power dft bins + * \param width the frame width in characters + * \param height the frame height in characters + * \param samp_rate the sample rate in Sps + * \param dc_freq the DC frequency in Hz + * \param dyn_rng the dynamic range in dB + * \param ref_lvl the reference level in dB + * \return the plot as an ascii string + */ + std::string dft_to_plot( + const log_pwr_dft_type &dft, + size_t width, + size_t height, + double samp_rate, + double dc_freq, + float dyn_rng, + float ref_lvl + ); + +} //namespace ascii_dft + +/*********************************************************************** + * Implementation includes + **********************************************************************/ +#include <cmath> +#include <sstream> +#include <algorithm> + +/*********************************************************************** + * Helper functions + **********************************************************************/ +namespace {/*anon*/ + + static const double pi = double(std::acos(-1.0)); + + //! Round a floating-point value to the nearest integer + template <typename T> int iround(T val){ + return (val > 0)? int(val + 0.5) : int(val - 0.5); + } + + //! Pick the closest number that is nice to display + template <typename T> T to_clean_num(const T num){ + if (num == 0) return 0; + const T pow10 = std::pow(T(10), int(std::floor(std::log10(std::abs(num))))); + const T norm = std::abs(num)/pow10; + static const int cleans[] = {1, 2, 5, 10}; + int clean = cleans[0]; + for (size_t i = 1; i < sizeof(cleans)/sizeof(cleans[0]); i++){ + if (std::abs(norm - cleans[i]) < std::abs(norm - clean)) + clean = cleans[i]; + } + return ((num < 0)? -1 : 1)*clean*pow10; + } + + //! Compute an FFT with pre-computed factors using Cooley-Tukey + template <typename T> std::complex<T> ct_fft_f( + const std::complex<T> *samps, size_t nsamps, + const std::complex<T> *factors, + size_t start = 0, size_t step = 1 + ){ + if (nsamps == 1) return samps[start]; + std::complex<T> E_k = ct_fft_f(samps, nsamps/2, factors+1, start, step*2); + std::complex<T> O_k = ct_fft_f(samps, nsamps/2, factors+1, start+step, step*2); + return E_k + factors[0]*O_k; + } + + //! Compute an FFT for a particular bin k using Cooley-Tukey + template <typename T> std::complex<T> ct_fft_k( + const std::complex<T> *samps, size_t nsamps, size_t k + ){ + //pre-compute the factors to use in Cooley-Tukey + std::vector<std::complex<T> > factors; + for (size_t N = nsamps; N != 0; N /= 2){ + factors.push_back(std::exp(std::complex<T>(0, T(-2*pi*k/N)))); + } + return ct_fft_f(samps, nsamps, &factors.front()); + } + + //! Helper class to build a DFT plot frame + class frame_type{ + public: + frame_type(size_t width, size_t height): + _frame(width-1, std::vector<char>(height, ' ')) + { + /* NOP */ + } + + //accessors to parts of the frame + char &get_plot(size_t b, size_t z){return _frame.at(b+albl_w).at(z+flbl_h);} + char &get_albl(size_t b, size_t z){return _frame.at(b) .at(z+flbl_h);} + char &get_ulbl(size_t b) {return _frame.at(b) .at(flbl_h-1);} + char &get_flbl(size_t b) {return _frame.at(b+albl_w).at(flbl_h-1);} + + //dimension accessors + size_t get_plot_h(void) const{return _frame.front().size() - flbl_h;} + size_t get_plot_w(void) const{return _frame.size() - albl_w;} + size_t get_albl_w(void) const{return albl_w;} + + std::string to_string(void){ + std::stringstream frame_ss; + for (size_t z = 0; z < _frame.front().size(); z++){ + for (size_t b = 0; b < _frame.size(); b++){ + frame_ss << _frame[b][_frame[b].size()-z-1]; + } + frame_ss << std::endl; + } + return frame_ss.str(); + } + + private: + static const size_t albl_w = 6, flbl_h = 1; + std::vector<std::vector<char> > _frame; + }; + +} //namespace /*anon*/ + +/*********************************************************************** + * Implementation code + **********************************************************************/ +namespace acsii_art_dft{ + + //! skip constants for amplitude and frequency labels + static const size_t albl_skip = 5, flbl_skip = 20; + + template <typename T> log_pwr_dft_type log_pwr_dft( + const std::complex<T> *samps, size_t nsamps + ){ + if (nsamps & (nsamps - 1)) + throw std::runtime_error("num samps is not a power of 2"); + + //compute the window + double win_pwr = 0; + std::vector<std::complex<T> > win_samps; + for(size_t n = 0; n < nsamps; n++){ + //double w_n = 1; + //double w_n = 0.54 //hamming window + // -0.46*std::cos(2*pi*n/(nsamps-1)) + //; + double w_n = 0.35875 //blackman-harris window + -0.48829*std::cos(2*pi*n/(nsamps-1)) + +0.14128*std::cos(4*pi*n/(nsamps-1)) + -0.01168*std::cos(6*pi*n/(nsamps-1)) + ; + //double w_n = 1 // flat top window + // -1.930*std::cos(2*pi*n/(nsamps-1)) + // +1.290*std::cos(4*pi*n/(nsamps-1)) + // -0.388*std::cos(6*pi*n/(nsamps-1)) + // +0.032*std::cos(8*pi*n/(nsamps-1)) + //; + win_samps.push_back(T(w_n)*samps[n]); + win_pwr += w_n*w_n; + } + + //compute the log-power dft + log_pwr_dft_type log_pwr_dft; + for(size_t k = 0; k < nsamps; k++){ + std::complex<T> dft_k = ct_fft_k(&win_samps.front(), nsamps, k); + log_pwr_dft.push_back(float( + + 20*std::log10(std::abs(dft_k)) + - 20*std::log10(T(nsamps)) + - 10*std::log10(win_pwr/nsamps) + + 3 + )); + } + + return log_pwr_dft; + } + + std::string dft_to_plot( + const log_pwr_dft_type &dft_, + size_t width, + size_t height, + double samp_rate, + double dc_freq, + float dyn_rng, + float ref_lvl + ){ + frame_type frame(width, height); //fill this frame + + //re-order the dft so dc in in the center + const size_t num_bins = dft_.size() - 1 + dft_.size()%2; //make it odd + log_pwr_dft_type dft(num_bins); + for (size_t n = 0; n < num_bins; n++){ + dft[n] = dft_[(n + num_bins/2)%num_bins]; + } + + //fill the plot with dft bins + for (size_t b = 0; b < frame.get_plot_w(); b++){ + //indexes from the dft to grab for the plot + const size_t n_start = std::max(iround(double(b-0.5)*(num_bins-1)/(frame.get_plot_w()-1)), 0); + const size_t n_stop = std::min(iround(double(b+0.5)*(num_bins-1)/(frame.get_plot_w()-1)), int(num_bins)); + + //calculate val as the max across points + float val = dft.at(n_start); + for (size_t n = n_start; n < n_stop; n++) val = std::max(val, dft.at(n)); + + const float scaled = (val - (ref_lvl - dyn_rng))*(frame.get_plot_h()-1)/dyn_rng; + for (size_t z = 0; z < frame.get_plot_h(); z++){ + static const std::string syms(".:!|"); + if (scaled-z > 1) frame.get_plot(b, z) = syms.at(syms.size()-1); + else if (scaled-z > 0) frame.get_plot(b, z) = syms.at(size_t((scaled-z)*syms.size())); + } + } + + //create vertical amplitude labels + const float db_step = to_clean_num(dyn_rng/(frame.get_plot_h()-1)*albl_skip); + for ( + float db = db_step*(int((ref_lvl - dyn_rng)/db_step)); + db <= db_step*(int(ref_lvl/db_step)); + db += db_step + ){ + const int z = iround((db - (ref_lvl - dyn_rng))*(frame.get_plot_h()-1)/dyn_rng); + if (z < 0 or size_t(z) >= frame.get_plot_h()) continue; + std::stringstream ss; ss << db; std::string lbl = ss.str(); + for (size_t i = 0; i < lbl.size() and i < frame.get_albl_w(); i++){ + frame.get_albl(i, z) = lbl[i]; + } + } + + //create vertical units label + std::string ulbl = "dBfs"; + for (size_t i = 0; i < ulbl.size(); i++){ + frame.get_ulbl(i+1) = ulbl[i]; + } + + //create horizontal frequency labels + const double f_step = to_clean_num(samp_rate/frame.get_plot_w()*flbl_skip); + for ( + double freq = f_step*int((-samp_rate/2/f_step)); + freq <= f_step*int((+samp_rate/2/f_step)); + freq += f_step + ){ + const int b = iround((freq + samp_rate/2)*(frame.get_plot_w()-1)/samp_rate); + std::stringstream ss; ss << (freq+dc_freq)/1e6 << "MHz"; std::string lbl = ss.str(); + if (b < int(lbl.size()/2) or b + lbl.size() - lbl.size()/2 >= frame.get_plot_w()) continue; + for (size_t i = 0; i < lbl.size(); i++){ + frame.get_flbl(b + i - lbl.size()/2) = lbl[i]; + } + } + + return frame.to_string(); + } +} //namespace ascii_dft + +#endif /*ASCII_ART_DFT_HPP*/ + +/* + +//example main function to test the dft + +#include <iostream> +#include <cstdlib> +#include <curses.h> + +int main(void){ + initscr(); + + while (true){ + clear(); + + std::vector<std::complex<float> > samples; + for(size_t i = 0; i < 512; i++){ + samples.push_back(std::complex<float>( + float(std::rand() - RAND_MAX/2)/(RAND_MAX)/4, + float(std::rand() - RAND_MAX/2)/(RAND_MAX)/4 + )); + samples[i] += 0.5*std::sin(i*3.14/2) + 0.7; + } + + acsii_art_dft::log_pwr_dft_type dft; + dft = acsii_art_dft::log_pwr_dft(&samples.front(), samples.size()); + + printw("%s", acsii_art_dft::dft_to_plot( + dft, COLS, LINES, + 12.5e4, 2.45e9, + 60, 0 + ).c_str()); + + sleep(1); + } + + + endwin(); + std::cout << "here\n"; + return 0; +} + +*/ + diff --git a/host/examples/benchmark_rx_rate.cpp b/host/examples/benchmark_rx_rate.cpp new file mode 100644 index 000000000..b189368f9 --- /dev/null +++ b/host/examples/benchmark_rx_rate.cpp @@ -0,0 +1,160 @@ +// +// Copyright 2010 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/>. +// + +#include <uhd/utils/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/usrp/single_usrp.hpp> +#include <boost/math/special_functions/round.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <iostream> +#include <complex> + +namespace po = boost::program_options; + +static inline void test_device( + uhd::usrp::single_usrp::sptr sdev, + double rx_rate_sps, + double duration_secs +){ + uhd::device::sptr dev = sdev->get_device(); + std::cout << boost::format("Testing receive rate %f Msps (%f second run)") % (rx_rate_sps/1e6) % duration_secs << std::endl; + + //allocate recv buffer and metatdata + uhd::rx_metadata_t md; + std::vector<std::complex<float> > buff(dev->get_max_recv_samps_per_packet()); + + //flush the buffers in the recv path + while(dev->recv( + &buff.front(), buff.size(), md, + uhd::io_type_t::COMPLEX_FLOAT32, + uhd::device::RECV_MODE_ONE_PACKET + )){ + /* NOP */ + }; + + //declare status variables + bool got_first_packet = false; + size_t total_recv_packets = 0; + size_t total_lost_samples = 0; + size_t total_recv_samples = 0; + uhd::time_spec_t initial_time_spec; + uhd::time_spec_t next_expected_time_spec; + + sdev->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + do { + size_t num_rx_samps = dev->recv( + &buff.front(), buff.size(), md, + uhd::io_type_t::COMPLEX_FLOAT32, + uhd::device::RECV_MODE_ONE_PACKET + ); + + //handle the error codes + switch(md.error_code){ + case uhd::rx_metadata_t::ERROR_CODE_NONE: + case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW: + break; + + default: + std::cerr << "Unexpected error on recv, exit test..." << std::endl; + return; + } + + if (not md.has_time_spec){ + std::cerr << "Metadata missing time spec, exit test..." << std::endl; + return; + } + + total_recv_samples += num_rx_samps; + total_recv_packets++; + + if (not got_first_packet){ + initial_time_spec = md.time_spec; + next_expected_time_spec = initial_time_spec; + got_first_packet = true; + } + + total_lost_samples += boost::math::iround(rx_rate_sps*(md.time_spec - next_expected_time_spec).get_real_secs()); + next_expected_time_spec = md.time_spec + uhd::time_spec_t(0, num_rx_samps, rx_rate_sps); + + } while((next_expected_time_spec - initial_time_spec) < uhd::time_spec_t(duration_secs)); + sdev->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); + + //print a summary + std::cout << std::endl; //go to newline, recv may spew SXSYSZ... + std::cout << boost::format(" Received packets: %d") % total_recv_packets << std::endl; + std::cout << boost::format(" Received samples: %d") % total_recv_samples << std::endl; + std::cout << boost::format(" Lost samples: %d") % total_lost_samples << std::endl; + size_t packets_lost = boost::math::iround(double(total_lost_samples)/dev->get_max_recv_samps_per_packet()); + std::cout << boost::format(" Lost packets: %d (approximate)") % packets_lost << std::endl; + double actual_rx_rate_sps = (total_recv_samples*rx_rate_sps)/(total_recv_samples+total_lost_samples); + std::cout << boost::format(" Sustained receive rate: %f Msps") % (actual_rx_rate_sps/1e6) << std::endl; + std::cout << std::endl << std::endl; +} + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + uhd::set_thread_priority_safe(); + + //variables to be set by po + std::string args; + double duration; + double only_rate; + + //setup the program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args") + ("duration", po::value<double>(&duration)->default_value(10.0), "duration for each test in seconds") + ("rate", po::value<double>(&only_rate), "specify to perform a single test as this rate (sps)") + ; + 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 Benchmark RX Rate %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::single_usrp::sptr sdev = uhd::usrp::single_usrp::make(args); + std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl; + + if (not vm.count("rate")){ + sdev->set_rx_rate(500e3); //initial rate + while(true){ + double rate = sdev->get_rx_rate(); + test_device(sdev, rate, duration); + sdev->set_rx_rate(rate*2); //double the rate + if (sdev->get_rx_rate() == rate) break; + } + } + else{ + sdev->set_rx_rate(only_rate); + double rate = sdev->get_rx_rate(); + test_device(sdev, rate, duration); + } + + //finished + std::cout << std::endl << "Done!" << std::endl << std::endl; + + return 0; +} diff --git a/host/examples/rx_ascii_art_dft.cpp b/host/examples/rx_ascii_art_dft.cpp new file mode 100644 index 000000000..5a24867b4 --- /dev/null +++ b/host/examples/rx_ascii_art_dft.cpp @@ -0,0 +1,143 @@ +// +// Copyright 2010 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/>. +// + +#include <uhd/utils/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/usrp/single_usrp.hpp> +#include "ascii_art_dft.hpp" //implementation +#include <boost/program_options.hpp> +#include <boost/thread.hpp> //gets time +#include <boost/format.hpp> +#include <curses.h> +#include <iostream> +#include <complex> + +namespace po = boost::program_options; + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + uhd::set_thread_priority_safe(); + + //variables to be set by po + std::string args; + size_t num_bins; + double rate, freq, frame_rate; + float gain, ref_lvl, dyn_rng; + + //setup the program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args") + // hardware parameters + ("rate", po::value<double>(&rate), "rate of incoming samples (sps)") + ("freq", po::value<double>(&freq)->default_value(0), "RF center frequency in Hz") + ("gain", po::value<float>(&gain)->default_value(0), "gain for the RF chain") + // display parameters + ("num-bins", po::value<size_t>(&num_bins)->default_value(512), "the number of bins in the DFT") + ("frame-rate", po::value<double>(&frame_rate)->default_value(5), "frame rate of the display (fps)") + ("ref-lvl", po::value<float>(&ref_lvl)->default_value(0), "reference level for the display (dB)") + ("dyn-rng", po::value<float>(&dyn_rng)->default_value(60), "dynamic range for the display (dB)") + ; + 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") or not vm.count("rate")){ + std::cout << boost::format("UHD RX ASCII Art DFT %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::single_usrp::sptr sdev = uhd::usrp::single_usrp::make(args); + std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl; + + //set the rx sample rate + std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate/1e6) << std::endl; + sdev->set_rx_rate(rate); + std::cout << boost::format("Actual RX Rate: %f Msps...") % (sdev->get_rx_rate()/1e6) << std::endl << std::endl; + + //set the rx center frequency + std::cout << boost::format("Setting RX Freq: %f Mhz...") % (freq/1e6) << std::endl; + sdev->set_rx_freq(freq); + std::cout << boost::format("Actual RX Freq: %f Mhz...") % (sdev->get_rx_freq()/1e6) << std::endl << std::endl; + + //set the rx rf gain + std::cout << boost::format("Setting RX Gain: %f dB...") % gain << std::endl; + sdev->set_rx_gain(gain); + std::cout << boost::format("Actual RX Gain: %f dB...") % sdev->get_rx_gain() << std::endl << std::endl; + + //allocate recv buffer and metatdata + uhd::rx_metadata_t md; + std::vector<std::complex<float> > buff(num_bins); + //------------------------------------------------------------------ + //-- Initialize + //------------------------------------------------------------------ + initscr(); //curses init + sdev->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + boost::system_time next_refresh = boost::get_system_time(); + + //------------------------------------------------------------------ + //-- Main loop + //------------------------------------------------------------------ + while (true){ + //read a buffer's worth of samples every iteration + size_t num_rx_samps = sdev->get_device()->recv( + &buff.front(), buff.size(), md, + uhd::io_type_t::COMPLEX_FLOAT32, + uhd::device::RECV_MODE_FULL_BUFF + ); + if (num_rx_samps != buff.size()) continue; + + //check and update the display refresh condition + if (boost::get_system_time() < next_refresh) continue; + next_refresh = boost::get_system_time() + boost::posix_time::microseconds(long(1e6/frame_rate)); + + //calculate the dft and create the ascii art frame + acsii_art_dft::log_pwr_dft_type lpdft( + acsii_art_dft::log_pwr_dft(&buff.front(), num_rx_samps) + ); + std::string frame = acsii_art_dft::dft_to_plot( + lpdft, COLS, LINES, + sdev->get_rx_rate(), + sdev->get_rx_freq(), + dyn_rng, ref_lvl + ); + + //curses screen handling: clear and print frame + clear(); + printw("%s", frame.c_str()); + + //curses key handling: no timeout, any key to exit + timeout(0); + int ch = getch(); + if (ch != KEY_RESIZE and ch != ERR) break; + } + + //------------------------------------------------------------------ + //-- Cleanup + //------------------------------------------------------------------ + sdev->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); + endwin(); //curses done + + //finished + std::cout << std::endl << "Done!" << std::endl << std::endl; + + return 0; +} diff --git a/host/examples/rx_samples_to_file.cpp b/host/examples/rx_samples_to_file.cpp new file mode 100644 index 000000000..c80d2a6de --- /dev/null +++ b/host/examples/rx_samples_to_file.cpp @@ -0,0 +1,135 @@ +// +// Copyright 2010 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/>. +// + +#include <uhd/utils/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/usrp/single_usrp.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <boost/thread.hpp> +#include <iostream> +#include <fstream> +#include <complex> + +namespace po = boost::program_options; + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + uhd::set_thread_priority_safe(); + + //variables to be set by po + std::string args, file; + size_t total_num_samps; + double rate, freq; + float gain; + + //setup the program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args") + ("file", po::value<std::string>(&file)->default_value("out.16sc.dat"), "name of the file to write binary samples to") + ("nsamps", po::value<size_t>(&total_num_samps)->default_value(1000), "total number of samples to receive") + ("rate", po::value<double>(&rate)->default_value(100e6/16), "rate of incoming samples") + ("freq", po::value<double>(&freq)->default_value(0), "rf center frequency in Hz") + ("gain", po::value<float>(&gain)->default_value(0), "gain for the RF chain") + ; + 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 to File %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::single_usrp::sptr sdev = uhd::usrp::single_usrp::make(args); + uhd::device::sptr dev = sdev->get_device(); + std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl; + + //set the rx sample rate + std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate/1e6) << std::endl; + sdev->set_rx_rate(rate); + std::cout << boost::format("Actual RX Rate: %f Msps...") % (sdev->get_rx_rate()/1e6) << std::endl << std::endl; + + //set the rx center frequency + std::cout << boost::format("Setting RX Freq: %f Mhz...") % (freq/1e6) << std::endl; + sdev->set_rx_freq(freq); + std::cout << boost::format("Actual RX Freq: %f Mhz...") % (sdev->get_rx_freq()/1e6) << std::endl << std::endl; + + //set the rx rf gain + std::cout << boost::format("Setting RX Gain: %f dB...") % gain << std::endl; + sdev->set_rx_gain(gain); + std::cout << boost::format("Actual RX Gain: %f dB...") % sdev->get_rx_gain() << std::endl << std::endl; + + boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow for some setup time + std::cout << "LO Locked = " << sdev->get_rx_lo_locked() << std::endl; + + //setup streaming + uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); + stream_cmd.num_samps = total_num_samps; + stream_cmd.stream_now = true; + sdev->issue_stream_cmd(stream_cmd); + + //loop until total number of samples reached + size_t num_acc_samps = 0; //number of accumulated samples + uhd::rx_metadata_t md; + std::vector<std::complex<short> > buff(dev->get_max_recv_samps_per_packet()); + std::ofstream outfile(file.c_str(), std::ofstream::binary); + + while(num_acc_samps < total_num_samps){ + size_t num_rx_samps = dev->recv( + &buff.front(), buff.size(), md, + uhd::io_type_t::COMPLEX_INT16, + uhd::device::RECV_MODE_ONE_PACKET + ); + + //handle the error codes + switch(md.error_code){ + case uhd::rx_metadata_t::ERROR_CODE_NONE: + break; + + case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT: + if (num_acc_samps == 0) continue; + std::cout << boost::format( + "Got timeout before all samples received, possible packet loss, exiting loop..." + ) << std::endl; + goto done_loop; + + default: + std::cout << boost::format( + "Got error code 0x%x, exiting loop..." + ) % md.error_code << std::endl; + goto done_loop; + } + + //write complex short integer samples to the binary file + outfile.write((const char*)&buff[0], num_rx_samps * sizeof(std::complex<short>)); + + num_acc_samps += num_rx_samps; + } done_loop: + + outfile.close(); + + //finished + std::cout << std::endl << "Done!" << std::endl << std::endl; + + return 0; +} diff --git a/host/examples/rx_samples_to_udp.cpp b/host/examples/rx_samples_to_udp.cpp new file mode 100644 index 000000000..488c95494 --- /dev/null +++ b/host/examples/rx_samples_to_udp.cpp @@ -0,0 +1,135 @@ +// +// Copyright 2010 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/>. +// + +#include <uhd/utils/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/usrp/single_usrp.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <boost/thread.hpp> +#include <iostream> +#include <complex> + +namespace po = boost::program_options; + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + uhd::set_thread_priority_safe(); + + //variables to be set by po + std::string args; + size_t total_num_samps; + double rate, freq; + float gain; + std::string addr, port; + + //setup the program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args") + ("nsamps", po::value<size_t>(&total_num_samps)->default_value(1000), "total number of samples to receive") + ("rate", po::value<double>(&rate)->default_value(100e6/16), "rate of incoming samples") + ("freq", po::value<double>(&freq)->default_value(0), "rf center frequency in Hz") + ("gain", po::value<float>(&gain)->default_value(0), "gain for the RF chain") + ("port", po::value<std::string>(&port)->default_value("7124"), "server udp port") + ("addr", po::value<std::string>(&addr)->default_value("192.168.1.10"), "resolvable server address") + ; + 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 to UDP %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::single_usrp::sptr sdev = uhd::usrp::single_usrp::make(args); + uhd::device::sptr dev = sdev->get_device(); + std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl; + + //set the rx sample rate + std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate/1e6) << std::endl; + sdev->set_rx_rate(rate); + std::cout << boost::format("Actual RX Rate: %f Msps...") % (sdev->get_rx_rate()/1e6) << std::endl << std::endl; + + //set the rx center frequency + std::cout << boost::format("Setting RX Freq: %f Mhz...") % (freq/1e6) << std::endl; + sdev->set_rx_freq(freq); + std::cout << boost::format("Actual RX Freq: %f Mhz...") % (sdev->get_rx_freq()/1e6) << std::endl << std::endl; + + //set the rx rf gain + std::cout << boost::format("Setting RX Gain: %f dB...") % gain << std::endl; + sdev->set_rx_gain(gain); + std::cout << boost::format("Actual RX Gain: %f dB...") % sdev->get_rx_gain() << std::endl << std::endl; + + boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow for some setup time + std::cout << "LO Locked = " << sdev->get_rx_lo_locked() << std::endl; + + //setup streaming + uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); + stream_cmd.num_samps = total_num_samps; + stream_cmd.stream_now = true; + sdev->issue_stream_cmd(stream_cmd); + + //loop until total number of samples reached + size_t num_acc_samps = 0; //number of accumulated samples + uhd::rx_metadata_t md; + std::vector<std::complex<float> > buff(dev->get_max_recv_samps_per_packet()); + uhd::transport::udp_simple::sptr udp_xport = uhd::transport::udp_simple::make_connected(addr, port); + + while(num_acc_samps < total_num_samps){ + size_t num_rx_samps = dev->recv( + &buff.front(), buff.size(), md, + uhd::io_type_t::COMPLEX_FLOAT32, + uhd::device::RECV_MODE_ONE_PACKET + ); + + //handle the error codes + switch(md.error_code){ + case uhd::rx_metadata_t::ERROR_CODE_NONE: + break; + + case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT: + if (num_acc_samps == 0) continue; + std::cout << boost::format( + "Got timeout before all samples received, possible packet loss, exiting loop..." + ) << std::endl; + goto done_loop; + + default: + std::cout << boost::format( + "Got error code 0x%x, exiting loop..." + ) % md.error_code << std::endl; + goto done_loop; + } + + //send complex single precision floating point samples over udp + udp_xport->send(boost::asio::buffer(buff, num_rx_samps)); + + num_acc_samps += num_rx_samps; + } done_loop: + + //finished + std::cout << std::endl << "Done!" << std::endl << std::endl; + + return 0; +} diff --git a/host/examples/rx_timed_samples.cpp b/host/examples/rx_timed_samples.cpp new file mode 100644 index 000000000..8a810811f --- /dev/null +++ b/host/examples/rx_timed_samples.cpp @@ -0,0 +1,132 @@ +// +// Copyright 2010 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/>. +// + +#include <uhd/utils/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/usrp/single_usrp.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <iostream> +#include <complex> + +namespace po = boost::program_options; + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + uhd::set_thread_priority_safe(); + + //variables to be set by po + std::string args; + time_t seconds_in_future; + size_t total_num_samps; + double rate, freq; + + //setup the program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args") + ("secs", po::value<time_t>(&seconds_in_future)->default_value(3), "number of seconds in the future to receive") + ("nsamps", po::value<size_t>(&total_num_samps)->default_value(1000), "total number of samples to receive") + ("rate", po::value<double>(&rate)->default_value(100e6/16), "rate of incoming samples") + ("freq", po::value<double>(&freq)->default_value(0), "rf center frequency in Hz") + ("dilv", "specify to disable inner-loop verbose") + ; + 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 Timed Samples %s") % desc << std::endl; + return ~0; + } + + bool verbose = vm.count("dilv") == 0; + + //create a usrp device + std::cout << std::endl; + std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; + uhd::usrp::single_usrp::sptr sdev = uhd::usrp::single_usrp::make(args); + uhd::device::sptr dev = sdev->get_device(); + std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl; + + //set the rx sample rate + std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate/1e6) << std::endl; + sdev->set_rx_rate(rate); + std::cout << boost::format("Actual RX Rate: %f Msps...") % (sdev->get_rx_rate()/1e6) << std::endl << std::endl; + + //set the rx center frequency + std::cout << boost::format("Setting RX Freq: %f Mhz...") % (freq/1e6) << std::endl; + sdev->set_rx_freq(freq); + std::cout << boost::format("Actual RX Freq: %f Mhz...") % (sdev->get_rx_freq()/1e6) << std::endl << std::endl; + + std::cout << boost::format("Setting device timestamp to 0...") << std::endl; + sdev->set_time_now(uhd::time_spec_t(0.0)); + + //setup streaming + std::cout << std::endl; + std::cout << boost::format( + "Begin streaming %u samples, %d seconds in the future..." + ) % total_num_samps % seconds_in_future << std::endl; + uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); + stream_cmd.num_samps = total_num_samps; + stream_cmd.stream_now = false; + stream_cmd.time_spec = uhd::time_spec_t(seconds_in_future); + sdev->issue_stream_cmd(stream_cmd); + + //loop until total number of samples reached + size_t num_acc_samps = 0; //number of accumulated samples + while(num_acc_samps < total_num_samps){ + uhd::rx_metadata_t md; + std::vector<std::complex<float> > buff(dev->get_max_recv_samps_per_packet()); + size_t num_rx_samps = dev->recv( + &buff.front(), buff.size(), md, + uhd::io_type_t::COMPLEX_FLOAT32, + uhd::device::RECV_MODE_ONE_PACKET + ); + + //handle the error codes + switch(md.error_code){ + case uhd::rx_metadata_t::ERROR_CODE_NONE: + break; + + case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT: + if (num_acc_samps == 0) continue; + std::cout << boost::format( + "Got timeout before all samples received, possible packet loss, exiting loop..." + ) << std::endl; + goto done_loop; + + default: + std::cout << boost::format( + "Got error code 0x%x, exiting loop..." + ) % md.error_code << std::endl; + goto done_loop; + } + + if(verbose) std::cout << boost::format( + "Got packet: %u samples, %u full secs, %f frac secs" + ) % num_rx_samps % md.time_spec.get_full_secs() % md.time_spec.get_frac_secs() << std::endl; + + num_acc_samps += num_rx_samps; + } done_loop: + + //finished + std::cout << std::endl << "Done!" << std::endl << std::endl; + + return 0; +} diff --git a/host/examples/test_async_messages.cpp b/host/examples/test_async_messages.cpp new file mode 100644 index 000000000..b1d9d56d4 --- /dev/null +++ b/host/examples/test_async_messages.cpp @@ -0,0 +1,260 @@ +// +// Copyright 2010 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/>. +// + +#include <uhd/utils/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/utils/static.hpp> +#include <uhd/usrp/single_usrp.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/program_options.hpp> +#include <boost/foreach.hpp> +#include <boost/bind.hpp> +#include <boost/format.hpp> +#include <cstdlib> +#include <complex> +#include <iostream> + +namespace po = boost::program_options; + +/*! + * 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::single_usrp::sptr sdev){ + uhd::device::sptr dev = sdev->get_device(); + 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; + + //3 times max-sps guarantees a SOB, no burst, and EOB packet + std::vector<std::complex<float> > buff(dev->get_max_send_samps_per_packet()*3); + + dev->send( + &buff.front(), buff.size(), md, + uhd::io_type_t::COMPLEX_FLOAT32, + uhd::device::SEND_MODE_FULL_BUFF + ); + + uhd::async_metadata_t async_md; + if (not dev->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_BURST_ACK: + std::cout << boost::format( + "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::single_usrp::sptr sdev){ + uhd::device::sptr dev = sdev->get_device(); + 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; + + dev->send( + NULL, 0, md, + uhd::io_type_t::COMPLEX_FLOAT32, + uhd::device::SEND_MODE_FULL_BUFF + ); + + uhd::async_metadata_t async_md; + if (not dev->recv_async_msg(async_md, 1)){ + 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_UNDERFLOW: + std::cout << boost::format( + "success:\n" + " Got event code underflow 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 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::single_usrp::sptr sdev){ + uhd::device::sptr dev = sdev->get_device(); + 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 + + sdev->set_time_now(uhd::time_spec_t(200.0)); //time at 200s + + dev->send( + NULL, 0, md, + uhd::io_type_t::COMPLEX_FLOAT32, + uhd::device::SEND_MODE_FULL_BUFF + ); + + uhd::async_metadata_t async_md; + if (not dev->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_md(uhd::usrp::single_usrp::sptr sdev){ + uhd::device::sptr dev = sdev->get_device(); + uhd::async_metadata_t async_md; + while (dev->recv_async_msg(async_md, 1.0)){} +} + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + uhd::set_thread_priority_safe(); + + //variables to be set by po + std::string args; + double rate; + size_t ntests; + + //setup the program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args") + ("rate", po::value<double>(&rate)->default_value(1.5e6), "rate of outgoing samples") + ("ntests", po::value<size_t>(&ntests)->default_value(10), "number of tests to run") + ; + 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 Async 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::single_usrp::sptr sdev = uhd::usrp::single_usrp::make(args); + std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl; + + //set the tx sample rate + std::cout << boost::format("Setting TX Rate: %f Msps...") % (rate/1e6) << std::endl; + sdev->set_tx_rate(rate); + std::cout << boost::format("Actual TX Rate: %f Msps...") % (sdev->get_tx_rate()/1e6) << std::endl << std::endl; + + //------------------------------------------------------------------ + // begin asyc messages test + //------------------------------------------------------------------ + static const uhd::dict<std::string, boost::function<bool(uhd::usrp::single_usrp::sptr)> > + tests = boost::assign::map_list_of + ("Test Burst ACK ", &test_burst_ack_message) + ("Test Underflow ", &test_underflow_message) + ("Test Time Error", &test_time_error_message) + ; + + //init result counts + uhd::dict<std::string, size_t> failures, successes; + BOOST_FOREACH(const std::string &key, tests.keys()){ + failures[key] = 0; + successes[key] = 0; + } + + //run the tests, pick at random + for (size_t n = 0; n < ntests; n++){ + std::string key = tests.keys()[std::rand() % tests.size()]; + bool pass = tests[key](sdev); + flush_async_md(sdev); + + //store result + if (pass) successes[key]++; + else failures[key]++; + } + + //print the result summary + std::cout << std::endl << "Summary:" << std::endl << std::endl; + BOOST_FOREACH(const std::string &key, tests.keys()){ + std::cout << boost::format( + "%s -> %3d successes, %3d failures" + ) % key % successes[key] % failures[key] << std::endl; + } + + //finished + std::cout << std::endl << "Done!" << std::endl << std::endl; + + return 0; +} diff --git a/host/examples/test_pps_input.cpp b/host/examples/test_pps_input.cpp new file mode 100644 index 000000000..4b2bb62a3 --- /dev/null +++ b/host/examples/test_pps_input.cpp @@ -0,0 +1,86 @@ +// +// Copyright 2010 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/>. +// + +#include <uhd/utils/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/usrp/single_usrp.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <boost/thread.hpp> +#include <iostream> +#include <complex> + +namespace po = boost::program_options; + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + uhd::set_thread_priority_safe(); + + //variables to be set by po + std::string args; + double seconds; + + //setup the program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args") + ; + 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 PPS Input %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::single_usrp::sptr sdev = uhd::usrp::single_usrp::make(args); + uhd::device::sptr dev = sdev->get_device(); + std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl; + + //set a known time value + std::cout << "Set time to known value (100.0) without regard to pps:" << std::endl; + sdev->set_time_now(uhd::time_spec_t(100.0)); + boost::this_thread::sleep(boost::posix_time::seconds(1)); + std::cout << boost::format("Reading time 1 second later: %f\n") % (sdev->get_time_now().get_real_secs()) << std::endl; + + //store the time to see if PPS resets it + seconds = sdev->get_time_now().get_real_secs(); + + //set a known time at next PPS, check that time increments + uhd::time_spec_t time_spec = uhd::time_spec_t(0.0); + std::cout << "Set time to known value (0.0) at next pps:" << std::endl; + sdev->set_time_next_pps(time_spec); + boost::this_thread::sleep(boost::posix_time::seconds(1)); + std::cout << boost::format("Reading time 1 second later: %f\n") % (sdev->get_time_now().get_real_secs()) << std::endl; + + //finished + if (seconds > sdev->get_time_now().get_real_secs()){ + std::cout << std::endl << "Success!" << std::endl << std::endl; + return 0; + } else { + std::cout << std::endl << "Failed!" << std::endl << std::endl + << "If you expected PPS to work:" << std::endl + << "\tsee Device App Notes for PPS level information" + << std::endl << std::endl; + return -1; + } +} diff --git a/host/examples/tx_from_file.cpp b/host/examples/tx_from_file.cpp new file mode 100644 index 000000000..9611cf47c --- /dev/null +++ b/host/examples/tx_from_file.cpp @@ -0,0 +1,125 @@ +// +// Copyright 2010 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/>. +// + +#include <uhd/utils/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/usrp/simple_usrp.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <iostream> +#include <complex> +#include <fstream> + +namespace po = boost::program_options; + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + uhd::set_thread_priority_safe(); + + //variables to be set by po + std::string args; + time_t seconds_in_future; + size_t total_num_samps; + size_t samps_per_packet; + double tx_rate, freq; + float ampl; + float tx_gain; + + //setup the program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "simple uhd device address args") + ("secs", po::value<time_t>(&seconds_in_future)->default_value(3), "number of seconds in the future to transmit") + ("nsamps", po::value<size_t>(&total_num_samps)->default_value(1000), "total number of samples to transmit") + ("txrate", po::value<double>(&tx_rate)->default_value(100e6/16), "rate of outgoing samples") + ("freq", po::value<double>(&freq)->default_value(0), "rf center frequency in Hz") + ("ampl", po::value<float>(&l)->default_value(float(0.3)), "amplitude of each sample") + ("gain", po::value<float>(&tx_gain)->default_value(float(0)), "amplitude of each sample") + ("dilv", "specify to disable inner-loop verbose") + ; + 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 TX Timed Samples %s") % desc << std::endl; + return ~0; + } + + bool verbose = vm.count("dilv") == 0; + + //create a usrp device + std::cout << std::endl; + std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; + uhd::usrp::simple_usrp::sptr sdev = uhd::usrp::simple_usrp::make(args); + uhd::device::sptr dev = sdev->get_device(); + std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl; + + //set properties on the device + std::cout << boost::format("Setting TX Rate: %f Msps...") % (tx_rate/1e6) << std::endl; + sdev->set_tx_rate(tx_rate); + std::cout << boost::format("Actual TX Rate: %f Msps...") % (sdev->get_tx_rate()/1e6) << std::endl; + std::cout << boost::format("Setting device timestamp to 0...") << std::endl; + sdev->set_tx_freq(freq); + sdev->set_time_now(uhd::time_spec_t(0.0)); + + sdev->set_tx_gain(tx_gain); + + //allocate data to send + std::vector<std::complex<short> > buff; + uhd::tx_metadata_t md; + + std::cout << "Read data to send from file: in.dat" << std::endl; + std::ifstream infile("in.dat", std::ifstream::binary); + while (!infile.eof()) { + std::complex<short> c; + infile.read((char *)&c, sizeof(std::complex<short>)); + if (!((c.real() == 0) && (c.imag() == 0))) { + buff.push_back(c); +//std::cout << "C = " << c << std::endl; + } + } + samps_per_packet = buff.size(); + infile.close(); + std::cout << "Number of samples in file: " << samps_per_packet << std::endl; + + //send the data in multiple packets + size_t num_packets = (total_num_samps+samps_per_packet-1)/samps_per_packet; + for (size_t i = 0; i < num_packets; i++){ + //setup the metadata flags and time spec + md.start_of_burst = true; //always SOB (good for continuous streaming) + md.end_of_burst = (i == num_packets-1); //only last packet has EOB + md.has_time_spec = (i == 0); //only first packet has time + md.time_spec = uhd::time_spec_t(seconds_in_future); + + size_t samps_to_send = std::min(total_num_samps - samps_per_packet*i, samps_per_packet); + + //send the entire packet (driver fragments internally) + size_t num_tx_samps = dev->send( + &buff.front(), samps_to_send, md, + uhd::io_type_t::COMPLEX_INT16, + uhd::device::SEND_MODE_FULL_BUFF + ); + if(verbose) std::cout << std::endl << boost::format("Sent %d samples") % num_tx_samps << std::endl; + } + + //finished + std::cout << std::endl << "Done!" << std::endl << std::endl; + + return 0; +} diff --git a/host/examples/tx_timed_samples.cpp b/host/examples/tx_timed_samples.cpp new file mode 100644 index 000000000..799da37e0 --- /dev/null +++ b/host/examples/tx_timed_samples.cpp @@ -0,0 +1,115 @@ +// +// Copyright 2010 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/>. +// + +#include <uhd/utils/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/usrp/single_usrp.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <iostream> +#include <complex> + +namespace po = boost::program_options; + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + uhd::set_thread_priority_safe(); + + //variables to be set by po + std::string args; + time_t seconds_in_future; + size_t total_num_samps; + size_t samps_per_packet; + double rate, freq; + float ampl; + + //setup the program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args") + ("secs", po::value<time_t>(&seconds_in_future)->default_value(3), "number of seconds in the future to transmit") + ("nsamps", po::value<size_t>(&total_num_samps)->default_value(1000), "total number of samples to transmit") + ("spp", po::value<size_t>(&samps_per_packet)->default_value(1000), "number of samples per packet") + ("rate", po::value<double>(&rate)->default_value(100e6/16), "rate of outgoing samples") + ("freq", po::value<double>(&freq)->default_value(0), "rf center frequency in Hz") + ("ampl", po::value<float>(&l)->default_value(float(0.3)), "amplitude of each sample") + ("dilv", "specify to disable inner-loop verbose") + ; + 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 TX Timed Samples %s") % desc << std::endl; + return ~0; + } + + bool verbose = vm.count("dilv") == 0; + + //create a usrp device + std::cout << std::endl; + std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; + uhd::usrp::single_usrp::sptr sdev = uhd::usrp::single_usrp::make(args); + uhd::device::sptr dev = sdev->get_device(); + std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl; + + //set the tx sample rate + std::cout << boost::format("Setting TX Rate: %f Msps...") % (rate/1e6) << std::endl; + sdev->set_tx_rate(rate); + std::cout << boost::format("Actual TX Rate: %f Msps...") % (sdev->get_tx_rate()/1e6) << std::endl << std::endl; + + //set the tx center frequency + std::cout << boost::format("Setting TX Freq: %f Mhz...") % (freq/1e6) << std::endl; + sdev->set_tx_freq(freq); + std::cout << boost::format("Actual TX Freq: %f Mhz...") % (sdev->get_tx_freq()/1e6) << std::endl << std::endl; + + std::cout << boost::format("Setting device timestamp to 0...") << std::endl; + sdev->set_time_now(uhd::time_spec_t(0.0)); + + //allocate data to send + std::vector<std::complex<float> > buff(samps_per_packet, std::complex<float>(ampl, ampl)); + + //send the data in multiple packets + size_t num_packets = (total_num_samps+samps_per_packet-1)/samps_per_packet; + for (size_t i = 0; i < num_packets; i++){ + //setup the metadata flags and time spec + uhd::tx_metadata_t md; + md.start_of_burst = true; //always SOB (good for continuous streaming) + md.end_of_burst = (i == num_packets-1); //only last packet has EOB + md.has_time_spec = (i == 0); //only first packet has time + md.time_spec = uhd::time_spec_t(seconds_in_future); + + size_t samps_to_send = std::min(total_num_samps - samps_per_packet*i, samps_per_packet); + + //send the entire packet (driver fragments internally) + size_t num_tx_samps = dev->send( + &buff.front(), samps_to_send, md, + uhd::io_type_t::COMPLEX_FLOAT32, + uhd::device::SEND_MODE_FULL_BUFF, + //send will backup into the host this many seconds before sending: + seconds_in_future + 0.1 //timeout (delay before transmit + padding) + ); + if (num_tx_samps < samps_to_send) std::cout << "Send timeout..." << std::endl; + if(verbose) std::cout << std::endl << boost::format("Sent %d samples") % num_tx_samps << std::endl; + } + + //finished + std::cout << std::endl << "Done!" << std::endl << std::endl; + + return 0; +} diff --git a/host/examples/tx_waveforms.cpp b/host/examples/tx_waveforms.cpp new file mode 100644 index 000000000..50982cf88 --- /dev/null +++ b/host/examples/tx_waveforms.cpp @@ -0,0 +1,184 @@ +// +// Copyright 2010 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/>. +// + +#include <uhd/utils/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/utils/static.hpp> +#include <uhd/usrp/single_usrp.hpp> +#include <boost/program_options.hpp> +#include <boost/thread/thread_time.hpp> //system time +#include <boost/math/special_functions/round.hpp> +#include <boost/format.hpp> +#include <boost/function.hpp> +#include <iostream> +#include <complex> +#include <cmath> + +namespace po = boost::program_options; + +/*********************************************************************** + * Waveform generators + **********************************************************************/ +float gen_const(float){ + return 1; +} + +float gen_square(float x){ + return float((std::fmod(x, 1) < float(0.5))? 0 : 1); +} + +float gen_ramp(float x){ + return std::fmod(x, 1)*2 - 1; +} + +#define sine_table_len 2048 +static float sine_table[sine_table_len]; +UHD_STATIC_BLOCK(gen_sine_table){ + static const float m_pi = std::acos(float(-1)); + for (size_t i = 0; i < sine_table_len; i++) + sine_table[i] = std::sin((2*m_pi*i)/sine_table_len); +} + +float gen_sine(float x){ + return sine_table[size_t(x*sine_table_len)%sine_table_len]; +} + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + uhd::set_thread_priority_safe(); + + //variables to be set by po + std::string args, wave_type; + size_t total_duration, spb; + double rate, freq, wave_freq; + float ampl, gain; + + //setup the program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args") + ("duration", po::value<size_t>(&total_duration)->default_value(3), "number of seconds to transmit") + ("spb", po::value<size_t>(&spb)->default_value(10000), "samples per buffer") + ("rate", po::value<double>(&rate)->default_value(1.5e6), "rate of outgoing samples") + ("freq", po::value<double>(&freq)->default_value(0), "rf center frequency in Hz") + ("ampl", po::value<float>(&l)->default_value(float(0.3)), "amplitude of the waveform") + ("gain", po::value<float>(&gain)->default_value(float(0)), "gain for the RF chain") + ("wave-type", po::value<std::string>(&wave_type)->default_value("CONST"), "waveform type (CONST, SQUARE, RAMP, SINE)") + ("wave-freq", po::value<double>(&wave_freq)->default_value(0), "waveform frequency in Hz") + ; + 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 TX Waveforms %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::single_usrp::sptr sdev = uhd::usrp::single_usrp::make(args); + uhd::device::sptr dev = sdev->get_device(); + std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl; + + //set the tx sample rate + std::cout << boost::format("Setting TX Rate: %f Msps...") % (rate/1e6) << std::endl; + sdev->set_tx_rate(rate); + std::cout << boost::format("Actual TX Rate: %f Msps...") % (sdev->get_tx_rate()/1e6) << std::endl << std::endl; + + //set the tx center frequency + std::cout << boost::format("Setting TX Freq: %f Mhz...") % (freq/1e6) << std::endl; + sdev->set_tx_freq(freq); + std::cout << boost::format("Actual TX Freq: %f Mhz...") % (sdev->get_tx_freq()/1e6) << std::endl << std::endl; + + //set the tx rf gain + std::cout << boost::format("Setting TX Gain: %f dB...") % gain << std::endl; + sdev->set_tx_gain(gain); + std::cout << boost::format("Actual TX Gain: %f dB...") % sdev->get_tx_gain() << std::endl << std::endl; + + //for the const wave, set the wave freq for small samples per period + if (wave_freq == 0 and wave_type == "CONST"){ + wave_freq = sdev->get_tx_rate()/2; + } + + //error when the waveform is not possible to generate + if (std::abs(wave_freq) > sdev->get_tx_rate()/2){ + throw std::runtime_error("wave freq out of Nyquist zone"); + } + if (sdev->get_tx_rate()/std::abs(wave_freq) > sine_table_len/2 and wave_type == "SINE"){ + throw std::runtime_error("sine freq too small for table"); + } + + //store the generator function for the selected waveform + boost::function<float(float)> wave_gen; + if (wave_type == "CONST") wave_gen = &gen_const; + else if (wave_type == "SQUARE") wave_gen = &gen_square; + else if (wave_type == "RAMP") wave_gen = &gen_ramp; + else if (wave_type == "SINE") wave_gen = &gen_sine; + else throw std::runtime_error("unknown waveform type: " + wave_type); + + //allocate the buffer and precalculate values + std::vector<std::complex<float> > buff(spb); + const float cps = float(wave_freq/sdev->get_tx_rate()); + const float i_off = (wave_freq > 0)? float(0.25) : 0; + const float q_off = (wave_freq < 0)? float(0.25) : 0; + float theta = 0; + + //setup the metadata flags + uhd::tx_metadata_t md; + md.start_of_burst = true; //always SOB (good for continuous streaming) + md.end_of_burst = false; + + //send the data in multiple packets + boost::system_time end_time(boost::get_system_time() + boost::posix_time::seconds(total_duration)); + while(end_time > boost::get_system_time()){ + + //fill the buffer with the waveform + for (size_t n = 0; n < buff.size(); n++){ + buff[n] = std::complex<float>( + ampl*wave_gen(i_off + theta), + ampl*wave_gen(q_off + theta) + ); + theta += cps; + } + + //bring the theta back into range [0, 1) + theta = std::fmod(theta, 1); + + //send the entire contents of the buffer + dev->send( + &buff.front(), buff.size(), md, + uhd::io_type_t::COMPLEX_FLOAT32, + uhd::device::SEND_MODE_FULL_BUFF + ); + } + + //send a mini EOB packet + md.start_of_burst = false; + md.end_of_burst = true; + dev->send(NULL, 0, md, + uhd::io_type_t::COMPLEX_FLOAT32, + uhd::device::SEND_MODE_FULL_BUFF + ); + + //finished + std::cout << std::endl << "Done!" << std::endl << std::endl; + + return 0; +} |