aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2017-05-19 17:21:26 -0700
committerMartin Braun <martin.braun@ettus.com>2017-12-22 15:03:53 -0800
commit0d10ac5faa5bd02ccaf997489ad42449d0903d24 (patch)
treeec0936c1a1dd690f3525a895de00992ea78e2e8f /host/lib/usrp
parent0e4eca71df0c8de32c954ff0d6d400e7d90dfc5e (diff)
downloaduhd-0d10ac5faa5bd02ccaf997489ad42449d0903d24.tar.gz
uhd-0d10ac5faa5bd02ccaf997489ad42449d0903d24.tar.bz2
uhd-0d10ac5faa5bd02ccaf997489ad42449d0903d24.zip
eiscat: Antenna selection, gain setting
Diffstat (limited to 'host/lib/usrp')
-rw-r--r--host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp270
-rw-r--r--host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp68
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 */