diff options
Diffstat (limited to 'host/lib/include/uhdlib/usrp/dboard/zbx/zbx_expert.hpp')
-rw-r--r-- | host/lib/include/uhdlib/usrp/dboard/zbx/zbx_expert.hpp | 837 |
1 files changed, 837 insertions, 0 deletions
diff --git a/host/lib/include/uhdlib/usrp/dboard/zbx/zbx_expert.hpp b/host/lib/include/uhdlib/usrp/dboard/zbx/zbx_expert.hpp new file mode 100644 index 000000000..f386a4fdb --- /dev/null +++ b/host/lib/include/uhdlib/usrp/dboard/zbx/zbx_expert.hpp @@ -0,0 +1,837 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include "zbx_constants.hpp" +#include "zbx_cpld_ctrl.hpp" +#include "zbx_lo_ctrl.hpp" +#include <uhd/cal/container.hpp> +#include <uhd/cal/dsa_cal.hpp> +#include <uhd/property_tree.hpp> +#include <uhd/types/ranges.hpp> +#include <uhdlib/experts/expert_nodes.hpp> +#include <uhdlib/rfnoc/rf_control/gain_profile_iface.hpp> +#include <uhdlib/usrp/common/pwr_cal_mgr.hpp> +#include <uhdlib/usrp/common/rpc.hpp> +#include <uhdlib/usrp/common/x400_rfdc_control.hpp> +#include <uhdlib/utils/rpc.hpp> +#include <cmath> +#include <memory> + +namespace uhd { namespace usrp { namespace zbx { + +namespace { + +//! Depending on the given \p lo_step_size, this will return a valid frequency +// range on a quantized grid for the the LOs. The lower limit of this range will +// never be smaller than LMX2572_MIN_FREQ and the upper frequency will never be +// larger than LMX2572_MAX_FREQ. All frequencies will be integer multiples of +// the given \p lo_step_size. +uhd::freq_range_t _get_quantized_lo_range(const double lo_step_size) +{ + const double start = std::ceil(LMX2572_MIN_FREQ / lo_step_size) * lo_step_size; + const double stop = std::floor(LMX2572_MAX_FREQ / lo_step_size) * lo_step_size; + UHD_ASSERT_THROW(start >= LMX2572_MIN_FREQ); + UHD_ASSERT_THROW(stop <= LMX2572_MAX_FREQ); + return uhd::freq_range_t(start, stop, lo_step_size); +} + +} // namespace + +/*!--------------------------------------------------------- + * zbx_scheduling_expert + * + * This expert is responsible for scheduling time sensitive actions + * in other experts. It responds to changes in the command time and + * selectively causes experts to run in order to ensure a synchronized + * system. + * + * There is one scheduling expert per channel, they are shared between RX and TX. + * So, 2 scheduling experts total per radio block. + * --------------------------------------------------------- + */ +class zbx_scheduling_expert : public experts::worker_node_t +{ +public: + zbx_scheduling_expert(const experts::node_retriever_t& db, const uhd::fs_path fe_path) + : experts::worker_node_t(fe_path / "zbx_scheduling_expert") + , _command_time(db, fe_path / "time/cmd") + , _frontend_time(db, fe_path / "time/fe") + { + bind_accessor(_command_time); + bind_accessor(_frontend_time); + } + +private: + virtual void resolve(); + + // Inputs + experts::data_reader_t<time_spec_t> _command_time; + + // Outputs + experts::data_writer_t<time_spec_t> _frontend_time; +}; + +/*!--------------------------------------------------------- + * zbx_freq_fe_expert (Frequency Front-end Expert) + * + * This expert is responsible for responding to user requests for center frequency tuning + * + * This should trigger: + * - relevant LO experts + * - adjacent MPM expert + * - adjacent CPLD (tx/rx) Programming expert + * After all of the above, the Frequency Backend expert should be triggered to returned + * the coerced center frequency + * + * One instance of this expert is required for each combination of Direction (TX/RX) and + * Channel (0,1); four total + * -------------------------------------------------------- + */ +class zbx_freq_fe_expert : public uhd::experts::worker_node_t +{ +public: + zbx_freq_fe_expert(const uhd::experts::node_retriever_t& db, + const uhd::fs_path fe_path, + const uhd::direction_t trx, + const size_t chan, + const double rfdc_rate, + const double lo_step_size) + : experts::worker_node_t(fe_path / "zbx_freq_fe_expert") + , _desired_frequency(db, fe_path / "freq" / "desired") + , _desired_lo1_frequency(db, fe_path / "los" / ZBX_LO1 / "freq" / "value" / "desired") + , _desired_lo2_frequency(db, fe_path / "los" / ZBX_LO2 / "freq" / "value" / "desired") + , _lo1_enabled(db, fe_path / ZBX_LO1 / "enabled") + , _lo2_enabled(db, fe_path / ZBX_LO2 / "enabled") + , _desired_if2_frequency(db, fe_path / "if_freq" / "desired") + , _band_inverted(db, fe_path / "band_inverted") + , _is_highband(db, fe_path / "is_highband") + , _mixer1_m(db, fe_path / "mixer1_m") + , _mixer1_n(db, fe_path / "mixer1_n") + , _mixer2_m(db, fe_path / "mixer2_m") + , _mixer2_n(db, fe_path / "mixer2_n") + , _rf_filter(db, fe_path / "rf" / "filter") + , _if1_filter(db, fe_path / "if1" / "filter") + , _if2_filter(db, fe_path / "if2" / "filter") + , _rfdc_rate(rfdc_rate) + , _lo_freq_range(_get_quantized_lo_range(lo_step_size)) + , _trx(trx) + , _chan(chan) + { + // Inputs + bind_accessor(_desired_frequency); + + // Outputs + bind_accessor(_desired_lo1_frequency); + bind_accessor(_desired_lo2_frequency); + bind_accessor(_lo1_enabled); + bind_accessor(_lo2_enabled); + bind_accessor(_desired_if2_frequency); + bind_accessor(_band_inverted); + bind_accessor(_is_highband); + bind_accessor(_mixer1_m); + bind_accessor(_mixer1_n); + bind_accessor(_mixer2_m); + bind_accessor(_mixer2_n); + bind_accessor(_rf_filter); + bind_accessor(_if1_filter); + bind_accessor(_if2_filter); + } + +private: + void resolve() override; + + // Inputs from user/API + uhd::experts::data_reader_t<double> _desired_frequency; + + // Outputs + // From calculation, to LO expert + uhd::experts::data_writer_t<double> _desired_lo1_frequency; + uhd::experts::data_writer_t<double> _desired_lo2_frequency; + uhd::experts::data_writer_t<bool> _lo1_enabled; + uhd::experts::data_writer_t<bool> _lo2_enabled; + // From calculation, to MPM/RPC expert + uhd::experts::data_writer_t<double> _desired_if2_frequency; + uhd::experts::data_writer_t<bool> _band_inverted; + // From calculation, to Frequency Backend expert + uhd::experts::data_writer_t<bool> _is_highband; + uhd::experts::data_writer_t<int> _mixer1_m; + uhd::experts::data_writer_t<int> _mixer1_n; + uhd::experts::data_writer_t<int> _mixer2_m; + uhd::experts::data_writer_t<int> _mixer2_n; + // From calculation, to CPLD Programming expert + uhd::experts::data_writer_t<int> _rf_filter; + uhd::experts::data_writer_t<int> _if1_filter; + uhd::experts::data_writer_t<int> _if2_filter; + + const double _rfdc_rate; + const uhd::freq_range_t _lo_freq_range; + tune_map_item_t _tune_settings; + // Channel properties + const uhd::direction_t _trx; + const size_t _chan; +}; + +/*!--------------------------------------------------------- + * zbx_freq_be_expert (Frequency Back-end Expert) + * + * This expert is responsible for calculating the final coerced frequency and returning it + * to the user + * + * This should trigger: + * - adjacent gain expert + * + * One instance of this expert is required for each combination of Direction (TX/RX) and + * Channel (0,1); four total + * -------------------------------------------------------- + */ +class zbx_freq_be_expert : public uhd::experts::worker_node_t +{ +public: + zbx_freq_be_expert(const uhd::experts::node_retriever_t& db, + const uhd::fs_path fe_path, + const uhd::direction_t trx, + const size_t chan) + : uhd::experts::worker_node_t(fe_path / "zbx_freq_be_expert") + , _coerced_lo1_frequency(db, fe_path / "los" / ZBX_LO1 / "freq" / "value" / "coerced") + , _coerced_lo2_frequency(db, fe_path / "los" / ZBX_LO2 / "freq" / "value" / "coerced") + , _coerced_if2_frequency(db, fe_path / "if_freq" / "coerced") + , _is_highband(db, fe_path / "is_highband") + , _mixer1_m(db, fe_path / "mixer1_m") + , _mixer1_n(db, fe_path / "mixer1_n") + , _mixer2_m(db, fe_path / "mixer2_m") + , _mixer2_n(db, fe_path / "mixer2_n") + , _coerced_frequency(db, fe_path / "freq" / "coerced") + , _trx(trx) + , _chan(chan) + { + // Inputs + bind_accessor(_coerced_lo1_frequency); + bind_accessor(_coerced_lo2_frequency); + bind_accessor(_coerced_if2_frequency); + bind_accessor(_is_highband); + bind_accessor(_mixer1_m); + bind_accessor(_mixer1_n); + bind_accessor(_mixer2_m); + bind_accessor(_mixer2_n); + + // Outputs + bind_accessor(_coerced_frequency); + } + +private: + void resolve() override; + + // Inputs from LO expert(s) + uhd::experts::data_reader_t<double> _coerced_lo1_frequency; + uhd::experts::data_reader_t<double> _coerced_lo2_frequency; + // Input from MPM/RPC expert + uhd::experts::data_reader_t<double> _coerced_if2_frequency; + uhd::experts::data_reader_t<bool> _is_highband; + // Input from Frequency FE + uhd::experts::data_reader_t<int> _mixer1_m; + uhd::experts::data_reader_t<int> _mixer1_n; + uhd::experts::data_reader_t<int> _mixer2_m; + uhd::experts::data_reader_t<int> _mixer2_n; + + // Output to user/API + uhd::experts::data_writer_t<double> _coerced_frequency; + + // Channel properties + const uhd::direction_t _trx; + const size_t _chan; +}; + +/*!--------------------------------------------------------- + * zbx_lo_expert + * + * This expert is responsible for controlling one LO on the zbx + * note: LO source control is handled by the CPLD Programming Expert + * + * This should trigger: + * - Relevant (tx/rx, channel) Frequency Back-end Expert + * + * One instance of this expert is required for each LO (lo1, lo2) per Direction (TX/RX) + * and Channel (0,1); eight total + * -------------------------------------------------------- + */ +class zbx_lo_expert : public uhd::experts::worker_node_t +{ +public: + zbx_lo_expert(const uhd::experts::node_retriever_t& db, + const uhd::fs_path fe_path, + const uhd::direction_t trx, + const size_t chan, + const std::string lo, + std::shared_ptr<zbx_lo_ctrl> zbx_lo_ctrl) + : uhd::experts::worker_node_t(fe_path / "zbx_" + lo + "_expert") + , _desired_lo_frequency(db, fe_path / "los" / lo / "freq" / "value" / "desired") + , _set_is_enabled(db, fe_path / lo / "enabled") + , _test_mode_enabled(db, fe_path / lo / "test_mode") + , _coerced_lo_frequency(db, fe_path / "los" / lo / "freq" / "value" / "coerced") + , _lo_ctrl(zbx_lo_ctrl) + , _trx(trx) + , _chan(chan) + { + bind_accessor(_desired_lo_frequency); + bind_accessor(_test_mode_enabled); + bind_accessor(_set_is_enabled); + bind_accessor(_coerced_lo_frequency); + } + +private: + void resolve() override; + + // Inputs from Frequency FE expert or user/API + uhd::experts::data_reader_t<double> _desired_lo_frequency; + uhd::experts::data_reader_t<bool> _set_is_enabled; + // Inputs from user/API + uhd::experts::data_reader_t<bool> _test_mode_enabled; + + // Outputs to Frequency BE expert or user/API + uhd::experts::data_writer_t<double> _coerced_lo_frequency; + + std::shared_ptr<zbx_lo_ctrl> _lo_ctrl; + const uhd::direction_t _trx; + const size_t _chan; +}; + + +/*! DSA coercer expert + * + * Knows how to coerce a DSA value. + */ +class zbx_gain_coercer_expert : public uhd::experts::worker_node_t +{ +public: + zbx_gain_coercer_expert(const uhd::experts::node_retriever_t& db, + const uhd::fs_path gain_path, + const uhd::meta_range_t valid_range) + : uhd::experts::worker_node_t(gain_path / "zbx_gain_coercer_expert") + , _gain_desired(db, gain_path / "desired") + , _gain_coerced(db, gain_path / "coerced") + , _valid_range(valid_range) + { + bind_accessor(_gain_desired); + bind_accessor(_gain_coerced); + } + +private: + void resolve() override; + // Input + uhd::experts::data_reader_t<double> _gain_desired; + // Output + uhd::experts::data_writer_t<double> _gain_coerced; + // Attributes + const uhd::meta_range_t _valid_range; +}; + +/*!--------------------------------------------------------- + * zbx_tx_gain_expert (TX Gain Expert) + * + * This expert is responsible for controlling the gain of each TX channel. + * If the gain profile is set to default, then it will look up the corresponding + * amp and DSA values and write them to those nodes. + * + * This should trigger: + * - Adjacent CPLD TX Programming Expert + * + * One instance of this expert is required for each TX Channel (0,1); two total + * -------------------------------------------------------- + */ +class zbx_tx_gain_expert : public uhd::experts::worker_node_t +{ +public: + zbx_tx_gain_expert(const uhd::experts::node_retriever_t& db, + const uhd::fs_path fe_path, + const size_t chan, + uhd::usrp::pwr_cal_mgr::sptr power_mgr, + uhd::usrp::cal::zbx_tx_dsa_cal::sptr dsa_cal) + : uhd::experts::worker_node_t(fe_path / "zbx_gain_expert") + , _gain_in(db, fe_path / "gains" / ZBX_GAIN_STAGE_ALL / "value" / "desired") + , _profile(db, fe_path / "gains" / "all" / "profile") + , _frequency(db, fe_path / "freq" / "coerced") + , _gain_out(db, fe_path / "gains" / ZBX_GAIN_STAGE_ALL / "value" / "coerced") + , _dsa1(db, fe_path / "gains" / ZBX_GAIN_STAGE_DSA1 / "value" / "desired") + , _dsa2(db, fe_path / "gains" / ZBX_GAIN_STAGE_DSA2 / "value" / "desired") + , _amp_gain(db, fe_path / "gains" / ZBX_GAIN_STAGE_AMP / "value" / "desired") + , _power_mgr(power_mgr) + , _dsa_cal(dsa_cal) + , _chan(chan) + { + bind_accessor(_gain_in); + bind_accessor(_profile); + bind_accessor(_frequency); + bind_accessor(_gain_out); + bind_accessor(_dsa1); + bind_accessor(_dsa2); + bind_accessor(_amp_gain); + } + +private: + void resolve() override; + void _set_tx_dsa(const std::string, const uint8_t desired_gain); + double _set_tx_amp_by_gain(const double gain); + // Inputs from user/API + uhd::experts::data_reader_t<double> _gain_in; + // Inputs for DSA calibration + uhd::experts::data_reader_t<std::string> _profile; + uhd::experts::data_reader_t<double> _frequency; + + // Output to user/API + uhd::experts::data_writer_t<double> _gain_out; + // Outputs to CPLD programming expert + uhd::experts::data_writer_t<double> _dsa1; + uhd::experts::data_writer_t<double> _dsa2; + uhd::experts::data_writer_t<double> _amp_gain; + + uhd::usrp::pwr_cal_mgr::sptr _power_mgr; + uhd::usrp::cal::zbx_tx_dsa_cal::sptr _dsa_cal; + const size_t _chan; +}; + +/*!--------------------------------------------------------- + * zbx_rx_gain_expert (RX Gain Expert) + * + * This expert is responsible for controlling the gain of each RX channel + * + * This should trigger: + * - Adjacent CPLD RX Programming Expert + * + * One instance of this expert is required for each RX Channel (0,1); two total + * -------------------------------------------------------- + */ +class zbx_rx_gain_expert : public uhd::experts::worker_node_t +{ +public: + zbx_rx_gain_expert(const uhd::experts::node_retriever_t& db, + const uhd::fs_path fe_path, + const size_t chan, + uhd::usrp::pwr_cal_mgr::sptr power_mgr, + uhd::usrp::cal::zbx_rx_dsa_cal::sptr dsa_cal) + : uhd::experts::worker_node_t(fe_path / "zbx_gain_expert") + , _gain_in(db, fe_path / "gains" / ZBX_GAIN_STAGE_ALL / "value" / "desired") + , _profile(db, fe_path / "gains" / "all" / "profile") + , _frequency(db, fe_path / "freq" / "coerced") + , _gain_out(db, fe_path / "gains" / ZBX_GAIN_STAGE_ALL / "value" / "coerced") + , _dsa1(db, fe_path / "gains" / ZBX_GAIN_STAGE_DSA1 / "value" / "desired") + , _dsa2(db, fe_path / "gains" / ZBX_GAIN_STAGE_DSA2 / "value" / "desired") + , _dsa3a(db, fe_path / "gains" / ZBX_GAIN_STAGE_DSA3A / "value" / "desired") + , _dsa3b(db, fe_path / "gains" / ZBX_GAIN_STAGE_DSA3B / "value" / "desired") + , _power_mgr(power_mgr) + , _dsa_cal(dsa_cal) + , _chan(chan) + { + bind_accessor(_gain_in); + bind_accessor(_profile); + bind_accessor(_frequency); + bind_accessor(_gain_out); + bind_accessor(_dsa1); + bind_accessor(_dsa2); + bind_accessor(_dsa3a); + bind_accessor(_dsa3b); + } + +private: + void resolve() override; + + // Inputs from user/API + uhd::experts::data_reader_t<double> _gain_in; + uhd::experts::data_reader_t<std::string> _profile; + // Inputs for dsa calibration + uhd::experts::data_reader_t<double> _frequency; + + // Output to user/API + uhd::experts::data_writer_t<double> _gain_out; + // Outputs to CPLD programming expert + uhd::experts::data_writer_t<double> _dsa1; + uhd::experts::data_writer_t<double> _dsa2; + uhd::experts::data_writer_t<double> _dsa3a; + uhd::experts::data_writer_t<double> _dsa3b; + + uhd::usrp::pwr_cal_mgr::sptr _power_mgr; + uhd::usrp::cal::zbx_rx_dsa_cal::sptr _dsa_cal; + const size_t _chan; +}; + +/*!--------------------------------------------------------- + * zbx_tx_programming_expert (TX CPLD Programming Expert) + * + * This expert is responsible for programming the ZBX CPLD with parameters determined by + * user input or other experts This includes antenna setting, gain/dsa steps, lo source + * control, rf filter settings + * + * This expert should not trigger any other experts, these are all blind parameters + * + * One instance of this expert is required for each TX Channel (0,1); two total + * -------------------------------------------------------- + */ +class zbx_tx_programming_expert : public uhd::experts::worker_node_t +{ +public: + zbx_tx_programming_expert(const uhd::experts::node_retriever_t& db, + const uhd::fs_path tx_fe_path, + const uhd::fs_path rx_fe_path, /*needed for shared command time*/ + const size_t chan, + uhd::usrp::cal::zbx_tx_dsa_cal::sptr dsa_cal, + std::shared_ptr<zbx_cpld_ctrl> cpld) + : experts::worker_node_t(tx_fe_path / "zbx_tx_programming_expert") + , _antenna(db, tx_fe_path / "antenna" / "value") + , _atr_mode(db, tx_fe_path / "atr_mode") + , _profile(db, tx_fe_path / "gains" / "all" / "profile") + , _command_time(db, rx_fe_path / "time" / "cmd") + , _frequency(db, tx_fe_path / "freq" / "coerced") + , _dsa1(db, tx_fe_path / "gains" / ZBX_GAIN_STAGE_DSA1 / "value" / "coerced") + , _dsa2(db, tx_fe_path / "gains" / ZBX_GAIN_STAGE_DSA2 / "value" / "coerced") + , _amp_gain(db, tx_fe_path / "gains" / ZBX_GAIN_STAGE_AMP / "value" / "coerced") + , _rf_filter(db, tx_fe_path / "rf" / "filter") + , _if1_filter(db, tx_fe_path / "if1" / "filter") + , _if2_filter(db, tx_fe_path / "if2" / "filter") + , _is_highband(db, tx_fe_path / "is_highband") + , _lo1_source(db, tx_fe_path / "ch" / ZBX_LO1 / "source") + , _lo2_source(db, tx_fe_path / "ch" / ZBX_LO2 / "source") + , _dsa_cal(dsa_cal) + , _cpld(cpld) + , _chan(chan) + { + bind_accessor(_antenna); + bind_accessor(_atr_mode); + bind_accessor(_profile); + bind_accessor(_command_time); + bind_accessor(_frequency); + bind_accessor(_dsa1); + bind_accessor(_dsa2); + bind_accessor(_amp_gain); + bind_accessor(_rf_filter); + bind_accessor(_if1_filter); + bind_accessor(_if2_filter); + bind_accessor(_is_highband); + bind_accessor(_lo1_source); + bind_accessor(_lo2_source); + } + +private: + void resolve() override; + + // Inputs from user/API + uhd::experts::data_reader_t<std::string> _antenna; + uhd::experts::data_reader_t<zbx_cpld_ctrl::atr_mode> _atr_mode; + uhd::experts::data_reader_t<std::string> _profile; + + // Inputs from the Frequency FE expert + // Note: this is just for node dependencies, we want to be notified if just the tune + // frequency has been changed. + uhd::experts::data_reader_t<time_spec_t> _command_time; + uhd::experts::data_reader_t<double> _frequency; + + // Inputs from Gain TX expert + uhd::experts::data_reader_t<double> _dsa1; + uhd::experts::data_reader_t<double> _dsa2; + uhd::experts::data_reader_t<double> _amp_gain; + + // Inputs from Frequency FE expert + uhd::experts::data_reader_t<int> _rf_filter; + uhd::experts::data_reader_t<int> _if1_filter; + uhd::experts::data_reader_t<int> _if2_filter; + uhd::experts::data_reader_t<bool> _is_highband; + // Inputs from LO expert(s) + uhd::experts::data_reader_t<zbx_lo_source_t> _lo1_source; + uhd::experts::data_reader_t<zbx_lo_source_t> _lo2_source; + + uhd::usrp::cal::zbx_tx_dsa_cal::sptr _dsa_cal; + // Expects constructed cpld control objects + std::shared_ptr<zbx_cpld_ctrl> _cpld; + const size_t _chan; +}; + +/*!--------------------------------------------------------- + * zbx_rx_programming_expert (RX CPLD Programming Expert) + * + * This expert is responsible for programming the ZBX CPLD with parameters determined by + * user input or other experts. + * This includes antenna setting, gain/dsa steps, lo source control, rf filter settings + * + * This expert should not trigger any other experts, these are all blind parameters + * + * One instance of this expert is required for each RX Channel (0,1); two total + * -------------------------------------------------------- + */ +class zbx_rx_programming_expert : public uhd::experts::worker_node_t +{ +public: + zbx_rx_programming_expert(const uhd::experts::node_retriever_t& db, + const uhd::fs_path fe_path, + const size_t chan, + uhd::usrp::cal::zbx_rx_dsa_cal::sptr dsa_cal, + std::shared_ptr<zbx_cpld_ctrl> cpld) + : experts::worker_node_t(fe_path / "zbx_rx_programming_expert") + , _antenna(db, fe_path / "antenna" / "value") + , _atr_mode(db, fe_path / "atr_mode") + , _profile(db, fe_path / "gains" / "all" / "profile") + , _command_time(db, fe_path / "time" / "cmd") + , _frequency(db, fe_path / "freq" / "coerced") + , _dsa1(db, fe_path / "gains" / ZBX_GAIN_STAGE_DSA1 / "value" / "coerced") + , _dsa2(db, fe_path / "gains" / ZBX_GAIN_STAGE_DSA2 / "value" / "coerced") + , _dsa3a(db, fe_path / "gains" / ZBX_GAIN_STAGE_DSA3A / "value" / "coerced") + , _dsa3b(db, fe_path / "gains" / ZBX_GAIN_STAGE_DSA3B / "value" / "coerced") + , _rf_filter(db, fe_path / "rf" / "filter") + , _if1_filter(db, fe_path / "if1" / "filter") + , _if2_filter(db, fe_path / "if2" / "filter") + , _is_highband(db, fe_path / "is_highband") + , _lo1_source(db, fe_path / "ch" / ZBX_LO1 / "source") + , _lo2_source(db, fe_path / "ch" / ZBX_LO2 / "source") + , _dsa_cal(dsa_cal) + , _cpld(cpld) + , _chan(chan) + { + bind_accessor(_antenna); + bind_accessor(_atr_mode); + bind_accessor(_profile); + bind_accessor(_command_time); + bind_accessor(_frequency); + bind_accessor(_dsa1); + bind_accessor(_dsa2); + bind_accessor(_dsa3a); + bind_accessor(_dsa3b); + bind_accessor(_rf_filter); + bind_accessor(_if1_filter); + bind_accessor(_if2_filter); + bind_accessor(_is_highband); + bind_accessor(_lo1_source); + bind_accessor(_lo2_source); + } + +private: + void resolve() override; + void _update_leds(); + + // Inputs from user/API + uhd::experts::data_reader_t<std::string> _antenna; + uhd::experts::data_reader_t<zbx_cpld_ctrl::atr_mode> _atr_mode; + uhd::experts::data_reader_t<std::string> _profile; + + // Inputs from the Frequency FE expert + // Note: this is just for node dependencies, we want to be notified if just the tune + // frequency has been changed. + uhd::experts::data_reader_t<time_spec_t> _command_time; + uhd::experts::data_reader_t<double> _frequency; + + // Inputs from Gain expert + uhd::experts::data_reader_t<double> _dsa1; + uhd::experts::data_reader_t<double> _dsa2; + uhd::experts::data_reader_t<double> _dsa3a; + uhd::experts::data_reader_t<double> _dsa3b; + + // Inputs from Frequency FE expert + uhd::experts::data_reader_t<int> _rf_filter; + uhd::experts::data_reader_t<int> _if1_filter; + uhd::experts::data_reader_t<int> _if2_filter; + uhd::experts::data_reader_t<bool> _is_highband; + // Inputs from LO expert(s) + uhd::experts::data_reader_t<zbx_lo_source_t> _lo1_source; + uhd::experts::data_reader_t<zbx_lo_source_t> _lo2_source; + + uhd::usrp::cal::zbx_rx_dsa_cal::sptr _dsa_cal; + // Expects constructed cpld control objects + std::shared_ptr<zbx_cpld_ctrl> _cpld; + const size_t _chan; +}; + +/*!--------------------------------------------------------- + * zbx_band_inversion_expert + * + * This expert is responsible for handling the band inversion calls to MPM on the target + * device + * + * This expert should not trigger any others + * + * One instance of this expert is required for each Direction (TX/RX) and Channel (0,1); + * four total + * -------------------------------------------------------- + */ +class zbx_band_inversion_expert : public uhd::experts::worker_node_t +{ +public: + zbx_band_inversion_expert(const uhd::experts::node_retriever_t& db, + const uhd::fs_path fe_path, + const uhd::direction_t trx, + const size_t chan, + const int db_idx, + uhd::usrp::zbx_rpc_iface::sptr rpcc) + : uhd::experts::worker_node_t(fe_path / "zbx_band_inversion_expert") + , _is_band_inverted(db, fe_path / "band_inverted") + , _db_idx(db_idx) + , _rpcc(rpcc) + , _trx(trx) + , _chan(chan) + { + bind_accessor(_is_band_inverted); + } + +private: + void resolve() override; + + // Inputs from Frequency FE expert + uhd::experts::data_reader_t<bool> _is_band_inverted; + + const size_t _db_idx; + uhd::usrp::zbx_rpc_iface::sptr _rpcc; + const uhd::direction_t _trx; + const size_t _chan; +}; + +/*!--------------------------------------------------------- + * zbx_rfdc_freq_expert + * + * This expert is responsible for handling any rfdc frequency calls to MPM on the target + * device + * + * This expert should not trigger any experts + * + * One instance of this expert is required for each Direction (TX/RX) and Channel (0,1); + * four total + * -------------------------------------------------------- + */ +class zbx_rfdc_freq_expert : public uhd::experts::worker_node_t +{ +public: + zbx_rfdc_freq_expert(const uhd::experts::node_retriever_t& db, + const uhd::fs_path fe_path, + const uhd::direction_t trx, + const size_t chan, + const std::string rpc_prefix, + int db_idx, + uhd::usrp::x400_rpc_iface::sptr rpcc) + : uhd::experts::worker_node_t(fe_path / "zbx_rfdc_freq_expert") + , _rfdc_freq_desired( + db, fe_path / "los" / RFDC_NCO / "freq" / "value" / "desired") + , _rfdc_freq_coerced( + db, fe_path / "los" / RFDC_NCO / "freq" / "value" / "coerced") + , _if2_frequency_desired(db, fe_path / "if_freq" / "desired") + , _if2_frequency_coerced(db, fe_path / "if_freq" / "coerced") + , _rpc_prefix(rpc_prefix) + , _db_idx(db_idx) + , _rpcc(rpcc) + , _trx(trx) + , _chan(chan) + { + bind_accessor(_rfdc_freq_desired); + bind_accessor(_rfdc_freq_coerced); + bind_accessor(_if2_frequency_desired); + bind_accessor(_if2_frequency_coerced); + } + +private: + void resolve() override; + + // Inputs from user/API + uhd::experts::data_reader_t<double> _rfdc_freq_desired; + + // Outputs to user/API + uhd::experts::data_writer_t<double> _rfdc_freq_coerced; + + + // Inputs from Frequency FE expert + uhd::experts::data_reader_t<double> _if2_frequency_desired; + + // Outputs to Frequency BE expert + uhd::experts::data_writer_t<double> _if2_frequency_coerced; + + + const std::string _rpc_prefix; + const size_t _db_idx; + uhd::usrp::x400_rpc_iface::sptr _rpcc; + const uhd::direction_t _trx; + const size_t _chan; +}; + +using uhd::rfnoc::x400::rfdc_control; +/*!--------------------------------------------------------- + * zbx_sync_expert + * + * This expert is responsible for handling the phase alignment. + * Per channel, there are up to 4 things whose phase need syncing: The two + * LOs, the NCO, and the ADC/DAC gearboxes. However, the LOs share a sync + * register, and so do the NCOs. To minimize writes, we thus need a single sync + * expert at the end of the graph, who combines all LOs and all NCOs. + * -------------------------------------------------------- + */ +class zbx_sync_expert : public uhd::experts::worker_node_t +{ +public: + zbx_sync_expert(const uhd::experts::node_retriever_t& db, + const uhd::fs_path tx_fe_path, + const uhd::fs_path rx_fe_path, + rfdc_control::sptr rfdcc, + std::shared_ptr<zbx_cpld_ctrl> cpld) + : uhd::experts::worker_node_t("zbx_sync_expert") + , _fe_time{{db, rx_fe_path / 0 / "time/fe"}, {db, rx_fe_path / 1 / "time/fe"}} + , _lo_freqs{{zbx_lo_t::RX0_LO1, + {db, rx_fe_path / 0 / "los" / ZBX_LO1 / "freq" / "value" / "coerced"}}, + {zbx_lo_t::RX0_LO2, + {db, rx_fe_path / 0 / "los" / ZBX_LO2 / "freq" / "value" / "coerced"}}, + {zbx_lo_t::TX0_LO1, + {db, tx_fe_path / 0 / "los" / ZBX_LO1 / "freq" / "value" / "coerced"}}, + {zbx_lo_t::TX0_LO2, + {db, tx_fe_path / 0 / "los" / ZBX_LO2 / "freq" / "value" / "coerced"}}, + {zbx_lo_t::RX1_LO1, + {db, rx_fe_path / 1 / "los" / ZBX_LO1 / "freq" / "value" / "coerced"}}, + {zbx_lo_t::RX1_LO2, + {db, rx_fe_path / 1 / "los" / ZBX_LO2 / "freq" / "value" / "coerced"}}, + {zbx_lo_t::TX1_LO1, + {db, tx_fe_path / 1 / "los" / ZBX_LO1 / "freq" / "value" / "coerced"}}, + {zbx_lo_t::TX1_LO2, + {db, tx_fe_path / 1 / "los" / ZBX_LO2 / "freq" / "value" / "coerced"}}} + , _nco_freqs{{rfdc_control::rfdc_type::RX0, + {db, rx_fe_path / 0 / "if_freq" / "coerced"}}, + {rfdc_control::rfdc_type::RX1, + {db, rx_fe_path / 1 / "if_freq" / "coerced"}}, + {rfdc_control::rfdc_type::TX0, + {db, tx_fe_path / 0 / "if_freq" / "coerced"}}, + {rfdc_control::rfdc_type::TX1, + {db, tx_fe_path / 1 / "if_freq" / "coerced"}}} + , _rfdcc(rfdcc) + , _cpld(cpld) + { + for (auto& fe_time : _fe_time) { + bind_accessor(fe_time); + } + for (auto& lo_freq : _lo_freqs) { + bind_accessor(lo_freq.second); + } + for (auto& nco_freq : _nco_freqs) { + bind_accessor(nco_freq.second); + } + } + +private: + void resolve() override; + + // Inputs from user/API + // Command time: We have 2 channels, one time spec per channel + std::vector<uhd::experts::data_reader_t<time_spec_t>> _fe_time; + // We have 8 LOs: + std::map<zbx_lo_t, uhd::experts::data_reader_t<double>> _lo_freqs; + // We have 4 NCOs + std::map<rfdc_control::rfdc_type, uhd::experts::data_reader_t<double>> _nco_freqs; + + // This expert has no outputs. + + // Attributes + rfdc_control::sptr _rfdcc; + std::shared_ptr<zbx_cpld_ctrl> _cpld; + //! Store the sync state of the ADC gearboxes. If false, we assume they're + // out of sync. This could also be a vector of booleans if we want to be + // able to sync ADC gearboxes individually. + bool _adcs_synced = false; + //! Store the sync state of the DAC gearboxes. If false, we assume they're + // out of sync. This could also be a vector of booleans if we want to be + // able to sync DAC gearboxes individually. + bool _dacs_synced = false; +}; + +}}} // namespace uhd::usrp::zbx |