Merged r11377:11390 from jcorgan/usrp-headers in to trunk.
[debian/gnuradio] / usrp / host / lib / limbo / db_wbx.cc
1 /* -*- c++ -*- */
2 //
3 // Copyright 2008 Free Software Foundation, Inc.
4 // 
5 // This file is part of GNU Radio
6 // 
7 // GNU Radio is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either asversion 3, or (at your option)
10 // any later version.
11 // 
12 // GNU Radio is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 // GNU General Public License for more details.
16 // 
17 // You should have received a copy of the GNU General Public License
18 // along with GNU Radio; see the file COPYING.  If not, write to
19 // the Free Software Foundation, Inc., 51 Franklin Street,
20 // Boston, MA 02110-1301, USA.
21
22 #include <db_wbx.h>
23 #include <fpga_regs_standard.h>
24 #include <fpga_regs_common.h>
25 #include <usrp_prims.h>
26 #include <usrp_spi_defs.h>
27 #include <stdexcept>
28 #include <cmath>
29
30 // d'board i/o pin defs
31
32 // TX IO Pins
33 #define TX_POWER        (1 << 0)  // TX Side Power
34 #define RX_TXN          (1 << 1)  // T/R antenna switch for TX/RX port
35 #define TX_ENB_MIX      (1 << 2)  // Enable IQ mixer
36 #define TX_ENB_VGA      (1 << 3)
37
38 // RX IO Pins
39 #define RX2_RX1N        (1 << 0)  // antenna switch between RX2 and TX/RX port
40 #define RXENABLE        (1 << 1)  // enables mixer
41 #define PLL_LOCK_DETECT (1 << 2)  // Muxout pin from PLL -- MUST BE INPUT
42 #define MReset          (1 << 3)  // NB6L239 Master Reset, asserted low
43 #define SELA0           (1 << 4)  // NB6L239 SelA0
44 #define SELA1           (1 << 5)  // NB6L239 SelA1
45 #define SELB0           (1 << 6)  // NB6L239 SelB0
46 #define SELB1           (1 << 7)  // NB6L239 SelB1
47 #define PLL_ENABLE      (1 << 8)  // CE Pin on PLL
48 #define AUX_SCLK        (1 << 9)  // ALT SPI SCLK
49 #define AUX_SDO         (1 << 10) // ALT SPI SDO
50 #define AUX_SEN         (1 << 11) // ALT SPI SEN
51
52
53 wbx_base::wbx_base(usrp_basic_sptr usrp, int which)
54   : db_base(usrp, which)
55 {
56   /*
57    * @param usrp: instance of usrp.source_c
58    * @param which: which side: 0 or 1 corresponding to side A or B respectively
59    * @type which: int
60    */
61
62   d_first = true;
63   d_spi_format = SPI_FMT_MSB | SPI_FMT_HDR_0;
64
65   // FIXME -- the write reg functions don't work with 0xffff for masks
66   _rx_write_oe(int(PLL_ENABLE|MReset|SELA0|SELA1|SELB0|SELB1|RX2_RX1N|RXENABLE), 0x7fff);
67   _rx_write_io((PLL_ENABLE|MReset|0|RXENABLE), (PLL_ENABLE|MReset|RX2_RX1N|RXENABLE));
68
69   _tx_write_oe((TX_POWER|RX_TXN|TX_ENB_MIX|TX_ENB_VGA), 0x7fff);
70   _tx_write_io((0|RX_TXN), (TX_POWER|RX_TXN|TX_ENB_MIX|TX_ENB_VGA));  // TX off, TR switch set to RX
71
72   if(d_which == 0) {
73     d_spi_enable = SPI_ENABLE_RX_A;
74   }
75   else {
76     d_spi_enable = SPI_ENABLE_RX_B;
77   }
78
79   set_auto_tr(false);
80         
81 }
82
83 wbx_base::~wbx_base()
84 {        
85   shutdown();
86 }
87
88
89 void
90 wbx_base::shutdown()
91 {
92   if (!d_is_shutdown){
93     d_is_shutdown = true;
94     // do whatever there is to do to shutdown
95
96     write_io(d_which, d_power_off, POWER_UP);   // turn off power to board
97     _write_oe(d_which, 0, 0xffff);   // turn off all outputs
98     set_auto_tr(false); // disable auto transmit
99   }
100 }
101
102 bool
103 wbx_base::_lock_detect()
104 {
105   /*
106    * @returns: the value of the VCO/PLL lock detect bit.
107    * @rtype: 0 or 1
108    */
109
110   if(_rx_read_io() & PLL_LOCK_DETECT) {
111     return true;
112   }
113   else {     // Give it a second chance
114     if(_rx_read_io() & PLL_LOCK_DETECT) {
115       return true;
116     }
117     else {
118       return false;
119     }
120   }
121 }
122
123 bool 
124 wbx_base::_tx_write_oe(int value, int mask)
125 {
126   int reg = (d_which == 0 ? FR_OE_0 : FR_OE_2);
127   return d_usrp->_write_fpga_reg(reg, ((mask & 0xffff) << 16) | (value & 0xffff));
128 }
129
130 bool 
131 wbx_base::_rx_write_oe(int value, int mask)
132 {
133   int reg = (d_which == 0 ? FR_OE_1 : FR_OE_3);
134   return d_usrp->_write_fpga_reg(reg, ((mask & 0xffff) << 16) | (value & 0xffff));
135 }
136
137 bool
138 wbx_base::_tx_write_io(int value, int mask)
139 {
140   int reg = (d_which == 0 ? FR_IO_0 : FR_IO_2);
141   return d_usrp->_write_fpga_reg(reg, ((mask & 0xffff) << 16) | (value & 0xffff));
142 }
143
144 bool
145 wbx_base::_rx_write_io(int value, int mask)
146 {
147   int reg = (d_which == 0 ? FR_IO_1 : FR_IO_3);
148   return d_usrp->_write_fpga_reg(reg, ((mask & 0xffff) << 16) | (value & 0xffff));
149 }
150
151 bool
152 wbx_base::_rx_read_io()
153 {
154   int reg = (d_which == 0 ? FR_RB_IO_RX_A_IO_TX_A : FR_RB_IO_RX_B_IO_TX_B);
155   int t = d_usrp->_read_fpga_reg(reg);
156   return (t >> 16) & 0xffff;
157 }
158
159 bool
160 wbx_base::_tx_read_io()
161 {
162   int reg = (d_which == 0 ? FR_RB_IO_RX_A_IO_TX_A : FR_RB_IO_RX_B_IO_TX_B);
163   int t = d_usrp->_read_fpga_reg(reg);
164   return (t & 0xffff);
165 }
166
167 bool
168 wbx_base::_compute_regs(double freq)
169 {
170   /*
171    * Determine values of registers, along with actual freq.
172    * 
173    * @param freq: target frequency in Hz
174    * @type freq: double
175    * @returns: (R, N, func, init, actual_freq)
176    * @rtype: tuple(int, int, int, int, double)
177    * 
178    * Override this in derived classes.
179    */
180   throw std::runtime_error("_compute_regs called from base class\n");
181 }
182
183 double
184 wbx_base::_refclk_freq()
185 {
186   return (double)(d_usrp->fpga_master_clock_freq())/_refclk_divisor();
187 }
188
189 int
190 wbx_base::_refclk_divisor()
191 {
192   /*
193    * Return value to stick in REFCLK_DIVISOR register
194    */
195   return 1;
196 }
197
198 struct freq_result_t
199 wbx_base::set_freq(double freq)
200 {
201   /*
202    * @returns (ok, actual_baseband_freq) where:
203    * ok is True or False and indicates success or failure,
204    * actual_baseband_freq is the RF frequency that corresponds to DC in the IF.
205    */
206   throw std::runtime_error("set_freq called from base class\n");
207 }
208
209 float
210 wbx_base::gain_min()
211 {
212   throw std::runtime_error("gain_min called from base class\n");
213 }
214
215 float
216 wbx_base::gain_max()
217 {
218   throw std::runtime_error("gain_max called from base class\n");
219 }
220
221 float
222 wbx_base::gain_db_per_step()
223 {
224   throw std::runtime_error("gain_db_per_step called from base class\n");
225 }
226
227 bool
228 wbx_base::set_gain(float gain)
229 {
230   /*
231    * Set the gain.
232    * 
233    * @param gain:  gain in decibels
234    * @returns True/False
235    */
236   throw std::runtime_error("set_gain called from base class\n");
237 }
238
239 bool
240 wbx_base::_set_pga(float pga_gain)
241 {
242   bool ok;
243   if(d_which == 0) {
244     ok  = d_usrp->set_pga(0, pga_gain);
245     ok |= d_usrp->set_pga(1, pga_gain);
246   }
247   else {
248     ok  = d_usrp->set_pga(2, pga_gain);
249     ok |= d_usrp->set_pga(3, pga_gain);
250   }
251   return ok;
252 }
253
254 bool
255 wbx_base::is_quadrature()
256 {
257   /*
258    * Return True if this board requires both I & Q analog channels.
259    * 
260    * This bit of info is useful when setting up the USRP Rx mux register.
261    */
262   return true;
263 }
264
265
266 /****************************************************************************/
267
268
269 wbx_base_tx::wbx_base_tx(usrp_basic_sptr usrp, int which)
270   : wbx_base(usrp, which)
271 {        
272   /*
273    * @param usrp: instance of usrp.sink_c
274    * @param which: 0 or 1 corresponding to side TX_A or TX_B respectively.
275    */
276   
277   // power up the transmit side, NO -- but set antenna to receive
278   d_usrp->write_io(d_which, (TX_POWER), (TX_POWER|RX_TXN));
279   d_lo_offset = 0e6;
280   
281   // Gain is not set by the PGA, but the PGA must be set at max gain in the TX
282   _set_pga(d_usrp->pga_max());
283 }
284
285 wbx_base_tx::~wbx_base_tx()
286 {
287   // Power down and leave the T/R switch in the R position
288   d_usrp->write_io(d_which, (RX_TXN), (TX_POWER|RX_TXN|TX_ENB_MIX|TX_ENB_VGA));
289 }
290
291 void
292 wbx_base_tx::set_auto_tr(bool on)
293 {
294   if(on) {
295     set_atr_mask (RX_TXN);
296     set_atr_txval(0);
297     set_atr_rxval(RX_TXN);
298   }
299   else {
300     set_atr_mask (0);
301     set_atr_txval(0);
302     set_atr_rxval(0);
303   }
304 }
305
306 void
307 wbx_base_tx::set_enable(bool on)
308 {
309   /*
310    * Enable transmitter if on is True
311    */
312
313   int mask = RX_TXN|TX_ENB_MIX|TX_ENB_VGA;
314   //printf("HERE!!!!\n");
315   if(on) {
316     d_usrp->write_io(d_which, TX_ENB_MIX|TX_ENB_VGA, mask);
317   }
318   else {
319     d_usrp->write_io(d_which, RX_TXN, mask);
320   }
321 }
322
323 void
324 wbx_base_tx::set_lo_offset(double offset)
325 {
326   /*
327    * Set amount by which LO is offset from requested tuning frequency.
328    * 
329    * @param offset: offset in Hz
330    */
331   
332   d_lo_offset = offset;
333 }
334
335 double
336 wbx_base_tx::lo_offset()
337 {
338   /*
339    * Get amount by which LO is offset from requested tuning frequency.
340    * 
341    * @returns Offset in Hz
342    */
343
344   return d_lo_offset;
345 }
346
347
348 /****************************************************************************/
349
350
351 wbx_base_rx::wbx_base_rx(usrp_basic_sptr usrp, int which)
352   : wbx_base(usrp, which)
353 {
354   /*
355    * @param usrp: instance of usrp.source_c
356    * @param which: 0 or 1 corresponding to side RX_A or RX_B respectively.
357    */
358   
359   // set up for RX on TX/RX port
360   select_rx_antenna("TX/RX");
361   
362   bypass_adc_buffers(true);
363
364   d_lo_offset = 0.0;
365 }
366
367 wbx_base_rx::~wbx_base_rx()
368 {
369   // Power down
370   d_usrp->write_io(d_which, 0, (RXENABLE));
371 }
372   
373 void
374 wbx_base_rx::set_auto_tr(bool on)
375 {
376   if(on) {
377     // FIXME: where does ENABLE come from?
378     //set_atr_mask (ENABLE);
379     set_atr_txval(     0);
380     //set_atr_rxval(ENABLE);
381   }
382   else {
383     set_atr_mask (0);
384     set_atr_txval(0);
385     set_atr_rxval(0);
386   }
387 }
388
389 void
390 wbx_base_rx::select_rx_antenna(int which_antenna)
391 {
392   /*
393    * Specify which antenna port to use for reception.
394    * @param which_antenna: either 'TX/RX' or 'RX2'
395    */
396   
397   if(which_antenna == 0) {
398     d_usrp->write_io(d_which, 0,        RX2_RX1N);
399   }
400   else if(which_antenna == 1) {
401     d_usrp->write_io(d_which, RX2_RX1N, RX2_RX1N);
402   }
403   else {
404     throw std::invalid_argument("which_antenna must be either 'TX/RX' or 'RX2'\n");
405   }
406 }
407
408 void
409 wbx_base_rx::select_rx_antenna(const std::string &which_antenna)
410 {
411   if(which_antenna == "TX/RX") {
412     select_rx_antenna(0);
413   }
414   else if(which_antenna == "RX2") {
415     select_rx_antenna(1);
416   }
417   else {
418     throw std::invalid_argument("which_antenna must be either 'TX/RX' or 'RX2'\n");
419   }
420 }
421
422 bool
423 wbx_base_rx::set_gain(float gain)
424 {
425   /*
426    * Set the gain.
427    * 
428    * @param gain:  gain in decibels
429    * @returns True/False
430    */
431   
432   float pga_gain, agc_gain;
433   float maxgain = gain_max() - d_usrp->pga_max();
434   float mingain = gain_min();
435   if(gain > maxgain) {
436     pga_gain = gain-maxgain;
437     assert(pga_gain <= d_usrp->pga_max());
438     agc_gain = maxgain;
439   }
440   else {
441     pga_gain = 0;
442     agc_gain = gain;
443   }
444    
445   float V_maxgain = .2;
446   float V_mingain = 1.2;
447   float V_fullscale = 3.3;
448   float dac_value = (agc_gain*(V_maxgain-V_mingain)/(maxgain-mingain) + V_mingain)*4096/V_fullscale;
449
450   assert(dac_value>=0 && dac_value<4096);
451
452   return d_usrp->write_aux_dac(d_which, 0, (int)(dac_value)) && _set_pga((int)(pga_gain));
453 }
454
455 void
456 wbx_base_rx::set_lo_offset(double offset)
457 {
458   /*
459    * Set amount by which LO is offset from requested tuning frequency.
460    * 
461    * @param offset: offset in Hz
462    */
463   d_lo_offset = offset;
464 }
465
466 double
467 wbx_base_rx::lo_offset()
468 {
469   /*
470    * Get amount by which LO is offset from requested tuning frequency.
471    * 
472    * @returns Offset in Hz
473    */
474   return d_lo_offset;
475 }
476
477 bool
478 wbx_base_rx::i_and_q_swapped()
479 {
480   /*
481    * Return True if this is a quadrature device and ADC 0 is Q.
482    */
483   return false;
484 }
485
486
487 /****************************************************************************/
488
489 _ADF410X_common::_ADF410X_common()
490 {
491   // R-Register Common Values
492   d_R_RSV = 0;    // bits 23,22,21
493   d_LDP = 1;      // bit 20     Lock detect in 5 cycles
494   d_TEST = 0;     // bit 19,18  Normal
495   d_ABP = 0;      // bit 17,16  2.9ns
496     
497   // N-Register Common Values
498   d_N_RSV = 0;    // 23,22
499   d_CP_GAIN = 0;  // 21
500     
501   // Function Register Common Values
502   d_P = 0;        // bits 23,22    0 = 8/9, 1 = 16/17, 2 = 32/33, 3 = 64/65
503   d_PD2 = 0;      // bit  21       Normal operation
504   d_CP2 = 4;      // bits 20,19,18 CP Gain = 5mA
505   d_CP1 = 4;      // bits 17,16,15 CP Gain = 5mA
506   d_TC = 0;       // bits 14-11    PFD Timeout
507   d_FL = 0;       // bit 10,9      Fastlock Disabled
508   d_CP3S = 0;     // bit 8         CP Enabled
509   d_PDP = 0;      // bit 7         Phase detector polarity, Positive=1
510   d_MUXOUT = 1;   // bits 6:4      Digital Lock Detect
511   d_PD1 = 0;      // bit 3         Normal operation
512   d_CR = 0;       // bit 2         Normal operation
513 }
514
515 _ADF410X_common::~_ADF410X_common()
516 {
517 }
518
519 bool 
520 _ADF410X_common::_compute_regs(double freq, int &retR, int &retcontrol, 
521                                int &retN, double &retfreq)
522 {
523   /*
524    * Determine values of R, control, and N registers, along with actual freq.
525    * 
526    * @param freq: target frequency in Hz
527    * @type freq: double
528    * @returns: (R, N, control, actual_freq)
529    * @rtype: tuple(int, int, int, double)
530    */
531   
532   //  Band-specific N-Register Values
533   double phdet_freq = _refclk_freq()/d_R_DIV;
534   printf("phdet_freq = %f\n", phdet_freq);
535
536   double desired_n = round(freq*d_freq_mult/phdet_freq);
537   printf("desired_n %f\n", desired_n);
538
539   double actual_freq = desired_n * phdet_freq;
540   printf("actual freq %f\n", actual_freq);
541
542   double B = floor(desired_n/_prescaler());
543   double A = desired_n - _prescaler()*B;
544   printf("A %f B %f\n", A, B);
545
546   d_B_DIV = int(B);    // bits 20:8;
547   d_A_DIV = int(A);    // bit 6:2;
548
549   if(d_B_DIV < d_A_DIV) {
550     retR = 0;
551     retN = 0;
552     retcontrol = 0;
553     retfreq = 0;
554     return false;
555   }
556
557   retR = (d_R_RSV<<21) | (d_LDP<<20) | (d_TEST<<18) |
558     (d_ABP<<16) | (d_R_DIV<<2);
559         
560   retN = (d_N_RSV<<22) | (d_CP_GAIN<<21) | (d_B_DIV<<8) | (d_A_DIV<<2);
561
562   retcontrol = (d_P<<22) | (d_PD2<<21) | (d_CP2<<18) | (d_CP1<<15) | 
563     (d_TC<<11) | (d_FL<<9) | (d_CP3S<<8) | (d_PDP<<7) |
564     (d_MUXOUT<<4) | (d_PD1<<3) | (d_CR<<2);
565   
566   retfreq = actual_freq/d_freq_mult;
567   
568   return true;
569 }
570
571 void 
572 _ADF410X_common::_write_all(int R, int N, int control)
573 {
574   /*
575    * Write all PLL registers:
576    *   R counter latch,
577    *   N counter latch,
578    *   Function latch,
579    *   Initialization latch
580    * 
581    * Adds 10ms delay between writing control and N if this is first call.
582    * This is the required power-up sequence.
583    * 
584    * @param R: 24-bit R counter latch
585    * @type R: int
586    * @param N: 24-bit N counter latch
587    * @type N: int
588    * @param control: 24-bit control latch
589    * @type control: int
590    */
591   static bool first = true;
592
593   timespec t;
594   t.tv_sec = 0;
595   t.tv_nsec = 10000000;
596   
597   _write_R(R);
598   _write_func(control);
599   _write_init(control);
600   if(first) {
601     //time.sleep(0.010);
602     nanosleep(&t, NULL);
603     first = false;
604   }
605   _write_N(N);
606 }
607
608 void
609 _ADF410X_common::_write_R(int R)
610 {
611   _write_it((R & ~0x3) | 0);
612 }
613
614 void
615 _ADF410X_common::_write_N(int N)
616 {
617   _write_it((N & ~0x3) | 1);
618 }
619
620 void
621 _ADF410X_common::_write_func(int func)
622 {
623   _write_it((func & ~0x3) | 2);
624 }
625
626 void
627 _ADF410X_common::_write_init(int init)
628 {
629   _write_it((init & ~0x3) | 3);
630 }
631
632 void
633 _ADF410X_common::_write_it(int v)
634 {
635   char c[3];
636   c[0] = (char)((v >> 16) & 0xff);
637   c[1] = (char)((v >>  8) & 0xff);
638   c[2] = (char)((v & 0xff));
639   std::string s(c, 3);
640   //d_usrp->_write_spi(0, d_spi_enable, d_spi_format, s);
641   usrp()->_write_spi(0, d_spi_enable, d_spi_format, s);
642 }
643
644 int
645 _ADF410X_common::_prescaler()
646 {
647   if(d_P == 0) {
648     return 8;
649   }
650   else if(d_P == 1) {
651     return 16;
652   }
653   else if(d_P == 2) {
654     return 32;
655   }
656   else if(d_P == 3) {
657     return 64;
658   }
659   else {
660     throw std::invalid_argument("Prescaler out of range\n");
661   }
662 }
663
664 double
665 _ADF410X_common::_refclk_freq()
666 {
667   throw std::runtime_error("_refclk_freq called from base class.");
668 }
669
670 bool
671 _ADF410X_common::_rx_write_io(int value, int mask)
672 {
673   throw std::runtime_error("_rx_write_io called from base class.");
674 }
675
676 bool
677 _ADF410X_common::_lock_detect()
678 {
679   throw std::runtime_error("_lock_detect called from base class.");
680 }
681
682 usrp_basic* 
683 _ADF410X_common::usrp()
684 {
685   throw std::runtime_error("usrp() called from base class.");
686 }
687
688
689 /****************************************************************************/
690
691
692 _lo_common::_lo_common()
693   : _ADF410X_common()
694 {
695   // Band-specific R-Register Values
696   d_R_DIV = 4;    // bits 15:2
697   
698   // Band-specific C-Register values
699   d_P = 0;        // bits 23,22   0 = Div by 8/9
700   d_CP2 = 4;      // bits 19:17
701   d_CP1 = 4;      // bits 16:14
702   
703   // Band specifc N-Register Values
704   d_DIVSEL = 0;   // bit 23
705   d_DIV2 = 0;     // bit 22
706   d_CPGAIN = 0;   // bit 21
707   d_freq_mult = 1;
708   
709   d_div = 1;
710   d_aux_div = 2;
711   d_main_div = 0;
712 }
713
714 _lo_common::~_lo_common()
715 {
716 }
717
718 double
719 _lo_common::freq_min()
720 {
721   return 50e6;
722 }
723
724 double
725 _lo_common::freq_max()
726 {
727   return 1000e6;
728 }
729
730 void
731 _lo_common::set_divider(int main_or_aux, int divisor)
732 {
733   if(main_or_aux == 0) {
734     if((divisor != 1) || (divisor != 2) || (divisor != 4) || (divisor != 8)) {
735       throw std::invalid_argument("Main Divider Must be 1, 2, 4, or 8\n");
736     }
737     d_main_div = (int)(log10(divisor)/log10(2.0));
738   }
739   else if(main_or_aux == 1) {
740     if((divisor != 2) || (divisor != 4) || (divisor != 8) || (divisor != 16)) {
741       throw std::invalid_argument("Aux Divider Must be 2, 4, 8 or 16\n");
742     }
743     d_main_div = (int)(log10(divisor/2.0)/log10(2.0));
744   }   
745   else {
746     throw std::invalid_argument("main_or_aux must be 'main' or 'aux'\n");
747   }
748   
749   int vala = d_main_div*SELA0;
750   int valb = d_aux_div*SELB0;
751   int mask = SELA0|SELA1|SELB0|SELB1;
752   
753   _rx_write_io((vala | valb), mask);
754 }
755
756 void
757 _lo_common::set_divider(const std::string &main_or_aux, int divisor)
758 {
759   if(main_or_aux == "main") {
760     set_divider(0, divisor);
761   }
762   else if(main_or_aux == "aux") {
763     set_divider(1, divisor);
764   }
765   else {
766     throw std::invalid_argument("main_or_aux must be 'main' or 'aux'\n");
767   }
768 }
769
770 struct freq_result_t
771 _lo_common::set_freq(double freq)
772 {
773   struct freq_result_t ret;
774   
775   if(freq < 20e6 or freq > 1200e6) {
776     throw std::invalid_argument("Requested frequency out of range\n");
777   }
778
779   int div = 1;
780   double lo_freq = freq * 2;
781   while((lo_freq < 1e9) && (div < 8)) {
782     div = div * 2;
783     lo_freq = lo_freq * 2;
784   }
785   
786   printf("For RF freq of %f, we set DIV=%d and LO Freq=%f\n", freq, div, lo_freq);
787
788   set_divider("main", div);
789   set_divider("aux", div*2);
790   
791   int R, N, control;
792   double actual_freq;
793   _compute_regs(lo_freq, R, N, control, actual_freq);
794   
795   printf("R %d N %d control %d actual freq %f\n", R, N, control, actual_freq);
796   if(R==0) {
797     ret.ok = false;
798     ret.baseband_freq = 0.0;
799     return ret;
800   }
801   _write_all(R, N, control);
802   
803   ret.ok = _lock_detect();
804   ret.baseband_freq = actual_freq/div/2;
805   return ret;
806 }
807         
808
809 /****************************************************************************/
810
811
812 db_wbx_lo_tx::db_wbx_lo_tx(usrp_basic_sptr usrp, int which)
813   : _lo_common(),
814     wbx_base_tx(usrp, which)
815 {
816 }
817
818 db_wbx_lo_tx::~db_wbx_lo_tx()
819 {
820 }
821
822 float
823 db_wbx_lo_tx::gain_min()
824 {
825   return -56.0;
826 }
827
828 float
829 db_wbx_lo_tx::gain_max()
830 {
831   return 0.0;
832 }
833
834 float
835 db_wbx_lo_tx::gain_db_per_step()
836 {
837   return 0.1;
838 }
839
840 bool
841 db_wbx_lo_tx::set_gain(float gain)
842 {
843   /*
844    * Set the gain.
845    * 
846    * @param gain:  gain in decibels
847    * @returns True/False
848    */
849
850   float txvga_gain;
851   float maxgain = gain_max();
852   float mingain = gain_min();
853   if(gain > maxgain) {
854     txvga_gain = maxgain;
855   }
856   else if(gain < mingain) {
857     txvga_gain = mingain;
858   }
859   else {
860     txvga_gain = gain;
861   }
862
863   float V_maxgain = 1.4;
864   float V_mingain = 0.1;
865   float V_fullscale = 3.3;
866   float dac_value = ((txvga_gain-mingain)*(V_maxgain-V_mingain)/
867                      (maxgain-mingain) + V_mingain)*4096/V_fullscale;
868
869   assert(dac_value>=0 && dac_value<4096);
870   printf("DAC value %f\n", dac_value);
871
872   return d_usrp->write_aux_dac(d_which, 1, (int)(dac_value));
873 }
874
875 double
876 db_wbx_lo_tx::_refclk_freq()
877 {
878   return wbx_base::_refclk_freq();
879 }
880
881 bool
882 db_wbx_lo_tx::_rx_write_io(int value, int mask)
883 {
884   return wbx_base::_rx_write_io(value, mask);
885 }
886
887 bool
888 db_wbx_lo_tx::_lock_detect()
889 {
890   return wbx_base::_lock_detect();
891 }
892
893 usrp_basic* 
894 db_wbx_lo_tx::usrp()
895 {
896   return d_usrp;
897 }
898
899
900 /****************************************************************************/
901
902
903 db_wbx_lo_rx::db_wbx_lo_rx(usrp_basic_sptr usrp, int which)
904   : _lo_common(),
905     wbx_base_rx(usrp, which)    
906 {
907 }
908
909 db_wbx_lo_rx::~db_wbx_lo_rx()
910 {
911 }
912
913 float
914 db_wbx_lo_rx::gain_min()
915 {
916   return d_usrp->pga_min();
917 }
918
919 float
920 db_wbx_lo_rx::gain_max()
921 {
922   return d_usrp->pga_max() + 45;
923 }
924
925 float
926 db_wbx_lo_rx::gain_db_per_step()
927 {
928   return 0.05;
929 }
930
931 double
932 db_wbx_lo_rx::_refclk_freq()
933 {
934   return wbx_base::_refclk_freq();
935 }
936
937 bool
938 db_wbx_lo_rx::_rx_write_io(int value, int mask)
939 {
940   return wbx_base::_rx_write_io(value, mask);
941 }
942
943 bool
944 db_wbx_lo_rx::_lock_detect()
945 {
946   return wbx_base::_lock_detect();
947 }
948
949 usrp_basic* 
950 db_wbx_lo_rx::usrp()
951 {
952   return d_usrp;
953 }