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