diff options
Diffstat (limited to 'host/examples')
| -rw-r--r-- | host/examples/CMakeLists.txt | 55 | ||||
| -rw-r--r-- | host/examples/ascii_art_dft.hpp | 320 | ||||
| -rw-r--r-- | host/examples/benchmark_rate.cpp | 243 | ||||
| -rw-r--r-- | host/examples/latency_test.cpp | 168 | ||||
| -rw-r--r-- | host/examples/network_relay.cpp | 227 | ||||
| -rw-r--r-- | host/examples/rx_ascii_art_dft.cpp | 198 | ||||
| -rw-r--r-- | host/examples/rx_multi_samples.cpp | 178 | ||||
| -rw-r--r-- | host/examples/rx_samples_to_file.cpp | 218 | ||||
| -rw-r--r-- | host/examples/rx_samples_to_udp.cpp | 172 | ||||
| -rw-r--r-- | host/examples/rx_timed_samples.cpp | 128 | ||||
| -rw-r--r-- | host/examples/test_messages.cpp | 354 | ||||
| -rw-r--r-- | host/examples/test_pps_input.cpp | 62 | ||||
| -rw-r--r-- | host/examples/tx_bursts.cpp | 164 | ||||
| -rw-r--r-- | host/examples/tx_samples_from_file.cpp | 174 | ||||
| -rw-r--r-- | host/examples/tx_timed_samples.cpp | 128 | ||||
| -rw-r--r-- | host/examples/tx_waveforms.cpp | 261 | 
16 files changed, 3050 insertions, 0 deletions
| diff --git a/host/examples/CMakeLists.txt b/host/examples/CMakeLists.txt new file mode 100644 index 000000000..3c9a3880a --- /dev/null +++ b/host/examples/CMakeLists.txt @@ -0,0 +1,55 @@ +# +# Copyright 2010-2011 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_rate.cpp +    network_relay.cpp +    rx_multi_samples.cpp +    rx_samples_to_file.cpp +    rx_samples_to_udp.cpp +    rx_timed_samples.cpp +    test_messages.cpp +    test_pps_input.cpp +    tx_bursts.cpp +    tx_samples_from_file.cpp +    tx_timed_samples.cpp +    tx_waveforms.cpp +    latency_test.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_LIB_DIR}/examples COMPONENT examples) +ENDFOREACH(example_source) + +######################################################################## +# ASCII Art DFT - requires curses, so this part is optional +######################################################################## +FIND_PACKAGE(Curses) + +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_LIB_DIR}/examples COMPONENT 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_rate.cpp b/host/examples/benchmark_rate.cpp new file mode 100644 index 000000000..8f00e25de --- /dev/null +++ b/host/examples/benchmark_rate.cpp @@ -0,0 +1,243 @@ +// +// Copyright 2011 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/multi_usrp.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <boost/thread/thread.hpp> +#include <boost/math/special_functions/round.hpp> +#include <iostream> +#include <complex> + +namespace po = boost::program_options; + +/*********************************************************************** + * Test result variables + **********************************************************************/ +unsigned long long num_overflows = 0; +unsigned long long num_underflows = 0; +unsigned long long num_rx_samps = 0; +unsigned long long num_tx_samps = 0; +unsigned long long num_dropped_samps = 0; +unsigned long long num_seq_errors = 0; + +/*********************************************************************** + * Benchmark RX Rate + **********************************************************************/ +void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_otw){ +    uhd::set_thread_priority_safe(); + +    //create a receive streamer +    uhd::stream_args_t stream_args("fc32", rx_otw); //complex floats +    uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + +    //print pre-test summary +    std::cout << boost::format( +        "Testing receive rate %f Msps" +    ) % (usrp->get_rx_rate()/1e6) << std::endl; + +    //setup variables and allocate buffer +    uhd::rx_metadata_t md; +    const size_t max_samps_per_packet = rx_stream->get_max_num_samps(); +    std::vector<std::complex<float> > buff(max_samps_per_packet); +    bool had_an_overflow = false; +    uhd::time_spec_t last_time; +    const double rate = usrp->get_rx_rate(); + +    usrp->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); +    while (not boost::this_thread::interruption_requested()){ +        num_rx_samps += rx_stream->recv( +            &buff.front(), buff.size(), md +        ); + +        //handle the error codes +        switch(md.error_code){ +        case uhd::rx_metadata_t::ERROR_CODE_NONE: +            if (had_an_overflow){ +                had_an_overflow = false; +                num_dropped_samps += boost::math::iround((md.time_spec - last_time).get_real_secs()*rate); +            } +            break; + +        case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW: +            had_an_overflow = true; +            last_time = md.time_spec; +            num_overflows++; +            break; + +        default: +            std::cerr << "Error code: " << md.error_code << std::endl; +            std::cerr << "Unexpected error on recv, continuing..." << std::endl; +            break; +        } + +    } +    usrp->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); +} + +/*********************************************************************** + * Benchmark TX Rate + **********************************************************************/ +void benchmark_tx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &tx_otw){ +    uhd::set_thread_priority_safe(); + +    //create a transmit streamer +    uhd::stream_args_t stream_args("fc32", tx_otw); //complex floats +    uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); + +    //print pre-test summary +    std::cout << boost::format( +        "Testing transmit rate %f Msps" +    ) % (usrp->get_tx_rate()/1e6) << std::endl; + +    //setup variables and allocate buffer +    uhd::tx_metadata_t md; +    md.has_time_spec = false; +    const size_t max_samps_per_packet = tx_stream->get_max_num_samps(); +    std::vector<std::complex<float> > buff(max_samps_per_packet); + +    while (not boost::this_thread::interruption_requested()){ +        num_tx_samps += tx_stream->send(&buff.front(), buff.size(), md); +    } + +    //send a mini EOB packet +    md.end_of_burst = true; +    tx_stream->send("", 0, md); +} + +void benchmark_tx_rate_async_helper(uhd::usrp::multi_usrp::sptr usrp){ +    //setup variables and allocate buffer +    uhd::async_metadata_t async_md; + +    while (not boost::this_thread::interruption_requested()){ + +        if (not usrp->get_device()->recv_async_msg(async_md)) continue; + +        //handle the error codes +        switch(async_md.event_code){ +        case uhd::async_metadata_t::EVENT_CODE_BURST_ACK: +            return; + +        case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW: +        case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET: +            num_underflows++; +            break; + +        case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR: +        case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST: +            num_seq_errors++; +            break; + +        default: +            std::cerr << "Event code: " << async_md.event_code << std::endl; +            std::cerr << "Unexpected event on async recv, continuing..." << std::endl; +            break; +        } +    } +} + +/*********************************************************************** + * Main code + dispatcher + **********************************************************************/ +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 rx_rate, tx_rate; +    std::string rx_otw, tx_otw; + +    //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 the test in seconds") +        ("rx_rate", po::value<double>(&rx_rate), "specify to perform a RX rate test (sps)") +        ("tx_rate", po::value<double>(&tx_rate), "specify to perform a TX rate test (sps)") +        ("rx_otw", po::value<std::string>(&rx_otw)->default_value("sc16"), "specify the over-the-wire sample mode for RX") +        ("tx_otw", po::value<std::string>(&tx_otw)->default_value("sc16"), "specify the over-the-wire sample mode for TX") +    ; +    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 (vm.count("rx_rate") + vm.count("tx_rate")) == 0){ +        std::cout << boost::format("UHD Benchmark Rate %s") % desc << std::endl; +        std::cout << +        "    Specify --rx_rate for a receive-only test.\n" +        "    Specify --tx_rate for a transmit-only test.\n" +        "    Specify both options for a full-duplex test.\n" +        << std::endl; +        return ~0; +    } + +    //create a usrp device +    std::cout << std::endl; +    uhd::device_addrs_t device_addrs = uhd::device::find(args); +    if (not device_addrs.empty() and device_addrs.at(0).get("type", "") == "usrp1"){ +        std::cerr << "*** Warning! ***" << std::endl; +        std::cerr << "Benchmark results will be inaccurate on USRP1 due to insufficient features.\n" << 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); +    std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; + +    boost::thread_group thread_group; + +    //spawn the receive test thread +    if (vm.count("rx_rate")){ +        usrp->set_rx_rate(rx_rate); +        thread_group.create_thread(boost::bind(&benchmark_rx_rate, usrp, rx_otw)); +    } + +    //spawn the transmit test thread +    if (vm.count("tx_rate")){ +        usrp->set_tx_rate(tx_rate); +        thread_group.create_thread(boost::bind(&benchmark_tx_rate, usrp, tx_otw)); +        thread_group.create_thread(boost::bind(&benchmark_tx_rate_async_helper, usrp)); +    } + +    //sleep for the required duration +    const long secs = long(duration); +    const long usecs = long((duration - secs)*1e6); +    boost::this_thread::sleep(boost::posix_time::seconds(secs) + boost::posix_time::microseconds(usecs)); + +    //interrupt and join the threads +    thread_group.interrupt_all(); +    thread_group.join_all(); + +    //print summary +    std::cout << std::endl << boost::format( +        "Benchmark rate summary:\n" +        "  Num received samples:    %u\n" +        "  Num dropped samples:     %u\n" +        "  Num overflows detected:  %u\n" +        "  Num transmitted samples: %u\n" +        "  Num sequence errors:     %u\n" +        "  Num underflows detected: %u\n" +    ) % num_rx_samps % num_dropped_samps % num_overflows % num_tx_samps % num_seq_errors % num_underflows << std::endl; + +    //finished +    std::cout << std::endl << "Done!" << std::endl << std::endl; + +    return 0; +} diff --git a/host/examples/latency_test.cpp b/host/examples/latency_test.cpp new file mode 100644 index 000000000..518d2383a --- /dev/null +++ b/host/examples/latency_test.cpp @@ -0,0 +1,168 @@ +// +// Copyright 2011 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/multi_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; +    size_t nsamps; +    double rate; +    double rtt; +    size_t nruns; + +    //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>(&nsamps)->default_value(100),   "number of samples per run") +        ("nruns",  po::value<size_t>(&nruns)->default_value(1000),   "number of tests to perform") +        ("rtt",    po::value<double>(&rtt)->default_value(0.001),    "delay between receive and transmit (seconds)") +        ("rate",   po::value<double>(&rate)->default_value(100e6/4), "sample rate for receive and transmit (sps)") +        ("verbose", "specify to enable 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 - Latency Test %s") % desc << std::endl; +        std::cout << +        "    Latency test receives a packet at time t,\n" +        "    and tries to send a packet at time t + rtt,\n" +        "    where rtt is the round trip time sample time\n" +        "    from device to host and back to the device.\n" +        << std::endl; +        return ~0; +    } + +    bool verbose = vm.count("verbose") != 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); +    //std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; + +    usrp->set_time_now(uhd::time_spec_t(0.0)); + +    //set the tx sample rate +    usrp->set_tx_rate(rate); +    std::cout << boost::format("Actual TX Rate: %f Msps...") % (usrp->get_tx_rate()/1e6) << std::endl; + +    //set the rx sample rate +    usrp->set_rx_rate(rate); +    std::cout << boost::format("Actual RX Rate: %f Msps...") % (usrp->get_rx_rate()/1e6) << std::endl; + +    //allocate a buffer to use +    std::vector<std::complex<float> > buffer(nsamps); + +    //create RX and TX streamers +    uhd::stream_args_t stream_args("fc32"); //complex floats +    uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); +    uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); + +    //initialize result counts +    int time_error = 0; +    int ack = 0; +    int underflow = 0; +    int other = 0; + +    for(size_t nrun = 0; nrun < nruns; nrun++){ + +        /*************************************************************** +         * Issue a stream command some time in the near future +         **************************************************************/ +        uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); +        stream_cmd.num_samps = buffer.size(); +        stream_cmd.stream_now = false; +        stream_cmd.time_spec = usrp->get_time_now() + uhd::time_spec_t(0.01); +        usrp->issue_stream_cmd(stream_cmd); + +        /*************************************************************** +         * Receive the requested packet +         **************************************************************/ +        uhd::rx_metadata_t rx_md; +        size_t num_rx_samps = rx_stream->recv( +            &buffer.front(), buffer.size(), rx_md +        ); + +        if(verbose) std::cout << boost::format("Got packet: %u samples, %u full secs, %f frac secs") +            % num_rx_samps % rx_md.time_spec.get_full_secs() % rx_md.time_spec.get_frac_secs() << std::endl; + +        /*************************************************************** +         * Transmit a packet with delta time after received packet +         **************************************************************/ +        uhd::tx_metadata_t tx_md; +        tx_md.start_of_burst = true; +        tx_md.end_of_burst = true; +        tx_md.has_time_spec = true; +        tx_md.time_spec = rx_md.time_spec + uhd::time_spec_t(rtt); +        size_t num_tx_samps = tx_stream->send( +            &buffer.front(), buffer.size(), tx_md +        ); +        if(verbose) std::cout << boost::format("Sent %d samples") % num_tx_samps << std::endl; + +        /*************************************************************** +         * Check the async messages for result +         **************************************************************/ +        uhd::async_metadata_t async_md; +        if (not usrp->get_device()->recv_async_msg(async_md)){ +            std::cout << boost::format("failed:\n    Async message recv timed out.\n") << std::endl; +            continue; +        } +        switch(async_md.event_code){ +        case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR: +            time_error++; +            break; + +        case uhd::async_metadata_t::EVENT_CODE_BURST_ACK: +            ack++; +            break; + +        case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW: +            underflow++; +            break; + +        default: +            std::cerr << boost::format( +                "failed:\n    Got unexpected event code 0x%x.\n" +            ) % async_md.event_code << std::endl; +            other++; +            break; +        } +    } + +    /*************************************************************** +     * Print the summary +     **************************************************************/ +    std::cout << boost::format("\nACK %d, UNDERFLOW %d, TIME_ERR %d, other %d") +        % ack % underflow % time_error % other << std::endl; +    return 0; +} diff --git a/host/examples/network_relay.cpp b/host/examples/network_relay.cpp new file mode 100644 index 000000000..a7fdb7e2e --- /dev/null +++ b/host/examples/network_relay.cpp @@ -0,0 +1,227 @@ +// +// Copyright 2010-2011 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 <boost/program_options.hpp> +#include <boost/thread/thread.hpp> +#include <boost/format.hpp> +#include <boost/asio.hpp> +#include <iostream> +#include <csignal> +#include <vector> + +namespace po = boost::program_options; +namespace asio = boost::asio; + +typedef boost::shared_ptr<asio::ip::udp::socket> socket_type; + +static const size_t insane_mtu = 9000; + +boost::mutex spawn_mutex; + +#if defined(UHD_PLATFORM_MACOS) || defined(UHD_PLATFORM_BSD) +    //limit buffer resize on macos or it will error +    const size_t rx_dsp_buff_size = size_t(1e6); +#elif defined(UHD_PLATFORM_LINUX) || defined(UHD_PLATFORM_WIN32) +    //set to half-a-second of buffering at max rate +    const size_t rx_dsp_buff_size = size_t(50e6); +#endif + +const size_t tx_dsp_buff_size = (1 << 20); + +/*********************************************************************** + * Signal handlers + **********************************************************************/ +static bool stop_signal_called = false; +void sig_int_handler(int){stop_signal_called = true;} + +static bool wait_for_recv_ready(int sock_fd){ +    //setup timeval for timeout +    timeval tv; +    tv.tv_sec = 0; +    tv.tv_usec = 100000; //100ms + +    //setup rset for timeout +    fd_set rset; +    FD_ZERO(&rset); +    FD_SET(sock_fd, &rset); + +    //call select with timeout on receive socket +    return ::select(sock_fd+1, &rset, NULL, NULL, &tv) > 0; +} + +/*********************************************************************** + * Relay class + **********************************************************************/ +class udp_relay_type{ +public: +    udp_relay_type( +        const std::string &server_addr, +        const std::string &client_addr, +        const std::string &port, +        const size_t server_rx_size = 0, +        const size_t server_tx_size = 0, +        const size_t client_rx_size = 0, +        const size_t client_tx_size = 0 +    ):_port(port){ +        { +            asio::ip::udp::resolver resolver(_io_service); +            asio::ip::udp::resolver::query query(asio::ip::udp::v4(), server_addr, port); +            asio::ip::udp::endpoint endpoint = *resolver.resolve(query); + +            _server_socket = boost::shared_ptr<asio::ip::udp::socket>(new asio::ip::udp::socket(_io_service, endpoint)); +            resize_buffs(_server_socket, server_rx_size, server_tx_size); +        } +        { +            asio::ip::udp::resolver resolver(_io_service); +            asio::ip::udp::resolver::query query(asio::ip::udp::v4(), client_addr, port); +            asio::ip::udp::endpoint endpoint = *resolver.resolve(query); + +            _client_socket = boost::shared_ptr<asio::ip::udp::socket>(new asio::ip::udp::socket(_io_service)); +            _client_socket->open(asio::ip::udp::v4()); +            _client_socket->connect(endpoint); +            resize_buffs(_client_socket, client_rx_size, client_tx_size); +        } + +        std::cout << "spawning relay threads... " << _port << std::endl; +        _thread_group.create_thread(boost::bind(&udp_relay_type::server_thread, this)); +        spawn_mutex.lock(); +        spawn_mutex.lock(); +        spawn_mutex.unlock(); +        _thread_group.create_thread(boost::bind(&udp_relay_type::client_thread, this)); +        spawn_mutex.lock(); +        spawn_mutex.lock(); +        spawn_mutex.unlock(); +        std::cout << "    done!" << std::endl << std::endl; +    } + +    ~udp_relay_type(void){ +        std::cout << "killing relay threads... " << _port << std::endl; +        _thread_group.interrupt_all(); +        _thread_group.join_all(); +        std::cout << "    done!" << std::endl << std::endl; +    } + +private: + +    static void resize_buffs(socket_type sock, const size_t rx_size, const size_t tx_size){ +        if (rx_size != 0) sock->set_option(asio::socket_base::receive_buffer_size(rx_size)); +        if (tx_size != 0) sock->set_option(asio::socket_base::send_buffer_size(tx_size)); +    } + +    void server_thread(void){ +        uhd::set_thread_priority_safe(); +        std::cout << "    entering server_thread..." << std::endl; +        spawn_mutex.unlock(); +        std::vector<char> buff(insane_mtu); +        while (not boost::this_thread::interruption_requested()){ +            if (wait_for_recv_ready(_server_socket->native())){ +                boost::mutex::scoped_lock lock(_endpoint_mutex); +                const size_t len = _server_socket->receive_from(asio::buffer(&buff.front(), buff.size()), _endpoint); +                lock.unlock(); +                _client_socket->send(asio::buffer(&buff.front(), len)); + +                //perform sequence error detection on tx dsp data (can detect bad network cards) +                /* +                if (_port[4] == '7'){ +                    static boost::uint32_t next_seq; +                    const boost::uint32_t this_seq = ntohl(reinterpret_cast<const boost::uint32_t *>(&buff.front())[0]); +                    if (next_seq != this_seq and this_seq != 0) std::cout << "S" << std::flush; +                    next_seq = this_seq + 1; +                } +                */ +            } +        } +        std::cout << "    exiting server_thread..." << std::endl; +    } + +    void client_thread(void){ +        uhd::set_thread_priority_safe(); +        std::cout << "    entering client_thread..." << std::endl; +        spawn_mutex.unlock(); +        std::vector<char> buff(insane_mtu); +        while (not boost::this_thread::interruption_requested()){ +            if (wait_for_recv_ready(_client_socket->native())){ +                const size_t len = _client_socket->receive(asio::buffer(&buff.front(), buff.size())); +                boost::mutex::scoped_lock lock(_endpoint_mutex); +                _server_socket->send_to(asio::buffer(&buff.front(), len), _endpoint); +            } +        } +        std::cout << "    exiting client_thread..." << std::endl; +    } + +    const std::string _port; +    boost::thread_group _thread_group; +    asio::io_service _io_service; +    asio::ip::udp::endpoint _endpoint; +    boost::mutex _endpoint_mutex; +    socket_type _server_socket, _client_socket; +}; + + +/*********************************************************************** + * Main + **********************************************************************/ +int UHD_SAFE_MAIN(int argc, char *argv[]){ +    uhd::set_thread_priority_safe(); + +    //variables to be set by po +    std::string addr; +    std::string bind; + +    //setup the program options +    po::options_description desc("Allowed options"); +    desc.add_options() +        ("help", "help message") +        ("addr", po::value<std::string>(&addr), "the resolvable address of the usrp (must be specified)") +        ("bind", po::value<std::string>(&bind)->default_value("0.0.0.0"), "bind the server to this network address (default: any)") +    ; +    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("addr")){ +        std::cout +            << boost::format("UHD Network Relay %s") % desc << std::endl +            << "Runs a network relay between UHD on one computer and a USRP on the network.\n" +            << "This example is basically for test purposes. Use at your own convenience.\n" +            << std::endl; +        return ~0; +    } + +    { +        boost::shared_ptr<udp_relay_type> ctrl  (new udp_relay_type(bind, addr, "49152")); +        boost::shared_ptr<udp_relay_type> rxdsp0(new udp_relay_type(bind, addr, "49156", 0, tx_dsp_buff_size, rx_dsp_buff_size, 0)); +        boost::shared_ptr<udp_relay_type> txdsp0(new udp_relay_type(bind, addr, "49157", tx_dsp_buff_size, 0, 0, tx_dsp_buff_size)); +        boost::shared_ptr<udp_relay_type> rxdsp1(new udp_relay_type(bind, addr, "49158", 0, tx_dsp_buff_size, rx_dsp_buff_size, 0)); +        boost::shared_ptr<udp_relay_type> gps   (new udp_relay_type(bind, addr, "49172")); + +        std::signal(SIGINT, &sig_int_handler); +        std::cout << "Press Ctrl + C to stop streaming..." << std::endl; + +        while (not stop_signal_called){ +            boost::this_thread::sleep(boost::posix_time::milliseconds(100)); +        } +    } + +    //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..fe8fb0347 --- /dev/null +++ b/host/examples/rx_ascii_art_dft.cpp @@ -0,0 +1,198 @@ +// +// Copyright 2010-2011 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/multi_usrp.hpp> +#include "ascii_art_dft.hpp" //implementation +#include <boost/program_options.hpp> +#include <boost/thread/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, ant, subdev, ref; +    size_t num_bins; +    double rate, freq, gain, bw, frame_rate; +    float 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(""), "multi uhd device address args") +        // hardware parameters +        ("rate", po::value<double>(&rate), "rate of incoming samples (sps)") +        ("freq", po::value<double>(&freq), "RF center frequency in Hz") +        ("gain", po::value<double>(&gain), "gain for the RF chain") +        ("ant", po::value<std::string>(&ant), "daughterboard antenna selection") +        ("subdev", po::value<std::string>(&subdev), "daughterboard subdevice specification") +        ("bw", po::value<double>(&bw), "daughterboard IF filter bandwidth in Hz") +        // 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)") +        ("ref", po::value<std::string>(&ref)->default_value("internal"), "waveform type (internal, external, mimo)") +    ; +    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::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); + +    //Lock mboard clocks +    usrp->set_clock_source(ref); + +     //always select the subdevice first, the channel mapping affects the other settings +    if (vm.count("subdev")) usrp->set_rx_subdev_spec(subdev); + +    std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; + +    //set the sample rate +    if (not vm.count("rate")){ +        std::cerr << "Please specify the sample rate with --rate" << std::endl; +        return ~0; +    } +    std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate/1e6) << std::endl; +    usrp->set_rx_rate(rate); +    std::cout << boost::format("Actual RX Rate: %f Msps...") % (usrp->get_rx_rate()/1e6) << std::endl << std::endl; + +    //set the center frequency +    if (not vm.count("freq")){ +        std::cerr << "Please specify the center frequency with --freq" << std::endl; +        return ~0; +    } +    std::cout << boost::format("Setting RX Freq: %f MHz...") % (freq/1e6) << std::endl; +    usrp->set_rx_freq(freq); +    std::cout << boost::format("Actual RX Freq: %f MHz...") % (usrp->get_rx_freq()/1e6) << std::endl << std::endl; + +    //set the rf gain +    if (vm.count("gain")){ +        std::cout << boost::format("Setting RX Gain: %f dB...") % gain << std::endl; +        usrp->set_rx_gain(gain); +        std::cout << boost::format("Actual RX Gain: %f dB...") % usrp->get_rx_gain() << std::endl << std::endl; +    } + +    //set the IF filter bandwidth +    if (vm.count("bw")){ +        std::cout << boost::format("Setting RX Bandwidth: %f MHz...") % bw << std::endl; +        usrp->set_rx_bandwidth(bw); +        std::cout << boost::format("Actual RX Bandwidth: %f MHz...") % usrp->get_rx_bandwidth() << std::endl << std::endl; +    } + +    //set the antenna +    if (vm.count("ant")) usrp->set_rx_antenna(ant); + +    boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow for some setup time + +    //Check Ref and LO Lock detect +    std::vector<std::string> sensor_names; +    sensor_names = usrp->get_rx_sensor_names(0); +    if (std::find(sensor_names.begin(), sensor_names.end(), "lo_locked") != sensor_names.end()) { +        uhd::sensor_value_t lo_locked = usrp->get_rx_sensor("lo_locked",0); +        std::cout << boost::format("Checking RX: %s ...") % lo_locked.to_pp_string() << std::endl; +        UHD_ASSERT_THROW(lo_locked.to_bool()); +    } +    sensor_names = usrp->get_mboard_sensor_names(0); +    if ((ref == "mimo") and (std::find(sensor_names.begin(), sensor_names.end(), "mimo_locked") != sensor_names.end())) { +        uhd::sensor_value_t mimo_locked = usrp->get_mboard_sensor("mimo_locked",0); +        std::cout << boost::format("Checking RX: %s ...") % mimo_locked.to_pp_string() << std::endl; +        UHD_ASSERT_THROW(mimo_locked.to_bool()); +    } +    if ((ref == "external") and (std::find(sensor_names.begin(), sensor_names.end(), "ref_locked") != sensor_names.end())) { +        uhd::sensor_value_t ref_locked = usrp->get_mboard_sensor("ref_locked",0); +        std::cout << boost::format("Checking RX: %s ...") % ref_locked.to_pp_string() << std::endl; +        UHD_ASSERT_THROW(ref_locked.to_bool()); +    } + +    //create a receive streamer +    uhd::stream_args_t stream_args("fc32"); //complex floats +    uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + +    //allocate recv buffer and metatdata +    uhd::rx_metadata_t md; +    std::vector<std::complex<float> > buff(num_bins); +    //------------------------------------------------------------------ +    //-- Initialize +    //------------------------------------------------------------------ +    initscr(); //curses init +    usrp->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 = rx_stream->recv( +            &buff.front(), buff.size(), md +        ); +        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, +            usrp->get_rx_rate(), +            usrp->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 +    //------------------------------------------------------------------ +    usrp->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_multi_samples.cpp b/host/examples/rx_multi_samples.cpp new file mode 100644 index 000000000..42ef33d70 --- /dev/null +++ b/host/examples/rx_multi_samples.cpp @@ -0,0 +1,178 @@ +// +// Copyright 2011 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/multi_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, sync, subdev; +    double seconds_in_future; +    size_t total_num_samps; +    double 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") +        ("secs", po::value<double>(&seconds_in_future)->default_value(1.5), "number of seconds in the future to receive") +        ("nsamps", po::value<size_t>(&total_num_samps)->default_value(10000), "total number of samples to receive") +        ("rate", po::value<double>(&rate)->default_value(100e6/16), "rate of incoming samples") +        ("sync", po::value<std::string>(&sync)->default_value("now"), "synchronization method: now, pps, mimo") +        ("subdev", po::value<std::string>(&subdev), "subdev spec (homogeneous across motherboards)") +        ("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 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 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::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(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...") % (rate/1e6) << std::endl; +    usrp->set_rx_rate(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)); +        boost::this_thread::sleep(boost::posix_time::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 +        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); +    } + +    //create a receive streamer +    //linearly map channels (index0 = channel0, index1 = channel1, ...) +    uhd::stream_args_t stream_args("fc32"); //complex floats +    for (size_t chan = 0; chan < usrp->get_rx_num_channels(); chan++) +        stream_args.channels.push_back(chan); //linear mapping +    uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + +    //setup streaming +    std::cout << std::endl; +    std::cout << boost::format( +        "Begin streaming %u samples, %f 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); +    usrp->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 +    while(num_acc_samps < total_num_samps){ +        //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) break; +        if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ +            throw std::runtime_error(str(boost::format( +                "Unexpected error code 0x%x" +            ) % md.error_code)); +        } + +        if(verbose) std::cout << boost::format( +            "Received 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; +    } + +    if (num_acc_samps < total_num_samps) std::cerr << "Receive timeout before all samples received..." << std::endl; + +    //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..5197bfe69 --- /dev/null +++ b/host/examples/rx_samples_to_file.cpp @@ -0,0 +1,218 @@ +// +// Copyright 2010-2011 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/multi_usrp.hpp> +#include <uhd/exception.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <boost/thread.hpp> +#include <iostream> +#include <fstream> +#include <csignal> +#include <complex> + +namespace po = boost::program_options; + +static bool stop_signal_called = false; +void sig_int_handler(int){stop_signal_called = true;} + +template<typename samp_type> void recv_to_file( +    uhd::usrp::multi_usrp::sptr usrp, +    const std::string &cpu_format, +    const std::string &wire_format, +    const std::string &file, +    size_t samps_per_buff, +    int num_requested_samples +){ +    int num_total_samps = 0; +    //create a receive streamer +    uhd::stream_args_t stream_args(cpu_format,wire_format); +    uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + +    uhd::rx_metadata_t md; +    std::vector<samp_type> buff(samps_per_buff); +    std::ofstream outfile(file.c_str(), std::ofstream::binary); +    bool overflow_message = true; + +    //setup streaming +    uhd::stream_cmd_t stream_cmd((num_requested_samples == 0)? +        uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS: +        uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE +    ); +    stream_cmd.num_samps = num_requested_samples; +    stream_cmd.stream_now = true; +    stream_cmd.time_spec = uhd::time_spec_t(); +    usrp->issue_stream_cmd(stream_cmd); + +    while(not stop_signal_called and (num_requested_samples != num_total_samps or num_requested_samples == 0)){ +        size_t num_rx_samps = rx_stream->recv(&buff.front(), buff.size(), md, 3.0); + +        if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) { +            std::cout << 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(samp_type)/1e6); +            } +            continue; +        } +        if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ +            throw std::runtime_error(str(boost::format( +                "Unexpected error code 0x%x" +            ) % md.error_code)); +        } + +        num_total_samps += num_rx_samps; + +        outfile.write((const char*)&buff.front(), num_rx_samps*sizeof(samp_type)); +    } + +    outfile.close(); +} + +int UHD_SAFE_MAIN(int argc, char *argv[]){ +    uhd::set_thread_priority_safe(); + +    //variables to be set by po +    std::string args, file, type, ant, subdev, ref, wirefmt; +    size_t total_num_samps, spb; +    double rate, freq, gain, bw; + +    //setup the program options +    po::options_description desc("Allowed options"); +    desc.add_options() +        ("help", "help message") +        ("args", po::value<std::string>(&args)->default_value(""), "multi uhd device address args") +        ("file", po::value<std::string>(&file)->default_value("usrp_samples.dat"), "name of the file to write binary samples to") +        ("type", po::value<std::string>(&type)->default_value("short"), "sample type: double, float, or short") +        ("nsamps", po::value<size_t>(&total_num_samps)->default_value(0), "total number of samples to receive") +        ("spb", po::value<size_t>(&spb)->default_value(10000), "samples per buffer") +        ("rate", po::value<double>(&rate), "rate of incoming samples") +        ("freq", po::value<double>(&freq), "RF center frequency in Hz") +        ("gain", po::value<double>(&gain), "gain for the RF chain") +        ("ant", po::value<std::string>(&ant), "daughterboard antenna selection") +        ("subdev", po::value<std::string>(&subdev), "daughterboard subdevice specification") +        ("bw", po::value<double>(&bw), "daughterboard IF filter bandwidth in Hz") +        ("ref", po::value<std::string>(&ref)->default_value("internal"), "waveform type (internal, external, mimo)") +        ("wirefmt", po::value<std::string>(&wirefmt)->default_value("sc16"), "wire format (sc8 or sc16)") +    ; +    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 samples 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::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); + +    //Lock mboard clocks +    usrp->set_clock_source(ref); + +    //always select the subdevice first, the channel mapping affects the other settings +    if (vm.count("subdev")) usrp->set_rx_subdev_spec(subdev); + +    std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; + +    //set the sample rate +    if (not vm.count("rate")){ +        std::cerr << "Please specify the sample rate with --rate" << std::endl; +        return ~0; +    } +    std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate/1e6) << std::endl; +    usrp->set_rx_rate(rate); +    std::cout << boost::format("Actual RX Rate: %f Msps...") % (usrp->get_rx_rate()/1e6) << std::endl << std::endl; + +    //set the center frequency +    if (not vm.count("freq")){ +        std::cerr << "Please specify the center frequency with --freq" << std::endl; +        return ~0; +    } +    std::cout << boost::format("Setting RX Freq: %f MHz...") % (freq/1e6) << std::endl; +    usrp->set_rx_freq(freq); +    std::cout << boost::format("Actual RX Freq: %f MHz...") % (usrp->get_rx_freq()/1e6) << std::endl << std::endl; + +    //set the rf gain +    if (vm.count("gain")){ +        std::cout << boost::format("Setting RX Gain: %f dB...") % gain << std::endl; +        usrp->set_rx_gain(gain); +        std::cout << boost::format("Actual RX Gain: %f dB...") % usrp->get_rx_gain() << std::endl << std::endl; +    } + +    //set the IF filter bandwidth +    if (vm.count("bw")){ +        std::cout << boost::format("Setting RX Bandwidth: %f MHz...") % bw << std::endl; +        usrp->set_rx_bandwidth(bw); +        std::cout << boost::format("Actual RX Bandwidth: %f MHz...") % usrp->get_rx_bandwidth() << std::endl << std::endl; +    } + +    //set the antenna +    if (vm.count("ant")) usrp->set_rx_antenna(ant); + +    boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow for some setup time + +    //Check Ref and LO Lock detect +    std::vector<std::string> sensor_names; +    sensor_names = usrp->get_rx_sensor_names(0); +    if (std::find(sensor_names.begin(), sensor_names.end(), "lo_locked") != sensor_names.end()) { +        uhd::sensor_value_t lo_locked = usrp->get_rx_sensor("lo_locked",0); +        std::cout << boost::format("Checking RX: %s ...") % lo_locked.to_pp_string() << std::endl; +        UHD_ASSERT_THROW(lo_locked.to_bool()); +    } +    sensor_names = usrp->get_mboard_sensor_names(0); +    if ((ref == "mimo") and (std::find(sensor_names.begin(), sensor_names.end(), "mimo_locked") != sensor_names.end())) { +        uhd::sensor_value_t mimo_locked = usrp->get_mboard_sensor("mimo_locked",0); +        std::cout << boost::format("Checking RX: %s ...") % mimo_locked.to_pp_string() << std::endl; +        UHD_ASSERT_THROW(mimo_locked.to_bool()); +    } +    if ((ref == "external") and (std::find(sensor_names.begin(), sensor_names.end(), "ref_locked") != sensor_names.end())) { +        uhd::sensor_value_t ref_locked = usrp->get_mboard_sensor("ref_locked",0); +        std::cout << boost::format("Checking RX: %s ...") % ref_locked.to_pp_string() << std::endl; +        UHD_ASSERT_THROW(ref_locked.to_bool()); +    } + +    if (total_num_samps == 0){ +        std::signal(SIGINT, &sig_int_handler); +        std::cout << "Press Ctrl + C to stop streaming..." << std::endl; +    } + +    //recv to file +    if (type == "double") recv_to_file<std::complex<double> >(usrp, "fc64", wirefmt, file, spb, total_num_samps); +    else if (type == "float") recv_to_file<std::complex<float> >(usrp, "fc32", wirefmt, file, spb, total_num_samps); +    else if (type == "short") recv_to_file<std::complex<short> >(usrp, "sc16", wirefmt, file, spb, total_num_samps); +    else throw std::runtime_error("Unknown type " + type); + +    //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..42b80762f --- /dev/null +++ b/host/examples/rx_samples_to_udp.cpp @@ -0,0 +1,172 @@ +// +// Copyright 2010-2011 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/multi_usrp.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <uhd/exception.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, file, ant, subdev, ref; +    size_t total_num_samps; +    double rate, freq, gain, bw; +    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(""), "multi 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<double>(&gain)->default_value(0), "gain for the RF chain") +        ("ant", po::value<std::string>(&ant), "daughterboard antenna selection") +        ("subdev", po::value<std::string>(&subdev), "daughterboard subdevice specification") +        ("bw", po::value<double>(&bw), "daughterboard IF filter bandwidth in Hz") +        ("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") +        ("ref", po::value<std::string>(&ref)->default_value("internal"), "waveform type (internal, external, mimo)") +    ; +    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::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); +    std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; + +    //Lock mboard clocks +    usrp->set_clock_source(ref); + +    //set the rx sample rate +    std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate/1e6) << std::endl; +    usrp->set_rx_rate(rate); +    std::cout << boost::format("Actual RX Rate: %f Msps...") % (usrp->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; +    usrp->set_rx_freq(freq); +    std::cout << boost::format("Actual RX Freq: %f Mhz...") % (usrp->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; +    usrp->set_rx_gain(gain); +    std::cout << boost::format("Actual RX Gain: %f dB...") % usrp->get_rx_gain() << std::endl << std::endl; + +    //set the IF filter bandwidth +    if (vm.count("bw")){ +        std::cout << boost::format("Setting RX Bandwidth: %f MHz...") % bw << std::endl; +        usrp->set_rx_bandwidth(bw); +        std::cout << boost::format("Actual RX Bandwidth: %f MHz...") % usrp->get_rx_bandwidth() << std::endl << std::endl; +    } + +    //set the antenna +    if (vm.count("ant")) usrp->set_rx_antenna(ant); + +    boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow for some setup time + +    //Check Ref and LO Lock detect +    std::vector<std::string> sensor_names; +    sensor_names = usrp->get_rx_sensor_names(0); +    if (std::find(sensor_names.begin(), sensor_names.end(), "lo_locked") != sensor_names.end()) { +        uhd::sensor_value_t lo_locked = usrp->get_rx_sensor("lo_locked",0); +        std::cout << boost::format("Checking RX: %s ...") % lo_locked.to_pp_string() << std::endl; +        UHD_ASSERT_THROW(lo_locked.to_bool()); +    } +    sensor_names = usrp->get_mboard_sensor_names(0); +    if ((ref == "mimo") and (std::find(sensor_names.begin(), sensor_names.end(), "mimo_locked") != sensor_names.end())) { +        uhd::sensor_value_t mimo_locked = usrp->get_mboard_sensor("mimo_locked",0); +        std::cout << boost::format("Checking RX: %s ...") % mimo_locked.to_pp_string() << std::endl; +        UHD_ASSERT_THROW(mimo_locked.to_bool()); +    } +    if ((ref == "external") and (std::find(sensor_names.begin(), sensor_names.end(), "ref_locked") != sensor_names.end())) { +        uhd::sensor_value_t ref_locked = usrp->get_mboard_sensor("ref_locked",0); +        std::cout << boost::format("Checking RX: %s ...") % ref_locked.to_pp_string() << std::endl; +        UHD_ASSERT_THROW(ref_locked.to_bool()); +    } + +    //create a receive streamer +    uhd::stream_args_t stream_args("fc32"); //complex floats +    uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + +    //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; +    usrp->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(rx_stream->get_max_num_samps()); +    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 = rx_stream->recv( +            &buff.front(), buff.size(), md +        ); + +        //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..143bceb03 --- /dev/null +++ b/host/examples/rx_timed_samples.cpp @@ -0,0 +1,128 @@ +// +// Copyright 2010-2011 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/multi_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; +    double seconds_in_future; +    size_t total_num_samps; +    double 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") +        ("secs", po::value<double>(&seconds_in_future)->default_value(1.5), "number of seconds in the future to receive") +        ("nsamps", po::value<size_t>(&total_num_samps)->default_value(10000), "total number of samples to receive") +        ("rate", po::value<double>(&rate)->default_value(100e6/16), "rate of incoming samples") +        ("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::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); +    std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; + +    //set the rx sample rate +    std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate/1e6) << std::endl; +    usrp->set_rx_rate(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; +    usrp->set_time_now(uhd::time_spec_t(0.0)); + +    //create a receive streamer +    uhd::stream_args_t stream_args("fc32"); //complex floats +    uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + +    //setup streaming +    std::cout << std::endl; +    std::cout << boost::format( +        "Begin streaming %u samples, %f 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); +    usrp->issue_stream_cmd(stream_cmd); + +    //meta-data will be filled in by recv() +    uhd::rx_metadata_t md; + +    //allocate buffer to receive with samples +    std::vector<std::complex<float> > buff(rx_stream->get_max_num_samps()); + +    //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 +    while(num_acc_samps < total_num_samps){ +        //receive a single packet +        size_t num_rx_samps = rx_stream->recv( +            &buff.front(), buff.size(), md, timeout, true +        ); + +        //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) break; +        if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ +            throw std::runtime_error(str(boost::format( +                "Unexpected error code 0x%x" +            ) % md.error_code)); +        } + +        if(verbose) std::cout << boost::format( +            "Received 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; +    } + +    if (num_acc_samps < total_num_samps) std::cerr << "Receive timeout before all samples received..." << std::endl; + +    //finished +    std::cout << std::endl << "Done!" << std::endl << std::endl; + +    return 0; +} diff --git a/host/examples/test_messages.cpp b/host/examples/test_messages.cpp new file mode 100644 index 000000000..afb092092 --- /dev/null +++ b/host/examples/test_messages.cpp @@ -0,0 +1,354 @@ +// +// Copyright 2010-2011 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/types/stream_cmd.hpp> +#include <uhd/usrp/multi_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 <ctime> +#include <complex> +#include <iostream> + +namespace po = boost::program_options; + +/*! + * Test the late command message: + *    Issue a stream command with a time that is in the past. + *    We expect to get an inline late command message. + */ +bool test_late_command_message(uhd::usrp::multi_usrp::sptr usrp, uhd::rx_streamer::sptr rx_stream, uhd::tx_streamer::sptr){ +    std::cout << "Test late command message... " << std::flush; + +    usrp->set_time_now(uhd::time_spec_t(200.0)); //set time + +    uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); +    stream_cmd.num_samps = rx_stream->get_max_num_samps(); +    stream_cmd.stream_now = false; +    stream_cmd.time_spec = uhd::time_spec_t(100.0); //time in the past +    usrp->issue_stream_cmd(stream_cmd); + +    std::vector<std::complex<float> > buff(rx_stream->get_max_num_samps()); +    uhd::rx_metadata_t md; + +    const size_t nsamps = rx_stream->recv( +        &buff.front(), buff.size(), md +    ); + +    switch(md.error_code){ +    case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND: +        std::cout << boost::format( +            "success:\n" +            "    Got error code late command message.\n" +        ) << std::endl; +        return true; + +    case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT: +        std::cout << boost::format( +            "failed:\n" +            "    Inline message recv timed out.\n" +        ) << std::endl; +        return false; + +    default: +        std::cout << boost::format( +            "failed:\n" +            "    Got unexpected error code 0x%x, nsamps %u.\n" +        ) % md.error_code % nsamps << std::endl; +        return false; +    } +} + +/*! + * Test the broken chain message: + *    Issue a stream command with num samps and more. + *    We expect to get an inline broken chain message. + */ +bool test_broken_chain_message(uhd::usrp::multi_usrp::sptr usrp, uhd::rx_streamer::sptr rx_stream, uhd::tx_streamer::sptr){ +    std::cout << "Test broken chain message... " << std::flush; + +    uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE); +    stream_cmd.stream_now = true; +    stream_cmd.num_samps = rx_stream->get_max_num_samps(); +    usrp->issue_stream_cmd(stream_cmd); + +    std::vector<std::complex<float> > buff(rx_stream->get_max_num_samps()); +    uhd::rx_metadata_t md; + +    rx_stream->recv( //once for the requested samples +        &buff.front(), buff.size(), md +    ); + +    rx_stream->recv( //again for the inline message +        &buff.front(), buff.size(), md +    ); + +    switch(md.error_code){ +    case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN: +        std::cout << boost::format( +            "success:\n" +            "    Got error code broken chain message.\n" +        ) << std::endl; +        return true; + +    case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT: +        std::cout << boost::format( +            "failed:\n" +            "    Inline message recv timed out.\n" +        ) << std::endl; +        return false; + +    default: +        std::cout << boost::format( +            "failed:\n" +            "    Got unexpected error code 0x%x.\n" +        ) % md.error_code << std::endl; +        return false; +    } +} + +/*! + * 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::multi_usrp::sptr usrp, uhd::rx_streamer::sptr, uhd::tx_streamer::sptr tx_stream){ +    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(tx_stream->get_max_num_samps()*3); + +    tx_stream->send( +        &buff.front(), buff.size(), md +    ); + +    uhd::async_metadata_t async_md; +    if (not usrp->get_device()->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::multi_usrp::sptr usrp, uhd::rx_streamer::sptr, uhd::tx_streamer::sptr tx_stream){ +    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; + +    tx_stream->send("", 0, md); + +    uhd::async_metadata_t async_md; +    if (not usrp->get_device()->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::multi_usrp::sptr usrp, uhd::rx_streamer::sptr, uhd::tx_streamer::sptr tx_stream){ +    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 + +    usrp->set_time_now(uhd::time_spec_t(200.0)); //time at 200s + +    tx_stream->send("", 0, md); + +    uhd::async_metadata_t async_md; +    if (not usrp->get_device()->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(uhd::usrp::multi_usrp::sptr usrp){ +    uhd::async_metadata_t async_md; +    while (usrp->get_device()->recv_async_msg(async_md)){} +} + +void flush_recv(uhd::rx_streamer::sptr rx_stream){ +    std::vector<std::complex<float> > buff(rx_stream->get_max_num_samps()); +    uhd::rx_metadata_t md; + +    do{ +        rx_stream->recv(&buff.front(), buff.size(), md); +    } while (md.error_code != uhd::rx_metadata_t::ERROR_CODE_TIMEOUT); +} + +int UHD_SAFE_MAIN(int argc, char *argv[]){ +    uhd::set_thread_priority_safe(); + +    //variables to be set by po +    std::string args; +    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(""), "multi uhd device address args") +        ("ntests", po::value<size_t>(&ntests)->default_value(50),    "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 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::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); +    std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; + +    //create RX and TX streamers +    uhd::stream_args_t stream_args("fc32"); //complex floats +    uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); +    uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); + +    //------------------------------------------------------------------ +    // begin messages test +    //------------------------------------------------------------------ +    static const uhd::dict<std::string, boost::function<bool(uhd::usrp::multi_usrp::sptr, uhd::rx_streamer::sptr, uhd::tx_streamer::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) +        ("Test Late Command", &test_late_command_message) +        ("Test Broken Chain", &test_broken_chain_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 +    std::srand((unsigned int) time(NULL)); +    for (size_t n = 0; n < ntests; n++){ +        std::string key = tests.keys()[std::rand() % tests.size()]; +        bool pass = tests[key](usrp, rx_stream, tx_stream); +        flush_async(usrp); +        flush_recv(rx_stream); + +        //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   ->   %3u successes, %3u 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..994b7ca87 --- /dev/null +++ b/host/examples/test_pps_input.cpp @@ -0,0 +1,62 @@ +// +// Copyright 2010-2011 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/multi_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; + +    //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::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); +    std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; + +    //set the time at an unknown pps (will throw if no pps) +    std::cout << std::endl << "Attempt to detect the PPS and set the time..." << std::endl << std::endl; +    usrp->set_time_unknown_pps(uhd::time_spec_t(0.0)); +    std::cout << std::endl << "Success!" << std::endl << std::endl; +    return 0; +} diff --git a/host/examples/tx_bursts.cpp b/host/examples/tx_bursts.cpp new file mode 100644 index 000000000..f5ae18a9f --- /dev/null +++ b/host/examples/tx_bursts.cpp @@ -0,0 +1,164 @@ +// +// Copyright 2010-2011 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/multi_usrp.hpp> +#include <boost/program_options.hpp> +#include <boost/thread/thread.hpp> +#include <boost/format.hpp> +#include <csignal> +#include <iostream> +#include <complex> + +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[]){ +    uhd::set_thread_priority_safe(); + +    //variables to be set by po +    std::string args; +    double seconds_in_future; +    size_t total_num_samps; +    double rate; +    float ampl; +    double freq; +    double rep_rate; +    double 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(""), "multi uhd device address args") +        ("secs", po::value<double>(&seconds_in_future)->default_value(1.5), "delay before first burst") +        ("repeat", "repeat burst") +        ("rep-delay", po::value<double>(&rep_rate)->default_value(0.5), "delay between bursts") +        ("nsamps", po::value<size_t>(&total_num_samps)->default_value(10000), "total number of samples to transmit") +        ("rate", po::value<double>(&rate)->default_value(100e6/16), "rate of outgoing samples") +        ("ampl", po::value<float>(&l)->default_value(float(0.3)), "amplitude of each sample") +        ("freq", po::value<double>(&freq)->default_value(0), "center frequency") +        ("gain", po::value<double>(&gain)->default_value(0), "gain") +        ("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; +    bool repeat = vm.count("repeat") != 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); +    std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; + +    //set the tx sample rate +    std::cout << boost::format("Setting TX Rate: %f Msps...") % (rate/1e6) << std::endl; +    usrp->set_tx_rate(rate); +    std::cout << boost::format("Actual TX Rate: %f Msps...") % (usrp->get_tx_rate()/1e6) << std::endl << std::endl; + +    std::cout << boost::format("Setting TX Freq: %f MHz...") % (freq/1e6) << std::endl; +    for(size_t i=0; i < usrp->get_tx_num_channels(); i++) usrp->set_tx_freq(freq, i); +    std::cout << boost::format("Actual TX Freq: %f MHz...") % (usrp->get_tx_freq()/1e6) << std::endl << std::endl; + +    std::cout << boost::format("Setting TX Gain: %f...") % (gain) << std::endl; +    for(size_t i=0; i < usrp->get_tx_num_channels(); i++) usrp->set_tx_gain(gain, i); +    std::cout << boost::format("Actual TX Gain: %f...") % (usrp->get_tx_gain()) << std::endl << std::endl; + +    std::cout << boost::format("Setting device timestamp to 0...") << std::endl; +    usrp->set_time_now(uhd::time_spec_t(0.0)); + +    //create a transmit streamer +    uhd::stream_args_t stream_args("fc32"); //complex floats +    uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); + +    //allocate buffer with data to send +    const size_t spb = tx_stream->get_max_num_samps(); + +    std::vector<std::complex<float> *> buffs; +    for(size_t i=0; i < usrp->get_num_mboards(); i++) { +        buffs.push_back(new std::complex<float>[spb]); +        for(size_t n=0; n < spb; n++) +            buffs.back()[n] = std::complex<float>(ampl, ampl); +    }; + +    std::signal(SIGINT, &sig_int_handler); +    if(repeat) std::cout << "Press Ctrl + C to quit..." << std::endl; + +    double time_to_send = seconds_in_future; + +    do { +        //setup metadata for the first packet +        uhd::tx_metadata_t md; +        md.start_of_burst = true; +        md.end_of_burst = false; +        md.has_time_spec = true; +        md.time_spec = uhd::time_spec_t(time_to_send); + +        //the first call to send() will block this many seconds before sending: +        double timeout = std::max(rep_rate, seconds_in_future) + 0.1; //timeout (delay before transmit + padding) + +        size_t num_acc_samps = 0; //number of accumulated samples +        while(num_acc_samps < total_num_samps){ +            size_t samps_to_send = std::min(total_num_samps - num_acc_samps, spb); + +            //ensure the the last packet has EOB set +            md.end_of_burst = samps_to_send < spb; + +            //send a single packet +            size_t num_tx_samps = tx_stream->send( +                buffs, samps_to_send, md, timeout +            ); +                 +            //do not use time spec for subsequent packets +            md.has_time_spec = false; +            md.start_of_burst = false; + +            if (num_tx_samps < samps_to_send) std::cerr << "Send timeout..." << std::endl; +            if(verbose) std::cout << boost::format("Sent packet: %u samples") % num_tx_samps << std::endl; + +            num_acc_samps += num_tx_samps; +        } + +        time_to_send += rep_rate; + +        std::cout << std::endl << "Waiting for async burst ACK... " << std::flush; +        uhd::async_metadata_t async_md; +        bool got_async_burst_ack = false; +        //loop through all messages for the ACK packet (may have underflow messages in queue) +        while (not got_async_burst_ack and usrp->get_device()->recv_async_msg(async_md, seconds_in_future)){ +            got_async_burst_ack = (async_md.event_code == uhd::async_metadata_t::EVENT_CODE_BURST_ACK); +        } +        std::cout << (got_async_burst_ack? "success" : "fail") << std::endl; +    } while (not stop_signal_called and repeat); + +    //finished +    std::cout << std::endl << "Done!" << std::endl << std::endl; + +    return 0; +} diff --git a/host/examples/tx_samples_from_file.cpp b/host/examples/tx_samples_from_file.cpp new file mode 100644 index 000000000..ea4add403 --- /dev/null +++ b/host/examples/tx_samples_from_file.cpp @@ -0,0 +1,174 @@ +// +// Copyright 2011 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/multi_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; + +template<typename samp_type> void send_from_file( +    uhd::usrp::multi_usrp::sptr usrp, +    const std::string &cpu_format, +    const std::string &file, +    size_t samps_per_buff +){ +    //create a transmit streamer +    uhd::stream_args_t stream_args(cpu_format); +    uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); + +    uhd::tx_metadata_t md; +    md.start_of_burst = false; +    md.end_of_burst = false; +    std::vector<samp_type> buff(samps_per_buff); +    std::ifstream infile(file.c_str(), std::ifstream::binary); + +    //loop until the entire file has been read +    while(not md.end_of_burst){ + +        infile.read((char*)&buff.front(), buff.size()*sizeof(samp_type)); +        size_t num_tx_samps = infile.gcount()/sizeof(samp_type); + +        md.end_of_burst = infile.eof(); + +        tx_stream->send(&buff.front(), num_tx_samps, md); +    } + +    infile.close(); +} + +int UHD_SAFE_MAIN(int argc, char *argv[]){ +    uhd::set_thread_priority_safe(); + +    //variables to be set by po +    std::string args, file, type, ant, subdev, ref; +    size_t spb; +    double rate, freq, gain, bw; + +    //setup the program options +    po::options_description desc("Allowed options"); +    desc.add_options() +        ("help", "help message") +        ("args", po::value<std::string>(&args)->default_value(""), "multi uhd device address args") +        ("file", po::value<std::string>(&file)->default_value("usrp_samples.dat"), "name of the file to read binary samples from") +        ("type", po::value<std::string>(&type)->default_value("short"), "sample type: double, float, or short") +        ("spb", po::value<size_t>(&spb)->default_value(10000), "samples per buffer") +        ("rate", po::value<double>(&rate), "rate of outgoing samples") +        ("freq", po::value<double>(&freq), "RF center frequency in Hz") +        ("gain", po::value<double>(&gain), "gain for the RF chain") +        ("ant", po::value<std::string>(&ant), "daughterboard antenna selection") +        ("subdev", po::value<std::string>(&subdev), "daughterboard subdevice specification") +        ("bw", po::value<double>(&bw), "daughterboard IF filter bandwidth in Hz") +        ("ref", po::value<std::string>(&ref)->default_value("internal"), "waveform type (internal, external, mimo)") +    ; +    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 samples from 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::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); + +    //Lock mboard clocks +    usrp->set_clock_source(ref); + +    //always select the subdevice first, the channel mapping affects the other settings +    if (vm.count("subdev")) usrp->set_tx_subdev_spec(subdev); + +    std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; + +    //set the sample rate +    if (not vm.count("rate")){ +        std::cerr << "Please specify the sample rate with --rate" << std::endl; +        return ~0; +    } +    std::cout << boost::format("Setting TX Rate: %f Msps...") % (rate/1e6) << std::endl; +    usrp->set_tx_rate(rate); +    std::cout << boost::format("Actual TX Rate: %f Msps...") % (usrp->get_tx_rate()/1e6) << std::endl << std::endl; + +    //set the center frequency +    if (not vm.count("freq")){ +        std::cerr << "Please specify the center frequency with --freq" << std::endl; +        return ~0; +    } +    std::cout << boost::format("Setting TX Freq: %f MHz...") % (freq/1e6) << std::endl; +    usrp->set_tx_freq(freq); +    std::cout << boost::format("Actual TX Freq: %f MHz...") % (usrp->get_tx_freq()/1e6) << std::endl << std::endl; + +    //set the rf gain +    if (vm.count("gain")){ +        std::cout << boost::format("Setting TX Gain: %f dB...") % gain << std::endl; +        usrp->set_tx_gain(gain); +        std::cout << boost::format("Actual TX Gain: %f dB...") % usrp->get_tx_gain() << std::endl << std::endl; +    } + +    //set the IF filter bandwidth +    if (vm.count("bw")){ +        std::cout << boost::format("Setting TX Bandwidth: %f MHz...") % bw << std::endl; +        usrp->set_tx_bandwidth(bw); +        std::cout << boost::format("Actual TX Bandwidth: %f MHz...") % usrp->get_tx_bandwidth() << std::endl << std::endl; +    } + +    //set the antenna +    if (vm.count("ant")) usrp->set_tx_antenna(ant); + +    boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow for some setup time + +    //Check Ref and LO Lock detect +    std::vector<std::string> sensor_names; +    sensor_names = usrp->get_tx_sensor_names(0); +    if (std::find(sensor_names.begin(), sensor_names.end(), "lo_locked") != sensor_names.end()) { +        uhd::sensor_value_t lo_locked = usrp->get_tx_sensor("lo_locked",0); +        std::cout << boost::format("Checking TX: %s ...") % lo_locked.to_pp_string() << std::endl; +        UHD_ASSERT_THROW(lo_locked.to_bool()); +    } +    sensor_names = usrp->get_mboard_sensor_names(0); +    if ((ref == "mimo") and (std::find(sensor_names.begin(), sensor_names.end(), "mimo_locked") != sensor_names.end())) { +        uhd::sensor_value_t mimo_locked = usrp->get_mboard_sensor("mimo_locked",0); +        std::cout << boost::format("Checking TX: %s ...") % mimo_locked.to_pp_string() << std::endl; +        UHD_ASSERT_THROW(mimo_locked.to_bool()); +    } +    if ((ref == "external") and (std::find(sensor_names.begin(), sensor_names.end(), "ref_locked") != sensor_names.end())) { +        uhd::sensor_value_t ref_locked = usrp->get_mboard_sensor("ref_locked",0); +        std::cout << boost::format("Checking TX: %s ...") % ref_locked.to_pp_string() << std::endl; +        UHD_ASSERT_THROW(ref_locked.to_bool()); +    } + +    //send from file +    if (type == "double") send_from_file<std::complex<double> >(usrp, "fc64", file, spb); +    else if (type == "float") send_from_file<std::complex<float> >(usrp, "fc32", file, spb); +    else if (type == "short") send_from_file<std::complex<short> >(usrp, "sc16", file, spb); +    else throw std::runtime_error("Unknown type " + type); + +    //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..3b8cc75d4 --- /dev/null +++ b/host/examples/tx_timed_samples.cpp @@ -0,0 +1,128 @@ +// +// Copyright 2010-2011 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/multi_usrp.hpp> +#include <boost/program_options.hpp> +#include <boost/thread/thread.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; +    double seconds_in_future; +    size_t total_num_samps; +    double rate; +    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<double>(&seconds_in_future)->default_value(1.5), "number of seconds in the future to transmit") +        ("nsamps", po::value<size_t>(&total_num_samps)->default_value(10000), "total number of samples to transmit") +        ("rate", po::value<double>(&rate)->default_value(100e6/16), "rate of outgoing samples") +        ("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::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); +    std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; + +    //set the tx sample rate +    std::cout << boost::format("Setting TX Rate: %f Msps...") % (rate/1e6) << std::endl; +    usrp->set_tx_rate(rate); +    std::cout << boost::format("Actual TX Rate: %f Msps...") % (usrp->get_tx_rate()/1e6) << std::endl << std::endl; + +    std::cout << boost::format("Setting device timestamp to 0...") << std::endl; +    usrp->set_time_now(uhd::time_spec_t(0.0)); + +    //create a transmit streamer +    uhd::stream_args_t stream_args("fc32"); //complex floats +    uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); + +    //allocate buffer with data to send +    std::vector<std::complex<float> > buff(tx_stream->get_max_num_samps(), std::complex<float>(ampl, ampl)); + +    //setup metadata for the first packet +    uhd::tx_metadata_t md; +    md.start_of_burst = false; +    md.end_of_burst = false; +    md.has_time_spec = true; +    md.time_spec = uhd::time_spec_t(seconds_in_future); + +    //the first call to send() will block this many seconds before sending: +    const double timeout = seconds_in_future + 0.1; //timeout (delay before transmit + padding) + +    size_t num_acc_samps = 0; //number of accumulated samples +    while(num_acc_samps < total_num_samps){ +        size_t samps_to_send = std::min(total_num_samps - num_acc_samps, buff.size()); + +        //send a single packet +        size_t num_tx_samps = tx_stream->send( +            &buff.front(), samps_to_send, md, timeout +        ); + +        //do not use time spec for subsequent packets +        md.has_time_spec = false; + +        if (num_tx_samps < samps_to_send) std::cerr << "Send timeout..." << std::endl; +        if(verbose) std::cout << boost::format("Sent packet: %u samples") % num_tx_samps << std::endl; + +        num_acc_samps += num_tx_samps; +    } + +    //send a mini EOB packet +    md.end_of_burst   = true; +    tx_stream->send("", 0, md); + +    std::cout << std::endl << "Waiting for async burst ACK... " << std::flush; +    uhd::async_metadata_t async_md; +    bool got_async_burst_ack = false; +    //loop through all messages for the ACK packet (may have underflow messages in queue) +    while (not got_async_burst_ack and usrp->get_device()->recv_async_msg(async_md, timeout)){ +        got_async_burst_ack = (async_md.event_code == uhd::async_metadata_t::EVENT_CODE_BURST_ACK); +    } +    std::cout << (got_async_burst_ack? "success" : "fail") << 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..6a377fdac --- /dev/null +++ b/host/examples/tx_waveforms.cpp @@ -0,0 +1,261 @@ +// +// Copyright 2010-2011 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/multi_usrp.hpp> +#include <uhd/exception.hpp> +#include <boost/program_options.hpp> +#include <boost/math/special_functions/round.hpp> +#include <boost/foreach.hpp> +#include <boost/format.hpp> +#include <iostream> +#include <complex> +#include <csignal> +#include <cmath> + +namespace po = boost::program_options; + +/*********************************************************************** + * Signal handlers + **********************************************************************/ +static bool stop_signal_called = false; +void sig_int_handler(int){stop_signal_called = true;} + +/*********************************************************************** + * Waveform generators + **********************************************************************/ +static const size_t wave_table_len = 8192; + +class wave_table_class{ +public: +    wave_table_class(const std::string &wave_type, const float ampl): +        _wave_table(wave_table_len) +    { +        //compute real wave table with 1.0 amplitude +        std::vector<double> real_wave_table(wave_table_len); +        if (wave_type == "CONST"){ +            for (size_t i = 0; i < wave_table_len; i++) +                real_wave_table[i] = 1.0; +        } +        else if (wave_type == "SQUARE"){ +            for (size_t i = 0; i < wave_table_len; i++) +                real_wave_table[i] = (i < wave_table_len/2)? 0.0 : 1.0; +        } +        else if (wave_type == "RAMP"){ +            for (size_t i = 0; i < wave_table_len; i++) +                real_wave_table[i] = 2.0*i/(wave_table_len-1) - 1.0; +        } +        else if (wave_type == "SINE"){ +            static const double tau = 2*std::acos(-1.0); +            for (size_t i = 0; i < wave_table_len; i++) +                real_wave_table[i] = std::sin((tau*i)/wave_table_len); +        } +        else throw std::runtime_error("unknown waveform type: " + wave_type); + +        //compute i and q pairs with 90% offset and scale to amplitude +        for (size_t i = 0; i < wave_table_len; i++){ +            const size_t q = (i+(3*wave_table_len)/4)%wave_table_len; +            _wave_table[i] = std::complex<float>(ampl*real_wave_table[i], ampl*real_wave_table[q]); +        } +    } + +    inline std::complex<float> operator()(const size_t index) const{ +        return _wave_table[index % wave_table_len]; +    } + +private: +    std::vector<std::complex<float> > _wave_table; +}; + +/*********************************************************************** + * Main function + **********************************************************************/ +int UHD_SAFE_MAIN(int argc, char *argv[]){ +    uhd::set_thread_priority_safe(); + +    //variables to be set by po +    std::string args, wave_type, ant, subdev, ref, otw; +    size_t spb; +    double rate, freq, gain, wave_freq, bw; +    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") +        ("spb", po::value<size_t>(&spb)->default_value(0), "samples per buffer, 0 for default") +        ("rate", po::value<double>(&rate), "rate of outgoing samples") +        ("freq", po::value<double>(&freq), "RF center frequency in Hz") +        ("ampl", po::value<float>(&l)->default_value(float(0.3)), "amplitude of the waveform [0 to 0.7]") +        ("gain", po::value<double>(&gain), "gain for the RF chain") +        ("ant", po::value<std::string>(&ant), "daughterboard antenna selection") +        ("subdev", po::value<std::string>(&subdev), "daughterboard subdevice specification") +        ("bw", po::value<double>(&bw), "daughterboard IF filter bandwidth in Hz") +        ("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") +        ("ref", po::value<std::string>(&ref)->default_value("internal"), "clock reference (internal, external, mimo)") +        ("otw", po::value<std::string>(&otw)->default_value("sc16"), "specify the over-the-wire sample mode") +    ; +    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::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); + +    //Lock mboard clocks +    usrp->set_clock_source(ref); + +    //always select the subdevice first, the channel mapping affects the other settings +    if (vm.count("subdev")) usrp->set_tx_subdev_spec(subdev); + +    std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; + +    //set the sample rate +    if (not vm.count("rate")){ +        std::cerr << "Please specify the sample rate with --rate" << std::endl; +        return ~0; +    } +    std::cout << boost::format("Setting TX Rate: %f Msps...") % (rate/1e6) << std::endl; +    usrp->set_tx_rate(rate); +    std::cout << boost::format("Actual TX Rate: %f Msps...") % (usrp->get_tx_rate()/1e6) << std::endl << std::endl; + +    //set the center frequency +    if (not vm.count("freq")){ +        std::cerr << "Please specify the center frequency with --freq" << std::endl; +        return ~0; +    } + +    for(size_t chan = 0; chan < usrp->get_tx_num_channels(); chan++) { +        std::cout << boost::format("Setting TX Freq: %f MHz...") % (freq/1e6) << std::endl; +        usrp->set_tx_freq(freq, chan); +        std::cout << boost::format("Actual TX Freq: %f MHz...") % (usrp->get_tx_freq(chan)/1e6) << std::endl << std::endl; + +        //set the rf gain +        if (vm.count("gain")){ +            std::cout << boost::format("Setting TX Gain: %f dB...") % gain << std::endl; +            usrp->set_tx_gain(gain, chan); +            std::cout << boost::format("Actual TX Gain: %f dB...") % usrp->get_tx_gain(chan) << std::endl << std::endl; +        } + +        //set the IF filter bandwidth +        if (vm.count("bw")){ +            std::cout << boost::format("Setting TX Bandwidth: %f MHz...") % bw << std::endl; +            usrp->set_tx_bandwidth(bw, chan); +            std::cout << boost::format("Actual TX Bandwidth: %f MHz...") % usrp->get_tx_bandwidth(chan) << std::endl << std::endl; +        } + +        //set the antenna +        if (vm.count("ant")) usrp->set_tx_antenna(ant, chan); +    } + +    //for the const wave, set the wave freq for small samples per period +    if (wave_freq == 0 and wave_type == "CONST"){ +        wave_freq = usrp->get_tx_rate()/2; +    } + +    //error when the waveform is not possible to generate +    if (std::abs(wave_freq) > usrp->get_tx_rate()/2){ +        throw std::runtime_error("wave freq out of Nyquist zone"); +    } +    if (usrp->get_tx_rate()/std::abs(wave_freq) > wave_table_len/2){ +        throw std::runtime_error("wave freq too small for table"); +    } + +    //pre-compute the waveform values +    const wave_table_class wave_table(wave_type, ampl); +    const size_t step = boost::math::iround(wave_freq/usrp->get_tx_rate() * wave_table_len); +    size_t index = 0; + +    //create a transmit streamer +    //linearly map channels (index0 = channel0, index1 = channel1, ...) +    uhd::stream_args_t stream_args("fc32", otw); +    for (size_t chan = 0; chan < usrp->get_tx_num_channels(); chan++) +        stream_args.channels.push_back(chan); //linear mapping +    uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); + +    //allocate a buffer which we re-use for each channel +    if (spb == 0) spb = tx_stream->get_max_num_samps()*10; +    std::vector<std::complex<float> > buff(spb); +    std::vector<std::complex<float> *> buffs(usrp->get_tx_num_channels(), &buff.front()); + +    //setup the metadata flags +    uhd::tx_metadata_t md; +    md.start_of_burst = true; +    md.end_of_burst   = false; +    md.has_time_spec  = true; +    md.time_spec = uhd::time_spec_t(0.1); + +    std::cout << boost::format("Setting device timestamp to 0...") << std::endl; +    usrp->set_time_now(uhd::time_spec_t(0.0)); + +    //Check Ref and LO Lock detect +    std::vector<std::string> sensor_names; +    sensor_names = usrp->get_tx_sensor_names(0); +    if (std::find(sensor_names.begin(), sensor_names.end(), "lo_locked") != sensor_names.end()) { +        uhd::sensor_value_t lo_locked = usrp->get_tx_sensor("lo_locked",0); +        std::cout << boost::format("Checking TX: %s ...") % lo_locked.to_pp_string() << std::endl; +        UHD_ASSERT_THROW(lo_locked.to_bool()); +    } +    sensor_names = usrp->get_mboard_sensor_names(0); +    if ((ref == "mimo") and (std::find(sensor_names.begin(), sensor_names.end(), "mimo_locked") != sensor_names.end())) { +        uhd::sensor_value_t mimo_locked = usrp->get_mboard_sensor("mimo_locked",0); +        std::cout << boost::format("Checking TX: %s ...") % mimo_locked.to_pp_string() << std::endl; +        UHD_ASSERT_THROW(mimo_locked.to_bool()); +    } +    if ((ref == "external") and (std::find(sensor_names.begin(), sensor_names.end(), "ref_locked") != sensor_names.end())) { +        uhd::sensor_value_t ref_locked = usrp->get_mboard_sensor("ref_locked",0); +        std::cout << boost::format("Checking TX: %s ...") % ref_locked.to_pp_string() << std::endl; +        UHD_ASSERT_THROW(ref_locked.to_bool()); +    } + +    std::signal(SIGINT, &sig_int_handler); +    std::cout << "Press Ctrl + C to stop streaming..." << std::endl; + +    //send data until the signal handler gets called +    while(not stop_signal_called){ +        //fill the buffer with the waveform +        for (size_t n = 0; n < buff.size(); n++){ +            buff[n] = wave_table(index += step); +        } + +        //send the entire contents of the buffer +        tx_stream->send(buffs, buff.size(), md); + +        md.start_of_burst = false; +        md.has_time_spec = false; +    } + +    //send a mini EOB packet +    md.end_of_burst = true; +    tx_stream->send("", 0, md); + +    //finished +    std::cout << std::endl << "Done!" << std::endl << std::endl; +    return 0; +} | 
