Statistics
| Branch: | Tag: | Revision:

root / host / lib / usrp / dboard / db_sbx_common.cpp @ 4b9d692f

History | View | Annotate | Download (15.5 KB)

1
//
2
// Copyright 2011 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 "db_sbx_common.hpp"
19
#include "adf4350_regs.hpp"
20

    
21
using namespace uhd;
22
using namespace uhd::usrp;
23
using namespace boost::assign;
24

    
25

    
26
/***********************************************************************
27
 * Register the SBX dboard (min freq, max freq, rx div2, tx div2)
28
 **********************************************************************/
29
static dboard_base::sptr make_sbx(dboard_base::ctor_args_t args){
30
    return dboard_base::sptr(new sbx_xcvr(args));
31
}
32

    
33
UHD_STATIC_BLOCK(reg_sbx_dboards){
34
    dboard_manager::register_dboard(0x0054, 0x0055, &make_sbx, "SBX");
35
    dboard_manager::register_dboard(0x0065, 0x0064, &make_sbx, "SBX v4");
36
}
37

    
38

    
39
/***********************************************************************
40
 * Gain Handling
41
 **********************************************************************/
42
static int rx_pga0_gain_to_iobits(double &gain){
43
    //clip the input
44
    gain = sbx_rx_gain_ranges["PGA0"].clip(gain);
45

    
46
    //convert to attenuation and update iobits for atr
47
    double attn = sbx_rx_gain_ranges["PGA0"].stop() - gain;
48

    
49
    //calculate the RX attenuation
50
    int attn_code = int(floor(attn*2));
51
    int iobits = ((~attn_code) << RX_ATTN_SHIFT) & RX_ATTN_MASK;
52

    
53
    UHD_LOGV(often) << boost::format(
54
        "SBX TX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x"
55
    ) % attn % attn_code % (iobits & RX_ATTN_MASK) % RX_ATTN_MASK << std::endl;
56

    
57
    //the actual gain setting
58
    gain = sbx_rx_gain_ranges["PGA0"].stop() - double(attn_code)/2;
59

    
60
    return iobits;
61
}
62

    
63
static int tx_pga0_gain_to_iobits(double &gain){
64
    //clip the input
65
    gain = sbx_tx_gain_ranges["PGA0"].clip(gain);
66

    
67
    //convert to attenuation and update iobits for atr
68
    double attn = sbx_tx_gain_ranges["PGA0"].stop() - gain;
69

    
70
    //calculate the TX attenuation
71
    int attn_code = int(floor(attn*2));
72
    int iobits = ((~attn_code) << TX_ATTN_SHIFT) & TX_ATTN_MASK;
73

    
74
    UHD_LOGV(often) << boost::format(
75
        "SBX TX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x"
76
    ) % attn % attn_code % (iobits & TX_ATTN_MASK) % TX_ATTN_MASK << std::endl;
77

    
78
    //the actual gain setting
79
    gain = sbx_tx_gain_ranges["PGA0"].stop() - double(attn_code)/2;
80

    
81
    return iobits;
82
}
83

    
84
double sbx_xcvr::set_tx_gain(double gain, const std::string &name){
85
    assert_has(sbx_tx_gain_ranges.keys(), name, "sbx tx gain name");
86
    if(name == "PGA0"){
87
        tx_pga0_gain_to_iobits(gain);
88
        _tx_gains[name] = gain;
89

    
90
        //write the new gain to atr regs
91
        update_atr();
92
    }
93
    else UHD_THROW_INVALID_CODE_PATH();
94
    return _tx_gains[name];
95
}
96

    
97
double sbx_xcvr::set_rx_gain(double gain, const std::string &name){
98
    assert_has(sbx_rx_gain_ranges.keys(), name, "sbx rx gain name");
99
    if(name == "PGA0"){
100
        rx_pga0_gain_to_iobits(gain);
101
        _rx_gains[name] = gain;
102

    
103
        //write the new gain to atr regs
104
        update_atr();
105
    }
106
    else UHD_THROW_INVALID_CODE_PATH();
107
    return _rx_gains[name];
108
}
109

    
110

    
111
/***********************************************************************
112
 * Structors
113
 **********************************************************************/
