aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAshish Chaudhari <ashish@ettus.com>2016-07-19 09:24:46 -0700
committerMartin Braun <martin.braun@ettus.com>2016-07-19 09:24:46 -0700
commit890a8e10d65dd35e2ad3449bf3a3dce79ccb6083 (patch)
tree247198e868e8a68784ba5d248b1727c276765fdb
parent3cc87cba98686638bd04fbd08597ed00d24f2177 (diff)
downloaduhd-890a8e10d65dd35e2ad3449bf3a3dce79ccb6083.tar.gz
uhd-890a8e10d65dd35e2ad3449bf3a3dce79ccb6083.tar.bz2
uhd-890a8e10d65dd35e2ad3449bf3a3dce79ccb6083.zip
Added TwinRX daughterboard control.
-rw-r--r--host/lib/usrp/dboard/CMakeLists.txt4
-rw-r--r--host/lib/usrp/dboard/db_twinrx.cpp337
-rw-r--r--host/lib/usrp/dboard/twinrx/table_to_cpp.py26
-rw-r--r--host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp643
-rw-r--r--host/lib/usrp/dboard/twinrx/twinrx_ctrl.hpp101
-rw-r--r--host/lib/usrp/dboard/twinrx/twinrx_experts.cpp637
-rw-r--r--host/lib/usrp/dboard/twinrx/twinrx_experts.hpp630
-rw-r--r--host/lib/usrp/dboard/twinrx/twinrx_gain_tables.cpp860
-rw-r--r--host/lib/usrp/dboard/twinrx/twinrx_gain_tables.hpp83
-rw-r--r--host/lib/usrp/dboard/twinrx/twinrx_io.hpp524
10 files changed, 3845 insertions, 0 deletions
diff --git a/host/lib/usrp/dboard/CMakeLists.txt b/host/lib/usrp/dboard/CMakeLists.txt
index 6cebecdbf..183496734 100644
--- a/host/lib/usrp/dboard/CMakeLists.txt
+++ b/host/lib/usrp/dboard/CMakeLists.txt
@@ -39,5 +39,9 @@ LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/db_dbsrx2.cpp
${CMAKE_CURRENT_SOURCE_DIR}/db_tvrx2.cpp
${CMAKE_CURRENT_SOURCE_DIR}/db_e3x0.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/twinrx/twinrx_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/twinrx/twinrx_experts.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/twinrx/twinrx_gain_tables.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/db_twinrx.cpp
)
diff --git a/host/lib/usrp/dboard/db_twinrx.cpp b/host/lib/usrp/dboard/db_twinrx.cpp
new file mode 100644
index 000000000..e960f134f
--- /dev/null
+++ b/host/lib/usrp/dboard/db_twinrx.cpp
@@ -0,0 +1,337 @@
+//
+// Copyright 2014-15 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 "twinrx/twinrx_experts.hpp"
+#include "twinrx/twinrx_ctrl.hpp"
+#include "twinrx/twinrx_io.hpp"
+#include <expert_factory.hpp>
+#include <uhd/types/device_addr.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/usrp/dboard_base.hpp>
+#include <uhd/usrp/dboard_manager.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/static.hpp>
+#include "dboard_ctor_args.hpp"
+#include <boost/assign/list_of.hpp>
+#include <boost/make_shared.hpp>
+#include <boost/thread.hpp>
+#include <boost/thread/mutex.hpp>
+//#include <fstream> //Needed for _expert->to_dot() below
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::usrp::dboard::twinrx;
+using namespace uhd::experts;
+
+static const dboard_id_t TWINRX_V100_000_ID(0x91);
+
+/*!
+ * twinrx_rcvr_fe is the dbaord class (dboard_base) that
+ * represents each front-end of a TwinRX board. UHD will
+ * create and hold two instances of this class per TwinRX
+ * dboard.
+ *
+ */
+class twinrx_rcvr_fe : public rx_dboard_base
+{
+public:
+ twinrx_rcvr_fe(
+ ctor_args_t args,
+ expert_container::sptr expert,
+ twinrx_ctrl::sptr ctrl
+ ) :
+ rx_dboard_base(args), _expert(expert), _ctrl(ctrl),
+ _ch_name(dboard_ctor_args_t::cast(args).sd_name)
+ {
+ //---------------------------------------------------------
+ // Add user-visible, channel specific properties to front-end tree
+ //---------------------------------------------------------
+
+ //Generic
+ get_rx_subtree()->create<std::string>("name")
+ .set("TwinRX RX" + _ch_name);
+ get_rx_subtree()->create<bool>("use_lo_offset")
+ .set(false);
+ get_rx_subtree()->create<std::string>("connection")
+ .set(_ch_name == "0" ? "II" : "QQ"); //Ch->ADC port mapping
+ static const double BW = 80e6;
+ get_rx_subtree()->create<double>("bandwidth/value")
+ .set(BW);
+ get_rx_subtree()->create<meta_range_t>("bandwidth/range")
+ .set(freq_range_t(BW, BW));
+
+ //Frequency Specific
+ get_rx_subtree()->create<meta_range_t>("freq/range")
+ .set(freq_range_t(10e6, 6.0e9));
+ expert_factory::add_dual_prop_node<double>(_expert, get_rx_subtree(),
+ "freq/value", prepend_ch("freq/desired", _ch_name), prepend_ch("freq/coerced", _ch_name),
+ 1.0e9, AUTO_RESOLVE_ON_READ_WRITE);
+ get_rx_subtree()->create<device_addr_t>("tune_args")
+ .set(device_addr_t());
+
+ static const double DEFAULT_IF_FREQ = 150e6;
+ meta_range_t if_freq_range;
+ if_freq_range.push_back(range_t(-DEFAULT_IF_FREQ-(BW/2), -DEFAULT_IF_FREQ+(BW/2)));
+ if_freq_range.push_back(range_t( DEFAULT_IF_FREQ-(BW/2), DEFAULT_IF_FREQ+(BW/2)));
+ get_rx_subtree()->create<meta_range_t>("if_freq/range")
+ .set(if_freq_range);
+ expert_factory::add_dual_prop_node<double>(_expert, get_rx_subtree(),
+ "if_freq/value", prepend_ch("if_freq/desired", _ch_name), prepend_ch("if_freq/coerced", _ch_name),
+ DEFAULT_IF_FREQ, AUTO_RESOLVE_ON_WRITE);
+
+ //LO Specific
+ get_rx_subtree()->create<meta_range_t>("los/LO1/freq/range")
+ .set(freq_range_t(2.0e9, 6.8e9));
+ expert_factory::add_dual_prop_node<double>(_expert, get_rx_subtree(),
+ "los/LO1/freq/value", prepend_ch("los/LO1/freq/desired", _ch_name), prepend_ch("los/LO1/freq/coerced", _ch_name),
+ 0.0, AUTO_RESOLVE_ON_READ_WRITE);
+ get_rx_subtree()->create<meta_range_t>("los/LO2/freq/range")
+ .set(freq_range_t(1.0e9, 3.0e9));
+ expert_factory::add_dual_prop_node<double>(_expert, get_rx_subtree(),
+ "los/LO2/freq/value", prepend_ch("los/LO2/freq/desired", _ch_name), prepend_ch("los/LO2/freq/coerced", _ch_name),
+ 0.0, AUTO_RESOLVE_ON_READ_WRITE);
+ get_rx_subtree()->create<std::vector<std::string> >("los/all/source/options")
+ .set(boost::assign::list_of("internal")("external")("companion")("disabled"));
+ expert_factory::add_prop_node<std::string>(_expert, get_rx_subtree(),
+ "los/all/source/value", prepend_ch("los/all/source", _ch_name),
+ "internal", AUTO_RESOLVE_ON_WRITE);
+ expert_factory::add_prop_node<bool>(_expert, get_rx_subtree(),
+ "los/all/export", prepend_ch("los/all/export", _ch_name),
+ false, AUTO_RESOLVE_ON_WRITE);
+
+ //Gain Specific
+ get_rx_subtree()->create<meta_range_t>("gains/all/range")
+ .set(gain_range_t(0, 95, double(1.0)));
+ expert_factory::add_prop_node<double>(_expert, get_rx_subtree(),
+ "gains/all/value", prepend_ch("gain", _ch_name),
+ 0.0, AUTO_RESOLVE_ON_WRITE);
+ get_rx_subtree()->create<std::vector<std::string> >("gains/all/profile/options")
+ .set(boost::assign::list_of("low-noise")("low-distortion")("default"));
+ expert_factory::add_prop_node<std::string>(_expert, get_rx_subtree(),
+ "gains/all/profile/value", prepend_ch("gain_profile", _ch_name),
+ "default", AUTO_RESOLVE_ON_WRITE);
+
+ //Antenna Specific
+ get_rx_subtree()->create<std::vector<std::string> >("antenna/options")
+ .set(boost::assign::list_of("RX1")("RX2"));
+ expert_factory::add_prop_node<std::string>(_expert, get_rx_subtree(),
+ "antenna/value", prepend_ch("antenna", _ch_name),
+ (_ch_name == "0" ? "RX1" : "RX2"), AUTO_RESOLVE_ON_WRITE);
+ expert_factory::add_prop_node<bool>(_expert, get_rx_subtree(),
+ "enabled", prepend_ch("enabled", _ch_name),
+ false, AUTO_RESOLVE_ON_WRITE);
+
+ //Readback
+ get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked")
+ .set_publisher(boost::bind(&twinrx_rcvr_fe::get_lo_locked, this));
+
+ //---------------------------------------------------------
+ // Add internal channel-specific data nodes to expert
+ //---------------------------------------------------------
+ expert_factory::add_data_node<lo_inj_side_t>(_expert,
+ prepend_ch("ch/LO1/inj_side", _ch_name), INJ_LOW_SIDE);
+ expert_factory::add_data_node<lo_inj_side_t>(_expert,
+ prepend_ch("ch/LO2/inj_side", _ch_name), INJ_LOW_SIDE);
+ expert_factory::add_data_node<twinrx_ctrl::signal_path_t>(_expert,
+ prepend_ch("ch/signal_path", _ch_name), twinrx_ctrl::PATH_LOWBAND);
+ expert_factory::add_data_node<twinrx_ctrl::preselector_path_t>(_expert,
+ prepend_ch("ch/lb_presel", _ch_name), twinrx_ctrl::PRESEL_PATH1);
+ expert_factory::add_data_node<twinrx_ctrl::preselector_path_t>(_expert,
+ prepend_ch("ch/hb_presel", _ch_name), twinrx_ctrl::PRESEL_PATH1);
+ expert_factory::add_data_node<bool>(_expert,
+ prepend_ch("ch/lb_preamp_presel", _ch_name), false);
+ expert_factory::add_data_node<bool>(_expert,
+ prepend_ch("ant/lb_preamp_presel", _ch_name), false);
+ expert_factory::add_data_node<twinrx_ctrl::preamp_state_t>(_expert,
+ prepend_ch("ch/preamp1", _ch_name), twinrx_ctrl::PREAMP_BYPASS);
+ expert_factory::add_data_node<twinrx_ctrl::preamp_state_t>(_expert,
+ prepend_ch("ant/preamp1", _ch_name), twinrx_ctrl::PREAMP_BYPASS);
+ expert_factory::add_data_node<bool>(_expert,
+ prepend_ch("ch/preamp2", _ch_name), false);
+ expert_factory::add_data_node<bool>(_expert,
+ prepend_ch("ant/preamp2", _ch_name), false);
+ expert_factory::add_data_node<boost::uint8_t>(_expert,
+ prepend_ch("ch/input_atten", _ch_name), 0);
+ expert_factory::add_data_node<boost::uint8_t>(_expert,
+ prepend_ch("ant/input_atten", _ch_name), 0);
+ expert_factory::add_data_node<boost::uint8_t>(_expert,
+ prepend_ch("ch/lb_atten", _ch_name), 0);
+ expert_factory::add_data_node<boost::uint8_t>(_expert,
+ prepend_ch("ch/hb_atten", _ch_name), 0);
+ expert_factory::add_data_node<twinrx_ctrl::lo_source_t>(_expert,
+ prepend_ch("ch/LO1/source", _ch_name), twinrx_ctrl::LO_INTERNAL);
+ expert_factory::add_data_node<twinrx_ctrl::lo_source_t>(_expert,
+ prepend_ch("ch/LO2/source", _ch_name), twinrx_ctrl::LO_INTERNAL);
+ expert_factory::add_data_node<lo_synth_mapping_t>(_expert,
+ prepend_ch("synth/LO1/mapping", _ch_name), MAPPING_NONE);
+ expert_factory::add_data_node<lo_synth_mapping_t>(_expert,
+ prepend_ch("synth/LO2/mapping", _ch_name), MAPPING_NONE);
+
+ }
+
+ virtual ~twinrx_rcvr_fe(void)
+ {
+ }
+
+ sensor_value_t get_lo_locked()
+ {
+ bool locked = true;
+ twinrx_ctrl::channel_t ch = (_ch_name == "0") ? twinrx_ctrl::CH1 : twinrx_ctrl::CH2;
+ locked &= _ctrl->read_lo1_locked(ch);
+ locked &= _ctrl->read_lo2_locked(ch);
+ return sensor_value_t("LO", locked, "locked", "unlocked");
+ }
+
+private:
+ expert_container::sptr _expert;
+ twinrx_ctrl::sptr _ctrl;
+ const std::string _ch_name;
+};
+
+/*!
+ * twinrx_rcvr is the top-level container for each
+ * TwinRX board. UHD will hold one instance of this
+ * class per TwinRX dboard. This class is responsible
+ * for owning all the control classes for the board.
+ *
+ */
+class twinrx_rcvr : public rx_dboard_base
+{
+public:
+ typedef boost::shared_ptr<twinrx_rcvr> sptr;
+
+ twinrx_rcvr(ctor_args_t args) : rx_dboard_base(args)
+ {
+ _db_iface = get_iface();
+ twinrx_gpio::sptr gpio_iface = boost::make_shared<twinrx_gpio>(_db_iface);
+ twinrx_cpld_regmap::sptr cpld_regs = boost::make_shared<twinrx_cpld_regmap>();
+ cpld_regs->initialize(*gpio_iface, false);
+ _ctrl = twinrx_ctrl::make(_db_iface, gpio_iface, cpld_regs);
+ _expert = expert_factory::create_container("twinrx_expert");
+ }
+
+ virtual ~twinrx_rcvr(void)
+ {
+ }
+
+ inline expert_container::sptr get_expert() {
+ return _expert;
+ }
+
+ inline twinrx_ctrl::sptr get_ctrl() {
+ return _ctrl;
+ }
+
+ virtual void initialize()
+ {
+ //---------------------------------------------------------
+ // Add internal channel-agnostic data nodes to expert
+ //---------------------------------------------------------
+ expert_factory::add_data_node<twinrx_ctrl::lo_export_source_t>(_expert,
+ "com/LO1/export_source", twinrx_ctrl::LO_EXPORT_DISABLED);
+ expert_factory::add_data_node<twinrx_ctrl::lo_export_source_t>(_expert,
+ "com/LO2/export_source", twinrx_ctrl::LO_EXPORT_DISABLED);
+ expert_factory::add_data_node<twinrx_ctrl::antenna_mapping_t>(_expert,
+ "com/ant_mapping", twinrx_ctrl::ANTX_NATIVE);
+ expert_factory::add_data_node<twinrx_ctrl::cal_mode_t>(_expert,
+ "com/cal_mode", twinrx_ctrl::CAL_DISABLED);
+ expert_factory::add_data_node<bool>(_expert,
+ "com/synth/LO1/hopping_enabled", false);
+ expert_factory::add_data_node<bool>(_expert,
+ "com/synth/LO2/hopping_enabled", false);
+
+ //---------------------------------------------------------
+ // Add workers to expert
+ //---------------------------------------------------------
+ //Channel (front-end) specific
+ BOOST_FOREACH(const std::string& fe, _fe_names) {
+ expert_factory::add_worker_node<twinrx_freq_path_expert>(_expert, _expert->node_retriever(), fe);
+ expert_factory::add_worker_node<twinrx_freq_coercion_expert>(_expert, _expert->node_retriever(), fe);
+ expert_factory::add_worker_node<twinrx_chan_gain_expert>(_expert, _expert->node_retriever(), fe);
+ expert_factory::add_worker_node<twinrx_nyquist_expert>(_expert, _expert->node_retriever(), fe, _db_iface);
+ }
+
+ //Channel (front-end) agnostic
+ expert_factory::add_worker_node<twinrx_lo_config_expert>(_expert, _expert->node_retriever());
+ expert_factory::add_worker_node<twinrx_lo_mapping_expert>(_expert, _expert->node_retriever(), STAGE_LO1);
+ expert_factory::add_worker_node<twinrx_lo_mapping_expert>(_expert, _expert->node_retriever(), STAGE_LO2);
+ expert_factory::add_worker_node<twinrx_antenna_expert>(_expert, _expert->node_retriever());
+ expert_factory::add_worker_node<twinrx_ant_gain_expert>(_expert, _expert->node_retriever());
+ expert_factory::add_worker_node<twinrx_settings_expert>(_expert, _expert->node_retriever(), _ctrl);
+
+ /*//Expert debug code
+ std::ofstream dot_file("/tmp/twinrx.dot", std::ios::out);
+ dot_file << _expert->to_dot();
+ dot_file.close();
+ */
+
+ _expert->debug_audit();
+ _expert->resolve_all(true);
+ }
+
+ static dboard_base::sptr make_twinrx_fe(dboard_base::ctor_args_t args)
+ {
+ const dboard_ctor_args_t& db_args = dboard_ctor_args_t::cast(args);
+ sptr container = boost::dynamic_pointer_cast<twinrx_rcvr>(db_args.rx_container);
+ if (container) {
+ dboard_base::sptr fe = dboard_base::sptr(
+ new twinrx_rcvr_fe(args, container->get_expert(), container->get_ctrl()));
+ container->add_twinrx_fe(db_args.sd_name);
+ return fe;
+ } else {
+ throw uhd::assertion_error("error creating twinrx frontend");
+ }
+ }
+
+protected:
+ inline void add_twinrx_fe(const std::string& name) {
+ _fe_names.push_back(name);
+ }
+
+private:
+ typedef std::map<std::string, dboard_base::sptr> twinrx_fe_map_t;
+
+ dboard_iface::sptr _db_iface;
+ twinrx_ctrl::sptr _ctrl;
+ std::vector<std::string> _fe_names;
+ expert_container::sptr _expert;
+};
+
+/*!
+ * Initialization Sequence for each TwinRX board:
+ * - make_twinrx_container is called which creates an instance of twinrx_rcvr
+ * - twinrx_rcvr::make_twinrx_fe is called with channel "0" which creates an instance of twinrx_rcvr_fe
+ * - twinrx_rcvr::make_twinrx_fe is called with channel "1" which creates an instance of twinrx_rcvr_fe
+ * - twinrx_rcvr::initialize is called with finishes the init sequence
+ *
+ */
+static dboard_base::sptr make_twinrx_container(dboard_base::ctor_args_t args)
+{
+ return dboard_base::sptr(new twinrx_rcvr(args));
+}
+
+UHD_STATIC_BLOCK(reg_twinrx_dboards)
+{
+ dboard_manager::register_dboard_restricted(
+ TWINRX_V100_000_ID,
+ &twinrx_rcvr::make_twinrx_fe,
+ "TwinRX v1.0",
+ boost::assign::list_of("0")("1"),
+ &make_twinrx_container
+ );
+}
diff --git a/host/lib/usrp/dboard/twinrx/table_to_cpp.py b/host/lib/usrp/dboard/twinrx/table_to_cpp.py
new file mode 100644
index 000000000..ea0e3bf66
--- /dev/null
+++ b/host/lib/usrp/dboard/twinrx/table_to_cpp.py
@@ -0,0 +1,26 @@
+#!/usr/bin/python
+
+import sys
+
+if len(sys.argv) != 3:
+ print 'Usage: ' + sys.argv[0] + ' <table filename> <table name>'
+ sys.exit(1)
+
+with open(sys.argv[1], 'rb') as f:
+ print ('static const std::vector<twinrx_gain_config_t> ' +
+ sys.argv[2] + ' = boost::assign::list_of')
+ i = -1
+ for line in f.readlines():
+ if (i != -1):
+ row = line.strip().split(',')
+ print (' ( twinrx_gain_config_t( %s, %6.1f, %s, %s, %s, %s ) )' % (
+ str(i).rjust(5),
+ float(row[0].rjust(6)),
+ row[1].rjust(6), row[2].rjust(6),
+ ('true' if int(row[3]) == 1 else 'false').rjust(5),
+ ('true' if int(row[4]) == 1 else 'false').rjust(5)))
+ else:
+ print (' // %s, %s, %s, %s, %s, %s' % (
+ 'Index', ' Gain', 'Atten1', 'Atten2', ' Amp1', ' Amp2'))
+ i += 1
+ print ';'
diff --git a/host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp b/host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp
new file mode 100644
index 000000000..172992f0a
--- /dev/null
+++ b/host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp
@@ -0,0 +1,643 @@
+//
+// Copyright 2015 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 <boost/thread/thread.hpp>
+#include <uhd/utils/math.hpp>
+#include <uhd/utils/safe_call.hpp>
+#include "twinrx_ctrl.hpp"
+#include "adf435x.hpp"
+#include "adf5355.hpp"
+
+using namespace uhd;
+using namespace usrp;
+using namespace dboard::twinrx;
+typedef twinrx_cpld_regmap rm;
+
+static boost::uint32_t bool2bin(bool x) { return x ? 1 : 0; }
+
+static const double TWINRX_DESIRED_REFERENCE_FREQ = 50e6;
+
+class twinrx_ctrl_impl : public twinrx_ctrl {
+public:
+ twinrx_ctrl_impl(
+ dboard_iface::sptr db_iface,
+ twinrx_gpio::sptr gpio_iface,
+ twinrx_cpld_regmap::sptr cpld_regmap
+ ) : _db_iface(db_iface), _gpio_iface(gpio_iface), _cpld_regs(cpld_regmap)
+ {
+
+ //Turn on switcher and wait for power good
+ _gpio_iface->set_field(twinrx_gpio::FIELD_SWPS_EN, 1);
+ size_t timeout_ms = 100;
+ while (_gpio_iface->get_field(twinrx_gpio::FIELD_SWPS_PWR_GOOD) == 0) {
+ boost::this_thread::sleep(boost::posix_time::microsec(1000));
+ if (--timeout_ms == 0) {
+ throw uhd::runtime_error("power supply failure");
+ }
+ }
+ //Initialize synthesizer objects
+ _lo1_iface[size_t(CH1)] = adf5355_iface::make(
+ boost::bind(&twinrx_ctrl_impl::_write_lo_spi, this, dboard_iface::UNIT_TX, _1));
+ _lo1_iface[size_t(CH2)] = adf5355_iface::make(
+ boost::bind(&twinrx_ctrl_impl::_write_lo_spi, this, dboard_iface::UNIT_TX, _1));
+
+ _lo2_iface[size_t(CH1)] = adf435x_iface::make_adf4351(
+ boost::bind(&twinrx_ctrl_impl::_write_lo_spi, this, dboard_iface::UNIT_RX, _1));
+ _lo2_iface[size_t(CH2)] = adf435x_iface::make_adf4351(
+ boost::bind(&twinrx_ctrl_impl::_write_lo_spi, this, dboard_iface::UNIT_RX, _1));
+
+ // Assert synthesizer chip enables
+ _gpio_iface->set_field(twinrx_gpio::FIELD_LO1_CE_CH1, 1);
+ _gpio_iface->set_field(twinrx_gpio::FIELD_LO1_CE_CH2, 1);
+ _gpio_iface->set_field(twinrx_gpio::FIELD_LO2_CE_CH1, 1);
+ _gpio_iface->set_field(twinrx_gpio::FIELD_LO2_CE_CH2, 1);
+
+ //Initialize default state
+ set_chan_enabled(BOTH, false, false);
+ set_preamp1(BOTH, PREAMP_BYPASS, false);
+ set_preamp2(BOTH, false, false);
+ set_lb_preamp_preselector(BOTH, false, false);
+ set_signal_path(BOTH, PATH_LOWBAND, false);
+ set_lb_preselector(BOTH, PRESEL_PATH3, false);
+ set_hb_preselector(BOTH, PRESEL_PATH1, false);
+ set_input_atten(BOTH, 31, false);
+ set_lb_atten(BOTH, 31, false);
+ set_hb_atten(BOTH, 31, false);
+ set_lo1_source(BOTH, LO_INTERNAL, false);
+ set_lo2_source(BOTH, LO_INTERNAL, false);
+ set_lo1_export_source(LO_EXPORT_DISABLED, false);
+ set_lo2_export_source(LO_EXPORT_DISABLED, false);
+ set_antenna_mapping(ANTX_NATIVE, false);
+ set_crossover_cal_mode(CAL_DISABLED, false);
+ commit();
+
+ //Initialize clocks and LO
+ bool found_rate = false;
+ BOOST_FOREACH(double rate, _db_iface->get_clock_rates(dboard_iface::UNIT_TX)) {
+ found_rate |= uhd::math::frequencies_are_equal(rate, TWINRX_DESIRED_REFERENCE_FREQ);
+ }
+ BOOST_FOREACH(double rate, _db_iface->get_clock_rates(dboard_iface::UNIT_RX)) {
+ found_rate |= uhd::math::frequencies_are_equal(rate, TWINRX_DESIRED_REFERENCE_FREQ);
+ }
+ if (not found_rate) {
+ throw uhd::runtime_error("TwinRX not supported on this motherboard");
+ }
+ _db_iface->set_clock_rate(dboard_iface::UNIT_TX, TWINRX_DESIRED_REFERENCE_FREQ);
+ _db_iface->set_clock_rate(dboard_iface::UNIT_RX, TWINRX_DESIRED_REFERENCE_FREQ);
+
+ _db_iface->set_clock_enabled(dboard_iface::UNIT_TX, true);
+ _db_iface->set_clock_enabled(dboard_iface::UNIT_RX, true);
+ for (size_t i = 0; i < NUM_CHANS; i++) {
+ _config_lo1_route(i==0?LO_CONFIG_CH1:LO_CONFIG_CH2);
+ _config_lo2_route(i==0?LO_CONFIG_CH1:LO_CONFIG_CH2);
+ _lo1_iface[i]->set_output_power(adf5355_iface::OUTPUT_POWER_5DBM);
+ _lo1_iface[i]->set_reference_freq(TWINRX_DESIRED_REFERENCE_FREQ);
+ _lo1_iface[i]->set_muxout_mode(adf5355_iface::MUXOUT_DLD);
+ _lo1_iface[i]->set_frequency(3e9, 1.0e3);
+ _lo2_iface[i]->set_feedback_select(adf435x_iface::FB_SEL_DIVIDED);
+ _lo2_iface[i]->set_output_power(adf435x_iface::OUTPUT_POWER_5DBM);
+ _lo2_iface[i]->set_reference_freq(TWINRX_DESIRED_REFERENCE_FREQ);
+ _lo2_iface[i]->set_muxout_mode(adf435x_iface::MUXOUT_DLD);
+ _lo1_iface[i]->commit();
+ _lo2_iface[i]->commit();
+ }
+ _config_lo1_route(LO_CONFIG_NONE);
+ _config_lo2_route(LO_CONFIG_NONE);
+ }
+
+ ~twinrx_ctrl_impl()
+ {
+ UHD_SAFE_CALL(
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ _gpio_iface->set_field(twinrx_gpio::FIELD_SWPS_EN, 0);
+ )
+ }
+
+ void commit()
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ _commit();
+ }
+
+ void set_chan_enabled(channel_t ch, bool enabled, bool commit = true)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ if (ch == CH1 or ch == BOTH) {
+ _cpld_regs->rf1_reg1.set(rm::rf1_reg1_t::AMP_LO1_EN_CH1, bool2bin(enabled));
+ _cpld_regs->if0_reg3.set(rm::if0_reg3_t::IF1_IF2_EN_CH1, bool2bin(enabled));
+ _cpld_regs->if0_reg0.set(rm::if0_reg0_t::AMP_LO2_EN_CH1, bool2bin(enabled));
+ }
+ if (ch == CH2 or ch == BOTH) {
+ _cpld_regs->rf1_reg5.set(rm::rf1_reg5_t::AMP_LO1_EN_CH2, bool2bin(enabled));
+ _cpld_regs->if0_reg4.set(rm::if0_reg4_t::IF1_IF2_EN_CH2, bool2bin(enabled));
+ _cpld_regs->if0_reg0.set(rm::if0_reg0_t::AMP_LO2_EN_CH2, bool2bin(enabled));
+ }
+ if (commit) _commit();
+ }
+
+ void set_preamp1(channel_t ch, preamp_state_t value, bool commit = true)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ if (ch == CH1 or ch == BOTH) {
+ _cpld_regs->rf0_reg1.set(rm::rf0_reg1_t::SWPA1_CTL_CH1, bool2bin(value==PREAMP_HIGHBAND));
+ _cpld_regs->rf2_reg2.set(rm::rf2_reg2_t::SWPA2_CTRL_CH1, bool2bin(value==PREAMP_BYPASS));
+ _cpld_regs->rf0_reg1.set(rm::rf0_reg1_t::HB_PREAMP_EN_CH1, bool2bin(value==PREAMP_HIGHBAND));
+ _cpld_regs->rf0_reg1.set(rm::rf0_reg1_t::LB_PREAMP_EN_CH1, bool2bin(value==PREAMP_LOWBAND));
+ }
+ if (ch == CH2 or ch == BOTH) {
+ _cpld_regs->rf0_reg7.set(rm::rf0_reg7_t::SWPA1_CTRL_CH2, bool2bin(value==PREAMP_HIGHBAND));
+ _cpld_regs->rf2_reg5.set(rm::rf2_reg5_t::SWPA2_CTRL_CH2, bool2bin(value==PREAMP_BYPASS));
+ _cpld_regs->rf0_reg5.set(rm::rf0_reg5_t::HB_PREAMP_EN_CH2, bool2bin(value==PREAMP_HIGHBAND));
+ _cpld_regs->rf2_reg6.set(rm::rf2_reg6_t::LB_PREAMP_EN_CH2, bool2bin(value==PREAMP_LOWBAND));
+ }
+ if (commit) _commit();
+ }
+
+ void set_preamp2(channel_t ch, bool enabled, bool commit = true)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ if (ch == CH1 or ch == BOTH) {
+ _cpld_regs->rf2_reg7.set(rm::rf2_reg7_t::SWPA4_CTRL_CH1, bool2bin(not enabled));
+ _cpld_regs->rf2_reg3.set(rm::rf2_reg3_t::PREAMP2_EN_CH1, bool2bin(enabled));
+ }
+ if (ch == CH2 or ch == BOTH) {
+ _cpld_regs->rf0_reg6.set(rm::rf0_reg6_t::SWPA4_CTRL_CH2, bool2bin(not enabled));
+ _cpld_regs->rf1_reg6.set(rm::rf1_reg6_t::PREAMP2_EN_CH2, bool2bin(enabled));
+ }
+ if (commit) _commit();
+ }
+
+ void set_lb_preamp_preselector(channel_t ch, bool enabled, bool commit = true)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ if (ch == CH1 or ch == BOTH) {
+ _cpld_regs->rf0_reg7.set(rm::rf0_reg7_t::SWPA3_CTRL_CH1, bool2bin(not enabled));
+ }
+ if (ch == CH2 or ch == BOTH) {
+ _cpld_regs->rf0_reg1.set(rm::rf0_reg1_t::SWPA3_CTRL_CH2, bool2bin(not enabled));
+ }
+ if (commit) _commit();
+ }
+
+ void set_signal_path(channel_t ch, signal_path_t path, bool commit = true)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ if (ch == CH1 or ch == BOTH) {
+ _cpld_regs->rf2_reg2.set(rm::rf2_reg2_t::SW11_CTRL_CH1, bool2bin(path==PATH_LOWBAND));
+ _cpld_regs->rf1_reg2.set(rm::rf1_reg2_t::SW12_CTRL_CH1, bool2bin(path==PATH_LOWBAND));
+ _cpld_regs->rf1_reg6.set(rm::rf1_reg6_t::HB_PRESEL_PGA_EN_CH1, bool2bin(path==PATH_HIGHBAND));
+ _cpld_regs->rf0_reg2.set(rm::rf0_reg2_t::SW6_CTRL_CH1, bool2bin(path==PATH_LOWBAND));
+ _cpld_regs->if0_reg3.set(rm::if0_reg3_t::SW13_CTRL_CH1, bool2bin(path==PATH_LOWBAND));
+ _cpld_regs->if0_reg2.set(rm::if0_reg2_t::AMP_LB_IF1_EN_CH1, bool2bin(path==PATH_LOWBAND));
+ _cpld_regs->if0_reg0.set(rm::if0_reg0_t::AMP_HB_IF1_EN_CH1, bool2bin(path==PATH_HIGHBAND));
+ _cpld_regs->rf1_reg2.set(rm::rf1_reg2_t::AMP_HB_EN_CH1, bool2bin(path==PATH_HIGHBAND));
+ _cpld_regs->rf2_reg2.set(rm::rf2_reg2_t::AMP_LB_EN_CH1, bool2bin(path==PATH_LOWBAND));
+ }
+ if (ch == CH2 or ch == BOTH) {
+ _cpld_regs->rf2_reg7.set(rm::rf2_reg7_t::SW11_CTRL_CH2, bool2bin(path==PATH_LOWBAND));
+ _cpld_regs->rf1_reg7.set(rm::rf1_reg7_t::SW12_CTRL_CH2, bool2bin(path==PATH_LOWBAND));
+ _cpld_regs->rf1_reg2.set(rm::rf1_reg2_t::HB_PRESEL_PGA_EN_CH2, bool2bin(path==PATH_HIGHBAND));
+ _cpld_regs->rf0_reg6.set(rm::rf0_reg6_t::SW6_CTRL_CH2, bool2bin(path==PATH_HIGHBAND));
+ _cpld_regs->if0_reg6.set(rm::if0_reg6_t::SW13_CTRL_CH2, bool2bin(path==PATH_HIGHBAND));
+ _cpld_regs->if0_reg2.set(rm::if0_reg2_t::AMP_LB_IF1_EN_CH2, bool2bin(path==PATH_LOWBAND));
+ _cpld_regs->if0_reg6.set(rm::if0_reg6_t::AMP_HB_IF1_EN_CH2, bool2bin(path==PATH_HIGHBAND));
+ _cpld_regs->rf1_reg7.set(rm::rf1_reg7_t::AMP_HB_EN_CH2, bool2bin(path==PATH_HIGHBAND));
+ _cpld_regs->rf2_reg7.set(rm::rf2_reg7_t::AMP_LB_EN_CH2, bool2bin(path==PATH_LOWBAND));
+ }
+ if (commit) _commit();
+ }
+
+ void set_lb_preselector(channel_t ch, preselector_path_t path, bool commit = true)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ boost::uint32_t sw7val = 0, sw8val = 0;
+ switch (path) {
+ case PRESEL_PATH1: sw7val = 3; sw8val = 1; break;
+ case PRESEL_PATH2: sw7val = 2; sw8val = 0; break;
+ case PRESEL_PATH3: sw7val = 0; sw8val = 2; break;
+ case PRESEL_PATH4: sw7val = 1; sw8val = 3; break;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+ if (ch == CH1 or ch == BOTH) {
+ _cpld_regs->rf0_reg3.set(rm::rf0_reg3_t::SW7_CTRL_CH1, sw7val);
+ _cpld_regs->rf2_reg3.set(rm::rf2_reg3_t::SW8_CTRL_CH1, sw8val);
+ }
+ if (ch == CH2 or ch == BOTH) {
+ _cpld_regs->rf0_reg7.set(rm::rf0_reg7_t::SW7_CTRL_CH2, sw7val);
+ _cpld_regs->rf2_reg7.set(rm::rf2_reg7_t::SW8_CTRL_CH2, sw8val);
+ }
+ if (commit) _commit();
+ }
+
+ void set_hb_preselector(channel_t ch, preselector_path_t path, bool commit = true)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ boost::uint32_t sw9ch1val = 0, sw10ch1val = 0, sw9ch2val = 0, sw10ch2val = 0;
+ switch (path) {
+ case PRESEL_PATH1: sw9ch1val = 3; sw10ch1val = 0; sw9ch2val = 0; sw10ch2val = 3; break;
+ case PRESEL_PATH2: sw9ch1val = 1; sw10ch1val = 2; sw9ch2val = 1; sw10ch2val = 1; break;
+ case PRESEL_PATH3: sw9ch1val = 2; sw10ch1val = 1; sw9ch2val = 2; sw10ch2val = 2; break;
+ case PRESEL_PATH4: sw9ch1val = 0; sw10ch1val = 3; sw9ch2val = 3; sw10ch2val = 0; break;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+ if (ch == CH1 or ch == BOTH) {
+ _cpld_regs->rf0_reg5.set(rm::rf0_reg5_t::SW9_CTRL_CH1, sw9ch1val);
+ _cpld_regs->rf1_reg3.set(rm::rf1_reg3_t::SW10_CTRL_CH1, sw10ch1val);
+ }
+ if (ch == CH2 or ch == BOTH) {
+ _cpld_regs->rf0_reg3.set(rm::rf0_reg3_t::SW9_CTRL_CH2, sw9ch2val);
+ _cpld_regs->rf1_reg7.set(rm::rf1_reg7_t::SW10_CTRL_CH2, sw10ch2val);
+ }
+ if (commit) _commit();
+
+ }
+
+ void set_input_atten(channel_t ch, boost::uint8_t atten, bool commit = true)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ if (ch == CH1 or ch == BOTH) {
+ _cpld_regs->rf0_reg0.set(rm::rf0_reg0_t::ATTEN_IN_CH1, atten&0x1F);
+ }
+ if (ch == CH2 or ch == BOTH) {
+ _cpld_regs->rf0_reg4.set(rm::rf0_reg4_t::ATTEN_IN_CH2, atten&0x1F);
+ }
+ if (commit) _commit();
+ }
+
+ void set_lb_atten(channel_t ch, boost::uint8_t atten, bool commit = true)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ if (ch == CH1 or ch == BOTH) {
+ _cpld_regs->rf2_reg0.set(rm::rf2_reg0_t::ATTEN_LB_CH1, atten&0x1F);
+ }
+ if (ch == CH2 or ch == BOTH) {
+ _cpld_regs->rf2_reg4.set(rm::rf2_reg4_t::ATTEN_LB_CH2, atten&0x1F);
+ }
+ if (commit) _commit();
+ }
+
+ void set_hb_atten(channel_t ch, boost::uint8_t atten, bool commit = true)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ if (ch == CH1 or ch == BOTH) {
+ _cpld_regs->rf1_reg0.set(rm::rf1_reg0_t::ATTEN_HB_CH1, atten&0x1F);
+ }
+ if (ch == CH2 or ch == BOTH) {
+ _cpld_regs->rf1_reg4.set(rm::rf1_reg4_t::ATTEN_HB_CH2, atten&0x1F);
+ }
+ if (commit) _commit();
+ }
+
+ void set_lo1_source(channel_t ch, lo_source_t source, bool commit = true)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ if (ch == CH1 or ch == BOTH) {
+ _cpld_regs->rf1_reg5.set(rm::rf1_reg5_t::SW14_CTRL_CH2, bool2bin(source!=LO_COMPANION));
+ _cpld_regs->rf1_reg1.set(rm::rf1_reg1_t::SW15_CTRL_CH1, bool2bin(source==LO_EXTERNAL));
+ _cpld_regs->rf1_reg1.set(rm::rf1_reg1_t::SW16_CTRL_CH1, bool2bin(source!=LO_INTERNAL));
+ _lo1_src[size_t(CH1)] = source;
+ }
+ if (ch == CH2 or ch == BOTH) {
+ _cpld_regs->rf1_reg1.set(rm::rf1_reg1_t::SW14_CTRL_CH1, bool2bin(source==LO_COMPANION));
+ _cpld_regs->rf1_reg5.set(rm::rf1_reg5_t::SW15_CTRL_CH2, bool2bin(source!=LO_INTERNAL));
+ _cpld_regs->rf1_reg6.set(rm::rf1_reg6_t::SW16_CTRL_CH2, bool2bin(source==LO_INTERNAL));
+ _lo1_src[size_t(CH2)] = source;
+ }
+ if (commit) _commit();
+ }
+
+ void set_lo2_source(channel_t ch, lo_source_t source, bool commit = true)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ if (ch == CH1 or ch == BOTH) {
+ _cpld_regs->if0_reg0.set(rm::if0_reg0_t::SW19_CTRL_CH2, bool2bin(source==LO_COMPANION));
+ _cpld_regs->if0_reg1.set(rm::if0_reg1_t::SW20_CTRL_CH1, bool2bin(source==LO_COMPANION));
+ _cpld_regs->if0_reg4.set(rm::if0_reg4_t::SW21_CTRL_CH1, bool2bin(source==LO_INTERNAL));
+ _lo2_src[size_t(CH1)] = source;
+ }
+ if (ch == CH2 or ch == BOTH) {
+ _cpld_regs->if0_reg4.set(rm::if0_reg4_t::SW19_CTRL_CH1, bool2bin(source==LO_EXTERNAL));
+ _cpld_regs->if0_reg0.set(rm::if0_reg0_t::SW20_CTRL_CH2, bool2bin(source==LO_INTERNAL||source==LO_DISABLED));
+ _cpld_regs->if0_reg4.set(rm::if0_reg4_t::SW21_CTRL_CH2, bool2bin(source==LO_INTERNAL));
+ _lo2_src[size_t(CH2)] = source;
+ }
+ if (commit) _commit();
+ }
+
+ void set_lo1_export_source(lo_export_source_t source, bool commit = true)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ //SW22 may conflict with the cal switch but this attr takes priority and we assume
+ //that the cal switch is disabled (by disabling it!)
+ _set_cal_mode(CAL_DISABLED, source);
+ _cpld_regs->rf1_reg3.set(rm::rf1_reg3_t::SW23_CTRL, bool2bin(source!=LO_CH1_SYNTH));
+ _lo1_export = source;
+
+ if (commit) _commit();
+ }
+
+ void set_lo2_export_source(lo_export_source_t source, bool commit = true)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ _cpld_regs->if0_reg7.set(rm::if0_reg7_t::SW24_CTRL_CH2, bool2bin(source==LO_CH2_SYNTH));
+ _cpld_regs->if0_reg4.set(rm::if0_reg4_t::SW25_CTRL, bool2bin(source!=LO_CH1_SYNTH));
+ _cpld_regs->if0_reg3.set(rm::if0_reg3_t::SW24_CTRL_CH1, bool2bin(source!=LO_CH1_SYNTH));
+ _lo2_export = source;
+
+ if (commit) _commit();
+ }
+
+ void set_antenna_mapping(antenna_mapping_t mapping, bool commit = true)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+
+ enum switch_path_t { CONNECT, TERM, EXPORT, IMPORT, SWAP };
+ switch_path_t path1, path2;
+
+ switch (mapping) {
+ case ANTX_NATIVE:
+ path1 = CONNECT; path2 = CONNECT; break;
+ case ANT1_SHARED:
+ path1 = EXPORT; path2 = IMPORT; break;
+ case ANT2_SHARED:
+ path1 = IMPORT; path2 = EXPORT; break;
+ case ANTX_SWAPPED:
+ path1 = SWAP; path2 = SWAP; break;
+ default:
+ path1 = TERM; path2 = TERM; break;
+ }
+
+ _cpld_regs->rf0_reg5.set(rm::rf0_reg5_t::SW3_CTRL_CH1, bool2bin(path1==EXPORT||path1==SWAP));
+ _cpld_regs->rf0_reg2.set(rm::rf0_reg2_t::SW4_CTRL_CH1, bool2bin(!(path1==IMPORT||path1==SWAP)));
+ _cpld_regs->rf0_reg2.set(rm::rf0_reg2_t::SW5_CTRL_CH1, bool2bin(path1==CONNECT));
+ _cpld_regs->rf0_reg7.set(rm::rf0_reg7_t::SW3_CTRL_CH2, bool2bin(path2==EXPORT||path2==SWAP));
+ _cpld_regs->rf0_reg6.set(rm::rf0_reg6_t::SW4_CTRL_CH2, bool2bin(path2==IMPORT||path2==SWAP));
+ _cpld_regs->rf0_reg6.set(rm::rf0_reg6_t::SW5_CTRL_CH2, bool2bin(path2==CONNECT));
+
+ if (commit) _commit();
+ }
+
+ void set_crossover_cal_mode(cal_mode_t cal_mode, bool commit = true)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ if (_lo1_export == LO_CH1_SYNTH && cal_mode == CAL_CH2) {
+ throw uhd::runtime_error("cannot enable cal crossover on CH2 when LO1 in CH1 is exported");
+ }
+ if (_lo1_export == LO_CH2_SYNTH && cal_mode == CAL_CH1) {
+ throw uhd::runtime_error("cannot enable cal crossover on CH1 when LO1 in CH2 is exported");
+ }
+ _set_cal_mode(cal_mode, _lo1_export);
+
+ if (commit) _commit();
+ }
+
+ double set_lo1_synth_freq(channel_t ch, double freq, bool commit = true)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ static const double RESOLUTION = 1e3;
+
+ double coerced_freq = 0.0;
+ if (ch == CH1 or ch == BOTH) {
+ coerced_freq = _lo1_iface[size_t(CH1)]->set_frequency(freq, RESOLUTION, false);
+ _lo1_freq[size_t(CH1)] = tune_freq_t(freq);
+ }
+ if (ch == CH2 or ch == BOTH) {
+ coerced_freq = _lo1_iface[size_t(CH2)]->set_frequency(freq, RESOLUTION, false);
+ _lo1_freq[size_t(CH2)] = tune_freq_t(freq);
+ }
+
+ if (commit) _commit();
+ return coerced_freq;
+ }
+
+ double set_lo2_synth_freq(channel_t ch, double freq, bool commit = true)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ static const double PRESCALER_THRESH = 3.6e9;
+
+ double coerced_freq = 0.0;
+ if (ch == CH1 or ch == BOTH) {
+ _lo2_iface[size_t(CH1)]->set_prescaler(freq > PRESCALER_THRESH ?
+ adf435x_iface::PRESCALER_8_9 : adf435x_iface::PRESCALER_4_5);
+ coerced_freq = _lo2_iface[size_t(CH1)]->set_frequency(freq, false, false);
+ _lo2_freq[size_t(CH1)] = tune_freq_t(freq);
+ }
+ if (ch == CH2 or ch == BOTH) {
+ _lo2_iface[size_t(CH2)]->set_prescaler(freq > PRESCALER_THRESH ?
+ adf435x_iface::PRESCALER_8_9 : adf435x_iface::PRESCALER_4_5);
+ coerced_freq = _lo2_iface[size_t(CH2)]->set_frequency(freq, false, false);
+ _lo2_freq[size_t(CH2)] = tune_freq_t(freq);
+ }
+
+ if (commit) _commit();
+ return coerced_freq;
+ }
+
+ bool read_lo1_locked(channel_t ch)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+
+ bool locked = true;
+ if (ch == CH1 or ch == BOTH) {
+ locked = locked && (_gpio_iface->get_field(twinrx_gpio::FIELD_LO1_MUXOUT_CH1) == 1);
+ }
+ if (ch == CH2 or ch == BOTH) {
+ locked = locked && (_gpio_iface->get_field(twinrx_gpio::FIELD_LO1_MUXOUT_CH2) == 1);
+ }
+ return locked;
+ }
+
+ bool read_lo2_locked(channel_t ch)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+
+ bool locked = true;
+ if (ch == CH1 or ch == BOTH) {
+ locked = locked && (_gpio_iface->get_field(twinrx_gpio::FIELD_LO2_MUXOUT_CH1) == 1);
+ }
+ if (ch == CH2 or ch == BOTH) {
+ locked = locked && (_gpio_iface->get_field(twinrx_gpio::FIELD_LO2_MUXOUT_CH2) == 1);
+ }
+ return locked;
+ }
+
+private: //Functions
+ void _set_cal_mode(cal_mode_t cal_mode, lo_export_source_t lo1_export_src)
+ {
+ _cpld_regs->rf1_reg1.set(rm::rf1_reg1_t::SW17_CTRL_CH1, bool2bin(cal_mode!=CAL_CH1));
+ _cpld_regs->rf1_reg6.set(rm::rf1_reg6_t::SW17_CTRL_CH2, bool2bin(cal_mode!=CAL_CH2));
+ _cpld_regs->rf1_reg5.set(rm::rf1_reg5_t::SW18_CTRL_CH1, bool2bin(cal_mode!=CAL_CH1));
+ _cpld_regs->rf2_reg3.set(rm::rf2_reg3_t::SW18_CTRL_CH2, bool2bin(cal_mode!=CAL_CH2));
+ _cpld_regs->rf1_reg3.set(rm::rf1_reg3_t::SW22_CTRL_CH1, bool2bin((lo1_export_src!=LO_CH1_SYNTH)||(cal_mode==CAL_CH1)));
+ _cpld_regs->rf1_reg7.set(rm::rf1_reg7_t::SW22_CTRL_CH2, bool2bin((lo1_export_src!=LO_CH2_SYNTH)||(cal_mode==CAL_CH2)));
+ }
+
+ void _config_lo1_route(lo_config_route_t source)
+ {
+ //Route SPI LEs through CPLD (will not assert them)
+ _cpld_regs->rf0_reg2.set(rm::rf0_reg2_t::LO1_LE_CH1, bool2bin(source==LO_CONFIG_CH1||source==LO_CONFIG_BOTH));
+ _cpld_regs->rf0_reg2.set(rm::rf0_reg2_t::LO1_LE_CH2, bool2bin(source==LO_CONFIG_CH2||source==LO_CONFIG_BOTH));
+ _cpld_regs->rf0_reg2.flush();
+ }
+
+ void _config_lo2_route(lo_config_route_t source)
+ {
+ //Route SPI LEs through CPLD (will not assert them)
+ _cpld_regs->if0_reg2.set(rm::if0_reg2_t::LO2_LE_CH1, bool2bin(source==LO_CONFIG_CH1||source==LO_CONFIG_BOTH));
+ _cpld_regs->if0_reg2.set(rm::if0_reg2_t::LO2_LE_CH2, bool2bin(source==LO_CONFIG_CH2||source==LO_CONFIG_BOTH));
+ _cpld_regs->if0_reg2.flush();
+ }
+
+ void _write_lo_spi(dboard_iface::unit_t unit, const std::vector<boost::uint32_t> &regs)
+ {
+ BOOST_FOREACH(boost::uint32_t reg, regs) {
+ spi_config_t spi_config = spi_config_t(spi_config_t::EDGE_RISE);
+ spi_config.use_custom_divider = true;
+ spi_config.divider = 67;
+ _db_iface->write_spi(unit, spi_config, reg, 32);
+ }
+ }
+
+ void _commit()
+ {
+ //Commit everything except the LO synthesizers
+ _cpld_regs->flush();
+
+ // Disable unused LO synthesizers
+ _lo1_enable[size_t(CH1)] = _lo1_src[size_t(CH1)] == LO_INTERNAL ||
+ _lo1_src[size_t(CH2)] == LO_COMPANION ||
+ _lo1_export == LO_CH1_SYNTH;
+
+ _lo1_enable[size_t(CH2)] = _lo1_src[size_t(CH2)] == LO_INTERNAL ||
+ _lo1_src[size_t(CH1)] == LO_COMPANION ||
+ _lo1_export == LO_CH2_SYNTH;
+ _lo2_enable[size_t(CH1)] = _lo2_src[size_t(CH1)] == LO_INTERNAL ||
+ _lo2_src[size_t(CH2)] == LO_COMPANION ||
+ _lo2_export == LO_CH1_SYNTH;
+
+ _lo2_enable[size_t(CH2)] = _lo2_src[size_t(CH2)] == LO_INTERNAL ||
+ _lo2_src[size_t(CH1)] == LO_COMPANION ||
+ _lo2_export == LO_CH2_SYNTH;
+
+ _lo1_iface[size_t(CH1)]->set_output_enable(adf5355_iface::RF_OUTPUT_A, _lo1_enable[size_t(CH1)].get());
+ _lo1_iface[size_t(CH2)]->set_output_enable(adf5355_iface::RF_OUTPUT_A, _lo1_enable[size_t(CH2)].get());
+
+ _lo2_iface[size_t(CH1)]->set_output_enable(adf435x_iface::RF_OUTPUT_A, _lo2_enable[size_t(CH1)].get());
+ _lo2_iface[size_t(CH2)]->set_output_enable(adf435x_iface::RF_OUTPUT_A, _lo2_enable[size_t(CH2)].get());
+
+ //Commit LO1 frequency
+ // Commit Channel 1's settings to both channels simultaneously if the frequency is the same.
+ bool simultaneous_commit_lo1 = _lo1_freq[size_t(CH1)].is_dirty() and
+ _lo1_freq[size_t(CH2)].is_dirty() and
+ _lo1_freq[size_t(CH1)].get() == _lo1_freq[size_t(CH2)].get() and
+ _lo1_enable[size_t(CH1)].get() == _lo1_enable[size_t(CH2)].get();
+
+ if (simultaneous_commit_lo1) {
+ _config_lo1_route(LO_CONFIG_BOTH);
+ //Only commit one of the channels. The route LO_CONFIG_BOTH
+ //will ensure that the LEs for both channels are enabled
+ _lo1_iface[size_t(CH1)]->commit();
+ _lo1_freq[size_t(CH1)].mark_clean();
+ _lo1_freq[size_t(CH2)].mark_clean();
+ _lo1_enable[size_t(CH1)].mark_clean();
+ _lo1_enable[size_t(CH2)].mark_clean();
+ _config_lo1_route(LO_CONFIG_NONE);
+ } else {
+ if (_lo1_freq[size_t(CH1)].is_dirty() || _lo1_enable[size_t(CH1)].is_dirty()) {
+ _config_lo1_route(LO_CONFIG_CH1);
+ _lo1_iface[size_t(CH1)]->commit();
+ _lo1_freq[size_t(CH1)].mark_clean();
+ _lo1_enable[size_t(CH1)].mark_clean();
+ _config_lo1_route(LO_CONFIG_NONE);
+ }
+ if (_lo1_freq[size_t(CH2)].is_dirty() || _lo1_enable[size_t(CH2)].is_dirty()) {
+ _config_lo1_route(LO_CONFIG_CH2);
+ _lo1_iface[size_t(CH2)]->commit();
+ _lo1_freq[size_t(CH2)].mark_clean();
+ _lo1_enable[size_t(CH2)].mark_clean();
+ _config_lo1_route(LO_CONFIG_NONE);
+ }
+ }
+
+ //Commit LO2 frequency
+ bool simultaneous_commit_lo2 = _lo2_freq[size_t(CH1)].is_dirty() and
+ _lo2_freq[size_t(CH2)].is_dirty() and
+ _lo2_freq[size_t(CH1)].get() == _lo2_freq[size_t(CH2)].get() and
+ _lo2_enable[size_t(CH1)].get() == _lo2_enable[size_t(CH2)].get();
+
+ if (simultaneous_commit_lo2) {
+ _config_lo2_route(LO_CONFIG_BOTH);
+ //Only commit one of the channels. The route LO_CONFIG_BOTH
+ //will ensure that the LEs for both channels are enabled
+ _lo2_iface[size_t(CH1)]->commit();
+ _lo2_freq[size_t(CH1)].mark_clean();
+ _lo2_freq[size_t(CH2)].mark_clean();
+ _lo2_enable[size_t(CH1)].mark_clean();
+ _lo2_enable[size_t(CH2)].mark_clean();
+ _config_lo2_route(LO_CONFIG_NONE);
+ } else {
+ if (_lo2_freq[size_t(CH1)].is_dirty() || _lo2_enable[size_t(CH1)].is_dirty()) {
+ _config_lo2_route(LO_CONFIG_CH1);
+ _lo2_iface[size_t(CH1)]->commit();
+ _lo2_freq[size_t(CH1)].mark_clean();
+ _lo2_enable[size_t(CH1)].mark_clean();
+ _config_lo2_route(LO_CONFIG_NONE);
+ }
+ if (_lo2_freq[size_t(CH2)].is_dirty() || _lo2_enable[size_t(CH2)].is_dirty()) {
+ _config_lo2_route(LO_CONFIG_CH2);
+ _lo2_iface[size_t(CH2)]->commit();
+ _lo2_freq[size_t(CH2)].mark_clean();
+ _lo2_enable[size_t(CH2)].mark_clean();
+ _config_lo2_route(LO_CONFIG_NONE);
+ }
+ }
+ }
+
+private: //Members
+ static const size_t NUM_CHANS = 2;
+
+ struct tune_freq_t : public uhd::math::fp_compare::fp_compare_delta<double> {
+ tune_freq_t() : uhd::math::fp_compare::fp_compare_delta<double>(
+ 0.0, uhd::math::FREQ_COMPARISON_DELTA_HZ) {}
+
+ tune_freq_t(double freq) : uhd::math::fp_compare::fp_compare_delta<double>(
+ freq, uhd::math::FREQ_COMPARISON_DELTA_HZ) {}
+ };
+
+ boost::mutex _mutex;
+ dboard_iface::sptr _db_iface;
+ twinrx_gpio::sptr _gpio_iface;
+ twinrx_cpld_regmap::sptr _cpld_regs;
+ adf5355_iface::sptr _lo1_iface[NUM_CHANS];
+ adf435x_iface::sptr _lo2_iface[NUM_CHANS];
+ lo_source_t _lo1_src[NUM_CHANS];
+ lo_source_t _lo2_src[NUM_CHANS];
+ dirty_tracked<tune_freq_t> _lo1_freq[NUM_CHANS];
+ dirty_tracked<tune_freq_t> _lo2_freq[NUM_CHANS];
+ dirty_tracked<bool> _lo1_enable[NUM_CHANS];
+ dirty_tracked<bool> _lo2_enable[NUM_CHANS];
+ lo_export_source_t _lo1_export;
+ lo_export_source_t _lo2_export;
+};
+
+twinrx_ctrl::sptr twinrx_ctrl::make(
+ dboard_iface::sptr db_iface,
+ twinrx_gpio::sptr gpio_iface,
+ twinrx_cpld_regmap::sptr cpld_regmap
+) {
+ return sptr(new twinrx_ctrl_impl(db_iface, gpio_iface, cpld_regmap));
+}
diff --git a/host/lib/usrp/dboard/twinrx/twinrx_ctrl.hpp b/host/lib/usrp/dboard/twinrx/twinrx_ctrl.hpp
new file mode 100644
index 000000000..521e27ae9
--- /dev/null
+++ b/host/lib/usrp/dboard/twinrx/twinrx_ctrl.hpp
@@ -0,0 +1,101 @@
+//
+// Copyright 2015 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_DBOARD_TWINRX_CTRL_HPP
+#define INCLUDED_DBOARD_TWINRX_CTRL_HPP
+
+#include <boost/noncopyable.hpp>
+#include <uhd/types/wb_iface.hpp>
+#include "twinrx_io.hpp"
+
+namespace uhd { namespace usrp { namespace dboard { namespace twinrx {
+
+class twinrx_ctrl : public boost::noncopyable {
+public:
+ typedef boost::shared_ptr<twinrx_ctrl> sptr;
+
+ static sptr make(
+ dboard_iface::sptr db_iface,
+ twinrx_gpio::sptr gpio_iface,
+ twinrx_cpld_regmap::sptr cpld_regmap);
+
+ virtual ~twinrx_ctrl() {}
+
+ enum channel_t { CH1 = 0, CH2 = 1, BOTH = 2};
+
+ enum preamp_state_t { PREAMP_LOWBAND, PREAMP_HIGHBAND, PREAMP_BYPASS };
+
+ enum signal_path_t { PATH_LOWBAND, PATH_HIGHBAND };
+
+ enum preselector_path_t { PRESEL_PATH1, PRESEL_PATH2, PRESEL_PATH3, PRESEL_PATH4 };
+
+ enum lo_source_t { LO_INTERNAL, LO_EXTERNAL, LO_COMPANION, LO_DISABLED };
+
+ enum lo_export_source_t { LO_CH1_SYNTH, LO_CH2_SYNTH, LO_EXPORT_DISABLED };
+
+ enum antenna_mapping_t { ANTX_NATIVE, ANT1_SHARED, ANT2_SHARED, ANTX_SWAPPED, ANTX_DISABLED };
+
+ enum lo_config_route_t { LO_CONFIG_CH1, LO_CONFIG_CH2, LO_CONFIG_BOTH, LO_CONFIG_NONE };
+
+ enum cal_mode_t { CAL_DISABLED, CAL_CH1, CAL_CH2 };
+
+ virtual void commit() = 0;
+
+ virtual void set_chan_enabled(channel_t ch, bool enabled, bool commit = true) = 0;
+
+ virtual void set_preamp1(channel_t ch, preamp_state_t value, bool commit = true) = 0;
+
+ virtual void set_preamp2(channel_t ch, bool enabled, bool commit = true) = 0;
+
+ virtual void set_lb_preamp_preselector(channel_t ch, bool enabled, bool commit = true) = 0;
+
+ virtual void set_signal_path(channel_t ch, signal_path_t path, bool commit = true) = 0;
+
+ virtual void set_lb_preselector(channel_t ch, preselector_path_t path, bool commit = true) = 0;
+
+ virtual void set_hb_preselector(channel_t ch, preselector_path_t path, bool commit = true) = 0;
+
+ virtual void set_input_atten(channel_t ch, boost::uint8_t atten, bool commit = true) = 0;
+
+ virtual void set_lb_atten(channel_t ch, boost::uint8_t atten, bool commit = true) = 0;
+
+ virtual void set_hb_atten(channel_t ch, boost::uint8_t atten, bool commit = true) = 0;
+
+ virtual void set_lo1_source(channel_t ch, lo_source_t source, bool commit = true) = 0;
+
+ virtual void set_lo2_source(channel_t ch, lo_source_t source, bool commit = true) = 0;
+
+ virtual void set_lo1_export_source(lo_export_source_t source, bool commit = true) = 0;
+
+ virtual void set_lo2_export_source(lo_export_source_t source, bool commit = true) = 0;
+
+ virtual void set_antenna_mapping(antenna_mapping_t mapping, bool commit = true) = 0;
+
+ virtual void set_crossover_cal_mode(cal_mode_t cal_mode, bool commit = true) = 0;
+
+ virtual double set_lo1_synth_freq(channel_t ch, double freq, bool commit = true) = 0;
+
+ virtual double set_lo2_synth_freq(channel_t ch, double freq, bool commit = true) = 0;
+
+ virtual bool read_lo1_locked(channel_t ch) = 0;
+
+ virtual bool read_lo2_locked(channel_t ch) = 0;
+};
+
+}}}} //namespaces
+
+#endif /* INCLUDED_DBOARD_TWINRX_CTRL_HPP */
diff --git a/host/lib/usrp/dboard/twinrx/twinrx_experts.cpp b/host/lib/usrp/dboard/twinrx/twinrx_experts.cpp
new file mode 100644
index 000000000..3d04098e9
--- /dev/null
+++ b/host/lib/usrp/dboard/twinrx/twinrx_experts.cpp
@@ -0,0 +1,637 @@
+//
+// 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 "twinrx_experts.hpp"
+#include "twinrx_gain_tables.hpp"
+#include <uhd/utils/math.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/types/dict.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/math/special_functions/round.hpp>
+
+using namespace uhd::experts;
+using namespace uhd::math;
+using namespace uhd::usrp::dboard::twinrx;
+
+/*!---------------------------------------------------------
+ * twinrx_freq_path_expert::resolve
+ * ---------------------------------------------------------
+ */
+void twinrx_freq_path_expert::resolve()
+{
+ //Lowband/highband switch point
+ static const double LB_HB_THRESHOLD_FREQ = 1.8e9;
+ static const double LB_TARGET_IF1_FREQ = 2.345e9;
+ static const double HB_TARGET_IF1_FREQ = 1.25e9;
+ static const double INJ_SIDE_THRESHOLD_FREQ = 5.1e9;
+
+ static const double FIXED_LO1_THRESHOLD_FREQ= 50e6;
+
+ //Preselector filter switch point
+ static const double LB_FILT1_THRESHOLD_FREQ = 0.5e9;
+ static const double LB_FILT2_THRESHOLD_FREQ = 0.8e9;
+ static const double LB_FILT3_THRESHOLD_FREQ = 1.2e9;
+ static const double LB_FILT4_THRESHOLD_FREQ = 1.8e9;
+ static const double HB_FILT1_THRESHOLD_FREQ = 3.0e9;
+ static const double HB_FILT2_THRESHOLD_FREQ = 4.1e9;
+ static const double HB_FILT3_THRESHOLD_FREQ = 5.1e9;
+ static const double HB_FILT4_THRESHOLD_FREQ = 6.0e9;
+
+ static const double LB_PREAMP_PRESEL_THRESHOLD_FREQ = 0.8e9;
+
+ //Misc
+ static const double INST_BANDWIDTH = 80e6;
+ static const double MANUAL_LO_HYSTERESIS_PPM = 1.0;
+
+ static const freq_range_t FREQ_RANGE(10e6, 6e9);
+ rf_freq_abs_t rf_freq(FREQ_RANGE.clip(_rf_freq_d));
+
+ // Choose low-band vs high-band depending on frequency
+ _signal_path = (rf_freq > LB_HB_THRESHOLD_FREQ) ?
+ twinrx_ctrl::PATH_HIGHBAND : twinrx_ctrl::PATH_LOWBAND;
+ if (_signal_path == twinrx_ctrl::PATH_LOWBAND) {
+ // Choose low-band preselector filter
+ if (rf_freq < LB_FILT1_THRESHOLD_FREQ) {
+ _lb_presel = twinrx_ctrl::PRESEL_PATH1;
+ } else if (rf_freq < LB_FILT2_THRESHOLD_FREQ) {
+ _lb_presel = twinrx_ctrl::PRESEL_PATH2;
+ } else if (rf_freq < LB_FILT3_THRESHOLD_FREQ) {
+ _lb_presel = twinrx_ctrl::PRESEL_PATH3;
+ } else if (rf_freq < LB_FILT4_THRESHOLD_FREQ) {
+ _lb_presel = twinrx_ctrl::PRESEL_PATH4;
+ } else {
+ _lb_presel = twinrx_ctrl::PRESEL_PATH4;
+ }
+ } else if (_signal_path == twinrx_ctrl::PATH_HIGHBAND) {
+ // Choose high-band preselector filter
+ if (rf_freq < HB_FILT1_THRESHOLD_FREQ) {
+ _hb_presel = twinrx_ctrl::PRESEL_PATH1;
+ } else if (rf_freq < HB_FILT2_THRESHOLD_FREQ) {
+ _hb_presel = twinrx_ctrl::PRESEL_PATH2;
+ } else if (rf_freq < HB_FILT3_THRESHOLD_FREQ) {
+ _hb_presel = twinrx_ctrl::PRESEL_PATH3;
+ } else if (rf_freq < HB_FILT4_THRESHOLD_FREQ) {
+ _hb_presel = twinrx_ctrl::PRESEL_PATH4;
+ } else {
+ _hb_presel = twinrx_ctrl::PRESEL_PATH4;
+ }
+ } else {
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+
+ //Choose low-band preamp preselector
+ _lb_preamp_presel = (rf_freq > LB_PREAMP_PRESEL_THRESHOLD_FREQ);
+
+ //Choose LO frequencies
+ const double target_if1_freq = (_signal_path == twinrx_ctrl::PATH_HIGHBAND) ?
+ HB_TARGET_IF1_FREQ : LB_TARGET_IF1_FREQ;
+ const double target_if2_freq = _if_freq_d;
+
+ // LO1
+ double lo1_freq_ideal = 0.0, lo2_freq_ideal = 0.0;
+ if (rf_freq <= FIXED_LO1_THRESHOLD_FREQ) {
+ //LO1 Freq static
+ lo1_freq_ideal = target_if1_freq + FIXED_LO1_THRESHOLD_FREQ;
+ } else if (rf_freq <= INJ_SIDE_THRESHOLD_FREQ) {
+ //High-side LO1 Injection
+ lo1_freq_ideal = rf_freq.get() + target_if1_freq;
+ } else {
+ //Low-side LO1 Injection
+ lo1_freq_ideal = rf_freq.get() - target_if1_freq;
+ }
+
+ if (_lo1_freq_d.get_author() == experts::AUTHOR_USER) {
+ if (_lo1_freq_d.is_dirty()) { //Are we here because the LO frequency was set?
+ // The user explicitly requested to set the LO freq so don't touch it!
+ } else {
+ // Something else changed which may cause the LO frequency to update.
+ // Only commit if the frequency is stale. If the user's value is stale
+ // reset the author to expert.
+ if (rf_freq_ppm_t(lo1_freq_ideal, MANUAL_LO_HYSTERESIS_PPM) != _lo1_freq_d.get()) {
+ _lo1_freq_d = lo1_freq_ideal; //Reset author
+ }
+ }
+ } else {
+ // The LO frequency was never set by the user. Let the expert take care of it
+ _lo1_freq_d = lo1_freq_ideal; //Reset author
+ }
+
+ // LO2
+ lo_inj_side_t lo2_inj_side_ideal = _compute_lo2_inj_side(
+ lo1_freq_ideal, target_if1_freq, target_if2_freq, INST_BANDWIDTH);
+ if (lo2_inj_side_ideal == INJ_HIGH_SIDE) {
+ lo2_freq_ideal = target_if1_freq + target_if2_freq;
+ } else {
+ lo2_freq_ideal = target_if1_freq - target_if2_freq;
+ }
+
+ if (_lo2_freq_d.get_author() == experts::AUTHOR_USER) {
+ if (_lo2_freq_d.is_dirty()) { //Are we here because the LO frequency was set?
+ // The user explicitly requested to set the LO freq so don't touch it!
+ } else {
+ // Something else changed which may cause the LO frequency to update.
+ // Only commit if the frequency is stale. If the user's value is stale
+ // reset the author to expert.
+ if (rf_freq_ppm_t(lo2_freq_ideal, MANUAL_LO_HYSTERESIS_PPM) != _lo2_freq_d.get()) {
+ _lo2_freq_d = lo2_freq_ideal; //Reset author
+ }
+ }
+ } else {
+ // The LO frequency was never set by the user. Let the expert take care of it
+ _lo2_freq_d = lo2_freq_ideal; //Reset author
+ }
+
+ // Determine injection side using the final LO frequency
+ _lo1_inj_side = (_lo1_freq_d > rf_freq.get()) ? INJ_HIGH_SIDE : INJ_LOW_SIDE;
+ _lo2_inj_side = (_lo2_freq_d > target_if1_freq) ? INJ_HIGH_SIDE : INJ_LOW_SIDE;
+}
+
+lo_inj_side_t twinrx_freq_path_expert::_compute_lo2_inj_side(
+ double lo1_freq, double if1_freq, double if2_freq, double bandwidth
+) {
+ static const int MAX_SPUR_ORDER = 5;
+ for (int ord = MAX_SPUR_ORDER; ord >= 1; ord--) {
+ // Check high-side injection first
+ if (not _has_mixer_spurs(lo1_freq, if1_freq + if2_freq, if2_freq, bandwidth, ord)) {
+ return INJ_HIGH_SIDE;
+ }
+ // Check low-side injection second
+ if (not _has_mixer_spurs(lo1_freq, if1_freq - if2_freq, if2_freq, bandwidth, ord)) {
+ return INJ_LOW_SIDE;
+ }
+ }
+ // If we reached here, then there are spurs everywhere. Pick high-side as the default
+ return INJ_HIGH_SIDE;
+}
+
+bool twinrx_freq_path_expert::_has_mixer_spurs(
+ double lo1_freq, double lo2_freq, double if2_freq,
+ double bandwidth, int spur_order
+) {
+ // Iterate through all N-th order harmomic combinations
+ // of LOs...
+ for (int lo1h_i = 1; lo1h_i <= spur_order; lo1h_i++) {
+ double lo1harm_freq = lo1_freq * lo1h_i;
+ for (int lo2h_i = 1; lo2h_i <= spur_order; lo2h_i++) {
+ double lo2harm_freq = lo2_freq * lo2h_i;
+ double hdelta = lo1harm_freq - lo2harm_freq;
+ // .. and check if there is a mixer spur in the IF band
+ if (std::abs(hdelta + if2_freq) < bandwidth/2 or
+ std::abs(hdelta - if2_freq) < bandwidth/2) {
+ return true;
+ }
+ }
+ }
+ // No spurs were found after NxN search
+ return false;
+}
+
+/*!---------------------------------------------------------
+ * twinrx_freq_coercion_expert::resolve
+ * ---------------------------------------------------------
+ */
+void twinrx_freq_coercion_expert::resolve()
+{
+ const double actual_if2_freq = _if_freq_d;
+ const double actual_if1_freq = (_lo2_inj_side == INJ_LOW_SIDE) ?
+ (_lo2_freq_c + actual_if2_freq) : (_lo2_freq_c - actual_if2_freq);
+
+ _rf_freq_c = (_lo1_inj_side == INJ_LOW_SIDE) ?
+ (_lo1_freq_c + actual_if1_freq) : (_lo1_freq_c - actual_if1_freq);
+}
+
+/*!---------------------------------------------------------
+ * twinrx_nyquist_expert::resolve
+ * ---------------------------------------------------------
+ */
+void twinrx_nyquist_expert::resolve()
+{
+ double if_freq_sign = 1.0;
+ if (_lo1_inj_side == INJ_HIGH_SIDE) if_freq_sign *= -1.0;
+ if (_lo2_inj_side == INJ_HIGH_SIDE) if_freq_sign *= -1.0;
+ _if_freq_c = _if_freq_d * if_freq_sign;
+
+ _db_iface->set_fe_connection(dboard_iface::UNIT_RX, _channel,
+ usrp::fe_connection_t(_codec_conn, _if_freq_c));
+}
+
+/*!---------------------------------------------------------
+ * twinrx_chan_gain_expert::resolve
+ * ---------------------------------------------------------
+ */
+void twinrx_chan_gain_expert::resolve()
+{
+ if (_gain_profile != "default") {
+ //TODO: Implement me!
+ throw uhd::not_implemented_error("custom gain strategies not implemeted yet");
+ }
+
+ //Lookup table using settings
+ const twinrx_gain_table table = twinrx_gain_table::lookup_table(
+ _signal_path,
+ (_signal_path==twinrx_ctrl::PATH_HIGHBAND) ? _hb_presel : _lb_presel,
+ _gain_profile);
+
+ //Compute minimum gain. The user-specified gain value will be interpreted as
+ //the gain applied on top of the minimum gain state.
+ //If antennas are shared or swapped, the switch has 6dB of loss
+ size_t gain_index = std::min(static_cast<size_t>(boost::math::round(_gain.get())), table.get_num_entries()-1);
+
+ //Translate gain to an index in the gain table
+ const twinrx_gain_config_t& config = table.find_by_index(gain_index);
+
+ _input_atten = config.atten1;
+ if (_signal_path == twinrx_ctrl::PATH_HIGHBAND) {
+ _hb_atten = config.atten2;
+ } else {
+ _lb_atten = config.atten2;
+ }
+
+ // Preamp 1 should use the Highband amp for frequencies above 3 GHz
+ if (_signal_path == twinrx_ctrl::PATH_HIGHBAND && _hb_presel != twinrx_ctrl::PRESEL_PATH1) {
+ _preamp1 = config.amp1 ? twinrx_ctrl::PREAMP_HIGHBAND : twinrx_ctrl::PREAMP_BYPASS;
+ } else {
+ _preamp1 = config.amp1 ? twinrx_ctrl::PREAMP_LOWBAND : twinrx_ctrl::PREAMP_BYPASS;
+ }
+
+ _preamp2 = config.amp2;
+}
+
+/*!---------------------------------------------------------
+ * twinrx_lo_config_expert::resolve
+ * ---------------------------------------------------------
+ */
+void twinrx_lo_config_expert::resolve()
+{
+ static const uhd::dict<std::string, twinrx_ctrl::lo_source_t> src_lookup =
+ boost::assign::map_list_of
+ ("internal", twinrx_ctrl::LO_INTERNAL)
+ ("external", twinrx_ctrl::LO_EXTERNAL)
+ ("companion", twinrx_ctrl::LO_COMPANION)
+ ("disabled", twinrx_ctrl::LO_DISABLED);
+
+ if (src_lookup.has_key(_lo_source_ch0)) {
+ _lo1_src_ch0 = _lo2_src_ch0 = src_lookup[_lo_source_ch0];
+ } else {
+ throw uhd::value_error("Invalid LO source for channel 0.Choose from {internal, external, companion}");
+ }
+ if (src_lookup.has_key(_lo_source_ch1)) {
+ _lo1_src_ch1 = _lo2_src_ch1 = src_lookup[_lo_source_ch1];
+ } else {
+ throw uhd::value_error("Invalid LO source for channel 1.Choose from {internal, external, companion}");
+ }
+
+ twinrx_ctrl::lo_export_source_t export_src = twinrx_ctrl::LO_EXPORT_DISABLED;
+ if (_lo_export_ch0 and (_lo_source_ch0 == "external")) {
+ throw uhd::value_error("Cannot export an external LO for channel 0");
+ }
+ if (_lo_export_ch1 and (_lo_source_ch1 == "external")) {
+ throw uhd::value_error("Cannot export an external LO for channel 1");
+ }
+ if (_lo_export_ch0 and _lo_export_ch1) {
+ throw uhd::value_error("Cannot export LOs for both channels");
+ } else if (_lo_export_ch0) {
+ export_src = (_lo1_src_ch0 == twinrx_ctrl::LO_INTERNAL) ?
+ twinrx_ctrl::LO_CH1_SYNTH : twinrx_ctrl::LO_CH2_SYNTH;
+ } else if (_lo_export_ch1) {
+ export_src = (_lo1_src_ch1 == twinrx_ctrl::LO_INTERNAL) ?
+ twinrx_ctrl::LO_CH2_SYNTH : twinrx_ctrl::LO_CH1_SYNTH;
+ }
+ _lo1_export_src = _lo2_export_src = export_src;
+}
+
+/*!---------------------------------------------------------
+ * twinrx_lo_freq_expert::resolve
+ * ---------------------------------------------------------
+ */
+void twinrx_lo_mapping_expert::resolve()
+{
+ static const size_t CH0_MSK = 0x1;
+ static const size_t CH1_MSK = 0x2;
+
+ // Determine which channels are "driving" each synthesizer
+ // First check for explicit requests i.e. lo_source "internal" or "companion"
+ size_t synth_map[] = {0, 0};
+ if (_lox_src_ch0 == twinrx_ctrl::LO_INTERNAL) {
+ synth_map[0] = synth_map[0] | CH0_MSK;
+ } else if (_lox_src_ch0 == twinrx_ctrl::LO_COMPANION) {
+ synth_map[1] = synth_map[1] | CH0_MSK;
+ }
+ if (_lox_src_ch1 == twinrx_ctrl::LO_INTERNAL) {
+ synth_map[1] = synth_map[1] | CH1_MSK;
+ } else if (_lox_src_ch1 == twinrx_ctrl::LO_COMPANION) {
+ synth_map[0] = synth_map[0] | CH1_MSK;
+ }
+
+ // If a particular channel has its LO source disabled then the other
+ // channel is automatically put in hop mode i.e. the synthesizer that
+ // belongs to the disabled channel can be re-purposed as a redundant LO
+ // to overlap tuning with signal dwell time.
+ bool hopping_enabled = false;
+ if (_lox_src_ch0 == twinrx_ctrl::LO_DISABLED) {
+ if (_lox_src_ch1 == twinrx_ctrl::LO_INTERNAL) {
+ synth_map[0] = synth_map[0] | CH0_MSK;
+ hopping_enabled = true;
+ } else if (_lox_src_ch1 == twinrx_ctrl::LO_COMPANION) {
+ synth_map[1] = synth_map[1] | CH0_MSK;
+ hopping_enabled = true;
+ }
+ }
+ if (_lox_src_ch1 == twinrx_ctrl::LO_DISABLED) {
+ if (_lox_src_ch0 == twinrx_ctrl::LO_INTERNAL) {
+ synth_map[1] = synth_map[1] | CH1_MSK;
+ hopping_enabled = true;
+ } else if (_lox_src_ch0 == twinrx_ctrl::LO_COMPANION) {
+ synth_map[0] = synth_map[0] | CH1_MSK;
+ hopping_enabled = true;
+ }
+ }
+
+ // For each synthesizer come up with the final mapping
+ for (size_t synth = 0; synth < 2; synth++) {
+ experts::data_writer_t<lo_synth_mapping_t>& lox_mapping =
+ (synth == 0) ? _lox_mapping_synth0 : _lox_mapping_synth1;
+ if (synth_map[synth] == (CH0_MSK|CH1_MSK)) {
+ lox_mapping = MAPPING_SHARED;
+ } else if (synth_map[synth] == CH0_MSK) {
+ lox_mapping = MAPPING_CH0;
+ } else if (synth_map[synth] == CH1_MSK) {
+ lox_mapping = MAPPING_CH1;
+ } else {
+ lox_mapping = MAPPING_NONE;
+ }
+ }
+ _lox_hopping_enabled = hopping_enabled;
+}
+
+/*!---------------------------------------------------------
+ * twinrx_antenna_expert::resolve
+ * ---------------------------------------------------------
+ */
+void twinrx_antenna_expert::resolve()
+{
+ static const std::string ANT0 = "RX1", ANT1 = "RX2";
+
+ if (_antenna_ch0 == ANT0 and _antenna_ch1 == ANT1) {
+ _ant_mapping = twinrx_ctrl::ANTX_NATIVE;
+ } else if (_antenna_ch0 == ANT0 and _antenna_ch1 == ANT0) {
+ if (_enabled_ch0 and _enabled_ch1) {
+ _ant_mapping = twinrx_ctrl::ANT1_SHARED;
+ } else if (_enabled_ch0) {
+ _ant_mapping = twinrx_ctrl::ANTX_NATIVE;
+ } else if (_enabled_ch1) {
+ _ant_mapping = twinrx_ctrl::ANTX_SWAPPED;
+ }
+ } else if (_antenna_ch0 == ANT1 and _antenna_ch1 == ANT1) {
+ if (_enabled_ch0 and _enabled_ch1) {
+ _ant_mapping = twinrx_ctrl::ANT2_SHARED;
+ } else if (_enabled_ch0) {
+ _ant_mapping = twinrx_ctrl::ANTX_SWAPPED;
+ } else if (_enabled_ch1) {
+ _ant_mapping = twinrx_ctrl::ANTX_NATIVE;
+ }
+ } else if (_antenna_ch0 == ANT1 and _antenna_ch1 == ANT0) {
+ _ant_mapping = twinrx_ctrl::ANTX_SWAPPED;
+ } else if (_antenna_ch0 != ANT0 and _antenna_ch0 != ANT1) {
+ throw uhd::value_error("Invalid antenna selection " + _antenna_ch0.get() + " for channel 0. Must be " + ANT0 + " or " + ANT1);
+ } else if (_antenna_ch1 != ANT0 and _antenna_ch1 != ANT1) {
+ throw uhd::value_error("Invalid antenna selection " + _antenna_ch1.get() + " for channel 1. Must be " + ANT0 + " or " + ANT1);
+ }
+
+ //TODO: Implement hooks for the calibration switch
+ _cal_mode = twinrx_ctrl::CAL_DISABLED;
+
+ if (_cal_mode == twinrx_ctrl::CAL_CH1 and _lo_export_ch1) {
+ throw uhd::value_error("Cannot calibrate channel 0 and export the LO for channel 1.");
+ } else if (_cal_mode == twinrx_ctrl::CAL_CH2 and _lo_export_ch0) {
+ throw uhd::value_error("Cannot calibrate channel 1 and export the LO for channel 0.");
+ }
+}
+
+/*!---------------------------------------------------------
+ * twinrx_ant_gain_expert::resolve
+ * ---------------------------------------------------------
+ */
+void twinrx_ant_gain_expert::resolve()
+{
+ switch (_ant_mapping) {
+ case twinrx_ctrl::ANTX_NATIVE:
+ _ant0_input_atten = _ch0_input_atten;
+ _ant0_preamp1 = _ch0_preamp1;
+ _ant0_preamp2 = _ch0_preamp2;
+ _ant0_lb_preamp_presel = _ch0_lb_preamp_presel;
+ _ant1_input_atten = _ch1_input_atten;
+ _ant1_preamp1 = _ch1_preamp1;
+ _ant1_preamp2 = _ch1_preamp2;
+ _ant1_lb_preamp_presel = _ch1_lb_preamp_presel;
+ break;
+ case twinrx_ctrl::ANTX_SWAPPED:
+ _ant0_input_atten = _ch1_input_atten;
+ _ant0_preamp1 = _ch1_preamp1;
+ _ant0_preamp2 = _ch1_preamp2;
+ _ant0_lb_preamp_presel = _ch1_lb_preamp_presel;
+ _ant1_input_atten = _ch0_input_atten;
+ _ant1_preamp1 = _ch0_preamp1;
+ _ant1_preamp2 = _ch0_preamp2;
+ _ant1_lb_preamp_presel = _ch0_lb_preamp_presel;
+ break;
+ case twinrx_ctrl::ANT1_SHARED:
+ if ((_ch0_input_atten != _ch1_input_atten) or
+ (_ch0_preamp1 != _ch1_preamp1) or
+ (_ch0_preamp2 != _ch1_preamp2) or
+ (_ch0_lb_preamp_presel != _ch1_lb_preamp_presel))
+ {
+ UHD_MSG(warning) << "incompatible gain settings for antenna sharing. temporarily using Ch0 settings for Ch1.";
+ }
+ _ant0_input_atten = _ch0_input_atten;
+ _ant0_preamp1 = _ch0_preamp1;
+ _ant0_preamp2 = _ch0_preamp2;
+ _ant0_lb_preamp_presel = _ch0_lb_preamp_presel;
+
+ _ant1_input_atten = 0;
+ _ant1_preamp1 = twinrx_ctrl::PREAMP_BYPASS;
+ _ant1_preamp2 = false;
+ _ant1_lb_preamp_presel = false;
+ break;
+ case twinrx_ctrl::ANT2_SHARED:
+ if ((_ch0_input_atten != _ch1_input_atten) or
+ (_ch0_preamp1 != _ch1_preamp1) or
+ (_ch0_preamp2 != _ch1_preamp2) or
+ (_ch0_lb_preamp_presel != _ch1_lb_preamp_presel))
+ {
+ UHD_MSG(warning) << "incompatible gain settings for antenna sharing. temporarily using Ch0 settings for Ch1.";
+ }
+ _ant1_input_atten = _ch0_input_atten;
+ _ant1_preamp1 = _ch0_preamp1;
+ _ant1_preamp2 = _ch0_preamp2;
+ _ant1_lb_preamp_presel = _ch0_lb_preamp_presel;
+
+ _ant0_input_atten = 0;
+ _ant0_preamp1 = twinrx_ctrl::PREAMP_BYPASS;
+ _ant0_preamp2 = false;
+ _ant0_lb_preamp_presel = false;
+ break;
+ default:
+ _ant0_input_atten = 0;
+ _ant0_preamp1 = twinrx_ctrl::PREAMP_BYPASS;
+ _ant0_preamp2 = false;
+ _ant0_lb_preamp_presel = false;
+ _ant1_input_atten = 0;
+ _ant1_preamp1 = twinrx_ctrl::PREAMP_BYPASS;
+ _ant1_preamp2 = false;
+ _ant1_lb_preamp_presel = false;
+ break;
+ }
+}
+
+/*!---------------------------------------------------------
+ * twinrx_settings_expert::resolve
+ * ---------------------------------------------------------
+ */
+const bool twinrx_settings_expert::FORCE_COMMIT = false;
+
+void twinrx_settings_expert::resolve()
+{
+ for (size_t i = 0; i < 2; i++) {
+ ch_settings& ch_set = (i == 1) ? _ch1 : _ch0;
+ twinrx_ctrl::channel_t ch = (i == 1) ? twinrx_ctrl::CH2 : twinrx_ctrl::CH1;
+ _ctrl->set_chan_enabled(ch, ch_set.chan_enabled, FORCE_COMMIT);
+ _ctrl->set_preamp1(ch, ch_set.preamp1, FORCE_COMMIT);
+ _ctrl->set_preamp2(ch, ch_set.preamp2, FORCE_COMMIT);
+ _ctrl->set_lb_preamp_preselector(ch, ch_set.lb_preamp_presel, FORCE_COMMIT);
+ _ctrl->set_signal_path(ch, ch_set.signal_path, FORCE_COMMIT);
+ _ctrl->set_lb_preselector(ch, ch_set.lb_presel, FORCE_COMMIT);
+ _ctrl->set_hb_preselector(ch, ch_set.hb_presel, FORCE_COMMIT);
+ _ctrl->set_input_atten(ch, ch_set.input_atten, FORCE_COMMIT);
+ _ctrl->set_lb_atten(ch, ch_set.lb_atten, FORCE_COMMIT);
+ _ctrl->set_hb_atten(ch, ch_set.hb_atten, FORCE_COMMIT);
+ _ctrl->set_lo1_source(ch, ch_set.lo1_source, FORCE_COMMIT);
+ _ctrl->set_lo2_source(ch, ch_set.lo2_source, FORCE_COMMIT);
+ }
+
+ _resolve_lox_freq(STAGE_LO1,
+ _ch0.lo1_freq_d, _ch1.lo1_freq_d, _ch0.lo1_freq_c, _ch1.lo1_freq_c,
+ _ch0.lo1_source, _ch1.lo1_source, _lo1_synth0_mapping, _lo1_synth1_mapping,
+ _lo1_hopping_enabled);
+ _resolve_lox_freq(STAGE_LO2,
+ _ch0.lo2_freq_d, _ch1.lo2_freq_d, _ch0.lo2_freq_c, _ch1.lo2_freq_c,
+ _ch0.lo2_source, _ch1.lo2_source, _lo2_synth0_mapping, _lo2_synth1_mapping,
+ _lo2_hopping_enabled);
+
+ _ctrl->set_lo1_export_source(_lo1_export_src, FORCE_COMMIT);
+ _ctrl->set_lo2_export_source(_lo2_export_src, FORCE_COMMIT);
+ _ctrl->set_antenna_mapping(_ant_mapping, FORCE_COMMIT);
+ //TODO: Re-enable this when we support this mode
+ //_ctrl->set_crossover_cal_mode(_cal_mode, FORCE_COMMIT);
+
+ _ctrl->commit();
+}
+
+void twinrx_settings_expert::_resolve_lox_freq(
+ lo_stage_t lo_stage,
+ uhd::experts::data_reader_t<double>& ch0_freq_d,
+ uhd::experts::data_reader_t<double>& ch1_freq_d,
+ uhd::experts::data_writer_t<double>& ch0_freq_c,
+ uhd::experts::data_writer_t<double>& ch1_freq_c,
+ twinrx_ctrl::lo_source_t ch0_lo_source,
+ twinrx_ctrl::lo_source_t ch1_lo_source,
+ lo_synth_mapping_t synth0_mapping,
+ lo_synth_mapping_t synth1_mapping,
+ bool hopping_enabled)
+{
+ if (ch0_lo_source == twinrx_ctrl::LO_EXTERNAL) {
+ // If the LO is external then we don't need to program any synthesizers
+ ch0_freq_c = ch0_freq_d;
+ } else {
+ // When in hopping mode, only attempt to write the LO frequency if it is actually
+ // dirty to avoid reconfiguring the LO if it is being "double-buffered". If not
+ // hopping, then always write the frequency because other inputs might require
+ // an LO re-commit
+ const bool freq_update_request = (not hopping_enabled) or ch0_freq_d.is_dirty();
+ if (synth0_mapping == MAPPING_CH0 and freq_update_request) {
+ ch0_freq_c = _set_lox_synth_freq(lo_stage, twinrx_ctrl::CH1, ch0_freq_d);
+ } else if (synth1_mapping == MAPPING_CH0 and freq_update_request) {
+ ch0_freq_c = _set_lox_synth_freq(lo_stage, twinrx_ctrl::CH2, ch0_freq_d);
+ } else if (synth0_mapping == MAPPING_SHARED or synth1_mapping == MAPPING_SHARED) {
+ // If any synthesizer is being shared then we are not in hopping mode
+ if (rf_freq_ppm_t(ch0_freq_d) != ch1_freq_d) {
+ UHD_MSG(warning) <<
+ "Incompatible RF/LO frequencies for LO sharing. Using Ch0 settings for both channels.";
+ }
+ twinrx_ctrl::channel_t ch = (synth0_mapping == MAPPING_SHARED) ? twinrx_ctrl::CH1 : twinrx_ctrl::CH2;
+ ch0_freq_c = _set_lox_synth_freq(lo_stage, ch, ch0_freq_d);
+ ch1_freq_c = ch0_freq_c;
+ }
+ }
+
+ if (ch1_lo_source == twinrx_ctrl::LO_EXTERNAL) {
+ // If the LO is external then we don't need to program any synthesizers
+ ch1_freq_c = ch1_freq_d;
+ } else {
+ // When in hopping mode, only attempt to write the LO frequency if it is actually
+ // dirty to avoid reconfiguring the LO if it is being "double-buffered". If not
+ // hopping, then always write the frequency because other inputs might require
+ // an LO re-commit
+ const bool freq_update_request = (not hopping_enabled) or ch1_freq_d.is_dirty();
+ // As an additional layer of protection from unnecessarily commiting the LO, check
+ // if the frequency has actually changed.
+ if (synth0_mapping == MAPPING_CH1 and freq_update_request) {
+ ch1_freq_c = _set_lox_synth_freq(lo_stage, twinrx_ctrl::CH1, ch1_freq_d);
+ } else if (synth1_mapping == MAPPING_CH1 and freq_update_request) {
+ ch1_freq_c = _set_lox_synth_freq(lo_stage, twinrx_ctrl::CH2, ch1_freq_d);
+ } else if (synth0_mapping == MAPPING_SHARED or synth1_mapping == MAPPING_SHARED) {
+ // If any synthesizer is being shared then we are not in hopping mode
+ if (rf_freq_ppm_t(ch0_freq_d) != ch1_freq_d) {
+ UHD_MSG(warning) <<
+ "Incompatible RF/LO frequencies for LO sharing. Using Ch0 settings for both channels.";
+ }
+ twinrx_ctrl::channel_t ch = (synth0_mapping == MAPPING_SHARED) ? twinrx_ctrl::CH1 : twinrx_ctrl::CH2;
+ ch0_freq_c = _set_lox_synth_freq(lo_stage, ch, ch0_freq_d);
+ ch1_freq_c = ch0_freq_c;
+ }
+ }
+}
+
+double twinrx_settings_expert::_set_lox_synth_freq(lo_stage_t stage, twinrx_ctrl::channel_t ch, double freq)
+{
+ lo_freq_cache_t* freq_cache = NULL;
+ if (stage == STAGE_LO1) {
+ freq_cache = (ch == twinrx_ctrl::CH1) ? &_cached_lo1_synth0_freq : &_cached_lo1_synth1_freq;
+ } else if (stage == STAGE_LO2) {
+ freq_cache = (ch == twinrx_ctrl::CH1) ? &_cached_lo2_synth0_freq : &_cached_lo2_synth1_freq;
+ } else {
+ throw uhd::assertion_error("Invalid LO stage");
+ }
+
+ // Check if the frequency has actually changed before configuring synthesizers
+ double coerced_freq = 0.0;
+ if (freq_cache->desired != freq) {
+ if (stage == STAGE_LO1) {
+ coerced_freq = _ctrl->set_lo1_synth_freq(ch, freq, FORCE_COMMIT);
+ } else {
+ coerced_freq = _ctrl->set_lo2_synth_freq(ch, freq, FORCE_COMMIT);
+ }
+ freq_cache->desired = rf_freq_ppm_t(freq);
+ freq_cache->coerced = coerced_freq;
+ } else {
+ coerced_freq = freq_cache->coerced;
+ }
+ return coerced_freq;
+}
+
diff --git a/host/lib/usrp/dboard/twinrx/twinrx_experts.hpp b/host/lib/usrp/dboard/twinrx/twinrx_experts.hpp
new file mode 100644
index 000000000..f2601a09b
--- /dev/null
+++ b/host/lib/usrp/dboard/twinrx/twinrx_experts.hpp
@@ -0,0 +1,630 @@
+//
+// 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_DBOARD_TWINRX_EXPERTS_HPP
+#define INCLUDED_DBOARD_TWINRX_EXPERTS_HPP
+
+#include "twinrx_ctrl.hpp"
+#include "expert_nodes.hpp"
+#include <uhd/utils/math.hpp>
+
+namespace uhd { namespace usrp { namespace dboard { namespace twinrx {
+
+//---------------------------------------------------------
+// Misc types and definitions
+//---------------------------------------------------------
+
+struct rf_freq_abs_t : public uhd::math::fp_compare::fp_compare_delta<double> {
+ rf_freq_abs_t(double freq = 0.0, double epsilon = 1.0 /* 1Hz epsilon */) :
+ uhd::math::fp_compare::fp_compare_delta<double>(freq, epsilon) {}
+ inline double get() const { return _value; }
+};
+
+struct rf_freq_ppm_t : public rf_freq_abs_t {
+ rf_freq_ppm_t(double freq = 0.0, double epsilon_ppm = 0.1 /* 1PPM epsilon */) :
+ rf_freq_abs_t(freq, 1e-6 * freq * epsilon_ppm) {}
+};
+
+enum lo_stage_t { STAGE_LO1, STAGE_LO2 };
+enum lo_inj_side_t { INJ_LOW_SIDE, INJ_HIGH_SIDE };
+enum lo_synth_mapping_t { MAPPING_NONE, MAPPING_CH0, MAPPING_CH1, MAPPING_SHARED };
+
+static const std::string prepend_ch(std::string name, const std::string& ch) {
+ return ch + "/" + name;
+}
+
+static const std::string lo_stage_str(lo_stage_t stage, bool lower = false) {
+ std::string prefix = lower ? "lo" : "LO";
+ return prefix + ((stage == STAGE_LO1) ? "1" : "2");
+}
+
+/*!---------------------------------------------------------
+ * twinrx_freq_path_expert
+ *
+ * This expert is responsble for translating a user-specified
+ * RF and IF center frequency into TwinRX specific settings
+ * like band, preselector path, LO frequency and injection
+ * sides for both the LO stages.
+ *
+ * One instance of this expert is required for each channel
+ * ---------------------------------------------------------
+ */
+class twinrx_freq_path_expert : public experts::worker_node_t {
+public:
+ twinrx_freq_path_expert(const experts::node_retriever_t& db, std::string ch)
+ : experts::worker_node_t(prepend_ch("twinrx_freq_path_expert", ch)),
+ _rf_freq_d (db, prepend_ch("freq/desired", ch)),
+ _if_freq_d (db, prepend_ch("if_freq/desired", ch)),
+ _signal_path (db, prepend_ch("ch/signal_path", ch)),
+ _lb_presel (db, prepend_ch("ch/lb_presel", ch)),
+ _hb_presel (db, prepend_ch("ch/hb_presel", ch)),
+ _lb_preamp_presel (db, prepend_ch("ch/lb_preamp_presel", ch)),
+ _lo1_freq_d (db, prepend_ch("los/LO1/freq/desired", ch)),
+ _lo2_freq_d (db, prepend_ch("los/LO2/freq/desired", ch)),
+ _lo1_inj_side (db, prepend_ch("ch/LO1/inj_side", ch)),
+ _lo2_inj_side (db, prepend_ch("ch/LO2/inj_side", ch))
+ {
+ bind_accessor(_rf_freq_d);
+ bind_accessor(_if_freq_d);
+ bind_accessor(_signal_path);
+ bind_accessor(_lb_presel);
+ bind_accessor(_hb_presel);
+ bind_accessor(_lb_preamp_presel);
+ bind_accessor(_lo1_freq_d);
+ bind_accessor(_lo2_freq_d);
+ bind_accessor(_lo1_inj_side);
+ bind_accessor(_lo2_inj_side);
+ }
+
+private:
+ virtual void resolve();
+ static lo_inj_side_t _compute_lo2_inj_side(
+ double lo1_freq, double if1_freq, double if2_freq, double bandwidth);
+ static bool _has_mixer_spurs(
+ double lo1_freq, double lo2_freq, double if2_freq,
+ double bandwidth, int spur_order);
+
+ //Inputs
+ experts::data_reader_t<double> _rf_freq_d;
+ experts::data_reader_t<double> _if_freq_d;
+ //Outputs
+ experts::data_writer_t<twinrx_ctrl::signal_path_t> _signal_path;
+ experts::data_writer_t<twinrx_ctrl::preselector_path_t> _lb_presel;
+ experts::data_writer_t<twinrx_ctrl::preselector_path_t> _hb_presel;
+ experts::data_writer_t<bool> _lb_preamp_presel;
+ experts::data_writer_t<double> _lo1_freq_d;
+ experts::data_writer_t<double> _lo2_freq_d;
+ experts::data_writer_t<lo_inj_side_t> _lo1_inj_side;
+ experts::data_writer_t<lo_inj_side_t> _lo2_inj_side;
+};
+
+/*!---------------------------------------------------------
+ * twinrx_lo_config_expert
+ *
+ * This expert is responsible for translating high level
+ * channel-scoped LO source and export settings to low-level
+ * channel-scoped settings. The expert only deals with
+ * the source and export attributes, not frequency.
+ *
+ * One instance of this expert is required for all channels
+ * ---------------------------------------------------------
+ */
+class twinrx_lo_config_expert : public experts::worker_node_t {
+public:
+ twinrx_lo_config_expert(const experts::node_retriever_t& db)
+ : experts::worker_node_t("twinrx_lo_config_expert"),
+ _lo_source_ch0 (db, prepend_ch("los/all/source", "0")),
+ _lo_source_ch1 (db, prepend_ch("los/all/source", "1")),
+ _lo_export_ch0 (db, prepend_ch("los/all/export", "0")),
+ _lo_export_ch1 (db, prepend_ch("los/all/export", "1")),
+ _lo1_src_ch0 (db, prepend_ch("ch/LO1/source", "0")),
+ _lo1_src_ch1 (db, prepend_ch("ch/LO1/source", "1")),
+ _lo2_src_ch0 (db, prepend_ch("ch/LO2/source", "0")),
+ _lo2_src_ch1 (db, prepend_ch("ch/LO2/source", "1")),
+ _lo1_export_src (db, "com/LO1/export_source"),
+ _lo2_export_src (db, "com/LO2/export_source")
+ {
+ bind_accessor(_lo_source_ch0);
+ bind_accessor(_lo_source_ch1);
+ bind_accessor(_lo_export_ch0);
+ bind_accessor(_lo_export_ch1);
+ bind_accessor(_lo1_src_ch0);
+ bind_accessor(_lo1_src_ch1);
+ bind_accessor(_lo2_src_ch0);
+ bind_accessor(_lo2_src_ch1);
+ bind_accessor(_lo1_export_src);
+ bind_accessor(_lo2_export_src);
+ }
+
+private:
+ virtual void resolve();
+
+ //Inputs
+ experts::data_reader_t<std::string> _lo_source_ch0;
+ experts::data_reader_t<std::string> _lo_source_ch1;
+ experts::data_reader_t<bool> _lo_export_ch0;
+ experts::data_reader_t<bool> _lo_export_ch1;
+ //Outputs
+ experts::data_writer_t<twinrx_ctrl::lo_source_t> _lo1_src_ch0;
+ experts::data_writer_t<twinrx_ctrl::lo_source_t> _lo1_src_ch1;
+ experts::data_writer_t<twinrx_ctrl::lo_source_t> _lo2_src_ch0;
+ experts::data_writer_t<twinrx_ctrl::lo_source_t> _lo2_src_ch1;
+ experts::data_writer_t<twinrx_ctrl::lo_export_source_t> _lo1_export_src;
+ experts::data_writer_t<twinrx_ctrl::lo_export_source_t> _lo2_export_src;
+};
+
+/*!---------------------------------------------------------
+ * twinrx_lo_mapping_expert
+ *
+ * This expert is responsible for translating low-level
+ * channel-scoped LO source and export settings to low-level
+ * synthesizer-scoped settings. The expert deals with the
+ * extremely flexible channel->synthesizer mapping and handles
+ * frequency hopping modes.
+ *
+ * One instance of this expert is required for each LO stage
+ * ---------------------------------------------------------
+ */
+class twinrx_lo_mapping_expert : public experts::worker_node_t {
+public:
+ twinrx_lo_mapping_expert(const experts::node_retriever_t& db, lo_stage_t stage)
+ : experts::worker_node_t("twinrx_" + lo_stage_str(stage, true) + "_mapping_expert"),
+ _lox_src_ch0 (db, prepend_ch("ch/" + lo_stage_str(stage) + "/source", "0")),
+ _lox_src_ch1 (db, prepend_ch("ch/" + lo_stage_str(stage) + "/source", "1")),
+ _lox_mapping_synth0 (db, prepend_ch("synth/" + lo_stage_str(stage) + "/mapping", "0")),
+ _lox_mapping_synth1 (db, prepend_ch("synth/" + lo_stage_str(stage) + "/mapping", "1")),
+ _lox_hopping_enabled (db, "com/synth/" + lo_stage_str(stage) + "/hopping_enabled")
+ {
+ bind_accessor(_lox_src_ch0);
+ bind_accessor(_lox_src_ch1);
+ bind_accessor(_lox_mapping_synth0);
+ bind_accessor(_lox_mapping_synth1);
+ bind_accessor(_lox_hopping_enabled);
+ }
+
+private:
+ virtual void resolve();
+
+ //Inputs
+ experts::data_reader_t<twinrx_ctrl::lo_source_t> _lox_src_ch0;
+ experts::data_reader_t<twinrx_ctrl::lo_source_t> _lox_src_ch1;
+ //Outputs
+ experts::data_writer_t<lo_synth_mapping_t> _lox_mapping_synth0;
+ experts::data_writer_t<lo_synth_mapping_t> _lox_mapping_synth1;
+ experts::data_writer_t<bool> _lox_hopping_enabled;
+};
+
+/*!---------------------------------------------------------
+ * twinrx_freq_coercion_expert
+ *
+ * This expert is responsible for calculating the coerced
+ * RF frequency after most settings and modes have been
+ * resolved.
+ *
+ * One instance of this expert is required for each channel
+ * ---------------------------------------------------------
+ */
+class twinrx_freq_coercion_expert : public experts::worker_node_t {
+public:
+ twinrx_freq_coercion_expert(const experts::node_retriever_t& db, std::string ch)
+ : experts::worker_node_t(prepend_ch("twinrx_freq_coercion_expert", ch)),
+ _lo1_freq_c (db, prepend_ch("los/LO1/freq/coerced", ch)),
+ _lo2_freq_c (db, prepend_ch("los/LO2/freq/coerced", ch)),
+ _if_freq_d (db, prepend_ch("if_freq/desired", ch)),
+ _lo1_inj_side (db, prepend_ch("ch/LO1/inj_side", ch)),
+ _lo2_inj_side (db, prepend_ch("ch/LO2/inj_side", ch)),
+ _rf_freq_c (db, prepend_ch("freq/coerced", ch))
+ {
+ bind_accessor(_lo1_freq_c);
+ bind_accessor(_lo2_freq_c);
+ bind_accessor(_if_freq_d);
+ bind_accessor(_lo1_inj_side);
+ bind_accessor(_lo2_inj_side);
+ bind_accessor(_rf_freq_c);
+ }
+
+private:
+ virtual void resolve();
+
+ //Inputs
+ experts::data_reader_t<double> _lo1_freq_c;
+ experts::data_reader_t<double> _lo2_freq_c;
+ experts::data_reader_t<double> _if_freq_d;
+ experts::data_reader_t<lo_inj_side_t> _lo1_inj_side;
+ experts::data_reader_t<lo_inj_side_t> _lo2_inj_side;
+ //Outputs
+ experts::data_writer_t<double> _rf_freq_c;
+};
+
+/*!---------------------------------------------------------
+ * twinrx_nyquist_expert
+ *
+ * This expert is responsible for figuring out the DSP
+ * front-end settings required for each channel
+ *
+ * One instance of this expert is required for each channel
+ * ---------------------------------------------------------
+ */
+class twinrx_nyquist_expert : public experts::worker_node_t {
+public:
+ twinrx_nyquist_expert(const experts::node_retriever_t& db, std::string ch,
+ dboard_iface::sptr db_iface)
+ : experts::worker_node_t(prepend_ch("twinrx_nyquist_expert", ch)),
+ _channel (ch),
+ _codec_conn (ch=="0"?"II":"QQ"), //Ch->ADC Port mapping
+ _if_freq_d (db, prepend_ch("if_freq/desired", ch)),
+ _lo1_inj_side (db, prepend_ch("ch/LO1/inj_side", ch)),
+ _lo2_inj_side (db, prepend_ch("ch/LO2/inj_side", ch)),
+ _if_freq_c (db, prepend_ch("if_freq/coerced", ch)),
+ _db_iface (db_iface)
+ {
+ bind_accessor(_if_freq_d);
+ bind_accessor(_lo1_inj_side);
+ bind_accessor(_lo2_inj_side);
+ bind_accessor(_if_freq_c);
+ }
+
+private:
+ virtual void resolve();
+
+ //Inputs
+ const std::string _channel;
+ const std::string _codec_conn;
+ experts::data_reader_t<double> _if_freq_d;
+ experts::data_reader_t<lo_inj_side_t> _lo1_inj_side;
+ experts::data_reader_t<lo_inj_side_t> _lo2_inj_side;
+ //Outputs
+ experts::data_writer_t<double> _if_freq_c;
+ dboard_iface::sptr _db_iface;
+};
+
+/*!---------------------------------------------------------
+ * twinrx_antenna_expert
+ *
+ * This expert is responsible for translating high-level
+ * antenna selection settings and channel enables to low-level
+ * switch configurations.
+ *
+ * One instance of this expert is required for all channels
+ * ---------------------------------------------------------
+ */
+class twinrx_antenna_expert : public experts::worker_node_t {
+public:
+ twinrx_antenna_expert(const experts::node_retriever_t& db)
+ : experts::worker_node_t("twinrx_antenna_expert"),
+ _antenna_ch0 (db, prepend_ch("antenna", "0")),
+ _antenna_ch1 (db, prepend_ch("antenna", "1")),
+ _enabled_ch0 (db, prepend_ch("enabled", "0")),
+ _enabled_ch1 (db, prepend_ch("enabled", "1")),
+ _lo_export_ch0 (db, prepend_ch("los/all/export", "0")),
+ _lo_export_ch1 (db, prepend_ch("los/all/export", "1")),
+ _ant_mapping (db, "com/ant_mapping"),
+ _cal_mode (db, "com/cal_mode")
+ {
+ bind_accessor(_antenna_ch0);
+ bind_accessor(_antenna_ch1);
+ bind_accessor(_enabled_ch0);
+ bind_accessor(_enabled_ch1);
+ bind_accessor(_lo_export_ch0);
+ bind_accessor(_lo_export_ch1);
+ bind_accessor(_ant_mapping);
+ bind_accessor(_cal_mode);
+ }
+
+private:
+ virtual void resolve();
+
+ //Inputs
+ experts::data_reader_t<std::string> _antenna_ch0;
+ experts::data_reader_t<std::string> _antenna_ch1;
+ experts::data_reader_t<bool> _enabled_ch0;
+ experts::data_reader_t<bool> _enabled_ch1;
+ experts::data_reader_t<bool> _lo_export_ch0;
+ experts::data_reader_t<bool> _lo_export_ch1;
+ //Outputs
+ experts::data_writer_t<twinrx_ctrl::antenna_mapping_t> _ant_mapping;
+ experts::data_writer_t<twinrx_ctrl::cal_mode_t> _cal_mode;
+};
+
+/*!---------------------------------------------------------
+ * twinrx_chan_gain_expert
+ *
+ * This expert is responsible for mapping high-level channel
+ * gain settings to individual attenuator and amp configurations
+ * that are also channel-scoped. This expert will implement
+ * the gain distribution strategy.
+ *
+ * One instance of this expert is required for each channel
+ * ---------------------------------------------------------
+ */
+class twinrx_chan_gain_expert : public experts::worker_node_t {
+public:
+ twinrx_chan_gain_expert(const experts::node_retriever_t& db, std::string ch)
+ : experts::worker_node_t(prepend_ch("twinrx_chan_gain_expert", ch)),
+ _gain (db, prepend_ch("gain", ch)),
+ _gain_profile (db, prepend_ch("gain_profile", ch)),
+ _signal_path (db, prepend_ch("ch/signal_path", ch)),
+ _lb_presel (db, prepend_ch("ch/lb_presel", ch)),
+ _hb_presel (db, prepend_ch("ch/hb_presel", ch)),
+ _ant_mapping (db, "com/ant_mapping"),
+ _input_atten (db, prepend_ch("ch/input_atten", ch)),
+ _lb_atten (db, prepend_ch("ch/lb_atten", ch)),
+ _hb_atten (db, prepend_ch("ch/hb_atten", ch)),
+ _preamp1 (db, prepend_ch("ch/preamp1", ch)),
+ _preamp2 (db, prepend_ch("ch/preamp2", ch))
+ {
+ bind_accessor(_gain);
+ bind_accessor(_gain_profile);
+ bind_accessor(_signal_path);
+ bind_accessor(_lb_presel);
+ bind_accessor(_hb_presel);
+ bind_accessor(_ant_mapping);
+ bind_accessor(_input_atten);
+ bind_accessor(_lb_atten);
+ bind_accessor(_hb_atten);
+ bind_accessor(_preamp1);
+ bind_accessor(_preamp2);
+ }
+
+private:
+ virtual void resolve();
+
+ //Inputs
+ experts::data_reader_t<double> _gain;
+ experts::data_reader_t<std::string> _gain_profile;
+ experts::data_reader_t<twinrx_ctrl::signal_path_t> _signal_path;
+ experts::data_reader_t<twinrx_ctrl::preselector_path_t> _lb_presel;
+ experts::data_reader_t<twinrx_ctrl::preselector_path_t> _hb_presel;
+ experts::data_reader_t<twinrx_ctrl::antenna_mapping_t> _ant_mapping;
+ //Outputs
+ experts::data_writer_t<boost::uint8_t> _input_atten;
+ experts::data_writer_t<boost::uint8_t> _lb_atten;
+ experts::data_writer_t<boost::uint8_t> _hb_atten;
+ experts::data_writer_t<twinrx_ctrl::preamp_state_t> _preamp1;
+ experts::data_writer_t<bool> _preamp2;
+};
+
+/*!---------------------------------------------------------
+ * twinrx_ant_gain_expert
+ *
+ * This expert is responsible for translating between the
+ * channel-scoped low-level gain settings to antenna-scoped
+ * gain settings.
+ *
+ * One instance of this expert is required for all channels
+ * ---------------------------------------------------------
+ */
+class twinrx_ant_gain_expert : public experts::worker_node_t {
+public:
+ twinrx_ant_gain_expert(const experts::node_retriever_t& db)
+ : experts::worker_node_t("twinrx_ant_gain_expert"),
+ _ant_mapping (db, "com/ant_mapping"),
+ _ch0_input_atten (db, prepend_ch("ch/input_atten", "0")),
+ _ch0_preamp1 (db, prepend_ch("ch/preamp1", "0")),
+ _ch0_preamp2 (db, prepend_ch("ch/preamp2", "0")),
+ _ch0_lb_preamp_presel (db, prepend_ch("ch/lb_preamp_presel", "0")),
+ _ch1_input_atten (db, prepend_ch("ch/input_atten", "1")),
+ _ch1_preamp1 (db, prepend_ch("ch/preamp1", "1")),
+ _ch1_preamp2 (db, prepend_ch("ch/preamp2", "1")),
+ _ch1_lb_preamp_presel (db, prepend_ch("ch/lb_preamp_presel", "1")),
+ _ant0_input_atten (db, prepend_ch("ant/input_atten", "0")),
+ _ant0_preamp1 (db, prepend_ch("ant/preamp1", "0")),
+ _ant0_preamp2 (db, prepend_ch("ant/preamp2", "0")),
+ _ant0_lb_preamp_presel(db, prepend_ch("ant/lb_preamp_presel", "0")),
+ _ant1_input_atten (db, prepend_ch("ant/input_atten", "1")),
+ _ant1_preamp1 (db, prepend_ch("ant/preamp1", "1")),
+ _ant1_preamp2 (db, prepend_ch("ant/preamp2", "1")),
+ _ant1_lb_preamp_presel(db, prepend_ch("ant/lb_preamp_presel", "1"))
+ {
+ bind_accessor(_ant_mapping);
+ bind_accessor(_ch0_input_atten);
+ bind_accessor(_ch0_preamp1);
+ bind_accessor(_ch0_preamp2);
+ bind_accessor(_ch0_lb_preamp_presel);
+ bind_accessor(_ch1_input_atten);
+ bind_accessor(_ch1_preamp1);
+ bind_accessor(_ch1_preamp2);
+ bind_accessor(_ch1_lb_preamp_presel);
+ bind_accessor(_ant0_input_atten);
+ bind_accessor(_ant0_preamp1);
+ bind_accessor(_ant0_preamp2);
+ bind_accessor(_ant0_lb_preamp_presel);
+ bind_accessor(_ant1_input_atten);
+ bind_accessor(_ant1_preamp1);
+ bind_accessor(_ant1_preamp2);
+ bind_accessor(_ant1_lb_preamp_presel);
+ }
+
+private:
+ virtual void resolve();
+
+ //Inputs
+ experts::data_reader_t<twinrx_ctrl::antenna_mapping_t> _ant_mapping;
+ experts::data_reader_t<boost::uint8_t> _ch0_input_atten;
+ experts::data_reader_t<twinrx_ctrl::preamp_state_t> _ch0_preamp1;
+ experts::data_reader_t<bool> _ch0_preamp2;
+ experts::data_reader_t<bool> _ch0_lb_preamp_presel;
+ experts::data_reader_t<boost::uint8_t> _ch1_input_atten;
+ experts::data_reader_t<twinrx_ctrl::preamp_state_t> _ch1_preamp1;
+ experts::data_reader_t<bool> _ch1_preamp2;
+ experts::data_reader_t<bool> _ch1_lb_preamp_presel;
+
+ //Outputs
+ experts::data_writer_t<boost::uint8_t> _ant0_input_atten;
+ experts::data_writer_t<twinrx_ctrl::preamp_state_t> _ant0_preamp1;
+ experts::data_writer_t<bool> _ant0_preamp2;
+ experts::data_writer_t<bool> _ant0_lb_preamp_presel;
+ experts::data_writer_t<boost::uint8_t> _ant1_input_atten;
+ experts::data_writer_t<twinrx_ctrl::preamp_state_t> _ant1_preamp1;
+ experts::data_writer_t<bool> _ant1_preamp2;
+ experts::data_writer_t<bool> _ant1_lb_preamp_presel;
+};
+
+/*!---------------------------------------------------------
+ * twinrx_settings_expert
+ *
+ * This expert is responsible for gathering all low-level
+ * settings and writing them to hardware. All LO frequency
+ * settings are cached with a hysteresis. All other settings
+ * are always written to twinrx_ctrl and rely on register
+ * level caching.
+ *
+ * One instance of this expert is required for all channels
+ * ---------------------------------------------------------
+ */
+class twinrx_settings_expert : public experts::worker_node_t {
+public:
+ twinrx_settings_expert(const experts::node_retriever_t& db, twinrx_ctrl::sptr ctrl)
+ : experts::worker_node_t("twinrx_settings_expert"), _ctrl(ctrl),
+ _ch0 (db, "0"),
+ _ch1 (db, "1"),
+ _lo1_synth0_mapping(db, "0/synth/LO1/mapping"),
+ _lo1_synth1_mapping(db, "1/synth/LO1/mapping"),
+ _lo2_synth0_mapping(db, "0/synth/LO2/mapping"),
+ _lo2_synth1_mapping(db, "1/synth/LO2/mapping"),
+ _lo1_hopping_enabled(db, "com/synth/LO1/hopping_enabled"),
+ _lo2_hopping_enabled(db, "com/synth/LO2/hopping_enabled"),
+ _lo1_export_src (db, "com/LO1/export_source"),
+ _lo2_export_src (db, "com/LO2/export_source"),
+ _ant_mapping (db, "com/ant_mapping"),
+ _cal_mode (db, "com/cal_mode")
+ {
+ for (size_t i = 0; i < 2; i++) {
+ ch_settings& ch = (i==1) ? _ch1 : _ch0;
+ bind_accessor(ch.chan_enabled);
+ bind_accessor(ch.preamp1);
+ bind_accessor(ch.preamp2);
+ bind_accessor(ch.lb_preamp_presel);
+ bind_accessor(ch.signal_path);
+ bind_accessor(ch.lb_presel);
+ bind_accessor(ch.hb_presel);
+ bind_accessor(ch.input_atten);
+ bind_accessor(ch.lb_atten);
+ bind_accessor(ch.hb_atten);
+ bind_accessor(ch.lo1_source);
+ bind_accessor(ch.lo2_source);
+ bind_accessor(ch.lo1_freq_d);
+ bind_accessor(ch.lo2_freq_d);
+ bind_accessor(ch.lo1_freq_c);
+ bind_accessor(ch.lo2_freq_c);
+ }
+ bind_accessor(_lo1_synth0_mapping);
+ bind_accessor(_lo1_synth1_mapping);
+ bind_accessor(_lo2_synth0_mapping);
+ bind_accessor(_lo2_synth1_mapping);
+ bind_accessor(_lo1_hopping_enabled);
+ bind_accessor(_lo2_hopping_enabled);
+ bind_accessor(_lo1_export_src);
+ bind_accessor(_lo2_export_src);
+ bind_accessor(_ant_mapping);
+ bind_accessor(_cal_mode);
+ }
+
+private:
+ virtual void resolve();
+ void _resolve_lox_freq(
+ lo_stage_t lo_stage,
+ experts::data_reader_t<double>& ch0_freq_d,
+ experts::data_reader_t<double>& ch1_freq_d,
+ experts::data_writer_t<double>& ch0_freq_c,
+ experts::data_writer_t<double>& ch1_freq_c,
+ twinrx_ctrl::lo_source_t ch0_lo_source,
+ twinrx_ctrl::lo_source_t ch1_lo_source,
+ lo_synth_mapping_t synth0_mapping,
+ lo_synth_mapping_t synth1_mapping,
+ bool hopping_enabled);
+ double _set_lox_synth_freq(lo_stage_t stage, twinrx_ctrl::channel_t ch, double freq);
+
+ class ch_settings {
+ public:
+ ch_settings(const experts::node_retriever_t& db, const std::string& ch) :
+ chan_enabled (db, prepend_ch("enabled", ch)),
+ preamp1 (db, prepend_ch("ant/preamp1", ch)),
+ preamp2 (db, prepend_ch("ant/preamp2", ch)),
+ lb_preamp_presel(db, prepend_ch("ant/lb_preamp_presel", ch)),
+ signal_path (db, prepend_ch("ch/signal_path", ch)),
+ lb_presel (db, prepend_ch("ch/lb_presel", ch)),
+ hb_presel (db, prepend_ch("ch/hb_presel", ch)),
+ input_atten (db, prepend_ch("ant/input_atten", ch)),
+ lb_atten (db, prepend_ch("ch/lb_atten", ch)),
+ hb_atten (db, prepend_ch("ch/hb_atten", ch)),
+ lo1_source (db, prepend_ch("ch/LO1/source", ch)),
+ lo2_source (db, prepend_ch("ch/LO2/source", ch)),
+ lo1_freq_d (db, prepend_ch("los/LO1/freq/desired", ch)),
+ lo2_freq_d (db, prepend_ch("los/LO2/freq/desired", ch)),
+ lo1_freq_c (db, prepend_ch("los/LO1/freq/coerced", ch)),
+ lo2_freq_c (db, prepend_ch("los/LO2/freq/coerced", ch))
+ {}
+
+ //Inputs (channel specific)
+ experts::data_reader_t<bool> chan_enabled;
+ experts::data_reader_t<twinrx_ctrl::preamp_state_t> preamp1;
+ experts::data_reader_t<bool> preamp2;
+ experts::data_reader_t<bool> lb_preamp_presel;
+ experts::data_reader_t<twinrx_ctrl::signal_path_t> signal_path;
+ experts::data_reader_t<twinrx_ctrl::preselector_path_t> lb_presel;
+ experts::data_reader_t<twinrx_ctrl::preselector_path_t> hb_presel;
+ experts::data_reader_t<boost::uint8_t> input_atten;
+ experts::data_reader_t<boost::uint8_t> lb_atten;
+ experts::data_reader_t<boost::uint8_t> hb_atten;
+ experts::data_reader_t<twinrx_ctrl::lo_source_t> lo1_source;
+ experts::data_reader_t<twinrx_ctrl::lo_source_t> lo2_source;
+ experts::data_reader_t<double> lo1_freq_d;
+ experts::data_reader_t<double> lo2_freq_d;
+
+ //Output (channel specific)
+ experts::data_writer_t<double> lo1_freq_c;
+ experts::data_writer_t<double> lo2_freq_c;
+ };
+
+ //External interface
+ twinrx_ctrl::sptr _ctrl;
+
+ //Inputs (channel agnostic)
+ ch_settings _ch0;
+ ch_settings _ch1;
+ experts::data_reader_t<lo_synth_mapping_t> _lo1_synth0_mapping;
+ experts::data_reader_t<lo_synth_mapping_t> _lo1_synth1_mapping;
+ experts::data_reader_t<lo_synth_mapping_t> _lo2_synth0_mapping;
+ experts::data_reader_t<lo_synth_mapping_t> _lo2_synth1_mapping;
+ experts::data_reader_t<bool> _lo1_hopping_enabled;
+ experts::data_reader_t<bool> _lo2_hopping_enabled;
+ experts::data_reader_t<twinrx_ctrl::lo_export_source_t> _lo1_export_src;
+ experts::data_reader_t<twinrx_ctrl::lo_export_source_t> _lo2_export_src;
+ experts::data_reader_t<twinrx_ctrl::antenna_mapping_t> _ant_mapping;
+ experts::data_reader_t<twinrx_ctrl::cal_mode_t> _cal_mode;
+
+ //Outputs (channel agnostic)
+ //None
+
+ //Misc
+ struct lo_freq_cache_t {
+ rf_freq_ppm_t desired;
+ double coerced;
+ };
+ lo_freq_cache_t _cached_lo1_synth0_freq;
+ lo_freq_cache_t _cached_lo2_synth0_freq;
+ lo_freq_cache_t _cached_lo1_synth1_freq;
+ lo_freq_cache_t _cached_lo2_synth1_freq;
+
+ static const bool FORCE_COMMIT;
+};
+
+
+}}}} //namespaces
+
+#endif /* INCLUDED_DBOARD_TWINRX_EXPERTS_HPP */
diff --git a/host/lib/usrp/dboard/twinrx/twinrx_gain_tables.cpp b/host/lib/usrp/dboard/twinrx/twinrx_gain_tables.cpp
new file mode 100644
index 000000000..5cc8b49f3
--- /dev/null
+++ b/host/lib/usrp/dboard/twinrx/twinrx_gain_tables.cpp
@@ -0,0 +1,860 @@
+//
+// 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 "twinrx_gain_tables.hpp"
+#include <uhd/exception.hpp>
+#include <boost/assign/list_of.hpp>
+
+using namespace uhd::usrp::dboard::twinrx;
+
+static const std::vector<twinrx_gain_config_t> HIGHBAND1_TABLE = boost::assign::list_of
+ // Index, Gain, Atten1, Atten2, Amp1, Amp2
+ ( twinrx_gain_config_t( 0, -28.3, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 1, -28.3, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 2, -28.3, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 3, -28.3, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 4, -28.3, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 5, -28.3, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 6, -28.3, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 7, -27.3, 31, 30, false, false ) )
+ ( twinrx_gain_config_t( 8, -26.3, 31, 29, false, false ) )
+ ( twinrx_gain_config_t( 9, -25.3, 31, 28, false, false ) )
+ ( twinrx_gain_config_t( 10, -24.3, 31, 27, false, false ) )
+ ( twinrx_gain_config_t( 11, -23.3, 31, 26, false, false ) )
+ ( twinrx_gain_config_t( 12, -22.3, 31, 25, false, false ) )
+ ( twinrx_gain_config_t( 13, -21.3, 31, 24, false, false ) )
+ ( twinrx_gain_config_t( 14, -20.3, 31, 23, false, false ) )
+ ( twinrx_gain_config_t( 15, -19.3, 31, 22, false, false ) )
+ ( twinrx_gain_config_t( 16, -18.3, 31, 21, false, false ) )
+ ( twinrx_gain_config_t( 17, -17.3, 31, 20, false, false ) )
+ ( twinrx_gain_config_t( 18, -16.3, 31, 19, false, false ) )
+ ( twinrx_gain_config_t( 19, -15.3, 31, 18, false, false ) )
+ ( twinrx_gain_config_t( 20, -14.3, 31, 17, false, false ) )
+ ( twinrx_gain_config_t( 21, -13.3, 31, 16, false, false ) )
+ ( twinrx_gain_config_t( 22, -12.3, 31, 15, false, false ) )
+ ( twinrx_gain_config_t( 23, -11.3, 31, 14, false, false ) )
+ ( twinrx_gain_config_t( 24, -10.3, 31, 13, false, false ) )
+ ( twinrx_gain_config_t( 25, -9.3, 31, 12, false, false ) )
+ ( twinrx_gain_config_t( 26, -8.3, 30, 12, false, false ) )
+ ( twinrx_gain_config_t( 27, -7.3, 30, 11, false, false ) )
+ ( twinrx_gain_config_t( 28, -6.3, 29, 11, false, false ) )
+ ( twinrx_gain_config_t( 29, -5.3, 28, 11, false, false ) )
+ ( twinrx_gain_config_t( 30, -4.3, 27, 11, false, false ) )
+ ( twinrx_gain_config_t( 31, -3.3, 27, 10, false, false ) )
+ ( twinrx_gain_config_t( 32, -2.3, 26, 10, false, false ) )
+ ( twinrx_gain_config_t( 33, -1.3, 25, 10, false, false ) )
+ ( twinrx_gain_config_t( 34, -0.3, 24, 10, false, false ) )
+ ( twinrx_gain_config_t( 35, 0.7, 23, 10, false, false ) )
+ ( twinrx_gain_config_t( 36, 1.7, 22, 10, false, false ) )
+ ( twinrx_gain_config_t( 37, 2.7, 21, 10, false, false ) )
+ ( twinrx_gain_config_t( 38, 3.7, 21, 9, false, false ) )
+ ( twinrx_gain_config_t( 39, 4.7, 20, 9, false, false ) )
+ ( twinrx_gain_config_t( 40, 5.7, 19, 9, false, false ) )
+ ( twinrx_gain_config_t( 41, 6.7, 18, 9, false, false ) )
+ ( twinrx_gain_config_t( 42, 7.7, 17, 9, false, false ) )
+ ( twinrx_gain_config_t( 43, 8.7, 16, 9, false, false ) )
+ ( twinrx_gain_config_t( 44, 9.7, 15, 9, false, false ) )
+ ( twinrx_gain_config_t( 45, 10.7, 14, 9, false, false ) )
+ ( twinrx_gain_config_t( 46, 11.7, 13, 9, false, false ) )
+ ( twinrx_gain_config_t( 47, 12.7, 12, 9, false, false ) )
+ ( twinrx_gain_config_t( 48, 13.7, 11, 9, false, false ) )
+ ( twinrx_gain_config_t( 49, 14.7, 10, 9, false, false ) )
+ ( twinrx_gain_config_t( 50, 15.7, 9, 9, false, false ) )
+ ( twinrx_gain_config_t( 51, 16.7, 8, 9, false, false ) )
+ ( twinrx_gain_config_t( 52, 17.7, 7, 9, false, false ) )
+ ( twinrx_gain_config_t( 53, 18.7, 6, 9, false, false ) )
+ ( twinrx_gain_config_t( 54, 19.7, 5, 9, false, false ) )
+ ( twinrx_gain_config_t( 55, 20.7, 4, 9, false, false ) )
+ ( twinrx_gain_config_t( 56, 21.7, 3, 9, false, false ) )
+ ( twinrx_gain_config_t( 57, 22.7, 2, 9, false, false ) )
+ ( twinrx_gain_config_t( 58, 23.7, 1, 9, false, false ) )
+ ( twinrx_gain_config_t( 59, 24.7, 0, 9, false, false ) )
+ ( twinrx_gain_config_t( 60, 25.7, 0, 8, false, false ) )
+ ( twinrx_gain_config_t( 61, 26.7, 0, 7, false, false ) )
+ ( twinrx_gain_config_t( 62, 27.7, 0, 6, false, false ) )
+ ( twinrx_gain_config_t( 63, 28.7, 0, 5, false, false ) )
+ ( twinrx_gain_config_t( 64, 29.7, 0, 4, false, false ) )
+ ( twinrx_gain_config_t( 65, 30.7, 0, 3, false, false ) )
+ ( twinrx_gain_config_t( 66, 31.7, 0, 2, false, false ) )
+ ( twinrx_gain_config_t( 67, 32.7, 0, 1, false, false ) )
+ ( twinrx_gain_config_t( 68, 33.7, 0, 0, false, false ) )
+ ( twinrx_gain_config_t( 69, 33.9, 3, 9, true, false ) )
+ ( twinrx_gain_config_t( 70, 34.9, 2, 9, true, false ) )
+ ( twinrx_gain_config_t( 71, 35.9, 1, 9, true, false ) )
+ ( twinrx_gain_config_t( 72, 36.9, 0, 9, true, false ) )
+ ( twinrx_gain_config_t( 73, 37.9, 0, 8, true, false ) )
+ ( twinrx_gain_config_t( 74, 38.9, 0, 7, true, false ) )
+ ( twinrx_gain_config_t( 75, 39.9, 0, 6, true, false ) )
+ ( twinrx_gain_config_t( 76, 40.9, 0, 5, true, false ) )
+ ( twinrx_gain_config_t( 77, 41.9, 0, 4, true, false ) )
+ ( twinrx_gain_config_t( 78, 42.9, 0, 3, true, false ) )
+ ( twinrx_gain_config_t( 79, 43.9, 0, 2, true, false ) )
+ ( twinrx_gain_config_t( 80, 44.9, 0, 1, true, false ) )
+ ( twinrx_gain_config_t( 81, 45.9, 0, 0, true, false ) )
+ ( twinrx_gain_config_t( 82, 47.3, 1, 10, true, true ) )
+ ( twinrx_gain_config_t( 83, 48.3, 0, 10, true, true ) )
+ ( twinrx_gain_config_t( 84, 49.3, 0, 9, true, true ) )
+ ( twinrx_gain_config_t( 85, 50.3, 0, 8, true, true ) )
+ ( twinrx_gain_config_t( 86, 51.3, 0, 7, true, true ) )
+ ( twinrx_gain_config_t( 87, 52.3, 0, 6, true, true ) )
+ ( twinrx_gain_config_t( 88, 53.3, 0, 5, true, true ) )
+ ( twinrx_gain_config_t( 89, 54.3, 0, 4, true, true ) )
+ ( twinrx_gain_config_t( 90, 55.3, 0, 3, true, true ) )
+ ( twinrx_gain_config_t( 91, 56.3, 0, 2, true, true ) )
+ ( twinrx_gain_config_t( 92, 57.3, 0, 1, true, true ) )
+ ( twinrx_gain_config_t( 93, 58.3, 0, 0, true, true ) )
+;
+
+static const std::vector<twinrx_gain_config_t> HIGHBAND2_TABLE = boost::assign::list_of
+ // Index, Gain, Atten1, Atten2, Amp1, Amp2
+ ( twinrx_gain_config_t( 0, -30.9, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 1, -30.9, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 2, -30.9, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 3, -30.9, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 4, -30.9, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 5, -30.9, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 6, -30.9, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 7, -30.9, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 8, -29.9, 31, 30, false, false ) )
+ ( twinrx_gain_config_t( 9, -28.9, 31, 29, false, false ) )
+ ( twinrx_gain_config_t( 10, -27.9, 31, 28, false, false ) )
+ ( twinrx_gain_config_t( 11, -26.9, 31, 27, false, false ) )
+ ( twinrx_gain_config_t( 12, -25.9, 31, 26, false, false ) )
+ ( twinrx_gain_config_t( 13, -24.9, 31, 25, false, false ) )
+ ( twinrx_gain_config_t( 14, -23.9, 31, 24, false, false ) )
+ ( twinrx_gain_config_t( 15, -22.9, 31, 23, false, false ) )
+ ( twinrx_gain_config_t( 16, -21.9, 31, 22, false, false ) )
+ ( twinrx_gain_config_t( 17, -20.9, 31, 21, false, false ) )
+ ( twinrx_gain_config_t( 18, -19.9, 31, 20, false, false ) )
+ ( twinrx_gain_config_t( 19, -18.9, 31, 19, false, false ) )
+ ( twinrx_gain_config_t( 20, -17.9, 31, 18, false, false ) )
+ ( twinrx_gain_config_t( 21, -16.9, 31, 17, false, false ) )
+ ( twinrx_gain_config_t( 22, -15.9, 31, 16, false, false ) )
+ ( twinrx_gain_config_t( 23, -14.9, 31, 15, false, false ) )
+ ( twinrx_gain_config_t( 24, -13.9, 31, 14, false, false ) )
+ ( twinrx_gain_config_t( 25, -12.9, 31, 13, false, false ) )
+ ( twinrx_gain_config_t( 26, -11.9, 31, 12, false, false ) )
+ ( twinrx_gain_config_t( 27, -10.9, 31, 11, false, false ) )
+ ( twinrx_gain_config_t( 28, -9.9, 30, 11, false, false ) )
+ ( twinrx_gain_config_t( 29, -8.9, 29, 11, false, false ) )
+ ( twinrx_gain_config_t( 30, -7.9, 29, 10, false, false ) )
+ ( twinrx_gain_config_t( 31, -6.9, 28, 10, false, false ) )
+ ( twinrx_gain_config_t( 32, -5.9, 27, 10, false, false ) )
+ ( twinrx_gain_config_t( 33, -4.9, 27, 9, false, false ) )
+ ( twinrx_gain_config_t( 34, -3.9, 26, 9, false, false ) )
+ ( twinrx_gain_config_t( 35, -2.9, 25, 9, false, false ) )
+ ( twinrx_gain_config_t( 36, -1.9, 24, 9, false, false ) )
+ ( twinrx_gain_config_t( 37, -0.9, 23, 9, false, false ) )
+ ( twinrx_gain_config_t( 38, 0.1, 23, 8, false, false ) )
+ ( twinrx_gain_config_t( 39, 1.1, 22, 8, false, false ) )
+ ( twinrx_gain_config_t( 40, 2.1, 21, 8, false, false ) )
+ ( twinrx_gain_config_t( 41, 3.1, 20, 8, false, false ) )
+ ( twinrx_gain_config_t( 42, 4.1, 19, 8, false, false ) )
+ ( twinrx_gain_config_t( 43, 5.1, 18, 8, false, false ) )
+ ( twinrx_gain_config_t( 44, 6.1, 17, 8, false, false ) )
+ ( twinrx_gain_config_t( 45, 7.1, 16, 8, false, false ) )
+ ( twinrx_gain_config_t( 46, 8.1, 15, 8, false, false ) )
+ ( twinrx_gain_config_t( 47, 9.1, 14, 8, false, false ) )
+ ( twinrx_gain_config_t( 48, 10.1, 13, 8, false, false ) )
+ ( twinrx_gain_config_t( 49, 11.1, 12, 8, false, false ) )
+ ( twinrx_gain_config_t( 50, 12.1, 11, 8, false, false ) )
+ ( twinrx_gain_config_t( 51, 13.1, 10, 8, false, false ) )
+ ( twinrx_gain_config_t( 52, 14.1, 9, 8, false, false ) )
+ ( twinrx_gain_config_t( 53, 15.1, 8, 8, false, false ) )
+ ( twinrx_gain_config_t( 54, 16.1, 7, 8, false, false ) )
+ ( twinrx_gain_config_t( 55, 17.1, 6, 8, false, false ) )
+ ( twinrx_gain_config_t( 56, 18.1, 5, 8, false, false ) )
+ ( twinrx_gain_config_t( 57, 19.1, 4, 8, false, false ) )
+ ( twinrx_gain_config_t( 58, 20.1, 3, 8, false, false ) )
+ ( twinrx_gain_config_t( 59, 21.1, 2, 8, false, false ) )
+ ( twinrx_gain_config_t( 60, 22.1, 1, 8, false, false ) )
+ ( twinrx_gain_config_t( 61, 23.1, 0, 8, false, false ) )
+ ( twinrx_gain_config_t( 62, 24.1, 0, 7, false, false ) )
+ ( twinrx_gain_config_t( 63, 25.1, 0, 6, false, false ) )
+ ( twinrx_gain_config_t( 64, 26.1, 0, 5, false, false ) )
+ ( twinrx_gain_config_t( 65, 27.1, 0, 4, false, false ) )
+ ( twinrx_gain_config_t( 66, 28.1, 0, 3, false, false ) )
+ ( twinrx_gain_config_t( 67, 29.1, 0, 2, false, false ) )
+ ( twinrx_gain_config_t( 68, 30.1, 0, 1, false, false ) )
+ ( twinrx_gain_config_t( 69, 31.9, 0, 10, false, true ) )
+ ( twinrx_gain_config_t( 70, 31.9, 0, 10, false, true ) )
+ ( twinrx_gain_config_t( 71, 32.9, 0, 9, false, true ) )
+ ( twinrx_gain_config_t( 72, 33.9, 0, 8, false, true ) )
+ ( twinrx_gain_config_t( 73, 34.9, 0, 7, false, true ) )
+ ( twinrx_gain_config_t( 74, 35.9, 0, 6, false, true ) )
+ ( twinrx_gain_config_t( 75, 36.9, 0, 5, false, true ) )
+ ( twinrx_gain_config_t( 76, 38.6, 0, 6, true, false ) )
+ ( twinrx_gain_config_t( 77, 39.6, 0, 5, true, false ) )
+ ( twinrx_gain_config_t( 78, 40.6, 0, 4, true, false ) )
+ ( twinrx_gain_config_t( 79, 41.6, 0, 3, true, false ) )
+ ( twinrx_gain_config_t( 80, 42.6, 0, 2, true, false ) )
+ ( twinrx_gain_config_t( 81, 43.6, 0, 1, true, false ) )
+ ( twinrx_gain_config_t( 82, 44.4, 2, 9, true, true ) )
+ ( twinrx_gain_config_t( 83, 45.4, 1, 9, true, true ) )
+ ( twinrx_gain_config_t( 84, 46.4, 0, 9, true, true ) )
+ ( twinrx_gain_config_t( 85, 47.4, 0, 8, true, true ) )
+ ( twinrx_gain_config_t( 86, 48.4, 0, 7, true, true ) )
+ ( twinrx_gain_config_t( 87, 49.4, 0, 6, true, true ) )
+ ( twinrx_gain_config_t( 88, 50.4, 0, 5, true, true ) )
+ ( twinrx_gain_config_t( 89, 51.4, 0, 4, true, true ) )
+ ( twinrx_gain_config_t( 90, 52.4, 0, 3, true, true ) )
+ ( twinrx_gain_config_t( 91, 53.4, 0, 2, true, true ) )
+ ( twinrx_gain_config_t( 92, 54.4, 0, 1, true, true ) )
+ ( twinrx_gain_config_t( 93, 55.4, 0, 0, true, true ) )
+;
+
+static const std::vector<twinrx_gain_config_t> HIGHBAND3_TABLE = boost::assign::list_of
+ // Index, Gain, Atten1, Atten2, Amp1, Amp2
+ ( twinrx_gain_config_t( 0, -31.1, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 1, -31.1, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 2, -31.1, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 3, -31.1, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 4, -31.1, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 5, -31.1, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 6, -31.1, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 7, -30.1, 31, 30, false, false ) )
+ ( twinrx_gain_config_t( 8, -29.1, 31, 29, false, false ) )
+ ( twinrx_gain_config_t( 9, -28.1, 31, 28, false, false ) )
+ ( twinrx_gain_config_t( 10, -27.1, 31, 27, false, false ) )
+ ( twinrx_gain_config_t( 11, -26.1, 31, 26, false, false ) )
+ ( twinrx_gain_config_t( 12, -25.1, 31, 25, false, false ) )
+ ( twinrx_gain_config_t( 13, -24.1, 31, 24, false, false ) )
+ ( twinrx_gain_config_t( 14, -23.1, 31, 23, false, false ) )
+ ( twinrx_gain_config_t( 15, -22.1, 31, 22, false, false ) )
+ ( twinrx_gain_config_t( 16, -21.1, 31, 21, false, false ) )
+ ( twinrx_gain_config_t( 17, -20.1, 31, 20, false, false ) )
+ ( twinrx_gain_config_t( 18, -19.1, 31, 19, false, false ) )
+ ( twinrx_gain_config_t( 19, -18.1, 31, 18, false, false ) )
+ ( twinrx_gain_config_t( 20, -17.1, 31, 17, false, false ) )
+ ( twinrx_gain_config_t( 21, -16.1, 31, 16, false, false ) )
+ ( twinrx_gain_config_t( 22, -15.1, 31, 15, false, false ) )
+ ( twinrx_gain_config_t( 23, -14.1, 31, 14, false, false ) )
+ ( twinrx_gain_config_t( 24, -13.1, 31, 13, false, false ) )
+ ( twinrx_gain_config_t( 25, -12.1, 30, 13, false, false ) )
+ ( twinrx_gain_config_t( 26, -11.1, 30, 12, false, false ) )
+ ( twinrx_gain_config_t( 27, -10.1, 29, 12, false, false ) )
+ ( twinrx_gain_config_t( 28, -9.1, 28, 12, false, false ) )
+ ( twinrx_gain_config_t( 29, -8.1, 28, 11, false, false ) )
+ ( twinrx_gain_config_t( 30, -7.1, 27, 11, false, false ) )
+ ( twinrx_gain_config_t( 31, -6.1, 26, 11, false, false ) )
+ ( twinrx_gain_config_t( 32, -5.1, 26, 10, false, false ) )
+ ( twinrx_gain_config_t( 33, -4.1, 25, 10, false, false ) )
+ ( twinrx_gain_config_t( 34, -3.1, 24, 10, false, false ) )
+ ( twinrx_gain_config_t( 35, -2.1, 23, 10, false, false ) )
+ ( twinrx_gain_config_t( 36, -1.1, 22, 10, false, false ) )
+ ( twinrx_gain_config_t( 37, -0.1, 21, 10, false, false ) )
+ ( twinrx_gain_config_t( 38, 0.9, 21, 9, false, false ) )
+ ( twinrx_gain_config_t( 39, 1.9, 20, 9, false, false ) )
+ ( twinrx_gain_config_t( 40, 2.9, 19, 9, false, false ) )
+ ( twinrx_gain_config_t( 41, 3.9, 18, 9, false, false ) )
+ ( twinrx_gain_config_t( 42, 4.9, 17, 9, false, false ) )
+ ( twinrx_gain_config_t( 43, 5.9, 16, 9, false, false ) )
+ ( twinrx_gain_config_t( 44, 6.9, 15, 9, false, false ) )
+ ( twinrx_gain_config_t( 45, 7.9, 14, 9, false, false ) )
+ ( twinrx_gain_config_t( 46, 8.9, 13, 9, false, false ) )
+ ( twinrx_gain_config_t( 47, 9.9, 12, 9, false, false ) )
+ ( twinrx_gain_config_t( 48, 10.9, 11, 9, false, false ) )
+ ( twinrx_gain_config_t( 49, 11.9, 10, 9, false, false ) )
+ ( twinrx_gain_config_t( 50, 12.9, 9, 9, false, false ) )
+ ( twinrx_gain_config_t( 51, 13.9, 8, 9, false, false ) )
+ ( twinrx_gain_config_t( 52, 14.9, 7, 9, false, false ) )
+ ( twinrx_gain_config_t( 53, 15.9, 6, 9, false, false ) )
+ ( twinrx_gain_config_t( 54, 16.9, 5, 9, false, false ) )
+ ( twinrx_gain_config_t( 55, 17.9, 4, 9, false, false ) )
+ ( twinrx_gain_config_t( 56, 18.9, 3, 9, false, false ) )
+ ( twinrx_gain_config_t( 57, 19.9, 2, 9, false, false ) )
+ ( twinrx_gain_config_t( 58, 20.9, 1, 9, false, false ) )
+ ( twinrx_gain_config_t( 59, 21.9, 0, 9, false, false ) )
+ ( twinrx_gain_config_t( 60, 22.9, 0, 8, false, false ) )
+ ( twinrx_gain_config_t( 61, 23.9, 0, 7, false, false ) )
+ ( twinrx_gain_config_t( 62, 24.9, 0, 6, false, false ) )
+ ( twinrx_gain_config_t( 63, 25.9, 0, 5, false, false ) )
+ ( twinrx_gain_config_t( 64, 26.9, 0, 4, false, false ) )
+ ( twinrx_gain_config_t( 65, 27.9, 0, 3, false, false ) )
+ ( twinrx_gain_config_t( 66, 28.9, 0, 2, false, false ) )
+ ( twinrx_gain_config_t( 67, 29.9, 0, 1, false, false ) )
+ ( twinrx_gain_config_t( 68, 31.3, 0, 9, false, true ) )
+ ( twinrx_gain_config_t( 69, 32.3, 0, 8, false, true ) )
+ ( twinrx_gain_config_t( 70, 33.3, 0, 7, false, true ) )
+ ( twinrx_gain_config_t( 71, 34.3, 0, 6, false, true ) )
+ ( twinrx_gain_config_t( 72, 35.3, 0, 5, false, true ) )
+ ( twinrx_gain_config_t( 73, 36.3, 0, 4, false, true ) )
+ ( twinrx_gain_config_t( 74, 37.3, 0, 3, false, true ) )
+ ( twinrx_gain_config_t( 75, 37.6, 0, 9, true, false ) )
+ ( twinrx_gain_config_t( 76, 38.6, 0, 8, true, false ) )
+ ( twinrx_gain_config_t( 77, 39.6, 0, 7, true, false ) )
+ ( twinrx_gain_config_t( 78, 40.6, 0, 6, true, false ) )
+ ( twinrx_gain_config_t( 79, 41.6, 0, 5, true, false ) )
+ ( twinrx_gain_config_t( 80, 42.6, 0, 4, true, false ) )
+ ( twinrx_gain_config_t( 81, 43.6, 0, 3, true, false ) )
+ ( twinrx_gain_config_t( 82, 44.6, 0, 2, true, false ) )
+ ( twinrx_gain_config_t( 83, 45.6, 0, 1, true, false ) )
+ ( twinrx_gain_config_t( 84, 47.0, 0, 9, true, true ) )
+ ( twinrx_gain_config_t( 85, 48.0, 0, 8, true, true ) )
+ ( twinrx_gain_config_t( 86, 49.0, 0, 7, true, true ) )
+ ( twinrx_gain_config_t( 87, 50.0, 0, 6, true, true ) )
+ ( twinrx_gain_config_t( 88, 51.0, 0, 5, true, true ) )
+ ( twinrx_gain_config_t( 89, 52.0, 0, 4, true, true ) )
+ ( twinrx_gain_config_t( 90, 53.0, 0, 3, true, true ) )
+ ( twinrx_gain_config_t( 91, 54.0, 0, 2, true, true ) )
+ ( twinrx_gain_config_t( 92, 55.0, 0, 1, true, true ) )
+ ( twinrx_gain_config_t( 93, 56.0, 0, 0, true, true ) )
+;
+
+static const std::vector<twinrx_gain_config_t> HIGHBAND4_TABLE = boost::assign::list_of
+ // Index, Gain, Atten1, Atten2, Amp1, Amp2
+ ( twinrx_gain_config_t( 0, -37.2, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 1, -37.2, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 2, -37.2, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 3, -37.2, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 4, -37.2, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 5, -37.2, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 6, -37.2, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 7, -37.2, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 8, -37.2, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 9, -36.2, 31, 30, false, false ) )
+ ( twinrx_gain_config_t( 10, -35.2, 31, 29, false, false ) )
+ ( twinrx_gain_config_t( 11, -34.2, 31, 28, false, false ) )
+ ( twinrx_gain_config_t( 12, -33.2, 31, 27, false, false ) )
+ ( twinrx_gain_config_t( 13, -32.2, 31, 26, false, false ) )
+ ( twinrx_gain_config_t( 14, -31.2, 31, 25, false, false ) )
+ ( twinrx_gain_config_t( 15, -30.2, 31, 24, false, false ) )
+ ( twinrx_gain_config_t( 16, -29.2, 31, 23, false, false ) )
+ ( twinrx_gain_config_t( 17, -28.2, 31, 22, false, false ) )
+ ( twinrx_gain_config_t( 18, -27.2, 31, 21, false, false ) )
+ ( twinrx_gain_config_t( 19, -26.2, 31, 20, false, false ) )
+ ( twinrx_gain_config_t( 20, -25.2, 31, 19, false, false ) )
+ ( twinrx_gain_config_t( 21, -24.2, 31, 18, false, false ) )
+ ( twinrx_gain_config_t( 22, -23.2, 31, 17, false, false ) )
+ ( twinrx_gain_config_t( 23, -22.2, 31, 16, false, false ) )
+ ( twinrx_gain_config_t( 24, -21.2, 31, 15, false, false ) )
+ ( twinrx_gain_config_t( 25, -20.2, 31, 14, false, false ) )
+ ( twinrx_gain_config_t( 26, -19.2, 31, 13, false, false ) )
+ ( twinrx_gain_config_t( 27, -18.2, 31, 12, false, false ) )
+ ( twinrx_gain_config_t( 28, -17.2, 31, 11, false, false ) )
+ ( twinrx_gain_config_t( 29, -16.2, 31, 10, false, false ) )
+ ( twinrx_gain_config_t( 30, -15.2, 30, 10, false, false ) )
+ ( twinrx_gain_config_t( 31, -14.2, 30, 9, false, false ) )
+ ( twinrx_gain_config_t( 32, -13.2, 29, 9, false, false ) )
+ ( twinrx_gain_config_t( 33, -12.2, 28, 9, false, false ) )
+ ( twinrx_gain_config_t( 34, -11.2, 28, 8, false, false ) )
+ ( twinrx_gain_config_t( 35, -10.2, 27, 8, false, false ) )
+ ( twinrx_gain_config_t( 36, -9.2, 27, 7, false, false ) )
+ ( twinrx_gain_config_t( 37, -8.2, 26, 7, false, false ) )
+ ( twinrx_gain_config_t( 38, -7.2, 25, 7, false, false ) )
+ ( twinrx_gain_config_t( 39, -6.2, 24, 7, false, false ) )
+ ( twinrx_gain_config_t( 40, -5.2, 24, 6, false, false ) )
+ ( twinrx_gain_config_t( 41, -4.2, 23, 6, false, false ) )
+ ( twinrx_gain_config_t( 42, -3.2, 22, 6, false, false ) )
+ ( twinrx_gain_config_t( 43, -2.2, 21, 6, false, false ) )
+ ( twinrx_gain_config_t( 44, -1.2, 20, 6, false, false ) )
+ ( twinrx_gain_config_t( 45, -0.2, 19, 6, false, false ) )
+ ( twinrx_gain_config_t( 46, 0.8, 18, 6, false, false ) )
+ ( twinrx_gain_config_t( 47, 1.8, 17, 6, false, false ) )
+ ( twinrx_gain_config_t( 48, 2.8, 16, 6, false, false ) )
+ ( twinrx_gain_config_t( 49, 3.8, 16, 5, false, false ) )
+ ( twinrx_gain_config_t( 50, 4.8, 15, 5, false, false ) )
+ ( twinrx_gain_config_t( 51, 5.8, 14, 5, false, false ) )
+ ( twinrx_gain_config_t( 52, 6.8, 13, 5, false, false ) )
+ ( twinrx_gain_config_t( 53, 7.8, 12, 5, false, false ) )
+ ( twinrx_gain_config_t( 54, 8.8, 11, 5, false, false ) )
+ ( twinrx_gain_config_t( 55, 9.8, 10, 5, false, false ) )
+ ( twinrx_gain_config_t( 56, 10.8, 9, 5, false, false ) )
+ ( twinrx_gain_config_t( 57, 11.8, 8, 5, false, false ) )
+ ( twinrx_gain_config_t( 58, 12.8, 7, 5, false, false ) )
+ ( twinrx_gain_config_t( 59, 13.8, 6, 5, false, false ) )
+ ( twinrx_gain_config_t( 60, 14.8, 5, 5, false, false ) )
+ ( twinrx_gain_config_t( 61, 15.8, 4, 5, false, false ) )
+ ( twinrx_gain_config_t( 62, 16.8, 3, 5, false, false ) )
+ ( twinrx_gain_config_t( 63, 17.8, 2, 5, false, false ) )
+ ( twinrx_gain_config_t( 64, 18.8, 1, 5, false, false ) )
+ ( twinrx_gain_config_t( 65, 19.8, 0, 5, false, false ) )
+ ( twinrx_gain_config_t( 66, 20.8, 0, 4, false, false ) )
+ ( twinrx_gain_config_t( 67, 21.8, 0, 3, false, false ) )
+ ( twinrx_gain_config_t( 68, 22.8, 0, 2, false, false ) )
+ ( twinrx_gain_config_t( 69, 23.8, 0, 1, false, false ) )
+ ( twinrx_gain_config_t( 70, 24.8, 0, 0, false, false ) )
+ ( twinrx_gain_config_t( 71, 26.1, 0, 6, false, true ) )
+ ( twinrx_gain_config_t( 72, 26.1, 0, 6, false, true ) )
+ ( twinrx_gain_config_t( 73, 27.1, 0, 5, false, true ) )
+ ( twinrx_gain_config_t( 74, 28.1, 0, 4, false, true ) )
+ ( twinrx_gain_config_t( 75, 29.1, 0, 3, false, true ) )
+ ( twinrx_gain_config_t( 76, 30.1, 0, 2, false, true ) )
+ ( twinrx_gain_config_t( 77, 31.1, 0, 1, false, true ) )
+ ( twinrx_gain_config_t( 78, 32.1, 0, 0, false, true ) )
+ ( twinrx_gain_config_t( 79, 33.3, 0, 7, true, false ) )
+ ( twinrx_gain_config_t( 80, 34.3, 0, 6, true, false ) )
+ ( twinrx_gain_config_t( 81, 35.3, 0, 5, true, false ) )
+ ( twinrx_gain_config_t( 82, 36.3, 0, 4, true, false ) )
+ ( twinrx_gain_config_t( 83, 37.3, 0, 3, true, false ) )
+ ( twinrx_gain_config_t( 84, 38.3, 0, 2, true, false ) )
+ ( twinrx_gain_config_t( 85, 39.3, 0, 1, true, false ) )
+ ( twinrx_gain_config_t( 86, 40.3, 0, 0, true, false ) )
+ ( twinrx_gain_config_t( 87, 41.6, 0, 6, true, true ) )
+ ( twinrx_gain_config_t( 88, 42.6, 0, 5, true, true ) )
+ ( twinrx_gain_config_t( 89, 43.6, 0, 4, true, true ) )
+ ( twinrx_gain_config_t( 90, 44.6, 0, 3, true, true ) )
+ ( twinrx_gain_config_t( 91, 45.6, 0, 2, true, true ) )
+ ( twinrx_gain_config_t( 92, 46.6, 0, 1, true, true ) )
+ ( twinrx_gain_config_t( 93, 47.6, 0, 0, true, true ) )
+;
+
+static const std::vector<twinrx_gain_config_t> LOWBAND1_TABLE = boost::assign::list_of
+ // Index, Gain, Atten1, Atten2, Amp1, Amp2
+ ( twinrx_gain_config_t( 0, -31.1, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 1, -30.1, 31, 30, false, false ) )
+ ( twinrx_gain_config_t( 2, -29.1, 31, 29, false, false ) )
+ ( twinrx_gain_config_t( 3, -28.1, 31, 28, false, false ) )
+ ( twinrx_gain_config_t( 4, -27.1, 31, 27, false, false ) )
+ ( twinrx_gain_config_t( 5, -26.1, 31, 26, false, false ) )
+ ( twinrx_gain_config_t( 6, -25.1, 31, 25, false, false ) )
+ ( twinrx_gain_config_t( 7, -24.1, 31, 24, false, false ) )
+ ( twinrx_gain_config_t( 8, -23.1, 31, 23, false, false ) )
+ ( twinrx_gain_config_t( 9, -22.1, 31, 22, false, false ) )
+ ( twinrx_gain_config_t( 10, -21.1, 31, 21, false, false ) )
+ ( twinrx_gain_config_t( 11, -20.1, 31, 20, false, false ) )
+ ( twinrx_gain_config_t( 12, -19.1, 31, 19, false, false ) )
+ ( twinrx_gain_config_t( 13, -18.1, 31, 18, false, false ) )
+ ( twinrx_gain_config_t( 14, -17.1, 31, 17, false, false ) )
+ ( twinrx_gain_config_t( 15, -16.1, 31, 16, false, false ) )
+ ( twinrx_gain_config_t( 16, -15.1, 31, 15, false, false ) )
+ ( twinrx_gain_config_t( 17, -14.1, 31, 14, false, false ) )
+ ( twinrx_gain_config_t( 18, -13.1, 31, 13, false, false ) )
+ ( twinrx_gain_config_t( 19, -12.1, 31, 12, false, false ) )
+ ( twinrx_gain_config_t( 20, -11.1, 31, 11, false, false ) )
+ ( twinrx_gain_config_t( 21, -10.1, 31, 10, false, false ) )
+ ( twinrx_gain_config_t( 22, -9.1, 31, 9, false, false ) )
+ ( twinrx_gain_config_t( 23, -8.1, 31, 8, false, false ) )
+ ( twinrx_gain_config_t( 24, -7.1, 31, 7, false, false ) )
+ ( twinrx_gain_config_t( 25, -6.1, 31, 6, false, false ) )
+ ( twinrx_gain_config_t( 26, -5.1, 31, 5, false, false ) )
+ ( twinrx_gain_config_t( 27, -4.1, 31, 4, false, false ) )
+ ( twinrx_gain_config_t( 28, -3.1, 31, 3, false, false ) )
+ ( twinrx_gain_config_t( 29, -2.1, 31, 2, false, false ) )
+ ( twinrx_gain_config_t( 30, -1.1, 31, 1, false, false ) )
+ ( twinrx_gain_config_t( 31, -0.1, 31, 0, false, false ) )
+ ( twinrx_gain_config_t( 32, 0.9, 30, 0, false, false ) )
+ ( twinrx_gain_config_t( 33, 1.9, 29, 0, false, false ) )
+ ( twinrx_gain_config_t( 34, 2.9, 28, 0, false, false ) )
+ ( twinrx_gain_config_t( 35, 3.9, 27, 0, false, false ) )
+ ( twinrx_gain_config_t( 36, 4.9, 26, 0, false, false ) )
+ ( twinrx_gain_config_t( 37, 5.9, 25, 0, false, false ) )
+ ( twinrx_gain_config_t( 38, 6.9, 24, 0, false, false ) )
+ ( twinrx_gain_config_t( 39, 7.9, 23, 0, false, false ) )
+ ( twinrx_gain_config_t( 40, 8.9, 22, 0, false, false ) )
+ ( twinrx_gain_config_t( 41, 9.9, 21, 0, false, false ) )
+ ( twinrx_gain_config_t( 42, 10.9, 20, 0, false, false ) )
+ ( twinrx_gain_config_t( 43, 11.9, 19, 0, false, false ) )
+ ( twinrx_gain_config_t( 44, 12.9, 18, 0, false, false ) )
+ ( twinrx_gain_config_t( 45, 13.9, 17, 0, false, false ) )
+ ( twinrx_gain_config_t( 46, 14.9, 16, 0, false, false ) )
+ ( twinrx_gain_config_t( 47, 15.9, 15, 0, false, false ) )
+ ( twinrx_gain_config_t( 48, 16.9, 14, 0, false, false ) )
+ ( twinrx_gain_config_t( 49, 17.9, 13, 0, false, false ) )
+ ( twinrx_gain_config_t( 50, 18.9, 12, 0, false, false ) )
+ ( twinrx_gain_config_t( 51, 19.9, 11, 0, false, false ) )
+ ( twinrx_gain_config_t( 52, 20.9, 10, 0, false, false ) )
+ ( twinrx_gain_config_t( 53, 21.9, 9, 0, false, false ) )
+ ( twinrx_gain_config_t( 54, 22.9, 8, 0, false, false ) )
+ ( twinrx_gain_config_t( 55, 23.9, 7, 0, false, false ) )
+ ( twinrx_gain_config_t( 56, 24.9, 6, 0, false, false ) )
+ ( twinrx_gain_config_t( 57, 25.9, 5, 0, false, false ) )
+ ( twinrx_gain_config_t( 58, 26.9, 4, 0, false, false ) )
+ ( twinrx_gain_config_t( 59, 27.9, 3, 0, false, false ) )
+ ( twinrx_gain_config_t( 60, 28.9, 2, 0, false, false ) )
+ ( twinrx_gain_config_t( 61, 29.9, 1, 0, false, false ) )
+ ( twinrx_gain_config_t( 62, 30.9, 0, 0, false, false ) )
+ ( twinrx_gain_config_t( 63, 31.2, 4, 11, false, true ) )
+ ( twinrx_gain_config_t( 64, 32.2, 3, 11, false, true ) )
+ ( twinrx_gain_config_t( 65, 33.2, 2, 11, false, true ) )
+ ( twinrx_gain_config_t( 66, 34.2, 1, 11, false, true ) )
+ ( twinrx_gain_config_t( 67, 35.2, 0, 11, false, true ) )
+ ( twinrx_gain_config_t( 68, 36.2, 10, 0, true, false ) )
+ ( twinrx_gain_config_t( 69, 37.2, 9, 0, true, false ) )
+ ( twinrx_gain_config_t( 70, 38.2, 8, 0, true, false ) )
+ ( twinrx_gain_config_t( 71, 39.2, 7, 0, true, false ) )
+ ( twinrx_gain_config_t( 72, 40.2, 6, 0, true, false ) )
+ ( twinrx_gain_config_t( 73, 41.2, 5, 0, true, false ) )
+ ( twinrx_gain_config_t( 74, 42.2, 4, 0, true, false ) )
+ ( twinrx_gain_config_t( 75, 43.2, 3, 0, true, false ) )
+ ( twinrx_gain_config_t( 76, 44.2, 2, 0, true, false ) )
+ ( twinrx_gain_config_t( 77, 45.2, 1, 0, true, false ) )
+ ( twinrx_gain_config_t( 78, 46.2, 0, 0, true, false ) )
+ ( twinrx_gain_config_t( 79, 47.5, 4, 10, true, true ) )
+ ( twinrx_gain_config_t( 80, 48.5, 3, 10, true, true ) )
+ ( twinrx_gain_config_t( 81, 49.5, 3, 9, true, true ) )
+ ( twinrx_gain_config_t( 82, 50.5, 2, 9, true, true ) )
+ ( twinrx_gain_config_t( 83, 51.5, 1, 9, true, true ) )
+ ( twinrx_gain_config_t( 84, 52.5, 1, 8, true, true ) )
+ ( twinrx_gain_config_t( 85, 53.5, 0, 8, true, true ) )
+ ( twinrx_gain_config_t( 86, 54.5, 0, 7, true, true ) )
+ ( twinrx_gain_config_t( 87, 55.5, 0, 6, true, true ) )
+ ( twinrx_gain_config_t( 88, 56.5, 0, 5, true, true ) )
+ ( twinrx_gain_config_t( 89, 57.5, 0, 4, true, true ) )
+ ( twinrx_gain_config_t( 90, 58.5, 0, 3, true, true ) )
+ ( twinrx_gain_config_t( 91, 59.5, 0, 2, true, true ) )
+ ( twinrx_gain_config_t( 92, 60.5, 0, 1, true, true ) )
+ ( twinrx_gain_config_t( 93, 61.5, 0, 0, true, true ) )
+;
+
+static const std::vector<twinrx_gain_config_t> LOWBAND2_TABLE = boost::assign::list_of
+ // Index, Gain, Atten1, Atten2, Amp1, Amp2
+ ( twinrx_gain_config_t( 0, -33.4, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 1, -33.4, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 2, -32.4, 31, 30, false, false ) )
+ ( twinrx_gain_config_t( 3, -31.4, 31, 29, false, false ) )
+ ( twinrx_gain_config_t( 4, -30.4, 31, 28, false, false ) )
+ ( twinrx_gain_config_t( 5, -29.4, 31, 27, false, false ) )
+ ( twinrx_gain_config_t( 6, -28.4, 31, 26, false, false ) )
+ ( twinrx_gain_config_t( 7, -27.4, 31, 25, false, false ) )
+ ( twinrx_gain_config_t( 8, -26.4, 31, 24, false, false ) )
+ ( twinrx_gain_config_t( 9, -25.4, 31, 23, false, false ) )
+ ( twinrx_gain_config_t( 10, -24.4, 31, 22, false, false ) )
+ ( twinrx_gain_config_t( 11, -23.4, 31, 21, false, false ) )
+ ( twinrx_gain_config_t( 12, -22.4, 31, 20, false, false ) )
+ ( twinrx_gain_config_t( 13, -21.4, 31, 19, false, false ) )
+ ( twinrx_gain_config_t( 14, -20.4, 31, 18, false, false ) )
+ ( twinrx_gain_config_t( 15, -19.4, 31, 17, false, false ) )
+ ( twinrx_gain_config_t( 16, -18.4, 31, 16, false, false ) )
+ ( twinrx_gain_config_t( 17, -17.4, 31, 15, false, false ) )
+ ( twinrx_gain_config_t( 18, -16.4, 31, 14, false, false ) )
+ ( twinrx_gain_config_t( 19, -15.4, 31, 13, false, false ) )
+ ( twinrx_gain_config_t( 20, -14.4, 31, 12, false, false ) )
+ ( twinrx_gain_config_t( 21, -13.4, 31, 11, false, false ) )
+ ( twinrx_gain_config_t( 22, -12.4, 31, 10, false, false ) )
+ ( twinrx_gain_config_t( 23, -11.4, 31, 9, false, false ) )
+ ( twinrx_gain_config_t( 24, -10.4, 31, 8, false, false ) )
+ ( twinrx_gain_config_t( 25, -9.4, 31, 7, false, false ) )
+ ( twinrx_gain_config_t( 26, -8.4, 31, 6, false, false ) )
+ ( twinrx_gain_config_t( 27, -7.4, 31, 5, false, false ) )
+ ( twinrx_gain_config_t( 28, -6.4, 31, 4, false, false ) )
+ ( twinrx_gain_config_t( 29, -5.4, 31, 3, false, false ) )
+ ( twinrx_gain_config_t( 30, -4.4, 31, 2, false, false ) )
+ ( twinrx_gain_config_t( 31, -3.4, 31, 1, false, false ) )
+ ( twinrx_gain_config_t( 32, -2.4, 31, 0, false, false ) )
+ ( twinrx_gain_config_t( 33, -1.4, 30, 0, false, false ) )
+ ( twinrx_gain_config_t( 34, -0.4, 29, 0, false, false ) )
+ ( twinrx_gain_config_t( 35, 0.6, 28, 0, false, false ) )
+ ( twinrx_gain_config_t( 36, 1.6, 27, 0, false, false ) )
+ ( twinrx_gain_config_t( 37, 2.6, 26, 0, false, false ) )
+ ( twinrx_gain_config_t( 38, 3.6, 25, 0, false, false ) )
+ ( twinrx_gain_config_t( 39, 4.6, 24, 0, false, false ) )
+ ( twinrx_gain_config_t( 40, 5.6, 23, 0, false, false ) )
+ ( twinrx_gain_config_t( 41, 6.6, 22, 0, false, false ) )
+ ( twinrx_gain_config_t( 42, 7.6, 21, 0, false, false ) )
+ ( twinrx_gain_config_t( 43, 8.6, 20, 0, false, false ) )
+ ( twinrx_gain_config_t( 44, 9.6, 19, 0, false, false ) )
+ ( twinrx_gain_config_t( 45, 10.6, 18, 0, false, false ) )
+ ( twinrx_gain_config_t( 46, 11.6, 17, 0, false, false ) )
+ ( twinrx_gain_config_t( 47, 12.6, 16, 0, false, false ) )
+ ( twinrx_gain_config_t( 48, 13.6, 15, 0, false, false ) )
+ ( twinrx_gain_config_t( 49, 14.6, 14, 0, false, false ) )
+ ( twinrx_gain_config_t( 50, 15.6, 13, 0, false, false ) )
+ ( twinrx_gain_config_t( 51, 16.6, 12, 0, false, false ) )
+ ( twinrx_gain_config_t( 52, 17.6, 11, 0, false, false ) )
+ ( twinrx_gain_config_t( 53, 18.6, 10, 0, false, false ) )
+ ( twinrx_gain_config_t( 54, 19.6, 9, 0, false, false ) )
+ ( twinrx_gain_config_t( 55, 20.6, 8, 0, false, false ) )
+ ( twinrx_gain_config_t( 56, 21.6, 7, 0, false, false ) )
+ ( twinrx_gain_config_t( 57, 22.6, 6, 0, false, false ) )
+ ( twinrx_gain_config_t( 58, 23.6, 5, 0, false, false ) )
+ ( twinrx_gain_config_t( 59, 24.6, 4, 0, false, false ) )
+ ( twinrx_gain_config_t( 60, 25.6, 3, 0, false, false ) )
+ ( twinrx_gain_config_t( 61, 26.6, 2, 0, false, false ) )
+ ( twinrx_gain_config_t( 62, 27.6, 1, 0, false, false ) )
+ ( twinrx_gain_config_t( 63, 28.6, 0, 0, false, false ) )
+ ( twinrx_gain_config_t( 64, 29.7, 5, 9, false, true ) )
+ ( twinrx_gain_config_t( 65, 30.7, 4, 9, false, true ) )
+ ( twinrx_gain_config_t( 66, 31.7, 3, 9, false, true ) )
+ ( twinrx_gain_config_t( 67, 32.7, 2, 9, false, true ) )
+ ( twinrx_gain_config_t( 68, 33.7, 1, 9, false, true ) )
+ ( twinrx_gain_config_t( 69, 34.7, 0, 9, false, true ) )
+ ( twinrx_gain_config_t( 70, 35.7, 0, 8, false, true ) )
+ ( twinrx_gain_config_t( 71, 36.7, 7, 0, true, false ) )
+ ( twinrx_gain_config_t( 72, 37.7, 6, 0, true, false ) )
+ ( twinrx_gain_config_t( 73, 38.7, 5, 0, true, false ) )
+ ( twinrx_gain_config_t( 74, 39.7, 4, 0, true, false ) )
+ ( twinrx_gain_config_t( 75, 40.7, 3, 0, true, false ) )
+ ( twinrx_gain_config_t( 76, 41.7, 2, 0, true, false ) )
+ ( twinrx_gain_config_t( 77, 42.7, 1, 0, true, false ) )
+ ( twinrx_gain_config_t( 78, 43.7, 0, 0, true, false ) )
+ ( twinrx_gain_config_t( 79, 44.8, 6, 8, true, true ) )
+ ( twinrx_gain_config_t( 80, 45.8, 5, 8, true, true ) )
+ ( twinrx_gain_config_t( 81, 46.8, 4, 8, true, true ) )
+ ( twinrx_gain_config_t( 82, 47.8, 4, 7, true, true ) )
+ ( twinrx_gain_config_t( 83, 48.8, 3, 7, true, true ) )
+ ( twinrx_gain_config_t( 84, 49.8, 2, 7, true, true ) )
+ ( twinrx_gain_config_t( 85, 50.8, 1, 7, true, true ) )
+ ( twinrx_gain_config_t( 86, 51.8, 1, 6, true, true ) )
+ ( twinrx_gain_config_t( 87, 52.8, 0, 6, true, true ) )
+ ( twinrx_gain_config_t( 88, 53.8, 0, 5, true, true ) )
+ ( twinrx_gain_config_t( 89, 54.8, 0, 4, true, true ) )
+ ( twinrx_gain_config_t( 90, 55.8, 0, 3, true, true ) )
+ ( twinrx_gain_config_t( 91, 56.8, 0, 2, true, true ) )
+ ( twinrx_gain_config_t( 92, 57.8, 0, 1, true, true ) )
+ ( twinrx_gain_config_t( 93, 58.8, 0, 0, true, true ) )
+;
+
+static const std::vector<twinrx_gain_config_t> LOWBAND3_TABLE = boost::assign::list_of
+ // Index, Gain, Atten1, Atten2, Amp1, Amp2
+ ( twinrx_gain_config_t( 0, -34.0, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 1, -34.0, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 2, -34.0, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 3, -33.0, 31, 30, false, false ) )
+ ( twinrx_gain_config_t( 4, -32.0, 31, 29, false, false ) )
+ ( twinrx_gain_config_t( 5, -31.0, 31, 28, false, false ) )
+ ( twinrx_gain_config_t( 6, -30.0, 31, 27, false, false ) )
+ ( twinrx_gain_config_t( 7, -29.0, 31, 26, false, false ) )
+ ( twinrx_gain_config_t( 8, -28.0, 31, 25, false, false ) )
+ ( twinrx_gain_config_t( 9, -27.0, 31, 24, false, false ) )
+ ( twinrx_gain_config_t( 10, -26.0, 31, 23, false, false ) )
+ ( twinrx_gain_config_t( 11, -25.0, 31, 22, false, false ) )
+ ( twinrx_gain_config_t( 12, -24.0, 31, 21, false, false ) )
+ ( twinrx_gain_config_t( 13, -23.0, 31, 20, false, false ) )
+ ( twinrx_gain_config_t( 14, -22.0, 31, 19, false, false ) )
+ ( twinrx_gain_config_t( 15, -21.0, 31, 18, false, false ) )
+ ( twinrx_gain_config_t( 16, -20.0, 31, 17, false, false ) )
+ ( twinrx_gain_config_t( 17, -19.0, 31, 16, false, false ) )
+ ( twinrx_gain_config_t( 18, -18.0, 31, 15, false, false ) )
+ ( twinrx_gain_config_t( 19, -17.0, 31, 14, false, false ) )
+ ( twinrx_gain_config_t( 20, -16.0, 31, 13, false, false ) )
+ ( twinrx_gain_config_t( 21, -15.0, 31, 12, false, false ) )
+ ( twinrx_gain_config_t( 22, -14.0, 31, 11, false, false ) )
+ ( twinrx_gain_config_t( 23, -13.0, 31, 10, false, false ) )
+ ( twinrx_gain_config_t( 24, -12.0, 31, 9, false, false ) )
+ ( twinrx_gain_config_t( 25, -11.0, 31, 8, false, false ) )
+ ( twinrx_gain_config_t( 26, -10.0, 31, 7, false, false ) )
+ ( twinrx_gain_config_t( 27, -9.0, 31, 6, false, false ) )
+ ( twinrx_gain_config_t( 28, -8.0, 31, 5, false, false ) )
+ ( twinrx_gain_config_t( 29, -7.0, 31, 4, false, false ) )
+ ( twinrx_gain_config_t( 30, -6.0, 31, 3, false, false ) )
+ ( twinrx_gain_config_t( 31, -5.0, 31, 2, false, false ) )
+ ( twinrx_gain_config_t( 32, -4.0, 31, 1, false, false ) )
+ ( twinrx_gain_config_t( 33, -3.0, 31, 0, false, false ) )
+ ( twinrx_gain_config_t( 34, -2.0, 30, 0, false, false ) )
+ ( twinrx_gain_config_t( 35, -1.0, 29, 0, false, false ) )
+ ( twinrx_gain_config_t( 36, -0.0, 28, 0, false, false ) )
+ ( twinrx_gain_config_t( 37, 1.0, 27, 0, false, false ) )
+ ( twinrx_gain_config_t( 38, 2.0, 26, 0, false, false ) )
+ ( twinrx_gain_config_t( 39, 3.0, 25, 0, false, false ) )
+ ( twinrx_gain_config_t( 40, 4.0, 24, 0, false, false ) )
+ ( twinrx_gain_config_t( 41, 5.0, 23, 0, false, false ) )
+ ( twinrx_gain_config_t( 42, 6.0, 22, 0, false, false ) )
+ ( twinrx_gain_config_t( 43, 7.0, 21, 0, false, false ) )
+ ( twinrx_gain_config_t( 44, 8.0, 20, 0, false, false ) )
+ ( twinrx_gain_config_t( 45, 9.0, 19, 0, false, false ) )
+ ( twinrx_gain_config_t( 46, 10.0, 18, 0, false, false ) )
+ ( twinrx_gain_config_t( 47, 11.0, 17, 0, false, false ) )
+ ( twinrx_gain_config_t( 48, 12.0, 16, 0, false, false ) )
+ ( twinrx_gain_config_t( 49, 13.0, 15, 0, false, false ) )
+ ( twinrx_gain_config_t( 50, 14.0, 14, 0, false, false ) )
+ ( twinrx_gain_config_t( 51, 15.0, 13, 0, false, false ) )
+ ( twinrx_gain_config_t( 52, 16.0, 12, 0, false, false ) )
+ ( twinrx_gain_config_t( 53, 17.0, 11, 0, false, false ) )
+ ( twinrx_gain_config_t( 54, 18.0, 10, 0, false, false ) )
+ ( twinrx_gain_config_t( 55, 19.0, 9, 0, false, false ) )
+ ( twinrx_gain_config_t( 56, 20.0, 8, 0, false, false ) )
+ ( twinrx_gain_config_t( 57, 21.0, 7, 0, false, false ) )
+ ( twinrx_gain_config_t( 58, 22.0, 6, 0, false, false ) )
+ ( twinrx_gain_config_t( 59, 23.0, 5, 0, false, false ) )
+ ( twinrx_gain_config_t( 60, 24.0, 4, 0, false, false ) )
+ ( twinrx_gain_config_t( 61, 25.0, 3, 0, false, false ) )
+ ( twinrx_gain_config_t( 62, 26.0, 2, 0, false, false ) )
+ ( twinrx_gain_config_t( 63, 27.0, 1, 0, false, false ) )
+ ( twinrx_gain_config_t( 64, 28.0, 0, 0, false, false ) )
+ ( twinrx_gain_config_t( 65, 29.5, 5, 8, false, true ) )
+ ( twinrx_gain_config_t( 66, 30.5, 4, 8, false, true ) )
+ ( twinrx_gain_config_t( 67, 31.5, 3, 8, false, true ) )
+ ( twinrx_gain_config_t( 68, 32.5, 2, 8, false, true ) )
+ ( twinrx_gain_config_t( 69, 33.5, 1, 8, false, true ) )
+ ( twinrx_gain_config_t( 70, 34.5, 0, 8, false, true ) )
+ ( twinrx_gain_config_t( 71, 34.5, 0, 8, false, true ) )
+ ( twinrx_gain_config_t( 72, 36.5, 6, 0, true, false ) )
+ ( twinrx_gain_config_t( 73, 36.5, 6, 0, true, false ) )
+ ( twinrx_gain_config_t( 74, 37.5, 5, 0, true, false ) )
+ ( twinrx_gain_config_t( 75, 38.5, 4, 0, true, false ) )
+ ( twinrx_gain_config_t( 76, 39.5, 3, 0, true, false ) )
+ ( twinrx_gain_config_t( 77, 40.5, 2, 0, true, false ) )
+ ( twinrx_gain_config_t( 78, 41.5, 1, 0, true, false ) )
+ ( twinrx_gain_config_t( 79, 42.5, 0, 0, true, false ) )
+ ( twinrx_gain_config_t( 80, 44.0, 6, 7, true, true ) )
+ ( twinrx_gain_config_t( 81, 45.0, 5, 7, true, true ) )
+ ( twinrx_gain_config_t( 82, 46.0, 4, 7, true, true ) )
+ ( twinrx_gain_config_t( 83, 47.0, 3, 7, true, true ) )
+ ( twinrx_gain_config_t( 84, 48.0, 3, 6, true, true ) )
+ ( twinrx_gain_config_t( 85, 49.0, 2, 6, true, true ) )
+ ( twinrx_gain_config_t( 86, 50.0, 1, 6, true, true ) )
+ ( twinrx_gain_config_t( 87, 51.0, 0, 6, true, true ) )
+ ( twinrx_gain_config_t( 88, 52.0, 0, 5, true, true ) )
+ ( twinrx_gain_config_t( 89, 53.0, 0, 4, true, true ) )
+ ( twinrx_gain_config_t( 90, 54.0, 0, 3, true, true ) )
+ ( twinrx_gain_config_t( 91, 55.0, 0, 2, true, true ) )
+ ( twinrx_gain_config_t( 92, 56.0, 0, 1, true, true ) )
+ ( twinrx_gain_config_t( 93, 57.0, 0, 0, true, true ) )
+;
+
+static const std::vector<twinrx_gain_config_t> LOWBAND4_TABLE = boost::assign::list_of
+ // Index, Gain, Atten1, Atten2, Amp1, Amp2
+ ( twinrx_gain_config_t( 0, -32.8, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 1, -32.8, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 2, -32.8, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 3, -32.8, 31, 31, false, false ) )
+ ( twinrx_gain_config_t( 4, -31.8, 31, 30, false, false ) )
+ ( twinrx_gain_config_t( 5, -30.8, 31, 29, false, false ) )
+ ( twinrx_gain_config_t( 6, -29.8, 31, 28, false, false ) )
+ ( twinrx_gain_config_t( 7, -28.8, 31, 27, false, false ) )
+ ( twinrx_gain_config_t( 8, -27.8, 31, 26, false, false ) )
+ ( twinrx_gain_config_t( 9, -26.8, 31, 25, false, false ) )
+ ( twinrx_gain_config_t( 10, -25.8, 31, 24, false, false ) )
+ ( twinrx_gain_config_t( 11, -24.8, 31, 23, false, false ) )
+ ( twinrx_gain_config_t( 12, -23.8, 31, 22, false, false ) )
+ ( twinrx_gain_config_t( 13, -22.8, 31, 21, false, false ) )
+ ( twinrx_gain_config_t( 14, -21.8, 31, 20, false, false ) )
+ ( twinrx_gain_config_t( 15, -20.8, 31, 19, false, false ) )
+ ( twinrx_gain_config_t( 16, -19.8, 31, 18, false, false ) )
+ ( twinrx_gain_config_t( 17, -18.8, 31, 17, false, false ) )
+ ( twinrx_gain_config_t( 18, -17.8, 31, 16, false, false ) )
+ ( twinrx_gain_config_t( 19, -16.8, 31, 15, false, false ) )
+ ( twinrx_gain_config_t( 20, -15.8, 31, 14, false, false ) )
+ ( twinrx_gain_config_t( 21, -14.8, 31, 13, false, false ) )
+ ( twinrx_gain_config_t( 22, -13.8, 31, 12, false, false ) )
+ ( twinrx_gain_config_t( 23, -12.8, 31, 11, false, false ) )
+ ( twinrx_gain_config_t( 24, -11.8, 31, 10, false, false ) )
+ ( twinrx_gain_config_t( 25, -10.8, 31, 9, false, false ) )
+ ( twinrx_gain_config_t( 26, -9.8, 31, 8, false, false ) )
+ ( twinrx_gain_config_t( 27, -8.8, 31, 7, false, false ) )
+ ( twinrx_gain_config_t( 28, -7.8, 31, 6, false, false ) )
+ ( twinrx_gain_config_t( 29, -6.8, 31, 5, false, false ) )
+ ( twinrx_gain_config_t( 30, -5.8, 31, 4, false, false ) )
+ ( twinrx_gain_config_t( 31, -4.8, 31, 3, false, false ) )
+ ( twinrx_gain_config_t( 32, -3.8, 31, 2, false, false ) )
+ ( twinrx_gain_config_t( 33, -2.8, 31, 1, false, false ) )
+ ( twinrx_gain_config_t( 34, -1.8, 31, 0, false, false ) )
+ ( twinrx_gain_config_t( 35, -0.8, 30, 0, false, false ) )
+ ( twinrx_gain_config_t( 36, 0.2, 29, 0, false, false ) )
+ ( twinrx_gain_config_t( 37, 1.2, 28, 0, false, false ) )
+ ( twinrx_gain_config_t( 38, 2.2, 27, 0, false, false ) )
+ ( twinrx_gain_config_t( 39, 3.2, 26, 0, false, false ) )
+ ( twinrx_gain_config_t( 40, 4.2, 25, 0, false, false ) )
+ ( twinrx_gain_config_t( 41, 5.2, 24, 0, false, false ) )
+ ( twinrx_gain_config_t( 42, 6.2, 23, 0, false, false ) )
+ ( twinrx_gain_config_t( 43, 7.2, 22, 0, false, false ) )
+ ( twinrx_gain_config_t( 44, 8.2, 21, 0, false, false ) )
+ ( twinrx_gain_config_t( 45, 9.2, 20, 0, false, false ) )
+ ( twinrx_gain_config_t( 46, 10.2, 19, 0, false, false ) )
+ ( twinrx_gain_config_t( 47, 11.2, 18, 0, false, false ) )
+ ( twinrx_gain_config_t( 48, 12.2, 17, 0, false, false ) )
+ ( twinrx_gain_config_t( 49, 13.2, 16, 0, false, false ) )
+ ( twinrx_gain_config_t( 50, 14.2, 15, 0, false, false ) )
+ ( twinrx_gain_config_t( 51, 15.2, 14, 0, false, false ) )
+ ( twinrx_gain_config_t( 52, 16.2, 13, 0, false, false ) )
+ ( twinrx_gain_config_t( 53, 17.2, 12, 0, false, false ) )
+ ( twinrx_gain_config_t( 54, 18.2, 11, 0, false, false ) )
+ ( twinrx_gain_config_t( 55, 19.2, 10, 0, false, false ) )
+ ( twinrx_gain_config_t( 56, 20.2, 9, 0, false, false ) )
+ ( twinrx_gain_config_t( 57, 21.2, 8, 0, false, false ) )
+ ( twinrx_gain_config_t( 58, 22.2, 7, 0, false, false ) )
+ ( twinrx_gain_config_t( 59, 23.2, 6, 0, false, false ) )
+ ( twinrx_gain_config_t( 60, 24.2, 5, 0, false, false ) )
+ ( twinrx_gain_config_t( 61, 25.2, 4, 0, false, false ) )
+ ( twinrx_gain_config_t( 62, 26.2, 3, 0, false, false ) )
+ ( twinrx_gain_config_t( 63, 27.2, 2, 0, false, false ) )
+ ( twinrx_gain_config_t( 64, 28.2, 1, 0, false, false ) )
+ ( twinrx_gain_config_t( 65, 29.2, 0, 0, false, false ) )
+ ( twinrx_gain_config_t( 66, 30.4, 4, 9, false, true ) )
+ ( twinrx_gain_config_t( 67, 31.4, 3, 9, false, true ) )
+ ( twinrx_gain_config_t( 68, 32.4, 2, 9, false, true ) )
+ ( twinrx_gain_config_t( 69, 33.4, 1, 9, false, true ) )
+ ( twinrx_gain_config_t( 70, 34.4, 0, 9, false, true ) )
+ ( twinrx_gain_config_t( 71, 35.4, 8, 0, true, false ) )
+ ( twinrx_gain_config_t( 72, 36.4, 7, 0, true, false ) )
+ ( twinrx_gain_config_t( 73, 37.4, 6, 0, true, false ) )
+ ( twinrx_gain_config_t( 74, 38.4, 5, 0, true, false ) )
+ ( twinrx_gain_config_t( 75, 39.4, 4, 0, true, false ) )
+ ( twinrx_gain_config_t( 76, 40.4, 3, 0, true, false ) )
+ ( twinrx_gain_config_t( 77, 41.4, 2, 0, true, false ) )
+ ( twinrx_gain_config_t( 78, 42.4, 1, 0, true, false ) )
+ ( twinrx_gain_config_t( 79, 43.4, 0, 0, true, false ) )
+ ( twinrx_gain_config_t( 80, 44.6, 4, 9, true, true ) )
+ ( twinrx_gain_config_t( 81, 45.6, 4, 8, true, true ) )
+ ( twinrx_gain_config_t( 82, 46.6, 3, 8, true, true ) )
+ ( twinrx_gain_config_t( 83, 47.6, 2, 8, true, true ) )
+ ( twinrx_gain_config_t( 84, 48.6, 1, 8, true, true ) )
+ ( twinrx_gain_config_t( 85, 49.6, 1, 7, true, true ) )
+ ( twinrx_gain_config_t( 86, 50.6, 0, 7, true, true ) )
+ ( twinrx_gain_config_t( 87, 51.6, 0, 6, true, true ) )
+ ( twinrx_gain_config_t( 88, 52.6, 0, 5, true, true ) )
+ ( twinrx_gain_config_t( 89, 53.6, 0, 4, true, true ) )
+ ( twinrx_gain_config_t( 90, 54.6, 0, 3, true, true ) )
+ ( twinrx_gain_config_t( 91, 55.6, 0, 2, true, true ) )
+ ( twinrx_gain_config_t( 92, 56.6, 0, 1, true, true ) )
+ ( twinrx_gain_config_t( 93, 57.6, 0, 0, true, true ) )
+;
+
+const twinrx_gain_table twinrx_gain_table::lookup_table
+(
+ twinrx_ctrl::signal_path_t signal_path,
+ twinrx_ctrl::preselector_path_t preselector_path,
+ std::string
+) {
+
+ if (signal_path == twinrx_ctrl::PATH_HIGHBAND) {
+ switch (preselector_path) {
+ case twinrx_ctrl::PRESEL_PATH1:
+ return twinrx_gain_table(HIGHBAND1_TABLE);
+ case twinrx_ctrl::PRESEL_PATH2:
+ return twinrx_gain_table(HIGHBAND2_TABLE);
+ case twinrx_ctrl::PRESEL_PATH3:
+ return twinrx_gain_table(HIGHBAND3_TABLE);
+ case twinrx_ctrl::PRESEL_PATH4:
+ return twinrx_gain_table(HIGHBAND4_TABLE);
+ }
+ } else {
+ switch (preselector_path) {
+ case twinrx_ctrl::PRESEL_PATH1:
+ return twinrx_gain_table(LOWBAND1_TABLE);
+ case twinrx_ctrl::PRESEL_PATH2:
+ return twinrx_gain_table(LOWBAND2_TABLE);
+ case twinrx_ctrl::PRESEL_PATH3:
+ return twinrx_gain_table(LOWBAND3_TABLE);
+ case twinrx_ctrl::PRESEL_PATH4:
+ return twinrx_gain_table(LOWBAND4_TABLE);
+ }
+ }
+ throw runtime_error("NO GAIN TABLE SELECTED");
+ return twinrx_gain_table(HIGHBAND1_TABLE);
+}
+
+const twinrx_gain_config_t& twinrx_gain_table::find_by_index(size_t index) const {
+ if (index >= get_num_entries()) throw uhd::value_error("invalid gain table index");
+ return _tbl.at(index);
+}
+
+uhd::gain_range_t twinrx_gain_table::get_gain_range() const {
+ double max = std::numeric_limits<double>::min();
+ double min = std::numeric_limits<double>::max();
+ for (size_t i = 0; i < get_num_entries(); i++) {
+ const twinrx_gain_config_t& config = find_by_index(i);
+ if (config.sys_gain > max) {
+ max = config.sys_gain;
+ }
+ if (config.sys_gain < min) {
+ min = config.sys_gain;
+ }
+ }
+ return uhd::gain_range_t(min, max, 1.0);
+}
diff --git a/host/lib/usrp/dboard/twinrx/twinrx_gain_tables.hpp b/host/lib/usrp/dboard/twinrx/twinrx_gain_tables.hpp
new file mode 100644
index 000000000..0148965da
--- /dev/null
+++ b/host/lib/usrp/dboard/twinrx/twinrx_gain_tables.hpp
@@ -0,0 +1,83 @@
+//
+// 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_DBOARD_TWINRX_GAIN_TABLES_HPP
+#define INCLUDED_DBOARD_TWINRX_GAIN_TABLES_HPP
+
+#include <uhd/config.hpp>
+#include <boost/cstdint.hpp>
+#include <uhd/types/ranges.hpp>
+#include "twinrx_ctrl.hpp"
+
+namespace uhd { namespace usrp { namespace dboard { namespace twinrx {
+
+class twinrx_gain_config_t {
+public:
+ twinrx_gain_config_t(
+ size_t index_, double sys_gain_,
+ boost::uint8_t atten1_, boost::uint8_t atten2_,
+ bool amp1_, bool amp2_
+ ): index(index_), sys_gain(sys_gain_), atten1(atten1_), atten2(atten2_),
+ amp1(amp1_), amp2(amp2_)
+ {}
+
+ twinrx_gain_config_t& operator=(const twinrx_gain_config_t& src) {
+ if (this != &src) {
+ this->index = src.index;
+ this->sys_gain = src.sys_gain;
+ this->atten1 = src.atten1;
+ this->atten2 = src.atten2;
+ this->amp1 = src.amp1;
+ this->amp2 = src.amp2;
+ }
+ return *this;
+ }
+
+ size_t index;
+ double sys_gain;
+ boost::uint8_t atten1;
+ boost::uint8_t atten2;
+ bool amp1;
+ bool amp2;
+};
+
+class twinrx_gain_table {
+public:
+ static const twinrx_gain_table lookup_table(
+ twinrx_ctrl::signal_path_t signal_path,
+ twinrx_ctrl::preselector_path_t presel_path,
+ std::string profile);
+
+ twinrx_gain_table(const std::vector<twinrx_gain_config_t>& tbl)
+ : _tbl(tbl) {}
+
+ const twinrx_gain_config_t& find_by_index(size_t index) const;
+
+ inline size_t get_num_entries() const {
+ return _tbl.size();
+ }
+
+ uhd::gain_range_t get_gain_range() const;
+
+private:
+ const std::vector<twinrx_gain_config_t>& _tbl;
+};
+
+
+}}}} //namespaces
+
+#endif /* INCLUDED_DBOARD_TWINRX_GAIN_TABLES_HPP */
diff --git a/host/lib/usrp/dboard/twinrx/twinrx_io.hpp b/host/lib/usrp/dboard/twinrx/twinrx_io.hpp
new file mode 100644
index 000000000..8b504b549
--- /dev/null
+++ b/host/lib/usrp/dboard/twinrx/twinrx_io.hpp
@@ -0,0 +1,524 @@
+//
+// Copyright 2015 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_DBOARD_TWINRX_IO_HPP
+#define INCLUDED_DBOARD_TWINRX_IO_HPP
+
+#include <uhd/types/wb_iface.hpp>
+#include <uhd/usrp/dboard_base.hpp>
+#include <uhd/utils/soft_register.hpp>
+#include <boost/thread.hpp>
+#include "gpio_atr_3000.hpp"
+
+namespace uhd { namespace usrp { namespace dboard { namespace twinrx {
+
+static const boost::uint32_t SET_ALL_BITS = 0xFFFFFFFF;
+
+namespace cpld {
+static wb_iface::wb_addr_type addr(boost::uint8_t cpld_num, boost::uint8_t cpld_addr) {
+ //Decode CPLD addressing for the following bitmap:
+ // {CPLD1_EN, CPLD2_EN, CPLD3_EN, CPLD4_EN, CPLD_ADDR[2:0]}
+ boost::uint8_t addr = 0;
+ switch (cpld_num) {
+ case 1: addr = 0x8 << 3; break;
+ case 2: addr = 0x4 << 3; break;
+ case 3: addr = 0x2 << 3; break;
+ case 4: addr = 0x1 << 3; break;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+ return static_cast<wb_iface::wb_addr_type>(addr | (cpld_addr & 0x7));
+}
+
+static boost::uint32_t get_reg(wb_iface::wb_addr_type addr) {
+ return static_cast<boost::uint32_t>(addr) & 0x7;
+}
+}
+
+class twinrx_gpio : public timed_wb_iface {
+public:
+ typedef boost::shared_ptr<twinrx_gpio> sptr;
+
+ //----------------------------------------------
+ //Public GPIO fields
+ UHD_DEFINE_SOFT_REG_FIELD(FIELD_LO2_CE_CH1, /*width*/ 1, /*shift*/ 0); //GPIO[0] OUT
+ UHD_DEFINE_SOFT_REG_FIELD(FIELD_LO2_CE_CH2, /*width*/ 1, /*shift*/ 1); //GPIO[1] OUT
+ UHD_DEFINE_SOFT_REG_FIELD(FIELD_LO2_MUXOUT_CH1, /*width*/ 1, /*shift*/ 2); //GPIO[2] IN
+ UHD_DEFINE_SOFT_REG_FIELD(FIELD_LO2_MUXOUT_CH2, /*width*/ 1, /*shift*/ 3); //GPIO[3] IN
+ UHD_DEFINE_SOFT_REG_FIELD(FIELD_LO2_LD_CH1, /*width*/ 1, /*shift*/ 4); //GPIO[4] IN
+ UHD_DEFINE_SOFT_REG_FIELD(FIELD_LO2_LD_CH2, /*width*/ 1, /*shift*/ 5); //GPIO[5] IN
+ // NO CONNECT //GPIO[8:6]
+ // PRIVATE //GPIO[15:9]
+ // NO CONNECT //GPIO[16]
+ UHD_DEFINE_SOFT_REG_FIELD(FIELD_LO1_CE_CH1, /*width*/ 1, /*shift*/ 17); //GPIO[17] OUT
+ UHD_DEFINE_SOFT_REG_FIELD(FIELD_LO1_CE_CH2, /*width*/ 1, /*shift*/ 18); //GPIO[18] OUT
+ UHD_DEFINE_SOFT_REG_FIELD(FIELD_LO1_MUXOUT_CH1, /*width*/ 1, /*shift*/ 19); //GPIO[19] IN
+ UHD_DEFINE_SOFT_REG_FIELD(FIELD_LO1_MUXOUT_CH2, /*width*/ 1, /*shift*/ 20); //GPIO[20] IN
+ // NO CONNECT //GPIO[21:23]
+ UHD_DEFINE_SOFT_REG_FIELD(FIELD_SWPS_CLK, /*width*/ 1, /*shift*/ 24); //GPIO[24] IN
+ UHD_DEFINE_SOFT_REG_FIELD(FIELD_SWPS_PWR_GOOD, /*width*/ 1, /*shift*/ 25); //GPIO[25] IN
+ UHD_DEFINE_SOFT_REG_FIELD(FIELD_SWPS_EN, /*width*/ 1, /*shift*/ 26); //GPIO[26] OUT
+ // PRIVATE //GPIO[27:31]
+ //----------------------------------------------
+
+ twinrx_gpio(dboard_iface::sptr iface) : _db_iface(iface) {
+ _db_iface->set_gpio_ddr(dboard_iface::UNIT_BOTH, GPIO_OUTPUT_MASK, SET_ALL_BITS);
+ _db_iface->set_pin_ctrl(dboard_iface::UNIT_BOTH, GPIO_PINCTRL_MASK, SET_ALL_BITS);
+ _db_iface->set_gpio_out(dboard_iface::UNIT_BOTH, 0, ~GPIO_PINCTRL_MASK);
+ }
+
+ ~twinrx_gpio() {
+ _db_iface->set_gpio_ddr(dboard_iface::UNIT_BOTH, ~GPIO_OUTPUT_MASK, SET_ALL_BITS);
+ }
+
+ void set_field(const uhd::soft_reg_field_t field, const boost::uint32_t value) {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ using namespace soft_reg_field;
+
+ _db_iface->set_gpio_out(dboard_iface::UNIT_BOTH,
+ (value << shift(field)),
+ mask<boost::uint32_t>(field));
+ }
+
+ boost::uint32_t get_field(const uhd::soft_reg_field_t field) {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ using namespace soft_reg_field;
+ return (_db_iface->read_gpio(dboard_iface::UNIT_BOTH) & mask<boost::uint32_t>(field)) >> shift(field);
+ }
+
+ // CPLD register write-only interface
+ void poke32(const wb_addr_type addr, const boost::uint32_t data) {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ using namespace soft_reg_field;
+
+ //Step 1: Write the reg offset and data to the GPIO bus and de-assert all enables
+ _db_iface->set_gpio_out(dboard_iface::UNIT_BOTH,
+ (cpld::get_reg(addr) << shift(CPLD_FULL_ADDR)) | (data << shift(CPLD_DATA)),
+ mask<boost::uint32_t>(CPLD_FULL_ADDR)|mask<boost::uint32_t>(CPLD_DATA));
+ //Sleep for 166ns to ensure that we don't toggle the enables to quickly
+ //nanosleep is not really accurate in userland and it is also not very
+ //cross-platform. So just sleep for the minimum amount of time in us.
+ boost::this_thread::sleep(boost::posix_time::microseconds(0));
+ //Step 2: Write the reg offset and data, and assert the necessary enable
+ _db_iface->set_gpio_out(dboard_iface::UNIT_BOTH,
+ (static_cast<boost::uint32_t>(addr) << shift(CPLD_FULL_ADDR)) | (data << shift(CPLD_DATA)),
+ mask<boost::uint32_t>(CPLD_FULL_ADDR)|mask<boost::uint32_t>(CPLD_DATA));
+ }
+
+ // Timed command interface
+ inline time_spec_t get_time() {
+ return _db_iface->get_command_time();
+ }
+
+ void set_time(const time_spec_t& t) {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ _db_iface->set_command_time(t);
+ }
+
+private: //Members/definitions
+ static const boost::uint32_t GPIO_OUTPUT_MASK = 0xFC06FE03;
+ static const boost::uint32_t GPIO_PINCTRL_MASK = 0x00000000;
+
+ //Private GPIO fields
+ UHD_DEFINE_SOFT_REG_FIELD(CPLD_FULL_ADDR, /*width*/ 7, /*shift*/ 9); //GPIO[15:9]
+ UHD_DEFINE_SOFT_REG_FIELD(CPLD_DATA, /*width*/ 5, /*shift*/ 27); //GPIO[31:27]
+
+ //Members
+ dboard_iface::sptr _db_iface;
+ boost::mutex _mutex;
+};
+
+class twinrx_cpld_regmap : public uhd::soft_regmap_t {
+public:
+ typedef boost::shared_ptr<twinrx_cpld_regmap> sptr;
+
+ //----------------------------------------------
+ // IF CCA: CPLD 1
+ //----------------------------------------------
+ class if0_reg0_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(AMP_HB_IF1_EN_CH1, /*width*/ 1, /*shift*/ 0);
+ UHD_DEFINE_SOFT_REG_FIELD(AMP_LO2_EN_CH1, /*width*/ 1, /*shift*/ 1);
+ UHD_DEFINE_SOFT_REG_FIELD(AMP_LO2_EN_CH2, /*width*/ 1, /*shift*/ 2);
+ UHD_DEFINE_SOFT_REG_FIELD(SW19_CTRL_CH2, /*width*/ 1, /*shift*/ 3);
+ UHD_DEFINE_SOFT_REG_FIELD(SW20_CTRL_CH2, /*width*/ 1, /*shift*/ 4);
+
+ if0_reg0_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 1, /*reg*/ 0), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } if0_reg0;
+
+ class if0_reg1_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(SW20_CTRL_CH1, /*width*/ 1, /*shift*/ 2);
+
+ if0_reg1_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 1, /*reg*/ 1), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } if0_reg1;
+
+ class if0_reg2_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(AMP_LB_IF1_EN_CH2, /*width*/ 1, /*shift*/ 0);
+ UHD_DEFINE_SOFT_REG_FIELD(AMP_LB_IF1_EN_CH1, /*width*/ 1, /*shift*/ 1);
+ UHD_DEFINE_SOFT_REG_FIELD(LO2_LE_CH1, /*width*/ 1, /*shift*/ 3);
+ UHD_DEFINE_SOFT_REG_FIELD(LO2_LE_CH2, /*width*/ 1, /*shift*/ 4);
+
+ if0_reg2_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 1, /*reg*/ 2), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } if0_reg2;
+
+ class if0_reg3_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(SW24_CTRL_CH1, /*width*/ 1, /*shift*/ 1);
+ UHD_DEFINE_SOFT_REG_FIELD(SW13_CTRL_CH1, /*width*/ 1, /*shift*/ 2);
+ UHD_DEFINE_SOFT_REG_FIELD(IF1_IF2_EN_CH1, /*width*/ 1, /*shift*/ 3);
+
+ if0_reg3_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 1, /*reg*/ 3), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } if0_reg3;
+
+ class if0_reg4_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(SW21_CTRL_CH2, /*width*/ 1, /*shift*/ 0);
+ UHD_DEFINE_SOFT_REG_FIELD(SW25_CTRL, /*width*/ 1, /*shift*/ 1);
+ UHD_DEFINE_SOFT_REG_FIELD(IF1_IF2_EN_CH2, /*width*/ 1, /*shift*/ 2);
+ UHD_DEFINE_SOFT_REG_FIELD(SW19_CTRL_CH1, /*width*/ 1, /*shift*/ 3);
+ UHD_DEFINE_SOFT_REG_FIELD(SW21_CTRL_CH1, /*width*/ 1, /*shift*/ 4);
+
+ if0_reg4_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 1, /*reg*/ 4), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } if0_reg4;
+
+ class if0_reg6_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(AMP_HB_IF1_EN_CH2, /*width*/ 1, /*shift*/ 0);
+ UHD_DEFINE_SOFT_REG_FIELD(SW13_CTRL_CH2, /*width*/ 1, /*shift*/ 2);
+
+ if0_reg6_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 1, /*reg*/ 6), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } if0_reg6;
+
+ class if0_reg7_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(SW24_CTRL_CH2, /*width*/ 1, /*shift*/ 0);
+
+ if0_reg7_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 1, /*reg*/ 7), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } if0_reg7;
+
+ //----------------------------------------------
+ // RF CCA: CPLD 2
+ //----------------------------------------------
+ class rf0_reg0_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(ATTEN_IN_CH1, /*width*/ 5, /*shift*/ 0);
+
+ rf0_reg0_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 2, /*reg*/ 0), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } rf0_reg0;
+
+ class rf0_reg1_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(SWPA1_CTL_CH1, /*width*/ 1, /*shift*/ 1);
+ UHD_DEFINE_SOFT_REG_FIELD(HB_PREAMP_EN_CH1, /*width*/ 1, /*shift*/ 2);
+ UHD_DEFINE_SOFT_REG_FIELD(LB_PREAMP_EN_CH1, /*width*/ 1, /*shift*/ 3);
+ UHD_DEFINE_SOFT_REG_FIELD(SWPA3_CTRL_CH2, /*width*/ 1, /*shift*/ 4);
+
+ rf0_reg1_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 2, /*reg*/ 1), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } rf0_reg1;
+
+ class rf0_reg2_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(SW6_CTRL_CH1, /*width*/ 1, /*shift*/ 0);
+ UHD_DEFINE_SOFT_REG_FIELD(SW5_CTRL_CH1, /*width*/ 1, /*shift*/ 1);
+ UHD_DEFINE_SOFT_REG_FIELD(SW4_CTRL_CH1, /*width*/ 1, /*shift*/ 2);
+ UHD_DEFINE_SOFT_REG_FIELD(LO1_LE_CH1, /*width*/ 1, /*shift*/ 3);
+ UHD_DEFINE_SOFT_REG_FIELD(LO1_LE_CH2, /*width*/ 1, /*shift*/ 4);
+
+ rf0_reg2_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 2, /*reg*/ 2), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } rf0_reg2;
+
+ class rf0_reg3_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(SW9_CTRL_CH2, /*width*/ 2, /*shift*/ 0);
+ UHD_DEFINE_SOFT_REG_FIELD(SW7_CTRL_CH1, /*width*/ 2, /*shift*/ 2);
+
+ rf0_reg3_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 2, /*reg*/ 3), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } rf0_reg3;
+
+ class rf0_reg4_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(ATTEN_IN_CH2, /*width*/ 5, /*shift*/ 0);
+
+ rf0_reg4_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 2, /*reg*/ 4), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } rf0_reg4;
+
+ class rf0_reg5_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(SW9_CTRL_CH1, /*width*/ 2, /*shift*/ 0);
+ UHD_DEFINE_SOFT_REG_FIELD(HB_PREAMP_EN_CH2, /*width*/ 1, /*shift*/ 2);
+ UHD_DEFINE_SOFT_REG_FIELD(SW3_CTRL_CH1, /*width*/ 1, /*shift*/ 4);
+
+ rf0_reg5_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 2, /*reg*/ 5), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } rf0_reg5;
+
+ class rf0_reg6_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(SW6_CTRL_CH2, /*width*/ 1, /*shift*/ 0);
+ UHD_DEFINE_SOFT_REG_FIELD(SW5_CTRL_CH2, /*width*/ 1, /*shift*/ 1);
+ UHD_DEFINE_SOFT_REG_FIELD(SW4_CTRL_CH2, /*width*/ 1, /*shift*/ 2);
+ UHD_DEFINE_SOFT_REG_FIELD(SWPA4_CTRL_CH2, /*width*/ 1, /*shift*/ 4);
+
+ rf0_reg6_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 2, /*reg*/ 6), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } rf0_reg6;
+
+ class rf0_reg7_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(SWPA1_CTRL_CH2, /*width*/ 1, /*shift*/ 0);
+ UHD_DEFINE_SOFT_REG_FIELD(SWPA3_CTRL_CH1, /*width*/ 1, /*shift*/ 1);
+ UHD_DEFINE_SOFT_REG_FIELD(SW3_CTRL_CH2, /*width*/ 1, /*shift*/ 2);
+ UHD_DEFINE_SOFT_REG_FIELD(SW7_CTRL_CH2, /*width*/ 2, /*shift*/ 3);
+
+ rf0_reg7_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 2, /*reg*/ 7), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } rf0_reg7;
+
+ //----------------------------------------------
+ // RF CCA: CPLD 3
+ //----------------------------------------------
+ class rf1_reg0_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(ATTEN_HB_CH1, /*width*/ 5, /*shift*/ 0);
+
+ rf1_reg0_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 3, /*reg*/ 0), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } rf1_reg0;
+
+ class rf1_reg1_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(SW17_CTRL_CH1, /*width*/ 1, /*shift*/ 0);
+ UHD_DEFINE_SOFT_REG_FIELD(AMP_LO1_EN_CH1, /*width*/ 1, /*shift*/ 1);
+ UHD_DEFINE_SOFT_REG_FIELD(SW16_CTRL_CH1, /*width*/ 1, /*shift*/ 2);
+ UHD_DEFINE_SOFT_REG_FIELD(SW15_CTRL_CH1, /*width*/ 1, /*shift*/ 3);
+ UHD_DEFINE_SOFT_REG_FIELD(SW14_CTRL_CH1, /*width*/ 1, /*shift*/ 4);
+
+ rf1_reg1_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 3, /*reg*/ 1), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } rf1_reg1;
+
+ class rf1_reg2_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(SW12_CTRL_CH1, /*width*/ 1, /*shift*/ 0);
+ UHD_DEFINE_SOFT_REG_FIELD(AMP_HB_EN_CH1, /*width*/ 1, /*shift*/ 1);
+ UHD_DEFINE_SOFT_REG_FIELD(HB_PRESEL_PGA_EN_CH2, /*width*/ 1, /*shift*/ 2);
+
+ rf1_reg2_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 3, /*reg*/ 2), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } rf1_reg2;
+
+ class rf1_reg3_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(SW23_CTRL, /*width*/ 1, /*shift*/ 0);
+ UHD_DEFINE_SOFT_REG_FIELD(SW22_CTRL_CH1, /*width*/ 1, /*shift*/ 1);
+ UHD_DEFINE_SOFT_REG_FIELD(SW10_CTRL_CH1, /*width*/ 2, /*shift*/ 2);
+
+ rf1_reg3_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 3, /*reg*/ 3), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } rf1_reg3;
+
+ class rf1_reg4_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(ATTEN_HB_CH2, /*width*/ 5, /*shift*/ 0);
+
+ rf1_reg4_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 3, /*reg*/ 4), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } rf1_reg4;
+
+ class rf1_reg5_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(AMP_LO1_EN_CH2, /*width*/ 1, /*shift*/ 0);
+ UHD_DEFINE_SOFT_REG_FIELD(SW15_CTRL_CH2, /*width*/ 1, /*shift*/ 1);
+ UHD_DEFINE_SOFT_REG_FIELD(SW14_CTRL_CH2, /*width*/ 1, /*shift*/ 2);
+ UHD_DEFINE_SOFT_REG_FIELD(SW18_CTRL_CH1, /*width*/ 1, /*shift*/ 4);
+
+ rf1_reg5_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 3, /*reg*/ 5), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } rf1_reg5;
+
+ class rf1_reg6_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(HB_PRESEL_PGA_EN_CH1, /*width*/ 1, /*shift*/ 0);
+ UHD_DEFINE_SOFT_REG_FIELD(SW17_CTRL_CH2, /*width*/ 1, /*shift*/ 2);
+ UHD_DEFINE_SOFT_REG_FIELD(SW16_CTRL_CH2, /*width*/ 1, /*shift*/ 3);
+ UHD_DEFINE_SOFT_REG_FIELD(PREAMP2_EN_CH2, /*width*/ 1, /*shift*/ 4);
+
+ rf1_reg6_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 3, /*reg*/ 6), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } rf1_reg6;
+
+ class rf1_reg7_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(SW22_CTRL_CH2, /*width*/ 1, /*shift*/ 0);
+ UHD_DEFINE_SOFT_REG_FIELD(SW10_CTRL_CH2, /*width*/ 2, /*shift*/ 1);
+ UHD_DEFINE_SOFT_REG_FIELD(SW12_CTRL_CH2, /*width*/ 1, /*shift*/ 3);
+ UHD_DEFINE_SOFT_REG_FIELD(AMP_HB_EN_CH2, /*width*/ 1, /*shift*/ 4);
+
+ rf1_reg7_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 3, /*reg*/ 7), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } rf1_reg7;
+
+ //----------------------------------------------
+ // RF CCA: CPLD 4
+ //----------------------------------------------
+ class rf2_reg0_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(ATTEN_LB_CH1, /*width*/ 5, /*shift*/ 0);
+
+ rf2_reg0_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 4, /*reg*/ 0), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } rf2_reg0;
+
+ class rf2_reg2_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(SW11_CTRL_CH1, /*width*/ 1, /*shift*/ 0);
+ UHD_DEFINE_SOFT_REG_FIELD(AMP_LB_EN_CH1, /*width*/ 1, /*shift*/ 1);
+ UHD_DEFINE_SOFT_REG_FIELD(SWPA2_CTRL_CH1, /*width*/ 1, /*shift*/ 2);
+
+ rf2_reg2_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 4, /*reg*/ 2), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } rf2_reg2;
+
+ class rf2_reg3_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(PREAMP2_EN_CH1, /*width*/ 1, /*shift*/ 0);
+ UHD_DEFINE_SOFT_REG_FIELD(SW18_CTRL_CH2, /*width*/ 1, /*shift*/ 1);
+ UHD_DEFINE_SOFT_REG_FIELD(SW8_CTRL_CH1, /*width*/ 2, /*shift*/ 2);
+
+ rf2_reg3_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 4, /*reg*/ 3), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } rf2_reg3;
+
+ class rf2_reg4_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(ATTEN_LB_CH2, /*width*/ 5, /*shift*/ 0);
+
+ rf2_reg4_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 4, /*reg*/ 4), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } rf2_reg4;
+
+ class rf2_reg5_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(SWPA2_CTRL_CH2, /*width*/ 1, /*shift*/ 0);
+
+ rf2_reg5_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 4, /*reg*/ 5), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } rf2_reg5;
+
+ class rf2_reg6_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(LB_PREAMP_EN_CH2, /*width*/ 1, /*shift*/ 0);
+
+ rf2_reg6_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 4, /*reg*/ 6), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } rf2_reg6;
+
+ class rf2_reg7_t : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(SWPA4_CTRL_CH1, /*width*/ 1, /*shift*/ 0);
+ UHD_DEFINE_SOFT_REG_FIELD(SW8_CTRL_CH2, /*width*/ 2, /*shift*/ 1);
+ UHD_DEFINE_SOFT_REG_FIELD(SW11_CTRL_CH2, /*width*/ 1, /*shift*/ 3);
+ UHD_DEFINE_SOFT_REG_FIELD(AMP_LB_EN_CH2, /*width*/ 1, /*shift*/ 4);
+
+ rf2_reg7_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 4, /*reg*/ 7), OPTIMIZED_FLUSH) {
+ set(REGISTER, 0);
+ }
+ } rf2_reg7;
+
+ twinrx_cpld_regmap() : soft_regmap_t("twinrx_cpld") {
+ // IF CCA: CPLD 1
+ add_to_map(if0_reg0, "if0_reg0");
+ add_to_map(if0_reg1, "if0_reg1");
+ add_to_map(if0_reg2, "if0_reg2");
+ add_to_map(if0_reg3, "if0_reg3");
+ add_to_map(if0_reg4, "if0_reg4");
+ add_to_map(if0_reg6, "if0_reg6");
+ add_to_map(if0_reg7, "if0_reg7");
+ // RF CCA: CPLD 2
+ add_to_map(rf0_reg0, "rf0_reg0");
+ add_to_map(rf0_reg1, "rf0_reg1");
+ add_to_map(rf0_reg2, "rf0_reg2");
+ add_to_map(rf0_reg3, "rf0_reg3");
+ add_to_map(rf0_reg4, "rf0_reg4");
+ add_to_map(rf0_reg5, "rf0_reg5");
+ add_to_map(rf0_reg6, "rf0_reg6");
+ add_to_map(rf0_reg7, "rf0_reg7");
+ // RF CCA: CPLD 3
+ add_to_map(rf1_reg0, "rf1_reg0");
+ add_to_map(rf1_reg1, "rf1_reg1");
+ add_to_map(rf1_reg2, "rf1_reg2");
+ add_to_map(rf1_reg3, "rf1_reg3");
+ add_to_map(rf1_reg4, "rf1_reg4");
+ add_to_map(rf1_reg5, "rf1_reg5");
+ add_to_map(rf1_reg6, "rf1_reg6");
+ add_to_map(rf1_reg7, "rf1_reg7");
+ // RF CCA: CPLD 4
+ add_to_map(rf2_reg0, "rf2_reg0");
+ add_to_map(rf2_reg2, "rf2_reg2");
+ add_to_map(rf2_reg3, "rf2_reg3");
+ add_to_map(rf2_reg4, "rf2_reg4");
+ add_to_map(rf2_reg5, "rf2_reg5");
+ add_to_map(rf2_reg6, "rf2_reg6");
+ add_to_map(rf2_reg7, "rf2_reg7");
+ }
+};
+
+}}}} //namespaces
+
+#endif /* INCLUDED_DBOARD_TWINRX_IO_HPP */