diff options
Diffstat (limited to 'host/lib/usrp/dboard/twinrx')
-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 |
8 files changed, 3504 insertions, 0 deletions
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 */ |