diff options
Diffstat (limited to 'host/lib/usrp/common')
| -rw-r--r-- | host/lib/usrp/common/ad9361_ctrl.cpp | 78 | ||||
| -rw-r--r-- | host/lib/usrp/common/ad9361_ctrl.hpp | 51 | ||||
| -rw-r--r-- | host/lib/usrp/common/ad9361_driver/ad9361_device.cpp | 981 | ||||
| -rw-r--r-- | host/lib/usrp/common/ad9361_driver/ad9361_device.h | 121 | ||||
| -rw-r--r-- | host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h | 162 | ||||
| -rw-r--r-- | host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h | 28 | 
6 files changed, 1215 insertions, 206 deletions
diff --git a/host/lib/usrp/common/ad9361_ctrl.cpp b/host/lib/usrp/common/ad9361_ctrl.cpp index bedd2eb32..f3ab36247 100644 --- a/host/lib/usrp/common/ad9361_ctrl.cpp +++ b/host/lib/usrp/common/ad9361_ctrl.cpp @@ -16,7 +16,6 @@  //  #include "ad9361_ctrl.hpp" -#include <uhd/exception.hpp>  #include <uhd/types/ranges.hpp>  #include <uhd/utils/msg.hpp>  #include <uhd/types/serial.hpp> @@ -108,6 +107,27 @@ public:          return _device.set_gain(direction, chain, value);      } +    void set_agc(const std::string &which, bool enable) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); + +        ad9361_device_t::chain_t chain =_get_chain_from_antenna(which); +         _device.set_agc(chain, enable); +    } + +    void set_agc_mode(const std::string &which, const std::string &mode) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); +        ad9361_device_t::chain_t chain =_get_chain_from_antenna(which); +        if(mode == "slow") { +            _device.set_agc_mode(chain, ad9361_device_t::GAIN_MODE_SLOW_AGC); +        } else if (mode == "fast"){ +            _device.set_agc_mode(chain, ad9361_device_t::GAIN_MODE_FAST_AGC); +        } else { +            throw uhd::runtime_error("ad9361_ctrl got an invalid AGC option."); +        } +    } +      //! set a new clock rate, return the exact value      double set_clock_rate(const double rate)      { @@ -166,6 +186,62 @@ public:          return sensor_value_t("RSSI", _device.get_rssi(chain), "dB");      } +    //! read the internal temp sensor. Average over 3 results +    sensor_value_t get_temperature() +    { +        return sensor_value_t("temp", _device.get_average_temperature(), "C"); +    } + +    void set_dc_offset_auto(const std::string &which, const bool on) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); + +        ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); +        _device.set_dc_offset_auto(direction,on); +    } + +    void set_iq_balance_auto(const std::string &which, const bool on) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); + +        ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); +        _device.set_iq_balance_auto(direction,on); +    } + +    double set_bw_filter(const std::string &which, const double bw) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); + +        ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); +        return _device.set_bw_filter(direction, bw); +    } + +    std::vector<std::string> get_filter_names(const std::string &which) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); + +        ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); +        return _device.get_filter_names(direction); +    } + +    filter_info_base::sptr get_filter(const std::string &which, const std::string &filter_name) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); + +        ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); +        ad9361_device_t::chain_t chain =_get_chain_from_antenna(which); +        return _device.get_filter(direction, chain, filter_name); +    } + +    void set_filter(const std::string &which, const std::string &filter_name, const filter_info_base::sptr filter) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); + +        ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); +        ad9361_device_t::chain_t chain = _get_chain_from_antenna(which); +        _device.set_filter(direction, chain, filter_name, filter); +    } +  private:      static ad9361_device_t::direction_t _get_direction_from_antenna(const std::string& antenna)      { diff --git a/host/lib/usrp/common/ad9361_ctrl.hpp b/host/lib/usrp/common/ad9361_ctrl.hpp index c5718302f..a6d65ad11 100644 --- a/host/lib/usrp/common/ad9361_ctrl.hpp +++ b/host/lib/usrp/common/ad9361_ctrl.hpp @@ -22,9 +22,13 @@  #include <uhd/types/ranges.hpp>  #include <uhd/types/serial.hpp>  #include <uhd/types/sensors.hpp> +#include <uhd/exception.hpp>  #include <boost/shared_ptr.hpp>  #include <ad9361_device.h>  #include <string> +#include <complex> +#include <uhd/types/filters.hpp> +#include <vector>  namespace uhd { namespace usrp { @@ -77,15 +81,18 @@ public:          return uhd::meta_range_t(5e6, ad9361_device_t::AD9361_MAX_CLOCK_RATE); //5 MHz DCM low end      } -    //! set the filter bandwidth for the frontend -    double set_bw_filter(const std::string &/*which*/, const double /*bw*/) -    { -        return 56e6; //TODO -    } +    //! set the filter bandwidth for the frontend's analog low pass +    virtual double set_bw_filter(const std::string &/*which*/, const double /*bw*/) = 0;      //! set the gain for a particular gain element      virtual double set_gain(const std::string &which, const double value) = 0; +    //! Enable or disable the AGC module +    virtual void set_agc(const std::string &which, bool enable) = 0; + +    //! configure the AGC module to slow or fast mode +    virtual void set_agc_mode(const std::string &which, const std::string &mode) = 0; +      //! set a new clock rate, return the exact value      virtual double set_clock_rate(const double rate) = 0; @@ -95,11 +102,43 @@ public:      //! tune the given frontend, return the exact value      virtual double tune(const std::string &which, const double value) = 0; -    //! turn on/off data port loopback +    //! set the DC offset for I and Q manually +    void set_dc_offset(const std::string &, const std::complex<double>) +    { +        //This feature should not be used according to Analog Devices +        throw uhd::runtime_error("ad9361_ctrl::set_dc_offset this feature is not supported on this device."); +    } + +    //! enable or disable the BB/RF DC tracking feature +    virtual void set_dc_offset_auto(const std::string &which, const bool on) = 0; + +    //! set the IQ correction value manually +    void set_iq_balance(const std::string &, const std::complex<double>) +    { +        //This feature should not be used according to Analog Devices +        throw uhd::runtime_error("ad9361_ctrl::set_iq_balance this feature is not supported on this device."); +    } + +    //! enable or disable the quadrature calibration +    virtual void set_iq_balance_auto(const std::string &which, const bool on) = 0; + +    //! turn on/off Catalina's data port loopback      virtual void data_port_loopback(const bool on) = 0;      //! read internal RSSI sensor      virtual sensor_value_t get_rssi(const std::string &which) = 0; + +    //! read the internal temp sensor +    virtual sensor_value_t get_temperature() = 0; + +    //! List all available filters by name +    virtual std::vector<std::string> get_filter_names(const std::string &which) = 0; + +    //! Return a list of all filters +    virtual filter_info_base::sptr get_filter(const std::string &which, const std::string &filter_name) = 0; + +    //! Write back a filter +    virtual void set_filter(const std::string &which, const std::string &filter_name, const filter_info_base::sptr) = 0;  };  }} diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp index e3139cd39..8737837b3 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp +++ b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp @@ -11,6 +11,7 @@  #include <cmath>  #include <uhd/exception.hpp>  #include <uhd/utils/log.hpp> +#include <uhd/utils/msg.hpp>  #include <boost/cstdint.hpp>  #include <boost/date_time/posix_time/posix_time.hpp>  #include <boost/thread/thread.hpp> @@ -78,6 +79,7 @@ int get_num_taps(int max_num_taps) {  const double ad9361_device_t::AD9361_MAX_GAIN        = 89.75;  const double ad9361_device_t::AD9361_MAX_CLOCK_RATE  = 61.44e6; +const double ad9361_device_t::AD9361_CAL_VALID_WINDOW = 100e6;  // Max bandwdith is due to filter rolloff in analog filter stage  const double ad9361_device_t::AD9361_RECOMMENDED_MAX_BANDWIDTH = 56e6; @@ -87,7 +89,7 @@ const double ad9361_device_t::AD9361_RECOMMENDED_MAX_BANDWIDTH = 56e6;   * how many taps are in the filter, and given a vector of the taps   * themselves.  */ -void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, boost::uint16_t *coeffs) +void ad9361_device_t::_program_fir_filter(direction_t direction, chain_t chain, int num_taps, boost::uint16_t *coeffs)  {      boost::uint16_t base; @@ -102,8 +104,20 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b      /* Encode number of filter taps for programming register */      boost::uint8_t reg_numtaps = (((num_taps / 16) - 1) & 0x07) << 5; +    boost::uint8_t reg_chain = 0; +    switch (chain) { +    case CHAIN_1: +        reg_chain = 0x01 << 3; +        break; +    case CHAIN_2: +        reg_chain = 0x02 << 3; +        break; +    default: +        reg_chain = 0x03 << 3; +    } +      /* Turn on the filter clock. */ -    _io_iface->poke8(base + 5, reg_numtaps | 0x1a); +    _io_iface->poke8(base + 5, reg_numtaps | reg_chain | 0x02);      boost::this_thread::sleep(boost::posix_time::milliseconds(1));      /* Zero the unused taps just in case they have stale data */ @@ -112,7 +126,7 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b          _io_iface->poke8(base + 0, addr);          _io_iface->poke8(base + 1, 0x0);          _io_iface->poke8(base + 2, 0x0); -        _io_iface->poke8(base + 5, reg_numtaps | 0x1e); +        _io_iface->poke8(base + 5, reg_numtaps | reg_chain | (1 << 1) | (1 << 2));          _io_iface->poke8(base + 4, 0x00);          _io_iface->poke8(base + 4, 0x00);      } @@ -122,7 +136,7 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b          _io_iface->poke8(base + 0, addr);          _io_iface->poke8(base + 1, (coeffs[addr]) & 0xff);          _io_iface->poke8(base + 2, (coeffs[addr] >> 8) & 0xff); -        _io_iface->poke8(base + 5, reg_numtaps | 0x1e); +        _io_iface->poke8(base + 5, reg_numtaps | reg_chain | (1 << 1) | (1 << 2));          _io_iface->poke8(base + 4, 0x00);          _io_iface->poke8(base + 4, 0x00);      } @@ -133,9 +147,9 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b       before the clock stops. Wait 4 sample clock periods after setting D2 high while that data writes into the table"       */ -    _io_iface->poke8(base + 5, reg_numtaps | 0x1A); +    _io_iface->poke8(base + 5, reg_numtaps | reg_chain | (1 << 1));      if (direction == RX) { -        _io_iface->poke8(base + 5, reg_numtaps | 0x18); +        _io_iface->poke8(base + 5, reg_numtaps | reg_chain );          /* Rx Gain, set to prevent digital overflow/saturation in filters             0:+6dB, 1:0dB, 2:-6dB, 3:-12dB             page 35 of UG-671 */ @@ -144,7 +158,7 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b          /* Tx Gain. bit[0]. set to prevent digital overflow/saturation in filters             0: 0dB, 1:-6dB             page 25 of UG-671 */ -        _io_iface->poke8(base + 5, reg_numtaps | 0x18); +        _io_iface->poke8(base + 5, reg_numtaps | reg_chain );      }  } @@ -175,7 +189,7 @@ void ad9361_device_t::_setup_rx_fir(size_t num_taps, boost::int32_t decimation)          }      } -    _program_fir_filter(RX, num_taps, coeffs.get()); +    _program_fir_filter(RX, CHAIN_BOTH, num_taps, coeffs.get());  }  /* Program the TX FIR Filter. */ @@ -207,7 +221,7 @@ void ad9361_device_t::_setup_tx_fir(size_t num_taps, boost::int32_t interpolatio          }      } -    _program_fir_filter(TX, num_taps, coeffs.get()); +    _program_fir_filter(TX, CHAIN_BOTH, num_taps, coeffs.get());  }  /*********************************************************************** @@ -282,16 +296,24 @@ void ad9361_device_t::_calibrate_synth_charge_pumps()   *   * Note that the filter calibration depends heavily on the baseband   * bandwidth, so this must be re-done after any change to the RX sample - * rate. */ -double ad9361_device_t::_calibrate_baseband_rx_analog_filter() + * rate. + * UG570 Page 33 states that this filter should be calibrated to 1.4 * bbbw*/ +double ad9361_device_t::_calibrate_baseband_rx_analog_filter(double req_rfbw)  { -    /* For filter tuning, baseband BW is half the complex BW, and must be -     * between 28e6 and 0.2e6. */ -    double bbbw = _baseband_bw / 2.0; +    double bbbw = req_rfbw / 2.0; +    if(bbbw > _baseband_bw / 2.0) +    { +        UHD_LOG << "baseband bandwidth too large for current sample rate. Setting bandwidth to: "<<_baseband_bw; +        bbbw = _baseband_bw / 2.0; +    } + +    /* Baseband BW must be between 28e6 and 0.143e6. +     * Max filter BW is 39.2 MHz. 39.2 / 1.4 = 28 +     * Min filter BW is 200kHz. 200 / 1.4 = 143 */      if (bbbw > 28e6) {          bbbw = 28e6; -    } else if (bbbw < 0.20e6) { -        bbbw = 0.20e6; +    } else if (bbbw < 0.143e6) { +        bbbw = 0.143e6;      }      double rxtune_clk = ((1.4 * bbbw * 2 * M_PI) / M_LN2); @@ -340,16 +362,25 @@ double ad9361_device_t::_calibrate_baseband_rx_analog_filter()   *   * Note that the filter calibration depends heavily on the baseband   * bandwidth, so this must be re-done after any change to the TX sample - * rate. */ -double ad9361_device_t::_calibrate_baseband_tx_analog_filter() + * rate. + * UG570 Page 32 states that this filter should be calibrated to 1.6 * bbbw*/ +double ad9361_device_t::_calibrate_baseband_tx_analog_filter(double req_rfbw)  { -    /* For filter tuning, baseband BW is half the complex BW, and must be -     * between 28e6 and 0.2e6. */ -    double bbbw = _baseband_bw / 2.0; +    double bbbw = req_rfbw / 2.0; + +    if(bbbw > _baseband_bw / 2.0) +    { +        UHD_LOG << "baseband bandwidth too large for current sample rate. Setting bandwidth to: "<<_baseband_bw; +        bbbw = _baseband_bw / 2.0; +    } + +    /* Baseband BW must be between 20e6 and 0.391e6. +     * Max filter BW is 32 MHz. 32 / 1.6 = 20 +     * Min filter BW is 625 kHz. 625 / 1.6 = 391 */      if (bbbw > 20e6) {          bbbw = 20e6; -    } else if (bbbw < 0.625e6) { -        bbbw = 0.625e6; +    } else if (bbbw < 0.391e6) { +        bbbw = 0.391e6;      }      double txtune_clk = ((1.6 * bbbw * 2 * M_PI) / M_LN2); @@ -386,16 +417,25 @@ double ad9361_device_t::_calibrate_baseband_tx_analog_filter()  /* Calibrate the secondary TX filter.   *   * This filter also depends on the TX sample rate, so if a rate change is - * made, the previous calibration will no longer be valid. */ -void ad9361_device_t::_calibrate_secondary_tx_filter() + * made, the previous calibration will no longer be valid. + * UG570 Page 32 states that this filter should be calibrated to 5 * bbbw*/ +double ad9361_device_t::_calibrate_secondary_tx_filter(double req_rfbw)  { -    /* For filter tuning, baseband BW is half the complex BW, and must be -     * between 20e6 and 0.53e6. */ -    double bbbw = _baseband_bw / 2.0; +    double bbbw = req_rfbw / 2.0; + +    if(bbbw > _baseband_bw / 2.0) +    { +        UHD_LOG << "baseband bandwidth too large for current sample rate. Setting bandwidth to: "<<_baseband_bw; +        bbbw = _baseband_bw / 2.0; +    } + +    /* Baseband BW must be between 20e6 and 0.54e6. +     * Max filter BW is 100 MHz. 100 / 5 = 20 +     * Min filter BW is 2.7 MHz. 2.7 / 5 = 0.54 */      if (bbbw > 20e6) {          bbbw = 20e6; -    } else if (bbbw < 0.53e6) { -        bbbw = 0.53e6; +    } else if (bbbw < 0.54e6) { +        bbbw = 0.54e6;      }      double bbbw_mhz = bbbw / 1e6; @@ -456,13 +496,17 @@ void ad9361_device_t::_calibrate_secondary_tx_filter()      _io_iface->poke8(0x0d2, reg0d2);      _io_iface->poke8(0x0d1, reg0d1);      _io_iface->poke8(0x0d0, reg0d0); + +    return bbbw;  }  /* Calibrate the RX TIAs.   *   * Note that the values in the TIA register, after calibration, vary with - * the RX gain settings. */ -void ad9361_device_t::_calibrate_rx_TIAs() + * the RX gain settings. + * We do not really program the BW here. Most settings are taken form the BB LPF registers + * UG570 page 33 states that this filter should be calibrated to 2.5 * bbbw */ +double ad9361_device_t::_calibrate_rx_TIAs(double req_rfbw)  {      boost::uint8_t reg1eb = _io_iface->peek8(0x1eb) & 0x3F;      boost::uint8_t reg1ec = _io_iface->peek8(0x1ec) & 0x7F; @@ -473,13 +517,21 @@ void ad9361_device_t::_calibrate_rx_TIAs()      boost::uint8_t reg1de = 0x00;      boost::uint8_t reg1df = 0x00; -    /* For calibration, baseband BW is half the complex BW, and must be -     * between 28e6 and 0.2e6. */ -    double bbbw = _baseband_bw / 2.0; -    if (bbbw > 20e6) { -        bbbw = 20e6; -    } else if (bbbw < 0.20e6) { -        bbbw = 0.20e6; +    double bbbw = req_rfbw / 2.0; + +    if(bbbw > _baseband_bw / 2.0) +    { +        UHD_LOG << "baseband bandwidth too large for current sample rate. Setting bandwidth to: "<<_baseband_bw; +        bbbw = _baseband_bw / 2.0; +    } + +    /* Baseband BW must be between 28e6 and 0.4e6. +     * Max filter BW is 70 MHz. 70 / 2.5 = 28 +     * Min filter BW is 1 MHz. 1 / 2.5 =  0.4*/ +    if (bbbw > 28e6) { +        bbbw = 28e6; +    } else if (bbbw < 0.40e6) { +        bbbw = 0.40e6;      }      double ceil_bbbw_mhz = std::ceil(bbbw / 1e6); @@ -520,6 +572,8 @@ void ad9361_device_t::_calibrate_rx_TIAs()      _io_iface->poke8(0x1df, reg1df);      _io_iface->poke8(0x1dc, reg1dc);      _io_iface->poke8(0x1de, reg1de); + +    return bbbw;  }  /* Setup the AD9361 ADC. @@ -651,11 +705,12 @@ void ad9361_device_t::_setup_adc()  }  /* Calibrate the baseband DC offset. - * - * Note that this function is called from within the TX quadrature - * calibration function! */ + * Disables tracking + */  void ad9361_device_t::_calibrate_baseband_dc_offset()  { +    _io_iface->poke8(0x18b, 0x83); //Reset RF DC tracking flag +      _io_iface->poke8(0x193, 0x3f); // Calibration settings      _io_iface->poke8(0x190, 0x0f); // Set tracking coefficient      //write_ad9361_reg(device, 0x190, /*0x0f*//*0xDF*/0x80*1 | 0x40*1 | (16+8/*+4*/)); // Set tracking coefficient: don't *4 counter, do decim /4, increased gain shift @@ -675,9 +730,8 @@ void ad9361_device_t::_calibrate_baseband_dc_offset()  }  /* Calibrate the RF DC offset. - * - * Note that this function is called from within the TX quadrature - * calibration function. */ + * Disables tracking + */  void ad9361_device_t::_calibrate_rf_dc_offset()  {      /* Some settings are frequency-dependent. */ @@ -692,7 +746,7 @@ void ad9361_device_t::_calibrate_rf_dc_offset()      }      _io_iface->poke8(0x185, 0x20); // RF DC Offset wait count -    _io_iface->poke8(0x18b, 0x83); +    _io_iface->poke8(0x18b, 0x83); // Disable tracking      _io_iface->poke8(0x189, 0x30);      /* Run the calibration! */ @@ -708,6 +762,16 @@ void ad9361_device_t::_calibrate_rf_dc_offset()      }  } +void ad9361_device_t::_configure_bb_rf_dc_tracking(const bool on) +{ +    if(on) +    { +        _io_iface->poke8(0x18b, 0xad); // Enable BB and RF DC tracking +    } else { +        _io_iface->poke8(0x18b, 0x83); // Disable BB and RF DC tracking +    } +} +  /* Start the RX quadrature calibration.   *   * Note that we are using AD9361's 'tracking' feature for RX quadrature @@ -720,16 +784,20 @@ void ad9361_device_t::_calibrate_rx_quadrature()      _io_iface->poke8(0x16e, 0x25); // RX Gain index to use for cal      _io_iface->poke8(0x16a, 0x75); // Set Kexp phase      _io_iface->poke8(0x16b, 0x15); // Set Kexp amplitude -    _io_iface->poke8(0x169, 0xcf); // Continuous tracking mode -    _io_iface->poke8(0x18b, 0xad); + +    if(_use_iq_balance_correction) +    { +        _io_iface->poke8(0x169, 0xcf); // Continuous tracking mode. Gets disabled in _tx_quadrature_cal_routine! +    }  } -/* TX quadtrature calibration routine. +/* TX quadrature calibration routine.   *   * The TX quadrature needs to be done twice, once for each TX chain, with   * only one register change in between. Thus, this function enacts the   * calibrations, and it is called from calibrate_tx_quadrature. */  void ad9361_device_t::_tx_quadrature_cal_routine() { +      /* This is a weird process, but here is how it works:       * 1) Read the calibrated NCO frequency bits out of 0A3.       * 2) Write the two bits to the RX NCO freq part of 0A0. @@ -774,12 +842,6 @@ void ad9361_device_t::_tx_quadrature_cal_routine() {      _io_iface->poke8(0x0a4, 0xf0); // Cal setting conut      _io_iface->poke8(0x0ae, 0x00); // Cal LPF gain index (split mode) -    /* First, calibrate the baseband DC offset. */ -    _calibrate_baseband_dc_offset(); - -    /* Second, calibrate the RF DC offset. */ -    _calibrate_rf_dc_offset(); -      /* Now, calibrate the TX quadrature! */      size_t count = 0;      _io_iface->poke8(0x016, 0x10); @@ -794,9 +856,7 @@ void ad9361_device_t::_tx_quadrature_cal_routine() {  }  /* Run the TX quadrature calibration. - * - * Note that from within this function we are also triggering the baseband - * and RF DC calibrations. */ + */  void ad9361_device_t::_calibrate_tx_quadrature()  {      /* Make sure we are, in fact, in the ALERT state. If not, something is @@ -880,7 +940,7 @@ void ad9361_device_t::_program_mixer_gm_subtable()  void ad9361_device_t::_program_gain_table() {      /* Figure out which gain table we should be using for our current       * frequency band. */ -    boost::uint8_t (*gain_table)[5] = NULL; +    boost::uint8_t (*gain_table)[3] = NULL;      boost::uint8_t new_gain_table;      if (_rx_freq < 1300e6) {          gain_table = gain_table_sub_1300mhz; @@ -911,9 +971,9 @@ void ad9361_device_t::_program_gain_table() {      boost::uint8_t index = 0;      for (; index < 77; index++) {          _io_iface->poke8(0x130, index); -        _io_iface->poke8(0x131, gain_table[index][1]); -        _io_iface->poke8(0x132, gain_table[index][2]); -        _io_iface->poke8(0x133, gain_table[index][3]); +        _io_iface->poke8(0x131, gain_table[index][0]); +        _io_iface->poke8(0x132, gain_table[index][1]); +        _io_iface->poke8(0x133, gain_table[index][2]);          _io_iface->poke8(0x137, 0x1E);          _io_iface->poke8(0x134, 0x00);          _io_iface->poke8(0x134, 0x00); @@ -939,28 +999,58 @@ void ad9361_device_t::_program_gain_table() {  /* Setup gain control registers.   * - * This really only needs to be done once, at initialization. */ -void ad9361_device_t::_setup_gain_control() + * This really only needs to be done once, at initialization. + * If AGC is used the mode select bits (Reg 0x0FA) must be written manually */ +void ad9361_device_t::_setup_gain_control(bool agc)  { -    _io_iface->poke8(0x0FA, 0xE0); // Gain Control Mode Select -    _io_iface->poke8(0x0FB, 0x08); // Table, Digital Gain, Man Gain Ctrl -    _io_iface->poke8(0x0FC, 0x23); // Incr Step Size, ADC Overrange Size -    _io_iface->poke8(0x0FD, 0x4C); // Max Full/LMT Gain Table Index -    _io_iface->poke8(0x0FE, 0x44); // Decr Step Size, Peak Overload Time -    _io_iface->poke8(0x100, 0x6F); // Max Digital Gain -    _io_iface->poke8(0x104, 0x2F); // ADC Small Overload Threshold -    _io_iface->poke8(0x105, 0x3A); // ADC Large Overload Threshold -    _io_iface->poke8(0x107, 0x31); // Large LMT Overload Threshold -    _io_iface->poke8(0x108, 0x39); // Small LMT Overload Threshold -    _io_iface->poke8(0x109, 0x23); // Rx1 Full/LMT Gain Index -    _io_iface->poke8(0x10A, 0x58); // Rx1 LPF Gain Index -    _io_iface->poke8(0x10B, 0x00); // Rx1 Digital Gain Index -    _io_iface->poke8(0x10C, 0x23); // Rx2 Full/LMT Gain Index -    _io_iface->poke8(0x10D, 0x18); // Rx2 LPF Gain Index -    _io_iface->poke8(0x10E, 0x00); // Rx2 Digital Gain Index -    _io_iface->poke8(0x114, 0x30); // Low Power Threshold -    _io_iface->poke8(0x11A, 0x27); // Initial LMT Gain Limit -    _io_iface->poke8(0x081, 0x00); // Tx Symbol Gain Control +    /* The AGC mode configuration should be good for all cases. +     * However, non AGC configuration still used for backward compatibility. */ +    if (agc) { +        /*mode select bits must be written before hand!*/ +        _io_iface->poke8(0x0FB, 0x08); // Table, Digital Gain, Man Gain Ctrl +        _io_iface->poke8(0x0FC, 0x23); // Incr Step Size, ADC Overrange Size +        _io_iface->poke8(0x0FD, 0x4C); // Max Full/LMT Gain Table Index +        _io_iface->poke8(0x0FE, 0x44); // Decr Step Size, Peak Overload Time +        _io_iface->poke8(0x100, 0x6F); // Max Digital Gain +        _io_iface->poke8(0x101, 0x0A); // Max Digital Gain +        _io_iface->poke8(0x103, 0x08); // Max Digital Gain +        _io_iface->poke8(0x104, 0x2F); // ADC Small Overload Threshold +        _io_iface->poke8(0x105, 0x3A); // ADC Large Overload Threshold +        _io_iface->poke8(0x106, 0x22); // Max Digital Gain +        _io_iface->poke8(0x107, 0x2B); // Large LMT Overload Threshold +        _io_iface->poke8(0x108, 0x31); +        _io_iface->poke8(0x111, 0x0A); +        _io_iface->poke8(0x11A, 0x1C); +        _io_iface->poke8(0x120, 0x0C); +        _io_iface->poke8(0x121, 0x44); +        _io_iface->poke8(0x122, 0x44); +        _io_iface->poke8(0x123, 0x11); +        _io_iface->poke8(0x124, 0xF5); +        _io_iface->poke8(0x125, 0x3B); +        _io_iface->poke8(0x128, 0x03); +        _io_iface->poke8(0x129, 0x56); +        _io_iface->poke8(0x12A, 0x22); +    } else { +        _io_iface->poke8(0x0FA, 0xE0); // Gain Control Mode Select +        _io_iface->poke8(0x0FB, 0x08); // Table, Digital Gain, Man Gain Ctrl +        _io_iface->poke8(0x0FC, 0x23); // Incr Step Size, ADC Overrange Size +        _io_iface->poke8(0x0FD, 0x4C); // Max Full/LMT Gain Table Index +        _io_iface->poke8(0x0FE, 0x44); // Decr Step Size, Peak Overload Time +        _io_iface->poke8(0x100, 0x6F); // Max Digital Gain +        _io_iface->poke8(0x104, 0x2F); // ADC Small Overload Threshold +        _io_iface->poke8(0x105, 0x3A); // ADC Large Overload Threshold +        _io_iface->poke8(0x107, 0x31); // Large LMT Overload Threshold +        _io_iface->poke8(0x108, 0x39); // Small LMT Overload Threshold +        _io_iface->poke8(0x109, 0x23); // Rx1 Full/LMT Gain Index +        _io_iface->poke8(0x10A, 0x58); // Rx1 LPF Gain Index +        _io_iface->poke8(0x10B, 0x00); // Rx1 Digital Gain Index +        _io_iface->poke8(0x10C, 0x23); // Rx2 Full/LMT Gain Index +        _io_iface->poke8(0x10D, 0x18); // Rx2 LPF Gain Index +        _io_iface->poke8(0x10E, 0x00); // Rx2 Digital Gain Index +        _io_iface->poke8(0x114, 0x30); // Low Power Threshold +        _io_iface->poke8(0x11A, 0x27); // Initial LMT Gain Limit +        _io_iface->poke8(0x081, 0x00); // Tx Symbol Gain Control +    }  }  /* Setup the RX or TX synthesizers. @@ -1257,6 +1347,7 @@ double ad9361_device_t::_setup_rates(const double rate)      int divfactor = 0;      _tfir_factor = 0;      _rfir_factor = 0; +      if (rate < 0.33e6) {          // RX1 + RX2 enabled, 3, 2, 2, 4          _regs.rxfilt = B8(11101111); @@ -1412,6 +1503,19 @@ void ad9361_device_t::initialize()      _rx2_gain = 0;      _tx1_gain = 0;      _tx2_gain = 0; +    _use_dc_offset_correction = true; +    _use_iq_balance_correction = true; +    _rx1_agc_mode = GAIN_MODE_SLOW_AGC; +    _rx2_agc_mode = GAIN_MODE_SLOW_AGC; +    _rx1_agc_enable = false; +    _rx2_agc_enable = false; +    _last_calibration_freq = -AD9361_CAL_VALID_WINDOW; +    _rx_analog_bw = 0; +    _tx_analog_bw = 0; +    _rx_tia_lp_bw = 0; +    _tx_sec_lp_bw = 0; +    _rx_bb_lp_bw = 0; +    _tx_bb_lp_bw = 0;      /* Reset the device. */      _io_iface->poke8(0x000, 0x01); @@ -1501,7 +1605,7 @@ void ad9361_device_t::initialize()      /* Setup AuxADC */      _io_iface->poke8(0x00B, 0x00); // Temp Sensor Setup (Offset)      _io_iface->poke8(0x00C, 0x00); // Temp Sensor Setup (Temp Window) -    _io_iface->poke8(0x00D, 0x03); // Temp Sensor Setup (Periodic Measure) +    _io_iface->poke8(0x00D, 0x00); // Temp Sensor Setup (Manual  Measure)      _io_iface->poke8(0x00F, 0x04); // Temp Sensor Setup (Decimation)      _io_iface->poke8(0x01C, 0x10); // AuxADC Setup (Clock Div)      _io_iface->poke8(0x01D, 0x01); // AuxADC Setup (Decimation/Enable) @@ -1555,17 +1659,18 @@ void ad9361_device_t::initialize()      _program_mixer_gm_subtable();      _program_gain_table(); -    _setup_gain_control(); +    _setup_gain_control(false); -    _calibrate_baseband_rx_analog_filter(); -    _calibrate_baseband_tx_analog_filter(); -    _calibrate_rx_TIAs(); -    _calibrate_secondary_tx_filter(); +    set_bw_filter(RX, _baseband_bw); +    set_bw_filter(TX, _baseband_bw);      _setup_adc(); +    _calibrate_baseband_dc_offset(); +    _calibrate_rf_dc_offset();      _calibrate_tx_quadrature();      _calibrate_rx_quadrature(); +    _configure_bb_rf_dc_tracking(_use_dc_offset_correction);      // cals done, set PPORT config      switch (_client_params->get_digital_interface_mode()) { @@ -1678,18 +1783,19 @@ double ad9361_device_t::set_clock_rate(const double req_rate)      _program_mixer_gm_subtable();      _program_gain_table(); -    _setup_gain_control(); +    _setup_gain_control(false);      _reprogram_gains(); -    _calibrate_baseband_rx_analog_filter(); -    _calibrate_baseband_tx_analog_filter(); -    _calibrate_rx_TIAs(); -    _calibrate_secondary_tx_filter(); +    set_bw_filter(RX, _baseband_bw); +    set_bw_filter(TX, _baseband_bw);      _setup_adc(); +    _calibrate_baseband_dc_offset(); +    _calibrate_rf_dc_offset();      _calibrate_tx_quadrature();      _calibrate_rx_quadrature(); +    _configure_bb_rf_dc_tracking(_use_dc_offset_correction);      // cals done, set PPORT config      switch (_client_params->get_digital_interface_mode()) { @@ -1841,9 +1947,16 @@ double ad9361_device_t::tune(direction_t direction, const double value)      /* Update the gain settings. */      _reprogram_gains(); -    /* Run the calibration algorithms. */ -    _calibrate_tx_quadrature(); -    _calibrate_rx_quadrature(); +    /* Only run the following calibrations if we are more than 100MHz away +     * from the previous calibration point. */ +    if (std::abs(_last_calibration_freq - tune_freq) > AD9361_CAL_VALID_WINDOW) { +        /* Run the calibration algorithms. */ +        _calibrate_rf_dc_offset(); +        _calibrate_tx_quadrature(); +        _calibrate_rx_quadrature(); +        _configure_bb_rf_dc_tracking(_use_dc_offset_correction); +        _last_calibration_freq = tune_freq; +    }      /* If we were in the FDD state, return it now. */      if (not_in_alert) { @@ -1947,4 +2060,678 @@ double ad9361_device_t::get_rssi(chain_t chain)      return rssi;  } +/* + * Returns the reading of the internal temperature sensor. + * One point calibration of the sensor was done according to datasheet + * leading to the given default constant correction factor. + */ +double ad9361_device_t::_get_temperature(const double cal_offset, const double timeout) +{ +    //set 0x01D[0] to 1 to disable AuxADC GPIO reading +    boost::uint8_t tmp = 0; +    tmp = _io_iface->peek8(0x01D); +    _io_iface->poke8(0x01D, (tmp | 0x01)); +    _io_iface->poke8(0x00B, 0); //set offset to 0 + +    _io_iface->poke8(0x00C, 0x01); //start reading, clears bit 0x00C[1] +    boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time(); +    boost::posix_time::time_duration elapsed; +    //wait for valid data (toggle of bit 1 in 0x00C) +    while(((_io_iface->peek8(0x00C) >> 1) & 0x01) == 0) { +        boost::this_thread::sleep(boost::posix_time::microseconds(100)); +        elapsed = boost::posix_time::microsec_clock::local_time() - start_time; +        if(elapsed.total_milliseconds() > (timeout*1000)) +        { +            throw uhd::runtime_error("[ad9361_device_t] timeout while reading temperature"); +        } +    } +    _io_iface->poke8(0x00C, 0x00); //clear read flag + +    boost::uint8_t temp = _io_iface->peek8(0x00E); //read temperature. +    double tmp_temp = temp/1.140f; //according to ADI driver +    tmp_temp = tmp_temp + cal_offset; //Constant offset acquired by one point calibration. + +    return tmp_temp; +} + +double ad9361_device_t::get_average_temperature(const double cal_offset, const size_t num_samples) +{ +    double d_temp = 0; +    for(size_t i = 0; i < num_samples; i++) { +        double tmp_temp = _get_temperature(cal_offset); +        d_temp += (tmp_temp/num_samples); +    } +    return d_temp; +} + +void ad9361_device_t::set_dc_offset_auto(direction_t direction, const bool on) +{ +    if(direction == RX) +    { +         _use_dc_offset_correction = on; +         _configure_bb_rf_dc_tracking(_use_dc_offset_correction); +        if(on) +        { +            _io_iface->poke8(0x182, (_io_iface->peek8(0x182) & (~((1 << 7) | (1 << 6) | (1 << 3) | (1 << 2))))); //Clear force bits +            //Do a single shot DC offset cal before enabling tracking (Not possible if not in ALERT state. Is it necessary?) +        } else { +            //clear current config values +            _io_iface->poke8(0x182, (_io_iface->peek8(0x182) | ((1 << 7) | (1 << 6) | (1 << 3) | (1 << 2)))); //Set input A and input B&C force enable bits +            _io_iface->poke8(0x174, 0x00); +            _io_iface->poke8(0x175, 0x00); +            _io_iface->poke8(0x176, 0x00); +            _io_iface->poke8(0x177, 0x00); +            _io_iface->poke8(0x178, 0x00); +            _io_iface->poke8(0x17D, 0x00); +            _io_iface->poke8(0x17E, 0x00); +            _io_iface->poke8(0x17F, 0x00); +            _io_iface->poke8(0x180, 0x00); +            _io_iface->poke8(0x181, 0x00); +        } +    } else { +        // DC offset is removed during TX quad cal +        throw uhd::runtime_error("[ad9361_device_t] [set_iq_balance_auto] INVALID_CODE_PATH"); +    } +} + +void ad9361_device_t::set_iq_balance_auto(direction_t direction, const bool on) +{ +    if(direction == RX) +    { +        _use_iq_balance_correction = on; +        if(on) +        { +            //disable force registers and enable tracking +            _io_iface->poke8(0x182, (_io_iface->peek8(0x182) & (~ ( (1<<1) | (1<<0) | (1<<5) | (1<<4) )))); +            _calibrate_rx_quadrature(); +        } else { +            //disable IQ tracking +            _io_iface->poke8(0x169, 0xc0); +            //clear current config values +            _io_iface->poke8(0x182, (_io_iface->peek8(0x182) | ((1 << 1) | (1 << 0) | (1 << 5) | (1 << 4)))); //Set Rx2 input B&C force enable bit +            _io_iface->poke8(0x17B, 0x00); +            _io_iface->poke8(0x17C, 0x00); +            _io_iface->poke8(0x179, 0x00); +            _io_iface->poke8(0x17A, 0x00); +            _io_iface->poke8(0x170, 0x00); +            _io_iface->poke8(0x171, 0x00); +            _io_iface->poke8(0x172, 0x00); +            _io_iface->poke8(0x173, 0x00); +        } +    } else { +        throw uhd::runtime_error("[ad9361_device_t] [set_iq_balance_auto] INVALID_CODE_PATH"); +    } +} + +/* Sets the RX gain mode to be used. + * If a transition from an AGC to an non AGC mode occurs (or vice versa) + * the gain configuration will be reloaded. */ +void ad9361_device_t::_setup_agc(chain_t chain, gain_mode_t gain_mode) +{ +    boost::uint8_t gain_mode_reg = 0; +    boost::uint8_t gain_mode_prev = 0; +    boost::uint8_t gain_mode_bits_pos = 0; + +    gain_mode_reg = _io_iface->peek8(0x0FA); +    gain_mode_prev = (gain_mode_reg & 0x0F); + +    if (chain == CHAIN_1) { +        gain_mode_bits_pos = 0; +    } else if (chain == CHAIN_2) { +        gain_mode_bits_pos = 2; +    } else +    { +        throw uhd::runtime_error("[ad9361_device_t] Wrong value for chain"); +    } + +    gain_mode_reg = (gain_mode_reg & (~(0x03<<gain_mode_bits_pos))); //clear mode bits +    switch (gain_mode) { +        case GAIN_MODE_MANUAL: +            //leave bits cleared +            break; +        case GAIN_MODE_SLOW_AGC: +            gain_mode_reg = (gain_mode_reg | (0x02<<gain_mode_bits_pos)); +            break; +        case GAIN_MODE_FAST_AGC: +            gain_mode_reg = (gain_mode_reg | (0x01<<gain_mode_bits_pos)); +            break; +        default: +            throw uhd::runtime_error("[ad9361_device_t] Gain mode does not exist"); +    } +    _io_iface->poke8(0x0FA, gain_mode_reg); +    boost::uint8_t gain_mode_status = _io_iface->peek8(0x0FA); +    gain_mode_status = (gain_mode_status & 0x0F); +    /*Check if gain mode configuration needs to be reprogrammed*/ +    if (((gain_mode_prev == 0) && (gain_mode_status != 0)) || ((gain_mode_prev != 0) && (gain_mode_status == 0))) { +        if (gain_mode_status == 0) { +            /*load manual mode config*/ +            _setup_gain_control(false); +        } else { +            /*load agc mode config*/ +            _setup_gain_control(true); +        } +    } +} + +void ad9361_device_t::set_agc(chain_t chain, bool enable) +{ +    if(chain == CHAIN_1) { +        _rx1_agc_enable = enable; +        if(enable) { +            _setup_agc(chain, _rx1_agc_mode); +        } else { +            _setup_agc(chain, GAIN_MODE_MANUAL); +        } +    } else if (chain == CHAIN_2){ +        _rx2_agc_enable = enable; +        if(enable) { +            _setup_agc(chain, _rx2_agc_mode); +        } else { +            _setup_agc(chain, GAIN_MODE_MANUAL); +        } +    } else +    { +        throw uhd::runtime_error("[ad9361_device_t] Wrong value for chain"); +    } +} + +void ad9361_device_t::set_agc_mode(chain_t chain, gain_mode_t gain_mode) +{ +    if(chain == CHAIN_1) { +        _rx1_agc_mode = gain_mode; +        if(_rx1_agc_enable) { +            _setup_agc(chain, _rx1_agc_mode); +        } +    } else if(chain == CHAIN_2){ +        _rx2_agc_mode = gain_mode; +        if(_rx2_agc_enable) { +            _setup_agc(chain, _rx2_agc_mode); +        } +    } else +    { +        throw uhd::runtime_error("[ad9361_device_t] Wrong value for chain"); +    } +} + +std::vector<std::string> ad9361_device_t::get_filter_names(direction_t direction) +{ +    std::vector<std::string> ret; +    if(direction == RX) { +        for(std::map<std::string, filter_query_helper>::iterator it = _rx_filters.begin(); it != _rx_filters.end(); ++it) { +            ret.push_back(it->first); +        } +    } else if (direction == TX) +    { +        for(std::map<std::string, filter_query_helper>::iterator it = _tx_filters.begin(); it != _tx_filters.end(); ++it) { +            ret.push_back(it->first); +        } +    } +    return ret; +} + +filter_info_base::sptr ad9361_device_t::get_filter(direction_t direction, chain_t chain, const std::string &name) +{ +    if(direction == RX) { +        if (not _rx_filters[name].get) +        { +            throw uhd::runtime_error("ad9361_device_t::get_filter this filter can not be read."); +        } +        return _rx_filters[name].get(direction, chain); +    } else if (direction == TX) { +        if (not _tx_filters[name].get) +        { +            throw uhd::runtime_error("ad9361_device_t::get_filter this filter can not be read."); +        } +        return _tx_filters[name].get(direction, chain); +    } + +    throw uhd::runtime_error("ad9361_device_t::get_filter wrong direction parameter."); +} + +void ad9361_device_t::set_filter(direction_t direction, chain_t chain, const std::string &name, filter_info_base::sptr filter) +{ + +    if(direction == RX) { +        if(not _rx_filters[name].set) +        { +            throw uhd::runtime_error("ad9361_device_t::set_filter this filter can not be written."); +        } +        _rx_filters[name].set(direction, chain, filter); +    } else if (direction == TX) { +        if(not _tx_filters[name].set) +        { +            throw uhd::runtime_error("ad9361_device_t::set_filter this filter can not be written."); +        } +        _tx_filters[name].set(direction, chain, filter); +    } + +} + +double ad9361_device_t::set_bw_filter(direction_t direction, const double rf_bw) +{ +    //both low pass filters are programmed to the same bw. However, their cutoffs will differ. +    //Together they should create the requested bb bw. +    double set_analog_bb_bw = 0; +    if(direction == RX) +    { +        _rx_bb_lp_bw = _calibrate_baseband_rx_analog_filter(rf_bw); //returns bb bw +        _rx_tia_lp_bw = _calibrate_rx_TIAs(rf_bw); +        _rx_analog_bw = _rx_bb_lp_bw; +        set_analog_bb_bw = _rx_analog_bw; +    } else { +        _tx_bb_lp_bw = _calibrate_baseband_tx_analog_filter(rf_bw); //returns bb bw +        _tx_sec_lp_bw = _calibrate_secondary_tx_filter(rf_bw); +        _tx_analog_bw = _tx_bb_lp_bw; +        set_analog_bb_bw = _tx_analog_bw; +    } +    return (2.0 * set_analog_bb_bw); +} + +void ad9361_device_t::_set_fir_taps(direction_t direction, chain_t chain, const std::vector<boost::int16_t>& taps) +{ +    size_t num_taps = taps.size(); +    size_t num_taps_avail = _get_num_fir_taps(direction); +    if(num_taps == num_taps_avail) +    { +        boost::scoped_array<boost::uint16_t> coeffs(new boost::uint16_t[num_taps_avail]); +        for (size_t i = 0; i < num_taps_avail; i++) +        { +            coeffs[i] = boost::uint16_t(taps[i]); +        } +        _program_fir_filter(direction, chain, num_taps_avail, coeffs.get()); +    } else if(num_taps < num_taps_avail){ +        throw uhd::runtime_error("ad9361_device_t::_set_fir_taps not enough coefficients."); +    } else { +        throw uhd::runtime_error("ad9361_device_t::_set_fir_taps too many coefficients."); +    } +} + +size_t ad9361_device_t::_get_num_fir_taps(direction_t direction) +{ +    boost::uint8_t num = 0; +    if(direction == RX) +        num = _io_iface->peek8(0x0F5); +    else +        num = _io_iface->peek8(0x065); +    num = ((num >> 5) & 0x07); +    return ((num + 1) * 16); +} + +size_t ad9361_device_t::_get_fir_dec_int(direction_t direction) +{ +    boost::uint8_t dec_int = 0; +    if(direction == RX) +        dec_int = _io_iface->peek8(0x003); +    else +        dec_int = _io_iface->peek8(0x002); +    /* +     * 0 = dec/int by 1 and bypass filter +     * 1 = dec/int by 1 +     * 2 = dec/int by 2 +     * 3 = dec/int by 4 */ +    dec_int = (dec_int & 0x03); +    if(dec_int == 3) +    { +        return 4; +    } +    return dec_int; +} + +std::vector<boost::int16_t> ad9361_device_t::_get_fir_taps(direction_t direction, chain_t chain) +{ +    int base; +    size_t num_taps = _get_num_fir_taps(direction); +    boost::uint8_t config; +    boost::uint8_t reg_numtaps = (((num_taps / 16) - 1) & 0x07) << 5; +    config = reg_numtaps | 0x02; //start the programming clock + +    if(chain == CHAIN_1) +    { +        config = config | (1 << 3); +    } else if (chain == CHAIN_2){ +        config = config | (1 << 4); +    } else { +        throw uhd::runtime_error("[ad9361_device_t] Can not read both chains synchronously"); +    } + +    if(direction == RX) +    { +        base = 0xF0; +    } else { +        base = 0x60; +    } + +    _io_iface->poke8(base+5,config); + +    std::vector<boost::int16_t> taps; +    boost::uint8_t lower_val; +    boost::uint8_t higher_val; +    boost::uint16_t coeff; +    for(size_t i = 0;i < num_taps;i++) +    { +        _io_iface->poke8(base,0x00+i); +        lower_val = _io_iface->peek8(base+3); +        higher_val = _io_iface->peek8(base+4); +        coeff = ((higher_val << 8) | lower_val); +        taps.push_back(boost::int16_t(coeff)); +    } + +    config = (config & (~(1 << 1))); //disable filter clock +    _io_iface->poke8(base+5,config); +    return taps; +} + +/* + * Returns either RX TIA LPF or TX Secondary LPF + * depending on the direction. + * See UG570 for details on used scaling factors. */ +filter_info_base::sptr ad9361_device_t::_get_filter_lp_tia_sec(direction_t direction) +{ +    double cutoff = 0; + +    if(direction == RX) +    { +       cutoff = 2.5 * _rx_tia_lp_bw; +    } else { +       cutoff = 5 * _tx_sec_lp_bw; +    } + +    filter_info_base::sptr lp(new analog_filter_lp(filter_info_base::ANALOG_LOW_PASS, false, 0, "single-pole", cutoff, 20)); +    return  lp; +} + +/* + * Returns RX/TX BB LPF. + * See UG570 for details on used scaling factors. */ +filter_info_base::sptr ad9361_device_t::_get_filter_lp_bb(direction_t direction) +{ +    double cutoff = 0; +    if(direction == RX) +    { +        cutoff = 1.4 * _rx_bb_lp_bw; +    } else { +        cutoff = 1.6 * _tx_bb_lp_bw; +    } + +    filter_info_base::sptr bb_lp(new analog_filter_lp(filter_info_base::ANALOG_LOW_PASS, false, 1, "third-order Butterworth", cutoff, 60)); +    return  bb_lp; +} + +/* + * For RX direction the DEC3 is returned. + * For TX direction the INT3 is returned. */ +filter_info_base::sptr ad9361_device_t::_get_filter_dec_int_3(direction_t direction) +{ +    boost::uint8_t enable = 0; +    double rate = _adcclock_freq; +    double full_scale; +    size_t dec = 0; +    size_t interpol = 0; +    filter_info_base::filter_type type = filter_info_base::DIGITAL_I16; +    std::string name; +    boost::int16_t taps_array_rx[] = {55, 83, 0, -393, -580, 0, 1914, 4041, 5120, 4041, 1914, 0, -580, -393, 0, 83, 55}; +    boost::int16_t taps_array_tx[] = {36, -19, 0, -156, -12, 0, 479, 233, 0, -1215, -993, 0, 3569, 6277, 8192, 6277, 3569, 0, -993, -1215, 0, 223, 479, 0, -12, -156, 0, -19, 36}; +    std::vector<boost::int16_t> taps; + +    filter_info_base::sptr ret; + +    if(direction == RX) +    { +        full_scale = 16384; +        dec = 3; +        interpol = 1; + +        enable = _io_iface->peek8(0x003); +        enable = ((enable >> 4) & 0x03); +        taps.assign(taps_array_rx, taps_array_rx + sizeof(taps_array_rx) / sizeof(boost::int16_t) ); + +    } else { +        full_scale = 8192; +        dec = 1; +        interpol = 3; + +        boost::uint8_t use_dac_clk_div = _io_iface->peek8(0x00A); +        use_dac_clk_div = ((use_dac_clk_div >> 3) & 0x01); +        if(use_dac_clk_div == 1) +        { +            rate = rate / 2; +        } + +        enable = _io_iface->peek8(0x002); +        enable = ((enable >> 4) & 0x03); +        if(enable == 2) //0 => int. by 1, 1 => int. by 2 (HB3), 2 => int. by 3 +        { +            rate /= 3; +        } + +        taps.assign(taps_array_tx, taps_array_tx + sizeof(taps_array_tx) / sizeof(boost::int16_t) ); +    } + +    ret = filter_info_base::sptr(new digital_filter_base<boost::int16_t>(type, (enable != 2) ? true : false, 2, rate, interpol, dec, full_scale, taps.size(), taps)); +    return  ret; +} + +filter_info_base::sptr ad9361_device_t::_get_filter_hb_3(direction_t direction) +{ +    boost::uint8_t enable = 0; +    double rate = _adcclock_freq; +    double full_scale = 0; +    size_t dec = 1; +    size_t interpol = 1; +    filter_info_base::filter_type type = filter_info_base::DIGITAL_I16; +    boost::int16_t taps_array_rx[] = {1, 4, 6, 4, 1}; +    boost::int16_t taps_array_tx[] = {1, 2, 1}; +    std::vector<boost::int16_t> taps; + +    if(direction == RX) +    { +        full_scale = 16; +        dec = 2; + +        enable = _io_iface->peek8(0x003); +        enable = ((enable >> 4) & 0x03); +        taps.assign(taps_array_rx, taps_array_rx + sizeof(taps_array_rx) / sizeof(boost::int16_t) ); +    } else { +        full_scale = 2; +        interpol = 2; + +        boost::uint8_t use_dac_clk_div = _io_iface->peek8(0x00A); +        use_dac_clk_div = ((use_dac_clk_div >> 3) & 0x01); +        if(use_dac_clk_div == 1) +        { +            rate = rate / 2; +        } + +        enable = _io_iface->peek8(0x002); +        enable = ((enable >> 4) & 0x03); +        if(enable == 1) +        { +            rate /= 2; +        } +        taps.assign(taps_array_tx, taps_array_tx + sizeof(taps_array_tx) / sizeof(boost::int16_t) ); +    } + +    filter_info_base::sptr hb = filter_info_base::sptr(new digital_filter_base<boost::int16_t>(type, (enable != 1) ? true : false, 2, rate, interpol, dec, full_scale, taps.size(), taps)); +    return  hb; +} + +filter_info_base::sptr ad9361_device_t::_get_filter_hb_2(direction_t direction) +{ +    boost::uint8_t enable = 0; +    double rate = _adcclock_freq; +    double full_scale = 0; +    size_t dec = 1; +    size_t interpol = 1; +    filter_info_base::filter_type type = filter_info_base::DIGITAL_I16; +    boost::int16_t taps_array[] = {-9, 0, 73, 128, 73, 0, -9}; +    std::vector<boost::int16_t> taps(taps_array, taps_array + sizeof(taps_array) / sizeof(boost::int16_t) ); + +    digital_filter_base<boost::int16_t>::sptr hb_3 = boost::dynamic_pointer_cast<digital_filter_base<boost::int16_t> >(_get_filter_hb_3(direction)); +    digital_filter_base<boost::int16_t>::sptr dec_int_3 = boost::dynamic_pointer_cast<digital_filter_base<boost::int16_t> >(_get_filter_dec_int_3(direction)); + +    if(direction == RX) +    { +        full_scale = 256; +        dec = 2; +        enable = _io_iface->peek8(0x003); +    } else { +        full_scale = 128; +        interpol = 2; +        enable = _io_iface->peek8(0x002); +    } + +    enable = ((enable >> 3) & 0x01); + +    if(!(hb_3->is_bypassed())) +    { +        if(direction == RX) +        { +            rate = hb_3->get_output_rate(); +        }else if (direction == TX) { +            rate = hb_3->get_input_rate(); +            if(enable) +            { +                rate /= 2; +            } +        } +    } else { //else dec3/int3 or none of them is used. +        if(direction == RX) +        { +            rate = dec_int_3->get_output_rate(); +        }else if (direction == TX) { +            rate = dec_int_3->get_input_rate(); +            if(enable) +            { +                rate /= 2; +            } +        } +    } + +    filter_info_base::sptr hb(new digital_filter_base<boost::int16_t>(type, (enable == 0) ? true : false, 3, rate, interpol, dec, full_scale, taps.size(), taps)); +    return  hb; +} + +filter_info_base::sptr ad9361_device_t::_get_filter_hb_1(direction_t direction) +{ +    boost::uint8_t enable = 0; +    double rate = 0; +    double full_scale = 0; +    size_t dec = 1; +    size_t interpol = 1; +    filter_info_base::filter_type type = filter_info_base::DIGITAL_I16; + +    std::vector<boost::int16_t> taps; +    boost::int16_t taps_rx_array[] = {-8, 0, 42, 0, -147, 0, 619, 1013, 619, 0, -147, 0, 42, 0, -8}; +    boost::int16_t taps_tx_array[] = {-53, 0, 313, 0, -1155, 0, 4989, 8192, 4989, 0, -1155, 0, 313, 0, -53}; + +    digital_filter_base<boost::int16_t>::sptr hb_2 = boost::dynamic_pointer_cast<digital_filter_base<boost::int16_t> >(_get_filter_hb_2(direction)); + +    if(direction == RX) +    { +        full_scale = 2048; +        dec = 2; +        enable = _io_iface->peek8(0x003); +        enable = ((enable >> 2) & 0x01); +        rate = hb_2->get_output_rate(); +        taps.assign(taps_rx_array, taps_rx_array + sizeof(taps_rx_array) / sizeof(boost::int16_t) ); +    } else if (direction == TX) { +        full_scale = 8192; +        interpol = 2; +        enable = _io_iface->peek8(0x002); +        enable = ((enable >> 2) & 0x01); +        rate = hb_2->get_input_rate(); +        if(enable) +        { +            rate /= 2; +        } +        taps.assign(taps_tx_array, taps_tx_array + sizeof(taps_tx_array) / sizeof(boost::int16_t) ); +    } + +    filter_info_base::sptr hb(new digital_filter_base<boost::int16_t>(type, (enable == 0) ? true : false, 4, rate, interpol, dec, full_scale, taps.size(), taps)); +    return  hb; +} + +filter_info_base::sptr ad9361_device_t::_get_filter_fir(direction_t direction, chain_t chain) +{ +    double rate = 0; +    size_t dec = 1; +    size_t interpol = 1; +    size_t max_num_taps = 128; +    boost::uint8_t enable = 1; + +    digital_filter_base<boost::int16_t>::sptr hb_1 = boost::dynamic_pointer_cast<digital_filter_base<boost::int16_t> >(_get_filter_hb_1(direction)); + +    if(direction == RX) +    { +        dec = _get_fir_dec_int(direction); +        if(dec == 0) +        { +            enable = 0; +            dec = 1; +        } +        interpol = 1; +        rate = hb_1->get_output_rate(); +    }else if (direction == TX) { +        interpol = _get_fir_dec_int(direction); +        if(interpol == 0) +        { +            enable = 0; +            interpol = 1; +        } +        dec = 1; +        rate = hb_1->get_input_rate(); +        if(enable) +        { +            rate /= interpol; +        } +    } +    max_num_taps = _get_num_fir_taps(direction); + +    filter_info_base::sptr fir(new digital_filter_fir<boost::int16_t>(filter_info_base::DIGITAL_FIR_I16, (enable == 0) ? true : false, 5, rate, interpol, dec, 32767, max_num_taps, _get_fir_taps(direction, chain))); + +    return fir; +} + +void ad9361_device_t::_set_filter_fir(direction_t direction, chain_t channel, filter_info_base::sptr filter) +{ +    digital_filter_fir<boost::int16_t>::sptr fir = boost::dynamic_pointer_cast<digital_filter_fir<boost::int16_t> >(filter); +    //only write taps. Ignore everything else for now +    _set_fir_taps(direction, channel, fir->get_taps()); +} + +/* + * If BW of one of the analog filters gets overwritten manually, + * _tx_analog_bw and _rx_analog_bw are not valid any more! + * For useful data in those variables set_bw_filter method should be used + */ +void ad9361_device_t::_set_filter_lp_bb(direction_t direction, filter_info_base::sptr filter) +{ +    analog_filter_lp::sptr lpf = boost::dynamic_pointer_cast<analog_filter_lp>(filter); +    double bw = lpf->get_cutoff(); +    if(direction == RX) +    { +        //remember: this function takes rf bw as its input and calibrated to 1.4 x the given value +        _rx_bb_lp_bw = _calibrate_baseband_rx_analog_filter(2 * bw / 1.4); //returns bb bw + +    } else { +        //remember: this function takes rf bw as its input and calibrates to 1.6 x the given value +        _tx_bb_lp_bw = _calibrate_baseband_tx_analog_filter(2 * bw / 1.6); +    } +} + +void ad9361_device_t::_set_filter_lp_tia_sec(direction_t direction, filter_info_base::sptr filter) +{ +    analog_filter_lp::sptr lpf = boost::dynamic_pointer_cast<analog_filter_lp>(filter); +    double bw = lpf->get_cutoff(); +    if(direction == RX) +    { +        //remember: this function takes rf bw as its input and calibrated to 2.5 x the given value +        _rx_tia_lp_bw = _calibrate_rx_TIAs(2 * bw / 2.5); //returns bb bw + +    } else { +        //remember: this function takes rf bw as its input and calibrates to 5 x the given value +        _tx_sec_lp_bw = _calibrate_secondary_tx_filter(2 * bw / 5); +    } +} +  }} diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.h b/host/lib/usrp/common/ad9361_driver/ad9361_device.h index bd4ad368f..0c7a7e4f7 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_device.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_device.h @@ -8,6 +8,14 @@  #include <ad9361_client.h>  #include <boost/noncopyable.hpp>  #include <boost/thread/recursive_mutex.hpp> +#include <uhd/types/filters.hpp> +#include <uhd/types/sensors.hpp> +#include <complex> +#include <vector> +#include <map> +#include "boost/assign.hpp" +#include "boost/bind.hpp" +#include "boost/function.hpp"  namespace uhd { namespace usrp { @@ -15,10 +23,34 @@ class ad9361_device_t : public boost::noncopyable  {  public:      enum direction_t { RX, TX }; -    enum chain_t { CHAIN_1, CHAIN_2 }; +    enum gain_mode_t {GAIN_MODE_MANUAL, GAIN_MODE_SLOW_AGC, GAIN_MODE_FAST_AGC}; +    enum chain_t { CHAIN_1, CHAIN_2, CHAIN_BOTH };      ad9361_device_t(ad9361_params::sptr client, ad9361_io::sptr io_iface) : -        _client_params(client), _io_iface(io_iface) {} +        _client_params(client), _io_iface(io_iface) { + +        _rx_filters = boost::assign::map_list_of("LPF_TIA", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_lp_tia_sec, this, _1), +                                                    boost::bind(&ad9361_device_t::_set_filter_lp_tia_sec, this, _1, _3))) +                                            ("LPF_BB", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_lp_bb, this, _1), +                                                    boost::bind(&ad9361_device_t::_set_filter_lp_bb, this, _1, _3))) +                                            ("HB_3", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_3, this, _1), 0)) +                                            ("DEC_3", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_dec_int_3, this, _1), 0)) +                                            ("HB_2", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_2, this, _1), 0)) +                                            ("HB_1", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_1, this, _1), 0)) +                                            ("FIR_1", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_fir, this, _1, _2), +                                                    boost::bind(&ad9361_device_t::_set_filter_fir, this, _1, _2, _3))); + +        _tx_filters = boost::assign::map_list_of("LPF_SECONDARY", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_lp_tia_sec, this, _1), +                                                    boost::bind(&ad9361_device_t::_set_filter_lp_tia_sec, this, _1, _3))) +                                            ("LPF_BB", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_lp_bb, this, _1), +                                                    boost::bind(&ad9361_device_t::_set_filter_lp_bb, this, _1, _3))) +                                            ("HB_3", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_3, this, _1), 0)) +                                            ("INT_3", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_dec_int_3, this, _1), 0)) +                                            ("HB_2", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_2, this, _1), 0)) +                                            ("HB_1", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_1, this, _1), 0)) +                                            ("FIR_1", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_fir, this, _1, _2), +                                                    boost::bind(&ad9361_device_t::_set_filter_fir, this, _1, _2, _3))); +    }      /* Initialize the AD9361 codec. */      void initialize(); @@ -66,21 +98,56 @@ public:      /* Read back the internal RSSI measurement data. */      double get_rssi(chain_t chain); +    /*! Read the internal temperature sensor +     *\param calibrate return raw sensor readings or apply calibration factor. +     *\param num_samples number of measurements to average over +     */ +    double get_average_temperature(const double cal_offset = -30.0, const size_t num_samples = 3); + +    /* Turn on/off AD9361's RX DC offset correction */ +    void set_dc_offset_auto(direction_t direction, const bool on); + +    /* Turn on/off AD9361's RX IQ imbalance correction */ +    void set_iq_balance_auto(direction_t direction, const bool on); + +    /* Configure AD9361's AGC module to use either fast or slow AGC mode. */ +    void set_agc_mode(chain_t chain, gain_mode_t gain_mode); + +    /* Enable AD9361's AGC gain mode. */ +    void set_agc(chain_t chain, bool enable); + +    /* Set bandwidth of AD9361's analog LP filters. +     * Bandwidth should be RF bandwidth */ +    double set_bw_filter(direction_t direction, const double rf_bw); + +    /* +     * Filter API implementation +     * */ +    filter_info_base::sptr get_filter(direction_t direction, chain_t chain, const std::string &name); + +    void set_filter(direction_t direction, chain_t chain, const std::string &name, filter_info_base::sptr filter); + +    std::vector<std::string> get_filter_names(direction_t direction); +      //Constants      static const double AD9361_MAX_GAIN;      static const double AD9361_MAX_CLOCK_RATE; +    static const double AD9361_CAL_VALID_WINDOW;      static const double AD9361_RECOMMENDED_MAX_BANDWIDTH;  private:    //Methods      void _program_fir_filter(direction_t direction, int num_taps, boost::uint16_t *coeffs);      void _setup_tx_fir(size_t num_taps, boost::int32_t interpolation);      void _setup_rx_fir(size_t num_taps, boost::int32_t decimation); +    void _program_fir_filter(direction_t direction, chain_t chain, int num_taps, boost::uint16_t *coeffs); +    void _setup_tx_fir(size_t num_taps); +    void _setup_rx_fir(size_t num_taps);      void _calibrate_lock_bbpll();      void _calibrate_synth_charge_pumps(); -    double _calibrate_baseband_rx_analog_filter(); -    double _calibrate_baseband_tx_analog_filter(); -    void _calibrate_secondary_tx_filter(); -    void _calibrate_rx_TIAs(); +    double _calibrate_baseband_rx_analog_filter(double rfbw); +    double _calibrate_baseband_tx_analog_filter(double rfbw); +    double _calibrate_secondary_tx_filter(double rfbw); +    double _calibrate_rx_TIAs(double rfbw);      void _setup_adc();      void _calibrate_baseband_dc_offset();      void _calibrate_rf_dc_offset(); @@ -89,12 +156,29 @@ private:    //Methods      void _calibrate_tx_quadrature();      void _program_mixer_gm_subtable();      void _program_gain_table(); -    void _setup_gain_control(); +    void _setup_gain_control(bool use_agc);      void _setup_synth(direction_t direction, double vcorate);      double _tune_bbvco(const double rate);      void _reprogram_gains();      double _tune_helper(direction_t direction, const double value);      double _setup_rates(const double rate); +    double _get_temperature(const double cal_offset, const double timeout = 0.1); +    void _configure_bb_rf_dc_tracking(const bool on); +    void _setup_agc(chain_t chain, gain_mode_t gain_mode); +    void _set_fir_taps(direction_t direction, chain_t chain, const std::vector<boost::int16_t>& taps); +    std::vector<boost::int16_t> _get_fir_taps(direction_t direction, chain_t chain); +    size_t _get_num_fir_taps(direction_t direction); +    size_t _get_fir_dec_int(direction_t direction); +    filter_info_base::sptr _get_filter_lp_tia_sec(direction_t direction); +    filter_info_base::sptr _get_filter_lp_bb(direction_t direction); +    filter_info_base::sptr _get_filter_dec_int_3(direction_t direction); +    filter_info_base::sptr _get_filter_hb_3(direction_t direction); +    filter_info_base::sptr _get_filter_hb_2(direction_t direction); +    filter_info_base::sptr _get_filter_hb_1(direction_t direction); +    filter_info_base::sptr _get_filter_fir(direction_t direction, chain_t chain); +    void _set_filter_fir(direction_t direction, chain_t channel, filter_info_base::sptr filter); +    void _set_filter_lp_bb(direction_t direction, filter_info_base::sptr filter); +    void _set_filter_lp_tia_sec(direction_t direction, filter_info_base::sptr filter);  private:    //Members      typedef struct { @@ -107,22 +191,45 @@ private:    //Members          boost::uint8_t bbftune_mode;      } chip_regs_t; +    struct filter_query_helper +    { +        filter_query_helper( +                boost::function<filter_info_base::sptr (direction_t, chain_t)> p_get, +                boost::function<void (direction_t, chain_t, filter_info_base::sptr)> p_set +                ) : get(p_get), set(p_set) {  } + +        filter_query_helper(){ } + +        boost::function<filter_info_base::sptr (direction_t, chain_t)> get; +        boost::function<void (direction_t, chain_t, filter_info_base::sptr)> set; +    }; + +    std::map<std::string, filter_query_helper> _rx_filters; +    std::map<std::string, filter_query_helper> _tx_filters; +      //Interfaces      ad9361_params::sptr _client_params;      ad9361_io::sptr     _io_iface;      //Intermediate state      double              _rx_freq, _tx_freq, _req_rx_freq, _req_tx_freq; +    double              _last_calibration_freq;      double              _baseband_bw, _bbpll_freq, _adcclock_freq; +    double              _rx_analog_bw, _tx_analog_bw, _rx_bb_lp_bw, _tx_bb_lp_bw; +    double              _rx_tia_lp_bw, _tx_sec_lp_bw;      double              _req_clock_rate, _req_coreclk;      boost::uint16_t     _rx_bbf_tunediv;      boost::uint8_t      _curr_gain_table;      double              _rx1_gain, _rx2_gain, _tx1_gain, _tx2_gain;      boost::int32_t      _tfir_factor;      boost::int32_t      _rfir_factor; +    gain_mode_t         _rx1_agc_mode, _rx2_agc_mode; +    bool                _rx1_agc_enable, _rx2_agc_enable;      //Register soft-copies      chip_regs_t         _regs;      //Synchronization      boost::recursive_mutex  _mutex; +    bool _use_dc_offset_correction; +    bool _use_iq_balance_correction;  };  }}  //namespace diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h b/host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h index 786029d6e..553655fa5 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h @@ -7,91 +7,91 @@  #include <boost/cstdint.hpp> -boost::uint8_t gain_table_sub_1300mhz[77][5] = { {0,0x00,0x00,0x20,1}, -    {1,0x00,0x00,0x00,0}, {2,0x00,0x00,0x00,0}, {3,0x00,0x01,0x00,0}, -    {4,0x00,0x02,0x00,0}, {5,0x00,0x03,0x00,0}, {6,0x00,0x04,0x00,0}, -    {7,0x00,0x05,0x00,0}, {8,0x01,0x03,0x20,1}, {9,0x01,0x04,0x00,0}, -    {10,0x01,0x05,0x00,0}, {11,0x01,0x06,0x00,0}, {12,0x01,0x07,0x00,0}, -    {13,0x01,0x08,0x00,0}, {14,0x01,0x09,0x00,0}, {15,0x01,0x0A,0x00,0}, -    {16,0x01,0x0B,0x00,0}, {17,0x01,0x0C,0x00,0}, {18,0x01,0x0D,0x00,0}, -    {19,0x01,0x0E,0x00,0}, {20,0x02,0x09,0x20,1}, {21,0x02,0x0A,0x00,0}, -    {22,0x02,0x0B,0x00,0}, {23,0x02,0x0C,0x00,0}, {24,0x02,0x0D,0x00,0}, -    {25,0x02,0x0E,0x00,0}, {26,0x02,0x0F,0x00,0}, {27,0x02,0x10,0x00,0}, -    {28,0x02,0x2B,0x20,1}, {29,0x02,0x2C,0x00,0}, {30,0x04,0x27,0x20,1}, -    {31,0x04,0x28,0x00,0}, {32,0x04,0x29,0x00,0}, {33,0x04,0x2A,0x00,0}, -    {34,0x04,0x2B,0x00,1}, {35,0x24,0x21,0x20,0}, {36,0x24,0x22,0x00,1}, -    {37,0x44,0x20,0x20,0}, {38,0x44,0x21,0x00,0}, {39,0x44,0x22,0x00,0}, -    {40,0x44,0x23,0x00,0}, {41,0x44,0x24,0x00,0}, {42,0x44,0x25,0x00,0}, -    {43,0x44,0x26,0x00,0}, {44,0x44,0x27,0x00,0}, {45,0x44,0x28,0x00,0}, -    {46,0x44,0x29,0x00,0}, {47,0x44,0x2A,0x00,0}, {48,0x44,0x2B,0x00,0}, -    {49,0x44,0x2C,0x00,0}, {50,0x44,0x2D,0x00,0}, {51,0x44,0x2E,0x00,0}, -    {52,0x44,0x2F,0x00,0}, {53,0x44,0x30,0x00,0}, {54,0x44,0x31,0x00,0}, -    {55,0x64,0x2E,0x20,1}, {56,0x64,0x2F,0x00,0}, {57,0x64,0x30,0x00,0}, -    {58,0x64,0x31,0x00,0}, {59,0x64,0x32,0x00,0}, {60,0x64,0x33,0x00,0}, -    {61,0x64,0x34,0x00,0}, {62,0x64,0x35,0x00,0}, {63,0x64,0x36,0x00,0}, -    {64,0x64,0x37,0x00,0}, {65,0x64,0x38,0x00,0}, {66,0x65,0x38,0x20,1}, -    {67,0x66,0x38,0x20,1}, {68,0x67,0x38,0x20,1}, {69,0x68,0x38,0x20,1}, -    {70,0x69,0x38,0x20,1}, {71,0x6A,0x38,0x20,1}, {72,0x6B,0x38,0x20,1}, -    {73,0x6C,0x38,0x20,1}, {74,0x6D,0x38,0x20,1}, {75,0x6E,0x38,0x20,1}, -    {76,0x6F,0x38,0x20,1}}; +boost::uint8_t gain_table_sub_1300mhz[77][3] = { +{ 0x00, 0x00, 0x20 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x00 }, { 0x00, 0x02, 0x00 }, { 0x00, 0x03, 0x00 }, +{ 0x00, 0x04, 0x00 }, { 0x00, 0x05, 0x00 }, { 0x01, 0x03, 0x20 }, +{ 0x01, 0x04, 0x00 }, { 0x01, 0x05, 0x00 }, { 0x01, 0x06, 0x00 }, +{ 0x01, 0x07, 0x00 }, { 0x01, 0x08, 0x00 }, { 0x01, 0x09, 0x00 }, +{ 0x01, 0x0A, 0x00 }, { 0x01, 0x0B, 0x00 }, { 0x01, 0x0C, 0x00 }, +{ 0x01, 0x0D, 0x00 }, { 0x01, 0x0E, 0x00 }, { 0x02, 0x09, 0x20 }, +{ 0x02, 0x0A, 0x00 }, { 0x02, 0x0B, 0x00 }, { 0x02, 0x0C, 0x00 }, +{ 0x02, 0x0D, 0x00 }, { 0x02, 0x0E, 0x00 }, { 0x02, 0x0F, 0x00 }, +{ 0x02, 0x10, 0x00 }, { 0x02, 0x2B, 0x20 }, { 0x02, 0x2C, 0x00 }, +{ 0x04, 0x28, 0x20 }, { 0x04, 0x29, 0x00 }, { 0x04, 0x2A, 0x00 }, +{ 0x04, 0x2B, 0x00 }, { 0x24, 0x20, 0x20 }, { 0x24, 0x21, 0x00 }, +{ 0x44, 0x20, 0x20 }, { 0x44, 0x21, 0x00 }, { 0x44, 0x22, 0x00 }, +{ 0x44, 0x23, 0x00 }, { 0x44, 0x24, 0x00 }, { 0x44, 0x25, 0x00 }, +{ 0x44, 0x26, 0x00 }, { 0x44, 0x27, 0x00 }, { 0x44, 0x28, 0x00 }, +{ 0x44, 0x29, 0x00 }, { 0x44, 0x2A, 0x00 }, { 0x44, 0x2B, 0x00 }, +{ 0x44, 0x2C, 0x00 }, { 0x44, 0x2D, 0x00 }, { 0x44, 0x2E, 0x00 }, +{ 0x44, 0x2F, 0x00 }, { 0x44, 0x30, 0x00 }, { 0x44, 0x31, 0x00 }, +{ 0x44, 0x32, 0x00 }, { 0x64, 0x2E, 0x20 }, { 0x64, 0x2F, 0x00 }, +{ 0x64, 0x30, 0x00 }, { 0x64, 0x31, 0x00 }, { 0x64, 0x32, 0x00 }, +{ 0x64, 0x33, 0x00 }, { 0x64, 0x34, 0x00 }, { 0x64, 0x35, 0x00 }, +{ 0x64, 0x36, 0x00 }, { 0x64, 0x37, 0x00 }, { 0x64, 0x38, 0x00 }, +{ 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 }, +{ 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 }, +{ 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 }, +{ 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 } }; -boost::uint8_t gain_table_1300mhz_to_4000mhz[77][5] = {   {0,0x00,0x00,0x20,1}, -    {1,0x00,0x00,0x00,0}, {2,0x00,0x00,0x00,0}, {3,0x00,0x01,0x00,0}, -    {4,0x00,0x02,0x00,0}, {5,0x00,0x03,0x00,0}, {6,0x00,0x04,0x00,0}, -    {7,0x00,0x05,0x00,0}, {8,0x01,0x03,0x20,1}, {9,0x01,0x04,0x00,0}, -    {10,0x01,0x05,0x00,0}, {11,0x01,0x06,0x00,0}, {12,0x01,0x07,0x00,0}, -    {13,0x01,0x08,0x00,0}, {14,0x01,0x09,0x00,0}, {15,0x01,0x0A,0x00,0}, -    {16,0x01,0x0B,0x00,0}, {17,0x01,0x0C,0x00,0}, {18,0x01,0x0D,0x00,0}, -    {19,0x01,0x0E,0x00,0}, {20,0x02,0x09,0x20,1}, {21,0x02,0x0A,0x00,0}, -    {22,0x02,0x0B,0x00,0}, {23,0x02,0x0C,0x00,0}, {24,0x02,0x0D,0x00,0}, -    {25,0x02,0x0E,0x00,0}, {26,0x02,0x0F,0x00,0}, {27,0x02,0x10,0x00,0}, -    {28,0x02,0x2B,0x20,1}, {29,0x02,0x2C,0x00,0}, {30,0x04,0x28,0x20,1}, -    {31,0x04,0x29,0x00,0}, {32,0x04,0x2A,0x00,0}, {33,0x04,0x2B,0x00,0}, -    {34,0x24,0x20,0x20,0}, {35,0x24,0x21,0x00,1}, {36,0x44,0x20,0x20,0}, -    {37,0x44,0x21,0x00,1}, {38,0x44,0x22,0x00,0}, {39,0x44,0x23,0x00,0}, -    {40,0x44,0x24,0x00,0}, {41,0x44,0x25,0x00,0}, {42,0x44,0x26,0x00,0}, -    {43,0x44,0x27,0x00,0}, {44,0x44,0x28,0x00,0}, {45,0x44,0x29,0x00,0}, -    {46,0x44,0x2A,0x00,0}, {47,0x44,0x2B,0x00,0}, {48,0x44,0x2C,0x00,0}, -    {49,0x44,0x2D,0x00,0}, {50,0x44,0x2E,0x00,0}, {51,0x44,0x2F,0x00,0}, -    {52,0x44,0x30,0x00,0}, {53,0x44,0x31,0x00,0}, {54,0x44,0x32,0x00,0}, -    {55,0x64,0x2E,0x20,1}, {56,0x64,0x2F,0x00,0}, {57,0x64,0x30,0x00,0}, -    {58,0x64,0x31,0x00,0}, {59,0x64,0x32,0x00,0}, {60,0x64,0x33,0x00,0}, -    {61,0x64,0x34,0x00,0}, {62,0x64,0x35,0x00,0}, {63,0x64,0x36,0x00,0}, -    {64,0x64,0x37,0x00,0}, {65,0x64,0x38,0x00,0}, {66,0x65,0x38,0x20,1}, -    {67,0x66,0x38,0x20,1}, {68,0x67,0x38,0x20,1}, {69,0x68,0x38,0x20,1}, -    {70,0x69,0x38,0x20,1}, {71,0x6A,0x38,0x20,1}, {72,0x6B,0x38,0x20,1}, -    {73,0x6C,0x38,0x20,1}, {74,0x6D,0x38,0x20,1}, {75,0x6E,0x38,0x20,1}, -    {76,0x6F,0x38,0x20,1}}; +boost::uint8_t gain_table_1300mhz_to_4000mhz[77][3] = { +{ 0x00, 0x00, 0x20 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x00 }, { 0x00, 0x02, 0x00 }, { 0x00, 0x03, 0x00 }, +{ 0x00, 0x04, 0x00 }, { 0x00, 0x05, 0x00 }, { 0x01, 0x03, 0x20 }, +{ 0x01, 0x04, 0x00 }, { 0x01, 0x05, 0x00 }, { 0x01, 0x06, 0x00 }, +{ 0x01, 0x07, 0x00 }, { 0x01, 0x08, 0x00 }, { 0x01, 0x09, 0x00 }, +{ 0x01, 0x0A, 0x00 }, { 0x01, 0x0B, 0x00 }, { 0x01, 0x0C, 0x00 }, +{ 0x01, 0x0D, 0x00 }, { 0x01, 0x0E, 0x00 }, { 0x02, 0x09, 0x20 }, +{ 0x02, 0x0A, 0x00 }, { 0x02, 0x0B, 0x00 }, { 0x02, 0x0C, 0x00 }, +{ 0x02, 0x0D, 0x00 }, { 0x02, 0x0E, 0x00 }, { 0x02, 0x0F, 0x00 }, +{ 0x02, 0x10, 0x00 }, { 0x02, 0x2B, 0x20 }, { 0x02, 0x2C, 0x00 }, +{ 0x04, 0x27, 0x20 }, { 0x04, 0x28, 0x00 }, { 0x04, 0x29, 0x00 }, +{ 0x04, 0x2A, 0x00 }, { 0x04, 0x2B, 0x00 }, { 0x24, 0x21, 0x20 }, +{ 0x24, 0x22, 0x00 }, { 0x44, 0x20, 0x20 }, { 0x44, 0x21, 0x00 }, +{ 0x44, 0x22, 0x00 }, { 0x44, 0x23, 0x00 }, { 0x44, 0x24, 0x00 }, +{ 0x44, 0x25, 0x00 }, { 0x44, 0x26, 0x00 }, { 0x44, 0x27, 0x00 }, +{ 0x44, 0x28, 0x00 }, { 0x44, 0x29, 0x00 }, { 0x44, 0x2A, 0x00 }, +{ 0x44, 0x2B, 0x00 }, { 0x44, 0x2C, 0x00 }, { 0x44, 0x2D, 0x00 }, +{ 0x44, 0x2E, 0x00 }, { 0x44, 0x2F, 0x00 }, { 0x44, 0x30, 0x00 }, +{ 0x44, 0x31, 0x00 }, { 0x64, 0x2E, 0x20 }, { 0x64, 0x2F, 0x00 }, +{ 0x64, 0x30, 0x00 }, { 0x64, 0x31, 0x00 }, { 0x64, 0x32, 0x00 }, +{ 0x64, 0x33, 0x00 }, { 0x64, 0x34, 0x00 }, { 0x64, 0x35, 0x00 }, +{ 0x64, 0x36, 0x00 }, { 0x64, 0x37, 0x00 }, { 0x64, 0x38, 0x00 }, +{ 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 }, +{ 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 }, +{ 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 }, +{ 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 } }; -boost::uint8_t gain_table_4000mhz_to_6000mhz[77][5] = { {0,0x00,0x00,0x20,1}, -    {1,0x00,0x00,0x00,0}, {2,0x00,0x00,0x00,0}, {3,0x00,0x00,0x00,0}, -    {4,0x00,0x00,0x00,0}, {5,0x00,0x01,0x00,0}, {6,0x00,0x02,0x00,0}, -    {7,0x00,0x03,0x00,0}, {8,0x01,0x01,0x20,1}, {9,0x01,0x02,0x00,0}, -    {10,0x01,0x03,0x00,0}, {11,0x01,0x04,0x20,1}, {12,0x01,0x05,0x00,0}, -    {13,0x01,0x06,0x00,0}, {14,0x01,0x07,0x00,0}, {15,0x01,0x08,0x00,0}, -    {16,0x01,0x09,0x00,0}, {17,0x01,0x0A,0x00,0}, {18,0x01,0x0B,0x00,0}, -    {19,0x01,0x0C,0x00,0}, {20,0x02,0x08,0x20,1}, {21,0x02,0x09,0x00,0}, -    {22,0x02,0x0A,0x00,0}, {23,0x02,0x0B,0x20,1}, {24,0x02,0x0C,0x00,0}, -    {25,0x02,0x0D,0x00,0}, {26,0x02,0x0E,0x00,0}, {27,0x02,0x0F,0x00,0}, -    {28,0x02,0x2A,0x20,1}, {29,0x02,0x2B,0x00,0}, {30,0x04,0x27,0x20,1}, -    {31,0x04,0x28,0x00,0}, {32,0x04,0x29,0x00,0}, {33,0x04,0x2A,0x00,0}, -    {34,0x04,0x2B,0x00,0}, {35,0x04,0x2C,0x00,0}, {36,0x04,0x2D,0x00,0}, -    {37,0x24,0x20,0x20,1}, {38,0x24,0x21,0x00,0}, {39,0x24,0x22,0x00,0}, -    {40,0x44,0x20,0x20,1}, {41,0x44,0x21,0x00,0}, {42,0x44,0x22,0x00,0}, -    {43,0x44,0x23,0x00,0}, {44,0x44,0x24,0x00,0}, {45,0x44,0x25,0x00,0}, -    {46,0x44,0x26,0x00,0}, {47,0x44,0x27,0x00,0}, {48,0x44,0x28,0x00,0}, -    {49,0x44,0x29,0x00,0}, {50,0x44,0x2A,0x00,0}, {51,0x44,0x2B,0x00,0}, -    {52,0x44,0x2C,0x00,0}, {53,0x44,0x2D,0x00,0}, {54,0x44,0x2E,0x00,0}, -    {55,0x64,0x2E,0x20,1}, {56,0x64,0x2F,0x00,0}, {57,0x64,0x30,0x00,0}, -    {58,0x64,0x31,0x00,0}, {59,0x64,0x32,0x00,0}, {60,0x64,0x33,0x00,0}, -    {61,0x64,0x34,0x00,0}, {62,0x64,0x35,0x00,0}, {63,0x64,0x36,0x00,0}, -    {64,0x64,0x37,0x00,0}, {65,0x64,0x38,0x00,0}, {66,0x65,0x38,0x20,1}, -    {67,0x66,0x38,0x20,1}, {68,0x67,0x38,0x20,1}, {69,0x68,0x38,0x20,1}, -    {70,0x69,0x38,0x20,1}, {71,0x6A,0x38,0x20,1}, {72,0x6B,0x38,0x20,1}, -    {73,0x6C,0x38,0x20,1}, {74,0x6D,0x38,0x20,1}, {75,0x6E,0x38,0x20,1}, -    {76,0x6F,0x38,0x20,1}}; +boost::uint8_t gain_table_4000mhz_to_6000mhz[77][3] = { +{ 0x00, 0x00, 0x20 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, +{ 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x01, 0x00 }, +{ 0x00, 0x02, 0x00 }, { 0x00, 0x03, 0x00 }, { 0x01, 0x01, 0x20 }, +{ 0x01, 0x02, 0x00 }, { 0x01, 0x03, 0x00 }, { 0x01, 0x04, 0x20 }, +{ 0x01, 0x05, 0x00 }, { 0x01, 0x06, 0x00 }, { 0x01, 0x07, 0x00 }, +{ 0x01, 0x08, 0x00 }, { 0x01, 0x09, 0x00 }, { 0x01, 0x0A, 0x00 }, +{ 0x01, 0x0B, 0x00 }, { 0x01, 0x0C, 0x00 }, { 0x02, 0x08, 0x20 }, +{ 0x02, 0x09, 0x00 }, { 0x02, 0x0A, 0x00 }, { 0x02, 0x0B, 0x20 }, +{ 0x02, 0x0C, 0x00 }, { 0x02, 0x0D, 0x00 }, { 0x02, 0x0E, 0x00 }, +{ 0x02, 0x0F, 0x00 }, { 0x02, 0x2A, 0x20 }, { 0x02, 0x2B, 0x00 }, +{ 0x04, 0x27, 0x20 }, { 0x04, 0x28, 0x00 }, { 0x04, 0x29, 0x00 }, +{ 0x04, 0x2A, 0x00 }, { 0x04, 0x2B, 0x00 }, { 0x04, 0x2C, 0x00 }, +{ 0x04, 0x2D, 0x00 }, { 0x24, 0x20, 0x20 }, { 0x24, 0x21, 0x00 }, +{ 0x24, 0x22, 0x00 }, { 0x44, 0x20, 0x20 }, { 0x44, 0x21, 0x00 }, +{ 0x44, 0x22, 0x00 }, { 0x44, 0x23, 0x00 }, { 0x44, 0x24, 0x00 }, +{ 0x44, 0x25, 0x00 }, { 0x44, 0x26, 0x00 }, { 0x44, 0x27, 0x00 }, +{ 0x44, 0x28, 0x00 }, { 0x44, 0x29, 0x00 }, { 0x44, 0x2A, 0x00 }, +{ 0x44, 0x2B, 0x00 }, { 0x44, 0x2C, 0x00 }, { 0x44, 0x2D, 0x00 }, +{ 0x44, 0x2E, 0x00 }, { 0x64, 0x2E, 0x20 }, { 0x64, 0x2F, 0x00 }, +{ 0x64, 0x30, 0x00 }, { 0x64, 0x31, 0x00 }, { 0x64, 0x32, 0x00 }, +{ 0x64, 0x33, 0x00 }, { 0x64, 0x34, 0x00 }, { 0x64, 0x35, 0x00 }, +{ 0x64, 0x36, 0x00 }, { 0x64, 0x37, 0x00 }, { 0x64, 0x38, 0x00 }, +{ 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 }, +{ 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 }, +{ 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 }, +{ 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 } };  #endif /* INCLUDED_AD9361_GAIN_TABLES_HPP */ diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h b/host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h index cb320e1f4..0475a5eb1 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h @@ -6,20 +6,20 @@  #define INCLUDED_AD9361_SYNTH_LUT_HPP -double vco_index[53] = {12605000000, 12245000000, 11906000000, 11588000000, -                        11288000000, 11007000000, 10742000000, 10492000000, -                        10258000000, 10036000000, 9827800000, 9631100000, -                        9445300000, 9269800000, 9103600000, 8946300000, -                        8797000000, 8655300000, 8520600000, 8392300000, -                        8269900000, 8153100000, 8041400000, 7934400000, -                        7831800000, 7733200000, 7638400000, 7547100000, -                        7459000000, 7374000000, 7291900000, 7212400000, -                        7135500000, 7061000000, 6988700000, 6918600000, -                        6850600000, 6784600000, 6720500000, 6658200000, -                        6597800000, 6539200000, 6482300000, 6427000000, -                        6373400000, 6321400000, 6270900000, 6222000000, -                        6174500000, 6128400000, 6083600000, 6040100000, -                        5997700000}; +double vco_index[53] = {12605000000.0, 12245000000.0, 11906000000.0, 11588000000.0, +                        11288000000.0, 11007000000.0, 10742000000.0, 10492000000.0, +                        10258000000.0, 10036000000.0, 9827800000.0, 9631100000.0, +                        9445300000.0, 9269800000.0, 9103600000.0, 8946300000.0, +                        8797000000.0, 8655300000.0, 8520600000.0, 8392300000.0, +                        8269900000.0, 8153100000.0, 8041400000.0, 7934400000.0, +                        7831800000.0, 7733200000.0, 7638400000.0, 7547100000.0, +                        7459000000.0, 7374000000.0, 7291900000.0, 7212400000.0, +                        7135500000.0, 7061000000.0, 6988700000.0, 6918600000.0, +                        6850600000.0, 6784600000.0, 6720500000.0, 6658200000.0, +                        6597800000.0, 6539200000.0, 6482300000.0, 6427000000.0, +                        6373400000.0, 6321400000.0, 6270900000.0, 6222000000.0, +                        6174500000.0, 6128400000.0, 6083600000.0, 6040100000.0, +                        5997700000.0};  int synth_cal_lut[53][12] = {   {10, 0, 4, 0, 15, 8, 8, 13, 4, 13, 15, 9},                                  {10, 0, 4, 0, 15, 8, 9, 13, 4, 13, 15, 9},  | 
