summaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/dboard
diff options
context:
space:
mode:
authorNick Foster <nick@nerdnetworks.org>2010-09-28 13:52:09 -0700
committerNick Foster <nick@nerdnetworks.org>2010-09-28 13:52:09 -0700
commit15eacbd920a0527ec16c24baeb1d6b87f29d9110 (patch)
tree7ed14b4522fa6236b193b21aaf9909a4e8091a20 /host/lib/usrp/dboard
parenta6f60ab4911fa756656a62ebe3a1093d52836a6e (diff)
downloaduhd-15eacbd920a0527ec16c24baeb1d6b87f29d9110.tar.gz
uhd-15eacbd920a0527ec16c24baeb1d6b87f29d9110.tar.bz2
uhd-15eacbd920a0527ec16c24baeb1d6b87f29d9110.zip
TVRX: Don't have mboard impl modified for ADC buffer disable. The rest of TVRX should be in there. Not debugged.
Diffstat (limited to 'host/lib/usrp/dboard')
-rw-r--r--host/lib/usrp/dboard/CMakeLists.txt1
-rw-r--r--host/lib/usrp/dboard/db_tvrx.cpp219
2 files changed, 131 insertions, 89 deletions
diff --git a/host/lib/usrp/dboard/CMakeLists.txt b/host/lib/usrp/dboard/CMakeLists.txt
index 3e995009e..8d3d11530 100644
--- a/host/lib/usrp/dboard/CMakeLists.txt
+++ b/host/lib/usrp/dboard/CMakeLists.txt
@@ -24,5 +24,6 @@ LIBUHD_APPEND_SOURCES(
${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_wbx.cpp
${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_dbsrx.cpp
${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_unknown.cpp
+ ${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_tvrx.cpp
)
diff --git a/host/lib/usrp/dboard/db_tvrx.cpp b/host/lib/usrp/dboard/db_tvrx.cpp
index ed6025433..2988367e2 100644
--- a/host/lib/usrp/dboard/db_tvrx.cpp
+++ b/host/lib/usrp/dboard/db_tvrx.cpp
@@ -39,9 +39,11 @@
#include <boost/assign/list_of.hpp>
#include <boost/format.hpp>
#include <boost/thread.hpp>
+#include <boost/array.hpp>
#include <boost/math/special_functions/round.hpp>
#include <utility>
#include <cmath>
+#include <cfloat>
#include <tuner_4937di5_regs.hpp>
using namespace uhd;
@@ -55,12 +57,11 @@ static const bool tvrx_debug = true;
static const freq_range_t tvrx_freq_range(50e6, 860e6);
-static const prop_names_t tvrx_antennas = ""; //only got one
+static const std::string tvrx_antennas = std::string(""); //only got one
-static const uhd::dict<std::string, gain_range_t> tvrx_gain_ranges = map_list_of
- ("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?
-;
+//a note on these: the gain of the TVRX varies over frequency. the gain ranges
+//below correspond to the maximum and minimum gains over the entire frequency range.
+//specifying a gain higher or lower than the TVRX can accomplish will of course just clip.
static const uhd::dict<std::string, freq_range_t> tvrx_freq_ranges = map_list_of
("VHFLO", freq_range_t(50e6, 158e6))
@@ -68,63 +69,68 @@ static const uhd::dict<std::string, freq_range_t> tvrx_freq_ranges = map_list_of
("UHF" , freq_range_t(454e6, 860e6))
;
+static const boost::array<float, 17> vhflo_gains_db =
+ {{-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}};
+
+static const boost::array<float, 17> vhfhi_gains_db =
+ {{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}};
+
+static const boost::array<float, 17> uhf_gains_db =
+ {{-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 boost::array<float, 17> tvrx_if_gains_db =
+ {{-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}};
+
//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)
+static const uhd::dict<std::string, boost::array<float, 17> > tvrx_rf_gains_db = map_list_of
+ ("VHFLO", vhflo_gains_db)
+ ("VHFHI", vhfhi_gains_db)
+ ("UHF" , uhf_gains_db)
;
//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);
-
-
-/*!
- * 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;
+static const boost::array<float, 17> tvrx_gains_volts =
+ {{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 uhd::dict<std::string, gain_range_t> get_tvrx_gain_ranges(void) {
+ float rfmax = 0.0, rfmin = FLT_MAX;
+ BOOST_FOREACH(const std::string range, tvrx_rf_gains_db.keys()) {
+ float my_max = tvrx_rf_gains_db[range].back(); //we're assuming it's monotonic
+ float my_min = tvrx_rf_gains_db[range].front(); //if it's not this is wrong wrong wrong
+ if(my_max > rfmax) rfmax = my_max;
+ if(my_min < rfmin) rfmin = my_min;
}
-
- return volts;
+ float ifmin = tvrx_if_gains_db.front();
+ float ifmax = tvrx_if_gains_db.back();
+
+ return map_list_of
+ ("RF", gain_range_t(rfmin, rfmax, (rfmax-rfmin)/4096.0))
+ ("IF", gain_range_t(ifmin, ifmax, (ifmax-ifmin)/4096.0))
+ ;
}
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 double tvrx_if_freq = 43.75e6; //IF freq of TVRX module
static const boost::uint16_t reference_divider = 640; //clock reference divider to use
+static const double reference_freq = 4.0e6;
/***********************************************************************
* The tvrx dboard class
@@ -139,12 +145,14 @@ public:
private:
uhd::dict<std::string, float> _gains;
+ double _lo_freq;
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 set_freq(double freq);
void update_regs(void){
byte_vector_t regs_vector(4);
@@ -192,12 +200,14 @@ tvrx::tvrx(ctor_args_t args) : rx_dboard_base(args){
this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x0); // All Inputs
}
- //send initial register settings
- //HAAAAY GUYYYYYSSS
+ //send initial register settings if necessary
+
+ //set default freq
+ set_freq(tvrx_freq_range.min);
//set default gains
- BOOST_FOREACH(const std::string &name, tvrx_gain_ranges.keys()){
- set_gain(tvrx_gain_ranges[name].min, name);
+ BOOST_FOREACH(const std::string &name, get_tvrx_gain_ranges().keys()){
+ set_gain(get_tvrx_gain_ranges()[name].min, name);
}
}
@@ -212,7 +222,7 @@ tvrx::~tvrx(void){
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;
+ return band;
}
UHD_THROW_INVALID_CODE_PATH();
}
@@ -220,17 +230,34 @@ static std::string get_band(double freq) {
/***********************************************************************
* 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.
+/*!
+ * 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, boost::array<float, 17> db_vector, boost::array<float, 17> 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;
+ //find which bin we're in
+ for(size_t i = 0; i < db_vector.size()-1; i++) {
+ if(gain > db_vector[i] && gain < db_vector[i+1]) gain_step = i;
+ }
+
+ //find the current slope for linear interpolation
+ float slope = (volts_vector[gain_step + 1] - volts_vector[gain_step])
+ / (db_vector[gain_step + 1] - db_vector[gain_step]);
+
+ //use the volts per dB slope to find the final interpolated voltage
+ volts = volts_vector[gain_step] + (slope * (gain - db_vector[gain_step]));
+
+ return volts;
+}
+
/*!
* 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.
@@ -238,22 +265,18 @@ static std::string get_band(double freq) {
* \return dac voltage value
*/
-static float rf_gain_to_voltage(float &gain){
+static float rf_gain_to_voltage(float gain, double lo_freq){
//clip the input
- gain = std::clip<float>(gain, tvrx_gain_ranges["RF"].min, tvrx_gain_ranges["RF"].max);
+ gain = std::clip<float>(gain, get_tvrx_gain_ranges()["RF"].min, get_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;
+ //first we need to find out what band we're in, because gains are different across different bands
+ std::string band = get_band(lo_freq + tvrx_if_freq);
//this is the voltage at the TVRX gain input
- float gain_volts = gain*slope + min_volts
+ float gain_volts = gain_interp(gain, tvrx_rf_gains_db[band], tvrx_gains_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 RF AGC gain: %f dB, dac_volts: %f V"
) % gain % dac_volts << std::endl;
@@ -268,22 +291,13 @@ static float rf_gain_to_voltage(float &gain){
* \return dac voltage value
*/
-static float if_gain_to_voltage(float &gain){
+static float if_gain_to_voltage(float gain){
//clip the input
- gain = std::clip<float>(gain, tvrx_gain_ranges["IF"].min, tvrx_gain_ranges["IF"].max);
-
- //voltage level constants
- 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;
-
- //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
+ gain = std::clip<float>(gain, get_tvrx_gain_ranges()["IF"].min, get_tvrx_gain_ranges()["IF"].max);
+
+ float gain_volts = gain_interp(gain, tvrx_if_gains_db, tvrx_gains_volts);
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;
@@ -292,9 +306,9 @@ static float if_gain_to_voltage(float &gain){
}
void tvrx::set_gain(float gain, const std::string &name){
- assert_has(tvrx_gain_ranges.keys(), name, "tvrx gain name");
+ assert_has(get_tvrx_gain_ranges().keys(), name, "tvrx gain name");
if (name == "RF"){
- this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_A, rf_gain_to_voltage(gain));
+ this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_A, rf_gain_to_voltage(gain, _lo_freq));
}
else if(name == "IF"){
this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_B, if_gain_to_voltage(gain));
@@ -309,9 +323,36 @@ void tvrx::set_gain(float gain, const std::string &name){
*/
void tvrx::set_freq(double freq) {
+ freq = std::clip<float>(freq, tvrx_freq_range.min, tvrx_freq_range.max);
+ std::string prev_band = get_band(_lo_freq + tvrx_if_freq);
+ std::string new_band = get_band(freq);
+ double lo_freq = freq + tvrx_if_freq; //the desired LO freq for high-side mixing
+ double f_ref = reference_freq / double(reference_divider); //your tuning step size
+ int divisor = int(lo_freq + (f_ref * 4.0)) / (f_ref * 8); //the divisor we'll use
+ double actual_lo_freq = (f_ref * 8 * divisor); //the LO freq we'll actually get
+
+ if((divisor & ~0x7fff)) UHD_THROW_INVALID_CODE_PATH();
+
+ //now we update the registers
+ _tuner_4937di5_regs.db1 = (divisor >> 8) & 0xff;
+ _tuner_4937di5_regs.db2 = divisor & 0xff;
+
+ if(new_band == "VHFLO") _tuner_4937di5_regs.bandsel = tuner_4937di5_regs_t::BANDSEL_VHFLO;
+ else if(new_band == "VHFHI") _tuner_4937di5_regs.bandsel = tuner_4937di5_regs_t::BANDSEL_VHFHI;
+ else if(new_band == "UHF") _tuner_4937di5_regs.bandsel = tuner_4937di5_regs_t::BANDSEL_UHF;
+ else UHD_THROW_INVALID_CODE_PATH();
+
+ _tuner_4937di5_regs.power = tuner_4937di5_regs_t::POWER_ON;
+ update_regs();
+
+ //ok don't forget to reset RF gain here if the new band != the old band
+ //we do this because the gains are different for different band settings
+ //not FAR off, but we do this to be consistent
+ if(prev_band != new_band) set_gain(_gains["RF"], "RF");
+ _lo_freq = actual_lo_freq; //for rx props
}
/***********************************************************************
@@ -336,16 +377,16 @@ void tvrx::rx_get(const wax::obj &key_, wax::obj &val){
return;
case SUBDEV_PROP_GAIN_RANGE:
- assert_has(tvrx_gain_ranges.keys(), key.name, "tvrx gain name");
- val = tvrx_gain_ranges[key.name];
+ assert_has(get_tvrx_gain_ranges().keys(), key.name, "tvrx gain name");
+ val = get_tvrx_gain_ranges()[key.name];
return;
case SUBDEV_PROP_GAIN_NAMES:
- val = prop_names_t(tvrx_gain_ranges.keys());
+ val = prop_names_t(get_tvrx_gain_ranges().keys());
return;
case SUBDEV_PROP_FREQ:
- val = tvrx_lo_freq;
+ val = _lo_freq - tvrx_if_freq;
return;
case SUBDEV_PROP_FREQ_RANGE: