2 # Copyright 2004,2005,2007 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 *
31 FPGA_MODE_NORMAL = usrp1.FPGA_MODE_NORMAL
32 FPGA_MODE_LOOPBACK = usrp1.FPGA_MODE_LOOPBACK
33 FPGA_MODE_COUNTING = usrp1.FPGA_MODE_COUNTING
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
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
53 # Import all the daughterboard classes we know about.
54 # This hooks them into the auto-instantiation framework.
56 import db_instantiator
68 def _look_for_usrp(which):
70 Try to open the specified usrp.
72 @param which: int >= 0 specifying which USRP to open
75 @return: Returns version number, or raises RuntimeError
78 d = usrp_prims.usrp_find_device(which)
80 raise RuntimeError, "Unable to find USRP #%d" % (which,)
82 return usrp_prims.usrp_hw_rev(d)
85 def _ensure_rev2(which):
86 v = _look_for_usrp(which)
88 raise RuntimeError, "Sorry, unsupported USRP revision (rev=%d)" % (v,)
91 class tune_result(object):
93 Container for intermediate tuning information.
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
102 def tune(u, chan, subdev, target_freq):
104 Set the center frequency we're interested in.
106 @param u: instance of usrp.source_* or usrp.sink_*
107 @param chan: DDC/DUC channel
109 @param subdev: daughterboard subdevice
110 @param target_freq: frequency in Hz
111 @returns False if failure else tune_result
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.
119 # Does this usrp instance do Tx or Rx?
123 except AttributeError:
126 ok, baseband_freq = subdev.set_freq(target_freq)
127 dxc_freq, inverted = calc_dxc_freq(target_freq, baseband_freq, u.converter_rate())
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)
133 if subdev.spectrum_inverted():
134 inverted = not(inverted)
136 if inverted and not(subdev.is_quadrature()):
138 inverted = not(inverted)
141 ok = ok and u.set_rx_freq(chan, dxc_freq)
144 ok = ok and u.set_tx_freq(chan, dxc_freq)
149 # residual_freq is the offset left over because of dxc tuning step size
151 residual_freq = dxc_freq - u.rx_freq(chan)
153 # FIXME 50-50 chance this has the wrong sign...
154 residual_freq = dxc_freq - u.tx_freq(chan)
156 return tune_result(baseband_freq, dxc_freq, residual_freq, inverted)
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 # ------------------------------------------------------------------------
165 class usrp_common(object):
167 # read capability register
168 r = self._u._read_fpga_reg(FR_RB_CAPS)
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)
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()
184 def __getattr__(self, name):
185 return getattr(self._u, name)
187 def tune(self, chan, subdev, target_freq):
188 return tune(self, chan, subdev, target_freq)
190 def has_rx_halfband(self):
191 return self._fpga_caps & bmFR_RB_CAPS_RX_HAS_HALFBAND != 0
193 def has_tx_halfband(self):
194 return self._fpga_caps & bmFR_RB_CAPS_TX_HAS_HALFBAND != 0
198 Number of Digital Down Converters implemented in FPGA
200 return (self._fpga_caps & bmFR_RB_CAPS_NDDC_MASK) >> bmFR_RB_CAPS_NDDC_SHIFT
204 Number of Digital Up Converters implemented in FPGA
206 return (self._fpga_caps & bmFR_RB_CAPS_NDUC_MASK) >> bmFR_RB_CAPS_NDUC_SHIFT
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=""):
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)
223 self.db = None # will fire d'board destructors
224 self._u = None # will fire usrp1.* destructor
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=""):
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)
241 self.db = None # will fire d'board destructors
242 self._u = None # will fire usrp1.* destructor
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=""):
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)
259 self.db = None # will fire d'board destructors
260 self._u = None # will fire usrp1.* destructor
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=""):
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)
277 self.db = None # will fire d'board destructors
278 self._u = None # will fire usrp1.* destructor
281 # ------------------------------------------------------------------------
283 # ------------------------------------------------------------------------
285 def determine_rx_mux_value(u, subdev_spec):
287 Determine appropriate Rx mux value as a function of the subdevice choosen and the
288 characteristics of the respective daughterboard.
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
295 # Figure out which A/D's to connect to the DDC.
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.
305 # If the card uses only a single A/D, we wire a zero into the DDC Q input.
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.
311 side = subdev_spec[0] # side A = 0, side B = 1
313 if not(side in (0, 1)):
314 raise ValueError, "Invalid subdev_spec: %r:" % (subdev_spec,)
316 db = u.db[side] # This is a tuple of length 1 or 2 containing the subdevice
317 # classes for the selected side.
319 # compute bitmasks of used A/D's
321 if db[0].is_quadrature():
322 subdev0_uses = 0x3 # uses A/D 0 and 1
324 subdev0_uses = 0x1 # uses A/D 0 only
327 subdev1_uses = 0x2 # uses A/D 1 only
329 subdev1_uses = 0x0 # uses no A/D (doesn't exist)
331 if subdev_spec[1] == 0:
333 elif subdev_spec[1] == 1:
336 raise ValueError, "Invalid subdev_spec: %r: " % (subdev_spec,)
339 raise RuntimeError, "Daughterboard doesn't have a subdevice 1: %r: " % (subdev_spec,)
341 swap_iq = db[0].i_and_q_swapped()
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
355 return gru.hexint(truth_table[(side, uses, swap_iq)])
358 def determine_tx_mux_value(u, subdev_spec):
360 Determine appropriate Tx mux value as a function of the subdevice choosen.
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
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.
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,)
377 if(db[0].i_and_q_swapped()):
378 return gru.hexint([0x0089, 0x8900][side])
380 return gru.hexint([0x0098, 0x9800][side])
383 def selected_subdev(u, subdev_spec):
385 Return the user specified daughterboard subdevice.
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
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])
397 def calc_dxc_freq(target_freq, baseband_freq, fs):
399 Calculate the frequency to use for setting the digital up or down converter.
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
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
413 delta = target_freq - baseband_freq
419 return (-delta, False) # non-inverted region
421 return (delta - fs, True) # inverted region
426 return (-delta, False) # non-inverted region
428 return (delta + fs, True) # inverted region
431 # ------------------------------------------------------------------------
433 # ------------------------------------------------------------------------
435 def pick_tx_subdevice(u):
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.
441 @return a subdev_spec
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,
449 def pick_rx_subdevice(u):
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.
455 @return a subdev_spec
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,
462 usrp_dbid.TV_RX_REV_2,
464 usrp_dbid.DBS_RX_REV_2_1,
467 def pick_subdev(u, candidates):
469 @param u: usrp instance
470 @param candidates: list of dbids
471 @returns: subdev specification
473 db0 = u.db[0][0].dbid()
474 db1 = u.db[1][0].dbid()
476 if c == db0: return (0, 0)
477 if c == db1: return (1, 0)
482 raise RuntimeError, "No suitable daughterboard found!"