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