aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--host/include/uhd/usrp/CMakeLists.txt1
-rw-r--r--host/include/uhd/usrp/fe_connection.hpp127
-rw-r--r--host/lib/usrp/CMakeLists.txt1
-rw-r--r--host/lib/usrp/fe_connection.cpp67
-rw-r--r--host/tests/CMakeLists.txt1
-rw-r--r--host/tests/fe_conn_test.cpp108
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);
+}