diff options
Diffstat (limited to 'gnuradio-examples')
102 files changed, 15353 insertions, 0 deletions
diff --git a/gnuradio-examples/AUTHORS b/gnuradio-examples/AUTHORS new file mode 100644 index 000000000..6bd41174b --- /dev/null +++ b/gnuradio-examples/AUTHORS @@ -0,0 +1,2 @@ +Eric Blossom <eb@comsec.com> +Matt Ettus <matt@ettus.com> diff --git a/gnuradio-examples/ChangeLog b/gnuradio-examples/ChangeLog new file mode 100644 index 000000000..e2c630eb3 --- /dev/null +++ b/gnuradio-examples/ChangeLog @@ -0,0 +1,381 @@ +2006-06-20 Eric Blossom <eb@comsec.com> + + * python/gmsk2/receive_path.py, python/gmsk2/transmit_path.py, + benchmark_gmsk_{tx,rx}.py: refactored to use modulation + independent pkt.py code. Lays the foundation for a single set of + tools that can exercise all modulations. More work required to + meet that goal, but we're closing in. + + * python/gmsk2/tunnel.py: refactored version of + tunnel_ip_null_mac. This creates a virtual ethernet inteface + using the TAP driver, and then tunnels packets across the air + using GMSK and a simple carrier sense MAC. Fixed problem with + truncation of large packets which was leading to stalls. + + * python/gmsk2/tunnel_ip_null_mac.py: removed. Replaced with + tunnel.py + +2006-06-15 Eric Blossom <eb@comsec.com> + + * python/usrp/usrp_fft.py (app_flow_graph.set_decim): added + missing return value. + + * python/usrp/usrp_oscope.py, python/usrp/usrp_fft.py: you can now + change the decimation factor in the GUI. + +2006-06-06 Eric Blossom <eb@comsec.com> + + * python/gmsk2/mpsk.py: Lots of rework. QPSK is now + working with mpsk_test.py. Need to confirm it on the air, and then + make BPSK work again. + + +2006-05-30 Eric Blossom <eb@comsec.com> + + Standardized on mpsk as the prefix. Renamed a bunch of stuff to + restore sanity: + * python/gmsk2/benchmark_xpsk_rx.py -> python/gmsk2/benchmark_mpsk_rx.py + * python/gmsk2/benchmark_xpsk_tx.py -> python/gmsk2/benchmark_mpsk_tx.py + * python/gmsk2/psk_receive_path.py -> python/gmsk2/mpsk_receive_path.py + * python/gmsk2/psk_transmit_path.py -> python/gmsk2/mpsk_transmit_path.py + * python/gmsk2/xpsk.py -> python/gmsk2/mpsk.py + * python/gmsk2/xpsk_pkt.py -> python/gmsk2/mpsk_pkt.py + * python/gmsk2/xpsk_test.py -> python/gmsk2/mpsk_test.py + +2006-05-29 Eric Blossom <eb@comsec.com> + + * python/gmsk2/qpsk_tester.py, python/gmsk2/qpsk_usrp_tester.py: + new. Test benches for qpsk code. Thanks Tom! + +2005-12-19 Martin Dvh <gnuradiomail@olifantasia.com> + + updated multi_usrp examples which should now work 'out-of-the-box' with current cvs + (you need updated usrp,gr-usrp, gnuradio-core and gnuradio-examples) + + * configure.ac: added python/usrp_multi/Makefile + * python/Makefile.am: added multi_usrp + * python/multi_usrp/.cvsignore: new + * python/multi_usrp/Makefile.am: new + * python/multi_usrp/README: Made this into a real readme + * python/multi_usrp/multi_usrp_oscope.py: changed to work with current usrp_multi code in cvs + * python/multi_usrp/multi_usrp_rx_cfile.py: changed to work with current usrp_multi code in cvs + * python/multi_usrp/multi_usrp.py: removed + * python/multi_usrp/multi_usrp_oscope_showcounters.py: removed + * python/multi_usrp/multi_usrp_oscope_simple.py: removed + * python/multi_usrp/multi_usrp_oscope_standalone.py: removed + * python/multi_usrp/multi_usrp_rx_tofile.py: removed + * python/multi_usrp/multi_usrp_rx_tofile_standalone.py: removed + * python/multi_usrp/multi_usrp_test_counting.py: removed + +2006-05-10 Eric Blossom <eb@comsec.com> + + * python/gmsk2/psk_transmit_path.py: add diff=False per Bob Vincent. + +2006-04-06 Eric Blossom <eb@comsec.com> + + * python/usrp/usrp_fft.py: removed explicit setting of fft_rate. + The code will now pick it up from config files. + +2006-03-12 Eric Blossom <eb@comsec.com> + + * python/usrp/usrp_siggen.py: added daughterboard support. + * python/usrp/usrp_oscope.py, python/usrp/usrp_fft.py: fixed so + frequency can be set even after an invalid frequency is passed on + the command line. + +2006-03-10 Eric Blossom <eb@comsec.com> + + * python/gmsk2/xpsk.py: doc fixes. + +2006-03-08 Eric Blossom <eb@comsec.com> + + * python/usrp/fm_tx_2_daughterboards.py: example that transmits + different signals out two daughterboards. + +2006-02-02 Eric Blossom <eb@comsec.com> + + * python/usrp/usrp_oscope.py: support manual scaling of + Y-axis. Smarter defaulting of rx_subdev_spec if user doesn't pick. + * python/usrp/usrp_fft.py: Smarter defaulting of rx_subdev_spec if user doesn't pick. + +2006-02-01 Eric Blossom <eb@comsec.com> + + * python/gmsk2/transmit_path.py: fixed so it works on both A and B sides. + +2006-01-30 Eric Blossom <eb@comsec.com> + + * python/gmsk2/receive_path.py (receive_path.set_auto_tr): added + set_auto_tr to the receive path to have it work properly with + changes in auto t/r switching semantics. + + * python/usrp/fm_tx4.py (fm_tx_graph.__init__): changes to have it + work in both A and B sides. + +2005-12-19 Martin Dvh <gnuradiomail@olifantasia.com> + + * multi_usrp/README, multi_usrp/multi_usrp.py, multi_usrp/multi_usrp_oscope.py, + multi_usrp/multi_usrp_oscope_showcounters.py, multi_usrp/multi_usrp_oscope_simple.py, + multi_usrp/multi_usrp_oscope_standalone.py, multi_usrp/multi_usrp_rx_tofile.py, + multi_usrp/multi_usrp_rx_tofile_standalone.py, multi_usrp/multi_usrp_test_counting.py: + new directory and examples to exercise multiple usrps coherently + connected together. These require additional code that is not yet + in the repository. + +2005-12-13 Eric Blossom <eb@comsec.com> + + * python/usrp/usrp_wfm_rcv2_nogui.py: new: receive two FM stations + simultaneously. + +2005-12-09 Eric Blossom <eb@comsec.com> + + * python/gmsk2/tunnel_ip_null_mac.py: cleaned up. gathered mac + functions together. + + * python/usrp/usrp_wfm_rcv_pll.py (pick_subdevice), + python/usrp/usrp_wfm_rcv_nogui.py (pick_subdevice): use library version. + * python/usrp/fm_tx4.py: explicity call set_enable(True) to turn + on transmitter (required due to policy change). Also removed + pick_tx_subdevice. Now use version in usrp library. + +2005-12-09 Eric Blossom <eb@comsec.com> + + Made release 2.6 + +2005-12-08 Eric Blossom <eb@comsec.com> + + * python/gmsk/rx.py, python/gmsk/rx2.py, python/gmsk/tx.py, + python/gmsk/tx2.py, python/gmsk/xcvr.py, python/gmsk/xcvr2.py, + ggmsk-test.py, gmsk-test.py: removed. Use the code in ../gmsk2 instead. + + * python/gmsk2/receive_path.py, python/gmsk2/transmit_path.py: + New. Extracted hierarchical blocks from benchmark_gmsk_{tx,rx}.py + +2005-12-06 Eric Blossom <eb@comsec.com> + + * python/gmsk2/benchmark_gmsk_tx.py, + python/gmsk2/benchmark_gmsk_rx.py: now support "--bitrate" command + line arg. + * python/gmsk2/pick_bitrate.py: given a bitrate, determine + samples/baud and interp/decim ratio. + +2005-11-11 Eric Blossom <eb@comsec.com> + + * python/usrp/usrp_nbfm_ptt.py, python/usrp/usrp_wfm_rcv.py: moved + pick_xx_subdev to library. + +2005-11-01 Eric Blossom <eb@comsec.com> + + * python/usrp/dbs_nbfm.py: removed. Use usrp_nbfm_rcv.py instead. + +2005-10-24 Eric Blossom <eb@comsec.com> + + * python/usrp/usrp_oscope.py: basically identical to usrp_fft.py, + but uses oscope sink. + + * python/usrp/usrp_wfm_rcv_nogui.py, python/usr/usrp_wfm_rcv.py: + increased usrp decimation factor to 200 reduce host cpu + requirements. With the half-band in the FPGA, good decimation + factors are even values in [8,256]. + +2005-10-14 Eric Blossom <eb@comsec.com> + + * python/usrp/usrp_fft.py: gui mods. + * python/usrp/usrp_wfm_rcv.py: reworked gui. + +2005-10-11 Eric Blossom <eb@comsec.com> + + * python/usrp/usrp_waterfall.py: removed. Use usrp_fft.py -W instead. + +2005-10-10 Eric Blossom <eb@comsec.com> + + * python/audio/audio_copy.py, python/audio/audio_play.py, + python/audio/audio_to_file.py, python/audio/dial_tone.py, + python/audio/mono_tone.py, python/audio/multi_tone.py, + python/audio/spectrum_inversion.py: revised to use standard + command line args and canonical structure. + +2005-09-21 Eric Blossom <eb@comsec.com> + + * python/usrp/usrp_fft.py: extensively reworked to use new + daughterboard auto-instantiation framework. + +2005-07-30 Martin Dvh <gnuradiomail@olifantasia.com> + + * python/usrp/tvrx_tv_rcv.py: PAL/NTSC TV demod. Work in progress. + +2005-07-21 Eric Blossom <eb@comsec.com> + + * python/usrp/benchmark_usb.py, python/usrp/usrp_rx_cfile.py, + python/usrp/usrp_oscope.py, python/usrp/dbs_debug: doc fix. + +2005-07-19 Eric Blossom <eb@comsec.com> + + * python/usrp/ayfabtu.py: new. Transmit on 25 frequencies simultaneously! + +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: mods for x86_64, $(NO_UNDEFINED) + + * python/usrp/usrp_waterfall.py, python/usrp/dbs_debug.py, + python/usrp/flexrf_debug.py, python/usrp/flexrf_siggen.py, + python/usrp/usrp_fft.py, python/usrp/usrp_oscope.py, + python/usrp/usrp_rx_cfile.py, python/usrp/usrp_rx_file.py, + python/usrp/usrp_siggen.py, python/usrp/usrp_waterfall.py: + gru.hexint fix for python 2.4. + +2005-06-17 Eric Blossom <eb@comsec.com> + + * python/apps/README: new directory for applications. + +2005-06-16 Eric Blossom <eb@comsec.com> + + * python/usrp/tvrx_wfm_rcv_gui.py: add -d audio_device option; fix + volume increment so up == 1.0/down; fix default range of bottom FFT. + +2005-06-13 Eric Blossom <eb@comsec.com> + + * python/usrp/tvrx_wfm_rcv_gui.py: removed limit on what frequency + can be set. + +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-04-27 Eric Blossom <eb@comsec.com> + + * python/digital_voice/encdec.py: simulate gsm voice encode / + gmsk mod / gmsk demod / gsm voice decode. + +2005-03-29 Eric Blossom <eb@comsec.com> + + * python/gmsk/ggmsk-test.py, python/gmsk/gmsk-test.py: new GMSK + test code from Joshua Lackey <jl@noether.uoregon.edu> + + * python/gmsk/tx.py, python/gmsk/rx.py: new tx and rx modules that + depending on command line options do either FSK or GMSK and use + either files, sockets, named pipes or a USRP for their inputs and + outputs. From Bob Vincent <bvincent@bbn.com> + +2005-03-29 Eric Blossom <eb@comsec.com> + + * python/audio/audio_copy.py, python/audio/multi_tone.py, + python/audio/mono_tone.py: replace ALSA specific default argument + with "" + +2005-03-04 Eric Blossom <eb@comsec.com> + + * python/usrp/nbfm_ptt_quick_and_dirty.py: simple NBFM + push-to-talk transceiver. (Works, but has a latency problem that + needs to be fixed) + +2005-02-28 Eric Blossom <eb@comsec.com> + + * python/usrp/fsk_tx.py: reduced gain, added -N (no gui) flag. + * python/usrp/fsk_rx.py: added -N (no gui) flag. + Running Tx and Rx on same machine (1.4 GHz Pentium M) 100kb/sec + burns up about 70% of CPU with -N options on both. + +2005-02-25 Eric Blossom <eb@comsec.com> + + Removed usrp0 directory. + Moved contents of usrp1 directory into usrp directory. + +2005-02-22 Eric Blossom <eb@comsec.com> + + * python/usrp1/nbfm_rcv.py: added squelch. + +2004-10-13 Eric Blossom <eb@comsec.com> + + * configure.ac: upped rev to 0.2cvs + +2004-10-11 Eric Blossom <eb@comsec.com> + + * Makefile.am (EXTRA_DIST): added config.h.in + +2004-10-07 Chuck Swiger <cswiger@widomaker.com> + + * python/audio/audio_to_file.py: coerce audio sample rate to int. + +2004-10-07 Eric Blossom <eb@comsec.com> + + * python/usrp1/wfm_rcv.py: new. Single channel broadcast FM + receiver for usrp1 + microtune 4937. + +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-09-18 Eric Blossom <eb@comsec.com> + + These changes happened somewhere around DCC + + * python/usrp/usrp_fft.py: new + * python/usrp1/dsb_tx.py: new double sideband AM transmitter + * python/usrp1/fm_tx4.py: simultaneous 4 channel narrow band FM transmitter + * python/usrp1/fsk_rx.py: FSK Rx module. Needs some work. + * python/usrp1/fsk_tx.py: FSK Tx module. Needs some work. + +2004-08-19 Eric Blossom <eb@comsec.com> + + * python/usrp/usrp_rx_file.py (main): added command line parsing. + * python/usrp/usrp_siggen.py: added command line parsing. + + * python/usrp1/test_digital_loopback_lfsr.py, + python/usrp1/test_digital_loopback_counting.py, + python/usrp1/test_digital_counting.py: new. Take advantage of + scheduler fix to implement both the tx and rx path's in the same + flow graph. + + +2004-07-31 Eric Blossom <eb@comsec.com> + + * python/usrp0, python/usrp1: new directories for rev specific examples. + * python/usrp1/loopback.py (main): new. + +2004-07-30 Eric Blossom <eb@comsec.com> + + * python/usrp/usrp_siggen.py: now directly queries hardware for dac freq. + * python/usrp/usrp_rx_file.py: now directly queries hardware for dac freq. + +2004-07-17 Eric Blossom <eb@comsec.com> + + * python/mc4020/fm_demod.py: updated to use new mc4020 block, formatting + +2004-07-12 Eric Blossom <eb@comsec.com> + + * configure.ac: upped rev to 0.1cvs + +2004-07-03 Eric Blossom <eb@comsec.com> + + * python/usrp/usrp_siggen.py: Yeah! The first demo program that + reconfigures the flow graph on the fly! + +# +# 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. +# diff --git a/gnuradio-examples/Makefile.am b/gnuradio-examples/Makefile.am new file mode 100644 index 000000000..761113847 --- /dev/null +++ b/gnuradio-examples/Makefile.am @@ -0,0 +1,28 @@ +# +# 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.hacking +SUBDIRS = python + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = diff --git a/gnuradio-examples/NEWS b/gnuradio-examples/NEWS new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/gnuradio-examples/NEWS diff --git a/gnuradio-examples/README b/gnuradio-examples/README new file mode 100644 index 000000000..89515bb3c --- /dev/null +++ b/gnuradio-examples/README @@ -0,0 +1,68 @@ +# +# 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. +# + +Welcome! + +This package is a collection of examples for the GNU Radio 2.x toolkit. + +They assume that at a minimum, you've got the gnuradio-core +package installed. In addition, depending on what i/o devices you +have, you may want to consider installing: + + gr-usrp : support for Universal Software Radio Peripheral (Rev 1) + gr-audio-oss : sound card support using OSS + gr-audio-alsa : sound card support using ALSA + gr-audio-osx : sound card support for OS/X + gr-mc4020 : Measurement Computing PCI-DAS 4020/12 A/D card + + +You'll need to set your PYTHONPATH environment variable so that the +GNU Radio toolkit and optional packages can be found by python. + +PYTHONPATH should include the path of the local site-packages +directory. It has the general form: + + ${prefix}/lib/python${pythonversion}/site-packages + +If the above packages were installed using the default prefix +(/usr/local) and you're using python 2.3, this should work: + + $ export PYTHONPATH=/usr/local/lib/python2.3/site-packages + +You may want to add this to your ~/.bash_profile or similar file. + + +Once PYTHONPATH is set, you should be able to run any of the examples +for which you have the required i/o devices. + +To ensure that your setup is sane, try this: + + $ python + >>> from gnuradio import gr + +If this works, your PYTHONPATH is set correctly. + + +Have fun! + +The GNU Radio team + + diff --git a/gnuradio-examples/README.hacking b/gnuradio-examples/README.hacking new file mode 100644 index 000000000..44d424741 --- /dev/null +++ b/gnuradio-examples/README.hacking @@ -0,0 +1,39 @@ +* Standard command line options + +When building applications or examples, if your command has any of +these options, please use the following short and long names: + + + + # General + + parser = OptionParser(option_class=eng_option) + + parser.add_option("-f", "--freq", type="eng_float", default=None, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=None, + help="set gain in dB (default is midpoint)") + parser.add_option("-F", "--filename", type="string", default=None) + + # Audio devices + + parser.add_option("-I", "--audio-input", type=string, default="", + help="pcm input device name. E.g., hw:0,0 or /dev/dsp") + parser.add_option("-O", "--audio-output", type=string, default="", + help="pcm output device name. E.g., hw:0,0 or /dev/dsp") + parser.add_option("-r", "--sample-rate", type="eng_float", default=48000, + help="set sample rate to RATE (48000)") + + # USRP specific + + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=(0, 0), + help="select USRP Rx side A or B (default=A)") + parser.add_option("-T", "--tx-subdev-spec", type="subdev", default=(0, 0), + help="select USRP Tx side A or B (default=A)") + parser.add_option("-d", "--decim", type="int", default=16, + help="set fgpa decimation rate to DECIM (default=16)") + parser.add_option("-i", "--interp", type="int", default=32, + help="set fgpa interpolation rate to INTERP (default=32)") + parser.add_option("-n", "--nchannels", type=intx, default=1, + help="set number of channels to NCHANNELS (default=1)") + diff --git a/gnuradio-examples/python/Makefile.am b/gnuradio-examples/python/Makefile.am new file mode 100644 index 000000000..73b7921b2 --- /dev/null +++ b/gnuradio-examples/python/Makefile.am @@ -0,0 +1,22 @@ +# +# 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. +# + +SUBDIRS = audio mc4020 usrp gmsk2 digital_voice multi_usrp diff --git a/gnuradio-examples/python/apps/README b/gnuradio-examples/python/apps/README new file mode 100644 index 000000000..a3f041bfd --- /dev/null +++ b/gnuradio-examples/python/apps/README @@ -0,0 +1,26 @@ +# +# 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. +# + +This directory servers as the parent directory for various and sundry +applications such as scanners, HF radios, etc. Each subdirectory +shall have a README file that includes a short description of what the +application does, and a list of hardware dependencies. E.g., requires +a USRP with an xyz daughterboard, connected to a footronics magic box. diff --git a/gnuradio-examples/python/apps/hf_explorer/README b/gnuradio-examples/python/apps/hf_explorer/README new file mode 100644 index 000000000..5f780b3d9 --- /dev/null +++ b/gnuradio-examples/python/apps/hf_explorer/README @@ -0,0 +1,48 @@ +hfx.py is meant to be a full-featured Long Wave / Medium Wave +and Short Wave (250kHz to 30Mhz) AM and Single Sideband receiver. +It uses the USRP with a Basic RX board, and will need an +antenna and some preamps, about 30db gain will work. See the +'Help' menu or hfx_help for more info. + +---------------------------------------------------------- + +hfx2.py is a major revision built about complex fir filter +coeffecients ability and cleaner python script. Inherits +most features from hfx.py - Powermate knob supported but +not required, tooltip frequency display, single click +tuning, AGC, record to disk, play from disk and record audio. +New feature is ability to tailor the audio passband with two +sliders over the spectrum display. The sliders almost align +with the actual frequency. Preset filter settings for LSB +(-3000 to 0kHz), USB (0 to +3000kHz), CW (-400 to -800Hz) +and AM (-5kHz from carrier to +5kHz). + +AM now switches in a synchronous PLL detector with the +carriers at 7.5kHz. The PLL carrier is displayed in the +bottom display and helps show where on the upper spectrum +the demodulated signal lies. Everything gets shifted up +7.5kHz in AM, center frequency, tooltips, etc. The target +AM carrier needs to be closely tuned in, it will have a +hollow sound untill it is locked, and then the PLL carrier +in the bottom display will jump up and remain relatively +constant. There is a slider "AM sync carrier" to play with +different levels to mix with the signal for demodulation. +The filter in AM is preset to 2500/12500 (7.5kHz +/- 5kHz) +and is handy for removing adjacent channel interference. +Change AM_SYNC_DISPLAY in script for whether to show AM +Sync carrier or not. +Run with "-h" for command line help with setting USRP +ddc center frequency, decimation, rf data record, playback +and audio data recording. + +There are some controls for controlling a varactor and +tuning an antenna - just ignore them unless you want +to build a voltage tuned antenna to track frequency. + +There is also code for Web based control of frequency and +volume - so I can tune the radio with an Ipaq from bed. +Disabled by default - it takes a web server, some +directories and scripts to use. + + + diff --git a/gnuradio-examples/python/apps/hf_explorer/hfx2.py b/gnuradio-examples/python/apps/hf_explorer/hfx2.py new file mode 100755 index 000000000..c09f962cf --- /dev/null +++ b/gnuradio-examples/python/apps/hf_explorer/hfx2.py @@ -0,0 +1,786 @@ +#!/usr/bin/env python +# -*- coding: ANSI_X3.4-1968 -*- +# generated by wxGlade 0.4 on Tue Mar 14 10:16:06 2006 +# +# Copyright 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. +# +#----------------------------------------------------------------- +# +# +-->(fft) +# | +# (src)->(xlate)--+->(audio filter)--+-->(sel_am)-+--------------+ +# | | | +# | (pll) | +# | | | +# | (pll_carrier_scale) | +# | | | +# | (pll_carrier_filter) | +# | | | +# | +--(fft2) | +# | | | +# | +--(c2f3)--+ | +# | | | | +# | (phaser1) (phaser2) | +# | | | | +# | +--(f2c)---+ | +# | | V +# V +---------->(am_det) +# (c2f) | +# | (c2f2) +# | | +# +-->(sel_sb)------------>(combine) +# | +# V +# +--------------------------(scale) +# | | +# | | +# | +++ +# V | | +# (agc)<--(offset)<--(intr)<---(sqr1) +# | +# V +# (dst) +# +#---------------------------------------------------------------------- +# +# Versions 2.2.1 adds loop antenna automatic tuner +# +# 2.3.1 adds web control, made AM Sync display optional, +# added more comments. +# +# 2.4.1 updates usrp interface to support auto subdev + +# Web server control disabled by default. Do not enable +# until directory structure and scripts are in place. +WEB_CONTROL = False + +# Controls display of AM Sync Carrier - turn off for smaller +# window if not needed +AM_SYNC_DISPLAY = False + +import os, wx, sys, math +import wx.lib.evtmgr as em +from gnuradio.wxgui import powermate, fftsink +from gnuradio import gr, audio, eng_notation, usrp, gru +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +ID_BUTTON_1 = wx.NewId() # LSB button +ID_BUTTON_2 = wx.NewId() # USB +ID_BUTTON_3 = wx.NewId() # AM +ID_BUTTON_4 = wx.NewId() # CW +ID_BUTTON_5 = wx.NewId() # Powermate controls: Upper audio freq cutoff +ID_BUTTON_6 = wx.NewId() # " Lower audio freq cutoff +ID_BUTTON_7 = wx.NewId() # " Frequency +ID_BUTTON_8 = wx.NewId() # " Volume +ID_BUTTON_9 = wx.NewId() # " Time +ID_BUTTON_10 = wx.NewId() # Time Seek Forwards +ID_BUTTON_11 = wx.NewId() # Time Seek Backwards +ID_BUTTON_12 = wx.NewId() # Automatic Antenna Tune (AT) enable +ID_BUTTON_13 = wx.NewId() # AT Calibrate point +ID_BUTTON_14 = wx.NewId() # AT Reset +ID_TEXT_1 = wx.NewId() # Band Center, USRP ddc Freq +ID_SPIN_1 = wx.NewId() # Frequency display and control +ID_SLIDER_1 = wx.NewId() # Upper audio freq cutoff +ID_SLIDER_2 = wx.NewId() # Lower audio freq cutoff +ID_SLIDER_3 = wx.NewId() # Frequency +ID_SLIDER_4 = wx.NewId() # Volume +ID_SLIDER_5 = wx.NewId() # Programmable Gain Amp, PGA, RF gain +ID_SLIDER_6 = wx.NewId() # AM Sync carrier level +ID_SLIDER_7 = wx.NewId() # AT control voltage output +ID_EXIT = wx.NewId() # Menu Exit + + +def pick_subdevice(u): + """ + The user didn't specify a subdevice on the command line. + If there's a daughterboard on A, select A. + If there's a daughterboard on B, select B. + Otherwise, select A. + """ + if u.db[0][0].dbid() >= 0: # dbid is < 0 if there's no d'board or a problem + return (0, 0) + if u.db[1][0].dbid() >= 0: + return (1, 0) + return (0, 0) + + +class MyFrame(wx.Frame): + def __init__(self, *args, **kwds): + # begin wxGlade: MyFrame.__init__ + kwds["style"] = wx.DEFAULT_FRAME_STYLE + wx.Frame.__init__(self, *args, **kwds) + + # Menu Bar + self.frame_1_menubar = wx.MenuBar() + self.SetMenuBar(self.frame_1_menubar) + wxglade_tmp_menu = wx.Menu() + self.Exit = wx.MenuItem(wxglade_tmp_menu, ID_EXIT, "Exit", "Exit", wx.ITEM_NORMAL) + wxglade_tmp_menu.AppendItem(self.Exit) + self.frame_1_menubar.Append(wxglade_tmp_menu, "File") + # Menu Bar end + self.panel_1 = wx.Panel(self, -1) + self.button_1 = wx.Button(self, ID_BUTTON_1, "LSB") + self.button_2 = wx.Button(self, ID_BUTTON_2, "USB") + self.button_3 = wx.Button(self, ID_BUTTON_3, "AM") + self.button_4 = wx.Button(self, ID_BUTTON_4, "CW") + self.button_5 = wx.ToggleButton(self, ID_BUTTON_5, "Upper") + self.slider_1 = wx.Slider(self, ID_SLIDER_1, 0, -15799, 15799, style=wx.SL_HORIZONTAL|wx.SL_LABELS) + self.button_6 = wx.ToggleButton(self, ID_BUTTON_6, "Lower") + self.slider_2 = wx.Slider(self, ID_SLIDER_2, 0, -15799, 15799, style=wx.SL_HORIZONTAL|wx.SL_LABELS) + self.panel_5 = wx.Panel(self, -1) + self.label_1 = wx.StaticText(self, -1, " Band\nCenter") + self.text_ctrl_1 = wx.TextCtrl(self, ID_TEXT_1, "") + self.panel_6 = wx.Panel(self, -1) + self.panel_7 = wx.Panel(self, -1) + self.panel_2 = wx.Panel(self, -1) + self.button_7 = wx.ToggleButton(self, ID_BUTTON_7, "Freq") + self.slider_3 = wx.Slider(self, ID_SLIDER_3, 3000, 0, 6000) + self.spin_ctrl_1 = wx.SpinCtrl(self, ID_SPIN_1, "", min=0, max=100) + self.button_8 = wx.ToggleButton(self, ID_BUTTON_8, "Vol") + self.slider_4 = wx.Slider(self, ID_SLIDER_4, 0, 0, 500) + self.slider_5 = wx.Slider(self, ID_SLIDER_5, 0, 0, 20) + self.button_9 = wx.ToggleButton(self, ID_BUTTON_9, "Time") + self.button_11 = wx.Button(self, ID_BUTTON_11, "Rew") + self.button_10 = wx.Button(self, ID_BUTTON_10, "Fwd") + self.panel_3 = wx.Panel(self, -1) + self.label_2 = wx.StaticText(self, -1, "PGA ") + self.panel_4 = wx.Panel(self, -1) + self.panel_8 = wx.Panel(self, -1) + self.panel_9 = wx.Panel(self, -1) + self.label_3 = wx.StaticText(self, -1, "AM Sync\nCarrier") + self.slider_6 = wx.Slider(self, ID_SLIDER_6, 50, 0, 200, style=wx.SL_HORIZONTAL|wx.SL_LABELS) + self.label_4 = wx.StaticText(self, -1, "Antenna Tune") + self.slider_7 = wx.Slider(self, ID_SLIDER_7, 1575, 950, 2200, style=wx.SL_HORIZONTAL|wx.SL_LABELS) + self.panel_10 = wx.Panel(self, -1) + self.button_12 = wx.ToggleButton(self, ID_BUTTON_12, "Auto Tune") + self.button_13 = wx.Button(self, ID_BUTTON_13, "Calibrate") + self.button_14 = wx.Button(self, ID_BUTTON_14, "Reset") + self.panel_11 = wx.Panel(self, -1) + self.panel_12 = wx.Panel(self, -1) + + self.__set_properties() + self.__do_layout() + # end wxGlade + + parser = OptionParser (option_class=eng_option) + parser.add_option ("-c", "--ddc-freq", type="eng_float", default=3.9e6, + help="set Rx DDC frequency to FREQ", metavar="FREQ") + parser.add_option ("-a", "--audio_file", default="", + help="audio output file", metavar="FILE") + parser.add_option ("-r", "--radio_file", default="", + help="radio output file", metavar="FILE") + parser.add_option ("-i", "--input_file", default="", + help="radio input file", metavar="FILE") + parser.add_option ("-d", "--decim", type="int", default=250, + help="USRP decimation") + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, help="select USRP Rx side A or B (default=first one with a daughterboard)") + (options, args) = parser.parse_args () + + self.usrp_center = options.ddc_freq + usb_rate = 64e6 / options.decim + self.slider_range = usb_rate * 0.9375 + self.f_lo = self.usrp_center - (self.slider_range/2) + self.f_hi = self.usrp_center + (self.slider_range/2) + self.af_sample_rate = 32000 + fir_decim = long (usb_rate / self.af_sample_rate) + + # data point arrays for antenna tuner + self.xdata = [] + self.ydata = [] + + self.fg = gr.flow_graph() + + # radio variables, initial conditions + self.frequency = self.usrp_center + # these map the frequency slider (0-6000) to the actual range + self.f_slider_offset = self.f_lo + self.f_slider_scale = 10000/options.decim + self.spin_ctrl_1.SetRange(self.f_lo,self.f_hi) + self.text_ctrl_1.SetValue(str(int(self.usrp_center))) + self.slider_5.SetValue(0) + self.AM_mode = False + + self.slider_3.SetValue((self.frequency-self.f_slider_offset)/self.f_slider_scale) + self.spin_ctrl_1.SetValue(int(self.frequency)) + + POWERMATE = True + try: + self.pm = powermate.powermate(self) + except: + sys.stderr.write("Unable to find PowerMate or Contour Shuttle\n") + POWERMATE = False + + if POWERMATE: + powermate.EVT_POWERMATE_ROTATE(self, self.on_rotate) + powermate.EVT_POWERMATE_BUTTON(self, self.on_pmButton) + self.active_button = 7 + + # command line options + if options.audio_file == "": SAVE_AUDIO_TO_FILE = False + else: SAVE_AUDIO_TO_FILE = True + if options.radio_file == "": SAVE_RADIO_TO_FILE = False + else: SAVE_RADIO_TO_FILE = True + if options.input_file == "": self.PLAY_FROM_USRP = True + else: self.PLAY_FROM_USRP = False + + if self.PLAY_FROM_USRP: + self.src = usrp.source_c(decim_rate=options.decim) + if options.rx_subdev_spec is None: + options.rx_subdev_spec = pick_subdevice(self.src) + self.src.set_mux(usrp.determine_rx_mux_value(self.src, options.rx_subdev_spec)) + self.subdev = usrp.selected_subdev(self.src, options.rx_subdev_spec) + self.src.tune(0, self.subdev, self.usrp_center) + self.tune_offset = 0 # -self.usrp_center - self.src.rx_freq(0) + + else: + self.src = gr.file_source (gr.sizeof_gr_complex,options.input_file) + self.tune_offset = 2200 # 2200 works for 3.5-4Mhz band + + # save radio data to a file + if SAVE_RADIO_TO_FILE: + file = gr.file_sink(gr.sizeof_gr_complex, options.radio_file) + self.fg.connect (self.src, file) + + # 2nd DDC + xlate_taps = gr.firdes.low_pass ( \ + 1.0, usb_rate, 16e3, 4e3, gr.firdes.WIN_HAMMING ) + self.xlate = gr.freq_xlating_fir_filter_ccf ( \ + fir_decim, xlate_taps, self.tune_offset, usb_rate ) + + # Complex Audio filter + audio_coeffs = gr.firdes.complex_band_pass ( + 1.0, # gain + self.af_sample_rate, # sample rate + -3000, # low cutoff + 0, # high cutoff + 100, # transition + gr.firdes.WIN_HAMMING) # window + self.slider_1.SetValue(0) + self.slider_2.SetValue(-3000) + + self.audio_filter = gr.fir_filter_ccc ( 1, audio_coeffs) + + # Main +/- 16Khz spectrum display + self.fft = fftsink.fft_sink_c (self.fg, self.panel_2, fft_size=512, sample_rate=self.af_sample_rate, average=True, size=(640,240)) + + # AM Sync carrier + if AM_SYNC_DISPLAY: + self.fft2 = fftsink.fft_sink_c (self.fg, self.panel_9, y_per_div=20, fft_size=512, sample_rate=self.af_sample_rate, average=True, size=(640,240)) + + c2f = gr.complex_to_float() + + # AM branch + self.sel_am = gr.multiply_const_cc(0) + # the following frequencies turn out to be in radians/sample + # gr.pll_refout_cc(alpha,beta,min_freq,max_freq) + # suggested alpha = X, beta = .25 * X * X + pll = gr.pll_refout_cc(.5,.0625,(2.*math.pi*7.5e3/self.af_sample_rate),(2.*math.pi*6.5e3/self.af_sample_rate)) + self.pll_carrier_scale = gr.multiply_const_cc(complex(10,0)) + am_det = gr.multiply_cc() + # these are for converting +7.5kHz to -7.5kHz + # for some reason gr.conjugate_cc() adds noise ?? + c2f2 = gr.complex_to_float() + c2f3 = gr.complex_to_float() + f2c = gr.float_to_complex() + phaser1 = gr.multiply_const_ff(1) + phaser2 = gr.multiply_const_ff(-1) + + # filter for pll generated carrier + pll_carrier_coeffs = gr.firdes.complex_band_pass ( + 2.0, # gain + self.af_sample_rate, # sample rate + 7400, # low cutoff + 7600, # high cutoff + 100, # transition + gr.firdes.WIN_HAMMING) # window + + self.pll_carrier_filter = gr.fir_filter_ccc ( 1, pll_carrier_coeffs) + + self.sel_sb = gr.multiply_const_ff(1) + combine = gr.add_ff() + + #AGC + sqr1 = gr.multiply_ff() + intr = gr.iir_filter_ffd ( [.004, 0], [0, .999] ) + offset = gr.add_const_ff(1) + agc = gr.divide_ff() + + + self.scale = gr.multiply_const_ff(0.00001) + dst = audio.sink(long(self.af_sample_rate)) + + self.fg.connect(self.src,self.xlate,self.fft) + self.fg.connect(self.xlate,self.audio_filter,self.sel_am,(am_det,0)) + self.fg.connect(self.sel_am,pll,self.pll_carrier_scale,self.pll_carrier_filter,c2f3) + self.fg.connect((c2f3,0),phaser1,(f2c,0)) + self.fg.connect((c2f3,1),phaser2,(f2c,1)) + self.fg.connect(f2c,(am_det,1)) + self.fg.connect(am_det,c2f2,(combine,0)) + self.fg.connect(self.audio_filter,c2f,self.sel_sb,(combine,1)) + if AM_SYNC_DISPLAY: + self.fg.connect(self.pll_carrier_filter,self.fft2) + self.fg.connect(combine,self.scale) + self.fg.connect(self.scale,(sqr1,0)) + self.fg.connect(self.scale,(sqr1,1)) + self.fg.connect(sqr1, intr, offset, (agc, 1)) + self.fg.connect(self.scale,(agc, 0)) + self.fg.connect(agc,dst) + + if SAVE_AUDIO_TO_FILE: + f_out = gr.file_sink(gr.sizeof_short,options.audio_file) + sc1 = gr.multiply_const_ff(64000) + f2s1 = gr.float_to_short() + self.fg.connect(agc,sc1,f2s1,f_out) + + self.fg.start() + + # for mouse position reporting on fft display + em.eventManager.Register(self.Mouse, wx.EVT_MOTION, self.fft.win) + # and left click to re-tune + em.eventManager.Register(self.Click, wx.EVT_LEFT_DOWN, self.fft.win) + + # start a timer to check for web commands + if WEB_CONTROL: + self.timer = UpdateTimer(self, 1000) # every 1000 mSec, 1 Sec + + + wx.EVT_BUTTON(self,ID_BUTTON_1,self.set_lsb) + wx.EVT_BUTTON(self,ID_BUTTON_2,self.set_usb) + wx.EVT_BUTTON(self,ID_BUTTON_3,self.set_am) + wx.EVT_BUTTON(self,ID_BUTTON_4,self.set_cw) + wx.EVT_BUTTON(self,ID_BUTTON_10,self.fwd) + wx.EVT_BUTTON(self,ID_BUTTON_11,self.rew) + wx.EVT_BUTTON(self, ID_BUTTON_13, self.AT_calibrate) + wx.EVT_BUTTON(self, ID_BUTTON_14, self.AT_reset) + wx.EVT_TOGGLEBUTTON(self,ID_BUTTON_5,self.on_button) + wx.EVT_TOGGLEBUTTON(self,ID_BUTTON_6,self.on_button) + wx.EVT_TOGGLEBUTTON(self,ID_BUTTON_7,self.on_button) + wx.EVT_TOGGLEBUTTON(self,ID_BUTTON_8,self.on_button) + wx.EVT_TOGGLEBUTTON(self,ID_BUTTON_9,self.on_button) + wx.EVT_SLIDER(self,ID_SLIDER_1,self.set_filter) + wx.EVT_SLIDER(self,ID_SLIDER_2,self.set_filter) + wx.EVT_SLIDER(self,ID_SLIDER_3,self.slide_tune) + wx.EVT_SLIDER(self,ID_SLIDER_4,self.set_volume) + wx.EVT_SLIDER(self,ID_SLIDER_5,self.set_pga) + wx.EVT_SLIDER(self,ID_SLIDER_6,self.am_carrier) + wx.EVT_SLIDER(self,ID_SLIDER_7,self.antenna_tune) + wx.EVT_SPINCTRL(self,ID_SPIN_1,self.spin_tune) + + wx.EVT_MENU(self, ID_EXIT, self.TimeToQuit) + + def __set_properties(self): + # begin wxGlade: MyFrame.__set_properties + self.SetTitle("HF Explorer 2") + self.slider_1.SetMinSize((450, 38)) + self.slider_2.SetMinSize((450, 38)) + self.panel_2.SetMinSize((640, 240)) + self.button_7.SetValue(1) + self.slider_3.SetMinSize((450, 19)) + self.slider_4.SetMinSize((275, 19)) + self.slider_5.SetMinSize((275, 19)) + if AM_SYNC_DISPLAY: + self.panel_9.SetMinSize((640, 240)) + self.slider_6.SetMinSize((300, 38)) + self.slider_7.SetMinSize((400, 38)) + # end wxGlade + + def __do_layout(self): + # begin wxGlade: MyFrame.__do_layout + sizer_1 = wx.BoxSizer(wx.VERTICAL) + grid_sizer_1 = wx.FlexGridSizer(11, 2, 0, 0) + sizer_7 = wx.BoxSizer(wx.HORIZONTAL) + sizer_5 = wx.BoxSizer(wx.HORIZONTAL) + sizer_4 = wx.BoxSizer(wx.HORIZONTAL) + sizer_3 = wx.BoxSizer(wx.HORIZONTAL) + sizer_6 = wx.BoxSizer(wx.VERTICAL) + sizer_2 = wx.BoxSizer(wx.HORIZONTAL) + grid_sizer_1.Add(self.panel_1, 1, wx.EXPAND, 0) + sizer_2.Add(self.button_1, 0, wx.ADJUST_MINSIZE, 0) + sizer_2.Add(self.button_2, 0, wx.ADJUST_MINSIZE, 0) + sizer_2.Add(self.button_3, 0, wx.ADJUST_MINSIZE, 0) + sizer_2.Add(self.button_4, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_1.Add(sizer_2, 1, wx.EXPAND, 0) + grid_sizer_1.Add(self.button_5, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_1.Add(self.slider_1, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ADJUST_MINSIZE, 0) + grid_sizer_1.Add(self.button_6, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_1.Add(self.slider_2, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ADJUST_MINSIZE, 0) + sizer_6.Add(self.panel_5, 1, wx.EXPAND, 0) + sizer_6.Add(self.label_1, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ADJUST_MINSIZE, 0) + sizer_6.Add(self.text_ctrl_1, 0, wx.ADJUST_MINSIZE, 0) + sizer_6.Add(self.panel_6, 1, wx.EXPAND, 0) + sizer_6.Add(self.panel_7, 1, wx.EXPAND, 0) + grid_sizer_1.Add(sizer_6, 1, wx.EXPAND, 0) + grid_sizer_1.Add(self.panel_2, 1, wx.EXPAND, 0) + grid_sizer_1.Add(self.button_7, 0, wx.ADJUST_MINSIZE, 0) + sizer_3.Add(self.slider_3, 0, wx.ADJUST_MINSIZE, 0) + sizer_3.Add(self.spin_ctrl_1, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_1.Add(sizer_3, 1, wx.EXPAND, 0) + grid_sizer_1.Add(self.button_8, 0, wx.ADJUST_MINSIZE, 0) + sizer_4.Add(self.slider_4, 0, wx.ADJUST_MINSIZE, 0) + sizer_4.Add(self.slider_5, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_1.Add(sizer_4, 1, wx.EXPAND, 0) + grid_sizer_1.Add(self.button_9, 0, wx.ADJUST_MINSIZE, 0) + sizer_5.Add(self.button_11, 0, wx.ADJUST_MINSIZE, 0) + sizer_5.Add(self.button_10, 0, wx.ADJUST_MINSIZE, 0) + sizer_5.Add(self.panel_3, 1, wx.EXPAND, 0) + sizer_5.Add(self.label_2, 0, wx.ADJUST_MINSIZE, 0) + sizer_5.Add(self.panel_4, 1, wx.EXPAND, 0) + grid_sizer_1.Add(sizer_5, 1, wx.EXPAND, 0) + grid_sizer_1.Add(self.panel_8, 1, wx.EXPAND, 0) + grid_sizer_1.Add(self.panel_9, 1, wx.EXPAND, 0) + grid_sizer_1.Add(self.label_3, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0) + grid_sizer_1.Add(self.slider_6, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_1.Add(self.label_4, 0, wx.ALIGN_BOTTOM|wx.ADJUST_MINSIZE, 0) + grid_sizer_1.Add(self.slider_7, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_1.Add(self.panel_10, 1, wx.EXPAND, 0) + sizer_7.Add(self.button_12, 0, wx.ADJUST_MINSIZE, 0) + sizer_7.Add(self.button_13, 0, wx.ADJUST_MINSIZE, 0) + sizer_7.Add(self.button_14, 0, wx.ADJUST_MINSIZE, 0) + sizer_7.Add(self.panel_11, 1, wx.EXPAND, 0) + sizer_7.Add(self.panel_12, 1, wx.EXPAND, 0) + grid_sizer_1.Add(sizer_7, 1, wx.EXPAND, 0) + sizer_1.Add(grid_sizer_1, 1, wx.EXPAND, 0) + self.SetAutoLayout(True) + self.SetSizer(sizer_1) + sizer_1.Fit(self) + sizer_1.SetSizeHints(self) + self.Layout() + # end wxGlade + + # Menu exit + def TimeToQuit(self, event): + self.fg.stop() + self.Close(True) + + # Powermate being turned + def on_rotate(self, event): + if self.active_button == 5: + self.slider_1.SetValue(self.slider_1.GetValue()+event.delta) + if self.slider_2.GetValue() > (self.slider_1.GetValue() - 200) : + self.slider_2.SetValue(self.slider_1.GetValue() - 200) + self.filter() + if self.active_button == 6: + self.slider_2.SetValue(self.slider_2.GetValue()+event.delta) + if self.slider_1.GetValue() < (self.slider_2.GetValue() + 200) : + self.slider_1.SetValue(self.slider_2.GetValue() + 200) + self.filter() + if self.active_button == 7: + new = max(0, min(6000, self.slider_3.GetValue() + event.delta)) + self.slider_3.SetValue(new) + self.frequency = (self.f_slider_scale * new) + self.f_slider_offset + self.spin_ctrl_1.SetValue(self.frequency) + if self.AM_mode == False: + self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset)) + else: + self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset - 7.5e3)) + if self.button_12.GetValue(): + self.auto_antenna_tune() + if self.active_button == 8: + new = max(0, min(500, self.slider_4.GetValue() + event.delta)) + self.slider_4.SetValue(new) + self.scale.set_k(math.pow(10.,((self.slider_4.GetValue()-500.)/100.))) + if self.active_button == 9: + if self.PLAY_FROM_USRP == False: + if event.delta == -1: + self.src.seek(-1000000,gr.SEEK_CUR) + elif event.delta == 1: + self.src.seek(1000000,gr.SEEK_CUR) + + + # Powermate pressed to switch controlled function + def on_pmButton(self, event): + if event.value == 0: + if self.active_button == 5: + self.active_button = 6 + self.button_5.SetValue(False) + self.button_6.SetValue(True) + elif self.active_button == 6: + self.active_button = 7 + self.button_6.SetValue(False) + self.button_7.SetValue(True) + elif self.active_button == 7: + self.active_button = 8 + self.button_7.SetValue(False) + self.button_8.SetValue(True) + elif self.active_button == 8: + self.active_button = 9 + self.button_8.SetValue(False) + self.button_9.SetValue(True) + elif self.active_button == 9: + self.active_button = 5 + self.button_9.SetValue(False) + self.button_5.SetValue(True) + + # Clicking one PM control button turns the rest off + def on_button(self, event): + id = event.GetId() + if id == ID_BUTTON_5: + self.active_button = 5 + self.button_6.SetValue(False) + self.button_7.SetValue(False) + self.button_8.SetValue(False) + self.button_9.SetValue(False) + if id == ID_BUTTON_6: + self.active_button = 6 + self.button_5.SetValue(False) + self.button_7.SetValue(False) + self.button_8.SetValue(False) + self.button_9.SetValue(False) + if id == ID_BUTTON_7: + self.active_button = 7 + self.button_5.SetValue(False) + self.button_6.SetValue(False) + self.button_8.SetValue(False) + self.button_9.SetValue(False) + if id == ID_BUTTON_8: + self.active_button = 8 + self.button_5.SetValue(False) + self.button_6.SetValue(False) + self.button_7.SetValue(False) + self.button_9.SetValue(False) + if id == ID_BUTTON_9: + self.active_button = 9 + self.button_5.SetValue(False) + self.button_6.SetValue(False) + self.button_7.SetValue(False) + self.button_8.SetValue(False) + + # Make sure filter settings are legal + def set_filter(self, event): + slider = event.GetId() + slider1 = self.slider_1.GetValue() + slider2 = self.slider_2.GetValue() + if slider == ID_SLIDER_1: + if slider2 > (self.slider_1.GetValue() - 200) : + self.slider_2.SetValue(slider1 - 200) + elif slider == ID_SLIDER_2: + if slider1 < (self.slider_2.GetValue() + 200) : + self.slider_1.SetValue(slider2 + 200) + self.filter() + + # Calculate taps and apply + def filter(self): + audio_coeffs = gr.firdes.complex_band_pass ( + 1.0, # gain + self.af_sample_rate, # sample rate + self.slider_2.GetValue(), # low cutoff + self.slider_1.GetValue(), # high cutoff + 100, # transition + gr.firdes.WIN_HAMMING) # window + self.audio_filter.set_taps(audio_coeffs) + + def set_lsb(self, event): + self.AM_mode = False + self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset)) + self.sel_sb.set_k(1) + self.sel_am.set_k(0) + self.slider_1.SetValue(0) + self.slider_2.SetValue(-3000) + self.filter() + + def set_usb(self, event): + self.AM_mode = False + self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset)) + self.sel_sb.set_k(1) + self.sel_am.set_k(0) + self.slider_1.SetValue(3000) + self.slider_2.SetValue(0) + self.filter() + + def set_am(self, event): + self.AM_mode = True + self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset - 7.5e3)) + self.sel_sb.set_k(0) + self.sel_am.set_k(1) + self.slider_1.SetValue(12500) + self.slider_2.SetValue(2500) + self.filter() + + def set_cw(self, event): + self.AM_mode = False + self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset)) + self.AM_mode = False + self.sel_sb.set_k(1) + self.sel_am.set_k(0) + self.slider_1.SetValue(-400) + self.slider_2.SetValue(-800) + self.filter() + + def set_volume(self, event): + self.scale.set_k(math.pow(10.,((self.slider_4.GetValue()-500.)/100.))) + + def set_pga(self,event): + if self.PLAY_FROM_USRP: + self.subdev.set_gain(self.slider_5.GetValue()) + + def slide_tune(self, event): + self.frequency = (self.f_slider_scale * self.slider_3.GetValue()) + self.f_slider_offset + if self.AM_mode == False: + self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset)) + else: + self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset - 7.5e3)) + self.spin_ctrl_1.SetValue(self.frequency) + if self.button_12.GetValue(): + self.auto_antenna_tune() + + def spin_tune(self, event): + self.frequency = self.spin_ctrl_1.GetValue() + if self.AM_mode == False: + self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset)) + else: + self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset - 7.5e3)) + self.slider_3.SetValue(int((self.frequency-self.f_slider_offset)/self.f_slider_scale)) + if self.button_12.GetValue(): + self.auto_antenna_tune() + + # Seek forwards in file + def fwd(self, event): + if self.PLAY_FROM_USRP == False: + self.src.seek(10000000,gr.SEEK_CUR) + + # Seek backwards in file + def rew(self, event): + if self.PLAY_FROM_USRP == False: + self.src.seek(-10000000,gr.SEEK_CUR) + + # Mouse over fft display - show frequency in tooltip + def Mouse(self,event): + if self.AM_mode: + fRel = ( event.GetX() - 330. ) / 14.266666 - 7.5 + else: + fRel = ( event.GetX() - 330. ) / 14.266666 + self.fft.win.SetToolTip(wx.ToolTip(eng_notation.num_to_str(self.frequency + (fRel*1e3)))) + + # Mouse clicked on fft display - change frequency + def Click(self,event): + fRel = ( event.GetX() - 330. ) / 14.266666 + if self.AM_mode == False: + self.frequency = self.frequency + (fRel*1e3) + else: + self.frequency = self.frequency + (fRel*1e3) - 7.5e3 + self.spin_ctrl_1.SetValue(int(self.frequency)) + self.slider_3.SetValue(int((self.frequency-self.f_slider_offset)/self.f_slider_scale)) + if self.AM_mode == False: + self.xlate.set_center_freq ( self.usrp_center - ( self.frequency - self.tune_offset )) + else: + self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset - 7.5e3)) + + # Set power of AM sync carrier + def am_carrier(self,event): + scale = math.pow(10,(self.slider_6.GetValue())/50.) + self.pll_carrier_scale.set_k(complex(scale,0)) + + # Reset AT data and start calibrate over + def AT_reset(self, event): + self.xdata = [] + self.ydata = [] + + # Save AT setting for a particular frequency + def AT_calibrate(self, event): + self.xdata.append(float(self.frequency)) + self.ydata.append(self.slider_7.GetValue()) + if len(self.xdata) > 1: + self.m = [] + self.b = [] + for i in range(0,len(self.xdata)-1): + self.m.append( (self.ydata[i+1] - self.ydata[i]) / (self.xdata[i+1] - self.xdata[i]) ) + self.b.append( self.ydata[i] - self.m[i] * self.xdata[i] ) + + # Lookup calibrated points and calculate interpolated antenna tune voltage. + # This is to automatically tune a narrowband loop antenna when the freq + # is changed, to keep signals peaked. + def auto_antenna_tune(self): + for i in range(0,len(self.xdata)-1): + if (self.frequency > self.xdata[i]) & (self.frequency < self.xdata[i+1]): + self.slider_7.SetValue(self.m[i]*self.frequency + self.b[i]) + self.antenna_tune(0) + + # Slider to set loop antenna capacitance + def antenna_tune(self, evt): + if self.PLAY_FROM_USRP: + self.src.write_aux_dac(0,3,self.slider_7.GetValue()) + + # Timer events - check for web commands + def OnUpdate(self): + cmds = os.listdir("/var/www/cgi-bin/commands/") + if cmds!=[]: + if cmds[0]=='chfreq': + fd=open("/var/www/cgi-bin/commands/chfreq","r") + new=fd.readline() + fd.close() + if new!='': + os.unlink("/var/www/cgi-bin/commands/chfreq") + if ( int(new) >= self.f_lo ) & ( int(new) <= self.f_hi ): + self.frequency = int(new) + self.slider_3.SetValue(( self.frequency - self.f_slider_offset) / self.f_slider_scale ) + self.spin_ctrl_1.SetValue(self.frequency) + if self.button_12.GetValue(): + self.auto_antenna_tune() + if self.AM_mode: + self.xlate.set_center_freq ( self.usrp_center - ( self.frequency - self.tune_offset - 7.5e3 )) + else: + self.xlate.set_center_freq ( self.usrp_center - ( self.frequency - self.tune_offset )) + + if cmds[0]=='chvolume': + fd=open("/var/www/cgi-bin/commands/chvolume","r") + new=fd.readline() + fd.close() + if new!='': + os.unlink("/var/www/cgi-bin/commands/chvolume") + if ( int(new) >= 0 ) & ( int(new) <= 500 ): + self.volume = int(new) + self.slider_4.SetValue(self.volume) + self.scale.set_k(math.pow(10.,((self.slider_4.GetValue()-500.)/100.))) + + else: # no new web commands, update state + fh = open("/var/www/cgi-bin/state/freq","w") + fh.write(str(int(self.frequency))+'\n') + fh.close() + fh = open("/var/www/cgi-bin/state/volume","w") + fh.write(str(self.slider_4.GetValue())+'\n') + fh.close() + + +# end of class MyFrame + +# wx.Timer to check for web updates +class UpdateTimer(wx.Timer): + def __init__(self, target, dur=1000): + wx.Timer.__init__(self) + self.target = target + self.Start(dur) + + def Notify(self): + """Called every timer interval""" + if self.target: + self.target.OnUpdate() + + +class MyApp(wx.App): + def OnInit(self): + frame = MyFrame(None, -1, "HF Explorer 2") + frame.Show(True) + self.SetTopWindow(frame) + return True + +app = MyApp(0) +app.MainLoop() + diff --git a/gnuradio-examples/python/apps/hf_explorer/hfx_help b/gnuradio-examples/python/apps/hf_explorer/hfx_help new file mode 100644 index 000000000..9a52dd2bb --- /dev/null +++ b/gnuradio-examples/python/apps/hf_explorer/hfx_help @@ -0,0 +1,180 @@ + + HF Explorer Help + + ----------------------------------------------------------------------- + + Command Line Switches: + + -c DDC center frequency, set band. + -c 7.2e6 or -c 7.2M for 40 meter ham band. + Default is 3.9e6 80 meter ham band. + Example: + + hfx.py -c 9500k + + starts up in the 31 meter band. + + -a Audio output file. Output file for 32kHz two channel + signed word audio. Two channels are used for + independent sideband. This file can be converted + to a wav file with sox. Example: + + sox -c 2 -r 3200 file.sw file.wav + + sox needs the .sw extension to indicate file type. + If not specified no audio file is created. + + -r Radio output file. File to write RF data to for later + demodulation. Records the entire band to disk, width + determined by sample rate/decimation. Be sure to + note the decimation and center freq for later use! + Example: + + hfx.py -c 900e3 -d 80 -r rf_data_AM-c900e3-d80 + + writes a pre-demod rf file centered on 900kHz with a + bandwidth of 800kHz (That's 80 AM stations!). The + center and decimation could be put in the filename for + proper use later. + If not specified no rf data file is created. + At default 250 decimation disk usage is about + 8Gb / hour. + + -i Radio input file. Use to play back a previously + recorded rf data file. Example: + + hfx.py -c 900e3 -d 80 -i rf_data_AM-c900e3-d80 + + plays back the previously recorded band, no + usrp hardware needed. Tune about the 800kHz wide band. + When playing a recorded file, time controls + fast-forward and rewind are available. + + -d Decimation. Sets sample rate and bandwidth. + This is the factor that the usrp sample rate, 64e6, + is divided by. Default is 250 for 256kHz bandwidth + which is enough to record a ham band without + eating up disk space too fast. The 64e6 sample + rate limits the upper practical frequency to 32MHz. + The Basic RX transformer limits the lower frequency + to about 200kHz. + + + Powermate Knob: + + A Powermate knob is recommended but not necessary. If a knob + is used, it is in one of 3 or 4 modes controlling frequency, + volume, filter and (if playing a recorded file) time. + Pushing the knob switches mode and the buttons on the HFX panel + change to show which mode is in effect. Then just turn the knob + to set frequency, volume, filter or go fast forward or rewind. + + + Bandswitch: + + Across the top are a set of predefined bands and buttons + to rapidly switch to the center of that band. To change a band, + type the frequency in Hz into the box under "Center Frequency", + then press "Set" on the left, then the button you want to + program. From then on (untill the program is exited) pushing + that button takes you to that band. To make a band button + permenant edit the hfx.py script with whatever frequency you + want assigned to what button. + + + Frequency: + + There are 6 ways to set the frequency. + 1) Move the slider with the mouse + 2) Use the Spin Control up/down arrows (very fine 1Hz control) + 3) Type the frequency in Hz into the Spin Control + 4) Turn the Powermate knob + 5) Web control. + 6) Clicking on the FFT display to set demod center. This is very + convenient for tuning +-15kHz when you see a signal on the + display. If in Lower Sideband, clicking just to the right of + a signal will tune to it immediately. Clicking several times + on the far right right or left of the display will rapidly + tune up or down the band. + + + Volume: + + Move the volume slider with the mouse, or push the Powermate knob + untill the volume button is active, or click on the volume button, + then turn the knob. Volume can also be set by web control if web + control is setup and enabled. + + + Filter: + + Similar to volume, switches in any of 30 audio filters from 600 + to 3600Hz in Sideband or up to 5kHz in AM. + + + Mode: + + Demodulation modes are chosen by clicking on the buttons for + Lower Sideband, Upper Sideband, or AM. + + + PGA: + + PGA slider sets the rf gain in the Analog-to-Digital converter + before digitizing. 0 to 20db gain easily shows up on the FFT + display. + + + Time: + + When playing back a recorded RF data file, you can enjoy the + freedom of rewinding or fast-forwarding. Replay a weak signal + or skip through annoying AM commercials. + + + Antennas and Preamps: + + The USRP Basic RX board is not sensitive enough for anything but + the strongest signals. In my experience about 40 db of small + signal gain is required to make the HFX as sensitive as other + receivers. Some working amplifiers are the Ramsey PR-2 with 20db + gain, fairly low noise and more bandwidth than we can use here. + Also the amp modules from Advanced Receiver Research are nice. + I use an ARR 7-7.4MHz GaAsFET 29db amp with .5db noise at the + apex of a 40 meter dipole with excellent results. Another + amp I like is a Minicircuits ZHL-32A 29db amp but they are + expensive and hard to find. Also it may help to use some filters + to keep strong local signals from the ADC, or limit rf input + to the band of interest, etc. + Resonant outdoor antennas, like a dipole, in a low-noise (away + from consumer electronics) environment are nice. Long random wires + with a tuner work. I like a small indoor tuned loop made from 10ft + of 1/4" copper tube, a 365pf tuning cap and a pickup loop connected + to rg-58. + + + Web Control: + + To control your radio remotely, ensure you have a web server + (Apache, etc) working and a compatible directory structure in + place. Directories /var/www/cgi-bin/commands and + /var/www/cgi-bin/state must already exist. You will need a + home page with forms and a set of scripts to put commands in + and put the current state on the home page. email me for further + help. Setting WEB_CONTROL to True in hfx.py turns on the timers + that check for commands and update the state. + + + IF Output: + + There is a provision for outputting un-demodulated complex + through the audio out in stereo for use with Digital Radio + Mondial (DRM) or using a seperate demodulation program like + SDRadio (by I2PHD). + Set IF_OUTPUT to True in weaver_isb_am1_usrp4.py. + + + --Good luck and happy LW/MW/SW Exploring. + Chuck + chuckek@musicriver.homeunix.com + diff --git a/gnuradio-examples/python/apps/hf_radio/README.TXT b/gnuradio-examples/python/apps/hf_radio/README.TXT new file mode 100644 index 000000000..df8a901e4 --- /dev/null +++ b/gnuradio-examples/python/apps/hf_radio/README.TXT @@ -0,0 +1,53 @@ +The files in this directory implement a fairly simple HF radio that works +with the basic rx daughter board on the USRP. + +Many thanks to the Gnu Radio folks for a great new way to waste large blocks +of time in infinitely tweaking a huge number of free parameters. + +Start the receiver by running the radio.py in this directory. Or from the +Python prompt type "from radio import *" and you'll get the prompt back +with the receiver running. You can then poke around to see what's going on. + +There are two spectrum displays. One is the output of the USRP and displays +about 300KHz of bandwidth centered at the current tuning freq. The other +displays the output spectrum of the demodulator. + +The demodulator does AM demod using the complex modulus block from gr. It +does SSB demod using the frequency translating fir filter as a complex +hilbert transformer. The taps for this filter were generated using a program +called Scilab and the Scilab program in the file hfir.sci. More details in +the associated files. + +Tune the receiver using the spin buttons under the big frequency display. + +The agc block is a roll your own. The standard agc in the newer CVS updates +seems to work but doesn't seem to have adjustable time constants or provide +access to internal signal nodes which are used for the RSSI. + +The AGC authority (a sort of gain parameter) and the reference level used +in the power to dB computagion can be adjusted using the spin buttons. + +The audio bandwidth can be similarly adjusted from about 50Hz to 10KHz. + +The GUI layout was produced using wxGlade. The file radio.xml is the GUI +specification. It will produce a file called ui.py which is subclassed +by classes defined in radio.py. The ui.py is purely generated by wxGlade +all app specific code for the GUI is in radio.py. + +Most of the actual signal processing code is built up in the other included +files using the hierarchical block facilities. This organization should +make it easier to tweak to your heart's content. + +Known bugs weakness and other + +wxPython and wxGlade seem to conspire to insure that the layout can never +be exactly what you have in mind. + +Some of the controls don't behave as one might like. wx spin controls +and spin boxes only support integers so it is rather a nuisance to make +units come out nice. In the process of development I came up with a reasonable +kluge so there is a mixture of approaches. + +Enjoy. + +M. Revnell 2006-Jan-06 diff --git a/gnuradio-examples/python/apps/hf_radio/hfir.sci b/gnuradio-examples/python/apps/hf_radio/hfir.sci new file mode 100644 index 000000000..a2d5e2a62 --- /dev/null +++ b/gnuradio-examples/python/apps/hf_radio/hfir.sci @@ -0,0 +1,59 @@ +// designs a complex tap fir filter akin to the hilbert transformer. +// +// The hilbert transformer is classified as a linear phase fir +// with allpass magnitude response and 90 degree phase response for +// positive frequencies and -90 degrees phase for negative frequencies. +// Or, if you prefer, normalized frequencies between .5 and 1 since +// negative frequencies don't really have much meaning outside the complex +// domain. +// +// Normally one would use the hilbert transformer in one leg of a complex +// processing block and a compensating delay in the other. +// +// This one differs in the following respects: +// It is low pass with a cutoff of .078125 +// The filter is a lowpass kaiser windowed filter with parameter 3 +// The phase response is 45 degrees for positive frequencies and -45 +// for negative frequencies. +// The coefficent set is used in one path and the same coefficients +// are used time reversed in the other. This results in the net effect +// of +/- 90 degrees as in the usual hilbert application. +// +// The coefficient set can be used in the gnuradio frequency translating +// fir filter for ssb demodulation. +// +// This isn't as computationally efficient as using the hilbert transformer +// and compensating delay but fascinating none the less. +// +// This program is for the scilab language a very powerful free math +// package similar to Matlab with infinitely better price/performace. +// +// compute the prototype lowpass fir +// length is 255 (odd) for the same symmetry reasons as the hilbert transformer + +len = 1023; +l2 = floor(len/2); +md = l2 + 1; +l3 = md + 1; + +h = wfir( 'lp', len, [10.0/256 0], 'kr', [3 0] ); + +H = fft(h); + +H(1:l2)=H(1:l2)*exp(%i*%pi/4); +H(md)=0+%i*0; +H(l3:len)=H(l3:len)*exp(-%i*%pi/4); + +j=real(ifft(H)); +k(1:len)=j(len:-1:1); +x=j+%i.*k; +X=fft(x); +plot(abs(X)) + +f = file('open','taps') +for i=(1:len) + fprintf( f, '%f%+fj', j(i), k(i) ) +end + +file('close',f) + diff --git a/gnuradio-examples/python/apps/hf_radio/input.py b/gnuradio-examples/python/apps/hf_radio/input.py new file mode 100644 index 000000000..3a62a68cc --- /dev/null +++ b/gnuradio-examples/python/apps/hf_radio/input.py @@ -0,0 +1,41 @@ +# Basic USRP setup and control. +# It's only ever been tried with a basic rx daughter card. +# +# Imagine that the gnuradio boilerplate is here. +# +# M. Revnell 2005-Dec + +from gnuradio import gr, gru, optfir +from gnuradio import usrp +import usrp_dbid +import math + +# Put special knowlege of usrp here. + +class input: + def __init__( self, decim ): + self.freq = -2.5e6 + self.src = usrp.source_c( ) + self.subdev = usrp.pick_subdev( self.src, + (usrp_dbid.BASIC_RX, + usrp_dbid.TV_RX)) + + print self.subdev + + self.subdevice = usrp.selected_subdev( self.src, + self.subdev ) + + self.mux = usrp.determine_rx_mux_value( self.src, + self.subdev ) + self.decim = decim + + self.adc_rate = self.src.adc_rate() + self.usrp_rate = self.adc_rate / self.decim + self.src.set_decim_rate( self.decim ) + self.src.set_mux( self.mux ) + usrp.tune( self.src, 0, self.subdevice, self.freq ) + + def set_freq( self, x ): + r = usrp.tune( self.src, 0, self.subdevice, -x ) + if r: + self.freq = -x diff --git a/gnuradio-examples/python/apps/hf_radio/output.py b/gnuradio-examples/python/apps/hf_radio/output.py new file mode 100644 index 000000000..dc9caf528 --- /dev/null +++ b/gnuradio-examples/python/apps/hf_radio/output.py @@ -0,0 +1,17 @@ +# Audio output with a volume control. +# +# M. Revnell 2005-Dec + +from gnuradio import gr, gru +from gnuradio import audio + +class output( gr.hier_block ): + def __init__( self, fg, rate ): + self.out = audio.sink( rate ) + self.vol = gr.multiply_const_ff( 0.1 ) + fg.connect( self.vol, self.out ) + gr.hier_block.__init__(self, fg, self.vol, None ) + + def set( self, val ): + self.vol.set_k( val ) + diff --git a/gnuradio-examples/python/apps/hf_radio/radio.py b/gnuradio-examples/python/apps/hf_radio/radio.py new file mode 100755 index 000000000..9f444b916 --- /dev/null +++ b/gnuradio-examples/python/apps/hf_radio/radio.py @@ -0,0 +1,304 @@ +#!/usr/bin/env python + +# GUI interactions and high level connections handled here. +# +# Interacts with classes defined by wxGlade in ui.py. +# +# The usual gnuradio copyright boilerplate incorperated here by reference. +# +# M. Revnell 2006-Jan + +from threading import * +import wx +import wx.lib.evtmgr as em +import time + +from gnuradio import gr, gru, eng_notation, optfir +from gnuradio import audio +from gnuradio import usrp +from gnuradio import blks +from gnuradio.wxgui import fftsink +from gnuradio.wxgui import waterfallsink +from gnuradio.wxgui import scopesink + +from input import * +from output import * +from ssbdemod import * +from ssbagc import * +from ui import * +from math import log10 + +class graph( gr.hier_block ): + def __init__( self, fg ): + self.graph = fg + self.fe_decim = 250 + self.src = input( self.fe_decim ) + self.adc_rate = self.src.adc_rate + self.fe_rate = self.adc_rate / self.fe_decim + self.filter_decim = 1 + self.audio_decim = 16 + self.demod_rate = self.fe_rate / self.filter_decim + self.audio_rate = self.demod_rate / self.audio_decim + + self.demod = ssb_demod( fg, self.demod_rate, self.audio_rate ) + self.agc = agc( fg ) + #self.agc = gr.agc_ff() + self.out = output( fg, self.audio_rate ) + + fg.connect( self.src.src, + self.demod, + self.agc, + self.out ) + + gr.hier_block.__init__( self, fg, None, None ) + + def tune( self, freq ): + fe_target = -freq + self.src.set_freq( fe_target ) + fe_freq = self.src.src.rx_freq( 0 ) + demod_cf = fe_target - fe_freq + self.demod.tune( demod_cf ) + +class radio_frame( ui_frame ): + def __init__( self, block, *args, **kwds ): + ui_frame.__init__( self, *args, **kwds ) + self.block = block + self.freq_disp.SetRange(0, 30e6) + f = self.block.src.freq + self.freq_disp.SetValue( -f ) + self.volume.SetRange( 0, 20 ) + self.pga.SetRange( 0, 20 ) + self.rssi_range = 1 + self.rssi.SetRange( self.rssi_range ) + self.agc_max.SetValue( str( self.rssi_range ) ) + self.spin_e0.SetValue( 50 ) + self.spin_e1.SetValue( 50 ) + self.spin_e2.SetValue( 50 ) + self.spin_e3.SetValue( 50 ) + self.spin_e4.SetValue( 50 ) + self.spin_e5.SetValue( 50 ) + self.spin_e6.SetValue( 50 ) + bw = 3.3e3 + self.bandwidth.SetValue( str( bw ) ) + self.block.demod.set_bw( bw ) + self.bw_spin.SetValue( 5 ) + agc_gain = self.block.agc.gain.k() + self.agc_gain_s.SetValue( 5 ) + self.agc_gain.SetValue( str( agc_gain ) ) + agc_ref = self.block.agc.offs.k() + self.agc_ref.SetValue( str( agc_ref ) ) + self.agc_ref_s.SetValue( 5 ) + + self.fespectrum = fftsink.fft_sink_c( + self.block.graph, + self.fe_panel, + fft_size=512, + sample_rate = block.fe_rate, + baseband_freq = 0, + average = False, + size = ( 680, 140 ) ) + + self.ifspectrum = fftsink.fft_sink_c( + self.block.graph, + self.if_panel, + fft_size=512, + sample_rate = block.audio_rate, + baseband_freq = 0, + average = False, + size = ( 680, 140 ) ) + + em.eventManager.Register( self.fe_mouse, + wx.EVT_MOTION, + self.fespectrum.win ) + + em.eventManager.Register( self.fe_click, + wx.EVT_LEFT_DOWN, + self.fespectrum.win ) + + block.graph.connect( block.src.src, self.fespectrum ) + block.graph.connect( block.demod.xlate, self.ifspectrum ) + + def agc_ref_up( self, event ): + self.agc_ref_s.SetValue( 5 ) + r = float( self.agc_ref.GetValue() ) + r = r + 5 + self.agc_ref.SetValue( str( r ) ) + self.block.agc.offs.set_k( r ) + + def agc_ref_down( self, event ): + self.agc_ref_s.SetValue( 5 ) + r = float( self.agc_ref.GetValue() ) + r = r - 5 + self.agc_ref.SetValue( str( r ) ) + self.block.agc.offs.set_k( r ) + + def agc_gain_up( self, event ): + self.agc_gain_s.SetValue( 5 ) + g = float(self.agc_gain.GetValue()) + g = g + 10 + self.agc_gain.SetValue( str( g ) ) + self.block.agc.gain.set_k( g ) + + def agc_gain_down( self, event ): + self.agc_gain_s.SetValue( 5 ) + g = float(self.agc_gain.GetValue()) + g = g - 10 + self.agc_gain.SetValue( str( g ) ) + self.block.agc.gain.set_k( g ) + + def fe_mouse( self, event ): + f = int(self.freq_disp.GetValue()) + f = f+((event.GetX()-346.)*(400./610.))*1000 + self.fespectrum.win.SetToolTip( + wx.ToolTip( eng_notation.num_to_str(f))) + + def fe_click( self, event ): + f = int(self.freq_disp.GetValue()) + f = f+((event.GetX()-346.)*(400./610.))*1000 + self.tune( f ) + + def setrssi( self, level ): + if level < 0: + level = 0 + if level > self.rssi_range: + self.rssi_range = level + self.rssi.SetRange( level ) + self.agc_max.SetValue( str( level )) + self.rssi.SetValue( level ) + self.agc_level.SetValue( str( level )) + + def tune_evt( self, event ): + f = self.freq_disp.GetValue() + self.tune( f ) + + def tune( self, frequency ): + self.freq_disp.SetValue( frequency ) + self.block.tune( frequency ) + + def up_e0( self, event ): + self.spin_e0.SetValue( 50 ) + self.tune( self.freq_disp.GetValue() + 1e0 ) + + def down_e0( self, event ): + self.spin_e0.SetValue( 50 ) + self.tune( self.freq_disp.GetValue() - 1e0 ) + + def up_e1( self, event ): + self.spin_e1.SetValue( 50 ) + self.tune( self.freq_disp.GetValue() + 1e1 ) + + def down_e1( self, event ): + self.spin_e1.SetValue( 50 ) + self.tune( self.freq_disp.GetValue() - 1e1 ) + + def up_e2( self, event ): + self.spin_e2.SetValue( 50 ) + self.tune( self.freq_disp.GetValue() + 1e2 ) + + def down_e2( self, event ): + self.spin_e2.SetValue( 50 ) + self.tune( self.freq_disp.GetValue() - 1e2 ) + + def up_e3( self, event ): + self.spin_e3.SetValue( 50 ) + self.tune( self.freq_disp.GetValue() + 1e3 ) + + def down_e3( self, event ): + self.spin_e3.SetValue( 50 ) + self.tune( self.freq_disp.GetValue() - 1e3 ) + + def up_e4( self, event ): + self.spin_e4.SetValue( 50 ) + self.tune( self.freq_disp.GetValue() + 1e4 ) + + def down_e4( self, event ): + self.spin_e4.SetValue( 50 ) + self.tune( self.freq_disp.GetValue() - 1e4 ) + + def up_e5( self, event ): + self.spin_e5.SetValue( 50 ) + self.tune( self.freq_disp.GetValue() + 1e5 ) + + def down_e5( self, event ): + self.spin_e5.SetValue( 50 ) + self.tune( self.freq_disp.GetValue() - 1e5 ) + + def up_e6( self, event ): + self.spin_e6.SetValue( 50 ) + self.tune( self.freq_disp.GetValue() + 1e6 ) + + def down_e6( self, event ): + self.spin_e6.SetValue( 50 ) + self.tune( self.freq_disp.GetValue() - 1e6 ) + + def event_pga( self, event ): + self.block.src.src.set_pga( 0, self.pga.GetValue()) + + def event_vol( self, event ): + self.block.out.set( self.volume.GetValue()/20.0 ) + + def set_usb( self, event ): + self.block.demod.upper_sb() + + def set_lsb( self, event ): + self.block.demod.lower_sb() + + def set_am( self, event ): + self.block.demod.set_am() + + def bw_up( self, event ): + self.bw_spin.SetValue( 5 ) + bw = float(self.bandwidth.GetValue()) + bw = bw + 20.0 + if bw > 10e3: + bw = 10e3 + self.bandwidth.SetValue( str( bw ) ) + self.block.demod.set_bw( bw ) + + def bw_down( self, event ): + self.bw_spin.SetValue( 5 ) + bw = float(self.bandwidth.GetValue()) + bw = bw - 20.0 + if bw < 50: + bw = 50 + self.bandwidth.SetValue( str( bw ) ) + self.block.demod.set_bw( bw ) + + +class radio( wx.App ): + def OnInit( self ): + self.graph = gr.flow_graph() + self.block = graph( self.graph ) + self.frame = radio_frame( self.block, None, -1, "Title" ) + self.frame.Show( True ) + self.SetTopWindow( self.frame ) + return True + +a=radio( 0 ) + +l=gr.probe_signal_f() +#l=gr.probe_avg_mag_sqrd_f(1,.001) +a.graph.connect(a.block.agc.offs,l ) +#a.graph.connect(a.block.demod,l) + +def main_function(): + global a + a.MainLoop() + + +def rssi_function(): + global a + global l + while 1: + level = l.level() + wx.CallAfter( a.frame.setrssi, level ) + time.sleep( .1 ) + +thread1 = Thread( target = main_function ) +thread2 = Thread( target = rssi_function ) + +thread1.start() +thread2.start() + +a.graph.start() + diff --git a/gnuradio-examples/python/apps/hf_radio/radio.xml b/gnuradio-examples/python/apps/hf_radio/radio.xml new file mode 100644 index 000000000..81daa19b0 --- /dev/null +++ b/gnuradio-examples/python/apps/hf_radio/radio.xml @@ -0,0 +1,441 @@ +<?xml version="1.0"?> +<!-- generated by wxGlade 0.4 on Fri Jan 6 09:51:36 2006 --> + +<application path="/root/radio/ui.py" name="" class="" option="0" language="python" top_window="" encoding="UTF-8" use_gettext="0" overwrite="0" use_new_namespace="1" for_version="2.6"> + <object class="ui_frame" name="frame_1" base="EditFrame"> + <style>wxDEFAULT_FRAME_STYLE</style> + <title>frame_1</title> + <object class="wxStaticBoxSizer" name="sizer_1" base="EditStaticBoxSizer"> + <orient>wxVERTICAL</orient> + <label>sizer_1</label> + <object class="sizeritem"> + <flag>wxEXPAND</flag> + <border>0</border> + <option>1</option> + <object class="wxBoxSizer" name="sizer_2" base="EditBoxSizer"> + <orient>wxHORIZONTAL</orient> + <object class="sizeritem"> + <flag>wxEXPAND</flag> + <border>0</border> + <option>1</option> + <object class="wxBoxSizer" name="sizer_3" base="EditBoxSizer"> + <orient>wxVERTICAL</orient> + <object class="sizeritem"> + <flag>wxEXPAND|wxADJUST_MINSIZE</flag> + <border>0</border> + <option>1</option> + <object class="wxSpinCtrl" name="freq_disp" base="EditSpinCtrl"> + <font> + <size>32</size> + <family>default</family> + <style>normal</style> + <weight>normal</weight> + <underlined>0</underlined> + <face></face> + </font> + <events> + <handler event="EVT_SPINCTRL">tune_evt</handler> + </events> + </object> + </object> + <object class="sizeritem"> + <flag>wxEXPAND</flag> + <border>0</border> + <option>1</option> + <object class="wxGridSizer" name="grid_sizer_1" base="EditGridSizer"> + <hgap>0</hgap> + <rows>2</rows> + <cols>3</cols> + <vgap>0</vgap> + <object class="sizeritem"> + <flag>wxEXPAND</flag> + <border>0</border> + <option>1</option> + <object class="wxGridSizer" name="grid_sizer_2" base="EditGridSizer"> + <hgap>0</hgap> + <rows>1</rows> + <cols>7</cols> + <vgap>0</vgap> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxSpinButton" name="spin_e6" base="EditSpinButton"> + <events> + <handler event="EVT_SPIN_UP">up_e6</handler> + <handler event="EVT_SPIN_DOWN">down_e6</handler> + </events> + </object> + </object> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxSpinButton" name="spin_e5" base="EditSpinButton"> + <events> + <handler event="EVT_SPIN_UP">up_e5</handler> + <handler event="EVT_SPIN_DOWN">down_e5</handler> + </events> + </object> + </object> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxSpinButton" name="spin_e4" base="EditSpinButton"> + <events> + <handler event="EVT_SPIN_UP">up_e4</handler> + <handler event="EVT_SPIN_DOWN">down_e4</handler> + </events> + </object> + </object> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxSpinButton" name="spin_e3" base="EditSpinButton"> + <events> + <handler event="EVT_SPIN_UP">up_e3</handler> + <handler event="EVT_SPIN_DOWN">down_e3</handler> + </events> + </object> + </object> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxSpinButton" name="spin_e2" base="EditSpinButton"> + <events> + <handler event="EVT_SPIN_UP">up_e2</handler> + <handler event="EVT_SPIN_DOWN">down_e2</handler> + </events> + </object> + </object> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxSpinButton" name="spin_e1" base="EditSpinButton"> + <events> + <handler event="EVT_SPIN_UP">up_e1</handler> + <handler event="EVT_SPIN_DOWN">down_e1</handler> + </events> + </object> + </object> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxSpinButton" name="spin_e0" base="EditSpinButton"> + <events> + <handler event="EVT_SPIN_UP">up_e0</handler> + <handler event="EVT_SPIN_DOWN">down_e0</handler> + </events> + </object> + </object> + </object> + </object> + <object class="sizeritem"> + <flag>wxEXPAND</flag> + <border>0</border> + <option>1</option> + <object class="wxPanel" name="panel_1" base="EditPanel"> + <style>wxTAB_TRAVERSAL</style> + </object> + </object> + <object class="sizeritem"> + <flag>wxEXPAND</flag> + <border>0</border> + <option>1</option> + <object class="wxPanel" name="panel_2" base="EditPanel"> + <style>wxTAB_TRAVERSAL</style> + </object> + </object> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxButton" name="button_lsb" base="EditButton"> + <label>LSB</label> + <events> + <handler event="EVT_BUTTON">set_lsb</handler> + </events> + </object> + </object> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxButton" name="button_usb" base="EditButton"> + <label>USB</label> + <events> + <handler event="EVT_BUTTON">set_usb</handler> + </events> + </object> + </object> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxButton" name="button_am" base="EditButton"> + <label>AM</label> + <events> + <handler event="EVT_BUTTON">set_am</handler> + </events> + </object> + </object> + </object> + </object> + </object> + </object> + <object class="sizeritem"> + <flag>wxEXPAND</flag> + <border>0</border> + <option>1</option> + <object class="wxBoxSizer" name="sizer_4" base="EditBoxSizer"> + <orient>wxVERTICAL</orient> + <object class="sizeritem"> + <flag>wxEXPAND</flag> + <border>0</border> + <option>1</option> + <object class="wxGridSizer" name="grid_sizer_3" base="EditGridSizer"> + <hgap>0</hgap> + <rows>2</rows> + <cols>4</cols> + <vgap>0</vgap> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxStaticText" name="label_1" base="EditStaticText"> + <attribute>1</attribute> + <label>VOLUME</label> + </object> + </object> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxStaticText" name="label_2" base="EditStaticText"> + <attribute>1</attribute> + <label>PGA</label> + </object> + </object> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxTextCtrl" name="agc_level" base="EditTextCtrl"> + </object> + </object> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxStaticText" name="label_6" base="EditStaticText"> + <attribute>1</attribute> + </object> + </object> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxSpinCtrl" name="volume" base="EditSpinCtrl"> + <events> + <handler event="EVT_SPINCTRL">event_vol</handler> + </events> + </object> + </object> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxSpinCtrl" name="pga" base="EditSpinCtrl"> + <events> + <handler event="EVT_SPINCTRL">event_pga</handler> + </events> + </object> + </object> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxTextCtrl" name="agc_max" base="EditTextCtrl"> + </object> + </object> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxStaticText" name="label_7" base="EditStaticText"> + <attribute>1</attribute> + </object> + </object> + </object> + </object> + <object class="sizeritem"> + <flag>wxEXPAND</flag> + <border>0</border> + <option>1</option> + <object class="wxGridSizer" name="grid_sizer_4" base="EditGridSizer"> + <hgap>0</hgap> + <rows>2</rows> + <cols>4</cols> + <vgap>0</vgap> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxStaticText" name="label_4" base="EditStaticText"> + <attribute>1</attribute> + <label>AGC AUTHORITY</label> + </object> + </object> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxStaticText" name="label_5" base="EditStaticText"> + <attribute>1</attribute> + <label>AGC REF LVL</label> + </object> + </object> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxStaticText" name="label_3" base="EditStaticText"> + <attribute>1</attribute> + <label>BANDWIDTH</label> + </object> + </object> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxStaticText" name="label_8" base="EditStaticText"> + <attribute>1</attribute> + </object> + </object> + <object class="sizeritem"> + <flag>wxEXPAND</flag> + <border>0</border> + <option>1</option> + <object class="wxBoxSizer" name="sizer_6" base="EditBoxSizer"> + <orient>wxHORIZONTAL</orient> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxTextCtrl" name="agc_gain" base="EditTextCtrl"> + </object> + </object> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxSpinButton" name="agc_gain_s" base="EditSpinButton"> + <events> + <handler event="EVT_SPIN_UP">agc_gain_up</handler> + <handler event="EVT_SPIN_DOWN">agc_gain_down</handler> + </events> + </object> + </object> + </object> + </object> + <object class="sizeritem"> + <flag>wxEXPAND</flag> + <border>0</border> + <option>1</option> + <object class="wxBoxSizer" name="sizer_7" base="EditBoxSizer"> + <orient>wxHORIZONTAL</orient> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxTextCtrl" name="agc_ref" base="EditTextCtrl"> + </object> + </object> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxSpinButton" name="agc_ref_s" base="EditSpinButton"> + <events> + <handler event="EVT_SPIN_UP">agc_ref_up</handler> + <handler event="EVT_SPIN_DOWN">agc_ref_down</handler> + </events> + </object> + </object> + </object> + </object> + <object class="sizeritem"> + <flag>wxEXPAND</flag> + <border>0</border> + <option>1</option> + <object class="wxBoxSizer" name="sizer_5" base="EditBoxSizer"> + <orient>wxHORIZONTAL</orient> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxTextCtrl" name="bandwidth" base="EditTextCtrl"> + </object> + </object> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxSpinButton" name="bw_spin" base="EditSpinButton"> + <events> + <handler event="EVT_SPIN_UP">bw_up</handler> + <handler event="EVT_SPIN_DOWN">bw_down</handler> + </events> + </object> + </object> + </object> + </object> + <object class="sizeritem"> + <flag>wxADJUST_MINSIZE</flag> + <border>0</border> + <option>0</option> + <object class="wxStaticText" name="label_9" base="EditStaticText"> + <attribute>1</attribute> + </object> + </object> + </object> + </object> + <object class="sizeritem"> + <flag>wxEXPAND|wxADJUST_MINSIZE</flag> + <border>0</border> + <option>1</option> + <object class="wxGauge" name="rssi" base="EditGauge"> + <foreground>#ff0000</foreground> + <style>wxGA_HORIZONTAL|wxGA_SMOOTH</style> + <range>10</range> + <size>315, 10</size> + </object> + </object> + </object> + </object> + </object> + </object> + <object class="sizeritem"> + <flag>wxEXPAND</flag> + <border>0</border> + <option>1</option> + <object class="wxPanel" name="fe_panel" base="EditPanel"> + <style>wxTAB_TRAVERSAL</style> + </object> + </object> + <object class="sizeritem"> + <flag>wxEXPAND</flag> + <border>0</border> + <option>1</option> + <object class="wxPanel" name="if_panel" base="EditPanel"> + <style>wxTAB_TRAVERSAL</style> + </object> + </object> + </object> + </object> +</application> diff --git a/gnuradio-examples/python/apps/hf_radio/ssb_taps b/gnuradio-examples/python/apps/hf_radio/ssb_taps new file mode 100644 index 000000000..0ef3bbf26 --- /dev/null +++ b/gnuradio-examples/python/apps/hf_radio/ssb_taps @@ -0,0 +1,1023 @@ +-0.000035-0.000009j +-0.000066-0.000020j +-0.000080-0.000044j +-0.000084-0.000071j +-0.000077-0.000100j +-0.000063-0.000127j +-0.000041-0.000150j +-0.000013-0.000167j +0.000020-0.000177j +0.000054-0.000180j +0.000089-0.000174j +0.000121-0.000161j +0.000150-0.000140j +0.000173-0.000113j +0.000188-0.000081j +0.000196-0.000046j +0.000194-0.000011j +0.000184+0.000022j +0.000165+0.000052j +0.000139+0.000077j +0.000107+0.000093j +0.000071+0.000102j +0.000034+0.000101j +-0.000002+0.000090j +-0.000036+0.000070j +-0.000064+0.000042j +-0.000086+0.000006j +-0.000098-0.000034j +-0.000101-0.000077j +-0.000093-0.000120j +-0.000076-0.000161j +-0.000049-0.000197j +-0.000014-0.000227j +0.000026-0.000248j +0.000071-0.000259j +0.000117-0.000259j +0.000162-0.000249j +0.000203-0.000228j +0.000237-0.000199j +0.000263-0.000162j +0.000279-0.000120j +0.000284-0.000075j +0.000278-0.000031j +0.000260+0.000010j +0.000232+0.000045j +0.000196+0.000073j +0.000153+0.000090j +0.000106+0.000095j +0.000059+0.000089j +0.000014+0.000071j +-0.000027+0.000041j +-0.000059+0.000002j +-0.000082-0.000046j +-0.000093-0.000098j +-0.000091-0.000152j +-0.000077-0.000206j +-0.000050-0.000255j +-0.000012-0.000298j +0.000034-0.000330j +0.000088-0.000351j +0.000145-0.000359j +0.000202-0.000354j +0.000256-0.000335j +0.000304-0.000304j +0.000343-0.000262j +0.000370-0.000213j +0.000384-0.000158j +0.000384-0.000102j +0.000369-0.000048j +0.000341+0.000002j +0.000302+0.000042j +0.000252+0.000072j +0.000196+0.000088j +0.000137+0.000089j +0.000078+0.000075j +0.000024+0.000047j +-0.000023+0.000005j +-0.000059-0.000048j +-0.000082-0.000109j +-0.000090-0.000175j +-0.000081-0.000242j +-0.000058-0.000306j +-0.000019-0.000364j +0.000032-0.000411j +0.000093-0.000445j +0.000161-0.000465j +0.000232-0.000467j +0.000301-0.000453j +0.000365-0.000423j +0.000419-0.000379j +0.000461-0.000323j +0.000487-0.000259j +0.000497-0.000190j +0.000489-0.000121j +0.000464-0.000056j +0.000423+0.000001j +0.000369+0.000046j +0.000304+0.000076j +0.000233+0.000089j +0.000160+0.000083j +0.000089+0.000060j +0.000025+0.000018j +-0.000027-0.000038j +-0.000065-0.000107j +-0.000086-0.000185j +-0.000088-0.000266j +-0.000071-0.000347j +-0.000035-0.000422j +0.000019-0.000487j +0.000086-0.000538j +0.000164-0.000573j +0.000248-0.000588j +0.000334-0.000582j +0.000415-0.000557j +0.000488-0.000513j +0.000548-0.000452j +0.000592-0.000379j +0.000616-0.000297j +0.000619-0.000213j +0.000600-0.000130j +0.000561-0.000054j +0.000504+0.000010j +0.000432+0.000058j +0.000350+0.000086j +0.000261+0.000093j +0.000173+0.000078j +0.000090+0.000040j +0.000017-0.000017j +-0.000040-0.000092j +-0.000078-0.000180j +-0.000095-0.000275j +-0.000088-0.000374j +-0.000058-0.000469j +-0.000006-0.000555j +0.000065-0.000627j +0.000152-0.000681j +0.000250-0.000712j +0.000352-0.000720j +0.000454-0.000703j +0.000548-0.000663j +0.000630-0.000601j +0.000694-0.000521j +0.000737-0.000428j +0.000755-0.000327j +0.000748-0.000225j +0.000715-0.000128j +0.000659-0.000042j +0.000582+0.000028j +0.000490+0.000077j +0.000387+0.000101j +0.000280+0.000099j +0.000175+0.000070j +0.000080+0.000015j +-0.000001-0.000063j +-0.000061-0.000159j +-0.000097-0.000268j +-0.000106-0.000385j +-0.000087-0.000501j +-0.000040-0.000611j +0.000032-0.000708j +0.000125-0.000786j +0.000235-0.000839j +0.000354-0.000866j +0.000477-0.000863j +0.000595-0.000831j +0.000702-0.000772j +0.000792-0.000688j +0.000859-0.000585j +0.000898-0.000469j +0.000907-0.000347j +0.000886-0.000227j +0.000835-0.000115j +0.000757-0.000019j +0.000657+0.000055j +0.000540+0.000102j +0.000415+0.000119j +0.000287+0.000103j +0.000165+0.000056j +0.000058-0.000020j +-0.000029-0.000122j +-0.000090-0.000243j +-0.000121-0.000378j +-0.000118-0.000517j +-0.000082-0.000653j +-0.000014-0.000778j +0.000082-0.000885j +0.000201-0.000966j +0.000337-0.001017j +0.000481-0.001034j +0.000626-0.001016j +0.000762-0.000965j +0.000882-0.000882j +0.000978-0.000772j +0.001044-0.000643j +0.001076-0.000501j +0.001072-0.000355j +0.001032-0.000215j +0.000958-0.000089j +0.000854+0.000016j +0.000726+0.000091j +0.000582+0.000133j +0.000431+0.000138j +0.000281+0.000105j +0.000143+0.000035j +0.000024-0.000068j +-0.000068-0.000199j +-0.000126-0.000350j +-0.000147-0.000513j +-0.000128-0.000677j +-0.000070-0.000834j +0.000024-0.000974j +0.000150-0.001089j +0.000300-0.001171j +0.000465-0.001216j +0.000637-0.001219j +0.000805-0.001182j +0.000960-0.001105j +0.001091-0.000993j +0.001191-0.000853j +0.001253-0.000692j +0.001274-0.000521j +0.001252-0.000350j +0.001188-0.000189j +0.001085-0.000049j +0.000950+0.000062j +0.000790+0.000136j +0.000614+0.000168j +0.000435+0.000156j +0.000261+0.000099j +0.000105+0.000000j +-0.000024-0.000136j +-0.000117-0.000301j +-0.000169-0.000486j +-0.000175-0.000680j +-0.000134-0.000872j +-0.000048-0.001051j +0.000079-0.001206j +0.000240-0.001326j +0.000426-0.001406j +0.000626-0.001439j +0.000829-0.001424j +0.001022-0.001361j +0.001195-0.001252j +0.001336-0.001106j +0.001437-0.000929j +0.001491-0.000733j +0.001496-0.000529j +0.001449-0.000330j +0.001354-0.000148j +0.001217+0.000005j +0.001044+0.000120j +0.000846+0.000188j +0.000635+0.000206j +0.000424+0.000170j +0.000226+0.000083j +0.000053-0.000053j +-0.000084-0.000228j +-0.000176-0.000434j +-0.000216-0.000659j +-0.000201-0.000889j +-0.000131-0.001111j +-0.000009-0.001312j +0.000158-0.001480j +0.000362-0.001604j +0.000590-0.001677j +0.000829-0.001694j +0.001066-0.001653j +0.001286-0.001556j +0.001477-0.001408j +0.001626-0.001219j +0.001724-0.001000j +0.001765-0.000762j +0.001746-0.000522j +0.001668-0.000293j +0.001534-0.000090j +0.001353+0.000075j +0.001135+0.000190j +0.000893+0.000247j +0.000642+0.000243j +0.000396+0.000176j +0.000172+0.000049j +-0.000017-0.000131j +-0.000159-0.000355j +-0.000244-0.000609j +-0.000266-0.000880j +-0.000222-0.001151j +-0.000114-0.001406j +0.000053-0.001630j +0.000270-0.001810j +0.000524-0.001934j +0.000802-0.001994j +0.001087-0.001987j +0.001362-0.001911j +0.001611-0.001772j +0.001819-0.001576j +0.001973-0.001336j +0.002063-0.001065j +0.002084-0.000779j +0.002033-0.000497j +0.001914-0.000236j +0.001732-0.000011j +0.001498+0.000162j +0.001225+0.000272j +0.000931+0.000312j +0.000633+0.000277j +0.000349+0.000168j +0.000097-0.000009j +-0.000107-0.000246j +-0.000250-0.000528j +-0.000322-0.000841j +-0.000316-0.001166j +-0.000233-0.001484j +-0.000076-0.001775j +0.000148-0.002024j +0.000426-0.002213j +0.000743-0.002332j +0.001080-0.002373j +0.001418-0.002332j +0.001736-0.002211j +0.002017-0.002017j +0.002241-0.001760j +0.002397-0.001457j +0.002473-0.001124j +0.002464-0.000783j +0.002371-0.000453j +0.002198-0.000156j +0.001954+0.000091j +0.001654+0.000269j +0.001316+0.000369j +0.000959+0.000381j +0.000606+0.000303j +0.000279+0.000139j +-0.000003-0.000103j +-0.000220-0.000411j +-0.000359-0.000768j +-0.000408-0.001152j +-0.000365-0.001542j +-0.000229-0.001915j +-0.000006-0.002248j +0.000292-0.002522j +0.000648-0.002719j +0.001042-0.002826j +0.001452-0.002837j +0.001853-0.002750j +0.002223-0.002569j +0.002537-0.002304j +0.002778-0.001971j +0.002930-0.001588j +0.002984-0.001179j +0.002933-0.000770j +0.002782-0.000384j +0.002538-0.000046j +0.002215+0.000222j +0.001831+0.000402j +0.001410+0.000481j +0.000976+0.000452j +0.000557+0.000316j +0.000179+0.000077j +-0.000134-0.000252j +-0.000362-0.000654j +-0.000490-0.001105j +-0.000506-0.001580j +-0.000407-0.002052j +-0.000197-0.002492j +0.000113-0.002875j +0.000508-0.003176j +0.000966-0.003377j +0.001460-0.003464j +0.001963-0.003432j +0.002444-0.003281j +0.002876-0.003019j +0.003231-0.002659j +0.003488-0.002224j +0.003631-0.001739j +0.003649-0.001233j +0.003540-0.000737j +0.003309-0.000282j +0.002969+0.000103j +0.002539+0.000393j +0.002044+0.000568j +0.001515+0.000614j +0.000982+0.000525j +0.000480+0.000304j +0.000039-0.000039j +-0.000311-0.000487j +-0.000547-0.001016j +-0.000652-0.001595j +-0.000615-0.002193j +-0.000436-0.002773j +-0.000122-0.003301j +0.000311-0.003746j +0.000842-0.004081j +0.001440-0.004282j +0.002071-0.004338j +0.002700-0.004243j +0.003289-0.004000j +0.003803-0.003622j +0.004210-0.003132j +0.004485-0.002556j +0.004610-0.001930j +0.004574-0.001291j +0.004379-0.000679j +0.004034-0.000133j +0.003558+0.000313j +0.002977+0.000627j +0.002327+0.000787j +0.001647+0.000779j +0.000977+0.000598j +0.000360+0.000250j +-0.000163-0.000249j +-0.000559-0.000872j +-0.000799-0.001587j +-0.000864-0.002354j +-0.000744-0.003129j +-0.000442-0.003866j +0.000031-0.004522j +0.000651-0.005057j +0.001385-0.005437j +0.002193-0.005637j +0.003030-0.005643j +0.003848-0.005451j +0.004597-0.005069j +0.005234-0.004519j +0.005718-0.003830j +0.006019-0.003044j +0.006114-0.002206j +0.005997-0.001368j +0.005669-0.000583j +0.005147+0.000099j +0.004460+0.000631j +0.003647+0.000975j +0.002757+0.001105j +0.001842+0.001003j +0.000961+0.000668j +0.000168+0.000111j +-0.000482-0.000643j +-0.000944-0.001555j +-0.001183-0.002578j +-0.001176-0.003656j +-0.000912-0.004727j +-0.000397-0.005728j +0.000346-0.006601j +0.001284-0.007292j +0.002368-0.007755j +0.003540-0.007959j +0.004735-0.007886j +0.005885-0.007535j +0.006922-0.006922j +0.007782-0.006078j +0.008412-0.005050j +0.008769-0.003898j +0.008825-0.002691j +0.008572-0.001504j +0.008016-0.000412j +0.007185+0.000510j +0.006123+0.001196j +0.004891+0.001591j +0.003560+0.001657j +0.002212+0.001372j +0.000933+0.000734j +-0.000192-0.000236j +-0.001083-0.001500j +-0.001672-0.002998j +-0.001905-0.004655j +-0.001747-0.006384j +-0.001182-0.008090j +-0.000222-0.009676j +0.001101-0.011045j +0.002732-0.012114j +0.004593-0.012810j +0.006591-0.013081j +0.008621-0.012896j +0.010571-0.012253j +0.012328-0.011175j +0.013785-0.009712j +0.014847-0.007941j +0.015438-0.005962j +0.015507-0.003893j +0.015029-0.001863j +0.014011-0.000011j +0.012494+0.001529j +0.010549+0.002630j +0.008277+0.003179j +0.005809+0.003090j +0.003294+0.002304j +0.000896+0.000795j +-0.001212-0.001423j +-0.002859-0.004299j +-0.003886-0.007744j +-0.004151-0.011630j +-0.003539-0.015799j +-0.001969-0.020066j +0.000601-0.024225j +0.004168-0.028064j +0.008684-0.031367j +0.014056-0.033927j +0.020145-0.035558j +0.026775-0.036101j +0.033735-0.035433j +0.040789-0.033475j +0.047684-0.030197j +0.054164-0.025619j +0.059974-0.019815j +0.064878-0.012907j +0.068665-0.005067j +0.071159+0.003494j +0.072228+0.012531j +0.071791+0.021776j +0.069818+0.030947j +0.066339+0.039761j +0.061435+0.047944j +0.055243+0.055243j +0.047944+0.061435j +0.039761+0.066339j +0.030947+0.069818j +0.021776+0.071791j +0.012531+0.072228j +0.003494+0.071159j +-0.005067+0.068665j +-0.012907+0.064878j +-0.019815+0.059974j +-0.025619+0.054164j +-0.030197+0.047684j +-0.033475+0.040789j +-0.035433+0.033735j +-0.036101+0.026775j +-0.035558+0.020145j +-0.033927+0.014056j +-0.031367+0.008684j +-0.028064+0.004168j +-0.024225+0.000601j +-0.020066-0.001969j +-0.015799-0.003539j +-0.011630-0.004151j +-0.007744-0.003886j +-0.004299-0.002859j +-0.001423-0.001212j +0.000795+0.000896j +0.002304+0.003294j +0.003090+0.005809j +0.003179+0.008277j +0.002630+0.010549j +0.001529+0.012494j +-0.000011+0.014011j +-0.001863+0.015029j +-0.003893+0.015507j +-0.005962+0.015438j +-0.007941+0.014847j +-0.009712+0.013785j +-0.011175+0.012328j +-0.012253+0.010571j +-0.012896+0.008621j +-0.013081+0.006591j +-0.012810+0.004593j +-0.012114+0.002732j +-0.011045+0.001101j +-0.009676-0.000222j +-0.008090-0.001182j +-0.006384-0.001747j +-0.004655-0.001905j +-0.002998-0.001672j +-0.001500-0.001083j +-0.000236-0.000192j +0.000734+0.000933j +0.001372+0.002212j +0.001657+0.003560j +0.001591+0.004891j +0.001196+0.006123j +0.000510+0.007185j +-0.000412+0.008016j +-0.001504+0.008572j +-0.002691+0.008825j +-0.003898+0.008769j +-0.005050+0.008412j +-0.006078+0.007782j +-0.006922+0.006922j +-0.007535+0.005885j +-0.007886+0.004735j +-0.007959+0.003540j +-0.007755+0.002368j +-0.007292+0.001284j +-0.006601+0.000346j +-0.005728-0.000397j +-0.004727-0.000912j +-0.003656-0.001176j +-0.002578-0.001183j +-0.001555-0.000944j +-0.000643-0.000482j +0.000111+0.000168j +0.000668+0.000961j +0.001003+0.001842j +0.001105+0.002757j +0.000975+0.003647j +0.000631+0.004460j +0.000099+0.005147j +-0.000583+0.005669j +-0.001368+0.005997j +-0.002206+0.006114j +-0.003044+0.006019j +-0.003830+0.005718j +-0.004519+0.005234j +-0.005069+0.004597j +-0.005451+0.003848j +-0.005643+0.003030j +-0.005637+0.002193j +-0.005437+0.001385j +-0.005057+0.000651j +-0.004522+0.000031j +-0.003866-0.000442j +-0.003129-0.000744j +-0.002354-0.000864j +-0.001587-0.000799j +-0.000872-0.000559j +-0.000249-0.000163j +0.000250+0.000360j +0.000598+0.000977j +0.000779+0.001647j +0.000787+0.002327j +0.000627+0.002977j +0.000313+0.003558j +-0.000133+0.004034j +-0.000679+0.004379j +-0.001291+0.004574j +-0.001930+0.004610j +-0.002556+0.004485j +-0.003132+0.004210j +-0.003622+0.003803j +-0.004000+0.003289j +-0.004243+0.002700j +-0.004338+0.002071j +-0.004282+0.001440j +-0.004081+0.000842j +-0.003746+0.000311j +-0.003301-0.000122j +-0.002773-0.000436j +-0.002193-0.000615j +-0.001595-0.000652j +-0.001016-0.000547j +-0.000487-0.000311j +-0.000039+0.000039j +0.000304+0.000480j +0.000525+0.000982j +0.000614+0.001515j +0.000568+0.002044j +0.000393+0.002539j +0.000103+0.002969j +-0.000282+0.003309j +-0.000737+0.003540j +-0.001233+0.003649j +-0.001739+0.003631j +-0.002224+0.003488j +-0.002659+0.003231j +-0.003019+0.002876j +-0.003281+0.002444j +-0.003432+0.001963j +-0.003464+0.001460j +-0.003377+0.000966j +-0.003176+0.000508j +-0.002875+0.000113j +-0.002492-0.000197j +-0.002052-0.000407j +-0.001580-0.000506j +-0.001105-0.000490j +-0.000654-0.000362j +-0.000252-0.000134j +0.000077+0.000179j +0.000316+0.000557j +0.000452+0.000976j +0.000481+0.001410j +0.000402+0.001831j +0.000222+0.002215j +-0.000046+0.002538j +-0.000384+0.002782j +-0.000770+0.002933j +-0.001179+0.002984j +-0.001588+0.002930j +-0.001971+0.002778j +-0.002304+0.002537j +-0.002569+0.002223j +-0.002750+0.001853j +-0.002837+0.001452j +-0.002826+0.001042j +-0.002719+0.000648j +-0.002522+0.000292j +-0.002248-0.000006j +-0.001915-0.000229j +-0.001542-0.000365j +-0.001152-0.000408j +-0.000768-0.000359j +-0.000411-0.000220j +-0.000103-0.000003j +0.000139+0.000279j +0.000303+0.000606j +0.000381+0.000959j +0.000369+0.001316j +0.000269+0.001654j +0.000091+0.001954j +-0.000156+0.002198j +-0.000453+0.002371j +-0.000783+0.002464j +-0.001124+0.002473j +-0.001457+0.002397j +-0.001760+0.002241j +-0.002017+0.002017j +-0.002211+0.001736j +-0.002332+0.001418j +-0.002373+0.001080j +-0.002332+0.000743j +-0.002213+0.000426j +-0.002024+0.000148j +-0.001775-0.000076j +-0.001484-0.000233j +-0.001166-0.000316j +-0.000841-0.000322j +-0.000528-0.000250j +-0.000246-0.000107j +-0.000009+0.000097j +0.000168+0.000349j +0.000277+0.000633j +0.000312+0.000931j +0.000272+0.001225j +0.000162+0.001498j +-0.000011+0.001732j +-0.000236+0.001914j +-0.000497+0.002033j +-0.000779+0.002084j +-0.001065+0.002063j +-0.001336+0.001973j +-0.001576+0.001819j +-0.001772+0.001611j +-0.001911+0.001362j +-0.001987+0.001087j +-0.001994+0.000802j +-0.001934+0.000524j +-0.001810+0.000270j +-0.001630+0.000053j +-0.001406-0.000114j +-0.001151-0.000222j +-0.000880-0.000266j +-0.000609-0.000244j +-0.000355-0.000159j +-0.000131-0.000017j +0.000049+0.000172j +0.000176+0.000396j +0.000243+0.000642j +0.000247+0.000893j +0.000190+0.001135j +0.000075+0.001353j +-0.000090+0.001534j +-0.000293+0.001668j +-0.000522+0.001746j +-0.000762+0.001765j +-0.001000+0.001724j +-0.001219+0.001626j +-0.001408+0.001477j +-0.001556+0.001286j +-0.001653+0.001066j +-0.001694+0.000829j +-0.001677+0.000590j +-0.001604+0.000362j +-0.001480+0.000158j +-0.001312-0.000009j +-0.001111-0.000131j +-0.000889-0.000201j +-0.000659-0.000216j +-0.000434-0.000176j +-0.000228-0.000084j +-0.000053+0.000053j +0.000083+0.000226j +0.000170+0.000424j +0.000206+0.000635j +0.000188+0.000846j +0.000120+0.001044j +0.000005+0.001217j +-0.000148+0.001354j +-0.000330+0.001449j +-0.000529+0.001496j +-0.000733+0.001491j +-0.000929+0.001437j +-0.001106+0.001336j +-0.001252+0.001195j +-0.001361+0.001022j +-0.001424+0.000829j +-0.001439+0.000626j +-0.001406+0.000426j +-0.001326+0.000240j +-0.001206+0.000079j +-0.001051-0.000048j +-0.000872-0.000134j +-0.000680-0.000175j +-0.000486-0.000169j +-0.000301-0.000117j +-0.000136-0.000024j +0.000000+0.000105j +0.000099+0.000261j +0.000156+0.000435j +0.000168+0.000614j +0.000136+0.000790j +0.000062+0.000950j +-0.000049+0.001085j +-0.000189+0.001188j +-0.000350+0.001252j +-0.000521+0.001274j +-0.000692+0.001253j +-0.000853+0.001191j +-0.000993+0.001091j +-0.001105+0.000960j +-0.001182+0.000805j +-0.001219+0.000637j +-0.001216+0.000465j +-0.001171+0.000300j +-0.001089+0.000150j +-0.000974+0.000024j +-0.000834-0.000070j +-0.000677-0.000128j +-0.000513-0.000147j +-0.000350-0.000126j +-0.000199-0.000068j +-0.000068+0.000024j +0.000035+0.000143j +0.000105+0.000281j +0.000138+0.000431j +0.000133+0.000582j +0.000091+0.000726j +0.000016+0.000854j +-0.000089+0.000958j +-0.000215+0.001032j +-0.000355+0.001072j +-0.000501+0.001076j +-0.000643+0.001044j +-0.000772+0.000978j +-0.000882+0.000882j +-0.000965+0.000762j +-0.001016+0.000626j +-0.001034+0.000481j +-0.001017+0.000337j +-0.000966+0.000201j +-0.000885+0.000082j +-0.000778-0.000014j +-0.000653-0.000082j +-0.000517-0.000118j +-0.000378-0.000121j +-0.000243-0.000090j +-0.000122-0.000029j +-0.000020+0.000058j +0.000056+0.000165j +0.000103+0.000287j +0.000119+0.000415j +0.000102+0.000540j +0.000055+0.000657j +-0.000019+0.000757j +-0.000115+0.000835j +-0.000227+0.000886j +-0.000347+0.000907j +-0.000469+0.000898j +-0.000585+0.000859j +-0.000688+0.000792j +-0.000772+0.000702j +-0.000831+0.000595j +-0.000863+0.000477j +-0.000866+0.000354j +-0.000839+0.000235j +-0.000786+0.000125j +-0.000708+0.000032j +-0.000611-0.000040j +-0.000501-0.000087j +-0.000385-0.000106j +-0.000268-0.000097j +-0.000159-0.000061j +-0.000063-0.000001j +0.000015+0.000080j +0.000070+0.000175j +0.000099+0.000280j +0.000101+0.000387j +0.000077+0.000490j +0.000028+0.000582j +-0.000042+0.000659j +-0.000128+0.000715j +-0.000225+0.000748j +-0.000327+0.000755j +-0.000428+0.000737j +-0.000521+0.000694j +-0.000601+0.000630j +-0.000663+0.000548j +-0.000703+0.000454j +-0.000720+0.000352j +-0.000712+0.000250j +-0.000681+0.000152j +-0.000627+0.000065j +-0.000555-0.000006j +-0.000469-0.000058j +-0.000374-0.000088j +-0.000275-0.000095j +-0.000180-0.000078j +-0.000092-0.000040j +-0.000017+0.000017j +0.000040+0.000090j +0.000078+0.000173j +0.000093+0.000261j +0.000086+0.000350j +0.000058+0.000432j +0.000010+0.000504j +-0.000054+0.000561j +-0.000130+0.000600j +-0.000213+0.000619j +-0.000297+0.000616j +-0.000379+0.000592j +-0.000452+0.000548j +-0.000513+0.000488j +-0.000557+0.000415j +-0.000582+0.000334j +-0.000588+0.000248j +-0.000573+0.000164j +-0.000538+0.000086j +-0.000487+0.000019j +-0.000422-0.000035j +-0.000347-0.000071j +-0.000266-0.000088j +-0.000185-0.000086j +-0.000107-0.000065j +-0.000038-0.000027j +0.000018+0.000025j +0.000060+0.000089j +0.000083+0.000160j +0.000089+0.000233j +0.000076+0.000304j +0.000046+0.000369j +0.000001+0.000423j +-0.000056+0.000464j +-0.000121+0.000489j +-0.000190+0.000497j +-0.000259+0.000487j +-0.000323+0.000461j +-0.000379+0.000419j +-0.000423+0.000365j +-0.000453+0.000301j +-0.000467+0.000232j +-0.000465+0.000161j +-0.000445+0.000093j +-0.000411+0.000032j +-0.000364-0.000019j +-0.000306-0.000058j +-0.000242-0.000081j +-0.000175-0.000090j +-0.000109-0.000082j +-0.000048-0.000059j +0.000005-0.000023j +0.000047+0.000024j +0.000075+0.000078j +0.000089+0.000137j +0.000088+0.000196j +0.000072+0.000252j +0.000042+0.000302j +0.000002+0.000341j +-0.000048+0.000369j +-0.000102+0.000384j +-0.000158+0.000384j +-0.000213+0.000370j +-0.000262+0.000343j +-0.000304+0.000304j +-0.000335+0.000256j +-0.000354+0.000202j +-0.000359+0.000145j +-0.000351+0.000088j +-0.000330+0.000034j +-0.000298-0.000012j +-0.000255-0.000050j +-0.000206-0.000077j +-0.000152-0.000091j +-0.000098-0.000093j +-0.000046-0.000082j +0.000002-0.000059j +0.000041-0.000027j +0.000071+0.000014j +0.000089+0.000059j +0.000095+0.000106j +0.000090+0.000153j +0.000073+0.000196j +0.000045+0.000232j +0.000010+0.000260j +-0.000031+0.000278j +-0.000075+0.000284j +-0.000120+0.000279j +-0.000162+0.000263j +-0.000199+0.000237j +-0.000228+0.000203j +-0.000249+0.000162j +-0.000259+0.000117j +-0.000259+0.000071j +-0.000248+0.000026j +-0.000227-0.000014j +-0.000197-0.000049j +-0.000161-0.000076j +-0.000120-0.000093j +-0.000077-0.000101j +-0.000034-0.000098j +0.000006-0.000086j +0.000042-0.000064j +0.000070-0.000036j +0.000090-0.000002j +0.000101+0.000034j +0.000102+0.000071j +0.000093+0.000107j +0.000077+0.000139j +0.000052+0.000165j +0.000022+0.000184j +-0.000011+0.000194j +-0.000046+0.000196j +-0.000081+0.000188j +-0.000113+0.000173j +-0.000140+0.000150j +-0.000161+0.000121j +-0.000174+0.000089j +-0.000180+0.000054j +-0.000177+0.000020j +-0.000167-0.000013j +-0.000150-0.000041j +-0.000127-0.000063j +-0.000100-0.000077j +-0.000071-0.000084j +-0.000044-0.000080j +-0.000020-0.000066j +-0.000009-0.000035j diff --git a/gnuradio-examples/python/apps/hf_radio/ssbagc.py b/gnuradio-examples/python/apps/hf_radio/ssbagc.py new file mode 100644 index 000000000..fdf40bc6b --- /dev/null +++ b/gnuradio-examples/python/apps/hf_radio/ssbagc.py @@ -0,0 +1,48 @@ +# post detection agc processing +# +# Imagine that the usual gnuradio copyright stuff is right here. +# +# This agc strategy is copied more or less verbatim from +# weaver_isb_am1_usrp3.py by cswiger. +# +# Thanks. +# +# Then modified in a variety of ways. +# +# There doesn't appear to be a way to hook multiple blocks to the +# input port when building a hier block like this. Thus the +# split below. +# +# Basic operation. +# Power is estimated by squaring the input. +# Low pass filter using a 1 pole iir. +# The time constant can be tweaked by changing the taps. +# Currently there is no implementation to change this while operating +# a potentially useful addition. +# The log block turns this into dB +# gain adjusts the agc authority. +# +# M. Revnell 2006-Jan + +from gnuradio import gr, gru + +class agc( gr.hier_block ): + def __init__( self, fg ): + self.split = gr.multiply_const_ff( 1 ) + self.sqr = gr.multiply_ff( ) + self.int0 = gr.iir_filter_ffd( [.004, 0], [0, .999] ) + self.offs = gr.add_const_ff( -30 ) + self.gain = gr.multiply_const_ff( 70 ) + self.log = gr.nlog10_ff( 10, 1 ) + self.agc = gr.divide_ff( ) + + fg.connect( self.split, ( self.agc, 0 ) ) + fg.connect( self.split, ( self.sqr, 0 ) ) + fg.connect( self.split, ( self.sqr, 1 ) ) + fg.connect( self.sqr, self.int0 ) + fg.connect( self.int0, self.log ) + fg.connect( self.log, self.offs ) + fg.connect( self.offs, self.gain ) + fg.connect( self.gain, ( self.agc, 1 ) ) + + gr.hier_block.__init__( self, fg, self.split, self.agc ) diff --git a/gnuradio-examples/python/apps/hf_radio/ssbdemod.py b/gnuradio-examples/python/apps/hf_radio/ssbdemod.py new file mode 100644 index 000000000..c73567b66 --- /dev/null +++ b/gnuradio-examples/python/apps/hf_radio/ssbdemod.py @@ -0,0 +1,97 @@ +# This tries to push the hilbert transform for ssb demod back into the +# freq. xlating filter. +# +# The usual gnuradio copyright notice is hereby included by reference. +# +# The starting point for this was weaver_isb_am1_usrp3.py. +# +# The tap coefficients for freq_xlating_fir_filter_ccf were generated +# externally and are read from a file because I didn't want to learn how +# to make fir filters with arbitrary phase response using python numeric +# facilities. +# +# They were generated using Scilab which I am already familiar with. +# M. Revnell Jan 06 + +from gnuradio import gr, gru +from gnuradio import audio +from gnuradio import usrp + +class ssb_demod( gr.hier_block ): + def __init__( self, fg, if_rate, af_rate ): + + self.if_rate = if_rate + self.af_rate = af_rate + self.if_decim = if_rate / af_rate + self.sideband = 1 + + self.xlate_taps = ([complex(v) for v in file('ssb_taps').readlines()]) + + self.audio_taps = gr.firdes.low_pass( + 1.0, + self.af_rate, + 3e3, + 600, + gr.firdes.WIN_HAMMING ) + + self.xlate = gr.freq_xlating_fir_filter_ccc( + self.if_decim, + self.xlate_taps, + 0, + self.if_rate ) + + self.split = gr.complex_to_float() + + self.lpf = gr.fir_filter_fff( + 1, self.audio_taps ) + + self.sum = gr.add_ff( ) + self.am_sel = gr.multiply_const_ff( 0 ) + self.sb_sel = gr.multiply_const_ff( 1 ) + self.mixer = gr.add_ff() + self.am_det = gr.complex_to_mag() + + fg.connect( self.xlate, self.split ) + fg.connect( ( self.split,0 ), ( self.sum,0 ) ) + fg.connect( ( self.split,1 ), ( self.sum,1 ) ) + fg.connect( self.sum, self.sb_sel ) + fg.connect( self.xlate, self.am_det ) + fg.connect( self.sb_sel, ( self.mixer, 0 ) ) + fg.connect( self.am_det, self.am_sel ) + fg.connect( self.am_sel, ( self.mixer, 1 ) ) + fg.connect( self.mixer, self.lpf ) + + gr.hier_block.__init__( self, fg, self.xlate, self.lpf ) + + def upper_sb( self ): + self.xlate.set_taps([v.conjugate() for v in self.xlate_taps]) + self.sb_sel.set_k( 1.0 ) + self.am_sel.set_k( 0.0 ) + + def lower_sb( self ): + self.xlate.set_taps(self.xlate_taps) + self.sb_sel.set_k( 1.0 ) + self.am_sel.set_k( 0.0 ) + + def set_am( self ): + taps = gr.firdes.low_pass( 1.0, + self.if_rate, + 5e3, + 2e3, + gr.firdes.WIN_HAMMING ) + self.xlate.set_taps( taps ) + self.sb_sel.set_k( 0.0 ) + self.am_sel.set_k( 1.0 ) + + def set_bw( self, bw ): + self.audio_taps = gr.firdes.low_pass( + 1.0, + self.af_rate, + bw, + 600, + gr.firdes.WIN_HAMMING ) + self.lpf.set_taps( self.audio_taps ) + + def tune( self, freq ): + self.xlate.set_center_freq( freq ) + diff --git a/gnuradio-examples/python/apps/hf_radio/startup.py b/gnuradio-examples/python/apps/hf_radio/startup.py new file mode 100644 index 000000000..093369b57 --- /dev/null +++ b/gnuradio-examples/python/apps/hf_radio/startup.py @@ -0,0 +1 @@ +from radio import * diff --git a/gnuradio-examples/python/apps/hf_radio/ui.py b/gnuradio-examples/python/apps/hf_radio/ui.py new file mode 100755 index 000000000..71b73c128 --- /dev/null +++ b/gnuradio-examples/python/apps/hf_radio/ui.py @@ -0,0 +1,295 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +# generated by wxGlade 0.4 on Mon Jan 2 19:02:03 2006 + +import wx + +class ui_frame(wx.Frame): + def __init__(self, *args, **kwds): + # begin wxGlade: ui_frame.__init__ + kwds["style"] = wx.DEFAULT_FRAME_STYLE + wx.Frame.__init__(self, *args, **kwds) + self.sizer_1_staticbox = wx.StaticBox(self, -1, "sizer_1") + self.freq_disp = wx.SpinCtrl(self, -1, "", min=0, max=100) + self.spin_e6 = wx.SpinButton(self, -1 ) + self.spin_e5 = wx.SpinButton(self, -1 ) + self.spin_e4 = wx.SpinButton(self, -1 ) + self.spin_e3 = wx.SpinButton(self, -1 ) + self.spin_e2 = wx.SpinButton(self, -1 ) + self.spin_e1 = wx.SpinButton(self, -1 ) + self.spin_e0 = wx.SpinButton(self, -1 ) + self.panel_1 = wx.Panel(self, -1) + self.panel_2 = wx.Panel(self, -1) + self.button_lsb = wx.Button(self, -1, "LSB") + self.button_usb = wx.Button(self, -1, "USB") + self.button_am = wx.Button(self, -1, "AM") + self.label_1 = wx.StaticText(self, -1, "VOLUME") + self.label_2 = wx.StaticText(self, -1, "PGA") + self.agc_level = wx.TextCtrl(self, -1, "") + self.label_6 = wx.StaticText(self, -1, "") + self.volume = wx.SpinCtrl(self, -1, "", min=0, max=100) + self.pga = wx.SpinCtrl(self, -1, "", min=0, max=100) + self.agc_max = wx.TextCtrl(self, -1, "") + self.label_7 = wx.StaticText(self, -1, "") + self.label_4 = wx.StaticText(self, -1, "AGC AUTHORITY") + self.label_5 = wx.StaticText(self, -1, "AGC REF LVL") + self.label_3 = wx.StaticText(self, -1, "BANDWIDTH") + self.label_8 = wx.StaticText(self, -1, "") + self.agc_gain = wx.TextCtrl(self, -1, "") + self.agc_gain_s = wx.SpinButton(self, -1 ) + self.agc_ref = wx.TextCtrl(self, -1, "") + self.agc_ref_s = wx.SpinButton(self, -1 ) + self.bandwidth = wx.TextCtrl(self, -1, "") + self.bw_spin = wx.SpinButton(self, -1 ) + self.label_9 = wx.StaticText(self, -1, "") + self.rssi = wx.Gauge(self, -1, 10, style=wx.GA_HORIZONTAL|wx.GA_SMOOTH) + self.fe_panel = wx.Panel(self, -1) + self.if_panel = wx.Panel(self, -1) + + self.__set_properties() + self.__do_layout() + + self.Bind(wx.EVT_SPINCTRL, self.tune_evt, self.freq_disp) + self.Bind(wx.EVT_SPIN_DOWN, self.down_e6, self.spin_e6) + self.Bind(wx.EVT_SPIN_UP, self.up_e6, self.spin_e6) + self.Bind(wx.EVT_SPIN_DOWN, self.down_e5, self.spin_e5) + self.Bind(wx.EVT_SPIN_UP, self.up_e5, self.spin_e5) + self.Bind(wx.EVT_SPIN_DOWN, self.down_e4, self.spin_e4) + self.Bind(wx.EVT_SPIN_UP, self.up_e4, self.spin_e4) + self.Bind(wx.EVT_SPIN_DOWN, self.down_e3, self.spin_e3) + self.Bind(wx.EVT_SPIN_UP, self.up_e3, self.spin_e3) + self.Bind(wx.EVT_SPIN_DOWN, self.down_e2, self.spin_e2) + self.Bind(wx.EVT_SPIN_UP, self.up_e2, self.spin_e2) + self.Bind(wx.EVT_SPIN_DOWN, self.down_e1, self.spin_e1) + self.Bind(wx.EVT_SPIN_UP, self.up_e1, self.spin_e1) + self.Bind(wx.EVT_SPIN_DOWN, self.down_e0, self.spin_e0) + self.Bind(wx.EVT_SPIN_UP, self.up_e0, self.spin_e0) + self.Bind(wx.EVT_BUTTON, self.set_lsb, self.button_lsb) + self.Bind(wx.EVT_BUTTON, self.set_usb, self.button_usb) + self.Bind(wx.EVT_BUTTON, self.set_am, self.button_am) + self.Bind(wx.EVT_SPINCTRL, self.event_vol, self.volume) + self.Bind(wx.EVT_SPINCTRL, self.event_pga, self.pga) + self.Bind(wx.EVT_SPIN_DOWN, self.agc_gain_down, self.agc_gain_s) + self.Bind(wx.EVT_SPIN_UP, self.agc_gain_up, self.agc_gain_s) + self.Bind(wx.EVT_SPIN_DOWN, self.agc_ref_down, self.agc_ref_s) + self.Bind(wx.EVT_SPIN_UP, self.agc_ref_up, self.agc_ref_s) + self.Bind(wx.EVT_SPIN_DOWN, self.bw_down, self.bw_spin) + self.Bind(wx.EVT_SPIN_UP, self.bw_up, self.bw_spin) + # end wxGlade + + def __set_properties(self): + # begin wxGlade: ui_frame.__set_properties + self.SetTitle("frame_1") + self.freq_disp.SetFont(wx.Font(32, wx.DEFAULT, wx.NORMAL, wx.NORMAL, 0, "")) + self.rssi.SetMinSize((315, 10)) + self.rssi.SetForegroundColour(wx.Colour(255, 0, 0)) + # end wxGlade + + def __do_layout(self): + # begin wxGlade: ui_frame.__do_layout + sizer_1 = wx.StaticBoxSizer(self.sizer_1_staticbox, wx.VERTICAL) + sizer_2 = wx.BoxSizer(wx.HORIZONTAL) + sizer_4 = wx.BoxSizer(wx.VERTICAL) + grid_sizer_4 = wx.GridSizer(2, 4, 0, 0) + sizer_5 = wx.BoxSizer(wx.HORIZONTAL) + sizer_7 = wx.BoxSizer(wx.HORIZONTAL) + sizer_6 = wx.BoxSizer(wx.HORIZONTAL) + grid_sizer_3 = wx.GridSizer(2, 4, 0, 0) + sizer_3 = wx.BoxSizer(wx.VERTICAL) + grid_sizer_1 = wx.GridSizer(2, 3, 0, 0) + grid_sizer_2 = wx.GridSizer(1, 7, 0, 0) + sizer_3.Add(self.freq_disp, 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0) + grid_sizer_2.Add(self.spin_e6, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_2.Add(self.spin_e5, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_2.Add(self.spin_e4, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_2.Add(self.spin_e3, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_2.Add(self.spin_e2, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_2.Add(self.spin_e1, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_2.Add(self.spin_e0, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_1.Add(grid_sizer_2, 1, wx.EXPAND, 0) + grid_sizer_1.Add(self.panel_1, 1, wx.EXPAND, 0) + grid_sizer_1.Add(self.panel_2, 1, wx.EXPAND, 0) + grid_sizer_1.Add(self.button_lsb, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_1.Add(self.button_usb, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_1.Add(self.button_am, 0, wx.ADJUST_MINSIZE, 0) + sizer_3.Add(grid_sizer_1, 1, wx.EXPAND, 0) + sizer_2.Add(sizer_3, 1, wx.EXPAND, 0) + grid_sizer_3.Add(self.label_1, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_3.Add(self.label_2, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_3.Add(self.agc_level, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_3.Add(self.label_6, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_3.Add(self.volume, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_3.Add(self.pga, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_3.Add(self.agc_max, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_3.Add(self.label_7, 0, wx.ADJUST_MINSIZE, 0) + sizer_4.Add(grid_sizer_3, 1, wx.EXPAND, 0) + grid_sizer_4.Add(self.label_4, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_4.Add(self.label_5, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_4.Add(self.label_3, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_4.Add(self.label_8, 0, wx.ADJUST_MINSIZE, 0) + sizer_6.Add(self.agc_gain, 0, wx.ADJUST_MINSIZE, 0) + sizer_6.Add(self.agc_gain_s, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_4.Add(sizer_6, 1, wx.EXPAND, 0) + sizer_7.Add(self.agc_ref, 0, wx.ADJUST_MINSIZE, 0) + sizer_7.Add(self.agc_ref_s, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_4.Add(sizer_7, 1, wx.EXPAND, 0) + sizer_5.Add(self.bandwidth, 0, wx.ADJUST_MINSIZE, 0) + sizer_5.Add(self.bw_spin, 0, wx.ADJUST_MINSIZE, 0) + grid_sizer_4.Add(sizer_5, 1, wx.EXPAND, 0) + grid_sizer_4.Add(self.label_9, 0, wx.ADJUST_MINSIZE, 0) + sizer_4.Add(grid_sizer_4, 1, wx.EXPAND, 0) + sizer_4.Add(self.rssi, 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0) + sizer_2.Add(sizer_4, 1, wx.EXPAND, 0) + sizer_1.Add(sizer_2, 1, wx.EXPAND, 0) + sizer_1.Add(self.fe_panel, 1, wx.EXPAND, 0) + sizer_1.Add(self.if_panel, 1, wx.EXPAND, 0) + self.SetAutoLayout(True) + self.SetSizer(sizer_1) + sizer_1.Fit(self) + sizer_1.SetSizeHints(self) + self.Layout() + # end wxGlade + + def down_e6(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `down_e6' not implemented" + event.Skip() + + def up_e6(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `up_e6' not implemented" + event.Skip() + + def down_e5(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `down_e5' not implemented" + event.Skip() + + def up_e5(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `up_e5' not implemented" + event.Skip() + + def down_e4(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `down_e4' not implemented" + event.Skip() + + def up_e4(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `up_e4' not implemented" + event.Skip() + + def down_e3(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `down_e3' not implemented" + event.Skip() + + def up_e3(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `up_e3' not implemented" + event.Skip() + + def down_e2(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `down_e2' not implemented" + event.Skip() + + def up_e2(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `up_e2' not implemented" + event.Skip() + + def down_e1(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `down_e1' not implemented" + event.Skip() + + def up_e1(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `up_e1' not implemented" + event.Skip() + + def down_e0(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `down_e0' not implemented" + event.Skip() + + def up_e0(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `up_e0' not implemented" + event.Skip() + + def event_vol(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `event_vol' not implemented" + event.Skip() + + def event_pga(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `event_pga' not implemented" + event.Skip() + + def set_lsb(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `set_lsb' not implemented" + event.Skip() + + def set_usb(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `set_usb' not implemented" + event.Skip() + + def set_am(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `set_am' not implemented" + event.Skip() + + def set_bw(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `set_bw' not implemented" + event.Skip() + + def tune_evt(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `tune_evt' not implemented" + event.Skip() + + def bw_down(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `bw_down' not implemented" + event.Skip() + + def bw_up(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `bw_up' not implemented" + event.Skip() + + def agc_gain_down(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `agc_gain_down' not implemented" + event.Skip() + + def agc_gain_up(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `agc_gain_up' not implemented" + event.Skip() + + def agc_ref_down(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `agc_ref_down' not implemented" + event.Skip() + + def agc_ref_up(self, event): # wxGlade: ui_frame.<event_handler> + print "Event handler `agc_ref_up' not implemented" + event.Skip() + +# end of class ui_frame + + +class RadioFrame(wx.Frame): + def __init__(self, *args, **kwds): + # content of this block not found: did you rename this class? + pass + + def __set_properties(self): + # content of this block not found: did you rename this class? + pass + + def __do_layout(self): + # content of this block not found: did you rename this class? + pass + +# end of class RadioFrame + + +class MyFrame(wx.Frame): + def __init__(self, *args, **kwds): + # content of this block not found: did you rename this class? + pass + + def __set_properties(self): + # content of this block not found: did you rename this class? + pass + + def __do_layout(self): + # content of this block not found: did you rename this class? + pass + +# end of class MyFrame + + diff --git a/gnuradio-examples/python/audio/Makefile.am b/gnuradio-examples/python/audio/Makefile.am new file mode 100644 index 000000000..27cea62a3 --- /dev/null +++ b/gnuradio-examples/python/audio/Makefile.am @@ -0,0 +1,32 @@ +# +# 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. +# + +EXTRA_DIST = \ + audio_copy.py \ + audio_play.py \ + audio_to_file.py \ + dial_tone.py \ + mono_tone.py \ + multi_tone.py \ + spectrum_inversion.py \ + test_resampler.py + + diff --git a/gnuradio-examples/python/audio/audio_copy.py b/gnuradio-examples/python/audio/audio_copy.py new file mode 100755 index 000000000..56fa836d2 --- /dev/null +++ b/gnuradio-examples/python/audio/audio_copy.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# +# 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. +# + +from gnuradio import gr +from gnuradio import audio +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +class my_graph(gr.flow_graph): + + def __init__(self): + gr.flow_graph.__init__(self) + + parser = OptionParser(option_class=eng_option) + parser.add_option("-I", "--audio-input", type="string", default="", + help="pcm input device name. E.g., hw:0,0 or /dev/dsp") + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm output device name. E.g., hw:0,0 or /dev/dsp") + parser.add_option("-r", "--sample-rate", type="eng_float", default=48000, + help="set sample rate to RATE (48000)") + (options, args) = parser.parse_args () + if len(args) != 0: + parser.print_help() + raise SystemExit, 1 + + sample_rate = int(options.sample_rate) + src = audio.source (sample_rate, options.audio_input) + dst = audio.sink (sample_rate, options.audio_output) + + # Determine the maximum number of outputs on the source and + # maximum number of inputs on the sink, then connect together + # the most channels we can without overlap + nchan = min (src.output_signature().max_streams(), + dst.input_signature().max_streams()) + + for i in range (nchan): + self.connect ((src, i), (dst, i)) + + +if __name__ == '__main__': + try: + my_graph().run() + except KeyboardInterrupt: + pass + diff --git a/gnuradio-examples/python/audio/audio_play.py b/gnuradio-examples/python/audio/audio_play.py new file mode 100755 index 000000000..e70b0f942 --- /dev/null +++ b/gnuradio-examples/python/audio/audio_play.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# +# 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. +# + +from gnuradio import gr +from gnuradio import audio +from gnuradio.eng_option import eng_option +from optparse import OptionParser + + +class my_graph(gr.flow_graph): + + def __init__(self): + gr.flow_graph.__init__(self) + + parser = OptionParser(option_class=eng_option) + parser.add_option("-F", "--filename", type="string", default="audio.dat", + help="read input from FILE") + parser.add_option("-r", "--sample-rate", type="eng_float", default=48000, + help="set sample rate to RATE (48000)") + parser.add_option("-R", "--repeat", action="store_true", default=False) + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm output device name. E.g., hw:0,0 or /dev/dsp") + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + raise SystemExit, 1 + + sample_rate = int(options.sample_rate) + src = gr.file_source (gr.sizeof_float, options.filename, options.repeat) + dst = audio.sink (sample_rate, options.audio_output) + self.connect(src, dst) + + +if __name__ == '__main__': + try: + my_graph().run() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/audio/audio_to_file.py b/gnuradio-examples/python/audio/audio_to_file.py new file mode 100755 index 000000000..74e1ea7ac --- /dev/null +++ b/gnuradio-examples/python/audio/audio_to_file.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# +# 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. +# + +from gnuradio import gr +from gnuradio import audio +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +class my_graph(gr.flow_graph): + + def __init__(self): + gr.flow_graph.__init__(self) + + usage="%prog: [options] output_filename" + parser = OptionParser(option_class=eng_option, usage=usage) + parser.add_option("-I", "--audio-input", type="string", default="", + help="pcm input device name. E.g., hw:0,0 or /dev/dsp") + parser.add_option("-r", "--sample-rate", type="eng_float", default=48000, + help="set sample rate to RATE (48000)") + parser.add_option("-N", "--nsamples", type="eng_float", default=None, + help="number of samples to collect [default=+inf]") + + (options, args) = parser.parse_args () + if len(args) != 1: + parser.print_help() + raise SystemExit, 1 + filename = args[0] + + sample_rate = int(options.sample_rate) + src = audio.source (sample_rate, options.audio_input) + dst = gr.file_sink (gr.sizeof_float, filename) + + if options.nsamples is None: + self.connect((src, 0), dst) + else: + head = gr.head(gr.sizeof_float, int(options.nsamples)) + self.connect((src, 0), head, dst) + + +if __name__ == '__main__': + try: + my_graph().run() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/audio/dial_squelch.py b/gnuradio-examples/python/audio/dial_squelch.py new file mode 100755 index 000000000..d947e5860 --- /dev/null +++ b/gnuradio-examples/python/audio/dial_squelch.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python + +# Copyright 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. +# + +from gnuradio import gr, audio, eng_option +from gnuradio.eng_option import eng_option +from math import pi, cos +from optparse import OptionParser + +""" +This script generates a standard dial tone and then applies a sinusoidal +envelope to vary it's loudness. The audio is then passed through the +power squelch block before it gets sent to the sound card. By varying +the command line parameters, one can see the effect of differing +amounts of power averaging, threshold, and attack/decay ramping. +""" + +class app_flow_graph(gr.flow_graph): + def __init__(self, options, args): + gr.flow_graph.__init__(self) + + # Create dial tone by adding two sine waves + SRC1 = gr.sig_source_f(options.rate, gr.GR_SIN_WAVE, 350, 0.5, 0.0) + SRC2 = gr.sig_source_f(options.rate, gr.GR_SIN_WAVE, 440, 0.5, 0.0) + ADD = gr.add_ff() + + # Convert to vector stream (and back) to apply raised cosine envelope + # You could also do this with a vector_source_f block that repeats. + S2V = gr.stream_to_vector(gr.sizeof_float, options.rate) + ENV = [0.5-cos(2*pi*x/options.rate)/2 for x in range(options.rate)] + MLT = gr.multiply_const_vff(ENV) + V2S = gr.vector_to_stream(gr.sizeof_float, options.rate) + + # Run through power squelch with user supplied or default options + # Zero output when squelch is invoked + SQL = gr.pwr_squelch_ff(options.threshold, options.alpha, options.ramp, False) + DST = audio.sink(options.rate) + + # Solder it all together + self.connect(SRC1, (ADD, 0)) + self.connect(SRC2, (ADD, 1)) + self.connect(ADD, S2V, MLT, V2S, SQL, DST) + +def main(): + parser = OptionParser(option_class=eng_option) + parser.add_option("-r", "--rate", type="int", default=8000, help="set audio output sample rate to RATE", metavar="RATE") + parser.add_option("-t", "--threshold", type="eng_float", default=-10.0, help="set power squelch to DB", metavar="DB") + parser.add_option("-a", "--alpha", type="eng_float", default=None, help="set alpha to ALPHA", metavar="ALPHA") + parser.add_option("-m", "--ramp", type="int", default=None, help="set attack/decay ramp to SAMPLES", metavar="SAMPLES") + (options, args) = parser.parse_args() + + if options.alpha == None: + options.alpha = 50.0/options.rate + + if options.ramp == None: + options.ramp = options.rate/50 # ~ 20 ms + + print "Using audio rate of", options.rate + print "Using threshold of", options.threshold, "db" + print "Using alpha of", options.alpha + print "Using ramp of", options.ramp, "samples" + + fg = app_flow_graph(options, args) + + try: + fg.run() + except KeyboardInterrupt: + pass + +if __name__ == "__main__": + main() diff --git a/gnuradio-examples/python/audio/dial_tone.py b/gnuradio-examples/python/audio/dial_tone.py new file mode 100755 index 000000000..3ce84eb59 --- /dev/null +++ b/gnuradio-examples/python/audio/dial_tone.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# +# 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. +# + +from gnuradio import gr +from gnuradio import audio +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +class my_graph(gr.flow_graph): + + def __init__(self): + gr.flow_graph.__init__(self) + + parser = OptionParser(option_class=eng_option) + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm output device name. E.g., hw:0,0 or /dev/dsp") + parser.add_option("-r", "--sample-rate", type="eng_float", default=48000, + help="set sample rate to RATE (48000)") + (options, args) = parser.parse_args () + if len(args) != 0: + parser.print_help() + raise SystemExit, 1 + + sample_rate = int(options.sample_rate) + ampl = 0.1 + + src0 = gr.sig_source_f (sample_rate, gr.GR_SIN_WAVE, 350, ampl) + src1 = gr.sig_source_f (sample_rate, gr.GR_SIN_WAVE, 440, ampl) + dst = audio.sink (sample_rate, options.audio_output) + self.connect (src0, (dst, 0)) + self.connect (src1, (dst, 1)) + + +if __name__ == '__main__': + try: + my_graph().run() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/audio/dialtone_v.py b/gnuradio-examples/python/audio/dialtone_v.py new file mode 100755 index 000000000..e704414a9 --- /dev/null +++ b/gnuradio-examples/python/audio/dialtone_v.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python + +# Copyright 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. +# + +from gnuradio import gr, audio +from math import pi, sin + +""" +This test script demonstrates the use of element-wise vector processing +vs. stream processing. The example is artificial in that the stream +version in dial_tone.py is the normal way to do it; in addition, the +envelope processing here is just for demo purposes and isn't needed. +""" + +# For testing different buffer sizes +rate = 48000 + +fg = gr.flow_graph() + +# Two streams of floats +a = gr.sig_source_f(rate, gr.GR_SIN_WAVE, 350, 0.5, 0.0); +b = gr.sig_source_f(rate, gr.GR_SIN_WAVE, 440, 0.5, 0.0); + +# Turn them into vectors of length 'size' +av = gr.stream_to_vector(gr.sizeof_float, rate) +bv = gr.stream_to_vector(gr.sizeof_float, rate) + +# Make a vector adder for float vectors +adder = gr.add_vff(rate) + +# Make a 1 Hz sine envelope +envelope = [sin(2*pi*x/rate)*0.5 for x in range(rate)] +multiplier = gr.multiply_const_vff(envelope) + +# Make an offset adder +offset = gr.add_const_vff((0.5,)*rate) + +# Turn the vector back into a stream of floats +result = gr.vector_to_stream(gr.sizeof_float, rate) + +# Play it +sink = audio.sink(rate) + +fg.connect(a, av) +fg.connect(b, bv) +fg.connect(av, (adder, 0)) +fg.connect(bv, (adder, 1)) +fg.connect(adder, multiplier, offset, result, sink) + +try: + fg.run() +except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/audio/mono_tone.py b/gnuradio-examples/python/audio/mono_tone.py new file mode 100755 index 000000000..84c3b542e --- /dev/null +++ b/gnuradio-examples/python/audio/mono_tone.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# +# 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. +# + +from gnuradio import gr +from gnuradio import audio +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +#import os +#print os.getpid() +#raw_input('Attach gdb and press Enter: ') + +class my_graph(gr.flow_graph): + + def __init__(self): + gr.flow_graph.__init__(self) + + parser = OptionParser(option_class=eng_option) + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm output device name. E.g., hw:0,0 or /dev/dsp") + parser.add_option("-r", "--sample-rate", type="eng_float", default=48000, + help="set sample rate to RATE (48000)") + parser.add_option("-D", "--dont-block", action="store_false", default=True, + dest="ok_to_block") + + (options, args) = parser.parse_args () + if len(args) != 0: + parser.print_help() + raise SystemExit, 1 + + sample_rate = int(options.sample_rate) + ampl = 0.1 + + src0 = gr.sig_source_f (sample_rate, gr.GR_SIN_WAVE, 650, ampl) + + dst = audio.sink (sample_rate, + options.audio_output, + options.ok_to_block) + + self.connect (src0, (dst, 0)) + + +if __name__ == '__main__': + try: + my_graph().run() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/audio/multi_tone.py b/gnuradio-examples/python/audio/multi_tone.py new file mode 100755 index 000000000..c6f83050e --- /dev/null +++ b/gnuradio-examples/python/audio/multi_tone.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python +# +# 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. +# + +from gnuradio import gr +from gnuradio import audio +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +#import os +#print os.getpid() +#raw_input('Attach gdb and press Enter: ') + +class my_graph(gr.flow_graph): + + def __init__(self): + gr.flow_graph.__init__(self) + + parser = OptionParser(option_class=eng_option) + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm output device name. E.g., hw:0,0 or /dev/dsp") + parser.add_option("-r", "--sample-rate", type="eng_float", default=48000, + help="set sample rate to RATE (48000)") + parser.add_option ("-m", "--max-channels", type="int", default="16", + help="set maximum channels to use") + parser.add_option("-D", "--dont-block", action="store_false", default=True, + dest="ok_to_block") + (options, args) = parser.parse_args () + if len(args) != 0: + parser.print_help() + raise SystemExit, 1 + + sample_rate = int(options.sample_rate) + limit_channels = options.max_channels + + ampl = 0.1 + + # With a tip of the hat to Harry Partch, may he R.I.P. + # See "Genesis of a Music". He was into some very wild tunings... + base = 392 + ratios = { 1 : 1.0, + 3 : 3.0/2, + 5 : 5.0/4, + 7 : 7.0/4, + 9 : 9.0/8, + 11 : 11.0/8 } + + # progression = (1, 5, 3, 7) + # progression = (1, 9, 3, 7) + # progression = (3, 7, 9, 11) + # progression = (7, 11, 1, 5) + progression = (7, 11, 1, 5, 9) + + dst = audio.sink (sample_rate, + options.audio_output, + options.ok_to_block) + + max_chan = dst.input_signature().max_streams() + if (max_chan == -1) or (max_chan > limit_channels): + max_chan = limit_channels + + for i in range (max_chan): + quo, rem = divmod (i, len (progression)) + freq = base * ratios[progression[rem]] * (quo + 1) + src = gr.sig_source_f (sample_rate, gr.GR_SIN_WAVE, freq, ampl) + self.connect (src, (dst, i)) + +if __name__ == '__main__': + try: + my_graph().run() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/audio/spectrum_inversion.py b/gnuradio-examples/python/audio/spectrum_inversion.py new file mode 100755 index 000000000..9bb87aa4b --- /dev/null +++ b/gnuradio-examples/python/audio/spectrum_inversion.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# +# Gang - Here's a simple script that demonstrates spectrum inversion +# using the multiply by [1,-1] method (mixing with Nyquist frequency). +# Requires nothing but a sound card, and sounds just like listening +# to a SSB signal on the wrong sideband. +# + +from gnuradio import gr +from gnuradio import audio +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +class my_graph(gr.flow_graph): + + def __init__(self): + gr.flow_graph.__init__(self) + + parser = OptionParser(option_class=eng_option) + parser.add_option("-I", "--audio-input", type="string", default="", + help="pcm input device name. E.g., hw:0,0 or /dev/dsp") + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm output device name. E.g., hw:0,0 or /dev/dsp") + parser.add_option("-r", "--sample-rate", type="eng_float", default=8000, + help="set sample rate to RATE (8000)") + (options, args) = parser.parse_args () + if len(args) != 0: + parser.print_help() + raise SystemExit, 1 + + sample_rate = int(options.sample_rate) + src = audio.source (sample_rate, options.audio_input) + dst = audio.sink (sample_rate, options.audio_output) + + vec1 = [1, -1] + vsource = gr.vector_source_f(vec1, True) + multiply = gr.multiply_ff() + + self.connect(src, (multiply, 0)) + self.connect(vsource, (multiply, 1)) + self.connect(multiply, dst) + + +if __name__ == '__main__': + try: + my_graph().run() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/audio/test_resampler.py b/gnuradio-examples/python/audio/test_resampler.py new file mode 100755 index 000000000..6c9e5a760 --- /dev/null +++ b/gnuradio-examples/python/audio/test_resampler.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# +# 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. +# + +from gnuradio import gr, gru, blks +from gnuradio import audio +from gnuradio.eng_option import eng_option +from optparse import OptionParser + + +class my_graph(gr.flow_graph): + + def __init__(self): + gr.flow_graph.__init__(self) + + parser = OptionParser(option_class=eng_option) + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm output device name. E.g., hw:0,0 or /dev/dsp") + parser.add_option("-i", "--input-rate", type="eng_float", default=8000, + help="set input sample rate to RATE (%default)") + parser.add_option("-o", "--output-rate", type="eng_float", default=48000, + help="set output sample rate to RATE (%default)") + (options, args) = parser.parse_args () + if len(args) != 0: + parser.print_help() + raise SystemExit, 1 + + input_rate = int(options.input_rate) + output_rate = int(options.output_rate) + + interp = gru.lcm(input_rate, output_rate) / input_rate + decim = gru.lcm(input_rate, output_rate) / output_rate + + print "interp =", interp + print "decim =", decim + + ampl = 0.1 + src0 = gr.sig_source_f (input_rate, gr.GR_SIN_WAVE, 650, ampl) + rr = blks.rational_resampler_fff(self, interp, decim) + dst = audio.sink (output_rate, options.audio_output) + self.connect (src0, rr, (dst, 0)) + + +if __name__ == '__main__': + try: + my_graph().run() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/digital_voice/Makefile.am b/gnuradio-examples/python/digital_voice/Makefile.am new file mode 100644 index 000000000..ed88c22d9 --- /dev/null +++ b/gnuradio-examples/python/digital_voice/Makefile.am @@ -0,0 +1,23 @@ +# +# 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. +# + +EXTRA_DIST = \ + encdec.py diff --git a/gnuradio-examples/python/digital_voice/encdec.py b/gnuradio-examples/python/digital_voice/encdec.py new file mode 100755 index 000000000..11936a4b9 --- /dev/null +++ b/gnuradio-examples/python/digital_voice/encdec.py @@ -0,0 +1,58 @@ +#!/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, blks +from gnuradio import audio +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +class my_graph(gr.flow_graph): + + def __init__(self): + gr.flow_graph.__init__(self) + + parser = OptionParser(option_class=eng_option) + parser.add_option("-I", "--audio-input", type="string", default="", + help="pcm input device name. E.g., hw:0,0 or /dev/dsp") + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm output device name. E.g., hw:0,0 or /dev/dsp") + (options, args) = parser.parse_args () + if len(args) != 0: + parser.print_help() + raise SystemExit, 1 + + sample_rate = 8000 + src = audio.source(sample_rate, options.audio_input) + tx = blks.digital_voice_tx(self) + if_gain = gr.multiply_const_cc(10000) + # channel simulator here... + rx = blks.digital_voice_rx(self) + dst = audio.sink(sample_rate, options.audio_output) + + self.connect(src, tx, if_gain, rx, dst) + + +if __name__ == '__main__': + try: + my_graph().run() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/gmsk2/Makefile.am b/gnuradio-examples/python/gmsk2/Makefile.am new file mode 100644 index 000000000..cf2ffb39e --- /dev/null +++ b/gnuradio-examples/python/gmsk2/Makefile.am @@ -0,0 +1,38 @@ +# +# 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. +# + +EXTRA_DIST = \ + benchmark_gmsk_rx.py \ + benchmark_gmsk_tx.py \ + benchmark_mpsk_rx.py \ + benchmark_mpsk_tx.py \ + fusb_options.py \ + gen_whitener.py \ + gmsk_test.py \ + pick_bitrate.py \ + receive_path.py \ + transmit_path.py \ + tunnel_ip_null_mac.py + + + + + diff --git a/gnuradio-examples/python/gmsk2/README b/gnuradio-examples/python/gmsk2/README new file mode 100644 index 000000000..b3c343e4b --- /dev/null +++ b/gnuradio-examples/python/gmsk2/README @@ -0,0 +1,81 @@ +Quick overview of what's here: + +* gmsk_test.py: stand-alone program that exercises the GMSK packet tx +and rx code. The two halves are connected with a simulated noisy +channel. It's easy to add extra instrumentation to log various internal +states. We used a variant of this code to get this working in the +first place. + +* benchmark_gmsk_tx.py: generates packets of the size you +specify and sends them across the air using the USRP. Known to work +well using the USRP with the Flex 400 transceiver daughterboard. +You can specify the bitrate to use with the -r <bitrate> command line +parameter. The default is 500k. Some machines will do 1M or more. + +* benchmark_gmsk_rx.py: the receiver half of benchmark_gmsk_tx.py. +Command line arguments are pretty much the same as tx. Works well +with a USRP and Flex 400 transceiver daughterboard. Will also work +with TVRX daugherboard, but you'll need to fiddle with the gain. See +below. Prints a summary of each packet received and keeps a running +total of packets received, and how many of them were error free. +There are two levels of error reporting going on. If the access code +(PN code) and header of a packet were properly detected, then you'll +get an output line. If the CRC32 of the payload was correct you get +"ok = True", else "ok = False". The "pktno" is extracted from the +received packet. If there are skipped numbers, you're missing some +packets. Be sure you've got a suitable antenna connected to the TX/RX +port on each board. A "70 cm" / 420 MHz antenna for a ham +handi-talkie works great. These are available at ham radio supplies, +etc. The boards need to be at least 3m apart. You can also try +experimenting with the rx gain (-g <gain> command line option). + +Generally speaking, I start the rx first on one machine, and then fire +up the tx on the other machine. The tx also supports a discontinous +transmission mode where it sends bursts of 5 packets and then waits 1 +second. This is useful for ensuring that all the receiver control +loops lock up fast enough. + +* tunnel.py: This program provides a framework for building your own +MACs. It creates a "TAP" interface in the kernel, typically gr0, +and sends and receives ethernet frames through it. See +/usr/src/linux/Documentation/networking/tuntap.txt and/or Google for +"universal tun tap". The Linux 2.6 kernel includes the tun module, you +don't have to build it. You may have to "modprobe tun" if it's not +loaded by default. If /dev/net/tun doesn't exist, try "modprobe tun". + +To run this program you'll need to be root or running with the +appropriate capability to open the tun interface. You'll need to fire +up two copies on different machines. Once each is running you'll need +to ifconfig the gr0 interface to set the IP address. + +This will allow two machines to talk, but anything beyond the two +machines depends on your networking setup. Left as an exercise... + +On machine A: + + $ su + # ./tunnel.py --freq 423.0M --bitrate 500k + # # in another window on A, also as root... + # ifconfig gr0 10.10.10.1 + + +On machine B: + + $ su + # ./tunnel.py --freq 423.0M --bitrate 500k + # # in another window on B, also as root... + # ifconfig gr0 10.10.10.2 + +Now, on machine A you shold be able to ping machine B: + + $ ping 10.10.10.2 + +and you should see some output for each packet in the +tunnel.py window if you used the -v option. + +Likewise, on machine B: + + $ ping 10.10.10.1 + +This now uses a carrier sense MAC, so you should be able to ssh +between the machines, web browse, etc. diff --git a/gnuradio-examples/python/gmsk2/benchmark_gmsk_rx.py b/gnuradio-examples/python/gmsk2/benchmark_gmsk_rx.py new file mode 100755 index 000000000..d1f70c546 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/benchmark_gmsk_rx.py @@ -0,0 +1,128 @@ +#!/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, gru, blks +from gnuradio import usrp +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +import random +import struct + +# from current dir +from receive_path import receive_path +import fusb_options + +#import os +#print os.getpid() +#raw_input('Attach and press enter') + + +class my_graph(gr.flow_graph): + + def __init__(self, demod_class, rx_subdev_spec, + bitrate, decim_rate, spb, + rx_callback, options, demod_kwargs): + gr.flow_graph.__init__(self) + self.rxpath = receive_path(self, demod_class, rx_subdev_spec, + bitrate, decim_rate, spb, + rx_callback, options, demod_kwargs) + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +global n_rcvd, n_right + +def main(): + global n_rcvd, n_right + + n_rcvd = 0 + n_right = 0 + + def rx_callback(ok, payload): + global n_rcvd, n_right + (pktno,) = struct.unpack('!H', payload[0:2]) + n_rcvd += 1 + if ok: + n_right += 1 + + print "ok = %r pktno = %4d n_rcvd = %4d n_right = %4d" % ( + ok, pktno, n_rcvd, n_right) + + parser = OptionParser (option_class=eng_option) + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, + help="select USRP Rx side A or B") + parser.add_option("-f", "--freq", type="eng_float", default=423.1e6, + help="set Rx frequency to FREQ [default=%default]", + metavar="FREQ") + parser.add_option("-r", "--bitrate", type="eng_float", default=None, + help="specify bitrate. spb and interp will be derived.") + parser.add_option("-g", "--rx-gain", type="eng_float", default=27, + help="set rx gain") + parser.add_option("-S", "--spb", type="int", default=None, + help="set samples/baud [default=%default]") + parser.add_option("-d", "--decim", type="intx", default=None, + help="set fpga decim rate to DECIM [default=%default]") + fusb_options.add_options(parser) + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if options.freq < 1e6: + options.freq *= 1e6 + + demod_kwargs = { } # placeholder + + # build the graph + fg = my_graph(blks.gmsk2_demod, + options.rx_subdev_spec, options.bitrate, + options.decim, options.spb, rx_callback, + options, demod_kwargs) + + print "bitrate: %sb/sec" % (eng_notation.num_to_str(fg.rxpath.bitrate()),) + print "spb: %3d" % (fg.rxpath.spb(),) + print "decim: %3d" % (fg.rxpath.decim(),) + + ok = fg.rxpath.set_freq(options.freq) + if not ok: + print "Failed to set Rx frequency to %s" % (eng_notation.num_to_str(options.freq),) + raise SystemExit + + fg.rxpath.set_gain(options.rx_gain) + print "Rx gain_range: ", fg.rxpath.subdev.gain_range(), " using", fg.rxpath.gain + + r = gr.enable_realtime_scheduling() + if r != gr.RT_OK: + print "Warning: Failed to enable realtime scheduling." + + fg.start() # start flow graph + fg.wait() # wait for it to finish + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/gmsk2/benchmark_gmsk_tx.py b/gnuradio-examples/python/gmsk2/benchmark_gmsk_tx.py new file mode 100755 index 000000000..2994003a0 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/benchmark_gmsk_tx.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python +# +# 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. +# + +from gnuradio import gr, gru, blks +from gnuradio import usrp +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +import random +import time +import struct +import sys + +# from current dir +from transmit_path import transmit_path +import fusb_options + +#import os +#print os.getpid() +#raw_input('Attach and press enter') + + +class my_graph(gr.flow_graph): + + def __init__(self, mod_class, tx_subdev_spec, + bitrate, interp_rate, spb, gain, + options, mod_kwargs): + gr.flow_graph.__init__(self) + self.txpath = transmit_path(self, mod_class, tx_subdev_spec, + bitrate, interp_rate, spb, gain, + options, mod_kwargs) + + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +def main(): + + def send_pkt(payload='', eof=False): + return fg.txpath.send_pkt(payload, eof) + + def rx_callback(ok, payload): + print "ok = %r, payload = '%s'" % (ok, payload) + + parser = OptionParser (option_class=eng_option) + parser.add_option("-T", "--tx-subdev-spec", type="subdev", default=None, + help="select USRP Tx side A or B") + parser.add_option("-f", "--freq", type="eng_float", default=423.1e6, + help="set Tx and Rx frequency to FREQ [default=%default]", + metavar="FREQ") + parser.add_option("-r", "--bitrate", type="eng_float", default=None, + help="specify bitrate. spb and interp will be derived.") + parser.add_option("-S", "--spb", type="int", default=None, + help="set samples/baud [default=%default]") + parser.add_option("-i", "--interp", type="intx", default=None, + help="set fpga interpolation rate to INTERP [default=%default]") + parser.add_option("-s", "--size", type="eng_float", default=1500, + help="set packet size [default=%default]") + parser.add_option("", "--bt", type="float", default=0.3, + help="set bandwidth-time product [default=%default]") + parser.add_option("-g", "--gain", type="eng_float", default=100.0, + help="transmitter gain [default=%default]") + parser.add_option("-M", "--megabytes", type="eng_float", default=1.0, + help="set megabytes to transmit [default=%default]") + parser.add_option("","--discontinuous", action="store_true", default=False, + help="enable discontinous transmission (bursts of 5 packets)") + fusb_options.add_options(parser) + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if options.freq < 1e6: + options.freq *= 1e6 + + pkt_size = int(options.size) + + mod_kwargs = { + 'bt' : options.bt, + } + + # build the graph + fg = my_graph(blks.gmsk2_mod, options.tx_subdev_spec, + options.bitrate, options.interp, options.spb, options.gain, + options, mod_kwargs) + + print "bitrate: %sb/sec" % (eng_notation.num_to_str(fg.txpath.bitrate()),) + print "spb: %3d" % (fg.txpath.spb(),) + print "interp: %3d" % (fg.txpath.interp(),) + + ok = fg.txpath.set_freq(options.freq) + if not ok: + print "Failed to set Tx frequency to %s" % (eng_notation.num_to_str(options.freq),) + raise SystemExit + + r = gr.enable_realtime_scheduling() + if r != gr.RT_OK: + print "Warning: failed to enable realtime scheduling" + + fg.start() # start flow graph + + # generate and send packets + nbytes = int(1e6 * options.megabytes) + n = 0 + pktno = 0 + + while n < nbytes: + send_pkt(struct.pack('!H', pktno) + (pkt_size - 2) * chr(pktno & 0xff)) + n += pkt_size + sys.stderr.write('.') + if options.discontinuous and pktno % 5 == 4: + time.sleep(1) + pktno += 1 + + send_pkt(eof=True) + fg.wait() # wait for it to finish + fg.txpath.set_auto_tr(False) + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/gmsk2/benchmark_mpsk_rx.py b/gnuradio-examples/python/gmsk2/benchmark_mpsk_rx.py new file mode 100755 index 000000000..ad6218b69 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/benchmark_mpsk_rx.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python +# +# Copyright 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. +# + +from gnuradio import gr, gru, blks +from gnuradio import usrp +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +import random +import struct + +# from current dir +from bpsk import bpsk_demod +from dbpsk import dbpsk_demod +from dqpsk import dqpsk_demod +from receive_path import receive_path +import fusb_options + +if 1: + import os + print os.getpid() + raw_input('Attach and press enter') + + +class my_graph(gr.flow_graph): + + def __init__(self, demod_class, rx_subdev_spec, + bitrate, decim_rate, spb, + rx_callback, options, demod_kwargs): + gr.flow_graph.__init__(self) + self.rxpath = receive_path(self, demod_class, rx_subdev_spec, + bitrate, decim_rate, spb, + rx_callback, options, demod_kwargs) + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +global n_rcvd, n_right + +def main(): + global n_rcvd, n_right + + n_rcvd = 0 + n_right = 0 + + def rx_callback(ok, payload): + global n_rcvd, n_right + (pktno,) = struct.unpack('!H', payload[0:2]) + n_rcvd += 1 + if ok: + n_right += 1 + + print "ok = %r pktno = %4d n_rcvd = %4d n_right = %4d" % ( + ok, pktno, n_rcvd, n_right) + + parser = OptionParser (option_class=eng_option) + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, + help="select USRP Rx side A or B") + parser.add_option("-f", "--freq", type="eng_float", default=423.1e6, + help="set Rx frequency to FREQ [default=%default]", + metavar="FREQ") + parser.add_option("-r", "--bitrate", type="eng_float", default=None, + help="specify bitrate. spb and interp will be derived.") + parser.add_option("-S", "--spb", type="int", default=None, + help="set samples/baud [default=%default]") + parser.add_option("-d", "--decim", type="intx", default=None, + help="set fpga decim rate to DECIM [default=%default]") + parser.add_option("-m", "--modulation", type="string", default='dbpsk', + help="modulation type (bpsk, dbpsk, dqpsk) [default=%default]") + parser.add_option("", "--excess-bw", type="float", default=0.3, + help="set RRC excess bandwith factor [default=%default]") + parser.add_option("-g", "--gain", type="eng_float", default=27, + help="set rx gain") + parser.add_option("","--log", action="store_true", default=False, + help="enable diagnostic logging") + fusb_options.add_options(parser) + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if options.freq < 1e6: + options.freq *= 1e6 + + demod_kwargs = { + 'excess_bw' : options.excess_bw, + } + + #FIXME: Needs to be worked in to overall structure; this will be fixed + # once static class definitions for modulations are defined + if(options.modulation=='bpsk'): + modulation=bpsk_demod + elif(options.modulation=='dbpsk'): + modulation=dbpsk_demod + else: + modulation=dqpsk_demod + + # build the graph + fg = my_graph(modulation, + options.rx_subdev_spec, options.bitrate, + options.decim, options.spb, + rx_callback, options, demod_kwargs) + + print "bitrate: %sb/sec" % (eng_notation.num_to_str(fg.rxpath.bitrate()),) + print "spb: %3d" % (fg.rxpath.spb(),) + print "decim: %3d" % (fg.rxpath.decim(),) + + ok = fg.rxpath.set_freq(options.freq) + if not ok: + print "Failed to set Rx frequency to %s" % (eng_notation.num_to_str(options.freq),) + raise SystemExit + + fg.rxpath.set_gain(options.gain) + print "Rx gain_range: ", fg.rxpath.subdev.gain_range(), " using", fg.rxpath.gain + + r = gr.enable_realtime_scheduling() + if r != gr.RT_OK: + print "Warning: Failed to enable realtime scheduling." + + fg.start() # start flow graph + fg.wait() # wait for it to finish + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/gmsk2/benchmark_mpsk_tx.py b/gnuradio-examples/python/gmsk2/benchmark_mpsk_tx.py new file mode 100755 index 000000000..7b37da7fe --- /dev/null +++ b/gnuradio-examples/python/gmsk2/benchmark_mpsk_tx.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python +# +# 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. +# + +from gnuradio import gr, gru, blks +from gnuradio import usrp +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +import random, time, struct, sys + +# from current dir +from bpsk import bpsk_mod +from dbpsk import dbpsk_mod +from dqpsk import dqpsk_mod +from transmit_path import transmit_path +import fusb_options + +#import os +#print os.getpid() +#raw_input('Attach and press enter') + + +class my_graph(gr.flow_graph): + + def __init__(self, mod_class, tx_subdev_spec, + bitrate, interp_rate, spb, gain, + options, mod_kwargs): + gr.flow_graph.__init__(self) + self.txpath = transmit_path(self, mod_class, tx_subdev_spec, + bitrate, interp_rate, spb, gain, + options, mod_kwargs) + + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +def main(): + + def send_pkt(payload='', eof=False): + return fg.txpath.send_pkt(payload, eof) + + def rx_callback(ok, payload): + print "ok = %r, payload = '%s'" % (ok, payload) + + parser = OptionParser (option_class=eng_option) + parser.add_option("-T", "--tx-subdev-spec", type="subdev", default=None, + help="select USRP Tx side A or B") + parser.add_option("-f", "--freq", type="eng_float", default=423.1e6, + help="set Tx and Rx frequency to FREQ [default=%default]", + metavar="FREQ") + parser.add_option("-r", "--bitrate", type="eng_float", default=None, + help="specify bitrate. spb and interp will be derived.") + parser.add_option("-S", "--spb", type="int", default=None, + help="set samples/baud [default=%default]") + parser.add_option("-i", "--interp", type="intx", default=None, + help="set fpga interpolation rate to INTERP [default=%default]") + parser.add_option("-s", "--size", type="eng_float", default=1500, + help="set packet size [default=%default]") + parser.add_option("-m", "--modulation", type="string", default='dbpsk', + help="modulation type (bpsk, dbpsk, dqpsk) [default=%default]") + parser.add_option("", "--excess-bw", type="float", default=0.3, + help="set RRC excess bandwith factor [default=%default]") + parser.add_option("-g", "--gain", type="eng_float", default=100.0, + help="transmitter gain [default=%default]") + parser.add_option("-M", "--megabytes", type="eng_float", default=1.0, + help="set megabytes to transmit [default=%default]") + parser.add_option("","--discontinuous", action="store_true", default=False, + help="enable discontinous transmission (bursts of 5 packets)") + fusb_options.add_options(parser) + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if options.freq < 1e6: + options.freq *= 1e6 + + pkt_size = int(options.size) + + mod_kwargs = { + 'excess_bw' : options.excess_bw, + } + + #FIXME: rework when static class defintions are ready for the modulation types + if(options.modulation=='bpsk'): + modulation=bpsk_mod + elif( options.modulation=='dbpsk'): + modulation=dbpsk_mod + else: + modulation=dqpsk_mod + + # build the graph + fg = my_graph(modulation, + options.tx_subdev_spec, options.bitrate, options.interp, + options.spb, options.gain, + options, mod_kwargs) + + print "bitrate: %sb/sec" % (eng_notation.num_to_str(fg.txpath.bitrate()),) + print "spb: %3d" % (fg.txpath.spb(),) + print "interp: %3d" % (fg.txpath.interp(),) + + ok = fg.txpath.set_freq(options.freq) + if not ok: + print "Failed to set Tx frequency to %s" % (eng_notation.num_to_str(options.freq),) + raise SystemExit + + r = gr.enable_realtime_scheduling() + if r != gr.RT_OK: + print "Warning: failed to enable realtime scheduling" + + fg.start() # start flow graph + + # generate and send packets + nbytes = int(1e6 * options.megabytes) + n = 0 + pktno = 0 + + while n < nbytes: + send_pkt(struct.pack('!H', pktno) + (pkt_size - 2) * chr(pktno & 0xff)) + n += pkt_size + sys.stderr.write('.') + if options.discontinuous and pktno % 5 == 4: + time.sleep(1) + pktno += 1 + + send_pkt(eof=True) + fg.wait() # wait for it to finish + fg.txpath.set_auto_tr(False) + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/gmsk2/bpsk.py b/gnuradio-examples/python/gmsk2/bpsk.py new file mode 100644 index 000000000..14cea7b10 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/bpsk.py @@ -0,0 +1,256 @@ +# +# 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. +# + +# See gnuradio-examples/python/gmsk2 for examples + +""" +BPSK modulation and demodulation. +""" + +from gnuradio import gr, gru +from math import pi, sqrt +import cmath +import Numeric +from pprint import pprint + +_use_gray_code = True + +def make_constellation(m): + return [cmath.exp(i * 2 * pi / m * 1j) for i in range(m)] + +# Common definition of constellations for Tx and Rx +constellation = { + 2 : make_constellation(2), # BPSK + 4 : make_constellation(4), # QPSK + 8 : make_constellation(8) # 8PSK + } + +if 0: + print "const(2) =" + pprint(constellation[2]) + print "const(4) =" + pprint(constellation[4]) + print "const(8) =" + pprint(constellation[8]) + + +if _use_gray_code: + # ----------------------- + # Do Gray code + # ----------------------- + # binary to gray coding + binary_to_gray = { + 2 : (0, 1), + 4 : (0, 1, 3, 2), + 8 : (0, 1, 3, 2, 7, 6, 4, 5) + } + + # gray to binary + gray_to_binary = { + 2 : (0, 1), + 4 : (0, 1, 3, 2), + 8 : (0, 1, 3, 2, 6, 7, 5, 4) + } +else: + # ----------------------- + # Don't Gray code + # ----------------------- + # identity mapping + binary_to_gray = { + 2 : (0, 1), + 4 : (0, 1, 2, 3), + 8 : (0, 1, 2, 3, 4, 5, 6, 7) + } + + # identity mapping + gray_to_binary = { + 2 : (0, 1), + 4 : (0, 1, 2, 3), + 8 : (0, 1, 2, 3, 4, 5, 6, 7) + } + + +# ///////////////////////////////////////////////////////////////////////////// +# mPSK mod/demod with steams of bytes as data i/o +# ///////////////////////////////////////////////////////////////////////////// + + +class bpsk_mod(gr.hier_block): + + def __init__(self, fg, spb, excess_bw): + """ + Hierarchical block for RRC-filtered BPSK modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + @param fg: flow graph + @type fg: flow graph + @param spb: samples per baud >= 2 + @type spb: integer + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + """ + if not isinstance(spb, int) or spb < 2: + raise TypeError, "sbp must be an integer >= 2" + self.spb = spb + + ntaps = 11 * spb + + bits_per_symbol = self.bits_per_baud() + arity = pow(2,bits_per_symbol) + print "bits_per_symbol =", bits_per_symbol + + # turn bytes into k-bit vectors + self.bytes2chunks = \ + gr.packed_to_unpacked_bb(bits_per_symbol, gr.GR_MSB_FIRST) + + self.chunks2symbols = gr.chunks_to_symbols_bc(constellation[arity]) + + # pulse shaping filter + self.rrc_taps = gr.firdes.root_raised_cosine( + spb, # gain (spb since we're interpolating by spb) + spb, # sampling rate + 1.0, # symbol rate + excess_bw, # excess bandwidth (roll-off factor) + ntaps) + + self.rrc_filter = gr.interp_fir_filter_ccf(spb, self.rrc_taps) + + # Connect + fg.connect(self.bytes2chunks, self.chunks2symbols, self.rrc_filter) + + if 1: + fg.connect(self.rrc_filter, + gr.file_sink(gr.sizeof_gr_complex, "rrc.dat")) + + # Initialize base class + gr.hier_block.__init__(self, fg, self.bytes2chunks, self.rrc_filter) + + def samples_per_baud(self): + return self.spb + + def bits_per_baud(self=None): # staticmethod that's also callable on an instance + return 1 + bits_per_baud = staticmethod(bits_per_baud) # make it a static method. RTFM + + +class bpsk_demod__coherent_detection_of_psk(gr.hier_block): + def __init__(self, fg, spb, excess_bw, costas_alpha=0.005, gain_mu=0.05): + """ + Hierarchical block for RRC-filtered BPSK demodulation + + The input is the complex modulated signal at baseband. + The output is a stream of bits packed 1 bit per byte (LSB) + + @param fg: flow graph + @type fg: flow graph + @param spb: samples per baud >= 2 + @type spb: float + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + @param costas_alpha: loop filter gain + @type costas_alphas: float + @param gain_mu: + @type gain_mu: float + """ + if spb < 2: + raise TypeError, "sbp must be >= 2" + self.spb = spb + + bits_per_symbol = self.bits_per_baud() + arity = pow(2,bits_per_symbol) + print "bits_per_symbol =", bits_per_symbol + + # Automatic gain control + self.preamp = gr.multiply_const_cc(10e-5) + self.agc = gr.agc_cc(1e-3, 1, 1) + + # Costas loop (carrier tracking) + # FIXME: need to decide how to handle this more generally; do we pull it from higher layer? + costas_order = 2 + costas_alpha *= 15 # 2nd order loop needs more gain + beta = .25 * costas_alpha * costas_alpha + self.costas_loop = gr.costas_loop_cc(costas_alpha, beta, 0.05, -0.05, costas_order) + + # RRC data filter + ntaps = 11 * spb + self.rrc_taps = gr.firdes.root_raised_cosine( + 1.0, # gain + spb, # sampling rate + 1.0, # symbol rate + excess_bw, # excess bandwidth (roll-off factor) + ntaps) + + self.rrc_filter=gr.fir_filter_ccf(1, self.rrc_taps) + + # symbol clock recovery + omega = spb + gain_omega = .25 * gain_mu * gain_mu + omega_rel_limit = 0.5 + mu = 0.05 + gain_mu = 0.1 + self.clock_recovery=gr.clock_recovery_mm_cc(omega, gain_omega, + mu, gain_mu, omega_rel_limit) + + # find closest constellation point + #rot = .707 + .707j + rot = 1 + rotated_const = map(lambda pt: pt * rot, constellation[arity]) + print "rotated_const =", rotated_const + + self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity)) + self.gray_decoder = gr.map_bb(gray_to_binary[arity]) + + # unpack the k bit vector into a stream of bits + self.unpack = gr.unpack_k_bits_bb(bits_per_symbol) + + fg.connect(self.preamp, self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery, + self.slicer, self.gray_decoder, self.unpack) + + # Debug sinks + if 1: + fg.connect(self.agc, + gr.file_sink(gr.sizeof_gr_complex, "agc.dat")) + fg.connect(self.costas_loop, + gr.file_sink(gr.sizeof_gr_complex, "costas_loop.dat")) + fg.connect(self.rrc_filter, + gr.file_sink(gr.sizeof_gr_complex, "rrc.dat")) + fg.connect(self.clock_recovery, + gr.file_sink(gr.sizeof_gr_complex, "clock_recovery.dat")) + fg.connect(self.slicer, + gr.file_sink(gr.sizeof_char, "slicer.dat")) + fg.connect(self.unpack, + gr.file_sink(gr.sizeof_char, "unpack.dat")) + + # Initialize base class + gr.hier_block.__init__(self, fg, self.preamp, self.unpack) + + def samples_per_baud(self): + return self.spb + + def bits_per_baud(self=None): # staticmethod that's also callable on an instance + return 1 + bits_per_baud = staticmethod(bits_per_baud) # make it a static method. RTFM + + +bpsk_demod = bpsk_demod__coherent_detection_of_psk + diff --git a/gnuradio-examples/python/gmsk2/dbpsk.py b/gnuradio-examples/python/gmsk2/dbpsk.py new file mode 100644 index 000000000..213e90c61 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/dbpsk.py @@ -0,0 +1,282 @@ +# +# 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. +# + +# See gnuradio-examples/python/gmsk2 for examples + +""" +differential BPSK modulation and demodulation. +""" + +from gnuradio import gr, gru +from math import pi, sqrt +import cmath +import Numeric +from pprint import pprint + +_use_gray_code = True + +def make_constellation(m): + return [cmath.exp(i * 2 * pi / m * 1j) for i in range(m)] + +# Common definition of constellations for Tx and Rx +constellation = { + 2 : make_constellation(2), # BPSK + 4 : make_constellation(4), # QPSK + 8 : make_constellation(8) # 8PSK + } + +if 0: + print "const(2) =" + pprint(constellation[2]) + print "const(4) =" + pprint(constellation[4]) + print "const(8) =" + pprint(constellation[8]) + + +if _use_gray_code: + # ----------------------- + # Do Gray code + # ----------------------- + # binary to gray coding + binary_to_gray = { + 2 : (0, 1), + 4 : (0, 1, 3, 2), + 8 : (0, 1, 3, 2, 7, 6, 4, 5) + } + + # gray to binary + gray_to_binary = { + 2 : (0, 1), + 4 : (0, 1, 3, 2), + 8 : (0, 1, 3, 2, 6, 7, 5, 4) + } +else: + # ----------------------- + # Don't Gray code + # ----------------------- + # identity mapping + binary_to_gray = { + 2 : (0, 1), + 4 : (0, 1, 2, 3), + 8 : (0, 1, 2, 3, 4, 5, 6, 7) + } + + # identity mapping + gray_to_binary = { + 2 : (0, 1), + 4 : (0, 1, 2, 3), + 8 : (0, 1, 2, 3, 4, 5, 6, 7) + } + + +# ///////////////////////////////////////////////////////////////////////////// +# BPSK mod/demod with steams of bytes as data i/o +# ///////////////////////////////////////////////////////////////////////////// + + +class dbpsk_mod(gr.hier_block): + + def __init__(self, fg, spb, excess_bw): + """ + Hierarchical block for RRC-filtered QPSK modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + @param fg: flow graph + @type fg: flow graph + @param spb: samples per baud >= 2 + @type spb: integer + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + """ + if not isinstance(spb, int) or spb < 2: + raise TypeError, "sbp must be an integer >= 2" + self.spb = spb + + ntaps = 11 * spb + + bits_per_symbol = self.bits_per_baud() + arity = pow(2,bits_per_symbol) + self.bits_per_symbol = bits_per_symbol + print "bits_per_symbol =", bits_per_symbol + + # turn bytes into k-bit vectors + self.bytes2chunks = \ + gr.packed_to_unpacked_bb(bits_per_symbol, gr.GR_MSB_FIRST) + + if True: + self.gray_coder = gr.map_bb(binary_to_gray[arity]) + else: + self.gray_coder = None + + self.diffenc = gr.diff_encoder_bb(arity) + + self.chunks2symbols = gr.chunks_to_symbols_bc(constellation[arity]) + + # pulse shaping filter + self.rrc_taps = gr.firdes.root_raised_cosine( + spb, # gain (spb since we're interpolating by spb) + spb, # sampling rate + 1.0, # symbol rate + excess_bw, # excess bandwidth (roll-off factor) + ntaps) + + self.rrc_filter = gr.interp_fir_filter_ccf(spb, self.rrc_taps) + + # Connect + if self.gray_coder: + fg.connect(self.bytes2chunks, self.gray_coder) + t = self.gray_coder + else: + t = self.bytes2chunks + + fg.connect(t, self.diffenc, self.chunks2symbols, self.rrc_filter) + + if 1: + fg.connect(self.gray_coder, + gr.file_sink(gr.sizeof_char, "graycoder.dat")) + fg.connect(self.diffenc, + gr.file_sink(gr.sizeof_char, "diffenc.dat")) + + # Initialize base class + gr.hier_block.__init__(self, fg, self.bytes2chunks, self.rrc_filter) + + def samples_per_baud(self): + return self.spb + + def bits_per_baud(self=None): # staticmethod that's also callable on an instance + return 1 + bits_per_baud = staticmethod(bits_per_baud) # make it a static method. RTFM + + + +class dbpsk_demod__coherent_detection_of_differentially_encoded_psk(gr.hier_block): + def __init__(self, fg, spb, excess_bw, costas_alpha=0.005, gain_mu=0.05): + """ + Hierarchical block for RRC-filtered BPSK demodulation + + The input is the complex modulated signal at baseband. + The output is a stream of bits packed 1 bit per byte (LSB) + + @param fg: flow graph + @type fg: flow graph + @param spb: samples per baud >= 2 + @type spb: float + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + @param costas_alpha: loop filter gain + @type costas_alphas: float + @param gain_mu: + @type gain_mu: float + """ + if spb < 2: + raise TypeError, "sbp must be >= 2" + self.spb = spb + + bits_per_symbol = self.bits_per_baud() + arity = pow(2,bits_per_symbol) + print "bits_per_symbol =", bits_per_symbol + + # Automatic gain control + self.preamp = gr.multiply_const_cc(10e-5) + self.agc = gr.agc_cc(1e-3, 1, 1, 1000) + + # Costas loop (carrier tracking) + # FIXME: need to decide how to handle this more generally; do we pull it from higher layer? + costas_order = 2 + costas_alpha *= 15 # 2nd order loop needs more gain + beta = .25 * costas_alpha * costas_alpha + self.costas_loop = gr.costas_loop_cc(costas_alpha, beta, 0.05, -0.05, costas_order) + + # RRC data filter + ntaps = 11 * spb + self.rrc_taps = gr.firdes.root_raised_cosine( + 1.0, # gain + spb, # sampling rate + 1.0, # symbol rate + excess_bw, # excess bandwidth (roll-off factor) + ntaps) + + self.rrc_filter=gr.fir_filter_ccf(1, self.rrc_taps) + + # symbol clock recovery + omega = spb + gain_omega = .25 * gain_mu * gain_mu + omega_rel_limit = 0.5 + mu = 0.05 + gain_mu = 0.1 + self.clock_recovery=gr.clock_recovery_mm_cc(omega, gain_omega, + mu, gain_mu, omega_rel_limit) + + # find closest constellation point + #rot = .707 + .707j + rot = 1 + rotated_const = map(lambda pt: pt * rot, constellation[arity]) + print "rotated_const =", rotated_const + + self.diffdec = gr.diff_phasor_cc() + #self.diffdec = gr.diff_decoder_bb(arity) + + self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity)) + self.gray_decoder = gr.map_bb(gray_to_binary[arity]) + + # unpack the k bit vector into a stream of bits + self.unpack = gr.unpack_k_bits_bb(bits_per_symbol) + + fg.connect(self.preamp, self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery, + self.diffdec, self.slicer, self.gray_decoder, self.unpack) + #fg.connect(self.preamp, self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery, + # self.slicer, self.diffdec, self.gray_decoder, self.unpack) + + # Debug sinks + if 1: + fg.connect(self.agc, + gr.file_sink(gr.sizeof_gr_complex, "agc.dat")) + fg.connect(self.costas_loop, + gr.file_sink(gr.sizeof_gr_complex, "costas_loop.dat")) + fg.connect(self.rrc_filter, + gr.file_sink(gr.sizeof_gr_complex, "rrc.dat")) + fg.connect(self.clock_recovery, + gr.file_sink(gr.sizeof_gr_complex, "clock_recovery.dat")) + fg.connect(self.slicer, + gr.file_sink(gr.sizeof_char, "slicer.dat")) + fg.connect(self.diffdec, + gr.file_sink(gr.sizeof_gr_complex, "diffdec.dat")) + #fg.connect(self.diffdec, + # gr.file_sink(gr.sizeof_char, "diffdec.dat")) + fg.connect(self.unpack, + gr.file_sink(gr.sizeof_char, "unpack.dat")) + + # Initialize base class + gr.hier_block.__init__(self, fg, self.preamp, self.unpack) + + def samples_per_baud(self): + return self.spb + + def bits_per_baud(self=None): # staticmethod that's also callable on an instance + return 1 + bits_per_baud = staticmethod(bits_per_baud) # make it a static method. RTFM + + +dbpsk_demod = dbpsk_demod__coherent_detection_of_differentially_encoded_psk + diff --git a/gnuradio-examples/python/gmsk2/dqpsk.py b/gnuradio-examples/python/gmsk2/dqpsk.py new file mode 100644 index 000000000..490dc6f73 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/dqpsk.py @@ -0,0 +1,280 @@ +# +# 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. +# + +# See gnuradio-examples/python/gmsk2 for examples + +""" +differential QPSK modulation and demodulation. +""" + +from gnuradio import gr, gru +from math import pi, sqrt +import cmath +import Numeric +from pprint import pprint + +_use_gray_code = True + +def make_constellation(m): + return [cmath.exp(i * 2 * pi / m * 1j) for i in range(m)] + +# Common definition of constellations for Tx and Rx +constellation = { + 2 : make_constellation(2), # BPSK + 4 : make_constellation(4), # QPSK + 8 : make_constellation(8) # 8PSK + } + +if 0: + print "const(2) =" + pprint(constellation[2]) + print "const(4) =" + pprint(constellation[4]) + print "const(8) =" + pprint(constellation[8]) + + +if _use_gray_code: + # ----------------------- + # Do Gray code + # ----------------------- + # binary to gray coding + binary_to_gray = { + 2 : (0, 1), + 4 : (0, 1, 3, 2), + 8 : (0, 1, 3, 2, 7, 6, 4, 5) + } + + # gray to binary + gray_to_binary = { + 2 : (0, 1), + 4 : (0, 1, 3, 2), + 8 : (0, 1, 3, 2, 6, 7, 5, 4) + } +else: + # ----------------------- + # Don't Gray code + # ----------------------- + # identity mapping + binary_to_gray = { + 2 : (0, 1), + 4 : (0, 1, 2, 3), + 8 : (0, 1, 2, 3, 4, 5, 6, 7) + } + + # identity mapping + gray_to_binary = { + 2 : (0, 1), + 4 : (0, 1, 2, 3), + 8 : (0, 1, 2, 3, 4, 5, 6, 7) + } + + +# ///////////////////////////////////////////////////////////////////////////// +# QPSK mod/demod with steams of bytes as data i/o +# ///////////////////////////////////////////////////////////////////////////// + + +class dqpsk_mod(gr.hier_block): + + def __init__(self, fg, spb, excess_bw): + """ + Hierarchical block for RRC-filtered QPSK modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + @param fg: flow graph + @type fg: flow graph + @param spb: samples per baud >= 2 + @type spb: integer + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + """ + if not isinstance(spb, int) or spb < 2: + raise TypeError, "sbp must be an integer >= 2" + self.spb = spb + + ntaps = 11 * spb + + bits_per_symbol = self.bits_per_baud() + arity = pow(2,bits_per_symbol) + print "bits_per_symbol =", bits_per_symbol + + # turn bytes into k-bit vectors + self.bytes2chunks = \ + gr.packed_to_unpacked_bb(bits_per_symbol, gr.GR_MSB_FIRST) + + if True: + self.gray_coder = gr.map_bb(binary_to_gray[arity]) + else: + self.gray_coder = None + + self.diffenc = gr.diff_encoder_bb(arity) + + self.chunks2symbols = gr.chunks_to_symbols_bc(constellation[arity]) + + # pulse shaping filter + self.rrc_taps = gr.firdes.root_raised_cosine( + spb, # gain (spb since we're interpolating by spb) + spb, # sampling rate + 1.0, # symbol rate + excess_bw, # excess bandwidth (roll-off factor) + ntaps) + + self.rrc_filter = gr.interp_fir_filter_ccf(spb, self.rrc_taps) + + # Connect + if self.gray_coder: + fg.connect(self.bytes2chunks, self.gray_coder) + t = self.gray_coder + else: + t = self.bytes2chunks + + fg.connect(t, self.diffenc, self.chunks2symbols, self.rrc_filter) + + if 1: + fg.connect(self.gray_coder, + gr.file_sink(gr.sizeof_char, "graycoder.dat")) + fg.connect(self.diffenc, + gr.file_sink(gr.sizeof_char, "diffenc.dat")) + + # Initialize base class + gr.hier_block.__init__(self, fg, self.bytes2chunks, self.rrc_filter) + + def samples_per_baud(self): + return self.spb + + def bits_per_baud(self=None): # staticmethod that's also callable on an instance + return 2 + bits_per_baud = staticmethod(bits_per_baud) # make it a static method. RTFM + + + +class dqpsk_demod__coherent_detection_of_differentially_encoded_psk(gr.hier_block): + def __init__(self, fg, spb, excess_bw, costas_alpha=0.005, gain_mu=0.05): + """ + Hierarchical block for RRC-filtered QPSK demodulation + + The input is the complex modulated signal at baseband. + The output is a stream of bits packed 1 bit per byte (LSB) + + @param fg: flow graph + @type fg: flow graph + @param spb: samples per baud >= 2 + @type spb: float + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + @param costas_alpha: loop filter gain + @type costas_alphas: float + @param gain_mu: + @type gain_mu: float + """ + if spb < 2: + raise TypeError, "sbp must be >= 2" + self.spb = spb + + bits_per_symbol = self.bits_per_baud() + arity = pow(2,bits_per_symbol) + print "bits_per_symbol =", bits_per_symbol + + # Automatic gain control + self.preamp = gr.multiply_const_cc(10e-5) + self.agc = gr.agc_cc(1e-3, 1, 1) + + # Costas loop (carrier tracking) + # FIXME: need to decide how to handle this more generally; do we pull it from higher layer? + costas_order = 4 + beta = .25 * costas_alpha * costas_alpha + self.costas_loop = gr.costas_loop_cc(costas_alpha, beta, 0.05, -0.05, costas_order) + + # RRC data filter + ntaps = 11 * spb + self.rrc_taps = gr.firdes.root_raised_cosine( + 1.0, # gain + spb, # sampling rate + 1.0, # symbol rate + excess_bw, # excess bandwidth (roll-off factor) + ntaps) + + self.rrc_filter=gr.fir_filter_ccf(1, self.rrc_taps) + + # symbol clock recovery + omega = spb + gain_omega = .25 * gain_mu * gain_mu + omega_rel_limit = 0.5 + mu = 0.05 + gain_mu = 0.1 + self.clock_recovery=gr.clock_recovery_mm_cc(omega, gain_omega, + mu, gain_mu, omega_rel_limit) + + # find closest constellation point + #rot = .707 + .707j + rot = 1 + rotated_const = map(lambda pt: pt * rot, constellation[arity]) + print "rotated_const =", rotated_const + + self.diffdec = gr.diff_phasor_cc() + #self.diffdec = gr.diff_decoder_bb(arity) + + self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity)) + self.gray_decoder = gr.map_bb(gray_to_binary[arity]) + + # unpack the k bit vector into a stream of bits + self.unpack = gr.unpack_k_bits_bb(bits_per_symbol) + + fg.connect(self.preamp, self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery, + self.diffdec, self.slicer, self.gray_decoder, self.unpack) + #fg.connect(self.preamp, self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery, + # self.slicer, self.diffdec, self.gray_decoder, self.unpack) + + # Debug sinks + if 1: + fg.connect(self.agc, + gr.file_sink(gr.sizeof_gr_complex, "agc.dat")) + fg.connect(self.costas_loop, + gr.file_sink(gr.sizeof_gr_complex, "costas_loop.dat")) + fg.connect(self.rrc_filter, + gr.file_sink(gr.sizeof_gr_complex, "rrc.dat")) + fg.connect(self.clock_recovery, + gr.file_sink(gr.sizeof_gr_complex, "clock_recovery.dat")) + fg.connect(self.slicer, + gr.file_sink(gr.sizeof_char, "slicer.dat")) + fg.connect(self.diffdec, + gr.file_sink(gr.sizeof_gr_complex, "diffdec.dat")) + #fg.connect(self.diffdec, + # gr.file_sink(gr.sizeof_char, "diffdec.dat")) + fg.connect(self.unpack, + gr.file_sink(gr.sizeof_char, "unpack.dat")) + + # Initialize base class + gr.hier_block.__init__(self, fg, self.preamp, self.unpack) + + def samples_per_baud(self): + return self.spb + + def bits_per_baud(self=None): # staticmethod that's also callable on an instance + return 2 + bits_per_baud = staticmethod(bits_per_baud) # make it a static method. RTFM + + +dqpsk_demod = dqpsk_demod__coherent_detection_of_differentially_encoded_psk + diff --git a/gnuradio-examples/python/gmsk2/fusb_options.py b/gnuradio-examples/python/gmsk2/fusb_options.py new file mode 100644 index 000000000..0d04e30c7 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/fusb_options.py @@ -0,0 +1,31 @@ +# +# Copyright 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. +# + +def add_options(parser): + """ + Add Fast USB specifc options to command line parser. + + @param parser: instance of OptionParser + """ + parser.add_option("-B", "--fusb-block-size", type="int", default=0, + help="specify fast usb block size [default=%default]") + parser.add_option("-N", "--fusb-nblocks", type="int", default=0, + help="specify number of fast usb blocks [default=%default]") diff --git a/gnuradio-examples/python/gmsk2/gen_whitener.py b/gnuradio-examples/python/gmsk2/gen_whitener.py new file mode 100755 index 000000000..93aea2b82 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/gen_whitener.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +from gnuradio import gr, gru +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import sys + +class my_graph(gr.flow_graph): + + def __init__(self): + gr.flow_graph.__init__(self) + + parser = OptionParser(option_class=eng_option) + (options, args) = parser.parse_args () + if len(args) != 0: + parser.print_help() + raise SystemExit, 1 + + src = gr.lfsr_32k_source_s() + head = gr.head(gr.sizeof_short, 2048) + self.dst = gr.vector_sink_s() + self.connect(src, head, self.dst) + +if __name__ == '__main__': + try: + fg = my_graph() + fg.run() + f = sys.stdout + i = 0 + for s in fg.dst.data(): + f.write("%3d, " % (s & 0xff,)) + f.write("%3d, " % ((s >> 8) & 0xff,)) + i = i+2 + if i % 16 == 0: + f.write('\n') + + except KeyboardInterrupt: + pass + + diff --git a/gnuradio-examples/python/gmsk2/gmsk_test.py b/gnuradio-examples/python/gmsk2/gmsk_test.py new file mode 100755 index 000000000..7ae69a0a9 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/gmsk_test.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python + +from gnuradio import gr, blks +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import random +import struct + +#import os +#print os.getpid() +#raw_input('Attach and press enter') + + +class my_graph(gr.flow_graph): + + def __init__(self, rx_callback, spb, bt, SNR, freq_error): + gr.flow_graph.__init__(self) + + fg = self + + # Tuning Parameters + gain_mu = 0.002*spb + omega = spb*(1+freq_error) + + # transmitter + self.packet_transmitter = blks.gmsk2_mod_pkts(fg, spb=spb, bt=bt) + + # add some noise + add = gr.add_cc() + noise = gr.noise_source_c(gr.GR_GAUSSIAN, pow(10.0,-SNR/20.0)) + + # channel filter + rx_filt_taps = gr.firdes.low_pass(1,spb,0.8,0.1,gr.firdes.WIN_HANN) + rx_filt = gr.fir_filter_ccf(1,rx_filt_taps) + + # receiver + self.packet_receiver = blks.gmsk2_demod_pkts(fg, callback=rx_callback, + spb=spb, gain_mu=gain_mu, + freq_error=freq_error, omega=omega) + + fg.connect (self.packet_transmitter, (add,0)) + fg.connect (noise, (add,1)) + fg.connect(add, rx_filt, self.packet_receiver) + + +class stats(object): + def __init__(self): + self.npkts = 0 + self.nright = 0 + +def main(): + st = stats() + + def send_pkt(payload='', eof=False): + fg.packet_transmitter.send_pkt(payload, eof) + + def rx_callback(ok, payload): + st.npkts += 1 + if ok: + st.nright += 1 + if len(payload) <= 16: + print "ok = %5r payload = '%s' %d/%d" % (ok, payload, st.nright, st.npkts) + else: + (pktno,) = struct.unpack('!H', payload[0:2]) + print "ok = %5r pktno = %4d len(payload) = %4d %d/%d" % (ok, pktno, len(payload), + st.nright, st.npkts) + + + parser = OptionParser (option_class=eng_option) + parser.add_option("-M", "--megabytes", type="eng_float", default=1, + help="set megabytes to transmit [default=%default]") + parser.add_option("-s", "--size", type="eng_float", default=1500, + help="set packet size [default=%default]") + parser.add_option("","--spb", type=int, default=4, + help="set samples per baud to SPB [default=%default]") + parser.add_option("", "--bt", type="eng_float", default=0.3, + help="set bandwidth time product for Gaussian filter [default=%default]") + parser.add_option("", "--snr", type="eng_float", default=20, + help="set SNR in dB for simulation [default=%default]") + parser.add_option("", "--freq-error", type="eng_float", default=0, + help="set frequency error for simulation [default=%default]") + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + pkt_size = int(options.size) + + fg = my_graph(rx_callback, options.spb, options.bt, options.snr, options.freq_error) + fg.start() + + nbytes = int(1e6 * options.megabytes) + n = 0 + pktno = 0 + + send_pkt('Hello World') + + # generate and send packets + while n < nbytes: + send_pkt(struct.pack('!H', pktno) + (pkt_size - 2) * chr(pktno & 0xff)) + n += pkt_size + pktno += 1 + + send_pkt('Goodbye World') + send_pkt(eof=True) # tell modulator we're not sending any more pkts + + fg.wait() + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/gmsk2/mpsk_test.py b/gnuradio-examples/python/gmsk2/mpsk_test.py new file mode 100755 index 000000000..1f257d9c5 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/mpsk_test.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python + +from gnuradio import gr, blks +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import random +import struct +from mpsk_pkt import * +import cmath + +class my_graph(gr.flow_graph): + + def __init__(self, rx_callback, spb, excess_bw, SNR, freq_error, arity): + gr.flow_graph.__init__(self) + + fg = self + + # Tuning Parameters + gain_mu = 0.05/spb + + # transmitter + self.packet_transmitter = \ + mpsk_mod_pkts(fg, spb=spb, excess_bw=excess_bw, diff=True, arity=arity) + + # ---------------------------------------------------------------- + # Channel model + # ---------------------------------------------------------------- + + awgn = gr.noise_source_c(gr.GR_GAUSSIAN, pow(10.0,-SNR/20.0)) + add = gr.add_cc() # add some noise + fg.connect(awgn, (add,1)) + radians = 17*pi/180 + phase_rotate = gr.multiply_const_cc(cmath.exp(radians * 1j)) + + # ---------------------------------------------------------------- + + # channel filter + rx_filt_taps = gr.firdes.low_pass(1,spb,0.8,0.1,gr.firdes.WIN_HANN) + #rx_filt_taps = (1,) + rx_filt = gr.fir_filter_ccf(1,rx_filt_taps) + + # receiver + self.packet_receiver = \ + mpsk_demod_pkts(fg, callback=rx_callback, + excess_bw=excess_bw, arity=arity, diff=True, + costas_alpha=.005, gain_mu=gain_mu, spb=spb) + + fg.connect (self.packet_transmitter, (add,0)) + fg.connect(add, phase_rotate, rx_filt, self.packet_receiver) + + +class stats(object): + def __init__(self): + self.npkts = 0 + self.nright = 0 + +def main(): + st = stats() + + def send_pkt(payload='', eof=False): + fg.packet_transmitter.send_pkt(payload, eof) + + def rx_callback(ok, payload): + st.npkts += 1 + if ok: + st.nright += 1 + if len(payload) <= 16: + print "ok = %5r payload = '%s' %d/%d" % (ok, payload, st.nright, st.npkts) + else: + (pktno,) = struct.unpack('!H', payload[0:2]) + print "ok = %5r pktno = %4d len(payload) = %4d %d/%d" % (ok, pktno, len(payload), + st.nright, st.npkts) + + + parser = OptionParser (option_class=eng_option) + parser.add_option("-M", "--megabytes", type="eng_float", default=1, + help="set megabytes to transmit [default=%default]") + parser.add_option("-s", "--size", type="eng_float", default=1500, + help="set packet size [default=%default]") + parser.add_option("","--spb", type=int, default=4, + help="set samples per baud to SPB [default=%default]") + parser.add_option("", "--excess-bw", type="eng_float", default=0.4, + help="set excess bandwidth for RRC filter [default=%default]") + parser.add_option("", "--snr", type="eng_float", default=40, + help="set SNR in dB for simulation [default=%default]") + parser.add_option("", "--m-arity", type=int, default=4, + help="PSK arity [default=%default]") + parser.add_option("", "--freq-error", type="eng_float", default=0, + help="set frequency error for simulation [default=%default]") + + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + pkt_size = int(options.size) + + fg = my_graph(rx_callback, options.spb, options.excess_bw, options.snr, + options.freq_error, options.m_arity) + fg.start() + + nbytes = int(1e6 * options.megabytes) + n = 0 + pktno = 0 + + send_pkt('Hello World') + + # generate and send packets + while n < nbytes: + send_pkt(struct.pack('!H', pktno) + (pkt_size - 2) * chr(pktno & 0xff)) + n += pkt_size + pktno += 1 + + send_pkt('Goodbye World') + send_pkt(eof=True) # tell modulator we're not sending any more pkts + + fg.wait() + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/gmsk2/pick_bitrate.py b/gnuradio-examples/python/gmsk2/pick_bitrate.py new file mode 100644 index 000000000..b35991caf --- /dev/null +++ b/gnuradio-examples/python/gmsk2/pick_bitrate.py @@ -0,0 +1,143 @@ +# +# 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. +# + +_default_bitrate = 500e3 + +_valid_spbs = (2,3,4,5,6,7) + +def _gen_tx_info(converter_rate): + results = [] + for spb in _valid_spbs: + for interp in range(16, 512 + 1, 4): + bitrate = converter_rate / interp / spb + results.append((bitrate, spb, interp)) + results.sort() + return results + +def _gen_rx_info(converter_rate): + results = [] + for spb in _valid_spbs: + for decim in range(8, 256 + 1, 2): + bitrate = converter_rate / decim / spb + results.append((bitrate, spb, decim)) + results.sort() + return results + +def _filter_info(info, samples_per_baud, xrate): + if samples_per_baud is not None: + info = [x for x in info if x[1] == samples_per_baud] + if xrate is not None: + info = [x for x in info if x[2] == xrate] + return info + +def _pick_best(target_bitrate, bits_per_symbol, info): + """ + @returns tuple (bitrate, samples_per_baud, interp_rate_or_decim_rate) + """ + if len(info) == 0: + raise RuntimeError, "info is zero length!" + + if target_bitrate is None: # return the fastest one + return info[-1] + + # convert bit rate to symbol rate + target_symbolrate = target_bitrate / bits_per_symbol + + # Find the closest matching symbol rate. + # In the event of a tie, the one with the lowest samples_per_baud wins. + # (We already sorted them, so the first one is the one we take) + + best = info[0] + best_delta = abs(target_symbolrate - best[0]) + for x in info[1:]: + delta = abs(target_symbolrate - x[0]) + if delta < best_delta: + best_delta = delta + best = x + + # convert symbol rate back to bit rate + return ((best[0] * bits_per_symbol),) + best[1:] + +def _pick_bitrate(bitrate, bits_per_symbol, samples_per_baud, + xrate, converter_rate, gen_info): + """ + @returns tuple (bitrate, samples_per_baud, interp_rate_or_decim_rate) + """ + if not isinstance(bits_per_symbol, int) or bits_per_symbol < 1: + raise ValueError, "bits_per_symbol must be an int >= 1" + + if samples_per_baud is not None and xrate is not None: # completely determined + return (float(converter_rate) / xrate / samples_per_baud, + samples_per_baud, xrate) + + if bitrate is None and samples_per_baud is None and xrate is None: + bitrate = _default_bitrate + + # now we have a target bitrate and possibly an xrate or + # samples_per_baud constraint, but not both of them. + + return _pick_best(bitrate, bits_per_symbol, + _filter_info(gen_info(converter_rate), samples_per_baud, xrate)) + +# --------------------------------------------------------------------------------------- + +def pick_tx_bitrate(bitrate, bits_per_symbol, samples_per_baud, + interp_rate, converter_rate=128e6): + """ + Given the 4 input parameters, return at configuration that matches + + @param bitrate: desired bitrate or None + @type bitrate: number or None + @param bits_per_symbol: E.g., BPSK -> 1, QPSK -> 2, 8-PSK -> 3 + @type bits_per_symbol: integer >= 1 + @param samples_per_baud: samples/baud (aka samples/symbol) + @type samples_per_baud: number or None + @param interp_rate: USRP interpolation factor + @type interp_rate: integer or None + @param converter_rate: converter sample rate in Hz + @type converter_rate: number + + @returns tuple (bitrate, samples_per_baud, interp_rate) + """ + return _pick_bitrate(bitrate, bits_per_symbol, samples_per_baud, + interp_rate, converter_rate, _gen_tx_info) + + +def pick_rx_bitrate(bitrate, bits_per_symbol, samples_per_baud, + decim_rate, converter_rate=64e6): + """ + Given the 4 input parameters, return at configuration that matches + + @param bitrate: desired bitrate or None + @type bitrate: number or None + @param bits_per_symbol: E.g., BPSK -> 1, QPSK -> 2, 8-PSK -> 3 + @type bits_per_symbol: integer >= 1 + @param samples_per_baud: samples/baud (aka samples/symbol) + @type samples_per_baud: number or None + @param decim_rate: USRP decimation factor + @type decim_rate: integer or None + @param converter_rate: converter sample rate in Hz + @type converter_rate: number + + @returns tuple (bitrate, samples_per_baud, decim_rate) + """ + return _pick_bitrate(bitrate, bits_per_symbol, samples_per_baud, + decim_rate, converter_rate, _gen_rx_info) diff --git a/gnuradio-examples/python/gmsk2/qpsk.py b/gnuradio-examples/python/gmsk2/qpsk.py new file mode 100644 index 000000000..6ca7f0e61 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/qpsk.py @@ -0,0 +1,418 @@ +# +# 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. +# + +# See gnuradio-examples/python/gmsk2 for examples + +""" +PSK and differential PSK modulation and demodulation. +""" + +from gnuradio import gr, gru +from math import pi, sqrt +import cmath +import Numeric +from pprint import pprint + +_use_gray_code = True + +def make_constellation(m): + return [cmath.exp(i * 2 * pi / m * 1j) for i in range(m)] + +# Common definition of constellations for Tx and Rx +constellation = { + 2 : make_constellation(2), # BPSK + 4 : make_constellation(4), # QPSK + 8 : make_constellation(8) # 8PSK + } + +if 0: + print "const(2) =" + pprint(constellation[2]) + print "const(4) =" + pprint(constellation[4]) + print "const(8) =" + pprint(constellation[8]) + + +if _use_gray_code: + # ----------------------- + # Do Gray code + # ----------------------- + # binary to gray coding + binary_to_gray = { + 2 : (0, 1), + 4 : (0, 1, 3, 2), + 8 : (0, 1, 3, 2, 7, 6, 4, 5) + } + + # gray to binary + gray_to_binary = { + 2 : (0, 1), + 4 : (0, 1, 3, 2), + 8 : (0, 1, 3, 2, 6, 7, 5, 4) + } +else: + # ----------------------- + # Don't Gray code + # ----------------------- + # identity mapping + binary_to_gray = { + 2 : (0, 1), + 4 : (0, 1, 2, 3), + 8 : (0, 1, 2, 3, 4, 5, 6, 7) + } + + # identity mapping + gray_to_binary = { + 2 : (0, 1), + 4 : (0, 1, 2, 3), + 8 : (0, 1, 2, 3, 4, 5, 6, 7) + } + + +# ///////////////////////////////////////////////////////////////////////////// +# mPSK mod/demod with steams of bytes as data i/o +# ///////////////////////////////////////////////////////////////////////////// + + +class mpsk_mod(gr.hier_block): + + def __init__(self, fg, spb, arity, excess_bw, diff=False): + """ + Hierarchical block for RRC-filtered PSK modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + @param fg: flow graph + @type fg: flow graph + @param spb: samples per baud >= 2 + @type spb: integer + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + @param arity: whick PSK: 2, 4, 8 + @type arity: int in {2, 4, 8} + @param diff: differential PSK if true + @type diff: bool + """ + if not isinstance(spb, int) or spb < 2: + raise TypeError, "sbp must be an integer >= 2" + self.spb = spb + + if not arity in (2, 4): + raise ValueError, "n must be 2, 4, or 8" + + ntaps = 11 * spb + + bits_per_symbol = int(gru.log2(arity)) + self.bits_per_symbol = bits_per_symbol + print "bits_per_symbol =", bits_per_symbol + + # turn bytes into k-bit vectors + self.bytes2chunks = \ + gr.packed_to_unpacked_bb(bits_per_symbol, gr.GR_MSB_FIRST) + + if True or arity > 2: + self.gray_coder = gr.map_bb(binary_to_gray[arity]) + else: + self.gray_coder = None + + if diff: + self.diffenc = gr.diff_encoder_bb(arity) + else: + self.diffenc = None + + self.chunks2symbols = gr.chunks_to_symbols_bc(constellation[arity]) + + # pulse shaping filter + self.rrc_taps = gr.firdes.root_raised_cosine( + spb, # gain (spb since we're interpolating by spb) + spb, # sampling rate + 1.0, # symbol rate + excess_bw, # excess bandwidth (roll-off factor) + ntaps) + + self.rrc_filter = gr.interp_fir_filter_ccf(spb, self.rrc_taps) + + # Connect + if self.gray_coder: + fg.connect(self.bytes2chunks, self.gray_coder) + t = self.gray_coder + else: + t = self.bytes2chunks + + if diff: + fg.connect(t, self.diffenc, self.chunks2symbols, self.rrc_filter) + else: + fg.connect(t, self.chunks2symbols, self.rrc_filter) + + if 1: + fg.connect(self.gray_coder, + gr.file_sink(gr.sizeof_char, "graycoder.dat")) + if diff: + fg.connect(self.diffenc, + gr.file_sink(gr.sizeof_char, "diffenc.dat")) + + # Initialize base class + gr.hier_block.__init__(self, fg, self.bytes2chunks, self.rrc_filter) + + def samples_per_baud(self): + return self.spb + + def bits_per_baud(self): + return self.bits_per_symbol + + +class mpsk_demod__coherent_detection_of_differentially_encoded_psk(gr.hier_block): + def __init__(self, fg, spb, arity, excess_bw, diff=False, costas_alpha=0.005, gain_mu=0.05): + """ + Hierarchical block for RRC-filtered PSK demodulation + + The input is the complex modulated signal at baseband. + The output is a stream of bits packed 1 bit per byte (LSB) + + @param fg: flow graph + @type fg: flow graph + @param spb: samples per baud >= 2 + @type spb: float + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + @param arity: whick PSK: 2, 4, 8 + @type arity: int in {2, 4, 8} + @param diff: differential PSK if true + @type diff: bool + @param costas_alpha: loop filter gain + @type costas_alphas: float + @param gain_mu: + @type gain_mu: float + """ + if spb < 2: + raise TypeError, "sbp must be >= 2" + self.spb = spb + + if not arity in (2, 4): + raise ValueError, "n must be 2 or 4" + + if not diff and arity==4: + raise NotImplementedError, "non-differential QPSK not supported yet" + + bits_per_symbol = int(gru.log2(arity)) + print "bits_per_symbol =", bits_per_symbol + + # Automatic gain control + self.agc = gr.agc_cc(1e-3, 1, 1) + + # Costas loop (carrier tracking) + # FIXME: need to decide how to handle this more generally; do we pull it from higher layer? + if arity == 2: + costas_order = 2 + costas_alpha *= 15 # 2nd order loop needs more gain + else: + costas_order = 4 + beta = .25 * costas_alpha * costas_alpha + self.costas_loop = gr.costas_loop_cc(costas_alpha, beta, 0.05, -0.05, costas_order) + + # RRC data filter + ntaps = 11 * spb + self.rrc_taps = gr.firdes.root_raised_cosine( + 1.0, # gain + spb, # sampling rate + 1.0, # symbol rate + excess_bw, # excess bandwidth (roll-off factor) + ntaps) + + self.rrc_filter=gr.fir_filter_ccf(1, self.rrc_taps) + + # symbol clock recovery + omega = spb + gain_omega = .25 * gain_mu * gain_mu + omega_rel_limit = 0.5 + mu = 0.05 + gain_mu = 0.1 + self.clock_recovery=gr.clock_recovery_mm_cc(omega, gain_omega, + mu, gain_mu, omega_rel_limit) + + # find closest constellation point + #rot = .707 + .707j + rot = 1 + rotated_const = map(lambda pt: pt * rot, constellation[arity]) + print "rotated_const =", rotated_const + + if(diff): + self.diffdec = gr.diff_phasor_cc() + #self.diffdec = gr.diff_decoder_bb(arity) + + self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity)) + self.gray_decoder = gr.map_bb(gray_to_binary[arity]) + + # unpack the k bit vector into a stream of bits + self.unpack = gr.unpack_k_bits_bb(bits_per_symbol) + + if(diff): + fg.connect(self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery, + self.diffdec, self.slicer, self.gray_decoder, self.unpack) + else: + fg.connect(self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery, + self.slicer, self.gray_decoder, self.unpack) + + #fg.connect(self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery, + # self.slicer, self.diffdec, self.gray_decoder, self.unpack) + + # Debug sinks + if 1: + fg.connect(self.agc, + gr.file_sink(gr.sizeof_gr_complex, "agc.dat")) + fg.connect(self.costas_loop, + gr.file_sink(gr.sizeof_gr_complex, "costas_loop.dat")) + fg.connect(self.rrc_filter, + gr.file_sink(gr.sizeof_gr_complex, "rrc.dat")) + fg.connect(self.clock_recovery, + gr.file_sink(gr.sizeof_gr_complex, "clock_recovery.dat")) + fg.connect(self.slicer, + gr.file_sink(gr.sizeof_char, "slicer.dat")) + if(diff): + fg.connect(self.diffdec, + gr.file_sink(gr.sizeof_gr_complex, "diffdec.dat")) + #fg.connect(self.diffdec, + # gr.file_sink(gr.sizeof_char, "diffdec.dat")) + fg.connect(self.unpack, + gr.file_sink(gr.sizeof_char, "unpack.dat")) + + # Initialize base class + gr.hier_block.__init__(self, fg, self.agc, self.unpack) + + def samples_per_baud(self): + return self.spb + + def bits_per_baud(self): + return self.bits_per_symbol + + +######################################################################### + +class mpsk_demod__coherent_detection_of_nondifferentially_encoded_psk(gr.hier_block): + def __init__(self, fg, spb, arity, excess_bw, diff=False, costas_alpha=0.005, gain_mu=0.05): + """ + Hierarchical block for RRC-filtered PSK demodulation + + The input is the complex modulated signal at baseband. + The output is a stream of bits packed 1 bit per byte (LSB) + + @param fg: flow graph + @type fg: flow graph + @param spb: samples per baud >= 2 + @type spb: float + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + @param arity: whick PSK: 2, 4, 8 + @type arity: int in {2, 4, 8} + @param diff: differential PSK if true + @type diff: bool + @param costas_alpha: loop filter gain + @type costas_alphas: float + @param gain_mu: + @type gain_mu: float + """ + if spb < 2: + raise TypeError, "sbp must be >= 2" + self.spb = spb + + if not arity in (2, 4): + raise ValueError, "n must be 2 or 4" + + bits_per_symbol = int(gru.log2(arity)) + print "bits_per_symbol =", bits_per_symbol + + # Automatic gain control + self.agc = gr.agc_cc(1e-3, 1, 1) + + # Costas loop (carrier tracking) + # FIXME: need to decide how to handle this more generally; do we pull it from higher layer? + if arity == 2: + costas_order = 2 + costas_alpha *= 15 # 2nd order loop needs more gain + else: + costas_order = 4 + beta = .25 * costas_alpha * costas_alpha + self.costas_loop = gr.costas_loop_cc(costas_alpha, beta, 0.05, -0.05, costas_order) + + # RRC data filter + ntaps = 11 * spb + self.rrc_taps = gr.firdes.root_raised_cosine( + 1.0, # gain + spb, # sampling rate + 1.0, # symbol rate + excess_bw, # excess bandwidth (roll-off factor) + ntaps) + + self.rrc_filter=gr.fir_filter_ccf(1, self.rrc_taps) + + # symbol clock recovery + omega = spb + gain_omega = .25 * gain_mu * gain_mu + omega_rel_limit = 0.5 + mu = 0.05 + gain_mu = 0.1 + self.clock_recovery=gr.clock_recovery_mm_cc(omega, gain_omega, + mu, gain_mu, omega_rel_limit) + + # find closest constellation point + #rot = .707 + .707j + rot = 1 + rotated_const = map(lambda pt: pt * rot, constellation[arity]) + print "rotated_const =", rotated_const + + self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity)) + self.gray_decoder = gr.map_bb(gray_to_binary[arity]) + + # unpack the k bit vector into a stream of bits + self.unpack = gr.unpack_k_bits_bb(bits_per_symbol) + + fg.connect(self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery, + self.slicer, self.gray_decoder, self.unpack) + + # Debug sinks + if 1: + fg.connect(self.agc, + gr.file_sink(gr.sizeof_gr_complex, "agc.dat")) + fg.connect(self.costas_loop, + gr.file_sink(gr.sizeof_gr_complex, "costas_loop.dat")) + fg.connect(self.rrc_filter, + gr.file_sink(gr.sizeof_gr_complex, "rrc.dat")) + fg.connect(self.clock_recovery, + gr.file_sink(gr.sizeof_gr_complex, "clock_recovery.dat")) + fg.connect(self.slicer, + gr.file_sink(gr.sizeof_char, "slicer.dat")) + fg.connect(self.unpack, + gr.file_sink(gr.sizeof_char, "unpack.dat")) + + # Initialize base class + gr.hier_block.__init__(self, fg, self.agc, self.unpack) + + def samples_per_baud(self): + return self.spb + + def bits_per_baud(self): + return self.bits_per_symbol + + +mpsk_demod = mpsk_demod__coherent_detection_of_differentially_encoded_psk +#mpsk_demod = mpsk_demod__coherent_detection_of_nondifferentially_encoded_psk diff --git a/gnuradio-examples/python/gmsk2/qpsk_tester.py b/gnuradio-examples/python/gmsk2/qpsk_tester.py new file mode 100644 index 000000000..ea7bae2eb --- /dev/null +++ b/gnuradio-examples/python/gmsk2/qpsk_tester.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python + +import random +from gnuradio import gr + +default_access_code = '\xAC\xDD\xA4\xE2\xF2\x8C\x20\xFC' + +def string_to_1_0_list(s): + r = [] + for ch in s: + x = ord(ch) + for i in range(8): + t = (x >> i) & 0x1 + r.append(t) + + return r + +def to_1_0_string(L): + return ''.join(map(lambda x: chr(x + ord('0')), L)) + +code = string_to_1_0_list(default_access_code) +access_code = to_1_0_string(code) + +use_agc = 0 +use_rrc_tx = 1 +use_rrc_rx = 1 +use_sync_loop = 1 +use_clock_sync = 1 + +def main(): + fg = gr.flow_graph() + +# data = (1,2,3,4,5,6,7,8,9) + random.seed() + data = [random.randint(1,100) for i in range(20000)] + data[0] = 0 # you know, for the diff encoding stuff + bytes_src = gr.vector_source_b(data,False) + + k = 2 + spb = 50 + ntaps = 11*spb + excess_bw = 0.9 + threshold = 12 + constellation = (1+0j, 0+1j, -1+0j, 0-1j) + + bytes2chunks = gr.packed_to_unpacked_bb(k, gr.GR_MSB_FIRST) + diffenc = gr.diff_encoder_bb(4) + chunks2symbols = gr.chunks_to_symbols_bc(constellation) + + if use_rrc_tx: + rrc_taps_tx = gr.firdes.root_raised_cosine(spb, spb, 1.0, \ + excess_bw, ntaps) + rrc_tx = gr.interp_fir_filter_ccf(spb, rrc_taps_tx) + else: + rrc_tx = gr.interp_fir_filter_ccf(1, (1,)) + +################### CHANNEL MODEL ############################# + + phase_rotate = gr.multiply_const_cc(1-0.36j) + channel = gr.add_cc() + awgn = gr.noise_source_c(gr.GR_GAUSSIAN, 0.5) + fg.connect(awgn, (channel,1)) + +################### CHANNEL MODEL ############################# + + if use_agc: + agc = gr.agc_cc(1e-4, 1, 1) + else: + agc = gr.multiply_const_cc(1) + + # Downconverter + if use_sync_loop: + costas_alpha=0.005 + beta = costas_alpha*costas_alpha*0.25 + sync_loop = gr.costas_loop_cc(costas_alpha, beta, 0.05, -0.05, 0) + else: + sync_loop = gr.multiply_const_cc(1) + + if use_rrc_rx: + rrc_taps_rx = gr.firdes.root_raised_cosine(1, spb, 1.0, \ + excess_bw, ntaps) + rrc_rx = gr.fir_filter_ccf(1, rrc_taps_rx) + else: + rrc_rx = gr.fir_filter_ccf(1, (1,)) + + # Sampler + if use_clock_sync: + mu = 0.05 + omega = spb + gain_mu = 0.05 + gain_omega = 0.25*gain_mu*gain_mu + omega_rel_limit = 0.5 + clock_sync = gr.clock_recovery_mm_cc(omega, gain_omega, mu, \ + gain_mu, omega_rel_limit) + #clock_sync.set_verbose(True); + else: + clock_sync = gr.fir_filter_ccf(1, (1,)) + + diff_phasor = gr.diff_phasor_cc() + slicer = gr.constellation_decoder_cb((constellation), (0,1,2,3)) + unpack = gr.unpack_k_bits_bb(k) + access = gr.correlate_access_code_bb(access_code,threshold) + + sink = gr.file_sink(gr.sizeof_char, 'output.dat') + + fg.connect(bytes_src, bytes2chunks, diffenc, chunks2symbols, rrc_tx) + fg.connect(rrc_tx, phase_rotate, channel, agc) + fg.connect(agc, sync_loop, rrc_rx, clock_sync, diff_phasor, slicer, sink) + + test = gr.file_sink(gr.sizeof_gr_complex, 'test.dat') + fg.connect(rrc_rx, test) + + fg.connect(chunks2symbols, gr.file_sink(gr.sizeof_gr_complex, 'rrc_tx.dat')) # into TX RRC + fg.connect(channel, gr.file_sink(gr.sizeof_gr_complex, 'channel.dat')) # Out of TX RRC + fg.connect(rrc_rx, gr.file_sink(gr.sizeof_gr_complex, 'rrc_rx.dat')) # Out of RX RRC -> clock_sync + fg.connect(clock_sync, gr.file_sink(gr.sizeof_gr_complex, 'clock_sync.dat')) # Out of M&M sync loop + fg.connect(bytes2chunks, gr.file_sink(gr.sizeof_char, 'source.dat')) + + fg.start() + fg.wait() + +if __name__ == "__main__": + main() + + + + diff --git a/gnuradio-examples/python/gmsk2/qpsk_usrp_tester.py b/gnuradio-examples/python/gmsk2/qpsk_usrp_tester.py new file mode 100644 index 000000000..bf7698a2b --- /dev/null +++ b/gnuradio-examples/python/gmsk2/qpsk_usrp_tester.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python + +import random +from gnuradio import gr, gru, usrp + +default_access_code = '\xAC\xDD\xA4\xE2\xF2\x8C\x20\xFC' + +def string_to_1_0_list(s): + r = [] + for ch in s: + x = ord(ch) + for i in range(8): + t = (x >> i) & 0x1 + r.append(t) + + return r + +def to_1_0_string(L): + return ''.join(map(lambda x: chr(x + ord('0')), L)) + +code = string_to_1_0_list(default_access_code) + +access_code = to_1_0_string(code) + +def main(): + + fg = gr.flow_graph() + f_rf = 5e6 + fs = 400e3 + sr = 100e3 + alpha = 0.5 + M = 4 + k = int(gru.log2(M)) + + # Source + src = usrp.source_c () + adc_rate = src.adc_rate() + usrp_decim = int(adc_rate / fs) + src.set_decim_rate(usrp_decim) + + subdev_spec = usrp.pick_rx_subdevice(src) + subdev = usrp.selected_subdev(src, subdev_spec) + print "Using RX d'board %s" % (subdev.side_and_name(),) + src.set_mux(usrp.determine_rx_mux_value(src, subdev_spec)) + src.tune(0, subdev, f_rf) + + g = subdev.gain_range() + subdev.set_gain(g[1]) + subdev.set_auto_tr(True) + + print "USRP Decimation Rate = %d" % usrp_decim + print "RF Frequency = %d" % f_rf + + agc = gr.multiply_const_cc(0.0025) + + # Downconverter + costas_alpha=0.005 + beta = costas_alpha*costas_alpha*0.25 + sync_loop = gr.costas_loop_cc(costas_alpha, beta, 0.05, -0.05, 0) + + # Stage 2 +# fs2 = 200e3 +# D = int(fs / fs2) +# decimator = gr.keep_one_in_n(gr.sizeof_gr_complex, D) +# print "D = %d\nAfter decimator fs = %f" % (D, fs2) + + # Demodulator + taps = gr.firdes.root_raised_cosine(1, fs, sr, alpha, 45) + rrc = gr.fir_filter_ccf(1, taps) + + # Sampler + mu = 0.01 + omega = 4.3 + gain_mu = 0.05 + gain_omega = 0.25*gain_mu*gain_mu + omega_rel_limit = 0.5 + clock_sync = gr.clock_recovery_mm_cc(omega, gain_omega, mu, + gain_mu, omega_rel_limit) + clock_sync.set_verbose(False) + + diff_phasor = gr.diff_phasor_cc() + + threshold = 12 + constellation = (1+0j, 0+1j, -1+0j, 0-1j) + slicer = gr.constellation_decoder_cb((constellation), (0,1,2,3)) + unpack = gr.unpack_k_bits_bb(k) + access = gr.correlate_access_code_bb(access_code,threshold) + + test = gr.file_sink(gr.sizeof_gr_complex, 'test.dat') + sink = gr.file_sink(gr.sizeof_char, 'output.dat') + + fg.connect(src, agc, sync_loop, rrc, clock_sync) + fg.connect(clock_sync, diff_phasor, slicer, unpack, access, sink) + + fg.connect(slicer, gr.file_sink(gr.sizeof_char, 'chunks.dat')) + fg.connect(unpack, gr.file_sink(gr.sizeof_char, 'unpack.dat')) + fg.connect(clock_sync, gr.file_sink(gr.sizeof_gr_complex, 'phasor.dat')) + + fg.start() + fg.wait() + +if __name__ == "__main__": + main() diff --git a/gnuradio-examples/python/gmsk2/receive_path.py b/gnuradio-examples/python/gmsk2/receive_path.py new file mode 100644 index 000000000..505ff7c4d --- /dev/null +++ b/gnuradio-examples/python/gmsk2/receive_path.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python +# +# 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. +# + +from gnuradio import gr, gru, blks +from gnuradio import usrp + +# from current dir +from pick_bitrate import pick_rx_bitrate + +# ///////////////////////////////////////////////////////////////////////////// +# receive path +# ///////////////////////////////////////////////////////////////////////////// + +class receive_path(gr.hier_block): + def __init__(self, fg, demod_class, rx_subdev_spec, + bitrate, decim, spb, + rx_callback, options, demod_kwargs): + + self.u = usrp.source_c (fusb_block_size=options.fusb_block_size, + fusb_nblocks=options.fusb_nblocks) + adc_rate = self.u.adc_rate() + + (self._bitrate, self._spb, self._decim) = \ + pick_rx_bitrate(bitrate, demod_class.bits_per_baud(), spb, decim, adc_rate) + + self.u.set_decim_rate(self._decim) + sw_decim = 1 + + if rx_subdev_spec is None: + rx_subdev_spec = usrp.pick_rx_subdevice(self.u) + self.subdev = usrp.selected_subdev(self.u, rx_subdev_spec) + print "Using RX d'board %s" % (self.subdev.side_and_name(),) + + self.u.set_mux(usrp.determine_rx_mux_value(self.u, rx_subdev_spec)) + + # Create filter to get actual channel we want + chan_coeffs = gr.firdes.low_pass (1.0, # gain + sw_decim * self._spb, # sampling rate + 1.0, # midpoint of trans. band + 0.1, # width of trans. band + gr.firdes.WIN_HANN) # filter type + + print "len(rx_chan_coeffs) =", len(chan_coeffs) + + # Decimating Channel filter + # complex in and out, float taps + self.chan_filt = gr.fft_filter_ccc(sw_decim, chan_coeffs) + #self.chan_filt = gr.fir_filter_ccf(sw_decim, chan_coeffs) + + # receiver + self.packet_receiver = \ + blks.demod_pkts(fg, + demod_class(fg, spb=self._spb, **demod_kwargs), + access_code=None, + callback=rx_callback, + threshold=-1) + + fg.connect(self.u, self.chan_filt, self.packet_receiver) + gr.hier_block.__init__(self, fg, None, None) + + g = self.subdev.gain_range() + #self.set_gain((g[0] + g[1])/2) # set gain to midpoint + self.set_gain(g[1]) # set gain to max + self.set_auto_tr(True) # enable Auto Transmit/Receive switching + + # Carrier Sensing Blocks + alpha = 0.001 + thresh = 30 # in dB, will have to adjust + self.probe = gr.probe_avg_mag_sqrd_c(thresh,alpha) + fg.connect(self.chan_filt, self.probe) + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + + 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 up converter. + """ + r = self.u.tune(0, self.subdev, target_freq) + if r: + return True + + return False + + def set_gain(self, gain): + if gain is None: + r = self.subdev.gain_range() + gain = (r[0] + r[1])/2 # set gain to midpoint + self.gain = gain + return self.subdev.set_gain(gain) + + def set_auto_tr(self, enable): + return self.subdev.set_auto_tr(enable) + + def bitrate(self): + return self._bitrate + + def spb(self): + return self._spb + + def decim(self): + return self._decim + + + def carrier_sensed(self): + """ + Return True if we think carrier is present. + """ + #return self.probe.level() > X + return self.probe.unmuted() + + def carrier_threshold(self): + """ + Return current setting in dB. + """ + return self.probe.threshold() + + def set_carrier_threshold(self, threshold_in_db): + """ + Set carrier threshold. + + @param threshold_in_db: set detection threshold + @type threshold_in_db: float (dB) + """ + self.probe.set_threshold(threshold_in_db) + diff --git a/gnuradio-examples/python/gmsk2/rx_voice.py b/gnuradio-examples/python/gmsk2/rx_voice.py new file mode 100755 index 000000000..3f5cc3b47 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/rx_voice.py @@ -0,0 +1,132 @@ +#!/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, gru, blks +from gnuradio import usrp +from gnuradio import audio +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +from gnuradio.vocoder import gsm_full_rate + +import random +import struct + +# from current dir +from receive_path import receive_path + +#import os +#print os.getpid() +#raw_input('Attach and press enter') + + +class audio_tx(gr.hier_block): + def __init__(self, fg): + self.packet_src = gr.message_source(33) + voice_decoder = gsm_full_rate.decode_ps() + s2f = gr.short_to_float () + sink_scale = gr.multiply_const_ff(1.0/32767.) + audio_sink = audio.sink(8000) + fg.connect(self.packet_src, voice_decoder, s2f, sink_scale, audio_sink) + gr.hier_block.__init__(self, fg, self.packet_src, audio_sink) + + def msgq(self): + return self.packet_src.msgq() + + +class my_graph(gr.flow_graph): + + def __init__(self, rx_subdev_spec, bitrate, decim_rate, spb, rx_callback, log): + gr.flow_graph.__init__(self) + self.rxpath = receive_path(self, rx_subdev_spec, bitrate, decim_rate, + spb, rx_callback, log) + + self.audio_tx = audio_tx(self) + + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +global n_rcvd, n_right + +def main(): + global n_rcvd, n_right + + n_rcvd = 0 + n_right = 0 + + def rx_callback(ok, payload): + global n_rcvd, n_right + n_rcvd += 1 + if ok: + n_right += 1 + + fg.audio_tx.msgq().insert_tail(gr.message_from_string(payload)) + + print "ok = %r n_rcvd = %4d n_right = %4d" % ( + ok, n_rcvd, n_right) + + parser = OptionParser (option_class=eng_option) + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, + help="select USRP Rx side A or B") + parser.add_option("-f", "--freq", type="eng_float", default=423.1e6, + help="set Rx frequency to FREQ [default=%default]", metavar="FREQ") + parser.add_option("-r", "--bitrate", type="eng_float", default=100e3, + help="specify bitrate. spb and interp will be derived.") + parser.add_option("-S", "--spb", type="int", default=None, help="set samples/baud [default=%default]") + parser.add_option("-d", "--decim", type="intx", default=None, + help="set fpga decim rate to DECIM [default=%default]") + parser.add_option("-g", "--gain", type="eng_float", default=27, + help="set rx gain") + parser.add_option("","--log", action="store_true", default=False, + help="enable diagnostic logging") + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if options.freq < 1e6: + options.freq *= 1e6 + + # build the graph + fg = my_graph(options.rx_subdev_spec, options.bitrate, + options.decim, options.spb, rx_callback, options.log) + + print "bitrate: %sb/sec" % (eng_notation.num_to_str(fg.rxpath.bitrate()),) + print "spb: %3d" % (fg.rxpath.spb(),) + print "decim: %3d" % (fg.rxpath.decim(),) + + fg.rxpath.set_freq(options.freq) + fg.rxpath.set_gain(options.gain) + print "Rx gain_range: ", fg.rxpath.subdev.gain_range(), " using", fg.rxpath.gain + + fg.start() # start flow graph + fg.wait() # wait for it to finish + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/gmsk2/transmit_path.py b/gnuradio-examples/python/gmsk2/transmit_path.py new file mode 100644 index 000000000..a1df1579f --- /dev/null +++ b/gnuradio-examples/python/gmsk2/transmit_path.py @@ -0,0 +1,108 @@ +# +# 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. +# + +from gnuradio import gr, gru, blks +from gnuradio import usrp + +# from current dir +from pick_bitrate import pick_tx_bitrate + +# ///////////////////////////////////////////////////////////////////////////// +# transmit path +# ///////////////////////////////////////////////////////////////////////////// + +class transmit_path(gr.hier_block): + def __init__(self, fg, mod_class, tx_subdev_spec, + bitrate, interp, spb, gain, + options, mod_kwargs): + + self.normal_gain = gain + + self.u = usrp.sink_c (fusb_block_size=options.fusb_block_size, + fusb_nblocks=options.fusb_nblocks) + dac_rate = self.u.dac_rate(); + + print mod_class + print mod_class.bits_per_baud() + (self._bitrate, self._spb, self._interp) = \ + pick_tx_bitrate(bitrate, mod_class.bits_per_baud(), spb, interp, dac_rate) + + self.u.set_interp_rate(self._interp) + + # determine the daughterboard subdevice we're using + if tx_subdev_spec is None: + tx_subdev_spec = usrp.pick_tx_subdevice(self.u) + self.u.set_mux(usrp.determine_tx_mux_value(self.u, tx_subdev_spec)) + self.subdev = usrp.selected_subdev(self.u, tx_subdev_spec) + print "Using TX d'board %s" % (self.subdev.side_and_name(),) + + # transmitter + self.packet_transmitter = \ + blks.mod_pkts(fg, + mod_class(fg, spb=self._spb, **mod_kwargs), + access_code=None, + msgq_limit=4, + pad_for_usrp=True) + + self.amp = gr.multiply_const_cc (self.normal_gain) + + fg.connect(self.packet_transmitter, self.amp, self.u) + gr.hier_block.__init__(self, fg, None, None) + + self.set_gain(self.subdev.gain_range()[1]) # set max Tx gain + self.set_auto_tr(True) # enable Auto Transmit/Receive switching + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + + 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 up converter. + """ + r = self.u.tune(self.subdev._which, self.subdev, target_freq) + if r: + return True + + return False + + def set_gain(self, gain): + self.gain = gain + self.subdev.set_gain(gain) + + def set_auto_tr(self, enable): + return self.subdev.set_auto_tr(enable) + + def send_pkt(self, payload='', eof=False): + return self.packet_transmitter.send_pkt(payload, eof) + + def bitrate(self): + return self._bitrate + + def spb(self): + return self._spb + + def interp(self): + return self._interp diff --git a/gnuradio-examples/python/gmsk2/tunnel.py b/gnuradio-examples/python/gmsk2/tunnel.py new file mode 100755 index 000000000..bc10d3e34 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/tunnel.py @@ -0,0 +1,300 @@ +#!/usr/bin/env python +# +# 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. +# + + +# ///////////////////////////////////////////////////////////////////////////// +# +# This code sets up up a virtual ethernet interface (typically gr0), +# and relays packets between the interface and the GNU Radio PHY+MAC +# +# What this means in plain language, is that if you've got a couple +# of USRPs on different machines, and if you run this code on those +# machines, you can talk between them using normal TCP/IP networking. +# +# ///////////////////////////////////////////////////////////////////////////// + + +from gnuradio import gr, gru, blks +from gnuradio import usrp +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +import random +import time +import struct +import sys +import os + +# from current dir +from transmit_path import transmit_path +from receive_path import receive_path +import fusb_options + +#print os.getpid() +#raw_input('Attach and press enter') + + +# ///////////////////////////////////////////////////////////////////////////// +# +# Use the Universal TUN/TAP device driver to move packets to/from kernel +# +# See /usr/src/linux/Documentation/networking/tuntap.txt +# +# ///////////////////////////////////////////////////////////////////////////// + +# Linux specific... +# TUNSETIFF ifr flags from <linux/tun_if.h> + +IFF_TUN = 0x0001 # tunnel IP packets +IFF_TAP = 0x0002 # tunnel ethernet frames +IFF_NO_PI = 0x1000 # don't pass extra packet info +IFF_ONE_QUEUE = 0x2000 # beats me ;) + +def open_tun_interface(tun_device_filename): + from fcntl import ioctl + + mode = IFF_TAP | IFF_NO_PI + TUNSETIFF = 0x400454ca + + tun = os.open(tun_device_filename, os.O_RDWR) + ifs = ioctl(tun, TUNSETIFF, struct.pack("16sH", "gr%d", mode)) + ifname = ifs[:16].strip("\x00") + return (tun, ifname) + + +# ///////////////////////////////////////////////////////////////////////////// +# the flow graph +# ///////////////////////////////////////////////////////////////////////////// + +class my_graph(gr.flow_graph): + + def __init__(self, mod_class, demod_class, tx_subdev_spec, rx_subdev_spec, + bitrate, decim_rate, interp_rate, spb, + bt, rx_callback, options): + + gr.flow_graph.__init__(self) + self.txpath = transmit_path(self, mod_class, tx_subdev_spec, + bitrate, interp_rate, spb, + bt, options) + self.rxpath = receive_path(self, demod_class, rx_subdev_spec, + bitrate, decim_rate, spb, + rx_callback, options) + + def send_pkt(self, payload='', eof=False): + return self.txpath.send_pkt(payload, eof) + + def carrier_sensed(self): + """ + Return True if the receive path thinks there's carrier + """ + return self.rxpath.carrier_sensed() + + +# ///////////////////////////////////////////////////////////////////////////// +# Carrier Sense MAC +# ///////////////////////////////////////////////////////////////////////////// + +class cs_mac(object): + """ + Prototype carrier sense MAC + + Reads packets from the TUN/TAP interface, and sends them to the PHY. + Receives packets from the PHY via phy_rx_callback, and sends them + into the TUN/TAP interface. + + Of course, we're not restricted to getting packets via TUN/TAP, this + is just an example. + """ + def __init__(self, tun_fd, verbose=False): + self.tun_fd = tun_fd # file descriptor for TUN/TAP interface + self.verbose = verbose + self.fg = None # flow graph (access to PHY) + + def set_flow_graph(self, fg): + self.fg = fg + + def phy_rx_callback(self, ok, payload): + """ + Invoked by thread associated with PHY to pass received packet up. + + @param ok: bool indicating whether payload CRC was OK + @param payload: contents of the packet (string) + """ + if self.verbose: + print "Rx: ok = %r len(payload) = %4d" % (ok, len(payload)) + if ok: + os.write(self.tun_fd, payload) + + def main_loop(self): + """ + Main loop for MAC. + Only returns if we get an error reading from TUN. + + FIXME: may want to check for EINTR and EAGAIN and reissue read + """ + min_delay = 0.001 # seconds + + while 1: + payload = os.read(self.tun_fd, 10*1024) + if not payload: + self.fg.send_pkt(eof=True) + break + + if self.verbose: + print "Tx: len(payload) = %4d" % (len(payload),) + + delay = min_delay + while self.fg.carrier_sensed(): + sys.stderr.write('B') + time.sleep(delay) + if delay < 0.050: + delay = delay * 2 # exponential back-off + + self.fg.send_pkt(payload) + + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +def main(): + + parser = OptionParser (option_class=eng_option) + parser.add_option("-f", "--freq", type="eng_float", default=423.1e6, + help="set Tx and Rx frequency to FREQ [default=%default]", metavar="FREQ") + parser.add_option("-r", "--bitrate", type="eng_float", default=None, + help="specify bitrate. spb and interp will be derived.") + parser.add_option("-g", "--rx-gain", type="eng_float", default=27, + help="set rx gain") + parser.add_option("-T", "--tx-subdev-spec", type="subdev", default=None, + help="select USRP Tx side A or B") + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, + help="select USRP Rx side A or B") + parser.add_option("-S", "--spb", type="int", default=None, help="set samples/baud [default=%default]") + parser.add_option("-d", "--decim", type="intx", default=None, + help="set fpga decim rate to DECIM [default=%default]") + parser.add_option("-i", "--interp", type="intx", default=None, + help="set fpga interpolation rate to INTERP [default=%default]") + parser.add_option("-c", "--carrier-threshold", type="eng_float", default=30, + help="set carrier detect threshold (dB) [default=%default]") + parser.add_option("", "--bt", type="float", default=0.3, help="set bandwidth-time product [default=%default]") + parser.add_option("","--tun-device-filename", default="/dev/net/tun", + help="path to tun device file [default=%default]") + parser.add_option("-v","--verbose", action="store_true", default=False) + fusb_options.add_options(parser) + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if options.freq < 1e6: + options.freq *= 1e6 + + # open the TUN/TAP interface + (tun_fd, tun_ifname) = open_tun_interface(options.tun_device_filename) + + # Attempt to enable realtime scheduling + r = gr.enable_realtime_scheduling() + if r == gr.RT_OK: + realtime = True + else: + realtime = False + print "Note: failed to enable realtime scheduling" + + + # If the user hasn't set the fusb_* parameters on the command line, + # pick some values that will reduce latency. + + if options.fusb_block_size == 0 and options.fusb_nblocks == 0: + if realtime: # be more aggressive + options.fusb_block_size = gr.prefs().get_long('fusb', 'rt_block_size', 1024) + options.fusb_nblocks = gr.prefs().get_long('fusb', 'rt_nblocks', 16) + else: + options.fusb_block_size = gr.prefs().get_long('fusb', 'block_size', 4096) + options.fusb_nblocks = gr.prefs().get_long('fusb', 'nblocks', 16) + + print "fusb_block_size =", options.fusb_block_size + print "fusb_nblocks =", options.fusb_nblocks + + # instantiate the MAC + mac = cs_mac(tun_fd, verbose=True) + + + # build the graph (PHY) + fg = my_graph(blks.gmsk2_mod, blks.gmsk2_demod, + options.tx_subdev_spec, options.rx_subdev_spec, + options.bitrate, options.decim, options.interp, + options.spb, options.bt, mac.phy_rx_callback, + options) + + mac.set_flow_graph(fg) # give the MAC a handle for the PHY + + if fg.txpath.bitrate() != fg.rxpath.bitrate(): + print "WARNING: Transmit bitrate = %sb/sec, Receive bitrate = %sb/sec" % ( + eng_notation.num_to_str(fg.txpath.bitrate()), + eng_notation.num_to_str(fg.rxpath.bitrate())) + + print "bitrate: %sb/sec" % (eng_notation.num_to_str(fg.txpath.bitrate()),) + print "spb: %3d" % (fg.txpath.spb(),) + print "interp: %3d" % (fg.txpath.interp(),) + + ok = fg.txpath.set_freq(options.freq) + if not ok: + print "Failed to set Tx frequency to %s" % (eng_notation.num_to_str(options.freq),) + raise SystemExit + + ok = fg.rxpath.set_freq(options.freq) + if not ok: + print "Failed to set Rx frequency to %s" % (eng_notation.num_to_str(options.freq),) + raise SystemExit + + fg.rxpath.set_gain(options.rx_gain) + print "Rx gain_range: ", fg.rxpath.subdev.gain_range(), " using", fg.rxpath.gain + + fg.rxpath.set_carrier_threshold(options.carrier_threshold) + print "Carrier sense threshold:", options.carrier_threshold, "dB" + + print + print "Allocated virtual ethernet interface: %s" % (tun_ifname,) + print "You must now use ifconfig to set its IP address. E.g.," + print + print " $ sudo ifconfig %s 10.10.10.1" % (tun_ifname,) + print + print "Be sure to use a different address in the same subnet for each machine." + print + + + fg.start() # Start executing the flow graph (runs in separate threads) + + mac.main_loop() # don't expect this to return... + + fg.stop() # but if it does, tell flow graph to stop. + fg.wait() # wait for it to finish + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/gmsk2/tx_voice.py b/gnuradio-examples/python/gmsk2/tx_voice.py new file mode 100755 index 000000000..9e274e5e0 --- /dev/null +++ b/gnuradio-examples/python/gmsk2/tx_voice.py @@ -0,0 +1,139 @@ +#!/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, gru, blks +from gnuradio import usrp +from gnuradio import audio +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +from gnuradio.vocoder import gsm_full_rate + +import random +import time +import struct +import sys + +# from current dir +from transmit_path import transmit_path + +#import os +#print os.getpid() +#raw_input('Attach and press enter') + + +class my_graph(gr.flow_graph): + + def __init__(self, tx_subdev_spec, bitrate, interp_rate, spb, bt): + gr.flow_graph.__init__(self) + self.txpath = transmit_path(self, tx_subdev_spec, bitrate, interp_rate, spb, bt) + + audio_input = "" + sample_rate = 8000 + src = audio.source(sample_rate, audio_input) + src_scale = gr.multiply_const_ff(32767) + f2s = gr.float_to_short() + voice_coder = gsm_full_rate.encode_sp() + self.packets_from_encoder = gr.msg_queue() + packet_sink = gr.message_sink(33, self.packets_from_encoder, False) + self.connect(src, src_scale, f2s, voice_coder, packet_sink) + + + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +def main(): + + def send_pkt(payload='', eof=False): + return fg.txpath.send_pkt(payload, eof) + + def rx_callback(ok, payload): + print "ok = %r, payload = '%s'" % (ok, payload) + + parser = OptionParser (option_class=eng_option) + parser.add_option("-T", "--tx-subdev-spec", type="subdev", default=None, + help="select USRP Tx side A or B") + parser.add_option("-f", "--freq", type="eng_float", default=423.1e6, + help="set Tx and Rx frequency to FREQ [default=%default]", metavar="FREQ") + parser.add_option("-r", "--bitrate", type="eng_float", default=100e3, + help="specify bitrate. spb and interp will be derived.") + parser.add_option("-S", "--spb", type="int", default=None, help="set samples/baud [default=%default]") + parser.add_option("-i", "--interp", type="intx", default=None, + help="set fpga interpolation rate to INTERP [default=%default]") + parser.add_option("-s", "--size", type="eng_float", default=1500, + help="set packet size [default=%default]") + parser.add_option("", "--bt", type="float", default=0.3, help="set bandwidth-time product [default=%default]") + parser.add_option("-M", "--megabytes", type="eng_float", default=1.0, + help="set megabytes to transmit [default=%default]") + parser.add_option("","--discontinuous", action="store_true", default=False, + help="enable discontinous transmission (bursts of 5 packets)") + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if options.freq < 1e6: + options.freq *= 1e6 + + pkt_size = int(options.size) + + # build the graph + fg = my_graph(options.tx_subdev_spec, options.bitrate, options.interp, + options.spb, options.bt) + + print "bitrate: %sb/sec" % (eng_notation.num_to_str(fg.txpath.bitrate()),) + print "spb: %3d" % (fg.txpath.spb(),) + print "interp: %3d" % (fg.txpath.interp(),) + + fg.txpath.set_freq(options.freq) + + fg.start() # start flow graph + + # generate and send packets + nbytes = int(1e6 * options.megabytes) + n = 0 + pktno = 0 + + while n < nbytes: + packet = fg.packets_from_encoder.delete_head() + s = packet.to_string() + send_pkt(s) + n += len(s) + sys.stderr.write('.') + if options.discontinuous and pktno % 5 == 4: + time.sleep(1) + pktno += 1 + + send_pkt(eof=True) + fg.wait() # wait for it to finish + fg.txpath.set_auto_tr(False) + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/mc4020/Makefile.am b/gnuradio-examples/python/mc4020/Makefile.am new file mode 100644 index 000000000..21bed3e46 --- /dev/null +++ b/gnuradio-examples/python/mc4020/Makefile.am @@ -0,0 +1,28 @@ +# +# 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. +# + +EXTRA_DIST = \ + fm_demod.py \ + mc4020_fft.py + + + + diff --git a/gnuradio-examples/python/mc4020/fm_demod.py b/gnuradio-examples/python/mc4020/fm_demod.py new file mode 100755 index 000000000..e58407f73 --- /dev/null +++ b/gnuradio-examples/python/mc4020/fm_demod.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python + +from gnuradio import gr +from gnuradio import audio +from gnuradio import mc4020 +import sys + +def high_speed_adc (fg, input_rate): + # return gr.file_source (gr.sizeof_short, "dummy.dat", False) + return mc4020.source (input_rate, mc4020.MCC_CH3_EN | mc4020.MCC_ALL_1V) + +# +# return a gr.flow_graph +# +def build_graph (freq1, freq2): + input_rate = 20e6 + cfir_decimation = 125 + audio_decimation = 5 + + quad_rate = input_rate / cfir_decimation + audio_rate = quad_rate / audio_decimation + + fg = gr.flow_graph () + + # use high speed ADC as input source + src = high_speed_adc (fg, input_rate) + + # compute FIR filter taps for channel selection + channel_coeffs = \ + gr.firdes.low_pass (1.0, # gain + input_rate, # sampling rate + 250e3, # low pass cutoff freq + 8*100e3, # width of trans. band + gr.firdes.WIN_HAMMING) + + # input: short; output: complex + chan_filter1 = \ + gr.freq_xlating_fir_filter_scf (cfir_decimation, + channel_coeffs, + freq1, # 1st station freq + input_rate) + + (head1, tail1) = build_pipeline (fg, quad_rate, audio_decimation) + + # sound card as final sink + audio_sink = audio.sink (int (audio_rate)) + + # now wire it all together + fg.connect (src, chan_filter1) + fg.connect (chan_filter1, head1) + fg.connect (tail1, (audio_sink, 0)) + + # two stations at once? + if freq2: + # Extract the second station and connect + # it to a second pipeline... + + # input: short; output: complex + chan_filter2 = \ + gr.freq_xlating_fir_filter_scf (cfir_decimation, + channel_coeffs, + freq2, # 2nd station freq + input_rate) + + (head2, tail2) = build_pipeline (fg, quad_rate, audio_decimation) + + fg.connect (src, chan_filter2) + fg.connect (chan_filter2, head2) + fg.connect (tail2, (audio_sink, 1)) + + return fg + +def build_pipeline (fg, quad_rate, audio_decimation): + '''Given a flow_graph, fg, construct a pipeline + for demodulating a broadcast FM signal. The + input is the downconverteed complex baseband + signal. The output is the demodulated audio. + + build_pipeline returns a two element tuple + containing the input and output endpoints. + ''' + fm_demod_gain = 2200.0/32768.0 + audio_rate = quad_rate / audio_decimation + volume = 1.0 + + # input: complex; output: float + fm_demod = gr.quadrature_demod_cf (volume*fm_demod_gain) + + # compute FIR filter taps for audio filter + width_of_transition_band = audio_rate / 32 + audio_coeffs = gr.firdes.low_pass (1.0, # gain + quad_rate, # sampling rate + audio_rate/2 - width_of_transition_band, + width_of_transition_band, + gr.firdes.WIN_HAMMING) + + # input: float; output: float + audio_filter = gr.fir_filter_fff (audio_decimation, audio_coeffs) + + fg.connect (fm_demod, audio_filter) + return ((fm_demod, 0), (audio_filter, 0)) + + +def main (args): + nargs = len (args) + if nargs == 1: + freq1 = float (args[0]) * 1e6 + freq2 = None + elif nargs == 2: + freq1 = float (args[0]) * 1e6 + freq2 = float (args[1]) * 1e6 + else: + sys.stderr.write ('usage: fm_demod freq1 [freq2]\n') + sys.exit (1) + + # connect to RF front end + rf_front_end = gr.microtune_4937_eval_board () + if not rf_front_end.board_present_p (): + raise IOError, 'RF front end not found' + + # set front end gain + rf_front_end.set_AGC (300) + IF_freq = rf_front_end.get_output_freq () + IF_freq = 5.75e6 + + if not freq2: # one station + + rf_front_end.set_RF_freq (freq1) + fg = build_graph (IF_freq, None) + + else: # two stations + + if abs (freq1 - freq2) > 5.5e6: + raise IOError, 'freqs too far apart' + + target_freq = (freq1 + freq2) / 2 + actual_freq = rf_front_end.set_RF_freq (target_freq) + #actual_freq = target_freq + + fg = build_graph (IF_freq + freq1 - actual_freq, + IF_freq + freq2 - actual_freq) + + fg.start () # fork thread(s) and return + raw_input ('Press Enter to quit: ') + fg.stop () + +if __name__ == '__main__': + main (sys.argv[1:]) + + diff --git a/gnuradio-examples/python/mc4020/mc4020_fft.py b/gnuradio-examples/python/mc4020/mc4020_fft.py new file mode 100755 index 000000000..3348ad4f6 --- /dev/null +++ b/gnuradio-examples/python/mc4020/mc4020_fft.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# +# 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. +# + +from gnuradio import gr +from gnuradio import mc4020 +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import stdgui, fftsink +from optparse import OptionParser +import wx + +class app_flow_graph (stdgui.gui_flow_graph): + def __init__(self, frame, panel, vbox, argv): + stdgui.gui_flow_graph.__init__ (self, frame, panel, vbox, argv) + + + parser = OptionParser (option_class=eng_option) + (options, args) = parser.parse_args () + + input_rate = 20e6 + src = mc4020.source (input_rate, mc4020.MCC_CH3_EN | mc4020.MCC_ALL_1V) + cvt = gr.short_to_float () + block, fft_win = fftsink.make_fft_sink_f (self, panel, "Rx Data", 512, input_rate) + + self.connect (src, cvt) + self.connect (cvt, block) + vbox.Add (fft_win, 1, wx.EXPAND) + + +def main (): + app = stdgui.stdapp (app_flow_graph, "USRP FFT") + app.MainLoop () + +if __name__ == '__main__': + main () diff --git a/gnuradio-examples/python/multi-antenna/multi_fft.py b/gnuradio-examples/python/multi-antenna/multi_fft.py new file mode 100755 index 000000000..ca9c614a8 --- /dev/null +++ b/gnuradio-examples/python/multi-antenna/multi_fft.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python + +from gnuradio import gr, gru, eng_notation +from gnuradio import usrp +from gnuradio.eng_option import eng_option +from gnuradio import eng_notation +from gnuradio import optfir +from optparse import OptionParser +from gnuradio.wxgui import stdgui, fftsink, waterfallsink, scopesink, form, slider +import wx +import usrp_dbid +import time +import os.path +import sys + +# required FPGA that can do 4 rx channels. + + +class my_graph(stdgui.gui_flow_graph): + + def __init__(self, frame, panel, vbox, argv): + stdgui.gui_flow_graph.__init__(self) + + self.frame = frame + self.panel = panel + + parser = OptionParser (option_class=eng_option) + #parser.add_option("-S", "--subdev", type="subdev", default=(0, None), + # help="select USRP Rx side A or B (default=A)") + parser.add_option("-d", "--decim", type="int", default=128, + help="set fgpa decimation rate to DECIM [default=%default]") + parser.add_option("-f", "--freq", type="eng_float", default=146.585e6, + help="set frequency to FREQ [default=%default])", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=20, + help="set gain in dB [default=%default]") + parser.add_option("-F", "--filter", action="store_true", default=True, + help="Enable channel filter") + (options, args) = parser.parse_args() + + if len(args) != 0: + parser.print_help() + raise SystemExit + + nchan = 4 + + if options.filter: + sw_decim = 4 + else: + sw_decim = 1 + + self.u = usrp.source_c(0, options.decim, fpga_filename="std_4rx_0tx.rbf") + if self.u.nddc() < nchan: + sys.stderr.write('This code requires an FPGA build with %d DDCs. This FPGA has only %d.\n' % ( + nchan, self.u.nddc())) + raise SystemExit + + if not self.u.set_nchannels(nchan): + sys.stderr.write('set_nchannels(%d) failed\n' % (nchan,)) + raise SystemExit + + input_rate = self.u.adc_freq() / self.u.decim_rate() + print "USB data rate = %s" % (eng_notation.num_to_str(input_rate),) + print "Scope data rate = %s" % (eng_notation.num_to_str(input_rate/sw_decim),) + + self.subdev = self.u.db[0] + self.u.db[1] + + if (len (self.subdev) != 4 or + self.u.db[0][0].dbid() != usrp_dbid.BASIC_RX or + self.u.db[1][0].dbid() != usrp_dbid.BASIC_RX): + sys.stderr.write('This code requires a Basic Rx board on Sides A & B\n') + sys.exit(1) + + self.u.set_mux(gru.hexint(0xf3f2f1f0)) + + # deinterleave four channels from FPGA + di = gr.deinterleave(gr.sizeof_gr_complex) + + self.connect(self.u, di) + + + + # taps for channel filter + chan_filt_coeffs = optfir.low_pass (1, # gain + input_rate, # sampling rate + 80e3, # passband cutoff + 115e3, # stopband cutoff + 0.1, # passband ripple + 60) # stopband attenuation + #print len(chan_filt_coeffs) + + for i in range(nchan): + scope = fftsink.fft_sink_c(self, panel, sample_rate=input_rate/sw_decim, + title="Input %d" % (i,), + ref_level=80, y_per_div=20) + vbox.Add(scope.win, 10, wx.EXPAND) + + if options.filter: + chan_filt = gr.fir_filter_ccf(sw_decim, chan_filt_coeffs) + self.connect((di, i), chan_filt, scope) + else: + self.connect((di, i), scope) + + + self.set_gain(options.gain) + self.set_freq(options.freq) + + def set_gain(self, gain): + for i in range(len(self.subdev)): + self.subdev[i].set_gain(gain) + + def set_freq(self, target_freq): + ok = True + for i in range(len(self.subdev)): + r = usrp.tune(self.u, i, self.subdev[i], target_freq) + if not r: + ok = False + print "set_freq: failed to set subdev[%d] freq to %f" % ( + i, target_freq) + + return ok + + +def main (): + app = stdgui.stdapp(my_graph, "Multi Scope", nstatus=1) + app.MainLoop() + +if __name__ == '__main__': + main () diff --git a/gnuradio-examples/python/multi-antenna/multi_file.py b/gnuradio-examples/python/multi-antenna/multi_file.py new file mode 100755 index 000000000..e2a6e04f6 --- /dev/null +++ b/gnuradio-examples/python/multi-antenna/multi_file.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python + +from gnuradio import gr, gru, eng_notation +from gnuradio import usrp +from gnuradio.eng_option import eng_option +from gnuradio import eng_notation +from gnuradio import optfir +from optparse import OptionParser +from gnuradio.wxgui import stdgui, fftsink, waterfallsink, scopesink, form, slider +import wx +import usrp_dbid +import time +import os.path +import sys + +# required FPGA that can do 4 rx channels. + + +class my_graph(gr.flow_graph): + + def __init__(self): + gr.flow_graph.__init__(self) + + parser = OptionParser (option_class=eng_option) + #parser.add_option("-S", "--subdev", type="subdev", default=(0, None), + # help="select USRP Rx side A or B (default=A)") + parser.add_option("-d", "--decim", type="int", default=128, + help="set fgpa decimation rate to DECIM [default=%default]") + parser.add_option("-f", "--freq", type="eng_float", default=146.585e6, + help="set frequency to FREQ [default=%default])", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=20, + help="set gain in dB [default=%default]") + parser.add_option("-F", "--filter", action="store_true", default=True, + help="Enable channel filter") + parser.add_option("-o", "--output", type="string", default=None, + help="set output basename") + (options, args) = parser.parse_args() + + if len(args) != 0: + parser.print_help() + raise SystemExit + + if options.output is None: + parser.print_help() + sys.stderr.write("You must provide an output filename base with -o OUTPUT\n") + raise SystemExit + else: + basename = options.output + + nchan = 4 + nsecs = 4.0 + + if options.filter: + sw_decim = 4 + else: + sw_decim = 1 + + self.u = usrp.source_c(0, options.decim, fpga_filename="std_4rx_0tx.rbf") + if self.u.nddc() < nchan: + sys.stderr.write('This code requires an FPGA build with %d DDCs. This FPGA has only %d.\n' % ( + nchan, self.u.nddc())) + raise SystemExit + + if not self.u.set_nchannels(nchan): + sys.stderr.write('set_nchannels(%d) failed\n' % (nchan,)) + raise SystemExit + + input_rate = self.u.adc_freq() / self.u.decim_rate() + print "USB data rate = %s" % (eng_notation.num_to_str(input_rate),) + sink_data_rate = input_rate/sw_decim + print "Scope data rate = %s" % (eng_notation.num_to_str(sink_data_rate),) + + self.subdev = self.u.db[0] + self.u.db[1] + + if (len(self.subdev) != 4 or + self.u.db[0][0].dbid() != usrp_dbid.BASIC_RX or + self.u.db[1][0].dbid() != usrp_dbid.BASIC_RX): + sys.stderr.write('This code requires a Basic Rx board on Sides A & B\n') + sys.exit(1) + + self.u.set_mux(gru.hexint(0xf3f2f1f0)) + + # collect 1 second worth of data + limit = int(nsecs * input_rate * nchan) + print "limit = ", limit + head = gr.head(gr.sizeof_gr_complex, limit) + + # deinterleave four channels from FPGA + di = gr.deinterleave(gr.sizeof_gr_complex) + + self.connect(self.u, head, di) + + # taps for channel filter + chan_filt_coeffs = optfir.low_pass (1, # gain + input_rate, # sampling rate + 80e3, # passband cutoff + 115e3, # stopband cutoff + 0.1, # passband ripple + 60) # stopband attenuation + #print len(chan_filt_coeffs) + + for i in range(nchan): + + sink = gr.file_sink(gr.sizeof_gr_complex, + basename + ("-%s-%d.dat" % (eng_notation.num_to_str(sink_data_rate), i))) + if options.filter: + chan_filt = gr.fir_filter_ccf(sw_decim, chan_filt_coeffs) + self.connect((di, i), chan_filt, sink) + else: + self.connect((di, i), sink) + + + self.set_gain(options.gain) + self.set_freq(options.freq) + + def set_gain(self, gain): + for i in range(len(self.subdev)): + self.subdev[i].set_gain(gain) + + def set_freq(self, target_freq): + ok = True + for i in range(len(self.subdev)): + r = usrp.tune(self.u, i, self.subdev[i], target_freq) + if not r: + ok = False + print "set_freq: failed to set subdev[%d] freq to %f" % ( + i, target_freq) + + return ok + + +def main (): + my_graph().run() + +if __name__ == '__main__': + main () diff --git a/gnuradio-examples/python/multi-antenna/multi_scope.py b/gnuradio-examples/python/multi-antenna/multi_scope.py new file mode 100755 index 000000000..43aa77b0d --- /dev/null +++ b/gnuradio-examples/python/multi-antenna/multi_scope.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python + +from gnuradio import gr, gru, eng_notation +from gnuradio import usrp +from gnuradio.eng_option import eng_option +from gnuradio import eng_notation +from gnuradio import optfir +from optparse import OptionParser +from gnuradio.wxgui import stdgui, fftsink, waterfallsink, scopesink, form, slider +import wx +import usrp_dbid +import time +import os.path +import sys + +# required FPGA that can do 4 rx channels. + + +class my_graph(stdgui.gui_flow_graph): + + def __init__(self, frame, panel, vbox, argv): + stdgui.gui_flow_graph.__init__(self) + + self.frame = frame + self.panel = panel + + parser = OptionParser (option_class=eng_option) + #parser.add_option("-S", "--subdev", type="subdev", default=(0, None), + # help="select USRP Rx side A or B (default=A)") + parser.add_option("-d", "--decim", type="int", default=128, + help="set fgpa decimation rate to DECIM [default=%default]") + parser.add_option("-f", "--freq", type="eng_float", default=146.585e6, + help="set frequency to FREQ [default=%default])", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=20, + help="set gain in dB [default=%default]") + parser.add_option("-F", "--filter", action="store_true", default=True, + help="Enable channel filter") + (options, args) = parser.parse_args() + + if len(args) != 0: + parser.print_help() + raise SystemExit + + nchan = 4 + + if options.filter: + sw_decim = 4 + else: + sw_decim = 1 + + self.u = usrp.source_c(0, options.decim, fpga_filename="std_4rx_0tx.rbf") + if self.u.nddc() < nchan: + sys.stderr.write('This code requires an FPGA build with %d DDCs. This FPGA has only %d.\n' % ( + nchan, self.u.nddc())) + raise SystemExit + + if not self.u.set_nchannels(nchan): + sys.stderr.write('set_nchannels(%d) failed\n' % (nchan,)) + raise SystemExit + + input_rate = self.u.adc_freq() / self.u.decim_rate() + print "USB data rate = %s" % (eng_notation.num_to_str(input_rate),) + print "Scope data rate = %s" % (eng_notation.num_to_str(input_rate/sw_decim),) + + self.subdev = self.u.db[0] + self.u.db[1] + + if (len(self.subdev) != 4 or + self.u.db[0][0].dbid() != usrp_dbid.BASIC_RX or + self.u.db[1][0].dbid() != usrp_dbid.BASIC_RX): + sys.stderr.write('This code requires a Basic Rx board on Sides A & B\n') + sys.exit(1) + + self.u.set_mux(gru.hexint(0xf3f2f1f0)) + + # deinterleave four channels from FPGA + di = gr.deinterleave(gr.sizeof_gr_complex) + + self.connect(self.u, di) + + # our destination (8 float inputs) + self.scope = scopesink.scope_sink_f(self, panel, sample_rate=input_rate/sw_decim) + + # taps for channel filter + chan_filt_coeffs = optfir.low_pass (1, # gain + input_rate, # sampling rate + 80e3, # passband cutoff + 115e3, # stopband cutoff + 0.1, # passband ripple + 60) # stopband attenuation + #print len(chan_filt_coeffs) + + # bust the deinterleaved complex channels into floats + for i in range(nchan): + + if options.filter: + chan_filt = gr.fir_filter_ccf(sw_decim, chan_filt_coeffs) + c2f = gr.complex_to_float() + self.connect((di, i), chan_filt, c2f) + else: + c2f = gr.complex_to_float() + self.connect((di, i), c2f) + + self.connect((c2f, 0), (self.scope, 2*i + 0)) + self.connect((c2f, 1), (self.scope, 2*i + 1)) + + + self._build_gui(vbox) + + self.set_gain(options.gain) + self.set_freq(options.freq) + + def set_gain(self, gain): + for i in range(len(self.subdev)): + self.subdev[i].set_gain(gain) + + def set_freq(self, target_freq): + ok = True + for i in range(len(self.subdev)): + r = usrp.tune(self.u, i, self.subdev[i], target_freq) + if not r: + ok = False + print "set_freq: failed to set subdev[%d] freq to %f" % ( + i, target_freq) + + return ok + + + def _build_gui(self, vbox): + vbox.Add(self.scope.win, 10, wx.EXPAND) + + + +def main (): + app = stdgui.stdapp(my_graph, "Multi Scope", nstatus=1) + app.MainLoop() + +if __name__ == '__main__': + main () diff --git a/gnuradio-examples/python/multi_usrp/Makefile.am b/gnuradio-examples/python/multi_usrp/Makefile.am new file mode 100644 index 000000000..c2c6a2943 --- /dev/null +++ b/gnuradio-examples/python/multi_usrp/Makefile.am @@ -0,0 +1,28 @@ +# +# Copyright 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. +# + +EXTRA_DIST = \ + README \ + multi_usrp_oscope.py \ + multi_usrp_rx_cfile.py + + + diff --git a/gnuradio-examples/python/multi_usrp/README b/gnuradio-examples/python/multi_usrp/README new file mode 100644 index 000000000..c93d7736a --- /dev/null +++ b/gnuradio-examples/python/multi_usrp/README @@ -0,0 +1,260 @@ +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 + + +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 description of the detail blocks used in usrp_multi.py + +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. + +The easiest thing to see how this works is just looking at the code in + multi_usrp_oscope.py + multi_usrp_rx_cfile.py + +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. + + + +Here follows a brief of the new blocks and (changes)functionality written for multi-usrp support. + +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/gnuradio-examples/python/multi_usrp/multi_usrp_oscope.py b/gnuradio-examples/python/multi_usrp/multi_usrp_oscope.py new file mode 100755 index 000000000..d64cd1e64 --- /dev/null +++ b/gnuradio-examples/python/multi_usrp/multi_usrp_oscope.py @@ -0,0 +1,343 @@ +#!/usr/bin/env python +# +# 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. +# + +# print "Loading revised usrp_oscope with additional options for scopesink..." + +from gnuradio import gr, gru +from gnuradio import usrp +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import stdgui, fftsink, waterfallsink, scopesink, form, slider +from optparse import OptionParser +import wx +import sys + +import time +from gnuradio import usrp_multi + + +def pick_subdevice(u): + """ + The user didn't specify a subdevice on the command line. + If there's a daughterboard on A, select A. + If there's a daughterboard on B, select B. + Otherwise, select A. + """ + if u.db[0][0].dbid() >= 0: # dbid is < 0 if there's no d'board or a problem + return (0, 0) + if u.db[1][0].dbid() >= 0: + return (1, 0) + return (0, 0) + + +class app_flow_graph(stdgui.gui_flow_graph): + def __init__(self, frame, panel, vbox, argv): + stdgui.gui_flow_graph.__init__(self) + + self.frame = frame + self.panel = panel + + parser = OptionParser(option_class=eng_option) + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, + help="select USRP Rx side A or B (default=first one with a daughterboard)") + parser.add_option("-d", "--decim", type="int", default=128, + help="set fgpa decimation rate to DECIM [default=%default]") + parser.add_option("-f", "--freq", type="eng_float", default=None, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=None, + help="set gain in dB (default is midpoint)") + #align interval is default not yet enabled in gr.align_on_samplenumbers_ss + #parser.add_option("-a", "--align_interval", type="int", default=-1, + # help="Align master and slave every so much samples.") + + # width 8 does not work yet with multi_usrp because it interferes with the 32 bit samplecounter + #parser.add_option("-8", "--width-8", action="store_true", default=False, + # help="Enable 8-bit samples across USB") + parser.add_option("-m", "--mux", type="intx", default=None, + help="set fpga FR_RX_MUX register to MUX") + parser.add_option("-n", "--frame-decim", type="int", default=1, + help="set oscope frame decimation factor to n [default=1]") + parser.add_option("-N", "--nchan", type="int", default=2, + help="set nchannels to NCHAN") + parser.add_option("-q", "--show-q", action="store_true", default=False, + help="show the q value of the complex samples") + parser.add_option("-s", "--show-counters", action="store_true", default=False, + help="show the counters") + parser.add_option("-v", "--v-scale", type="eng_float", default=1000, + help="set oscope initial V/div to SCALE [default=%default]") + parser.add_option("-t", "--t-scale", type="eng_float", default=49e-6, + help="set oscope initial s/div to SCALE [default=50us]") + parser.add_option("-x", "--master-serialno", type="string", default=None, + help="Serial_no of the usrp which should be the MASTER (default= select any)") + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + + self.show_debug_info = True + + # build the graph + + #self.u = usrp.source_c(which=options.which_usrp,decim_rate=options.decim) + if (options.mux is None) | (4==options.nchan): + init_mux=None #use default mux which is 0x10321032 + else: + init_mux=options.mux + + init_gain=0.0 + init_freq=0.0 + init_align_interval=-1 + + self.multi=usrp_multi.multi_source_align( self, options.master_serialno, options.decim, + options.nchan, init_gain, init_freq, init_mux, init_align_interval) + self.um=self.multi.get_master_usrp() + self.us=self.multi.get_slave_usrp() + + if options.rx_subdev_spec is None: + options.rx_subdev_spec = pick_subdevice(self.um) + if (options.mux==None) and (options.nchan!=4): + mux=usrp.determine_rx_mux_value(self.um, options.rx_subdev_spec) + mux= (mux<<8 & 0xffffffff) | (mux & 0xff) + self.um.set_mux(mux) + self.us.set_mux(mux) + + # width 8 does not work yet with multi_usrp because it interferes with the 32 bit samplecounter + #if options.width_8: + # width = 8 + # shift = 8 + # format = self.um.make_format(width, shift) + # r = self.um.set_format(format) + # r = self.us.set_format(format) + + # determine the daughterboard subdevice of the first channel we're using + self.subdevm = usrp.selected_subdev(self.um, options.rx_subdev_spec) + self.subdevs = usrp.selected_subdev(self.us, options.rx_subdev_spec) + + input_rate = self.um.adc_freq() / self.um.decim_rate() + + self.scope = scopesink.scope_sink_f(self, panel, sample_rate=input_rate, + frame_decim=options.frame_decim, + v_scale=options.v_scale, + t_scale=options.t_scale) + self.sink_count=0 + self.add_to_scope((self.multi.get_master_source_c(),1),options.show_q) + self.add_to_scope((self.multi.get_slave_source_c(),1),options.show_q) + if 4==options.nchan: + self.add_to_scope((self.multi.get_master_source_c(),2),options.show_q) + self.add_to_scope((self.multi.get_slave_source_c(),2),options.show_q) + + if options.show_counters: + self.add_to_scope((self.multi.get_master_source_c(),0),options.show_q) + self.add_to_scope((self.multi.get_slave_source_c(),0),options.show_q) + + self._build_gui(vbox) + + # set initial values + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + g = self.subdevm.gain_range() + options.gain = float(g[0]+g[1])/2 + + if options.freq is None: + # if no freq was specified, use the mid-point + r = self.subdevm.freq_range() + options.freq = float(r[0]+r[1])/2 + + self.set_gain(options.gain) + + if self.show_debug_info: + self.myform['decim'].set_value(self.um.decim_rate()) + self.myform['fs@usb'].set_value(self.um.adc_freq() / self.um.decim_rate()) + self.myform['dbname'].set_value(self.subdevm.name()) + self.myform['baseband'].set_value(0) + self.myform['ddc'].set_value(0) + + if not(self.set_freq(options.freq)): + self._set_status_msg("Failed to set initial frequency") + + self.multi.print_db_info() + self.unsynced=True + frame.Bind(wx.EVT_IDLE, self.onIdle) + + def add_to_scope(self,source_c,show_q): + c2f= gr.complex_to_float () + self.connect(source_c, c2f) + self.connect((c2f,0), (self.scope,self.sink_count)) + self.sink_count=self.sink_count+1 + if show_q: + self.connect((c2f,1), (self.scope,self.sink_count)) + self.sink_count=self.sink_count+1 + + + def _set_status_msg(self, msg): + self.frame.GetStatusBar().SetStatusText(msg, 0) + + def _build_gui(self, vbox): + + def _form_set_freq(kv): + return self.set_freq(kv['freq']) + + vbox.Add(self.scope.win, 10, wx.EXPAND) + + # add control area at the bottom + self.myform = myform = form.form() + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0, 0) + myform['freq'] = form.float_field( + parent=self.panel, sizer=hbox, label="Center freq", weight=1, + callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg)) + + hbox.Add((5,0), 0, 0) + g = self.subdevm.gain_range() + myform['gain'] = form.slider_field(parent=self.panel, sizer=hbox, label="Gain", + weight=3, + min=int(g[0]), max=int(g[1]), + callback=self.set_gain) + + hbox.Add((5,0), 0, 0) + buttonSync = form.button_with_callback(parent=self.panel, label='sync',callback=self.sync_usrps) + hbox.Add(buttonSync,0,wx.EXPAND) + + hbox.Add((5,0), 0, 0) + vbox.Add(hbox, 0, wx.EXPAND) + + self._build_subpanel(vbox) + + def _build_subpanel(self, vbox_arg): + # build a secondary information panel (sometimes hidden) + + # FIXME figure out how to have this be a subpanel that is always + # created, but has its visibility controlled by foo.Show(True/False) + + if not(self.show_debug_info): + return + + panel = self.panel + vbox = vbox_arg + myform = self.myform + + #panel = wx.Panel(self.panel, -1) + #vbox = wx.BoxSizer(wx.VERTICAL) + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + myform['decim'] = form.static_float_field( + parent=panel, sizer=hbox, label="Decim") + + hbox.Add((5,0), 1) + myform['fs@usb'] = form.static_float_field( + parent=panel, sizer=hbox, label="Fs@USB") + + hbox.Add((5,0), 1) + myform['dbname'] = form.static_text_field( + parent=panel, sizer=hbox) + + hbox.Add((5,0), 1) + myform['baseband'] = form.static_float_field( + parent=panel, sizer=hbox, label="Analog BB") + + hbox.Add((5,0), 1) + myform['ddc'] = form.static_float_field( + parent=panel, sizer=hbox, label="DDC") + + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + + def set_freq(self, target_freq, sync=True): + """ + Set the center frequency we're interested in for all channels, + on all rx daughterboards on MASTER and SLAVE. + + @param target_freq: frequency in Hz + @param sync: sync the usrps after setting the freqs (this will clear any phase differences in the DDCS) + @rypte: bool + + 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. + """ + result,r1,r2,r3,r4 = self.multi.tune_all_rx(target_freq) + if sync: + self.sync_usrps() #sync master and slave and clear any DDC phase differences + if r1: + self.myform['freq'].set_value(target_freq) # update displayed value + if self.show_debug_info: + self.myform['baseband'].set_value(r1.baseband_freq) + self.myform['ddc'].set_value(r1.dxc_freq) + return result + + def set_freq_chan0(self, target_freq, sync=True): + """ + Set the center frequency we're interested in for rx chan 0 only on MASTER and SLAVE. + + @param target_freq: frequency in Hz + @param sync: sync the usrps after setting the freqs (this will clear any phase differences in the DDCS) + @rypte: bool + + 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. + """ + rm = usrp.tune(self.um, 0, self.subdevm, target_freq) + rs = usrp.tune(self.us, 0, self.subdevs, target_freq) + r=rm + if sync: + self.sync_usrps() #sync master and slave and clear any DDC phase differences + if r: + self.myform['freq'].set_value(target_freq) # update displayed value + if self.show_debug_info: + self.myform['baseband'].set_value(r.baseband_freq) + self.myform['ddc'].set_value(r.dxc_freq) + return True + + return False + + def set_gain(self, gain): + self.myform['gain'].set_value(gain) # update displayed value + self.multi.set_gain_all_rx(gain) + + def set_gain_chan0(self, gain): + self.myform['gain'].set_value(gain) # update displayed value + self.subdevm.set_gain(gain) + self.subdevs.set_gain(gain) + + def onIdle(self,evt): + if self.unsynced: + time.sleep(0.5) + self.unsynced=True + self.multi.sync() + self.unsynced=False + #print 'synced' + + def sync_usrps(self): + self.multi.sync() + +def main (): + app = stdgui.stdapp(app_flow_graph, "MULTI_USRP O'scope", nstatus=1) + app.MainLoop() + +if __name__ == '__main__': + main () diff --git a/gnuradio-examples/python/multi_usrp/multi_usrp_rx_cfile.py b/gnuradio-examples/python/multi_usrp/multi_usrp_rx_cfile.py new file mode 100755 index 000000000..6084aba3d --- /dev/null +++ b/gnuradio-examples/python/multi_usrp/multi_usrp_rx_cfile.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python + +""" +Read samples 2 from two linked synchronised USRP's and write to file formatted as binary +single-precision complex values. +Make sure you read README on how to link the two usrps + +""" + +from gnuradio import gr, eng_notation +from gnuradio import audio +from gnuradio import usrp +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import sys + +import time +from gnuradio import usrp_multi + +class my_graph(gr.flow_graph): + + def __init__(self): + gr.flow_graph.__init__(self) + + usage="%prog: [options] output_filename" + parser = OptionParser(option_class=eng_option, usage=usage) + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=(0, 0), + help="select USRP Rx side A or B (default=A)") + parser.add_option("-d", "--decim", type="int", default=128, + help="set selfpa decimation rate to DECIM [default=%default]") + parser.add_option("-f", "--freq", type="eng_float", default=0.0, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=None, + help="set gain in dB (default is midpoint)") + parser.add_option("-n", "--nchan", type="intx", default=2, + help="set nchannels to NCHAN") + parser.add_option("-N", "--nsamples", type="eng_float", default=None, + help="number of samples to collect [default=+inf]") + parser.add_option ("-o", "--output-file-m", default="usrp_rx_m.dat", + help="write master data to FILE", metavar="FILE") + parser.add_option ("--output-file-m2", default="usrp_rx_m2.dat", + help="write master data from second channel to FILE", metavar="FILE") + parser.add_option ("-p", "--output-file-s", default="usrp_rx_s.dat", + help="write slave data to FILE", metavar="FILE") + parser.add_option ("--output-file-s2", default="usrp_rx_s2.dat", + help="write slave data from second channel to FILE", metavar="FILE") + parser.add_option("-x", "--master-serialno", type="string", default=None, + help="Serial_no of the usrp which should be the MASTER (default= select any)") + (options, args) = parser.parse_args () + if len(args) != 0: + parser.print_help() + raise SystemExit, 1 + + if options.freq is None: + parser.print_help() + sys.stderr.write('You must specify the frequency with -f FREQ\n'); + raise SystemExit, 1 + + # build the graph + self.multi=usrp_multi.multi_source_align( fg=self, master_serialno=options.master_serialno, decim=options.decim, + nchan=options.nchan ) + self.um=self.multi.get_master_usrp() + self.us=self.multi.get_slave_usrp() + + dst_m=gr.file_sink (gr.sizeof_gr_complex, options.output_file_m) + dst_s=gr.file_sink (gr.sizeof_gr_complex, options.output_file_s) + if options.nsamples is None: + self.connect((self.multi.get_master_source_c(),1),dst_m) + self.connect((self.multi.get_slave_source_c(),1),dst_s) + else: + head_m = gr.head(gr.sizeof_gr_complex, int(options.nsamples)) + head_s = gr.head(gr.sizeof_gr_complex, int(options.nsamples)) + self.connect((self.multi.get_master_source_c(),1),head_m,dst_m) + self.connect((self.multi.get_slave_source_c(),1),head_s,dst_s) + + if(4==options.nchan): + dst_m2=gr.file_sink (gr.sizeof_gr_complex, options.output_file_m2) + dst_s2=gr.file_sink (gr.sizeof_gr_complex, options.output_file_s2) + if options.nsamples is None: + self.connect((self.multi.get_master_source_c(),2),dst_m2) + self.connect((self.multi.get_slave_source_c(),2),dst_s2) + else: + head_m2 = gr.head(gr.sizeof_gr_complex, int(options.nsamples)) + head_s2 = gr.head(gr.sizeof_gr_complex, int(options.nsamples)) + self.connect((self.multi.get_master_source_c(),2),head_m2,dst_m2) + self.connect((self.multi.get_slave_source_c(),2),head_s2,dst_s2) + + if options.rx_subdev_spec is None: + options.rx_subdev_spec = usrp.pick_rx_subdevice(self.um) + + if (options.nchan!=4): + mux=usrp.determine_rx_mux_value(self.um, options.rx_subdev_spec) + mux= (mux<<8 & 0xffffffff) | (mux & 0xff) + self.um.set_mux(mux) + self.us.set_mux(mux) + + # determine the daughterboard subdevice we're using + self.subdevm = usrp.selected_subdev(self.um, options.rx_subdev_spec) + self.subdevs = usrp.selected_subdev(self.us, options.rx_subdev_spec) + print "Using MASTER RX d'board %s" % (self.subdevm.side_and_name(),) + print "Using SLAVE RX d'board %s" % (self.subdevs.side_and_name(),) + input_rate = self.um.adc_freq() / self.um.decim_rate() + print "USB sample rate %s" % (eng_notation.num_to_str(input_rate)) + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + g = self.subdevm.gain_range() + options.gain = float(g[0]+g[1])/2 + + self.multi.set_gain_all_rx(options.gain) + result,r1,r2,r3,r4 = self.multi.tune_all_rx(options.freq) + if not result: + sys.stderr.write('Failed to set frequency\n') + raise SystemExit, 1 + + def sync_usrps(self): + self.multi.sync() + + +if __name__ == '__main__': + fg=my_graph() + fg.start() + #time.sleep(0.5) + fg.sync_usrps() + raw_input ('Press Enter to quit: ') + fg.stop() + #try: + # fg.start() + # fg.sync_usrps() + #except KeyboardInterrupt: + # pass diff --git a/gnuradio-examples/python/networking/measurement_slave.py b/gnuradio-examples/python/networking/measurement_slave.py new file mode 100755 index 000000000..2bc7c39c7 --- /dev/null +++ b/gnuradio-examples/python/networking/measurement_slave.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python +# +# Copyright 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 struct +import socket +import asyncore +import sys +import optparse +import random +from gnuradio.eng_option import eng_option +import gnuradio.gr.gr_threading as _threading + +LOOP_TIMEOUT = 0.001 +BROADCAST_ADDR = '255.255.255.255' + +BROADCAST_PORT = 27010 # UDP +CONTROL_PORT = 27011 # TCP + +PKT_HEADER_SIZE = 4 # 32-bit int + +logfile = None + +def unpack_header(s): + (len,) = struct.unpack('!i', s) + return (len,) + +def pack_header(len): + return struct.pack('!i', len) + + +class control_port_listener(asyncore.dispatcher): + def __init__(self, port=CONTROL_PORT, udp_socket=None, verbose=False): + """ + @param port: TCP port to listen on. + @type port: int + """ + asyncore.dispatcher.__init__(self) + + self._verbose = verbose + self._udp_socket = udp_socket + + host = '' # symbolic name for localhost + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.set_reuse_addr() + + try: + self.bind((host, port)) + except socket.error, err: + sys.stderr.write('Failed to bind to %s: %s\n' % + ((host, port), os.strerror (err.args[0]),)) + sys.exit(1) + + self.listen(3) + + def handle_accept(self): + socket, addr = self.accept() + sys.stderr.write("handle_accept: %r\n" % (addr,)) + if not(socket is None): + # instantiate a server + s = control_server(socket, addr, self._udp_socket, self._verbose) + + +class gr_dispatcher(asyncore.dispatcher): + def __init__(self, sock=None, map=None): + asyncore.dispatcher.__init__(self, sock=sock, map=map) + + def read_packet(self): + """ + Returns None or packet + """ + s = self.recvall(PKT_HEADER_SIZE) + if s == '': + return None + + (payload_len,) = unpack_header(s) + + payload_len = int(payload_len) + + if payload_len == 0: + payload = '' + else: + payload = self.recvall(payload_len) + if len(payload) != payload_len: + sys.stderr.write ('short recv, expected %d bytes, got %d\n' % ( + payload_len, len(payload))) + raise RuntimeError, "short recv" + + return payload + + def recvall(self, buffer_size): + result = '' + while len(result) < buffer_size: + data = self.recv(buffer_size - len(result)) + if not data: + return '' + result += data + return result + + +class pkt_receiver_thread(_threading.Thread): + def __init__(self, socket): + _threading.Thread.__init__(self) + self.setDaemon(1) + self.socket = socket + self.keep_running = True + self.start() + + def run(self): + while self.keep_running: + pkt, sender = self.socket.recvfrom(10000) + if pkt: + if len(pkt) > 2: + t = struct.unpack('!H', pkt[0:2]) + seqno = t[0] + else: + seqno = -1 + + logfile.write('RCVD seqno %4d len %4d from %s\n' % (seqno, len(pkt), sender)) + logfile.flush() + + +class control_server(gr_dispatcher): + def __init__(self, socket, addr, udp_socket, verbose=False): + gr_dispatcher.__init__(self, sock=socket) + + self._udp_socket = udp_socket + self.verbose = verbose + self.setblocking(1) + + def writable(self): + return False + + def handle_read(self): + pkt = self.read_packet() + if pkt: + annotate = 'ANNOTATE' + if pkt.startswith(annotate): + logfile.write(pkt[len(annotate)+1:]) + logfile.write('\n') + logfile.flush() + elif pkt.startswith('SEND'): + tokens = pkt.split() + if len(tokens) < 4: + invalid_packet(pkt) + else: + npkts = int(tokens[1]) + size = int(tokens[2]) + power = float(tokens[3]) + send_test_packets(self._udp_socket, npkts, size, power) + else: + invalid_packet(pkt) + + def handle_close(self): + self.close() + + +def invalid_packet(pkt): + sys.stderr.write('received unrecognized packet: %s\n' % (pkt,)) + + +def make_random_payload(size): + p = [0] * size + if 1: + for i in range(size): + p[i] = chr(random.randint(0, 255)) + else: + for i in range(size): + p[i] = chr(i % 256) + return ''.join(p) + + +def send_test_packets(udp_socket, npkts, size, power): + # we ignore power for now... + size = max(2, size) + payload = make_random_payload(size - 2) + for n in range(npkts): + pkt = struct.pack('!H', n) + payload + udp_socket.sendto(pkt, (BROADCAST_ADDR, BROADCAST_PORT)) + #udp_socket.sendall(pkt) + + +def open_udp_broadcast_socket(gr0_host_ip, port): + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +# s.bind((gr0_host_ip, port)) + s.bind(('', port)) + s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) +# s.connect((BROADCAST_ADDR, port)) + return s + + +def main(): + global logfile + + usage = 'usage: %prog [options] gr0-ip-addr' + parser = optparse.OptionParser (option_class=eng_option, usage=usage) + parser.add_option('-l', '--logfile', type='string', default=None, + help="specify log file name [default=<stdout>]") + parser.add_option('-v', '--verbose', action="store_true", default=False, + help="enable verbose diagnostics") + + (options, args) = parser.parse_args () + if len(args) != 1: + parser.print_help() + sys.exit(1) + + gr0_ip_addr = args[0] + if options.logfile is None: + logfile = sys.stdout + else: + logfile = file(options.logfile, 'w') + + udp_socket = open_udp_broadcast_socket(gr0_ip_addr, BROADCAST_PORT) + R = pkt_receiver_thread(udp_socket) + L = control_port_listener(CONTROL_PORT, udp_socket=udp_socket, verbose=options.verbose) + asyncore.loop(LOOP_TIMEOUT) + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/usrp/Makefile.am b/gnuradio-examples/python/usrp/Makefile.am new file mode 100644 index 000000000..32442d5cb --- /dev/null +++ b/gnuradio-examples/python/usrp/Makefile.am @@ -0,0 +1,52 @@ +# +# 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. +# + +EXTRA_DIST = \ + am_rcv.py \ + ayfabtu.py \ + benchmark_usb.py \ + fm_tx4.py \ + fsk_loopback.py \ + fsk_rx.py \ + fsk_tx.py \ + max_power.py \ + test_counting.py \ + test_dft_analysis.py \ + test_dft_synth.py \ + test_digital_loopback_counting.py \ + test_digital_loopback_lfsr.py \ + tvrx_am_rcv_gui.py \ + tvrx_tv_rcv.py \ + usrp_fft.py \ + usrp_fft_old.py \ + usrp_nbfm_ptt.py \ + usrp_nbfm_rcv.py \ + usrp_oscope.py \ + usrp_rx_cfile.py \ + usrp_rx_file.py \ + usrp_rx_nogui.py \ + usrp_siggen.py \ + usrp_wfm_rcv.py \ + usrp_wfm_rcv_nogui.py \ + usrp_wfm_rcv2_nogui.py \ + usrp_wfm_rcv_pll.py \ + usrp_wxapt_rcv.py \ + wfm_rcv_file.py diff --git a/gnuradio-examples/python/usrp/am_rcv.py b/gnuradio-examples/python/usrp/am_rcv.py new file mode 100755 index 000000000..2908dcbf5 --- /dev/null +++ b/gnuradio-examples/python/usrp/am_rcv.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python + +from gnuradio import gr, eng_notation +from gnuradio import audio +from gnuradio import usrp +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import sys +import math + +from gnuradio.wxgui import stdgui, fftsink +import wx + +class am_rx_graph (stdgui.gui_flow_graph): + def __init__(self,frame,panel,vbox,argv): + stdgui.gui_flow_graph.__init__ (self,frame,panel,vbox,argv) + + station = parseargs(argv[1:]) + offset_freq = 30e3 + IF_freq = offset_freq - station + + adc_rate = 64e6 + usrp_decim = 250 + if_rate = adc_rate / usrp_decim # 256 kHz + if_decim = 4 + demod_rate = if_rate / if_decim # 64 kHz + audio_decimation = 2 + audio_rate = demod_rate / audio_decimation # 16 kHz + + # usrp is data source + src = usrp.source_c (0, usrp_decim) + src.set_rx_freq (0, IF_freq) + actual_IF_freq =src.rx_freq(0) + actual_offset = actual_IF_freq + station + + #print actual_IF_freq + #print actual_offset + + src.set_pga(0,20) + # sound card as final sink + audio_sink = audio.sink (int (audio_rate)) + + channel_coeffs = \ + gr.firdes.low_pass (1.0, # gain + if_rate, # sampling rate + 9e3, # low pass cutoff freq + 10e3, # width of trans. band + gr.firdes.WIN_HANN) + + ddc = gr.freq_xlating_fir_filter_ccf (if_decim,channel_coeffs,-actual_offset,if_rate) + + magblock = gr.complex_to_mag() + volumecontrol = gr.multiply_const_ff(.003) + + # Deemphasis. Is this necessary on AM? + TAU = 75e-6 # 75us in US, 50us in EUR + fftaps = [ 1 - math.exp(-1/TAU/if_rate), 0] + fbtaps= [ 0 , math.exp(-1/TAU/if_rate) ] + + deemph = gr.iir_filter_ffd(fftaps,fbtaps) + + # compute FIR filter taps for audio filter + width_of_transition_band = audio_rate / 8 + audio_coeffs = gr.firdes.low_pass (1.0, # gain + if_rate, # sampling rate + 9e3, #audio_rate/2 - width_of_transition_band, + 4e3, # width_of_transition_band, + gr.firdes.WIN_HANN) + + # input: float; output: float + audio_filter = gr.fir_filter_fff (audio_decimation, audio_coeffs) + + + + + print len(channel_coeffs) + print len(audio_coeffs) + + # now wire it all together + self.connect (src, ddc) + self.connect (ddc, magblock) + self.connect (magblock, volumecontrol) + self.connect (volumecontrol,deemph) + self.connect (deemph,audio_filter) + self.connect (audio_filter, (audio_sink, 0)) + + if 1: + pre_demod = fftsink.fft_sink_c (self, panel, title="Pre-Demodulation", fft_size=128, sample_rate=if_rate) + self.connect (src, pre_demod) + vbox.Add (pre_demod.win, 1, wx.EXPAND) + + if 0: + post_demod = fftsink.fft_sink_c (self, panel, title="Post Demodulation", fft_size=256, sample_rate=demod_rate) + self.connect (ddc, post_demod) + vbox.Add (post_demod.win, 1, wx.EXPAND) + + if 0: + post_filt = fftsink.fft_sink_f (self, panel, title="Post Filter", fft_size=512, sample_rate=audio_rate) + self.connect (magblock,post_filt) + vbox.Add (post_filt.win, 1, wx.EXPAND) + +def parseargs (args): + nargs = len (args) + if nargs == 1: + freq1 = float (args[0]) * 1e3 + else: + sys.stderr.write ('usage: am_rcv freq1\n') + sys.exit (1) + + return freq1 + +if __name__ == '__main__': + app = stdgui.stdapp (am_rx_graph, "AM RX") + app.MainLoop () + diff --git a/gnuradio-examples/python/usrp/ayfabtu.dat b/gnuradio-examples/python/usrp/ayfabtu.dat Binary files differnew file mode 100644 index 000000000..5c65cf483 --- /dev/null +++ b/gnuradio-examples/python/usrp/ayfabtu.dat diff --git a/gnuradio-examples/python/usrp/ayfabtu.py b/gnuradio-examples/python/usrp/ayfabtu.py new file mode 100755 index 000000000..7647e93b8 --- /dev/null +++ b/gnuradio-examples/python/usrp/ayfabtu.py @@ -0,0 +1,169 @@ +#!/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. +# + +# +# All Your Frequencies are Belong to Us! +# +# Transmit NBFM message on 25 channels simultaneously! +# + +from gnuradio import gr, gru, eng_notation +from gnuradio import usrp +from gnuradio import audio +from gnuradio import blks +from gnuradio import optfir +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import math +import sys +import random + +from gnuradio.wxgui import stdgui, fftsink +import wx + + +def make_random_complex_tuple(L): + result = [] + for x in range(L): + result.append(complex(random.gauss(0, 1),random.gauss(0, 1))) + + return tuple(result) + +def random_noise_c(): + src = gr.vector_source_c(make_random_complex_tuple(32*1024), True) + return src + + +def plot_taps(taps, sample_rate=2): + return gru.gnuplot_freqz (gru.freqz (taps, 1), sample_rate) + + +class ayfabtu_graph (stdgui.gui_flow_graph): + def __init__(self, frame, panel, vbox, argv): + stdgui.gui_flow_graph.__init__ (self, frame, panel, vbox, argv) + + parser = OptionParser (option_class=eng_option) + parser.add_option ("-c", "--duc-freq", type="eng_float", default=29.325e6, + help="set Tx ddc frequency to FREQ", metavar="FREQ") + (options, args) = parser.parse_args () + + nchan = 25 + IF_GAIN = 80000 + AUDIO_GAIN = 100 + + self.dac_rate = 128e6 + self.usrp_interp = 256 + self.usrp_rate = self.dac_rate / self.usrp_interp # 500 kS/s + self.audio_rate = 32000 # 32 kS/s + + self.audio_src = gr.file_source(gr.sizeof_float, "ayfabtu.dat", True) + + ahp_taps = gr.firdes.high_pass(1, # gain + 32e3, # Fs + 300, # cutoff + 600, # trans width + gr.firdes.WIN_HANN) + self.audio_hp = gr.fir_filter_fff(1, ahp_taps) + + self.audio_gain = gr.multiply_const_ff(AUDIO_GAIN) + + null_src = gr.null_source(gr.sizeof_gr_complex) + #noise_src = gr.noise_source_c(gr.GR_UNIFORM, 1, 0) + noise_src = random_noise_c() + + if 0: + artaps = optfir.low_pass(1, # gain + 2, # Fs + .75/32, # freq1 + 1.0/32, # freq2 + 1, # pb ripple in dB + 50, # stopband atten in dB + 2) # + extra taps + else: + artaps = gr.firdes.low_pass(1, # gain + 32e3*15,# Fs + 2.7e3, # cutoff + .3e3, # trans width + gr.firdes.WIN_HANN) + print "len(artaps) =", len(artaps) + self.audio_resampler = blks.rational_resampler_fff(self, 15, 32, artaps) + + self.fm_mod = blks.nbfm_tx(self, 15000, 15000, max_dev=4.5e3) + + + fbtaps = gr.firdes.low_pass(1, # gain + 25*15e3, # rate + 13e3, # cutoff + 2e3, # trans width + gr.firdes.WIN_HANN) + print "len(fbtabs) =", len(fbtaps) + #self.plot = plot_taps(fbtaps, 25*15e3) + self.filter_bank = blks.synthesis_filterbank(self, nchan, fbtaps) + + self.if_gain = gr.multiply_const_cc(IF_GAIN) + + if 0: + ifrtaps = optfir.low_pass(1, + 2, # Fs + .75/3, # freq1 + 1.0/3, # freq2 + 1, # pb ripple in dB + 50, # stopband atten in dB + 2) # + extra taps + else: + ifrtaps = gr.firdes.low_pass(1, + 2, # Fs + .75/3, # freq1 + .25/3, # trans width + gr.firdes.WIN_HANN) + + + print "len(ifrtaps) =", len(ifrtaps) + self.if_resampler = blks.rational_resampler_ccf(self, 4, 3, ifrtaps) + + + self.u = usrp.sink_c(0, 256) + self.u.set_tx_freq(0, options.duc_freq) + self.u.set_pga(0, self.u.pga_max()) + + # wire it all together + + self.connect(self.audio_src, self.audio_hp, self.audio_gain, + self.audio_resampler, self.fm_mod) + + null_sink = gr.null_sink(gr.sizeof_gr_complex) + + for i in range(nchan): + if True or i == 0: + self.connect(self.fm_mod, (self.filter_bank, i)) + else: + self.connect(null_src, (self.filter_bank, i)) + + self.connect(self.filter_bank, self.if_gain, self.if_resampler, self.u) + + +def main (): + app = stdgui.stdapp (ayfabtu_graph, "All Your Frequency Are Belong to Us") + app.MainLoop () + +if __name__ == '__main__': + main () diff --git a/gnuradio-examples/python/usrp/benchmark_usb.py b/gnuradio-examples/python/usrp/benchmark_usb.py new file mode 100755 index 000000000..8418f5062 --- /dev/null +++ b/gnuradio-examples/python/usrp/benchmark_usb.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python +# +# 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. +# + +""" +Benchmark the USB/USRP throughput. Finds the maximum full-duplex speed +the USRP/USB combination can sustain without errors. + +This program does not currently give reliable results. Sorry about that... +""" + +from gnuradio import gr +from gnuradio import usrp +from gnuradio import eng_notation + +import sys + +def run_test (usb_throughput, verbose): + # usb_throughput is in bytes/sec. + # + # Returns True or False + + nsec = 1 + stream_length = int (usb_throughput/2 * nsec) # length of stream to examine + + adc_freq = 64e6 + dac_freq = 128e6 + sizeof_sample = 2 * gr.sizeof_short + + usb_throughput_in_samples = usb_throughput / sizeof_sample + + # allocate usb throughput 50/50 between Tx and Rx + + tx_interp = int (dac_freq) / int (usb_throughput_in_samples / 2) + rx_decim = int (adc_freq) / int (usb_throughput_in_samples / 2) + + # print "tx_interp =", tx_interp, "rx_decim =", rx_decim + assert (tx_interp == 2 * rx_decim) + + fg = gr.flow_graph () + + # Build the Tx pipeline + data_src = gr.lfsr_32k_source_s () + src_head = gr.head (gr.sizeof_short, int (stream_length * 2)) + usrp_tx = usrp.sink_s (0, tx_interp) + fg.connect (data_src, src_head, usrp_tx) + + # and the Rx pipeline + usrp_rx = usrp.source_s (0, rx_decim, 1, 0x32103210, usrp.FPGA_MODE_LOOPBACK) + head = gr.head (gr.sizeof_short, stream_length) + check = gr.check_lfsr_32k_s () + fg.connect (usrp_rx, head, check) + + fg.run () + + ntotal = check.ntotal () + nright = check.nright () + runlength = check.runlength () + + if verbose: + print "usb_throughput =", eng_notation.num_to_str (usb_throughput) + print "ntotal =", ntotal + print "nright =", nright + print "runlength =", runlength + print "delta =", ntotal - runlength + + return runlength >= stream_length - 80000 + +def main (): + verbose = True + best_rate = 0 + usb_rate = [ 2e6, 4e6, 8e6, 16e6, 32e6 ] + #usb_rate = [ 32e6, 32e6, 32e6, 32e6, 32e6 ] + # usb_rate.reverse () + for rate in usb_rate: + sys.stdout.write ("Testing %sB/sec... " % (eng_notation.num_to_str (rate))) + sys.stdout.flush () + ok = run_test (rate, verbose) + if ok: + best_rate = max (best_rate, rate) + sys.stdout.write ("OK\n") + else: + sys.stdout.write ("FAILED\n") + + print "Max USB/USRP throughput = %sB/sec" % (eng_notation.num_to_str (best_rate),) + +if __name__ == '__main__': + main () diff --git a/gnuradio-examples/python/usrp/dsb_tx.py b/gnuradio-examples/python/usrp/dsb_tx.py new file mode 100755 index 000000000..df65761cd --- /dev/null +++ b/gnuradio-examples/python/usrp/dsb_tx.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python + +from gnuradio import gr, eng_notation +from gnuradio import usrp +from gnuradio import audio +from gnuradio.eng_option import eng_option +from optparse import OptionParser + + +def build_graph (cordic_freq): + + audio_rate = 32000 + sw_interp = 4 + usrp_interp = 1000 + audio_file = "/home/eb/demo/testing-audio.dat" + + fg = gr.flow_graph () + + src = gr.file_source (gr.sizeof_float, audio_file, True) + gain = gr.multiply_const_ff (10000) + + interp = gr.interp_fir_filter_fff (sw_interp, (1, 1, 1, 1)) + + f2c = gr.float_to_complex () + + u = usrp.sink_c (0, usrp_interp) + u.set_tx_freq (0, cordic_freq) + + fg.connect (src, gain) + fg.connect (gain, interp) + fg.connect (interp, (f2c, 0)) + fg.connect (interp, (f2c, 1)) + fg.connect (f2c, u) + + return fg + +def main (): + parser = OptionParser (option_class=eng_option) + parser.add_option ("-c", "--cordic-freq", type="eng_float", default=10e6, + help="set Tx cordic frequency to FREQ", metavar="FREQ") + (options, args) = parser.parse_args () + + print "cordic_freq = %s" % (eng_notation.num_to_str (options.cordic_freq)) + fg = build_graph (options.cordic_freq) + + fg.start () + raw_input ('Press Enter to quit: ') + fg.stop () + +if __name__ == '__main__': + main () diff --git a/gnuradio-examples/python/usrp/flexrf_debug.py b/gnuradio-examples/python/usrp/flexrf_debug.py new file mode 100755 index 000000000..70c630960 --- /dev/null +++ b/gnuradio-examples/python/usrp/flexrf_debug.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python +# +# 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. +# + +from gnuradio import gr, gru +from gnuradio import usrp +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import stdgui, fftsink, scopesink, slider +from optparse import OptionParser +import wx + +class app_flow_graph (stdgui.gui_flow_graph): + def __init__(self, frame, panel, vbox, argv): + stdgui.gui_flow_graph.__init__ (self, frame, panel, vbox, argv) + + self.frame = frame + self.panel = panel + + parser = OptionParser (option_class=eng_option) + parser.add_option ("-d", "--decim", type="int", default=8, + help="set fgpa decimation rate to DECIM") + parser.add_option ("-c", "--ddc-freq", type="eng_float", default=0, + help="set Digital downconverter frequency to FREQ", metavar="FREQ") + parser.add_option ("-f", "--freq", type="eng_float", default=950e6, + help="set RF downconverter frequency to FREQ", metavar="FREQ") + parser.add_option ("-m", "--mux", type="intx", default=0x32103210, + help="set fpga FR_RX_MUX register to MUX") + parser.add_option ("-g", "--gain", type="eng_float", default=0, + help="set Rx PGA gain in dB (default 0 dB)") + (options, args) = parser.parse_args () + + self.u = usrp.source_c (0, options.decim, 1, gru.hexint(options.mux), 0) + + self.u.set_verbose (0) + + input_rate = self.u.adc_freq () / self.u.decim_rate () + + block = fftsink.fft_sink_c (self, panel, fft_size=1024, sample_rate=input_rate) + self.connect (self.u, block) + vbox.Add (block.win, 10, wx.EXPAND) + + if 0: + c2f_1 = gr.complex_to_float () + scope = scopesink.scope_sink_f (self, panel, "Rx Data", input_rate) + vbox.Add (scope.win, 6, wx.EXPAND) + + self.connect (self.u,c2f_1) + self.connect ((c2f_1, 0), (scope, 0)) + self.connect ((c2f_1, 1), (scope, 1)) + + if 0: + rms_complex = gr.rms_cf(.0001) + rms_i = gr.rms_ff(.0001) + rms_q = gr.rms_ff(.0001) + + self.connect(self.u,rms_complex) + self.connect((c2f_1,0),rms_i) + self.connect((c2f_1,1),rms_q) + + ns1 = gr.null_sink(4) + ns2 = gr.null_sink(4) + ns3 = gr.null_sink(4) + + self.connect(rms_complex,ns1) + self.connect(rms_i,ns2) + self.connect(rms_q,ns3) + + # sliders + + #vbox.Add(slider.slider(panel, 0, 104, self.set_gain), 1, wx.ALIGN_CENTER) + + #vbox.Add(slider.slider(panel, 0, 4095, self.set_gain_gc1), 1, wx.ALIGN_CENTER) + #vbox.Add(slider.slider(panel, 0, 31, self.set_gain_gc2), 1, wx.ALIGN_CENTER) + #vbox.Add(slider.slider(panel, 0, 1, self.set_gain_dl), 1, wx.ALIGN_CENTER) + #vbox.Add(slider.slider(panel, 0, 200, self.set_gain_i), 1, wx.ALIGN_CENTER) + #vbox.Add(slider.slider(panel, 0, 200, self.set_gain_q), 1, wx.ALIGN_CENTER) + + self.offset = 0 + #vbox.Add(slider.slider(panel, -200, 200, self.set_offset_i), 1, wx.ALIGN_CENTER) + #vbox.Add(slider.slider(panel, -200, 200, self.set_offset_q), 1, wx.ALIGN_CENTER) + + vbox.Add(slider.slider(panel, 380, 480, self.set_rf_freq), 1, wx.EXPAND|wx.ALIGN_CENTER) + vbox.Add(slider.slider(panel, -32000, +32000, self.set_if_freq), 1, wx.EXPAND|wx.ALIGN_CENTER) + vbox.Add(slider.slider(panel, 0, 4095, self.set_gain), 1, wx.EXPAND|wx.ALIGN_CENTER) + + # build small control area at bottom + hbox = wx.BoxSizer (wx.HORIZONTAL) + hbox.Add ((1, 1), 1, wx.EXPAND) + hbox.Add (wx.StaticText (panel, -1, "Set ddc freq: "), 0, wx.ALIGN_CENTER) + self.tc_freq = wx.TextCtrl (panel, -1, "", style=wx.TE_PROCESS_ENTER) + hbox.Add (self.tc_freq, 0, wx.ALIGN_CENTER) + wx.EVT_TEXT_ENTER (self.tc_freq, self.tc_freq.GetId(), self.handle_text_enter) + hbox.Add ((1, 1), 1, wx.EXPAND) + # add it to the main vbox + vbox.Add (hbox, 0, wx.EXPAND) + + self.update_status_bar () + + def set_rf_freq (self,freq): + (success,actual_freq) = self.set_freq(1e6*freq) + if not success: + print "Failed on ",freq + def set_if_freq (self,freq): + self.u.set_rx_freq(0,freq*1e3) + + def set_gain (self,gain): + self.rfrx.set_gain(gain) + + def set_gain_i (self,gain): + self.u.set_pga(0,gain/10.0) + def set_gain_q (self,gain): + self.u.set_pga(1,gain/10.0) + + def set_offset_i(self,offset): + self.offset = (self.offset & 0x0000ffff) | ((offset&0xffff)<<16) + self.u._write_fpga_reg (3,self.offset) + + def set_offset_q(self,offset): + self.offset = (self.offset & 0xffff0000) | (offset&0xffff) + self.u._write_fpga_reg (3,self.offset) + + def handle_text_enter (self, event): + str = event.GetString () + self.tc_freq.Clear () + self.u.set_rx_freq (0, eng_notation.str_to_num (str)) + self.update_status_bar () + + def update_status_bar (self): + ddc_freq = self.u.rx_freq (0) + decim_rate = self.u.decim_rate () + sample_rate = self.u.adc_freq () / decim_rate + msg = "decim: %d %sS/s DDC: %s" % ( + decim_rate, + eng_notation.num_to_str (sample_rate), + eng_notation.num_to_str (ddc_freq)) + + self.frame.GetStatusBar().SetStatusText (msg, 1) + + def set_gain(self,gain): + assert gain>=0 and gain<4096 + self.u.write_aux_dac(0,0,int(gain)) + +def main (): + app = stdgui.stdapp (app_flow_graph, "USRP FFT") + app.MainLoop () + +if __name__ == '__main__': + main () + + diff --git a/gnuradio-examples/python/usrp/flexrf_siggen.py b/gnuradio-examples/python/usrp/flexrf_siggen.py new file mode 100755 index 000000000..6a59148b8 --- /dev/null +++ b/gnuradio-examples/python/usrp/flexrf_siggen.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python + +from gnuradio import gr, gru +from gnuradio import usrp +from gnuradio.eng_option import eng_option +from optparse import OptionParser +from gnuradio.wxgui import stdgui, slider +import wx + +class flex_siggen (stdgui.gui_flow_graph): + __slots__ = ['interp', 'waveform_type', 'waveform_ampl', + 'waveform_freq', 'waveform_offset', 'fg', 'usrp', + 'siggen', 'noisegen', 'src', 'file_sink' ] + + def __init__ (self,frame,panel,vbox,argv): + stdgui.gui_flow_graph.__init__ (self, frame, panel, vbox, argv) + + self.frame = frame + self.panel = panel + + parser = OptionParser (option_class=eng_option) + parser.add_option ("-a", "--amplitude", type="int", default=32000, + help="amplitude") + parser.add_option ("-i", "--interp", type="int", default=64, + help="set fpga interpolation rate to INTERP") + parser.add_option ("-n", "--nchannels", type="int", default=1, + help="set number of output channels to NCHANNELS") + (options, args) = parser.parse_args () + + self.waveform_type = gr.GR_CONST_WAVE + self.waveform_ampl = options.amplitude + self.waveform_freq = 100.12345e3 + self.waveform_offset = 0 + + self.interp = options.interp + self._instantiate_blocks () + self.usrp.set_nchannels (options.nchannels) + + self.dboard=self.usrp.db[0][0] + + self.set_waveform_type (self.waveform_type) + vbox.Add(slider.slider(panel, 390, 510, self.set_rf_freq), 1, wx.EXPAND|wx.ALIGN_CENTER) + vbox.Add(slider.slider(panel, -45000, +45000, self.set_if_freq), 1, wx.EXPAND|wx.ALIGN_CENTER) + #vbox.Add(slider.slider(panel, 0, 4095, self.set_gain), 1, wx.EXPAND|wx.ALIGN_CENTER) + + def usb_freq (self): + return self.usrp.dac_freq() / self.interp + + def usb_throughput (self): + return self.usb_freq () * 4 + + def set_waveform_type (self, type): + ''' + valid waveform types are: gr.GR_SIN_WAVE, gr.GR_CONST_WAVE, + gr.GR_UNIFORM and gr.GR_GAUSSIAN + ''' + self._configure_graph (type) + self.waveform_type = type + + def set_waveform_ampl (self, ampl): + self.waveform_ampl = ampl + self.siggen.set_amplitude (ampl) + self.noisegen.set_amplitude (ampl) + + def set_waveform_freq (self, freq): + self.waveform_freq = freq + self.siggen.set_frequency (freq) + + def set_if_freq (self, freq): + self.if_freq = freq + self.usrp.set_tx_freq (0,freq*1e3) + + def set_rf_freq (self, freq): + self.rf_freq = freq + (success,actual_freq) = self.dboard.set_freq (freq*1e6) + if not success: + print "Failed on ", freq + + def set_waveform_offset (self, offset): + self.waveform_offset = offset + self.siggen.set_offset (offset) + + def set_interpolator (self, interp): + self.interp = interp + self.siggen.set_sampling_freq (self.usb_freq ()) + self.usrp.set_interp_rate (interp) + + def set_duc_freq (self, freq): + self.usrp.set_tx_freq (0, freq) + + def _instantiate_blocks (self): + self.src = None + self.usrp = usrp.sink_c (0, self.interp) + + self.siggen = gr.sig_source_c (self.usb_freq (), + gr.GR_SIN_WAVE, + self.waveform_freq, + self.waveform_ampl, + self.waveform_offset) + + self.noisegen = gr.noise_source_c (gr.GR_UNIFORM, + self.waveform_ampl) + print "done" + + def _configure_graph (self, type): + was_running = self.is_running () + if was_running: + self.stop () + self.disconnect_all () + if type == gr.GR_SIN_WAVE or type == gr.GR_CONST_WAVE: + self.connect (self.siggen, self.usrp) + self.siggen.set_waveform (type) + self.src = self.siggen + elif type == gr.GR_UNIFORM or type == gr.GR_GAUSSIAN: + self.connect (self.noisegen, self.usrp) + self.noisegen.set_type (type) + self.src = self.noisegen + else: + raise ValueError, type + if was_running: + self.start () + + +if __name__ == '__main__': + parser = OptionParser (option_class=eng_option) + parser.add_option ("--sine", dest="type", action="store_const", const=gr.GR_SIN_WAVE, + help="generate a complex sinusoid [default]", default=gr.GR_SIN_WAVE) + parser.add_option ("--const", dest="type", action="store_const", const=gr.GR_CONST_WAVE, + help="generate a constant output") + parser.add_option ("--gaussian", dest="type", action="store_const", const=gr.GR_GAUSSIAN, + help="generate Gaussian random output") + parser.add_option ("--uniform", dest="type", action="store_const", const=gr.GR_UNIFORM, + help="generate Uniform random output") + parser.add_option ("-f", "--freq", type="eng_float", default=100e3, + help="set waveform frequency to FREQ") + parser.add_option ("-r", "--rf-freq", type="eng_float", default=910e6, + help="set waveform frequency to FREQ") + parser.add_option ("-a", "--amplitude", type="eng_float", default=16e3, + help="set waveform amplitude to AMPLITUDE", metavar="AMPL") + parser.add_option ("-o", "--offset", type="eng_float", default=0, + help="set waveform offset to OFFSET") + parser.add_option ("-c", "--duc-freq", type="eng_float", default=0, + help="set Tx DUC frequency to FREQ", metavar="FREQ") + parser.add_option ("-m", "--mux", type="intx", default=0x98, + help="set output mux register") + + app = stdgui.stdapp (flex_siggen, "USRP FlexRF Siggen") + app.MainLoop () diff --git a/gnuradio-examples/python/usrp/fm_tx4.py b/gnuradio-examples/python/usrp/fm_tx4.py new file mode 100755 index 000000000..a9201b3f4 --- /dev/null +++ b/gnuradio-examples/python/usrp/fm_tx4.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python + +""" +Transmit N simultaneous narrow band FM signals. + +They will be centered at the frequency specified on the command line, +and will spaced at 25kHz steps from there. + +The program opens N files with names audio-N.dat where N is in [0,7]. +These files should contain floating point audio samples in the range [-1,1] +sampled at 32kS/sec. You can create files like this using +audio_to_file.py +""" + +from gnuradio import gr, eng_notation +from gnuradio import usrp +from gnuradio import audio +from gnuradio import blks +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import usrp_dbid +import math +import sys + +from gnuradio.wxgui import stdgui, fftsink +from gnuradio import tx_debug_gui +import wx + + +######################################################## +# instantiate one transmit chain for each call + +class pipeline(gr.hier_block): + def __init__(self, fg, filename, lo_freq, audio_rate, if_rate): + + src = gr.file_source (gr.sizeof_float, filename, True) + fmtx = blks.nbfm_tx (fg, audio_rate, if_rate, + max_dev=5e3, tau=75e-6) + + # Local oscillator + lo = gr.sig_source_c (if_rate, # sample rate + gr.GR_SIN_WAVE, # waveform type + lo_freq, #frequency + 1.0, # amplitude + 0) # DC Offset + mixer = gr.multiply_cc () + + fg.connect (src, fmtx, (mixer, 0)) + fg.connect (lo, (mixer, 1)) + + gr.hier_block.__init__(self, fg, src, mixer) + + + +class fm_tx_graph (stdgui.gui_flow_graph): + def __init__(self, frame, panel, vbox, argv): + MAX_CHANNELS = 7 + stdgui.gui_flow_graph.__init__ (self, frame, panel, vbox, argv) + + parser = OptionParser (option_class=eng_option) + parser.add_option("-T", "--tx-subdev-spec", type="subdev", default=None, + help="select USRP Tx side A or B") + parser.add_option("-f", "--freq", type="eng_float", default=None, + help="set Tx frequency to FREQ [required]", metavar="FREQ") + parser.add_option("-n", "--nchannels", type="int", default=4, + help="number of Tx channels [1,4]") + parser.add_option("","--debug", action="store_true", default=False, + help="Launch Tx debugger") + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if options.nchannels < 1 or options.nchannels > MAX_CHANNELS: + sys.stderr.write ("fm_tx4: nchannels out of range. Must be in [1,%d]\n" % MAX_CHANNELS) + sys.exit(1) + + if options.freq is None: + sys.stderr.write("fm_tx4: must specify frequency with -f FREQ\n") + parser.print_help() + sys.exit(1) + + # ---------------------------------------------------------------- + # Set up constants and parameters + + self.u = usrp.sink_c () # the USRP sink (consumes samples) + + self.dac_rate = self.u.dac_rate() # 128 MS/s + self.usrp_interp = 400 + self.u.set_interp_rate(self.usrp_interp) + self.usrp_rate = self.dac_rate / self.usrp_interp # 320 kS/s + self.sw_interp = 10 + self.audio_rate = self.usrp_rate / self.sw_interp # 32 kS/s + + # determine the daughterboard subdevice we're using + if options.tx_subdev_spec is None: + options.tx_subdev_spec = usrp.pick_tx_subdevice(self.u) + + m = usrp.determine_tx_mux_value(self.u, options.tx_subdev_spec) + #print "mux = %#04x" % (m,) + self.u.set_mux(m) + self.subdev = usrp.selected_subdev(self.u, options.tx_subdev_spec) + print "Using TX d'board %s" % (self.subdev.side_and_name(),) + + self.subdev.set_gain(self.subdev.gain_range()[1]) # set max Tx gain + self.set_freq(options.freq) + self.subdev.set_enable(True) # enable transmitter + + sum = gr.add_cc () + + # Instantiate N NBFM channels + step = 25e3 + offset = (0 * step, 1 * step, -1 * step, 2 * step, -2 * step, 3 * step, -3 * step) + for i in range (options.nchannels): + t = pipeline (self, "audio-%d.dat" % (i % 4), offset[i], + self.audio_rate, self.usrp_rate) + self.connect (t, (sum, i)) + + gain = gr.multiply_const_cc (4000.0 / options.nchannels) + + # connect it all + self.connect (sum, gain) + self.connect (gain, self.u) + + # plot an FFT to verify we are sending what we want + if 1: + post_mod = fftsink.fft_sink_c(self, panel, title="Post Modulation", + fft_size=512, sample_rate=self.usrp_rate, + y_per_div=20, ref_level=40) + self.connect (sum, post_mod) + vbox.Add (post_mod.win, 1, wx.EXPAND) + + + if options.debug: + self.debugger = tx_debug_gui.tx_debug_gui(self.subdev) + self.debugger.Show(True) + + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + + 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 up converter. Finally, we feed + any residual_freq to the s/w freq translater. + """ + + r = self.u.tune(self.subdev._which, self.subdev, target_freq) + if r: + print "r.baseband_freq =", eng_notation.num_to_str(r.baseband_freq) + print "r.dxc_freq =", eng_notation.num_to_str(r.dxc_freq) + print "r.residual_freq =", eng_notation.num_to_str(r.residual_freq) + print "r.inverted =", r.inverted + + # Could use residual_freq in s/w freq translator + return True + + return False + +def main (): + app = stdgui.stdapp (fm_tx_graph, "Multichannel FM Tx") + app.MainLoop () + +if __name__ == '__main__': + main () diff --git a/gnuradio-examples/python/usrp/fm_tx_2_daughterboards.py b/gnuradio-examples/python/usrp/fm_tx_2_daughterboards.py new file mode 100755 index 000000000..1cb161018 --- /dev/null +++ b/gnuradio-examples/python/usrp/fm_tx_2_daughterboards.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python + +""" +Transmit 2 signals, one out each daughterboard. + +Outputs SSB (USB) signals on side A and side B at frequencies +specified on command line. + +Side A is 600 Hz tone. +Side B is 350 + 440 Hz tones. +""" + +from gnuradio import gr +from gnuradio.eng_notation import num_to_str, str_to_num +from gnuradio import usrp +from gnuradio import audio +from gnuradio import blks +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import usrp_dbid +import math +import sys + + +class example_signal_0(gr.hier_block): + """ + Sinusoid at 600 Hz. + """ + def __init__(self, fg, sample_rate): + + src = gr.sig_source_c (sample_rate, # sample rate + gr.GR_SIN_WAVE, # waveform type + 600, # frequency + 1.0, # amplitude + 0) # DC Offset + + gr.hier_block.__init__(self, fg, None, src) + + +class example_signal_1(gr.hier_block): + """ + North American dial tone (350 + 440 Hz). + """ + def __init__(self, fg, sample_rate): + + src0 = gr.sig_source_c (sample_rate, # sample rate + gr.GR_SIN_WAVE, # waveform type + 350, # frequency + 1.0, # amplitude + 0) # DC Offset + + src1 = gr.sig_source_c (sample_rate, # sample rate + gr.GR_SIN_WAVE, # waveform type + 440, # frequency + 1.0, # amplitude + 0) # DC Offset + sum = gr.add_cc() + fg.connect(src0, (sum, 0)) + fg.connect(src1, (sum, 1)) + + gr.hier_block.__init__(self, fg, None, sum) + + + +class my_graph(gr.flow_graph): + + def __init__(self): + gr.flow_graph.__init__ (self) + + usage="%prog: [options] side-A-tx-freq side-B-tx-freq" + parser = OptionParser (option_class=eng_option, usage=usage) + (options, args) = parser.parse_args () + + if len(args) != 2: + parser.print_help() + raise SystemExit + else: + freq0 = str_to_num(args[0]) + freq1 = str_to_num(args[1]) + + # ---------------------------------------------------------------- + # Set up USRP to transmit on both daughterboards + + self.u = usrp.sink_c(nchan=2) # say we want two channels + + self.dac_rate = self.u.dac_rate() # 128 MS/s + self.usrp_interp = 400 + self.u.set_interp_rate(self.usrp_interp) + self.usrp_rate = self.dac_rate / self.usrp_interp # 320 kS/s + + # we're using both daughterboard slots, thus subdev is a 2-tuple + self.subdev = (self.u.db[0][0], self.u.db[1][0]) + print "Using TX d'board %s" % (self.subdev[0].side_and_name(),) + print "Using TX d'board %s" % (self.subdev[1].side_and_name(),) + + # set up the Tx mux so that + # channel 0 goes to Slot A I&Q and channel 1 to Slot B I&Q + self.u.set_mux(0xba98) + + self.subdev[0].set_gain(self.subdev[0].gain_range()[1]) # set max Tx gain + self.subdev[1].set_gain(self.subdev[1].gain_range()[1]) # set max Tx gain + + self.set_freq(0, freq0) + self.set_freq(1, freq1) + self.subdev[0].set_enable(True) # enable transmitter + self.subdev[1].set_enable(True) # enable transmitter + + # ---------------------------------------------------------------- + # build two signal sources, interleave them, amplify and connect them to usrp + + sig0 = example_signal_0(self, self.usrp_rate) + sig1 = example_signal_1(self, self.usrp_rate) + + intl = gr.interleave(gr.sizeof_gr_complex) + self.connect(sig0, (intl, 0)) + self.connect(sig1, (intl, 1)) + + # apply some gain + if_gain = 10000 + ifamp = gr.multiply_const_cc(if_gain) + + # and wire them up + self.connect(intl, ifamp, self.u) + + + def set_freq(self, side, target_freq): + """ + Set the center frequency we're interested in. + + @param side: 0 = side A, 1 = side B + @param target_freq: frequency in Hz + @rtype: bool + + 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 up converter. + """ + + print "Tuning side %s to %sHz" % (("A", "B")[side], num_to_str(target_freq)) + r = self.u.tune(self.subdev[side]._which, self.subdev[side], target_freq) + if r: + print " r.baseband_freq =", num_to_str(r.baseband_freq) + print " r.dxc_freq =", num_to_str(r.dxc_freq) + print " r.residual_freq =", num_to_str(r.residual_freq) + print " r.inverted =", r.inverted + print " OK" + return True + + else: + print " Failed!" + + return False + + +if __name__ == '__main__': + try: + my_graph().run() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/usrp/max_power.py b/gnuradio-examples/python/usrp/max_power.py new file mode 100755 index 000000000..b4ad86b09 --- /dev/null +++ b/gnuradio-examples/python/usrp/max_power.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# +# 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. +# + +""" +Setup USRP for maximum power consumption. +""" + + +from gnuradio import gr +from gnuradio import usrp +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +def ramp_source (fg): + period = 2**16 + src = gr.vector_source_s (range (-period/2, period/2, 1), True) + return src + +def build_graph (tx_enable, rx_enable): + max_usb_rate = 8e6 # 8 MS/sec + dac_freq = 128e6 + adc_freq = 64e6 + + tx_nchan = 2 + tx_mux = 0x0000ba98 + tx_interp = int (dac_freq / (max_usb_rate/2 * tx_nchan)) # 16 + + rx_nchan = 2 + rx_mux = 0x00003210 + rx_decim = int ((adc_freq * rx_nchan) / (max_usb_rate/2)) # 32 + + fg = gr.flow_graph () + + if tx_enable: + tx_src0 = gr.sig_source_c (dac_freq/tx_interp, gr.GR_CONST_WAVE, 0, 16e3, 0) + usrp_tx = usrp.sink_c (0, tx_interp, tx_nchan, tx_mux) + usrp_tx.set_tx_freq (0, 10e6) + usrp_tx.set_tx_freq (1, 9e6) + fg.connect (tx_src0, usrp_tx) + + if rx_enable: + usrp_rx = usrp.source_c (0, rx_decim, rx_nchan, rx_mux) + usrp_rx.set_rx_freq (0, 5.5e6) + usrp_rx.set_rx_freq (1, 6.5e6) + rx_dst0 = gr.null_sink (gr.sizeof_gr_complex) + fg.connect (usrp_rx, rx_dst0) + + return fg + +def main (): + parser = OptionParser (option_class=eng_option) + parser.add_option ("-t", action="store_true", dest="tx_enable", + default=False, help="enable Tx path") + parser.add_option ("-r", action="store_true", dest="rx_enable", + default=False, help="enable Rx path") + (options, args) = parser.parse_args () + fg = build_graph (options.tx_enable, options.rx_enable) + + fg.start () + raw_input ('Press Enter to quit: ') + fg.stop () + +if __name__ == '__main__': + main () diff --git a/gnuradio-examples/python/usrp/siggen_min2.py b/gnuradio-examples/python/usrp/siggen_min2.py new file mode 100755 index 000000000..8709e3373 --- /dev/null +++ b/gnuradio-examples/python/usrp/siggen_min2.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python + +from gnuradio import gr +from gnuradio import usrp +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + + + +def build_graph (): + + # interp = 32 + interp = 64 + nchan = 2 + + if nchan == 1: + mux = 0x0098 + #mux = 0x9800 + else: + mux = 0xba98 + + f0 = 100e3 + a0 = 16e3 + duc0 = 5e6 + + f1 = 50e3 + a1 = 16e3 + duc1 = 7e6 + + fg = gr.flow_graph () + + u = usrp.sink_c (0, interp, nchan, mux) + sample_rate = u.dac_freq () / interp + print "sample_rate = ", eng_notation.num_to_str (sample_rate) + print "usb_sample_rate = ", eng_notation.num_to_str (sample_rate * nchan) + + u.set_tx_freq (0, duc0) + u.set_tx_freq (1, duc1) + + interleave = gr.interleave (gr.sizeof_gr_complex) + fg.connect (interleave, u) + + src0 = gr.sig_source_c (sample_rate, gr.GR_SIN_WAVE, f0, a0, 0) + fg.connect (src0, (interleave, 0)) + + if nchan == 2: + if 1: + src1 = gr.sig_source_c (sample_rate, gr.GR_SIN_WAVE, f1, a1, 0) + else: + src1 = gr.noise_source_c (gr.GR_UNIFORM, a1) + fg.connect (src1, (interleave, 1)) + + return fg + + +if __name__ == '__main__': + fg = build_graph () + fg.start () + raw_input ('Press Enter to quit: ') + fg.stop () + diff --git a/gnuradio-examples/python/usrp/test_counting.py b/gnuradio-examples/python/usrp/test_counting.py new file mode 100755 index 000000000..ed9328a33 --- /dev/null +++ b/gnuradio-examples/python/usrp/test_counting.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# +# 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. +# + +""" +Check Rx path or USRP Rev 1. + +This configures the USRP to return a periodic sequence of integers +""" + +from gnuradio import gr +from gnuradio import usrp + +def build_graph (): + rx_decim = 32 + + fg = gr.flow_graph () + usrp_rx = usrp.source_s (0, rx_decim, 1, 0x32103210, usrp.FPGA_MODE_COUNTING) + sink = gr.check_counting_s () + fg.connect (usrp_rx, sink) + + # file_sink = gr.file_sink (gr.sizeof_short, 'counting.dat') + # fg.connect (usrp_rx, file_sink) + + return fg + +def main (): + fg = build_graph () + try: + fg.run() + except KeyboardInterrupt: + pass + +if __name__ == '__main__': + main () diff --git a/gnuradio-examples/python/usrp/test_dft_analysis.py b/gnuradio-examples/python/usrp/test_dft_analysis.py new file mode 100755 index 000000000..a1d9eda46 --- /dev/null +++ b/gnuradio-examples/python/usrp/test_dft_analysis.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python + +from gnuradio import gr, gru, blks +from gnuradio.wxgui import stdgui, fftsink, slider +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import wx + +class test_graph (stdgui.gui_flow_graph): + def __init__(self, frame, panel, vbox, argv): + stdgui.gui_flow_graph.__init__(self, frame, panel, vbox, argv) + + parser = OptionParser (option_class=eng_option) + (options, args) = parser.parse_args () + + sample_rate = 16e3 + mpoints = 4 + ampl = 1000 + freq = 0 + + lo_freq = 1e6 + lo_ampl = 1 + + vbox.Add(slider.slider(panel, + -sample_rate/2, sample_rate/2, + self.set_lo_freq), 0, wx.ALIGN_CENTER) + + + src = gr.sig_source_c(sample_rate, gr.GR_CONST_WAVE, + freq, ampl, 0) + + self.lo = gr.sig_source_c(sample_rate, gr.GR_SIN_WAVE, + lo_freq, lo_ampl, 0) + + mixer = gr.multiply_cc() + self.connect(src, (mixer, 0)) + self.connect(self.lo, (mixer, 1)) + + # We add these throttle blocks so that this demo doesn't + # suck down all the CPU available. Normally you wouldn't use these. + thr = gr.throttle(gr.sizeof_gr_complex, sample_rate) + + taps = gr.firdes.low_pass(1, # gain + 1, # rate + 1.0/mpoints * 0.4, # cutoff + 1.0/mpoints * 0.1, # trans width + gr.firdes.WIN_HANN) + print len(taps) + analysis = blks.analysis_filterbank(self, mpoints, taps) + + self.connect(mixer, thr) + self.connect(thr, analysis) + + for i in range(mpoints): + fft = fftsink.fft_sink_c(self, frame, fft_size=128, + sample_rate=sample_rate/mpoints, + fft_rate=5, + title="Ch %d" % (i,)) + self.connect((analysis, i), fft) + vbox.Add(fft.win, 1, wx.EXPAND) + + def set_lo_freq(self, freq): + self.lo.set_frequency(freq) + + + +def main (): + app = stdgui.stdapp (test_graph, "Test DFT filterbank") + app.MainLoop () + +if __name__ == '__main__': + main () diff --git a/gnuradio-examples/python/usrp/test_dft_synth.py b/gnuradio-examples/python/usrp/test_dft_synth.py new file mode 100755 index 000000000..60a49e3b3 --- /dev/null +++ b/gnuradio-examples/python/usrp/test_dft_synth.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python + +from gnuradio import gr, gru, blks +from gnuradio.wxgui import stdgui, fftsink +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import wx +import random + + +def make_random_complex_tuple(L, gain=1): + result = [] + for x in range(L): + result.append(gain * complex(random.gauss(0, 1),random.gauss(0, 1))) + + return tuple(result) + +def random_noise_c(gain=1): + src = gr.vector_source_c(make_random_complex_tuple(32*1024, gain), True) + return src + + +class test_graph (stdgui.gui_flow_graph): + def __init__(self, frame, panel, vbox, argv): + stdgui.gui_flow_graph.__init__(self, frame, panel, vbox, argv) + + parser = OptionParser (option_class=eng_option) + (options, args) = parser.parse_args () + + sample_rate = 16e6 + mpoints = 16 + ampl = 1000 + + enable = mpoints * [0] + enable[0] = 1 + + taps = gr.firdes.low_pass(1, # gain + 1, # rate + 1.0/mpoints * 0.4, # cutoff + 1.0/mpoints * 0.1, # trans width + gr.firdes.WIN_HANN) + + synth = blks.synthesis_filterbank(self, mpoints, taps) + + null_source = gr.null_source(gr.sizeof_gr_complex) + + if 0: + for i in range(mpoints): + s = gr.sig_source_c(sample_rate/mpoints, gr.GR_SIN_WAVE, + 300e3, ampl * enable[i], 0) + self.connect(s, (synth, i)) + + else: + for i in range(mpoints): + if i == 0: + s = gr.sig_source_c(sample_rate/mpoints, gr.GR_SIN_WAVE, + 300e3, ampl * enable[i], 0) + #s = random_noise_c(ampl) + self.connect(s, (synth, i)) + else: + self.connect(null_source, (synth, i)) + + + # We add these throttle blocks so that this demo doesn't + # suck down all the CPU available. Normally you wouldn't use these. + thr = gr.throttle(gr.sizeof_gr_complex, sample_rate) + fft = fftsink.fft_sink_c(self, frame, fft_size=1024, + sample_rate=sample_rate) + vbox.Add(fft.win, 1, wx.EXPAND) + + self.connect(synth, thr, fft) + + +def main (): + app = stdgui.stdapp (test_graph, "Test DFT filterbank") + app.MainLoop () + +if __name__ == '__main__': + main () diff --git a/gnuradio-examples/python/usrp/test_digital_loopback_counting.py b/gnuradio-examples/python/usrp/test_digital_loopback_counting.py new file mode 100755 index 000000000..e985e4b4e --- /dev/null +++ b/gnuradio-examples/python/usrp/test_digital_loopback_counting.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# +# 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. +# + +""" +Digital loopback (Tx to Rx) for the USRP Rev1. +""" + + +from gnuradio import gr +from gnuradio import usrp + + +def ramp_source (fg): + period = 2**16 + src = gr.vector_source_s (range (-period/2, period/2, 1), True) + return src + +def build_graph (): + tx_interp = 32 # tx should be twice rx + rx_decim = 16 + + fg = gr.flow_graph () + + data_src = ramp_source (fg) + # usrp_tx = usrp.sink_s (0, tx_interp, 1, 0x98) + usrp_tx = usrp.sink_s (0, tx_interp) + fg.connect (data_src, usrp_tx) + + usrp_rx = usrp.source_s (0, rx_decim, 1, 0x32103210, usrp.FPGA_MODE_LOOPBACK) + sink = gr.check_counting_s () + fg.connect (usrp_rx, sink) + + # file_sink = gr.file_sink (gr.sizeof_short, "loopback.dat") + # fg.connect (usrp_rx, file_sink) + + return fg + +def main (): + fg = build_graph () + try: + fg.run() + except KeyboardInterrupt: + pass + +if __name__ == '__main__': + main () diff --git a/gnuradio-examples/python/usrp/test_digital_loopback_lfsr.py b/gnuradio-examples/python/usrp/test_digital_loopback_lfsr.py new file mode 100755 index 000000000..ae78c7143 --- /dev/null +++ b/gnuradio-examples/python/usrp/test_digital_loopback_lfsr.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +# +# 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. +# + +""" +Digital loopback (Tx to Rx) for the USRP Rev1. +""" + +from gnuradio import gr +from gnuradio import usrp + + +def build_graph (): + tx_interp = 32 # tx should be twice rx + rx_decim = 16 + + fg = gr.flow_graph () + + data_src = gr.lfsr_32k_source_s () + + # usrp_tx = usrp.sink_s (0, tx_interp, 1, 0x98) + usrp_tx = usrp.sink_s (0, tx_interp) + + fg.connect (data_src, usrp_tx) + + usrp_rx = usrp.source_s (0, rx_decim, 1, 0x32103210, usrp.FPGA_MODE_LOOPBACK) + + sink = gr.check_lfsr_32k_s () + fg.connect (usrp_rx, sink) + + # file_sink = gr.file_sink (gr.sizeof_short, "loopback.dat") + # fg.connect (usrp_rx, file_sink) + + return fg + +def main (): + fg = build_graph () + try: + fg.run() + except KeyboardInterrupt: + pass + +if __name__ == '__main__': + main () diff --git a/gnuradio-examples/python/usrp/tvrx_am_rcv_gui.py b/gnuradio-examples/python/usrp/tvrx_am_rcv_gui.py new file mode 100755 index 000000000..e4ad36931 --- /dev/null +++ b/gnuradio-examples/python/usrp/tvrx_am_rcv_gui.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python +# +# 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. +# +# +# Demodulate an AM signal from the TVRX or a recorded file. +# The file format must be 256 ksps, complex data. +# + +from gnuradio import gr, gru, eng_notation +from gnuradio import audio_oss as audio +from gnuradio import usrp +from gnuradio import tv_rx +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import sys +import math +from gnuradio.wxgui import stdgui, fftsink, scopesink +import wx + +# +# return a gr.flow_graph +# +class wfm_rx_graph (stdgui.gui_flow_graph): + def __init__(self,frame,panel,vbox,argv): + stdgui.gui_flow_graph.__init__ (self,frame,panel,vbox,argv) + + #set rf freq + rf_freq = 120.e6 + + # Decimation rate from USRP ADC to IF. + usrp_decim = 100 + + # Calculate the sampling rate of the USRP and capture file. + # Decimate the IF sampling rate down by 4 to 64 ksps + # This is a flow graph that has an input (capture file) and output (audio channel). + #self = gr.flow_graph () + + # Signal source is assumed to be 256 kspb / complex data stream. + which_side = 0 + # usrp is data source + if which_side == 0: + src = usrp.source_c (0, usrp_decim, 1, gru.hexint(0xf0f0f0f0), 0) + else: + src = usrp.source_c (0, usrp_decim, 1, gru.hexint(0xf0f0f0f2), 0) + + if_rate = 640e3 # src.adc_freq() / usrp_decim + if_decim = 5 + demod_rate = if_rate / if_decim + + audio_decimation = 4 + audio_rate = demod_rate / audio_decimation + + # set up frontend + dboard = tv_rx.tv_rx (src, which_side) + self.dboard = dboard + (success, actual_freq) = dboard.set_freq(rf_freq) + assert success + + if_freq = rf_freq - actual_freq + src.set_rx_freq (0, -if_freq) + + print "actual freq ", actual_freq + print "IF freq ", if_freq + + dboard.set_gain(50) + + #src = gr.file_source (gr.sizeof_gr_complex, "samples/atis_ffz_am_baseband_256k_complex.dat") + #src = gr.file_source (gr.sizeof_gr_complex, "samples/garagedoor1.dat", True) + + #channel_coeffs = gr.firdes.band_pass ( + # 1.0, # gain + # if_rate, + # 10, # center of low transition band + # 10000, # center of hi transition band + # 200, # width of transition band + # gr.firdes.WIN_HAMMING) + + channel_coeffs = gr.firdes.low_pass (1.0, if_rate, 10e3, 4e3, gr.firdes.WIN_HANN) + print "len(channel_coeffs) = ", len(channel_coeffs) + + # Tune to the desired frequency. + ddc = gr.freq_xlating_fir_filter_ccf (if_decim, channel_coeffs, -20e3, if_rate) + + # Demodule with classic sqrt (I*I + Q*Q) + magblock = gr.complex_to_mag() + + # Scale the audio + volumecontrol = gr.multiply_const_ff(.1) + + #band-pass + audio_coeffs = gr.firdes.band_pass ( + 1.0, # gain + demod_rate, + 10, # center of low transition band + 6000, # center of hi transition band + 200, # width of transition band + gr.firdes.WIN_HAMMING) + + + # Low pass filter the demodulator output + #audio_coeffs = gr.firdes.low_pass (1.0, demod_rate, 500, 200, gr.firdes.WIN_HANN) + print "len(audio_coeffs) = ", len(audio_coeffs) + + # input: float; output: float + audio_filter = gr.fir_filter_fff (audio_decimation, audio_coeffs) + + # sound card as final sink + audio_sink = audio.sink (int (audio_rate)) + + # now wire it all together + self.connect (src, ddc) + self.connect (ddc, magblock) + self.connect (magblock, volumecontrol) + self.connect (volumecontrol, audio_filter) + self.connect (audio_filter, (audio_sink, 0)) + + d_win = fftsink.fft_sink_c (self, panel, title="RF", fft_size=512, sample_rate=if_rate) + self.connect (src,d_win) + vbox.Add (d_win.win, 4, wx.EXPAND) + + p_win = fftsink.fft_sink_c (self, panel, title="IF", fft_size=512, sample_rate=demod_rate) + self.connect (ddc,p_win) + vbox.Add (p_win.win, 4, wx.EXPAND) + + r_win = fftsink.fft_sink_f (self, panel, title="Audio", fft_size=512, sample_rate=audio_rate) + self.connect (audio_filter,r_win) + vbox.Add (r_win.win, 4, wx.EXPAND) + + #audio_oscope = scopesink.scope_sink_f (self, panel, "Oscope Data", audio_rate) + #self.connect (audio_filter, audio_oscope) + #vbox.Add (audio_oscope.win, 4, wx.EXPAND) + +if __name__ == '__main__': + + app = stdgui.stdapp (wfm_rx_graph, "TVRX AM RX") + app.MainLoop () diff --git a/gnuradio-examples/python/usrp/usrp_fft.py b/gnuradio-examples/python/usrp/usrp_fft.py new file mode 100755 index 000000000..f760e39aa --- /dev/null +++ b/gnuradio-examples/python/usrp/usrp_fft.py @@ -0,0 +1,251 @@ +#!/usr/bin/env python +# +# 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. +# + +from gnuradio import gr, gru +from gnuradio import usrp +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import stdgui, fftsink, waterfallsink, scopesink, form, slider +from optparse import OptionParser +import wx +import sys + + +def pick_subdevice(u): + """ + The user didn't specify a subdevice on the command line. + If there's a daughterboard on A, select A. + If there's a daughterboard on B, select B. + Otherwise, select A. + """ + if u.db[0][0].dbid() >= 0: # dbid is < 0 if there's no d'board or a problem + return (0, 0) + if u.db[1][0].dbid() >= 0: + return (1, 0) + return (0, 0) + + +class app_flow_graph(stdgui.gui_flow_graph): + def __init__(self, frame, panel, vbox, argv): + stdgui.gui_flow_graph.__init__(self) + + self.frame = frame + self.panel = panel + + parser = OptionParser(option_class=eng_option) + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, + help="select USRP Rx side A or B (default=first one with a daughterboard)") + parser.add_option("-d", "--decim", type="int", default=16, + help="set fgpa decimation rate to DECIM [default=%default]") + parser.add_option("-f", "--freq", type="eng_float", default=None, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=None, + help="set gain in dB (default is midpoint)") + parser.add_option("-W", "--waterfall", action="store_true", default=False, + help="Enable waterfall display") + parser.add_option("-8", "--width-8", action="store_true", default=False, + help="Enable 8-bit samples across USB") + parser.add_option("-S", "--oscilloscope", action="store_true", default=False, + help="Enable oscilloscope display") + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + + self.show_debug_info = True + + # build the graph + + self.u = usrp.source_c(decim_rate=options.decim) + if options.rx_subdev_spec is None: + options.rx_subdev_spec = pick_subdevice(self.u) + self.u.set_mux(usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec)) + + if options.width_8: + width = 8 + shift = 8 + format = self.u.make_format(width, shift) + print "format =", hex(format) + r = self.u.set_format(format) + print "set_format =", r + + # determine the daughterboard subdevice we're using + self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec) + + input_rate = self.u.adc_freq() / self.u.decim_rate() + + if options.waterfall: + self.scope = \ + waterfallsink.waterfall_sink_c (self, panel, fft_size=1024, sample_rate=input_rate) + elif options.oscilloscope: + self.scope = scopesink.scope_sink_c(self, panel, sample_rate=input_rate) + else: + self.scope = fftsink.fft_sink_c (self, panel, fft_size=1024, sample_rate=input_rate) + + self.connect(self.u, self.scope) + + self._build_gui(vbox) + + # set initial values + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + g = self.subdev.gain_range() + options.gain = float(g[0]+g[1])/2 + + if options.freq is None: + # if no freq was specified, use the mid-point + r = self.subdev.freq_range() + options.freq = float(r[0]+r[1])/2 + + self.set_gain(options.gain) + + if self.show_debug_info: + self.myform['decim'].set_value(self.u.decim_rate()) + self.myform['fs@usb'].set_value(self.u.adc_freq() / self.u.decim_rate()) + self.myform['dbname'].set_value(self.subdev.name()) + self.myform['baseband'].set_value(0) + self.myform['ddc'].set_value(0) + + if not(self.set_freq(options.freq)): + self._set_status_msg("Failed to set initial frequency") + + def _set_status_msg(self, msg): + self.frame.GetStatusBar().SetStatusText(msg, 0) + + def _build_gui(self, vbox): + + def _form_set_freq(kv): + return self.set_freq(kv['freq']) + + vbox.Add(self.scope.win, 10, wx.EXPAND) + + # add control area at the bottom + self.myform = myform = form.form() + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0, 0) + myform['freq'] = form.float_field( + parent=self.panel, sizer=hbox, label="Center freq", weight=1, + callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg)) + + hbox.Add((5,0), 0, 0) + g = self.subdev.gain_range() + myform['gain'] = form.slider_field(parent=self.panel, sizer=hbox, label="Gain", + weight=3, + min=int(g[0]), max=int(g[1]), + callback=self.set_gain) + + hbox.Add((5,0), 0, 0) + vbox.Add(hbox, 0, wx.EXPAND) + + self._build_subpanel(vbox) + + def _build_subpanel(self, vbox_arg): + # build a secondary information panel (sometimes hidden) + + # FIXME figure out how to have this be a subpanel that is always + # created, but has its visibility controlled by foo.Show(True/False) + + def _form_set_decim(kv): + return self.set_decim(kv['decim']) + + if not(self.show_debug_info): + return + + panel = self.panel + vbox = vbox_arg + myform = self.myform + + #panel = wx.Panel(self.panel, -1) + #vbox = wx.BoxSizer(wx.VERTICAL) + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + + myform['decim'] = form.int_field( + parent=panel, sizer=hbox, label="Decim", + callback=myform.check_input_and_call(_form_set_decim, self._set_status_msg)) + + hbox.Add((5,0), 1) + myform['fs@usb'] = form.static_float_field( + parent=panel, sizer=hbox, label="Fs@USB") + + hbox.Add((5,0), 1) + myform['dbname'] = form.static_text_field( + parent=panel, sizer=hbox) + + hbox.Add((5,0), 1) + myform['baseband'] = form.static_float_field( + parent=panel, sizer=hbox, label="Analog BB") + + hbox.Add((5,0), 1) + myform['ddc'] = form.static_float_field( + parent=panel, sizer=hbox, label="DDC") + + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + + 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. + """ + r = self.u.tune(0, self.subdev, target_freq) + + if r: + self.myform['freq'].set_value(target_freq) # update displayed value + if self.show_debug_info: + self.myform['baseband'].set_value(r.baseband_freq) + self.myform['ddc'].set_value(r.dxc_freq) + return True + + return False + + def set_gain(self, gain): + self.myform['gain'].set_value(gain) # update displayed value + self.subdev.set_gain(gain) + + def set_decim(self, decim): + ok = self.u.set_decim_rate(decim) + if not ok: + print "set_decim failed" + input_rate = self.u.adc_freq() / self.u.decim_rate() + self.scope.set_sample_rate(input_rate) + if self.show_debug_info: # update displayed values + self.myform['decim'].set_value(self.u.decim_rate()) + self.myform['fs@usb'].set_value(self.u.adc_freq() / self.u.decim_rate()) + return ok + +def main (): + app = stdgui.stdapp(app_flow_graph, "USRP FFT", nstatus=1) + app.MainLoop() + +if __name__ == '__main__': + main () diff --git a/gnuradio-examples/python/usrp/usrp_fft_old.py b/gnuradio-examples/python/usrp/usrp_fft_old.py new file mode 100755 index 000000000..c0776ea5c --- /dev/null +++ b/gnuradio-examples/python/usrp/usrp_fft_old.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python +# +# 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. +# + +from gnuradio import gr, gru +from gnuradio import usrp +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import stdgui, fftsink, scopesink +from optparse import OptionParser +import wx + +class app_flow_graph (stdgui.gui_flow_graph): + def __init__(self, frame, panel, vbox, argv): + stdgui.gui_flow_graph.__init__ (self, frame, panel, vbox, argv) + + self.frame = frame + self.panel = panel + + parser = OptionParser (option_class=eng_option) + parser.add_option ("-d", "--decim", type="int", default=16, + help="set fgpa decimation rate to DECIM") + parser.add_option ("-c", "--ddc-freq", type="eng_float", default=0, + help="set Rx DDC frequency to FREQ", metavar="FREQ") + parser.add_option ("-m", "--mux", type="intx", default=0x32103210, + help="set fpga FR_RX_MUX register to MUX") + parser.add_option ("-g", "--gain", type="eng_float", default=0, + help="set Rx PGA gain in dB (default 0 dB)") + (options, args) = parser.parse_args () + + self.u = usrp.source_c (0, options.decim, 1, gru.hexint(options.mux), 0) + self.u.set_rx_freq (0, options.ddc_freq) + + self.u.set_pga (0, options.gain) + self.u.set_pga (1, options.gain) + + self.u.set_verbose (0) + + input_rate = self.u.adc_freq () / self.u.decim_rate () + + fft = fftsink.fft_sink_c (self, panel, fft_size=1024, sample_rate=input_rate) + #fft = fftsink.fft_sink_c (self, panel, fft_size=1024, fft_rate=50, sample_rate=input_rate) + self.connect (self.u, fft) + vbox.Add (fft.win, 10, wx.EXPAND) + + if 0: + c2f_1 = gr.complex_to_float () + scope = scopesink.scope_sink_f (self, panel, "Rx Data", input_rate) + vbox.Add (scope.win, 4, wx.EXPAND) + + self.connect (self.u,c2f_1) + self.connect ((c2f_1, 0), (scope, 0)) + self.connect ((c2f_1, 1), (scope, 1)) + + # build small control area at bottom + hbox = wx.BoxSizer (wx.HORIZONTAL) + hbox.Add ((1, 1), 1, wx.EXPAND) + hbox.Add (wx.StaticText (panel, -1, "Set ddc freq: "), 0, wx.ALIGN_CENTER) + self.tc_freq = wx.TextCtrl (panel, -1, "", style=wx.TE_PROCESS_ENTER) + hbox.Add (self.tc_freq, 0, wx.ALIGN_CENTER) + wx.EVT_TEXT_ENTER (self.tc_freq, self.tc_freq.GetId(), self.handle_text_enter) + hbox.Add ((1, 1), 1, wx.EXPAND) + # add it to the main vbox + vbox.Add (hbox, 0, wx.EXPAND) + + self.update_status_bar () + + def handle_text_enter (self, event): + str = event.GetString () + self.tc_freq.Clear () + self.u.set_rx_freq (0, eng_notation.str_to_num (str)) + self.update_status_bar () + + def update_status_bar (self): + ddc_freq = self.u.rx_freq (0) + decim_rate = self.u.decim_rate () + sample_rate = self.u.adc_freq () / decim_rate + msg = "decim: %d %sS/s DDC: %s" % ( + decim_rate, + eng_notation.num_to_str (sample_rate), + eng_notation.num_to_str (ddc_freq)) + + self.frame.GetStatusBar().SetStatusText (msg, 1) + + + +def main (): + app = stdgui.stdapp (app_flow_graph, "USRP FFT") + app.MainLoop () + +if __name__ == '__main__': + main () diff --git a/gnuradio-examples/python/usrp/usrp_nbfm_ptt.py b/gnuradio-examples/python/usrp/usrp_nbfm_ptt.py new file mode 100755 index 000000000..3e930bbb5 --- /dev/null +++ b/gnuradio-examples/python/usrp/usrp_nbfm_ptt.py @@ -0,0 +1,491 @@ +#!/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 math +import sys +import wx +from optparse import OptionParser + +from gnuradio import gr, gru, eng_notation +from gnuradio import usrp +from gnuradio import audio +from gnuradio import blks +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import stdgui, fftsink, scopesink, slider, form +import usrp_dbid + +from Numeric import convolve, array + +#import os +#print "pid =", os.getpid() +#raw_input('Press Enter to continue: ') + +# //////////////////////////////////////////////////////////////////////// +# Control Stuff +# //////////////////////////////////////////////////////////////////////// + +class ptt_graph(stdgui.gui_flow_graph): + def __init__(self, frame, panel, vbox, argv): + stdgui.gui_flow_graph.__init__ (self, frame, panel, vbox, argv) + + self.frame = frame + self.space_bar_pressed = False + + parser = OptionParser (option_class=eng_option) + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, + help="select USRP Rx side A or B") + parser.add_option("-T", "--tx-subdev-spec", type="subdev", default=None, + help="select USRP Tx side A or B") + parser.add_option ("-f", "--freq", type="eng_float", default=442.1e6, + help="set Tx and Rx frequency to FREQ", metavar="FREQ") + parser.add_option ("-g", "--rx-gain", type="eng_float", default=None, + help="set rx gain [default=midpoint in dB]") + parser.add_option("-I", "--audio-input", type="string", default="", + help="pcm input device name. E.g., hw:0,0 or /dev/dsp") + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm output device name. E.g., hw:0,0 or /dev/dsp") + parser.add_option ("-N", "--no-gui", action="store_true", default=False) + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if options.freq < 1e6: + options.freq *= 1e6 + + self.txpath = transmit_path(self, options.tx_subdev_spec, options.audio_input) + self.rxpath = receive_path(self, options.rx_subdev_spec, options.rx_gain, options.audio_output) + self._build_gui(frame, panel, vbox, argv, options.no_gui) + + self.set_transmit(False) + self.set_freq(options.freq) + self.set_rx_gain(self.rxpath.gain) # update gui + self.set_volume(self.rxpath.volume) # update gui + self.set_squelch(self.rxpath.threshold()) # update gui + + + def set_transmit(self, enabled): + self.txpath.set_enable(enabled) + self.rxpath.set_enable(not(enabled)) + if enabled: + self.frame.SetStatusText ("Transmitter ON", 1) + else: + self.frame.SetStatusText ("Receiver ON", 1) + + + def set_rx_gain(self, gain): + self.myform['rx_gain'].set_value(gain) # update displayed value + self.rxpath.set_gain(gain) + + def set_tx_gain(self, gain): + self.txpath.set_gain(gain) + + def set_squelch(self, threshold): + self.rxpath.set_squelch(threshold) + self.myform['squelch'].set_value(self.rxpath.threshold()) + + def set_volume (self, vol): + self.rxpath.set_volume(vol) + self.myform['volume'].set_value(self.rxpath.volume) + #self.update_status_bar () + + def set_freq(self, freq): + r1 = self.txpath.set_freq(freq) + r2 = self.rxpath.set_freq(freq) + #print "txpath.set_freq =", r1 + #print "rxpath.set_freq =", r2 + if r1 and r2: + self.myform['freq'].set_value(freq) # update displayed value + return r1 and r2 + + def _build_gui(self, frame, panel, vbox, argv, no_gui): + + def _form_set_freq(kv): + return self.set_freq(kv['freq']) + + self.panel = panel + + # FIXME This REALLY needs to be replaced with a hand-crafted button + # that sends both button down and button up events + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((10,0), 1) + self.status_msg = wx.StaticText(panel, -1, "Press Space Bar to Transmit") + of = self.status_msg.GetFont() + self.status_msg.SetFont(wx.Font(15, of.GetFamily(), of.GetStyle(), of.GetWeight())) + hbox.Add(self.status_msg, 0, wx.ALIGN_CENTER) + hbox.Add((10,0), 1) + vbox.Add(hbox, 0, wx.EXPAND | wx.ALIGN_CENTER) + + panel.Bind(wx.EVT_KEY_DOWN, self._on_key_down) + panel.Bind(wx.EVT_KEY_UP, self._on_key_up) + panel.Bind(wx.EVT_KILL_FOCUS, self._on_kill_focus) + panel.SetFocus() + + if 1 and not(no_gui): + rx_fft = fftsink.fft_sink_c (self, panel, title="Rx Input", fft_size=512, + sample_rate=self.rxpath.if_rate, + ref_level=80, y_per_div=20) + self.connect (self.rxpath.u, rx_fft) + vbox.Add (rx_fft.win, 1, wx.EXPAND) + + if 1 and not(no_gui): + rx_fft = fftsink.fft_sink_c (self, panel, title="Post s/w DDC", + fft_size=512, sample_rate=self.rxpath.quad_rate, + ref_level=80, y_per_div=20) + self.connect (self.rxpath.ddc, rx_fft) + vbox.Add (rx_fft.win, 1, wx.EXPAND) + + if 0 and not(no_gui): + foo = scopesink.scope_sink_f (self, panel, title="Squelch", + sample_rate=32000) + self.connect (self.rxpath.fmrx.div, (foo,0)) + self.connect (self.rxpath.fmrx.gate, (foo,1)) + self.connect (self.rxpath.fmrx.squelch_lpf, (foo,2)) + vbox.Add (foo.win, 1, wx.EXPAND) + + if 0 and not(no_gui): + tx_fft = fftsink.fft_sink_c (self, panel, title="Tx Output", + fft_size=512, sample_rate=self.txpath.usrp_rate) + self.connect (self.txpath.amp, tx_fft) + vbox.Add (tx_fft.win, 1, wx.EXPAND) + + + # add control area at the bottom + + self.myform = myform = form.form() + + # first row + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0, 0) + myform['freq'] = form.float_field( + parent=panel, sizer=hbox, label="Freq", weight=1, + callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg)) + + hbox.Add((5,0), 0, 0) + vbox.Add(hbox, 0, wx.EXPAND) + + + # second row + hbox = wx.BoxSizer(wx.HORIZONTAL) + myform['volume'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Volume", + weight=3, range=self.rxpath.volume_range(), + callback=self.set_volume) + hbox.Add((5,0), 0) + myform['squelch'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Squelch", + weight=3, range=self.rxpath.squelch_range(), + callback=self.set_squelch) + hbox.Add((5,0), 0) + myform['rx_gain'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Rx Gain", + weight=3, range=self.rxpath.subdev.gain_range(), + callback=self.set_rx_gain) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + + self._build_subpanel(vbox) + + def _build_subpanel(self, vbox_arg): + # build a secondary information panel (sometimes hidden) + + # FIXME figure out how to have this be a subpanel that is always + # created, but has its visibility controlled by foo.Show(True/False) + + #if not(self.show_debug_info): + # return + + panel = self.panel + vbox = vbox_arg + myform = self.myform + + #panel = wx.Panel(self.panel, -1) + #vbox = wx.BoxSizer(wx.VERTICAL) + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + #myform['decim'] = form.static_float_field( + # parent=panel, sizer=hbox, label="Decim") + + #hbox.Add((5,0), 1) + #myform['fs@usb'] = form.static_float_field( + # parent=panel, sizer=hbox, label="Fs@USB") + + #hbox.Add((5,0), 1) + #myform['dbname'] = form.static_text_field( + # parent=panel, sizer=hbox) + + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + + def _set_status_msg(self, msg, which=0): + self.frame.GetStatusBar().SetStatusText(msg, which) + + def _on_key_down(self, evt): + # print "key_down:", evt.m_keyCode + if evt.m_keyCode == wx.WXK_SPACE and not(self.space_bar_pressed): + self.space_bar_pressed = True + self.set_transmit(True) + + def _on_key_up(self, evt): + # print "key_up", evt.m_keyCode + if evt.m_keyCode == wx.WXK_SPACE: + self.space_bar_pressed = False + self.set_transmit(False) + + def _on_kill_focus(self, evt): + # if we lose the keyboard focus, turn off the transmitter + self.space_bar_pressed = False + self.set_transmit(False) + + +# //////////////////////////////////////////////////////////////////////// +# Transmit Path +# //////////////////////////////////////////////////////////////////////// + +class transmit_path(gr.hier_block): + def __init__(self, fg, subdev_spec, audio_input): + + self.u = usrp.sink_c () + + dac_rate = self.u.dac_rate(); + self.if_rate = 320e3 # 320 kS/s + self.usrp_interp = int(dac_rate // self.if_rate) + self.u.set_interp_rate(self.usrp_interp) + self.sw_interp = 10 + self.audio_rate = self.if_rate // self.sw_interp # 32 kS/s + + self.audio_gain = 10 + self.normal_gain = 32000 + + self.audio = audio.source(int(self.audio_rate), audio_input) + self.audio_amp = gr.multiply_const_ff(self.audio_gain) + + lpf = gr.firdes.low_pass (1, # gain + self.audio_rate, # sampling rate + 3800, # low pass cutoff freq + 300, # width of trans. band + gr.firdes.WIN_HANN) # filter type + + hpf = gr.firdes.high_pass (1, # gain + self.audio_rate, # sampling rate + 325, # low pass cutoff freq + 50, # width of trans. band + gr.firdes.WIN_HANN) # filter type + + audio_taps = convolve(array(lpf),array(hpf)) + self.audio_filt = gr.fir_filter_fff(1,audio_taps) + + self.pl = blks.ctcss_gen_f(fg, self.audio_rate,123.0) + self.add_pl = gr.add_ff() + fg.connect(self.pl,(self.add_pl,1)) + + self.fmtx = blks.nbfm_tx(fg, self.audio_rate, self.if_rate) + self.amp = gr.multiply_const_cc (self.normal_gain) + + # determine the daughterboard subdevice we're using + if subdev_spec is None: + subdev_spec = usrp.pick_tx_subdevice(self.u) + self.u.set_mux(usrp.determine_tx_mux_value(self.u, subdev_spec)) + self.subdev = usrp.selected_subdev(self.u, subdev_spec) + print "TX using", self.subdev.name() + + fg.connect(self.audio, self.audio_amp, self.audio_filt, + (self.add_pl,0), self.fmtx, self.amp, self.u) + + gr.hier_block.__init__(self, fg, None, None) + + self.set_gain(self.subdev.gain_range()[1]) # set max Tx gain + + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + + 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 up converter. Finally, we feed + any residual_freq to the s/w freq translater. + """ + r = self.u.tune(self.subdev._which, self.subdev, target_freq) + if r: + # Use residual_freq in s/w freq translator + return True + + return False + + def set_gain(self, gain): + self.gain = gain + self.subdev.set_gain(gain) + + def set_enable(self, enable): + self.subdev.set_enable(enable) # set H/W Tx enable + if enable: + self.amp.set_k (self.normal_gain) + else: + self.amp.set_k (0) + + + +# //////////////////////////////////////////////////////////////////////// +# Receive Path +# //////////////////////////////////////////////////////////////////////// + +class receive_path(gr.hier_block): + def __init__(self, fg, subdev_spec, gain, audio_output): + + self.u = usrp.source_c () + adc_rate = self.u.adc_rate() + + self.if_rate = 256e3 # 256 kS/s + usrp_decim = int(adc_rate // self.if_rate) + if_decim = 4 + self.u.set_decim_rate(usrp_decim) + self.quad_rate = self.if_rate // if_decim # 64 kS/s + audio_decim = 2 + audio_rate = self.quad_rate // audio_decim # 32 kS/s + + if subdev_spec is None: + subdev_spec = usrp.pick_rx_subdevice(self.u) + self.subdev = usrp.selected_subdev(self.u, subdev_spec) + print "RX using", self.subdev.name() + + self.u.set_mux(usrp.determine_rx_mux_value(self.u, subdev_spec)) + + # Create filter to get actual channel we want + chan_coeffs = gr.firdes.low_pass (1.0, # gain + self.if_rate, # sampling rate + 13e3, # low pass cutoff freq + 4e3, # width of trans. band + gr.firdes.WIN_HANN) # filter type + + print "len(rx_chan_coeffs) =", len(chan_coeffs) + + # Decimating Channel filter with frequency translation + # complex in and out, float taps + self.ddc = gr.freq_xlating_fir_filter_ccf(if_decim, # decimation rate + chan_coeffs, # taps + 0, # frequency translation amount + self.if_rate) # input sample rate + + # instantiate the guts of the single channel receiver + self.fmrx = blks.nbfm_rx(fg, audio_rate, self.quad_rate) + + # standard squelch block + self.squelch = blks.standard_squelch(fg, audio_rate) + + # audio gain / mute block + self._audio_gain = gr.multiply_const_ff(1.0) + + # sound card as final sink + audio_sink = audio.sink (int(audio_rate), audio_output) + + # now wire it all together + fg.connect (self.u, self.ddc, self.fmrx, self.squelch, self._audio_gain, audio_sink) + gr.hier_block.__init__(self, fg, self.u, audio_sink) + + if gain is None: + # if no gain was specified, use the mid-point in dB + g = self.subdev.gain_range() + gain = float(g[0]+g[1])/2 + + self.enabled = True + self.set_gain(gain) + v = self.volume_range() + self.set_volume((v[0]+v[1])/2) + s = self.squelch_range() + self.set_squelch((s[0]+s[1])/2) + + + def volume_range(self): + return (-20.0, 0.0, 0.5) + + def set_volume (self, vol): + g = self.volume_range() + self.volume = max(g[0], min(g[1], vol)) + self._update_audio_gain() + + def set_enable(self, enable): + self.enabled = enable + self._update_audio_gain() + + def _update_audio_gain(self): + if self.enabled: + self._audio_gain.set_k(10**(self.volume/10)) + else: + self._audio_gain.set_k(0) + + def squelch_range(self): + return self.squelch.squelch_range() + + def set_squelch(self, threshold): + print "SQL =", threshold + self.squelch.set_threshold(threshold) + + def threshold(self): + return self.squelch.threshold() + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + + 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 in the + FPGA. Finally, we feed any residual_freq to the s/w freq + translator. + """ + r = self.u.tune(0, self.subdev, target_freq) + if r: + # Use residual_freq in s/w freq translater + # print "residual_freq =", r.residual_freq + self.ddc.set_center_freq(-r.residual_freq) + return True + + return False + + def set_gain(self, gain): + self.gain = gain + self.subdev.set_gain(gain) + + +# //////////////////////////////////////////////////////////////////////// +# Main +# //////////////////////////////////////////////////////////////////////// + +def main(): + app = stdgui.stdapp(ptt_graph, "NBFM Push to Talk") + app.MainLoop() + +if __name__ == '__main__': + main() diff --git a/gnuradio-examples/python/usrp/usrp_nbfm_rcv.py b/gnuradio-examples/python/usrp/usrp_nbfm_rcv.py new file mode 100755 index 000000000..018a5b76e --- /dev/null +++ b/gnuradio-examples/python/usrp/usrp_nbfm_rcv.py @@ -0,0 +1,362 @@ +#!/usr/bin/env python + +from gnuradio import gr, gru, eng_notation, optfir +from gnuradio import audio +from gnuradio import usrp +from gnuradio import blks +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import slider, powermate +from gnuradio.wxgui import stdgui, fftsink, form +from optparse import OptionParser +import usrp_dbid +import sys +import math +import wx + + +#//////////////////////////////////////////////////////////////////////// +# Control Stuff +#//////////////////////////////////////////////////////////////////////// + +class my_graph (stdgui.gui_flow_graph): + def __init__(self,frame,panel,vbox,argv): + stdgui.gui_flow_graph.__init__ (self,frame,panel,vbox,argv) + + parser=OptionParser(option_class=eng_option) + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, + help="select USRP Rx side A or B (default=A)") + parser.add_option("-f", "--freq", type="eng_float", default=146.585e6, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=None, + help="set gain in dB (default is midpoint)") + parser.add_option("-V", "--volume", type="eng_float", default=None, + help="set volume (default is midpoint)") + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm device name. E.g., hw:0,0 or surround51 or /dev/dsp") + parser.add_option("-N", "--no-gui", action="store_true", default=False) + + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if options.freq < 1e6: + options.freq *= 1e6 + + self.frame = frame + self.panel = panel + + self.state = "FREQ" + self.freq = 0 + self.freq_step = 25e3 + + self.rxpath = receive_path(self, options.rx_subdev_spec, options.gain, options.audio_output) + + self._build_gui(vbox, options.no_gui) + + # set initial values + + if options.volume is not None: + self.set_volume(options.volume) + + if not(self.set_freq(options.freq)): + self._set_status_msg("Failed to set initial frequency") + + self.set_gain(self.rxpath.gain) # update gui + self.set_volume(self.rxpath.volume) # update gui + self.set_squelch(self.rxpath.threshold()) # update gui + + + def _set_status_msg(self, msg, which=0): + self.frame.GetStatusBar().SetStatusText(msg, which) + + + def _build_gui(self, vbox, no_gui): + + def _form_set_freq(kv): + return self.set_freq(kv['freq']) + + + self.src_fft = None + if 1 and not(no_gui): + self.src_fft = fftsink.fft_sink_c (self, self.panel, title="Data from USRP", + fft_size=512, sample_rate=self.rxpath.if_rate, + ref_level=80, y_per_div=20) + self.connect (self.rxpath.u, self.src_fft) + vbox.Add (self.src_fft.win, 4, wx.EXPAND) + + if 1 and not(no_gui): + rx_fft = fftsink.fft_sink_c (self, self.panel, title="Post s/w DDC", + fft_size=512, sample_rate=self.rxpath.quad_rate, + ref_level=80, y_per_div=20) + self.connect (self.rxpath.ddc, rx_fft) + vbox.Add (rx_fft.win, 4, wx.EXPAND) + + if 1 and not(no_gui): + post_deemph_fft = fftsink.fft_sink_f (self, self.panel, title="Post Deemph", + fft_size=512, sample_rate=self.rxpath.audio_rate, + y_per_div=10, ref_level=-40) + self.connect (self.rxpath.fmrx.deemph, post_deemph_fft) + vbox.Add (post_deemph_fft.win, 4, wx.EXPAND) + + if 0: + post_filt_fft = fftsink.fft_sink_f (self, self.panel, title="Post Filter", + fft_size=512, sample_rate=audio_rate, + y_per_div=10, ref_level=-40) + self.connect (self.guts.audio_filter, post_filt) + vbox.Add (fft_win4, 4, wx.EXPAND) + + + # control area form at bottom + self.myform = myform = form.form() + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + myform['freq'] = form.float_field( + parent=self.panel, sizer=hbox, label="Freq", weight=1, + callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg)) + + #hbox.Add((5,0), 0) + #myform['freq_slider'] = \ + # form.quantized_slider_field(parent=self.panel, sizer=hbox, weight=3, + # range=(87.9e6, 108.1e6, 0.1e6), + # callback=self.set_freq) + + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + + myform['volume'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Volume", + weight=3, range=self.volume_range(), + callback=self.set_volume) + hbox.Add((5,0), 0) + myform['squelch'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Squelch", + weight=3, range=self.rxpath.squelch_range(), + callback=self.set_squelch) + hbox.Add((5,0), 0) + myform['gain'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Gain", + weight=3, range=self.rxpath.subdev.gain_range(), + callback=self.set_gain) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + try: + self.knob = powermate.powermate(self.frame) + self.rot = 0 + powermate.EVT_POWERMATE_ROTATE (self.frame, self.on_rotate) + powermate.EVT_POWERMATE_BUTTON (self.frame, self.on_button) + except: + print "FYI: No Powermate or Contour Knob found" + + + def on_rotate (self, event): + self.rot += event.delta + if (self.state == "FREQ"): + if self.rot >= 3: + self.set_freq(self.freq + self.freq_step) + self.rot -= 3 + elif self.rot <=-3: + self.set_freq(self.freq - self.freq_step) + self.rot += 3 + else: + step = self.volume_range()[2] + if self.rot >= 3: + self.set_volume(self.rxpath.volume + step) + self.rot -= 3 + elif self.rot <=-3: + self.set_volume(self.rxpath.volume - step) + self.rot += 3 + + def on_button (self, event): + if event.value == 0: # button up + return + self.rot = 0 + if self.state == "FREQ": + self.state = "VOL" + else: + self.state = "FREQ" + self.update_status_bar () + + + def set_squelch(self, threshold_in_db): + self.rxpath.set_squelch(threshold_in_db) + self.myform['squelch'].set_value(self.rxpath.threshold()) + + def set_volume (self, vol): + self.rxpath.set_volume(vol) + self.myform['volume'].set_value(self.rxpath.volume) + self.update_status_bar () + + def set_freq(self, target_freq): + r = self.rxpath.set_freq(target_freq) + if r: + self.freq = target_freq + self.myform['freq'].set_value(target_freq) # update displayed value + #self.myform['freq_slider'].set_value(target_freq) # update displayed value + self.update_status_bar() + self._set_status_msg("OK", 0) + return True + + self._set_status_msg("Failed", 0) + return False + + def set_gain(self, gain): + self.myform['gain'].set_value(gain) # update displayed value + self.rxpath.set_gain(gain) + + def update_status_bar (self): + msg = "Volume:%r Setting:%s" % (self.rxpath.volume, self.state) + self._set_status_msg(msg, 1) + if self.src_fft: + self.src_fft.set_baseband_freq(self.freq) + + def volume_range(self): + return (-20.0, 0.0, 0.5) + + +#//////////////////////////////////////////////////////////////////////// +# Receive Path +#//////////////////////////////////////////////////////////////////////// + +USE_SIMPLE_SQUELCH = False + +class receive_path(gr.hier_block): + def __init__(self, fg, subdev_spec, gain, audio_output): + + self.u = usrp.source_c () + adc_rate = self.u.adc_rate() + + self.if_rate = 256e3 # 256 kS/s + usrp_decim = int(adc_rate // self.if_rate) + if_decim = 4 + self.u.set_decim_rate(usrp_decim) + self.quad_rate = self.if_rate // if_decim # 64 kS/s + audio_decim = 2 + self.audio_rate = self.quad_rate // audio_decim # 32 kS/s + + + if subdev_spec is None: + subdev_spec = usrp.pick_rx_subdevice(self.u) + self.subdev = usrp.selected_subdev(self.u, subdev_spec) + print "Using RX d'board %s" % (self.subdev.side_and_name(),) + + self.u.set_mux(usrp.determine_rx_mux_value(self.u, subdev_spec)) + + # Create filter to get actual channel we want + chan_coeffs = gr.firdes.low_pass (1.0, # gain + self.if_rate, # sampling rate + 13e3, # low pass cutoff freq + 4e3, # width of trans. band + gr.firdes.WIN_HANN) # filter type + + print "len(rx_chan_coeffs) =", len(chan_coeffs) + + # Decimating Channel filter with frequency translation + # complex in and out, float taps + self.ddc = gr.freq_xlating_fir_filter_ccf(if_decim, # decimation rate + chan_coeffs, # taps + 0, # frequency translation amount + self.if_rate) # input sample rate + + if USE_SIMPLE_SQUELCH: + self.squelch = gr.simple_squelch_cc(20) + else: + self.squelch = blks.standard_squelch(fg, self.audio_rate) + + # instantiate the guts of the single channel receiver + self.fmrx = blks.nbfm_rx(fg, self.audio_rate, self.quad_rate) + + # audio gain / mute block + self._audio_gain = gr.multiply_const_ff(1.0) + + # sound card as final sink + audio_sink = audio.sink (int(self.audio_rate), audio_output) + + # now wire it all together + if USE_SIMPLE_SQUELCH: + fg.connect (self.u, self.ddc, self.squelch, self.fmrx, + self._audio_gain, audio_sink) + else: + fg.connect (self.u, self.ddc, self.fmrx, self.squelch, + self._audio_gain, audio_sink) + + gr.hier_block.__init__(self, fg, self.u, audio_sink) + + if gain is None: + # if no gain was specified, use the mid-point in dB + g = self.subdev.gain_range() + gain = float(g[0]+g[1])/2 + + self.set_gain(gain) + + v = self.volume_range() + self.set_volume((v[0]+v[1])/2) + s = self.squelch_range() + self.set_squelch((s[0]+s[1])/2) + + def volume_range(self): + return (-20.0, 0.0, 0.5) + + def set_volume (self, vol): + g = self.volume_range() + self.volume = max(g[0], min(g[1], vol)) + self._update_audio_gain() + + def _update_audio_gain(self): + self._audio_gain.set_k(10**(self.volume/10)) + + def squelch_range(self): + r = self.squelch.squelch_range() + #print "squelch_range: ", r + return r + + def set_squelch(self, threshold): + #print "SQL =", threshold + self.squelch.set_threshold(threshold) + + def threshold(self): + t = self.squelch.threshold() + #print "t =", t + return t + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + + 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 in the + FPGA. Finally, we feed any residual_freq to the s/w freq + translator. + """ + + r = usrp.tune(self.u, 0, self.subdev, target_freq) + if r: + # Use residual_freq in s/w freq translater + # print "residual_freq =", r.residual_freq + self.ddc.set_center_freq(-r.residual_freq) + return True + + return False + + def set_gain(self, gain): + self.gain = gain + self.subdev.set_gain(gain) + + +# //////////////////////////////////////////////////////////////////////// +# Main +# //////////////////////////////////////////////////////////////////////// + +if __name__ == '__main__': + app = stdgui.stdapp (my_graph, "USRP NBFM RX") + app.MainLoop () diff --git a/gnuradio-examples/python/usrp/usrp_oscope.py b/gnuradio-examples/python/usrp/usrp_oscope.py new file mode 100755 index 000000000..f4469f996 --- /dev/null +++ b/gnuradio-examples/python/usrp/usrp_oscope.py @@ -0,0 +1,252 @@ +#!/usr/bin/env python +# +# 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. +# + +# print "Loading revised usrp_oscope with additional options for scopesink..." + +from gnuradio import gr, gru +from gnuradio import usrp +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import stdgui, fftsink, waterfallsink, scopesink, form, slider +from optparse import OptionParser +import wx +import sys + + +def pick_subdevice(u): + """ + The user didn't specify a subdevice on the command line. + If there's a daughterboard on A, select A. + If there's a daughterboard on B, select B. + Otherwise, select A. + """ + if u.db[0][0].dbid() >= 0: # dbid is < 0 if there's no d'board or a problem + return (0, 0) + if u.db[1][0].dbid() >= 0: + return (1, 0) + return (0, 0) + + +class app_flow_graph(stdgui.gui_flow_graph): + def __init__(self, frame, panel, vbox, argv): + stdgui.gui_flow_graph.__init__(self) + + self.frame = frame + self.panel = panel + + parser = OptionParser(option_class=eng_option) + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, + help="select USRP Rx side A or B (default=first one with a daughterboard)") + parser.add_option("-d", "--decim", type="int", default=16, + help="set fgpa decimation rate to DECIM [default=%default]") + parser.add_option("-f", "--freq", type="eng_float", default=None, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=None, + help="set gain in dB (default is midpoint)") + parser.add_option("-8", "--width-8", action="store_true", default=False, + help="Enable 8-bit samples across USB") + parser.add_option("-n", "--frame-decim", type="int", default=1, + help="set oscope frame decimation factor to n [default=1]") + parser.add_option("-v", "--v-scale", type="eng_float", default=1000, + help="set oscope initial V/div to SCALE [default=%default]") + parser.add_option("-t", "--t-scale", type="eng_float", default=49e-6, + help="set oscope initial s/div to SCALE [default=50us]") + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + + self.show_debug_info = True + + # build the graph + + self.u = usrp.source_c(decim_rate=options.decim) + if options.rx_subdev_spec is None: + options.rx_subdev_spec = pick_subdevice(self.u) + self.u.set_mux(usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec)) + + if options.width_8: + width = 8 + shift = 8 + format = self.u.make_format(width, shift) + #print "format =", hex(format) + r = self.u.set_format(format) + #print "set_format =", r + + # determine the daughterboard subdevice we're using + self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec) + + input_rate = self.u.adc_freq() / self.u.decim_rate() + + self.scope = scopesink.scope_sink_c(self, panel, sample_rate=input_rate, + frame_decim=options.frame_decim, + v_scale=options.v_scale, + t_scale=options.t_scale) + self.connect(self.u, self.scope) + + self._build_gui(vbox) + + # set initial values + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + g = self.subdev.gain_range() + options.gain = float(g[0]+g[1])/2 + + if options.freq is None: + # if no freq was specified, use the mid-point + r = self.subdev.freq_range() + options.freq = float(r[0]+r[1])/2 + + self.set_gain(options.gain) + + if self.show_debug_info: + self.myform['decim'].set_value(self.u.decim_rate()) + self.myform['fs@usb'].set_value(self.u.adc_freq() / self.u.decim_rate()) + self.myform['dbname'].set_value(self.subdev.name()) + self.myform['baseband'].set_value(0) + self.myform['ddc'].set_value(0) + + if not(self.set_freq(options.freq)): + self._set_status_msg("Failed to set initial frequency") + + + def _set_status_msg(self, msg): + self.frame.GetStatusBar().SetStatusText(msg, 0) + + def _build_gui(self, vbox): + + def _form_set_freq(kv): + return self.set_freq(kv['freq']) + + vbox.Add(self.scope.win, 10, wx.EXPAND) + + # add control area at the bottom + self.myform = myform = form.form() + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0, 0) + myform['freq'] = form.float_field( + parent=self.panel, sizer=hbox, label="Center freq", weight=1, + callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg)) + + hbox.Add((5,0), 0, 0) + g = self.subdev.gain_range() + myform['gain'] = form.slider_field(parent=self.panel, sizer=hbox, label="Gain", + weight=3, + min=int(g[0]), max=int(g[1]), + callback=self.set_gain) + + hbox.Add((5,0), 0, 0) + vbox.Add(hbox, 0, wx.EXPAND) + + self._build_subpanel(vbox) + + def _build_subpanel(self, vbox_arg): + # build a secondary information panel (sometimes hidden) + + # FIXME figure out how to have this be a subpanel that is always + # created, but has its visibility controlled by foo.Show(True/False) + + def _form_set_decim(kv): + return self.set_decim(kv['decim']) + + if not(self.show_debug_info): + return + + panel = self.panel + vbox = vbox_arg + myform = self.myform + + #panel = wx.Panel(self.panel, -1) + #vbox = wx.BoxSizer(wx.VERTICAL) + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + + myform['decim'] = form.int_field( + parent=panel, sizer=hbox, label="Decim", + callback=myform.check_input_and_call(_form_set_decim, self._set_status_msg)) + + hbox.Add((5,0), 1) + myform['fs@usb'] = form.static_float_field( + parent=panel, sizer=hbox, label="Fs@USB") + + hbox.Add((5,0), 1) + myform['dbname'] = form.static_text_field( + parent=panel, sizer=hbox) + + hbox.Add((5,0), 1) + myform['baseband'] = form.static_float_field( + parent=panel, sizer=hbox, label="Analog BB") + + hbox.Add((5,0), 1) + myform['ddc'] = form.static_float_field( + parent=panel, sizer=hbox, label="DDC") + + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + + 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. + """ + r = usrp.tune(self.u, 0, self.subdev, target_freq) + + if r: + self.myform['freq'].set_value(target_freq) # update displayed value + if self.show_debug_info: + self.myform['baseband'].set_value(r.baseband_freq) + self.myform['ddc'].set_value(r.dxc_freq) + return True + + return False + + def set_gain(self, gain): + self.myform['gain'].set_value(gain) # update displayed value + self.subdev.set_gain(gain) + + def set_decim(self, decim): + ok = self.u.set_decim_rate(decim) + if not ok: + print "set_decim failed" + input_rate = self.u.adc_freq() / self.u.decim_rate() + self.scope.set_sample_rate(input_rate) + if self.show_debug_info: # update displayed values + self.myform['decim'].set_value(self.u.decim_rate()) + self.myform['fs@usb'].set_value(self.u.adc_freq() / self.u.decim_rate()) + return ok + +def main (): + app = stdgui.stdapp(app_flow_graph, "USRP O'scope", nstatus=1) + app.MainLoop() + +if __name__ == '__main__': + main () diff --git a/gnuradio-examples/python/usrp/usrp_rx_cfile.py b/gnuradio-examples/python/usrp/usrp_rx_cfile.py new file mode 100755 index 000000000..306e101d3 --- /dev/null +++ b/gnuradio-examples/python/usrp/usrp_rx_cfile.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python + +""" +Read samples from the USRP and write to file formatted as binary +outputs single precision complex float values or complex short values (interleaved 16 bit signed short integers). + +""" + +from gnuradio import gr, eng_notation +from gnuradio import audio +from gnuradio import usrp +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +class my_graph(gr.flow_graph): + + def __init__(self): + gr.flow_graph.__init__(self) + + usage="%prog: [options] output_filename" + parser = OptionParser(option_class=eng_option, usage=usage) + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=(0, 0), + help="select USRP Rx side A or B (default=A)") + parser.add_option("-d", "--decim", type="int", default=16, + help="set fgpa decimation rate to DECIM [default=%default]") + parser.add_option("-f", "--freq", type="eng_float", default=None, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=None, + help="set gain in dB (default is midpoint)") + parser.add_option("-8", "--width-8", action="store_true", default=False, + help="Enable 8-bit samples across USB") + parser.add_option( "--no-hb", action="store_true", default=False, + help="don't use halfband filter in usrp") + parser.add_option( "-s","--output-shorts", action="store_true", default=False, + help="output interleaved shorts in stead of complex floats") + parser.add_option("-N", "--nsamples", type="eng_float", default=None, + help="number of samples to collect [default=+inf]") + (options, args) = parser.parse_args () + if len(args) != 1: + parser.print_help() + raise SystemExit, 1 + filename = args[0] + + if options.freq is None: + parser.print_help() + sys.stderr.write('You must specify the frequency with -f FREQ\n'); + raise SystemExit, 1 + + # build the graph + if options.no_hb or (options.decim<8): + self.fpga_filename="std_4rx_0tx.rbf" #Min decimation of this firmware is 4. contains 4 Rx paths without halfbands and 0 tx paths. + if options.output_shorts: + self.u = usrp.source_s(decim_rate=options.decim,fpga_filename=self.fpga_filename) + else: + self.u = usrp.source_c(decim_rate=options.decim,fpga_filename=self.fpga_filename) + else: + #standard fpga firmware "std_2rxhb_2tx.rbf" contains 2 Rx paths with halfband filters and 2 tx paths (the default) min decimation 8 + if options.output_shorts: + self.u = usrp.source_s(decim_rate=options.decim) + else: + self.u = usrp.source_c(decim_rate=options.decim) + if options.width_8: + sample_width = 8 + sample_shift = 8 + format = self.u.make_format(sample_width, sample_shift) + r = self.u.set_format(format) + if options.output_shorts: + self.dst = gr.file_sink(gr.sizeof_short, filename) + else: + self.dst = gr.file_sink(gr.sizeof_gr_complex, filename) + if options.nsamples is None: + self.connect(self.u, self.dst) + else: + if options.output_shorts: + self.head = gr.head(gr.sizeof_short, int(options.nsamples)*2) + else: + self.head = gr.head(gr.sizeof_gr_complex, int(options.nsamples)) + self.connect(self.u, self.head, self.dst) + + if options.rx_subdev_spec is None: + options.rx_subdev_spec = usrp.pick_rx_subdevice(self.u) + self.u.set_mux(usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec)) + + # determine the daughterboard subdevice we're using + self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec) + print "Using RX d'board %s" % (self.subdev.side_and_name(),) + input_rate = self.u.adc_freq() / self.u.decim_rate() + print "USB sample rate %s" % (eng_notation.num_to_str(input_rate)) + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + g = self.subdev.gain_range() + options.gain = float(g[0]+g[1])/2 + + self.subdev.set_gain(options.gain) + + r = self.u.tune(0, self.subdev, options.freq) + if not r: + sys.stderr.write('Failed to set frequency\n') + raise SystemExit, 1 + + +if __name__ == '__main__': + try: + my_graph().run() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/usrp/usrp_rx_nogui.py b/gnuradio-examples/python/usrp/usrp_rx_nogui.py new file mode 100755 index 000000000..b33d626e2 --- /dev/null +++ b/gnuradio-examples/python/usrp/usrp_rx_nogui.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python + +from gnuradio import gr, gru, usrp, optfir, audio, eng_notation, blks +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +""" +This example application demonstrates receiving and demodulating +different types of signals using the USRP. + +A receive chain is built up of the following signal processing +blocks: + +USRP - Daughter board source generating complex baseband signal. +CHAN - Low pass filter to select channel bandwidth +RFSQL - RF squelch zeroing output when input power below threshold +AGC - Automatic gain control leveling signal at [-1.0, +1.0] +DEMOD - Demodulation block appropriate to selected signal type. + This converts the complex baseband to real audio frequencies, + and applies an appropriate low pass decimating filter. +CTCSS - Optional tone squelch zeroing output when tone is not present. +RSAMP - Resampler block to convert audio sample rate to user specified + sound card output rate. +AUDIO - Audio sink for playing final output to speakers. + +The following are required command line parameters: + +-f FREQ USRP receive frequency +-m MOD Modulation type, select from AM, FM, or WFM + +The following are optional command line parameters: + +-R SUBDEV Daughter board specification, defaults to first found +-c FREQ Calibration offset. Gets added to receive frequency. + Defaults to 0.0 Hz. +-g GAIN Daughterboard gain setting. Defaults to mid-range. +-o RATE Sound card output rate. Defaults to 32000. Useful if + your sound card only accepts particular sample rates. +-r RFSQL RF squelch in db. Defaults to -50.0. +-p FREQ CTCSS frequency. Opens squelch when tone is present. + +Once the program is running, ctrl-break (Ctrl-C) stops operation. + +Please see fm_demod.py and am_demod.py for details of the demodulation +blocks. +""" + +# (usrp_decim, channel_decim, audio_decim, channel_pass, channel_stop, demod) +demod_params = { + 'AM' : (250, 16, 1, 5000, 8000, blks.demod_10k0a3e_cf), + 'FM' : (250, 8, 4, 8000, 9000, blks.demod_20k0f3e_cf), + 'WFM' : (250, 1, 8, 90000, 100000, blks.demod_200kf3e_cf) + } + +class usrp_source_c(gr.hier_block): + """ + Create a USRP source object supplying complex floats. + + Selects user supplied subdevice or chooses first available one. + + Calibration value is the offset from the tuned frequency to + the actual frequency. + """ + def __init__(self, fg, subdev_spec, decim, gain=None, calibration=0.0): + self._decim = decim + self._src = usrp.source_c() + if subdev_spec is None: + subdev_spec = usrp.pick_rx_subdevice(self._src) + self._subdev = usrp.selected_subdev(self._src, subdev_spec) + self._src.set_mux(usrp.determine_rx_mux_value(self._src, subdev_spec)) + self._src.set_decim_rate(self._decim) + + # If no gain specified, set to midrange + if gain is None: + g = self._subdev.gain_range() + gain = (g[0]+g[1])/2.0 + + self._subdev.set_gain(gain) + self._cal = calibration + gr.hier_block.__init__(self, fg, self._src, self._src) + + def tune(self, freq): + result = usrp.tune(self._src, 0, self._subdev, freq+self._cal) + # TODO: deal with residual + + def rate(self): + return self._src.adc_rate()/self._decim + +class app_flow_graph(gr.flow_graph): + def __init__(self, options, args): + gr.flow_graph.__init__(self) + self.options = options + self.args = args + + (usrp_decim, channel_decim, audio_decim, + channel_pass, channel_stop, demod) = demod_params[options.modulation] + + USRP = usrp_source_c(self, # Flow graph + options.rx_subdev_spec, # Daugherboard spec + usrp_decim, # IF decimation ratio + options.gain, # Receiver gain + options.calibration) # Frequency offset + USRP.tune(options.frequency) + + if_rate = USRP.rate() + channel_rate = if_rate // channel_decim + audio_rate = channel_rate // audio_decim + + CHAN_taps = optfir.low_pass(1.0, # Filter gain + if_rate, # Sample rate + channel_pass, # One sided modulation bandwidth + channel_stop, # One sided channel bandwidth + 0.1, # Passband ripple + 60) # Stopband attenuation + + CHAN = gr.freq_xlating_fir_filter_ccf(channel_decim, # Decimation rate + CHAN_taps, # Filter taps + 0.0, # Offset frequency + if_rate) # Sample rate + + RFSQL = gr.pwr_squelch_cc(options.rf_squelch, # Power threshold + 125.0/channel_rate, # Time constant + channel_rate/20, # 50ms rise/fall + False) # Zero, not gate output + + AGC = gr.agc_cc(1.0/channel_rate, # Time constant + 1.0, # Reference power + 1.0, # Initial gain + 1.0) # Maximum gain + + DEMOD = demod(self, channel_rate, audio_decim) + + # From RF to audio + self.connect(USRP, CHAN, RFSQL, AGC, DEMOD) + + # Optionally add CTCSS and RSAMP if needed + tail = DEMOD + if options.ctcss != None and options.ctcss > 60.0: + CTCSS = gr.ctcss_squelch_ff(audio_rate, # Sample rate + options.ctcss) # Squelch tone + self.connect(DEMOD, CTCSS) + tail = CTCSS + + if options.output_rate != audio_rate: + out_lcm = gru.lcm(audio_rate, options.output_rate) + out_interp = int(out_lcm // audio_rate) + out_decim = int(out_lcm // options.output_rate) + RSAMP = blks.rational_resampler_fff(self, out_interp, out_decim) + self.connect(tail, RSAMP) + tail = RSAMP + + # Send to default audio output + AUDIO = audio.sink(options.output_rate, "") + self.connect(tail, AUDIO) + +def main(): + parser = OptionParser(option_class=eng_option) + parser.add_option("-f", "--frequency", type="eng_float", + help="set receive frequency to Hz", metavar="Hz") + parser.add_option("-R", "--rx-subdev-spec", type="subdev", + help="select USRP Rx side A or B", metavar="SUBDEV") + parser.add_option("-c", "--calibration", type="eng_float", default=0.0, + help="set frequency offset to Hz", metavar="Hz") + parser.add_option("-g", "--gain", type="int", default=None, + help="set RF gain", metavar="dB") + parser.add_option("-m", "--modulation", type="choice", choices=('AM','FM','WFM'), + help="set modulation type (AM,FM)", metavar="TYPE") + parser.add_option("-o", "--output-rate", type="int", default=32000, + help="set audio output rate to RATE", metavar="RATE") + parser.add_option("-r", "--rf-squelch", type="eng_float", default=-50.0, + help="set RF squelch to dB", metavar="dB") + parser.add_option("-p", "--ctcss", type="float", + help="set CTCSS squelch to FREQ", metavar="FREQ") + (options, args) = parser.parse_args() + + if options.frequency < 1e6: + options.frequency *= 1e6 + + fg = app_flow_graph(options, args) + try: + fg.run() + except KeyboardInterrupt: + pass + +if __name__ == "__main__": + main() diff --git a/gnuradio-examples/python/usrp/usrp_siggen.py b/gnuradio-examples/python/usrp/usrp_siggen.py new file mode 100755 index 000000000..59e01e0a9 --- /dev/null +++ b/gnuradio-examples/python/usrp/usrp_siggen.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python + +from gnuradio import gr, gru +from gnuradio import usrp +from gnuradio.eng_option import eng_option +from gnuradio import eng_notation +from optparse import OptionParser +import sys + + +class my_graph(gr.flow_graph): + def __init__ (self): + gr.flow_graph.__init__(self) + + # controllable values + self.interp = 64 + self.waveform_type = gr.GR_SIN_WAVE + self.waveform_ampl = 16000 + self.waveform_freq = 100.12345e3 + self.waveform_offset = 0 + self._instantiate_blocks () + self.set_waveform_type (self.waveform_type) + + def usb_freq (self): + return self.u.dac_freq() / self.interp + + def usb_throughput (self): + return self.usb_freq () * 4 + + def set_waveform_type (self, type): + ''' + valid waveform types are: gr.GR_SIN_WAVE, gr.GR_CONST_WAVE, + gr.GR_UNIFORM and gr.GR_GAUSSIAN + ''' + self._configure_graph (type) + self.waveform_type = type + + def set_waveform_ampl (self, ampl): + self.waveform_ampl = ampl + self.siggen.set_amplitude (ampl) + self.noisegen.set_amplitude (ampl) + + def set_waveform_freq (self, freq): + self.waveform_freq = freq + self.siggen.set_frequency (freq) + + def set_waveform_offset (self, offset): + self.waveform_offset = offset + self.siggen.set_offset (offset) + + def set_interpolator (self, interp): + self.interp = interp + self.siggen.set_sampling_freq (self.usb_freq ()) + self.u.set_interp_rate (interp) + + def _instantiate_blocks (self): + self.src = None + self.u = usrp.sink_c (0, self.interp) + + self.siggen = gr.sig_source_c (self.usb_freq (), + gr.GR_SIN_WAVE, + self.waveform_freq, + self.waveform_ampl, + self.waveform_offset) + + self.noisegen = gr.noise_source_c (gr.GR_UNIFORM, + self.waveform_ampl) + + # self.file_sink = gr.file_sink (gr.sizeof_gr_complex, "siggen.dat") + + def _configure_graph (self, type): + was_running = self.is_running () + if was_running: + self.stop () + self.disconnect_all () + if type == gr.GR_SIN_WAVE or type == gr.GR_CONST_WAVE: + self.connect (self.siggen, self.u) + # self.connect (self.siggen, self.file_sink) + self.siggen.set_waveform (type) + self.src = self.siggen + elif type == gr.GR_UNIFORM or type == gr.GR_GAUSSIAN: + self.connect (self.noisegen, self.u) + self.noisegen.set_type (type) + self.src = self.noisegen + else: + raise ValueError, type + if was_running: + self.start () + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + + 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 up converter. + """ + r = self.u.tune(self.subdev._which, self.subdev, target_freq) + if r: + print "r.baseband_freq =", eng_notation.num_to_str(r.baseband_freq) + print "r.dxc_freq =", eng_notation.num_to_str(r.dxc_freq) + print "r.residual_freq =", eng_notation.num_to_str(r.residual_freq) + print "r.inverted =", r.inverted + return True + + return False + + + +def main (): + parser = OptionParser (option_class=eng_option) + parser.add_option ("-T", "--tx-subdev-spec", type="subdev", default=(0, 0), + help="select USRP Tx side A or B") + parser.add_option ("-f", "--rf-freq", type="eng_float", default=None, + help="set RF center frequency to FREQ") + parser.add_option ("-i", "--interp", type="int", default=64, + help="set fgpa interpolation rate to INTERP") + + parser.add_option ("--sine", dest="type", action="store_const", const=gr.GR_SIN_WAVE, + help="generate a complex sinusoid [default]", default=gr.GR_SIN_WAVE) + parser.add_option ("--const", dest="type", action="store_const", const=gr.GR_CONST_WAVE, + help="generate a constant output") + parser.add_option ("--gaussian", dest="type", action="store_const", const=gr.GR_GAUSSIAN, + help="generate Gaussian random output") + parser.add_option ("--uniform", dest="type", action="store_const", const=gr.GR_UNIFORM, + help="generate Uniform random output") + + parser.add_option ("-w", "--waveform-freq", type="eng_float", default=100e3, + help="set waveform frequency to FREQ") + parser.add_option ("-a", "--amplitude", type="eng_float", default=16e3, + help="set waveform amplitude to AMPLITUDE", metavar="AMPL") + parser.add_option ("-o", "--offset", type="eng_float", default=0, + help="set waveform offset to OFFSET") + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + raise SystemExit + + if options.rf_freq is None: + sys.stderr.write("usrp_siggen: must specify RF center frequency with -f RF_FREQ\n") + parser.print_help() + raise SystemExit + + fg = my_graph() + fg.set_interpolator (options.interp) + fg.set_waveform_type (options.type) + fg.set_waveform_freq (options.waveform_freq) + fg.set_waveform_ampl (options.amplitude) + fg.set_waveform_offset (options.offset) + + # determine the daughterboard subdevice we're using + if options.tx_subdev_spec is None: + options.tx_subdev_spec = usrp.pick_tx_subdevice(fg.u) + + m = usrp.determine_tx_mux_value(fg.u, options.tx_subdev_spec) + #print "mux = %#04x" % (m,) + fg.u.set_mux(m) + fg.subdev = usrp.selected_subdev(fg.u, options.tx_subdev_spec) + print "Using TX d'board %s" % (fg.subdev.side_and_name(),) + + fg.subdev.set_gain(fg.subdev.gain_range()[1]) # set max Tx gain + + if not fg.set_freq(options.rf_freq): + sys.stderr.write('Failed to set RF frequency\n') + raise SystemExit + + fg.subdev.set_enable(True) # enable transmitter + + try: + fg.run() + except KeyboardInterrupt: + pass + +if __name__ == '__main__': + main () diff --git a/gnuradio-examples/python/usrp/usrp_tv_rcv.py b/gnuradio-examples/python/usrp/usrp_tv_rcv.py new file mode 100755 index 000000000..ed78273a7 --- /dev/null +++ b/gnuradio-examples/python/usrp/usrp_tv_rcv.py @@ -0,0 +1,394 @@ +#!/usr/bin/env python +""" +Realtime capture and display of analog Tv stations. +Can also use a file as source or sink +When you use an output file you can show the results frame-by-frame using ImageMagick +When you want to use the realtime sdl display window you must first install gr-video-sdl (is in gnuradio cvs). +When you use a file source, in stead of the usrp, make sure you capture interleaved shorts. +(Use usrp_rx_file.py, or use usrp_rx_cfile.py --output-shorts if you have a recent enough usrp_rx_cfile.py) +There is no synchronisation yet. The sync blocks are in development but not yet in cvs. + +""" +from gnuradio import gr, gru, eng_notation, optfir +try: + from gnuradio import video_sdl +except: + print "FYI: gr-video-sdl is not installed" + print "realtime SDL video output window will not be available" +from gnuradio import usrp +from gnuradio import blks +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import slider, powermate +from gnuradio.wxgui import stdgui, fftsink, form +from optparse import OptionParser +import usrp_dbid +import sys +import math +import wx + +# To debug, insert this in your test code... +#import os +#print 'Blocked waiting for GDB attach (pid = %d)' % (os.getpid(),) +#raw_input ('Press Enter to continue: ') +# remainder of your test code follows... + +def pick_subdevice(u): + """ + The user didn't specify a subdevice on the command line. + Try for one of these, in order: TV_RX, BASIC_RX, whatever is on side A. + + @return a subdev_spec + """ + return usrp.pick_subdev(u, (usrp_dbid.TV_RX, + usrp_dbid.TV_RX_REV_2, + usrp_dbid.BASIC_RX)) + + +class tv_rx_graph (stdgui.gui_flow_graph): + def __init__(self,frame,panel,vbox,argv): + stdgui.gui_flow_graph.__init__ (self,frame,panel,vbox,argv) + + usage="%prog: [options] [input_filename]. \n If you don't specify an input filename the usrp will be used as source\n " \ + "Make sure your input capture file containes interleaved shorts not complex floats" + parser=OptionParser(option_class=eng_option) + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, + help="select USRP Rx side A or B (default=A)") + parser.add_option("-d", "--decim", type="int", default=64, + help="set fgpa decimation rate to DECIM [default=%default]") + parser.add_option("-f", "--freq", type="eng_float", default=519.25e6, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=None, + help="set gain in dB (default is midpoint)") + parser.add_option("-c", "--contrast", type="eng_float", default=1.0, + help="set contrast (default is 1.0)") + parser.add_option("-b", "--brightness", type="eng_float", default=0.0, + help="set brightness (default is 0)") + parser.add_option("-8", "--width-8", action="store_true", default=False, + help="Enable 8-bit samples across USB") + parser.add_option("-p", "--pal", action="store_true", default=False, + help="PAL video format (this is the default)") + parser.add_option("-n", "--ntsc", action="store_true", default=False, + help="NTSC video format") + parser.add_option("-o", "--out-filename", type="string", default="sdl", + help="For example out_raw_uchar.gray. If you don't specify an output filename you will get a video_sink_sdl realtime output window. You then need to have gr-video-sdl installed)") + parser.add_option("-r", "--repeat", action="store_false", default=True, + help="repeat file in a loop") + parser.add_option("-N", "--no-hb", action="store_true", default=False, + help="don't use halfband filter in usrp") + + (options, args) = parser.parse_args() + if not ((len(args) == 1) or (len(args) == 0)): + parser.print_help() + sys.exit(1) + + if len(args) == 1: + filename = args[0] + else: + filename = None + + self.frame = frame + self.panel = panel + + self.contrast = options.contrast + self.brightness = options.brightness + self.state = "FREQ" + self.freq = 0 + + # build graph + + self.u=None + + usrp_decim = options.decim # 32 + + if not (options.out_filename=="sdl"): + options.repeat=False + + if not ((filename is None) or (filename=="usrp")): + self.filesource = gr.file_source(gr.sizeof_short,filename,options.repeat) # file is data source + self.istoc = gr.interleaved_short_to_complex() + self.connect(self.filesource,self.istoc) + adc_rate=64e6 + self.src=self.istoc + options.gain=0.0 + self.gain=0.0 + else: + if options.no_hb or (options.decim<8): + self.fpga_filename="std_4rx_0tx.rbf" #contains 4 Rx paths without halfbands and 0 tx paths + else: + self.fpga_filename="std_2rxhb_2tx.rbf" # contains 2 Rx paths with halfband filters and 2 tx paths (the default) + self.u = usrp.source_c(0,fpga_filename=self.fpga_filename) # usrp is data source + if options.width_8: + sample_width = 8 + sample_shift = 8 + format = self.u.make_format(sample_width, sample_shift) + r = self.u.set_format(format) + adc_rate = self.u.adc_rate() # 64 MS/s + self.u.set_decim_rate(usrp_decim) + if options.rx_subdev_spec is None: + options.rx_subdev_spec = pick_subdevice(self.u) + self.u.set_mux(usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec)) + self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec) + print "Using RX d'board %s" % (self.subdev.side_and_name(),) + if options.gain is None: + # if no gain was specified, use the mid-point in dB + g = self.subdev.gain_range() + options.gain = float(g[0]+g[1])/2 + self.src=self.u + + usrp_rate = adc_rate / usrp_decim # 320 kS/s + + f2uc=gr.float_to_uchar() + # sdl window as final sink + if not (options.pal or options.ntsc): + options.pal=True #set default to PAL + if options.pal: + lines_per_frame=625.0 + frames_per_sec=25.0 + show_width=768 + elif options.ntsc: + lines_per_frame=525.0 + frames_per_sec=29.97002997 + show_width=640 + width=int(usrp_rate/(lines_per_frame*frames_per_sec)) + height=int(lines_per_frame) + + if (options.out_filename=="sdl"): + #Here comes the tv screen, you have to build and install gr-video-sdl for this (subproject of gnuradio, only in cvs for now) + try: + video_sink = video_sdl.sink_uc ( frames_per_sec, width, height,0,show_width,height) + except: + print "gr-video-sdl is not installed" + print "realtime \"sdl\" video output window is not available" + raise SystemExit, 1 + self.dst=video_sink + else: + print "You can use the imagemagick display tool to show the resulting imagesequence" + print "use the following line to show the demodulated TV-signal:" + print "display -depth 8 -size " +str(width)+ "x" + str(height) + " gray:" + options.out_filename + print "(Use the spacebar to advance to next frames)" + options.repeat=False + file_sink=gr.file_sink(gr.sizeof_char, options.out_filename) + self.dst =file_sink + + self.agc=gr.agc_cc(1e-7,1.0,1.0) #1e-7 + self.am_demod = gr.complex_to_mag () + self.set_blacklevel=gr.add_const_ff(0.0) + self.invert_and_scale = gr.multiply_const_ff (0.0) #-self.contrast *128.0*255.0/(200.0) + + # now wire it all together + #sample_rate=options.width*options.height*options.framerate + + process_type='do_no_sync' + if process_type=='do_no_sync': + self.connect (self.src, self.agc,self.am_demod,self.invert_and_scale, self.set_blacklevel,f2uc,self.dst) + elif process_type=='do_tv_sync_adv': + #defaults: gr.tv_sync_adv (double sampling_freq, unsigned int tv_format,bool output_active_video_only=false, bool do_invert=false, double wanted_black_level=0.0, double wanted_white_level=255.0, double avg_alpha=0.1, double initial_gain=1.0, double initial_offset=0.0,bool debug=false) + self.tv_sync_adv=gr.tv_sync_adv(usrp_rate,0,False,False,0.0,255.0,0.01,1.0,0.0,False) #note, this block is not yet in cvs + self.connect (self.src, self.am_demod,self.invert_and_scale,self.tv_sync_adv,s2f,f2uc,self.dst) + elif process_type=='do_nullsink': + #self.connect (self.src, self.am_demod,self.invert_and_scale,f2uc,video_sink) + c2r=gr.complex_to_real() + nullsink=gr.null_sink(gr.sizeof_float) + self.connect (self.src, c2r,nullsink) #video_sink) + elif process_type=='do_tv_sync_corr': + frame_size=width*height #int(usrp_rate/25.0) + nframes=10# 32 + search_window=20*nframes + debug=False + video_alpha=0.3 #0.1 + corr_alpha=0.3 + tv_corr=gr.tv_correlator_ff(frame_size,nframes, search_window, video_alpha, corr_alpha,debug) #Note: this block is not yet in cvs + shift=gr.add_const_ff(-0.7) + self.connect (self.src, self.agc,self.am_demod,tv_corr,self.invert_and_scale, self.set_blacklevel,f2uc,self.dst) #self.agc, + else: # process_type=='do_test_image': + src_vertical_bars = gr.sig_source_f (usrp_rate, gr.GR_SIN_WAVE, 10.0 *usrp_rate/320, 255,128) + self.connect(src_vertical_bars,f2uc,self.dst) + + self._build_gui(vbox, usrp_rate, usrp_rate, usrp_rate) + + + if abs(options.freq) < 1e6: + options.freq *= 1e6 + + # set initial values + self.set_gain(options.gain) + self.set_contrast(self.contrast) + self.set_brightness(options.brightness) + if not(self.set_freq(options.freq)): + self._set_status_msg("Failed to set initial frequency") + + + def _set_status_msg(self, msg, which=0): + self.frame.GetStatusBar().SetStatusText(msg, which) + + + def _build_gui(self, vbox, usrp_rate, demod_rate, audio_rate): + + def _form_set_freq(kv): + return self.set_freq(kv['freq']) + + + if 0: + self.src_fft = fftsink.fft_sink_c (self, self.panel, title="Data from USRP", + fft_size=512, sample_rate=usrp_rate) + self.connect (self.src, self.src_fft) + vbox.Add (self.src_fft.win, 4, wx.EXPAND) + + if 0: + post_demod_fft = fftsink.fft_sink_f (self, self.panel, title="Post Demod", + fft_size=512, sample_rate=demod_rate, + y_per_div=10, ref_level=-40) + self.connect (self.am_demod, post_demod_fft) + vbox.Add (post_demod_fft.win, 4, wx.EXPAND) + + if 0: + post_filt_fft = fftsink.fft_sink_f (self, self.panel, title="Post Filter", + fft_size=512, sample_rate=audio_rate, + y_per_div=10, ref_level=-40) + self.connect (self.set_blacklevel, post_filt) + vbox.Add (fft_win4, 4, wx.EXPAND) + + + # control area form at bottom + self.myform = myform = form.form() + + if not (self.u is None): + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + myform['freq'] = form.float_field( + parent=self.panel, sizer=hbox, label="Freq", weight=1, + callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg)) + + hbox.Add((5,0), 0) + myform['freq_slider'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, weight=3, + range=(50.25e6, 900.25e6, 0.25e6), + callback=self.set_freq) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + + myform['contrast'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Contrast", + weight=3, range=(-2.0, 2.0, 0.1), + callback=self.set_contrast) + hbox.Add((5,0), 1) + + myform['brightness'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Brightness", + weight=3, range=(-255.0, 255.0, 1.0), + callback=self.set_brightness) + hbox.Add((5,0), 0) + + if not (self.u is None): + myform['gain'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Gain", + weight=3, range=self.subdev.gain_range(), + callback=self.set_gain) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + try: + self.knob = powermate.powermate(self.frame) + self.rot = 0 + powermate.EVT_POWERMATE_ROTATE (self.frame, self.on_rotate) + powermate.EVT_POWERMATE_BUTTON (self.frame, self.on_button) + except: + print "FYI: No Powermate or Contour Knob found" + + + def on_rotate (self, event): + self.rot += event.delta + if (self.state == "FREQ"): + if self.rot >= 3: + self.set_freq(self.freq + .1e6) + self.rot -= 3 + elif self.rot <=-3: + self.set_freq(self.freq - .1e6) + self.rot += 3 + elif (self.state == "CONTRAST"): + step = 0.1 + if self.rot >= 3: + self.set_contrast(self.contrast + step) + self.rot -= 3 + elif self.rot <=-3: + self.set_contrast(self.contrast - step) + self.rot += 3 + else: + step = 1 + if self.rot >= 3: + self.set_brightness(self.brightness + step) + self.rot -= 3 + elif self.rot <=-3: + self.set_brightness(self.brightness - step) + self.rot += 3 + + def on_button (self, event): + if event.value == 0: # button up + return + self.rot = 0 + if self.state == "FREQ": + self.state = "CONTRAST" + elif self.state == "CONTRAST": + self.state = "BRIGHTNESS" + else: + self.state = "FREQ" + self.update_status_bar () + + + def set_contrast (self, contrast): + self.contrast = contrast + self.invert_and_scale.set_k(-self.contrast *128.0*255.0/(200.0)) + self.myform['contrast'].set_value(self.contrast) + self.update_status_bar () + + def set_brightness (self, brightness): + self.brightness = brightness + self.set_blacklevel.set_k(self.brightness +255.0) + self.myform['brightness'].set_value(self.brightness) + self.update_status_bar () + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + + 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. + """ + if not (self.u is None): + r = usrp.tune(self.u, 0, self.subdev, target_freq) + if r: + self.freq = target_freq + self.myform['freq'].set_value(target_freq) # update displayed value + self.myform['freq_slider'].set_value(target_freq) # update displayed value + self.update_status_bar() + self._set_status_msg("OK", 0) + return True + + self._set_status_msg("Failed", 0) + return False + + def set_gain(self, gain): + if not (self.u is None): + self.gain=gain + self.myform['gain'].set_value(gain) # update displayed value + self.subdev.set_gain(gain) + self.update_status_bar() + + def update_status_bar (self): + msg = "Setting:%s Contrast:%r Brightness:%r Gain: %r" % (self.state, self.contrast,self.brightness,self.gain) + self._set_status_msg(msg, 1) + #self.src_fft.set_baseband_freq(self.freq) + + + +if __name__ == '__main__': + app = stdgui.stdapp (tv_rx_graph, "USRP TV RX black-and-white") + app.MainLoop () diff --git a/gnuradio-examples/python/usrp/usrp_tv_rcv_nogui.py b/gnuradio-examples/python/usrp/usrp_tv_rcv_nogui.py new file mode 100755 index 000000000..e563188b9 --- /dev/null +++ b/gnuradio-examples/python/usrp/usrp_tv_rcv_nogui.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python + +""" +Reads from a file and generates PAL TV pictures in black and white +which can be displayed using ImageMagick or realtime using gr-video-sdl +(To capture the input file Use usrp_rx_file.py, or use usrp_rx_cfile.py --output-shorts if you have a recent enough usrp_rx_cfile.py) +Can also use usrp directly as capture source, but then you need a higher decimation factor (64) +and thus get a lower horizontal resulution. +There is no synchronisation yet. The sync blocks are in development but not yet in cvs. + +""" + +from gnuradio import gr, eng_notation +from gnuradio import audio +from gnuradio import usrp +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import sys + +try: + from gnuradio import video_sdl +except: + print "FYI: gr-video-sdl is not installed" + print "realtime \"sdl\" video output window will not be available" + + +class my_graph(gr.flow_graph): + + def __init__(self): + gr.flow_graph.__init__(self) + + usage="%prog: [options] output_filename. \n Special output_filename \"sdl\" will use video_sink_sdl as realtime output window. " \ + "You then need to have gr-video-sdl installed. \n" \ + "Make sure your input capture file containes interleaved shorts not complex floats" + parser = OptionParser(option_class=eng_option, usage=usage) + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=(0, 0), + help="select USRP Rx side A or B (default=A)") + parser.add_option("-c", "--contrast", type="eng_float", default=1.0, + help="set contrast (default is 1.0)") + parser.add_option("-b", "--brightness", type="eng_float", default=0.0, + help="set brightness (default is 0)") + parser.add_option("-d", "--decim", type="int", default=8, + help="set fgpa decimation rate to DECIM [default=%default]") + parser.add_option("-i", "--in-filename", type="string", default=None, + help="Use input file as source. samples must be interleaved shorts \n " + + "Use usrp_rx_file.py or usrp_rx_cfile.py --output-shorts. \n" + "Special name \"usrp\" results in realtime capturing and processing using usrp. \n" + + "You then probably need a decimation factor of 64 or higher.") + parser.add_option("-f", "--freq", type="eng_float", default=None, + help="set frequency to FREQ.\nNote that the frequency of the video carrier is not at the middle of the TV channel", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=None, + help="set gain in dB (default is midpoint)") + parser.add_option("-p", "--pal", action="store_true", default=False, + help="PAL video format (this is the default)") + parser.add_option("-n", "--ntsc", action="store_true", default=False, + help="NTSC video format") + parser.add_option("-r", "--repeat", action="store_false", default=True, + help="repeat in_file in a loop") + parser.add_option("-8", "--width-8", action="store_true", default=False, + help="Enable 8-bit samples across USB") + parser.add_option("-N", "--nframes", type="eng_float", default=None, + help="number of frames to collect [default=+inf]") + parser.add_option( "--no-hb", action="store_true", default=False, + help="don't use halfband filter in usrp") + (options, args) = parser.parse_args () + if not (len(args) == 1): + parser.print_help() + sys.stderr.write('You must specify the output. FILENAME or sdl \n'); + sys.exit(1) + + filename = args[0] + + if options.in_filename is None: + parser.print_help() + sys.stderr.write('You must specify the input -i FILENAME or -i usrp\n'); + raise SystemExit, 1 + + if not (filename=="sdl"): + options.repeat=False + + if not (options.in_filename=="usrp"): + self.filesource = gr.file_source(gr.sizeof_short,options.in_filename,options.repeat) # file is data source, capture with usr_rx_csfile.py + self.istoc = gr.interleaved_short_to_complex() + self.connect(self.filesource,self.istoc) + self.adc_rate=64e6 + self.src=self.istoc + else: + if options.freq is None: + parser.print_help() + sys.stderr.write('You must specify the frequency with -f FREQ\n'); + raise SystemExit, 1 + if abs(options.freq) < 1e6: + options.freq *= 1e6 + if options.no_hb or (options.decim<8): + self.fpga_filename="std_4rx_0tx.rbf" #contains 4 Rx paths without halfbands and 0 tx paths + else: + self.fpga_filename="std_2rxhb_2tx.rbf" # contains 2 Rx paths with halfband filters and 2 tx paths (the default) + + # build the graph + self.u = usrp.source_c(decim_rate=options.decim,fpga_filename=self.fpga_filename) + self.src=self.u + if options.width_8: + sample_width = 8 + sample_shift = 8 + format = self.u.make_format(sample_width, sample_shift) + r = self.u.set_format(format) + self.adc_rate=self.u.adc_freq() + if options.rx_subdev_spec is None: + options.rx_subdev_spec = usrp.pick_rx_subdevice(self.u) + self.u.set_mux(usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec)) + # determine the daughterboard subdevice we're using + self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec) + print "Using RX d'board %s" % (self.subdev.side_and_name(),) + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + g = self.subdev.gain_range() + options.gain = float(g[0]+g[1])/2 + self.subdev.set_gain(options.gain) + + r = self.u.tune(0, self.subdev, options.freq) + if not r: + sys.stderr.write('Failed to set frequency\n') + raise SystemExit, 1 + + input_rate = self.adc_rate / options.decim + print "video sample rate %s" % (eng_notation.num_to_str(input_rate)) + + self.agc=gr.agc_cc(1e-7,1.0,1.0) #1e-7 + self.am_demod = gr.complex_to_mag () + self.set_blacklevel=gr.add_const_ff(options.brightness +255.0) + self.invert_and_scale = gr.multiply_const_ff (-options.contrast *128.0*255.0/(200.0)) + self.f2uc=gr.float_to_uchar() + + # sdl window as final sink + if not (options.pal or options.ntsc): + options.pal=True #set default to PAL + if options.pal: + lines_per_frame=625.0 + frames_per_sec=25.0 + show_width=768 + elif options.ntsc: + lines_per_frame=525.0 + frames_per_sec=29.97002997 + show_width=640 + width=int(input_rate/(lines_per_frame*frames_per_sec)) + height=int(lines_per_frame) + + if filename=="sdl": + #Here comes the tv screen, you have to build and install gr-video-sdl for this (subproject of gnuradio, only in cvs for now) + try: + video_sink = video_sdl.sink_uc ( frames_per_sec, width, height,0,show_width,height) + except: + print "gr-video-sdl is not installed" + print "realtime \"sdl\" video output window is not available" + raise SystemExit, 1 + self.dst=video_sink + else: + print "You can use the imagemagick display tool to show the resulting imagesequence" + print "use the following line to show the demodulated TV-signal:" + print "display -depth 8 -size " +str(width)+ "x" + str(height) + " gray:" +filename + print "(Use the spacebar to advance to next frames)" + file_sink=gr.file_sink(gr.sizeof_char, filename) + self.dst =file_sink + + if options.nframes is None: + self.connect(self.src, self.agc) + else: + self.head = gr.head(gr.sizeof_gr_complex, int(options.nframes*width*height)) + self.connect(self.src, self.head, self.agc) + + self.connect (self.agc,self.am_demod,self.invert_and_scale, self.set_blacklevel,self.f2uc,self.dst) + + +if __name__ == '__main__': + try: + my_graph().run() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/usrp/usrp_wfm_rcv.py b/gnuradio-examples/python/usrp/usrp_wfm_rcv.py new file mode 100755 index 000000000..202910767 --- /dev/null +++ b/gnuradio-examples/python/usrp/usrp_wfm_rcv.py @@ -0,0 +1,269 @@ +#!/usr/bin/env python + +from gnuradio import gr, gru, eng_notation, optfir +from gnuradio import audio +from gnuradio import usrp +from gnuradio import blks +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import slider, powermate +from gnuradio.wxgui import stdgui, fftsink, form +from optparse import OptionParser +import usrp_dbid +import sys +import math +import wx + +def pick_subdevice(u): + """ + The user didn't specify a subdevice on the command line. + Try for one of these, in order: TV_RX, BASIC_RX, whatever is on side A. + + @return a subdev_spec + """ + return usrp.pick_subdev(u, (usrp_dbid.TV_RX, + usrp_dbid.TV_RX_REV_2, + usrp_dbid.BASIC_RX)) + + +class wfm_rx_graph (stdgui.gui_flow_graph): + def __init__(self,frame,panel,vbox,argv): + stdgui.gui_flow_graph.__init__ (self,frame,panel,vbox,argv) + + parser=OptionParser(option_class=eng_option) + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, + help="select USRP Rx side A or B (default=A)") + parser.add_option("-f", "--freq", type="eng_float", default=100.1e6, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=40, + help="set gain in dB (default is midpoint)") + parser.add_option("-V", "--volume", type="eng_float", default=None, + help="set volume (default is midpoint)") + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm device name. E.g., hw:0,0 or surround51 or /dev/dsp") + + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + + self.frame = frame + self.panel = panel + + self.vol = 0 + self.state = "FREQ" + self.freq = 0 + + # build graph + + self.u = usrp.source_c() # usrp is data source + + adc_rate = self.u.adc_rate() # 64 MS/s + usrp_decim = 200 + self.u.set_decim_rate(usrp_decim) + usrp_rate = adc_rate / usrp_decim # 320 kS/s + chanfilt_decim = 1 + demod_rate = usrp_rate / chanfilt_decim + audio_decimation = 10 + audio_rate = demod_rate / audio_decimation # 32 kHz + + if options.rx_subdev_spec is None: + options.rx_subdev_spec = pick_subdevice(self.u) + + self.u.set_mux(usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec)) + self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec) + print "Using RX d'board %s" % (self.subdev.side_and_name(),) + + + chan_filt_coeffs = optfir.low_pass (1, # gain + usrp_rate, # sampling rate + 80e3, # passband cutoff + 115e3, # stopband cutoff + 0.1, # passband ripple + 60) # stopband attenuation + #print len(chan_filt_coeffs) + chan_filt = gr.fir_filter_ccf (chanfilt_decim, chan_filt_coeffs) + + self.guts = blks.wfm_rcv (self, demod_rate, audio_decimation) + + self.volume_control = gr.multiply_const_ff(self.vol) + + # sound card as final sink + audio_sink = audio.sink (int (audio_rate), + options.audio_output, + False) # ok_to_block + + # now wire it all together + self.connect (self.u, chan_filt, self.guts, self.volume_control, audio_sink) + + self._build_gui(vbox, usrp_rate, demod_rate, audio_rate) + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + g = self.subdev.gain_range() + options.gain = float(g[0]+g[1])/2 + + if options.volume is None: + g = self.volume_range() + options.volume = float(g[0]+g[1])/2 + + if abs(options.freq) < 1e6: + options.freq *= 1e6 + + # set initial values + + self.set_gain(options.gain) + self.set_vol(options.volume) + if not(self.set_freq(options.freq)): + self._set_status_msg("Failed to set initial frequency") + + + def _set_status_msg(self, msg, which=0): + self.frame.GetStatusBar().SetStatusText(msg, which) + + + def _build_gui(self, vbox, usrp_rate, demod_rate, audio_rate): + + def _form_set_freq(kv): + return self.set_freq(kv['freq']) + + + if 1: + self.src_fft = fftsink.fft_sink_c (self, self.panel, title="Data from USRP", + fft_size=512, sample_rate=usrp_rate) + self.connect (self.u, self.src_fft) + vbox.Add (self.src_fft.win, 4, wx.EXPAND) + + if 1: + post_filt_fft = fftsink.fft_sink_f (self, self.panel, title="Post Demod", + fft_size=1024, sample_rate=usrp_rate, + y_per_div=10, ref_level=0) + self.connect (self.guts.fm_demod, post_filt_fft) + vbox.Add (post_filt_fft.win, 4, wx.EXPAND) + + if 0: + post_deemph_fft = fftsink.fft_sink_f (self, self.panel, title="Post Deemph", + fft_size=512, sample_rate=audio_rate, + y_per_div=10, ref_level=-20) + self.connect (self.guts.deemph, post_deemph_fft) + vbox.Add (post_deemph_fft.win, 4, wx.EXPAND) + + + # control area form at bottom + self.myform = myform = form.form() + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + myform['freq'] = form.float_field( + parent=self.panel, sizer=hbox, label="Freq", weight=1, + callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg)) + + hbox.Add((5,0), 0) + myform['freq_slider'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, weight=3, + range=(87.9e6, 108.1e6, 0.1e6), + callback=self.set_freq) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + + myform['volume'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Volume", + weight=3, range=self.volume_range(), + callback=self.set_vol) + hbox.Add((5,0), 1) + + myform['gain'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Gain", + weight=3, range=self.subdev.gain_range(), + callback=self.set_gain) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + try: + self.knob = powermate.powermate(self.frame) + self.rot = 0 + powermate.EVT_POWERMATE_ROTATE (self.frame, self.on_rotate) + powermate.EVT_POWERMATE_BUTTON (self.frame, self.on_button) + except: + print "FYI: No Powermate or Contour Knob found" + + + def on_rotate (self, event): + self.rot += event.delta + if (self.state == "FREQ"): + if self.rot >= 3: + self.set_freq(self.freq + .1e6) + self.rot -= 3 + elif self.rot <=-3: + self.set_freq(self.freq - .1e6) + self.rot += 3 + else: + step = self.volume_range()[2] + if self.rot >= 3: + self.set_vol(self.vol + step) + self.rot -= 3 + elif self.rot <=-3: + self.set_vol(self.vol - step) + self.rot += 3 + + def on_button (self, event): + if event.value == 0: # button up + return + self.rot = 0 + if self.state == "FREQ": + self.state = "VOL" + else: + self.state = "FREQ" + self.update_status_bar () + + + def set_vol (self, vol): + g = self.volume_range() + self.vol = max(g[0], min(g[1], vol)) + self.volume_control.set_k(10**(self.vol/10)) + self.myform['volume'].set_value(self.vol) + self.update_status_bar () + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + + 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. + """ + r = usrp.tune(self.u, 0, self.subdev, target_freq) + + if r: + self.freq = target_freq + self.myform['freq'].set_value(target_freq) # update displayed value + self.myform['freq_slider'].set_value(target_freq) # update displayed value + self.update_status_bar() + self._set_status_msg("OK", 0) + return True + + self._set_status_msg("Failed", 0) + return False + + def set_gain(self, gain): + self.myform['gain'].set_value(gain) # update displayed value + self.subdev.set_gain(gain) + + def update_status_bar (self): + msg = "Volume:%r Setting:%s" % (self.vol, self.state) + self._set_status_msg(msg, 1) + self.src_fft.set_baseband_freq(self.freq) + + def volume_range(self): + return (-20.0, 0.0, 0.5) + + +if __name__ == '__main__': + app = stdgui.stdapp (wfm_rx_graph, "USRP WFM RX") + app.MainLoop () diff --git a/gnuradio-examples/python/usrp/usrp_wfm_rcv2_nogui.py b/gnuradio-examples/python/usrp/usrp_wfm_rcv2_nogui.py new file mode 100755 index 000000000..40e7dd754 --- /dev/null +++ b/gnuradio-examples/python/usrp/usrp_wfm_rcv2_nogui.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python + +from gnuradio import gr, gru, eng_notation, optfir +from gnuradio import audio +from gnuradio import usrp +from gnuradio import blks +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import usrp_dbid +import sys +import math + +def pick_subdevice(u): + """ + The user didn't specify a subdevice on the command line. + Try for one of these, in order: TV_RX, BASIC_RX, whatever is on side A. + + @return a subdev_spec + """ + return usrp.pick_subdev(u, (usrp_dbid.TV_RX, + usrp_dbid.TV_RX_REV_2, + usrp_dbid.BASIC_RX)) + + +class wfm_rx_graph (gr.flow_graph): + + def __init__(self): + gr.flow_graph.__init__(self) + + parser=OptionParser(option_class=eng_option) + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, + help="select USRP Rx side A or B (default=A)") + parser.add_option("", "--f1", type="eng_float", default=100.7e6, + help="set 1st station frequency to FREQ", metavar="FREQ") + parser.add_option("", "--f2", type="eng_float", default=102.5e6, + help="set 2nd station freq to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=40, + help="set gain in dB (default is midpoint)") + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm device name. E.g., hw:0,0 or surround51 or /dev/dsp") + + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if abs(options.f1) < 1e6: + options.f1 *= 1e6 + + if abs(options.f2) < 1e6: + options.f2 *= 1e6 + + if abs(options.f1 - options.f2) > 5.5e6: + print "Sorry, two stations must be within 5.5MHz of each other" + raise SystemExit + + f = (options.f1, options.f2) + + self.vol = .1 + self.state = "FREQ" + + # build graph + + self.u = usrp.source_c(0, nchan=2) # usrp is data source + + adc_rate = self.u.adc_rate() # 64 MS/s + usrp_decim = 200 + self.u.set_decim_rate(usrp_decim) + usrp_rate = adc_rate / usrp_decim # 320 kS/s + chanfilt_decim = 1 + demod_rate = usrp_rate / chanfilt_decim + audio_decimation = 10 + audio_rate = demod_rate / audio_decimation # 32 kHz + + + if options.rx_subdev_spec is None: + options.rx_subdev_spec = pick_subdevice(self.u) + + mv = usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec) + mv |= (mv << 8) & 0xff00 # both DDC inputs setup same way + self.u.set_mux(mv) + self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec) + print "Using RX d'board %s" % (self.subdev.side_and_name(),) + + + # deinterleave two channels from FPGA + di = gr.deinterleave(gr.sizeof_gr_complex) + + # wire up the head of the chain + self.connect(self.u, di) + + # sound card as final sink + audio_sink = audio.sink(int(audio_rate), options.audio_output) + + # taps for channel filter + chan_filt_coeffs = optfir.low_pass (1, # gain + usrp_rate, # sampling rate + 80e3, # passband cutoff + 115e3, # stopband cutoff + 0.1, # passband ripple + 60) # stopband attenuation + #print len(chan_filt_coeffs) + + mid_freq = (f[0] + f[1]) / 2 + # set front end PLL to middle frequency + ok, baseband_freq = self.subdev.set_freq(mid_freq) + + for n in range(2): + chan_filt = gr.fir_filter_ccf (chanfilt_decim, chan_filt_coeffs) + guts = blks.wfm_rcv (self, demod_rate, audio_decimation) + volume_control = gr.multiply_const_ff(self.vol) + self.connect((di, n), chan_filt) + self.connect(chan_filt, guts, volume_control) + self.connect(volume_control, (audio_sink, n)) + dxc_freq, inverted = usrp.calc_dxc_freq(f[n], baseband_freq, + self.u.converter_rate()) + self.u.set_rx_freq(n, dxc_freq) + + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + g = self.subdev.gain_range() + options.gain = float(g[0]+g[1])/2 + + + # set initial values + self.set_gain(options.gain) + + + def set_vol (self, vol): + self.vol = vol + self.volume_control.set_k(self.vol) + + + def set_gain(self, gain): + self.subdev.set_gain(gain) + + +if __name__ == '__main__': + fg = wfm_rx_graph() + try: + fg.run() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/usrp/usrp_wfm_rcv_nogui.py b/gnuradio-examples/python/usrp/usrp_wfm_rcv_nogui.py new file mode 100755 index 000000000..69f810bba --- /dev/null +++ b/gnuradio-examples/python/usrp/usrp_wfm_rcv_nogui.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python + +from gnuradio import gr, gru, eng_notation, optfir +from gnuradio import audio +from gnuradio import usrp +from gnuradio import blks +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import usrp_dbid +import sys +import math + +def pick_subdevice(u): + """ + The user didn't specify a subdevice on the command line. + Try for one of these, in order: TV_RX, BASIC_RX, whatever is on side A. + + @return a subdev_spec + """ + return usrp.pick_subdev(u, (usrp_dbid.TV_RX, + usrp_dbid.TV_RX_REV_2, + usrp_dbid.BASIC_RX)) + + +class wfm_rx_graph (gr.flow_graph): + + def __init__(self): + gr.flow_graph.__init__(self) + + parser=OptionParser(option_class=eng_option) + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, + help="select USRP Rx side A or B (default=A)") + parser.add_option("-f", "--freq", type="eng_float", default=100.1e6, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=None, + help="set gain in dB (default is midpoint)") + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm device name. E.g., hw:0,0 or surround51 or /dev/dsp") + + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + + self.vol = .1 + self.state = "FREQ" + self.freq = 0 + + # build graph + + self.u = usrp.source_c() # usrp is data source + + adc_rate = self.u.adc_rate() # 64 MS/s + usrp_decim = 200 + self.u.set_decim_rate(usrp_decim) + usrp_rate = adc_rate / usrp_decim # 320 kS/s + chanfilt_decim = 1 + demod_rate = usrp_rate / chanfilt_decim + audio_decimation = 10 + audio_rate = demod_rate / audio_decimation # 32 kHz + + + if options.rx_subdev_spec is None: + options.rx_subdev_spec = pick_subdevice(self.u) + + self.u.set_mux(usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec)) + self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec) + print "Using RX d'board %s" % (self.subdev.side_and_name(),) + + + chan_filt_coeffs = optfir.low_pass (1, # gain + usrp_rate, # sampling rate + 80e3, # passband cutoff + 115e3, # stopband cutoff + 0.1, # passband ripple + 60) # stopband attenuation + #print len(chan_filt_coeffs) + chan_filt = gr.fir_filter_ccf (chanfilt_decim, chan_filt_coeffs) + + self.guts = blks.wfm_rcv (self, demod_rate, audio_decimation) + + self.volume_control = gr.multiply_const_ff(self.vol) + + # sound card as final sink + audio_sink = audio.sink(int(audio_rate), + options.audio_output, + False) # ok_to_block + + # now wire it all together + self.connect (self.u, chan_filt, self.guts, self.volume_control, audio_sink) + + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + g = self.subdev.gain_range() + options.gain = float(g[0]+g[1])/2 + + if abs(options.freq) < 1e6: + options.freq *= 1e6 + + # set initial values + + self.set_gain(options.gain) + + if not(self.set_freq(options.freq)): + self._set_status_msg("Failed to set initial frequency") + + def set_vol (self, vol): + self.vol = vol + self.volume_control.set_k(self.vol) + self.update_status_bar () + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + + 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. + """ + r = self.u.tune(0, self.subdev, target_freq) + + if r: + self.freq = target_freq + self.update_status_bar() + self._set_status_msg("OK", 0) + return True + + self._set_status_msg("Failed", 0) + return False + + def set_gain(self, gain): + self.subdev.set_gain(gain) + + def update_status_bar (self): + msg = "Freq: %s Volume:%f Setting:%s" % ( + eng_notation.num_to_str(self.freq), self.vol, self.state) + self._set_status_msg(msg, 1) + + def _set_status_msg(self, msg, which=0): + print msg + + +if __name__ == '__main__': + fg = wfm_rx_graph() + try: + fg.run() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/usrp/usrp_wfm_rcv_pll.py b/gnuradio-examples/python/usrp/usrp_wfm_rcv_pll.py new file mode 100755 index 000000000..773076ae5 --- /dev/null +++ b/gnuradio-examples/python/usrp/usrp_wfm_rcv_pll.py @@ -0,0 +1,311 @@ +#!/usr/bin/env python + +from gnuradio import gr, gru, eng_notation, optfir +from gnuradio import audio +from gnuradio import usrp +from gnuradio import blks +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import slider, powermate +from gnuradio.wxgui import stdgui, fftsink, form, scopesink +from optparse import OptionParser +import usrp_dbid +import sys +import math +import wx + +def pick_subdevice(u): + """ + The user didn't specify a subdevice on the command line. + Try for one of these, in order: TV_RX, BASIC_RX, whatever is on side A. + + @return a subdev_spec + """ + return usrp.pick_subdev(u, (usrp_dbid.TV_RX, + usrp_dbid.TV_RX_REV_2, + usrp_dbid.BASIC_RX)) + +class wfm_rx_graph (stdgui.gui_flow_graph): + def __init__(self,frame,panel,vbox,argv): + stdgui.gui_flow_graph.__init__ (self,frame,panel,vbox,argv) + + parser=OptionParser(option_class=eng_option) + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, + help="select USRP Rx side A or B (default=A)") + parser.add_option("-f", "--freq", type="eng_float", default=100.1e6, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=65, + help="set gain in dB (default is midpoint)") + parser.add_option("-s", "--squelch", type="eng_float", default=0, + help="set squelch level (default is 0)") + parser.add_option("-V", "--volume", type="eng_float", default=None, + help="set volume (default is midpoint)") + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm device name. E.g., hw:0,0 or surround51 or /dev/dsp") + + + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + + self.frame = frame + self.panel = panel + + self.vol = 0 + self.state = "FREQ" + self.freq = 0 + + # build graph + + self.u = usrp.source_c() # usrp is data source + + adc_rate = self.u.adc_rate() # 64 MS/s + usrp_decim = 200 + self.u.set_decim_rate(usrp_decim) + usrp_rate = adc_rate / usrp_decim # 320 kS/s + chanfilt_decim = 1 + demod_rate = usrp_rate / chanfilt_decim + audio_decimation = 10 + audio_rate = demod_rate / audio_decimation # 32 kHz + + if options.rx_subdev_spec is None: + options.rx_subdev_spec = pick_subdevice(self.u) + + self.u.set_mux(usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec)) + self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec) + + + chan_filt_coeffs = optfir.low_pass (1, # gain + usrp_rate, # sampling rate + 80e3, # passband cutoff + 115e3, # stopband cutoff + 0.1, # passband ripple + 60) # stopband attenuation + #print len(chan_filt_coeffs) + chan_filt = gr.fir_filter_ccf (chanfilt_decim, chan_filt_coeffs) + + + #self.guts = blks.wfm_rcv (self, demod_rate, audio_decimation) + self.guts = blks.wfm_rcv_pll (self, demod_rate, audio_decimation) + + # FIXME rework {add,multiply}_const_* to handle multiple streams + self.volume_control_l = gr.multiply_const_ff(self.vol) + self.volume_control_r = gr.multiply_const_ff(self.vol) + + # sound card as final sink + audio_sink = audio.sink (int (audio_rate), + options.audio_output, + False) # ok_to_block + + # now wire it all together + self.connect (self.u, chan_filt, self.guts) + self.connect ((self.guts, 0), self.volume_control_l, (audio_sink, 0)) + self.connect ((self.guts, 1), self.volume_control_r, (audio_sink, 1)) + self.guts.stereo_carrier_pll_recovery.squelch_enable(True); + + self._build_gui(vbox, usrp_rate, demod_rate, audio_rate) + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + g = self.subdev.gain_range() + options.gain = float(g[0]+g[1])/2 + + if options.volume is None: + g = self.volume_range() + options.volume = float(g[0]+g[1])/2 + + if abs(options.freq) < 1e6: + options.freq *= 1e6 + + # set initial values + + self.set_gain(options.gain) + self.set_vol(options.volume) + self.guts.stereo_carrier_pll_recovery.set_lock_threshold(options.squelch); + if not(self.set_freq(options.freq)): + self._set_status_msg("Failed to set initial frequency") + + + def _set_status_msg(self, msg, which=0): + self.frame.GetStatusBar().SetStatusText(msg, which) + + + def _build_gui(self, vbox, usrp_rate, demod_rate, audio_rate): + + def _form_set_freq(kv): + return self.set_freq(kv['freq']) + + + if 1: + self.src_fft = fftsink.fft_sink_c (self, self.panel, title="Data from USRP", + fft_size=512, sample_rate=usrp_rate) + self.connect (self.u, self.src_fft) + vbox.Add (self.src_fft.win, 4, wx.EXPAND) + + if 1: + post_fm_demod_fft = fftsink.fft_sink_f (self, self.panel, title="Post FM Demod", + fft_size=512, sample_rate=demod_rate, + y_per_div=10, ref_level=0) + self.connect (self.guts.fm_demod, post_fm_demod_fft) + vbox.Add (post_fm_demod_fft.win, 4, wx.EXPAND) + + if 0: + post_stereo_carrier_generator_fft = fftsink.fft_sink_c (self, self.panel, title="Post Stereo_carrier_generator", + fft_size=512, sample_rate=audio_rate, + y_per_div=10, ref_level=0) + self.connect (self.guts.stereo_carrier_generator, post_stereo_carrier_generator_fft) + vbox.Add (post_stereo_carrier_generator_fft.win, 4, wx.EXPAND) + + if 0: + post_deemphasis_left = fftsink.fft_sink_f (self, self.panel, title="Post_Deemphasis_Left", + fft_size=512, sample_rate=audio_rate, + y_per_div=10, ref_level=0) + self.connect (self.guts.deemph_Left, post_deemphasis_left) + vbox.Add (post_deemphasis_left.win, 4, wx.EXPAND) + + if 0: + post_deemphasis_right = fftsink.fft_sink_f (self, self.panel, title="Post_Deemphasis_Right", + fft_size=512, sample_rate=audio_rate, + y_per_div=10, ref_level=-20) + self.connect (self.guts.deemph_Left, post_deemphasis_right) + vbox.Add (post_deemphasis_right.win, 4, wx.EXPAND) + + + if 0: + LmR_fft = fftsink.fft_sink_f (self, self.panel, title="LmR", + fft_size=512, sample_rate=audio_rate, + y_per_div=10, ref_level=-20) + self.connect (self.guts.LmR_real,LmR_fft) + vbox.Add (LmR_fft.win, 4, wx.EXPAND) + + if 0: + self.scope = scopesink.scope_sink_f(self, self.panel, sample_rate=demod_rate) + self.connect (self.guts.fm_demod_a,self.scope) + vbox.Add (self.scope.win,4,wx.EXPAND) + + # control area form at bottom + self.myform = myform = form.form() + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + myform['freq'] = form.float_field( + parent=self.panel, sizer=hbox, label="Freq", weight=1, + callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg)) + + hbox.Add((5,0), 0) + myform['freq_slider'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, weight=3, + range=(87.9e6, 108.1e6, 0.1e6), + callback=self.set_freq) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + + myform['volume'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Volume", + weight=3, range=self.volume_range(), + callback=self.set_vol) + hbox.Add((5,0), 1) + + myform['gain'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Gain", + weight=3, range=self.subdev.gain_range(), + callback=self.set_gain) + hbox.Add((5,0), 0) + + myform['sqlch_thrsh'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Stereo Squelch Threshold", + weight=3, range=(0.0,1.0,0.01), + callback=self.guts.stereo_carrier_pll_recovery.set_lock_threshold) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + try: + self.knob = powermate.powermate(self.frame) + self.rot = 0 + powermate.EVT_POWERMATE_ROTATE (self.frame, self.on_rotate) + powermate.EVT_POWERMATE_BUTTON (self.frame, self.on_button) + except: + print "FYI: No Powermate or Contour Knob found" + + + def on_rotate (self, event): + self.rot += event.delta + if (self.state == "FREQ"): + if self.rot >= 3: + self.set_freq(self.freq + .1e6) + self.rot -= 3 + elif self.rot <=-3: + self.set_freq(self.freq - .1e6) + self.rot += 3 + else: + step = self.volume_range()[2] + if self.rot >= 3: + self.set_vol(self.vol + step) + self.rot -= 3 + elif self.rot <=-3: + self.set_vol(self.vol - step) + self.rot += 3 + + def on_button (self, event): + if event.value == 0: # button up + return + self.rot = 0 + if self.state == "FREQ": + self.state = "VOL" + else: + self.state = "FREQ" + self.update_status_bar () + + + def set_vol (self, vol): + g = self.volume_range() + self.vol = max(g[0], min(g[1], vol)) + self.volume_control_l.set_k(10**(self.vol/10)) + self.volume_control_r.set_k(10**(self.vol/10)) + self.myform['volume'].set_value(self.vol) + self.update_status_bar () + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + + 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. + """ + r = usrp.tune(self.u, 0, self.subdev, target_freq) + + if r: + self.freq = target_freq + self.myform['freq'].set_value(target_freq) # update displayed value + self.myform['freq_slider'].set_value(target_freq) # update displayed value + self.update_status_bar() + self._set_status_msg("OK", 0) + return True + + self._set_status_msg("Failed", 0) + return False + + def set_gain(self, gain): + self.myform['gain'].set_value(gain) # update displayed value + self.subdev.set_gain(gain) + + def update_status_bar (self): + msg = "Volume:%r Setting:%s" % (self.vol, self.state) + self._set_status_msg(msg, 1) + self.src_fft.set_baseband_freq(self.freq) + + def volume_range(self): + return (-20.0, 0.0, 0.5) + + +if __name__ == '__main__': + app = stdgui.stdapp (wfm_rx_graph, "USRP WFM RX") + app.MainLoop () diff --git a/gnuradio-examples/python/usrp/usrp_wxapt_rcv.py b/gnuradio-examples/python/usrp/usrp_wxapt_rcv.py new file mode 100755 index 000000000..7c04c988a --- /dev/null +++ b/gnuradio-examples/python/usrp/usrp_wxapt_rcv.py @@ -0,0 +1,267 @@ +#!/usr/bin/env python + +from gnuradio import gr, gru, eng_notation, optfir +from gnuradio import audio +from gnuradio import usrp +from gnuradio import blks +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import slider, powermate +from gnuradio.wxgui import stdgui, fftsink, form +from optparse import OptionParser +import usrp_dbid +import sys +import math +import wx + +def pick_subdevice(u): + """ + The user didn't specify a subdevice on the command line. + Try for one of these, in order: TV_RX, BASIC_RX, whatever is on side A. + + @return a subdev_spec + """ + return usrp.pick_subdev(u, (usrp_dbid.TV_RX, + usrp_dbid.TV_RX_REV_2, + usrp_dbid.BASIC_RX)) + + +class wxapt_rx_graph (stdgui.gui_flow_graph): + def __init__(self,frame,panel,vbox,argv): + stdgui.gui_flow_graph.__init__ (self,frame,panel,vbox,argv) + + parser=OptionParser(option_class=eng_option) + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, + help="select USRP Rx side A or B (default=A)") + parser.add_option("-f", "--freq", type="eng_float", default=137.5e6, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=None, + help="set gain in dB (default is midpoint)") + parser.add_option("-V", "--volume", type="eng_float", default=None, + help="set volume (default is midpoint)") + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm device name. E.g., hw:0,0 or surround51 or /dev/dsp") + + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + + self.frame = frame + self.panel = panel + + self.vol = 0 + self.state = "FREQ" + self.freq = 0 + + # build graph + + self.u = usrp.source_c() # usrp is data source + + adc_rate = self.u.adc_rate() # 64 MS/s + usrp_decim = 200 + self.u.set_decim_rate(usrp_decim) + usrp_rate = adc_rate / usrp_decim # 320 kS/s + chanfilt_decim = 4 + demod_rate = usrp_rate / chanfilt_decim + audio_decimation = 10 + audio_rate = demod_rate / audio_decimation # 32 kHz + + if options.rx_subdev_spec is None: + options.rx_subdev_spec = pick_subdevice(self.u) + + self.u.set_mux(usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec)) + self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec) + print "Using RX d'board %s" % (self.subdev.side_and_name(),) + + + chan_filt_coeffs = optfir.low_pass (1, # gain + usrp_rate, # sampling rate + 40e3, # passband cutoff + 60e3, # stopband cutoff + 0.1, # passband ripple + 60) # stopband attenuation + #print len(chan_filt_coeffs) + chan_filt = gr.fir_filter_ccf (chanfilt_decim, chan_filt_coeffs) + + self.guts = blks.wfm_rcv (self, demod_rate, audio_decimation) + + self.volume_control = gr.multiply_const_ff(self.vol) + + # sound card as final sink + audio_sink = audio.sink (int (audio_rate), options.audio_output) + + # now wire it all together + self.connect (self.u, chan_filt, self.guts, self.volume_control, audio_sink) + + self._build_gui(vbox, usrp_rate, demod_rate, audio_rate) + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + g = self.subdev.gain_range() + options.gain = float(g[0]+g[1])/2 + + if options.volume is None: + g = self.volume_range() + options.volume = float(g[0]+g[1])/2 + + if abs(options.freq) < 1e6: + options.freq *= 1e6 + + # set initial values + + self.set_gain(options.gain) + self.set_vol(options.volume) + if not(self.set_freq(options.freq)): + self._set_status_msg("Failed to set initial frequency") + + + def _set_status_msg(self, msg, which=0): + self.frame.GetStatusBar().SetStatusText(msg, which) + + + def _build_gui(self, vbox, usrp_rate, demod_rate, audio_rate): + + def _form_set_freq(kv): + return self.set_freq(kv['freq']) + + + if 1: + self.src_fft = fftsink.fft_sink_c (self, self.panel, title="Data from USRP", + fft_size=512, sample_rate=usrp_rate) + self.connect (self.u, self.src_fft) + vbox.Add (self.src_fft.win, 4, wx.EXPAND) + + if 1: + post_deemph_fft = fftsink.fft_sink_f (self, self.panel, title="Post Deemph", + fft_size=512, sample_rate=demod_rate, + y_per_div=10, ref_level=-20) + self.connect (self.guts.deemph, post_deemph_fft) + vbox.Add (post_deemph_fft.win, 4, wx.EXPAND) + + if 1: + post_filt_fft = fftsink.fft_sink_f (self, self.panel, title="Post Filter", + fft_size=512, sample_rate=audio_rate, + y_per_div=10, ref_level=0) + self.connect (self.guts.audio_filter, post_filt_fft) + vbox.Add (post_filt_fft.win, 4, wx.EXPAND) + + + # control area form at bottom + self.myform = myform = form.form() + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + myform['freq'] = form.float_field( + parent=self.panel, sizer=hbox, label="Freq", weight=1, + callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg)) + + hbox.Add((5,0), 0) + myform['freq_slider'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, weight=3, + range=(137.0e6, 138.0e6, 0.0005e6), + callback=self.set_freq) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + + myform['volume'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Volume", + weight=3, range=self.volume_range(), + callback=self.set_vol) + hbox.Add((5,0), 1) + + myform['gain'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Gain", + weight=3, range=self.subdev.gain_range(), + callback=self.set_gain) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + try: + self.knob = powermate.powermate(self.frame) + self.rot = 0 + powermate.EVT_POWERMATE_ROTATE (self.frame, self.on_rotate) + powermate.EVT_POWERMATE_BUTTON (self.frame, self.on_button) + except: + print "FYI: No Powermate or Contour Knob found" + + + def on_rotate (self, event): + self.rot += event.delta + if (self.state == "FREQ"): + if self.rot >= 3: + self.set_freq(self.freq + .1e6) + self.rot -= 3 + elif self.rot <=-3: + self.set_freq(self.freq - .1e6) + self.rot += 3 + else: + step = self.volume_range()[2] + if self.rot >= 3: + self.set_vol(self.vol + step) + self.rot -= 3 + elif self.rot <=-3: + self.set_vol(self.vol - step) + self.rot += 3 + + def on_button (self, event): + if event.value == 0: # button up + return + self.rot = 0 + if self.state == "FREQ": + self.state = "VOL" + else: + self.state = "FREQ" + self.update_status_bar () + + + def set_vol (self, vol): + g = self.volume_range() + self.vol = max(g[0], min(g[1], vol)) + self.volume_control.set_k(10**(self.vol/10)) + self.myform['volume'].set_value(self.vol) + self.update_status_bar () + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + + 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. + """ + r = usrp.tune(self.u, 0, self.subdev, target_freq) + + if r: + self.freq = target_freq + self.myform['freq'].set_value(target_freq) # update displayed value + self.myform['freq_slider'].set_value(target_freq) # update displayed value + self.update_status_bar() + self._set_status_msg("OK", 0) + return True + + self._set_status_msg("Failed", 0) + return False + + def set_gain(self, gain): + self.myform['gain'].set_value(gain) # update displayed value + self.subdev.set_gain(gain) + + def update_status_bar (self): + msg = "Volume:%r Setting:%s" % (self.vol, self.state) + self._set_status_msg(msg, 1) + self.src_fft.set_baseband_freq(self.freq) + + def volume_range(self): + return (-20.0, 0.0, 0.5) + + +if __name__ == '__main__': + app = stdgui.stdapp (wxapt_rx_graph, "USRP WXAPT RX") + app.MainLoop () diff --git a/gnuradio-examples/python/usrp/wfm_rcv_file.py b/gnuradio-examples/python/usrp/wfm_rcv_file.py new file mode 100755 index 000000000..2c8d4f760 --- /dev/null +++ b/gnuradio-examples/python/usrp/wfm_rcv_file.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python + +from gnuradio import gr, eng_notation +from gnuradio import audio +# from gnuradio import usrp +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import sys +import math + + +# +# return a gr.flow_graph +# +def build_graph (input_filename, repeat): + adc_rate = 64e6 # USRP A/D sampling rate + decim = 250 # FPGA decimated by this amount + + quad_rate = adc_rate / decim # 256 kHz (the sample rate of the file) + audio_decimation = 8 + audio_rate = quad_rate / audio_decimation # 32 kHz + + fg = gr.flow_graph () + + # usrp is data source + # src = usrp.source_c (0, decim) + # src.set_rx_freq (0, -IF_freq) + + src = gr.file_source (gr.sizeof_gr_complex, input_filename, repeat) + + (head, tail) = build_pipeline (fg, quad_rate, audio_decimation) + + # sound card as final sink + audio_sink = audio.sink (int (audio_rate)) + + # now wire it all together + fg.connect (src, head) + fg.connect (tail, (audio_sink, 0)) + + return fg + +def build_pipeline (fg, quad_rate, audio_decimation): + '''Given a flow_graph, fg, construct a pipeline + for demodulating a broadcast FM signal. The + input is the downconverteed complex baseband + signal. The output is the demodulated audio. + + build_pipeline returns a two element tuple + containing the input and output endpoints. + ''' + fm_demod_gain = 2200.0/32768.0 + audio_rate = quad_rate / audio_decimation + volume = 1.0 + + # input: complex; output: float + fm_demod = gr.quadrature_demod_cf (volume*fm_demod_gain) + + # compute FIR filter taps for audio filter + width_of_transition_band = audio_rate / 32 + audio_coeffs = gr.firdes.low_pass (1.0, # gain + quad_rate, # sampling rate + audio_rate/2 - width_of_transition_band, + width_of_transition_band, + gr.firdes.WIN_HAMMING) + + TAU = 75e-6 # 75us in US, 50us in EUR + fftaps = [ 1 - math.exp(-1/TAU/quad_rate), 0] + fbtaps= [ 0 , math.exp(-1/TAU/quad_rate) ] + deemph = gr.iir_filter_ffd(fftaps,fbtaps) + + # input: float; output: float + audio_filter = gr.fir_filter_fff (audio_decimation, audio_coeffs) + + fg.connect (fm_demod, deemph) + fg.connect (deemph, audio_filter) + return ((fm_demod, 0), (audio_filter, 0)) + + +def main (): + usage = "usage: %prog [options] filename" + parser = OptionParser (option_class=eng_option, usage=usage) + parser.add_option ("-r", "--repeat", action="store_true", default=False) + # parser.add_option (... your stuff here...) + (options, args) = parser.parse_args () + + if len (args) != 1: + parser.print_help () + sys.exit (1) + + fg = build_graph (args[0], options.repeat) + + fg.start () # fork thread(s) and return + raw_input ('Press Enter to quit: ') + fg.stop () + +if __name__ == '__main__': + main () + + |