2 # Copyright 2004,2005 Free Software Foundation, Inc.
4 # This file is part of GNU Radio
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)
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.
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.
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 *
30 FPGA_MODE_NORMAL = usrp1.FPGA_MODE_NORMAL
31 FPGA_MODE_LOOPBACK = usrp1.FPGA_MODE_LOOPBACK
32 FPGA_MODE_COUNTING = usrp1.FPGA_MODE_COUNTING
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
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
52 # Import all the daughterboard classes we know about.
53 # This hooks them into the auto-instantiation framework.
55 import db_instantiator
65 def _look_for_usrp(which):
67 Try to open the specified usrp.
69 @param which: int >= 0 specifying which USRP to open
72 @return: Returns version number, or raises RuntimeError
75 d = usrp_prims.usrp_find_device(which)
77 raise RuntimeError, "Unable to find USRP #%d" % (which,)
79 return usrp_prims.usrp_hw_rev(d)
82 def _ensure_rev2(which):
83 v = _look_for_usrp(which)
85 raise RuntimeError, "Sorry, unsupported USRP revision (rev=%d)" % (v,)
88 class tune_result(object):
90 Container for intermediate tuning information.
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
99 def tune(u, chan, subdev, target_freq):
101 Set the center frequency we're interested in.
103 @param u: instance of usrp.source_* or usrp.sink_*
104 @param chan: DDC/DUC channel
106 @param subdev: daughterboard subdevice
107 @param target_freq: frequency in Hz
108 @returns False if failure else tune_result
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.
116 # Does this usrp instance do Tx or Rx?
120 except AttributeError:
123 ok, baseband_freq = subdev.set_freq(target_freq)
124 dxc_freq, inverted = calc_dxc_freq(target_freq, baseband_freq, u.converter_rate())
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)
130 if subdev.spectrum_inverted():
131 inverted = not(inverted)
133 if inverted and not(subdev.is_quadrature()):
135 inverted = not(inverted)
138 ok = ok and u.set_rx_freq(chan, dxc_freq)
141 ok = ok and u.set_tx_freq(chan, dxc_freq)
146 # residual_freq is the offset left over because of dxc tuning step size
148 residual_freq = dxc_freq - u.rx_freq(chan)
150 # FIXME 50-50 chance this has the wrong sign...
151 residual_freq = dxc_freq - u.tx_freq(chan)
153 return tune_result(baseband_freq, dxc_freq, residual_freq, inverted)
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 # ------------------------------------------------------------------------
162 class usrp_common(object):
164 # read capability register
165 r = self._u._read_fpga_reg(FR_RB_CAPS)
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)
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()
181 def __getattr__(self, name):
182 return getattr(self._u, name)
184 def tune(self, chan, subdev, target_freq):
185 return tune(self, chan, subdev, target_freq)
187 def has_rx_halfband(self):
188 return self._fpga_caps & bmFR_RB_CAPS_RX_HAS_HALFBAND != 0
190 def has_tx_halfband(self):
191 return self._fpga_caps & bmFR_RB_CAPS_TX_HAS_HALFBAND != 0
195 Number of Digital Down Converters implemented in FPGA
197 return (self._fpga_caps & bmFR_RB_CAPS_NDDC_MASK) >> bmFR_RB_CAPS_NDDC_SHIFT
201 Number of Digital Up Converters implemented in FPGA
203 return (self._fpga_caps & bmFR_RB_CAPS_NDUC_MASK) >> bmFR_RB_CAPS_NDUC_SHIFT
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=""):
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)
220 self.db = None # will fire d'board destructors
221 self._u = None # will fire usrp1.* destructor
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=""):
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)
238 self.db = None # will fire d'board destructors
239 self._u = None # will fire usrp1.* destructor
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=""):
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)
256 self.db = None # will fire d'board destructors
257 self._u = None # will fire usrp1.* destructor
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=""):
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)
274 self.db = None # will fire d'board destructors
275 self._u = None # will fire usrp1.* destructor
278 # ------------------------------------------------------------------------
280 # ------------------------------------------------------------------------
282 def determine_rx_mux_value(u, subdev_spec):
284 Determine appropriate Rx mux value as a function of the subdevice choosen and the
285 characteristics of the respective daughterboard.
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
292 # Figure out which A/D's to connect to the DDC.
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.
302 # If the card uses only a single A/D, we wire a zero into the DDC Q input.
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.
308 side = subdev_spec[0] # side A = 0, side B = 1
310 if not(side in (0, 1)):
311 raise ValueError, "Invalid subdev_spec: %r:" % (subdev_spec,)
313 db = u.db[side] # This is a tuple of length 1 or 2 containing the subdevice
314 # classes for the selected side.
316 # compute bitmasks of used A/D's
318 if db[0].is_quadrature():
319 subdev0_uses = 0x3 # uses A/D 0 and 1
321 subdev0_uses = 0x1 # uses A/D 0 only
324 subdev1_uses = 0x2 # uses A/D 1 only
326 subdev1_uses = 0x0 # uses no A/D (doesn't exist)
328 if subdev_spec[1] == 0:
330 elif subdev_spec[1] == 1:
333 raise ValueError, "Invalid subdev_spec: %r: " % (subdev_spec,)
336 raise RuntimeError, "Daughterboard doesn't have a subdevice 1: %r: " % (subdev_spec,)
338 swap_iq = db[0].i_and_q_swapped()
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
352 return gru.hexint(truth_table[(side, uses, swap_iq)])
355 def determine_tx_mux_value(u, subdev_spec):
357 Determine appropriate Tx mux value as a function of the subdevice choosen.
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
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.
368 side = subdev_spec[0] # side A = 0, side B = 1
370 if not(side in (0, 1)):
371 raise ValueError, "Invalid subdev_spec: %r:" % (subdev_spec,)
373 return gru.hexint([0x0098, 0x9800][side])
376 def selected_subdev(u, subdev_spec):
378 Return the user specified daughterboard subdevice.
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
385 side, subdev = subdev_spec
386 return u.db[side][subdev]
389 def calc_dxc_freq(target_freq, baseband_freq, fs):
391 Calculate the frequency to use for setting the digital up or down converter.
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
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
405 delta = target_freq - baseband_freq
411 return (-delta, False) # non-inverted region
413 return (delta - fs, True) # inverted region
418 return (-delta, False) # non-inverted region
420 return (delta + fs, True) # inverted region
423 # ------------------------------------------------------------------------
425 # ------------------------------------------------------------------------
427 def pick_tx_subdevice(u):
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.
433 @return a subdev_spec
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,
441 def pick_rx_subdevice(u):
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.
447 @return a subdev_spec
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,
454 usrp_dbid.TV_RX_REV_2,
456 usrp_dbid.DBS_RX_REV_2_1,
459 def pick_subdev(u, candidates):
461 @param u: usrp instance
462 @param candidates: list of dbids
463 @returns: subdev specification
465 db0 = u.db[0][0].dbid()
466 db1 = u.db[1][0].dbid()
468 if c == db0: return (0, 0)
469 if c == db1: return (1, 0)
474 raise RuntimeError, "No suitable daughterboard found!"