114
sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){
115
    switch(get_rx_id().to_uint16()) {
116
        case 0x054:
117
            db_actual = sbx_versionx_sptr(new sbx_version3(this));
118
            break;
119
        case 0x065:
120
            db_actual = sbx_versionx_sptr(new sbx_version4(this));
121
            break;
122
        default:
123
            /* We didn't recognize the version of the board... */
124
            UHD_THROW_INVALID_CODE_PATH();
125
    }
126

    
127
    ////////////////////////////////////////////////////////////////////
128
    // Register RX properties
129
    ////////////////////////////////////////////////////////////////////
130
    this->get_rx_subtree()->create<std::string>("name").set("SBX RX");
131
    this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked")
132
        .publish(boost::bind(&sbx_xcvr::get_locked, this, dboard_iface::UNIT_RX));
133
    BOOST_FOREACH(const std::string &name, sbx_rx_gain_ranges.keys()){
134
        this->get_rx_subtree()->create<double>("gains/"+name+"/value")
135
            .coerce(boost::bind(&sbx_xcvr::set_rx_gain, this, _1, name))
136
            .set(sbx_rx_gain_ranges[name].start());
137
        this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range")
138
            .set(sbx_rx_gain_ranges[name]);
139
    }
140
    this->get_rx_subtree()->create<double>("freq/value")
141
        .coerce(boost::bind(&sbx_xcvr::set_lo_freq, this, dboard_iface::UNIT_RX, _1))
142
        .set((sbx_freq_range.start() + sbx_freq_range.stop())/2.0);
143
    this->get_rx_subtree()->create<meta_range_t>("freq/range").set(sbx_freq_range);
144
    this->get_rx_subtree()->create<std::string>("antenna/value")
145
        .subscribe(boost::bind(&sbx_xcvr::set_rx_ant, this, _1))
146
        .set("RX2");
147
    this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options")
148
        .set(sbx_rx_antennas);
149
    this->get_rx_subtree()->create<std::string>("connection").set("IQ");
150
    this->get_rx_subtree()->create<bool>("enabled").set(true); //always enabled
151
    this->get_rx_subtree()->create<bool>("use_lo_offset").set(false);
152
    this->get_rx_subtree()->create<double>("bandwidth/value").set(2*20.0e6); //20MHz low-pass, we want complex double-sided
153
    this->get_rx_subtree()->create<meta_range_t>("bandwidth/range")
154
        .set(freq_range_t(2*20.0e6, 2*20.0e6));
155

    
156
    ////////////////////////////////////////////////////////////////////
157
    // Register TX properties
158
    ////////////////////////////////////////////////////////////////////
159
    this->get_tx_subtree()->create<std::string>("name").set("SBX TX");
160
    this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked")
161
        .publish(boost::bind(&sbx_xcvr::get_locked, this, dboard_iface::UNIT_TX));
162
    BOOST_FOREACH(const std::string &name, sbx_tx_gain_ranges.keys()){
163
        this->get_tx_subtree()->create<double>("gains/"+name+"/value")
164
            .coerce(boost::bind(&sbx_xcvr::set_tx_gain, this, _1, name))
165
            .set(sbx_tx_gain_ranges[name].start());
166
        this->get_tx_subtree()->create<meta_range_t>("gains/"+name+"/range")
167
            .set(sbx_tx_gain_ranges[name]);
168
    }
169
    this->get_tx_subtree()->create<double>("freq/value")
170
        .coerce(boost::bind(&sbx_xcvr::set_lo_freq, this, dboard_iface::UNIT_TX, _1))
171
        .set((sbx_freq_range.start() + sbx_freq_range.stop())/2.0);
172
    this->get_tx_subtree()->create<meta_range_t>("freq/range").set(sbx_freq_range);
173
    this->get_tx_subtree()->create<std::string>("antenna/value")
174
        .subscribe(boost::bind(&sbx_xcvr::set_tx_ant, this, _1))
175
        .set(sbx_tx_antennas.at(0));
176
    this->get_tx_subtree()->create<std::vector<std::string> >("antenna/options")
177
        .set(sbx_tx_antennas);
178
    this->get_tx_subtree()->create<std::string>("connection").set("QI");
179
    this->get_tx_subtree()->create<bool>("enabled").set(true); //always enabled
180
    this->get_tx_subtree()->create<bool>("use_lo_offset").set(false);
181
    this->get_tx_subtree()->create<double>("bandwidth/value").set(2*20.0e6); //20MHz low-pass, we want complex double-sided
182
    this->get_tx_subtree()->create<meta_range_t>("bandwidth/range")
183
        .set(freq_range_t(2*20.0e6, 2*20.0e6));
184

    
185
    //enable the clocks that we need
186
    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true);
