Statistics
| Branch: | Tag: | Revision:

root / host / utils / usrp_cal_utils.hpp @ 18abd4db

History | View | Annotate | Download (6.72 KB)

1
//
2
// Copyright 2010 Ettus Research LLC
3
//
4
// This program is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// This program is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
//
17

    
18
#include <uhd/utils/paths.hpp>
19
#include <uhd/property_tree.hpp>
20
#include <uhd/usrp/multi_usrp.hpp>
21
#include <uhd/usrp/dboard_eeprom.hpp>
22
#include <boost/filesystem.hpp>
23
#include <iostream>
24
#include <vector>
25
#include <complex>
26
#include <cmath>
27
#include <fstream>
28

    
29
namespace fs = boost::filesystem;
30

    
31
struct result_t{double freq, real_corr, imag_corr, best, delta;};
32

    
33
/***********************************************************************
34
 * Constants
35
 **********************************************************************/
36
static const double tau = 6.28318531;
37
static const double alpha = 0.0001; //very tight iir filter
38
static const size_t wave_table_len = 8192;
39
static const size_t num_search_steps = 5;
40
static const size_t num_search_iters = 7;
41
static const size_t skip_initial_samps = 20;
42
static const double default_freq_step = 7.3e6;
43
static const size_t default_num_samps = 10000;
44

    
45
/***********************************************************************
46
 * Determine gain settings
47
 **********************************************************************/
48
static inline void set_optimum_gain(uhd::usrp::multi_usrp::sptr usrp){
49
    uhd::property_tree::sptr tree = usrp->get_device()->get_tree();
50
    const uhd::fs_path tx_fe_path = "/mboards/0/dboards/A/tx_frontends/0";
51
    const std::string tx_name = tree->access<std::string>(tx_fe_path / "name").get();
52
    if (tx_name.find("WBX") != std::string::npos or tx_name.find("SBX") != std::string::npos){
53
        usrp->set_tx_gain(0);
54
    }
55
    else{
56
        throw std::runtime_error("self-calibration is not supported for this hardware");
57
    }
58

    
59
    const uhd::fs_path rx_fe_path = "/mboards/0/dboards/A/tx_frontends/0";
60
    const std::string rx_name = tree->access<std::string>(rx_fe_path / "name").get();
61
    if (rx_name.find("WBX") != std::string::npos or rx_name.find("SBX") != std::string::npos){
62
        usrp->set_rx_gain(25);
63
    }
64
    else{
65
        throw std::runtime_error("self-calibration is not supported for this hardware");
66
    }
67

    
68
}
69

    
70
/***********************************************************************
71
 * Sinusoid wave table
72
 **********************************************************************/
73
static inline std::vector<std::complex<float> > gen_table(void){
74
    std::vector<std::complex<float> > wave_table(wave_table_len);
75
    for (size_t i = 0; i < wave_table_len; i++){
76
        wave_table[i] = std::complex<float>(std::polar(1.0, (tau*i)/wave_table_len));
77
    }
78
    return wave_table;
79
}
80

    
81
static inline std::complex<float> wave_table_lookup(const size_t index){
82
    static const std::vector<std::complex<float> > wave_table = gen_table();
83
    return wave_table[index % wave_table_len];
84
}
85

    
86
/***********************************************************************
87
 * Compute power of a tone
88
 **********************************************************************/
89
static inline double compute_tone_dbrms(
90
    const std::vector<std::complex<float> > &samples,
91
    const double freq //freq is fractional
92
){
93
    //shift the samples so the tone at freq is down at DC
94
    std::vector<std::complex<double> > shifted(samples.size() - skip_initial_samps);
95
    for (size_t i = 0; i < shifted.size(); i++){
96
        shifted[i] = std::complex<double>(samples[i+skip_initial_samps]) * std::polar<double>(1.0, -freq*tau*i);
97
    }
98

    
99
    //filter the samples with a narrow low pass
100
    std::complex<double> iir_output = 0, iir_last = 0;
101
    double output = 0;
102
    for (size_t i = 0; i < shifted.size(); i++){
103
        iir_output = alpha * shifted[i] + (1-alpha)*iir_last;
104
        iir_last = iir_output;
105
        output += std::abs(iir_output);
106
    }
107

    
108
    return 20*std::log10(output/shifted.size());
109
}
110

    
111
/***********************************************************************
112
 * Write a dat file
113
 **********************************************************************/
114
static inline void write_samples_to_file(
115
    const std::vector<std::complex<float> > &samples, const std::string &file
116
){
117
    std::ofstream outfile(file.c_str(), std::ofstream::binary);
118
    outfile.write((const char*)&samples.front(), samples.size()*sizeof(std::complex<float>));
119
    outfile.close();
120
}
121

    
122
/***********************************************************************
123
 * Store data to file
124
 **********************************************************************/
125
static void store_results(
126
    uhd::usrp::multi_usrp::sptr usrp,
127
    const std::vector<result_t> &results,
128
    const std::string &XX,
129
    const std::string &xx,
130
    const std::string &what
131
){
132
    //extract eeprom serial
133
    uhd::property_tree::sptr tree = usrp->get_device()->get_tree();
134
    const uhd::fs_path db_path = "/mboards/0/dboards/A/" + xx + "_eeprom";
135
    const uhd::usrp::dboard_eeprom_t db_eeprom = tree->access<uhd::usrp::dboard_eeprom_t>(db_path).get();
136
    if (db_eeprom.serial.empty()) throw std::runtime_error(XX + " dboard has empty serial!");
137

    
138
    //make the calibration file path
139
    fs::path cal_data_path = fs::path(uhd::get_app_path()) / ".uhd";
140
    fs::create_directory(cal_data_path);
141
    cal_data_path = cal_data_path / "cal";
142
    fs::create_directory(cal_data_path);
143
    cal_data_path = cal_data_path / str(boost::format("%s_%s_cal_v0.1_%s.csv") % xx % what % db_eeprom.serial);
144
    if (fs::exists(cal_data_path)){
145
        fs::rename(cal_data_path, cal_data_path.string() + str(boost::format(".%d") % time(NULL)));
146
    }
147

    
148
    //fill the calibration file
149
    std::ofstream cal_data(cal_data_path.string().c_str());
150
    cal_data << boost::format("name, %s Frontend Calibration\n") % XX;
151
    cal_data << boost::format("serial, %s\n") % db_eeprom.serial;
152
    cal_data << boost::format("timestamp, %d\n") % time(NULL);
153
    cal_data << boost::format("version, 0, 1\n");
154
    cal_data << boost::format("DATA STARTS HERE\n");
155
    cal_data << "lo_frequency, correction_real, correction_imag, measured, delta\n";
156

    
157
    for (size_t i = 0; i < results.size(); i++){
158
        cal_data
159
            << results[i].freq << ", "
160
            << results[i].real_corr << ", "
161
            << results[i].imag_corr << ", "
162
            << results[i].best << ", "
163
            << results[i].delta << "\n"
164
        ;
165
    }
166

    
167
    std::cout << "wrote cal data to " << cal_data_path << std::endl;
168
}