diff options
Diffstat (limited to 'gnuradio-core/src/python')
94 files changed, 11518 insertions, 0 deletions
diff --git a/gnuradio-core/src/python/Makefile.am b/gnuradio-core/src/python/Makefile.am new file mode 100644 index 000000000..99f860264 --- /dev/null +++ b/gnuradio-core/src/python/Makefile.am @@ -0,0 +1,29 @@ +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +include $(top_srcdir)/Makefile.common + +SUBDIRS = gnuradio bin + +noinst_PYTHON = \ + build_utils.py \ + build_utils_codes.py + diff --git a/gnuradio-core/src/python/bin/Makefile.am b/gnuradio-core/src/python/bin/Makefile.am new file mode 100644 index 000000000..3e9b90763 --- /dev/null +++ b/gnuradio-core/src/python/bin/Makefile.am @@ -0,0 +1,30 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +include $(top_srcdir)/Makefile.common + + +EXTRA_DIST = microtune.py + +bin_SCRIPTS = \ + microtune.py + +CLEANFILES = *.pyc diff --git a/gnuradio-core/src/python/bin/microtune.py b/gnuradio-core/src/python/bin/microtune.py new file mode 100755 index 000000000..0e799c93d --- /dev/null +++ b/gnuradio-core/src/python/bin/microtune.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +# -*- Python -*- + +from gnuradio import gr +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import stdgui, fftsink +from optparse import OptionParser +from gnuradio import eng_notation + + +def main (): + parser = OptionParser (option_class=eng_option) + parser.add_option ("-g", "--gain", type="eng_float", default=-1, + help="set front end gain to GAIN [0,1000]") + parser.add_option ("-f", "--freq", type="eng_float", default=-1, + help="set front end center frequency to FREQ") + parser.add_option ("-t", "--type", type="string", default="4937", + help="select eval board type {4937 or 4702}") + parser.add_option ("-p", "--port", type="int", default=0, + help="parallel port eval board is attached to") + (options, args) = parser.parse_args () + + if options.type == "4937": + front_end = gr.microtune_4937_eval_board (options.port) + elif options.type == "4702": + front_end = gr.microtune_4702_eval_board (options.port) + else: + raise RuntimeError, "Invalid board type. Must be either -t 4937 or -t 4702" + + if options.gain != -1: + front_end.set_AGC (options.gain) + + if options.freq != -1: + if options.freq < 1e6: + options.freq = options.freq * 1e6 + + actual = front_end.set_RF_freq (options.freq) + print "microtune: actual freq = %s" % (eng_notation.num_to_str (actual),) + + +if __name__ == '__main__': + main () diff --git a/gnuradio-core/src/python/build_utils.py b/gnuradio-core/src/python/build_utils.py new file mode 100644 index 000000000..18cfc2e7d --- /dev/null +++ b/gnuradio-core/src/python/build_utils.py @@ -0,0 +1,163 @@ +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +"""Misc utilities used at build time +""" + +import re, os, os.path +from build_utils_codes import * + + +# set srcdir to the directory that contains Makefile.am +try: + srcdir = os.environ['srcdir'] +except KeyError, e: + srcdir = "." +srcdir = srcdir + '/' + + +name_dict = {} + +def log_output_name (name): + (base, ext) = os.path.splitext (name) + ext = ext[1:] # drop the leading '.' + + entry = name_dict.setdefault (ext, []) + entry.append (name) + +def open_and_log_name (name, dir): + f = open (name, dir) + log_output_name (name) + return f + +def expand_template (d, template_filename, extra = ""): + '''Given a dictionary D and a TEMPLATE_FILENAME, expand template into output file + ''' + output_extension = extract_extension (template_filename) + template = open_src (template_filename, 'r') + output_name = d['NAME'] + extra + '.' + output_extension + log_output_name (output_name) + output = open (output_name, 'w') + do_substitution (d, template, output) + template.close () + output.close () + +def output_glue (dirname): + output_makefile_fragment () + output_ifile_include (dirname) + +def output_makefile_fragment (): + f = open ('Makefile.gen', 'w') + f.write ('#\n# This file is machine generated. All edits will be overwritten\n#\n') + output_subfrag (f, 'h') + output_subfrag (f, 'i') + output_subfrag (f, 'cc') + f.close () + +def output_ifile_include (dirname): + f = open ('%s_generated.i' % (dirname,), 'w') + f.write ('//\n// This file is machine generated. All edits will be overwritten\n//\n') + files = name_dict.setdefault ('i', []) + files.sort () + f.write ('%{\n') + for file in files: + f.write ('#include <%s>\n' % (file[0:-1] + 'h',)) + f.write ('%}\n\n') + for file in files: + f.write ('%%include <%s>\n' % (file,)) + +def output_subfrag (f, ext): + files = name_dict.setdefault (ext, []) + files.sort () + f.write ("GENERATED_%s =" % (ext.upper ())) + for file in files: + f.write (" \\\n\t%s" % (file,)) + f.write ("\n\n") + + +def extract_extension (template_name): + # template name is something like: GrFIRfilterXXX.h.t + # we return everything between the penultimate . and .t + mo = re.search (r'\.([a-z]+)\.t$', template_name) + if not mo: + raise ValueError, "Incorrectly formed template_name '%s'" % (template_name,) + return mo.group (1) + +def open_src (name, mode): + global srcdir + return open (os.path.join (srcdir, name), mode) + +def do_substitution (d, in_file, out_file): + def repl (match_obj): + key = match_obj.group (1) + # print key + return d[key] + + inp = in_file.read () + out = re.sub (r"@([a-zA-Z0-9_]+)@", repl, inp) + out_file.write (out) + + + +copyright = '''/* -*- c++ -*- */ +/* + * Copyright 2003,2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +''' + +def is_complex (code3): + if i_code (code3) == 'c' or o_code (code3) == 'c': + return '1' + else: + return '0' + + +def standard_dict (name, code3): + d = {} + d['NAME'] = name + d['GUARD_NAME'] = 'INCLUDED_%s_H' % name.upper () + d['BASE_NAME'] = re.sub ('^gr_', '', name) + d['SPTR_NAME'] = '%s_sptr' % name + d['WARNING'] = 'WARNING: this file is machine generated. Edits will be over written' + d['COPYRIGHT'] = copyright + d['TYPE'] = i_type (code3) + d['I_TYPE'] = i_type (code3) + d['O_TYPE'] = o_type (code3) + d['TAP_TYPE'] = tap_type (code3) + d['IS_COMPLEX'] = is_complex (code3) + return d diff --git a/gnuradio-core/src/python/build_utils_codes.py b/gnuradio-core/src/python/build_utils_codes.py new file mode 100644 index 000000000..f4215f2b4 --- /dev/null +++ b/gnuradio-core/src/python/build_utils_codes.py @@ -0,0 +1,52 @@ +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +def i_code (code3): + return code3[0] + +def o_code (code3): + if len (code3) >= 2: + return code3[1] + else: + return code3[0] + +def tap_code (code3): + if len (code3) >= 3: + return code3[2] + else: + return code3[0] + +def i_type (code3): + return char_to_type[i_code (code3)] + +def o_type (code3): + return char_to_type[o_code (code3)] + +def tap_type (code3): + return char_to_type[tap_code (code3)] + + +char_to_type = {} +char_to_type['s'] = 'short' +char_to_type['i'] = 'int' +char_to_type['f'] = 'float' +char_to_type['c'] = 'gr_complex' +char_to_type['b'] = 'unsigned char' diff --git a/gnuradio-core/src/python/gnuradio/Makefile.am b/gnuradio-core/src/python/gnuradio/Makefile.am new file mode 100644 index 000000000..3222c5db3 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/Makefile.am @@ -0,0 +1,36 @@ +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +include $(top_srcdir)/Makefile.common + +SUBDIRS = gr gru gruimpl blks blksimpl + +grpython_PYTHON = \ + __init__.py \ + audio.py \ + eng_notation.py \ + eng_option.py \ + packet_utils.py \ + gr_unittest.py \ + optfir.py \ + window.py + +CLEANFILES = *.pyc diff --git a/gnuradio-core/src/python/gnuradio/__init__.py b/gnuradio-core/src/python/gnuradio/__init__.py new file mode 100644 index 000000000..a4917cf64 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/__init__.py @@ -0,0 +1 @@ +# make this a package diff --git a/gnuradio-core/src/python/gnuradio/audio.py b/gnuradio-core/src/python/gnuradio/audio.py new file mode 100644 index 000000000..5a9d09c77 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/audio.py @@ -0,0 +1,88 @@ +# +# Copyright 2004,2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +""" +This is the 'generic' audio or soundcard interface. + +The behavior of this module is controlled by the [audio] audio_module +configuration parameter. If it is 'auto' we attempt to import modules +from the known_modules list, using the first one imported successfully. + +If [audio] audio_module is not 'auto', we assume it's the name of +an audio module and attempt to import it. +""" + +__all__ = ['source', 'sink'] + +from gnuradio import gr +import sys + +source = None +sink = None + + +known_modules = ( + 'audio_alsa', 'audio_oss', 'audio_osx', 'audio_jack', 'audio_portaudio') + + +def try_import(name): + """ + Build a blob of code and try to execute it. + If it succeeds we will have set the globals source and sink + as side effects. + + returns True or False + """ + global source, sink + full_name = "gnuradio." + name + code = """ +import %s +source = %s.source +sink = %s.sink +""" % (full_name, full_name, full_name) + try: + exec code in globals() + return True + except ImportError: + return False + + +def __init__ (): + p = gr.prefs() # get preferences (config file) object + verbose = p.get_bool('audio', 'verbose', False) + module = p.get_string('audio', 'audio_module', 'auto') + + if module == 'auto': # search our list for the first one that we can import + for m in known_modules: + if try_import(m): + if verbose: sys.stderr.write('audio: using %s\n' % (m,)) + return + raise ImportError, 'Unable to locate an audio module.' + + else: # use the one the user specified + if try_import(module): + if verbose: sys.stderr.write('audio: using %s\n' % (module,)) + else: + msg = 'Failed to import user-specified audio module %s' % (module,) + if verbose: sys.stderr.write('audio: %s\n' % (msg,)) + raise ImportError, msg + +__init__() diff --git a/gnuradio-core/src/python/gnuradio/blks/Makefile.am b/gnuradio-core/src/python/gnuradio/blks/Makefile.am new file mode 100644 index 000000000..17574d77b --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blks/Makefile.am @@ -0,0 +1,35 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +include $(top_srcdir)/Makefile.common + +# EXTRA_DIST = run_tests.in +# TESTS = run_tests + +grblkspythondir = $(grpythondir)/blks + +grblkspython_PYTHON = \ + __init__.py + + +noinst_PYTHON = + +CLEANFILES = *.pyc *.pyo diff --git a/gnuradio-core/src/python/gnuradio/blks/__init__.py b/gnuradio-core/src/python/gnuradio/blks/__init__.py new file mode 100644 index 000000000..4cc10ebb3 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blks/__init__.py @@ -0,0 +1,37 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +import glob +import os.path + +# Semi-hideous kludge to import everything in the blksimpl directory +# into the gnuradio.blks namespace. This keeps us from having to remember +# to manually update this file. + +for p in __path__: + filenames = glob.glob (os.path.join (p, "..", "blksimpl", "*.py")) + for f in filenames: + f = os.path.basename(f).lower() + f = f[:-3] + if f == '__init__': + continue + # print f + exec "from gnuradio.blksimpl.%s import *" % (f,) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/Makefile.am b/gnuradio-core/src/python/gnuradio/blksimpl/Makefile.am new file mode 100644 index 000000000..415920b29 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/Makefile.am @@ -0,0 +1,49 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +include $(top_srcdir)/Makefile.common + +# EXTRA_DIST = run_tests.in +# TESTS = run_tests + +grblkspythondir = $(grpythondir)/blksimpl + +grblkspython_PYTHON = \ + __init__.py \ + am_demod.py \ + filterbank.py \ + fm_demod.py \ + fm_emph.py \ + gmsk2.py \ + gmsk2_pkt.py \ + nbfm_rx.py \ + nbfm_tx.py \ + pkt.py \ + rational_resampler.py \ + standard_squelch.py \ + wfm_rcv.py \ + wfm_rcv_pll.py \ + wfm_tx.py + + +noinst_PYTHON = + +CLEANFILES = *.pyc *.pyo diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/__init__.py b/gnuradio-core/src/python/gnuradio/blksimpl/__init__.py new file mode 100644 index 000000000..a4917cf64 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/__init__.py @@ -0,0 +1 @@ +# make this a package diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/am_demod.py b/gnuradio-core/src/python/gnuradio/blksimpl/am_demod.py new file mode 100644 index 000000000..309f5e650 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/am_demod.py @@ -0,0 +1,75 @@ +# +# Copyright 2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, optfir + +class am_demod_cf(gr.hier_block): + """ + Generalized AM demodulation block with audio filtering. + + This block demodulates a band-limited, complex down-converted AM + channel into the the original baseband signal, applying low pass + filtering to the audio output. It produces a float stream in the + range [-1.0, +1.0]. + + @param fg: flowgraph + @param channel_rate: incoming sample rate of the AM baseband + @type sample_rate: integer + @param audio_decim: input to output decimation rate + @type audio_decim: integer + @param audio_pass: audio low pass filter passband frequency + @type audio_pass: float + @param audio_stop: audio low pass filter stop frequency + @type audio_stop: float + """ + def __init__(self, fg, channel_rate, audio_decim, audio_pass, audio_stop): + MAG = gr.complex_to_mag() + DCR = gr.add_const_ff(-1.0) + + audio_taps = optfir.low_pass(0.5, # Filter gain + channel_rate, # Sample rate + audio_pass, # Audio passband + audio_stop, # Audio stopband + 0.1, # Passband ripple + 60) # Stopband attenuation + LPF = gr.fir_filter_fff(audio_decim, audio_taps) + + fg.connect(MAG, DCR, LPF) + gr.hier_block.__init__(self, fg, MAG, LPF) + +class demod_10k0a3e_cf(am_demod_cf): + """ + AM demodulation block, 10 KHz channel. + + This block demodulates an AM channel conformant to 10K0A3E emission + standards, such as broadcast band AM transmissions. + + @param fg: flowgraph + @param channel_rate: incoming sample rate of the AM baseband + @type sample_rate: integer + @param audio_decim: input to output decimation rate + @type audio_decim: integer + """ + def __init__(self, fg, channel_rate, audio_decim): + am_demod_cf.__init__(self, fg, channel_rate, audio_decim, + 5000, # Audio passband + 5500) # Audio stopband +
\ No newline at end of file diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/digital_voice.py.real b/gnuradio-core/src/python/gnuradio/blksimpl/digital_voice.py.real new file mode 100644 index 000000000..1b3a14f3e --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/digital_voice.py.real @@ -0,0 +1,102 @@ +#!/usr/bin/env python +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +""" +Digital voice Tx and Rx using GSM 13kbit vocoder and GMSK. + +Runs channel at 32kbit/sec. Currently uses fake channel coding, +but there's room for a rate 1/2 coder. +""" + +from gnuradio import gr, gru +from gnuradio.blksimpl.gmsk import gmsk_mod, gmsk_demod + +from gnuradio.vocoder import gsm_full_rate + +# Size of gsm full rate speech encoder output packet in bytes + +GSM_FRAME_SIZE = 33 + +# Size of packet in bytes that we send to GMSK modulator: +# +# Target: 256kS/sec air rate. +# +# 256kS 1 sym 1 bit 1 byte 0.020 sec 80 bytes +# ---- * ----- * ----- * ------ * --------- = -------- +# sec 8 S 1 sym 8 bits frame frame +# +# gr_simple_framer add 10 bytes of overhead. + +AIR_FRAME_SIZE = 70 + + +class digital_voice_tx(gr.hier_block): + """ + Hierarchical block for digital voice tranmission. + + The input is 8kS/sec floating point audio in the range [-1,+1] + The output is 256kS/sec GMSK modulated complex baseband signal in the range [-1,+1]. + """ + def __init__(self, fg): + samples_per_symbol = 8 + symbol_rate = 32000 + bt = 0.3 # Gaussian filter bandwidth * symbol time + + src_scale = gr.multiply_const_ff(32767) + f2s = gr.float_to_short() + voice_coder = gsm_full_rate.encode_sp() + + channel_coder = gr.fake_channel_encoder_pp(GSM_FRAME_SIZE, AIR_FRAME_SIZE) + p2s = gr.parallel_to_serial(gr.sizeof_char, AIR_FRAME_SIZE) + + mod = gmsk_mod(fg, sps=samples_per_symbol, + symbol_rate=symbol_rate, bt=bt, + p_size=AIR_FRAME_SIZE) + + fg.connect(src_scale, f2s, voice_coder, channel_coder, p2s, mod) + gr.hier_block.__init__(self, fg, src_scale, mod) + + +class digital_voice_rx(gr.hier_block): + """ + Hierarchical block for digital voice reception. + + The input is 256kS/sec GMSK modulated complex baseband signal. + The output is 8kS/sec floating point audio in the range [-1,+1] + """ + def __init__(self, fg): + samples_per_symbol = 8 + symbol_rate = 32000 + + demod = gmsk_demod(fg, sps=samples_per_symbol, + symbol_rate=symbol_rate, + p_size=AIR_FRAME_SIZE) + + s2p = gr.serial_to_parallel(gr.sizeof_char, AIR_FRAME_SIZE) + channel_decoder = gr.fake_channel_decoder_pp(AIR_FRAME_SIZE, GSM_FRAME_SIZE) + + voice_decoder = gsm_full_rate.decode_ps() + s2f = gr.short_to_float () + sink_scale = gr.multiply_const_ff(1.0/32767.) + + fg.connect(demod, s2p, channel_decoder, voice_decoder, s2f, sink_scale) + gr.hier_block.__init__(self, fg, demod, sink_scale) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/filterbank.py b/gnuradio-core/src/python/gnuradio/blksimpl/filterbank.py new file mode 100644 index 000000000..bd23f7936 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/filterbank.py @@ -0,0 +1,160 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +import sys +from gnuradio import gr, gru + +def _generate_synthesis_taps(mpoints): + return [] # FIXME + + +def _split_taps(taps, mpoints): + assert (len(taps) % mpoints) == 0 + result = [list() for x in range(mpoints)] + for i in xrange(len(taps)): + (result[i % mpoints]).append(taps[i]) + return [tuple(x) for x in result] + + +class synthesis_filterbank(gr.hier_block): + """ + Uniformly modulated polyphase DFT filter bank: synthesis + + See http://cnx.rice.edu/content/m10424/latest + """ + def __init__(self, fg, mpoints, taps=None): + """ + Takes M complex streams in, produces single complex stream out + that runs at M times the input sample rate + + @param fg: flow_graph + @param mpoints: number of freq bins/interpolation factor/subbands + @param taps: filter taps for subband filter + + The channel spacing is equal to the input sample rate. + The total bandwidth and output sample rate are equal the input + sample rate * nchannels. + + Output stream to frequency mapping: + + channel zero is at zero frequency. + + if mpoints is odd: + + Channels with increasing positive frequencies come from + channels 1 through (N-1)/2. + + Channel (N+1)/2 is the maximum negative frequency, and + frequency increases through N-1 which is one channel lower + than the zero frequency. + + if mpoints is even: + + Channels with increasing positive frequencies come from + channels 1 through (N/2)-1. + + Channel (N/2) is evenly split between the max positive and + negative bins. + + Channel (N/2)+1 is the maximum negative frequency, and + frequency increases through N-1 which is one channel lower + than the zero frequency. + + Channels near the frequency extremes end up getting cut + off by subsequent filters and therefore have diminished + utility. + """ + item_size = gr.sizeof_gr_complex + + if taps is None: + taps = _generate_synthesis_taps(mpoints) + + # pad taps to multiple of mpoints + r = len(taps) % mpoints + if r != 0: + taps = taps + (mpoints - r) * (0,) + + # split in mpoints separate set of taps + sub_taps = _split_taps(taps, mpoints) + + self.ss2v = gr.streams_to_vector(item_size, mpoints) + self.ifft = gr.fft_vcc(mpoints, False, []) + self.v2ss = gr.vector_to_streams(item_size, mpoints) + # mpoints filters go in here... + self.ss2s = gr.streams_to_stream(item_size, mpoints) + + fg.connect(self.ss2v, self.ifft, self.v2ss) + + # build mpoints fir filters... + for i in range(mpoints): + f = gr.fft_filter_ccc(1, sub_taps[i]) + fg.connect((self.v2ss, i), f) + fg.connect(f, (self.ss2s, i)) + + gr.hier_block.__init__(self, fg, self.ss2v, self.ss2s) + + +class analysis_filterbank(gr.hier_block): + """ + Uniformly modulated polyphase DFT filter bank: analysis + + See http://cnx.rice.edu/content/m10424/latest + """ + def __init__(self, fg, mpoints, taps=None): + """ + Takes 1 complex stream in, produces M complex streams out + that runs at 1/M times the input sample rate + + @param fg: flow_graph + @param mpoints: number of freq bins/interpolation factor/subbands + @param taps: filter taps for subband filter + + Same channel to frequency mapping as described above. + """ + item_size = gr.sizeof_gr_complex + + if taps is None: + taps = _generate_synthesis_taps(mpoints) + + # pad taps to multiple of mpoints + r = len(taps) % mpoints + if r != 0: + taps = taps + (mpoints - r) * (0,) + + # split in mpoints separate set of taps + sub_taps = _split_taps(taps, mpoints) + + # print >> sys.stderr, "mpoints =", mpoints, "len(sub_taps) =", len(sub_taps) + + self.s2ss = gr.stream_to_streams(item_size, mpoints) + # filters here + self.ss2v = gr.streams_to_vector(item_size, mpoints) + self.fft = gr.fft_vcc(mpoints, True, []) + self.v2ss = gr.vector_to_streams(item_size, mpoints) + + # build mpoints fir filters... + for i in range(mpoints): + f = gr.fft_filter_ccc(1, sub_taps[mpoints-i-1]) + fg.connect((self.s2ss, i), f) + fg.connect(f, (self.ss2v, i)) + + fg.connect(self.ss2v, self.fft, self.v2ss) + gr.hier_block.__init__(self, fg, self.s2ss, self.v2ss) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/fm_demod.py b/gnuradio-core/src/python/gnuradio/blksimpl/fm_demod.py new file mode 100644 index 000000000..9487e0f0f --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/fm_demod.py @@ -0,0 +1,122 @@ +# +# Copyright 2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, optfir +from gnuradio.blksimpl.fm_emph import fm_deemph +from math import pi + +class fm_demod_cf(gr.hier_block): + """ + Generalized FM demodulation block with deemphasis and audio + filtering. + + This block demodulates a band-limited, complex down-converted FM + channel into the the original baseband signal, optionally applying + deemphasis. Low pass filtering is done on the resultant signal. It + produces an output float strem in the range of [-1.0, +1.0]. + + @param fg: flowgraph + @param channel_rate: incoming sample rate of the FM baseband + @type sample_rate: integer + @param deviation: maximum FM deviation (default = 5000) + @type deviation: float + @param audio_decim: input to output decimation rate + @type audio_decim: integer + @param audio_pass: audio low pass filter passband frequency + @type audio_pass: float + @param audio_stop: audio low pass filter stop frequency + @type audio_stop: float + @param gain: gain applied to audio output (default = 1.0) + @type gain: float + @param tau: deemphasis time constant (default = 75e-6), specify 'None' + to prevent deemphasis + """ + def __init__(self, fg, channel_rate, audio_decim, deviation, + audio_pass, audio_stop, gain=1.0, tau=75e-6): + + """ + # Equalizer for ~100 us delay + delay = 100e-6 + num_taps = int(channel_rate*delay) + + mu = 1e-4/num_taps + print "CMA: delay =", delay, "n =", num_taps, "mu =", mu + CMA = gr.cma_equalizer_cc(num_taps, 1.0, mu) + """ + k = channel_rate/(2*pi*deviation) + QUAD = gr.quadrature_demod_cf(k) + + audio_taps = optfir.low_pass(gain, # Filter gain + channel_rate, # Sample rate + audio_pass, # Audio passband + audio_stop, # Audio stopband + 0.1, # Passband ripple + 60) # Stopband attenuation + LPF = gr.fir_filter_fff(audio_decim, audio_taps) + + if tau is not None: + DEEMPH = fm_deemph(fg, channel_rate, tau) + fg.connect(QUAD, DEEMPH, LPF) + else: + fg.connect(QUAD, LPF) + + gr.hier_block.__init__(self, fg, QUAD, LPF) + +class demod_20k0f3e_cf(fm_demod_cf): + """ + NBFM demodulation block, 20 KHz channels + + This block demodulates a complex, downconverted, narrowband FM + channel conforming to 20K0F3E emission standards, outputting + floats in the range [-1.0, +1.0]. + + @param fg: flowgraph + @param sample_rate: incoming sample rate of the FM baseband + @type sample_rate: integer + @param audio_decim: input to output decimation rate + @type audio_decim: integer + """ + def __init__(self, fg, channel_rate, audio_decim): + fm_demod_cf.__init__(self, fg, channel_rate, audio_decim, + 5000, # Deviation + 3000, # Audio passband frequency + 4000) # Audio stopband frequency + +class demod_200kf3e_cf(fm_demod_cf): + """ + WFM demodulation block, mono. + + This block demodulates a complex, downconverted, wideband FM + channel conforming to 200KF3E emission standards, outputting + floats in the range [-1.0, +1.0]. + + @param fg: flowgraph + @param sample_rate: incoming sample rate of the FM baseband + @type sample_rate: integer + @param audio_decim: input to output decimation rate + @type audio_decim: integer + """ + def __init__(self, fg, channel_rate, audio_decim): + fm_demod_cf.__init__(self, fg, channel_rate, audio_decim, + 75000, # Deviation + 15000, # Audio passband + 16000, # Audio stopband + 20.0) # Audio gain diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/fm_emph.py b/gnuradio-core/src/python/gnuradio/blksimpl/fm_emph.py new file mode 100644 index 000000000..5c256f5d0 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/fm_emph.py @@ -0,0 +1,145 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr +import math + + +# +# 1 +# H(s) = ------- +# 1 + s +# +# tau is the RC time constant. +# critical frequency: w_p = 1/tau +# +# We prewarp and use the bilinear z-transform to get our IIR coefficients. +# See "Digital Signal Processing: A Practical Approach" by Ifeachor and Jervis +# + +class fm_deemph(gr.hier_block): + """ + FM Deemphasis IIR filter. + """ + def __init__(self, fg, fs, tau=75e-6): + """ + @param fg: flow graph + @type fg: gr.flow_graph + @param fs: sampling frequency in Hz + @type fs: float + @param tau: Time constant in seconds (75us in US, 50us in EUR) + @type tau: float + """ + w_p = 1/tau + w_pp = math.tan (w_p / (fs * 2)) # prewarped analog freq + + a1 = (w_pp - 1)/(w_pp + 1) + b0 = w_pp/(1 + w_pp) + b1 = b0 + + btaps = [b0, b1] + ataps = [1, a1] + + if 0: + print "btaps =", btaps + print "ataps =", ataps + global plot1 + plot1 = gru.gnuplot_freqz (gru.freqz (btaps, ataps), fs, True) + + deemph = gr.iir_filter_ffd(btaps, ataps) + gr.hier_block.__init__(self, fg, deemph, deemph) + +# +# 1 + s*t1 +# H(s) = ---------- +# 1 + s*t2 +# +# I think this is the right transfer function. +# +# +# This fine ASCII rendition is based on Figure 5-15 +# in "Digital and Analog Communication Systems", Leon W. Couch II +# +# +# R1 +# +-----||------+ +# | | +# o------+ +-----+--------o +# | C1 | | +# +----/\/\/\/--+ \ +# / +# \ R2 +# / +# \ +# | +# o--------------------------+--------o +# +# f1 = 1/(2*pi*t1) = 1/(2*pi*R1*C) +# +# 1 R1 + R2 +# f2 = ------- = ------------ +# 2*pi*t2 2*pi*R1*R2*C +# +# t1 is 75us in US, 50us in EUR +# f2 should be higher than our audio bandwidth. +# +# +# The Bode plot looks like this: +# +# +# /---------------- +# / +# / <-- slope = 20dB/decade +# / +# -------------/ +# f1 f2 +# +# We prewarp and use the bilinear z-transform to get our IIR coefficients. +# See "Digital Signal Processing: A Practical Approach" by Ifeachor and Jervis +# + +class fm_preemph(gr.hier_block): + """ + FM Preemphasis IIR filter. + """ + def __init__(self, fg, fs, tau=75e-6): + """ + @param fg: flow graph + @type fg: gr.flow_graph + @param fs: sampling frequency in Hz + @type fs: float + @param tau: Time constant in seconds (75us in US, 50us in EUR) + @type tau: float + """ + + # FIXME make this compute the right answer + + btaps = [1] + ataps = [1] + + if 0: + print "btaps =", btaps + print "ataps =", ataps + global plot2 + plot2 = gru.gnuplot_freqz (gru.freqz (btaps, ataps), fs, True) + + preemph = gr.iir_filter_ffd(btaps, ataps) + gr.hier_block.__init__(self, fg, preemph, preemph) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/gmsk2.py b/gnuradio-core/src/python/gnuradio/blksimpl/gmsk2.py new file mode 100644 index 000000000..68d189679 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/gmsk2.py @@ -0,0 +1,159 @@ +# +# GMSK modulation and demodulation. +# +# +# Copyright 2005,2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +# See gnuradio-examples/python/gmsk2 for examples + +from gnuradio import gr +from math import pi +import Numeric + +# ///////////////////////////////////////////////////////////////////////////// +# GMSK mod/demod with steams of bytes as data i/o +# ///////////////////////////////////////////////////////////////////////////// + +class gmsk2_mod(gr.hier_block): + + def __init__(self, fg, spb = 2, bt = 0.3): + """ + Hierarchical block for Gaussian Minimum Shift Key (GMSK) + modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + @param fg: flow graph + @type fg: flow graph + @param spb: samples per baud >= 2 + @type spb: integer + @param bt: Gaussian filter bandwidth * symbol time + @type bt: float + """ + if not isinstance(spb, int) or spb < 2: + raise TypeError, "sbp must be an integer >= 2" + self.spb = spb + + ntaps = 4 * spb # up to 3 bits in filter at once + sensitivity = (pi / 2) / spb # phase change per bit = pi / 2 + + # Turn it into NRZ data. + self.nrz = gr.bytes_to_syms() + + # Form Gaussian filter + + # Generate Gaussian response (Needs to be convolved with window below). + self.gaussian_taps = gr.firdes.gaussian( + 1, # gain + spb, # symbol_rate + bt, # bandwidth * symbol time + ntaps # number of taps + ) + + self.sqwave = (1,) * spb # rectangular window + self.taps = Numeric.convolve(Numeric.array(self.gaussian_taps),Numeric.array(self.sqwave)) + self.gaussian_filter = gr.interp_fir_filter_fff(spb, self.taps) + + # FM modulation + self.fmmod = gr.frequency_modulator_fc(sensitivity) + + # Connect + fg.connect(self.nrz, self.gaussian_filter, self.fmmod) + + # Initialize base class + gr.hier_block.__init__(self, fg, self.nrz, self.fmmod) + + def samples_per_baud(self): + return self.spb + + def bits_per_baud(self=None): # staticmethod that's also callable on an instance + return 1 + bits_per_baud = staticmethod(bits_per_baud) # make it a static method. RTFM + + +class gmsk2_demod(gr.hier_block): + + def __init__(self, fg, spb=2, omega=None, gain_mu=0.03, mu=0.5, + omega_relative_limit=0.000200, freq_error=0.0): + """ + Hierarchical block for Gaussian Minimum Shift Key (GMSK) + demodulation. + + The input is the complex modulated signal at baseband. + The output is a stream of symbols ready to be sliced at zero. + + @param fg: flow graph + @type fg: flow graph + @param spb: samples per baud + @type spb: integer + + Clock recovery parameters. These all have reasonble defaults. + + @param omega: nominal relative freq (defaults to spb) + @type omega: float + @param gain_mu: controls rate of mu adjustment + @type gain_mu: float + @param mu: fractional delay [0.0, 1.0] + @type mu: float + @param omega_relative_limit: sets max variation in omega + @type omega_relative_limit: float, typically 0.000200 (200 ppm) + @param freq_error: bit rate error as a fraction + @param float + """ + if spb < 2: + raise TypeError, "sbp >= 2" + self.spb = spb + + if omega is None: + omega = spb*(1+freq_error) + + gain_omega = .25*gain_mu*gain_mu # critically damped + + # Automatic gain control + self.preamp = gr.multiply_const_cc(10e-5) + self.agc = gr.agc_cc(1e-3, 1, 1, 1000) + + # Demodulate FM + sensitivity = (pi / 2) / spb + self.fmdemod = gr.quadrature_demod_cf(1.0 / sensitivity) + + alpha = 0.0008 + + # the clock recovery block tracks the symbol clock and resamples as needed. + # the output of the block is a stream of soft symbols (float) + self.clock_recovery = gr.clock_recovery_mm_ff(omega, gain_omega, mu, gain_mu, + omega_relative_limit) + + # slice the floats at 0, outputting 1 bit (the LSB of the output byte) per sample + self.slicer = gr.binary_slicer_fb() + + fg.connect(self.preamp, self.agc, self.fmdemod, self.clock_recovery, self.slicer) + + # Initialize base class + gr.hier_block.__init__(self, fg, self.preamp, self.slicer) + + def samples_per_baud(self): + return self.spb + + def bits_per_baud(self=None): # staticmethod that's also callable on an instance + return 1 + bits_per_baud = staticmethod(bits_per_baud) # make it a static method. RTFM diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/gmsk2_pkt.py b/gnuradio-core/src/python/gnuradio/blksimpl/gmsk2_pkt.py new file mode 100644 index 000000000..af586239a --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/gmsk2_pkt.py @@ -0,0 +1,174 @@ +# +# Copyright 2005,2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from math import pi +import Numeric + +from gnuradio import gr, packet_utils +import gnuradio.gr.gr_threading as _threading +import gmsk2 + + +def _deprecation_warning(old_name, new_name): + print '#' + print '# Warning: %s is deprecated and will be removed soon.' % (old_name,) + print '# Please use the modulation independent block, %s.' % (new_name,) + print "#" + + +# ///////////////////////////////////////////////////////////////////////////// +# GMSK mod/demod with packets as i/o +# ///////////////////////////////////////////////////////////////////////////// + +class gmsk2_mod_pkts(gr.hier_block): + """ + GSM modulator that is a GNU Radio source. + + Send packets by calling send_pkt + """ + def __init__(self, fg, access_code=None, msgq_limit=2, pad_for_usrp=True, *args, **kwargs): + """ + Hierarchical block for Gaussian Minimum Shift Key (GMSK) modulation. + + Packets to be sent are enqueued by calling send_pkt. + The output is the complex modulated signal at baseband. + + @param fg: flow graph + @type fg: flow graph + @param access_code: AKA sync vector + @type access_code: string of 1's and 0's between 1 and 64 long + @param msgq_limit: maximum number of messages in message queue + @type msgq_limit: int + @param pad_for_usrp: If true, packets are padded such that they end up a multiple of 128 samples + + See gmsk_mod for remaining parameters + """ + _deprecation_warning('gmsk2_mod_pkts', 'mod_pkts') + + self.pad_for_usrp = pad_for_usrp + if access_code is None: + access_code = packet_utils.default_access_code + if not packet_utils.is_1_0_string(access_code): + raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,) + self._access_code = access_code + + # accepts messages from the outside world + self.pkt_input = gr.message_source(gr.sizeof_char, msgq_limit) + self.gmsk_mod = gmsk2.gmsk2_mod(fg, *args, **kwargs) + fg.connect(self.pkt_input, self.gmsk_mod) + gr.hier_block.__init__(self, fg, None, self.gmsk_mod) + + def send_pkt(self, payload='', eof=False): + """ + Send the payload. + + @param payload: data to send + @type payload: string + """ + if eof: + msg = gr.message(1) # tell self.pkt_input we're not sending any more packets + else: + # print "original_payload =", string_to_hex_list(payload) + pkt = packet_utils.make_packet(payload, + self.gmsk_mod.samples_per_baud(), + self.gmsk_mod.bits_per_baud(), + self._access_code, + self.pad_for_usrp) + #print "pkt =", string_to_hex_list(pkt) + msg = gr.message_from_string(pkt) + self.pkt_input.msgq().insert_tail(msg) + + + +class gmsk2_demod_pkts(gr.hier_block): + """ + GSM demodulator that is a GNU Radio sink. + + The input is complex baseband. When packets are demodulated, they are passed to the + app via the callback. + """ + + def __init__(self, fg, access_code=None, callback=None, threshold=-1, *args, **kwargs): + """ + Hierarchical block for Gaussian Minimum Shift Key (GMSK) + demodulation. + + The input is the complex modulated signal at baseband. + Demodulated packets are sent to the handler. + + @param fg: flow graph + @type fg: flow graph + @param access_code: AKA sync vector + @type access_code: string of 1's and 0's + @param callback: function of two args: ok, payload + @type callback: ok: bool; payload: string + @param threshold: detect access_code with up to threshold bits wrong (-1 -> use default) + @type threshold: int + + See gmsk_demod for remaining parameters. + """ + + _deprecation_warning('gmsk2_demod_pkts', 'demod_pkts') + + if access_code is None: + access_code = packet_utils.default_access_code + if not packet_utils.is_1_0_string(access_code): + raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,) + self._access_code = access_code + + if threshold == -1: + threshold = 12 # FIXME raise exception + + self._rcvd_pktq = gr.msg_queue() # holds packets from the PHY + self.gmsk_demod = gmsk2.gmsk2_demod(fg, *args, **kwargs) + self.correlator = gr.correlate_access_code_bb(access_code, threshold) + + self.framer_sink = gr.framer_sink_1(self._rcvd_pktq) + fg.connect(self.gmsk_demod, self.correlator, self.framer_sink) + + gr.hier_block.__init__(self, fg, self.gmsk_demod, None) + self._watcher = _queue_watcher_thread(self._rcvd_pktq, callback) + + def carrier_sensed(self): + """ + Return True if we detect carrier. + """ + return False # FIXME + + +class _queue_watcher_thread(_threading.Thread): + def __init__(self, rcvd_pktq, callback): + _threading.Thread.__init__(self) + self.setDaemon(1) + self.rcvd_pktq = rcvd_pktq + self.callback = callback + self.keep_running = True + self.start() + + #def stop(self): + # self.keep_running = False + + def run(self): + while self.keep_running: + msg = self.rcvd_pktq.delete_head() + ok, payload = packet_utils.unmake_packet(msg.to_string()) + if self.callback: + self.callback(ok, payload) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/nbfm_rx.py b/gnuradio-core/src/python/gnuradio/blksimpl/nbfm_rx.py new file mode 100644 index 000000000..39059ec9c --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/nbfm_rx.py @@ -0,0 +1,87 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +import math +from gnuradio import gr, optfir +from gnuradio.blksimpl.fm_emph import fm_deemph +from gnuradio.blksimpl.standard_squelch import standard_squelch + +class nbfm_rx(gr.hier_block): + def __init__(self, fg, audio_rate, quad_rate, tau=75e-6, max_dev=5e3): + """ + Narrow Band FM Receiver. + + Takes a single complex baseband input stream and produces a single + float output stream of audio sample in the range [-1, +1]. + + @param fg: flow graph + @param audio_rate: sample rate of audio stream, >= 16k + @type audio_rate: integer + @param quad_rate: sample rate of output stream + @type quad_rate: integer + @param tau: preemphasis time constant (default 75e-6) + @type tau: float + @param max_dev: maximum deviation in Hz (default 5e3) + @type max_dev: float + + quad_rate must be an integer multiple of audio_rate. + + Exported sub-blocks (attributes): + squelch + quad_demod + deemph + audio_filter + """ + + # FIXME audio_rate and quad_rate ought to be exact rationals + audio_rate = int(audio_rate) + quad_rate = int(quad_rate) + + if quad_rate % audio_rate != 0: + raise ValueError, "quad_rate is not an integer multiple of audio_rate" + + squelch_threshold = 20 # dB + #self.squelch = gr.simple_squelch_cc(squelch_threshold, 0.001) + + # FM Demodulator input: complex; output: float + k = quad_rate/(2*math.pi*max_dev) + self.quad_demod = gr.quadrature_demod_cf(k) + + # FM Deemphasis IIR filter + self.deemph = fm_deemph (fg, quad_rate, tau=tau) + + # compute FIR taps for audio filter + audio_decim = quad_rate // audio_rate + audio_taps = gr.firdes.low_pass (1.0, # gain + quad_rate, # sampling rate + 4.5e3, # Audio LPF cutoff + 2.5e3, # Transition band + gr.firdes.WIN_HAMMING) # filter type + + print "len(audio_taps) =", len(audio_taps) + + # Decimating audio filter + # input: float; output: float; taps: float + self.audio_filter = gr.fir_filter_fff(audio_decim, audio_taps) + + fg.connect(self.quad_demod, self.deemph, self.audio_filter) + + gr.hier_block.__init__(self, fg, self.quad_demod, self.audio_filter) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/nbfm_tx.py b/gnuradio-core/src/python/gnuradio/blksimpl/nbfm_tx.py new file mode 100644 index 000000000..2f636b67f --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/nbfm_tx.py @@ -0,0 +1,95 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +import math +from gnuradio import gr, optfir +from gnuradio.blksimpl.fm_emph import fm_preemph + +#from gnuradio import ctcss + +class nbfm_tx(gr.hier_block): + def __init__(self, fg, audio_rate, quad_rate, tau=75e-6, max_dev=5e3): + """ + Narrow Band FM Transmitter. + + Takes a single float input stream of audio samples in the range [-1,+1] + and produces a single FM modulated complex baseband output. + + @param fg: flow graph + @param audio_rate: sample rate of audio stream, >= 16k + @type audio_rate: integer + @param quad_rate: sample rate of output stream + @type quad_rate: integer + @param tau: preemphasis time constant (default 75e-6) + @type tau: float + @param max_dev: maximum deviation in Hz (default 5e3) + @type max_dev: float + + quad_rate must be an integer multiple of audio_rate. + """ + + # FIXME audio_rate and quad_rate ought to be exact rationals + audio_rate = int(audio_rate) + quad_rate = int(quad_rate) + + if quad_rate % audio_rate != 0: + raise ValueError, "quad_rate is not an integer multiple of audio_rate" + + + do_interp = audio_rate != quad_rate + + if do_interp: + interp_factor = quad_rate / audio_rate + interp_taps = optfir.low_pass (interp_factor, # gain + quad_rate, # Fs + 4500, # passband cutoff + 7000, # stopband cutoff + 0.1, # passband ripple dB + 40) # stopband atten dB + + #print "len(interp_taps) =", len(interp_taps) + self.interpolator = gr.interp_fir_filter_fff (interp_factor, interp_taps) + + self.preemph = fm_preemph (fg, quad_rate, tau=tau) + + k = 2 * math.pi * max_dev / quad_rate + self.modulator = gr.frequency_modulator_fc (k) + + if do_interp: + fg.connect (self.interpolator, self.preemph, self.modulator) + gr.hier_block.__init__(self, fg, self.interpolator, self.modulator) + else: + fg.connect(self.preemph, self.modulator) + gr.hier_block.__init__(self, fg, self.preemph, self.modulator) + + +#class ctcss_gen_f(gr.sig_source_f): +# def __init__(self, sample_rate, tone_freq): +# gr.sig_source_f.__init__(self, sample_rate, gr.SIN_WAVE, tone_freq, 0.1, 0.0) +# +# def set_tone (self, tone): +# gr.sig_source_f.set_frequency(self,tone) + +class ctcss_gen_f(gr.hier_block): + def __init__(self, fg, sample_rate, tone_freq): + self.plgen = gr.sig_source_f(sample_rate, gr.GR_SIN_WAVE, tone_freq, 0.1, 0.0) + + gr.hier_block.__init__(self, fg, self.plgen, self.plgen) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/pkt.py b/gnuradio-core/src/python/gnuradio/blksimpl/pkt.py new file mode 100644 index 000000000..3ebb7229c --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/pkt.py @@ -0,0 +1,156 @@ +# +# Copyright 2005,2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from math import pi +import Numeric + +from gnuradio import gr, packet_utils +import gnuradio.gr.gr_threading as _threading + + +# ///////////////////////////////////////////////////////////////////////////// +# mod/demod with packets as i/o +# ///////////////////////////////////////////////////////////////////////////// + +class mod_pkts(gr.hier_block): + """ + Wrap an arbitrary digital modulator in our packet handling framework. + + Send packets by calling send_pkt + """ + def __init__(self, fg, modulator, access_code=None, msgq_limit=2, pad_for_usrp=True): + """ + Hierarchical block for sending packets + + Packets to be sent are enqueued by calling send_pkt. + The output is the complex modulated signal at baseband. + + @param fg: flow graph + @type fg: flow graph + @param modulator: instance of modulator class (gr_block or hier_block) + @type modulator: complex baseband out + @param access_code: AKA sync vector + @type access_code: string of 1's and 0's between 1 and 64 long + @param msgq_limit: maximum number of messages in message queue + @type msgq_limit: int + @param pad_for_usrp: If true, packets are padded such that they end up a multiple of 128 samples + + See gmsk_mod for remaining parameters + """ + self._modulator = modulator + self._pad_for_usrp = pad_for_usrp + + if access_code is None: + access_code = packet_utils.default_access_code + if not packet_utils.is_1_0_string(access_code): + raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,) + self._access_code = access_code + + # accepts messages from the outside world + self._pkt_input = gr.message_source(gr.sizeof_char, msgq_limit) + fg.connect(self._pkt_input, self._modulator) + gr.hier_block.__init__(self, fg, None, self._modulator) + + def send_pkt(self, payload='', eof=False): + """ + Send the payload. + + @param payload: data to send + @type payload: string + """ + if eof: + msg = gr.message(1) # tell self._pkt_input we're not sending any more packets + else: + # print "original_payload =", string_to_hex_list(payload) + pkt = packet_utils.make_packet(payload, + self._modulator.samples_per_baud(), + self._modulator.bits_per_baud(), + self._access_code, + self._pad_for_usrp) + #print "pkt =", string_to_hex_list(pkt) + msg = gr.message_from_string(pkt) + self._pkt_input.msgq().insert_tail(msg) + + + +class demod_pkts(gr.hier_block): + """ + Wrap an arbitrary digital demodulator in our packet handling framework. + + The input is complex baseband. When packets are demodulated, they are passed to the + app via the callback. + """ + + def __init__(self, fg, demodulator, access_code=None, callback=None, threshold=-1): + """ + Hierarchical block for demodulating and deframing packets. + + The input is the complex modulated signal at baseband. + Demodulated packets are sent to the handler. + + @param fg: flow graph + @type fg: flow graph + @param demodulator: instance of demodulator class (gr_block or hier_block) + @type demodulator: complex baseband in + @param access_code: AKA sync vector + @type access_code: string of 1's and 0's + @param callback: function of two args: ok, payload + @type callback: ok: bool; payload: string + @param threshold: detect access_code with up to threshold bits wrong (-1 -> use default) + @type threshold: int + """ + + self._demodulator = demodulator + if access_code is None: + access_code = packet_utils.default_access_code + if not packet_utils.is_1_0_string(access_code): + raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,) + self._access_code = access_code + + if threshold == -1: + threshold = 12 # FIXME raise exception + + self._rcvd_pktq = gr.msg_queue() # holds packets from the PHY + self.correlator = gr.correlate_access_code_bb(access_code, threshold) + + self.framer_sink = gr.framer_sink_1(self._rcvd_pktq) + fg.connect(self._demodulator, self.correlator, self.framer_sink) + + gr.hier_block.__init__(self, fg, self._demodulator, None) + self._watcher = _queue_watcher_thread(self._rcvd_pktq, callback) + + +class _queue_watcher_thread(_threading.Thread): + def __init__(self, rcvd_pktq, callback): + _threading.Thread.__init__(self) + self.setDaemon(1) + self.rcvd_pktq = rcvd_pktq + self.callback = callback + self.keep_running = True + self.start() + + + def run(self): + while self.keep_running: + msg = self.rcvd_pktq.delete_head() + ok, payload = packet_utils.unmake_packet(msg.to_string()) + if self.callback: + self.callback(ok, payload) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/rational_resampler.py b/gnuradio-core/src/python/gnuradio/blksimpl/rational_resampler.py new file mode 100644 index 000000000..8b928b102 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/rational_resampler.py @@ -0,0 +1,137 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gru + +_plot = None + +def design_filter(interpolation, decimation, fractional_bw): + """ + Given the interpolation rate, decimation rate and a fractional bandwidth, + design a set of taps. + + @param interpolation: interpolation factor + @type interpolation: integer > 0 + @param decimation: decimation factor + @type decimation: integer > 0 + @param fractional_bw: fractional bandwidth in (0, 0.5) 0.4 works well. + @type fractional_bw: float + @returns: sequence of numbers + """ + + global _plot + + if fractional_bw >= 0.5 or fractional_bw <= 0: + raise ValueError, "Invalid fractional_bandwidth, must be in (0, 0.5)" + + beta = 5.0 + trans_width = 0.5 - fractional_bw + mid_transition_band = 0.5 - trans_width/2 + + taps = gr.firdes.low_pass(interpolation, # gain + 1, # Fs + mid_transition_band/interpolation, # trans mid point + trans_width/interpolation, # transition width + gr.firdes.WIN_KAISER, + beta # beta + ) + # print "len(resampler_taps) =", len(taps) + # _plot = gru.gnuplot_freqz(gru.freqz(taps, 1), 1) + + return taps + + + +class _rational_resampler_base(gr.hier_block): + """ + base class for all rational resampler variants. + """ + def __init__(self, resampler_base, fg, + interpolation, decimation, taps=None, fractional_bw=None): + """ + Rational resampling polyphase FIR filter. + + Either taps or fractional_bw may be specified, but not both. + If neither is specified, a reasonable default, 0.4, is used as + the fractional_bw. + + @param fg: flow graph + @param interpolation: interpolation factor + @type interpolation: integer > 0 + @param decimation: decimation factor + @type decimation: integer > 0 + @param taps: optional filter coefficients + @type taps: sequence + @param fractional_bw: fractional bandwidth in (0, 0.5), measured at final freq (use 0.4) + @type fractional_bw: float + """ + + if not isinstance(interpolation, int) or interpolation < 1: + raise ValueError, "interpolation must be an integer >= 1" + + if not isinstance(decimation, int) or decimation < 1: + raise ValueError, "decimation must be an integer >= 1" + + if taps is None and fractional_bw is None: + fractional_bw = 0.4 + + d = gru.gcd(interpolation, decimation) + interpolation = interpolation // d + decimation = decimation // d + + if taps is None: + taps = design_filter(interpolation, decimation, fractional_bw) + + resampler = resampler_base(interpolation, decimation, taps) + gr.hier_block.__init__(self, fg, resampler, resampler) + + + +class rational_resampler_fff(_rational_resampler_base): + def __init__(self, fg, interpolation, decimation, taps=None, fractional_bw=None): + """ + Rational resampling polyphase FIR filter with + float input, float output and float taps. + """ + _rational_resampler_base.__init__(self, gr.rational_resampler_base_fff, fg, + interpolation, decimation, + taps, fractional_bw) + +class rational_resampler_ccf(_rational_resampler_base): + def __init__(self, fg, interpolation, decimation, taps=None, fractional_bw=None): + """ + Rational resampling polyphase FIR filter with + complex input, complex output and float taps. + """ + _rational_resampler_base.__init__(self, gr.rational_resampler_base_ccf, fg, + interpolation, decimation, + taps, fractional_bw) + +class rational_resampler_ccc(_rational_resampler_base): + def __init__(self, fg, interpolation, decimation, taps=None, fractional_bw=None): + """ + Rational resampling polyphase FIR filter with + complex input, complex output and complex taps. + """ + _rational_resampler_base.__init__(self, gr.rational_resampler_base_ccc, fg, + interpolation, decimation, + taps, fractional_bw) + diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/standard_squelch.py b/gnuradio-core/src/python/gnuradio/blksimpl/standard_squelch.py new file mode 100644 index 000000000..2c80dd5af --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/standard_squelch.py @@ -0,0 +1,73 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +import math +from gnuradio import gr, optfir + +class standard_squelch(gr.hier_block): + def __init__(self, fg, audio_rate): + + self.input_node = gr.add_const_ff(0) # FIXME kludge + + self.low_iir = gr.iir_filter_ffd((0.0193,0,-0.0193),(1,1.9524,-0.9615)) + self.low_square = gr.multiply_ff() + self.low_smooth = gr.single_pole_iir_filter_ff(1/(0.01*audio_rate)) # 100ms time constant + + self.hi_iir = gr.iir_filter_ffd((0.0193,0,-0.0193),(1,1.3597,-0.9615)) + self.hi_square = gr.multiply_ff() + self.hi_smooth = gr.single_pole_iir_filter_ff(1/(0.01*audio_rate)) + + self.sub = gr.sub_ff(); + self.add = gr.add_ff(); + self.gate = gr.threshold_ff(0.3,0.43,0) + self.squelch_lpf = gr.single_pole_iir_filter_ff(1/(0.01*audio_rate)) + + self.div = gr.divide_ff() + self.squelch_mult = gr.multiply_ff() + + fg.connect (self.input_node, (self.squelch_mult, 0)) + + fg.connect (self.input_node,self.low_iir) + fg.connect (self.low_iir,(self.low_square,0)) + fg.connect (self.low_iir,(self.low_square,1)) + fg.connect (self.low_square,self.low_smooth,(self.sub,0)) + fg.connect (self.low_smooth, (self.add,0)) + + fg.connect (self.input_node,self.hi_iir) + fg.connect (self.hi_iir,(self.hi_square,0)) + fg.connect (self.hi_iir,(self.hi_square,1)) + fg.connect (self.hi_square,self.hi_smooth,(self.sub,1)) + fg.connect (self.hi_smooth, (self.add,1)) + + fg.connect (self.sub, (self.div, 0)) + fg.connect (self.add, (self.div, 1)) + fg.connect (self.div, self.gate, self.squelch_lpf, (self.squelch_mult,1)) + + gr.hier_block.__init__(self, fg, self.input_node, self.squelch_mult) + + def set_threshold(self, threshold): + self.gate.set_hi(threshold) + + def threshold(self): + return self.gate.hi() + + def squelch_range(self): + return (0.0, 1.0, 1.0/100) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/wfm_rcv.py b/gnuradio-core/src/python/gnuradio/blksimpl/wfm_rcv.py new file mode 100644 index 000000000..55dbbaa0c --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/wfm_rcv.py @@ -0,0 +1,72 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr +from gnuradio.blksimpl.fm_emph import fm_deemph +import math + +class wfm_rcv(gr.hier_block): + def __init__ (self, fg, quad_rate, audio_decimation): + """ + Hierarchical block for demodulating a broadcast FM signal. + + The input is the downconverted complex baseband signal (gr_complex). + The output is the demodulated audio (float). + + @param fg: flow graph. + @type fg: flow graph + @param quad_rate: input sample rate of complex baseband input. + @type quad_rate: float + @param audio_decimation: how much to decimate quad_rate to get to audio. + @type audio_decimation: integer + """ + volume = 20. + + max_dev = 75e3 + fm_demod_gain = quad_rate/(2*math.pi*max_dev) + audio_rate = quad_rate / audio_decimation + + + # We assign to self so that outsiders can grab the demodulator + # if they need to. E.g., to plot its output. + # + # input: complex; output: float + self.fm_demod = gr.quadrature_demod_cf (fm_demod_gain) + + # input: float; output: float + self.deemph = fm_deemph (fg, audio_rate) + + # compute FIR filter taps for audio filter + width_of_transition_band = audio_rate / 32 + audio_coeffs = gr.firdes.low_pass (1.0, # gain + quad_rate, # sampling rate + audio_rate/2 - width_of_transition_band, + width_of_transition_band, + gr.firdes.WIN_HAMMING) + # input: float; output: float + self.audio_filter = gr.fir_filter_fff (audio_decimation, audio_coeffs) + + fg.connect (self.fm_demod, self.audio_filter, self.deemph) + + gr.hier_block.__init__(self, + fg, + self.fm_demod, # head of the pipeline + self.deemph) # tail of the pipeline diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/wfm_rcv_pll.py b/gnuradio-core/src/python/gnuradio/blksimpl/wfm_rcv_pll.py new file mode 100644 index 000000000..d116090e0 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/wfm_rcv_pll.py @@ -0,0 +1,206 @@ +# +# Copyright 2005,2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr +from gnuradio.blksimpl.fm_emph import fm_deemph +import math + +class wfm_rcv_pll(gr.hier_block): + def __init__ (self, fg, demod_rate, audio_decimation): + """ + Hierarchical block for demodulating a broadcast FM signal. + + The input is the downconverted complex baseband signal (gr_complex). + The output is two streams of the demodulated audio (float) 0=Left, 1=Right. + + @param fg: flow graph. + @type fg: flow graph + @param demod_rate: input sample rate of complex baseband input. + @type demod_rate: float + @param audio_decimation: how much to decimate demod_rate to get to audio. + @type audio_decimation: integer + """ + + bandwidth = 200e3 + audio_rate = demod_rate / audio_decimation + + + # We assign to self so that outsiders can grab the demodulator + # if they need to. E.g., to plot its output. + # + # input: complex; output: float + alpha = 0.25*bandwidth * math.pi / demod_rate + beta = alpha * alpha / 4.0 + max_freq = 2.0*math.pi*100e3/demod_rate + + self.fm_demod = gr.pll_freqdet_cf (alpha,beta,max_freq,-max_freq) + + # input: float; output: float + self.deemph_Left = fm_deemph (fg, audio_rate) + self.deemph_Right = fm_deemph (fg, audio_rate) + + # compute FIR filter taps for audio filter + width_of_transition_band = audio_rate / 32 + audio_coeffs = gr.firdes.low_pass (1.0 , # gain + demod_rate, # sampling rate + 15000 , + width_of_transition_band, + gr.firdes.WIN_HAMMING) + # input: float; output: float + self.audio_filter = gr.fir_filter_fff (audio_decimation, audio_coeffs) + if 1: + # Pick off the stereo carrier/2 with this filter. It attenuated 10 dB so apply 10 dB gain + # We pick off the negative frequency half because we want to base band by it! + ## NOTE THIS WAS HACKED TO OFFSET INSERTION LOSS DUE TO DEEMPHASIS + + stereo_carrier_filter_coeffs = gr.firdes.complex_band_pass(10.0, + demod_rate, + -19020, + -18980, + width_of_transition_band, + gr.firdes.WIN_HAMMING) + + #print "len stereo carrier filter = ",len(stereo_carrier_filter_coeffs) + #print "stereo carrier filter ", stereo_carrier_filter_coeffs + #print "width of transition band = ",width_of_transition_band, " audio rate = ", audio_rate + + # Pick off the double side band suppressed carrier Left-Right audio. It is attenuated 10 dB so apply 10 dB gain + + stereo_dsbsc_filter_coeffs = gr.firdes.complex_band_pass(20.0, + demod_rate, + 38000-15000/2, + 38000+15000/2, + width_of_transition_band, + gr.firdes.WIN_HAMMING) + #print "len stereo dsbsc filter = ",len(stereo_dsbsc_filter_coeffs) + #print "stereo dsbsc filter ", stereo_dsbsc_filter_coeffs + # construct overlap add filter system from coefficients for stereo carrier + + self.stereo_carrier_filter = gr.fir_filter_fcc(audio_decimation, stereo_carrier_filter_coeffs) + + # carrier is twice the picked off carrier so arrange to do a commplex multiply + + self.stereo_carrier_generator = gr.multiply_cc(); + + # Pick off the rds signal + + stereo_rds_filter_coeffs = gr.firdes.complex_band_pass(30.0, + demod_rate, + 57000 - 1500, + 57000 + 1500, + width_of_transition_band, + gr.firdes.WIN_HAMMING) + #print "len stereo dsbsc filter = ",len(stereo_dsbsc_filter_coeffs) + #print "stereo dsbsc filter ", stereo_dsbsc_filter_coeffs + # construct overlap add filter system from coefficients for stereo carrier + + self.stereo_carrier_filter = gr.fir_filter_fcc(audio_decimation, stereo_carrier_filter_coeffs) + self.rds_signal_filter = gr.fir_filter_fcc(audio_decimation, stereo_rds_filter_coeffs) + + + + + + + self.rds_carrier_generator = gr.multiply_cc(); + self.rds_signal_generator = gr.multiply_cc(); + self_rds_signal_processor = gr.null_sink(gr.sizeof_gr_complex); + + + + alpha = 5 * 0.25 * math.pi / (audio_rate) + beta = alpha * alpha / 4.0 + max_freq = -2.0*math.pi*18990/audio_rate; + min_freq = -2.0*math.pi*19010/audio_rate; + + self.stereo_carrier_pll_recovery = gr.pll_carriertracking_cc(alpha,beta,max_freq,min_freq); + self.stereo_carrier_pll_recovery.squelch_enable(False); + + + # set up mixer (multiplier) to get the L-R signal at baseband + + self.stereo_basebander = gr.multiply_cc(); + + # pick off the real component of the basebanded L-R signal. The imaginary SHOULD be zero + + self.LmR_real = gr.complex_to_real(); + self.Make_Left = gr.add_ff(); + self.Make_Right = gr.sub_ff(); + + self.stereo_dsbsc_filter = gr.fir_filter_fcc(audio_decimation, stereo_dsbsc_filter_coeffs) + + + if 1: + + # send the real signal to complex filter to pick off the carrier and then to one side of a multiplier + fg.connect (self.fm_demod,self.stereo_carrier_filter,self.stereo_carrier_pll_recovery, (self.stereo_carrier_generator,0)) + # send the already filtered carrier to the otherside of the carrier + fg.connect (self.stereo_carrier_pll_recovery, (self.stereo_carrier_generator,1)) + # the resulting signal from this multiplier is the carrier with correct phase but at -38000 Hz. + + # send the new carrier to one side of the mixer (multiplier) + fg.connect (self.stereo_carrier_generator, (self.stereo_basebander,0)) + # send the demphasized audio to the DSBSC pick off filter, the complex + # DSBSC signal at +38000 Hz is sent to the other side of the mixer/multiplier + fg.connect (self.fm_demod,self.stereo_dsbsc_filter, (self.stereo_basebander,1)) + # the result is BASEBANDED DSBSC with phase zero! + + # Pick off the real part since the imaginary is theoretically zero and then to one side of a summer + fg.connect (self.stereo_basebander, self.LmR_real, (self.Make_Left,0)) + #take the same real part of the DSBSC baseband signal and send it to negative side of a subtracter + fg.connect (self.LmR_real,(self.Make_Right,1)) + + # Make rds carrier by taking the squared pilot tone and multiplying by pilot tone + fg.connect (self.stereo_basebander,(self.rds_carrier_generator,0)) + fg.connect (self.stereo_carrier_pll_recovery,(self.rds_carrier_generator,1)) + # take signal, filter off rds, send into mixer 0 channel + fg.connect (self.fm_demod,self.rds_signal_filter,(self.rds_signal_generator,0)) + # take rds_carrier_generator output and send into mixer 1 channel + fg.connect (self.rds_carrier_generator,(self.rds_signal_generator,1)) + # send basebanded rds signal and send into "processor" which for now is a null sink + fg.connect (self.rds_signal_generator,self_rds_signal_processor) + + + if 1: + # pick off the audio, L+R that is what we used to have and send it to the summer + fg.connect(self.fm_demod, self.audio_filter, (self.Make_Left, 1)) + # take the picked off L+R audio and send it to the PLUS side of the subtractor + fg.connect(self.audio_filter,(self.Make_Right, 0)) + # The result of Make_Left gets (L+R) + (L-R) and results in 2*L + # The result of Make_Right gets (L+R) - (L-R) and results in 2*R + + + # kludge the signals into a stereo channel + kludge = gr.kludge_copy(gr.sizeof_float) + fg.connect(self.Make_Left , self.deemph_Left, (kludge, 0)) + fg.connect(self.Make_Right, self.deemph_Right, (kludge, 1)) + + #send it to the audio system + gr.hier_block.__init__(self, + fg, + self.fm_demod, # head of the pipeline + kludge) # tail of the pipeline + else: + fg.connect (self.fm_demod, self.audio_filter) + gr.hier_block.__init__(self, + fg, + self.fm_demod, # head of the pipeline + self.audio_filter) # tail of the pipeline diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/wfm_tx.py b/gnuradio-core/src/python/gnuradio/blksimpl/wfm_tx.py new file mode 100644 index 000000000..505455571 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/wfm_tx.py @@ -0,0 +1,79 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +import math +from gnuradio import gr, optfir +from gnuradio.blksimpl.fm_emph import fm_preemph + +class wfm_tx(gr.hier_block): + def __init__(self, fg, audio_rate, quad_rate, tau=75e-6, max_dev=75e3): + """ + Wide Band FM Transmitter. + + Takes a single float input stream of audio samples in the range [-1,+1] + and produces a single FM modulated complex baseband output. + + @param fg: flow graph + @param audio_rate: sample rate of audio stream, >= 16k + @type audio_rate: integer + @param quad_rate: sample rate of output stream + @type quad_rate: integer + @param tau: preemphasis time constant (default 75e-6) + @type tau: float + @param max_dev: maximum deviation in Hz (default 75e3) + @type max_dev: float + + quad_rate must be an integer multiple of audio_rate. + """ + + # FIXME audio_rate and quad_rate ought to be exact rationals + audio_rate = int(audio_rate) + quad_rate = int(quad_rate) + + if quad_rate % audio_rate != 0: + raise ValueError, "quad_rate is not an integer multiple of audio_rate" + + + do_interp = audio_rate != quad_rate + + if do_interp: + interp_factor = quad_rate / audio_rate + interp_taps = optfir.low_pass (interp_factor, # gain + quad_rate, # Fs + 16000, # passband cutoff + 18000, # stopband cutoff + 0.1, # passband ripple dB + 40) # stopband atten dB + + print "len(interp_taps) =", len(interp_taps) + self.interpolator = gr.interp_fir_filter_fff (interp_factor, interp_taps) + + self.preemph = fm_preemph (fg, quad_rate, tau=tau) + + k = 2 * math.pi * max_dev / quad_rate + self.modulator = gr.frequency_modulator_fc (k) + + if do_interp: + fg.connect (self.interpolator, self.preemph, self.modulator) + gr.hier_block.__init__(self, fg, self.interpolator, self.modulator) + else: + fg.connect(self.preemph, self.modulator) + gr.hier_block.__init__(self, fg, self.preemph, self.modulator) diff --git a/gnuradio-core/src/python/gnuradio/eng_notation.py b/gnuradio-core/src/python/gnuradio/eng_notation.py new file mode 100644 index 000000000..72cd8931e --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/eng_notation.py @@ -0,0 +1,71 @@ +# +# Copyright 2003 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +scale_factor = {} +scale_factor['E'] = 1e18 +scale_factor['P'] = 1e15 +scale_factor['T'] = 1e12 +scale_factor['G'] = 1e9 +scale_factor['M'] = 1e6 +scale_factor['k'] = 1e3 +scale_factor['m'] = 1e-3 +scale_factor['u'] = 1e-6 +scale_factor['n'] = 1e-9 +scale_factor['p'] = 1e-12 +scale_factor['f'] = 1e-15 +scale_factor['a'] = 1e-18 + +def num_to_str (n): + '''Convert a number to a string in engineering notation. E.g., 5e-9 -> 5n''' + m = abs(n) + if m >= 1e9: + return "%gG" % (n * 1e-9) + elif m >= 1e6: + return "%gM" % (n * 1e-6) + elif m >= 1e3: + return "%gk" % (n * 1e-3) + elif m >= 1: + return "%g" % (n) + elif m >= 1e-3: + return "%gm" % (n * 1e3) + elif m >= 1e-6: + return "%gu" % (n * 1e6) # where's that mu when you need it (unicode?) + elif m >= 1e-9: + return "%gn" % (n * 1e9) + elif m >= 1e-12: + return "%gp" % (n * 1e12) + elif m >= 1e-15: + return "%gf" % (n * 1e15) + else: + return "%g" % (n) + + +def str_to_num (value): + '''Convert a string in engineering notation to a number. E.g., '15m' -> 15e-3''' + try: + scale = 1.0 + suffix = value[-1] + if scale_factor.has_key (suffix): + return float (value[0:-1]) * scale_factor[suffix] + return float (value) + except: + raise RuntimeError ( + "Invalid engineering notation value: %r" % (value,)) diff --git a/gnuradio-core/src/python/gnuradio/eng_option.py b/gnuradio-core/src/python/gnuradio/eng_option.py new file mode 100644 index 000000000..3e25c5788 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/eng_option.py @@ -0,0 +1,80 @@ +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +'''Add support for engineering notation to optparse.OptionParser''' + +from copy import copy +from optparse import Option, OptionValueError + +scale_factor = {} +scale_factor['E'] = 1e18 +scale_factor['P'] = 1e15 +scale_factor['T'] = 1e12 +scale_factor['G'] = 1e9 +scale_factor['M'] = 1e6 +scale_factor['k'] = 1e3 +scale_factor['m'] = 1e-3 +scale_factor['u'] = 1e-6 +scale_factor['n'] = 1e-9 +scale_factor['p'] = 1e-12 +scale_factor['f'] = 1e-15 +scale_factor['a'] = 1e-18 + + +def check_eng_float (option, opt, value): + try: + scale = 1.0 + suffix = value[-1] + if scale_factor.has_key (suffix): + return float (value[0:-1]) * scale_factor[suffix] + return float (value) + except: + raise OptionValueError ( + "option %s: invalid engineering notation value: %r" % (opt, value)) + +def check_intx (option, opt, value): + try: + return int (value, 0) + except: + raise OptionValueError ( + "option %s: invalid integer value: %r" % (opt, value)) + +def check_subdev (option, opt, value): + """ + Value has the form: (A|B)(:0|1)? + + @returns a 2-tuple (0|1, 0|1) + """ + d = { 'A' : (0, 0), 'A:0' : (0, 0), 'A:1' : (0, 1), + 'B' : (1, 0), 'B:0' : (1, 0), 'B:1' : (1, 1) } + try: + return d[value.upper()] + except: + raise OptionValueError( + "option %s: invalid subdev: '%r', must be one of A, B, A:0, A:1, B:0, B:1" % (opt, value)) + +class eng_option (Option): + TYPES = Option.TYPES + ("eng_float", "intx", "subdev") + TYPE_CHECKER = copy (Option.TYPE_CHECKER) + TYPE_CHECKER["eng_float"] = check_eng_float + TYPE_CHECKER["intx"] = check_intx + TYPE_CHECKER["subdev"] = check_subdev + diff --git a/gnuradio-core/src/python/gnuradio/gr/Makefile.am b/gnuradio-core/src/python/gnuradio/gr/Makefile.am new file mode 100644 index 000000000..05dee29f0 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/Makefile.am @@ -0,0 +1,77 @@ +# +# Copyright 2004,2005,2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +include $(top_srcdir)/Makefile.common + +EXTRA_DIST = run_tests.in + + +TESTS = \ + run_tests + + +grgrpythondir = $(grpythondir)/gr + +grgrpython_PYTHON = \ + __init__.py \ + basic_flow_graph.py \ + exceptions.py \ + flow_graph.py \ + gr_threading.py \ + gr_threading_23.py \ + gr_threading_24.py \ + hier_block.py \ + prefs.py \ + scheduler.py + +noinst_PYTHON = \ + qa_add_and_friends.py \ + qa_basic_flow_graph.py \ + qa_complex_to_xxx.py \ + qa_correlate_access_code.py \ + qa_diff_encoder.py \ + qa_diff_phasor_cc.py \ + qa_feval.py \ + qa_fft_filter.py \ + qa_filter_delay_fc.py \ + qa_flow_graph.py \ + qa_frequency_modulator.py \ + qa_fsk_stuff.py \ + qa_head.py \ + qa_hilbert.py \ + qa_iir.py \ + qa_interleave.py \ + qa_interp_fir_filter.py \ + qa_kludge_copy.py \ + qa_kludged_imports.py \ + qa_message.py \ + qa_mute.py \ + qa_nlog10.py \ + qa_packed_to_unpacked.py \ + qa_pipe_fittings.py \ + qa_rational_resampler.py \ + qa_sig_source.py \ + qa_single_pole_iir.py \ + qa_single_pole_iir_cc.py \ + qa_unpack_k_bits.py + + +CLEANFILES = *.pyc diff --git a/gnuradio-core/src/python/gnuradio/gr/__init__.py b/gnuradio-core/src/python/gnuradio/gr/__init__.py new file mode 100644 index 000000000..5583c412a --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/__init__.py @@ -0,0 +1,40 @@ +# +# Copyright 2003,2004,2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +# The presence of this file turns this directory into a Python package + +# This is the main GNU Radio python module. +# We pull the swig output and the other modules into the gnuradio.gr namespace + +from gnuradio_swig_python import * +from basic_flow_graph import * +from flow_graph import * +from exceptions import * +from hier_block import * + + +# create a couple of aliases +serial_to_parallel = stream_to_vector +parallel_to_serial = vector_to_stream + +# Force the preference database to be initialized +from prefs import prefs + diff --git a/gnuradio-core/src/python/gnuradio/gr/basic_flow_graph.py b/gnuradio-core/src/python/gnuradio/gr/basic_flow_graph.py new file mode 100644 index 000000000..1afc96298 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/basic_flow_graph.py @@ -0,0 +1,267 @@ +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio_swig_python import gr_block_sptr +import types +import hier_block + +def remove_duplicates (seq): + new = [] + for x in seq: + if not x in new: + new.append (x) + return new + + +class endpoint (object): + __slots__ = ['block', 'port'] + def __init__ (self, block, port): + self.block = block + self.port = port + + def __cmp__ (self, other): + if self.block == other.block and self.port == other.port: + return 0 + return 1 + + def __str__ (self): + return '<endpoint (%s, %s)>' % (self.block, self.port) + +def expand_src_endpoint (src_endpoint): + # A src_endpoint is an output of a block + src_endpoint = coerce_endpoint (src_endpoint) + if isinstance (src_endpoint.block, hier_block.hier_block_base): + return expand_src_endpoint ( + coerce_endpoint (src_endpoint.block.resolve_output_port(src_endpoint.port))) + else: + return src_endpoint + +def expand_dst_endpoint (dst_endpoint): + # a dst_endpoint is the input to a block + dst_endpoint = coerce_endpoint (dst_endpoint) + if isinstance (dst_endpoint.block, hier_block.hier_block_base): + exp = [coerce_endpoint(x) for x in + dst_endpoint.block.resolve_input_port(dst_endpoint.port)] + return expand_dst_endpoints (exp) + else: + return [dst_endpoint] + +def expand_dst_endpoints (endpoint_list): + r = [] + for e in endpoint_list: + r.extend (expand_dst_endpoint (e)) + return r + + +def coerce_endpoint (x): + if isinstance (x, endpoint): + return x + elif isinstance (x, types.TupleType) and len (x) == 2: + return endpoint (x[0], x[1]) + elif hasattr (x, 'block'): # assume it's a block + return endpoint (x, 0) + elif isinstance(x, hier_block.hier_block_base): + return endpoint (x, 0) + else: + raise ValueError, "Not coercible to endpoint: %s" % (x,) + + +class edge (object): + __slots__ = ['src', 'dst'] + def __init__ (self, src_endpoint, dst_endpoint): + self.src = src_endpoint + self.dst = dst_endpoint + + def __cmp__ (self, other): + if self.src == other.src and self.dst == other.dst: + return 0 + return 1 + + def __repr__ (self): + return '<edge (%s, %s)>' % (self.src, self.dst) + +class basic_flow_graph (object): + '''basic_flow_graph -- describe connections between blocks''' + # __slots__ is incompatible with weakrefs (for some reason!) + # __slots__ = ['edge_list'] + def __init__ (self): + self.edge_list = [] + + def connect (self, *points): + '''connect requires two or more arguments that can be coerced to endpoints. + If more than two arguments are provided, they are connected together successively. + ''' + if len (points) < 2: + raise ValueError, ("connect requires at least two endpoints; %d provided." % (len (points),)) + for i in range (1, len (points)): + self._connect (points[i-1], points[i]) + + def _connect (self, src_endpoint, dst_endpoint): + s = expand_src_endpoint (src_endpoint) + for d in expand_dst_endpoint (dst_endpoint): + self._connect_prim (s, d) + + def _connect_prim (self, src_endpoint, dst_endpoint): + src_endpoint = coerce_endpoint (src_endpoint) + dst_endpoint = coerce_endpoint (dst_endpoint) + self._check_valid_src_port (src_endpoint) + self._check_valid_dst_port (dst_endpoint) + self._check_dst_in_use (dst_endpoint) + self._check_type_match (src_endpoint, dst_endpoint) + self.edge_list.append (edge (src_endpoint, dst_endpoint)) + + def disconnect (self, src_endpoint, dst_endpoint): + s = expand_src_endpoint (src_endpoint) + for d in expand_dst_endpoint (dst_endpoint): + self._disconnect_prim (s, d) + + def _disconnect_prim (self, src_endpoint, dst_endpoint): + src_endpoint = coerce_endpoint (src_endpoint) + dst_endpoint = coerce_endpoint (dst_endpoint) + e = edge (src_endpoint, dst_endpoint) + self.edge_list.remove (e) + + def disconnect_all (self): + self.edge_list = [] + + def validate (self): + # check all blocks to ensure: + # (1a) their input ports are contiguously assigned + # (1b) the number of input ports is between min and max + # (2a) their output ports are contiguously assigned + # (2b) the number of output ports is between min and max + # (3) check_topology returns true + + for m in self.all_blocks (): + # print m + + edges = self.in_edges (m) + used_ports = [e.dst.port for e in edges] + ninputs = self._check_contiguity (m, m.input_signature (), used_ports, "input") + + edges = self.out_edges (m) + used_ports = [e.src.port for e in edges] + noutputs = self._check_contiguity (m, m.output_signature (), used_ports, "output") + + if not m.check_topology (ninputs, noutputs): + raise ValueError, ("%s::check_topology (%d, %d) failed" % (m, ninputs, noutputs)) + + + # --- public utilities --- + + def all_blocks (self): + '''return list of all blocks in the graph''' + all_blocks = [] + for edge in self.edge_list: + m = edge.src.block + if not m in all_blocks: + all_blocks.append (m) + m = edge.dst.block + if not m in all_blocks: + all_blocks.append (m) + return all_blocks + + def in_edges (self, m): + '''return list of all edges that have M as a destination''' + return [e for e in self.edge_list if e.dst.block == m] + + def out_edges (self, m): + '''return list of all edges that have M as a source''' + return [e for e in self.edge_list if e.src.block == m] + + def downstream_verticies (self, m): + return [e.dst.block for e in self.out_edges (m)] + + def downstream_verticies_port (self, m, port): + return [e.dst.block for e in self.out_edges(m) if e.src.port == port] + + def upstream_verticies (self, m): + return [e.src.block for e in self.in_edges (m)] + + def adjacent_verticies (self, m): + '''return list of all verticies adjacent to M''' + return self.downstream_verticies (m) + self.upstream_verticies (m) + + def sink_p (self, m): + '''return True iff this block is a sink''' + e = self.out_edges (m) + return len (e) == 0 + + def source_p (self, m): + '''return True iff this block is a source''' + e = self.in_edges (m) + return len (e) == 0 + + # --- internal methods --- + + def _check_dst_in_use (self, dst_endpoint): + '''Ensure that there is not already an endpoint that terminates at dst_endpoint.''' + x = [ep for ep in self.edge_list if ep.dst == dst_endpoint] + if x: # already in use + raise ValueError, ("destination endpoint already in use: %s" % (dst_endpoint)) + + def _check_valid_src_port (self, src_endpoint): + self._check_port (src_endpoint.block.output_signature(), src_endpoint.port) + + def _check_valid_dst_port (self, dst_endpoint): + self._check_port (dst_endpoint.block.input_signature(), dst_endpoint.port) + + def _check_port (self, signature, port): + if port < 0: + raise ValueError, 'port number out of range.' + if signature.max_streams () == -1: # infinite + return # OK + if port >= signature.max_streams (): + raise ValueError, 'port number out of range.' + + def _check_type_match (self, src_endpoint, dst_endpoint): + # for now, we just ensure that the stream item sizes match + src_sig = src_endpoint.block.output_signature () + dst_sig = dst_endpoint.block.input_signature () + src_size = src_sig.sizeof_stream_item (src_endpoint.port) + dst_size = dst_sig.sizeof_stream_item (dst_endpoint.port) + if src_size != dst_size: + raise ValueError, 'source and destination data sizes are different' + + def _check_contiguity (self, m, sig, used_ports, dir): + used_ports.sort () + used_ports = remove_duplicates (used_ports) + min_s = sig.min_streams () + + l = len (used_ports) + if l == 0: + if min_s == 0: + return l + raise ValueError, ("%s requires %d %s connections. It has none." % + (m, min_s, dir)) + + if used_ports[-1] + 1 < min_s: + raise ValueError, ("%s requires %d %s connections. It has %d." % + (m, min_s, dir, used_ports[-1] + 1)) + + if used_ports[-1] + 1 != l: + for i in range (l): + if used_ports[i] != i: + raise ValueError, ("%s %s port %d is not connected" % + (m, dir, i)) + + # print "%s ports: %s" % (dir, used_ports) + return l diff --git a/gnuradio-core/src/python/gnuradio/gr/benchmark_filters.py b/gnuradio-core/src/python/gnuradio/gr/benchmark_filters.py new file mode 100755 index 000000000..7b2f44c44 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/benchmark_filters.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python + +import time +import random +from optparse import OptionParser +from gnuradio import gr +from gnuradio.eng_option import eng_option + +def make_random_complex_tuple(L): + result = [] + for x in range(L): + result.append(complex(random.uniform(-1000,1000), + random.uniform(-1000,1000))) + return tuple(result) + +def benchmark(name, creator, dec, ntaps, total_test_size, block_size): + block_size = 32768 + + fg = gr.flow_graph() + taps = make_random_complex_tuple(ntaps) + src = gr.vector_source_c(make_random_complex_tuple(block_size), True) + head = gr.head(gr.sizeof_gr_complex, int(total_test_size)) + op = creator(dec, taps) + dst = gr.null_sink(gr.sizeof_gr_complex) + fg.connect(src, head, op, dst) + start = time.time() + fg.run() + stop = time.time() + delta = stop - start + print "%16s: taps: %4d input: %4g, time: %6.3f taps/sec: %10.4g" % ( + name, ntaps, total_test_size, delta, ntaps*total_test_size/delta) + +def main(): + parser = OptionParser(option_class=eng_option) + parser.add_option("-n", "--ntaps", type="int", default=256) + parser.add_option("-t", "--total-input-size", type="eng_float", default=40e6) + parser.add_option("-b", "--block-size", type="intx", default=50000) + parser.add_option("-d", "--decimation", type="int", default=1) + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + + ntaps = options.ntaps + total_input_size = options.total_input_size + block_size = options.block_size + dec = options.decimation + + benchmark("gr.fir_filter_ccc", gr.fir_filter_ccc, + dec, ntaps, total_input_size, block_size) + benchmark("gr.fft_filter_ccc", gr.fft_filter_ccc, + dec, ntaps, total_input_size, block_size) + +if __name__ == '__main__': + main() diff --git a/gnuradio-core/src/python/gnuradio/gr/exceptions.py b/gnuradio-core/src/python/gnuradio/gr/exceptions.py new file mode 100644 index 000000000..0cbeb143a --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/exceptions.py @@ -0,0 +1,27 @@ +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +class NotDAG (Exception): + """Not a directed acyclic graph""" + pass + +class CantHappen (Exception): + """Can't happen""" + pass diff --git a/gnuradio-core/src/python/gnuradio/gr/flow_graph.py b/gnuradio-core/src/python/gnuradio/gr/flow_graph.py new file mode 100644 index 000000000..db9c58768 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/flow_graph.py @@ -0,0 +1,234 @@ +# +# Copyright 2004,2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio.gr.basic_flow_graph import remove_duplicates, endpoint, edge, \ + basic_flow_graph + +from gnuradio.gr.exceptions import * +from gnuradio_swig_python import buffer, buffer_add_reader, block_detail, \ + single_threaded_scheduler + +from gnuradio.gr.scheduler import scheduler + +_WHITE = 0 # graph coloring tags +_GRAY = 1 +_BLACK = 2 + +_flow_graph_debug = False + +def set_flow_graph_debug(on): + global _flow_graph_debug + _flow_graph_debug = on + + +class buffer_sizes (object): + """compute buffer sizes to use""" + def __init__ (self, flow_graph): + # We could scan the graph here and determine individual sizes + # based on relative_rate, number of readers, etc. + + # The simplest thing that could possibly work: all buffers same size + self.flow_graph = flow_graph + self.fixed_buffer_size = 32*1024 + + def allocate (self, m, index): + """allocate buffer for output index of block m""" + item_size = m.output_signature().sizeof_stream_item (index) + nitems = self.fixed_buffer_size / item_size + if nitems < 2 * m.output_multiple (): + nitems = 2 * m.output_multiple () + + # if any downstream blocks is a decimator and/or has a large output_multiple, + # ensure that we have a buffer at least 2 * their decimation_factor*output_multiple + for mdown in self.flow_graph.downstream_verticies_port(m, index): + decimation = int(1.0 / mdown.relative_rate()) + nitems = max(nitems, 2 * (decimation * mdown.output_multiple() + mdown.history())) + + return buffer (nitems, item_size) + + +class flow_graph (basic_flow_graph): + """add physical connection info to simple_flow_graph + """ + # __slots__ is incompatible with weakrefs (for some reason!) + # __slots__ = ['blocks', 'scheduler'] + + def __init__ (self): + basic_flow_graph.__init__ (self); + self.blocks = None + self.scheduler = None + + def __del__(self): + # print "\nflow_graph.__del__" + # this ensures that i/o devices such as the USRP get shutdown gracefully + self.stop() + + def start (self): + '''start graph, forking thread(s), return immediately''' + if self.scheduler: + raise RuntimeError, "Scheduler already running" + self._setup_connections () + + # cast down to gr_module_sptr + # t = [x.block () for x in self.topological_sort (self.blocks)] + self.scheduler = scheduler (self) + self.scheduler.start () + + def stop (self): + '''tells scheduler to stop and waits for it to happen''' + if self.scheduler: + self.scheduler.stop () + self.scheduler = None + + def wait (self): + '''waits for scheduler to stop''' + if self.scheduler: + self.scheduler.wait () + self.scheduler = None + + def is_running (self): + return not not self.scheduler + + def run (self): + '''start graph, wait for completion''' + self.start () + self.wait () + + def _setup_connections (self): + """given the basic flow graph, setup all the physical connections""" + self.validate () + self.blocks = self.all_blocks () + self._assign_details () + self._assign_buffers () + self._connect_inputs () + + def _assign_details (self): + for m in self.blocks: + edges = self.in_edges (m) + used_ports = remove_duplicates ([e.dst.port for e in edges]) + ninputs = len (used_ports) + + edges = self.out_edges (m) + used_ports = remove_duplicates ([e.src.port for e in edges]) + noutputs = len (used_ports) + + m.set_detail (block_detail (ninputs, noutputs)) + + def _assign_buffers (self): + """determine the buffer sizes to use, allocate them and attach to detail""" + sizes = buffer_sizes (self) + for m in self.blocks: + d = m.detail () + for index in range (d.noutputs ()): + d.set_output (index, sizes.allocate (m, index)) + + def _connect_inputs (self): + """connect all block inputs to appropriate upstream buffers""" + for m in self.blocks: + d = m.detail () + # print "%r history = %d" % (m, m.history()) + for e in self.in_edges(m): + # FYI, sources don't have any in_edges + our_port = e.dst.port + upstream_block = e.src.block + upstream_port = e.src.port + upstream_buffer = upstream_block.detail().output(upstream_port) + d.set_input(our_port, buffer_add_reader(upstream_buffer, m.history())) + + + def topological_sort (self, all_v): + ''' + Return a topologically sorted list of vertices. + This is basically a depth-first search with checks + for back edges (the non-DAG condition) + + ''' + + # it's correct without this sort, but this + # should give better ordering for cache utilization + all_v = self._sort_sources_first (all_v) + + output = [] + for v in all_v: + v.ts_color = _WHITE + for v in all_v: + if v.ts_color == _WHITE: + self._dfs_visit (v, output) + output.reverse () + return output + + def _dfs_visit (self, u, output): + # print "dfs_visit (enter): ", u + u.ts_color = _GRAY + for v in self.downstream_verticies (u): + if v.ts_color == _WHITE: # (u, v) is a tree edge + self._dfs_visit (v, output) + elif v.ts_color == _GRAY: # (u, v) is a back edge + raise NotDAG, "The graph is not an acyclic graph (It's got a loop)" + elif v.ts_color == _BLACK: # (u, v) is a cross or forward edge + pass + else: + raise CantHappen, "Invalid color on vertex" + u.ts_color = _BLACK + output.append (u) + # print "dfs_visit (exit): ", u, output + + def _sort_sources_first (self, all_v): + # the sort function is not guaranteed to be stable. + # We add the unique_id in to the key so we're guaranteed + # of reproducible results. This is important for the test + # code. There is often more than one valid topological sort. + # We want to force a reproducible choice. + x = [(not self.source_p(v), v.unique_id(), v) for v in all_v] + x.sort () + x = [v[2] for v in x] + # print "sorted: ", x + return x + + def partition_graph (self, all_v): + '''Return a list of lists of nodes that are connected. + The result is a list of disjoint graphs. + The sublists are topologically sorted. + ''' + result = [] + working_v = all_v[:] # make copy + while working_v: + rv = self._reachable_verticies (working_v[0], working_v) + result.append (self.topological_sort (rv)) + for v in rv: + working_v.remove (v) + if _flow_graph_debug: + print "partition_graph:", result + return result + + def _reachable_verticies (self, start, all_v): + for v in all_v: + v.ts_color = _WHITE + + self._reachable_dfs_visit (start) + return [v for v in all_v if v.ts_color == _BLACK] + + def _reachable_dfs_visit (self, u): + u.ts_color = _BLACK + for v in self.adjacent_verticies (u): + if v.ts_color == _WHITE: + self._reachable_dfs_visit (v) + return None diff --git a/gnuradio-core/src/python/gnuradio/gr/gr_threading.py b/gnuradio-core/src/python/gnuradio/gr/gr_threading.py new file mode 100644 index 000000000..6a09a3239 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/gr_threading.py @@ -0,0 +1,35 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from sys import version_info as _version_info + +# import patched version of standard threading module + +if _version_info[0:2] == (2, 3): + #print "Importing gr_threading_23" + from gr_threading_23 import * +elif _version_info[0:2] == (2, 4): + #print "Importing gr_threading_24" + from gr_threading_24 import * +else: + # assume the patch was applied... + #print "Importing system provided threading" + from threading import * diff --git a/gnuradio-core/src/python/gnuradio/gr/gr_threading_23.py b/gnuradio-core/src/python/gnuradio/gr/gr_threading_23.py new file mode 100644 index 000000000..dee8034c1 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/gr_threading_23.py @@ -0,0 +1,724 @@ +"""Thread module emulating a subset of Java's threading model.""" + +# This started life as the threading.py module of Python 2.3 +# It's been patched to fix a problem with join, where a KeyboardInterrupt +# caused a lock to be left in the acquired state. + +import sys as _sys + +try: + import thread +except ImportError: + del _sys.modules[__name__] + raise + +from StringIO import StringIO as _StringIO +from time import time as _time, sleep as _sleep +from traceback import print_exc as _print_exc + +# Rename some stuff so "from threading import *" is safe +__all__ = ['activeCount', 'Condition', 'currentThread', 'enumerate', 'Event', + 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Thread', + 'Timer', 'setprofile', 'settrace'] + +_start_new_thread = thread.start_new_thread +_allocate_lock = thread.allocate_lock +_get_ident = thread.get_ident +ThreadError = thread.error +del thread + + +# Debug support (adapted from ihooks.py). +# All the major classes here derive from _Verbose. We force that to +# be a new-style class so that all the major classes here are new-style. +# This helps debugging (type(instance) is more revealing for instances +# of new-style classes). + +_VERBOSE = False + +if __debug__: + + class _Verbose(object): + + def __init__(self, verbose=None): + if verbose is None: + verbose = _VERBOSE + self.__verbose = verbose + + def _note(self, format, *args): + if self.__verbose: + format = format % args + format = "%s: %s\n" % ( + currentThread().getName(), format) + _sys.stderr.write(format) + +else: + # Disable this when using "python -O" + class _Verbose(object): + def __init__(self, verbose=None): + pass + def _note(self, *args): + pass + +# Support for profile and trace hooks + +_profile_hook = None +_trace_hook = None + +def setprofile(func): + global _profile_hook + _profile_hook = func + +def settrace(func): + global _trace_hook + _trace_hook = func + +# Synchronization classes + +Lock = _allocate_lock + +def RLock(*args, **kwargs): + return _RLock(*args, **kwargs) + +class _RLock(_Verbose): + + def __init__(self, verbose=None): + _Verbose.__init__(self, verbose) + self.__block = _allocate_lock() + self.__owner = None + self.__count = 0 + + def __repr__(self): + return "<%s(%s, %d)>" % ( + self.__class__.__name__, + self.__owner and self.__owner.getName(), + self.__count) + + def acquire(self, blocking=1): + me = currentThread() + if self.__owner is me: + self.__count = self.__count + 1 + if __debug__: + self._note("%s.acquire(%s): recursive success", self, blocking) + return 1 + rc = self.__block.acquire(blocking) + if rc: + self.__owner = me + self.__count = 1 + if __debug__: + self._note("%s.acquire(%s): initial succes", self, blocking) + else: + if __debug__: + self._note("%s.acquire(%s): failure", self, blocking) + return rc + + def release(self): + me = currentThread() + assert self.__owner is me, "release() of un-acquire()d lock" + self.__count = count = self.__count - 1 + if not count: + self.__owner = None + self.__block.release() + if __debug__: + self._note("%s.release(): final release", self) + else: + if __debug__: + self._note("%s.release(): non-final release", self) + + # Internal methods used by condition variables + + def _acquire_restore(self, (count, owner)): + self.__block.acquire() + self.__count = count + self.__owner = owner + if __debug__: + self._note("%s._acquire_restore()", self) + + def _release_save(self): + if __debug__: + self._note("%s._release_save()", self) + count = self.__count + self.__count = 0 + owner = self.__owner + self.__owner = None + self.__block.release() + return (count, owner) + + def _is_owned(self): + return self.__owner is currentThread() + + +def Condition(*args, **kwargs): + return _Condition(*args, **kwargs) + +class _Condition(_Verbose): + + def __init__(self, lock=None, verbose=None): + _Verbose.__init__(self, verbose) + if lock is None: + lock = RLock() + self.__lock = lock + # Export the lock's acquire() and release() methods + self.acquire = lock.acquire + self.release = lock.release + # If the lock defines _release_save() and/or _acquire_restore(), + # these override the default implementations (which just call + # release() and acquire() on the lock). Ditto for _is_owned(). + try: + self._release_save = lock._release_save + except AttributeError: + pass + try: + self._acquire_restore = lock._acquire_restore + except AttributeError: + pass + try: + self._is_owned = lock._is_owned + except AttributeError: + pass + self.__waiters = [] + + def __repr__(self): + return "<Condition(%s, %d)>" % (self.__lock, len(self.__waiters)) + + def _release_save(self): + self.__lock.release() # No state to save + + def _acquire_restore(self, x): + self.__lock.acquire() # Ignore saved state + + def _is_owned(self): + # Return True if lock is owned by currentThread. + # This method is called only if __lock doesn't have _is_owned(). + if self.__lock.acquire(0): + self.__lock.release() + return False + else: + return True + + def wait(self, timeout=None): + currentThread() # for side-effect + assert self._is_owned(), "wait() of un-acquire()d lock" + waiter = _allocate_lock() + waiter.acquire() + self.__waiters.append(waiter) + saved_state = self._release_save() + try: # restore state no matter what (e.g., KeyboardInterrupt) + if timeout is None: + waiter.acquire() + if __debug__: + self._note("%s.wait(): got it", self) + else: + # Balancing act: We can't afford a pure busy loop, so we + # have to sleep; but if we sleep the whole timeout time, + # we'll be unresponsive. The scheme here sleeps very + # little at first, longer as time goes on, but never longer + # than 20 times per second (or the timeout time remaining). + endtime = _time() + timeout + delay = 0.0005 # 500 us -> initial delay of 1 ms + while True: + gotit = waiter.acquire(0) + if gotit: + break + remaining = endtime - _time() + if remaining <= 0: + break + delay = min(delay * 2, remaining, .05) + _sleep(delay) + if not gotit: + if __debug__: + self._note("%s.wait(%s): timed out", self, timeout) + try: + self.__waiters.remove(waiter) + except ValueError: + pass + else: + if __debug__: + self._note("%s.wait(%s): got it", self, timeout) + finally: + self._acquire_restore(saved_state) + + def notify(self, n=1): + currentThread() # for side-effect + assert self._is_owned(), "notify() of un-acquire()d lock" + __waiters = self.__waiters + waiters = __waiters[:n] + if not waiters: + if __debug__: + self._note("%s.notify(): no waiters", self) + return + self._note("%s.notify(): notifying %d waiter%s", self, n, + n!=1 and "s" or "") + for waiter in waiters: + waiter.release() + try: + __waiters.remove(waiter) + except ValueError: + pass + + def notifyAll(self): + self.notify(len(self.__waiters)) + + +def Semaphore(*args, **kwargs): + return _Semaphore(*args, **kwargs) + +class _Semaphore(_Verbose): + + # After Tim Peters' semaphore class, but not quite the same (no maximum) + + def __init__(self, value=1, verbose=None): + assert value >= 0, "Semaphore initial value must be >= 0" + _Verbose.__init__(self, verbose) + self.__cond = Condition(Lock()) + self.__value = value + + def acquire(self, blocking=1): + rc = False + self.__cond.acquire() + while self.__value == 0: + if not blocking: + break + if __debug__: + self._note("%s.acquire(%s): blocked waiting, value=%s", + self, blocking, self.__value) + self.__cond.wait() + else: + self.__value = self.__value - 1 + if __debug__: + self._note("%s.acquire: success, value=%s", + self, self.__value) + rc = True + self.__cond.release() + return rc + + def release(self): + self.__cond.acquire() + self.__value = self.__value + 1 + if __debug__: + self._note("%s.release: success, value=%s", + self, self.__value) + self.__cond.notify() + self.__cond.release() + + +def BoundedSemaphore(*args, **kwargs): + return _BoundedSemaphore(*args, **kwargs) + +class _BoundedSemaphore(_Semaphore): + """Semaphore that checks that # releases is <= # acquires""" + def __init__(self, value=1, verbose=None): + _Semaphore.__init__(self, value, verbose) + self._initial_value = value + + def release(self): + if self._Semaphore__value >= self._initial_value: + raise ValueError, "Semaphore released too many times" + return _Semaphore.release(self) + + +def Event(*args, **kwargs): + return _Event(*args, **kwargs) + +class _Event(_Verbose): + + # After Tim Peters' event class (without is_posted()) + + def __init__(self, verbose=None): + _Verbose.__init__(self, verbose) + self.__cond = Condition(Lock()) + self.__flag = False + + def isSet(self): + return self.__flag + + def set(self): + self.__cond.acquire() + try: + self.__flag = True + self.__cond.notifyAll() + finally: + self.__cond.release() + + def clear(self): + self.__cond.acquire() + try: + self.__flag = False + finally: + self.__cond.release() + + def wait(self, timeout=None): + self.__cond.acquire() + try: + if not self.__flag: + self.__cond.wait(timeout) + finally: + self.__cond.release() + +# Helper to generate new thread names +_counter = 0 +def _newname(template="Thread-%d"): + global _counter + _counter = _counter + 1 + return template % _counter + +# Active thread administration +_active_limbo_lock = _allocate_lock() +_active = {} +_limbo = {} + + +# Main class for threads + +class Thread(_Verbose): + + __initialized = False + + def __init__(self, group=None, target=None, name=None, + args=(), kwargs={}, verbose=None): + assert group is None, "group argument must be None for now" + _Verbose.__init__(self, verbose) + self.__target = target + self.__name = str(name or _newname()) + self.__args = args + self.__kwargs = kwargs + self.__daemonic = self._set_daemon() + self.__started = False + self.__stopped = False + self.__block = Condition(Lock()) + self.__initialized = True + + def _set_daemon(self): + # Overridden in _MainThread and _DummyThread + return currentThread().isDaemon() + + def __repr__(self): + assert self.__initialized, "Thread.__init__() was not called" + status = "initial" + if self.__started: + status = "started" + if self.__stopped: + status = "stopped" + if self.__daemonic: + status = status + " daemon" + return "<%s(%s, %s)>" % (self.__class__.__name__, self.__name, status) + + def start(self): + assert self.__initialized, "Thread.__init__() not called" + assert not self.__started, "thread already started" + if __debug__: + self._note("%s.start(): starting thread", self) + _active_limbo_lock.acquire() + _limbo[self] = self + _active_limbo_lock.release() + _start_new_thread(self.__bootstrap, ()) + self.__started = True + _sleep(0.000001) # 1 usec, to let the thread run (Solaris hack) + + def run(self): + if self.__target: + self.__target(*self.__args, **self.__kwargs) + + def __bootstrap(self): + try: + self.__started = True + _active_limbo_lock.acquire() + _active[_get_ident()] = self + del _limbo[self] + _active_limbo_lock.release() + if __debug__: + self._note("%s.__bootstrap(): thread started", self) + + if _trace_hook: + self._note("%s.__bootstrap(): registering trace hook", self) + _sys.settrace(_trace_hook) + if _profile_hook: + self._note("%s.__bootstrap(): registering profile hook", self) + _sys.setprofile(_profile_hook) + + try: + self.run() + except SystemExit: + if __debug__: + self._note("%s.__bootstrap(): raised SystemExit", self) + except: + if __debug__: + self._note("%s.__bootstrap(): unhandled exception", self) + s = _StringIO() + _print_exc(file=s) + _sys.stderr.write("Exception in thread %s:\n%s\n" % + (self.getName(), s.getvalue())) + else: + if __debug__: + self._note("%s.__bootstrap(): normal return", self) + finally: + self.__stop() + try: + self.__delete() + except: + pass + + def __stop(self): + self.__block.acquire() + self.__stopped = True + self.__block.notifyAll() + self.__block.release() + + def __delete(self): + _active_limbo_lock.acquire() + del _active[_get_ident()] + _active_limbo_lock.release() + + def join(self, timeout=None): + assert self.__initialized, "Thread.__init__() not called" + assert self.__started, "cannot join thread before it is started" + assert self is not currentThread(), "cannot join current thread" + if __debug__: + if not self.__stopped: + self._note("%s.join(): waiting until thread stops", self) + self.__block.acquire() + try: + if timeout is None: + while not self.__stopped: + self.__block.wait() + if __debug__: + self._note("%s.join(): thread stopped", self) + else: + deadline = _time() + timeout + while not self.__stopped: + delay = deadline - _time() + if delay <= 0: + if __debug__: + self._note("%s.join(): timed out", self) + break + self.__block.wait(delay) + else: + if __debug__: + self._note("%s.join(): thread stopped", self) + finally: + self.__block.release() + + def getName(self): + assert self.__initialized, "Thread.__init__() not called" + return self.__name + + def setName(self, name): + assert self.__initialized, "Thread.__init__() not called" + self.__name = str(name) + + def isAlive(self): + assert self.__initialized, "Thread.__init__() not called" + return self.__started and not self.__stopped + + def isDaemon(self): + assert self.__initialized, "Thread.__init__() not called" + return self.__daemonic + + def setDaemon(self, daemonic): + assert self.__initialized, "Thread.__init__() not called" + assert not self.__started, "cannot set daemon status of active thread" + self.__daemonic = daemonic + +# The timer class was contributed by Itamar Shtull-Trauring + +def Timer(*args, **kwargs): + return _Timer(*args, **kwargs) + +class _Timer(Thread): + """Call a function after a specified number of seconds: + + t = Timer(30.0, f, args=[], kwargs={}) + t.start() + t.cancel() # stop the timer's action if it's still waiting + """ + + def __init__(self, interval, function, args=[], kwargs={}): + Thread.__init__(self) + self.interval = interval + self.function = function + self.args = args + self.kwargs = kwargs + self.finished = Event() + + def cancel(self): + """Stop the timer if it hasn't finished yet""" + self.finished.set() + + def run(self): + self.finished.wait(self.interval) + if not self.finished.isSet(): + self.function(*self.args, **self.kwargs) + self.finished.set() + +# Special thread class to represent the main thread +# This is garbage collected through an exit handler + +class _MainThread(Thread): + + def __init__(self): + Thread.__init__(self, name="MainThread") + self._Thread__started = True + _active_limbo_lock.acquire() + _active[_get_ident()] = self + _active_limbo_lock.release() + import atexit + atexit.register(self.__exitfunc) + + def _set_daemon(self): + return False + + def __exitfunc(self): + self._Thread__stop() + t = _pickSomeNonDaemonThread() + if t: + if __debug__: + self._note("%s: waiting for other threads", self) + while t: + t.join() + t = _pickSomeNonDaemonThread() + if __debug__: + self._note("%s: exiting", self) + self._Thread__delete() + +def _pickSomeNonDaemonThread(): + for t in enumerate(): + if not t.isDaemon() and t.isAlive(): + return t + return None + + +# Dummy thread class to represent threads not started here. +# These aren't garbage collected when they die, +# nor can they be waited for. +# Their purpose is to return *something* from currentThread(). +# They are marked as daemon threads so we won't wait for them +# when we exit (conform previous semantics). + +class _DummyThread(Thread): + + def __init__(self): + Thread.__init__(self, name=_newname("Dummy-%d")) + self._Thread__started = True + _active_limbo_lock.acquire() + _active[_get_ident()] = self + _active_limbo_lock.release() + + def _set_daemon(self): + return True + + def join(self, timeout=None): + assert False, "cannot join a dummy thread" + + +# Global API functions + +def currentThread(): + try: + return _active[_get_ident()] + except KeyError: + ##print "currentThread(): no current thread for", _get_ident() + return _DummyThread() + +def activeCount(): + _active_limbo_lock.acquire() + count = len(_active) + len(_limbo) + _active_limbo_lock.release() + return count + +def enumerate(): + _active_limbo_lock.acquire() + active = _active.values() + _limbo.values() + _active_limbo_lock.release() + return active + +# Create the main thread object + +_MainThread() + + +# Self-test code + +def _test(): + + class BoundedQueue(_Verbose): + + def __init__(self, limit): + _Verbose.__init__(self) + self.mon = RLock() + self.rc = Condition(self.mon) + self.wc = Condition(self.mon) + self.limit = limit + self.queue = [] + + def put(self, item): + self.mon.acquire() + while len(self.queue) >= self.limit: + self._note("put(%s): queue full", item) + self.wc.wait() + self.queue.append(item) + self._note("put(%s): appended, length now %d", + item, len(self.queue)) + self.rc.notify() + self.mon.release() + + def get(self): + self.mon.acquire() + while not self.queue: + self._note("get(): queue empty") + self.rc.wait() + item = self.queue.pop(0) + self._note("get(): got %s, %d left", item, len(self.queue)) + self.wc.notify() + self.mon.release() + return item + + class ProducerThread(Thread): + + def __init__(self, queue, quota): + Thread.__init__(self, name="Producer") + self.queue = queue + self.quota = quota + + def run(self): + from random import random + counter = 0 + while counter < self.quota: + counter = counter + 1 + self.queue.put("%s.%d" % (self.getName(), counter)) + _sleep(random() * 0.00001) + + + class ConsumerThread(Thread): + + def __init__(self, queue, count): + Thread.__init__(self, name="Consumer") + self.queue = queue + self.count = count + + def run(self): + while self.count > 0: + item = self.queue.get() + print item + self.count = self.count - 1 + + NP = 3 + QL = 4 + NI = 5 + + Q = BoundedQueue(QL) + P = [] + for i in range(NP): + t = ProducerThread(Q, NI) + t.setName("Producer-%d" % (i+1)) + P.append(t) + C = ConsumerThread(Q, NI*NP) + for t in P: + t.start() + _sleep(0.000001) + C.start() + for t in P: + t.join() + C.join() + +if __name__ == '__main__': + _test() diff --git a/gnuradio-core/src/python/gnuradio/gr/gr_threading_24.py b/gnuradio-core/src/python/gnuradio/gr/gr_threading_24.py new file mode 100644 index 000000000..8539bfc04 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/gr_threading_24.py @@ -0,0 +1,793 @@ +"""Thread module emulating a subset of Java's threading model.""" + +# This started life as the threading.py module of Python 2.4 +# It's been patched to fix a problem with join, where a KeyboardInterrupt +# caused a lock to be left in the acquired state. + +import sys as _sys + +try: + import thread +except ImportError: + del _sys.modules[__name__] + raise + +from time import time as _time, sleep as _sleep +from traceback import format_exc as _format_exc +from collections import deque + +# Rename some stuff so "from threading import *" is safe +__all__ = ['activeCount', 'Condition', 'currentThread', 'enumerate', 'Event', + 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Thread', + 'Timer', 'setprofile', 'settrace', 'local'] + +_start_new_thread = thread.start_new_thread +_allocate_lock = thread.allocate_lock +_get_ident = thread.get_ident +ThreadError = thread.error +del thread + + +# Debug support (adapted from ihooks.py). +# All the major classes here derive from _Verbose. We force that to +# be a new-style class so that all the major classes here are new-style. +# This helps debugging (type(instance) is more revealing for instances +# of new-style classes). + +_VERBOSE = False + +if __debug__: + + class _Verbose(object): + + def __init__(self, verbose=None): + if verbose is None: + verbose = _VERBOSE + self.__verbose = verbose + + def _note(self, format, *args): + if self.__verbose: + format = format % args + format = "%s: %s\n" % ( + currentThread().getName(), format) + _sys.stderr.write(format) + +else: + # Disable this when using "python -O" + class _Verbose(object): + def __init__(self, verbose=None): + pass + def _note(self, *args): + pass + +# Support for profile and trace hooks + +_profile_hook = None +_trace_hook = None + +def setprofile(func): + global _profile_hook + _profile_hook = func + +def settrace(func): + global _trace_hook + _trace_hook = func + +# Synchronization classes + +Lock = _allocate_lock + +def RLock(*args, **kwargs): + return _RLock(*args, **kwargs) + +class _RLock(_Verbose): + + def __init__(self, verbose=None): + _Verbose.__init__(self, verbose) + self.__block = _allocate_lock() + self.__owner = None + self.__count = 0 + + def __repr__(self): + return "<%s(%s, %d)>" % ( + self.__class__.__name__, + self.__owner and self.__owner.getName(), + self.__count) + + def acquire(self, blocking=1): + me = currentThread() + if self.__owner is me: + self.__count = self.__count + 1 + if __debug__: + self._note("%s.acquire(%s): recursive success", self, blocking) + return 1 + rc = self.__block.acquire(blocking) + if rc: + self.__owner = me + self.__count = 1 + if __debug__: + self._note("%s.acquire(%s): initial succes", self, blocking) + else: + if __debug__: + self._note("%s.acquire(%s): failure", self, blocking) + return rc + + def release(self): + me = currentThread() + assert self.__owner is me, "release() of un-acquire()d lock" + self.__count = count = self.__count - 1 + if not count: + self.__owner = None + self.__block.release() + if __debug__: + self._note("%s.release(): final release", self) + else: + if __debug__: + self._note("%s.release(): non-final release", self) + + # Internal methods used by condition variables + + def _acquire_restore(self, (count, owner)): + self.__block.acquire() + self.__count = count + self.__owner = owner + if __debug__: + self._note("%s._acquire_restore()", self) + + def _release_save(self): + if __debug__: + self._note("%s._release_save()", self) + count = self.__count + self.__count = 0 + owner = self.__owner + self.__owner = None + self.__block.release() + return (count, owner) + + def _is_owned(self): + return self.__owner is currentThread() + + +def Condition(*args, **kwargs): + return _Condition(*args, **kwargs) + +class _Condition(_Verbose): + + def __init__(self, lock=None, verbose=None): + _Verbose.__init__(self, verbose) + if lock is None: + lock = RLock() + self.__lock = lock + # Export the lock's acquire() and release() methods + self.acquire = lock.acquire + self.release = lock.release + # If the lock defines _release_save() and/or _acquire_restore(), + # these override the default implementations (which just call + # release() and acquire() on the lock). Ditto for _is_owned(). + try: + self._release_save = lock._release_save + except AttributeError: + pass + try: + self._acquire_restore = lock._acquire_restore + except AttributeError: + pass + try: + self._is_owned = lock._is_owned + except AttributeError: + pass + self.__waiters = [] + + def __repr__(self): + return "<Condition(%s, %d)>" % (self.__lock, len(self.__waiters)) + + def _release_save(self): + self.__lock.release() # No state to save + + def _acquire_restore(self, x): + self.__lock.acquire() # Ignore saved state + + def _is_owned(self): + # Return True if lock is owned by currentThread. + # This method is called only if __lock doesn't have _is_owned(). + if self.__lock.acquire(0): + self.__lock.release() + return False + else: + return True + + def wait(self, timeout=None): + assert self._is_owned(), "wait() of un-acquire()d lock" + waiter = _allocate_lock() + waiter.acquire() + self.__waiters.append(waiter) + saved_state = self._release_save() + try: # restore state no matter what (e.g., KeyboardInterrupt) + if timeout is None: + waiter.acquire() + if __debug__: + self._note("%s.wait(): got it", self) + else: + # Balancing act: We can't afford a pure busy loop, so we + # have to sleep; but if we sleep the whole timeout time, + # we'll be unresponsive. The scheme here sleeps very + # little at first, longer as time goes on, but never longer + # than 20 times per second (or the timeout time remaining). + endtime = _time() + timeout + delay = 0.0005 # 500 us -> initial delay of 1 ms + while True: + gotit = waiter.acquire(0) + if gotit: + break + remaining = endtime - _time() + if remaining <= 0: + break + delay = min(delay * 2, remaining, .05) + _sleep(delay) + if not gotit: + if __debug__: + self._note("%s.wait(%s): timed out", self, timeout) + try: + self.__waiters.remove(waiter) + except ValueError: + pass + else: + if __debug__: + self._note("%s.wait(%s): got it", self, timeout) + finally: + self._acquire_restore(saved_state) + + def notify(self, n=1): + assert self._is_owned(), "notify() of un-acquire()d lock" + __waiters = self.__waiters + waiters = __waiters[:n] + if not waiters: + if __debug__: + self._note("%s.notify(): no waiters", self) + return + self._note("%s.notify(): notifying %d waiter%s", self, n, + n!=1 and "s" or "") + for waiter in waiters: + waiter.release() + try: + __waiters.remove(waiter) + except ValueError: + pass + + def notifyAll(self): + self.notify(len(self.__waiters)) + + +def Semaphore(*args, **kwargs): + return _Semaphore(*args, **kwargs) + +class _Semaphore(_Verbose): + + # After Tim Peters' semaphore class, but not quite the same (no maximum) + + def __init__(self, value=1, verbose=None): + assert value >= 0, "Semaphore initial value must be >= 0" + _Verbose.__init__(self, verbose) + self.__cond = Condition(Lock()) + self.__value = value + + def acquire(self, blocking=1): + rc = False + self.__cond.acquire() + while self.__value == 0: + if not blocking: + break + if __debug__: + self._note("%s.acquire(%s): blocked waiting, value=%s", + self, blocking, self.__value) + self.__cond.wait() + else: + self.__value = self.__value - 1 + if __debug__: + self._note("%s.acquire: success, value=%s", + self, self.__value) + rc = True + self.__cond.release() + return rc + + def release(self): + self.__cond.acquire() + self.__value = self.__value + 1 + if __debug__: + self._note("%s.release: success, value=%s", + self, self.__value) + self.__cond.notify() + self.__cond.release() + + +def BoundedSemaphore(*args, **kwargs): + return _BoundedSemaphore(*args, **kwargs) + +class _BoundedSemaphore(_Semaphore): + """Semaphore that checks that # releases is <= # acquires""" + def __init__(self, value=1, verbose=None): + _Semaphore.__init__(self, value, verbose) + self._initial_value = value + + def release(self): + if self._Semaphore__value >= self._initial_value: + raise ValueError, "Semaphore released too many times" + return _Semaphore.release(self) + + +def Event(*args, **kwargs): + return _Event(*args, **kwargs) + +class _Event(_Verbose): + + # After Tim Peters' event class (without is_posted()) + + def __init__(self, verbose=None): + _Verbose.__init__(self, verbose) + self.__cond = Condition(Lock()) + self.__flag = False + + def isSet(self): + return self.__flag + + def set(self): + self.__cond.acquire() + try: + self.__flag = True + self.__cond.notifyAll() + finally: + self.__cond.release() + + def clear(self): + self.__cond.acquire() + try: + self.__flag = False + finally: + self.__cond.release() + + def wait(self, timeout=None): + self.__cond.acquire() + try: + if not self.__flag: + self.__cond.wait(timeout) + finally: + self.__cond.release() + +# Helper to generate new thread names +_counter = 0 +def _newname(template="Thread-%d"): + global _counter + _counter = _counter + 1 + return template % _counter + +# Active thread administration +_active_limbo_lock = _allocate_lock() +_active = {} +_limbo = {} + + +# Main class for threads + +class Thread(_Verbose): + + __initialized = False + # Need to store a reference to sys.exc_info for printing + # out exceptions when a thread tries to use a global var. during interp. + # shutdown and thus raises an exception about trying to perform some + # operation on/with a NoneType + __exc_info = _sys.exc_info + + def __init__(self, group=None, target=None, name=None, + args=(), kwargs={}, verbose=None): + assert group is None, "group argument must be None for now" + _Verbose.__init__(self, verbose) + self.__target = target + self.__name = str(name or _newname()) + self.__args = args + self.__kwargs = kwargs + self.__daemonic = self._set_daemon() + self.__started = False + self.__stopped = False + self.__block = Condition(Lock()) + self.__initialized = True + # sys.stderr is not stored in the class like + # sys.exc_info since it can be changed between instances + self.__stderr = _sys.stderr + + def _set_daemon(self): + # Overridden in _MainThread and _DummyThread + return currentThread().isDaemon() + + def __repr__(self): + assert self.__initialized, "Thread.__init__() was not called" + status = "initial" + if self.__started: + status = "started" + if self.__stopped: + status = "stopped" + if self.__daemonic: + status = status + " daemon" + return "<%s(%s, %s)>" % (self.__class__.__name__, self.__name, status) + + def start(self): + assert self.__initialized, "Thread.__init__() not called" + assert not self.__started, "thread already started" + if __debug__: + self._note("%s.start(): starting thread", self) + _active_limbo_lock.acquire() + _limbo[self] = self + _active_limbo_lock.release() + _start_new_thread(self.__bootstrap, ()) + self.__started = True + _sleep(0.000001) # 1 usec, to let the thread run (Solaris hack) + + def run(self): + if self.__target: + self.__target(*self.__args, **self.__kwargs) + + def __bootstrap(self): + try: + self.__started = True + _active_limbo_lock.acquire() + _active[_get_ident()] = self + del _limbo[self] + _active_limbo_lock.release() + if __debug__: + self._note("%s.__bootstrap(): thread started", self) + + if _trace_hook: + self._note("%s.__bootstrap(): registering trace hook", self) + _sys.settrace(_trace_hook) + if _profile_hook: + self._note("%s.__bootstrap(): registering profile hook", self) + _sys.setprofile(_profile_hook) + + try: + self.run() + except SystemExit: + if __debug__: + self._note("%s.__bootstrap(): raised SystemExit", self) + except: + if __debug__: + self._note("%s.__bootstrap(): unhandled exception", self) + # If sys.stderr is no more (most likely from interpreter + # shutdown) use self.__stderr. Otherwise still use sys (as in + # _sys) in case sys.stderr was redefined since the creation of + # self. + if _sys: + _sys.stderr.write("Exception in thread %s:\n%s\n" % + (self.getName(), _format_exc())) + else: + # Do the best job possible w/o a huge amt. of code to + # approximate a traceback (code ideas from + # Lib/traceback.py) + exc_type, exc_value, exc_tb = self.__exc_info() + try: + print>>self.__stderr, ( + "Exception in thread " + self.getName() + + " (most likely raised during interpreter shutdown):") + print>>self.__stderr, ( + "Traceback (most recent call last):") + while exc_tb: + print>>self.__stderr, ( + ' File "%s", line %s, in %s' % + (exc_tb.tb_frame.f_code.co_filename, + exc_tb.tb_lineno, + exc_tb.tb_frame.f_code.co_name)) + exc_tb = exc_tb.tb_next + print>>self.__stderr, ("%s: %s" % (exc_type, exc_value)) + # Make sure that exc_tb gets deleted since it is a memory + # hog; deleting everything else is just for thoroughness + finally: + del exc_type, exc_value, exc_tb + else: + if __debug__: + self._note("%s.__bootstrap(): normal return", self) + finally: + self.__stop() + try: + self.__delete() + except: + pass + + def __stop(self): + self.__block.acquire() + self.__stopped = True + self.__block.notifyAll() + self.__block.release() + + def __delete(self): + "Remove current thread from the dict of currently running threads." + + # Notes about running with dummy_thread: + # + # Must take care to not raise an exception if dummy_thread is being + # used (and thus this module is being used as an instance of + # dummy_threading). dummy_thread.get_ident() always returns -1 since + # there is only one thread if dummy_thread is being used. Thus + # len(_active) is always <= 1 here, and any Thread instance created + # overwrites the (if any) thread currently registered in _active. + # + # An instance of _MainThread is always created by 'threading'. This + # gets overwritten the instant an instance of Thread is created; both + # threads return -1 from dummy_thread.get_ident() and thus have the + # same key in the dict. So when the _MainThread instance created by + # 'threading' tries to clean itself up when atexit calls this method + # it gets a KeyError if another Thread instance was created. + # + # This all means that KeyError from trying to delete something from + # _active if dummy_threading is being used is a red herring. But + # since it isn't if dummy_threading is *not* being used then don't + # hide the exception. + + _active_limbo_lock.acquire() + try: + try: + del _active[_get_ident()] + except KeyError: + if 'dummy_threading' not in _sys.modules: + raise + finally: + _active_limbo_lock.release() + + def join(self, timeout=None): + assert self.__initialized, "Thread.__init__() not called" + assert self.__started, "cannot join thread before it is started" + assert self is not currentThread(), "cannot join current thread" + if __debug__: + if not self.__stopped: + self._note("%s.join(): waiting until thread stops", self) + self.__block.acquire() + try: + if timeout is None: + while not self.__stopped: + self.__block.wait() + if __debug__: + self._note("%s.join(): thread stopped", self) + else: + deadline = _time() + timeout + while not self.__stopped: + delay = deadline - _time() + if delay <= 0: + if __debug__: + self._note("%s.join(): timed out", self) + break + self.__block.wait(delay) + else: + if __debug__: + self._note("%s.join(): thread stopped", self) + finally: + self.__block.release() + + def getName(self): + assert self.__initialized, "Thread.__init__() not called" + return self.__name + + def setName(self, name): + assert self.__initialized, "Thread.__init__() not called" + self.__name = str(name) + + def isAlive(self): + assert self.__initialized, "Thread.__init__() not called" + return self.__started and not self.__stopped + + def isDaemon(self): + assert self.__initialized, "Thread.__init__() not called" + return self.__daemonic + + def setDaemon(self, daemonic): + assert self.__initialized, "Thread.__init__() not called" + assert not self.__started, "cannot set daemon status of active thread" + self.__daemonic = daemonic + +# The timer class was contributed by Itamar Shtull-Trauring + +def Timer(*args, **kwargs): + return _Timer(*args, **kwargs) + +class _Timer(Thread): + """Call a function after a specified number of seconds: + + t = Timer(30.0, f, args=[], kwargs={}) + t.start() + t.cancel() # stop the timer's action if it's still waiting + """ + + def __init__(self, interval, function, args=[], kwargs={}): + Thread.__init__(self) + self.interval = interval + self.function = function + self.args = args + self.kwargs = kwargs + self.finished = Event() + + def cancel(self): + """Stop the timer if it hasn't finished yet""" + self.finished.set() + + def run(self): + self.finished.wait(self.interval) + if not self.finished.isSet(): + self.function(*self.args, **self.kwargs) + self.finished.set() + +# Special thread class to represent the main thread +# This is garbage collected through an exit handler + +class _MainThread(Thread): + + def __init__(self): + Thread.__init__(self, name="MainThread") + self._Thread__started = True + _active_limbo_lock.acquire() + _active[_get_ident()] = self + _active_limbo_lock.release() + import atexit + atexit.register(self.__exitfunc) + + def _set_daemon(self): + return False + + def __exitfunc(self): + self._Thread__stop() + t = _pickSomeNonDaemonThread() + if t: + if __debug__: + self._note("%s: waiting for other threads", self) + while t: + t.join() + t = _pickSomeNonDaemonThread() + if __debug__: + self._note("%s: exiting", self) + self._Thread__delete() + +def _pickSomeNonDaemonThread(): + for t in enumerate(): + if not t.isDaemon() and t.isAlive(): + return t + return None + + +# Dummy thread class to represent threads not started here. +# These aren't garbage collected when they die, +# nor can they be waited for. +# Their purpose is to return *something* from currentThread(). +# They are marked as daemon threads so we won't wait for them +# when we exit (conform previous semantics). + +class _DummyThread(Thread): + + def __init__(self): + Thread.__init__(self, name=_newname("Dummy-%d")) + self._Thread__started = True + _active_limbo_lock.acquire() + _active[_get_ident()] = self + _active_limbo_lock.release() + + def _set_daemon(self): + return True + + def join(self, timeout=None): + assert False, "cannot join a dummy thread" + + +# Global API functions + +def currentThread(): + try: + return _active[_get_ident()] + except KeyError: + ##print "currentThread(): no current thread for", _get_ident() + return _DummyThread() + +def activeCount(): + _active_limbo_lock.acquire() + count = len(_active) + len(_limbo) + _active_limbo_lock.release() + return count + +def enumerate(): + _active_limbo_lock.acquire() + active = _active.values() + _limbo.values() + _active_limbo_lock.release() + return active + +# Create the main thread object + +_MainThread() + +# get thread-local implementation, either from the thread +# module, or from the python fallback + +try: + from thread import _local as local +except ImportError: + from _threading_local import local + + +# Self-test code + +def _test(): + + class BoundedQueue(_Verbose): + + def __init__(self, limit): + _Verbose.__init__(self) + self.mon = RLock() + self.rc = Condition(self.mon) + self.wc = Condition(self.mon) + self.limit = limit + self.queue = deque() + + def put(self, item): + self.mon.acquire() + while len(self.queue) >= self.limit: + self._note("put(%s): queue full", item) + self.wc.wait() + self.queue.append(item) + self._note("put(%s): appended, length now %d", + item, len(self.queue)) + self.rc.notify() + self.mon.release() + + def get(self): + self.mon.acquire() + while not self.queue: + self._note("get(): queue empty") + self.rc.wait() + item = self.queue.popleft() + self._note("get(): got %s, %d left", item, len(self.queue)) + self.wc.notify() + self.mon.release() + return item + + class ProducerThread(Thread): + + def __init__(self, queue, quota): + Thread.__init__(self, name="Producer") + self.queue = queue + self.quota = quota + + def run(self): + from random import random + counter = 0 + while counter < self.quota: + counter = counter + 1 + self.queue.put("%s.%d" % (self.getName(), counter)) + _sleep(random() * 0.00001) + + + class ConsumerThread(Thread): + + def __init__(self, queue, count): + Thread.__init__(self, name="Consumer") + self.queue = queue + self.count = count + + def run(self): + while self.count > 0: + item = self.queue.get() + print item + self.count = self.count - 1 + + NP = 3 + QL = 4 + NI = 5 + + Q = BoundedQueue(QL) + P = [] + for i in range(NP): + t = ProducerThread(Q, NI) + t.setName("Producer-%d" % (i+1)) + P.append(t) + C = ConsumerThread(Q, NI*NP) + for t in P: + t.start() + _sleep(0.000001) + C.start() + for t in P: + t.join() + C.join() + +if __name__ == '__main__': + _test() diff --git a/gnuradio-core/src/python/gnuradio/gr/hier_block.py b/gnuradio-core/src/python/gnuradio/gr/hier_block.py new file mode 100644 index 000000000..d669d7178 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/hier_block.py @@ -0,0 +1,132 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio_swig_python import io_signature +import weakref + +class hier_block_base(object): + """ + Abstract base class for hierarchical signal processing blocks. + """ + def __init__(self, fg): + """ + @param fg: The flow graph that contains this hierarchical block. + @type fg: gr.flow_graph + """ + self.fg = weakref.proxy(fg) + + def input_signature(self): + """ + @return input signature of hierarchical block. + @rtype gr.io_signature + """ + raise NotImplemented + + def output_signature(self): + """ + @return output signature of hierarchical block. + @rtype gr.io_signature + """ + raise NotImplemented + + def resolve_input_port(self, port_number): + """ + @param port_number: which input port number to resolve to an endpoint. + @type port_number: int + @return: sequence of endpoints + @rtype: sequence of endpoint + + Note that an input port can resolve to more than one endpoint. + """ + raise NotImplemented + + def resolve_output_port(self, port_number): + """ + @param port_number: which output port number to resolve to an endpoint. + @type port_number: int + @return: endpoint + @rtype: endpoint + + Output ports resolve to a single endpoint. + """ + raise NotImplemented + + +class hier_block(hier_block_base): + """ + Simple concrete class for building hierarchical blocks. + + This class assumes that there is at most a single block at the + head of the chain and a single block at the end of the chain. + Either head or tail may be None indicating a sink or source respectively. + + If you needs something more elaborate than this, derive a new class from + hier_block_base. + """ + def __init__(self, fg, head_block, tail_block): + """ + @param fg: The flow graph that contains this hierarchical block. + @type fg: flow_graph + @param head_block: the first block in the signal processing chain. + @type head_block: None or subclass of gr.block or gr.hier_block_base + @param tail_block: the last block in the signal processing chain. + @type tail_block: None or subclass of gr.block or gr.hier_block_base + """ + hier_block_base.__init__(self, fg) + # FIXME add type checks here for easier debugging of misuse + self.head = head_block + self.tail = tail_block + + def input_signature(self): + if self.head: + return self.head.input_signature() + else: + return io_signature(0,0,0) + + def output_signature(self): + if self.tail: + return self.tail.output_signature() + else: + return io_signature(0,0,0) + + def resolve_input_port(self, port_number): + return ((self.head, port_number),) + + def resolve_output_port(self, port_number): + return (self.tail, port_number) + + +class compose(hier_block): + """ + Compose one or more blocks (primitive or hierarchical) into a new hierarchical block. + """ + def __init__(self, fg, *blocks): + """ + @param fg: flow graph + @type fg: gr.flow_graph + @param *blocks: list of blocks + @type *blocks: list of blocks + """ + if len(blocks) < 1: + raise ValueError, ("compose requires at least one block; none provided.") + if len(blocks) > 1: + fg.connect(*blocks) + hier_block.__init__(self, fg, blocks[0], blocks[-1]) diff --git a/gnuradio-core/src/python/gnuradio/gr/prefs.py b/gnuradio-core/src/python/gnuradio/gr/prefs.py new file mode 100644 index 000000000..fa1291271 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/prefs.py @@ -0,0 +1,129 @@ +# +# Copyright 2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +import gnuradio_swig_python as gsp +_prefs_base = gsp.gr_prefs + + +import ConfigParser +import os +import os.path +import sys + + +def _user_prefs_filename(): + return os.path.expanduser('~/.gnuradio/config.conf') + +def _sys_prefs_dirname(): + return os.path.join(gsp.prefix(), 'etc/gnuradio/conf.d') + +def _bool(x): + """ + Try to coerce obj to a True or False + """ + if isinstance(x, bool): + return x + if isinstance(x, (float, int)): + return bool(x) + raise TypeError, x + + +class _prefs(_prefs_base): + """ + Derive our 'real class' from the stubbed out base class that has support + for SWIG directors. This allows C++ code to magically and transparently + invoke the methods in this python class. + """ + def __init__(self): + _prefs_base.__init__(self) + self.cp = ConfigParser.RawConfigParser() + + def _sys_prefs_filenames(self): + dir = _sys_prefs_dirname() + try: + fnames = os.listdir(dir) + except (IOError, OSError): + return [] + fnames.sort() + return [os.path.join(dir, f) for f in fnames] + + + def _read_files(self): + filenames = self._sys_prefs_filenames() + filenames.append(_user_prefs_filename()) + #print "filenames: ", filenames + self.cp.read(filenames) + + def __getattr__(self, name): + return getattr(self.cp, name) + + # ---------------------------------------------------------------- + # These methods override the C++ virtual methods of the same name + # ---------------------------------------------------------------- + def has_section(self, section): + return self.cp.has_section(section) + + def has_option(self, section, option): + return self.cp.has_option(section, option) + + def get_string(self, section, option, default_val): + try: + return self.cp.get(section, option) + except: + return default_val + + def get_bool(self, section, option, default_val): + try: + return self.cp.getboolean(section, option) + except: + return default_val + + def get_long(self, section, option, default_val): + try: + return self.cp.getint(section, option) + except: + return default_val + + def get_double(self, section, option, default_val): + try: + return self.cp.getfloat(section, option) + except: + return default_val + # ---------------------------------------------------------------- + # End override of C++ virtual methods + # ---------------------------------------------------------------- + + +_prefs_db = _prefs() + +# if GR_DONT_LOAD_PREFS is set, don't load them. +# (make check uses this to avoid interactions.) +if os.getenv("GR_DONT_LOAD_PREFS", None) is None: + _prefs_db._read_files() + + +_prefs_base.set_singleton(_prefs_db) # tell C++ what instance to use + +def prefs(): + """ + Return the global preference data base + """ + return _prefs_db diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_add_and_friends.py b/gnuradio-core/src/python/gnuradio/gr/qa_add_and_friends.py new file mode 100755 index 000000000..6cb74e9f5 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_add_and_friends.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest + +class test_head (gr_unittest.TestCase): + + def setUp (self): + self.fg = gr.flow_graph () + + def tearDown (self): + self.fg = None + + def help_ii (self, src_data, exp_data, op): + for s in zip (range (len (src_data)), src_data): + src = gr.vector_source_i (s[1]) + self.fg.connect (src, (op, s[0])) + dst = gr.vector_sink_i () + self.fg.connect (op, dst) + self.fg.run () + result_data = dst.data () + self.assertEqual (exp_data, result_data) + + def help_ff (self, src_data, exp_data, op): + for s in zip (range (len (src_data)), src_data): + src = gr.vector_source_f (s[1]) + self.fg.connect (src, (op, s[0])) + dst = gr.vector_sink_f () + self.fg.connect (op, dst) + self.fg.run () + result_data = dst.data () + self.assertEqual (exp_data, result_data) + + def help_cc (self, src_data, exp_data, op): + for s in zip (range (len (src_data)), src_data): + src = gr.vector_source_c (s[1]) + self.fg.connect (src, (op, s[0])) + dst = gr.vector_sink_c () + self.fg.connect (op, dst) + self.fg.run () + result_data = dst.data () + self.assertEqual (exp_data, result_data) + + def test_add_const_ii (self): + src_data = (1, 2, 3, 4, 5) + expected_result = (6, 7, 8, 9, 10) + op = gr.add_const_ii (5) + self.help_ii ((src_data,), expected_result, op) + + def test_add_const_cc (self): + src_data = (1, 2, 3, 4, 5) + expected_result = (1+5j, 2+5j, 3+5j, 4+5j, 5+5j) + op = gr.add_const_cc (5j) + self.help_cc ((src_data,), expected_result, op) + + def test_mult_const_ii (self): + src_data = (-1, 0, 1, 2, 3) + expected_result = (-5, 0, 5, 10, 15) + op = gr.multiply_const_ii (5) + self.help_ii ((src_data,), expected_result, op) + + def test_add_ii (self): + src1_data = (1, 2, 3, 4, 5) + src2_data = (8, -3, 4, 8, 2) + expected_result = (9, -1, 7, 12, 7) + op = gr.add_ii () + self.help_ii ((src1_data, src2_data), + expected_result, op) + + def test_mult_ii (self): + src1_data = (1, 2, 3, 4, 5) + src2_data = (8, -3, 4, 8, 2) + expected_result = (8, -6, 12, 32, 10) + op = gr.multiply_ii () + self.help_ii ((src1_data, src2_data), + expected_result, op) + + def test_sub_ii_1 (self): + src1_data = (1, 2, 3, 4, 5) + expected_result = (-1, -2, -3, -4, -5) + op = gr.sub_ii () + self.help_ii ((src1_data,), + expected_result, op) + + def test_sub_ii_2 (self): + src1_data = (1, 2, 3, 4, 5) + src2_data = (8, -3, 4, 8, 2) + expected_result = (-7, 5, -1, -4, 3) + op = gr.sub_ii () + self.help_ii ((src1_data, src2_data), + expected_result, op) + + def test_div_ff_1 (self): + src1_data = (1, 2, 4, -8) + expected_result = (1, 0.5, 0.25, -.125) + op = gr.divide_ff () + self.help_ff ((src1_data,), + expected_result, op) + + def test_div_ff_2 (self): + src1_data = ( 5, 9, -15, 1024) + src2_data = (10, 3, -5, 64) + expected_result = (0.5, 3, 3, 16) + op = gr.divide_ff () + self.help_ff ((src1_data, src2_data), + expected_result, op) + + +if __name__ == '__main__': + gr_unittest.main () diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_add_v_and_friends.py b/gnuradio-core/src/python/gnuradio/gr/qa_add_v_and_friends.py new file mode 100755 index 000000000..11e69e373 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_add_v_and_friends.py @@ -0,0 +1,353 @@ +#!/usr/bin/env python +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest + +class test_add_v_and_friends(gr_unittest.TestCase): + + def setUp(self): + self.fg = gr.flow_graph() + + def tearDown(self): + self.fg = None + + def help_ss(self, size, src_data, exp_data, op): + for s in zip(range (len (src_data)), src_data): + src = gr.vector_source_s(s[1]) + srcv = gr.stream_to_vector(gr.sizeof_short, size) + self.fg.connect(src, srcv) + self.fg.connect(srcv, (op, s[0])) + rhs = gr.vector_to_stream(gr.sizeof_short, size) + dst = gr.vector_sink_s() + self.fg.connect(op, rhs, dst) + self.fg.run() + result_data = dst.data() + self.assertEqual(exp_data, result_data) + + def help_ii(self, size, src_data, exp_data, op): + for s in zip(range (len (src_data)), src_data): + src = gr.vector_source_i(s[1]) + srcv = gr.stream_to_vector(gr.sizeof_int, size) + self.fg.connect(src, srcv) + self.fg.connect(srcv, (op, s[0])) + rhs = gr.vector_to_stream(gr.sizeof_int, size) + dst = gr.vector_sink_i() + self.fg.connect(op, rhs, dst) + self.fg.run() + result_data = dst.data() + self.assertEqual(exp_data, result_data) + + def help_ff(self, size, src_data, exp_data, op): + for s in zip(range (len (src_data)), src_data): + src = gr.vector_source_f(s[1]) + srcv = gr.stream_to_vector(gr.sizeof_float, size) + self.fg.connect(src, srcv) + self.fg.connect(srcv, (op, s[0])) + rhs = gr.vector_to_stream(gr.sizeof_float, size) + dst = gr.vector_sink_f() + self.fg.connect(op, rhs, dst) + self.fg.run() + result_data = dst.data() + self.assertEqual(exp_data, result_data) + + def help_cc(self, size, src_data, exp_data, op): + for s in zip(range (len (src_data)), src_data): + src = gr.vector_source_c(s[1]) + srcv = gr.stream_to_vector(gr.sizeof_gr_complex, size) + self.fg.connect(src, srcv) + self.fg.connect(srcv, (op, s[0])) + rhs = gr.vector_to_stream(gr.sizeof_gr_complex, size) + dst = gr.vector_sink_c() + self.fg.connect(op, rhs, dst) + self.fg.run() + result_data = dst.data() + self.assertEqual(exp_data, result_data) + + def help_const_ss(self, src_data, exp_data, op): + src = gr.vector_source_s(src_data) + srcv = gr.stream_to_vector(gr.sizeof_short, len(src_data)) + rhs = gr.vector_to_stream(gr.sizeof_short, len(src_data)) + dst = gr.vector_sink_s() + self.fg.connect(src, srcv, op, rhs, dst) + self.fg.run() + result_data = dst.data() + self.assertEqual(exp_data, result_data) + + def help_const_ii(self, src_data, exp_data, op): + src = gr.vector_source_i(src_data) + srcv = gr.stream_to_vector(gr.sizeof_int, len(src_data)) + rhs = gr.vector_to_stream(gr.sizeof_int, len(src_data)) + dst = gr.vector_sink_i() + self.fg.connect(src, srcv, op, rhs, dst) + self.fg.run() + result_data = dst.data() + self.assertEqual(exp_data, result_data) + + def help_const_ff(self, src_data, exp_data, op): + src = gr.vector_source_f(src_data) + srcv = gr.stream_to_vector(gr.sizeof_float, len(src_data)) + rhs = gr.vector_to_stream(gr.sizeof_float, len(src_data)) + dst = gr.vector_sink_f() + self.fg.connect(src, srcv, op, rhs, dst) + self.fg.run() + result_data = dst.data() + self.assertEqual(exp_data, result_data) + + def help_const_cc(self, src_data, exp_data, op): + src = gr.vector_source_c(src_data) + srcv = gr.stream_to_vector(gr.sizeof_gr_complex, len(src_data)) + rhs = gr.vector_to_stream(gr.sizeof_gr_complex, len(src_data)) + dst = gr.vector_sink_c() + self.fg.connect(src, srcv, op, rhs, dst) + self.fg.run() + result_data = dst.data() + self.assertEqual(exp_data, result_data) + + + def test_add_vss_one(self): + src1_data = (1,) + src2_data = (2,) + src3_data = (3,) + expected_result = (6,) + op = gr.add_vss(1) + self.help_ss(1, (src1_data, src2_data, src3_data), expected_result, op) + + def test_add_vss_five(self): + src1_data = (1, 2, 3, 4, 5) + src2_data = (6, 7, 8, 9, 10) + src3_data = (11, 12, 13, 14, 15) + expected_result = (18, 21, 24, 27, 30) + op = gr.add_vss(5) + self.help_ss(5, (src1_data, src2_data, src3_data), expected_result, op) + + def test_add_vii_one(self): + src1_data = (1,) + src2_data = (2,) + src3_data = (3,) + expected_result = (6,) + op = gr.add_vii(1) + self.help_ii(1, (src1_data, src2_data, src3_data), expected_result, op) + + def test_add_vii_five(self): + src1_data = (1, 2, 3, 4, 5) + src2_data = (6, 7, 8, 9, 10) + src3_data = (11, 12, 13, 14, 15) + expected_result = (18, 21, 24, 27, 30) + op = gr.add_vii(5) + self.help_ii(5, (src1_data, src2_data, src3_data), expected_result, op) + + def test_add_vff_one(self): + src1_data = (1.0,) + src2_data = (2.0,) + src3_data = (3.0,) + expected_result = (6.0,) + op = gr.add_vff(1) + self.help_ff(1, (src1_data, src2_data, src3_data), expected_result, op) + + def test_add_vff_five(self): + src1_data = (1.0, 2.0, 3.0, 4.0, 5.0) + src2_data = (6.0, 7.0, 8.0, 9.0, 10.0) + src3_data = (11.0, 12.0, 13.0, 14.0, 15.0) + expected_result = (18.0, 21.0, 24.0, 27.0, 30.0) + op = gr.add_vff(5) + self.help_ff(5, (src1_data, src2_data, src3_data), expected_result, op) + + def test_add_vcc_one(self): + src1_data = (1.0+2.0j,) + src2_data = (3.0+4.0j,) + src3_data = (5.0+6.0j,) + expected_result = (9.0+12j,) + op = gr.add_vcc(1) + self.help_cc(1, (src1_data, src2_data, src3_data), expected_result, op) + + def test_add_vcc_five(self): + src1_data = (1.0+2.0j, 3.0+4.0j, 5.0+6.0j, 7.0+8.0j, 9.0+10.0j) + src2_data = (11.0+12.0j, 13.0+14.0j, 15.0+16.0j, 17.0+18.0j, 19.0+20.0j) + src3_data = (21.0+22.0j, 23.0+24.0j, 25.0+26.0j, 27.0+28.0j, 29.0+30.0j) + expected_result = (33.0+36.0j, 39.0+42.0j, 45.0+48.0j, 51.0+54.0j, 57.0+60.0j) + op = gr.add_vcc(5) + self.help_cc(5, (src1_data, src2_data, src3_data), expected_result, op) + + def test_add_const_vss_one(self): + src_data = (1,) + op = gr.add_const_vss((2,)) + exp_data = (3,) + self.help_const_ss(src_data, exp_data, op) + + def test_add_const_vss_five(self): + src_data = (1, 2, 3, 4, 5) + op = gr.add_const_vss((6, 7, 8, 9, 10)) + exp_data = (7, 9, 11, 13, 15) + self.help_const_ss(src_data, exp_data, op) + + def test_add_const_vii_one(self): + src_data = (1,) + op = gr.add_const_vii((2,)) + exp_data = (3,) + self.help_const_ii(src_data, exp_data, op) + + def test_add_const_vii_five(self): + src_data = (1, 2, 3, 4, 5) + op = gr.add_const_vii((6, 7, 8, 9, 10)) + exp_data = (7, 9, 11, 13, 15) + self.help_const_ii(src_data, exp_data, op) + + def test_add_const_vff_one(self): + src_data = (1.0,) + op = gr.add_const_vff((2.0,)) + exp_data = (3.0,) + self.help_const_ff(src_data, exp_data, op) + + def test_add_const_vff_five(self): + src_data = (1.0, 2.0, 3.0, 4.0, 5.0) + op = gr.add_const_vff((6.0, 7.0, 8.0, 9.0, 10.0)) + exp_data = (7.0, 9.0, 11.0, 13.0, 15.0) + self.help_const_ff(src_data, exp_data, op) + + def test_add_const_vcc_one(self): + src_data = (1.0+2.0j,) + op = gr.add_const_vcc((2.0+3.0j,)) + exp_data = (3.0+5.0j,) + self.help_const_cc(src_data, exp_data, op) + + def test_add_const_vcc_five(self): + src_data = (1.0+2.0j, 3.0+4.0j, 5.0+6.0j, 7.0+8.0j, 9.0+10.0j) + op = gr.add_const_vcc((11.0+12.0j, 13.0+14.0j, 15.0+16.0j, 17.0+18.0j, 19.0+20.0j)) + exp_data = (12.0+14.0j, 16.0+18.0j, 20.0+22.0j, 24.0+26.0j, 28.0+30.0j) + self.help_const_cc(src_data, exp_data, op) + + + def test_multiply_vss_one(self): + src1_data = (1,) + src2_data = (2,) + src3_data = (3,) + expected_result = (6,) + op = gr.multiply_vss(1) + self.help_ss(1, (src1_data, src2_data, src3_data), expected_result, op) + + def test_multiply_vss_five(self): + src1_data = (1, 2, 3, 4, 5) + src2_data = (6, 7, 8, 9, 10) + src3_data = (11, 12, 13, 14, 15) + expected_result = (66, 168, 312, 504, 750) + op = gr.multiply_vss(5) + self.help_ss(5, (src1_data, src2_data, src3_data), expected_result, op) + + def test_multiply_vii_one(self): + src1_data = (1,) + src2_data = (2,) + src3_data = (3,) + expected_result = (6,) + op = gr.multiply_vii(1) + self.help_ii(1, (src1_data, src2_data, src3_data), expected_result, op) + + def test_multiply_vii_five(self): + src1_data = (1, 2, 3, 4, 5) + src2_data = (6, 7, 8, 9, 10) + src3_data = (11, 12, 13, 14, 15) + expected_result = (66, 168, 312, 504, 750) + op = gr.multiply_vii(5) + self.help_ii(5, (src1_data, src2_data, src3_data), expected_result, op) + + def test_multiply_vff_one(self): + src1_data = (1.0,) + src2_data = (2.0,) + src3_data = (3.0,) + expected_result = (6.0,) + op = gr.multiply_vff(1) + self.help_ff(1, (src1_data, src2_data, src3_data), expected_result, op) + + def test_multiply_vff_five(self): + src1_data = (1.0, 2.0, 3.0, 4.0, 5.0) + src2_data = (6.0, 7.0, 8.0, 9.0, 10.0) + src3_data = (11.0, 12.0, 13.0, 14.0, 15.0) + expected_result = (66.0, 168.0, 312.0, 504.0, 750.0) + op = gr.multiply_vff(5) + self.help_ff(5, (src1_data, src2_data, src3_data), expected_result, op) + + def test_multiply_vcc_one(self): + src1_data = (1.0+2.0j,) + src2_data = (3.0+4.0j,) + src3_data = (5.0+6.0j,) + expected_result = (-85+20j,) + op = gr.multiply_vcc(1) + self.help_cc(1, (src1_data, src2_data, src3_data), expected_result, op) + + def test_multiply_vcc_five(self): + src1_data = (1.0+2.0j, 3.0+4.0j, 5.0+6.0j, 7.0+8.0j, 9.0+10.0j) + src2_data = (11.0+12.0j, 13.0+14.0j, 15.0+16.0j, 17.0+18.0j, 19.0+20.0j) + src3_data = (21.0+22.0j, 23.0+24.0j, 25.0+26.0j, 27.0+28.0j, 29.0+30.0j) + expected_result = (-1021.0+428.0j, -2647.0+1754.0j, -4945.0+3704.0j, -8011.0+6374.0j, -11941.0+9860.0j) + op = gr.multiply_vcc(5) + self.help_cc(5, (src1_data, src2_data, src3_data), expected_result, op) + + def test_multiply_const_vss_one(self): + src_data = (2,) + op = gr.multiply_const_vss((3,)) + exp_data = (6,) + self.help_const_ss(src_data, exp_data, op) + + def test_multiply_const_vss_five(self): + src_data = (1, 2, 3, 4, 5) + op = gr.multiply_const_vss((6, 7, 8, 9, 10)) + exp_data = (6, 14, 24, 36, 50) + self.help_const_ss(src_data, exp_data, op) + + def test_multiply_const_vii_one(self): + src_data = (2,) + op = gr.multiply_const_vii((3,)) + exp_data = (6,) + self.help_const_ii(src_data, exp_data, op) + + def test_multiply_const_vii_five(self): + src_data = (1, 2, 3, 4, 5) + op = gr.multiply_const_vii((6, 7, 8, 9, 10)) + exp_data = (6, 14, 24, 36, 50) + self.help_const_ii(src_data, exp_data, op) + + def test_multiply_const_vff_one(self): + src_data = (2.0,) + op = gr.multiply_const_vff((3.0,)) + exp_data = (6.0,) + self.help_const_ff(src_data, exp_data, op) + + def test_multiply_const_vff_five(self): + src_data = (1.0, 2.0, 3.0, 4.0, 5.0) + op = gr.multiply_const_vff((6.0, 7.0, 8.0, 9.0, 10.0)) + exp_data = (6.0, 14.0, 24.0, 36.0, 50.0) + self.help_const_ff(src_data, exp_data, op) + + def test_multiply_const_vcc_one(self): + src_data = (1.0+2.0j,) + op = gr.multiply_const_vcc((2.0+3.0j,)) + exp_data = (-4.0+7.0j,) + self.help_const_cc(src_data, exp_data, op) + + def test_multiply_const_vcc_five(self): + src_data = (1.0+2.0j, 3.0+4.0j, 5.0+6.0j, 7.0+8.0j, 9.0+10.0j) + op = gr.multiply_const_vcc((11.0+12.0j, 13.0+14.0j, 15.0+16.0j, 17.0+18.0j, 19.0+20.0j)) + exp_data = (-13.0+34.0j, -17.0+94.0j, -21.0+170.0j, -25.0+262.0j, -29.0+370.0j) + self.help_const_cc(src_data, exp_data, op) + + +if __name__ == '__main__': + gr_unittest.main () diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_basic_flow_graph.py b/gnuradio-core/src/python/gnuradio/gr/qa_basic_flow_graph.py new file mode 100755 index 000000000..0799c72e4 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_basic_flow_graph.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python + +from gnuradio import gr, gr_unittest + + +# ---------------------------------------------------------------- + + +class test_basic_flow_graph (gr_unittest.TestCase): + + def setUp (self): + self.fg = gr.basic_flow_graph () + + def tearDown (self): + self.fg = None + + def test_000_create_delete (self): + pass + + def test_001a_insert_1 (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + fg.connect (gr.endpoint (src1, 0), gr.endpoint (dst1, 0)) + + def test_001b_insert_1 (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + fg.connect (src1, gr.endpoint (dst1, 0)) + + def test_001c_insert_1 (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + fg.connect (gr.endpoint (src1, 0), dst1) + + def test_001d_insert_1 (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + fg.connect (src1, dst1) + + def test_001e_insert_1 (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + fg.connect ((src1, 0), (dst1, 0)) + + def test_002_dst_in_use (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + src2 = gr.null_source (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + fg.connect ((src1, 0), (dst1, 0)) + self.assertRaises (ValueError, + lambda : fg.connect ((src2, 0), + (dst1, 0))) + + def test_003_no_such_src_port (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + self.assertRaises (ValueError, + lambda : fg.connect ((src1, 1), + (dst1, 0))) + + def test_004_no_such_dst_port (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + self.assertRaises (ValueError, + lambda : fg.connect ((src1, 0), + (dst1, 1))) + + def test_005_one_src_two_dst (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + dst2 = gr.null_sink (gr.sizeof_int) + fg.connect ((src1, 0), (dst1, 0)) + fg.connect ((src1, 0), (dst2, 0)) + + def test_006_check_item_sizes (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_char) + self.assertRaises (ValueError, + lambda : fg.connect ((src1, 0), + (dst1, 0))) + + def test_007_validate (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + dst2 = gr.null_sink (gr.sizeof_int) + fg.connect ((src1, 0), (dst1, 0)) + fg.connect ((src1, 0), (dst2, 0)) + fg.validate () + + def test_008_validate (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + nop1 = gr.nop (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + dst2 = gr.null_sink (gr.sizeof_int) + fg.connect ((src1, 0), (nop1, 0)) + fg.connect ((src1, 0), (nop1, 1)) + fg.connect ((nop1, 0), (dst1, 0)) + fg.connect ((nop1, 1), (dst2, 0)) + fg.validate () + + def test_009_validate (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + nop1 = gr.nop (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + dst2 = gr.null_sink (gr.sizeof_int) + fg.connect ((src1, 0), (nop1, 0)) + fg.connect ((src1, 0), (nop1, 2)) + fg.connect ((nop1, 0), (dst1, 0)) + fg.connect ((nop1, 1), (dst2, 0)) + self.assertRaises (ValueError, + lambda : fg.validate ()) + + + def test_010_validate (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + nop1 = gr.nop (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + dst2 = gr.null_sink (gr.sizeof_int) + fg.connect ((src1, 0), (nop1, 0)) + fg.connect ((src1, 0), (nop1, 1)) + fg.connect ((nop1, 0), (dst1, 0)) + fg.connect ((nop1, 2), (dst2, 0)) + self.assertRaises (ValueError, + lambda : fg.validate ()) + + + def test_011_disconnect (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + nop1 = gr.nop (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + dst2 = gr.null_sink (gr.sizeof_int) + fg.connect ((src1, 0), (nop1, 0)) + fg.connect ((src1, 0), (nop1, 1)) + fg.connect ((nop1, 0), (dst1, 0)) + fg.connect ((nop1, 1), (dst2, 0)) + fg.validate () + fg.disconnect ((src1, 0), (nop1, 1)) + fg.validate () + self.assertRaises (ValueError, + lambda : fg.disconnect ((src1, 0), + (nop1, 1))) + + def test_012_connect (self): + fg = self.fg + src_data = (0, 1, 2, 3) + expected_result = (2, 3, 4, 5) + src1 = gr.vector_source_i (src_data) + op = gr.add_const_ii (2) + dst1 = gr.vector_sink_i () + fg.connect (src1, op) + fg.connect (op, dst1) + # print "edge_list:", fg.edge_list + fg.validate () + + def test_013_connect_varargs (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + nop1 = gr.nop (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + self.assertRaises (ValueError, + lambda : fg.connect ()) + self.assertRaises (ValueError, + lambda : fg.connect (src1)) + + def test_014_connect_varargs (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + nop1 = gr.nop (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + fg.connect (src1, nop1, dst1) + fg.validate () + +if __name__ == '__main__': + gr_unittest.main () + diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_cma_equalizer.py b/gnuradio-core/src/python/gnuradio/gr/qa_cma_equalizer.py new file mode 100755 index 000000000..6b3e9aa9e --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_cma_equalizer.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +from gnuradio import gr, gr_unittest + +class test_cma_equalizer_fir(gr_unittest.TestCase): + + def setUp(self): + self.fg = gr.flow_graph() + + def tearDown(self): + self.fg = None + + def transform(self, src_data): + SRC = gr.vector_source_c(src_data, False) + EQU = gr.cma_equalizer_cc(4, 1.0, .001) + DST = gr.vector_sink_c() + self.fg.connect(SRC, EQU, DST) + self.fg.run() + return DST.data() + + def test_001_identity(self): + # Constant modulus signal so no adjustments + src_data = (1+0j, 0+1j, -1+0j, 0-1j)*1000 + expected_data = src_data + result = self.transform(src_data) + self.assertComplexTuplesAlmostEqual(expected_data, result) + +if __name__ == "__main__": + gr_unittest.main()
\ No newline at end of file diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_complex_to_xxx.py b/gnuradio-core/src/python/gnuradio/gr/qa_complex_to_xxx.py new file mode 100755 index 000000000..4bc193350 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_complex_to_xxx.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest +import math + +class test_complex_ops (gr_unittest.TestCase): + + def setUp (self): + self.fg = gr.flow_graph () + + def tearDown (self): + self.fg = None + + def test_complex_to_float_1 (self): + src_data = (0, 1, -1, 3+4j, -3-4j, -3+4j) + expected_result = (0, 1, -1, 3, -3, -3) + src = gr.vector_source_c (src_data) + op = gr.complex_to_float () + dst = gr.vector_sink_f () + self.fg.connect (src, op) + self.fg.connect (op, dst) + self.fg.run () # run the graph and wait for it to finish + actual_result = dst.data () # fetch the contents of the sink + self.assertFloatTuplesAlmostEqual (expected_result, actual_result) + + def test_complex_to_float_2 (self): + src_data = (0, 1, -1, 3+4j, -3-4j, -3+4j) + expected_result0 = (0, 1, -1, 3, -3, -3) + expected_result1 = (0, 0, 0, 4, -4, 4) + src = gr.vector_source_c (src_data) + op = gr.complex_to_float () + dst0 = gr.vector_sink_f () + dst1 = gr.vector_sink_f () + self.fg.connect (src, op) + self.fg.connect ((op, 0), dst0) + self.fg.connect ((op, 1), dst1) + self.fg.run () + actual_result = dst0.data () + self.assertFloatTuplesAlmostEqual (expected_result0, actual_result) + actual_result = dst1.data () + self.assertFloatTuplesAlmostEqual (expected_result1, actual_result) + + def test_complex_to_real (self): + src_data = (0, 1, -1, 3+4j, -3-4j, -3+4j) + expected_result = (0, 1, -1, 3, -3, -3) + src = gr.vector_source_c (src_data) + op = gr.complex_to_real () + dst = gr.vector_sink_f () + self.fg.connect (src, op) + self.fg.connect (op, dst) + self.fg.run () + actual_result = dst.data () + self.assertFloatTuplesAlmostEqual (expected_result, actual_result) + + def test_complex_to_imag (self): + src_data = (0, 1, -1, 3+4j, -3-4j, -3+4j) + expected_result = (0, 0, 0, 4, -4, 4) + src = gr.vector_source_c (src_data) + op = gr.complex_to_imag () + dst = gr.vector_sink_f () + self.fg.connect (src, op) + self.fg.connect (op, dst) + self.fg.run () + actual_result = dst.data () + self.assertFloatTuplesAlmostEqual (expected_result, actual_result,5) + + def test_complex_to_mag (self): + src_data = (0, 1, -1, 3+4j, -3-4j, -3+4j) + expected_result = (0, 1, 1, 5, 5, 5) + src = gr.vector_source_c (src_data) + op = gr.complex_to_mag () + dst = gr.vector_sink_f () + self.fg.connect (src, op) + self.fg.connect (op, dst) + self.fg.run () + actual_result = dst.data () + self.assertFloatTuplesAlmostEqual (expected_result, actual_result,5) + + def test_complex_to_arg (self): + pi = math.pi + expected_result = (0, pi/6, pi/4, pi/2, 3*pi/4, 7*pi/8, + -pi/6, -pi/4, -pi/2, -3*pi/4, -7*pi/8) + src_data = tuple ([math.cos (x) + math.sin (x) * 1j for x in expected_result]) + src = gr.vector_source_c (src_data) + op = gr.complex_to_arg () + dst = gr.vector_sink_f () + self.fg.connect (src, op) + self.fg.connect (op, dst) + self.fg.run () + actual_result = dst.data () + self.assertFloatTuplesAlmostEqual (expected_result, actual_result, 5) + + +if __name__ == '__main__': + gr_unittest.main () + diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_constellation_decoder_cb.py b/gnuradio-core/src/python/gnuradio/gr/qa_constellation_decoder_cb.py new file mode 100755 index 000000000..956412228 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_constellation_decoder_cb.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest +import math + +class test_head (gr_unittest.TestCase): + + def setUp (self): + self.fg = gr.flow_graph () + + def tearDown (self): + self.fg = None + + def test_constellation_decoder_cb (self): + symbol_positions = [1 + 0j, 0 + 1j , -1 + 0j, 0 - 1j] + symbol_values_out = [0, 1, 2, 3] + expected_result = ( 0, 3, 2, 1, 0, 0, 3) + src_data = (0.5 + 0j, 0.1 - 1.2j, -0.8 - 0.1j, -0.45 + 0.8j, 0.8 - 0j, 0.5 + 0j, 0.1 - 1.2j) + src = gr.vector_source_c (src_data) + op = gr.constellation_decoder_cb (symbol_positions, symbol_values_out) + dst = gr.vector_sink_b () + self.fg.connect (src, op) + self.fg.connect (op, dst) + self.fg.run () # run the graph and wait for it to finish + actual_result = dst.data () # fetch the contents of the sink + #print "actual result", actual_result + #print "expected result", expected_result + self.assertFloatTuplesAlmostEqual (expected_result, actual_result) + + +if __name__ == '__main__': + gr_unittest.main () + diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_correlate_access_code.py b/gnuradio-core/src/python/gnuradio/gr/qa_correlate_access_code.py new file mode 100755 index 000000000..89b4909eb --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_correlate_access_code.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# +# Copyright 2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest +import math + +default_access_code = '\xAC\xDD\xA4\xE2\xF2\x8C\x20\xFC' + +def string_to_1_0_list(s): + r = [] + for ch in s: + x = ord(ch) + for i in range(8): + t = (x >> i) & 0x1 + r.append(t) + + return r + +def to_1_0_string(L): + return ''.join(map(lambda x: chr(x + ord('0')), L)) + +class test_correlate_access_code(gr_unittest.TestCase): + + def setUp(self): + self.fg = gr.flow_graph() + + def tearDown(self): + self.fg = None + + def test_001(self): + pad = (0,) * 64 + # 0 0 0 1 0 0 0 1 + src_data = (1, 0, 1, 1, 1, 1, 0, 1, 1) + pad + (0,) * 7 + expected_result = pad + (1, 0, 1, 1, 3, 1, 0, 1, 1, 2) + (0,) * 6 + src = gr.vector_source_b (src_data) + op = gr.correlate_access_code_bb("1011", 0) + dst = gr.vector_sink_b () + self.fg.connect (src, op, dst) + self.fg.run () + result_data = dst.data () + self.assertEqual (expected_result, result_data) + + + def test_002(self): + code = tuple(string_to_1_0_list(default_access_code)) + access_code = to_1_0_string(code) + pad = (0,) * 64 + #print code + #print access_code + src_data = code + (1, 0, 1, 1) + pad + expected_result = pad + code + (3, 0, 1, 1) + src = gr.vector_source_b (src_data) + op = gr.correlate_access_code_bb(access_code, 0) + dst = gr.vector_sink_b () + self.fg.connect (src, op, dst) + self.fg.run () + result_data = dst.data () + self.assertEqual (expected_result, result_data) + + + +if __name__ == '__main__': + gr_unittest.main () + diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_diff_encoder.py b/gnuradio-core/src/python/gnuradio/gr/qa_diff_encoder.py new file mode 100755 index 000000000..44840709b --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_diff_encoder.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python +# +# Copyright 2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest +import math +import random + +def make_random_int_tuple(L, min, max): + result = [] + for x in range(L): + result.append(random.randint(min, max)) + return tuple(result) + + +class test_encoder (gr_unittest.TestCase): + + def setUp (self): + self.fg = gr.flow_graph () + + def tearDown (self): + self.fg = None + + def test_diff_encdec_000(self): + random.seed(0) + modulus = 2 + src_data = make_random_int_tuple(1000, 0, modulus-1) + expected_result = src_data + src = gr.vector_source_b(src_data) + enc = gr.diff_encoder_bb(modulus) + dec = gr.diff_decoder_bb(modulus) + dst = gr.vector_sink_b() + self.fg.connect(src, enc, dec, dst) + self.fg.run() # run the graph and wait for it to finish + actual_result = dst.data() # fetch the contents of the sink + self.assertEqual(expected_result, actual_result) + + def test_diff_encdec_001(self): + random.seed(0) + modulus = 4 + src_data = make_random_int_tuple(1000, 0, modulus-1) + expected_result = src_data + src = gr.vector_source_b(src_data) + enc = gr.diff_encoder_bb(modulus) + dec = gr.diff_decoder_bb(modulus) + dst = gr.vector_sink_b() + self.fg.connect(src, enc, dec, dst) + self.fg.run() # run the graph and wait for it to finish + actual_result = dst.data() # fetch the contents of the sink + self.assertEqual(expected_result, actual_result) + + def test_diff_encdec_002(self): + random.seed(0) + modulus = 8 + src_data = make_random_int_tuple(40000, 0, modulus-1) + expected_result = src_data + src = gr.vector_source_b(src_data) + enc = gr.diff_encoder_bb(modulus) + dec = gr.diff_decoder_bb(modulus) + dst = gr.vector_sink_b() + self.fg.connect(src, enc, dec, dst) + self.fg.run() # run the graph and wait for it to finish + actual_result = dst.data() # fetch the contents of the sink + self.assertEqual(expected_result, actual_result) + +if __name__ == '__main__': + gr_unittest.main () + diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_diff_phasor_cc.py b/gnuradio-core/src/python/gnuradio/gr/qa_diff_phasor_cc.py new file mode 100755 index 000000000..0b7160202 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_diff_phasor_cc.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest +import math + +class test_complex_ops (gr_unittest.TestCase): + + def setUp (self): + self.fg = gr.flow_graph () + + def tearDown (self): + self.fg = None + + def test_diff_phasor_cc (self): + src_data = (0+0j, 1+0j, -1+0j, 3+4j, -3-4j, -3+4j) + expected_result = (0+0j, 0+0j, -1+0j, -3-4j, -25+0j, -7-24j) + src = gr.vector_source_c (src_data) + op = gr.diff_phasor_cc () + dst = gr.vector_sink_c () + self.fg.connect (src, op) + self.fg.connect (op, dst) + self.fg.run () # run the graph and wait for it to finish + actual_result = dst.data () # fetch the contents of the sink + self.assertComplexTuplesAlmostEqual (expected_result, actual_result) + + + +if __name__ == '__main__': + gr_unittest.main () + diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_feval.py b/gnuradio-core/src/python/gnuradio/gr/qa_feval.py new file mode 100755 index 000000000..1de49369a --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_feval.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python +# +# Copyright 2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest + +class my_add2_dd(gr.feval_dd): + def eval(self, x): + return x + 2 + +class my_add2_ll(gr.feval_ll): + def eval(self, x): + return x + 2 + +class my_add2_cc(gr.feval_cc): + def eval(self, x): + return x + (2 - 2j) + + +class test_feval(gr_unittest.TestCase): + + def test_dd_1(self): + f = my_add2_dd() + src_data = (0.0, 1.0, 2.0, 3.0, 4.0) + expected_result = (2.0, 3.0, 4.0, 5.0, 6.0) + # this is all in python... + actual_result = tuple([f.eval(x) for x in src_data]) + self.assertEqual(expected_result, actual_result) + + def test_dd_2(self): + f = my_add2_dd() + src_data = (0.0, 1.0, 2.0, 3.0, 4.0) + expected_result = (2.0, 3.0, 4.0, 5.0, 6.0) + # this is python -> C++ -> python and back again... + actual_result = tuple([gr.feval_dd_example(f, x) for x in src_data]) + self.assertEqual(expected_result, actual_result) + + + def test_ll_1(self): + f = my_add2_ll() + src_data = (0, 1, 2, 3, 4) + expected_result = (2, 3, 4, 5, 6) + # this is all in python... + actual_result = tuple([f.eval(x) for x in src_data]) + self.assertEqual(expected_result, actual_result) + + def test_ll_2(self): + f = my_add2_ll() + src_data = (0, 1, 2, 3, 4) + expected_result = (2, 3, 4, 5, 6) + # this is python -> C++ -> python and back again... + actual_result = tuple([gr.feval_ll_example(f, x) for x in src_data]) + self.assertEqual(expected_result, actual_result) + + + def test_cc_1(self): + f = my_add2_cc() + src_data = (0+1j, 2+3j, 4+5j, 6+7j) + expected_result = (2-1j, 4+1j, 6+3j, 8+5j) + # this is all in python... + actual_result = tuple([f.eval(x) for x in src_data]) + self.assertEqual(expected_result, actual_result) + + def test_cc_2(self): + f = my_add2_cc() + src_data = (0+1j, 2+3j, 4+5j, 6+7j) + expected_result = (2-1j, 4+1j, 6+3j, 8+5j) + # this is python -> C++ -> python and back again... + actual_result = tuple([gr.feval_cc_example(f, x) for x in src_data]) + self.assertEqual(expected_result, actual_result) + + +if __name__ == '__main__': + gr_unittest.main () diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_fft_filter.py b/gnuradio-core/src/python/gnuradio/gr/qa_fft_filter.py new file mode 100755 index 000000000..cb2e76b18 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_fft_filter.py @@ -0,0 +1,268 @@ +#!/usr/bin/env python +# +# Copyright 2004,2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest +import sys +import random + +def make_random_complex_tuple(L): + result = [] + for x in range(L): + result.append(complex(random.uniform(-1000,1000), + random.uniform(-1000,1000))) + return tuple(result) + +def make_random_float_tuple(L): + result = [] + for x in range(L): + result.append(float(int(random.uniform(-1000,1000)))) + return tuple(result) + + +def reference_filter_ccc(dec, taps, input): + """ + compute result using conventional fir filter + """ + fg = gr.flow_graph() + #src = gr.vector_source_c(((0,) * (len(taps) - 1)) + input) + src = gr.vector_source_c(input) + op = gr.fir_filter_ccc(dec, taps) + dst = gr.vector_sink_c() + fg.connect(src, op, dst) + fg.run() + return dst.data() + +def reference_filter_fff(dec, taps, input): + """ + compute result using conventional fir filter + """ + fg = gr.flow_graph() + #src = gr.vector_source_f(((0,) * (len(taps) - 1)) + input) + src = gr.vector_source_f(input) + op = gr.fir_filter_fff(dec, taps) + dst = gr.vector_sink_f() + fg.connect(src, op, dst) + fg.run() + return dst.data() + + +def print_complex(x): + for i in x: + i = complex(i) + sys.stdout.write("(%6.3f,%6.3fj), " % (i.real, i.imag)) + sys.stdout.write('\n') + + +class test_fft_filter(gr_unittest.TestCase): + + def setUp(self): + self.fg = gr.flow_graph () + + def tearDown(self): + self.fg = None + + def assert_fft_ok2(self, expected_result, result_data): + expected_result = expected_result[:len(result_data)] + self.assertComplexTuplesAlmostEqual2 (expected_result, result_data, + abs_eps=1e-9, rel_eps=4e-4) + + def assert_fft_float_ok2(self, expected_result, result_data, abs_eps=1e-9, rel_eps=4e-4): + expected_result = expected_result[:len(result_data)] + self.assertFloatTuplesAlmostEqual2 (expected_result, result_data, + abs_eps, rel_eps) + + #def test_ccc_000(self): + # self.assertRaises (RuntimeError, gr.fft_filter_ccc, 2, (1,)) + + def test_ccc_001(self): + src_data = (0,1,2,3,4,5,6,7) + taps = (1,) + expected_result = tuple([complex(x) for x in (0,1,2,3,4,5,6,7)]) + src = gr.vector_source_c(src_data) + op = gr.fft_filter_ccc(1, taps) + dst = gr.vector_sink_c() + self.fg.connect(src, op, dst) + self.fg.run() + result_data = dst.data() + #print 'expected:', expected_result + #print 'results: ', result_data + self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5) + + + def test_ccc_002(self): + src_data = (0,1,2,3,4,5,6,7) + taps = (2,) + expected_result = tuple([2 * complex(x) for x in (0,1,2,3,4,5,6,7)]) + src = gr.vector_source_c(src_data) + op = gr.fft_filter_ccc(1, taps) + dst = gr.vector_sink_c() + self.fg.connect(src, op, dst) + self.fg.run() + result_data = dst.data() + #print 'expected:', expected_result + #print 'results: ', result_data + self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5) + + def test_ccc_004(self): + random.seed(0) + for i in xrange(25): + # sys.stderr.write("\n>>> Loop = %d\n" % (i,)) + src_len = 4*1024 + src_data = make_random_complex_tuple(src_len) + ntaps = int(random.uniform(2, 1000)) + taps = make_random_complex_tuple(ntaps) + expected_result = reference_filter_ccc(1, taps, src_data) + + src = gr.vector_source_c(src_data) + op = gr.fft_filter_ccc(1, taps) + dst = gr.vector_sink_c() + self.fg.connect(src, op, dst) + self.fg.run() + result_data = dst.data() + + self.assert_fft_ok2(expected_result, result_data) + + def test_ccc_005(self): + random.seed(0) + for i in xrange(25): + # sys.stderr.write("\n>>> Loop = %d\n" % (i,)) + dec = i + 1 + src_len = 4*1024 + src_data = make_random_complex_tuple(src_len) + ntaps = int(random.uniform(2, 100)) + taps = make_random_complex_tuple(ntaps) + expected_result = reference_filter_ccc(dec, taps, src_data) + + src = gr.vector_source_c(src_data) + op = gr.fft_filter_ccc(dec, taps) + dst = gr.vector_sink_c() + self.fg.connect(src, op, dst) + self.fg.run() + result_data = dst.data() + + self.assert_fft_ok2(expected_result, result_data) + + # ---------------------------------------------------------------- + # test _fff version + # ---------------------------------------------------------------- + + def test_fff_001(self): + src_data = (0,1,2,3,4,5,6,7) + taps = (1,) + expected_result = tuple([float(x) for x in (0,1,2,3,4,5,6,7)]) + src = gr.vector_source_f(src_data) + op = gr.fft_filter_fff(1, taps) + dst = gr.vector_sink_f() + self.fg.connect(src, op, dst) + self.fg.run() + result_data = dst.data() + #print 'expected:', expected_result + #print 'results: ', result_data + self.assertFloatTuplesAlmostEqual (expected_result, result_data, 5) + + + def test_fff_002(self): + src_data = (0,1,2,3,4,5,6,7) + taps = (2,) + expected_result = tuple([2 * float(x) for x in (0,1,2,3,4,5,6,7)]) + src = gr.vector_source_f(src_data) + op = gr.fft_filter_fff(1, taps) + dst = gr.vector_sink_f() + self.fg.connect(src, op, dst) + self.fg.run() + result_data = dst.data() + #print 'expected:', expected_result + #print 'results: ', result_data + self.assertFloatTuplesAlmostEqual (expected_result, result_data, 5) + + def xtest_fff_003(self): + random.seed(0) + for i in xrange(25): + sys.stderr.write("\n>>> Loop = %d\n" % (i,)) + src_len = 4096 + src_data = make_random_float_tuple(src_len) + ntaps = int(random.uniform(2, 1000)) + taps = make_random_float_tuple(ntaps) + expected_result = reference_filter_fff(1, taps, src_data) + + src = gr.vector_source_f(src_data) + op = gr.fft_filter_fff(1, taps) + dst = gr.vector_sink_f() + self.fg.connect(src, op, dst) + self.fg.run() + result_data = dst.data() + + #print "src_len =", src_len, " ntaps =", ntaps + try: + self.assert_fft_float_ok2(expected_result, result_data, abs_eps=1.0) + except: + expected = open('expected', 'w') + for x in expected_result: + expected.write(`x` + '\n') + actual = open('actual', 'w') + for x in result_data: + actual.write(`x` + '\n') + raise + + def xtest_fff_004(self): + random.seed(0) + for i in xrange(25): + sys.stderr.write("\n>>> Loop = %d\n" % (i,)) + src_len = 4*1024 + src_data = make_random_float_tuple(src_len) + ntaps = int(random.uniform(2, 1000)) + taps = make_random_float_tuple(ntaps) + expected_result = reference_filter_fff(1, taps, src_data) + + src = gr.vector_source_f(src_data) + op = gr.fft_filter_fff(1, taps) + dst = gr.vector_sink_f() + self.fg.connect(src, op, dst) + self.fg.run() + result_data = dst.data() + + self.assert_fft_float_ok2(expected_result, result_data, abs_eps=2.0) + + def xtest_fff_005(self): + random.seed(0) + for i in xrange(25): + sys.stderr.write("\n>>> Loop = %d\n" % (i,)) + dec = i + 1 + src_len = 4*1024 + src_data = make_random_float_tuple(src_len) + ntaps = int(random.uniform(2, 100)) + taps = make_random_float_tuple(ntaps) + expected_result = reference_filter_fff(dec, taps, src_data) + + src = gr.vector_source_f(src_data) + op = gr.fft_filter_fff(dec, taps) + dst = gr.vector_sink_f() + self.fg.connect(src, op, dst) + self.fg.run() + result_data = dst.data() + + self.assert_fft_float_ok2(expected_result, result_data) + + +if __name__ == '__main__': + gr_unittest.main () + diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_filter_delay_fc.py b/gnuradio-core/src/python/gnuradio/gr/qa_filter_delay_fc.py new file mode 100755 index 000000000..191552ca2 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_filter_delay_fc.py @@ -0,0 +1,317 @@ +#!/usr/bin/env python +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest +import math + +class qa_filter_delay_fc (gr_unittest.TestCase): + + def setUp (self): + self.fg = gr.flow_graph () + + def tearDown (self): + self.fg = None + + def test_001_filter_delay_one_input (self): + + # expected result + expected_result = ( -1.4678005338941702e-11j, + -0.0011950774351134896j, + -0.0019336787518113852j, + -0.0034673355985432863j, + -0.0036765895783901215j, + -0.004916108213365078j, + -0.0042778430506587029j, + -0.006028641015291214j, + -0.005476709920912981j, + -0.0092810001224279404j, + -0.0095402700826525688j, + -0.016060983762145042j, + -0.016446959227323532j, + -0.02523401565849781j, + -0.024382550269365311j, + -0.035477779805660248j, + -0.033021725714206696j, + -0.048487484455108643j, + -0.04543270543217659j, + -0.069477587938308716j, + -0.066984444856643677j, + -0.10703597217798233j, + -0.10620346665382385j, + -0.1852707713842392j, + -0.19357112050056458j, + (7.2191945754696007e-09 -0.50004088878631592j), + (0.58778399229049683 -0.6155126690864563j), + (0.95105588436126709 -0.12377222627401352j), + (0.95105588436126709 +0.41524654626846313j), + (0.5877838134765625 +0.91611981391906738j), + (5.8516356205018383e-09 +1.0670661926269531j), + (-0.5877840518951416 +0.87856143712997437j), + (-0.95105588436126709 +0.35447561740875244j), + (-0.95105588436126709 -0.26055556535720825j), + (-0.5877838134765625 -0.77606213092803955j), + (-8.7774534307527574e-09 -0.96460390090942383j), + (0.58778399229049683 -0.78470128774642944j), + (0.95105588436126709 -0.28380891680717468j), + (0.95105588436126709 +0.32548999786376953j), + (0.5877838134765625 +0.82514488697052002j), + (1.4629089051254596e-08 +1.0096219778060913j), + (-0.5877840518951416 +0.81836479902267456j), + (-0.95105588436126709 +0.31451958417892456j), + (-0.95105588436126709 -0.3030143678188324j), + (-0.5877838134765625 -0.80480599403381348j), + (-1.7554906861505515e-08 -0.99516552686691284j), + (0.58778399229049683 -0.80540722608566284j), + (0.95105582475662231 -0.30557557940483093j), + (0.95105588436126709 +0.31097668409347534j), + (0.5877838134765625 +0.81027895212173462j), + (2.3406542482007353e-08 +1.0000816583633423j), + (-0.5877840518951416 +0.80908381938934326j), + (-0.95105588436126709 +0.30904293060302734j), + (-0.95105588436126709 -0.30904296040534973j), + (-0.5877838134765625 -0.80908387899398804j), + (-2.6332360292258272e-08 -1.0000815391540527j), + (0.58778399229049683 -0.80908381938934326j), + (0.95105582475662231 -0.30904299020767212j), + (0.95105588436126709 +0.30904293060302734j), + (0.5877838134765625 +0.80908381938934326j), + (3.218399768911695e-08 +1.0000815391540527j)) + + fg = self.fg + + sampling_freq = 100 + + ntaps = 51 + src1 = gr.sig_source_f (sampling_freq, gr.GR_SIN_WAVE, + sampling_freq * 0.10, 1.0) + head = gr.head (gr.sizeof_float, int (ntaps + sampling_freq * 0.10)) + dst2 = gr.vector_sink_c () + + # calculate taps + taps = gr.firdes_hilbert (ntaps) + hd = gr.filter_delay_fc (taps) + + fg.connect (src1, head) + fg.connect (head, hd) + fg.connect (hd,dst2) + + fg.run () + + # get output + result_data = dst2.data () + self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5) + + def test_002_filter_delay_two_inputs (self): + + # giving the same signal to both the inputs should fetch the same results + # as above + + # expected result + expected_result = ( -1.4678005338941702e-11j, + -0.0011950774351134896j, + -0.0019336787518113852j, + -0.0034673355985432863j, + -0.0036765895783901215j, + -0.004916108213365078j, + -0.0042778430506587029j, + -0.006028641015291214j, + -0.005476709920912981j, + -0.0092810001224279404j, + -0.0095402700826525688j, + -0.016060983762145042j, + -0.016446959227323532j, + -0.02523401565849781j, + -0.024382550269365311j, + -0.035477779805660248j, + -0.033021725714206696j, + -0.048487484455108643j, + -0.04543270543217659j, + -0.069477587938308716j, + -0.066984444856643677j, + -0.10703597217798233j, + -0.10620346665382385j, + -0.1852707713842392j, + -0.19357112050056458j, + (7.2191945754696007e-09 -0.50004088878631592j), + (0.58778399229049683 -0.6155126690864563j), + (0.95105588436126709 -0.12377222627401352j), + (0.95105588436126709 +0.41524654626846313j), + (0.5877838134765625 +0.91611981391906738j), + (5.8516356205018383e-09 +1.0670661926269531j), + (-0.5877840518951416 +0.87856143712997437j), + (-0.95105588436126709 +0.35447561740875244j), + (-0.95105588436126709 -0.26055556535720825j), + (-0.5877838134765625 -0.77606213092803955j), + (-8.7774534307527574e-09 -0.96460390090942383j), + (0.58778399229049683 -0.78470128774642944j), + (0.95105588436126709 -0.28380891680717468j), + (0.95105588436126709 +0.32548999786376953j), + (0.5877838134765625 +0.82514488697052002j), + (1.4629089051254596e-08 +1.0096219778060913j), + (-0.5877840518951416 +0.81836479902267456j), + (-0.95105588436126709 +0.31451958417892456j), + (-0.95105588436126709 -0.3030143678188324j), + (-0.5877838134765625 -0.80480599403381348j), + (-1.7554906861505515e-08 -0.99516552686691284j), + (0.58778399229049683 -0.80540722608566284j), + (0.95105582475662231 -0.30557557940483093j), + (0.95105588436126709 +0.31097668409347534j), + (0.5877838134765625 +0.81027895212173462j), + (2.3406542482007353e-08 +1.0000816583633423j), + (-0.5877840518951416 +0.80908381938934326j), + (-0.95105588436126709 +0.30904293060302734j), + (-0.95105588436126709 -0.30904296040534973j), + (-0.5877838134765625 -0.80908387899398804j), + (-2.6332360292258272e-08 -1.0000815391540527j), + (0.58778399229049683 -0.80908381938934326j), + (0.95105582475662231 -0.30904299020767212j), + (0.95105588436126709 +0.30904293060302734j), + (0.5877838134765625 +0.80908381938934326j), + (3.218399768911695e-08 +1.0000815391540527j)) + + + fg = self.fg + + sampling_freq = 100 + ntaps = 51 + src1 = gr.sig_source_f (sampling_freq, gr.GR_SIN_WAVE, + sampling_freq * 0.10, 1.0) + head = gr.head (gr.sizeof_float, int (ntaps + sampling_freq * 0.10)) + dst2 = gr.vector_sink_c () + + + # calculate taps + taps = gr.firdes_hilbert (ntaps) + hd = gr.filter_delay_fc (taps) + + fg.connect (src1, head) + fg.connect (head, (hd,0)) + fg.connect (head, (hd,1)) + fg.connect (hd,dst2) + fg.run () + + # get output + result_data = dst2.data () + + self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5) + + + def test_003_filter_delay_two_inputs (self): + + # give two different inputs + + # expected result + expected_result = ( -0.0020331963896751404j, + -0.0016448829555884004j, + -0.0032375147566199303j, + -0.0014826074475422502j, + -0.0033034090884029865j, + -0.00051144487224519253j, + -0.0043686260469257832j, + -0.0010198024101555347j, + -0.0082517862319946289j, + -0.003456643782556057j, + -0.014193611219525337j, + -0.005875137634575367j, + -0.020293503999710083j, + -0.0067503536120057106j, + -0.026798896491527557j, + -0.0073488112539052963j, + -0.037041611969470978j, + -0.010557252913713455j, + -0.055669989436864853j, + -0.018332764506340027j, + -0.089904911816120148j, + -0.033361352980136871j, + -0.16902604699134827j, + -0.074318811297416687j, + -0.58429563045501709j, + (7.2191945754696007e-09 -0.35892376303672791j), + (0.58778399229049683 +0.63660913705825806j), + (0.95105588436126709 +0.87681591510772705j), + (0.95105588436126709 +0.98705857992172241j), + (0.5877838134765625 +0.55447429418563843j), + (5.8516356205018383e-09 +0.026006083935499191j), + (-0.5877840518951416 -0.60616838932037354j), + (-0.95105588436126709 -0.9311758279800415j), + (-0.95105588436126709 -0.96169203519821167j), + (-0.5877838134765625 -0.57292771339416504j), + (-8.7774534307527574e-09 -0.0073488391935825348j), + (0.58778399229049683 +0.59720659255981445j), + (0.95105588436126709 +0.94438445568084717j), + (0.95105588436126709 +0.95582199096679688j), + (0.5877838134765625 +0.58196049928665161j), + (1.4629089051254596e-08 +0.0026587247848510742j), + (-0.5877840518951416 -0.59129220247268677j), + (-0.95105588436126709 -0.94841635227203369j), + (-0.95105588436126709 -0.95215457677841187j), + (-0.5877838134765625 -0.58535969257354736j), + (-1.7554906861505515e-08 -0.00051158666610717773j), + (0.58778399229049683 +0.58867418766021729j), + (0.95105582475662231 +0.94965213537216187j), + (0.95105588436126709 +0.95050644874572754j), + (0.5877838134765625 +0.58619076013565063j), + (2.3406542482007353e-08 +1.1920928955078125e-07j), + (-0.5877840518951416 -0.58783555030822754j), + (-0.95105588436126709 -0.95113480091094971j), + (-0.95105588436126709 -0.95113474130630493j), + (-0.5877838134765625 -0.58783555030822754j), + (-2.6332360292258272e-08 -8.1956386566162109e-08j), + (0.58778399229049683 +0.58783555030822754j), + (0.95105582475662231 +0.95113474130630493j), + (0.95105588436126709 +0.95113474130630493j), + (0.5877838134765625 +0.58783560991287231j), + (3.218399768911695e-08 +1.1920928955078125e-07j)) + + fg = self.fg + + sampling_freq = 100 + ntaps = 51 + + src1 = gr.sig_source_f (sampling_freq, gr.GR_SIN_WAVE,sampling_freq * 0.10, 1.0) + src2 = gr.sig_source_f (sampling_freq, gr.GR_COS_WAVE,sampling_freq * 0.10, 1.0) + + head1 = gr.head (gr.sizeof_float, int (ntaps + sampling_freq * 0.10)) + head2 = gr.head (gr.sizeof_float, int (ntaps + sampling_freq * 0.10)) + + taps = gr.firdes_hilbert (ntaps) + hd = gr.filter_delay_fc (taps) + + dst2 = gr.vector_sink_c () + + fg.connect (src1, head1) + fg.connect (src2, head2) + + fg.connect (head1, (hd,0)) + fg.connect (head2, (hd,1)) + fg.connect (hd, dst2) + + fg.run () + + # get output + result_data = dst2.data () + + self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5) + + +if __name__ == '__main__': + gr_unittest.main () diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_flow_graph.py b/gnuradio-core/src/python/gnuradio/gr/qa_flow_graph.py new file mode 100755 index 000000000..455cc1359 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_flow_graph.py @@ -0,0 +1,356 @@ +#!/usr/bin/env python +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest +import qa_basic_flow_graph + + +def all_counts (): + return (gr.block_ncurrently_allocated (), + gr.block_detail_ncurrently_allocated (), + gr.buffer_ncurrently_allocated (), + gr.buffer_reader_ncurrently_allocated ()) + + +class wrap_add(gr.hier_block): + def __init__(self, fg, a): + add = gr.add_const_ii (a) + gr.hier_block.__init__ (self, fg, add, add) + +class mult_add(gr.hier_block): + def __init__(self, fg, m, a): + mult = gr.multiply_const_ii (m) + add = gr.add_const_ii (a) + fg.connect (mult, add) + gr.hier_block.__init__ (self, fg, mult, add) + + +class test_flow_graph (qa_basic_flow_graph.test_basic_flow_graph): + + def setUp (self): + ''' override qa_basic_flow_graph setUp in order to use our class''' + self.fg = gr.flow_graph () + + def tearDown (self): + qa_basic_flow_graph.test_basic_flow_graph.tearDown (self) + + + # we inherit all their tests, so we can be sure we don't break + # any of the underlying code + + + def leak_check (self, fct): + begin = all_counts () + fct () + # tear down early so we can check for leaks + self.tearDown () + end = all_counts () + self.assertEqual (begin, end) + + def test_100_tsort_null (self): + self.assertEqual ([], self.fg.topological_sort (self.fg.all_blocks ())) + + def test_101_tsort_two (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + fg.connect ((src1, 0), (dst1, 0)) + fg.validate () + self.assertEqual ([src1, dst1], fg.topological_sort (fg.all_blocks ())) + + def test_102_tsort_three_a (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + nop1 = gr.nop (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + fg.connect (src1, nop1) + fg.connect (nop1, dst1) + fg.validate () + self.assertEqual ([src1, nop1, dst1], fg.topological_sort (fg.all_blocks ())) + + def test_103_tsort_three_b (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + nop1 = gr.nop (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + fg.connect (nop1, dst1) + fg.connect (src1, nop1) + fg.validate () + self.assertEqual ([src1, nop1, dst1], fg.topological_sort (fg.all_blocks ())) + + def test_104_trivial_dag_check (self): + fg = self.fg + nop1 = gr.nop (gr.sizeof_int) + fg.connect (nop1, nop1) + fg.validate () + self.assertRaises (gr.NotDAG, + lambda : fg.topological_sort (fg.all_blocks ())) + + def test_105 (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + src2 = gr.null_source (gr.sizeof_int) + nop1 = gr.nop (gr.sizeof_int) + nop2 = gr.nop (gr.sizeof_int) + nop3 = gr.nop (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + + fg.connect (src1, nop1) + fg.connect (src2, nop2) + fg.connect (nop1, (nop3, 0)) + fg.connect (nop2, (nop3, 1)) + fg.connect (nop3, dst1) + fg.validate () + ts = fg.topological_sort (fg.all_blocks ()) + self.assertEqual ([src2, nop2, src1, nop1, nop3, dst1], ts) + + + def test_106 (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + src2 = gr.null_source (gr.sizeof_int) + nop1 = gr.nop (gr.sizeof_int) + nop2 = gr.nop (gr.sizeof_int) + nop3 = gr.nop (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + + fg.connect (nop3, dst1) + fg.connect (nop2, (nop3, 1)) + fg.connect (nop1, (nop3, 0)) + fg.connect (src2, nop2) + fg.connect (src1, nop1) + fg.validate () + ts = fg.topological_sort (fg.all_blocks ()) + self.assertEqual ([src2, nop2, src1, nop1, nop3, dst1], ts) + + def test_107 (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + src2 = gr.null_source (gr.sizeof_int) + nop1 = gr.nop (gr.sizeof_int) + nop2 = gr.nop (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + dst2 = gr.null_sink (gr.sizeof_int) + + fg.connect (src1, nop1) + fg.connect (nop1, dst1) + fg.connect (src2, nop2) + fg.connect (nop2, dst2) + fg.validate () + ts = fg.topological_sort (fg.all_blocks ()) + self.assertEqual ([src2, nop2, dst2, src1, nop1, dst1], ts) + + def test_108 (self): + self.leak_check (self.body_108) + + def body_108 (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + src2 = gr.null_source (gr.sizeof_int) + nop1 = gr.nop (gr.sizeof_int) + nop2 = gr.nop (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + dst2 = gr.null_sink (gr.sizeof_int) + + fg.connect (nop2, dst2) + fg.connect (src1, nop1) + fg.connect (src2, nop2) + fg.connect (nop1, dst1) + fg.validate () + ts = fg.topological_sort (fg.all_blocks ()) + self.assertEqual ([src2, nop2, dst2, src1, nop1, dst1], ts) + self.assertEqual ((6,0,0,0), all_counts ()) + + def test_109__setup_connections (self): + self.leak_check (self.body_109) + + def body_109 (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + fg.connect (src1, dst1) + fg._setup_connections () + self.assertEqual ((2,2,1,1), all_counts ()) + + def test_110_scheduler (self): + self.leak_check (self.body_110) + + def body_110 (self): + fg = self.fg + src_data = (0, 1, 2, 3) + src1 = gr.vector_source_i (src_data) + dst1 = gr.vector_sink_i () + fg.connect ((src1, 0), (dst1, 0)) + fg.run () + dst_data = dst1.data () + self.assertEqual (src_data, dst_data) + + def test_111_scheduler (self): + self.leak_check (self.body_111) + + def body_111 (self): + fg = self.fg + src_data = (0, 1, 2, 3) + expected_result = (2, 3, 4, 5) + src1 = gr.vector_source_i (src_data) + op = gr.add_const_ii (2) + dst1 = gr.vector_sink_i () + fg.connect (src1, op) + fg.connect (op, dst1) + fg.run () + dst_data = dst1.data () + self.assertEqual (expected_result, dst_data) + + def test_111v_scheduler (self): + self.leak_check (self.body_111v) + + def body_111v (self): + fg = self.fg + src_data = (0, 1, 2, 3) + expected_result = (2, 3, 4, 5) + src1 = gr.vector_source_i (src_data) + op = gr.add_const_ii (2) + dst1 = gr.vector_sink_i () + fg.connect (src1, op, dst1) + fg.run () + dst_data = dst1.data () + self.assertEqual (expected_result, dst_data) + + def test_112 (self): + fg = self.fg + nop1 = gr.nop (gr.sizeof_int) + nop2 = gr.nop (gr.sizeof_int) + nop3 = gr.nop (gr.sizeof_int) + fg.connect ((nop1, 0), (nop2, 0)) + fg.connect ((nop1, 1), (nop3, 0)) + fg._setup_connections () + self.assertEqual (2, nop1.detail().noutputs()) + + def test_113 (self): + fg = self.fg + nop1 = gr.nop (gr.sizeof_int) + nop2 = gr.nop (gr.sizeof_int) + nop3 = gr.nop (gr.sizeof_int) + fg.connect ((nop1, 0), (nop2, 0)) + fg.connect ((nop1, 0), (nop3, 0)) + fg._setup_connections () + self.assertEqual (1, nop1.detail().noutputs()) + + def test_200_partition (self): + self.leak_check (self.body_200) + + def body_200 (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + nop1 = gr.nop (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + fg.connect (nop1, dst1) + fg.connect (src1, nop1) + fg.validate () + p = fg.partition_graph (fg.all_blocks ()) + self.assertEqual ([[src1, nop1, dst1]], p) + self.assertEqual ((3,0,0,0), all_counts ()) + + def test_201_partition (self): + self.leak_check (self.body_201) + + def body_201 (self): + fg = self.fg + src1 = gr.null_source (gr.sizeof_int) + src2 = gr.null_source (gr.sizeof_int) + nop1 = gr.nop (gr.sizeof_int) + nop2 = gr.nop (gr.sizeof_int) + dst1 = gr.null_sink (gr.sizeof_int) + dst2 = gr.null_sink (gr.sizeof_int) + + fg.connect (nop2, dst2) + fg.connect (src1, nop1) + fg.connect (src2, nop2) + fg.connect (nop1, dst1) + fg.validate () + p = fg.partition_graph (fg.all_blocks ()) + self.assertEqual ([[src2, nop2, dst2], [src1, nop1, dst1]], p) + self.assertEqual ((6,0,0,0), all_counts ()) + + def test_300_hier (self): + self.leak_check (self.body_300_hier) + + def body_300_hier (self): + fg = self.fg + src_data = (0, 1, 2, 3) + expected_result = (10, 11, 12, 13) + src1 = gr.vector_source_i (src_data) + op = wrap_add (fg, 10) + dst1 = gr.vector_sink_i () + fg.connect (src1, op, dst1) + fg.run () + dst_data = dst1.data () + self.assertEqual (expected_result, dst_data) + + def test_301_hier (self): + self.leak_check (self.body_301_hier) + + def body_301_hier (self): + fg = self.fg + src_data = (0, 1, 2, 3) + expected_result = (5, 8, 11, 14) + src1 = gr.vector_source_i (src_data) + op = mult_add (fg, 3, 5) + dst1 = gr.vector_sink_i () + fg.connect (src1, op, dst1) + fg.run () + dst_data = dst1.data () + self.assertEqual (expected_result, dst_data) + + def test_302_hier (self): + self.leak_check (self.body_302_hier) + + def body_302_hier (self): + fg = self.fg + src_data = (0, 1, 2, 3) + expected_result = (10, 11, 12, 13) + src1 = gr.vector_source_i (src_data) + op = gr.compose (fg, gr.add_const_ii (10)) + dst1 = gr.vector_sink_i () + fg.connect (src1, op, dst1) + fg.run () + dst_data = dst1.data () + self.assertEqual (expected_result, dst_data) + + def test_303_hier (self): + self.leak_check (self.body_303_hier) + + def body_303_hier (self): + fg = self.fg + src_data = (0, 1, 2, 3) + expected_result = (35, 38, 41, 44) + src1 = gr.vector_source_i (src_data) + op = gr.compose (fg, gr.add_const_ii (10), mult_add (fg, 3, 5)) + dst1 = gr.vector_sink_i () + fg.connect (src1, op, dst1) + fg.run () + dst_data = dst1.data () + self.assertEqual (expected_result, dst_data) + + +if __name__ == '__main__': + gr_unittest.main () diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_frequency_modulator.py b/gnuradio-core/src/python/gnuradio/gr/qa_frequency_modulator.py new file mode 100755 index 000000000..cfa34830d --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_frequency_modulator.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest +import math + +def sincos(x): + return math.cos(x) + math.sin(x) * 1j + + +class test_frequency_modulator (gr_unittest.TestCase): + + def setUp (self): + self.fg = gr.flow_graph () + + def tearDown (self): + self.fg = None + + def test_fm_001 (self): + pi = math.pi + sensitivity = pi/4 + src_data = (1.0/4, 1.0/2, 1.0/4, -1.0/4, -1.0/2, -1/4.0) + running_sum = (pi/16, 3*pi/16, pi/4, 3*pi/16, pi/16, 0) + expected_result = tuple ([sincos (x) for x in running_sum]) + src = gr.vector_source_f (src_data) + op = gr.frequency_modulator_fc (sensitivity) + dst = gr.vector_sink_c () + self.fg.connect (src, op) + self.fg.connect (op, dst) + self.fg.run () + result_data = dst.data () + self.assertComplexTuplesAlmostEqual (expected_result, result_data) + + +if __name__ == '__main__': + gr_unittest.main () + diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_fsk_stuff.py b/gnuradio-core/src/python/gnuradio/gr/qa_fsk_stuff.py new file mode 100755 index 000000000..d61ddb1a0 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_fsk_stuff.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest +import math + +def sincos(x): + return math.cos(x) + math.sin(x) * 1j + +class test_bytes_to_syms (gr_unittest.TestCase): + + def setUp (self): + self.fg = gr.flow_graph () + + def tearDown (self): + self.fg = None + + def test_bytes_to_syms_001 (self): + src_data = (0x01, 0x80, 0x03) + expected_result = (-1, -1, -1, -1, -1, -1, -1, +1, + +1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, +1, +1) + src = gr.vector_source_b (src_data) + op = gr.bytes_to_syms () + dst = gr.vector_sink_f () + self.fg.connect (src, op) + self.fg.connect (op, dst) + self.fg.run () + result_data = dst.data () + self.assertEqual (expected_result, result_data) + + def test_simple_framer (self): + src_data = (0x00, 0x11, 0x22, 0x33, + 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, + 0xcc, 0xdd, 0xee, 0xff) + + expected_result = ( + 0xac, 0xdd, 0xa4, 0xe2, 0xf2, 0x8c, 0x20, 0xfc, 0x00, 0x00, 0x11, 0x22, 0x33, 0x55, + 0xac, 0xdd, 0xa4, 0xe2, 0xf2, 0x8c, 0x20, 0xfc, 0x01, 0x44, 0x55, 0x66, 0x77, 0x55, + 0xac, 0xdd, 0xa4, 0xe2, 0xf2, 0x8c, 0x20, 0xfc, 0x02, 0x88, 0x99, 0xaa, 0xbb, 0x55, + 0xac, 0xdd, 0xa4, 0xe2, 0xf2, 0x8c, 0x20, 0xfc, 0x03, 0xcc, 0xdd, 0xee, 0xff, 0x55) + + src = gr.vector_source_b (src_data) + op = gr.simple_framer (4) + dst = gr.vector_sink_b () + self.fg.connect (src, op) + self.fg.connect (op, dst) + self.fg.run () + result_data = dst.data () + self.assertEqual (expected_result, result_data) + + +if __name__ == '__main__': + gr_unittest.main () + diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_goertzel.py b/gnuradio-core/src/python/gnuradio/gr/qa_goertzel.py new file mode 100755 index 000000000..da9d65f62 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_goertzel.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# +# Copyright 2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest +from math import pi, cos + +class test_goertzel(gr_unittest.TestCase): + + def setUp(self): + self.fg = gr.flow_graph() + + def tearDown(self): + self.fg = None + + def make_tone_data(self, rate, freq): + return [cos(2*pi*x*freq/rate) for x in range(rate)] + + def transform(self, src_data, rate, freq): + src = gr.vector_source_f(src_data, False) + dft = gr.goertzel_fc(rate, rate, freq) + dst = gr.vector_sink_c() + self.fg.connect(src, dft, dst) + self.fg.run() + return dst.data() + + def test_001(self): # Measure single tone magnitude + rate = 8000 + freq = 100 + bin = freq + src_data = self.make_tone_data(rate, freq) + expected_result = 0.5 + actual_result = abs(self.transform(src_data, rate, bin)[0]) + self.assertAlmostEqual(expected_result, actual_result, places=5) + + def test_002(self): # Measure off frequency magnitude + rate = 8000 + freq = 100 + bin = freq/2 + src_data = self.make_tone_data(rate, freq) + expected_result = 0.0 + actual_result = abs(self.transform(src_data, rate, bin)[0]) + self.assertAlmostEqual(expected_result, actual_result, places=5) + +if __name__ == '__main__': + gr_unittest.main() diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_head.py b/gnuradio-core/src/python/gnuradio/gr/qa_head.py new file mode 100755 index 000000000..a6fcd7f98 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_head.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest + +class test_head (gr_unittest.TestCase): + + def setUp (self): + self.fg = gr.flow_graph () + + def tearDown (self): + self.fg = None + + def test_head (self): + src_data = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + expected_result = (1, 2, 3, 4) + src1 = gr.vector_source_i (src_data) + op = gr.head (gr.sizeof_int, 4) + dst1 = gr.vector_sink_i () + self.fg.connect (src1, op) + self.fg.connect (op, dst1) + self.fg.run () + dst_data = dst1.data () + self.assertEqual (expected_result, dst_data) + + +if __name__ == '__main__': + gr_unittest.main () diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_hilbert.py b/gnuradio-core/src/python/gnuradio/gr/qa_hilbert.py new file mode 100755 index 000000000..a8f39dd6d --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_hilbert.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest +import math + +class test_sig_source (gr_unittest.TestCase): + + def setUp (self): + self.fg = gr.flow_graph () + + def tearDown (self): + self.fg = None + + def test_hilbert (self): + fg = self.fg + ntaps = 51 + sampling_freq = 100 + + expected_result = ( -1.4678005338941702e-11j, + -0.0011950774351134896j, + -0.0019336787518113852j, + -0.0034673355985432863j, + -0.0036765895783901215j, + -0.004916108213365078j, + -0.0042778430506587029j, + -0.006028641015291214j, + -0.005476709920912981j, + -0.0092810001224279404j, + -0.0095402700826525688j, + -0.016060983762145042j, + -0.016446959227323532j, + -0.02523401565849781j, + -0.024382550269365311j, + -0.035477779805660248j, + -0.033021725714206696j, + -0.048487484455108643j, + -0.04543270543217659j, + -0.069477587938308716j, + -0.066984444856643677j, + -0.10703597217798233j, + -0.10620346665382385j, + -0.1852707713842392j, + -0.19357112050056458j, + (7.2191945754696007e-09 -0.50004088878631592j), + (0.58778399229049683 -0.6155126690864563j), + (0.95105588436126709 -0.12377222627401352j), + (0.95105588436126709 +0.41524654626846313j), + (0.5877838134765625 +0.91611981391906738j), + (5.8516356205018383e-09 +1.0670661926269531j), + (-0.5877840518951416 +0.87856143712997437j), + (-0.95105588436126709 +0.35447561740875244j), + (-0.95105588436126709 -0.26055556535720825j), + (-0.5877838134765625 -0.77606213092803955j), + (-8.7774534307527574e-09 -0.96460390090942383j), + (0.58778399229049683 -0.78470128774642944j), + (0.95105588436126709 -0.28380891680717468j), + (0.95105588436126709 +0.32548999786376953j), + (0.5877838134765625 +0.82514488697052002j), + (1.4629089051254596e-08 +1.0096219778060913j), + (-0.5877840518951416 +0.81836479902267456j), + (-0.95105588436126709 +0.31451958417892456j), + (-0.95105588436126709 -0.3030143678188324j), + (-0.5877838134765625 -0.80480599403381348j), + (-1.7554906861505515e-08 -0.99516552686691284j), + (0.58778399229049683 -0.80540722608566284j), + (0.95105582475662231 -0.30557557940483093j), + (0.95105588436126709 +0.31097668409347534j), + (0.5877838134765625 +0.81027895212173462j), + (2.3406542482007353e-08 +1.0000816583633423j), + (-0.5877840518951416 +0.80908381938934326j), + (-0.95105588436126709 +0.30904293060302734j), + (-0.95105588436126709 -0.30904296040534973j), + (-0.5877838134765625 -0.80908387899398804j), + (-2.6332360292258272e-08 -1.0000815391540527j), + (0.58778399229049683 -0.80908381938934326j), + (0.95105582475662231 -0.30904299020767212j), + (0.95105588436126709 +0.30904293060302734j), + (0.5877838134765625 +0.80908381938934326j), + (3.218399768911695e-08 +1.0000815391540527j)) + + + src1 = gr.sig_source_f (sampling_freq, gr.GR_SIN_WAVE, + sampling_freq * 0.10, 1.0) + + head = gr.head (gr.sizeof_float, int (ntaps + sampling_freq * 0.10)) + hilb = gr.hilbert_fc (ntaps) + dst1 = gr.vector_sink_c () + fg.connect (src1, head) + fg.connect (head, hilb) + fg.connect (hilb, dst1) + fg.run () + dst_data = dst1.data () + self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 5) + +if __name__ == '__main__': + gr_unittest.main () diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_iir.py b/gnuradio-core/src/python/gnuradio/gr/qa_iir.py new file mode 100755 index 000000000..a1f2aa077 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_iir.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest + +class test_iir (gr_unittest.TestCase): + + def setUp (self): + self.fg = gr.flow_graph () + + def tearDown (self): + self.fg = None + + def test_iir_direct_001 (self): + src_data = (1, 2, 3, 4, 5, 6, 7, 8) + fftaps = () + fbtaps = () + expected_result = (0, 0, 0, 0, 0, 0, 0, 0) + src = gr.vector_source_f (src_data) + op = gr.iir_filter_ffd (fftaps, fbtaps) + dst = gr.vector_sink_f () + self.fg.connect (src, op) + self.fg.connect (op, dst) + self.fg.run () + result_data = dst.data () + self.assertFloatTuplesAlmostEqual (expected_result, result_data) + + def test_iir_direct_002 (self): + src_data = (1, 2, 3, 4, 5, 6, 7, 8) + fftaps = (2,) + fbtaps = (0,) + expected_result = (2, 4, 6, 8, 10, 12, 14, 16) + src = gr.vector_source_f (src_data) + op = gr.iir_filter_ffd (fftaps, fbtaps) + dst = gr.vector_sink_f () + self.fg.connect (src, op) + self.fg.connect (op, dst) + self.fg.run () + result_data = dst.data () + self.assertFloatTuplesAlmostEqual (expected_result, result_data) + + def test_iir_direct_003 (self): + src_data = (1, 2, 3, 4, 5, 6, 7, 8) + fftaps = (2, 11) + fbtaps = (0, 0) + expected_result = (2, 15, 28, 41, 54, 67, 80, 93) + src = gr.vector_source_f (src_data) + op = gr.iir_filter_ffd (fftaps, fbtaps) + dst = gr.vector_sink_f () + self.fg.connect (src, op) + self.fg.connect (op, dst) + self.fg.run () + result_data = dst.data () + self.assertFloatTuplesAlmostEqual (expected_result, result_data) + + def test_iir_direct_004 (self): + src_data = (1, 2, 3, 4, 5, 6, 7, 8) + fftaps = (2, 11) + fbtaps = (0, -1) + expected_result = (2, 13, 15, 26, 28, 39, 41, 52) + src = gr.vector_source_f (src_data) + op = gr.iir_filter_ffd (fftaps, fbtaps) + dst = gr.vector_sink_f () + self.fg.connect (src, op) + self.fg.connect (op, dst) + self.fg.run () + result_data = dst.data () + self.assertFloatTuplesAlmostEqual (expected_result, result_data) + + def test_iir_direct_005 (self): + src_data = (1, 2, 3, 4, 5, 6, 7, 8) + fftaps = (2, 11, 0) + fbtaps = (0, -1, 3) + expected_result = (2, 13, 21, 59, 58, 186, 68, 583) + src = gr.vector_source_f (src_data) + op = gr.iir_filter_ffd (fftaps, fbtaps) + dst = gr.vector_sink_f () + self.fg.connect (src, op) + self.fg.connect (op, dst) + self.fg.run () + result_data = dst.data () + self.assertFloatTuplesAlmostEqual (expected_result, result_data) + + def test_iir_direct_006 (self): + fftaps = (2, 11, 0) + fbtaps = (0, -1) + self.assertRaises ((RuntimeError, ValueError), gr.iir_filter_ffd, fftaps, fbtaps) + + def test_iir_direct_007 (self): + src_data = (1, 2, 3, 4, 5, 6, 7, 8) + expected_result = (2, 13, 21, 59, 58, 186, 68, 583) + fftaps = (2, 1) + fbtaps = (0, -1) + src = gr.vector_source_f (src_data) + op = gr.iir_filter_ffd (fftaps, fbtaps) + fftaps = (2, 11, 0) + fbtaps = (0, -1, 3) + op.set_taps (fftaps, fbtaps) + dst = gr.vector_sink_f () + self.fg.connect (src, op) + self.fg.connect (op, dst) + self.fg.run () + result_data = dst.data () + self.assertFloatTuplesAlmostEqual (expected_result, result_data) + + def test_iir_direct_008 (self): + fftaps = (2, 1) + fbtaps = (0, -1) + op = gr.iir_filter_ffd (fftaps, fbtaps) + fftaps = (2, 11) + fbtaps = (0, -1, 3) + self.assertRaises ((RuntimeError, ValueError), op.set_taps, fftaps, fbtaps) + +if __name__ == '__main__': + gr_unittest.main () + diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_interleave.py b/gnuradio-core/src/python/gnuradio/gr/qa_interleave.py new file mode 100755 index 000000000..003d12e64 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_interleave.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest +import math + +class test_interleave (gr_unittest.TestCase): + + def setUp (self): + self.fg = gr.flow_graph () + + def tearDown (self): + self.fg = None + + def test_int_001 (self): + lenx = 64 + src0 = gr.vector_source_f (range (0, lenx, 4)) + src1 = gr.vector_source_f (range (1, lenx, 4)) + src2 = gr.vector_source_f (range (2, lenx, 4)) + src3 = gr.vector_source_f (range (3, lenx, 4)) + op = gr.interleave (gr.sizeof_float) + dst = gr.vector_sink_f () + + self.fg.connect (src0, (op, 0)) + self.fg.connect (src1, (op, 1)) + self.fg.connect (src2, (op, 2)) + self.fg.connect (src3, (op, 3)) + self.fg.connect (op, dst) + self.fg.run () + expected_result = tuple (range (lenx)) + result_data = dst.data () + self.assertFloatTuplesAlmostEqual (expected_result, result_data) + + def test_deint_001 (self): + lenx = 64 + src = gr.vector_source_f (range (lenx)) + op = gr.deinterleave (gr.sizeof_float) + dst0 = gr.vector_sink_f () + dst1 = gr.vector_sink_f () + dst2 = gr.vector_sink_f () + dst3 = gr.vector_sink_f () + + self.fg.connect (src, op) + self.fg.connect ((op, 0), dst0) + self.fg.connect ((op, 1), dst1) + self.fg.connect ((op, 2), dst2) + self.fg.connect ((op, 3), dst3) + self.fg.run () + + expected_result0 = tuple (range (0, lenx, 4)) + expected_result1 = tuple (range (1, lenx, 4)) + expected_result2 = tuple (range (2, lenx, 4)) + expected_result3 = tuple (range (3, lenx, 4)) + + self.assertFloatTuplesAlmostEqual (expected_result0, dst0.data ()) + self.assertFloatTuplesAlmostEqual (expected_result1, dst1.data ()) + self.assertFloatTuplesAlmostEqual (expected_result2, dst2.data ()) + self.assertFloatTuplesAlmostEqual (expected_result3, dst3.data ()) + +if __name__ == '__main__': + gr_unittest.main () + diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_interp_fir_filter.py b/gnuradio-core/src/python/gnuradio/gr/qa_interp_fir_filter.py new file mode 100755 index 000000000..4119e3e2d --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_interp_fir_filter.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest +import math + +class test_interp_fir_filter (gr_unittest.TestCase): + + def setUp (self): + self.fg = gr.flow_graph () + + def tearDown (self): + self.fg = None + + def test_fff (self): + taps = [1, 10, 100, 1000, 10000] + src_data = (0, 2, 3, 5, 7, 11, 13, 17) + interpolation = 3 + xr = (0,0,0,0,2,20,200,2003,20030,300,3005,30050,500,5007,50070,700,7011,70110,1100,11013,110130,1300,13017,130170) + expected_result = tuple ([float (x) for x in xr]) + + src = gr.vector_source_f (src_data) + op = gr.interp_fir_filter_fff (interpolation, taps) + dst = gr.vector_sink_f () + self.fg.connect (src, op) + self.fg.connect (op, dst) + self.fg.run () + result_data = dst.data () + L = min(len(result_data), len(expected_result)) + self.assertEqual (expected_result[0:L], result_data[0:L]) + + +if __name__ == '__main__': + gr_unittest.main () + diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_kludge_copy.py b/gnuradio-core/src/python/gnuradio/gr/qa_kludge_copy.py new file mode 100755 index 000000000..f5dee1528 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_kludge_copy.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# +# Copyright 2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest +import math +import random + + +class test_kludge_copy(gr_unittest.TestCase): + + def setUp(self): + self.fg = gr.flow_graph() + self.rng = random.Random() + self.rng.seed(0) + + def tearDown(self): + self.fg = None + self.rng = None + + def make_random_int_tuple(self, L): + result = [] + for x in range(L): + result.append(self.rng.randint(int(-1e9), int(+1e9))) + return tuple(result) + + + def test_001(self): + # 1 input stream; 1 output stream + src0_data = self.make_random_int_tuple(16000) + src0 = gr.vector_source_i(src0_data) + op = gr.kludge_copy(gr.sizeof_int) + dst0 = gr.vector_sink_i() + self.fg.connect(src0, op, dst0) + self.fg.run() + dst0_data = dst0.data() + self.assertEqual(src0_data, dst0_data) + + def test_002(self): + # 2 input streams; 2 output streams + src0_data = self.make_random_int_tuple(16000) + src1_data = self.make_random_int_tuple(16000) + src0 = gr.vector_source_i(src0_data) + src1 = gr.vector_source_i(src1_data) + op = gr.kludge_copy(gr.sizeof_int) + dst0 = gr.vector_sink_i() + dst1 = gr.vector_sink_i() + self.fg.connect(src0, (op, 0), dst0) + self.fg.connect(src1, (op, 1), dst1) + self.fg.run() + dst0_data = dst0.data() + dst1_data = dst1.data() + self.assertEqual(src0_data, dst0_data) + self.assertEqual(src1_data, dst1_data) + + def test_003(self): + # number of input streams != number of output streams + src0_data = self.make_random_int_tuple(16000) + src1_data = self.make_random_int_tuple(16000) + src0 = gr.vector_source_i(src0_data) + src1 = gr.vector_source_i(src1_data) + op = gr.kludge_copy(gr.sizeof_int) + dst0 = gr.vector_sink_i() + dst1 = gr.vector_sink_i() + self.fg.connect(src0, (op, 0), dst0) + self.fg.connect(src1, (op, 1)) + self.assertRaises(ValueError, self.fg.run) + +if __name__ == '__main__': + gr_unittest.main () + diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_kludged_imports.py b/gnuradio-core/src/python/gnuradio/gr/qa_kludged_imports.py new file mode 100755 index 000000000..b20867722 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_kludged_imports.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest + +class test_head (gr_unittest.TestCase): + + def setUp(self): + pass + + def tearDown(self): + pass + + def test_blks_import(self): + # make sure that this somewhat magic import works + from gnuradio import blks + + def test_gru_import(self): + # make sure that this somewhat magic import works + from gnuradio import gru + + +if __name__ == '__main__': + gr_unittest.main () diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_message.py b/gnuradio-core/src/python/gnuradio/gr/qa_message.py new file mode 100755 index 000000000..d5d511149 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_message.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest +import qa_basic_flow_graph + + +def all_counts (): + return (gr.block_ncurrently_allocated (), + gr.block_detail_ncurrently_allocated (), + gr.buffer_ncurrently_allocated (), + gr.buffer_reader_ncurrently_allocated (), + gr.message_ncurrently_allocated ()) + + +class test_message (gr_unittest.TestCase): + + def setUp (self): + self.fg = gr.flow_graph() + self.msgq = gr.msg_queue () + + def tearDown (self): + self.fg = None + self.msgq = None + + def leak_check (self, fct): + begin = all_counts () + fct () + # tear down early so we can check for leaks + self.tearDown () + end = all_counts () + self.assertEqual (begin, end) + + def test_100 (self): + msg = gr.message (0, 1.5, 2.3) + self.assertEquals (0, msg.type()) + self.assertAlmostEqual (1.5, msg.arg1()) + self.assertAlmostEqual (2.3, msg.arg2()) + self.assertEquals (0, msg.length()) + + def test_101 (self): + s = 'This is a test' + msg = gr.message_from_string(s) + self.assertEquals(s, msg.to_string()) + + def test_200 (self): + self.leak_check (self.body_200) + + def body_200 (self): + self.msgq.insert_tail (gr.message (0)) + self.assertEquals (1, self.msgq.count()) + self.msgq.insert_tail (gr.message (1)) + self.assertEquals (2, self.msgq.count()) + msg0 = self.msgq.delete_head () + self.assertEquals (0, msg0.type()) + msg1 = self.msgq.delete_head () + self.assertEquals (1, msg1.type()) + self.assertEquals (0, self.msgq.count()) + + def test_201 (self): + self.leak_check (self.body_201) + + def body_201 (self): + self.msgq.insert_tail (gr.message (0)) + self.assertEquals (1, self.msgq.count()) + self.msgq.insert_tail (gr.message (1)) + self.assertEquals (2, self.msgq.count()) + + def test_202 (self): + self.leak_check (self.body_202) + + def body_202 (self): + # global msg + msg = gr.message (666) + + def test_300(self): + input_data = (0,1,2,3,4,5,6,7,8,9) + src = gr.vector_source_b(input_data) + dst = gr.vector_sink_b() + self.fg.connect(src, dst) + self.fg.run() + self.assertEquals(input_data, dst.data()) + + def test_301(self): + src = gr.message_source(gr.sizeof_char) + dst = gr.vector_sink_b() + self.fg.connect(src, dst) + src.msgq().insert_tail(gr.message_from_string('01234')) + src.msgq().insert_tail(gr.message_from_string('5')) + src.msgq().insert_tail(gr.message_from_string('')) + src.msgq().insert_tail(gr.message_from_string('6789')) + src.msgq().insert_tail(gr.message(1)) # send EOF + self.fg.run() + self.assertEquals(tuple(map(ord, '0123456789')), dst.data()) + + +if __name__ == '__main__': + gr_unittest.main () diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_mute.py b/gnuradio-core/src/python/gnuradio/gr/qa_mute.py new file mode 100755 index 000000000..863c308a4 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_mute.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# +# Copyright 2004,2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest + +class test_head (gr_unittest.TestCase): + + def setUp (self): + self.fg = gr.flow_graph () + + def tearDown (self): + self.fg = None + + def help_ii (self, src_data, exp_data, op): + for s in zip (range (len (src_data)), src_data): + src = gr.vector_source_i (s[1]) + self.fg.connect (src, (op, s[0])) + dst = gr.vector_sink_i () + self.fg.connect (op, dst) + self.fg.run () + result_data = dst.data () + self.assertEqual (exp_data, result_data) + + def help_ff (self, src_data, exp_data, op): + for s in zip (range (len (src_data)), src_data): + src = gr.vector_source_f (s[1]) + self.fg.connect (src, (op, s[0])) + dst = gr.vector_sink_f () + self.fg.connect (op, dst) + self.fg.run () + result_data = dst.data () + self.assertEqual (exp_data, result_data) + + def help_cc (self, src_data, exp_data, op): + for s in zip (range (len (src_data)), src_data): + src = gr.vector_source_c (s[1]) + self.fg.connect (src, (op, s[0])) + dst = gr.vector_sink_c () + self.fg.connect (op, dst) + self.fg.run () + result_data = dst.data () + self.assertEqual (exp_data, result_data) + + def test_unmute_ii(self): + src_data = (1, 2, 3, 4, 5) + expected_result = (1, 2, 3, 4, 5) + op = gr.mute_ii (False) + self.help_ii ((src_data,), expected_result, op) + + def test_mute_ii(self): + src_data = (1, 2, 3, 4, 5) + expected_result = (0, 0, 0, 0, 0) + op = gr.mute_ii (True) + self.help_ii ((src_data,), expected_result, op) + + def test_unmute_cc (self): + src_data = (1+5j, 2+5j, 3+5j, 4+5j, 5+5j) + expected_result = (1+5j, 2+5j, 3+5j, 4+5j, 5+5j) + op = gr.mute_cc (False) + self.help_cc ((src_data,), expected_result, op) + + def test_unmute_cc (self): + src_data = (1+5j, 2+5j, 3+5j, 4+5j, 5+5j) + expected_result = (0+0j, 0+0j, 0+0j, 0+0j, 0+0j) + op = gr.mute_cc (True) + self.help_cc ((src_data,), expected_result, op) + + +if __name__ == '__main__': + gr_unittest.main () diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_nlog10.py b/gnuradio-core/src/python/gnuradio/gr/qa_nlog10.py new file mode 100755 index 000000000..f056d1100 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_nlog10.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest + +class test_single_pole_iir(gr_unittest.TestCase): + + def setUp (self): + self.fg = gr.flow_graph () + + def tearDown (self): + self.fg = None + + def test_001(self): + src_data = (-10, 0, 10, 100, 1000, 10000, 100000) + expected_result = (-180, -180, 10, 20, 30, 40, 50) + src = gr.vector_source_f(src_data) + op = gr.nlog10_ff(10) + dst = gr.vector_sink_f() + self.fg.connect (src, op, dst) + self.fg.run() + result_data = dst.data() + self.assertFloatTuplesAlmostEqual (expected_result, result_data) + + +if __name__ == '__main__': + gr_unittest.main () + diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_packed_to_unpacked.py b/gnuradio-core/src/python/gnuradio/gr/qa_packed_to_unpacked.py new file mode 100755 index 000000000..d5472c8d7 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_packed_to_unpacked.py @@ -0,0 +1,405 @@ +#!/usr/bin/env python +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest +import random + +class test_packing(gr_unittest.TestCase): + + def setUp(self): + self.fg = gr.flow_graph () + + def tearDown(self): + self.fg = None + + def test_001(self): + """ + Test stream_to_streams. + """ + src_data = (0x80,) + expected_results = (1,0,0,0,0,0,0,0) + src = gr.vector_source_b(src_data,False) + op = gr.packed_to_unpacked_bb(1, gr.GR_MSB_FIRST) + self.fg.connect(src, op) + + dst = gr.vector_sink_b() + self.fg.connect(op, dst) + + self.fg.run() + + self.assertEqual(expected_results, dst.data()) + + def test_002(self): + """ + Test stream_to_streams. + """ + src_data = (0x80,) + expected_results = (0,0,0,0,0,0,0, 1) + src = gr.vector_source_b(src_data,False) + op = gr.packed_to_unpacked_bb(1, gr.GR_LSB_FIRST) + self.fg.connect(src, op) + + dst = gr.vector_sink_b() + self.fg.connect(op, dst) + + self.fg.run() + + self.assertEqual(expected_results, dst.data()) + + def test_003(self): + """ + Test stream_to_streams. + """ + src_data = (0x11,) + expected_results = (4, 2) + src = gr.vector_source_b(src_data,False) + op = gr.packed_to_unpacked_bb(3, gr.GR_LSB_FIRST) + self.fg.connect(src, op) + + dst = gr.vector_sink_b() + self.fg.connect(op, dst) + + self.fg.run() + + self.assertEqual(expected_results, dst.data()) + + def test_004(self): + """ + Test stream_to_streams. + """ + src_data = (0x11,) + expected_results = (0, 4) + src = gr.vector_source_b(src_data,False) + op = gr.packed_to_unpacked_bb(3, gr.GR_MSB_FIRST) + self.fg.connect(src, op) + + dst = gr.vector_sink_b() + self.fg.connect(op, dst) + + self.fg.run() + + self.assertEqual(expected_results, dst.data()) + + def test_005(self): + """ + Test stream_to_streams. + """ + src_data = (1,0,0,0,0,0,1,0,0,1,0,1,1,0,1,0) + expected_results = (0x82,0x5a) + src = gr.vector_source_b(src_data,False) + op = gr.unpacked_to_packed_bb(1, gr.GR_MSB_FIRST) + self.fg.connect(src, op) + + dst = gr.vector_sink_b() + self.fg.connect(op, dst) + + self.fg.run() + + self.assertEqual(expected_results, dst.data()) + + def test_006(self): + """ + Test stream_to_streams. + """ + src_data = (0,1,0,0,0,0,0,1,0,1,0,1,1,0,1,0) + expected_results = (0x82,0x5a) + src = gr.vector_source_b(src_data,False) + op = gr.unpacked_to_packed_bb(1, gr.GR_LSB_FIRST) + self.fg.connect(src, op) + + dst = gr.vector_sink_b() + self.fg.connect(op, dst) + + self.fg.run() + + self.assertEqual(expected_results, dst.data()) + + + def test_007(self): + """ + Test stream_to_streams. + """ + src_data = (4, 2, 0,0,0) + expected_results = (0x11,) + src = gr.vector_source_b(src_data,False) + op = gr.unpacked_to_packed_bb(3, gr.GR_LSB_FIRST) + self.fg.connect(src, op) + + dst = gr.vector_sink_b() + self.fg.connect(op, dst) + + self.fg.run() + + self.assertEqual(expected_results, dst.data()) + + def test_008(self): + """ + Test stream_to_streams. + """ + src_data = (0, 4, 2,0,0) + expected_results = (0x11,) + src = gr.vector_source_b(src_data,False) + op = gr.unpacked_to_packed_bb(3, gr.GR_MSB_FIRST) + self.fg.connect(src, op) + + dst = gr.vector_sink_b() + self.fg.connect(op, dst) + + self.fg.run() + + self.assertEqual(expected_results, dst.data()) + + def test_009(self): + """ + Test stream_to_streams. + """ + + random.seed(0) + src_data = [] + for i in xrange(202): + src_data.append((random.randint(0,255))) + src_data = tuple(src_data) + expected_results = src_data + + src = gr.vector_source_b(tuple(src_data),False) + op1 = gr.packed_to_unpacked_bb(3, gr.GR_MSB_FIRST) + op2 = gr.unpacked_to_packed_bb(3, gr.GR_MSB_FIRST) + self.fg.connect(src, op1, op2) + + dst = gr.vector_sink_b() + self.fg.connect(op2, dst) + + self.fg.run() + + self.assertEqual(expected_results[0:201], dst.data()) + + def test_010(self): + """ + Test stream_to_streams. + """ + + random.seed(0) + src_data = [] + for i in xrange(56): + src_data.append((random.randint(0,255))) + src_data = tuple(src_data) + expected_results = src_data + src = gr.vector_source_b(tuple(src_data),False) + op1 = gr.packed_to_unpacked_bb(7, gr.GR_MSB_FIRST) + op2 = gr.unpacked_to_packed_bb(7, gr.GR_MSB_FIRST) + self.fg.connect(src, op1, op2) + dst = gr.vector_sink_b() + self.fg.connect(op2, dst) + + self.fg.run() + self.assertEqual(expected_results[0:201], dst.data()) + + def test_011(self): + """ + Test stream_to_streams. + """ + + random.seed(0) + src_data = [] + for i in xrange(56): + src_data.append((random.randint(0,255))) + src_data = tuple(src_data) + expected_results = src_data + src = gr.vector_source_b(tuple(src_data),False) + op1 = gr.packed_to_unpacked_bb(7, gr.GR_LSB_FIRST) + op2 = gr.unpacked_to_packed_bb(7, gr.GR_LSB_FIRST) + self.fg.connect(src, op1, op2) + dst = gr.vector_sink_b() + self.fg.connect(op2, dst) + + self.fg.run() + self.assertEqual(expected_results[0:201], dst.data()) + + + # tests on shorts + + def test_100a(self): + """ + test short version + """ + random.seed(0) + src_data = [] + for i in xrange(100): + src_data.append((random.randint(-2**15,2**15-1))) + src_data = tuple(src_data) + expected_results = src_data + src = gr.vector_source_s(tuple(src_data),False) + op1 = gr.packed_to_unpacked_ss(1, gr.GR_MSB_FIRST) + op2 = gr.unpacked_to_packed_ss(1, gr.GR_MSB_FIRST) + self.fg.connect(src, op1, op2) + dst = gr.vector_sink_s() + self.fg.connect(op2, dst) + + self.fg.run() + self.assertEqual(expected_results, dst.data()) + + def test_100b(self): + """ + test short version + """ + random.seed(0) + src_data = [] + for i in xrange(100): + src_data.append((random.randint(-2**15,2**15-1))) + src_data = tuple(src_data) + expected_results = src_data + src = gr.vector_source_s(tuple(src_data),False) + op1 = gr.packed_to_unpacked_ss(1, gr.GR_LSB_FIRST) + op2 = gr.unpacked_to_packed_ss(1, gr.GR_LSB_FIRST) + self.fg.connect(src, op1, op2) + dst = gr.vector_sink_s() + self.fg.connect(op2, dst) + + self.fg.run() + self.assertEqual(expected_results, dst.data()) + + def test_101a(self): + """ + test short version + """ + random.seed(0) + src_data = [] + for i in xrange(100): + src_data.append((random.randint(-2**15,2**15-1))) + src_data = tuple(src_data) + expected_results = src_data + src = gr.vector_source_s(tuple(src_data),False) + op1 = gr.packed_to_unpacked_ss(8, gr.GR_MSB_FIRST) + op2 = gr.unpacked_to_packed_ss(8, gr.GR_MSB_FIRST) + self.fg.connect(src, op1, op2) + dst = gr.vector_sink_s() + self.fg.connect(op2, dst) + + self.fg.run() + self.assertEqual(expected_results, dst.data()) + + def test_101b(self): + """ + test short version + """ + random.seed(0) + src_data = [] + for i in xrange(100): + src_data.append((random.randint(-2**15,2**15-1))) + src_data = tuple(src_data) + expected_results = src_data + src = gr.vector_source_s(tuple(src_data),False) + op1 = gr.packed_to_unpacked_ss(8, gr.GR_LSB_FIRST) + op2 = gr.unpacked_to_packed_ss(8, gr.GR_LSB_FIRST) + self.fg.connect(src, op1, op2) + dst = gr.vector_sink_s() + self.fg.connect(op2, dst) + + self.fg.run() + self.assertEqual(expected_results, dst.data()) + + # tests on ints + + def test_200a(self): + """ + test int version + """ + random.seed(0) + src_data = [] + for i in xrange(100): + src_data.append((random.randint(-2**31,2**31-1))) + src_data = tuple(src_data) + expected_results = src_data + src = gr.vector_source_i(tuple(src_data),False) + op1 = gr.packed_to_unpacked_ii(1, gr.GR_MSB_FIRST) + op2 = gr.unpacked_to_packed_ii(1, gr.GR_MSB_FIRST) + self.fg.connect(src, op1, op2) + dst = gr.vector_sink_i() + self.fg.connect(op2, dst) + + self.fg.run() + self.assertEqual(expected_results, dst.data()) + + def test_200b(self): + """ + test int version + """ + random.seed(0) + src_data = [] + for i in xrange(100): + src_data.append((random.randint(-2**31,2**31-1))) + src_data = tuple(src_data) + expected_results = src_data + src = gr.vector_source_i(tuple(src_data),False) + op1 = gr.packed_to_unpacked_ii(1, gr.GR_LSB_FIRST) + op2 = gr.unpacked_to_packed_ii(1, gr.GR_LSB_FIRST) + self.fg.connect(src, op1, op2) + dst = gr.vector_sink_i() + self.fg.connect(op2, dst) + + self.fg.run() + self.assertEqual(expected_results, dst.data()) + + def test_201a(self): + """ + test int version + """ + random.seed(0) + src_data = [] + for i in xrange(100): + src_data.append((random.randint(-2**31,2**31-1))) + src_data = tuple(src_data) + expected_results = src_data + src = gr.vector_source_i(tuple(src_data),False) + op1 = gr.packed_to_unpacked_ii(8, gr.GR_MSB_FIRST) + op2 = gr.unpacked_to_packed_ii(8, gr.GR_MSB_FIRST) + self.fg.connect(src, op1, op2) + dst = gr.vector_sink_i() + self.fg.connect(op2, dst) + + self.fg.run() + self.assertEqual(expected_results, dst.data()) + + def test_201b(self): + """ + test int version + """ + random.seed(0) + src_data = [] + for i in xrange(100): + src_data.append((random.randint(-2**31,2**31-1))) + src_data = tuple(src_data) + expected_results = src_data + src = gr.vector_source_i(tuple(src_data),False) + op1 = gr.packed_to_unpacked_ii(8, gr.GR_LSB_FIRST) + op2 = gr.unpacked_to_packed_ii(8, gr.GR_LSB_FIRST) + self.fg.connect(src, op1, op2) + dst = gr.vector_sink_i() + self.fg.connect(op2, dst) + + self.fg.run() + self.assertEqual(expected_results, dst.data()) + + +if __name__ == '__main__': + gr_unittest.main () + diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_pipe_fittings.py b/gnuradio-core/src/python/gnuradio/gr/qa_pipe_fittings.py new file mode 100755 index 000000000..dca18c8ec --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_pipe_fittings.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest + +if 0: + import os + print "pid =", os.getpid() + raw_input("Attach, then press Enter to continue") + + +def calc_expected_result(src_data, n): + assert (len(src_data) % n) == 0 + result = [list() for x in range(n)] + #print "len(result) =", len(result) + for i in xrange(len(src_data)): + (result[i % n]).append(src_data[i]) + return [tuple(x) for x in result] + + +class test_pipe_fittings(gr_unittest.TestCase): + + def setUp(self): + self.fg = gr.flow_graph () + + def tearDown(self): + self.fg = None + + def test_001(self): + """ + Test stream_to_streams. + """ + n = 8 + src_len = n * 8 + src_data = range(src_len) + + expected_results = calc_expected_result(src_data, n) + #print "expected results: ", expected_results + src = gr.vector_source_i(src_data) + op = gr.stream_to_streams(gr.sizeof_int, n) + self.fg.connect(src, op) + + dsts = [] + for i in range(n): + dst = gr.vector_sink_i() + self.fg.connect((op, i), (dst, 0)) + dsts.append(dst) + + self.fg.run() + + for d in range(n): + self.assertEqual(expected_results[d], dsts[d].data()) + + def test_002(self): + """ + Test streams_to_stream (using stream_to_streams). + """ + n = 8 + src_len = n * 8 + src_data = tuple(range(src_len)) + expected_results = src_data + + src = gr.vector_source_i(src_data) + op1 = gr.stream_to_streams(gr.sizeof_int, n) + op2 = gr.streams_to_stream(gr.sizeof_int, n) + dst = gr.vector_sink_i() + + self.fg.connect(src, op1) + for i in range(n): + self.fg.connect((op1, i), (op2, i)) + self.fg.connect(op2, dst) + + self.fg.run() + self.assertEqual(expected_results, dst.data()) + + def test_003(self): + """ + Test streams_to_vector (using stream_to_streams & vector_to_stream). + """ + n = 8 + src_len = n * 8 + src_data = tuple(range(src_len)) + expected_results = src_data + + src = gr.vector_source_i(src_data) + op1 = gr.stream_to_streams(gr.sizeof_int, n) + op2 = gr.streams_to_vector(gr.sizeof_int, n) + op3 = gr.vector_to_stream(gr.sizeof_int, n) + dst = gr.vector_sink_i() + + self.fg.connect(src, op1) + for i in range(n): + self.fg.connect((op1, i), (op2, i)) + self.fg.connect(op2, op3, dst) + + self.fg.run() + self.assertEqual(expected_results, dst.data()) + + def test_004(self): + """ + Test vector_to_streams. + """ + n = 8 + src_len = n * 8 + src_data = tuple(range(src_len)) + expected_results = src_data + + src = gr.vector_source_i(src_data) + op1 = gr.stream_to_vector(gr.sizeof_int, n) + op2 = gr.vector_to_streams(gr.sizeof_int, n) + op3 = gr.streams_to_stream(gr.sizeof_int, n) + dst = gr.vector_sink_i() + + self.fg.connect(src, op1, op2) + for i in range(n): + self.fg.connect((op2, i), (op3, i)) + self.fg.connect(op3, dst) + + self.fg.run() + self.assertEqual(expected_results, dst.data()) + +if __name__ == '__main__': + gr_unittest.main () + diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_rational_resampler.py b/gnuradio-core/src/python/gnuradio/gr/qa_rational_resampler.py new file mode 100755 index 000000000..2505482d5 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_rational_resampler.py @@ -0,0 +1,290 @@ +#!/usr/bin/env python +# +# Copyright 2005,2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest +from gnuradio import blks +import math +import random +import sys + +#import os +#print os.getpid() +#raw_input('Attach with gdb, then press Enter: ') + + +def random_floats(n): + r = [] + for x in xrange(n): + r.append(float(random.randint(-32768, 32768))) + return tuple(r) + + +def reference_dec_filter(src_data, decim, taps): + fg = gr.flow_graph() + src = gr.vector_source_f(src_data) + op = gr.fir_filter_fff(decim, taps) + dst = gr.vector_sink_f() + fg.connect(src, op, dst) + fg.run() + result_data = dst.data() + fg = None + return result_data + +def reference_interp_filter(src_data, interp, taps): + fg = gr.flow_graph() + src = gr.vector_source_f(src_data) + op = gr.interp_fir_filter_fff(interp, taps) + dst = gr.vector_sink_f() + fg.connect(src, op, dst) + fg.run() + result_data = dst.data() + fg = None + return result_data + +def reference_interp_dec_filter(src_data, interp, decim, taps): + fg = gr.flow_graph() + src = gr.vector_source_f(src_data) + up = gr.interp_fir_filter_fff(interp, (1,)) + dn = gr.fir_filter_fff(decim, taps) + dst = gr.vector_sink_f() + fg.connect(src, up, dn, dst) + fg.run() + result_data = dst.data() + fg = None + return result_data + + +class test_rational_resampler (gr_unittest.TestCase): + + def setUp(self): + self.fg = gr.flow_graph() + + def tearDown(self): + self.fg = None + + # + # test the gr.rational_resampler_base primitives... + # + + def test_000_1_to_1(self): + taps = (-4, 5) + src_data = (234, -4, 23, -56, 45, 98, -23, -7) + xr = (-936, 1186, -112, 339, -460, -167, 582) + expected_result = tuple([float(x) for x in xr]) + + src = gr.vector_source_f(src_data) + op = gr.rational_resampler_base_fff(1, 1, taps) + dst = gr.vector_sink_f() + self.fg.connect(src, op) + self.fg.connect(op, dst) + self.fg.run() + result_data = dst.data() + self.assertEqual(expected_result, result_data) + + def test_001_interp(self): + taps = [1, 10, 100, 1000, 10000] + src_data = (0, 2, 3, 5, 7, 11, 13, 17) + interpolation = 3 + xr = (0,0,0,0,2,20,200,2003,20030,300,3005,30050,500,5007,50070,700,7011,70110,1100,11013,110130,1300,13017,130170,1700.0,17000.0,170000.0) + expected_result = tuple([float(x) for x in xr]) + + src = gr.vector_source_f(src_data) + op = gr.rational_resampler_base_fff(interpolation, 1, taps) + dst = gr.vector_sink_f() + self.fg.connect(src, op) + self.fg.connect(op, dst) + self.fg.run() + result_data = dst.data() + self.assertEqual(expected_result, result_data) + + def test_002_interp(self): + taps = random_floats(31) + #src_data = random_floats(10000) # FIXME the 10k case fails! + src_data = random_floats(1000) + interpolation = 3 + + expected_result = reference_interp_filter(src_data, interpolation, taps) + + src = gr.vector_source_f(src_data) + op = gr.rational_resampler_base_fff(interpolation, 1, taps) + dst = gr.vector_sink_f() + self.fg.connect(src, op) + self.fg.connect(op, dst) + self.fg.run() + result_data = dst.data() + + L1 = len(result_data) + L2 = len(expected_result) + L = min(L1, L2) + if False: + sys.stderr.write('delta = %2d: ntaps = %d interp = %d ilen = %d\n' % + (L2 - L1, len(taps), interpolation, len(src_data))) + sys.stderr.write(' len(result_data) = %d len(expected_result) = %d\n' % + (len(result_data), len(expected_result))) + #self.assertEqual(expected_result[0:L], result_data[0:L]) + # FIXME check first 3 answers + self.assertEqual(expected_result[3:L], result_data[3:L]) + + def test_003_interp(self): + taps = random_floats(31) + src_data = random_floats(10000) + decimation = 3 + + expected_result = reference_dec_filter(src_data, decimation, taps) + + src = gr.vector_source_f(src_data) + op = gr.rational_resampler_base_fff(1, decimation, taps) + dst = gr.vector_sink_f() + self.fg.connect(src, op) + self.fg.connect(op, dst) + self.fg.run() + result_data = dst.data() + + L1 = len(result_data) + L2 = len(expected_result) + L = min(L1, L2) + if False: + sys.stderr.write('delta = %2d: ntaps = %d decim = %d ilen = %d\n' % + (L2 - L1, len(taps), decimation, len(src_data))) + sys.stderr.write(' len(result_data) = %d len(expected_result) = %d\n' % + (len(result_data), len(expected_result))) + self.assertEqual(expected_result[0:L], result_data[0:L]) + + # FIXME disabled. Triggers hang on SuSE 10.0 + def xtest_004_decim_random_vals(self): + MAX_TAPS = 9 + MAX_DECIM = 7 + OUTPUT_LEN = 9 + + random.seed(0) # we want reproducibility + + for ntaps in xrange(1, MAX_TAPS + 1): + for decim in xrange(1, MAX_DECIM+1): + for ilen in xrange(ntaps + decim, ntaps + OUTPUT_LEN*decim): + src_data = random_floats(ilen) + taps = random_floats(ntaps) + expected_result = reference_dec_filter(src_data, decim, taps) + + fg = gr.flow_graph() + src = gr.vector_source_f(src_data) + op = gr.rational_resampler_base_fff(1, decim, taps) + dst = gr.vector_sink_f() + fg.connect(src, op, dst) + fg.run() + fg = None + result_data = dst.data() + L1 = len(result_data) + L2 = len(expected_result) + L = min(L1, L2) + if False: + sys.stderr.write('delta = %2d: ntaps = %d decim = %d ilen = %d\n' % (L2 - L1, ntaps, decim, ilen)) + sys.stderr.write(' len(result_data) = %d len(expected_result) = %d\n' % + (len(result_data), len(expected_result))) + self.assertEqual(expected_result[0:L], result_data[0:L]) + + + # FIXME disabled. Triggers hang on SuSE 10.0 + def xtest_005_interp_random_vals(self): + MAX_TAPS = 9 + MAX_INTERP = 7 + INPUT_LEN = 9 + + random.seed(0) # we want reproducibility + + for ntaps in xrange(1, MAX_TAPS + 1): + for interp in xrange(1, MAX_INTERP+1): + for ilen in xrange(ntaps, ntaps + INPUT_LEN): + src_data = random_floats(ilen) + taps = random_floats(ntaps) + expected_result = reference_interp_filter(src_data, interp, taps) + + fg = gr.flow_graph() + src = gr.vector_source_f(src_data) + op = gr.rational_resampler_base_fff(interp, 1, taps) + dst = gr.vector_sink_f() + fg.connect(src, op, dst) + fg.run() + fg = None + result_data = dst.data() + L1 = len(result_data) + L2 = len(expected_result) + L = min(L1, L2) + #if True or abs(L1-L2) > 1: + if False: + sys.stderr.write('delta = %2d: ntaps = %d interp = %d ilen = %d\n' % (L2 - L1, ntaps, interp, ilen)) + #sys.stderr.write(' len(result_data) = %d len(expected_result) = %d\n' % + # (len(result_data), len(expected_result))) + #self.assertEqual(expected_result[0:L], result_data[0:L]) + # FIXME check first ntaps+1 answers + self.assertEqual(expected_result[ntaps+1:L], result_data[ntaps+1:L]) + + + def test_006_interp_decim(self): + taps = (0,1,0,0) + src_data = range(10000) + interp = 3 + decimation = 2 + + expected_result = reference_interp_dec_filter(src_data, interp, decimation, taps) + + src = gr.vector_source_f(src_data) + op = gr.rational_resampler_base_fff(interp, decimation, taps) + dst = gr.vector_sink_f() + self.fg.connect(src, op) + self.fg.connect(op, dst) + self.fg.run() + result_data = dst.data() + + L1 = len(result_data) + L2 = len(expected_result) + L = min(L1, L2) + if False: + sys.stderr.write('delta = %2d: ntaps = %d decim = %d ilen = %d\n' % + (L2 - L1, len(taps), decimation, len(src_data))) + sys.stderr.write(' len(result_data) = %d len(expected_result) = %d\n' % + (len(result_data), len(expected_result))) + self.assertEqual(expected_result[1:L], result_data[1:L]) + + # + # test the blks.rational_resampler_??? primitives... + # + + def test_101_interp(self): + taps = [1, 10, 100, 1000, 10000] + src_data = (0, 2, 3, 5, 7, 11, 13, 17) + interpolation = 3 + xr = (0,0,0,0,2,20,200,2003,20030,300,3005,30050,500,5007,50070,700,7011,70110,1100,11013,110130,1300,13017,130170,1700.0,17000.0,170000.0) + expected_result = tuple([float(x) for x in xr]) + + src = gr.vector_source_f(src_data) + op = blks.rational_resampler_fff(self.fg, interpolation, 1, taps=taps) + dst = gr.vector_sink_f() + self.fg.connect(src, op) + self.fg.connect(op, dst) + self.fg.run() + result_data = dst.data() + self.assertEqual(expected_result, result_data) + + +if __name__ == '__main__': + gr_unittest.main() + diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_sig_source.py b/gnuradio-core/src/python/gnuradio/gr/qa_sig_source.py new file mode 100755 index 000000000..39866ac43 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_sig_source.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest +import math + +class test_sig_source (gr_unittest.TestCase): + + def setUp (self): + self.fg = gr.flow_graph () + + def tearDown (self): + self.fg = None + + def test_const_f (self): + fg = self.fg + expected_result = (1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5) + src1 = gr.sig_source_f (1e6, gr.GR_CONST_WAVE, 0, 1.5) + op = gr.head (gr.sizeof_float, 10) + dst1 = gr.vector_sink_f () + fg.connect (src1, op) + fg.connect (op, dst1) + fg.run () + dst_data = dst1.data () + self.assertEqual (expected_result, dst_data) + + def test_const_i (self): + fg = self.fg + expected_result = (1, 1, 1, 1) + src1 = gr.sig_source_i (1e6, gr.GR_CONST_WAVE, 0, 1) + op = gr.head (gr.sizeof_int, 4) + dst1 = gr.vector_sink_i () + fg.connect (src1, op) + fg.connect (op, dst1) + fg.run () + dst_data = dst1.data () + self.assertEqual (expected_result, dst_data) + + def test_sine_f (self): + fg = self.fg + sqrt2 = math.sqrt(2) / 2 + expected_result = (0, sqrt2, 1, sqrt2, 0, -sqrt2, -1, -sqrt2, 0) + src1 = gr.sig_source_f (8, gr.GR_SIN_WAVE, 1.0, 1.0) + op = gr.head (gr.sizeof_float, 9) + dst1 = gr.vector_sink_f () + fg.connect (src1, op) + fg.connect (op, dst1) + fg.run () + dst_data = dst1.data () + self.assertFloatTuplesAlmostEqual (expected_result, dst_data, 5) + + def test_cosine_f (self): + fg = self.fg + sqrt2 = math.sqrt(2) / 2 + expected_result = (1, sqrt2, 0, -sqrt2, -1, -sqrt2, 0, sqrt2, 1) + src1 = gr.sig_source_f (8, gr.GR_COS_WAVE, 1.0, 1.0) + op = gr.head (gr.sizeof_float, 9) + dst1 = gr.vector_sink_f () + fg.connect (src1, op) + fg.connect (op, dst1) + fg.run () + dst_data = dst1.data () + self.assertFloatTuplesAlmostEqual (expected_result, dst_data, 5) + +if __name__ == '__main__': + gr_unittest.main () diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_single_pole_iir.py b/gnuradio-core/src/python/gnuradio/gr/qa_single_pole_iir.py new file mode 100755 index 000000000..5898188f8 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_single_pole_iir.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest + +class test_single_pole_iir(gr_unittest.TestCase): + + def setUp (self): + self.fg = gr.flow_graph () + + def tearDown (self): + self.fg = None + + def test_001(self): + src_data = (0, 1000, 2000, 3000, 4000, 5000) + expected_result = src_data + src = gr.vector_source_f(src_data) + op = gr.single_pole_iir_filter_ff (1.0) + dst = gr.vector_sink_f() + self.fg.connect (src, op, dst) + self.fg.run() + result_data = dst.data() + self.assertFloatTuplesAlmostEqual (expected_result, result_data) + + def test_002(self): + src_data = (0, 1000, 2000, 3000, 4000, 5000) + expected_result = (0, 125, 359.375, 689.453125, 1103.271484, 1590.36255) + src = gr.vector_source_f(src_data) + op = gr.single_pole_iir_filter_ff (0.125) + dst = gr.vector_sink_f() + self.fg.connect (src, op, dst) + self.fg.run() + result_data = dst.data() + self.assertFloatTuplesAlmostEqual (expected_result, result_data, 3) + + def test_003(self): + block_size = 2 + src_data = (0, 1000, 2000, 3000, 4000, 5000) + expected_result = (0, 125, 250, 484.375, 718.75, 1048.828125) + src = gr.vector_source_f(src_data) + s2p = gr.serial_to_parallel(gr.sizeof_float, block_size) + op = gr.single_pole_iir_filter_ff (0.125, block_size) + p2s = gr.parallel_to_serial(gr.sizeof_float, block_size) + dst = gr.vector_sink_f() + self.fg.connect (src, s2p, op, p2s, dst) + self.fg.run() + result_data = dst.data() + self.assertFloatTuplesAlmostEqual (expected_result, result_data, 3) + + +if __name__ == '__main__': + gr_unittest.main () + diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_single_pole_iir_cc.py b/gnuradio-core/src/python/gnuradio/gr/qa_single_pole_iir_cc.py new file mode 100755 index 000000000..a7889d177 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_single_pole_iir_cc.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python +# +# Copyright 2005,2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest + +class test_single_pole_iir_cc(gr_unittest.TestCase): + + def setUp (self): + self.fg = gr.flow_graph () + + def tearDown (self): + self.fg = None + + def test_001(self): + src_data = (0+0j, 1000+1000j, 2000+2000j, 3000+3000j, 4000+4000j, 5000+5000j) + expected_result = src_data + src = gr.vector_source_c(src_data) + op = gr.single_pole_iir_filter_cc (1.0) + dst = gr.vector_sink_c() + self.fg.connect (src, op, dst) + self.fg.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual (expected_result, result_data) + + def test_002(self): + src_data = (complex(0,0), complex(1000,-1000), complex(2000,-2000), complex(3000,-3000), complex(4000,-4000), complex(5000,-5000)) + expected_result = (complex(0,0), complex(125,-125), complex(359.375,-359.375), complex(689.453125,-689.453125), complex(1103.271484,-1103.271484), complex(1590.36255,-1590.36255)) + src = gr.vector_source_c(src_data) + op = gr.single_pole_iir_filter_cc (0.125) + dst = gr.vector_sink_c() + self.fg.connect (src, op, dst) + self.fg.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual (expected_result, result_data, 3) + + def test_003(self): + block_size = 2 + src_data = (complex(0,0), complex(1000,-1000), complex(2000,-2000), complex(3000,-3000), complex(4000,-4000), complex(5000,-5000)) + expected_result = (complex(0,0), complex(125,-125), complex(250,-250), complex(484.375,-484.375), complex(718.75,-718.75), complex(1048.828125,-1048.828125)) + src = gr.vector_source_c(src_data) + s2p = gr.serial_to_parallel(gr.sizeof_gr_complex, block_size) + op = gr.single_pole_iir_filter_cc (0.125, block_size) + p2s = gr.parallel_to_serial(gr.sizeof_gr_complex, block_size) + dst = gr.vector_sink_c() + self.fg.connect (src, s2p, op, p2s, dst) + self.fg.run() + result_data = dst.data() + self.assertComplexTuplesAlmostEqual (expected_result, result_data, 3) + + +if __name__ == '__main__': + gr_unittest.main () + diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_unpack_k_bits.py b/gnuradio-core/src/python/gnuradio/gr/qa_unpack_k_bits.py new file mode 100755 index 000000000..59f838a4d --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_unpack_k_bits.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# +# Copyright 2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, gr_unittest +import random + +class test_unpack(gr_unittest.TestCase): + + def setUp(self): + self.fg = gr.flow_graph () + + def tearDown(self): + self.fg = None + + def test_001(self): + src_data = (1,0,1,1,0,1,1,0) + expected_results = (1,0,1,1,0,1,1,0) + src = gr.vector_source_b(src_data,False) + op = gr.unpack_k_bits_bb(1) + dst = gr.vector_sink_b() + self.fg.connect(src, op, dst) + self.fg.run() + self.assertEqual(expected_results, dst.data()) + + def test_002(self): + src_data = ( 2, 3, 0, 1) + expected_results = (1,0,1,1,0,0,0,1) + src = gr.vector_source_b(src_data,False) + op = gr.unpack_k_bits_bb(2) + dst = gr.vector_sink_b() + self.fg.connect(src, op, dst) + self.fg.run() + self.assertEqual(expected_results, dst.data()) + + +if __name__ == '__main__': + gr_unittest.main () + diff --git a/gnuradio-core/src/python/gnuradio/gr/run_tests.in b/gnuradio-core/src/python/gnuradio/gr/run_tests.in new file mode 100755 index 000000000..87d0afdaf --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/run_tests.in @@ -0,0 +1,33 @@ +#!/bin/sh + +swigbld=@abs_top_builddir@/gnuradio-core/src/lib/swig +swigsrc=@abs_top_srcdir@/gnuradio-core/src/lib/swig +py=@abs_top_srcdir@/gnuradio-core/src/python + +PYTHONPATH="$swigbld:$swigbld/.libs:$swigsrc:$py" +export PYTHONPATH + +# for OS/X +DYLD_LIBRARY_PATH="@abs_top_builddir@/gnuradio-core/src/lib/.libs" +export DYLD_LIBRARY_PATH + +# Don't load user or system prefs +GR_DONT_LOAD_PREFS=1 +export GR_DONT_LOAD_PREFS + +ok=yes +for file in @srcdir@/qa_*.py +do + echo $file + if ! $file + then + ok=no + fi +done + +if [ $ok = yes ] +then + exit 0 +else + exit 1 +fi diff --git a/gnuradio-core/src/python/gnuradio/gr/scheduler.py b/gnuradio-core/src/python/gnuradio/gr/scheduler.py new file mode 100644 index 000000000..919d07f0a --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/scheduler.py @@ -0,0 +1,70 @@ +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio.gr.exceptions import * +from gnuradio_swig_python import single_threaded_scheduler, sts_pyrun +import gr_threading as _threading +#import threading as _threading + +class scheduler_thread(_threading.Thread): + def __init__(self, sts): + _threading.Thread.__init__(self) + self.sts = sts + def run(self): + # Invoke the single threaded scheduler's run method + # + # Note that we're in a new thread, and that sts_pyrun + # releases the global interpreter lock. This has the + # effect of evaluating the graph in parallel to the + # main line control code. + sts_pyrun(self.sts) + self.sts = None + +class scheduler(object): + def __init__(self, fg): + graphs = fg.partition_graph(fg.blocks) + # print "@@@ # graphs = %d" % (len(graphs)) + + self.state = [] + + for g in graphs: + list_of_blocks = [x.block() for x in g] + sts = single_threaded_scheduler(list_of_blocks) + thread = scheduler_thread(sts) + thread.setDaemon(1) + self.state.append((sts, thread)) + + def start(self): + for (sts, thread) in self.state: + thread.start() + + def stop(self): + for (sts, thread) in self.state: + sts.stop() + self.wait() + + def wait(self): + for (sts, thread) in self.state: + timeout = 0.100 + while True: + thread.join(timeout) + if not thread.isAlive(): + break diff --git a/gnuradio-core/src/python/gnuradio/gr_unittest.py b/gnuradio-core/src/python/gnuradio/gr_unittest.py new file mode 100755 index 000000000..a74a06153 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr_unittest.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +import unittest +import sys + +class TestCase(unittest.TestCase): + """A subclass of unittest.TestCase that adds additional assertions + + Adds new methods assertComplexAlmostEqual, + assertComplexTuplesAlmostEqual and assertFloatTuplesAlmostEqual + """ + + def assertComplexAlmostEqual (self, first, second, places=7, msg=None): + """Fail if the two complex objects are unequal as determined by their + difference rounded to the given number of decimal places + (default 7) and comparing to zero. + + Note that decimal places (from zero) is usually not the same + as significant digits (measured from the most signficant digit). + """ + if round(second.real-first.real, places) != 0: + raise self.failureException, \ + (msg or '%s != %s within %s places' % (`first`, `second`, `places` )) + if round(second.imag-first.imag, places) != 0: + raise self.failureException, \ + (msg or '%s != %s within %s places' % (`first`, `second`, `places` )) + + def assertComplexAlmostEqual2 (self, ref, x, abs_eps=1e-12, rel_eps=1e-6, msg=None): + """ + Fail if the two complex objects are unequal as determined by... + """ + if abs(ref - x) < abs_eps: + return + + if abs(ref) > abs_eps: + if abs(ref-x)/abs(ref) > rel_eps: + raise self.failureException, \ + (msg or '%s != %s rel_error = %s rel_limit = %s' % ( + `ref`, `x`, abs(ref-x)/abs(ref), `rel_eps` )) + else: + raise self.failureException, \ + (msg or '%s != %s rel_error = %s rel_limit = %s' % ( + `ref`, `x`, abs(ref-x)/abs(ref), `rel_eps` )) + + + + def assertComplexTuplesAlmostEqual (self, a, b, places=7, msg=None): + self.assertEqual (len(a), len(b)) + for i in xrange (len(a)): + self.assertComplexAlmostEqual (a[i], b[i], places, msg) + + def assertComplexTuplesAlmostEqual2 (self, ref, x, + abs_eps=1e-12, rel_eps=1e-6, msg=None): + self.assertEqual (len(ref), len(x)) + for i in xrange (len(ref)): + try: + self.assertComplexAlmostEqual2 (ref[i], x[i], abs_eps, rel_eps, msg) + except self.failureException, e: + #sys.stderr.write("index = %d " % (i,)) + #sys.stderr.write("%s\n" % (e,)) + raise + + def assertFloatTuplesAlmostEqual (self, a, b, places=7, msg=None): + self.assertEqual (len(a), len(b)) + for i in xrange (len(a)): + self.assertAlmostEqual (a[i], b[i], places, msg) + + + def assertFloatTuplesAlmostEqual2 (self, ref, x, + abs_eps=1e-12, rel_eps=1e-6, msg=None): + self.assertEqual (len(ref), len(x)) + for i in xrange (len(ref)): + try: + self.assertComplexAlmostEqual2 (ref[i], x[i], abs_eps, rel_eps, msg) + except self.failureException, e: + #sys.stderr.write("index = %d " % (i,)) + #sys.stderr.write("%s\n" % (e,)) + raise + + +TestResult = unittest.TestResult +TestSuite = unittest.TestSuite +FunctionTestCase = unittest.FunctionTestCase +TestLoader = unittest.TestLoader +TextTestRunner = unittest.TextTestRunner +TestProgram = unittest.TestProgram +main = TestProgram + +############################################################################## +# Executing this module from the command line +############################################################################## + +if __name__ == "__main__": + main(module=None) diff --git a/gnuradio-core/src/python/gnuradio/gru/Makefile.am b/gnuradio-core/src/python/gnuradio/gru/Makefile.am new file mode 100644 index 000000000..44b52b6c8 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gru/Makefile.am @@ -0,0 +1,35 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +include $(top_srcdir)/Makefile.common + +# EXTRA_DIST = run_tests.in +# TESTS = run_tests + +grblkspythondir = $(grpythondir)/gru + +grblkspython_PYTHON = \ + __init__.py + + +noinst_PYTHON = + +CLEANFILES = *.pyc *.pyo diff --git a/gnuradio-core/src/python/gnuradio/gru/__init__.py b/gnuradio-core/src/python/gnuradio/gru/__init__.py new file mode 100644 index 000000000..272c7a5f8 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gru/__init__.py @@ -0,0 +1,37 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +import glob +import os.path + +# Semi-hideous kludge to import everything in the gruimpl directory +# into the gnuradio.gru namespace. This keeps us from having to remember +# to manually update this file. + +for p in __path__: + filenames = glob.glob (os.path.join (p, "..", "gruimpl", "*.py")) + for f in filenames: + f = os.path.basename(f).lower() + f = f[:-3] + if f == '__init__': + continue + # print f + exec "from gnuradio.gruimpl.%s import *" % (f,) diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/Makefile.am b/gnuradio-core/src/python/gnuradio/gruimpl/Makefile.am new file mode 100644 index 000000000..06eb7a1f9 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gruimpl/Makefile.am @@ -0,0 +1,40 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +include $(top_srcdir)/Makefile.common + +grupythondir = $(grpythondir)/gruimpl + +grupython_PYTHON = \ + __init__.py \ + crc.py \ + freqz.py \ + gnuplot_freqz.py \ + hexint.py \ + listmisc.py \ + mathmisc.py \ + lmx2306.py \ + os_read_exactly.py \ + sdr_1000.py \ + seq_with_cursor.py \ + socket_stuff.py + +CLEANFILES = *.pyc diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/__init__.py b/gnuradio-core/src/python/gnuradio/gruimpl/__init__.py new file mode 100644 index 000000000..a4917cf64 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gruimpl/__init__.py @@ -0,0 +1 @@ +# make this a package diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/crc.py b/gnuradio-core/src/python/gnuradio/gruimpl/crc.py new file mode 100644 index 000000000..6a97c81b5 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gruimpl/crc.py @@ -0,0 +1,36 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr +from hexint import * +import struct + +def gen_and_append_crc32(s): + crc = gr.crc32(s) + return s + struct.pack(">I", hexint(crc)) + +def check_crc32(s): + msg = s[:-4] + #print "msg = '%s'" % (msg,) + actual = gr.crc32(msg) + (expected,) = struct.unpack(">I", s[-4:]) + # print "actual =", hex(actual), "expected =", hex(expected) + return (actual == expected, msg) diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/freqz.py b/gnuradio-core/src/python/gnuradio/gruimpl/freqz.py new file mode 100644 index 000000000..21704944c --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gruimpl/freqz.py @@ -0,0 +1,344 @@ +#!/usr/bin/env python +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +# This code lifted from various parts of www.scipy.org -eb 2005-01-24 + +# Copyright (c) 2001, 2002 Enthought, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# a. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# b. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# c. Neither the name of the Enthought nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +# DAMAGE. +# + +__all__ = ['freqz'] + +import Numeric +from Numeric import * +Num=Numeric + +def atleast_1d(*arys): + """ Force a sequence of arrays to each be at least 1D. + + Description: + Force an array to be at least 1D. If an array is 0D, the + array is converted to a single row of values. Otherwise, + the array is unaltered. + Arguments: + *arys -- arrays to be converted to 1 or more dimensional array. + Returns: + input array converted to at least 1D array. + """ + res = [] + for ary in arys: + ary = asarray(ary) + if len(ary.shape) == 0: + result = Numeric.array([ary[0]]) + else: + result = ary + res.append(result) + if len(res) == 1: + return res[0] + else: + return res + + +def polyval(p,x): + """Evaluate the polynomial p at x. If x is a polynomial then composition. + + Description: + + If p is of length N, this function returns the value: + p[0]*(x**N-1) + p[1]*(x**N-2) + ... + p[N-2]*x + p[N-1] + + x can be a sequence and p(x) will be returned for all elements of x. + or x can be another polynomial and the composite polynomial p(x) will be + returned. + """ + p = asarray(p) + if isinstance(x,poly1d): + y = 0 + else: + x = asarray(x) + y = Numeric.zeros(x.shape,x.typecode()) + for i in range(len(p)): + y = x * y + p[i] + return y + +class poly1d: + """A one-dimensional polynomial class. + + p = poly1d([1,2,3]) constructs the polynomial x**2 + 2 x + 3 + + p(0.5) evaluates the polynomial at the location + p.r is a list of roots + p.c is the coefficient array [1,2,3] + p.order is the polynomial order (after leading zeros in p.c are removed) + p[k] is the coefficient on the kth power of x (backwards from + sequencing the coefficient array. + + polynomials can be added, substracted, multplied and divided (returns + quotient and remainder). + asarray(p) will also give the coefficient array, so polynomials can + be used in all functions that accept arrays. + """ + def __init__(self, c_or_r, r=0): + if isinstance(c_or_r,poly1d): + for key in c_or_r.__dict__.keys(): + self.__dict__[key] = c_or_r.__dict__[key] + return + if r: + c_or_r = poly(c_or_r) + c_or_r = atleast_1d(c_or_r) + if len(c_or_r.shape) > 1: + raise ValueError, "Polynomial must be 1d only." + c_or_r = trim_zeros(c_or_r, trim='f') + if len(c_or_r) == 0: + c_or_r = Numeric.array([0]) + self.__dict__['coeffs'] = c_or_r + self.__dict__['order'] = len(c_or_r) - 1 + + def __array__(self,t=None): + if t: + return asarray(self.coeffs,t) + else: + return asarray(self.coeffs) + + def __coerce__(self,other): + return None + + def __repr__(self): + vals = repr(self.coeffs) + vals = vals[6:-1] + return "poly1d(%s)" % vals + + def __len__(self): + return self.order + + def __str__(self): + N = self.order + thestr = "0" + for k in range(len(self.coeffs)): + coefstr ='%.4g' % abs(self.coeffs[k]) + if coefstr[-4:] == '0000': + coefstr = coefstr[:-5] + power = (N-k) + if power == 0: + if coefstr != '0': + newstr = '%s' % (coefstr,) + else: + if k == 0: + newstr = '0' + else: + newstr = '' + elif power == 1: + if coefstr == '0': + newstr = '' + elif coefstr == '1': + newstr = 'x' + else: + newstr = '%s x' % (coefstr,) + else: + if coefstr == '0': + newstr = '' + elif coefstr == '1': + newstr = 'x**%d' % (power,) + else: + newstr = '%s x**%d' % (coefstr, power) + + if k > 0: + if newstr != '': + if self.coeffs[k] < 0: + thestr = "%s - %s" % (thestr, newstr) + else: + thestr = "%s + %s" % (thestr, newstr) + elif (k == 0) and (newstr != '') and (self.coeffs[k] < 0): + thestr = "-%s" % (newstr,) + else: + thestr = newstr + return _raise_power(thestr) + + + def __call__(self, val): + return polyval(self.coeffs, val) + + def __mul__(self, other): + if isscalar(other): + return poly1d(self.coeffs * other) + else: + other = poly1d(other) + return poly1d(polymul(self.coeffs, other.coeffs)) + + def __rmul__(self, other): + if isscalar(other): + return poly1d(other * self.coeffs) + else: + other = poly1d(other) + return poly1d(polymul(self.coeffs, other.coeffs)) + + def __add__(self, other): + other = poly1d(other) + return poly1d(polyadd(self.coeffs, other.coeffs)) + + def __radd__(self, other): + other = poly1d(other) + return poly1d(polyadd(self.coeffs, other.coeffs)) + + def __pow__(self, val): + if not isscalar(val) or int(val) != val or val < 0: + raise ValueError, "Power to non-negative integers only." + res = [1] + for k in range(val): + res = polymul(self.coeffs, res) + return poly1d(res) + + def __sub__(self, other): + other = poly1d(other) + return poly1d(polysub(self.coeffs, other.coeffs)) + + def __rsub__(self, other): + other = poly1d(other) + return poly1d(polysub(other.coeffs, self.coeffs)) + + def __div__(self, other): + if isscalar(other): + return poly1d(self.coeffs/other) + else: + other = poly1d(other) + return map(poly1d,polydiv(self.coeffs, other.coeffs)) + + def __rdiv__(self, other): + if isscalar(other): + return poly1d(other/self.coeffs) + else: + other = poly1d(other) + return map(poly1d,polydiv(other.coeffs, self.coeffs)) + + def __setattr__(self, key, val): + raise ValueError, "Attributes cannot be changed this way." + + def __getattr__(self, key): + if key in ['r','roots']: + return roots(self.coeffs) + elif key in ['c','coef','coefficients']: + return self.coeffs + elif key in ['o']: + return self.order + else: + return self.__dict__[key] + + def __getitem__(self, val): + ind = self.order - val + if val > self.order: + return 0 + if val < 0: + return 0 + return self.coeffs[ind] + + def __setitem__(self, key, val): + ind = self.order - key + if key < 0: + raise ValueError, "Does not support negative powers." + if key > self.order: + zr = Numeric.zeros(key-self.order,self.coeffs.typecode()) + self.__dict__['coeffs'] = Numeric.concatenate((zr,self.coeffs)) + self.__dict__['order'] = key + ind = 0 + self.__dict__['coeffs'][ind] = val + return + + def integ(self, m=1, k=0): + return poly1d(polyint(self.coeffs,m=m,k=k)) + + def deriv(self, m=1): + return poly1d(polyder(self.coeffs,m=m)) + +def freqz(b, a, worN=None, whole=0, plot=None): + """Compute frequency response of a digital filter. + + Description: + + Given the numerator (b) and denominator (a) of a digital filter compute + its frequency response. + + jw -jw -jmw + jw B(e) b[0] + b[1]e + .... + b[m]e + H(e) = ---- = ------------------------------------ + jw -jw -jnw + A(e) a[0] + a[2]e + .... + a[n]e + + Inputs: + + b, a --- the numerator and denominator of a linear filter. + worN --- If None, then compute at 512 frequencies around the unit circle. + If a single integer, the compute at that many frequencies. + Otherwise, compute the response at frequencies given in worN + whole -- Normally, frequencies are computed from 0 to pi (upper-half of + unit-circle. If whole is non-zero compute frequencies from 0 + to 2*pi. + + Outputs: (h,w) + + h -- The frequency response. + w -- The frequencies at which h was computed. + """ + b, a = map(atleast_1d, (b,a)) + if whole: + lastpoint = 2*pi + else: + lastpoint = pi + if worN is None: + N = 512 + w = Num.arange(0,lastpoint,lastpoint/N) + elif isinstance(worN, types.IntType): + N = worN + w = Num.arange(0,lastpoint,lastpoint/N) + else: + w = worN + w = atleast_1d(w) + zm1 = exp(-1j*w) + h = polyval(b[::-1], zm1) / polyval(a[::-1], zm1) + # if not plot is None: + # plot(w, h) + return h, w diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/gnuplot_freqz.py b/gnuradio-core/src/python/gnuradio/gruimpl/gnuplot_freqz.py new file mode 100755 index 000000000..5a117605a --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gruimpl/gnuplot_freqz.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +__all__ = ['gnuplot_freqz'] + +import tempfile +import os +import math +import Numeric + +from gnuradio import gr +from gnuradio.gruimpl.freqz import freqz + + +def gnuplot_freqz (hw, Fs=None, logfreq=False): + + """hw is a tuple of the form (h, w) where h is sequence of complex + freq responses, and w is a sequence of corresponding frequency + points. Plot the frequency response using gnuplot. If Fs is + provide, use it as the sampling frequency, else use 2*pi. + + Returns a handle to the gnuplot graph. When the handle is reclaimed + the graph is torn down.""" + + data_file = tempfile.NamedTemporaryFile () + cmd_file = os.popen ('gnuplot', 'w') + + h, w = hw + ampl = 20 * Numeric.log10 (Numeric.absolute (h) + 1e-9) + phase = map (lambda x: math.atan2 (x.imag, x.real), h) + + if Fs: + w *= (Fs/(2*math.pi)) + + for freq, a, ph in zip (w, ampl, phase): + data_file.write ("%g\t%g\t%g\n" % (freq, a, ph)) + + data_file.flush () + + cmd_file.write ("set grid\n") + if logfreq: + cmd_file.write ("set logscale x\n") + else: + cmd_file.write ("unset logscale x\n") + cmd_file.write ("plot '%s' using 1:2 with lines\n" % (data_file.name,)) + cmd_file.flush () + + return (cmd_file, data_file) + + +def test_plot (): + sample_rate = 2.0e6 + taps = gr.firdes.low_pass (1.0, # gain + sample_rate, # sampling rate + 200e3, # low pass cutoff freq + 100e3, # width of trans. band + gr.firdes.WIN_HAMMING) + # print len (taps) + return gnuplot_freqz (freqz (taps, 1), sample_rate) + +if __name__ == '__main__': + handle = test_plot () + raw_input ('Press Enter to continue: ') diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/hexint.py b/gnuradio-core/src/python/gnuradio/gruimpl/hexint.py new file mode 100644 index 000000000..1220755cb --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gruimpl/hexint.py @@ -0,0 +1,32 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +def hexint(mask): + """ + Convert unsigned masks into signed ints. + + This allows us to use hex constants like 0xf0f0f0f2 when talking to + our hardware and not get screwed by them getting treated as python + longs. + """ + if mask >= 2**31: + return int(mask-2**32) + return mask diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/listmisc.py b/gnuradio-core/src/python/gnuradio/gruimpl/listmisc.py new file mode 100644 index 000000000..857e417f2 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gruimpl/listmisc.py @@ -0,0 +1,29 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +def list_reverse(x): + """ + Return a copy of x that is reverse order. + """ + r = list(x) + r.reverse() + return r + diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/lmx2306.py b/gnuradio-core/src/python/gnuradio/gruimpl/lmx2306.py new file mode 100755 index 000000000..b46c896f7 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gruimpl/lmx2306.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python +# +# Copyright 2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +'''Control National LMX2306 based frequency synthesizer''' + +from gnuradio import gr +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +# bottom two bits of 21 bit word select which register to program + +R_REG = 0x0 +AB_REG = 0x1 +F_REG = 0x2 + +F_counter_reset = (1 << 2) +F_phase_detector_polarity = (1 << 7) + +F_LD_tri_state = (0 << 4) +F_LD_R_divider_output = (4 << 4) +F_LD_N_divider_output = (2 << 4) +F_LD_serial_data_output = (6 << 4) +F_LD_digital_lock_detect = (1 << 4) +F_LD_open_drain = (5 << 4) +F_LD_high = (3 << 4) +F_LD_low = (7 << 4) + +# F_default = F_LD_digital_lock_detect | F_phase_detector_polarity +F_default = F_LD_open_drain | F_phase_detector_polarity + +# +# 4 control pins: +# CE always high +# LE load enable. When LE goes high, data stored in the shift register +# is loaded into one of the three registers +# CLK data is clocked in on the rising edge +# DATA single data bit. Entered MSB first + +DB_CLK = (1 << 0) +DB_DATA = (1 << 1) +DB_LE = (1 << 2) +DB_CE = (1 << 3) + +class lmx2306 (object): + '''Control the National LMX2306 PLL''' + __slots__ = ['pp', 'shadow', 'fosc', 'r', 'step_size', 'verbose'] + def __init__ (self, fosc, step_size, which_pp = 0): + '''FOSC is the frequency of the reference oscillator, + STEP_SIZE is the step between valid frequencies, + WHICH_PP specifies which parallel port to use + ''' + self.pp = gr.make_ppio (which_pp) + self.shadow = DB_CE + self.pp.lock () + self.pp.write_data (self.shadow) + self.pp.unlock () + self.verbose = False + self._set_fosc (fosc) + self._set_step (step_size) + + + def program (self, r, a, b): + if self.verbose: + print "lmx2306: r = %d a = %d b = %d" % (r, a, b) + self.pp.lock () + self._write_word (F_REG | F_default | F_counter_reset) + self._write_word (R_REG | ((r & 0x3fff) << 2)) + self._write_word (AB_REG | ((a & 0x1f) << 2) | ((b & 0x1fff) << 7)) + self._write_word (F_REG | F_default) + self.pp.unlock () + + def set_freq (self, freq): + '''Set the PLL frequency to FREQ + + Return the actual freq value set. It will be rounded down to a + multiple of step_size + ''' + divisor = int (freq / self.step_size) + actual = divisor * self.step_size + (a, b) = self._compute_ab (divisor) + self.program (self.r, a, b) + return actual + + # ---------------------------------------------------------------- + + def _set_fosc (self, ref_oscillator_freq): + self.fosc = ref_oscillator_freq + + def _set_step (self, step_size): + r = int (self.fosc / step_size) + if r * step_size != self.fosc: + raise ValueError, "step_size is not a factor of self.fosc" + if r < 3 or r > 16383: + raise ValueError, "r is out of range" + self.r = r + self.step_size = step_size + + def _compute_ab (self, divisor): + b = divisor / 8 + a = divisor - (b * 8) + if b < 3 or b > 8191 or a > b: + raise ValueError, "Invalid divisor" + return (a, b) + + def _write_word (self, w): + for i in range(21): + if w & (1 << 20): + self._set_DATA_1 () + else: + self._set_DATA_0 () + w = (w << 1) & 0x0ffffff + self._set_CLK_1 () + self._set_CLK_0 () + self._set_LE_1 () + self._set_LE_0 () + + def _set_LE_0 (self): + self.shadow = self.shadow & ~DB_LE + self.pp.write_data (self.shadow) + + def _set_LE_1 (self): + self.shadow = self.shadow | DB_LE + self.pp.write_data (self.shadow) + + def _set_CLK_0 (self): + self.shadow = self.shadow & ~DB_CLK + self.pp.write_data (self.shadow) + + def _set_CLK_1 (self): + self.shadow = self.shadow | DB_CLK + self.pp.write_data (self.shadow) + + def _set_DATA_0 (self): + self.shadow = self.shadow & ~DB_DATA + self.pp.write_data (self.shadow) + + def _set_DATA_1 (self): + self.shadow = self.shadow | DB_DATA + self.pp.write_data (self.shadow) + +if __name__ == '__main__': + parser = OptionParser (option_class=eng_option) + parser.add_option ("-o", "--fosc", type="eng_float", default=32e6, + help="set reference oscillator freq to FREQ", metavar="FREQ") + parser.add_option ("-s", "--step-size", type="eng_float", default=10e3, + help="set the frequency step size to STEP_SIZE") + parser.add_option ("-f", "--freq", type="eng_float", default=430e6, + help="set VCO frequency to FREQ") + parser.add_option ("-v", "--verbose", action="store_true", default=False) + (options, args) = parser.parse_args () + + if options.verbose: + print "fosc = %s step = %s fvco = %s" % ( + eng_notation.num_to_str (options.fosc), + eng_notation.num_to_str (options.step_size), + eng_notation.num_to_str (options.freq)) + + lmx = lmx2306 (options.fosc, options.step_size) + lmx.verbose = options.verbose + + actual = lmx.set_freq (options.freq) + + if options.verbose: + print "fvco_actual = %s delta = %s" % ( + eng_notation.num_to_str (actual), + eng_notation.num_to_str (options.freq - actual)) diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/mathmisc.py b/gnuradio-core/src/python/gnuradio/gruimpl/mathmisc.py new file mode 100644 index 000000000..13e6de80e --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gruimpl/mathmisc.py @@ -0,0 +1,33 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +import math + +def gcd(a,b): + while b: + a,b = b, a % b + return a + +def lcm(a,b): + return a * b / gcd(a, b) + +def log2(x): + return math.log(x)/math.log(2) diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/os_read_exactly.py b/gnuradio-core/src/python/gnuradio/gruimpl/os_read_exactly.py new file mode 100644 index 000000000..afdfb514b --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gruimpl/os_read_exactly.py @@ -0,0 +1,36 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +import os + +def os_read_exactly(file_descriptor, nbytes): + """ + Replacement for os.read that blocks until it reads exactly nbytes. + + """ + s = '' + while nbytes > 0: + sbuf = os.read(file_descriptor, nbytes) + if not(sbuf): + return '' + nbytes -= len(sbuf) + s = s + sbuf + return s diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/sdr_1000.py b/gnuradio-core/src/python/gnuradio/gruimpl/sdr_1000.py new file mode 100644 index 000000000..5de23b720 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gruimpl/sdr_1000.py @@ -0,0 +1,84 @@ +# +# Copyright 2003,2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr + +class sdr_1000 (gr.sdr_1000_base): + "Control the DDS on the SDR-1000" + def __init__(self, pport = 0): + gr.sdr_1000_base.__init__(self, pport) + self.write_latch (3, 0x00, 0xC0) # Reset low, WRS/ low + self.write_reg (0x20, 0x40) + + def write_reg(self, addr, data): + self.write_latch (3, addr & 0x3f, 0x3f) + self.write_latch (2, data, 0xff) + self.write_latch (3, 0x40, 0x40) + self.write_latch (3, 0x00, 0x40) + + def set_freq(self, freq): + self.set_band (freq) + ftw = freq / 200e6; + for i in xrange(6): + word = int(ftw * 256) + ftw = ftw*256 - word + # print (('%d [%02x]') % (i, word)) + self.write_reg (4+i, word) + + def set_band (self, freq): + if freq <= 2.25e6: + band = 0 + elif freq <= 5.5e6: + band = 1 + elif freq <= 11e6: + band = 3 # due to wiring mistake on board + elif freq <= 22e6: + band = 2 # due to wiring mistake on board + elif freq <= 37.5e6: + band = 4 + else: + band = 5 + + self.write_latch (1, 1 << band, 0x3f) + + def set_bit (self, reg, bit, state): + val = 0x00 + if state: val = 1<<bit + self.write_latch (reg, val, 1<<bit) + + def set_tx (self, on = 1): + self.set_bit(1, 6, on) + + def set_rx (self): + self.set_bit(1, 6, 0) + + def set_gain (self, high): + self.set_bit(0, 7, high) + + def set_mute (self, mute = 1): + self.set_bit(1, 7, mute) + + def set_unmute (self): + self.set_bit(1, 7, 0) + + def set_external_pin (self, pin, on = 1): + assert (pin < 8 and pin > 0), "Out of range 1..7" + self.set_bit(0, pin-1, on) diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/seq_with_cursor.py b/gnuradio-core/src/python/gnuradio/gruimpl/seq_with_cursor.py new file mode 100644 index 000000000..5616dea9d --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gruimpl/seq_with_cursor.py @@ -0,0 +1,77 @@ +# +# Copyright 2003,2004 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +# misc utilities + +import types +import exceptions + +class seq_with_cursor (object): + __slots__ = [ 'items', 'index' ] + + def __init__ (self, items, initial_index = None, initial_value = None): + assert len (items) > 0, "seq_with_cursor: len (items) == 0" + self.items = items + self.set_index (initial_index) + if initial_value is not None: + self.set_index_by_value(initial_value) + + def set_index (self, initial_index): + if initial_index is None: + self.index = len (self.items) / 2 + elif initial_index >= 0 and initial_index < len (self.items): + self.index = initial_index + else: + raise exceptions.ValueError + + def set_index_by_value(self, v): + """ + Set index to the smallest value such that items[index] >= v. + If there is no such item, set index to the maximum value. + """ + self.set_index(0) # side effect! + cv = self.current() + more = True + while cv < v and more: + cv, more = self.next() # side effect! + + def next (self): + new_index = self.index + 1 + if new_index < len (self.items): + self.index = new_index + return self.items[new_index], True + else: + return self.items[self.index], False + + def prev (self): + new_index = self.index - 1 + if new_index >= 0: + self.index = new_index + return self.items[new_index], True + else: + return self.items[self.index], False + + def current (self): + return self.items[self.index] + + def get_seq (self): + return self.items[:] # copy of items + diff --git a/gnuradio-core/src/python/gnuradio/gruimpl/socket_stuff.py b/gnuradio-core/src/python/gnuradio/gruimpl/socket_stuff.py new file mode 100644 index 000000000..05be0d432 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gruimpl/socket_stuff.py @@ -0,0 +1,56 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +# random socket related stuff + +import socket +import os +import sys + +def tcp_connect_or_die(sock_addr): + """ + @param sock_addr: (host, port) to connect to + @type sock_addr: tuple + @returns: socket or exits + """ + s = socket.socket (socket.AF_INET, socket.SOCK_STREAM) + try: + s.connect(sock_addr) + except socket.error, err: + sys.stderr.write('Failed to connect to %s: %s\n' % + (sock_addr, os.strerror (err.args[0]),)) + sys.exit(1) + return s + +def udp_connect_or_die(sock_addr): + """ + @param sock_addr: (host, port) to connect to + @type sock_addr: tuple + @returns: socket or exits + """ + s = socket.socket (socket.AF_INET, socket.SOCK_DGRAM) + try: + s.connect(sock_addr) + except socket.error, err: + sys.stderr.write('Failed to connect to %s: %s\n' % + (sock_addr, os.strerror (err.args[0]),)) + sys.exit(1) + return s diff --git a/gnuradio-core/src/python/gnuradio/optfir.py b/gnuradio-core/src/python/gnuradio/optfir.py new file mode 100644 index 000000000..eb3f321be --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/optfir.py @@ -0,0 +1,242 @@ +# +# Copyright 2004,2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +''' +Routines for designing optimal FIR filters. + +For a great intro to how all this stuff works, see section 6.6 of +"Digital Signal Processing: A Practical Approach", Emmanuael C. Ifeachor +and Barrie W. Jervis, Adison-Wesley, 1993. ISBN 0-201-54413-X. +''' + +import math +from gnuradio import gr + +remez = gr.remez + +# ---------------------------------------------------------------- + +def low_pass (gain, Fs, freq1, freq2, passband_ripple_db, stopband_atten_db, + nextra_taps=0): + passband_dev = passband_ripple_to_dev (passband_ripple_db) + stopband_dev = stopband_atten_to_dev (stopband_atten_db) + desired_ampls = (gain, 0) + (n, fo, ao, w) = remezord ([freq1, freq2], desired_ampls, + [passband_dev, stopband_dev], Fs) + taps = gr.remez (n + nextra_taps, fo, ao, w, "bandpass") + return taps + +# FIXME high_passs is broken... +def high_pass (Fs, freq1, freq2, stopband_atten_db, passband_ripple_db, + nextra_taps=0): + """FIXME: broken""" + passband_dev = passband_ripple_to_dev (passband_ripple_db) + stopband_dev = stopband_atten_to_dev (stopband_atten_db) + desired_ampls = (0, 1) + (n, fo, ao, w) = remezord ([freq1, freq2], desired_ampls, + [stopband_dev, passband_dev], Fs) + taps = gr.remez (n + nextra_taps, fo, ao, w, "bandpass") + return taps + +# ---------------------------------------------------------------- + +def stopband_atten_to_dev (atten_db): + """Convert a stopband attenuation in dB to an absolute value""" + return 10**(-atten_db/20) + +def passband_ripple_to_dev (ripple_db): + """Convert passband ripple spec expressed in dB to an absolute value""" + return (10**(ripple_db/20)-1)/(10**(ripple_db/20)+1) + +# ---------------------------------------------------------------- + +def remezord (fcuts, mags, devs, fsamp = 2): + ''' + FIR order estimator (lowpass, highpass, bandpass, mulitiband). + + (n, fo, ao, w) = remezord (f, a, dev) + (n, fo, ao, w) = remezord (f, a, dev, fs) + + (n, fo, ao, w) = remezord (f, a, dev) finds the approximate order, + normalized frequency band edges, frequency band amplitudes, and + weights that meet input specifications f, a, and dev, to use with + the remez command. + + * f is a sequence of frequency band edges (between 0 and Fs/2, where + Fs is the sampling frequency), and a is a sequence specifying the + desired amplitude on the bands defined by f. The length of f is + twice the length of a, minus 2. The desired function is + piecewise constant. + + * dev is a sequence the same size as a that specifies the maximum + allowable deviation or ripples between the frequency response + and the desired amplitude of the output filter, for each band. + + Use remez with the resulting order n, frequency sequence fo, + amplitude response sequence ao, and weights w to design the filter b + which approximately meets the specifications given by remezord + input parameters f, a, and dev: + + b = remez (n, fo, ao, w) + + (n, fo, ao, w) = remezord (f, a, dev, Fs) specifies a sampling frequency Fs. + + Fs defaults to 2 Hz, implying a Nyquist frequency of 1 Hz. You can + therefore specify band edges scaled to a particular applications + sampling frequency. + + In some cases remezord underestimates the order n. If the filter + does not meet the specifications, try a higher order such as n+1 + or n+2. + ''' + # get local copies + fcuts = fcuts[:] + mags = mags[:] + devs = devs[:] + + for i in range (len (fcuts)): + fcuts[i] = float (fcuts[i]) / fsamp + + nf = len (fcuts) + nm = len (mags) + nd = len (devs) + nbands = nm + + if nm != nd: + raise ValueError, "Length of mags and devs must be equal" + + if nf != 2 * (nbands - 1): + raise ValueError, "Length of f must be 2 * len (mags) - 2" + + for i in range (len (mags)): + if mags[i] != 0: # if not stopband, get relative deviation + devs[i] = devs[i] / mags[i] + + # separate the passband and stopband edges + f1 = fcuts[0::2] + f2 = fcuts[1::2] + + n = 0 + min_delta = 2 + for i in range (len (f1)): + if f2[i] - f1[i] < min_delta: + n = i + min_delta = f2[i] - f1[i] + + if nbands == 2: + # lowpass or highpass case (use formula) + l = lporder (f1[n], f2[n], devs[0], devs[1]) + else: + # bandpass or multipass case + # try different lowpasses and take the worst one that + # goes through the BP specs + l = 0 + for i in range (1, nbands-1): + l1 = lporder (f1[i-1], f2[i-1], devs[i], devs[i-1]) + l2 = lporder (f1[i], f2[i], devs[i], devs[i+1]) + l = max (l, l1, l2) + + n = int (math.ceil (l)) - 1 # need order, not length for remez + + # cook up remez compatible result + ff = [0] + fcuts + [1] + for i in range (1, len (ff) - 1): + ff[i] *= 2 + + aa = [] + for a in mags: + aa = aa + [a, a] + + max_dev = max (devs) + wts = [1] * len(devs) + for i in range (len (wts)): + wts[i] = max_dev / devs[i] + + return (n, ff, aa, wts) + +# ---------------------------------------------------------------- + +def lporder (freq1, freq2, delta_p, delta_s): + ''' + FIR lowpass filter length estimator. freq1 and freq2 are + normalized to the sampling frequency. delta_p is the passband + deviation (ripple), delta_s is the stopband deviation (ripple). + + Note, this works for high pass filters too (freq1 > freq2), but + doesnt work well if the transition is near f == 0 or f == fs/2 + + From Herrmann et al (1973), Practical design rules for optimum + finite impulse response filters. Bell System Technical J., 52, 769-99 + ''' + df = abs (freq2 - freq1) + ddp = math.log10 (delta_p) + dds = math.log10 (delta_s) + + a1 = 5.309e-3 + a2 = 7.114e-2 + a3 = -4.761e-1 + a4 = -2.66e-3 + a5 = -5.941e-1 + a6 = -4.278e-1 + + b1 = 11.01217 + b2 = 0.5124401 + + t1 = a1 * ddp * ddp + t2 = a2 * ddp + t3 = a4 * ddp * ddp + t4 = a5 * ddp + + dinf=((t1 + t2 + a3) * dds) + (t3 + t4 + a6) + ff = b1 + b2 * (ddp - dds) + n = dinf / df - ff * df + 1 + return n + + +def bporder (freq1, freq2, delta_p, delta_s): + ''' + FIR bandpass filter length estimator. freq1 and freq2 are + normalized to the sampling frequency. delta_p is the passband + deviation (ripple), delta_s is the stopband deviation (ripple). + + From Mintzer and Liu (1979) + ''' + df = abs (freq2 - freq1) + ddp = math.log10 (delta_p) + dds = math.log10 (delta_s) + + a1 = 0.01201 + a2 = 0.09664 + a3 = -0.51325 + a4 = 0.00203 + a5 = -0.57054 + a6 = -0.44314 + + t1 = a1 * ddp * ddp + t2 = a2 * ddp + t3 = a4 * ddp * ddp + t4 = a5 * ddp + + cinf = dds * (t1 + t2 + a3) + t3 + t4 + a6 + ginf = -14.6 * math.log10 (delta_p / delta_s) - 16.9 + n = cinf / df + ginf * df + 1 + return n + diff --git a/gnuradio-core/src/python/gnuradio/packet_utils.py b/gnuradio-core/src/python/gnuradio/packet_utils.py new file mode 100644 index 000000000..d7235540f --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/packet_utils.py @@ -0,0 +1,433 @@ +# +# Copyright 2005,2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +import struct +import Numeric +from gnuradio import gru + + +def conv_packed_binary_string_to_1_0_string(s): + """ + '\xAF' --> '10101111' + """ + r = [] + for ch in s: + x = ord(ch) + for i in range(7,-1,-1): + t = (x >> i) & 0x1 + r.append(t) + + return ''.join(map(lambda x: chr(x + ord('0')), r)) + +def conv_1_0_string_to_packed_binary_string(s): + """ + '10101111' -> ('\xAF', False) + + Basically the inverse of conv_packed_binary_string_to_1_0_string, + but also returns a flag indicating if we had to pad with leading zeros + to get to a multiple of 8. + """ + if not is_1_0_string(s): + raise ValueError, "Input must be a string containing only 0's and 1's" + + # pad to multiple of 8 + padded = False + rem = len(s) % 8 + if rem != 0: + npad = 8 - rem + s = '0' * npad + s + padded = True + + assert len(s) % 8 == 0 + + r = [] + i = 0 + while i < len(s): + t = 0 + for j in range(8): + t = (t << 1) | (ord(s[i + j]) - ord('0')) + r.append(chr(t)) + i += 8 + return (''.join(r), padded) + + +default_access_code = \ + conv_packed_binary_string_to_1_0_string('\xAC\xDD\xA4\xE2\xF2\x8C\x20\xFC') + + +def is_1_0_string(s): + if not isinstance(s, str): + return False + for ch in s: + if not ch in ('0', '1'): + return False + return True + +def string_to_hex_list(s): + return map(lambda x: hex(ord(x)), s) + + +def whiten(s): + sa = Numeric.fromstring(s, Numeric.UnsignedInt8) + z = sa ^ random_mask_vec8[0:len(sa)] + return z.tostring() + +def dewhiten(s): + return whiten(s) # self inverse + + +def make_header(payload_len): + return struct.pack('!HH', payload_len, payload_len) + +def make_packet(payload, spb, bits_per_baud, access_code=default_access_code, pad_for_usrp=True): + """ + Build a packet, given access code and payload. + + @param payload: packet payload, len [0, 4096] + @param spb: samples per baud (needed for padding calculation) + @type spb: int + @param bits_per_baud: (needed for padding calculation) + @type bits_per_baud: int + @param access_code: string of ascii 0's and 1's + + Packet will have access code at the beginning, followed by length, payload + and finally CRC-32. + """ + if not is_1_0_string(access_code): + raise ValueError, "access_code must be a string containing only 0's and 1's (%r)" % (access_code,) + + (packed_access_code, padded) = conv_1_0_string_to_packed_binary_string(access_code) + + payload_with_crc = gru.gen_and_append_crc32(payload) + #print "outbound crc =", string_to_hex_list(payload_with_crc[-4:]) + + L = len(payload_with_crc) + MAXLEN = len(random_mask_tuple) + if L > MAXLEN: + raise ValueError, "len(payload) must be in [0, %d]" % (MAXLEN,) + + pkt = ''.join((packed_access_code, make_header(L), whiten(payload_with_crc), '\x55')) + if pad_for_usrp: + pkt = pkt + (_npadding_bytes(len(pkt), spb, bits_per_baud) * '\x55') + + #print "make_packet: len(pkt) =", len(pkt) + return pkt + +def _npadding_bytes(pkt_byte_len, spb, bits_per_baud): + """ + Generate sufficient padding such that each packet ultimately ends + up being a multiple of 512 bytes when sent across the USB. We + send 4-byte samples across the USB (16-bit I and 16-bit Q), thus + we want to pad so that after modulation the resulting packet + is a multiple of 128 samples. + + @param ptk_byte_len: len in bytes of packet, not including padding. + @param spb: samples per baud == samples per bit (1 bit / baud with GMSK) + @type spb: int + + @returns number of bytes of padding to append. + """ + modulus = 128 + byte_modulus = gru.lcm(modulus/8, spb) * bits_per_baud / spb + r = pkt_byte_len % byte_modulus + if r == 0: + return 0 + return byte_modulus - r + + +def unmake_packet(whitened_payload_with_crc): + """ + Return (ok, payload) + + @param whitened_payload_with_crc: string + """ + payload_with_crc = dewhiten(whitened_payload_with_crc) + ok, payload = gru.check_crc32(payload_with_crc) + + if 0: + print "payload_with_crc =", string_to_hex_list(payload_with_crc) + print "ok = %r, len(payload) = %d" % (ok, len(payload)) + print "payload =", string_to_hex_list(payload) + + return ok, payload + + +# FYI, this PN code is the output of a 15-bit LFSR +random_mask_tuple = ( + 255, 63, 0, 16, 0, 12, 0, 5, 192, 3, 16, 1, 204, 0, 85, 192, + 63, 16, 16, 12, 12, 5, 197, 195, 19, 17, 205, 204, 85, 149, 255, 47, + 0, 28, 0, 9, 192, 6, 208, 2, 220, 1, 153, 192, 106, 208, 47, 28, + 28, 9, 201, 198, 214, 210, 222, 221, 152, 89, 170, 186, 255, 51, 0, 21, + 192, 15, 16, 4, 12, 3, 69, 193, 243, 16, 69, 204, 51, 21, 213, 207, + 31, 20, 8, 15, 70, 132, 50, 227, 85, 137, 255, 38, 192, 26, 208, 11, + 28, 7, 73, 194, 182, 209, 182, 220, 118, 217, 230, 218, 202, 219, 23, 27, + 78, 139, 116, 103, 103, 106, 170, 175, 63, 60, 16, 17, 204, 12, 85, 197, + 255, 19, 0, 13, 192, 5, 144, 3, 44, 1, 221, 192, 89, 144, 58, 236, + 19, 13, 205, 197, 149, 147, 47, 45, 220, 29, 153, 201, 170, 214, 255, 30, + 192, 8, 80, 6, 188, 2, 241, 193, 132, 80, 99, 124, 41, 225, 222, 200, + 88, 86, 186, 190, 243, 48, 69, 212, 51, 31, 85, 200, 63, 22, 144, 14, + 236, 4, 77, 195, 117, 145, 231, 44, 74, 157, 247, 41, 134, 158, 226, 232, + 73, 142, 182, 228, 118, 203, 102, 215, 106, 222, 175, 24, 124, 10, 161, 199, + 56, 82, 146, 189, 173, 177, 189, 180, 113, 183, 100, 118, 171, 102, 255, 106, + 192, 47, 16, 28, 12, 9, 197, 198, 211, 18, 221, 205, 153, 149, 170, 239, + 63, 12, 16, 5, 204, 3, 21, 193, 207, 16, 84, 12, 63, 69, 208, 51, + 28, 21, 201, 207, 22, 212, 14, 223, 68, 88, 51, 122, 149, 227, 47, 9, + 220, 6, 217, 194, 218, 209, 155, 28, 107, 73, 239, 118, 204, 38, 213, 218, + 223, 27, 24, 11, 74, 135, 119, 34, 166, 153, 186, 234, 243, 15, 5, 196, + 3, 19, 65, 205, 240, 85, 132, 63, 35, 80, 25, 252, 10, 193, 199, 16, + 82, 140, 61, 165, 209, 187, 28, 115, 73, 229, 246, 203, 6, 215, 66, 222, + 177, 152, 116, 106, 167, 111, 58, 172, 19, 61, 205, 209, 149, 156, 111, 41, + 236, 30, 205, 200, 85, 150, 191, 46, 240, 28, 68, 9, 243, 70, 197, 242, + 211, 5, 157, 195, 41, 145, 222, 236, 88, 77, 250, 181, 131, 55, 33, 214, + 152, 94, 234, 184, 79, 50, 180, 21, 183, 79, 54, 180, 22, 247, 78, 198, + 180, 82, 247, 125, 134, 161, 162, 248, 121, 130, 162, 225, 185, 136, 114, 230, + 165, 138, 251, 39, 3, 90, 129, 251, 32, 67, 88, 49, 250, 148, 67, 47, + 113, 220, 36, 89, 219, 122, 219, 99, 27, 105, 203, 110, 215, 108, 94, 173, + 248, 125, 130, 161, 161, 184, 120, 114, 162, 165, 185, 187, 50, 243, 85, 133, + 255, 35, 0, 25, 192, 10, 208, 7, 28, 2, 137, 193, 166, 208, 122, 220, + 35, 25, 217, 202, 218, 215, 27, 30, 139, 72, 103, 118, 170, 166, 255, 58, + 192, 19, 16, 13, 204, 5, 149, 195, 47, 17, 220, 12, 89, 197, 250, 211, + 3, 29, 193, 201, 144, 86, 236, 62, 205, 208, 85, 156, 63, 41, 208, 30, + 220, 8, 89, 198, 186, 210, 243, 29, 133, 201, 163, 22, 249, 206, 194, 212, + 81, 159, 124, 104, 33, 238, 152, 76, 106, 181, 239, 55, 12, 22, 133, 206, + 227, 20, 73, 207, 118, 212, 38, 223, 90, 216, 59, 26, 147, 75, 45, 247, + 93, 134, 185, 162, 242, 249, 133, 130, 227, 33, 137, 216, 102, 218, 170, 219, + 63, 27, 80, 11, 124, 7, 97, 194, 168, 81, 190, 188, 112, 113, 228, 36, + 75, 91, 119, 123, 102, 163, 106, 249, 239, 2, 204, 1, 149, 192, 111, 16, + 44, 12, 29, 197, 201, 147, 22, 237, 206, 205, 148, 85, 175, 127, 60, 32, + 17, 216, 12, 90, 133, 251, 35, 3, 89, 193, 250, 208, 67, 28, 49, 201, + 212, 86, 223, 126, 216, 32, 90, 152, 59, 42, 147, 95, 45, 248, 29, 130, + 137, 161, 166, 248, 122, 194, 163, 17, 185, 204, 114, 213, 229, 159, 11, 40, + 7, 94, 130, 184, 97, 178, 168, 117, 190, 167, 48, 122, 148, 35, 47, 89, + 220, 58, 217, 211, 26, 221, 203, 25, 151, 74, 238, 183, 12, 118, 133, 230, + 227, 10, 201, 199, 22, 210, 142, 221, 164, 89, 187, 122, 243, 99, 5, 233, + 195, 14, 209, 196, 92, 83, 121, 253, 226, 193, 137, 144, 102, 236, 42, 205, + 223, 21, 152, 15, 42, 132, 31, 35, 72, 25, 246, 138, 198, 231, 18, 202, + 141, 151, 37, 174, 155, 60, 107, 81, 239, 124, 76, 33, 245, 216, 71, 26, + 178, 139, 53, 167, 87, 58, 190, 147, 48, 109, 212, 45, 159, 93, 168, 57, + 190, 146, 240, 109, 132, 45, 163, 93, 185, 249, 178, 194, 245, 145, 135, 44, + 98, 157, 233, 169, 142, 254, 228, 64, 75, 112, 55, 100, 22, 171, 78, 255, + 116, 64, 39, 112, 26, 164, 11, 59, 71, 83, 114, 189, 229, 177, 139, 52, + 103, 87, 106, 190, 175, 48, 124, 20, 33, 207, 88, 84, 58, 191, 83, 48, + 61, 212, 17, 159, 76, 104, 53, 238, 151, 12, 110, 133, 236, 99, 13, 233, + 197, 142, 211, 36, 93, 219, 121, 155, 98, 235, 105, 143, 110, 228, 44, 75, + 93, 247, 121, 134, 162, 226, 249, 137, 130, 230, 225, 138, 200, 103, 22, 170, + 142, 255, 36, 64, 27, 112, 11, 100, 7, 107, 66, 175, 113, 188, 36, 113, + 219, 100, 91, 107, 123, 111, 99, 108, 41, 237, 222, 205, 152, 85, 170, 191, + 63, 48, 16, 20, 12, 15, 69, 196, 51, 19, 85, 205, 255, 21, 128, 15, + 32, 4, 24, 3, 74, 129, 247, 32, 70, 152, 50, 234, 149, 143, 47, 36, + 28, 27, 73, 203, 118, 215, 102, 222, 170, 216, 127, 26, 160, 11, 56, 7, + 82, 130, 189, 161, 177, 184, 116, 114, 167, 101, 186, 171, 51, 63, 85, 208, + 63, 28, 16, 9, 204, 6, 213, 194, 223, 17, 152, 12, 106, 133, 239, 35, + 12, 25, 197, 202, 211, 23, 29, 206, 137, 148, 102, 239, 106, 204, 47, 21, + 220, 15, 25, 196, 10, 211, 71, 29, 242, 137, 133, 166, 227, 58, 201, 211, + 22, 221, 206, 217, 148, 90, 239, 123, 12, 35, 69, 217, 243, 26, 197, 203, + 19, 23, 77, 206, 181, 148, 119, 47, 102, 156, 42, 233, 223, 14, 216, 4, + 90, 131, 123, 33, 227, 88, 73, 250, 182, 195, 54, 209, 214, 220, 94, 217, + 248, 90, 194, 187, 17, 179, 76, 117, 245, 231, 7, 10, 130, 135, 33, 162, + 152, 121, 170, 162, 255, 57, 128, 18, 224, 13, 136, 5, 166, 131, 58, 225, + 211, 8, 93, 198, 185, 146, 242, 237, 133, 141, 163, 37, 185, 219, 50, 219, + 85, 155, 127, 43, 96, 31, 104, 8, 46, 134, 156, 98, 233, 233, 142, 206, + 228, 84, 75, 127, 119, 96, 38, 168, 26, 254, 139, 0, 103, 64, 42, 176, + 31, 52, 8, 23, 70, 142, 178, 228, 117, 139, 103, 39, 106, 154, 175, 43, + 60, 31, 81, 200, 60, 86, 145, 254, 236, 64, 77, 240, 53, 132, 23, 35, + 78, 153, 244, 106, 199, 111, 18, 172, 13, 189, 197, 177, 147, 52, 109, 215, + 109, 158, 173, 168, 125, 190, 161, 176, 120, 116, 34, 167, 89, 186, 186, 243, + 51, 5, 213, 195, 31, 17, 200, 12, 86, 133, 254, 227, 0, 73, 192, 54, + 208, 22, 220, 14, 217, 196, 90, 211, 123, 29, 227, 73, 137, 246, 230, 198, + 202, 210, 215, 29, 158, 137, 168, 102, 254, 170, 192, 127, 16, 32, 12, 24, + 5, 202, 131, 23, 33, 206, 152, 84, 106, 191, 111, 48, 44, 20, 29, 207, + 73, 148, 54, 239, 86, 204, 62, 213, 208, 95, 28, 56, 9, 210, 134, 221, + 162, 217, 185, 154, 242, 235, 5, 143, 67, 36, 49, 219, 84, 91, 127, 123, + 96, 35, 104, 25, 238, 138, 204, 103, 21, 234, 143, 15, 36, 4, 27, 67, + 75, 113, 247, 100, 70, 171, 114, 255, 101, 128, 43, 32, 31, 88, 8, 58, + 134, 147, 34, 237, 217, 141, 154, 229, 171, 11, 63, 71, 80, 50, 188, 21, + 177, 207, 52, 84, 23, 127, 78, 160, 52, 120, 23, 98, 142, 169, 164, 126, + 251, 96, 67, 104, 49, 238, 148, 76, 111, 117, 236, 39, 13, 218, 133, 155, + 35, 43, 89, 223, 122, 216, 35, 26, 153, 203, 42, 215, 95, 30, 184, 8, + 114, 134, 165, 162, 251, 57, 131, 82, 225, 253, 136, 65, 166, 176, 122, 244, + 35, 7, 89, 194, 186, 209, 179, 28, 117, 201, 231, 22, 202, 142, 215, 36, + 94, 155, 120, 107, 98, 175, 105, 188, 46, 241, 220, 68, 89, 243, 122, 197, + 227, 19, 9, 205, 198, 213, 146, 223, 45, 152, 29, 170, 137, 191, 38, 240, + 26, 196, 11, 19, 71, 77, 242, 181, 133, 183, 35, 54, 153, 214, 234, 222, + 207, 24, 84, 10, 191, 71, 48, 50, 148, 21, 175, 79, 60, 52, 17, 215, + 76, 94, 181, 248, 119, 2, 166, 129, 186, 224, 115, 8, 37, 198, 155, 18, + 235, 77, 143, 117, 164, 39, 59, 90, 147, 123, 45, 227, 93, 137, 249, 166, + 194, 250, 209, 131, 28, 97, 201, 232, 86, 206, 190, 212, 112, 95, 100, 56, + 43, 82, 159, 125, 168, 33, 190, 152, 112, 106, 164, 47, 59, 92, 19, 121, + 205, 226, 213, 137, 159, 38, 232, 26, 206, 139, 20, 103, 79, 106, 180, 47, + 55, 92, 22, 185, 206, 242, 212, 69, 159, 115, 40, 37, 222, 155, 24, 107, + 74, 175, 119, 60, 38, 145, 218, 236, 91, 13, 251, 69, 131, 115, 33, 229, + 216, 75, 26, 183, 75, 54, 183, 86, 246, 190, 198, 240, 82, 196, 61, 147, + 81, 173, 252, 125, 129, 225, 160, 72, 120, 54, 162, 150, 249, 174, 194, 252, + 81, 129, 252, 96, 65, 232, 48, 78, 148, 52, 111, 87, 108, 62, 173, 208, + 125, 156, 33, 169, 216, 126, 218, 160, 91, 56, 59, 82, 147, 125, 173, 225, + 189, 136, 113, 166, 164, 122, 251, 99, 3, 105, 193, 238, 208, 76, 92, 53, + 249, 215, 2, 222, 129, 152, 96, 106, 168, 47, 62, 156, 16, 105, 204, 46, + 213, 220, 95, 25, 248, 10, 194, 135, 17, 162, 140, 121, 165, 226, 251, 9, + 131, 70, 225, 242, 200, 69, 150, 179, 46, 245, 220, 71, 25, 242, 138, 197, + 167, 19, 58, 141, 211, 37, 157, 219, 41, 155, 94, 235, 120, 79, 98, 180, + 41, 183, 94, 246, 184, 70, 242, 178, 197, 181, 147, 55, 45, 214, 157, 158, + 233, 168, 78, 254, 180, 64, 119, 112, 38, 164, 26, 251, 75, 3, 119, 65, + 230, 176, 74, 244, 55, 7, 86, 130, 190, 225, 176, 72, 116, 54, 167, 86, + 250, 190, 195, 48, 81, 212, 60, 95, 81, 248, 60, 66, 145, 241, 172, 68, + 125, 243, 97, 133, 232, 99, 14, 169, 196, 126, 211, 96, 93, 232, 57, 142, + 146, 228, 109, 139, 109, 167, 109, 186, 173, 179, 61, 181, 209, 183, 28, 118, + 137, 230, 230, 202, 202, 215, 23, 30, 142, 136, 100, 102, 171, 106, 255, 111, + 0, 44, 0, 29, 192, 9, 144, 6, 236, 2, 205, 193, 149, 144, 111, 44, + 44, 29, 221, 201, 153, 150, 234, 238, 207, 12, 84, 5, 255, 67, 0, 49, + 192, 20, 80, 15, 124, 4, 33, 195, 88, 81, 250, 188, 67, 49, 241, 212, + 68, 95, 115, 120, 37, 226, 155, 9, 171, 70, 255, 114, 192, 37, 144, 27, + 44, 11, 93, 199, 121, 146, 162, 237, 185, 141, 178, 229, 181, 139, 55, 39, + 86, 154, 190, 235, 48, 79, 84, 52, 63, 87, 80, 62, 188, 16, 113, 204, + 36, 85, 219, 127, 27, 96, 11, 104, 7, 110, 130, 172, 97, 189, 232, 113, + 142, 164, 100, 123, 107, 99, 111, 105, 236, 46, 205, 220, 85, 153, 255, 42, + 192, 31, 16, 8, 12, 6, 133, 194, 227, 17, 137, 204, 102, 213, 234, 223, + 15, 24, 4, 10, 131, 71, 33, 242, 152, 69, 170, 179, 63, 53, 208, 23, + 28, 14, 137, 196, 102, 211, 106, 221, 239, 25, 140, 10, 229, 199, 11, 18, + 135, 77, 162, 181, 185, 183, 50, 246, 149, 134, 239, 34, 204, 25, 149, 202, + 239, 23, 12, 14, 133, 196, 99, 19, 105, 205, 238, 213, 140, 95, 37, 248, + 27, 2, 139, 65, 167, 112, 122, 164, 35, 59, 89, 211, 122, 221, 227, 25, + 137, 202, 230, 215, 10, 222, 135, 24, 98, 138, 169, 167, 62, 250, 144, 67, + 44, 49, 221, 212, 89, 159, 122, 232, 35, 14, 153, 196, 106, 211, 111, 29, + 236, 9, 141, 198, 229, 146, 203, 45, 151, 93, 174, 185, 188, 114, 241, 229, + 132, 75, 35, 119, 89, 230, 186, 202, 243, 23, 5, 206, 131, 20, 97, 207, + 104, 84, 46, 191, 92, 112, 57, 228, 18, 203, 77, 151, 117, 174, 167, 60, + 122, 145, 227, 44, 73, 221, 246, 217, 134, 218, 226, 219, 9, 155, 70, 235, + 114, 207, 101, 148, 43, 47, 95, 92, 56, 57, 210, 146, 221, 173, 153, 189, + 170, 241, 191, 4, 112, 3, 100, 1, 235, 64, 79, 112, 52, 36, 23, 91, + 78, 187, 116, 115, 103, 101, 234, 171, 15, 63, 68, 16, 51, 76, 21, 245, + 207, 7, 20, 2, 143, 65, 164, 48, 123, 84, 35, 127, 89, 224, 58, 200, + 19, 22, 141, 206, 229, 148, 75, 47, 119, 92, 38, 185, 218, 242, 219, 5, + 155, 67, 43, 113, 223, 100, 88, 43, 122, 159, 99, 40, 41, 222, 158, 216, + 104, 90, 174, 187, 60, 115, 81, 229, 252, 75, 1, 247, 64, 70, 176, 50, + 244, 21, 135, 79, 34, 180, 25, 183, 74, 246, 183, 6, 246, 130, 198, 225, + 146, 200, 109, 150, 173, 174, 253, 188, 65, 177, 240, 116, 68, 39, 115, 90, + 165, 251, 59, 3, 83, 65, 253, 240, 65, 132, 48, 99, 84, 41, 255, 94, + 192, 56, 80, 18, 188, 13, 177, 197, 180, 83, 55, 125, 214, 161, 158, 248, + 104, 66, 174, 177, 188, 116, 113, 231, 100, 74, 171, 119, 63, 102, 144, 42, + 236, 31, 13, 200, 5, 150, 131, 46, 225, 220, 72, 89, 246, 186, 198, 243, + 18, 197, 205, 147, 21, 173, 207, 61, 148, 17, 175, 76, 124, 53, 225, 215, + 8, 94, 134, 184, 98, 242, 169, 133, 190, 227, 48, 73, 212, 54, 223, 86, + 216, 62, 218, 144, 91, 44, 59, 93, 211, 121, 157, 226, 233, 137, 142, 230, + 228, 74, 203, 119, 23, 102, 142, 170, 228, 127, 11, 96, 7, 104, 2, 174, + 129, 188, 96, 113, 232, 36, 78, 155, 116, 107, 103, 111, 106, 172, 47, 61, + 220, 17, 153, 204, 106, 213, 239, 31, 12, 8, 5, 198, 131, 18, 225, 205, + 136, 85, 166, 191, 58, 240, 19, 4, 13, 195, 69, 145, 243, 44, 69, 221, + 243, 25, 133, 202, 227, 23, 9, 206, 134, 212, 98, 223, 105, 152, 46, 234, + 156, 79, 41, 244, 30, 199, 72, 82, 182, 189, 182, 241, 182, 196, 118, 211, + 102, 221, 234, 217, 143, 26, 228, 11, 11, 71, 71, 114, 178, 165, 181, 187, + 55, 51, 86, 149, 254, 239, 0, 76, 0, 53, 192, 23, 16, 14, 140, 4, + 101, 195, 107, 17, 239, 76, 76, 53, 245, 215, 7, 30, 130, 136, 97, 166, + 168, 122, 254, 163, 0, 121, 192, 34, 208, 25, 156, 10, 233, 199, 14, 210, + 132, 93, 163, 121, 185, 226, 242, 201, 133, 150, 227, 46, 201, 220, 86, 217, + 254, 218, 192, 91, 16, 59, 76, 19, 117, 205, 231, 21, 138, 143, 39, 36, + 26, 155, 75, 43, 119, 95, 102, 184, 42, 242, 159, 5, 168, 3, 62, 129, + 208, 96, 92, 40, 57, 222, 146, 216, 109, 154, 173, 171, 61, 191, 81, 176, + 60, 116, 17, 231, 76, 74, 181, 247, 55, 6, 150, 130, 238, 225, 140, 72, + 101, 246, 171, 6, 255, 66, 192, 49, 144, 20, 108, 15, 109, 196, 45, 147, + 93, 173, 249, 189, 130, 241, 161, 132, 120, 99, 98, 169, 233, 190, 206, 240, + 84, 68, 63, 115, 80, 37, 252, 27, 1, 203, 64, 87, 112, 62, 164, 16, + 123, 76, 35, 117, 217, 231, 26, 202, 139, 23, 39, 78, 154, 180, 107, 55, + 111, 86, 172, 62, 253, 208, 65, 156, 48, 105, 212, 46, 223, 92, 88, 57, + 250, 146, 195, 45, 145, 221, 172, 89, 189, 250, 241, 131, 4, 97, 195, 104, + 81, 238, 188, 76, 113, 245, 228, 71, 11, 114, 135, 101, 162, 171, 57, 191, + 82, 240, 61, 132, 17, 163, 76, 121, 245, 226, 199, 9, 146, 134, 237, 162, + 205, 185, 149, 178, 239, 53, 140, 23, 37, 206, 155, 20, 107, 79, 111, 116, + 44, 39, 93, 218, 185, 155, 50, 235, 85, 143, 127, 36, 32, 27, 88, 11, + 122, 135, 99, 34, 169, 217, 190, 218, 240, 91, 4, 59, 67, 83, 113, 253, + 228, 65, 139, 112, 103, 100, 42, 171, 95, 63, 120, 16, 34, 140, 25, 165, + 202, 251, 23, 3, 78, 129, 244, 96, 71, 104, 50, 174, 149, 188, 111, 49, + 236, 20, 77, 207, 117, 148, 39, 47, 90, 156, 59, 41, 211, 94, 221, 248, + 89, 130, 186, 225, 179, 8, 117, 198, 167, 18, 250, 141, 131, 37, 161, 219, + 56, 91, 82, 187, 125, 179, 97, 181, 232, 119, 14, 166, 132, 122, 227, 99, + 9, 233, 198, 206, 210, 212, 93, 159, 121, 168, 34, 254, 153, 128, 106, 224, + 47, 8, 28, 6, 137, 194, 230, 209, 138, 220, 103, 25, 234, 138, 207, 39, + 20, 26, 143, 75, 36, 55, 91, 86, 187, 126, 243, 96, 69, 232, 51, 14, + 149, 196, 111, 19, 108, 13, 237, 197, 141, 147, 37, 173, 219, 61, 155, 81, + 171, 124, 127, 97, 224, 40, 72, 30, 182, 136, 118, 230, 166, 202, 250, 215, + 3, 30, 129, 200, 96, 86, 168, 62, 254, 144, 64, 108, 48, 45, 212, 29, + 159, 73, 168, 54, 254, 150, 192, 110, 208, 44, 92, 29, 249, 201, 130, 214, + 225, 158, 200, 104, 86, 174, 190, 252, 112, 65, 228, 48, 75, 84, 55, 127, + 86, 160, 62, 248, 16, 66, 140, 49, 165, 212, 123, 31, 99, 72, 41, 246, + 158, 198, 232, 82, 206, 189, 148, 113, 175, 100, 124, 43, 97, 223, 104, 88, + 46, 186, 156, 115, 41, 229, 222, 203, 24, 87, 74, 190, 183, 48, 118, 148, + 38, 239, 90, 204, 59, 21, 211, 79, 29, 244, 9, 135, 70, 226, 178, 201, + 181, 150, 247, 46, 198, 156, 82, 233, 253, 142, 193, 164, 80, 123, 124, 35, + 97, 217, 232, 90, 206, 187, 20, 115, 79, 101, 244, 43, 7, 95, 66, 184, + 49, 178, 148, 117, 175, 103, 60, 42, 145, 223, 44, 88, 29, 250, 137, 131, + 38, 225, 218, 200, 91, 22, 187, 78, 243, 116, 69, 231, 115, 10, 165, 199, + 59, 18, 147, 77, 173, 245, 189, 135, 49, 162, 148, 121, 175, 98, 252, 41, + 129, 222, 224, 88, 72, 58, 182, 147, 54, 237, 214, 205, 158, 213, 168, 95, + 62, 184, 16, 114, 140, 37, 165, 219, 59, 27, 83, 75, 125, 247, 97, 134, + 168, 98, 254, 169, 128, 126, 224, 32, 72, 24, 54, 138, 150, 231, 46, 202, + 156, 87, 41, 254, 158, 192, 104, 80, 46, 188, 28, 113, 201, 228, 86, 203, + 126, 215, 96, 94, 168, 56, 126, 146, 160, 109, 184, 45, 178, 157, 181, 169, + 183, 62, 246, 144, 70, 236, 50, 205, 213, 149, 159, 47, 40, 28, 30, 137, + 200, 102, 214, 170, 222, 255, 24, 64, 10, 176, 7, 52, 2, 151, 65, 174, + 176, 124, 116, 33, 231, 88, 74, 186, 183, 51, 54, 149, 214, 239, 30, 204, + 8, 85, 198, 191, 18, 240, 13, 132, 5, 163, 67, 57, 241, 210, 196, 93, + 147, 121, 173, 226, 253, 137, 129, 166, 224, 122, 200, 35, 22, 153, 206, 234, + 212, 79, 31, 116, 8, 39, 70, 154, 178, 235, 53, 143, 87, 36, 62, 155, + 80, 107, 124, 47, 97, 220, 40, 89, 222, 186, 216, 115, 26, 165, 203, 59, + 23, 83, 78, 189, 244, 113, 135, 100, 98, 171, 105, 191, 110, 240, 44, 68, + 29, 243, 73, 133, 246, 227, 6, 201, 194, 214, 209, 158, 220, 104, 89, 238, + 186, 204, 115, 21, 229, 207, 11, 20, 7, 79, 66, 180, 49, 183, 84, 118, + 191, 102, 240, 42, 196, 31, 19, 72, 13, 246, 133, 134, 227, 34, 201, 217, + 150, 218, 238, 219, 12, 91, 69, 251, 115, 3, 101, 193, 235, 16, 79, 76, + 52, 53, 215, 87, 30, 190, 136, 112, 102, 164, 42, 251, 95, 3, 120, 1, + 226, 128, 73, 160, 54, 248, 22, 194, 142, 209, 164, 92, 123, 121, 227, 98, + 201, 233, 150, 206, 238, 212, 76, 95, 117, 248, 39, 2, 154, 129, 171, 32, + 127, 88, 32, 58, 152, 19, 42, 141, 223, 37, 152, 27, 42, 139, 95, 39, + 120, 26, 162, 139, 57, 167, 82, 250, 189, 131, 49, 161, 212, 120, 95, 98, + 184, 41, 178, 158, 245, 168, 71, 62, 178, 144, 117, 172, 39, 61, 218, 145, + 155, 44, 107, 93, 239, 121, 140, 34, 229, 217, 139, 26, 231, 75, 10, 183, + 71, 54, 178, 150, 245, 174, 199, 60, 82, 145, 253, 172, 65, 189, 240, 113, + 132, 36, 99, 91, 105, 251, 110, 195, 108, 81, 237, 252, 77, 129, 245, 160, + 71, 56, 50, 146, 149, 173, 175, 61, 188, 17, 177, 204, 116, 85, 231, 127, + 10, 160, 7, 56, 2, 146, 129, 173, 160, 125, 184, 33, 178, 152, 117, 170, + 167, 63, 58, 144, 19, 44, 13, 221, 197, 153, 147, 42, 237, 223, 13, 152, + 5, 170, 131, 63, 33, 208, 24, 92, 10, 185, 199, 50, 210, 149, 157, 175, + 41, 188, 30, 241, 200, 68, 86, 179, 126, 245, 224, 71, 8, 50, 134, 149, + 162, 239, 57, 140, 18, 229, 205, 139, 21, 167, 79, 58, 180, 19, 55, 77, + 214, 181, 158, 247, 40, 70, 158, 178, 232, 117, 142, 167, 36, 122, 155, 99, + 43, 105, 223, 110, 216, 44, 90, 157, 251, 41, 131, 94, 225, 248, 72, 66, + 182, 177, 182, 244, 118, 199, 102, 210, 170, 221, 191, 25, 176, 10, 244, 7, + 7, 66, 130, 177, 161, 180, 120, 119, 98, 166, 169, 186, 254, 243, 0, 69, + 192, 51, 16, 21, 204, 15, 21, 196, 15, 19, 68, 13, 243, 69, 133, 243, + 35, 5, 217, 195, 26, 209, 203, 28, 87, 73, 254, 182, 192, 118, 208, 38, + 220, 26, 217, 203, 26, 215, 75, 30, 183, 72, 118, 182, 166, 246, 250, 198, + 195, 18, 209, 205, 156, 85, 169, 255, 62, 192, 16, 80, 12, 60, 5, 209, + 195, 28, 81, 201, 252, 86, 193, 254, 208, 64, 92, 48, 57, 212, 18, 223, + 77, 152, 53, 170, 151, 63, 46, 144, 28, 108, 9, 237, 198, 205, 146, 213, + 173, 159, 61, 168, 17, 190, 140, 112, 101, 228, 43, 11, 95, 71, 120, 50, + 162, 149, 185, 175, 50, 252, 21, 129, 207, 32, 84, 24, 63, 74, 144, 55, + 44, 22, 157, 206, 233, 148, 78, 239, 116, 76, 39, 117, 218, 167, 27, 58, + 139, 83, 39, 125, 218, 161, 155, 56, 107, 82, 175, 125, 188, 33, 177, 216, + 116, 90, 167, 123, 58, 163, 83, 57, 253, 210, 193, 157, 144, 105, 172, 46, + 253, 220, 65, 153, 240, 106, 196, 47, 19, 92, 13, 249, 197, 130, 211, 33, + 157, 216, 105, 154, 174, 235, 60, 79, 81, 244, 60, 71, 81, 242, 188, 69, + 177, 243, 52, 69, 215, 115, 30, 165, 200, 123, 22, 163, 78, 249, 244, 66, + 199, 113, 146, 164, 109, 187, 109, 179, 109, 181, 237, 183, 13, 182, 133, 182, + 227, 54, 201, 214, 214, 222, 222, 216, 88, 90, 186, 187, 51, 51, 255, 63 ) + +random_mask_vec8 = Numeric.array(random_mask_tuple, Numeric.UnsignedInt8) + diff --git a/gnuradio-core/src/python/gnuradio/window.py b/gnuradio-core/src/python/gnuradio/window.py new file mode 100644 index 000000000..391b83c37 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/window.py @@ -0,0 +1,190 @@ +# +# Copyright 2004,2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +''' +Routines for designing window functions. +''' + +import math +from gnuradio import gr + +def izero(x): + izeroepsilon = 1e-21 + halfx = x/2.0 + accum = u = n = 1 + while 1: + temp = halfx/n + n += 1 + temp *= temp + u *= temp + accum += u + if u >= IzeroEPSILON*sum: + break + return accum + +def midm1(fft_size): + return (fft_size - 1)/2 + +def midp1(fft_size): + return (fft_size+1)/2 + +def freq(fft_size): + return 2.0*math.pi/fft_size + +def rate(fft_size): + return 1.0/(fft_size >> 1) + +def expn(fft_size): + math.log(2.0)/(midn(fft_size) + 1.0) + +def hamming(fft_size): + window = [] + for index in xrange(fft_size): + window.append(0.54 - 0.46 * math.cos (2 * math.pi / fft_size * index)) # Hamming window + return window + +def hanning(fft_size): + window = [] + for index in xrange(fft_size): + window.append(0.5 - 0.5 * math.cos (2 * math.pi / fft_size * index)) # von Hann window + return window + +def welch(fft_size): + window = [0 for i in range(fft_size)] + j = fft_size-1 + for index in xrange(midn(fft_size)+1): + window[j] = window[index] = (1.0 - math.sqrt((index - midm1(fft_size)) / midp1(fft_size))) + j -= 1 + return window + +def parzen(fft_size): + window = [0 for i in range(fft_size)] + j = fft_size-1 + for index in xrange(midn(fft_size)+1): + window[j] = window[index] = (1.0 - math.abs((index - midm1(fft_size)) / midp1(fft_size))) + j -= 1 + return window + +def bartlett(fft_size): + mfrq = freq(fft_size) + angle = 0 + window = [0 for i in range(fft_size)] + j = fft_size-1 + for index in xrange(midn(fft_size)+1): + window[j] = window[index] = angle + angle += freq + j -= 1 + return window + +def blackman2(fft_size): + mfrq = freq(fft_size) + angle = 0 + window = [0 for i in range(fft_size)] + j = fft_size-1 + for index in xrange(midn(fft_size)+1): + cx = math.cos(angle) + window[j] = window[index] = (.34401 + (cx * (-.49755 + (cx * .15844)))) + angle += freq + j -= 1 + return window + +def blackman3(fft_size): + mfrq = freq(fft_size) + angle = 0 + window = [0 for i in range(fft_size)] + j = fft_size-1 + for index in xrange(midn(fft_size)+1): + cx = math.cos(angle) + window[j] = window[index] = (.21747 + (cx * (-.45325 + (cx * (.28256 - (cx * .04672)))))) + angle += freq + j -= 1 + return window + +def blackman4(fft_size): + mfrq = freq(fft_size) + angle = 0 + window = [0 for i in range(fft_size)] + j = fft_size-1 + for index in xrange(midn(fft_size)+1): + cx = math.cos(angle) + window[j] = window[index] = (.084037 + (cx * (-.29145 + (cx * (.375696 + (cx * (-.20762 + (cx * .041194)))))))) + angle += freq + j -= 1 + return window + +def exponential(fft_size): + expsum = 1.0 + window = [0 for i in range(fft_size)] + j = fft_size-1 + for index in xrange(midn(fft_size)+1): + window[j] = window[i] = (expsum - 1.0) + expsum *= expn(fft_size) + j -= 1 + return window + +def riemann(fft_size): + sr1 = freq(fft_size) + window = [0 for i in range(fft_size)] + j = fft_size-1 + for index in xrange(midn(fft_size)): + if index == midn(fft_size): + window[index] = window[j] = 1.0 + else: + cx = sr1*midn(fft_size) - index + window[index] = window[j] = math.sin(cx)/cx + j -= 1 + return window + +def blackmanharris(fft_size): + a0 = 0.35875 + a1 = 0.48829 + a2 = 0.14128 + a3 = 0.01168 + window = [0 for i in range(fft_size)] + for index in xrange(fft_size): + window[index] = a0 + window[index] -= a1*math.cos(2.0*math.pi*(index+0.5)/(fft_size - 1)) + window[index] += a2*math.cos(4.0*math.pi*(index+0.5)/(fft_size - 1)) + window[index] -= a3*math.cos(6.0*math.pi*(index+0.5)/(fft_size - 1)) + return window + +def nuttall(fft_size): + a0 = 0.3635819 + a1 = 0.4891775 + a2 = 0.1365995 + a3 = 0.0106411 + window = [0 for i in range(fft_size)] + for index in xrange(fft_size): + window[index] = a0 + window[index] -= a1*math.cos(2.0*math.pi*(index+0.5)/(fft_size - 1)) + window[index] += a2*math.cos(4.0*math.pi*(index+0.5)/(fft_size - 1)) + window[index] -= a3*math.cos(6.0*math.pi*(index+0.5)/(fft_size - 1)) + return window + +def kaiser(fft_size,beta): + ibeta = 1.0/izero(beta) + inm1 = 1.0/(fft_size) + window = [0 for i in range(fft_size)] + for index in xrange(fft_size): + window[index] = izero(beta*math.sqrt(1.0 - (index * inm1)*(index * inm1))) * ibeta + return window + + |