1 //
2 // Copyright 2011-2014 Ettus Research LLC
3 // Copyright 2018 Ettus Research, a National Instruments Company
4 //
5 // SPDX-License-Identifier: GPL-3.0-or-later
6 //
7 
8 #include "db_wbx_common.hpp"
9 #include <uhd/types/dict.hpp>
10 #include <uhd/types/ranges.hpp>
11 #include <uhd/types/sensors.hpp>
12 #include <uhd/utils/algorithm.hpp>
13 #include <uhd/utils/assert_has.hpp>
14 #include <uhd/utils/log.hpp>
15 #include <functional>
16 
17 using namespace uhd;
18 using namespace uhd::usrp;
19 using namespace boost::assign;
20 
21 
22 /***********************************************************************
23  * Gain-related functions
24  **********************************************************************/
rx_pga0_gain_to_iobits(double & gain)25 static int rx_pga0_gain_to_iobits(double& gain)
26 {
27     // clip the input
28     gain = wbx_rx_gain_ranges["PGA0"].clip(gain);
29 
30     // convert to attenuation
31     double attn = wbx_rx_gain_ranges["PGA0"].stop() - gain;
32 
33     // calculate the attenuation
34     int attn_code = boost::math::iround(attn * 2);
35     int iobits    = ((~attn_code) << RX_ATTN_SHIFT) & RX_ATTN_MASK;
36 
37     UHD_LOGGER_TRACE("WBX")
38         << boost::format("WBX RX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x")
39                % attn % attn_code % (iobits & RX_ATTN_MASK) % RX_ATTN_MASK;
40 
41     // the actual gain setting
42     gain = wbx_rx_gain_ranges["PGA0"].stop() - double(attn_code) / 2;
43 
44     return iobits;
45 }
46 
47 
48 /***********************************************************************
49  * WBX Common Implementation
50  **********************************************************************/
wbx_base(ctor_args_t args)51 wbx_base::wbx_base(ctor_args_t args) : xcvr_dboard_base(args)
52 {
53     // enable the clocks that we need
54     this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true);
55     this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true);
56 
57     ////////////////////////////////////////////////////////////////////
58     // Register RX and TX properties
59     ////////////////////////////////////////////////////////////////////
60     uint16_t rx_id = this->get_rx_id().to_uint16();
61 
62     this->get_rx_subtree()->create<device_addr_t>("tune_args").set(device_addr_t());
63     this->get_rx_subtree()
64         ->create<sensor_value_t>("sensors/lo_locked")
65         .set_publisher(std::bind(&wbx_base::get_locked, this, dboard_iface::UNIT_RX));
66     for (const std::string& name : wbx_rx_gain_ranges.keys()) {
67         this->get_rx_subtree()
68             ->create<double>("gains/" + name + "/value")
69             .set_coercer(
70                 std::bind(&wbx_base::set_rx_gain, this, std::placeholders::_1, name))
71             .set(wbx_rx_gain_ranges[name].start());
72         this->get_rx_subtree()
73             ->create<meta_range_t>("gains/" + name + "/range")
74             .set(wbx_rx_gain_ranges[name]);
75     }
76     this->get_rx_subtree()->create<std::string>("connection").set("IQ");
77     this->get_rx_subtree()
78         ->create<bool>("enabled")
79         .add_coerced_subscriber(
80             std::bind(&wbx_base::set_rx_enabled, this, std::placeholders::_1))
81         .set(true); // start enabled
82     this->get_rx_subtree()->create<bool>("use_lo_offset").set(false);
83 
84     // Value of bw low-pass dependent on board, we want complex double-sided
85     double bw = (rx_id != 0x0081) ? 20.0e6 : 60.0e6;
86     this->get_rx_subtree()->create<double>("bandwidth/value").set(2 * bw);
87     this->get_rx_subtree()
88         ->create<meta_range_t>("bandwidth/range")
89         .set(freq_range_t(2 * bw, 2 * bw));
90     this->get_tx_subtree()->create<double>("bandwidth/value").set(2 * bw);
91     this->get_tx_subtree()
92         ->create<meta_range_t>("bandwidth/range")
93         .set(freq_range_t(2 * bw, 2 * bw));
94 
95     this->get_tx_subtree()->create<device_addr_t>("tune_args").set(device_addr_t());
96     this->get_tx_subtree()
97         ->create<sensor_value_t>("sensors/lo_locked")
98         .set_publisher(std::bind(&wbx_base::get_locked, this, dboard_iface::UNIT_TX));
99     this->get_tx_subtree()->create<std::string>("connection").set("IQ");
100     this->get_tx_subtree()->create<bool>("use_lo_offset").set(false);
101 
102     // instantiate subclass foo
103     switch (rx_id) {
104         case 0x0053:
105             db_actual = wbx_versionx_sptr(new wbx_version2(this));
106             return;
107         case 0x0057:
108             db_actual = wbx_versionx_sptr(new wbx_version3(this));
109             return;
110         case 0x0063:
111             db_actual = wbx_versionx_sptr(new wbx_version4(this));
112             return;
113         case 0x0081:
114             db_actual = wbx_versionx_sptr(new wbx_version4(this));
115             return;
116         default:
117             /* We didn't recognize the version of the board... */
118             UHD_THROW_INVALID_CODE_PATH();
119     }
120 }
121 
122 
~wbx_base(void)123 wbx_base::~wbx_base(void)
124 {
125     /* NOP */
126 }
127 
128 /***********************************************************************
129  * Enables
130  **********************************************************************/
set_rx_enabled(bool enb)131 void wbx_base::set_rx_enabled(bool enb)
132 {
133     this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX,
134         (enb) ? RX_POWER_UP : RX_POWER_DOWN,
135         RX_POWER_UP | RX_POWER_DOWN);
136 }
137 
138 /***********************************************************************
139  * Gain Handling
140  **********************************************************************/
set_rx_gain(double gain,const std::string & name)141 double wbx_base::set_rx_gain(double gain, const std::string& name)
142 {
143     assert_has(wbx_rx_gain_ranges.keys(), name, "wbx rx gain name");
144     if (name == "PGA0") {
145         uint16_t io_bits = rx_pga0_gain_to_iobits(gain);
146         _rx_gains[name]  = gain;
147 
148         // write the new gain to rx gpio outputs
149         this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, io_bits, RX_ATTN_MASK);
150     } else
151         UHD_THROW_INVALID_CODE_PATH();
152     return _rx_gains[name]; // returned shadowed
153 }
154 
155 /***********************************************************************
156  * Tuning
157  **********************************************************************/
get_locked(dboard_iface::unit_t unit)158 sensor_value_t wbx_base::get_locked(dboard_iface::unit_t unit)
159 {
160     const bool locked = (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0;
161     return sensor_value_t("LO", locked, "locked", "unlocked");
162 }
163 
write_lo_regs(dboard_iface::unit_t unit,const std::vector<uint32_t> & regs)164 void wbx_base::wbx_versionx::write_lo_regs(
165     dboard_iface::unit_t unit, const std::vector<uint32_t>& regs)
166 {
167     for (uint32_t reg : regs) {
168         self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, reg, 32);
169     }
170 }
171