First Functional TX modulator
[debian/gnuradio] / usrp / host / lib / db_wbxng.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 <usrp/db_wbxng.h>
22 #include <usrp/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 TXMOD_EN        (1 << 4)
33 #define PLL_CE          (1 << 3)
34 #define PLL_PDBRF       (1 << 2)
35 #define PLL_MUXOUT      (1 << 1)
36 #define PLL_LOCK_DETECT (1 << 0)
37
38 wbxng_base::wbxng_base(usrp_basic_sptr _usrp, int which, int _power_on)
39   : db_base(_usrp, which), d_power_on(_power_on)
40 {
41   /*
42     @param usrp: instance of usrp.source_c
43     @param which: which side: 0 or 1 corresponding to side A or B respectively
44     @type which: int
45   */
46
47   usrp()->_write_oe(d_which, 0, 0xffff);   // turn off all outputs
48
49   d_first = true;
50   d_spi_format = SPI_FMT_MSB | SPI_FMT_HDR_0;
51
52   _enable_refclk(false);                // disable refclk
53
54   set_auto_tr(false);
55 }
56
57 wbxng_base::~wbxng_base()
58 {
59   delete d_common;
60 }
61
62 void
63 wbxng_base::_write_all(int R, int control, int N)
64 {
65   /*
66     Write R counter latch, control latch and N counter latch to VCO.
67     
68     Adds 10ms delay between writing control and N if this is first call.
69     This is the required power-up sequence.
70     
71     @param R: 24-bit R counter latch
72     @type R: int
73     @param control: 24-bit control latch
74     @type control: int
75     @param N: 24-bit N counter latch
76     @type N: int
77   */
78   timespec t;
79   t.tv_sec = 0;
80   t.tv_nsec = 10000000;
81
82   /*
83   _write_R(R);
84   _write_control(control);
85   if(d_first) {
86     //time.sleep(0.010);
87     nanosleep(&t, NULL);
88     d_first = false;
89   }
90   _write_N(N);
91   */
92 }
93
94 void
95 wbxng_base::_write_control(int control)
96 {
97   //_write_it((control & ~0x3) | 0);
98 }
99
100 void
101 wbxng_base::_write_R(int R)
102 {
103   //_write_it((R & ~0x3) | 1);
104 }
105
106 void
107 wbxng_base::_write_N(int N)
108 {
109   //_write_it((N & ~0x3) | 2);
110 }
111
112 void
113 wbxng_base::_write_it(int v)
114 {
115   char s[3];
116   s[0] = (char)((v >> 16) & 0xff);
117   s[1] = (char)((v >>  8) & 0xff);
118   s[2] = (char)(v & 0xff);
119   std::string str(s, 3);
120   //usrp()->_write_spi(0, d_spi_enable, d_spi_format, str);
121 }
122         
123 bool
124 wbxng_base::_lock_detect()
125 {
126   /*
127     @returns: the value of the VCO/PLL lock detect bit.
128     @rtype: 0 or 1
129   */
130   
131   if(d_common->_get_locked()){
132     return true;
133   }
134   else {      // Give it a second chance
135     return false;
136     /*
137     // FIXME: make portable sleep
138     timespec t;
139     t.tv_sec = 0;
140     t.tv_nsec = 100000000;
141     nanosleep(&t, NULL);
142     
143     if(usrp()->read_io(d_which) & PLL_LOCK_DETECT) {
144       return true;
145     }
146     else {
147       return false;
148     }
149     */
150   }
151  
152   throw std::runtime_error("_lock_detect called from wbxng_base\n");
153 }
154
155 /*
156 bool
157 wbxng_base::_compute_regs(double freq, int &retR, int &retcontrol,
158                            int &retN, double &retfreq)
159 {
160   **COMMENT**
161     Determine values of R, control, and N registers, along with actual freq.
162     
163     @param freq: target frequency in Hz
164     @type freq: float
165     @returns: (R, control, N, actual_freq)
166     @rtype: tuple(int, int, int, float)
167     
168     Override this in derived classes.
169   **COMMENT**
170   
171   //raise NotImplementedError;
172   throw std::runtime_error("_compute_regs called from wbxng_base\n");
173 }
174 */
175
176 int
177 wbxng_base::_compute_control_reg()
178 {
179   throw std::runtime_error("_compute_control_reg called from wbxng_base\n");
180   //return d_common->_compute_control_reg();
181 }
182
183 int
184 wbxng_base::_refclk_divisor()
185 {
186   throw std::runtime_error("_refclk_divisor called from wbxng_base\n");
187   //return d_common->_refclk_divisor();
188 }
189
190 double
191 wbxng_base::_refclk_freq()
192 {
193   throw std::runtime_error("_refclk_divisor called from wbxng_base\n");
194   // *** TODO *** Magic Number 64e6?
195   //return 64e6/_refclk_divisor();
196 }
197
198 struct freq_result_t
199 wbxng_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
207   freq_t int_freq = freq_t(freq);
208   bool ok = d_common->_set_freq(int_freq*2);
209   double freq_result = (double) d_common->_get_freq()/2.0;
210   struct freq_result_t args = {ok, freq_result};
211
212   /* Wait before reading Lock Detect*/
213   timespec t;
214   t.tv_sec = 0;
215   t.tv_nsec = 10000000;
216   nanosleep(&t, NULL);
217
218   fprintf(stderr,"Setting WBXNG frequency, requested %d, obtained %f, lock_detect %d\n", 
219           int_freq, freq_result, _lock_detect());
220
221   // Offsetting the LO helps get the Tx carrier leakage out of the way.
222   // This also ensures that on Rx, we're not getting hosed by the
223   // FPGA's DC removal loop's time constant.  We were seeing a
224   // problem when running with discontinuous transmission.
225   // Offsetting the LO made the problem go away.
226   //freq += d_lo_offset;
227   
228   //int R, control, N;
229   //double actual_freq;
230   //_compute_regs(freq, R, control, N, actual_freq);
231
232   //if(R==0) {
233   //  return args;
234   //}
235    
236   //_write_all(R, control, N);
237   //args.ok = _lock_detect();
238   //args.baseband_freq = actual_freq;
239   return args;
240 }
241
242 bool
243 wbxng_base::_set_pga(float pga_gain)
244 {
245   /*
246   if(d_which == 0) {
247     usrp()->set_pga(0, pga_gain);
248     usrp()->set_pga(1, pga_gain);
249   }
250   else {
251     usrp()->set_pga(2, pga_gain);
252     usrp()->set_pga(3, pga_gain);
253   }
254   */
255   return true;
256 }
257
258 bool
259 wbxng_base::is_quadrature()
260 {
261   /*
262     Return True if this board requires both I & Q analog channels.
263     
264     This bit of info is useful when setting up the USRP Rx mux register.
265   */
266   return true;
267 }
268
269 double
270 wbxng_base::freq_min()
271 {
272   return (double) d_common->_get_min_freq()/2.0;
273 }
274
275 double
276 wbxng_base::freq_max()
277 {
278   return (double) d_common->_get_max_freq()/2.0;
279 }
280
281 // ----------------------------------------------------------------
282
283 wbxng_base_tx::wbxng_base_tx(usrp_basic_sptr _usrp, int which, int _power_on)
284   : wbxng_base(_usrp, which, _power_on)
285 {
286   /*
287     @param usrp: instance of usrp.sink_c
288     @param which: 0 or 1 corresponding to side TX_A or TX_B respectively.
289   */
290   
291   if(which == 0) {
292     d_spi_enable = SPI_ENABLE_TX_A;
293   }
294   else {
295     d_spi_enable = SPI_ENABLE_TX_B;
296   }
297
298   d_common = new adf4350(_usrp, d_which, d_spi_enable);
299   
300   // power up the transmit side, but don't enable the mixer
301   usrp()->_write_oe(d_which,(RX_TXN|TXMOD_EN|ENABLE_33|ENABLE_5), (RX_TXN|TXMOD_EN|ENABLE_33|ENABLE_5));
302   usrp()->write_io(d_which, (power_on()|RX_TXN|TXMOD_EN|ENABLE_33|ENABLE_5), (RX_TXN|TXMOD_EN|ENABLE_33|ENABLE_5));
303   fprintf(stderr,"Setting WBXNG TXMOD on"); 
304   //set_lo_offset(4e6);
305
306   //set_gain((gain_min() + gain_max()) / 2.0);  // initialize gain
307 }
308
309 wbxng_base_tx::~wbxng_base_tx()
310 {
311   shutdown();
312 }
313
314
315 void
316 wbxng_base_tx::shutdown()
317 {
318   // fprintf(stderr, "wbxng_base_tx::shutdown  d_is_shutdown = %d\n", d_is_shutdown);
319
320   if (!d_is_shutdown){
321     d_is_shutdown = true;
322     // do whatever there is to do to shutdown
323
324     // Power down and leave the T/R switch in the R position
325     usrp()->write_io(d_which, (power_off()|RX_TXN), (RX_TXN|ENABLE_33|ENABLE_5));
326
327     // Power down VCO/PLL
328     d_common->_enable(false);
329   
330     /*
331     _write_control(_compute_control_reg());
332     */
333     _enable_refclk(false);                       // turn off refclk
334     set_auto_tr(false);
335   }
336 }
337
338 bool
339 wbxng_base_tx::set_auto_tr(bool on)
340 {
341   bool ok = true;
342   if(on) {
343     ok &= set_atr_mask (RX_TXN | ENABLE_33 | ENABLE_5);
344     ok &= set_atr_txval(0      | ENABLE_33 | ENABLE_5);
345     ok &= set_atr_rxval(RX_TXN | 0);
346   }
347   else {
348     ok &= set_atr_mask (0);
349     ok &= set_atr_txval(0);
350     ok &= set_atr_rxval(0);
351   }
352   return ok;
353 }
354
355 bool
356 wbxng_base_tx::set_enable(bool on)
357 {
358   /*
359     Enable transmitter if on is true
360   */
361
362   int v;
363   int mask = RX_TXN | ENABLE_5 | ENABLE_33;
364   if(on) {
365     v = ENABLE_5 | ENABLE_33;
366   }
367   else {
368     v = RX_TXN;
369   }
370   return usrp()->write_io(d_which, v, mask);
371 }
372
373 float
374 wbxng_base_tx::gain_min()
375 {
376   return usrp()->pga_max();
377 }
378
379 float
380 wbxng_base_tx::gain_max()
381 {
382   return usrp()->pga_max();
383 }
384
385 float
386 wbxng_base_tx::gain_db_per_step()
387 {
388   return 1;
389 }
390
391 bool
392 wbxng_base_tx::set_gain(float gain)
393 {
394   /*
395     Set the gain.
396     
397     @param gain:  gain in decibels
398     @returns True/False
399   */
400   return _set_pga(usrp()->pga_max());
401 }
402
403
404 /**************************************************************************/
405
406
407 wbxng_base_rx::wbxng_base_rx(usrp_basic_sptr _usrp, int which, int _power_on)
408   : wbxng_base(_usrp, which, _power_on)
409 {
410   /*
411     @param usrp: instance of usrp.source_c
412     @param which: 0 or 1 corresponding to side RX_A or RX_B respectively.
413   */
414
415   if(which == 0) {
416     d_spi_enable = SPI_ENABLE_RX_A;
417   }
418   else {
419     d_spi_enable = SPI_ENABLE_RX_B;
420   }
421
422   d_common = new adf4350(_usrp, d_which, d_spi_enable);
423
424   usrp()->_write_oe(d_which, (RX2_RX1N|ENABLE_33|ENABLE_5), (RX2_RX1N|ENABLE_33|ENABLE_5));
425   usrp()->write_io(d_which,  (power_on()|RX2_RX1N|ENABLE_33|ENABLE_5), (RX2_RX1N|ENABLE_33|ENABLE_5));
426   
427   // set up for RX on TX/RX port
428   select_rx_antenna("TX/RX");
429   
430   bypass_adc_buffers(true);
431
432   /*  
433   set_lo_offset(-4e6);
434   */
435 }
436
437 wbxng_base_rx::~wbxng_base_rx()
438 {
439   shutdown();
440 }
441
442 void
443 wbxng_base_rx::shutdown()
444 {
445   // fprintf(stderr, "wbxng_base_rx::shutdown  d_is_shutdown = %d\n", d_is_shutdown);
446
447   if (!d_is_shutdown){
448     d_is_shutdown = true;
449     // do whatever there is to do to shutdown
450
451     // Power down
452     usrp()->common_write_io(C_RX, d_which, power_off(), (ENABLE_33|ENABLE_5));
453
454     // Power down VCO/PLL
455     d_common->_enable(false);
456
457     // fprintf(stderr, "wbxng_base_rx::shutdown  before _write_control\n");
458     //_write_control(_compute_control_reg());
459
460     // fprintf(stderr, "wbxng_base_rx::shutdown  before _enable_refclk\n");
461     _enable_refclk(false);                       // turn off refclk
462
463     // fprintf(stderr, "wbxng_base_rx::shutdown  before set_auto_tr\n");
464     set_auto_tr(false);
465
466     // fprintf(stderr, "wbxng_base_rx::shutdown  after set_auto_tr\n");
467   }
468 }
469
470 bool
471 wbxng_base_rx::set_auto_tr(bool on)
472 {
473   bool ok = true;
474   if(on) {
475     ok &= set_atr_mask (ENABLE_33|ENABLE_5);
476     ok &= set_atr_txval(     0);
477     ok &= set_atr_rxval(ENABLE_33|ENABLE_5);
478   }
479   else {
480     ok &= set_atr_mask (0);
481     ok &= set_atr_txval(0);
482     ok &= set_atr_rxval(0);
483   }
484   return true;
485 }
486
487 bool
488 wbxng_base_rx::select_rx_antenna(int which_antenna)
489 {
490   /*
491     Specify which antenna port to use for reception.
492     @param which_antenna: either 'TX/RX' or 'RX2'
493   */
494
495   if(which_antenna == 0) {
496     usrp()->write_io(d_which, 0,RX2_RX1N);
497   }
498   else if(which_antenna == 1) {
499     usrp()->write_io(d_which, RX2_RX1N, RX2_RX1N);
500   }
501   else {
502     return false;
503     // throw std::invalid_argument("which_antenna must be either 'TX/RX' or 'RX2'\n");
504   }
505   return true;
506 }
507
508 bool
509 wbxng_base_rx::select_rx_antenna(const std::string &which_antenna)
510 {
511   /*
512     Specify which antenna port to use for reception.
513     @param which_antenna: either 'TX/RX' or 'RX2'
514   */
515
516   
517   if(which_antenna == "TX/RX") {
518     usrp()->write_io(d_which, 0, RX2_RX1N);
519   }
520   else if(which_antenna == "RX2") {
521     usrp()->write_io(d_which, RX2_RX1N, RX2_RX1N);
522   }
523   else {
524     // throw std::invalid_argument("which_antenna must be either 'TX/RX' or 'RX2'\n");
525     return false;
526   }
527   
528   return true;
529 }
530
531 bool
532 wbxng_base_rx::set_gain(float gain)
533 {
534   /*
535     Set the gain.
536     
537     @param gain:  gain in decibels
538     @returns True/False
539   */
540   
541   /*
542   // clamp gain
543   gain = std::max(gain_min(), std::min(gain, gain_max()));
544
545   float pga_gain, agc_gain;
546   float V_maxgain, V_mingain, V_fullscale, dac_value;
547
548   float maxgain = gain_max() - usrp()->pga_max();
549   float mingain = gain_min();
550   if(gain > maxgain) {
551     pga_gain = gain-maxgain;
552     assert(pga_gain <= usrp()->pga_max());
553     agc_gain = maxgain;
554   }
555   else {
556     pga_gain = 0;
557     agc_gain = gain;
558   }
559   
560   V_maxgain = .2;
561   V_mingain = 1.2;
562   V_fullscale = 3.3;
563   dac_value = (agc_gain*(V_maxgain-V_mingain)/(maxgain-mingain) + V_mingain)*4096/V_fullscale;
564
565   assert(dac_value>=0 && dac_value<4096);
566
567   return (usrp()->write_aux_dac(d_which, 0, int(dac_value))
568           && _set_pga(int(pga_gain)));
569   */
570   return false;
571 }
572
573 // ----------------------------------------------------------------
574
575 db_wbxng_tx::db_wbxng_tx(usrp_basic_sptr usrp, int which)
576   : wbxng_base_tx(usrp, which)
577 {
578 }
579
580 db_wbxng_tx::~db_wbxng_tx()
581 {
582 }
583
584 /*
585 bool
586 db_wbxng_tx::_compute_regs(double freq, int &retR, int &retcontrol,
587                                  int &retN, double &retfreq)
588 {
589   return d_common->_compute_regs(_refclk_freq(), freq, retR,
590                                  retcontrol, retN, retfreq);
591 }
592 */
593
594
595 db_wbxng_rx::db_wbxng_rx(usrp_basic_sptr usrp, int which)
596   : wbxng_base_rx(usrp, which)
597 {
598   set_gain((gain_min() + gain_max()) / 2.0);  // initialize gain
599 }
600
601 db_wbxng_rx::~db_wbxng_rx()
602 {
603 }
604
605 float
606 db_wbxng_rx::gain_min()
607 {
608   return usrp()->pga_min();
609 }
610
611 float
612 db_wbxng_rx::gain_max()
613 {
614   return usrp()->pga_max()+70;
615 }
616
617 float
618 db_wbxng_rx::gain_db_per_step()
619 {
620   return 0.05;
621 }
622
623
624 bool
625 db_wbxng_rx::i_and_q_swapped()
626 {
627   return true;
628 }
629
630 /*
631 bool
632 db_wbxng_rx::_compute_regs(double freq, int &retR, int &retcontrol,
633                                  int &retN, double &retfreq)
634 {
635   return d_common->_compute_regs(_refclk_freq(), freq, retR,
636                                  retcontrol, retN, retfreq);
637 }
638 */
639