diff options
| author | Ashish Chaudhari <ashish@ettus.com> | 2016-03-09 16:19:34 -0800 | 
|---|---|---|
| committer | Ashish Chaudhari <ashish@ettus.com> | 2016-03-21 17:46:12 -0700 | 
| commit | 9d6c0b7f875ac24c284e355ef54f52ed41f8137e (patch) | |
| tree | 77238a91c46ff078c0b56aa543de64e3667881ed /host | |
| parent | 98dba897268a8c7f6e2eaf807c83502b8835f6da (diff) | |
| download | uhd-9d6c0b7f875ac24c284e355ef54f52ed41f8137e.tar.gz uhd-9d6c0b7f875ac24c284e355ef54f52ed41f8137e.tar.bz2 uhd-9d6c0b7f875ac24c284e355ef54f52ed41f8137e.zip | |
usrp: Added fe_connection type and unit test
- Wraps a sampling mode and IF frequency
- Built-in parser to deduce swap,invert,mode bits from string connection
Diffstat (limited to 'host')
| -rw-r--r-- | host/include/uhd/usrp/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/include/uhd/usrp/fe_connection.hpp | 127 | ||||
| -rw-r--r-- | host/lib/usrp/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/lib/usrp/fe_connection.cpp | 67 | ||||
| -rw-r--r-- | host/tests/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/tests/fe_conn_test.cpp | 108 | 
6 files changed, 305 insertions, 0 deletions
| diff --git a/host/include/uhd/usrp/CMakeLists.txt b/host/include/uhd/usrp/CMakeLists.txt index 2d3717357..fd37ef560 100644 --- a/host/include/uhd/usrp/CMakeLists.txt +++ b/host/include/uhd/usrp/CMakeLists.txt @@ -18,6 +18,7 @@  UHD_INSTALL(FILES      #### dboard headers ### +    fe_connection.hpp      dboard_base.hpp      dboard_eeprom.hpp      dboard_id.hpp diff --git a/host/include/uhd/usrp/fe_connection.hpp b/host/include/uhd/usrp/fe_connection.hpp new file mode 100644 index 000000000..969246087 --- /dev/null +++ b/host/include/uhd/usrp/fe_connection.hpp @@ -0,0 +1,127 @@ +// +// Copyright 2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_USRP_FE_CONNECTION_HPP +#define INCLUDED_UHD_USRP_FE_CONNECTION_HPP + +#include <uhd/config.hpp> +#include <boost/operators.hpp> +#include <string> + +namespace uhd { namespace usrp { + +    class UHD_API fe_connection_t : boost::equality_comparable<fe_connection_t> { +    public: +        /** Sampling mode. +         *  Represents the sampling architecture for the front-end +         */ +        enum sampling_t { +            QUADRATURE, /**< Complex sampling (Complex input, Complex output). */ +            HETERODYNE, /**< Heterodyne sampling (Real input, Complex output). Only one of the I and Q inputs is used. */ +            REAL        /**< Real sampling (Real input, Real output). Only one of the I and Q inputs is used. */ +        }; + +        /*! +         * Create a frontend connection class from individual settings. +         * \param sampling_mode can be { QUADRATURE, HETERODYNE, REAL } +         * \param iq_swapped indicates if the IQ channels are swapped (after inverion and heterodyne correction) +         * \param i_inverted indicates if the I channel is inverted (negated) +         * \param q_inverted indicates if the Q channel is inverted (negated) +         * \param if_freq the baseband sampling frequency. +         */ +        fe_connection_t( +            sampling_t sampling_mode, bool iq_swapped, +            bool i_inverted, bool q_inverted, double if_freq = 0.0 +        ); + +        /*! +         * Create a frontend connection class from a connection string +         * The connection string can be: +         * - in {I, Q}: Real mode sampling with no inversion. +         * - in {Ib, Qb}: Real mode sampling with inversion. +         * - in {IQ, QI}: Quadrature sampling with no inversion. +         * - in {IbQb, QbIb}: Quadrature sampling with inversion. +         * - in {II, QQ}: Heterodyne sampling with no inversion. +         * - in {IbIb, QbQb}: Heterodyne sampling with inversion. +         * +         * \param conn_str the connection string. +         * \param if_freq the baseband sampling frequency. +         */ +        fe_connection_t(const std::string& conn_str, double if_freq = 0.0); + +        /*! +         * Accessor for sampling mode +         */ +        inline sampling_t get_sampling_mode() const { +            return _sampling_mode; +        } + +        /*! +         * Accessor for IQ swap parameter +         */ +        inline bool is_iq_swapped() const { +            return _iq_swapped; +        } + +        /*! +         * Accessor for I inversion parameter +         */ +        inline bool is_i_inverted() const { +            return _i_inverted; +        } + +        /*! +         * Accessor for Q inversion parameter +         */ +        inline bool is_q_inverted() const { +            return _q_inverted; +        } + +        /*! +         * Accessor for IF frequency +         */ +        inline double get_if_freq() const { +            return _if_freq; +        } + +        /*! +         * Mutator for IF frequency +         */ +        inline void set_if_freq(double freq) { +            _if_freq = freq; +        } + +    private: +        sampling_t  _sampling_mode; +        bool        _iq_swapped; +        bool        _i_inverted; +        bool        _q_inverted; +        double      _if_freq; +    }; + +    /*! +     * Comparator operator overloaded for fe_connection_t. +     * The boost::equality_comparable provides the !=. +     * \param lhs the fe_connection_t to the left of the operator +     * \param rhs the fe_connection_t to the right of the operator +     * \return true when the fe connections are equal +     */ +    UHD_API bool operator==(const fe_connection_t &lhs, const fe_connection_t &rhs); + +}} //namespace + +#endif /* INCLUDED_UHD_USRP_FE_CONNECTION_HPP */ diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt index 71e40395e..dde4f02c3 100644 --- a/host/lib/usrp/CMakeLists.txt +++ b/host/lib/usrp/CMakeLists.txt @@ -29,6 +29,7 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/mboard_eeprom.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/multi_usrp.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/subdev_spec.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/fe_connection.cpp  )  IF(ENABLE_C_API) diff --git a/host/lib/usrp/fe_connection.cpp b/host/lib/usrp/fe_connection.cpp new file mode 100644 index 000000000..071f5ecf2 --- /dev/null +++ b/host/lib/usrp/fe_connection.cpp @@ -0,0 +1,67 @@ +// +// Copyright 2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/usrp/fe_connection.hpp> +#include <uhd/exception.hpp> +#include <boost/regex.hpp> +#include <uhd/utils/math.hpp> + +using namespace uhd::usrp; + +fe_connection_t::fe_connection_t( +    sampling_t sampling_mode, bool iq_swapped, +    bool i_inverted, bool q_inverted, double if_freq +) : _sampling_mode(sampling_mode), _iq_swapped(iq_swapped), +    _i_inverted(i_inverted), _q_inverted(q_inverted), _if_freq(if_freq) +{ +} + +fe_connection_t::fe_connection_t(const std::string& conn_str, double if_freq) { +    static const boost::regex conn_regex("([IQ])(b?)(([IQ])(b?))?"); +    boost::cmatch matches; +    if (boost::regex_match(conn_str.c_str(), matches, conn_regex)) { +        if (matches[3].length() == 0) { +            //Connection in {I, Q, Ib, Qb} +            _sampling_mode = REAL; +            _iq_swapped = (matches[1].str() == "Q"); +            _i_inverted = (matches[2].length() != 0); +            _q_inverted = false;    //IQ is swapped after inversion +        } else { +            //Connection in {I(b?)Q(b?), Q(b?)I(b?), I(b?)I(b?), Q(b?)Q(b?)} +            _sampling_mode = (matches[1].str() == matches[4].str()) ? HETERODYNE : QUADRATURE; +            _iq_swapped = (matches[1].str() == "Q"); +            size_t i_idx = _iq_swapped ? 5 : 2, q_idx = _iq_swapped ? 2 : 5; +            _i_inverted = (matches[i_idx].length() != 0); +            _q_inverted = (matches[q_idx].length() != 0); + +            if (_sampling_mode == HETERODYNE and _i_inverted != _q_inverted) { +                throw uhd::value_error("Invalid connection string: " + conn_str); +            } +        } +        _if_freq = if_freq; +    } else { +        throw uhd::value_error("Invalid connection string: " + conn_str); +    } +} + +bool uhd::usrp::operator==(const fe_connection_t &lhs, const fe_connection_t &rhs){ +    return ((lhs.get_sampling_mode() == rhs.get_sampling_mode()) and +            (lhs.is_iq_swapped() == rhs.is_iq_swapped()) and +            (lhs.is_i_inverted() == rhs.is_i_inverted()) and +            (lhs.is_q_inverted() == rhs.is_q_inverted()) and +            uhd::math::frequencies_are_equal(lhs.get_if_freq(), rhs.get_if_freq())); +} diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt index 14c8daa09..c5f25913e 100644 --- a/host/tests/CMakeLists.txt +++ b/host/tests/CMakeLists.txt @@ -46,6 +46,7 @@ SET(test_sources      time_spec_test.cpp      vrt_test.cpp      expert_test.cpp +    fe_conn_test.cpp  )  #turn each test cpp file into an executable with an int main() function diff --git a/host/tests/fe_conn_test.cpp b/host/tests/fe_conn_test.cpp new file mode 100644 index 000000000..b8e69816a --- /dev/null +++ b/host/tests/fe_conn_test.cpp @@ -0,0 +1,108 @@ +// +// Copyright 2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/usrp/fe_connection.hpp> +#include <uhd/exception.hpp> +#include <boost/test/unit_test.hpp> + +using namespace uhd::usrp; + +BOOST_AUTO_TEST_CASE(test_quardrature){ +    fe_connection_t IQ("IQ"), QI("QI"), IbQ("IbQ"), QbI("QbI"), QbIb("QbIb"); +    BOOST_CHECK(IQ.get_sampling_mode()==fe_connection_t::QUADRATURE); +    BOOST_CHECK(QI.get_sampling_mode()==fe_connection_t::QUADRATURE); +    BOOST_CHECK(IbQ.get_sampling_mode()==fe_connection_t::QUADRATURE); +    BOOST_CHECK(QbI.get_sampling_mode()==fe_connection_t::QUADRATURE); +    BOOST_CHECK(QbIb.get_sampling_mode()==fe_connection_t::QUADRATURE); + +    BOOST_CHECK(not IQ.is_iq_swapped()); +    BOOST_CHECK(QI.is_iq_swapped()); +    BOOST_CHECK(not IbQ.is_iq_swapped()); +    BOOST_CHECK(QbI.is_iq_swapped()); +    BOOST_CHECK(QbIb.is_iq_swapped()); + +    BOOST_CHECK(not IQ.is_i_inverted()); +    BOOST_CHECK(not QI.is_i_inverted()); +    BOOST_CHECK(IbQ.is_i_inverted()); +    BOOST_CHECK(not QbI.is_i_inverted()); +    BOOST_CHECK(QbIb.is_i_inverted()); + +    BOOST_CHECK(not IQ.is_q_inverted()); +    BOOST_CHECK(not QI.is_q_inverted()); +    BOOST_CHECK(not IbQ.is_q_inverted()); +    BOOST_CHECK(QbI.is_q_inverted()); +    BOOST_CHECK(QbIb.is_q_inverted()); +} + +BOOST_AUTO_TEST_CASE(test_heterodyne){ +    fe_connection_t II("II"), QQ("QQ"), IbIb("IbIb"), QbQb("QbQb"); +    BOOST_CHECK(II.get_sampling_mode()==fe_connection_t::HETERODYNE); +    BOOST_CHECK(QQ.get_sampling_mode()==fe_connection_t::HETERODYNE); +    BOOST_CHECK(IbIb.get_sampling_mode()==fe_connection_t::HETERODYNE); +    BOOST_CHECK(QbQb.get_sampling_mode()==fe_connection_t::HETERODYNE); + +    BOOST_CHECK(not II.is_iq_swapped()); +    BOOST_CHECK(QQ.is_iq_swapped()); +    BOOST_CHECK(not IbIb.is_iq_swapped()); +    BOOST_CHECK(QbQb.is_iq_swapped()); + +    BOOST_CHECK(not II.is_i_inverted()); +    BOOST_CHECK(not QQ.is_i_inverted()); +    BOOST_CHECK(IbIb.is_i_inverted()); +    BOOST_CHECK(QbQb.is_i_inverted()); + +    BOOST_CHECK(not II.is_q_inverted()); +    BOOST_CHECK(not QQ.is_q_inverted()); +    BOOST_CHECK(IbIb.is_q_inverted()); +    BOOST_CHECK(QbQb.is_q_inverted()); + +    BOOST_CHECK_THROW(fe_connection_t dummy("IIb"), uhd::value_error); +    BOOST_CHECK_THROW(fe_connection_t dummy("IbI"), uhd::value_error); +    BOOST_CHECK_THROW(fe_connection_t dummy("QQb"), uhd::value_error); +    BOOST_CHECK_THROW(fe_connection_t dummy("QbQ"), uhd::value_error); +} + +BOOST_AUTO_TEST_CASE(test_real){ +    fe_connection_t I("I"), Q("Q"), Ib("Ib"), Qb("Qb"); +    BOOST_CHECK(I.get_sampling_mode()==fe_connection_t::REAL); +    BOOST_CHECK(Q.get_sampling_mode()==fe_connection_t::REAL); +    BOOST_CHECK(Ib.get_sampling_mode()==fe_connection_t::REAL); +    BOOST_CHECK(Qb.get_sampling_mode()==fe_connection_t::REAL); + +    BOOST_CHECK(not I.is_iq_swapped()); +    BOOST_CHECK(Q.is_iq_swapped()); +    BOOST_CHECK(not Ib.is_iq_swapped()); +    BOOST_CHECK(Qb.is_iq_swapped()); + +    BOOST_CHECK(not I.is_i_inverted()); +    BOOST_CHECK(not Q.is_i_inverted()); +    BOOST_CHECK(Ib.is_i_inverted()); +    BOOST_CHECK(Qb.is_i_inverted()); + +    BOOST_CHECK(not I.is_q_inverted()); +    BOOST_CHECK(not Q.is_q_inverted()); +    BOOST_CHECK(not Ib.is_q_inverted()); +    BOOST_CHECK(not Qb.is_q_inverted()); +} + +BOOST_AUTO_TEST_CASE(test_invalid){ +    BOOST_CHECK_THROW(fe_connection_t dummy("blah"), uhd::value_error); +    BOOST_CHECK_THROW(fe_connection_t dummy("123456"), uhd::value_error); +    BOOST_CHECK_THROW(fe_connection_t dummy("ii"), uhd::value_error); +    BOOST_CHECK_THROW(fe_connection_t dummy("qb"), uhd::value_error); +    BOOST_CHECK_THROW(fe_connection_t dummy("IIIQ"), uhd::value_error); +} | 
