// // Copyright 2008,2009 Free Software Foundation, Inc. // // This file is part of GNU Radio // // GNU Radio is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either asversion 3, or (at your option) // any later version. // // GNU Radio is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with GNU Radio; see the file COPYING. If not, write to // the Free Software Foundation, Inc., 51 Franklin Street, // Boston, MA 02110-1301, USA. #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include /*****************************************************************************/ db_dbs_rx::db_dbs_rx(usrp_basic_sptr _usrp, int which) : db_base(_usrp, which) { // Control DBS receiver based USRP daughterboard. // // @param usrp: instance of usrp.source_c // @param which: which side: 0, 1 corresponding to RX_A or RX_B respectively usrp()->_write_oe(d_which, 0x0001, 0x0001); if(which == 0) { d_i2c_addr = 0x67; } else { d_i2c_addr = 0x65; } d_n = 950; d_div2 = 0; d_osc = 5; d_cp = 3; d_r = 4; d_r_int = 1; d_fdac = 127; d_m = 2; d_dl = 0; d_ade = 0; d_adl = 0; d_gc1 = 0; d_gc2 = 31; d_diag = 0; _enable_refclk(true); set_gain((gain_min() + gain_max()) / 2.0); // initialize gain bypass_adc_buffers(true); } db_dbs_rx::~db_dbs_rx() { shutdown(); } void db_dbs_rx::shutdown() { if (!d_is_shutdown){ d_is_shutdown = true; // do whatever there is to do to shutdown orderly _enable_refclk(false); } } void db_dbs_rx::_write_reg (int regno, int v) { //regno is in [0,5], v is value to write to register""" assert (0 <= regno && regno <= 5); std::vector args(2); args[0] = regno; args[1] = v; usrp()->write_i2c (d_i2c_addr, int_seq_to_str (args)); } void db_dbs_rx::_write_regs (int starting_regno, const std::vector &vals) { // starting_regno is in [0,5], // vals is a seq of integers to write to consecutive registers""" //FIXME std::vector args; args.push_back(starting_regno); args.insert(args.end(), vals.begin(), vals.end()); usrp()->write_i2c (d_i2c_addr, int_seq_to_str (args)); } std::vector db_dbs_rx::_read_status () { //If successful, return list of two ints: [status_info, filter_DAC]""" std::string s = usrp()->read_i2c (d_i2c_addr, 2); if(s.size() != 2) { std::vector ret(0); return ret; } return str_to_int_seq (s); } void db_dbs_rx::_send_reg(int regno) { assert(0 <= regno && regno <= 5); if(regno == 0) _write_reg(0,(d_div2<<7) + (d_n>>8)); if(regno == 1) _write_reg(1,d_n & 255); if(regno == 2) _write_reg(2,d_osc + (d_cp<<3) + (d_r_int<<5)); if(regno == 3) _write_reg(3,d_fdac); if(regno == 4) _write_reg(4,d_m + (d_dl<<5) + (d_ade<<6) + (d_adl<<7)); if(regno == 5) _write_reg(5,d_gc2 + (d_diag<<5)); } // BW setting void db_dbs_rx::_set_m(int m) { assert(m>0 && m<32); d_m = m; _send_reg(4); } void db_dbs_rx::_set_fdac(int fdac) { assert(fdac>=0 && fdac<128); d_fdac = fdac; _send_reg(3); } bool db_dbs_rx::set_bw (float bw) { if (bw < 1e6 || bw > 33e6) { fprintf(stderr, "db_dbs_rx::set_bw: bw (=%f) must be between 1e6 and 33e6 inclusive\n", bw); return false; } // struct bw_t ret = {0, 0, 0}; int m_max, m_min, m_test, fdac_test; if(bw >= 4e6) m_max = int(std::min(31, (int)floor(_refclk_freq()/1e6))); else if(bw >= 2e6) // Outside of Specs! m_max = int(std::min(31, (int)floor(_refclk_freq()/.5e6))); else // Way outside of Specs! m_max = int(std::min(31, (int)floor(_refclk_freq()/.25e6))); m_min = int(ceil(_refclk_freq()/2.5e6)); m_test = m_max; while(m_test >= m_min) { fdac_test = static_cast(round(((bw * m_test / _refclk_freq())-4)/.145)); if(fdac_test > 127) m_test = m_test - 1; else break; } if(m_test>=m_min && fdac_test>=0) { _set_m(m_test); _set_fdac(fdac_test); //ret.m = d_m; //ret.fdac = d_fdac; //ret.div = _refclk_freq()/d_m*(4+0.145*d_fdac); } else { fprintf(stderr, "db_dbs_rx::set_bw: failed\n"); return false; } return true; } // Gain setting void db_dbs_rx::_set_dl(int dl) { assert(dl == 0 || dl == 1); d_dl = dl; _send_reg(4); } void db_dbs_rx::_set_gc2(int gc2) { assert(gc2<32 && gc2>=0); d_gc2 = gc2; _send_reg(5); } void db_dbs_rx::_set_gc1(int gc1) { assert(gc1>=0 && gc1<4096); d_gc1 = gc1; usrp()->write_aux_dac(d_which, 0, gc1); } void db_dbs_rx::_set_pga(int pga_gain) { assert(pga_gain>=0 && pga_gain<=20); if(d_which == 0) { usrp()->set_pga (0, pga_gain); usrp()->set_pga (1, pga_gain); } else { usrp()->set_pga (2, pga_gain); usrp()->set_pga (3, pga_gain); } } float db_dbs_rx::gain_min() { return 0; } float db_dbs_rx::gain_max() { return 104; } float db_dbs_rx::gain_db_per_step() { return 1; } bool db_dbs_rx::set_gain(float gain) { // Set the gain. // // @param gain: gain in decibels // @returns True/False if(!(gain>=0 && gain<105)) { throw std::runtime_error("gain out of range\n"); } int gc1=0, gc2=0, dl=0, pga=0; if(gain < 56) { gc1 = int((-gain*1.85/56.0 + 2.6)*4096.0/3.3); gain = 0; } else { gc1 = 0; gain -= 56; } if(gain < 24) { gc2 = static_cast(round(31.0 * (1-gain/24.0))); gain = 0; } else { gc2 = 0; gain -=24; } if(gain >= 4.58) { dl = 1; gain -= 4.58; } pga = gain; _set_gc1(gc1); _set_gc2(gc2); _set_dl(dl); _set_pga(pga); return true; } // Frequency setting void db_dbs_rx::_set_osc(int osc) { assert(osc>=0 && osc<8); d_osc = osc; _send_reg(2); } void db_dbs_rx::_set_cp(int cp) { assert(cp>=0 && cp<4); d_cp = cp; _send_reg(2); } void db_dbs_rx::_set_n(int n) { assert(n>256 && n<32768); d_n = n; _send_reg(0); _send_reg(1); } void db_dbs_rx::_set_div2(int div2) { assert(div2 == 0 || div2 == 1); d_div2 = div2; _send_reg(0); } void db_dbs_rx::_set_r(int r) { assert(r>=0 && r<128); d_r = r; d_r_int = static_cast(round(log10(r)/log10(2)) - 1); _send_reg(2); } // FIXME How do we handle ADE and ADL properly? void db_dbs_rx::_set_ade(int ade) { assert(ade == 0 || ade == 1); d_ade = ade; _send_reg(4); } double db_dbs_rx::freq_min() { return 500e6; } double db_dbs_rx::freq_max() { return 2.6e9; } struct freq_result_t db_dbs_rx::set_freq(double freq) { // Set the frequency. // // @param freq: target RF frequency in Hz // @type freq: double // // @returns (ok, actual_baseband_freq) where: // ok is True or False and indicates success or failure, // actual_baseband_freq is RF frequency that corresponds to DC in the IF. freq_result_t args = {false, 0}; if(!(freq>=freq_min() && freq<=freq_max())) { return args; } double vcofreq; if(freq<1150e6) { _set_div2(0); vcofreq = 4 * freq; } else { _set_div2(1); vcofreq = 2 * freq; } _set_ade(1); int rmin = std::max(2, (int)(_refclk_freq()/2e6)); int rmax = std::min(128, (int)(_refclk_freq()/500e3)); int r = 2; int n = 0; int best_r = 2; int best_n = 0; int best_delta = 10e6; int delta; while(r <= rmax) { n = static_cast(round(freq/(_refclk_freq()/r))); if(r(round(best_n))); int vco; if(vcofreq < 2433e6) vco = 0; else if(vcofreq < 2711e6) vco=1; else if(vcofreq < 3025e6) vco=2; else if(vcofreq < 3341e6) vco=3; else if(vcofreq < 3727e6) vco=4; else if(vcofreq < 4143e6) vco=5; else if(vcofreq < 4493e6) vco=6; else vco=7; _set_osc(vco); // Set CP current int adc_val = 0; std::vector bytes(2); while(adc_val == 0 || adc_val == 7) { bytes = _read_status(); adc_val = bytes[0] >> 2; if(adc_val == 0) { if(vco <= 0) { return args; } else { vco = vco - 1; } } else if(adc_val == 7) { if(vco >= 7) { return args; } else { vco = vco + 1; } } _set_osc(vco); } if(adc_val == 1 || adc_val == 2) { _set_cp(1); } else if(adc_val == 3 || adc_val == 4) { _set_cp(2); } else { _set_cp(3); } args.ok = true; args.baseband_freq = d_n * _refclk_freq() / d_r; return args; } int db_dbs_rx::_refclk_divisor() { //Return value to stick in REFCLK_DIVISOR register return 16; } bool db_dbs_rx::is_quadrature() { // Return True if this board requires both I & Q analog channels. return true; }