Merge branch 'wip/wbxng' of git@gnuradio.org:jcorgan
[debian/gnuradio] / usrp / host / lib / db_wbxng.cc
1 //
2 // Copyright 2008,2009 Free Software Foundation, Inc.
3 //
4 // This file is part of GNU Radio
5 //
6 // GNU Radio is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either asversion 3, or (at your option)
9 // any later version.
10 //
11 // GNU Radio is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with GNU Radio; see the file COPYING.  If not, write to
18 // the Free Software Foundation, Inc., 51 Franklin Street,
19 // Boston, MA 02110-1301, USA.
20
21 #include <usrp/db_wbxng.h>
22 #include "db_wbxng_adf4350.h"
23 #include <db_base_impl.h>
24 #include <stdio.h>
25
26 // d'board i/o pin defs
27 // Tx and Rx have shared defs, but different i/o regs
28 #define ENABLE_5        (1 << 7)         // enables 5.0V power supply
29 #define ENABLE_33       (1 << 6)         // enables 3.3V supply
30 #define RX_TXN          (1 << 5)         // Tx only: T/R antenna switch for TX/RX port
31 #define RX2_RX1N        (1 << 5)         // Rx only: antenna switch between RX2 and TX/RX port
32 #define RXBB_EN         (1 << 4)
33 #define TXMOD_EN        (1 << 4)
34 #define PLL_CE          (1 << 3)
35 #define PLL_PDBRF       (1 << 2)
36 #define PLL_MUXOUT      (1 << 1)
37 #define PLL_LOCK_DETECT (1 << 0)
38
39 // RX Attenuator constants
40 #define ATTN_SHIFT      8
41 #define ATTN_MASK       (63 << ATTN_SHIFT)
42
43 wbxng_base::wbxng_base(usrp_basic_sptr _usrp, int which, int _power_on)
44   : db_base(_usrp, which), d_power_on(_power_on)
45 {
46   /*
47     @param usrp: instance of usrp.source_c
48     @param which: which side: 0 or 1 corresponding to side A or B respectively
49     @type which: int
50   */
51
52   usrp()->_write_oe(d_which, 0, 0xffff);   // turn off all outputs
53
54   d_first = true;
55   d_spi_format = SPI_FMT_MSB | SPI_FMT_HDR_0;
56
57   _enable_refclk(false);                // disable refclk
58
59   set_auto_tr(false);
60 }
61
62 wbxng_base::~wbxng_base()
63 {
64   if (d_common)
65     delete d_common;
66 }
67
68 struct freq_result_t
69 wbxng_base::set_freq(double freq)
70 {
71   /*
72     @returns (ok, actual_baseband_freq) where:
73     ok is True or False and indicates success or failure,
74     actual_baseband_freq is the RF frequency that corresponds to DC in the IF.
75   */
76
77   freq_t int_freq = freq_t(freq);
78   bool ok = d_common->_set_freq(int_freq*2);
79   double freq_result = (double) d_common->_get_freq()/2.0;
80   struct freq_result_t args = {ok, freq_result};
81
82   /* Wait before reading Lock Detect*/
83   timespec t;
84   t.tv_sec = 0;
85   t.tv_nsec = 10000000;
86   nanosleep(&t, NULL);
87
88   fprintf(stderr,"Setting WBXNG frequency, requested %d, obtained %f, lock_detect %d\n",
89           int_freq, freq_result, d_common->_get_locked());
90
91   // FIXME
92   // Offsetting the LO helps get the Tx carrier leakage out of the way.
93   // This also ensures that on Rx, we're not getting hosed by the
94   // FPGA's DC removal loop's time constant.  We were seeing a
95   // problem when running with discontinuous transmission.
96   // Offsetting the LO made the problem go away.
97   //freq += d_lo_offset;
98
99   return args;
100 }
101
102 bool
103 wbxng_base::_set_pga(float pga_gain)
104 {
105   if(d_which == 0) {
106     usrp()->set_pga(0, pga_gain);
107     usrp()->set_pga(1, pga_gain);
108   }
109   else {
110     usrp()->set_pga(2, pga_gain);
111     usrp()->set_pga(3, pga_gain);
112   }
113   return true;
114 }
115
116 bool
117 wbxng_base::is_quadrature()
118 {
119   /*
120     Return True if this board requires both I & Q analog channels.
121
122     This bit of info is useful when setting up the USRP Rx mux register.
123   */
124   return true;
125 }
126
127 double
128 wbxng_base::freq_min()
129 {
130   return (double) d_common->_get_min_freq()/2.0;
131 }
132
133 double
134 wbxng_base::freq_max()
135 {
136   return (double) d_common->_get_max_freq()/2.0;
137 }
138
139 // ----------------------------------------------------------------
140
141 wbxng_base_tx::wbxng_base_tx(usrp_basic_sptr _usrp, int which, int _power_on)
142   : wbxng_base(_usrp, which, _power_on)
143 {
144   /*
145     @param usrp: instance of usrp.sink_c
146     @param which: 0 or 1 corresponding to side TX_A or TX_B respectively.
147   */
148
149   if(which == 0) {
150     d_spi_enable = SPI_ENABLE_TX_A;
151   }
152   else {
153     d_spi_enable = SPI_ENABLE_TX_B;
154   }
155
156   d_common = new adf4350(_usrp, d_which, d_spi_enable);
157
158   // FIXME: power up the transmit side, but don't enable the mixer
159   usrp()->_write_oe(d_which,(RX_TXN|TXMOD_EN|ENABLE_33|ENABLE_5), (RX_TXN|TXMOD_EN|ENABLE_33|ENABLE_5));
160   usrp()->write_io(d_which, (power_on()|RX_TXN|TXMOD_EN|ENABLE_33|ENABLE_5), (RX_TXN|TXMOD_EN|ENABLE_33|ENABLE_5));
161   fprintf(stderr,"Setting WBXNG TXMOD on");
162   //set_lo_offset(4e6);
163
164   set_gain((gain_min() + gain_max()) / 2.0);  // initialize gain
165 }
166
167 wbxng_base_tx::~wbxng_base_tx()
168 {
169   shutdown();
170 }
171
172
173 void
174 wbxng_base_tx::shutdown()
175 {
176   // fprintf(stderr, "wbxng_base_tx::shutdown  d_is_shutdown = %d\n", d_is_shutdown);
177
178   if (!d_is_shutdown){
179     d_is_shutdown = true;
180     // do whatever there is to do to shutdown
181
182     // Power down and leave the T/R switch in the R position
183     usrp()->write_io(d_which, (power_off()|RX_TXN), (RX_TXN|ENABLE_33|ENABLE_5));
184
185     // Power down VCO/PLL
186     d_common->_enable(false);
187
188     /*
189     _write_control(_compute_control_reg());
190     */
191     _enable_refclk(false);                       // turn off refclk
192     set_auto_tr(false);
193   }
194 }
195
196 bool
197 wbxng_base_tx::set_auto_tr(bool on)
198 {
199   bool ok = true;
200   if(on) {
201     ok &= set_atr_mask (RX_TXN | ENABLE_33 | ENABLE_5);
202     ok &= set_atr_txval(0      | ENABLE_33 | ENABLE_5);
203     ok &= set_atr_rxval(RX_TXN | 0);
204   }
205   else {
206     ok &= set_atr_mask (0);
207     ok &= set_atr_txval(0);
208     ok &= set_atr_rxval(0);
209   }
210   return ok;
211 }
212
213 bool
214 wbxng_base_tx::set_enable(bool on)
215 {
216   /*
217     Enable transmitter if on is true
218   */
219
220   int v;
221   int mask = RX_TXN | ENABLE_5 | ENABLE_33;
222   if(on) {
223     v = ENABLE_5 | ENABLE_33;
224   }
225   else {
226     v = RX_TXN;
227   }
228   return usrp()->write_io(d_which, v, mask);
229 }
230
231 float
232 wbxng_base_tx::gain_min()
233 {
234   return usrp()->pga_max();
235 }
236
237 float
238 wbxng_base_tx::gain_max()
239 {
240   return usrp()->pga_max() + 25.0;
241 }
242
243 float
244 wbxng_base_tx::gain_db_per_step()
245 {
246   return 1;
247 }
248
249 bool
250 wbxng_base_tx::set_gain(float gain)
251 {
252   /*
253     Set the gain.
254
255     @param gain:  gain in decibels
256     @returns True/False
257   */
258
259   // clamp gain
260   gain = std::max(gain_min(), std::min(gain, gain_max()));
261
262   float pga_gain, agc_gain;
263   float V_maxgain, V_mingain, V_fullscale, dac_value;
264
265   float maxgain = gain_max() - usrp()->pga_max();
266   float mingain = gain_min();
267   if(gain > maxgain) {
268     pga_gain = gain-maxgain;
269     assert(pga_gain <= usrp()->pga_max());
270     agc_gain = maxgain;
271   }
272   else {
273     pga_gain = 0;
274     agc_gain = gain;
275   }
276
277   V_maxgain = 0.7;
278   V_mingain = 1.4;
279   V_fullscale = 3.3;
280   dac_value = (agc_gain*(V_maxgain-V_mingain)/(maxgain-mingain) + V_mingain)*4096/V_fullscale;
281
282   fprintf(stderr, "TXGAIN: %f dB, Dac Code: %d, Voltage: %f\n", gain, int(dac_value), float((dac_value/4096.0)*V_fullscale));
283   assert(dac_value>=0 && dac_value<4096);
284
285   return (usrp()->write_aux_dac(d_which, 0, int(dac_value))
286           && _set_pga(int(pga_gain)));
287 }
288
289
290 /**************************************************************************/
291
292
293 wbxng_base_rx::wbxng_base_rx(usrp_basic_sptr _usrp, int which, int _power_on)
294   : wbxng_base(_usrp, which, _power_on)
295 {
296   /*
297     @param usrp: instance of usrp.source_c
298     @param which: 0 or 1 corresponding to side RX_A or RX_B respectively.
299   */
300
301   if(which == 0) {
302     d_spi_enable = SPI_ENABLE_RX_A;
303   }
304   else {
305     d_spi_enable = SPI_ENABLE_RX_B;
306   }
307
308   d_common = new adf4350(_usrp, d_which, d_spi_enable);
309
310   usrp()->_write_oe(d_which, (RX2_RX1N|RXBB_EN|ATTN_MASK|ENABLE_33|ENABLE_5), (RX2_RX1N|RXBB_EN|ATTN_MASK|ENABLE_33|ENABLE_5));
311   usrp()->write_io(d_which,  (power_on()|RX2_RX1N|RXBB_EN|ENABLE_33|ENABLE_5), (RX2_RX1N|RXBB_EN|ATTN_MASK|ENABLE_33|ENABLE_5));
312   fprintf(stderr,"Setting WBXNG RXBB on");
313
314   // set up for RX on TX/RX port
315   select_rx_antenna("TX/RX");
316
317   bypass_adc_buffers(true);
318
319   /*
320   set_lo_offset(-4e6);
321   */
322 }
323
324 wbxng_base_rx::~wbxng_base_rx()
325 {
326   shutdown();
327 }
328
329 void
330 wbxng_base_rx::shutdown()
331 {
332   // fprintf(stderr, "wbxng_base_rx::shutdown  d_is_shutdown = %d\n", d_is_shutdown);
333
334   if (!d_is_shutdown){
335     d_is_shutdown = true;
336     // do whatever there is to do to shutdown
337
338     // Power down
339     usrp()->common_write_io(C_RX, d_which, power_off(), (ENABLE_33|ENABLE_5));
340
341     // Power down VCO/PLL
342     d_common->_enable(false);
343
344     // fprintf(stderr, "wbxng_base_rx::shutdown  before _write_control\n");
345     //_write_control(_compute_control_reg());
346
347     // fprintf(stderr, "wbxng_base_rx::shutdown  before _enable_refclk\n");
348     _enable_refclk(false);                       // turn off refclk
349
350     // fprintf(stderr, "wbxng_base_rx::shutdown  before set_auto_tr\n");
351     set_auto_tr(false);
352
353     // fprintf(stderr, "wbxng_base_rx::shutdown  after set_auto_tr\n");
354   }
355 }
356
357 bool
358 wbxng_base_rx::set_auto_tr(bool on)
359 {
360   bool ok = true;
361   if(on) {
362     ok &= set_atr_mask (ENABLE_33|ENABLE_5);
363     ok &= set_atr_txval(     0);
364     ok &= set_atr_rxval(ENABLE_33|ENABLE_5);
365   }
366   else {
367     ok &= set_atr_mask (0);
368     ok &= set_atr_txval(0);
369     ok &= set_atr_rxval(0);
370   }
371   return true;
372 }
373
374 bool
375 wbxng_base_rx::select_rx_antenna(int which_antenna)
376 {
377   /*
378     Specify which antenna port to use for reception.
379     @param which_antenna: either 'TX/RX' or 'RX2'
380   */
381
382   if(which_antenna == 0) {
383     usrp()->write_io(d_which, 0,RX2_RX1N);
384   }
385   else if(which_antenna == 1) {
386     usrp()->write_io(d_which, RX2_RX1N, RX2_RX1N);
387   }
388   else {
389     return false;
390   }
391   return true;
392 }
393
394 bool
395 wbxng_base_rx::select_rx_antenna(const std::string &which_antenna)
396 {
397   /*
398     Specify which antenna port to use for reception.
399     @param which_antenna: either 'TX/RX' or 'RX2'
400   */
401
402
403   if(which_antenna == "TX/RX") {
404     usrp()->write_io(d_which, 0, RX2_RX1N);
405   }
406   else if(which_antenna == "RX2") {
407     usrp()->write_io(d_which, RX2_RX1N, RX2_RX1N);
408   }
409   else {
410     return false;
411   }
412
413   return true;
414 }
415
416 bool
417 wbxng_base_rx::set_gain(float gain)
418 {
419   /*
420     Set the gain.
421
422     @param gain:  gain in decibels
423     @returns True/False
424   */
425
426   // clamp gain
427   gain = std::max(gain_min(), std::min(gain, gain_max()));
428
429   float pga_gain, agc_gain;
430
431   float maxgain = gain_max() - usrp()->pga_max();
432   float mingain = gain_min();
433   if(gain > maxgain) {
434     pga_gain = gain-maxgain;
435     assert(pga_gain <= usrp()->pga_max());
436     agc_gain = maxgain;
437   }
438   else {
439     pga_gain = 0;
440     agc_gain = gain;
441   }
442
443   return _set_attn(maxgain-agc_gain) && _set_pga(int(pga_gain));
444 }
445
446 bool
447 wbxng_base_rx::_set_attn(float attn)
448 {
449   int attn_code = int(floor(attn/0.5));
450   unsigned int iobits = (~attn_code) << ATTN_SHIFT;
451   fprintf(stderr, "Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x \n", attn, attn_code, iobits & ATTN_MASK, ATTN_MASK);
452   return usrp()->write_io(d_which, iobits, ATTN_MASK);
453 }
454
455 // ----------------------------------------------------------------
456
457 db_wbxng_tx::db_wbxng_tx(usrp_basic_sptr usrp, int which)
458   : wbxng_base_tx(usrp, which)
459 {
460 }
461
462 db_wbxng_tx::~db_wbxng_tx()
463 {
464 }
465
466 db_wbxng_rx::db_wbxng_rx(usrp_basic_sptr usrp, int which)
467   : wbxng_base_rx(usrp, which)
468 {
469   set_gain((gain_min() + gain_max()) / 2.0);  // initialize gain
470 }
471
472 db_wbxng_rx::~db_wbxng_rx()
473 {
474 }
475
476 float
477 db_wbxng_rx::gain_min()
478 {
479   return usrp()->pga_min();
480 }
481
482 float
483 db_wbxng_rx::gain_max()
484 {
485   return usrp()->pga_max()+30.5;
486 }
487
488 float
489 db_wbxng_rx::gain_db_per_step()
490 {
491   return 0.05;
492 }
493
494
495 bool
496 db_wbxng_rx::i_and_q_swapped()
497 {
498   return false;
499 }