summaryrefslogtreecommitdiff
path: root/gnuradio-core/src/python
diff options
context:
space:
mode:
Diffstat (limited to 'gnuradio-core/src/python')
-rw-r--r--gnuradio-core/src/python/gnuradio/Makefile.am2
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am8
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/bpsk.py98
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/generic_mod_demod.py395
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/ofdm.py75
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/pkt.py36
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/psk2.py121
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/qam.py289
-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/qpsk.py79
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/Makefile.am2
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/qa_constellation.py203
-rw-r--r--gnuradio-core/src/python/gnuradio/gr/qa_constellation_receiver.py132
-rwxr-xr-xgnuradio-core/src/python/gnuradio/gr/qa_int_to_float.py49
-rw-r--r--gnuradio-core/src/python/gnuradio/gr_xmlrunner.py12
-rw-r--r--gnuradio-core/src/python/gnuradio/modulation_utils2.py20
-rw-r--r--gnuradio-core/src/python/gnuradio/utils/Makefile.am35
-rw-r--r--gnuradio-core/src/python/gnuradio/utils/__init__.py21
-rw-r--r--gnuradio-core/src/python/gnuradio/utils/alignment.py139
-rw-r--r--gnuradio-core/src/python/gnuradio/utils/gray_code.py66
-rw-r--r--gnuradio-core/src/python/gnuradio/utils/mod_codes.py33
-rw-r--r--gnuradio-core/src/python/gnuradio/utils/run_tests.in11
25 files changed, 1677 insertions, 983 deletions
diff --git a/gnuradio-core/src/python/gnuradio/Makefile.am b/gnuradio-core/src/python/gnuradio/Makefile.am
index eff35e95c..1bec7dd19 100644
--- a/gnuradio-core/src/python/gnuradio/Makefile.am
+++ b/gnuradio-core/src/python/gnuradio/Makefile.am
@@ -22,7 +22,7 @@
include $(top_srcdir)/Makefile.common
if PYTHON
-SUBDIRS = gr gru gruimpl blks2 blks2impl vocoder
+SUBDIRS = gr gru gruimpl blks2 blks2impl vocoder utils
grpython_PYTHON = \
__init__.py \
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am b/gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am
index 7b24fb69d..6a2e7d5f7 100644
--- a/gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am
@@ -29,6 +29,7 @@ grblkspythondir = $(grpythondir)/blks2impl
grblkspython_PYTHON = \
__init__.py \
am_demod.py \
+ bpsk.py \
channel_model.py \
dbpsk.py \
dbpsk2.py \
@@ -38,6 +39,7 @@ grblkspython_PYTHON = \
filterbank.py \
fm_demod.py \
fm_emph.py \
+ generic_mod_demod.py \
generic_usrp.py \
gmsk.py \
cpm.py \
@@ -56,11 +58,9 @@ grblkspython_PYTHON = \
pfb_interpolator.py \
pkt.py \
psk.py \
+ psk2.py \
qam.py \
- qam8.py \
- qam16.py \
- qam64.py \
- qam256.py \
+ qpsk.py \
rational_resampler.py \
standard_squelch.py \
stream_to_vector_decimator.py \
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/bpsk.py b/gnuradio-core/src/python/gnuradio/blks2impl/bpsk.py
new file mode 100644
index 000000000..16524de94
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/bpsk.py
@@ -0,0 +1,98 @@
+#
+# Copyright 2005,2006,2011 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+"""
+BPSK modulation and demodulation.
+"""
+
+from math import pi, log
+from cmath import exp
+
+from gnuradio import gr, modulation_utils2
+from gnuradio.blks2impl.generic_mod_demod import generic_mod, generic_demod
+
+# Default number of points in constellation.
+_def_constellation_points = 2
+# Whether differential coding is used.
+_def_differential = True
+
+# /////////////////////////////////////////////////////////////////////////////
+# BPSK constellation
+# /////////////////////////////////////////////////////////////////////////////
+
+def bpsk_constellation(m=_def_constellation_points):
+ if m != _def_constellation_points:
+ raise ValueError("BPSK can only have 2 constellation points.")
+ return gr.constellation_bpsk()
+
+# /////////////////////////////////////////////////////////////////////////////
+# BPSK modulator
+# /////////////////////////////////////////////////////////////////////////////
+
+class bpsk_mod(generic_mod):
+
+ def __init__(self, constellation_points=_def_constellation_points,
+ *args, **kwargs):
+
+ """
+ Hierarchical block for RRC-filtered BPSK modulation.
+
+ The input is a byte stream (unsigned char) and the
+ output is the complex modulated signal at baseband.
+
+ See generic_mod block for list of parameters.
+ """
+
+ constellation = gr.constellation_bpsk()
+ if constellation_points != 2:
+ raise ValueError('Number of constellation points must be 2 for BPSK.')
+ super(bpsk_mod, self).__init__(constellation, *args, **kwargs)
+
+# /////////////////////////////////////////////////////////////////////////////
+# BPSK demodulator
+#
+# /////////////////////////////////////////////////////////////////////////////
+
+class bpsk_demod(generic_demod):
+
+ def __init__(self, constellation_points=_def_constellation_points,
+ *args, **kwargs):
+
+ """
+ Hierarchical block for RRC-filtered BPSK modulation.
+
+ The input is a byte stream (unsigned char) and the
+ output is the complex modulated signal at baseband.
+
+ See generic_demod block for list of parameters.
+ """
+
+ constellation = gr.constellation_bpsk()
+ if constellation_points != 2:
+ raise ValueError('Number of constellation points must be 2 for BPSK.')
+ super(bpsk_demod, self).__init__(constellation, *args, **kwargs)
+
+#
+# Add these to the mod/demod registry
+#
+modulation_utils2.add_type_1_mod('bpsk', bpsk_mod)
+modulation_utils2.add_type_1_demod('bpsk', bpsk_demod)
+modulation_utils2.add_type_1_constellation('bpsk', bpsk_constellation)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/generic_mod_demod.py b/gnuradio-core/src/python/gnuradio/blks2impl/generic_mod_demod.py
new file mode 100644
index 000000000..44779754b
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/generic_mod_demod.py
@@ -0,0 +1,395 @@
+#
+# Copyright 2005,2006,2007,2009,2011 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+# See gnuradio-examples/python/digital for examples
+
+"""
+Generic modulation and demodulation.
+"""
+
+from gnuradio import gr
+from gnuradio.modulation_utils2 import extract_kwargs_from_options_for_class
+from gnuradio.utils import mod_codes
+
+# default values (used in __init__ and add_options)
+_def_samples_per_symbol = 2
+_def_excess_bw = 0.35
+_def_verbose = False
+_def_log = False
+
+# Frequency correction
+_def_freq_alpha = 0.010
+# Symbol timing recovery
+_def_timing_alpha = 0.100
+_def_timing_beta = 0.010
+_def_timing_max_dev = 1.5
+# Fine frequency / Phase correction
+_def_phase_alpha = 0.1
+# Number of points in constellation
+_def_constellation_points = 16
+# Whether differential coding is used.
+_def_differential = True
+
+def add_common_options(parser):
+ """
+ Sets options common to both modulator and demodulator.
+ """
+ parser.add_option("-p", "--constellation-points", type="int", default=_def_constellation_points,
+ help="set the number of constellation points (must be a power of 2 (power of 4 for QAM) [default=%default]")
+ parser.add_option("", "--differential", action="store_true", dest="differential", default=True,
+ help="use differential encoding [default=%default]")
+ parser.add_option("", "--not-differential", action="store_false", dest="differential",
+ help="do not use differential encoding [default=%default]")
+ parser.add_option("", "--mod-code", type="choice", choices=mod_codes.codes,
+ default=mod_codes.NO_CODE,
+ help="Select modulation code from: %s [default=%%default]"
+ % (', '.join(mod_codes.codes),))
+ parser.add_option("", "--excess-bw", type="float", default=_def_excess_bw,
+ help="set RRC excess bandwith factor [default=%default]")
+
+
+# /////////////////////////////////////////////////////////////////////////////
+# Generic modulator
+# /////////////////////////////////////////////////////////////////////////////
+
+class generic_mod(gr.hier_block2):
+
+ def __init__(self, constellation,
+ differential=_def_differential,
+ samples_per_symbol=_def_samples_per_symbol,
+ excess_bw=_def_excess_bw,
+ verbose=_def_verbose,
+ log=_def_log):
+ """
+ Hierarchical block for RRC-filtered differential generic modulation.
+
+ The input is a byte stream (unsigned char) and the
+ output is the complex modulated signal at baseband.
+
+ @param constellation: determines the modulation type
+ @type constellation: gnuradio.gr.gr_constellation
+ @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 verbose: Print information about modulator?
+ @type verbose: bool
+ @param log: Log modulation data to files?
+ @type log: bool
+ """
+
+ gr.hier_block2.__init__(self, "generic_mod",
+ gr.io_signature(1, 1, gr.sizeof_char), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature
+
+ self._constellation = constellation.base()
+ self._samples_per_symbol = samples_per_symbol
+ self._excess_bw = excess_bw
+ self._differential = differential
+
+ 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._constellation.apply_pre_diff_code():
+ self.symbol_mapper = gr.map_bb(self._constellation.pre_diff_code())
+
+ if differential:
+ self.diffenc = gr.diff_encoder_bb(arity)
+
+ self.chunks2symbols = gr.chunks_to_symbols_bc(self._constellation.points())
+
+ # 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
+ blocks = [self, self.bytes2chunks]
+ if self._constellation.apply_pre_diff_code():
+ blocks.append(self.symbol_mapper)
+ if differential:
+ blocks.append(self.diffenc)
+ blocks += [self.chunks2symbols, self.rrc_filter, self]
+ self.connect(*blocks)
+
+ 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): # static method that's also callable on an instance
+ return self._constellation.bits_per_symbol()
+
+ def add_options(parser):
+ """
+ Adds generic modulation options to the standard parser
+ """
+ add_common_options(parser)
+ add_options=staticmethod(add_options)
+
+ def extract_kwargs_from_options(cls, options):
+ """
+ Given command line options, create dictionary suitable for passing to __init__
+ """
+ return extract_kwargs_from_options_for_class(cls, options)
+ extract_kwargs_from_options=classmethod(extract_kwargs_from_options)
+
+
+ def _print_verbage(self):
+ print "\nModulator:"
+ print "bits per symbol: %d" % self.bits_per_symbol()
+ 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"))
+ if self._constellation.apply_pre_diff_code():
+ self.connect(self.symbol_mapper,
+ gr.file_sink(gr.sizeof_char, "tx_symbol_mapper.dat"))
+ if self._differential:
+ 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"))
+
+
+# /////////////////////////////////////////////////////////////////////////////
+# Generic demodulator
+#
+# Differentially coherent detection of differentially encoded generically
+# modulated signal.
+# /////////////////////////////////////////////////////////////////////////////
+
+class generic_demod(gr.hier_block2):
+
+ def __init__(self, constellation,
+ samples_per_symbol=_def_samples_per_symbol,
+ differential=_def_differential,
+ excess_bw=_def_excess_bw,
+ freq_alpha=_def_freq_alpha,
+ timing_alpha=_def_timing_alpha,
+ timing_max_dev=_def_timing_max_dev,
+ phase_alpha=_def_phase_alpha,
+ verbose=_def_verbose,
+ log=_def_log):
+ """
+ Hierarchical block for RRC-filtered differential generic demodulation.
+
+ The input is the complex modulated signal at baseband.
+ The output is a stream of bits packed 1 bit per byte (LSB)
+
+ @param constellation: determines the modulation type
+ @type constellation: gnuradio.gr.gr_constellation
+ @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 freq_alpha: loop filter gain for frequency recovery
+ @type freq_alpha: float
+ @param timing_alpha: loop alpha gain for timing recovery
+ @type timing_alpha: float
+ @param timing_max_dev: timing loop maximum rate deviations
+ @type timing_max_dev: float
+ @param phase_alpha: loop filter gain in phase loop
+ @type phase_alphas: 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, "generic_demod",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_char)) # Output signature
+
+ self._constellation = constellation.base()
+ self._samples_per_symbol = samples_per_symbol
+ self._excess_bw = excess_bw
+ self._phase_alpha = phase_alpha
+ self._freq_alpha = freq_alpha
+ self._freq_beta = 0.10*self._freq_alpha
+ self._timing_alpha = timing_alpha
+ self._timing_beta = _def_timing_beta
+ self._timing_max_dev=timing_max_dev
+ self._differential = differential
+
+ 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)
+
+ arity = pow(2,self.bits_per_symbol())
+
+ # Automatic gain control
+ self.agc = gr.agc2_cc(0.6e-1, 1e-3, 1, 1, 100)
+
+ # Frequency correction
+ self.freq_recov = gr.fll_band_edge_cc(self._samples_per_symbol, self._excess_bw,
+ 11*int(self._samples_per_symbol),
+ self._freq_alpha, self._freq_beta)
+
+ # symbol timing recovery with RRC data filter
+ nfilts = 32
+ ntaps = 11 * int(self._samples_per_symbol*nfilts)
+ taps = gr.firdes.root_raised_cosine(nfilts, nfilts,
+ 1.0/float(self._samples_per_symbol),
+ self._excess_bw, ntaps)
+ self.time_recov = gr.pfb_clock_sync_ccf(self._samples_per_symbol,
+ self._timing_alpha,
+ taps, nfilts, nfilts/2, self._timing_max_dev)
+ self.time_recov.set_beta(self._timing_beta)
+
+ #self._phase_beta = 0.25 * self._phase_alpha * self._phase_alpha
+ self._phase_beta = 0.25 * self._phase_alpha * self._phase_alpha
+ fmin = -0.25
+ fmax = 0.25
+
+ self.receiver = gr.constellation_receiver_cb(
+ self._constellation,
+ self._phase_alpha, self._phase_beta,
+ fmin, fmax)
+
+ # Do differential decoding based on phase change of symbols
+ if differential:
+ self.diffdec = gr.diff_decoder_bb(arity)
+
+ if self._constellation.apply_pre_diff_code():
+ self.symbol_mapper = gr.map_bb(
+ mod_codes.invert_code(self._constellation.pre_diff_code()))
+
+ # 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
+ blocks = [self, self.agc, self.freq_recov, self.time_recov, self.receiver]
+ if differential:
+ blocks.append(self.diffdec)
+ if self._constellation.apply_pre_diff_code():
+ blocks.append(self.symbol_mapper)
+ blocks += [self.unpack, self]
+ self.connect(*blocks)
+
+ def samples_per_symbol(self):
+ return self._samples_per_symbol
+
+ def bits_per_symbol(self): # staticmethod that's also callable on an instance
+ return self._constellation.bits_per_symbol()
+
+ def _print_verbage(self):
+ print "\nDemodulator:"
+ print "bits per symbol: %d" % self.bits_per_symbol()
+ print "RRC roll-off factor: %.2f" % self._excess_bw
+ print "FLL gain: %.2e" % self._freq_alpha
+ print "Timing alpha gain: %.2e" % self._timing_alpha
+ print "Timing beta gain: %.2e" % self._timing_beta
+ print "Timing max dev: %.2f" % self._timing_max_dev
+ print "Phase track alpha: %.2e" % self._phase_alpha
+ print "Phase track beta: %.2e" % self._phase_beta
+
+ def _setup_logging(self):
+ print "Modulation logging turned on."
+ self.connect(self.agc,
+ gr.file_sink(gr.sizeof_gr_complex, "rx_agc.dat"))
+ self.connect((self.freq_recov, 0),
+ gr.file_sink(gr.sizeof_gr_complex, "rx_freq_recov.dat"))
+ self.connect((self.freq_recov, 1),
+ gr.file_sink(gr.sizeof_float, "rx_freq_recov_freq.dat"))
+ self.connect((self.freq_recov, 2),
+ gr.file_sink(gr.sizeof_float, "rx_freq_recov_phase.dat"))
+ self.connect((self.freq_recov, 3),
+ gr.file_sink(gr.sizeof_gr_complex, "rx_freq_recov_error.dat"))
+ self.connect((self.time_recov, 0),
+ gr.file_sink(gr.sizeof_gr_complex, "rx_time_recov.dat"))
+ self.connect((self.time_recov, 1),
+ gr.file_sink(gr.sizeof_float, "rx_time_recov_error.dat"))
+ self.connect((self.time_recov, 2),
+ gr.file_sink(gr.sizeof_float, "rx_time_recov_rate.dat"))
+ self.connect((self.time_recov, 3),
+ gr.file_sink(gr.sizeof_float, "rx_time_recov_phase.dat"))
+ self.connect((self.receiver, 0),
+ gr.file_sink(gr.sizeof_char, "rx_receiver.dat"))
+ self.connect((self.receiver, 1),
+ gr.file_sink(gr.sizeof_float, "rx_receiver_error.dat"))
+ self.connect((self.receiver, 2),
+ gr.file_sink(gr.sizeof_float, "rx_receiver_phase.dat"))
+ self.connect((self.receiver, 3),
+ gr.file_sink(gr.sizeof_float, "rx_receiver_freq.dat"))
+ if self._differential:
+ self.connect(self.diffdec,
+ gr.file_sink(gr.sizeof_char, "rx_diffdec.dat"))
+ if self._constellation.apply_pre_diff_code():
+ 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 generic demodulation options to the standard parser
+ """
+ # Add options shared with modulator.
+ add_common_options(parser)
+ # Add options specific to demodulator.
+ parser.add_option("", "--freq-alpha", type="float", default=_def_freq_alpha,
+ help="set frequency lock loop alpha gain value [default=%default]")
+ parser.add_option("", "--phase-alpha", type="float", default=_def_phase_alpha,
+ help="set phase tracking loop alpha value [default=%default]")
+ parser.add_option("", "--timing-alpha", type="float", default=_def_timing_alpha,
+ help="set timing symbol sync loop gain alpha value [default=%default]")
+ parser.add_option("", "--timing-beta", type="float", default=_def_timing_beta,
+ help="set timing symbol sync loop gain beta value [default=%default]")
+ parser.add_option("", "--timing-max-dev", type="float", default=_def_timing_max_dev,
+ help="set timing symbol sync loop maximum deviation [default=%default]")
+ add_options=staticmethod(add_options)
+
+ def extract_kwargs_from_options(cls, options):
+ """
+ Given command line options, create dictionary suitable for passing to __init__
+ """
+ return extract_kwargs_from_options_for_class(cls, options)
+ extract_kwargs_from_options=classmethod(extract_kwargs_from_options)
+
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/ofdm.py b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm.py
index 2663f7cf8..3b1cd12ac 100644
--- a/gnuradio-core/src/python/gnuradio/blks2impl/ofdm.py
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm.py
@@ -21,12 +21,26 @@
#
import math
-from gnuradio import gr, ofdm_packet_utils
+from gnuradio import gr, ofdm_packet_utils, modulation_utils2
import gnuradio.gr.gr_threading as _threading
import psk, qam
from gnuradio.blks2impl.ofdm_receiver import ofdm_receiver
+def _add_common_options(normal, expert):
+ """
+ Adds OFDM-specific options to the Options Parser that are common
+ both to the modulator and demodulator.
+ """
+ mods_list = ", ".join(modulation_utils2.type_1_constellations().keys())
+ normal.add_option("-m", "--modulation", type="string", default="psk",
+ help="set modulation type (" + mods_list + ") [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]")
# /////////////////////////////////////////////////////////////////////////////
# mod/demod with packets as i/o
@@ -61,6 +75,8 @@ class ofdm_mod(gr.hier_block2):
self._fft_length = options.fft_length
self._occupied_tones = options.occupied_tones
self._cp_length = options.cp_length
+
+ arity = options.constellation_points
win = [] #[1 for i in range(self._fft_length)]
@@ -82,19 +98,9 @@ class ofdm_mod(gr.hier_block2):
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,
+ const = modulation_utils2.type_1_constellations()[self._modulation](arity).points()
+
+ self._pkt_input = gr.ofdm_mapper_bcv(const, msgq_limit,
options.occupied_tones, options.fft_length)
self.preambles = gr.ofdm_insert_preamble(self._fft_length, padded_preambles)
@@ -140,14 +146,10 @@ class ofdm_mod(gr.hier_block2):
"""
Adds OFDM-specific options to the Options Parser
"""
- normal.add_option("-m", "--modulation", type="string", default="bpsk",
- help="set modulation type (bpsk, qpsk, 8psk, qam{16,64}) [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]")
+ _add_common_options(normal, expert)
+ for mod in modulation_utils2.type_1_mods().values():
+ mod.add_options(expert)
+
# Make a static method to call before instantiation
add_options = staticmethod(add_options)
@@ -196,6 +198,9 @@ class ofdm_demod(gr.hier_block2):
self._cp_length = options.cp_length
self._snr = options.snr
+ arity = options.constellation_points
+ print("con points is %s" % options.constellation_points)
+
# 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]
@@ -211,22 +216,11 @@ class ofdm_demod(gr.hier_block2):
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
+ constell = modulation_utils2.type_1_constellations()[self._modulation](arity)
phgain = 0.25
frgain = phgain*phgain / 4.0
- self.ofdm_demod = gr.ofdm_frame_sink(rotated_const, range(arity),
+ self.ofdm_demod = gr.ofdm_frame_sink2(constell.base(),
self._rcvd_pktq,
self._occupied_tones,
phgain, frgain)
@@ -253,14 +247,9 @@ class ofdm_demod(gr.hier_block2):
"""
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]")
+ _add_common_options(normal, expert)
+ for mod in modulation_utils2.type_1_mods().values():
+ mod.add_options(expert)
# Make a static method to call before instantiation
add_options = staticmethod(add_options)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/pkt.py b/gnuradio-core/src/python/gnuradio/blks2impl/pkt.py
index 908437ef2..aa720d1a5 100644
--- a/gnuradio-core/src/python/gnuradio/blks2impl/pkt.py
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/pkt.py
@@ -34,7 +34,8 @@ class mod_pkts(gr.hier_block2):
Send packets by calling send_pkt
"""
- def __init__(self, modulator, access_code=None, msgq_limit=2, pad_for_usrp=True, use_whitener_offset=False):
+ def __init__(self, modulator, access_code=None, msgq_limit=2, pad_for_usrp=True, use_whitener_offset=False,
+ modulate=True):
"""
Hierarchical block for sending packets
@@ -49,13 +50,18 @@ class mod_pkts(gr.hier_block2):
@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
+ @param modulate: If false, no modulation will be performed.
See gmsk_mod for remaining parameters
"""
+ if modulate:
+ output_size = gr.sizeof_gr_complex
+ else:
+ output_size = gr.sizeof_char
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
+ gr.io_signature(1, 1, output_size)) # Output signature
self._modulator = modulator
self._pad_for_usrp = pad_for_usrp
@@ -70,7 +76,10 @@ class mod_pkts(gr.hier_block2):
# 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)
+ if modulate:
+ self.connect(self._pkt_input, self._modulator, self)
+ else:
+ self.connect(self._pkt_input, self)
def send_pkt(self, payload='', eof=False):
"""
@@ -106,13 +115,15 @@ class demod_pkts(gr.hier_block2):
app via the callback.
"""
- def __init__(self, demodulator, access_code=None, callback=None, threshold=-1):
+ def __init__(self, demodulator, access_code=None, callback=None, threshold=-1, demodulate=True):
"""
Hierarchical block for demodulating and deframing packets.
The input is the complex modulated signal at baseband.
Demodulated packets are sent to the handler.
+ If demodulator is None it is assumed the input is already demodulated.
+
@param demodulator: instance of demodulator class (gr_block or hier_block2)
@type demodulator: complex baseband in
@param access_code: AKA sync vector
@@ -123,9 +134,14 @@ class demod_pkts(gr.hier_block2):
@type threshold: int
"""
+ if demodulator is not None:
+ input_size = gr.sizeof_gr_complex
+ else:
+ input_size = gr.sizeof_char
+
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
+ gr.io_signature(1, 1, input_size), # Input signature
+ gr.io_signature(0, 0, 0)) # Output signature
self._demodulator = demodulator
if access_code is None:
@@ -141,9 +157,13 @@ class demod_pkts(gr.hier_block2):
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)
+ if self._demodulator is not None:
+ self.connect(self, self._demodulator, self.correlator, self.framer_sink)
+ else:
+ self.connect(self, self.correlator, self.framer_sink)
- self._watcher = _queue_watcher_thread(self._rcvd_pktq, callback)
+ if callback is not None:
+ self._watcher = _queue_watcher_thread(self._rcvd_pktq, callback)
class _queue_watcher_thread(_threading.Thread):
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/psk2.py b/gnuradio-core/src/python/gnuradio/blks2impl/psk2.py
new file mode 100644
index 000000000..aaa9659a4
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/psk2.py
@@ -0,0 +1,121 @@
+#
+# Copyright 2005,2006,2011 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+"""
+PSK modulation and demodulation.
+"""
+
+from math import pi, log
+from cmath import exp
+
+from gnuradio import gr, modulation_utils2
+from gnuradio.blks2impl.generic_mod_demod import generic_mod, generic_demod
+from gnuradio.utils import mod_codes, gray_code
+
+# Default number of points in constellation.
+_def_constellation_points = 4
+# The default encoding (e.g. gray-code, set-partition)
+_def_mod_code = mod_codes.GRAY_CODE
+
+def create_encodings(mod_code, arity):
+ post_diff_code = None
+ if mod_code not in mod_codes.codes:
+ raise ValueError('That modulation code does not exist.')
+ if mod_code == mod_codes.GRAY_CODE:
+ pre_diff_code = gray_code.gray_code(arity)
+ elif mod_code == mod_codes.SET_PARTITION_CODE:
+ pre_diff_code = set_partition_code.set_partition_code(arity)
+ elif mod_code == mod_codes.NO_CODE:
+ pre_diff_code = []
+ else:
+ raise ValueError('That modulation code is not implemented for this constellation.')
+ return (pre_diff_code, post_diff_code)
+
+# /////////////////////////////////////////////////////////////////////////////
+# PSK constellation
+# /////////////////////////////////////////////////////////////////////////////
+
+def psk_constellation(m=_def_constellation_points, mod_code=_def_mod_code):
+ """
+ Creates a PSK constellation object.
+ """
+ k = log(m) / log(2.0)
+ if (k != int(k)):
+ raise StandardError('Number of constellation points must be a power of two.')
+ points = [exp(2*pi*(0+1j)*i/m) for i in range(0,m)]
+ pre_diff_code, post_diff_code = create_encodings(mod_code, m)
+ if post_diff_code is not None:
+ inverse_post_diff_code = mod_codes.invert_code(post_diff_code)
+ points = [points[x] for x in inverse_post_diff_code]
+ constellation = gr.constellation_psk(points, pre_diff_code, m)
+ return constellation
+
+# /////////////////////////////////////////////////////////////////////////////
+# PSK modulator
+# /////////////////////////////////////////////////////////////////////////////
+
+class psk_mod(generic_mod):
+
+ def __init__(self, constellation_points=_def_constellation_points,
+ mod_code=_def_mod_code,
+ *args, **kwargs):
+
+ """
+ Hierarchical block for RRC-filtered PSK modulation.
+
+ The input is a byte stream (unsigned char) and the
+ output is the complex modulated signal at baseband.
+
+ See generic_mod block for list of parameters.
+ """
+
+ constellation = psk_constellation(constellation_points, mod_code)
+ super(psk_mod, self).__init__(constellation, *args, **kwargs)
+
+# /////////////////////////////////////////////////////////////////////////////
+# PSK demodulator
+#
+# /////////////////////////////////////////////////////////////////////////////
+
+class psk_demod(generic_demod):
+
+ def __init__(self, constellation_points=_def_constellation_points,
+ mod_code=_def_mod_code,
+ *args, **kwargs):
+
+ """
+ Hierarchical block for RRC-filtered PSK modulation.
+
+ The input is a byte stream (unsigned char) and the
+ output is the complex modulated signal at baseband.
+
+ See generic_demod block for list of parameters.
+ """
+
+ constellation = psk_constellation(constellation_points, mod_code)
+ super(psk_demod, self).__init__(constellation, *args, **kwargs)
+
+#
+# Add these to the mod/demod registry
+#
+modulation_utils2.add_type_1_mod('psk', psk_mod)
+modulation_utils2.add_type_1_demod('psk', psk_demod)
+modulation_utils2.add_type_1_constellation('psk', psk_constellation)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/qam.py b/gnuradio-core/src/python/gnuradio/blks2impl/qam.py
index 22b1e1dab..9c135b25a 100644
--- a/gnuradio-core/src/python/gnuradio/blks2impl/qam.py
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/qam.py
@@ -1,5 +1,5 @@
#
-# Copyright 2005,2006 Free Software Foundation, Inc.
+# Copyright 2005,2006,2011 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
@@ -19,95 +19,208 @@
# Boston, MA 02110-1301, USA.
#
-from math import pi, sqrt
-import math
+"""
+QAM modulation and demodulation.
+"""
-# These constellations are generated for Gray coding when symbos [1, ..., m] are used
-# Mapping to Gray coding is therefore unnecessary
+from math import pi, sqrt, log
-def make_constellation(m):
- # number of bits/symbol (log2(M))
- k = int(math.log10(m) / math.log10(2.0))
+from gnuradio import gr, modulation_utils2
+from gnuradio.blks2impl.generic_mod_demod import generic_mod, generic_demod
+from gnuradio.utils.gray_code import gray_code
+from gnuradio.utils import mod_codes
- coeff = 1
+# Default number of points in constellation.
+_def_constellation_points = 16
+# Whether the quadrant bits are coded differentially.
+_def_differential = True
+# Whether gray coding is used. If differential is True then gray
+# coding is used within but not between each quadrant.
+_def_mod_code = mod_codes.NO_CODE
+
+def is_power_of_four(x):
+ v = log(x)/log(4)
+ return int(v) == v
+
+def get_bit(x, n):
+ """ Get the n'th bit of integer x (from little end)."""
+ return (x&(0x01 << n)) >> n
+
+def get_bits(x, n, k):
+ """ Get the k bits of integer x starting at bit n(from little end)."""
+ # Remove the n smallest bits
+ v = x >> n
+ # Remove all bits bigger than n+k-1
+ return v % pow(2, k)
+
+def make_differential_constellation(m, gray_coded):
+ """
+ Create a constellation with m possible symbols where m must be
+ a power of 4.
+
+ Points are laid out in a square grid.
+
+ Bits referring to the quadrant are differentilly encoded,
+ remaining bits are gray coded.
+
+ """
+ sqrtm = pow(m, 0.5)
+ if (not isinstance(m, int) or m < 4 or not is_power_of_four(m)):
+ raise ValueError("m must be a power of 4 integer.")
+ # Each symbol holds k bits.
+ k = int(log(m) / log(2.0))
+ # First create a constellation for one quadrant containing m/4 points.
+ # The quadrant has 'side' points along each side of a quadrant.
+ side = int(sqrtm/2)
+ if gray_coded:
+ # Number rows and columns using gray codes.
+ gcs = gray_code(side)
+ # Get inverse gray codes.
+ i_gcs = dict([(v, key) for key, v in enumerate(gcs)])
+ else:
+ i_gcs = dict([(i, i) for i in range(0, side)])
+ # The distance between points is found.
+ step = 1/(side-0.5)
+
+ gc_to_x = [(i_gcs[gc]+0.5)*step for gc in range(0, side)]
+
+ # Takes the (x, y) location of the point with the quadrant along
+ # with the quadrant number. (x, y) are integers referring to which
+ # point within the quadrant it is.
+ # A complex number representing this location of this point is returned.
+ def get_c(gc_x, gc_y, quad):
+ if quad == 0:
+ return complex(gc_to_x[gc_x], gc_to_x[gc_y])
+ if quad == 1:
+ return complex(-gc_to_x[gc_y], gc_to_x[gc_x])
+ if quad == 2:
+ return complex(-gc_to_x[gc_x], -gc_to_x[gc_y])
+ if quad == 3:
+ return complex(gc_to_x[gc_y], -gc_to_x[gc_x])
+ raise StandardError("Impossible!")
+
+ # First two bits determine quadrant.
+ # Next (k-2)/2 bits determine x position.
+ # Following (k-2)/2 bits determine y position.
+ # How x and y relate to real and imag depends on quadrant (see get_c function).
+ const_map = []
+ for i in range(m):
+ y = get_bits(i, 0, (k-2)/2)
+ x = get_bits(i, (k-2)/2, (k-2)/2)
+ quad = get_bits(i, k-2, 2)
+ const_map.append(get_c(x, y, quad))
+
+ return const_map
+
+def make_not_differential_constellation(m, gray_coded):
+ side = int(pow(m, 0.5))
+ if (not isinstance(m, int) or m < 4 or not is_power_of_four(m)):
+ raise ValueError("m must be a power of 4 integer.")
+ # Each symbol holds k bits.
+ k = int(log(m) / log(2.0))
+ if gray_coded:
+ # Number rows and columns using gray codes.
+ gcs = gray_code(side)
+ # Get inverse gray codes.
+ i_gcs = mod_codes.invert_code(gcs)
+ else:
+ i_gcs = range(0, side)
+ # The distance between points is found.
+ step = 2.0/(side-1)
+
+ gc_to_x = [-1 + i_gcs[gc]*step for gc in range(0, side)]
+ # First k/2 bits determine x position.
+ # Following k/2 bits determine y position.
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)
- }
+ y = gc_to_x[get_bits(i, 0, k/2)]
+ x = gc_to_x[get_bits(i, k/2, k/2)]
+ const_map.append(complex(x,y))
+ return const_map
+
+# /////////////////////////////////////////////////////////////////////////////
+# QAM constellation
+# /////////////////////////////////////////////////////////////////////////////
+
+def qam_constellation(constellation_points=_def_constellation_points,
+ differential=_def_differential,
+ mod_code=_def_mod_code):
+ """
+ Creates a QAM constellation object.
+ """
+ if mod_code == mod_codes.GRAY_CODE:
+ gray_coded = True
+ elif mod_code == mod_codes.NO_CODE:
+ gray_coded = False
+ else:
+ raise ValueError("Mod code is not implemented for QAM")
+ if differential:
+ points = make_differential_constellation(constellation_points, gray_coded)
+ else:
+ points = make_not_differential_constellation(constellation_points, gray_coded)
+ side = int(sqrt(constellation_points))
+ width = 2.0/(side-1)
+ # No pre-diff code
+ # Should add one so that we can gray-code the quadrant bits too.
+ pre_diff_code = []
+ constellation = gr.constellation_rect(points, pre_diff_code, 4, side, side, width, width)
+ return constellation
+
+# /////////////////////////////////////////////////////////////////////////////
+# QAM modulator
+# /////////////////////////////////////////////////////////////////////////////
+
+class qam_mod(generic_mod):
+
+ def __init__(self, constellation_points=_def_constellation_points,
+ differential=_def_differential,
+ mod_code=_def_mod_code,
+ *args, **kwargs):
+
+ """
+ Hierarchical block for RRC-filtered QAM modulation.
+
+ The input is a byte stream (unsigned char) and the
+ output is the complex modulated signal at baseband.
+
+ See generic_mod block for list of parameters.
+ """
+
+ constellation = qam_constellation(constellation_points, differential, mod_code)
+ # We take care of the gray coding in the constellation generation so it doesn't
+ # need to be done in the block.
+ super(qam_mod, self).__init__(constellation, differential=differential,
+ *args, **kwargs)
+
+# /////////////////////////////////////////////////////////////////////////////
+# QAM demodulator
+#
+# /////////////////////////////////////////////////////////////////////////////
+
+class qam_demod(generic_demod):
+
+ def __init__(self, constellation_points=_def_constellation_points,
+ differential=_def_differential,
+ mod_code=_def_mod_code,
+ *args, **kwargs):
+
+ """
+ Hierarchical block for RRC-filtered QAM modulation.
+
+ The input is a byte stream (unsigned char) and the
+ output is the complex modulated signal at baseband.
+
+ See generic_demod block for list of parameters.
+ """
+ constellation = qam_constellation(constellation_points, differential, mod_code)
+ # We take care of the gray coding in the constellation generation so it doesn't
+ # need to be done in the block.
+ super(qam_demod, self).__init__(constellation, differential=differential,
+ *args, **kwargs)
+
+#
+# Add these to the mod/demod registry
+#
+modulation_utils2.add_type_1_mod('qam', qam_mod)
+modulation_utils2.add_type_1_demod('qam', qam_demod)
+modulation_utils2.add_type_1_constellation('qam', qam_constellation)
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/qam16.py b/gnuradio-core/src/python/gnuradio/blks2impl/qam16.py
deleted file mode 100644
index 0bdb9c6fb..000000000
--- a/gnuradio-core/src/python/gnuradio/blks2impl/qam16.py
+++ /dev/null
@@ -1,208 +0,0 @@
-#
-# 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
deleted file mode 100644
index fc455f17c..000000000
--- a/gnuradio-core/src/python/gnuradio/blks2impl/qam256.py
+++ /dev/null
@@ -1,209 +0,0 @@
-#
-# 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
deleted file mode 100644
index 5509f3745..000000000
--- a/gnuradio-core/src/python/gnuradio/blks2impl/qam64.py
+++ /dev/null
@@ -1,208 +0,0 @@
-#
-# 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
deleted file mode 100644
index 6a7b35597..000000000
--- a/gnuradio-core/src/python/gnuradio/blks2impl/qam8.py
+++ /dev/null
@@ -1,209 +0,0 @@
-#
-# 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_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/qpsk.py b/gnuradio-core/src/python/gnuradio/blks2impl/qpsk.py
new file mode 100644
index 000000000..4af8d0018
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/qpsk.py
@@ -0,0 +1,79 @@
+#
+# Copyright 2005,2006,2011 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+"""
+QPSK modulation.
+
+Demodulation is not included since the generic_mod_demod
+doesn't work for non-differential encodings.
+"""
+
+from gnuradio import gr, modulation_utils2
+from gnuradio.blks2impl.generic_mod_demod import generic_mod
+
+
+# Default number of points in constellation.
+_def_constellation_points = 4
+# Whether differential coding is used.
+_def_differential = False
+_def_gray_coded = True
+
+# /////////////////////////////////////////////////////////////////////////////
+# QPSK constellation
+# /////////////////////////////////////////////////////////////////////////////
+
+def qpsk_constellation(m=_def_constellation_points):
+ if m != _def_constellation_points:
+ raise ValueError("QPSK can only have 4 constellation points.")
+ return gr.constellation_qpsk()
+
+# /////////////////////////////////////////////////////////////////////////////
+# QPSK modulator
+# /////////////////////////////////////////////////////////////////////////////
+
+class qpsk_mod(generic_mod):
+
+ def __init__(self, constellation_points=_def_constellation_points,
+ differential=_def_differential,
+ gray_coded=_def_gray_coded,
+ *args, **kwargs):
+
+ """
+ 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.
+
+ See generic_mod block for list of parameters.
+ """
+
+ constellation = gr.constellation_qpsk()
+ if constellation_points != 4:
+ raise ValueError("QPSK can only have 4 constellation points.")
+ if differential or not gray_coded:
+ raise ValueError("This QPSK mod/demod works only for gray-coded, non-differential.")
+ super(qpsk_mod, self).__init__(constellation, differential, gray_coded, *args, **kwargs)
+
+#
+# Add these to the mod/demod registry
+#
+modulation_utils2.add_type_1_mod('qpsk', qpsk_mod)
+modulation_utils2.add_type_1_constellation('qpsk', qpsk_constellation)
diff --git a/gnuradio-core/src/python/gnuradio/gr/Makefile.am b/gnuradio-core/src/python/gnuradio/gr/Makefile.am
index b8da9cf48..78480b412 100644
--- a/gnuradio-core/src/python/gnuradio/gr/Makefile.am
+++ b/gnuradio-core/src/python/gnuradio/gr/Makefile.am
@@ -51,6 +51,7 @@ noinst_PYTHON = \
qa_classify.py \
qa_cma_equalizer.py \
qa_complex_to_xxx.py \
+ qa_constellation.py \
qa_constellation_decoder_cb.py \
qa_copy.py \
qa_correlate_access_code.py \
@@ -71,6 +72,7 @@ noinst_PYTHON = \
qa_hier_block2.py \
qa_hilbert.py \
qa_iir.py \
+ qa_int_to_float.py \
qa_interleave.py \
qa_interp_fir_filter.py \
qa_kludge_copy.py \
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_constellation.py b/gnuradio-core/src/python/gnuradio/gr/qa_constellation.py
new file mode 100644
index 000000000..5c3c04160
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_constellation.py
@@ -0,0 +1,203 @@
+#!/usr/bin/env python
+#
+# Copyright 2011 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import random
+from cmath import exp, pi, log
+
+from gnuradio import gr, gr_unittest, blks2
+from gnuradio.utils import mod_codes
+
+tested_mod_codes = (mod_codes.NO_CODE, mod_codes.GRAY_CODE)
+
+# A list of the constellations to test.
+# Each constellation is given by a 3-tuple.
+# First item is a function to generate the constellation
+# Second item is a dictionary of arguments for function with lists of
+# possible values.
+# Third item is whether differential encoding should be tested.
+# Fourth item is the name of the argument to constructor that specifices
+# whether differential encoding is used.
+
+def twod_constell():
+ """
+
+ """
+ points = ((1+0j), (0+1j),
+ (-1+0j), (0-1j))
+ rot_sym = 2
+ dim = 2
+ return gr.constellation_calcdist(points, [], rot_sym, dim)
+
+def threed_constell():
+ oned_points = ((1+0j), (0+1j), (-1+0j), (0-1j))
+ points = []
+ r4 = range(0, 4)
+ for ia in r4:
+ for ib in r4:
+ for ic in r4:
+ points += [oned_points[ia], oned_points[ib], oned_points[ic]]
+ rot_sym = 4
+ dim = 3
+ return gr.constellation_calcdist(points, [], rot_sym, dim)
+
+tested_constellation_info = (
+ (blks2.psk_constellation,
+ {'m': (2, 4, 8, 16, 32, 64),
+ 'mod_code': tested_mod_codes, },
+ True, None),
+ (blks2.qam_constellation,
+ {'constellation_points': (4, 16, 64),
+ 'mod_code': tested_mod_codes, },
+ True, 'differential'),
+ (blks2.bpsk_constellation, {}, True, None),
+ # No differential testing for qpsk because it is gray-coded.
+ # This is because soft decision making is simpler if we can assume
+ # gray coding.
+ (blks2.qpsk_constellation, {}, False, None),
+ (twod_constell, {}, True, None),
+ (threed_constell, {}, True, None),
+ )
+
+def tested_constellations():
+ """
+ Generator to produce (constellation, differential) tuples for testing purposes.
+ """
+ for constructor, poss_args, differential, diff_argname in tested_constellation_info:
+ if differential:
+ diff_poss = (True, False)
+ else:
+ diff_poss = (False,)
+ poss_args = [[argname, argvalues, 0] for argname, argvalues in poss_args.items()]
+ for current_diff in diff_poss:
+ # Add an index into args to keep track of current position in argvalues
+ while True:
+ current_args = dict([(argname, argvalues[argindex])
+ for argname, argvalues, argindex in poss_args])
+ if diff_argname is not None:
+ current_args[diff_argname] = current_diff
+ constellation = constructor(**current_args)
+ yield (constellation, current_diff)
+ for this_poss_arg in poss_args:
+ argname, argvalues, argindex = this_poss_arg
+ if argindex < len(argvalues) - 1:
+ this_poss_arg[2] += 1
+ break
+ else:
+ this_poss_arg[2] = 0
+ if sum([argindex for argname, argvalues, argindex in poss_args]) == 0:
+ break
+
+
+class test_constellation (gr_unittest.TestCase):
+
+ src_length = 256
+
+ def setUp(self):
+ # Generate a list of random bits.
+ self.src_data = tuple([random.randint(0,1) for i in range(0, self.src_length)])
+
+ def tearDown(self):
+ pass
+
+ def test_hard_decision(self):
+ for constellation, differential in tested_constellations():
+ if differential:
+ rs = constellation.rotational_symmetry()
+ rotations = [exp(i*2*pi*(0+1j)/rs) for i in range(0, rs)]
+ else:
+ rotations = [None]
+ for rotation in rotations:
+ src = gr.vector_source_b(self.src_data)
+ content = mod_demod(constellation, differential, rotation)
+ dst = gr.vector_sink_b()
+ self.tb = gr.top_block()
+ self.tb.connect(src, content, dst)
+ self.tb.run()
+ data = dst.data()
+ # Don't worry about cut off data for now.
+ first = constellation.bits_per_symbol()
+ self.assertEqual (self.src_data[first:len(data)], data[first:])
+
+
+class mod_demod(gr.hier_block2):
+ def __init__(self, constellation, differential, rotation):
+ if constellation.arity() > 256:
+ # If this becomes limiting some of the blocks should be generalised so that they can work
+ # with shorts and ints as well as chars.
+ raise ValueError("Constellation cannot contain more than 256 points.")
+
+ gr.hier_block2.__init__(self, "mod_demod",
+ gr.io_signature(1, 1, gr.sizeof_char), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_char)) # Output signature
+
+ arity = constellation.arity()
+
+ # TX
+ self.constellation = constellation
+ self.differential = differential
+ self.blocks = [self]
+ # We expect a stream of unpacked bits.
+ # First step is to pack them.
+ self.blocks.append(
+ gr.unpacked_to_packed_bb(1, gr.GR_MSB_FIRST))
+ # Second step we unpack them such that we have k bits in each byte where
+ # each constellation symbol hold k bits.
+ self.blocks.append(
+ gr.packed_to_unpacked_bb(self.constellation.bits_per_symbol(),
+ gr.GR_MSB_FIRST))
+ # Apply any pre-differential coding
+ # Gray-coding is done here if we're also using differential coding.
+ if self.constellation.apply_pre_diff_code():
+ self.blocks.append(gr.map_bb(self.constellation.pre_diff_code()))
+ # Differential encoding.
+ if self.differential:
+ self.blocks.append(gr.diff_encoder_bb(arity))
+ # Convert to constellation symbols.
+ self.blocks.append(gr.chunks_to_symbols_bc(self.constellation.points(), self.constellation.dimensionality()))
+ # CHANNEL
+ # Channel just consists of a rotation to check differential coding.
+ if rotation is not None:
+ self.blocks.append(gr.multiply_const_cc(rotation))
+
+ # RX
+ # Convert the constellation symbols back to binary values.
+ self.blocks.append(gr.constellation_decoder2_cb(self.constellation.base()))
+ # Differential decoding.
+ if self.differential:
+ self.blocks.append(gr.diff_decoder_bb(arity))
+ # Decode any pre-differential coding.
+ if self.constellation.apply_pre_diff_code():
+ self.blocks.append(gr.map_bb(
+ mod_codes.invert_code(self.constellation.pre_diff_code())))
+ # unpack the k bit vector into a stream of bits
+ self.blocks.append(gr.unpack_k_bits_bb(
+ self.constellation.bits_per_symbol()))
+ # connect to block output
+ check_index = len(self.blocks)
+ self.blocks = self.blocks[:check_index]
+ self.blocks.append(self)
+
+ self.connect(*self.blocks)
+
+
+if __name__ == '__main__':
+ gr_unittest.run(test_constellation, "test_constellation.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_constellation_receiver.py b/gnuradio-core/src/python/gnuradio/gr/qa_constellation_receiver.py
new file mode 100644
index 000000000..544c84cd4
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_constellation_receiver.py
@@ -0,0 +1,132 @@
+#!/usr/bin/env python
+#
+# Copyright 2011 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import random
+
+from gnuradio import gr, blks2, packet_utils, gr_unittest
+from gnuradio.utils import mod_codes, alignment
+
+from qa_constellation import tested_constellations, twod_constell
+
+# Set a seed so that if errors turn up they are reproducible.
+# 1234 fails
+random.seed(1239)
+
+# TESTING PARAMETERS
+# The number of symbols to test with.
+# We need this many to let the frequency recovery block converge.
+DATA_LENGTH = 200000
+# Test fails if fraction of output that is correct is less than this.
+REQ_CORRECT = 0.8
+
+# CHANNEL PARAMETERS
+NOISE_VOLTAGE = 0.01
+FREQUENCY_OFFSET = 0.01
+TIMING_OFFSET = 1.0
+
+# RECEIVER PARAMETERS
+# Increased from normal default of 0.01 to speed things up.
+FREQ_ALPHA = 0.02
+# Decreased from normal default of 0.1 is required for the constellations
+# with smaller point separations.
+PHASE_ALPHA = 0.02
+
+
+class test_constellation_receiver (gr_unittest.TestCase):
+
+ # We ignore the first half of the output data since often it takes
+ # a while for the receiver to lock on.
+ ignore_fraction = 0.8
+ seed = 1234
+ max_data_length = DATA_LENGTH * 6
+ max_num_samples = 1000
+
+ def test_basic(self):
+ """
+ Tests a bunch of different constellations by using generic
+ modulation, a channel, and generic demodulation. The generic
+ demodulation uses constellation_receiver which is what
+ we're really trying to test.
+ """
+ # Assumes not more than 64 points in a constellation
+ # Generates some random input data to use.
+ self.src_data = tuple(
+ [random.randint(0,1) for i in range(0, self.max_data_length)])
+ # Generates some random indices to use for comparing input and
+ # output data (a full comparison is too slow in python).
+ self.indices = alignment.random_sample(
+ self.max_data_length, self.max_num_samples, self.seed)
+
+ for constellation, differential in tested_constellations():
+ # The constellation_receiver doesn't work for constellations
+ # of multple dimensions (i.e. multiple complex numbers to a
+ # single symbol).
+ # That is not implemented since the receiver has no way of
+ # knowing where the beginning of a symbol is.
+ # It also doesn't work for non-differential modulation.
+ if constellation.dimensionality() != 1 or not differential:
+ continue
+ data_length = DATA_LENGTH * constellation.bits_per_symbol()
+ tb = rec_test_tb(constellation, differential,
+ src_data=self.src_data[:data_length])
+ tb.run()
+ data = tb.dst.data()
+ d1 = tb.src_data[:int(len(tb.src_data)*self.ignore_fraction)]
+ d2 = data[:int(len(data)*self.ignore_fraction)]
+ correct, overlap, offset, indices = alignment.align_sequences(
+ d1, d2, indices=self.indices)
+ self.assertTrue(correct > REQ_CORRECT)
+
+
+class rec_test_tb (gr.top_block):
+ """
+ Takes a constellation an runs a generic modulation, channel,
+ and generic demodulation.
+ """
+ def __init__(self, constellation, differential,
+ data_length=None, src_data=None):
+ """
+ constellation -- a constellation object
+ differential -- whether differential encoding is used
+ data_length -- the number of bits of data to use
+ src_data -- a list of the bits to use
+ """
+ super(rec_test_tb, self).__init__()
+ # Transmission Blocks
+ if src_data is None:
+ self.src_data = tuple([random.randint(0,1) for i in range(0, data_length)])
+ else:
+ self.src_data = src_data
+ packer = gr.unpacked_to_packed_bb(1, gr.GR_MSB_FIRST)
+ src = gr.vector_source_b(self.src_data)
+ mod = blks2.generic_mod(constellation, differential=differential)
+ # Channel
+ channel = gr.channel_model(NOISE_VOLTAGE, FREQUENCY_OFFSET, TIMING_OFFSET)
+ # Receiver Blocks
+ demod = blks2.generic_demod(constellation, differential=differential,
+ freq_alpha=FREQ_ALPHA,
+ phase_alpha=PHASE_ALPHA)
+ self.dst = gr.vector_sink_b()
+ self.connect(src, packer, mod, channel, demod, self.dst)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_constellation_receiver, "test_constellation_receiver.xml")
diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_int_to_float.py b/gnuradio-core/src/python/gnuradio/gr/qa_int_to_float.py
new file mode 100755
index 000000000..edfc26409
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/gr/qa_int_to_float.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+#
+# Copyright 2011 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+
+class test_int_to_float (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_001(self):
+
+ src_data = (0, 1, 2, 3, 4, 5, -1, -2, -3, -4, -5)
+ expected_result = [float(s) for s in src_data]
+ src = gr.vector_source_i(src_data)
+ op = gr.int_to_float()
+ dst = gr.vector_sink_f()
+
+ self.tb.connect(src, op, dst)
+ self.tb.run()
+ result_data = dst.data()
+
+ self.assertFloatTuplesAlmostEqual(expected_result, result_data)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_int_to_float, "test_int_to_float.xml")
+
diff --git a/gnuradio-core/src/python/gnuradio/gr_xmlrunner.py b/gnuradio-core/src/python/gnuradio/gr_xmlrunner.py
index ded77f5f3..c3dc5cf13 100644
--- a/gnuradio-core/src/python/gnuradio/gr_xmlrunner.py
+++ b/gnuradio-core/src/python/gnuradio/gr_xmlrunner.py
@@ -6,8 +6,6 @@ XML Test Runner for PyUnit
# the Public Domain. With contributions by Paolo Borelli and others.
# Added to GNU Radio Oct. 3, 2010
-from __future__ import with_statement
-
__version__ = "0.1"
import os.path
@@ -185,7 +183,9 @@ class XMLTestRunner(object):
result = _XMLTestResult(classname)
start_time = time.time()
- with _fake_std_streams():
+ fss = _fake_std_streams()
+ fss.__enter__()
+ try:
test(result)
try:
out_s = sys.stdout.getvalue()
@@ -195,6 +195,8 @@ class XMLTestRunner(object):
err_s = sys.stderr.getvalue()
except AttributeError:
err_s = ""
+ finally:
+ fss.__exit__(None, None, None)
time_taken = time.time() - start_time
result.print_report(stream, time_taken, out_s, err_s)
@@ -218,8 +220,8 @@ class _fake_std_streams(object):
def __enter__(self):
self._orig_stdout = sys.stdout
self._orig_stderr = sys.stderr
- sys.stdout = StringIO()
- sys.stderr = StringIO()
+ #sys.stdout = StringIO()
+ #sys.stderr = StringIO()
def __exit__(self, exc_type, exc_val, exc_tb):
sys.stdout = self._orig_stdout
diff --git a/gnuradio-core/src/python/gnuradio/modulation_utils2.py b/gnuradio-core/src/python/gnuradio/modulation_utils2.py
index c5dba3e79..f30055f4a 100644
--- a/gnuradio-core/src/python/gnuradio/modulation_utils2.py
+++ b/gnuradio-core/src/python/gnuradio/modulation_utils2.py
@@ -47,6 +47,15 @@ def type_1_demods():
def add_type_1_demod(name, demod_class):
_type_1_demodulators[name] = demod_class
+# Also record the constellation making functions of the modulations
+_type_1_constellations = {}
+
+def type_1_constellations():
+ return _type_1_constellations
+
+def add_type_1_constellation(name, constellation):
+ _type_1_constellations[name] = constellation
+
def extract_kwargs_from_options(function, excluded_args, options):
"""
@@ -79,3 +88,14 @@ def extract_kwargs_from_options(function, excluded_args, options):
if getattr(options, kw) is not None:
d[kw] = getattr(options, kw)
return d
+
+def extract_kwargs_from_options_for_class(cls, options):
+ """
+ Given command line options, create dictionary suitable for passing to __init__
+ """
+ d = extract_kwargs_from_options(
+ cls.__init__, ('self',), options)
+ for base in cls.__bases__:
+ if hasattr(base, 'extract_kwargs_from_options'):
+ d.update(base.extract_kwargs_from_options(options))
+ return d
diff --git a/gnuradio-core/src/python/gnuradio/utils/Makefile.am b/gnuradio-core/src/python/gnuradio/utils/Makefile.am
new file mode 100644
index 000000000..c35951b44
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/utils/Makefile.am
@@ -0,0 +1,35 @@
+#
+# Copyright 2007,2009 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+include $(top_srcdir)/Makefile.common
+
+if PYTHON
+utilspythondir = $(grpythondir)/utils
+
+TESTS = \
+ run_tests
+
+nobase_utilspython_PYTHON = \
+ __init__.py \
+ gray_code.py \
+ mod_codes.py \
+ alignment.py
+endif \ No newline at end of file
diff --git a/gnuradio-core/src/python/gnuradio/utils/__init__.py b/gnuradio-core/src/python/gnuradio/utils/__init__.py
new file mode 100644
index 000000000..b3e997f9f
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/utils/__init__.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+#
+# Copyright 2011 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
diff --git a/gnuradio-core/src/python/gnuradio/utils/alignment.py b/gnuradio-core/src/python/gnuradio/utils/alignment.py
new file mode 100644
index 000000000..d32365866
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/utils/alignment.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python
+#
+# Copyright 2011 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+"""
+This module contains functions for aligning sequences.
+
+>>> import random
+>>> random.seed(1234)
+>>> ran_seq = [random.randint(0,1) for i in range(0, 100)]
+>>> offset_seq = [0] * 20 + ran_seq
+>>> correct, overlap, offset = align_sequences(ran_seq, offset_seq)
+>>> print(correct, overlap, offset)
+(1.0, 100, -20)
+>>> offset_err_seq = []
+>>> for bit in offset_seq:
+... if random.randint(0,4) == 4:
+... offset_err_seq.append(random.randint(0,1))
+... else:
+... offset_err_seq.append(bit)
+>>> correct, overlap, offset = align_sequences(ran_seq, offset_err_seq)
+>>> print(overlap, offset)
+(100, -20)
+
+"""
+
+import random
+
+# DEFAULT PARAMETERS
+# If the fraction of matching bits between two sequences is greater than
+# this the sequences are assumed to be aligned.
+def_correct_cutoff = 0.9
+# The maximum offset to test during sequence alignment.
+def_max_offset = 500
+# The maximum number of samples to take from two sequences to check alignment.
+def_num_samples = 1000
+
+def compare_sequences(d1, d2, offset, sample_indices=None):
+ """
+ Takes two binary sequences and an offset and returns the number of
+ matching entries and the number of compared entries.
+ d1 & d2 -- sequences
+ offset -- offset of d2 relative to d1
+ sample_indices -- a list of indices to use for the comparison
+ """
+ max_index = min(len(d1), len(d2)+offset)
+ if sample_indices is None:
+ sample_indices = range(0, max_index)
+ correct = 0
+ total = 0
+ for i in sample_indices:
+ if i >= max_index:
+ break
+ if d1[i] == d2[i-offset]:
+ correct += 1
+ total += 1
+ return (correct, total)
+
+def random_sample(size, num_samples=def_num_samples, seed=None):
+ """
+ Returns a set of random integers between 0 and (size-1).
+ The set contains no more than num_samples integers.
+ """
+ random.seed(seed)
+ if num_samples > size:
+ indices = set(range(0, size))
+ else:
+ if num_samples > size/2:
+ num_samples = num_samples/2
+ indices = set([])
+ while len(indices) < num_samples:
+ index = random.randint(0, size-1)
+ indices.add(index)
+ indices = list(indices)
+ indices.sort()
+ return indices
+
+def align_sequences(d1, d2,
+ num_samples=def_num_samples,
+ max_offset=def_max_offset,
+ correct_cutoff=def_correct_cutoff,
+ seed=None,
+ indices=None):
+ """
+ Takes two sequences and finds the offset and which the two sequences best
+ match. It returns the fraction correct, the number of entries compared,
+ the offset.
+ d1 & d2 -- sequences to compare
+ num_samples -- the maximum number of entries to compare
+ max_offset -- the maximum offset between the sequences that is checked
+ correct_cutoff -- If the fraction of bits correct is greater than this then
+ the offset is assumed to optimum.
+ seed -- a random number seed
+ indices -- an explicit list of the indices used to compare the two sequences
+ """
+ max_overlap = max(len(d1), len(d2))
+ if indices is None:
+ indices = random_sample(max_overlap, num_samples, seed)
+ max_frac_correct = 0
+ best_offset = None
+ best_compared = None
+ best_correct = None
+ pos_range = range(0, min(len(d1), max_offset))
+ neg_range = range(-1, -min(len(d2), max_offset), -1)
+ # Interleave the positive and negative offsets.
+ int_range = [item for items in zip(pos_range, neg_range) for item in items]
+ for offset in int_range:
+ correct, compared = compare_sequences(d1, d2, offset, indices)
+ frac_correct = 1.0*correct/compared
+ if frac_correct > max_frac_correct:
+ max_frac_correct = frac_correct
+ best_offset = offset
+ best_compared = compared
+ best_correct = correct
+ if frac_correct > correct_cutoff:
+ break
+ return max_frac_correct, best_compared, best_offset, indices
+
+if __name__ == "__main__":
+ import doctest
+ doctest.testmod()
+
diff --git a/gnuradio-core/src/python/gnuradio/utils/gray_code.py b/gnuradio-core/src/python/gnuradio/utils/gray_code.py
new file mode 100644
index 000000000..926a1ded1
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/utils/gray_code.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+#
+# Copyright 2011 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+class GrayCodeGenerator(object):
+ """
+ Generates and caches gray codes.
+ """
+
+ def __init__(self):
+ self.gcs = [0, 1]
+ # The last power of two passed through.
+ self.lp2 = 2
+ # The next power of two that will be passed through.
+ self.np2 = 4
+ # Curent index
+ self.i = 2
+
+ def get_gray_code(self, length):
+ """
+ Returns a list of gray code of given length.
+ """
+ if len(self.gcs) < length:
+ self.generate_new_gray_code(length)
+ return self.gcs[:length]
+
+ def generate_new_gray_code(self, length):
+ """
+ Generates new gray code and places into cache.
+ """
+ while len(self.gcs) < length:
+ if self.i == self.lp2:
+ # if i is a power of two then gray number is of form 1100000...
+ result = self.i + self.i/2
+ else:
+ # if not we take advantage of the symmetry of all but the last bit
+ # around a power of two.
+ result = self.gcs[2*self.lp2-1-self.i] + self.lp2
+ self.gcs.append(result)
+ self.i += 1
+ if self.i == self.np2:
+ self.lp2 = self.i
+ self.np2 = self.i*2
+
+_gray_code_generator = GrayCodeGenerator()
+
+gray_code = _gray_code_generator.get_gray_code
+
diff --git a/gnuradio-core/src/python/gnuradio/utils/mod_codes.py b/gnuradio-core/src/python/gnuradio/utils/mod_codes.py
new file mode 100644
index 000000000..caacda5cc
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/utils/mod_codes.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+#
+# Copyright 2011 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+GRAY_CODE = 'gray'
+SET_PARTITION_CODE = 'set-partition'
+NO_CODE = 'none'
+
+codes = (GRAY_CODE, SET_PARTITION_CODE, NO_CODE)
+
+def invert_code(code):
+ c = enumerate(code)
+ ic = [(b, a) for (a, b) in c]
+ ic.sort()
+ return [a for (b, a) in ic]
diff --git a/gnuradio-core/src/python/gnuradio/utils/run_tests.in b/gnuradio-core/src/python/gnuradio/utils/run_tests.in
new file mode 100644
index 000000000..adcbdfd21
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/utils/run_tests.in
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# 1st parameter is absolute path to component source directory
+# 2nd parameter is absolute path to component build directory
+# 3rd parameter is path to Python QA directory
+
+# Note: calling master run_tests.sh in gnuradio core is not strictly
+# correct, as it will result in a partially bogus PYTHONPATH, but it
+# does make the correct paths in the second half so all is well.
+
+# Nothing in here at the moment.