187
    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true);
188

    
189
    //set the gpio directions and atr controls (identically)
190
    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO));
191
    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO));
192
    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO));
193
    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO));
194

    
195
    //flash LEDs
196
    flash_leds();
197

    
198
    UHD_LOGV(often) << boost::format(
199
        "SBX GPIO Direction: RX: 0x%08x, TX: 0x%08x"
200
    ) % RXIO_MASK % TXIO_MASK << std::endl;
201
}
202

    
203
sbx_xcvr::~sbx_xcvr(void){
204
    /* NOP */
205
}
206

    
207
/***********************************************************************
208
 * Antenna Handling
209
 **********************************************************************/
210
void sbx_xcvr::update_atr(void){
211
    //calculate atr pins
212
    int rx_pga0_iobits = rx_pga0_gain_to_iobits(_rx_gains["PGA0"]);
213
    int tx_pga0_iobits = tx_pga0_gain_to_iobits(_tx_gains["PGA0"]);
214
    int rx_lo_lpf_en = (_rx_lo_freq == sbx_enable_rx_lo_filter.clip(_rx_lo_freq)) ? LO_LPF_EN : 0;
215
    int tx_lo_lpf_en = (_tx_lo_freq == sbx_enable_tx_lo_filter.clip(_tx_lo_freq)) ? LO_LPF_EN : 0;
216
    int rx_ld_led = get_locked(dboard_iface::UNIT_RX).to_bool() ? 0 : RX_LED_LD;
217
    int tx_ld_led = get_locked(dboard_iface::UNIT_TX).to_bool() ? 0 : TX_LED_LD;
218
    int rx_ant_led = _rx_ant == "TX/RX" ? RX_LED_RX1RX2 : 0;
219
    int tx_ant_led = _tx_ant == "TX/RX" ? 0 : TX_LED_TXRX;
220

    
221
    //setup the tx atr (this does not change with antenna)
222
    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE,
223
        tx_pga0_iobits | tx_lo_lpf_en | tx_ld_led | tx_ant_led | TX_POWER_UP | ANT_XX | TX_MIXER_DIS);
224

    
225
    //setup the rx atr (this does not change with antenna)
226
    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE,
227
        rx_pga0_iobits | rx_lo_lpf_en | rx_ld_led | rx_ant_led | RX_POWER_UP | ANT_XX | RX_MIXER_DIS);
228

    
229
    //set the RX atr regs that change with antenna setting
230
    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY,
231
        rx_pga0_iobits | rx_lo_lpf_en | rx_ld_led | rx_ant_led | RX_POWER_UP | RX_MIXER_ENB | 
232
            ((_rx_ant != "RX2")? ANT_TXRX : ANT_RX2));
233
    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,
234
        rx_pga0_iobits | rx_lo_lpf_en | rx_ld_led | rx_ant_led | RX_POWER_UP | RX_MIXER_DIS |
235
            ((_rx_ant == "CAL")? ANT_TXRX : ANT_RX2));
236
    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX,
237
        rx_pga0_iobits | rx_lo_lpf_en | rx_ld_led | rx_ant_led | RX_POWER_UP | RX_MIXER_ENB |
238
            ((_rx_ant == "CAL")? ANT_TXRX : ANT_RX2));
239

    
240
    //set the TX atr regs that change with antenna setting
241
    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY,
242
        tx_pga0_iobits | tx_lo_lpf_en | tx_ld_led | tx_ant_led | TX_POWER_UP | TX_MIXER_DIS |
243
            ((_rx_ant != "RX2")? ANT_RX : ANT_TX));
244
    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,
245
        tx_pga0_iobits | tx_lo_lpf_en | tx_ld_led | tx_ant_led | TX_POWER_UP | TX_MIXER_ENB |
246
            ((_tx_ant == "CAL")? ANT_RX : ANT_TX));
247
    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX,
248
        tx_pga0_iobits | tx_lo_lpf_en | tx_ld_led | tx_ant_led | TX_POWER_UP | TX_MIXER_ENB |
