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