53ec27afcad9239ce7a7fdf5402d10d2d69d8b13
[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_base_impl.h>
23 #include <cmath>
24 #include <boost/thread.hpp>
25 #include <boost/weak_ptr.hpp>
26 #include <cstdio>
27
28 #if 0
29 #define LO_OFFSET 4.25e6
30 #else
31 #define LO_OFFSET 0
32 #define NO_LO_OFFSET
33 #endif
34
35
36 /* ------------------------------------------------------------------------
37  *  **** TODO ****
38  *    Fix the comment below to reflect WBX NG
39  */
40
41 /* ------------------------------------------------------------------------
42  *  A few comments about the XCVR2450:
43  *
44  * It is half-duplex.  I.e., transmit and receive are mutually exclusive.
45  * There is a single LO for both the Tx and Rx sides.
46  * For our purposes the board is always either receiving or transmitting.
47  *
48  * Each board is uniquely identified by the *USRP hardware* instance and side
49  * This dictionary holds a weak reference to existing board controller so it
50  * can be created or retrieved as needed.
51  */
52
53
54
55 // TX IO Pins
56 #define HB_PA_OFF      (1 << 15)    // 5GHz PA, 1 = off, 0 = on
57 #define LB_PA_OFF      (1 << 14)    // 2.4GHz PA, 1 = off, 0 = on
58 #define ANTSEL_TX1_RX2 (1 << 13)    // 1 = Ant 1 to TX, Ant 2 to RX
59 #define ANTSEL_TX2_RX1 (1 << 12)    // 1 = Ant 2 to TX, Ant 1 to RX
60 #define TX_EN          (1 << 11)    // 1 = TX on, 0 = TX off
61 #define AD9515DIV      (1 << 4)     // 1 = Div  by 3, 0 = Div by 2
62
63 #define TX_OE_MASK HB_PA_OFF|LB_PA_OFF|ANTSEL_TX1_RX2|ANTSEL_TX2_RX1|TX_EN|AD9515DIV
64 #define TX_SAFE_IO HB_PA_OFF|LB_PA_OFF|ANTSEL_TX1_RX2|AD9515DIV
65
66 // RX IO Pins
67 #define LOCKDET (1 << 15)           // This is an INPUT!!!
68 #define EN      (1 << 14)
69 #define RX_EN   (1 << 13)           // 1 = RX on, 0 = RX off
70 #define RX_HP   (1 << 12)
71 #define RX_OE_MASK EN|RX_EN|RX_HP
72 #define RX_SAFE_IO EN
73
74 struct wbxng_key {
75   std::string serial_no;
76   int which;
77
78   bool operator==(const wbxng_key &x){
79     return x.serial_no ==serial_no && x.which == which;
80   }
81 };
82
83 class wbxng
84 {
85 private:
86   usrp_basic *d_raw_usrp;
87   int d_which;
88
89   bool d_is_shutdown;
90   int d_spi_format, d_spi_enable;
91   
92   int d_mimo, d_int_div, d_frac_div, d_highband, d_five_gig;
93   int d_cp_current, d_ref_div, d_rssi_hbw;
94   int d_txlpf_bw, d_rxlpf_bw, d_rxlpf_fine, d_rxvga_ser;
95   int d_rssi_range, d_rssi_mode, d_rssi_mux;
96   int d_rx_hp_pin, d_rx_hpf, d_rx_ant;
97   int d_tx_ant, d_txvga_ser, d_tx_driver_lin;
98   int d_tx_vga_lin, d_tx_upconv_lin, d_tx_bb_gain;
99   int d_pabias_delay, d_pabias, rx_rf_gain, rx_bb_gain, d_txgain;
100   int d_rx_rf_gain, d_rx_bb_gain;
101
102   int d_reg_standby, d_reg_int_divider, d_reg_frac_divider, d_reg_bandselpll;
103   int d_reg_cal, dsend_reg, d_reg_lpf, d_reg_rxrssi_ctrl, d_reg_txlin_gain;
104   int d_reg_pabias, d_reg_rxgain, d_reg_txgain;
105
106   int d_ad9515_div;
107
108   void _set_rfagc(float gain);
109   void _set_ifagc(float gain);
110   void _set_pga(float pga_gain);
111
112 public:
113   usrp_basic *usrp(){
114     return d_raw_usrp;
115   }
116
117   wbxng(usrp_basic_sptr usrp, int which);
118   ~wbxng();
119   void shutdown();
120
121   void set_reg_standby();
122   
123   // Integer-Divider Ratio (3)
124   void set_reg_int_divider();
125   
126   // Fractional-Divider Ratio (4)
127   void set_reg_frac_divider();
128   
129   // Band Select and PLL (5)
130   void set_reg_bandselpll();
131   
132   // Calibration (6)
133   void set_reg_cal();
134
135   // Lowpass Filter (7)
136   void set_reg_lpf();
137   
138   // Rx Control/RSSI (8)
139   void set_reg_rxrssi_ctrl();
140   
141   // Tx Linearity/Baseband Gain (9)
142   void set_reg_txlin_gain();
143   
144   // PA Bias DAC (10)
145   void set_reg_pabias();
146   
147   // Rx Gain (11)
148   void set_reg_rxgain();
149   
150   // Tx Gain (12)
151   void set_reg_txgain();
152   
153   // Send register write to SPI
154   void send_reg(int v);
155
156   void set_gpio();
157   bool lock_detect();
158   bool set_rx_gain(float gain);
159   bool set_tx_gain(float gain);
160
161   struct freq_result_t set_freq(double target_freq);
162 };
163
164
165 /*****************************************************************************/
166
167
168 wbxng::wbxng(usrp_basic_sptr _usrp, int which)
169   : d_raw_usrp(_usrp.get()), d_which(which), d_is_shutdown(false)
170 {
171   // Handler for WBX NG daughterboards.
172   // 
173   // @param usrp: instance of usrp.source_c
174   // @param which: which side: 0, 1 corresponding to RX_A or RX_B respectively
175
176   // Use MSB with no header
177   d_spi_format = SPI_FMT_MSB | SPI_FMT_HDR_0;
178
179   if(which == 0) {
180     d_spi_enable = SPI_ENABLE_RX_A;
181   }
182   else {
183     d_spi_enable = SPI_ENABLE_RX_B;
184   }
185
186   // Sane defaults
187   d_mimo = 1;          // 0 = OFF, 1 = ON
188   d_int_div = 192;     // 128 = min, 255 = max
189   d_frac_div = 0;      // 0 = min, 65535 = max
190   d_highband = 0;      // 0 = freq <= 5.4e9, 1 = freq > 5.4e9
191   d_five_gig = 0;      // 0 = freq <= 3.e9, 1 = freq > 3e9
192   d_cp_current = 1;    // 0 = 2mA, 1 = 4mA
193   d_ref_div = 1;       // 1 to 7
194   d_rssi_hbw = 0;      // 0 = 2 MHz, 1 = 6 MHz
195   d_txlpf_bw = 1;      // 1 = 12 MHz, 2 = 18 MHz, 3 = 24 MHz
196   d_rxlpf_bw = 1;      // 0 = 7.5 MHz, 1 = 9.5 MHz, 2 = 14 MHz, 3 = 18 MHz
197   d_rxlpf_fine = 2;    // 0 = 90%, 1 = 95%, 2 = 100%, 3 = 105%, 4 = 110%
198   d_rxvga_ser = 1;     // 0 = RXVGA controlled by B7:1, 1=controlled serially
199   d_rssi_range = 1;    // 0 = low range (datasheet typo), 1=high range (0.5V - 2.0V) 
200   d_rssi_mode = 1;     // 0 = enable follows RXHP, 1 = enabled
201   d_rssi_mux = 0;      // 0 = RSSI, 1 = TEMP
202   d_rx_hp_pin = 0;     // 0 = Fc set by rx_hpf, 1 = 600 KHz
203   d_rx_hpf = 0;        // 0 = 100Hz, 1 = 30KHz
204   d_rx_ant = 0;        // 0 = Ant. #1, 1 = Ant. #2
205   d_tx_ant = 0;        // 0 = Ant. #1, 1 = Ant. #2
206   d_txvga_ser = 1;     // 0 = TXVGA controlled by B6:1, 1=controlled serially
207   d_tx_driver_lin = 2; // 0=50% (worst linearity), 1=63%, 2=78%, 3=100% (best lin)
208   d_tx_vga_lin = 2;    // 0=50% (worst linearity), 1=63%, 2=78%, 3=100% (best lin)
209   d_tx_upconv_lin = 2; // 0=50% (worst linearity), 1=63%, 2=78%, 3=100% (best lin)
210   d_tx_bb_gain = 3;    // 0=maxgain-5dB, 1=max-3dB, 2=max-1.5dB, 3=max
211   d_pabias_delay = 15; // 0 = 0, 15 = 7uS
212   d_pabias = 0;        // 0 = 0 uA, 63 = 315uA
213   d_rx_rf_gain = 0;    // 0 = 0dB, 1 = 0dB, 2 = 15dB, 3 = 30dB
214   d_rx_bb_gain = 16;   // 0 = min, 31 = max (0 - 62 dB)
215
216   d_txgain = 63;       // 0 = min, 63 = max
217
218   /*
219   // Initialize GPIO and ATR  
220   usrp()->common_write_io(C_TX, d_which, TX_SAFE_IO, TX_OE_MASK);
221   usrp()->_common_write_oe(C_TX, d_which, TX_OE_MASK, 0xffff);
222   usrp()->common_write_atr_txval(C_TX, d_which, TX_SAFE_IO);
223   usrp()->common_write_atr_rxval(C_TX, d_which, TX_SAFE_IO);
224   usrp()->common_write_atr_mask(C_TX, d_which, TX_OE_MASK);
225
226   usrp()->common_write_io(C_RX, d_which, RX_SAFE_IO, RX_OE_MASK);
227   usrp()->_common_write_oe(C_RX, d_which, RX_OE_MASK, 0xffff);
228   usrp()->common_write_atr_txval(C_RX, d_which, RX_SAFE_IO);
229   usrp()->common_write_atr_rxval(C_RX, d_which, RX_SAFE_IO);
230   usrp()->common_write_atr_mask(C_RX, d_which, RX_OE_MASK);
231
232   // Initialize chipset
233   // TODO: perform reset sequence to ensure power up defaults
234   set_reg_standby();
235   set_reg_bandselpll();
236   set_reg_cal();
237   set_reg_lpf();
238   set_reg_rxrssi_ctrl();
239   set_reg_txlin_gain();
240   set_reg_pabias();
241   set_reg_rxgain();
242   set_reg_txgain();
243   //FIXME: set_freq(2.45e9);
244   */
245 }
246
247 wbxng::~wbxng()
248 {
249   //printf("wbxng::destructor\n");
250   shutdown();
251 }
252
253 void
254 wbxng::shutdown()
255 {
256   if (!d_is_shutdown){
257     d_is_shutdown = true;
258     /*
259     usrp()->common_write_atr_txval(C_TX, d_which, TX_SAFE_IO);
260     usrp()->common_write_atr_rxval(C_TX, d_which, TX_SAFE_IO);
261     usrp()->common_write_atr_txval(C_RX, d_which, RX_SAFE_IO);
262     usrp()->common_write_atr_rxval(C_RX, d_which, RX_SAFE_IO);
263     */
264   }
265 }
266
267
268 void
269 wbxng::set_reg_standby()
270 {
271   d_reg_standby = ((d_mimo<<17) | 
272                    (1<<16)      | 
273                    (1<<6)       | 
274                    (1<<5)       | 
275                    (1<<4)       | 2);
276   //send_reg(d_reg_standby);
277 }
278
279 void
280 wbxng::set_reg_int_divider()
281 {
282   d_reg_int_divider = (((d_frac_div & 0x03)<<16) | 
283                        (d_int_div<<4)            | 3);
284   //send_reg(d_reg_int_divider);
285 }
286
287 void
288 wbxng::set_reg_frac_divider()
289 {
290   d_reg_frac_divider = ((d_frac_div & 0xfffc)<<2) | 4;
291   //send_reg(d_reg_frac_divider);
292 }
293         
294 void
295 wbxng::set_reg_bandselpll()
296 {
297   d_reg_bandselpll = ((d_mimo<<17)      |
298                       (1<<16)           |
299                       (1<<15)           |
300                       (0<<11)           |
301                       (d_highband<<10)  |
302                       (d_cp_current<<9) |
303                       (d_ref_div<<5)    |
304                       (d_five_gig<<4)   | 5);
305   //send_reg(d_reg_bandselpll);
306   d_reg_bandselpll = ((d_mimo<<17)      |
307                       (1<<16)           |
308                       (1<<15)           |
309                       (1<<11)           |
310                       (d_highband<<10)  |
311                       (d_cp_current<<9) |
312                       (d_ref_div<<5)    |
313                       (d_five_gig<<4)   | 5);
314   //send_reg(d_reg_bandselpll);
315 }
316      
317 void
318 wbxng::set_reg_cal()
319 {
320   // FIXME do calibration
321   d_reg_cal = (1<<14)|6;
322   //send_reg(d_reg_cal);
323 }
324
325 void
326 wbxng::set_reg_lpf()
327 {
328   d_reg_lpf = (
329              (d_rssi_hbw<<15)  |
330              (d_txlpf_bw<<10)  |
331              (d_rxlpf_bw<<9)   |
332              (d_rxlpf_fine<<4) | 7);
333   //send_reg(d_reg_lpf);
334 }
335
336 void
337 wbxng::set_reg_rxrssi_ctrl()
338 {
339   d_reg_rxrssi_ctrl = ((d_rxvga_ser<<16)  |
340                        (d_rssi_range<<15) |
341                        (d_rssi_mode<<14)  |
342                        (d_rssi_mux<<12)   |
343                        (1<<9)             |
344                        (d_rx_hpf<<6)      |
345                        (1<<4)             | 8);
346   //send_reg(d_reg_rxrssi_ctrl);
347 }
348
349 void
350 wbxng::set_reg_txlin_gain()
351 {
352   d_reg_txlin_gain = ((d_txvga_ser<<14)     |
353                       (d_tx_driver_lin<<12) |
354                       (d_tx_vga_lin<<10)    |
355                       (d_tx_upconv_lin<<6)  |
356                       (d_tx_bb_gain<<4)     | 9);
357   //send_reg(d_reg_txlin_gain);
358 }
359
360 void
361 wbxng::set_reg_pabias()
362 {
363   d_reg_pabias = (
364                   (d_pabias_delay<<10) |
365                   (d_pabias<<4)        | 10);
366   //send_reg(d_reg_pabias);
367 }
368
369 void
370 wbxng::set_reg_rxgain()
371 {
372   d_reg_rxgain = (
373                   (d_rx_rf_gain<<9) |
374                   (d_rx_bb_gain<<4) | 11);
375   //send_reg(d_reg_rxgain);
376 }
377
378 void
379 wbxng::set_reg_txgain()
380 {
381   d_reg_txgain = (d_txgain<<4) | 12;
382   //send_reg(d_reg_txgain);
383 }
384
385 void
386 wbxng::send_reg(int v)
387 {
388   // Send 24 bits, it keeps last 18 clocked in
389   char c[3];
390   c[0] = (char)((v >> 16) & 0xff);
391   c[1] = (char)((v >>  8) & 0xff);
392   c[2] = (char)((v & 0xff));
393   std::string s(c, 3);
394   
395   //usrp()->_write_spi(0, d_spi_enable, d_spi_format, s);
396   //printf("wbxng: Setting reg %d to %X\n", (v&15), v);
397 }
398
399 // ----------------------------------------------------------------
400
401 void
402 wbxng::set_gpio()
403 {
404   // We calculate four values:
405   //
406   // io_rx_while_rx: what to drive onto io_rx_* when receiving
407   // io_rx_while_tx: what to drive onto io_rx_* when transmitting
408   // io_tx_while_rx: what to drive onto io_tx_* when receiving
409   // io_tx_while_tx: what to drive onto io_tx_* when transmitting
410   //
411   // B1-B7 is ignored as gain is set serially for now.
412   
413   int rx_hp, tx_antsel, rx_antsel, tx_pa_sel;
414   if(d_rx_hp_pin)
415     rx_hp = RX_HP;
416   else
417     rx_hp = 0;
418   
419   if(d_tx_ant)
420     tx_antsel = ANTSEL_TX2_RX1;
421   else
422     tx_antsel = ANTSEL_TX1_RX2;
423
424   if(d_rx_ant)
425     rx_antsel = ANTSEL_TX2_RX1;
426   else
427     rx_antsel = ANTSEL_TX1_RX2;
428
429   if(d_five_gig)
430     tx_pa_sel = LB_PA_OFF;
431   else
432     tx_pa_sel = HB_PA_OFF;
433  
434   /*
435   // Reset GPIO and ATR
436   // FIXME: dont set io, oe, atr mask once basic code stops overriding our settings
437   usrp()->common_write_io(C_TX, d_which, TX_SAFE_IO, TX_OE_MASK);
438   usrp()->_common_write_oe(C_TX, d_which, TX_OE_MASK, 0xffff);
439   usrp()->common_write_atr_txval(C_TX, d_which, tx_pa_sel|tx_antsel|TX_EN|AD9515DIV);
440   usrp()->common_write_atr_rxval(C_TX, d_which, HB_PA_OFF|LB_PA_OFF|rx_antsel|AD9515DIV);
441   usrp()->common_write_atr_mask(C_TX, d_which, TX_OE_MASK);
442
443   usrp()->common_write_io(C_RX, d_which, RX_SAFE_IO, RX_OE_MASK);
444   usrp()->_common_write_oe(C_RX, d_which, RX_OE_MASK, 0xffff);
445   usrp()->common_write_atr_txval(C_RX, d_which, EN|rx_hp);
446   usrp()->common_write_atr_rxval(C_RX, d_which, EN|rx_hp|RX_EN);
447   usrp()->common_write_atr_mask(C_RX, d_which, RX_OE_MASK);
448   */
449
450   //printf("GPIO: RXRX=%04X RXTX=%04X TXRX=%04X TXTX=%04X\n",
451   //       io_rx_while_rx, io_rx_while_tx, io_tx_while_rx, io_tx_while_tx);
452 }
453   
454
455 struct freq_result_t
456 wbxng::set_freq(double target_freq)
457 {
458   struct freq_result_t args = {false, 0};
459
460   double scaler;
461
462   if(target_freq > 3e9) {
463     d_five_gig = 1;
464     d_ad9515_div = 3;
465     scaler = 4.0/5.0;
466   }
467   else {
468     d_five_gig = 0;
469     d_ad9515_div = 3;
470     scaler = 4.0/3.0;
471   }
472
473   if(target_freq > 5.408e9) {
474     d_highband = 1;
475   }
476   else {
477     d_highband = 0;
478   }
479
480   double vco_freq = target_freq*scaler;
481   double sys_clk = usrp()->fpga_master_clock_freq();  // Usually 64e6 
482   double ref_clk = sys_clk / d_ad9515_div;
483         
484   double phdet_freq = ref_clk/d_ref_div;
485   double div = vco_freq/phdet_freq;
486   d_int_div = int(floor(div));
487   d_frac_div = int((div-d_int_div)*65536.0);
488   // double actual_freq = phdet_freq*(d_int_div+(d_frac_div/65536.0))/scaler;
489   
490   //printf("RF=%f VCO=%f R=%d PHD=%f DIV=%3.5f I=%3d F=%5d ACT=%f\n",
491   //     target_freq, vco_freq, d_ref_div, phdet_freq,
492   //     div, d_int_div, d_frac_div, actual_freq);
493
494   args.ok = lock_detect();
495
496   /*
497   set_gpio();
498   set_reg_int_divider();
499   set_reg_frac_divider();
500   set_reg_bandselpll();
501
502   args.ok = lock_detect();
503 #ifdef NO_LO_OFFSET
504   args.baseband_freq = target_freq;
505 #else
506   args.baseband_freq = actual_freq;
507 #endif
508   */
509
510   if(!args.ok){
511     printf("Fail %f\n", target_freq);
512   }
513   return args;
514 }
515
516 bool
517 wbxng::lock_detect()
518 {
519   /*
520     @returns: the value of the VCO/PLL lock detect bit.
521     @rtype: 0 or 1
522   */
523   /*
524   if(usrp()->common_read_io(C_RX, d_which) & LOCKDET) {
525     return true;
526   }
527   else {      // Give it a second chance
528     if(usrp()->common_read_io(C_RX, d_which) & LOCKDET)
529       return true;
530     else
531       return false;
532   }
533   */
534   return true;
535 }
536
537 bool
538 wbxng::set_rx_gain(float gain)
539 {
540   if(gain < 0.0) 
541     gain = 0.0;
542   if(gain > 92.0)
543     gain = 92.0;
544
545   // Split the gain between RF and baseband
546   // This is experimental, not prescribed
547   if(gain < 31.0) {
548     d_rx_rf_gain = 0;                      // 0 dB RF gain
549     rx_bb_gain = int(gain/2.0);
550   }
551   
552   if(gain >= 30.0 and gain < 60.5) {
553     d_rx_rf_gain = 2;                    // 15 dB RF gain
554     d_rx_bb_gain = int((gain-15.0)/2.0);
555   }
556   
557   if(gain >= 60.5) {
558     d_rx_rf_gain = 3;                      // 30.5 dB RF gain
559     d_rx_bb_gain = int((gain-30.5)/2.0);
560   }
561   
562   //set_reg_rxgain();
563   
564   return true;
565 }
566
567 bool
568 wbxng::set_tx_gain(float gain)
569 {
570   if(gain < 0.0) {
571     gain = 0.0;
572   }
573   if(gain > 30.0) {
574     gain = 30.0;
575   }
576   
577   d_txgain = int((gain/30.0)*63);
578   //set_reg_txgain();
579
580   return true;
581 }
582
583
584 /*****************************************************************************/
585
586
587 struct wbxng_table_entry {
588   wbxng_key                     key;
589   boost::weak_ptr<wbxng>        value;
590
591   wbxng_table_entry(const wbxng_key &_key, boost::weak_ptr<wbxng> _value)
592     : key(_key), value(_value) {}
593 };
594
595 typedef std::vector<wbxng_table_entry> wbxng_table;
596
597 static boost::mutex s_table_mutex;
598 static wbxng_table s_table;
599
600 static wbxng_sptr
601 _get_or_make_wbxng(usrp_basic_sptr usrp, int which)
602 {
603   wbxng_key key = {usrp->serial_number(), which};
604
605   boost::mutex::scoped_lock     guard(s_table_mutex);
606
607   for (wbxng_table::iterator p = s_table.begin(); p != s_table.end();){
608     if (p->value.expired())     // weak pointer is now dead
609       p = s_table.erase(p);     // erase it
610     else {
611       if (key == p->key){       // found it
612         return wbxng_sptr(p->value);
613       }
614       else                      
615         ++p;                    // keep looking
616     }
617   }
618
619   // We don't have the wbxng we're looking for
620
621   // create a new one and stick it in the table.
622   wbxng_sptr r(new wbxng(usrp, which));
623   wbxng_table_entry t(key, r);
624   s_table.push_back(t);
625
626   return r;
627 }
628
629
630 /*****************************************************************************/
631
632
633 db_wbxng_base::db_wbxng_base(usrp_basic_sptr usrp, int which)
634   : db_base(usrp, which)
635 {
636   /*
637    * Abstract base class for all wbxng boards.
638    * 
639    * Derive board specific subclasses from db_wbxng_base_{tx,rx}
640    *
641    * @param usrp: instance of usrp.source_c
642    * @param which: which side: 0 or 1 corresponding to side A or B respectively
643    * @type which: int
644    */
645   
646   d_wbxng = _get_or_make_wbxng(usrp, which);
647 }
648
649 db_wbxng_base::~db_wbxng_base()
650 {
651 }
652
653 void
654 db_wbxng_base::shutdown_common()
655 {
656   // If the usrp_basic in the wbxng is the same as the usrp_basic
657   // in the daughterboard, shutdown the wbxng now (when only one of Tx
658   // and Rx is open, this is always true).
659
660   if (d_wbxng->usrp() == usrp()){
661     //std::cerr << "db_wbxng_base::shutdown_common: same -> shutting down\n";
662     d_wbxng->shutdown();
663   }
664   else {
665     //std::cerr << "db_wbxng_base::shutdown_common: different -> ignoring\n";
666   }
667 }
668
669 struct freq_result_t
670 db_wbxng_base::set_freq(double target_freq)
671 {
672   /*
673    * @returns (ok, actual_baseband_freq) where:
674    * ok is True or False and indicates success or failure,
675    * actual_baseband_freq is the RF frequency that corresponds to DC in the IF.
676    */
677   return d_wbxng->set_freq(target_freq+d_lo_offset);
678 }
679
680 bool
681 db_wbxng_base::is_quadrature()
682 {
683   /*
684    * Return True if this board requires both I & Q analog channels.
685    *
686    * This bit of info is useful when setting up the USRP Rx mux register.
687    */
688    return true;
689 }
690
691 double
692 db_wbxng_base::freq_min()
693 {
694   return 2.4e9;
695 }
696
697 double
698 db_wbxng_base::freq_max()
699 {
700   return 6.0e9;
701 }
702
703
704 /******************************************************************************/
705
706
707 db_wbxng_tx::db_wbxng_tx(usrp_basic_sptr usrp, int which)
708   : db_wbxng_base(usrp, which)
709 {
710   set_lo_offset(LO_OFFSET);
711   //printf("db_wbxng_tx::db_wbxng_tx\n");
712 }
713
714 db_wbxng_tx::~db_wbxng_tx()
715 {
716   shutdown();
717 }
718
719 void
720 db_wbxng_tx::shutdown()
721 {
722   if (!d_is_shutdown){
723     d_is_shutdown = true;
724     shutdown_common();
725   }
726 }
727
728 float
729 db_wbxng_tx::gain_min()
730 {
731   return 0;
732 }
733
734 float
735 db_wbxng_tx::gain_max()
736 {
737   return 30;
738 }
739
740 float
741 db_wbxng_tx::gain_db_per_step()
742 {
743   return (30.0/63.0);
744 }
745
746 bool
747 db_wbxng_tx::set_gain(float gain)
748 {
749   return d_wbxng->set_tx_gain(gain);
750 }
751
752 bool
753 db_wbxng_tx::i_and_q_swapped()
754 {
755   return true;
756 }
757
758
759 /******************************************************************************/
760
761
762 db_wbxng_rx::db_wbxng_rx(usrp_basic_sptr usrp, int which)
763   : db_wbxng_base(usrp, which)
764 {
765   /*
766    * @param usrp: instance of usrp.source_c
767    * @param which: 0 or 1 corresponding to side RX_A or RX_B respectively.
768    */
769   set_lo_offset(LO_OFFSET);
770   //printf("db_wbxng_rx:d_wbxng_rx\n");
771 }
772
773 db_wbxng_rx::~db_wbxng_rx()
774 {
775   shutdown();
776 }
777
778 void
779 db_wbxng_rx::shutdown()
780 {
781   if (!d_is_shutdown){
782     d_is_shutdown = true;
783     shutdown_common();
784   }
785 }
786
787 float
788 db_wbxng_rx::gain_min()
789 {
790   return 0.0;
791 }
792
793 float
794 db_wbxng_rx::gain_max()
795 {
796   return 92.0;
797 }
798
799 float
800 db_wbxng_rx::gain_db_per_step()
801 {
802   return 1;
803 }
804
805 bool
806 db_wbxng_rx::set_gain(float gain)
807 {
808   return d_wbxng->set_rx_gain(gain);
809 }