summaryrefslogtreecommitdiff
path: root/gnuradio-core/src/python/gnuradio/blks2impl
diff options
context:
space:
mode:
Diffstat (limited to 'gnuradio-core/src/python/gnuradio/blks2impl')
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am65
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/__init__.py1
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/am_demod.py76
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/channel_model.py59
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/cpm.py249
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/d8psk.py363
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/dbpsk.py363
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/digital_voice.py.real102
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/dqpsk.py363
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/filterbank.py168
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/fm_demod.py111
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/fm_emph.py151
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/gmsk.py292
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/nbfm_rx.py88
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/nbfm_tx.py92
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/ofdm.py305
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/ofdm_receiver.py64
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_fixed.py41
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_ml.py135
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_pn.py135
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_pnac.py126
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/pkt.py164
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/psk.py94
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/qam.py113
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/qam16.py208
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/qam256.py209
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/qam64.py208
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/qam8.py209
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/rational_resampler.py131
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/standard_squelch.py76
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/wfm_rcv.py69
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/wfm_rcv_pll.py192
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/wfm_tx.py79
33 files changed, 5101 insertions, 0 deletions
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am b/gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am
new file mode 100644
index 0000000000..3fb23caef3
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am
@@ -0,0 +1,65 @@
+#
+# Copyright 2005,2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+include $(top_srcdir)/Makefile.common
+
+# EXTRA_DIST = run_tests.in
+# TESTS = run_tests
+
+grblkspythondir = $(grpythondir)/blks2impl
+
+grblkspython_PYTHON = \
+ __init__.py \
+ am_demod.py \
+ channel_model.py \
+ dbpsk.py \
+ dqpsk.py \
+ d8psk.py \
+ filterbank.py \
+ fm_demod.py \
+ fm_emph.py \
+ gmsk.py \
+ cpm.py \
+ nbfm_rx.py \
+ nbfm_tx.py \
+ ofdm.py \
+ ofdm_receiver.py \
+ ofdm_sync_fixed.py \
+ ofdm_sync_ml.py \
+ ofdm_sync_pnac.py \
+ ofdm_sync_pn.py \
+ pkt.py \
+ psk.py \
+ qam.py \
+ qam8.py \
+ qam16.py \
+ qam64.py \
+ qam256.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/blks2impl/__init__.py b/gnuradio-core/src/python/gnuradio/blks2impl/__init__.py
new file mode 100644
index 0000000000..a4917cf64c
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/__init__.py
@@ -0,0 +1 @@
+# make this a package
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/am_demod.py b/gnuradio-core/src/python/gnuradio/blks2impl/am_demod.py
new file mode 100644
index 0000000000..b454f0942d
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/am_demod.py
@@ -0,0 +1,76 @@
+#
+# Copyright 2006,2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, optfir
+
+class am_demod_cf(gr.hier_block2):
+ """
+ Generalized AM demodulation block with audio filtering.
+
+ This block demodulates a band-limited, complex down-converted AM
+ channel into the the original baseband signal, applying low pass
+ filtering to the audio output. It produces a float stream in the
+ range [-1.0, +1.0].
+
+ @param channel_rate: incoming sample rate of the AM baseband
+ @type sample_rate: integer
+ @param audio_decim: input to output decimation rate
+ @type audio_decim: integer
+ @param audio_pass: audio low pass filter passband frequency
+ @type audio_pass: float
+ @param audio_stop: audio low pass filter stop frequency
+ @type audio_stop: float
+ """
+ def __init__(self, channel_rate, audio_decim, audio_pass, audio_stop):
+ gr.hier_block2.__init__(self, "am_demod_cf",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Input signature
+
+ MAG = gr.complex_to_mag()
+ DCR = gr.add_const_ff(-1.0)
+
+ audio_taps = optfir.low_pass(0.5, # Filter gain
+ channel_rate, # Sample rate
+ audio_pass, # Audio passband
+ audio_stop, # Audio stopband
+ 0.1, # Passband ripple
+ 60) # Stopband attenuation
+ LPF = gr.fir_filter_fff(audio_decim, audio_taps)
+
+ self.connect(self, MAG, DCR, LPF, self)
+
+class demod_10k0a3e_cf(am_demod_cf):
+ """
+ AM demodulation block, 10 KHz channel.
+
+ This block demodulates an AM channel conformant to 10K0A3E emission
+ standards, such as broadcast band AM transmissions.
+
+ @param channel_rate: incoming sample rate of the AM baseband
+ @type sample_rate: integer
+ @param audio_decim: input to output decimation rate
+ @type audio_decim: integer
+ """
+ def __init__(self, channel_rate, audio_decim):
+ am_demod_cf.__init__(self, channel_rate, audio_decim,
+ 5000, # Audio passband
+ 5500) # Audio stopband
+ \ No newline at end of file
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/channel_model.py b/gnuradio-core/src/python/gnuradio/blks2impl/channel_model.py
new file mode 100644
index 0000000000..21980a22e5
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/channel_model.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr
+
+class channel_model(gr.hier_block):
+ def __init__(self, fg, noise_voltage=0.0, frequency_offset=0.0, epsilon=1.0, taps=[1.0,0.0]):
+ ''' Creates a channel model that includes:
+ - AWGN noise power in terms of noise voltage
+ - A frequency offest in the channel in ratio
+ - A timing offset ratio to model clock difference (epsilon)
+ - Multipath taps
+ '''
+
+ print epsilon
+ self.timing_offset = gr.fractional_interpolator_cc(0, epsilon)
+
+ self.multipath = gr.fir_filter_ccc(1, taps)
+
+ self.noise_adder = gr.add_cc()
+ self.noise = gr.noise_source_c(gr.GR_GAUSSIAN,noise_voltage)
+ self.freq_offset = gr.sig_source_c(1, gr.GR_SIN_WAVE, frequency_offset, 1.0, 0.0)
+ self.mixer_offset = gr.multiply_cc()
+
+ fg.connect(self.timing_offset, self.multipath)
+ fg.connect(self.multipath, (self.mixer_offset,0))
+ fg.connect(self.freq_offset,(self.mixer_offset,1))
+ fg.connect(self.mixer_offset, (self.noise_adder,1))
+ fg.connect(self.noise, (self.noise_adder,0))
+
+ gr.hier_block.__init__(self, fg, self.timing_offset, self.noise_adder)
+
+ def set_noise_voltage(noise_voltage):
+ self.noise.set_amplitude(noise_voltage)
+
+ def set_frequency_offset(frequency_offset):
+ self.freq_offset.set_frequency(frequency_offset)
+
+ def set_taps(taps):
+ self.multipath.set_taps(taps)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/cpm.py b/gnuradio-core/src/python/gnuradio/blks2impl/cpm.py
new file mode 100644
index 0000000000..8f593cd51e
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/cpm.py
@@ -0,0 +1,249 @@
+#
+# CPM modulation and demodulation.
+#
+#
+# Copyright 2005,2006,2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+# See gnuradio-examples/python/digital for examples
+
+from gnuradio import gr
+from gnuradio import modulation_utils
+from math import pi
+import numpy
+from pprint import pprint
+import inspect
+
+# default values (used in __init__ and add_options)
+_def_samples_per_symbol = 2
+_def_bits_per_symbol = 1
+_def_h_numerator = 1
+_def_h_denominator = 2
+_def_cpm_type = 0 # 0=CPFSK, 1=GMSK, 2=RC, 3=GENERAL
+_def_bt = 0.35
+_def_symbols_per_pulse = 1
+_def_generic_taps = numpy.empty(1)
+_def_verbose = False
+_def_log = False
+
+
+# /////////////////////////////////////////////////////////////////////////////
+# CPM modulator
+# /////////////////////////////////////////////////////////////////////////////
+
+class cpm_mod(gr.hier_block2):
+ def __init__(self,
+ samples_per_symbol=_def_samples_per_symbol,
+ bits_per_symbol=_def_bits_per_symbol,
+ h_numerator=_def_h_numerator,
+ h_denominator=_def_h_denominator,
+ cpm_type=_def_cpm_type,
+ bt=_def_bt,
+ symbols_per_pulse=_def_symbols_per_pulse,
+ generic_taps=_def_generic_taps,
+ verbose=_def_verbose,
+ log=_def_log):
+ """
+ Hierarchical block for Continuous Phase
+ modulation.
+
+ The input is a byte stream (unsigned char)
+ representing packed bits and the
+ output is the complex modulated signal at baseband.
+
+ See Proakis for definition of generic CPM signals:
+ s(t)=exp(j phi(t))
+ phi(t)= 2 pi h int_0^t f(t') dt'
+ f(t)=sum_k a_k g(t-kT)
+ (normalizing assumption: int_0^infty g(t) dt = 1/2)
+
+ @param samples_per_symbol: samples per baud >= 2
+ @type samples_per_symbol: integer
+ @param bits_per_symbol: bits per symbol
+ @type bits_per_symbol: integer
+ @param h_numerator: numerator of modulation index
+ @type h_numerator: integer
+ @param h_denominator: denominator of modulation index (numerator and denominator must be relative primes)
+ @type h_denominator: integer
+ @param cpm_type: supported types are: 0=CPFSK, 1=GMSK, 2=RC, 3=GENERAL
+ @type cpm_type: integer
+ @param bt: bandwidth symbol time product for GMSK
+ @type bt: float
+ @param symbols_per_pulse: shaping pulse duration in symbols
+ @type symbols_per_pulse: integer
+ @param generic_taps: define a generic CPM pulse shape (sum = samples_per_symbol/2)
+ @type generic_taps: array of floats
+
+ @param verbose: Print information about modulator?
+ @type verbose: bool
+ @param debug: Print modulation data to files?
+ @type debug: bool
+ """
+
+ gr.hier_block2.__init__("cpm_mod",
+ gr.io_signature(1, 1, gr.sizeof_char), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature
+
+ self._samples_per_symbol = samples_per_symbol
+ self._bits_per_symbol = bits_per_symbol
+ self._h_numerator = h_numerator
+ self._h_denominator = h_denominator
+ self._cpm_type = cpm_type
+ self._bt=bt
+ if cpm_type == 0 or cpm_type == 2 or cpm_type == 3: # CPFSK, RC, Generic
+ self._symbols_per_pulse = symbols_per_pulse
+ elif cpm_type == 1: # GMSK
+ self._symbols_per_pulse = 4
+ else:
+ raise TypeError, ("cpm_type must be an integer in {0,1,2,3}, is %r" % (cpm_type,))
+
+ self._generic_taps=numpy.array(generic_taps)
+
+ if not isinstance(samples_per_symbol, int) or samples_per_symbol < 2:
+ raise TypeError, ("samples_per_symbol must be an integer >= 2, is %r" % (samples_per_symbol,))
+
+ self.nsymbols = 2**bits_per_symbol
+ self.sym_alphabet=numpy.arange(-(self.nsymbols-1),self.nsymbols,2)
+
+
+ self.ntaps = self._symbols_per_pulse * samples_per_symbol
+ sensitivity = 2 * pi * h_numerator / h_denominator / samples_per_symbol
+
+ # Unpack Bytes into bits_per_symbol groups
+ self.B2s = gr.packed_to_unpacked_bb(bits_per_symbol,gr.GR_MSB_FIRST)
+
+
+ # Turn it into symmetric PAM data.
+ self.pam = gr.chunks_to_symbols_bf(self.sym_alphabet,1)
+
+ # Generate pulse (sum of taps = samples_per_symbol/2)
+ if cpm_type == 0: # CPFSK
+ self.taps= (1.0/self._symbols_per_pulse/2,) * self.ntaps
+ elif cpm_type == 1: # GMSK
+ gaussian_taps = gr.firdes.gaussian(
+ 1.0/2, # gain
+ samples_per_symbol, # symbol_rate
+ bt, # bandwidth * symbol time
+ self.ntaps # number of taps
+ )
+ sqwave = (1,) * samples_per_symbol # rectangular window
+ self.taps = numpy.convolve(numpy.array(gaussian_taps),numpy.array(sqwave))
+ elif cpm_type == 2: # Raised Cosine
+ # generalize it for arbitrary roll-off factor
+ self.taps = (1-numpy.cos(2*pi*numpy.arange(0,self.ntaps)/samples_per_symbol/self._symbols_per_pulse))/(2*self._symbols_per_pulse)
+ elif cpm_type == 3: # Generic CPM
+ self.taps = generic_taps
+ else:
+ raise TypeError, ("cpm_type must be an integer in {0,1,2,3}, is %r" % (cpm_type,))
+
+ self.filter = gr.interp_fir_filter_fff(samples_per_symbol, self.taps)
+
+ # FM modulation
+ self.fmmod = gr.frequency_modulator_fc(sensitivity)
+
+ if verbose:
+ self._print_verbage()
+
+ if log:
+ self._setup_logging()
+
+ # Connect
+ self.connect(self, self.B2s, self.pam, self.filter, self.fmmod, self)
+
+ #def samples_per_symbol(self):
+ #return self._samples_per_symbol
+
+ #def bits_per_symbol(self):
+ #return self._bits_per_symbol
+
+ #def h_numerator(self):
+ #return self._h_numerator
+
+ #def h_denominator(self):
+ #return self._h_denominator
+
+ #def cpm_type(self):
+ #return self._cpm_type
+
+ #def bt(self):
+ #return self._bt
+
+ #def symbols_per_pulse(self):
+ #return self._symbols_per_pulse
+
+
+ def _print_verbage(self):
+ print "Samples per symbol = %d" % self._samples_per_symbol
+ print "Bits per symbol = %d" % self._bits_per_symbol
+ print "h = " , self._h_numerator , " / " , self._h_denominator
+ print "Symbol alphabet = " , self.sym_alphabet
+ print "Symbols per pulse = %d" % self._symbols_per_pulse
+ print "taps = " , self.taps
+
+ print "CPM type = %d" % self._cpm_type
+ if self._cpm_type == 1:
+ print "Gaussian filter BT = %.2f" % self._bt
+
+
+ def _setup_logging(self):
+ print "Modulation logging turned on."
+ self.connect(self.B2s,
+ gr.file_sink(gr.sizeof_float, "symbols.dat"))
+ self.connect(self.pam,
+ gr.file_sink(gr.sizeof_float, "pam.dat"))
+ self.connect(self.filter,
+ gr.file_sink(gr.sizeof_float, "filter.dat"))
+ self.connect(self.fmmod,
+ gr.file_sink(gr.sizeof_gr_complex, "fmmod.dat"))
+
+
+ def add_options(parser):
+ """
+ Adds CPM modulation-specific options to the standard parser
+ """
+ parser.add_option("", "--bt", type="float", default=_def_bt,
+ help="set bandwidth-time product [default=%default] (GMSK)")
+ add_options=staticmethod(add_options)
+
+
+ def extract_kwargs_from_options(options):
+ """
+ Given command line options, create dictionary suitable for passing to __init__
+ """
+ return modulation_utils.extract_kwargs_from_options(cpm_mod.__init__,
+ ('self',), options)
+ extract_kwargs_from_options=staticmethod(extract_kwargs_from_options)
+
+
+
+# /////////////////////////////////////////////////////////////////////////////
+# CPM demodulator
+# /////////////////////////////////////////////////////////////////////////////
+#
+# Not yet implemented
+#
+
+
+
+#
+# Add these to the mod/demod registry
+#
+modulation_utils.add_type_1_mod('cpm', cpm_mod)
+#modulation_utils.add_type_1_demod('cpm', cpm_demod)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/d8psk.py b/gnuradio-core/src/python/gnuradio/blks2impl/d8psk.py
new file mode 100644
index 0000000000..67cf9f5696
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/d8psk.py
@@ -0,0 +1,363 @@
+#
+# Copyright 2005,2006,2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+# See gnuradio-examples/python/digital for examples
+
+"""
+differential 8PSK modulation and demodulation.
+"""
+
+from gnuradio import gr, gru, modulation_utils
+from math import pi, sqrt
+import psk
+import cmath
+from pprint import pprint
+
+# default values (used in __init__ and add_options)
+_def_samples_per_symbol = 3
+_def_excess_bw = 0.35
+_def_gray_code = True
+_def_verbose = False
+_def_log = False
+
+_def_costas_alpha = 0.175
+_def_gain_mu = 0.175
+_def_mu = 0.5
+_def_omega_relative_limit = 0.005
+
+
+# /////////////////////////////////////////////////////////////////////////////
+# D8PSK modulator
+# /////////////////////////////////////////////////////////////////////////////
+
+class d8psk_mod(gr.hier_block2):
+
+ def __init__(self,
+ samples_per_symbol=_def_samples_per_symbol,
+ excess_bw=_def_excess_bw,
+ gray_code=_def_gray_code,
+ verbose=_def_verbose,
+ log=_def_log):
+ """
+ Hierarchical block for RRC-filtered QPSK modulation.
+
+ The input is a byte stream (unsigned char) and the
+ output is the complex modulated signal at baseband.
+
+ @param samples_per_symbol: samples per symbol >= 2
+ @type samples_per_symbol: integer
+ @param excess_bw: Root-raised cosine filter excess bandwidth
+ @type excess_bw: float
+ @param gray_code: Tell modulator to Gray code the bits
+ @type gray_code: bool
+ @param verbose: Print information about modulator?
+ @type verbose: bool
+ @param debug: Print modualtion data to files?
+ @type debug: bool
+ """
+
+ gr.hier_block2.__init__(self, "d8psk_mod",
+ gr.io_signature(1, 1, gr.sizeof_char), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature
+
+ self._samples_per_symbol = samples_per_symbol
+ self._excess_bw = excess_bw
+ self._gray_code = gray_code
+
+ if not isinstance(samples_per_symbol, int) or samples_per_symbol < 2:
+ raise TypeError, ("sbp must be an integer >= 2, is %d" % samples_per_symbol)
+
+ ntaps = 11 * samples_per_symbol
+
+ arity = pow(2,self.bits_per_symbol())
+
+ # turn bytes into k-bit vectors
+ self.bytes2chunks = \
+ gr.packed_to_unpacked_bb(self.bits_per_symbol(), gr.GR_MSB_FIRST)
+
+ if self._gray_code:
+ self.symbol_mapper = gr.map_bb(psk.binary_to_gray[arity])
+ else:
+ self.symbol_mapper = gr.map_bb(psk.binary_to_ungray[arity])
+
+ self.diffenc = gr.diff_encoder_bb(arity)
+
+ rot = 1
+ rotated_const = map(lambda pt: pt * rot, psk.constellation[arity])
+ self.chunks2symbols = gr.chunks_to_symbols_bc(rotated_const)
+
+ # pulse shaping filter
+ self.rrc_taps = gr.firdes.root_raised_cosine(
+ self._samples_per_symbol, # gain (sps since we're interpolating by sps)
+ self._samples_per_symbol, # sampling rate
+ 1.0, # symbol rate
+ self._excess_bw, # excess bandwidth (roll-off factor)
+ ntaps)
+
+ self.rrc_filter = gr.interp_fir_filter_ccf(self._samples_per_symbol, self.rrc_taps)
+
+ if verbose:
+ self._print_verbage()
+
+ if log:
+ self._setup_logging()
+
+ # Connect & Initialize base class
+ self.connect(self, self.bytes2chunks, self.symbol_mapper, self.diffenc,
+ self.chunks2symbols, self.rrc_filter, self)
+
+ def samples_per_symbol(self):
+ return self._samples_per_symbol
+
+ def bits_per_symbol(self=None): # staticmethod that's also callable on an instance
+ return 3
+ bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM
+
+ def _print_verbage(self):
+ print "bits per symbol = %d" % self.bits_per_symbol()
+ print "Gray code = %s" % self._gray_code
+ print "RS roll-off factor = %f" % self._excess_bw
+
+ def _setup_logging(self):
+ print "Modulation logging turned on."
+ self.connect(self.bytes2chunks,
+ gr.file_sink(gr.sizeof_char, "tx_bytes2chunks.dat"))
+ self.connect(self.symbol_mapper,
+ gr.file_sink(gr.sizeof_char, "tx_graycoder.dat"))
+ self.connect(self.diffenc,
+ gr.file_sink(gr.sizeof_char, "tx_diffenc.dat"))
+ self.connect(self.chunks2symbols,
+ gr.file_sink(gr.sizeof_gr_complex, "tx_chunks2symbols.dat"))
+ self.connect(self.rrc_filter,
+ gr.file_sink(gr.sizeof_gr_complex, "tx_rrc_filter.dat"))
+
+ def add_options(parser):
+ """
+ Adds 8PSK modulation-specific options to the standard parser
+ """
+ parser.add_option("", "--excess-bw", type="float", default=_def_excess_bw,
+ help="set RRC excess bandwith factor [default=%default] (PSK)")
+ parser.add_option("", "--no-gray-code", dest="gray_code",
+ action="store_false", default=_def_gray_code,
+ help="disable gray coding on modulated bits (PSK)")
+ add_options=staticmethod(add_options)
+
+
+ def extract_kwargs_from_options(options):
+ """
+ Given command line options, create dictionary suitable for passing to __init__
+ """
+ return modulation_utils.extract_kwargs_from_options(d8psk_mod.__init__,
+ ('self',), options)
+ extract_kwargs_from_options=staticmethod(extract_kwargs_from_options)
+
+
+# /////////////////////////////////////////////////////////////////////////////
+# D8PSK demodulator
+#
+# Differentially coherent detection of differentially encoded 8psk
+# /////////////////////////////////////////////////////////////////////////////
+
+class d8psk_demod(gr.hier_block2):
+
+ def __init__(self,
+ samples_per_symbol=_def_samples_per_symbol,
+ excess_bw=_def_excess_bw,
+ costas_alpha=_def_costas_alpha,
+ gain_mu=_def_gain_mu,
+ mu=_def_mu,
+ omega_relative_limit=_def_omega_relative_limit,
+ gray_code=_def_gray_code,
+ verbose=_def_verbose,
+ log=_def_log):
+ """
+ Hierarchical block for RRC-filtered DQPSK demodulation
+
+ The input is the complex modulated signal at baseband.
+ The output is a stream of bits packed 1 bit per byte (LSB)
+
+ @param samples_per_symbol: samples per symbol >= 2
+ @type samples_per_symbol: float
+ @param excess_bw: Root-raised cosine filter excess bandwidth
+ @type excess_bw: float
+ @param costas_alpha: loop filter gain
+ @type costas_alphas: float
+ @param gain_mu: for M&M block
+ @type gain_mu: float
+ @param mu: for M&M block
+ @type mu: float
+ @param omega_relative_limit: for M&M block
+ @type omega_relative_limit: float
+ @param gray_code: Tell modulator to Gray code the bits
+ @type gray_code: bool
+ @param verbose: Print information about modulator?
+ @type verbose: bool
+ @param debug: Print modualtion data to files?
+ @type debug: bool
+ """
+
+ gr.hier_block2.__init__(self, "d8psk_demod",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_char)) # Output signature
+
+ self._samples_per_symbol = samples_per_symbol
+ self._excess_bw = excess_bw
+ self._costas_alpha = costas_alpha
+ self._mm_gain_mu = gain_mu
+ self._mm_mu = mu
+ self._mm_omega_relative_limit = omega_relative_limit
+ self._gray_code = gray_code
+
+ if samples_per_symbol < 2:
+ raise TypeError, "sbp must be >= 2, is %d" % samples_per_symbol
+
+ arity = pow(2,self.bits_per_symbol())
+
+ # Automatic gain control
+ scale = (1.0/16384.0)
+ self.pre_scaler = gr.multiply_const_cc(scale) # scale the signal from full-range to +-1
+ #self.agc = gr.agc_cc(1e-2, 1, 1, 100)
+ self.agc = gr.agc2_cc(1e-1, 1e-2, 1, 1, 100)
+ #self.agc = gr.feedforward_agc_cc(16, 1.0)
+
+ # RRC data filter
+ ntaps = 11 * samples_per_symbol
+ self.rrc_taps = gr.firdes.root_raised_cosine(
+ 1.0, # gain
+ self._samples_per_symbol, # sampling rate
+ 1.0, # symbol rate
+ self._excess_bw, # excess bandwidth (roll-off factor)
+ ntaps)
+ self.rrc_filter=gr.interp_fir_filter_ccf(1, self.rrc_taps)
+
+ # symbol clock recovery
+ self._mm_omega = self._samples_per_symbol
+ self._mm_gain_omega = .25 * self._mm_gain_mu * self._mm_gain_mu
+ self._costas_beta = 0.25 * self._costas_alpha * self._costas_alpha
+ fmin = -0.025
+ fmax = 0.025
+
+ self.receiver=gr.mpsk_receiver_cc(arity, 0,
+ self._costas_alpha, self._costas_beta,
+ fmin, fmax,
+ self._mm_mu, self._mm_gain_mu,
+ self._mm_omega, self._mm_gain_omega,
+ self._mm_omega_relative_limit)
+
+ # Perform Differential decoding on the constellation
+ self.diffdec = gr.diff_phasor_cc()
+
+ # find closest constellation point
+ rot = 1
+ rotated_const = map(lambda pt: pt * rot, psk.constellation[arity])
+ self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity))
+
+ if self._gray_code:
+ self.symbol_mapper = gr.map_bb(psk.gray_to_binary[arity])
+ else:
+ self.symbol_mapper = gr.map_bb(psk.ungray_to_binary[arity])
+
+
+ # unpack the k bit vector into a stream of bits
+ self.unpack = gr.unpack_k_bits_bb(self.bits_per_symbol())
+
+ if verbose:
+ self._print_verbage()
+
+ if log:
+ self._setup_logging()
+
+ # Connect
+ self.connect(self, self.pre_scaler, self.agc, self.rrc_filter, self.receiver,
+ self.diffdec, self.slicer, self.symbol_mapper, self.unpack, self)
+
+ def samples_per_symbol(self):
+ return self._samples_per_symbol
+
+ def bits_per_symbol(self=None): # staticmethod that's also callable on an instance
+ return 3
+ bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM
+
+ def _print_verbage(self):
+ print "\nDemodulator:"
+ print "bits per symbol: %d" % self.bits_per_symbol()
+ print "Gray code: %s" % self._gray_code
+ print "RRC roll-off factor: %.2f" % self._excess_bw
+ print "Costas Loop alpha: %.2e" % self._costas_alpha
+ print "Costas Loop beta: %.2e" % self._costas_beta
+ print "M&M mu: %.2f" % self._mm_mu
+ print "M&M mu gain: %.2e" % self._mm_gain_mu
+ print "M&M omega: %.2f" % self._mm_omega
+ print "M&M omega gain: %.2e" % self._mm_gain_omega
+ print "M&M omega limit: %.2f" % self._mm_omega_relative_limit
+
+
+ def _setup_logging(self):
+ print "Modulation logging turned on."
+ self.connect(self.pre_scaler,
+ gr.file_sink(gr.sizeof_gr_complex, "rx_prescaler.dat"))
+ self.connect(self.agc,
+ gr.file_sink(gr.sizeof_gr_complex, "rx_agc.dat"))
+ self.connect(self.rrc_filter,
+ gr.file_sink(gr.sizeof_gr_complex, "rx_rrc_filter.dat"))
+ self.connect(self.receiver,
+ gr.file_sink(gr.sizeof_gr_complex, "rx_receiver.dat"))
+ self.connect(self.diffdec,
+ gr.file_sink(gr.sizeof_gr_complex, "rx_diffdec.dat"))
+ self.connect(self.slicer,
+ gr.file_sink(gr.sizeof_char, "rx_slicer.dat"))
+ self.connect(self.symbol_mapper,
+ gr.file_sink(gr.sizeof_char, "rx_gray_decoder.dat"))
+ self.connect(self.unpack,
+ gr.file_sink(gr.sizeof_char, "rx_unpack.dat"))
+
+ def add_options(parser):
+ """
+ Adds modulation-specific options to the standard parser
+ """
+ parser.add_option("", "--excess-bw", type="float", default=_def_excess_bw,
+ help="set RRC excess bandwith factor [default=%default] (PSK)")
+ parser.add_option("", "--no-gray-code", dest="gray_code",
+ action="store_false", default=_def_gray_code,
+ help="disable gray coding on modulated bits (PSK)")
+ parser.add_option("", "--costas-alpha", type="float", default=_def_costas_alpha,
+ help="set Costas loop alpha value [default=%default] (PSK)")
+ parser.add_option("", "--gain-mu", type="float", default=_def_gain_mu,
+ help="set M&M symbol sync loop gain mu value [default=%default] (PSK)")
+ parser.add_option("", "--mu", type="float", default=_def_mu,
+ help="set M&M symbol sync loop mu value [default=%default] (PSK)")
+ add_options=staticmethod(add_options)
+
+ def extract_kwargs_from_options(options):
+ """
+ Given command line options, create dictionary suitable for passing to __init__
+ """
+ return modulation_utils.extract_kwargs_from_options(
+ d8psk_demod.__init__, ('self',), options)
+ extract_kwargs_from_options=staticmethod(extract_kwargs_from_options)
+
+
+#
+# Add these to the mod/demod registry
+#
+# NOT READY TO BE USED YET -- ENABLE AT YOUR OWN RISK
+modulation_utils.add_type_1_mod('d8psk', d8psk_mod)
+modulation_utils.add_type_1_demod('d8psk', d8psk_demod)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/dbpsk.py b/gnuradio-core/src/python/gnuradio/blks2impl/dbpsk.py
new file mode 100644
index 0000000000..27063767c9
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/dbpsk.py
@@ -0,0 +1,363 @@
+#
+# Copyright 2005,2006,2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+# See gnuradio-examples/python/digital for examples
+
+"""
+differential BPSK modulation and demodulation.
+"""
+
+from gnuradio import gr, gru, modulation_utils
+from math import pi, sqrt
+import psk
+import cmath
+from pprint import pprint
+
+# default values (used in __init__ and add_options)
+_def_samples_per_symbol = 2
+_def_excess_bw = 0.35
+_def_gray_code = True
+_def_verbose = False
+_def_log = False
+
+_def_costas_alpha = 0.1
+_def_gain_mu = None
+_def_mu = 0.5
+_def_omega_relative_limit = 0.005
+
+
+# /////////////////////////////////////////////////////////////////////////////
+# DBPSK modulator
+# /////////////////////////////////////////////////////////////////////////////
+
+class dbpsk_mod(gr.hier_block2):
+
+ def __init__(self,
+ samples_per_symbol=_def_samples_per_symbol,
+ excess_bw=_def_excess_bw,
+ gray_code=_def_gray_code,
+ verbose=_def_verbose,
+ log=_def_log):
+ """
+ Hierarchical block for RRC-filtered differential BPSK modulation.
+
+ The input is a byte stream (unsigned char) and the
+ output is the complex modulated signal at baseband.
+
+ @param samples_per_symbol: samples per baud >= 2
+ @type samples_per_symbol: integer
+ @param excess_bw: Root-raised cosine filter excess bandwidth
+ @type excess_bw: float
+ @param gray_code: Tell modulator to Gray code the bits
+ @type gray_code: bool
+ @param verbose: Print information about modulator?
+ @type verbose: bool
+ @param log: Log modulation data to files?
+ @type log: bool
+ """
+
+ gr.hier_block2.__init__(self, "dbpsk_mod",
+ gr.io_signature(1, 1, gr.sizeof_char), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature
+
+ self._samples_per_symbol = samples_per_symbol
+ self._excess_bw = excess_bw
+ self._gray_code = gray_code
+
+ if not isinstance(self._samples_per_symbol, int) or self._samples_per_symbol < 2:
+ raise TypeError, ("sbp must be an integer >= 2, is %d" % self._samples_per_symbol)
+
+ ntaps = 11 * self._samples_per_symbol
+
+ arity = pow(2,self.bits_per_symbol())
+
+ # turn bytes into k-bit vectors
+ self.bytes2chunks = \
+ gr.packed_to_unpacked_bb(self.bits_per_symbol(), gr.GR_MSB_FIRST)
+
+ if self._gray_code:
+ self.symbol_mapper = gr.map_bb(psk.binary_to_gray[arity])
+ else:
+ self.symbol_mapper = gr.map_bb(psk.binary_to_ungray[arity])
+
+ self.diffenc = gr.diff_encoder_bb(arity)
+
+ self.chunks2symbols = gr.chunks_to_symbols_bc(psk.constellation[arity])
+
+ # pulse shaping filter
+ self.rrc_taps = gr.firdes.root_raised_cosine(
+ self._samples_per_symbol, # gain (samples_per_symbol since we're
+ # interpolating by samples_per_symbol)
+ self._samples_per_symbol, # sampling rate
+ 1.0, # symbol rate
+ self._excess_bw, # excess bandwidth (roll-off factor)
+ ntaps)
+ self.rrc_filter = gr.interp_fir_filter_ccf(self._samples_per_symbol,
+ self.rrc_taps)
+
+ # Connect
+ self.connect(self, self.bytes2chunks, self.symbol_mapper, self.diffenc,
+ self.chunks2symbols, self.rrc_filter, self)
+
+ if verbose:
+ self._print_verbage()
+
+ if log:
+ self._setup_logging()
+
+
+ def samples_per_symbol(self):
+ return self._samples_per_symbol
+
+ def bits_per_symbol(self=None): # static method that's also callable on an instance
+ return 1
+ bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM
+
+ def add_options(parser):
+ """
+ Adds DBPSK modulation-specific options to the standard parser
+ """
+ parser.add_option("", "--excess-bw", type="float", default=_def_excess_bw,
+ help="set RRC excess bandwith factor [default=%default]")
+ parser.add_option("", "--no-gray-code", dest="gray_code",
+ action="store_false", default=True,
+ help="disable gray coding on modulated bits (PSK)")
+ add_options=staticmethod(add_options)
+
+ def extract_kwargs_from_options(options):
+ """
+ Given command line options, create dictionary suitable for passing to __init__
+ """
+ return modulation_utils.extract_kwargs_from_options(dbpsk_mod.__init__,
+ ('self',), options)
+ extract_kwargs_from_options=staticmethod(extract_kwargs_from_options)
+
+
+ def _print_verbage(self):
+ print "\nModulator:"
+ print "bits per symbol: %d" % self.bits_per_symbol()
+ print "Gray code: %s" % self._gray_code
+ print "RRC roll-off factor: %.2f" % self._excess_bw
+
+ def _setup_logging(self):
+ print "Modulation logging turned on."
+ self.connect(self.bytes2chunks,
+ gr.file_sink(gr.sizeof_char, "tx_bytes2chunks.dat"))
+ self.connect(self.symbol_mapper,
+ gr.file_sink(gr.sizeof_char, "tx_graycoder.dat"))
+ self.connect(self.diffenc,
+ gr.file_sink(gr.sizeof_char, "tx_diffenc.dat"))
+ self.connect(self.chunks2symbols,
+ gr.file_sink(gr.sizeof_gr_complex, "tx_chunks2symbols.dat"))
+ self.connect(self.rrc_filter,
+ gr.file_sink(gr.sizeof_gr_complex, "tx_rrc_filter.dat"))
+
+
+# /////////////////////////////////////////////////////////////////////////////
+# DBPSK demodulator
+#
+# Differentially coherent detection of differentially encoded BPSK
+# /////////////////////////////////////////////////////////////////////////////
+
+class dbpsk_demod(gr.hier_block2):
+
+ def __init__(self,
+ samples_per_symbol=_def_samples_per_symbol,
+ excess_bw=_def_excess_bw,
+ costas_alpha=_def_costas_alpha,
+ gain_mu=_def_gain_mu,
+ mu=_def_mu,
+ omega_relative_limit=_def_omega_relative_limit,
+ gray_code=_def_gray_code,
+ verbose=_def_verbose,
+ log=_def_log):
+ """
+ Hierarchical block for RRC-filtered differential BPSK demodulation
+
+ The input is the complex modulated signal at baseband.
+ The output is a stream of bits packed 1 bit per byte (LSB)
+
+ @param samples_per_symbol: samples per symbol >= 2
+ @type samples_per_symbol: float
+ @param excess_bw: Root-raised cosine filter excess bandwidth
+ @type excess_bw: float
+ @param costas_alpha: loop filter gain
+ @type costas_alphas: float
+ @param gain_mu: for M&M block
+ @type gain_mu: float
+ @param mu: for M&M block
+ @type mu: float
+ @param omega_relative_limit: for M&M block
+ @type omega_relative_limit: float
+ @param gray_code: Tell modulator to Gray code the bits
+ @type gray_code: bool
+ @param verbose: Print information about modulator?
+ @type verbose: bool
+ @param debug: Print modualtion data to files?
+ @type debug: bool
+ """
+
+ gr.hier_block2.__init__(self, "dbpsk_demod",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_char)) # Output signature
+
+ self._samples_per_symbol = samples_per_symbol
+ self._excess_bw = excess_bw
+ self._costas_alpha = costas_alpha
+ self._mm_gain_mu = gain_mu
+ self._mm_mu = mu
+ self._mm_omega_relative_limit = omega_relative_limit
+ self._gray_code = gray_code
+
+ if samples_per_symbol < 2:
+ raise TypeError, "samples_per_symbol must be >= 2, is %r" % (samples_per_symbol,)
+
+ arity = pow(2,self.bits_per_symbol())
+
+ # Automatic gain control
+ scale = (1.0/16384.0)
+ self.pre_scaler = gr.multiply_const_cc(scale) # scale the signal from full-range to +-1
+ #self.agc = gr.agc2_cc(0.6e-1, 1e-3, 1, 1, 100)
+ self.agc = gr.feedforward_agc_cc(16, 2.0)
+
+ # RRC data filter
+ ntaps = 11 * samples_per_symbol
+ self.rrc_taps = gr.firdes.root_raised_cosine(
+ 1.0, # gain
+ self._samples_per_symbol, # sampling rate
+ 1.0, # symbol rate
+ self._excess_bw, # excess bandwidth (roll-off factor)
+ ntaps)
+ self.rrc_filter=gr.interp_fir_filter_ccf(1, self.rrc_taps)
+
+ # symbol clock recovery
+ if not self._mm_gain_mu:
+ self._mm_gain_mu = 0.1
+
+ self._mm_omega = self._samples_per_symbol
+ self._mm_gain_omega = .25 * self._mm_gain_mu * self._mm_gain_mu
+ self._costas_beta = 0.25 * self._costas_alpha * self._costas_alpha
+ fmin = -0.025
+ fmax = 0.025
+
+ self.receiver=gr.mpsk_receiver_cc(arity, 0,
+ self._costas_alpha, self._costas_beta,
+ fmin, fmax,
+ self._mm_mu, self._mm_gain_mu,
+ self._mm_omega, self._mm_gain_omega,
+ self._mm_omega_relative_limit)
+
+ # Do differential decoding based on phase change of symbols
+ self.diffdec = gr.diff_phasor_cc()
+
+ # find closest constellation point
+ rot = 1
+ rotated_const = map(lambda pt: pt * rot, psk.constellation[arity])
+ self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity))
+
+ if self._gray_code:
+ self.symbol_mapper = gr.map_bb(psk.gray_to_binary[arity])
+ else:
+ self.symbol_mapper = gr.map_bb(psk.ungray_to_binary[arity])
+
+ # unpack the k bit vector into a stream of bits
+ self.unpack = gr.unpack_k_bits_bb(self.bits_per_symbol())
+
+ if verbose:
+ self._print_verbage()
+
+ if log:
+ self._setup_logging()
+
+ # Connect and Initialize base class
+ self.connect(self, self.pre_scaler, self.agc, self.rrc_filter, self.receiver,
+ self.diffdec, self.slicer, self.symbol_mapper, self.unpack, self)
+
+ def samples_per_symbol(self):
+ return self._samples_per_symbol
+
+ def bits_per_symbol(self=None): # staticmethod that's also callable on an instance
+ return 1
+ bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM
+
+ def _print_verbage(self):
+ print "\nDemodulator:"
+ print "bits per symbol: %d" % self.bits_per_symbol()
+ print "Gray code: %s" % self._gray_code
+ print "RRC roll-off factor: %.2f" % self._excess_bw
+ print "Costas Loop alpha: %.2e" % self._costas_alpha
+ print "Costas Loop beta: %.2e" % self._costas_beta
+ print "M&M mu: %.2f" % self._mm_mu
+ print "M&M mu gain: %.2e" % self._mm_gain_mu
+ print "M&M omega: %.2f" % self._mm_omega
+ print "M&M omega gain: %.2e" % self._mm_gain_omega
+ print "M&M omega limit: %.2f" % self._mm_omega_relative_limit
+
+ def _setup_logging(self):
+ print "Modulation logging turned on."
+ self.connect(self.pre_scaler,
+ gr.file_sink(gr.sizeof_gr_complex, "rx_prescaler.dat"))
+ self.connect(self.agc,
+ gr.file_sink(gr.sizeof_gr_complex, "rx_agc.dat"))
+ self.connect(self.rrc_filter,
+ gr.file_sink(gr.sizeof_gr_complex, "rx_rrc_filter.dat"))
+ self.connect(self.receiver,
+ gr.file_sink(gr.sizeof_gr_complex, "rx_receiver.dat"))
+ self.connect(self.diffdec,
+ gr.file_sink(gr.sizeof_gr_complex, "rx_diffdec.dat"))
+ self.connect(self.slicer,
+ gr.file_sink(gr.sizeof_char, "rx_slicer.dat"))
+ self.connect(self.symbol_mapper,
+ gr.file_sink(gr.sizeof_char, "rx_symbol_mapper.dat"))
+ self.connect(self.unpack,
+ gr.file_sink(gr.sizeof_char, "rx_unpack.dat"))
+
+ def add_options(parser):
+ """
+ Adds DBPSK demodulation-specific options to the standard parser
+ """
+ parser.add_option("", "--excess-bw", type="float", default=_def_excess_bw,
+ help="set RRC excess bandwith factor [default=%default] (PSK)")
+ parser.add_option("", "--no-gray-code", dest="gray_code",
+ action="store_false", default=_def_gray_code,
+ help="disable gray coding on modulated bits (PSK)")
+ parser.add_option("", "--costas-alpha", type="float", default=None,
+ help="set Costas loop alpha value [default=%default] (PSK)")
+ parser.add_option("", "--gain-mu", type="float", default=_def_gain_mu,
+ help="set M&M symbol sync loop gain mu value [default=%default] (GMSK/PSK)")
+ parser.add_option("", "--mu", type="float", default=_def_mu,
+ help="set M&M symbol sync loop mu value [default=%default] (GMSK/PSK)")
+ parser.add_option("", "--omega-relative-limit", type="float", default=_def_omega_relative_limit,
+ help="M&M clock recovery omega relative limit [default=%default] (GMSK/PSK)")
+ add_options=staticmethod(add_options)
+
+ def extract_kwargs_from_options(options):
+ """
+ Given command line options, create dictionary suitable for passing to __init__
+ """
+ return modulation_utils.extract_kwargs_from_options(
+ dbpsk_demod.__init__, ('self',), options)
+ extract_kwargs_from_options=staticmethod(extract_kwargs_from_options)
+#
+# Add these to the mod/demod registry
+#
+modulation_utils.add_type_1_mod('dbpsk', dbpsk_mod)
+modulation_utils.add_type_1_demod('dbpsk', dbpsk_demod)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/digital_voice.py.real b/gnuradio-core/src/python/gnuradio/blks2impl/digital_voice.py.real
new file mode 100644
index 0000000000..6ec66825c1
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/digital_voice.py.real
@@ -0,0 +1,102 @@
+#!/usr/bin/env python
+#
+# Copyright 2005 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+"""
+Digital voice Tx and Rx using GSM 13kbit vocoder and GMSK.
+
+Runs channel at 32kbit/sec. Currently uses fake channel coding,
+but there's room for a rate 1/2 coder.
+"""
+
+from gnuradio import gr, gru
+from gnuradio.blksimpl.gmsk import gmsk_mod, gmsk_demod
+
+from gnuradio.vocoder import gsm_full_rate
+
+# Size of gsm full rate speech encoder output packet in bytes
+
+GSM_FRAME_SIZE = 33
+
+# Size of packet in bytes that we send to GMSK modulator:
+#
+# Target: 256kS/sec air rate.
+#
+# 256kS 1 sym 1 bit 1 byte 0.020 sec 80 bytes
+# ---- * ----- * ----- * ------ * --------- = --------
+# sec 8 S 1 sym 8 bits frame frame
+#
+# gr_simple_framer add 10 bytes of overhead.
+
+AIR_FRAME_SIZE = 70
+
+
+class digital_voice_tx(gr.hier_block):
+ """
+ Hierarchical block for digital voice tranmission.
+
+ The input is 8kS/sec floating point audio in the range [-1,+1]
+ The output is 256kS/sec GMSK modulated complex baseband signal in the range [-1,+1].
+ """
+ def __init__(self, fg):
+ samples_per_symbol = 8
+ symbol_rate = 32000
+ bt = 0.3 # Gaussian filter bandwidth * symbol time
+
+ src_scale = gr.multiply_const_ff(32767)
+ f2s = gr.float_to_short()
+ voice_coder = gsm_full_rate.encode_sp()
+
+ channel_coder = gr.fake_channel_encoder_pp(GSM_FRAME_SIZE, AIR_FRAME_SIZE)
+ p2s = gr.parallel_to_serial(gr.sizeof_char, AIR_FRAME_SIZE)
+
+ mod = gmsk_mod(fg, sps=samples_per_symbol,
+ symbol_rate=symbol_rate, bt=bt,
+ p_size=AIR_FRAME_SIZE)
+
+ fg.connect(src_scale, f2s, voice_coder, channel_coder, p2s, mod)
+ gr.hier_block.__init__(self, fg, src_scale, mod)
+
+
+class digital_voice_rx(gr.hier_block):
+ """
+ Hierarchical block for digital voice reception.
+
+ The input is 256kS/sec GMSK modulated complex baseband signal.
+ The output is 8kS/sec floating point audio in the range [-1,+1]
+ """
+ def __init__(self, fg):
+ samples_per_symbol = 8
+ symbol_rate = 32000
+
+ demod = gmsk_demod(fg, sps=samples_per_symbol,
+ symbol_rate=symbol_rate,
+ p_size=AIR_FRAME_SIZE)
+
+ s2p = gr.serial_to_parallel(gr.sizeof_char, AIR_FRAME_SIZE)
+ channel_decoder = gr.fake_channel_decoder_pp(AIR_FRAME_SIZE, GSM_FRAME_SIZE)
+
+ voice_decoder = gsm_full_rate.decode_ps()
+ s2f = gr.short_to_float ()
+ sink_scale = gr.multiply_const_ff(1.0/32767.)
+
+ fg.connect(demod, s2p, channel_decoder, voice_decoder, s2f, sink_scale)
+ gr.hier_block.__init__(self, fg, demod, sink_scale)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/dqpsk.py b/gnuradio-core/src/python/gnuradio/blks2impl/dqpsk.py
new file mode 100644
index 0000000000..8c15d21734
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/dqpsk.py
@@ -0,0 +1,363 @@
+#
+# Copyright 2005,2006,2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+# See gnuradio-examples/python/digital for examples
+
+"""
+differential QPSK modulation and demodulation.
+"""
+
+from gnuradio import gr, gru, modulation_utils
+from math import pi, sqrt
+import psk
+import cmath
+from pprint import pprint
+
+# default values (used in __init__ and add_options)
+_def_samples_per_symbol = 2
+_def_excess_bw = 0.35
+_def_gray_code = True
+_def_verbose = False
+_def_log = False
+
+_def_costas_alpha = 0.15
+_def_gain_mu = None
+_def_mu = 0.5
+_def_omega_relative_limit = 0.005
+
+
+# /////////////////////////////////////////////////////////////////////////////
+# DQPSK modulator
+# /////////////////////////////////////////////////////////////////////////////
+
+class dqpsk_mod(gr.hier_block2):
+
+ def __init__(self,
+ samples_per_symbol=_def_samples_per_symbol,
+ excess_bw=_def_excess_bw,
+ gray_code=_def_gray_code,
+ verbose=_def_verbose,
+ log=_def_log):
+ """
+ Hierarchical block for RRC-filtered QPSK modulation.
+
+ The input is a byte stream (unsigned char) and the
+ output is the complex modulated signal at baseband.
+
+ @param samples_per_symbol: samples per symbol >= 2
+ @type samples_per_symbol: integer
+ @param excess_bw: Root-raised cosine filter excess bandwidth
+ @type excess_bw: float
+ @param gray_code: Tell modulator to Gray code the bits
+ @type gray_code: bool
+ @param verbose: Print information about modulator?
+ @type verbose: bool
+ @param debug: Print modualtion data to files?
+ @type debug: bool
+ """
+
+ gr.hier_block2.__init__(self, "dqpsk_mod",
+ gr.io_signature(1, 1, gr.sizeof_char), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature
+
+ self._samples_per_symbol = samples_per_symbol
+ self._excess_bw = excess_bw
+ self._gray_code = gray_code
+
+ if not isinstance(samples_per_symbol, int) or samples_per_symbol < 2:
+ raise TypeError, ("sbp must be an integer >= 2, is %d" % samples_per_symbol)
+
+ ntaps = 11 * samples_per_symbol
+
+ arity = pow(2,self.bits_per_symbol())
+
+ # turn bytes into k-bit vectors
+ self.bytes2chunks = \
+ gr.packed_to_unpacked_bb(self.bits_per_symbol(), gr.GR_MSB_FIRST)
+
+ if self._gray_code:
+ self.symbol_mapper = gr.map_bb(psk.binary_to_gray[arity])
+ else:
+ self.symbol_mapper = gr.map_bb(psk.binary_to_ungray[arity])
+
+ self.diffenc = gr.diff_encoder_bb(arity)
+
+ rot = .707 + .707j
+ rotated_const = map(lambda pt: pt * rot, psk.constellation[arity])
+ self.chunks2symbols = gr.chunks_to_symbols_bc(rotated_const)
+
+ # pulse shaping filter
+ self.rrc_taps = gr.firdes.root_raised_cosine(
+ self._samples_per_symbol, # gain (sps since we're interpolating by sps)
+ self._samples_per_symbol, # sampling rate
+ 1.0, # symbol rate
+ self._excess_bw, # excess bandwidth (roll-off factor)
+ ntaps)
+
+ self.rrc_filter = gr.interp_fir_filter_ccf(self._samples_per_symbol, self.rrc_taps)
+
+ if verbose:
+ self._print_verbage()
+
+ if log:
+ self._setup_logging()
+
+ # Connect & Initialize base class
+ self.connect(self, self.bytes2chunks, self.symbol_mapper, self.diffenc,
+ self.chunks2symbols, self.rrc_filter, self)
+
+ def samples_per_symbol(self):
+ return self._samples_per_symbol
+
+ def bits_per_symbol(self=None): # staticmethod that's also callable on an instance
+ return 2
+ bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM
+
+ def _print_verbage(self):
+ print "\nModulator:"
+ print "bits per symbol: %d" % self.bits_per_symbol()
+ print "Gray code: %s" % self._gray_code
+ print "RRS roll-off factor: %f" % self._excess_bw
+
+ def _setup_logging(self):
+ print "Modulation logging turned on."
+ self.connect(self.bytes2chunks,
+ gr.file_sink(gr.sizeof_char, "tx_bytes2chunks.dat"))
+ self.connect(self.symbol_mapper,
+ gr.file_sink(gr.sizeof_char, "tx_graycoder.dat"))
+ self.connect(self.diffenc,
+ gr.file_sink(gr.sizeof_char, "tx_diffenc.dat"))
+ self.connect(self.chunks2symbols,
+ gr.file_sink(gr.sizeof_gr_complex, "tx_chunks2symbols.dat"))
+ self.connect(self.rrc_filter,
+ gr.file_sink(gr.sizeof_gr_complex, "tx_rrc_filter.dat"))
+
+ def add_options(parser):
+ """
+ Adds QPSK modulation-specific options to the standard parser
+ """
+ parser.add_option("", "--excess-bw", type="float", default=_def_excess_bw,
+ help="set RRC excess bandwith factor [default=%default] (PSK)")
+ parser.add_option("", "--no-gray-code", dest="gray_code",
+ action="store_false", default=_def_gray_code,
+ help="disable gray coding on modulated bits (PSK)")
+ add_options=staticmethod(add_options)
+
+
+ def extract_kwargs_from_options(options):
+ """
+ Given command line options, create dictionary suitable for passing to __init__
+ """
+ return modulation_utils.extract_kwargs_from_options(dqpsk_mod.__init__,
+ ('self',), options)
+ extract_kwargs_from_options=staticmethod(extract_kwargs_from_options)
+
+
+# /////////////////////////////////////////////////////////////////////////////
+# DQPSK demodulator
+#
+# Differentially coherent detection of differentially encoded qpsk
+# /////////////////////////////////////////////////////////////////////////////
+
+class dqpsk_demod(gr.hier_block2):
+
+ def __init__(self,
+ samples_per_symbol=_def_samples_per_symbol,
+ excess_bw=_def_excess_bw,
+ costas_alpha=_def_costas_alpha,
+ gain_mu=_def_gain_mu,
+ mu=_def_mu,
+ omega_relative_limit=_def_omega_relative_limit,
+ gray_code=_def_gray_code,
+ verbose=_def_verbose,
+ log=_def_log):
+ """
+ Hierarchical block for RRC-filtered DQPSK demodulation
+
+ The input is the complex modulated signal at baseband.
+ The output is a stream of bits packed 1 bit per byte (LSB)
+
+ @param samples_per_symbol: samples per symbol >= 2
+ @type samples_per_symbol: float
+ @param excess_bw: Root-raised cosine filter excess bandwidth
+ @type excess_bw: float
+ @param costas_alpha: loop filter gain
+ @type costas_alphas: float
+ @param gain_mu: for M&M block
+ @type gain_mu: float
+ @param mu: for M&M block
+ @type mu: float
+ @param omega_relative_limit: for M&M block
+ @type omega_relative_limit: float
+ @param gray_code: Tell modulator to Gray code the bits
+ @type gray_code: bool
+ @param verbose: Print information about modulator?
+ @type verbose: bool
+ @param debug: Print modualtion data to files?
+ @type debug: bool
+ """
+
+ gr.hier_block2.__init__(self, "dqpsk_demod",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_char)) # Output signature
+
+ self._samples_per_symbol = samples_per_symbol
+ self._excess_bw = excess_bw
+ self._costas_alpha = costas_alpha
+ self._mm_gain_mu = gain_mu
+ self._mm_mu = mu
+ self._mm_omega_relative_limit = omega_relative_limit
+ self._gray_code = gray_code
+
+ if samples_per_symbol < 2:
+ raise TypeError, "sbp must be >= 2, is %d" % samples_per_symbol
+
+ arity = pow(2,self.bits_per_symbol())
+
+ # Automatic gain control
+ scale = (1.0/16384.0)
+ self.pre_scaler = gr.multiply_const_cc(scale) # scale the signal from full-range to +-1
+ #self.agc = gr.agc2_cc(0.6e-1, 1e-3, 1, 1, 100)
+ self.agc = gr.feedforward_agc_cc(16, 2.0)
+
+ # RRC data filter
+ ntaps = 11 * samples_per_symbol
+ self.rrc_taps = gr.firdes.root_raised_cosine(
+ 1.0, # gain
+ self._samples_per_symbol, # sampling rate
+ 1.0, # symbol rate
+ self._excess_bw, # excess bandwidth (roll-off factor)
+ ntaps)
+ self.rrc_filter=gr.interp_fir_filter_ccf(1, self.rrc_taps)
+
+ if not self._mm_gain_mu:
+ sbs_to_mm = {2: 0.050, 3: 0.075, 4: 0.11, 5: 0.125, 6: 0.15, 7: 0.15}
+ self._mm_gain_mu = sbs_to_mm[samples_per_symbol]
+
+ self._mm_omega = self._samples_per_symbol
+ self._mm_gain_omega = .25 * self._mm_gain_mu * self._mm_gain_mu
+ self._costas_beta = 0.25 * self._costas_alpha * self._costas_alpha
+ fmin = -0.025
+ fmax = 0.025
+
+ self.receiver=gr.mpsk_receiver_cc(arity, pi/4.0,
+ self._costas_alpha, self._costas_beta,
+ fmin, fmax,
+ self._mm_mu, self._mm_gain_mu,
+ self._mm_omega, self._mm_gain_omega,
+ self._mm_omega_relative_limit)
+
+ # Perform Differential decoding on the constellation
+ self.diffdec = gr.diff_phasor_cc()
+
+ # find closest constellation point
+ rot = 1
+ rotated_const = map(lambda pt: pt * rot, psk.constellation[arity])
+ self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity))
+
+ if self._gray_code:
+ self.symbol_mapper = gr.map_bb(psk.gray_to_binary[arity])
+ else:
+ self.symbol_mapper = gr.map_bb(psk.ungray_to_binary[arity])
+
+ # unpack the k bit vector into a stream of bits
+ self.unpack = gr.unpack_k_bits_bb(self.bits_per_symbol())
+
+ if verbose:
+ self._print_verbage()
+
+ if log:
+ self._setup_logging()
+
+ # Connect & Initialize base class
+ self.connect(self, self.pre_scaler, self.agc, self.rrc_filter, self.receiver,
+ self.diffdec, self.slicer, self.symbol_mapper, self.unpack, self)
+
+ def samples_per_symbol(self):
+ return self._samples_per_symbol
+
+ def bits_per_symbol(self=None): # staticmethod that's also callable on an instance
+ return 2
+ bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM
+
+ def _print_verbage(self):
+ print "\nDemodulator:"
+ print "bits per symbol: %d" % self.bits_per_symbol()
+ print "Gray code: %s" % self._gray_code
+ print "RRC roll-off factor: %.2f" % self._excess_bw
+ print "Costas Loop alpha: %.2e" % self._costas_alpha
+ print "Costas Loop beta: %.2e" % self._costas_beta
+ print "M&M mu: %.2f" % self._mm_mu
+ print "M&M mu gain: %.2e" % self._mm_gain_mu
+ print "M&M omega: %.2f" % self._mm_omega
+ print "M&M omega gain: %.2e" % self._mm_gain_omega
+ print "M&M omega limit: %.2f" % self._mm_omega_relative_limit
+
+ def _setup_logging(self):
+ print "Modulation logging turned on."
+ self.connect(self.pre_scaler,
+ gr.file_sink(gr.sizeof_gr_complex, "rx_prescaler.dat"))
+ self.connect(self.agc,
+ gr.file_sink(gr.sizeof_gr_complex, "rx_agc.dat"))
+ self.connect(self.rrc_filter,
+ gr.file_sink(gr.sizeof_gr_complex, "rx_rrc_filter.dat"))
+ self.connect(self.receiver,
+ gr.file_sink(gr.sizeof_gr_complex, "rx_receiver.dat"))
+ self.connect(self.diffdec,
+ gr.file_sink(gr.sizeof_gr_complex, "rx_diffdec.dat"))
+ self.connect(self.slicer,
+ gr.file_sink(gr.sizeof_char, "rx_slicer.dat"))
+ self.connect(self.symbol_mapper,
+ gr.file_sink(gr.sizeof_char, "rx_gray_decoder.dat"))
+ self.connect(self.unpack,
+ gr.file_sink(gr.sizeof_char, "rx_unpack.dat"))
+
+ def add_options(parser):
+ """
+ Adds modulation-specific options to the standard parser
+ """
+ parser.add_option("", "--excess-bw", type="float", default=_def_excess_bw,
+ help="set RRC excess bandwith factor [default=%default] (PSK)")
+ parser.add_option("", "--no-gray-code", dest="gray_code",
+ action="store_false", default=_def_gray_code,
+ help="disable gray coding on modulated bits (PSK)")
+ parser.add_option("", "--costas-alpha", type="float", default=_def_costas_alpha,
+ help="set Costas loop alpha value [default=%default] (PSK)")
+ parser.add_option("", "--gain-mu", type="float", default=_def_gain_mu,
+ help="set M&M symbol sync loop gain mu value [default=%default] (PSK)")
+ parser.add_option("", "--mu", type="float", default=_def_mu,
+ help="set M&M symbol sync loop mu value [default=%default] (PSK)")
+ add_options=staticmethod(add_options)
+
+ def extract_kwargs_from_options(options):
+ """
+ Given command line options, create dictionary suitable for passing to __init__
+ """
+ return modulation_utils.extract_kwargs_from_options(
+ dqpsk_demod.__init__, ('self',), options)
+ extract_kwargs_from_options=staticmethod(extract_kwargs_from_options)
+
+
+#
+# Add these to the mod/demod registry
+#
+modulation_utils.add_type_1_mod('dqpsk', dqpsk_mod)
+modulation_utils.add_type_1_demod('dqpsk', dqpsk_demod)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/filterbank.py b/gnuradio-core/src/python/gnuradio/blks2impl/filterbank.py
new file mode 100644
index 0000000000..c1284565f3
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/filterbank.py
@@ -0,0 +1,168 @@
+#
+# Copyright 2005,2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import sys
+from gnuradio import gr, gru
+
+def _generate_synthesis_taps(mpoints):
+ return [] # FIXME
+
+
+def _split_taps(taps, mpoints):
+ assert (len(taps) % mpoints) == 0
+ result = [list() for x in range(mpoints)]
+ for i in xrange(len(taps)):
+ (result[i % mpoints]).append(taps[i])
+ return [tuple(x) for x in result]
+
+
+class synthesis_filterbank(gr.hier_block2):
+ """
+ Uniformly modulated polyphase DFT filter bank: synthesis
+
+ See http://cnx.rice.edu/content/m10424/latest
+ """
+ def __init__(self, mpoints, taps=None):
+ """
+ Takes M complex streams in, produces single complex stream out
+ that runs at M times the input sample rate
+
+ @param mpoints: number of freq bins/interpolation factor/subbands
+ @param taps: filter taps for subband filter
+
+ The channel spacing is equal to the input sample rate.
+ The total bandwidth and output sample rate are equal the input
+ sample rate * nchannels.
+
+ Output stream to frequency mapping:
+
+ channel zero is at zero frequency.
+
+ if mpoints is odd:
+
+ Channels with increasing positive frequencies come from
+ channels 1 through (N-1)/2.
+
+ Channel (N+1)/2 is the maximum negative frequency, and
+ frequency increases through N-1 which is one channel lower
+ than the zero frequency.
+
+ if mpoints is even:
+
+ Channels with increasing positive frequencies come from
+ channels 1 through (N/2)-1.
+
+ Channel (N/2) is evenly split between the max positive and
+ negative bins.
+
+ Channel (N/2)+1 is the maximum negative frequency, and
+ frequency increases through N-1 which is one channel lower
+ than the zero frequency.
+
+ Channels near the frequency extremes end up getting cut
+ off by subsequent filters and therefore have diminished
+ utility.
+ """
+ item_size = gr.sizeof_gr_complex
+ gr.hier_block2.__init__(self, "synthesis_filterbank",
+ gr.io_signature(mpoints, mpoints, item_size), # Input signature
+ gr.io_signature(1, 1, item_size)) # Output signature
+
+
+ if taps is None:
+ taps = _generate_synthesis_taps(mpoints)
+
+ # pad taps to multiple of mpoints
+ r = len(taps) % mpoints
+ if r != 0:
+ taps = taps + (mpoints - r) * (0,)
+
+ # split in mpoints separate set of taps
+ sub_taps = _split_taps(taps, mpoints)
+
+ self.ss2v = gr.streams_to_vector(item_size, mpoints)
+ self.ifft = gr.fft_vcc(mpoints, False, [])
+ self.v2ss = gr.vector_to_streams(item_size, mpoints)
+ # mpoints filters go in here...
+ self.ss2s = gr.streams_to_stream(item_size, mpoints)
+
+ for i in range(mpoints):
+ self.connect((self, i), (self.ss2v, i))
+
+ self.connect(self.ss2v, self.ifft, self.v2ss, self)
+
+ # build mpoints fir filters...
+ for i in range(mpoints):
+ f = gr.fft_filter_ccc(1, sub_taps[i])
+ self.connect((self.v2ss, i), f)
+ self.connect(f, (self.ss2s, i))
+
+
+class analysis_filterbank(gr.hier_block2):
+ """
+ Uniformly modulated polyphase DFT filter bank: analysis
+
+ See http://cnx.rice.edu/content/m10424/latest
+ """
+ def __init__(self, mpoints, taps=None):
+ """
+ Takes 1 complex stream in, produces M complex streams out
+ that runs at 1/M times the input sample rate
+
+ @param mpoints: number of freq bins/interpolation factor/subbands
+ @param taps: filter taps for subband filter
+
+ Same channel to frequency mapping as described above.
+ """
+ item_size = gr.sizeof_gr_complex
+ gr.hier_block2.__init__(self, "analysis_filterbank",
+ gr.io_signature(1, 1, item_size), # Input signature
+ gr.io_signature(mpoints, mpoints, item_size)) # Output signature
+
+ if taps is None:
+ taps = _generate_synthesis_taps(mpoints)
+
+ # pad taps to multiple of mpoints
+ r = len(taps) % mpoints
+ if r != 0:
+ taps = taps + (mpoints - r) * (0,)
+
+ # split in mpoints separate set of taps
+ sub_taps = _split_taps(taps, mpoints)
+
+ # print >> sys.stderr, "mpoints =", mpoints, "len(sub_taps) =", len(sub_taps)
+
+ self.s2ss = gr.stream_to_streams(item_size, mpoints)
+ # filters here
+ self.ss2v = gr.streams_to_vector(item_size, mpoints)
+ self.fft = gr.fft_vcc(mpoints, True, [])
+ self.v2ss = gr.vector_to_streams(item_size, mpoints)
+
+ self.connect(self, self.s2ss)
+
+ # build mpoints fir filters...
+ for i in range(mpoints):
+ f = gr.fft_filter_ccc(1, sub_taps[mpoints-i-1])
+ self.connect((self.s2ss, i), f)
+ self.connect(f, (self.ss2v, i))
+ self.connect((self.v2ss, i), (self, i))
+
+ self.connect(self.ss2v, self.fft, self.v2ss)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/fm_demod.py b/gnuradio-core/src/python/gnuradio/blks2impl/fm_demod.py
new file mode 100644
index 0000000000..1910b50117
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/fm_demod.py
@@ -0,0 +1,111 @@
+#
+# Copyright 2006,2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, optfir
+from gnuradio.blks2impl.fm_emph import fm_deemph
+from math import pi
+
+class fm_demod_cf(gr.hier_block2):
+ """
+ Generalized FM demodulation block with deemphasis and audio
+ filtering.
+
+ This block demodulates a band-limited, complex down-converted FM
+ channel into the the original baseband signal, optionally applying
+ deemphasis. Low pass filtering is done on the resultant signal. It
+ produces an output float strem in the range of [-1.0, +1.0].
+
+ @param channel_rate: incoming sample rate of the FM baseband
+ @type sample_rate: integer
+ @param deviation: maximum FM deviation (default = 5000)
+ @type deviation: float
+ @param audio_decim: input to output decimation rate
+ @type audio_decim: integer
+ @param audio_pass: audio low pass filter passband frequency
+ @type audio_pass: float
+ @param audio_stop: audio low pass filter stop frequency
+ @type audio_stop: float
+ @param gain: gain applied to audio output (default = 1.0)
+ @type gain: float
+ @param tau: deemphasis time constant (default = 75e-6), specify 'None'
+ to prevent deemphasis
+ """
+ def __init__(self, channel_rate, audio_decim, deviation,
+ audio_pass, audio_stop, gain=1.0, tau=75e-6):
+ gr.hier_block2.__init__(self, "fm_demod_cf",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Output signature
+
+ k = channel_rate/(2*pi*deviation)
+ QUAD = gr.quadrature_demod_cf(k)
+
+ audio_taps = optfir.low_pass(gain, # Filter gain
+ channel_rate, # Sample rate
+ audio_pass, # Audio passband
+ audio_stop, # Audio stopband
+ 0.1, # Passband ripple
+ 60) # Stopband attenuation
+ LPF = gr.fir_filter_fff(audio_decim, audio_taps)
+
+ if tau is not None:
+ DEEMPH = fm_deemph(channel_rate, tau)
+ self.connect(self, QUAD, DEEMPH, LPF, self)
+ else:
+ self.connect(self, QUAD, LPF, self)
+
+class demod_20k0f3e_cf(fm_demod_cf):
+ """
+ NBFM demodulation block, 20 KHz channels
+
+ This block demodulates a complex, downconverted, narrowband FM
+ channel conforming to 20K0F3E emission standards, outputting
+ floats in the range [-1.0, +1.0].
+
+ @param sample_rate: incoming sample rate of the FM baseband
+ @type sample_rate: integer
+ @param audio_decim: input to output decimation rate
+ @type audio_decim: integer
+ """
+ def __init__(self, channel_rate, audio_decim):
+ fm_demod_cf.__init__(self, channel_rate, audio_decim,
+ 5000, # Deviation
+ 3000, # Audio passband frequency
+ 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 sample_rate: incoming sample rate of the FM baseband
+ @type sample_rate: integer
+ @param audio_decim: input to output decimation rate
+ @type audio_decim: integer
+ """
+ def __init__(self, channel_rate, audio_decim):
+ fm_demod_cf.__init__(self, channel_rate, audio_decim,
+ 75000, # Deviation
+ 15000, # Audio passband
+ 16000, # Audio stopband
+ 20.0) # Audio gain
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/fm_emph.py b/gnuradio-core/src/python/gnuradio/blks2impl/fm_emph.py
new file mode 100644
index 0000000000..fd19f5fd99
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/fm_emph.py
@@ -0,0 +1,151 @@
+#
+# Copyright 2005,2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr
+import math
+
+
+#
+# 1
+# H(s) = -------
+# 1 + s
+#
+# tau is the RC time constant.
+# critical frequency: w_p = 1/tau
+#
+# We prewarp and use the bilinear z-transform to get our IIR coefficients.
+# See "Digital Signal Processing: A Practical Approach" by Ifeachor and Jervis
+#
+
+class fm_deemph(gr.hier_block2):
+ """
+ FM Deemphasis IIR filter.
+ """
+
+
+ def __init__(self, fs, tau=75e-6):
+ """
+ @param fs: sampling frequency in Hz
+ @type fs: float
+ @param tau: Time constant in seconds (75us in US, 50us in EUR)
+ @type tau: float
+ """
+ gr.hier_block2.__init__(self, "fm_deemph",
+ gr.io_signature(1, 1, gr.sizeof_float), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Output signature
+
+ w_p = 1/tau
+ w_pp = math.tan (w_p / (fs * 2)) # prewarped analog freq
+
+ a1 = (w_pp - 1)/(w_pp + 1)
+ b0 = w_pp/(1 + w_pp)
+ b1 = b0
+
+ btaps = [b0, b1]
+ ataps = [1, a1]
+
+ if 0:
+ print "btaps =", btaps
+ print "ataps =", ataps
+ global plot1
+ plot1 = gru.gnuplot_freqz (gru.freqz (btaps, ataps), fs, True)
+
+ deemph = gr.iir_filter_ffd(btaps, ataps)
+ self.connect(self, deemph, self)
+
+#
+# 1 + s*t1
+# H(s) = ----------
+# 1 + s*t2
+#
+# I think this is the right transfer function.
+#
+#
+# This fine ASCII rendition is based on Figure 5-15
+# in "Digital and Analog Communication Systems", Leon W. Couch II
+#
+#
+# R1
+# +-----||------+
+# | |
+# o------+ +-----+--------o
+# | C1 | |
+# +----/\/\/\/--+ \
+# /
+# \ R2
+# /
+# \
+# |
+# o--------------------------+--------o
+#
+# f1 = 1/(2*pi*t1) = 1/(2*pi*R1*C)
+#
+# 1 R1 + R2
+# f2 = ------- = ------------
+# 2*pi*t2 2*pi*R1*R2*C
+#
+# t1 is 75us in US, 50us in EUR
+# f2 should be higher than our audio bandwidth.
+#
+#
+# The Bode plot looks like this:
+#
+#
+# /----------------
+# /
+# / <-- slope = 20dB/decade
+# /
+# -------------/
+# f1 f2
+#
+# We prewarp and use the bilinear z-transform to get our IIR coefficients.
+# See "Digital Signal Processing: A Practical Approach" by Ifeachor and Jervis
+#
+
+class fm_preemph(gr.hier_block2):
+ """
+ FM Preemphasis IIR filter.
+ """
+ def __init__(self, fs, tau=75e-6):
+ """
+ @param fs: sampling frequency in Hz
+ @type fs: float
+ @param tau: Time constant in seconds (75us in US, 50us in EUR)
+ @type tau: float
+ """
+
+ gr.hier_block2.__init__(self, "fm_deemph",
+ gr.io_signature(1, 1, gr.sizeof_float), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Output signature
+
+ # FIXME make this compute the right answer
+
+ btaps = [1]
+ ataps = [1]
+
+ if 0:
+ print "btaps =", btaps
+ print "ataps =", ataps
+ global plot2
+ plot2 = gru.gnuplot_freqz (gru.freqz (btaps, ataps), fs, True)
+
+ preemph = gr.iir_filter_ffd(btaps, ataps)
+ self.connect(self, preemph, self)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/gmsk.py b/gnuradio-core/src/python/gnuradio/blks2impl/gmsk.py
new file mode 100644
index 0000000000..3b6c016a0b
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/gmsk.py
@@ -0,0 +1,292 @@
+#
+# GMSK modulation and demodulation.
+#
+#
+# Copyright 2005,2006,2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+# See gnuradio-examples/python/digital for examples
+
+from gnuradio import gr
+from gnuradio import modulation_utils
+from math import pi
+import numpy
+from pprint import pprint
+import inspect
+
+# default values (used in __init__ and add_options)
+_def_samples_per_symbol = 2
+_def_bt = 0.35
+_def_verbose = False
+_def_log = False
+
+_def_gain_mu = None
+_def_mu = 0.5
+_def_freq_error = 0.0
+_def_omega_relative_limit = 0.005
+
+
+# /////////////////////////////////////////////////////////////////////////////
+# GMSK modulator
+# /////////////////////////////////////////////////////////////////////////////
+
+class gmsk_mod(gr.hier_block2):
+
+ def __init__(self,
+ samples_per_symbol=_def_samples_per_symbol,
+ bt=_def_bt,
+ verbose=_def_verbose,
+ log=_def_log):
+ """
+ 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 samples_per_symbol: samples per baud >= 2
+ @type samples_per_symbol: integer
+ @param bt: Gaussian filter bandwidth * symbol time
+ @type bt: float
+ @param verbose: Print information about modulator?
+ @type verbose: bool
+ @param debug: Print modualtion data to files?
+ @type debug: bool
+ """
+
+ gr.hier_block2.__init__(self, "gmsk_mod",
+ gr.io_signature(1, 1, gr.sizeof_char), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature
+
+ self._samples_per_symbol = samples_per_symbol
+ self._bt = bt
+
+ if not isinstance(samples_per_symbol, int) or samples_per_symbol < 2:
+ raise TypeError, ("samples_per_symbol must be an integer >= 2, is %r" % (samples_per_symbol,))
+
+ ntaps = 4 * samples_per_symbol # up to 3 bits in filter at once
+ sensitivity = (pi / 2) / samples_per_symbol # 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
+ samples_per_symbol, # symbol_rate
+ bt, # bandwidth * symbol time
+ ntaps # number of taps
+ )
+
+ self.sqwave = (1,) * samples_per_symbol # rectangular window
+ self.taps = numpy.convolve(numpy.array(self.gaussian_taps),numpy.array(self.sqwave))
+ self.gaussian_filter = gr.interp_fir_filter_fff(samples_per_symbol, self.taps)
+
+ # FM modulation
+ self.fmmod = gr.frequency_modulator_fc(sensitivity)
+
+ if verbose:
+ self._print_verbage()
+
+ if log:
+ self._setup_logging()
+
+ # Connect & Initialize base class
+ self.connect(self, self.nrz, self.gaussian_filter, self.fmmod, self)
+
+ def samples_per_symbol(self):
+ return self._samples_per_symbol
+
+ def bits_per_symbol(self=None): # staticmethod that's also callable on an instance
+ return 1
+ bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method.
+
+
+ def _print_verbage(self):
+ print "bits per symbol = %d" % self.bits_per_symbol()
+ print "Gaussian filter bt = %.2f" % self._bt
+
+
+ def _setup_logging(self):
+ print "Modulation logging turned on."
+ self.connect(self.nrz,
+ gr.file_sink(gr.sizeof_float, "nrz.dat"))
+ self.connect(self.gaussian_filter,
+ gr.file_sink(gr.sizeof_float, "gaussian_filter.dat"))
+ self.connect(self.fmmod,
+ gr.file_sink(gr.sizeof_gr_complex, "fmmod.dat"))
+
+
+ def add_options(parser):
+ """
+ Adds GMSK modulation-specific options to the standard parser
+ """
+ parser.add_option("", "--bt", type="float", default=_def_bt,
+ help="set bandwidth-time product [default=%default] (GMSK)")
+ add_options=staticmethod(add_options)
+
+
+ def extract_kwargs_from_options(options):
+ """
+ Given command line options, create dictionary suitable for passing to __init__
+ """
+ return modulation_utils.extract_kwargs_from_options(gmsk_mod.__init__,
+ ('self',), options)
+ extract_kwargs_from_options=staticmethod(extract_kwargs_from_options)
+
+
+
+# /////////////////////////////////////////////////////////////////////////////
+# GMSK demodulator
+# /////////////////////////////////////////////////////////////////////////////
+
+class gmsk_demod(gr.hier_block2):
+
+ def __init__(self,
+ samples_per_symbol=_def_samples_per_symbol,
+ gain_mu=_def_gain_mu,
+ mu=_def_mu,
+ omega_relative_limit=_def_omega_relative_limit,
+ freq_error=_def_freq_error,
+ verbose=_def_verbose,
+ log=_def_log):
+ """
+ Hierarchical block for Gaussian Minimum Shift Key (GMSK)
+ demodulation.
+
+ The input is the complex modulated signal at baseband.
+ The output is a stream of bits packed 1 bit per byte (the LSB)
+
+ @param samples_per_symbol: samples per baud
+ @type samples_per_symbol: integer
+ @param verbose: Print information about modulator?
+ @type verbose: bool
+ @param log: Print modualtion data to files?
+ @type log: bool
+
+ Clock recovery parameters. These all have reasonble defaults.
+
+ @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
+ """
+
+ gr.hier_block2.__init__(self, "gmsk_demod",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_char)) # Output signature
+
+ self._samples_per_symbol = samples_per_symbol
+ self._gain_mu = gain_mu
+ self._mu = mu
+ self._omega_relative_limit = omega_relative_limit
+ self._freq_error = freq_error
+
+ if samples_per_symbol < 2:
+ raise TypeError, "samples_per_symbol >= 2, is %f" % samples_per_symbol
+
+ self._omega = samples_per_symbol*(1+self._freq_error)
+
+ if not self._gain_mu:
+ self._gain_mu = 0.175
+
+ self._gain_omega = .25 * self._gain_mu * self._gain_mu # critically damped
+
+ # Demodulate FM
+ sensitivity = (pi / 2) / samples_per_symbol
+ self.fmdemod = gr.quadrature_demod_cf(1.0 / sensitivity)
+
+ # 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(self._omega, self._gain_omega,
+ self._mu, self._gain_mu,
+ self._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()
+
+ if verbose:
+ self._print_verbage()
+
+ if log:
+ self._setup_logging()
+
+ # Connect & Initialize base class
+ self.connect(self, self.fmdemod, self.clock_recovery, self.slicer, self)
+
+ def samples_per_symbol(self):
+ return self._samples_per_symbol
+
+ def bits_per_symbol(self=None): # staticmethod that's also callable on an instance
+ return 1
+ bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method.
+
+
+ def _print_verbage(self):
+ print "bits per symbol = %d" % self.bits_per_symbol()
+ print "M&M clock recovery omega = %f" % self._omega
+ print "M&M clock recovery gain mu = %f" % self._gain_mu
+ print "M&M clock recovery mu = %f" % self._mu
+ print "M&M clock recovery omega rel. limit = %f" % self._omega_relative_limit
+ print "frequency error = %f" % self._freq_error
+
+
+ def _setup_logging(self):
+ print "Demodulation logging turned on."
+ self.connect(self.fmdemod,
+ gr.file_sink(gr.sizeof_float, "fmdemod.dat"))
+ self.connect(self.clock_recovery,
+ gr.file_sink(gr.sizeof_float, "clock_recovery.dat"))
+ self.connect(self.slicer,
+ gr.file_sink(gr.sizeof_char, "slicer.dat"))
+
+ def add_options(parser):
+ """
+ Adds GMSK demodulation-specific options to the standard parser
+ """
+ parser.add_option("", "--gain-mu", type="float", default=_def_gain_mu,
+ help="M&M clock recovery gain mu [default=%default] (GMSK/PSK)")
+ parser.add_option("", "--mu", type="float", default=_def_mu,
+ help="M&M clock recovery mu [default=%default] (GMSK/PSK)")
+ parser.add_option("", "--omega-relative-limit", type="float", default=_def_omega_relative_limit,
+ help="M&M clock recovery omega relative limit [default=%default] (GMSK/PSK)")
+ parser.add_option("", "--freq-error", type="float", default=_def_freq_error,
+ help="M&M clock recovery frequency error [default=%default] (GMSK)")
+ add_options=staticmethod(add_options)
+
+ def extract_kwargs_from_options(options):
+ """
+ Given command line options, create dictionary suitable for passing to __init__
+ """
+ return modulation_utils.extract_kwargs_from_options(gmsk_demod.__init__,
+ ('self',), options)
+ extract_kwargs_from_options=staticmethod(extract_kwargs_from_options)
+
+
+#
+# Add these to the mod/demod registry
+#
+modulation_utils.add_type_1_mod('gmsk', gmsk_mod)
+modulation_utils.add_type_1_demod('gmsk', gmsk_demod)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/nbfm_rx.py b/gnuradio-core/src/python/gnuradio/blks2impl/nbfm_rx.py
new file mode 100644
index 0000000000..8a1704000a
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/nbfm_rx.py
@@ -0,0 +1,88 @@
+#
+# Copyright 2005 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import math
+from gnuradio import gr, optfir
+from gnuradio.blks2impl.fm_emph import fm_deemph
+#from gnuradio.blks2impl.standard_squelch import standard_squelch
+
+class nbfm_rx(gr.hier_block2):
+ def __init__(self, audio_rate, quad_rate, tau=75e-6, max_dev=5e3):
+ """
+ Narrow Band FM Receiver.
+
+ Takes a single complex baseband input stream and produces a single
+ float output stream of audio sample in the range [-1, +1].
+
+ @param audio_rate: sample rate of audio stream, >= 16k
+ @type audio_rate: integer
+ @param quad_rate: sample rate of output stream
+ @type quad_rate: integer
+ @param tau: preemphasis time constant (default 75e-6)
+ @type tau: float
+ @param max_dev: maximum deviation in Hz (default 5e3)
+ @type max_dev: float
+
+ quad_rate must be an integer multiple of audio_rate.
+
+ Exported sub-blocks (attributes):
+ squelch
+ quad_demod
+ deemph
+ audio_filter
+ """
+
+ gr.hier_block2.__init__(self, "nbfm_rx",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Output signature
+
+ # FIXME audio_rate and quad_rate ought to be exact rationals
+ audio_rate = int(audio_rate)
+ quad_rate = int(quad_rate)
+
+ if quad_rate % audio_rate != 0:
+ raise ValueError, "quad_rate is not an integer multiple of audio_rate"
+
+ squelch_threshold = 20 # dB
+ #self.squelch = gr.simple_squelch_cc(squelch_threshold, 0.001)
+
+ # FM Demodulator input: complex; output: float
+ k = quad_rate/(2*math.pi*max_dev)
+ self.quad_demod = gr.quadrature_demod_cf(k)
+
+ # FM Deemphasis IIR filter
+ self.deemph = fm_deemph (quad_rate, tau=tau)
+
+ # compute FIR taps for audio filter
+ audio_decim = quad_rate // audio_rate
+ audio_taps = gr.firdes.low_pass (1.0, # gain
+ quad_rate, # sampling rate
+ 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)
+
+ self.connect(self, self.quad_demod, self.deemph, self.audio_filter, self)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/nbfm_tx.py b/gnuradio-core/src/python/gnuradio/blks2impl/nbfm_tx.py
new file mode 100644
index 0000000000..15818c2043
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/nbfm_tx.py
@@ -0,0 +1,92 @@
+#
+# Copyright 2005 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import math
+from gnuradio import gr, optfir
+from gnuradio.blks2impl.fm_emph import fm_preemph
+
+#from gnuradio import ctcss
+
+class nbfm_tx(gr.hier_block2):
+ def __init__(self, audio_rate, quad_rate, tau=75e-6, max_dev=5e3):
+ """
+ Narrow Band FM Transmitter.
+
+ Takes a single float input stream of audio samples in the range [-1,+1]
+ and produces a single FM modulated complex baseband output.
+
+ @param audio_rate: sample rate of audio stream, >= 16k
+ @type audio_rate: integer
+ @param quad_rate: sample rate of output stream
+ @type quad_rate: integer
+ @param tau: preemphasis time constant (default 75e-6)
+ @type tau: float
+ @param max_dev: maximum deviation in Hz (default 5e3)
+ @type max_dev: float
+
+ quad_rate must be an integer multiple of audio_rate.
+ """
+
+ gr.hier_block2.__init__(self, "nbfm_tx",
+ gr.io_signature(1, 1, gr.sizeof_float), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature
+
+ # FIXME audio_rate and quad_rate ought to be exact rationals
+ audio_rate = int(audio_rate)
+ quad_rate = int(quad_rate)
+
+ if quad_rate % audio_rate != 0:
+ raise ValueError, "quad_rate is not an integer multiple of audio_rate"
+
+
+ do_interp = audio_rate != quad_rate
+
+ if do_interp:
+ interp_factor = quad_rate / audio_rate
+ interp_taps = optfir.low_pass (interp_factor, # gain
+ quad_rate, # Fs
+ 4500, # passband cutoff
+ 7000, # stopband cutoff
+ 0.1, # passband ripple dB
+ 40) # stopband atten dB
+
+ #print "len(interp_taps) =", len(interp_taps)
+ self.interpolator = gr.interp_fir_filter_fff (interp_factor, interp_taps)
+
+ self.preemph = fm_preemph (quad_rate, tau=tau)
+
+ k = 2 * math.pi * max_dev / quad_rate
+ self.modulator = gr.frequency_modulator_fc (k)
+
+ if do_interp:
+ self.connect (self, self.interpolator, self.preemph, self.modulator, self)
+ else:
+ self.connect(self, self.preemph, self.modulator, self)
+
+
+class ctcss_gen_f(gr.hier_block2):
+ def __init__(self, sample_rate, tone_freq):
+ gr.hier_block2.__init__(self, "ctcss_gen_f",
+ gr.io_signature(0, 0, 0), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Output signature
+
+ self.plgen = gr.sig_source_f(sample_rate, gr.GR_SIN_WAVE, tone_freq, 0.1, 0.0)
+ self.connect(self.plgen, self)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/ofdm.py b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm.py
new file mode 100644
index 0000000000..b040d8c7f4
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm.py
@@ -0,0 +1,305 @@
+
+#
+# Copyright 2006,2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import math
+from numpy import fft
+from gnuradio import gr, ofdm_packet_utils
+import gnuradio.gr.gr_threading as _threading
+import psk, qam
+
+from gnuradio.blksimpl.ofdm_receiver import ofdm_receiver
+
+
+# /////////////////////////////////////////////////////////////////////////////
+# mod/demod with packets as i/o
+# /////////////////////////////////////////////////////////////////////////////
+
+class ofdm_mod(gr.hier_block):
+ """
+ Modulates an OFDM stream. Based on the options fft_length, occupied_tones, and
+ cp_length, this block creates OFDM symbols using a specified modulation option.
+
+ Send packets by calling send_pkt
+ """
+ def __init__(self, fg, options, 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 options: pass modulation options from higher layers (fft length, occupied tones, etc.)
+ @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
+ """
+
+ self._pad_for_usrp = pad_for_usrp
+ self._modulation = options.modulation
+ self._fft_length = options.fft_length
+ self._occupied_tones = options.occupied_tones
+ self._cp_length = options.cp_length
+
+ win = [] #[1 for i in range(self._fft_length)]
+
+ # Use freq domain to get doubled-up known symbol for correlation in time domain
+ zeros_on_left = int(math.ceil((self._fft_length - self._occupied_tones)/2.0))
+ ksfreq = known_symbols_4512_3[0:self._occupied_tones]
+ for i in range(len(ksfreq)):
+ if((zeros_on_left + i) & 1):
+ ksfreq[i] = 0
+
+ # hard-coded known symbols
+ preambles = (ksfreq,
+ known_symbols_4512_1[0:self._occupied_tones],
+ known_symbols_4512_2[0:self._occupied_tones])
+
+ padded_preambles = list()
+ for pre in preambles:
+ padded = self._fft_length*[0,]
+ padded[zeros_on_left : zeros_on_left + self._occupied_tones] = pre
+ padded_preambles.append(padded)
+
+ symbol_length = options.fft_length + options.cp_length
+
+ mods = {"bpsk": 2, "qpsk": 4, "8psk": 8, "qam8": 8, "qam16": 16, "qam64": 64, "qam256": 256}
+ arity = mods[self._modulation]
+
+ rot = 1
+ if self._modulation == "qpsk":
+ rot = (0.707+0.707j)
+
+ if(self._modulation.find("psk") >= 0):
+ rotated_const = map(lambda pt: pt * rot, psk.gray_constellation[arity])
+ elif(self._modulation.find("qam") >= 0):
+ rotated_const = map(lambda pt: pt * rot, qam.constellation[arity])
+ #print rotated_const
+ self._pkt_input = gr.ofdm_mapper_bcv(rotated_const, msgq_limit,
+ options.occupied_tones, options.fft_length)
+
+ self.preambles = gr.ofdm_insert_preamble(self._fft_length, padded_preambles)
+ self.ifft = gr.fft_vcc(self._fft_length, False, win, True)
+ self.cp_adder = gr.ofdm_cyclic_prefixer(self._fft_length, symbol_length)
+ self.scale = gr.multiply_const_cc(1.0 / math.sqrt(self._fft_length))
+
+ fg.connect((self._pkt_input, 0), (self.preambles, 0))
+ fg.connect((self._pkt_input, 1), (self.preambles, 1))
+ fg.connect(self.preambles, self.ifft, self.cp_adder, self.scale)
+
+ if options.verbose:
+ self._print_verbage()
+
+ if options.log:
+ fg.connect(self._pkt_input, gr.file_sink(gr.sizeof_gr_complex*options.fft_length,
+ "ofdm_mapper_c.dat"))
+
+ gr.hier_block.__init__(self, fg, None, self.scale)
+
+ 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 = ofdm_packet_utils.make_packet(payload, 1, 1, self._pad_for_usrp, whitening=True)
+
+ #print "pkt =", string_to_hex_list(pkt)
+ msg = gr.message_from_string(pkt)
+ self._pkt_input.msgq().insert_tail(msg)
+
+ def add_options(normal, expert):
+ """
+ Adds OFDM-specific options to the Options Parser
+ """
+ normal.add_option("-m", "--modulation", type="string", default="bpsk",
+ help="set modulation type (bpsk or qpsk) [default=%default]")
+ expert.add_option("", "--fft-length", type="intx", default=512,
+ help="set the number of FFT bins [default=%default]")
+ expert.add_option("", "--occupied-tones", type="intx", default=200,
+ help="set the number of occupied FFT bins [default=%default]")
+ expert.add_option("", "--cp-length", type="intx", default=128,
+ help="set the number of bits in the cyclic prefix [default=%default]")
+ # Make a static method to call before instantiation
+ add_options = staticmethod(add_options)
+
+ def _print_verbage(self):
+ """
+ Prints information about the OFDM modulator
+ """
+ print "\nOFDM Modulator:"
+ print "Modulation Type: %s" % (self._modulation)
+ print "FFT length: %3d" % (self._fft_length)
+ print "Occupied Tones: %3d" % (self._occupied_tones)
+ print "CP length: %3d" % (self._cp_length)
+
+
+class ofdm_demod(gr.hier_block):
+ """
+ Demodulates a received OFDM stream. Based on the options fft_length, occupied_tones, and
+ cp_length, this block performs synchronization, FFT, and demodulation of incoming OFDM
+ symbols and passes packets up the a higher layer.
+
+ The input is complex baseband. When packets are demodulated, they are passed to the
+ app via the callback.
+ """
+
+ def __init__(self, fg, options, callback=None):
+ """
+ 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 options: pass modulation options from higher layers (fft length, occupied tones, etc.)
+ @param callback: function of two args: ok, payload
+ @type callback: ok: bool; payload: string
+ """
+ self._rcvd_pktq = gr.msg_queue() # holds packets from the PHY
+
+ self._modulation = options.modulation
+ self._fft_length = options.fft_length
+ self._occupied_tones = options.occupied_tones
+ self._cp_length = options.cp_length
+ self._snr = options.snr
+
+
+ # Use freq domain to get doubled-up known symbol for correlation in time domain
+ ksfreq = known_symbols_4512_3[0:self._occupied_tones]
+ for i in range(len(ksfreq)):
+ if(i&1):
+ ksfreq[i] = 0
+
+ zeros_on_left = int(math.ceil((self._fft_length - self._occupied_tones)/2.0))
+ zeros_on_right = self._fft_length - self._occupied_tones - zeros_on_left
+ ks0 = zeros_on_left*[0.0,]
+ ks0.extend(ksfreq)
+ ks0.extend(zeros_on_right*[0.0,])
+
+ ks0time = fft.ifft(ks0)
+ # ADD SCALING FACTOR
+ ks0time = ks0time.tolist()
+
+ # hard-coded known symbols
+ preambles = (ks0time,
+ known_symbols_4512_1[0:self._occupied_tones],
+ known_symbols_4512_2[0:self._occupied_tones])
+
+ symbol_length = self._fft_length + self._cp_length
+ self.ofdm_recv = ofdm_receiver(fg, self._fft_length, self._cp_length,
+ self._occupied_tones, self._snr, preambles,
+ options.log)
+
+ mods = {"bpsk": 2, "qpsk": 4, "8psk": 8, "qam8": 8, "qam16": 16, "qam64": 64, "qam256": 256}
+ arity = mods[self._modulation]
+
+ rot = 1
+ if self._modulation == "qpsk":
+ rot = (0.707+0.707j)
+
+ if(self._modulation.find("psk") >= 0):
+ rotated_const = map(lambda pt: pt * rot, psk.gray_constellation[arity])
+ elif(self._modulation.find("qam") >= 0):
+ rotated_const = map(lambda pt: pt * rot, qam.constellation[arity])
+ #print rotated_const
+ self.ofdm_demod = gr.ofdm_frame_sink(rotated_const, range(arity),
+ self._rcvd_pktq,
+ self._occupied_tones)
+
+ fg.connect((self.ofdm_recv, 0), (self.ofdm_demod, 0))
+ fg.connect((self.ofdm_recv, 1), (self.ofdm_demod, 1))
+
+ if options.verbose:
+ self._print_verbage()
+
+ gr.hier_block.__init__(self, fg, self.ofdm_recv, None)
+ self._watcher = _queue_watcher_thread(self._rcvd_pktq, callback)
+
+
+ def add_options(normal, expert):
+ """
+ Adds OFDM-specific options to the Options Parser
+ """
+ normal.add_option("-m", "--modulation", type="string", default="bpsk",
+ help="set modulation type (bpsk or qpsk) [default=%default]")
+ expert.add_option("", "--fft-length", type="intx", default=512,
+ help="set the number of FFT bins [default=%default]")
+ expert.add_option("", "--occupied-tones", type="intx", default=200,
+ help="set the number of occupied FFT bins [default=%default]")
+ expert.add_option("", "--cp-length", type="intx", default=128,
+ help="set the number of bits in the cyclic prefix [default=%default]")
+ # Make a static method to call before instantiation
+ add_options = staticmethod(add_options)
+
+ def _print_verbage(self):
+ """
+ Prints information about the OFDM demodulator
+ """
+ print "\nOFDM Demodulator:"
+ print "Modulation Type: %s" % (self._modulation)
+ print "FFT length: %3d" % (self._fft_length)
+ print "Occupied Tones: %3d" % (self._occupied_tones)
+ print "CP length: %3d" % (self._cp_length)
+
+
+
+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 = ofdm_packet_utils.unmake_packet(msg.to_string())
+ if self.callback:
+ self.callback(ok, payload)
+
+# Generating known symbols with:
+# i = [2*random.randint(0,1)-1 for i in range(4512)]
+
+known_symbols_200_1 = [1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0]
+
+known_symbols_200_2 = [-1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0]
+
+
+known_symbols_4512_1 = [-1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1]
+
+known_symbols_4512_2 = [1, 1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1]
+
+
+known_symbols_4512_impulse = 4512*[1,]
+
+known_symbols_4512_3 = [-1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1]
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_receiver.py b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_receiver.py
new file mode 100644
index 0000000000..d16d2e2940
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_receiver.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+#
+# Copyright 2006, 2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import math
+from gnuradio import gr
+from gnuradio.blksimpl.ofdm_sync_ml import ofdm_sync_ml
+from gnuradio.blksimpl.ofdm_sync_pn import ofdm_sync_pn
+from gnuradio.blksimpl.ofdm_sync_pnac import ofdm_sync_pnac
+
+class ofdm_receiver(gr.hier_block):
+ def __init__(self, fg, fft_length, cp_length, occupied_tones, snr, ks, logging=False):
+ self.fg = fg
+
+ bw = (float(occupied_tones) / float(fft_length)) / 2.0
+ tb = bw*0.08
+ chan_coeffs = gr.firdes.low_pass (1.0, # gain
+ 1.0, # sampling rate
+ bw+tb, # midpoint of trans. band
+ tb, # width of trans. band
+ gr.firdes.WIN_HAMMING) # filter type
+ self.chan_filt = gr.fft_filter_ccc(1, chan_coeffs)
+
+ win = [1 for i in range(fft_length)]
+
+ SYNC = "pn"
+ if SYNC == "ml":
+ self.ofdm_sync = ofdm_sync_ml(fg, fft_length, cp_length, snr, logging)
+ elif SYNC == "pn":
+ self.ofdm_sync = ofdm_sync_pn(fg, fft_length, cp_length, logging)
+ elif SYNC == "pnac":
+ self.ofdm_sync = ofdm_sync_pnac(fg, fft_length, cp_length, ks[0])
+
+ self.fft_demod = gr.fft_vcc(fft_length, True, win, True)
+ self.ofdm_corr = gr.ofdm_correlator(occupied_tones, fft_length,
+ cp_length, ks[1], ks[2])
+
+ self.fg.connect(self.chan_filt, self.ofdm_sync, self.fft_demod, self.ofdm_corr)
+
+ if logging:
+ self.fg.connect(self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "chan_filt_c.dat"))
+ self.fg.connect(self.fft_demod, gr.file_sink(gr.sizeof_gr_complex*fft_length, "fft_out_c.dat"))
+ self.fg.connect(self.ofdm_corr, gr.file_sink(gr.sizeof_gr_complex*occupied_tones, "ofdm_corr_out_c.dat"))
+ self.fg.connect((self.ofdm_corr,1), gr.file_sink(1, "found_corr_b.dat"))
+
+ gr.hier_block.__init__(self, fg, self.chan_filt, self.ofdm_corr)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_fixed.py b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_fixed.py
new file mode 100644
index 0000000000..b56f65660e
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_fixed.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import math
+from gnuradio import gr
+
+class ofdm_sync_fixed(gr.hier_block):
+ def __init__(self, fg, fft_length, cp_length, snr):
+ self.fg = fg
+
+ # Use a fixed trigger point instead of sync block
+ data = (fft_length+cp_len)*[0,]
+ data[(fft_length+cp_len)-1] = 1
+ peak_trigger = gr.vector_source_b(data, True)
+
+ self.fg.connect(peak_trigger, (self.sampler,1))
+
+ if 1:
+ self.fg.connect(self.sampler, gr.file_sink(gr.sizeof_gr_complex*fft_length,
+ "ofdm_sync_fixed-sampler_c.dat"))
+
+ gr.hier_block.__init__(self, fg, (self.sampler,0), self.sampler)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_ml.py b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_ml.py
new file mode 100644
index 0000000000..d58f56cffc
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_ml.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import math
+from gnuradio import gr
+
+class ofdm_sync_ml(gr.hier_block):
+ def __init__(self, fg, fft_length, cp_length, snr, logging):
+ ''' Maximum Likelihood OFDM synchronizer:
+ J. van de Beek, M. Sandell, and P. O. Borjesson, "ML Estimation
+ of Time and Frequency Offset in OFDM Systems," IEEE Trans.
+ Signal Processing, vol. 45, no. 7, pp. 1800-1805, 1997.
+ '''
+
+ self.fg = fg
+
+ # FIXME: when converting to hier_block2's, the output signature
+ # should be the output of the divider (the normalized peaks) and
+ # the angle value out of the sample and hold block
+
+ self.input = gr.add_const_cc(0)
+
+ SNR = 10.0**(snr/10.0)
+ rho = SNR / (SNR + 1.0)
+ symbol_length = fft_length + cp_length
+
+ # ML Sync
+
+ # Energy Detection from ML Sync
+
+ # Create a delay line
+ self.delay = gr.delay(gr.sizeof_gr_complex, fft_length)
+ self.fg.connect(self.input, self.delay)
+
+ # magnitude squared blocks
+ self.magsqrd1 = gr.complex_to_mag_squared()
+ self.magsqrd2 = gr.complex_to_mag_squared()
+ self.adder = gr.add_ff()
+
+ moving_sum_taps = [rho/2 for i in range(cp_length)]
+ self.moving_sum_filter = gr.fir_filter_fff(1,moving_sum_taps)
+
+ self.fg.connect(self.input,self.magsqrd1)
+ self.fg.connect(self.delay,self.magsqrd2)
+ self.fg.connect(self.magsqrd1,(self.adder,0))
+ self.fg.connect(self.magsqrd2,(self.adder,1))
+ self.fg.connect(self.adder,self.moving_sum_filter)
+
+
+ # Correlation from ML Sync
+ self.conjg = gr.conjugate_cc();
+ self.mixer = gr.multiply_cc();
+
+ movingsum2_taps = [1.0 for i in range(cp_length)]
+ self.movingsum2 = gr.fir_filter_ccf(1,movingsum2_taps)
+
+ # Correlator data handler
+ self.c2mag = gr.complex_to_mag()
+ self.angle = gr.complex_to_arg()
+ self.fg.connect(self.input,(self.mixer,1))
+ self.fg.connect(self.delay,self.conjg,(self.mixer,0))
+ self.fg.connect(self.mixer,self.movingsum2,self.c2mag)
+ self.fg.connect(self.movingsum2,self.angle)
+
+ # ML Sync output arg, need to find maximum point of this
+ self.diff = gr.sub_ff()
+ self.fg.connect(self.c2mag,(self.diff,0))
+ self.fg.connect(self.moving_sum_filter,(self.diff,1))
+
+ #ML measurements input to sampler block and detect
+ nco_sensitivity = -1.0/fft_length
+ self.f2c = gr.float_to_complex()
+ self.sampler = gr.ofdm_sampler(fft_length,symbol_length)
+ self.pk_detect = gr.peak_detector_fb(0.2, 0.25, 30, 0.0005)
+ self.sample_and_hold = gr.sample_and_hold_ff()
+ self.nco = gr.frequency_modulator_fc(nco_sensitivity)
+ self.sigmix = gr.multiply_cc()
+
+ # Mix the signal with an NCO controlled by the sync loop
+ self.fg.connect(self.input, (self.sigmix,0))
+ self.fg.connect(self.nco, (self.sigmix,1))
+ self.fg.connect(self.sigmix, (self.sampler,0))
+
+ # use the sync loop values to set the sampler and the NCO
+ # self.diff = theta
+ # self.angle = epsilon
+
+ self.fg.connect(self.diff, self.pk_detect)
+
+ use_dpll = 1
+ if use_dpll:
+ self.dpll = gr.dpll_bb(float(symbol_length),0.01)
+ self.fg.connect(self.pk_detect, self.dpll)
+ self.fg.connect(self.dpll, (self.sampler,1))
+ self.fg.connect(self.dpll, (self.sample_and_hold,1))
+ else:
+ self.fg.connect(self.pk_detect, (self.sampler,1))
+ self.fg.connect(self.pk_detect, (self.sample_and_hold,1))
+
+ self.fg.connect(self.angle, (self.sample_and_hold,0))
+ self.fg.connect(self.sample_and_hold, self.nco)
+
+ if logging:
+ self.fg.connect(self.diff, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-theta_f.dat"))
+ self.fg.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-epsilon_f.dat"))
+ self.fg.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_ml-peaks_b.dat"))
+ if use_dpll:
+ self.fg.connect(self.dpll, gr.file_sink(gr.sizeof_char, "ofdm_sync_ml-dpll_b.dat"))
+
+ self.fg.connect(self.sigmix, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-sigmix_c.dat"))
+ self.fg.connect(self.sampler, gr.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_sync_ml-sampler_c.dat"))
+ self.fg.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-sample_and_hold_f.dat"))
+ self.fg.connect(self.nco, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-nco_c.dat"))
+ self.fg.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-input_c.dat"))
+
+ gr.hier_block.__init__(self, fg, self.input, self.sampler)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_pn.py b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_pn.py
new file mode 100644
index 0000000000..56425868f9
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_pn.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import math
+from numpy import fft
+from gnuradio import gr
+
+class ofdm_sync_pn(gr.hier_block):
+ def __init__(self, fg, fft_length, cp_length, logging=False):
+ ''' OFDM synchronization using PN Correlation:
+ T. M. Schmidl and D. C. Cox, "Robust Frequency and Timing
+ Synchonization for OFDM," IEEE Trans. Communications, vol. 45,
+ no. 12, 1997.
+ '''
+
+ self.fg = fg
+
+ # FIXME: when converting to hier_block2's, the output signature
+ # should be the output of the divider (the normalized peaks) and
+ # the angle value out of the sample and hold block
+
+ self.input = gr.add_const_cc(0)
+
+ symbol_length = fft_length + cp_length
+
+ # PN Sync
+
+ # Create a delay line
+ self.delay = gr.delay(gr.sizeof_gr_complex, fft_length/2)
+
+ # Correlation from ML Sync
+ self.conjg = gr.conjugate_cc();
+ self.corr = gr.multiply_cc();
+
+ # Create a moving sum filter for the corr output
+ if 1:
+ moving_sum_taps = [1.0 for i in range(fft_length//2)]
+ self.moving_sum_filter = gr.fir_filter_ccf(1,moving_sum_taps)
+ else:
+ moving_sum_taps = [complex(1.0,0.0) for i in range(fft_length//2)]
+ self.moving_sum_filter = gr.fft_filter_ccc(1,moving_sum_taps)
+
+ # Create a moving sum filter for the input
+ self.inputmag2 = gr.complex_to_mag_squared()
+ movingsum2_taps = [1.0 for i in range(fft_length//2)]
+
+ if 1:
+ self.inputmovingsum = gr.fir_filter_fff(1,movingsum2_taps)
+ else:
+ self.inputmovingsum = gr.fft_filter_fff(1,movingsum2_taps)
+
+ self.square = gr.multiply_ff()
+ self.normalize = gr.divide_ff()
+
+ # Get magnitude (peaks) and angle (phase/freq error)
+ self.c2mag = gr.complex_to_mag_squared()
+ self.angle = gr.complex_to_arg()
+
+ self.sample_and_hold = gr.sample_and_hold_ff()
+
+ # Mix the signal with an NCO controlled by the sync loop
+ nco_sensitivity = -2.0/fft_length
+ self.nco = gr.frequency_modulator_fc(nco_sensitivity)
+ self.sigmix = gr.multiply_cc()
+
+ #ML measurements input to sampler block and detect
+ self.sub1 = gr.add_const_ff(-1)
+ self.pk_detect = gr.peak_detector_fb(0.2, 0.25, 30, 0.0005)
+ self.regen = gr.regenerate_bb(symbol_length)
+
+ self.sampler = gr.ofdm_sampler(fft_length,symbol_length)
+
+ self.fg.connect(self.input, self.delay)
+ self.fg.connect(self.input, (self.corr,0))
+ self.fg.connect(self.delay, self.conjg)
+ self.fg.connect(self.conjg, (self.corr,1))
+ self.fg.connect(self.corr, self.moving_sum_filter)
+ self.fg.connect(self.moving_sum_filter, self.c2mag)
+ self.fg.connect(self.moving_sum_filter, self.angle)
+ self.fg.connect(self.angle, (self.sample_and_hold,0))
+ self.fg.connect(self.sample_and_hold, self.nco)
+
+ self.fg.connect(self.input, (self.sigmix,0))
+ self.fg.connect(self.nco, (self.sigmix,1))
+ self.fg.connect(self.sigmix, (self.sampler,0))
+
+ self.fg.connect(self.input, self.inputmag2, self.inputmovingsum)
+ self.fg.connect(self.inputmovingsum, (self.square,0))
+ self.fg.connect(self.inputmovingsum, (self.square,1))
+ self.fg.connect(self.square, (self.normalize,1))
+ self.fg.connect(self.c2mag, (self.normalize,0))
+
+ # Create a moving sum filter for the corr output
+ matched_filter_taps = [1.0/cp_length for i in range(cp_length)]
+ self.matched_filter = gr.fir_filter_fff(1,matched_filter_taps)
+ self.fg.connect(self.normalize, self.matched_filter)
+
+ self.fg.connect(self.matched_filter, self.sub1, self.pk_detect)
+ self.fg.connect(self.pk_detect, self.regen)
+ self.fg.connect(self.regen, (self.sampler,1))
+ self.fg.connect(self.pk_detect, (self.sample_and_hold,1))
+
+
+ if logging:
+ self.fg.connect(self.matched_filter, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-mf_f.dat"))
+ self.fg.connect(self.normalize, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-theta_f.dat"))
+ self.fg.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-epsilon_f.dat"))
+ self.fg.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_pn-peaks_b.dat"))
+ self.fg.connect(self.regen, gr.file_sink(gr.sizeof_char, "ofdm_sync_pn-regen_b.dat"))
+ self.fg.connect(self.sigmix, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pn-sigmix_c.dat"))
+ self.fg.connect(self.sampler, gr.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_sync_pn-sampler_c.dat"))
+ self.fg.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-sample_and_hold_f.dat"))
+ self.fg.connect(self.nco, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pn-nco_c.dat"))
+ self.fg.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pn-input_c.dat"))
+
+ gr.hier_block.__init__(self, fg, self.input, self.sampler)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_pnac.py b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_pnac.py
new file mode 100644
index 0000000000..e3774e3410
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_pnac.py
@@ -0,0 +1,126 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import math
+from numpy import fft
+from gnuradio import gr
+
+class ofdm_sync_pnac(gr.hier_block):
+ def __init__(self, fg, fft_length, cp_length, ks):
+ self.fg = fg
+
+ # FIXME: when converting to hier_block2's, the output signature
+ # should be the output of the divider (the normalized peaks) and
+ # the angle value out of the sample and hold block
+
+ self.input = gr.add_const_cc(0)
+
+ symbol_length = fft_length + cp_length
+
+ # PN Sync
+
+ # autocorrelate with the known symbol
+ ks = ks[0:fft_length//2]
+ ks.reverse()
+ self.crosscorr_filter = gr.fir_filter_ccc(1, ks)
+ self.fg.connect(self.crosscorr_filter, gr.file_sink(gr.sizeof_gr_complex, "crosscorr.dat"))
+
+ # Create a delay line
+ self.delay = gr.delay(gr.sizeof_gr_complex, fft_length/2)
+
+ # Correlation from ML Sync
+ self.conjg = gr.conjugate_cc();
+ self.corr = gr.multiply_cc();
+
+ # Create a moving sum filter for the corr output
+ moving_sum_taps = [1.0 for i in range(fft_length//2)]
+ self.moving_sum_filter = gr.fir_filter_ccf(1,moving_sum_taps)
+
+ # Create a moving sum filter for the input
+ self.inputmag2 = gr.complex_to_mag_squared()
+ movingsum2_taps = [1.0 for i in range(fft_length/2)]
+ self.inputmovingsum = gr.fir_filter_fff(1,movingsum2_taps)
+ self.square = gr.multiply_ff()
+ self.normalize = gr.divide_ff()
+
+ # Get magnitude (peaks) and angle (phase/freq error)
+ self.c2mag = gr.complex_to_mag_squared()
+ self.angle = gr.complex_to_arg()
+
+ self.sample_and_hold = gr.sample_and_hold_ff()
+
+ # Mix the signal with an NCO controlled by the sync loop
+ nco_sensitivity = -1.0/fft_length
+ self.nco = gr.frequency_modulator_fc(nco_sensitivity)
+ self.sigmix = gr.multiply_cc()
+
+ #ML measurements input to sampler block and detect
+ self.sub1 = gr.add_const_ff(-1)
+ self.pk_detect = gr.peak_detector_fb(0.2, 0.25, 30, 0.0005)
+
+ self.sampler = gr.ofdm_sampler(fft_length,symbol_length)
+
+ self.fg.connect(self.input, self.crosscorr_filter)
+ self.fg.connect(self.crosscorr_filter, self.delay)
+ self.fg.connect(self.crosscorr_filter, (self.corr,0))
+ self.fg.connect(self.delay, self.conjg)
+ self.fg.connect(self.conjg, (self.corr,1))
+ self.fg.connect(self.corr, self.moving_sum_filter)
+ self.fg.connect(self.moving_sum_filter, self.c2mag)
+ self.fg.connect(self.moving_sum_filter, self.angle)
+ self.fg.connect(self.angle, (self.sample_and_hold,0))
+ self.fg.connect(self.sample_and_hold, self.nco)
+
+ self.fg.connect(self.input, (self.sigmix,0))
+ self.fg.connect(self.nco, (self.sigmix,1))
+ self.fg.connect(self.sigmix, (self.sampler,0))
+
+ self.fg.connect(self.input, self.inputmag2, self.inputmovingsum)
+ self.fg.connect(self.inputmovingsum, (self.square,0))
+ self.fg.connect(self.inputmovingsum, (self.square,1))
+ self.fg.connect(self.square, (self.normalize,1))
+ self.fg.connect(self.c2mag, (self.normalize,0))
+ self.fg.connect(self.normalize, self.sub1, self.pk_detect)
+
+ self.fg.connect(self.pk_detect, (self.sampler,1))
+ self.fg.connect(self.pk_detect, (self.sample_and_hold,1))
+
+
+ if 1:
+ self.fg.connect(self.normalize, gr.file_sink(gr.sizeof_float,
+ "ofdm_sync_pnac-theta_f.dat"))
+ self.fg.connect(self.angle, gr.file_sink(gr.sizeof_float,
+ "ofdm_sync_pnac-epsilon_f.dat"))
+ self.fg.connect(self.pk_detect, gr.file_sink(gr.sizeof_char,
+ "ofdm_sync_pnac-peaks_b.dat"))
+ self.fg.connect(self.sigmix, gr.file_sink(gr.sizeof_gr_complex,
+ "ofdm_sync_pnac-sigmix_c.dat"))
+ self.fg.connect(self.sampler, gr.file_sink(gr.sizeof_gr_complex*fft_length,
+ "ofdm_sync_pnac-sampler_c.dat"))
+ self.fg.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float,
+ "ofdm_sync_pnac-sample_and_hold_f.dat"))
+ self.fg.connect(self.nco, gr.file_sink(gr.sizeof_gr_complex,
+ "ofdm_sync_pnac-nco_c.dat"))
+ self.fg.connect(self.input, gr.file_sink(gr.sizeof_gr_complex,
+ "ofdm_sync_pnac-input_c.dat"))
+
+ gr.hier_block.__init__(self, fg, self.input, self.sampler)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/pkt.py b/gnuradio-core/src/python/gnuradio/blks2impl/pkt.py
new file mode 100644
index 0000000000..908437ef2b
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/pkt.py
@@ -0,0 +1,164 @@
+#
+# Copyright 2005, 2006, 2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from math import pi
+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_block2):
+ """
+ Wrap an arbitrary digital modulator in our packet handling framework.
+
+ Send packets by calling send_pkt
+ """
+ def __init__(self, modulator, access_code=None, msgq_limit=2, pad_for_usrp=True, use_whitener_offset=False):
+ """
+ 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 modulator: instance of modulator class (gr_block or hier_block2)
+ @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
+ @param use_whitener_offset: If true, start of whitener XOR string is incremented each packet
+
+ See gmsk_mod for remaining parameters
+ """
+
+ gr.hier_block2.__init__(self, "mod_pkts",
+ gr.io_signature(0, 0, 0), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature
+
+ self._modulator = modulator
+ self._pad_for_usrp = pad_for_usrp
+ self._use_whitener_offset = use_whitener_offset
+ self._whitener_offset = 0
+
+ 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.connect(self._pkt_input, self._modulator, self)
+
+ 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_symbol(),
+ self._modulator.bits_per_symbol(),
+ self._access_code,
+ self._pad_for_usrp,
+ self._whitener_offset)
+ #print "pkt =", string_to_hex_list(pkt)
+ msg = gr.message_from_string(pkt)
+ if self._use_whitener_offset is True:
+ self._whitener_offset = (self._whitener_offset + 1) % 16
+
+ self._pkt_input.msgq().insert_tail(msg)
+
+
+
+class demod_pkts(gr.hier_block2):
+ """
+ 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, 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 demodulator: instance of demodulator class (gr_block or hier_block2)
+ @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
+ """
+
+ gr.hier_block2.__init__(self, "demod_pkts",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(0, 0, 0)) # Output signature
+
+ 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)
+ self.connect(self, self._demodulator, self.correlator, self.framer_sink)
+
+ 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(), int(msg.arg1()))
+ if self.callback:
+ self.callback(ok, payload)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/psk.py b/gnuradio-core/src/python/gnuradio/blks2impl/psk.py
new file mode 100644
index 0000000000..acedf3b69a
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/psk.py
@@ -0,0 +1,94 @@
+#
+# Copyright 2005,2006 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from math import pi, sqrt, log10
+import math, cmath
+
+# The following algorithm generates Gray coded constellations for M-PSK for M=[2,4,8]
+def make_gray_constellation(m):
+ # number of bits/symbol (log2(M))
+ k = int(log10(m) / log10(2.0))
+
+ coeff = 1
+ const_map = []
+ bits = [0]*3
+ for i in range(m):
+ # get a vector of the k bits to use in this mapping
+ bits[3-k:3] = [((i&(0x01 << k-j-1)) >> k-j-1) for j in range(k)]
+
+ theta = -(2*bits[0]-1)*(2*pi/m)*(bits[0]+abs(bits[1]-bits[2])+2*bits[1])
+ re = math.cos(theta)
+ im = math.sin(theta)
+ const_map.append(complex(re, im)) # plug it into the constellation
+
+ # return the constellation; by default, it is normalized
+ return const_map
+
+# This makes a constellation that increments around the unit circle
+def make_constellation(m):
+ return [cmath.exp(i * 2 * pi / m * 1j) for i in range(m)]
+
+# Common definition of constellations for Tx and Rx
+constellation = {
+ 2 : make_constellation(2), # BPSK
+ 4 : make_constellation(4), # QPSK
+ 8 : make_constellation(8) # 8PSK
+ }
+
+gray_constellation = {
+ 2 : make_gray_constellation(2), # BPSK
+ 4 : make_gray_constellation(4), # QPSK
+ 8 : make_gray_constellation(8) # 8PSK
+ }
+
+# -----------------------
+# Do Gray code
+# -----------------------
+# binary to gray coding -- constellation does Gray coding
+binary_to_gray = {
+ 2 : range(2),
+ 4 : [0,1,3,2],
+ 8 : [0, 1, 3, 2, 7, 6, 4, 5]
+ }
+
+# gray to binary
+gray_to_binary = {
+ 2 : range(2),
+ 4 : [0,1,3,2],
+ 8 : [0, 1, 3, 2, 6, 7, 5, 4]
+ }
+
+# -----------------------
+# Don't Gray code
+# -----------------------
+# identity mapping
+binary_to_ungray = {
+ 2 : range(2),
+ 4 : range(4),
+ 8 : range(8)
+ }
+
+# identity mapping
+ungray_to_binary = {
+ 2 : range(2),
+ 4 : range(4),
+ 8 : range(8)
+ }
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/qam.py b/gnuradio-core/src/python/gnuradio/blks2impl/qam.py
new file mode 100644
index 0000000000..22b1e1dabb
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/qam.py
@@ -0,0 +1,113 @@
+#
+# Copyright 2005,2006 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from math import pi, sqrt
+import math
+
+# These constellations are generated for Gray coding when symbos [1, ..., m] are used
+# Mapping to Gray coding is therefore unnecessary
+
+def make_constellation(m):
+ # number of bits/symbol (log2(M))
+ k = int(math.log10(m) / math.log10(2.0))
+
+ coeff = 1
+ const_map = []
+ for i in range(m):
+ a = (i&(0x01 << k-1)) >> k-1
+ b = (i&(0x01 << k-2)) >> k-2
+ bits_i = [((i&(0x01 << k-j-1)) >> k-j-1) for j in range(2, k, 2)]
+ bits_q = [((i&(0x01 << k-j-1)) >> k-j-1) for j in range(3, k, 2)]
+
+ ss = 0
+ ll = len(bits_i)
+ for ii in range(ll):
+ rr = 0
+ for jj in range(ll-ii):
+ rr = abs(bits_i[jj] - rr)
+ ss += rr*pow(2.0, ii+1)
+ re = (2*a-1)*(ss+1)
+
+ ss = 0
+ ll = len(bits_q)
+ for ii in range(ll):
+ rr = 0
+ for jj in range(ll-ii):
+ rr = abs(bits_q[jj] - rr)
+ ss += rr*pow(2.0, ii+1)
+ im = (2*b-1)*(ss+1)
+
+ a = max(re, im)
+ if a > coeff:
+ coeff = a
+ const_map.append(complex(re, im))
+
+ norm_map = [complex(i.real/coeff, i.imag/coeff) for i in const_map]
+ return norm_map
+
+# Common definition of constellations for Tx and Rx
+constellation = {
+ 4 : make_constellation(4), # QAM4 (QPSK)
+ 8 : make_constellation(8), # QAM8
+ 16: make_constellation(16), # QAM16
+ 64: make_constellation(64), # QAM64
+ 256: make_constellation(256) # QAM256
+ }
+
+# -----------------------
+# Do Gray code
+# -----------------------
+# binary to gray coding
+binary_to_gray = {
+ 4 : range(4),
+ 8 : range(8),
+ 16: range(16),
+ 64: range(64),
+ 256: range(256)
+ }
+
+# gray to binary
+gray_to_binary = {
+ 4 : range(4),
+ 8 : range(8),
+ 16: range(16),
+ 64: range(64),
+ 256: range(256)
+ }
+
+# -----------------------
+# Don't Gray code
+# -----------------------
+# identity mapping
+binary_to_ungray = {
+ 4 : range(4),
+ 8 : range(8),
+ 16: range(16),
+ 64: range(64)
+ }
+
+# identity mapping
+ungray_to_binary = {
+ 4 : range(4),
+ 8 : range(8),
+ 16: range(16),
+ 64: range(64)
+ }
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/qam16.py b/gnuradio-core/src/python/gnuradio/blks2impl/qam16.py
new file mode 100644
index 0000000000..0bdb9c6fb8
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/qam16.py
@@ -0,0 +1,208 @@
+#
+# Copyright 2005,2006,2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+# See gnuradio-examples/python/digital for examples
+
+"""
+QAM16 modulation and demodulation.
+"""
+
+from gnuradio import gr, gru, modulation_utils
+from math import pi, sqrt
+import qam
+import cmath
+from pprint import pprint
+
+# default values (used in __init__ and add_options)
+_def_samples_per_symbol = 2
+_def_excess_bw = 0.35
+_def_gray_code = True
+_def_verbose = False
+_def_log = False
+
+_def_costas_alpha = None
+_def_gain_mu = 0.03
+_def_mu = 0.05
+_def_omega_relative_limit = 0.005
+
+
+# /////////////////////////////////////////////////////////////////////////////
+# QAM16 modulator
+# /////////////////////////////////////////////////////////////////////////////
+
+class qam16_mod(gr.hier_block2):
+
+ def __init__(self,
+ samples_per_symbol=_def_samples_per_symbol,
+ excess_bw=_def_excess_bw,
+ gray_code=_def_gray_code,
+ verbose=_def_verbose,
+ log=_def_log):
+
+ """
+ Hierarchical block for RRC-filtered QPSK modulation.
+
+ The input is a byte stream (unsigned char) and the
+ output is the complex modulated signal at baseband.
+
+ @param samples_per_symbol: samples per symbol >= 2
+ @type samples_per_symbol: integer
+ @param excess_bw: Root-raised cosine filter excess bandwidth
+ @type excess_bw: float
+ @param gray_code: Tell modulator to Gray code the bits
+ @type gray_code: bool
+ @param verbose: Print information about modulator?
+ @type verbose: bool
+ @param debug: Print modualtion data to files?
+ @type debug: bool
+ """
+
+ gr.hier_block2.__init__(self, "qam16_mod",
+ gr.io_signature(1, 1, gr.sizeof_char), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature
+
+ self._samples_per_symbol = samples_per_symbol
+ self._excess_bw = excess_bw
+ self._gray_code = gray_code
+
+ if not isinstance(samples_per_symbol, int) or samples_per_symbol < 2:
+ raise TypeError, ("sbp must be an integer >= 2, is %d" % samples_per_symbol)
+
+ ntaps = 11 * samples_per_symbol
+
+ arity = pow(2, self.bits_per_symbol())
+
+ # turn bytes into k-bit vectors
+ self.bytes2chunks = \
+ gr.packed_to_unpacked_bb(self.bits_per_symbol(), gr.GR_MSB_FIRST)
+
+ if self._gray_code:
+ self.symbol_mapper = gr.map_bb(qam.binary_to_gray[arity])
+ else:
+ self.symbol_mapper = gr.map_bb(qam.binary_to_ungray[arity])
+
+ self.diffenc = gr.diff_encoder_bb(arity)
+
+ rot = 1.0
+ print "constellation with %d arity" % arity
+ rotated_const = map(lambda pt: pt * rot, qam.constellation[arity])
+ self.chunks2symbols = gr.chunks_to_symbols_bc(rotated_const)
+
+ # pulse shaping filter
+ self.rrc_taps = gr.firdes.root_raised_cosine(
+ self._samples_per_symbol, # gain (sps since we're interpolating by sps)
+ self._samples_per_symbol, # sampling rate
+ 1.0, # symbol rate
+ self._excess_bw, # excess bandwidth (roll-off factor)
+ ntaps)
+
+ self.rrc_filter = gr.interp_fir_filter_ccf(self._samples_per_symbol, self.rrc_taps)
+
+ if verbose:
+ self._print_verbage()
+
+ if log:
+ self._setup_logging()
+
+ # Connect
+ self.connect(self, self.bytes2chunks, self.symbol_mapper, self.diffenc,
+ self.chunks2symbols, self.rrc_filter, self)
+
+ def samples_per_symbol(self):
+ return self._samples_per_symbol
+
+ def bits_per_symbol(self=None): # staticmethod that's also callable on an instance
+ return 4
+ bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM
+
+ def _print_verbage(self):
+ print "bits per symbol = %d" % self.bits_per_symbol()
+ print "Gray code = %s" % self._gray_code
+ print "RRS roll-off factor = %f" % self._excess_bw
+
+ def _setup_logging(self):
+ print "Modulation logging turned on."
+ self.connect(self.bytes2chunks,
+ gr.file_sink(gr.sizeof_char, "bytes2chunks.dat"))
+ self.connect(self.symbol_mapper,
+ gr.file_sink(gr.sizeof_char, "graycoder.dat"))
+ self.connect(self.diffenc,
+ gr.file_sink(gr.sizeof_char, "diffenc.dat"))
+ self.connect(self.chunks2symbols,
+ gr.file_sink(gr.sizeof_gr_complex, "chunks2symbols.dat"))
+ self.connect(self.rrc_filter,
+ gr.file_sink(gr.sizeof_gr_complex, "rrc_filter.dat"))
+
+ def add_options(parser):
+ """
+ Adds QAM modulation-specific options to the standard parser
+ """
+ parser.add_option("", "--excess-bw", type="float", default=_def_excess_bw,
+ help="set RRC excess bandwith factor [default=%default] (PSK)")
+ parser.add_option("", "--no-gray-code", dest="gray_code",
+ action="store_false", default=_def_gray_code,
+ help="disable gray coding on modulated bits (PSK)")
+ add_options=staticmethod(add_options)
+
+
+ def extract_kwargs_from_options(options):
+ """
+ Given command line options, create dictionary suitable for passing to __init__
+ """
+ return modulation_utils.extract_kwargs_from_options(qam16_mod.__init__,
+ ('self',), options)
+ extract_kwargs_from_options=staticmethod(extract_kwargs_from_options)
+
+
+# /////////////////////////////////////////////////////////////////////////////
+# QAM16 demodulator
+#
+# /////////////////////////////////////////////////////////////////////////////
+
+class qam16_demod(gr.hier_block2):
+
+ def __init__(self,
+ samples_per_symbol=_def_samples_per_symbol,
+ excess_bw=_def_excess_bw,
+ costas_alpha=_def_costas_alpha,
+ gain_mu=_def_gain_mu,
+ mu=_def_mu,
+ omega_relative_limit=_def_omega_relative_limit,
+ gray_code=_def_gray_code,
+ verbose=_def_verbose,
+ log=_def_log):
+
+ gr.hier_block2.__init__(self, "qam16_demod",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_char)) # Output signature
+ # do this
+ pass
+
+ def bits_per_symbol(self=None): # staticmethod that's also callable on an instance
+ return 4
+ bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM
+
+#
+# Add these to the mod/demod registry
+#
+# NOT READY TO BE USED YET -- ENABLE AT YOUR OWN RISK
+#modulation_utils.add_type_1_mod('qam16', qam16_mod)
+#modulation_utils.add_type_1_demod('qam16', qam16_demod)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/qam256.py b/gnuradio-core/src/python/gnuradio/blks2impl/qam256.py
new file mode 100644
index 0000000000..fc455f17c1
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/qam256.py
@@ -0,0 +1,209 @@
+#
+# Copyright 2005,2006,2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+# See gnuradio-examples/python/digital for examples
+
+"""
+QAM256 modulation and demodulation.
+"""
+
+from gnuradio import gr, gru, modulation_utils
+from math import pi, sqrt
+import qam
+import cmath
+from pprint import pprint
+
+# default values (used in __init__ and add_options)
+_def_samples_per_symbol = 2
+_def_excess_bw = 0.35
+_def_gray_code = True
+_def_verbose = False
+_def_log = False
+
+_def_costas_alpha = None
+_def_gain_mu = 0.03
+_def_mu = 0.05
+_def_omega_relative_limit = 0.005
+
+
+# /////////////////////////////////////////////////////////////////////////////
+# QAM256 modulator
+# /////////////////////////////////////////////////////////////////////////////
+
+class qam256_mod(gr.hier_block2):
+
+ def __init__(self,
+ samples_per_symbol=_def_samples_per_symbol,
+ excess_bw=_def_excess_bw,
+ gray_code=_def_gray_code,
+ verbose=_def_verbose,
+ log=_def_log):
+
+ """
+ Hierarchical block for RRC-filtered QPSK modulation.
+
+ The input is a byte stream (unsigned char) and the
+ output is the complex modulated signal at baseband.
+
+ @param samples_per_symbol: samples per symbol >= 2
+ @type samples_per_symbol: integer
+ @param excess_bw: Root-raised cosine filter excess bandwidth
+ @type excess_bw: float
+ @param gray_code: Tell modulator to Gray code the bits
+ @type gray_code: bool
+ @param verbose: Print information about modulator?
+ @type verbose: bool
+ @param debug: Print modualtion data to files?
+ @type debug: bool
+ """
+
+ gr.hier_block2.__init__(self, "qam256_mod",
+ gr.io_signature(1, 1, gr.sizeof_char), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature
+
+ self._samples_per_symbol = samples_per_symbol
+ self._excess_bw = excess_bw
+ self._gray_code = gray_code
+
+ if not isinstance(samples_per_symbol, int) or samples_per_symbol < 2:
+ raise TypeError, ("sbp must be an integer >= 2, is %d" % samples_per_symbol)
+
+ ntaps = 11 * samples_per_symbol
+
+ arity = pow(2, self.bits_per_symbol())
+
+ # turn bytes into k-bit vectors
+ self.bytes2chunks = \
+ gr.packed_to_unpacked_bb(self.bits_per_symbol(), gr.GR_MSB_FIRST)
+
+ if self._gray_code:
+ self.symbol_mapper = gr.map_bb(qam.binary_to_gray[arity])
+ else:
+ self.symbol_mapper = gr.map_bb(qam.binary_to_ungray[arity])
+
+ self.diffenc = gr.diff_encoder_bb(arity)
+
+ rot = 1.0
+ print "constellation with %d arity" % arity
+ rotated_const = map(lambda pt: pt * rot, qam.constellation[arity])
+ self.chunks2symbols = gr.chunks_to_symbols_bc(rotated_const)
+
+ # pulse shaping filter
+ self.rrc_taps = gr.firdes.root_raised_cosine(
+ self._samples_per_symbol, # gain (sps since we're interpolating by sps)
+ self._samples_per_symbol, # sampling rate
+ 1.0, # symbol rate
+ self._excess_bw, # excess bandwidth (roll-off factor)
+ ntaps)
+
+ self.rrc_filter = gr.interp_fir_filter_ccf(self._samples_per_symbol, self.rrc_taps)
+
+ if verbose:
+ self._print_verbage()
+
+ if log:
+ self._setup_logging()
+
+ # Connect
+ self.connect(self, self.bytes2chunks, self.symbol_mapper, self.diffenc,
+ self.chunks2symbols, self.rrc_filter, self)
+
+ def samples_per_symbol(self):
+ return self._samples_per_symbol
+
+ def bits_per_symbol(self=None): # staticmethod that's also callable on an instance
+ return 8
+ bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM
+
+ def _print_verbage(self):
+ print "bits per symbol = %d" % self.bits_per_symbol()
+ print "Gray code = %s" % self._gray_code
+ print "RRS roll-off factor = %f" % self._excess_bw
+
+ def _setup_logging(self):
+ print "Modulation logging turned on."
+ self.connect(self.bytes2chunks,
+ gr.file_sink(gr.sizeof_char, "bytes2chunks.dat"))
+ self.connect(self.symbol_mapper,
+ gr.file_sink(gr.sizeof_char, "graycoder.dat"))
+ self.connect(self.diffenc,
+ gr.file_sink(gr.sizeof_char, "diffenc.dat"))
+ self.connect(self.chunks2symbols,
+ gr.file_sink(gr.sizeof_gr_complex, "chunks2symbols.dat"))
+ self.connect(self.rrc_filter,
+ gr.file_sink(gr.sizeof_gr_complex, "rrc_filter.dat"))
+
+ def add_options(parser):
+ """
+ Adds QAM modulation-specific options to the standard parser
+ """
+ parser.add_option("", "--excess-bw", type="float", default=_def_excess_bw,
+ help="set RRC excess bandwith factor [default=%default] (PSK)")
+ parser.add_option("", "--no-gray-code", dest="gray_code",
+ action="store_false", default=_def_gray_code,
+ help="disable gray coding on modulated bits (PSK)")
+ add_options=staticmethod(add_options)
+
+
+ def extract_kwargs_from_options(options):
+ """
+ Given command line options, create dictionary suitable for passing to __init__
+ """
+ return modulation_utils.extract_kwargs_from_options(qam256_mod.__init__,
+ ('self',), options)
+ extract_kwargs_from_options=staticmethod(extract_kwargs_from_options)
+
+
+# /////////////////////////////////////////////////////////////////////////////
+# QAM256 demodulator
+#
+# /////////////////////////////////////////////////////////////////////////////
+
+class qam256_demod(gr.hier_block2):
+
+ def __init__(self,
+ samples_per_symbol=_def_samples_per_symbol,
+ excess_bw=_def_excess_bw,
+ costas_alpha=_def_costas_alpha,
+ gain_mu=_def_gain_mu,
+ mu=_def_mu,
+ omega_relative_limit=_def_omega_relative_limit,
+ gray_code=_def_gray_code,
+ verbose=_def_verbose,
+ log=_def_log):
+
+ gr.hier_block2.__init__(self, "qam256_demod",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_char)) # Output signature
+
+ # do this
+ pass
+
+ def bits_per_symbol(self=None): # staticmethod that's also callable on an instance
+ return 8
+ bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM
+
+#
+# Add these to the mod/demod registry
+#
+# NOT READY TO BE USED YET -- ENABLE AT YOUR OWN RISK
+#modulation_utils.add_type_1_mod('qam256', qam256_mod)
+#modulation_utils.add_type_1_demod('qam256', qam256_demod)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/qam64.py b/gnuradio-core/src/python/gnuradio/blks2impl/qam64.py
new file mode 100644
index 0000000000..5509f37458
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/qam64.py
@@ -0,0 +1,208 @@
+#
+# Copyright 2005,2006,2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+# See gnuradio-examples/python/digital for examples
+
+"""
+differential QPSK modulation and demodulation.
+"""
+
+from gnuradio import gr, gru, modulation_utils
+from math import pi, sqrt
+import qam
+import cmath
+from pprint import pprint
+
+# default values (used in __init__ and add_options)
+_def_samples_per_symbol = 2
+_def_excess_bw = 0.35
+_def_gray_code = True
+_def_verbose = False
+_def_log = False
+
+_def_costas_alpha = None
+_def_gain_mu = 0.03
+_def_mu = 0.05
+_def_omega_relative_limit = 0.005
+
+
+# /////////////////////////////////////////////////////////////////////////////
+# QAM64 modulator
+# /////////////////////////////////////////////////////////////////////////////
+
+class qam64_mod(gr.hier_block2):
+
+ def __init__(self,
+ samples_per_symbol=_def_samples_per_symbol,
+ excess_bw=_def_excess_bw,
+ gray_code=_def_gray_code,
+ verbose=_def_verbose,
+ log=_def_log):
+
+ """
+ Hierarchical block for RRC-filtered QPSK modulation.
+
+ The input is a byte stream (unsigned char) and the
+ output is the complex modulated signal at baseband.
+
+ @param samples_per_symbol: samples per symbol >= 2
+ @type samples_per_symbol: integer
+ @param excess_bw: Root-raised cosine filter excess bandwidth
+ @type excess_bw: float
+ @param gray_code: Tell modulator to Gray code the bits
+ @type gray_code: bool
+ @param verbose: Print information about modulator?
+ @type verbose: bool
+ @param debug: Print modualtion data to files?
+ @type debug: bool
+ """
+
+ gr.hier_block2.__init__(self, "qam64_mod",
+ gr.io_signature(1, 1, gr.sizeof_char), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature
+ self._samples_per_symbol = samples_per_symbol
+ self._excess_bw = excess_bw
+ self._gray_code = gray_code
+
+ if not isinstance(samples_per_symbol, int) or samples_per_symbol < 2:
+ raise TypeError, ("sbp must be an integer >= 2, is %d" % samples_per_symbol)
+
+ ntaps = 11 * samples_per_symbol
+
+ arity = pow(2, self.bits_per_symbol())
+
+ # turn bytes into k-bit vectors
+ self.bytes2chunks = \
+ gr.packed_to_unpacked_bb(self.bits_per_symbol(), gr.GR_MSB_FIRST)
+
+ if self._gray_code:
+ self.symbol_mapper = gr.map_bb(qam.binary_to_gray[arity])
+ else:
+ self.symbol_mapper = gr.map_bb(qam.binary_to_ungray[arity])
+
+ self.diffenc = gr.diff_encoder_bb(arity)
+
+ rot = 1.0
+ print "constellation with %d arity" % arity
+ rotated_const = map(lambda pt: pt * rot, qam.constellation[arity])
+ self.chunks2symbols = gr.chunks_to_symbols_bc(rotated_const)
+
+ # pulse shaping filter
+ self.rrc_taps = gr.firdes.root_raised_cosine(
+ self._samples_per_symbol, # gain (sps since we're interpolating by sps)
+ self._samples_per_symbol, # sampling rate
+ 1.0, # symbol rate
+ self._excess_bw, # excess bandwidth (roll-off factor)
+ ntaps)
+
+ self.rrc_filter = gr.interp_fir_filter_ccf(self._samples_per_symbol, self.rrc_taps)
+
+ if verbose:
+ self._print_verbage()
+
+ if log:
+ self._setup_logging()
+
+ # Connect
+ self.connect(self, self.bytes2chunks, self.symbol_mapper, self.diffenc,
+ self.chunks2symbols, self.rrc_filter, self)
+
+ def samples_per_symbol(self):
+ return self._samples_per_symbol
+
+ def bits_per_symbol(self=None): # staticmethod that's also callable on an instance
+ return 6
+ bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM
+
+ def _print_verbage(self):
+ print "bits per symbol = %d" % self.bits_per_symbol()
+ print "Gray code = %s" % self._gray_code
+ print "RRS roll-off factor = %f" % self._excess_bw
+
+ def _setup_logging(self):
+ print "Modulation logging turned on."
+ self.connect(self.bytes2chunks,
+ gr.file_sink(gr.sizeof_char, "bytes2chunks.dat"))
+ self.connect(self.symbol_mapper,
+ gr.file_sink(gr.sizeof_char, "graycoder.dat"))
+ self.connect(self.diffenc,
+ gr.file_sink(gr.sizeof_char, "diffenc.dat"))
+ self.connect(self.chunks2symbols,
+ gr.file_sink(gr.sizeof_gr_complex, "chunks2symbols.dat"))
+ self.connect(self.rrc_filter,
+ gr.file_sink(gr.sizeof_gr_complex, "rrc_filter.dat"))
+
+ def add_options(parser):
+ """
+ Adds QAM modulation-specific options to the standard parser
+ """
+ parser.add_option("", "--excess-bw", type="float", default=_def_excess_bw,
+ help="set RRC excess bandwith factor [default=%default] (PSK)")
+ parser.add_option("", "--no-gray-code", dest="gray_code",
+ action="store_false", default=_def_gray_code,
+ help="disable gray coding on modulated bits (PSK)")
+ add_options=staticmethod(add_options)
+
+
+ def extract_kwargs_from_options(options):
+ """
+ Given command line options, create dictionary suitable for passing to __init__
+ """
+ return modulation_utils.extract_kwargs_from_options(qam64_mod.__init__,
+ ('self',), options)
+ extract_kwargs_from_options=staticmethod(extract_kwargs_from_options)
+
+
+# /////////////////////////////////////////////////////////////////////////////
+# QAM16 demodulator
+#
+# /////////////////////////////////////////////////////////////////////////////
+
+class qam64_demod(gr.hier_block2):
+
+ def __init__(self,
+ samples_per_symbol=_def_samples_per_symbol,
+ excess_bw=_def_excess_bw,
+ costas_alpha=_def_costas_alpha,
+ gain_mu=_def_gain_mu,
+ mu=_def_mu,
+ omega_relative_limit=_def_omega_relative_limit,
+ gray_code=_def_gray_code,
+ verbose=_def_verbose,
+ log=_def_log):
+
+ gr.hier_block2.__init__(self, "qam64_demod",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_char)) # Output signature
+
+ # do this
+ pass
+
+ def bits_per_symbol(self=None): # staticmethod that's also callable on an instance
+ return 6
+ bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM
+
+#
+# Add these to the mod/demod registry
+#
+# NOT READY TO BE USED YET -- ENABLE AT YOUR OWN RISK
+#modulation_utils.add_type_1_mod('qam64', qam64_mod)
+#modulation_utils.add_type_1_demod('qam16', qam16_demod)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/qam8.py b/gnuradio-core/src/python/gnuradio/blks2impl/qam8.py
new file mode 100644
index 0000000000..977dec930a
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/qam8.py
@@ -0,0 +1,209 @@
+#
+# Copyright 2005,2006,2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+# See gnuradio-examples/python/digital for examples
+
+"""
+QAM8 modulation and demodulation.
+"""
+
+from gnuradio import gr, gru, modulation_utils
+from math import pi, sqrt
+import qam
+import cmath
+from pprint import pprint
+
+# default values (used in __init__ and add_options)
+_def_samples_per_symbol = 2
+_def_excess_bw = 0.35
+_def_gray_code = True
+_def_verbose = False
+_def_log = False
+
+_def_costas_alpha = None
+_def_gain_mu = 0.03
+_def_mu = 0.05
+_def_omega_relative_limit = 0.005
+
+
+# /////////////////////////////////////////////////////////////////////////////
+# QAM8 modulator
+# /////////////////////////////////////////////////////////////////////////////
+
+class qam8_mod(gr.hier_block2):
+
+ def __init__(self,
+ samples_per_symbol=_def_samples_per_symbol,
+ excess_bw=_def_excess_bw,
+ gray_code=_def_gray_code,
+ verbose=_def_verbose,
+ log=_def_log):
+
+ """
+ Hierarchical block for RRC-filtered QPSK modulation.
+
+ The input is a byte stream (unsigned char) and the
+ output is the complex modulated signal at baseband.
+
+ @param samples_per_symbol: samples per symbol >= 2
+ @type samples_per_symbol: integer
+ @param excess_bw: Root-raised cosine filter excess bandwidth
+ @type excess_bw: float
+ @param gray_code: Tell modulator to Gray code the bits
+ @type gray_code: bool
+ @param verbose: Print information about modulator?
+ @type verbose: bool
+ @param debug: Print modualtion data to files?
+ @type debug: bool
+ """
+
+ gr.hier_block2.__init__(self, "qam8_mod",
+ gr.io_signature(1, 1, gr.sizeof_char), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature
+
+ self._samples_per_symbol = samples_per_symbol
+ self._excess_bw = excess_bw
+ self._gray_code = gray_code
+
+ if not isinstance(samples_per_symbol, int) or samples_per_symbol < 2:
+ raise TypeError, ("sbp must be an integer >= 2, is %d" % samples_per_symbol)
+
+ ntaps = 11 * samples_per_symbol
+
+ arity = pow(2, self.bits_per_symbol())
+
+ # turn bytes into k-bit vectors
+ self.bytes2chunks = \
+ gr.packed_to_unpacked_bb(self.bits_per_symbol(), gr.GR_MSB_FIRST)
+
+ if self._gray_code:
+ self.symbol_mapper = gr.map_bb(qam.binary_to_gray[arity])
+ else:
+ self.symbol_mapper = gr.map_bb(qam.binary_to_ungray[arity])
+
+ self.diffenc = gr.diff_encoder_bb(arity)
+
+ rot = 1.0
+ print "constellation with %d arity" % arity
+ rotated_const = map(lambda pt: pt * rot, qam.constellation[arity])
+ self.chunks2symbols = gr.chunks_to_symbols_bc(rotated_const)
+
+ # pulse shaping filter
+ self.rrc_taps = gr.firdes.root_raised_cosine(
+ self._samples_per_symbol, # gain (sps since we're interpolating by sps)
+ self._samples_per_symbol, # sampling rate
+ 1.0, # symbol rate
+ self._excess_bw, # excess bandwidth (roll-off factor)
+ ntaps)
+
+ self.rrc_filter = gr.interp_fir_filter_ccf(self._samples_per_symbol, self.rrc_taps)
+
+ if verbose:
+ self._print_verbage()
+
+ if log:
+ self._setup_logging()
+
+ # Connect
+ self.connect(self, self.bytes2chunks, self.symbol_mapper, self.diffenc,
+ self.chunks2symbols, self.rrc_filter, self)
+
+ def samples_per_symbol(self):
+ return self._samples_per_symbol
+
+ def bits_per_symbol(self=None): # staticmethod that's also callable on an instance
+ return 3
+ bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM
+
+ def _print_verbage(self):
+ print "bits per symbol = %d" % self.bits_per_symbol()
+ print "Gray code = %s" % self._gray_code
+ print "RRS roll-off factor = %f" % self._excess_bw
+
+ def _setup_logging(self):
+ print "Modulation logging turned on."
+ self.connect(self.bytes2chunks,
+ gr.file_sink(gr.sizeof_char, "bytes2chunks.dat"))
+ self.connect(self.symbol_mapper,
+ gr.file_sink(gr.sizeof_char, "graycoder.dat"))
+ self.connect(self.diffenc,
+ gr.file_sink(gr.sizeof_char, "diffenc.dat"))
+ self.connect(self.chunks2symbols,
+ gr.file_sink(gr.sizeof_gr_complex, "chunks2symbols.dat"))
+ self.connect(self.rrc_filter,
+ gr.file_sink(gr.sizeof_gr_complex, "rrc_filter.dat"))
+
+ def add_options(parser):
+ """
+ Adds QAM modulation-specific options to the standard parser
+ """
+ parser.add_option("", "--excess-bw", type="float", default=_def_excess_bw,
+ help="set RRC excess bandwith factor [default=%default] (PSK)")
+ parser.add_option("", "--no-gray-code", dest="gray_code",
+ action="store_false", default=_def_gray_code,
+ help="disable gray coding on modulated bits (PSK)")
+ add_options=staticmethod(add_options)
+
+
+ def extract_kwargs_from_options(options):
+ """
+ Given command line options, create dictionary suitable for passing to __init__
+ """
+ return modulation_utils.extract_kwargs_from_options(qam8_mod.__init__,
+ ('self',), options)
+ extract_kwargs_from_options=staticmethod(extract_kwargs_from_options)
+
+
+# /////////////////////////////////////////////////////////////////////////////
+# QAM8 demodulator
+#
+# /////////////////////////////////////////////////////////////////////////////
+
+class qam8_demod(gr.hier_block2):
+
+ def __init__(self,
+ samples_per_symbol=_def_samples_per_symbol,
+ excess_bw=_def_excess_bw,
+ costas_alpha=_def_costas_alpha,
+ gain_mu=_def_gain_mu,
+ mu=_def_mu,
+ omega_relative_limit=_def_omega_relative_limit,
+ gray_code=_def_gray_code,
+ verbose=_def_verbose,
+ log=_def_log):
+
+ gr.hier_block2.__init__(self, "qam8_demod",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_gr_char)) # Output signature
+
+ # do this
+ pass
+
+ def bits_per_symbol(self=None): # staticmethod that's also callable on an instance
+ return 3
+ bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM
+
+#
+# Add these to the mod/demod registry
+#
+# NOT READY TO BE USED YET -- ENABLE AT YOUR OWN RISK
+modulation_utils.add_type_1_mod('qam8', qam8_mod)
+#modulation_utils.add_type_1_demod('qam8', qam8_demod)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/rational_resampler.py b/gnuradio-core/src/python/gnuradio/blks2impl/rational_resampler.py
new file mode 100644
index 0000000000..b7de0de7cf
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/rational_resampler.py
@@ -0,0 +1,131 @@
+#
+# Copyright 2005,2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gru
+
+_plot = None
+
+def design_filter(interpolation, decimation, fractional_bw):
+ """
+ Given the interpolation rate, decimation rate and a fractional bandwidth,
+ design a set of taps.
+
+ @param interpolation: interpolation factor
+ @type interpolation: integer > 0
+ @param decimation: decimation factor
+ @type decimation: integer > 0
+ @param fractional_bw: fractional bandwidth in (0, 0.5) 0.4 works well.
+ @type fractional_bw: float
+ @returns: sequence of numbers
+ """
+
+ if fractional_bw >= 0.5 or fractional_bw <= 0:
+ raise ValueError, "Invalid fractional_bandwidth, must be in (0, 0.5)"
+
+ beta = 5.0
+ trans_width = 0.5 - fractional_bw
+ mid_transition_band = 0.5 - trans_width/2
+
+ taps = gr.firdes.low_pass(interpolation, # gain
+ 1, # Fs
+ mid_transition_band/interpolation, # trans mid point
+ trans_width/interpolation, # transition width
+ gr.firdes.WIN_KAISER,
+ beta # beta
+ )
+
+ return taps
+
+
+
+class _rational_resampler_base(gr.hier_block2):
+ """
+ base class for all rational resampler variants.
+ """
+ def __init__(self, resampler_base,
+ interpolation, decimation, taps=None, fractional_bw=None):
+ """
+ Rational resampling polyphase FIR filter.
+
+ Either taps or fractional_bw may be specified, but not both.
+ If neither is specified, a reasonable default, 0.4, is used as
+ the fractional_bw.
+
+ @param interpolation: interpolation factor
+ @type interpolation: integer > 0
+ @param decimation: decimation factor
+ @type decimation: integer > 0
+ @param taps: optional filter coefficients
+ @type taps: sequence
+ @param fractional_bw: fractional bandwidth in (0, 0.5), measured at final freq (use 0.4)
+ @type fractional_bw: float
+ """
+
+ if not isinstance(interpolation, int) or interpolation < 1:
+ raise ValueError, "interpolation must be an integer >= 1"
+
+ if not isinstance(decimation, int) or decimation < 1:
+ raise ValueError, "decimation must be an integer >= 1"
+
+ if taps is None and fractional_bw is None:
+ fractional_bw = 0.4
+
+ d = gru.gcd(interpolation, decimation)
+ interpolation = interpolation // d
+ decimation = decimation // d
+
+ if taps is None:
+ taps = design_filter(interpolation, decimation, fractional_bw)
+
+ resampler = resampler_base(interpolation, decimation, taps)
+ gr.hier_block2.__init__(self, "rational_resampler",
+ gr.io_signature(1, 1, resampler.input_signature().sizeof_stream_item(0)),
+ gr.io_signature(1, 1, resampler.output_signature().sizeof_stream_item(0)))
+
+ self.connect(self, resampler, self)
+
+
+class rational_resampler_fff(_rational_resampler_base):
+ def __init__(self, interpolation, decimation, taps=None, fractional_bw=None):
+ """
+ Rational resampling polyphase FIR filter with
+ float input, float output and float taps.
+ """
+ _rational_resampler_base.__init__(self, gr.rational_resampler_base_fff,
+ interpolation, decimation, taps, fractional_bw)
+
+class rational_resampler_ccf(_rational_resampler_base):
+ def __init__(self, interpolation, decimation, taps=None, fractional_bw=None):
+ """
+ Rational resampling polyphase FIR filter with
+ complex input, complex output and float taps.
+ """
+ _rational_resampler_base.__init__(self, gr.rational_resampler_base_ccf,
+ interpolation, decimation, taps, fractional_bw)
+
+class rational_resampler_ccc(_rational_resampler_base):
+ def __init__(self, interpolation, decimation, taps=None, fractional_bw=None):
+ """
+ Rational resampling polyphase FIR filter with
+ complex input, complex output and complex taps.
+ """
+ _rational_resampler_base.__init__(self, gr.rational_resampler_base_ccc,
+ interpolation, decimation, taps, fractional_bw)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/standard_squelch.py b/gnuradio-core/src/python/gnuradio/blks2impl/standard_squelch.py
new file mode 100644
index 0000000000..c5fdc01dbc
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/standard_squelch.py
@@ -0,0 +1,76 @@
+#
+# Copyright 2005,2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import math
+from gnuradio import gr, optfir
+
+class standard_squelch(gr.hier_block2):
+ def __init__(self, audio_rate):
+ gr.hier_block2.__init__(self, "standard_squelch",
+ gr.io_signature(1, 1, gr.sizeof_float), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Output signature
+
+ self.input_node = gr.add_const_ff(0) # FIXME kludge
+
+ self.low_iir = gr.iir_filter_ffd((0.0193,0,-0.0193),(1,1.9524,-0.9615))
+ self.low_square = gr.multiply_ff()
+ self.low_smooth = gr.single_pole_iir_filter_ff(1/(0.01*audio_rate)) # 100ms time constant
+
+ self.hi_iir = gr.iir_filter_ffd((0.0193,0,-0.0193),(1,1.3597,-0.9615))
+ self.hi_square = gr.multiply_ff()
+ self.hi_smooth = gr.single_pole_iir_filter_ff(1/(0.01*audio_rate))
+
+ self.sub = gr.sub_ff();
+ self.add = gr.add_ff();
+ self.gate = gr.threshold_ff(0.3,0.43,0)
+ self.squelch_lpf = gr.single_pole_iir_filter_ff(1/(0.01*audio_rate))
+
+ self.div = gr.divide_ff()
+ self.squelch_mult = gr.multiply_ff()
+
+ self.connect (self, self.input_node)
+ self.connect (self.input_node, (self.squelch_mult, 0))
+
+ self.connect (self.input_node,self.low_iir)
+ self.connect (self.low_iir,(self.low_square,0))
+ self.connect (self.low_iir,(self.low_square,1))
+ self.connect (self.low_square,self.low_smooth,(self.sub,0))
+ self.connect (self.low_smooth, (self.add,0))
+
+ self.connect (self.input_node,self.hi_iir)
+ self.connect (self.hi_iir,(self.hi_square,0))
+ self.connect (self.hi_iir,(self.hi_square,1))
+ self.connect (self.hi_square,self.hi_smooth,(self.sub,1))
+ self.connect (self.hi_smooth, (self.add,1))
+
+ self.connect (self.sub, (self.div, 0))
+ self.connect (self.add, (self.div, 1))
+ self.connect (self.div, self.gate, self.squelch_lpf, (self.squelch_mult,1))
+ self.connect (self.squelch_mult, self)
+
+ def set_threshold(self, threshold):
+ self.gate.set_hi(threshold)
+
+ def threshold(self):
+ return self.gate.hi()
+
+ def squelch_range(self):
+ return (0.0, 1.0, 1.0/100)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/wfm_rcv.py b/gnuradio-core/src/python/gnuradio/blks2impl/wfm_rcv.py
new file mode 100644
index 0000000000..3bdb22cce9
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/wfm_rcv.py
@@ -0,0 +1,69 @@
+#
+# Copyright 2005,2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr
+from gnuradio.blks2impl.fm_emph import fm_deemph
+import math
+
+class wfm_rcv(gr.hier_block2):
+ def __init__ (self, quad_rate, audio_decimation):
+ """
+ Hierarchical block for demodulating a broadcast FM signal.
+
+ The input is the downconverted complex baseband signal (gr_complex).
+ The output is the demodulated audio (float).
+
+ @param quad_rate: input sample rate of complex baseband input.
+ @type quad_rate: float
+ @param audio_decimation: how much to decimate quad_rate to get to audio.
+ @type audio_decimation: integer
+ """
+ gr.hier_block2.__init__(self, "wfm_rcv",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Output signature
+
+ volume = 20.
+
+ max_dev = 75e3
+ fm_demod_gain = quad_rate/(2*math.pi*max_dev)
+ audio_rate = quad_rate / audio_decimation
+
+
+ # We assign to self so that outsiders can grab the demodulator
+ # if they need to. E.g., to plot its output.
+ #
+ # input: complex; output: float
+ self.fm_demod = gr.quadrature_demod_cf (fm_demod_gain)
+
+ # input: float; output: float
+ self.deemph = fm_deemph (audio_rate)
+
+ # compute FIR filter taps for audio filter
+ width_of_transition_band = audio_rate / 32
+ audio_coeffs = gr.firdes.low_pass (1.0, # gain
+ quad_rate, # sampling rate
+ audio_rate/2 - width_of_transition_band,
+ width_of_transition_band,
+ gr.firdes.WIN_HAMMING)
+ # input: float; output: float
+ self.audio_filter = gr.fir_filter_fff (audio_decimation, audio_coeffs)
+
+ self.connect (self, self.fm_demod, self.audio_filter, self.deemph, self)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/wfm_rcv_pll.py b/gnuradio-core/src/python/gnuradio/blks2impl/wfm_rcv_pll.py
new file mode 100644
index 0000000000..d7a930754b
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/wfm_rcv_pll.py
@@ -0,0 +1,192 @@
+#
+# Copyright 2005,2006 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr
+from gnuradio.blks2impl.fm_emph import fm_deemph
+import math
+
+class wfm_rcv_pll(gr.hier_block2):
+ def __init__ (self, demod_rate, audio_decimation):
+ """
+ Hierarchical block for demodulating a broadcast FM signal.
+
+ The input is the downconverted complex baseband signal (gr_complex).
+ The output is two streams of the demodulated audio (float) 0=Left, 1=Right.
+
+ @param demod_rate: input sample rate of complex baseband input.
+ @type demod_rate: float
+ @param audio_decimation: how much to decimate demod_rate to get to audio.
+ @type audio_decimation: integer
+ """
+ gr.hier_block2.__init__(self, "wfm_rcv_pll",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 2, gr.sizeof_float)) # Output signature
+ 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 (audio_rate)
+ self.deemph_Right = fm_deemph (audio_rate)
+
+ # compute FIR filter taps for audio filter
+ width_of_transition_band = audio_rate / 32
+ audio_coeffs = gr.firdes.low_pass (1.0 , # gain
+ demod_rate, # sampling rate
+ 15000 ,
+ width_of_transition_band,
+ gr.firdes.WIN_HAMMING)
+ # input: float; output: float
+ self.audio_filter = gr.fir_filter_fff (audio_decimation, audio_coeffs)
+ if 1:
+ # Pick off the stereo carrier/2 with this filter. It attenuated 10 dB so apply 10 dB gain
+ # We pick off the negative frequency half because we want to base band by it!
+ ## NOTE THIS WAS HACKED TO OFFSET INSERTION LOSS DUE TO DEEMPHASIS
+
+ stereo_carrier_filter_coeffs = gr.firdes.complex_band_pass(10.0,
+ demod_rate,
+ -19020,
+ -18980,
+ width_of_transition_band,
+ gr.firdes.WIN_HAMMING)
+
+ #print "len stereo carrier filter = ",len(stereo_carrier_filter_coeffs)
+ #print "stereo carrier filter ", stereo_carrier_filter_coeffs
+ #print "width of transition band = ",width_of_transition_band, " audio rate = ", audio_rate
+
+ # Pick off the double side band suppressed carrier Left-Right audio. It is attenuated 10 dB so apply 10 dB gain
+
+ stereo_dsbsc_filter_coeffs = gr.firdes.complex_band_pass(20.0,
+ demod_rate,
+ 38000-15000/2,
+ 38000+15000/2,
+ width_of_transition_band,
+ gr.firdes.WIN_HAMMING)
+ #print "len stereo dsbsc filter = ",len(stereo_dsbsc_filter_coeffs)
+ #print "stereo dsbsc filter ", stereo_dsbsc_filter_coeffs
+ # construct overlap add filter system from coefficients for stereo carrier
+
+ self.stereo_carrier_filter = gr.fir_filter_fcc(audio_decimation, stereo_carrier_filter_coeffs)
+
+ # carrier is twice the picked off carrier so arrange to do a commplex multiply
+
+ self.stereo_carrier_generator = gr.multiply_cc();
+
+ # Pick off the rds signal
+
+ stereo_rds_filter_coeffs = gr.firdes.complex_band_pass(30.0,
+ demod_rate,
+ 57000 - 1500,
+ 57000 + 1500,
+ width_of_transition_band,
+ gr.firdes.WIN_HAMMING)
+ #print "len stereo dsbsc filter = ",len(stereo_dsbsc_filter_coeffs)
+ #print "stereo dsbsc filter ", stereo_dsbsc_filter_coeffs
+ # construct overlap add filter system from coefficients for stereo carrier
+
+ self.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_refout_cc(alpha,beta,max_freq,min_freq);
+ #self.stereo_carrier_pll_recovery.squelch_enable(False) #pll_refout does not have squelch yet, so disabled for now
+
+
+ # set up mixer (multiplier) to get the L-R signal at baseband
+
+ self.stereo_basebander = gr.multiply_cc();
+
+ # pick off the real component of the basebanded L-R signal. The imaginary SHOULD be zero
+
+ self.LmR_real = gr.complex_to_real();
+ self.Make_Left = gr.add_ff();
+ self.Make_Right = gr.sub_ff();
+
+ self.stereo_dsbsc_filter = gr.fir_filter_fcc(audio_decimation, stereo_dsbsc_filter_coeffs)
+
+
+ if 1:
+
+ # send the real signal to complex filter to pick off the carrier and then to one side of a multiplier
+ self.connect (self, self.fm_demod,self.stereo_carrier_filter,self.stereo_carrier_pll_recovery, (self.stereo_carrier_generator,0))
+ # send the already filtered carrier to the otherside of the carrier
+ self.connect (self.stereo_carrier_pll_recovery, (self.stereo_carrier_generator,1))
+ # the resulting signal from this multiplier is the carrier with correct phase but at -38000 Hz.
+
+ # send the new carrier to one side of the mixer (multiplier)
+ self.connect (self.stereo_carrier_generator, (self.stereo_basebander,0))
+ # send the demphasized audio to the DSBSC pick off filter, the complex
+ # DSBSC signal at +38000 Hz is sent to the other side of the mixer/multiplier
+ self.connect (self.fm_demod,self.stereo_dsbsc_filter, (self.stereo_basebander,1))
+ # the result is BASEBANDED DSBSC with phase zero!
+
+ # Pick off the real part since the imaginary is theoretically zero and then to one side of a summer
+ self.connect (self.stereo_basebander, self.LmR_real, (self.Make_Left,0))
+ #take the same real part of the DSBSC baseband signal and send it to negative side of a subtracter
+ self.connect (self.LmR_real,(self.Make_Right,1))
+
+ # Make rds carrier by taking the squared pilot tone and multiplying by pilot tone
+ self.connect (self.stereo_basebander,(self.rds_carrier_generator,0))
+ self.connect (self.stereo_carrier_pll_recovery,(self.rds_carrier_generator,1))
+ # take signal, filter off rds, send into mixer 0 channel
+ self.connect (self.fm_demod,self.rds_signal_filter,(self.rds_signal_generator,0))
+ # take rds_carrier_generator output and send into mixer 1 channel
+ self.connect (self.rds_carrier_generator,(self.rds_signal_generator,1))
+ # send basebanded rds signal and send into "processor" which for now is a null sink
+ self.connect (self.rds_signal_generator,self_rds_signal_processor)
+
+
+ if 1:
+ # pick off the audio, L+R that is what we used to have and send it to the summer
+ self.connect(self.fm_demod, self.audio_filter, (self.Make_Left, 1))
+ # take the picked off L+R audio and send it to the PLUS side of the subtractor
+ self.connect(self.audio_filter,(self.Make_Right, 0))
+ # The result of Make_Left gets (L+R) + (L-R) and results in 2*L
+ # The result of Make_Right gets (L+R) - (L-R) and results in 2*R
+ self.connect(self.Make_Left , self.deemph_Left, (self, 0))
+ self.connect(self.Make_Right, self.deemph_Right, (self, 1))
+ else:
+ self.connect (self.fm_demod, self.audio_filter, self)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/wfm_tx.py b/gnuradio-core/src/python/gnuradio/blks2impl/wfm_tx.py
new file mode 100644
index 0000000000..c7c831ca16
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/wfm_tx.py
@@ -0,0 +1,79 @@
+#
+# Copyright 2005,2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import math
+from gnuradio import gr, optfir
+from gnuradio.blks2impl.fm_emph import fm_preemph
+
+class wfm_tx(gr.hier_block2):
+ def __init__(self, audio_rate, quad_rate, tau=75e-6, max_dev=75e3):
+ """
+ Wide Band FM Transmitter.
+
+ Takes a single float input stream of audio samples in the range [-1,+1]
+ and produces a single FM modulated complex baseband output.
+
+ @param audio_rate: sample rate of audio stream, >= 16k
+ @type audio_rate: integer
+ @param quad_rate: sample rate of output stream
+ @type quad_rate: integer
+ @param tau: preemphasis time constant (default 75e-6)
+ @type tau: float
+ @param max_dev: maximum deviation in Hz (default 75e3)
+ @type max_dev: float
+
+ quad_rate must be an integer multiple of audio_rate.
+ """
+ gr.hier_block2.__init__(self, "wfm_tx",
+ gr.io_signature(1, 1, gr.sizeof_float), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature
+
+ # FIXME audio_rate and quad_rate ought to be exact rationals
+ audio_rate = int(audio_rate)
+ quad_rate = int(quad_rate)
+
+ if quad_rate % audio_rate != 0:
+ raise ValueError, "quad_rate is not an integer multiple of audio_rate"
+
+
+ do_interp = audio_rate != quad_rate
+
+ if do_interp:
+ interp_factor = quad_rate / audio_rate
+ interp_taps = optfir.low_pass (interp_factor, # gain
+ quad_rate, # Fs
+ 16000, # passband cutoff
+ 18000, # stopband cutoff
+ 0.1, # passband ripple dB
+ 40) # stopband atten dB
+
+ print "len(interp_taps) =", len(interp_taps)
+ self.interpolator = gr.interp_fir_filter_fff (interp_factor, interp_taps)
+
+ self.preemph = fm_preemph (quad_rate, tau=tau)
+
+ k = 2 * math.pi * max_dev / quad_rate
+ self.modulator = gr.frequency_modulator_fc (k)
+
+ if do_interp:
+ self.connect (self, self.interpolator, self.preemph, self.modulator, self)
+ else:
+ self.connect(self, self.preemph, self.modulator, self)