diff options
author | Martin Braun <martin.braun@ettus.com> | 2017-05-19 17:21:26 -0700 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2017-12-22 15:03:53 -0800 |
commit | 0d10ac5faa5bd02ccaf997489ad42449d0903d24 (patch) | |
tree | ec0936c1a1dd690f3525a895de00992ea78e2e8f /host/lib/usrp/dboard | |
parent | 0e4eca71df0c8de32c954ff0d6d400e7d90dfc5e (diff) | |
download | uhd-0d10ac5faa5bd02ccaf997489ad42449d0903d24.tar.gz uhd-0d10ac5faa5bd02ccaf997489ad42449d0903d24.tar.bz2 uhd-0d10ac5faa5bd02ccaf997489ad42449d0903d24.zip |
eiscat: Antenna selection, gain setting
Diffstat (limited to 'host/lib/usrp/dboard')
-rw-r--r-- | host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp | 270 | ||||
-rw-r--r-- | host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp | 68 |
2 files changed, 306 insertions, 32 deletions
diff --git a/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp b/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp index f78876791..648492486 100644 --- a/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp +++ b/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp @@ -30,44 +30,74 @@ 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; +namespace { + const size_t SR_ANTENNA_GAIN_BASE = 204; + const size_t SR_ANTENNA_SELECT_BASE = 192; // Note: On other dboards, 192 is DB_GPIO address space + + const double EISCAT_TICK_RATE = 208e6; // Hz + const double EISCAT_RADIO_RATE = 104e6; // Hz + const double EISCAT_CENTER_FREQ = 104e6; // Hz + const double EISCAT_DEFAULT_NULL_GAIN = 0.0; // dB. This is not the digital antenna gain, this a fake stub value. + const double EISCAT_DEFAULT_BANDWIDTH = 52e6; // Hz + const char* EISCAT_DEFAULT_ANTENNA = "BF"; + const size_t EISCAT_NUM_ANTENNAS = 16; + const size_t EISCAT_NUM_BEAMS = 10; + const size_t EISCAT_NUM_PORTS = 5; + const size_t EISCAT_GAIN_RANGE = 18; // Bits, *signed*. + const int32_t EISCAT_MAX_GAIN = (1<<(EISCAT_GAIN_RANGE-1))-1; + const int32_t EISCAT_MIN_GAIN = -(1<<(EISCAT_GAIN_RANGE-1)); + const double EISCAT_DEFAULT_NORM_GAIN = 1.0; // Normalized. This is the actual digital gain value. + const size_t EISCAT_BITS_PER_TAP = 18; + const eiscat_radio_ctrl_impl::fir_tap_t EISCAT_MAX_TAP_VALUE = (1<<(EISCAT_BITS_PER_TAP-1))-1; + const eiscat_radio_ctrl_impl::fir_tap_t EISCAT_MIN_TAP_VALUE = -(1<<(EISCAT_BITS_PER_TAP-1)); + const size_t EISCAT_NUM_FIR_TAPS = 10; + const size_t EISCAT_NUM_FIR_SETS = 1024; // BRAM must be at least EISCAT_NUM_FIR_TAPS * EISCAT_NUM_FIR_SETS + const size_t EISCAT_FIR_INDEX_IMPULSE = 1002; + const size_t EISCAT_FIR_INDEX_ZEROS = 1003; // FIXME +}; + 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); - + _num_ports = get_output_ports().size(); + UHD_LOG_TRACE("EISCAT", "Number of channels: " << _num_ports); UHD_LOG_TRACE("EISCAT", "Setting tick rate to " << EISCAT_TICK_RATE/1e6 << " MHz"); - /**** Configure the radio itself ***************************************/ + /**** Configure the radio_ctrl itself ***********************************/ radio_ctrl_impl::set_rate(EISCAT_TICK_RATE); - for (size_t chan = 0; chan < num_chans; chan++) { + for (size_t chan = 0; chan < _num_ports; 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_gain(EISCAT_DEFAULT_NULL_GAIN, chan); + radio_ctrl_impl::set_rx_antenna(EISCAT_DEFAULT_ANTENNA, chan); radio_ctrl_impl::set_rx_bandwidth(EISCAT_DEFAULT_BANDWIDTH, chan); } + /**** Configure the digital gain controls *******************************/ + for (size_t i = 0; i < EISCAT_NUM_ANTENNAS; i++) { + _tree->access<double>(get_arg_path("gain", i) / "value") + .set_coercer([](double gain){ return std::max(-1.0, std::min(1.0, gain)); }) + .add_coerced_subscriber([this, i](double gain){ this->set_antenna_gain(i, gain); }) + .set(EISCAT_DEFAULT_NORM_GAIN) + ; + } + /**** 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"; + auto antenna_options = std::vector<std::string>{"BF"}; + for (size_t i = 0; i < EISCAT_NUM_ANTENNAS; i++) { + antenna_options.push_back(str(boost::format("Rx%d") % i)); + } + // 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++) { + for (size_t fe_idx = 0; fe_idx < _num_ports; fe_idx++) { _tree->create<std::string>(fe_path / fe_idx / "name") .set(str(boost::format("EISCAT Beam Contributions %d") % fe_idx)) ; @@ -75,13 +105,11 @@ UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(eiscat_radio_ctrl) .set("I") ; _tree->create<std::string>(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; }) + .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)) ; _tree->create<std::vector<std::string>>(fe_path / fe_idx / "antenna" / "options") - .set(std::vector<std::string>(1, "Rx")) + .set(antenna_options) ; _tree->create<double>(fe_path / fe_idx / "freq" / "value") .set(EISCAT_CENTER_FREQ) @@ -92,12 +120,12 @@ UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(eiscat_radio_ctrl) .set(meta_range_t(EISCAT_CENTER_FREQ, EISCAT_CENTER_FREQ)) ; _tree->create<double>(fe_path / fe_idx / "gains" / "null" / "value") - .set(EISCAT_DEFAULT_GAIN) + .set(EISCAT_DEFAULT_NULL_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<meta_range_t>(fe_path / fe_idx / "gains" / "null" / "range") - .set(meta_range_t(EISCAT_DEFAULT_GAIN, EISCAT_DEFAULT_GAIN)) + .set(meta_range_t(EISCAT_DEFAULT_NULL_GAIN, EISCAT_DEFAULT_NULL_GAIN)) ; _tree->create<double>(fe_path / fe_idx / "bandwidth" / "value") .set(EISCAT_DEFAULT_BANDWIDTH) @@ -115,7 +143,7 @@ UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(eiscat_radio_ctrl) // 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++) { + for (size_t fe_idx = 0; fe_idx < _num_ports; fe_idx++) { _tree->create<std::string>(fe_path / fe_idx / "name") .set(str(boost::format("EISCAT Uplink %d") % fe_idx)) ; @@ -141,14 +169,78 @@ eiscat_radio_ctrl_impl::~eiscat_radio_ctrl_impl() UHD_LOG_TRACE("EISCAT", "eiscat_radio_ctrl_impl::dtor() "); } + +/**************************************************************************** + * Public API calls + ***************************************************************************/ 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 */) +void eiscat_radio_ctrl_impl::set_rx_antenna(const std::string &ant, const size_t port) { - UHD_LOG_WARNING("EISCAT", "Ignoring attempt to set Rx antenna"); + UHD_ASSERT_THROW(port < EISCAT_NUM_PORTS); + if (ant == "BF") { + UHD_LOG_TRACE("EISCAT", "Setting antenna to 'BF' (which is a no-op)"); + return; + } + if (ant.size() < 3 or ant.size() > 4 or (ant.substr(0, 2) != "Rx" and ant.substr(0, 2) != "BF")) { + throw uhd::value_error(str( + boost::format("EISCAT: Invalid antenna selection: %s") + % ant + )); + } + + bool use_fir_matrix = (ant.substr(0, 2) == "BF"); + + const size_t antenna_idx = [&ant](){ + try { + return boost::lexical_cast<size_t>(ant.substr(2)); + } catch (const boost::bad_lexical_cast&) { + throw uhd::value_error(str( + boost::format("EISCAT: Invalid antenna selection: %s") + % ant + )); + } + }(); + + if (use_fir_matrix) { + UHD_LOG_TRACE("EISCAT", str( + boost::format("Setting port %d to only receive on antenna %d via FIR matrix") + % port % antenna_idx + )); + // TODO: When we have a way to select neighbour contributions, we will need + // to calculate the beam_index as a function of the port *and* if we're the + // left or right USRP + const size_t beam_index = port; + uhd::time_spec_t send_now(0.0); + + for (size_t i = 0; i < EISCAT_NUM_ANTENNAS; i++) { + if (i == antenna_idx) { + select_filter( + beam_index, + i, + EISCAT_FIR_INDEX_IMPULSE, + send_now + ); + } else { + select_filter( + beam_index, + i, + EISCAT_FIR_INDEX_ZEROS, + send_now + ); + } + } + } else { + set_arg<int>("choose_beams", 6); + UHD_LOG_TRACE("EISCAT", str( + boost::format("Setting port %d to only receive on antenna %d directly") + % port % antenna_idx + )); + sr_write(SR_ANTENNA_SELECT_BASE + port, antenna_idx); + } } double eiscat_radio_ctrl_impl::get_tx_frequency(const size_t /* chan */) @@ -201,13 +293,17 @@ double eiscat_radio_ctrl_impl::set_rate(double rate) return get_rate(); } -size_t eiscat_radio_ctrl_impl::get_chan_from_dboard_fe(const std::string &fe, const uhd::direction_t dir) -{ +size_t eiscat_radio_ctrl_impl::get_chan_from_dboard_fe( + const std::string &fe, + const uhd::direction_t /* dir */ +) { return boost::lexical_cast<size_t>(fe); } -std::string eiscat_radio_ctrl_impl::get_dboard_fe_from_chan(const size_t chan, const uhd::direction_t dir) -{ +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); } @@ -232,4 +328,116 @@ bool eiscat_radio_ctrl_impl::check_radio_config() return true; } +/**************************************************************************** + * Internal methods + ***************************************************************************/ +void eiscat_radio_ctrl_impl::write_fir_taps( + const size_t fir_idx, + const std::vector<eiscat_radio_ctrl_impl::fir_tap_t> &taps +) { + if (taps.size() > EISCAT_NUM_FIR_TAPS) { + throw uhd::value_error(str( + boost::format("Too many FIR taps for EISCAT filters (%d)") + % taps.size() + )); + } + for (const auto &tap: taps) { + if (tap > EISCAT_MAX_TAP_VALUE or tap < EISCAT_MIN_TAP_VALUE) { + throw uhd::value_error(str( + boost::format("Filter tap for filter_idx %d exceeds dynamic range (%d bits are allowed)") + % fir_idx % EISCAT_BITS_PER_TAP + )); + } + } + + UHD_LOG_TRACE("EISCAT", str( + boost::format("Writing %d filter taps for filter index %d") + % taps.size() % fir_idx + )); + for (size_t i = 0; i < EISCAT_NUM_FIR_TAPS; i++) { + // Payload: + // - bottom 14 bits address, fir_idx * 16 + tap_index + // - top 18 bits are value + uint32_t reg_value = (fir_idx * 16) + i;; + if (taps.size() > i) { + reg_value |= (taps[i] & 0x3FFFF) << 14; + } + sr_write("SR_FIR_BRAM_WRITE_TAPS", reg_value); + } +} + +void eiscat_radio_ctrl_impl::select_filter( + const size_t beam_index, + const size_t antenna_index, + const size_t fir_index, + const uhd::time_spec_t &time_spec +) { + if (antenna_index >= EISCAT_NUM_ANTENNAS) { + throw uhd::value_error(str( + boost::format("Antenna index %d out of range. There are %d antennas in EISCAT.") + % antenna_index % EISCAT_NUM_ANTENNAS + )); + } + if (beam_index >= EISCAT_NUM_BEAMS) { + throw uhd::value_error(str( + boost::format("Beam index %d out of range. There are %d beam channels in EISCAT.") + % beam_index % EISCAT_NUM_BEAMS + )); + } + + UHD_LOGGER_TRACE("EISCAT") + << "Selecting filter " << fir_index + << " for beam " << beam_index + << " and antenna " << antenna_index + ; + bool send_now = (time_spec == uhd::time_spec_t(0.0)); + uint32_t reg_value = 0 + | (fir_index * 16) + | (antenna_index & 0xF) << 14 + | (beam_index & 0xF) << 18 + | send_now << 22 + ; + + if (not send_now) { + uint64_t cmd_time_ticks = time_spec.to_ticks(EISCAT_TICK_RATE); + UHD_LOG_TRACE("EISCAT", str( + boost::format("Filter selection will be applied at time %f (0x%016X == %u)") + % time_spec.get_full_secs() + % cmd_time_ticks + )); + sr_write("SR_FIR_COMMANDS_CTRL_TIME_LO", cmd_time_ticks & 0xFFFFFFFF); + sr_write("SR_FIR_COMMANDS_CTRL_TIME_HI", (cmd_time_ticks >> 32) & 0xFFFFFFFF); + } + sr_write("SR_FIR_COMMANDS_RELOAD", reg_value); +} + +void eiscat_radio_ctrl_impl::set_antenna_gain( + const size_t antenna_idx, + const double normalized_gain +) { + if (normalized_gain < -1.0 or normalized_gain > 1.0) { + throw uhd::value_error(str( + boost::format("Invalid gain value for antenna %d: %f") + % antenna_idx % normalized_gain + )); + } + + const auto fixpoint_gain = std::max<int32_t>( + EISCAT_MIN_GAIN, + std::min( + EISCAT_MAX_GAIN, + int32_t(normalized_gain * EISCAT_MAX_GAIN) + ) + ); + + UHD_LOG_TRACE("EISCAT", str( + boost::format("Setting digital gain value for antenna %d to %f (%d)") + % antenna_idx % normalized_gain % fixpoint_gain + )); + sr_write(SR_ANTENNA_GAIN_BASE + antenna_idx, fixpoint_gain); +} + +/**************************************************************************** + * Registry + ***************************************************************************/ UHD_RFNOC_BLOCK_REGISTER(eiscat_radio_ctrl, "EISCATRadio"); diff --git a/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp b/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp index 5bfbbc035..cb45208ce 100644 --- a/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp +++ b/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp @@ -31,7 +31,8 @@ namespace uhd { class eiscat_radio_ctrl_impl : public radio_ctrl_impl { public: - typedef boost::shared_ptr<eiscat_radio_ctrl_impl> sptr; + using sptr = boost::shared_ptr<eiscat_radio_ctrl_impl>; + using fir_tap_t = int32_t; /************************************************************************ * Structors @@ -46,6 +47,21 @@ public: double set_rate(double rate); void set_tx_antenna(const std::string &ant, const size_t chan); + + /*! Configures FPGA switching for antenna selection + * + * + * Valid antenna values: + * - BF: This is the default. Will apply the beamforming matrix in whatever + * state it currently is. + * - Rx0...Rx15: Will mux the antenna signal 0...15 straight to this + * channel. Note that this will disable the FIR matrix entirely. + * - BF0...BF15: Will configure the FIR filter matrix such that only the + * contributions from antenna 0...15 are passed to this channel. + * + * + * \throws uhd::value_error if the antenna value was not valid + */ void set_rx_antenna(const std::string &ant, const size_t chan); double set_tx_frequency(const double freq, const size_t chan); @@ -66,6 +82,56 @@ protected: private: + /*! Write filter taps for a specific FIR filter. + * + * Note: If the number of taps is smaller than the number of available + * filter taps, it is padded with zero. + * + * \param fir_idx The index of the FIR filter we are reprogramming + * \param taps A list of FIR filter taps for this filter. + * + * \throws uhd::value_error if the number of taps is longer than the number + * of taps that the filter can handle, or if any + * tap has more bits than allowed. + */ + void write_fir_taps( + const size_t fir_idx, + const std::vector<fir_tap_t> &taps + ); + + + /*! Choose a filter to be applied between an output beam and antenna input + * + * \param beam_index Beam index + * \param antenna_index Antenna index + * \param fir_index The index of the FIR filter taps that get applied + * \param time_spec If non-zero, the taps get applied at this time + */ + void select_filter( + const size_t beam_index, + const size_t antenna_index, + const size_t fir_index, + const uhd::time_spec_t &time_spec + ); + + /*! Sets the digital gain on a specific antenna + * + * \param antenna_idx Antenna for which this gain setting applies + * \param normalized_gain A value in [0, 1] which gets converted to a + * digital gain value + */ + void set_antenna_gain( + const size_t antenna_idx, + const double normalized_gain + ); + + /*! The number of channels this block outputs + * + * This is *not* the number of antennas, but the number of streams a single + * block outputs to the crossbar. + */ + size_t _num_ports; + }; /* class radio_ctrl_impl */ }} /* namespace uhd::rfnoc */ |