mirror of
https://github.com/fairwaves/UHD-Fairwaves.git
synced 2025-11-02 13:03:13 +00:00
umtrx: added calibration back in
This commit is contained in:
@@ -39,6 +39,7 @@ list(APPEND UMTRX_SOURCES
|
||||
cores/tx_dsp_core_200.cpp
|
||||
cores/time64_core_200.cpp
|
||||
cores/validate_subdev_spec.cpp
|
||||
cores/apply_corrections.cpp
|
||||
)
|
||||
|
||||
########################################################################
|
||||
|
||||
192
host/cores/apply_corrections.cpp
Normal file
192
host/cores/apply_corrections.cpp
Normal file
@@ -0,0 +1,192 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
//
|
||||
// This program 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 version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#include "apply_corrections.hpp"
|
||||
#include <uhd/usrp/dboard_eeprom.hpp>
|
||||
#include <uhd/utils/paths.hpp>
|
||||
#include <uhd/utils/msg.hpp>
|
||||
#include <uhd/utils/csv.hpp>
|
||||
#include <uhd/types/dict.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <cstdio>
|
||||
#include <complex>
|
||||
#include <fstream>
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
boost::mutex corrections_mutex;
|
||||
|
||||
/***********************************************************************
|
||||
* Helper routines
|
||||
**********************************************************************/
|
||||
static double linear_interp(double x, double x0, double y0, double x1, double y1){
|
||||
return y0 + (x - x0)*(y1 - y0)/(x1 - x0);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* FE apply corrections implementation
|
||||
**********************************************************************/
|
||||
struct fe_cal_t{
|
||||
double lo_freq;
|
||||
double iq_corr_real;
|
||||
double iq_corr_imag;
|
||||
};
|
||||
|
||||
static bool fe_cal_comp(fe_cal_t a, fe_cal_t b){
|
||||
return (a.lo_freq < b.lo_freq);
|
||||
}
|
||||
|
||||
static uhd::dict<std::string, std::vector<fe_cal_t> > fe_cal_cache;
|
||||
|
||||
static bool is_same_freq(const double f1, const double f2)
|
||||
{
|
||||
const double epsilon = 0.1;
|
||||
return ((f1 - epsilon) < f2 and (f1 + epsilon) > f2);
|
||||
}
|
||||
|
||||
static std::complex<double> get_fe_correction(
|
||||
const std::string &key, const double lo_freq
|
||||
){
|
||||
const std::vector<fe_cal_t> &datas = fe_cal_cache[key];
|
||||
if (datas.empty()) throw uhd::runtime_error("empty calibration table " + key);
|
||||
|
||||
//search for lo freq
|
||||
size_t lo_index = 0;
|
||||
size_t hi_index = datas.size()-1;
|
||||
for (size_t i = 0; i < datas.size(); i++){
|
||||
if (is_same_freq(datas[i].lo_freq, lo_freq))
|
||||
{
|
||||
hi_index = i;
|
||||
lo_index = i;
|
||||
break;
|
||||
}
|
||||
if (datas[i].lo_freq > lo_freq){
|
||||
hi_index = i;
|
||||
break;
|
||||
}
|
||||
lo_index = i;
|
||||
}
|
||||
|
||||
if (lo_index == 0) return std::complex<double>(datas[lo_index].iq_corr_real, datas[lo_index].iq_corr_imag);
|
||||
if (hi_index == lo_index) return std::complex<double>(datas[hi_index].iq_corr_real, datas[hi_index].iq_corr_imag);
|
||||
|
||||
//interpolation time
|
||||
return std::complex<double>(
|
||||
linear_interp(lo_freq, datas[lo_index].lo_freq, datas[lo_index].iq_corr_real, datas[hi_index].lo_freq, datas[hi_index].iq_corr_real),
|
||||
linear_interp(lo_freq, datas[lo_index].lo_freq, datas[lo_index].iq_corr_imag, datas[hi_index].lo_freq, datas[hi_index].iq_corr_imag)
|
||||
);
|
||||
}
|
||||
|
||||
static void apply_fe_corrections(
|
||||
uhd::property_tree::sptr sub_tree,
|
||||
const uhd::fs_path &db_path,
|
||||
const uhd::fs_path &fe_path,
|
||||
const std::string &file_prefix,
|
||||
const double lo_freq
|
||||
){
|
||||
//extract eeprom serial
|
||||
const uhd::usrp::dboard_eeprom_t db_eeprom = sub_tree->access<uhd::usrp::dboard_eeprom_t>(db_path).get();
|
||||
|
||||
//make the calibration file path
|
||||
const fs::path cal_data_path = fs::path(uhd::get_app_path()) / ".uhd" / "cal" / (file_prefix + db_eeprom.serial + ".csv");
|
||||
if (not fs::exists(cal_data_path)) return;
|
||||
|
||||
//parse csv file or get from cache
|
||||
if (not fe_cal_cache.has_key(cal_data_path.string())){
|
||||
std::ifstream cal_data(cal_data_path.string().c_str());
|
||||
const uhd::csv::rows_type rows = uhd::csv::to_rows(cal_data);
|
||||
|
||||
bool read_data = false, skip_next = false;;
|
||||
std::vector<fe_cal_t> datas;
|
||||
BOOST_FOREACH(const uhd::csv::row_type &row, rows){
|
||||
if (not read_data and not row.empty() and row[0] == "DATA STARTS HERE"){
|
||||
read_data = true;
|
||||
skip_next = true;
|
||||
continue;
|
||||
}
|
||||
if (not read_data) continue;
|
||||
if (skip_next){
|
||||
skip_next = false;
|
||||
continue;
|
||||
}
|
||||
fe_cal_t data;
|
||||
std::sscanf(row[0].c_str(), "%lf" , &data.lo_freq);
|
||||
std::sscanf(row[1].c_str(), "%lf" , &data.iq_corr_real);
|
||||
std::sscanf(row[2].c_str(), "%lf" , &data.iq_corr_imag);
|
||||
datas.push_back(data);
|
||||
}
|
||||
std::sort(datas.begin(), datas.end(), fe_cal_comp);
|
||||
fe_cal_cache[cal_data_path.string()] = datas;
|
||||
UHD_MSG(status) << "Loaded " << cal_data_path.string() << std::endl;
|
||||
|
||||
}
|
||||
|
||||
sub_tree->access<std::complex<double> >(fe_path)
|
||||
.set(get_fe_correction(cal_data_path.string(), lo_freq));
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Wrapper routines with nice try/catch + print
|
||||
**********************************************************************/
|
||||
void uhd::usrp::apply_tx_fe_corrections(
|
||||
property_tree::sptr sub_tree, //starts at mboards/x
|
||||
const std::string &slot, //name of dboard slot
|
||||
const double lo_freq //actual lo freq
|
||||
){
|
||||
boost::mutex::scoped_lock l(corrections_mutex);
|
||||
try{
|
||||
apply_fe_corrections(
|
||||
sub_tree,
|
||||
"dboards/" + slot + "/tx_eeprom",
|
||||
"tx_frontends/" + slot + "/iq_balance/value",
|
||||
"tx_iq_cal_v0.2_",
|
||||
lo_freq
|
||||
);
|
||||
apply_fe_corrections(
|
||||
sub_tree,
|
||||
"dboards/" + slot + "/tx_eeprom",
|
||||
"tx_frontends/" + slot + "/dc_offset/value",
|
||||
"tx_dc_cal_v0.2_",
|
||||
lo_freq
|
||||
);
|
||||
}
|
||||
catch(const std::exception &e){
|
||||
UHD_MSG(error) << "Failure in apply_tx_fe_corrections: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void uhd::usrp::apply_rx_fe_corrections(
|
||||
property_tree::sptr sub_tree, //starts at mboards/x
|
||||
const std::string &slot, //name of dboard slot
|
||||
const double lo_freq //actual lo freq
|
||||
){
|
||||
boost::mutex::scoped_lock l(corrections_mutex);
|
||||
try{
|
||||
apply_fe_corrections(
|
||||
sub_tree,
|
||||
"dboards/" + slot + "/rx_eeprom",
|
||||
"rx_frontends/" + slot + "/iq_balance/value",
|
||||
"rx_iq_cal_v0.2_",
|
||||
lo_freq
|
||||
);
|
||||
}
|
||||
catch(const std::exception &e){
|
||||
UHD_MSG(error) << "Failure in apply_rx_fe_corrections: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
41
host/cores/apply_corrections.hpp
Normal file
41
host/cores/apply_corrections.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
//
|
||||
// This program 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 version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#ifndef INCLUDED_LIBUHD_USRP_COMMON_APPLY_CORRECTIONS_HPP
|
||||
#define INCLUDED_LIBUHD_USRP_COMMON_APPLY_CORRECTIONS_HPP
|
||||
|
||||
#include <uhd/config.hpp>
|
||||
#include <uhd/property_tree.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace uhd{ namespace usrp{
|
||||
|
||||
void apply_tx_fe_corrections(
|
||||
property_tree::sptr sub_tree, //starts at mboards/x
|
||||
const std::string &slot, //name of dboard slot
|
||||
const double tx_lo_freq //actual lo freq
|
||||
);
|
||||
|
||||
void apply_rx_fe_corrections(
|
||||
property_tree::sptr sub_tree, //starts at mboards/x
|
||||
const std::string &slot, //name of dboard slot
|
||||
const double rx_lo_freq //actual lo freq
|
||||
);
|
||||
|
||||
}} //namespace uhd::usrp
|
||||
|
||||
#endif /* INCLUDED_LIBUHD_USRP_COMMON_APPLY_CORRECTIONS_HPP */
|
||||
@@ -174,6 +174,16 @@ public:
|
||||
return lms_tx_gain_ranges[name];
|
||||
}
|
||||
|
||||
uint8_t get_tx_vga1dc_i_int(void)
|
||||
{
|
||||
return lms.get_tx_vga1dc_i_int();
|
||||
}
|
||||
|
||||
uint8_t get_tx_vga1dc_q_int(void)
|
||||
{
|
||||
return lms.get_tx_vga1dc_i_int();
|
||||
}
|
||||
|
||||
double set_freq(dboard_iface::unit_t unit, double f) {
|
||||
if (verbosity>0) printf("lms6002d_ctrl_impl::set_freq(%f)\n", f);
|
||||
unsigned ref_freq = _clock_rate;
|
||||
@@ -563,15 +573,5 @@ lms6002d_ctrl_impl::lms6002d_ctrl_impl(uhd::spi_iface::sptr spiface, const int l
|
||||
|
||||
// Perform autocalibration
|
||||
lms.auto_calibration(_clock_rate, 0xf);
|
||||
|
||||
// UmTRX specific calibration
|
||||
/*
|
||||
this->get_tx_subtree()->create<uint8_t>("lms6002d/tx_dc_i/value")
|
||||
.subscribe(boost::bind(&lms6002d_ctrl_impl::_set_tx_vga1dc_i_int, this, _1))
|
||||
.publish(boost::bind(&umtrx_lms6002d_dev::get_tx_vga1dc_i_int, &lms));
|
||||
this->get_tx_subtree()->create<uint8_t>("lms6002d/tx_dc_q/value")
|
||||
.subscribe(boost::bind(&lms6002d_ctrl_impl::_set_tx_vga1dc_q_int, this, _1))
|
||||
.publish(boost::bind(&umtrx_lms6002d_dev::get_tx_vga1dc_q_int, &lms));
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,12 @@ public:
|
||||
|
||||
virtual uhd::gain_range_t get_rx_gain_range(const std::string &name) = 0;
|
||||
virtual uhd::gain_range_t get_tx_gain_range(const std::string &name) = 0;
|
||||
|
||||
virtual uint8_t _set_tx_vga1dc_i_int(uint8_t offset) = 0;
|
||||
virtual uint8_t _set_tx_vga1dc_q_int(uint8_t offset) = 0;
|
||||
|
||||
virtual uint8_t get_tx_vga1dc_i_int(void) = 0;
|
||||
virtual uint8_t get_tx_vga1dc_q_int(void) = 0;
|
||||
};
|
||||
|
||||
#endif /* INCLUDED_LMS6002D_CTRL_HPP */
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "umtrx_impl.hpp"
|
||||
#include "umtrx_regs.hpp"
|
||||
#include "cores/apply_corrections.hpp"
|
||||
#include <uhd/utils/log.hpp>
|
||||
#include <uhd/utils/msg.hpp>
|
||||
#include <uhd/types/sensors.hpp>
|
||||
@@ -249,8 +250,8 @@ umtrx_impl::umtrx_impl(const device_addr_t &device_addr)
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// create RF frontend interfacing
|
||||
////////////////////////////////////////////////////////////////////
|
||||
_lms_ctrl["A"] = lms6002d_ctrl::make(_iface, SPI_SS_LMS1, SPI_SS_AUX1, this->get_master_clock_rate()/2.0);
|
||||
_lms_ctrl["B"] = lms6002d_ctrl::make(_iface, SPI_SS_LMS2, SPI_SS_AUX2, this->get_master_clock_rate()/2.0);
|
||||
_lms_ctrl["A"] = lms6002d_ctrl::make(_iface, SPI_SS_LMS1, SPI_SS_AUX1, this->get_master_clock_rate());
|
||||
_lms_ctrl["B"] = lms6002d_ctrl::make(_iface, SPI_SS_LMS2, SPI_SS_AUX2, this->get_master_clock_rate());
|
||||
|
||||
BOOST_FOREACH(const std::string &fe_name, _lms_ctrl.keys())
|
||||
{
|
||||
@@ -338,10 +339,39 @@ umtrx_impl::umtrx_impl(const device_addr_t &device_addr)
|
||||
_tree->create<meta_range_t>(tx_rf_fe_path / "bandwidth" / "range")
|
||||
.publish(boost::bind(&lms6002d_ctrl::get_tx_bw_range, ctrl));
|
||||
|
||||
//TODO // UmTRX specific calibration
|
||||
//bind frontend corrections to the dboard freq props
|
||||
_tree->access<double>(tx_rf_fe_path / "freq" / "value")
|
||||
.subscribe(boost::bind(&umtrx_impl::set_tx_fe_corrections, this, "0", fe_name, _1));
|
||||
_tree->access<double>(rx_rf_fe_path / "freq" / "value")
|
||||
.subscribe(boost::bind(&umtrx_impl::set_rx_fe_corrections, this, "0", fe_name, _1));
|
||||
|
||||
//tx cal props
|
||||
_tree->create<uint8_t>(tx_rf_fe_path / "lms6002d" / "tx_dc_i" / "value")
|
||||
.subscribe(boost::bind(&lms6002d_ctrl::_set_tx_vga1dc_i_int, ctrl, _1))
|
||||
.publish(boost::bind(&lms6002d_ctrl::get_tx_vga1dc_i_int, ctrl));
|
||||
_tree->create<uint8_t>(tx_rf_fe_path / "lms6002d" / "tx_dc_q" / "value")
|
||||
.subscribe(boost::bind(&lms6002d_ctrl::_set_tx_vga1dc_q_int, ctrl, _1))
|
||||
.publish(boost::bind(&lms6002d_ctrl::get_tx_vga1dc_q_int, ctrl));
|
||||
|
||||
//set Tx DC calibration values, which are read from mboard EEPROM
|
||||
std::string tx_name = (fe_name=="A")?"tx1":"tx2";
|
||||
if (_iface->mb_eeprom.has_key(tx_name+"-vga1-dc-i") and not _iface->mb_eeprom[tx_name+"-vga1-dc-i"].empty()) {
|
||||
_tree->access<uint8_t>(tx_rf_fe_path / "lms6002d" / "tx_dc_i" / "value")
|
||||
.set(boost::lexical_cast<int>(_iface->mb_eeprom[tx_name+"-vga1-dc-i"]));
|
||||
}
|
||||
if (_iface->mb_eeprom.has_key(tx_name+"-vga1-dc-q") and not _iface->mb_eeprom[tx_name+"-vga1-dc-q"].empty()) {
|
||||
_tree->access<uint8_t>(tx_rf_fe_path / "lms6002d" / "tx_dc_q" / "value")
|
||||
.set(boost::lexical_cast<int>(_iface->mb_eeprom[tx_name+"-vga1-dc-q"]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//set TCXO DAC calibration value, which is read from mboard EEPROM
|
||||
if (_iface->mb_eeprom.has_key("tcxo-dac") and not _iface->mb_eeprom["tcxo-dac"].empty()) {
|
||||
_tree->create<uint16_t>(mb_path / "tcxo_dac" / "value")
|
||||
.subscribe(boost::bind(&umtrx_impl::set_tcxo_dac, this, _iface, _1))
|
||||
.set(boost::lexical_cast<uint16_t>(_iface->mb_eeprom["tcxo-dac"]));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// post config tasks
|
||||
@@ -384,3 +414,22 @@ void umtrx_impl::time64_self_test(void)
|
||||
}
|
||||
|
||||
void umtrx_impl::update_clock_source(const std::string &){}
|
||||
|
||||
void umtrx_impl::set_rx_fe_corrections(const std::string &mb, const std::string &board, const double lo_freq){
|
||||
apply_rx_fe_corrections(this->get_tree()->subtree("/mboards/" + mb), board, lo_freq);
|
||||
}
|
||||
|
||||
void umtrx_impl::set_tx_fe_corrections(const std::string &mb, const std::string &board, const double lo_freq){
|
||||
apply_tx_fe_corrections(this->get_tree()->subtree("/mboards/" + mb), board, lo_freq);
|
||||
}
|
||||
|
||||
void umtrx_impl::set_tcxo_dac(const umtrx_iface::sptr &iface, const uint16_t val){
|
||||
if (verbosity>0) printf("umtrx_impl::set_tcxo_dac(%d)\n", val);
|
||||
iface->send_zpu_action(UMTRX_ZPU_REQUEST_SET_VCTCXO_DAC, val);
|
||||
}
|
||||
|
||||
uint16_t umtrx_impl::get_tcxo_dac(const umtrx_iface::sptr &iface){
|
||||
uint16_t val = iface->send_zpu_action(UMTRX_ZPU_REQUEST_GET_VCTCXO_DAC, 0);
|
||||
if (verbosity>0) printf("umtrx_impl::get_tcxo_dac(): %d\n", val);
|
||||
return (uint16_t)val;
|
||||
}
|
||||
|
||||
@@ -104,6 +104,10 @@ private:
|
||||
void update_tx_samp_rate(const size_t, const double rate);
|
||||
void time64_self_test(void);
|
||||
void update_rates(void);
|
||||
void set_rx_fe_corrections(const std::string &mb, const std::string &board, const double);
|
||||
void set_tx_fe_corrections(const std::string &mb, const std::string &board, const double);
|
||||
void set_tcxo_dac(const umtrx_iface::sptr &, const uint16_t val);
|
||||
uint16_t get_tcxo_dac(const umtrx_iface::sptr &);
|
||||
|
||||
//streaming
|
||||
std::vector<boost::weak_ptr<uhd::rx_streamer> > _rx_streamers;
|
||||
|
||||
Reference in New Issue
Block a user