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