diff options
author | jcorgan | 2006-08-03 04:51:51 +0000 |
---|---|---|
committer | jcorgan | 2006-08-03 04:51:51 +0000 |
commit | 5d69a524f81f234b3fbc41d49ba18d6f6886baba (patch) | |
tree | b71312bf7f1e8d10fef0f3ac6f28784065e73e72 /gr-usrp | |
download | gnuradio-5d69a524f81f234b3fbc41d49ba18d6f6886baba.tar.gz gnuradio-5d69a524f81f234b3fbc41d49ba18d6f6886baba.tar.bz2 gnuradio-5d69a524f81f234b3fbc41d49ba18d6f6886baba.zip |
Houston, we have a trunk.
git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@3122 221aa14e-8319-0410-a670-987f0aec2ac5
Diffstat (limited to 'gr-usrp')
31 files changed, 7043 insertions, 0 deletions
diff --git a/gr-usrp/AUTHORS b/gr-usrp/AUTHORS new file mode 100644 index 000000000..ee4560a55 --- /dev/null +++ b/gr-usrp/AUTHORS @@ -0,0 +1 @@ +Eric Blossom <eb@comsec.com> diff --git a/gr-usrp/ChangeLog b/gr-usrp/ChangeLog new file mode 100644 index 000000000..9744d3972 --- /dev/null +++ b/gr-usrp/ChangeLog @@ -0,0 +1,374 @@ +2006-06-17 Eric Blossom <eb@comsec.com> + + * src/usrp.py, src/usrp1.i, src/usrp1_sink_base.cc, src/usrp1_sink_base.h, + src/usrp1_sink_c.cc, src/usrp1_sink_c.h, src/usrp1_sink_s.cc, + src/usrp1_sink_s.h, src/usrp1_source_base.cc, src/usrp1_source_base.h, + src/usrp1_source_c.cc, src/usrp1_source_c.h, src/usrp1_source_s.cc, + src/usrp1_source_s.h: changed constructor args to add + fusb_block_size and fusb_nblocks so that the application can + adjust the amount of buffering being done. [This was an awful lot + of files to have to touch to make this change. There must be an + easier way?] + + +2006-05-11 Martin Dudok van Heel <nldudok1 at olifantasia dot com> + Added synchronised multi_usrp support. + This work was funded by Toby Oliver at Sensus Analytics / Path Intelligence. + + See README_MULTI_USRP.txt and the multi_usrp examples on how to use. + In short: + Connect the 64MHz clocks between the boards with a short sma coax cable. + (See the wiki on how to enable clock-out and clock-in + http://comsec.com/wiki?USRPClockingNotes ) + Connect io15 on the RXA daughterboards of both usrps + instantiate multi=usrp_multi.multi_source_align([args]) + The 4 aligned channels become available as: + (multi.get_master_source_c(),1) (multi.get_master_source_c(),2) + (multi.get_slave_source(),1) (multi.get_slave_source(),2) + call multi.sync() at least once AFTER the flowgraph has started running + + * READMU_MULTI_USRP.txt: new + * configure.ac: added missing newline at end of file + * src/Makefile.am: added usrp_multi.py + * src/usrp1.i: added _write_fpga_reg_masked + * src/usrp1_source_base.[cc,h]: added _write_fpga_reg_masked + * src/usrp_multi.py: new Instantiate a usrp_multi.multi_source_align to + get aligned streams from two usrps. + +2006-03-11 Matt Ettus <matt@ettus.com> + + * src/Makefile.am, src/db_flexrf_mimo.py, src/usrp.py: New skeleton + file for mimo mode with the flexrf boards + + * src/db_base.py: added standard code to control refclock and + adc buffer bypass so all dboards can do it the same way. Taken from + db_dbs_rx.py + + * src/db_tv_rx.py: Use standard method for adc buffer bypass + + * src/db_flexrf.py: Use standard method for adc buffer bypass + + * src/db_dbs_rx.py: Use standard methods for adc buffer bypass and + refclock control + + * src/db_basic.py: Use standard method for adc buffer bypass, + and instantiate a BasicTX when the unknown or missing board is on + the TX side + +2006-03-10 Eric Blossom <eb@comsec.com> + + * src/db_dbs_rx.py (db_dbs_rx._refclk_freq): replaced 64e6 with + call to fpga_master_clock_freq(). + +2006-02-18 Eric Blossom <eb@comsec.com> + + * src/usrp1_{sink,source}_{base,c,s}.{h,cc}, src/usrp1.i, + src/usrp.py: added support for specifying the firmware and fpga + files that are to be loaded. + +2006-02-17 Eric Blossom <eb@comsec.com> + + * src/usrp1.i, src/usrp1_{sink,source}_base.{h,cc}: added serial_number() + +2006-01-30 Eric Blossom <eb@comsec.com> + + * src/db_base.py, src/db_flexrf.py: revised to use new auto t/r + switching strategy. FR_ATR_CTL no longer exists. We control auto + t/r via the FR_ATR_MASK* registers. + +2006-01-25 Eric Blossom <eb@comsec.com> + + * src/usrp.py (usrp_common): added code to read FPGA capability register. + (determine_tx_mux_value): new utility. + +2006-01-22 Eric Blossom <eb@comsec.com> + + * src/usrp.py (pick_rx_subdevice): added latest d'board revs to list. + +2006-01-04 Eric Blossom <eb@comsec.com> + + * src/usrp1_{sink,source}_{s,c}.cc: added usrp_bytesex.h include and + use usrp_to_host_short or host_to_usrp_short as appropriate to + handle usrp to host endianness differences. + + * configure.ac: added AC_C_BIGENDIAN and header check for byteswap.h + +2005-12-20 Matt Ettus <matt@ettus.com> + + * src/db_base.py (db_base.spectrum_inverted): Base function + defaults to no spectrum inversion, so we can handle daughterboards + which have analog spectra which are inverted (currently only tvrx2) + + * src/db_tv_rx.py (db_tv_rx.spectrum_inverted): Report that the + tvrx2 has inverted spectrum, the tvrx does not + + * src/usrp.py (tune): Handle daughterboards which have analog + spectra which are inverted (currently only tvrx2) + + +2005-12-08 Eric Blossom <eb@comsec.com> + + * src/usrp1_sink_s.cc (usrp1_sink_s): call set_output_multiple so + we ensure 512 byte writes across USB. + +2005-12-07 Eric Blossom <eb@comsec.com> + + * src/usrp.py: revised sink_c, sink_s, source_c and source_s to + properly fire daughterboard destructors. Without this, we had the + nasty habit of leaving the transmitter running if the user didn't + explicitly disable it. + +2005-12-05 Eric Blossom <eb@comsec.com> + + * src/db_base.py, src/db_flexrf.py: refactored to use new + Auto T/R switching. + + * src/db_flexrf.py (flexrf_base.set_freq): Offset the LO by 4 MHz. + Helps initial lock-up time with discontinuous transmission. + +2005-11-22 Eric Blossom <eb@comsec.com> + + * src/db_base.py, src/db_flexrf.py: renamed set_auto_tx to set_auto_tr. + +2005-11-17 Eric Blossom <eb@comsec.com> + + * src/usrp1_sink_base.cc: reduced amount of USB Tx buffering. + +2005-11-13 Eric Blossom <eb@comsec.com> + + * src/db_base.py (db_base.set_auto_tx): new stub method. + +2005-11-11 Eric Blossom <eb@comsec.com> + + * src/usrp.py (pick_rx_subdevice): moved to library. + +2005-11-10 Matt Ettus <matt@ettus.com> + + * src/db_flexrf.py (flexrf_base_tx.set_enable): fixed definition. + +2005-10-27 Eric Blossom <eb@comsec.com> + + * src/db_base.py, src/db_flexrf.py: new method: set_enable + +2005-10-20 Eric Blossom <eb@comsec.com> + + * src/usrp1_source_s.{h,cc}, src/usrp1_source_c.{h,cc}: support + both 8 and 16-bit samples across the USB. + * src/usrp1.i, src/usrp1_source_base.{h,cc}: new methods for + setting and getting rx format. + +2005-10-11 Eric Blossom <eb@comsec.com> + + * src/usrp.py: removed dispatch on usrp revision (no longer + support rev0 boards). Constructors now take keyword args, thus + all args are optional. This will allow us to get rid of the 64e6's + and 128e6's that are scattered throughout the example code. + * src/usrp1.i: removed default values from constructors. They are + now provided by keyword args in usrp.py + +2005-09-29 Eric Blossom <eb@comsec.com> + + * src/db_dbs_rx.py (db_dbs_rx.freq_range): set freq step size to 1M + +2005-09-27 Eric Blossom <eb@comsec.com> + + * src/db_base.py (db_base.i_and_q_swapped): new method for + Flex 400 Rx and other boards that route I into ADC 1 instead of 0. + + * src/usrp.py (tune): added tune fct. Reworked subdev_spec. No + longer accepts (0|1, None). Check for and handle i_and_q_swapped. + +2005-09-21 Eric Blossom <eb@comsec.com> + + * src/db_basic.py: new. + * src/db_instantiator.py, src/usrp.py: added framework for + automatically instantiating daughterboard subclasses. + + * src/usrp1.i, src/usrp1_sink_base.{h,cc}, + src/usrp1_source_base.{h,cc}: deprecated adc_freq(), dac_freq(), + recommend converter_rate(). + +2005-09-17 Eric Blossom <eb@comsec.com> + + * src/db_dbs_rx.py: renamed from dbs_rx.py + * src/db_flexrf.py: renamed from flexrf.py + * src/db_tv_rx.py: renamed from tv_rx.py + * src/db_base.py: renamed from daughterboard_base.py + +2005-08-28 Eric Blossom <eb@comsec.com> + + * src/flexrf.py: added hook to invoke debugging gui. + * src/flexrf_debug_gui.py: new debugging tool. + +2005-07-19 Eric Blossom <eb@comsec.com> + + * src/usrp1_source_base.{h,cc}, src/usrp1.i: new method: set_ddc_phase. + +2005-07-02 Eric Blossom <eb@comsec.com> + + * config/gr_no_undefined.m4, config/gr_x86_64.m4: new, x86_64 support. + * config/gr_python.m4: backed out search for libpython, making + x86_64 work and breaking Cygwin/MinGW. + * configure.ac, src/Makefile.am: mods for x86_64, $(NO_UNDEFINED) + +2005-06-09 Eric Blossom <eb@comsec.com> + + * src/gen_usrp_dbids.py: new. Generate usrp_dbids.py from + usrp_daughterboards.h. This file contains symbolic names for for + daughterboard ID's. + +2005-05-18 Eric Blossom <eb@comsec.com> + + * src/usrp1_sink_base.{h,cc}, src/usrp1_source_base.{h,cc}: use + new start/stop methods. + +2005-05-09 Stephane Fillod <f8cfe@free.fr> + + * config/gr_sysv_shm.m4: SysV shared memory not mandatory + * config/gr_pwin32.m4, config/gr_python.m4, config/lf_cxx.m4: + fixes for Cygwin, MinGW + +2005-03-29 Eric Blossom <eb@comsec.com> + + * src/usrp.py: now check for usrp version at open time, not import + time. + +2005-03-15 Eric Blossom <eb@comsec.com> + + * src/usrp1_sink_base.{h,cc}, src/usrp1_source_base.{h,cc}: + read_aux_dac and write_aux_dac now take which_dboard instead of slot. + +2005-03-13 Matt Ettus <matt@ettus.com> + + * src/Makefile.am, src/tv_rx.py: first cut at TV RX dboard + * src/dbs_rx.py: minor fixes + +2005-03-11 Eric Blossom <eb@comsec.com> + + * src/usrp1_sink_base.{h,cc},src/usrp1_source_base.{h,cc}: new + methods: set_adc_offset, set_dac_offset, set_adc_buffer_bypass. + +2005-02-18 Eric Blossom <eb@comsec.com> + + * src/usrp1_sink_base.cc (_write_oe): fix delegation. + * src/usrp1_source_base.cc (_write_oe): fix delegation. + +2005-02-16 Eric Blossom <eb@comsec.com> + + * src/dbs_rx.py: new. control DBS_RX daughterboard. + * src/usrp1_{sink,source}_base.{h,cc}: add read_i2c and write_i2c. + +2005-02-06 Eric Blossom <eb@comsec.com> + + * configure.ac: upped rev to 0.4 for release. + * src/Makefile.am: backed out dependency on libpython + +2005-01-28 Stephane Fillod <f8cfe@free.fr> + + * src/Makefile.am: fixes for MinGW. + +2005-01-12 Eric Blossom <eb@comsec.com> + + * src/usrp1_sink_base.cc,src/usrp1_source_base.cc: changed + under/overrun diagnostic indicator to "uU" and "uO" to reduce + amount of diagnostic output. + +2005-01-10 Eric Blossom <eb@comsec.com> + + * src/usrp1.i,src/usrp1_sink_base.{h,cc}, + src/usrp1_source_base.{h,cc}: new methods to control all knobs. + +2005-01-04 Eric Blossom <eb@comsec.com> + + * src/usrp1.i,src/usrp1_source_base.{h,cc}: new methods for + messing with Rx PGA. + +2004-11-14 Eric Blossom <eb@comsec.com> + + * src/usrp1_sink_base.cc (work): corrected increment of obi. + +2004-10-13 Eric Blossom <eb@comsec.com> + + * configure.ac: upped rev to 0.2cvs + +2004-10-11 Eric Blossom <eb@comsec.com> + + * configure.ac: bumped rev to 0.2, make release. + * Makefile.am (EXTRA_DIST): added config.h.in + +2004-09-30 Eric Blossom <eb@comsec.com> + + * src/usrp1_sink_base.{h,cc}, src/usrp1_source_base.{h,cc}, + src/usrp1.i: new methods for reading and writing aux dac and adc, + eeproms, and fpga registers. + +2004-09-23 Eric Blossom <eb@comsec.com> + + * config/usrp_fusb_tech.m4, config/bnv_have_qt.m4, config/cppunit.m4, + config/gr_check_mc4020.m4, config/gr_check_usrp.m4, config/gr_doxygen.m4, + config/gr_gprof.m4, config/gr_scripting.m4, config/gr_set_md_cpu.m4, + config/pkg.m4, config/usrp_fusb_tech.m4: added additional quoting + to first arg of AC_DEFUN to silence automake warning. + +2004-08-19 Eric Blossom <eb@comsec.com> + + * src/usrp1_{sink,source}_base.{h,cc}, src/usrp1.i: new method: set_verbose. + +2004-08-03 Eric Blossom <eb@comsec.com> + + * src/usrp1_source_base.{h,cc}, src/usrp1_sink_base.{h,cc}: + extracted base class that handles everything but the packing and + unpacking of data into the usrp buffer. + * src/usrp1_source_c.{h,cc}, src/usrp1_sink_c.{h,cc}: revised to + use new base class. + * src/usrp1_source_s.{h,cc}, src/usrp1_sink_s.{h,cc}: new. + + +2004-07-31 Eric Blossom <eb@comsec.com> + + * src/usrp1_{sink,source}_c.{h,cc}: new method: set_loopback + * src/usrp1.i: new method: set_loopback + +2004-07-30 Eric Blossom <eb@comsec.com> + + * src/usrp1_source_c.{h,cc}: renamed from usrp_source_c.{h,cc} + * src/usrp1_sink_c.{h,cc}: renamed from usrp_sink_c.{h,cc} + * src/usrp1.i: renamed from usrp.i + * src/usrp.py: new. Binds proper class depending on hardware found. + + +2004-07-29 Eric Blossom <eb@comsec.com> + + * src/usrp_{sink,source}_c.h: doc fix. + * src/usrp0.i: new + * src/usrp0_source_c.{h,cc}: new + * src/usrp0_sink_c.{h,cc}: new + +2004-07-12 Eric Blossom <eb@comsec.com> + + * configure.ac: upped rev to 0.1cvs + +2004-07-08 Eric Blossom <eb@comsec.com> + + * src/usrp_source_c.{h,cc}: new. + +# +# Copyright 2004,2005,2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# diff --git a/gr-usrp/Makefile.am b/gr-usrp/Makefile.am new file mode 100644 index 000000000..e500700cb --- /dev/null +++ b/gr-usrp/Makefile.am @@ -0,0 +1,25 @@ +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +include $(top_srcdir)/Makefile.common + +EXTRA_DIST = README_MULTI_USRP.txt +SUBDIRS = src diff --git a/gr-usrp/README_MULTI_USRP.txt b/gr-usrp/README_MULTI_USRP.txt new file mode 100644 index 000000000..4ed7a15b3 --- /dev/null +++ b/gr-usrp/README_MULTI_USRP.txt @@ -0,0 +1,251 @@ +Multi usrp + +With this code you can connect two or more usrps (with a locked clock) and get synchronised samples. +You must connect a (flat)cable between a dboard on the master in RXA and a dboard on the slave in RXA. +You then put one usrp in master mode, put the other in slave mode. + +For a quick start using the examples look at gnuradio-examples/python/multi_usrp/README + +Use the usrp_multi block which is installed by gr-usrp. +instantiate in the following way: + + self.multi=usrp_multi.multi_source_align( fg=self, master_serialno=options.master_serialno, decim=options.decim, nchan=options.nchan ) + +nchan should be 2 or 4. + +You determine which is the master by master_serialno (this is a text string a hexadecimal number). +If you enter a serial number which is not found it will print the serial numbers which are available. +If you give no serial number (master_serialno=None), the code will pick a Master for you. + +You can get a reference to the master and the slave usrp in the following way: + + self.um=self.multi.get_master_usrp() + self.us=self.multi.get_slave_usrp() + +You only need these references for setting freqs/gains or getting info about daughterboards. +Don't use the output directly but use the aligned output from multi.get_master_source_c() and multi.get_slave_source_c() + +You get references to the aligned output samples in the following way: +aligned_master_source_c=self.multi.get_master_source_c() +aligned_slave_source_c=self.multi.get_slave_source_c() + +These blocks have multiple outputs. +output 0 is the sample counter (high bits in I, low bits in Q) +You normally don't need the samplecounters so you can ignore output 0 + +output 1 is the first aligend output channel (if you enable 2 or 4 channels) +output 2 is the second output channel (only if you enable 4 channels) + +so the usefull 4 channels are: +self.aligned_master_chan1=(self.multi.get_master_source_c(),1) +self.aligned_master_chan2=(self.multi.get_master_source_c(),2) +self.aligned_slave_chan1=(self.multi.get_slave_source_c(),1) +self.aligned_slave_chan2=(self.multi.get_slave_source_c(),2) + +The two samplecounters are: +self.aligned_master_samplecounter=(self.multi.get_master_source_c(),0) +self.aligned_slave_samplecounter=(self.multi.get_slave_source_c(),0) + +You can set the gain or tune the frequency for all 4 receive daughetrboards at once: + self.multi.set_gain_all_rx(options.gain) + result,r1,r2,r3,r4 = self.multi.tune_all_rx(options.freq) + +This will only work reliably when you have all the same daughterboards. +Otherwise set all freqs and gains individually. + +You must call self.multi.sync() at least once AFTER the flowgraph has started running. +(This will synchronise the streams of the two usrps) + +This work was funded by Toby Oliver at Sensus Analytics / Path Intelligence. +Many Thanks for making this possible. + +It was written by Martin Dudok van Heel at Olifantasia. + +Quick start multi-usrp: +Unpack, build and install usrp, gnuradio-core and gr-usrp +Versions need to be more recent then 2.7cvs/svn 11 may 2006 + +Make sure usrp/fpga/rbf/rev2/multi*.rbf is installed in /usr/local/share/usrp/rev2/ +Make sure usrp/fpga/rbf/rev4/multi*.rbf is installed in /usr/local/share/usrp/rev4/ +(If in doubt, copy manually) + +build and install gr-wxgui gr-audio-xxx and so on. + +unpack gnuradio-examples. + +There is a gnuradio-examples/python/multi_usrp directory which contains examples and a README + + +Put at least a basic RX or dbsrx board in RXA of the master and RXA of the slave board. +Make sure that the usrps have a serial or unique identifier programmed in their eeprom. +(All new rev 4.1 boards have this) +You can do without a serial but then you never know which usrp is the master and which is the slave. + + +CONNECTING THE CABLES +Now connect the 64MHz clocks between the boards with a short sma coax cable. +(See the wiki on how to enable clock-out and clock-in +http://comsec.com/wiki?USRPClockingNotes ) + +You need one board with a clock out and one board with a clock in. + +You can choose any of the two boards as master or slave, this is not dependant on which board has the clock-out or in. +In my experiments I had fewer problems when the board that has the clock-in will be the master board. + +You can use a standard 16-pole flatcable to connect tvrx, basic-rx or dbsrx boards. +Of this 16pin flatcable only two pins are used (io15 and ground) +For all new daughterboards which use up a lot of io pins you have to use a cable with fewer connections. +The savest is using a 2pin headercable connected to io15,gnd (a cable like the ones used to connect frontpanel leds to the mainboard of a PC) + +If using basic rx board: + Connect a 16-pole flatcable from J25 on basicrx/dbs_rx in rxa of the master usrp to J25 on basicrx/dbsrx in RXA of the slave usrp + Don't twist the cable (Make sure the pin1 marker (red line on the flatcable) is on the same side of the connector (at io-8 on the master and at io8 on the slave.)) + For basic_rx this means the marker should be on the side of the dboard with the sma connectors. + For dbs_rx this means the marker should be on the side of the dboard with the two little chips. + In other words, don't twist the cable, you will burn your board if you do. + +You can also connect a flatcable with multiple connectors from master-J25 to slave1-J25 to slave2-J25 to ... +You will however have to think of something to create a common 64Mhz clock for more then two usrps. + +For all other daughterboards, connect a 2wire cable from masterRXA J25 io15,gnd to slaveRXA J25 io15,gnd + + +So now the hardware is setup, software is setup. Lets do some tests. + +Connect power to both usrps. +unpack the gnuradio_examples somewhere (cvs version later then 11 may 2006) +go to the gnuradio-examples/python/multi_usrp folder. + +Now run + ./multi_usrp_oscope.py -x 12345678 + +It should tell you that usrp 12345678 is not found and tell you which serials are available. + +Now run ./multi_usrp_oscope.py -x actualserialnum +You should now get an oscope with two channels, one is from the master and one is from the slave +It will which show the I-signal from channel 0 of the master usrp and I-signal from channel 0 of the slave usrp. +(For testing connect the same signal source to the inputs of both boards) +The signals should be aligned. +If you click the sync button, it will resync the master and slave (should never be needed) + +Now run +./multi_usrp_oscope.py --help +To see all available options. + + +Now you are ready to do phase-locked aligned signal processing. + +You can also capture to file with: +./multi_usrp_rx_cfile.py + +run ./multi_usrp_rx_cfile.py --help to see all available options. + + +Here follows a brief of the new blocks and (changes)functionality for multi-usrp. +You can also look at the generated documentation in +/usr/local/share/doc/gnuradio-core-X.X +/usr/local/share/doc/usrp-X.X +(Make sure to build and install the documentation, go to the doc directory of the sourcetree and issue make doc; make install) + + +gnuradio-examples: +new/changed files: +multi_usrp/multi_usrp_oscope.py +multi_usrp/multi_usrp_rx_cfile.py + + +gnuradio-core: +gr.align_on_samplenumbers_ss (int nchan,int align_interval) + +align several complex short (interleaved short) input channels with corresponding unsigned 32 bit sample_counters (provided as interleaved 16 bit values) + +Parameters: + nchan number of complex_short input channels (including the 32 bit counting channel) + align_interval interval at which the samples are aligned, ignored for now. + +Pay attention on how you connect this block It expects a minimum of 2 usrp_source_s with nchan number of channels and as mode usrp_prims.bmFR_MODE_RX_COUNTING_32BIT enabled. This means that the first complex_short channel is an interleaved 32 bit counter. The samples are aligned by dropping samples untill the samplenumbers match. + +files: +gnuradio-core/src/lib/general/gr_align_on_samplenumbers_ss.cc +gnuradio-core/src/lib/general/gr_align_on_samplenumbers_ss.h +gnuradio-core/src/lib/general/gr_align_on_samplenumbers_ss.i + + +gr-usrp + added _write_fpga_reg_masked + added usrp_multi.py + new usrp_multi block which can instantiate two linked usrps as master and slave and alignes their output. + It has a sync() function which should be called AFTER the flowgraph has started running. + bool sync(); + \brief Call this on a master usrp to sync master and slave by outputing a sync pulse on rx_a_io[15]. + The 32 bit samplecounter of master and slave will be reset to zero and all phase and buffer related things in the usrps are reset. + Call this only after the flowgraph has been started, otherwise there will be no effect since everything is kept in reset state as long as the flowgraph is not running. + \returns true if successfull. + +files: +configure.ac +src/Makefile.am +src/usrp1.i +src/usrp1_source_base.cc +src/usrp1_source_base.h +src/usrp_multi.py + +usrp-0.11cvsmulti: +usrp: + new constant bmFR_MODE_RX_COUNTING_32BIT (could also be added as extra mode like FPGA_MODE_COUNTING_32BIT) + Use this for the mode parameter when creating a usrp when you want to use the master/slave setup or if you want to use the 32 bit counter for other things, like testing with gr.check_counting_s(True) + + added register FR_RX_MASTER_SLAVE + added bitno and bitmaskes: + bmFR_MODE_RX_COUNTING_32BIT + + bitnoFR_RX_SYNC + bitnoFR_RX_SYNC_MASTER + bitnoFR_RX_SYNC_SLAVE + + bitnoFR_RX_SYNC_INPUT_IOPIN 15 + bmFR_RX_SYNC_INPUT_IOPIN (1<<bitnoFR_RX_SYNC_INPUT_IOPIN) + bitnoFR_RX_SYNC_OUTPUT_IOPIN 15 + bmFR_RX_SYNC_OUTPUT_IOPIN (1<<bitnoFR_RX_SYNC_OUTPUT_IOPIN) + + added _write_fpga_reg_masked() + added new toplevel folder usrp_multi + added usrp_multi.v and master_control_multi.v + added new MULTI_ON and COUNTER_32BIT_ON defines + If these are turned off usrp_multi.v will behave exactly as usrp_std.v + + added setting_reg_masked.v + changed reset behaviour of phase_acc.v and rx_buffer.v + + changed generate_regs.py to handle bm and bitno defines + + +files: +firmware/include/fpga_regs_standard.v +firmware/include/fpga_regs_common.h +firmware/include/generate_regs.py +firmware/include/fpga_regs_standard.h +host/lib/usrp_basic.h +host/lib/usrp_basic.cc +host/lib/usrp_standard.h +fpga/rbf/Makefile.am +fpga/toplevel/usrp_std/usrp_std.v +fpga/toplevel/usrp_multi/usrp_multi.esf +fpga/toplevel/usrp_multi/usrp_multi.vh +fpga/toplevel/usrp_multi/usrp_std.vh +fpga/toplevel/usrp_multi/usrp_multi_config_2rxhb_0tx.vh +fpga/toplevel/usrp_multi/usrp_multi_config_2rxhb_2tx.vh +fpga/toplevel/usrp_multi/usrp_multi.v +fpga/toplevel/usrp_multi/usrp_multi.qpf +fpga/toplevel/usrp_multi/usrp_multi.psf +fpga/toplevel/usrp_multi/usrp_multi_config_2rx_0tx.vh +fpga/toplevel/usrp_multi/usrp_multi.qsf +fpga/toplevel/usrp_multi/usrp_multi_config_4rx_0tx.vh +fpga/toplevel/usrp_multi/usrp_multi.csf +fpga/toplevel/usrp_multi/.cvsignore +fpga/sdr_lib/rx_buffer.v +fpga/sdr_lib/master_control_multi.v +fpga/sdr_lib/phase_acc.v +fpga/sdr_lib/setting_reg_masked.v + + diff --git a/gr-usrp/src/Makefile.am b/gr-usrp/src/Makefile.am new file mode 100644 index 000000000..0bcd6c475 --- /dev/null +++ b/gr-usrp/src/Makefile.am @@ -0,0 +1,120 @@ +# +# Copyright 2004,2005,2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +include $(top_srcdir)/Makefile.common + +# Install this stuff so that it ends up as the gnuradio.usrp module +# This usually ends up at: +# ${prefix}/lib/python${python_version}/site-packages/gnuradio + +ourpythondir = $(grpythondir) +ourlibdir = $(grpyexecdir) + +EXTRA_DIST = run_tests.in +TESTS = run_tests + +LOCAL_IFILES = \ + usrp1.i + +NON_LOCAL_IFILES = \ + $(top_srcdir)/gnuradio-core/src/lib/swig/gnuradio.i + +ALL_IFILES = \ + $(LOCAL_IFILES) \ + $(NON_LOCAL_IFILES) + +BUILT_SOURCES = \ + usrp1.cc \ + usrp1.py + +ourpython_PYTHON = \ + db_base.py \ + db_basic.py \ + db_dbs_rx.py \ + db_flexrf.py \ + db_flexrf_mimo.py \ + db_instantiator.py \ + db_tv_rx.py \ + flexrf_debug_gui.py \ + tx_debug_gui.py \ + usrp.py \ + usrp1.py \ + usrp_multi.py + + +USRP_INCLUDES = -I$(top_srcdir)/usrp/host/lib -I$(top_srcdir)/usrp/firmware/include +USRP_LIBS = -L$(top_srcdir)/usrp/host/lib/.libs -lusrp + +INCLUDES = $(STD_DEFINES_AND_INCLUDES) $(PYTHON_CPPFLAGS) + +SWIGPYTHONARGS = $(SWIGPYTHONFLAGS) $(STD_DEFINES_AND_INCLUDES) $(USRP_INCLUDES) + +grinclude_HEADERS = \ + usrp1_sink_base.h \ + usrp1_sink_c.h \ + usrp1_sink_s.h \ + usrp1_source_base.h \ + usrp1_source_c.h \ + usrp1_source_s.h + +swiginclude_HEADERS = \ + $(LOCAL_IFILES) + + +ourlib_LTLIBRARIES = _usrp1.la + + +_usrp1_la_SOURCES = \ + usrp1.cc \ + usrp1_sink_base.cc \ + usrp1_sink_c.cc \ + usrp1_sink_s.cc \ + usrp1_source_base.cc \ + usrp1_source_c.cc \ + usrp1_source_s.cc + + +_usrp1_la_LIBADD = \ + $(PYTHON_LDFLAGS) \ + $(GNURADIO_CORE_LIBS) \ + $(USRP_LIBS) \ + -lstdc++ + + +_usrp1_la_LDFLAGS = $(NO_UNDEFINED) -module -avoid-version + + +usrp1.cc usrp1.py: usrp1.i $(NON_LOCAL_IFILES) + $(SWIG) $(SWIGPYTHONARGS) -module usrp1 -o usrp1.cc $< + + +noinst_PYTHON = \ + qa_usrp.py + +MOSTLYCLEANFILES = \ + $(BUILT_SOURCES) *~ *.pyc + + +# Don't distribute output of swig +dist-hook: + @for file in $(BUILT_SOURCES); do echo $(RM) $(distdir)/$$file; done + @for file in $(BUILT_SOURCES); do $(RM) $(distdir)/$$file; done + diff --git a/gr-usrp/src/db_base.py b/gr-usrp/src/db_base.py new file mode 100644 index 000000000..4f4db38f4 --- /dev/null +++ b/gr-usrp/src/db_base.py @@ -0,0 +1,234 @@ +# +# Copyright 2005,2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +import usrp_prims +import weakref +from usrp_fpga_regs import * + +class db_base(object): + """ + Abstract base class for all daughterboards. + + This defines the required operations and interfaces for all d'boards. + """ + def __init__(self, usrp, which): + """ + Initialize daughterboard interface. + + @param usrp: instance of usrp + @param which: which daughterboard side: A = 0, B = 1 + @type which: int + """ + + if not (which in (0, 1)): + raise ValueError, "Invalid value of which: %s" % (which,) + + self._u = weakref.proxy(usrp) + + self._which = which + if hasattr(self._u, 'tx_freq'): # is this a tx or rx daughterboard? + self._tx = True + self._slot = which * 2 + else: + self._tx = False + self._slot = which * 2 + 1 + + FR_TX_A_REFCLK = 40 + FR_RX_A_REFCLK = 41 + FR_TX_B_REFCLK = 42 + FR_RX_B_REFCLK = 43 + + self._refclk_reg = (FR_TX_A_REFCLK,FR_RX_A_REFCLK,FR_TX_B_REFCLK,FR_RX_B_REFCLK)[self._slot] + + + def dbid(self): + return self._u.daughterboard_id(self._which) + + def name(self): + return usrp_prims.usrp_dbid_to_string(self.dbid()) + + def side_and_name(self): + return "AB"[self._which] + ': ' + self.name() + + # Function to bypass ADC buffers. Any board which is DC-coupled should bypass the buffers + def bypass_adc_buffers(self,bypass): + if self._tx: + raise RuntimeError, "TX Board has no adc buffers" + if self._which==0: + self._u.set_adc_buffer_bypass(0, bypass) + self._u.set_adc_buffer_bypass(1, bypass) + else: + self._u.set_adc_buffer_bypass(2, bypass) + self._u.set_adc_buffer_bypass(3, bypass) + + # ------------------------------------------------------------------------ + # Reference Clock section + + # Control whether a reference clock is sent to the daughterboards, + # and what frequency + # + # Bit 7 -- 1 turns on refclk, 0 allows IO use + # Bits 6:0 Divider value + # + # FIXME get these from the fpga_regs_standard.h + + def _refclk_freq(self): + return self._u.fpga_master_clock_freq()/self._refclk_divisor() + + def _enable_refclk(self,enable): + CLOCK_OUT = 1 # Clock is on lowest bit + REFCLK_ENABLE = 0x80 + REFCLK_DIVISOR_MASK = 0x7f + if enable: + self._u._write_oe(self._which, CLOCK_OUT, CLOCK_OUT) # output enable + self._u._write_fpga_reg(self._refclk_reg, + ((self._refclk_divisor() & REFCLK_DIVISOR_MASK) + | REFCLK_ENABLE)) + else: + self._u._write_fpga_reg(self._refclk_reg, 0) + + def _refclk_divisor(self): + """ + Return value to stick in REFCLK_DIVISOR register + """ + raise NotImplementedError + + # ------------------------------------------------------------------------ + # Automatic Transmit/Receive switching + # + # The presence or absence of data in the FPGA transmit fifo + # selects between two sets of values for each of the 4 banks of + # daughterboard i/o pins. + # + # Each daughterboard slot has 3 16-bit registers associated with it: + # FR_ATR_MASK_*, FR_ATR_TXVAL_* and FR_ATR_RXVAL_* + # + # FR_ATR_MASK_{0,1,2,3}: + # + # These registers determine which of the daugherboard i/o pins are + # affected by ATR switching. If a bit in the mask is set, the + # corresponding i/o bit is controlled by ATR, else it's output + # value comes from the normal i/o pin output register: + # FR_IO_{0,1,2,3}. + # + # FR_ATR_TXVAL_{0,1,2,3}: + # FR_ATR_RXVAL_{0,1,2,3}: + # + # If the Tx fifo contains data, then the bits from TXVAL that are + # selected by MASK are output. Otherwise, the bits from RXVAL that + # are selected by MASK are output. + + def set_atr_mask(self, v): + """ + Set Auto T/R mask. + """ + return self._u._write_fpga_reg(FR_ATR_MASK_0 + 3 * self._slot, v) + + def set_atr_txval(self, v): + """ + Set Auto T/R register value to be used when transmitting. + """ + return self._u._write_fpga_reg(FR_ATR_TXVAL_0 + 3 * self._slot, v) + + def set_atr_rxval(self, v): + """ + Set Auto T/R register value to be used when receiving. + """ + return self._u._write_fpga_reg(FR_ATR_RXVAL_0 + 3 * self._slot, v) + + # derived classes should override the following methods + + def freq_range(self): + """ + Return range of frequencies in Hz that can be tuned by this d'board. + + @returns (min_freq, max_freq, step_size) + @rtype tuple + """ + raise NotImplementedError + + def set_freq(self, target_freq): + """ + Set the frequency. + + @param freq: target RF frequency in Hz + @type freq: float + + @returns (ok, actual_baseband_freq) where: + ok is True or False and indicates success or failure, + actual_baseband_freq is the RF frequency that corresponds to DC in the IF. + """ + raise NotImplementedError + + def gain_range(self): + """ + Return range of gain that can be set by this d'board. + + @returns (min_gain, max_gain, step_size) + Where gains are expressed in decibels (your mileage may vary) + """ + raise NotImplementedError + + def set_gain(self, gain): + """ + Set the gain. + + @param gain: gain in decibels + @returns True/False + """ + raise NotImplementedError + + def is_quadrature(self): + """ + Return True if this daughterboard does quadrature up or down conversion. + That is, return True if this board requires both I & Q analog channels. + + This bit of info is useful when setting up the USRP Rx mux register. + """ + raise NotImplementedError + + def i_and_q_swapped(self): + """ + Return True if this is a quadrature device and ADC 0 is Q. + """ + return False + + def spectrum_inverted(self): + """ + Return True if the dboard gives an inverted spectrum + """ + return False + + def set_enable(self, on): + """ + For tx daughterboards, this controls the transmitter enable. + """ + pass + + def set_auto_tr(self,on): + """ + Enable automatic Transmit/Receive switching (ATR). + + Should be overridden in subclasses that care. This will typically + set the atr_mask, txval and rxval. + """ + pass + diff --git a/gr-usrp/src/db_basic.py b/gr-usrp/src/db_basic.py new file mode 100644 index 000000000..e9139466e --- /dev/null +++ b/gr-usrp/src/db_basic.py @@ -0,0 +1,252 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +import sys +import usrp_dbid +import db_base +import db_instantiator + +class db_basic_tx(db_base.db_base): + def __init__(self, usrp, which): + """ + Handler for Basic Tx daughterboards. + + @param usrp: instance of usrp.source_c + @param which: which side: 0 or 1 corresponding to TX_A or TX_B respectively + """ + # sets _u and _which + db_base.db_base.__init__(self, usrp, which) + + if 0: # Doing this would give us a different default than the historical values... + g = self.gain_range() # initialize gain + self.set_gain(float(g[0]+g[1]) / 2) + + + def freq_range(self): + """ + Return range of frequencies in Hz that can be tuned by this d'board. + + @returns (min_freq, max_freq, step_size) + @rtype tuple + + We say we can do pretty much anything... + """ + return (-90e9, 90e9, 1e-6) + + def set_freq(self, target_freq): + """ + Set the frequency. + + @param freq: target RF frequency in Hz + @type freq: float + + @returns (ok, actual_baseband_freq) where: + ok is True or False and indicates success or failure, + actual_baseband_freq is the RF frequency that corresponds to DC in the IF. + """ + return (True, 0) + + def gain_range(self): + """ + Return range of gain that can be set by this d'board. + + @returns (min_gain, max_gain, step_size) + Where gains are expressed in decibels (your mileage may vary) + """ + return (self._u.pga_min(), self._u.pga_max(), self._u.pga_db_per_step()) + + def set_gain(self, gain): + """ + Set the gain. + + @param gain: gain in decibels + @returns True/False + """ + ok = self._u.set_pga(self._which * 2 + 0, gain) + ok = ok and self._u.set_pga(self._which * 2 + 1, gain) + return ok + + def is_quadrature(self): + """ + Return True if this board requires both I & Q analog channels. + """ + return True + + +class db_basic_rx(db_base.db_base): + def __init__(self, usrp, which, subdev): + """ + Handler for Basic Rx daughterboards. + + @param usrp: instance of usrp.source_c + @param which: which side: 0 or 1 corresponding to RX_A or RX_B respectively + @param subdev: which analog i/o channel: 0 or 1 + @type subdev: int + """ + # sets _u and _which + db_base.db_base.__init__(self, usrp, which) + self._subdev = subdev + + self.bypass_adc_buffers(True) + + if 0: # Doing this would give us a different default than the historical values... + g = self.gain_range() # initialize gain + self.set_gain(float(g[0]+g[1]) / 2) + + + def freq_range(self): + """ + Return range of frequencies in Hz that can be tuned by this d'board. + + @returns (min_freq, max_freq, step_size) + @rtype tuple + + We say we can do pretty much anything... + """ + return (0, 90e9, 1e-6) + + def set_freq(self, target_freq): + """ + Set the frequency. + + @param freq: target RF frequency in Hz + @type freq: float + + @returns (ok, actual_baseband_freq) where: + ok is True or False and indicates success or failure, + actual_baseband_freq is the RF frequency that corresponds to DC in the IF. + """ + return (True, 0) + + + def gain_range(self): + """ + Return range of gain that can be set by this d'board. + + @returns (min_gain, max_gain, step_size) + Where gains are expressed in decibels (your mileage may vary) + """ + return (self._u.pga_min(), self._u.pga_max(), self._u.pga_db_per_step()) + + def set_gain(self, gain): + """ + Set the gain. + + @param gain: gain in decibels + @returns True/False + """ + return self._u.set_pga(self._which * 2 + self._subdev, gain) + + def is_quadrature(self): + """ + Return True if this board requires both I & Q analog channels. + + This bit of info is useful when setting up the USRP Rx mux register. + """ + return False + +class db_lf_rx(db_basic_rx): + def __init__(self, usrp, which, subdev): + """ + Handler for Low Freq Rx daughterboards. + + @param usrp: instance of usrp.source_c + @param which: which side: 0 or 1 corresponding to RX_A or RX_B respectively + @param subdev: which analog i/o channel: 0 or 1 + @type subdev: int + """ + # sets _u and _which + db_basic_rx.__init__(self, usrp, which, subdev) + + def freq_range(self): + """ + Return range of frequencies in Hz that can be tuned by this d'board. + + @returns (min_freq, max_freq, step_size) + @rtype tuple + + We cover the first nyquist zone only + """ + return (0, 32e6, 1e-6) + +class db_lf_tx(db_basic_tx): + def __init__(self, usrp, which): + """ + Handler for Low Freq Tx daughterboards. + + @param usrp: instance of usrp.source_c + @param which: which side: 0 or 1 corresponding to RX_A or RX_B respectively + """ + # sets _u and _which + db_basic_tx.__init__(self, usrp, which) + + def freq_range(self): + """ + Return range of frequencies in Hz that can be tuned by this d'board. + + @returns (min_freq, max_freq, step_size) + @rtype tuple + + We cover the first nyquist zone only + """ + return (-32e6, 32e6, 1e-6) + + +# hook these daughterboard classes into the auto-instantiation framework + +def _basic_rx_instantiator(usrp, which): + # two single channel subdevices + return (db_basic_rx(usrp, which, 0), db_basic_rx(usrp, which, 1)) + +def _lf_rx_instantiator(usrp, which): + # two single channel subdevices + return (db_lf_rx(usrp, which, 0), db_lf_rx(usrp, which, 1)) + +def _basic_tx_instantiator(usrp, which): + # one quadrature subdevice + return (db_basic_tx(usrp, which),) + +def _lf_tx_instantiator(usrp, which): + # one quadrature subdevice + return (db_lf_tx(usrp, which),) + +def _no_db_instantiator(usrp, which): + if hasattr(usrp, 'tx_freq'): # is this a tx or rx daughterboard? + return (_basic_tx_instantiator(usrp, which)) + else: + return (_basic_rx_instantiator(usrp, which)) + +def _invalid_instantiator(usrp, which): + if hasattr(usrp, 'tx_freq'): # is this a tx or rx daughterboard? + sys.stderr.write('\n\aWarning: Treating daughterboard with invalid EEPROM contents as if it were a "Basic Tx."\n') + sys.stderr.write('Warning: This is almost certainly wrong... Use appropriate burn-*-eeprom utility.\n\n') + return _basic_tx_instantiator(usrp, which) + else: + sys.stderr.write('\n\aWarning: Treating daughterboard with invalid EEPROM contents as if it were a "Basic Rx."\n') + sys.stderr.write('Warning: This is almost certainly wrong... Use appropriate burn-*-eeprom utility.\n\n') + return _basic_rx_instantiator(usrp, which) + +db_instantiator.add(-1, _no_db_instantiator) # no daughterboard +db_instantiator.add(-2, _invalid_instantiator) # invalid eeprom contents +db_instantiator.add(usrp_dbid.BASIC_TX, _basic_tx_instantiator) +db_instantiator.add(usrp_dbid.BASIC_RX, _basic_rx_instantiator) +db_instantiator.add(usrp_dbid.LF_TX, _lf_tx_instantiator) +db_instantiator.add(usrp_dbid.LF_RX, _lf_rx_instantiator) diff --git a/gr-usrp/src/db_dbs_rx.py b/gr-usrp/src/db_dbs_rx.py new file mode 100644 index 000000000..24e8d7dbe --- /dev/null +++ b/gr-usrp/src/db_dbs_rx.py @@ -0,0 +1,345 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +import math +import usrp_dbid +import db_base +import db_instantiator + +def int_seq_to_str (seq): + """convert a sequence of integers into a string""" + return ''.join (map (chr, seq)) + +def str_to_int_seq (str): + """convert a string to a list of integers""" + return map (ord, str) + +class db_dbs_rx (db_base.db_base): + def __init__ (self, usrp, which): + """ + Control DBS receiver based USRP daughterboard. + + @param usrp: instance of usrp.source_c + @param which: which side: 0 or 1 corresponding to RX_A or RX_B respectively + @type which: int + """ + # sets _u and _which + db_base.db_base.__init__(self, usrp, which) + + self._u._write_oe(self._which,0x0001,0x0001) + self.i2c_addr = (0x67, 0x65)[self._which] + # set basic parameters + # set default values + self.n = 950 + self.div2 = 0 + self.osc = 5 + self.cp = 3 + self.r = 4 + self.r_int = 1 + self.fdac = 127 + self.m = 2 + self.dl = 0 + self.ade = 0 + self.adl = 0 + self.gc2 = 31 + self.diag = 0 + + # FIXME this should be in the core dboard class + self.refclk_divisor = 16 + self._enable_refclk(True) + + g = self.gain_range() + self.set_gain(float(g[0]+g[1]) / 2) + self.bypass_adc_buffers(True) + + def __del__(self): + if self._u: + self._enable_refclk(False) + + def _write_reg (self, regno, v): + """regno is in [0,5], v is value to write to register""" + assert (0 <= regno and regno <= 5) + self._u.write_i2c (self.i2c_addr, int_seq_to_str ((regno, v))) + + def _write_regs (self, starting_regno, vals): + """starting_regno is in [0,5], + vals is a seq of integers to write to consecutive registers""" + self._u.write_i2c (self.i2c_addr, + int_seq_to_str ((starting_regno,) + tuple (vals))) + + def _read_status (self): + """If successful, return list of two ints: [status_info, filter_DAC]""" + s = self._u.read_i2c (self.i2c_addr, 2) + if len (s) != 2: + return None + return str_to_int_seq (s) + + def _send_reg(self,regno): + assert (0 <= regno and regno <= 5) + if regno == 0: + self._write_reg(0,(self.div2<<7) + (self.n>>8)) + if regno == 1: + self._write_reg(1,self.n & 255) + if regno == 2: + self._write_reg(2,self.osc + (self.cp<<3) + (self.r_int<<5)) + if regno == 3: + self._write_reg(3,self.fdac) + if regno == 4: + self._write_reg(4,self.m + (self.dl<<5) + (self.ade<<6) + (self.adl<<7)) + if regno == 5: + self._write_reg(5,self.gc2 + (self.diag<<5)) + + # BW setting + def _set_m(self,m): + assert m>0 and m<32 + self.m = m + self._send_reg(4) + + def _set_fdac(self,fdac): + assert fdac>=0 and fdac<128 + self.fdac = fdac + self._send_reg(3) + + def set_bw (self, bw): + #assert (bw>=4e6 and bw<=33e6) + assert (bw>=1e6 and bw<=33e6) + if bw >= 4e6: + m_max = int(min(31,math.floor(self._refclk_freq()/1e6))) + elif bw >= 2e6: # Outside of Specs! + m_max = int(min(31,math.floor(self._refclk_freq()/.5e6))) + else: # Way outside of Specs! + m_max = int(min(31,math.floor(self._refclk_freq()/.25e6))) + + m_min = int(math.ceil(self._refclk_freq()/2.5e6)) + m_test = m_max + while m_test >= m_min: + fdac_test = int(round(((bw * m_test / self._refclk_freq())-4)/.145)) + if fdac_test>127: + m_test = m_test - 1 + else: + break + if (m_test>=m_min and fdac_test >=0): + self._set_m(m_test) + self._set_fdac(fdac_test) + return (self.m,self.fdac,self._refclk_freq()/self.m*(4+0.145*self.fdac)) + else: + print "Failed to set bw" + + # Gain setting + def _set_dl(self,dl): + assert dl == 0 or dl == 1 + self.dl = dl + self._send_reg(4) + + def _set_gc2(self,gc2): + assert gc2<32 and gc2>=0 + self.gc2 = gc2 + self._send_reg(5) + + def _set_gc1(self,gc1): + assert gc1>=0 and gc1<4096 + self.gc1 = gc1 + self._u.write_aux_dac(self._which,0,int(gc1)) + + def _set_pga(self, pga_gain): + assert pga_gain >=0 and pga_gain <=20 + if(self._which == 0): + self._u.set_pga (0, pga_gain) + self._u.set_pga (1, pga_gain) + else: + self._u.set_pga (2, pga_gain) + self._u.set_pga (3, pga_gain) + + def gain_range(self): + return (0, 104, 1) + + def set_gain(self,gain): + if not (gain>=0 and gain<105): + raise ValueError, "gain out of range" + gc1 = 0 + gc2 = 0 + dl = 0 + pga = 0 + if gain <56: + gc1 = int((-gain*1.85/56.0 + 2.6)*4096.0/3.3) + gain = 0 + else: + gc1 = 0 + gain = gain - 56 + if gain < 24: + gc2 = int(round(31.0 * (1-gain/24.0))) + gain = 0 + else: + gc2 = 0 + gain = gain - 24 + if gain >= 4.58: + dl = 1 + gain = gain - 4.58 + pga = gain + self._set_gc1(gc1) + self._set_gc2(gc2) + self._set_dl(dl) + self._set_pga(pga) + + # Frequency setting + def _set_osc(self,osc): + assert osc>=0 and osc<8 + self.osc = osc + self._send_reg(2) + + def _set_cp(self,cp): + assert cp>=0 and cp<4 + self.cp = cp + self._send_reg(2) + + def _set_n(self,n): + assert n>256 and n<32768 + self.n = n + self._send_reg(0) + self._send_reg(1) + + def _set_div2(self,div2): + assert div2 == 0 or div2 == 1 + self.div2 = div2 + self._send_reg(0) + + def _set_r(self,r): + assert r>=0 and r<128 + self.r = r + self.r_int = int(round(math.log10(r)/math.log10(2)) - 1) + self._send_reg(2) + + # FIXME How do we handle ADE and ADL properly? + def _set_ade(self,ade): + assert ade == 0 or ade == 1 + self.ade = ade + self._send_reg(4) + + def freq_range(self): + return (500e6, 2.6e9, 1e6) + + def _refclk_divisor(self): + """ + Return value to stick in REFCLK_DIVISOR register + """ + return 16 + + def set_freq(self, freq): + """ + Set the frequency. + + @param freq: target frequency in Hz + @type freq: float + + @returns (ok, actual_baseband_freq) where: + ok is True or False and indicates success or failure, + actual_baseband_freq is the RF frequency that corresponds to DC in the IF. + """ + if not (freq>=500e6 and freq<=2.6e9): + return (False, 0) + + if(freq<1150e6): + self._set_div2(0) + vcofreq = 4 * freq + else: + self._set_div2(1) + vcofreq = 2 * freq + self._set_ade(1) + rmin=max(2,self._refclk_freq()/2e6) + rmax=min(128,self._refclk_freq()/150e3) + r = 2 + n=0 + best_r = 2 + best_n =0 + best_delta = 10e6 + while r <= rmax: + n = round(freq/(self._refclk_freq()/r)) + if r<rmin or n<256: + r = r * 2 + continue + delta = abs(n*self._refclk_freq()/r - freq) + if delta < 75e3: + best_r = r + best_n = n + break + if delta < best_delta*0.9: + best_r = r + best_n = n + best_delta = delta + r = r * 2 + self._set_r(int(best_r)) + + self._set_n(int(round(best_n))) + + if vcofreq < 2433e6: + vco = 0 + elif vcofreq < 2711e6: + vco=1 + elif vcofreq < 3025e6: + vco=2 + elif vcofreq < 3341e6: + vco=3 + elif vcofreq < 3727e6: + vco=4 + elif vcofreq < 4143e6: + vco=5 + elif vcofreq < 4493e6: + vco=6 + else: + vco=7 + + self._set_osc(vco) + + # Set CP current + adc_val = 0 + while adc_val == 0 or adc_val == 7: + (byte1,byte2) = self._read_status() + adc_val = byte1 >> 2 + if(adc_val == 0): + if vco <= 0: + return (False, 0) + else: + vco = vco - 1 + elif(adc_val == 7): + if(vco >= 7): + return (False, 0) + else: + vco = vco + 1 + self._set_osc(vco) + if adc_val == 1 or adc_val == 2: + self._set_cp(1) + elif adc_val == 3 or adc_val == 4: + self._set_cp(2) + else: + self._set_cp(3) + + return (True, self.n * self._refclk_freq() / self.r) + + def is_quadrature(self): + """ + Return True if this board requires both I & Q analog channels. + + This bit of info is useful when setting up the USRP Rx mux register. + """ + return True + +# hook this daughterboard class into the auto-instantiation framework +db_instantiator.add(usrp_dbid.DBS_RX, lambda usrp, which : (db_dbs_rx(usrp, which),)) diff --git a/gr-usrp/src/db_flexrf.py b/gr-usrp/src/db_flexrf.py new file mode 100644 index 000000000..a00cfe896 --- /dev/null +++ b/gr-usrp/src/db_flexrf.py @@ -0,0 +1,680 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import usrp1 +import time,math + +import usrp_dbid +import db_base +import db_instantiator +from usrp_fpga_regs import * + +#debug_using_gui = True # Must be set to True or False +debug_using_gui = False # Must be set to True or False + +if debug_using_gui: + import flexrf_debug_gui + +# d'board i/o pin defs +# Tx and Rx have shared defs, but different i/o regs +AUX_RXAGC = (1 << 8) +POWER_UP = (1 << 7) # enables power supply +RX_TXN = (1 << 6) # Tx only: T/R antenna switch for TX/RX port +RX2_RX1N = (1 << 6) # Rx only: antenna switch between RX2 and TX/RX port +ENABLE = (1 << 5) # enables mixer +AUX_SEN = (1 << 4) +AUX_SCLK = (1 << 3) +PLL_LOCK_DETECT = (1 << 2) +AUX_SDO = (1 << 1) +CLOCK_OUT = (1 << 0) + +SPI_ENABLE_TX_A = usrp1.SPI_ENABLE_TX_A +SPI_ENABLE_TX_B = usrp1.SPI_ENABLE_TX_B +SPI_ENABLE_RX_A = usrp1.SPI_ENABLE_RX_A +SPI_ENABLE_RX_B = usrp1.SPI_ENABLE_RX_B + +class flexrf_base(db_base.db_base): + """ + Abstract base class for all flexrf boards. + + Derive board specific subclasses from db_flexrf_base_{tx,rx} + """ + def __init__(self, usrp, which): + """ + @param usrp: instance of usrp.source_c + @param which: which side: 0 or 1 corresponding to side A or B respectively + @type which: int + """ + # sets _u _which _tx and _slot + db_base.db_base.__init__(self, usrp, which) + + self.first = True + self.spi_format = usrp1.SPI_FMT_MSB | usrp1.SPI_FMT_HDR_0 + + self._u._write_oe(self._which, 0, 0xffff) # turn off all outputs + self._enable_refclk(False) # disable refclk + + g = self.gain_range() # initialize gain + self.set_gain(float(g[0]+g[1]) / 2) + + self.set_auto_tr(False) + + if debug_using_gui: + title = "FlexRF Debug Rx" + if self._tx: + title = "FlexRF Debug Tx" + self.gui = flexrf_debug_gui.flexrf_debug_gui(self, title) + self.gui.Show(True) + + + def __del__(self): + #print "flexrf_base.__del__" + self._u.write_io(self._which, self.power_off, POWER_UP) # turn off power to board + self._enable_refclk(False) # turn off refclk + self.set_auto_tr(False) + + def _write_all(self, R, control, N): + """ + Write R counter latch, control latch and N counter latch to VCO. + + Adds 10ms delay between writing control and N if this is first call. + This is the required power-up sequence. + + @param $: 24-bit R counter latch + @type R: int + @param control: 24-bit control latch + @type control: int + @param N: 24-bit N counter latch + @type N: int + """ + self._write_R(R) + self._write_control( control) + if self.first: + time.sleep(0.010) + self.first = False + self._write_N(N) + + def _write_control(self, control): + self._write_it((control & ~0x3) | 0) + + def _write_R(self, R): + self._write_it((R & ~0x3) | 1) + + def _write_N(self, N): + self._write_it((N & ~0x3) | 2) + + def _write_it(self, v): + s = ''.join((chr((v >> 16) & 0xff), + chr((v >> 8) & 0xff), + chr(v & 0xff))) + self._u._write_spi(0, self.spi_enable, self.spi_format, s) + + def _lock_detect(self): + """ + @returns: the value of the VCO/PLL lock detect bit. + @rtype: 0 or 1 + """ + if self._u.read_io(self._which) & PLL_LOCK_DETECT: + return True + else: # Give it a second chance + if self._u.read_io(self._which) & PLL_LOCK_DETECT: + return True + else: + return False + + def _compute_regs(self, freq): + """ + Determine values of R, control, and N registers, along with actual freq. + + @param freq: target frequency in Hz + @type freq: float + @returns: (R, control, N, actual_freq) + @rtype: tuple(int, int, int, float) + + Override this in derived classes. + """ + raise NotImplementedError + + def _refclk_freq(self): + # return float(self._u.fpga_master_clock_freq())/self._refclk_divisor() + return 64e6/self._refclk_divisor() + + def set_freq(self, freq): + """ + @returns (ok, actual_baseband_freq) where: + ok is True or False and indicates success or failure, + actual_baseband_freq is the RF frequency that corresponds to DC in the IF. + """ + + # Offsetting the LO helps get the Tx carrier leakage out of the way. + # This also ensures that on Rx, we're not getting hosed by the + # FPGA's DC removal loop's time constant. We were seeing a + # problem when running with discontinuous transmission. + # Offsetting the LO made the problem go away. + freq += self.lo_offset + + R, control, N, actual_freq = self._compute_regs(freq) + if R==0: + return(False,0) + self._write_all(R, control, N) + return (self._lock_detect(), actual_freq) + + def gain_range(self): + """ + Return range of gain that can be set by this d'board. + + @returns (min_gain, max_gain, step_size) + Where gains are expressed in decibels (your mileage may vary) + """ + return (self._u.pga_min(), self._u.pga_max(), self._u.pga_db_per_step()) + + def set_gain(self, gain): + """ + Set the gain. + + @param gain: gain in decibels + @returns True/False + """ + return self._set_pga(gain) + + def _set_pga(self, pga_gain): + if(self._which == 0): + self._u.set_pga (0, pga_gain) + self._u.set_pga (1, pga_gain) + else: + self._u.set_pga (2, pga_gain) + self._u.set_pga (3, pga_gain) + + def is_quadrature(self): + """ + Return True if this board requires both I & Q analog channels. + + This bit of info is useful when setting up the USRP Rx mux register. + """ + return True + +# ---------------------------------------------------------------- + +class flexrf_base_tx(flexrf_base): + def __init__(self, usrp, which): + """ + @param usrp: instance of usrp.sink_c + @param which: 0 or 1 corresponding to side TX_A or TX_B respectively. + """ + flexrf_base.__init__(self, usrp, which) + self.spi_enable = (SPI_ENABLE_TX_A, SPI_ENABLE_TX_B)[which] + + # power up the transmit side, but don't enable the mixer + self._u._write_oe(self._which,(POWER_UP|RX_TXN|ENABLE), 0xffff) + self._u.write_io(self._which, (self.power_on|RX_TXN), (POWER_UP|RX_TXN|ENABLE)) + self.lo_offset = 4e6 # FIXME may want to be a function of d'board + + def __del__(self): + #print "flexrf_base_tx.__del__" + # Power down and leave the T/R switch in the R position + self._u.write_io(self._which, (self.power_off|RX_TXN), (POWER_UP|RX_TXN|ENABLE)) + flexrf_base.__del__(self) + + def set_auto_tr(self, on): + if on: + self.set_atr_mask (RX_TXN | ENABLE) + self.set_atr_txval(0 | ENABLE) + self.set_atr_rxval(RX_TXN | 0) + else: + self.set_atr_mask (0) + self.set_atr_txval(0) + self.set_atr_rxval(0) + + def set_enable(self, on): + """ + Enable transmitter if on is True + """ + mask = RX_TXN | ENABLE + if on: + v = ENABLE + else: + v = RX_TXN + self._u.write_io(self._which, v, mask) + + def gain_range(self): + """ + Return range of gain that can be set by this d'board. + + @returns (min_gain, max_gain, step_size) + Where gains are expressed in decibels (your mileage may vary) + + Flex Tx boards require that the PGA be maxed out to properly bias their circuitry. + """ + g = self._u.pga_max() + return (g, g, 1.0) + + def set_gain(self, gain): + """ + Set the gain. + + @param gain: gain in decibels + @returns True/False + """ + return self._set_pga(self._u.pga_max()) + + +class flexrf_base_rx(flexrf_base): + def __init__(self, usrp, which): + """ + @param usrp: instance of usrp.source_c + @param which: 0 or 1 corresponding to side RX_A or RX_B respectively. + """ + flexrf_base.__init__(self, usrp, which) + self.spi_enable = (SPI_ENABLE_RX_A, SPI_ENABLE_RX_B)[which] + + self._u._write_oe(self._which, (POWER_UP|RX2_RX1N|ENABLE), 0xffff) + self._u.write_io(self._which, (self.power_on|RX2_RX1N|ENABLE), (POWER_UP|RX2_RX1N|ENABLE)) + + # set up for RX on TX/RX port + self.select_rx_antenna('TX/RX') + + self.bypass_adc_buffers(True) + + self.lo_offset = -4e6 # FIXME may want to be a function of d'board + + def __del__(self): + # print "flexrf_base_rx.__del__" + # Power down + self._u.write_io(self._which, self.power_off, (POWER_UP|ENABLE)) + flexrf_base.__del__(self) + + def set_auto_tr(self, on): + if on: + self.set_atr_mask (ENABLE) + self.set_atr_txval( 0) + self.set_atr_rxval(ENABLE) + else: + self.set_atr_mask (0) + self.set_atr_txval(0) + self.set_atr_rxval(0) + + def select_rx_antenna(self, which_antenna): + """ + Specify which antenna port to use for reception. + @param which_antenna: either 'TX/RX' or 'RX2' + """ + if which_antenna in (0, 'TX/RX'): + self._u.write_io(self._which, 0, RX2_RX1N) + elif which_antenna in (1, 'RX2'): + self._u.write_io(self._which, RX2_RX1N, RX2_RX1N) + else: + raise ValueError, "which_antenna must be either 'TX/RX' or 'RX2'" + + def set_gain(self, gain): + """ + Set the gain. + + @param gain: gain in decibels + @returns True/False + """ + maxgain = self.gain_range()[1] - self._u.pga_max() + if gain > maxgain: + pga_gain = gain-maxgain + assert pga_gain <= self._u.pga_max() + agc_gain = maxgain + else: + pga_gain = 0 + agc_gain = gain + V_maxgain = .2 + V_mingain = 1.2 + V_fullscale = 3.3 + dac_value = (agc_gain*(V_maxgain-V_mingain)/maxgain + V_mingain)*4096/V_fullscale + assert dac_value>=0 and dac_value<4096 + return self._u.write_aux_dac(self._which, 0, int(dac_value)) and \ + self._set_pga(int(pga_gain)) + + +# ---------------------------------------------------------------- + +class _AD4360_common(object): + def __init__(self): + # R-Register Common Values + self.R_RSV = 0 # bits 23,22 + self.BSC = 3 # bits 21,20 Div by 8 to be safe + self.TEST = 0 # bit 19 + self.LDP = 1 # bit 18 + self.ABP = 0 # bit 17,16 3ns + + # N-Register Common Values + self.N_RSV = 0 # bit 7 + + # Control Register Common Values + self.PD = 0 # bits 21,20 Normal operation + self.PL = 0 # bits 13,12 11mA + self.MTLD = 1 # bit 11 enabled + self.CPG = 0 # bit 10 CP setting 1 + self.CP3S = 0 # bit 9 Normal + self.PDP = 1 # bit 8 Positive + self.MUXOUT = 1 # bits 7:5 Digital Lock Detect + self.CR = 0 # bit 4 Normal + self.PC = 1 # bits 3,2 Core power 10mA + + def _compute_regs(self, freq): + """ + Determine values of R, control, and N registers, along with actual freq. + + @param freq: target frequency in Hz + @type freq: float + @returns: (R, control, N, actual_freq) + @rtype: tuple(int, int, int, float) + """ + + # Band-specific N-Register Values + phdet_freq = self._refclk_freq()/self.R_DIV + desired_n = round(freq*self.freq_mult/phdet_freq) + actual_freq = desired_n * phdet_freq + B = math.floor(desired_n/self._prescaler()) + A = desired_n - self._prescaler()*B + self.B_DIV = int(B) # bits 20:8 + self.A_DIV = int(A) # bit 6:2 + #assert self.B_DIV >= self.A_DIV + if self.B_DIV < self.A_DIV: + return (0,0,0,0) + R = (self.R_RSV<<22) | (self.BSC<<20) | (self.TEST<<19) | (self.LDP<<18) \ + | (self.ABP<<16) | (self.R_DIV<<2) + + control = (self.P<<22) | (self.PD<<20) | (self.CP2<<17) | (self.CP1<<14) | (self.PL<<12) \ + | (self.MTLD<<11) | (self.CPG<<10) | (self.CP3S<<9) | (self.PDP<<8) | \ + (self.MUXOUT<<5) | (self.CR<<4) | (self.PC<<2) + + N = (self.DIVSEL<<23) | (self.DIV2<<22) | (self.CPGAIN<<21) | (self.B_DIV<<8) | \ + (self.N_RSV<<7) | (self.A_DIV<<2) + + return (R,control,N,actual_freq/self.freq_mult) + + def _refclk_divisor(self): + """ + Return value to stick in REFCLK_DIVISOR register + """ + return 1 + + def _prescaler(self): + if self.P == 0: + return 8 + elif self.P == 1: + return 16 + else: + return 32 + +#---------------------------------------------------------------------- +class _2400_common(_AD4360_common): + def __init__(self): + _AD4360_common.__init__(self) + + # Band-specific R-Register Values + self.R_DIV = 16 # bits 15:2 + + # Band-specific C-Register values + self.P = 1 # bits 23,22 Div by 16/17 + self.CP2 = 7 # bits 19:17 + self.CP1 = 7 # bits 16:14 + + # Band specifc N-Register Values + self.DIVSEL = 0 # bit 23 + self.DIV2 = 0 # bit 22 + self.CPGAIN = 0 # bit 21 + self.freq_mult = 1 + + def freq_range(self): # FIXME + return (2300e6, 2700e6, 4e6) + +#---------------------------------------------------------------------- +class _1200_common(_AD4360_common): + def __init__(self): + _AD4360_common.__init__(self) + + # Band-specific R-Register Values + self.R_DIV = 16 # bits 15:2 DIV by 16 for a 1 MHz phase detector freq + + # Band-specific C-Register values + self.P = 1 # bits 23,22 Div by 16/17 + self.CP2 = 7 # bits 19:17 1.25 mA + self.CP1 = 7 # bits 16:14 1.25 mA + + # Band specifc N-Register Values + self.DIVSEL = 0 # bit 23 + self.DIV2 = 1 # bit 22 + self.CPGAIN = 0 # bit 21 + self.freq_mult = 2 + + def freq_range(self): # FIXME + return (1150e6, 1350e6, 4e6) + +#------------------------------------------------------------------------- +class _1800_common(_AD4360_common): + def __init__(self): + _AD4360_common.__init__(self) + + # Band-specific R-Register Values + self.R_DIV = 16 # bits 15:2 DIV by 16 for a 1 MHz phase detector freq + + # Band-specific C-Register values + self.P = 1 # bits 23,22 Div by 16/17 + self.CP2 = 7 # bits 19:17 1.25 mA + self.CP1 = 7 # bits 16:14 1.25 mA + + # Band specifc N-Register Values + self.DIVSEL = 0 # bit 23 + self.DIV2 = 0 # bit 22 + self.freq_mult = 1 + self.CPGAIN = 0 # bit 21 + + def freq_range(self): # FIXME + return (1600e6, 2000e6, 4e6) + +#------------------------------------------------------------------------- +class _900_common(_AD4360_common): + def __init__(self): + _AD4360_common.__init__(self) + + # Band-specific R-Register Values + self.R_DIV = 16 # bits 15:2 DIV by 16 for a 1 MHz phase detector freq + + # Band-specific C-Register values + self.P = 1 # bits 23,22 Div by 16/17 + self.CP2 = 7 # bits 19:17 1.25 mA + self.CP1 = 7 # bits 16:14 1.25 mA + + # Band specifc N-Register Values + self.DIVSEL = 0 # bit 23 + self.DIV2 = 1 # bit 22 + self.freq_mult = 2 + self.CPGAIN = 0 # bit 21 + + def freq_range(self): # FIXME + return (800e6, 1000e6, 4e6) + +#------------------------------------------------------------------------- +class _400_common(_AD4360_common): + def __init__(self): + _AD4360_common.__init__(self) + + # Band-specific R-Register Values + self.R_DIV = 16 # bits 15:2 + + # Band-specific C-Register values + self.P = 0 # bits 23,22 Div by 8/9 + self.CP2 = 7 # bits 19:17 1.25 mA + self.CP1 = 7 # bits 16:14 1.25 mA + + # Band specifc N-Register Values These are different for TX/RX + self.DIVSEL = 0 # bit 23 + if self._tx: + self.DIV2 = 1 # bit 22 + else: + self.DIV2 = 0 # bit 22 # RX side has built-in DIV2 in AD8348 + self.freq_mult = 2 + + self.CPGAIN = 0 # bit 21 + + def freq_range(self): + #return (350e6, 465e6, 1e6) # FIXME prototype + return (400e6, 500e6, 1e6) # final version + + +#------------------------------------------------------------ +class db_flexrf_2400_tx(_2400_common, flexrf_base_tx): + def __init__(self, usrp, which): + self.power_on = ~POWER_UP + self.power_off = ~POWER_UP # powering it off kills the serial bus + flexrf_base_tx.__init__(self, usrp, which) + _2400_common.__init__(self) + +class db_flexrf_2400_rx(_2400_common, flexrf_base_rx): + def __init__(self, usrp, which): + self.power_on = ~POWER_UP + self.power_off = ~POWER_UP # Powering it off kills the serial bus + flexrf_base_rx.__init__(self, usrp, which) + _2400_common.__init__(self) + + def gain_range(self): + """ + Return range of gain that can be set by this d'board. + + @returns (min_gain, max_gain, step_size) + Where gains are expressed in decibels (your mileage may vary) + """ + return (self._u.pga_min(), self._u.pga_max() + 70, 0.05) + + def i_and_q_swapped(self): + return True + +class db_flexrf_1200_tx(_1200_common, flexrf_base_tx): + def __init__(self, usrp, which): + self.power_on = ~POWER_UP + self.power_off = ~POWER_UP # powering it off kills the serial bus + flexrf_base_tx.__init__(self, usrp, which) + _1200_common.__init__(self) + +class db_flexrf_1200_rx(_1200_common, flexrf_base_rx): + def __init__(self, usrp, which): + self.power_on = ~POWER_UP + self.power_off = ~POWER_UP # powering it off kills the serial bus + flexrf_base_rx.__init__(self, usrp, which) + _1200_common.__init__(self) + + def gain_range(self): + """ + Return range of gain that can be set by this d'board. + + @returns (min_gain, max_gain, step_size) + Where gains are expressed in decibels (your mileage may vary) + """ + return (self._u.pga_min(), self._u.pga_max() + 70, 0.05) + + def i_and_q_swapped(self): + return True + +class db_flexrf_1800_tx(_1800_common, flexrf_base_tx): + def __init__(self, usrp, which): + self.power_on = ~POWER_UP + self.power_off = ~POWER_UP # powering it off kills the serial bus + flexrf_base_tx.__init__(self, usrp, which) + _1800_common.__init__(self) + +class db_flexrf_1800_rx(_1800_common, flexrf_base_rx): + def __init__(self, usrp, which): + self.power_on = ~POWER_UP + self.power_off = ~POWER_UP # powering it off kills the serial bus + flexrf_base_rx.__init__(self, usrp, which) + _1800_common.__init__(self) + + def gain_range(self): + """ + Return range of gain that can be set by this d'board. + + @returns (min_gain, max_gain, step_size) + Where gains are expressed in decibels (your mileage may vary) + """ + return (self._u.pga_min(), self._u.pga_max() + 70, 0.05) + + def i_and_q_swapped(self): + return True + +class db_flexrf_900_tx(_900_common, flexrf_base_tx): + def __init__(self, usrp, which): + self.power_on = ~POWER_UP + self.power_off = ~POWER_UP # powering it off kills the serial bus + flexrf_base_tx.__init__(self, usrp, which) + _900_common.__init__(self) + +class db_flexrf_900_rx(_900_common, flexrf_base_rx): + def __init__(self, usrp, which): + self.power_on = ~POWER_UP + self.power_off = ~POWER_UP # powering it off kills the serial bus + flexrf_base_rx.__init__(self, usrp, which) + _900_common.__init__(self) + + def gain_range(self): + """ + Return range of gain that can be set by this d'board. + + @returns (min_gain, max_gain, step_size) + Where gains are expressed in decibels (your mileage may vary) + """ + return (self._u.pga_min(), self._u.pga_max() + 70, 0.05) + + def i_and_q_swapped(self): + return True + +class db_flexrf_400_tx(_400_common, flexrf_base_tx): + def __init__(self, usrp, which): + self.power_on = POWER_UP + self.power_off = ~POWER_UP + flexrf_base_tx.__init__(self, usrp, which) + _400_common.__init__(self) + +class db_flexrf_400_rx(_400_common, flexrf_base_rx): + def __init__(self, usrp, which): + self.power_on = POWER_UP + self.power_off = ~POWER_UP + flexrf_base_rx.__init__(self, usrp, which) + _400_common.__init__(self) + + def gain_range(self): + """ + Return range of gain that can be set by this d'board. + + @returns (min_gain, max_gain, step_size) + Where gains are expressed in decibels (your mileage may vary) + """ + return (self._u.pga_min(), self._u.pga_max() + 45, 0.035) + + def i_and_q_swapped(self): + return True + +# hook these daughterboard classes into the auto-instantiation framework + +db_instantiator.add(usrp_dbid.FLEX_2400_TX, lambda usrp, which : (db_flexrf_2400_tx(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_2400_RX, lambda usrp, which : (db_flexrf_2400_rx(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_1200_TX, lambda usrp, which : (db_flexrf_1200_tx(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_1200_RX, lambda usrp, which : (db_flexrf_1200_rx(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_1800_TX, lambda usrp, which : (db_flexrf_1800_tx(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_1800_RX, lambda usrp, which : (db_flexrf_1800_rx(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_900_TX, lambda usrp, which : (db_flexrf_900_tx(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_900_RX, lambda usrp, which : (db_flexrf_900_rx(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_400_TX, lambda usrp, which : (db_flexrf_400_tx(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_400_RX, lambda usrp, which : (db_flexrf_400_rx(usrp, which),)) diff --git a/gr-usrp/src/db_flexrf_mimo.py b/gr-usrp/src/db_flexrf_mimo.py new file mode 100644 index 000000000..71c49a8f0 --- /dev/null +++ b/gr-usrp/src/db_flexrf_mimo.py @@ -0,0 +1,286 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import usrp1 +import time,math + +import usrp_dbid +import db_base +import db_instantiator +from usrp_fpga_regs import * +from db_flexrf import * + +# self._u.fpga_master_clock_freq() + +# MIMO Classes +class db_flexrf_2400_tx_mimo_a(db_flexrf_2400_tx): + def __init__(self, usrp, which): + db_flexrf_2400_tx.__init__(self, usrp, which) + self._enable_refclk(True) + self.R_DIV = 1 + + def _refclk_divisor(self): + """ + Return value to stick in REFCLK_DIVISOR register + """ + return 16 + +class db_flexrf_2400_rx_mimo_a(db_flexrf_2400_rx): + def __init__(self, usrp, which): + db_flexrf_2400_rx.__init__(self, usrp, which) + self._enable_refclk(True) + self.R_DIV = 1 + + def _refclk_divisor(self): + """ + Return value to stick in REFCLK_DIVISOR register + """ + return 16 + +class db_flexrf_2400_tx_mimo_b(db_flexrf_2400_tx): + def __init__(self, usrp, which): + db_flexrf_2400_tx.__init__(self, usrp, which) + self.R_DIV = 16 + + def _refclk_divisor(self): + """ + Return value to stick in REFCLK_DIVISOR register + """ + return 1 + +class db_flexrf_2400_rx_mimo_b(db_flexrf_2400_rx): + def __init__(self, usrp, which): + db_flexrf_2400_rx.__init__(self, usrp, which) + self.R_DIV = 16 + + def _refclk_divisor(self): + """ + Return value to stick in REFCLK_DIVISOR register + """ + return 1 + +class db_flexrf_1800_tx_mimo_a(db_flexrf_1800_tx): + def __init__(self, usrp, which): + db_flexrf_1800_tx.__init__(self, usrp, which) + self._enable_refclk(True) + self.R_DIV = 1 + + def _refclk_divisor(self): + """ + Return value to stick in REFCLK_DIVISOR register + """ + return 16 + +class db_flexrf_1800_rx_mimo_a(db_flexrf_1800_rx): + def __init__(self, usrp, which): + db_flexrf_1800_rx.__init__(self, usrp, which) + self._enable_refclk(True) + self.R_DIV = 1 + + def _refclk_divisor(self): + """ + Return value to stick in REFCLK_DIVISOR register + """ + return 16 + +class db_flexrf_1800_tx_mimo_b(db_flexrf_1800_tx): + def __init__(self, usrp, which): + db_flexrf_1800_tx.__init__(self, usrp, which) + self.R_DIV = 16 + + def _refclk_divisor(self): + """ + Return value to stick in REFCLK_DIVISOR register + """ + return 1 + +class db_flexrf_1800_rx_mimo_b(db_flexrf_1800_rx): + def __init__(self, usrp, which): + db_flexrf_1800_rx.__init__(self, usrp, which) + self.R_DIV = 16 + + def _refclk_divisor(self): + """ + Return value to stick in REFCLK_DIVISOR register + """ + return 1 + +class db_flexrf_1200_tx_mimo_a(db_flexrf_1200_tx): + def __init__(self, usrp, which): + db_flexrf_1200_tx.__init__(self, usrp, which) + self._enable_refclk(True) + self.R_DIV = 1 + + def _refclk_divisor(self): + """ + Return value to stick in REFCLK_DIVISOR register + """ + return 16 + +class db_flexrf_1200_rx_mimo_a(db_flexrf_1200_rx): + def __init__(self, usrp, which): + db_flexrf_1200_rx.__init__(self, usrp, which) + self._enable_refclk(True) + self.R_DIV = 1 + + def _refclk_divisor(self): + """ + Return value to stick in REFCLK_DIVISOR register + """ + return 16 + +class db_flexrf_1200_tx_mimo_b(db_flexrf_1200_tx): + def __init__(self, usrp, which): + db_flexrf_1200_tx.__init__(self, usrp, which) + self.R_DIV = 16 + + def _refclk_divisor(self): + """ + Return value to stick in REFCLK_DIVISOR register + """ + return 1 + +class db_flexrf_1200_rx_mimo_b(db_flexrf_1200_rx): + def __init__(self, usrp, which): + db_flexrf_1200_rx.__init__(self, usrp, which) + self.R_DIV = 16 + + def _refclk_divisor(self): + """ + Return value to stick in REFCLK_DIVISOR register + """ + return 1 + +class db_flexrf_900_tx_mimo_a(db_flexrf_900_tx): + def __init__(self, usrp, which): + db_flexrf_900_tx.__init__(self, usrp, which) + self._enable_refclk(True) + self.R_DIV = 1 + + def _refclk_divisor(self): + """ + Return value to stick in REFCLK_DIVISOR register + """ + return 16 + +class db_flexrf_900_rx_mimo_a(db_flexrf_900_rx): + def __init__(self, usrp, which): + db_flexrf_900_rx.__init__(self, usrp, which) + self._enable_refclk(True) + self.R_DIV = 1 + + def _refclk_divisor(self): + """ + Return value to stick in REFCLK_DIVISOR register + """ + return 16 + +class db_flexrf_900_tx_mimo_b(db_flexrf_900_tx): + def __init__(self, usrp, which): + db_flexrf_900_tx.__init__(self, usrp, which) + self.R_DIV = 16 + + def _refclk_divisor(self): + """ + Return value to stick in REFCLK_DIVISOR register + """ + return 1 + +class db_flexrf_900_rx_mimo_b(db_flexrf_900_rx): + def __init__(self, usrp, which): + db_flexrf_900_rx.__init__(self, usrp, which) + self.R_DIV = 16 + + def _refclk_divisor(self): + """ + Return value to stick in REFCLK_DIVISOR register + """ + return 1 + +class db_flexrf_400_tx_mimo_a(db_flexrf_400_tx): + def __init__(self, usrp, which): + db_flexrf_400_tx.__init__(self, usrp, which) + self._enable_refclk(True) + self.R_DIV = 1 + + def _refclk_divisor(self): + """ + Return value to stick in REFCLK_DIVISOR register + """ + return 16 + +class db_flexrf_400_rx_mimo_a(db_flexrf_400_rx): + def __init__(self, usrp, which): + db_flexrf_400_rx.__init__(self, usrp, which) + self._enable_refclk(True) + self.R_DIV = 1 + + def _refclk_divisor(self): + """ + Return value to stick in REFCLK_DIVISOR register + """ + return 16 + +class db_flexrf_400_tx_mimo_b(db_flexrf_400_tx): + def __init__(self, usrp, which): + db_flexrf_400_tx.__init__(self, usrp, which) + self.R_DIV = 16 + + def _refclk_divisor(self): + """ + Return value to stick in REFCLK_DIVISOR register + """ + return 1 + +class db_flexrf_400_rx_mimo_b(db_flexrf_400_rx): + def __init__(self, usrp, which): + db_flexrf_400_rx.__init__(self, usrp, which) + self.R_DIV = 16 + + def _refclk_divisor(self): + """ + Return value to stick in REFCLK_DIVISOR register + """ + return 1 + +# hook these daughterboard classes into the auto-instantiation framework +db_instantiator.add(usrp_dbid.FLEX_2400_TX_MIMO_A, lambda usrp, which : (db_flexrf_2400_tx_mimo_a(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_2400_RX_MIMO_A, lambda usrp, which : (db_flexrf_2400_rx_mimo_a(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_1800_TX_MIMO_A, lambda usrp, which : (db_flexrf_1800_tx_mimo_a(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_1800_RX_MIMO_A, lambda usrp, which : (db_flexrf_1800_rx_mimo_a(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_1200_TX_MIMO_A, lambda usrp, which : (db_flexrf_1200_tx_mimo_a(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_1200_RX_MIMO_A, lambda usrp, which : (db_flexrf_1200_rx_mimo_a(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_900_TX_MIMO_A, lambda usrp, which : (db_flexrf_900_tx_mimo_a(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_900_RX_MIMO_A, lambda usrp, which : (db_flexrf_900_rx_mimo_a(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_400_TX_MIMO_A, lambda usrp, which : (db_flexrf_400_tx_mimo_a(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_400_RX_MIMO_A, lambda usrp, which : (db_flexrf_400_rx_mimo_a(usrp, which),)) + +db_instantiator.add(usrp_dbid.FLEX_2400_TX_MIMO_B, lambda usrp, which : (db_flexrf_2400_tx_mimo_b(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_2400_RX_MIMO_B, lambda usrp, which : (db_flexrf_2400_rx_mimo_b(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_1800_TX_MIMO_B, lambda usrp, which : (db_flexrf_1800_tx_mimo_b(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_1800_RX_MIMO_B, lambda usrp, which : (db_flexrf_1800_rx_mimo_b(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_1200_TX_MIMO_B, lambda usrp, which : (db_flexrf_1200_tx_mimo_b(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_1200_RX_MIMO_B, lambda usrp, which : (db_flexrf_1200_rx_mimo_b(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_900_TX_MIMO_B, lambda usrp, which : (db_flexrf_900_tx_mimo_b(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_900_RX_MIMO_B, lambda usrp, which : (db_flexrf_900_rx_mimo_b(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_400_TX_MIMO_B, lambda usrp, which : (db_flexrf_400_tx_mimo_b(usrp, which),)) +db_instantiator.add(usrp_dbid.FLEX_400_RX_MIMO_B, lambda usrp, which : (db_flexrf_400_rx_mimo_b(usrp, which),)) + diff --git a/gr-usrp/src/db_instantiator.py b/gr-usrp/src/db_instantiator.py new file mode 100644 index 000000000..db10de4a6 --- /dev/null +++ b/gr-usrp/src/db_instantiator.py @@ -0,0 +1,31 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +_instantiator_map = {} + +def add(dbid, instantiator): + _instantiator_map[dbid] = instantiator + +def instantiate(usrp, which): + dbid = usrp.daughterboard_id(which) + if _instantiator_map.has_key(dbid): + return _instantiator_map[dbid](usrp, which) + raise ValueError, "No class defined to handle daughterboard (dbid = %d)" % (dbid,) diff --git a/gr-usrp/src/db_tv_rx.py b/gr-usrp/src/db_tv_rx.py new file mode 100644 index 000000000..05504553a --- /dev/null +++ b/gr-usrp/src/db_tv_rx.py @@ -0,0 +1,198 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +__all__ = ['tv_rx'] + +import math +import usrp_dbid +import db_base +import db_instantiator + +def int_seq_to_str(seq): + """convert a sequence of integers into a string""" + return ''.join (map (chr, seq)) + +def str_to_int_seq(str): + """convert a string to a list of integers""" + return map (ord, str) + +def control_byte_1(fast_tuning_p, reference_divisor): + c = 0x88 + if fast_tuning_p: + c |= 0x40 + if reference_divisor == 512: + c |= 0x3 << 1 + elif reference_divisor == 640: + c |= 0x0 << 1 + elif reference_divisor == 1024: + c |= 0x1 << 1 + else: + assert 0 + return c + +def control_byte_2(target_freq, shutdown_tx_PGA): + if target_freq < 158e6: # VHF low + c = 0xa0 + elif target_freq < 464e6: # VHF high + c = 0x90 + else: # UHF + c = 0x30 + if shutdown_tx_PGA: + c |= 0x08 + return c + +class db_tv_rx(db_base.db_base): + def __init__(self, usrp, which, first_IF, second_IF): + """ + Control Microtune 4937 based USRP daughterboard. + + @param usrp: instance of usrp.source_c + @param which: which side: 0 or 1 corresponding to RX_A or RX_B respectively + @type which: int + """ + # sets _u and _which + db_base.db_base.__init__(self, usrp, which) + + self._i2c_addr = (0x60, 0x61)[which] + + self._first_IF = first_IF + self._second_IF = second_IF + self._reference_divisor = 640 + self._fast_tuning = False + self._inverted = False # FIXME get rid of this + + g = self.gain_range() # initialize gain + self.set_gain(float(g[0]+g[1]) / 2) + + self.bypass_adc_buffers(False) + + # Gain setting + def _set_rfagc(self,gain): + assert gain <= 60 and gain >= 0 + # FIXME this has a 0.5V step between gain = 60 and gain = 59. + # Why are there two cases instead of a single linear case? + if gain == 60: + voltage = 4 + else: + voltage = gain/60.0 * 2.25 + 1.25 + dacword = int(4096*voltage/1.22/3.3) # 1.22 = opamp gain + + assert dacword>=0 and dacword<4096 + self._u.write_aux_dac(self._which, 1, dacword) + + def _set_ifagc(self,gain): + assert gain <= 35 and gain >= 0 + voltage = gain/35.0 * 2.1 + 1.4 + dacword = int(4096*voltage/1.22/3.3) # 1.22 = opamp gain + + assert dacword>=0 and dacword<4096 + self._u.write_aux_dac(self._which, 0, dacword) + + def _set_pga(self,pga_gain): + assert pga_gain >=0 and pga_gain <=20 + if(self._which == 0): + self._u.set_pga (0, pga_gain) + else: + self._u.set_pga (2, pga_gain) + + def gain_range(self): + return (0, 115, 1) + + def set_gain(self,gain): + assert gain>=0 and gain<=115 + if gain>60: + rfgain = 60 + gain = gain - 60 + else: + rfgain = gain + gain = 0 + if gain > 35: + ifgain = 35 + gain = gain - 35 + else: + ifgain = gain + gain = 0 + pgagain = gain + self._set_rfagc(rfgain) + self._set_ifagc(ifgain) + self._set_pga(pgagain) + + def freq_range(self): + return (50e6, 860e6, 10e3) + + def set_freq(self, target_freq): + """ + @returns (ok, actual_baseband_freq) where: + ok is True or False and indicates success or failure, + actual_baseband_freq is the RF frequency that corresponds to DC in the IF. + """ + r = self.freq_range() + if target_freq < r[0] or target_freq > r[1]: + return (False, 0) + + target_lo_freq = target_freq + self._first_IF; # High side mixing + f_ref = 4e6 / self._reference_divisor # frequency steps + + divisor = int((target_lo_freq + (f_ref * 4)) / (f_ref * 8)) + actual_lo_freq = (f_ref * 8 * divisor) + actual_freq = actual_lo_freq - self._first_IF; + + if (divisor & ~0x7fff) != 0: # must be 15-bits or less + return (False, 0) + + # build i2c command string + buf = [0] * 4 + buf[0] = (divisor >> 8) & 0xff # DB1 + buf[1] = divisor & 0xff # DB2 + buf[2] = control_byte_1(self._fast_tuning, self._reference_divisor) + buf[3] = control_byte_2(actual_freq, True) + + ok = self._u.write_i2c(self._i2c_addr, int_seq_to_str (buf)) + + return (ok, actual_freq - self._second_IF) + + def is_quadrature(self): + """ + Return True if this board requires both I & Q analog channels. + + This bit of info is useful when setting up the USRP Rx mux register. + """ + return False + + def spectrum_inverted(self): + """ + The 43.75 MHz version is inverted + """ + return self._inverted + +# hook this daughterboard class into the auto-instantiation framework + +# With MT4937DI5-3x7702 with second downconversion +db_instantiator.add(usrp_dbid.TV_RX, + lambda usrp, which : (db_tv_rx(usrp, which, 43.75e6, 5.75e6),)) + +# With MT4937DI5-3x8680, and 3x8769 without second downconversion +db_instantiator.add(usrp_dbid.TV_RX_REV_2, + lambda usrp, which : (db_tv_rx(usrp, which, 44e6, 20e6),)) + +# With MT4937DI5-3x7901 without second downconversion, basically the same as tvrx2 +db_instantiator.add(usrp_dbid.TV_RX_REV_3, + lambda usrp, which : (db_tv_rx(usrp, which, 44e6, 20e6),)) diff --git a/gr-usrp/src/flexrf_debug_gui.py b/gr-usrp/src/flexrf_debug_gui.py new file mode 100755 index 000000000..173d48a71 --- /dev/null +++ b/gr-usrp/src/flexrf_debug_gui.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +import sys +import wx +from gnuradio.wxgui import form + +class flexrf_debug_gui(wx.Frame): + def __init__(self, flexrf, title="Flexrf Debug"): + wx.Frame.__init__(self, None, -1, title) + + self.flexrf = flexrf + + self.CreateStatusBar (1) + + self.panel = wx.Panel(self, -1) + self.vbox = wx.BoxSizer(wx.VERTICAL) + self.panel.SetSizer(self.vbox) + self.panel.SetAutoLayout(True) + + self._create_form() + + self.vbox.Fit(self.panel) + + self.frame_vbox = wx.BoxSizer(wx.VERTICAL) + self.frame_vbox.Add(self.panel, 1, wx.EXPAND) + self.SetSizer(self.frame_vbox) + self.SetAutoLayout(True) + self.frame_vbox.Fit(self) + + def _create_form(self): + self._create_set_freq() + self._create_write_fpga() + self._create_write_all() + self._create_write_it() + #self._create_set_gain() + + # ---------------------------------------------------------------- + + def _create_set_freq(self): + + def _set_freq(kv): + return self.flexrf.set_freq(kv['freq'])[0] + + sbs = wx.StaticBoxSizer(wx.StaticBox(self.panel), wx.HORIZONTAL) + sbs.Add((5,0), 0.1) # stretchy space + #sbs.Add(wx.StaticText(self.panel, -1, "set_freq "), 0, 0) + #sbs.Add((5,0), 0.1) # stretchy space + myform = form.form() + myform['freq'] = form.float_field(self.panel, sbs, "Set Frequency") + sbs.Add((5,0), 0.1) # stretchy space + sbs.Add(form.button_with_callback(self.panel, "Do It!", + self._generic_doit(_set_freq, myform)), 1, wx.EXPAND) + sbs.Add((5,0), 0.1) # stretchy space + self.vbox.Add(sbs, 0, wx.EXPAND) + + + def _create_write_fpga(self): + + def _write_fpga(kv): + return self.flexrf._u._write_fpga_reg(kv['regno'], kv['value']) + + sbs = wx.StaticBoxSizer(wx.StaticBox(self.panel), wx.HORIZONTAL) + sbs.Add((5,0), 0.1) # stretchy space + sbs.Add(wx.StaticText(self.panel, -1, "write_fpga_reg "), 0, 0) + sbs.Add((5,0), 0.1) # stretchy space + myform = form.form() + myform['regno'] = form.int_field(self.panel, sbs, "regno") + sbs.Add((5,0), 0.1) # stretchy space + myform['value'] = form.int_field(self.panel, sbs, "value") + sbs.Add((5,0), 0.1) # stretchy space + sbs.Add(form.button_with_callback(self.panel, "Do It!", + self._generic_doit(_write_fpga, myform)), 1, wx.EXPAND) + sbs.Add((5,0), 0.1) # stretchy space + self.vbox.Add(sbs, 0, wx.EXPAND) + + + def _create_write_all(self): + + def _write_all(kv): + self.flexrf._write_all(kv['R'], kv['control'], kv['N']) # void + return True + + sbs = wx.StaticBoxSizer(wx.StaticBox(self.panel), wx.HORIZONTAL) + sbs.Add((5,0), 0.1) # stretchy space + sbs.Add(wx.StaticText(self.panel, -1, "write_all "), 0, 0) + sbs.Add((5,0), 0.1) # stretchy space + myform = form.form() + myform['R'] = form.int_field(self.panel, sbs, "R") + sbs.Add((5,0), 0.1) # stretchy space + myform['control'] = form.int_field(self.panel, sbs, "control") + sbs.Add((5,0), 0.1) # stretchy space + myform['N'] = form.int_field(self.panel, sbs, "N") + sbs.Add((5,0), 0.1) # stretchy space + sbs.Add(form.button_with_callback(self.panel, "Do It!", + self._generic_doit(_write_all, myform)), 1, wx.EXPAND) + sbs.Add((5,0), 0.1) # stretchy space + self.vbox.Add(sbs, 0, wx.EXPAND) + + + def _create_write_it(self): + + def _write_it(kv): + self.flexrf._write_it(kv['v']) # void + return True + + sbs = wx.StaticBoxSizer(wx.StaticBox(self.panel), wx.HORIZONTAL) + sbs.Add((5,0), 0.1) # stretchy space + sbs.Add(wx.StaticText(self.panel, -1, "write_it "), 0, 0) + sbs.Add((5,0), 0.1) # stretchy space + myform = form.form() + myform['v'] = form.int_field(self.panel, sbs, "24-bit value") + sbs.Add((5,0), 0.1) # stretchy space + sbs.Add(form.button_with_callback(self.panel, "Do It!", + self._generic_doit(_write_it, myform)), 1, wx.EXPAND) + sbs.Add((5,0), 0.1) # stretchy space + self.vbox.Add(sbs, 0, wx.EXPAND) + + + # ---------------------------------------------------------------- + + def _set_status_msg(self, msg): + self.GetStatusBar().SetStatusText(msg, 0) + + def _generic_doit(self, callback, form): + + def button_callback(): + errors = form.check_input_for_errors() + if errors: + self._set_status_msg(errors[0]) + print '\n'.join(tuple(errors)) + else: + kv = form.get_key_vals() + if callback(kv): + self._set_status_msg("OK") + else: + self._set_status_msg("Failed") + + return button_callback + + + +if False and __name__ == '__main__': + + class demo_app (wx.App): + def __init__ (self): + wx.App.__init__(self) + + def OnInit (self): + frame = flexrf_debug_gui(None, "Debug FlexRF TX") + frame.Show(True) + self.SetTopWindow (frame) + return True + + app = demo_app() + app.MainLoop() + diff --git a/gr-usrp/src/qa_usrp.py b/gr-usrp/src/qa_usrp.py new file mode 100755 index 000000000..eb68c6407 --- /dev/null +++ b/gr-usrp/src/qa_usrp.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest +import usrp1 + +class qa_usrp (gr_unittest.TestCase): + + def setUp (self): + self.fg = gr.flow_graph () + + def tearDown (self): + self.fg = None + + def test_000_nop (self): + """Just see if we can import the module... + They may not have a USRP connected, etc. Don't try to run anything""" + pass + +if __name__ == '__main__': + gr_unittest.main () diff --git a/gr-usrp/src/run_tests.in b/gr-usrp/src/run_tests.in new file mode 100644 index 000000000..793cb2491 --- /dev/null +++ b/gr-usrp/src/run_tests.in @@ -0,0 +1,47 @@ +#!/bin/sh + +# All this strange PYTHONPATH manipulation is required to run our +# tests using our just built shared library and swig-generated python +# code prior to installation. + +# build tree == src tree unless you're doing a VPATH build. +# If you don't know what a VPATH build is, you're not doing one. Relax... + +prefix=@prefix@ +exec_prefix=@exec_prefix@ + +# Where to look in the build tree for our shared library +libbld=@abs_top_builddir@/gr-usrp/src +# Where to look in the src tree for swig generated python code +libsrc=@abs_top_srcdir@/gr-usrp/src +# Where to look in the src tree for hand written python code +py=@abs_top_srcdir@/gr-usrp/src + +# Where to look for GNU Radio python modules in current build tree +# FIXME this is wrong on a distcheck. We really need to ask gnuradio-core +# where it put its python files. +grpythonbld=@abs_top_builddir@/gnuradio-core/src/python/:@abs_top_builddir@/gnuradio-core/src/lib/swig/:@abs_top_builddir@/gnuradio-core/src/lib/swig/.libs + +PYTHONPATH="$grpythonbld:$libbld:$libbld/.libs:$libsrc:$py:$PYTHONPATH" +export PYTHONPATH + +# +# This is the simple part... +# Run everything that matches qa_*.py and return the final result. +# + +ok=yes +for file in @srcdir@/qa_*.py +do + if ! $file + then + ok=no + fi +done + +if [ $ok = yes ] +then + exit 0 +else + exit 1 +fi diff --git a/gr-usrp/src/tx_debug_gui.py b/gr-usrp/src/tx_debug_gui.py new file mode 100755 index 000000000..6988f986e --- /dev/null +++ b/gr-usrp/src/tx_debug_gui.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +import sys +import wx +from gnuradio.wxgui import form + +class tx_debug_gui(wx.Frame): + def __init__(self, tx_subdev, title="Tx Debug"): + wx.Frame.__init__(self, None, -1, title) + + self.subdev = tx_subdev + self.subdev._u.set_verbose(True) + + self.CreateStatusBar (1) + + self.panel = wx.Panel(self, -1) + self.vbox = wx.BoxSizer(wx.VERTICAL) + self.panel.SetSizer(self.vbox) + self.panel.SetAutoLayout(True) + + self._create_form() + + self.vbox.Fit(self.panel) + + self.frame_vbox = wx.BoxSizer(wx.VERTICAL) + self.frame_vbox.Add(self.panel, 1, wx.EXPAND) + self.SetSizer(self.frame_vbox) + self.SetAutoLayout(True) + self.frame_vbox.Fit(self) + + # ---------------------------------------------------------------- + + def _write_9862(self, regno, v): + return self.subdev._u._write_9862(self.subdev._which, regno, v) + + def _set_dac_offset(self, i_or_q, offset, offset_pin): + return self.subdev._u.set_dac_offset(self.subdev._which * 2 + i_or_q, offset, offset_pin) + + def _set_dac_fine_gain(self, i_or_q, gain, coarse): + return self._write_9862(14 + i_or_q, (coarse & 0xC0) | (gain & 0x3f)) + + def _create_form(self): + self._create_dac_offset() + self._create_dac_fine_gain() + self._create_pga() + + # ---------------------------------------------------------------- + + def _create_dac_offset(self): + + sbs = wx.StaticBoxSizer(wx.StaticBox(self.panel), wx.VERTICAL) + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add(wx.StaticText(self.panel, -1, "DAC Offset"), 5, 0) + sbs.Add(hbox, 0, 1) + + + self._create_dac_offset_helper(sbs, 0) + self._create_dac_offset_helper(sbs, 1) + + self.vbox.Add(sbs, 0, wx.EXPAND) + + def _create_dac_offset_helper(self, vbox, i_or_q): + + def doit(kv): + drive_positive = kv['drive_positive'] + dac_offset = kv['dac_offset'] + print "drive_positive =", drive_positive + print "dac_offset[%d] = %4d" % (i_or_q, dac_offset) + + # FIXME signed magnitude?? + # dac_offset = signed_mag10(dac_offset) + return self._set_dac_offset(i_or_q, dac_offset, int(drive_positive)) + + def signed_mag10(x): + # not clear from doc if this is really 2's comp or 10-bit signed magnitude + # we'll guess it's 10-bit signed mag + if x < 0: + return (1 << 9) | min(511, max(0, abs(x))) + else: + return (0 << 9) | min(511, max(0, abs(x))) + + myform = form.form() + hbox = wx.BoxSizer(wx.HORIZONTAL) + vbox.Add(hbox, 0, wx.EXPAND) + myform['drive_positive'] = form.checkbox_field(parent=self.panel, sizer=hbox, + callback=myform.check_input_and_call(doit), + weight=0, + label="drive +ve") + myform['dac_offset'] = form.slider_field(parent=self.panel, sizer=hbox, + callback=myform.check_input_and_call(doit), + min=-512, max=511, value=0, + weight=5) + + # ---------------------------------------------------------------- + + def _create_dac_fine_gain(self): + sbs = wx.StaticBoxSizer(wx.StaticBox(self.panel), wx.VERTICAL) + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add(wx.StaticText(self.panel, -1, "DAC Gain"), 5, 0) + sbs.Add(hbox, 0, 1) + + self._create_dac_gain_helper(sbs, 0) + self._create_dac_gain_helper(sbs, 1) + + self.vbox.Add(sbs, 0, wx.EXPAND) + + def _create_dac_gain_helper(self, vbox, i_or_q): + + d = { "1/1" : 0xC0, + "1/2" : 0x40, + "1/11" : 0x00 } + + def doit(kv): + dac_gain = kv['dac_gain'] + coarse_s = kv['coarse'] + print "dac_gain[%d] = %4d" % (i_or_q, dac_gain) + print "coarse = ", coarse_s + return self._set_dac_fine_gain(i_or_q, dac_gain, d[coarse_s]) + + myform = form.form() + hbox = wx.BoxSizer(wx.HORIZONTAL) + vbox.Add(hbox, 0, wx.EXPAND) + myform['coarse'] = form.radiobox_field(parent=self.panel, sizer=hbox, + callback=myform.check_input_and_call(doit), + choices=['1/11', '1/2', '1/1'], + weight=1, value='1/1') + myform['dac_gain'] = form.slider_field(parent=self.panel, sizer=hbox, + callback=myform.check_input_and_call(doit), + min=-32, max=31, value=0, + weight=4) + + + # ---------------------------------------------------------------- + + def _create_pga(self): + sbs = wx.StaticBoxSizer(wx.StaticBox(self.panel), wx.VERTICAL) + + form.quantized_slider_field(parent=self.panel, sizer=sbs, label="PGA", + weight=3, range=self.subdev.gain_range(), + callback=self.subdev.set_gain) + + self.vbox.Add(sbs, 0, wx.EXPAND) + + + # ---------------------------------------------------------------- + + + def _set_status_msg(self, msg): + self.GetStatusBar().SetStatusText(msg, 0) + + +if False and __name__ == '__main__': + + class demo_app (wx.App): + def __init__ (self): + wx.App.__init__(self) + + def OnInit (self): + frame = tx_debug_gui(None, "Debug TX") + frame.Show(True) + self.SetTopWindow (frame) + return True + + app = demo_app() + app.MainLoop() diff --git a/gr-usrp/src/usrp.py b/gr-usrp/src/usrp.py new file mode 100644 index 000000000..6e19c1bb3 --- /dev/null +++ b/gr-usrp/src/usrp.py @@ -0,0 +1,474 @@ +# +# Copyright 2004,2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + + + +import usrp_prims +import usrp_dbid +from gnuradio import usrp1 # usrp Rev 1 and later +from gnuradio import gru +from usrp_fpga_regs import * + +FPGA_MODE_NORMAL = usrp1.FPGA_MODE_NORMAL +FPGA_MODE_LOOPBACK = usrp1.FPGA_MODE_LOOPBACK +FPGA_MODE_COUNTING = usrp1.FPGA_MODE_COUNTING + +SPI_FMT_xSB_MASK = usrp1.SPI_FMT_xSB_MASK +SPI_FMT_LSB = usrp1.SPI_FMT_LSB +SPI_FMT_MSB = usrp1.SPI_FMT_MSB +SPI_FMT_HDR_MASK = usrp1.SPI_FMT_HDR_MASK +SPI_FMT_HDR_0 = usrp1.SPI_FMT_HDR_0 +SPI_FMT_HDR_1 = usrp1.SPI_FMT_HDR_1 +SPI_FMT_HDR_2 = usrp1.SPI_FMT_HDR_2 + +SPI_ENABLE_FPGA = usrp1.SPI_ENABLE_FPGA +SPI_ENABLE_CODEC_A = usrp1.SPI_ENABLE_CODEC_A +SPI_ENABLE_CODEC_B = usrp1.SPI_ENABLE_CODEC_B +SPI_ENABLE_reserved = usrp1.SPI_ENABLE_reserved +SPI_ENABLE_TX_A = usrp1.SPI_ENABLE_TX_A +SPI_ENABLE_RX_A = usrp1.SPI_ENABLE_RX_A +SPI_ENABLE_TX_B = usrp1.SPI_ENABLE_TX_B +SPI_ENABLE_RX_B = usrp1.SPI_ENABLE_RX_B + + +# Import all the daughterboard classes we know about. +# This hooks them into the auto-instantiation framework. + +import db_instantiator + +import db_basic +import db_dbs_rx +import db_flexrf +import db_flexrf_mimo +import db_tv_rx + + +def _look_for_usrp(which): + """ + Try to open the specified usrp. + + @param which: int >= 0 specifying which USRP to open + @type which: int + + @return: Returns version number, or raises RuntimeError + @rtype: int + """ + d = usrp_prims.usrp_find_device(which) + if not d: + raise RuntimeError, "Unable to find USRP #%d" % (which,) + + return usrp_prims.usrp_hw_rev(d) + + +def _ensure_rev2(which): + v = _look_for_usrp(which) + if not v in (2, 4): + raise RuntimeError, "Sorry, unsupported USRP revision (rev=%d)" % (v,) + + +class tune_result(object): + """ + Container for intermediate tuning information. + """ + def __init__(self, baseband_freq, dxc_freq, residual_freq, inverted): + self.baseband_freq = baseband_freq + self.dxc_freq = dxc_freq + self.residual_freq = residual_freq + self.inverted = inverted + + +def tune(u, chan, subdev, target_freq): + """ + Set the center frequency we're interested in. + + @param u: instance of usrp.source_* or usrp.sink_* + @param chan: DDC/DUC channel + @type chan: int + @param subdev: daughterboard subdevice + @param target_freq: frequency in Hz + @returns False if failure else tune_result + + Tuning is a two step process. First we ask the front-end to + tune as close to the desired frequency as it can. Then we use + the result of that operation and our target_frequency to + determine the value for the digital down converter. + """ + + # Does this usrp instance do Tx or Rx? + rx_p = True + try: + u.rx_freq + except AttributeError: + rx_p = False + + ok, baseband_freq = subdev.set_freq(target_freq) + dxc_freq, inverted = calc_dxc_freq(target_freq, baseband_freq, u.converter_rate()) + + # If the spectrum is inverted, and the daughterboard doesn't do + # quadrature downconversion, we can fix the inversion by flipping the + # sign of the dxc_freq... (This only happens using the basic_rx board) + + if subdev.spectrum_inverted(): + inverted = not(inverted) + + if inverted and not(subdev.is_quadrature()): + dxc_freq = -dxc_freq + inverted = not(inverted) + + if rx_p: + ok = ok and u.set_rx_freq(chan, dxc_freq) + else: + dxc_freq = -dxc_freq + ok = ok and u.set_tx_freq(chan, dxc_freq) + + if not(ok): + return False + + # residual_freq is the offset left over because of dxc tuning step size + if rx_p: + residual_freq = dxc_freq - u.rx_freq(chan) + else: + # FIXME 50-50 chance this has the wrong sign... + residual_freq = dxc_freq - u.tx_freq(chan) + + return tune_result(baseband_freq, dxc_freq, residual_freq, inverted) + + +# ------------------------------------------------------------------------ +# Build subclasses of raw usrp1.* class that add the db attribute +# by automatically instantiating the appropriate daughterboard classes. +# [Also provides keyword args.] +# ------------------------------------------------------------------------ + +class usrp_common(object): + def __init__(self): + # read capability register + r = self._u._read_fpga_reg(FR_RB_CAPS) + if r < 0: + r += 2**32 + if r == 0xaa55ff77: # value of this reg prior to being defined as cap reg + r = ((2 << bmFR_RB_CAPS_NDUC_SHIFT) + | (2 << bmFR_RB_CAPS_NDDC_SHIFT) + | bmFR_RB_CAPS_RX_HAS_HALFBAND) + self._fpga_caps = r + + if False: + print "FR_RB_CAPS = %#08x" % (self._fpga_caps,) + print "has_rx_halfband =", self.has_rx_halfband() + print "nDDCs =", self.nddc() + print "has_tx_halfband =", self.has_tx_halfband() + print "nDUCs =", self.nduc() + + def __getattr__(self, name): + return getattr(self._u, name) + + def tune(self, chan, subdev, target_freq): + return tune(self, chan, subdev, target_freq) + + def has_rx_halfband(self): + return self._fpga_caps & bmFR_RB_CAPS_RX_HAS_HALFBAND != 0 + + def has_tx_halfband(self): + return self._fpga_caps & bmFR_RB_CAPS_TX_HAS_HALFBAND != 0 + + def nddc(self): + """ + Number of Digital Down Converters implemented in FPGA + """ + return (self._fpga_caps & bmFR_RB_CAPS_NDDC_MASK) >> bmFR_RB_CAPS_NDDC_SHIFT + + def nduc(self): + """ + Number of Digital Up Converters implemented in FPGA + """ + return (self._fpga_caps & bmFR_RB_CAPS_NDUC_MASK) >> bmFR_RB_CAPS_NDUC_SHIFT + + +class sink_c(usrp_common): + def __init__(self, which=0, interp_rate=128, nchan=1, mux=0x98, + fusb_block_size=0, fusb_nblocks=0, + fpga_filename="", firmware_filename=""): + _ensure_rev2(which) + self._u = usrp1.sink_c(which, interp_rate, nchan, mux, + fusb_block_size, fusb_nblocks, + fpga_filename, firmware_filename) + # Add the db attribute, which contains a 2-tuple of tuples of daughterboard classes + self.db = (db_instantiator.instantiate(self._u, 0), + db_instantiator.instantiate(self._u, 1)) + usrp_common.__init__(self) + + def __del__(self): + self.db = None # will fire d'board destructors + self._u = None # will fire usrp1.* destructor + + +class sink_s(usrp_common): + def __init__(self, which=0, interp_rate=128, nchan=1, mux=0x98, + fusb_block_size=0, fusb_nblocks=0, + fpga_filename="", firmware_filename=""): + _ensure_rev2(which) + self._u = usrp1.sink_s(which, interp_rate, nchan, mux, + fusb_block_size, fusb_nblocks, + fpga_filename, firmware_filename) + # Add the db attribute, which contains a 2-tuple of tuples of daughterboard classes + self.db = (db_instantiator.instantiate(self._u, 0), + db_instantiator.instantiate(self._u, 1)) + usrp_common.__init__(self) + + def __del__(self): + self.db = None # will fire d'board destructors + self._u = None # will fire usrp1.* destructor + + +class source_c(usrp_common): + def __init__(self, which=0, decim_rate=64, nchan=1, mux=0x32103210, mode=0, + fusb_block_size=0, fusb_nblocks=0, + fpga_filename="", firmware_filename=""): + _ensure_rev2(which) + self._u = usrp1.source_c(which, decim_rate, nchan, mux, mode, + fusb_block_size, fusb_nblocks, + fpga_filename, firmware_filename) + # Add the db attribute, which contains a 2-tuple of tuples of daughterboard classes + self.db = (db_instantiator.instantiate(self._u, 0), + db_instantiator.instantiate(self._u, 1)) + usrp_common.__init__(self) + + def __del__(self): + self.db = None # will fire d'board destructors + self._u = None # will fire usrp1.* destructor + + +class source_s(usrp_common): + def __init__(self, which=0, decim_rate=64, nchan=1, mux=0x32103210, mode=0, + fusb_block_size=0, fusb_nblocks=0, + fpga_filename="", firmware_filename=""): + _ensure_rev2(which) + self._u = usrp1.source_s(which, decim_rate, nchan, mux, mode, + fusb_block_size, fusb_nblocks, + fpga_filename, firmware_filename) + # Add the db attribute, which contains a 2-tuple of tuples of daughterboard classes + self.db = (db_instantiator.instantiate(self._u, 0), + db_instantiator.instantiate(self._u, 1)) + usrp_common.__init__(self) + + def __del__(self): + self.db = None # will fire d'board destructors + self._u = None # will fire usrp1.* destructor + + +# ------------------------------------------------------------------------ +# utilities +# ------------------------------------------------------------------------ + +def determine_rx_mux_value(u, subdev_spec): + """ + Determine appropriate Rx mux value as a function of the subdevice choosen and the + characteristics of the respective daughterboard. + + @param u: instance of USRP source + @param subdev_spec: return value from subdev option parser. + @type subdev_spec: (side, subdev), where side is 0 or 1 and subdev is 0 or 1 + @returns: the Rx mux value + """ + # Figure out which A/D's to connect to the DDC. + # + # Each daughterboard consists of 1 or 2 subdevices. (At this time, + # all but the Basic Rx have a single subdevice. The Basic Rx + # has two independent channels, treated as separate subdevices). + # subdevice 0 of a daughterboard may use 1 or 2 A/D's. We determine this + # by checking the is_quadrature() method. If subdevice 0 uses only a single + # A/D, it's possible that the daughterboard has a second subdevice, subdevice 1, + # and it uses the second A/D. + # + # If the card uses only a single A/D, we wire a zero into the DDC Q input. + # + # (side, 0) says connect only the A/D's used by subdevice 0 to the DDC. + # (side, 1) says connect only the A/D's used by subdevice 1 to the DDC. + # + + side = subdev_spec[0] # side A = 0, side B = 1 + + if not(side in (0, 1)): + raise ValueError, "Invalid subdev_spec: %r:" % (subdev_spec,) + + db = u.db[side] # This is a tuple of length 1 or 2 containing the subdevice + # classes for the selected side. + + # compute bitmasks of used A/D's + + if db[0].is_quadrature(): + subdev0_uses = 0x3 # uses A/D 0 and 1 + else: + subdev0_uses = 0x1 # uses A/D 0 only + + if len(db) > 1: + subdev1_uses = 0x2 # uses A/D 1 only + else: + subdev1_uses = 0x0 # uses no A/D (doesn't exist) + + if subdev_spec[1] == 0: + uses = subdev0_uses + elif subdev_spec[1] == 1: + uses = subdev1_uses + else: + raise ValueError, "Invalid subdev_spec: %r: " % (subdev_spec,) + + if uses == 0: + raise RuntimeError, "Daughterboard doesn't have a subdevice 1: %r: " % (subdev_spec,) + + swap_iq = db[0].i_and_q_swapped() + + truth_table = { + # (side, uses, swap_iq) : mux_val + (0, 0x1, False) : 0xf0f0f0f0, + (0, 0x2, False) : 0xf0f0f0f1, + (0, 0x3, False) : 0x00000010, + (0, 0x3, True) : 0x00000001, + (1, 0x1, False) : 0xf0f0f0f2, + (1, 0x2, False) : 0xf0f0f0f3, + (1, 0x3, False) : 0x00000032, + (1, 0x3, True) : 0x00000023 + } + + return gru.hexint(truth_table[(side, uses, swap_iq)]) + + +def determine_tx_mux_value(u, subdev_spec): + """ + Determine appropriate Tx mux value as a function of the subdevice choosen. + + @param u: instance of USRP source + @param subdev_spec: return value from subdev option parser. + @type subdev_spec: (side, subdev), where side is 0 or 1 and subdev is 0 + @returns: the Rx mux value + """ + # This is simpler than the rx case. Either you want to talk + # to side A or side B. If you want to talk to both sides at once, + # determine the value manually. + + side = subdev_spec[0] # side A = 0, side B = 1 + + if not(side in (0, 1)): + raise ValueError, "Invalid subdev_spec: %r:" % (subdev_spec,) + + return gru.hexint([0x0098, 0x9800][side]) + + +def selected_subdev(u, subdev_spec): + """ + Return the user specified daughterboard subdevice. + + @param u: an instance of usrp.source_* or usrp.sink_* + @param subdev_spec: return value from subdev option parser. + @type subdev_spec: (side, subdev), where side is 0 or 1 and subdev is 0 or 1 + @returns: an instance derived from db_base + """ + side, subdev = subdev_spec + return u.db[side][subdev] + + +def calc_dxc_freq(target_freq, baseband_freq, fs): + """ + Calculate the frequency to use for setting the digital up or down converter. + + @param target_freq: desired RF frequency (Hz) + @type target_freq: number + @param baseband_freq: the RF frequency that corresponds to DC in the IF. + @type baseband_freq: number + @param fs: converter sample rate + @type fs: number + + @returns: 2-tuple (ddc_freq, inverted) where ddc_freq is the value + for the ddc and inverted is True if we're operating in an inverted + Nyquist zone. + """ + + delta = target_freq - baseband_freq + + if delta >= 0: + while delta > fs: + delta -= fs + if delta <= fs/2: + return (-delta, False) # non-inverted region + else: + return (delta - fs, True) # inverted region + else: + while delta < -fs: + delta += fs + if delta >= -fs/2: + return (-delta, False) # non-inverted region + else: + return (delta + fs, True) # inverted region + + +# ------------------------------------------------------------------------ +# Utilities +# ------------------------------------------------------------------------ + +def pick_tx_subdevice(u): + """ + The user didn't specify a tx subdevice on the command line. + Try for one of these, in order: FLEX_400, FLEX_900, FLEX_1200, FLEX_2400, + BASIC_TX, whatever's on side A. + + @return a subdev_spec + """ + return pick_subdev(u, (usrp_dbid.FLEX_400_TX, + usrp_dbid.FLEX_900_TX, + usrp_dbid.FLEX_1200_TX, + usrp_dbid.FLEX_2400_TX, + usrp_dbid.BASIC_TX)) + +def pick_rx_subdevice(u): + """ + The user didn't specify an rx subdevice on the command line. + Try for one of these, in order: FLEX_400, FLEX_900, FLEX_1200, FLEX_2400, + TV_RX, DBS_RX, BASIC_RX, whatever's on side A. + + @return a subdev_spec + """ + return pick_subdev(u, (usrp_dbid.FLEX_400_RX, + usrp_dbid.FLEX_900_RX, + usrp_dbid.FLEX_1200_RX, + usrp_dbid.FLEX_2400_RX, + usrp_dbid.TV_RX, + usrp_dbid.TV_RX_REV_2, + usrp_dbid.DBS_RX, + usrp_dbid.DBS_RX_REV_2_1, + usrp_dbid.BASIC_RX)) + +def pick_subdev(u, candidates): + """ + @param u: usrp instance + @param candidates: list of dbids + @returns: subdev specification + """ + db0 = u.db[0][0].dbid() + db1 = u.db[1][0].dbid() + for c in candidates: + if c == db0: return (0, 0) + if c == db1: return (1, 0) + if db0 >= 0: + return (0, 0) + if db1 >= 0: + return (1, 0) + raise RuntimeError, "No suitable daughterboard found!" + diff --git a/gr-usrp/src/usrp1.i b/gr-usrp/src/usrp1.i new file mode 100644 index 000000000..b73abcd76 --- /dev/null +++ b/gr-usrp/src/usrp1.i @@ -0,0 +1,657 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +%feature("autodoc", "1"); // generate python docstrings + +%include "exception.i" +%import "gnuradio.i" // the common stuff + +%{ + +#include "gnuradio_swig_bug_workaround.h" // mandatory bug fix +#include "usrp1_sink_c.h" +#include "usrp1_sink_s.h" +#include "usrp1_source_c.h" +#include "usrp1_source_s.h" +#include <stdexcept> +#include <usrp_standard.h> +#include <usrp_spi_defs.h> +%} + +%include <usrp_spi_defs.h> + +%constant int FPGA_MODE_NORMAL = usrp_standard_rx::FPGA_MODE_NORMAL; +%constant int FPGA_MODE_LOOPBACK = usrp_standard_rx::FPGA_MODE_LOOPBACK; +%constant int FPGA_MODE_COUNTING = usrp_standard_rx::FPGA_MODE_COUNTING; + +// ================================================================ +// abstract classes +// ================================================================ + +class usrp1_sink_base : public gr_sync_block { +protected: + usrp1_sink_base (const std::string &name, + gr_io_signature_sptr input_signature, + int which_board, + unsigned int interp_rate, + int nchan, + int mux, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error); + + virtual void copy_to_usrp_buffer (gr_vector_const_void_star &input_items, + int input_index, + int input_items_available, + int &input_items_consumed, + void *usrp_buffer, + int usrp_buffer_length, + int &bytes_written) = 0; + public: + ~usrp1_sink_base (); + + /*! + * \brief Set interpolator rate. \p rate must be in [4, 1024] and a multiple of 4. + * + * The final complex sample rate across the USB is + * dac_freq () * nchannels () / interp_rate () + */ + bool set_interp_rate (unsigned int rate); + bool set_nchannels (int nchan); + bool set_mux (int mux); + + /*! + * \brief set the frequency of the digital up converter. + * + * \p channel must be 0 or 1. \p freq is the center frequency in Hz. + * It must be in the range [-44M, 44M]. The frequency specified is + * quantized. Use tx_freq to retrieve the actual value used. + */ + bool set_tx_freq (int channel, double freq); + + void set_verbose (bool verbose); + + // ACCESSORS + + long fpga_master_clock_freq() const; + long converter_rate() const; // D/A sample rate + long dac_rate() const; // alias + long dac_freq () const; // deprecated name. Use converter_rate() or dac_rate(). + + unsigned int interp_rate () const; + double tx_freq (int channel) const; + int nunderruns () const { return d_nunderruns; } + + /*! + * \brief Set Programmable Gain Amplifier (PGA) + * + * \param which which D/A [0,3] + * \param gain_in_db gain value (linear in dB) + * + * gain is rounded to closest setting supported by hardware. + * Note that DAC 0 and DAC 1 share a gain setting as do DAC 2 and DAC 3. + * Setting DAC 0 affects DAC 1 and vice versa. Same with DAC 2 and DAC 3. + * + * \returns true iff sucessful. + * + * \sa pga_min(), pga_max(), pga_db_per_step() + */ + bool set_pga (int which, double gain_in_db); + + /*! + * \brief Return programmable gain amplifier gain in dB. + * + * \param which which D/A [0,3] + */ + double pga (int which) const; + + /*! + * \brief Return minimum legal PGA gain in dB. + */ + double pga_min () const; + + /*! + * \brief Return maximum legal PGA gain in dB. + */ + double pga_max () const; + + /*! + * \brief Return hardware step size of PGA (linear in dB). + */ + double pga_db_per_step () const; + + /*! + * \brief Return daughterboard ID for given Tx daughterboard slot [0,1]. + * + * \return daughterboard id >= 0 if successful + * \return -1 if no daugherboard + * \return -2 if invalid EEPROM on daughterboard + */ + int daughterboard_id (int which_dboard) const; + + /*! + * \brief Set ADC offset correction + * \param which which ADC[0,3]: 0 = RX_A I, 1 = RX_A Q... + * \param offset 16-bit value to subtract from raw ADC input. + */ + bool set_adc_offset (int which, int offset); + + /*! + * \brief Set DAC offset correction + * \param which which DAC[0,3]: 0 = TX_A I, 1 = TX_A Q... + * \param offset 10-bit offset value (ambiguous format: See AD9862 datasheet). + * \param offset_pin 1-bit value. If 0 offset applied to -ve differential pin; + * If 1 offset applied to +ve differential pin. + */ + bool set_dac_offset (int which, int offset, int offset_pin); + + /*! + * \brief Control ADC input buffer + * \param which which ADC[0,3] + * \param bypass if non-zero, bypass input buffer and connect input + * directly to switched cap SHA input of RxPGA. + */ + bool set_adc_buffer_bypass (int which, bool bypass); + + /*! + * \brief return the usrp's serial number. + * + * \returns non-zero length string iff successful. + */ + std::string serial_number(); + + /*! + * \brief Write direction register (output enables) for pins that go to daughterboard. + * + * \param which_dboard [0,1] which d'board + * \param value value to write into register + * \param mask which bits of value to write into reg + * + * Each d'board has 16-bits of general purpose i/o. + * Setting the bit makes it an output from the FPGA to the d'board. + * + * This register is initialized based on a value stored in the + * d'board EEPROM. In general, you shouldn't be using this routine + * without a very good reason. Using this method incorrectly will + * kill your USRP motherboard and/or daughterboard. + */ + bool _write_oe (int which_dboard, int value, int mask); + + /*! + * \brief Write daughterboard i/o pin value + * + * \param which_dboard [0,1] which d'board + * \param value value to write into register + * \param mask which bits of value to write into reg + */ + bool write_io (int which_dboard, int value, int mask); + + /*! + * \brief Read daughterboard i/o pin value + * + * \param which_dboard [0,1] which d'board + * \returns register value if successful, else READ_FAILED + */ + int read_io (int which_dboard); + + bool write_aux_dac (int which_dboard, int which_dac, int value); + int read_aux_adc (int which_dboard, int which_adc); + bool write_eeprom (int i2c_addr, int eeprom_offset, const std::string buf); + std::string read_eeprom (int i2c_addr, int eeprom_offset, int len); + bool write_i2c (int i2c_addr, const std::string buf); + std::string read_i2c (int i2c_addr, int len); + + bool _write_fpga_reg (int regno, int value); //< 7-bit regno, 32-bit value + int _read_fpga_reg (int regno); + bool _write_9862 (int which_codec, int regno, unsigned char value); + int _read_9862 (int which_codec, int regno) const; + + /*! + * \brief Write data to SPI bus peripheral. + * + * \param optional_header 0,1 or 2 bytes to write before buf. + * \param enables bitmask of peripherals to write. See usrp_spi_defs.h + * \param format transaction format. See usrp_spi_defs.h SPI_FMT_* + * \param buf the data to write + * \returns true iff successful + * Writes are limited to a maximum of 64 bytes. + * + * If \p format specifies that optional_header bytes are present, they are + * written to the peripheral immediately prior to writing \p buf. + */ + bool _write_spi (int optional_header, int enables, int format, std::string buf); + + /* + * \brief Read data from SPI bus peripheral. + * + * \param optional_header 0,1 or 2 bytes to write before buf. + * \param enables bitmask of peripheral to read. See usrp_spi_defs.h + * \param format transaction format. See usrp_spi_defs.h SPI_FMT_* + * \param len number of bytes to read. Must be in [0,64]. + * \returns the data read if sucessful, else a zero length string. + * + * Reads are limited to a maximum of 64 bytes. + * + * If \p format specifies that optional_header bytes are present, they + * are written to the peripheral first. Then \p len bytes are read from + * the peripheral and returned. + */ + std::string _read_spi (int optional_header, int enables, int format, int len); +}; + +// ---------------------------------------------------------------- + +class usrp1_source_base : public gr_sync_block { + protected: + + usrp1_sink_base (const std::string &name, + gr_io_signature_sptr input_signature, + int which_board, + unsigned int interp_rate, + int nchan, + int mux, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error); + + virtual int ninput_bytes_reqd_for_noutput_items (int noutput_items) = 0; + + virtual void copy_from_usrp_buffer (gr_vector_void_star &output_items, + int output_index, + int output_items_available, + int &output_items_produced, + const void *usrp_buffer, + int usrp_buffer_length, + int &bytes_read) = 0; + public: + ~usrp1_source_base (); + + + /*! + * \brief Set decimator rate. \p rate must be EVEN and in [8, 256]. + * + * The final complex sample rate across the USB is + * adc_freq () / decim_rate () + */ + bool set_decim_rate (unsigned int rate); + bool set_nchannels (int nchan); + bool set_mux (int mux); + + /*! + * \brief set the center frequency of the digital down converter. + * + * \p channel must be 0. \p freq is the center frequency in Hz. + * It must be in the range [-FIXME, FIXME]. The frequency specified is + * quantized. Use rx_freq to retrieve the actual value used. + */ + bool set_rx_freq (int channel, double freq); + + /*! + * \brief set fpga special modes + */ + bool set_fpga_mode (int mode); + + /*! + * \brief Set the digital down converter phase register. + * + * \param channel which ddc channel [0, 3] + * \param phase 32-bit integer phase value. + */ + bool set_ddc_phase(int channel, int phase); + + + void set_verbose (bool verbose); + + // ACCESSORS + + long fpga_master_clock_freq() const; + long converter_rate() const; // A/D sample rate + long adc_rate() const; // alias + long adc_freq() const; // Deprecated name. Use converter_rate() or adc_rate(). + + unsigned int decim_rate () const; + double rx_freq (int channel) const; + int noverruns () const { return d_noverruns; } + + + // PGA stuff + /*! + * \brief Set Programmable Gain Amplifier (PGA) + * + * \param which which A/D [0,3] + * \param gain_in_db gain value (linear in dB) + * + * gain is rounded to closest setting supported by hardware. + * + * \returns true iff sucessful. + * + * \sa pga_min(), pga_max(), pga_db_per_step() + */ + bool set_pga (int which, double gain_in_db); + + /*! + * \brief Return programmable gain amplifier gain setting in dB. + * + * \param which which A/D [0,3] + */ + double pga (int which) const; + + /*! + * \brief Return minimum legal PGA setting in dB. + */ + double pga_min () const; + + /*! + * \brief Return maximum legal PGA setting in dB. + */ + double pga_max () const; + + /*! + * \brief Return hardware step size of PGA (linear in dB). + */ + double pga_db_per_step () const; + + /*! + * \brief Return daughterboard ID for given Rx daughterboard slot [0,1]. + * + * \return daughterboard id >= 0 if successful + * \return -1 if no daugherboard + * \return -2 if invalid EEPROM on daughterboard + */ + int daughterboard_id (int which_dboard) const; + + /*! + * \brief Set ADC offset correction + * \param which which ADC[0,3]: 0 = RX_A I, 1 = RX_A Q... + * \param offset 16-bit value to subtract from raw ADC input. + */ + bool set_adc_offset (int which, int offset); + + /*! + * \brief Set DAC offset correction + * \param which which DAC[0,3]: 0 = TX_A I, 1 = TX_A Q... + * \param offset 10-bit offset value (ambiguous format: See AD9862 datasheet). + * \param offset_pin 1-bit value. If 0 offset applied to -ve differential pin; + * If 1 offset applied to +ve differential pin. + */ + bool set_dac_offset (int which, int offset, int offset_pin); + + /*! + * \brief Control ADC input buffer + * \param which which ADC[0,3] + * \param bypass if non-zero, bypass input buffer and connect input + * directly to switched cap SHA input of RxPGA. + */ + bool set_adc_buffer_bypass (int which, bool bypass); + + /*! + * \brief return the usrp's serial number. + * + * \returns non-zero length string iff successful. + */ + std::string serial_number(); + + /*! + * \brief Write direction register (output enables) for pins that go to daughterboard. + * + * \param which_dboard [0,1] which d'board + * \param value value to write into register + * \param mask which bits of value to write into reg + * + * Each d'board has 16-bits of general purpose i/o. + * Setting the bit makes it an output from the FPGA to the d'board. + * + * This register is initialized based on a value stored in the + * d'board EEPROM. In general, you shouldn't be using this routine + * without a very good reason. Using this method incorrectly will + * kill your USRP motherboard and/or daughterboard. + */ + bool _write_oe (int which_dboard, int value, int mask); + + /*! + * \brief Write daughterboard i/o pin value + * + * \param which_dboard [0,1] which d'board + * \param value value to write into register + * \param mask which bits of value to write into reg + */ + bool write_io (int which_dboard, int value, int mask); + + /*! + * \brief Read daughterboard i/o pin value + * + * \param which_dboard [0,1] which d'board + * \returns register value if successful, else READ_FAILED + */ + int read_io (int which_dboard); + + /*! + * \brief Enable/disable automatic DC offset removal control loop in FPGA + * + * \param bits which control loops to enable + * \param mask which \p bits to pay attention to + * + * If the corresponding bit is set, enable the automatic DC + * offset correction control loop. + * + * <pre> + * The 4 low bits are significant: + * + * ADC0 = (1 << 0) + * ADC1 = (1 << 1) + * ADC2 = (1 << 2) + * ADC3 = (1 << 3) + * </pre> + * + * By default the control loop is enabled on all ADC's. + */ + bool set_dc_offset_cl_enable(int bits, int mask); + + /*! + * \brief Specify Rx data format. + * + * \param format format specifier + * + * Rx data format control register + * + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------------------------------------+-+-+---------+-------+ + * | Reserved (Must be zero) |B|Q| WIDTH | SHIFT | + * +-----------------------------------------+-+-+---------+-------+ + * + * SHIFT specifies arithmetic right shift [0, 15] + * WIDTH specifies bit-width of I & Q samples across the USB [1, 16] (not all valid) + * Q if set deliver both I & Q, else just I + * B if set bypass half-band filter. + * + * Right now the acceptable values are: + * + * B Q WIDTH SHIFT + * 0 1 16 0 + * 0 1 8 8 + * + * More valid combos to come. + * + * Default value is 0x00000300 16-bits, 0 shift, deliver both I & Q. + */ + bool set_format(unsigned int format); + + /*! + * \brief return current format + */ + unsigned int format () const; + + static unsigned int make_format(int width=16, int shift=0, + bool want_q=true, bool bypass_halfband=false); + static int format_width(unsigned int format); + static int format_shift(unsigned int format); + static bool format_want_q(unsigned int format); + static bool format_bypass_halfband(unsigned int format); + + + + + bool write_aux_dac (int which_dboard, int which_dac, int value); + int read_aux_adc (int which_dboard, int which_adc); + bool write_eeprom (int i2c_addr, int eeprom_offset, const std::string buf); + std::string read_eeprom (int i2c_addr, int eeprom_offset, int len); + bool write_i2c (int i2c_addr, const std::string buf); + std::string read_i2c (int i2c_addr, int len); + bool _write_fpga_reg (int regno, int value); //< 7-bit regno, 32-bit value + bool _write_fpga_reg_masked (int regno, int value, int mask); //< 7-bit regno, 16-bit value, 16-bit mask + int _read_fpga_reg (int regno); + bool _write_9862 (int which_codec, int regno, unsigned char value); + int _read_9862 (int which_codec, int regno) const; + + bool _write_spi (int optional_header, int enables, int format, std::string buf); + + /* + * \brief Read data from SPI bus peripheral. + * + * \param optional_header 0,1 or 2 bytes to write before buf. + * \param enables bitmask of peripheral to read. See usrp_spi_defs.h + * \param format transaction format. See usrp_spi_defs.h SPI_FMT_* + * \param len number of bytes to read. Must be in [0,64]. + * \returns the data read if sucessful, else a zero length string. + * + * Reads are limited to a maximum of 64 bytes. + * + * If \p format specifies that optional_header bytes are present, they + * are written to the peripheral first. Then \p len bytes are read from + * the peripheral and returned. + */ + std::string _read_spi (int optional_header, int enables, int format, int len); +}; + + +// ================================================================ +// concrete sinks +// ================================================================ + + +GR_SWIG_BLOCK_MAGIC(usrp1,sink_c) + +usrp1_sink_c_sptr +usrp1_make_sink_c (int which_board, + unsigned int interp_rate, + int nchan, + int mux, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error); + + +class usrp1_sink_c : public usrp1_sink_base { + protected: + usrp1_sink_c (int which_board, unsigned int interp_rate, + int nchan, int mux); + + public: + ~usrp1_sink_c (); +}; + +// ---------------------------------------------------------------- + +GR_SWIG_BLOCK_MAGIC(usrp1,sink_s) + +usrp1_sink_s_sptr +usrp1_make_sink_s (int which_board, + unsigned int interp_rate, + int nchan, + int mux, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error); + + +class usrp1_sink_s : public usrp1_sink_base { + protected: + usrp1_sink_s (int which_board, unsigned int interp_rate, + int nchan, int mux); + + public: + ~usrp1_sink_s (); +}; + +// ================================================================ +// concrete sources +// ================================================================ + +GR_SWIG_BLOCK_MAGIC(usrp1,source_c) + + +usrp1_source_c_sptr +usrp1_make_source_c (int which_board, + unsigned int decim_rate, + int nchan, + int mux, + int mode, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error); + +class usrp1_source_c : public usrp1_source_base { + protected: + usrp1_source_c (int which_board, unsigned int decim_rate, + int nchan, int mux, int mode); + + public: + ~usrp1_source_c (); +}; + +// ---------------------------------------------------------------- + +GR_SWIG_BLOCK_MAGIC(usrp1,source_s) + +usrp1_source_s_sptr +usrp1_make_source_s (int which_board, + unsigned int decim_rate, + int nchan, + int mux, + int mode, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error); + + +class usrp1_source_s : public usrp1_source_base { + protected: + usrp1_source_s (int which_board, unsigned int decim_rate, + int nchan, int mux, int mode); + + public: + ~usrp1_source_s (); +}; + diff --git a/gr-usrp/src/usrp1_sink_base.cc b/gr-usrp/src/usrp1_sink_base.cc new file mode 100644 index 000000000..19e0b23f2 --- /dev/null +++ b/gr-usrp/src/usrp1_sink_base.cc @@ -0,0 +1,359 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <usrp1_sink_base.h> +#include <gr_io_signature.h> +#include <usrp_standard.h> +#include <assert.h> + +static const int OUTPUT_MULTIPLE_SAMPLES = 128; // DON'T CHANGE THIS VALUE! + +usrp1_sink_base::usrp1_sink_base (const std::string &name, + gr_io_signature_sptr input_signature, + int which_board, + unsigned int interp_rate, + int nchan, + int mux, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error) + : gr_sync_block (name, + input_signature, + gr_make_io_signature (0, 0, 0)), + d_nunderruns (0) +{ + d_usrp = usrp_standard_tx::make (which_board, + interp_rate, + nchan, mux, + fusb_block_size, + fusb_nblocks, + fpga_filename, + firmware_filename + ); + if (d_usrp == 0) + throw std::runtime_error ("can't open usrp1"); + + // All calls to d_usrp->write must be multiples of 512 bytes. + + set_output_multiple (OUTPUT_MULTIPLE_SAMPLES); +} + +usrp1_sink_base::~usrp1_sink_base () +{ + delete d_usrp; +} + +bool +usrp1_sink_base::start() +{ + return d_usrp->start(); +} + +bool +usrp1_sink_base::stop() +{ + return d_usrp->stop(); +} + +int +usrp1_sink_base::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + static const int BUFSIZE = 16 * (1L << 10); // 16kB + unsigned char outbuf[BUFSIZE]; + int obi = 0; + int input_index = 0; + int input_items_consumed; + int bytes_written; + bool underrun; + + + while (input_index < noutput_items){ + + copy_to_usrp_buffer (input_items, + input_index, + noutput_items - input_index, // input_items_available + input_items_consumed, // [out] + &outbuf[obi], // [out] usrp_buffer + BUFSIZE - obi, // usrp_buffer_length + bytes_written); // [out] + + assert (input_index + input_items_consumed <= noutput_items); + assert (obi + bytes_written <= BUFSIZE); + + input_index += input_items_consumed; + obi += bytes_written; + + if (obi >= BUFSIZE){ // flush + if (d_usrp->write (outbuf, obi, &underrun) != obi) + return -1; // indicate we're done + + if (underrun){ + d_nunderruns++; + // fprintf (stderr, "usrp1_sink: underrun\n"); + fputs ("uU", stderr); + } + obi = 0; + } + } + + if (obi != 0){ + assert (obi % 512 == 0); + if (d_usrp->write (outbuf, obi, &underrun) != obi) + return -1; // indicate we're done + + if (underrun){ + d_nunderruns++; + // fprintf (stderr, "usrp1_sink: underrun\n"); + fputs ("uU", stderr); + } + } + + return noutput_items; +} + +bool +usrp1_sink_base::set_interp_rate (unsigned int rate) +{ + return d_usrp->set_interp_rate (rate); +} + +bool +usrp1_sink_base::set_nchannels (int nchan) +{ + return d_usrp->set_nchannels (nchan); +} + +bool +usrp1_sink_base::set_mux (int mux) +{ + return d_usrp->set_mux (mux); +} + +bool +usrp1_sink_base::set_tx_freq (int channel, double freq) +{ + return d_usrp->set_tx_freq (channel, freq); +} + +long +usrp1_sink_base::fpga_master_clock_freq() const +{ + return d_usrp->fpga_master_clock_freq(); +} + +long +usrp1_sink_base::converter_rate () const +{ + return d_usrp->converter_rate (); +} + +unsigned int +usrp1_sink_base::interp_rate () const +{ + return d_usrp->interp_rate (); +} + +int +usrp1_sink_base::nchannels () const +{ + return d_usrp->nchannels (); +} + +int +usrp1_sink_base::mux () const +{ + return d_usrp->mux (); +} + + +double +usrp1_sink_base::tx_freq (int channel) const +{ + return d_usrp->tx_freq (channel); +} + +void +usrp1_sink_base::set_verbose (bool verbose) +{ + d_usrp->set_verbose (verbose); +} + +bool +usrp1_sink_base::write_aux_dac (int which_dboard, int which_dac, int value) +{ + return d_usrp->write_aux_dac (which_dboard, which_dac, value); +} + +int +usrp1_sink_base::read_aux_adc (int which_dboard, int which_adc) +{ + return d_usrp->read_aux_adc (which_dboard, which_adc); +} + +bool +usrp1_sink_base::write_eeprom (int i2c_addr, int eeprom_offset, const std::string buf) +{ + return d_usrp->write_eeprom (i2c_addr, eeprom_offset, buf); +} + +std::string +usrp1_sink_base::read_eeprom (int i2c_addr, int eeprom_offset, int len) +{ + return d_usrp->read_eeprom (i2c_addr, eeprom_offset, len); +} + +bool +usrp1_sink_base::write_i2c (int i2c_addr, const std::string buf) +{ + return d_usrp->write_i2c (i2c_addr, buf); +} + +std::string +usrp1_sink_base::read_i2c (int i2c_addr, int len) +{ + return d_usrp->read_i2c (i2c_addr, len); +} + +bool +usrp1_sink_base::set_pga (int which, double gain) +{ + return d_usrp->set_pga (which, gain); +} + +double +usrp1_sink_base::pga (int which) const +{ + return d_usrp->pga (which); +} + +double +usrp1_sink_base::pga_min () const +{ + return d_usrp->pga_min (); +} + +double +usrp1_sink_base::pga_max () const +{ + return d_usrp->pga_max (); +} + +double +usrp1_sink_base::pga_db_per_step () const +{ + return d_usrp->pga_db_per_step (); +} + +int +usrp1_sink_base::daughterboard_id (int which) const +{ + return d_usrp->daughterboard_id (which); +} + +bool +usrp1_sink_base::set_adc_offset (int which, int offset) +{ + return d_usrp->set_adc_offset (which, offset); +} + +bool +usrp1_sink_base::set_dac_offset (int which, int offset, int offset_pin) +{ + return d_usrp->set_dac_offset (which, offset, offset_pin); +} + +bool +usrp1_sink_base::set_adc_buffer_bypass (int which, bool bypass) +{ + return d_usrp->set_adc_buffer_bypass (which, bypass); +} + +std::string +usrp1_sink_base::serial_number() +{ + return d_usrp->serial_number(); +} + +bool +usrp1_sink_base::_write_oe (int which_dboard, int value, int mask) +{ + return d_usrp->_write_oe (which_dboard, value, mask); +} + +bool +usrp1_sink_base::write_io (int which_dboard, int value, int mask) +{ + return d_usrp->write_io (which_dboard, value, mask); +} + +int +usrp1_sink_base::read_io (int which_dboard) +{ + return d_usrp->read_io (which_dboard); +} + +// internal routines... + +bool +usrp1_sink_base::_write_fpga_reg (int regno, int value) +{ + return d_usrp->_write_fpga_reg (regno, value); +} + +int +usrp1_sink_base::_read_fpga_reg (int regno) +{ + return d_usrp->_read_fpga_reg (regno); +} + +bool +usrp1_sink_base::_write_9862 (int which_codec, int regno, unsigned char value) +{ + return d_usrp->_write_9862 (which_codec, regno, value); +} + +int +usrp1_sink_base::_read_9862 (int which_codec, int regno) const +{ + return d_usrp->_read_9862 (which_codec, regno); +} + +bool +usrp1_sink_base::_write_spi (int optional_header, int enables, + int format, std::string buf) +{ + return d_usrp->_write_spi (optional_header, enables, format, buf); +} + +std::string +usrp1_sink_base::_read_spi (int optional_header, int enables, int format, int len) +{ + return d_usrp->_read_spi (optional_header, enables, format, len); +} diff --git a/gr-usrp/src/usrp1_sink_base.h b/gr-usrp/src/usrp1_sink_base.h new file mode 100644 index 000000000..d5775377b --- /dev/null +++ b/gr-usrp/src/usrp1_sink_base.h @@ -0,0 +1,359 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2006 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef INCLUDED_USRP1_SINK_BASE_H +#define INCLUDED_USRP1_SINK_BASE_H + +#include <gr_sync_block.h> +#include <stdexcept> + +class usrp_standard_tx; + + +/*! + * \brief abstract interface to Universal Software Radio Peripheral Tx path (Rev 1) + */ +class usrp1_sink_base : public gr_sync_block { + private: + usrp_standard_tx *d_usrp; + int d_nunderruns; + + protected: + usrp1_sink_base (const std::string &name, + gr_io_signature_sptr input_signature, + int which_board, + unsigned int interp_rate, + int nchan, + int mux, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error); + + /*! + * \brief convert between input item format and usrp native format + * + * \param input_items[in] stream(s) of input items + * \param input_index[in] starting index in input_items + * \param input_items_available[in] number of items available starting at item[index] + * \param input_items_consumed[out] number of input items consumed by copy + * \param usrp_buffer[out] destination buffer + * \param usrp_buffer_length[in] \p usrp_buffer length in bytes + * \param bytes_written[out] number of bytes written into \p usrp_buffer + */ + virtual void copy_to_usrp_buffer (gr_vector_const_void_star &input_items, + int input_index, + int input_items_available, + int &input_items_consumed, + void *usrp_buffer, + int usrp_buffer_length, + int &bytes_written) = 0; + + public: + //! magic value used on alternate register read interfaces + static const int READ_FAILED = -99999; + + + ~usrp1_sink_base (); + + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + bool start(); + bool stop(); + + /*! + * \brief Set interpolator rate. \p rate must be in [4, 1024] and a multiple of 4. + * + * The final complex sample rate across the USB is + * dac_freq () / interp_rate () * nchannels () + */ + bool set_interp_rate (unsigned int rate); + bool set_nchannels (int nchan); + bool set_mux (int mux); + + /*! + * \brief set the frequency of the digital up converter. + * + * \p channel must be 0. \p freq is the center frequency in Hz. + * It must be in the range [-44M, 44M]. The frequency specified is + * quantized. Use tx_freq to retrieve the actual value used. + */ + bool set_tx_freq (int channel, double freq); + + void set_verbose (bool verbose); + + /*! + * \brief Set Programmable Gain Amplifier (PGA) + * + * \param which which D/A [0,3] + * \param gain_in_db gain value (linear in dB) + * + * gain is rounded to closest setting supported by hardware. + * Note that DAC 0 and DAC 1 share a gain setting as do DAC 2 and DAC 3. + * Setting DAC 0 affects DAC 1 and vice versa. Same with DAC 2 and DAC 3. + * + * \returns true iff sucessful. + * + * \sa pga_min(), pga_max(), pga_db_per_step() + */ + bool set_pga (int which, double gain_in_db); + + /*! + * \brief Return programmable gain amplifier gain in dB. + * + * \param which which D/A [0,3] + */ + double pga (int which) const; + + /*! + * \brief Return minimum legal PGA gain in dB. + */ + double pga_min () const; + + /*! + * \brief Return maximum legal PGA gain in dB. + */ + double pga_max () const; + + /*! + * \brief Return hardware step size of PGA (linear in dB). + */ + double pga_db_per_step () const; + + + // ACCESSORS + + long fpga_master_clock_freq() const; + long converter_rate() const; + long dac_rate() const { return converter_rate(); } // alias + long dac_freq() const { return converter_rate(); } // deprecated alias + + unsigned int interp_rate () const; + int nchannels () const; + int mux () const; + double tx_freq (int channel) const; + int nunderruns () const { return d_nunderruns; } + + /*! + * \brief Return daughterboard ID for given Rx daughterboard slot [0,1]. + * + * \return daughterboard id >= 0 if successful + * \return -1 if no daugherboard + * \return -2 if invalid EEPROM on daughterboard + */ + int daughterboard_id (int which_dboard) const; + + /*! + * \brief Write auxiliary digital to analog converter. + * + * \param which_dboard [0,1] which d'board + * N.B., SLOT_TX_A and SLOT_RX_A share the same AUX DAC's. + * SLOT_TX_B and SLOT_RX_B share the same AUX DAC's. + * \param which_dac [2,3] TX slots must use only 2 and 3. + * \param value [0,4095] + * \returns true iff successful + */ + bool write_aux_dac (int which_board, int which_dac, int value); + + /*! + * \brief Read auxiliary analog to digital converter. + * + * \param which_dboard [0,1] which d'board + * \param which_adc [0,1] + * \returns value in the range [0,4095] if successful, else READ_FAILED. + */ + int read_aux_adc (int which_dboard, int which_adc); + + /*! + * \brief Write EEPROM on motherboard or any daughterboard. + * \param i2c_addr I2C bus address of EEPROM + * \param eeprom_offset byte offset in EEPROM to begin writing + * \param buf the data to write + * \returns true iff sucessful + */ + bool write_eeprom (int i2c_addr, int eeprom_offset, const std::string buf); + + /*! + * \brief Write EEPROM on motherboard or any daughterboard. + * \param i2c_addr I2C bus address of EEPROM + * \param eeprom_offset byte offset in EEPROM to begin reading + * \param len number of bytes to read + * \returns the data read if successful, else a zero length string. + */ + std::string read_eeprom (int i2c_addr, int eeprom_offset, int len); + + /*! + * \brief Write to I2C peripheral + * \param i2c_addr I2C bus address (7-bits) + * \param buf the data to write + * \returns true iff successful + * Writes are limited to a maximum of of 64 bytes. + */ + bool write_i2c (int i2c_addr, const std::string buf); + + /*! + * \brief Read from I2C peripheral + * \param i2c_addr I2C bus address (7-bits) + * \param len number of bytes to read + * \returns the data read if successful, else a zero length string. + * Reads are limited to a maximum of of 64 bytes. + */ + std::string read_i2c (int i2c_addr, int len); + + /*! + * \brief Set ADC offset correction + * \param which which ADC[0,3]: 0 = RX_A I, 1 = RX_A Q... + * \param offset 16-bit value to subtract from raw ADC input. + */ + bool set_adc_offset (int which, int offset); + + /*! + * \brief Set DAC offset correction + * \param which which DAC[0,3]: 0 = TX_A I, 1 = TX_A Q... + * \param offset 10-bit offset value (ambiguous format: See AD9862 datasheet). + * \param offset_pin 1-bit value. If 0 offset applied to -ve differential pin; + * If 1 offset applied to +ve differential pin. + */ + bool set_dac_offset (int which, int offset, int offset_pin); + + /*! + * \brief Control ADC input buffer + * \param which which ADC[0,3] + * \param bypass if non-zero, bypass input buffer and connect input + * directly to switched cap SHA input of RxPGA. + */ + bool set_adc_buffer_bypass (int which, bool bypass); + + /*! + * \brief return the usrp's serial number. + * + * \returns non-zero length string iff successful. + */ + std::string serial_number(); + + /*! + * \brief Write direction register (output enables) for pins that go to daughterboard. + * + * \param which_dboard [0,1] which d'board + * \param value value to write into register + * \param mask which bits of value to write into reg + * + * Each d'board has 16-bits of general purpose i/o. + * Setting the bit makes it an output from the FPGA to the d'board. + * + * This register is initialized based on a value stored in the + * d'board EEPROM. In general, you shouldn't be using this routine + * without a very good reason. Using this method incorrectly will + * kill your USRP motherboard and/or daughterboard. + */ + bool _write_oe (int which_dboard, int value, int mask); + + /*! + * \brief Write daughterboard i/o pin value + * + * \param which_dboard [0,1] which d'board + * \param value value to write into register + * \param mask which bits of value to write into reg + */ + bool write_io (int which_dboard, int value, int mask); + + /*! + * \brief Read daughterboard i/o pin value + * + * \param which_dboard [0,1] which d'board + * \returns register value if successful, else READ_FAILED + */ + int read_io (int which_dboard); + + // + // internal routines... + // You probably shouldn't be using these... + // + /*! + * \brief Write FPGA register. + * \param regno 7-bit register number + * \param value 32-bit value + * \returns true iff successful + */ + bool _write_fpga_reg (int regno, int value); //< 7-bit regno, 32-bit value + + /*! + * \brief Read FPGA register. + * \param regno 7-bit register number + * \returns register value if successful, else READ_FAILED + */ + int _read_fpga_reg (int regno); + + /*! + * \brief Write AD9862 register. + * \param which_codec 0 or 1 + * \param regno 6-bit register number + * \param value 8-bit value + * \returns true iff successful + */ + bool _write_9862 (int which_codec, int regno, unsigned char value); + + /*! + * \brief Read AD9862 register. + * \param which_codec 0 or 1 + * \param regno 6-bit register number + * \returns register value if successful, else READ_FAILED + */ + int _read_9862 (int which_codec, int regno) const; + + /*! + * \brief Write data to SPI bus peripheral. + * + * \param optional_header 0,1 or 2 bytes to write before buf. + * \param enables bitmask of peripherals to write. See usrp_spi_defs.h + * \param format transaction format. See usrp_spi_defs.h SPI_FMT_* + * \param buf the data to write + * \returns true iff successful + * Writes are limited to a maximum of 64 bytes. + * + * If \p format specifies that optional_header bytes are present, they are + * written to the peripheral immediately prior to writing \p buf. + */ + bool _write_spi (int optional_header, int enables, int format, std::string buf); + + /* + * \brief Read data from SPI bus peripheral. + * + * \param optional_header 0,1 or 2 bytes to write before buf. + * \param enables bitmask of peripheral to read. See usrp_spi_defs.h + * \param format transaction format. See usrp_spi_defs.h SPI_FMT_* + * \param len number of bytes to read. Must be in [0,64]. + * \returns the data read if sucessful, else a zero length string. + * + * Reads are limited to a maximum of 64 bytes. + * + * If \p format specifies that optional_header bytes are present, they + * are written to the peripheral first. Then \p len bytes are read from + * the peripheral and returned. + */ + std::string _read_spi (int optional_header, int enables, int format, int len); +}; + +#endif /* INCLUDED_USRP1_SINK_BASE_H */ diff --git a/gr-usrp/src/usrp1_sink_c.cc b/gr-usrp/src/usrp1_sink_c.cc new file mode 100644 index 000000000..16296cfdf --- /dev/null +++ b/gr-usrp/src/usrp1_sink_c.cc @@ -0,0 +1,106 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2006 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <usrp1_sink_c.h> +#include <gr_io_signature.h> +#include <usrp_standard.h> +#include <usrp_bytesex.h> + +usrp1_sink_c_sptr +usrp1_make_sink_c (int which_board, + unsigned int interp_rate, + int nchan, + int mux, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error) +{ + return usrp1_sink_c_sptr (new usrp1_sink_c (which_board, + interp_rate, + nchan, + mux, + fusb_block_size, + fusb_nblocks, + fpga_filename, + firmware_filename + )); +} + + +usrp1_sink_c::usrp1_sink_c (int which_board, + unsigned int interp_rate, + int nchan, + int mux, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error) + : usrp1_sink_base ("usrp1_sink_c", + gr_make_io_signature (1, 1, sizeof (gr_complex)), + which_board, interp_rate, nchan, mux, + fusb_block_size, fusb_nblocks, + fpga_filename, firmware_filename) +{ +} + +usrp1_sink_c::~usrp1_sink_c () +{ + // NOP +} + +/* + * Take one complex input stream and format it into interleaved short I & Q + * for the usrp. + */ +void +usrp1_sink_c::copy_to_usrp_buffer (gr_vector_const_void_star &input_items, + int input_index, + int input_items_available, + int &input_items_consumed, // out + void *usrp_buffer, + int usrp_buffer_length, + int &bytes_written) // out +{ + gr_complex *in = &((gr_complex *) input_items[0])[input_index]; + short *dst = (short *) usrp_buffer; + + static const int usrp_bytes_per_input_item = 2 * sizeof (short); // I & Q + + int nitems = std::min (input_items_available, + usrp_buffer_length / usrp_bytes_per_input_item); + + for (int i = 0; i < nitems; i++){ + dst[2*i + 0] = host_to_usrp_short((short) real(in[i])); // FIXME saturate? + dst[2*i + 1] = host_to_usrp_short((short) imag(in[i])); // FIXME saturate? + } + + input_items_consumed = nitems; + bytes_written = nitems * usrp_bytes_per_input_item; +} + diff --git a/gr-usrp/src/usrp1_sink_c.h b/gr-usrp/src/usrp1_sink_c.h new file mode 100644 index 000000000..15d92a9cc --- /dev/null +++ b/gr-usrp/src/usrp1_sink_c.h @@ -0,0 +1,87 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef INCLUDED_USRP1_SINK_C_H +#define INCLUDED_USRP1_SINK_C_H + +#include <usrp1_sink_base.h> + +class usrp1_sink_c; +typedef boost::shared_ptr<usrp1_sink_c> usrp1_sink_c_sptr; + + +// public shared_ptr constructor + +usrp1_sink_c_sptr +usrp1_make_sink_c (int which_board, + unsigned int interp_rate, + int nchan, + int mux, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error); + + +/*! + * \brief interface to Universal Software Radio Peripheral Tx path (Rev 1) + * + * input: gr_complex + */ +class usrp1_sink_c : public usrp1_sink_base { + private: + + friend usrp1_sink_c_sptr + usrp1_make_sink_c (int which_board, + unsigned int interp_rate, + int nchan, + int mux, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error); + + protected: + usrp1_sink_c (int which_board, + unsigned int interp_rate, + int nchan, + int mux, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error); + + virtual void copy_to_usrp_buffer (gr_vector_const_void_star &input_items, + int input_index, + int input_items_available, + int &input_items_consumed, + void *usrp_buffer, + int usrp_buffer_length, + int &bytes_written); + public: + ~usrp1_sink_c (); +}; + +#endif /* INCLUDED_USRP1_SINK_C_H */ diff --git a/gr-usrp/src/usrp1_sink_s.cc b/gr-usrp/src/usrp1_sink_s.cc new file mode 100644 index 000000000..634eb5aea --- /dev/null +++ b/gr-usrp/src/usrp1_sink_s.cc @@ -0,0 +1,106 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2006 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <usrp1_sink_s.h> +#include <gr_io_signature.h> +#include <usrp_standard.h> +#include <usrp_bytesex.h> + +usrp1_sink_s_sptr +usrp1_make_sink_s (int which_board, + unsigned int interp_rate, + int nchan, + int mux, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error) +{ + return usrp1_sink_s_sptr (new usrp1_sink_s (which_board, + interp_rate, + nchan, + mux, + fusb_block_size, + fusb_nblocks, + fpga_filename, + firmware_filename + )); +} + + +usrp1_sink_s::usrp1_sink_s (int which_board, + unsigned int interp_rate, + int nchan, + int mux, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error) + : usrp1_sink_base ("usrp1_sink_s", + gr_make_io_signature (1, 1, sizeof (short)), + which_board, interp_rate, nchan, mux, + fusb_block_size, fusb_nblocks, + fpga_filename, firmware_filename) +{ + set_output_multiple (512 / sizeof(short)); // don't change +} + +usrp1_sink_s::~usrp1_sink_s () +{ + // NOP +} + +/* + * Take one short input stream and format it into interleaved short I & Q + * for the usrp. + */ +void +usrp1_sink_s::copy_to_usrp_buffer (gr_vector_const_void_star &input_items, + int input_index, + int input_items_available, + int &input_items_consumed, // out + void *usrp_buffer, + int usrp_buffer_length, + int &bytes_written) // out +{ + short *in = &((short *) input_items[0])[input_index]; + short *dst = (short *) usrp_buffer; + + static const int usrp_bytes_per_input_item = sizeof (short); + + int nitems = std::min (input_items_available, + usrp_buffer_length / usrp_bytes_per_input_item); + + for (int i = 0; i < nitems; i++){ // FIXME unroll + dst[i] = host_to_usrp_short(in[i]); + } + + input_items_consumed = nitems; + bytes_written = nitems * usrp_bytes_per_input_item; +} + diff --git a/gr-usrp/src/usrp1_sink_s.h b/gr-usrp/src/usrp1_sink_s.h new file mode 100644 index 000000000..a2e086cad --- /dev/null +++ b/gr-usrp/src/usrp1_sink_s.h @@ -0,0 +1,86 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef INCLUDED_USRP1_SINK_S_H +#define INCLUDED_USRP1_SINK_S_H + +#include <usrp1_sink_base.h> + +class usrp1_sink_s; +typedef boost::shared_ptr<usrp1_sink_s> usrp1_sink_s_sptr; + + +// public shared_ptr constructor + +usrp1_sink_s_sptr +usrp1_make_sink_s (int which_board, + unsigned int interp_rate, + int nchan, + int mux, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error); + +/*! + * \brief interface to Universal Software Radio Peripheral Tx path (Rev 1) + * + * input: short + */ +class usrp1_sink_s : public usrp1_sink_base { + private: + + friend usrp1_sink_s_sptr + usrp1_make_sink_s (int which_board, + unsigned int interp_rate, + int nchan, + int mux, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error); + + protected: + usrp1_sink_s (int which_board, + unsigned int interp_rate, + int nchan, + int mux, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error); + + virtual void copy_to_usrp_buffer (gr_vector_const_void_star &input_items, + int input_index, + int input_items_available, + int &input_items_consumed, + void *usrp_buffer, + int usrp_buffer_length, + int &bytes_written); + public: + ~usrp1_sink_s (); +}; + +#endif /* INCLUDED_USRP1_SINK_S_H */ diff --git a/gr-usrp/src/usrp1_source_base.cc b/gr-usrp/src/usrp1_source_base.cc new file mode 100644 index 000000000..9f5cb2510 --- /dev/null +++ b/gr-usrp/src/usrp1_source_base.cc @@ -0,0 +1,425 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <usrp1_source_base.h> +#include <gr_io_signature.h> +#include <usrp_standard.h> +#include <assert.h> + +static const int OUTPUT_MULTIPLE_BYTES = 4 * 1024; + +usrp1_source_base::usrp1_source_base (const std::string &name, + gr_io_signature_sptr output_signature, + int which_board, + unsigned int decim_rate, + int nchan, + int mux, + int mode, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error) + : gr_sync_block (name, + gr_make_io_signature (0, 0, 0), + output_signature), + d_noverruns (0) +{ + d_usrp = usrp_standard_rx::make (which_board, decim_rate, + nchan, mux, mode, + fusb_block_size, + fusb_nblocks, + fpga_filename, + firmware_filename); + if (d_usrp == 0) + throw std::runtime_error ("can't open usrp1"); + + // All calls to d_usrp->read must be multiples of 512 bytes. + // We jack this up to 4k to reduce overhead. + + set_output_multiple (OUTPUT_MULTIPLE_BYTES / output_signature->sizeof_stream_item (0)); +} + +usrp1_source_base::~usrp1_source_base () +{ + delete d_usrp; +} + +unsigned int +usrp1_source_base::sizeof_basic_sample() const +{ + return usrp_standard_rx::format_width(d_usrp->format()) / 8; +} + +bool +usrp1_source_base::start() +{ + return d_usrp->start(); +} + +bool +usrp1_source_base::stop() +{ + return d_usrp->stop(); +} + +int +usrp1_source_base::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + static const int BUFSIZE = 4 * OUTPUT_MULTIPLE_BYTES; + unsigned char buf[BUFSIZE]; + int output_index = 0; + int output_items_produced; + int bytes_read; + bool overrun; + + while (output_index < noutput_items){ + int nbytes = ninput_bytes_reqd_for_noutput_items (noutput_items - output_index); + nbytes = std::min (nbytes, BUFSIZE); + + int result_nbytes = d_usrp->read (buf, nbytes, &overrun); + if (overrun){ + // fprintf (stderr, "usrp1_source: overrun\n"); + fputs ("uO", stderr); + d_noverruns++; + } + + if (result_nbytes < 0) // We've got a problem. Usually board unplugged or powered down. + return -1; // Indicate we're done. + + if (result_nbytes != nbytes){ // not really an error, but unexpected + fprintf (stderr, "usrp1_source: short read. Expected %d, got %d\n", + nbytes, result_nbytes); + } + + copy_from_usrp_buffer (output_items, + output_index, + noutput_items - output_index, // output_items_available + output_items_produced, // [out] + buf, // usrp_buffer + result_nbytes, // usrp_buffer_length + bytes_read); // [out] + + assert (output_index + output_items_produced <= noutput_items); + assert (bytes_read == result_nbytes); + + output_index += output_items_produced; + } + + return noutput_items; +} + + +bool +usrp1_source_base::set_decim_rate (unsigned int rate) +{ + return d_usrp->set_decim_rate (rate); +} + +bool +usrp1_source_base::set_nchannels (int nchan) +{ + return d_usrp->set_nchannels (nchan); +} + +bool +usrp1_source_base::set_mux (int mux) +{ + return d_usrp->set_mux (mux); +} + +bool +usrp1_source_base::set_rx_freq (int channel, double freq) +{ + return d_usrp->set_rx_freq (channel, freq); +} + +long +usrp1_source_base::fpga_master_clock_freq() const +{ + return d_usrp->fpga_master_clock_freq(); +} + +long +usrp1_source_base::converter_rate() const +{ + return d_usrp->converter_rate(); +} + +unsigned int +usrp1_source_base::decim_rate () const +{ + return d_usrp->decim_rate (); +} + +int +usrp1_source_base::nchannels () const +{ + return d_usrp->nchannels (); +} + +int +usrp1_source_base::mux () const +{ + return d_usrp->mux (); +} + +double +usrp1_source_base::rx_freq (int channel) const +{ + return d_usrp->rx_freq (channel); +} + +bool +usrp1_source_base::set_fpga_mode (int mode) +{ + return d_usrp->set_fpga_mode (mode); +} + +bool +usrp1_source_base::set_ddc_phase (int channel, int phase) +{ + return d_usrp->set_ddc_phase(channel, phase); +} + +bool +usrp1_source_base::set_dc_offset_cl_enable(int bits, int mask) +{ + return d_usrp->set_dc_offset_cl_enable(bits, mask); +} + +void +usrp1_source_base::set_verbose (bool verbose) +{ + d_usrp->set_verbose (verbose); +} + +bool +usrp1_source_base::write_aux_dac (int which_dboard, int which_dac, int value) +{ + return d_usrp->write_aux_dac (which_dboard, which_dac, value); +} + +int +usrp1_source_base::read_aux_adc (int which_dboard, int which_adc) +{ + return d_usrp->read_aux_adc (which_dboard, which_adc); +} + +bool +usrp1_source_base::write_eeprom (int i2c_addr, int eeprom_offset, const std::string buf) +{ + return d_usrp->write_eeprom (i2c_addr, eeprom_offset, buf); +} + +std::string +usrp1_source_base::read_eeprom (int i2c_addr, int eeprom_offset, int len) +{ + return d_usrp->read_eeprom (i2c_addr, eeprom_offset, len); +} + +bool +usrp1_source_base::write_i2c (int i2c_addr, const std::string buf) +{ + return d_usrp->write_i2c (i2c_addr, buf); +} + +std::string +usrp1_source_base::read_i2c (int i2c_addr, int len) +{ + return d_usrp->read_i2c (i2c_addr, len); +} + +bool +usrp1_source_base::set_pga (int which, double gain) +{ + return d_usrp->set_pga (which, gain); +} + +double +usrp1_source_base::pga (int which) const +{ + return d_usrp->pga (which); +} + +double +usrp1_source_base::pga_min () const +{ + return d_usrp->pga_min (); +} + +double +usrp1_source_base::pga_max () const +{ + return d_usrp->pga_max (); +} + +double +usrp1_source_base::pga_db_per_step () const +{ + return d_usrp->pga_db_per_step (); +} + +int +usrp1_source_base::daughterboard_id (int which) const +{ + return d_usrp->daughterboard_id (which); +} + + +bool +usrp1_source_base::set_adc_offset (int which, int offset) +{ + return d_usrp->set_adc_offset (which, offset); +} + +bool +usrp1_source_base::set_dac_offset (int which, int offset, int offset_pin) +{ + return d_usrp->set_dac_offset (which, offset, offset_pin); +} + +bool +usrp1_source_base::set_adc_buffer_bypass (int which, bool bypass) +{ + return d_usrp->set_adc_buffer_bypass (which, bypass); +} + +std::string +usrp1_source_base::serial_number() +{ + return d_usrp->serial_number(); +} + +bool +usrp1_source_base::_write_oe (int which_dboard, int value, int mask) +{ + return d_usrp->_write_oe (which_dboard, value, mask); +} + +bool +usrp1_source_base::write_io (int which_dboard, int value, int mask) +{ + return d_usrp->write_io (which_dboard, value, mask); +} + +int +usrp1_source_base::read_io (int which_dboard) +{ + return d_usrp->read_io (which_dboard); +} + + + + +// internal routines... + +bool +usrp1_source_base::_write_fpga_reg (int regno, int value) +{ + return d_usrp->_write_fpga_reg (regno, value); +} + +bool +usrp1_source_base::_write_fpga_reg_masked (int regno, int value, int mask) +{ + return d_usrp->_write_fpga_reg_masked (regno, value, mask); +} + +int +usrp1_source_base::_read_fpga_reg (int regno) +{ + return d_usrp->_read_fpga_reg (regno); +} + +bool +usrp1_source_base::_write_9862 (int which_codec, int regno, unsigned char value) +{ + return d_usrp->_write_9862 (which_codec, regno, value); +} + +int +usrp1_source_base::_read_9862 (int which_codec, int regno) const +{ + return d_usrp->_read_9862 (which_codec, regno); +} + +bool +usrp1_source_base::_write_spi (int optional_header, int enables, + int format, std::string buf) +{ + return d_usrp->_write_spi (optional_header, enables, format, buf); +} + +std::string +usrp1_source_base::_read_spi (int optional_header, int enables, int format, int len) +{ + return d_usrp->_read_spi (optional_header, enables, format, len); +} + +bool +usrp1_source_base::set_format(unsigned int format) +{ + return d_usrp->set_format(format); +} + +unsigned int +usrp1_source_base::format() const +{ + return d_usrp->format(); +} + +unsigned int +usrp1_source_base::make_format(int width, int shift, bool want_q, bool bypass_halfband) +{ + return usrp_standard_rx::make_format(width, shift, want_q, bypass_halfband); +} + +int +usrp1_source_base::format_width(unsigned int format) +{ + return usrp_standard_rx::format_width(format); +} + +int +usrp1_source_base::format_shift(unsigned int format) +{ + return usrp_standard_rx::format_shift(format); +} + +bool +usrp1_source_base::format_want_q(unsigned int format) +{ + return usrp_standard_rx::format_want_q(format); +} + +bool +usrp1_source_base::format_bypass_halfband(unsigned int format) +{ + return usrp_standard_rx::format_bypass_halfband(format); +} diff --git a/gr-usrp/src/usrp1_source_base.h b/gr-usrp/src/usrp1_source_base.h new file mode 100644 index 000000000..a04eadf2b --- /dev/null +++ b/gr-usrp/src/usrp1_source_base.h @@ -0,0 +1,455 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef INCLUDED_USRP1_SOURCE_BASE_H +#define INCLUDED_USRP1_SOURCE_BASE_H + +#include <gr_sync_block.h> +#include <stdexcept> + +class usrp_standard_rx; + +/*! + * \brief abstract interface to Universal Software Radio Peripheral Rx path (Rev 1) + */ +class usrp1_source_base : public gr_sync_block { + private: + usrp_standard_rx *d_usrp; + int d_noverruns; + + protected: + usrp1_source_base (const std::string &name, + gr_io_signature_sptr output_signature, + int which_board, + unsigned int decim_rate, + int nchan, + int mux, + int mode, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error); + + /*! + * \brief return number of usrp input bytes required to produce noutput items. + */ + virtual int ninput_bytes_reqd_for_noutput_items (int noutput_items) = 0; + + /*! + * \brief number of bytes in a low-level sample + */ + unsigned int sizeof_basic_sample() const; + + /*! + * \brief convert between native usrp format and output item format + * + * \param output_items[out] stream(s) of output items + * \param output_index[in] starting index in output_items + * \param output_items_available[in] number of empty items available at item[index] + * \param output_items_produced[out] number of items produced by copy + * \param usrp_buffer[in] source buffer + * \param usrp_buffer_length[in] number of bytes available in \p usrp_buffer + * \param bytes_read[out] number of bytes read from \p usrp_buffer + * + * The copy must consume all bytes available. That is, \p bytes_read must equal + * \p usrp_buffer_length. + */ + virtual void copy_from_usrp_buffer (gr_vector_void_star &output_items, + int output_index, + int output_items_available, + int &output_items_produced, + const void *usrp_buffer, + int usrp_buffer_length, + int &bytes_read) = 0; + + public: + //! magic value used on alternate register read interfaces + static const int READ_FAILED = -99999; + + ~usrp1_source_base (); + + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + bool start(); + bool stop(); + + /*! + * \brief Set decimator rate. \p rate must be EVEN and in [8, 256]. + * + * The final complex sample rate across the USB is + * adc_freq () / decim_rate () + */ + bool set_decim_rate (unsigned int rate); + bool set_nchannels (int nchan); + bool set_mux (int mux); + + /*! + * \brief set the center frequency of the digital down converter. + * + * \p channel must be 0. \p freq is the center frequency in Hz. + * It must be in the range [-FIXME, FIXME]. The frequency specified is + * quantized. Use rx_freq to retrieve the actual value used. + */ + bool set_rx_freq (int channel, double freq); + + /*! + * \brief set fpga special modes + */ + bool set_fpga_mode (int mode); + + void set_verbose (bool verbose); + + /*! + * \brief Set the digital down converter phase register. + * + * \param channel which ddc channel [0, 3] + * \param phase 32-bit integer phase value. + */ + bool set_ddc_phase(int channel, int phase); + + /*! + * \brief Set Programmable Gain Amplifier (PGA) + * + * \param which which A/D [0,3] + * \param gain_in_db gain value (linear in dB) + * + * gain is rounded to closest setting supported by hardware. + * + * \returns true iff sucessful. + * + * \sa pga_min(), pga_max(), pga_db_per_step() + */ + bool set_pga (int which, double gain_in_db); + + /*! + * \brief Return programmable gain amplifier gain setting in dB. + * + * \param which which A/D [0,3] + */ + double pga (int which) const; + + /*! + * \brief Return minimum legal PGA setting in dB. + */ + double pga_min () const; + + /*! + * \brief Return maximum legal PGA setting in dB. + */ + double pga_max () const; + + /*! + * \brief Return hardware step size of PGA (linear in dB). + */ + double pga_db_per_step () const; + + // ACCESSORS + + long fpga_master_clock_freq() const; + long converter_rate() const; + long adc_rate() const { return converter_rate(); } // alias + long adc_freq() const { return converter_rate(); } // deprecated alias + + unsigned int decim_rate () const; + int nchannels () const; + int mux () const; + double rx_freq (int channel) const; + int noverruns () const { return d_noverruns; } + + /*! + * \brief Return daughterboard ID for given Rx daughterboard slot [0,1]. + * + * \return daughterboard id >= 0 if successful + * \return -1 if no daugherboard + * \return -2 if invalid EEPROM on daughterboard + */ + int daughterboard_id (int which_dboard) const; + + /*! + * \brief Write auxiliary digital to analog converter. + * + * \param which_dboard [0,1] which d'board + * N.B., SLOT_TX_A and SLOT_RX_A share the same AUX DAC's. + * SLOT_TX_B and SLOT_RX_B share the same AUX DAC's. + * \param which_dac [2,3] TX slots must use only 2 and 3. + * \param value [0,4095] + * \returns true iff successful + */ + bool write_aux_dac (int which_board, int which_dac, int value); + + /*! + * \brief Read auxiliary analog to digital converter. + * + * \param which_dboard [0,1] which d'board + * \param which_adc [0,1] + * \returns value in the range [0,4095] if successful, else READ_FAILED. + */ + int read_aux_adc (int which_dboard, int which_adc); + + /*! + * \brief Write EEPROM on motherboard or any daughterboard. + * \param i2c_addr I2C bus address of EEPROM + * \param eeprom_offset byte offset in EEPROM to begin writing + * \param buf the data to write + * \returns true iff sucessful + */ + bool write_eeprom (int i2c_addr, int eeprom_offset, const std::string buf); + + /*! + * \brief Write EEPROM on motherboard or any daughterboard. + * \param i2c_addr I2C bus address of EEPROM + * \param eeprom_offset byte offset in EEPROM to begin reading + * \param len number of bytes to read + * \returns the data read if successful, else a zero length string. + */ + std::string read_eeprom (int i2c_addr, int eeprom_offset, int len); + + /*! + * \brief Write to I2C peripheral + * \param i2c_addr I2C bus address (7-bits) + * \param buf the data to write + * \returns true iff successful + * Writes are limited to a maximum of of 64 bytes. + */ + bool write_i2c (int i2c_addr, const std::string buf); + + /*! + * \brief Read from I2C peripheral + * \param i2c_addr I2C bus address (7-bits) + * \param len number of bytes to read + * \returns the data read if successful, else a zero length string. + * Reads are limited to a maximum of of 64 bytes. + */ + std::string read_i2c (int i2c_addr, int len); + + /*! + * \brief Set ADC offset correction + * \param which which ADC[0,3]: 0 = RX_A I, 1 = RX_A Q... + * \param offset 16-bit value to subtract from raw ADC input. + */ + bool set_adc_offset (int which, int offset); + + /*! + * \brief Set DAC offset correction + * \param which which DAC[0,3]: 0 = TX_A I, 1 = TX_A Q... + * \param offset 10-bit offset value (ambiguous format: See AD9862 datasheet). + * \param offset_pin 1-bit value. If 0 offset applied to -ve differential pin; + * If 1 offset applied to +ve differential pin. + */ + bool set_dac_offset (int which, int offset, int offset_pin); + + /*! + * \brief Control ADC input buffer + * \param which which ADC[0,3] + * \param bypass if non-zero, bypass input buffer and connect input + * directly to switched cap SHA input of RxPGA. + */ + bool set_adc_buffer_bypass (int which, bool bypass); + + /*! + * \brief return the usrp's serial number. + * + * \returns non-zero length string iff successful. + */ + std::string serial_number(); + + /*! + * \brief Write direction register (output enables) for pins that go to daughterboard. + * + * \param which_dboard [0,1] which d'board + * \param value value to write into register + * \param mask which bits of value to write into reg + * + * Each d'board has 16-bits of general purpose i/o. + * Setting the bit makes it an output from the FPGA to the d'board. + * + * This register is initialized based on a value stored in the + * d'board EEPROM. In general, you shouldn't be using this routine + * without a very good reason. Using this method incorrectly will + * kill your USRP motherboard and/or daughterboard. + */ + bool _write_oe (int which_dboard, int value, int mask); + + /*! + * \brief Write daughterboard i/o pin value + * + * \param which_dboard [0,1] which d'board + * \param value value to write into register + * \param mask which bits of value to write into reg + */ + bool write_io (int which_dboard, int value, int mask); + + /*! + * \brief Read daughterboard i/o pin value + * + * \param which_dboard [0,1] which d'board + * \returns register value if successful, else READ_FAILED + */ + int read_io (int which_dboard); + + /*! + * \brief Enable/disable automatic DC offset removal control loop in FPGA + * + * \param bits which control loops to enable + * \param mask which \p bits to pay attention to + * + * If the corresponding bit is set, enable the automatic DC + * offset correction control loop. + * + * <pre> + * The 4 low bits are significant: + * + * ADC0 = (1 << 0) + * ADC1 = (1 << 1) + * ADC2 = (1 << 2) + * ADC3 = (1 << 3) + * </pre> + * + * By default the control loop is enabled on all ADC's. + */ + bool set_dc_offset_cl_enable(int bits, int mask); + + /*! + * \brief Specify Rx data format. + * + * \param format format specifier + * + * Rx data format control register + * + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------------------------------------+-+-+---------+-------+ + * | Reserved (Must be zero) |B|Q| WIDTH | SHIFT | + * +-----------------------------------------+-+-+---------+-------+ + * + * SHIFT specifies arithmetic right shift [0, 15] + * WIDTH specifies bit-width of I & Q samples across the USB [1, 16] (not all valid) + * Q if set deliver both I & Q, else just I + * B if set bypass half-band filter. + * + * Right now the acceptable values are: + * + * B Q WIDTH SHIFT + * 0 1 16 0 + * 0 1 8 8 + * + * More valid combos to come. + * + * Default value is 0x00000300 16-bits, 0 shift, deliver both I & Q. + */ + bool set_format(unsigned int format); + + /*! + * \brief return current format + */ + unsigned int format () const; + + static unsigned int make_format(int width=16, int shift=0, + bool want_q=true, bool bypass_halfband=false); + static int format_width(unsigned int format); + static int format_shift(unsigned int format); + static bool format_want_q(unsigned int format); + static bool format_bypass_halfband(unsigned int format); + + // ---------------------------------------------------------------- + // internal routines... + // You probably shouldn't be using these... + // ---------------------------------------------------------------- + + /*! + * \brief Write FPGA register. + * \param regno 7-bit register number + * \param value 32-bit value + * \returns true iff successful + */ + bool _write_fpga_reg (int regno, int value); //< 7-bit regno, 32-bit value + + /*! + * \brief Write FPGA register masked. + * \param regno 7-bit register number + * \param value 16-bit value + * \param mask 16-bit mask + * \returns true iff successful + */ + bool _write_fpga_reg_masked (int regno, int value, int mask); //< 7-bit regno, 16-bit value, 16-bit mask + + /*! + * \brief Read FPGA register. + * \param regno 7-bit register number + * \returns register value if successful, else READ_FAILED + */ + int _read_fpga_reg (int regno); + + /*! + * \brief Write AD9862 register. + * \param which_codec 0 or 1 + * \param regno 6-bit register number + * \param value 8-bit value + * \returns true iff successful + */ + bool _write_9862 (int which_codec, int regno, unsigned char value); + + /*! + * \brief Read AD9862 register. + * \param which_codec 0 or 1 + * \param regno 6-bit register number + * \returns register value if successful, else READ_FAILED + */ + int _read_9862 (int which_codec, int regno) const; + + /*! + * \brief Write data to SPI bus peripheral. + * + * \param optional_header 0,1 or 2 bytes to write before buf. + * \param enables bitmask of peripherals to write. See usrp_spi_defs.h + * \param format transaction format. See usrp_spi_defs.h SPI_FMT_* + * \param buf the data to write + * \returns true iff successful + * Writes are limited to a maximum of 64 bytes. + * + * If \p format specifies that optional_header bytes are present, they are + * written to the peripheral immediately prior to writing \p buf. + */ + bool _write_spi (int optional_header, int enables, int format, std::string buf); + + /* + * \brief Read data from SPI bus peripheral. + * + * \param optional_header 0,1 or 2 bytes to write before buf. + * \param enables bitmask of peripheral to read. See usrp_spi_defs.h + * \param format transaction format. See usrp_spi_defs.h SPI_FMT_* + * \param len number of bytes to read. Must be in [0,64]. + * \returns the data read if sucessful, else a zero length string. + * + * Reads are limited to a maximum of 64 bytes. + * + * If \p format specifies that optional_header bytes are present, they + * are written to the peripheral first. Then \p len bytes are read from + * the peripheral and returned. + */ + std::string _read_spi (int optional_header, int enables, int format, int len); +}; + +#endif /* INCLUDED_USRP1_SOURCE_BASE_H */ diff --git a/gr-usrp/src/usrp1_source_c.cc b/gr-usrp/src/usrp1_source_c.cc new file mode 100644 index 000000000..e61437006 --- /dev/null +++ b/gr-usrp/src/usrp1_source_c.cc @@ -0,0 +1,131 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2006 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <usrp1_source_c.h> +#include <gr_io_signature.h> +#include <usrp_standard.h> +#include <usrp_bytesex.h> + +static const int NBASIC_SAMPLES_PER_ITEM = 2; // I & Q + +usrp1_source_c_sptr +usrp1_make_source_c (int which_board, + unsigned int decim_rate, + int nchan, + int mux, + int mode, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error) +{ + return usrp1_source_c_sptr (new usrp1_source_c (which_board, + decim_rate, + nchan, + mux, + mode, + fusb_block_size, + fusb_nblocks, + fpga_filename, + firmware_filename + )); +} + + +usrp1_source_c::usrp1_source_c (int which_board, + unsigned int decim_rate, + int nchan, + int mux, + int mode, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error) + : usrp1_source_base ("usrp1_source_c", + gr_make_io_signature (1, 1, sizeof (gr_complex)), + which_board, decim_rate, nchan, mux, mode, + fusb_block_size, fusb_nblocks, + fpga_filename, firmware_filename) +{ +} + +usrp1_source_c::~usrp1_source_c () +{ + // NOP +} + +int +usrp1_source_c::ninput_bytes_reqd_for_noutput_items (int noutput_items) +{ + return noutput_items * NBASIC_SAMPLES_PER_ITEM * sizeof_basic_sample(); +} + +/* + * Convert interleaved 8 or 16-bit I & Q from usrp buffer into a single + * complex output stream. + */ +void +usrp1_source_c::copy_from_usrp_buffer (gr_vector_void_star &output_items, + int output_index, + int output_items_available, + int &output_items_produced, + const void *usrp_buffer, + int usrp_buffer_length, + int &bytes_read) +{ + gr_complex *out = &((gr_complex *) output_items[0])[output_index]; + unsigned sbs = sizeof_basic_sample(); + unsigned nusrp_bytes_per_item = NBASIC_SAMPLES_PER_ITEM * sbs; + + int nitems = std::min (output_items_available, + (int)(usrp_buffer_length / nusrp_bytes_per_item)); + + signed char *s8 = (signed char *) usrp_buffer; + short *s16 = (short *) usrp_buffer; + + switch (sbs){ + case 1: + for (int i = 0; i < nitems; i++){ + out[i] = gr_complex ((float)(s8[2*i+0] << 8), (float)(s8[2*i+1] << 8)); + } + break; + + case 2: + for (int i = 0; i < nitems; i++){ + out[i] = gr_complex ((float) usrp_to_host_short(s16[2*i+0]), + (float) usrp_to_host_short(s16[2*i+1])); + } + break; + + default: + assert(0); + } + + output_items_produced = nitems; + bytes_read = nitems * nusrp_bytes_per_item; +} diff --git a/gr-usrp/src/usrp1_source_c.h b/gr-usrp/src/usrp1_source_c.h new file mode 100644 index 000000000..4b8749356 --- /dev/null +++ b/gr-usrp/src/usrp1_source_c.h @@ -0,0 +1,93 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef INCLUDED_USRP1_SOURCE_C_H +#define INCLUDED_USRP1_SOURCE_C_H + +#include <usrp1_source_base.h> +#include <stdexcept> + +class usrp_standard_rx; + + +class usrp1_source_c; +typedef boost::shared_ptr<usrp1_source_c> usrp1_source_c_sptr; + + +// public shared_ptr constructor + +usrp1_source_c_sptr +usrp1_make_source_c (int which_board, + unsigned int decim_rate, + int nchan, + int mux, + int mode, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error); + +/*! + * \brief interface to Universal Software Radio Peripheral Rx path (Rev 1) + */ +class usrp1_source_c : public usrp1_source_base { + private: + friend usrp1_source_c_sptr + usrp1_make_source_c (int which_board, + unsigned int decim_rate, + int nchan, + int mux, + int mode, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error); + + protected: + usrp1_source_c (int which_board, + unsigned int decim_rate, + int nchan, + int mux, + int mode, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error); + + virtual int ninput_bytes_reqd_for_noutput_items (int noutput_items); + + virtual void copy_from_usrp_buffer (gr_vector_void_star &output_items, + int output_index, + int output_items_available, + int &output_items_produced, + const void *usrp_buffer, + int usrp_buffer_length, + int &bytes_read); + + public: + ~usrp1_source_c (); +}; + +#endif /* INCLUDED_USRP1_SOURCE_C_H */ diff --git a/gr-usrp/src/usrp1_source_s.cc b/gr-usrp/src/usrp1_source_s.cc new file mode 100644 index 000000000..c2c105bc8 --- /dev/null +++ b/gr-usrp/src/usrp1_source_s.cc @@ -0,0 +1,131 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2006 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <usrp1_source_s.h> +#include <gr_io_signature.h> +#include <usrp_standard.h> +#include <usrp_bytesex.h> + +static const int NBASIC_SAMPLES_PER_ITEM = 1; + +usrp1_source_s_sptr +usrp1_make_source_s (int which_board, + unsigned int decim_rate, + int nchan, + int mux, + int mode, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error) +{ + return usrp1_source_s_sptr (new usrp1_source_s (which_board, + decim_rate, + nchan, + mux, + mode, + fusb_block_size, + fusb_nblocks, + fpga_filename, + firmware_filename + )); +} + + +usrp1_source_s::usrp1_source_s (int which_board, + unsigned int decim_rate, + int nchan, + int mux, + int mode, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error) + : usrp1_source_base ("usrp1_source_s", + gr_make_io_signature (1, 1, sizeof (short)), + which_board, decim_rate, nchan, mux, mode, + fusb_block_size, + fusb_nblocks, + fpga_filename, firmware_filename) +{ +} + +usrp1_source_s::~usrp1_source_s () +{ + // NOP +} + +int +usrp1_source_s::ninput_bytes_reqd_for_noutput_items (int noutput_items) +{ + return noutput_items * NBASIC_SAMPLES_PER_ITEM * sizeof_basic_sample(); +} + +/* + * Convert interleaved 8 or 16-bit I & Q from usrp buffer into a single + * short output stream. + */ +void +usrp1_source_s::copy_from_usrp_buffer (gr_vector_void_star &output_items, + int output_index, + int output_items_available, + int &output_items_produced, + const void *usrp_buffer, + int usrp_buffer_length, + int &bytes_read) +{ + short *out = &((short *) output_items[0])[output_index]; + unsigned sbs = sizeof_basic_sample(); + unsigned nusrp_bytes_per_item = NBASIC_SAMPLES_PER_ITEM * sbs; + + int nitems = std::min (output_items_available, + (int)(usrp_buffer_length / nusrp_bytes_per_item)); + + signed char *s8 = (signed char *) usrp_buffer; + short *s16 = (short *) usrp_buffer; + + switch(sbs){ + case 1: + for (int i = 0; i < nitems; i++){ + out[i] = s8[i] << 8; + } + break; + + case 2: + for (int i = 0; i < nitems; i++){ + out[i] = usrp_to_host_short(s16[i]); + } + break; + + default: + assert(0); + } + + output_items_produced = nitems; + bytes_read = nitems * nusrp_bytes_per_item; +} diff --git a/gr-usrp/src/usrp1_source_s.h b/gr-usrp/src/usrp1_source_s.h new file mode 100644 index 000000000..a3a3e743a --- /dev/null +++ b/gr-usrp/src/usrp1_source_s.h @@ -0,0 +1,94 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef INCLUDED_USRP1_SOURCE_S_H +#define INCLUDED_USRP1_SOURCE_S_H + +#include <usrp1_source_base.h> +#include <stdexcept> + +class usrp_standard_rx; + + +class usrp1_source_s; +typedef boost::shared_ptr<usrp1_source_s> usrp1_source_s_sptr; + + +// public shared_ptr constructor + +usrp1_source_s_sptr +usrp1_make_source_s (int which_board, + unsigned int decim_rate, + int nchan, + int mux, + int mode, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error); + +/*! + * \brief interface to Universal Software Radio Peripheral Rx path (Rev 1) + * + * output: 1 stream of short + */ +class usrp1_source_s : public usrp1_source_base { + private: + friend usrp1_source_s_sptr + usrp1_make_source_s (int which_board, + unsigned int decim_rate, + int nchan, + int mux, + int mode, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error); + + protected: + usrp1_source_s (int which_board, + unsigned int decim_rate, + int nchan, + int mux, + int mode, + int fusb_block_size, + int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) throw (std::runtime_error); + + virtual int ninput_bytes_reqd_for_noutput_items (int noutput_items); + + virtual void copy_from_usrp_buffer (gr_vector_void_star &output_items, + int output_index, + int output_items_available, + int &output_items_produced, + const void *usrp_buffer, + int usrp_buffer_length, + int &bytes_read); + public: + ~usrp1_source_s (); +}; + +#endif /* INCLUDED_USRP1_SOURCE_S_H */ diff --git a/gr-usrp/src/usrp_multi.py b/gr-usrp/src/usrp_multi.py new file mode 100644 index 000000000..865e42fa1 --- /dev/null +++ b/gr-usrp/src/usrp_multi.py @@ -0,0 +1,233 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +import math +from gnuradio import gr, gru +from gnuradio.gr import hier_block_base +from gnuradio import usrp +from gnuradio import usrp1 # usrp Rev 1 and later +from gnuradio import blks +import usrp_prims +import sys + + +class multi_source_align(object): + def __init__(self, fg, master_serialno,decim,nchan=2,pga_gain=0.0,cordic_freq=0.0,mux=None,align_interval=-1): + """ + Align multiple sources (usrps) using samplenumbers in the first channel. + + Takes two ore more sources producing interleaved shorts. + produces nchan * nsources gr_complex output streams. + + @param nchan: number of interleaved channels in source + @param align_interval: number of samples to minimally skip between alignments + default = -1 which means align only once per work call. + @param master_serial_no: serial number of the source which must be the master. + + + Exported sub-blocks (attributes): + master_source + slave_source + usrp_master + usrp_slave + """ + mode=usrp.FPGA_MODE_NORMAL + mode = mode | usrp_prims.bmFR_MODE_RX_COUNTING_32BIT #(1 << 2) #usrp1.FPGA_MODE_COUNTING_32BIT + align=gr.align_on_samplenumbers_ss (nchan,align_interval) + self.usrp_master = None + self.usrp_slave = None + # um is master usrp + # us is slave usrp + if mux is None: + mux=self.get_default_mux() #Note that all channels have shifted left because of the added 32 bit counter channel + + u1 = usrp.source_s (1, decim, nchan, gru.hexint(mux), mode,fpga_filename="multi_2rxhb_2tx.rbf" ) + u0 = usrp.source_s (0, decim, nchan, gru.hexint(mux), mode,fpga_filename="multi_2rxhb_2tx.rbf" ) + print 'usrp[0] serial',u0.serial_number() + print 'usrp[1] serial',u1.serial_number() + #default, choose the second found usrp as master (which is usually the usrp which was first plugged in) + um_index=1 + um=u1 + us_index=0 + us=u0 + if (not (master_serialno is None)): #((master_serialno>0) | (master_serialno <-2)): + if (u0.serial_number() == master_serialno): + um_index=0 + um=u0 + us_index=1 + us=u1 + elif (u1.serial_number() != master_serialno): + errorstring = 'Error. requested master_serialno ' + master_serialno +' not found\n' + errorstring = errorstring + 'Available are:\n' + errorstring = errorstring + 'usrp[1] serial_no = ' + u1.serial_number() +'\n' + errorstring = errorstring + 'usrp[0] serial_no = ' + u0.serial_number() +'\n' + print errorstring + raise ValueError, errorstring + else: #default, just choose the first found usrp as master + um_index=0 + um=u0 + us_index=1 + us=u1 + + self.usrp_master=um + self.usrp_slave=us + print 'usrp_master=usrp[%i] serial_no = %s' % (um_index,self.usrp_master.serial_number() ,) + print 'usrp_slave=usrp[%i] serial_no = %s' % (us_index,self.usrp_slave.serial_number() ,) + self.subdev_mAr = usrp.selected_subdev(self.usrp_master, (0,0)) + self.subdev_mBr = usrp.selected_subdev(self.usrp_master, (1,0)) + self.subdev_sAr = usrp.selected_subdev(self.usrp_slave, (0,0)) + self.subdev_sBr = usrp.selected_subdev(self.usrp_slave, (1,0)) + #throttle = gr.throttle(gr.sizeof_gr_complex, input_rate) + if not (pga_gain is None): + um.set_pga (0, pga_gain) + um.set_pga (1, pga_gain) + + us.set_pga (0, pga_gain) + us.set_pga (1, pga_gain) + + self.input_rate = um.adc_freq () / um.decim_rate () + deintm=gr.deinterleave(gr.sizeof_gr_complex) + deints=gr.deinterleave(gr.sizeof_gr_complex) + nullsinkm=gr.null_sink(gr.sizeof_gr_complex) + nullsinks=gr.null_sink(gr.sizeof_gr_complex) + + tocomplexm=gr.interleaved_short_to_complex() + tocomplexs=gr.interleaved_short_to_complex() + + fg.connect(um,(align,0)) + fg.connect(us,(align,1)) + fg.connect((align,0),tocomplexm) + fg.connect((align,1),tocomplexs) + fg.connect(tocomplexm,deintm) + fg.connect(tocomplexs,deints) + fg.connect((deintm,0),nullsinkm) #The counters are not usefull for the user but must be connected to something + fg.connect((deints,0),nullsinks) #The counters are not usefull for the user but must be connected to something + if 4==nchan: + nullsinkm3=gr.null_sink(gr.sizeof_gr_complex) + nullsinks3=gr.null_sink(gr.sizeof_gr_complex) + fg.connect((deintm,3), nullsinkm3) #channel 4 is not used but must be connected + fg.connect((deints,3), nullsinks3) #channel 4 is not used but must be connected + + self.fg=fg + self.master_source=deintm + self.slave_source=deints + + if not (cordic_freq is None): + um.set_rx_freq (1, cordic_freq) + um.set_rx_freq (0, cordic_freq) + us.set_rx_freq (1, cordic_freq) + us.set_rx_freq (0, cordic_freq) + + self.enable_master_and_slave() + # add an idle handler + self.unsynced=True + + # wire the block together + #hier_block_multi_tail.__init__(self, fg, nchan,deintm,deints) + + def get_default_mux(self): + return 0x10321032 # Note that all channels have shifted left because of the added 32 bit counter channel + + def get_master_source_c(self): + return self.master_source + + def get_slave_source_c(self): + return self.slave_source + + def get_master_usrp(self): + return self.usrp_master + + def get_slave_usrp(self): + return self.usrp_slave + + def enable_master_and_slave(self): + # Warning, allways FIRST enable the slave before you enable the master + # This is to be sure you don't have two masters connecting to each other + # Otherwise you could ruin your hardware because the two sync outputs would be connected together + + #SLAVE + #disable master, enable slave and set sync pulse to zero + reg_mask = usrp_prims.bmFR_RX_SYNC_SLAVE | usrp_prims.bmFR_RX_SYNC_MASTER | usrp_prims.bmFR_RX_SYNC + self.usrp_slave._u._write_fpga_reg_masked(usrp_prims.FR_RX_MASTER_SLAVE, usrp_prims.bmFR_RX_SYNC_SLAVE,reg_mask) + #set SYNC slave iopin on daughterboards RXA as input + oe = 0 # set rx_a_io[bitnoFR_RX_SYNC_INPUT_IOPIN] as input + oe_mask = usrp_prims.bmFR_RX_SYNC_INPUT_IOPIN + self.usrp_slave._u._write_oe(0,oe,oe_mask) + #Now it is save to enable the master + + #MASTER + #enable master, disable slave and set sync pulse to zero + reg_mask = usrp_prims.bmFR_RX_SYNC_SLAVE | usrp_prims.bmFR_RX_SYNC_MASTER | usrp_prims.bmFR_RX_SYNC + self.usrp_master._u._write_fpga_reg_masked(usrp_prims.FR_RX_MASTER_SLAVE,usrp_prims.bmFR_RX_SYNC_MASTER,reg_mask) + #set SYNC master iopin on daughterboards RXA as output + oe = usrp_prims.bmFR_RX_SYNC_OUTPUT_IOPIN # set rx_a_io[bitnoFR_RX_SYNC_OUTPUT_IOPIN] as output + oe_mask = usrp_prims.bmFR_RX_SYNC_OUTPUT_IOPIN + self.usrp_master._u._write_oe(0,oe,oe_mask) + + def sync_usrps(self, evt): + self.sync() + + def sync(self): + result=False + result = self.usrp_master._u._write_fpga_reg_masked (usrp_prims.FR_RX_MASTER_SLAVE, usrp_prims.bmFR_RX_SYNC, usrp_prims.bmFR_RX_SYNC ) + #There should be a small delay here, but the time it takes to get the sync to the usrp is long enough + #turn sync pulse off + result = result & self.usrp_master._u._write_fpga_reg_masked (usrp_prims.FR_RX_MASTER_SLAVE,0 ,usrp_prims.bmFR_RX_SYNC); + return result; + + def nullsink_counters(self): + nullsinkm=gr.null_sink(gr.sizeof_gr_complex) + nullsinks=gr.null_sink(gr.sizeof_gr_complex) + self.fg.connect((self.master_source,0),nullsinkm) + self.fg.connect((self.slave_source,0),nullsinks) + + + def print_db_info(self): + print "MASTER RX d'board %s" % (self.subdev_mAr.side_and_name(),) + print "MASTER RX d'board %s" % (self.subdev_mBr.side_and_name(),) + #print "TX d'board %s" % (self.subdev_At.side_and_name(),) + #print "TX d'board %s" % (self.subdev_Bt.side_and_name(),) + print "SLAVE RX d'board %s" % (self.subdev_sAr.side_and_name(),) + print "SLAVE RX d'board %s" % (self.subdev_sBr.side_and_name(),) + #print "TX d'board %s" % (self.subdev_At.side_and_name(),) + #print "TX d'board %s" % (self.subdev_Bt.side_and_name(),) + + def tune_all_rx(self,target_freq): + result = True + r1 = usrp.tune(self.usrp_master, 0, self.subdev_mAr, target_freq) + if r1 is None: + result=False + r2 = usrp.tune(self.usrp_master, 1, self.subdev_mBr, target_freq) + if r2 is None: + result=False + r3 = usrp.tune(self.usrp_slave, 0, self.subdev_sAr, target_freq) + if r3 is None: + result=False + r4 = usrp.tune(self.usrp_slave, 1, self.subdev_sBr, target_freq) + if r4 is None: + result=False + return result,r1,r2,r3,r4 + + def set_gain_all_rx(self, gain): + self.subdev_mAr.set_gain(gain) + self.subdev_mBr.set_gain(gain) + self.subdev_sAr.set_gain(gain) + self.subdev_sBr.set_gain(gain) |