aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/common/ad9361_driver
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/usrp/common/ad9361_driver')
-rw-r--r--host/lib/usrp/common/ad9361_driver/ad9361_device.cpp981
-rw-r--r--host/lib/usrp/common/ad9361_driver/ad9361_device.h121
-rw-r--r--host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h162
-rw-r--r--host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h28
4 files changed, 1093 insertions, 199 deletions
diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp
index e63460730..85e81cf97 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()) {
@@ -1680,18 +1785,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()) {
@@ -1843,9 +1949,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) {
@@ -1960,4 +2073,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 71ce78da7..1c5c97829 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();
@@ -69,21 +101,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();
@@ -92,12 +159,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 {
@@ -110,11 +194,30 @@ 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 _rx_analog_bw, _tx_analog_bw, _rx_bb_lp_bw, _tx_bb_lp_bw;
+ double _rx_tia_lp_bw, _tx_sec_lp_bw;
//! Current baseband sampling rate (this is the actual rate the device is
// is running at)
double _baseband_bw;
@@ -129,10 +232,14 @@ private: //Members
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},