diff options
author | Mark Meserve <mark.meserve@ni.com> | 2017-03-23 14:49:42 -0700 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2017-12-22 15:03:44 -0800 |
commit | bc4d1bf0ca341a72a7d7f6b2168aa3d2e936aabf (patch) | |
tree | 5af9a631efc95c2a1e94e58aa1a86b84e2c91712 /mpm/lib/mykonos/ad937x_ctrl.cpp | |
parent | 1a4348038d0eb57d53475074dca49e8192aeb2d7 (diff) | |
download | uhd-bc4d1bf0ca341a72a7d7f6b2168aa3d2e936aabf.tar.gz uhd-bc4d1bf0ca341a72a7d7f6b2168aa3d2e936aabf.tar.bz2 uhd-bc4d1bf0ca341a72a7d7f6b2168aa3d2e936aabf.zip |
mpm: Rearchitecture of AD9371 code to use ctrl/device paradigm
Diffstat (limited to 'mpm/lib/mykonos/ad937x_ctrl.cpp')
-rw-r--r-- | mpm/lib/mykonos/ad937x_ctrl.cpp | 442 |
1 files changed, 72 insertions, 370 deletions
diff --git a/mpm/lib/mykonos/ad937x_ctrl.cpp b/mpm/lib/mykonos/ad937x_ctrl.cpp index b4ff38139..a5cd842f5 100644 --- a/mpm/lib/mykonos/ad937x_ctrl.cpp +++ b/mpm/lib/mykonos/ad937x_ctrl.cpp @@ -1,15 +1,31 @@ -#include "adi/mykonos.h" +// +// Copyright 2017 Ettus Research (National Instruments) +// +// 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 <http://www.gnu.org/licenses/>. +// -#include "ad937x_ctrl.hpp" #include "ad937x_device.hpp" +#include "ad937x_ctrl.hpp" +#include "adi/mykonos.h" + #include <sstream> +#include <set> #include <functional> -#include <iostream> -#include <cmath> uhd::meta_range_t ad937x_ctrl::get_rf_freq_range(void) { - return uhd::meta_range_t(300e6, 6e9); + return uhd::meta_range_t(ad937x_device::MIN_FREQ, ad937x_device::MAX_FREQ); } uhd::meta_range_t ad937x_ctrl::get_bw_filter_range(void) @@ -18,7 +34,7 @@ uhd::meta_range_t ad937x_ctrl::get_bw_filter_range(void) return uhd::meta_range_t(0, 1); } -std::vector<double> ad937x_ctrl::get_clock_rates(void) +std::set<double> ad937x_ctrl::get_clock_rates(void) { // TODO: fix return { 125e6 }; @@ -30,16 +46,16 @@ uhd::meta_range_t ad937x_ctrl::get_gain_range(const std::string &which) switch (dir) { case uhd::direction_t::RX_DIRECTION: - return uhd::meta_range_t(0, 30, 0.5); + return uhd::meta_range_t(ad937x_device::MIN_RX_GAIN, ad937x_device::MAX_RX_GAIN, ad937x_device::RX_GAIN_STEP); case uhd::direction_t::TX_DIRECTION: - return uhd::meta_range_t(0, 41.95, 0.05); + return uhd::meta_range_t(ad937x_device::MIN_TX_GAIN, ad937x_device::MAX_TX_GAIN, ad937x_device::TX_GAIN_STEP); default: - throw uhd::runtime_error("ad937x_ctrl got an invalid channel string."); + UHD_THROW_INVALID_CODE_PATH(); return uhd::meta_range_t(); } } -std::vector<size_t> ad937x_ctrl::_get_valid_fir_lengths(const std::string& which) +std::set<size_t> ad937x_ctrl::_get_valid_fir_lengths(const std::string& which) { auto dir = _get_direction_from_antenna(which); switch (dir) @@ -49,8 +65,8 @@ std::vector<size_t> ad937x_ctrl::_get_valid_fir_lengths(const std::string& which case uhd::direction_t::TX_DIRECTION: return { 16, 32, 48, 64, 80, 96 }; default: - throw uhd::runtime_error("ad937x_ctrl got an invalid channel string."); - return std::vector<size_t>(); + UHD_THROW_INVALID_CODE_PATH(); + return std::set<size_t>(); } } @@ -87,30 +103,27 @@ ad937x_device::chain_t ad937x_ctrl::_get_chain_from_antenna(const std::string& a class ad937x_ctrl_impl : public ad937x_ctrl { public: - // change to uhd::spi_iface - static sptr make(spi_lock::sptr spi_l, mpm::spi_iface::sptr iface); - - ad937x_ctrl_impl(spi_lock::sptr spi_l, mpm::spi_iface::sptr iface) : + ad937x_ctrl_impl(spi_lock::sptr spi_l, uhd::spi_iface::sptr iface) : spi_l(spi_l), - iface(iface) + device(iface) { } virtual uint8_t get_product_id() { - std::lock_guard<spi_lock>(*spi_l); + std::lock_guard<spi_lock> lock(*spi_l); return device.get_product_id(); } virtual uint8_t get_device_rev() { - std::lock_guard<spi_lock>(*spi_l); + std::lock_guard<spi_lock> lock(*spi_l); return device.get_device_rev(); } virtual std::string get_api_version() { - std::lock_guard<spi_lock>(*spi_l); + std::lock_guard<spi_lock> lock(*spi_l); auto api = device.get_api_version(); std::ostringstream ss; ss << api.silicon_ver << "." @@ -122,7 +135,7 @@ public: virtual std::string get_arm_version() { - std::lock_guard<spi_lock>(*spi_l); + std::lock_guard<spi_lock> lock(*spi_l); auto arm = device.get_arm_version(); std::ostringstream ss; ss << arm.major_ver << "." @@ -131,27 +144,19 @@ public: return ss.str(); } - virtual double set_bw_filter(const std::string &which, const double value) + virtual double set_bw_filter(const std::string &which, double value) { // TODO implement return double(); } - virtual double set_gain(const std::string &which, const double value) + virtual double set_gain(const std::string &which, double value) { auto dir = _get_direction_from_antenna(which); auto chain = _get_chain_from_antenna(which); - return device.set_gain(dir, chain, value); - } - virtual void set_agc(const std::string &which, const bool enable) - { - auto dir = _get_direction_from_antenna(which); - if (dir != uhd::direction_t::RX_DIRECTION) - { - throw uhd::runtime_error("ad937x_ctrl::set_agc was called on a non-RX channel"); - } - return device.set_agc(dir, enable); + std::lock_guard<spi_lock> lock(*spi_l); + return device.set_gain(dir, chain, value); } virtual void set_agc_mode(const std::string &which, const std::string &mode) @@ -159,7 +164,7 @@ public: auto dir = _get_direction_from_antenna(which); if (dir != uhd::direction_t::RX_DIRECTION) { - throw uhd::runtime_error("ad937x_ctrl::set_agc was called on a non-RX channel"); + throw uhd::runtime_error("set_agc not valid for non-rx channels"); } ad937x_device::gain_mode_t gain_mode; @@ -174,392 +179,89 @@ public: gain_mode = ad937x_device::gain_mode_t::HYBRID; } else { - throw uhd::runtime_error("ad937x_ctrl::set_agc_mode was called on a non-RX channel"); + throw uhd::runtime_error("invalid agc mode"); } + std::lock_guard<spi_lock> lock(*spi_l); device.set_agc_mode(dir, gain_mode); } - virtual double set_clock_rate(const double value) + virtual double set_clock_rate(double value) { auto rates = get_clock_rates(); - auto coerced_value = value; - if (std::find(rates.begin(), rates.end(), value) == rates.end()) + if (std::find(rates.cbegin(), rates.cend(), value) == rates.end()) { - coerced_value = rates[0]; + value = *(rates.cbegin()); } - return device.set_clock_rate(coerced_value); + std::lock_guard<spi_lock> lock(*spi_l); + return device.set_clock_rate(value); } - virtual void enable_channel(const std::string &which, const bool enable) + virtual void enable_channel(const std::string &which, bool enable) { auto dir = _get_direction_from_antenna(which); auto chain = _get_chain_from_antenna(which); + + std::lock_guard<spi_lock> lock(*spi_l); return device.enable_channel(dir, chain, enable); } - virtual double set_freq(const std::string &which, const double value) + virtual double set_freq(const std::string &which, double value) { auto dir = _get_direction_from_antenna(which); auto clipped_value = get_rf_freq_range().clip(value); + + std::lock_guard<spi_lock> lock(*spi_l); return device.tune(dir, clipped_value); } virtual double get_freq(const std::string &which) { auto dir = _get_direction_from_antenna(which); + + std::lock_guard<spi_lock> lock(*spi_l); return device.get_freq(dir); } virtual void set_fir(const std::string &which, int8_t gain, const std::vector<int16_t> & fir) { + auto dir = _get_direction_from_antenna(which); + auto chain = _get_chain_from_antenna(which); + auto lengths = _get_valid_fir_lengths(which); if (std::find(lengths.begin(), lengths.end(), fir.size()) == lengths.end()) { - throw uhd::value_error("ad937x_ctrl::set_fir got filter of invalid length"); + throw uhd::value_error("invalid filter length"); } - ad937x_fir(gain, fir); - - } - virtual std::vector<int16_t> get_fir(const std::string &which) = 0; - - virtual int16_t get_temperature() = 0; - -private: - ad937x_device device; - spi_lock::sptr spi_l; - mpm::spi_iface::sptr iface; -}; - -const double ad937x_ctrl_impl::MIN_FREQ = 300e6; -const double ad937x_ctrl_impl::MAX_FREQ = 6e9; - -/* -ad937x_ctrl::sptr ad937x_ctrl_impl::make( - spi_lock::sptr spi_l, - mpm::spi_iface::sptr iface) -{ - return std::make_shared<ad937x_ctrl_impl>(spi_l, iface); -} - - -void ad937x_ctrl::initialize() -{ - //headlessinit(mykonos_config.device); - // TODO: finish initialization - { - std::lock_guard<spi_lock> lock(*spi_l); - call_api_function(std::bind(MYKONOS_initialize, mykonos_config.device)); - } - get_product_id(); -} - -ad937x_ctrl::ad937x_ctrl( - spi_lock::sptr spi_l, - mpm::spi_iface::sptr iface) : - spi_l(spi_l), - iface(iface) -{ - mpm_sps.spi_iface = iface.get(); - - //TODO assert iface->get_chip_select() is 1-8 - mpm_sps.spi_settings.chipSelectIndex = static_cast<uint8_t>(iface->get_chip_select()); - mpm_sps.spi_settings.writeBitPolarity = 1; - mpm_sps.spi_settings.longInstructionWord = 1; // set to 1 by initialize - mpm_sps.spi_settings.MSBFirst = - (iface->get_endianness() == mpm::spi_iface::spi_endianness_t::LSB_FIRST) ? 0 : 1; - mpm_sps.spi_settings.CPHA = 0; // set to 0 by initialize - mpm_sps.spi_settings.CPOL = 0; // set to 0 by initialize - mpm_sps.spi_settings.enSpiStreaming = 1; - mpm_sps.spi_settings.autoIncAddrUp = 1; - mpm_sps.spi_settings.fourWireMode = - (iface->get_wire_mode() == mpm::spi_iface::spi_wire_mode_t::THREE_WIRE_MODE) ? 0 : 1; - mpm_sps.spi_settings.spiClkFreq_Hz = 25000000; - - initialize(); -} - -// helper function to unify error handling -// bind is bad, but maybe this is justifiable -void ad937x_ctrl::call_api_function(std::function<mykonosErr_t()> func) -{ - auto error = func(); - if (error != MYKONOS_ERR_OK) - { - std::cout << getMykonosErrorMessage(error); - // TODO: make UHD exception - //throw std::exception(getMykonosErrorMessage(error)); - } -} - -uint8_t ad937x_ctrl::get_product_id() -{ - std::lock_guard<spi_lock> lock(*spi_l); - uint8_t id; - call_api_function(std::bind(MYKONOS_getProductId, mykonos_config.device, &id)); - return id; -} - -double ad937x_ctrl::set_clock_rate(const double req_rate) -{ - auto rate = static_cast<decltype(mykonos_config.device->clocks->deviceClock_kHz)>(req_rate / 1000); - mykonos_config.device->clocks->deviceClock_kHz = rate; - { - std::lock_guard<spi_lock> lock(*spi_l); - call_api_function(std::bind(MYKONOS_initDigitalClocks, mykonos_config.device)); - } - return static_cast<decltype(set_clock_rate(0))>(rate); -} - -void ad937x_ctrl::_set_active_tx_chains(bool tx1, bool tx2) -{ - decltype(mykonos_config.device->tx->txChannels) newTxChannel; - if (tx1 && tx2) - { - newTxChannel = TX1_TX2; - } - else if (tx1) { - newTxChannel = TX1; - } - else if (tx2) { - newTxChannel = TX2; - } - else { - newTxChannel = TXOFF; - } - mykonos_config.device->tx->txChannels = newTxChannel; -} - -void ad937x_ctrl::_set_active_rx_chains(bool rx1, bool rx2) -{ - decltype(mykonos_config.device->rx->rxChannels) newRxChannel; - if (rx1 && rx2) - { - newRxChannel = RX1_RX2; - } - else if (rx1) { - newRxChannel = RX1; - } - else if (rx2) { - newRxChannel = RX2; - } - else { - newRxChannel = RXOFF; - } - mykonos_config.device->rx->rxChannels = newRxChannel; -} - -void ad937x_ctrl::set_active_chains(direction_t direction, bool channel1, bool channel2) -{ - switch (direction) - { - case TX: _set_active_tx_chains(channel1, channel2); break; - case RX: _set_active_rx_chains(channel1, channel2); break; - default: - // TODO: bad code path exception - throw std::exception(); - } - // TODO: make this apply the setting -} - -double ad937x_ctrl::tune(direction_t direction, const double value) -{ - // I'm not really sure why we set the PLL value in the config AND as a function parameter - // but here it is - - mykonosRfPllName_t pll; - uint64_t integer_value = static_cast<uint64_t>(value); - switch (direction) - { - case TX: - pll = TX_PLL; - mykonos_config.device->tx->txPllLoFrequency_Hz = integer_value; - break; - case RX: - pll = RX_PLL; - mykonos_config.device->rx->rxPllLoFrequency_Hz = integer_value; - break; - default: - // TODO: bad code path exception - throw std::exception(); - } - - { - std::lock_guard<spi_lock> lock(*spi_l); - call_api_function(std::bind(MYKONOS_setRfPllFrequency, mykonos_config.device, pll, integer_value)); - } - - // TODO: coercion here causes extra device accesses, when the formula is provided on pg 119 of the user guide - // Furthermore, because coerced is returned as an integer, it's not even accurate - uint64_t coerced_pll; - { std::lock_guard<spi_lock> lock(*spi_l); - call_api_function(std::bind(MYKONOS_getRfPllFrequency, mykonos_config.device, pll, &coerced_pll)); + device.set_fir(dir, chain, gain, fir); } - return static_cast<double>(coerced_pll); -} -double ad937x_ctrl::get_freq(direction_t direction) -{ - mykonosRfPllName_t pll; - switch (direction) + std::vector<int16_t> get_fir(const std::string &which, int8_t &gain) { - case TX: pll = TX_PLL; break; - case RX: pll = RX_PLL; break; - default: - // TODO: bad code path exception - throw std::exception(); - } + auto dir = _get_direction_from_antenna(which); + auto chain = _get_chain_from_antenna(which); - // TODO: coercion here causes extra device accesses, when the formula is provided on pg 119 of the user guide - // Furthermore, because coerced is returned as an integer, it's not even accurate - uint64_t coerced_pll; - { std::lock_guard<spi_lock> lock(*spi_l); - call_api_function(std::bind(MYKONOS_getRfPllFrequency, mykonos_config.device, pll, &coerced_pll)); + return device.get_fir(dir, chain, gain); } - return static_cast<double>(coerced_pll); - return double(); -} - -// RX Gain values are table entries given in mykonos_user.h -// An array of gain values is programmed at initialization, which the API will then use for its gain values -// In general, Gain Value = (255 - Gain Table Index) -uint8_t ad937x_ctrl::_convert_rx_gain(double inGain, double &coercedGain) -{ - // TODO: use uhd::meta_range? - const static double min_gain = 0; - const static double max_gain = 30; - const static double gain_step = 0.5; - coercedGain = inGain; - if (coercedGain < min_gain) + virtual int16_t get_temperature() { - coercedGain = min_gain; - } - if (coercedGain > max_gain) - { - coercedGain = max_gain; - } - - // round to nearest step - coercedGain = std::round(coercedGain * (1.0 / gain_step)) / (1.0 / gain_step); - - // gain should be a value 0-60, add 195 to make 195-255 - return static_cast<uint8_t>((coercedGain * 2) + 195); -} - -// TX gain is completely different from RX gain for no good reason so deal with it -// TX is set as attenuation using a value from 0-41950 mdB -// Only increments of 50 mdB are valid -uint16_t ad937x_ctrl::_convert_tx_gain(double inGain, double &coercedGain) -{ - // TODO: use uhd::meta_range? - const static double min_gain = 0; - const static double max_gain = 41.95; - const static double gain_step = 0.05; - - coercedGain = inGain; - if (coercedGain < min_gain) - { - coercedGain = min_gain; - } - if (coercedGain > max_gain) - { - coercedGain = max_gain; - } - - coercedGain = std::round(coercedGain * (1.0 / gain_step)) / (1.0 / gain_step); - - // attenuation is inverted and in mdB not dB - return static_cast<uint16_t>((max_gain - (coercedGain)) * 1000); -} - - -double ad937x_ctrl::set_gain(direction_t direction, chain_t chain, const double value) -{ - double coerced_value; - switch (direction) - { - case TX: - { - uint16_t attenuation = _convert_tx_gain(value, coerced_value); - std::function<mykonosErr_t(mykonosDevice_t*, uint16_t)> func; - switch (chain) - { - case CHAIN_1: - func = MYKONOS_setTx1Attenuation; - break; - case CHAIN_2: - func = MYKONOS_setTx2Attenuation; - break; - default: - // TODO: bad code path exception - throw std::exception(); - } std::lock_guard<spi_lock> lock(*spi_l); - call_api_function(std::bind(func, mykonos_config.device, attenuation)); - break; + return device.get_temperature(); } - case RX: - { - uint8_t gain = _convert_rx_gain(value, coerced_value); - std::function<mykonosErr_t(mykonosDevice_t*, uint8_t)> func; - switch (chain) - { - case CHAIN_1: - func = MYKONOS_setRx1ManualGain; - break; - case CHAIN_2: - func = MYKONOS_setRx2ManualGain; - break; - default: - // TODO: bad code path exception - throw std::exception(); - } - std::lock_guard<spi_lock> lock(*spi_l); - call_api_function(std::bind(func, mykonos_config.device, gain)); - break; - } - default: - // TODO: bad code path exception - throw std::exception(); - } - return coerced_value; -} + +private: + ad937x_device device; + spi_lock::sptr spi_l; +}; -double ad937x_ctrl::set_agc_mode(direction_t direction, chain_t chain, gain_mode_t mode) +ad937x_ctrl::sptr ad937x_ctrl::make(spi_lock::sptr spi_l, uhd::spi_iface::sptr iface) { - std::lock_guard<spi_lock> lock(*spi_l); - switch (direction) - { - case RX: - switch (mode) - { - case GAIN_MODE_MANUAL: - call_api_function(std::bind(MYKONOS_resetRxAgc, mykonos_config.device)); - break; - case GAIN_MODE_SLOW_AGC: - case GAIN_MODE_FAST_AGC: - // TODO: differentiate these - call_api_function(std::bind(MYKONOS_setupRxAgc, mykonos_config.device)); - break; - default: - // TODO: bad code path exception - throw std::exception(); - } - default: - // TODO: bad code path exception - throw std::exception(); - } - return double(); + return std::make_shared<ad937x_ctrl_impl>(spi_l, iface); } -ad937x_ctrl::sptr ad937x_ctrl::make(spi_lock::sptr spi_l, mpm::spi_iface::sptr iface) -{ - return std::make_shared<ad937x_ctrl>(spi_l, iface); -} -*/ |