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