summaryrefslogtreecommitdiff
path: root/gnuradio-core/src/python
diff options
context:
space:
mode:
Diffstat (limited to 'gnuradio-core/src/python')
-rw-r--r--gnuradio-core/src/python/Makefile.am29
-rw-r--r--gnuradio-core/src/python/bin/Makefile.am30
-rwxr-xr-xgnuradio-core/src/python/bin/microtune.py42
-rw-r--r--gnuradio-core/src/python/build_utils.py163
-rw-r--r--gnuradio-core/src/python/build_utils_codes.py52
-rw-r--r--gnuradio-core/src/python/gnuradio/Makefile.am36
-rw-r--r--gnuradio-core/src/python/gnuradio/__init__.py1
-rw-r--r--gnuradio-core/src/python/gnuradio/audio.py88
-rw-r--r--gnuradio-core/src/python/gnuradio/blks/Makefile.am35
-rw-r--r--gnuradio-core/src/python/gnuradio/blks/__init__.py37
-rw-r--r--gnuradio-core/src/python/gnuradio/blksimpl/Makefile.am49
-rw-r--r--gnuradio-core/src/python/gnuradio/blksimpl/__init__.py1
-rw-r--r--gnuradio-core/src/python/gnuradio/blksimpl/am_demod.py75
-rw-r--r--gnuradio-core/src/python/gnuradio/blksimpl/digital_voice.py.real102
-rw-r--r--gnuradio-core/src/python/gnuradio/blksimpl/filterbank.py160
-rw-r--r--gnuradio-core/src/python/gnuradio/blksimpl/fm_demod.py122
-rw-r--r--gnuradio-core/src/python/gnuradio/blksimpl/fm_emph.py145
-rw-r--r--gnuradio-core/src/python/gnuradio/blksimpl/gmsk2.py159
-rw-r--r--gnuradio-core/src/python/gnuradio/blksimpl/gmsk2_pkt.py174
-rw-r--r--gnuradio-core/src/python/gnuradio/blksimpl/nbfm_rx.py87
-rw-r--r--gnuradio-core/src/python/gnuradio/blksimpl/nbfm_tx.py95
-rw-r--r--gnuradio-core/src/python/gnuradio/blksimpl/pkt.py156
-rw-r--r--gnuradio-core/src/python/gnuradio/blksimpl/rational_resampler.py137
-rw-r--r--gnuradio-core/src/python/gnuradio/blksimpl/standard_squelch.py73
-rw-r--r--gnuradio-core/src/python/gnuradio/blksimpl/wfm_rcv.py72
-rw-r--r--gnuradio-core/src/python/gnuradio/blksimpl/wfm_rcv_pll.py206
-rw-r--r--gnuradio-core/src/python/gnuradio/blksimpl/wfm_tx.py79
-rw-r--r--gnuradio-core/src/python/gnuradio/eng_notation.py71
-rw-r--r--gnuradio-core/src/python/gnuradio/eng_option.py80
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/Makefile.am77
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/__init__.py40
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/basic_flow_graph.py267
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/benchmark_filters.py55
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/exceptions.py27
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/flow_graph.py234
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/gr_threading.py35
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/gr_threading_23.py724
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/gr_threading_24.py793
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/hier_block.py132
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/prefs.py129
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_add_and_friends.py129
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_add_v_and_friends.py353
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_basic_flow_graph.py190
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_cma_equalizer.py29
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_complex_to_xxx.py116
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_constellation_decoder_cb.py53
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_correlate_access_code.py83
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_diff_encoder.py86
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_diff_phasor_cc.py50
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_feval.py92
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_fft_filter.py268
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_filter_delay_fc.py317
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_flow_graph.py356
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_frequency_modulator.py56
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_fsk_stuff.py75
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_goertzel.py64
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_head.py47
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_hilbert.py116
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_iir.py135
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_interleave.py81
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_interp_fir_filter.py54
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_kludge_copy.py89
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_kludged_imports.py43
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_message.py117
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_mute.py89
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_nlog10.py47
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_packed_to_unpacked.py405
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_pipe_fittings.py143
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_rational_resampler.py290
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_sig_source.py85
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_single_pole_iir.py72
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_single_pole_iir_cc.py72
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_unpack_k_bits.py57
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/run_tests.in33
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/scheduler.py70
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr_unittest.py114
-rw-r--r--gnuradio-core/src/python/gnuradio/gru/Makefile.am35
-rw-r--r--gnuradio-core/src/python/gnuradio/gru/__init__.py37
-rw-r--r--gnuradio-core/src/python/gnuradio/gruimpl/Makefile.am40
-rw-r--r--gnuradio-core/src/python/gnuradio/gruimpl/__init__.py1
-rw-r--r--gnuradio-core/src/python/gnuradio/gruimpl/crc.py36
-rw-r--r--gnuradio-core/src/python/gnuradio/gruimpl/freqz.py344
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gruimpl/gnuplot_freqz.py82
-rw-r--r--gnuradio-core/src/python/gnuradio/gruimpl/hexint.py32
-rw-r--r--gnuradio-core/src/python/gnuradio/gruimpl/listmisc.py29
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gruimpl/lmx2306.py186
-rw-r--r--gnuradio-core/src/python/gnuradio/gruimpl/mathmisc.py33
-rw-r--r--gnuradio-core/src/python/gnuradio/gruimpl/os_read_exactly.py36
-rw-r--r--gnuradio-core/src/python/gnuradio/gruimpl/sdr_1000.py84
-rw-r--r--gnuradio-core/src/python/gnuradio/gruimpl/seq_with_cursor.py77
-rw-r--r--gnuradio-core/src/python/gnuradio/gruimpl/socket_stuff.py56
-rw-r--r--gnuradio-core/src/python/gnuradio/optfir.py242
-rw-r--r--gnuradio-core/src/python/gnuradio/packet_utils.py433
-rw-r--r--gnuradio-core/src/python/gnuradio/window.py190
94 files changed, 11518 insertions, 0 deletions
diff --git a/gnuradio-core/src/python/Makefile.am b/gnuradio-core/src/python/Makefile.am
new file mode 100644
index 000000000..99f860264
--- /dev/null
+++ b/gnuradio-core/src/python/Makefile.am
@@ -0,0 +1,29 @@
+#
+# 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
+
+SUBDIRS = gnuradio bin
+
+noinst_PYTHON = \
+ build_utils.py \
+ build_utils_codes.py
+
diff --git a/gnuradio-core/src/python/bin/Makefile.am b/gnuradio-core/src/python/bin/Makefile.am
new file mode 100644
index 000000000..3e9b90763
--- /dev/null
+++ b/gnuradio-core/src/python/bin/Makefile.am
@@ -0,0 +1,30 @@
+#
+# 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.
+#
+
+include $(top_srcdir)/Makefile.common
+
+
+EXTRA_DIST = microtune.py
+
+bin_SCRIPTS = \
+ microtune.py
+
+CLEANFILES = *.pyc
diff --git a/gnuradio-core/src/python/bin/microtune.py b/gnuradio-core/src/python/bin/microtune.py
new file mode 100755
index 000000000..0e799c93d
--- /dev/null
+++ b/gnuradio-core/src/python/bin/microtune.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+# -*- Python -*-
+
+from gnuradio import gr
+from gnuradio.eng_option import eng_option
+from gnuradio.wxgui import stdgui, fftsink
+from optparse import OptionParser
+from gnuradio import eng_notation
+
+
+def main ():
+ parser = OptionParser (option_class=eng_option)
+ parser.add_option ("-g", "--gain", type="eng_float", default=-1,
+ help="set front end gain to GAIN [0,1000]")
+ parser.add_option ("-f", "--freq", type="eng_float", default=-1,
+ help="set front end center frequency to FREQ")
+ parser.add_option ("-t", "--type", type="string", default="4937",
+ help="select eval board type {4937 or 4702}")
+ parser.add_option ("-p", "--port", type="int", default=0,
+ help="parallel port eval board is attached to")
+ (options, args) = parser.parse_args ()
+
+ if options.type == "4937":
+ front_end = gr.microtune_4937_eval_board (options.port)
+ elif options.type == "4702":
+ front_end = gr.microtune_4702_eval_board (options.port)
+ else:
+ raise RuntimeError, "Invalid board type. Must be either -t 4937 or -t 4702"
+
+ if options.gain != -1:
+ front_end.set_AGC (options.gain)
+
+ if options.freq != -1:
+ if options.freq < 1e6:
+ options.freq = options.freq * 1e6
+
+ actual = front_end.set_RF_freq (options.freq)
+ print "microtune: actual freq = %s" % (eng_notation.num_to_str (actual),)
+
+
+if __name__ == '__main__':
+ main ()
diff --git a/gnuradio-core/src/python/build_utils.py b/gnuradio-core/src/python/build_utils.py
new file mode 100644
index 000000000..18cfc2e7d
--- /dev/null
+++ b/gnuradio-core/src/python/build_utils.py
@@ -0,0 +1,163 @@
+#
+# 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.
+#
+
+"""Misc utilities used at build time
+"""
+
+import re, os, os.path
+from build_utils_codes import *
+
+
+# set srcdir to the directory that contains Makefile.am
+try:
+ srcdir = os.environ['srcdir']
+except KeyError, e:
+ srcdir = "."
+srcdir = srcdir + '/'
+
+
+name_dict = {}
+
+def log_output_name (name):
+ (base, ext) = os.path.splitext (name)
+ ext = ext[1:] # drop the leading '.'
+
+ entry = name_dict.setdefault (ext, [])
+ entry.append (name)
+
+def open_and_log_name (name, dir):
+ f = open (name, dir)
+ log_output_name (name)
+ return f
+
+def expand_template (d, template_filename, extra = ""):
+ '''Given a dictionary D and a TEMPLATE_FILENAME, expand template into output file
+ '''
+ output_extension = extract_extension (template_filename)
+ template = open_src (template_filename, 'r')
+ output_name = d['NAME'] + extra + '.' + output_extension
+ log_output_name (output_name)
+ output = open (output_name, 'w')
+ do_substitution (d, template, output)
+ template.close ()
+ output.close ()
+
+def output_glue (dirname):
+ output_makefile_fragment ()
+ output_ifile_include (dirname)
+
+def output_makefile_fragment ():
+ f = open ('Makefile.gen', 'w')
+ f.write ('#\n# This file is machine generated. All edits will be overwritten\n#\n')
+ output_subfrag (f, 'h')
+ output_subfrag (f, 'i')
+ output_subfrag (f, 'cc')
+ f.close ()
+
+def output_ifile_include (dirname):
+ f = open ('%s_generated.i' % (dirname,), 'w')
+ f.write ('//\n// This file is machine generated. All edits will be overwritten\n//\n')
+ files = name_dict.setdefault ('i', [])
+ files.sort ()
+ f.write ('%{\n')
+ for file in files:
+ f.write ('#include <%s>\n' % (file[0:-1] + 'h',))
+ f.write ('%}\n\n')
+ for file in files:
+ f.write ('%%include <%s>\n' % (file,))
+
+def output_subfrag (f, ext):
+ files = name_dict.setdefault (ext, [])
+ files.sort ()
+ f.write ("GENERATED_%s =" % (ext.upper ()))
+ for file in files:
+ f.write (" \\\n\t%s" % (file,))
+ f.write ("\n\n")
+
+
+def extract_extension (template_name):
+ # template name is something like: GrFIRfilterXXX.h.t
+ # we return everything between the penultimate . and .t
+ mo = re.search (r'\.([a-z]+)\.t$', template_name)
+ if not mo:
+ raise ValueError, "Incorrectly formed template_name '%s'" % (template_name,)
+ return mo.group (1)
+
+def open_src (name, mode):
+ global srcdir
+ return open (os.path.join (srcdir, name), mode)
+
+def do_substitution (d, in_file, out_file):
+ def repl (match_obj):
+ key = match_obj.group (1)
+ # print key
+ return d[key]
+
+ inp = in_file.read ()
+ out = re.sub (r"@([a-zA-Z0-9_]+)@", repl, inp)
+ out_file.write (out)
+
+
+
+copyright = '''/* -*- c++ -*- */
+/*
+ * Copyright 2003,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.
+ */
+'''
+
+def is_complex (code3):
+ if i_code (code3) == 'c' or o_code (code3) == 'c':
+ return '1'
+ else:
+ return '0'
+
+
+def standard_dict (name, code3):
+ d = {}
+ d['NAME'] = name
+ d['GUARD_NAME'] = 'INCLUDED_%s_H' % name.upper ()
+ d['BASE_NAME'] = re.sub ('^gr_', '', name)
+ d['SPTR_NAME'] = '%s_sptr' % name
+ d['WARNING'] = 'WARNING: this file is machine generated. Edits will be over written'
+ d['COPYRIGHT'] = copyright
+ d['TYPE'] = i_type (code3)
+ d['I_TYPE'] = i_type (code3)
+ d['O_TYPE'] = o_type (code3)
+ d['TAP_TYPE'] = tap_type (code3)
+ d['IS_COMPLEX'] = is_complex (code3)
+ return d
diff --git a/gnuradio-core/src/python/build_utils_codes.py b/gnuradio-core/src/python/build_utils_codes.py
new file mode 100644
index 000000000..f4215f2b4
--- /dev/null
+++ b/gnuradio-core/src/python/build_utils_codes.py
@@ -0,0 +1,52 @@
+#
+# 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.
+#
+
+def i_code (code3):
+ return code3[0]
+
+def o_code (code3):
+ if len (code3) >= 2:
+ return code3[1]
+ else:
+ return code3[0]
+
+def tap_code (code3):
+ if len (code3) >= 3:
+ return code3[2]
+ else:
+ return code3[0]
+
+def i_type (code3):
+ return char_to_type[i_code (code3)]
+
+def o_type (code3):
+ return char_to_type[o_code (code3)]
+
+def tap_type (code3):
+ return char_to_type[tap_code (code3)]
+
+
+char_to_type = {}
+char_to_type['s'] = 'short'
+char_to_type['i'] = 'int'
+char_to_type['f'] = 'float'
+char_to_type['c'] = 'gr_complex'
+char_to_type['b'] = 'unsigned char'
diff --git a/gnuradio-core/src/python/gnuradio/Makefile.am b/gnuradio-core/src/python/gnuradio/Makefile.am
new file mode 100644
index 000000000..3222c5db3
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/Makefile.am
@@ -0,0 +1,36 @@
+#
+# 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
+
+SUBDIRS = gr gru gruimpl blks blksimpl
+
+grpython_PYTHON = \
+ __init__.py \
+ audio.py \
+ eng_notation.py \
+ eng_option.py \
+ packet_utils.py \
+ gr_unittest.py \
+ optfir.py \
+ window.py
+
+CLEANFILES = *.pyc
diff --git a/gnuradio-core/src/python/gnuradio/__init__.py b/gnuradio-core/src/python/gnuradio/__init__.py
new file mode 100644
index 000000000..a4917cf64
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/__init__.py
@@ -0,0 +1 @@
+# make this a package
diff --git a/gnuradio-core/src/python/gnuradio/audio.py b/gnuradio-core/src/python/gnuradio/audio.py
new file mode 100644
index 000000000..5a9d09c77
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/audio.py
@@ -0,0 +1,88 @@
+#
+# 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.
+#
+
+"""
+This is the 'generic' audio or soundcard interface.
+
+The behavior of this module is controlled by the [audio] audio_module
+configuration parameter. If it is 'auto' we attempt to import modules
+from the known_modules list, using the first one imported successfully.
+
+If [audio] audio_module is not 'auto', we assume it's the name of
+an audio module and attempt to import it.
+"""
+
+__all__ = ['source', 'sink']
+
+from gnuradio import gr
+import sys
+
+source = None
+sink = None
+
+
+known_modules = (
+ 'audio_alsa', 'audio_oss', 'audio_osx', 'audio_jack', 'audio_portaudio')
+
+
+def try_import(name):
+ """
+ Build a blob of code and try to execute it.
+ If it succeeds we will have set the globals source and sink
+ as side effects.
+
+ returns True or False
+ """
+ global source, sink
+ full_name = "gnuradio." + name
+ code = """
+import %s
+source = %s.source
+sink = %s.sink
+""" % (full_name, full_name, full_name)
+ try:
+ exec code in globals()
+ return True
+ except ImportError:
+ return False
+
+
+def __init__ ():
+ p = gr.prefs() # get preferences (config file) object
+ verbose = p.get_bool('audio', 'verbose', False)
+ module = p.get_string('audio', 'audio_module', 'auto')
+
+ if module == 'auto': # search our list for the first one that we can import
+ for m in known_modules:
+ if try_import(m):
+ if verbose: sys.stderr.write('audio: using %s\n' % (m,))
+ return
+ raise ImportError, 'Unable to locate an audio module.'
+
+ else: # use the one the user specified
+ if try_import(module):
+ if verbose: sys.stderr.write('audio: using %s\n' % (module,))
+ else:
+ msg = 'Failed to import user-specified audio module %s' % (module,)
+ if verbose: sys.stderr.write('audio: %s\n' % (msg,))
+ raise ImportError, msg
+
+__init__()
diff --git a/gnuradio-core/src/python/gnuradio/blks/Makefile.am b/gnuradio-core/src/python/gnuradio/blks/Makefile.am
new file mode 100644
index 000000000..17574d77b
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks/Makefile.am
@@ -0,0 +1,35 @@
+#
+# 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.
+#
+
+include $(top_srcdir)/Makefile.common
+
+# EXTRA_DIST = run_tests.in
+# TESTS = run_tests
+
+grblkspythondir = $(grpythondir)/blks
+
+grblkspython_PYTHON = \
+ __init__.py
+
+
+noinst_PYTHON =
+
+CLEANFILES = *.pyc *.pyo
diff --git a/gnuradio-core/src/python/gnuradio/blks/__init__.py b/gnuradio-core/src/python/gnuradio/blks/__init__.py
new file mode 100644
index 000000000..4cc10ebb3
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks/__init__.py
@@ -0,0 +1,37 @@
+#
+# 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 glob
+import os.path
+
+# Semi-hideous kludge to import everything in the blksimpl directory
+# into the gnuradio.blks namespace. This keeps us from having to remember
+# to manually update this file.
+
+for p in __path__:
+ filenames = glob.glob (os.path.join (p, "..", "blksimpl", "*.py"))
+ for f in filenames:
+ f = os.path.basename(f).lower()
+ f = f[:-3]
+ if f == '__init__':
+ continue
+ # print f
+ exec "from gnuradio.blksimpl.%s import *" % (f,)
diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/Makefile.am b/gnuradio-core/src/python/gnuradio/blksimpl/Makefile.am
new file mode 100644
index 000000000..415920b29
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blksimpl/Makefile.am
@@ -0,0 +1,49 @@
+#
+# 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.
+#
+
+include $(top_srcdir)/Makefile.common
+
+# EXTRA_DIST = run_tests.in
+# TESTS = run_tests
+
+grblkspythondir = $(grpythondir)/blksimpl
+
+grblkspython_PYTHON = \
+ __init__.py \
+ am_demod.py \
+ filterbank.py \
+ fm_demod.py \
+ fm_emph.py \
+ gmsk2.py \
+ gmsk2_pkt.py \
+ nbfm_rx.py \
+ nbfm_tx.py \
+ pkt.py \
+ rational_resampler.py \
+ standard_squelch.py \
+ wfm_rcv.py \
+ wfm_rcv_pll.py \
+ wfm_tx.py
+
+
+noinst_PYTHON =
+
+CLEANFILES = *.pyc *.pyo
diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/__init__.py b/gnuradio-core/src/python/gnuradio/blksimpl/__init__.py
new file mode 100644
index 000000000..a4917cf64
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blksimpl/__init__.py
@@ -0,0 +1 @@
+# make this a package
diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/am_demod.py b/gnuradio-core/src/python/gnuradio/blksimpl/am_demod.py
new file mode 100644
index 000000000..309f5e650
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blksimpl/am_demod.py
@@ -0,0 +1,75 @@
+#
+# 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, optfir
+
+class am_demod_cf(gr.hier_block):
+ """
+ Generalized AM demodulation block with audio filtering.
+
+ This block demodulates a band-limited, complex down-converted AM
+ channel into the the original baseband signal, applying low pass
+ filtering to the audio output. It produces a float stream in the
+ range [-1.0, +1.0].
+
+ @param fg: flowgraph
+ @param channel_rate: incoming sample rate of the AM baseband
+ @type sample_rate: integer
+ @param audio_decim: input to output decimation rate
+ @type audio_decim: integer
+ @param audio_pass: audio low pass filter passband frequency
+ @type audio_pass: float
+ @param audio_stop: audio low pass filter stop frequency
+ @type audio_stop: float
+ """
+ def __init__(self, fg, channel_rate, audio_decim, audio_pass, audio_stop):
+ MAG = gr.complex_to_mag()
+ DCR = gr.add_const_ff(-1.0)
+
+ audio_taps = optfir.low_pass(0.5, # Filter gain
+ channel_rate, # Sample rate
+ audio_pass, # Audio passband
+ audio_stop, # Audio stopband
+ 0.1, # Passband ripple
+ 60) # Stopband attenuation
+ LPF = gr.fir_filter_fff(audio_decim, audio_taps)
+
+ fg.connect(MAG, DCR, LPF)
+ gr.hier_block.__init__(self, fg, MAG, LPF)
+
+class demod_10k0a3e_cf(am_demod_cf):
+ """
+ AM demodulation block, 10 KHz channel.
+
+ This block demodulates an AM channel conformant to 10K0A3E emission
+ standards, such as broadcast band AM transmissions.
+
+ @param fg: flowgraph
+ @param channel_rate: incoming sample rate of the AM baseband
+ @type sample_rate: integer
+ @param audio_decim: input to output decimation rate
+ @type audio_decim: integer
+ """
+ def __init__(self, fg, channel_rate, audio_decim):
+ am_demod_cf.__init__(self, fg, channel_rate, audio_decim,
+ 5000, # Audio passband
+ 5500) # Audio stopband
+ \ No newline at end of file
diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/digital_voice.py.real b/gnuradio-core/src/python/gnuradio/blksimpl/digital_voice.py.real
new file mode 100644
index 000000000..1b3a14f3e
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blksimpl/digital_voice.py.real
@@ -0,0 +1,102 @@
+#!/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.
+#
+
+"""
+Digital voice Tx and Rx using GSM 13kbit vocoder and GMSK.
+
+Runs channel at 32kbit/sec. Currently uses fake channel coding,
+but there's room for a rate 1/2 coder.
+"""
+
+from gnuradio import gr, gru
+from gnuradio.blksimpl.gmsk import gmsk_mod, gmsk_demod
+
+from gnuradio.vocoder import gsm_full_rate
+
+# Size of gsm full rate speech encoder output packet in bytes
+
+GSM_FRAME_SIZE = 33
+
+# Size of packet in bytes that we send to GMSK modulator:
+#
+# Target: 256kS/sec air rate.
+#
+# 256kS 1 sym 1 bit 1 byte 0.020 sec 80 bytes
+# ---- * ----- * ----- * ------ * --------- = --------
+# sec 8 S 1 sym 8 bits frame frame
+#
+# gr_simple_framer add 10 bytes of overhead.
+
+AIR_FRAME_SIZE = 70
+
+
+class digital_voice_tx(gr.hier_block):
+ """
+ Hierarchical block for digital voice tranmission.
+
+ The input is 8kS/sec floating point audio in the range [-1,+1]
+ The output is 256kS/sec GMSK modulated complex baseband signal in the range [-1,+1].
+ """
+ def __init__(self, fg):
+ samples_per_symbol = 8
+ symbol_rate = 32000
+ bt = 0.3 # Gaussian filter bandwidth * symbol time
+
+ src_scale = gr.multiply_const_ff(32767)
+ f2s = gr.float_to_short()
+ voice_coder = gsm_full_rate.encode_sp()
+
+ channel_coder = gr.fake_channel_encoder_pp(GSM_FRAME_SIZE, AIR_FRAME_SIZE)
+ p2s = gr.parallel_to_serial(gr.sizeof_char, AIR_FRAME_SIZE)
+
+ mod = gmsk_mod(fg, sps=samples_per_symbol,
+ symbol_rate=symbol_rate, bt=bt,
+ p_size=AIR_FRAME_SIZE)
+
+ fg.connect(src_scale, f2s, voice_coder, channel_coder, p2s, mod)
+ gr.hier_block.__init__(self, fg, src_scale, mod)
+
+
+class digital_voice_rx(gr.hier_block):
+ """
+ Hierarchical block for digital voice reception.
+
+ The input is 256kS/sec GMSK modulated complex baseband signal.
+ The output is 8kS/sec floating point audio in the range [-1,+1]
+ """
+ def __init__(self, fg):
+ samples_per_symbol = 8
+ symbol_rate = 32000
+
+ demod = gmsk_demod(fg, sps=samples_per_symbol,
+ symbol_rate=symbol_rate,
+ p_size=AIR_FRAME_SIZE)
+
+ s2p = gr.serial_to_parallel(gr.sizeof_char, AIR_FRAME_SIZE)
+ channel_decoder = gr.fake_channel_decoder_pp(AIR_FRAME_SIZE, GSM_FRAME_SIZE)
+
+ voice_decoder = gsm_full_rate.decode_ps()
+ s2f = gr.short_to_float ()
+ sink_scale = gr.multiply_const_ff(1.0/32767.)
+
+ fg.connect(demod, s2p, channel_decoder, voice_decoder, s2f, sink_scale)
+ gr.hier_block.__init__(self, fg, demod, sink_scale)
diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/filterbank.py b/gnuradio-core/src/python/gnuradio/blksimpl/filterbank.py
new file mode 100644
index 000000000..bd23f7936
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blksimpl/filterbank.py
@@ -0,0 +1,160 @@
+#
+# Copyright 2005 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+import sys
+from gnuradio import gr, gru
+
+def _generate_synthesis_taps(mpoints):
+ return [] # FIXME
+
+
+def _split_taps(taps, mpoints):
+ assert (len(taps) % mpoints) == 0
+ result = [list() for x in range(mpoints)]
+ for i in xrange(len(taps)):
+ (result[i % mpoints]).append(taps[i])
+ return [tuple(x) for x in result]
+
+
+class synthesis_filterbank(gr.hier_block):
+ """
+ Uniformly modulated polyphase DFT filter bank: synthesis
+
+ See http://cnx.rice.edu/content/m10424/latest
+ """
+ def __init__(self, fg, mpoints, taps=None):
+ """
+ Takes M complex streams in, produces single complex stream out
+ that runs at M times the input sample rate
+
+ @param fg: flow_graph
+ @param mpoints: number of freq bins/interpolation factor/subbands
+ @param taps: filter taps for subband filter
+
+ The channel spacing is equal to the input sample rate.
+ The total bandwidth and output sample rate are equal the input
+ sample rate * nchannels.
+
+ Output stream to frequency mapping:
+
+ channel zero is at zero frequency.
+
+ if mpoints is odd:
+
+ Channels with increasing positive frequencies come from
+ channels 1 through (N-1)/2.
+
+ Channel (N+1)/2 is the maximum negative frequency, and
+ frequency increases through N-1 which is one channel lower
+ than the zero frequency.
+
+ if mpoints is even:
+
+ Channels with increasing positive frequencies come from
+ channels 1 through (N/2)-1.
+
+ Channel (N/2) is evenly split between the max positive and
+ negative bins.
+
+ Channel (N/2)+1 is the maximum negative frequency, and
+ frequency increases through N-1 which is one channel lower
+ than the zero frequency.
+
+ Channels near the frequency extremes end up getting cut
+ off by subsequent filters and therefore have diminished
+ utility.
+ """
+ item_size = gr.sizeof_gr_complex
+
+ if taps is None:
+ taps = _generate_synthesis_taps(mpoints)
+
+ # pad taps to multiple of mpoints
+ r = len(taps) % mpoints
+ if r != 0:
+ taps = taps + (mpoints - r) * (0,)
+
+ # split in mpoints separate set of taps
+ sub_taps = _split_taps(taps, mpoints)
+
+ self.ss2v = gr.streams_to_vector(item_size, mpoints)
+ self.ifft = gr.fft_vcc(mpoints, False, [])
+ self.v2ss = gr.vector_to_streams(item_size, mpoints)
+ # mpoints filters go in here...
+ self.ss2s = gr.streams_to_stream(item_size, mpoints)
+
+ fg.connect(self.ss2v, self.ifft, self.v2ss)
+
+ # build mpoints fir filters...
+ for i in range(mpoints):
+ f = gr.fft_filter_ccc(1, sub_taps[i])
+ fg.connect((self.v2ss, i), f)
+ fg.connect(f, (self.ss2s, i))
+
+ gr.hier_block.__init__(self, fg, self.ss2v, self.ss2s)
+
+
+class analysis_filterbank(gr.hier_block):
+ """
+ Uniformly modulated polyphase DFT filter bank: analysis
+
+ See http://cnx.rice.edu/content/m10424/latest
+ """
+ def __init__(self, fg, mpoints, taps=None):
+ """
+ Takes 1 complex stream in, produces M complex streams out
+ that runs at 1/M times the input sample rate
+
+ @param fg: flow_graph
+ @param mpoints: number of freq bins/interpolation factor/subbands
+ @param taps: filter taps for subband filter
+
+ Same channel to frequency mapping as described above.
+ """
+ item_size = gr.sizeof_gr_complex
+
+ if taps is None:
+ taps = _generate_synthesis_taps(mpoints)
+
+ # pad taps to multiple of mpoints
+ r = len(taps) % mpoints
+ if r != 0:
+ taps = taps + (mpoints - r) * (0,)
+
+ # split in mpoints separate set of taps
+ sub_taps = _split_taps(taps, mpoints)
+
+ # print >> sys.stderr, "mpoints =", mpoints, "len(sub_taps) =", len(sub_taps)
+
+ self.s2ss = gr.stream_to_streams(item_size, mpoints)
+ # filters here
+ self.ss2v = gr.streams_to_vector(item_size, mpoints)
+ self.fft = gr.fft_vcc(mpoints, True, [])
+ self.v2ss = gr.vector_to_streams(item_size, mpoints)
+
+ # build mpoints fir filters...
+ for i in range(mpoints):
+ f = gr.fft_filter_ccc(1, sub_taps[mpoints-i-1])
+ fg.connect((self.s2ss, i), f)
+ fg.connect(f, (self.ss2v, i))
+
+ fg.connect(self.ss2v, self.fft, self.v2ss)
+ gr.hier_block.__init__(self, fg, self.s2ss, self.v2ss)
diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/fm_demod.py b/gnuradio-core/src/python/gnuradio/blksimpl/fm_demod.py
new file mode 100644
index 000000000..9487e0f0f
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blksimpl/fm_demod.py
@@ -0,0 +1,122 @@
+#
+# 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, optfir
+from gnuradio.blksimpl.fm_emph import fm_deemph
+from math import pi
+
+class fm_demod_cf(gr.hier_block):
+ """
+ Generalized FM demodulation block with deemphasis and audio
+ filtering.
+
+ This block demodulates a band-limited, complex down-converted FM
+ channel into the the original baseband signal, optionally applying
+ deemphasis. Low pass filtering is done on the resultant signal. It
+ produces an output float strem in the range of [-1.0, +1.0].
+
+ @param fg: flowgraph
+ @param channel_rate: incoming sample rate of the FM baseband
+ @type sample_rate: integer
+ @param deviation: maximum FM deviation (default = 5000)
+ @type deviation: float
+ @param audio_decim: input to output decimation rate
+ @type audio_decim: integer
+ @param audio_pass: audio low pass filter passband frequency
+ @type audio_pass: float
+ @param audio_stop: audio low pass filter stop frequency
+ @type audio_stop: float
+ @param gain: gain applied to audio output (default = 1.0)
+ @type gain: float
+ @param tau: deemphasis time constant (default = 75e-6), specify 'None'
+ to prevent deemphasis
+ """
+ def __init__(self, fg, channel_rate, audio_decim, deviation,
+ audio_pass, audio_stop, gain=1.0, tau=75e-6):
+
+ """
+ # Equalizer for ~100 us delay
+ delay = 100e-6
+ num_taps = int(channel_rate*delay)
+
+ mu = 1e-4/num_taps
+ print "CMA: delay =", delay, "n =", num_taps, "mu =", mu
+ CMA = gr.cma_equalizer_cc(num_taps, 1.0, mu)
+ """
+ k = channel_rate/(2*pi*deviation)
+ QUAD = gr.quadrature_demod_cf(k)
+
+ audio_taps = optfir.low_pass(gain, # Filter gain
+ channel_rate, # Sample rate
+ audio_pass, # Audio passband
+ audio_stop, # Audio stopband
+ 0.1, # Passband ripple
+ 60) # Stopband attenuation
+ LPF = gr.fir_filter_fff(audio_decim, audio_taps)
+
+ if tau is not None:
+ DEEMPH = fm_deemph(fg, channel_rate, tau)
+ fg.connect(QUAD, DEEMPH, LPF)
+ else:
+ fg.connect(QUAD, LPF)
+
+ gr.hier_block.__init__(self, fg, QUAD, LPF)
+
+class demod_20k0f3e_cf(fm_demod_cf):
+ """
+ NBFM demodulation block, 20 KHz channels
+
+ This block demodulates a complex, downconverted, narrowband FM
+ channel conforming to 20K0F3E emission standards, outputting
+ floats in the range [-1.0, +1.0].
+
+ @param fg: flowgraph
+ @param sample_rate: incoming sample rate of the FM baseband
+ @type sample_rate: integer
+ @param audio_decim: input to output decimation rate
+ @type audio_decim: integer
+ """
+ def __init__(self, fg, channel_rate, audio_decim):
+ fm_demod_cf.__init__(self, fg, channel_rate, audio_decim,
+ 5000, # Deviation
+ 3000, # Audio passband frequency
+ 4000) # Audio stopband frequency
+
+class demod_200kf3e_cf(fm_demod_cf):
+ """
+ WFM demodulation block, mono.
+
+ This block demodulates a complex, downconverted, wideband FM
+ channel conforming to 200KF3E emission standards, outputting
+ floats in the range [-1.0, +1.0].
+
+ @param fg: flowgraph
+ @param sample_rate: incoming sample rate of the FM baseband
+ @type sample_rate: integer
+ @param audio_decim: input to output decimation rate
+ @type audio_decim: integer
+ """
+ def __init__(self, fg, channel_rate, audio_decim):
+ fm_demod_cf.__init__(self, fg, channel_rate, audio_decim,
+ 75000, # Deviation
+ 15000, # Audio passband
+ 16000, # Audio stopband
+ 20.0) # Audio gain
diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/fm_emph.py b/gnuradio-core/src/python/gnuradio/blksimpl/fm_emph.py
new file mode 100644
index 000000000..5c256f5d0
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blksimpl/fm_emph.py
@@ -0,0 +1,145 @@
+#
+# 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
+import math
+
+
+#
+# 1
+# H(s) = -------
+# 1 + s
+#
+# tau is the RC time constant.
+# critical frequency: w_p = 1/tau
+#
+# We prewarp and use the bilinear z-transform to get our IIR coefficients.
+# See "Digital Signal Processing: A Practical Approach" by Ifeachor and Jervis
+#
+
+class fm_deemph(gr.hier_block):
+ """
+ FM Deemphasis IIR filter.
+ """
+ def __init__(self, fg, fs, tau=75e-6):
+ """
+ @param fg: flow graph
+ @type fg: gr.flow_graph
+ @param fs: sampling frequency in Hz
+ @type fs: float
+ @param tau: Time constant in seconds (75us in US, 50us in EUR)
+ @type tau: float
+ """
+ w_p = 1/tau
+ w_pp = math.tan (w_p / (fs * 2)) # prewarped analog freq
+
+ a1 = (w_pp - 1)/(w_pp + 1)
+ b0 = w_pp/(1 + w_pp)
+ b1 = b0
+
+ btaps = [b0, b1]
+ ataps = [1, a1]
+
+ if 0:
+ print "btaps =", btaps
+ print "ataps =", ataps
+ global plot1
+ plot1 = gru.gnuplot_freqz (gru.freqz (btaps, ataps), fs, True)
+
+ deemph = gr.iir_filter_ffd(btaps, ataps)
+ gr.hier_block.__init__(self, fg, deemph, deemph)
+
+#
+# 1 + s*t1
+# H(s) = ----------
+# 1 + s*t2
+#
+# I think this is the right transfer function.
+#
+#
+# This fine ASCII rendition is based on Figure 5-15
+# in "Digital and Analog Communication Systems", Leon W. Couch II
+#
+#
+# R1
+# +-----||------+
+# | |
+# o------+ +-----+--------o
+# | C1 | |
+# +----/\/\/\/--+ \
+# /
+# \ R2
+# /
+# \
+# |
+# o--------------------------+--------o
+#
+# f1 = 1/(2*pi*t1) = 1/(2*pi*R1*C)
+#
+# 1 R1 + R2
+# f2 = ------- = ------------
+# 2*pi*t2 2*pi*R1*R2*C
+#
+# t1 is 75us in US, 50us in EUR
+# f2 should be higher than our audio bandwidth.
+#
+#
+# The Bode plot looks like this:
+#
+#
+# /----------------
+# /
+# / <-- slope = 20dB/decade
+# /
+# -------------/
+# f1 f2
+#
+# We prewarp and use the bilinear z-transform to get our IIR coefficients.
+# See "Digital Signal Processing: A Practical Approach" by Ifeachor and Jervis
+#
+
+class fm_preemph(gr.hier_block):
+ """
+ FM Preemphasis IIR filter.
+ """
+ def __init__(self, fg, fs, tau=75e-6):
+ """
+ @param fg: flow graph
+ @type fg: gr.flow_graph
+ @param fs: sampling frequency in Hz
+ @type fs: float
+ @param tau: Time constant in seconds (75us in US, 50us in EUR)
+ @type tau: float
+ """
+
+ # FIXME make this compute the right answer
+
+ btaps = [1]
+ ataps = [1]
+
+ if 0:
+ print "btaps =", btaps
+ print "ataps =", ataps
+ global plot2
+ plot2 = gru.gnuplot_freqz (gru.freqz (btaps, ataps), fs, True)
+
+ preemph = gr.iir_filter_ffd(btaps, ataps)
+ gr.hier_block.__init__(self, fg, preemph, preemph)
diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/gmsk2.py b/gnuradio-core/src/python/gnuradio/blksimpl/gmsk2.py
new file mode 100644
index 000000000..68d189679
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blksimpl/gmsk2.py
@@ -0,0 +1,159 @@
+#
+# GMSK modulation and demodulation.
+#
+#
+# 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
+
+from gnuradio import gr
+from math import pi
+import Numeric
+
+# /////////////////////////////////////////////////////////////////////////////
+# GMSK mod/demod with steams of bytes as data i/o
+# /////////////////////////////////////////////////////////////////////////////
+
+class gmsk2_mod(gr.hier_block):
+
+ def __init__(self, fg, spb = 2, bt = 0.3):
+ """
+ Hierarchical block for Gaussian Minimum Shift Key (GMSK)
+ 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 bt: Gaussian filter bandwidth * symbol time
+ @type bt: float
+ """
+ if not isinstance(spb, int) or spb < 2:
+ raise TypeError, "sbp must be an integer >= 2"
+ self.spb = spb
+
+ ntaps = 4 * spb # up to 3 bits in filter at once
+ sensitivity = (pi / 2) / spb # phase change per bit = pi / 2
+
+ # Turn it into NRZ data.
+ self.nrz = gr.bytes_to_syms()
+
+ # Form Gaussian filter
+
+ # Generate Gaussian response (Needs to be convolved with window below).
+ self.gaussian_taps = gr.firdes.gaussian(
+ 1, # gain
+ spb, # symbol_rate
+ bt, # bandwidth * symbol time
+ ntaps # number of taps
+ )
+
+ self.sqwave = (1,) * spb # rectangular window
+ self.taps = Numeric.convolve(Numeric.array(self.gaussian_taps),Numeric.array(self.sqwave))
+ self.gaussian_filter = gr.interp_fir_filter_fff(spb, self.taps)
+
+ # FM modulation
+ self.fmmod = gr.frequency_modulator_fc(sensitivity)
+
+ # Connect
+ fg.connect(self.nrz, self.gaussian_filter, self.fmmod)
+
+ # Initialize base class
+ gr.hier_block.__init__(self, fg, self.nrz, self.fmmod)
+
+ 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 gmsk2_demod(gr.hier_block):
+
+ def __init__(self, fg, spb=2, omega=None, gain_mu=0.03, mu=0.5,
+ omega_relative_limit=0.000200, freq_error=0.0):
+ """
+ Hierarchical block for Gaussian Minimum Shift Key (GMSK)
+ demodulation.
+
+ The input is the complex modulated signal at baseband.
+ The output is a stream of symbols ready to be sliced at zero.
+
+ @param fg: flow graph
+ @type fg: flow graph
+ @param spb: samples per baud
+ @type spb: integer
+
+ Clock recovery parameters. These all have reasonble defaults.
+
+ @param omega: nominal relative freq (defaults to spb)
+ @type omega: float
+ @param gain_mu: controls rate of mu adjustment
+ @type gain_mu: float
+ @param mu: fractional delay [0.0, 1.0]
+ @type mu: float
+ @param omega_relative_limit: sets max variation in omega
+ @type omega_relative_limit: float, typically 0.000200 (200 ppm)
+ @param freq_error: bit rate error as a fraction
+ @param float
+ """
+ if spb < 2:
+ raise TypeError, "sbp >= 2"
+ self.spb = spb
+
+ if omega is None:
+ omega = spb*(1+freq_error)
+
+ gain_omega = .25*gain_mu*gain_mu # critically damped
+
+ # Automatic gain control
+ self.preamp = gr.multiply_const_cc(10e-5)
+ self.agc = gr.agc_cc(1e-3, 1, 1, 1000)
+
+ # Demodulate FM
+ sensitivity = (pi / 2) / spb
+ self.fmdemod = gr.quadrature_demod_cf(1.0 / sensitivity)
+
+ alpha = 0.0008
+
+ # the clock recovery block tracks the symbol clock and resamples as needed.
+ # the output of the block is a stream of soft symbols (float)
+ self.clock_recovery = gr.clock_recovery_mm_ff(omega, gain_omega, mu, gain_mu,
+ omega_relative_limit)
+
+ # slice the floats at 0, outputting 1 bit (the LSB of the output byte) per sample
+ self.slicer = gr.binary_slicer_fb()
+
+ fg.connect(self.preamp, self.agc, self.fmdemod, self.clock_recovery, self.slicer)
+
+ # Initialize base class
+ gr.hier_block.__init__(self, fg, self.preamp, self.slicer)
+
+ 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
diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/gmsk2_pkt.py b/gnuradio-core/src/python/gnuradio/blksimpl/gmsk2_pkt.py
new file mode 100644
index 000000000..af586239a
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blksimpl/gmsk2_pkt.py
@@ -0,0 +1,174 @@
+#
+# 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 math import pi
+import Numeric
+
+from gnuradio import gr, packet_utils
+import gnuradio.gr.gr_threading as _threading
+import gmsk2
+
+
+def _deprecation_warning(old_name, new_name):
+ print '#'
+ print '# Warning: %s is deprecated and will be removed soon.' % (old_name,)
+ print '# Please use the modulation independent block, %s.' % (new_name,)
+ print "#"
+
+
+# /////////////////////////////////////////////////////////////////////////////
+# GMSK mod/demod with packets as i/o
+# /////////////////////////////////////////////////////////////////////////////
+
+class gmsk2_mod_pkts(gr.hier_block):
+ """
+ GSM modulator that is a GNU Radio source.
+
+ Send packets by calling send_pkt
+ """
+ def __init__(self, fg, access_code=None, msgq_limit=2, pad_for_usrp=True, *args, **kwargs):
+ """
+ Hierarchical block for Gaussian Minimum Shift Key (GMSK) modulation.
+
+ Packets to be sent are enqueued by calling send_pkt.
+ The output is the complex modulated signal at baseband.
+
+ @param fg: flow graph
+ @type fg: flow graph
+ @param access_code: AKA sync vector
+ @type access_code: string of 1's and 0's between 1 and 64 long
+ @param msgq_limit: maximum number of messages in message queue
+ @type msgq_limit: int
+ @param pad_for_usrp: If true, packets are padded such that they end up a multiple of 128 samples
+
+ See gmsk_mod for remaining parameters
+ """
+ _deprecation_warning('gmsk2_mod_pkts', 'mod_pkts')
+
+ self.pad_for_usrp = pad_for_usrp
+ if access_code is None:
+ access_code = packet_utils.default_access_code
+ if not packet_utils.is_1_0_string(access_code):
+ raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,)
+ self._access_code = access_code
+
+ # accepts messages from the outside world
+ self.pkt_input = gr.message_source(gr.sizeof_char, msgq_limit)
+ self.gmsk_mod = gmsk2.gmsk2_mod(fg, *args, **kwargs)
+ fg.connect(self.pkt_input, self.gmsk_mod)
+ gr.hier_block.__init__(self, fg, None, self.gmsk_mod)
+
+ def send_pkt(self, payload='', eof=False):
+ """
+ Send the payload.
+
+ @param payload: data to send
+ @type payload: string
+ """
+ if eof:
+ msg = gr.message(1) # tell self.pkt_input we're not sending any more packets
+ else:
+ # print "original_payload =", string_to_hex_list(payload)
+ pkt = packet_utils.make_packet(payload,
+ self.gmsk_mod.samples_per_baud(),
+ self.gmsk_mod.bits_per_baud(),
+ self._access_code,
+ self.pad_for_usrp)
+ #print "pkt =", string_to_hex_list(pkt)
+ msg = gr.message_from_string(pkt)
+ self.pkt_input.msgq().insert_tail(msg)
+
+
+
+class gmsk2_demod_pkts(gr.hier_block):
+ """
+ GSM demodulator that is a GNU Radio sink.
+
+ The input is complex baseband. When packets are demodulated, they are passed to the
+ app via the callback.
+ """
+
+ def __init__(self, fg, access_code=None, callback=None, threshold=-1, *args, **kwargs):
+ """
+ Hierarchical block for Gaussian Minimum Shift Key (GMSK)
+ demodulation.
+
+ The input is the complex modulated signal at baseband.
+ Demodulated packets are sent to the handler.
+
+ @param fg: flow graph
+ @type fg: flow graph
+ @param access_code: AKA sync vector
+ @type access_code: string of 1's and 0's
+ @param callback: function of two args: ok, payload
+ @type callback: ok: bool; payload: string
+ @param threshold: detect access_code with up to threshold bits wrong (-1 -> use default)
+ @type threshold: int
+
+ See gmsk_demod for remaining parameters.
+ """
+
+ _deprecation_warning('gmsk2_demod_pkts', 'demod_pkts')
+
+ if access_code is None:
+ access_code = packet_utils.default_access_code
+ if not packet_utils.is_1_0_string(access_code):
+ raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,)
+ self._access_code = access_code
+
+ if threshold == -1:
+ threshold = 12 # FIXME raise exception
+
+ self._rcvd_pktq = gr.msg_queue() # holds packets from the PHY
+ self.gmsk_demod = gmsk2.gmsk2_demod(fg, *args, **kwargs)
+ self.correlator = gr.correlate_access_code_bb(access_code, threshold)
+
+ self.framer_sink = gr.framer_sink_1(self._rcvd_pktq)
+ fg.connect(self.gmsk_demod, self.correlator, self.framer_sink)
+
+ gr.hier_block.__init__(self, fg, self.gmsk_demod, None)
+ self._watcher = _queue_watcher_thread(self._rcvd_pktq, callback)
+
+ def carrier_sensed(self):
+ """
+ Return True if we detect carrier.
+ """
+ return False # FIXME
+
+
+class _queue_watcher_thread(_threading.Thread):
+ def __init__(self, rcvd_pktq, callback):
+ _threading.Thread.__init__(self)
+ self.setDaemon(1)
+ self.rcvd_pktq = rcvd_pktq
+ self.callback = callback
+ self.keep_running = True
+ self.start()
+
+ #def stop(self):
+ # self.keep_running = False
+
+ def run(self):
+ while self.keep_running:
+ msg = self.rcvd_pktq.delete_head()
+ ok, payload = packet_utils.unmake_packet(msg.to_string())
+ if self.callback:
+ self.callback(ok, payload)
diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/nbfm_rx.py b/gnuradio-core/src/python/gnuradio/blksimpl/nbfm_rx.py
new file mode 100644
index 000000000..39059ec9c
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blksimpl/nbfm_rx.py
@@ -0,0 +1,87 @@
+#
+# Copyright 2005 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+import math
+from gnuradio import gr, optfir
+from gnuradio.blksimpl.fm_emph import fm_deemph
+from gnuradio.blksimpl.standard_squelch import standard_squelch
+
+class nbfm_rx(gr.hier_block):
+ def __init__(self, fg, audio_rate, quad_rate, tau=75e-6, max_dev=5e3):
+ """
+ Narrow Band FM Receiver.
+
+ Takes a single complex baseband input stream and produces a single
+ float output stream of audio sample in the range [-1, +1].
+
+ @param fg: flow graph
+ @param audio_rate: sample rate of audio stream, >= 16k
+ @type audio_rate: integer
+ @param quad_rate: sample rate of output stream
+ @type quad_rate: integer
+ @param tau: preemphasis time constant (default 75e-6)
+ @type tau: float
+ @param max_dev: maximum deviation in Hz (default 5e3)
+ @type max_dev: float
+
+ quad_rate must be an integer multiple of audio_rate.
+
+ Exported sub-blocks (attributes):
+ squelch
+ quad_demod
+ deemph
+ audio_filter
+ """
+
+ # FIXME audio_rate and quad_rate ought to be exact rationals
+ audio_rate = int(audio_rate)
+ quad_rate = int(quad_rate)
+
+ if quad_rate % audio_rate != 0:
+ raise ValueError, "quad_rate is not an integer multiple of audio_rate"
+
+ squelch_threshold = 20 # dB
+ #self.squelch = gr.simple_squelch_cc(squelch_threshold, 0.001)
+
+ # FM Demodulator input: complex; output: float
+ k = quad_rate/(2*math.pi*max_dev)
+ self.quad_demod = gr.quadrature_demod_cf(k)
+
+ # FM Deemphasis IIR filter
+ self.deemph = fm_deemph (fg, quad_rate, tau=tau)
+
+ # compute FIR taps for audio filter
+ audio_decim = quad_rate // audio_rate
+ audio_taps = gr.firdes.low_pass (1.0, # gain
+ quad_rate, # sampling rate
+ 4.5e3, # Audio LPF cutoff
+ 2.5e3, # Transition band
+ gr.firdes.WIN_HAMMING) # filter type
+
+ print "len(audio_taps) =", len(audio_taps)
+
+ # Decimating audio filter
+ # input: float; output: float; taps: float
+ self.audio_filter = gr.fir_filter_fff(audio_decim, audio_taps)
+
+ fg.connect(self.quad_demod, self.deemph, self.audio_filter)
+
+ gr.hier_block.__init__(self, fg, self.quad_demod, self.audio_filter)
diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/nbfm_tx.py b/gnuradio-core/src/python/gnuradio/blksimpl/nbfm_tx.py
new file mode 100644
index 000000000..2f636b67f
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blksimpl/nbfm_tx.py
@@ -0,0 +1,95 @@
+#
+# Copyright 2005 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+import math
+from gnuradio import gr, optfir
+from gnuradio.blksimpl.fm_emph import fm_preemph
+
+#from gnuradio import ctcss
+
+class nbfm_tx(gr.hier_block):
+ def __init__(self, fg, audio_rate, quad_rate, tau=75e-6, max_dev=5e3):
+ """
+ Narrow Band FM Transmitter.
+
+ Takes a single float input stream of audio samples in the range [-1,+1]
+ and produces a single FM modulated complex baseband output.
+
+ @param fg: flow graph
+ @param audio_rate: sample rate of audio stream, >= 16k
+ @type audio_rate: integer
+ @param quad_rate: sample rate of output stream
+ @type quad_rate: integer
+ @param tau: preemphasis time constant (default 75e-6)
+ @type tau: float
+ @param max_dev: maximum deviation in Hz (default 5e3)
+ @type max_dev: float
+
+ quad_rate must be an integer multiple of audio_rate.
+ """
+
+ # FIXME audio_rate and quad_rate ought to be exact rationals
+ audio_rate = int(audio_rate)
+ quad_rate = int(quad_rate)
+
+ if quad_rate % audio_rate != 0:
+ raise ValueError, "quad_rate is not an integer multiple of audio_rate"
+
+
+ do_interp = audio_rate != quad_rate
+
+ if do_interp:
+ interp_factor = quad_rate / audio_rate
+ interp_taps = optfir.low_pass (interp_factor, # gain
+ quad_rate, # Fs
+ 4500, # passband cutoff
+ 7000, # stopband cutoff
+ 0.1, # passband ripple dB
+ 40) # stopband atten dB
+
+ #print "len(interp_taps) =", len(interp_taps)
+ self.interpolator = gr.interp_fir_filter_fff (interp_factor, interp_taps)
+
+ self.preemph = fm_preemph (fg, quad_rate, tau=tau)
+
+ k = 2 * math.pi * max_dev / quad_rate
+ self.modulator = gr.frequency_modulator_fc (k)
+
+ if do_interp:
+ fg.connect (self.interpolator, self.preemph, self.modulator)
+ gr.hier_block.__init__(self, fg, self.interpolator, self.modulator)
+ else:
+ fg.connect(self.preemph, self.modulator)
+ gr.hier_block.__init__(self, fg, self.preemph, self.modulator)
+
+
+#class ctcss_gen_f(gr.sig_source_f):
+# def __init__(self, sample_rate, tone_freq):
+# gr.sig_source_f.__init__(self, sample_rate, gr.SIN_WAVE, tone_freq, 0.1, 0.0)
+#
+# def set_tone (self, tone):
+# gr.sig_source_f.set_frequency(self,tone)
+
+class ctcss_gen_f(gr.hier_block):
+ def __init__(self, fg, sample_rate, tone_freq):
+ self.plgen = gr.sig_source_f(sample_rate, gr.GR_SIN_WAVE, tone_freq, 0.1, 0.0)
+
+ gr.hier_block.__init__(self, fg, self.plgen, self.plgen)
diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/pkt.py b/gnuradio-core/src/python/gnuradio/blksimpl/pkt.py
new file mode 100644
index 000000000..3ebb7229c
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blksimpl/pkt.py
@@ -0,0 +1,156 @@
+#
+# 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 math import pi
+import Numeric
+
+from gnuradio import gr, packet_utils
+import gnuradio.gr.gr_threading as _threading
+
+
+# /////////////////////////////////////////////////////////////////////////////
+# mod/demod with packets as i/o
+# /////////////////////////////////////////////////////////////////////////////
+
+class mod_pkts(gr.hier_block):
+ """
+ Wrap an arbitrary digital modulator in our packet handling framework.
+
+ Send packets by calling send_pkt
+ """
+ def __init__(self, fg, modulator, access_code=None, msgq_limit=2, pad_for_usrp=True):
+ """
+ Hierarchical block for sending packets
+
+ Packets to be sent are enqueued by calling send_pkt.
+ The output is the complex modulated signal at baseband.
+
+ @param fg: flow graph
+ @type fg: flow graph
+ @param modulator: instance of modulator class (gr_block or hier_block)
+ @type modulator: complex baseband out
+ @param access_code: AKA sync vector
+ @type access_code: string of 1's and 0's between 1 and 64 long
+ @param msgq_limit: maximum number of messages in message queue
+ @type msgq_limit: int
+ @param pad_for_usrp: If true, packets are padded such that they end up a multiple of 128 samples
+
+ See gmsk_mod for remaining parameters
+ """
+ self._modulator = modulator
+ self._pad_for_usrp = pad_for_usrp
+
+ if access_code is None:
+ access_code = packet_utils.default_access_code
+ if not packet_utils.is_1_0_string(access_code):
+ raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,)
+ self._access_code = access_code
+
+ # accepts messages from the outside world
+ self._pkt_input = gr.message_source(gr.sizeof_char, msgq_limit)
+ fg.connect(self._pkt_input, self._modulator)
+ gr.hier_block.__init__(self, fg, None, self._modulator)
+
+ def send_pkt(self, payload='', eof=False):
+ """
+ Send the payload.
+
+ @param payload: data to send
+ @type payload: string
+ """
+ if eof:
+ msg = gr.message(1) # tell self._pkt_input we're not sending any more packets
+ else:
+ # print "original_payload =", string_to_hex_list(payload)
+ pkt = packet_utils.make_packet(payload,
+ self._modulator.samples_per_baud(),
+ self._modulator.bits_per_baud(),
+ self._access_code,
+ self._pad_for_usrp)
+ #print "pkt =", string_to_hex_list(pkt)
+ msg = gr.message_from_string(pkt)
+ self._pkt_input.msgq().insert_tail(msg)
+
+
+
+class demod_pkts(gr.hier_block):
+ """
+ Wrap an arbitrary digital demodulator in our packet handling framework.
+
+ The input is complex baseband. When packets are demodulated, they are passed to the
+ app via the callback.
+ """
+
+ def __init__(self, fg, demodulator, access_code=None, callback=None, threshold=-1):
+ """
+ Hierarchical block for demodulating and deframing packets.
+
+ The input is the complex modulated signal at baseband.
+ Demodulated packets are sent to the handler.
+
+ @param fg: flow graph
+ @type fg: flow graph
+ @param demodulator: instance of demodulator class (gr_block or hier_block)
+ @type demodulator: complex baseband in
+ @param access_code: AKA sync vector
+ @type access_code: string of 1's and 0's
+ @param callback: function of two args: ok, payload
+ @type callback: ok: bool; payload: string
+ @param threshold: detect access_code with up to threshold bits wrong (-1 -> use default)
+ @type threshold: int
+ """
+
+ self._demodulator = demodulator
+ if access_code is None:
+ access_code = packet_utils.default_access_code
+ if not packet_utils.is_1_0_string(access_code):
+ raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,)
+ self._access_code = access_code
+
+ if threshold == -1:
+ threshold = 12 # FIXME raise exception
+
+ self._rcvd_pktq = gr.msg_queue() # holds packets from the PHY
+ self.correlator = gr.correlate_access_code_bb(access_code, threshold)
+
+ self.framer_sink = gr.framer_sink_1(self._rcvd_pktq)
+ fg.connect(self._demodulator, self.correlator, self.framer_sink)
+
+ gr.hier_block.__init__(self, fg, self._demodulator, None)
+ self._watcher = _queue_watcher_thread(self._rcvd_pktq, callback)
+
+
+class _queue_watcher_thread(_threading.Thread):
+ def __init__(self, rcvd_pktq, callback):
+ _threading.Thread.__init__(self)
+ self.setDaemon(1)
+ self.rcvd_pktq = rcvd_pktq
+ self.callback = callback
+ self.keep_running = True
+ self.start()
+
+
+ def run(self):
+ while self.keep_running:
+ msg = self.rcvd_pktq.delete_head()
+ ok, payload = packet_utils.unmake_packet(msg.to_string())
+ if self.callback:
+ self.callback(ok, payload)
diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/rational_resampler.py b/gnuradio-core/src/python/gnuradio/blksimpl/rational_resampler.py
new file mode 100644
index 000000000..8b928b102
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blksimpl/rational_resampler.py
@@ -0,0 +1,137 @@
+#
+# 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
+
+_plot = None
+
+def design_filter(interpolation, decimation, fractional_bw):
+ """
+ Given the interpolation rate, decimation rate and a fractional bandwidth,
+ design a set of taps.
+
+ @param interpolation: interpolation factor
+ @type interpolation: integer > 0
+ @param decimation: decimation factor
+ @type decimation: integer > 0
+ @param fractional_bw: fractional bandwidth in (0, 0.5) 0.4 works well.
+ @type fractional_bw: float
+ @returns: sequence of numbers
+ """
+
+ global _plot
+
+ if fractional_bw >= 0.5 or fractional_bw <= 0:
+ raise ValueError, "Invalid fractional_bandwidth, must be in (0, 0.5)"
+
+ beta = 5.0
+ trans_width = 0.5 - fractional_bw
+ mid_transition_band = 0.5 - trans_width/2
+
+ taps = gr.firdes.low_pass(interpolation, # gain
+ 1, # Fs
+ mid_transition_band/interpolation, # trans mid point
+ trans_width/interpolation, # transition width
+ gr.firdes.WIN_KAISER,
+ beta # beta
+ )
+ # print "len(resampler_taps) =", len(taps)
+ # _plot = gru.gnuplot_freqz(gru.freqz(taps, 1), 1)
+
+ return taps
+
+
+
+class _rational_resampler_base(gr.hier_block):
+ """
+ base class for all rational resampler variants.
+ """
+ def __init__(self, resampler_base, fg,
+ interpolation, decimation, taps=None, fractional_bw=None):
+ """
+ Rational resampling polyphase FIR filter.
+
+ Either taps or fractional_bw may be specified, but not both.
+ If neither is specified, a reasonable default, 0.4, is used as
+ the fractional_bw.
+
+ @param fg: flow graph
+ @param interpolation: interpolation factor
+ @type interpolation: integer > 0
+ @param decimation: decimation factor
+ @type decimation: integer > 0
+ @param taps: optional filter coefficients
+ @type taps: sequence
+ @param fractional_bw: fractional bandwidth in (0, 0.5), measured at final freq (use 0.4)
+ @type fractional_bw: float
+ """
+
+ if not isinstance(interpolation, int) or interpolation < 1:
+ raise ValueError, "interpolation must be an integer >= 1"
+
+ if not isinstance(decimation, int) or decimation < 1:
+ raise ValueError, "decimation must be an integer >= 1"
+
+ if taps is None and fractional_bw is None:
+ fractional_bw = 0.4
+
+ d = gru.gcd(interpolation, decimation)
+ interpolation = interpolation // d
+ decimation = decimation // d
+
+ if taps is None:
+ taps = design_filter(interpolation, decimation, fractional_bw)
+
+ resampler = resampler_base(interpolation, decimation, taps)
+ gr.hier_block.__init__(self, fg, resampler, resampler)
+
+
+
+class rational_resampler_fff(_rational_resampler_base):
+ def __init__(self, fg, interpolation, decimation, taps=None, fractional_bw=None):
+ """
+ Rational resampling polyphase FIR filter with
+ float input, float output and float taps.
+ """
+ _rational_resampler_base.__init__(self, gr.rational_resampler_base_fff, fg,
+ interpolation, decimation,
+ taps, fractional_bw)
+
+class rational_resampler_ccf(_rational_resampler_base):
+ def __init__(self, fg, interpolation, decimation, taps=None, fractional_bw=None):
+ """
+ Rational resampling polyphase FIR filter with
+ complex input, complex output and float taps.
+ """
+ _rational_resampler_base.__init__(self, gr.rational_resampler_base_ccf, fg,
+ interpolation, decimation,
+ taps, fractional_bw)
+
+class rational_resampler_ccc(_rational_resampler_base):
+ def __init__(self, fg, interpolation, decimation, taps=None, fractional_bw=None):
+ """
+ Rational resampling polyphase FIR filter with
+ complex input, complex output and complex taps.
+ """
+ _rational_resampler_base.__init__(self, gr.rational_resampler_base_ccc, fg,
+ interpolation, decimation,
+ taps, fractional_bw)
+
diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/standard_squelch.py b/gnuradio-core/src/python/gnuradio/blksimpl/standard_squelch.py
new file mode 100644
index 000000000..2c80dd5af
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blksimpl/standard_squelch.py
@@ -0,0 +1,73 @@
+#
+# Copyright 2005 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+import math
+from gnuradio import gr, optfir
+
+class standard_squelch(gr.hier_block):
+ def __init__(self, fg, audio_rate):
+
+ self.input_node = gr.add_const_ff(0) # FIXME kludge
+
+ self.low_iir = gr.iir_filter_ffd((0.0193,0,-0.0193),(1,1.9524,-0.9615))
+ self.low_square = gr.multiply_ff()
+ self.low_smooth = gr.single_pole_iir_filter_ff(1/(0.01*audio_rate)) # 100ms time constant
+
+ self.hi_iir = gr.iir_filter_ffd((0.0193,0,-0.0193),(1,1.3597,-0.9615))
+ self.hi_square = gr.multiply_ff()
+ self.hi_smooth = gr.single_pole_iir_filter_ff(1/(0.01*audio_rate))
+
+ self.sub = gr.sub_ff();
+ self.add = gr.add_ff();
+ self.gate = gr.threshold_ff(0.3,0.43,0)
+ self.squelch_lpf = gr.single_pole_iir_filter_ff(1/(0.01*audio_rate))
+
+ self.div = gr.divide_ff()
+ self.squelch_mult = gr.multiply_ff()
+
+ fg.connect (self.input_node, (self.squelch_mult, 0))
+
+ fg.connect (self.input_node,self.low_iir)
+ fg.connect (self.low_iir,(self.low_square,0))
+ fg.connect (self.low_iir,(self.low_square,1))
+ fg.connect (self.low_square,self.low_smooth,(self.sub,0))
+ fg.connect (self.low_smooth, (self.add,0))
+
+ fg.connect (self.input_node,self.hi_iir)
+ fg.connect (self.hi_iir,(self.hi_square,0))
+ fg.connect (self.hi_iir,(self.hi_square,1))
+ fg.connect (self.hi_square,self.hi_smooth,(self.sub,1))
+ fg.connect (self.hi_smooth, (self.add,1))
+
+ fg.connect (self.sub, (self.div, 0))
+ fg.connect (self.add, (self.div, 1))
+ fg.connect (self.div, self.gate, self.squelch_lpf, (self.squelch_mult,1))
+
+ gr.hier_block.__init__(self, fg, self.input_node, self.squelch_mult)
+
+ def set_threshold(self, threshold):
+ self.gate.set_hi(threshold)
+
+ def threshold(self):
+ return self.gate.hi()
+
+ def squelch_range(self):
+ return (0.0, 1.0, 1.0/100)
diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/wfm_rcv.py b/gnuradio-core/src/python/gnuradio/blksimpl/wfm_rcv.py
new file mode 100644
index 000000000..55dbbaa0c
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blksimpl/wfm_rcv.py
@@ -0,0 +1,72 @@
+#
+# 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
+from gnuradio.blksimpl.fm_emph import fm_deemph
+import math
+
+class wfm_rcv(gr.hier_block):
+ def __init__ (self, fg, quad_rate, audio_decimation):
+ """
+ Hierarchical block for demodulating a broadcast FM signal.
+
+ The input is the downconverted complex baseband signal (gr_complex).
+ The output is the demodulated audio (float).
+
+ @param fg: flow graph.
+ @type fg: flow graph
+ @param quad_rate: input sample rate of complex baseband input.
+ @type quad_rate: float
+ @param audio_decimation: how much to decimate quad_rate to get to audio.
+ @type audio_decimation: integer
+ """
+ volume = 20.
+
+ max_dev = 75e3
+ fm_demod_gain = quad_rate/(2*math.pi*max_dev)
+ audio_rate = quad_rate / audio_decimation
+
+
+ # We assign to self so that outsiders can grab the demodulator
+ # if they need to. E.g., to plot its output.
+ #
+ # input: complex; output: float
+ self.fm_demod = gr.quadrature_demod_cf (fm_demod_gain)
+
+ # input: float; output: float
+ self.deemph = fm_deemph (fg, audio_rate)
+
+ # 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
+ self.audio_filter = gr.fir_filter_fff (audio_decimation, audio_coeffs)
+
+ fg.connect (self.fm_demod, self.audio_filter, self.deemph)
+
+ gr.hier_block.__init__(self,
+ fg,
+ self.fm_demod, # head of the pipeline
+ self.deemph) # tail of the pipeline
diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/wfm_rcv_pll.py b/gnuradio-core/src/python/gnuradio/blksimpl/wfm_rcv_pll.py
new file mode 100644
index 000000000..d116090e0
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blksimpl/wfm_rcv_pll.py
@@ -0,0 +1,206 @@
+#
+# 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
+from gnuradio.blksimpl.fm_emph import fm_deemph
+import math
+
+class wfm_rcv_pll(gr.hier_block):
+ def __init__ (self, fg, demod_rate, audio_decimation):
+ """
+ Hierarchical block for demodulating a broadcast FM signal.
+
+ The input is the downconverted complex baseband signal (gr_complex).
+ The output is two streams of the demodulated audio (float) 0=Left, 1=Right.
+
+ @param fg: flow graph.
+ @type fg: flow graph
+ @param demod_rate: input sample rate of complex baseband input.
+ @type demod_rate: float
+ @param audio_decimation: how much to decimate demod_rate to get to audio.
+ @type audio_decimation: integer
+ """
+
+ bandwidth = 200e3
+ audio_rate = demod_rate / audio_decimation
+
+
+ # We assign to self so that outsiders can grab the demodulator
+ # if they need to. E.g., to plot its output.
+ #
+ # input: complex; output: float
+ alpha = 0.25*bandwidth * math.pi / demod_rate
+ beta = alpha * alpha / 4.0
+ max_freq = 2.0*math.pi*100e3/demod_rate
+
+ self.fm_demod = gr.pll_freqdet_cf (alpha,beta,max_freq,-max_freq)
+
+ # input: float; output: float
+ self.deemph_Left = fm_deemph (fg, audio_rate)
+ self.deemph_Right = fm_deemph (fg, audio_rate)
+
+ # compute FIR filter taps for audio filter
+ width_of_transition_band = audio_rate / 32
+ audio_coeffs = gr.firdes.low_pass (1.0 , # gain
+ demod_rate, # sampling rate
+ 15000 ,
+ width_of_transition_band,
+ gr.firdes.WIN_HAMMING)
+ # input: float; output: float
+ self.audio_filter = gr.fir_filter_fff (audio_decimation, audio_coeffs)
+ if 1:
+ # Pick off the stereo carrier/2 with this filter. It attenuated 10 dB so apply 10 dB gain
+ # We pick off the negative frequency half because we want to base band by it!
+ ## NOTE THIS WAS HACKED TO OFFSET INSERTION LOSS DUE TO DEEMPHASIS
+
+ stereo_carrier_filter_coeffs = gr.firdes.complex_band_pass(10.0,
+ demod_rate,
+ -19020,
+ -18980,
+ width_of_transition_band,
+ gr.firdes.WIN_HAMMING)
+
+ #print "len stereo carrier filter = ",len(stereo_carrier_filter_coeffs)
+ #print "stereo carrier filter ", stereo_carrier_filter_coeffs
+ #print "width of transition band = ",width_of_transition_band, " audio rate = ", audio_rate
+
+ # Pick off the double side band suppressed carrier Left-Right audio. It is attenuated 10 dB so apply 10 dB gain
+
+ stereo_dsbsc_filter_coeffs = gr.firdes.complex_band_pass(20.0,
+ demod_rate,
+ 38000-15000/2,
+ 38000+15000/2,
+ width_of_transition_band,
+ gr.firdes.WIN_HAMMING)
+ #print "len stereo dsbsc filter = ",len(stereo_dsbsc_filter_coeffs)
+ #print "stereo dsbsc filter ", stereo_dsbsc_filter_coeffs
+ # construct overlap add filter system from coefficients for stereo carrier
+
+ self.stereo_carrier_filter = gr.fir_filter_fcc(audio_decimation, stereo_carrier_filter_coeffs)
+
+ # carrier is twice the picked off carrier so arrange to do a commplex multiply
+
+ self.stereo_carrier_generator = gr.multiply_cc();
+
+ # Pick off the rds signal
+
+ stereo_rds_filter_coeffs = gr.firdes.complex_band_pass(30.0,
+ demod_rate,
+ 57000 - 1500,
+ 57000 + 1500,
+ width_of_transition_band,
+ gr.firdes.WIN_HAMMING)
+ #print "len stereo dsbsc filter = ",len(stereo_dsbsc_filter_coeffs)
+ #print "stereo dsbsc filter ", stereo_dsbsc_filter_coeffs
+ # construct overlap add filter system from coefficients for stereo carrier
+
+ self.stereo_carrier_filter = gr.fir_filter_fcc(audio_decimation, stereo_carrier_filter_coeffs)
+ self.rds_signal_filter = gr.fir_filter_fcc(audio_decimation, stereo_rds_filter_coeffs)
+
+
+
+
+
+
+ self.rds_carrier_generator = gr.multiply_cc();
+ self.rds_signal_generator = gr.multiply_cc();
+ self_rds_signal_processor = gr.null_sink(gr.sizeof_gr_complex);
+
+
+
+ alpha = 5 * 0.25 * math.pi / (audio_rate)
+ beta = alpha * alpha / 4.0
+ max_freq = -2.0*math.pi*18990/audio_rate;
+ min_freq = -2.0*math.pi*19010/audio_rate;
+
+ self.stereo_carrier_pll_recovery = gr.pll_carriertracking_cc(alpha,beta,max_freq,min_freq);
+ self.stereo_carrier_pll_recovery.squelch_enable(False);
+
+
+ # set up mixer (multiplier) to get the L-R signal at baseband
+
+ self.stereo_basebander = gr.multiply_cc();
+
+ # pick off the real component of the basebanded L-R signal. The imaginary SHOULD be zero
+
+ self.LmR_real = gr.complex_to_real();
+ self.Make_Left = gr.add_ff();
+ self.Make_Right = gr.sub_ff();
+
+ self.stereo_dsbsc_filter = gr.fir_filter_fcc(audio_decimation, stereo_dsbsc_filter_coeffs)
+
+
+ if 1:
+
+ # send the real signal to complex filter to pick off the carrier and then to one side of a multiplier
+ fg.connect (self.fm_demod,self.stereo_carrier_filter,self.stereo_carrier_pll_recovery, (self.stereo_carrier_generator,0))
+ # send the already filtered carrier to the otherside of the carrier
+ fg.connect (self.stereo_carrier_pll_recovery, (self.stereo_carrier_generator,1))
+ # the resulting signal from this multiplier is the carrier with correct phase but at -38000 Hz.
+
+ # send the new carrier to one side of the mixer (multiplier)
+ fg.connect (self.stereo_carrier_generator, (self.stereo_basebander,0))
+ # send the demphasized audio to the DSBSC pick off filter, the complex
+ # DSBSC signal at +38000 Hz is sent to the other side of the mixer/multiplier
+ fg.connect (self.fm_demod,self.stereo_dsbsc_filter, (self.stereo_basebander,1))
+ # the result is BASEBANDED DSBSC with phase zero!
+
+ # Pick off the real part since the imaginary is theoretically zero and then to one side of a summer
+ fg.connect (self.stereo_basebander, self.LmR_real, (self.Make_Left,0))
+ #take the same real part of the DSBSC baseband signal and send it to negative side of a subtracter
+ fg.connect (self.LmR_real,(self.Make_Right,1))
+
+ # Make rds carrier by taking the squared pilot tone and multiplying by pilot tone
+ fg.connect (self.stereo_basebander,(self.rds_carrier_generator,0))
+ fg.connect (self.stereo_carrier_pll_recovery,(self.rds_carrier_generator,1))
+ # take signal, filter off rds, send into mixer 0 channel
+ fg.connect (self.fm_demod,self.rds_signal_filter,(self.rds_signal_generator,0))
+ # take rds_carrier_generator output and send into mixer 1 channel
+ fg.connect (self.rds_carrier_generator,(self.rds_signal_generator,1))
+ # send basebanded rds signal and send into "processor" which for now is a null sink
+ fg.connect (self.rds_signal_generator,self_rds_signal_processor)
+
+
+ if 1:
+ # pick off the audio, L+R that is what we used to have and send it to the summer
+ fg.connect(self.fm_demod, self.audio_filter, (self.Make_Left, 1))
+ # take the picked off L+R audio and send it to the PLUS side of the subtractor
+ fg.connect(self.audio_filter,(self.Make_Right, 0))
+ # The result of Make_Left gets (L+R) + (L-R) and results in 2*L
+ # The result of Make_Right gets (L+R) - (L-R) and results in 2*R
+
+
+ # kludge the signals into a stereo channel
+ kludge = gr.kludge_copy(gr.sizeof_float)
+ fg.connect(self.Make_Left , self.deemph_Left, (kludge, 0))
+ fg.connect(self.Make_Right, self.deemph_Right, (kludge, 1))
+
+ #send it to the audio system
+ gr.hier_block.__init__(self,
+ fg,
+ self.fm_demod, # head of the pipeline
+ kludge) # tail of the pipeline
+ else:
+ fg.connect (self.fm_demod, self.audio_filter)
+ gr.hier_block.__init__(self,
+ fg,
+ self.fm_demod, # head of the pipeline
+ self.audio_filter) # tail of the pipeline
diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/wfm_tx.py b/gnuradio-core/src/python/gnuradio/blksimpl/wfm_tx.py
new file mode 100644
index 000000000..505455571
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blksimpl/wfm_tx.py
@@ -0,0 +1,79 @@
+#
+# Copyright 2005 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+import math
+from gnuradio import gr, optfir
+from gnuradio.blksimpl.fm_emph import fm_preemph
+
+class wfm_tx(gr.hier_block):
+ def __init__(self, fg, audio_rate, quad_rate, tau=75e-6, max_dev=75e3):
+ """
+ Wide Band FM Transmitter.
+
+ Takes a single float input stream of audio samples in the range [-1,+1]
+ and produces a single FM modulated complex baseband output.
+
+ @param fg: flow graph
+ @param audio_rate: sample rate of audio stream, >= 16k
+ @type audio_rate: integer
+ @param quad_rate: sample rate of output stream
+ @type quad_rate: integer
+ @param tau: preemphasis time constant (default 75e-6)
+ @type tau: float
+ @param max_dev: maximum deviation in Hz (default 75e3)
+ @type max_dev: float
+
+ quad_rate must be an integer multiple of audio_rate.
+ """
+
+ # FIXME audio_rate and quad_rate ought to be exact rationals
+ audio_rate = int(audio_rate)
+ quad_rate = int(quad_rate)
+
+ if quad_rate % audio_rate != 0:
+ raise ValueError, "quad_rate is not an integer multiple of audio_rate"
+
+
+ do_interp = audio_rate != quad_rate
+
+ if do_interp:
+ interp_factor = quad_rate / audio_rate
+ interp_taps = optfir.low_pass (interp_factor, # gain
+ quad_rate, # Fs
+ 16000, # passband cutoff
+ 18000, # stopband cutoff
+ 0.1, # passband ripple dB
+ 40) # stopband atten dB
+
+ print "len(interp_taps) =", len(interp_taps)
+ self.interpolator = gr.interp_fir_filter_fff (interp_factor, interp_taps)
+
+ self.preemph = fm_preemph (fg, quad_rate, tau=tau)
+
+ k = 2 * math.pi * max_dev / quad_rate
+ self.modulator = gr.frequency_modulator_fc (k)
+
+ if do_interp:
+ fg.connect (self.interpolator, self.preemph, self.modulator)
+ gr.hier_block.__init__(self, fg, self.interpolator, self.modulator)
+ else:
+ fg.connect(self.preemph, self.modulator)
+ gr.hier_block.__init__(self, fg, self.preemph, self.modulator)
diff --git a/gnuradio-core/src/python/gnuradio/eng_notation.py b/gnuradio-core/src/python/gnuradio/eng_notation.py
new file mode 100644
index 000000000..72cd8931e
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/eng_notation.py
@@ -0,0 +1,71 @@
+#
+# Copyright 2003 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.
+#
+
+scale_factor = {}
+scale_factor['E'] = 1e18
+scale_factor['P'] = 1e15
+scale_factor['T'] = 1e12
+scale_factor['G'] = 1e9
+scale_factor['M'] = 1e6
+scale_factor['k'] = 1e3
+scale_factor['m'] = 1e-3
+scale_factor['u'] = 1e-6
+scale_factor['n'] = 1e-9
+scale_factor['p'] = 1e-12
+scale_factor['f'] = 1e-15
+scale_factor['a'] = 1e-18
+
+def num_to_str (n):
+ '''Convert a number to a string in engineering notation. E.g., 5e-9 -> 5n'''
+ m = abs(n)
+ if m >= 1e9:
+ return "%gG" % (n * 1e-9)
+ elif m >= 1e6:
+ return "%gM" % (n * 1e-6)
+ elif m >= 1e3:
+ return "%gk" % (n * 1e-3)
+ elif m >= 1:
+ return "%g" % (n)
+ elif m >= 1e-3:
+ return "%gm" % (n * 1e3)
+ elif m >= 1e-6:
+ return "%gu" % (n * 1e6) # where's that mu when you need it (unicode?)
+ elif m >= 1e-9:
+ return "%gn" % (n * 1e9)
+ elif m >= 1e-12:
+ return "%gp" % (n * 1e12)
+ elif m >= 1e-15:
+ return "%gf" % (n * 1e15)
+ else:
+ return "%g" % (n)
+
+
+def str_to_num (value):
+ '''Convert a string in engineering notation to a number. E.g., '15m' -> 15e-3'''
+ try:
+ scale = 1.0
+ suffix = value[-1]
+ if scale_factor.has_key (suffix):
+ return float (value[0:-1]) * scale_factor[suffix]
+ return float (value)
+ except:
+ raise RuntimeError (
+ "Invalid engineering notation value: %r" % (value,))
diff --git a/gnuradio-core/src/python/gnuradio/eng_option.py b/gnuradio-core/src/python/gnuradio/eng_option.py
new file mode 100644
index 000000000..3e25c5788
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/eng_option.py
@@ -0,0 +1,80 @@
+#
+# 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.
+#
+
+'''Add support for engineering notation to optparse.OptionParser'''
+
+from copy import copy
+from optparse import Option, OptionValueError
+
+scale_factor = {}
+scale_factor['E'] = 1e18
+scale_factor['P'] = 1e15
+scale_factor['T'] = 1e12
+scale_factor['G'] = 1e9
+scale_factor['M'] = 1e6
+scale_factor['k'] = 1e3
+scale_factor['m'] = 1e-3
+scale_factor['u'] = 1e-6
+scale_factor['n'] = 1e-9
+scale_factor['p'] = 1e-12
+scale_factor['f'] = 1e-15
+scale_factor['a'] = 1e-18
+
+
+def check_eng_float (option, opt, value):
+ try:
+ scale = 1.0
+ suffix = value[-1]
+ if scale_factor.has_key (suffix):
+ return float (value[0:-1]) * scale_factor[suffix]
+ return float (value)
+ except:
+ raise OptionValueError (
+ "option %s: invalid engineering notation value: %r" % (opt, value))
+
+def check_intx (option, opt, value):
+ try:
+ return int (value, 0)
+ except:
+ raise OptionValueError (
+ "option %s: invalid integer value: %r" % (opt, value))
+
+def check_subdev (option, opt, value):
+ """
+ Value has the form: (A|B)(:0|1)?
+
+ @returns a 2-tuple (0|1, 0|1)
+ """
+ d = { 'A' : (0, 0), 'A:0' : (0, 0), 'A:1' : (0, 1),
+ 'B' : (1, 0), 'B:0' : (1, 0), 'B:1' : (1, 1) }
+ try:
+ return d[value.upper()]
+ except:
+ raise OptionValueError(
+ "option %s: invalid subdev: '%r', must be one of A, B, A:0, A:1, B:0, B:1" % (opt, value))
+
+class eng_option (Option):
+ TYPES = Option.TYPES + ("eng_float", "intx", "subdev")
+ TYPE_CHECKER = copy (Option.TYPE_CHECKER)
+ TYPE_CHECKER["eng_float"] = check_eng_float
+ TYPE_CHECKER["intx"] = check_intx
+ TYPE_CHECKER["subdev"] = check_subdev
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/Makefile.am b/gnuradio-core/src/python/gnuradio/gr/Makefile.am
new file mode 100644
index 000000000..05dee29f0
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/Makefile.am
@@ -0,0 +1,77 @@
+#
+# Copyright 2004,2005,2006 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+include $(top_srcdir)/Makefile.common
+
+EXTRA_DIST = run_tests.in
+
+
+TESTS = \
+ run_tests
+
+
+grgrpythondir = $(grpythondir)/gr
+
+grgrpython_PYTHON = \
+ __init__.py \
+ basic_flow_graph.py \
+ exceptions.py \
+ flow_graph.py \
+ gr_threading.py \
+ gr_threading_23.py \
+ gr_threading_24.py \
+ hier_block.py \
+ prefs.py \
+ scheduler.py
+
+noinst_PYTHON = \
+ qa_add_and_friends.py \
+ qa_basic_flow_graph.py \
+ qa_complex_to_xxx.py \
+ qa_correlate_access_code.py \
+ qa_diff_encoder.py \
+ qa_diff_phasor_cc.py \
+ qa_feval.py \
+ qa_fft_filter.py \
+ qa_filter_delay_fc.py \
+ qa_flow_graph.py \
+ qa_frequency_modulator.py \
+ qa_fsk_stuff.py \
+ qa_head.py \
+ qa_hilbert.py \
+ qa_iir.py \
+ qa_interleave.py \
+ qa_interp_fir_filter.py \
+ qa_kludge_copy.py \
+ qa_kludged_imports.py \
+ qa_message.py \
+ qa_mute.py \
+ qa_nlog10.py \
+ qa_packed_to_unpacked.py \
+ qa_pipe_fittings.py \
+ qa_rational_resampler.py \
+ qa_sig_source.py \
+ qa_single_pole_iir.py \
+ qa_single_pole_iir_cc.py \
+ qa_unpack_k_bits.py
+
+
+CLEANFILES = *.pyc
diff --git a/gnuradio-core/src/python/gnuradio/gr/__init__.py b/gnuradio-core/src/python/gnuradio/gr/__init__.py
new file mode 100644
index 000000000..5583c412a
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/__init__.py
@@ -0,0 +1,40 @@
+#
+# Copyright 2003,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.
+#
+
+# The presence of this file turns this directory into a Python package
+
+# This is the main GNU Radio python module.
+# We pull the swig output and the other modules into the gnuradio.gr namespace
+
+from gnuradio_swig_python import *
+from basic_flow_graph import *
+from flow_graph import *
+from exceptions import *
+from hier_block import *
+
+
+# create a couple of aliases
+serial_to_parallel = stream_to_vector
+parallel_to_serial = vector_to_stream
+
+# Force the preference database to be initialized
+from prefs import prefs
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/basic_flow_graph.py b/gnuradio-core/src/python/gnuradio/gr/basic_flow_graph.py
new file mode 100644
index 000000000..1afc96298
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/basic_flow_graph.py
@@ -0,0 +1,267 @@
+#
+# 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_swig_python import gr_block_sptr
+import types
+import hier_block
+
+def remove_duplicates (seq):
+ new = []
+ for x in seq:
+ if not x in new:
+ new.append (x)
+ return new
+
+
+class endpoint (object):
+ __slots__ = ['block', 'port']
+ def __init__ (self, block, port):
+ self.block = block
+ self.port = port
+
+ def __cmp__ (self, other):
+ if self.block == other.block and self.port == other.port:
+ return 0
+ return 1
+
+ def __str__ (self):
+ return '<endpoint (%s, %s)>' % (self.block, self.port)
+
+def expand_src_endpoint (src_endpoint):
+ # A src_endpoint is an output of a block
+ src_endpoint = coerce_endpoint (src_endpoint)
+ if isinstance (src_endpoint.block, hier_block.hier_block_base):
+ return expand_src_endpoint (
+ coerce_endpoint (src_endpoint.block.resolve_output_port(src_endpoint.port)))
+ else:
+ return src_endpoint
+
+def expand_dst_endpoint (dst_endpoint):
+ # a dst_endpoint is the input to a block
+ dst_endpoint = coerce_endpoint (dst_endpoint)
+ if isinstance (dst_endpoint.block, hier_block.hier_block_base):
+ exp = [coerce_endpoint(x) for x in
+ dst_endpoint.block.resolve_input_port(dst_endpoint.port)]
+ return expand_dst_endpoints (exp)
+ else:
+ return [dst_endpoint]
+
+def expand_dst_endpoints (endpoint_list):
+ r = []
+ for e in endpoint_list:
+ r.extend (expand_dst_endpoint (e))
+ return r
+
+
+def coerce_endpoint (x):
+ if isinstance (x, endpoint):
+ return x
+ elif isinstance (x, types.TupleType) and len (x) == 2:
+ return endpoint (x[0], x[1])
+ elif hasattr (x, 'block'): # assume it's a block
+ return endpoint (x, 0)
+ elif isinstance(x, hier_block.hier_block_base):
+ return endpoint (x, 0)
+ else:
+ raise ValueError, "Not coercible to endpoint: %s" % (x,)
+
+
+class edge (object):
+ __slots__ = ['src', 'dst']
+ def __init__ (self, src_endpoint, dst_endpoint):
+ self.src = src_endpoint
+ self.dst = dst_endpoint
+
+ def __cmp__ (self, other):
+ if self.src == other.src and self.dst == other.dst:
+ return 0
+ return 1
+
+ def __repr__ (self):
+ return '<edge (%s, %s)>' % (self.src, self.dst)
+
+class basic_flow_graph (object):
+ '''basic_flow_graph -- describe connections between blocks'''
+ # __slots__ is incompatible with weakrefs (for some reason!)
+ # __slots__ = ['edge_list']
+ def __init__ (self):
+ self.edge_list = []
+
+ def connect (self, *points):
+ '''connect requires two or more arguments that can be coerced to endpoints.
+ If more than two arguments are provided, they are connected together successively.
+ '''
+ if len (points) < 2:
+ raise ValueError, ("connect requires at least two endpoints; %d provided." % (len (points),))
+ for i in range (1, len (points)):
+ self._connect (points[i-1], points[i])
+
+ def _connect (self, src_endpoint, dst_endpoint):
+ s = expand_src_endpoint (src_endpoint)
+ for d in expand_dst_endpoint (dst_endpoint):
+ self._connect_prim (s, d)
+
+ def _connect_prim (self, src_endpoint, dst_endpoint):
+ src_endpoint = coerce_endpoint (src_endpoint)
+ dst_endpoint = coerce_endpoint (dst_endpoint)
+ self._check_valid_src_port (src_endpoint)
+ self._check_valid_dst_port (dst_endpoint)
+ self._check_dst_in_use (dst_endpoint)
+ self._check_type_match (src_endpoint, dst_endpoint)
+ self.edge_list.append (edge (src_endpoint, dst_endpoint))
+
+ def disconnect (self, src_endpoint, dst_endpoint):
+ s = expand_src_endpoint (src_endpoint)
+ for d in expand_dst_endpoint (dst_endpoint):
+ self._disconnect_prim (s, d)
+
+ def _disconnect_prim (self, src_endpoint, dst_endpoint):
+ src_endpoint = coerce_endpoint (src_endpoint)
+ dst_endpoint = coerce_endpoint (dst_endpoint)
+ e = edge (src_endpoint, dst_endpoint)
+ self.edge_list.remove (e)
+
+ def disconnect_all (self):
+ self.edge_list = []
+
+ def validate (self):
+ # check all blocks to ensure:
+ # (1a) their input ports are contiguously assigned
+ # (1b) the number of input ports is between min and max
+ # (2a) their output ports are contiguously assigned
+ # (2b) the number of output ports is between min and max
+ # (3) check_topology returns true
+
+ for m in self.all_blocks ():
+ # print m
+
+ edges = self.in_edges (m)
+ used_ports = [e.dst.port for e in edges]
+ ninputs = self._check_contiguity (m, m.input_signature (), used_ports, "input")
+
+ edges = self.out_edges (m)
+ used_ports = [e.src.port for e in edges]
+ noutputs = self._check_contiguity (m, m.output_signature (), used_ports, "output")
+
+ if not m.check_topology (ninputs, noutputs):
+ raise ValueError, ("%s::check_topology (%d, %d) failed" % (m, ninputs, noutputs))
+
+
+ # --- public utilities ---
+
+ def all_blocks (self):
+ '''return list of all blocks in the graph'''
+ all_blocks = []
+ for edge in self.edge_list:
+ m = edge.src.block
+ if not m in all_blocks:
+ all_blocks.append (m)
+ m = edge.dst.block
+ if not m in all_blocks:
+ all_blocks.append (m)
+ return all_blocks
+
+ def in_edges (self, m):
+ '''return list of all edges that have M as a destination'''
+ return [e for e in self.edge_list if e.dst.block == m]
+
+ def out_edges (self, m):
+ '''return list of all edges that have M as a source'''
+ return [e for e in self.edge_list if e.src.block == m]
+
+ def downstream_verticies (self, m):
+ return [e.dst.block for e in self.out_edges (m)]
+
+ def downstream_verticies_port (self, m, port):
+ return [e.dst.block for e in self.out_edges(m) if e.src.port == port]
+
+ def upstream_verticies (self, m):
+ return [e.src.block for e in self.in_edges (m)]
+
+ def adjacent_verticies (self, m):
+ '''return list of all verticies adjacent to M'''
+ return self.downstream_verticies (m) + self.upstream_verticies (m)
+
+ def sink_p (self, m):
+ '''return True iff this block is a sink'''
+ e = self.out_edges (m)
+ return len (e) == 0
+
+ def source_p (self, m):
+ '''return True iff this block is a source'''
+ e = self.in_edges (m)
+ return len (e) == 0
+
+ # --- internal methods ---
+
+ def _check_dst_in_use (self, dst_endpoint):
+ '''Ensure that there is not already an endpoint that terminates at dst_endpoint.'''
+ x = [ep for ep in self.edge_list if ep.dst == dst_endpoint]
+ if x: # already in use
+ raise ValueError, ("destination endpoint already in use: %s" % (dst_endpoint))
+
+ def _check_valid_src_port (self, src_endpoint):
+ self._check_port (src_endpoint.block.output_signature(), src_endpoint.port)
+
+ def _check_valid_dst_port (self, dst_endpoint):
+ self._check_port (dst_endpoint.block.input_signature(), dst_endpoint.port)
+
+ def _check_port (self, signature, port):
+ if port < 0:
+ raise ValueError, 'port number out of range.'
+ if signature.max_streams () == -1: # infinite
+ return # OK
+ if port >= signature.max_streams ():
+ raise ValueError, 'port number out of range.'
+
+ def _check_type_match (self, src_endpoint, dst_endpoint):
+ # for now, we just ensure that the stream item sizes match
+ src_sig = src_endpoint.block.output_signature ()
+ dst_sig = dst_endpoint.block.input_signature ()
+ src_size = src_sig.sizeof_stream_item (src_endpoint.port)
+ dst_size = dst_sig.sizeof_stream_item (dst_endpoint.port)
+ if src_size != dst_size:
+ raise ValueError, 'source and destination data sizes are different'
+
+ def _check_contiguity (self, m, sig, used_ports, dir):
+ used_ports.sort ()
+ used_ports = remove_duplicates (used_ports)
+ min_s = sig.min_streams ()
+
+ l = len (used_ports)
+ if l == 0:
+ if min_s == 0:
+ return l
+ raise ValueError, ("%s requires %d %s connections. It has none." %
+ (m, min_s, dir))
+
+ if used_ports[-1] + 1 < min_s:
+ raise ValueError, ("%s requires %d %s connections. It has %d." %
+ (m, min_s, dir, used_ports[-1] + 1))
+
+ if used_ports[-1] + 1 != l:
+ for i in range (l):
+ if used_ports[i] != i:
+ raise ValueError, ("%s %s port %d is not connected" %
+ (m, dir, i))
+
+ # print "%s ports: %s" % (dir, used_ports)
+ return l
diff --git a/gnuradio-core/src/python/gnuradio/gr/benchmark_filters.py b/gnuradio-core/src/python/gnuradio/gr/benchmark_filters.py
new file mode 100755
index 000000000..7b2f44c44
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/benchmark_filters.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+
+import time
+import random
+from optparse import OptionParser
+from gnuradio import gr
+from gnuradio.eng_option import eng_option
+
+def make_random_complex_tuple(L):
+ result = []
+ for x in range(L):
+ result.append(complex(random.uniform(-1000,1000),
+ random.uniform(-1000,1000)))
+ return tuple(result)
+
+def benchmark(name, creator, dec, ntaps, total_test_size, block_size):
+ block_size = 32768
+
+ fg = gr.flow_graph()
+ taps = make_random_complex_tuple(ntaps)
+ src = gr.vector_source_c(make_random_complex_tuple(block_size), True)
+ head = gr.head(gr.sizeof_gr_complex, int(total_test_size))
+ op = creator(dec, taps)
+ dst = gr.null_sink(gr.sizeof_gr_complex)
+ fg.connect(src, head, op, dst)
+ start = time.time()
+ fg.run()
+ stop = time.time()
+ delta = stop - start
+ print "%16s: taps: %4d input: %4g, time: %6.3f taps/sec: %10.4g" % (
+ name, ntaps, total_test_size, delta, ntaps*total_test_size/delta)
+
+def main():
+ parser = OptionParser(option_class=eng_option)
+ parser.add_option("-n", "--ntaps", type="int", default=256)
+ parser.add_option("-t", "--total-input-size", type="eng_float", default=40e6)
+ parser.add_option("-b", "--block-size", type="intx", default=50000)
+ parser.add_option("-d", "--decimation", type="int", default=1)
+ (options, args) = parser.parse_args()
+ if len(args) != 0:
+ parser.print_help()
+ sys.exit(1)
+
+ ntaps = options.ntaps
+ total_input_size = options.total_input_size
+ block_size = options.block_size
+ dec = options.decimation
+
+ benchmark("gr.fir_filter_ccc", gr.fir_filter_ccc,
+ dec, ntaps, total_input_size, block_size)
+ benchmark("gr.fft_filter_ccc", gr.fft_filter_ccc,
+ dec, ntaps, total_input_size, block_size)
+
+if __name__ == '__main__':
+ main()
diff --git a/gnuradio-core/src/python/gnuradio/gr/exceptions.py b/gnuradio-core/src/python/gnuradio/gr/exceptions.py
new file mode 100644
index 000000000..0cbeb143a
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/exceptions.py
@@ -0,0 +1,27 @@
+#
+# 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.
+
+class NotDAG (Exception):
+ """Not a directed acyclic graph"""
+ pass
+
+class CantHappen (Exception):
+ """Can't happen"""
+ pass
diff --git a/gnuradio-core/src/python/gnuradio/gr/flow_graph.py b/gnuradio-core/src/python/gnuradio/gr/flow_graph.py
new file mode 100644
index 000000000..db9c58768
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/flow_graph.py
@@ -0,0 +1,234 @@
+#
+# 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.gr.basic_flow_graph import remove_duplicates, endpoint, edge, \
+ basic_flow_graph
+
+from gnuradio.gr.exceptions import *
+from gnuradio_swig_python import buffer, buffer_add_reader, block_detail, \
+ single_threaded_scheduler
+
+from gnuradio.gr.scheduler import scheduler
+
+_WHITE = 0 # graph coloring tags
+_GRAY = 1
+_BLACK = 2
+
+_flow_graph_debug = False
+
+def set_flow_graph_debug(on):
+ global _flow_graph_debug
+ _flow_graph_debug = on
+
+
+class buffer_sizes (object):
+ """compute buffer sizes to use"""
+ def __init__ (self, flow_graph):
+ # We could scan the graph here and determine individual sizes
+ # based on relative_rate, number of readers, etc.
+
+ # The simplest thing that could possibly work: all buffers same size
+ self.flow_graph = flow_graph
+ self.fixed_buffer_size = 32*1024
+
+ def allocate (self, m, index):
+ """allocate buffer for output index of block m"""
+ item_size = m.output_signature().sizeof_stream_item (index)
+ nitems = self.fixed_buffer_size / item_size
+ if nitems < 2 * m.output_multiple ():
+ nitems = 2 * m.output_multiple ()
+
+ # if any downstream blocks is a decimator and/or has a large output_multiple,
+ # ensure that we have a buffer at least 2 * their decimation_factor*output_multiple
+ for mdown in self.flow_graph.downstream_verticies_port(m, index):
+ decimation = int(1.0 / mdown.relative_rate())
+ nitems = max(nitems, 2 * (decimation * mdown.output_multiple() + mdown.history()))
+
+ return buffer (nitems, item_size)
+
+
+class flow_graph (basic_flow_graph):
+ """add physical connection info to simple_flow_graph
+ """
+ # __slots__ is incompatible with weakrefs (for some reason!)
+ # __slots__ = ['blocks', 'scheduler']
+
+ def __init__ (self):
+ basic_flow_graph.__init__ (self);
+ self.blocks = None
+ self.scheduler = None
+
+ def __del__(self):
+ # print "\nflow_graph.__del__"
+ # this ensures that i/o devices such as the USRP get shutdown gracefully
+ self.stop()
+
+ def start (self):
+ '''start graph, forking thread(s), return immediately'''
+ if self.scheduler:
+ raise RuntimeError, "Scheduler already running"
+ self._setup_connections ()
+
+ # cast down to gr_module_sptr
+ # t = [x.block () for x in self.topological_sort (self.blocks)]
+ self.scheduler = scheduler (self)
+ self.scheduler.start ()
+
+ def stop (self):
+ '''tells scheduler to stop and waits for it to happen'''
+ if self.scheduler:
+ self.scheduler.stop ()
+ self.scheduler = None
+
+ def wait (self):
+ '''waits for scheduler to stop'''
+ if self.scheduler:
+ self.scheduler.wait ()
+ self.scheduler = None
+
+ def is_running (self):
+ return not not self.scheduler
+
+ def run (self):
+ '''start graph, wait for completion'''
+ self.start ()
+ self.wait ()
+
+ def _setup_connections (self):
+ """given the basic flow graph, setup all the physical connections"""
+ self.validate ()
+ self.blocks = self.all_blocks ()
+ self._assign_details ()
+ self._assign_buffers ()
+ self._connect_inputs ()
+
+ def _assign_details (self):
+ for m in self.blocks:
+ edges = self.in_edges (m)
+ used_ports = remove_duplicates ([e.dst.port for e in edges])
+ ninputs = len (used_ports)
+
+ edges = self.out_edges (m)
+ used_ports = remove_duplicates ([e.src.port for e in edges])
+ noutputs = len (used_ports)
+
+ m.set_detail (block_detail (ninputs, noutputs))
+
+ def _assign_buffers (self):
+ """determine the buffer sizes to use, allocate them and attach to detail"""
+ sizes = buffer_sizes (self)
+ for m in self.blocks:
+ d = m.detail ()
+ for index in range (d.noutputs ()):
+ d.set_output (index, sizes.allocate (m, index))
+
+ def _connect_inputs (self):
+ """connect all block inputs to appropriate upstream buffers"""
+ for m in self.blocks:
+ d = m.detail ()
+ # print "%r history = %d" % (m, m.history())
+ for e in self.in_edges(m):
+ # FYI, sources don't have any in_edges
+ our_port = e.dst.port
+ upstream_block = e.src.block
+ upstream_port = e.src.port
+ upstream_buffer = upstream_block.detail().output(upstream_port)
+ d.set_input(our_port, buffer_add_reader(upstream_buffer, m.history()))
+
+
+ def topological_sort (self, all_v):
+ '''
+ Return a topologically sorted list of vertices.
+ This is basically a depth-first search with checks
+ for back edges (the non-DAG condition)
+
+ '''
+
+ # it's correct without this sort, but this
+ # should give better ordering for cache utilization
+ all_v = self._sort_sources_first (all_v)
+
+ output = []
+ for v in all_v:
+ v.ts_color = _WHITE
+ for v in all_v:
+ if v.ts_color == _WHITE:
+ self._dfs_visit (v, output)
+ output.reverse ()
+ return output
+
+ def _dfs_visit (self, u, output):
+ # print "dfs_visit (enter): ", u
+ u.ts_color = _GRAY
+ for v in self.downstream_verticies (u):
+ if v.ts_color == _WHITE: # (u, v) is a tree edge
+ self._dfs_visit (v, output)
+ elif v.ts_color == _GRAY: # (u, v) is a back edge
+ raise NotDAG, "The graph is not an acyclic graph (It's got a loop)"
+ elif v.ts_color == _BLACK: # (u, v) is a cross or forward edge
+ pass
+ else:
+ raise CantHappen, "Invalid color on vertex"
+ u.ts_color = _BLACK
+ output.append (u)
+ # print "dfs_visit (exit): ", u, output
+
+ def _sort_sources_first (self, all_v):
+ # the sort function is not guaranteed to be stable.
+ # We add the unique_id in to the key so we're guaranteed
+ # of reproducible results. This is important for the test
+ # code. There is often more than one valid topological sort.
+ # We want to force a reproducible choice.
+ x = [(not self.source_p(v), v.unique_id(), v) for v in all_v]
+ x.sort ()
+ x = [v[2] for v in x]
+ # print "sorted: ", x
+ return x
+
+ def partition_graph (self, all_v):
+ '''Return a list of lists of nodes that are connected.
+ The result is a list of disjoint graphs.
+ The sublists are topologically sorted.
+ '''
+ result = []
+ working_v = all_v[:] # make copy
+ while working_v:
+ rv = self._reachable_verticies (working_v[0], working_v)
+ result.append (self.topological_sort (rv))
+ for v in rv:
+ working_v.remove (v)
+ if _flow_graph_debug:
+ print "partition_graph:", result
+ return result
+
+ def _reachable_verticies (self, start, all_v):
+ for v in all_v:
+ v.ts_color = _WHITE
+
+ self._reachable_dfs_visit (start)
+ return [v for v in all_v if v.ts_color == _BLACK]
+
+ def _reachable_dfs_visit (self, u):
+ u.ts_color = _BLACK
+ for v in self.adjacent_verticies (u):
+ if v.ts_color == _WHITE:
+ self._reachable_dfs_visit (v)
+ return None
diff --git a/gnuradio-core/src/python/gnuradio/gr/gr_threading.py b/gnuradio-core/src/python/gnuradio/gr/gr_threading.py
new file mode 100644
index 000000000..6a09a3239
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/gr_threading.py
@@ -0,0 +1,35 @@
+#
+# 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 sys import version_info as _version_info
+
+# import patched version of standard threading module
+
+if _version_info[0:2] == (2, 3):
+ #print "Importing gr_threading_23"
+ from gr_threading_23 import *
+elif _version_info[0:2] == (2, 4):
+ #print "Importing gr_threading_24"
+ from gr_threading_24 import *
+else:
+ # assume the patch was applied...
+ #print "Importing system provided threading"
+ from threading import *
diff --git a/gnuradio-core/src/python/gnuradio/gr/gr_threading_23.py b/gnuradio-core/src/python/gnuradio/gr/gr_threading_23.py
new file mode 100644
index 000000000..dee8034c1
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/gr_threading_23.py
@@ -0,0 +1,724 @@
+"""Thread module emulating a subset of Java's threading model."""
+
+# This started life as the threading.py module of Python 2.3
+# It's been patched to fix a problem with join, where a KeyboardInterrupt
+# caused a lock to be left in the acquired state.
+
+import sys as _sys
+
+try:
+ import thread
+except ImportError:
+ del _sys.modules[__name__]
+ raise
+
+from StringIO import StringIO as _StringIO
+from time import time as _time, sleep as _sleep
+from traceback import print_exc as _print_exc
+
+# Rename some stuff so "from threading import *" is safe
+__all__ = ['activeCount', 'Condition', 'currentThread', 'enumerate', 'Event',
+ 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Thread',
+ 'Timer', 'setprofile', 'settrace']
+
+_start_new_thread = thread.start_new_thread
+_allocate_lock = thread.allocate_lock
+_get_ident = thread.get_ident
+ThreadError = thread.error
+del thread
+
+
+# Debug support (adapted from ihooks.py).
+# All the major classes here derive from _Verbose. We force that to
+# be a new-style class so that all the major classes here are new-style.
+# This helps debugging (type(instance) is more revealing for instances
+# of new-style classes).
+
+_VERBOSE = False
+
+if __debug__:
+
+ class _Verbose(object):
+
+ def __init__(self, verbose=None):
+ if verbose is None:
+ verbose = _VERBOSE
+ self.__verbose = verbose
+
+ def _note(self, format, *args):
+ if self.__verbose:
+ format = format % args
+ format = "%s: %s\n" % (
+ currentThread().getName(), format)
+ _sys.stderr.write(format)
+
+else:
+ # Disable this when using "python -O"
+ class _Verbose(object):
+ def __init__(self, verbose=None):
+ pass
+ def _note(self, *args):
+ pass
+
+# Support for profile and trace hooks
+
+_profile_hook = None
+_trace_hook = None
+
+def setprofile(func):
+ global _profile_hook
+ _profile_hook = func
+
+def settrace(func):
+ global _trace_hook
+ _trace_hook = func
+
+# Synchronization classes
+
+Lock = _allocate_lock
+
+def RLock(*args, **kwargs):
+ return _RLock(*args, **kwargs)
+
+class _RLock(_Verbose):
+
+ def __init__(self, verbose=None):
+ _Verbose.__init__(self, verbose)
+ self.__block = _allocate_lock()
+ self.__owner = None
+ self.__count = 0
+
+ def __repr__(self):
+ return "<%s(%s, %d)>" % (
+ self.__class__.__name__,
+ self.__owner and self.__owner.getName(),
+ self.__count)
+
+ def acquire(self, blocking=1):
+ me = currentThread()
+ if self.__owner is me:
+ self.__count = self.__count + 1
+ if __debug__:
+ self._note("%s.acquire(%s): recursive success", self, blocking)
+ return 1
+ rc = self.__block.acquire(blocking)
+ if rc:
+ self.__owner = me
+ self.__count = 1
+ if __debug__:
+ self._note("%s.acquire(%s): initial succes", self, blocking)
+ else:
+ if __debug__:
+ self._note("%s.acquire(%s): failure", self, blocking)
+ return rc
+
+ def release(self):
+ me = currentThread()
+ assert self.__owner is me, "release() of un-acquire()d lock"
+ self.__count = count = self.__count - 1
+ if not count:
+ self.__owner = None
+ self.__block.release()
+ if __debug__:
+ self._note("%s.release(): final release", self)
+ else:
+ if __debug__:
+ self._note("%s.release(): non-final release", self)
+
+ # Internal methods used by condition variables
+
+ def _acquire_restore(self, (count, owner)):
+ self.__block.acquire()
+ self.__count = count
+ self.__owner = owner
+ if __debug__:
+ self._note("%s._acquire_restore()", self)
+
+ def _release_save(self):
+ if __debug__:
+ self._note("%s._release_save()", self)
+ count = self.__count
+ self.__count = 0
+ owner = self.__owner
+ self.__owner = None
+ self.__block.release()
+ return (count, owner)
+
+ def _is_owned(self):
+ return self.__owner is currentThread()
+
+
+def Condition(*args, **kwargs):
+ return _Condition(*args, **kwargs)
+
+class _Condition(_Verbose):
+
+ def __init__(self, lock=None, verbose=None):
+ _Verbose.__init__(self, verbose)
+ if lock is None:
+ lock = RLock()
+ self.__lock = lock
+ # Export the lock's acquire() and release() methods
+ self.acquire = lock.acquire
+ self.release = lock.release
+ # If the lock defines _release_save() and/or _acquire_restore(),
+ # these override the default implementations (which just call
+ # release() and acquire() on the lock). Ditto for _is_owned().
+ try:
+ self._release_save = lock._release_save
+ except AttributeError:
+ pass
+ try:
+ self._acquire_restore = lock._acquire_restore
+ except AttributeError:
+ pass
+ try:
+ self._is_owned = lock._is_owned
+ except AttributeError:
+ pass
+ self.__waiters = []
+
+ def __repr__(self):
+ return "<Condition(%s, %d)>" % (self.__lock, len(self.__waiters))
+
+ def _release_save(self):
+ self.__lock.release() # No state to save
+
+ def _acquire_restore(self, x):
+ self.__lock.acquire() # Ignore saved state
+
+ def _is_owned(self):
+ # Return True if lock is owned by currentThread.
+ # This method is called only if __lock doesn't have _is_owned().
+ if self.__lock.acquire(0):
+ self.__lock.release()
+ return False
+ else:
+ return True
+
+ def wait(self, timeout=None):
+ currentThread() # for side-effect
+ assert self._is_owned(), "wait() of un-acquire()d lock"
+ waiter = _allocate_lock()
+ waiter.acquire()
+ self.__waiters.append(waiter)
+ saved_state = self._release_save()
+ try: # restore state no matter what (e.g., KeyboardInterrupt)
+ if timeout is None:
+ waiter.acquire()
+ if __debug__:
+ self._note("%s.wait(): got it", self)
+ else:
+ # Balancing act: We can't afford a pure busy loop, so we
+ # have to sleep; but if we sleep the whole timeout time,
+ # we'll be unresponsive. The scheme here sleeps very
+ # little at first, longer as time goes on, but never longer
+ # than 20 times per second (or the timeout time remaining).
+ endtime = _time() + timeout
+ delay = 0.0005 # 500 us -> initial delay of 1 ms
+ while True:
+ gotit = waiter.acquire(0)
+ if gotit:
+ break
+ remaining = endtime - _time()
+ if remaining <= 0:
+ break
+ delay = min(delay * 2, remaining, .05)
+ _sleep(delay)
+ if not gotit:
+ if __debug__:
+ self._note("%s.wait(%s): timed out", self, timeout)
+ try:
+ self.__waiters.remove(waiter)
+ except ValueError:
+ pass
+ else:
+ if __debug__:
+ self._note("%s.wait(%s): got it", self, timeout)
+ finally:
+ self._acquire_restore(saved_state)
+
+ def notify(self, n=1):
+ currentThread() # for side-effect
+ assert self._is_owned(), "notify() of un-acquire()d lock"
+ __waiters = self.__waiters
+ waiters = __waiters[:n]
+ if not waiters:
+ if __debug__:
+ self._note("%s.notify(): no waiters", self)
+ return
+ self._note("%s.notify(): notifying %d waiter%s", self, n,
+ n!=1 and "s" or "")
+ for waiter in waiters:
+ waiter.release()
+ try:
+ __waiters.remove(waiter)
+ except ValueError:
+ pass
+
+ def notifyAll(self):
+ self.notify(len(self.__waiters))
+
+
+def Semaphore(*args, **kwargs):
+ return _Semaphore(*args, **kwargs)
+
+class _Semaphore(_Verbose):
+
+ # After Tim Peters' semaphore class, but not quite the same (no maximum)
+
+ def __init__(self, value=1, verbose=None):
+ assert value >= 0, "Semaphore initial value must be >= 0"
+ _Verbose.__init__(self, verbose)
+ self.__cond = Condition(Lock())
+ self.__value = value
+
+ def acquire(self, blocking=1):
+ rc = False
+ self.__cond.acquire()
+ while self.__value == 0:
+ if not blocking:
+ break
+ if __debug__:
+ self._note("%s.acquire(%s): blocked waiting, value=%s",
+ self, blocking, self.__value)
+ self.__cond.wait()
+ else:
+ self.__value = self.__value - 1
+ if __debug__:
+ self._note("%s.acquire: success, value=%s",
+ self, self.__value)
+ rc = True
+ self.__cond.release()
+ return rc
+
+ def release(self):
+ self.__cond.acquire()
+ self.__value = self.__value + 1
+ if __debug__:
+ self._note("%s.release: success, value=%s",
+ self, self.__value)
+ self.__cond.notify()
+ self.__cond.release()
+
+
+def BoundedSemaphore(*args, **kwargs):
+ return _BoundedSemaphore(*args, **kwargs)
+
+class _BoundedSemaphore(_Semaphore):
+ """Semaphore that checks that # releases is <= # acquires"""
+ def __init__(self, value=1, verbose=None):
+ _Semaphore.__init__(self, value, verbose)
+ self._initial_value = value
+
+ def release(self):
+ if self._Semaphore__value >= self._initial_value:
+ raise ValueError, "Semaphore released too many times"
+ return _Semaphore.release(self)
+
+
+def Event(*args, **kwargs):
+ return _Event(*args, **kwargs)
+
+class _Event(_Verbose):
+
+ # After Tim Peters' event class (without is_posted())
+
+ def __init__(self, verbose=None):
+ _Verbose.__init__(self, verbose)
+ self.__cond = Condition(Lock())
+ self.__flag = False
+
+ def isSet(self):
+ return self.__flag
+
+ def set(self):
+ self.__cond.acquire()
+ try:
+ self.__flag = True
+ self.__cond.notifyAll()
+ finally:
+ self.__cond.release()
+
+ def clear(self):
+ self.__cond.acquire()
+ try:
+ self.__flag = False
+ finally:
+ self.__cond.release()
+
+ def wait(self, timeout=None):
+ self.__cond.acquire()
+ try:
+ if not self.__flag:
+ self.__cond.wait(timeout)
+ finally:
+ self.__cond.release()
+
+# Helper to generate new thread names
+_counter = 0
+def _newname(template="Thread-%d"):
+ global _counter
+ _counter = _counter + 1
+ return template % _counter
+
+# Active thread administration
+_active_limbo_lock = _allocate_lock()
+_active = {}
+_limbo = {}
+
+
+# Main class for threads
+
+class Thread(_Verbose):
+
+ __initialized = False
+
+ def __init__(self, group=None, target=None, name=None,
+ args=(), kwargs={}, verbose=None):
+ assert group is None, "group argument must be None for now"
+ _Verbose.__init__(self, verbose)
+ self.__target = target
+ self.__name = str(name or _newname())
+ self.__args = args
+ self.__kwargs = kwargs
+ self.__daemonic = self._set_daemon()
+ self.__started = False
+ self.__stopped = False
+ self.__block = Condition(Lock())
+ self.__initialized = True
+
+ def _set_daemon(self):
+ # Overridden in _MainThread and _DummyThread
+ return currentThread().isDaemon()
+
+ def __repr__(self):
+ assert self.__initialized, "Thread.__init__() was not called"
+ status = "initial"
+ if self.__started:
+ status = "started"
+ if self.__stopped:
+ status = "stopped"
+ if self.__daemonic:
+ status = status + " daemon"
+ return "<%s(%s, %s)>" % (self.__class__.__name__, self.__name, status)
+
+ def start(self):
+ assert self.__initialized, "Thread.__init__() not called"
+ assert not self.__started, "thread already started"
+ if __debug__:
+ self._note("%s.start(): starting thread", self)
+ _active_limbo_lock.acquire()
+ _limbo[self] = self
+ _active_limbo_lock.release()
+ _start_new_thread(self.__bootstrap, ())
+ self.__started = True
+ _sleep(0.000001) # 1 usec, to let the thread run (Solaris hack)
+
+ def run(self):
+ if self.__target:
+ self.__target(*self.__args, **self.__kwargs)
+
+ def __bootstrap(self):
+ try:
+ self.__started = True
+ _active_limbo_lock.acquire()
+ _active[_get_ident()] = self
+ del _limbo[self]
+ _active_limbo_lock.release()
+ if __debug__:
+ self._note("%s.__bootstrap(): thread started", self)
+
+ if _trace_hook:
+ self._note("%s.__bootstrap(): registering trace hook", self)
+ _sys.settrace(_trace_hook)
+ if _profile_hook:
+ self._note("%s.__bootstrap(): registering profile hook", self)
+ _sys.setprofile(_profile_hook)
+
+ try:
+ self.run()
+ except SystemExit:
+ if __debug__:
+ self._note("%s.__bootstrap(): raised SystemExit", self)
+ except:
+ if __debug__:
+ self._note("%s.__bootstrap(): unhandled exception", self)
+ s = _StringIO()
+ _print_exc(file=s)
+ _sys.stderr.write("Exception in thread %s:\n%s\n" %
+ (self.getName(), s.getvalue()))
+ else:
+ if __debug__:
+ self._note("%s.__bootstrap(): normal return", self)
+ finally:
+ self.__stop()
+ try:
+ self.__delete()
+ except:
+ pass
+
+ def __stop(self):
+ self.__block.acquire()
+ self.__stopped = True
+ self.__block.notifyAll()
+ self.__block.release()
+
+ def __delete(self):
+ _active_limbo_lock.acquire()
+ del _active[_get_ident()]
+ _active_limbo_lock.release()
+
+ def join(self, timeout=None):
+ assert self.__initialized, "Thread.__init__() not called"
+ assert self.__started, "cannot join thread before it is started"
+ assert self is not currentThread(), "cannot join current thread"
+ if __debug__:
+ if not self.__stopped:
+ self._note("%s.join(): waiting until thread stops", self)
+ self.__block.acquire()
+ try:
+ if timeout is None:
+ while not self.__stopped:
+ self.__block.wait()
+ if __debug__:
+ self._note("%s.join(): thread stopped", self)
+ else:
+ deadline = _time() + timeout
+ while not self.__stopped:
+ delay = deadline - _time()
+ if delay <= 0:
+ if __debug__:
+ self._note("%s.join(): timed out", self)
+ break
+ self.__block.wait(delay)
+ else:
+ if __debug__:
+ self._note("%s.join(): thread stopped", self)
+ finally:
+ self.__block.release()
+
+ def getName(self):
+ assert self.__initialized, "Thread.__init__() not called"
+ return self.__name
+
+ def setName(self, name):
+ assert self.__initialized, "Thread.__init__() not called"
+ self.__name = str(name)
+
+ def isAlive(self):
+ assert self.__initialized, "Thread.__init__() not called"
+ return self.__started and not self.__stopped
+
+ def isDaemon(self):
+ assert self.__initialized, "Thread.__init__() not called"
+ return self.__daemonic
+
+ def setDaemon(self, daemonic):
+ assert self.__initialized, "Thread.__init__() not called"
+ assert not self.__started, "cannot set daemon status of active thread"
+ self.__daemonic = daemonic
+
+# The timer class was contributed by Itamar Shtull-Trauring
+
+def Timer(*args, **kwargs):
+ return _Timer(*args, **kwargs)
+
+class _Timer(Thread):
+ """Call a function after a specified number of seconds:
+
+ t = Timer(30.0, f, args=[], kwargs={})
+ t.start()
+ t.cancel() # stop the timer's action if it's still waiting
+ """
+
+ def __init__(self, interval, function, args=[], kwargs={}):
+ Thread.__init__(self)
+ self.interval = interval
+ self.function = function
+ self.args = args
+ self.kwargs = kwargs
+ self.finished = Event()
+
+ def cancel(self):
+ """Stop the timer if it hasn't finished yet"""
+ self.finished.set()
+
+ def run(self):
+ self.finished.wait(self.interval)
+ if not self.finished.isSet():
+ self.function(*self.args, **self.kwargs)
+ self.finished.set()
+
+# Special thread class to represent the main thread
+# This is garbage collected through an exit handler
+
+class _MainThread(Thread):
+
+ def __init__(self):
+ Thread.__init__(self, name="MainThread")
+ self._Thread__started = True
+ _active_limbo_lock.acquire()
+ _active[_get_ident()] = self
+ _active_limbo_lock.release()
+ import atexit
+ atexit.register(self.__exitfunc)
+
+ def _set_daemon(self):
+ return False
+
+ def __exitfunc(self):
+ self._Thread__stop()
+ t = _pickSomeNonDaemonThread()
+ if t:
+ if __debug__:
+ self._note("%s: waiting for other threads", self)
+ while t:
+ t.join()
+ t = _pickSomeNonDaemonThread()
+ if __debug__:
+ self._note("%s: exiting", self)
+ self._Thread__delete()
+
+def _pickSomeNonDaemonThread():
+ for t in enumerate():
+ if not t.isDaemon() and t.isAlive():
+ return t
+ return None
+
+
+# Dummy thread class to represent threads not started here.
+# These aren't garbage collected when they die,
+# nor can they be waited for.
+# Their purpose is to return *something* from currentThread().
+# They are marked as daemon threads so we won't wait for them
+# when we exit (conform previous semantics).
+
+class _DummyThread(Thread):
+
+ def __init__(self):
+ Thread.__init__(self, name=_newname("Dummy-%d"))
+ self._Thread__started = True
+ _active_limbo_lock.acquire()
+ _active[_get_ident()] = self
+ _active_limbo_lock.release()
+
+ def _set_daemon(self):
+ return True
+
+ def join(self, timeout=None):
+ assert False, "cannot join a dummy thread"
+
+
+# Global API functions
+
+def currentThread():
+ try:
+ return _active[_get_ident()]
+ except KeyError:
+ ##print "currentThread(): no current thread for", _get_ident()
+ return _DummyThread()
+
+def activeCount():
+ _active_limbo_lock.acquire()
+ count = len(_active) + len(_limbo)
+ _active_limbo_lock.release()
+ return count
+
+def enumerate():
+ _active_limbo_lock.acquire()
+ active = _active.values() + _limbo.values()
+ _active_limbo_lock.release()
+ return active
+
+# Create the main thread object
+
+_MainThread()
+
+
+# Self-test code
+
+def _test():
+
+ class BoundedQueue(_Verbose):
+
+ def __init__(self, limit):
+ _Verbose.__init__(self)
+ self.mon = RLock()
+ self.rc = Condition(self.mon)
+ self.wc = Condition(self.mon)
+ self.limit = limit
+ self.queue = []
+
+ def put(self, item):
+ self.mon.acquire()
+ while len(self.queue) >= self.limit:
+ self._note("put(%s): queue full", item)
+ self.wc.wait()
+ self.queue.append(item)
+ self._note("put(%s): appended, length now %d",
+ item, len(self.queue))
+ self.rc.notify()
+ self.mon.release()
+
+ def get(self):
+ self.mon.acquire()
+ while not self.queue:
+ self._note("get(): queue empty")
+ self.rc.wait()
+ item = self.queue.pop(0)
+ self._note("get(): got %s, %d left", item, len(self.queue))
+ self.wc.notify()
+ self.mon.release()
+ return item
+
+ class ProducerThread(Thread):
+
+ def __init__(self, queue, quota):
+ Thread.__init__(self, name="Producer")
+ self.queue = queue
+ self.quota = quota
+
+ def run(self):
+ from random import random
+ counter = 0
+ while counter < self.quota:
+ counter = counter + 1
+ self.queue.put("%s.%d" % (self.getName(), counter))
+ _sleep(random() * 0.00001)
+
+
+ class ConsumerThread(Thread):
+
+ def __init__(self, queue, count):
+ Thread.__init__(self, name="Consumer")
+ self.queue = queue
+ self.count = count
+
+ def run(self):
+ while self.count > 0:
+ item = self.queue.get()
+ print item
+ self.count = self.count - 1
+
+ NP = 3
+ QL = 4
+ NI = 5
+
+ Q = BoundedQueue(QL)
+ P = []
+ for i in range(NP):
+ t = ProducerThread(Q, NI)
+ t.setName("Producer-%d" % (i+1))
+ P.append(t)
+ C = ConsumerThread(Q, NI*NP)
+ for t in P:
+ t.start()
+ _sleep(0.000001)
+ C.start()
+ for t in P:
+ t.join()
+ C.join()
+
+if __name__ == '__main__':
+ _test()
diff --git a/gnuradio-core/src/python/gnuradio/gr/gr_threading_24.py b/gnuradio-core/src/python/gnuradio/gr/gr_threading_24.py
new file mode 100644
index 000000000..8539bfc04
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/gr_threading_24.py
@@ -0,0 +1,793 @@
+"""Thread module emulating a subset of Java's threading model."""
+
+# This started life as the threading.py module of Python 2.4
+# It's been patched to fix a problem with join, where a KeyboardInterrupt
+# caused a lock to be left in the acquired state.
+
+import sys as _sys
+
+try:
+ import thread
+except ImportError:
+ del _sys.modules[__name__]
+ raise
+
+from time import time as _time, sleep as _sleep
+from traceback import format_exc as _format_exc
+from collections import deque
+
+# Rename some stuff so "from threading import *" is safe
+__all__ = ['activeCount', 'Condition', 'currentThread', 'enumerate', 'Event',
+ 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Thread',
+ 'Timer', 'setprofile', 'settrace', 'local']
+
+_start_new_thread = thread.start_new_thread
+_allocate_lock = thread.allocate_lock
+_get_ident = thread.get_ident
+ThreadError = thread.error
+del thread
+
+
+# Debug support (adapted from ihooks.py).
+# All the major classes here derive from _Verbose. We force that to
+# be a new-style class so that all the major classes here are new-style.
+# This helps debugging (type(instance) is more revealing for instances
+# of new-style classes).
+
+_VERBOSE = False
+
+if __debug__:
+
+ class _Verbose(object):
+
+ def __init__(self, verbose=None):
+ if verbose is None:
+ verbose = _VERBOSE
+ self.__verbose = verbose
+
+ def _note(self, format, *args):
+ if self.__verbose:
+ format = format % args
+ format = "%s: %s\n" % (
+ currentThread().getName(), format)
+ _sys.stderr.write(format)
+
+else:
+ # Disable this when using "python -O"
+ class _Verbose(object):
+ def __init__(self, verbose=None):
+ pass
+ def _note(self, *args):
+ pass
+
+# Support for profile and trace hooks
+
+_profile_hook = None
+_trace_hook = None
+
+def setprofile(func):
+ global _profile_hook
+ _profile_hook = func
+
+def settrace(func):
+ global _trace_hook
+ _trace_hook = func
+
+# Synchronization classes
+
+Lock = _allocate_lock
+
+def RLock(*args, **kwargs):
+ return _RLock(*args, **kwargs)
+
+class _RLock(_Verbose):
+
+ def __init__(self, verbose=None):
+ _Verbose.__init__(self, verbose)
+ self.__block = _allocate_lock()
+ self.__owner = None
+ self.__count = 0
+
+ def __repr__(self):
+ return "<%s(%s, %d)>" % (
+ self.__class__.__name__,
+ self.__owner and self.__owner.getName(),
+ self.__count)
+
+ def acquire(self, blocking=1):
+ me = currentThread()
+ if self.__owner is me:
+ self.__count = self.__count + 1
+ if __debug__:
+ self._note("%s.acquire(%s): recursive success", self, blocking)
+ return 1
+ rc = self.__block.acquire(blocking)
+ if rc:
+ self.__owner = me
+ self.__count = 1
+ if __debug__:
+ self._note("%s.acquire(%s): initial succes", self, blocking)
+ else:
+ if __debug__:
+ self._note("%s.acquire(%s): failure", self, blocking)
+ return rc
+
+ def release(self):
+ me = currentThread()
+ assert self.__owner is me, "release() of un-acquire()d lock"
+ self.__count = count = self.__count - 1
+ if not count:
+ self.__owner = None
+ self.__block.release()
+ if __debug__:
+ self._note("%s.release(): final release", self)
+ else:
+ if __debug__:
+ self._note("%s.release(): non-final release", self)
+
+ # Internal methods used by condition variables
+
+ def _acquire_restore(self, (count, owner)):
+ self.__block.acquire()
+ self.__count = count
+ self.__owner = owner
+ if __debug__:
+ self._note("%s._acquire_restore()", self)
+
+ def _release_save(self):
+ if __debug__:
+ self._note("%s._release_save()", self)
+ count = self.__count
+ self.__count = 0
+ owner = self.__owner
+ self.__owner = None
+ self.__block.release()
+ return (count, owner)
+
+ def _is_owned(self):
+ return self.__owner is currentThread()
+
+
+def Condition(*args, **kwargs):
+ return _Condition(*args, **kwargs)
+
+class _Condition(_Verbose):
+
+ def __init__(self, lock=None, verbose=None):
+ _Verbose.__init__(self, verbose)
+ if lock is None:
+ lock = RLock()
+ self.__lock = lock
+ # Export the lock's acquire() and release() methods
+ self.acquire = lock.acquire
+ self.release = lock.release
+ # If the lock defines _release_save() and/or _acquire_restore(),
+ # these override the default implementations (which just call
+ # release() and acquire() on the lock). Ditto for _is_owned().
+ try:
+ self._release_save = lock._release_save
+ except AttributeError:
+ pass
+ try:
+ self._acquire_restore = lock._acquire_restore
+ except AttributeError:
+ pass
+ try:
+ self._is_owned = lock._is_owned
+ except AttributeError:
+ pass
+ self.__waiters = []
+
+ def __repr__(self):
+ return "<Condition(%s, %d)>" % (self.__lock, len(self.__waiters))
+
+ def _release_save(self):
+ self.__lock.release() # No state to save
+
+ def _acquire_restore(self, x):
+ self.__lock.acquire() # Ignore saved state
+
+ def _is_owned(self):
+ # Return True if lock is owned by currentThread.
+ # This method is called only if __lock doesn't have _is_owned().
+ if self.__lock.acquire(0):
+ self.__lock.release()
+ return False
+ else:
+ return True
+
+ def wait(self, timeout=None):
+ assert self._is_owned(), "wait() of un-acquire()d lock"
+ waiter = _allocate_lock()
+ waiter.acquire()
+ self.__waiters.append(waiter)
+ saved_state = self._release_save()
+ try: # restore state no matter what (e.g., KeyboardInterrupt)
+ if timeout is None:
+ waiter.acquire()
+ if __debug__:
+ self._note("%s.wait(): got it", self)
+ else:
+ # Balancing act: We can't afford a pure busy loop, so we
+ # have to sleep; but if we sleep the whole timeout time,
+ # we'll be unresponsive. The scheme here sleeps very
+ # little at first, longer as time goes on, but never longer
+ # than 20 times per second (or the timeout time remaining).
+ endtime = _time() + timeout
+ delay = 0.0005 # 500 us -> initial delay of 1 ms
+ while True:
+ gotit = waiter.acquire(0)
+ if gotit:
+ break
+ remaining = endtime - _time()
+ if remaining <= 0:
+ break
+ delay = min(delay * 2, remaining, .05)
+ _sleep(delay)
+ if not gotit:
+ if __debug__:
+ self._note("%s.wait(%s): timed out", self, timeout)
+ try:
+ self.__waiters.remove(waiter)
+ except ValueError:
+ pass
+ else:
+ if __debug__:
+ self._note("%s.wait(%s): got it", self, timeout)
+ finally:
+ self._acquire_restore(saved_state)
+
+ def notify(self, n=1):
+ assert self._is_owned(), "notify() of un-acquire()d lock"
+ __waiters = self.__waiters
+ waiters = __waiters[:n]
+ if not waiters:
+ if __debug__:
+ self._note("%s.notify(): no waiters", self)
+ return
+ self._note("%s.notify(): notifying %d waiter%s", self, n,
+ n!=1 and "s" or "")
+ for waiter in waiters:
+ waiter.release()
+ try:
+ __waiters.remove(waiter)
+ except ValueError:
+ pass
+
+ def notifyAll(self):
+ self.notify(len(self.__waiters))
+
+
+def Semaphore(*args, **kwargs):
+ return _Semaphore(*args, **kwargs)
+
+class _Semaphore(_Verbose):
+
+ # After Tim Peters' semaphore class, but not quite the same (no maximum)
+
+ def __init__(self, value=1, verbose=None):
+ assert value >= 0, "Semaphore initial value must be >= 0"
+ _Verbose.__init__(self, verbose)
+ self.__cond = Condition(Lock())
+ self.__value = value
+
+ def acquire(self, blocking=1):
+ rc = False
+ self.__cond.acquire()
+ while self.__value == 0:
+ if not blocking:
+ break
+ if __debug__:
+ self._note("%s.acquire(%s): blocked waiting, value=%s",
+ self, blocking, self.__value)
+ self.__cond.wait()
+ else:
+ self.__value = self.__value - 1
+ if __debug__:
+ self._note("%s.acquire: success, value=%s",
+ self, self.__value)
+ rc = True
+ self.__cond.release()
+ return rc
+
+ def release(self):
+ self.__cond.acquire()
+ self.__value = self.__value + 1
+ if __debug__:
+ self._note("%s.release: success, value=%s",
+ self, self.__value)
+ self.__cond.notify()
+ self.__cond.release()
+
+
+def BoundedSemaphore(*args, **kwargs):
+ return _BoundedSemaphore(*args, **kwargs)
+
+class _BoundedSemaphore(_Semaphore):
+ """Semaphore that checks that # releases is <= # acquires"""
+ def __init__(self, value=1, verbose=None):
+ _Semaphore.__init__(self, value, verbose)
+ self._initial_value = value
+
+ def release(self):
+ if self._Semaphore__value >= self._initial_value:
+ raise ValueError, "Semaphore released too many times"
+ return _Semaphore.release(self)
+
+
+def Event(*args, **kwargs):
+ return _Event(*args, **kwargs)
+
+class _Event(_Verbose):
+
+ # After Tim Peters' event class (without is_posted())
+
+ def __init__(self, verbose=None):
+ _Verbose.__init__(self, verbose)
+ self.__cond = Condition(Lock())
+ self.__flag = False
+
+ def isSet(self):
+ return self.__flag
+
+ def set(self):
+ self.__cond.acquire()
+ try:
+ self.__flag = True
+ self.__cond.notifyAll()
+ finally:
+ self.__cond.release()
+
+ def clear(self):
+ self.__cond.acquire()
+ try:
+ self.__flag = False
+ finally:
+ self.__cond.release()
+
+ def wait(self, timeout=None):
+ self.__cond.acquire()
+ try:
+ if not self.__flag:
+ self.__cond.wait(timeout)
+ finally:
+ self.__cond.release()
+
+# Helper to generate new thread names
+_counter = 0
+def _newname(template="Thread-%d"):
+ global _counter
+ _counter = _counter + 1
+ return template % _counter
+
+# Active thread administration
+_active_limbo_lock = _allocate_lock()
+_active = {}
+_limbo = {}
+
+
+# Main class for threads
+
+class Thread(_Verbose):
+
+ __initialized = False
+ # Need to store a reference to sys.exc_info for printing
+ # out exceptions when a thread tries to use a global var. during interp.
+ # shutdown and thus raises an exception about trying to perform some
+ # operation on/with a NoneType
+ __exc_info = _sys.exc_info
+
+ def __init__(self, group=None, target=None, name=None,
+ args=(), kwargs={}, verbose=None):
+ assert group is None, "group argument must be None for now"
+ _Verbose.__init__(self, verbose)
+ self.__target = target
+ self.__name = str(name or _newname())
+ self.__args = args
+ self.__kwargs = kwargs
+ self.__daemonic = self._set_daemon()
+ self.__started = False
+ self.__stopped = False
+ self.__block = Condition(Lock())
+ self.__initialized = True
+ # sys.stderr is not stored in the class like
+ # sys.exc_info since it can be changed between instances
+ self.__stderr = _sys.stderr
+
+ def _set_daemon(self):
+ # Overridden in _MainThread and _DummyThread
+ return currentThread().isDaemon()
+
+ def __repr__(self):
+ assert self.__initialized, "Thread.__init__() was not called"
+ status = "initial"
+ if self.__started:
+ status = "started"
+ if self.__stopped:
+ status = "stopped"
+ if self.__daemonic:
+ status = status + " daemon"
+ return "<%s(%s, %s)>" % (self.__class__.__name__, self.__name, status)
+
+ def start(self):
+ assert self.__initialized, "Thread.__init__() not called"
+ assert not self.__started, "thread already started"
+ if __debug__:
+ self._note("%s.start(): starting thread", self)
+ _active_limbo_lock.acquire()
+ _limbo[self] = self
+ _active_limbo_lock.release()
+ _start_new_thread(self.__bootstrap, ())
+ self.__started = True
+ _sleep(0.000001) # 1 usec, to let the thread run (Solaris hack)
+
+ def run(self):
+ if self.__target:
+ self.__target(*self.__args, **self.__kwargs)
+
+ def __bootstrap(self):
+ try:
+ self.__started = True
+ _active_limbo_lock.acquire()
+ _active[_get_ident()] = self
+ del _limbo[self]
+ _active_limbo_lock.release()
+ if __debug__:
+ self._note("%s.__bootstrap(): thread started", self)
+
+ if _trace_hook:
+ self._note("%s.__bootstrap(): registering trace hook", self)
+ _sys.settrace(_trace_hook)
+ if _profile_hook:
+ self._note("%s.__bootstrap(): registering profile hook", self)
+ _sys.setprofile(_profile_hook)
+
+ try:
+ self.run()
+ except SystemExit:
+ if __debug__:
+ self._note("%s.__bootstrap(): raised SystemExit", self)
+ except:
+ if __debug__:
+ self._note("%s.__bootstrap(): unhandled exception", self)
+ # If sys.stderr is no more (most likely from interpreter
+ # shutdown) use self.__stderr. Otherwise still use sys (as in
+ # _sys) in case sys.stderr was redefined since the creation of
+ # self.
+ if _sys:
+ _sys.stderr.write("Exception in thread %s:\n%s\n" %
+ (self.getName(), _format_exc()))
+ else:
+ # Do the best job possible w/o a huge amt. of code to
+ # approximate a traceback (code ideas from
+ # Lib/traceback.py)
+ exc_type, exc_value, exc_tb = self.__exc_info()
+ try:
+ print>>self.__stderr, (
+ "Exception in thread " + self.getName() +
+ " (most likely raised during interpreter shutdown):")
+ print>>self.__stderr, (
+ "Traceback (most recent call last):")
+ while exc_tb:
+ print>>self.__stderr, (
+ ' File "%s", line %s, in %s' %
+ (exc_tb.tb_frame.f_code.co_filename,
+ exc_tb.tb_lineno,
+ exc_tb.tb_frame.f_code.co_name))
+ exc_tb = exc_tb.tb_next
+ print>>self.__stderr, ("%s: %s" % (exc_type, exc_value))
+ # Make sure that exc_tb gets deleted since it is a memory
+ # hog; deleting everything else is just for thoroughness
+ finally:
+ del exc_type, exc_value, exc_tb
+ else:
+ if __debug__:
+ self._note("%s.__bootstrap(): normal return", self)
+ finally:
+ self.__stop()
+ try:
+ self.__delete()
+ except:
+ pass
+
+ def __stop(self):
+ self.__block.acquire()
+ self.__stopped = True
+ self.__block.notifyAll()
+ self.__block.release()
+
+ def __delete(self):
+ "Remove current thread from the dict of currently running threads."
+
+ # Notes about running with dummy_thread:
+ #
+ # Must take care to not raise an exception if dummy_thread is being
+ # used (and thus this module is being used as an instance of
+ # dummy_threading). dummy_thread.get_ident() always returns -1 since
+ # there is only one thread if dummy_thread is being used. Thus
+ # len(_active) is always <= 1 here, and any Thread instance created
+ # overwrites the (if any) thread currently registered in _active.
+ #
+ # An instance of _MainThread is always created by 'threading'. This
+ # gets overwritten the instant an instance of Thread is created; both
+ # threads return -1 from dummy_thread.get_ident() and thus have the
+ # same key in the dict. So when the _MainThread instance created by
+ # 'threading' tries to clean itself up when atexit calls this method
+ # it gets a KeyError if another Thread instance was created.
+ #
+ # This all means that KeyError from trying to delete something from
+ # _active if dummy_threading is being used is a red herring. But
+ # since it isn't if dummy_threading is *not* being used then don't
+ # hide the exception.
+
+ _active_limbo_lock.acquire()
+ try:
+ try:
+ del _active[_get_ident()]
+ except KeyError:
+ if 'dummy_threading' not in _sys.modules:
+ raise
+ finally:
+ _active_limbo_lock.release()
+
+ def join(self, timeout=None):
+ assert self.__initialized, "Thread.__init__() not called"
+ assert self.__started, "cannot join thread before it is started"
+ assert self is not currentThread(), "cannot join current thread"
+ if __debug__:
+ if not self.__stopped:
+ self._note("%s.join(): waiting until thread stops", self)
+ self.__block.acquire()
+ try:
+ if timeout is None:
+ while not self.__stopped:
+ self.__block.wait()
+ if __debug__:
+ self._note("%s.join(): thread stopped", self)
+ else:
+ deadline = _time() + timeout
+ while not self.__stopped:
+ delay = deadline - _time()
+ if delay <= 0:
+ if __debug__:
+ self._note("%s.join(): timed out", self)
+ break
+ self.__block.wait(delay)
+ else:
+ if __debug__:
+ self._note("%s.join(): thread stopped", self)
+ finally:
+ self.__block.release()
+
+ def getName(self):
+ assert self.__initialized, "Thread.__init__() not called"
+ return self.__name
+
+ def setName(self, name):
+ assert self.__initialized, "Thread.__init__() not called"
+ self.__name = str(name)
+
+ def isAlive(self):
+ assert self.__initialized, "Thread.__init__() not called"
+ return self.__started and not self.__stopped
+
+ def isDaemon(self):
+ assert self.__initialized, "Thread.__init__() not called"
+ return self.__daemonic
+
+ def setDaemon(self, daemonic):
+ assert self.__initialized, "Thread.__init__() not called"
+ assert not self.__started, "cannot set daemon status of active thread"
+ self.__daemonic = daemonic
+
+# The timer class was contributed by Itamar Shtull-Trauring
+
+def Timer(*args, **kwargs):
+ return _Timer(*args, **kwargs)
+
+class _Timer(Thread):
+ """Call a function after a specified number of seconds:
+
+ t = Timer(30.0, f, args=[], kwargs={})
+ t.start()
+ t.cancel() # stop the timer's action if it's still waiting
+ """
+
+ def __init__(self, interval, function, args=[], kwargs={}):
+ Thread.__init__(self)
+ self.interval = interval
+ self.function = function
+ self.args = args
+ self.kwargs = kwargs
+ self.finished = Event()
+
+ def cancel(self):
+ """Stop the timer if it hasn't finished yet"""
+ self.finished.set()
+
+ def run(self):
+ self.finished.wait(self.interval)
+ if not self.finished.isSet():
+ self.function(*self.args, **self.kwargs)
+ self.finished.set()
+
+# Special thread class to represent the main thread
+# This is garbage collected through an exit handler
+
+class _MainThread(Thread):
+
+ def __init__(self):
+ Thread.__init__(self, name="MainThread")
+ self._Thread__started = True
+ _active_limbo_lock.acquire()
+ _active[_get_ident()] = self
+ _active_limbo_lock.release()
+ import atexit
+ atexit.register(self.__exitfunc)
+
+ def _set_daemon(self):
+ return False
+
+ def __exitfunc(self):
+ self._Thread__stop()
+ t = _pickSomeNonDaemonThread()
+ if t:
+ if __debug__:
+ self._note("%s: waiting for other threads", self)
+ while t:
+ t.join()
+ t = _pickSomeNonDaemonThread()
+ if __debug__:
+ self._note("%s: exiting", self)
+ self._Thread__delete()
+
+def _pickSomeNonDaemonThread():
+ for t in enumerate():
+ if not t.isDaemon() and t.isAlive():
+ return t
+ return None
+
+
+# Dummy thread class to represent threads not started here.
+# These aren't garbage collected when they die,
+# nor can they be waited for.
+# Their purpose is to return *something* from currentThread().
+# They are marked as daemon threads so we won't wait for them
+# when we exit (conform previous semantics).
+
+class _DummyThread(Thread):
+
+ def __init__(self):
+ Thread.__init__(self, name=_newname("Dummy-%d"))
+ self._Thread__started = True
+ _active_limbo_lock.acquire()
+ _active[_get_ident()] = self
+ _active_limbo_lock.release()
+
+ def _set_daemon(self):
+ return True
+
+ def join(self, timeout=None):
+ assert False, "cannot join a dummy thread"
+
+
+# Global API functions
+
+def currentThread():
+ try:
+ return _active[_get_ident()]
+ except KeyError:
+ ##print "currentThread(): no current thread for", _get_ident()
+ return _DummyThread()
+
+def activeCount():
+ _active_limbo_lock.acquire()
+ count = len(_active) + len(_limbo)
+ _active_limbo_lock.release()
+ return count
+
+def enumerate():
+ _active_limbo_lock.acquire()
+ active = _active.values() + _limbo.values()
+ _active_limbo_lock.release()
+ return active
+
+# Create the main thread object
+
+_MainThread()
+
+# get thread-local implementation, either from the thread
+# module, or from the python fallback
+
+try:
+ from thread import _local as local
+except ImportError:
+ from _threading_local import local
+
+
+# Self-test code
+
+def _test():
+
+ class BoundedQueue(_Verbose):
+
+ def __init__(self, limit):
+ _Verbose.__init__(self)
+ self.mon = RLock()
+ self.rc = Condition(self.mon)
+ self.wc = Condition(self.mon)
+ self.limit = limit
+ self.queue = deque()
+
+ def put(self, item):
+ self.mon.acquire()
+ while len(self.queue) >= self.limit:
+ self._note("put(%s): queue full", item)
+ self.wc.wait()
+ self.queue.append(item)
+ self._note("put(%s): appended, length now %d",
+ item, len(self.queue))
+ self.rc.notify()
+ self.mon.release()
+
+ def get(self):
+ self.mon.acquire()
+ while not self.queue:
+ self._note("get(): queue empty")
+ self.rc.wait()
+ item = self.queue.popleft()
+ self._note("get(): got %s, %d left", item, len(self.queue))
+ self.wc.notify()
+ self.mon.release()
+ return item
+
+ class ProducerThread(Thread):
+
+ def __init__(self, queue, quota):
+ Thread.__init__(self, name="Producer")
+ self.queue = queue
+ self.quota = quota
+
+ def run(self):
+ from random import random
+ counter = 0
+ while counter < self.quota:
+ counter = counter + 1
+ self.queue.put("%s.%d" % (self.getName(), counter))
+ _sleep(random() * 0.00001)
+
+
+ class ConsumerThread(Thread):
+
+ def __init__(self, queue, count):
+ Thread.__init__(self, name="Consumer")
+ self.queue = queue
+ self.count = count
+
+ def run(self):
+ while self.count > 0:
+ item = self.queue.get()
+ print item
+ self.count = self.count - 1
+
+ NP = 3
+ QL = 4
+ NI = 5
+
+ Q = BoundedQueue(QL)
+ P = []
+ for i in range(NP):
+ t = ProducerThread(Q, NI)
+ t.setName("Producer-%d" % (i+1))
+ P.append(t)
+ C = ConsumerThread(Q, NI*NP)
+ for t in P:
+ t.start()
+ _sleep(0.000001)
+ C.start()
+ for t in P:
+ t.join()
+ C.join()
+
+if __name__ == '__main__':
+ _test()
diff --git a/gnuradio-core/src/python/gnuradio/gr/hier_block.py b/gnuradio-core/src/python/gnuradio/gr/hier_block.py
new file mode 100644
index 000000000..d669d7178
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/hier_block.py
@@ -0,0 +1,132 @@
+#
+# 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_swig_python import io_signature
+import weakref
+
+class hier_block_base(object):
+ """
+ Abstract base class for hierarchical signal processing blocks.
+ """
+ def __init__(self, fg):
+ """
+ @param fg: The flow graph that contains this hierarchical block.
+ @type fg: gr.flow_graph
+ """
+ self.fg = weakref.proxy(fg)
+
+ def input_signature(self):
+ """
+ @return input signature of hierarchical block.
+ @rtype gr.io_signature
+ """
+ raise NotImplemented
+
+ def output_signature(self):
+ """
+ @return output signature of hierarchical block.
+ @rtype gr.io_signature
+ """
+ raise NotImplemented
+
+ def resolve_input_port(self, port_number):
+ """
+ @param port_number: which input port number to resolve to an endpoint.
+ @type port_number: int
+ @return: sequence of endpoints
+ @rtype: sequence of endpoint
+
+ Note that an input port can resolve to more than one endpoint.
+ """
+ raise NotImplemented
+
+ def resolve_output_port(self, port_number):
+ """
+ @param port_number: which output port number to resolve to an endpoint.
+ @type port_number: int
+ @return: endpoint
+ @rtype: endpoint
+
+ Output ports resolve to a single endpoint.
+ """
+ raise NotImplemented
+
+
+class hier_block(hier_block_base):
+ """
+ Simple concrete class for building hierarchical blocks.
+
+ This class assumes that there is at most a single block at the
+ head of the chain and a single block at the end of the chain.
+ Either head or tail may be None indicating a sink or source respectively.
+
+ If you needs something more elaborate than this, derive a new class from
+ hier_block_base.
+ """
+ def __init__(self, fg, head_block, tail_block):
+ """
+ @param fg: The flow graph that contains this hierarchical block.
+ @type fg: flow_graph
+ @param head_block: the first block in the signal processing chain.
+ @type head_block: None or subclass of gr.block or gr.hier_block_base
+ @param tail_block: the last block in the signal processing chain.
+ @type tail_block: None or subclass of gr.block or gr.hier_block_base
+ """
+ hier_block_base.__init__(self, fg)
+ # FIXME add type checks here for easier debugging of misuse
+ self.head = head_block
+ self.tail = tail_block
+
+ def input_signature(self):
+ if self.head:
+ return self.head.input_signature()
+ else:
+ return io_signature(0,0,0)
+
+ def output_signature(self):
+ if self.tail:
+ return self.tail.output_signature()
+ else:
+ return io_signature(0,0,0)
+
+ def resolve_input_port(self, port_number):
+ return ((self.head, port_number),)
+
+ def resolve_output_port(self, port_number):
+ return (self.tail, port_number)
+
+
+class compose(hier_block):
+ """
+ Compose one or more blocks (primitive or hierarchical) into a new hierarchical block.
+ """
+ def __init__(self, fg, *blocks):
+ """
+ @param fg: flow graph
+ @type fg: gr.flow_graph
+ @param *blocks: list of blocks
+ @type *blocks: list of blocks
+ """
+ if len(blocks) < 1:
+ raise ValueError, ("compose requires at least one block; none provided.")
+ if len(blocks) > 1:
+ fg.connect(*blocks)
+ hier_block.__init__(self, fg, blocks[0], blocks[-1])
diff --git a/gnuradio-core/src/python/gnuradio/gr/prefs.py b/gnuradio-core/src/python/gnuradio/gr/prefs.py
new file mode 100644
index 000000000..fa1291271
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/prefs.py
@@ -0,0 +1,129 @@
+#
+# 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 gnuradio_swig_python as gsp
+_prefs_base = gsp.gr_prefs
+
+
+import ConfigParser
+import os
+import os.path
+import sys
+
+
+def _user_prefs_filename():
+ return os.path.expanduser('~/.gnuradio/config.conf')
+
+def _sys_prefs_dirname():
+ return os.path.join(gsp.prefix(), 'etc/gnuradio/conf.d')
+
+def _bool(x):
+ """
+ Try to coerce obj to a True or False
+ """
+ if isinstance(x, bool):
+ return x
+ if isinstance(x, (float, int)):
+ return bool(x)
+ raise TypeError, x
+
+
+class _prefs(_prefs_base):
+ """
+ Derive our 'real class' from the stubbed out base class that has support
+ for SWIG directors. This allows C++ code to magically and transparently
+ invoke the methods in this python class.
+ """
+ def __init__(self):
+ _prefs_base.__init__(self)
+ self.cp = ConfigParser.RawConfigParser()
+
+ def _sys_prefs_filenames(self):
+ dir = _sys_prefs_dirname()
+ try:
+ fnames = os.listdir(dir)
+ except (IOError, OSError):
+ return []
+ fnames.sort()
+ return [os.path.join(dir, f) for f in fnames]
+
+
+ def _read_files(self):
+ filenames = self._sys_prefs_filenames()
+ filenames.append(_user_prefs_filename())
+ #print "filenames: ", filenames
+ self.cp.read(filenames)
+
+ def __getattr__(self, name):
+ return getattr(self.cp, name)
+
+ # ----------------------------------------------------------------
+ # These methods override the C++ virtual methods of the same name
+ # ----------------------------------------------------------------
+ def has_section(self, section):
+ return self.cp.has_section(section)
+
+ def has_option(self, section, option):
+ return self.cp.has_option(section, option)
+
+ def get_string(self, section, option, default_val):
+ try:
+ return self.cp.get(section, option)
+ except:
+ return default_val
+
+ def get_bool(self, section, option, default_val):
+ try:
+ return self.cp.getboolean(section, option)
+ except:
+ return default_val
+
+ def get_long(self, section, option, default_val):
+ try:
+ return self.cp.getint(section, option)
+ except:
+ return default_val
+
+ def get_double(self, section, option, default_val):
+ try:
+ return self.cp.getfloat(section, option)
+ except:
+ return default_val
+ # ----------------------------------------------------------------
+ # End override of C++ virtual methods
+ # ----------------------------------------------------------------
+
+
+_prefs_db = _prefs()
+
+# if GR_DONT_LOAD_PREFS is set, don't load them.
+# (make check uses this to avoid interactions.)
+if os.getenv("GR_DONT_LOAD_PREFS", None) is None:
+ _prefs_db._read_files()
+
+
+_prefs_base.set_singleton(_prefs_db) # tell C++ what instance to use
+
+def prefs():
+ """
+ Return the global preference data base
+ """
+ return _prefs_db
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_add_and_friends.py b/gnuradio-core/src/python/gnuradio/gr/qa_add_and_friends.py
new file mode 100755
index 000000000..6cb74e9f5
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_add_and_friends.py
@@ -0,0 +1,129 @@
+#!/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, gr_unittest
+
+class test_head (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown (self):
+ self.fg = None
+
+ def help_ii (self, src_data, exp_data, op):
+ for s in zip (range (len (src_data)), src_data):
+ src = gr.vector_source_i (s[1])
+ self.fg.connect (src, (op, s[0]))
+ dst = gr.vector_sink_i ()
+ self.fg.connect (op, dst)
+ self.fg.run ()
+ result_data = dst.data ()
+ self.assertEqual (exp_data, result_data)
+
+ def help_ff (self, src_data, exp_data, op):
+ for s in zip (range (len (src_data)), src_data):
+ src = gr.vector_source_f (s[1])
+ self.fg.connect (src, (op, s[0]))
+ dst = gr.vector_sink_f ()
+ self.fg.connect (op, dst)
+ self.fg.run ()
+ result_data = dst.data ()
+ self.assertEqual (exp_data, result_data)
+
+ def help_cc (self, src_data, exp_data, op):
+ for s in zip (range (len (src_data)), src_data):
+ src = gr.vector_source_c (s[1])
+ self.fg.connect (src, (op, s[0]))
+ dst = gr.vector_sink_c ()
+ self.fg.connect (op, dst)
+ self.fg.run ()
+ result_data = dst.data ()
+ self.assertEqual (exp_data, result_data)
+
+ def test_add_const_ii (self):
+ src_data = (1, 2, 3, 4, 5)
+ expected_result = (6, 7, 8, 9, 10)
+ op = gr.add_const_ii (5)
+ self.help_ii ((src_data,), expected_result, op)
+
+ def test_add_const_cc (self):
+ src_data = (1, 2, 3, 4, 5)
+ expected_result = (1+5j, 2+5j, 3+5j, 4+5j, 5+5j)
+ op = gr.add_const_cc (5j)
+ self.help_cc ((src_data,), expected_result, op)
+
+ def test_mult_const_ii (self):
+ src_data = (-1, 0, 1, 2, 3)
+ expected_result = (-5, 0, 5, 10, 15)
+ op = gr.multiply_const_ii (5)
+ self.help_ii ((src_data,), expected_result, op)
+
+ def test_add_ii (self):
+ src1_data = (1, 2, 3, 4, 5)
+ src2_data = (8, -3, 4, 8, 2)
+ expected_result = (9, -1, 7, 12, 7)
+ op = gr.add_ii ()
+ self.help_ii ((src1_data, src2_data),
+ expected_result, op)
+
+ def test_mult_ii (self):
+ src1_data = (1, 2, 3, 4, 5)
+ src2_data = (8, -3, 4, 8, 2)
+ expected_result = (8, -6, 12, 32, 10)
+ op = gr.multiply_ii ()
+ self.help_ii ((src1_data, src2_data),
+ expected_result, op)
+
+ def test_sub_ii_1 (self):
+ src1_data = (1, 2, 3, 4, 5)
+ expected_result = (-1, -2, -3, -4, -5)
+ op = gr.sub_ii ()
+ self.help_ii ((src1_data,),
+ expected_result, op)
+
+ def test_sub_ii_2 (self):
+ src1_data = (1, 2, 3, 4, 5)
+ src2_data = (8, -3, 4, 8, 2)
+ expected_result = (-7, 5, -1, -4, 3)
+ op = gr.sub_ii ()
+ self.help_ii ((src1_data, src2_data),
+ expected_result, op)
+
+ def test_div_ff_1 (self):
+ src1_data = (1, 2, 4, -8)
+ expected_result = (1, 0.5, 0.25, -.125)
+ op = gr.divide_ff ()
+ self.help_ff ((src1_data,),
+ expected_result, op)
+
+ def test_div_ff_2 (self):
+ src1_data = ( 5, 9, -15, 1024)
+ src2_data = (10, 3, -5, 64)
+ expected_result = (0.5, 3, 3, 16)
+ op = gr.divide_ff ()
+ self.help_ff ((src1_data, src2_data),
+ expected_result, op)
+
+
+if __name__ == '__main__':
+ gr_unittest.main ()
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_add_v_and_friends.py b/gnuradio-core/src/python/gnuradio/gr/qa_add_v_and_friends.py
new file mode 100755
index 000000000..11e69e373
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_add_v_and_friends.py
@@ -0,0 +1,353 @@
+#!/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, gr_unittest
+
+class test_add_v_and_friends(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.fg = gr.flow_graph()
+
+ def tearDown(self):
+ self.fg = None
+
+ def help_ss(self, size, src_data, exp_data, op):
+ for s in zip(range (len (src_data)), src_data):
+ src = gr.vector_source_s(s[1])
+ srcv = gr.stream_to_vector(gr.sizeof_short, size)
+ self.fg.connect(src, srcv)
+ self.fg.connect(srcv, (op, s[0]))
+ rhs = gr.vector_to_stream(gr.sizeof_short, size)
+ dst = gr.vector_sink_s()
+ self.fg.connect(op, rhs, dst)
+ self.fg.run()
+ result_data = dst.data()
+ self.assertEqual(exp_data, result_data)
+
+ def help_ii(self, size, src_data, exp_data, op):
+ for s in zip(range (len (src_data)), src_data):
+ src = gr.vector_source_i(s[1])
+ srcv = gr.stream_to_vector(gr.sizeof_int, size)
+ self.fg.connect(src, srcv)
+ self.fg.connect(srcv, (op, s[0]))
+ rhs = gr.vector_to_stream(gr.sizeof_int, size)
+ dst = gr.vector_sink_i()
+ self.fg.connect(op, rhs, dst)
+ self.fg.run()
+ result_data = dst.data()
+ self.assertEqual(exp_data, result_data)
+
+ def help_ff(self, size, src_data, exp_data, op):
+ for s in zip(range (len (src_data)), src_data):
+ src = gr.vector_source_f(s[1])
+ srcv = gr.stream_to_vector(gr.sizeof_float, size)
+ self.fg.connect(src, srcv)
+ self.fg.connect(srcv, (op, s[0]))
+ rhs = gr.vector_to_stream(gr.sizeof_float, size)
+ dst = gr.vector_sink_f()
+ self.fg.connect(op, rhs, dst)
+ self.fg.run()
+ result_data = dst.data()
+ self.assertEqual(exp_data, result_data)
+
+ def help_cc(self, size, src_data, exp_data, op):
+ for s in zip(range (len (src_data)), src_data):
+ src = gr.vector_source_c(s[1])
+ srcv = gr.stream_to_vector(gr.sizeof_gr_complex, size)
+ self.fg.connect(src, srcv)
+ self.fg.connect(srcv, (op, s[0]))
+ rhs = gr.vector_to_stream(gr.sizeof_gr_complex, size)
+ dst = gr.vector_sink_c()
+ self.fg.connect(op, rhs, dst)
+ self.fg.run()
+ result_data = dst.data()
+ self.assertEqual(exp_data, result_data)
+
+ def help_const_ss(self, src_data, exp_data, op):
+ src = gr.vector_source_s(src_data)
+ srcv = gr.stream_to_vector(gr.sizeof_short, len(src_data))
+ rhs = gr.vector_to_stream(gr.sizeof_short, len(src_data))
+ dst = gr.vector_sink_s()
+ self.fg.connect(src, srcv, op, rhs, dst)
+ self.fg.run()
+ result_data = dst.data()
+ self.assertEqual(exp_data, result_data)
+
+ def help_const_ii(self, src_data, exp_data, op):
+ src = gr.vector_source_i(src_data)
+ srcv = gr.stream_to_vector(gr.sizeof_int, len(src_data))
+ rhs = gr.vector_to_stream(gr.sizeof_int, len(src_data))
+ dst = gr.vector_sink_i()
+ self.fg.connect(src, srcv, op, rhs, dst)
+ self.fg.run()
+ result_data = dst.data()
+ self.assertEqual(exp_data, result_data)
+
+ def help_const_ff(self, src_data, exp_data, op):
+ src = gr.vector_source_f(src_data)
+ srcv = gr.stream_to_vector(gr.sizeof_float, len(src_data))
+ rhs = gr.vector_to_stream(gr.sizeof_float, len(src_data))
+ dst = gr.vector_sink_f()
+ self.fg.connect(src, srcv, op, rhs, dst)
+ self.fg.run()
+ result_data = dst.data()
+ self.assertEqual(exp_data, result_data)
+
+ def help_const_cc(self, src_data, exp_data, op):
+ src = gr.vector_source_c(src_data)
+ srcv = gr.stream_to_vector(gr.sizeof_gr_complex, len(src_data))
+ rhs = gr.vector_to_stream(gr.sizeof_gr_complex, len(src_data))
+ dst = gr.vector_sink_c()
+ self.fg.connect(src, srcv, op, rhs, dst)
+ self.fg.run()
+ result_data = dst.data()
+ self.assertEqual(exp_data, result_data)
+
+
+ def test_add_vss_one(self):
+ src1_data = (1,)
+ src2_data = (2,)
+ src3_data = (3,)
+ expected_result = (6,)
+ op = gr.add_vss(1)
+ self.help_ss(1, (src1_data, src2_data, src3_data), expected_result, op)
+
+ def test_add_vss_five(self):
+ src1_data = (1, 2, 3, 4, 5)
+ src2_data = (6, 7, 8, 9, 10)
+ src3_data = (11, 12, 13, 14, 15)
+ expected_result = (18, 21, 24, 27, 30)
+ op = gr.add_vss(5)
+ self.help_ss(5, (src1_data, src2_data, src3_data), expected_result, op)
+
+ def test_add_vii_one(self):
+ src1_data = (1,)
+ src2_data = (2,)
+ src3_data = (3,)
+ expected_result = (6,)
+ op = gr.add_vii(1)
+ self.help_ii(1, (src1_data, src2_data, src3_data), expected_result, op)
+
+ def test_add_vii_five(self):
+ src1_data = (1, 2, 3, 4, 5)
+ src2_data = (6, 7, 8, 9, 10)
+ src3_data = (11, 12, 13, 14, 15)
+ expected_result = (18, 21, 24, 27, 30)
+ op = gr.add_vii(5)
+ self.help_ii(5, (src1_data, src2_data, src3_data), expected_result, op)
+
+ def test_add_vff_one(self):
+ src1_data = (1.0,)
+ src2_data = (2.0,)
+ src3_data = (3.0,)
+ expected_result = (6.0,)
+ op = gr.add_vff(1)
+ self.help_ff(1, (src1_data, src2_data, src3_data), expected_result, op)
+
+ def test_add_vff_five(self):
+ src1_data = (1.0, 2.0, 3.0, 4.0, 5.0)
+ src2_data = (6.0, 7.0, 8.0, 9.0, 10.0)
+ src3_data = (11.0, 12.0, 13.0, 14.0, 15.0)
+ expected_result = (18.0, 21.0, 24.0, 27.0, 30.0)
+ op = gr.add_vff(5)
+ self.help_ff(5, (src1_data, src2_data, src3_data), expected_result, op)
+
+ def test_add_vcc_one(self):
+ src1_data = (1.0+2.0j,)
+ src2_data = (3.0+4.0j,)
+ src3_data = (5.0+6.0j,)
+ expected_result = (9.0+12j,)
+ op = gr.add_vcc(1)
+ self.help_cc(1, (src1_data, src2_data, src3_data), expected_result, op)
+
+ def test_add_vcc_five(self):
+ src1_data = (1.0+2.0j, 3.0+4.0j, 5.0+6.0j, 7.0+8.0j, 9.0+10.0j)
+ src2_data = (11.0+12.0j, 13.0+14.0j, 15.0+16.0j, 17.0+18.0j, 19.0+20.0j)
+ src3_data = (21.0+22.0j, 23.0+24.0j, 25.0+26.0j, 27.0+28.0j, 29.0+30.0j)
+ expected_result = (33.0+36.0j, 39.0+42.0j, 45.0+48.0j, 51.0+54.0j, 57.0+60.0j)
+ op = gr.add_vcc(5)
+ self.help_cc(5, (src1_data, src2_data, src3_data), expected_result, op)
+
+ def test_add_const_vss_one(self):
+ src_data = (1,)
+ op = gr.add_const_vss((2,))
+ exp_data = (3,)
+ self.help_const_ss(src_data, exp_data, op)
+
+ def test_add_const_vss_five(self):
+ src_data = (1, 2, 3, 4, 5)
+ op = gr.add_const_vss((6, 7, 8, 9, 10))
+ exp_data = (7, 9, 11, 13, 15)
+ self.help_const_ss(src_data, exp_data, op)
+
+ def test_add_const_vii_one(self):
+ src_data = (1,)
+ op = gr.add_const_vii((2,))
+ exp_data = (3,)
+ self.help_const_ii(src_data, exp_data, op)
+
+ def test_add_const_vii_five(self):
+ src_data = (1, 2, 3, 4, 5)
+ op = gr.add_const_vii((6, 7, 8, 9, 10))
+ exp_data = (7, 9, 11, 13, 15)
+ self.help_const_ii(src_data, exp_data, op)
+
+ def test_add_const_vff_one(self):
+ src_data = (1.0,)
+ op = gr.add_const_vff((2.0,))
+ exp_data = (3.0,)
+ self.help_const_ff(src_data, exp_data, op)
+
+ def test_add_const_vff_five(self):
+ src_data = (1.0, 2.0, 3.0, 4.0, 5.0)
+ op = gr.add_const_vff((6.0, 7.0, 8.0, 9.0, 10.0))
+ exp_data = (7.0, 9.0, 11.0, 13.0, 15.0)
+ self.help_const_ff(src_data, exp_data, op)
+
+ def test_add_const_vcc_one(self):
+ src_data = (1.0+2.0j,)
+ op = gr.add_const_vcc((2.0+3.0j,))
+ exp_data = (3.0+5.0j,)
+ self.help_const_cc(src_data, exp_data, op)
+
+ def test_add_const_vcc_five(self):
+ src_data = (1.0+2.0j, 3.0+4.0j, 5.0+6.0j, 7.0+8.0j, 9.0+10.0j)
+ op = gr.add_const_vcc((11.0+12.0j, 13.0+14.0j, 15.0+16.0j, 17.0+18.0j, 19.0+20.0j))
+ exp_data = (12.0+14.0j, 16.0+18.0j, 20.0+22.0j, 24.0+26.0j, 28.0+30.0j)
+ self.help_const_cc(src_data, exp_data, op)
+
+
+ def test_multiply_vss_one(self):
+ src1_data = (1,)
+ src2_data = (2,)
+ src3_data = (3,)
+ expected_result = (6,)
+ op = gr.multiply_vss(1)
+ self.help_ss(1, (src1_data, src2_data, src3_data), expected_result, op)
+
+ def test_multiply_vss_five(self):
+ src1_data = (1, 2, 3, 4, 5)
+ src2_data = (6, 7, 8, 9, 10)
+ src3_data = (11, 12, 13, 14, 15)
+ expected_result = (66, 168, 312, 504, 750)
+ op = gr.multiply_vss(5)
+ self.help_ss(5, (src1_data, src2_data, src3_data), expected_result, op)
+
+ def test_multiply_vii_one(self):
+ src1_data = (1,)
+ src2_data = (2,)
+ src3_data = (3,)
+ expected_result = (6,)
+ op = gr.multiply_vii(1)
+ self.help_ii(1, (src1_data, src2_data, src3_data), expected_result, op)
+
+ def test_multiply_vii_five(self):
+ src1_data = (1, 2, 3, 4, 5)
+ src2_data = (6, 7, 8, 9, 10)
+ src3_data = (11, 12, 13, 14, 15)
+ expected_result = (66, 168, 312, 504, 750)
+ op = gr.multiply_vii(5)
+ self.help_ii(5, (src1_data, src2_data, src3_data), expected_result, op)
+
+ def test_multiply_vff_one(self):
+ src1_data = (1.0,)
+ src2_data = (2.0,)
+ src3_data = (3.0,)
+ expected_result = (6.0,)
+ op = gr.multiply_vff(1)
+ self.help_ff(1, (src1_data, src2_data, src3_data), expected_result, op)
+
+ def test_multiply_vff_five(self):
+ src1_data = (1.0, 2.0, 3.0, 4.0, 5.0)
+ src2_data = (6.0, 7.0, 8.0, 9.0, 10.0)
+ src3_data = (11.0, 12.0, 13.0, 14.0, 15.0)
+ expected_result = (66.0, 168.0, 312.0, 504.0, 750.0)
+ op = gr.multiply_vff(5)
+ self.help_ff(5, (src1_data, src2_data, src3_data), expected_result, op)
+
+ def test_multiply_vcc_one(self):
+ src1_data = (1.0+2.0j,)
+ src2_data = (3.0+4.0j,)
+ src3_data = (5.0+6.0j,)
+ expected_result = (-85+20j,)
+ op = gr.multiply_vcc(1)
+ self.help_cc(1, (src1_data, src2_data, src3_data), expected_result, op)
+
+ def test_multiply_vcc_five(self):
+ src1_data = (1.0+2.0j, 3.0+4.0j, 5.0+6.0j, 7.0+8.0j, 9.0+10.0j)
+ src2_data = (11.0+12.0j, 13.0+14.0j, 15.0+16.0j, 17.0+18.0j, 19.0+20.0j)
+ src3_data = (21.0+22.0j, 23.0+24.0j, 25.0+26.0j, 27.0+28.0j, 29.0+30.0j)
+ expected_result = (-1021.0+428.0j, -2647.0+1754.0j, -4945.0+3704.0j, -8011.0+6374.0j, -11941.0+9860.0j)
+ op = gr.multiply_vcc(5)
+ self.help_cc(5, (src1_data, src2_data, src3_data), expected_result, op)
+
+ def test_multiply_const_vss_one(self):
+ src_data = (2,)
+ op = gr.multiply_const_vss((3,))
+ exp_data = (6,)
+ self.help_const_ss(src_data, exp_data, op)
+
+ def test_multiply_const_vss_five(self):
+ src_data = (1, 2, 3, 4, 5)
+ op = gr.multiply_const_vss((6, 7, 8, 9, 10))
+ exp_data = (6, 14, 24, 36, 50)
+ self.help_const_ss(src_data, exp_data, op)
+
+ def test_multiply_const_vii_one(self):
+ src_data = (2,)
+ op = gr.multiply_const_vii((3,))
+ exp_data = (6,)
+ self.help_const_ii(src_data, exp_data, op)
+
+ def test_multiply_const_vii_five(self):
+ src_data = (1, 2, 3, 4, 5)
+ op = gr.multiply_const_vii((6, 7, 8, 9, 10))
+ exp_data = (6, 14, 24, 36, 50)
+ self.help_const_ii(src_data, exp_data, op)
+
+ def test_multiply_const_vff_one(self):
+ src_data = (2.0,)
+ op = gr.multiply_const_vff((3.0,))
+ exp_data = (6.0,)
+ self.help_const_ff(src_data, exp_data, op)
+
+ def test_multiply_const_vff_five(self):
+ src_data = (1.0, 2.0, 3.0, 4.0, 5.0)
+ op = gr.multiply_const_vff((6.0, 7.0, 8.0, 9.0, 10.0))
+ exp_data = (6.0, 14.0, 24.0, 36.0, 50.0)
+ self.help_const_ff(src_data, exp_data, op)
+
+ def test_multiply_const_vcc_one(self):
+ src_data = (1.0+2.0j,)
+ op = gr.multiply_const_vcc((2.0+3.0j,))
+ exp_data = (-4.0+7.0j,)
+ self.help_const_cc(src_data, exp_data, op)
+
+ def test_multiply_const_vcc_five(self):
+ src_data = (1.0+2.0j, 3.0+4.0j, 5.0+6.0j, 7.0+8.0j, 9.0+10.0j)
+ op = gr.multiply_const_vcc((11.0+12.0j, 13.0+14.0j, 15.0+16.0j, 17.0+18.0j, 19.0+20.0j))
+ exp_data = (-13.0+34.0j, -17.0+94.0j, -21.0+170.0j, -25.0+262.0j, -29.0+370.0j)
+ self.help_const_cc(src_data, exp_data, op)
+
+
+if __name__ == '__main__':
+ gr_unittest.main ()
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_basic_flow_graph.py b/gnuradio-core/src/python/gnuradio/gr/qa_basic_flow_graph.py
new file mode 100755
index 000000000..0799c72e4
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_basic_flow_graph.py
@@ -0,0 +1,190 @@
+#!/usr/bin/env python
+
+from gnuradio import gr, gr_unittest
+
+
+# ----------------------------------------------------------------
+
+
+class test_basic_flow_graph (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.fg = gr.basic_flow_graph ()
+
+ def tearDown (self):
+ self.fg = None
+
+ def test_000_create_delete (self):
+ pass
+
+ def test_001a_insert_1 (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+ fg.connect (gr.endpoint (src1, 0), gr.endpoint (dst1, 0))
+
+ def test_001b_insert_1 (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+ fg.connect (src1, gr.endpoint (dst1, 0))
+
+ def test_001c_insert_1 (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+ fg.connect (gr.endpoint (src1, 0), dst1)
+
+ def test_001d_insert_1 (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+ fg.connect (src1, dst1)
+
+ def test_001e_insert_1 (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+ fg.connect ((src1, 0), (dst1, 0))
+
+ def test_002_dst_in_use (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ src2 = gr.null_source (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+ fg.connect ((src1, 0), (dst1, 0))
+ self.assertRaises (ValueError,
+ lambda : fg.connect ((src2, 0),
+ (dst1, 0)))
+
+ def test_003_no_such_src_port (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+ self.assertRaises (ValueError,
+ lambda : fg.connect ((src1, 1),
+ (dst1, 0)))
+
+ def test_004_no_such_dst_port (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+ self.assertRaises (ValueError,
+ lambda : fg.connect ((src1, 0),
+ (dst1, 1)))
+
+ def test_005_one_src_two_dst (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+ dst2 = gr.null_sink (gr.sizeof_int)
+ fg.connect ((src1, 0), (dst1, 0))
+ fg.connect ((src1, 0), (dst2, 0))
+
+ def test_006_check_item_sizes (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_char)
+ self.assertRaises (ValueError,
+ lambda : fg.connect ((src1, 0),
+ (dst1, 0)))
+
+ def test_007_validate (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+ dst2 = gr.null_sink (gr.sizeof_int)
+ fg.connect ((src1, 0), (dst1, 0))
+ fg.connect ((src1, 0), (dst2, 0))
+ fg.validate ()
+
+ def test_008_validate (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ nop1 = gr.nop (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+ dst2 = gr.null_sink (gr.sizeof_int)
+ fg.connect ((src1, 0), (nop1, 0))
+ fg.connect ((src1, 0), (nop1, 1))
+ fg.connect ((nop1, 0), (dst1, 0))
+ fg.connect ((nop1, 1), (dst2, 0))
+ fg.validate ()
+
+ def test_009_validate (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ nop1 = gr.nop (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+ dst2 = gr.null_sink (gr.sizeof_int)
+ fg.connect ((src1, 0), (nop1, 0))
+ fg.connect ((src1, 0), (nop1, 2))
+ fg.connect ((nop1, 0), (dst1, 0))
+ fg.connect ((nop1, 1), (dst2, 0))
+ self.assertRaises (ValueError,
+ lambda : fg.validate ())
+
+
+ def test_010_validate (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ nop1 = gr.nop (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+ dst2 = gr.null_sink (gr.sizeof_int)
+ fg.connect ((src1, 0), (nop1, 0))
+ fg.connect ((src1, 0), (nop1, 1))
+ fg.connect ((nop1, 0), (dst1, 0))
+ fg.connect ((nop1, 2), (dst2, 0))
+ self.assertRaises (ValueError,
+ lambda : fg.validate ())
+
+
+ def test_011_disconnect (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ nop1 = gr.nop (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+ dst2 = gr.null_sink (gr.sizeof_int)
+ fg.connect ((src1, 0), (nop1, 0))
+ fg.connect ((src1, 0), (nop1, 1))
+ fg.connect ((nop1, 0), (dst1, 0))
+ fg.connect ((nop1, 1), (dst2, 0))
+ fg.validate ()
+ fg.disconnect ((src1, 0), (nop1, 1))
+ fg.validate ()
+ self.assertRaises (ValueError,
+ lambda : fg.disconnect ((src1, 0),
+ (nop1, 1)))
+
+ def test_012_connect (self):
+ fg = self.fg
+ src_data = (0, 1, 2, 3)
+ expected_result = (2, 3, 4, 5)
+ src1 = gr.vector_source_i (src_data)
+ op = gr.add_const_ii (2)
+ dst1 = gr.vector_sink_i ()
+ fg.connect (src1, op)
+ fg.connect (op, dst1)
+ # print "edge_list:", fg.edge_list
+ fg.validate ()
+
+ def test_013_connect_varargs (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ nop1 = gr.nop (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+ self.assertRaises (ValueError,
+ lambda : fg.connect ())
+ self.assertRaises (ValueError,
+ lambda : fg.connect (src1))
+
+ def test_014_connect_varargs (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ nop1 = gr.nop (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+ fg.connect (src1, nop1, dst1)
+ fg.validate ()
+
+if __name__ == '__main__':
+ gr_unittest.main ()
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_cma_equalizer.py b/gnuradio-core/src/python/gnuradio/gr/qa_cma_equalizer.py
new file mode 100755
index 000000000..6b3e9aa9e
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_cma_equalizer.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+
+from gnuradio import gr, gr_unittest
+
+class test_cma_equalizer_fir(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.fg = gr.flow_graph()
+
+ def tearDown(self):
+ self.fg = None
+
+ def transform(self, src_data):
+ SRC = gr.vector_source_c(src_data, False)
+ EQU = gr.cma_equalizer_cc(4, 1.0, .001)
+ DST = gr.vector_sink_c()
+ self.fg.connect(SRC, EQU, DST)
+ self.fg.run()
+ return DST.data()
+
+ def test_001_identity(self):
+ # Constant modulus signal so no adjustments
+ src_data = (1+0j, 0+1j, -1+0j, 0-1j)*1000
+ expected_data = src_data
+ result = self.transform(src_data)
+ self.assertComplexTuplesAlmostEqual(expected_data, result)
+
+if __name__ == "__main__":
+ gr_unittest.main() \ No newline at end of file
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_complex_to_xxx.py b/gnuradio-core/src/python/gnuradio/gr/qa_complex_to_xxx.py
new file mode 100755
index 000000000..4bc193350
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_complex_to_xxx.py
@@ -0,0 +1,116 @@
+#!/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, gr_unittest
+import math
+
+class test_complex_ops (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown (self):
+ self.fg = None
+
+ def test_complex_to_float_1 (self):
+ src_data = (0, 1, -1, 3+4j, -3-4j, -3+4j)
+ expected_result = (0, 1, -1, 3, -3, -3)
+ src = gr.vector_source_c (src_data)
+ op = gr.complex_to_float ()
+ dst = gr.vector_sink_f ()
+ self.fg.connect (src, op)
+ self.fg.connect (op, dst)
+ self.fg.run () # run the graph and wait for it to finish
+ actual_result = dst.data () # fetch the contents of the sink
+ self.assertFloatTuplesAlmostEqual (expected_result, actual_result)
+
+ def test_complex_to_float_2 (self):
+ src_data = (0, 1, -1, 3+4j, -3-4j, -3+4j)
+ expected_result0 = (0, 1, -1, 3, -3, -3)
+ expected_result1 = (0, 0, 0, 4, -4, 4)
+ src = gr.vector_source_c (src_data)
+ op = gr.complex_to_float ()
+ dst0 = gr.vector_sink_f ()
+ dst1 = gr.vector_sink_f ()
+ self.fg.connect (src, op)
+ self.fg.connect ((op, 0), dst0)
+ self.fg.connect ((op, 1), dst1)
+ self.fg.run ()
+ actual_result = dst0.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result0, actual_result)
+ actual_result = dst1.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result1, actual_result)
+
+ def test_complex_to_real (self):
+ src_data = (0, 1, -1, 3+4j, -3-4j, -3+4j)
+ expected_result = (0, 1, -1, 3, -3, -3)
+ src = gr.vector_source_c (src_data)
+ op = gr.complex_to_real ()
+ dst = gr.vector_sink_f ()
+ self.fg.connect (src, op)
+ self.fg.connect (op, dst)
+ self.fg.run ()
+ actual_result = dst.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, actual_result)
+
+ def test_complex_to_imag (self):
+ src_data = (0, 1, -1, 3+4j, -3-4j, -3+4j)
+ expected_result = (0, 0, 0, 4, -4, 4)
+ src = gr.vector_source_c (src_data)
+ op = gr.complex_to_imag ()
+ dst = gr.vector_sink_f ()
+ self.fg.connect (src, op)
+ self.fg.connect (op, dst)
+ self.fg.run ()
+ actual_result = dst.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, actual_result,5)
+
+ def test_complex_to_mag (self):
+ src_data = (0, 1, -1, 3+4j, -3-4j, -3+4j)
+ expected_result = (0, 1, 1, 5, 5, 5)
+ src = gr.vector_source_c (src_data)
+ op = gr.complex_to_mag ()
+ dst = gr.vector_sink_f ()
+ self.fg.connect (src, op)
+ self.fg.connect (op, dst)
+ self.fg.run ()
+ actual_result = dst.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, actual_result,5)
+
+ def test_complex_to_arg (self):
+ pi = math.pi
+ expected_result = (0, pi/6, pi/4, pi/2, 3*pi/4, 7*pi/8,
+ -pi/6, -pi/4, -pi/2, -3*pi/4, -7*pi/8)
+ src_data = tuple ([math.cos (x) + math.sin (x) * 1j for x in expected_result])
+ src = gr.vector_source_c (src_data)
+ op = gr.complex_to_arg ()
+ dst = gr.vector_sink_f ()
+ self.fg.connect (src, op)
+ self.fg.connect (op, dst)
+ self.fg.run ()
+ actual_result = dst.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, actual_result, 5)
+
+
+if __name__ == '__main__':
+ gr_unittest.main ()
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_constellation_decoder_cb.py b/gnuradio-core/src/python/gnuradio/gr/qa_constellation_decoder_cb.py
new file mode 100755
index 000000000..956412228
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_constellation_decoder_cb.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, gr_unittest
+import math
+
+class test_head (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown (self):
+ self.fg = None
+
+ def test_constellation_decoder_cb (self):
+ symbol_positions = [1 + 0j, 0 + 1j , -1 + 0j, 0 - 1j]
+ symbol_values_out = [0, 1, 2, 3]
+ expected_result = ( 0, 3, 2, 1, 0, 0, 3)
+ src_data = (0.5 + 0j, 0.1 - 1.2j, -0.8 - 0.1j, -0.45 + 0.8j, 0.8 - 0j, 0.5 + 0j, 0.1 - 1.2j)
+ src = gr.vector_source_c (src_data)
+ op = gr.constellation_decoder_cb (symbol_positions, symbol_values_out)
+ dst = gr.vector_sink_b ()
+ self.fg.connect (src, op)
+ self.fg.connect (op, dst)
+ self.fg.run () # run the graph and wait for it to finish
+ actual_result = dst.data () # fetch the contents of the sink
+ #print "actual result", actual_result
+ #print "expected result", expected_result
+ self.assertFloatTuplesAlmostEqual (expected_result, actual_result)
+
+
+if __name__ == '__main__':
+ gr_unittest.main ()
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_correlate_access_code.py b/gnuradio-core/src/python/gnuradio/gr/qa_correlate_access_code.py
new file mode 100755
index 000000000..89b4909eb
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_correlate_access_code.py
@@ -0,0 +1,83 @@
+#!/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, gr_unittest
+import math
+
+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))
+
+class test_correlate_access_code(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.fg = gr.flow_graph()
+
+ def tearDown(self):
+ self.fg = None
+
+ def test_001(self):
+ pad = (0,) * 64
+ # 0 0 0 1 0 0 0 1
+ src_data = (1, 0, 1, 1, 1, 1, 0, 1, 1) + pad + (0,) * 7
+ expected_result = pad + (1, 0, 1, 1, 3, 1, 0, 1, 1, 2) + (0,) * 6
+ src = gr.vector_source_b (src_data)
+ op = gr.correlate_access_code_bb("1011", 0)
+ dst = gr.vector_sink_b ()
+ self.fg.connect (src, op, dst)
+ self.fg.run ()
+ result_data = dst.data ()
+ self.assertEqual (expected_result, result_data)
+
+
+ def test_002(self):
+ code = tuple(string_to_1_0_list(default_access_code))
+ access_code = to_1_0_string(code)
+ pad = (0,) * 64
+ #print code
+ #print access_code
+ src_data = code + (1, 0, 1, 1) + pad
+ expected_result = pad + code + (3, 0, 1, 1)
+ src = gr.vector_source_b (src_data)
+ op = gr.correlate_access_code_bb(access_code, 0)
+ dst = gr.vector_sink_b ()
+ self.fg.connect (src, op, dst)
+ self.fg.run ()
+ result_data = dst.data ()
+ self.assertEqual (expected_result, result_data)
+
+
+
+if __name__ == '__main__':
+ gr_unittest.main ()
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_diff_encoder.py b/gnuradio-core/src/python/gnuradio/gr/qa_diff_encoder.py
new file mode 100755
index 000000000..44840709b
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_diff_encoder.py
@@ -0,0 +1,86 @@
+#!/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, gr_unittest
+import math
+import random
+
+def make_random_int_tuple(L, min, max):
+ result = []
+ for x in range(L):
+ result.append(random.randint(min, max))
+ return tuple(result)
+
+
+class test_encoder (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown (self):
+ self.fg = None
+
+ def test_diff_encdec_000(self):
+ random.seed(0)
+ modulus = 2
+ src_data = make_random_int_tuple(1000, 0, modulus-1)
+ expected_result = src_data
+ src = gr.vector_source_b(src_data)
+ enc = gr.diff_encoder_bb(modulus)
+ dec = gr.diff_decoder_bb(modulus)
+ dst = gr.vector_sink_b()
+ self.fg.connect(src, enc, dec, dst)
+ self.fg.run() # run the graph and wait for it to finish
+ actual_result = dst.data() # fetch the contents of the sink
+ self.assertEqual(expected_result, actual_result)
+
+ def test_diff_encdec_001(self):
+ random.seed(0)
+ modulus = 4
+ src_data = make_random_int_tuple(1000, 0, modulus-1)
+ expected_result = src_data
+ src = gr.vector_source_b(src_data)
+ enc = gr.diff_encoder_bb(modulus)
+ dec = gr.diff_decoder_bb(modulus)
+ dst = gr.vector_sink_b()
+ self.fg.connect(src, enc, dec, dst)
+ self.fg.run() # run the graph and wait for it to finish
+ actual_result = dst.data() # fetch the contents of the sink
+ self.assertEqual(expected_result, actual_result)
+
+ def test_diff_encdec_002(self):
+ random.seed(0)
+ modulus = 8
+ src_data = make_random_int_tuple(40000, 0, modulus-1)
+ expected_result = src_data
+ src = gr.vector_source_b(src_data)
+ enc = gr.diff_encoder_bb(modulus)
+ dec = gr.diff_decoder_bb(modulus)
+ dst = gr.vector_sink_b()
+ self.fg.connect(src, enc, dec, dst)
+ self.fg.run() # run the graph and wait for it to finish
+ actual_result = dst.data() # fetch the contents of the sink
+ self.assertEqual(expected_result, actual_result)
+
+if __name__ == '__main__':
+ gr_unittest.main ()
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_diff_phasor_cc.py b/gnuradio-core/src/python/gnuradio/gr/qa_diff_phasor_cc.py
new file mode 100755
index 000000000..0b7160202
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_diff_phasor_cc.py
@@ -0,0 +1,50 @@
+#!/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, gr_unittest
+import math
+
+class test_complex_ops (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown (self):
+ self.fg = None
+
+ def test_diff_phasor_cc (self):
+ src_data = (0+0j, 1+0j, -1+0j, 3+4j, -3-4j, -3+4j)
+ expected_result = (0+0j, 0+0j, -1+0j, -3-4j, -25+0j, -7-24j)
+ src = gr.vector_source_c (src_data)
+ op = gr.diff_phasor_cc ()
+ dst = gr.vector_sink_c ()
+ self.fg.connect (src, op)
+ self.fg.connect (op, dst)
+ self.fg.run () # run the graph and wait for it to finish
+ actual_result = dst.data () # fetch the contents of the sink
+ self.assertComplexTuplesAlmostEqual (expected_result, actual_result)
+
+
+
+if __name__ == '__main__':
+ gr_unittest.main ()
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_feval.py b/gnuradio-core/src/python/gnuradio/gr/qa_feval.py
new file mode 100755
index 000000000..1de49369a
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_feval.py
@@ -0,0 +1,92 @@
+#!/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, gr_unittest
+
+class my_add2_dd(gr.feval_dd):
+ def eval(self, x):
+ return x + 2
+
+class my_add2_ll(gr.feval_ll):
+ def eval(self, x):
+ return x + 2
+
+class my_add2_cc(gr.feval_cc):
+ def eval(self, x):
+ return x + (2 - 2j)
+
+
+class test_feval(gr_unittest.TestCase):
+
+ def test_dd_1(self):
+ f = my_add2_dd()
+ src_data = (0.0, 1.0, 2.0, 3.0, 4.0)
+ expected_result = (2.0, 3.0, 4.0, 5.0, 6.0)
+ # this is all in python...
+ actual_result = tuple([f.eval(x) for x in src_data])
+ self.assertEqual(expected_result, actual_result)
+
+ def test_dd_2(self):
+ f = my_add2_dd()
+ src_data = (0.0, 1.0, 2.0, 3.0, 4.0)
+ expected_result = (2.0, 3.0, 4.0, 5.0, 6.0)
+ # this is python -> C++ -> python and back again...
+ actual_result = tuple([gr.feval_dd_example(f, x) for x in src_data])
+ self.assertEqual(expected_result, actual_result)
+
+
+ def test_ll_1(self):
+ f = my_add2_ll()
+ src_data = (0, 1, 2, 3, 4)
+ expected_result = (2, 3, 4, 5, 6)
+ # this is all in python...
+ actual_result = tuple([f.eval(x) for x in src_data])
+ self.assertEqual(expected_result, actual_result)
+
+ def test_ll_2(self):
+ f = my_add2_ll()
+ src_data = (0, 1, 2, 3, 4)
+ expected_result = (2, 3, 4, 5, 6)
+ # this is python -> C++ -> python and back again...
+ actual_result = tuple([gr.feval_ll_example(f, x) for x in src_data])
+ self.assertEqual(expected_result, actual_result)
+
+
+ def test_cc_1(self):
+ f = my_add2_cc()
+ src_data = (0+1j, 2+3j, 4+5j, 6+7j)
+ expected_result = (2-1j, 4+1j, 6+3j, 8+5j)
+ # this is all in python...
+ actual_result = tuple([f.eval(x) for x in src_data])
+ self.assertEqual(expected_result, actual_result)
+
+ def test_cc_2(self):
+ f = my_add2_cc()
+ src_data = (0+1j, 2+3j, 4+5j, 6+7j)
+ expected_result = (2-1j, 4+1j, 6+3j, 8+5j)
+ # this is python -> C++ -> python and back again...
+ actual_result = tuple([gr.feval_cc_example(f, x) for x in src_data])
+ self.assertEqual(expected_result, actual_result)
+
+
+if __name__ == '__main__':
+ gr_unittest.main ()
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_fft_filter.py b/gnuradio-core/src/python/gnuradio/gr/qa_fft_filter.py
new file mode 100755
index 000000000..cb2e76b18
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_fft_filter.py
@@ -0,0 +1,268 @@
+#!/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, gr_unittest
+import sys
+import random
+
+def make_random_complex_tuple(L):
+ result = []
+ for x in range(L):
+ result.append(complex(random.uniform(-1000,1000),
+ random.uniform(-1000,1000)))
+ return tuple(result)
+
+def make_random_float_tuple(L):
+ result = []
+ for x in range(L):
+ result.append(float(int(random.uniform(-1000,1000))))
+ return tuple(result)
+
+
+def reference_filter_ccc(dec, taps, input):
+ """
+ compute result using conventional fir filter
+ """
+ fg = gr.flow_graph()
+ #src = gr.vector_source_c(((0,) * (len(taps) - 1)) + input)
+ src = gr.vector_source_c(input)
+ op = gr.fir_filter_ccc(dec, taps)
+ dst = gr.vector_sink_c()
+ fg.connect(src, op, dst)
+ fg.run()
+ return dst.data()
+
+def reference_filter_fff(dec, taps, input):
+ """
+ compute result using conventional fir filter
+ """
+ fg = gr.flow_graph()
+ #src = gr.vector_source_f(((0,) * (len(taps) - 1)) + input)
+ src = gr.vector_source_f(input)
+ op = gr.fir_filter_fff(dec, taps)
+ dst = gr.vector_sink_f()
+ fg.connect(src, op, dst)
+ fg.run()
+ return dst.data()
+
+
+def print_complex(x):
+ for i in x:
+ i = complex(i)
+ sys.stdout.write("(%6.3f,%6.3fj), " % (i.real, i.imag))
+ sys.stdout.write('\n')
+
+
+class test_fft_filter(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown(self):
+ self.fg = None
+
+ def assert_fft_ok2(self, expected_result, result_data):
+ expected_result = expected_result[:len(result_data)]
+ self.assertComplexTuplesAlmostEqual2 (expected_result, result_data,
+ abs_eps=1e-9, rel_eps=4e-4)
+
+ def assert_fft_float_ok2(self, expected_result, result_data, abs_eps=1e-9, rel_eps=4e-4):
+ expected_result = expected_result[:len(result_data)]
+ self.assertFloatTuplesAlmostEqual2 (expected_result, result_data,
+ abs_eps, rel_eps)
+
+ #def test_ccc_000(self):
+ # self.assertRaises (RuntimeError, gr.fft_filter_ccc, 2, (1,))
+
+ def test_ccc_001(self):
+ src_data = (0,1,2,3,4,5,6,7)
+ taps = (1,)
+ expected_result = tuple([complex(x) for x in (0,1,2,3,4,5,6,7)])
+ src = gr.vector_source_c(src_data)
+ op = gr.fft_filter_ccc(1, taps)
+ dst = gr.vector_sink_c()
+ self.fg.connect(src, op, dst)
+ self.fg.run()
+ result_data = dst.data()
+ #print 'expected:', expected_result
+ #print 'results: ', result_data
+ self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5)
+
+
+ def test_ccc_002(self):
+ src_data = (0,1,2,3,4,5,6,7)
+ taps = (2,)
+ expected_result = tuple([2 * complex(x) for x in (0,1,2,3,4,5,6,7)])
+ src = gr.vector_source_c(src_data)
+ op = gr.fft_filter_ccc(1, taps)
+ dst = gr.vector_sink_c()
+ self.fg.connect(src, op, dst)
+ self.fg.run()
+ result_data = dst.data()
+ #print 'expected:', expected_result
+ #print 'results: ', result_data
+ self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5)
+
+ def test_ccc_004(self):
+ random.seed(0)
+ for i in xrange(25):
+ # sys.stderr.write("\n>>> Loop = %d\n" % (i,))
+ src_len = 4*1024
+ src_data = make_random_complex_tuple(src_len)
+ ntaps = int(random.uniform(2, 1000))
+ taps = make_random_complex_tuple(ntaps)
+ expected_result = reference_filter_ccc(1, taps, src_data)
+
+ src = gr.vector_source_c(src_data)
+ op = gr.fft_filter_ccc(1, taps)
+ dst = gr.vector_sink_c()
+ self.fg.connect(src, op, dst)
+ self.fg.run()
+ result_data = dst.data()
+
+ self.assert_fft_ok2(expected_result, result_data)
+
+ def test_ccc_005(self):
+ random.seed(0)
+ for i in xrange(25):
+ # sys.stderr.write("\n>>> Loop = %d\n" % (i,))
+ dec = i + 1
+ src_len = 4*1024
+ src_data = make_random_complex_tuple(src_len)
+ ntaps = int(random.uniform(2, 100))
+ taps = make_random_complex_tuple(ntaps)
+ expected_result = reference_filter_ccc(dec, taps, src_data)
+
+ src = gr.vector_source_c(src_data)
+ op = gr.fft_filter_ccc(dec, taps)
+ dst = gr.vector_sink_c()
+ self.fg.connect(src, op, dst)
+ self.fg.run()
+ result_data = dst.data()
+
+ self.assert_fft_ok2(expected_result, result_data)
+
+ # ----------------------------------------------------------------
+ # test _fff version
+ # ----------------------------------------------------------------
+
+ def test_fff_001(self):
+ src_data = (0,1,2,3,4,5,6,7)
+ taps = (1,)
+ expected_result = tuple([float(x) for x in (0,1,2,3,4,5,6,7)])
+ src = gr.vector_source_f(src_data)
+ op = gr.fft_filter_fff(1, taps)
+ dst = gr.vector_sink_f()
+ self.fg.connect(src, op, dst)
+ self.fg.run()
+ result_data = dst.data()
+ #print 'expected:', expected_result
+ #print 'results: ', result_data
+ self.assertFloatTuplesAlmostEqual (expected_result, result_data, 5)
+
+
+ def test_fff_002(self):
+ src_data = (0,1,2,3,4,5,6,7)
+ taps = (2,)
+ expected_result = tuple([2 * float(x) for x in (0,1,2,3,4,5,6,7)])
+ src = gr.vector_source_f(src_data)
+ op = gr.fft_filter_fff(1, taps)
+ dst = gr.vector_sink_f()
+ self.fg.connect(src, op, dst)
+ self.fg.run()
+ result_data = dst.data()
+ #print 'expected:', expected_result
+ #print 'results: ', result_data
+ self.assertFloatTuplesAlmostEqual (expected_result, result_data, 5)
+
+ def xtest_fff_003(self):
+ random.seed(0)
+ for i in xrange(25):
+ sys.stderr.write("\n>>> Loop = %d\n" % (i,))
+ src_len = 4096
+ src_data = make_random_float_tuple(src_len)
+ ntaps = int(random.uniform(2, 1000))
+ taps = make_random_float_tuple(ntaps)
+ expected_result = reference_filter_fff(1, taps, src_data)
+
+ src = gr.vector_source_f(src_data)
+ op = gr.fft_filter_fff(1, taps)
+ dst = gr.vector_sink_f()
+ self.fg.connect(src, op, dst)
+ self.fg.run()
+ result_data = dst.data()
+
+ #print "src_len =", src_len, " ntaps =", ntaps
+ try:
+ self.assert_fft_float_ok2(expected_result, result_data, abs_eps=1.0)
+ except:
+ expected = open('expected', 'w')
+ for x in expected_result:
+ expected.write(`x` + '\n')
+ actual = open('actual', 'w')
+ for x in result_data:
+ actual.write(`x` + '\n')
+ raise
+
+ def xtest_fff_004(self):
+ random.seed(0)
+ for i in xrange(25):
+ sys.stderr.write("\n>>> Loop = %d\n" % (i,))
+ src_len = 4*1024
+ src_data = make_random_float_tuple(src_len)
+ ntaps = int(random.uniform(2, 1000))
+ taps = make_random_float_tuple(ntaps)
+ expected_result = reference_filter_fff(1, taps, src_data)
+
+ src = gr.vector_source_f(src_data)
+ op = gr.fft_filter_fff(1, taps)
+ dst = gr.vector_sink_f()
+ self.fg.connect(src, op, dst)
+ self.fg.run()
+ result_data = dst.data()
+
+ self.assert_fft_float_ok2(expected_result, result_data, abs_eps=2.0)
+
+ def xtest_fff_005(self):
+ random.seed(0)
+ for i in xrange(25):
+ sys.stderr.write("\n>>> Loop = %d\n" % (i,))
+ dec = i + 1
+ src_len = 4*1024
+ src_data = make_random_float_tuple(src_len)
+ ntaps = int(random.uniform(2, 100))
+ taps = make_random_float_tuple(ntaps)
+ expected_result = reference_filter_fff(dec, taps, src_data)
+
+ src = gr.vector_source_f(src_data)
+ op = gr.fft_filter_fff(dec, taps)
+ dst = gr.vector_sink_f()
+ self.fg.connect(src, op, dst)
+ self.fg.run()
+ result_data = dst.data()
+
+ self.assert_fft_float_ok2(expected_result, result_data)
+
+
+if __name__ == '__main__':
+ gr_unittest.main ()
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_filter_delay_fc.py b/gnuradio-core/src/python/gnuradio/gr/qa_filter_delay_fc.py
new file mode 100755
index 000000000..191552ca2
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_filter_delay_fc.py
@@ -0,0 +1,317 @@
+#!/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, gr_unittest
+import math
+
+class qa_filter_delay_fc (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown (self):
+ self.fg = None
+
+ def test_001_filter_delay_one_input (self):
+
+ # expected result
+ expected_result = ( -1.4678005338941702e-11j,
+ -0.0011950774351134896j,
+ -0.0019336787518113852j,
+ -0.0034673355985432863j,
+ -0.0036765895783901215j,
+ -0.004916108213365078j,
+ -0.0042778430506587029j,
+ -0.006028641015291214j,
+ -0.005476709920912981j,
+ -0.0092810001224279404j,
+ -0.0095402700826525688j,
+ -0.016060983762145042j,
+ -0.016446959227323532j,
+ -0.02523401565849781j,
+ -0.024382550269365311j,
+ -0.035477779805660248j,
+ -0.033021725714206696j,
+ -0.048487484455108643j,
+ -0.04543270543217659j,
+ -0.069477587938308716j,
+ -0.066984444856643677j,
+ -0.10703597217798233j,
+ -0.10620346665382385j,
+ -0.1852707713842392j,
+ -0.19357112050056458j,
+ (7.2191945754696007e-09 -0.50004088878631592j),
+ (0.58778399229049683 -0.6155126690864563j),
+ (0.95105588436126709 -0.12377222627401352j),
+ (0.95105588436126709 +0.41524654626846313j),
+ (0.5877838134765625 +0.91611981391906738j),
+ (5.8516356205018383e-09 +1.0670661926269531j),
+ (-0.5877840518951416 +0.87856143712997437j),
+ (-0.95105588436126709 +0.35447561740875244j),
+ (-0.95105588436126709 -0.26055556535720825j),
+ (-0.5877838134765625 -0.77606213092803955j),
+ (-8.7774534307527574e-09 -0.96460390090942383j),
+ (0.58778399229049683 -0.78470128774642944j),
+ (0.95105588436126709 -0.28380891680717468j),
+ (0.95105588436126709 +0.32548999786376953j),
+ (0.5877838134765625 +0.82514488697052002j),
+ (1.4629089051254596e-08 +1.0096219778060913j),
+ (-0.5877840518951416 +0.81836479902267456j),
+ (-0.95105588436126709 +0.31451958417892456j),
+ (-0.95105588436126709 -0.3030143678188324j),
+ (-0.5877838134765625 -0.80480599403381348j),
+ (-1.7554906861505515e-08 -0.99516552686691284j),
+ (0.58778399229049683 -0.80540722608566284j),
+ (0.95105582475662231 -0.30557557940483093j),
+ (0.95105588436126709 +0.31097668409347534j),
+ (0.5877838134765625 +0.81027895212173462j),
+ (2.3406542482007353e-08 +1.0000816583633423j),
+ (-0.5877840518951416 +0.80908381938934326j),
+ (-0.95105588436126709 +0.30904293060302734j),
+ (-0.95105588436126709 -0.30904296040534973j),
+ (-0.5877838134765625 -0.80908387899398804j),
+ (-2.6332360292258272e-08 -1.0000815391540527j),
+ (0.58778399229049683 -0.80908381938934326j),
+ (0.95105582475662231 -0.30904299020767212j),
+ (0.95105588436126709 +0.30904293060302734j),
+ (0.5877838134765625 +0.80908381938934326j),
+ (3.218399768911695e-08 +1.0000815391540527j))
+
+ fg = self.fg
+
+ sampling_freq = 100
+
+ ntaps = 51
+ src1 = gr.sig_source_f (sampling_freq, gr.GR_SIN_WAVE,
+ sampling_freq * 0.10, 1.0)
+ head = gr.head (gr.sizeof_float, int (ntaps + sampling_freq * 0.10))
+ dst2 = gr.vector_sink_c ()
+
+ # calculate taps
+ taps = gr.firdes_hilbert (ntaps)
+ hd = gr.filter_delay_fc (taps)
+
+ fg.connect (src1, head)
+ fg.connect (head, hd)
+ fg.connect (hd,dst2)
+
+ fg.run ()
+
+ # get output
+ result_data = dst2.data ()
+ self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5)
+
+ def test_002_filter_delay_two_inputs (self):
+
+ # giving the same signal to both the inputs should fetch the same results
+ # as above
+
+ # expected result
+ expected_result = ( -1.4678005338941702e-11j,
+ -0.0011950774351134896j,
+ -0.0019336787518113852j,
+ -0.0034673355985432863j,
+ -0.0036765895783901215j,
+ -0.004916108213365078j,
+ -0.0042778430506587029j,
+ -0.006028641015291214j,
+ -0.005476709920912981j,
+ -0.0092810001224279404j,
+ -0.0095402700826525688j,
+ -0.016060983762145042j,
+ -0.016446959227323532j,
+ -0.02523401565849781j,
+ -0.024382550269365311j,
+ -0.035477779805660248j,
+ -0.033021725714206696j,
+ -0.048487484455108643j,
+ -0.04543270543217659j,
+ -0.069477587938308716j,
+ -0.066984444856643677j,
+ -0.10703597217798233j,
+ -0.10620346665382385j,
+ -0.1852707713842392j,
+ -0.19357112050056458j,
+ (7.2191945754696007e-09 -0.50004088878631592j),
+ (0.58778399229049683 -0.6155126690864563j),
+ (0.95105588436126709 -0.12377222627401352j),
+ (0.95105588436126709 +0.41524654626846313j),
+ (0.5877838134765625 +0.91611981391906738j),
+ (5.8516356205018383e-09 +1.0670661926269531j),
+ (-0.5877840518951416 +0.87856143712997437j),
+ (-0.95105588436126709 +0.35447561740875244j),
+ (-0.95105588436126709 -0.26055556535720825j),
+ (-0.5877838134765625 -0.77606213092803955j),
+ (-8.7774534307527574e-09 -0.96460390090942383j),
+ (0.58778399229049683 -0.78470128774642944j),
+ (0.95105588436126709 -0.28380891680717468j),
+ (0.95105588436126709 +0.32548999786376953j),
+ (0.5877838134765625 +0.82514488697052002j),
+ (1.4629089051254596e-08 +1.0096219778060913j),
+ (-0.5877840518951416 +0.81836479902267456j),
+ (-0.95105588436126709 +0.31451958417892456j),
+ (-0.95105588436126709 -0.3030143678188324j),
+ (-0.5877838134765625 -0.80480599403381348j),
+ (-1.7554906861505515e-08 -0.99516552686691284j),
+ (0.58778399229049683 -0.80540722608566284j),
+ (0.95105582475662231 -0.30557557940483093j),
+ (0.95105588436126709 +0.31097668409347534j),
+ (0.5877838134765625 +0.81027895212173462j),
+ (2.3406542482007353e-08 +1.0000816583633423j),
+ (-0.5877840518951416 +0.80908381938934326j),
+ (-0.95105588436126709 +0.30904293060302734j),
+ (-0.95105588436126709 -0.30904296040534973j),
+ (-0.5877838134765625 -0.80908387899398804j),
+ (-2.6332360292258272e-08 -1.0000815391540527j),
+ (0.58778399229049683 -0.80908381938934326j),
+ (0.95105582475662231 -0.30904299020767212j),
+ (0.95105588436126709 +0.30904293060302734j),
+ (0.5877838134765625 +0.80908381938934326j),
+ (3.218399768911695e-08 +1.0000815391540527j))
+
+
+ fg = self.fg
+
+ sampling_freq = 100
+ ntaps = 51
+ src1 = gr.sig_source_f (sampling_freq, gr.GR_SIN_WAVE,
+ sampling_freq * 0.10, 1.0)
+ head = gr.head (gr.sizeof_float, int (ntaps + sampling_freq * 0.10))
+ dst2 = gr.vector_sink_c ()
+
+
+ # calculate taps
+ taps = gr.firdes_hilbert (ntaps)
+ hd = gr.filter_delay_fc (taps)
+
+ fg.connect (src1, head)
+ fg.connect (head, (hd,0))
+ fg.connect (head, (hd,1))
+ fg.connect (hd,dst2)
+ fg.run ()
+
+ # get output
+ result_data = dst2.data ()
+
+ self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5)
+
+
+ def test_003_filter_delay_two_inputs (self):
+
+ # give two different inputs
+
+ # expected result
+ expected_result = ( -0.0020331963896751404j,
+ -0.0016448829555884004j,
+ -0.0032375147566199303j,
+ -0.0014826074475422502j,
+ -0.0033034090884029865j,
+ -0.00051144487224519253j,
+ -0.0043686260469257832j,
+ -0.0010198024101555347j,
+ -0.0082517862319946289j,
+ -0.003456643782556057j,
+ -0.014193611219525337j,
+ -0.005875137634575367j,
+ -0.020293503999710083j,
+ -0.0067503536120057106j,
+ -0.026798896491527557j,
+ -0.0073488112539052963j,
+ -0.037041611969470978j,
+ -0.010557252913713455j,
+ -0.055669989436864853j,
+ -0.018332764506340027j,
+ -0.089904911816120148j,
+ -0.033361352980136871j,
+ -0.16902604699134827j,
+ -0.074318811297416687j,
+ -0.58429563045501709j,
+ (7.2191945754696007e-09 -0.35892376303672791j),
+ (0.58778399229049683 +0.63660913705825806j),
+ (0.95105588436126709 +0.87681591510772705j),
+ (0.95105588436126709 +0.98705857992172241j),
+ (0.5877838134765625 +0.55447429418563843j),
+ (5.8516356205018383e-09 +0.026006083935499191j),
+ (-0.5877840518951416 -0.60616838932037354j),
+ (-0.95105588436126709 -0.9311758279800415j),
+ (-0.95105588436126709 -0.96169203519821167j),
+ (-0.5877838134765625 -0.57292771339416504j),
+ (-8.7774534307527574e-09 -0.0073488391935825348j),
+ (0.58778399229049683 +0.59720659255981445j),
+ (0.95105588436126709 +0.94438445568084717j),
+ (0.95105588436126709 +0.95582199096679688j),
+ (0.5877838134765625 +0.58196049928665161j),
+ (1.4629089051254596e-08 +0.0026587247848510742j),
+ (-0.5877840518951416 -0.59129220247268677j),
+ (-0.95105588436126709 -0.94841635227203369j),
+ (-0.95105588436126709 -0.95215457677841187j),
+ (-0.5877838134765625 -0.58535969257354736j),
+ (-1.7554906861505515e-08 -0.00051158666610717773j),
+ (0.58778399229049683 +0.58867418766021729j),
+ (0.95105582475662231 +0.94965213537216187j),
+ (0.95105588436126709 +0.95050644874572754j),
+ (0.5877838134765625 +0.58619076013565063j),
+ (2.3406542482007353e-08 +1.1920928955078125e-07j),
+ (-0.5877840518951416 -0.58783555030822754j),
+ (-0.95105588436126709 -0.95113480091094971j),
+ (-0.95105588436126709 -0.95113474130630493j),
+ (-0.5877838134765625 -0.58783555030822754j),
+ (-2.6332360292258272e-08 -8.1956386566162109e-08j),
+ (0.58778399229049683 +0.58783555030822754j),
+ (0.95105582475662231 +0.95113474130630493j),
+ (0.95105588436126709 +0.95113474130630493j),
+ (0.5877838134765625 +0.58783560991287231j),
+ (3.218399768911695e-08 +1.1920928955078125e-07j))
+
+ fg = self.fg
+
+ sampling_freq = 100
+ ntaps = 51
+
+ src1 = gr.sig_source_f (sampling_freq, gr.GR_SIN_WAVE,sampling_freq * 0.10, 1.0)
+ src2 = gr.sig_source_f (sampling_freq, gr.GR_COS_WAVE,sampling_freq * 0.10, 1.0)
+
+ head1 = gr.head (gr.sizeof_float, int (ntaps + sampling_freq * 0.10))
+ head2 = gr.head (gr.sizeof_float, int (ntaps + sampling_freq * 0.10))
+
+ taps = gr.firdes_hilbert (ntaps)
+ hd = gr.filter_delay_fc (taps)
+
+ dst2 = gr.vector_sink_c ()
+
+ fg.connect (src1, head1)
+ fg.connect (src2, head2)
+
+ fg.connect (head1, (hd,0))
+ fg.connect (head2, (hd,1))
+ fg.connect (hd, dst2)
+
+ fg.run ()
+
+ # get output
+ result_data = dst2.data ()
+
+ self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5)
+
+
+if __name__ == '__main__':
+ gr_unittest.main ()
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_flow_graph.py b/gnuradio-core/src/python/gnuradio/gr/qa_flow_graph.py
new file mode 100755
index 000000000..455cc1359
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_flow_graph.py
@@ -0,0 +1,356 @@
+#!/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, gr_unittest
+import qa_basic_flow_graph
+
+
+def all_counts ():
+ return (gr.block_ncurrently_allocated (),
+ gr.block_detail_ncurrently_allocated (),
+ gr.buffer_ncurrently_allocated (),
+ gr.buffer_reader_ncurrently_allocated ())
+
+
+class wrap_add(gr.hier_block):
+ def __init__(self, fg, a):
+ add = gr.add_const_ii (a)
+ gr.hier_block.__init__ (self, fg, add, add)
+
+class mult_add(gr.hier_block):
+ def __init__(self, fg, m, a):
+ mult = gr.multiply_const_ii (m)
+ add = gr.add_const_ii (a)
+ fg.connect (mult, add)
+ gr.hier_block.__init__ (self, fg, mult, add)
+
+
+class test_flow_graph (qa_basic_flow_graph.test_basic_flow_graph):
+
+ def setUp (self):
+ ''' override qa_basic_flow_graph setUp in order to use our class'''
+ self.fg = gr.flow_graph ()
+
+ def tearDown (self):
+ qa_basic_flow_graph.test_basic_flow_graph.tearDown (self)
+
+
+ # we inherit all their tests, so we can be sure we don't break
+ # any of the underlying code
+
+
+ def leak_check (self, fct):
+ begin = all_counts ()
+ fct ()
+ # tear down early so we can check for leaks
+ self.tearDown ()
+ end = all_counts ()
+ self.assertEqual (begin, end)
+
+ def test_100_tsort_null (self):
+ self.assertEqual ([], self.fg.topological_sort (self.fg.all_blocks ()))
+
+ def test_101_tsort_two (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+ fg.connect ((src1, 0), (dst1, 0))
+ fg.validate ()
+ self.assertEqual ([src1, dst1], fg.topological_sort (fg.all_blocks ()))
+
+ def test_102_tsort_three_a (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ nop1 = gr.nop (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+ fg.connect (src1, nop1)
+ fg.connect (nop1, dst1)
+ fg.validate ()
+ self.assertEqual ([src1, nop1, dst1], fg.topological_sort (fg.all_blocks ()))
+
+ def test_103_tsort_three_b (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ nop1 = gr.nop (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+ fg.connect (nop1, dst1)
+ fg.connect (src1, nop1)
+ fg.validate ()
+ self.assertEqual ([src1, nop1, dst1], fg.topological_sort (fg.all_blocks ()))
+
+ def test_104_trivial_dag_check (self):
+ fg = self.fg
+ nop1 = gr.nop (gr.sizeof_int)
+ fg.connect (nop1, nop1)
+ fg.validate ()
+ self.assertRaises (gr.NotDAG,
+ lambda : fg.topological_sort (fg.all_blocks ()))
+
+ def test_105 (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ src2 = gr.null_source (gr.sizeof_int)
+ nop1 = gr.nop (gr.sizeof_int)
+ nop2 = gr.nop (gr.sizeof_int)
+ nop3 = gr.nop (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+
+ fg.connect (src1, nop1)
+ fg.connect (src2, nop2)
+ fg.connect (nop1, (nop3, 0))
+ fg.connect (nop2, (nop3, 1))
+ fg.connect (nop3, dst1)
+ fg.validate ()
+ ts = fg.topological_sort (fg.all_blocks ())
+ self.assertEqual ([src2, nop2, src1, nop1, nop3, dst1], ts)
+
+
+ def test_106 (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ src2 = gr.null_source (gr.sizeof_int)
+ nop1 = gr.nop (gr.sizeof_int)
+ nop2 = gr.nop (gr.sizeof_int)
+ nop3 = gr.nop (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+
+ fg.connect (nop3, dst1)
+ fg.connect (nop2, (nop3, 1))
+ fg.connect (nop1, (nop3, 0))
+ fg.connect (src2, nop2)
+ fg.connect (src1, nop1)
+ fg.validate ()
+ ts = fg.topological_sort (fg.all_blocks ())
+ self.assertEqual ([src2, nop2, src1, nop1, nop3, dst1], ts)
+
+ def test_107 (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ src2 = gr.null_source (gr.sizeof_int)
+ nop1 = gr.nop (gr.sizeof_int)
+ nop2 = gr.nop (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+ dst2 = gr.null_sink (gr.sizeof_int)
+
+ fg.connect (src1, nop1)
+ fg.connect (nop1, dst1)
+ fg.connect (src2, nop2)
+ fg.connect (nop2, dst2)
+ fg.validate ()
+ ts = fg.topological_sort (fg.all_blocks ())
+ self.assertEqual ([src2, nop2, dst2, src1, nop1, dst1], ts)
+
+ def test_108 (self):
+ self.leak_check (self.body_108)
+
+ def body_108 (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ src2 = gr.null_source (gr.sizeof_int)
+ nop1 = gr.nop (gr.sizeof_int)
+ nop2 = gr.nop (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+ dst2 = gr.null_sink (gr.sizeof_int)
+
+ fg.connect (nop2, dst2)
+ fg.connect (src1, nop1)
+ fg.connect (src2, nop2)
+ fg.connect (nop1, dst1)
+ fg.validate ()
+ ts = fg.topological_sort (fg.all_blocks ())
+ self.assertEqual ([src2, nop2, dst2, src1, nop1, dst1], ts)
+ self.assertEqual ((6,0,0,0), all_counts ())
+
+ def test_109__setup_connections (self):
+ self.leak_check (self.body_109)
+
+ def body_109 (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+ fg.connect (src1, dst1)
+ fg._setup_connections ()
+ self.assertEqual ((2,2,1,1), all_counts ())
+
+ def test_110_scheduler (self):
+ self.leak_check (self.body_110)
+
+ def body_110 (self):
+ fg = self.fg
+ src_data = (0, 1, 2, 3)
+ src1 = gr.vector_source_i (src_data)
+ dst1 = gr.vector_sink_i ()
+ fg.connect ((src1, 0), (dst1, 0))
+ fg.run ()
+ dst_data = dst1.data ()
+ self.assertEqual (src_data, dst_data)
+
+ def test_111_scheduler (self):
+ self.leak_check (self.body_111)
+
+ def body_111 (self):
+ fg = self.fg
+ src_data = (0, 1, 2, 3)
+ expected_result = (2, 3, 4, 5)
+ src1 = gr.vector_source_i (src_data)
+ op = gr.add_const_ii (2)
+ dst1 = gr.vector_sink_i ()
+ fg.connect (src1, op)
+ fg.connect (op, dst1)
+ fg.run ()
+ dst_data = dst1.data ()
+ self.assertEqual (expected_result, dst_data)
+
+ def test_111v_scheduler (self):
+ self.leak_check (self.body_111v)
+
+ def body_111v (self):
+ fg = self.fg
+ src_data = (0, 1, 2, 3)
+ expected_result = (2, 3, 4, 5)
+ src1 = gr.vector_source_i (src_data)
+ op = gr.add_const_ii (2)
+ dst1 = gr.vector_sink_i ()
+ fg.connect (src1, op, dst1)
+ fg.run ()
+ dst_data = dst1.data ()
+ self.assertEqual (expected_result, dst_data)
+
+ def test_112 (self):
+ fg = self.fg
+ nop1 = gr.nop (gr.sizeof_int)
+ nop2 = gr.nop (gr.sizeof_int)
+ nop3 = gr.nop (gr.sizeof_int)
+ fg.connect ((nop1, 0), (nop2, 0))
+ fg.connect ((nop1, 1), (nop3, 0))
+ fg._setup_connections ()
+ self.assertEqual (2, nop1.detail().noutputs())
+
+ def test_113 (self):
+ fg = self.fg
+ nop1 = gr.nop (gr.sizeof_int)
+ nop2 = gr.nop (gr.sizeof_int)
+ nop3 = gr.nop (gr.sizeof_int)
+ fg.connect ((nop1, 0), (nop2, 0))
+ fg.connect ((nop1, 0), (nop3, 0))
+ fg._setup_connections ()
+ self.assertEqual (1, nop1.detail().noutputs())
+
+ def test_200_partition (self):
+ self.leak_check (self.body_200)
+
+ def body_200 (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ nop1 = gr.nop (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+ fg.connect (nop1, dst1)
+ fg.connect (src1, nop1)
+ fg.validate ()
+ p = fg.partition_graph (fg.all_blocks ())
+ self.assertEqual ([[src1, nop1, dst1]], p)
+ self.assertEqual ((3,0,0,0), all_counts ())
+
+ def test_201_partition (self):
+ self.leak_check (self.body_201)
+
+ def body_201 (self):
+ fg = self.fg
+ src1 = gr.null_source (gr.sizeof_int)
+ src2 = gr.null_source (gr.sizeof_int)
+ nop1 = gr.nop (gr.sizeof_int)
+ nop2 = gr.nop (gr.sizeof_int)
+ dst1 = gr.null_sink (gr.sizeof_int)
+ dst2 = gr.null_sink (gr.sizeof_int)
+
+ fg.connect (nop2, dst2)
+ fg.connect (src1, nop1)
+ fg.connect (src2, nop2)
+ fg.connect (nop1, dst1)
+ fg.validate ()
+ p = fg.partition_graph (fg.all_blocks ())
+ self.assertEqual ([[src2, nop2, dst2], [src1, nop1, dst1]], p)
+ self.assertEqual ((6,0,0,0), all_counts ())
+
+ def test_300_hier (self):
+ self.leak_check (self.body_300_hier)
+
+ def body_300_hier (self):
+ fg = self.fg
+ src_data = (0, 1, 2, 3)
+ expected_result = (10, 11, 12, 13)
+ src1 = gr.vector_source_i (src_data)
+ op = wrap_add (fg, 10)
+ dst1 = gr.vector_sink_i ()
+ fg.connect (src1, op, dst1)
+ fg.run ()
+ dst_data = dst1.data ()
+ self.assertEqual (expected_result, dst_data)
+
+ def test_301_hier (self):
+ self.leak_check (self.body_301_hier)
+
+ def body_301_hier (self):
+ fg = self.fg
+ src_data = (0, 1, 2, 3)
+ expected_result = (5, 8, 11, 14)
+ src1 = gr.vector_source_i (src_data)
+ op = mult_add (fg, 3, 5)
+ dst1 = gr.vector_sink_i ()
+ fg.connect (src1, op, dst1)
+ fg.run ()
+ dst_data = dst1.data ()
+ self.assertEqual (expected_result, dst_data)
+
+ def test_302_hier (self):
+ self.leak_check (self.body_302_hier)
+
+ def body_302_hier (self):
+ fg = self.fg
+ src_data = (0, 1, 2, 3)
+ expected_result = (10, 11, 12, 13)
+ src1 = gr.vector_source_i (src_data)
+ op = gr.compose (fg, gr.add_const_ii (10))
+ dst1 = gr.vector_sink_i ()
+ fg.connect (src1, op, dst1)
+ fg.run ()
+ dst_data = dst1.data ()
+ self.assertEqual (expected_result, dst_data)
+
+ def test_303_hier (self):
+ self.leak_check (self.body_303_hier)
+
+ def body_303_hier (self):
+ fg = self.fg
+ src_data = (0, 1, 2, 3)
+ expected_result = (35, 38, 41, 44)
+ src1 = gr.vector_source_i (src_data)
+ op = gr.compose (fg, gr.add_const_ii (10), mult_add (fg, 3, 5))
+ dst1 = gr.vector_sink_i ()
+ fg.connect (src1, op, dst1)
+ fg.run ()
+ dst_data = dst1.data ()
+ self.assertEqual (expected_result, dst_data)
+
+
+if __name__ == '__main__':
+ gr_unittest.main ()
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_frequency_modulator.py b/gnuradio-core/src/python/gnuradio/gr/qa_frequency_modulator.py
new file mode 100755
index 000000000..cfa34830d
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_frequency_modulator.py
@@ -0,0 +1,56 @@
+#!/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, gr_unittest
+import math
+
+def sincos(x):
+ return math.cos(x) + math.sin(x) * 1j
+
+
+class test_frequency_modulator (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown (self):
+ self.fg = None
+
+ def test_fm_001 (self):
+ pi = math.pi
+ sensitivity = pi/4
+ src_data = (1.0/4, 1.0/2, 1.0/4, -1.0/4, -1.0/2, -1/4.0)
+ running_sum = (pi/16, 3*pi/16, pi/4, 3*pi/16, pi/16, 0)
+ expected_result = tuple ([sincos (x) for x in running_sum])
+ src = gr.vector_source_f (src_data)
+ op = gr.frequency_modulator_fc (sensitivity)
+ dst = gr.vector_sink_c ()
+ self.fg.connect (src, op)
+ self.fg.connect (op, dst)
+ self.fg.run ()
+ result_data = dst.data ()
+ self.assertComplexTuplesAlmostEqual (expected_result, result_data)
+
+
+if __name__ == '__main__':
+ gr_unittest.main ()
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_fsk_stuff.py b/gnuradio-core/src/python/gnuradio/gr/qa_fsk_stuff.py
new file mode 100755
index 000000000..d61ddb1a0
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_fsk_stuff.py
@@ -0,0 +1,75 @@
+#!/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, gr_unittest
+import math
+
+def sincos(x):
+ return math.cos(x) + math.sin(x) * 1j
+
+class test_bytes_to_syms (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown (self):
+ self.fg = None
+
+ def test_bytes_to_syms_001 (self):
+ src_data = (0x01, 0x80, 0x03)
+ expected_result = (-1, -1, -1, -1, -1, -1, -1, +1,
+ +1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, +1, +1)
+ src = gr.vector_source_b (src_data)
+ op = gr.bytes_to_syms ()
+ dst = gr.vector_sink_f ()
+ self.fg.connect (src, op)
+ self.fg.connect (op, dst)
+ self.fg.run ()
+ result_data = dst.data ()
+ self.assertEqual (expected_result, result_data)
+
+ def test_simple_framer (self):
+ src_data = (0x00, 0x11, 0x22, 0x33,
+ 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xaa, 0xbb,
+ 0xcc, 0xdd, 0xee, 0xff)
+
+ expected_result = (
+ 0xac, 0xdd, 0xa4, 0xe2, 0xf2, 0x8c, 0x20, 0xfc, 0x00, 0x00, 0x11, 0x22, 0x33, 0x55,
+ 0xac, 0xdd, 0xa4, 0xe2, 0xf2, 0x8c, 0x20, 0xfc, 0x01, 0x44, 0x55, 0x66, 0x77, 0x55,
+ 0xac, 0xdd, 0xa4, 0xe2, 0xf2, 0x8c, 0x20, 0xfc, 0x02, 0x88, 0x99, 0xaa, 0xbb, 0x55,
+ 0xac, 0xdd, 0xa4, 0xe2, 0xf2, 0x8c, 0x20, 0xfc, 0x03, 0xcc, 0xdd, 0xee, 0xff, 0x55)
+
+ src = gr.vector_source_b (src_data)
+ op = gr.simple_framer (4)
+ dst = gr.vector_sink_b ()
+ self.fg.connect (src, op)
+ self.fg.connect (op, dst)
+ self.fg.run ()
+ result_data = dst.data ()
+ self.assertEqual (expected_result, result_data)
+
+
+if __name__ == '__main__':
+ gr_unittest.main ()
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_goertzel.py b/gnuradio-core/src/python/gnuradio/gr/qa_goertzel.py
new file mode 100755
index 000000000..da9d65f62
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_goertzel.py
@@ -0,0 +1,64 @@
+#!/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, gr_unittest
+from math import pi, cos
+
+class test_goertzel(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.fg = gr.flow_graph()
+
+ def tearDown(self):
+ self.fg = None
+
+ def make_tone_data(self, rate, freq):
+ return [cos(2*pi*x*freq/rate) for x in range(rate)]
+
+ def transform(self, src_data, rate, freq):
+ src = gr.vector_source_f(src_data, False)
+ dft = gr.goertzel_fc(rate, rate, freq)
+ dst = gr.vector_sink_c()
+ self.fg.connect(src, dft, dst)
+ self.fg.run()
+ return dst.data()
+
+ def test_001(self): # Measure single tone magnitude
+ rate = 8000
+ freq = 100
+ bin = freq
+ src_data = self.make_tone_data(rate, freq)
+ expected_result = 0.5
+ actual_result = abs(self.transform(src_data, rate, bin)[0])
+ self.assertAlmostEqual(expected_result, actual_result, places=5)
+
+ def test_002(self): # Measure off frequency magnitude
+ rate = 8000
+ freq = 100
+ bin = freq/2
+ src_data = self.make_tone_data(rate, freq)
+ expected_result = 0.0
+ actual_result = abs(self.transform(src_data, rate, bin)[0])
+ self.assertAlmostEqual(expected_result, actual_result, places=5)
+
+if __name__ == '__main__':
+ gr_unittest.main()
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_head.py b/gnuradio-core/src/python/gnuradio/gr/qa_head.py
new file mode 100755
index 000000000..a6fcd7f98
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_head.py
@@ -0,0 +1,47 @@
+#!/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, gr_unittest
+
+class test_head (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown (self):
+ self.fg = None
+
+ def test_head (self):
+ src_data = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
+ expected_result = (1, 2, 3, 4)
+ src1 = gr.vector_source_i (src_data)
+ op = gr.head (gr.sizeof_int, 4)
+ dst1 = gr.vector_sink_i ()
+ self.fg.connect (src1, op)
+ self.fg.connect (op, dst1)
+ self.fg.run ()
+ dst_data = dst1.data ()
+ self.assertEqual (expected_result, dst_data)
+
+
+if __name__ == '__main__':
+ gr_unittest.main ()
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_hilbert.py b/gnuradio-core/src/python/gnuradio/gr/qa_hilbert.py
new file mode 100755
index 000000000..a8f39dd6d
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_hilbert.py
@@ -0,0 +1,116 @@
+#!/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, gr_unittest
+import math
+
+class test_sig_source (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown (self):
+ self.fg = None
+
+ def test_hilbert (self):
+ fg = self.fg
+ ntaps = 51
+ sampling_freq = 100
+
+ expected_result = ( -1.4678005338941702e-11j,
+ -0.0011950774351134896j,
+ -0.0019336787518113852j,
+ -0.0034673355985432863j,
+ -0.0036765895783901215j,
+ -0.004916108213365078j,
+ -0.0042778430506587029j,
+ -0.006028641015291214j,
+ -0.005476709920912981j,
+ -0.0092810001224279404j,
+ -0.0095402700826525688j,
+ -0.016060983762145042j,
+ -0.016446959227323532j,
+ -0.02523401565849781j,
+ -0.024382550269365311j,
+ -0.035477779805660248j,
+ -0.033021725714206696j,
+ -0.048487484455108643j,
+ -0.04543270543217659j,
+ -0.069477587938308716j,
+ -0.066984444856643677j,
+ -0.10703597217798233j,
+ -0.10620346665382385j,
+ -0.1852707713842392j,
+ -0.19357112050056458j,
+ (7.2191945754696007e-09 -0.50004088878631592j),
+ (0.58778399229049683 -0.6155126690864563j),
+ (0.95105588436126709 -0.12377222627401352j),
+ (0.95105588436126709 +0.41524654626846313j),
+ (0.5877838134765625 +0.91611981391906738j),
+ (5.8516356205018383e-09 +1.0670661926269531j),
+ (-0.5877840518951416 +0.87856143712997437j),
+ (-0.95105588436126709 +0.35447561740875244j),
+ (-0.95105588436126709 -0.26055556535720825j),
+ (-0.5877838134765625 -0.77606213092803955j),
+ (-8.7774534307527574e-09 -0.96460390090942383j),
+ (0.58778399229049683 -0.78470128774642944j),
+ (0.95105588436126709 -0.28380891680717468j),
+ (0.95105588436126709 +0.32548999786376953j),
+ (0.5877838134765625 +0.82514488697052002j),
+ (1.4629089051254596e-08 +1.0096219778060913j),
+ (-0.5877840518951416 +0.81836479902267456j),
+ (-0.95105588436126709 +0.31451958417892456j),
+ (-0.95105588436126709 -0.3030143678188324j),
+ (-0.5877838134765625 -0.80480599403381348j),
+ (-1.7554906861505515e-08 -0.99516552686691284j),
+ (0.58778399229049683 -0.80540722608566284j),
+ (0.95105582475662231 -0.30557557940483093j),
+ (0.95105588436126709 +0.31097668409347534j),
+ (0.5877838134765625 +0.81027895212173462j),
+ (2.3406542482007353e-08 +1.0000816583633423j),
+ (-0.5877840518951416 +0.80908381938934326j),
+ (-0.95105588436126709 +0.30904293060302734j),
+ (-0.95105588436126709 -0.30904296040534973j),
+ (-0.5877838134765625 -0.80908387899398804j),
+ (-2.6332360292258272e-08 -1.0000815391540527j),
+ (0.58778399229049683 -0.80908381938934326j),
+ (0.95105582475662231 -0.30904299020767212j),
+ (0.95105588436126709 +0.30904293060302734j),
+ (0.5877838134765625 +0.80908381938934326j),
+ (3.218399768911695e-08 +1.0000815391540527j))
+
+
+ src1 = gr.sig_source_f (sampling_freq, gr.GR_SIN_WAVE,
+ sampling_freq * 0.10, 1.0)
+
+ head = gr.head (gr.sizeof_float, int (ntaps + sampling_freq * 0.10))
+ hilb = gr.hilbert_fc (ntaps)
+ dst1 = gr.vector_sink_c ()
+ fg.connect (src1, head)
+ fg.connect (head, hilb)
+ fg.connect (hilb, dst1)
+ fg.run ()
+ dst_data = dst1.data ()
+ self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 5)
+
+if __name__ == '__main__':
+ gr_unittest.main ()
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_iir.py b/gnuradio-core/src/python/gnuradio/gr/qa_iir.py
new file mode 100755
index 000000000..a1f2aa077
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_iir.py
@@ -0,0 +1,135 @@
+#!/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, gr_unittest
+
+class test_iir (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown (self):
+ self.fg = None
+
+ def test_iir_direct_001 (self):
+ src_data = (1, 2, 3, 4, 5, 6, 7, 8)
+ fftaps = ()
+ fbtaps = ()
+ expected_result = (0, 0, 0, 0, 0, 0, 0, 0)
+ src = gr.vector_source_f (src_data)
+ op = gr.iir_filter_ffd (fftaps, fbtaps)
+ dst = gr.vector_sink_f ()
+ self.fg.connect (src, op)
+ self.fg.connect (op, dst)
+ self.fg.run ()
+ result_data = dst.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, result_data)
+
+ def test_iir_direct_002 (self):
+ src_data = (1, 2, 3, 4, 5, 6, 7, 8)
+ fftaps = (2,)
+ fbtaps = (0,)
+ expected_result = (2, 4, 6, 8, 10, 12, 14, 16)
+ src = gr.vector_source_f (src_data)
+ op = gr.iir_filter_ffd (fftaps, fbtaps)
+ dst = gr.vector_sink_f ()
+ self.fg.connect (src, op)
+ self.fg.connect (op, dst)
+ self.fg.run ()
+ result_data = dst.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, result_data)
+
+ def test_iir_direct_003 (self):
+ src_data = (1, 2, 3, 4, 5, 6, 7, 8)
+ fftaps = (2, 11)
+ fbtaps = (0, 0)
+ expected_result = (2, 15, 28, 41, 54, 67, 80, 93)
+ src = gr.vector_source_f (src_data)
+ op = gr.iir_filter_ffd (fftaps, fbtaps)
+ dst = gr.vector_sink_f ()
+ self.fg.connect (src, op)
+ self.fg.connect (op, dst)
+ self.fg.run ()
+ result_data = dst.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, result_data)
+
+ def test_iir_direct_004 (self):
+ src_data = (1, 2, 3, 4, 5, 6, 7, 8)
+ fftaps = (2, 11)
+ fbtaps = (0, -1)
+ expected_result = (2, 13, 15, 26, 28, 39, 41, 52)
+ src = gr.vector_source_f (src_data)
+ op = gr.iir_filter_ffd (fftaps, fbtaps)
+ dst = gr.vector_sink_f ()
+ self.fg.connect (src, op)
+ self.fg.connect (op, dst)
+ self.fg.run ()
+ result_data = dst.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, result_data)
+
+ def test_iir_direct_005 (self):
+ src_data = (1, 2, 3, 4, 5, 6, 7, 8)
+ fftaps = (2, 11, 0)
+ fbtaps = (0, -1, 3)
+ expected_result = (2, 13, 21, 59, 58, 186, 68, 583)
+ src = gr.vector_source_f (src_data)
+ op = gr.iir_filter_ffd (fftaps, fbtaps)
+ dst = gr.vector_sink_f ()
+ self.fg.connect (src, op)
+ self.fg.connect (op, dst)
+ self.fg.run ()
+ result_data = dst.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, result_data)
+
+ def test_iir_direct_006 (self):
+ fftaps = (2, 11, 0)
+ fbtaps = (0, -1)
+ self.assertRaises ((RuntimeError, ValueError), gr.iir_filter_ffd, fftaps, fbtaps)
+
+ def test_iir_direct_007 (self):
+ src_data = (1, 2, 3, 4, 5, 6, 7, 8)
+ expected_result = (2, 13, 21, 59, 58, 186, 68, 583)
+ fftaps = (2, 1)
+ fbtaps = (0, -1)
+ src = gr.vector_source_f (src_data)
+ op = gr.iir_filter_ffd (fftaps, fbtaps)
+ fftaps = (2, 11, 0)
+ fbtaps = (0, -1, 3)
+ op.set_taps (fftaps, fbtaps)
+ dst = gr.vector_sink_f ()
+ self.fg.connect (src, op)
+ self.fg.connect (op, dst)
+ self.fg.run ()
+ result_data = dst.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, result_data)
+
+ def test_iir_direct_008 (self):
+ fftaps = (2, 1)
+ fbtaps = (0, -1)
+ op = gr.iir_filter_ffd (fftaps, fbtaps)
+ fftaps = (2, 11)
+ fbtaps = (0, -1, 3)
+ self.assertRaises ((RuntimeError, ValueError), op.set_taps, fftaps, fbtaps)
+
+if __name__ == '__main__':
+ gr_unittest.main ()
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_interleave.py b/gnuradio-core/src/python/gnuradio/gr/qa_interleave.py
new file mode 100755
index 000000000..003d12e64
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_interleave.py
@@ -0,0 +1,81 @@
+#!/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, gr_unittest
+import math
+
+class test_interleave (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown (self):
+ self.fg = None
+
+ def test_int_001 (self):
+ lenx = 64
+ src0 = gr.vector_source_f (range (0, lenx, 4))
+ src1 = gr.vector_source_f (range (1, lenx, 4))
+ src2 = gr.vector_source_f (range (2, lenx, 4))
+ src3 = gr.vector_source_f (range (3, lenx, 4))
+ op = gr.interleave (gr.sizeof_float)
+ dst = gr.vector_sink_f ()
+
+ self.fg.connect (src0, (op, 0))
+ self.fg.connect (src1, (op, 1))
+ self.fg.connect (src2, (op, 2))
+ self.fg.connect (src3, (op, 3))
+ self.fg.connect (op, dst)
+ self.fg.run ()
+ expected_result = tuple (range (lenx))
+ result_data = dst.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, result_data)
+
+ def test_deint_001 (self):
+ lenx = 64
+ src = gr.vector_source_f (range (lenx))
+ op = gr.deinterleave (gr.sizeof_float)
+ dst0 = gr.vector_sink_f ()
+ dst1 = gr.vector_sink_f ()
+ dst2 = gr.vector_sink_f ()
+ dst3 = gr.vector_sink_f ()
+
+ self.fg.connect (src, op)
+ self.fg.connect ((op, 0), dst0)
+ self.fg.connect ((op, 1), dst1)
+ self.fg.connect ((op, 2), dst2)
+ self.fg.connect ((op, 3), dst3)
+ self.fg.run ()
+
+ expected_result0 = tuple (range (0, lenx, 4))
+ expected_result1 = tuple (range (1, lenx, 4))
+ expected_result2 = tuple (range (2, lenx, 4))
+ expected_result3 = tuple (range (3, lenx, 4))
+
+ self.assertFloatTuplesAlmostEqual (expected_result0, dst0.data ())
+ self.assertFloatTuplesAlmostEqual (expected_result1, dst1.data ())
+ self.assertFloatTuplesAlmostEqual (expected_result2, dst2.data ())
+ self.assertFloatTuplesAlmostEqual (expected_result3, dst3.data ())
+
+if __name__ == '__main__':
+ gr_unittest.main ()
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_interp_fir_filter.py b/gnuradio-core/src/python/gnuradio/gr/qa_interp_fir_filter.py
new file mode 100755
index 000000000..4119e3e2d
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_interp_fir_filter.py
@@ -0,0 +1,54 @@
+#!/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, gr_unittest
+import math
+
+class test_interp_fir_filter (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown (self):
+ self.fg = None
+
+ def test_fff (self):
+ taps = [1, 10, 100, 1000, 10000]
+ src_data = (0, 2, 3, 5, 7, 11, 13, 17)
+ interpolation = 3
+ xr = (0,0,0,0,2,20,200,2003,20030,300,3005,30050,500,5007,50070,700,7011,70110,1100,11013,110130,1300,13017,130170)
+ expected_result = tuple ([float (x) for x in xr])
+
+ src = gr.vector_source_f (src_data)
+ op = gr.interp_fir_filter_fff (interpolation, taps)
+ dst = gr.vector_sink_f ()
+ self.fg.connect (src, op)
+ self.fg.connect (op, dst)
+ self.fg.run ()
+ result_data = dst.data ()
+ L = min(len(result_data), len(expected_result))
+ self.assertEqual (expected_result[0:L], result_data[0:L])
+
+
+if __name__ == '__main__':
+ gr_unittest.main ()
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_kludge_copy.py b/gnuradio-core/src/python/gnuradio/gr/qa_kludge_copy.py
new file mode 100755
index 000000000..f5dee1528
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_kludge_copy.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, gr_unittest
+import math
+import random
+
+
+class test_kludge_copy(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.fg = gr.flow_graph()
+ self.rng = random.Random()
+ self.rng.seed(0)
+
+ def tearDown(self):
+ self.fg = None
+ self.rng = None
+
+ def make_random_int_tuple(self, L):
+ result = []
+ for x in range(L):
+ result.append(self.rng.randint(int(-1e9), int(+1e9)))
+ return tuple(result)
+
+
+ def test_001(self):
+ # 1 input stream; 1 output stream
+ src0_data = self.make_random_int_tuple(16000)
+ src0 = gr.vector_source_i(src0_data)
+ op = gr.kludge_copy(gr.sizeof_int)
+ dst0 = gr.vector_sink_i()
+ self.fg.connect(src0, op, dst0)
+ self.fg.run()
+ dst0_data = dst0.data()
+ self.assertEqual(src0_data, dst0_data)
+
+ def test_002(self):
+ # 2 input streams; 2 output streams
+ src0_data = self.make_random_int_tuple(16000)
+ src1_data = self.make_random_int_tuple(16000)
+ src0 = gr.vector_source_i(src0_data)
+ src1 = gr.vector_source_i(src1_data)
+ op = gr.kludge_copy(gr.sizeof_int)
+ dst0 = gr.vector_sink_i()
+ dst1 = gr.vector_sink_i()
+ self.fg.connect(src0, (op, 0), dst0)
+ self.fg.connect(src1, (op, 1), dst1)
+ self.fg.run()
+ dst0_data = dst0.data()
+ dst1_data = dst1.data()
+ self.assertEqual(src0_data, dst0_data)
+ self.assertEqual(src1_data, dst1_data)
+
+ def test_003(self):
+ # number of input streams != number of output streams
+ src0_data = self.make_random_int_tuple(16000)
+ src1_data = self.make_random_int_tuple(16000)
+ src0 = gr.vector_source_i(src0_data)
+ src1 = gr.vector_source_i(src1_data)
+ op = gr.kludge_copy(gr.sizeof_int)
+ dst0 = gr.vector_sink_i()
+ dst1 = gr.vector_sink_i()
+ self.fg.connect(src0, (op, 0), dst0)
+ self.fg.connect(src1, (op, 1))
+ self.assertRaises(ValueError, self.fg.run)
+
+if __name__ == '__main__':
+ gr_unittest.main ()
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_kludged_imports.py b/gnuradio-core/src/python/gnuradio/gr/qa_kludged_imports.py
new file mode 100755
index 000000000..b20867722
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_kludged_imports.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+#
+# Copyright 2005 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_head (gr_unittest.TestCase):
+
+ def setUp(self):
+ pass
+
+ def tearDown(self):
+ pass
+
+ def test_blks_import(self):
+ # make sure that this somewhat magic import works
+ from gnuradio import blks
+
+ def test_gru_import(self):
+ # make sure that this somewhat magic import works
+ from gnuradio import gru
+
+
+if __name__ == '__main__':
+ gr_unittest.main ()
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_message.py b/gnuradio-core/src/python/gnuradio/gr/qa_message.py
new file mode 100755
index 000000000..d5d511149
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_message.py
@@ -0,0 +1,117 @@
+#!/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, gr_unittest
+import qa_basic_flow_graph
+
+
+def all_counts ():
+ return (gr.block_ncurrently_allocated (),
+ gr.block_detail_ncurrently_allocated (),
+ gr.buffer_ncurrently_allocated (),
+ gr.buffer_reader_ncurrently_allocated (),
+ gr.message_ncurrently_allocated ())
+
+
+class test_message (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.fg = gr.flow_graph()
+ self.msgq = gr.msg_queue ()
+
+ def tearDown (self):
+ self.fg = None
+ self.msgq = None
+
+ def leak_check (self, fct):
+ begin = all_counts ()
+ fct ()
+ # tear down early so we can check for leaks
+ self.tearDown ()
+ end = all_counts ()
+ self.assertEqual (begin, end)
+
+ def test_100 (self):
+ msg = gr.message (0, 1.5, 2.3)
+ self.assertEquals (0, msg.type())
+ self.assertAlmostEqual (1.5, msg.arg1())
+ self.assertAlmostEqual (2.3, msg.arg2())
+ self.assertEquals (0, msg.length())
+
+ def test_101 (self):
+ s = 'This is a test'
+ msg = gr.message_from_string(s)
+ self.assertEquals(s, msg.to_string())
+
+ def test_200 (self):
+ self.leak_check (self.body_200)
+
+ def body_200 (self):
+ self.msgq.insert_tail (gr.message (0))
+ self.assertEquals (1, self.msgq.count())
+ self.msgq.insert_tail (gr.message (1))
+ self.assertEquals (2, self.msgq.count())
+ msg0 = self.msgq.delete_head ()
+ self.assertEquals (0, msg0.type())
+ msg1 = self.msgq.delete_head ()
+ self.assertEquals (1, msg1.type())
+ self.assertEquals (0, self.msgq.count())
+
+ def test_201 (self):
+ self.leak_check (self.body_201)
+
+ def body_201 (self):
+ self.msgq.insert_tail (gr.message (0))
+ self.assertEquals (1, self.msgq.count())
+ self.msgq.insert_tail (gr.message (1))
+ self.assertEquals (2, self.msgq.count())
+
+ def test_202 (self):
+ self.leak_check (self.body_202)
+
+ def body_202 (self):
+ # global msg
+ msg = gr.message (666)
+
+ def test_300(self):
+ input_data = (0,1,2,3,4,5,6,7,8,9)
+ src = gr.vector_source_b(input_data)
+ dst = gr.vector_sink_b()
+ self.fg.connect(src, dst)
+ self.fg.run()
+ self.assertEquals(input_data, dst.data())
+
+ def test_301(self):
+ src = gr.message_source(gr.sizeof_char)
+ dst = gr.vector_sink_b()
+ self.fg.connect(src, dst)
+ src.msgq().insert_tail(gr.message_from_string('01234'))
+ src.msgq().insert_tail(gr.message_from_string('5'))
+ src.msgq().insert_tail(gr.message_from_string(''))
+ src.msgq().insert_tail(gr.message_from_string('6789'))
+ src.msgq().insert_tail(gr.message(1)) # send EOF
+ self.fg.run()
+ self.assertEquals(tuple(map(ord, '0123456789')), dst.data())
+
+
+if __name__ == '__main__':
+ gr_unittest.main ()
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_mute.py b/gnuradio-core/src/python/gnuradio/gr/qa_mute.py
new file mode 100755
index 000000000..863c308a4
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_mute.py
@@ -0,0 +1,89 @@
+#!/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, gr_unittest
+
+class test_head (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown (self):
+ self.fg = None
+
+ def help_ii (self, src_data, exp_data, op):
+ for s in zip (range (len (src_data)), src_data):
+ src = gr.vector_source_i (s[1])
+ self.fg.connect (src, (op, s[0]))
+ dst = gr.vector_sink_i ()
+ self.fg.connect (op, dst)
+ self.fg.run ()
+ result_data = dst.data ()
+ self.assertEqual (exp_data, result_data)
+
+ def help_ff (self, src_data, exp_data, op):
+ for s in zip (range (len (src_data)), src_data):
+ src = gr.vector_source_f (s[1])
+ self.fg.connect (src, (op, s[0]))
+ dst = gr.vector_sink_f ()
+ self.fg.connect (op, dst)
+ self.fg.run ()
+ result_data = dst.data ()
+ self.assertEqual (exp_data, result_data)
+
+ def help_cc (self, src_data, exp_data, op):
+ for s in zip (range (len (src_data)), src_data):
+ src = gr.vector_source_c (s[1])
+ self.fg.connect (src, (op, s[0]))
+ dst = gr.vector_sink_c ()
+ self.fg.connect (op, dst)
+ self.fg.run ()
+ result_data = dst.data ()
+ self.assertEqual (exp_data, result_data)
+
+ def test_unmute_ii(self):
+ src_data = (1, 2, 3, 4, 5)
+ expected_result = (1, 2, 3, 4, 5)
+ op = gr.mute_ii (False)
+ self.help_ii ((src_data,), expected_result, op)
+
+ def test_mute_ii(self):
+ src_data = (1, 2, 3, 4, 5)
+ expected_result = (0, 0, 0, 0, 0)
+ op = gr.mute_ii (True)
+ self.help_ii ((src_data,), expected_result, op)
+
+ def test_unmute_cc (self):
+ src_data = (1+5j, 2+5j, 3+5j, 4+5j, 5+5j)
+ expected_result = (1+5j, 2+5j, 3+5j, 4+5j, 5+5j)
+ op = gr.mute_cc (False)
+ self.help_cc ((src_data,), expected_result, op)
+
+ def test_unmute_cc (self):
+ src_data = (1+5j, 2+5j, 3+5j, 4+5j, 5+5j)
+ expected_result = (0+0j, 0+0j, 0+0j, 0+0j, 0+0j)
+ op = gr.mute_cc (True)
+ self.help_cc ((src_data,), expected_result, op)
+
+
+if __name__ == '__main__':
+ gr_unittest.main ()
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_nlog10.py b/gnuradio-core/src/python/gnuradio/gr/qa_nlog10.py
new file mode 100755
index 000000000..f056d1100
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_nlog10.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+#
+# Copyright 2005 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_single_pole_iir(gr_unittest.TestCase):
+
+ def setUp (self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown (self):
+ self.fg = None
+
+ def test_001(self):
+ src_data = (-10, 0, 10, 100, 1000, 10000, 100000)
+ expected_result = (-180, -180, 10, 20, 30, 40, 50)
+ src = gr.vector_source_f(src_data)
+ op = gr.nlog10_ff(10)
+ dst = gr.vector_sink_f()
+ self.fg.connect (src, op, dst)
+ self.fg.run()
+ result_data = dst.data()
+ self.assertFloatTuplesAlmostEqual (expected_result, result_data)
+
+
+if __name__ == '__main__':
+ gr_unittest.main ()
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_packed_to_unpacked.py b/gnuradio-core/src/python/gnuradio/gr/qa_packed_to_unpacked.py
new file mode 100755
index 000000000..d5472c8d7
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_packed_to_unpacked.py
@@ -0,0 +1,405 @@
+#!/usr/bin/env python
+#
+# Copyright 2005 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import random
+
+class test_packing(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown(self):
+ self.fg = None
+
+ def test_001(self):
+ """
+ Test stream_to_streams.
+ """
+ src_data = (0x80,)
+ expected_results = (1,0,0,0,0,0,0,0)
+ src = gr.vector_source_b(src_data,False)
+ op = gr.packed_to_unpacked_bb(1, gr.GR_MSB_FIRST)
+ self.fg.connect(src, op)
+
+ dst = gr.vector_sink_b()
+ self.fg.connect(op, dst)
+
+ self.fg.run()
+
+ self.assertEqual(expected_results, dst.data())
+
+ def test_002(self):
+ """
+ Test stream_to_streams.
+ """
+ src_data = (0x80,)
+ expected_results = (0,0,0,0,0,0,0, 1)
+ src = gr.vector_source_b(src_data,False)
+ op = gr.packed_to_unpacked_bb(1, gr.GR_LSB_FIRST)
+ self.fg.connect(src, op)
+
+ dst = gr.vector_sink_b()
+ self.fg.connect(op, dst)
+
+ self.fg.run()
+
+ self.assertEqual(expected_results, dst.data())
+
+ def test_003(self):
+ """
+ Test stream_to_streams.
+ """
+ src_data = (0x11,)
+ expected_results = (4, 2)
+ src = gr.vector_source_b(src_data,False)
+ op = gr.packed_to_unpacked_bb(3, gr.GR_LSB_FIRST)
+ self.fg.connect(src, op)
+
+ dst = gr.vector_sink_b()
+ self.fg.connect(op, dst)
+
+ self.fg.run()
+
+ self.assertEqual(expected_results, dst.data())
+
+ def test_004(self):
+ """
+ Test stream_to_streams.
+ """
+ src_data = (0x11,)
+ expected_results = (0, 4)
+ src = gr.vector_source_b(src_data,False)
+ op = gr.packed_to_unpacked_bb(3, gr.GR_MSB_FIRST)
+ self.fg.connect(src, op)
+
+ dst = gr.vector_sink_b()
+ self.fg.connect(op, dst)
+
+ self.fg.run()
+
+ self.assertEqual(expected_results, dst.data())
+
+ def test_005(self):
+ """
+ Test stream_to_streams.
+ """
+ src_data = (1,0,0,0,0,0,1,0,0,1,0,1,1,0,1,0)
+ expected_results = (0x82,0x5a)
+ src = gr.vector_source_b(src_data,False)
+ op = gr.unpacked_to_packed_bb(1, gr.GR_MSB_FIRST)
+ self.fg.connect(src, op)
+
+ dst = gr.vector_sink_b()
+ self.fg.connect(op, dst)
+
+ self.fg.run()
+
+ self.assertEqual(expected_results, dst.data())
+
+ def test_006(self):
+ """
+ Test stream_to_streams.
+ """
+ src_data = (0,1,0,0,0,0,0,1,0,1,0,1,1,0,1,0)
+ expected_results = (0x82,0x5a)
+ src = gr.vector_source_b(src_data,False)
+ op = gr.unpacked_to_packed_bb(1, gr.GR_LSB_FIRST)
+ self.fg.connect(src, op)
+
+ dst = gr.vector_sink_b()
+ self.fg.connect(op, dst)
+
+ self.fg.run()
+
+ self.assertEqual(expected_results, dst.data())
+
+
+ def test_007(self):
+ """
+ Test stream_to_streams.
+ """
+ src_data = (4, 2, 0,0,0)
+ expected_results = (0x11,)
+ src = gr.vector_source_b(src_data,False)
+ op = gr.unpacked_to_packed_bb(3, gr.GR_LSB_FIRST)
+ self.fg.connect(src, op)
+
+ dst = gr.vector_sink_b()
+ self.fg.connect(op, dst)
+
+ self.fg.run()
+
+ self.assertEqual(expected_results, dst.data())
+
+ def test_008(self):
+ """
+ Test stream_to_streams.
+ """
+ src_data = (0, 4, 2,0,0)
+ expected_results = (0x11,)
+ src = gr.vector_source_b(src_data,False)
+ op = gr.unpacked_to_packed_bb(3, gr.GR_MSB_FIRST)
+ self.fg.connect(src, op)
+
+ dst = gr.vector_sink_b()
+ self.fg.connect(op, dst)
+
+ self.fg.run()
+
+ self.assertEqual(expected_results, dst.data())
+
+ def test_009(self):
+ """
+ Test stream_to_streams.
+ """
+
+ random.seed(0)
+ src_data = []
+ for i in xrange(202):
+ src_data.append((random.randint(0,255)))
+ src_data = tuple(src_data)
+ expected_results = src_data
+
+ src = gr.vector_source_b(tuple(src_data),False)
+ op1 = gr.packed_to_unpacked_bb(3, gr.GR_MSB_FIRST)
+ op2 = gr.unpacked_to_packed_bb(3, gr.GR_MSB_FIRST)
+ self.fg.connect(src, op1, op2)
+
+ dst = gr.vector_sink_b()
+ self.fg.connect(op2, dst)
+
+ self.fg.run()
+
+ self.assertEqual(expected_results[0:201], dst.data())
+
+ def test_010(self):
+ """
+ Test stream_to_streams.
+ """
+
+ random.seed(0)
+ src_data = []
+ for i in xrange(56):
+ src_data.append((random.randint(0,255)))
+ src_data = tuple(src_data)
+ expected_results = src_data
+ src = gr.vector_source_b(tuple(src_data),False)
+ op1 = gr.packed_to_unpacked_bb(7, gr.GR_MSB_FIRST)
+ op2 = gr.unpacked_to_packed_bb(7, gr.GR_MSB_FIRST)
+ self.fg.connect(src, op1, op2)
+ dst = gr.vector_sink_b()
+ self.fg.connect(op2, dst)
+
+ self.fg.run()
+ self.assertEqual(expected_results[0:201], dst.data())
+
+ def test_011(self):
+ """
+ Test stream_to_streams.
+ """
+
+ random.seed(0)
+ src_data = []
+ for i in xrange(56):
+ src_data.append((random.randint(0,255)))
+ src_data = tuple(src_data)
+ expected_results = src_data
+ src = gr.vector_source_b(tuple(src_data),False)
+ op1 = gr.packed_to_unpacked_bb(7, gr.GR_LSB_FIRST)
+ op2 = gr.unpacked_to_packed_bb(7, gr.GR_LSB_FIRST)
+ self.fg.connect(src, op1, op2)
+ dst = gr.vector_sink_b()
+ self.fg.connect(op2, dst)
+
+ self.fg.run()
+ self.assertEqual(expected_results[0:201], dst.data())
+
+
+ # tests on shorts
+
+ def test_100a(self):
+ """
+ test short version
+ """
+ random.seed(0)
+ src_data = []
+ for i in xrange(100):
+ src_data.append((random.randint(-2**15,2**15-1)))
+ src_data = tuple(src_data)
+ expected_results = src_data
+ src = gr.vector_source_s(tuple(src_data),False)
+ op1 = gr.packed_to_unpacked_ss(1, gr.GR_MSB_FIRST)
+ op2 = gr.unpacked_to_packed_ss(1, gr.GR_MSB_FIRST)
+ self.fg.connect(src, op1, op2)
+ dst = gr.vector_sink_s()
+ self.fg.connect(op2, dst)
+
+ self.fg.run()
+ self.assertEqual(expected_results, dst.data())
+
+ def test_100b(self):
+ """
+ test short version
+ """
+ random.seed(0)
+ src_data = []
+ for i in xrange(100):
+ src_data.append((random.randint(-2**15,2**15-1)))
+ src_data = tuple(src_data)
+ expected_results = src_data
+ src = gr.vector_source_s(tuple(src_data),False)
+ op1 = gr.packed_to_unpacked_ss(1, gr.GR_LSB_FIRST)
+ op2 = gr.unpacked_to_packed_ss(1, gr.GR_LSB_FIRST)
+ self.fg.connect(src, op1, op2)
+ dst = gr.vector_sink_s()
+ self.fg.connect(op2, dst)
+
+ self.fg.run()
+ self.assertEqual(expected_results, dst.data())
+
+ def test_101a(self):
+ """
+ test short version
+ """
+ random.seed(0)
+ src_data = []
+ for i in xrange(100):
+ src_data.append((random.randint(-2**15,2**15-1)))
+ src_data = tuple(src_data)
+ expected_results = src_data
+ src = gr.vector_source_s(tuple(src_data),False)
+ op1 = gr.packed_to_unpacked_ss(8, gr.GR_MSB_FIRST)
+ op2 = gr.unpacked_to_packed_ss(8, gr.GR_MSB_FIRST)
+ self.fg.connect(src, op1, op2)
+ dst = gr.vector_sink_s()
+ self.fg.connect(op2, dst)
+
+ self.fg.run()
+ self.assertEqual(expected_results, dst.data())
+
+ def test_101b(self):
+ """
+ test short version
+ """
+ random.seed(0)
+ src_data = []
+ for i in xrange(100):
+ src_data.append((random.randint(-2**15,2**15-1)))
+ src_data = tuple(src_data)
+ expected_results = src_data
+ src = gr.vector_source_s(tuple(src_data),False)
+ op1 = gr.packed_to_unpacked_ss(8, gr.GR_LSB_FIRST)
+ op2 = gr.unpacked_to_packed_ss(8, gr.GR_LSB_FIRST)
+ self.fg.connect(src, op1, op2)
+ dst = gr.vector_sink_s()
+ self.fg.connect(op2, dst)
+
+ self.fg.run()
+ self.assertEqual(expected_results, dst.data())
+
+ # tests on ints
+
+ def test_200a(self):
+ """
+ test int version
+ """
+ random.seed(0)
+ src_data = []
+ for i in xrange(100):
+ src_data.append((random.randint(-2**31,2**31-1)))
+ src_data = tuple(src_data)
+ expected_results = src_data
+ src = gr.vector_source_i(tuple(src_data),False)
+ op1 = gr.packed_to_unpacked_ii(1, gr.GR_MSB_FIRST)
+ op2 = gr.unpacked_to_packed_ii(1, gr.GR_MSB_FIRST)
+ self.fg.connect(src, op1, op2)
+ dst = gr.vector_sink_i()
+ self.fg.connect(op2, dst)
+
+ self.fg.run()
+ self.assertEqual(expected_results, dst.data())
+
+ def test_200b(self):
+ """
+ test int version
+ """
+ random.seed(0)
+ src_data = []
+ for i in xrange(100):
+ src_data.append((random.randint(-2**31,2**31-1)))
+ src_data = tuple(src_data)
+ expected_results = src_data
+ src = gr.vector_source_i(tuple(src_data),False)
+ op1 = gr.packed_to_unpacked_ii(1, gr.GR_LSB_FIRST)
+ op2 = gr.unpacked_to_packed_ii(1, gr.GR_LSB_FIRST)
+ self.fg.connect(src, op1, op2)
+ dst = gr.vector_sink_i()
+ self.fg.connect(op2, dst)
+
+ self.fg.run()
+ self.assertEqual(expected_results, dst.data())
+
+ def test_201a(self):
+ """
+ test int version
+ """
+ random.seed(0)
+ src_data = []
+ for i in xrange(100):
+ src_data.append((random.randint(-2**31,2**31-1)))
+ src_data = tuple(src_data)
+ expected_results = src_data
+ src = gr.vector_source_i(tuple(src_data),False)
+ op1 = gr.packed_to_unpacked_ii(8, gr.GR_MSB_FIRST)
+ op2 = gr.unpacked_to_packed_ii(8, gr.GR_MSB_FIRST)
+ self.fg.connect(src, op1, op2)
+ dst = gr.vector_sink_i()
+ self.fg.connect(op2, dst)
+
+ self.fg.run()
+ self.assertEqual(expected_results, dst.data())
+
+ def test_201b(self):
+ """
+ test int version
+ """
+ random.seed(0)
+ src_data = []
+ for i in xrange(100):
+ src_data.append((random.randint(-2**31,2**31-1)))
+ src_data = tuple(src_data)
+ expected_results = src_data
+ src = gr.vector_source_i(tuple(src_data),False)
+ op1 = gr.packed_to_unpacked_ii(8, gr.GR_LSB_FIRST)
+ op2 = gr.unpacked_to_packed_ii(8, gr.GR_LSB_FIRST)
+ self.fg.connect(src, op1, op2)
+ dst = gr.vector_sink_i()
+ self.fg.connect(op2, dst)
+
+ self.fg.run()
+ self.assertEqual(expected_results, dst.data())
+
+
+if __name__ == '__main__':
+ gr_unittest.main ()
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_pipe_fittings.py b/gnuradio-core/src/python/gnuradio/gr/qa_pipe_fittings.py
new file mode 100755
index 000000000..dca18c8ec
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_pipe_fittings.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python
+#
+# Copyright 2005 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+if 0:
+ import os
+ print "pid =", os.getpid()
+ raw_input("Attach, then press Enter to continue")
+
+
+def calc_expected_result(src_data, n):
+ assert (len(src_data) % n) == 0
+ result = [list() for x in range(n)]
+ #print "len(result) =", len(result)
+ for i in xrange(len(src_data)):
+ (result[i % n]).append(src_data[i])
+ return [tuple(x) for x in result]
+
+
+class test_pipe_fittings(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown(self):
+ self.fg = None
+
+ def test_001(self):
+ """
+ Test stream_to_streams.
+ """
+ n = 8
+ src_len = n * 8
+ src_data = range(src_len)
+
+ expected_results = calc_expected_result(src_data, n)
+ #print "expected results: ", expected_results
+ src = gr.vector_source_i(src_data)
+ op = gr.stream_to_streams(gr.sizeof_int, n)
+ self.fg.connect(src, op)
+
+ dsts = []
+ for i in range(n):
+ dst = gr.vector_sink_i()
+ self.fg.connect((op, i), (dst, 0))
+ dsts.append(dst)
+
+ self.fg.run()
+
+ for d in range(n):
+ self.assertEqual(expected_results[d], dsts[d].data())
+
+ def test_002(self):
+ """
+ Test streams_to_stream (using stream_to_streams).
+ """
+ n = 8
+ src_len = n * 8
+ src_data = tuple(range(src_len))
+ expected_results = src_data
+
+ src = gr.vector_source_i(src_data)
+ op1 = gr.stream_to_streams(gr.sizeof_int, n)
+ op2 = gr.streams_to_stream(gr.sizeof_int, n)
+ dst = gr.vector_sink_i()
+
+ self.fg.connect(src, op1)
+ for i in range(n):
+ self.fg.connect((op1, i), (op2, i))
+ self.fg.connect(op2, dst)
+
+ self.fg.run()
+ self.assertEqual(expected_results, dst.data())
+
+ def test_003(self):
+ """
+ Test streams_to_vector (using stream_to_streams & vector_to_stream).
+ """
+ n = 8
+ src_len = n * 8
+ src_data = tuple(range(src_len))
+ expected_results = src_data
+
+ src = gr.vector_source_i(src_data)
+ op1 = gr.stream_to_streams(gr.sizeof_int, n)
+ op2 = gr.streams_to_vector(gr.sizeof_int, n)
+ op3 = gr.vector_to_stream(gr.sizeof_int, n)
+ dst = gr.vector_sink_i()
+
+ self.fg.connect(src, op1)
+ for i in range(n):
+ self.fg.connect((op1, i), (op2, i))
+ self.fg.connect(op2, op3, dst)
+
+ self.fg.run()
+ self.assertEqual(expected_results, dst.data())
+
+ def test_004(self):
+ """
+ Test vector_to_streams.
+ """
+ n = 8
+ src_len = n * 8
+ src_data = tuple(range(src_len))
+ expected_results = src_data
+
+ src = gr.vector_source_i(src_data)
+ op1 = gr.stream_to_vector(gr.sizeof_int, n)
+ op2 = gr.vector_to_streams(gr.sizeof_int, n)
+ op3 = gr.streams_to_stream(gr.sizeof_int, n)
+ dst = gr.vector_sink_i()
+
+ self.fg.connect(src, op1, op2)
+ for i in range(n):
+ self.fg.connect((op2, i), (op3, i))
+ self.fg.connect(op3, dst)
+
+ self.fg.run()
+ self.assertEqual(expected_results, dst.data())
+
+if __name__ == '__main__':
+ gr_unittest.main ()
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_rational_resampler.py b/gnuradio-core/src/python/gnuradio/gr/qa_rational_resampler.py
new file mode 100755
index 000000000..2505482d5
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_rational_resampler.py
@@ -0,0 +1,290 @@
+#!/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, gr_unittest
+from gnuradio import blks
+import math
+import random
+import sys
+
+#import os
+#print os.getpid()
+#raw_input('Attach with gdb, then press Enter: ')
+
+
+def random_floats(n):
+ r = []
+ for x in xrange(n):
+ r.append(float(random.randint(-32768, 32768)))
+ return tuple(r)
+
+
+def reference_dec_filter(src_data, decim, taps):
+ fg = gr.flow_graph()
+ src = gr.vector_source_f(src_data)
+ op = gr.fir_filter_fff(decim, taps)
+ dst = gr.vector_sink_f()
+ fg.connect(src, op, dst)
+ fg.run()
+ result_data = dst.data()
+ fg = None
+ return result_data
+
+def reference_interp_filter(src_data, interp, taps):
+ fg = gr.flow_graph()
+ src = gr.vector_source_f(src_data)
+ op = gr.interp_fir_filter_fff(interp, taps)
+ dst = gr.vector_sink_f()
+ fg.connect(src, op, dst)
+ fg.run()
+ result_data = dst.data()
+ fg = None
+ return result_data
+
+def reference_interp_dec_filter(src_data, interp, decim, taps):
+ fg = gr.flow_graph()
+ src = gr.vector_source_f(src_data)
+ up = gr.interp_fir_filter_fff(interp, (1,))
+ dn = gr.fir_filter_fff(decim, taps)
+ dst = gr.vector_sink_f()
+ fg.connect(src, up, dn, dst)
+ fg.run()
+ result_data = dst.data()
+ fg = None
+ return result_data
+
+
+class test_rational_resampler (gr_unittest.TestCase):
+
+ def setUp(self):
+ self.fg = gr.flow_graph()
+
+ def tearDown(self):
+ self.fg = None
+
+ #
+ # test the gr.rational_resampler_base primitives...
+ #
+
+ def test_000_1_to_1(self):
+ taps = (-4, 5)
+ src_data = (234, -4, 23, -56, 45, 98, -23, -7)
+ xr = (-936, 1186, -112, 339, -460, -167, 582)
+ expected_result = tuple([float(x) for x in xr])
+
+ src = gr.vector_source_f(src_data)
+ op = gr.rational_resampler_base_fff(1, 1, taps)
+ dst = gr.vector_sink_f()
+ self.fg.connect(src, op)
+ self.fg.connect(op, dst)
+ self.fg.run()
+ result_data = dst.data()
+ self.assertEqual(expected_result, result_data)
+
+ def test_001_interp(self):
+ taps = [1, 10, 100, 1000, 10000]
+ src_data = (0, 2, 3, 5, 7, 11, 13, 17)
+ interpolation = 3
+ xr = (0,0,0,0,2,20,200,2003,20030,300,3005,30050,500,5007,50070,700,7011,70110,1100,11013,110130,1300,13017,130170,1700.0,17000.0,170000.0)
+ expected_result = tuple([float(x) for x in xr])
+
+ src = gr.vector_source_f(src_data)
+ op = gr.rational_resampler_base_fff(interpolation, 1, taps)
+ dst = gr.vector_sink_f()
+ self.fg.connect(src, op)
+ self.fg.connect(op, dst)
+ self.fg.run()
+ result_data = dst.data()
+ self.assertEqual(expected_result, result_data)
+
+ def test_002_interp(self):
+ taps = random_floats(31)
+ #src_data = random_floats(10000) # FIXME the 10k case fails!
+ src_data = random_floats(1000)
+ interpolation = 3
+
+ expected_result = reference_interp_filter(src_data, interpolation, taps)
+
+ src = gr.vector_source_f(src_data)
+ op = gr.rational_resampler_base_fff(interpolation, 1, taps)
+ dst = gr.vector_sink_f()
+ self.fg.connect(src, op)
+ self.fg.connect(op, dst)
+ self.fg.run()
+ result_data = dst.data()
+
+ L1 = len(result_data)
+ L2 = len(expected_result)
+ L = min(L1, L2)
+ if False:
+ sys.stderr.write('delta = %2d: ntaps = %d interp = %d ilen = %d\n' %
+ (L2 - L1, len(taps), interpolation, len(src_data)))
+ sys.stderr.write(' len(result_data) = %d len(expected_result) = %d\n' %
+ (len(result_data), len(expected_result)))
+ #self.assertEqual(expected_result[0:L], result_data[0:L])
+ # FIXME check first 3 answers
+ self.assertEqual(expected_result[3:L], result_data[3:L])
+
+ def test_003_interp(self):
+ taps = random_floats(31)
+ src_data = random_floats(10000)
+ decimation = 3
+
+ expected_result = reference_dec_filter(src_data, decimation, taps)
+
+ src = gr.vector_source_f(src_data)
+ op = gr.rational_resampler_base_fff(1, decimation, taps)
+ dst = gr.vector_sink_f()
+ self.fg.connect(src, op)
+ self.fg.connect(op, dst)
+ self.fg.run()
+ result_data = dst.data()
+
+ L1 = len(result_data)
+ L2 = len(expected_result)
+ L = min(L1, L2)
+ if False:
+ sys.stderr.write('delta = %2d: ntaps = %d decim = %d ilen = %d\n' %
+ (L2 - L1, len(taps), decimation, len(src_data)))
+ sys.stderr.write(' len(result_data) = %d len(expected_result) = %d\n' %
+ (len(result_data), len(expected_result)))
+ self.assertEqual(expected_result[0:L], result_data[0:L])
+
+ # FIXME disabled. Triggers hang on SuSE 10.0
+ def xtest_004_decim_random_vals(self):
+ MAX_TAPS = 9
+ MAX_DECIM = 7
+ OUTPUT_LEN = 9
+
+ random.seed(0) # we want reproducibility
+
+ for ntaps in xrange(1, MAX_TAPS + 1):
+ for decim in xrange(1, MAX_DECIM+1):
+ for ilen in xrange(ntaps + decim, ntaps + OUTPUT_LEN*decim):
+ src_data = random_floats(ilen)
+ taps = random_floats(ntaps)
+ expected_result = reference_dec_filter(src_data, decim, taps)
+
+ fg = gr.flow_graph()
+ src = gr.vector_source_f(src_data)
+ op = gr.rational_resampler_base_fff(1, decim, taps)
+ dst = gr.vector_sink_f()
+ fg.connect(src, op, dst)
+ fg.run()
+ fg = None
+ result_data = dst.data()
+ L1 = len(result_data)
+ L2 = len(expected_result)
+ L = min(L1, L2)
+ if False:
+ sys.stderr.write('delta = %2d: ntaps = %d decim = %d ilen = %d\n' % (L2 - L1, ntaps, decim, ilen))
+ sys.stderr.write(' len(result_data) = %d len(expected_result) = %d\n' %
+ (len(result_data), len(expected_result)))
+ self.assertEqual(expected_result[0:L], result_data[0:L])
+
+
+ # FIXME disabled. Triggers hang on SuSE 10.0
+ def xtest_005_interp_random_vals(self):
+ MAX_TAPS = 9
+ MAX_INTERP = 7
+ INPUT_LEN = 9
+
+ random.seed(0) # we want reproducibility
+
+ for ntaps in xrange(1, MAX_TAPS + 1):
+ for interp in xrange(1, MAX_INTERP+1):
+ for ilen in xrange(ntaps, ntaps + INPUT_LEN):
+ src_data = random_floats(ilen)
+ taps = random_floats(ntaps)
+ expected_result = reference_interp_filter(src_data, interp, taps)
+
+ fg = gr.flow_graph()
+ src = gr.vector_source_f(src_data)
+ op = gr.rational_resampler_base_fff(interp, 1, taps)
+ dst = gr.vector_sink_f()
+ fg.connect(src, op, dst)
+ fg.run()
+ fg = None
+ result_data = dst.data()
+ L1 = len(result_data)
+ L2 = len(expected_result)
+ L = min(L1, L2)
+ #if True or abs(L1-L2) > 1:
+ if False:
+ sys.stderr.write('delta = %2d: ntaps = %d interp = %d ilen = %d\n' % (L2 - L1, ntaps, interp, ilen))
+ #sys.stderr.write(' len(result_data) = %d len(expected_result) = %d\n' %
+ # (len(result_data), len(expected_result)))
+ #self.assertEqual(expected_result[0:L], result_data[0:L])
+ # FIXME check first ntaps+1 answers
+ self.assertEqual(expected_result[ntaps+1:L], result_data[ntaps+1:L])
+
+
+ def test_006_interp_decim(self):
+ taps = (0,1,0,0)
+ src_data = range(10000)
+ interp = 3
+ decimation = 2
+
+ expected_result = reference_interp_dec_filter(src_data, interp, decimation, taps)
+
+ src = gr.vector_source_f(src_data)
+ op = gr.rational_resampler_base_fff(interp, decimation, taps)
+ dst = gr.vector_sink_f()
+ self.fg.connect(src, op)
+ self.fg.connect(op, dst)
+ self.fg.run()
+ result_data = dst.data()
+
+ L1 = len(result_data)
+ L2 = len(expected_result)
+ L = min(L1, L2)
+ if False:
+ sys.stderr.write('delta = %2d: ntaps = %d decim = %d ilen = %d\n' %
+ (L2 - L1, len(taps), decimation, len(src_data)))
+ sys.stderr.write(' len(result_data) = %d len(expected_result) = %d\n' %
+ (len(result_data), len(expected_result)))
+ self.assertEqual(expected_result[1:L], result_data[1:L])
+
+ #
+ # test the blks.rational_resampler_??? primitives...
+ #
+
+ def test_101_interp(self):
+ taps = [1, 10, 100, 1000, 10000]
+ src_data = (0, 2, 3, 5, 7, 11, 13, 17)
+ interpolation = 3
+ xr = (0,0,0,0,2,20,200,2003,20030,300,3005,30050,500,5007,50070,700,7011,70110,1100,11013,110130,1300,13017,130170,1700.0,17000.0,170000.0)
+ expected_result = tuple([float(x) for x in xr])
+
+ src = gr.vector_source_f(src_data)
+ op = blks.rational_resampler_fff(self.fg, interpolation, 1, taps=taps)
+ dst = gr.vector_sink_f()
+ self.fg.connect(src, op)
+ self.fg.connect(op, dst)
+ self.fg.run()
+ result_data = dst.data()
+ self.assertEqual(expected_result, result_data)
+
+
+if __name__ == '__main__':
+ gr_unittest.main()
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_sig_source.py b/gnuradio-core/src/python/gnuradio/gr/qa_sig_source.py
new file mode 100755
index 000000000..39866ac43
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_sig_source.py
@@ -0,0 +1,85 @@
+#!/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, gr_unittest
+import math
+
+class test_sig_source (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown (self):
+ self.fg = None
+
+ def test_const_f (self):
+ fg = self.fg
+ expected_result = (1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5)
+ src1 = gr.sig_source_f (1e6, gr.GR_CONST_WAVE, 0, 1.5)
+ op = gr.head (gr.sizeof_float, 10)
+ dst1 = gr.vector_sink_f ()
+ fg.connect (src1, op)
+ fg.connect (op, dst1)
+ fg.run ()
+ dst_data = dst1.data ()
+ self.assertEqual (expected_result, dst_data)
+
+ def test_const_i (self):
+ fg = self.fg
+ expected_result = (1, 1, 1, 1)
+ src1 = gr.sig_source_i (1e6, gr.GR_CONST_WAVE, 0, 1)
+ op = gr.head (gr.sizeof_int, 4)
+ dst1 = gr.vector_sink_i ()
+ fg.connect (src1, op)
+ fg.connect (op, dst1)
+ fg.run ()
+ dst_data = dst1.data ()
+ self.assertEqual (expected_result, dst_data)
+
+ def test_sine_f (self):
+ fg = self.fg
+ sqrt2 = math.sqrt(2) / 2
+ expected_result = (0, sqrt2, 1, sqrt2, 0, -sqrt2, -1, -sqrt2, 0)
+ src1 = gr.sig_source_f (8, gr.GR_SIN_WAVE, 1.0, 1.0)
+ op = gr.head (gr.sizeof_float, 9)
+ dst1 = gr.vector_sink_f ()
+ fg.connect (src1, op)
+ fg.connect (op, dst1)
+ fg.run ()
+ dst_data = dst1.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, dst_data, 5)
+
+ def test_cosine_f (self):
+ fg = self.fg
+ sqrt2 = math.sqrt(2) / 2
+ expected_result = (1, sqrt2, 0, -sqrt2, -1, -sqrt2, 0, sqrt2, 1)
+ src1 = gr.sig_source_f (8, gr.GR_COS_WAVE, 1.0, 1.0)
+ op = gr.head (gr.sizeof_float, 9)
+ dst1 = gr.vector_sink_f ()
+ fg.connect (src1, op)
+ fg.connect (op, dst1)
+ fg.run ()
+ dst_data = dst1.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, dst_data, 5)
+
+if __name__ == '__main__':
+ gr_unittest.main ()
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_single_pole_iir.py b/gnuradio-core/src/python/gnuradio/gr/qa_single_pole_iir.py
new file mode 100755
index 000000000..5898188f8
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_single_pole_iir.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+#
+# Copyright 2005 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_single_pole_iir(gr_unittest.TestCase):
+
+ def setUp (self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown (self):
+ self.fg = None
+
+ def test_001(self):
+ src_data = (0, 1000, 2000, 3000, 4000, 5000)
+ expected_result = src_data
+ src = gr.vector_source_f(src_data)
+ op = gr.single_pole_iir_filter_ff (1.0)
+ dst = gr.vector_sink_f()
+ self.fg.connect (src, op, dst)
+ self.fg.run()
+ result_data = dst.data()
+ self.assertFloatTuplesAlmostEqual (expected_result, result_data)
+
+ def test_002(self):
+ src_data = (0, 1000, 2000, 3000, 4000, 5000)
+ expected_result = (0, 125, 359.375, 689.453125, 1103.271484, 1590.36255)
+ src = gr.vector_source_f(src_data)
+ op = gr.single_pole_iir_filter_ff (0.125)
+ dst = gr.vector_sink_f()
+ self.fg.connect (src, op, dst)
+ self.fg.run()
+ result_data = dst.data()
+ self.assertFloatTuplesAlmostEqual (expected_result, result_data, 3)
+
+ def test_003(self):
+ block_size = 2
+ src_data = (0, 1000, 2000, 3000, 4000, 5000)
+ expected_result = (0, 125, 250, 484.375, 718.75, 1048.828125)
+ src = gr.vector_source_f(src_data)
+ s2p = gr.serial_to_parallel(gr.sizeof_float, block_size)
+ op = gr.single_pole_iir_filter_ff (0.125, block_size)
+ p2s = gr.parallel_to_serial(gr.sizeof_float, block_size)
+ dst = gr.vector_sink_f()
+ self.fg.connect (src, s2p, op, p2s, dst)
+ self.fg.run()
+ result_data = dst.data()
+ self.assertFloatTuplesAlmostEqual (expected_result, result_data, 3)
+
+
+if __name__ == '__main__':
+ gr_unittest.main ()
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_single_pole_iir_cc.py b/gnuradio-core/src/python/gnuradio/gr/qa_single_pole_iir_cc.py
new file mode 100755
index 000000000..a7889d177
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_single_pole_iir_cc.py
@@ -0,0 +1,72 @@
+#!/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, gr_unittest
+
+class test_single_pole_iir_cc(gr_unittest.TestCase):
+
+ def setUp (self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown (self):
+ self.fg = None
+
+ def test_001(self):
+ src_data = (0+0j, 1000+1000j, 2000+2000j, 3000+3000j, 4000+4000j, 5000+5000j)
+ expected_result = src_data
+ src = gr.vector_source_c(src_data)
+ op = gr.single_pole_iir_filter_cc (1.0)
+ dst = gr.vector_sink_c()
+ self.fg.connect (src, op, dst)
+ self.fg.run()
+ result_data = dst.data()
+ self.assertComplexTuplesAlmostEqual (expected_result, result_data)
+
+ def test_002(self):
+ src_data = (complex(0,0), complex(1000,-1000), complex(2000,-2000), complex(3000,-3000), complex(4000,-4000), complex(5000,-5000))
+ expected_result = (complex(0,0), complex(125,-125), complex(359.375,-359.375), complex(689.453125,-689.453125), complex(1103.271484,-1103.271484), complex(1590.36255,-1590.36255))
+ src = gr.vector_source_c(src_data)
+ op = gr.single_pole_iir_filter_cc (0.125)
+ dst = gr.vector_sink_c()
+ self.fg.connect (src, op, dst)
+ self.fg.run()
+ result_data = dst.data()
+ self.assertComplexTuplesAlmostEqual (expected_result, result_data, 3)
+
+ def test_003(self):
+ block_size = 2
+ src_data = (complex(0,0), complex(1000,-1000), complex(2000,-2000), complex(3000,-3000), complex(4000,-4000), complex(5000,-5000))
+ expected_result = (complex(0,0), complex(125,-125), complex(250,-250), complex(484.375,-484.375), complex(718.75,-718.75), complex(1048.828125,-1048.828125))
+ src = gr.vector_source_c(src_data)
+ s2p = gr.serial_to_parallel(gr.sizeof_gr_complex, block_size)
+ op = gr.single_pole_iir_filter_cc (0.125, block_size)
+ p2s = gr.parallel_to_serial(gr.sizeof_gr_complex, block_size)
+ dst = gr.vector_sink_c()
+ self.fg.connect (src, s2p, op, p2s, dst)
+ self.fg.run()
+ result_data = dst.data()
+ self.assertComplexTuplesAlmostEqual (expected_result, result_data, 3)
+
+
+if __name__ == '__main__':
+ gr_unittest.main ()
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_unpack_k_bits.py b/gnuradio-core/src/python/gnuradio/gr/qa_unpack_k_bits.py
new file mode 100755
index 000000000..59f838a4d
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_unpack_k_bits.py
@@ -0,0 +1,57 @@
+#!/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, gr_unittest
+import random
+
+class test_unpack(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown(self):
+ self.fg = None
+
+ def test_001(self):
+ src_data = (1,0,1,1,0,1,1,0)
+ expected_results = (1,0,1,1,0,1,1,0)
+ src = gr.vector_source_b(src_data,False)
+ op = gr.unpack_k_bits_bb(1)
+ dst = gr.vector_sink_b()
+ self.fg.connect(src, op, dst)
+ self.fg.run()
+ self.assertEqual(expected_results, dst.data())
+
+ def test_002(self):
+ src_data = ( 2, 3, 0, 1)
+ expected_results = (1,0,1,1,0,0,0,1)
+ src = gr.vector_source_b(src_data,False)
+ op = gr.unpack_k_bits_bb(2)
+ dst = gr.vector_sink_b()
+ self.fg.connect(src, op, dst)
+ self.fg.run()
+ self.assertEqual(expected_results, dst.data())
+
+
+if __name__ == '__main__':
+ gr_unittest.main ()
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/run_tests.in b/gnuradio-core/src/python/gnuradio/gr/run_tests.in
new file mode 100755
index 000000000..87d0afdaf
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/run_tests.in
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+swigbld=@abs_top_builddir@/gnuradio-core/src/lib/swig
+swigsrc=@abs_top_srcdir@/gnuradio-core/src/lib/swig
+py=@abs_top_srcdir@/gnuradio-core/src/python
+
+PYTHONPATH="$swigbld:$swigbld/.libs:$swigsrc:$py"
+export PYTHONPATH
+
+# for OS/X
+DYLD_LIBRARY_PATH="@abs_top_builddir@/gnuradio-core/src/lib/.libs"
+export DYLD_LIBRARY_PATH
+
+# Don't load user or system prefs
+GR_DONT_LOAD_PREFS=1
+export GR_DONT_LOAD_PREFS
+
+ok=yes
+for file in @srcdir@/qa_*.py
+do
+ echo $file
+ if ! $file
+ then
+ ok=no
+ fi
+done
+
+if [ $ok = yes ]
+then
+ exit 0
+else
+ exit 1
+fi
diff --git a/gnuradio-core/src/python/gnuradio/gr/scheduler.py b/gnuradio-core/src/python/gnuradio/gr/scheduler.py
new file mode 100644
index 000000000..919d07f0a
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/scheduler.py
@@ -0,0 +1,70 @@
+#
+# 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.gr.exceptions import *
+from gnuradio_swig_python import single_threaded_scheduler, sts_pyrun
+import gr_threading as _threading
+#import threading as _threading
+
+class scheduler_thread(_threading.Thread):
+ def __init__(self, sts):
+ _threading.Thread.__init__(self)
+ self.sts = sts
+ def run(self):
+ # Invoke the single threaded scheduler's run method
+ #
+ # Note that we're in a new thread, and that sts_pyrun
+ # releases the global interpreter lock. This has the
+ # effect of evaluating the graph in parallel to the
+ # main line control code.
+ sts_pyrun(self.sts)
+ self.sts = None
+
+class scheduler(object):
+ def __init__(self, fg):
+ graphs = fg.partition_graph(fg.blocks)
+ # print "@@@ # graphs = %d" % (len(graphs))
+
+ self.state = []
+
+ for g in graphs:
+ list_of_blocks = [x.block() for x in g]
+ sts = single_threaded_scheduler(list_of_blocks)
+ thread = scheduler_thread(sts)
+ thread.setDaemon(1)
+ self.state.append((sts, thread))
+
+ def start(self):
+ for (sts, thread) in self.state:
+ thread.start()
+
+ def stop(self):
+ for (sts, thread) in self.state:
+ sts.stop()
+ self.wait()
+
+ def wait(self):
+ for (sts, thread) in self.state:
+ timeout = 0.100
+ while True:
+ thread.join(timeout)
+ if not thread.isAlive():
+ break
diff --git a/gnuradio-core/src/python/gnuradio/gr_unittest.py b/gnuradio-core/src/python/gnuradio/gr_unittest.py
new file mode 100755
index 000000000..a74a06153
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr_unittest.py
@@ -0,0 +1,114 @@
+#!/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.
+#
+
+import unittest
+import sys
+
+class TestCase(unittest.TestCase):
+ """A subclass of unittest.TestCase that adds additional assertions
+
+ Adds new methods assertComplexAlmostEqual,
+ assertComplexTuplesAlmostEqual and assertFloatTuplesAlmostEqual
+ """
+
+ def assertComplexAlmostEqual (self, first, second, places=7, msg=None):
+ """Fail if the two complex objects are unequal as determined by their
+ difference rounded to the given number of decimal places
+ (default 7) and comparing to zero.
+
+ Note that decimal places (from zero) is usually not the same
+ as significant digits (measured from the most signficant digit).
+ """
+ if round(second.real-first.real, places) != 0:
+ raise self.failureException, \
+ (msg or '%s != %s within %s places' % (`first`, `second`, `places` ))
+ if round(second.imag-first.imag, places) != 0:
+ raise self.failureException, \
+ (msg or '%s != %s within %s places' % (`first`, `second`, `places` ))
+
+ def assertComplexAlmostEqual2 (self, ref, x, abs_eps=1e-12, rel_eps=1e-6, msg=None):
+ """
+ Fail if the two complex objects are unequal as determined by...
+ """
+ if abs(ref - x) < abs_eps:
+ return
+
+ if abs(ref) > abs_eps:
+ if abs(ref-x)/abs(ref) > rel_eps:
+ raise self.failureException, \
+ (msg or '%s != %s rel_error = %s rel_limit = %s' % (
+ `ref`, `x`, abs(ref-x)/abs(ref), `rel_eps` ))
+ else:
+ raise self.failureException, \
+ (msg or '%s != %s rel_error = %s rel_limit = %s' % (
+ `ref`, `x`, abs(ref-x)/abs(ref), `rel_eps` ))
+
+
+
+ def assertComplexTuplesAlmostEqual (self, a, b, places=7, msg=None):
+ self.assertEqual (len(a), len(b))
+ for i in xrange (len(a)):
+ self.assertComplexAlmostEqual (a[i], b[i], places, msg)
+
+ def assertComplexTuplesAlmostEqual2 (self, ref, x,
+ abs_eps=1e-12, rel_eps=1e-6, msg=None):
+ self.assertEqual (len(ref), len(x))
+ for i in xrange (len(ref)):
+ try:
+ self.assertComplexAlmostEqual2 (ref[i], x[i], abs_eps, rel_eps, msg)
+ except self.failureException, e:
+ #sys.stderr.write("index = %d " % (i,))
+ #sys.stderr.write("%s\n" % (e,))
+ raise
+
+ def assertFloatTuplesAlmostEqual (self, a, b, places=7, msg=None):
+ self.assertEqual (len(a), len(b))
+ for i in xrange (len(a)):
+ self.assertAlmostEqual (a[i], b[i], places, msg)
+
+
+ def assertFloatTuplesAlmostEqual2 (self, ref, x,
+ abs_eps=1e-12, rel_eps=1e-6, msg=None):
+ self.assertEqual (len(ref), len(x))
+ for i in xrange (len(ref)):
+ try:
+ self.assertComplexAlmostEqual2 (ref[i], x[i], abs_eps, rel_eps, msg)
+ except self.failureException, e:
+ #sys.stderr.write("index = %d " % (i,))
+ #sys.stderr.write("%s\n" % (e,))
+ raise
+
+
+TestResult = unittest.TestResult
+TestSuite = unittest.TestSuite
+FunctionTestCase = unittest.FunctionTestCase
+TestLoader = unittest.TestLoader
+TextTestRunner = unittest.TextTestRunner
+TestProgram = unittest.TestProgram
+main = TestProgram
+
+##############################################################################
+# Executing this module from the command line
+##############################################################################
+
+if __name__ == "__main__":
+ main(module=None)
diff --git a/gnuradio-core/src/python/gnuradio/gru/Makefile.am b/gnuradio-core/src/python/gnuradio/gru/Makefile.am
new file mode 100644
index 000000000..44b52b6c8
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gru/Makefile.am
@@ -0,0 +1,35 @@
+#
+# 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.
+#
+
+include $(top_srcdir)/Makefile.common
+
+# EXTRA_DIST = run_tests.in
+# TESTS = run_tests
+
+grblkspythondir = $(grpythondir)/gru
+
+grblkspython_PYTHON = \
+ __init__.py
+
+
+noinst_PYTHON =
+
+CLEANFILES = *.pyc *.pyo
diff --git a/gnuradio-core/src/python/gnuradio/gru/__init__.py b/gnuradio-core/src/python/gnuradio/gru/__init__.py
new file mode 100644
index 000000000..272c7a5f8
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gru/__init__.py
@@ -0,0 +1,37 @@
+#
+# 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 glob
+import os.path
+
+# Semi-hideous kludge to import everything in the gruimpl directory
+# into the gnuradio.gru namespace. This keeps us from having to remember
+# to manually update this file.
+
+for p in __path__:
+ filenames = glob.glob (os.path.join (p, "..", "gruimpl", "*.py"))
+ for f in filenames:
+ f = os.path.basename(f).lower()
+ f = f[:-3]
+ if f == '__init__':
+ continue
+ # print f
+ exec "from gnuradio.gruimpl.%s import *" % (f,)
diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/Makefile.am b/gnuradio-core/src/python/gnuradio/gruimpl/Makefile.am
new file mode 100644
index 000000000..06eb7a1f9
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gruimpl/Makefile.am
@@ -0,0 +1,40 @@
+#
+# 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.
+#
+
+include $(top_srcdir)/Makefile.common
+
+grupythondir = $(grpythondir)/gruimpl
+
+grupython_PYTHON = \
+ __init__.py \
+ crc.py \
+ freqz.py \
+ gnuplot_freqz.py \
+ hexint.py \
+ listmisc.py \
+ mathmisc.py \
+ lmx2306.py \
+ os_read_exactly.py \
+ sdr_1000.py \
+ seq_with_cursor.py \
+ socket_stuff.py
+
+CLEANFILES = *.pyc
diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/__init__.py b/gnuradio-core/src/python/gnuradio/gruimpl/__init__.py
new file mode 100644
index 000000000..a4917cf64
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gruimpl/__init__.py
@@ -0,0 +1 @@
+# make this a package
diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/crc.py b/gnuradio-core/src/python/gnuradio/gruimpl/crc.py
new file mode 100644
index 000000000..6a97c81b5
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gruimpl/crc.py
@@ -0,0 +1,36 @@
+#
+# 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
+from hexint import *
+import struct
+
+def gen_and_append_crc32(s):
+ crc = gr.crc32(s)
+ return s + struct.pack(">I", hexint(crc))
+
+def check_crc32(s):
+ msg = s[:-4]
+ #print "msg = '%s'" % (msg,)
+ actual = gr.crc32(msg)
+ (expected,) = struct.unpack(">I", s[-4:])
+ # print "actual =", hex(actual), "expected =", hex(expected)
+ return (actual == expected, msg)
diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/freqz.py b/gnuradio-core/src/python/gnuradio/gruimpl/freqz.py
new file mode 100644
index 000000000..21704944c
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gruimpl/freqz.py
@@ -0,0 +1,344 @@
+#!/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.
+#
+
+# This code lifted from various parts of www.scipy.org -eb 2005-01-24
+
+# Copyright (c) 2001, 2002 Enthought, Inc.
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# a. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# b. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# c. Neither the name of the Enthought nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+# DAMAGE.
+#
+
+__all__ = ['freqz']
+
+import Numeric
+from Numeric import *
+Num=Numeric
+
+def atleast_1d(*arys):
+ """ Force a sequence of arrays to each be at least 1D.
+
+ Description:
+ Force an array to be at least 1D. If an array is 0D, the
+ array is converted to a single row of values. Otherwise,
+ the array is unaltered.
+ Arguments:
+ *arys -- arrays to be converted to 1 or more dimensional array.
+ Returns:
+ input array converted to at least 1D array.
+ """
+ res = []
+ for ary in arys:
+ ary = asarray(ary)
+ if len(ary.shape) == 0:
+ result = Numeric.array([ary[0]])
+ else:
+ result = ary
+ res.append(result)
+ if len(res) == 1:
+ return res[0]
+ else:
+ return res
+
+
+def polyval(p,x):
+ """Evaluate the polynomial p at x. If x is a polynomial then composition.
+
+ Description:
+
+ If p is of length N, this function returns the value:
+ p[0]*(x**N-1) + p[1]*(x**N-2) + ... + p[N-2]*x + p[N-1]
+
+ x can be a sequence and p(x) will be returned for all elements of x.
+ or x can be another polynomial and the composite polynomial p(x) will be
+ returned.
+ """
+ p = asarray(p)
+ if isinstance(x,poly1d):
+ y = 0
+ else:
+ x = asarray(x)
+ y = Numeric.zeros(x.shape,x.typecode())
+ for i in range(len(p)):
+ y = x * y + p[i]
+ return y
+
+class poly1d:
+ """A one-dimensional polynomial class.
+
+ p = poly1d([1,2,3]) constructs the polynomial x**2 + 2 x + 3
+
+ p(0.5) evaluates the polynomial at the location
+ p.r is a list of roots
+ p.c is the coefficient array [1,2,3]
+ p.order is the polynomial order (after leading zeros in p.c are removed)
+ p[k] is the coefficient on the kth power of x (backwards from
+ sequencing the coefficient array.
+
+ polynomials can be added, substracted, multplied and divided (returns
+ quotient and remainder).
+ asarray(p) will also give the coefficient array, so polynomials can
+ be used in all functions that accept arrays.
+ """
+ def __init__(self, c_or_r, r=0):
+ if isinstance(c_or_r,poly1d):
+ for key in c_or_r.__dict__.keys():
+ self.__dict__[key] = c_or_r.__dict__[key]
+ return
+ if r:
+ c_or_r = poly(c_or_r)
+ c_or_r = atleast_1d(c_or_r)
+ if len(c_or_r.shape) > 1:
+ raise ValueError, "Polynomial must be 1d only."
+ c_or_r = trim_zeros(c_or_r, trim='f')
+ if len(c_or_r) == 0:
+ c_or_r = Numeric.array([0])
+ self.__dict__['coeffs'] = c_or_r
+ self.__dict__['order'] = len(c_or_r) - 1
+
+ def __array__(self,t=None):
+ if t:
+ return asarray(self.coeffs,t)
+ else:
+ return asarray(self.coeffs)
+
+ def __coerce__(self,other):
+ return None
+
+ def __repr__(self):
+ vals = repr(self.coeffs)
+ vals = vals[6:-1]
+ return "poly1d(%s)" % vals
+
+ def __len__(self):
+ return self.order
+
+ def __str__(self):
+ N = self.order
+ thestr = "0"
+ for k in range(len(self.coeffs)):
+ coefstr ='%.4g' % abs(self.coeffs[k])
+ if coefstr[-4:] == '0000':
+ coefstr = coefstr[:-5]
+ power = (N-k)
+ if power == 0:
+ if coefstr != '0':
+ newstr = '%s' % (coefstr,)
+ else:
+ if k == 0:
+ newstr = '0'
+ else:
+ newstr = ''
+ elif power == 1:
+ if coefstr == '0':
+ newstr = ''
+ elif coefstr == '1':
+ newstr = 'x'
+ else:
+ newstr = '%s x' % (coefstr,)
+ else:
+ if coefstr == '0':
+ newstr = ''
+ elif coefstr == '1':
+ newstr = 'x**%d' % (power,)
+ else:
+ newstr = '%s x**%d' % (coefstr, power)
+
+ if k > 0:
+ if newstr != '':
+ if self.coeffs[k] < 0:
+ thestr = "%s - %s" % (thestr, newstr)
+ else:
+ thestr = "%s + %s" % (thestr, newstr)
+ elif (k == 0) and (newstr != '') and (self.coeffs[k] < 0):
+ thestr = "-%s" % (newstr,)
+ else:
+ thestr = newstr
+ return _raise_power(thestr)
+
+
+ def __call__(self, val):
+ return polyval(self.coeffs, val)
+
+ def __mul__(self, other):
+ if isscalar(other):
+ return poly1d(self.coeffs * other)
+ else:
+ other = poly1d(other)
+ return poly1d(polymul(self.coeffs, other.coeffs))
+
+ def __rmul__(self, other):
+ if isscalar(other):
+ return poly1d(other * self.coeffs)
+ else:
+ other = poly1d(other)
+ return poly1d(polymul(self.coeffs, other.coeffs))
+
+ def __add__(self, other):
+ other = poly1d(other)
+ return poly1d(polyadd(self.coeffs, other.coeffs))
+
+ def __radd__(self, other):
+ other = poly1d(other)
+ return poly1d(polyadd(self.coeffs, other.coeffs))
+
+ def __pow__(self, val):
+ if not isscalar(val) or int(val) != val or val < 0:
+ raise ValueError, "Power to non-negative integers only."
+ res = [1]
+ for k in range(val):
+ res = polymul(self.coeffs, res)
+ return poly1d(res)
+
+ def __sub__(self, other):
+ other = poly1d(other)
+ return poly1d(polysub(self.coeffs, other.coeffs))
+
+ def __rsub__(self, other):
+ other = poly1d(other)
+ return poly1d(polysub(other.coeffs, self.coeffs))
+
+ def __div__(self, other):
+ if isscalar(other):
+ return poly1d(self.coeffs/other)
+ else:
+ other = poly1d(other)
+ return map(poly1d,polydiv(self.coeffs, other.coeffs))
+
+ def __rdiv__(self, other):
+ if isscalar(other):
+ return poly1d(other/self.coeffs)
+ else:
+ other = poly1d(other)
+ return map(poly1d,polydiv(other.coeffs, self.coeffs))
+
+ def __setattr__(self, key, val):
+ raise ValueError, "Attributes cannot be changed this way."
+
+ def __getattr__(self, key):
+ if key in ['r','roots']:
+ return roots(self.coeffs)
+ elif key in ['c','coef','coefficients']:
+ return self.coeffs
+ elif key in ['o']:
+ return self.order
+ else:
+ return self.__dict__[key]
+
+ def __getitem__(self, val):
+ ind = self.order - val
+ if val > self.order:
+ return 0
+ if val < 0:
+ return 0
+ return self.coeffs[ind]
+
+ def __setitem__(self, key, val):
+ ind = self.order - key
+ if key < 0:
+ raise ValueError, "Does not support negative powers."
+ if key > self.order:
+ zr = Numeric.zeros(key-self.order,self.coeffs.typecode())
+ self.__dict__['coeffs'] = Numeric.concatenate((zr,self.coeffs))
+ self.__dict__['order'] = key
+ ind = 0
+ self.__dict__['coeffs'][ind] = val
+ return
+
+ def integ(self, m=1, k=0):
+ return poly1d(polyint(self.coeffs,m=m,k=k))
+
+ def deriv(self, m=1):
+ return poly1d(polyder(self.coeffs,m=m))
+
+def freqz(b, a, worN=None, whole=0, plot=None):
+ """Compute frequency response of a digital filter.
+
+ Description:
+
+ Given the numerator (b) and denominator (a) of a digital filter compute
+ its frequency response.
+
+ jw -jw -jmw
+ jw B(e) b[0] + b[1]e + .... + b[m]e
+ H(e) = ---- = ------------------------------------
+ jw -jw -jnw
+ A(e) a[0] + a[2]e + .... + a[n]e
+
+ Inputs:
+
+ b, a --- the numerator and denominator of a linear filter.
+ worN --- If None, then compute at 512 frequencies around the unit circle.
+ If a single integer, the compute at that many frequencies.
+ Otherwise, compute the response at frequencies given in worN
+ whole -- Normally, frequencies are computed from 0 to pi (upper-half of
+ unit-circle. If whole is non-zero compute frequencies from 0
+ to 2*pi.
+
+ Outputs: (h,w)
+
+ h -- The frequency response.
+ w -- The frequencies at which h was computed.
+ """
+ b, a = map(atleast_1d, (b,a))
+ if whole:
+ lastpoint = 2*pi
+ else:
+ lastpoint = pi
+ if worN is None:
+ N = 512
+ w = Num.arange(0,lastpoint,lastpoint/N)
+ elif isinstance(worN, types.IntType):
+ N = worN
+ w = Num.arange(0,lastpoint,lastpoint/N)
+ else:
+ w = worN
+ w = atleast_1d(w)
+ zm1 = exp(-1j*w)
+ h = polyval(b[::-1], zm1) / polyval(a[::-1], zm1)
+ # if not plot is None:
+ # plot(w, h)
+ return h, w
diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/gnuplot_freqz.py b/gnuradio-core/src/python/gnuradio/gruimpl/gnuplot_freqz.py
new file mode 100755
index 000000000..5a117605a
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gruimpl/gnuplot_freqz.py
@@ -0,0 +1,82 @@
+#!/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__ = ['gnuplot_freqz']
+
+import tempfile
+import os
+import math
+import Numeric
+
+from gnuradio import gr
+from gnuradio.gruimpl.freqz import freqz
+
+
+def gnuplot_freqz (hw, Fs=None, logfreq=False):
+
+ """hw is a tuple of the form (h, w) where h is sequence of complex
+ freq responses, and w is a sequence of corresponding frequency
+ points. Plot the frequency response using gnuplot. If Fs is
+ provide, use it as the sampling frequency, else use 2*pi.
+
+ Returns a handle to the gnuplot graph. When the handle is reclaimed
+ the graph is torn down."""
+
+ data_file = tempfile.NamedTemporaryFile ()
+ cmd_file = os.popen ('gnuplot', 'w')
+
+ h, w = hw
+ ampl = 20 * Numeric.log10 (Numeric.absolute (h) + 1e-9)
+ phase = map (lambda x: math.atan2 (x.imag, x.real), h)
+
+ if Fs:
+ w *= (Fs/(2*math.pi))
+
+ for freq, a, ph in zip (w, ampl, phase):
+ data_file.write ("%g\t%g\t%g\n" % (freq, a, ph))
+
+ data_file.flush ()
+
+ cmd_file.write ("set grid\n")
+ if logfreq:
+ cmd_file.write ("set logscale x\n")
+ else:
+ cmd_file.write ("unset logscale x\n")
+ cmd_file.write ("plot '%s' using 1:2 with lines\n" % (data_file.name,))
+ cmd_file.flush ()
+
+ return (cmd_file, data_file)
+
+
+def test_plot ():
+ sample_rate = 2.0e6
+ taps = gr.firdes.low_pass (1.0, # gain
+ sample_rate, # sampling rate
+ 200e3, # low pass cutoff freq
+ 100e3, # width of trans. band
+ gr.firdes.WIN_HAMMING)
+ # print len (taps)
+ return gnuplot_freqz (freqz (taps, 1), sample_rate)
+
+if __name__ == '__main__':
+ handle = test_plot ()
+ raw_input ('Press Enter to continue: ')
diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/hexint.py b/gnuradio-core/src/python/gnuradio/gruimpl/hexint.py
new file mode 100644
index 000000000..1220755cb
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gruimpl/hexint.py
@@ -0,0 +1,32 @@
+#
+# 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.
+#
+
+def hexint(mask):
+ """
+ Convert unsigned masks into signed ints.
+
+ This allows us to use hex constants like 0xf0f0f0f2 when talking to
+ our hardware and not get screwed by them getting treated as python
+ longs.
+ """
+ if mask >= 2**31:
+ return int(mask-2**32)
+ return mask
diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/listmisc.py b/gnuradio-core/src/python/gnuradio/gruimpl/listmisc.py
new file mode 100644
index 000000000..857e417f2
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gruimpl/listmisc.py
@@ -0,0 +1,29 @@
+#
+# 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.
+#
+
+def list_reverse(x):
+ """
+ Return a copy of x that is reverse order.
+ """
+ r = list(x)
+ r.reverse()
+ return r
+
diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/lmx2306.py b/gnuradio-core/src/python/gnuradio/gruimpl/lmx2306.py
new file mode 100755
index 000000000..b46c896f7
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gruimpl/lmx2306.py
@@ -0,0 +1,186 @@
+#!/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.
+#
+
+'''Control National LMX2306 based frequency synthesizer'''
+
+from gnuradio import gr
+from gnuradio import eng_notation
+from gnuradio.eng_option import eng_option
+from optparse import OptionParser
+
+# bottom two bits of 21 bit word select which register to program
+
+R_REG = 0x0
+AB_REG = 0x1
+F_REG = 0x2
+
+F_counter_reset = (1 << 2)
+F_phase_detector_polarity = (1 << 7)
+
+F_LD_tri_state = (0 << 4)
+F_LD_R_divider_output = (4 << 4)
+F_LD_N_divider_output = (2 << 4)
+F_LD_serial_data_output = (6 << 4)
+F_LD_digital_lock_detect = (1 << 4)
+F_LD_open_drain = (5 << 4)
+F_LD_high = (3 << 4)
+F_LD_low = (7 << 4)
+
+# F_default = F_LD_digital_lock_detect | F_phase_detector_polarity
+F_default = F_LD_open_drain | F_phase_detector_polarity
+
+#
+# 4 control pins:
+# CE always high
+# LE load enable. When LE goes high, data stored in the shift register
+# is loaded into one of the three registers
+# CLK data is clocked in on the rising edge
+# DATA single data bit. Entered MSB first
+
+DB_CLK = (1 << 0)
+DB_DATA = (1 << 1)
+DB_LE = (1 << 2)
+DB_CE = (1 << 3)
+
+class lmx2306 (object):
+ '''Control the National LMX2306 PLL'''
+ __slots__ = ['pp', 'shadow', 'fosc', 'r', 'step_size', 'verbose']
+ def __init__ (self, fosc, step_size, which_pp = 0):
+ '''FOSC is the frequency of the reference oscillator,
+ STEP_SIZE is the step between valid frequencies,
+ WHICH_PP specifies which parallel port to use
+ '''
+ self.pp = gr.make_ppio (which_pp)
+ self.shadow = DB_CE
+ self.pp.lock ()
+ self.pp.write_data (self.shadow)
+ self.pp.unlock ()
+ self.verbose = False
+ self._set_fosc (fosc)
+ self._set_step (step_size)
+
+
+ def program (self, r, a, b):
+ if self.verbose:
+ print "lmx2306: r = %d a = %d b = %d" % (r, a, b)
+ self.pp.lock ()
+ self._write_word (F_REG | F_default | F_counter_reset)
+ self._write_word (R_REG | ((r & 0x3fff) << 2))
+ self._write_word (AB_REG | ((a & 0x1f) << 2) | ((b & 0x1fff) << 7))
+ self._write_word (F_REG | F_default)
+ self.pp.unlock ()
+
+ def set_freq (self, freq):
+ '''Set the PLL frequency to FREQ
+
+ Return the actual freq value set. It will be rounded down to a
+ multiple of step_size
+ '''
+ divisor = int (freq / self.step_size)
+ actual = divisor * self.step_size
+ (a, b) = self._compute_ab (divisor)
+ self.program (self.r, a, b)
+ return actual
+
+ # ----------------------------------------------------------------
+
+ def _set_fosc (self, ref_oscillator_freq):
+ self.fosc = ref_oscillator_freq
+
+ def _set_step (self, step_size):
+ r = int (self.fosc / step_size)
+ if r * step_size != self.fosc:
+ raise ValueError, "step_size is not a factor of self.fosc"
+ if r < 3 or r > 16383:
+ raise ValueError, "r is out of range"
+ self.r = r
+ self.step_size = step_size
+
+ def _compute_ab (self, divisor):
+ b = divisor / 8
+ a = divisor - (b * 8)
+ if b < 3 or b > 8191 or a > b:
+ raise ValueError, "Invalid divisor"
+ return (a, b)
+
+ def _write_word (self, w):
+ for i in range(21):
+ if w & (1 << 20):
+ self._set_DATA_1 ()
+ else:
+ self._set_DATA_0 ()
+ w = (w << 1) & 0x0ffffff
+ self._set_CLK_1 ()
+ self._set_CLK_0 ()
+ self._set_LE_1 ()
+ self._set_LE_0 ()
+
+ def _set_LE_0 (self):
+ self.shadow = self.shadow & ~DB_LE
+ self.pp.write_data (self.shadow)
+
+ def _set_LE_1 (self):
+ self.shadow = self.shadow | DB_LE
+ self.pp.write_data (self.shadow)
+
+ def _set_CLK_0 (self):
+ self.shadow = self.shadow & ~DB_CLK
+ self.pp.write_data (self.shadow)
+
+ def _set_CLK_1 (self):
+ self.shadow = self.shadow | DB_CLK
+ self.pp.write_data (self.shadow)
+
+ def _set_DATA_0 (self):
+ self.shadow = self.shadow & ~DB_DATA
+ self.pp.write_data (self.shadow)
+
+ def _set_DATA_1 (self):
+ self.shadow = self.shadow | DB_DATA
+ self.pp.write_data (self.shadow)
+
+if __name__ == '__main__':
+ parser = OptionParser (option_class=eng_option)
+ parser.add_option ("-o", "--fosc", type="eng_float", default=32e6,
+ help="set reference oscillator freq to FREQ", metavar="FREQ")
+ parser.add_option ("-s", "--step-size", type="eng_float", default=10e3,
+ help="set the frequency step size to STEP_SIZE")
+ parser.add_option ("-f", "--freq", type="eng_float", default=430e6,
+ help="set VCO frequency to FREQ")
+ parser.add_option ("-v", "--verbose", action="store_true", default=False)
+ (options, args) = parser.parse_args ()
+
+ if options.verbose:
+ print "fosc = %s step = %s fvco = %s" % (
+ eng_notation.num_to_str (options.fosc),
+ eng_notation.num_to_str (options.step_size),
+ eng_notation.num_to_str (options.freq))
+
+ lmx = lmx2306 (options.fosc, options.step_size)
+ lmx.verbose = options.verbose
+
+ actual = lmx.set_freq (options.freq)
+
+ if options.verbose:
+ print "fvco_actual = %s delta = %s" % (
+ eng_notation.num_to_str (actual),
+ eng_notation.num_to_str (options.freq - actual))
diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/mathmisc.py b/gnuradio-core/src/python/gnuradio/gruimpl/mathmisc.py
new file mode 100644
index 000000000..13e6de80e
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gruimpl/mathmisc.py
@@ -0,0 +1,33 @@
+#
+# 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
+
+def gcd(a,b):
+ while b:
+ a,b = b, a % b
+ return a
+
+def lcm(a,b):
+ return a * b / gcd(a, b)
+
+def log2(x):
+ return math.log(x)/math.log(2)
diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/os_read_exactly.py b/gnuradio-core/src/python/gnuradio/gruimpl/os_read_exactly.py
new file mode 100644
index 000000000..afdfb514b
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gruimpl/os_read_exactly.py
@@ -0,0 +1,36 @@
+#
+# 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 os
+
+def os_read_exactly(file_descriptor, nbytes):
+ """
+ Replacement for os.read that blocks until it reads exactly nbytes.
+
+ """
+ s = ''
+ while nbytes > 0:
+ sbuf = os.read(file_descriptor, nbytes)
+ if not(sbuf):
+ return ''
+ nbytes -= len(sbuf)
+ s = s + sbuf
+ return s
diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/sdr_1000.py b/gnuradio-core/src/python/gnuradio/gruimpl/sdr_1000.py
new file mode 100644
index 000000000..5de23b720
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gruimpl/sdr_1000.py
@@ -0,0 +1,84 @@
+#
+# Copyright 2003,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
+
+class sdr_1000 (gr.sdr_1000_base):
+ "Control the DDS on the SDR-1000"
+ def __init__(self, pport = 0):
+ gr.sdr_1000_base.__init__(self, pport)
+ self.write_latch (3, 0x00, 0xC0) # Reset low, WRS/ low
+ self.write_reg (0x20, 0x40)
+
+ def write_reg(self, addr, data):
+ self.write_latch (3, addr & 0x3f, 0x3f)
+ self.write_latch (2, data, 0xff)
+ self.write_latch (3, 0x40, 0x40)
+ self.write_latch (3, 0x00, 0x40)
+
+ def set_freq(self, freq):
+ self.set_band (freq)
+ ftw = freq / 200e6;
+ for i in xrange(6):
+ word = int(ftw * 256)
+ ftw = ftw*256 - word
+ # print (('%d [%02x]') % (i, word))
+ self.write_reg (4+i, word)
+
+ def set_band (self, freq):
+ if freq <= 2.25e6:
+ band = 0
+ elif freq <= 5.5e6:
+ band = 1
+ elif freq <= 11e6:
+ band = 3 # due to wiring mistake on board
+ elif freq <= 22e6:
+ band = 2 # due to wiring mistake on board
+ elif freq <= 37.5e6:
+ band = 4
+ else:
+ band = 5
+
+ self.write_latch (1, 1 << band, 0x3f)
+
+ def set_bit (self, reg, bit, state):
+ val = 0x00
+ if state: val = 1<<bit
+ self.write_latch (reg, val, 1<<bit)
+
+ def set_tx (self, on = 1):
+ self.set_bit(1, 6, on)
+
+ def set_rx (self):
+ self.set_bit(1, 6, 0)
+
+ def set_gain (self, high):
+ self.set_bit(0, 7, high)
+
+ def set_mute (self, mute = 1):
+ self.set_bit(1, 7, mute)
+
+ def set_unmute (self):
+ self.set_bit(1, 7, 0)
+
+ def set_external_pin (self, pin, on = 1):
+ assert (pin < 8 and pin > 0), "Out of range 1..7"
+ self.set_bit(0, pin-1, on)
diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/seq_with_cursor.py b/gnuradio-core/src/python/gnuradio/gruimpl/seq_with_cursor.py
new file mode 100644
index 000000000..5616dea9d
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gruimpl/seq_with_cursor.py
@@ -0,0 +1,77 @@
+#
+# Copyright 2003,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.
+#
+
+# misc utilities
+
+import types
+import exceptions
+
+class seq_with_cursor (object):
+ __slots__ = [ 'items', 'index' ]
+
+ def __init__ (self, items, initial_index = None, initial_value = None):
+ assert len (items) > 0, "seq_with_cursor: len (items) == 0"
+ self.items = items
+ self.set_index (initial_index)
+ if initial_value is not None:
+ self.set_index_by_value(initial_value)
+
+ def set_index (self, initial_index):
+ if initial_index is None:
+ self.index = len (self.items) / 2
+ elif initial_index >= 0 and initial_index < len (self.items):
+ self.index = initial_index
+ else:
+ raise exceptions.ValueError
+
+ def set_index_by_value(self, v):
+ """
+ Set index to the smallest value such that items[index] >= v.
+ If there is no such item, set index to the maximum value.
+ """
+ self.set_index(0) # side effect!
+ cv = self.current()
+ more = True
+ while cv < v and more:
+ cv, more = self.next() # side effect!
+
+ def next (self):
+ new_index = self.index + 1
+ if new_index < len (self.items):
+ self.index = new_index
+ return self.items[new_index], True
+ else:
+ return self.items[self.index], False
+
+ def prev (self):
+ new_index = self.index - 1
+ if new_index >= 0:
+ self.index = new_index
+ return self.items[new_index], True
+ else:
+ return self.items[self.index], False
+
+ def current (self):
+ return self.items[self.index]
+
+ def get_seq (self):
+ return self.items[:] # copy of items
+
diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/socket_stuff.py b/gnuradio-core/src/python/gnuradio/gruimpl/socket_stuff.py
new file mode 100644
index 000000000..05be0d432
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gruimpl/socket_stuff.py
@@ -0,0 +1,56 @@
+#
+# 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.
+#
+
+# random socket related stuff
+
+import socket
+import os
+import sys
+
+def tcp_connect_or_die(sock_addr):
+ """
+ @param sock_addr: (host, port) to connect to
+ @type sock_addr: tuple
+ @returns: socket or exits
+ """
+ s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
+ try:
+ s.connect(sock_addr)
+ except socket.error, err:
+ sys.stderr.write('Failed to connect to %s: %s\n' %
+ (sock_addr, os.strerror (err.args[0]),))
+ sys.exit(1)
+ return s
+
+def udp_connect_or_die(sock_addr):
+ """
+ @param sock_addr: (host, port) to connect to
+ @type sock_addr: tuple
+ @returns: socket or exits
+ """
+ s = socket.socket (socket.AF_INET, socket.SOCK_DGRAM)
+ try:
+ s.connect(sock_addr)
+ except socket.error, err:
+ sys.stderr.write('Failed to connect to %s: %s\n' %
+ (sock_addr, os.strerror (err.args[0]),))
+ sys.exit(1)
+ return s
diff --git a/gnuradio-core/src/python/gnuradio/optfir.py b/gnuradio-core/src/python/gnuradio/optfir.py
new file mode 100644
index 000000000..eb3f321be
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/optfir.py
@@ -0,0 +1,242 @@
+#
+# 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.
+#
+
+'''
+Routines for designing optimal FIR filters.
+
+For a great intro to how all this stuff works, see section 6.6 of
+"Digital Signal Processing: A Practical Approach", Emmanuael C. Ifeachor
+and Barrie W. Jervis, Adison-Wesley, 1993. ISBN 0-201-54413-X.
+'''
+
+import math
+from gnuradio import gr
+
+remez = gr.remez
+
+# ----------------------------------------------------------------
+
+def low_pass (gain, Fs, freq1, freq2, passband_ripple_db, stopband_atten_db,
+ nextra_taps=0):
+ passband_dev = passband_ripple_to_dev (passband_ripple_db)
+ stopband_dev = stopband_atten_to_dev (stopband_atten_db)
+ desired_ampls = (gain, 0)
+ (n, fo, ao, w) = remezord ([freq1, freq2], desired_ampls,
+ [passband_dev, stopband_dev], Fs)
+ taps = gr.remez (n + nextra_taps, fo, ao, w, "bandpass")
+ return taps
+
+# FIXME high_passs is broken...
+def high_pass (Fs, freq1, freq2, stopband_atten_db, passband_ripple_db,
+ nextra_taps=0):
+ """FIXME: broken"""
+ passband_dev = passband_ripple_to_dev (passband_ripple_db)
+ stopband_dev = stopband_atten_to_dev (stopband_atten_db)
+ desired_ampls = (0, 1)
+ (n, fo, ao, w) = remezord ([freq1, freq2], desired_ampls,
+ [stopband_dev, passband_dev], Fs)
+ taps = gr.remez (n + nextra_taps, fo, ao, w, "bandpass")
+ return taps
+
+# ----------------------------------------------------------------
+
+def stopband_atten_to_dev (atten_db):
+ """Convert a stopband attenuation in dB to an absolute value"""
+ return 10**(-atten_db/20)
+
+def passband_ripple_to_dev (ripple_db):
+ """Convert passband ripple spec expressed in dB to an absolute value"""
+ return (10**(ripple_db/20)-1)/(10**(ripple_db/20)+1)
+
+# ----------------------------------------------------------------
+
+def remezord (fcuts, mags, devs, fsamp = 2):
+ '''
+ FIR order estimator (lowpass, highpass, bandpass, mulitiband).
+
+ (n, fo, ao, w) = remezord (f, a, dev)
+ (n, fo, ao, w) = remezord (f, a, dev, fs)
+
+ (n, fo, ao, w) = remezord (f, a, dev) finds the approximate order,
+ normalized frequency band edges, frequency band amplitudes, and
+ weights that meet input specifications f, a, and dev, to use with
+ the remez command.
+
+ * f is a sequence of frequency band edges (between 0 and Fs/2, where
+ Fs is the sampling frequency), and a is a sequence specifying the
+ desired amplitude on the bands defined by f. The length of f is
+ twice the length of a, minus 2. The desired function is
+ piecewise constant.
+
+ * dev is a sequence the same size as a that specifies the maximum
+ allowable deviation or ripples between the frequency response
+ and the desired amplitude of the output filter, for each band.
+
+ Use remez with the resulting order n, frequency sequence fo,
+ amplitude response sequence ao, and weights w to design the filter b
+ which approximately meets the specifications given by remezord
+ input parameters f, a, and dev:
+
+ b = remez (n, fo, ao, w)
+
+ (n, fo, ao, w) = remezord (f, a, dev, Fs) specifies a sampling frequency Fs.
+
+ Fs defaults to 2 Hz, implying a Nyquist frequency of 1 Hz. You can
+ therefore specify band edges scaled to a particular applications
+ sampling frequency.
+
+ In some cases remezord underestimates the order n. If the filter
+ does not meet the specifications, try a higher order such as n+1
+ or n+2.
+ '''
+ # get local copies
+ fcuts = fcuts[:]
+ mags = mags[:]
+ devs = devs[:]
+
+ for i in range (len (fcuts)):
+ fcuts[i] = float (fcuts[i]) / fsamp
+
+ nf = len (fcuts)
+ nm = len (mags)
+ nd = len (devs)
+ nbands = nm
+
+ if nm != nd:
+ raise ValueError, "Length of mags and devs must be equal"
+
+ if nf != 2 * (nbands - 1):
+ raise ValueError, "Length of f must be 2 * len (mags) - 2"
+
+ for i in range (len (mags)):
+ if mags[i] != 0: # if not stopband, get relative deviation
+ devs[i] = devs[i] / mags[i]
+
+ # separate the passband and stopband edges
+ f1 = fcuts[0::2]
+ f2 = fcuts[1::2]
+
+ n = 0
+ min_delta = 2
+ for i in range (len (f1)):
+ if f2[i] - f1[i] < min_delta:
+ n = i
+ min_delta = f2[i] - f1[i]
+
+ if nbands == 2:
+ # lowpass or highpass case (use formula)
+ l = lporder (f1[n], f2[n], devs[0], devs[1])
+ else:
+ # bandpass or multipass case
+ # try different lowpasses and take the worst one that
+ # goes through the BP specs
+ l = 0
+ for i in range (1, nbands-1):
+ l1 = lporder (f1[i-1], f2[i-1], devs[i], devs[i-1])
+ l2 = lporder (f1[i], f2[i], devs[i], devs[i+1])
+ l = max (l, l1, l2)
+
+ n = int (math.ceil (l)) - 1 # need order, not length for remez
+
+ # cook up remez compatible result
+ ff = [0] + fcuts + [1]
+ for i in range (1, len (ff) - 1):
+ ff[i] *= 2
+
+ aa = []
+ for a in mags:
+ aa = aa + [a, a]
+
+ max_dev = max (devs)
+ wts = [1] * len(devs)
+ for i in range (len (wts)):
+ wts[i] = max_dev / devs[i]
+
+ return (n, ff, aa, wts)
+
+# ----------------------------------------------------------------
+
+def lporder (freq1, freq2, delta_p, delta_s):
+ '''
+ FIR lowpass filter length estimator. freq1 and freq2 are
+ normalized to the sampling frequency. delta_p is the passband
+ deviation (ripple), delta_s is the stopband deviation (ripple).
+
+ Note, this works for high pass filters too (freq1 > freq2), but
+ doesnt work well if the transition is near f == 0 or f == fs/2
+
+ From Herrmann et al (1973), Practical design rules for optimum
+ finite impulse response filters. Bell System Technical J., 52, 769-99
+ '''
+ df = abs (freq2 - freq1)
+ ddp = math.log10 (delta_p)
+ dds = math.log10 (delta_s)
+
+ a1 = 5.309e-3
+ a2 = 7.114e-2
+ a3 = -4.761e-1
+ a4 = -2.66e-3
+ a5 = -5.941e-1
+ a6 = -4.278e-1
+
+ b1 = 11.01217
+ b2 = 0.5124401
+
+ t1 = a1 * ddp * ddp
+ t2 = a2 * ddp
+ t3 = a4 * ddp * ddp
+ t4 = a5 * ddp
+
+ dinf=((t1 + t2 + a3) * dds) + (t3 + t4 + a6)
+ ff = b1 + b2 * (ddp - dds)
+ n = dinf / df - ff * df + 1
+ return n
+
+
+def bporder (freq1, freq2, delta_p, delta_s):
+ '''
+ FIR bandpass filter length estimator. freq1 and freq2 are
+ normalized to the sampling frequency. delta_p is the passband
+ deviation (ripple), delta_s is the stopband deviation (ripple).
+
+ From Mintzer and Liu (1979)
+ '''
+ df = abs (freq2 - freq1)
+ ddp = math.log10 (delta_p)
+ dds = math.log10 (delta_s)
+
+ a1 = 0.01201
+ a2 = 0.09664
+ a3 = -0.51325
+ a4 = 0.00203
+ a5 = -0.57054
+ a6 = -0.44314
+
+ t1 = a1 * ddp * ddp
+ t2 = a2 * ddp
+ t3 = a4 * ddp * ddp
+ t4 = a5 * ddp
+
+ cinf = dds * (t1 + t2 + a3) + t3 + t4 + a6
+ ginf = -14.6 * math.log10 (delta_p / delta_s) - 16.9
+ n = cinf / df + ginf * df + 1
+ return n
+
diff --git a/gnuradio-core/src/python/gnuradio/packet_utils.py b/gnuradio-core/src/python/gnuradio/packet_utils.py
new file mode 100644
index 000000000..d7235540f
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/packet_utils.py
@@ -0,0 +1,433 @@
+#
+# Copyright 2005,2006 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+import struct
+import Numeric
+from gnuradio import gru
+
+
+def conv_packed_binary_string_to_1_0_string(s):
+ """
+ '\xAF' --> '10101111'
+ """
+ r = []
+ for ch in s:
+ x = ord(ch)
+ for i in range(7,-1,-1):
+ t = (x >> i) & 0x1
+ r.append(t)
+
+ return ''.join(map(lambda x: chr(x + ord('0')), r))
+
+def conv_1_0_string_to_packed_binary_string(s):
+ """
+ '10101111' -> ('\xAF', False)
+
+ Basically the inverse of conv_packed_binary_string_to_1_0_string,
+ but also returns a flag indicating if we had to pad with leading zeros
+ to get to a multiple of 8.
+ """
+ if not is_1_0_string(s):
+ raise ValueError, "Input must be a string containing only 0's and 1's"
+
+ # pad to multiple of 8
+ padded = False
+ rem = len(s) % 8
+ if rem != 0:
+ npad = 8 - rem
+ s = '0' * npad + s
+ padded = True
+
+ assert len(s) % 8 == 0
+
+ r = []
+ i = 0
+ while i < len(s):
+ t = 0
+ for j in range(8):
+ t = (t << 1) | (ord(s[i + j]) - ord('0'))
+ r.append(chr(t))
+ i += 8
+ return (''.join(r), padded)
+
+
+default_access_code = \
+ conv_packed_binary_string_to_1_0_string('\xAC\xDD\xA4\xE2\xF2\x8C\x20\xFC')
+
+
+def is_1_0_string(s):
+ if not isinstance(s, str):
+ return False
+ for ch in s:
+ if not ch in ('0', '1'):
+ return False
+ return True
+
+def string_to_hex_list(s):
+ return map(lambda x: hex(ord(x)), s)
+
+
+def whiten(s):
+ sa = Numeric.fromstring(s, Numeric.UnsignedInt8)
+ z = sa ^ random_mask_vec8[0:len(sa)]
+ return z.tostring()
+
+def dewhiten(s):
+ return whiten(s) # self inverse
+
+
+def make_header(payload_len):
+ return struct.pack('!HH', payload_len, payload_len)
+
+def make_packet(payload, spb, bits_per_baud, access_code=default_access_code, pad_for_usrp=True):
+ """
+ Build a packet, given access code and payload.
+
+ @param payload: packet payload, len [0, 4096]
+ @param spb: samples per baud (needed for padding calculation)
+ @type spb: int
+ @param bits_per_baud: (needed for padding calculation)
+ @type bits_per_baud: int
+ @param access_code: string of ascii 0's and 1's
+
+ Packet will have access code at the beginning, followed by length, payload
+ and finally CRC-32.
+ """
+ if not is_1_0_string(access_code):
+ raise ValueError, "access_code must be a string containing only 0's and 1's (%r)" % (access_code,)
+
+ (packed_access_code, padded) = conv_1_0_string_to_packed_binary_string(access_code)
+
+ payload_with_crc = gru.gen_and_append_crc32(payload)
+ #print "outbound crc =", string_to_hex_list(payload_with_crc[-4:])
+
+ L = len(payload_with_crc)
+ MAXLEN = len(random_mask_tuple)
+ if L > MAXLEN:
+ raise ValueError, "len(payload) must be in [0, %d]" % (MAXLEN,)
+
+ pkt = ''.join((packed_access_code, make_header(L), whiten(payload_with_crc), '\x55'))
+ if pad_for_usrp:
+ pkt = pkt + (_npadding_bytes(len(pkt), spb, bits_per_baud) * '\x55')
+
+ #print "make_packet: len(pkt) =", len(pkt)
+ return pkt
+
+def _npadding_bytes(pkt_byte_len, spb, bits_per_baud):
+ """
+ Generate sufficient padding such that each packet ultimately ends
+ up being a multiple of 512 bytes when sent across the USB. We
+ send 4-byte samples across the USB (16-bit I and 16-bit Q), thus
+ we want to pad so that after modulation the resulting packet
+ is a multiple of 128 samples.
+
+ @param ptk_byte_len: len in bytes of packet, not including padding.
+ @param spb: samples per baud == samples per bit (1 bit / baud with GMSK)
+ @type spb: int
+
+ @returns number of bytes of padding to append.
+ """
+ modulus = 128
+ byte_modulus = gru.lcm(modulus/8, spb) * bits_per_baud / spb
+ r = pkt_byte_len % byte_modulus
+ if r == 0:
+ return 0
+ return byte_modulus - r
+
+
+def unmake_packet(whitened_payload_with_crc):
+ """
+ Return (ok, payload)
+
+ @param whitened_payload_with_crc: string
+ """
+ payload_with_crc = dewhiten(whitened_payload_with_crc)
+ ok, payload = gru.check_crc32(payload_with_crc)
+
+ if 0:
+ print "payload_with_crc =", string_to_hex_list(payload_with_crc)
+ print "ok = %r, len(payload) = %d" % (ok, len(payload))
+ print "payload =", string_to_hex_list(payload)
+
+ return ok, payload
+
+
+# FYI, this PN code is the output of a 15-bit LFSR
+random_mask_tuple = (
+ 255, 63, 0, 16, 0, 12, 0, 5, 192, 3, 16, 1, 204, 0, 85, 192,
+ 63, 16, 16, 12, 12, 5, 197, 195, 19, 17, 205, 204, 85, 149, 255, 47,
+ 0, 28, 0, 9, 192, 6, 208, 2, 220, 1, 153, 192, 106, 208, 47, 28,
+ 28, 9, 201, 198, 214, 210, 222, 221, 152, 89, 170, 186, 255, 51, 0, 21,
+ 192, 15, 16, 4, 12, 3, 69, 193, 243, 16, 69, 204, 51, 21, 213, 207,
+ 31, 20, 8, 15, 70, 132, 50, 227, 85, 137, 255, 38, 192, 26, 208, 11,
+ 28, 7, 73, 194, 182, 209, 182, 220, 118, 217, 230, 218, 202, 219, 23, 27,
+ 78, 139, 116, 103, 103, 106, 170, 175, 63, 60, 16, 17, 204, 12, 85, 197,
+ 255, 19, 0, 13, 192, 5, 144, 3, 44, 1, 221, 192, 89, 144, 58, 236,
+ 19, 13, 205, 197, 149, 147, 47, 45, 220, 29, 153, 201, 170, 214, 255, 30,
+ 192, 8, 80, 6, 188, 2, 241, 193, 132, 80, 99, 124, 41, 225, 222, 200,
+ 88, 86, 186, 190, 243, 48, 69, 212, 51, 31, 85, 200, 63, 22, 144, 14,
+ 236, 4, 77, 195, 117, 145, 231, 44, 74, 157, 247, 41, 134, 158, 226, 232,
+ 73, 142, 182, 228, 118, 203, 102, 215, 106, 222, 175, 24, 124, 10, 161, 199,
+ 56, 82, 146, 189, 173, 177, 189, 180, 113, 183, 100, 118, 171, 102, 255, 106,
+ 192, 47, 16, 28, 12, 9, 197, 198, 211, 18, 221, 205, 153, 149, 170, 239,
+ 63, 12, 16, 5, 204, 3, 21, 193, 207, 16, 84, 12, 63, 69, 208, 51,
+ 28, 21, 201, 207, 22, 212, 14, 223, 68, 88, 51, 122, 149, 227, 47, 9,
+ 220, 6, 217, 194, 218, 209, 155, 28, 107, 73, 239, 118, 204, 38, 213, 218,
+ 223, 27, 24, 11, 74, 135, 119, 34, 166, 153, 186, 234, 243, 15, 5, 196,
+ 3, 19, 65, 205, 240, 85, 132, 63, 35, 80, 25, 252, 10, 193, 199, 16,
+ 82, 140, 61, 165, 209, 187, 28, 115, 73, 229, 246, 203, 6, 215, 66, 222,
+ 177, 152, 116, 106, 167, 111, 58, 172, 19, 61, 205, 209, 149, 156, 111, 41,
+ 236, 30, 205, 200, 85, 150, 191, 46, 240, 28, 68, 9, 243, 70, 197, 242,
+ 211, 5, 157, 195, 41, 145, 222, 236, 88, 77, 250, 181, 131, 55, 33, 214,
+ 152, 94, 234, 184, 79, 50, 180, 21, 183, 79, 54, 180, 22, 247, 78, 198,
+ 180, 82, 247, 125, 134, 161, 162, 248, 121, 130, 162, 225, 185, 136, 114, 230,
+ 165, 138, 251, 39, 3, 90, 129, 251, 32, 67, 88, 49, 250, 148, 67, 47,
+ 113, 220, 36, 89, 219, 122, 219, 99, 27, 105, 203, 110, 215, 108, 94, 173,
+ 248, 125, 130, 161, 161, 184, 120, 114, 162, 165, 185, 187, 50, 243, 85, 133,
+ 255, 35, 0, 25, 192, 10, 208, 7, 28, 2, 137, 193, 166, 208, 122, 220,
+ 35, 25, 217, 202, 218, 215, 27, 30, 139, 72, 103, 118, 170, 166, 255, 58,
+ 192, 19, 16, 13, 204, 5, 149, 195, 47, 17, 220, 12, 89, 197, 250, 211,
+ 3, 29, 193, 201, 144, 86, 236, 62, 205, 208, 85, 156, 63, 41, 208, 30,
+ 220, 8, 89, 198, 186, 210, 243, 29, 133, 201, 163, 22, 249, 206, 194, 212,
+ 81, 159, 124, 104, 33, 238, 152, 76, 106, 181, 239, 55, 12, 22, 133, 206,
+ 227, 20, 73, 207, 118, 212, 38, 223, 90, 216, 59, 26, 147, 75, 45, 247,
+ 93, 134, 185, 162, 242, 249, 133, 130, 227, 33, 137, 216, 102, 218, 170, 219,
+ 63, 27, 80, 11, 124, 7, 97, 194, 168, 81, 190, 188, 112, 113, 228, 36,
+ 75, 91, 119, 123, 102, 163, 106, 249, 239, 2, 204, 1, 149, 192, 111, 16,
+ 44, 12, 29, 197, 201, 147, 22, 237, 206, 205, 148, 85, 175, 127, 60, 32,
+ 17, 216, 12, 90, 133, 251, 35, 3, 89, 193, 250, 208, 67, 28, 49, 201,
+ 212, 86, 223, 126, 216, 32, 90, 152, 59, 42, 147, 95, 45, 248, 29, 130,
+ 137, 161, 166, 248, 122, 194, 163, 17, 185, 204, 114, 213, 229, 159, 11, 40,
+ 7, 94, 130, 184, 97, 178, 168, 117, 190, 167, 48, 122, 148, 35, 47, 89,
+ 220, 58, 217, 211, 26, 221, 203, 25, 151, 74, 238, 183, 12, 118, 133, 230,
+ 227, 10, 201, 199, 22, 210, 142, 221, 164, 89, 187, 122, 243, 99, 5, 233,
+ 195, 14, 209, 196, 92, 83, 121, 253, 226, 193, 137, 144, 102, 236, 42, 205,
+ 223, 21, 152, 15, 42, 132, 31, 35, 72, 25, 246, 138, 198, 231, 18, 202,
+ 141, 151, 37, 174, 155, 60, 107, 81, 239, 124, 76, 33, 245, 216, 71, 26,
+ 178, 139, 53, 167, 87, 58, 190, 147, 48, 109, 212, 45, 159, 93, 168, 57,
+ 190, 146, 240, 109, 132, 45, 163, 93, 185, 249, 178, 194, 245, 145, 135, 44,
+ 98, 157, 233, 169, 142, 254, 228, 64, 75, 112, 55, 100, 22, 171, 78, 255,
+ 116, 64, 39, 112, 26, 164, 11, 59, 71, 83, 114, 189, 229, 177, 139, 52,
+ 103, 87, 106, 190, 175, 48, 124, 20, 33, 207, 88, 84, 58, 191, 83, 48,
+ 61, 212, 17, 159, 76, 104, 53, 238, 151, 12, 110, 133, 236, 99, 13, 233,
+ 197, 142, 211, 36, 93, 219, 121, 155, 98, 235, 105, 143, 110, 228, 44, 75,
+ 93, 247, 121, 134, 162, 226, 249, 137, 130, 230, 225, 138, 200, 103, 22, 170,
+ 142, 255, 36, 64, 27, 112, 11, 100, 7, 107, 66, 175, 113, 188, 36, 113,
+ 219, 100, 91, 107, 123, 111, 99, 108, 41, 237, 222, 205, 152, 85, 170, 191,
+ 63, 48, 16, 20, 12, 15, 69, 196, 51, 19, 85, 205, 255, 21, 128, 15,
+ 32, 4, 24, 3, 74, 129, 247, 32, 70, 152, 50, 234, 149, 143, 47, 36,
+ 28, 27, 73, 203, 118, 215, 102, 222, 170, 216, 127, 26, 160, 11, 56, 7,
+ 82, 130, 189, 161, 177, 184, 116, 114, 167, 101, 186, 171, 51, 63, 85, 208,
+ 63, 28, 16, 9, 204, 6, 213, 194, 223, 17, 152, 12, 106, 133, 239, 35,
+ 12, 25, 197, 202, 211, 23, 29, 206, 137, 148, 102, 239, 106, 204, 47, 21,
+ 220, 15, 25, 196, 10, 211, 71, 29, 242, 137, 133, 166, 227, 58, 201, 211,
+ 22, 221, 206, 217, 148, 90, 239, 123, 12, 35, 69, 217, 243, 26, 197, 203,
+ 19, 23, 77, 206, 181, 148, 119, 47, 102, 156, 42, 233, 223, 14, 216, 4,
+ 90, 131, 123, 33, 227, 88, 73, 250, 182, 195, 54, 209, 214, 220, 94, 217,
+ 248, 90, 194, 187, 17, 179, 76, 117, 245, 231, 7, 10, 130, 135, 33, 162,
+ 152, 121, 170, 162, 255, 57, 128, 18, 224, 13, 136, 5, 166, 131, 58, 225,
+ 211, 8, 93, 198, 185, 146, 242, 237, 133, 141, 163, 37, 185, 219, 50, 219,
+ 85, 155, 127, 43, 96, 31, 104, 8, 46, 134, 156, 98, 233, 233, 142, 206,
+ 228, 84, 75, 127, 119, 96, 38, 168, 26, 254, 139, 0, 103, 64, 42, 176,
+ 31, 52, 8, 23, 70, 142, 178, 228, 117, 139, 103, 39, 106, 154, 175, 43,
+ 60, 31, 81, 200, 60, 86, 145, 254, 236, 64, 77, 240, 53, 132, 23, 35,
+ 78, 153, 244, 106, 199, 111, 18, 172, 13, 189, 197, 177, 147, 52, 109, 215,
+ 109, 158, 173, 168, 125, 190, 161, 176, 120, 116, 34, 167, 89, 186, 186, 243,
+ 51, 5, 213, 195, 31, 17, 200, 12, 86, 133, 254, 227, 0, 73, 192, 54,
+ 208, 22, 220, 14, 217, 196, 90, 211, 123, 29, 227, 73, 137, 246, 230, 198,
+ 202, 210, 215, 29, 158, 137, 168, 102, 254, 170, 192, 127, 16, 32, 12, 24,
+ 5, 202, 131, 23, 33, 206, 152, 84, 106, 191, 111, 48, 44, 20, 29, 207,
+ 73, 148, 54, 239, 86, 204, 62, 213, 208, 95, 28, 56, 9, 210, 134, 221,
+ 162, 217, 185, 154, 242, 235, 5, 143, 67, 36, 49, 219, 84, 91, 127, 123,
+ 96, 35, 104, 25, 238, 138, 204, 103, 21, 234, 143, 15, 36, 4, 27, 67,
+ 75, 113, 247, 100, 70, 171, 114, 255, 101, 128, 43, 32, 31, 88, 8, 58,
+ 134, 147, 34, 237, 217, 141, 154, 229, 171, 11, 63, 71, 80, 50, 188, 21,
+ 177, 207, 52, 84, 23, 127, 78, 160, 52, 120, 23, 98, 142, 169, 164, 126,
+ 251, 96, 67, 104, 49, 238, 148, 76, 111, 117, 236, 39, 13, 218, 133, 155,
+ 35, 43, 89, 223, 122, 216, 35, 26, 153, 203, 42, 215, 95, 30, 184, 8,
+ 114, 134, 165, 162, 251, 57, 131, 82, 225, 253, 136, 65, 166, 176, 122, 244,
+ 35, 7, 89, 194, 186, 209, 179, 28, 117, 201, 231, 22, 202, 142, 215, 36,
+ 94, 155, 120, 107, 98, 175, 105, 188, 46, 241, 220, 68, 89, 243, 122, 197,
+ 227, 19, 9, 205, 198, 213, 146, 223, 45, 152, 29, 170, 137, 191, 38, 240,
+ 26, 196, 11, 19, 71, 77, 242, 181, 133, 183, 35, 54, 153, 214, 234, 222,
+ 207, 24, 84, 10, 191, 71, 48, 50, 148, 21, 175, 79, 60, 52, 17, 215,
+ 76, 94, 181, 248, 119, 2, 166, 129, 186, 224, 115, 8, 37, 198, 155, 18,
+ 235, 77, 143, 117, 164, 39, 59, 90, 147, 123, 45, 227, 93, 137, 249, 166,
+ 194, 250, 209, 131, 28, 97, 201, 232, 86, 206, 190, 212, 112, 95, 100, 56,
+ 43, 82, 159, 125, 168, 33, 190, 152, 112, 106, 164, 47, 59, 92, 19, 121,
+ 205, 226, 213, 137, 159, 38, 232, 26, 206, 139, 20, 103, 79, 106, 180, 47,
+ 55, 92, 22, 185, 206, 242, 212, 69, 159, 115, 40, 37, 222, 155, 24, 107,
+ 74, 175, 119, 60, 38, 145, 218, 236, 91, 13, 251, 69, 131, 115, 33, 229,
+ 216, 75, 26, 183, 75, 54, 183, 86, 246, 190, 198, 240, 82, 196, 61, 147,
+ 81, 173, 252, 125, 129, 225, 160, 72, 120, 54, 162, 150, 249, 174, 194, 252,
+ 81, 129, 252, 96, 65, 232, 48, 78, 148, 52, 111, 87, 108, 62, 173, 208,
+ 125, 156, 33, 169, 216, 126, 218, 160, 91, 56, 59, 82, 147, 125, 173, 225,
+ 189, 136, 113, 166, 164, 122, 251, 99, 3, 105, 193, 238, 208, 76, 92, 53,
+ 249, 215, 2, 222, 129, 152, 96, 106, 168, 47, 62, 156, 16, 105, 204, 46,
+ 213, 220, 95, 25, 248, 10, 194, 135, 17, 162, 140, 121, 165, 226, 251, 9,
+ 131, 70, 225, 242, 200, 69, 150, 179, 46, 245, 220, 71, 25, 242, 138, 197,
+ 167, 19, 58, 141, 211, 37, 157, 219, 41, 155, 94, 235, 120, 79, 98, 180,
+ 41, 183, 94, 246, 184, 70, 242, 178, 197, 181, 147, 55, 45, 214, 157, 158,
+ 233, 168, 78, 254, 180, 64, 119, 112, 38, 164, 26, 251, 75, 3, 119, 65,
+ 230, 176, 74, 244, 55, 7, 86, 130, 190, 225, 176, 72, 116, 54, 167, 86,
+ 250, 190, 195, 48, 81, 212, 60, 95, 81, 248, 60, 66, 145, 241, 172, 68,
+ 125, 243, 97, 133, 232, 99, 14, 169, 196, 126, 211, 96, 93, 232, 57, 142,
+ 146, 228, 109, 139, 109, 167, 109, 186, 173, 179, 61, 181, 209, 183, 28, 118,
+ 137, 230, 230, 202, 202, 215, 23, 30, 142, 136, 100, 102, 171, 106, 255, 111,
+ 0, 44, 0, 29, 192, 9, 144, 6, 236, 2, 205, 193, 149, 144, 111, 44,
+ 44, 29, 221, 201, 153, 150, 234, 238, 207, 12, 84, 5, 255, 67, 0, 49,
+ 192, 20, 80, 15, 124, 4, 33, 195, 88, 81, 250, 188, 67, 49, 241, 212,
+ 68, 95, 115, 120, 37, 226, 155, 9, 171, 70, 255, 114, 192, 37, 144, 27,
+ 44, 11, 93, 199, 121, 146, 162, 237, 185, 141, 178, 229, 181, 139, 55, 39,
+ 86, 154, 190, 235, 48, 79, 84, 52, 63, 87, 80, 62, 188, 16, 113, 204,
+ 36, 85, 219, 127, 27, 96, 11, 104, 7, 110, 130, 172, 97, 189, 232, 113,
+ 142, 164, 100, 123, 107, 99, 111, 105, 236, 46, 205, 220, 85, 153, 255, 42,
+ 192, 31, 16, 8, 12, 6, 133, 194, 227, 17, 137, 204, 102, 213, 234, 223,
+ 15, 24, 4, 10, 131, 71, 33, 242, 152, 69, 170, 179, 63, 53, 208, 23,
+ 28, 14, 137, 196, 102, 211, 106, 221, 239, 25, 140, 10, 229, 199, 11, 18,
+ 135, 77, 162, 181, 185, 183, 50, 246, 149, 134, 239, 34, 204, 25, 149, 202,
+ 239, 23, 12, 14, 133, 196, 99, 19, 105, 205, 238, 213, 140, 95, 37, 248,
+ 27, 2, 139, 65, 167, 112, 122, 164, 35, 59, 89, 211, 122, 221, 227, 25,
+ 137, 202, 230, 215, 10, 222, 135, 24, 98, 138, 169, 167, 62, 250, 144, 67,
+ 44, 49, 221, 212, 89, 159, 122, 232, 35, 14, 153, 196, 106, 211, 111, 29,
+ 236, 9, 141, 198, 229, 146, 203, 45, 151, 93, 174, 185, 188, 114, 241, 229,
+ 132, 75, 35, 119, 89, 230, 186, 202, 243, 23, 5, 206, 131, 20, 97, 207,
+ 104, 84, 46, 191, 92, 112, 57, 228, 18, 203, 77, 151, 117, 174, 167, 60,
+ 122, 145, 227, 44, 73, 221, 246, 217, 134, 218, 226, 219, 9, 155, 70, 235,
+ 114, 207, 101, 148, 43, 47, 95, 92, 56, 57, 210, 146, 221, 173, 153, 189,
+ 170, 241, 191, 4, 112, 3, 100, 1, 235, 64, 79, 112, 52, 36, 23, 91,
+ 78, 187, 116, 115, 103, 101, 234, 171, 15, 63, 68, 16, 51, 76, 21, 245,
+ 207, 7, 20, 2, 143, 65, 164, 48, 123, 84, 35, 127, 89, 224, 58, 200,
+ 19, 22, 141, 206, 229, 148, 75, 47, 119, 92, 38, 185, 218, 242, 219, 5,
+ 155, 67, 43, 113, 223, 100, 88, 43, 122, 159, 99, 40, 41, 222, 158, 216,
+ 104, 90, 174, 187, 60, 115, 81, 229, 252, 75, 1, 247, 64, 70, 176, 50,
+ 244, 21, 135, 79, 34, 180, 25, 183, 74, 246, 183, 6, 246, 130, 198, 225,
+ 146, 200, 109, 150, 173, 174, 253, 188, 65, 177, 240, 116, 68, 39, 115, 90,
+ 165, 251, 59, 3, 83, 65, 253, 240, 65, 132, 48, 99, 84, 41, 255, 94,
+ 192, 56, 80, 18, 188, 13, 177, 197, 180, 83, 55, 125, 214, 161, 158, 248,
+ 104, 66, 174, 177, 188, 116, 113, 231, 100, 74, 171, 119, 63, 102, 144, 42,
+ 236, 31, 13, 200, 5, 150, 131, 46, 225, 220, 72, 89, 246, 186, 198, 243,
+ 18, 197, 205, 147, 21, 173, 207, 61, 148, 17, 175, 76, 124, 53, 225, 215,
+ 8, 94, 134, 184, 98, 242, 169, 133, 190, 227, 48, 73, 212, 54, 223, 86,
+ 216, 62, 218, 144, 91, 44, 59, 93, 211, 121, 157, 226, 233, 137, 142, 230,
+ 228, 74, 203, 119, 23, 102, 142, 170, 228, 127, 11, 96, 7, 104, 2, 174,
+ 129, 188, 96, 113, 232, 36, 78, 155, 116, 107, 103, 111, 106, 172, 47, 61,
+ 220, 17, 153, 204, 106, 213, 239, 31, 12, 8, 5, 198, 131, 18, 225, 205,
+ 136, 85, 166, 191, 58, 240, 19, 4, 13, 195, 69, 145, 243, 44, 69, 221,
+ 243, 25, 133, 202, 227, 23, 9, 206, 134, 212, 98, 223, 105, 152, 46, 234,
+ 156, 79, 41, 244, 30, 199, 72, 82, 182, 189, 182, 241, 182, 196, 118, 211,
+ 102, 221, 234, 217, 143, 26, 228, 11, 11, 71, 71, 114, 178, 165, 181, 187,
+ 55, 51, 86, 149, 254, 239, 0, 76, 0, 53, 192, 23, 16, 14, 140, 4,
+ 101, 195, 107, 17, 239, 76, 76, 53, 245, 215, 7, 30, 130, 136, 97, 166,
+ 168, 122, 254, 163, 0, 121, 192, 34, 208, 25, 156, 10, 233, 199, 14, 210,
+ 132, 93, 163, 121, 185, 226, 242, 201, 133, 150, 227, 46, 201, 220, 86, 217,
+ 254, 218, 192, 91, 16, 59, 76, 19, 117, 205, 231, 21, 138, 143, 39, 36,
+ 26, 155, 75, 43, 119, 95, 102, 184, 42, 242, 159, 5, 168, 3, 62, 129,
+ 208, 96, 92, 40, 57, 222, 146, 216, 109, 154, 173, 171, 61, 191, 81, 176,
+ 60, 116, 17, 231, 76, 74, 181, 247, 55, 6, 150, 130, 238, 225, 140, 72,
+ 101, 246, 171, 6, 255, 66, 192, 49, 144, 20, 108, 15, 109, 196, 45, 147,
+ 93, 173, 249, 189, 130, 241, 161, 132, 120, 99, 98, 169, 233, 190, 206, 240,
+ 84, 68, 63, 115, 80, 37, 252, 27, 1, 203, 64, 87, 112, 62, 164, 16,
+ 123, 76, 35, 117, 217, 231, 26, 202, 139, 23, 39, 78, 154, 180, 107, 55,
+ 111, 86, 172, 62, 253, 208, 65, 156, 48, 105, 212, 46, 223, 92, 88, 57,
+ 250, 146, 195, 45, 145, 221, 172, 89, 189, 250, 241, 131, 4, 97, 195, 104,
+ 81, 238, 188, 76, 113, 245, 228, 71, 11, 114, 135, 101, 162, 171, 57, 191,
+ 82, 240, 61, 132, 17, 163, 76, 121, 245, 226, 199, 9, 146, 134, 237, 162,
+ 205, 185, 149, 178, 239, 53, 140, 23, 37, 206, 155, 20, 107, 79, 111, 116,
+ 44, 39, 93, 218, 185, 155, 50, 235, 85, 143, 127, 36, 32, 27, 88, 11,
+ 122, 135, 99, 34, 169, 217, 190, 218, 240, 91, 4, 59, 67, 83, 113, 253,
+ 228, 65, 139, 112, 103, 100, 42, 171, 95, 63, 120, 16, 34, 140, 25, 165,
+ 202, 251, 23, 3, 78, 129, 244, 96, 71, 104, 50, 174, 149, 188, 111, 49,
+ 236, 20, 77, 207, 117, 148, 39, 47, 90, 156, 59, 41, 211, 94, 221, 248,
+ 89, 130, 186, 225, 179, 8, 117, 198, 167, 18, 250, 141, 131, 37, 161, 219,
+ 56, 91, 82, 187, 125, 179, 97, 181, 232, 119, 14, 166, 132, 122, 227, 99,
+ 9, 233, 198, 206, 210, 212, 93, 159, 121, 168, 34, 254, 153, 128, 106, 224,
+ 47, 8, 28, 6, 137, 194, 230, 209, 138, 220, 103, 25, 234, 138, 207, 39,
+ 20, 26, 143, 75, 36, 55, 91, 86, 187, 126, 243, 96, 69, 232, 51, 14,
+ 149, 196, 111, 19, 108, 13, 237, 197, 141, 147, 37, 173, 219, 61, 155, 81,
+ 171, 124, 127, 97, 224, 40, 72, 30, 182, 136, 118, 230, 166, 202, 250, 215,
+ 3, 30, 129, 200, 96, 86, 168, 62, 254, 144, 64, 108, 48, 45, 212, 29,
+ 159, 73, 168, 54, 254, 150, 192, 110, 208, 44, 92, 29, 249, 201, 130, 214,
+ 225, 158, 200, 104, 86, 174, 190, 252, 112, 65, 228, 48, 75, 84, 55, 127,
+ 86, 160, 62, 248, 16, 66, 140, 49, 165, 212, 123, 31, 99, 72, 41, 246,
+ 158, 198, 232, 82, 206, 189, 148, 113, 175, 100, 124, 43, 97, 223, 104, 88,
+ 46, 186, 156, 115, 41, 229, 222, 203, 24, 87, 74, 190, 183, 48, 118, 148,
+ 38, 239, 90, 204, 59, 21, 211, 79, 29, 244, 9, 135, 70, 226, 178, 201,
+ 181, 150, 247, 46, 198, 156, 82, 233, 253, 142, 193, 164, 80, 123, 124, 35,
+ 97, 217, 232, 90, 206, 187, 20, 115, 79, 101, 244, 43, 7, 95, 66, 184,
+ 49, 178, 148, 117, 175, 103, 60, 42, 145, 223, 44, 88, 29, 250, 137, 131,
+ 38, 225, 218, 200, 91, 22, 187, 78, 243, 116, 69, 231, 115, 10, 165, 199,
+ 59, 18, 147, 77, 173, 245, 189, 135, 49, 162, 148, 121, 175, 98, 252, 41,
+ 129, 222, 224, 88, 72, 58, 182, 147, 54, 237, 214, 205, 158, 213, 168, 95,
+ 62, 184, 16, 114, 140, 37, 165, 219, 59, 27, 83, 75, 125, 247, 97, 134,
+ 168, 98, 254, 169, 128, 126, 224, 32, 72, 24, 54, 138, 150, 231, 46, 202,
+ 156, 87, 41, 254, 158, 192, 104, 80, 46, 188, 28, 113, 201, 228, 86, 203,
+ 126, 215, 96, 94, 168, 56, 126, 146, 160, 109, 184, 45, 178, 157, 181, 169,
+ 183, 62, 246, 144, 70, 236, 50, 205, 213, 149, 159, 47, 40, 28, 30, 137,
+ 200, 102, 214, 170, 222, 255, 24, 64, 10, 176, 7, 52, 2, 151, 65, 174,
+ 176, 124, 116, 33, 231, 88, 74, 186, 183, 51, 54, 149, 214, 239, 30, 204,
+ 8, 85, 198, 191, 18, 240, 13, 132, 5, 163, 67, 57, 241, 210, 196, 93,
+ 147, 121, 173, 226, 253, 137, 129, 166, 224, 122, 200, 35, 22, 153, 206, 234,
+ 212, 79, 31, 116, 8, 39, 70, 154, 178, 235, 53, 143, 87, 36, 62, 155,
+ 80, 107, 124, 47, 97, 220, 40, 89, 222, 186, 216, 115, 26, 165, 203, 59,
+ 23, 83, 78, 189, 244, 113, 135, 100, 98, 171, 105, 191, 110, 240, 44, 68,
+ 29, 243, 73, 133, 246, 227, 6, 201, 194, 214, 209, 158, 220, 104, 89, 238,
+ 186, 204, 115, 21, 229, 207, 11, 20, 7, 79, 66, 180, 49, 183, 84, 118,
+ 191, 102, 240, 42, 196, 31, 19, 72, 13, 246, 133, 134, 227, 34, 201, 217,
+ 150, 218, 238, 219, 12, 91, 69, 251, 115, 3, 101, 193, 235, 16, 79, 76,
+ 52, 53, 215, 87, 30, 190, 136, 112, 102, 164, 42, 251, 95, 3, 120, 1,
+ 226, 128, 73, 160, 54, 248, 22, 194, 142, 209, 164, 92, 123, 121, 227, 98,
+ 201, 233, 150, 206, 238, 212, 76, 95, 117, 248, 39, 2, 154, 129, 171, 32,
+ 127, 88, 32, 58, 152, 19, 42, 141, 223, 37, 152, 27, 42, 139, 95, 39,
+ 120, 26, 162, 139, 57, 167, 82, 250, 189, 131, 49, 161, 212, 120, 95, 98,
+ 184, 41, 178, 158, 245, 168, 71, 62, 178, 144, 117, 172, 39, 61, 218, 145,
+ 155, 44, 107, 93, 239, 121, 140, 34, 229, 217, 139, 26, 231, 75, 10, 183,
+ 71, 54, 178, 150, 245, 174, 199, 60, 82, 145, 253, 172, 65, 189, 240, 113,
+ 132, 36, 99, 91, 105, 251, 110, 195, 108, 81, 237, 252, 77, 129, 245, 160,
+ 71, 56, 50, 146, 149, 173, 175, 61, 188, 17, 177, 204, 116, 85, 231, 127,
+ 10, 160, 7, 56, 2, 146, 129, 173, 160, 125, 184, 33, 178, 152, 117, 170,
+ 167, 63, 58, 144, 19, 44, 13, 221, 197, 153, 147, 42, 237, 223, 13, 152,
+ 5, 170, 131, 63, 33, 208, 24, 92, 10, 185, 199, 50, 210, 149, 157, 175,
+ 41, 188, 30, 241, 200, 68, 86, 179, 126, 245, 224, 71, 8, 50, 134, 149,
+ 162, 239, 57, 140, 18, 229, 205, 139, 21, 167, 79, 58, 180, 19, 55, 77,
+ 214, 181, 158, 247, 40, 70, 158, 178, 232, 117, 142, 167, 36, 122, 155, 99,
+ 43, 105, 223, 110, 216, 44, 90, 157, 251, 41, 131, 94, 225, 248, 72, 66,
+ 182, 177, 182, 244, 118, 199, 102, 210, 170, 221, 191, 25, 176, 10, 244, 7,
+ 7, 66, 130, 177, 161, 180, 120, 119, 98, 166, 169, 186, 254, 243, 0, 69,
+ 192, 51, 16, 21, 204, 15, 21, 196, 15, 19, 68, 13, 243, 69, 133, 243,
+ 35, 5, 217, 195, 26, 209, 203, 28, 87, 73, 254, 182, 192, 118, 208, 38,
+ 220, 26, 217, 203, 26, 215, 75, 30, 183, 72, 118, 182, 166, 246, 250, 198,
+ 195, 18, 209, 205, 156, 85, 169, 255, 62, 192, 16, 80, 12, 60, 5, 209,
+ 195, 28, 81, 201, 252, 86, 193, 254, 208, 64, 92, 48, 57, 212, 18, 223,
+ 77, 152, 53, 170, 151, 63, 46, 144, 28, 108, 9, 237, 198, 205, 146, 213,
+ 173, 159, 61, 168, 17, 190, 140, 112, 101, 228, 43, 11, 95, 71, 120, 50,
+ 162, 149, 185, 175, 50, 252, 21, 129, 207, 32, 84, 24, 63, 74, 144, 55,
+ 44, 22, 157, 206, 233, 148, 78, 239, 116, 76, 39, 117, 218, 167, 27, 58,
+ 139, 83, 39, 125, 218, 161, 155, 56, 107, 82, 175, 125, 188, 33, 177, 216,
+ 116, 90, 167, 123, 58, 163, 83, 57, 253, 210, 193, 157, 144, 105, 172, 46,
+ 253, 220, 65, 153, 240, 106, 196, 47, 19, 92, 13, 249, 197, 130, 211, 33,
+ 157, 216, 105, 154, 174, 235, 60, 79, 81, 244, 60, 71, 81, 242, 188, 69,
+ 177, 243, 52, 69, 215, 115, 30, 165, 200, 123, 22, 163, 78, 249, 244, 66,
+ 199, 113, 146, 164, 109, 187, 109, 179, 109, 181, 237, 183, 13, 182, 133, 182,
+ 227, 54, 201, 214, 214, 222, 222, 216, 88, 90, 186, 187, 51, 51, 255, 63 )
+
+random_mask_vec8 = Numeric.array(random_mask_tuple, Numeric.UnsignedInt8)
+
diff --git a/gnuradio-core/src/python/gnuradio/window.py b/gnuradio-core/src/python/gnuradio/window.py
new file mode 100644
index 000000000..391b83c37
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/window.py
@@ -0,0 +1,190 @@
+#
+# 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.
+#
+
+'''
+Routines for designing window functions.
+'''
+
+import math
+from gnuradio import gr
+
+def izero(x):
+ izeroepsilon = 1e-21
+ halfx = x/2.0
+ accum = u = n = 1
+ while 1:
+ temp = halfx/n
+ n += 1
+ temp *= temp
+ u *= temp
+ accum += u
+ if u >= IzeroEPSILON*sum:
+ break
+ return accum
+
+def midm1(fft_size):
+ return (fft_size - 1)/2
+
+def midp1(fft_size):
+ return (fft_size+1)/2
+
+def freq(fft_size):
+ return 2.0*math.pi/fft_size
+
+def rate(fft_size):
+ return 1.0/(fft_size >> 1)
+
+def expn(fft_size):
+ math.log(2.0)/(midn(fft_size) + 1.0)
+
+def hamming(fft_size):
+ window = []
+ for index in xrange(fft_size):
+ window.append(0.54 - 0.46 * math.cos (2 * math.pi / fft_size * index)) # Hamming window
+ return window
+
+def hanning(fft_size):
+ window = []
+ for index in xrange(fft_size):
+ window.append(0.5 - 0.5 * math.cos (2 * math.pi / fft_size * index)) # von Hann window
+ return window
+
+def welch(fft_size):
+ window = [0 for i in range(fft_size)]
+ j = fft_size-1
+ for index in xrange(midn(fft_size)+1):
+ window[j] = window[index] = (1.0 - math.sqrt((index - midm1(fft_size)) / midp1(fft_size)))
+ j -= 1
+ return window
+
+def parzen(fft_size):
+ window = [0 for i in range(fft_size)]
+ j = fft_size-1
+ for index in xrange(midn(fft_size)+1):
+ window[j] = window[index] = (1.0 - math.abs((index - midm1(fft_size)) / midp1(fft_size)))
+ j -= 1
+ return window
+
+def bartlett(fft_size):
+ mfrq = freq(fft_size)
+ angle = 0
+ window = [0 for i in range(fft_size)]
+ j = fft_size-1
+ for index in xrange(midn(fft_size)+1):
+ window[j] = window[index] = angle
+ angle += freq
+ j -= 1
+ return window
+
+def blackman2(fft_size):
+ mfrq = freq(fft_size)
+ angle = 0
+ window = [0 for i in range(fft_size)]
+ j = fft_size-1
+ for index in xrange(midn(fft_size)+1):
+ cx = math.cos(angle)
+ window[j] = window[index] = (.34401 + (cx * (-.49755 + (cx * .15844))))
+ angle += freq
+ j -= 1
+ return window
+
+def blackman3(fft_size):
+ mfrq = freq(fft_size)
+ angle = 0
+ window = [0 for i in range(fft_size)]
+ j = fft_size-1
+ for index in xrange(midn(fft_size)+1):
+ cx = math.cos(angle)
+ window[j] = window[index] = (.21747 + (cx * (-.45325 + (cx * (.28256 - (cx * .04672))))))
+ angle += freq
+ j -= 1
+ return window
+
+def blackman4(fft_size):
+ mfrq = freq(fft_size)
+ angle = 0
+ window = [0 for i in range(fft_size)]
+ j = fft_size-1
+ for index in xrange(midn(fft_size)+1):
+ cx = math.cos(angle)
+ window[j] = window[index] = (.084037 + (cx * (-.29145 + (cx * (.375696 + (cx * (-.20762 + (cx * .041194))))))))
+ angle += freq
+ j -= 1
+ return window
+
+def exponential(fft_size):
+ expsum = 1.0
+ window = [0 for i in range(fft_size)]
+ j = fft_size-1
+ for index in xrange(midn(fft_size)+1):
+ window[j] = window[i] = (expsum - 1.0)
+ expsum *= expn(fft_size)
+ j -= 1
+ return window
+
+def riemann(fft_size):
+ sr1 = freq(fft_size)
+ window = [0 for i in range(fft_size)]
+ j = fft_size-1
+ for index in xrange(midn(fft_size)):
+ if index == midn(fft_size):
+ window[index] = window[j] = 1.0
+ else:
+ cx = sr1*midn(fft_size) - index
+ window[index] = window[j] = math.sin(cx)/cx
+ j -= 1
+ return window
+
+def blackmanharris(fft_size):
+ a0 = 0.35875
+ a1 = 0.48829
+ a2 = 0.14128
+ a3 = 0.01168
+ window = [0 for i in range(fft_size)]
+ for index in xrange(fft_size):
+ window[index] = a0
+ window[index] -= a1*math.cos(2.0*math.pi*(index+0.5)/(fft_size - 1))
+ window[index] += a2*math.cos(4.0*math.pi*(index+0.5)/(fft_size - 1))
+ window[index] -= a3*math.cos(6.0*math.pi*(index+0.5)/(fft_size - 1))
+ return window
+
+def nuttall(fft_size):
+ a0 = 0.3635819
+ a1 = 0.4891775
+ a2 = 0.1365995
+ a3 = 0.0106411
+ window = [0 for i in range(fft_size)]
+ for index in xrange(fft_size):
+ window[index] = a0
+ window[index] -= a1*math.cos(2.0*math.pi*(index+0.5)/(fft_size - 1))
+ window[index] += a2*math.cos(4.0*math.pi*(index+0.5)/(fft_size - 1))
+ window[index] -= a3*math.cos(6.0*math.pi*(index+0.5)/(fft_size - 1))
+ return window
+
+def kaiser(fft_size,beta):
+ ibeta = 1.0/izero(beta)
+ inm1 = 1.0/(fft_size)
+ window = [0 for i in range(fft_size)]
+ for index in xrange(fft_size):
+ window[index] = izero(beta*math.sqrt(1.0 - (index * inm1)*(index * inm1))) * ibeta
+ return window
+
+