diff options
| author | Nick Foster <nick@nerdnetworks.org> | 2010-09-27 19:19:55 -0700 | 
|---|---|---|
| committer | Nick Foster <nick@nerdnetworks.org> | 2010-09-27 19:19:55 -0700 | 
| commit | a6f60ab4911fa756656a62ebe3a1093d52836a6e (patch) | |
| tree | 202dbee350fa51d11242e8af158e2e57dcda779d /host/lib | |
| parent | ba658f4d2cac9fb0f9d22060da989c9078cc4d1e (diff) | |
| download | uhd-a6f60ab4911fa756656a62ebe3a1093d52836a6e.tar.gz uhd-a6f60ab4911fa756656a62ebe3a1093d52836a6e.tar.bz2 uhd-a6f60ab4911fa756656a62ebe3a1093d52836a6e.zip | |
TVRX: not done yet but getting there. gain linearization framework in place.
Diffstat (limited to 'host/lib')
| -rw-r--r-- | host/lib/ic_reg_maps/CMakeLists.txt | 4 | ||||
| -rw-r--r--[-rwxr-xr-x] | host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py (renamed from host/lib/ic_reg_maps/gen_dtt75403_regs.py) | 30 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_tvrx.cpp | 218 | 
3 files changed, 160 insertions, 92 deletions
| diff --git a/host/lib/ic_reg_maps/CMakeLists.txt b/host/lib/ic_reg_maps/CMakeLists.txt index b396db4ca..25f34a280 100644 --- a/host/lib/ic_reg_maps/CMakeLists.txt +++ b/host/lib/ic_reg_maps/CMakeLists.txt @@ -70,6 +70,6 @@ LIBUHD_PYTHON_GEN_SOURCE(  )  LIBUHD_PYTHON_GEN_SOURCE( -    ${CMAKE_SOURCE_DIR}/lib/ic_reg_maps/gen_dtt75403_regs.py -    ${CMAKE_BINARY_DIR}/lib/ic_reg_maps/dtt75403_regs.py +    ${CMAKE_SOURCE_DIR}/lib/ic_reg_maps/gen_tuner_4937di5_regs.py +    ${CMAKE_BINARY_DIR}/lib/ic_reg_maps/tuner_4937di5_regs.hpp  ) diff --git a/host/lib/ic_reg_maps/gen_dtt75403_regs.py b/host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py index fe64bd92a..73f7aa3db 100755..100644 --- a/host/lib/ic_reg_maps/gen_dtt75403_regs.py +++ b/host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py @@ -24,36 +24,26 @@ REGS_TMPL="""\  ########################################################################  ## Note: offsets given from perspective of data bits (excludes address)  ######################################################################## -## -######################################################################## -## Address byte -######################################################################## -adb                   0[1:2]        0 -########################################################################  ## Divider byte 1  ######################################################################## -db1                   1[0:6]        0x00 +db1                   0[0:6]        0x00  ########################################################################  ## Divider byte 2  ######################################################################## -db2                   2[0:7]        0x00 +db2                   1[0:7]        0x00  ########################################################################  ## Control byte 1  ######################################################################## -atp                   3[3:5]        0       112, 109, 106, 103, 100, 94, 94, disable -refdiv                3[0:2]        0       166.667khz, 142.857khz, 80khz, 62.5khz, 31.25khz, 50khz -######################################################################## -## Band switch byte -######################################################################## -cpsel                 4[6:7]        0 -filterbw              4[4]          0       8mhz, 7mhz -bandsel               4[0:3]        0 +cb7                   2[7]          0x01 +cp                    2[6]          0x00     low,high +os                    2[0]          0x00     on,off +rs                    2[1:2]        0x00     d512=3,d640=0,d1024=1 +test                  2[3:5]        0x01     normal=0x01,cpoff=0x02,cpsink=0x06,cpsrc=0x07,cptest1=0x04,cptest2=0x05  ########################################################################  ## Control byte 2  ######################################################################## -atc                   5[5]          0       low, high -stby                  5[4]          0       standby, on -xto                   5[0]          0       disable, enable +bandsel               3[4:7]        0x03     uhf=0x03,vhfhi=0x09,vhflo=0x0a +power                 3[3]          0x00     on,off  """  ######################################################################## @@ -78,7 +68,7 @@ boost::uint8_t get_reg(boost::uint8_t addr){  if __name__ == '__main__':      import common; common.generate( -        name='dtt75403_regs', +        name='tuner_4937di5_regs',          regs_tmpl=REGS_TMPL,          body_tmpl=BODY_TMPL,          file=__file__, diff --git a/host/lib/usrp/dboard/db_tvrx.cpp b/host/lib/usrp/dboard/db_tvrx.cpp index 5582dbe33..ed6025433 100644 --- a/host/lib/usrp/dboard/db_tvrx.cpp +++ b/host/lib/usrp/dboard/db_tvrx.cpp @@ -42,6 +42,7 @@  #include <boost/math/special_functions/round.hpp>  #include <utility>  #include <cmath> +#include <tuner_4937di5_regs.hpp>  using namespace uhd;  using namespace uhd::usrp; @@ -57,19 +58,73 @@ static const freq_range_t tvrx_freq_range(50e6, 860e6);  static const prop_names_t tvrx_antennas = ""; //only got one  static const uhd::dict<std::string, gain_range_t> tvrx_gain_ranges = map_list_of -    ("RF", gain_range_t(0, 60, float(60.0/4096))) //both gains are analog and controlled by DAC -    ("IF", gain_range_t(0, 35, float(35.0/4096))) //the old driver used 1dB for both; i don't think that's right? +    ("RF", gain_range_t(-13.3, 50.3, float(50.3/4096))) //both gains are analog and controlled by DAC +    ("IF", gain_range_t(-1.5, 32.5, float(32.5/4096))) //the old driver used 1dB for both; i don't think that's right?  ;  static const uhd::dict<std::string, freq_range_t> tvrx_freq_ranges = map_list_of -    ("VHFLO", freq_range_t(50e6, 158e6)) //this isn't used outside this driver. just for us. +    ("VHFLO", freq_range_t(50e6, 158e6))      ("VHFHI", freq_range_t(158e6, 454e6))      ("UHF"  , freq_range_t(454e6, 860e6))  ; -//we might need to spec the various bands for band selection. +//gain linearization data +//this is from the datasheet and is dB vs. volts (below) +//i tried to curve fit this, but it's really just so nonlinear that you'd +//need dang near as many coefficients as to just map it like this and interp. +//these numbers are culled from the 4937DI5 datasheet and are probably totally inaccurate +//but if it's better than the old linear fit i'm happy +static const uhd::dict<std::string, std::vector<float> > tvrx_rf_gains_db = map_list_of +    ("VHFLO", std::vector<float>(-6.00000, -6.00000, -6.00000, -4.00000, 0.00000, +                                 5.00000, 10.00000, 17.40000, 26.30000, 36.00000, +                                 43.00000, 48.00000, 49.50000, 50.10000, 50.30000, +                                 50.30000, 50.30000)) +    ("VHFHI", std::vector<float>(-13.3000,  -13.3000,  -13.3000,   -1.0000,    7.7000, +                                 11.0000,   14.7000,   19.3000,   26.1000,   36.0000, +                                 42.7000,   46.0000,   47.0000,   47.8000,   48.2000, +                                 48.2000,   48.2000)) +    ("UHF"  , std::vector<float>(-8.0000,   -8.0000,   -7.0000,    4.0000,   10.2000, +                                 14.5000,   17.5000,   20.0000,   24.5000,   30.8000, +                                 37.0000,   39.8000,   40.7000,   41.6000,   42.6000, +                                 43.2000,   43.8000)) +; + +static const std::vector<float> tvrx_if_gains_db =  +              std::vector<float>(-1.50000,   -1.50000,   -1.50000,   -1.00000,    0.20000, +                                 2.10000,    4.30000,    6.40000,    9.00000,   12.00000, +                                 14.80000,   18.20000,   26.10000,   32.50000,  32.50000, +                                 32.50000,   32.50000) +; + +//sample voltages for the above points +static const std::vector<float> tvrx_rf_gains_volts =  +              std::vector<float>(0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0); -static const double tvrx_lo_freq = 36e6; //LO freq of TVRX module + +/*! + * Execute a linear interpolation to find the voltage corresponding to a desired gain + * \param gain the desired gain in dB + * \param db_vector the vector of dB readings + * \param volts_vector the corresponding vector of voltages db_vector was sampled at + * \return a voltage to feed the TVRX analog gain + */ + +static float gain_interp(float gain, std::vector<float> db_vector, std::vector<float> volts_vector) { +    float volts; +    gain = std::clip<float>(gain, db_vector.front(), db_vector.back()); //let's not get carried away here +     +    boost::uint8_t gain_step = 0; +    for(int i = 0; i < db_vector.size()-1; i++) { +        if(gain > db_vector[i] && gain < db_vector.[i+1]) gain_step = i; +    } +         +     +    return volts; +} + +static const double opamp_gain = 1.22; //onboard DAC opamp gain +static const double tvrx_lo_freq = 43.75e6; //LO freq of TVRX module +static const boost::uint16_t reference_divider = 640; //clock reference divider to use  /***********************************************************************   * The tvrx dboard class @@ -84,40 +139,28 @@ public:  private:      uhd::dict<std::string, float> _gains; -    dtt75403_regs_t _dtt75403_regs; -    boost::uint8_t _dtt75403_addr(void){ +    tuner_4937di5_regs_t _tuner_4937di5_regs; +    boost::uint8_t _tuner_4937di5_addr(void){          return (this->get_iface()->get_special_props().mangle_i2c_addrs)? 0x60 : 0x61; //ok really? we could rename that call      };      void set_gain(float gain, const std::string &name); -    void send_reg(boost::uint8_t start_reg, boost::uint8_t stop_reg){ -        //why is this an inline anyway? bring this out -        start_reg = boost::uint8_t(std::clip(int(start_reg), 0x0, 0x5)); -        stop_reg = boost::uint8_t(std::clip(int(stop_reg), 0x0, 0x5)); - -        for(boost::uint8_t start_addr=start_reg; start_addr <= stop_reg; start_addr += sizeof(boost::uint32_t) - 1){ -            int num_bytes = int(stop_reg - start_addr + 1) > int(sizeof(boost::uint32_t)) - 1 ? sizeof(boost::uint32_t) - 1 : stop_reg - start_addr + 1; - -            //create buffer for register data (+1 for start address) -            byte_vector_t regs_vector(num_bytes + 1); +    void update_regs(void){ +        byte_vector_t regs_vector(4); -            //first byte is the address of first register -            regs_vector[0] = start_addr; - -            //get the register data -            for(int i=0; i<num_bytes; i++){ -                regs_vector[1+i] = _max2118_write_regs.get_reg(start_addr+i); -                if(tvrx_debug) std::cerr << boost::format( -                    "tvrx: send reg 0x%02x, value 0x%04x, start_addr = 0x%04x, num_bytes %d" -                ) % int(start_addr+i) % int(regs_vector[1+i]) % int(start_addr) % num_bytes << std::endl; -            } - -            //send the data -            this->get_iface()->write_i2c( -                _max2118_addr(), regs_vector -            ); +        //get the register data +        for(int i=0; i<4; i++){ +            regs_vector[i] = _tuner_4937di5_regs.get_reg(i); +            if(tvrx_debug) std::cerr << boost::format( +                "tvrx: send reg 0x%02x, value 0x%04x" +            ) % int(i) % int(regs_vector[i]) << std::endl;          } + +        //send the data +        this->get_iface()->write_i2c( +            _tuner_4937di5_addr(), regs_vector +        );      }  }; @@ -138,10 +181,6 @@ UHD_STATIC_BLOCK(reg_tvrx_dboard){   * Structors   **********************************************************************/  tvrx::tvrx(ctor_args_t args) : rx_dboard_base(args){ -    //enable only the clocks we need -     -    //TODO: YOU GOT SOME CLOCK SETUP TO DO HERE, DOGGGG -          this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true);      //set the gpio directions and atr controls (identically) @@ -165,79 +204,116 @@ tvrx::tvrx(ctor_args_t args) : rx_dboard_base(args){  tvrx::~tvrx(void){  } +/*! Return a string corresponding to the relevant band + * \param freq the frequency of interest + * \return a string corresponding to the band + */ + +static std::string get_band(double freq) { +    BOOST_FOREACH(const std::string &band, tvrx_freq_ranges.keys()) { +        if(freq >= tvrx_freq_ranges[band].min && freq <= tvrx_freq_ranges[band].max) +            return name; +    } +    UHD_THROW_INVALID_CODE_PATH(); +} +  /***********************************************************************   * Gain Handling   **********************************************************************/ +/*  + * Gain accuracy on this thing is basically a crap shoot. In other words, there is none. + * The old driver basically picked a linear approximation of the median gain, but the actual + * gain slope is heavily nonlinear and varies both with gain and with frequency. + * It also probably varies markedly over temperature, but there's only so much we can do. + * The best approximation to the RF gain data is fifth-order. If we ignore the curve portions + * below 0dB (yes, it's a pad below a certain gain value) and if we ignore the curve portions + * near the max-gain asymptotes, it looks pretty close to third-order. This is less insane to + * approximate. + */ +  /*! - * Convert a requested gain for the GC2 vga into the integer register value. + * Convert a requested gain for the RF gain into a DAC voltage.   * The gain passed into the function will be set to the actual value.   * \param gain the requested gain in dB - * \return 5 bit the register value + * \return dac voltage value   */ -//yo this should look the same as the below one for the IF, dogg -static int gain_to_gc2_vga_reg(float &gain){ -    int reg = 0; -    gain = std::clip<float>(float(boost::math::iround(gain)), tvrx_gain_ranges["GC2"].min, tvrx_gain_ranges["GC2"].max); - -    // Half dB steps from 0-5dB, 1dB steps from 5-24dB -    if (gain < 5) { -        reg = boost::math::iround(31.0 - gain/0.5); -        gain = float(boost::math::iround(gain) * 0.5); -    } else { -        reg = boost::math::iround(22.0 - (gain - 4.0)); -        gain = float(boost::math::iround(gain)); -    } +static float rf_gain_to_voltage(float &gain){ +    //clip the input +    gain = std::clip<float>(gain, tvrx_gain_ranges["RF"].min, tvrx_gain_ranges["RF"].max); + +    //voltage level constants +    static const float max_volts = float(4.0), min_volts = float(0.6); +    static const float slope = (max_volts-min_volts)/tvrx_gain_ranges["RF"].max; + +    //this is the voltage at the TVRX gain input +    float gain_volts = gain*slope + min_volts +    //this is the voltage at the USRP DAC output +    float dac_volts = gain_volts / opamp_gain; + +    //the actual gain setting +    gain = (dac_volts - min_volts)/slope;      if (tvrx_debug) std::cerr << boost::format( -        "tvrx GC2 Gain: %f dB, reg: %d" -    ) % gain % reg << std::endl; +        "tvrx RF AGC gain: %f dB, dac_volts: %f V" +    ) % gain % dac_volts << std::endl; -    return reg; +    return dac_volts;  }  /*! - * Convert a requested gain for the RF gain into the dac_volts value. + * Convert a requested gain for the IF gain into a DAC voltage.   * The gain passed into the function will be set to the actual value.   * \param gain the requested gain in dB   * \return dac voltage value   */ -//yo what about the 1.22 opamp gain of the USRP1 -- is this replicated on U2? -static float gain_to_rfgain_dac(float &gain){ +static float if_gain_to_voltage(float &gain){      //clip the input -    gain = std::clip<float>(gain, tvrx_gain_ranges["RF"].min, tvrx_gain_ranges["RF"].max); +    gain = std::clip<float>(gain, tvrx_gain_ranges["IF"].min, tvrx_gain_ranges["IF"].max);      //voltage level constants -    static const float max_volts = float(0), min_volts = float(3.3); -    static const float slope = (max_volts-min_volts)/tvrx_gain_ranges["RF"].max; +    static const float max_volts = float(4.0), min_volts = float(0.0); +    static const float slope = (max_volts-min_volts)/tvrx_gain_ranges["IF"].max; -    //calculate the voltage for the aux dac -    float dac_volts = gain*slope + min_volts; - -    if (tvrx_debug) std::cerr << boost::format( -        "tvrx RFAGC Gain: %f dB, dac_volts: %f V" -    ) % gain % dac_volts << std::endl; +    //this is the voltage at the TVRX gain input +    float gain_volts = gain*slope + 1.25; +    //this is the voltage at the USRP DAC output +    float dac_volts = gain_volts / opamp_gain;      //the actual gain setting      gain = (dac_volts - min_volts)/slope; +    if (tvrx_debug) std::cerr << boost::format( +        "tvrx IF AGC gain: %f dB, dac_volts: %f V" +    ) % gain % dac_volts << std::endl; +      return dac_volts;  }  void tvrx::set_gain(float gain, const std::string &name){      assert_has(tvrx_gain_ranges.keys(), name, "tvrx gain name");      if (name == "RF"){ -//should look the same as below +        this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_A, rf_gain_to_voltage(gain));      }      else if(name == "IF"){ -        //write the new voltage to the aux dac                                CHECK THIS -        this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_A, gain_to_gc1_rfvga_dac(gain)); +        this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_B, if_gain_to_voltage(gain));      }      else UHD_THROW_INVALID_CODE_PATH();      _gains[name] = gain;  } +/*! + * Set the tuner to center the desired frequency at 43.75MHz + * \param freq the requested frequency + */ + +void tvrx::set_freq(double freq) { +     +     +     +} +  /***********************************************************************   * RX Get and Set   **********************************************************************/ @@ -300,7 +376,6 @@ void tvrx::rx_get(const wax::obj &key_, wax::obj &val){      }  } -//for smaaht kids to use to set advanced props  void tvrx::rx_set(const wax::obj &key_, const wax::obj &val){      named_prop_t key = named_prop_t::extract(key_); @@ -309,6 +384,9 @@ void tvrx::rx_set(const wax::obj &key_, const wax::obj &val){      case SUBDEV_PROP_GAIN:          this->set_gain(val.as<float>(), key.name);          return; +    case SUBDEV_PROP_FREQ: +        this->set_freq(val.as<double>()); +        return;      default: UHD_THROW_PROP_SET_ERROR();      } | 