249
            ((_tx_ant == "CAL")? ANT_RX : ANT_TX));
250
}
251

    
252
void sbx_xcvr::set_rx_ant(const std::string &ant){
253
    //validate input
254
    assert_has(sbx_rx_antennas, ant, "sbx rx antenna name");
255

    
256
    //shadow the setting
257
    _rx_ant = ant;
258

    
259
    //write the new antenna setting to atr regs
260
    update_atr();
261
}
262

    
263
void sbx_xcvr::set_tx_ant(const std::string &ant){
264
    assert_has(sbx_tx_antennas, ant, "sbx tx antenna name");
265

    
266
    //shadow the setting
267
    _tx_ant = ant;
268

    
269
    //write the new antenna setting to atr regs
270
    update_atr();
271
}
272

    
273
/***********************************************************************
274
 * Tuning
275
 **********************************************************************/
276
void sbx_xcvr::set_rx_lo_freq(double freq){
277
    _rx_lo_freq = db_actual->set_lo_freq(dboard_iface::UNIT_RX, freq);
278
}
279

    
280
void sbx_xcvr::set_tx_lo_freq(double freq){
281
    _tx_lo_freq = db_actual->set_lo_freq(dboard_iface::UNIT_TX, freq);
282
}
283

    
284
double sbx_xcvr::set_lo_freq(dboard_iface::unit_t unit, double target_freq) {
285
    return db_actual->set_lo_freq(unit, target_freq);
286
}
287

    
288
sensor_value_t sbx_xcvr::get_locked(dboard_iface::unit_t unit) {
289
    const bool locked = (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0;
290
    return sensor_value_t("LO", locked, "locked", "unlocked");
291
}
292

    
293

    
294
void sbx_xcvr::flash_leds(void) {
295
    //Remove LED gpios from ATR control temporarily and set to outputs
296
    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, TXIO_MASK);
297
    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXIO_MASK);
298
    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|RX_LED_IO));
299
    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO));
300

    
301
    /*
302
    //flash All LEDs
303
    for (int i = 0; i < 3; i++) {
304
        this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_IO, RX_LED_IO);
305
        this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_IO, TX_LED_IO);
306

307
        boost::this_thread::sleep(boost::posix_time::milliseconds(100));
308

309
        this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0, RX_LED_IO);
310
        this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0, TX_LED_IO);
311

312
        boost::this_thread::sleep(boost::posix_time::milliseconds(100));
313
    }
314
    */
315

    
316
    this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_LD, TX_LED_IO);
317
    boost::this_thread::sleep(boost::posix_time::milliseconds(100));
318

    
319
    this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_TXRX|TX_LED_LD, TX_LED_IO);
320
    boost::this_thread::sleep(boost::posix_time::milliseconds(100));
321

    
322
    this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_LD, RX_LED_IO);
323
    boost::this_thread::sleep(boost::posix_time::milliseconds(100));
324

    
325
    this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_RX1RX2|RX_LED_LD, RX_LED_IO);
326
    boost::this_thread::sleep(boost::posix_time::milliseconds(100));
327

    
328
    this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_LD, RX_LED_IO);
329
    boost::this_thread::sleep(boost::posix_time::milliseconds(100));
330

    
331
    this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0, RX_LED_IO);
332
    boost::this_thread::sleep(boost::posix_time::milliseconds(100));
333

    
334
    this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_LD, TX_LED_IO);
335
    boost::this_thread::sleep(boost::posix_time::milliseconds(100));
336

    
337
    this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0, TX_LED_IO);
338
    boost::this_thread::sleep(boost::posix_time::milliseconds(100));
339

    
340
    /*
341
    //flash All LEDs
342
    for (int i = 0; i < 3; i++) {
343
        this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0, RX_LED_IO);
344
        this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0, TX_LED_IO);
345

346
        boost::this_thread::sleep(boost::posix_time::milliseconds(100));
347

348
        this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_IO, RX_LED_IO);
349
        this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_IO, TX_LED_IO);
350

351
        boost::this_thread::sleep(boost::posix_time::milliseconds(100));
352
    }
353
    */
354
    //Put LED gpios back in ATR control and update atr
355
    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO));
356
    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO));
357
    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO));
358
    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO));
359
}
360