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