diff options
Diffstat (limited to 'host/lib/usrp')
| -rw-r--r-- | host/lib/usrp/dboard/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_twinrx.cpp | 337 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/twinrx/table_to_cpp.py | 26 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp | 643 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/twinrx/twinrx_ctrl.hpp | 101 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/twinrx/twinrx_experts.cpp | 637 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/twinrx/twinrx_experts.hpp | 630 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/twinrx/twinrx_gain_tables.cpp | 860 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/twinrx/twinrx_gain_tables.hpp | 83 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/twinrx/twinrx_io.hpp | 524 | 
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> ®s) +    { +        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 */ | 
