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