summaryrefslogtreecommitdiff
path: root/gnuradio-core/src/python
diff options
context:
space:
mode:
Diffstat (limited to 'gnuradio-core/src/python')
-rwxr-xr-xgnuradio-core/src/python/bin/microtune.py42
-rw-r--r--gnuradio-core/src/python/build_utils.py226
-rw-r--r--gnuradio-core/src/python/build_utils_codes.py52
-rw-r--r--gnuradio-core/src/python/gnuradio/CMakeLists.txt38
-rw-r--r--gnuradio-core/src/python/gnuradio/__init__.py1
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2/CMakeLists.txt26
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2/__init__.py37
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/CMakeLists.txt45
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/__init__.py1
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/am_demod.py75
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/channel_model.py26
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/digital_voice.py.real102
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/filterbank.py169
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/fm_demod.py111
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/fm_emph.py151
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/logpwrfft.py155
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/nbfm_rx.py88
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/nbfm_tx.py92
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/pfb_arb_resampler.py128
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/pfb_channelizer.py73
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/pfb_decimator.py69
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/pfb_interpolator.py70
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/rational_resampler.py131
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/standard_squelch.py76
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/stream_to_vector_decimator.py93
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/wfm_rcv.py69
-rwxr-xr-xgnuradio-core/src/python/gnuradio/blks2impl/wfm_rcv_fmdet.py216
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/wfm_rcv_pll.py190
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/wfm_tx.py79
-rw-r--r--gnuradio-core/src/python/gnuradio/eng_notation.py71
-rw-r--r--gnuradio-core/src/python/gnuradio/eng_option.py62
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/CMakeLists.txt49
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/__init__.py116
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/benchmark_filters.py75
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/exceptions.py27
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/gateway.py246
-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_block2.py129
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/prefs.py127
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/pubsub.py153
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_add_and_friends.py163
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_add_v_and_friends.py353
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/qa_affinity.py49
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_agc.py433
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/qa_argmax.py77
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_bin_statistics.py230
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/qa_block_gateway.py235
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_boolean_operators.py162
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_complex_to_xxx.py142
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/qa_conjugate.py53
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_copy.py58
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_dc_blocker.py108
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_delay.py65
-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_ecc_ccsds_27.py50
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/qa_endian_swap.py66
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_feval.py110
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_fft.py212
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_fft_filter.py383
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_filter_delay_fc.py317
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_float_to_char.py82
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_float_to_int.py85
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_float_to_short.py86
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_float_to_uchar.py64
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_fractional_interpolator.py38
-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_glfsr_source.py94
-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_hier_block2.py369
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_hilbert.py116
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_iir.py159
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_int_to_float.py69
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_integrate.py75
-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_keep_m_in_n.py58
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_kludge_copy.py91
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_kludged_imports.py43
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_max.py70
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_message.py131
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/qa_multiply_conjugate.py57
-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_noise.py51
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_pack_k_bits.py67
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_packed_to_unpacked.py405
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_pdu.py109
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_pipe_fittings.py143
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_pll_carriertracking.py156
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_pll_freqdet.py160
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_pll_refout.py156
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_pn_correlator_cc.py50
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/qa_probe_signal.py67
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/qa_python_message_passing.py123
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_rational_resampler.py298
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_regenerate.py90
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_repeat.py48
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_scrambler.py64
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_short_to_char.py69
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_short_to_float.py70
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_sig_source.py157
-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_skiphead.py102
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_stream_mux.py168
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_tag_debug.py43
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_tag_utils.py55
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_udp_sink_source.py99
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_unpack_k_bits.py57
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_vector_insert.py58
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/qa_vector_map.py105
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_vector_sink_source.py65
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_wavefile.py69
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/tag_utils.py54
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/test_16bit_1chunk.wavbin0 -> 52 bytes
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/top_block.py165
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr_unittest.py167
-rw-r--r--gnuradio-core/src/python/gnuradio/gr_xmlrunner.py387
-rw-r--r--gnuradio-core/src/python/gnuradio/gru/CMakeLists.txt26
-rw-r--r--gnuradio-core/src/python/gnuradio/gru/__init__.py37
-rw-r--r--gnuradio-core/src/python/gnuradio/gruimpl/CMakeLists.txt38
-rw-r--r--gnuradio-core/src/python/gnuradio/gruimpl/__init__.py1
-rw-r--r--gnuradio-core/src/python/gnuradio/gruimpl/daemon.py102
-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.py44
-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/msgq_runner.py82
-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.py341
-rw-r--r--gnuradio-core/src/python/gnuradio/window.py180
141 files changed, 16639 insertions, 0 deletions
diff --git a/gnuradio-core/src/python/bin/microtune.py b/gnuradio-core/src/python/bin/microtune.py
new file mode 100755
index 000000000..fbe743f39
--- /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..cf58a9763
--- /dev/null
+++ b/gnuradio-core/src/python/build_utils.py
@@ -0,0 +1,226 @@
+#
+# Copyright 2004,2009,2012 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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 + '/'
+
+# set do_makefile to either true or false dependeing on the environment
+try:
+ if os.environ['do_makefile'] == '0':
+ do_makefile = False
+ else:
+ do_makefile = True
+except KeyError, e:
+ do_makefile = False
+
+# set do_sources to either true or false dependeing on the environment
+try:
+ if os.environ['do_sources'] == '0':
+ do_sources = False
+ else:
+ do_sources = True
+except KeyError, e:
+ do_sources = True
+
+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):
+ global do_sources
+ if do_sources:
+ f = open (name, dir)
+ else:
+ f = None
+ 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
+ '''
+ global do_sources
+ output_extension = extract_extension (template_filename)
+ template = open_src (template_filename, 'r')
+ output_name = d['NAME'] + extra + '.' + output_extension
+ log_output_name (output_name)
+ if do_sources:
+ output = open (output_name, 'w')
+ do_substitution (d, template, output)
+ output.close ()
+ template.close ()
+
+def output_glue (dirname):
+ output_makefile_fragment ()
+ output_ifile_include (dirname)
+
+def output_makefile_fragment ():
+ global do_makefile
+ if not do_makefile:
+ return
+# overwrite the source, which must be writable; this should have been
+# checked for beforehand in the top-level Makefile.gen.gen .
+ f = open (os.path.join (os.environ.get('gendir', os.environ.get('srcdir', '.')), '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):
+ global do_sources
+ if do_sources:
+ 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, 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, package='gr'):
+ d = {}
+ d['NAME'] = name
+ d['NAME_IMPL'] = name+'_impl'
+ d['GUARD_NAME'] = 'INCLUDED_%s_%s_H' % (package.upper(), name.upper())
+ d['GUARD_NAME_IMPL'] = 'INCLUDED_%s_%s_IMPL_H' % (package.upper(), name.upper())
+ d['BASE_NAME'] = re.sub ('^' + package + '_', '', name)
+ d['SPTR_NAME'] = '%s_sptr' % name
+ d['WARNING'] = 'WARNING: this file is machine generated. Edits will be overwritten'
+ 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
+
+
+def standard_dict2 (name, code3, package):
+ d = {}
+ d['NAME'] = name
+ d['BASE_NAME'] = name
+ d['GUARD_NAME'] = 'INCLUDED_%s_%s_H' % (package.upper(), name.upper())
+ d['WARNING'] = 'WARNING: this file is machine generated. Edits will be overwritten'
+ 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
+
+def standard_impl_dict2 (name, code3, package):
+ d = {}
+ d['NAME'] = name
+ d['IMPL_NAME'] = name
+ d['BASE_NAME'] = name.rstrip("impl").rstrip("_")
+ d['GUARD_NAME'] = 'INCLUDED_%s_%s_H' % (package.upper(), name.upper())
+ d['WARNING'] = 'WARNING: this file is machine generated. Edits will be overwritten'
+ d['COPYRIGHT'] = copyright
+ d['FIR_TYPE'] = "fir_filter_" + code3
+ d['CFIR_TYPE'] = "fir_filter_" + code3[0:2] + 'c'
+ 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..9ea96baae
--- /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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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/CMakeLists.txt b/gnuradio-core/src/python/gnuradio/CMakeLists.txt
new file mode 100644
index 000000000..bf696e0d3
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/CMakeLists.txt
@@ -0,0 +1,38 @@
+# Copyright 2010-2011 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+
+include(GrPython)
+
+add_subdirectory(gr)
+add_subdirectory(gru)
+add_subdirectory(gruimpl)
+add_subdirectory(blks2)
+add_subdirectory(blks2impl)
+
+GR_PYTHON_INSTALL(FILES
+ __init__.py
+ eng_notation.py
+ eng_option.py
+ gr_unittest.py
+ gr_xmlrunner.py
+ optfir.py
+ window.py
+ DESTINATION ${GR_PYTHON_DIR}/gnuradio
+ COMPONENT "core_python"
+)
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/blks2/CMakeLists.txt b/gnuradio-core/src/python/gnuradio/blks2/CMakeLists.txt
new file mode 100644
index 000000000..83d11dd83
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2/CMakeLists.txt
@@ -0,0 +1,26 @@
+# Copyright 2010-2011 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+
+include(GrPython)
+
+GR_PYTHON_INSTALL(
+ FILES __init__.py
+ DESTINATION ${GR_PYTHON_DIR}/gnuradio/blks2
+ COMPONENT "core_python"
+)
diff --git a/gnuradio-core/src/python/gnuradio/blks2/__init__.py b/gnuradio-core/src/python/gnuradio/blks2/__init__.py
new file mode 100644
index 000000000..2dfdc77f4
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2/__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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import glob
+import os.path
+
+# Semi-hideous kludge to import everything in the blksimpl2 directory
+# into the gnuradio.blks2 namespace. This keeps us from having to remember
+# to manually update this file.
+
+for p in __path__:
+ filenames = glob.glob (os.path.join (p, "..", "blks2impl", "*.py"))
+ for f in filenames:
+ f = os.path.basename(f).lower()
+ f = f[:-3]
+ if f == '__init__':
+ continue
+ # print f
+ exec "from gnuradio.blks2impl.%s import *" % (f,)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/CMakeLists.txt b/gnuradio-core/src/python/gnuradio/blks2impl/CMakeLists.txt
new file mode 100644
index 000000000..61fcdda42
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/CMakeLists.txt
@@ -0,0 +1,45 @@
+# Copyright 2010-2011 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+
+include(GrPython)
+
+GR_PYTHON_INSTALL(FILES
+ __init__.py
+ am_demod.py
+ channel_model.py
+ filterbank.py
+ fm_demod.py
+ fm_emph.py
+ logpwrfft.py
+ nbfm_rx.py
+ nbfm_tx.py
+ pfb_arb_resampler.py
+ pfb_channelizer.py
+ pfb_decimator.py
+ pfb_interpolator.py
+ rational_resampler.py
+ standard_squelch.py
+ stream_to_vector_decimator.py
+ wfm_rcv.py
+ wfm_rcv_fmdet.py
+ wfm_rcv_pll.py
+ wfm_tx.py
+ DESTINATION ${GR_PYTHON_DIR}/gnuradio/blks2impl
+ COMPONENT "core_python"
+)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/__init__.py b/gnuradio-core/src/python/gnuradio/blks2impl/__init__.py
new file mode 100644
index 000000000..a4917cf64
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/__init__.py
@@ -0,0 +1 @@
+# make this a package
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/am_demod.py b/gnuradio-core/src/python/gnuradio/blks2impl/am_demod.py
new file mode 100644
index 000000000..68d024565
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/am_demod.py
@@ -0,0 +1,75 @@
+#
+# Copyright 2006,2007 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, optfir
+
+class am_demod_cf(gr.hier_block2):
+ """
+ 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 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, channel_rate, audio_decim, audio_pass, audio_stop):
+ gr.hier_block2.__init__(self, "am_demod_cf",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Input signature
+
+ 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)
+
+ self.connect(self, MAG, DCR, LPF, self)
+
+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 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, channel_rate, audio_decim):
+ am_demod_cf.__init__(self, channel_rate, audio_decim,
+ 5000, # Audio passband
+ 5500) # Audio stopband
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/channel_model.py b/gnuradio-core/src/python/gnuradio/blks2impl/channel_model.py
new file mode 100644
index 000000000..e5cd471df
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/channel_model.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+#
+# Copyright 2009 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr
+
+# This block is now a C++ hierarchical block, gr.channel_model
+channel_model = gr.channel_model
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/digital_voice.py.real b/gnuradio-core/src/python/gnuradio/blks2impl/digital_voice.py.real
new file mode 100644
index 000000000..6ec66825c
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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/blks2impl/filterbank.py b/gnuradio-core/src/python/gnuradio/blks2impl/filterbank.py
new file mode 100644
index 000000000..08f1d450b
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/filterbank.py
@@ -0,0 +1,169 @@
+#
+# Copyright 2005,2007 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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_block2):
+ """
+ Uniformly modulated polyphase DFT filter bank: synthesis
+
+ See http://cnx.org/content/m10424/latest
+ """
+ def __init__(self, mpoints, taps=None):
+ """
+ Takes M complex streams in, produces single complex stream out
+ that runs at M times the input sample rate
+
+ @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
+ gr.hier_block2.__init__(self, "synthesis_filterbank",
+ gr.io_signature(mpoints, mpoints, item_size), # Input signature
+ gr.io_signature(1, 1, item_size)) # Output signature
+
+
+ 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)
+
+ for i in range(mpoints):
+ self.connect((self, i), (self.ss2v, i))
+
+ self.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])
+ self.connect((self.v2ss, i), f)
+ self.connect(f, (self.ss2s, i))
+
+ self.connect(self.ss2s, self)
+
+class analysis_filterbank(gr.hier_block2):
+ """
+ Uniformly modulated polyphase DFT filter bank: analysis
+
+ See http://cnx.org/content/m10424/latest
+ """
+ def __init__(self, mpoints, taps=None):
+ """
+ Takes 1 complex stream in, produces M complex streams out
+ that runs at 1/M times the input sample rate
+
+ @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
+ gr.hier_block2.__init__(self, "analysis_filterbank",
+ gr.io_signature(1, 1, item_size), # Input signature
+ gr.io_signature(mpoints, mpoints, item_size)) # Output signature
+
+ 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)
+
+ self.connect(self, self.s2ss)
+
+ # build mpoints fir filters...
+ for i in range(mpoints):
+ f = gr.fft_filter_ccc(1, sub_taps[mpoints-i-1])
+ self.connect((self.s2ss, i), f)
+ self.connect(f, (self.ss2v, i))
+ self.connect((self.v2ss, i), (self, i))
+
+ self.connect(self.ss2v, self.fft, self.v2ss)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/fm_demod.py b/gnuradio-core/src/python/gnuradio/blks2impl/fm_demod.py
new file mode 100644
index 000000000..6bc0d7ed0
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/fm_demod.py
@@ -0,0 +1,111 @@
+#
+# Copyright 2006,2007 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, optfir
+from gnuradio.blks2impl.fm_emph import fm_deemph
+from math import pi
+
+class fm_demod_cf(gr.hier_block2):
+ """
+ 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 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, channel_rate, audio_decim, deviation,
+ audio_pass, audio_stop, gain=1.0, tau=75e-6):
+ gr.hier_block2.__init__(self, "fm_demod_cf",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Output signature
+
+ 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(channel_rate, tau)
+ self.connect(self, QUAD, DEEMPH, LPF, self)
+ else:
+ self.connect(self, QUAD, LPF, self)
+
+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 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, channel_rate, audio_decim):
+ fm_demod_cf.__init__(self, channel_rate, audio_decim,
+ 5000, # Deviation
+ 3000, # Audio passband frequency
+ 4500) # 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 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, channel_rate, audio_decim):
+ fm_demod_cf.__init__(self, channel_rate, audio_decim,
+ 75000, # Deviation
+ 15000, # Audio passband
+ 16000, # Audio stopband
+ 20.0) # Audio gain
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/fm_emph.py b/gnuradio-core/src/python/gnuradio/blks2impl/fm_emph.py
new file mode 100644
index 000000000..fc3f2d60d
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/fm_emph.py
@@ -0,0 +1,151 @@
+#
+# Copyright 2005,2007 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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_block2):
+ """
+ FM Deemphasis IIR filter.
+ """
+
+
+ def __init__(self, fs, tau=75e-6):
+ """
+ @param fs: sampling frequency in Hz
+ @type fs: float
+ @param tau: Time constant in seconds (75us in US, 50us in EUR)
+ @type tau: float
+ """
+ gr.hier_block2.__init__(self, "fm_deemph",
+ gr.io_signature(1, 1, gr.sizeof_float), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Output signature
+
+ 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)
+ self.connect(self, deemph, self)
+
+#
+# 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_block2):
+ """
+ FM Preemphasis IIR filter.
+ """
+ def __init__(self, fs, tau=75e-6):
+ """
+ @param fs: sampling frequency in Hz
+ @type fs: float
+ @param tau: Time constant in seconds (75us in US, 50us in EUR)
+ @type tau: float
+ """
+
+ gr.hier_block2.__init__(self, "fm_deemph",
+ gr.io_signature(1, 1, gr.sizeof_float), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Output signature
+
+ # 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)
+ self.connect(self, preemph, self)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/logpwrfft.py b/gnuradio-core/src/python/gnuradio/blks2impl/logpwrfft.py
new file mode 100644
index 000000000..6f7fc520f
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/logpwrfft.py
@@ -0,0 +1,155 @@
+#
+# Copyright 2008 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, window
+from stream_to_vector_decimator import stream_to_vector_decimator
+import math
+
+class _logpwrfft_base(gr.hier_block2):
+ """
+ Create a log10(abs(fft)) stream chain, with real or complex input.
+ """
+
+ def __init__(self, sample_rate, fft_size, ref_scale, frame_rate, avg_alpha, average, win=None):
+ """
+ Create an log10(abs(fft)) stream chain.
+ Provide access to the setting the filter and sample rate.
+ @param sample_rate Incoming stream sample rate
+ @param fft_size Number of FFT bins
+ @param ref_scale Sets 0 dB value input amplitude
+ @param frame_rate Output frame rate
+ @param avg_alpha FFT averaging (over time) constant [0.0-1.0]
+ @param average Whether to average [True, False]
+ @param win the window taps generation function
+ """
+ gr.hier_block2.__init__(self, self._name,
+ gr.io_signature(1, 1, self._item_size), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float*fft_size)) # Output signature
+
+ self._sd = stream_to_vector_decimator(item_size=self._item_size,
+ sample_rate=sample_rate,
+ vec_rate=frame_rate,
+ vec_len=fft_size)
+
+ if win is None: win = window.blackmanharris
+ fft_window = win(fft_size)
+ fft = self._fft_block[0](fft_size, True, fft_window)
+ window_power = sum(map(lambda x: x*x, fft_window))
+
+ c2magsq = gr.complex_to_mag_squared(fft_size)
+ self._avg = gr.single_pole_iir_filter_ff(1.0, fft_size)
+ self._log = gr.nlog10_ff(10, fft_size,
+ -20*math.log10(fft_size) # Adjust for number of bins
+ -10*math.log10(window_power/fft_size) # Adjust for windowing loss
+ -20*math.log10(ref_scale/2)) # Adjust for reference scale
+ self.connect(self, self._sd, fft, c2magsq, self._avg, self._log, self)
+
+ self._average = average
+ self._avg_alpha = avg_alpha
+ self.set_avg_alpha(avg_alpha)
+ self.set_average(average)
+
+ def set_decimation(self, decim):
+ """
+ Set the decimation on stream decimator.
+ @param decim the new decimation
+ """
+ self._sd.set_decimation(decim)
+
+ def set_vec_rate(self, vec_rate):
+ """
+ Set the vector rate on stream decimator.
+ @param vec_rate the new vector rate
+ """
+ self._sd.set_vec_rate(vec_rate)
+
+ def set_sample_rate(self, sample_rate):
+ """
+ Set the new sampling rate
+ @param sample_rate the new rate
+ """
+ self._sd.set_sample_rate(sample_rate)
+
+ def set_average(self, average):
+ """
+ Set the averaging filter on/off.
+ @param average true to set averaging on
+ """
+ self._average = average
+ if self._average:
+ self._avg.set_taps(self._avg_alpha)
+ else:
+ self._avg.set_taps(1.0)
+
+ def set_avg_alpha(self, avg_alpha):
+ """
+ Set the average alpha and set the taps if average was on.
+ @param avg_alpha the new iir filter tap
+ """
+ self._avg_alpha = avg_alpha
+ self.set_average(self._average)
+
+ def sample_rate(self):
+ """
+ Return the current sample rate.
+ """
+ return self._sd.sample_rate()
+
+ def decimation(self):
+ """
+ Return the current decimation.
+ """
+ return self._sd.decimation()
+
+ def frame_rate(self):
+ """
+ Return the current frame rate.
+ """
+ return self._sd.frame_rate()
+
+ def average(self):
+ """
+ Return whether or not averaging is being performed.
+ """
+ return self._average
+
+ def avg_alpha(self):
+ """
+ Return averaging filter constant.
+ """
+ return self._avg_alpha
+
+class logpwrfft_f(_logpwrfft_base):
+ """
+ Create an fft block chain, with real input.
+ """
+ _name = "logpwrfft_f"
+ _item_size = gr.sizeof_float
+ _fft_block = (gr.fft_vfc, )
+
+class logpwrfft_c(_logpwrfft_base):
+ """
+ Create an fft block chain, with complex input.
+ """
+ _name = "logpwrfft_c"
+ _item_size = gr.sizeof_gr_complex
+ _fft_block = (gr.fft_vcc, )
+
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/nbfm_rx.py b/gnuradio-core/src/python/gnuradio/blks2impl/nbfm_rx.py
new file mode 100644
index 000000000..8bcb47ae1
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/nbfm_rx.py
@@ -0,0 +1,88 @@
+#
+# 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import math
+from gnuradio import gr, optfir
+from gnuradio.blks2impl.fm_emph import fm_deemph
+#from gnuradio.blks2impl.standard_squelch import standard_squelch
+
+class nbfm_rx(gr.hier_block2):
+ def __init__(self, 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 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
+ """
+
+ gr.hier_block2.__init__(self, "nbfm_rx",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Output signature
+
+ # 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 (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
+ 2.7e3, # Audio LPF cutoff
+ 0.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)
+
+ self.connect(self, self.quad_demod, self.deemph, self.audio_filter, self)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/nbfm_tx.py b/gnuradio-core/src/python/gnuradio/blks2impl/nbfm_tx.py
new file mode 100644
index 000000000..839cf6784
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/nbfm_tx.py
@@ -0,0 +1,92 @@
+#
+# 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import math
+from gnuradio import gr, optfir
+from gnuradio.blks2impl.fm_emph import fm_preemph
+
+#from gnuradio import ctcss
+
+class nbfm_tx(gr.hier_block2):
+ def __init__(self, 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 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.
+ """
+
+ gr.hier_block2.__init__(self, "nbfm_tx",
+ gr.io_signature(1, 1, gr.sizeof_float), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature
+
+ # 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 (quad_rate, tau=tau)
+
+ k = 2 * math.pi * max_dev / quad_rate
+ self.modulator = gr.frequency_modulator_fc (k)
+
+ if do_interp:
+ self.connect (self, self.interpolator, self.preemph, self.modulator, self)
+ else:
+ self.connect(self, self.preemph, self.modulator, self)
+
+
+class ctcss_gen_f(gr.hier_block2):
+ def __init__(self, sample_rate, tone_freq):
+ gr.hier_block2.__init__(self, "ctcss_gen_f",
+ gr.io_signature(0, 0, 0), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Output signature
+
+ self.plgen = gr.sig_source_f(sample_rate, gr.GR_SIN_WAVE, tone_freq, 0.1, 0.0)
+ self.connect(self.plgen, self)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/pfb_arb_resampler.py b/gnuradio-core/src/python/gnuradio/blks2impl/pfb_arb_resampler.py
new file mode 100644
index 000000000..e83c327fc
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/pfb_arb_resampler.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python
+#
+# Copyright 2009 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, optfir
+
+class pfb_arb_resampler_ccf(gr.hier_block2):
+ '''
+ Convenience wrapper for the polyphase filterbank arbitrary resampler.
+
+ The block takes a single complex stream in and outputs a single complex
+ stream out. As such, it requires no extra glue to handle the input/output
+ streams. This block is provided to be consistent with the interface to the
+ other PFB block.
+ '''
+ def __init__(self, rate, taps=None, flt_size=32, atten=100):
+ gr.hier_block2.__init__(self, "pfb_arb_resampler_ccf",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature
+
+ self._rate = rate
+ self._size = flt_size
+
+ if taps is not None:
+ self._taps = taps
+ else:
+ # Create a filter that covers the full bandwidth of the input signal
+ bw = 0.4
+ tb = 0.2
+ ripple = 0.1
+ #self._taps = gr.firdes.low_pass_2(self._size, self._size, bw, tb, atten)
+ made = False
+ while not made:
+ try:
+ self._taps = optfir.low_pass(self._size, self._size, bw, bw+tb, ripple, atten)
+ made = True
+ except RuntimeError:
+ ripple += 0.01
+ made = False
+ print("Warning: set ripple to %.4f dB. If this is a problem, adjust the attenuation or create your own filter taps." % (ripple))
+
+ # Build in an exit strategy; if we've come this far, it ain't working.
+ if(ripple >= 1.0):
+ raise RuntimeError("optfir could not generate an appropriate filter.")
+
+ self.pfb = gr.pfb_arb_resampler_ccf(self._rate, self._taps, self._size)
+ #print "PFB has %d taps\n" % (len(self._taps),)
+
+ self.connect(self, self.pfb)
+ self.connect(self.pfb, self)
+
+ # Note -- set_taps not implemented in base class yet
+ def set_taps(self, taps):
+ self.pfb.set_taps(taps)
+
+ def set_rate(self, rate):
+ self.pfb.set_rate(rate)
+
+
+class pfb_arb_resampler_fff(gr.hier_block2):
+ '''
+ Convenience wrapper for the polyphase filterbank arbitrary resampler.
+
+ The block takes a single float stream in and outputs a single float
+ stream out. As such, it requires no extra glue to handle the input/output
+ streams. This block is provided to be consistent with the interface to the
+ other PFB block.
+ '''
+ def __init__(self, rate, taps=None, flt_size=32, atten=100):
+ gr.hier_block2.__init__(self, "pfb_arb_resampler_fff",
+ gr.io_signature(1, 1, gr.sizeof_float), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Output signature
+
+ self._rate = rate
+ self._size = flt_size
+
+ if taps is not None:
+ self._taps = taps
+ else:
+ # Create a filter that covers the full bandwidth of the input signal
+ bw = 0.4
+ tb = 0.2
+ ripple = 0.1
+ #self._taps = gr.firdes.low_pass_2(self._size, self._size, bw, tb, atten)
+ made = False
+ while not made:
+ try:
+ self._taps = optfir.low_pass(self._size, self._size, bw, bw+tb, ripple, atten)
+ made = True
+ except RuntimeError:
+ ripple += 0.01
+ made = False
+ print("Warning: set ripple to %.4f dB. If this is a problem, adjust the attenuation or create your own filter taps." % (ripple))
+
+ # Build in an exit strategy; if we've come this far, it ain't working.
+ if(ripple >= 1.0):
+ raise RuntimeError("optfir could not generate an appropriate filter.")
+
+ self.pfb = gr.pfb_arb_resampler_fff(self._rate, self._taps, self._size)
+ #print "PFB has %d taps\n" % (len(self._taps),)
+
+ self.connect(self, self.pfb)
+ self.connect(self.pfb, self)
+
+ # Note -- set_taps not implemented in base class yet
+ def set_taps(self, taps):
+ self.pfb.set_taps(taps)
+
+ def set_rate(self, rate):
+ self.pfb.set_rate(rate)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/pfb_channelizer.py b/gnuradio-core/src/python/gnuradio/blks2impl/pfb_channelizer.py
new file mode 100644
index 000000000..4bbe1bec6
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/pfb_channelizer.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python
+#
+# Copyright 2009,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, optfir
+
+class pfb_channelizer_ccf(gr.hier_block2):
+ '''
+ Make a Polyphase Filter channelizer (complex in, complex out, floating-point taps)
+
+ This simplifies the interface by allowing a single input stream to connect to this block.
+ It will then output a stream for each channel.
+ '''
+ def __init__(self, numchans, taps=None, oversample_rate=1, atten=100):
+ gr.hier_block2.__init__(self, "pfb_channelizer_ccf",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(numchans, numchans, gr.sizeof_gr_complex)) # Output signature
+
+ self._nchans = numchans
+ self._oversample_rate = oversample_rate
+
+ if taps is not None:
+ self._taps = taps
+ else:
+ # Create a filter that covers the full bandwidth of the input signal
+ bw = 0.4
+ tb = 0.2
+ ripple = 0.1
+ made = False
+ while not made:
+ try:
+ self._taps = optfir.low_pass(1, self._nchans, bw, bw+tb, ripple, atten)
+ made = True
+ except RuntimeError:
+ ripple += 0.01
+ made = False
+ print("Warning: set ripple to %.4f dB. If this is a problem, adjust the attenuation or create your own filter taps." % (ripple))
+
+ # Build in an exit strategy; if we've come this far, it ain't working.
+ if(ripple >= 1.0):
+ raise RuntimeError("optfir could not generate an appropriate filter.")
+
+ self.s2ss = gr.stream_to_streams(gr.sizeof_gr_complex, self._nchans)
+ self.pfb = gr.pfb_channelizer_ccf(self._nchans, self._taps,
+ self._oversample_rate)
+ self.connect(self, self.s2ss)
+
+ for i in xrange(self._nchans):
+ self.connect((self.s2ss,i), (self.pfb,i))
+ self.connect((self.pfb,i), (self,i))
+
+ def set_channel_map(self, newmap):
+ self.pfb.set_channel_map(newmap)
+
+
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/pfb_decimator.py b/gnuradio-core/src/python/gnuradio/blks2impl/pfb_decimator.py
new file mode 100644
index 000000000..adcdfe9ba
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/pfb_decimator.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+#
+# Copyright 2009 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, optfir
+
+class pfb_decimator_ccf(gr.hier_block2):
+ '''
+ Make a Polyphase Filter decimator (complex in, complex out, floating-point taps)
+
+ This simplifies the interface by allowing a single input stream to connect to this block.
+ It will then output a stream that is the decimated output stream.
+ '''
+ def __init__(self, decim, taps=None, channel=0, atten=100):
+ gr.hier_block2.__init__(self, "pfb_decimator_ccf",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature
+
+ self._decim = decim
+ self._channel = channel
+
+ if taps is not None:
+ self._taps = taps
+ else:
+ # Create a filter that covers the full bandwidth of the input signal
+ bw = 0.4
+ tb = 0.2
+ ripple = 0.1
+ made = False
+ while not made:
+ try:
+ self._taps = optfir.low_pass(1, self._decim, bw, bw+tb, ripple, atten)
+ made = True
+ except RuntimeError:
+ ripple += 0.01
+ made = False
+ print("Warning: set ripple to %.4f dB. If this is a problem, adjust the attenuation or create your own filter taps." % (ripple))
+
+ # Build in an exit strategy; if we've come this far, it ain't working.
+ if(ripple >= 1.0):
+ raise RuntimeError("optfir could not generate an appropriate filter.")
+
+ self.s2ss = gr.stream_to_streams(gr.sizeof_gr_complex, self._decim)
+ self.pfb = gr.pfb_decimator_ccf(self._decim, self._taps, self._channel)
+
+ self.connect(self, self.s2ss)
+
+ for i in xrange(self._decim):
+ self.connect((self.s2ss,i), (self.pfb,i))
+
+ self.connect(self.pfb, self)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/pfb_interpolator.py b/gnuradio-core/src/python/gnuradio/blks2impl/pfb_interpolator.py
new file mode 100644
index 000000000..5492dfcac
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/pfb_interpolator.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+#
+# Copyright 2009 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, optfir
+
+class pfb_interpolator_ccf(gr.hier_block2):
+ '''
+ Make a Polyphase Filter interpolator (complex in, complex out, floating-point taps)
+
+ The block takes a single complex stream in and outputs a single complex
+ stream out. As such, it requires no extra glue to handle the input/output
+ streams. This block is provided to be consistent with the interface to the
+ other PFB block.
+ '''
+ def __init__(self, interp, taps=None, atten=100):
+ gr.hier_block2.__init__(self, "pfb_interpolator_ccf",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature
+
+ self._interp = interp
+ self._taps = taps
+
+ if taps is not None:
+ self._taps = taps
+ else:
+ # Create a filter that covers the full bandwidth of the input signal
+ bw = 0.4
+ tb = 0.2
+ ripple = 0.99
+ made = False
+ while not made:
+ try:
+ self._taps = optfir.low_pass(self._interp, self._interp, bw, bw+tb, ripple, atten)
+ made = True
+ except RuntimeError:
+ ripple += 0.01
+ made = False
+ print("Warning: set ripple to %.4f dB. If this is a problem, adjust the attenuation or create your own filter taps." % (ripple))
+
+ # Build in an exit strategy; if we've come this far, it ain't working.
+ if(ripple >= 1.0):
+ raise RuntimeError("optfir could not generate an appropriate filter.")
+
+ self.pfb = gr.pfb_interpolator_ccf(self._interp, self._taps)
+
+ self.connect(self, self.pfb)
+ self.connect(self.pfb, self)
+
+
+
+
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/rational_resampler.py b/gnuradio-core/src/python/gnuradio/blks2impl/rational_resampler.py
new file mode 100644
index 000000000..eea12af95
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/rational_resampler.py
@@ -0,0 +1,131 @@
+#
+# Copyright 2005,2007 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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
+ """
+
+ 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
+ )
+
+ return taps
+
+
+
+class _rational_resampler_base(gr.hier_block2):
+ """
+ base class for all rational resampler variants.
+ """
+ def __init__(self, resampler_base,
+ 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 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_block2.__init__(self, "rational_resampler",
+ gr.io_signature(1, 1, resampler.input_signature().sizeof_stream_item(0)),
+ gr.io_signature(1, 1, resampler.output_signature().sizeof_stream_item(0)))
+
+ self.connect(self, resampler, self)
+
+
+class rational_resampler_fff(_rational_resampler_base):
+ def __init__(self, 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,
+ interpolation, decimation, taps, fractional_bw)
+
+class rational_resampler_ccf(_rational_resampler_base):
+ def __init__(self, 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,
+ interpolation, decimation, taps, fractional_bw)
+
+class rational_resampler_ccc(_rational_resampler_base):
+ def __init__(self, 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,
+ interpolation, decimation, taps, fractional_bw)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/standard_squelch.py b/gnuradio-core/src/python/gnuradio/blks2impl/standard_squelch.py
new file mode 100644
index 000000000..bd7fb535a
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/standard_squelch.py
@@ -0,0 +1,76 @@
+#
+# Copyright 2005,2007 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import math
+from gnuradio import gr, optfir
+
+class standard_squelch(gr.hier_block2):
+ def __init__(self, audio_rate):
+ gr.hier_block2.__init__(self, "standard_squelch",
+ gr.io_signature(1, 1, gr.sizeof_float), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Output signature
+
+ 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()
+
+ self.connect (self, self.input_node)
+ self.connect (self.input_node, (self.squelch_mult, 0))
+
+ self.connect (self.input_node,self.low_iir)
+ self.connect (self.low_iir,(self.low_square,0))
+ self.connect (self.low_iir,(self.low_square,1))
+ self.connect (self.low_square,self.low_smooth,(self.sub,0))
+ self.connect (self.low_smooth, (self.add,0))
+
+ self.connect (self.input_node,self.hi_iir)
+ self.connect (self.hi_iir,(self.hi_square,0))
+ self.connect (self.hi_iir,(self.hi_square,1))
+ self.connect (self.hi_square,self.hi_smooth,(self.sub,1))
+ self.connect (self.hi_smooth, (self.add,1))
+
+ self.connect (self.sub, (self.div, 0))
+ self.connect (self.add, (self.div, 1))
+ self.connect (self.div, self.gate, self.squelch_lpf, (self.squelch_mult,1))
+ self.connect (self.squelch_mult, self)
+
+ 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/blks2impl/stream_to_vector_decimator.py b/gnuradio-core/src/python/gnuradio/blks2impl/stream_to_vector_decimator.py
new file mode 100644
index 000000000..8f75729c9
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/stream_to_vector_decimator.py
@@ -0,0 +1,93 @@
+#
+# Copyright 2008 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr
+
+class stream_to_vector_decimator(gr.hier_block2):
+ """
+ Convert the stream to a vector, decimate the vector stream to achieve the vector rate.
+ """
+
+ def __init__(self, item_size, sample_rate, vec_rate, vec_len):
+ """
+ Create the block chain.
+ @param item_size the number of bytes per sample
+ @param sample_rate the rate of incoming samples
+ @param vec_rate the rate of outgoing vectors (same units as sample_rate)
+ @param vec_len the length of the outgoing vectors in items
+ """
+ self._vec_rate = vec_rate
+ self._vec_len = vec_len
+ self._sample_rate = sample_rate
+
+ gr.hier_block2.__init__(self, "stream_to_vector_decimator",
+ gr.io_signature(1, 1, item_size), # Input signature
+ gr.io_signature(1, 1, item_size*vec_len)) # Output signature
+
+ s2v = gr.stream_to_vector(item_size, vec_len)
+ self.one_in_n = gr.keep_one_in_n(item_size*vec_len, 1)
+ self._update_decimator()
+ self.connect(self, s2v, self.one_in_n, self)
+
+ def set_sample_rate(self, sample_rate):
+ """
+ Set the new sampling rate and update the decimator.
+ @param sample_rate the new rate
+ """
+ self._sample_rate = sample_rate
+ self._update_decimator()
+
+ def set_vec_rate(self, vec_rate):
+ """
+ Set the new vector rate and update the decimator.
+ @param vec_rate the new rate
+ """
+ self._vec_rate = vec_rate
+ self._update_decimator()
+
+ def set_decimation(self, decim):
+ """
+ Set the decimation parameter directly.
+ @param decim the new decimation
+ """
+ self._decim = max(1, int(round(decim)))
+ self.one_in_n.set_n(self._decim)
+
+ def _update_decimator(self):
+ self.set_decimation(self._sample_rate/self._vec_len/self._vec_rate)
+
+ def decimation(self):
+ """
+ Returns the actual decimation.
+ """
+ return self._decim
+
+ def sample_rate(self):
+ """
+ Returns configured sample rate.
+ """
+ return self._sample_rate
+
+ def frame_rate(self):
+ """
+ Returns actual frame rate
+ """
+ return self._sample_rate/self._vec_len/self._decim
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/wfm_rcv.py b/gnuradio-core/src/python/gnuradio/blks2impl/wfm_rcv.py
new file mode 100644
index 000000000..d1cbcf912
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/wfm_rcv.py
@@ -0,0 +1,69 @@
+#
+# Copyright 2005,2007 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr
+from gnuradio.blks2impl.fm_emph import fm_deemph
+import math
+
+class wfm_rcv(gr.hier_block2):
+ def __init__ (self, 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 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
+ """
+ gr.hier_block2.__init__(self, "wfm_rcv",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Output signature
+
+ 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 (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)
+
+ self.connect (self, self.fm_demod, self.audio_filter, self.deemph, self)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/wfm_rcv_fmdet.py b/gnuradio-core/src/python/gnuradio/blks2impl/wfm_rcv_fmdet.py
new file mode 100755
index 000000000..e229bcc2e
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/wfm_rcv_fmdet.py
@@ -0,0 +1,216 @@
+#
+# 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr
+from gnuradio.blks2impl.fm_emph import fm_deemph
+import math
+
+class wfm_rcv_fmdet(gr.hier_block2):
+ def __init__ (self, 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 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
+ """
+ gr.hier_block2.__init__(self, "wfm_rcv_fmdet",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(2, 2, gr.sizeof_float)) # Output signature
+ lowfreq = -125e3/demod_rate
+ highfreq = 125e3/demod_rate
+ 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
+
+ self.fm_demod = gr.fmdet_cf (demod_rate, lowfreq, highfreq, 0.05)
+
+ # input: float; output: float
+ self.deemph_Left = fm_deemph (audio_rate)
+ self.deemph_Right = fm_deemph (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.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);
+
+ loop_bw = 2*math.pi/100.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_refout_cc(loop_bw,
+ max_freq,
+ min_freq);
+
+ #self.stereo_carrier_pll_recovery.squelch_enable(False)
+ ##pll_refout does not have squelch yet, so disabled for
+ #now
+
+ # 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
+ self.connect (self, 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
+ # the resulting signal from this multiplier is the carrier
+ # with correct phase but at -38000 Hz.
+ self.connect (self.stereo_carrier_pll_recovery, (self.stereo_carrier_generator,1))
+
+ # send the new carrier to one side of the mixer (multiplier)
+ self.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
+ # the result is BASEBANDED DSBSC with phase zero!
+ self.connect (self.fm_demod,self.stereo_dsbsc_filter, (self.stereo_basebander,1))
+
+ # Pick off the real part since the imaginary is
+ # theoretically zero and then to one side of a summer
+ self.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
+ self.connect (self.LmR_real,(self.Make_Right,1))
+
+ # Make rds carrier by taking the squared pilot tone and
+ # multiplying by pilot tone
+ self.connect (self.stereo_basebander,(self.rds_carrier_generator,0))
+ self.connect (self.stereo_carrier_pll_recovery,(self.rds_carrier_generator,1))
+
+ # take signal, filter off rds, send into mixer 0 channel
+ self.connect (self.fm_demod,self.rds_signal_filter,(self.rds_signal_generator,0))
+
+ # take rds_carrier_generator output and send into mixer 1
+ # channel
+ self.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
+ self.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
+ self.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
+ self.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
+ self.connect(self.Make_Left , self.deemph_Left, (self, 0))
+ self.connect(self.Make_Right, self.deemph_Right, (self, 1))
+
+ # NOTE: mono support will require variable number of outputs in hier_block2s
+ # See ticket:174 in Trac database
+ #else:
+ # self.connect (self.fm_demod, self.audio_filter, self)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/wfm_rcv_pll.py b/gnuradio-core/src/python/gnuradio/blks2impl/wfm_rcv_pll.py
new file mode 100644
index 000000000..d4ce6d223
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/wfm_rcv_pll.py
@@ -0,0 +1,190 @@
+#
+# 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr
+from gnuradio.blks2impl.fm_emph import fm_deemph
+import math
+
+class wfm_rcv_pll(gr.hier_block2):
+ def __init__ (self, 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 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
+ """
+ gr.hier_block2.__init__(self, "wfm_rcv_pll",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(2, 2, gr.sizeof_float)) # Output signature
+ bandwidth = 250e3
+ 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
+ loop_bw = 2*math.pi/100.0
+ max_freq = 2.0*math.pi*90e3/demod_rate
+ self.fm_demod = gr.pll_freqdet_cf (loop_bw, max_freq,-max_freq)
+
+ # input: float; output: float
+ self.deemph_Left = fm_deemph (audio_rate)
+ self.deemph_Right = fm_deemph (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.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);
+
+
+
+ loop_bw = 2*math.pi/100.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_refout_cc(loop_bw, max_freq, min_freq);
+ #self.stereo_carrier_pll_recovery.squelch_enable(False) #pll_refout does not have squelch yet, so disabled for now
+
+
+ # 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
+ self.connect (self, 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
+ self.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)
+ self.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
+ self.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
+ self.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
+ self.connect (self.LmR_real,(self.Make_Right,1))
+
+ # Make rds carrier by taking the squared pilot tone and multiplying by pilot tone
+ self.connect (self.stereo_basebander,(self.rds_carrier_generator,0))
+ self.connect (self.stereo_carrier_pll_recovery,(self.rds_carrier_generator,1))
+ # take signal, filter off rds, send into mixer 0 channel
+ self.connect (self.fm_demod,self.rds_signal_filter,(self.rds_signal_generator,0))
+ # take rds_carrier_generator output and send into mixer 1 channel
+ self.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
+ self.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
+ self.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
+ self.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
+ self.connect(self.Make_Left , self.deemph_Left, (self, 0))
+ self.connect(self.Make_Right, self.deemph_Right, (self, 1))
+ # NOTE: mono support will require variable number of outputs in hier_block2s
+ # See ticket:174 in Trac database
+ #else:
+ # self.connect (self.fm_demod, self.audio_filter, self)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/wfm_tx.py b/gnuradio-core/src/python/gnuradio/blks2impl/wfm_tx.py
new file mode 100644
index 000000000..3fcf98f89
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/wfm_tx.py
@@ -0,0 +1,79 @@
+#
+# Copyright 2005,2007 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import math
+from gnuradio import gr, optfir
+from gnuradio.blks2impl.fm_emph import fm_preemph
+
+class wfm_tx(gr.hier_block2):
+ def __init__(self, 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 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.
+ """
+ gr.hier_block2.__init__(self, "wfm_tx",
+ gr.io_signature(1, 1, gr.sizeof_float), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature
+
+ # 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 (quad_rate, tau=tau)
+
+ k = 2 * math.pi * max_dev / quad_rate
+ self.modulator = gr.frequency_modulator_fc (k)
+
+ if do_interp:
+ self.connect (self, self.interpolator, self.preemph, self.modulator, self)
+ else:
+ self.connect(self, self.preemph, self.modulator, self)
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..c552a45f5
--- /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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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..02e9b0b6d
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/eng_option.py
@@ -0,0 +1,62 @@
+#
+# 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+'''Add support for engineering notation to optparse.OptionParser'''
+
+from copy import copy
+from optparse import Option, OptionValueError
+import eng_notation
+
+def check_eng_float (option, opt, value):
+ try:
+ return eng_notation.str_to_num(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), 'A:2' : (0, 2),
+ 'B' : (1, 0), 'B:0' : (1, 0), 'B:1' : (1, 1), 'B:2' : (1, 2) }
+ try:
+ return d[value.upper()]
+ except:
+ raise OptionValueError(
+ "option %s: invalid subdev: '%r', must be one of %s" % (opt, value, ', '.join(sorted(d.keys()))))
+
+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/CMakeLists.txt b/gnuradio-core/src/python/gnuradio/gr/CMakeLists.txt
new file mode 100644
index 000000000..da22a5f98
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/CMakeLists.txt
@@ -0,0 +1,49 @@
+# Copyright 2010-2012 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+
+########################################################################
+include(GrPython)
+
+GR_PYTHON_INSTALL(FILES
+ __init__.py
+ exceptions.py
+ gateway.py
+ gr_threading.py
+ gr_threading_23.py
+ gr_threading_24.py
+ hier_block2.py
+ prefs.py
+ tag_utils.py
+ top_block.py
+ pubsub.py
+ DESTINATION ${GR_PYTHON_DIR}/gnuradio/gr
+ COMPONENT "core_python"
+)
+
+########################################################################
+# Handle the unit tests
+########################################################################
+if(ENABLE_TESTING)
+include(GrTest)
+file(GLOB py_qa_test_files "qa_*.py")
+foreach(py_qa_test_file ${py_qa_test_files})
+ get_filename_component(py_qa_test_name ${py_qa_test_file} NAME_WE)
+ GR_ADD_TEST(${py_qa_test_name} ${PYTHON_EXECUTABLE} ${PYTHON_DASH_B} ${py_qa_test_file})
+endforeach(py_qa_test_file)
+endif(ENABLE_TESTING)
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..768d88595
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/__init__.py
@@ -0,0 +1,116 @@
+#
+# Copyright 2003-2012 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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_core import *
+from exceptions import *
+#from hier_block2 import *
+#from top_block import *
+from gateway import basic_block, sync_block, decim_block, interp_block
+from tag_utils import tag_to_python, tag_to_pmt
+
+import gras
+
+RT_OK = 0
+RT_NOT_IMPLEMENTED = 1
+RT_NO_PRIVS = 2
+RT_OTHER_ERROR = 3
+
+
+def enable_realtime_scheduling():
+ """
+ This call is for backward compat purposes.
+ See gras/thread_pool.hpp for greater options.
+ """
+
+ #any prio greater than 0 means realtime scheduling
+ prio_value = 0.5
+
+ #test that prio
+ if not gras.ThreadPool.test_thread_priority(prio_value):
+ return RT_NO_PRIVS
+
+ #create a new thread pool with thread priority set
+ config = gras.ThreadPoolConfig()
+ config.thread_priority = prio_value
+ tp = gras.ThreadPool(config)
+ tp.set_active()
+
+ return RT_OK
+
+class top_block(gras.TopBlock):
+ def __init__(self, name="Top"):
+ gras.TopBlock.__init__(self, name)
+
+ def lock(self):
+ pass
+
+ def unlock(self):
+ self.commit()
+
+ def start(self, *args):
+ if args: self.global_config().maximum_output_items = args[0]
+ gras.TopBlock.start(self)
+
+ def run(self, *args):
+ if args: self.global_config().maximum_output_items = args[0]
+ gras.TopBlock.run(self)
+
+class hier_block2(gras.HierBlock):
+ def __init__(self, name="Hier", in_sig=None, out_sig=None):
+ gras.HierBlock.__init__(self, name)
+
+ self.__in_sig = in_sig
+ self.__out_sig = out_sig
+
+ #backwards compatible silliness
+ import weakref
+ self._hb = weakref.proxy(self)
+
+ def lock(self):
+ pass
+
+ def unlock(self):
+ self.commit()
+
+ def input_signature(self): return self.__in_sig
+ def output_signature(self): return self.__out_sig
+
+# create a couple of aliases
+serial_to_parallel = stream_to_vector
+parallel_to_serial = vector_to_stream
+
+# Force the preference database to be initialized
+prefs = gr_prefs.singleton
+
+#alias old gr_add_vXX and gr_multiply_vXX
+add_vcc = add_cc
+add_vff = add_ff
+add_vii = add_ii
+add_vss = add_ss
+multiply_vcc = multiply_cc
+multiply_vff = multiply_ff
+multiply_vii = multiply_ii
+multiply_vss = multiply_ss
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..4fc10b721
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/benchmark_filters.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+#
+# Copyright 2005,2006,2007 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+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
+
+ tb = gr.top_block()
+ 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)
+ tb.connect(src, head, op, dst)
+ start = time.time()
+ tb.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..dba04750b
--- /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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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/gateway.py b/gnuradio-core/src/python/gnuradio/gr/gateway.py
new file mode 100644
index 000000000..53fda17a4
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/gateway.py
@@ -0,0 +1,246 @@
+#
+# Copyright 2011-2012 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import gnuradio_core as gr_core
+from gnuradio_core import io_signature, io_signaturev
+from gnuradio_core import gr_block_gw_message_type
+from gnuradio_core import block_gateway
+import numpy
+
+########################################################################
+# Magic to turn pointers into numpy arrays
+# http://docs.scipy.org/doc/numpy/reference/arrays.interface.html
+########################################################################
+def pointer_to_ndarray(addr, dtype, nitems):
+ class array_like:
+ __array_interface__ = {
+ 'data' : (int(addr), False),
+ 'typestr' : dtype.base.str,
+ 'descr' : dtype.base.descr,
+ 'shape' : (nitems,) + dtype.shape,
+ 'strides' : None,
+ 'version' : 3
+ }
+ return numpy.asarray(array_like()).view(dtype.base)
+
+########################################################################
+# Handler that does callbacks from C++
+########################################################################
+class gateway_handler(gr_core.feval_ll):
+
+ #dont put a constructor, it wont work
+
+ def init(self, callback):
+ self._callback = callback
+
+ def eval(self, arg):
+ try: self._callback()
+ except Exception as ex:
+ print("handler caught exception: %s"%ex)
+ import traceback; traceback.print_exc()
+ raise ex
+ return 0
+
+########################################################################
+# Handler that does callbacks from C++
+########################################################################
+class msg_handler(gr_core.feval_p):
+
+ #dont put a constructor, it wont work
+
+ def init(self, callback):
+ self._callback = callback
+
+ def eval(self, arg):
+ try: self._callback(arg)
+ except Exception as ex:
+ print("handler caught exception: %s"%ex)
+ import traceback; traceback.print_exc()
+ raise ex
+ return 0
+
+########################################################################
+# The guts that make this into a gr block
+########################################################################
+class gateway_block(object):
+
+ def __init__(self, name, in_sig, out_sig, work_type, factor):
+
+ #ensure that the sigs are iterable dtypes
+ def sig_to_dtype_sig(sig):
+ if sig is None: sig = ()
+ return map(numpy.dtype, sig)
+ self.__in_sig = sig_to_dtype_sig(in_sig)
+ self.__out_sig = sig_to_dtype_sig(out_sig)
+
+ #cache the ranges to iterate when dispatching work
+ self.__in_indexes = range(len(self.__in_sig))
+ self.__out_indexes = range(len(self.__out_sig))
+
+ #convert the signatures into gr.io_signatures
+ def sig_to_gr_io_sigv(sig):
+ if not len(sig): return io_signature(0, 0, 0)
+ return io_signaturev(len(sig), len(sig), [s.itemsize for s in sig])
+ gr_in_sig = sig_to_gr_io_sigv(self.__in_sig)
+ gr_out_sig = sig_to_gr_io_sigv(self.__out_sig)
+
+ #create internal gateway block
+ self.__handler = gateway_handler()
+ self.__handler.init(self.__gr_block_handle)
+ self.__gateway = block_gateway(
+ self.__handler, name, gr_in_sig, gr_out_sig, work_type, factor)
+ self.__message = self.__gateway.gr_block_message()
+
+ #dict to keep references to all message handlers
+ self.__msg_handlers = {}
+
+ #register gr_block functions
+ prefix = 'gr_block__'
+ for attr in [x for x in dir(self.__gateway) if x.startswith(prefix)]:
+ setattr(self, attr.replace(prefix, ''), getattr(self.__gateway, attr))
+ self.pop_msg_queue = lambda: gr_core.gr_block_gw_pop_msg_queue_safe(self.__gateway)
+
+ #gras version of the to_basic_block()
+ def to_element(self): return self.__gateway.to_element()
+
+ def to_basic_block(self):
+ """
+ Makes this block connectable by hier/top block python
+ """
+ return self.__gateway.to_basic_block()
+
+ def __gr_block_handle(self):
+ """
+ Dispatch tasks according to the action type specified in the message.
+ """
+ if self.__message.action == gr_block_gw_message_type.ACTION_GENERAL_WORK:
+ self.__message.general_work_args_return_value = self.general_work(
+
+ input_items=[pointer_to_ndarray(
+ self.__message.general_work_args_input_items[i],
+ self.__in_sig[i],
+ self.__message.general_work_args_ninput_items[i]
+ ) for i in self.__in_indexes],
+
+ output_items=[pointer_to_ndarray(
+ self.__message.general_work_args_output_items[i],
+ self.__out_sig[i],
+ self.__message.general_work_args_noutput_items
+ ) for i in self.__out_indexes],
+ )
+
+ elif self.__message.action == gr_block_gw_message_type.ACTION_WORK:
+ self.__message.work_args_return_value = self.work(
+
+ input_items=[pointer_to_ndarray(
+ self.__message.work_args_input_items[i],
+ self.__in_sig[i],
+ self.__message.work_args_ninput_items
+ ) for i in self.__in_indexes],
+
+ output_items=[pointer_to_ndarray(
+ self.__message.work_args_output_items[i],
+ self.__out_sig[i],
+ self.__message.work_args_noutput_items
+ ) for i in self.__out_indexes],
+ )
+
+ elif self.__message.action == gr_block_gw_message_type.ACTION_FORECAST:
+ self.forecast(
+ noutput_items=self.__message.forecast_args_noutput_items,
+ ninput_items_required=self.__message.forecast_args_ninput_items_required,
+ )
+
+ elif self.__message.action == gr_block_gw_message_type.ACTION_START:
+ self.__message.start_args_return_value = self.start()
+
+ elif self.__message.action == gr_block_gw_message_type.ACTION_STOP:
+ self.__message.stop_args_return_value = self.stop()
+
+ def forecast(self, noutput_items, ninput_items_required):
+ """
+ forecast is only called from a general block
+ this is the default implementation
+ """
+ for ninput_item in ninput_items_required:
+ ninput_item = noutput_items + self.history() - 1;
+ return
+
+ def general_work(self, *args, **kwargs):
+ """general work to be overloaded in a derived class"""
+ raise NotImplementedError("general work not implemented")
+
+ def work(self, *args, **kwargs):
+ """work to be overloaded in a derived class"""
+ raise NotImplementedError("work not implemented")
+
+ def start(self): return True
+ def stop(self): return True
+
+ def set_msg_handler(self, which_port, handler_func):
+ handler = msg_handler()
+ handler.init(handler_func)
+ self.__gateway.set_msg_handler_feval(which_port, handler)
+ # Save handler object in class so it's not garbage collected
+ self.__msg_handlers[which_port] = handler
+
+########################################################################
+# Wrappers for the user to inherit from
+########################################################################
+class basic_block(gateway_block):
+ def __init__(self, name, in_sig, out_sig):
+ gateway_block.__init__(self,
+ name=name,
+ in_sig=in_sig,
+ out_sig=out_sig,
+ work_type=gr_core.GR_BLOCK_GW_WORK_GENERAL,
+ factor=1, #not relevant factor
+ )
+
+class sync_block(gateway_block):
+ def __init__(self, name, in_sig, out_sig):
+ gateway_block.__init__(self,
+ name=name,
+ in_sig=in_sig,
+ out_sig=out_sig,
+ work_type=gr_core.GR_BLOCK_GW_WORK_SYNC,
+ factor=1,
+ )
+
+class decim_block(gateway_block):
+ def __init__(self, name, in_sig, out_sig, decim):
+ gateway_block.__init__(self,
+ name=name,
+ in_sig=in_sig,
+ out_sig=out_sig,
+ work_type=gr_core.GR_BLOCK_GW_WORK_DECIM,
+ factor=decim,
+ )
+
+class interp_block(gateway_block):
+ def __init__(self, name, in_sig, out_sig, interp):
+ gateway_block.__init__(self,
+ name=name,
+ in_sig=in_sig,
+ out_sig=out_sig,
+ work_type=gr_core.GR_BLOCK_GW_WORK_INTERP,
+ factor=interp,
+ )
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..5d6f0fdaf
--- /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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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_block2.py b/gnuradio-core/src/python/gnuradio/gr/hier_block2.py
new file mode 100644
index 000000000..b95782238
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/hier_block2.py
@@ -0,0 +1,129 @@
+#
+# Copyright 2006,2007 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio_core import hier_block2_swig
+try:
+ import pmt
+except ImportError:
+ from gruel import pmt
+
+#
+# This hack forces a 'has-a' relationship to look like an 'is-a' one.
+#
+# It allows Python classes to subclass this one, while passing through
+# method calls to the C++ class shared pointer from SWIG.
+#
+# It also allows us to intercept method calls if needed
+#
+class hier_block2(object):
+ """
+ Python wrapper around the C++ hierarchical block implementation.
+ Provides convenience functions and allows proper Python subclassing.
+ """
+
+ def __init__(self, name, input_signature, output_signature):
+ """
+ Create a hierarchical block with a given name and I/O signatures.
+ """
+ self._hb = hier_block2_swig(name, input_signature, output_signature)
+
+ def __getattr__(self, name):
+ """
+ Pass-through member requests to the C++ object.
+ """
+ if not hasattr(self, "_hb"):
+ raise RuntimeError("hier_block2: invalid state--did you forget to call gr.hier_block2.__init__ in a derived class?")
+ return getattr(self._hb, name)
+
+ def connect(self, *points):
+ """
+ Connect two or more block endpoints. An endpoint is either a (block, port)
+ tuple or a block instance. In the latter case, the port number is assumed
+ to be zero.
+
+ To connect the hierarchical block external inputs or outputs to internal block
+ inputs or outputs, use 'self' in the connect call.
+
+ If multiple arguments are provided, connect will attempt to wire them in series,
+ interpreting the endpoints as inputs or outputs as appropriate.
+ """
+
+ if len (points) < 1:
+ raise ValueError, ("connect requires at least one endpoint; %d provided." % (len (points),))
+ else:
+ if len(points) == 1:
+ self._hb.primitive_connect(points[0].to_basic_block())
+ else:
+ for i in range (1, len (points)):
+ self._connect(points[i-1], points[i])
+
+ def _connect(self, src, dst):
+ (src_block, src_port) = self._coerce_endpoint(src)
+ (dst_block, dst_port) = self._coerce_endpoint(dst)
+ self._hb.primitive_connect(src_block.to_basic_block(), src_port,
+ dst_block.to_basic_block(), dst_port)
+
+ def _coerce_endpoint(self, endp):
+ if hasattr(endp, 'to_basic_block'):
+ return (endp, 0)
+ else:
+ if hasattr(endp, "__getitem__") and len(endp) == 2:
+ return endp # Assume user put (block, port)
+ else:
+ raise ValueError("unable to coerce endpoint")
+
+ def disconnect(self, *points):
+ """
+ Disconnect two endpoints in the flowgraph.
+
+ To disconnect the hierarchical block external inputs or outputs to internal block
+ inputs or outputs, use 'self' in the connect call.
+
+ If more than two arguments are provided, they are disconnected successively.
+ """
+
+ if len (points) < 1:
+ raise ValueError, ("disconnect requires at least one endpoint; %d provided." % (len (points),))
+ else:
+ if len (points) == 1:
+ self._hb.primitive_disconnect(points[0].to_basic_block())
+ else:
+ for i in range (1, len (points)):
+ self._disconnect(points[i-1], points[i])
+
+ def _disconnect(self, src, dst):
+ (src_block, src_port) = self._coerce_endpoint(src)
+ (dst_block, dst_port) = self._coerce_endpoint(dst)
+ self._hb.primitive_disconnect(src_block.to_basic_block(), src_port,
+ dst_block.to_basic_block(), dst_port)
+
+ def msg_connect(self, src, srcport, dst, dstport):
+ self.primitive_msg_connect(src.to_basic_block(), srcport, dst.to_basic_block(), dstport);
+
+ def msg_disconnect(self, src, srcport, dst, dstport):
+ self.primitive_msg_disconnect(src.to_basic_block(), srcport, dst.to_basic_block(), dstport);
+
+ def message_port_register_hier_in(self, portname):
+ self.primitive_message_port_register_hier_in(pmt.pmt_intern(portname));
+
+ def message_port_register_hier_out(self, portname):
+ self.primitive_message_port_register_hier_out(pmt.pmt_intern(portname));
+
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..25fa8cd6a
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/prefs.py
@@ -0,0 +1,127 @@
+#
+# Copyright 2006,2009 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import gnuradio_core as gsp
+_prefs_base = gsp.gr_prefs
+
+
+import ConfigParser
+import os
+import os.path
+import sys
+import glob
+
+
+def _user_prefs_filename():
+ return os.path.expanduser('~/.gnuradio/config.conf')
+
+def _sys_prefs_dirname():
+ return gsp.prefsdir()
+
+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()
+ self.__getattr__ = lambda self, name: getattr(self.cp, name)
+
+ def _sys_prefs_filenames(self):
+ dir = _sys_prefs_dirname()
+ try:
+ fnames = glob.glob(os.path.join(dir, '*.conf'))
+ except (IOError, OSError):
+ return []
+ fnames.sort()
+ return fnames
+
+ def _read_files(self):
+ filenames = self._sys_prefs_filenames()
+ filenames.append(_user_prefs_filename())
+ #print "filenames: ", filenames
+ self.cp.read(filenames)
+
+ # ----------------------------------------------------------------
+ # 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/pubsub.py b/gnuradio-core/src/python/gnuradio/gr/pubsub.py
new file mode 100644
index 000000000..90568418f
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/pubsub.py
@@ -0,0 +1,153 @@
+#!/usr/bin/env python
+#
+# Copyright 2008,2009 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+"""
+Abstract GNU Radio publisher/subscriber interface
+
+This is a proof of concept implementation, will likely change significantly.
+"""
+
+class pubsub(dict):
+ def __init__(self):
+ self._publishers = { }
+ self._subscribers = { }
+ self._proxies = { }
+
+ def __missing__(self, key, value=None):
+ dict.__setitem__(self, key, value)
+ self._publishers[key] = None
+ self._subscribers[key] = []
+ self._proxies[key] = None
+
+ def __setitem__(self, key, val):
+ if not self.has_key(key):
+ self.__missing__(key, val)
+ elif self._proxies[key] is not None:
+ (p, newkey) = self._proxies[key]
+ p[newkey] = val
+ else:
+ dict.__setitem__(self, key, val)
+ for sub in self._subscribers[key]:
+ # Note this means subscribers will get called in the thread
+ # context of the 'set' caller.
+ sub(val)
+
+ def __getitem__(self, key):
+ if not self.has_key(key): self.__missing__(key)
+ if self._proxies[key] is not None:
+ (p, newkey) = self._proxies[key]
+ return p[newkey]
+ elif self._publishers[key] is not None:
+ return self._publishers[key]()
+ else:
+ return dict.__getitem__(self, key)
+
+ def publish(self, key, publisher):
+ if not self.has_key(key): self.__missing__(key)
+ if self._proxies[key] is not None:
+ (p, newkey) = self._proxies[key]
+ p.publish(newkey, publisher)
+ else:
+ self._publishers[key] = publisher
+
+ def subscribe(self, key, subscriber):
+ if not self.has_key(key): self.__missing__(key)
+ if self._proxies[key] is not None:
+ (p, newkey) = self._proxies[key]
+ p.subscribe(newkey, subscriber)
+ else:
+ self._subscribers[key].append(subscriber)
+
+ def unpublish(self, key):
+ if self._proxies[key] is not None:
+ (p, newkey) = self._proxies[key]
+ p.unpublish(newkey)
+ else:
+ self._publishers[key] = None
+
+ def unsubscribe(self, key, subscriber):
+ if self._proxies[key] is not None:
+ (p, newkey) = self._proxies[key]
+ p.unsubscribe(newkey, subscriber)
+ else:
+ self._subscribers[key].remove(subscriber)
+
+ def proxy(self, key, p, newkey=None):
+ if not self.has_key(key): self.__missing__(key)
+ if newkey is None: newkey = key
+ self._proxies[key] = (p, newkey)
+
+ def unproxy(self, key):
+ self._proxies[key] = None
+
+# Test code
+if __name__ == "__main__":
+ import sys
+ o = pubsub()
+
+ # Non-existent key gets auto-created with None value
+ print "Auto-created key 'foo' value:", o['foo']
+
+ # Add some subscribers
+ # First is a bare function
+ def print_len(x):
+ print "len=%i" % (len(x), )
+ o.subscribe('foo', print_len)
+
+ # The second is a class member function
+ class subber(object):
+ def __init__(self, param):
+ self._param = param
+ def printer(self, x):
+ print self._param, `x`
+ s = subber('param')
+ o.subscribe('foo', s.printer)
+
+ # The third is a lambda function
+ o.subscribe('foo', lambda x: sys.stdout.write('val='+`x`+'\n'))
+
+ # Update key 'foo', will notify subscribers
+ print "Updating 'foo' with three subscribers:"
+ o['foo'] = 'bar';
+
+ # Remove first subscriber
+ o.unsubscribe('foo', print_len)
+
+ # Update now will only trigger second and third subscriber
+ print "Updating 'foo' after removing a subscriber:"
+ o['foo'] = 'bar2';
+
+ # Publish a key as a function, in this case, a lambda function
+ o.publish('baz', lambda : 42)
+ print "Published value of 'baz':", o['baz']
+
+ # Unpublish the key
+ o.unpublish('baz')
+
+ # This will return None, as there is no publisher
+ print "Value of 'baz' with no publisher:", o['baz']
+
+ # Set 'baz' key, it gets cached
+ o['baz'] = 'bazzz'
+
+ # Now will return cached value, since no provider
+ print "Cached value of 'baz' after being set:", o['baz']
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..7ccbbe8ad
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_add_and_friends.py
@@ -0,0 +1,163 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_add_and_friends (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = 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.tb.connect (src, (op, s[0]))
+ dst = gr.vector_sink_i ()
+ self.tb.connect (op, dst)
+ self.tb.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.tb.connect (src, (op, s[0]))
+ dst = gr.vector_sink_f ()
+ self.tb.connect (op, dst)
+ self.tb.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.tb.connect (src, (op, s[0]))
+ dst = gr.vector_sink_c ()
+ self.tb.connect (op, dst)
+ self.tb.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_mult_const_ff (self):
+ src_data = (-1, 0, 1, 2, 3)
+ expected_result = (-5, 0, 5, 10, 15)
+ op = gr.multiply_const_cc (5)
+ self.help_cc ((src_data,), expected_result, op)
+
+ def test_mult_const_cc (self):
+ src_data = (-1-1j, 0+0j, 1+1j, 2+2j, 3+3j)
+ expected_result = (-5-5j, 0+0j, 5+5j, 10+10j, 15+15j)
+ op = gr.multiply_const_cc (5)
+ self.help_cc ((src_data,), expected_result, op)
+
+ def test_mult_const_cc2 (self):
+ src_data = (-1-1j, 0+0j, 1+1j, 2+2j, 3+3j)
+ expected_result = (-3-7j, 0+0j, 3+7j, 6+14j, 9+21j)
+ op = gr.multiply_const_cc (5+2j)
+ self.help_cc ((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_mult_ff (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_ff ()
+ self.help_ff ((src1_data, src2_data),
+ expected_result, op)
+
+ def test_mult_cc (self):
+ src1_data = (1+1j, 2+2j, 3+3j, 4+4j, 5+5j)
+ src2_data = (8, -3, 4, 8, 2)
+ expected_result = (8+8j, -6-6j, 12+12j, 32+32j, 10+10j)
+ op = gr.multiply_cc ()
+ self.help_cc ((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.run(test_add_and_friends, "test_add_and_friends.xml")
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..c8df47b39
--- /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,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_add_v_and_friends(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.tb = gr.top_block()
+
+ def tearDown(self):
+ self.tb = 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.tb.connect(src, srcv)
+ self.tb.connect(srcv, (op, s[0]))
+ rhs = gr.vector_to_stream(gr.sizeof_short, size)
+ dst = gr.vector_sink_s()
+ self.tb.connect(op, rhs, dst)
+ self.tb.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.tb.connect(src, srcv)
+ self.tb.connect(srcv, (op, s[0]))
+ rhs = gr.vector_to_stream(gr.sizeof_int, size)
+ dst = gr.vector_sink_i()
+ self.tb.connect(op, rhs, dst)
+ self.tb.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.tb.connect(src, srcv)
+ self.tb.connect(srcv, (op, s[0]))
+ rhs = gr.vector_to_stream(gr.sizeof_float, size)
+ dst = gr.vector_sink_f()
+ self.tb.connect(op, rhs, dst)
+ self.tb.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.tb.connect(src, srcv)
+ self.tb.connect(srcv, (op, s[0]))
+ rhs = gr.vector_to_stream(gr.sizeof_gr_complex, size)
+ dst = gr.vector_sink_c()
+ self.tb.connect(op, rhs, dst)
+ self.tb.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.tb.connect(src, srcv, op, rhs, dst)
+ self.tb.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.tb.connect(src, srcv, op, rhs, dst)
+ self.tb.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.tb.connect(src, srcv, op, rhs, dst)
+ self.tb.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.tb.connect(src, srcv, op, rhs, dst)
+ self.tb.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.run(test_add_v_and_friends, "test_add_v_and_friends.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_affinity.py b/gnuradio-core/src/python/gnuradio/gr/qa_affinity.py
new file mode 100644
index 000000000..7b3ca6ed6
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_affinity.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+#
+# Copyright 2013 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_affinity(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.tb = gr.top_block()
+
+ def tearDown(self):
+ self.tb = None
+
+ def test_000(self):
+ # Just run some data through and make sure it doesn't puke.
+ src_data = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
+
+ src = gr.vector_source_f(src_data)
+ snk = gr.vector_sink_f()
+
+ src.set_processor_affinity([0,])
+ self.tb.connect(src, snk)
+ self.tb.run()
+
+ a = src.processor_affinity()
+
+ self.assertEqual((0,), a)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_affinity, "test_affinity.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_agc.py b/gnuradio-core/src/python/gnuradio/gr/qa_agc.py
new file mode 100755
index 000000000..9fd633576
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_agc.py
@@ -0,0 +1,433 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import math
+
+test_output = False
+
+class test_agc (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+
+ def test_001(self):
+ ''' Test the complex AGC loop (single rate input) '''
+ tb = self.tb
+
+ expected_result = (
+ (100.000244140625+7.2191943445432116e-07j),
+ (72.892257690429688+52.959323883056641j),
+ (25.089065551757812+77.216217041015625j),
+ (-22.611061096191406+69.589706420898438j),
+ (-53.357715606689453+38.766635894775391j),
+ (-59.458671569824219+3.4792964243024471e-07j),
+ (-43.373462677001953-31.512666702270508j),
+ (-14.94139289855957-45.984889984130859j),
+ (13.478158950805664-41.48150634765625j),
+ (31.838506698608398-23.132022857666016j),
+ (35.519271850585938-3.1176801940091536e-07j),
+ (25.942903518676758+18.848621368408203j),
+ (8.9492912292480469+27.5430908203125j),
+ (-8.0852642059326172+24.883890151977539j),
+ (-19.131628036499023+13.899936676025391j),
+ (-21.383295059204102+3.1281737733479531e-07j),
+ (-15.650330543518066-11.370632171630859j),
+ (-5.4110145568847656-16.65339469909668j),
+ (4.9008159637451172-15.083160400390625j),
+ (11.628337860107422-8.4484796524047852j),
+ (13.036135673522949-2.288476110834381e-07j),
+ (9.5726661682128906+6.954948902130127j),
+ (3.3216962814331055+10.223132133483887j),
+ (-3.0204284191131592+9.2959251403808594j),
+ (-7.1977195739746094+5.2294478416442871j),
+ (-8.1072216033935547+1.8976157889483147e-07j),
+ (-5.9838657379150391-4.3475332260131836j),
+ (-2.0879747867584229-6.4261269569396973j),
+ (1.9100792407989502-5.8786196708679199j),
+ (4.5814824104309082-3.3286411762237549j),
+ (5.1967458724975586-1.3684227440080576e-07j),
+ (3.8647139072418213+2.8078789710998535j),
+ (1.3594740629196167+4.1840314865112305j),
+ (-1.2544282674789429+3.8607344627380371j),
+ (-3.0366206169128418+2.2062335014343262j),
+ (-3.4781389236450195+1.1194014604143376e-07j),
+ (-2.6133756637573242-1.8987287282943726j),
+ (-0.9293016791343689-2.8600969314575195j),
+ (0.86727333068847656-2.6691930294036865j),
+ (2.1243946552276611-1.5434627532958984j),
+ (2.4633183479309082-8.6486437567145913e-08j),
+ (1.8744727373123169+1.3618841171264648j),
+ (0.67528903484344482+2.0783262252807617j),
+ (-0.63866174221038818+1.965599536895752j),
+ (-1.5857341289520264+1.152103066444397j),
+ (-1.8640764951705933+7.6355092915036948e-08j),
+ (-1.4381576776504517-1.0448826551437378j),
+ (-0.52529704570770264-1.6166983842849731j),
+ (0.50366902351379395-1.5501341819763184j),
+ (1.26766037940979-0.92100900411605835j))
+
+ sampling_freq = 100
+ src1 = gr.sig_source_c (sampling_freq, gr.GR_SIN_WAVE,
+ sampling_freq * 0.10, 100.0)
+ dst1 = gr.vector_sink_c ()
+ head = gr.head (gr.sizeof_gr_complex, int (5*sampling_freq * 0.10))
+
+ agc = gr.agc_cc(1e-3, 1, 1, 1000)
+
+ tb.connect (src1, head)
+ tb.connect (head, agc)
+ tb.connect (agc, dst1)
+
+ if test_output == True:
+ tb.connect (agc, gr.file_sink(gr.sizeof_gr_complex, "test_agc_cc.dat"))
+
+ tb.run ()
+ dst_data = dst1.data ()
+ self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 4)
+
+ def test_002(self):
+ ''' Test the floating point AGC loop (single rate input) '''
+ tb = self.tb
+
+ expected_result = (
+ 7.2191943445432116e-07,
+ 58.837181091308594,
+ 89.700050354003906,
+ 81.264183044433594,
+ 45.506141662597656,
+ 4.269894304798072e-07,
+ -42.948936462402344,
+ -65.50335693359375,
+ -59.368724822998047,
+ -33.261005401611328,
+ -4.683740257860336e-07,
+ 31.423542022705078,
+ 47.950984954833984,
+ 43.485683441162109,
+ 24.378345489501953,
+ 5.7254135299444897e-07,
+ -23.062990188598633,
+ -35.218441009521484,
+ -31.964075088500977,
+ -17.934831619262695,
+ -5.0591745548445033e-07,
+ 16.998210906982422,
+ 25.982204437255859,
+ 23.606258392333984,
+ 13.260685920715332,
+ 4.9936483037527069e-07,
+ -12.59880542755127,
+ -19.28221321105957,
+ -17.54347038269043,
+ -9.8700437545776367,
+ -4.188150626305287e-07,
+ 9.4074573516845703,
+ 14.422011375427246,
+ 13.145503044128418,
+ 7.41046142578125,
+ 3.8512698097292741e-07,
+ -7.0924453735351562,
+ -10.896408081054688,
+ -9.9552040100097656,
+ -5.6262712478637695,
+ -3.1982864356905338e-07,
+ 5.4131259918212891,
+ 8.3389215469360352,
+ 7.6409502029418945,
+ 4.3320145606994629,
+ 2.882407841298118e-07,
+ -4.194943904876709,
+ -6.4837145805358887,
+ -5.9621825218200684,
+ -3.3931560516357422)
+
+ sampling_freq = 100
+ src1 = gr.sig_source_f (sampling_freq, gr.GR_SIN_WAVE,
+ sampling_freq * 0.10, 100.0)
+ dst1 = gr.vector_sink_f ()
+ head = gr.head (gr.sizeof_float, int (5*sampling_freq * 0.10))
+
+ agc = gr.agc_ff(1e-3, 1, 1, 1000)
+
+ tb.connect (src1, head)
+ tb.connect (head, agc)
+ tb.connect (agc, dst1)
+
+ if test_output == True:
+ tb.connect (agc, gr.file_sink(gr.sizeof_float, "test_agc_ff.dat"))
+
+ tb.run ()
+ dst_data = dst1.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, dst_data, 4)
+
+ def test_003(self):
+ ''' Test the complex AGC loop (attack and decay rate inputs) '''
+ tb = self.tb
+
+ expected_result = \
+ ((100.000244140625+7.2191943445432116e-07j),
+ (0.80881959199905396+0.58764183521270752j),
+ (0.30894950032234192+0.95084899663925171j),
+ (-0.30895623564720154+0.95086973905563354j),
+ (-0.80887287855148315+0.58768033981323242j),
+ (-0.99984413385391235+5.850709250410091e-09j),
+ (-0.80889981985092163-0.58770018815994263j),
+ (-0.30897706747055054-0.95093393325805664j),
+ (0.30898112058639526-0.95094609260559082j),
+ (0.80893135070800781-0.58772283792495728j),
+ (0.99990922212600708-8.7766354184282136e-09j),
+ (0.80894720554351807+0.58773452043533325j),
+ (0.30899339914321899+0.95098406076431274j),
+ (-0.30899572372436523+0.95099133253097534j),
+ (-0.80896598100662231+0.58774799108505249j),
+ (-0.99994778633117676+1.4628290578855285e-08j),
+ (-0.80897533893585205-0.58775502443313599j),
+ (-0.30900305509567261-0.95101380348205566j),
+ (0.30900448560714722-0.95101797580718994j),
+ (0.80898630619049072-0.58776277303695679j),
+ (0.99997037649154663-1.7554345532744264e-08j),
+ (0.80899184942245483+0.58776694536209106j),
+ (0.30900871753692627+0.95103120803833008j),
+ (-0.30900952219963074+0.95103377103805542j),
+ (-0.8089984655380249+0.58777159452438354j),
+ (-0.99998390674591064+2.3406109050938539e-08j),
+ (-0.809001624584198-0.58777409791946411j),
+ (-0.30901208519935608-0.95104163885116577j),
+ (0.30901262164115906-0.95104306936264038j),
+ (0.80900543928146362-0.587776780128479j),
+ (0.99999171495437622-2.6332081404234486e-08j),
+ (0.80900734663009644+0.58777821063995361j),
+ (0.30901408195495605+0.95104765892028809j),
+ (-0.30901429057121277+0.95104855298995972j),
+ (-0.80900967121124268+0.58777981996536255j),
+ (-0.99999648332595825+3.2183805842578295e-08j),
+ (-0.80901080369949341-0.58778077363967896j),
+ (-0.30901527404785156-0.95105135440826416j),
+ (0.30901545286178589-0.95105189085006714j),
+ (0.80901217460632324-0.58778166770935059j),
+ (0.99999916553497314-3.5109700036173308e-08j),
+ (0.809012770652771+0.58778214454650879j),
+ (0.30901595950126648+0.9510534405708313j),
+ (-0.30901598930358887+0.95105385780334473j),
+ (-0.80901366472244263+0.58778274059295654j),
+ (-1.0000008344650269+4.0961388947380328e-08j),
+ (-0.8090139627456665-0.58778303861618042j),
+ (-0.30901634693145752-0.95105475187301636j),
+ (0.30901640653610229-0.95105493068695068j),
+ (0.80901449918746948-0.5877833366394043j))
+
+ sampling_freq = 100
+ src1 = gr.sig_source_c (sampling_freq, gr.GR_SIN_WAVE,
+ sampling_freq * 0.10, 100)
+ dst1 = gr.vector_sink_c ()
+ head = gr.head (gr.sizeof_gr_complex, int (5*sampling_freq * 0.10))
+
+ agc = gr.agc2_cc(1e-2, 1e-3, 1, 1, 1000)
+
+ tb.connect (src1, head)
+ tb.connect (head, agc)
+ tb.connect (agc, dst1)
+
+ if test_output == True:
+ tb.connect (agc, gr.file_sink(gr.sizeof_gr_complex, "test_agc2_cc.dat"))
+
+ tb.run ()
+ dst_data = dst1.data ()
+ self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 4)
+
+ def test_004(self):
+ ''' Test the floating point AGC loop (attack and decay rate inputs) '''
+ tb = self.tb
+
+ expected_result = \
+ (7.2191943445432116e-07,
+ 58.837181091308594,
+ 40.194305419921875,
+ 2.9183335304260254,
+ 0.67606079578399658,
+ 8.6260438791896377e-09,
+ -1.4542514085769653,
+ -1.9210131168365479,
+ -1.0450780391693115,
+ -0.61939650774002075,
+ -1.2590258613442984e-08,
+ 1.4308931827545166,
+ 1.9054338932037354,
+ 1.0443156957626343,
+ 0.61937344074249268,
+ 2.0983527804219193e-08,
+ -1.4308838844299316,
+ -1.9054274559020996,
+ -1.0443152189254761,
+ -0.61937344074249268,
+ -2.5180233009791664e-08,
+ 1.4308837652206421,
+ 1.9054274559020996,
+ 1.0443154573440552,
+ 0.61937344074249268,
+ 3.3573645197293445e-08,
+ -1.4308838844299316,
+ -1.9054274559020996,
+ -1.0443152189254761,
+ -0.61937350034713745,
+ -3.7770352179222755e-08,
+ 1.4308837652206421,
+ 1.9054274559020996,
+ 1.0443154573440552,
+ 0.61937350034713745,
+ 4.6163762590367696e-08,
+ -1.4308838844299316,
+ -1.9054274559020996,
+ -1.0443153381347656,
+ -0.61937344074249268,
+ -5.0360466019583328e-08,
+ 1.4308837652206421,
+ 1.9054274559020996,
+ 1.0443155765533447,
+ 0.61937344074249268,
+ 5.8753879983441948e-08,
+ -1.4308837652206421,
+ -1.9054274559020996,
+ -1.0443153381347656,
+ -0.61937344074249268)
+
+ sampling_freq = 100
+ src1 = gr.sig_source_f (sampling_freq, gr.GR_SIN_WAVE,
+ sampling_freq * 0.10, 100)
+ dst1 = gr.vector_sink_f ()
+ head = gr.head (gr.sizeof_float, int (5*sampling_freq * 0.10))
+
+ agc = gr.agc2_ff(1e-2, 1e-3, 1, 1, 1000)
+
+ tb.connect (src1, head)
+ tb.connect (head, agc)
+ tb.connect (agc, dst1)
+
+ if test_output == True:
+ tb.connect (agc, gr.file_sink(gr.sizeof_float, "test_agc2_ff.dat"))
+
+ tb.run ()
+ dst_data = dst1.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, dst_data, 4)
+
+
+ def test_005(self):
+ ''' Test the complex AGC loop (attack and decay rate inputs) '''
+ tb = self.tb
+
+ expected_result = \
+ ((100.000244140625+7.2191943445432116e-07j),
+ (0.80881959199905396+0.58764183521270752j),
+ (0.30894950032234192+0.95084899663925171j),
+ (-0.30895623564720154+0.95086973905563354j),
+ (-0.80887287855148315+0.58768033981323242j),
+ (-0.99984413385391235+5.850709250410091e-09j),
+ (-0.80889981985092163-0.58770018815994263j),
+ (-0.30897706747055054-0.95093393325805664j),
+ (0.30898112058639526-0.95094609260559082j),
+ (0.80893135070800781-0.58772283792495728j),
+ (0.99990922212600708-8.7766354184282136e-09j),
+ (0.80894720554351807+0.58773452043533325j),
+ (0.30899339914321899+0.95098406076431274j),
+ (-0.30899572372436523+0.95099133253097534j),
+ (-0.80896598100662231+0.58774799108505249j),
+ (-0.99994778633117676+1.4628290578855285e-08j),
+ (-0.80897533893585205-0.58775502443313599j),
+ (-0.30900305509567261-0.95101380348205566j),
+ (0.30900448560714722-0.95101797580718994j),
+ (0.80898630619049072-0.58776277303695679j),
+ (0.99997037649154663-1.7554345532744264e-08j),
+ (0.80899184942245483+0.58776694536209106j),
+ (0.30900871753692627+0.95103120803833008j),
+ (-0.30900952219963074+0.95103377103805542j),
+ (-0.8089984655380249+0.58777159452438354j),
+ (-0.99998390674591064+2.3406109050938539e-08j),
+ (-0.809001624584198-0.58777409791946411j),
+ (-0.30901208519935608-0.95104163885116577j),
+ (0.30901262164115906-0.95104306936264038j),
+ (0.80900543928146362-0.587776780128479j),
+ (0.99999171495437622-2.6332081404234486e-08j),
+ (0.80900734663009644+0.58777821063995361j),
+ (0.30901408195495605+0.95104765892028809j),
+ (-0.30901429057121277+0.95104855298995972j),
+ (-0.80900967121124268+0.58777981996536255j),
+ (-0.99999648332595825+3.2183805842578295e-08j),
+ (-0.80901080369949341-0.58778077363967896j),
+ (-0.30901527404785156-0.95105135440826416j),
+ (0.30901545286178589-0.95105189085006714j),
+ (0.80901217460632324-0.58778166770935059j),
+ (0.99999916553497314-3.5109700036173308e-08j),
+ (0.809012770652771+0.58778214454650879j),
+ (0.30901595950126648+0.9510534405708313j),
+ (-0.30901598930358887+0.95105385780334473j),
+ (-0.80901366472244263+0.58778274059295654j),
+ (-1.0000008344650269+4.0961388947380328e-08j),
+ (-0.8090139627456665-0.58778303861618042j),
+ (-0.30901634693145752-0.95105475187301636j),
+ (0.30901640653610229-0.95105493068695068j),
+ (0.80901449918746948-0.5877833366394043j))
+
+ sampling_freq = 100
+ src1 = gr.sig_source_c (sampling_freq, gr.GR_SIN_WAVE,
+ sampling_freq * 0.10, 100)
+ dst1 = gr.vector_sink_c ()
+ head = gr.head (gr.sizeof_gr_complex, int (5*sampling_freq * 0.10))
+
+ agc = gr.agc2_cc(1e-2, 1e-3, 1, 1, 1000)
+
+ tb.connect (src1, head)
+ tb.connect (head, agc)
+ tb.connect (agc, dst1)
+
+ if test_output == True:
+ tb.connect (agc, gr.file_sink(gr.sizeof_gr_complex, "test_agc2_cc.dat"))
+
+ tb.run ()
+ dst_data = dst1.data ()
+ self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 4)
+
+
+ def test_100(self): # FIXME needs work
+ ''' Test complex feedforward agc with constant input '''
+ input_data = 16*(0.0,) + 64*(1.0,) + 64*(0.0,)
+ expected_result = ()
+
+ src = gr.vector_source_c(input_data)
+ agc = gr.feedforward_agc_cc(16, 2.0)
+ dst = gr.vector_sink_c ()
+ self.tb.connect (src, agc, dst)
+
+ if test_output == True:
+ self.tb.connect (agc, gr.file_sink(gr.sizeof_gr_complex, "test_feedforward_cc.dat"))
+
+ self.tb.run ()
+ dst_data = dst.data ()
+ #self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 4)
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_agc, "test_agc.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_argmax.py b/gnuradio-core/src/python/gnuradio/gr/qa_argmax.py
new file mode 100644
index 000000000..564eb620b
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_argmax.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+#
+# Copyright 2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import math
+
+
+class test_arg_max (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+
+ def tearDown (self):
+ self.tb = None
+
+
+ def test_001(self):
+ tb = self.tb
+
+ src1_data = (0,0.2,-0.3,0,12,0)
+ src2_data = (0,0.0,3.0,0,10,0)
+ src3_data = (0,0.0,3.0,0,1,0)
+
+ src1 = gr.vector_source_f (src1_data)
+ s2v1 = gr.stream_to_vector(gr.sizeof_float, len(src1_data))
+ tb.connect( src1, s2v1 )
+
+ src2 = gr.vector_source_f (src2_data)
+ s2v2 = gr.stream_to_vector(gr.sizeof_float, len(src1_data))
+ tb.connect( src2, s2v2 )
+
+ src3 = gr.vector_source_f (src3_data)
+ s2v3 = gr.stream_to_vector(gr.sizeof_float, len(src1_data))
+ tb.connect( src3, s2v3 )
+
+ dst1 = gr.vector_sink_s ()
+ dst2 = gr.vector_sink_s ()
+ argmax = gr.argmax_fs (len(src1_data))
+
+ tb.connect (s2v1, (argmax, 0))
+ tb.connect (s2v2, (argmax, 1))
+ tb.connect (s2v3, (argmax, 2))
+
+ tb.connect ((argmax,0), dst1)
+ tb.connect ((argmax,1), dst2)
+
+ tb.run ()
+ index = dst1.data ()
+ source = dst2.data ()
+ self.assertEqual ( index, (4,))
+ self.assertEqual ( source, (0,))
+
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_arg_max, "test_arg_max.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_bin_statistics.py b/gnuradio-core/src/python/gnuradio/gr/qa_bin_statistics.py
new file mode 100755
index 000000000..8a6dd9056
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_bin_statistics.py
@@ -0,0 +1,230 @@
+#!/usr/bin/env python
+#
+# Copyright 2006,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import random
+import struct
+
+#import os
+#print "pid =", os.getpid()
+#raw_input("Attach gdb and press return...")
+
+"""
+Note: The QA tests below have been disabled by renaming them from test_*
+to xtest_*. See ticket:199 on http://gnuradio.org/trac/ticket/199
+"""
+
+class counter(gr.feval_dd):
+ def __init__(self, step_size=1):
+ gr.feval_dd.__init__(self)
+ self.step_size = step_size
+ self.count = 0
+
+ def eval(self, input):
+ #print "eval: self.count =", self.count
+ t = self.count
+ self.count = self.count + self.step_size
+ return t
+
+
+class counter3(gr.feval_dd):
+ def __init__(self, f, step_size):
+ gr.feval_dd.__init__(self)
+ self.f = f
+ self.step_size = step_size
+ self.count = 0
+
+ def eval(self, input):
+ try:
+ #print "eval: self.count =", self.count
+ t = self.count
+ self.count = self.count + self.step_size
+ self.f(self.count)
+ except Exception, e:
+ print "Exception: ", e
+ return t
+
+def foobar3(new_t):
+ #print "foobar3: new_t =", new_t
+ pass
+
+
+class counter4(gr.feval_dd):
+ def __init__(self, obj_instance, step_size):
+ gr.feval_dd.__init__(self)
+ self.obj_instance = obj_instance
+ self.step_size = step_size
+ self.count = 0
+
+ def eval(self, input):
+ try:
+ #print "eval: self.count =", self.count
+ t = self.count
+ self.count = self.count + self.step_size
+ self.obj_instance.foobar4(self.count)
+ except Exception, e:
+ print "Exception: ", e
+ return t
+
+
+class parse_msg(object):
+ def __init__(self, msg):
+ self.center_freq = msg.arg1()
+ self.vlen = int(msg.arg2())
+ assert(msg.length() == self.vlen * gr.sizeof_float)
+ self.data = struct.unpack('%df' % (self.vlen,), msg.to_string())
+
+# FIXME: see ticket:199
+class xtest_bin_statistics(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.tb = gr.top_block ()
+
+ def tearDown(self):
+ self.tb = None
+
+ def xtest_001(self):
+ vlen = 4
+ tune = counter(1)
+ tune_delay = 0
+ dwell_delay = 1
+ msgq = gr.msg_queue()
+
+ src_data = tuple([float(x) for x in
+ ( 1, 2, 3, 4,
+ 5, 6, 7, 8,
+ 9, 10, 11, 12,
+ 13, 14, 15, 16
+ )])
+
+ expected_results = tuple([float(x) for x in
+ ( 1, 2, 3, 4,
+ 5, 6, 7, 8,
+ 9, 10, 11, 12,
+ 13, 14, 15, 16
+ )])
+
+ src = gr.vector_source_f(src_data, False)
+ s2v = gr.stream_to_vector(gr.sizeof_float, vlen)
+ stats = gr.bin_statistics_f(vlen, msgq, tune, tune_delay, dwell_delay)
+ self.tb.connect(src, s2v, stats)
+ self.tb.run()
+ self.assertEqual(4, msgq.count())
+ for i in range(4):
+ m = parse_msg(msgq.delete_head())
+ #print "m =", m.center_freq, m.data
+ self.assertEqual(expected_results[vlen*i:vlen*i + vlen], m.data)
+
+ def xtest_002(self):
+ vlen = 4
+ tune = counter(1)
+ tune_delay = 1
+ dwell_delay = 2
+ msgq = gr.msg_queue()
+
+ src_data = tuple([float(x) for x in
+ ( 1, 2, 3, 4,
+ 9, 6, 11, 8,
+ 5, 10, 7, 12,
+ 13, 14, 15, 16
+ )])
+
+ expected_results = tuple([float(x) for x in
+ ( 9, 10, 11, 12)])
+
+ src = gr.vector_source_f(src_data, False)
+ s2v = gr.stream_to_vector(gr.sizeof_float, vlen)
+ stats = gr.bin_statistics_f(vlen, msgq, tune, tune_delay, dwell_delay)
+ self.tb.connect(src, s2v, stats)
+ self.tb.run()
+ self.assertEqual(1, msgq.count())
+ for i in range(1):
+ m = parse_msg(msgq.delete_head())
+ #print "m =", m.center_freq, m.data
+ self.assertEqual(expected_results[vlen*i:vlen*i + vlen], m.data)
+
+
+
+ def xtest_003(self):
+ vlen = 4
+ tune = counter3(foobar3, 1)
+ tune_delay = 1
+ dwell_delay = 2
+ msgq = gr.msg_queue()
+
+ src_data = tuple([float(x) for x in
+ ( 1, 2, 3, 4,
+ 9, 6, 11, 8,
+ 5, 10, 7, 12,
+ 13, 14, 15, 16
+ )])
+
+ expected_results = tuple([float(x) for x in
+ ( 9, 10, 11, 12)])
+
+ src = gr.vector_source_f(src_data, False)
+ s2v = gr.stream_to_vector(gr.sizeof_float, vlen)
+ stats = gr.bin_statistics_f(vlen, msgq, tune, tune_delay, dwell_delay)
+ self.tb.connect(src, s2v, stats)
+ self.tb.run()
+ self.assertEqual(1, msgq.count())
+ for i in range(1):
+ m = parse_msg(msgq.delete_head())
+ #print "m =", m.center_freq, m.data
+ self.assertEqual(expected_results[vlen*i:vlen*i + vlen], m.data)
+
+
+ def foobar4(self, new_t):
+ #print "foobar4: new_t =", new_t
+ pass
+
+ def xtest_004(self):
+ vlen = 4
+ tune = counter4(self, 1)
+ tune_delay = 1
+ dwell_delay = 2
+ msgq = gr.msg_queue()
+
+ src_data = tuple([float(x) for x in
+ ( 1, 2, 3, 4,
+ 9, 6, 11, 8,
+ 5, 10, 7, 12,
+ 13, 14, 15, 16
+ )])
+
+ expected_results = tuple([float(x) for x in
+ ( 9, 10, 11, 12)])
+
+ src = gr.vector_source_f(src_data, False)
+ s2v = gr.stream_to_vector(gr.sizeof_float, vlen)
+ stats = gr.bin_statistics_f(vlen, msgq, tune, tune_delay, dwell_delay)
+ self.tb.connect(src, s2v, stats)
+ self.tb.run()
+ self.assertEqual(1, msgq.count())
+ for i in range(1):
+ m = parse_msg(msgq.delete_head())
+ #print "m =", m.center_freq, m.data
+ self.assertEqual(expected_results[vlen*i:vlen*i + vlen], m.data)
+
+
+if __name__ == '__main__':
+ gr_unittest.run(xtest_bin_statistics, "test_bin_statistics.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_block_gateway.py b/gnuradio-core/src/python/gnuradio/gr/qa_block_gateway.py
new file mode 100644
index 000000000..911879f6f
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_block_gateway.py
@@ -0,0 +1,235 @@
+#
+# Copyright 2011-2012 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import pmt
+import numpy
+
+class add_2_f32_1_f32(gr.sync_block):
+ def __init__(self):
+ gr.sync_block.__init__(
+ self,
+ name = "add 2 f32",
+ in_sig = [numpy.float32, numpy.float32],
+ out_sig = [numpy.float32],
+ )
+
+ def work(self, input_items, output_items):
+ output_items[0][:] = input_items[0] + input_items[1]
+ return len(output_items[0])
+
+class add_2_fc32_1_fc32(gr.sync_block):
+ def __init__(self):
+ gr.sync_block.__init__(
+ self,
+ name = "add 2 fc32",
+ in_sig = [numpy.complex64, numpy.complex64],
+ out_sig = [numpy.complex64],
+ )
+
+ def work(self, input_items, output_items):
+ output_items[0][:] = input_items[0] + input_items[1]
+ return len(output_items[0])
+
+class convolve(gr.sync_block):
+ """
+ A demonstration using block history to properly perform a convolution.
+ """
+ def __init__(self):
+ gr.sync_block.__init__(
+ self,
+ name = "convolve",
+ in_sig = [numpy.float32],
+ out_sig = [numpy.float32]
+ )
+ self._taps = [1, 0, 0, 0]
+ self.set_history(len(self._taps))
+
+ def work(self, input_items, output_items):
+ output_items[0][:] = numpy.convolve(input_items[0], self._taps, mode='valid')
+ return len(output_items[0])
+
+class decim2x(gr.decim_block):
+ def __init__(self):
+ gr.decim_block.__init__(
+ self,
+ name = "decim2x",
+ in_sig = [numpy.float32],
+ out_sig = [numpy.float32],
+ decim = 2
+ )
+
+ def work(self, input_items, output_items):
+ output_items[0][:] = input_items[0][::2]
+ return len(output_items[0])
+
+class interp2x(gr.interp_block):
+ def __init__(self):
+ gr.interp_block.__init__(
+ self,
+ name = "interp2x",
+ in_sig = [numpy.float32],
+ out_sig = [numpy.float32],
+ interp = 2
+ )
+
+ def work(self, input_items, output_items):
+ output_items[0][1::2] = input_items[0]
+ output_items[0][::2] = input_items[0]
+ return len(output_items[0])
+
+class tag_source(gr.sync_block):
+ def __init__(self):
+ gr.sync_block.__init__(
+ self,
+ name = "tag source",
+ in_sig = None,
+ out_sig = [numpy.float32],
+ )
+
+ def work(self, input_items, output_items):
+ num_output_items = len(output_items[0])
+
+ #put code here to fill the output items...
+
+ #make a new tag on the middle element every time work is called
+ count = self.nitems_written(0) + num_output_items/2
+ key = pmt.pmt_string_to_symbol("example_key")
+ value = pmt.pmt_string_to_symbol("example_value")
+ self.add_item_tag(0, count, key, value)
+
+ return num_output_items
+
+class tag_sink(gr.sync_block):
+ def __init__(self):
+ gr.sync_block.__init__(
+ self,
+ name = "tag sink",
+ in_sig = [numpy.float32],
+ out_sig = None,
+ )
+ self.key = None
+
+ def work(self, input_items, output_items):
+ num_input_items = len(input_items[0])
+
+ #put code here to process the input items...
+
+ #print all the tags received in this work call
+ nread = self.nitems_read(0)
+ tags = self.get_tags_in_range(0, nread, nread+num_input_items)
+ for tag in tags:
+ #print tag.offset
+ #print pmt.pmt_symbol_to_string(tag.key)
+ #print pmt.pmt_symbol_to_string(tag.value)
+ self.key = pmt.pmt_symbol_to_string(tag.key)
+
+ return num_input_items
+
+class fc32_to_f32_2(gr.sync_block):
+ def __init__(self):
+ gr.sync_block.__init__(
+ self,
+ name = "fc32_to_f32_2",
+ in_sig = [numpy.complex64],
+ out_sig = [(numpy.float32, 2)],
+ )
+
+ def work(self, input_items, output_items):
+ output_items[0][::,0] = numpy.real(input_items[0])
+ output_items[0][::,1] = numpy.imag(input_items[0])
+ return len(output_items[0])
+
+class test_block_gateway(gr_unittest.TestCase):
+
+ def test_add_f32(self):
+ tb = gr.top_block()
+ src0 = gr.vector_source_f([1, 3, 5, 7, 9], False)
+ src1 = gr.vector_source_f([0, 2, 4, 6, 8], False)
+ adder = add_2_f32_1_f32()
+ sink = gr.vector_sink_f()
+ tb.connect((src0, 0), (adder, 0))
+ tb.connect((src1, 0), (adder, 1))
+ tb.connect(adder, sink)
+ tb.run()
+ self.assertEqual(sink.data(), (1, 5, 9, 13, 17))
+
+ def test_add_fc32(self):
+ tb = gr.top_block()
+ src0 = gr.vector_source_c([1, 3j, 5, 7j, 9], False)
+ src1 = gr.vector_source_c([0, 2j, 4, 6j, 8], False)
+ adder = add_2_fc32_1_fc32()
+ sink = gr.vector_sink_c()
+ tb.connect((src0, 0), (adder, 0))
+ tb.connect((src1, 0), (adder, 1))
+ tb.connect(adder, sink)
+ tb.run()
+ self.assertEqual(sink.data(), (1, 5j, 9, 13j, 17))
+
+ def test_convolve(self):
+ tb = gr.top_block()
+ src = gr.vector_source_f([1, 2, 3, 4, 5, 6, 7, 8], False)
+ cv = convolve()
+ sink = gr.vector_sink_f()
+ tb.connect(src, cv, sink)
+ tb.run()
+ self.assertEqual(sink.data(), (1, 2, 3, 4, 5, 6, 7, 8))
+
+ def test_decim2x(self):
+ tb = gr.top_block()
+ src = gr.vector_source_f([1, 2, 3, 4, 5, 6, 7, 8], False)
+ d2x = decim2x()
+ sink = gr.vector_sink_f()
+ tb.connect(src, d2x, sink)
+ tb.run()
+ self.assertEqual(sink.data(), (1, 3, 5, 7))
+
+ def test_interp2x(self):
+ tb = gr.top_block()
+ src = gr.vector_source_f([1, 3, 5, 7, 9], False)
+ i2x = interp2x()
+ sink = gr.vector_sink_f()
+ tb.connect(src, i2x, sink)
+ tb.run()
+ self.assertEqual(sink.data(), (1, 1, 3, 3, 5, 5, 7, 7, 9, 9))
+
+ def test_tags(self):
+ src = tag_source()
+ sink = tag_sink()
+ head = gr.head(gr.sizeof_float, 50000) #should be enough items to get a tag through
+ tb = gr.top_block()
+ tb.connect(src, head, sink)
+ tb.run()
+ self.assertEqual(sink.key, "example_key")
+
+ def test_fc32_to_f32_2(self):
+ tb = gr.top_block()
+ src = gr.vector_source_c([1+2j, 3+4j, 5+6j, 7+8j, 9+10j], False)
+ convert = fc32_to_f32_2()
+ v2s = gr.vector_to_stream(gr.sizeof_float, 2)
+ sink = gr.vector_sink_f()
+ tb.connect(src, convert, v2s, sink)
+ tb.run()
+ self.assertEqual(sink.data(), (1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
+
+if __name__ == '__main__':
+ gr_unittest.run(test_block_gateway, "test_block_gateway.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_boolean_operators.py b/gnuradio-core/src/python/gnuradio/gr/qa_boolean_operators.py
new file mode 100755
index 000000000..d7d134dcb
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_boolean_operators.py
@@ -0,0 +1,162 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2007,2008,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_boolean_operators (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def help_ss (self, src_data, exp_data, op):
+ for s in zip (range (len (src_data)), src_data):
+ src = gr.vector_source_s (s[1])
+ self.tb.connect (src, (op, s[0]))
+ dst = gr.vector_sink_s ()
+ self.tb.connect (op, dst)
+ self.tb.run ()
+ result_data = dst.data ()
+ self.assertEqual (exp_data, result_data)
+
+ def help_bb (self, src_data, exp_data, op):
+ for s in zip (range (len (src_data)), src_data):
+ src = gr.vector_source_b (s[1])
+ self.tb.connect (src, (op, s[0]))
+ dst = gr.vector_sink_b ()
+ self.tb.connect (op, dst)
+ self.tb.run ()
+ result_data = dst.data ()
+ self.assertEqual (exp_data, result_data)
+
+ 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.tb.connect (src, (op, s[0]))
+ dst = gr.vector_sink_i ()
+ self.tb.connect (op, dst)
+ self.tb.run ()
+ result_data = dst.data ()
+ self.assertEqual (exp_data, result_data)
+
+ def test_xor_ss (self):
+ src1_data = (1, 2, 3, 0x5004, 0x1150)
+ src2_data = (8, 2, 1 , 0x0508, 0x1105)
+ expected_result = (9, 0, 2, 0x550C, 0x0055)
+ op = gr.xor_ss ()
+ self.help_ss ((src1_data, src2_data),
+ expected_result, op)
+
+ def test_xor_bb (self):
+ src1_data = (1, 2, 3, 4, 0x50)
+ src2_data = (8, 2, 1 , 8, 0x05)
+ expected_result = (9, 0, 2, 0xC, 0x55)
+ op = gr.xor_bb ()
+ self.help_bb ((src1_data, src2_data),
+ expected_result, op)
+
+
+ def test_xor_ii (self):
+ src1_data = (1, 2, 3, 0x5000004, 0x11000050)
+ src2_data = (8, 2, 1 , 0x0500008, 0x11000005)
+ expected_result = (9, 0, 2, 0x550000C, 0x00000055)
+ op = gr.xor_ii ()
+ self.help_ii ((src1_data, src2_data),
+ expected_result, op)
+
+ def test_and_ss (self):
+ src1_data = (1, 2, 3, 0x5004, 0x1150)
+ src2_data = (8, 2, 1 , 0x0508, 0x1105)
+ expected_result = (0, 2, 1, 0x0000, 0x1100)
+ op = gr.and_ss ()
+ self.help_ss ((src1_data, src2_data),
+ expected_result, op)
+
+ def test_and_bb (self):
+ src1_data = (1, 2, 2, 3, 0x04, 0x50)
+ src2_data = (8, 2, 2, 1, 0x08, 0x05)
+ src3_data = (8, 2, 1, 1, 0x08, 0x05)
+ expected_result = (0, 2, 0, 1, 0x00, 0x00)
+ op = gr.and_bb ()
+ self.help_bb ((src1_data, src2_data, src3_data),
+ expected_result, op)
+
+ def test_and_ii (self):
+ src1_data = (1, 2, 3, 0x50005004, 0x11001150)
+ src2_data = (8, 2, 1 , 0x05000508, 0x11001105)
+ expected_result = (0, 2, 1, 0x00000000, 0x11001100)
+ op = gr.and_ii ()
+ self.help_ii ((src1_data, src2_data),
+ expected_result, op)
+
+ def test_or_ss (self):
+ src1_data = (1, 2, 3, 0x5004, 0x1150)
+ src2_data = (8, 2, 1 , 0x0508, 0x1105)
+ expected_result = (9, 2, 3, 0x550C, 0x1155)
+ op = gr.or_ss ()
+ self.help_ss ((src1_data, src2_data),
+ expected_result, op)
+
+ def test_or_bb (self):
+ src1_data = (1, 2, 2, 3, 0x04, 0x50)
+ src2_data = (8, 2, 2, 1 , 0x08, 0x05)
+ src3_data = (8, 2, 1, 1 , 0x08, 0x05)
+ expected_result = (9, 2, 3, 3, 0x0C, 0x55)
+ op = gr.or_bb ()
+ self.help_bb ((src1_data, src2_data, src3_data),
+ expected_result, op)
+
+ def test_or_ii (self):
+ src1_data = (1, 2, 3, 0x50005004, 0x11001150)
+ src2_data = (8, 2, 1 , 0x05000508, 0x11001105)
+ expected_result = (9, 2, 3, 0x5500550C, 0x11001155)
+ op = gr.or_ii ()
+ self.help_ii ((src1_data, src2_data),
+ expected_result, op)
+
+ def test_not_ss (self):
+ src1_data = (1, 2, 3, 0x5004, 0x1150)
+ expected_result = (~1, ~2, ~3, ~0x5004, ~0x1150)
+ op = gr.not_ss ()
+ self.help_ss ((((src1_data),)),
+ expected_result, op)
+
+ def test_not_bb (self):
+ src1_data = (1, 2, 2, 3, 0x04, 0x50)
+ expected_result = (0xFE, 0xFD, 0xFD, 0xFC, 0xFB, 0xAF)
+ op = gr.not_bb ()
+ self.help_bb (((src1_data), ),
+ expected_result, op)
+
+ def test_not_ii (self):
+ src1_data = (1, 2, 3, 0x50005004, 0x11001150)
+ expected_result = (~1 , ~2, ~3, ~0x50005004, ~0x11001150)
+ op = gr.not_ii ()
+ self.help_ii (((src1_data),),
+ expected_result, op)
+
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_boolean_operators, "test_boolean_operators.xml")
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..946c0d7f8
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_complex_to_xxx.py
@@ -0,0 +1,142 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import math
+
+class test_complex_ops (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = 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.tb.connect (src, op)
+ self.tb.connect (op, dst)
+ self.tb.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.tb.connect (src, op)
+ self.tb.connect ((op, 0), dst0)
+ self.tb.connect ((op, 1), dst1)
+ self.tb.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.tb.connect (src, op)
+ self.tb.connect (op, dst)
+ self.tb.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.tb.connect (src, op)
+ self.tb.connect (op, dst)
+ self.tb.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.tb.connect (src, op)
+ self.tb.connect (op, dst)
+ self.tb.run ()
+ actual_result = dst.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, actual_result,5)
+
+ def test_complex_to_mag_squared (self):
+ src_data = (0, 1, -1, 3+4j, -3-4j, -3+4j)
+ expected_result = (0, 1, 1, 25, 25, 25)
+ src = gr.vector_source_c (src_data)
+ op = gr.complex_to_mag_squared ()
+ dst = gr.vector_sink_f ()
+ self.tb.connect (src, op)
+ self.tb.connect (op, dst)
+ self.tb.run ()
+ actual_result = dst.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, actual_result,5)
+
+ def test_complex_to_arg (self):
+ pi = math.pi
+ input_data = (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)
+
+ expected_result = (0.0, # 0
+ 0.52382522821426392, # pi/6
+ 0.78539806604385376, # pi/4
+ 1.5707963705062866, # pi/2
+ 2.3561947345733643, # 3pi/4
+ 2.7491819858551025, # 7pi/8
+ -0.52382522821426392, # -pi/6
+ -0.78539806604385376, # -pi/4
+ -1.5707963705062866, # -pi/2
+ -2.3561947345733643, # -3pi/4
+ -2.7491819858551025) # -7pi/8
+
+ src_data = tuple ([math.cos (x) + math.sin (x) * 1j for x in input_data])
+ src = gr.vector_source_c (src_data)
+ op = gr.complex_to_arg ()
+ dst = gr.vector_sink_f ()
+ self.tb.connect (src, op)
+ self.tb.connect (op, dst)
+ self.tb.run ()
+ actual_result = dst.data ()
+
+ self.assertFloatTuplesAlmostEqual (expected_result, actual_result, 3)
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_complex_ops, "test_complex_ops.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_conjugate.py b/gnuradio-core/src/python/gnuradio/gr/qa_conjugate.py
new file mode 100644
index 000000000..17fa891e2
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_conjugate.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+#
+# Copyright 2012 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_conjugate (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_000 (self):
+ src_data = (-2-2j, -1-1j, -2+2j, -1+1j,
+ 2-2j, 1-1j, 2+2j, 1+1j,
+ 0+0j)
+
+ exp_data = (-2+2j, -1+1j, -2-2j, -1-1j,
+ 2+2j, 1+1j, 2-2j, 1-1j,
+ 0-0j)
+
+ src = gr.vector_source_c(src_data)
+ op = gr.conjugate_cc ()
+ dst = gr.vector_sink_c ()
+
+ self.tb.connect(src, op)
+ self.tb.connect(op, dst)
+ self.tb.run()
+ result_data = dst.data ()
+ self.assertEqual (exp_data, result_data)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_conjugate, "test_conjugate.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_copy.py b/gnuradio-core/src/python/gnuradio/gr/qa_copy.py
new file mode 100755
index 000000000..68c8e451f
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_copy.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+#
+# Copyright 2009,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_copy(gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_copy (self):
+ src_data = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
+ expected_result = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
+ src = gr.vector_source_b(src_data)
+ op = gr.copy(gr.sizeof_char)
+ dst = gr.vector_sink_b()
+ self.tb.connect(src, op, dst)
+ self.tb.run()
+ dst_data = dst.data()
+ self.assertEqual(expected_result, dst_data)
+
+ def test_copy_drop (self):
+ src_data = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
+ expected_result = ()
+ src = gr.vector_source_b(src_data)
+ op = gr.copy(gr.sizeof_char)
+ op.set_enabled(False)
+ dst = gr.vector_sink_b()
+ self.tb.connect(src, op, dst)
+ self.tb.run()
+ dst_data = dst.data()
+ self.assertEqual(expected_result, dst_data)
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_copy, "test_copy.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_dc_blocker.py b/gnuradio-core/src/python/gnuradio/gr/qa_dc_blocker.py
new file mode 100755
index 000000000..175735867
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_dc_blocker.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+#
+# Copyright 2011 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_dc_blocker(gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_001(self):
+ ''' Test impulse response - long form, cc '''
+ src_data = [1,] + 100*[0,]
+ expected_result = ((-0.02072429656982422+0j), (-0.02081298828125+0j),
+ (0.979156494140625+0j), (-0.02081298828125+0j),
+ (-0.02072429656982422+0j))
+
+ src = gr.vector_source_c(src_data)
+ op = gr.dc_blocker_cc(32, True)
+ dst = gr.vector_sink_c()
+
+ self.tb.connect (src, op, dst)
+ self.tb.run()
+
+ # only test samples around 2D-2
+ result_data = dst.data()[60:65]
+ self.assertComplexTuplesAlmostEqual (expected_result, result_data)
+
+ def test_002(self):
+ ''' Test impulse response - short form, cc '''
+ src_data = [1,] + 100*[0,]
+ expected_result = ((-0.029296875+0j), (-0.0302734375+0j),
+ (0.96875+0j), (-0.0302734375+0j),
+ (-0.029296875+0j))
+
+ src = gr.vector_source_c(src_data)
+ op = gr.dc_blocker_cc(32, False)
+ dst = gr.vector_sink_c()
+
+ self.tb.connect (src, op, dst)
+ self.tb.run()
+
+ # only test samples around D-1
+ result_data = dst.data()[29:34]
+ self.assertComplexTuplesAlmostEqual (expected_result, result_data)
+
+
+ def test_003(self):
+ ''' Test impulse response - long form, ff '''
+ src_data = [1,] + 100*[0,]
+ expected_result = ((-0.02072429656982422), (-0.02081298828125),
+ (0.979156494140625), (-0.02081298828125),
+ (-0.02072429656982422))
+
+ src = gr.vector_source_f(src_data)
+ op = gr.dc_blocker_ff(32, True)
+ dst = gr.vector_sink_f()
+
+ self.tb.connect (src, op, dst)
+ self.tb.run()
+
+ # only test samples around 2D-2
+ result_data = dst.data()[60:65]
+ self.assertFloatTuplesAlmostEqual (expected_result, result_data)
+
+ def test_004(self):
+ ''' Test impulse response - short form, ff '''
+ src_data = [1,] + 100*[0,]
+ expected_result = ((-0.029296875), (-0.0302734375),
+ (0.96875), (-0.0302734375),
+ (-0.029296875))
+
+ src = gr.vector_source_f(src_data)
+ op = gr.dc_blocker_ff(32, False)
+ dst = gr.vector_sink_f()
+
+ self.tb.connect (src, op, dst)
+ self.tb.run()
+
+ # only test samples around D-1
+ result_data = dst.data()[29:34]
+ self.assertFloatTuplesAlmostEqual (expected_result, result_data)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_dc_blocker, "test_dc_blocker.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_delay.py b/gnuradio-core/src/python/gnuradio/gr/qa_delay.py
new file mode 100755
index 000000000..0d0bc1330
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_delay.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import math
+
+class test_delay (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_000 (self):
+ delta_t = 0
+ tb = self.tb
+ src_data = [float(x) for x in range(0, 100)]
+ expected_result = tuple(delta_t*[0.0] + src_data)
+
+ src = gr.vector_source_f(src_data)
+ op = gr.delay(gr.sizeof_float, delta_t)
+ dst = gr.vector_sink_f ()
+
+ tb.connect (src, op, dst)
+ tb.run ()
+ dst_data = dst.data ()
+ self.assertEqual (expected_result, dst_data)
+
+ def test_010 (self):
+ delta_t = 10
+ tb = self.tb
+ src_data = [float(x) for x in range(0, 100)]
+ expected_result = tuple(delta_t*[0.0] + src_data)
+
+ src = gr.vector_source_f(src_data)
+ op = gr.delay(gr.sizeof_float, delta_t)
+ dst = gr.vector_sink_f ()
+
+ tb.connect (src, op, dst)
+ tb.run ()
+ dst_data = dst.data ()
+ self.assertEqual (expected_result, dst_data)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_delay, "test_delay.xml")
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..c1fe2a700
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_diff_encoder.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+#
+# Copyright 2006,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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_diff_encoder (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = 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.tb.connect(src, enc, dec, dst)
+ self.tb.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.tb.connect(src, enc, dec, dst)
+ self.tb.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.tb.connect(src, enc, dec, dst)
+ self.tb.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.run(test_diff_encoder, "test_diff_encoder.xml")
+
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..41f96aa61
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_diff_phasor_cc.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import math
+
+class test_diff_phasor (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = 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.tb.connect (src, op)
+ self.tb.connect (op, dst)
+ self.tb.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.run(test_diff_phasor, "test_diff_phasor.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_ecc_ccsds_27.py b/gnuradio-core/src/python/gnuradio/gr/qa_ecc_ccsds_27.py
new file mode 100755
index 000000000..29122ff3e
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_ecc_ccsds_27.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_ccsds_27 (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def xtest_ccsds_27 (self):
+ src_data = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
+ expected = (0, 0, 0, 0, 1, 2, 3, 4, 5, 6)
+ src = gr.vector_source_b(src_data)
+ enc = gr.encode_ccsds_27_bb()
+ b2f = gr.char_to_float()
+ add = gr.add_const_ff(-0.5)
+ mul = gr.multiply_const_ff(2.0)
+ dec = gr.decode_ccsds_27_fb()
+ dst = gr.vector_sink_b()
+ self.tb.connect(src, enc, b2f, add, mul, dec, dst)
+ self.tb.run()
+ dst_data = dst.data()
+ self.assertEqual(expected, dst_data)
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_ccsds_27, "test_ccsds_27.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_endian_swap.py b/gnuradio-core/src/python/gnuradio/gr/qa_endian_swap.py
new file mode 100644
index 000000000..4d2555cc4
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_endian_swap.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+#
+# Copyright 2011,2012 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import ctypes
+
+class test_endian_swap (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_001(self):
+
+ src_data = [1,2,3,4]
+ expected_result = [256, 512, 768, 1024];
+
+ src = gr.vector_source_s(src_data)
+ op = gr.endian_swap(2)
+ dst = gr.vector_sink_s()
+
+ self.tb.connect(src, op, dst)
+ self.tb.run()
+ result_data = list(dst.data())
+
+ self.assertEqual(expected_result, result_data)
+
+ def test_002(self):
+
+ src_data = [1,2,3,4]
+ expected_result = [16777216, 33554432, 50331648, 67108864];
+
+ src = gr.vector_source_i(src_data)
+ op = gr.endian_swap(4)
+ dst = gr.vector_sink_i()
+
+ self.tb.connect(src, op, dst)
+ self.tb.run()
+ result_data = list(dst.data())
+
+ self.assertEqual(expected_result, result_data)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_endian_swap, "test_endian_swap.xml")
+
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..9018e12f3
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_feval.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python
+#
+# Copyright 2006,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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 my_feval(gr.feval):
+ def __init__(self):
+ gr.feval.__init__(self)
+ self.fired = False
+ def eval(self):
+ self.fired = True
+
+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)
+
+ def test_void_1(self):
+ # this is all in python
+ f = my_feval()
+ f.eval()
+ self.assertEqual(True, f.fired)
+
+ def test_void_2(self):
+ # this is python -> C++ -> python and back again
+ f = my_feval()
+ gr.feval_example(f)
+ self.assertEqual(True, f.fired)
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_feval, "test_feval.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_fft.py b/gnuradio-core/src/python/gnuradio/gr/qa_fft.py
new file mode 100755
index 000000000..693d0e67c
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_fft.py
@@ -0,0 +1,212 @@
+#!/usr/bin/env python
+#
+# Copyright 2008,2010 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 3, 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 this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+from gnuradio import gr, gr_unittest
+import sys
+import random
+
+primes = (2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,
+ 59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,
+ 137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,
+ 227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311)
+
+
+class test_fft(gr_unittest.TestCase):
+
+ def setUp(self):
+ pass
+
+ def tearDown(self):
+ pass
+
+ 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_001(self):
+ tb = gr.top_block()
+ fft_size = 32
+ src_data = tuple([complex(primes[2*i], primes[2*i+1]) for i in range(fft_size)])
+
+ expected_result = ((4377+4516j),
+ (-1706.1268310546875+1638.4256591796875j),
+ (-915.2083740234375+660.69427490234375j),
+ (-660.370361328125+381.59600830078125j),
+ (-499.96044921875+238.41630554199219j),
+ (-462.26748657226562+152.88948059082031j),
+ (-377.98440551757812+77.5928955078125j),
+ (-346.85821533203125+47.152004241943359j),
+ (-295+20j),
+ (-286.33609008789062-22.257017135620117j),
+ (-271.52999877929688-33.081821441650391j),
+ (-224.6358642578125-67.019538879394531j),
+ (-244.24473571777344-91.524826049804688j),
+ (-203.09068298339844-108.54627227783203j),
+ (-198.45195007324219-115.90768432617188j),
+ (-182.97744750976562-128.12318420410156j),
+ (-167-180j),
+ (-130.33688354492188-173.83778381347656j),
+ (-141.19784545898438-190.28807067871094j),
+ (-111.09677124023438-214.48896789550781j),
+ (-70.039543151855469-242.41630554199219j),
+ (-68.960540771484375-228.30015563964844j),
+ (-53.049201965332031-291.47097778320312j),
+ (-28.695289611816406-317.64553833007812j),
+ (57-300j),
+ (45.301143646240234-335.69509887695312j),
+ (91.936195373535156-373.32437133789062j),
+ (172.09465026855469-439.275146484375j),
+ (242.24473571777344-504.47515869140625j),
+ (387.81732177734375-666.6788330078125j),
+ (689.48553466796875-918.2142333984375j),
+ (1646.539306640625-1694.1956787109375j))
+
+ src = gr.vector_source_c(src_data)
+ s2v = gr.stream_to_vector(gr.sizeof_gr_complex, fft_size)
+ fft = gr.fft_vcc(fft_size, True, [], False)
+ v2s = gr.vector_to_stream(gr.sizeof_gr_complex, fft_size)
+ dst = gr.vector_sink_c()
+ tb.connect(src, s2v, fft, v2s, dst)
+ tb.run()
+ result_data = dst.data()
+ #print 'expected:', expected_result
+ #print 'results: ', result_data
+ #self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5)
+ self.assert_fft_ok2(expected_result, result_data)
+
+ def test_002(self):
+ tb = gr.top_block()
+ fft_size = 32
+
+ tmp_data = ((4377+4516j),
+ (-1706.1268310546875+1638.4256591796875j),
+ (-915.2083740234375+660.69427490234375j),
+ (-660.370361328125+381.59600830078125j),
+ (-499.96044921875+238.41630554199219j),
+ (-462.26748657226562+152.88948059082031j),
+ (-377.98440551757812+77.5928955078125j),
+ (-346.85821533203125+47.152004241943359j),
+ (-295+20j),
+ (-286.33609008789062-22.257017135620117j),
+ (-271.52999877929688-33.081821441650391j),
+ (-224.6358642578125-67.019538879394531j),
+ (-244.24473571777344-91.524826049804688j),
+ (-203.09068298339844-108.54627227783203j),
+ (-198.45195007324219-115.90768432617188j),
+ (-182.97744750976562-128.12318420410156j),
+ (-167-180j),
+ (-130.33688354492188-173.83778381347656j),
+ (-141.19784545898438-190.28807067871094j),
+ (-111.09677124023438-214.48896789550781j),
+ (-70.039543151855469-242.41630554199219j),
+ (-68.960540771484375-228.30015563964844j),
+ (-53.049201965332031-291.47097778320312j),
+ (-28.695289611816406-317.64553833007812j),
+ (57-300j),
+ (45.301143646240234-335.69509887695312j),
+ (91.936195373535156-373.32437133789062j),
+ (172.09465026855469-439.275146484375j),
+ (242.24473571777344-504.47515869140625j),
+ (387.81732177734375-666.6788330078125j),
+ (689.48553466796875-918.2142333984375j),
+ (1646.539306640625-1694.1956787109375j))
+
+ src_data = tuple([x/fft_size for x in tmp_data])
+
+ expected_result = tuple([complex(primes[2*i], primes[2*i+1]) for i in range(fft_size)])
+
+ src = gr.vector_source_c(src_data)
+ s2v = gr.stream_to_vector(gr.sizeof_gr_complex, fft_size)
+ fft = gr.fft_vcc(fft_size, False, [], False)
+ v2s = gr.vector_to_stream(gr.sizeof_gr_complex, fft_size)
+ dst = gr.vector_sink_c()
+ tb.connect(src, s2v, fft, v2s, dst)
+ tb.run()
+ result_data = dst.data()
+ #print 'expected:', expected_result
+ #print 'results: ', result_data
+ #self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5)
+ self.assert_fft_ok2(expected_result, result_data)
+
+ def test_003(self):
+ # Same test as above, only use 2 threads
+
+ tb = gr.top_block()
+ fft_size = 32
+
+ tmp_data = ((4377+4516j),
+ (-1706.1268310546875+1638.4256591796875j),
+ (-915.2083740234375+660.69427490234375j),
+ (-660.370361328125+381.59600830078125j),
+ (-499.96044921875+238.41630554199219j),
+ (-462.26748657226562+152.88948059082031j),
+ (-377.98440551757812+77.5928955078125j),
+ (-346.85821533203125+47.152004241943359j),
+ (-295+20j),
+ (-286.33609008789062-22.257017135620117j),
+ (-271.52999877929688-33.081821441650391j),
+ (-224.6358642578125-67.019538879394531j),
+ (-244.24473571777344-91.524826049804688j),
+ (-203.09068298339844-108.54627227783203j),
+ (-198.45195007324219-115.90768432617188j),
+ (-182.97744750976562-128.12318420410156j),
+ (-167-180j),
+ (-130.33688354492188-173.83778381347656j),
+ (-141.19784545898438-190.28807067871094j),
+ (-111.09677124023438-214.48896789550781j),
+ (-70.039543151855469-242.41630554199219j),
+ (-68.960540771484375-228.30015563964844j),
+ (-53.049201965332031-291.47097778320312j),
+ (-28.695289611816406-317.64553833007812j),
+ (57-300j),
+ (45.301143646240234-335.69509887695312j),
+ (91.936195373535156-373.32437133789062j),
+ (172.09465026855469-439.275146484375j),
+ (242.24473571777344-504.47515869140625j),
+ (387.81732177734375-666.6788330078125j),
+ (689.48553466796875-918.2142333984375j),
+ (1646.539306640625-1694.1956787109375j))
+
+ src_data = tuple([x/fft_size for x in tmp_data])
+
+ expected_result = tuple([complex(primes[2*i], primes[2*i+1]) for i in range(fft_size)])
+
+ nthreads = 2
+
+ src = gr.vector_source_c(src_data)
+ s2v = gr.stream_to_vector(gr.sizeof_gr_complex, fft_size)
+ fft = gr.fft_vcc(fft_size, False, [], False, nthreads)
+ v2s = gr.vector_to_stream(gr.sizeof_gr_complex, fft_size)
+ dst = gr.vector_sink_c()
+ tb.connect(src, s2v, fft, v2s, dst)
+ tb.run()
+ result_data = dst.data()
+ self.assert_fft_ok2(expected_result, result_data)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_fft, "test_fft.xml")
+
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..c0aadc306
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_fft_filter.py
@@ -0,0 +1,383 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2005,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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
+ """
+ tb = gr.top_block()
+ #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()
+ tb.connect(src, op, dst)
+ tb.run()
+ return dst.data()
+
+def reference_filter_fff(dec, taps, input):
+ """
+ compute result using conventional fir filter
+ """
+ tb = gr.top_block()
+ #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()
+ tb.connect(src, op, dst)
+ tb.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):
+ pass
+
+ def tearDown(self):
+ pass
+
+ 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):
+ tb = gr.top_block()
+ 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()
+ tb.connect(src, op, dst)
+ tb.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):
+ # Test nthreads
+ tb = gr.top_block()
+ src_data = (0,1,2,3,4,5,6,7)
+ taps = (2,)
+ nthreads = 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, nthreads)
+ dst = gr.vector_sink_c()
+ tb.connect(src, op, dst)
+ tb.run()
+ result_data = dst.data()
+ #print 'expected:', expected_result
+ #print 'results: ', result_data
+ self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5)
+
+ def test_ccc_003(self):
+ tb = gr.top_block()
+ 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()
+ tb.connect(src, op, dst)
+ tb.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()
+ tb = gr.top_block()
+ tb.connect(src, op, dst)
+ tb.run()
+ result_data = dst.data()
+ del tb
+
+ 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()
+ tb = gr.top_block()
+ tb.connect(src, op, dst)
+ tb.run()
+ del tb
+ result_data = dst.data()
+
+ self.assert_fft_ok2(expected_result, result_data)
+
+ def test_ccc_006(self):
+ # Test decimating with nthreads=2
+ random.seed(0)
+ nthreads = 2
+ 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, nthreads)
+ dst = gr.vector_sink_c()
+ tb = gr.top_block()
+ tb.connect(src, op, dst)
+ tb.run()
+ del tb
+ result_data = dst.data()
+
+ self.assert_fft_ok2(expected_result, result_data)
+
+ # ----------------------------------------------------------------
+ # test _fff version
+ # ----------------------------------------------------------------
+
+ def test_fff_001(self):
+ tb = gr.top_block()
+ 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()
+ tb.connect(src, op, dst)
+ tb.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):
+ tb = gr.top_block()
+ 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()
+ tb.connect(src, op, dst)
+ tb.run()
+ result_data = dst.data()
+ #print 'expected:', expected_result
+ #print 'results: ', result_data
+ self.assertFloatTuplesAlmostEqual (expected_result, result_data, 5)
+
+ def test_fff_003(self):
+ # Test 02 with nthreads
+ tb = gr.top_block()
+ src_data = (0,1,2,3,4,5,6,7)
+ taps = (2,)
+ nthreads = 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, nthreads)
+ dst = gr.vector_sink_f()
+ tb.connect(src, op, dst)
+ tb.run()
+ result_data = dst.data()
+ self.assertFloatTuplesAlmostEqual (expected_result, result_data, 5)
+
+ def xtest_fff_004(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()
+ tb = gr.top_block()
+ tb.connect(src, op, dst)
+ tb.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_005(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()
+ tb = gr.top_block()
+ tb.connect(src, op, dst)
+ tb.run()
+ result_data = dst.data()
+
+ self.assert_fft_float_ok2(expected_result, result_data, abs_eps=2.0)
+
+ def xtest_fff_006(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()
+ tb = gr.top_block()
+ tb.connect(src, op, dst)
+ tb.run()
+ result_data = dst.data()
+
+ self.assert_fft_float_ok2(expected_result, result_data)
+
+ def xtest_fff_007(self):
+ # test decimation with nthreads
+ random.seed(0)
+ nthreads = 2
+ 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, nthreads)
+ dst = gr.vector_sink_f()
+ tb = gr.top_block()
+ tb.connect(src, op, dst)
+ tb.run()
+ result_data = dst.data()
+
+ self.assert_fft_float_ok2(expected_result, result_data)
+
+ def test_fff_get0(self):
+ random.seed(0)
+ for i in xrange(25):
+ ntaps = int(random.uniform(2, 100))
+ taps = make_random_float_tuple(ntaps)
+
+ op = gr.fft_filter_fff(1, taps)
+ result_data = op.taps()
+ #print result_data
+
+ self.assertEqual(taps, result_data)
+
+ def test_ccc_get0(self):
+ random.seed(0)
+ for i in xrange(25):
+ ntaps = int(random.uniform(2, 100))
+ taps = make_random_complex_tuple(ntaps)
+
+ op = gr.fft_filter_ccc(1, taps)
+ result_data = op.taps()
+ #print result_data
+
+ self.assertComplexTuplesAlmostEqual(taps, result_data, 4)
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_fft_filter, "test_fft_filter.xml")
+
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..8d325fc3e
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_filter_delay_fc.py
@@ -0,0 +1,317 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import math
+
+class test_filter_delay_fc (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = 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))
+
+ tb = self.tb
+
+ 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)
+
+ tb.connect (src1, head)
+ tb.connect (head, hd)
+ tb.connect (hd,dst2)
+
+ tb.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))
+
+
+ tb = self.tb
+
+ 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)
+
+ tb.connect (src1, head)
+ tb.connect (head, (hd,0))
+ tb.connect (head, (hd,1))
+ tb.connect (hd,dst2)
+ tb.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))
+
+ tb = self.tb
+
+ 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 ()
+
+ tb.connect (src1, head1)
+ tb.connect (src2, head2)
+
+ tb.connect (head1, (hd,0))
+ tb.connect (head2, (hd,1))
+ tb.connect (hd, dst2)
+
+ tb.run ()
+
+ # get output
+ result_data = dst2.data ()
+
+ self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5)
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_filter_delay_fc, "test_filter_delay_fc.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_float_to_char.py b/gnuradio-core/src/python/gnuradio/gr/qa_float_to_char.py
new file mode 100755
index 000000000..057e297f9
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_float_to_char.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+#
+# Copyright 2011,2012 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+class test_float_to_char (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_001(self):
+
+ src_data = (0.0, 1.1, 2.2, 3.3, 4.4, 5.5, -1.1, -2.2, -3.3)
+ expected_result = [0, 1, 2, 3, 4, 5, 255, 254, 253]
+ src = gr.vector_source_f(src_data)
+ op = gr.float_to_char()
+ dst = gr.vector_sink_b()
+
+ self.tb.connect(src, op, dst)
+ self.tb.run()
+ result_data = list(dst.data())
+
+ self.assertEqual(expected_result, result_data)
+
+ def test_002(self):
+
+ src_data = ( 126.0, 127.0, 128.0)
+ expected_result = [ 126, 127, 127 ]
+
+ src = gr.vector_source_f(src_data)
+ op = gr.float_to_char()
+ # Note: vector_sink_b returns uchar
+ dst = gr.vector_sink_b()
+
+ self.tb.connect(src, op, dst)
+ self.tb.run()
+ result_data = list(dst.data())
+
+ self.assertEqual(expected_result, result_data)
+
+ def test_003(self):
+
+ scale = 2
+ vlen = 3
+ src_data = (0.0, 1.1, 2.2, 3.3, 4.4, 5.5, -1.1, -2.2, -3.3)
+ expected_result = [0, 2, 4, 6, 8, 11, 254, 252, 250]
+ src = gr.vector_source_f(src_data)
+ s2v = gr.stream_to_vector(gr.sizeof_float, vlen)
+ op = gr.float_to_char(vlen, scale)
+ v2s = gr.vector_to_stream(gr.sizeof_char, vlen)
+ dst = gr.vector_sink_b()
+
+ self.tb.connect(src, s2v, op, v2s, dst)
+ self.tb.run()
+ result_data = list(dst.data())
+
+ self.assertEqual(expected_result, result_data)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_float_to_char, "test_float_to_char.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_float_to_int.py b/gnuradio-core/src/python/gnuradio/gr/qa_float_to_int.py
new file mode 100755
index 000000000..5c7a412d2
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_float_to_int.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+#
+# Copyright 2011 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_float_to_int (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_001(self):
+
+ src_data = (0.0, 1.1, 2.2, 3.3, 4.4, 5.5, -1.1, -2.2, -3.3, -4.4, -5.5)
+ expected_result = [0, 1, 2, 3, 4, 6, -1, -2, -3, -4, -6]
+
+ src = gr.vector_source_f(src_data)
+ op = gr.float_to_int()
+ dst = gr.vector_sink_i()
+
+ self.tb.connect(src, op, dst)
+ self.tb.run()
+ result_data = list(dst.data())
+
+ self.assertEqual(expected_result, result_data)
+
+ def test_002(self):
+
+ src_data = ( 2147483647, 2147483648, 2200000000,
+ -2147483648, -2147483649, -2200000000)
+ expected_result = [ 2147483647, 2147483647, 2147483647,
+ -2147483647, -2147483647, -2147483647]
+ src = gr.vector_source_f(src_data)
+ op = gr.float_to_int()
+ dst = gr.vector_sink_i()
+
+ self.tb.connect(src, op, dst)
+ self.tb.run()
+ result_data = list(dst.data())
+
+ self.assertEqual(expected_result, result_data)
+
+
+ def test_003(self):
+
+ scale = 2
+ vlen = 3
+ src_data = (0.0, 1.1, 2.2, 3.3, 4.4, 5.5, -1.1, -2.2, -3.3)
+ expected_result = [0, 2, 4, 7, 9, 11, -2, -4, -7,]
+ src = gr.vector_source_f(src_data)
+ s2v = gr.stream_to_vector(gr.sizeof_float, vlen)
+ op = gr.float_to_int(vlen, scale)
+ v2s = gr.vector_to_stream(gr.sizeof_int, vlen)
+ dst = gr.vector_sink_i()
+
+ self.tb.connect(src, s2v, op, v2s, dst)
+ self.tb.run()
+ result_data = list(dst.data())
+
+ self.assertEqual(expected_result, result_data)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_float_to_int, "test_float_to_int.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_float_to_short.py b/gnuradio-core/src/python/gnuradio/gr/qa_float_to_short.py
new file mode 100755
index 000000000..3f8b66975
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_float_to_short.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+#
+# Copyright 2011,2012 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import ctypes
+
+class test_float_to_short (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_001(self):
+
+ src_data = (0.0, 1.1, 2.2, 3.3, 4.4, 5.5, -1.1, -2.2, -3.3, -4.4, -5.5)
+ expected_result = [0, 1, 2, 3, 4, 6, -1, -2, -3, -4, -6]
+
+ src = gr.vector_source_f(src_data)
+ op = gr.float_to_short()
+ dst = gr.vector_sink_s()
+
+ self.tb.connect(src, op, dst)
+ self.tb.run()
+ result_data = list(dst.data())
+
+ self.assertEqual(expected_result, result_data)
+
+ def test_002(self):
+
+ src_data = ( 32766, 32767, 32768,
+ -32767, -32768, -32769)
+ expected_result = [ 32766, 32767, 32767,
+ -32767, -32768, -32768 ]
+
+ src = gr.vector_source_f(src_data)
+ op = gr.float_to_short()
+ dst = gr.vector_sink_s()
+
+ self.tb.connect(src, op, dst)
+ self.tb.run()
+ result_data = list(dst.data())
+
+ self.assertEqual(expected_result, result_data)
+
+ def test_003(self):
+
+ scale = 2
+ vlen = 3
+ src_data = (0.0, 1.1, 2.2, 3.3, 4.4, 5.5, -1.1, -2.2, -3.3)
+ expected_result = [0, 2, 4, 7, 9, 11, -2, -4, -7]
+ src = gr.vector_source_f(src_data)
+ s2v = gr.stream_to_vector(gr.sizeof_float, vlen)
+ op = gr.float_to_short(vlen, scale)
+ v2s = gr.vector_to_stream(gr.sizeof_short, vlen)
+ dst = gr.vector_sink_s()
+
+ self.tb.connect(src, s2v, op, v2s, dst)
+ self.tb.run()
+ result_data = list(dst.data())
+
+ self.assertEqual(expected_result, result_data)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_float_to_short, "test_float_to_short.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_float_to_uchar.py b/gnuradio-core/src/python/gnuradio/gr/qa_float_to_uchar.py
new file mode 100755
index 000000000..831bed93e
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_float_to_uchar.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+#
+# Copyright 2011 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import ctypes
+
+class test_float_to_uchar (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_001(self):
+
+ src_data = (0.0, 1.1, 2.2, 3.3, 4.4, 5.5, -1.1, -2.2, -3.3, -4.4, -5.5)
+ expected_result = [0, 1, 2, 3, 4, 6, 0, 0, 0, 0, 0]
+ src = gr.vector_source_f(src_data)
+ op = gr.float_to_uchar()
+ dst = gr.vector_sink_b()
+
+ self.tb.connect(src, op, dst)
+ self.tb.run()
+ result_data = list(dst.data())
+
+ self.assertEqual(expected_result, result_data)
+
+ def test_002(self):
+
+ src_data = ( 254.0, 255.0, 256.0)
+ expected_result = [ 254, 255, 255 ]
+ src = gr.vector_source_f(src_data)
+ op = gr.float_to_uchar()
+ dst = gr.vector_sink_b()
+
+ self.tb.connect(src, op, dst)
+ self.tb.run()
+ result_data = list(dst.data())
+
+ self.assertEqual(expected_result, result_data)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_float_to_uchar, "test_float_to_uchar.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_fractional_interpolator.py b/gnuradio-core/src/python/gnuradio/gr/qa_fractional_interpolator.py
new file mode 100755
index 000000000..e19bb28f3
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_fractional_interpolator.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+#
+# Copyright 2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_fractional_resampler (gr_unittest.TestCase):
+
+ def setUp(self):
+ self.tb = gr.top_block()
+
+ def tearDown(self):
+ self.tb = None
+
+ def test_000_make(self):
+ op = gr.fractional_interpolator_ff(0.0, 1.0)
+ op2 = gr.fractional_interpolator_cc(0.0, 1.0)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_fractional_resampler, "test_fractional_resampler.xml")
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..23459fff3
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_frequency_modulator.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = 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.tb.connect (src, op)
+ self.tb.connect (op, dst)
+ self.tb.run ()
+ result_data = dst.data ()
+ self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5)
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_frequency_modulator, "test_frequency_modulator.xml")
+
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..95b8c0664
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_fsk_stuff.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = 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.tb.connect (src, op)
+ self.tb.connect (op, dst)
+ self.tb.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.tb.connect (src, op)
+ self.tb.connect (op, dst)
+ self.tb.run ()
+ result_data = dst.data ()
+ self.assertEqual (expected_result, result_data)
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_bytes_to_syms, "test_bytes_to_syms.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_glfsr_source.py b/gnuradio-core/src/python/gnuradio/gr/qa_glfsr_source.py
new file mode 100755
index 000000000..161e4a5cc
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_glfsr_source.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python
+#
+# Copyright 2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_glfsr_source(gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_000_make_b(self):
+ src = gr.glfsr_source_b(16)
+ self.assertEquals(src.mask(), 0x8016)
+ self.assertEquals(src.period(), 2**16-1)
+
+ def test_001_degree_b(self):
+ self.assertRaises(RuntimeError,
+ lambda: gr.glfsr_source_b(0))
+ self.assertRaises(RuntimeError,
+ lambda: gr.glfsr_source_b(33))
+
+ def test_002_correlation_b(self):
+ for degree in range(1,11): # Higher degrees take too long to correlate
+ src = gr.glfsr_source_b(degree, False)
+ b2f = gr.chunks_to_symbols_bf((-1.0,1.0), 1)
+ dst = gr.vector_sink_f()
+ del self.tb # Discard existing top block
+ self.tb = gr.top_block()
+ self.tb.connect(src, b2f, dst)
+ self.tb.run()
+ self.tb.disconnect_all()
+ actual_result = dst.data()
+ R = auto_correlate(actual_result)
+ self.assertEqual(R[0], float(len(R))) # Auto-correlation peak at origin
+ for i in range(len(R)-1):
+ self.assertEqual(R[i+1], -1.0) # Auto-correlation minimum everywhere else
+
+ def test_003_make_f(self):
+ src = gr.glfsr_source_f(16)
+ self.assertEquals(src.mask(), 0x8016)
+ self.assertEquals(src.period(), 2**16-1)
+
+ def test_004_degree_f(self):
+ self.assertRaises(RuntimeError,
+ lambda: gr.glfsr_source_f(0))
+ self.assertRaises(RuntimeError,
+ lambda: gr.glfsr_source_f(33))
+ def test_005_correlation_f(self):
+ for degree in range(1,11): # Higher degrees take too long to correlate
+ src = gr.glfsr_source_f(degree, False)
+ dst = gr.vector_sink_f()
+ del self.tb # Discard existing top block
+ self.tb = gr.top_block()
+ self.tb.connect(src, dst)
+ self.tb.run()
+
+ actual_result = dst.data()
+ R = auto_correlate(actual_result)
+ self.assertEqual(R[0], float(len(R))) # Auto-correlation peak at origin
+ for i in range(len(R)-1):
+ self.assertEqual(R[i+1], -1.0) # Auto-correlation minimum everywhere else
+
+def auto_correlate(data):
+ l = len(data)
+ R = [0,]*l
+ for lag in range(l):
+ for i in range(l):
+ R[lag] += data[i]*data[i-lag]
+ return R
+
+if __name__ == '__main__':
+ gr_unittest.run(test_glfsr_source, "test_glfsr_source.xml")
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..77f1b5f89
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_goertzel.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+#
+# Copyright 2006,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+from math import pi, cos
+
+class test_goertzel(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.tb = gr.top_block()
+
+ def tearDown(self):
+ self.tb = 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.tb.connect(src, dft, dst)
+ self.tb.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=4)
+
+ 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=4)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_goertzel, "test_goertzel.xml")
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..d7cb354dc
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_head.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_head (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = 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.tb.connect (src1, op)
+ self.tb.connect (op, dst1)
+ self.tb.run ()
+ dst_data = dst1.data ()
+ self.assertEqual (expected_result, dst_data)
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_head, "test_head.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_hier_block2.py b/gnuradio-core/src/python/gnuradio/gr/qa_hier_block2.py
new file mode 100755
index 000000000..3132d91b0
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_hier_block2.py
@@ -0,0 +1,369 @@
+#!/usr/bin/env python
+
+from gnuradio import gr, gr_unittest
+
+class test_hier_block2(gr_unittest.TestCase):
+
+ def setUp(self):
+ pass
+
+ def tearDown(self):
+ pass
+
+ def test_001_make(self):
+ hblock = gr.hier_block2("test_block",
+ gr.io_signature(1,1,gr.sizeof_int),
+ gr.io_signature(1,1,gr.sizeof_int))
+ self.assertEqual("test_block", hblock.name())
+ self.assertEqual(1, hblock.input_signature().max_streams())
+ self.assertEqual(1, hblock.output_signature().min_streams())
+ self.assertEqual(1, hblock.output_signature().max_streams())
+ self.assertEqual(gr.sizeof_int, hblock.output_signature().sizeof_stream_item(0))
+
+ def test_002_connect_input(self):
+ hblock = gr.hier_block2("test_block",
+ gr.io_signature(1,1,gr.sizeof_int),
+ gr.io_signature(1,1,gr.sizeof_int))
+ nop1 = gr.nop(gr.sizeof_int)
+ hblock.connect(hblock, nop1)
+
+ def test_004_connect_output(self):
+ hblock = gr.hier_block2("test_block",
+ gr.io_signature(1,1,gr.sizeof_int),
+ gr.io_signature(1,1,gr.sizeof_int))
+ nop1 = gr.nop(gr.sizeof_int)
+ hblock.connect(nop1, hblock)
+
+ def test_005_connect_output_in_use(self):
+ hblock = gr.hier_block2("test_block",
+ gr.io_signature(1,1,gr.sizeof_int),
+ gr.io_signature(1,1,gr.sizeof_int))
+ nop1 = gr.nop(gr.sizeof_int)
+ nop2 = gr.nop(gr.sizeof_int)
+ hblock.connect(nop1, hblock)
+ self.assertRaises(ValueError,
+ lambda: hblock.connect(nop2, hblock))
+
+ def test_006_connect_invalid_src_port_neg(self):
+ hblock = gr.hier_block2("test_block",
+ gr.io_signature(1,1,gr.sizeof_int),
+ gr.io_signature(1,1,gr.sizeof_int))
+ nop1 = gr.nop(gr.sizeof_int)
+ self.assertRaises(ValueError,
+ lambda: hblock.connect((hblock, -1), nop1))
+
+ def test_005_connect_invalid_src_port_exceeds(self):
+ hblock = gr.hier_block2("test_block",
+ gr.io_signature(1,1,gr.sizeof_int),
+ gr.io_signature(1,1,gr.sizeof_int))
+ nop1 = gr.nop(gr.sizeof_int)
+ self.assertRaises(ValueError,
+ lambda: hblock.connect((hblock, 1), nop1))
+
+ def test_007_connect_invalid_dst_port_neg(self):
+ hblock = gr.hier_block2("test_block",
+ gr.io_signature(1,1,gr.sizeof_int),
+ gr.io_signature(1,1,gr.sizeof_int))
+ nop1 = gr.nop(gr.sizeof_int)
+ nop2 = gr.nop(gr.sizeof_int)
+ self.assertRaises(ValueError,
+ lambda: hblock.connect(nop1, (nop2, -1)))
+
+ def test_008_connect_invalid_dst_port_exceeds(self):
+ hblock = gr.hier_block2("test_block",
+ gr.io_signature(1,1,gr.sizeof_int),
+ gr.io_signature(1,1,gr.sizeof_int))
+ nop1 = gr.null_sink(gr.sizeof_int)
+ nop2 = gr.null_sink(gr.sizeof_int)
+ self.assertRaises(ValueError,
+ lambda: hblock.connect(nop1, (nop2, 1)))
+
+ def test_009_check_topology(self):
+ hblock = gr.top_block("test_block")
+ hblock.check_topology(0, 0)
+
+ def test_010_run(self):
+ expected = (1.0, 2.0, 3.0, 4.0)
+ hblock = gr.top_block("test_block")
+ src = gr.vector_source_f(expected, False)
+ sink1 = gr.vector_sink_f()
+ sink2 = gr.vector_sink_f()
+ hblock.connect(src, sink1)
+ hblock.connect(src, sink2)
+ hblock.run()
+ actual1 = sink1.data()
+ actual2 = sink2.data()
+ self.assertEquals(expected, actual1)
+ self.assertEquals(expected, actual2)
+
+ def test_012_disconnect_input(self):
+ hblock = gr.hier_block2("test_block",
+ gr.io_signature(1,1,gr.sizeof_int),
+ gr.io_signature(1,1,gr.sizeof_int))
+ nop1 = gr.nop(gr.sizeof_int)
+ hblock.connect(hblock, nop1)
+ hblock.disconnect(hblock, nop1)
+
+ def test_013_disconnect_input_not_connected(self):
+ hblock = gr.hier_block2("test_block",
+ gr.io_signature(1,1,gr.sizeof_int),
+ gr.io_signature(1,1,gr.sizeof_int))
+ nop1 = gr.nop(gr.sizeof_int)
+ nop2 = gr.nop(gr.sizeof_int)
+ hblock.connect(hblock, nop1)
+ self.assertRaises(ValueError,
+ lambda: hblock.disconnect(hblock, nop2))
+
+ def test_014_disconnect_input_neg(self):
+ hblock = gr.hier_block2("test_block",
+ gr.io_signature(1,1,gr.sizeof_int),
+ gr.io_signature(1,1,gr.sizeof_int))
+ nop1 = gr.nop(gr.sizeof_int)
+ hblock.connect(hblock, nop1)
+ self.assertRaises(ValueError,
+ lambda: hblock.disconnect((hblock, -1), nop1))
+
+ def test_015_disconnect_input_exceeds(self):
+ hblock = gr.hier_block2("test_block",
+ gr.io_signature(1,1,gr.sizeof_int),
+ gr.io_signature(1,1,gr.sizeof_int))
+ nop1 = gr.nop(gr.sizeof_int)
+ hblock.connect(hblock, nop1)
+ self.assertRaises(ValueError,
+ lambda: hblock.disconnect((hblock, 1), nop1))
+
+ def test_016_disconnect_output(self):
+ hblock = gr.hier_block2("test_block",
+ gr.io_signature(1,1,gr.sizeof_int),
+ gr.io_signature(1,1,gr.sizeof_int))
+ nop1 = gr.nop(gr.sizeof_int)
+ hblock.connect(nop1, hblock)
+ hblock.disconnect(nop1, hblock)
+
+ def test_017_disconnect_output_not_connected(self):
+ hblock = gr.hier_block2("test_block",
+ gr.io_signature(1,1,gr.sizeof_int),
+ gr.io_signature(1,1,gr.sizeof_int))
+ nop1 = gr.nop(gr.sizeof_int)
+ nop2 = gr.nop(gr.sizeof_int)
+ hblock.connect(nop1, hblock)
+ self.assertRaises(ValueError,
+ lambda: hblock.disconnect(nop2, hblock))
+
+ def test_018_disconnect_output_neg(self):
+ hblock = gr.hier_block2("test_block",
+ gr.io_signature(1,1,gr.sizeof_int),
+ gr.io_signature(1,1,gr.sizeof_int))
+ nop1 = gr.nop(gr.sizeof_int)
+ hblock.connect(hblock, nop1)
+ self.assertRaises(ValueError,
+ lambda: hblock.disconnect(nop1, (hblock, -1)))
+
+ def test_019_disconnect_output_exceeds(self):
+ hblock = gr.hier_block2("test_block",
+ gr.io_signature(1,1,gr.sizeof_int),
+ gr.io_signature(1,1,gr.sizeof_int))
+ nop1 = gr.nop(gr.sizeof_int)
+ hblock.connect(nop1, hblock)
+ self.assertRaises(ValueError,
+ lambda: hblock.disconnect(nop1, (hblock, 1)))
+
+ def test_020_run(self):
+ hblock = gr.top_block("test_block")
+ data = (1.0, 2.0, 3.0, 4.0)
+ src = gr.vector_source_f(data, False)
+ dst = gr.vector_sink_f()
+ hblock.connect(src, dst)
+ hblock.run()
+ self.assertEquals(data, dst.data())
+
+ def test_021_connect_single(self):
+ hblock = gr.top_block("test_block")
+ blk = gr.hier_block2("block",
+ gr.io_signature(0, 0, 0),
+ gr.io_signature(0, 0, 0))
+ hblock.connect(blk)
+
+ def test_022_connect_single_with_ports(self):
+ hblock = gr.top_block("test_block")
+ blk = gr.hier_block2("block",
+ gr.io_signature(1, 1, 1),
+ gr.io_signature(1, 1, 1))
+ self.assertRaises(ValueError,
+ lambda: hblock.connect(blk))
+
+ def test_023_connect_single_twice(self):
+ hblock = gr.top_block("test_block")
+ blk = gr.hier_block2("block",
+ gr.io_signature(0, 0, 0),
+ gr.io_signature(0, 0, 0))
+ hblock.connect(blk)
+ self.assertRaises(ValueError,
+ lambda: hblock.connect(blk))
+
+ def test_024_disconnect_single(self):
+ hblock = gr.top_block("test_block")
+ blk = gr.hier_block2("block",
+ gr.io_signature(0, 0, 0),
+ gr.io_signature(0, 0, 0))
+ hblock.connect(blk)
+ hblock.disconnect(blk)
+
+ def test_025_disconnect_single_not_connected(self):
+ hblock = gr.top_block("test_block")
+ blk = gr.hier_block2("block",
+ gr.io_signature(0, 0, 0),
+ gr.io_signature(0, 0, 0))
+ self.assertRaises(ValueError,
+ lambda: hblock.disconnect(blk))
+
+ def test_026_run_single(self):
+ expected_data = (1.0,)
+ tb = gr.top_block("top_block")
+ hb = gr.hier_block2("block",
+ gr.io_signature(0, 0, 0),
+ gr.io_signature(0, 0, 0))
+ src = gr.vector_source_f(expected_data)
+ dst = gr.vector_sink_f()
+ hb.connect(src, dst)
+ tb.connect(hb)
+ tb.run()
+ self.assertEquals(expected_data, dst.data())
+
+ def test_027a_internally_unconnected_input(self):
+ tb = gr.top_block()
+ hb = gr.hier_block2("block",
+ gr.io_signature(1, 1, 1),
+ gr.io_signature(1, 1, 1))
+ hsrc = gr.vector_source_b([1,])
+ hb.connect(hsrc, hb) # wire output internally
+ src = gr.vector_source_b([1, ])
+ dst = gr.vector_sink_b()
+ tb.connect(src, hb, dst) # hb's input is not connected internally
+ self.assertRaises(RuntimeError,
+ lambda: tb.run())
+
+ def test_027b_internally_unconnected_output(self):
+ tb = gr.top_block()
+
+ hb = gr.hier_block2("block",
+ gr.io_signature(1, 1, 1),
+ gr.io_signature(1, 1, 1))
+ hdst = gr.vector_sink_b()
+ hb.connect(hb, hdst) # wire input internally
+ src = gr.vector_source_b([1, ])
+ dst = gr.vector_sink_b()
+ tb.connect(src, hb, dst) # hb's output is not connected internally
+ self.assertRaises(RuntimeError,
+ lambda: tb.run())
+
+ def test_027c_fully_unconnected_output(self):
+ tb = gr.top_block()
+ hb = gr.hier_block2("block",
+ gr.io_signature(1, 1, 1),
+ gr.io_signature(1, 1, 1))
+ hsrc = gr.vector_sink_b()
+ hb.connect(hb, hsrc) # wire input internally
+ src = gr.vector_source_b([1, ])
+ dst = gr.vector_sink_b()
+ tb.connect(src, hb) # hb's output is not connected internally or externally
+ self.assertRaises(RuntimeError,
+ lambda: tb.run())
+
+ def test_027d_fully_unconnected_input(self):
+ tb = gr.top_block()
+ hb = gr.hier_block2("block",
+ gr.io_signature(1, 1, 1),
+ gr.io_signature(1, 1, 1))
+ hdst = gr.vector_source_b([1,])
+ hb.connect(hdst, hb) # wire output internally
+ dst = gr.vector_sink_b()
+ tb.connect(hb, dst) # hb's input is not connected internally or externally
+ self.assertRaises(RuntimeError,
+ lambda: tb.run())
+
+ def test_028_singleton_reconfigure(self):
+ tb = gr.top_block()
+ hb = gr.hier_block2("block",
+ gr.io_signature(0, 0, 0), gr.io_signature(0, 0, 0))
+ src = gr.vector_source_b([1, ])
+ dst = gr.vector_sink_b()
+ hb.connect(src, dst)
+ tb.connect(hb) # Singleton connect
+ tb.lock()
+ tb.disconnect_all()
+ tb.connect(src, dst)
+ tb.unlock()
+
+ def test_029_singleton_disconnect(self):
+ tb = gr.top_block()
+ src = gr.vector_source_b([1, ])
+ dst = gr.vector_sink_b()
+ tb.connect(src, dst)
+ tb.disconnect(src) # Singleton disconnect
+ tb.connect(src, dst)
+ tb.run()
+ self.assertEquals(dst.data(), (1,))
+
+ def test_030_nested_input(self):
+ tb = gr.top_block()
+ src = gr.vector_source_b([1,])
+ hb1 = gr.hier_block2("hb1",
+ gr.io_signature(1, 1, gr.sizeof_char),
+ gr.io_signature(0, 0, 0))
+ hb2 = gr.hier_block2("hb2",
+ gr.io_signature(1, 1, gr.sizeof_char),
+ gr.io_signature(0, 0, 0))
+ dst = gr.vector_sink_b()
+ tb.connect(src, hb1)
+ hb1.connect(hb1, hb2)
+ hb2.connect(hb2, gr.kludge_copy(gr.sizeof_char), dst)
+ tb.run()
+ self.assertEquals(dst.data(), (1,))
+
+ def test_031_multiple_internal_inputs(self):
+ tb = gr.top_block()
+ src = gr.vector_source_f([1.0,])
+ hb = gr.hier_block2("hb",
+ gr.io_signature(1, 1, gr.sizeof_float),
+ gr.io_signature(1, 1, gr.sizeof_float))
+ m1 = gr.multiply_const_ff(1.0)
+ m2 = gr.multiply_const_ff(2.0)
+ add = gr.add_ff()
+ hb.connect(hb, m1) # m1 is connected to hb external input #0
+ hb.connect(hb, m2) # m2 is also connected to hb external input #0
+ hb.connect(m1, (add, 0))
+ hb.connect(m2, (add, 1))
+ hb.connect(add, hb) # add is connected to hb external output #0
+ dst = gr.vector_sink_f()
+ tb.connect(src, hb, dst)
+ tb.run()
+ self.assertEquals(dst.data(), (3.0,))
+
+ def test_032_nested_multiple_internal_inputs(self):
+ tb = gr.top_block()
+ src = gr.vector_source_f([1.0,])
+ hb = gr.hier_block2("hb",
+ gr.io_signature(1, 1, gr.sizeof_float),
+ gr.io_signature(1, 1, gr.sizeof_float))
+ hb2 = gr.hier_block2("hb",
+ gr.io_signature(1, 1, gr.sizeof_float),
+ gr.io_signature(1, 1, gr.sizeof_float))
+
+ m1 = gr.multiply_const_ff(1.0)
+ m2 = gr.multiply_const_ff(2.0)
+ add = gr.add_ff()
+ hb2.connect(hb2, m1) # m1 is connected to hb2 external input #0
+ hb2.connect(hb2, m2) # m2 is also connected to hb2 external input #0
+ hb2.connect(m1, (add, 0))
+ hb2.connect(m2, (add, 1))
+ hb2.connect(add, hb2) # add is connected to hb2 external output #0
+ hb.connect(hb, hb2, hb) # hb as hb2 as nested internal block
+ dst = gr.vector_sink_f()
+ tb.connect(src, hb, dst)
+ tb.run()
+ self.assertEquals(dst.data(), (3.0,))
+
+
+if __name__ == "__main__":
+ gr_unittest.run(test_hier_block2, "test_hier_block2.xml")
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..27d01092b
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_hilbert.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import math
+
+class test_hilbert (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_hilbert (self):
+ tb = self.tb
+ 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 ()
+ tb.connect (src1, head)
+ tb.connect (head, hilb)
+ tb.connect (hilb, dst1)
+ tb.run ()
+ dst_data = dst1.data ()
+ self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 5)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_hilbert, "test_hilbert.xml")
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..06b8d767e
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_iir.py
@@ -0,0 +1,159 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_iir (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = 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.tb.connect (src, op)
+ self.tb.connect (op, dst)
+ self.tb.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.tb.connect (src, op)
+ self.tb.connect (op, dst)
+ self.tb.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.tb.connect (src, op)
+ self.tb.connect (op, dst)
+ self.tb.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.tb.connect (src, op)
+ self.tb.connect (op, dst)
+ self.tb.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.tb.connect (src, op)
+ self.tb.connect (op, dst)
+ self.tb.run ()
+ result_data = dst.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, result_data)
+
+ def test_iir_direct_006 (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.tb.connect (src, op)
+ self.tb.connect (op, dst)
+ self.tb.run ()
+ result_data = dst.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, result_data)
+
+ def test_iir_direct_007 (self):
+ src_data = (1, 2, 3, 4, 5, 6, 7, 8)
+ expected_result = (2,2,5,5,8,8,11,11)
+ fftaps = (2, 1)
+ fbtaps = (0, -1)
+ src = gr.vector_source_f (src_data)
+ op = gr.iir_filter_ffd (fftaps, fbtaps)
+ fftaps = (2,0,1)
+ fbtaps = (0, -1)
+ op.set_taps (fftaps, fbtaps)
+ dst = gr.vector_sink_f ()
+ self.tb.connect (src, op)
+ self.tb.connect (op, dst)
+ self.tb.run ()
+ result_data = dst.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, result_data)
+
+ def test_iir_direct_008 (self):
+ src_data = (1, 2, 3, 4, 5, 6, 7, 8)
+ expected_result = (2,4,4,10,18,14,26,56)
+ fftaps = (2,)
+ fbtaps = (0, 1)
+ src = gr.vector_source_f (src_data)
+ op = gr.iir_filter_ffd (fftaps, fbtaps)
+ fftaps_data = (1)
+ fbtaps = (0,0, -1,3)
+ op.set_taps (fftaps, fbtaps)
+ dst = gr.vector_sink_f ()
+ self.tb.connect (src, op)
+ self.tb.connect (op, dst)
+ self.tb.run ()
+ result_data = dst.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, result_data)
+
+
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_iir, "test_iir.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_int_to_float.py b/gnuradio-core/src/python/gnuradio/gr/qa_int_to_float.py
new file mode 100755
index 000000000..7536b3820
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_int_to_float.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+#
+# Copyright 2011 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_int_to_float (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_001(self):
+
+ src_data = (0, 1, 2, 3, 4, 5, -1, -2, -3, -4, -5)
+ expected_result = [float(s) for s in src_data]
+ src = gr.vector_source_i(src_data)
+ op = gr.int_to_float()
+ dst = gr.vector_sink_f()
+
+ self.tb.connect(src, op, dst)
+ self.tb.run()
+ result_data = dst.data()
+
+ self.assertFloatTuplesAlmostEqual(expected_result, result_data)
+
+ def test_002(self):
+
+ vlen = 3
+ src_data = ( 65000, 65001, 65002, 65003, 65004, 65005,
+ -65001, -65002, -65003)
+ expected_result = [ 65000.0, 65001.0, 65002.0,
+ 65003.0, 65004.0, 65005.0,
+ -65001.0, -65002.0, -65003.0]
+ src = gr.vector_source_i(src_data)
+ s2v = gr.stream_to_vector(gr.sizeof_int, vlen)
+ op = gr.int_to_float(vlen)
+ v2s = gr.vector_to_stream(gr.sizeof_float, vlen)
+ dst = gr.vector_sink_f()
+
+ self.tb.connect(src, s2v, op, v2s, dst)
+ self.tb.run()
+ result_data = list(dst.data())
+
+ self.assertEqual(expected_result, result_data)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_int_to_float, "test_int_to_float.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_integrate.py b/gnuradio-core/src/python/gnuradio/gr/qa_integrate.py
new file mode 100755
index 000000000..ddb1310b6
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_integrate.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+#
+# Copyright 2008,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import math
+
+class test_integrate (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_000_ss(self):
+ src_data = (1, 2, 3, 4, 5, 6)
+ dst_data = (6, 15)
+ src = gr.vector_source_s(src_data)
+ itg = gr.integrate_ss(3)
+ dst = gr.vector_sink_s()
+ self.tb.connect(src, itg, dst)
+ self.tb.run()
+ self.assertEqual(dst_data, dst.data())
+
+ def test_001_ii(self):
+ src_data = (1, 2, 3, 4, 5, 6)
+ dst_data = (6, 15)
+ src = gr.vector_source_i(src_data)
+ itg = gr.integrate_ii(3)
+ dst = gr.vector_sink_i()
+ self.tb.connect(src, itg, dst)
+ self.tb.run()
+ self.assertEqual(dst_data, dst.data())
+
+ def test_002_ff(self):
+ src_data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
+ dst_data = [6.0, 15.0]
+ src = gr.vector_source_f(src_data)
+ itg = gr.integrate_ff(3)
+ dst = gr.vector_sink_f()
+ self.tb.connect(src, itg, dst)
+ self.tb.run()
+ self.assertFloatTuplesAlmostEqual(dst_data, dst.data(), 6)
+
+ def test_003_cc(self):
+ src_data = [1.0+1.0j, 2.0+2.0j, 3.0+3.0j, 4.0+4.0j, 5.0+5.0j, 6.0+6.0j]
+ dst_data = [6.0+6.0j, 15.0+15.0j]
+ src = gr.vector_source_c(src_data)
+ itg = gr.integrate_cc(3)
+ dst = gr.vector_sink_c()
+ self.tb.connect(src, itg, dst)
+ self.tb.run()
+ self.assertComplexTuplesAlmostEqual(dst_data, dst.data(), 6)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_integrate, "test_integrate.xml")
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..1ff178251
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_interleave.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import math
+
+class test_interleave (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = 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.tb.connect (src0, (op, 0))
+ self.tb.connect (src1, (op, 1))
+ self.tb.connect (src2, (op, 2))
+ self.tb.connect (src3, (op, 3))
+ self.tb.connect (op, dst)
+ self.tb.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.tb.connect (src, op)
+ self.tb.connect ((op, 0), dst0)
+ self.tb.connect ((op, 1), dst1)
+ self.tb.connect ((op, 2), dst2)
+ self.tb.connect ((op, 3), dst3)
+ self.tb.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.run(test_interleave, "test_interleave.xml")
+
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..9bd9977c7
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_interp_fir_filter.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import math
+
+class test_interp_fir_filter (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = 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.tb.connect (src, op)
+ self.tb.connect (op, dst)
+ self.tb.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.run(test_interp_fir_filter, "test_interp_fir_filter.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_keep_m_in_n.py b/gnuradio-core/src/python/gnuradio/gr/qa_keep_m_in_n.py
new file mode 100755
index 000000000..922671d02
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_keep_m_in_n.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+#
+# Copyright 2008,2010 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 3, 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 this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+from gnuradio import gr, gr_unittest
+import sys
+import random
+
+class test_keep_m_in_n(gr_unittest.TestCase):
+
+ def setUp(self):
+ pass
+
+ def tearDown(self):
+ pass
+
+ def test_001(self):
+ self.maxDiff = None;
+ tb = gr.top_block()
+ src = gr.vector_source_b( range(0,100) )
+
+ # itemsize, M, N, offset
+ km2 = gr.keep_m_in_n( 1, 1, 2, 0 );
+ km3 = gr.keep_m_in_n( 1, 1, 3, 1 );
+ km7 = gr.keep_m_in_n( 1, 1, 7, 2 );
+ snk2 = gr.vector_sink_b();
+ snk3 = gr.vector_sink_b();
+ snk7 = gr.vector_sink_b();
+ tb.connect(src,km2,snk2);
+ tb.connect(src,km3,snk3);
+ tb.connect(src,km7,snk7);
+ tb.run();
+
+ self.assertEqual(range(0,100,2), list(snk2.data()));
+ self.assertEqual(range(1,100,3), list(snk3.data()));
+ self.assertEqual(range(2,100,7), list(snk7.data()));
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_keep_m_in_n, "test_keep_m_in_n.xml")
+
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..2a3aa44b1
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_kludge_copy.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python
+#
+# Copyright 2006,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import math
+import random
+
+
+class test_kludge_copy(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.tb = gr.top_block()
+ self.rng = random.Random()
+ self.rng.seed(0)
+
+ def tearDown(self):
+ del self.tb
+ del self.rng
+
+ 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.tb.connect(src0, op, dst0)
+ self.tb.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.tb.connect(src0, (op, 0), dst0)
+ self.tb.connect(src1, (op, 1), dst1)
+ self.tb.run()
+ dst0_data = dst0.data()
+ dst1_data = dst1.data()
+ self.assertEqual(src0_data, dst0_data)
+ self.assertEqual(src1_data, dst1_data)
+
+ # Note: this is disabled due to triggering bug in ticket:181
+ # It only occurs with new top block code
+ def xtest_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.tb.connect(src0, (op, 0), dst0)
+ self.tb.connect(src1, (op, 1))
+ self.assertRaises(ValueError, self.tb.run)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_kludge_copy, "test_kludge_copy.xml")
+
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..39b5d781e
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_kludged_imports.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+#
+# Copyright 2005,2008,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_kludged_imports (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 blks2
+
+ def test_gru_import(self):
+ # make sure that this somewhat magic import works
+ from gnuradio import gru
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_kludged_imports, "test_kludged_imports.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_max.py b/gnuradio-core/src/python/gnuradio/gr/qa_max.py
new file mode 100755
index 000000000..f962df457
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_max.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+#
+# Copyright 2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import math
+
+
+class test_max (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+
+ def tearDown (self):
+ self.tb = None
+
+
+ def test_001(self):
+
+ src_data = (0,0.2,-0.3,0,12,0)
+ expected_result = (float(max(src_data)), )
+
+ src = gr.vector_source_f(src_data)
+ s2v = gr.stream_to_vector(gr.sizeof_float, len(src_data))
+ op = gr.max_ff( len(src_data) )
+ dst = gr.vector_sink_f()
+
+
+ self.tb.connect(src, s2v, op, dst)
+ self.tb.run()
+ result_data = dst.data()
+ self.assertEqual(expected_result, result_data)
+
+ def test_002(self):
+
+ src_data=(-100,-99,-98,-97,-96,-1)
+ expected_result = (float(max(src_data)), )
+
+ src = gr.vector_source_f(src_data)
+ s2v = gr.stream_to_vector(gr.sizeof_float, len(src_data))
+ op = gr.max_ff( len(src_data) )
+ dst = gr.vector_sink_f()
+
+ self.tb.connect(src, s2v, op, dst)
+ self.tb.run()
+ result_data = dst.data()
+ self.assertEqual(expected_result, result_data)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_max, "test_max.xml")
+
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..4cef39bd7
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_message.py
@@ -0,0 +1,131 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+
+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.msgq = gr.msg_queue ()
+
+ def tearDown (self):
+ 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()
+ tb = gr.top_block()
+ tb.connect(src, dst)
+ tb.run()
+ self.assertEquals(input_data, dst.data())
+
+ def test_301(self):
+ # Use itemsize, limit constructor
+ src = gr.message_source(gr.sizeof_char)
+ dst = gr.vector_sink_b()
+ tb = gr.top_block()
+ tb.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
+ tb.run()
+ self.assertEquals(tuple(map(ord, '0123456789')), dst.data())
+
+ def test_302(self):
+ # Use itemsize, msgq constructor
+ msgq = gr.msg_queue()
+ src = gr.message_source(gr.sizeof_char, msgq)
+ dst = gr.vector_sink_b()
+ tb = gr.top_block()
+ tb.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
+ tb.run()
+ self.assertEquals(tuple(map(ord, '0123456789')), dst.data())
+
+if __name__ == '__main__':
+ gr_unittest.run(test_message, "test_message.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_multiply_conjugate.py b/gnuradio-core/src/python/gnuradio/gr/qa_multiply_conjugate.py
new file mode 100644
index 000000000..1601a109e
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_multiply_conjugate.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+#
+# Copyright 2012 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_multiply_conjugate (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_000 (self):
+ src_data0 = (-2-2j, -1-1j, -2+2j, -1+1j,
+ 2-2j, 1-1j, 2+2j, 1+1j,
+ 0+0j)
+ src_data1 = (-3-3j, -4-4j, -3+3j, -4+4j,
+ 3-3j, 4-4j, 3+3j, 4+4j,
+ 0+0j)
+
+ exp_data = (12+0j, 8+0j, 12+0j, 8+0j,
+ 12+0j, 8+0j, 12+0j, 8+0j,
+ 0+0j)
+ src0 = gr.vector_source_c(src_data0)
+ src1 = gr.vector_source_c(src_data1)
+ op = gr.multiply_conjugate_cc ()
+ dst = gr.vector_sink_c ()
+
+ self.tb.connect(src0, (op,0))
+ self.tb.connect(src1, (op,1))
+ self.tb.connect(op, dst)
+ self.tb.run()
+ result_data = dst.data ()
+ self.assertEqual (exp_data, result_data)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_multiply_conjugate, "test_multiply_conjugate.xml")
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..afdfdfe13
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_mute.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2005,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_mute (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = 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.tb.connect (src, (op, s[0]))
+ dst = gr.vector_sink_i ()
+ self.tb.connect (op, dst)
+ self.tb.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.tb.connect (src, (op, s[0]))
+ dst = gr.vector_sink_f ()
+ self.tb.connect (op, dst)
+ self.tb.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.tb.connect (src, (op, s[0]))
+ dst = gr.vector_sink_c ()
+ self.tb.connect (op, dst)
+ self.tb.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.run(test_mute, "test_mute.xml")
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..a87ed87ee
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_nlog10.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+#
+# Copyright 2005,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_nlog10(gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = 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.tb.connect (src, op, dst)
+ self.tb.run()
+ result_data = dst.data()
+ self.assertFloatTuplesAlmostEqual (expected_result, result_data)
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_nlog10, "test_nlog10.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_noise.py b/gnuradio-core/src/python/gnuradio/gr/qa_noise.py
new file mode 100755
index 000000000..e87519150
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_noise.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+#
+# Copyright 2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_noise_source(gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_001(self):
+ # Just confirm that we can instantiate a noise source
+ op = gr.noise_source_f(gr.GR_GAUSSIAN, 10, 10)
+
+ def test_002(self):
+ # Test get methods
+ set_type = gr.GR_GAUSSIAN
+ set_ampl = 10
+ op = gr.noise_source_f(set_type, set_ampl, 10)
+ get_type = op.type()
+ get_ampl = op.amplitude()
+
+ self.assertEqual(get_type, set_type)
+ self.assertEqual(get_ampl, set_ampl)
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_noise_source, "test_noise_source.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_pack_k_bits.py b/gnuradio-core/src/python/gnuradio/gr/qa_pack_k_bits.py
new file mode 100755
index 000000000..25fc5e9fc
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_pack_k_bits.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+#
+# Copyright 2006,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import random
+
+class test_pack(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.tb = gr.top_block ()
+
+ def tearDown(self):
+ self.tb = 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.pack_k_bits_bb(1)
+ dst = gr.vector_sink_b()
+ self.tb.connect(src, op, dst)
+ self.tb.run()
+ self.assertEqual(expected_results, dst.data())
+
+ def test_002(self):
+ src_data = (1,0,1,1,0,0,0,1)
+ expected_results = ( 2, 3, 0, 1)
+ src = gr.vector_source_b(src_data,False)
+ op = gr.pack_k_bits_bb(2)
+ dst = gr.vector_sink_b()
+ self.tb.connect(src, op, dst)
+ self.tb.run()
+ #self.assertEqual(expected_results, dst.data())
+ self.assertEqual(expected_results, dst.data())
+
+ def test_003(self):
+ src_data = expected_results = map(lambda x: random.randint(0,3), range(10));
+ src = gr.vector_source_b( src_data );
+ pack = gr.pack_k_bits_bb(2);
+ unpack = gr.unpack_k_bits_bb(2);
+ snk = gr.vector_sink_b();
+ self.tb.connect(src,unpack,pack,snk);
+ self.tb.run()
+ self.assertEqual(list(expected_results), list(snk.data()));
+
+if __name__ == '__main__':
+ gr_unittest.run(test_pack, "test_pack.xml")
+
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..08accd0ad
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_packed_to_unpacked.py
@@ -0,0 +1,405 @@
+#!/usr/bin/env python
+#
+# Copyright 2005,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import random
+
+class test_packing(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.tb = gr.top_block ()
+
+ def tearDown(self):
+ self.tb = 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.tb.connect(src, op)
+
+ dst = gr.vector_sink_b()
+ self.tb.connect(op, dst)
+
+ self.tb.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.tb.connect(src, op)
+
+ dst = gr.vector_sink_b()
+ self.tb.connect(op, dst)
+
+ self.tb.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.tb.connect(src, op)
+
+ dst = gr.vector_sink_b()
+ self.tb.connect(op, dst)
+
+ self.tb.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.tb.connect(src, op)
+
+ dst = gr.vector_sink_b()
+ self.tb.connect(op, dst)
+
+ self.tb.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.tb.connect(src, op)
+
+ dst = gr.vector_sink_b()
+ self.tb.connect(op, dst)
+
+ self.tb.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.tb.connect(src, op)
+
+ dst = gr.vector_sink_b()
+ self.tb.connect(op, dst)
+
+ self.tb.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.tb.connect(src, op)
+
+ dst = gr.vector_sink_b()
+ self.tb.connect(op, dst)
+
+ self.tb.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.tb.connect(src, op)
+
+ dst = gr.vector_sink_b()
+ self.tb.connect(op, dst)
+
+ self.tb.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.tb.connect(src, op1, op2)
+
+ dst = gr.vector_sink_b()
+ self.tb.connect(op2, dst)
+
+ self.tb.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.tb.connect(src, op1, op2)
+ dst = gr.vector_sink_b()
+ self.tb.connect(op2, dst)
+
+ self.tb.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.tb.connect(src, op1, op2)
+ dst = gr.vector_sink_b()
+ self.tb.connect(op2, dst)
+
+ self.tb.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.tb.connect(src, op1, op2)
+ dst = gr.vector_sink_s()
+ self.tb.connect(op2, dst)
+
+ self.tb.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.tb.connect(src, op1, op2)
+ dst = gr.vector_sink_s()
+ self.tb.connect(op2, dst)
+
+ self.tb.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.tb.connect(src, op1, op2)
+ dst = gr.vector_sink_s()
+ self.tb.connect(op2, dst)
+
+ self.tb.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.tb.connect(src, op1, op2)
+ dst = gr.vector_sink_s()
+ self.tb.connect(op2, dst)
+
+ self.tb.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.tb.connect(src, op1, op2)
+ dst = gr.vector_sink_i()
+ self.tb.connect(op2, dst)
+
+ self.tb.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.tb.connect(src, op1, op2)
+ dst = gr.vector_sink_i()
+ self.tb.connect(op2, dst)
+
+ self.tb.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.tb.connect(src, op1, op2)
+ dst = gr.vector_sink_i()
+ self.tb.connect(op2, dst)
+
+ self.tb.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.tb.connect(src, op1, op2)
+ dst = gr.vector_sink_i()
+ self.tb.connect(op2, dst)
+
+ self.tb.run()
+ self.assertEqual(expected_results, dst.data())
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_packing, "test_packing.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_pdu.py b/gnuradio-core/src/python/gnuradio/gr/qa_pdu.py
new file mode 100755
index 000000000..098aabb4a
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_pdu.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python
+#
+# Copyright 2012 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import pmt
+import time
+
+class test_pdu(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.tb = gr.top_block()
+
+ def tearDown(self):
+ self.tb = None
+
+ def test_000(self):
+ # Just run some data through and make sure it doesn't puke.
+ src_data = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
+
+ src = gr.pdu_to_tagged_stream(gr.pdu_byte)
+ snk3 = gr.tagged_stream_to_pdu(gr.pdu_byte)
+ snk2 = gr.vector_sink_b()
+ snk = gr.tag_debug(1, "test")
+
+ dbg = gr.message_debug()
+
+ # Test that the right number of ports exist.
+ pi = dbg.message_ports_in()
+ po = dbg.message_ports_out()
+ self.assertEqual(pmt.pmt_length(pi), 3)
+ self.assertEqual(pmt.pmt_length(po), 0)
+
+ pi = snk3.message_ports_in()
+ po = snk3.message_ports_out()
+ self.assertEqual(pmt.pmt_length(pi), 0)
+ self.assertEqual(pmt.pmt_length(po), 1)
+
+ #print "Message Debug input ports: "
+ #pmt.pmt_print(pi)
+ #print "Message Debug output ports: "
+ #pmt.pmt_print(po)
+
+ #print "Stream to PDU input ports: "
+ #pmt.pmt_print(pi)
+ #print "Stream to PDU output ports: "
+ #pmt.pmt_print(po)
+
+ self.tb.connect(src, snk)
+ self.tb.connect(src, snk2)
+ self.tb.connect(src, snk3)
+
+ self.tb.msg_connect(snk3, "pdus", dbg, "store")
+ self.tb.start()
+
+ # make our reference and message pmts
+ port = pmt.pmt_intern("pdus")
+ msg = pmt.pmt_cons( pmt.PMT_NIL, pmt.pmt_make_u8vector(16, 0xFF) )
+
+ #print "printing port & msg"
+ #pmt.pmt_print(port)
+ #pmt.pmt_print(msg)
+
+ # post the message
+ src.to_basic_block()._post( port, msg )
+
+ while(dbg.num_messages() < 1):
+ time.sleep(0.1)
+ self.tb.stop()
+ self.tb.wait()
+
+ # Get the vector of data from the vector sink
+ result_data = snk2.data()
+
+ # Get the vector of data from the message sink
+ # Convert the message PMT as a pair into its vector
+ result_msg = dbg.get_message(0)
+ msg_vec = pmt.pmt_cdr(result_msg)
+ pmt.pmt_print(msg_vec)
+
+ # Convert the PMT vector into a Python list
+ msg_data = []
+ for i in xrange(16):
+ msg_data.append(pmt.pmt_u8vector_ref(msg_vec, i))
+
+ actual_data = 16*[0xFF,]
+ self.assertEqual(actual_data, list(result_data))
+ self.assertEqual(actual_data, msg_data)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_pdu, "test_pdu.xml")
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..1f24062b1
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_pipe_fittings.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python
+#
+# Copyright 2005,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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.tb = gr.top_block ()
+
+ def tearDown(self):
+ self.tb = 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.tb.connect(src, op)
+
+ dsts = []
+ for i in range(n):
+ dst = gr.vector_sink_i()
+ self.tb.connect((op, i), (dst, 0))
+ dsts.append(dst)
+
+ self.tb.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.tb.connect(src, op1)
+ for i in range(n):
+ self.tb.connect((op1, i), (op2, i))
+ self.tb.connect(op2, dst)
+
+ self.tb.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.tb.connect(src, op1)
+ for i in range(n):
+ self.tb.connect((op1, i), (op2, i))
+ self.tb.connect(op2, op3, dst)
+
+ self.tb.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.tb.connect(src, op1, op2)
+ for i in range(n):
+ self.tb.connect((op2, i), (op3, i))
+ self.tb.connect(op3, dst)
+
+ self.tb.run()
+ self.assertEqual(expected_results, dst.data())
+
+if __name__ == '__main__':
+ gr_unittest.run(test_pipe_fittings, "test_pipe_fittings.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_pll_carriertracking.py b/gnuradio-core/src/python/gnuradio/gr/qa_pll_carriertracking.py
new file mode 100755
index 000000000..8964db53d
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_pll_carriertracking.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2007,2010,2011 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import math
+
+class test_pll_carriertracking (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_pll_carriertracking (self):
+ expected_result = ((1.00000238419+7.21919457547e-09j),
+ (0.998025715351+0.062790453434j),
+ (0.992777824402+0.119947694242j),
+ (0.985192835331+0.171441286802j),
+ (0.976061582565+0.217501848936j),
+ (0.966034710407+0.258409559727j),
+ (0.95565611124+0.294477283955j),
+ (0.945357382298+0.326030552387j),
+ (0.935475051403+0.353395611048j),
+ (0.926258146763+0.376889169216j),
+ (0.917895197868+0.39681750536j),
+ (0.910515546799+0.413470208645j),
+ (0.904196679592+0.427117019892j),
+ (0.898972511292+0.438006043434j),
+ (0.894769787788+0.446523308754j),
+ (0.891652584076+0.452715367079j),
+ (0.8895829916+0.456773489714j),
+ (0.888502895832+0.458873122931j),
+ (0.888343691826+0.459175437689j),
+ (0.889035582542+0.457833081484j),
+ (0.890497922897+0.454985737801j),
+ (0.892645597458+0.450762689114j),
+ (0.895388305187+0.445282936096j),
+ (0.898648142815+0.438664674759j),
+ (0.902342617512+0.431016951799j),
+ (0.906392872334+0.422441422939j),
+ (0.910642921925+0.413191765547j),
+ (0.915039420128+0.403358519077j),
+ (0.919594764709+0.392864197493j),
+ (0.92425006628+0.381792247295j),
+ (0.928944349289+0.370217680931j),
+ (0.933634519577+0.358220815659j),
+ (0.938279032707+0.345874190331j),
+ (0.942840516567+0.333247303963j),
+ (0.947280526161+0.32040438056j),
+ (0.951574921608+0.307409763336j),
+ (0.955703914165+0.294323593378j),
+ (0.959648966789+0.281201630831j),
+ (0.963392794132+0.268095195293j),
+ (0.966880619526+0.255221515894j),
+ (0.970162451267+0.242447137833j),
+ (0.973235487938+0.229809194803j),
+ (0.97609680891+0.217341512442j),
+ (0.978744983673+0.20507311821j),
+ (0.981189727783+0.193033605814j),
+ (0.983436584473+0.181248426437j),
+ (0.985490739346+0.169738590717j),
+ (0.987353682518+0.158523857594j),
+ (0.989041447639+0.147622272372j),
+ (0.990563035011+0.137049794197j),
+ (0.991928339005+0.126818582416j),
+ (0.993117690086+0.117111675441j),
+ (0.994156062603+0.107930034399j),
+ (0.995076179504+0.0990980416536j),
+ (0.995887458324+0.0906178802252j),
+ (0.996591091156+0.0824909061193j),
+ (0.997202515602+0.0747182965279j),
+ (0.997730851173+0.0672992765903j),
+ (0.998185396194+0.0602316558361j),
+ (0.99856698513+0.0535135567188j),
+ (0.998885989189+0.0471420884132j),
+ (0.99915266037+0.0411129891872j),
+ (0.999372899532+0.0354214012623j),
+ (0.999548316002+0.0300626158714j),
+ (0.999680638313+0.0252036750317j),
+ (0.999784469604+0.020652115345j),
+ (0.999865531921+0.0163950324059j),
+ (0.999923825264+0.0124222636223j),
+ (0.999960243702+0.00872156023979j),
+ (0.999983668327+0.00528120994568j),
+ (0.999997138977+0.00209015607834j),
+ (1.00000119209-0.00086285173893j),
+ (0.999992132187-0.00358882546425j),
+ (0.999979138374-0.00609711557627j),
+ (0.999963641167-0.00839691981673j),
+ (0.999947249889-0.0104993218556j),
+ (0.999924004078-0.0122378543019j),
+ (0.999904811382-0.0136305987835j),
+ (0.999888062477-0.0148707330227j),
+ (0.9998742342-0.0159679055214j),
+ (0.999856114388-0.0169314742088j),
+ (0.999839782715-0.0177700817585j),
+ (0.999826967716-0.0184917747974j),
+ (0.999818325043-0.0191045701504j),
+ (0.999807476997-0.0196143388748j),
+ (0.999797284603-0.0200265944004j),
+ (0.999791204929-0.0203481912613j),
+ (0.99978852272-0.0205836892128j),
+ (0.99978530407-0.0207380950451j),
+ (0.999785065651-0.0206423997879j),
+ (0.999787807465-0.0204866230488j),
+ (0.999794304371-0.0202808082104j),
+ (0.999800384045-0.0200312435627j),
+ (0.999803245068-0.0197458267212j),
+ (0.9998087883-0.0194311738014j),
+ (0.999816894531-0.0190933048725j),
+ (0.999825954437-0.0187371373177j),
+ (0.999829888344-0.0183679759502j),
+ (0.999835848808-0.017987690866j),
+ (0.999844014645-0.0176006518304j))
+
+ sampling_freq = 10e3
+ freq = sampling_freq / 100
+
+ loop_bw = math.pi/100.0
+ maxf = 1
+ minf = -1
+
+ src = gr.sig_source_c (sampling_freq, gr.GR_COS_WAVE, freq, 1.0)
+ pll = gr.pll_carriertracking_cc(loop_bw, maxf, minf)
+ head = gr.head (gr.sizeof_gr_complex, int (freq))
+ dst = gr.vector_sink_c ()
+
+ self.tb.connect (src, pll, head)
+ self.tb.connect (head, dst)
+
+ self.tb.run ()
+ dst_data = dst.data ()
+ self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 5)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_pll_carriertracking, "test_pll_carriertracking.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_pll_freqdet.py b/gnuradio-core/src/python/gnuradio/gr/qa_pll_freqdet.py
new file mode 100755
index 000000000..219e9b84b
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_pll_freqdet.py
@@ -0,0 +1,160 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2007,2010,2011 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import math
+
+class test_pll_freqdet (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_pll_freqdet (self):
+ expected_result = (0.0,
+ 4.33888922882e-08,
+ 0.367369994515,
+ 1.08135249597,
+ 2.10983253908,
+ 3.42221529438,
+ 4.98940390402,
+ 6.78379190842,
+ 8.77923286024,
+ 10.9510106794,
+ 13.2758363182,
+ 15.7317829127,
+ 18.2982902299,
+ 20.9561068599,
+ 23.6755271122,
+ 26.452952094,
+ 29.2731265301,
+ 32.1219053479,
+ 34.9862418188,
+ 37.8540971414,
+ 40.7144315483,
+ 43.5571390869,
+ 46.3730179743,
+ 49.1537231663,
+ 51.8917218889,
+ 54.58026103,
+ 57.2015358514,
+ 59.7513664199,
+ 62.2380533124,
+ 64.657612252,
+ 67.006640002,
+ 69.2822432184,
+ 71.4820384499,
+ 73.6041047056,
+ 75.6469478817,
+ 77.6094829742,
+ 79.4909866472,
+ 81.2911031615,
+ 83.0097850853,
+ 84.6355598352,
+ 86.1820937186,
+ 87.6504420946,
+ 89.0418441206,
+ 90.3577286819,
+ 91.5996432431,
+ 92.7692775646,
+ 93.8684162704,
+ 94.8989269904,
+ 95.8627662892,
+ 96.7619381633,
+ 97.598505899,
+ 98.362769679,
+ 99.0579904444,
+ 99.6992633875,
+ 100.288805948,
+ 100.828805921,
+ 101.321421457,
+ 101.76878699,
+ 102.17300138,
+ 102.536116055,
+ 102.860158727,
+ 103.147085962,
+ 103.398830608,
+ 103.617254366,
+ 103.792467691,
+ 103.939387906,
+ 104.060030865,
+ 104.15631756,
+ 104.230085975,
+ 104.283067372,
+ 104.316933727,
+ 104.333238432,
+ 104.333440018,
+ 104.318914008,
+ 104.290941063,
+ 104.250742554,
+ 104.187634452,
+ 104.103822339,
+ 104.013227468,
+ 103.916810336,
+ 103.815448432,
+ 103.709936239,
+ 103.600997093,
+ 103.489283183,
+ 103.375351833,
+ 103.259712936,
+ 103.142828952,
+ 103.025091195,
+ 102.90686726,
+ 102.776726069,
+ 102.648078982,
+ 102.521459607,
+ 102.397294831,
+ 102.275999684,
+ 102.157882471,
+ 102.043215927,
+ 101.93218978,
+ 101.824958181,
+ 101.72159228,
+ 101.622151366)
+
+ sampling_freq = 10e3
+ freq = sampling_freq / 100
+
+ loop_bw = math.pi/100.0
+ maxf = 1
+ minf = -1
+
+ src = gr.sig_source_c (sampling_freq, gr.GR_COS_WAVE, freq, 1.0)
+ pll = gr.pll_freqdet_cf(loop_bw, maxf, minf)
+ head = gr.head (gr.sizeof_float, int (freq))
+ dst = gr.vector_sink_f ()
+
+ self.tb.connect (src, pll, head)
+ self.tb.connect (head, dst)
+
+ self.tb.run ()
+ dst_data = dst.data ()
+
+ # convert it from normalized frequency to absolute frequency (Hz)
+ dst_data = [i*(sampling_freq/(2*math.pi)) for i in dst_data]
+
+ self.assertFloatTuplesAlmostEqual (expected_result, dst_data, 3)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_pll_freqdet, "test_pll_freqdet.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_pll_refout.py b/gnuradio-core/src/python/gnuradio/gr/qa_pll_refout.py
new file mode 100755
index 000000000..f319f6381
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_pll_refout.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import math
+
+class test_pll_refout (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_pll_refout (self):
+ expected_result = ((1+0j),
+ (1+6.4087357643e-10j),
+ (0.999985277653+0.00542619498447j),
+ (0.999868750572+0.0162021834403j),
+ (0.99948567152+0.0320679470897j),
+ (0.99860727787+0.0527590736747j),
+ (0.996953129768+0.0780025869608j),
+ (0.994203746319+0.107512556016j),
+ (0.990011692047+0.140985429287j),
+ (0.984013140202+0.178095817566j),
+ (0.975838363171+0.218493551016j),
+ (0.965121984482+0.261800557375j),
+ (0.95151245594+0.307610183954j),
+ (0.934681296349+0.355486690998j),
+ (0.914401650429+0.404808044434j),
+ (0.890356600285+0.455263823271j),
+ (0.862329125404+0.506348133087j),
+ (0.830152392387+0.557536482811j),
+ (0.793714106083+0.608290970325j),
+ (0.752960026264+0.658066213131j),
+ (0.707896590233+0.706316053867j),
+ (0.658591926098+0.752500295639j),
+ (0.605175673962+0.796091973782j),
+ (0.547837555408+0.836584687233j),
+ (0.48682525754+0.873499393463j),
+ (0.42244040966+0.906390726566j),
+ (0.355197101831+0.934791445732j),
+ (0.285494059324+0.958380460739j),
+ (0.213591173291+0.976923108101j),
+ (0.139945343137+0.990159213543j),
+ (0.065038472414+0.997882783413j),
+ (-0.0106285437942+0.999943494797j),
+ (-0.0865436866879+0.996248066425j),
+ (-0.162189796567+0.986759603024j),
+ (-0.23705175519+0.971496999264j),
+ (-0.310622543097+0.950533330441j),
+ (-0.38240903616+0.923993110657j),
+ (-0.451937526464+0.89204955101j),
+ (-0.518758952618+0.854920566082j),
+ (-0.582311093807+0.812966048717j),
+ (-0.642372369766+0.76639264822j),
+ (-0.698591887951+0.715520322323j),
+ (-0.750654160976+0.660695314407j),
+ (-0.798280358315+0.602286040783j),
+ (-0.841228663921+0.540679454803j),
+ (-0.87929558754+0.476276367903j),
+ (-0.912315964699+0.409486919641j),
+ (-0.940161883831+0.340728074312j),
+ (-0.962742805481+0.270418733358j),
+ (-0.980004072189+0.198977485299j),
+ (-0.991925954819+0.126818284392j),
+ (-0.99851256609+0.0545223206282j),
+ (-0.999846458435-0.0175215266645j),
+ (-0.996021270752-0.0891158208251j),
+ (-0.987133920193-0.159895718098j),
+ (-0.973306238651-0.2295101583j),
+ (-0.954683184624-0.297624111176j),
+ (-0.931430280209-0.363919824362j),
+ (-0.903732538223-0.428097635508j),
+ (-0.871792256832-0.489875763655j),
+ (-0.835827112198-0.548992812634j),
+ (-0.796068251133-0.605206847191j),
+ (-0.752758979797-0.658296227455j),
+ (-0.706152498722-0.70805978775j),
+ (-0.656641483307-0.754202902317j),
+ (-0.604367733002-0.79670548439j),
+ (-0.549597978592-0.835429251194j),
+ (-0.492602348328-0.870254516602j),
+ (-0.433654457331-0.901079237461j),
+ (-0.373029649258-0.927819430828j),
+ (-0.31100410223-0.950408577919j),
+ (-0.247853919864-0.968797445297j),
+ (-0.183855071664-0.982953369617j),
+ (-0.119282215834-0.992860376835j),
+ (-0.0544078871608-0.998518764973j),
+ (0.0104992967099-0.999944865704j),
+ (0.0749994292855-0.997183561325j),
+ (0.138844624162-0.990314185619j),
+ (0.201967850327-0.979392170906j),
+ (0.264124274254-0.964488625526j),
+ (0.325075358152-0.945688128471j),
+ (0.3845885396-0.92308807373j),
+ (0.442438393831-0.89679890871j),
+ (0.498407125473-0.866943061352j),
+ (0.552284479141-0.833655714989j),
+ (0.603869199753-0.797083437443j),
+ (0.652970373631-0.757383465767j),
+ (0.69940674305-0.714723825455j),
+ (0.743007957935-0.66928255558j),
+ (0.78350687027-0.62138313055j),
+ (0.820889055729-0.571087777615j),
+ (0.855021059513-0.51859331131j),
+ (0.885780930519-0.46410369873j),
+ (0.913058102131-0.407829582691j),
+ (0.936754107475-0.349988251925j),
+ (0.956783294678-0.290801793337j),
+ (0.973072886467-0.230497643352j),
+ (0.985563337803-0.169307261705j),
+ (0.9942086339-0.1074674353j),
+ (0.9989772439-0.0452152714133j))
+
+ sampling_freq = 10e3
+ freq = sampling_freq / 100
+
+ loop_bw = math.pi/100.0
+ maxf = 1
+ minf = -1
+
+ src = gr.sig_source_c (sampling_freq, gr.GR_COS_WAVE, freq, 1.0)
+ pll = gr.pll_refout_cc(loop_bw, maxf, minf)
+ head = gr.head (gr.sizeof_gr_complex, int (freq))
+ dst = gr.vector_sink_c ()
+
+ self.tb.connect (src, pll, head)
+ self.tb.connect (head, dst)
+
+ self.tb.run ()
+ dst_data = dst.data ()
+ self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 4)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_pll_refout, "test_pll_refout.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_pn_correlator_cc.py b/gnuradio-core/src/python/gnuradio/gr/qa_pn_correlator_cc.py
new file mode 100755
index 000000000..6a62a6997
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_pn_correlator_cc.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+#
+# Copyright 2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_pn_correlator_cc(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.tb = gr.top_block ()
+
+ def tearDown(self):
+ self.tb = None
+
+ def test_000_make(self):
+ c = gr.pn_correlator_cc(10)
+
+ def test_001_correlate(self):
+ degree = 10
+ length = 2**degree-1
+ src = gr.glfsr_source_f(degree)
+ head = gr.head(gr.sizeof_float, length*length)
+ f2c = gr.float_to_complex()
+ corr = gr.pn_correlator_cc(degree)
+ dst = gr.vector_sink_c()
+ self.tb.connect(src, head, f2c, corr, dst)
+ self.tb.run()
+ data = dst.data()
+ self.assertEqual(data[-1], (1.0+0j))
+
+if __name__ == '__main__':
+ gr_unittest.run(test_pn_correlator_cc, "test_pn_correlator_cc.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_probe_signal.py b/gnuradio-core/src/python/gnuradio/gr/qa_probe_signal.py
new file mode 100644
index 000000000..4e10afdb6
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_probe_signal.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+#
+# Copyright 2012 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import time
+
+from gnuradio import gr, gr_unittest
+
+class test_probe_signal (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_001(self):
+
+ value = 12.3
+ repeats = 100
+ src_data = [value] * repeats
+
+ src = gr.vector_source_f(src_data)
+ dst = gr.probe_signal_f()
+
+ self.tb.connect(src, dst)
+ self.tb.run()
+ output = dst.level()
+ self.assertAlmostEqual(value, output, places=6)
+
+ def test_002(self):
+
+ vector_length = 10
+ repeats = 10
+ value = [0.5+i for i in range(0, vector_length)]
+ src_data = value * repeats
+
+ src = gr.vector_source_f(src_data)
+ s2v = gr.stream_to_vector(gr.sizeof_float, vector_length)
+ dst = gr.probe_signal_vf(vector_length)
+
+ self.tb.connect(src, s2v, dst)
+ self.tb.run()
+ output = dst.level()
+ self.assertEqual(len(output), vector_length)
+ self.assertAlmostEqual(value[3], output[3], places=6)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_probe_signal, "test_probe_signal.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_python_message_passing.py b/gnuradio-core/src/python/gnuradio/gr/qa_python_message_passing.py
new file mode 100644
index 000000000..06bb96947
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_python_message_passing.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python
+#
+# Copyright 2013 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+try: import pmt
+except: from gruel import pmt
+import numpy
+import time
+
+# Simple block to generate messages
+class message_generator(gr.sync_block):
+ def __init__(self, msg_list, msg_interval):
+ gr.sync_block.__init__(
+ self,
+ name = "message generator",
+ in_sig = [numpy.float32],
+ out_sig = None
+ )
+ self.msg_list = msg_list
+ self.msg_interval = msg_interval
+ self.msg_ctr = 0
+ self.message_port_register_out(pmt.pmt_intern('out_port'))
+
+
+ def work(self, input_items, output_items):
+ inLen = len(input_items[0])
+ while self.msg_ctr < len(self.msg_list) and \
+ (self.msg_ctr * self.msg_interval) < \
+ (self.nitems_read(0) + inLen):
+ self.message_port_pub(pmt.pmt_intern('out_port'),
+ self.msg_list[self.msg_ctr])
+ self.msg_ctr += 1
+ return inLen
+
+# Simple block to consume messages
+class message_consumer(gr.sync_block):
+ def __init__(self):
+ gr.sync_block.__init__(
+ self,
+ name = "message consumer",
+ in_sig = None,
+ out_sig = None
+ )
+ self.msg_list = []
+ self.message_port_register_in(pmt.pmt_intern('in_port'))
+ self.set_msg_handler(pmt.pmt_intern('in_port'),
+ self.handle_msg)
+
+ def handle_msg(self, msg):
+ # Create a new PMT from long value and put in list
+ self.msg_list.append(pmt.pmt_from_long(pmt.pmt_to_long(msg)))
+
+class test_python_message_passing(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.tb = gr.top_block()
+
+ def tearDown(self):
+ self.tb = None
+
+ def test_000(self):
+ num_msgs = 10
+ msg_interval = 1000
+ msg_list = []
+ for i in range(num_msgs):
+ msg_list.append(pmt.pmt_from_long(i))
+
+ # Create vector source with dummy data to trigger messages
+ src_data = []
+ for i in range(num_msgs*msg_interval):
+ src_data.append(float(i))
+ src = gr.vector_source_f(src_data, False)
+ msg_gen = message_generator(msg_list, msg_interval)
+ msg_cons = message_consumer()
+
+ # Connect vector source to message gen
+ self.tb.connect(src, msg_gen)
+
+ # Connect message generator to message consumer
+ self.tb.msg_connect(msg_gen, 'out_port', msg_cons, 'in_port')
+
+ # Verify that the messgae port query functions work
+ self.assertEqual(pmt.pmt_symbol_to_string(pmt.pmt_vector_ref(
+ msg_gen.message_ports_out(), 0)), 'out_port')
+ self.assertEqual(pmt.pmt_symbol_to_string(pmt.pmt_vector_ref(
+ msg_cons.message_ports_in(), 0)), 'in_port')
+
+ # Run to verify message passing
+ self.tb.start()
+
+ # Wait for all messages to be sent
+ while msg_gen.msg_ctr < num_msgs:
+ time.sleep(0.5)
+ self.tb.stop()
+ self.tb.wait()
+
+ # Verify that the message consumer got all the messages
+ self.assertEqual(num_msgs, len(msg_cons.msg_list))
+ for i in range(num_msgs):
+ self.assertTrue(pmt.pmt_equal(msg_list[i], msg_cons.msg_list[i]))
+
+if __name__ == '__main__':
+ gr_unittest.run(test_python_message_passing,
+ 'test_python_message_passing.xml')
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..cc963d757
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_rational_resampler.py
@@ -0,0 +1,298 @@
+#!/usr/bin/env python
+#
+# Copyright 2005,2006,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+from gnuradio import blks2
+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):
+ tb = gr.top_block()
+ src = gr.vector_source_f(src_data)
+ op = gr.fir_filter_fff(decim, taps)
+ dst = gr.vector_sink_f()
+ tb.connect(src, op, dst)
+ tb.run()
+ result_data = dst.data()
+ tb = None
+ return result_data
+
+def reference_interp_filter(src_data, interp, taps):
+ tb = gr.top_block()
+ src = gr.vector_source_f(src_data)
+ op = gr.interp_fir_filter_fff(interp, taps)
+ dst = gr.vector_sink_f()
+ tb.connect(src, op, dst)
+ tb.run()
+ result_data = dst.data()
+ tb = None
+ return result_data
+
+def reference_interp_dec_filter(src_data, interp, decim, taps):
+ tb = gr.top_block()
+ 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()
+ tb.connect(src, up, dn, dst)
+ tb.run()
+ result_data = dst.data()
+ tb = None
+ return result_data
+
+
+class test_rational_resampler (gr_unittest.TestCase):
+
+ def setUp(self):
+ pass
+
+ def tearDown(self):
+ pass
+
+ #
+ # 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])
+
+ tb = gr.top_block()
+ src = gr.vector_source_f(src_data)
+ op = gr.rational_resampler_base_fff(1, 1, taps)
+ dst = gr.vector_sink_f()
+ tb.connect(src, op)
+ tb.connect(op, dst)
+ tb.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])
+
+ tb = gr.top_block()
+ src = gr.vector_source_f(src_data)
+ op = gr.rational_resampler_base_fff(interpolation, 1, taps)
+ dst = gr.vector_sink_f()
+ tb.connect(src, op)
+ tb.connect(op, dst)
+ tb.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)
+
+ tb = gr.top_block()
+ src = gr.vector_source_f(src_data)
+ op = gr.rational_resampler_base_fff(interpolation, 1, taps)
+ dst = gr.vector_sink_f()
+ tb.connect(src, op)
+ tb.connect(op, dst)
+ tb.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)
+
+ tb = gr.top_block()
+ src = gr.vector_source_f(src_data)
+ op = gr.rational_resampler_base_fff(1, decimation, taps)
+ dst = gr.vector_sink_f()
+ tb.connect(src, op)
+ tb.connect(op, dst)
+ tb.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)
+
+ tb = gr.top_block()
+ src = gr.vector_source_f(src_data)
+ op = gr.rational_resampler_base_fff(1, decim, taps)
+ dst = gr.vector_sink_f()
+ tb.connect(src, op, dst)
+ tb.run()
+ tb = 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)
+
+ tb = gr.top_block()
+ src = gr.vector_source_f(src_data)
+ op = gr.rational_resampler_base_fff(interp, 1, taps)
+ dst = gr.vector_sink_f()
+ tb.connect(src, op, dst)
+ tb.run()
+ tb = 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)
+
+ tb = gr.top_block()
+ src = gr.vector_source_f(src_data)
+ op = gr.rational_resampler_base_fff(interp, decimation, taps)
+ dst = gr.vector_sink_f()
+ tb.connect(src, op)
+ tb.connect(op, dst)
+ tb.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 blks2.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])
+
+ tb = gr.top_block()
+ src = gr.vector_source_f(src_data)
+ op = blks2.rational_resampler_fff(interpolation, 1, taps=taps)
+ dst = gr.vector_sink_f()
+ tb.connect(src, op)
+ tb.connect(op, dst)
+ tb.run()
+ result_data = dst.data()
+ self.assertEqual(expected_result, result_data)
+
+
+if __name__ == '__main__':
+ pass
+ # FIXME: Disabled, see ticket:210
+ # gr_unittest.run(test_rational_resampler, "test_rational_resampler.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_regenerate.py b/gnuradio-core/src/python/gnuradio/gr/qa_regenerate.py
new file mode 100755
index 000000000..5aca03b77
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_regenerate.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+#
+# Copyright 2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import math
+
+class test_regenerate (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_regen1 (self):
+ tb = self.tb
+
+ data = [0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+
+ expected_result = (0, 0, 0,
+ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+
+
+ src = gr.vector_source_b(data, False)
+ regen = gr.regenerate_bb(5, 2)
+ dst = gr.vector_sink_b()
+
+ tb.connect (src, regen)
+ tb.connect (regen, dst)
+ tb.run ()
+
+ dst_data = dst.data ()
+
+ self.assertEqual (expected_result, dst_data)
+
+ def test_regen2 (self):
+ tb = self.tb
+
+ data = 200*[0,]
+ data[9] = 1
+ data[99] = 1
+
+ expected_result = 200*[0,]
+ expected_result[9] = 1
+ expected_result[19] = 1
+ expected_result[29] = 1
+ expected_result[39] = 1
+
+ expected_result[99] = 1
+ expected_result[109] = 1
+ expected_result[119] = 1
+ expected_result[129] = 1
+
+ src = gr.vector_source_b(data, False)
+ regen = gr.regenerate_bb(10, 3)
+ dst = gr.vector_sink_b()
+
+ tb.connect (src, regen)
+ tb.connect (regen, dst)
+ tb.run ()
+
+ dst_data = dst.data ()
+
+ self.assertEqual (tuple(expected_result), dst_data)
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_regenerate, "test_regenerate.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_repeat.py b/gnuradio-core/src/python/gnuradio/gr/qa_repeat.py
new file mode 100755
index 000000000..116f37115
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_repeat.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+#
+# Copyright 2008,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import math
+
+class test_repeat (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_001_float(self):
+ src_data = [n*1.0 for n in range(100)];
+ dst_data = []
+ for n in range(100):
+ dst_data += [1.0*n, 1.0*n, 1.0*n]
+
+ src = gr.vector_source_f(src_data)
+ rpt = gr.repeat(gr.sizeof_float, 3)
+ dst = gr.vector_sink_f()
+ self.tb.connect(src, rpt, dst)
+ self.tb.run()
+ self.assertFloatTuplesAlmostEqual(dst_data, dst.data(), 6)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_repeat, "test_repeat.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_scrambler.py b/gnuradio-core/src/python/gnuradio/gr/qa_scrambler.py
new file mode 100755
index 000000000..5fe89bdc7
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_scrambler.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+#
+# Copyright 2008,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_scrambler(gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block()
+
+ def tearDown(self):
+ self.tb = None
+
+ def test_scrambler_descrambler(self):
+ src_data = (1,)*1000
+ src = gr.vector_source_b(src_data, False)
+ scrambler = gr.scrambler_bb(0x8a, 0x7F, 7) # CCSDS 7-bit scrambler
+ descrambler = gr.descrambler_bb(0x8a, 0x7F, 7)
+ dst = gr.vector_sink_b()
+ self.tb.connect(src, scrambler, descrambler, dst)
+ self.tb.run()
+ self.assertEqual(tuple(src_data[:-8]), dst.data()[8:]) # skip garbage during synchronization
+
+ def test_additive_scrambler(self):
+ src_data = (1,)*1000
+ src = gr.vector_source_b(src_data, False)
+ scrambler = gr.additive_scrambler_bb(0x8a, 0x7f, 7)
+ descrambler = gr.additive_scrambler_bb(0x8a, 0x7f, 7)
+ dst = gr.vector_sink_b()
+ self.tb.connect(src, scrambler, descrambler, dst)
+ self.tb.run()
+ self.assertEqual(src_data, dst.data())
+
+ def test_additive_scrambler_reset(self):
+ src_data = (1,)*1000
+ src = gr.vector_source_b(src_data, False)
+ scrambler = gr.additive_scrambler_bb(0x8a, 0x7f, 7, 100)
+ descrambler = gr.additive_scrambler_bb(0x8a, 0x7f, 7, 100)
+ dst = gr.vector_sink_b()
+ self.tb.connect(src, scrambler, descrambler, dst)
+ self.tb.run()
+ self.assertEqual(src_data, dst.data())
+
+if __name__ == '__main__':
+ gr_unittest.run(test_scrambler, "test_scrambler.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_short_to_char.py b/gnuradio-core/src/python/gnuradio/gr/qa_short_to_char.py
new file mode 100755
index 000000000..490b149c7
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_short_to_char.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+#
+# Copyright 2011,2012 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import ctypes
+
+class test_short_to_char (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_001(self):
+
+ src_data = range(0, 32767, 32767/127)
+ src_data = [int(s) for s in src_data]
+ expected_result = range(0, 128)
+ src = gr.vector_source_s(src_data)
+ op = gr.short_to_char()
+ dst = gr.vector_sink_b()
+
+ self.tb.connect(src, op, dst)
+ self.tb.run()
+ result_data = list(dst.data())
+
+ self.assertEqual(expected_result, result_data)
+
+ def test_002(self):
+
+ vlen = 3
+ src_data = range(0, 32400, 32767/127)
+ src_data = [int(s) for s in src_data]
+ expected_result = range(0, 126)
+ src = gr.vector_source_s(src_data)
+ s2v = gr.stream_to_vector(gr.sizeof_short, vlen)
+ op = gr.short_to_char(vlen)
+ v2s = gr.vector_to_stream(gr.sizeof_char, vlen)
+ dst = gr.vector_sink_b()
+
+ self.tb.connect(src, s2v, op, v2s, dst)
+ self.tb.run()
+ result_data = list(dst.data())
+
+ self.assertEqual(expected_result, result_data)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_short_to_char, "test_short_to_char.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_short_to_float.py b/gnuradio-core/src/python/gnuradio/gr/qa_short_to_float.py
new file mode 100755
index 000000000..130f034ec
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_short_to_float.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+#
+# Copyright 2011,2012 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import ctypes
+
+class test_short_to_float (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_001(self):
+
+ src_data = (0, 1, 2, 3, 4, 5, -1, -2, -3, -4, -5)
+ expected_result = [ 0.0, 1.0, 2.0, 3.0, 4.0, 5.0,
+ -1.0, -2.0, -3.0, -4.0, -5.0]
+
+ src = gr.vector_source_s(src_data)
+ op = gr.short_to_float()
+ dst = gr.vector_sink_f()
+
+ self.tb.connect(src, op, dst)
+ self.tb.run()
+ result_data = list(dst.data())
+
+ self.assertEqual(expected_result, result_data)
+
+ def test_002(self):
+
+ vlen = 3
+ src_data = (0, 1, 2, 3, 4, 5, -1, -2, -3)
+ expected_result = [0.0, 1.0, 2.0, 3.0, 4.0,
+ 5.0, -1.0, -2.0, -3.0]
+ src = gr.vector_source_s(src_data)
+ s2v = gr.stream_to_vector(gr.sizeof_short, vlen)
+ op = gr.short_to_float(vlen)
+ v2s = gr.vector_to_stream(gr.sizeof_float, vlen)
+ dst = gr.vector_sink_f()
+
+ self.tb.connect(src, s2v, op, v2s, dst)
+ self.tb.run()
+ result_data = list(dst.data())
+
+ self.assertEqual(expected_result, result_data)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_short_to_float, "test_short_to_float.xml")
+
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..122b169b7
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_sig_source.py
@@ -0,0 +1,157 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import math
+
+class test_sig_source (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_const_f (self):
+ tb = self.tb
+ 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 ()
+ tb.connect (src1, op)
+ tb.connect (op, dst1)
+ tb.run ()
+ dst_data = dst1.data ()
+ self.assertEqual (expected_result, dst_data)
+
+ def test_const_i (self):
+ tb = self.tb
+ 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 ()
+ tb.connect (src1, op)
+ tb.connect (op, dst1)
+ tb.run ()
+ dst_data = dst1.data ()
+ self.assertEqual (expected_result, dst_data)
+
+ def test_sine_f (self):
+ tb = self.tb
+ 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 ()
+ tb.connect (src1, op)
+ tb.connect (op, dst1)
+ tb.run ()
+ dst_data = dst1.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, dst_data, 5)
+
+ def test_cosine_f (self):
+ tb = self.tb
+ 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 ()
+ tb.connect (src1, op)
+ tb.connect (op, dst1)
+ tb.run ()
+ dst_data = dst1.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, dst_data, 5)
+
+ def test_sqr_c (self):
+ tb = self.tb #arg6 is a bit before -PI/2
+ expected_result = (1j, 1j, 0, 0, 1, 1, 1+0j, 1+1j, 1j)
+ src1 = gr.sig_source_c (8, gr.GR_SQR_WAVE, 1.0, 1.0)
+ op = gr.head (gr.sizeof_gr_complex, 9)
+ dst1 = gr.vector_sink_c ()
+ tb.connect (src1, op)
+ tb.connect (op, dst1)
+ tb.run ()
+ dst_data = dst1.data ()
+ self.assertEqual (expected_result, dst_data)
+
+ def test_tri_c (self):
+ tb = self.tb
+ expected_result = (1+.5j, .75+.75j, .5+1j, .25+.75j, 0+.5j, .25+.25j, .5+0j, .75+.25j, 1+.5j)
+ src1 = gr.sig_source_c (8, gr.GR_TRI_WAVE, 1.0, 1.0)
+ op = gr.head (gr.sizeof_gr_complex, 9)
+ dst1 = gr.vector_sink_c ()
+ tb.connect (src1, op)
+ tb.connect (op, dst1)
+ tb.run ()
+ dst_data = dst1.data ()
+ self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 5)
+
+ def test_saw_c (self):
+ tb = self.tb
+ expected_result = (.5+.25j, .625+.375j, .75+.5j, .875+.625j, 0+.75j, .125+.875j, .25+1j, .375+.125j, .5+.25j)
+ src1 = gr.sig_source_c (8, gr.GR_SAW_WAVE, 1.0, 1.0)
+ op = gr.head (gr.sizeof_gr_complex, 9)
+ dst1 = gr.vector_sink_c ()
+ tb.connect (src1, op)
+ tb.connect (op, dst1)
+ tb.run ()
+ dst_data = dst1.data ()
+ self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 5)
+
+ def test_sqr_f (self):
+ tb = self.tb
+ expected_result = (0, 0, 0, 0, 1, 1, 1, 1, 0)
+ src1 = gr.sig_source_f (8, gr.GR_SQR_WAVE, 1.0, 1.0)
+ op = gr.head (gr.sizeof_float, 9)
+ dst1 = gr.vector_sink_f ()
+ tb.connect (src1, op)
+ tb.connect (op, dst1)
+ tb.run ()
+ dst_data = dst1.data ()
+ self.assertEqual (expected_result, dst_data)
+
+ def test_tri_f (self):
+ tb = self.tb
+ expected_result = (1, .75, .5, .25, 0, .25, .5, .75, 1)
+ src1 = gr.sig_source_f (8, gr.GR_TRI_WAVE, 1.0, 1.0)
+ op = gr.head (gr.sizeof_float, 9)
+ dst1 = gr.vector_sink_f ()
+ tb.connect (src1, op)
+ tb.connect (op, dst1)
+ tb.run ()
+ dst_data = dst1.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, dst_data, 5)
+
+ def test_saw_f (self):
+ tb = self.tb
+ expected_result = (.5, .625, .75, .875, 0, .125, .25, .375, .5)
+ src1 = gr.sig_source_f (8, gr.GR_SAW_WAVE, 1.0, 1.0)
+ op = gr.head (gr.sizeof_float, 9)
+ dst1 = gr.vector_sink_f ()
+ tb.connect (src1, op)
+ tb.connect (op, dst1)
+ tb.run ()
+ dst_data = dst1.data ()
+ self.assertFloatTuplesAlmostEqual (expected_result, dst_data, 5)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_sig_source, "test_sig_source.xml")
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..bfe2d8fc8
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_single_pole_iir.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+#
+# Copyright 2005,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_single_pole_iir(gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = 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.tb.connect (src, op, dst)
+ self.tb.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.tb.connect (src, op, dst)
+ self.tb.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.tb.connect (src, s2p, op, p2s, dst)
+ self.tb.run()
+ result_data = dst.data()
+ self.assertFloatTuplesAlmostEqual (expected_result, result_data, 3)
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_single_pole_iir, "test_single_pole_iir.xml")
+
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..353df1bc0
--- /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,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_single_pole_iir_cc(gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = 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.tb.connect (src, op, dst)
+ self.tb.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.tb.connect (src, op, dst)
+ self.tb.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.tb.connect (src, s2p, op, p2s, dst)
+ self.tb.run()
+ result_data = dst.data()
+ self.assertComplexTuplesAlmostEqual (expected_result, result_data, 3)
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_single_pole_iir_cc, "test_single_pole_iir_cc.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_skiphead.py b/gnuradio-core/src/python/gnuradio/gr/qa_skiphead.py
new file mode 100755
index 000000000..1e730398c
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_skiphead.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python
+#
+# Copyright 2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_skiphead (gr_unittest.TestCase):
+
+ def setUp(self):
+ self.tb = gr.top_block ()
+ self.src_data = [int(x) for x in range(65536)]
+
+ def tearDown(self):
+ self.tb = None
+
+ def test_skip_0(self):
+ skip_cnt = 0
+ expected_result = tuple(self.src_data[skip_cnt:])
+ src1 = gr.vector_source_i (self.src_data)
+ op = gr.skiphead (gr.sizeof_int, skip_cnt)
+ dst1 = gr.vector_sink_i ()
+ self.tb.connect (src1, op, dst1)
+ self.tb.run ()
+ dst_data = dst1.data ()
+ self.assertEqual (expected_result, dst_data)
+
+ def test_skip_1(self):
+ skip_cnt = 1
+ expected_result = tuple(self.src_data[skip_cnt:])
+ src1 = gr.vector_source_i (self.src_data)
+ op = gr.skiphead (gr.sizeof_int, skip_cnt)
+ dst1 = gr.vector_sink_i ()
+ self.tb.connect (src1, op, dst1)
+ self.tb.run ()
+ dst_data = dst1.data ()
+ self.assertEqual (expected_result, dst_data)
+
+ def test_skip_1023(self):
+ skip_cnt = 1023
+ expected_result = tuple(self.src_data[skip_cnt:])
+ src1 = gr.vector_source_i (self.src_data)
+ op = gr.skiphead (gr.sizeof_int, skip_cnt)
+ dst1 = gr.vector_sink_i ()
+ self.tb.connect (src1, op, dst1)
+ self.tb.run ()
+ dst_data = dst1.data ()
+ self.assertEqual (expected_result, dst_data)
+
+ def test_skip_6339(self):
+ skip_cnt = 6339
+ expected_result = tuple(self.src_data[skip_cnt:])
+ src1 = gr.vector_source_i (self.src_data)
+ op = gr.skiphead (gr.sizeof_int, skip_cnt)
+ dst1 = gr.vector_sink_i ()
+ self.tb.connect (src1, op, dst1)
+ self.tb.run ()
+ dst_data = dst1.data ()
+ self.assertEqual (expected_result, dst_data)
+
+ def test_skip_12678(self):
+ skip_cnt = 12678
+ expected_result = tuple(self.src_data[skip_cnt:])
+ src1 = gr.vector_source_i (self.src_data)
+ op = gr.skiphead (gr.sizeof_int, skip_cnt)
+ dst1 = gr.vector_sink_i ()
+ self.tb.connect (src1, op, dst1)
+ self.tb.run ()
+ dst_data = dst1.data ()
+ self.assertEqual (expected_result, dst_data)
+
+ def test_skip_all(self):
+ skip_cnt = len(self.src_data)
+ expected_result = tuple(self.src_data[skip_cnt:])
+ src1 = gr.vector_source_i (self.src_data)
+ op = gr.skiphead (gr.sizeof_int, skip_cnt)
+ dst1 = gr.vector_sink_i ()
+ self.tb.connect (src1, op, dst1)
+ self.tb.run ()
+ dst_data = dst1.data ()
+ self.assertEqual (expected_result, dst_data)
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_skiphead, "test_skiphead.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_stream_mux.py b/gnuradio-core/src/python/gnuradio/gr/qa_stream_mux.py
new file mode 100755
index 000000000..779d0b25e
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_stream_mux.py
@@ -0,0 +1,168 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2005,2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_stream_mux (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def help_stream_2ff(self, N, stream_sizes):
+ v0 = gr.vector_source_f(N*[1,], False)
+ v1 = gr.vector_source_f(N*[2,], False)
+
+ mux = gr.stream_mux(gr.sizeof_float, stream_sizes)
+
+ dst = gr.vector_sink_f ()
+
+ self.tb.connect (v0, (mux,0))
+ self.tb.connect (v1, (mux,1))
+ self.tb.connect (mux, dst)
+ self.tb.run ()
+
+ return dst.data ()
+
+ def help_stream_ramp_2ff(self, N, stream_sizes):
+ r1 = range(N)
+ r2 = range(N)
+ r2.reverse()
+
+ v0 = gr.vector_source_f(r1, False)
+ v1 = gr.vector_source_f(r2, False)
+
+ mux = gr.stream_mux(gr.sizeof_float, stream_sizes)
+
+ dst = gr.vector_sink_f ()
+
+ self.tb.connect (v0, (mux,0))
+ self.tb.connect (v1, (mux,1))
+ self.tb.connect (mux, dst)
+ self.tb.run ()
+
+ return dst.data ()
+
+ def test_stream_2NN_ff(self):
+ N = 40
+ stream_sizes = [10, 10]
+ result_data = self.help_stream_2ff(N, stream_sizes)
+
+ exp_data = (1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
+ 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
+ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
+ 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
+ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
+ 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
+ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
+ 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0)
+ self.assertEqual (exp_data, result_data)
+
+ def test_stream_ramp_2NN_ff(self):
+ N = 40
+ stream_sizes = [10, 10]
+ result_data = self.help_stream_ramp_2ff(N, stream_sizes)
+
+ exp_data = ( 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0,
+ 39.0, 38.0, 37.0, 36.0, 35.0, 34.0, 33.0, 32.0, 31.0, 30.0,
+ 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0,
+ 29.0, 28.0, 27.0, 26.0, 25.0, 24.0, 23.0, 22.0, 21.0, 20.0,
+ 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0,
+ 19.0, 18.0, 17.0, 16.0, 15.0, 14.0, 13.0, 12.0, 11.0, 10.0,
+ 30.0, 31.0, 32.0, 33.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0,
+ 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0)
+ self.assertEqual (exp_data, result_data)
+
+ def test_stream_2NM_ff(self):
+ N = 40
+ stream_sizes = [7, 9]
+ self.help_stream_2ff(N, stream_sizes)
+
+ result_data = self.help_stream_2ff(N, stream_sizes)
+
+ exp_data = (1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
+ 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
+ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
+ 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
+ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
+ 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
+ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
+ 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
+ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
+ 2.0, 2.0, 2.0, 2.0)
+
+ self.assertEqual (exp_data, result_data)
+
+
+ def test_stream_2MN_ff(self):
+ N = 37
+ stream_sizes = [7, 9]
+ self.help_stream_2ff(N, stream_sizes)
+
+ result_data = self.help_stream_2ff(N, stream_sizes)
+
+ exp_data = (1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
+ 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
+ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
+ 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
+ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
+ 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
+ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
+ 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
+ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
+ 2.0)
+
+ self.assertEqual (exp_data, result_data)
+
+ def test_stream_2N0_ff(self):
+ N = 30
+ stream_sizes = [7, 0]
+ self.help_stream_2ff(N, stream_sizes)
+
+ result_data = self.help_stream_2ff(N, stream_sizes)
+
+ exp_data = (1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
+ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
+ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
+ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
+ 1.0, 1.0)
+
+ self.assertEqual (exp_data, result_data)
+
+ def test_stream_20N_ff(self):
+ N = 30
+ stream_sizes = [0, 9]
+ self.help_stream_2ff(N, stream_sizes)
+
+ result_data = self.help_stream_2ff(N, stream_sizes)
+
+ exp_data = (2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
+ 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
+ 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
+ 2.0, 2.0, 2.0)
+
+ self.assertEqual (exp_data, result_data)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_stream_mux, "test_stream_mux.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_tag_debug.py b/gnuradio-core/src/python/gnuradio/gr/qa_tag_debug.py
new file mode 100755
index 000000000..81babca04
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_tag_debug.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+#
+# Copyright 2012 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_tag_debug(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.tb = gr.top_block()
+
+ def tearDown(self):
+ self.tb = None
+
+ def test_001(self):
+ # Just run some data through and make sure it doesn't puke.
+ src_data = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
+ src = gr.vector_source_i(src_data)
+ op = gr.tag_debug(gr.sizeof_int, "tag QA")
+ self.tb.connect(src, op)
+ self.tb.run()
+ x = op.current_tags()
+
+if __name__ == '__main__':
+ gr_unittest.run(test_tag_debug, "test_tag_debug.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_tag_utils.py b/gnuradio-core/src/python/gnuradio/gr/qa_tag_utils.py
new file mode 100755
index 000000000..ca1184979
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_tag_utils.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+#
+# Copyright 2007,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import tag_utils
+
+try:
+ import pmt_swig as pmt
+except ImportError:
+ import pmt
+
+class test_tag_utils (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_001(self):
+ t = gr.gr_tag_t()
+ t.offset = 10
+ t.key = pmt.pmt_string_to_symbol('key')
+ t.value = pmt.pmt_from_long(23)
+ t.srcid = pmt.pmt_from_bool(False)
+ pt = tag_utils.tag_to_python(t)
+ self.assertEqual(pt.key, 'key')
+ self.assertEqual(pt.value, 23)
+ self.assertEqual(pt.offset, 10)
+
+
+if __name__ == '__main__':
+ print 'hi'
+ gr_unittest.run(test_tag_utils, "test_tag_utils.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_udp_sink_source.py b/gnuradio-core/src/python/gnuradio/gr/qa_udp_sink_source.py
new file mode 100755
index 000000000..0a719990e
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_udp_sink_source.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+#
+# Copyright 2008,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+from threading import Timer
+
+class test_udp_sink_source(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.tb_snd = gr.top_block()
+ self.tb_rcv = gr.top_block()
+
+ def tearDown(self):
+ self.tb_rcv = None
+ self.tb_snd = None
+
+ def test_001(self):
+ port = 65500
+
+ n_data = 16
+ src_data = [float(x) for x in range(n_data)]
+ expected_result = tuple(src_data)
+ src = gr.vector_source_f(src_data)
+ udp_snd = gr.udp_sink( gr.sizeof_float, 'localhost', port )
+ self.tb_snd.connect( src, udp_snd )
+
+ udp_rcv = gr.udp_source( gr.sizeof_float, 'localhost', port )
+ dst = gr.vector_sink_f()
+ self.tb_rcv.connect( udp_rcv, dst )
+
+ self.tb_rcv.start()
+ self.tb_snd.run()
+ udp_snd.disconnect()
+ self.timeout = False
+ q = Timer(3.0,self.stop_rcv)
+ q.start()
+ self.tb_rcv.wait()
+ q.cancel()
+
+ result_data = dst.data()
+ self.assertEqual(expected_result, result_data)
+ self.assert_(not self.timeout)
+
+ def test_002(self):
+ udp_rcv = gr.udp_source( gr.sizeof_float, '0.0.0.0', 0, eof=False )
+ rcv_port = udp_rcv.get_port()
+
+ udp_snd = gr.udp_sink( gr.sizeof_float, '127.0.0.1', 65500 )
+ udp_snd.connect( 'localhost', rcv_port )
+
+ n_data = 16
+ src_data = [float(x) for x in range(n_data)]
+ expected_result = tuple(src_data)
+ src = gr.vector_source_f(src_data)
+ dst = gr.vector_sink_f()
+
+ self.tb_snd.connect( src, udp_snd )
+ self.tb_rcv.connect( udp_rcv, dst )
+
+ self.tb_rcv.start()
+ self.tb_snd.run()
+ udp_snd.disconnect()
+ self.timeout = False
+ q = Timer(3.0,self.stop_rcv)
+ q.start()
+ self.tb_rcv.wait()
+ q.cancel()
+
+ result_data = dst.data()
+ self.assertEqual(expected_result, result_data)
+ self.assert_(self.timeout) # source ignores EOF?
+
+ def stop_rcv(self):
+ self.timeout = True
+ self.tb_rcv.stop()
+ #print "tb_rcv stopped by Timer"
+
+if __name__ == '__main__':
+ gr_unittest.run(test_udp_sink_source, "test_udp_sink_source.xml")
+
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..bb4e7733d
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_unpack_k_bits.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+#
+# Copyright 2006,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import random
+
+class test_unpack(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.tb = gr.top_block ()
+
+ def tearDown(self):
+ self.tb = 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.tb.connect(src, op, dst)
+ self.tb.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.tb.connect(src, op, dst)
+ self.tb.run()
+ self.assertEqual(expected_results, dst.data())
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_unpack, "test_unpack.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_vector_insert.py b/gnuradio-core/src/python/gnuradio/gr/qa_vector_insert.py
new file mode 100755
index 000000000..acc06dfde
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_vector_insert.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+#
+# Copyright 2012 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import math
+
+class test_vector_insert(gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_001(self):
+ src_data = [float(x) for x in range(16)]
+ expected_result = tuple(src_data)
+
+ period = 9177;
+ offset = 0;
+
+ src = gr.null_source(1)
+ head = gr.head(1, 10000000);
+ ins = gr.vector_insert_b([1], period, offset);
+ dst = gr.vector_sink_b()
+
+ self.tb.connect(src, head, ins, dst)
+ self.tb.run()
+ result_data = dst.data()
+
+ for i in range(10000):
+ if(i%period == offset):
+ self.assertEqual(1, result_data[i])
+ else:
+ self.assertEqual(0, result_data[i])
+
+if __name__ == '__main__':
+ gr_unittest.run(test_vector_insert, "test_vector_insert.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_vector_map.py b/gnuradio-core/src/python/gnuradio/gr/qa_vector_map.py
new file mode 100644
index 000000000..12f4be589
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_vector_map.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+#
+# Copyright 2012 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import math
+
+class test_vector_map(gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_reversing(self):
+ # Chunk data in blocks of N and reverse the block contents.
+ N = 5
+ src_data = range(0, 20)
+ expected_result = []
+ for i in range(N-1, len(src_data), N):
+ for j in range(0, N):
+ expected_result.append(1.0*(i-j))
+ mapping = [list(reversed([(0, i) for i in range(0, N)]))]
+ src = gr.vector_source_f(src_data, False, N)
+ vmap = gr.vector_map(gr.sizeof_float, (N, ), mapping)
+ dst = gr.vector_sink_f(N)
+ self.tb.connect(src, vmap, dst)
+ self.tb.run()
+ result_data = list(dst.data())
+ self.assertEqual(expected_result, result_data)
+
+ def test_vector_to_streams(self):
+ # Split an input vector into N streams.
+ N = 5
+ M = 20
+ src_data = range(0, M)
+ expected_results = []
+ for n in range(0, N):
+ expected_results.append(range(n, M, N))
+ mapping = [[(0, n)] for n in range(0, N)]
+ src = gr.vector_source_f(src_data, False, N)
+ vmap = gr.vector_map(gr.sizeof_float, (N, ), mapping)
+ dsts = [gr.vector_sink_f(1) for n in range(0, N)]
+ self.tb.connect(src, vmap)
+ for n in range(0, N):
+ self.tb.connect((vmap, n), dsts[n])
+ self.tb.run()
+ for n in range(0, N):
+ result_data = list(dsts[n].data())
+ self.assertEqual(expected_results[n], result_data)
+
+ def test_interleaving(self):
+ # Takes 3 streams (a, b and c)
+ # Outputs 2 streams.
+ # First (d) is interleaving of a and b.
+ # Second (e) is interleaving of a and b and c. c is taken in
+ # chunks of 2 which are reversed.
+ A = (1, 2, 3, 4, 5)
+ B = (11, 12, 13, 14, 15)
+ C = (99, 98, 97, 96, 95, 94, 93, 92, 91, 90)
+ expected_D = (1, 11, 2, 12, 3, 13, 4, 14, 5, 15)
+ expected_E = (1, 11, 98, 99, 2, 12, 96, 97, 3, 13, 94, 95,
+ 4, 14, 92, 93, 5, 15, 90, 91)
+ mapping = [[(0, 0), (1, 0)], # mapping to produce D
+ [(0, 0), (1, 0), (2, 1), (2, 0)], # mapping to produce E
+ ]
+ srcA = gr.vector_source_f(A, False, 1)
+ srcB = gr.vector_source_f(B, False, 1)
+ srcC = gr.vector_source_f(C, False, 2)
+ vmap = gr.vector_map(gr.sizeof_int, (1, 1, 2), mapping)
+ dstD = gr.vector_sink_f(2)
+ dstE = gr.vector_sink_f(4)
+ self.tb.connect(srcA, (vmap, 0))
+ self.tb.connect(srcB, (vmap, 1))
+ self.tb.connect(srcC, (vmap, 2))
+ self.tb.connect((vmap, 0), dstD)
+ self.tb.connect((vmap, 1), dstE)
+ self.tb.run()
+ self.assertEqual(expected_D, dstD.data())
+ self.assertEqual(expected_E, dstE.data())
+
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_vector_map, "test_vector_map.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_vector_sink_source.py b/gnuradio-core/src/python/gnuradio/gr/qa_vector_sink_source.py
new file mode 100755
index 000000000..64cbbe72a
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_vector_sink_source.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+#
+# Copyright 2008,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import math
+
+class test_vector_sink_source(gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_001(self):
+ src_data = [float(x) for x in range(16)]
+ expected_result = tuple(src_data)
+
+ src = gr.vector_source_f(src_data)
+ dst = gr.vector_sink_f()
+
+ self.tb.connect(src, dst)
+ self.tb.run()
+ result_data = dst.data()
+ self.assertEqual(expected_result, result_data)
+
+ def test_002(self):
+ src_data = [float(x) for x in range(16)]
+ expected_result = tuple(src_data)
+
+ src = gr.vector_source_f(src_data, False, 2)
+ dst = gr.vector_sink_f(2)
+
+ self.tb.connect(src, dst)
+ self.tb.run()
+ result_data = dst.data()
+ self.assertEqual(expected_result, result_data)
+
+ def test_003(self):
+ src_data = [float(x) for x in range(16)]
+ expected_result = tuple(src_data)
+ self.assertRaises(ValueError, lambda : gr.vector_source_f(src_data, False, 3))
+
+if __name__ == '__main__':
+ gr_unittest.run(test_vector_sink_source, "test_vector_sink_source.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_wavefile.py b/gnuradio-core/src/python/gnuradio/gr/qa_wavefile.py
new file mode 100755
index 000000000..3b9a3eb20
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_wavefile.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+#
+# Copyright 2008,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+import os
+from os.path import getsize
+
+g_in_file = os.path.join (os.getenv ("srcdir"), "test_16bit_1chunk.wav")
+
+class test_wavefile(gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_001_checkwavread (self):
+ wf = gr.wavfile_source(g_in_file)
+ self.assertEqual(wf.sample_rate(), 8000)
+
+ def test_002_checkwavcopy (self):
+ infile = g_in_file
+ outfile = "test_out.wav"
+
+ wf_in = gr.wavfile_source(infile)
+ wf_out = gr.wavfile_sink(outfile,
+ wf_in.channels(),
+ wf_in.sample_rate(),
+ wf_in.bits_per_sample())
+ self.tb.connect(wf_in, wf_out)
+ self.tb.run()
+ wf_out.close()
+
+ self.assertEqual(getsize(infile), getsize(outfile))
+
+ in_f = file(infile, 'rb')
+ out_f = file(outfile, 'rb')
+
+ in_data = in_f.read()
+ out_data = out_f.read()
+ out_f.close()
+ os.remove(outfile)
+
+ self.assertEqual(in_data, out_data)
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_wavefile, "test_wavefile.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/tag_utils.py b/gnuradio-core/src/python/gnuradio/gr/tag_utils.py
new file mode 100644
index 000000000..923718fc9
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/tag_utils.py
@@ -0,0 +1,54 @@
+#
+# Copyright 2003-2012 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+""" Conversion tools between stream tags and Python objects """
+
+try: import pmt
+except: from gruel import pmt
+
+from gnuradio_core import gr_tag_t
+
+class PythonTag(object):
+ " Python container for tags "
+ def __init__(self):
+ self.offset = None
+ self.key = None
+ self.value = None
+ self.srcid = None
+
+def tag_to_python(tag):
+ """ Convert a stream tag to a Python-readable object """
+ newtag = PythonTag()
+ newtag.offset = tag.offset
+ newtag.key = pmt.to_python(tag.key)
+ newtag.value = pmt.to_python(tag.value)
+ newtag.srcid = pmt.to_python(tag.srcid)
+ return newtag
+
+def tag_to_pmt(tag):
+ """ Convert a Python-readable object to a stream tag """
+ newtag = gr_tag_t()
+ newtag.offset = tag.offset
+ newtag.key = pmt.to_python(tag.key)
+ newtag.value = pmt.from_python(tag.value)
+ newtag.srcid = pmt.from_python(tag.srcid)
+ return newtag
+
+
diff --git a/gnuradio-core/src/python/gnuradio/gr/test_16bit_1chunk.wav b/gnuradio-core/src/python/gnuradio/gr/test_16bit_1chunk.wav
new file mode 100644
index 000000000..0fe12a7a1
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/test_16bit_1chunk.wav
Binary files differ
diff --git a/gnuradio-core/src/python/gnuradio/gr/top_block.py b/gnuradio-core/src/python/gnuradio/gr/top_block.py
new file mode 100644
index 000000000..dc1f443aa
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/top_block.py
@@ -0,0 +1,165 @@
+#
+# Copyright 2007 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio_core import top_block_swig, \
+ top_block_wait_unlocked, top_block_run_unlocked
+
+#import gnuradio.gr.gr_threading as _threading
+import gr_threading as _threading
+
+
+#
+# There is no problem that can't be solved with an additional
+# level of indirection...
+#
+# This kludge allows ^C to interrupt top_block.run and top_block.wait
+#
+# The problem that we are working around is that Python only services
+# signals (e.g., KeyboardInterrupt) in its main thread. If the main
+# thread is blocked in our C++ version of wait, even though Python's
+# SIGINT handler fires, and even though there may be other python
+# threads running, no one will know. Thus instead of directly waiting
+# in the thread that calls wait (which is likely to be the Python main
+# thread), we create a separate thread that does the blocking wait,
+# and then use the thread that called wait to do a slow poll of an
+# event queue. That thread, which is executing "wait" below is
+# interruptable, and if it sees a KeyboardInterrupt, executes a stop
+# on the top_block, then goes back to waiting for it to complete.
+# This ensures that the unlocked wait that was in progress (in the
+# _top_block_waiter thread) can complete, release its mutex and back
+# out. If we don't do that, we are never able to clean up, and nasty
+# things occur like leaving the USRP transmitter sending a carrier.
+#
+# See also top_block.wait (below), which uses this class to implement
+# the interruptable wait.
+#
+class _top_block_waiter(_threading.Thread):
+ def __init__(self, tb):
+ _threading.Thread.__init__(self)
+ self.setDaemon(1)
+ self.tb = tb
+ self.event = _threading.Event()
+ self.start()
+
+ def run(self):
+ top_block_wait_unlocked(self.tb)
+ self.event.set()
+
+ def wait(self):
+ try:
+ while not self.event.isSet():
+ self.event.wait(0.100)
+ except KeyboardInterrupt:
+ self.tb.stop()
+ self.wait()
+
+
+#
+# This hack forces a 'has-a' relationship to look like an 'is-a' one.
+#
+# It allows Python classes to subclass this one, while passing through
+# method calls to the C++ class shared pointer from SWIG.
+#
+# It also allows us to intercept method calls if needed.
+#
+# This allows the 'run_locked' methods, which are defined in gr_top_block.i,
+# to release the Python global interpreter lock before calling the actual
+# method in gr_top_block
+#
+class top_block(object):
+ def __init__(self, name="top_block"):
+ self._tb = top_block_swig(name)
+
+ def __getattr__(self, name):
+ if not hasattr(self, "_tb"):
+ raise RuntimeError("top_block: invalid state--did you forget to call gr.top_block.__init__ in a derived class?")
+ return getattr(self._tb, name)
+
+ def start(self, max_noutput_items=100000):
+ self._tb.start(max_noutput_items)
+
+ def stop(self):
+ self._tb.stop()
+
+ def run(self, max_noutput_items=100000):
+ self.start(max_noutput_items)
+ self.wait()
+
+ def wait(self):
+ _top_block_waiter(self._tb).wait()
+
+
+ # FIXME: these are duplicated from hier_block2.py; they should really be implemented
+ # in the original C++ class (gr_hier_block2), then they would all be inherited here
+
+ def connect(self, *points):
+ '''connect requires one or more arguments that can be coerced to endpoints.
+ If more than two arguments are provided, they are connected together successively.
+ '''
+ if len (points) < 1:
+ raise ValueError, ("connect requires at least one endpoint; %d provided." % (len (points),))
+ else:
+ if len(points) == 1:
+ self._tb.primitive_connect(points[0].to_basic_block())
+ else:
+ for i in range (1, len (points)):
+ self._connect(points[i-1], points[i])
+
+ def msg_connect(self, src, srcport, dst, dstport):
+ self.primitive_msg_connect(src.to_basic_block(), srcport, dst.to_basic_block(), dstport);
+
+ def msg_disconnect(self, src, srcport, dst, dstport):
+ self.primitive_msg_disconnect(src.to_basic_block(), srcport, dst.to_basic_block(), dstport);
+
+ def _connect(self, src, dst):
+ (src_block, src_port) = self._coerce_endpoint(src)
+ (dst_block, dst_port) = self._coerce_endpoint(dst)
+ self._tb.primitive_connect(src_block.to_basic_block(), src_port,
+ dst_block.to_basic_block(), dst_port)
+
+ def _coerce_endpoint(self, endp):
+ if hasattr(endp, 'to_basic_block'):
+ return (endp, 0)
+ else:
+ if hasattr(endp, "__getitem__") and len(endp) == 2:
+ return endp # Assume user put (block, port)
+ else:
+ raise ValueError("unable to coerce endpoint")
+
+ def disconnect(self, *points):
+ '''disconnect requires one or more arguments that can be coerced to endpoints.
+ If more than two arguments are provided, they are disconnected successively.
+ '''
+ if len (points) < 1:
+ raise ValueError, ("disconnect requires at least one endpoint; %d provided." % (len (points),))
+ else:
+ if len(points) == 1:
+ self._tb.primitive_disconnect(points[0].to_basic_block())
+ else:
+ for i in range (1, len (points)):
+ self._disconnect(points[i-1], points[i])
+
+ def _disconnect(self, src, dst):
+ (src_block, src_port) = self._coerce_endpoint(src)
+ (dst_block, dst_port) = self._coerce_endpoint(dst)
+ self._tb.primitive_disconnect(src_block.to_basic_block(), src_port,
+ dst_block.to_basic_block(), dst_port)
+
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..e4510a6eb
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr_unittest.py
@@ -0,0 +1,167 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2010 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import unittest
+import gr_xmlrunner
+import sys, os, stat
+
+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
+
+def run(PUT, filename=None):
+ '''
+ Runs the unittest on a TestCase and produces an optional XML report
+ PUT: the program under test and should be a gr_unittest.TestCase
+ filename: an optional filename to save the XML report of the tests
+ this will live in ./.unittests/python
+ '''
+
+ # Run this is given a file name
+ if(filename is not None):
+ basepath = "./.unittests"
+ path = basepath + "/python"
+
+ if not os.path.exists(basepath):
+ os.makedirs(basepath, 0750)
+
+ xmlrunner = None
+ # only proceed if .unittests is writable
+ st = os.stat(basepath)[stat.ST_MODE]
+ if(st & stat.S_IWUSR > 0):
+ # Test if path exists; if not, build it
+ if not os.path.exists(path):
+ os.makedirs(path, 0750)
+
+ # Just for safety: make sure we can write here, too
+ st = os.stat(path)[stat.ST_MODE]
+ if(st & stat.S_IWUSR > 0):
+ # Create an XML runner to filename
+ fout = file(path+"/"+filename, "w")
+ xmlrunner = gr_xmlrunner.XMLTestRunner(fout)
+
+ txtrunner = TextTestRunner(verbosity=1)
+
+ # Run the test; runner also creates XML output file
+ # FIXME: make xmlrunner output to screen so we don't have to do run and main
+ suite = TestLoader().loadTestsFromTestCase(PUT)
+
+ # use the xmlrunner if we can write the the directory
+ if(xmlrunner is not None):
+ xmlrunner.run(suite)
+
+ main()
+
+ # This will run and fail make check if problem
+ # but does not output to screen.
+ #main(testRunner = xmlrunner)
+
+ else:
+ # If no filename is given, just run the test
+ main()
+
+
+##############################################################################
+# Executing this module from the command line
+##############################################################################
+
+if __name__ == "__main__":
+ main(module=None)
diff --git a/gnuradio-core/src/python/gnuradio/gr_xmlrunner.py b/gnuradio-core/src/python/gnuradio/gr_xmlrunner.py
new file mode 100644
index 000000000..31298197f
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr_xmlrunner.py
@@ -0,0 +1,387 @@
+"""
+XML Test Runner for PyUnit
+"""
+
+# Written by Sebastian Rittau <srittau@jroger.in-berlin.de> and placed in
+# the Public Domain. With contributions by Paolo Borelli and others.
+# Added to GNU Radio Oct. 3, 2010
+
+__version__ = "0.1"
+
+import os.path
+import re
+import sys
+import time
+import traceback
+import unittest
+from xml.sax.saxutils import escape
+
+try:
+ from StringIO import StringIO
+except ImportError:
+ from io import StringIO
+
+
+class _TestInfo(object):
+
+ """Information about a particular test.
+
+ Used by _XMLTestResult.
+
+ """
+
+ def __init__(self, test, time):
+ (self._class, self._method) = test.id().rsplit(".", 1)
+ self._time = time
+ self._error = None
+ self._failure = None
+
+ @staticmethod
+ def create_success(test, time):
+ """Create a _TestInfo instance for a successful test."""
+ return _TestInfo(test, time)
+
+ @staticmethod
+ def create_failure(test, time, failure):
+ """Create a _TestInfo instance for a failed test."""
+ info = _TestInfo(test, time)
+ info._failure = failure
+ return info
+
+ @staticmethod
+ def create_error(test, time, error):
+ """Create a _TestInfo instance for an erroneous test."""
+ info = _TestInfo(test, time)
+ info._error = error
+ return info
+
+ def print_report(self, stream):
+ """Print information about this test case in XML format to the
+ supplied stream.
+
+ """
+ stream.write(' <testcase classname="%(class)s" name="%(method)s" time="%(time).4f">' % \
+ {
+ "class": self._class,
+ "method": self._method,
+ "time": self._time,
+ })
+ if self._failure is not None:
+ self._print_error(stream, 'failure', self._failure)
+ if self._error is not None:
+ self._print_error(stream, 'error', self._error)
+ stream.write('</testcase>\n')
+
+ def _print_error(self, stream, tagname, error):
+ """Print information from a failure or error to the supplied stream."""
+ text = escape(str(error[1]))
+ stream.write('\n')
+ stream.write(' <%s type="%s">%s\n' \
+ % (tagname, _clsname(error[0]), text))
+ tb_stream = StringIO()
+ traceback.print_tb(error[2], None, tb_stream)
+ stream.write(escape(tb_stream.getvalue()))
+ stream.write(' </%s>\n' % tagname)
+ stream.write(' ')
+
+
+def _clsname(cls):
+ return cls.__module__ + "." + cls.__name__
+
+
+class _XMLTestResult(unittest.TestResult):
+
+ """A test result class that stores result as XML.
+
+ Used by XMLTestRunner.
+
+ """
+
+ def __init__(self, classname):
+ unittest.TestResult.__init__(self)
+ self._test_name = classname
+ self._start_time = None
+ self._tests = []
+ self._error = None
+ self._failure = None
+
+ def startTest(self, test):
+ unittest.TestResult.startTest(self, test)
+ self._error = None
+ self._failure = None
+ self._start_time = time.time()
+
+ def stopTest(self, test):
+ time_taken = time.time() - self._start_time
+ unittest.TestResult.stopTest(self, test)
+ if self._error:
+ info = _TestInfo.create_error(test, time_taken, self._error)
+ elif self._failure:
+ info = _TestInfo.create_failure(test, time_taken, self._failure)
+ else:
+ info = _TestInfo.create_success(test, time_taken)
+ self._tests.append(info)
+
+ def addError(self, test, err):
+ unittest.TestResult.addError(self, test, err)
+ self._error = err
+
+ def addFailure(self, test, err):
+ unittest.TestResult.addFailure(self, test, err)
+ self._failure = err
+
+ def print_report(self, stream, time_taken, out, err):
+ """Prints the XML report to the supplied stream.
+
+ The time the tests took to perform as well as the captured standard
+ output and standard error streams must be passed in.a
+
+ """
+ stream.write('<testsuite errors="%(e)d" failures="%(f)d" ' % \
+ { "e": len(self.errors), "f": len(self.failures) })
+ stream.write('name="%(n)s" tests="%(t)d" time="%(time).3f">\n' % \
+ {
+ "n": self._test_name,
+ "t": self.testsRun,
+ "time": time_taken,
+ })
+ for info in self._tests:
+ info.print_report(stream)
+ stream.write(' <system-out><![CDATA[%s]]></system-out>\n' % out)
+ stream.write(' <system-err><![CDATA[%s]]></system-err>\n' % err)
+ stream.write('</testsuite>\n')
+
+
+class XMLTestRunner(object):
+
+ """A test runner that stores results in XML format compatible with JUnit.
+
+ XMLTestRunner(stream=None) -> XML test runner
+
+ The XML file is written to the supplied stream. If stream is None, the
+ results are stored in a file called TEST-<module>.<class>.xml in the
+ current working directory (if not overridden with the path property),
+ where <module> and <class> are the module and class name of the test class.
+
+ """
+
+ def __init__(self, stream=None):
+ self._stream = stream
+ self._path = "."
+
+ def run(self, test):
+ """Run the given test case or test suite."""
+ class_ = test.__class__
+ classname = class_.__module__ + "." + class_.__name__
+ if self._stream == None:
+ filename = "TEST-%s.xml" % classname
+ stream = file(os.path.join(self._path, filename), "w")
+ stream.write('<?xml version="1.0" encoding="utf-8"?>\n')
+ else:
+ stream = self._stream
+
+ result = _XMLTestResult(classname)
+ start_time = time.time()
+
+ fss = _fake_std_streams()
+ fss.__enter__()
+ try:
+ test(result)
+ try:
+ out_s = sys.stdout.getvalue()
+ except AttributeError:
+ out_s = ""
+ try:
+ err_s = sys.stderr.getvalue()
+ except AttributeError:
+ err_s = ""
+ finally:
+ fss.__exit__(None, None, None)
+
+ time_taken = time.time() - start_time
+ result.print_report(stream, time_taken, out_s, err_s)
+ if self._stream is None:
+ stream.close()
+
+ return result
+
+ def _set_path(self, path):
+ self._path = path
+
+ path = property(lambda self: self._path, _set_path, None,
+ """The path where the XML files are stored.
+
+ This property is ignored when the XML file is written to a file
+ stream.""")
+
+
+class _fake_std_streams(object):
+
+ def __enter__(self):
+ self._orig_stdout = sys.stdout
+ self._orig_stderr = sys.stderr
+ #sys.stdout = StringIO()
+ #sys.stderr = StringIO()
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ sys.stdout = self._orig_stdout
+ sys.stderr = self._orig_stderr
+
+
+class XMLTestRunnerTest(unittest.TestCase):
+
+ def setUp(self):
+ self._stream = StringIO()
+
+ def _try_test_run(self, test_class, expected):
+
+ """Run the test suite against the supplied test class and compare the
+ XML result against the expected XML string. Fail if the expected
+ string doesn't match the actual string. All time attributes in the
+ expected string should have the value "0.000". All error and failure
+ messages are reduced to "Foobar".
+
+ """
+
+ runner = XMLTestRunner(self._stream)
+ runner.run(unittest.makeSuite(test_class))
+
+ got = self._stream.getvalue()
+ # Replace all time="X.YYY" attributes by time="0.000" to enable a
+ # simple string comparison.
+ got = re.sub(r'time="\d+\.\d+"', 'time="0.000"', got)
+ # Likewise, replace all failure and error messages by a simple "Foobar"
+ # string.
+ got = re.sub(r'(?s)<failure (.*?)>.*?</failure>', r'<failure \1>Foobar</failure>', got)
+ got = re.sub(r'(?s)<error (.*?)>.*?</error>', r'<error \1>Foobar</error>', got)
+ # And finally Python 3 compatibility.
+ got = got.replace('type="builtins.', 'type="exceptions.')
+
+ self.assertEqual(expected, got)
+
+ def test_no_tests(self):
+ """Regression test: Check whether a test run without any tests
+ matches a previous run.
+
+ """
+ class TestTest(unittest.TestCase):
+ pass
+ self._try_test_run(TestTest, """<testsuite errors="0" failures="0" name="unittest.TestSuite" tests="0" time="0.000">
+ <system-out><![CDATA[]]></system-out>
+ <system-err><![CDATA[]]></system-err>
+</testsuite>
+""")
+
+ def test_success(self):
+ """Regression test: Check whether a test run with a successful test
+ matches a previous run.
+
+ """
+ class TestTest(unittest.TestCase):
+ def test_foo(self):
+ pass
+ self._try_test_run(TestTest, """<testsuite errors="0" failures="0" name="unittest.TestSuite" tests="1" time="0.000">
+ <testcase classname="__main__.TestTest" name="test_foo" time="0.000"></testcase>
+ <system-out><![CDATA[]]></system-out>
+ <system-err><![CDATA[]]></system-err>
+</testsuite>
+""")
+
+ def test_failure(self):
+ """Regression test: Check whether a test run with a failing test
+ matches a previous run.
+
+ """
+ class TestTest(unittest.TestCase):
+ def test_foo(self):
+ self.assert_(False)
+ self._try_test_run(TestTest, """<testsuite errors="0" failures="1" name="unittest.TestSuite" tests="1" time="0.000">
+ <testcase classname="__main__.TestTest" name="test_foo" time="0.000">
+ <failure type="exceptions.AssertionError">Foobar</failure>
+ </testcase>
+ <system-out><![CDATA[]]></system-out>
+ <system-err><![CDATA[]]></system-err>
+</testsuite>
+""")
+
+ def test_error(self):
+ """Regression test: Check whether a test run with a erroneous test
+ matches a previous run.
+
+ """
+ class TestTest(unittest.TestCase):
+ def test_foo(self):
+ raise IndexError()
+ self._try_test_run(TestTest, """<testsuite errors="1" failures="0" name="unittest.TestSuite" tests="1" time="0.000">
+ <testcase classname="__main__.TestTest" name="test_foo" time="0.000">
+ <error type="exceptions.IndexError">Foobar</error>
+ </testcase>
+ <system-out><![CDATA[]]></system-out>
+ <system-err><![CDATA[]]></system-err>
+</testsuite>
+""")
+
+ def test_stdout_capture(self):
+ """Regression test: Check whether a test run with output to stdout
+ matches a previous run.
+
+ """
+ class TestTest(unittest.TestCase):
+ def test_foo(self):
+ sys.stdout.write("Test\n")
+ self._try_test_run(TestTest, """<testsuite errors="0" failures="0" name="unittest.TestSuite" tests="1" time="0.000">
+ <testcase classname="__main__.TestTest" name="test_foo" time="0.000"></testcase>
+ <system-out><![CDATA[Test
+]]></system-out>
+ <system-err><![CDATA[]]></system-err>
+</testsuite>
+""")
+
+ def test_stderr_capture(self):
+ """Regression test: Check whether a test run with output to stderr
+ matches a previous run.
+
+ """
+ class TestTest(unittest.TestCase):
+ def test_foo(self):
+ sys.stderr.write("Test\n")
+ self._try_test_run(TestTest, """<testsuite errors="0" failures="0" name="unittest.TestSuite" tests="1" time="0.000">
+ <testcase classname="__main__.TestTest" name="test_foo" time="0.000"></testcase>
+ <system-out><![CDATA[]]></system-out>
+ <system-err><![CDATA[Test
+]]></system-err>
+</testsuite>
+""")
+
+ class NullStream(object):
+ """A file-like object that discards everything written to it."""
+ def write(self, buffer):
+ pass
+
+ def test_unittests_changing_stdout(self):
+ """Check whether the XMLTestRunner recovers gracefully from unit tests
+ that change stdout, but don't change it back properly.
+
+ """
+ class TestTest(unittest.TestCase):
+ def test_foo(self):
+ sys.stdout = XMLTestRunnerTest.NullStream()
+
+ runner = XMLTestRunner(self._stream)
+ runner.run(unittest.makeSuite(TestTest))
+
+ def test_unittests_changing_stderr(self):
+ """Check whether the XMLTestRunner recovers gracefully from unit tests
+ that change stderr, but don't change it back properly.
+
+ """
+ class TestTest(unittest.TestCase):
+ def test_foo(self):
+ sys.stderr = XMLTestRunnerTest.NullStream()
+
+ runner = XMLTestRunner(self._stream)
+ runner.run(unittest.makeSuite(TestTest))
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/gnuradio-core/src/python/gnuradio/gru/CMakeLists.txt b/gnuradio-core/src/python/gnuradio/gru/CMakeLists.txt
new file mode 100644
index 000000000..1c50989d9
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gru/CMakeLists.txt
@@ -0,0 +1,26 @@
+# Copyright 2010-2011 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+
+include(GrPython)
+
+GR_PYTHON_INSTALL(
+ FILES __init__.py
+ DESTINATION ${GR_PYTHON_DIR}/gnuradio/gru
+ COMPONENT "core_python"
+)
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..c24439ff5
--- /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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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/CMakeLists.txt b/gnuradio-core/src/python/gnuradio/gruimpl/CMakeLists.txt
new file mode 100644
index 000000000..7d48f3512
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gruimpl/CMakeLists.txt
@@ -0,0 +1,38 @@
+# Copyright 2010-2011 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+
+include(GrPython)
+
+GR_PYTHON_INSTALL(FILES
+ __init__.py
+ freqz.py
+ gnuplot_freqz.py
+ hexint.py
+ listmisc.py
+ mathmisc.py
+ lmx2306.py
+ msgq_runner.py
+ os_read_exactly.py
+ sdr_1000.py
+ seq_with_cursor.py
+ socket_stuff.py
+ daemon.py
+ DESTINATION ${GR_PYTHON_DIR}/gnuradio/gruimpl
+ COMPONENT "core_python"
+)
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/daemon.py b/gnuradio-core/src/python/gnuradio/gruimpl/daemon.py
new file mode 100644
index 000000000..e04702152
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gruimpl/daemon.py
@@ -0,0 +1,102 @@
+#
+# Copyright 2008 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+import os, sys, signal
+
+# Turn application into a background daemon process.
+#
+# When this function returns:
+#
+# 1) The calling process is disconnected from its controlling terminal
+# and will not exit when the controlling session exits
+# 2) If a pidfile name is provided, it is created and the new pid is
+# written into it.
+# 3) If a logfile name is provided, it is opened and stdout/stderr are
+# redirected to it.
+# 4) The process current working directory is changed to '/' to avoid
+# pinning any filesystem mounts.
+# 5) The process umask is set to 0111.
+#
+# The return value is the new pid.
+#
+# To create GNU Radio applications that operate as daemons, add a call to this
+# function after all initialization but just before calling gr.top_block.run()
+# or .start().
+#
+# Daemonized GNU Radio applications may be stopped by sending them a
+# SIGINT, SIGKILL, or SIGTERM, e.g., using 'kill pid' from the command line.
+#
+# If your application uses gr.top_block.run(), the flowgraph will be stopped
+# and the function will return. You should allow your daemon program to exit
+# at this point.
+#
+# If your application uses gr.top_block.start(), you are responsible for hooking
+# the Python signal handler (see 'signal' module) and calling gr.top_block.stop()
+# on your top block, and otherwise causing your daemon process to exit.
+#
+
+def daemonize(pidfile=None, logfile=None):
+ # fork() into background
+ try:
+ pid = os.fork()
+ except OSError, e:
+ raise Exception, "%s [%d]" % (e.strerror, e.errno)
+
+ if pid == 0: # First child of first fork()
+ # Become session leader of new session
+ os.setsid()
+
+ # fork() into background again
+ try:
+ pid = os.fork()
+ except OSError, e:
+ raise Exception, "%s [%d]" % (e.strerror, e.errno)
+
+ if pid != 0:
+ os._exit(0) # Second child of second fork()
+
+ else: # Second child of first fork()
+ os._exit(0)
+
+ os.umask(0111)
+
+ # Write pid
+ pid = os.getpid()
+ if pidfile is not None:
+ open(pidfile, 'w').write('%d\n'%pid)
+
+ # Redirect streams
+ if logfile is not None:
+ lf = open(logfile, 'a+')
+ sys.stdout = lf
+ sys.stderr = lf
+
+ # Prevent pinning any filesystem mounts
+ os.chdir('/')
+
+ # Tell caller what pid to send future signals to
+ return pid
+
+if __name__ == "__main__":
+ import time
+ daemonize()
+ print "Hello, world, from daemon process."
+ time.sleep(20)
+ print "Goodbye, world, from daemon process."
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..60dca64a5
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gruimpl/freqz.py
@@ -0,0 +1,344 @@
+#!/usr/bin/env python
+#
+# Copyright 2005,2007 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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 numpy
+from numpy import *
+Num=numpy
+
+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 = numpy.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 = numpy.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 = numpy.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 = numpy.zeros(key-self.order,self.coeffs.typecode())
+ self.__dict__['coeffs'] = numpy.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..defc47b59
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gruimpl/gnuplot_freqz.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+#
+# Copyright 2005,2007 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+__all__ = ['gnuplot_freqz']
+
+import tempfile
+import os
+import math
+import numpy
+
+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 * numpy.log10 (numpy.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..0fb5ecde0
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gruimpl/hexint.py
@@ -0,0 +1,44 @@
+#
+# 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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
+
+def hexshort(mask):
+ """
+ Convert unsigned masks into signed shorts.
+
+ This allows us to use hex constants like 0x8000 when talking to
+ our hardware and not get screwed by them getting treated as python
+ longs.
+ """
+ if mask >= 2**15:
+ return int(mask-2**16)
+ 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..9e70eb863
--- /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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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..aa4efc3e9
--- /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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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..7e6f23a34
--- /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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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/msgq_runner.py b/gnuradio-core/src/python/gnuradio/gruimpl/msgq_runner.py
new file mode 100644
index 000000000..767a74a71
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gruimpl/msgq_runner.py
@@ -0,0 +1,82 @@
+#
+# Copyright 2009 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+"""
+Convenience class for dequeuing messages from a gr.msg_queue and
+invoking a callback.
+
+Creates a Python thread that does a blocking read on the supplied
+gr.msg_queue, then invokes callback each time a msg is received.
+
+If the msg type is not 0, then it is treated as a signal to exit
+its loop.
+
+If the callback raises an exception, and the runner was created
+with 'exit_on_error' equal to True, then the runner will store the
+exception and exit its loop, otherwise the exception is ignored.
+
+To get the exception that the callback raised, if any, call
+exit_error() on the object.
+
+To manually stop the runner, call stop() on the object.
+
+To determine if the runner has exited, call exited() on the object.
+"""
+
+from gnuradio import gr
+import gnuradio.gr.gr_threading as _threading
+
+class msgq_runner(_threading.Thread):
+
+ def __init__(self, msgq, callback, exit_on_error=False):
+ _threading.Thread.__init__(self)
+
+ self._msgq = msgq
+ self._callback = callback
+ self._exit_on_error = exit_on_error
+ self._done = False
+ self._exited = False
+ self._exit_error = None
+ self.setDaemon(1)
+ self.start()
+
+ def run(self):
+ while not self._done:
+ msg = self._msgq.delete_head()
+ if msg.type() != 0:
+ self.stop()
+ else:
+ try:
+ self._callback(msg)
+ except Exception, e:
+ if self._exit_on_error:
+ self._exit_error = e
+ self.stop()
+ self._exited = True
+
+ def stop(self):
+ self._done = True
+
+ def exited(self):
+ return self._exited
+
+ def exit_error(self):
+ return self._exit_error
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..40b053770
--- /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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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..5192a7155
--- /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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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..def3299b6
--- /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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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..329fd2ed3
--- /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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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..bbf9ead74
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/optfir.py
@@ -0,0 +1,341 @@
+#
+# Copyright 2004,2005,2009 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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, cmath
+from gnuradio import gr
+
+remez = gr.remez
+
+# ----------------------------------------------------------------
+
+## Builds a low pass filter.
+# @param gain Filter gain in the passband (linear)
+# @param Fs Sampling rate (sps)
+# @param freq1 End of pass band (in Hz)
+# @param freq2 Start of stop band (in Hz)
+# @param passband_ripple_db Pass band ripple in dB (should be small, < 1)
+# @param stopband_atten_db Stop band attenuation in dB (should be large, >= 60)
+# @param nextra_taps Extra taps to use in the filter (default=2)
+def low_pass (gain, Fs, freq1, freq2, passband_ripple_db, stopband_atten_db,
+ nextra_taps=2):
+ 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)
+ # The remezord typically under-estimates the filter order, so add 2 taps by default
+ taps = gr.remez (n + nextra_taps, fo, ao, w, "bandpass")
+ return taps
+
+## Builds a band pass filter.
+# @param gain Filter gain in the passband (linear)
+# @param Fs Sampling rate (sps)
+# @param freq_sb1 End of stop band (in Hz)
+# @param freq_pb1 Start of pass band (in Hz)
+# @param freq_pb2 End of pass band (in Hz)
+# @param freq_sb2 Start of stop band (in Hz)
+# @param passband_ripple_db Pass band ripple in dB (should be small, < 1)
+# @param stopband_atten_db Stop band attenuation in dB (should be large, >= 60)
+# @param nextra_taps Extra taps to use in the filter (default=2)
+def band_pass (gain, Fs, freq_sb1, freq_pb1, freq_pb2, freq_sb2,
+ passband_ripple_db, stopband_atten_db,
+ nextra_taps=2):
+ passband_dev = passband_ripple_to_dev (passband_ripple_db)
+ stopband_dev = stopband_atten_to_dev (stopband_atten_db)
+ desired_ampls = (0, gain, 0)
+ desired_freqs = [freq_sb1, freq_pb1, freq_pb2, freq_sb2]
+ desired_ripple = [stopband_dev, passband_dev, stopband_dev]
+ (n, fo, ao, w) = remezord (desired_freqs, desired_ampls,
+ desired_ripple, Fs)
+ # The remezord typically under-estimates the filter order, so add 2 taps by default
+ taps = gr.remez (n + nextra_taps, fo, ao, w, "bandpass")
+ return taps
+
+
+## Builds a band pass filter with complex taps by making an LPF and
+# spinning it up to the right center frequency
+# @param gain Filter gain in the passband (linear)
+# @param Fs Sampling rate (sps)
+# @param freq_sb1 End of stop band (in Hz)
+# @param freq_pb1 Start of pass band (in Hz)
+# @param freq_pb2 End of pass band (in Hz)
+# @param freq_sb2 Start of stop band (in Hz)
+# @param passband_ripple_db Pass band ripple in dB (should be small, < 1)
+# @param stopband_atten_db Stop band attenuation in dB (should be large, >= 60)
+# @param nextra_taps Extra taps to use in the filter (default=2)
+def complex_band_pass (gain, Fs, freq_sb1, freq_pb1, freq_pb2, freq_sb2,
+ passband_ripple_db, stopband_atten_db,
+ nextra_taps=2):
+ center_freq = (freq_pb2 + freq_pb1) / 2.0
+ lp_pb = (freq_pb2 - center_freq)/1.0
+ lp_sb = freq_sb2 - center_freq
+ lptaps = low_pass(gain, Fs, lp_pb, lp_sb, passband_ripple_db,
+ stopband_atten_db, nextra_taps)
+ spinner = [cmath.exp(2j*cmath.pi*center_freq/Fs*i) for i in xrange(len(lptaps))]
+ taps = [s*t for s,t in zip(spinner, lptaps)]
+ return taps
+
+
+## Builds a band reject filter
+# spinning it up to the right center frequency
+# @param gain Filter gain in the passband (linear)
+# @param Fs Sampling rate (sps)
+# @param freq_pb1 End of pass band (in Hz)
+# @param freq_sb1 Start of stop band (in Hz)
+# @param freq_sb2 End of stop band (in Hz)
+# @param freq_pb2 Start of pass band (in Hz)
+# @param passband_ripple_db Pass band ripple in dB (should be small, < 1)
+# @param stopband_atten_db Stop band attenuation in dB (should be large, >= 60)
+# @param nextra_taps Extra taps to use in the filter (default=2)
+def band_reject (gain, Fs, freq_pb1, freq_sb1, freq_sb2, freq_pb2,
+ passband_ripple_db, stopband_atten_db,
+ nextra_taps=2):
+ passband_dev = passband_ripple_to_dev (passband_ripple_db)
+ stopband_dev = stopband_atten_to_dev (stopband_atten_db)
+ desired_ampls = (gain, 0, gain)
+ desired_freqs = [freq_pb1, freq_sb1, freq_sb2, freq_pb2]
+ desired_ripple = [passband_dev, stopband_dev, passband_dev]
+ (n, fo, ao, w) = remezord (desired_freqs, desired_ampls,
+ desired_ripple, Fs)
+ # Make sure we use an odd number of taps
+ if((n+nextra_taps)%2 == 1):
+ n += 1
+ # The remezord typically under-estimates the filter order, so add 2 taps by default
+ taps = gr.remez (n + nextra_taps, fo, ao, w, "bandpass")
+ return taps
+
+
+## Builds a high pass filter.
+# @param gain Filter gain in the passband (linear)
+# @param Fs Sampling rate (sps)
+# @param freq1 End of stop band (in Hz)
+# @param freq2 Start of pass band (in Hz)
+# @param passband_ripple_db Pass band ripple in dB (should be small, < 1)
+# @param stopband_atten_db Stop band attenuation in dB (should be large, >= 60)
+# @param nextra_taps Extra taps to use in the filter (default=2)
+def high_pass (gain, Fs, freq1, freq2, passband_ripple_db, stopband_atten_db,
+ nextra_taps=2):
+ 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)
+ # For a HPF, we need to use an odd number of taps
+ # In gr.remez, ntaps = n+1, so n must be even
+ if((n+nextra_taps)%2 == 1):
+ n += 1
+
+ # The remezord typically under-estimates the filter order, so add 2 taps by default
+ 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/window.py b/gnuradio-core/src/python/gnuradio/window.py
new file mode 100644
index 000000000..4a1d0c516
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/window.py
@@ -0,0 +1,180 @@
+#
+# Copyright 2004,2005,2009 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, 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 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
+
+# Closure to generate functions to create cos windows
+
+def coswindow(coeffs):
+ def closure(fft_size):
+ window = [0] * fft_size
+ #print list(enumerate(coeffs))
+ for w_index in range(fft_size):
+ for (c_index, coeff) in enumerate(coeffs):
+ window[w_index] += (-1)**c_index * coeff * math.cos(2.0*c_index*math.pi*(w_index+0.5)/(fft_size-1))
+ return window
+ return closure
+
+blackmanharris = coswindow((0.35875,0.48829,0.14128,0.01168))
+nuttall = coswindow((0.3635819,0.4891775,0.1365995,0.0106411)) # Wikipedia calls this Blackman-Nuttall
+nuttall_cfd = coswindow((0.355768,0.487396,0.144232,0.012604)) # Wikipedia calls this Nuttall, continuous first deriv
+flattop = coswindow((1.0,1.93,1.29,0.388,0.032)) # Flat top window, coeffs from Wikipedia
+rectangular = lambda fft_size: [1]*fft_size