// // Copyright 2017 Ettus Research (National Instruments Corp.) // // 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 "eiscat_radio_ctrl_impl.hpp" #include #include #include #include #include #include #include #include using namespace uhd; using namespace uhd::usrp; using namespace uhd::rfnoc; static const double EISCAT_TICK_RATE = 208e6; // Hz static const double EISCAT_RADIO_RATE = 104e6; // Hz static const double EISCAT_CENTER_FREQ = 104e6; // Hz static const double EISCAT_DEFAULT_GAIN = 0.0; // Hz static const double EISCAT_DEFAULT_BANDWIDTH = 52e6; // Hz static const char* EISCAT_ANTENNA_NAME = "Rx"; static const size_t EISCAT_NUM_CHANS = 5; static const size_t EISCAT_NUM_FRONTENDS = 16; UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(eiscat_radio_ctrl) { UHD_LOG_TRACE("EISCAT", "eiscat_radio_ctrl_impl::ctor() "); const size_t num_chans = get_output_ports().size(); UHD_ASSERT_THROW(num_chans == EISCAT_NUM_CHANS); UHD_LOG_TRACE("EISCAT", "Number of channels: " << num_chans); UHD_LOG_TRACE("EISCAT", "Setting tick rate to " << EISCAT_TICK_RATE/1e6 << " MHz"); /**** Configure the radio itself ***************************************/ radio_ctrl_impl::set_rate(EISCAT_TICK_RATE); for (size_t chan = 0; chan < num_chans; chan++) { radio_ctrl_impl::set_rx_frequency(EISCAT_CENTER_FREQ, chan); radio_ctrl_impl::set_rx_gain(EISCAT_DEFAULT_GAIN, chan); radio_ctrl_impl::set_rx_antenna(EISCAT_ANTENNA_NAME, chan); radio_ctrl_impl::set_rx_bandwidth(EISCAT_DEFAULT_BANDWIDTH, chan); } /**** Set up legacy compatible properties ******************************/ // For use with multi_usrp APIs etc. // For legacy prop tree init: fs_path fe_path = fs_path("dboards") / "A" / "rx_frontends"; // The EISCAT dboards have 16 frontends total, but they map to 5 channels // each through a matrix of FIR filters and summations. UHD will get much // less confused if we create 5 fake frontends, because that's also the // number of channels. Since we have no control over the frontends, // nothing is lost here. for (size_t fe_idx = 0; fe_idx < EISCAT_NUM_CHANS; fe_idx++) { _tree->create(fe_path / fe_idx / "name") .set(str(boost::format("EISCAT Beam Contributions %d") % fe_idx)) ; _tree->create(fe_path / fe_idx / "connection") .set("I") ; _tree->create(fe_path / fe_idx / "antenna" / "value") .set(EISCAT_ANTENNA_NAME) //.add_coerced_subscriber(boost::bind(&eiscat_radio_ctrl_impl::set_rx_antenna, this, _1, 0)) ////.set_publisher(boost::bind(&radio_ctrl_impl::get_rx_antenna, this, 0)) //.set_publisher([](){ return EISCAT_ANTENNA_NAME; }) ; _tree->create>(fe_path / fe_idx / "antenna" / "options") .set(std::vector(1, "Rx")) ; _tree->create(fe_path / fe_idx / "freq" / "value") .set(EISCAT_CENTER_FREQ) //.set_coercer(boost::bind(&eiscat_radio_ctrl_impl::set_rx_frequency, this, _1, 0)) ////.set_publisher(boost::bind(&radio_ctrl_impl::get_rx_frequency, this, 0)) ; _tree->create(fe_path / fe_idx / "freq" / "range") .set(meta_range_t(EISCAT_CENTER_FREQ, EISCAT_CENTER_FREQ)) ; _tree->create(fe_path / fe_idx / "gains" / "null" / "value") .set(EISCAT_DEFAULT_GAIN) //.set_coercer(boost::bind(&eiscat_radio_ctrl_impl::set_rx_gain, this, _1, 0)) //.set_publisher(boost::bind(&radio_ctrl_impl::get_rx_gain, this, 0)) ; _tree->create(fe_path / fe_idx / "gains" / "null" / "range") .set(meta_range_t(EISCAT_DEFAULT_GAIN, EISCAT_DEFAULT_GAIN)) ; _tree->create(fe_path / fe_idx / "bandwidth" / "value") .set(EISCAT_DEFAULT_BANDWIDTH) //.set_coercer(boost::bind(&eiscat_radio_ctrl_impl::set_rx_bandwidth, this, _1, 0)) //.set_publisher(boost::bind(&radio_ctrl_impl::get_rx_bandwidth, this, 0)) ; _tree->create(fe_path / fe_idx / "bandwidth" / "range") .set(meta_range_t(EISCAT_DEFAULT_BANDWIDTH, EISCAT_DEFAULT_BANDWIDTH)) ; _tree->create(fe_path / fe_idx / "use_lo_offset") .set(false) ; } // We can actually stream data to an EISCAT board, so it needs some tx // frontends too: fe_path = fs_path("dboards") / "A" / "tx_frontends"; for (size_t fe_idx = 0; fe_idx < EISCAT_NUM_CHANS; fe_idx++) { _tree->create(fe_path / fe_idx / "name") .set(str(boost::format("EISCAT Uplink %d") % fe_idx)) ; } // There is only ever one EISCAT radio per dboard, so this should be unset // when we reach this line: UHD_ASSERT_THROW(not _tree->exists("tick_rate")); _tree->create("tick_rate") //.set_coercer(boost::bind(&eiscat_radio_ctrl_impl::set_rate, this, _1)) .set(EISCAT_TICK_RATE) ; if (not _tree->exists(fs_path("clock_source/value"))) { _tree->create(fs_path("clock_source/value")).set("external"); } UHD_VAR((_tree->exists(fs_path("time/cmd")))); } eiscat_radio_ctrl_impl::~eiscat_radio_ctrl_impl() { UHD_LOG_TRACE("EISCAT", "eiscat_radio_ctrl_impl::dtor() "); } void eiscat_radio_ctrl_impl::set_tx_antenna(const std::string &, const size_t) { throw uhd::runtime_error("Cannot set Tx antenna on EISCAT daughterboard"); } void eiscat_radio_ctrl_impl::set_rx_antenna(const std::string & /* ant */, const size_t /* chan */) { UHD_LOG_WARNING("EISCAT", "Ignoring attempt to set Rx antenna"); } double eiscat_radio_ctrl_impl::get_tx_frequency(const size_t /* chan */) { UHD_LOG_WARNING("EISCAT", "Ignoring attempt to read Tx frequency"); return 0.0; } double eiscat_radio_ctrl_impl::set_tx_frequency(const double /* freq */, const size_t /* chan */) { throw uhd::runtime_error("Cannot set Tx frequency on EISCAT daughterboard"); } double eiscat_radio_ctrl_impl::set_rx_frequency(const double freq, const size_t chan) { if (freq != get_rx_frequency(chan)) { UHD_LOG_WARNING("EISCAT", "Ignoring attempt to set Rx frequency"); } return get_rx_frequency(chan); } double eiscat_radio_ctrl_impl::set_rx_bandwidth(const double bandwidth, const size_t chan) { if (bandwidth != get_rx_bandwidth(chan)) { UHD_LOG_WARNING("EISCAT", "Ignoring attempt to set Rx bandwidth"); } return get_rx_bandwidth(chan); } double eiscat_radio_ctrl_impl::set_tx_gain(const double /* gain */, const size_t /* chan */) { throw uhd::runtime_error("Cannot set Tx gain on EISCAT daughterboard"); } double eiscat_radio_ctrl_impl::set_rx_gain(const double gain, const size_t chan) { // TODO: Add ability to set digital gain or make it explicit this function is not supported. if (gain != get_rx_gain(chan)) { UHD_LOG_WARNING("EISCAT", "Ignoring attempt to set Rx gain."); } return get_rx_gain(chan); } double eiscat_radio_ctrl_impl::set_rate(double rate) { if (rate != get_rate()) { UHD_LOG_WARNING("EISCAT", "Attempting to set sampling rate to invalid value " << rate); } return get_rate(); } size_t eiscat_radio_ctrl_impl::get_chan_from_dboard_fe(const std::string &fe, const uhd::direction_t dir) { return boost::lexical_cast(fe); } std::string eiscat_radio_ctrl_impl::get_dboard_fe_from_chan(const size_t chan, const uhd::direction_t dir) { return std::to_string(chan); } double eiscat_radio_ctrl_impl::get_output_samp_rate(size_t /* port */) { return EISCAT_RADIO_RATE; } bool eiscat_radio_ctrl_impl::check_radio_config() { UHD_RFNOC_BLOCK_TRACE() << "x300_radio_ctrl_impl::check_radio_config() " ; const fs_path rx_fe_path = fs_path("dboards/A/rx_frontends"); uint32_t chan_enables = 0; for (const auto &enb: _rx_streamer_active) { if (enb.second) { chan_enables |= (1<