diff options
Diffstat (limited to 'mpm/lib/mykonos/ad937x_device.cpp')
| -rw-r--r-- | mpm/lib/mykonos/ad937x_device.cpp | 610 | 
1 files changed, 385 insertions, 225 deletions
| diff --git a/mpm/lib/mykonos/ad937x_device.cpp b/mpm/lib/mykonos/ad937x_device.cpp index e9815112e..511a0a0a1 100644 --- a/mpm/lib/mykonos/ad937x_device.cpp +++ b/mpm/lib/mykonos/ad937x_device.cpp @@ -45,56 +45,61 @@ static const double TX_DEFAULT_FREQ = 2.5e9;  static const double RX_DEFAULT_GAIN = 0;  static const double TX_DEFAULT_GAIN = 0; -// TODO: get the actual device ID -static const uint32_t AD9371_PRODUCT_ID = 0x1; - -// TODO: move this to whereever we declare the ARM binary +static const uint32_t AD9371_PRODUCT_ID = 0x3;  static const size_t ARM_BINARY_SIZE = 98304; +static const size_t PLL_LOCK_TIMEOUT_MS = 200;  static const uint32_t INIT_CAL_TIMEOUT_MS = 10000;  // TODO: actually figure out what cals we want to run +// minimum required cals are 0x4F  static const uint32_t INIT_CALS =      TX_BB_FILTER |      ADC_TUNER |      TIA_3DB_CORNER |      DC_OFFSET | -    TX_ATTENUATION_DELAY | -    RX_GAIN_DELAY | +//    TX_ATTENUATION_DELAY | +//    RX_GAIN_DELAY |      FLASH_CAL | -    PATH_DELAY | -    TX_LO_LEAKAGE_INTERNAL | -//  TX_LO_LEAKAGE_EXTERNAL | -    TX_QEC_INIT | -    LOOPBACK_RX_LO_DELAY | -    LOOPBACK_RX_RX_QEC_INIT | -    RX_LO_DELAY | -    RX_QEC_INIT | -//  DPD_INIT | -//  CLGC_INIT | -//  VSWR_INIT | +//    PATH_DELAY | +//    TX_LO_LEAKAGE_INTERNAL | +////  TX_LO_LEAKAGE_EXTERNAL | +//    TX_QEC_INIT | +//    LOOPBACK_RX_LO_DELAY | +//    LOOPBACK_RX_RX_QEC_INIT | +//    RX_LO_DELAY | +//    RX_QEC_INIT | +////  DPD_INIT | +////  CLGC_INIT | +////  VSWR_INIT |      0;  static const uint32_t TRACKING_CALS = -    TRACK_RX1_QEC | -    TRACK_RX2_QEC | -    TRACK_ORX1_QEC | -    TRACK_ORX2_QEC | -//  TRACK_TX1_LOL | -//  TRACK_TX2_LOL | -    TRACK_TX1_QEC | -    TRACK_TX2_QEC | -//  TRACK_TX1_DPD | -//  TRACK_TX2_DPD | -//  TRACK_TX1_CLGC | -//  TRACK_TX2_CLGC | -//  TRACK_TX1_VSWR | -//  TRACK_TX2_VSWR | -//  TRACK_ORX1_QEC_SNLO | -//  TRACK_ORX2_QEC_SNLO | -//  TRACK_SRX_QEC | +//    TRACK_RX1_QEC | +//    TRACK_RX2_QEC | +//    TRACK_ORX1_QEC | +//    TRACK_ORX2_QEC | +////  TRACK_TX1_LOL | +////  TRACK_TX2_LOL | +//    TRACK_TX1_QEC | +//    TRACK_TX2_QEC | +////  TRACK_TX1_DPD | +////  TRACK_TX2_DPD | +////  TRACK_TX1_CLGC | +////  TRACK_TX2_CLGC | +////  TRACK_TX1_VSWR | +////  TRACK_TX2_VSWR | +////  TRACK_ORX1_QEC_SNLO | +////  TRACK_ORX2_QEC_SNLO | +////  TRACK_SRX_QEC |      0; + + +/****************************************************** +Helper functions +******************************************************/ +  // helper function to unify error handling  void ad937x_device::_call_api_function(std::function<mykonosErr_t()> func)  { @@ -115,6 +120,37 @@ void ad937x_device::_call_gpio_api_function(std::function<mykonosGpioErr_t()> fu      }  } +// _move_to_config_state() and _restore_from_config_state() are a pair of functions +// that should be called at the beginning and end (respectively) of any configuration +// function that requires the AD9371 to be in the radioOff state.  _restore should be +// called with the return value of _move. + +// read the current state of the AD9371 and change it to radioOff (READY) +// returns the current state, to later be consumed by _restore_from_config_state() +ad937x_device::radio_state_t ad937x_device::_move_to_config_state() +{ +    uint32_t status; +    _call_api_function(std::bind(MYKONOS_getRadioState, mykonos_config.device, &status)); +    if ((status & 0x3) == 0x3) +    { +        stop_radio(); +        return radio_state_t::ON; +    } +    else { +        return radio_state_t::OFF; +    } +} + +// restores the state from before a call to _move_to_config_state +// if ON, move to radioOn, otherwise this function is a no-op +void ad937x_device::_restore_from_config_state(ad937x_device::radio_state_t state) +{ +    if (state == radio_state_t::ON) +    { +        start_radio(); +    } +} +  std::string ad937x_device::_get_arm_binary_path()  {      // TODO: possibly add more options here, for now it's always in /lib/firmware or we explode @@ -128,44 +164,20 @@ std::vector<uint8_t> ad937x_device::_get_arm_binary()      if (!file.is_open())      {          throw mpm::runtime_error("Could not open AD9371 ARM binary at path " + path); -        return{}; +        return {};      } +    // TODO: add check that opened file size is equal to ARM_BINARY_SIZE      std::vector<uint8_t> binary(ARM_BINARY_SIZE);      file.read(reinterpret_cast<char*>(binary.data()), ARM_BINARY_SIZE);      if (file.bad())      {          throw mpm::runtime_error("Error reading AD9371 ARM binary at path " + path); +        return {};      }      return binary;  } -void ad937x_device::_initialize_rf() -{ -    // Set frequencies -    tune(uhd::RX_DIRECTION, RX_DEFAULT_FREQ, true); -    tune(uhd::TX_DIRECTION, TX_DEFAULT_FREQ, true); - -    if (!get_pll_lock_status(pll_t::CLK_SYNTH)) -    { -        throw mpm::runtime_error("CLK SYNTH PLL became unlocked!"); -    } - -    // Set gain control GPIO pins -    _apply_gain_pins(uhd::RX_DIRECTION, chain_t::ONE); -    _apply_gain_pins(uhd::RX_DIRECTION, chain_t::TWO); -    _apply_gain_pins(uhd::TX_DIRECTION, chain_t::ONE); -    _apply_gain_pins(uhd::TX_DIRECTION, chain_t::TWO); - -    // Set manual gain values -    set_gain(uhd::RX_DIRECTION, chain_t::ONE, RX_DEFAULT_GAIN); -    set_gain(uhd::RX_DIRECTION, chain_t::TWO, RX_DEFAULT_GAIN); -    set_gain(uhd::TX_DIRECTION, chain_t::ONE, TX_DEFAULT_GAIN); -    set_gain(uhd::TX_DIRECTION, chain_t::TWO, TX_DEFAULT_GAIN); - -    // TODO: add calibration stuff -} -  void ad937x_device::_verify_product_id()  {      uint8_t product_id = get_product_id(); @@ -191,13 +203,166 @@ void ad937x_device::_verify_multichip_sync_status(multichip_sync_t mcs)      }  } +// 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_device::_convert_rx_gain_to_mykonos(double gain) +{ +    // TODO: derive 195 constant from gain table +    // gain should be a value 0-60, add 195 to make 195-255 +    return static_cast<uint8_t>((gain * 2) + 195); +} + +double ad937x_device::_convert_rx_gain_from_mykonos(uint8_t gain) +{ +    return (static_cast<double>(gain) - 195) / 2.0; +} + +// 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_device::_convert_tx_gain_to_mykonos(double gain) +{ +    // attenuation is inverted and in mdB not dB +    return static_cast<uint16_t>((MAX_TX_GAIN - (gain)) * 1e3); +} + +double ad937x_device::_convert_tx_gain_from_mykonos(uint16_t gain) +{ +    return (MAX_TX_GAIN - (static_cast<double>(gain) / 1e3)); +} + +void ad937x_device::_apply_gain_pins(direction_t direction, chain_t chain) +{ +    using namespace std::placeholders; + +    // get this channels configuration +    auto chan = gain_ctrl.config.at(direction).at(chain); + +    // TX direction does not support different steps per direction +    if (direction == TX_DIRECTION) +    { +        MPM_ASSERT_THROW(chan.inc_step == chan.dec_step); +    } + +    auto state = _move_to_config_state(); + +    switch (direction) +    { +        case RX_DIRECTION: +        { +            std::function<decltype(MYKONOS_setRx1GainCtrlPin)> func; +            switch (chain) +            { +            case chain_t::ONE: +                func = MYKONOS_setRx1GainCtrlPin; +                break; +            case chain_t::TWO: +                func = MYKONOS_setRx2GainCtrlPin; +                break; +            } +            _call_gpio_api_function( +                std::bind(func, +                    mykonos_config.device, +                    chan.inc_step, +                    chan.dec_step, +                    chan.inc_pin, +                    chan.dec_pin, +                    chan.enable)); +            break; +        } +        case TX_DIRECTION: +        { +            // TX sets attenuation, but the configuration should be stored correctly +            std::function<decltype(MYKONOS_setTx2AttenCtrlPin)> func; +            switch (chain) +            { +            case chain_t::ONE: +                // TX1 has an extra parameter "useTx1ForTx2" that we do not support +                func = std::bind(MYKONOS_setTx1AttenCtrlPin, _1, _2, _3, _4, _5, 0); +                break; +            case chain_t::TWO: +                func = MYKONOS_setTx2AttenCtrlPin; +                break; +            } +            _call_gpio_api_function( +                std::bind(func, +                    mykonos_config.device, +                    chan.inc_step, +                    chan.inc_pin, +                    chan.dec_pin, +                    chan.enable)); +            break; +        } +    } +    _restore_from_config_state(state); +} + + + +/****************************************************** +Initialization functions +******************************************************/ + +ad937x_device::ad937x_device( +    mpm::types::regs_iface* iface, +    gain_pins_t gain_pins) : +    full_spi_settings(iface), +    mykonos_config(&full_spi_settings.spi_settings), +    gain_ctrl(gain_pins) +{ +} + +void ad937x_device::_initialize_rf() +{ +    // TODO: add setRfPllLoopFilter here when we upgrade the API to >3546 + +    // Set frequencies +    tune(uhd::RX_DIRECTION, RX_DEFAULT_FREQ, false); +    tune(uhd::TX_DIRECTION, TX_DEFAULT_FREQ, false); + +    if (!get_pll_lock_status(CLK_SYNTH | RX_SYNTH | TX_SYNTH | SNIFF_SYNTH, true)) +    { +        throw mpm::runtime_error("PLLs did not lock after initial tuning!"); +    } + +    // Set gain control GPIO pins +    _apply_gain_pins(uhd::RX_DIRECTION, chain_t::ONE); +    _apply_gain_pins(uhd::RX_DIRECTION, chain_t::TWO); +    _apply_gain_pins(uhd::TX_DIRECTION, chain_t::ONE); +    _apply_gain_pins(uhd::TX_DIRECTION, chain_t::TWO); + +    _call_gpio_api_function(std::bind(MYKONOS_setupGpio, mykonos_config.device)); + +    // Set manual gain values +    set_gain(uhd::RX_DIRECTION, chain_t::ONE, RX_DEFAULT_GAIN); +    set_gain(uhd::RX_DIRECTION, chain_t::TWO, RX_DEFAULT_GAIN); +    set_gain(uhd::TX_DIRECTION, chain_t::ONE, TX_DEFAULT_GAIN); +    set_gain(uhd::TX_DIRECTION, chain_t::TWO, TX_DEFAULT_GAIN); + +    // Run and wait for init cals +    _call_api_function(std::bind(MYKONOS_runInitCals, mykonos_config.device, INIT_CALS)); + +    uint8_t errorFlag = 0, errorCode = 0; +    _call_api_function(std::bind(MYKONOS_waitInitCals, mykonos_config.device, INIT_CAL_TIMEOUT_MS, &errorFlag, &errorCode)); + +    if ((errorFlag != 0) || (errorCode != 0)) +    { +        throw mpm::runtime_error("Init cals failed!"); +        // TODO: add more debugging information here +    } + +    _call_api_function(std::bind(MYKONOS_enableTrackingCals, mykonos_config.device, TRACKING_CALS)); +    // ready for radioOn +} +  void ad937x_device::begin_initialization()  {      _call_api_function(std::bind(MYKONOS_initialize, mykonos_config.device));      _verify_product_id(); -    if (!get_pll_lock_status(pll_t::CLK_SYNTH)) +    if (!get_pll_lock_status(CLK_SYNTH))      {          throw mpm::runtime_error("AD937x CLK_SYNTH PLL failed to lock");      } @@ -232,6 +397,22 @@ void ad937x_device::start_jesd_rx()      _call_api_function(std::bind(MYKONOS_enableSysrefToDeframer, mykonos_config.device, 1));  } +void ad937x_device::start_radio() +{ +    _call_api_function(std::bind(MYKONOS_radioOn, mykonos_config.device)); +} + +void ad937x_device::stop_radio() +{ +    _call_api_function(std::bind(MYKONOS_radioOff, mykonos_config.device)); +} + + + +/****************************************************** +Get status functions +******************************************************/ +  uint8_t ad937x_device::get_multichip_sync_status()  {      uint8_t mcs_status = 0; @@ -261,21 +442,6 @@ uint16_t ad937x_device::get_ilas_config_match()      return ilas_status;  } -void ad937x_device::enable_jesd_loopback(uint8_t enable) -{ -    _call_api_function(std::bind(MYKONOS_setRxFramerDataSource, mykonos_config.device, enable)); -} - -ad937x_device::ad937x_device( -        mpm::types::regs_iface* iface, -        gain_pins_t gain_pins -) : -    full_spi_settings(iface), -    mykonos_config(&full_spi_settings.spi_settings), -    gain_ctrl(gain_pins) -{ -} -  uint8_t ad937x_device::get_product_id()  {      uint8_t id; @@ -313,11 +479,28 @@ arm_version_t ad937x_device::get_arm_version()      return arm;  } + + +/****************************************************** +Set configuration functions +******************************************************/ + +void ad937x_device::enable_jesd_loopback(uint8_t enable) +{ +    auto state = _move_to_config_state(); +    _call_api_function(std::bind(MYKONOS_setRxFramerDataSource, mykonos_config.device, enable)); +    _restore_from_config_state(state); +} +  double ad937x_device::set_clock_rate(double req_rate)  {      auto rate = static_cast<uint32_t>(req_rate / 1000.0); + +    auto state = _move_to_config_state();      mykonos_config.device->clocks->deviceClock_kHz = rate;      _call_api_function(std::bind(MYKONOS_initDigitalClocks, mykonos_config.device)); +    _restore_from_config_state(state); +      return static_cast<double>(rate);  } @@ -326,6 +509,9 @@ void ad937x_device::enable_channel(direction_t direction, chain_t chain, bool en      // TODO:      // Turns out the only code in the API that actually sets the channel enable settings      // is _initialize(). Need to figure out how to deal with this. +    // mmeserve 8/24/2017 +    // While it is possible to change the enable state after disabling the radio, we'll probably +    // always use the GPIO pins to do so. Delete this function at a later time.  }  double ad937x_device::tune(direction_t direction, double value, bool wait_for_lock = false) @@ -334,91 +520,39 @@ double ad937x_device::tune(direction_t direction, double value, bool wait_for_lo      // but here it is      mykonosRfPllName_t pll; -    pll_t locked_pll; +    uint8_t locked_pll; +    uint64_t* config_value;      uint64_t integer_value = static_cast<uint64_t>(value);      switch (direction)      {      case TX_DIRECTION:          pll = TX_PLL; -        locked_pll = pll_t::TX_SYNTH; -        mykonos_config.device->tx->txPllLoFrequency_Hz = integer_value; +        locked_pll = TX_SYNTH; +        config_value = &(mykonos_config.device->tx->txPllLoFrequency_Hz);          break;      case RX_DIRECTION:          pll = RX_PLL; -        locked_pll = pll_t::RX_SYNTH; -        mykonos_config.device->rx->rxPllLoFrequency_Hz = integer_value; +        locked_pll = RX_SYNTH; +        config_value = &(mykonos_config.device->rx->rxPllLoFrequency_Hz);          break;      default:          MPM_THROW_INVALID_CODE_PATH();      } +    auto state = _move_to_config_state(); +    *config_value = integer_value;      _call_api_function(std::bind(MYKONOS_setRfPllFrequency, mykonos_config.device, pll, integer_value)); -    auto lock_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(200); - -    // 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; -    _call_api_function(std::bind(MYKONOS_getRfPllFrequency, mykonos_config.device, pll, &coerced_pll));      if (wait_for_lock)      { -        bool locked = false; -        while (not locked and lock_time > std::chrono::steady_clock::now()) -        { -            locked = get_pll_lock_status(locked_pll); -        } - -        if (!locked) +        if (!get_pll_lock_status(locked_pll, true))          { -            if (!get_pll_lock_status(locked_pll)) // last chance -            { -                throw mpm::runtime_error("RF PLL did not lock"); -            } +            throw mpm::runtime_error("PLL did not lock");          }      } +    _restore_from_config_state(state); -    return static_cast<double>(coerced_pll); -} - -double ad937x_device::get_freq(direction_t direction) -{ -    mykonosRfPllName_t pll; -    switch (direction) -    { -    case TX_DIRECTION: pll = TX_PLL; break; -    case RX_DIRECTION: pll = RX_PLL; break; -    default: -        MPM_THROW_INVALID_CODE_PATH(); -    } - -    // 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; -    _call_api_function(std::bind(MYKONOS_getRfPllFrequency, mykonos_config.device, pll, &coerced_pll)); -    return static_cast<double>(coerced_pll); -} - -bool ad937x_device::get_pll_lock_status(pll_t pll) -{ -    uint8_t pll_status; -    _call_api_function(std::bind(MYKONOS_checkPllsLockStatus, mykonos_config.device, &pll_status)); - -    switch (pll) -    { -    case pll_t::CLK_SYNTH: -        return (pll_status & 0x01) > 0; -    case pll_t::RX_SYNTH: -        return (pll_status & 0x02) > 0; -    case pll_t::TX_SYNTH: -        return (pll_status & 0x04) > 0; -    case pll_t::SNIFF_SYNTH: -        return (pll_status & 0x08) > 0; -    case pll_t::CALPLL_SDM: -        return (pll_status & 0x10) > 0; -    default: -        MPM_THROW_INVALID_CODE_PATH(); -        return false; -    } +    return get_freq(direction);  }  double ad937x_device::set_bw_filter(direction_t direction, chain_t chain, double value) @@ -427,33 +561,16 @@ double ad937x_device::set_bw_filter(direction_t direction, chain_t chain, double      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_device::_convert_rx_gain(double gain) -{ -    // gain should be a value 0-60, add 195 to make 195-255 -    return static_cast<uint8_t>((gain * 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_device::_convert_tx_gain(double gain) -{ -    // attenuation is inverted and in mdB not dB -    return static_cast<uint16_t>((MAX_TX_GAIN - (gain)) * 1e3); -} -  double ad937x_device::set_gain(direction_t direction, chain_t chain, double value)  {      double coerced_value; +    auto state = _move_to_config_state();      switch (direction)      {      case TX_DIRECTION:      { -        uint16_t attenuation = _convert_tx_gain(value); +        uint16_t attenuation = _convert_tx_gain_to_mykonos(value);          coerced_value = static_cast<double>(attenuation);          std::function<mykonosErr_t(mykonosDevice_t*, uint16_t)> func; @@ -473,7 +590,7 @@ double ad937x_device::set_gain(direction_t direction, chain_t chain, double valu      }      case RX_DIRECTION:      { -        uint8_t gain = _convert_rx_gain(value); +        uint8_t gain = _convert_rx_gain_to_mykonos(value);          coerced_value = static_cast<double>(gain);          std::function<mykonosErr_t(mykonosDevice_t*, uint8_t)> func; @@ -494,31 +611,40 @@ double ad937x_device::set_gain(direction_t direction, chain_t chain, double valu      default:          MPM_THROW_INVALID_CODE_PATH();      } + +    _restore_from_config_state(state); +    // TODO: coerced_value here is wrong, should just call get_gain      return coerced_value;  }  void ad937x_device::set_agc_mode(direction_t direction, gain_mode_t mode)  { +    mykonosGainMode_t mykonos_mode;      switch (direction)      {      case RX_DIRECTION:          switch (mode)          {          case gain_mode_t::MANUAL: -            _call_api_function(std::bind(MYKONOS_setRxGainControlMode, mykonos_config.device, MGC)); +            mykonos_mode = MGC;              break;          case gain_mode_t::AUTOMATIC: -            _call_api_function(std::bind(MYKONOS_setRxGainControlMode, mykonos_config.device, AGC)); +            mykonos_mode = AGC;              break;          case gain_mode_t::HYBRID: -            _call_api_function(std::bind(MYKONOS_setRxGainControlMode, mykonos_config.device, HYBRID)); +            mykonos_mode = HYBRID;              break;          default:              MPM_THROW_INVALID_CODE_PATH();          } +        break;      default:          MPM_THROW_INVALID_CODE_PATH();      } + +    auto state = _move_to_config_state(); +    _call_api_function(std::bind(MYKONOS_setRxGainControlMode, mykonos_config.device, mykonos_mode)); +    _restore_from_config_state(state);  }  void ad937x_device::set_fir( @@ -538,30 +664,26 @@ void ad937x_device::set_fir(      default:          MPM_THROW_INVALID_CODE_PATH();      } + +    // TODO: reload this on device  } -std::vector<int16_t> ad937x_device::get_fir( -    const direction_t direction, -    const chain_t chain, -    int8_t &gain) +void ad937x_device::set_gain_pin_step_sizes(direction_t direction, chain_t chain, double inc_step, double dec_step)  { -    switch (direction) +    if (direction == RX_DIRECTION)      { -    case TX_DIRECTION: -        return mykonos_config.tx_fir_config.get_fir(gain); -    case RX_DIRECTION: -        return mykonos_config.rx_fir_config.get_fir(gain); -    default: +        gain_ctrl.config.at(direction).at(chain).inc_step = static_cast<uint8_t>(inc_step / 0.5); +        gain_ctrl.config.at(direction).at(chain).dec_step = static_cast<uint8_t>(dec_step / 0.5); +    } +    else if (direction == TX_DIRECTION) { +        // !!! TX is attenuation direction, so the pins are flipped !!! +        gain_ctrl.config.at(direction).at(chain).dec_step = static_cast<uint8_t>(inc_step / 0.05); +        gain_ctrl.config.at(direction).at(chain).inc_step = static_cast<uint8_t>(dec_step / 0.05); +    } +    else {          MPM_THROW_INVALID_CODE_PATH();      } -} - -int16_t ad937x_device::get_temperature() -{ -    // TODO: deal with the status.tempValid flag -    mykonosTempSensorStatus_t status; -    _call_gpio_api_function(std::bind(MYKONOS_readTempSensor, mykonos_config.device, &status)); -    return status.tempCode; +    _apply_gain_pins(direction, chain);  }  void ad937x_device::set_enable_gain_pins(direction_t direction, chain_t chain, bool enable) @@ -570,80 +692,118 @@ void ad937x_device::set_enable_gain_pins(direction_t direction, chain_t chain, b      _apply_gain_pins(direction, chain);  } -void ad937x_device::set_gain_pin_step_sizes(direction_t direction, chain_t chain, double inc_step, double dec_step) + + +/****************************************************** +Get configuration functions +******************************************************/ + +double ad937x_device::get_freq(direction_t direction)  { -    if (direction == RX_DIRECTION) +    mykonosRfPllName_t pll; +    switch (direction)      { -        gain_ctrl.config.at(direction).at(chain).inc_step = static_cast<uint8_t>(inc_step / 0.5); -        gain_ctrl.config.at(direction).at(chain).dec_step = static_cast<uint8_t>(dec_step / 0.5); -    } else if (direction == TX_DIRECTION) { -        // !!! TX is attenuation direction, so the pins are flipped !!! -        gain_ctrl.config.at(direction).at(chain).dec_step = static_cast<uint8_t>(inc_step / 0.05); -        gain_ctrl.config.at(direction).at(chain).inc_step = static_cast<uint8_t>(dec_step / 0.05); -    } else { +    case TX_DIRECTION: pll = TX_PLL; break; +    case RX_DIRECTION: pll = RX_PLL; break; +    default:          MPM_THROW_INVALID_CODE_PATH();      } -    _apply_gain_pins(direction, chain); + +    // 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; +    _call_api_function(std::bind(MYKONOS_getRfPllFrequency, mykonos_config.device, pll, &coerced_pll)); +    return static_cast<double>(coerced_pll);  } -void ad937x_device::_apply_gain_pins(direction_t direction, chain_t chain) +bool ad937x_device::get_pll_lock_status(uint8_t pll, bool wait_for_lock)  { -    using namespace std::placeholders; - -    // get this channels configuration -    auto chan = gain_ctrl.config.at(direction).at(chain); +    uint8_t pll_status; +    _call_api_function(std::bind(MYKONOS_checkPllsLockStatus, mykonos_config.device, &pll_status)); -    // TX direction does not support different steps per direction -    if (direction == TX_DIRECTION) +    if (not wait_for_lock)      { -        MPM_ASSERT_THROW(chan.inc_step == chan.dec_step); +        return (pll_status & pll) == pll;      } +    else { +        auto lock_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(PLL_LOCK_TIMEOUT_MS); +        bool locked = false; +        while (not locked and lock_time > std::chrono::steady_clock::now()) +        { +            locked = get_pll_lock_status(pll); +        } +        if (!locked) +        { +            // last chance +            locked = get_pll_lock_status(pll); +        } +        return locked; +    } +} + +double ad937x_device::get_gain(direction_t direction, chain_t chain) +{      switch (direction)      { -        case RX_DIRECTION: +        case TX_DIRECTION:          { -            std::function<decltype(MYKONOS_setRx1GainCtrlPin)> func; +            std::function<mykonosErr_t(mykonosDevice_t*, uint16_t*)> func;              switch (chain)              {              case chain_t::ONE: -                func = MYKONOS_setRx1GainCtrlPin; +                func = MYKONOS_getTx1Attenuation;                  break;              case chain_t::TWO: -                func = MYKONOS_setRx2GainCtrlPin; +                func = MYKONOS_getTx2Attenuation;                  break;              } -            _call_gpio_api_function( -                std::bind(func, -                    mykonos_config.device, -                    chan.inc_step, -                    chan.dec_step, -                    chan.inc_pin, -                    chan.dec_pin, -                    chan.enable)); +            uint16_t atten; +            _call_api_function(std::bind(func, mykonos_config.device, &atten)); +            return _convert_tx_gain_from_mykonos(atten);          } -        case TX_DIRECTION: +        case RX_DIRECTION:          { -            // TX sets attenuation, but the configuration should be stored correctly -            std::function<decltype(MYKONOS_setTx2AttenCtrlPin)> func; +            std::function<mykonosErr_t(mykonosDevice_t*, uint8_t*)> func;              switch (chain)              {              case chain_t::ONE: -                // TX1 has an extra parameter "useTx1ForTx2" that we do not support -                func = std::bind(MYKONOS_setTx1AttenCtrlPin, _1, _2, _3, _4, _5, 0); +                func = MYKONOS_getRx1Gain;                  break;              case chain_t::TWO: -                func = MYKONOS_setTx2AttenCtrlPin; +                func = MYKONOS_getRx2Gain;                  break;              } -            _call_gpio_api_function( -                std::bind(func, -                    mykonos_config.device, -                    chan.inc_step, -                    chan.inc_pin, -                    chan.dec_pin, -                    chan.enable)); +            uint8_t gain; +            _call_api_function(std::bind(func, mykonos_config.device, &gain)); +            return _convert_rx_gain_from_mykonos(gain);          } +        default: +            MPM_THROW_INVALID_CODE_PATH();      }  } +std::vector<int16_t> ad937x_device::get_fir( +    const direction_t direction, +    const chain_t chain, +    int8_t &gain) +{ +    switch (direction) +    { +    case TX_DIRECTION: +        return mykonos_config.tx_fir_config.get_fir(gain); +    case RX_DIRECTION: +        return mykonos_config.rx_fir_config.get_fir(gain); +    default: +        MPM_THROW_INVALID_CODE_PATH(); +    } +} + +int16_t ad937x_device::get_temperature() +{ +    // TODO: deal with the status.tempValid flag +    mykonosTempSensorStatus_t status; +    _call_gpio_api_function(std::bind(MYKONOS_readTempSensor, mykonos_config.device, &status)); +    return status.tempCode; +} + | 
