Merged eb/gcell-wip2 rev 10130:10152 into trunk.
[debian/gnuradio] / gr-usrp / src / db_wbx.py
1 #
2 # Copyright 2007 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 version 2, 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
22 from gnuradio import usrp1
23 import time,math
24
25 from usrpm import usrp_dbid
26 import db_base
27 import db_instantiator
28 from usrpm.usrp_fpga_regs import *
29
30 #debug_using_gui = True                  # Must be set to True or False
31 debug_using_gui = False                  # Must be set to True or False
32
33 #if debug_using_gui:
34 #    import flexrf_debug_gui
35
36 # d'board i/o pin defs
37
38 # TX IO Pins
39 TX_POWER = (1 << 0)         # TX Side Power
40 RX_TXN = (1 << 1)           # T/R antenna switch for TX/RX port
41 TX_ENB_MIX = (1 << 2)       # Enable IQ mixer
42 TX_ENB_VGA = (1 << 3)
43
44 # RX IO Pins
45 RX2_RX1N = (1 << 0)         # antenna switch between RX2 and TX/RX port
46 RXENABLE = (1 << 1)         # enables mixer
47 PLL_LOCK_DETECT = (1 << 2)  # Muxout pin from PLL -- MUST BE INPUT
48 MReset = (1 << 3)           # NB6L239 Master Reset, asserted low
49 SELA0 = (1 << 4)            # NB6L239 SelA0
50 SELA1 = (1 << 5)            # NB6L239 SelA1
51 SELB0 = (1 << 6)            # NB6L239 SelB0
52 SELB1 = (1 << 7)            # NB6L239 SelB1
53 PLL_ENABLE = (1 << 8)       # CE Pin on PLL
54 AUX_SCLK = (1 << 9)         # ALT SPI SCLK
55 AUX_SDO = (1 << 10)         # ALT SPI SDO
56 AUX_SEN = (1 << 11)         # ALT SPI SEN
57
58 SPI_ENABLE_TX_A = usrp1.SPI_ENABLE_TX_A
59 SPI_ENABLE_TX_B = usrp1.SPI_ENABLE_TX_B
60 SPI_ENABLE_RX_A = usrp1.SPI_ENABLE_RX_A
61 SPI_ENABLE_RX_B = usrp1.SPI_ENABLE_RX_B
62
63
64 """
65 A few comments about the WBX boards:
66   They are half-duplex.  I.e., transmit and receive are mutually exclusive.
67   There is a single LO for both the Tx and Rx sides.
68   The the shared control signals are hung off of the Rx side.
69   The shared io controls are duplexed onto the Rx side pins.
70   The wbx_high d'board always needs to be in 'auto_tr_mode'
71 """
72
73
74 class wbx_base(db_base.db_base):
75     """
76     Abstract base class for all wbx boards.
77
78     Derive board specific subclasses from db_wbx_base_{tx,rx}
79     """
80     def __init__(self, usrp, which):
81         """
82         @param usrp: instance of usrp.source_c
83         @param which: which side: 0 or 1 corresponding to side A or B respectively
84         @type which: int
85         """
86         # sets _u  _which _tx and _slot
87         db_base.db_base.__init__(self, usrp, which)
88
89         self.first = True
90         self.spi_format = usrp1.SPI_FMT_MSB | usrp1.SPI_FMT_HDR_0
91
92         # FIXME -- the write reg functions don't work with 0xffff for masks
93         self._rx_write_oe(int(PLL_ENABLE|MReset|SELA0|SELA1|SELB0|SELB1|RX2_RX1N|RXENABLE), 0x7fff)
94         self._rx_write_io((PLL_ENABLE|MReset|0|RXENABLE), (PLL_ENABLE|MReset|RX2_RX1N|RXENABLE))
95
96         self._tx_write_oe((TX_POWER|RX_TXN|TX_ENB_MIX|TX_ENB_VGA), 0x7fff)
97         self._tx_write_io((0|RX_TXN), (TX_POWER|RX_TXN|TX_ENB_MIX|TX_ENB_VGA))  # TX off, TR switch set to RX
98
99         self.spi_enable = (SPI_ENABLE_RX_A, SPI_ENABLE_RX_B)[which]
100
101         self.set_auto_tr(False)
102         
103         #if debug_using_gui:
104         #    title = "FlexRF Debug Rx"
105         #    if self._tx:
106         #        title = "FlexRF Debug Tx"
107         #    self.gui = flexrf_debug_gui.flexrf_debug_gui(self, title)
108         #    self.gui.Show(True)
109
110
111     def __del__(self):
112         #self._u.write_io(self._which, self.power_off, POWER_UP)   # turn off power to board
113         #self._u._write_oe(self._which, 0, 0xffff)   # turn off all outputs
114         self.set_auto_tr(False)
115
116     def _lock_detect(self):
117         """
118         @returns: the value of the VCO/PLL lock detect bit.
119         @rtype: 0 or 1
120         """
121         if self._rx_read_io() & PLL_LOCK_DETECT:
122             return True
123         else:      # Give it a second chance
124             if self._rx_read_io() & PLL_LOCK_DETECT:
125                 return True
126             else:
127                 return False
128         
129     # Both sides need access to the Rx pins.
130     # Write them directly, bypassing the convenience routines.
131     # (Sort of breaks modularity, but will work...)
132
133     def _tx_write_oe(self, value, mask):
134         return self._u._write_fpga_reg((FR_OE_0, FR_OE_2)[self._which],
135                                        ((mask & 0xffff) << 16) | (value & 0xffff))
136
137     def _rx_write_oe(self, value, mask):
138         return self._u._write_fpga_reg((FR_OE_1, FR_OE_3)[self._which],
139                                        ((mask & 0xffff) << 16) | (value & 0xffff))
140
141     def _tx_write_io(self, value, mask):
142         return self._u._write_fpga_reg((FR_IO_0, FR_IO_2)[self._which],
143                                        ((mask & 0xffff) << 16) | (value & 0xffff))
144
145     def _rx_write_io(self, value, mask):
146         return self._u._write_fpga_reg((FR_IO_1, FR_IO_3)[self._which],
147                                        ((mask & 0xffff) << 16) | (value & 0xffff))
148
149     def _rx_read_io(self):
150         t = self._u._read_fpga_reg((FR_RB_IO_RX_A_IO_TX_A, FR_RB_IO_RX_B_IO_TX_B)[self._which])
151         return (t >> 16) & 0xffff
152
153     def _tx_read_io(self):
154         t = self._u._read_fpga_reg((FR_RB_IO_RX_A_IO_TX_A, FR_RB_IO_RX_B_IO_TX_B)[self._which])
155         return t & 0xffff
156
157
158     def _compute_regs(self, freq):
159         """
160         Determine values of registers, along with actual freq.
161         
162         @param freq: target frequency in Hz
163         @type freq: float
164         @returns: (R, N, func, init, actual_freq)
165         @rtype: tuple(int, int, int, int, float)
166         
167         Override this in derived classes.
168         """
169         raise NotImplementedError
170
171     def _refclk_freq(self):
172         return float(self._u.fpga_master_clock_freq())/self._refclk_divisor()
173
174     def _refclk_divisor(self):
175         """
176         Return value to stick in REFCLK_DIVISOR register
177         """
178         return 1
179     
180     # ----------------------------------------------------------------
181     
182     def set_freq(self, freq):
183         """
184         @returns (ok, actual_baseband_freq) where:
185            ok is True or False and indicates success or failure,
186            actual_baseband_freq is the RF frequency that corresponds to DC in the IF.
187         """
188         raise NotImplementedError
189
190     def gain_range(self):
191         """
192         Return range of gain that can be set by this d'board.
193
194         @returns (min_gain, max_gain, step_size)
195         Where gains are expressed in decibels (your mileage may vary)
196         """
197         raise NotImplementedError
198
199     def set_gain(self, gain):
200         """
201         Set the gain.
202
203         @param gain:  gain in decibels
204         @returns True/False
205         """
206         raise NotImplementedError
207
208     def _set_pga(self, pga_gain):
209         if(self._which == 0):
210             self._u.set_pga (0, pga_gain)
211             self._u.set_pga (1, pga_gain)
212         else:
213             self._u.set_pga (2, pga_gain)
214             self._u.set_pga (3, pga_gain)
215
216     def is_quadrature(self):
217         """
218         Return True if this board requires both I & Q analog channels.
219
220         This bit of info is useful when setting up the USRP Rx mux register.
221         """
222         return True
223
224 # ----------------------------------------------------------------
225
226 class wbx_base_tx(wbx_base):
227     def __init__(self, usrp, which):
228         """
229         @param usrp: instance of usrp.sink_c
230         @param which: 0 or 1 corresponding to side TX_A or TX_B respectively.
231         """
232         wbx_base.__init__(self, usrp, which)
233
234         # power up the transmit side, NO -- but set antenna to receive
235         self._u.write_io(self._which, (TX_POWER), (TX_POWER|RX_TXN))
236         self._lo_offset = 0e6
237
238         #  Gain is not set by the PGA, but the PGA must be set at max gain in the TX
239         return self._set_pga(self._u.pga_max())
240
241     def __del__(self):
242         # Power down and leave the T/R switch in the R position
243         self._u.write_io(self._which, (RX_TXN), (TX_POWER|RX_TXN|TX_ENB_MIX|TX_ENB_VGA))
244         wbx_base.__del__(self)
245
246     def set_auto_tr(self, on):
247         if on:
248             self.set_atr_mask (RX_TXN)
249             self.set_atr_txval(0)
250             self.set_atr_rxval(RX_TXN)
251         else:
252             self.set_atr_mask (0)
253             self.set_atr_txval(0)
254             self.set_atr_rxval(0)
255
256     def set_enable(self, on):
257         """
258         Enable transmitter if on is True
259         """
260         mask = RX_TXN|TX_ENB_MIX|TX_ENB_VGA
261         print "HERE!!!!"
262         if on:
263             self._u.write_io(self._which, TX_ENB_MIX|TX_ENB_VGA, mask)
264         else:
265             self._u.write_io(self._which, RX_TXN, mask)
266
267
268     def set_lo_offset(self, offset):
269         """
270         Set amount by which LO is offset from requested tuning frequency.
271         
272         @param offset: offset in Hz
273         """
274         self._lo_offset = offset
275
276     def lo_offset(self):
277         """
278         Get amount by which LO is offset from requested tuning frequency.
279         
280         @returns Offset in Hz
281         """
282         return self._lo_offset
283         
284 class wbx_base_rx(wbx_base):
285     def __init__(self, usrp, which):
286         """
287         @param usrp: instance of usrp.source_c
288         @param which: 0 or 1 corresponding to side RX_A or RX_B respectively.
289         """
290         wbx_base.__init__(self, usrp, which)
291         
292         # set up for RX on TX/RX port
293         self.select_rx_antenna('TX/RX')
294
295         self.bypass_adc_buffers(True)
296
297         self._lo_offset = 0.0
298
299     def __del__(self):
300         # Power down
301         self._u.write_io(self._which, 0, (RXENABLE))
302         wbx_base.__del__(self)
303     
304     def set_auto_tr(self, on):
305         if on:
306             self.set_atr_mask (ENABLE)
307             self.set_atr_txval(     0)
308             self.set_atr_rxval(ENABLE)
309         else:
310             self.set_atr_mask (0)
311             self.set_atr_txval(0)
312             self.set_atr_rxval(0)
313
314     def select_rx_antenna(self, which_antenna):
315         """
316         Specify which antenna port to use for reception.
317         @param which_antenna: either 'TX/RX' or 'RX2'
318         """
319         if which_antenna in (0, 'TX/RX'):
320             self._u.write_io(self._which, 0,        RX2_RX1N)
321         elif which_antenna in (1, 'RX2'):
322             self._u.write_io(self._which, RX2_RX1N, RX2_RX1N)
323         else:
324             raise ValueError, "which_antenna must be either 'TX/RX' or 'RX2'"
325
326     def set_gain(self, gain):
327         """
328         Set the gain.
329
330         @param gain:  gain in decibels
331         @returns True/False
332         """
333         maxgain = self.gain_range()[1] - self._u.pga_max()
334         mingain = self.gain_range()[0]
335         if gain > maxgain:
336             pga_gain = gain-maxgain
337             assert pga_gain <= self._u.pga_max()
338             agc_gain = maxgain
339         else:
340             pga_gain = 0
341             agc_gain = gain
342         V_maxgain = .2
343         V_mingain = 1.2
344         V_fullscale = 3.3
345         dac_value = (agc_gain*(V_maxgain-V_mingain)/(maxgain-mingain) + V_mingain)*4096/V_fullscale
346         assert dac_value>=0 and dac_value<4096
347         return self._u.write_aux_dac(self._which, 0, int(dac_value)) and \
348                self._set_pga(int(pga_gain))
349
350     def set_lo_offset(self, offset):
351         """
352         Set amount by which LO is offset from requested tuning frequency.
353         
354         @param offset: offset in Hz
355         """
356         self._lo_offset = offset
357
358     def lo_offset(self):
359         """
360         Get amount by which LO is offset from requested tuning frequency.
361         
362         @returns Offset in Hz
363         """
364         return self._lo_offset
365
366
367     def i_and_q_swapped(self):
368         """
369         Return True if this is a quadrature device and ADC 0 is Q.
370         """
371         return False
372
373 # ----------------------------------------------------------------
374
375 class _ADF410X_common(object):
376     def __init__(self):
377         # R-Register Common Values
378         self.R_RSV = 0   # bits 23,22,21
379         self.LDP = 1   # bit 20     Lock detect in 5 cycles
380         self.TEST = 0  # bit 19,18  Normal
381         self.ABP = 0   # bit 17,16  2.9ns
382
383         # N-Register Common Values
384         self.N_RSV = 0      # 23,22
385         self.CP_GAIN = 0    # 21
386         
387         # Function Register Common Values
388         self.P = 0        # bits 23,22    0 = 8/9, 1 = 16/17, 2 = 32/33, 3 = 64/65
389         self.PD2 = 0      # bit  21       Normal operation
390         self.CP2 = 4      # bits 20,19,18 CP Gain = 5mA
391         self.CP1 = 4      # bits 17,16,15 CP Gain = 5mA
392         self.TC = 0       # bits 14-11    PFD Timeout
393         self.FL = 0       # bit 10,9      Fastlock Disabled
394         self.CP3S = 0     # bit 8         CP Enabled
395         self.PDP = 0      # bit 7         Phase detector polarity, Positive=1
396         self.MUXOUT = 1   # bits 6:4      Digital Lock Detect
397         self.PD1 = 0      # bit 3         Normal operation
398         self.CR = 0       # bit 2         Normal operation
399
400     def _compute_regs(self, freq):
401         """
402         Determine values of R, control, and N registers, along with actual freq.
403         
404         @param freq: target frequency in Hz
405         @type freq: float
406         @returns: (R, N, control, actual_freq)
407         @rtype: tuple(int, int, int, float)
408         """
409
410         #  Band-specific N-Register Values
411         phdet_freq = self._refclk_freq()/self.R_DIV
412         print "phdet_freq = %f" % (phdet_freq,)
413         desired_n = round(freq*self.freq_mult/phdet_freq)
414         print "desired_n %f" % (desired_n,)
415         actual_freq = desired_n * phdet_freq
416         print "actual freq %f" % (actual_freq,)
417         B = math.floor(desired_n/self._prescaler())
418         A = desired_n - self._prescaler()*B
419         print "A %d B %d" % (A,B)
420         self.B_DIV = int(B)    # bits 20:8
421         self.A_DIV = int(A)    # bit 6:2
422         #assert self.B_DIV >= self.A_DIV
423         if self.B_DIV < self.A_DIV:
424             return (0,0,0,0)
425         R = (self.R_RSV<<21) | (self.LDP<<20) | (self.TEST<<18) | \
426             (self.ABP<<16) | (self.R_DIV<<2)
427         
428         N = (self.N_RSV<<22) | (self.CP_GAIN<<21) | (self.B_DIV<<8) | (self.A_DIV<<2)
429
430         control = (self.P<<22) | (self.PD2<<21) | (self.CP2<<18) | (self.CP1<<15) | \
431                   (self.TC<<11) | (self.FL<<9) | (self.CP3S<<8) | (self.PDP<<7) | \
432                   (self.MUXOUT<<4) | (self.PD1<<3) | (self.CR<<2)
433
434         return (R,N,control,actual_freq/self.freq_mult)
435
436     def _write_all(self, R, N, control):
437         """
438         Write all PLL registers:
439             R counter latch,
440             N counter latch,
441             Function latch,
442             Initialization latch
443
444         Adds 10ms delay between writing control and N if this is first call.
445         This is the required power-up sequence.
446         
447         @param R: 24-bit R counter latch
448         @type R: int
449         @param N: 24-bit N counter latch
450         @type N: int
451         @param control: 24-bit control latch
452         @type control: int
453         """
454         self._write_R(R)
455         self._write_func(control)
456         self._write_init(control)
457         if self.first:
458             time.sleep(0.010)
459             self.first = False
460         self._write_N(N)
461
462     def _write_R(self, R):
463         self._write_it((R & ~0x3) | 0)
464
465     def _write_N(self, N):
466         self._write_it((N & ~0x3) | 1)
467
468     def _write_func(self, func):
469         self._write_it((func & ~0x3) | 2)
470
471     def _write_init(self, init):
472         self._write_it((init & ~0x3) | 3)
473
474     def _write_it(self, v):
475         s = ''.join((chr((v >> 16) & 0xff),
476                      chr((v >>  8) & 0xff),
477                      chr(v & 0xff)))
478         self._u._write_spi(0, self.spi_enable, self.spi_format, s)
479         
480     def _prescaler(self):
481         if self.P == 0:
482             return 8
483         elif self.P == 1:
484             return 16
485         elif self.P == 2:
486             return 32
487         elif self.P == 3:
488             return 64
489         else:
490             raise ValueError, "Prescaler out of range"
491
492 #----------------------------------------------------------------------
493 class _lo_common(_ADF410X_common):
494     def __init__(self):
495         _ADF410X_common.__init__(self)
496
497         # Band-specific R-Register Values
498         self.R_DIV = 4  # bits 15:2
499    
500         # Band-specific C-Register values
501         self.P = 0        # bits 23,22   0 = Div by 8/9
502         self.CP2 = 4      # bits 19:17
503         self.CP1 = 4      # bits 16:14
504
505         # Band specifc N-Register Values
506         self.DIVSEL = 0   # bit 23
507         self.DIV2 = 0     # bit 22
508         self.CPGAIN = 0   # bit 21
509         self.freq_mult = 1
510
511         self.div = 1
512         self.aux_div = 2
513
514     def freq_range(self):           # FIXME
515         return (50e6, 1000e6, 16e6)
516
517     def set_divider(self, main_or_aux, divisor):
518         if main_or_aux not in (0, 'main', 1, 'aux'):
519             raise ValueError, "main_or_aux must be 'main' or 'aux'"
520         if main_or_aux in (0, 'main'):
521             if divisor not in (1,2,4,8):
522                 raise ValueError, "Main Divider Must be 1, 2, 4, or 8"
523             for (div,val) in ((1,0),(2,1),(4,2),(8,3)):
524                 if(div == divisor):
525                     self.main_div = val
526         else:
527             if divisor not in (2,4,8,16):
528                 raise ValueError, "Aux Divider Must be 2, 4, 8 or 16"
529             for (div,val) in ((2,0),(4,1),(8,2),(16,3)):
530                 if(div == divisor):
531                     self.aux_div = val
532
533         vala = self.main_div*SELA0
534         valb = self.aux_div*SELB0
535         mask = SELA0|SELA1|SELB0|SELB1
536
537         self._rx_write_io(((self.main_div*SELA0) | (self.aux_div*SELB0)),
538                              (SELA0|SELA1|SELB0|SELB1))
539
540     def set_freq(self, freq):
541         #freq += self._lo_offset
542
543         if(freq < 20e6 or freq > 1200e6):
544             raise ValueError, "Requested frequency out of range"
545         div = 1
546         lo_freq = freq * 2
547         while lo_freq < 1e9 and div < 8:
548             div = div * 2
549             lo_freq = lo_freq * 2
550         print "For RF freq of %f, we set DIV=%d and LO Freq=%f" % (freq, div, lo_freq)
551         self.set_divider('main', div)
552         self.set_divider('aux', div*2)
553
554         R, N, control, actual_freq = self._compute_regs(lo_freq)
555         print "R %d N %d control %d actual freq %f" % (R,N,control,actual_freq)
556         if R==0:
557             return(False,0)
558         self._write_all(R, N, control)
559         return (self._lock_detect(), actual_freq/div/2)
560
561         
562 #------------------------------------------------------------    
563 class db_wbx_lo_tx(_lo_common, wbx_base_tx):
564     def __init__(self, usrp, which):
565         wbx_base_tx.__init__(self, usrp, which)
566         _lo_common.__init__(self)
567         
568     def gain_range(self):
569         """
570         Return range of gain that can be set by this d'board.
571
572         @returns (min_gain, max_gain, step_size)
573         Where gains are expressed in decibels (your mileage may vary)
574
575         Gain is controlled by a VGA in the output amplifier, not the PGA
576         """
577         return (-56, 0, 0.1)
578
579     def set_gain(self, gain):
580         """
581         Set the gain.
582         
583         @param gain:  gain in decibels
584         @returns True/False
585         """
586         maxgain = self.gain_range()[1]
587         mingain = self.gain_range()[0]
588         if gain > maxgain:
589             txvga_gain = maxgain
590         elif gain < mingain:
591             txvga_gain = mingain
592         else:
593             txvga_gain = gain
594
595         V_maxgain = 1.4
596         V_mingain = 0.1
597         V_fullscale = 3.3
598         dac_value = ((txvga_gain-mingain)*(V_maxgain-V_mingain)/(maxgain-mingain) + V_mingain)*4096/V_fullscale
599         assert dac_value>=0 and dac_value<4096
600         print "DAC value %d" % (dac_value,)
601         return self._u.write_aux_dac(self._which, 1, int(dac_value))
602
603 class db_wbx_lo_rx(_lo_common, wbx_base_rx):
604     def __init__(self, usrp, which):
605         wbx_base_rx.__init__(self, usrp, which)
606         _lo_common.__init__(self)
607
608     def gain_range(self):
609         """
610         Return range of gain that can be set by this d'board.
611         
612         @returns (min_gain, max_gain, step_size)
613         Where gains are expressed in decibels (your mileage may vary)
614         """
615         return (self._u.pga_min(), self._u.pga_max() + 45, 0.05)
616
617 #------------------------------------------------------------    
618 # hook these daughterboard classes into the auto-instantiation framework
619 db_instantiator.add(usrp_dbid.WBX_LO_TX, lambda usrp, which : (db_wbx_lo_tx(usrp, which),))
620 db_instantiator.add(usrp_dbid.WBX_LO_RX, lambda usrp, which : (db_wbx_lo_rx(usrp, which),))
621
622