diff options
Diffstat (limited to 'host/lib/usrp/dboard')
-rw-r--r-- | host/lib/usrp/dboard/CMakeLists.txt | 1 | ||||
-rw-r--r-- | host/lib/usrp/dboard/db_basic_and_lf.cpp | 43 | ||||
-rw-r--r-- | host/lib/usrp/dboard/db_dbsrx.cpp | 68 | ||||
-rw-r--r-- | host/lib/usrp/dboard/db_rfx.cpp | 47 | ||||
-rw-r--r-- | host/lib/usrp/dboard/db_tvrx.cpp | 495 | ||||
-rw-r--r-- | host/lib/usrp/dboard/db_unknown.cpp | 37 | ||||
-rw-r--r-- | host/lib/usrp/dboard/db_wbx.cpp | 37 | ||||
-rw-r--r-- | host/lib/usrp/dboard/db_xcvr2450.cpp | 161 |
8 files changed, 842 insertions, 47 deletions
diff --git a/host/lib/usrp/dboard/CMakeLists.txt b/host/lib/usrp/dboard/CMakeLists.txt index 3e995009e..8d3d11530 100644 --- a/host/lib/usrp/dboard/CMakeLists.txt +++ b/host/lib/usrp/dboard/CMakeLists.txt @@ -24,5 +24,6 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_wbx.cpp ${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_dbsrx.cpp ${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_unknown.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_tvrx.cpp ) diff --git a/host/lib/usrp/dboard/db_basic_and_lf.cpp b/host/lib/usrp/dboard/db_basic_and_lf.cpp index 4b0d8bf27..4c49b3bff 100644 --- a/host/lib/usrp/dboard/db_basic_and_lf.cpp +++ b/host/lib/usrp/dboard/db_basic_and_lf.cpp @@ -20,6 +20,7 @@ #include <uhd/types/ranges.hpp> #include <uhd/utils/assert.hpp> #include <uhd/utils/static.hpp> +#include <uhd/utils/warning.hpp> #include <uhd/usrp/dboard_base.hpp> #include <uhd/usrp/dboard_manager.hpp> #include <boost/assign/list_of.hpp> @@ -68,11 +69,11 @@ static const uhd::dict<std::string, subdev_conn_t> sd_name_to_conn = map_list_of * Register the basic and LF dboards **********************************************************************/ static dboard_base::sptr make_basic_rx(dboard_base::ctor_args_t args){ - return dboard_base::sptr(new basic_rx(args, 90e9)); + return dboard_base::sptr(new basic_rx(args, 250e6)); } static dboard_base::sptr make_basic_tx(dboard_base::ctor_args_t args){ - return dboard_base::sptr(new basic_tx(args, 90e9)); + return dboard_base::sptr(new basic_tx(args, 250e6)); } static dboard_base::sptr make_lf_rx(dboard_base::ctor_args_t args){ @@ -149,6 +150,10 @@ void basic_rx::rx_get(const wax::obj &key_, wax::obj &val){ val = sd_name_to_conn[get_subdev_name()]; return; + case SUBDEV_PROP_ENABLED: + val = true; //always enabled + return; + case SUBDEV_PROP_USE_LO_OFFSET: val = false; return; @@ -157,6 +162,10 @@ void basic_rx::rx_get(const wax::obj &key_, wax::obj &val){ val = true; //there is no LO, so it must be true! return; + case SUBDEV_PROP_BANDWIDTH: + val = 2*_max_freq; //we want complex double-sided + return; + default: UHD_THROW_PROP_GET_ERROR(); } } @@ -178,6 +187,17 @@ void basic_rx::rx_set(const wax::obj &key_, const wax::obj &val){ case SUBDEV_PROP_FREQ: return; // it wont do you much good, but you can set it + case SUBDEV_PROP_ENABLED: + return; //always enabled + + case SUBDEV_PROP_BANDWIDTH: + uhd::warning::post( + str(boost::format("%s: No tunable bandwidth, fixed filtered to %0.2fMHz") + % get_rx_id().to_pp_string() % _max_freq + ) + ); + return; + default: UHD_THROW_PROP_SET_ERROR(); } } @@ -241,6 +261,10 @@ void basic_tx::tx_get(const wax::obj &key_, wax::obj &val){ val = sd_name_to_conn[get_subdev_name()]; return; + case SUBDEV_PROP_ENABLED: + val = true; //always enabled + return; + case SUBDEV_PROP_USE_LO_OFFSET: val = false; return; @@ -249,6 +273,10 @@ void basic_tx::tx_get(const wax::obj &key_, wax::obj &val){ val = true; //there is no LO, so it must be true! return; + case SUBDEV_PROP_BANDWIDTH: + val = 2*_max_freq; //we want complex double-sided + return; + default: UHD_THROW_PROP_GET_ERROR(); } } @@ -270,6 +298,17 @@ void basic_tx::tx_set(const wax::obj &key_, const wax::obj &val){ case SUBDEV_PROP_FREQ: return; // it wont do you much good, but you can set it + case SUBDEV_PROP_ENABLED: + return; //always enabled + + case SUBDEV_PROP_BANDWIDTH: + uhd::warning::post( + str(boost::format("%s: No tunable bandwidth, fixed filtered to %0.2fMHz") + % get_tx_id().to_pp_string() % _max_freq + ) + ); + return; + default: UHD_THROW_PROP_SET_ERROR(); } } diff --git a/host/lib/usrp/dboard/db_dbsrx.cpp b/host/lib/usrp/dboard/db_dbsrx.cpp index 81434f054..85251bdf9 100644 --- a/host/lib/usrp/dboard/db_dbsrx.cpp +++ b/host/lib/usrp/dboard/db_dbsrx.cpp @@ -69,7 +69,7 @@ public: private: double _lo_freq; - float _bandwidth; + double _bandwidth; uhd::dict<std::string, float> _gains; max2118_write_regs_t _max2118_write_regs; max2118_read_regs_t _max2118_read_regs; @@ -79,7 +79,7 @@ private: void set_lo_freq(double target_freq); void set_gain(float gain, const std::string &name); - void set_bandwidth(float bandwidth); + void set_bandwidth(double bandwidth); void send_reg(boost::uint8_t start_reg, boost::uint8_t stop_reg){ start_reg = boost::uint8_t(std::clip(int(start_reg), 0x0, 0x5)); @@ -162,15 +162,10 @@ static dboard_base::sptr make_dbsrx(dboard_base::ctor_args_t args){ return dboard_base::sptr(new dbsrx(args)); } -//dbid for USRP2 version UHD_STATIC_BLOCK(reg_dbsrx_dboard){ - //register the factory function for the rx dbid + //register the factory function for the rx dbid (others version) dboard_manager::register_dboard(0x000D, &make_dbsrx, "DBSRX"); -} - -//dbid for USRP1 version -UHD_STATIC_BLOCK(reg_dbsrx_on_usrp1_dboard){ - //register the factory function for the rx dbid + //register the factory function for the rx dbid (USRP1 version) dboard_manager::register_dboard(0x0002, &make_dbsrx, "DBSRX"); } @@ -180,7 +175,7 @@ UHD_STATIC_BLOCK(reg_dbsrx_on_usrp1_dboard){ dbsrx::dbsrx(ctor_args_t args) : rx_dboard_base(args){ //warn user about incorrect DBID on USRP1, requires R193 populated if (this->get_iface()->get_special_props().soft_clock_divider and this->get_rx_id() == 0x000D) - uhd::print_warning( + uhd::warning::post( str(boost::format( "DBSRX: incorrect dbid\n" "Expected dbid 0x0002 and R193\n" @@ -191,7 +186,7 @@ dbsrx::dbsrx(ctor_args_t args) : rx_dboard_base(args){ //warn user about incorrect DBID on non-USRP1, requires R194 populated if (not this->get_iface()->get_special_props().soft_clock_divider and this->get_rx_id() == 0x0002) - uhd::print_warning( + uhd::warning::post( str(boost::format( "DBSRX: incorrect dbid\n" "Expected dbid 0x000D and R194\n" @@ -239,11 +234,12 @@ void dbsrx::set_lo_freq(double target_freq){ double actual_freq=0.0, pfd_freq=0.0, ref_clock=0.0; int R=0, N=0, r=0, m=0; bool update_filter_settings = false; - //choose refclock std::vector<double> clock_rates = this->get_iface()->get_clock_rates(dboard_iface::UNIT_RX); + const double max_clock_rate = std::sorted(clock_rates).back(); BOOST_FOREACH(ref_clock, std::reversed(std::sorted(clock_rates))){ if (ref_clock > 27.0e6) continue; + if (size_t(max_clock_rate/ref_clock)%2 == 1) continue; //reject asymmetric clocks (odd divisors) //choose m_divider such that filter tuning constraint is met m = 31; @@ -251,7 +247,7 @@ void dbsrx::set_lo_freq(double target_freq){ if(dbsrx_debug) std::cerr << boost::format( "DBSRX: trying ref_clock %f and m_divider %d" - ) % (this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX)) % m << std::endl; + ) % (ref_clock) % m << std::endl; if (m >= 32) continue; @@ -277,15 +273,17 @@ void dbsrx::set_lo_freq(double target_freq){ } } - //Assert because we failed to find a suitable combination of ref_clock, R and N - UHD_ASSERT_THROW(ref_clock/(1 << m) < 1e6 or ref_clock/(1 << m) > 2.5e6); - UHD_ASSERT_THROW((pfd_freq < dbsrx_pfd_freq_range.min) or (pfd_freq > dbsrx_pfd_freq_range.max)); - UHD_ASSERT_THROW((N < 256) or (N > 32768)); done_loop: + //Assert because we failed to find a suitable combination of ref_clock, R and N + UHD_ASSERT_THROW(ref_clock <= 27.0e6 and ref_clock >= 0.0); + UHD_ASSERT_THROW(ref_clock/m >= 1e6 and ref_clock/m <= 2.5e6); + UHD_ASSERT_THROW((pfd_freq >= dbsrx_pfd_freq_range.min) and (pfd_freq <= dbsrx_pfd_freq_range.max)); + UHD_ASSERT_THROW((N >= 256) and (N <= 32768)); + if(dbsrx_debug) std::cerr << boost::format( - "DBSRX: choose ref_clock %f and m_divider %d" - ) % (this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX)) % m << std::endl; + "DBSRX: choose ref_clock (current: %f, new: %f) and m_divider %d" + ) % (this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX)) % ref_clock % m << std::endl; //if ref_clock or m divider changed, we need to update the filter settings if (ref_clock != this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX) or m != _max2118_write_regs.m_divider) update_filter_settings = true; @@ -344,12 +342,12 @@ void dbsrx::set_lo_freq(double target_freq){ //vtune is too low, try lower frequency vco if (_max2118_read_regs.adc == 0){ if (_max2118_write_regs.osc_band == 0){ - uhd::print_warning( + uhd::warning::post( str(boost::format( "DBSRX: Tuning exceeded vco range, _max2118_write_regs.osc_band == %d\n" ) % int(_max2118_write_regs.osc_band)) ); - UHD_ASSERT_THROW(_max2118_read_regs.adc == 0); + UHD_ASSERT_THROW(_max2118_read_regs.adc != 0); //just to cause a throw } if (_max2118_write_regs.osc_band <= 0) break; _max2118_write_regs.osc_band -= 1; @@ -358,12 +356,12 @@ void dbsrx::set_lo_freq(double target_freq){ //vtune is too high, try higher frequency vco if (_max2118_read_regs.adc == 7){ if (_max2118_write_regs.osc_band == 7){ - uhd::print_warning( + uhd::warning::post( str(boost::format( "DBSRX: Tuning exceeded vco range, _max2118_write_regs.osc_band == %d\n" ) % int(_max2118_write_regs.osc_band)) ); - UHD_ASSERT_THROW(_max2118_read_regs.adc == 0); + UHD_ASSERT_THROW(_max2118_read_regs.adc != 7); //just to cause a throw } if (_max2118_write_regs.osc_band >= 7) break; _max2118_write_regs.osc_band += 1; @@ -401,6 +399,7 @@ void dbsrx::set_lo_freq(double target_freq){ << boost::format(" Ref Freq=%fMHz\n") % (ref_clock/1e6) << boost::format(" Target Freq=%fMHz\n") % (target_freq/1e6) << boost::format(" Actual Freq=%fMHz\n") % (_lo_freq/1e6) + << boost::format(" VCO Freq=%fMHz\n") % (vco_freq/1e6) << std::endl; if (update_filter_settings) set_bandwidth(_bandwidth); @@ -480,9 +479,9 @@ void dbsrx::set_gain(float gain, const std::string &name){ /*********************************************************************** * Bandwidth Handling **********************************************************************/ -void dbsrx::set_bandwidth(float bandwidth){ +void dbsrx::set_bandwidth(double bandwidth){ //clip the input - bandwidth = std::clip<float>(bandwidth, 4e6, 33e6); + bandwidth = std::clip<double>(bandwidth, 4e6, 33e6); double ref_clock = this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX); @@ -492,7 +491,7 @@ void dbsrx::set_bandwidth(float bandwidth){ _max2118_write_regs.f_dac = std::clip<int>(int((((bandwidth*_max2118_write_regs.m_divider)/ref_clock) - 4)/0.145),0,127); //determine actual bandwidth - _bandwidth = float((ref_clock/(_max2118_write_regs.m_divider))*(4+0.145*_max2118_write_regs.f_dac)); + _bandwidth = double((ref_clock/(_max2118_write_regs.m_divider))*(4+0.145*_max2118_write_regs.f_dac)); if (dbsrx_debug) std::cerr << boost::format( "DBSRX Filter Bandwidth: %f MHz, m: %d, f_dac: %d\n" @@ -551,6 +550,10 @@ void dbsrx::rx_get(const wax::obj &key_, wax::obj &val){ val = SUBDEV_CONN_COMPLEX_IQ; return; + case SUBDEV_PROP_ENABLED: + val = true; //always enabled + return; + case SUBDEV_PROP_USE_LO_OFFSET: val = false; return; @@ -559,14 +562,8 @@ void dbsrx::rx_get(const wax::obj &key_, wax::obj &val){ val = this->get_locked(); return; -/* - case SUBDEV_PROP_RSSI: - val = this->get_rssi(); - return; -*/ - case SUBDEV_PROP_BANDWIDTH: - val = _bandwidth; + val = 2*_bandwidth; //_bandwidth is low-pass, we want complex double-sided return; default: UHD_THROW_PROP_GET_ERROR(); @@ -587,8 +584,11 @@ void dbsrx::rx_set(const wax::obj &key_, const wax::obj &val){ this->set_gain(val.as<float>(), key.name); return; + case SUBDEV_PROP_ENABLED: + return; //always enabled + case SUBDEV_PROP_BANDWIDTH: - this->set_bandwidth(val.as<float>()); + this->set_bandwidth(val.as<double>()/2.0); //complex double-sided, we want low-pass return; default: UHD_THROW_PROP_SET_ERROR(); diff --git a/host/lib/usrp/dboard/db_rfx.cpp b/host/lib/usrp/dboard/db_rfx.cpp index c3ab96e59..12e458d8c 100644 --- a/host/lib/usrp/dboard/db_rfx.cpp +++ b/host/lib/usrp/dboard/db_rfx.cpp @@ -43,6 +43,7 @@ #include <uhd/utils/assert.hpp> #include <uhd/utils/static.hpp> #include <uhd/utils/algorithm.hpp> +#include <uhd/utils/warning.hpp> #include <uhd/usrp/dboard_id.hpp> #include <uhd/usrp/dboard_base.hpp> #include <uhd/usrp/dboard_manager.hpp> @@ -152,12 +153,12 @@ static dboard_base::sptr make_rfx_flex2400(dboard_base::ctor_args_t args){ } UHD_STATIC_BLOCK(reg_rfx_dboards){ - dboard_manager::register_dboard(0x0024, 0x0028, &make_rfx_flex400, "Flex 400 MIMO B"); - dboard_manager::register_dboard(0x0025, 0x0029, &make_rfx_flex900, "Flex 900 MIMO B"); - dboard_manager::register_dboard(0x0034, 0x0035, &make_rfx_flex1800, "Flex 1800 MIMO B"); - dboard_manager::register_dboard(0x0026, 0x002a, &make_rfx_flex1200, "Flex 1200 MIMO B"); - dboard_manager::register_dboard(0x002c, 0x002d, &make_rfx_flex2200, "Flex 2200 MIMO B"); - dboard_manager::register_dboard(0x0027, 0x002b, &make_rfx_flex2400, "Flex 2400 MIMO B"); + dboard_manager::register_dboard(0x0024, 0x0028, &make_rfx_flex400, "RFX400"); + dboard_manager::register_dboard(0x0025, 0x0029, &make_rfx_flex900, "RFX900"); + dboard_manager::register_dboard(0x0034, 0x0035, &make_rfx_flex1800, "RFX1800"); + dboard_manager::register_dboard(0x0026, 0x002a, &make_rfx_flex1200, "RFX1200"); + dboard_manager::register_dboard(0x002c, 0x002d, &make_rfx_flex2200, "RFX2200"); + dboard_manager::register_dboard(0x0027, 0x002b, &make_rfx_flex2400, "RFX2400"); } /*********************************************************************** @@ -444,6 +445,10 @@ void rfx_xcvr::rx_get(const wax::obj &key_, wax::obj &val){ val = SUBDEV_CONN_COMPLEX_QI; return; + case SUBDEV_PROP_ENABLED: + val = true; //always enabled + return; + case SUBDEV_PROP_USE_LO_OFFSET: val = false; return; @@ -452,6 +457,10 @@ void rfx_xcvr::rx_get(const wax::obj &key_, wax::obj &val){ val = this->get_locked(dboard_iface::UNIT_RX); return; + case SUBDEV_PROP_BANDWIDTH: + val = 2*20.0e6; //30MHz low-pass, we want complex double-sided + return; + default: UHD_THROW_PROP_GET_ERROR(); } } @@ -474,6 +483,15 @@ void rfx_xcvr::rx_set(const wax::obj &key_, const wax::obj &val){ this->set_rx_ant(val.as<std::string>()); return; + case SUBDEV_PROP_ENABLED: + return; //always enabled + + case SUBDEV_PROP_BANDWIDTH: + uhd::warning::post( + str(boost::format("RFX: No tunable bandwidth, fixed filtered to 40MHz")) + ); + return; + default: UHD_THROW_PROP_SET_ERROR(); } } @@ -524,6 +542,10 @@ void rfx_xcvr::tx_get(const wax::obj &key_, wax::obj &val){ val = SUBDEV_CONN_COMPLEX_IQ; return; + case SUBDEV_PROP_ENABLED: + val = true; //always enabled + return; + case SUBDEV_PROP_USE_LO_OFFSET: val = true; return; @@ -532,6 +554,10 @@ void rfx_xcvr::tx_get(const wax::obj &key_, wax::obj &val){ val = this->get_locked(dboard_iface::UNIT_TX); return; + case SUBDEV_PROP_BANDWIDTH: + val = 2*20.0e6; //30MHz low-pass, we want complex double-sided + return; + default: UHD_THROW_PROP_GET_ERROR(); } } @@ -554,6 +580,15 @@ void rfx_xcvr::tx_set(const wax::obj &key_, const wax::obj &val){ this->set_tx_ant(val.as<std::string>()); return; + case SUBDEV_PROP_ENABLED: + return; //always enabled + + case SUBDEV_PROP_BANDWIDTH: + uhd::warning::post( + str(boost::format("RFX: No tunable bandwidth, fixed filtered to 40MHz")) + ); + return; + default: UHD_THROW_PROP_SET_ERROR(); } } diff --git a/host/lib/usrp/dboard/db_tvrx.cpp b/host/lib/usrp/dboard/db_tvrx.cpp new file mode 100644 index 000000000..1f3c76556 --- /dev/null +++ b/host/lib/usrp/dboard/db_tvrx.cpp @@ -0,0 +1,495 @@ +// +// Copyright 2010 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/>. +// + +// No RX IO Pins Used + +// RX IO Functions + +//ADC/DAC functions: +//DAC 1: RF AGC +//DAC 2: IF AGC + +//min freq: 50e6 +//max freq: 860e6 +//gain range: [0:1dB:115dB] + +#include <uhd/utils/static.hpp> +#include <uhd/utils/assert.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/utils/warning.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/usrp/subdev_props.hpp> +#include <uhd/usrp/dboard_base.hpp> +#include <uhd/usrp/dboard_manager.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/format.hpp> +#include <boost/thread.hpp> +#include <boost/array.hpp> +#include <boost/math/special_functions/round.hpp> +#include <utility> +#include <cmath> +#include <cfloat> +#include <limits> +#include <tuner_4937di5_regs.hpp> + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + +/*********************************************************************** + * The tvrx constants + **********************************************************************/ +static const bool tvrx_debug = false; + +static const freq_range_t tvrx_freq_range(50e6, 860e6); + +static const prop_names_t tvrx_antennas = list_of("RX"); + +static const uhd::dict<std::string, freq_range_t> tvrx_freq_ranges = map_list_of + ("VHFLO", freq_range_t(50e6, 158e6)) + ("VHFHI", freq_range_t(158e6, 454e6)) + ("UHF" , freq_range_t(454e6, 860e6)) +; + +static const boost::array<double, 17> vhflo_gains_db = + {{-6.00000, -6.00000, -6.00000, -4.00000, 0.00000, + 5.00000, 10.00000, 17.40000, 26.30000, 36.00000, + 43.00000, 48.00000, 49.50000, 50.10000, 50.30000, + 50.30000, 50.30000}}; + +static const boost::array<double, 17> vhfhi_gains_db = + {{-13.3000, -13.3000, -13.3000, -1.0000, 7.7000, + 11.0000, 14.7000, 19.3000, 26.1000, 36.0000, + 42.7000, 46.0000, 47.0000, 47.8000, 48.2000, + 48.2000, 48.2000}}; + +static const boost::array<double, 17> uhf_gains_db = + {{-8.0000, -8.0000, -7.0000, 4.0000, 10.2000, + 14.5000, 17.5000, 20.0000, 24.5000, 30.8000, + 37.0000, 39.8000, 40.7000, 41.6000, 42.6000, + 43.2000, 43.8000}}; + +static const boost::array<double, 17> tvrx_if_gains_db = + {{-1.50000, -1.50000, -1.50000, -1.00000, 0.20000, + 2.10000, 4.30000, 6.40000, 9.00000, 12.00000, + 14.80000, 18.20000, 26.10000, 32.50000, 32.50000, + 32.50000, 32.50000}}; + +//gain linearization data +//this is from the datasheet and is dB vs. volts (below) +//i tried to curve fit this, but it's really just so nonlinear that you'd +//need dang near as many coefficients as to just map it like this and interp. +//these numbers are culled from the 4937DI5 datasheet and are probably totally inaccurate +//but if it's better than the old linear fit i'm happy +static const uhd::dict<std::string, boost::array<double, 17> > tvrx_rf_gains_db = map_list_of + ("VHFLO", vhflo_gains_db) + ("VHFHI", vhfhi_gains_db) + ("UHF" , uhf_gains_db) +; + +//sample voltages for the above points +static const boost::array<double, 17> tvrx_gains_volts = + {{0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0}}; + +static uhd::dict<std::string, gain_range_t> get_tvrx_gain_ranges(void) { + double rfmax = 0.0, rfmin = FLT_MAX; + BOOST_FOREACH(const std::string range, tvrx_rf_gains_db.keys()) { + double my_max = tvrx_rf_gains_db[range].back(); //we're assuming it's monotonic + double my_min = tvrx_rf_gains_db[range].front(); //if it's not this is wrong wrong wrong + if(my_max > rfmax) rfmax = my_max; + if(my_min < rfmin) rfmin = my_min; + } + + double ifmin = tvrx_if_gains_db.front(); + double ifmax = tvrx_if_gains_db.back(); + + return map_list_of + ("RF", gain_range_t(float(rfmin), float(rfmax), float((rfmax-rfmin)/4096.0))) + ("IF", gain_range_t(float(ifmin), float(ifmax), float((ifmax-ifmin)/4096.0))) + ; +} + +static const double opamp_gain = 1.22; //onboard DAC opamp gain +static const double tvrx_if_freq = 43.75e6; //IF freq of TVRX module +static const boost::uint16_t reference_divider = 640; //clock reference divider to use +static const double reference_freq = 4.0e6; + +/*********************************************************************** + * The tvrx dboard class + **********************************************************************/ +class tvrx : public rx_dboard_base{ +public: + tvrx(ctor_args_t args); + ~tvrx(void); + + void rx_get(const wax::obj &key, wax::obj &val); + void rx_set(const wax::obj &key, const wax::obj &val); + +private: + uhd::dict<std::string, float> _gains; + double _lo_freq; + tuner_4937di5_regs_t _tuner_4937di5_regs; + boost::uint8_t _tuner_4937di5_addr(void){ + return (this->get_iface()->get_special_props().mangle_i2c_addrs)? 0x61 : 0x60; //ok really? we could rename that call + }; + + void set_gain(float gain, const std::string &name); + void set_freq(double freq); + + void update_regs(void){ + byte_vector_t regs_vector(4); + + //get the register data + for(int i=0; i<4; i++){ + regs_vector[i] = _tuner_4937di5_regs.get_reg(i); + if(tvrx_debug) std::cerr << boost::format( + "tvrx: send reg 0x%02x, value 0x%04x" + ) % int(i) % int(regs_vector[i]) << std::endl; + } + + //send the data + this->get_iface()->write_i2c( + _tuner_4937di5_addr(), regs_vector + ); + } + +}; + +/*********************************************************************** + * Register the tvrx dboard + **********************************************************************/ +static dboard_base::sptr make_tvrx(dboard_base::ctor_args_t args){ + return dboard_base::sptr(new tvrx(args)); +} + +UHD_STATIC_BLOCK(reg_tvrx_dboard){ + //register the factory function for the rx dbid + dboard_manager::register_dboard(0x0040, &make_tvrx, "TVRX"); +} + +/*********************************************************************** + * Structors + **********************************************************************/ +tvrx::tvrx(ctor_args_t args) : rx_dboard_base(args){ + this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true); + + //set the gpio directions and atr controls (identically) + this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, 0x0); // All unused in atr + if (this->get_iface()->get_special_props().soft_clock_divider){ + this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x1); // GPIO0 is clock + } + else{ + this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x0); // All Inputs + } + + //send initial register settings if necessary + + //set default freq + _lo_freq = tvrx_freq_range.min + tvrx_if_freq; //init _lo_freq to a sane default + set_freq(tvrx_freq_range.min); + + //set default gains + BOOST_FOREACH(const std::string &name, get_tvrx_gain_ranges().keys()){ + set_gain(get_tvrx_gain_ranges()[name].min, name); + } +} + +tvrx::~tvrx(void){ +} + +/*! Return a string corresponding to the relevant band + * \param freq the frequency of interest + * \return a string corresponding to the band + */ + +static std::string get_band(double freq) { + BOOST_FOREACH(const std::string &band, tvrx_freq_ranges.keys()) { + if(freq >= tvrx_freq_ranges[band].min && freq <= tvrx_freq_ranges[band].max){ + if(tvrx_debug) std::cout << "Band: " << band << std::endl; + return band; + } + } + UHD_THROW_INVALID_CODE_PATH(); +} + +/*********************************************************************** + * Gain Handling + **********************************************************************/ +/*! + * Execute a linear interpolation to find the voltage corresponding to a desired gain + * \param gain the desired gain in dB + * \param db_vector the vector of dB readings + * \param volts_vector the corresponding vector of voltages db_vector was sampled at + * \return a voltage to feed the TVRX analog gain + */ + +static double gain_interp(double gain, boost::array<double, 17> db_vector, boost::array<double, 17> volts_vector) { + double volts; + gain = std::clip<double>(gain, db_vector.front(), db_vector.back()); //let's not get carried away here + + boost::uint8_t gain_step = 0; + //find which bin we're in + for(size_t i = 0; i < db_vector.size()-1; i++) { + if(gain >= db_vector[i] && gain <= db_vector[i+1]) gain_step = i; + } + + //find the current slope for linear interpolation + double slope = (volts_vector[gain_step + 1] - volts_vector[gain_step]) + / (db_vector[gain_step + 1] - db_vector[gain_step]); + + //the problem here is that for gains approaching the maximum, the voltage slope becomes infinite + //i.e., a small change in gain requires an infinite change in voltage + //to cope, we limit the slope + + if(slope == std::numeric_limits<double>::infinity()) + return volts_vector[gain_step]; + + //use the volts per dB slope to find the final interpolated voltage + volts = volts_vector[gain_step] + (slope * (gain - db_vector[gain_step])); + + if(tvrx_debug) + std::cout << "Gain interp: gain: " << gain << ", gain_step: " << int(gain_step) << ", slope: " << slope << ", volts: " << volts << std::endl; + + return volts; +} + +/*! + * Convert a requested gain for the RF gain into a DAC voltage. + * The gain passed into the function will be set to the actual value. + * \param gain the requested gain in dB + * \return dac voltage value + */ + +static float rf_gain_to_voltage(float gain, double lo_freq){ + //clip the input + gain = std::clip<float>(gain, get_tvrx_gain_ranges()["RF"].min, get_tvrx_gain_ranges()["RF"].max); + + //first we need to find out what band we're in, because gains are different across different bands + std::string band = get_band(lo_freq + tvrx_if_freq); + + //this is the voltage at the TVRX gain input + double gain_volts = gain_interp(gain, tvrx_rf_gains_db[band], tvrx_gains_volts); + //this is the voltage at the USRP DAC output + double dac_volts = gain_volts / opamp_gain; + + dac_volts = std::clip<double>(dac_volts, 0.0, 3.3); + + if (tvrx_debug) std::cerr << boost::format( + "tvrx RF AGC gain: %f dB, dac_volts: %f V" + ) % gain % dac_volts << std::endl; + + return float(dac_volts); +} + +/*! + * Convert a requested gain for the IF gain into a DAC voltage. + * The gain passed into the function will be set to the actual value. + * \param gain the requested gain in dB + * \return dac voltage value + */ + +static float if_gain_to_voltage(float gain){ + //clip the input + gain = std::clip<float>(gain, get_tvrx_gain_ranges()["IF"].min, get_tvrx_gain_ranges()["IF"].max); + + double gain_volts = gain_interp(gain, tvrx_if_gains_db, tvrx_gains_volts); + double dac_volts = gain_volts / opamp_gain; + + dac_volts = std::clip<double>(dac_volts, 0.0, 3.3); + + if (tvrx_debug) std::cerr << boost::format( + "tvrx IF AGC gain: %f dB, dac_volts: %f V" + ) % gain % dac_volts << std::endl; + + return float(dac_volts); +} + +void tvrx::set_gain(float gain, const std::string &name){ + assert_has(get_tvrx_gain_ranges().keys(), name, "tvrx gain name"); + if (name == "RF"){ + this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_B, rf_gain_to_voltage(gain, _lo_freq)); + } + else if(name == "IF"){ + this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_A, if_gain_to_voltage(gain)); + } + else UHD_THROW_INVALID_CODE_PATH(); + _gains[name] = gain; +} + +/*! + * Set the tuner to center the desired frequency at 43.75MHz + * \param freq the requested frequency + */ + +void tvrx::set_freq(double freq) { + freq = std::clip<double>(freq, tvrx_freq_range.min, tvrx_freq_range.max); + std::string prev_band = get_band(_lo_freq - tvrx_if_freq); + std::string new_band = get_band(freq); + + double target_lo_freq = freq + tvrx_if_freq; //the desired LO freq for high-side mixing + double f_ref = reference_freq / double(reference_divider); //your tuning step size + + int divisor = int((target_lo_freq + (f_ref * 4.0)) / (f_ref * 8)); //the divisor we'll use + double actual_lo_freq = (f_ref * 8 * divisor); //the LO freq we'll actually get + + if((divisor & ~0x7fff)) UHD_THROW_INVALID_CODE_PATH(); + + //now we update the registers + _tuner_4937di5_regs.db1 = (divisor >> 8) & 0xff; + _tuner_4937di5_regs.db2 = divisor & 0xff; + + if(new_band == "VHFLO") _tuner_4937di5_regs.bandsel = tuner_4937di5_regs_t::BANDSEL_VHFLO; + else if(new_band == "VHFHI") _tuner_4937di5_regs.bandsel = tuner_4937di5_regs_t::BANDSEL_VHFHI; + else if(new_band == "UHF") _tuner_4937di5_regs.bandsel = tuner_4937di5_regs_t::BANDSEL_UHF; + else UHD_THROW_INVALID_CODE_PATH(); + + _tuner_4937di5_regs.power = tuner_4937di5_regs_t::POWER_OFF; + update_regs(); + + //ok don't forget to reset RF gain here if the new band != the old band + //we do this because the gains are different for different band settings + //not FAR off, but we do this to be consistent + if(prev_band != new_band) set_gain(_gains["RF"], "RF"); + + if(tvrx_debug) + std::cout << boost::format("set_freq: target LO: %f f_ref: %f divisor: %i actual LO: %f") % target_lo_freq % f_ref % divisor % actual_lo_freq << std::endl; + + _lo_freq = actual_lo_freq; //for rx props +} + +/*********************************************************************** + * Get the alias frequency of frequency freq when sampled at fs. + * \param freq the frequency of interest + * \param fs the sample rate + * \return the alias frequency + **********************************************************************/ + +static double get_alias(double freq, double fs) { + double alias; + freq = fmod(freq, fs); + if(freq >= (fs/2)) { + alias = fs - freq; + } else { + alias = freq; + } + return alias; +} + +/*********************************************************************** + * RX Get and Set + **********************************************************************/ +void tvrx::rx_get(const wax::obj &key_, wax::obj &val){ + named_prop_t key = named_prop_t::extract(key_); + double codec_rate; + + //handle the get request conditioned on the key + switch(key.as<subdev_prop_t>()){ + case SUBDEV_PROP_NAME: + val = get_rx_id().to_pp_string(); + return; + + case SUBDEV_PROP_OTHERS: + val = prop_names_t(); //empty + return; + + case SUBDEV_PROP_GAIN: + assert_has(_gains.keys(), key.name, "tvrx gain name"); + val = _gains[key.name]; + return; + + case SUBDEV_PROP_GAIN_RANGE: + assert_has(get_tvrx_gain_ranges().keys(), key.name, "tvrx gain name"); + val = get_tvrx_gain_ranges()[key.name]; + return; + + case SUBDEV_PROP_GAIN_NAMES: + val = prop_names_t(get_tvrx_gain_ranges().keys()); + return; + + case SUBDEV_PROP_FREQ: + /* + * so here we have to do some magic. because the TVRX uses a relatively high IF, + * we have to watch the sample rate to see if the IF will be aliased + * or if it will fall within Nyquist. + */ + codec_rate = this->get_iface()->get_codec_rate(dboard_iface::UNIT_RX); + val = (_lo_freq - tvrx_if_freq) + get_alias(tvrx_if_freq, codec_rate); + return; + + case SUBDEV_PROP_FREQ_RANGE: + val = tvrx_freq_range; + return; + + case SUBDEV_PROP_ANTENNA: + val = tvrx_antennas.front(); //there's only one + return; + + case SUBDEV_PROP_ANTENNA_NAMES: + val = tvrx_antennas; + return; + + case SUBDEV_PROP_CONNECTION: + val = SUBDEV_CONN_COMPLEX_IQ; + return; + + case SUBDEV_PROP_ENABLED: + val = true; //always enabled + return; + + case SUBDEV_PROP_USE_LO_OFFSET: + val = false; + return; + + case SUBDEV_PROP_LO_LOCKED: + val = true; + return; + + case SUBDEV_PROP_BANDWIDTH: + val = 6.0e6; //30MHz low-pass, we want complex double-sided + return; + + default: UHD_THROW_PROP_GET_ERROR(); + } +} + +void tvrx::rx_set(const wax::obj &key_, const wax::obj &val){ + named_prop_t key = named_prop_t::extract(key_); + + //handle the get request conditioned on the key + switch(key.as<subdev_prop_t>()){ + case SUBDEV_PROP_GAIN: + this->set_gain(val.as<float>(), key.name); + return; + + case SUBDEV_PROP_FREQ: + this->set_freq(val.as<double>()); + return; + + case SUBDEV_PROP_ENABLED: + return; //always enabled + + case SUBDEV_PROP_BANDWIDTH: + uhd::warning::post( + str(boost::format("TVRX: No tunable bandwidth, fixed filtered to 6MHz")) + ); + return; + + default: UHD_THROW_PROP_SET_ERROR(); + } +} + diff --git a/host/lib/usrp/dboard/db_unknown.cpp b/host/lib/usrp/dboard/db_unknown.cpp index f6f4f4a61..a342471c4 100644 --- a/host/lib/usrp/dboard/db_unknown.cpp +++ b/host/lib/usrp/dboard/db_unknown.cpp @@ -19,6 +19,7 @@ #include <uhd/types/ranges.hpp> #include <uhd/utils/assert.hpp> #include <uhd/utils/static.hpp> +#include <uhd/utils/warning.hpp> #include <uhd/usrp/dboard_base.hpp> #include <uhd/usrp/dboard_manager.hpp> #include <boost/assign/list_of.hpp> @@ -122,6 +123,10 @@ void unknown_rx::rx_get(const wax::obj &key_, wax::obj &val){ val = SUBDEV_CONN_COMPLEX_IQ; return; + case SUBDEV_PROP_ENABLED: + val = true; //always enabled + return; + case SUBDEV_PROP_USE_LO_OFFSET: val = false; return; @@ -130,6 +135,10 @@ void unknown_rx::rx_get(const wax::obj &key_, wax::obj &val){ val = true; //there is no LO, so it must be true! return; + case SUBDEV_PROP_BANDWIDTH: + val = 0.0; + return; + default: UHD_THROW_PROP_GET_ERROR(); } } @@ -151,12 +160,21 @@ void unknown_rx::rx_set(const wax::obj &key_, const wax::obj &val){ case SUBDEV_PROP_FREQ: return; // it wont do you much good, but you can set it + case SUBDEV_PROP_ENABLED: + return; //always enabled + + case SUBDEV_PROP_BANDWIDTH: + uhd::warning::post( + str(boost::format("Unknown Daughterboard: No tunable bandwidth, fixed filtered to 0.0MHz")) + ); + return; + default: UHD_THROW_PROP_SET_ERROR(); } } /*********************************************************************** - * Basic and LF TX dboard + * Unknown TX dboard **********************************************************************/ unknown_tx::unknown_tx(ctor_args_t args) : tx_dboard_base(args){ /* NOP */ @@ -211,6 +229,10 @@ void unknown_tx::tx_get(const wax::obj &key_, wax::obj &val){ val = SUBDEV_CONN_COMPLEX_IQ; return; + case SUBDEV_PROP_ENABLED: + val = true; //always enabled + return; + case SUBDEV_PROP_USE_LO_OFFSET: val = false; return; @@ -219,6 +241,10 @@ void unknown_tx::tx_get(const wax::obj &key_, wax::obj &val){ val = true; //there is no LO, so it must be true! return; + case SUBDEV_PROP_BANDWIDTH: + val = 0.0; + return; + default: UHD_THROW_PROP_GET_ERROR(); } } @@ -240,6 +266,15 @@ void unknown_tx::tx_set(const wax::obj &key_, const wax::obj &val){ case SUBDEV_PROP_FREQ: return; // it wont do you much good, but you can set it + case SUBDEV_PROP_ENABLED: + return; //always enabled + + case SUBDEV_PROP_BANDWIDTH: + uhd::warning::post( + str(boost::format("Unknown Daughterboard: No tunable bandwidth, fixed filtered to 0.0MHz")) + ); + return; + default: UHD_THROW_PROP_SET_ERROR(); } } diff --git a/host/lib/usrp/dboard/db_wbx.cpp b/host/lib/usrp/dboard/db_wbx.cpp index ccd897674..647f1b975 100644 --- a/host/lib/usrp/dboard/db_wbx.cpp +++ b/host/lib/usrp/dboard/db_wbx.cpp @@ -71,6 +71,7 @@ #include <uhd/utils/assert.hpp> #include <uhd/utils/static.hpp> #include <uhd/utils/algorithm.hpp> +#include <uhd/utils/warning.hpp> #include <uhd/usrp/dboard_base.hpp> #include <uhd/usrp/dboard_manager.hpp> #include <boost/assign/list_of.hpp> @@ -154,7 +155,7 @@ static dboard_base::sptr make_wbx(dboard_base::ctor_args_t args){ } UHD_STATIC_BLOCK(reg_wbx_dboards){ - dboard_manager::register_dboard(0x0052, 0x0053, &make_wbx, "WBX"); + dboard_manager::register_dboard(0x0053, 0x0052, &make_wbx, "WBX"); } /*********************************************************************** @@ -513,6 +514,10 @@ void wbx_xcvr::rx_get(const wax::obj &key_, wax::obj &val){ val = SUBDEV_CONN_COMPLEX_IQ; return; + case SUBDEV_PROP_ENABLED: + val = true; //always enabled + return; + case SUBDEV_PROP_USE_LO_OFFSET: val = false; return; @@ -521,6 +526,10 @@ void wbx_xcvr::rx_get(const wax::obj &key_, wax::obj &val){ val = this->get_locked(dboard_iface::UNIT_RX); return; + case SUBDEV_PROP_BANDWIDTH: + val = 2*30.0e6; //20MHz low-pass, we want complex double-sided + return; + default: UHD_THROW_PROP_GET_ERROR(); } } @@ -543,6 +552,15 @@ void wbx_xcvr::rx_set(const wax::obj &key_, const wax::obj &val){ this->set_rx_ant(val.as<std::string>()); return; + case SUBDEV_PROP_ENABLED: + return; //always enabled + + case SUBDEV_PROP_BANDWIDTH: + uhd::warning::post( + str(boost::format("WBX: No tunable bandwidth, fixed filtered to 40MHz")) + ); + return; + default: UHD_THROW_PROP_SET_ERROR(); } } @@ -597,6 +615,10 @@ void wbx_xcvr::tx_get(const wax::obj &key_, wax::obj &val){ val = SUBDEV_CONN_COMPLEX_IQ; return; + case SUBDEV_PROP_ENABLED: + val = true; //always enabled + return; + case SUBDEV_PROP_USE_LO_OFFSET: val = false; return; @@ -605,6 +627,10 @@ void wbx_xcvr::tx_get(const wax::obj &key_, wax::obj &val){ val = this->get_locked(dboard_iface::UNIT_TX); return; + case SUBDEV_PROP_BANDWIDTH: + val = 2*30.0e6; //20MHz low-pass, we want complex double-sided + return; + default: UHD_THROW_PROP_GET_ERROR(); } } @@ -627,6 +653,15 @@ void wbx_xcvr::tx_set(const wax::obj &key_, const wax::obj &val){ this->set_tx_ant(val.as<std::string>()); return; + case SUBDEV_PROP_ENABLED: + return; //always enabled + + case SUBDEV_PROP_BANDWIDTH: + uhd::warning::post( + str(boost::format("WBX: No tunable bandwidth, fixed filtered to 40MHz")) + ); + return; + default: UHD_THROW_PROP_SET_ERROR(); } } diff --git a/host/lib/usrp/dboard/db_xcvr2450.cpp b/host/lib/usrp/dboard/db_xcvr2450.cpp index 798ff74a3..be0e42b92 100644 --- a/host/lib/usrp/dboard/db_xcvr2450.cpp +++ b/host/lib/usrp/dboard/db_xcvr2450.cpp @@ -51,6 +51,7 @@ #include <uhd/utils/static.hpp> #include <uhd/utils/assert.hpp> #include <uhd/utils/algorithm.hpp> +#include <uhd/utils/warning.hpp> #include <uhd/types/ranges.hpp> #include <uhd/types/dict.hpp> #include <uhd/usrp/subdev_props.hpp> @@ -72,6 +73,7 @@ using namespace boost::assign; static const bool xcvr2450_debug = false; static const freq_range_t xcvr_freq_range(2.4e9, 6.0e9); +static const freq_range_t xcvr_freq_band_seperation(2.5e9, 4.9e9); static const prop_names_t xcvr_antennas = list_of("J1")("J2"); @@ -100,6 +102,7 @@ public: private: double _lo_freq; + double _rx_bandwidth, _tx_bandwidth; uhd::dict<std::string, float> _tx_gains, _rx_gains; std::string _tx_ant, _rx_ant; int _ad9515div; @@ -110,6 +113,8 @@ private: void set_rx_ant(const std::string &ant); void set_tx_gain(float gain, const std::string &name); void set_rx_gain(float gain, const std::string &name); + void set_rx_bandwidth(double bandwidth); + void set_tx_bandwidth(double bandwidth); void update_atr(void); void spi_reset(void); @@ -176,6 +181,9 @@ xcvr2450::xcvr2450(ctor_args_t args) : xcvr_dboard_base(args){ spi_reset(); //prepare the spi + _rx_bandwidth = 9.5e6; + _tx_bandwidth = 12.0e6; + //setup the misc max2829 registers _max2829_regs.mimo_select = max2829_regs_t::MIMO_SELECT_MIMO; _max2829_regs.band_sel_mimo = max2829_regs_t::BAND_SEL_MIMO_MIMO; @@ -183,7 +191,7 @@ xcvr2450::xcvr2450(ctor_args_t args) : xcvr_dboard_base(args){ _max2829_regs.rssi_high_bw = max2829_regs_t::RSSI_HIGH_BW_6MHZ; _max2829_regs.tx_lpf_coarse_adj = max2829_regs_t::TX_LPF_COARSE_ADJ_12MHZ; _max2829_regs.rx_lpf_coarse_adj = max2829_regs_t::RX_LPF_COARSE_ADJ_9_5MHZ; - _max2829_regs.rx_lpf_fine_adj = max2829_regs_t::RX_LPF_FINE_ADJ_95; + _max2829_regs.rx_lpf_fine_adj = max2829_regs_t::RX_LPF_FINE_ADJ_100; _max2829_regs.rx_vga_gain_spi = max2829_regs_t::RX_VGA_GAIN_SPI_SPI; _max2829_regs.rssi_output_range = max2829_regs_t::RSSI_OUTPUT_RANGE_HIGH; _max2829_regs.rssi_op_mode = max2829_regs_t::RSSI_OP_MODE_ENABLED; @@ -244,15 +252,24 @@ void xcvr2450::update_atr(void){ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, POWER_UP_RXIO | RX_DIS_RXIO); this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, POWER_UP_RXIO | RX_ENB_RXIO); this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, POWER_UP_RXIO | RX_DIS_RXIO); - this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, POWER_UP_RXIO | RX_ENB_RXIO); + this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, POWER_UP_RXIO | RX_DIS_RXIO); } /*********************************************************************** * Tuning **********************************************************************/ void xcvr2450::set_lo_freq(double target_freq){ + //clip for highband and lowband + if((target_freq > xcvr_freq_band_seperation.min) and (target_freq < xcvr_freq_band_seperation.max)){ + if(target_freq - xcvr_freq_band_seperation.min < xcvr_freq_band_seperation.max - target_freq){ + target_freq = xcvr_freq_band_seperation.min; + }else{ + target_freq = xcvr_freq_band_seperation.max; + } + } + + //clip for max and min target_freq = std::clip(target_freq, xcvr_freq_range.min, xcvr_freq_range.max); - //TODO: clip for highband and lowband //variables used in the calculation below double scaler = xcvr2450::is_highband(target_freq)? (4.0/5.0) : (4.0/3.0); @@ -434,6 +451,114 @@ void xcvr2450::set_rx_gain(float gain, const std::string &name){ _rx_gains[name] = gain; } + +/*********************************************************************** + * Bandwidth Handling + **********************************************************************/ +static max2829_regs_t::tx_lpf_coarse_adj_t bandwidth_to_tx_lpf_coarse_reg(double &bandwidth){ + int reg = std::clip(boost::math::iround((bandwidth-6.0e6)/6.0e6), 1, 3); + + switch(reg){ + case 1: // bandwidth < 15MHz + bandwidth = 12e6; + return max2829_regs_t::TX_LPF_COARSE_ADJ_12MHZ; + case 2: // 15MHz < bandwidth < 21MHz + bandwidth = 18e6; + return max2829_regs_t::TX_LPF_COARSE_ADJ_18MHZ; + case 3: // bandwidth > 21MHz + bandwidth = 24e6; + return max2829_regs_t::TX_LPF_COARSE_ADJ_24MHZ; + } + UHD_THROW_INVALID_CODE_PATH(); +} + +static max2829_regs_t::rx_lpf_fine_adj_t bandwidth_to_rx_lpf_fine_reg(double &bandwidth, double requested_bandwidth){ + int reg = std::clip(boost::math::iround((requested_bandwidth/bandwidth)/0.05), 18, 22); + + switch(reg){ + case 18: // requested_bandwidth < 92.5% + bandwidth = 0.9 * bandwidth; + return max2829_regs_t::RX_LPF_FINE_ADJ_90; + case 19: // 92.5% < requested_bandwidth < 97.5% + bandwidth = 0.95 * bandwidth; + return max2829_regs_t::RX_LPF_FINE_ADJ_95; + case 20: // 97.5% < requested_bandwidth < 102.5% + bandwidth = 1.0 * bandwidth; + return max2829_regs_t::RX_LPF_FINE_ADJ_100; + case 21: // 102.5% < requested_bandwidth < 107.5% + bandwidth = 1.05 * bandwidth; + return max2829_regs_t::RX_LPF_FINE_ADJ_105; + case 22: // 107.5% < requested_bandwidth + bandwidth = 1.1 * bandwidth; + return max2829_regs_t::RX_LPF_FINE_ADJ_110; + } + UHD_THROW_INVALID_CODE_PATH(); +} + +static max2829_regs_t::rx_lpf_coarse_adj_t bandwidth_to_rx_lpf_coarse_reg(double &bandwidth){ + int reg = std::clip(boost::math::iround((bandwidth-7.0e6)/1.0e6), 0, 11); + + switch(reg){ + case 0: // bandwidth < 7.5MHz + case 1: // 7.5MHz < bandwidth < 8.5MHz + bandwidth = 7.5e6; + return max2829_regs_t::RX_LPF_COARSE_ADJ_7_5MHZ; + case 2: // 8.5MHz < bandwidth < 9.5MHz + case 3: // 9.5MHz < bandwidth < 10.5MHz + case 4: // 10.5MHz < bandwidth < 11.5MHz + bandwidth = 9.5e6; + return max2829_regs_t::RX_LPF_COARSE_ADJ_9_5MHZ; + case 5: // 11.5MHz < bandwidth < 12.5MHz + case 6: // 12.5MHz < bandwidth < 13.5MHz + case 7: // 13.5MHz < bandwidth < 14.5MHz + case 8: // 14.5MHz < bandwidth < 15.5MHz + bandwidth = 14e6; + return max2829_regs_t::RX_LPF_COARSE_ADJ_14MHZ; + case 9: // 15.5MHz < bandwidth < 16.5MHz + case 10: // 16.5MHz < bandwidth < 17.5MHz + case 11: // 17.5MHz < bandwidth + bandwidth = 18e6; + return max2829_regs_t::RX_LPF_COARSE_ADJ_18MHZ; + } + UHD_THROW_INVALID_CODE_PATH(); +} + +void xcvr2450::set_rx_bandwidth(double bandwidth){ + double requested_bandwidth = bandwidth; + + //compute coarse low pass cutoff frequency setting + _max2829_regs.rx_lpf_coarse_adj = bandwidth_to_rx_lpf_coarse_reg(bandwidth); + + //compute fine low pass cutoff frequency setting + _max2829_regs.rx_lpf_fine_adj = bandwidth_to_rx_lpf_fine_reg(bandwidth, requested_bandwidth); + + //shadow bandwidth setting + _rx_bandwidth = bandwidth; + + //update register + send_reg(0x7); + + if (xcvr2450_debug) std::cerr << boost::format( + "XCVR2450 RX Bandwidth (lp_fc): %f Hz, coarse reg: %d, fine reg: %d" + ) % _rx_bandwidth % (int(_max2829_regs.rx_lpf_coarse_adj)) % (int(_max2829_regs.rx_lpf_fine_adj)) << std::endl; +} + +void xcvr2450::set_tx_bandwidth(double bandwidth){ + //compute coarse low pass cutoff frequency setting + _max2829_regs.tx_lpf_coarse_adj = bandwidth_to_tx_lpf_coarse_reg(bandwidth); + + //shadow bandwidth setting + _tx_bandwidth = bandwidth; + + //update register + send_reg(0x7); + + if (xcvr2450_debug) std::cerr << boost::format( + "XCVR2450 TX Bandwidth (lp_fc): %f Hz, coarse reg: %d" + ) % _tx_bandwidth % (int(_max2829_regs.tx_lpf_coarse_adj)) << std::endl; +} + + /*********************************************************************** * RX Get and Set **********************************************************************/ @@ -484,6 +609,10 @@ void xcvr2450::rx_get(const wax::obj &key_, wax::obj &val){ val = SUBDEV_CONN_COMPLEX_IQ; return; + case SUBDEV_PROP_ENABLED: + val = true; //always enabled + return; + case SUBDEV_PROP_USE_LO_OFFSET: val = false; return; @@ -496,6 +625,10 @@ void xcvr2450::rx_get(const wax::obj &key_, wax::obj &val){ val = this->get_rssi(); return; + case SUBDEV_PROP_BANDWIDTH: + val = 2*_rx_bandwidth; //_tx_bandwidth is low-pass, we want complex double-sided + return; + default: UHD_THROW_PROP_GET_ERROR(); } } @@ -518,6 +651,13 @@ void xcvr2450::rx_set(const wax::obj &key_, const wax::obj &val){ this->set_rx_ant(val.as<std::string>()); return; + case SUBDEV_PROP_BANDWIDTH: + this->set_rx_bandwidth(val.as<double>()/2.0); //complex double-sided, we want low-pass + return; + + case SUBDEV_PROP_ENABLED: + return; //always enabled + default: UHD_THROW_PROP_SET_ERROR(); } } @@ -572,6 +712,10 @@ void xcvr2450::tx_get(const wax::obj &key_, wax::obj &val){ val = SUBDEV_CONN_COMPLEX_QI; return; + case SUBDEV_PROP_ENABLED: + val = true; //always enabled + return; + case SUBDEV_PROP_USE_LO_OFFSET: val = false; return; @@ -580,6 +724,10 @@ void xcvr2450::tx_get(const wax::obj &key_, wax::obj &val){ val = this->get_locked(); return; + case SUBDEV_PROP_BANDWIDTH: + val = 2*_tx_bandwidth; //_tx_bandwidth is low-pass, we want complex double-sided + return; + default: UHD_THROW_PROP_GET_ERROR(); } } @@ -598,10 +746,17 @@ void xcvr2450::tx_set(const wax::obj &key_, const wax::obj &val){ this->set_tx_gain(val.as<float>(), key.name); return; + case SUBDEV_PROP_BANDWIDTH: + this->set_tx_bandwidth(val.as<double>()/2.0); //complex double-sided, we want low-pass + return; + case SUBDEV_PROP_ANTENNA: this->set_tx_ant(val.as<std::string>()); return; + case SUBDEV_PROP_ENABLED: + return; //always enabled + default: UHD_THROW_PROP_SET_ERROR(); } } |