1 //
2 // Copyright 2010-2016 Ettus Research LLC
3 // Copyright 2018 Ettus Research, a National Instruments Company
4 // Copyright 2019 Ettus Research, a National Instruments Brand
5 //
6 // SPDX-License-Identifier: GPL-3.0-or-later
7 //
8 
9 #include <uhd/convert.hpp>
10 #include <uhd/exception.hpp>
11 #include <uhd/property_tree.hpp>
12 #include <uhd/types/eeprom.hpp>
13 #include <uhd/usrp/dboard_eeprom.hpp>
14 #include <uhd/usrp/dboard_id.hpp>
15 #include <uhd/usrp/gpio_defs.hpp>
16 #include <uhd/usrp/mboard_eeprom.hpp>
17 #include <uhd/usrp/multi_usrp.hpp>
18 #include <uhd/utils/gain_group.hpp>
19 #include <uhd/utils/log.hpp>
20 #include <uhd/utils/math.hpp>
21 #include <uhd/utils/soft_register.hpp>
22 #include <uhdlib/rfnoc/rfnoc_device.hpp>
23 #include <uhdlib/usrp/gpio_defs.hpp>
24 #include <boost/algorithm/string.hpp>
25 #include <boost/assign/list_of.hpp>
26 #include <boost/format.hpp>
27 #include <algorithm>
28 #include <bitset>
29 #include <chrono>
30 #include <cmath>
31 #include <functional>
32 #include <memory>
33 #include <thread>
34 
35 namespace uhd { namespace rfnoc {
36 
37 //! Factory function for RFNoC devices specifically
38 uhd::usrp::multi_usrp::sptr make_rfnoc_device(
39     uhd::rfnoc::detail::rfnoc_device::sptr rfnoc_device,
40     const uhd::device_addr_t& dev_addr);
41 
42 }} /* namespace uhd::rfnoc */
43 
44 using namespace uhd;
45 using namespace uhd::usrp;
46 
47 const size_t multi_usrp::ALL_MBOARDS    = size_t(~0);
48 const size_t multi_usrp::ALL_CHANS      = size_t(~0);
49 const std::string multi_usrp::ALL_GAINS = "";
50 const std::string multi_usrp::ALL_LOS   = "all";
51 
string_vector_to_string(std::vector<std::string> values,std::string delimiter=std::string (" "))52 UHD_INLINE std::string string_vector_to_string(
53     std::vector<std::string> values, std::string delimiter = std::string(" "))
54 {
55     std::string out = "";
56     for (std::vector<std::string>::iterator iter = values.begin(); iter != values.end();
57          iter++) {
58         out += (iter != values.begin() ? delimiter : "") + *iter;
59     }
60     return out;
61 }
62 
63 #define THROW_GAIN_NAME_ERROR(name, chan, dir)                                   \
64     throw uhd::exception::runtime_error(                                         \
65         (boost::format(                                                          \
66              "%s: gain \"%s\" not found for channel %d.\nAvailable gains: %s\n") \
67             % __FUNCTION__ % name % chan                                         \
68             % string_vector_to_string(get_##dir##_gain_names(chan)))             \
69             .str());
70 
71 /***********************************************************************
72  * Helper methods
73  **********************************************************************/
do_samp_rate_warning_message(double target_rate,double actual_rate,const std::string & xx)74 static void do_samp_rate_warning_message(
75     double target_rate, double actual_rate, const std::string& xx)
76 {
77     static const double max_allowed_error = 1.0; // Sps
78     if (std::abs(target_rate - actual_rate) > max_allowed_error) {
79         UHD_LOGGER_WARNING("MULTI_USRP")
80             << boost::format(
81                    "The hardware does not support the requested %s sample rate:\n"
82                    "Target sample rate: %f MSps\n"
83                    "Actual sample rate: %f MSps\n")
84                    % xx % (target_rate / 1e6) % (actual_rate / 1e6);
85     }
86 }
87 
88 /*static void do_tune_freq_results_message(
89     const tune_request_t &tune_req,
90     const tune_result_t &tune_result,
91     double actual_freq,
92     const std::string &xx
93 ){
94     const double target_freq = tune_req.target_freq;
95     const double clipped_target_freq = tune_result.clipped_rf_freq;
96     const double target_rf_freq = tune_result.target_rf_freq;
97     const double actual_rf_freq = tune_result.actual_rf_freq;
98     const double target_dsp_freq = tune_result.target_dsp_freq;
99     const double actual_dsp_freq = tune_result.actual_dsp_freq;
100 
101     if (tune_req.rf_freq_policy == tune_request_t::POLICY_MANUAL) return;
102     if (tune_req.dsp_freq_policy == tune_request_t::POLICY_MANUAL) return;
103 
104     bool requested_freq_success = uhd::math::frequencies_are_equal(target_freq,
105 clipped_target_freq); bool target_freq_success =
106 uhd::math::frequencies_are_equal(clipped_target_freq, actual_freq); bool
107 rf_lo_tune_success = uhd::math::frequencies_are_equal(target_rf_freq, actual_rf_freq);
108     bool dsp_tune_success = uhd::math::frequencies_are_equal(target_dsp_freq,
109 actual_dsp_freq);
110 
111     if(requested_freq_success and target_freq_success and rf_lo_tune_success
112             and dsp_tune_success) {
113         UHD_LOGGER_INFO("MULTI_USRP") << boost::format(
114                 "Successfully tuned to %f MHz\n\n")
115                 % (actual_freq / 1e6);
116     } else {
117         boost::format base_message ("Tune Request: %f MHz\n");
118         base_message % (target_freq / 1e6);
119         std::string results_string = base_message.str();
120 
121         if(requested_freq_success and (not rf_lo_tune_success)) {
122             boost::format rf_lo_message(
123                 "  The RF LO does not support the requested frequency:\n"
124                 "    Requested LO Frequency: %f MHz\n"
125                 "    RF LO Result: %f MHz\n"
126                 "  Attempted to use the DSP to reach the requested frequency:\n"
127                 "    Desired DSP Frequency: %f MHz\n"
128                 "    DSP Result: %f MHz\n"
129                 "  Successfully tuned to %f MHz\n\n");
130             rf_lo_message % (target_rf_freq / 1e6) % (actual_rf_freq / 1e6)
131                 % (target_dsp_freq / 1e6) % (actual_dsp_freq / 1e6)
132                 % (actual_freq / 1e6);
133 
134             results_string += rf_lo_message.str();
135 
136             UHD_LOGGER_INFO("MULTI_USRP") << results_string;
137 
138             return;
139         }
140 
141         if(not requested_freq_success) {
142             boost::format failure_message(
143                 "  The requested %s frequency is outside of the system range, and has been
144 clipped:\n" "    Target Frequency: %f MHz\n" "    Clipped Target Frequency: %f MHz\n");
145             failure_message % xx % (target_freq / 1e6) % (clipped_target_freq / 1e6);
146 
147             results_string += failure_message.str();
148         }
149 
150         if(not rf_lo_tune_success) {
151             boost::format rf_lo_message(
152                 "  The RF LO does not support the requested frequency:\n"
153                 "    Requested LO Frequency: %f MHz\n"
154                 "    RF LO Result: %f MHz\n"
155                 "  Attempted to use the DSP to reach the requested frequency:\n"
156                 "    Desired DSP Frequency: %f MHz\n"
157                 "    DSP Result: %f MHz\n");
158             rf_lo_message % (target_rf_freq / 1e6) % (actual_rf_freq / 1e6)
159                 % (target_dsp_freq / 1e6) % (actual_dsp_freq / 1e6);
160 
161             results_string += rf_lo_message.str();
162 
163         } else if(not dsp_tune_success) {
164             boost::format dsp_message(
165                 "  The DSP does not support the requested frequency:\n"
166                 "    Requested DSP Frequency: %f MHz\n"
167                 "    DSP Result: %f MHz\n");
168             dsp_message % (target_dsp_freq / 1e6) % (actual_dsp_freq / 1e6);
169 
170             results_string += dsp_message.str();
171         }
172 
173         if(target_freq_success) {
174             boost::format success_message(
175                 "  Successfully tuned to %f MHz\n\n");
176             success_message % (actual_freq / 1e6);
177 
178             results_string += success_message.str();
179         } else {
180             boost::format failure_message(
181                 "  Failed to tune to target frequency\n"
182                 "    Target Frequency: %f MHz\n"
183                 "    Actual Frequency: %f MHz\n\n");
184             failure_message % (clipped_target_freq / 1e6) % (actual_freq / 1e6);
185 
186             results_string += failure_message.str();
187         }
188 
189         UHD_LOGGER_WARNING("MULTI_USRP") << results_string ;
190     }
191 }*/
192 
193 /*! The CORDIC can be used to shift the baseband below / past the tunable
194  * limits of the actual RF front-end. The baseband filter, located on the
195  * daughterboard, however, limits the useful instantaneous bandwidth. We
196  * allow the user to tune to the edge of the filter, where the roll-off
197  * begins.  This prevents the user from tuning past the point where less
198  * than half of the spectrum would be useful. */
make_overall_tune_range(const meta_range_t & fe_range,const meta_range_t & dsp_range,const double bw)199 static meta_range_t make_overall_tune_range(
200     const meta_range_t& fe_range, const meta_range_t& dsp_range, const double bw)
201 {
202     meta_range_t range;
203     for (const range_t& sub_range : fe_range) {
204         range.push_back(range_t(sub_range.start() + std::max(dsp_range.start(), -bw / 2),
205             sub_range.stop() + std::min(dsp_range.stop(), bw / 2),
206             dsp_range.step()));
207     }
208     return range;
209 }
210 
211 
212 /***********************************************************************
213  * Gain helper functions
214  **********************************************************************/
get_gain_value(property_tree::sptr subtree)215 static double get_gain_value(property_tree::sptr subtree)
216 {
217     return subtree->access<double>("value").get();
218 }
219 
set_gain_value(property_tree::sptr subtree,const double gain)220 static void set_gain_value(property_tree::sptr subtree, const double gain)
221 {
222     subtree->access<double>("value").set(gain);
223 }
224 
get_gain_range(property_tree::sptr subtree)225 static meta_range_t get_gain_range(property_tree::sptr subtree)
226 {
227     return subtree->access<meta_range_t>("range").get();
228 }
229 
make_gain_fcns_from_subtree(property_tree::sptr subtree)230 static gain_fcns_t make_gain_fcns_from_subtree(property_tree::sptr subtree)
231 {
232     gain_fcns_t gain_fcns;
233     gain_fcns.get_range = std::bind(&get_gain_range, subtree);
234     gain_fcns.get_value = std::bind(&get_gain_value, subtree);
235     gain_fcns.set_value = std::bind(&set_gain_value, subtree, std::placeholders::_1);
236     return gain_fcns;
237 }
238 
239 /***********************************************************************
240  * Tune Helper Functions
241  **********************************************************************/
242 static const double RX_SIGN = +1.0;
243 static const double TX_SIGN = -1.0;
244 
tune_xx_subdev_and_dsp(const double xx_sign,property_tree::sptr dsp_subtree,property_tree::sptr rf_fe_subtree,const tune_request_t & tune_request)245 static tune_result_t tune_xx_subdev_and_dsp(const double xx_sign,
246     property_tree::sptr dsp_subtree,
247     property_tree::sptr rf_fe_subtree,
248     const tune_request_t& tune_request)
249 {
250     //------------------------------------------------------------------
251     //-- calculate the tunable frequency ranges of the system
252     //------------------------------------------------------------------
253     freq_range_t tune_range =
254         make_overall_tune_range(rf_fe_subtree->access<meta_range_t>("freq/range").get(),
255             dsp_subtree->access<meta_range_t>("freq/range").get(),
256             rf_fe_subtree->access<double>("bandwidth/value").get());
257 
258     freq_range_t dsp_range = dsp_subtree->access<meta_range_t>("freq/range").get();
259     freq_range_t rf_range  = rf_fe_subtree->access<meta_range_t>("freq/range").get();
260 
261     double clipped_requested_freq = tune_range.clip(tune_request.target_freq);
262 
263     //------------------------------------------------------------------
264     //-- If the RF FE requires an LO offset, build it into the tune request
265     //------------------------------------------------------------------
266 
267     /*! The automatically calculated LO offset is only used if the
268      * 'use_lo_offset' field in the daughterboard property tree is set to TRUE,
269      * and the tune policy is set to AUTO. To use an LO offset normally, the
270      * user should specify the MANUAL tune policy and lo_offset as part of the
271      * tune_request. This lo_offset is based on the requirements of the FE, and
272      * does not reflect a user-requested lo_offset, which is handled later. */
273     double lo_offset = 0.0;
274     if (rf_fe_subtree->exists("use_lo_offset")
275         and rf_fe_subtree->access<bool>("use_lo_offset").get()) {
276         // If the frontend has lo_offset value and range properties, trust it
277         // for lo_offset
278         if (rf_fe_subtree->exists("lo_offset/value")) {
279             lo_offset = rf_fe_subtree->access<double>("lo_offset/value").get();
280         }
281 
282         // If the local oscillator will be in the passband, use an offset.
283         // But constrain the LO offset by the width of the filter bandwidth.
284         const double rate = dsp_subtree->access<double>("rate/value").get();
285         const double bw   = rf_fe_subtree->access<double>("bandwidth/value").get();
286         if (bw > rate)
287             lo_offset = std::min((bw - rate) / 2, rate / 2);
288     }
289 
290     //------------------------------------------------------------------
291     //-- poke the tune request args into the dboard
292     //------------------------------------------------------------------
293     if (rf_fe_subtree->exists("tune_args")) {
294         rf_fe_subtree->access<device_addr_t>("tune_args").set(tune_request.args);
295     }
296 
297     //------------------------------------------------------------------
298     //-- set the RF frequency depending upon the policy
299     //------------------------------------------------------------------
300     double target_rf_freq = 0.0;
301 
302     switch (tune_request.rf_freq_policy) {
303         case tune_request_t::POLICY_AUTO:
304             target_rf_freq = clipped_requested_freq + lo_offset;
305             break;
306 
307         case tune_request_t::POLICY_MANUAL:
308             // If the rf_fe understands lo_offset settings, infer the desired
309             // lo_offset and set it. Side effect: In TVRX2 for example, after
310             // setting the lo_offset (if_freq) with a POLICY_MANUAL, there is no
311             // way for the user to automatically get back to default if_freq
312             // without deconstruct/reconstruct the rf_fe objects.
313             if (rf_fe_subtree->exists("lo_offset/value")) {
314                 rf_fe_subtree->access<double>("lo_offset/value")
315                     .set(tune_request.rf_freq - tune_request.target_freq);
316             }
317 
318             target_rf_freq = rf_range.clip(tune_request.rf_freq);
319             break;
320 
321         case tune_request_t::POLICY_NONE:
322             break; // does not set
323     }
324 
325     //------------------------------------------------------------------
326     //-- Tune the RF frontend
327     //------------------------------------------------------------------
328     if (tune_request.rf_freq_policy != tune_request_t::POLICY_NONE) {
329         rf_fe_subtree->access<double>("freq/value").set(target_rf_freq);
330     }
331     const double actual_rf_freq = rf_fe_subtree->access<double>("freq/value").get();
332 
333     //------------------------------------------------------------------
334     //-- Set the DSP frequency depending upon the DSP frequency policy.
335     //------------------------------------------------------------------
336     double target_dsp_freq = 0.0;
337     switch (tune_request.dsp_freq_policy) {
338         case tune_request_t::POLICY_AUTO:
339             /* If we are using the AUTO tuning policy, then we prevent the
340              * CORDIC from spinning us outside of the range of the baseband
341              * filter, regardless of what the user requested. This could happen
342              * if the user requested a center frequency so far outside of the
343              * tunable range of the FE that the CORDIC would spin outside the
344              * filtered baseband. */
345             target_dsp_freq = actual_rf_freq - clipped_requested_freq;
346 
347             // invert the sign on the dsp freq for transmit (spinning up vs down)
348             target_dsp_freq *= xx_sign;
349 
350             break;
351 
352         case tune_request_t::POLICY_MANUAL:
353             /* If the user has specified a manual tune policy, we will allow
354              * tuning outside of the baseband filter, but will still clip the
355              * target DSP frequency to within the bounds of the CORDIC to
356              * prevent undefined behavior (likely an overflow). */
357             target_dsp_freq = dsp_range.clip(tune_request.dsp_freq);
358             break;
359 
360         case tune_request_t::POLICY_NONE:
361             break; // does not set
362     }
363 
364     //------------------------------------------------------------------
365     //-- Tune the DSP
366     //------------------------------------------------------------------
367     if (tune_request.dsp_freq_policy != tune_request_t::POLICY_NONE) {
368         dsp_subtree->access<double>("freq/value").set(target_dsp_freq);
369     }
370     const double actual_dsp_freq = dsp_subtree->access<double>("freq/value").get();
371 
372     //------------------------------------------------------------------
373     //-- Load and return the tune result
374     //------------------------------------------------------------------
375     tune_result_t tune_result;
376     tune_result.clipped_rf_freq = clipped_requested_freq;
377     tune_result.target_rf_freq  = target_rf_freq;
378     tune_result.actual_rf_freq  = actual_rf_freq;
379     tune_result.target_dsp_freq = target_dsp_freq;
380     tune_result.actual_dsp_freq = actual_dsp_freq;
381     return tune_result;
382 }
383 
derive_freq_from_xx_subdev_and_dsp(const double xx_sign,property_tree::sptr dsp_subtree,property_tree::sptr rf_fe_subtree)384 static double derive_freq_from_xx_subdev_and_dsp(const double xx_sign,
385     property_tree::sptr dsp_subtree,
386     property_tree::sptr rf_fe_subtree)
387 {
388     // extract actual dsp and IF frequencies
389     const double actual_rf_freq  = rf_fe_subtree->access<double>("freq/value").get();
390     const double actual_dsp_freq = dsp_subtree->access<double>("freq/value").get();
391 
392     // invert the sign on the dsp freq for transmit
393     return actual_rf_freq - actual_dsp_freq * xx_sign;
394 }
395 
396 /***********************************************************************
397  * Multi USRP Implementation
398  **********************************************************************/
399 class multi_usrp_impl : public multi_usrp
400 {
401 public:
multi_usrp_impl(device::sptr dev)402     multi_usrp_impl(device::sptr dev) : _dev(dev)
403     {
404         _tree = _dev->get_tree();
405     }
406 
get_device(void)407     device::sptr get_device(void)
408     {
409         return _dev;
410     }
411 
get_tree() const412     uhd::property_tree::sptr get_tree() const
413     {
414         return _tree;
415     }
416 
get_usrp_rx_info(size_t chan)417     dict<std::string, std::string> get_usrp_rx_info(size_t chan)
418     {
419         mboard_chan_pair mcp = rx_chan_to_mcp(chan);
420         dict<std::string, std::string> usrp_info;
421         const auto mb_eeprom =
422             _tree->access<mboard_eeprom_t>(mb_root(mcp.mboard) / "eeprom").get();
423         usrp_info["mboard_id"] =
424             _tree->access<std::string>(mb_root(mcp.mboard) / "name").get();
425         usrp_info["mboard_name"]   = mb_eeprom.get("name", "n/a");
426         usrp_info["mboard_serial"] = mb_eeprom["serial"];
427         usrp_info["rx_subdev_name"] =
428             _tree->access<std::string>(rx_rf_fe_root(chan) / "name").get();
429         usrp_info["rx_subdev_spec"] =
430             _tree->access<subdev_spec_t>(mb_root(mcp.mboard) / "rx_subdev_spec")
431                 .get()
432                 .to_string();
433         usrp_info["rx_antenna"] =
434             _tree->access<std::string>(rx_rf_fe_root(chan) / "antenna" / "value").get();
435         if (_tree->exists(
436                 rx_rf_fe_root(chan).branch_path().branch_path() / "rx_eeprom")) {
437             const auto db_eeprom =
438                 _tree
439                     ->access<dboard_eeprom_t>(
440                         rx_rf_fe_root(chan).branch_path().branch_path() / "rx_eeprom")
441                     .get();
442             usrp_info["rx_serial"] = db_eeprom.serial;
443             usrp_info["rx_id"]     = db_eeprom.id.to_pp_string();
444         }
445         if (_tree->exists(rx_rf_fe_root(chan) / "ref_power/key")) {
446             usrp_info["rx_ref_power_key"] =
447                 _tree->access<std::string>(rx_rf_fe_root(chan) / "ref_power/key").get();
448         }
449         if (_tree->exists(rx_rf_fe_root(chan) / "ref_power/serial")) {
450             usrp_info["rx_ref_power_serial"] =
451                 _tree->access<std::string>(rx_rf_fe_root(chan) / "ref_power/serial").get();
452         }
453         return usrp_info;
454     }
455 
get_usrp_tx_info(size_t chan)456     dict<std::string, std::string> get_usrp_tx_info(size_t chan)
457     {
458         mboard_chan_pair mcp = tx_chan_to_mcp(chan);
459         dict<std::string, std::string> usrp_info;
460         const auto mb_eeprom =
461             _tree->access<mboard_eeprom_t>(mb_root(mcp.mboard) / "eeprom").get();
462         usrp_info["mboard_id"] =
463             _tree->access<std::string>(mb_root(mcp.mboard) / "name").get();
464         usrp_info["mboard_name"]   = mb_eeprom.get("name", "n/a");
465         usrp_info["mboard_serial"] = mb_eeprom["serial"];
466         usrp_info["tx_subdev_name"] =
467             _tree->access<std::string>(tx_rf_fe_root(chan) / "name").get();
468         usrp_info["tx_subdev_spec"] =
469             _tree->access<subdev_spec_t>(mb_root(mcp.mboard) / "tx_subdev_spec")
470                 .get()
471                 .to_string();
472         usrp_info["tx_antenna"] =
473             _tree->access<std::string>(tx_rf_fe_root(chan) / "antenna" / "value").get();
474         if (_tree->exists(
475                 tx_rf_fe_root(chan).branch_path().branch_path() / "tx_eeprom")) {
476             const auto db_eeprom =
477                 _tree
478                     ->access<dboard_eeprom_t>(
479                         tx_rf_fe_root(chan).branch_path().branch_path() / "tx_eeprom")
480                     .get();
481             usrp_info["tx_serial"] = db_eeprom.serial;
482             usrp_info["tx_id"]     = db_eeprom.id.to_pp_string();
483         }
484         if (_tree->exists(tx_rf_fe_root(chan) / "ref_power/key")) {
485             usrp_info["tx_ref_power_key"] =
486                 _tree->access<std::string>(tx_rf_fe_root(chan) / "ref_power/key").get();
487         }
488         if (_tree->exists(tx_rf_fe_root(chan) / "ref_power/serial")) {
489             usrp_info["tx_ref_power_serial"] =
490                 _tree->access<std::string>(tx_rf_fe_root(chan) / "ref_power/serial").get();
491         }
492         return usrp_info;
493     }
494 
495     /*******************************************************************
496      * Mboard methods
497      ******************************************************************/
set_master_clock_rate(double rate,size_t mboard)498     void set_master_clock_rate(double rate, size_t mboard)
499     {
500         if (mboard != ALL_MBOARDS) {
501             if (_tree->exists(mb_root(mboard) / "auto_tick_rate")
502                 and _tree->access<bool>(mb_root(mboard) / "auto_tick_rate").get()) {
503                 _tree->access<bool>(mb_root(mboard) / "auto_tick_rate").set(false);
504                 UHD_LOGGER_INFO("MULTI_USRP")
505                     << "Setting master clock rate selection to 'manual'.";
506             }
507             _tree->access<double>(mb_root(mboard) / "tick_rate").set(rate);
508             return;
509         }
510         for (size_t m = 0; m < get_num_mboards(); m++) {
511             set_master_clock_rate(rate, m);
512         }
513     }
514 
get_master_clock_rate(size_t mboard)515     double get_master_clock_rate(size_t mboard)
516     {
517         return _tree->access<double>(mb_root(mboard) / "tick_rate").get();
518     }
519 
get_master_clock_rate_range(const size_t mboard)520     meta_range_t get_master_clock_rate_range(const size_t mboard)
521     {
522         if (_tree->exists(mb_root(mboard) / "tick_rate/range")) {
523             return _tree->access<meta_range_t>(mb_root(mboard) / "tick_rate/range").get();
524         }
525         // The USRP may not have a range defined, in which case we create a
526         // fake range with a single value:
527         const double tick_rate = get_master_clock_rate(mboard);
528         return meta_range_t(tick_rate, tick_rate, 0);
529     }
530 
get_pp_string(void)531     std::string get_pp_string(void)
532     {
533         std::string buff = str(boost::format("%s USRP:\n"
534                                              "  Device: %s\n")
535                                % ((get_num_mboards() > 1) ? "Multi" : "Single")
536                                % (_tree->access<std::string>("/name").get()));
537         for (size_t m = 0; m < get_num_mboards(); m++) {
538             buff += str(boost::format("  Mboard %d: %s\n") % m
539                         % (_tree->access<std::string>(mb_root(m) / "name").get()));
540         }
541 
542         //----------- rx side of life ----------------------------------
543         for (size_t m = 0, chan = 0; m < get_num_mboards(); m++) {
544             for (; chan < (m + 1) * get_rx_subdev_spec(m).size(); chan++) {
545                 buff += str(
546                     boost::format("  RX Channel: %u\n"
547                                   "    RX DSP: %s\n"
548                                   "    RX Dboard: %s\n"
549                                   "    RX Subdev: %s\n")
550                     % chan % rx_dsp_root(chan).leaf()
551                     % rx_rf_fe_root(chan).branch_path().branch_path().leaf()
552                     % (_tree->access<std::string>(rx_rf_fe_root(chan) / "name").get()));
553             }
554         }
555 
556         //----------- tx side of life ----------------------------------
557         for (size_t m = 0, chan = 0; m < get_num_mboards(); m++) {
558             for (; chan < (m + 1) * get_tx_subdev_spec(m).size(); chan++) {
559                 buff += str(
560                     boost::format("  TX Channel: %u\n"
561                                   "    TX DSP: %s\n"
562                                   "    TX Dboard: %s\n"
563                                   "    TX Subdev: %s\n")
564                     % chan % tx_dsp_root(chan).leaf()
565                     % tx_rf_fe_root(chan).branch_path().branch_path().leaf()
566                     % (_tree->access<std::string>(tx_rf_fe_root(chan) / "name").get()));
567             }
568         }
569 
570         return buff;
571     }
572 
get_mboard_name(size_t mboard)573     std::string get_mboard_name(size_t mboard)
574     {
575         return _tree->access<std::string>(mb_root(mboard) / "name").get();
576     }
577 
get_time_now(size_t mboard=0)578     time_spec_t get_time_now(size_t mboard = 0)
579     {
580         return _tree->access<time_spec_t>(mb_root(mboard) / "time/now").get();
581     }
582 
get_time_last_pps(size_t mboard=0)583     time_spec_t get_time_last_pps(size_t mboard = 0)
584     {
585         return _tree->access<time_spec_t>(mb_root(mboard) / "time/pps").get();
586     }
587 
set_time_now(const time_spec_t & time_spec,size_t mboard)588     void set_time_now(const time_spec_t& time_spec, size_t mboard)
589     {
590         if (mboard != ALL_MBOARDS) {
591             _tree->access<time_spec_t>(mb_root(mboard) / "time/now").set(time_spec);
592             return;
593         }
594         for (size_t m = 0; m < get_num_mboards(); m++) {
595             set_time_now(time_spec, m);
596         }
597     }
598 
set_time_next_pps(const time_spec_t & time_spec,size_t mboard)599     void set_time_next_pps(const time_spec_t& time_spec, size_t mboard)
600     {
601         if (mboard != ALL_MBOARDS) {
602             _tree->access<time_spec_t>(mb_root(mboard) / "time/pps").set(time_spec);
603             return;
604         }
605         for (size_t m = 0; m < get_num_mboards(); m++) {
606             set_time_next_pps(time_spec, m);
607         }
608     }
609 
set_time_unknown_pps(const time_spec_t & time_spec)610     void set_time_unknown_pps(const time_spec_t& time_spec)
611     {
612         UHD_LOGGER_INFO("MULTI_USRP") << "    1) catch time transition at pps edge";
613         auto end_time =
614             std::chrono::steady_clock::now() + std::chrono::milliseconds(1100);
615         time_spec_t time_start_last_pps = get_time_last_pps();
616         while (time_start_last_pps == get_time_last_pps()) {
617             if (std::chrono::steady_clock::now() > end_time) {
618                 throw uhd::runtime_error("Board 0 may not be getting a PPS signal!\n"
619                                          "No PPS detected within the time interval.\n"
620                                          "See the application notes for your device.\n");
621             }
622             std::this_thread::sleep_for(std::chrono::milliseconds(1));
623         }
624 
625         UHD_LOGGER_INFO("MULTI_USRP") << "    2) set times next pps (synchronously)";
626         set_time_next_pps(time_spec, ALL_MBOARDS);
627         std::this_thread::sleep_for(std::chrono::seconds(1));
628 
629         // verify that the time registers are read to be within a few RTT
630         for (size_t m = 1; m < get_num_mboards(); m++) {
631             time_spec_t time_0 = this->get_time_now(0);
632             time_spec_t time_i = this->get_time_now(m);
633             if (time_i < time_0
634                 or (time_i - time_0)
635                        > time_spec_t(0.01)) { // 10 ms: greater than RTT but not too big
636                 UHD_LOGGER_WARNING("MULTI_USRP")
637                     << boost::format(
638                            "Detected time deviation between board %d and board 0.\n"
639                            "Board 0 time is %f seconds.\n"
640                            "Board %d time is %f seconds.\n")
641                            % m % time_0.get_real_secs() % m % time_i.get_real_secs();
642             }
643         }
644     }
645 
get_time_synchronized(void)646     bool get_time_synchronized(void)
647     {
648         for (size_t m = 1; m < get_num_mboards(); m++) {
649             time_spec_t time_0 = this->get_time_now(0);
650             time_spec_t time_i = this->get_time_now(m);
651             if (time_i < time_0 or (time_i - time_0) > time_spec_t(0.01))
652                 return false;
653         }
654         return true;
655     }
656 
set_command_time(const time_spec_t & time_spec,size_t mboard)657     void set_command_time(const time_spec_t& time_spec, size_t mboard)
658     {
659         if (mboard != ALL_MBOARDS) {
660             if (not _tree->exists(mb_root(mboard) / "time/cmd")) {
661                 throw uhd::not_implemented_error(
662                     "timed command feature not implemented on this hardware");
663             }
664             _tree->access<time_spec_t>(mb_root(mboard) / "time/cmd").set(time_spec);
665             return;
666         }
667         for (size_t m = 0; m < get_num_mboards(); m++) {
668             set_command_time(time_spec, m);
669         }
670     }
671 
clear_command_time(size_t mboard)672     void clear_command_time(size_t mboard)
673     {
674         if (mboard != ALL_MBOARDS) {
675             _tree->access<time_spec_t>(mb_root(mboard) / "time/cmd")
676                 .set(time_spec_t(0.0));
677             return;
678         }
679         for (size_t m = 0; m < get_num_mboards(); m++) {
680             clear_command_time(m);
681         }
682     }
683 
issue_stream_cmd(const stream_cmd_t & stream_cmd,size_t chan)684     void issue_stream_cmd(const stream_cmd_t& stream_cmd, size_t chan)
685     {
686         if (chan != ALL_CHANS) {
687             _tree->access<stream_cmd_t>(rx_dsp_root(chan) / "stream_cmd").set(stream_cmd);
688             return;
689         }
690         for (size_t c = 0; c < get_rx_num_channels(); c++) {
691             issue_stream_cmd(stream_cmd, c);
692         }
693     }
694 
set_time_source(const std::string & source,const size_t mboard)695     void set_time_source(const std::string& source, const size_t mboard)
696     {
697         if (mboard != ALL_MBOARDS) {
698             const auto time_source_path = mb_root(mboard) / "time_source/value";
699             const auto sync_source_path = mb_root(mboard) / "sync_source/value";
700             if (_tree->exists(time_source_path)) {
701                 _tree->access<std::string>(time_source_path).set(source);
702             } else if (_tree->exists(sync_source_path)) {
703                 auto sync_source = _tree->access<device_addr_t>(sync_source_path).get();
704                 sync_source["time_source"] = source;
705                 _tree->access<device_addr_t>(sync_source_path).set(sync_source);
706             } else {
707                 throw uhd::runtime_error("Can't set time source on this device.");
708             }
709             return;
710         }
711         for (size_t m = 0; m < get_num_mboards(); m++) {
712             this->set_time_source(source, m);
713         }
714     }
715 
get_time_source(const size_t mboard)716     std::string get_time_source(const size_t mboard)
717     {
718         const auto time_source_path = mb_root(mboard) / "time_source/value";
719         if (_tree->exists(time_source_path)) {
720             return _tree->access<std::string>(time_source_path).get();
721         } else if (_tree->exists(mb_root(mboard) / "sync_source/value")) {
722             auto sync_source =
723                 _tree->access<device_addr_t>(mb_root(mboard) / "sync_source" / "value")
724                     .get();
725             if (sync_source.has_key("time_source")) {
726                 return sync_source.get("time_source");
727             }
728         }
729         throw uhd::runtime_error("Cannot query time_source on this device!");
730     }
731 
get_time_sources(const size_t mboard)732     std::vector<std::string> get_time_sources(const size_t mboard)
733     {
734         const auto time_source_path = mb_root(mboard) / "time_source/options";
735         if (_tree->exists(time_source_path)) {
736             return _tree->access<std::vector<std::string>>(time_source_path).get();
737         } else if (_tree->exists(mb_root(mboard) / "sync_source/options")) {
738             const auto sync_sources = get_sync_sources(mboard);
739             std::vector<std::string> time_sources;
740             for (const auto& sync_source : sync_sources) {
741                 if (sync_source.has_key("time_source")) {
742                     time_sources.push_back(sync_source.get("time_source"));
743                 }
744             }
745         }
746         throw uhd::runtime_error("Cannot query time_source on this device!");
747     }
748 
set_clock_source(const std::string & source,const size_t mboard)749     void set_clock_source(const std::string& source, const size_t mboard)
750     {
751         if (mboard != ALL_MBOARDS) {
752             const auto clock_source_path = mb_root(mboard) / "clock_source/value";
753             const auto sync_source_path  = mb_root(mboard) / "sync_source/value";
754             if (_tree->exists(clock_source_path)) {
755                 _tree->access<std::string>(clock_source_path).set(source);
756             } else if (_tree->exists(sync_source_path)) {
757                 auto sync_source = _tree->access<device_addr_t>(sync_source_path).get();
758                 sync_source["clock_source"] = source;
759                 _tree->access<device_addr_t>(sync_source_path).set(sync_source);
760             } else {
761                 throw uhd::runtime_error("Can't set clock source on this device.");
762             }
763             return;
764         }
765         for (size_t m = 0; m < get_num_mboards(); m++) {
766             this->set_clock_source(source, m);
767         }
768     }
769 
get_clock_source(const size_t mboard)770     std::string get_clock_source(const size_t mboard)
771     {
772         const auto clock_source_path = mb_root(mboard) / "clock_source/value";
773         if (_tree->exists(clock_source_path)) {
774             return _tree->access<std::string>(mb_root(mboard) / "clock_source" / "value")
775                 .get();
776         } else if (_tree->exists(mb_root(mboard) / "sync_source/value")) {
777             auto sync_source =
778                 _tree->access<device_addr_t>(mb_root(mboard) / "sync_source" / "value")
779                     .get();
780             if (sync_source.has_key("clock_source")) {
781                 return sync_source.get("clock_source");
782             }
783         }
784         throw uhd::runtime_error("Cannot query clock_source on this device!");
785     }
786 
set_sync_source(const std::string & clock_source,const std::string & time_source,const size_t mboard)787     void set_sync_source(const std::string& clock_source,
788         const std::string& time_source,
789         const size_t mboard)
790     {
791         device_addr_t sync_args;
792         sync_args["clock_source"] = clock_source;
793         sync_args["time_source"]  = time_source;
794         set_sync_source(sync_args, mboard);
795     }
796 
set_sync_source(const device_addr_t & sync_source,const size_t mboard)797     void set_sync_source(const device_addr_t& sync_source, const size_t mboard)
798     {
799         if (mboard != ALL_MBOARDS) {
800             const auto sync_source_path = mb_root(mboard) / "sync_source/value";
801             if (_tree->exists(sync_source_path)) {
802                 _tree->access<device_addr_t>(sync_source_path).set(sync_source);
803             } else if (_tree->exists(mb_root(mboard) / "clock_source/value")
804                        and _tree->exists(mb_root(mboard) / "time_source/value")
805                        and sync_source.has_key("clock_source")
806                        and sync_source.has_key("time_source")) {
807                 const std::string clock_source = sync_source["clock_source"];
808                 const std::string time_source  = sync_source["time_source"];
809                 set_clock_source(clock_source, mboard);
810                 set_time_source(time_source, mboard);
811             } else {
812                 throw uhd::runtime_error("Can't set sync source on this device.");
813             }
814             return;
815         }
816         for (size_t m = 0; m < get_num_mboards(); m++) {
817             this->set_sync_source(sync_source, m);
818         }
819     }
820 
get_sync_source(const size_t mboard)821     device_addr_t get_sync_source(const size_t mboard)
822     {
823         const auto sync_source_path = mb_root(mboard) / "sync_source/value";
824         if (_tree->exists(sync_source_path)) {
825             return _tree->access<device_addr_t>(sync_source_path).get();
826         }
827         // If this path is not there, we fall back to the oldschool method and
828         // convert to a new-fangled sync source dictionary
829         const std::string clock_source = get_clock_source(mboard);
830         const std::string time_source  = get_time_source(mboard);
831         device_addr_t sync_source;
832         sync_source["clock_source"] = clock_source;
833         sync_source["time_source"]  = time_source;
834         return sync_source;
835     }
836 
get_sync_sources(const size_t mboard)837     std::vector<device_addr_t> get_sync_sources(const size_t mboard)
838     {
839         const auto sync_source_path = mb_root(mboard) / "sync_source/options";
840         if (_tree->exists(sync_source_path)) {
841             return _tree->access<std::vector<device_addr_t>>(sync_source_path).get();
842         }
843         // If this path is not there, we fall back to the oldschool method and
844         // convert to a new-fangled sync source dictionary
845         const auto clock_sources = get_clock_sources(mboard);
846         const auto time_sources  = get_time_sources(mboard);
847         std::vector<device_addr_t> sync_sources;
848         for (const auto& clock_source : clock_sources) {
849             for (const auto& time_source : time_sources) {
850                 device_addr_t sync_source;
851                 sync_source["clock_source"] = clock_source;
852                 sync_source["time_source"]  = time_source;
853                 sync_sources.push_back(sync_source);
854             }
855         }
856 
857         return sync_sources;
858     }
859 
get_clock_sources(const size_t mboard)860     std::vector<std::string> get_clock_sources(const size_t mboard)
861     {
862         const auto clock_source_path = mb_root(mboard) / "clock_source/options";
863         if (_tree->exists(clock_source_path)) {
864             return _tree->access<std::vector<std::string>>(clock_source_path).get();
865         } else if (_tree->exists(mb_root(mboard) / "sync_source/options")) {
866             const auto sync_sources = get_sync_sources(mboard);
867             std::vector<std::string> clock_sources;
868             for (const auto& sync_source : sync_sources) {
869                 if (sync_source.has_key("clock_source")) {
870                     clock_sources.push_back(sync_source.get("clock_source"));
871                 }
872             }
873         }
874         throw uhd::runtime_error("Cannot query clock_source on this device!");
875     }
876 
set_clock_source_out(const bool enb,const size_t mboard)877     void set_clock_source_out(const bool enb, const size_t mboard)
878     {
879         if (mboard != ALL_MBOARDS) {
880             if (_tree->exists(mb_root(mboard) / "clock_source" / "output")) {
881                 _tree->access<bool>(mb_root(mboard) / "clock_source" / "output").set(enb);
882             } else {
883                 throw uhd::runtime_error(
884                     "multi_usrp::set_clock_source_out - not supported on this device");
885             }
886             return;
887         }
888         for (size_t m = 0; m < get_num_mboards(); m++) {
889             this->set_clock_source_out(enb, m);
890         }
891     }
892 
set_time_source_out(const bool enb,const size_t mboard)893     void set_time_source_out(const bool enb, const size_t mboard)
894     {
895         if (mboard != ALL_MBOARDS) {
896             if (_tree->exists(mb_root(mboard) / "time_source" / "output")) {
897                 _tree->access<bool>(mb_root(mboard) / "time_source" / "output").set(enb);
898             } else {
899                 throw uhd::runtime_error(
900                     "multi_usrp::set_time_source_out - not supported on this device");
901             }
902             return;
903         }
904         for (size_t m = 0; m < get_num_mboards(); m++) {
905             this->set_time_source_out(enb, m);
906         }
907     }
908 
get_num_mboards(void)909     size_t get_num_mboards(void)
910     {
911         return _tree->list("/mboards").size();
912     }
913 
get_mboard_sensor(const std::string & name,size_t mboard)914     sensor_value_t get_mboard_sensor(const std::string& name, size_t mboard)
915     {
916         return _tree->access<sensor_value_t>(mb_root(mboard) / "sensors" / name).get();
917     }
918 
get_mboard_sensor_names(size_t mboard)919     std::vector<std::string> get_mboard_sensor_names(size_t mboard)
920     {
921         if (_tree->exists(mb_root(mboard) / "sensors")) {
922             return _tree->list(mb_root(mboard) / "sensors");
923         }
924         return {};
925     }
926 
set_user_register(const uint8_t addr,const uint32_t data,size_t mboard)927     void set_user_register(const uint8_t addr, const uint32_t data, size_t mboard)
928     {
929         if (mboard != ALL_MBOARDS) {
930             typedef std::pair<uint8_t, uint32_t> user_reg_t;
931             _tree->access<user_reg_t>(mb_root(mboard) / "user/regs")
932                 .set(user_reg_t(addr, data));
933             return;
934         }
935         for (size_t m = 0; m < get_num_mboards(); m++) {
936             set_user_register(addr, data, m);
937         }
938     }
939 
get_user_settings_iface(const size_t chan)940     wb_iface::sptr get_user_settings_iface(const size_t chan)
941     {
942         const auto user_settings_path = rx_rf_fe_root(chan) / "user_settings" / "iface";
943         if (_tree->exists(user_settings_path)) {
944             return _tree->access<wb_iface::sptr>(user_settings_path).get();
945         }
946         UHD_LOG_WARNING(
947             "MULTI_USRP", "Attempting to read back non-existent user settings iface!");
948         return nullptr;
949     }
950 
get_radio_control(const size_t)951     uhd::rfnoc::radio_control& get_radio_control(const size_t)
952     {
953         throw uhd::not_implemented_error(
954             "get_radio_control() not supported on this device!");
955     }
956 
957     /*******************************************************************
958      * RX methods
959      ******************************************************************/
get_rx_stream(const stream_args_t & args)960     rx_streamer::sptr get_rx_stream(const stream_args_t& args)
961     {
962         _check_link_rate(args, false);
963         stream_args_t args_ = args;
964         if (!args.args.has_key("spp")) {
965             for (auto chan : args.channels) {
966                 if (_rx_spp.count(chan)) {
967                     args_.args.set("spp", std::to_string(_rx_spp.at(chan)));
968                     break;
969                 }
970             }
971         }
972         return this->get_device()->get_rx_stream(args_);
973     }
974 
set_rx_subdev_spec(const subdev_spec_t & spec,size_t mboard)975     void set_rx_subdev_spec(const subdev_spec_t& spec, size_t mboard)
976     {
977         if (mboard != ALL_MBOARDS) {
978             _tree->access<subdev_spec_t>(mb_root(mboard) / "rx_subdev_spec").set(spec);
979             return;
980         }
981         for (size_t m = 0; m < get_num_mboards(); m++) {
982             set_rx_subdev_spec(spec, m);
983         }
984     }
985 
get_rx_subdev_spec(size_t mboard)986     subdev_spec_t get_rx_subdev_spec(size_t mboard)
987     {
988         subdev_spec_t spec =
989             _tree->access<subdev_spec_t>(mb_root(mboard) / "rx_subdev_spec").get();
990         if (spec.empty()) {
991             try {
992                 const std::string db_name =
993                     _tree->list(mb_root(mboard) / "dboards").at(0);
994                 const std::string fe_name =
995                     _tree->list(mb_root(mboard) / "dboards" / db_name / "rx_frontends")
996                         .at(0);
997                 spec.push_back(subdev_spec_pair_t(db_name, fe_name));
998                 _tree->access<subdev_spec_t>(mb_root(mboard) / "rx_subdev_spec")
999                     .set(spec);
1000             } catch (const std::exception& e) {
1001                 throw uhd::index_error(
1002                     str(boost::format("multi_usrp::get_rx_subdev_spec(%u) failed to make "
1003                                       "default spec - %s")
1004                         % mboard % e.what()));
1005             }
1006             UHD_LOGGER_INFO("MULTI_USRP")
1007                 << "Selecting default RX front end spec: " << spec.to_pp_string();
1008         }
1009         return spec;
1010     }
1011 
get_rx_num_channels(void)1012     size_t get_rx_num_channels(void)
1013     {
1014         size_t sum = 0;
1015         for (size_t m = 0; m < get_num_mboards(); m++) {
1016             sum += get_rx_subdev_spec(m).size();
1017         }
1018         return sum;
1019     }
1020 
get_rx_subdev_name(size_t chan)1021     std::string get_rx_subdev_name(size_t chan)
1022     {
1023         return _tree->access<std::string>(rx_rf_fe_root(chan) / "name").get();
1024     }
1025 
set_rx_rate(double rate,size_t chan)1026     void set_rx_rate(double rate, size_t chan)
1027     {
1028         if (chan != ALL_CHANS) {
1029             _tree->access<double>(rx_dsp_root(chan) / "rate" / "value").set(rate);
1030             do_samp_rate_warning_message(rate, get_rx_rate(chan), "RX");
1031             return;
1032         }
1033         for (size_t c = 0; c < get_rx_num_channels(); c++) {
1034             set_rx_rate(rate, c);
1035         }
1036     }
1037 
set_rx_spp(const size_t spp,const size_t chan=ALL_CHANS)1038     void set_rx_spp(const size_t spp, const size_t chan = ALL_CHANS)
1039     {
1040         _rx_spp[chan] = spp;
1041     }
1042 
get_rx_rate(size_t chan)1043     double get_rx_rate(size_t chan)
1044     {
1045         return _tree->access<double>(rx_dsp_root(chan) / "rate" / "value").get();
1046     }
1047 
get_rx_rates(size_t chan)1048     meta_range_t get_rx_rates(size_t chan)
1049     {
1050         return _tree->access<meta_range_t>(rx_dsp_root(chan) / "rate" / "range").get();
1051     }
1052 
set_rx_freq(const tune_request_t & tune_request,size_t chan)1053     tune_result_t set_rx_freq(const tune_request_t& tune_request, size_t chan)
1054     {
1055         // If any mixer is driven by an external LO the daughterboard assumes that no
1056         // CORDIC correction is necessary. Since the LO might be sourced from another
1057         // daughterboard which would normally apply a cordic correction a manual DSP tune
1058         // policy should be used to ensure identical configurations across daughterboards.
1059         if (tune_request.dsp_freq_policy == tune_request.POLICY_AUTO
1060             and tune_request.rf_freq_policy == tune_request.POLICY_AUTO) {
1061             for (size_t c = 0; c < get_rx_num_channels(); c++) {
1062                 const bool external_all_los =
1063                     _tree->exists(rx_rf_fe_root(chan) / "los" / ALL_LOS)
1064                     && get_rx_lo_source(ALL_LOS, c) == "external";
1065                 if (external_all_los) {
1066                     UHD_LOGGER_WARNING("MULTI_USRP")
1067                         << "At least one channel is using an external LO."
1068                         << "Using a manual DSP frequency policy is recommended to ensure "
1069                         << "the same frequency shift on all channels.";
1070                     break;
1071                 }
1072             }
1073         }
1074 
1075         tune_result_t result = tune_xx_subdev_and_dsp(RX_SIGN,
1076             _tree->subtree(rx_dsp_root(chan)),
1077             _tree->subtree(rx_rf_fe_root(chan)),
1078             tune_request);
1079         // do_tune_freq_results_message(tune_request, result, get_rx_freq(chan), "RX");
1080         return result;
1081     }
1082 
get_rx_freq(size_t chan)1083     double get_rx_freq(size_t chan)
1084     {
1085         return derive_freq_from_xx_subdev_and_dsp(RX_SIGN,
1086             _tree->subtree(rx_dsp_root(chan)),
1087             _tree->subtree(rx_rf_fe_root(chan)));
1088     }
1089 
get_rx_freq_range(size_t chan)1090     freq_range_t get_rx_freq_range(size_t chan)
1091     {
1092         return make_overall_tune_range(
1093             _tree->access<meta_range_t>(rx_rf_fe_root(chan) / "freq" / "range").get(),
1094             _tree->access<meta_range_t>(rx_dsp_root(chan) / "freq" / "range").get(),
1095             this->get_rx_bandwidth(chan));
1096     }
1097 
get_fe_rx_freq_range(size_t chan)1098     freq_range_t get_fe_rx_freq_range(size_t chan)
1099     {
1100         return _tree->access<meta_range_t>(rx_rf_fe_root(chan) / "freq" / "range").get();
1101     }
1102 
1103     /**************************************************************************
1104      * LO controls
1105      *************************************************************************/
get_rx_lo_names(size_t chan=0)1106     std::vector<std::string> get_rx_lo_names(size_t chan = 0)
1107     {
1108         std::vector<std::string> lo_names;
1109         if (_tree->exists(rx_rf_fe_root(chan) / "los")) {
1110             for (const std::string& name : _tree->list(rx_rf_fe_root(chan) / "los")) {
1111                 lo_names.push_back(name);
1112             }
1113         }
1114         return lo_names;
1115     }
1116 
set_rx_lo_source(const std::string & src,const std::string & name=ALL_LOS,size_t chan=0)1117     void set_rx_lo_source(
1118         const std::string& src, const std::string& name = ALL_LOS, size_t chan = 0)
1119     {
1120         if (_tree->exists(rx_rf_fe_root(chan) / "los")) {
1121             if (name == ALL_LOS) {
1122                 if (_tree->exists(rx_rf_fe_root(chan) / "los" / ALL_LOS)) {
1123                     // Special value ALL_LOS support atomically sets the source for all
1124                     // LOs
1125                     _tree
1126                         ->access<std::string>(
1127                             rx_rf_fe_root(chan) / "los" / ALL_LOS / "source" / "value")
1128                         .set(src);
1129                 } else {
1130                     for (const std::string& n :
1131                         _tree->list(rx_rf_fe_root(chan) / "los")) {
1132                         this->set_rx_lo_source(src, n, chan);
1133                     }
1134                 }
1135             } else {
1136                 if (_tree->exists(rx_rf_fe_root(chan) / "los")) {
1137                     _tree
1138                         ->access<std::string>(
1139                             rx_rf_fe_root(chan) / "los" / name / "source" / "value")
1140                         .set(src);
1141                 } else {
1142                     throw uhd::runtime_error("Could not find LO stage " + name);
1143                 }
1144             }
1145         } else {
1146             throw uhd::runtime_error(
1147                 "This device does not support manual configuration of LOs");
1148         }
1149     }
1150 
get_rx_lo_source(const std::string & name=ALL_LOS,size_t chan=0)1151     const std::string get_rx_lo_source(const std::string& name = ALL_LOS, size_t chan = 0)
1152     {
1153         if (_tree->exists(rx_rf_fe_root(chan) / "los")) {
1154             if (name == ALL_LOS) {
1155                 // Special value ALL_LOS support atomically sets the source for all LOs
1156                 return _tree
1157                     ->access<std::string>(
1158                         rx_rf_fe_root(chan) / "los" / ALL_LOS / "source" / "value")
1159                     .get();
1160             } else {
1161                 if (_tree->exists(rx_rf_fe_root(chan) / "los")) {
1162                     return _tree
1163                         ->access<std::string>(
1164                             rx_rf_fe_root(chan) / "los" / name / "source" / "value")
1165                         .get();
1166                 } else {
1167                     throw uhd::runtime_error("Could not find LO stage " + name);
1168                 }
1169             }
1170         } else {
1171             // If the daughterboard doesn't expose it's LO(s) then it can only be internal
1172             return "internal";
1173         }
1174     }
1175 
get_rx_lo_sources(const std::string & name=ALL_LOS,size_t chan=0)1176     std::vector<std::string> get_rx_lo_sources(
1177         const std::string& name = ALL_LOS, size_t chan = 0)
1178     {
1179         if (_tree->exists(rx_rf_fe_root(chan) / "los")) {
1180             if (name == ALL_LOS) {
1181                 if (_tree->exists(rx_rf_fe_root(chan) / "los" / ALL_LOS)) {
1182                     // Special value ALL_LOS support atomically sets the source for all
1183                     // LOs
1184                     return _tree
1185                         ->access<std::vector<std::string>>(
1186                             rx_rf_fe_root(chan) / "los" / ALL_LOS / "source" / "options")
1187                         .get();
1188                 } else {
1189                     return std::vector<std::string>();
1190                 }
1191             } else {
1192                 if (_tree->exists(rx_rf_fe_root(chan) / "los")) {
1193                     return _tree
1194                         ->access<std::vector<std::string>>(
1195                             rx_rf_fe_root(chan) / "los" / name / "source" / "options")
1196                         .get();
1197                 } else {
1198                     throw uhd::runtime_error("Could not find LO stage " + name);
1199                 }
1200             }
1201         } else {
1202             // If the daughterboard doesn't expose it's LO(s) then it can only be internal
1203             return std::vector<std::string>(1, "internal");
1204         }
1205     }
1206 
set_rx_lo_export_enabled(bool enabled,const std::string & name=ALL_LOS,size_t chan=0)1207     void set_rx_lo_export_enabled(
1208         bool enabled, const std::string& name = ALL_LOS, size_t chan = 0)
1209     {
1210         if (_tree->exists(rx_rf_fe_root(chan) / "los")) {
1211             if (name == ALL_LOS) {
1212                 if (_tree->exists(rx_rf_fe_root(chan) / "los" / ALL_LOS)) {
1213                     // Special value ALL_LOS support atomically sets the source for all
1214                     // LOs
1215                     _tree->access<bool>(rx_rf_fe_root(chan) / "los" / ALL_LOS / "export")
1216                         .set(enabled);
1217                 } else {
1218                     for (const std::string& n :
1219                         _tree->list(rx_rf_fe_root(chan) / "los")) {
1220                         this->set_rx_lo_export_enabled(enabled, n, chan);
1221                     }
1222                 }
1223             } else {
1224                 if (_tree->exists(rx_rf_fe_root(chan) / "los")) {
1225                     _tree->access<bool>(rx_rf_fe_root(chan) / "los" / name / "export")
1226                         .set(enabled);
1227                 } else {
1228                     throw uhd::runtime_error("Could not find LO stage " + name);
1229                 }
1230             }
1231         } else {
1232             throw uhd::runtime_error(
1233                 "This device does not support manual configuration of LOs");
1234         }
1235     }
1236 
get_rx_lo_export_enabled(const std::string & name=ALL_LOS,size_t chan=0)1237     bool get_rx_lo_export_enabled(const std::string& name = ALL_LOS, size_t chan = 0)
1238     {
1239         if (_tree->exists(rx_rf_fe_root(chan) / "los")) {
1240             if (name == ALL_LOS) {
1241                 // Special value ALL_LOS support atomically sets the source for all LOs
1242                 return _tree
1243                     ->access<bool>(rx_rf_fe_root(chan) / "los" / ALL_LOS / "export")
1244                     .get();
1245             } else {
1246                 if (_tree->exists(rx_rf_fe_root(chan) / "los")) {
1247                     return _tree
1248                         ->access<bool>(rx_rf_fe_root(chan) / "los" / name / "export")
1249                         .get();
1250                 } else {
1251                     throw uhd::runtime_error("Could not find LO stage " + name);
1252                 }
1253             }
1254         } else {
1255             // If the daughterboard doesn't expose it's LO(s), assume it cannot export
1256             return false;
1257         }
1258     }
1259 
set_rx_lo_freq(double freq,const std::string & name=ALL_LOS,size_t chan=0)1260     double set_rx_lo_freq(double freq, const std::string& name = ALL_LOS, size_t chan = 0)
1261     {
1262         if (_tree->exists(rx_rf_fe_root(chan) / "los")) {
1263             if (name == ALL_LOS) {
1264                 throw uhd::runtime_error(
1265                     "LO frequency must be set for each stage individually");
1266             } else {
1267                 if (_tree->exists(rx_rf_fe_root(chan) / "los")) {
1268                     _tree
1269                         ->access<double>(
1270                             rx_rf_fe_root(chan) / "los" / name / "freq" / "value")
1271                         .set(freq);
1272                     return _tree
1273                         ->access<double>(
1274                             rx_rf_fe_root(chan) / "los" / name / "freq" / "value")
1275                         .get();
1276                 } else {
1277                     throw uhd::runtime_error("Could not find LO stage " + name);
1278                 }
1279             }
1280         } else {
1281             throw uhd::runtime_error(
1282                 "This device does not support manual configuration of LOs");
1283         }
1284     }
1285 
get_rx_lo_freq(const std::string & name=ALL_LOS,size_t chan=0)1286     double get_rx_lo_freq(const std::string& name = ALL_LOS, size_t chan = 0)
1287     {
1288         if (_tree->exists(rx_rf_fe_root(chan) / "los")) {
1289             if (name == ALL_LOS) {
1290                 throw uhd::runtime_error(
1291                     "LO frequency must be retrieved for each stage individually");
1292             } else {
1293                 if (_tree->exists(rx_rf_fe_root(chan) / "los")) {
1294                     return _tree
1295                         ->access<double>(
1296                             rx_rf_fe_root(chan) / "los" / name / "freq" / "value")
1297                         .get();
1298                 } else {
1299                     throw uhd::runtime_error("Could not find LO stage " + name);
1300                 }
1301             }
1302         } else {
1303             // Return actual RF frequency if the daughterboard doesn't expose it's LO(s)
1304             return _tree->access<double>(rx_rf_fe_root(chan) / "freq" / " value").get();
1305         }
1306     }
1307 
get_rx_lo_freq_range(const std::string & name=ALL_LOS,size_t chan=0)1308     freq_range_t get_rx_lo_freq_range(const std::string& name = ALL_LOS, size_t chan = 0)
1309     {
1310         if (_tree->exists(rx_rf_fe_root(chan) / "los")) {
1311             if (name == ALL_LOS) {
1312                 throw uhd::runtime_error(
1313                     "LO frequency range must be retrieved for each stage individually");
1314             } else {
1315                 if (_tree->exists(rx_rf_fe_root(chan) / "los")) {
1316                     return _tree
1317                         ->access<freq_range_t>(
1318                             rx_rf_fe_root(chan) / "los" / name / "freq" / "range")
1319                         .get();
1320                 } else {
1321                     throw uhd::runtime_error("Could not find LO stage " + name);
1322                 }
1323             }
1324         } else {
1325             // Return the actual RF range if the daughterboard doesn't expose it's LO(s)
1326             return _tree->access<meta_range_t>(rx_rf_fe_root(chan) / "freq" / "range")
1327                 .get();
1328         }
1329     }
1330 
get_tx_lo_names(const size_t chan=0)1331     std::vector<std::string> get_tx_lo_names(const size_t chan = 0)
1332     {
1333         std::vector<std::string> lo_names;
1334         if (_tree->exists(tx_rf_fe_root(chan) / "los")) {
1335             for (const std::string& name : _tree->list(tx_rf_fe_root(chan) / "los")) {
1336                 lo_names.push_back(name);
1337             }
1338         }
1339         return lo_names;
1340     }
1341 
set_tx_lo_source(const std::string & src,const std::string & name=ALL_LOS,const size_t chan=0)1342     void set_tx_lo_source(
1343         const std::string& src, const std::string& name = ALL_LOS, const size_t chan = 0)
1344     {
1345         if (_tree->exists(tx_rf_fe_root(chan) / "los")) {
1346             if (name == ALL_LOS) {
1347                 if (_tree->exists(tx_rf_fe_root(chan) / "los" / ALL_LOS)) {
1348                     // Special value ALL_LOS support atomically sets the source
1349                     // for all LOs
1350                     _tree
1351                         ->access<std::string>(
1352                             tx_rf_fe_root(chan) / "los" / ALL_LOS / "source" / "value")
1353                         .set(src);
1354                 } else {
1355                     for (const auto& n : _tree->list(tx_rf_fe_root(chan) / "los")) {
1356                         this->set_tx_lo_source(src, n, chan);
1357                     }
1358                 }
1359             } else {
1360                 if (_tree->exists(tx_rf_fe_root(chan) / "los")) {
1361                     _tree
1362                         ->access<std::string>(
1363                             tx_rf_fe_root(chan) / "los" / name / "source" / "value")
1364                         .set(src);
1365                 } else {
1366                     throw uhd::runtime_error("Could not find LO stage " + name);
1367                 }
1368             }
1369         } else {
1370             throw uhd::runtime_error("This device does not support manual "
1371                                      "configuration of LOs");
1372         }
1373     }
1374 
get_tx_lo_source(const std::string & name=ALL_LOS,const size_t chan=0)1375     const std::string get_tx_lo_source(
1376         const std::string& name = ALL_LOS, const size_t chan = 0)
1377     {
1378         if (_tree->exists(tx_rf_fe_root(chan) / "los")) {
1379             if (_tree->exists(tx_rf_fe_root(chan) / "los")) {
1380                 return _tree
1381                     ->access<std::string>(
1382                         tx_rf_fe_root(chan) / "los" / name / "source" / "value")
1383                     .get();
1384             } else {
1385                 throw uhd::runtime_error("Could not find LO stage " + name);
1386             }
1387         } else {
1388             // If the daughterboard doesn't expose its LO(s) then it can only
1389             // be internal
1390             return "internal";
1391         }
1392     }
1393 
get_tx_lo_sources(const std::string & name=ALL_LOS,const size_t chan=0)1394     std::vector<std::string> get_tx_lo_sources(
1395         const std::string& name = ALL_LOS, const size_t chan = 0)
1396     {
1397         if (_tree->exists(tx_rf_fe_root(chan) / "los")) {
1398             if (name == ALL_LOS) {
1399                 if (_tree->exists(tx_rf_fe_root(chan) / "los" / ALL_LOS)) {
1400                     // Special value ALL_LOS support atomically sets the source
1401                     // for all LOs
1402                     return _tree
1403                         ->access<std::vector<std::string>>(
1404                             tx_rf_fe_root(chan) / "los" / ALL_LOS / "source" / "options")
1405                         .get();
1406                 } else {
1407                     return std::vector<std::string>();
1408                 }
1409             } else {
1410                 if (_tree->exists(tx_rf_fe_root(chan) / "los")) {
1411                     return _tree
1412                         ->access<std::vector<std::string>>(
1413                             tx_rf_fe_root(chan) / "los" / name / "source" / "options")
1414                         .get();
1415                 } else {
1416                     throw uhd::runtime_error("Could not find LO stage " + name);
1417                 }
1418             }
1419         } else {
1420             // If the daughterboard doesn't expose its LO(s) then it can only
1421             // be internal
1422             return std::vector<std::string>(1, "internal");
1423         }
1424     }
1425 
set_tx_lo_export_enabled(const bool enabled,const std::string & name=ALL_LOS,const size_t chan=0)1426     void set_tx_lo_export_enabled(
1427         const bool enabled, const std::string& name = ALL_LOS, const size_t chan = 0)
1428     {
1429         if (_tree->exists(tx_rf_fe_root(chan) / "los")) {
1430             if (name == ALL_LOS) {
1431                 if (_tree->exists(tx_rf_fe_root(chan) / "los" / ALL_LOS)) {
1432                     // Special value ALL_LOS support atomically sets the source for all
1433                     // LOs
1434                     _tree->access<bool>(tx_rf_fe_root(chan) / "los" / ALL_LOS / "export")
1435                         .set(enabled);
1436                 } else {
1437                     for (const std::string& n :
1438                         _tree->list(tx_rf_fe_root(chan) / "los")) {
1439                         this->set_tx_lo_export_enabled(enabled, n, chan);
1440                     }
1441                 }
1442             } else {
1443                 if (_tree->exists(tx_rf_fe_root(chan) / "los")) {
1444                     _tree->access<bool>(tx_rf_fe_root(chan) / "los" / name / "export")
1445                         .set(enabled);
1446                 } else {
1447                     throw uhd::runtime_error("Could not find LO stage " + name);
1448                 }
1449             }
1450         } else {
1451             throw uhd::runtime_error(
1452                 "This device does not support manual configuration of LOs");
1453         }
1454     }
1455 
get_tx_lo_export_enabled(const std::string & name=ALL_LOS,const size_t chan=0)1456     bool get_tx_lo_export_enabled(
1457         const std::string& name = ALL_LOS, const size_t chan = 0)
1458     {
1459         if (_tree->exists(tx_rf_fe_root(chan) / "los")) {
1460             if (_tree->exists(tx_rf_fe_root(chan) / "los")) {
1461                 return _tree->access<bool>(tx_rf_fe_root(chan) / "los" / name / "export")
1462                     .get();
1463             } else {
1464                 throw uhd::runtime_error("Could not find LO stage " + name);
1465             }
1466         } else {
1467             // If the daughterboard doesn't expose its LO(s), assume it cannot
1468             // export
1469             return false;
1470         }
1471     }
1472 
set_tx_lo_freq(const double freq,const std::string & name=ALL_LOS,const size_t chan=0)1473     double set_tx_lo_freq(
1474         const double freq, const std::string& name = ALL_LOS, const size_t chan = 0)
1475     {
1476         if (_tree->exists(tx_rf_fe_root(chan) / "los")) {
1477             if (name == ALL_LOS) {
1478                 throw uhd::runtime_error("LO frequency must be set for each "
1479                                          "stage individually");
1480             } else {
1481                 if (_tree->exists(tx_rf_fe_root(chan) / "los")) {
1482                     return _tree
1483                         ->access<double>(
1484                             tx_rf_fe_root(chan) / "los" / name / "freq" / "value")
1485                         .set(freq)
1486                         .get();
1487                 } else {
1488                     throw uhd::runtime_error("Could not find LO stage " + name);
1489                 }
1490             }
1491         } else {
1492             throw uhd::runtime_error("This device does not support manual "
1493                                      "configuration of LOs");
1494         }
1495     }
1496 
get_tx_lo_freq(const std::string & name=ALL_LOS,const size_t chan=0)1497     double get_tx_lo_freq(const std::string& name = ALL_LOS, const size_t chan = 0)
1498     {
1499         if (_tree->exists(tx_rf_fe_root(chan) / "los")) {
1500             if (name == ALL_LOS) {
1501                 throw uhd::runtime_error("LO frequency must be retrieved for "
1502                                          "each stage individually");
1503             } else {
1504                 if (_tree->exists(tx_rf_fe_root(chan) / "los")) {
1505                     return _tree
1506                         ->access<double>(
1507                             tx_rf_fe_root(chan) / "los" / name / "freq" / "value")
1508                         .get();
1509                 } else {
1510                     throw uhd::runtime_error("Could not find LO stage " + name);
1511                 }
1512             }
1513         } else {
1514             // Return actual RF frequency if the daughterboard doesn't expose
1515             // its LO(s)
1516             return _tree->access<double>(tx_rf_fe_root(chan) / "freq" / " value").get();
1517         }
1518     }
1519 
get_tx_lo_freq_range(const std::string & name=ALL_LOS,const size_t chan=0)1520     freq_range_t get_tx_lo_freq_range(
1521         const std::string& name = ALL_LOS, const size_t chan = 0)
1522     {
1523         if (_tree->exists(tx_rf_fe_root(chan) / "los")) {
1524             if (name == ALL_LOS) {
1525                 throw uhd::runtime_error("LO frequency range must be retrieved "
1526                                          "for each stage individually");
1527             } else {
1528                 if (_tree->exists(tx_rf_fe_root(chan) / "los")) {
1529                     return _tree
1530                         ->access<freq_range_t>(
1531                             tx_rf_fe_root(chan) / "los" / name / "freq" / "range")
1532                         .get();
1533                 } else {
1534                     throw uhd::runtime_error("Could not find LO stage " + name);
1535                 }
1536             }
1537         } else {
1538             // Return the actual RF range if the daughterboard doesn't expose
1539             // its LO(s)
1540             return _tree->access<meta_range_t>(tx_rf_fe_root(chan) / "freq" / "range")
1541                 .get();
1542         }
1543     }
1544 
1545     /**************************************************************************
1546      * Gain control
1547      *************************************************************************/
set_rx_gain(double gain,const std::string & name,size_t chan)1548     void set_rx_gain(double gain, const std::string& name, size_t chan)
1549     {
1550         /* Check if any AGC mode is enable and if so warn the user */
1551         if (chan != ALL_CHANS) {
1552             if (_tree->exists(rx_rf_fe_root(chan) / "gain" / "agc")) {
1553                 bool agc =
1554                     _tree->access<bool>(rx_rf_fe_root(chan) / "gain" / "agc" / "enable")
1555                         .get();
1556                 if (agc) {
1557                     UHD_LOGGER_WARNING("MULTI_USRP")
1558                         << "AGC enabled for this channel. Setting will be ignored.";
1559                 }
1560             }
1561         } else {
1562             for (size_t c = 0; c < get_rx_num_channels(); c++) {
1563                 if (_tree->exists(rx_rf_fe_root(c) / "gain" / "agc")) {
1564                     bool agc = _tree
1565                                    ->access<bool>(
1566                                        rx_rf_fe_root(chan) / "gain" / "agc" / "enable")
1567                                    .get();
1568                     if (agc) {
1569                         UHD_LOGGER_WARNING("MULTI_USRP")
1570                             << "AGC enabled for this channel. Setting will be ignored.";
1571                     }
1572                 }
1573             }
1574         }
1575         /* Apply gain setting.
1576          * If device is in AGC mode it will ignore the setting. */
1577         try {
1578             return rx_gain_group(chan)->set_value(gain, name);
1579         } catch (uhd::key_error&) {
1580             THROW_GAIN_NAME_ERROR(name, chan, rx);
1581         }
1582     }
1583 
set_rx_gain_profile(const std::string & profile,const size_t chan)1584     void set_rx_gain_profile(const std::string& profile, const size_t chan)
1585     {
1586         if (chan != ALL_CHANS) {
1587             if (_tree->exists(rx_rf_fe_root(chan) / "gains/all/profile/value")) {
1588                 _tree->access<std::string>(
1589                          rx_rf_fe_root(chan) / "gains/all/profile/value")
1590                     .set(profile);
1591             }
1592         } else {
1593             for (size_t c = 0; c < get_rx_num_channels(); c++) {
1594                 if (_tree->exists(rx_rf_fe_root(c) / "gains/all/profile/value")) {
1595                     _tree
1596                         ->access<std::string>(
1597                             rx_rf_fe_root(chan) / "gains/all/profile/value")
1598                         .set(profile);
1599                 }
1600             }
1601         }
1602     }
1603 
get_rx_gain_profile(const size_t chan)1604     std::string get_rx_gain_profile(const size_t chan)
1605     {
1606         if (chan != ALL_CHANS) {
1607             if (_tree->exists(rx_rf_fe_root(chan) / "gains/all/profile/value")) {
1608                 return _tree
1609                     ->access<std::string>(rx_rf_fe_root(chan) / "gains/all/profile/value")
1610                     .get();
1611             }
1612         } else {
1613             throw uhd::runtime_error("Can't get RX gain profile from "
1614                                      "all channels at once!");
1615         }
1616         return "";
1617     }
1618 
get_rx_gain_profile_names(const size_t chan)1619     std::vector<std::string> get_rx_gain_profile_names(const size_t chan)
1620     {
1621         if (chan != ALL_CHANS) {
1622             if (_tree->exists(rx_rf_fe_root(chan) / "gains/all/profile/options")) {
1623                 return _tree
1624                     ->access<std::vector<std::string>>(
1625                         rx_rf_fe_root(chan) / "gains/all/profile/options")
1626                     .get();
1627             }
1628         } else {
1629             throw uhd::runtime_error("Can't get RX gain profile names from "
1630                                      "all channels at once!");
1631         }
1632         return std::vector<std::string>();
1633     }
1634 
set_normalized_rx_gain(double gain,size_t chan=0)1635     void set_normalized_rx_gain(double gain, size_t chan = 0)
1636     {
1637         if (gain > 1.0 || gain < 0.0) {
1638             throw uhd::runtime_error("Normalized gain out of range, "
1639                                      "must be in [0, 1].");
1640         }
1641         const gain_range_t gain_range = get_rx_gain_range(ALL_GAINS, chan);
1642         const double abs_gain =
1643             (gain * (gain_range.stop() - gain_range.start())) + gain_range.start();
1644         set_rx_gain(abs_gain, ALL_GAINS, chan);
1645     }
1646 
set_rx_agc(bool enable,size_t chan=0)1647     void set_rx_agc(bool enable, size_t chan = 0)
1648     {
1649         if (chan != ALL_CHANS) {
1650             if (_tree->exists(rx_rf_fe_root(chan) / "gain" / "agc" / "enable")) {
1651                 _tree->access<bool>(rx_rf_fe_root(chan) / "gain" / "agc" / "enable")
1652                     .set(enable);
1653             } else {
1654                 UHD_LOGGER_WARNING("MULTI_USRP")
1655                     << "AGC is not available on this device.";
1656             }
1657             return;
1658         }
1659         for (size_t c = 0; c < get_rx_num_channels(); c++) {
1660             this->set_rx_agc(enable, c);
1661         }
1662     }
1663 
get_rx_gain(const std::string & name,size_t chan)1664     double get_rx_gain(const std::string& name, size_t chan)
1665     {
1666         try {
1667             return rx_gain_group(chan)->get_value(name);
1668         } catch (uhd::key_error&) {
1669             THROW_GAIN_NAME_ERROR(name, chan, rx);
1670         }
1671     }
1672 
get_normalized_rx_gain(size_t chan)1673     double get_normalized_rx_gain(size_t chan)
1674     {
1675         gain_range_t gain_range = get_rx_gain_range(ALL_GAINS, chan);
1676         double gain_range_width = gain_range.stop() - gain_range.start();
1677         // In case we have a device without a range of gains:
1678         if (gain_range_width == 0.0) {
1679             return 0;
1680         }
1681         double norm_gain =
1682             (get_rx_gain(ALL_GAINS, chan) - gain_range.start()) / gain_range_width;
1683         // Avoid rounding errors:
1684         if (norm_gain > 1.0)
1685             return 1.0;
1686         if (norm_gain < 0.0)
1687             return 0.0;
1688         return norm_gain;
1689     }
1690 
get_rx_gain_range(const std::string & name,size_t chan)1691     gain_range_t get_rx_gain_range(const std::string& name, size_t chan)
1692     {
1693         try {
1694             return rx_gain_group(chan)->get_range(name);
1695         } catch (uhd::key_error&) {
1696             THROW_GAIN_NAME_ERROR(name, chan, rx);
1697         }
1698     }
1699 
get_rx_gain_names(size_t chan)1700     std::vector<std::string> get_rx_gain_names(size_t chan)
1701     {
1702         return rx_gain_group(chan)->get_names();
1703     }
1704 
1705     /**************************************************************************
1706      * RX Power control
1707      *************************************************************************/
has_rx_power_reference(const size_t chan)1708     bool has_rx_power_reference(const size_t chan)
1709     {
1710         return _tree->exists(rx_rf_fe_root(chan) / "ref_power/value");
1711     }
1712 
set_rx_power_reference(const double power_dbm,const size_t chan=0)1713     void set_rx_power_reference(const double power_dbm, const size_t chan = 0)
1714     {
1715         const auto power_ref_path = rx_rf_fe_root(chan) / "ref_power/value";
1716         if (!_tree->exists(power_ref_path)) {
1717             throw uhd::not_implemented_error(
1718                 "set_rx_power_reference() not available for this device and channel");
1719         }
1720         _tree->access<double>(power_ref_path).set(power_dbm);
1721     }
1722 
get_rx_power_reference(const size_t chan=0)1723     double get_rx_power_reference(const size_t chan = 0)
1724     {
1725         const auto power_ref_path = rx_rf_fe_root(chan) / "ref_power/value";
1726         if (!_tree->exists(power_ref_path)) {
1727             throw uhd::not_implemented_error(
1728                 "get_rx_power_reference() not available for this device and channel");
1729         }
1730         return _tree->access<double>(power_ref_path).get();
1731     }
1732 
get_rx_power_range(const size_t chan)1733     meta_range_t get_rx_power_range(const size_t chan)
1734     {
1735         const auto power_ref_path = rx_rf_fe_root(chan) / "ref_power/range";
1736         if (!_tree->exists(power_ref_path)) {
1737             throw uhd::not_implemented_error(
1738                 "get_rx_power_range() not available for this device and channel");
1739         }
1740         return _tree->access<meta_range_t>(power_ref_path).get();
1741 
1742     }
1743 
set_rx_antenna(const std::string & ant,size_t chan)1744     void set_rx_antenna(const std::string& ant, size_t chan)
1745     {
1746         _tree->access<std::string>(rx_rf_fe_root(chan) / "antenna" / "value").set(ant);
1747     }
1748 
get_rx_antenna(size_t chan)1749     std::string get_rx_antenna(size_t chan)
1750     {
1751         return _tree->access<std::string>(rx_rf_fe_root(chan) / "antenna" / "value")
1752             .get();
1753     }
1754 
get_rx_antennas(size_t chan)1755     std::vector<std::string> get_rx_antennas(size_t chan)
1756     {
1757         return _tree
1758             ->access<std::vector<std::string>>(
1759                 rx_rf_fe_root(chan) / "antenna" / "options")
1760             .get();
1761     }
1762 
set_rx_bandwidth(double bandwidth,size_t chan)1763     void set_rx_bandwidth(double bandwidth, size_t chan)
1764     {
1765         _tree->access<double>(rx_rf_fe_root(chan) / "bandwidth" / "value").set(bandwidth);
1766     }
1767 
get_rx_bandwidth(size_t chan)1768     double get_rx_bandwidth(size_t chan)
1769     {
1770         return _tree->access<double>(rx_rf_fe_root(chan) / "bandwidth" / "value").get();
1771     }
1772 
get_rx_bandwidth_range(size_t chan)1773     meta_range_t get_rx_bandwidth_range(size_t chan)
1774     {
1775         return _tree->access<meta_range_t>(rx_rf_fe_root(chan) / "bandwidth" / "range")
1776             .get();
1777     }
1778 
get_rx_dboard_iface(size_t chan)1779     dboard_iface::sptr get_rx_dboard_iface(size_t chan)
1780     {
1781         return _tree
1782             ->access<dboard_iface::sptr>(
1783                 rx_rf_fe_root(chan).branch_path().branch_path() / "iface")
1784             .get();
1785     }
1786 
get_rx_sensor(const std::string & name,size_t chan)1787     sensor_value_t get_rx_sensor(const std::string& name, size_t chan)
1788     {
1789         return _tree->access<sensor_value_t>(rx_rf_fe_root(chan) / "sensors" / name)
1790             .get();
1791     }
1792 
get_rx_sensor_names(size_t chan)1793     std::vector<std::string> get_rx_sensor_names(size_t chan)
1794     {
1795         std::vector<std::string> sensor_names;
1796         if (_tree->exists(rx_rf_fe_root(chan) / "sensors")) {
1797             sensor_names = _tree->list(rx_rf_fe_root(chan) / "sensors");
1798         }
1799         return sensor_names;
1800     }
1801 
set_rx_dc_offset(const bool enb,size_t chan)1802     void set_rx_dc_offset(const bool enb, size_t chan)
1803     {
1804         if (chan != ALL_CHANS) {
1805             if (_tree->exists(rx_fe_root(chan) / "dc_offset" / "enable")) {
1806                 _tree->access<bool>(rx_fe_root(chan) / "dc_offset" / "enable").set(enb);
1807             } else if (_tree->exists(rx_rf_fe_root(chan) / "dc_offset" / "enable")) {
1808                 /*For B2xx devices the dc-offset correction is implemented in the rf
1809                  * front-end*/
1810                 _tree->access<bool>(rx_rf_fe_root(chan) / "dc_offset" / "enable")
1811                     .set(enb);
1812             } else {
1813                 UHD_LOGGER_WARNING("MULTI_USRP")
1814                     << "Setting DC offset compensation is not possible on this device.";
1815             }
1816             return;
1817         }
1818         for (size_t c = 0; c < get_rx_num_channels(); c++) {
1819             this->set_rx_dc_offset(enb, c);
1820         }
1821     }
1822 
set_rx_dc_offset(const std::complex<double> & offset,size_t chan)1823     void set_rx_dc_offset(const std::complex<double>& offset, size_t chan)
1824     {
1825         if (chan != ALL_CHANS) {
1826             if (_tree->exists(rx_fe_root(chan) / "dc_offset" / "value")) {
1827                 _tree
1828                     ->access<std::complex<double>>(
1829                         rx_fe_root(chan) / "dc_offset" / "value")
1830                     .set(offset);
1831             } else {
1832                 UHD_LOGGER_WARNING("MULTI_USRP")
1833                     << "Setting DC offset is not possible on this device.";
1834             }
1835             return;
1836         }
1837         for (size_t c = 0; c < get_rx_num_channels(); c++) {
1838             this->set_rx_dc_offset(offset, c);
1839         }
1840     }
1841 
get_rx_dc_offset_range(size_t chan)1842     meta_range_t get_rx_dc_offset_range(size_t chan)
1843     {
1844         if (_tree->exists(rx_fe_root(chan) / "dc_offset" / "range")) {
1845             return _tree
1846                 ->access<uhd::meta_range_t>(rx_fe_root(chan) / "dc_offset" / "range")
1847                 .get();
1848         } else {
1849             UHD_LOGGER_WARNING("MULTI_USRP")
1850                 << "This device does not support querying the RX DC offset range.";
1851             return meta_range_t(0, 0);
1852         }
1853     }
1854 
set_rx_iq_balance(const bool enb,size_t chan)1855     void set_rx_iq_balance(const bool enb, size_t chan)
1856     {
1857         if (chan != ALL_CHANS) {
1858             if (_tree->exists(rx_rf_fe_root(chan) / "iq_balance" / "enable")) {
1859                 _tree->access<bool>(rx_rf_fe_root(chan) / "iq_balance" / "enable")
1860                     .set(enb);
1861             } else {
1862                 UHD_LOGGER_WARNING("MULTI_USRP") << "Setting IQ imbalance compensation "
1863                                                     "is not possible on this device.";
1864             }
1865             return;
1866         }
1867         for (size_t c = 0; c < get_rx_num_channels(); c++) {
1868             this->set_rx_iq_balance(enb, c);
1869         }
1870     }
1871 
set_rx_iq_balance(const std::complex<double> & offset,size_t chan)1872     void set_rx_iq_balance(const std::complex<double>& offset, size_t chan)
1873     {
1874         if (chan != ALL_CHANS) {
1875             if (_tree->exists(rx_fe_root(chan) / "iq_balance" / "value")) {
1876                 _tree
1877                     ->access<std::complex<double>>(
1878                         rx_fe_root(chan) / "iq_balance" / "value")
1879                     .set(offset);
1880             } else {
1881                 UHD_LOGGER_WARNING("MULTI_USRP")
1882                     << "Setting IQ balance is not possible on this device.";
1883             }
1884             return;
1885         }
1886         for (size_t c = 0; c < get_rx_num_channels(); c++) {
1887             this->set_rx_iq_balance(offset, c);
1888         }
1889     }
1890 
get_rx_filter_names(const size_t chan)1891     std::vector<std::string> get_rx_filter_names(const size_t chan)
1892     {
1893         if (chan >= get_rx_num_channels()) {
1894             throw uhd::index_error("Attempting to get non-existent RX filter names");
1895         }
1896         std::vector<std::string> ret;
1897 
1898         if (_tree->exists(rx_rf_fe_root(chan) / "filters")) {
1899             std::vector<std::string> names = _tree->list(rx_rf_fe_root(chan) / "filters");
1900             for (size_t i = 0; i < names.size(); i++) {
1901                 std::string name = rx_rf_fe_root(chan) / "filters" / names[i];
1902                 ret.push_back(name);
1903             }
1904         }
1905         if (_tree->exists(rx_dsp_root(chan) / "filters")) {
1906             std::vector<std::string> names = _tree->list(rx_dsp_root(chan) / "filters");
1907             for (size_t i = 0; i < names.size(); i++) {
1908                 std::string name = rx_dsp_root(chan) / "filters" / names[i];
1909                 ret.push_back(name);
1910             }
1911         }
1912 
1913         return ret;
1914     }
1915 
get_rx_filter(const std::string & name,const size_t chan)1916     uhd::filter_info_base::sptr get_rx_filter(const std::string& name, const size_t chan)
1917     {
1918         std::vector<std::string> possible_names = get_rx_filter_names(chan);
1919         std::vector<std::string>::iterator it;
1920         it = find(possible_names.begin(), possible_names.end(), name);
1921         if (it == possible_names.end()) {
1922             throw uhd::runtime_error("Attempting to get non-existing filter: " + name);
1923         }
1924 
1925         return _tree->access<filter_info_base::sptr>(fs_path(name) / "value").get();
1926     }
1927 
set_rx_filter(const std::string & name,uhd::filter_info_base::sptr filter,const size_t chan)1928     void set_rx_filter(
1929         const std::string& name, uhd::filter_info_base::sptr filter, const size_t chan)
1930     {
1931         std::vector<std::string> possible_names = get_rx_filter_names(chan);
1932         std::vector<std::string>::iterator it;
1933         it = find(possible_names.begin(), possible_names.end(), name);
1934         if (it == possible_names.end()) {
1935             throw uhd::runtime_error("Attempting to set non-existing filter: " + name);
1936         }
1937 
1938         _tree->access<filter_info_base::sptr>(fs_path(name) / "value").set(filter);
1939     }
1940 
get_tx_filter_names(const size_t chan)1941     std::vector<std::string> get_tx_filter_names(const size_t chan)
1942     {
1943         if (chan >= get_tx_num_channels()) {
1944             throw uhd::index_error("Attempting to get non-existent TX filter names");
1945         }
1946         std::vector<std::string> ret;
1947 
1948         if (_tree->exists(tx_rf_fe_root(chan) / "filters")) {
1949             std::vector<std::string> names = _tree->list(tx_rf_fe_root(chan) / "filters");
1950             for (size_t i = 0; i < names.size(); i++) {
1951                 std::string name = tx_rf_fe_root(chan) / "filters" / names[i];
1952                 ret.push_back(name);
1953             }
1954         }
1955         if (_tree->exists(rx_dsp_root(chan) / "filters")) {
1956             std::vector<std::string> names = _tree->list(tx_dsp_root(chan) / "filters");
1957             for (size_t i = 0; i < names.size(); i++) {
1958                 std::string name = tx_dsp_root(chan) / "filters" / names[i];
1959                 ret.push_back(name);
1960             }
1961         }
1962 
1963         return ret;
1964     }
1965 
get_tx_filter(const std::string & name,const size_t chan)1966     uhd::filter_info_base::sptr get_tx_filter(const std::string& name, const size_t chan)
1967     {
1968         std::vector<std::string> possible_names = get_tx_filter_names(chan);
1969         std::vector<std::string>::iterator it;
1970         it = find(possible_names.begin(), possible_names.end(), name);
1971         if (it == possible_names.end()) {
1972             throw uhd::runtime_error("Attempting to get non-existing filter: " + name);
1973         }
1974 
1975         return _tree->access<filter_info_base::sptr>(fs_path(name) / "value").get();
1976     }
1977 
set_tx_filter(const std::string & name,uhd::filter_info_base::sptr filter,const size_t chan)1978     void set_tx_filter(
1979         const std::string& name, uhd::filter_info_base::sptr filter, const size_t chan)
1980     {
1981         std::vector<std::string> possible_names = get_tx_filter_names(chan);
1982         std::vector<std::string>::iterator it;
1983         it = find(possible_names.begin(), possible_names.end(), name);
1984         if (it == possible_names.end()) {
1985             throw uhd::runtime_error("Attempting to set non-existing filter: " + name);
1986         }
1987 
1988         _tree->access<filter_info_base::sptr>(fs_path(name) / "value").set(filter);
1989     }
1990 
1991     /*******************************************************************
1992      * TX methods
1993      ******************************************************************/
get_tx_stream(const stream_args_t & args)1994     tx_streamer::sptr get_tx_stream(const stream_args_t& args)
1995     {
1996         _check_link_rate(args, true);
1997         return this->get_device()->get_tx_stream(args);
1998     }
1999 
set_tx_subdev_spec(const subdev_spec_t & spec,size_t mboard)2000     void set_tx_subdev_spec(const subdev_spec_t& spec, size_t mboard)
2001     {
2002         if (mboard != ALL_MBOARDS) {
2003             _tree->access<subdev_spec_t>(mb_root(mboard) / "tx_subdev_spec").set(spec);
2004             return;
2005         }
2006         for (size_t m = 0; m < get_num_mboards(); m++) {
2007             set_tx_subdev_spec(spec, m);
2008         }
2009     }
2010 
get_tx_subdev_spec(size_t mboard)2011     subdev_spec_t get_tx_subdev_spec(size_t mboard)
2012     {
2013         subdev_spec_t spec =
2014             _tree->access<subdev_spec_t>(mb_root(mboard) / "tx_subdev_spec").get();
2015         if (spec.empty()) {
2016             try {
2017                 const std::string db_name =
2018                     _tree->list(mb_root(mboard) / "dboards").at(0);
2019                 const std::string fe_name =
2020                     _tree->list(mb_root(mboard) / "dboards" / db_name / "tx_frontends")
2021                         .at(0);
2022                 spec.push_back(subdev_spec_pair_t(db_name, fe_name));
2023                 _tree->access<subdev_spec_t>(mb_root(mboard) / "tx_subdev_spec")
2024                     .set(spec);
2025             } catch (const std::exception& e) {
2026                 throw uhd::index_error(
2027                     str(boost::format("multi_usrp::get_tx_subdev_spec(%u) failed to make "
2028                                       "default spec - %s")
2029                         % mboard % e.what()));
2030             }
2031             UHD_LOGGER_INFO("MULTI_USRP")
2032                 << "Selecting default TX front end spec: " << spec.to_pp_string();
2033         }
2034         return spec;
2035     }
2036 
get_tx_num_channels(void)2037     size_t get_tx_num_channels(void)
2038     {
2039         size_t sum = 0;
2040         for (size_t m = 0; m < get_num_mboards(); m++) {
2041             sum += get_tx_subdev_spec(m).size();
2042         }
2043         return sum;
2044     }
2045 
get_tx_subdev_name(size_t chan)2046     std::string get_tx_subdev_name(size_t chan)
2047     {
2048         return _tree->access<std::string>(tx_rf_fe_root(chan) / "name").get();
2049     }
2050 
set_tx_rate(double rate,size_t chan)2051     void set_tx_rate(double rate, size_t chan)
2052     {
2053         if (chan != ALL_CHANS) {
2054             _tree->access<double>(tx_dsp_root(chan) / "rate" / "value").set(rate);
2055             do_samp_rate_warning_message(rate, get_tx_rate(chan), "TX");
2056             return;
2057         }
2058         for (size_t c = 0; c < get_tx_num_channels(); c++) {
2059             set_tx_rate(rate, c);
2060         }
2061     }
2062 
get_tx_rate(size_t chan)2063     double get_tx_rate(size_t chan)
2064     {
2065         return _tree->access<double>(tx_dsp_root(chan) / "rate" / "value").get();
2066     }
2067 
get_tx_rates(size_t chan)2068     meta_range_t get_tx_rates(size_t chan)
2069     {
2070         return _tree->access<meta_range_t>(tx_dsp_root(chan) / "rate" / "range").get();
2071     }
2072 
set_tx_freq(const tune_request_t & tune_request,size_t chan)2073     tune_result_t set_tx_freq(const tune_request_t& tune_request, size_t chan)
2074     {
2075         tune_result_t result = tune_xx_subdev_and_dsp(TX_SIGN,
2076             _tree->subtree(tx_dsp_root(chan)),
2077             _tree->subtree(tx_rf_fe_root(chan)),
2078             tune_request);
2079         // do_tune_freq_results_message(tune_request, result, get_tx_freq(chan), "TX");
2080         return result;
2081     }
2082 
get_tx_freq(size_t chan)2083     double get_tx_freq(size_t chan)
2084     {
2085         return derive_freq_from_xx_subdev_and_dsp(TX_SIGN,
2086             _tree->subtree(tx_dsp_root(chan)),
2087             _tree->subtree(tx_rf_fe_root(chan)));
2088     }
2089 
get_tx_freq_range(size_t chan)2090     freq_range_t get_tx_freq_range(size_t chan)
2091     {
2092         return make_overall_tune_range(
2093             _tree->access<meta_range_t>(tx_rf_fe_root(chan) / "freq" / "range").get(),
2094             _tree->access<meta_range_t>(tx_dsp_root(chan) / "freq" / "range").get(),
2095             this->get_tx_bandwidth(chan));
2096     }
2097 
get_fe_tx_freq_range(size_t chan)2098     freq_range_t get_fe_tx_freq_range(size_t chan)
2099     {
2100         return _tree->access<meta_range_t>(tx_rf_fe_root(chan) / "freq" / "range").get();
2101     }
2102 
set_tx_gain(double gain,const std::string & name,size_t chan)2103     void set_tx_gain(double gain, const std::string& name, size_t chan)
2104     {
2105         try {
2106             return tx_gain_group(chan)->set_value(gain, name);
2107         } catch (uhd::key_error&) {
2108             THROW_GAIN_NAME_ERROR(name, chan, tx);
2109         }
2110     }
2111 
set_tx_gain_profile(const std::string & profile,const size_t chan)2112     void set_tx_gain_profile(const std::string& profile, const size_t chan)
2113     {
2114         if (chan != ALL_CHANS) {
2115             if (_tree->exists(tx_rf_fe_root(chan) / "gains/all/profile/value")) {
2116                 _tree->access<std::string>(
2117                          tx_rf_fe_root(chan) / "gains/all/profile/value")
2118                     .set(profile);
2119             }
2120         } else {
2121             for (size_t c = 0; c < get_tx_num_channels(); c++) {
2122                 if (_tree->exists(tx_rf_fe_root(c) / "gains/all/profile/value")) {
2123                     _tree
2124                         ->access<std::string>(
2125                             tx_rf_fe_root(chan) / "gains/all/profile/value")
2126                         .set(profile);
2127                 }
2128             }
2129         }
2130     }
2131 
get_tx_gain_profile(const size_t chan)2132     std::string get_tx_gain_profile(const size_t chan)
2133     {
2134         if (chan != ALL_CHANS) {
2135             if (_tree->exists(tx_rf_fe_root(chan) / "gains/all/profile/value")) {
2136                 return _tree
2137                     ->access<std::string>(tx_rf_fe_root(chan) / "gains/all/profile/value")
2138                     .get();
2139             }
2140         } else {
2141             throw uhd::runtime_error("Can't get TX gain profile from "
2142                                      "all channels at once!");
2143         }
2144         return "";
2145     }
2146 
get_tx_gain_profile_names(const size_t chan)2147     std::vector<std::string> get_tx_gain_profile_names(const size_t chan)
2148     {
2149         if (chan != ALL_CHANS) {
2150             if (_tree->exists(tx_rf_fe_root(chan) / "gains/all/profile/options")) {
2151                 return _tree
2152                     ->access<std::vector<std::string>>(
2153                         tx_rf_fe_root(chan) / "gains/all/profile/options")
2154                     .get();
2155             }
2156         } else {
2157             throw uhd::runtime_error("Can't get TX gain profile names from "
2158                                      "all channels at once!");
2159         }
2160         return std::vector<std::string>();
2161     }
2162 
set_normalized_tx_gain(double gain,size_t chan=0)2163     void set_normalized_tx_gain(double gain, size_t chan = 0)
2164     {
2165         if (gain > 1.0 || gain < 0.0) {
2166             throw uhd::runtime_error("Normalized gain out of range, must be in [0, 1].");
2167         }
2168         gain_range_t gain_range = get_tx_gain_range(ALL_GAINS, chan);
2169         double abs_gain =
2170             (gain * (gain_range.stop() - gain_range.start())) + gain_range.start();
2171         set_tx_gain(abs_gain, ALL_GAINS, chan);
2172     }
2173 
2174 
get_tx_gain(const std::string & name,size_t chan)2175     double get_tx_gain(const std::string& name, size_t chan)
2176     {
2177         try {
2178             return tx_gain_group(chan)->get_value(name);
2179         } catch (uhd::key_error&) {
2180             THROW_GAIN_NAME_ERROR(name, chan, tx);
2181         }
2182     }
2183 
get_normalized_tx_gain(size_t chan)2184     double get_normalized_tx_gain(size_t chan)
2185     {
2186         gain_range_t gain_range = get_tx_gain_range(ALL_GAINS, chan);
2187         double gain_range_width = gain_range.stop() - gain_range.start();
2188         // In case we have a device without a range of gains:
2189         if (gain_range_width == 0.0) {
2190             return 0.0;
2191         }
2192         double norm_gain =
2193             (get_tx_gain(ALL_GAINS, chan) - gain_range.start()) / gain_range_width;
2194         // Avoid rounding errors:
2195         if (norm_gain > 1.0)
2196             return 1.0;
2197         if (norm_gain < 0.0)
2198             return 0.0;
2199         return norm_gain;
2200     }
2201 
get_tx_gain_range(const std::string & name,size_t chan)2202     gain_range_t get_tx_gain_range(const std::string& name, size_t chan)
2203     {
2204         try {
2205             return tx_gain_group(chan)->get_range(name);
2206         } catch (uhd::key_error&) {
2207             THROW_GAIN_NAME_ERROR(name, chan, tx);
2208         }
2209     }
2210 
get_tx_gain_names(size_t chan)2211     std::vector<std::string> get_tx_gain_names(size_t chan)
2212     {
2213         return tx_gain_group(chan)->get_names();
2214     }
2215 
2216     /**************************************************************************
2217      * TX Power Controls
2218      *************************************************************************/
has_tx_power_reference(const size_t chan)2219     bool has_tx_power_reference(const size_t chan)
2220     {
2221         return _tree->exists(tx_rf_fe_root(chan) / "ref_power/value");
2222     }
2223 
set_tx_power_reference(const double power_dbm,const size_t chan=0)2224     void set_tx_power_reference(const double power_dbm, const size_t chan = 0)
2225     {
2226         const auto power_ref_path = tx_rf_fe_root(chan) / "ref_power/value";
2227         if (!_tree->exists(power_ref_path)) {
2228             throw uhd::not_implemented_error(
2229                 "set_tx_power_reference() not available for this device and channel");
2230         }
2231         _tree->access<double>(power_ref_path).set(power_dbm);
2232     }
2233 
get_tx_power_reference(const size_t chan=0)2234     double get_tx_power_reference(const size_t chan = 0)
2235     {
2236         const auto power_ref_path = tx_rf_fe_root(chan) / "ref_power/value";
2237         if (!_tree->exists(power_ref_path)) {
2238             throw uhd::not_implemented_error(
2239                 "get_tx_power_reference() not available for this device and channel");
2240         }
2241         return _tree->access<double>(power_ref_path).get();
2242     }
2243 
get_tx_power_range(const size_t chan)2244     meta_range_t get_tx_power_range(const size_t chan)
2245     {
2246         const auto power_ref_path = tx_rf_fe_root(chan) / "ref_power/range";
2247         if (!_tree->exists(power_ref_path)) {
2248             throw uhd::not_implemented_error(
2249                 "get_tx_power_range() not available for this device and channel");
2250         }
2251         return _tree->access<meta_range_t>(power_ref_path).get();
2252     }
2253 
set_tx_antenna(const std::string & ant,size_t chan)2254     void set_tx_antenna(const std::string& ant, size_t chan)
2255     {
2256         _tree->access<std::string>(tx_rf_fe_root(chan) / "antenna" / "value").set(ant);
2257     }
2258 
get_tx_antenna(size_t chan)2259     std::string get_tx_antenna(size_t chan)
2260     {
2261         return _tree->access<std::string>(tx_rf_fe_root(chan) / "antenna" / "value")
2262             .get();
2263     }
2264 
get_tx_antennas(size_t chan)2265     std::vector<std::string> get_tx_antennas(size_t chan)
2266     {
2267         return _tree
2268             ->access<std::vector<std::string>>(
2269                 tx_rf_fe_root(chan) / "antenna" / "options")
2270             .get();
2271     }
2272 
set_tx_bandwidth(double bandwidth,size_t chan)2273     void set_tx_bandwidth(double bandwidth, size_t chan)
2274     {
2275         _tree->access<double>(tx_rf_fe_root(chan) / "bandwidth" / "value").set(bandwidth);
2276     }
2277 
get_tx_bandwidth(size_t chan)2278     double get_tx_bandwidth(size_t chan)
2279     {
2280         return _tree->access<double>(tx_rf_fe_root(chan) / "bandwidth" / "value").get();
2281     }
2282 
get_tx_bandwidth_range(size_t chan)2283     meta_range_t get_tx_bandwidth_range(size_t chan)
2284     {
2285         return _tree->access<meta_range_t>(tx_rf_fe_root(chan) / "bandwidth" / "range")
2286             .get();
2287     }
2288 
get_tx_dboard_iface(size_t chan)2289     dboard_iface::sptr get_tx_dboard_iface(size_t chan)
2290     {
2291         return _tree
2292             ->access<dboard_iface::sptr>(
2293                 tx_rf_fe_root(chan).branch_path().branch_path() / "iface")
2294             .get();
2295     }
2296 
get_tx_sensor(const std::string & name,size_t chan)2297     sensor_value_t get_tx_sensor(const std::string& name, size_t chan)
2298     {
2299         return _tree->access<sensor_value_t>(tx_rf_fe_root(chan) / "sensors" / name)
2300             .get();
2301     }
2302 
get_tx_sensor_names(size_t chan)2303     std::vector<std::string> get_tx_sensor_names(size_t chan)
2304     {
2305         std::vector<std::string> sensor_names;
2306         if (_tree->exists(rx_rf_fe_root(chan) / "sensors")) {
2307             sensor_names = _tree->list(tx_rf_fe_root(chan) / "sensors");
2308         }
2309         return sensor_names;
2310     }
2311 
set_tx_dc_offset(const std::complex<double> & offset,size_t chan)2312     void set_tx_dc_offset(const std::complex<double>& offset, size_t chan)
2313     {
2314         if (chan != ALL_CHANS) {
2315             if (_tree->exists(tx_fe_root(chan) / "dc_offset" / "value")) {
2316                 _tree
2317                     ->access<std::complex<double>>(
2318                         tx_fe_root(chan) / "dc_offset" / "value")
2319                     .set(offset);
2320             } else {
2321                 UHD_LOGGER_WARNING("MULTI_USRP")
2322                     << "Setting DC offset is not possible on this device.";
2323             }
2324             return;
2325         }
2326         for (size_t c = 0; c < get_tx_num_channels(); c++) {
2327             this->set_tx_dc_offset(offset, c);
2328         }
2329     }
2330 
get_tx_dc_offset_range(size_t chan)2331     meta_range_t get_tx_dc_offset_range(size_t chan)
2332     {
2333         if (_tree->exists(tx_fe_root(chan) / "dc_offset" / "range")) {
2334             return _tree
2335                 ->access<uhd::meta_range_t>(tx_fe_root(chan) / "dc_offset" / "range")
2336                 .get();
2337         } else {
2338             UHD_LOGGER_WARNING("MULTI_USRP")
2339                 << "This device does not support querying the TX DC offset range.";
2340             return meta_range_t(0, 0);
2341         }
2342     }
2343 
set_tx_iq_balance(const std::complex<double> & offset,size_t chan)2344     void set_tx_iq_balance(const std::complex<double>& offset, size_t chan)
2345     {
2346         if (chan != ALL_CHANS) {
2347             if (_tree->exists(tx_fe_root(chan) / "iq_balance" / "value")) {
2348                 _tree
2349                     ->access<std::complex<double>>(
2350                         tx_fe_root(chan) / "iq_balance" / "value")
2351                     .set(offset);
2352             } else {
2353                 UHD_LOGGER_WARNING("MULTI_USRP")
2354                     << "Setting IQ balance is not possible on this device.";
2355             }
2356             return;
2357         }
2358         for (size_t c = 0; c < get_tx_num_channels(); c++) {
2359             this->set_tx_iq_balance(offset, c);
2360         }
2361     }
2362 
2363     /*******************************************************************
2364      * GPIO methods
2365      ******************************************************************/
get_gpio_banks(const size_t mboard)2366     std::vector<std::string> get_gpio_banks(const size_t mboard)
2367     {
2368         std::vector<std::string> banks;
2369         if (_tree->exists(mb_root(mboard) / "gpio")) {
2370             for (const std::string& name : _tree->list(mb_root(mboard) / "gpio")) {
2371                 banks.push_back(name);
2372             }
2373         }
2374         for (const std::string& name : _tree->list(mb_root(mboard) / "dboards")) {
2375             banks.push_back("RX" + name);
2376             banks.push_back("TX" + name);
2377         }
2378         return banks;
2379     }
2380 
set_gpio_attr(const std::string & bank,const std::string & attr,const uint32_t value,const uint32_t mask,const size_t mboard)2381     void set_gpio_attr(const std::string& bank,
2382         const std::string& attr,
2383         const uint32_t value,
2384         const uint32_t mask,
2385         const size_t mboard)
2386     {
2387         std::vector<std::string> attr_value;
2388         if (_tree->exists(mb_root(mboard) / "gpio" / bank)) {
2389             if (_tree->exists(mb_root(mboard) / "gpio" / bank / attr)) {
2390                 const auto attr_type = gpio_atr::gpio_attr_rev_map.at(attr);
2391                 switch (attr_type) {
2392                     case gpio_atr::GPIO_SRC:
2393                         throw uhd::runtime_error(
2394                             "Can't set SRC attribute using integer value!");
2395                         break;
2396                     case gpio_atr::GPIO_CTRL:
2397                     case gpio_atr::GPIO_DDR: {
2398                         attr_value = _tree
2399                                          ->access<std::vector<std::string>>(
2400                                              mb_root(mboard) / "gpio" / bank / attr)
2401                                          .get();
2402                         UHD_ASSERT_THROW(attr_value.size() <= 32);
2403                         std::bitset<32> bit_mask  = std::bitset<32>(mask);
2404                         std::bitset<32> bit_value = std::bitset<32>(value);
2405                         for (size_t i = 0; i < bit_mask.size(); i++) {
2406                             if (bit_mask[i] == 1) {
2407                                 attr_value[i] = gpio_atr::attr_value_map.at(attr_type).at(
2408                                     bit_value[i]);
2409                             }
2410                         }
2411                         _tree
2412                             ->access<std::vector<std::string>>(
2413                                 mb_root(mboard) / "gpio" / bank / attr)
2414                             .set(attr_value);
2415                     } break;
2416                     default: {
2417                         const uint32_t current =
2418                             _tree->access<uint32_t>(
2419                                      mb_root(mboard) / "gpio" / bank / attr)
2420                                 .get();
2421                         const uint32_t new_value = (current & ~mask) | (value & mask);
2422                         _tree->access<uint32_t>(mb_root(mboard) / "gpio" / bank / attr)
2423                             .set(new_value);
2424                     } break;
2425                 }
2426                 return;
2427             } else {
2428                 throw uhd::runtime_error(str(
2429                     boost::format("The hardware has no gpio attribute: `%s':\n") % attr));
2430             }
2431         }
2432         if (bank.size() > 2 and bank[1] == 'X') {
2433             const std::string name          = bank.substr(2);
2434             const dboard_iface::unit_t unit = (bank[0] == 'R') ? dboard_iface::UNIT_RX
2435                                                                : dboard_iface::UNIT_TX;
2436             auto iface = _tree
2437                              ->access<dboard_iface::sptr>(
2438                                  mb_root(mboard) / "dboards" / name / "iface")
2439                              .get();
2440             if (attr == gpio_atr::gpio_attr_map.at(gpio_atr::GPIO_CTRL))
2441                 iface->set_pin_ctrl(unit, uint16_t(value), uint16_t(mask));
2442             if (attr == gpio_atr::gpio_attr_map.at(gpio_atr::GPIO_DDR))
2443                 iface->set_gpio_ddr(unit, uint16_t(value), uint16_t(mask));
2444             if (attr == gpio_atr::gpio_attr_map.at(gpio_atr::GPIO_OUT))
2445                 iface->set_gpio_out(unit, uint16_t(value), uint16_t(mask));
2446             if (attr == gpio_atr::gpio_attr_map.at(gpio_atr::GPIO_ATR_0X))
2447                 iface->set_atr_reg(
2448                     unit, gpio_atr::ATR_REG_IDLE, uint16_t(value), uint16_t(mask));
2449             if (attr == gpio_atr::gpio_attr_map.at(gpio_atr::GPIO_ATR_RX))
2450                 iface->set_atr_reg(
2451                     unit, gpio_atr::ATR_REG_RX_ONLY, uint16_t(value), uint16_t(mask));
2452             if (attr == gpio_atr::gpio_attr_map.at(gpio_atr::GPIO_ATR_TX))
2453                 iface->set_atr_reg(
2454                     unit, gpio_atr::ATR_REG_TX_ONLY, uint16_t(value), uint16_t(mask));
2455             if (attr == gpio_atr::gpio_attr_map.at(gpio_atr::GPIO_ATR_XX))
2456                 iface->set_atr_reg(
2457                     unit, gpio_atr::ATR_REG_FULL_DUPLEX, uint16_t(value), uint16_t(mask));
2458             if (attr == gpio_atr::gpio_attr_map.at(gpio_atr::GPIO_SRC)) {
2459                 throw uhd::runtime_error(
2460                     "Setting gpio source does not supported in daughter board.");
2461             }
2462             return;
2463         }
2464         throw uhd::runtime_error(
2465             str(boost::format("The hardware has no GPIO bank `%s'") % bank));
2466     }
2467 
get_gpio_attr(const std::string & bank,const std::string & attr,const size_t mboard)2468     uint32_t get_gpio_attr(
2469         const std::string& bank, const std::string& attr, const size_t mboard)
2470     {
2471         std::vector<std::string> str_val;
2472 
2473         if (_tree->exists(mb_root(mboard) / "gpio" / bank)) {
2474             if (_tree->exists(mb_root(mboard) / "gpio" / bank / attr)) {
2475                 const auto attr_type = gpio_atr::gpio_attr_rev_map.at(attr);
2476                 switch (attr_type) {
2477                     case gpio_atr::GPIO_SRC:
2478                         throw uhd::runtime_error(
2479                             "Can't set SRC attribute using integer value");
2480                     case gpio_atr::GPIO_CTRL:
2481                     case gpio_atr::GPIO_DDR: {
2482                         str_val = _tree
2483                                       ->access<std::vector<std::string>>(
2484                                           mb_root(mboard) / "gpio" / bank / attr)
2485                                       .get();
2486                         uint32_t val = 0;
2487                         for (size_t i = 0; i < str_val.size(); i++) {
2488                             val += usrp::gpio_atr::gpio_attr_value_pair.at(attr).at(
2489                                        str_val[i])
2490                                    << i;
2491                         }
2492                         return val;
2493                     }
2494                     default:
2495                         return uint32_t(
2496                             _tree->access<uint32_t>(
2497                                      mb_root(mboard) / "gpio" / bank / attr)
2498                                 .get());
2499                 }
2500                 return 0;
2501             } else {
2502                 throw uhd::runtime_error(str(
2503                     boost::format("The hardware has no gpio attribute: `%s'") % attr));
2504             }
2505         }
2506         if (bank.size() > 2 and bank[1] == 'X') {
2507             const std::string name          = bank.substr(2);
2508             const dboard_iface::unit_t unit = (bank[0] == 'R') ? dboard_iface::UNIT_RX
2509                                                                : dboard_iface::UNIT_TX;
2510             auto iface = _tree
2511                              ->access<dboard_iface::sptr>(
2512                                  mb_root(mboard) / "dboards" / name / "iface")
2513                              .get();
2514             if (attr == "CTRL")
2515                 return iface->get_pin_ctrl(unit);
2516             if (attr == "DDR")
2517                 return iface->get_gpio_ddr(unit);
2518             if (attr == "OUT")
2519                 return iface->get_gpio_out(unit);
2520             if (attr == "ATR_0X")
2521                 return iface->get_atr_reg(unit, gpio_atr::ATR_REG_IDLE);
2522             if (attr == "ATR_RX")
2523                 return iface->get_atr_reg(unit, gpio_atr::ATR_REG_RX_ONLY);
2524             if (attr == "ATR_TX")
2525                 return iface->get_atr_reg(unit, gpio_atr::ATR_REG_TX_ONLY);
2526             if (attr == "ATR_XX")
2527                 return iface->get_atr_reg(unit, gpio_atr::ATR_REG_FULL_DUPLEX);
2528             if (attr == "READBACK")
2529                 return iface->read_gpio(unit);
2530         }
2531         throw uhd::runtime_error(
2532             str(boost::format("The hardware has no gpio bank `%s'") % bank));
2533     }
2534 
2535     // The next four methods are only for RFNoC devices
get_gpio_src_banks(const size_t)2536     std::vector<std::string> get_gpio_src_banks(const size_t)
2537     {
2538         throw uhd::not_implemented_error(
2539             "get_gpio_src_banks() not implemented for this motherboard!");
2540     }
2541 
get_gpio_srcs(const std::string &,const size_t)2542     std::vector<std::string> get_gpio_srcs(const std::string&, const size_t)
2543     {
2544         throw uhd::not_implemented_error(
2545             "get_gpio_srcs() not implemented for this motherboard!");
2546     }
2547 
get_gpio_src(const std::string &,const size_t)2548     std::vector<std::string> get_gpio_src(const std::string&, const size_t)
2549     {
2550         throw uhd::not_implemented_error(
2551             "get_gpio_src() not implemented for this motherboard!");
2552     }
2553 
set_gpio_src(const std::string &,const std::vector<std::string> &,const size_t)2554     void set_gpio_src(const std::string&, const std::vector<std::string>&, const size_t)
2555     {
2556         throw uhd::not_implemented_error(
2557             "set_gpio_src() not implemented for this motherboard!");
2558     }
2559 
2560 private:
2561     device::sptr _dev;
2562     property_tree::sptr _tree;
2563 
2564     //! Container for spp values set in set_rx_spp()
2565     std::unordered_map<size_t, size_t> _rx_spp;
2566 
2567     struct mboard_chan_pair
2568     {
2569         size_t mboard, chan;
mboard_chan_pairmulti_usrp_impl::mboard_chan_pair2570         mboard_chan_pair(void) : mboard(0), chan(0) {}
2571     };
2572 
rx_chan_to_mcp(size_t chan)2573     mboard_chan_pair rx_chan_to_mcp(size_t chan)
2574     {
2575         mboard_chan_pair mcp;
2576         mcp.chan = chan;
2577         for (mcp.mboard = 0; mcp.mboard < get_num_mboards(); mcp.mboard++) {
2578             size_t sss = get_rx_subdev_spec(mcp.mboard).size();
2579             if (mcp.chan < sss)
2580                 break;
2581             mcp.chan -= sss;
2582         }
2583         if (mcp.mboard >= get_num_mboards()) {
2584             throw uhd::index_error(str(
2585                 boost::format(
2586                     "multi_usrp: RX channel %u out of range for configured RX frontends")
2587                 % chan));
2588         }
2589         return mcp;
2590     }
2591 
tx_chan_to_mcp(size_t chan)2592     mboard_chan_pair tx_chan_to_mcp(size_t chan)
2593     {
2594         mboard_chan_pair mcp;
2595         mcp.chan = chan;
2596         for (mcp.mboard = 0; mcp.mboard < get_num_mboards(); mcp.mboard++) {
2597             size_t sss = get_tx_subdev_spec(mcp.mboard).size();
2598             if (mcp.chan < sss)
2599                 break;
2600             mcp.chan -= sss;
2601         }
2602         if (mcp.mboard >= get_num_mboards()) {
2603             throw uhd::index_error(str(
2604                 boost::format(
2605                     "multi_usrp: TX channel %u out of range for configured TX frontends")
2606                 % chan));
2607         }
2608         return mcp;
2609     }
2610 
mb_root(const size_t mboard)2611     fs_path mb_root(const size_t mboard)
2612     {
2613         try {
2614             const std::string tree_path = "/mboards/" + std::to_string(mboard);
2615             if (_tree->exists(tree_path)) {
2616                 return tree_path;
2617             } else {
2618                 throw uhd::index_error(str(
2619                     boost::format("multi_usrp::mb_root(%u) - path not found") % mboard));
2620             }
2621         } catch (const std::exception& e) {
2622             throw uhd::index_error(
2623                 str(boost::format("multi_usrp::mb_root(%u) - %s") % mboard % e.what()));
2624         }
2625     }
2626 
rx_dsp_root(const size_t chan)2627     fs_path rx_dsp_root(const size_t chan)
2628     {
2629         mboard_chan_pair mcp = rx_chan_to_mcp(chan);
2630         if (_tree->exists(mb_root(mcp.mboard) / "rx_chan_dsp_mapping")) {
2631             std::vector<size_t> map = _tree
2632                                           ->access<std::vector<size_t>>(
2633                                               mb_root(mcp.mboard) / "rx_chan_dsp_mapping")
2634                                           .get();
2635             UHD_ASSERT_THROW(map.size() > mcp.chan);
2636             mcp.chan = map[mcp.chan];
2637         }
2638 
2639         try {
2640             const std::string tree_path = mb_root(mcp.mboard) / "rx_dsps" / mcp.chan;
2641             if (_tree->exists(tree_path)) {
2642                 return tree_path;
2643             } else {
2644                 throw uhd::index_error(
2645                     str(boost::format(
2646                             "multi_usrp::rx_dsp_root(%u) - mcp(%u) - path not found")
2647                         % chan % mcp.chan));
2648             }
2649         } catch (const std::exception& e) {
2650             throw uhd::index_error(
2651                 str(boost::format("multi_usrp::rx_dsp_root(%u) - mcp(%u) - %s") % chan
2652                     % mcp.chan % e.what()));
2653         }
2654     }
2655 
tx_dsp_root(const size_t chan)2656     fs_path tx_dsp_root(const size_t chan)
2657     {
2658         mboard_chan_pair mcp = tx_chan_to_mcp(chan);
2659         if (_tree->exists(mb_root(mcp.mboard) / "tx_chan_dsp_mapping")) {
2660             std::vector<size_t> map = _tree
2661                                           ->access<std::vector<size_t>>(
2662                                               mb_root(mcp.mboard) / "tx_chan_dsp_mapping")
2663                                           .get();
2664             UHD_ASSERT_THROW(map.size() > mcp.chan);
2665             mcp.chan = map[mcp.chan];
2666         }
2667         try {
2668             const std::string tree_path = mb_root(mcp.mboard) / "tx_dsps" / mcp.chan;
2669             if (_tree->exists(tree_path)) {
2670                 return tree_path;
2671             } else {
2672                 throw uhd::index_error(
2673                     str(boost::format(
2674                             "multi_usrp::tx_dsp_root(%u) - mcp(%u) - path not found")
2675                         % chan % mcp.chan));
2676             }
2677         } catch (const std::exception& e) {
2678             throw uhd::index_error(
2679                 str(boost::format("multi_usrp::tx_dsp_root(%u) - mcp(%u) - %s") % chan
2680                     % mcp.chan % e.what()));
2681         }
2682     }
2683 
rx_fe_root(const size_t chan)2684     fs_path rx_fe_root(const size_t chan)
2685     {
2686         mboard_chan_pair mcp = rx_chan_to_mcp(chan);
2687         try {
2688             const subdev_spec_pair_t spec = get_rx_subdev_spec(mcp.mboard).at(mcp.chan);
2689             return mb_root(mcp.mboard) / "rx_frontends" / spec.db_name;
2690         } catch (const std::exception& e) {
2691             throw uhd::index_error(
2692                 str(boost::format("multi_usrp::rx_fe_root(%u) - mcp(%u) - %s") % chan
2693                     % mcp.chan % e.what()));
2694         }
2695     }
2696 
tx_fe_root(const size_t chan)2697     fs_path tx_fe_root(const size_t chan)
2698     {
2699         mboard_chan_pair mcp = tx_chan_to_mcp(chan);
2700         try {
2701             const subdev_spec_pair_t spec = get_tx_subdev_spec(mcp.mboard).at(mcp.chan);
2702             return mb_root(mcp.mboard) / "tx_frontends" / spec.db_name;
2703         } catch (const std::exception& e) {
2704             throw uhd::index_error(
2705                 str(boost::format("multi_usrp::tx_fe_root(%u) - mcp(%u) - %s") % chan
2706                     % mcp.chan % e.what()));
2707         }
2708     }
2709 
get_radio_index(const std::string slot_name)2710     size_t get_radio_index(const std::string slot_name)
2711     {
2712         if (slot_name == "A") {
2713             return 0;
2714         } else if (slot_name == "B") {
2715             return 1;
2716         } else if (slot_name == "C") {
2717             return 2;
2718         } else if (slot_name == "D") {
2719             return 3;
2720         } else {
2721             throw uhd::key_error(str(
2722                 boost::format("[multi_usrp]: radio slot name %s out of supported range.")
2723                 % slot_name));
2724         }
2725     }
2726 
rx_rf_fe_root(const size_t chan)2727     fs_path rx_rf_fe_root(const size_t chan)
2728     {
2729         mboard_chan_pair mcp = rx_chan_to_mcp(chan);
2730         try {
2731             const subdev_spec_pair_t spec = get_rx_subdev_spec(mcp.mboard).at(mcp.chan);
2732             return mb_root(mcp.mboard) / "dboards" / spec.db_name / "rx_frontends"
2733                    / spec.sd_name;
2734         } catch (const std::exception& e) {
2735             throw uhd::index_error(
2736                 str(boost::format("multi_usrp::rx_rf_fe_root(%u) - mcp(%u) - %s") % chan
2737                     % mcp.chan % e.what()));
2738         }
2739     }
2740 
tx_rf_fe_root(const size_t chan)2741     fs_path tx_rf_fe_root(const size_t chan)
2742     {
2743         mboard_chan_pair mcp = tx_chan_to_mcp(chan);
2744         try {
2745             const subdev_spec_pair_t spec = get_tx_subdev_spec(mcp.mboard).at(mcp.chan);
2746             return mb_root(mcp.mboard) / "dboards" / spec.db_name / "tx_frontends"
2747                    / spec.sd_name;
2748         } catch (const std::exception& e) {
2749             throw uhd::index_error(
2750                 str(boost::format("multi_usrp::tx_rf_fe_root(%u) - mcp(%u) - %s") % chan
2751                     % mcp.chan % e.what()));
2752         }
2753     }
2754 
rx_gain_group(size_t chan)2755     gain_group::sptr rx_gain_group(size_t chan)
2756     {
2757         mboard_chan_pair mcp          = rx_chan_to_mcp(chan);
2758         const subdev_spec_pair_t spec = get_rx_subdev_spec(mcp.mboard).at(mcp.chan);
2759         gain_group::sptr gg           = gain_group::make();
2760         for (const std::string& name :
2761             _tree->list(mb_root(mcp.mboard) / "rx_codecs" / spec.db_name / "gains")) {
2762             gg->register_fcns("ADC-" + name,
2763                 make_gain_fcns_from_subtree(_tree->subtree(
2764                     mb_root(mcp.mboard) / "rx_codecs" / spec.db_name / "gains" / name)),
2765                 0 /* low prio */);
2766         }
2767         for (const std::string& name : _tree->list(rx_rf_fe_root(chan) / "gains")) {
2768             gg->register_fcns(name,
2769                 make_gain_fcns_from_subtree(
2770                     _tree->subtree(rx_rf_fe_root(chan) / "gains" / name)),
2771                 1 /* high prio */);
2772         }
2773         return gg;
2774     }
2775 
tx_gain_group(size_t chan)2776     gain_group::sptr tx_gain_group(size_t chan)
2777     {
2778         mboard_chan_pair mcp          = tx_chan_to_mcp(chan);
2779         const subdev_spec_pair_t spec = get_tx_subdev_spec(mcp.mboard).at(mcp.chan);
2780         gain_group::sptr gg           = gain_group::make();
2781         for (const std::string& name :
2782             _tree->list(mb_root(mcp.mboard) / "tx_codecs" / spec.db_name / "gains")) {
2783             gg->register_fcns("DAC-" + name,
2784                 make_gain_fcns_from_subtree(_tree->subtree(
2785                     mb_root(mcp.mboard) / "tx_codecs" / spec.db_name / "gains" / name)),
2786                 1 /* high prio */);
2787         }
2788         for (const std::string& name : _tree->list(tx_rf_fe_root(chan) / "gains")) {
2789             gg->register_fcns(name,
2790                 make_gain_fcns_from_subtree(
2791                     _tree->subtree(tx_rf_fe_root(chan) / "gains" / name)),
2792                 0 /* low prio */);
2793         }
2794         return gg;
2795     }
2796 
2797     //! \param is_tx True for tx
2798     // Assumption is that all mboards use the same link
2799     // and that the rate sum is evenly distributed among the mboards
_check_link_rate(const stream_args_t & args,bool is_tx)2800     bool _check_link_rate(const stream_args_t& args, bool is_tx)
2801     {
2802         bool link_rate_is_ok    = true;
2803         size_t bytes_per_sample = convert::get_bytes_per_item(
2804             args.otw_format.empty() ? "sc16" : args.otw_format);
2805         double max_link_rate = 0;
2806         double sum_rate      = 0;
2807         for (const size_t chan : args.channels) {
2808             mboard_chan_pair mcp = is_tx ? tx_chan_to_mcp(chan) : rx_chan_to_mcp(chan);
2809             if (_tree->exists(mb_root(mcp.mboard) / "link_max_rate")) {
2810                 max_link_rate = std::max(max_link_rate,
2811                     _tree->access<double>(mb_root(mcp.mboard) / "link_max_rate").get());
2812             }
2813             sum_rate += is_tx ? get_tx_rate(chan) : get_rx_rate(chan);
2814         }
2815         sum_rate /= get_num_mboards();
2816         if (max_link_rate > 0 and (max_link_rate / bytes_per_sample) < sum_rate) {
2817             UHD_LOGGER_WARNING("MULTI_USRP")
2818                 << boost::format("The total sum of rates (%f MSps on %u channels) "
2819                                  "exceeds the maximum capacity of the connection.\n"
2820                                  "This can cause %s.")
2821                        % (sum_rate / 1e6) % args.channels.size()
2822                        % (is_tx ? "underruns (U)" : "overflows (O)");
2823             link_rate_is_ok = false;
2824         }
2825 
2826         return link_rate_is_ok;
2827     }
2828 };
2829 
~multi_usrp(void)2830 multi_usrp::~multi_usrp(void)
2831 {
2832     /* NOP */
2833 }
2834 
2835 
2836 /***********************************************************************
2837  * The Make Function
2838  **********************************************************************/
2839 namespace uhd { namespace rfnoc { namespace detail {
2840 // Forward declare
2841 multi_usrp::sptr make_rfnoc_device(
2842     detail::rfnoc_device::sptr rfnoc_device, const uhd::device_addr_t& dev_addr);
2843 }}} // namespace uhd::rfnoc::detail
2844 
2845 
make(const device_addr_t & dev_addr)2846 multi_usrp::sptr multi_usrp::make(const device_addr_t& dev_addr)
2847 {
2848     UHD_LOGGER_TRACE("MULTI_USRP")
2849         << "multi_usrp::make with args " << dev_addr.to_pp_string();
2850 
2851     device::sptr dev = device::make(dev_addr, device::USRP);
2852 
2853     auto rfnoc_dev = std::dynamic_pointer_cast<rfnoc::detail::rfnoc_device>(dev);
2854     if (rfnoc_dev) {
2855         return rfnoc::detail::make_rfnoc_device(rfnoc_dev, dev_addr);
2856     }
2857     return std::make_shared<multi_usrp_impl>(dev);
2858 }
2859