//
// Copyright 2014-15 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
#include "twinrx/twinrx_experts.hpp"
#include "twinrx/twinrx_ctrl.hpp"
#include "twinrx/twinrx_io.hpp"
#include
#include
#include
#include
#include
#include
#include
#include
#include "dboard_ctor_args.hpp"
#include
#include
#include
#include
//#include //Needed for _expert->to_dot() below
using namespace uhd;
using namespace uhd::usrp;
using namespace uhd::usrp::dboard::twinrx;
using namespace uhd::experts;
static const dboard_id_t TWINRX_V100_000_ID(0x91);
static const dboard_id_t TWINRX_V100_100_ID(0x93);
/*!
* twinrx_rcvr_fe is the dbaord class (dboard_base) that
* represents each front-end of a TwinRX board. UHD will
* create and hold two instances of this class per TwinRX
* dboard.
*
*/
class twinrx_rcvr_fe : public rx_dboard_base
{
public:
twinrx_rcvr_fe(
ctor_args_t args,
expert_container::sptr expert,
twinrx_ctrl::sptr ctrl
) :
rx_dboard_base(args), _expert(expert), _ctrl(ctrl),
_ch_name(dboard_ctor_args_t::cast(args).sd_name)
{
//---------------------------------------------------------
// Add user-visible, channel specific properties to front-end tree
//---------------------------------------------------------
//Generic
get_rx_subtree()->create("name")
.set("TwinRX RX" + _ch_name);
get_rx_subtree()->create("use_lo_offset")
.set(false);
get_rx_subtree()->create("connection")
.set(_ch_name == "0" ? "II" : "QQ"); //Ch->ADC port mapping
static const double BW = 80e6;
get_rx_subtree()->create("bandwidth/value")
.set(BW);
get_rx_subtree()->create("bandwidth/range")
.set(freq_range_t(BW, BW));
// Command Time
expert_factory::add_data_node(_expert, prepend_ch("time/rx_frontend", _ch_name), time_spec_t(0.0));
expert_factory::add_prop_node(_expert, get_rx_subtree(),
"time/cmd", prepend_ch("time/cmd", _ch_name), time_spec_t(0.0));
//Frequency Specific
get_rx_subtree()->create("freq/range")
.set(freq_range_t(10e6, 6.0e9));
expert_factory::add_dual_prop_node(_expert, get_rx_subtree(),
"freq/value", prepend_ch("freq/desired", _ch_name), prepend_ch("freq/coerced", _ch_name),
1.0e9, AUTO_RESOLVE_ON_READ_WRITE);
get_rx_subtree()->create("tune_args")
.set(device_addr_t());
static const double DEFAULT_IF_FREQ = 150e6;
meta_range_t if_freq_range;
if_freq_range.push_back(range_t(-DEFAULT_IF_FREQ-(BW/2), -DEFAULT_IF_FREQ+(BW/2)));
if_freq_range.push_back(range_t( DEFAULT_IF_FREQ-(BW/2), DEFAULT_IF_FREQ+(BW/2)));
get_rx_subtree()->create("if_freq/range")
.set(if_freq_range);
expert_factory::add_dual_prop_node(_expert, get_rx_subtree(),
"if_freq/value", prepend_ch("if_freq/desired", _ch_name), prepend_ch("if_freq/coerced", _ch_name),
DEFAULT_IF_FREQ, AUTO_RESOLVE_ON_WRITE);
//LO Specific
get_rx_subtree()->create("los/LO1/freq/range")
.set(freq_range_t(2.0e9, 6.8e9));
expert_factory::add_dual_prop_node(_expert, get_rx_subtree(),
"los/LO1/freq/value", prepend_ch("los/LO1/freq/desired", _ch_name), prepend_ch("los/LO1/freq/coerced", _ch_name),
0.0, AUTO_RESOLVE_ON_READ_WRITE);
get_rx_subtree()->create("los/LO2/freq/range")
.set(freq_range_t(1.0e9, 3.0e9));
expert_factory::add_dual_prop_node(_expert, get_rx_subtree(),
"los/LO2/freq/value", prepend_ch("los/LO2/freq/desired", _ch_name), prepend_ch("los/LO2/freq/coerced", _ch_name),
0.0, AUTO_RESOLVE_ON_READ_WRITE);
get_rx_subtree()->create >("los/all/source/options")
.set(boost::assign::list_of("internal")("external")("companion")("disabled"));
expert_factory::add_prop_node(_expert, get_rx_subtree(),
"los/all/source/value", prepend_ch("los/all/source", _ch_name),
"internal", AUTO_RESOLVE_ON_WRITE);
expert_factory::add_prop_node(_expert, get_rx_subtree(),
"los/all/export", prepend_ch("los/all/export", _ch_name),
false, AUTO_RESOLVE_ON_WRITE);
//Gain Specific
get_rx_subtree()->create("gains/all/range")
.set(gain_range_t(0, 95, double(1.0)));
expert_factory::add_prop_node(_expert, get_rx_subtree(),
"gains/all/value", prepend_ch("gain", _ch_name),
0.0, AUTO_RESOLVE_ON_WRITE);
get_rx_subtree()->create >("gains/all/profile/options")
.set(boost::assign::list_of("low-noise")("low-distortion")("default"));
expert_factory::add_prop_node(_expert, get_rx_subtree(),
"gains/all/profile/value", prepend_ch("gain_profile", _ch_name),
"default", AUTO_RESOLVE_ON_WRITE);
//Antenna Specific
get_rx_subtree()->create >("antenna/options")
.set(boost::assign::list_of("RX1")("RX2"));
expert_factory::add_prop_node(_expert, get_rx_subtree(),
"antenna/value", prepend_ch("antenna", _ch_name),
(_ch_name == "0" ? "RX1" : "RX2"), AUTO_RESOLVE_ON_WRITE);
expert_factory::add_prop_node(_expert, get_rx_subtree(),
"enabled", prepend_ch("enabled", _ch_name),
false, AUTO_RESOLVE_ON_WRITE);
//Readback
get_rx_subtree()->create("sensors/lo_locked")
.set_publisher(boost::bind(&twinrx_rcvr_fe::get_lo_locked, this));
//---------------------------------------------------------
// Add internal channel-specific data nodes to expert
//---------------------------------------------------------
expert_factory::add_data_node(_expert,
prepend_ch("ch/LO1/inj_side", _ch_name), INJ_LOW_SIDE);
expert_factory::add_data_node(_expert,
prepend_ch("ch/LO2/inj_side", _ch_name), INJ_LOW_SIDE);
expert_factory::add_data_node(_expert,
prepend_ch("ch/signal_path", _ch_name), twinrx_ctrl::PATH_LOWBAND);
expert_factory::add_data_node(_expert,
prepend_ch("ch/lb_presel", _ch_name), twinrx_ctrl::PRESEL_PATH1);
expert_factory::add_data_node(_expert,
prepend_ch("ch/hb_presel", _ch_name), twinrx_ctrl::PRESEL_PATH1);
expert_factory::add_data_node(_expert,
prepend_ch("ch/lb_preamp_presel", _ch_name), false);
expert_factory::add_data_node(_expert,
prepend_ch("ant/lb_preamp_presel", _ch_name), false);
expert_factory::add_data_node(_expert,
prepend_ch("ch/preamp1", _ch_name), twinrx_ctrl::PREAMP_BYPASS);
expert_factory::add_data_node(_expert,
prepend_ch("ant/preamp1", _ch_name), twinrx_ctrl::PREAMP_BYPASS);
expert_factory::add_data_node(_expert,
prepend_ch("ch/preamp2", _ch_name), false);
expert_factory::add_data_node(_expert,
prepend_ch("ant/preamp2", _ch_name), false);
expert_factory::add_data_node(_expert,
prepend_ch("ch/input_atten", _ch_name), 0);
expert_factory::add_data_node(_expert,
prepend_ch("ant/input_atten", _ch_name), 0);
expert_factory::add_data_node(_expert,
prepend_ch("ch/lb_atten", _ch_name), 0);
expert_factory::add_data_node(_expert,
prepend_ch("ch/hb_atten", _ch_name), 0);
expert_factory::add_data_node(_expert,
prepend_ch("ch/LO1/source", _ch_name), twinrx_ctrl::LO_INTERNAL);
expert_factory::add_data_node(_expert,
prepend_ch("ch/LO2/source", _ch_name), twinrx_ctrl::LO_INTERNAL);
expert_factory::add_data_node(_expert,
prepend_ch("synth/LO1/mapping", _ch_name), MAPPING_NONE);
expert_factory::add_data_node(_expert,
prepend_ch("synth/LO2/mapping", _ch_name), MAPPING_NONE);
}
virtual ~twinrx_rcvr_fe(void)
{
}
sensor_value_t get_lo_locked()
{
bool locked = true;
twinrx_ctrl::channel_t ch = (_ch_name == "0") ? twinrx_ctrl::CH1 : twinrx_ctrl::CH2;
locked &= _ctrl->read_lo1_locked(ch);
locked &= _ctrl->read_lo2_locked(ch);
return sensor_value_t("LO", locked, "locked", "unlocked");
}
private:
expert_container::sptr _expert;
twinrx_ctrl::sptr _ctrl;
const std::string _ch_name;
};
/*!
* twinrx_rcvr is the top-level container for each
* TwinRX board. UHD will hold one instance of this
* class per TwinRX dboard. This class is responsible
* for owning all the control classes for the board.
*
*/
class twinrx_rcvr : public rx_dboard_base
{
public:
typedef boost::shared_ptr sptr;
twinrx_rcvr(ctor_args_t args) : rx_dboard_base(args)
{
_db_iface = get_iface();
twinrx_gpio::sptr gpio_iface = boost::make_shared(_db_iface);
twinrx_cpld_regmap::sptr cpld_regs = boost::make_shared();
cpld_regs->initialize(*gpio_iface, false);
_ctrl = twinrx_ctrl::make(_db_iface, gpio_iface, cpld_regs);
_expert = expert_factory::create_container("twinrx_expert");
}
virtual ~twinrx_rcvr(void)
{
}
inline expert_container::sptr get_expert() {
return _expert;
}
inline twinrx_ctrl::sptr get_ctrl() {
return _ctrl;
}
virtual void initialize()
{
//---------------------------------------------------------
// Add internal channel-agnostic data nodes to expert
//---------------------------------------------------------
expert_factory::add_data_node(_expert,
"com/LO1/export_source", twinrx_ctrl::LO_EXPORT_DISABLED);
expert_factory::add_data_node(_expert,
"com/LO2/export_source", twinrx_ctrl::LO_EXPORT_DISABLED);
expert_factory::add_data_node(_expert,
"com/ant_mapping", twinrx_ctrl::ANTX_NATIVE);
expert_factory::add_data_node(_expert,
"com/cal_mode", twinrx_ctrl::CAL_DISABLED);
expert_factory::add_data_node(_expert,
"com/synth/LO1/hopping_enabled", false);
expert_factory::add_data_node(_expert,
"com/synth/LO2/hopping_enabled", false);
//---------------------------------------------------------
// Add workers to expert
//---------------------------------------------------------
//Channel (front-end) specific
for(const std::string& fe: _fe_names) {
expert_factory::add_worker_node(_expert, _expert->node_retriever(), fe);
expert_factory::add_worker_node(_expert, _expert->node_retriever(), fe);
expert_factory::add_worker_node(_expert, _expert->node_retriever(), fe);
expert_factory::add_worker_node(_expert, _expert->node_retriever(), fe);
expert_factory::add_worker_node(_expert, _expert->node_retriever(), fe, _db_iface);
}
//Channel (front-end) agnostic
expert_factory::add_worker_node(_expert, _expert->node_retriever());
expert_factory::add_worker_node(_expert, _expert->node_retriever(), STAGE_LO1);
expert_factory::add_worker_node(_expert, _expert->node_retriever(), STAGE_LO2);
expert_factory::add_worker_node(_expert, _expert->node_retriever());
expert_factory::add_worker_node(_expert, _expert->node_retriever());
expert_factory::add_worker_node(_expert, _expert->node_retriever(), _ctrl);
/*//Expert debug code
std::ofstream dot_file("/tmp/twinrx.dot", std::ios::out);
dot_file << _expert->to_dot();
dot_file.close();
*/
_expert->debug_audit();
_expert->resolve_all(true);
}
static dboard_base::sptr make_twinrx_fe(dboard_base::ctor_args_t args)
{
const dboard_ctor_args_t& db_args = dboard_ctor_args_t::cast(args);
sptr container = boost::dynamic_pointer_cast(db_args.rx_container);
if (container) {
dboard_base::sptr fe = dboard_base::sptr(
new twinrx_rcvr_fe(args, container->get_expert(), container->get_ctrl()));
container->add_twinrx_fe(db_args.sd_name);
return fe;
} else {
throw uhd::assertion_error("error creating twinrx frontend");
}
}
protected:
inline void add_twinrx_fe(const std::string& name) {
_fe_names.push_back(name);
}
private:
typedef std::map twinrx_fe_map_t;
dboard_iface::sptr _db_iface;
twinrx_ctrl::sptr _ctrl;
std::vector _fe_names;
expert_container::sptr _expert;
};
/*!
* Initialization Sequence for each TwinRX board:
* - make_twinrx_container is called which creates an instance of twinrx_rcvr
* - twinrx_rcvr::make_twinrx_fe is called with channel "0" which creates an instance of twinrx_rcvr_fe
* - twinrx_rcvr::make_twinrx_fe is called with channel "1" which creates an instance of twinrx_rcvr_fe
* - twinrx_rcvr::initialize is called with finishes the init sequence
*
*/
static dboard_base::sptr make_twinrx_container(dboard_base::ctor_args_t args)
{
return dboard_base::sptr(new twinrx_rcvr(args));
}
UHD_STATIC_BLOCK(reg_twinrx_dboards)
{
dboard_manager::register_dboard_restricted(
TWINRX_V100_000_ID,
&twinrx_rcvr::make_twinrx_fe,
"TwinRX v1.0",
boost::assign::list_of("0")("1"),
&make_twinrx_container
);
dboard_manager::register_dboard_restricted(
TWINRX_V100_100_ID,
&twinrx_rcvr::make_twinrx_fe,
"TwinRX v1.1",
boost::assign::list_of("0")("1"),
&make_twinrx_container
);
}