new experimental daughterboards with other tuners
[debian/gnuradio] / gr-usrp / src / usrp.py
1 #
2 # Copyright 2004,2005,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 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
22
23
24 from usrpm import usrp_prims
25 from usrpm import usrp_dbid
26 from gnuradio import usrp1              # usrp Rev 1 and later
27 from gnuradio import gru
28 from usrpm.usrp_fpga_regs import *
29 import weakref
30
31 FPGA_MODE_NORMAL   = usrp1.FPGA_MODE_NORMAL
32 FPGA_MODE_LOOPBACK = usrp1.FPGA_MODE_LOOPBACK
33 FPGA_MODE_COUNTING = usrp1.FPGA_MODE_COUNTING
34
35 SPI_FMT_xSB_MASK = usrp1.SPI_FMT_xSB_MASK
36 SPI_FMT_LSB      = usrp1.SPI_FMT_LSB
37 SPI_FMT_MSB      = usrp1.SPI_FMT_MSB
38 SPI_FMT_HDR_MASK = usrp1.SPI_FMT_HDR_MASK
39 SPI_FMT_HDR_0    = usrp1.SPI_FMT_HDR_0
40 SPI_FMT_HDR_1    = usrp1.SPI_FMT_HDR_1
41 SPI_FMT_HDR_2    = usrp1.SPI_FMT_HDR_2
42
43 SPI_ENABLE_FPGA     = usrp1.SPI_ENABLE_FPGA
44 SPI_ENABLE_CODEC_A  = usrp1.SPI_ENABLE_CODEC_A
45 SPI_ENABLE_CODEC_B  = usrp1.SPI_ENABLE_CODEC_B
46 SPI_ENABLE_reserved = usrp1.SPI_ENABLE_reserved
47 SPI_ENABLE_TX_A     = usrp1.SPI_ENABLE_TX_A
48 SPI_ENABLE_RX_A     = usrp1.SPI_ENABLE_RX_A
49 SPI_ENABLE_TX_B     = usrp1.SPI_ENABLE_TX_B
50 SPI_ENABLE_RX_B     = usrp1.SPI_ENABLE_RX_B
51
52
53 # Import all the daughterboard classes we know about.
54 # This hooks them into the auto-instantiation framework.
55
56 import db_instantiator
57
58 import db_basic
59 import db_dbs_rx
60 import db_flexrf
61 import db_flexrf_mimo
62 import db_tv_rx
63 import db_wbx
64 import db_xcvr2450
65 import db_dtt754
66 import db_dtt768
67
68 def _look_for_usrp(which):
69     """
70     Try to open the specified usrp.
71
72     @param which: int >= 0 specifying which USRP to open
73     @type which: int
74     
75     @return: Returns version number, or raises RuntimeError
76     @rtype: int
77     """
78     d = usrp_prims.usrp_find_device(which)
79     if not d:
80         raise RuntimeError, "Unable to find USRP #%d" % (which,)
81
82     return usrp_prims.usrp_hw_rev(d)
83
84
85 def _ensure_rev2(which):
86     v = _look_for_usrp(which)
87     if not v in (2, 4):
88         raise RuntimeError, "Sorry, unsupported USRP revision (rev=%d)" % (v,)
89
90
91 class tune_result(object):
92     """
93     Container for intermediate tuning information.
94     """
95     def __init__(self, baseband_freq, dxc_freq, residual_freq, inverted):
96         self.baseband_freq = baseband_freq
97         self.dxc_freq = dxc_freq
98         self.residual_freq = residual_freq
99         self.inverted = inverted
100
101
102 def tune(u, chan, subdev, target_freq):
103     """
104     Set the center frequency we're interested in.
105
106     @param u: instance of usrp.source_* or usrp.sink_*
107     @param chan: DDC/DUC channel
108     @type  chan: int
109     @param subdev: daughterboard subdevice
110     @param target_freq: frequency in Hz
111     @returns False if failure else tune_result
112     
113     Tuning is a two step process.  First we ask the front-end to
114     tune as close to the desired frequency as it can.  Then we use
115     the result of that operation and our target_frequency to
116     determine the value for the digital down converter.
117     """
118
119     # Does this usrp instance do Tx or Rx?
120     rx_p = True
121     try:
122         u.rx_freq
123     except AttributeError:
124         rx_p = False
125
126     ok, baseband_freq = subdev.set_freq(target_freq)
127     dxc_freq, inverted = calc_dxc_freq(target_freq, baseband_freq, u.converter_rate())
128
129     # If the spectrum is inverted, and the daughterboard doesn't do
130     # quadrature downconversion, we can fix the inversion by flipping the
131     # sign of the dxc_freq...  (This only happens using the basic_rx board)
132
133     if subdev.spectrum_inverted():
134         inverted = not(inverted)
135     
136     if inverted and not(subdev.is_quadrature()):
137         dxc_freq = -dxc_freq
138         inverted = not(inverted)
139
140     if rx_p:
141         ok = ok and u.set_rx_freq(chan, dxc_freq)
142     else:
143         dxc_freq = -dxc_freq
144         ok = ok and u.set_tx_freq(chan, dxc_freq)
145         
146     if not(ok):
147         return False
148
149     # residual_freq is the offset left over because of dxc tuning step size
150     if rx_p:
151         residual_freq = dxc_freq - u.rx_freq(chan)
152     else:
153         # FIXME 50-50 chance this has the wrong sign...
154         residual_freq = dxc_freq - u.tx_freq(chan)
155
156     return tune_result(baseband_freq, dxc_freq, residual_freq, inverted)
157     
158     
159 # ------------------------------------------------------------------------
160 # Build subclasses of raw usrp1.* class that add the db attribute
161 # by automatically instantiating the appropriate daughterboard classes.
162 # [Also provides keyword args.]
163 # ------------------------------------------------------------------------
164
165 class usrp_common(object):
166     def __init__(self):
167         # read capability register
168         r = self._u._read_fpga_reg(FR_RB_CAPS)
169         if r < 0:
170             r += 2**32
171         if r == 0xaa55ff77:    # value of this reg prior to being defined as cap reg
172             r = ((2 << bmFR_RB_CAPS_NDUC_SHIFT)
173                  | (2 << bmFR_RB_CAPS_NDDC_SHIFT)
174                  | bmFR_RB_CAPS_RX_HAS_HALFBAND)
175         self._fpga_caps = r
176
177         if False:
178             print "FR_RB_CAPS      = %#08x" % (self._fpga_caps,)
179             print "has_rx_halfband =", self.has_rx_halfband()
180             print "nDDCs           =", self.nddc()
181             print "has_tx_halfband =", self.has_tx_halfband()
182             print "nDUCs           =", self.nduc()
183     
184     def __getattr__(self, name):
185         return getattr(self._u, name)
186
187     def tune(self, chan, subdev, target_freq):
188         return tune(self, chan, subdev, target_freq)
189
190     def has_rx_halfband(self):
191         return self._fpga_caps & bmFR_RB_CAPS_RX_HAS_HALFBAND != 0
192
193     def has_tx_halfband(self):
194         return self._fpga_caps & bmFR_RB_CAPS_TX_HAS_HALFBAND != 0
195
196     def nddc(self):
197         """
198         Number of Digital Down Converters implemented in FPGA
199         """
200         return (self._fpga_caps & bmFR_RB_CAPS_NDDC_MASK) >> bmFR_RB_CAPS_NDDC_SHIFT
201
202     def nduc(self):
203         """
204         Number of Digital Up Converters implemented in FPGA
205         """
206         return (self._fpga_caps & bmFR_RB_CAPS_NDUC_MASK) >> bmFR_RB_CAPS_NDUC_SHIFT
207
208
209 class sink_c(usrp_common):
210     def __init__(self, which=0, interp_rate=128, nchan=1, mux=0x98,
211                  fusb_block_size=0, fusb_nblocks=0,
212                  fpga_filename="", firmware_filename=""):
213         _ensure_rev2(which)
214         self._u = usrp1.sink_c(which, interp_rate, nchan, mux,
215                                fusb_block_size, fusb_nblocks,
216                                fpga_filename, firmware_filename)
217         # Add the db attribute, which contains a 2-tuple of tuples of daughterboard classes
218         self.db = (db_instantiator.instantiate(self._u, 0),
219                    db_instantiator.instantiate(self._u, 1))
220         usrp_common.__init__(self)
221
222     def __del__(self):
223         self.db = None          # will fire d'board destructors
224         self._u = None          # will fire usrp1.* destructor
225
226
227 class sink_s(usrp_common):
228     def __init__(self, which=0, interp_rate=128, nchan=1, mux=0x98,
229                  fusb_block_size=0, fusb_nblocks=0,
230                  fpga_filename="", firmware_filename=""):
231         _ensure_rev2(which)
232         self._u = usrp1.sink_s(which, interp_rate, nchan, mux,
233                                fusb_block_size, fusb_nblocks,
234                                fpga_filename, firmware_filename)
235         # Add the db attribute, which contains a 2-tuple of tuples of daughterboard classes
236         self.db = (db_instantiator.instantiate(self._u, 0),
237                    db_instantiator.instantiate(self._u, 1))
238         usrp_common.__init__(self)
239
240     def __del__(self):
241         self.db = None          # will fire d'board destructors
242         self._u = None          # will fire usrp1.* destructor
243         
244
245 class source_c(usrp_common):
246     def __init__(self, which=0, decim_rate=64, nchan=1, mux=0x32103210, mode=0,
247                  fusb_block_size=0, fusb_nblocks=0,
248                  fpga_filename="", firmware_filename=""):
249         _ensure_rev2(which)
250         self._u = usrp1.source_c(which, decim_rate, nchan, mux, mode,
251                                  fusb_block_size, fusb_nblocks,
252                                  fpga_filename, firmware_filename)
253         # Add the db attribute, which contains a 2-tuple of tuples of daughterboard classes
254         self.db = (db_instantiator.instantiate(self._u, 0),
255                    db_instantiator.instantiate(self._u, 1))
256         usrp_common.__init__(self)
257
258     def __del__(self):
259         self.db = None          # will fire d'board destructors
260         self._u = None          # will fire usrp1.* destructor
261
262
263 class source_s(usrp_common):
264     def __init__(self, which=0, decim_rate=64, nchan=1, mux=0x32103210, mode=0,
265                  fusb_block_size=0, fusb_nblocks=0,
266                  fpga_filename="", firmware_filename=""):
267         _ensure_rev2(which)
268         self._u = usrp1.source_s(which, decim_rate, nchan, mux, mode,
269                                  fusb_block_size, fusb_nblocks,
270                                  fpga_filename, firmware_filename)
271         # Add the db attribute, which contains a 2-tuple of tuples of daughterboard classes
272         self.db = (db_instantiator.instantiate(self._u, 0),
273                    db_instantiator.instantiate(self._u, 1))
274         usrp_common.__init__(self)
275
276     def __del__(self):
277         self.db = None          # will fire d'board destructors
278         self._u = None          # will fire usrp1.* destructor
279         
280
281 # ------------------------------------------------------------------------
282 #                               utilities
283 # ------------------------------------------------------------------------
284
285 def determine_rx_mux_value(u, subdev_spec):
286     """
287     Determine appropriate Rx mux value as a function of the subdevice choosen and the
288     characteristics of the respective daughterboard.
289
290     @param u:           instance of USRP source
291     @param subdev_spec: return value from subdev option parser.  
292     @type  subdev_spec: (side, subdev), where side is 0 or 1 and subdev is 0 or 1
293     @returns:           the Rx mux value
294     """
295     # Figure out which A/D's to connect to the DDC.
296     #
297     # Each daughterboard consists of 1 or 2 subdevices.  (At this time,
298     # all but the Basic Rx have a single subdevice.  The Basic Rx
299     # has two independent channels, treated as separate subdevices).
300     # subdevice 0 of a daughterboard may use 1 or 2 A/D's.  We determine this
301     # by checking the is_quadrature() method.  If subdevice 0 uses only a single
302     # A/D, it's possible that the daughterboard has a second subdevice, subdevice 1,
303     # and it uses the second A/D.
304     #
305     # If the card uses only a single A/D, we wire a zero into the DDC Q input.
306     #
307     # (side, 0) says connect only the A/D's used by subdevice 0 to the DDC.
308     # (side, 1) says connect only the A/D's used by subdevice 1 to the DDC.
309     #
310
311     side = subdev_spec[0]  # side A = 0, side B = 1
312
313     if not(side in (0, 1)):
314         raise ValueError, "Invalid subdev_spec: %r:" % (subdev_spec,)
315
316     db = u.db[side]        # This is a tuple of length 1 or 2 containing the subdevice
317                            #   classes for the selected side.
318     
319     # compute bitmasks of used A/D's
320     
321     if db[0].is_quadrature():
322         subdev0_uses = 0x3              # uses A/D 0 and 1
323     else:
324         subdev0_uses = 0x1              # uses A/D 0 only
325
326     if len(db) > 1:
327         subdev1_uses = 0x2              # uses A/D 1 only
328     else:
329         subdev1_uses = 0x0              # uses no A/D (doesn't exist)
330
331     if subdev_spec[1] == 0:
332         uses = subdev0_uses
333     elif subdev_spec[1] == 1:
334         uses = subdev1_uses
335     else:
336         raise ValueError, "Invalid subdev_spec: %r: " % (subdev_spec,)
337     
338     if uses == 0:
339         raise RuntimeError, "Daughterboard doesn't have a subdevice 1: %r: " % (subdev_spec,)
340
341     swap_iq = db[0].i_and_q_swapped()
342     
343     truth_table = {
344         # (side, uses, swap_iq) : mux_val
345         (0, 0x1, False) : 0xf0f0f0f0,
346         (0, 0x2, False) : 0xf0f0f0f1,
347         (0, 0x3, False) : 0x00000010,
348         (0, 0x3, True)  : 0x00000001,
349         (1, 0x1, False) : 0xf0f0f0f2,
350         (1, 0x2, False) : 0xf0f0f0f3,
351         (1, 0x3, False) : 0x00000032,
352         (1, 0x3, True)  : 0x00000023
353         }
354
355     return gru.hexint(truth_table[(side, uses, swap_iq)])
356
357
358 def determine_tx_mux_value(u, subdev_spec):
359     """
360     Determine appropriate Tx mux value as a function of the subdevice choosen.
361
362     @param u:           instance of USRP source
363     @param subdev_spec: return value from subdev option parser.  
364     @type  subdev_spec: (side, subdev), where side is 0 or 1 and subdev is 0
365     @returns:           the Rx mux value
366     """
367     # This is simpler than the rx case.  Either you want to talk
368     # to side A or side B.  If you want to talk to both sides at once,
369     # determine the value manually.
370
371     side = subdev_spec[0]  # side A = 0, side B = 1
372     if not(side in (0, 1)):
373         raise ValueError, "Invalid subdev_spec: %r:" % (subdev_spec,)
374
375     db = u.db[side]
376
377     if(db[0].i_and_q_swapped()):
378         return gru.hexint([0x0089, 0x8900][side])
379     else:
380         return gru.hexint([0x0098, 0x9800][side])
381
382
383 def selected_subdev(u, subdev_spec):
384     """
385     Return the user specified daughterboard subdevice.
386
387     @param u: an instance of usrp.source_* or usrp.sink_*
388     @param subdev_spec: return value from subdev option parser.  
389     @type  subdev_spec: (side, subdev), where side is 0 or 1 and subdev is 0 or 1
390     @returns: an weakref to an instance derived from db_base
391     """
392     side, subdev = subdev_spec
393     # Note: This allows db to go out of scope at the right time
394     return weakref.proxy(u.db[side][subdev])
395
396
397 def calc_dxc_freq(target_freq, baseband_freq, fs):
398     """
399     Calculate the frequency to use for setting the digital up or down converter.
400     
401     @param target_freq: desired RF frequency (Hz)
402     @type  target_freq: number
403     @param baseband_freq: the RF frequency that corresponds to DC in the IF.
404     @type  baseband_freq: number
405     @param fs: converter sample rate
406     @type  fs: number
407     
408     @returns: 2-tuple (ddc_freq, inverted) where ddc_freq is the value
409        for the ddc and inverted is True if we're operating in an inverted
410        Nyquist zone.
411     """
412
413     delta = target_freq - baseband_freq
414
415     if delta >= 0:
416         while delta > fs:
417             delta -= fs
418         if delta <= fs/2:
419             return (-delta, False)        # non-inverted region
420         else:
421             return (delta - fs, True)     # inverted region
422     else:
423         while delta < -fs:
424             delta += fs
425         if delta >= -fs/2:
426             return (-delta, False)        # non-inverted region
427         else:
428             return (delta + fs, True)     # inverted region
429     
430     
431 # ------------------------------------------------------------------------
432 #                              Utilities
433 # ------------------------------------------------------------------------
434
435 def pick_tx_subdevice(u):
436     """
437     The user didn't specify a tx subdevice on the command line.
438     Try for one of these, in order: FLEX_400, FLEX_900, FLEX_1200, FLEX_2400,
439     BASIC_TX, whatever's on side A.
440
441     @return a subdev_spec
442     """
443     return pick_subdev(u, (usrp_dbid.FLEX_400_TX,
444                            usrp_dbid.FLEX_900_TX,
445                            usrp_dbid.FLEX_1200_TX,
446                            usrp_dbid.FLEX_2400_TX,
447                            usrp_dbid.BASIC_TX))
448
449 def pick_rx_subdevice(u):
450     """
451     The user didn't specify an rx subdevice on the command line.
452     Try for one of these, in order: FLEX_400, FLEX_900, FLEX_1200, FLEX_2400,
453     TV_RX, DBS_RX, BASIC_RX, whatever's on side A.
454
455     @return a subdev_spec
456     """
457     return pick_subdev(u, (usrp_dbid.FLEX_400_RX,
458                            usrp_dbid.FLEX_900_RX,
459                            usrp_dbid.FLEX_1200_RX,
460                            usrp_dbid.FLEX_2400_RX,
461                            usrp_dbid.TV_RX,
462                            usrp_dbid.TV_RX_REV_2,
463                            usrp_dbid.DBS_RX,
464                            usrp_dbid.DBS_RX_REV_2_1,
465                            usrp_dbid.BASIC_RX))
466
467 def pick_subdev(u, candidates):
468     """
469     @param u:          usrp instance
470     @param candidates: list of dbids
471     @returns: subdev specification
472     """
473     db0 = u.db[0][0].dbid()
474     db1 = u.db[1][0].dbid()
475     for c in candidates:
476         if c == db0: return (0, 0)
477         if c == db1: return (1, 0)
478     if db0 >= 0:
479         return (0, 0)
480     if db1 >= 0:
481         return (1, 0)
482     raise RuntimeError, "No suitable daughterboard found!"
483