diff options
Diffstat (limited to 'gr-digital')
49 files changed, 1525 insertions, 862 deletions
diff --git a/gr-digital/CMakeLists.txt b/gr-digital/CMakeLists.txt index cc00da69a9..bd11be4d32 100644 --- a/gr-digital/CMakeLists.txt +++ b/gr-digital/CMakeLists.txt @@ -33,6 +33,7 @@ GR_REGISTER_COMPONENT("gr-digital" ENABLE_GR_DIGITAL GR_SET_GLOBAL(GR_DIGITAL_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/lib + ${CMAKE_CURRENT_SOURCE_DIR}/hier ) GR_SET_GLOBAL(GR_DIGITAL_SWIG_INCLUDE_DIRS @@ -81,11 +82,13 @@ CPACK_COMPONENT("digital_swig" ######################################################################## # Add subdirectories ######################################################################## +GR_INCLUDE_SUBDIRECTORY(hier) #appends to lib sources ADD_SUBDIRECTORY(lib) IF(ENABLE_PYTHON) ADD_SUBDIRECTORY(swig) ADD_SUBDIRECTORY(python) ADD_SUBDIRECTORY(grc) + ADD_SUBDIRECTORY(examples) ENDIF(ENABLE_PYTHON) ######################################################################## diff --git a/gr-digital/Makefile.am b/gr-digital/Makefile.am index f1409793ff..346db120f5 100644 --- a/gr-digital/Makefile.am +++ b/gr-digital/Makefile.am @@ -21,7 +21,7 @@ include $(top_srcdir)/Makefile.common -SUBDIRS = lib +SUBDIRS = hier lib if PYTHON SUBDIRS += swig python apps grc examples diff --git a/gr-digital/examples/CMakeLists.txt b/gr-digital/examples/CMakeLists.txt new file mode 100644 index 0000000000..55a8379d9a --- /dev/null +++ b/gr-digital/examples/CMakeLists.txt @@ -0,0 +1,27 @@ +# 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. + +INCLUDE(GrPython) + +GR_PYTHON_INSTALL(PROGRAMS + transmit_path.py + receive_path.py + DESTINATION ${GR_PKG_DATA_DIR}/examples/digital + COMPONENT "digital_python" +) diff --git a/gr-digital/examples/benchmark_add_channel.py b/gr-digital/examples/benchmark_add_channel.py new file mode 100755 index 0000000000..def1f8267e --- /dev/null +++ b/gr-digital/examples/benchmark_add_channel.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python +# +# Copyright 2010,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +import random, math, sys + +class my_top_block(gr.top_block): + def __init__(self, ifile, ofile, options): + gr.top_block.__init__(self) + + SNR = 10.0**(options.snr/10.0) + frequency_offset = options.frequency_offset + time_offset = options.time_offset + phase_offset = options.phase_offset*(math.pi/180.0) + + # calculate noise voltage from SNR + # FIXME: normalize to signal amplitude + power_in_signal = abs(1.0)**2 + noise_power = power_in_signal/SNR + noise_voltage = math.sqrt(noise_power) + print noise_voltage + + self.src = gr.file_source(gr.sizeof_gr_complex, ifile) + #self.throttle = gr.throttle(gr.sizeof_gr_complex, options.sample_rate) + self.channel = gr.channel_model(noise_voltage, frequency_offset, + time_offset, noise_seed=random.randint(0,100000)) + self.phase = gr.multiply_const_cc(complex(math.cos(phase_offset), + math.sin(phase_offset))) + self.snk = gr.file_sink(gr.sizeof_gr_complex, ofile) + + self.connect(self.src, self.channel, self.phase, self.snk) + + +# ///////////////////////////////////////////////////////////////////////////// +# main +# ///////////////////////////////////////////////////////////////////////////// + +def main(): + # Create Options Parser: + usage = "benchmack_add_channel.py [options] <input file> <output file>" + parser = OptionParser (usage=usage, option_class=eng_option, conflict_handler="resolve") + parser.add_option("-n", "--snr", type="eng_float", default=30, + help="set the SNR of the channel in dB [default=%default]") + parser.add_option("", "--seed", action="store_true", default=False, + help="use a random seed for AWGN noise [default=%default]") + parser.add_option("-f", "--frequency-offset", type="eng_float", default=0, + help="set frequency offset introduced by channel [default=%default]") + parser.add_option("-t", "--time-offset", type="eng_float", default=1.0, + help="set timing offset between Tx and Rx [default=%default]") + parser.add_option("-p", "--phase-offset", type="eng_float", default=0, + help="set phase offset (in degrees) between Tx and Rx [default=%default]") + parser.add_option("-m", "--use-multipath", action="store_true", default=False, + help="Use a multipath channel [default=%default]") + + (options, args) = parser.parse_args () + + if len(args) != 2: + parser.print_help(sys.stderr) + sys.exit(1) + + ifile = args[0] + ofile = args[1] + + # build the graph + tb = my_top_block(ifile, ofile, options) + + r = gr.enable_realtime_scheduling() + if r != gr.RT_OK: + print "Warning: Failed to enable realtime scheduling." + + tb.start() # start flow graph + tb.wait() # wait for it to finish + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass diff --git a/gr-digital/grc/Makefile.am b/gr-digital/grc/Makefile.am index d271ca06a8..963939f127 100644 --- a/gr-digital/grc/Makefile.am +++ b/gr-digital/grc/Makefile.am @@ -34,4 +34,6 @@ dist_grcblocks_DATA = \ digital_psk_mod.xml \ digital_psk_demod.xml \ digital_qam_mod.xml \ - digital_qam_demod.xml + digital_qam_demod.xml \ + digital_fll_band_edge_cc.xml \ + digital_mpsk_receiver_cc.xml diff --git a/gr-digital/grc/digital_block_tree.xml b/gr-digital/grc/digital_block_tree.xml index 1be4110e17..3432d876c1 100644 --- a/gr-digital/grc/digital_block_tree.xml +++ b/gr-digital/grc/digital_block_tree.xml @@ -34,6 +34,8 @@ <block>digital_cma_equalizer_cc</block> <block>digital_lms_dd_equalizer_cc</block> <block>digital_kurtotic_equalizer_cc</block> + <block>digital_fll_band_edge_cc</block> + <block>digital_mpsk_receiver_cc</block> </cat> <cat> <name>Digital Modulators</name> diff --git a/gr-digital/grc/digital_costas_loop_cc.xml b/gr-digital/grc/digital_costas_loop_cc.xml index 087535b879..668c43dec6 100644 --- a/gr-digital/grc/digital_costas_loop_cc.xml +++ b/gr-digital/grc/digital_costas_loop_cc.xml @@ -8,16 +8,10 @@ <name>Costas Loop</name> <key>digital_costas_loop_cc</key> <import>from gnuradio import digital</import> - <make>digital.costas_loop_cc($eta, $w, $order)</make> - <callback>set_damping_factor($eta)</callback> - <callback>set_natural_freq($w)</callback> + <make>digital.costas_loop_cc($w, $order)</make> + <callback>set_loop_bandwidth($w)</callback> <param> - <name>Damping Factor</name> - <key>eta</key> - <type>real</type> - </param> - <param> - <name>Natural Frequency</name> + <name>Loop Bandwidth</name> <key>w</key> <type>real</type> </param> diff --git a/gr-digital/grc/digital_fll_band_edge_cc.xml b/gr-digital/grc/digital_fll_band_edge_cc.xml new file mode 100644 index 0000000000..1c5a34e540 --- /dev/null +++ b/gr-digital/grc/digital_fll_band_edge_cc.xml @@ -0,0 +1,70 @@ +<?xml version="1.0"?> +<!-- +################################################### +## FLL using Band-Edge Filters +################################################### + --> +<block> + <name>FLL Band-Edge</name> + <key>gr_fll_band_edge_cc</key> + <import>from gnuradio import gr</import> + <make>gr.fll_band_edge_cc($samps_per_sym, $rolloff, $filter_size, $w)</make> + <callback>set_loop_bandwidth($w)</callback> + <param> + <name>Type</name> + <key>type</key> + <type>enum</type> + <option> + <name>Complex->Complex</name> + <key>cc</key> + <opt>input:complex</opt> + <opt>output:complex</opt> + </option> + </param> + + <param> + <name>Samples Per Symbol</name> + <key>samps_per_sym</key> + <type>real</type> + </param> + <param> + <name>Filter Rolloff Factor</name> + <key>rolloff</key> + <type>real</type> + </param> + <param> + <name>Prototype Filter Size</name> + <key>filter_size</key> + <type>int</type> + </param> + + <param> + <name>Loop Bandwidth</name> + <key>w</key> + <type>real</type> + </param> + + <sink> + <name>in</name> + <type>$type.input</type> + </sink> + <source> + <name>out</name> + <type>$type.output</type> + </source> + <source> + <name>freq</name> + <type>float</type> + <optional>1</optional> + </source> + <source> + <name>phase</name> + <type>float</type> + <optional>1</optional> + </source> + <source> + <name>error</name> + <type>float</type> + <optional>1</optional> + </source> +</block> diff --git a/gr-digital/grc/digital_mpsk_receiver_cc.xml b/gr-digital/grc/digital_mpsk_receiver_cc.xml new file mode 100644 index 0000000000..948f18b6e6 --- /dev/null +++ b/gr-digital/grc/digital_mpsk_receiver_cc.xml @@ -0,0 +1,75 @@ +<?xml version="1.0"?> +<!-- +################################################### +##MPSK Receiver +################################################### + --> +<block> + <name>MPSK Receiver</name> + <key>gr_mpsk_receiver_cc</key> + <import>from gnuradio import gr</import> + <make>gr.mpsk_receiver_cc($M, $theta, $w, $fmin, $fmax, $mu, $gain_mu, $omega, $gain_omega, $omega_relative_limit)</make> + <callback>set_loop_bandwidth($w)</callback> + <callback>set_mu($mu)</callback> + <callback>set_gain_mu($gain_mu)</callback> + <callback>set_omega($omega)</callback> + <callback>set_gain_omega($gain_omega)</callback> + <param> + <name>M</name> + <key>M</key> + <type>int</type> + </param> + <param> + <name>Theta</name> + <key>theta</key> + <type>real</type> + </param> + <param> + <name>Loop Bandwidth</name> + <key>w</key> + <type>real</type> + </param> + <param> + <name>Min Freq</name> + <key>fmin</key> + <type>real</type> + </param> + <param> + <name>Max Freq</name> + <key>fmax</key> + <type>real</type> + </param> + <param> + <name>Mu</name> + <key>mu</key> + <type>real</type> + </param> + <param> + <name>Gain Mu</name> + <key>gain_mu</key> + <type>real</type> + </param> + <param> + <name>Omega</name> + <key>omega</key> + <type>real</type> + </param> + <param> + <name>Gain Omega</name> + <key>gain_omega</key> + <type>real</type> + </param> + <param> + <name>Omega Relative Limit</name> + <key>omega_relative_limit</key> + <type>real</type> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> +</block> diff --git a/gr-digital/hier/.gitignore b/gr-digital/hier/.gitignore new file mode 100644 index 0000000000..b336cc7cec --- /dev/null +++ b/gr-digital/hier/.gitignore @@ -0,0 +1,2 @@ +/Makefile +/Makefile.in diff --git a/gr-digital/hier/CMakeLists.txt b/gr-digital/hier/CMakeLists.txt new file mode 100644 index 0000000000..f46acc81e0 --- /dev/null +++ b/gr-digital/hier/CMakeLists.txt @@ -0,0 +1,43 @@ +# 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 file included, use CMake directory variables +######################################################################## + +LIST(APPEND gr_digital_sources + ${CMAKE_CURRENT_SOURCE_DIR}/digital_gmskmod_bc.cc + ${CMAKE_CURRENT_SOURCE_DIR}/digital_cpmmod_bc.cc +) + +INSTALL(FILES + ${CMAKE_CURRENT_SOURCE_DIR}/digital_gmskmod_bc.h + ${CMAKE_CURRENT_SOURCE_DIR}/digital_cpmmod_bc.h + DESTINATION ${GR_INCLUDE_DIR}/gnuradio + COMPONENT "digital_devel" +) + +INSTALL( + FILES + ${CMAKE_CURRENT_SOURCE_DIR}/_digital_hier.i + ${CMAKE_CURRENT_SOURCE_DIR}/digital_gmskmod_bc.i + ${CMAKE_CURRENT_SOURCE_DIR}/digital_cpmmod_bc.i + DESTINATION ${GR_INCLUDE_DIR}/gnuradio/swig + COMPONENT "digital_swig" +) diff --git a/gr-digital/hier/Makefile.am b/gr-digital/hier/Makefile.am new file mode 100644 index 0000000000..b11b85f77c --- /dev/null +++ b/gr-digital/hier/Makefile.am @@ -0,0 +1,40 @@ +# +# 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. +# + +include $(top_srcdir)/Makefile.common + +AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(WITH_INCLUDES) + +noinst_LTLIBRARIES = libdigital_hier.la + +libdigital_hier_la_SOURCES = \ + digital_gmskmod_bc.cc \ + digital_cpmmod_bc.cc + +grinclude_HEADERS = \ + digital_gmskmod_bc.h \ + digital_cpmmod_bc.h + + +swiginclude_HEADERS = \ + _digital_hier.i \ + digital_gmskmod_bc.i \ + digital_cpmmod_bc.i diff --git a/gr-digital/hier/_digital_hier.i b/gr-digital/hier/_digital_hier.i new file mode 100644 index 0000000000..022e38644b --- /dev/null +++ b/gr-digital/hier/_digital_hier.i @@ -0,0 +1,33 @@ +/* -*- c++ -*- */ +/* + * 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. + */ + +%{ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_cpmmod_bc.h> +#include <digital_gmskmod_bc.h> +%} + +%include "digital_cpmmod_bc.i" +%include "digital_gmskmod_bc.i" diff --git a/gr-digital/hier/digital_cpmmod_bc.cc b/gr-digital/hier/digital_cpmmod_bc.cc new file mode 100644 index 0000000000..a95b604d14 --- /dev/null +++ b/gr-digital/hier/digital_cpmmod_bc.cc @@ -0,0 +1,69 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Free Software Foundation, Inc. + * + * GNU Radio is free software; you can 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_cpmmod_bc.h> +#include <gr_io_signature.h> + + +// Shared pointer constructor +digital_cpmmod_bc_sptr +digital_make_cpmmod_bc(int type, float h, + unsigned samples_per_sym, + unsigned L, double beta) +{ + return gnuradio::get_initial_sptr(new digital_cpmmod_bc((gr_cpm::cpm_type)type, + h, samples_per_sym, + L, beta)); +} + + +digital_cpmmod_bc::digital_cpmmod_bc(gr_cpm::cpm_type type, float h, + unsigned samples_per_sym, + unsigned L, double beta) + : gr_hier_block2("digital_cpmmod_bc", + gr_make_io_signature(1, 1, sizeof(char)), + gr_make_io_signature2(1, 1, sizeof(gr_complex), sizeof(float))), + d_taps(gr_cpm::phase_response(type, samples_per_sym, L, beta)), + d_char_to_float(gr_make_char_to_float()), + d_pulse_shaper(gr_make_interp_fir_filter_fff(samples_per_sym, d_taps)), + d_fm(gr_make_frequency_modulator_fc(M_PI * h)) +{ + switch (type) { + case gr_cpm::LRC: + case gr_cpm::LSRC: + case gr_cpm::LREC: + case gr_cpm::TFM: + case gr_cpm::GAUSSIAN: + break; + + default: + throw std::invalid_argument("invalid CPM type"); + } + + connect(self(), 0, d_char_to_float, 0); + connect(d_char_to_float, 0, d_pulse_shaper, 0); + connect(d_pulse_shaper, 0, d_fm, 0); + connect(d_fm, 0, self(), 0); +} + diff --git a/gr-digital/hier/digital_cpmmod_bc.h b/gr-digital/hier/digital_cpmmod_bc.h new file mode 100644 index 0000000000..4e9547cd64 --- /dev/null +++ b/gr-digital/hier/digital_cpmmod_bc.h @@ -0,0 +1,96 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_CPMMOD_BC_H +#define INCLUDED_DIGITAL_CPMMOD_BC_H + +#include <digital_api.h> +#include <gr_hier_block2.h> +#include <gr_char_to_float.h> +#include <gr_interp_fir_filter_fff.h> +#include <gr_frequency_modulator_fc.h> +#include <gr_cpm.h> + + +class digital_cpmmod_bc; +typedef boost::shared_ptr<digital_cpmmod_bc> digital_cpmmod_bc_sptr; + + +DIGITAL_API digital_cpmmod_bc_sptr +digital_make_cpmmod_bc(int type, float h, + unsigned samples_per_sym, + unsigned L, double beta=0.3); + +/*! + * \brief Generic CPM modulator + * + * \ingroup modulation_blk + * + * \param type The modulation type. Can be one of LREC, LRC, LSRC, TFM + * or GAUSSIAN. See gr_cpm::phase_response() for a + * detailed description. + * \param h The modulation index. \f$ h \cdot \pi\f$ is the maximum + * phase change that can occur between two symbols, i.e., if + * you only send ones, the phase will increase by \f$ h \cdot + * \pi\f$ every \p samples_per_sym samples. Set this to 0.5 + * for Minimum Shift Keying variants. + * \param samples_per_sym Samples per symbol. + * \param L The length of the phase duration in symbols. For L=1, this + * yields full- response CPM symbols, for L > 1, + * partial-response. + * \param beta For LSRC, this is the rolloff factor. For Gaussian + * pulses, this is the 3 dB time-bandwidth product. + * + * Examples: + * - Setting h = 0.5, L = 1, type = LREC yields MSK. + * - Setting h = 0.5, type = GAUSSIAN and beta = 0.3 yields GMSK + * as used in GSM. + * + * The input of this block are symbols from an M-ary alphabet + * +/-1, +/-3, ..., +/-(M-1). Usually, M = 2 and therefore, the + * valid inputs are +/-1. + * The modulator will silently accept any other inputs, though. + * The output is the phase-modulated signal. + */ +class DIGITAL_API digital_cpmmod_bc : public gr_hier_block2 +{ + friend DIGITAL_API digital_cpmmod_bc_sptr digital_make_cpmmod_bc(int type, float h, + unsigned samples_per_sym, + unsigned L, double beta); + + std::vector<float> d_taps; + gr_char_to_float_sptr d_char_to_float; + gr_interp_fir_filter_fff_sptr d_pulse_shaper; + gr_frequency_modulator_fc_sptr d_fm; + +protected: + digital_cpmmod_bc(gr_cpm::cpm_type type, float h, + unsigned samples_per_sym, + unsigned L, double beta); + +public: + //! Return the phase response FIR taps + std::vector<float> get_taps() { return d_taps; }; +}; + +#endif /* INCLUDED_DIGITAL_CPMMOD_BC_H */ + diff --git a/gr-digital/hier/digital_cpmmod_bc.i b/gr-digital/hier/digital_cpmmod_bc.i new file mode 100644 index 0000000000..fa7c50da75 --- /dev/null +++ b/gr-digital/hier/digital_cpmmod_bc.i @@ -0,0 +1,40 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +GR_SWIG_BLOCK_MAGIC(digital, cpmmod_bc) + +digital_cpmmod_bc_sptr +digital_make_cpmmod_bc(int type, float h, + unsigned samples_per_sym, + unsigned L, double beta=0.3); + +class digital_cpmmod_bc : public gr_hier_block2 +{ + private: + digital_cpmmod_bc(int type, float h, + unsigned samples_per_sym, + unsigned L, double beta); + + public: + std::vector<float> get_taps(); +}; + diff --git a/gr-digital/hier/digital_gmskmod_bc.cc b/gr-digital/hier/digital_gmskmod_bc.cc new file mode 100644 index 0000000000..e53e900370 --- /dev/null +++ b/gr-digital/hier/digital_gmskmod_bc.cc @@ -0,0 +1,44 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_gmskmod_bc.h> +#include <gr_io_signature.h> + +// Shared pointer constructor +digital_gmskmod_bc_sptr +digital_make_gmskmod_bc(unsigned samples_per_sym, + double bt, unsigned L) +{ + return gnuradio::get_initial_sptr(new digital_gmskmod_bc(samples_per_sym, bt, L)); +} + + +digital_gmskmod_bc::digital_gmskmod_bc(unsigned samples_per_sym, + double bt, unsigned L) + : digital_cpmmod_bc(gr_cpm::GAUSSIAN, 0.5, samples_per_sym, L, bt) +{ +} + diff --git a/gr-digital/hier/digital_gmskmod_bc.h b/gr-digital/hier/digital_gmskmod_bc.h new file mode 100644 index 0000000000..33fcc6c126 --- /dev/null +++ b/gr-digital/hier/digital_gmskmod_bc.h @@ -0,0 +1,62 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_GMSKMOD_BC_H +#define INCLUDED_DIGITAL_GMSKMOD_BC_H + +#include <digital_api.h> +#include <digital_cpmmod_bc.h> + +class digital_gmskmod_bc; +typedef boost::shared_ptr<digital_gmskmod_bc> digital_gmskmod_bc_sptr; + + +DIGITAL_API digital_gmskmod_bc_sptr +digital_make_gmskmod_bc(unsigned samples_per_sym=2, + double bt=0.3, unsigned L=4); + +/*! + * \brief GMSK modulator + * + * \ingroup modulation_blk + * + * \param samples_per_sym Samples per symbol. + * \param bt The 3 dB time-bandwidth product. + * \param L The length of the phase duration in symbols. The Gaussian + * pulse is truncated after L symbols. + * + * The input of this block are symbols from an M-ary alphabet + * +/-1, +/-3, ..., +/-(M-1). Usually, M = 2 and therefore, the + * valid inputs are +/-1. + * The modulator will silently accept any other inputs, though. + * The output is the phase-modulated signal. + */ +class DIGITAL_API digital_gmskmod_bc : public digital_cpmmod_bc +{ + friend DIGITAL_API digital_gmskmod_bc_sptr digital_make_gmskmod_bc(unsigned samples_per_sym, + double bt, unsigned L); + digital_gmskmod_bc(unsigned samples_per_sym, + double bt, unsigned L); +}; + +#endif /* INCLUDED_DIGITAL_GMSKMOD_BC_H */ + diff --git a/gr-digital/hier/digital_gmskmod_bc.i b/gr-digital/hier/digital_gmskmod_bc.i new file mode 100644 index 0000000000..ad7b82237e --- /dev/null +++ b/gr-digital/hier/digital_gmskmod_bc.i @@ -0,0 +1,39 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +GR_SWIG_BLOCK_MAGIC(digital, gmskmod_bc) + +digital_gmskmod_bc_sptr +digital_make_gmskmod_bc(unsigned samples_per_sym=2, + double bt=0.3, unsigned L=4); + +class digital_gmskmod_bc : public gr_hier_block2 +{ + private: + digital_cpmmod_bc(int type, float h, + unsigned samples_per_sym, + double beta, unsigned L); + + public: + std::vector<float> get_taps(); +}; + diff --git a/gr-digital/lib/Makefile.am b/gr-digital/lib/Makefile.am index 4a9359fce2..ce961ef2ee 100644 --- a/gr-digital/lib/Makefile.am +++ b/gr-digital/lib/Makefile.am @@ -61,6 +61,7 @@ libgnuradio_digital_la_SOURCES = \ digital_mpsk_receiver_cc.cc libgnuradio_digital_la_LIBADD = \ - $(GNURADIO_CORE_LA) + $(GNURADIO_CORE_LA) \ + $(abs_top_builddir)/gr-digital/hier/libdigital_hier.la libgnuradio_digital_la_LDFLAGS = $(NO_UNDEFINED) $(LTVERSIONFLAGS) diff --git a/gr-digital/lib/digital_constellation.cc b/gr-digital/lib/digital_constellation.cc index d1f2184391..0c100f38ed 100644 --- a/gr-digital/lib/digital_constellation.cc +++ b/gr-digital/lib/digital_constellation.cc @@ -411,6 +411,20 @@ digital_constellation_qpsk::digital_constellation_qpsk () d_constellation[1] = gr_complex(SQRT_TWO, -SQRT_TWO); d_constellation[2] = gr_complex(-SQRT_TWO, SQRT_TWO); d_constellation[3] = gr_complex(SQRT_TWO, SQRT_TWO); + + /* + d_constellation[0] = gr_complex(SQRT_TWO, SQRT_TWO); + d_constellation[1] = gr_complex(-SQRT_TWO, SQRT_TWO); + d_constellation[2] = gr_complex(SQRT_TWO, -SQRT_TWO); + d_constellation[3] = gr_complex(SQRT_TWO, -SQRT_TWO); + */ + + d_pre_diff_code.resize(4); + d_pre_diff_code[0] = 0x0; + d_pre_diff_code[1] = 0x2; + d_pre_diff_code[2] = 0x3; + d_pre_diff_code[3] = 0x1; + d_rotational_symmetry = 4; d_dimensionality = 1; calc_arity(); @@ -422,8 +436,80 @@ digital_constellation_qpsk::decision_maker(const gr_complex *sample) // Real component determines small bit. // Imag component determines big bit. return 2*(imag(*sample)>0) + (real(*sample)>0); + + /* + bool a = real(*sample) > 0; + bool b = imag(*sample) > 0; + if(a) { + if(b) + return 0x0; + else + return 0x1; + } + else { + if(b) + return 0x2; + else + return 0x3; + } + */ +} + + +/********************************************************************/ + + +digital_constellation_dqpsk_sptr +digital_make_constellation_dqpsk() +{ + return digital_constellation_dqpsk_sptr(new digital_constellation_dqpsk ()); } +digital_constellation_dqpsk::digital_constellation_dqpsk () +{ + // This constellation is not gray coded, which allows + // us to use differential encodings (through gr_diff_encode and + // gr_diff_decode) on the symbols. + d_constellation.resize(4); + d_constellation[0] = gr_complex(+SQRT_TWO, +SQRT_TWO); + d_constellation[1] = gr_complex(-SQRT_TWO, +SQRT_TWO); + d_constellation[2] = gr_complex(-SQRT_TWO, -SQRT_TWO); + d_constellation[3] = gr_complex(+SQRT_TWO, -SQRT_TWO); + + // Use this mapping to convert to gray code before diff enc. + d_pre_diff_code.resize(4); + d_pre_diff_code[0] = 0x0; + d_pre_diff_code[1] = 0x1; + d_pre_diff_code[2] = 0x3; + d_pre_diff_code[3] = 0x2; + d_apply_pre_diff_code = true; + + d_rotational_symmetry = 4; + d_dimensionality = 1; + calc_arity(); +} + +unsigned int +digital_constellation_dqpsk::decision_maker(const gr_complex *sample) +{ + // Slower deicison maker as we can't slice along one axis. + // Maybe there's a better way to do this, still. + + bool a = real(*sample) > 0; + bool b = imag(*sample) > 0; + if(a) { + if(b) + return 0x0; + else + return 0x3; + } + else { + if(b) + return 0x1; + else + return 0x2; + } +} digital_constellation_8psk_sptr digital_make_constellation_8psk() diff --git a/gr-digital/lib/digital_constellation.h b/gr-digital/lib/digital_constellation.h index df00f38986..9b2a585883 100644 --- a/gr-digital/lib/digital_constellation.h +++ b/gr-digital/lib/digital_constellation.h @@ -42,8 +42,10 @@ typedef boost::shared_ptr<digital_constellation> digital_constellation_sptr; class DIGITAL_API digital_constellation : public boost::enable_shared_from_this<digital_constellation> { public: - digital_constellation (std::vector<gr_complex> constellation, std::vector<unsigned int> pre_diff_code, - unsigned int rotational_symmetry, unsigned int dimensionality); + digital_constellation (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality); digital_constellation (); //! Returns the constellation points for a symbol value @@ -74,6 +76,8 @@ public: std::vector<std::vector<gr_complex> > v_points(); //! Whether to apply an encoding before doing differential encoding. (e.g. gray coding) bool apply_pre_diff_code() { return d_apply_pre_diff_code;} + //! Whether to apply an encoding before doing differential encoding. (e.g. gray coding) + void set_pre_diff_code(bool a) { d_apply_pre_diff_code = a;} //! Returns the encoding to apply before differential encoding. std::vector<unsigned int> pre_diff_code() { return d_pre_diff_code;} //! Returns the order of rotational symmetry. @@ -82,7 +86,7 @@ public: unsigned int dimensionality() {return d_dimensionality;} unsigned int bits_per_symbol () { - return floor(log(double(d_constellation.size()))/d_dimensionality/log(2.0)); + return floor(log(d_constellation.size())/d_dimensionality/log(2)); } unsigned int arity () { @@ -120,17 +124,19 @@ typedef boost::shared_ptr<digital_constellation_calcdist> digital_constellation_ // public constructor DIGITAL_API digital_constellation_calcdist_sptr -digital_make_constellation_calcdist (std::vector<gr_complex> constellation, std::vector<unsigned int> pre_diff_code, - unsigned int rotational_symmetry, unsigned int dimensionality); +digital_make_constellation_calcdist (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality); class DIGITAL_API digital_constellation_calcdist : public digital_constellation { public: digital_constellation_calcdist (std::vector<gr_complex> constellation, - std::vector<unsigned int> pre_diff_code, - unsigned int rotational_symmetry, - unsigned int dimensionality); + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality); unsigned int decision_maker (const gr_complex *sample); // void calc_metric(gr_complex *sample, float *metric, trellis_metric_type_t type); // void calc_euclidean_metric(gr_complex *sample, float *metric); @@ -155,10 +161,10 @@ class DIGITAL_API digital_constellation_sector : public digital_constellation public: digital_constellation_sector (std::vector<gr_complex> constellation, - std::vector<unsigned int> pre_diff_code, - unsigned int rotational_symmetry, - unsigned int dimensionality, - unsigned int n_sectors); + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality, + unsigned int n_sectors); unsigned int decision_maker (const gr_complex *sample); @@ -194,19 +200,25 @@ typedef boost::shared_ptr<digital_constellation_rect> digital_constellation_rect // public constructor DIGITAL_API digital_constellation_rect_sptr -digital_make_constellation_rect (std::vector<gr_complex> constellation, std::vector<unsigned int> pre_diff_code, - unsigned int rotational_symmetry, - unsigned int real_sectors, unsigned int imag_sectors, - float width_real_sectors, float width_imag_sectors); +digital_make_constellation_rect (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int real_sectors, + unsigned int imag_sectors, + float width_real_sectors, + float width_imag_sectors); class DIGITAL_API digital_constellation_rect : public digital_constellation_sector { public: - digital_constellation_rect (std::vector<gr_complex> constellation, std::vector<unsigned int> pre_diff_code, - unsigned int rotational_symmetry, - unsigned int real_sectors, unsigned int imag_sectors, - float width_real_sectors, float width_imag_sectors); + digital_constellation_rect (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int real_sectors, + unsigned int imag_sectors, + float width_real_sectors, + float width_imag_sectors); protected: @@ -222,10 +234,13 @@ class DIGITAL_API digital_constellation_rect : public digital_constellation_sect float d_width_imag_sectors; friend DIGITAL_API digital_constellation_rect_sptr - digital_make_constellation_rect (std::vector<gr_complex> constellation, std::vector<unsigned int> pre_diff_code, - unsigned int rotational_symmetry, - unsigned int real_sectors, unsigned int imag_sectors, - float width_real_sectors, float width_imag_sectors); + digital_make_constellation_rect (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int real_sectors, + unsigned int imag_sectors, + float width_real_sectors, + float width_imag_sectors); }; @@ -323,6 +338,33 @@ class DIGITAL_API digital_constellation_qpsk : public digital_constellation }; +/************************************************************/ +/* digital_constellation_dqpsk */ +/* */ +/* Works with differential encoding; slower decisions. */ +/* */ +/************************************************************/ + +//! \brief DQPSK-specific constellation and decision maker +class digital_constellation_dqpsk; +typedef boost::shared_ptr<digital_constellation_dqpsk> digital_constellation_dqpsk_sptr; + +// public constructor +DIGITAL_API digital_constellation_dqpsk_sptr +digital_make_constellation_dqpsk (); + +class DIGITAL_API digital_constellation_dqpsk : public digital_constellation +{ + public: + + digital_constellation_dqpsk (); + unsigned int decision_maker (const gr_complex *sample); + + friend DIGITAL_API digital_constellation_dqpsk_sptr + digital_make_constellation_dqpsk (); + +}; + /************************************************************/ /* digital_constellation_8psk */ diff --git a/gr-digital/lib/digital_constellation_receiver_cb.cc b/gr-digital/lib/digital_constellation_receiver_cb.cc index e2b6bf1d80..b9239962a7 100644 --- a/gr-digital/lib/digital_constellation_receiver_cb.cc +++ b/gr-digital/lib/digital_constellation_receiver_cb.cc @@ -54,153 +54,20 @@ digital_constellation_receiver_cb::digital_constellation_receiver_cb (digital_co : gr_block ("constellation_receiver_cb", gr_make_io_signature (1, 1, sizeof (gr_complex)), gr_make_io_signaturev (1, 4, iosig)), - d_freq(0), d_max_freq(fmax), d_min_freq(fmin), d_phase(0), + gri_control_loop(loop_bw, fmax, fmin), d_constellation(constellation), d_current_const_point(0) { if (d_constellation->dimensionality() != 1) throw std::runtime_error ("This receiver only works with constellations of dimension 1."); - - // Set the damping factor for a critically damped system - d_damping = sqrtf(2.0f)/2.0f; - - // Set the bandwidth, which will then call update_gains() - set_loop_bandwidth(loop_bw); -} - - -/******************************************************************* - SET FUNCTIONS -*******************************************************************/ - -void -digital_constellation_receiver_cb::set_loop_bandwidth(float bw) -{ - if(bw < 0) { - throw std::out_of_range ("digital_constellation_receiver_cb: invalid bandwidth. Must be >= 0."); - } - - d_loop_bw = bw; - update_gains(); -} - -void -digital_constellation_receiver_cb::set_damping_factor(float df) -{ - if(df < 0 || df > 1.0) { - throw std::out_of_range ("digital_constellation_receiver_cb: invalid damping factor. Must be in [0,1]."); - } - - d_damping = df; - update_gains(); -} - -void -digital_constellation_receiver_cb::set_alpha(float alpha) -{ - if(alpha < 0 || alpha > 1.0) { - throw std::out_of_range ("digital_constellation_receiver_cb: invalid alpha. Must be in [0,1]."); - } - d_alpha = alpha; -} - -void -digital_constellation_receiver_cb::set_beta(float beta) -{ - if(beta < 0 || beta > 1.0) { - throw std::out_of_range ("digital_constellation_receiver_cb: invalid beta. Must be in [0,1]."); - } - d_beta = beta; -} - -void -digital_constellation_receiver_cb::set_frequency(float freq) -{ - if(freq > d_max_freq) - d_freq = d_min_freq; - else if(freq < d_min_freq) - d_freq = d_max_freq; - else - d_freq = freq; -} - -void -digital_constellation_receiver_cb::set_phase(float phase) -{ - d_phase = phase; - while(d_phase>M_TWOPI) - d_phase -= M_TWOPI; - while(d_phase<-M_TWOPI) - d_phase += M_TWOPI; -} - - -/******************************************************************* - GET FUNCTIONS -*******************************************************************/ - - -float -digital_constellation_receiver_cb::get_loop_bandwidth() const -{ - return d_loop_bw; -} - -float -digital_constellation_receiver_cb::get_damping_factor() const -{ - return d_damping; -} - -float -digital_constellation_receiver_cb::get_alpha() const -{ - return d_alpha; -} - -float -digital_constellation_receiver_cb::get_beta() const -{ - return d_beta; -} - -float -digital_constellation_receiver_cb::get_frequency() const -{ - return d_freq; -} - -float -digital_constellation_receiver_cb::get_phase() const -{ - return d_phase; -} - -/******************************************************************* -*******************************************************************/ - -void -digital_constellation_receiver_cb::update_gains() -{ - float denom = (1.0 + 2.0*d_damping*d_loop_bw + d_loop_bw*d_loop_bw); - d_alpha = (4*d_damping*d_loop_bw) / denom; - d_beta = (4*d_loop_bw*d_loop_bw) / denom; } void digital_constellation_receiver_cb::phase_error_tracking(float phase_error) { - d_freq += d_beta*phase_error; // adjust frequency based on error - d_phase += d_freq + d_alpha*phase_error; // adjust phase based on error - - // Make sure we stay within +-2pi - while(d_phase > M_TWOPI) - d_phase -= M_TWOPI; - while(d_phase < -M_TWOPI) - d_phase += M_TWOPI; - - // Limit the frequency range - d_freq = gr_branchless_clip(d_freq, d_max_freq); + advance_loop(phase_error); + phase_wrap(); + frequency_limit(); #if VERBOSE_COSTAS printf("cl: phase_error: %f phase: %f freq: %f sample: %f+j%f constellation: %f+j%f\n", @@ -236,10 +103,12 @@ digital_constellation_receiver_cb::general_work (int noutput_items, sample = in[i]; nco = gr_expj(d_phase); // get the NCO value for derotating the current sample sample = nco*sample; // get the downconverted symbol + sym_value = d_constellation->decision_maker_pe(&sample, &phase_error); - // phase_error = -arg(sample*conj(d_constellation->points()[sym_value])); phase_error_tracking(phase_error); // corrects phase and frequency offsets + out[i] = sym_value; + if(output_items.size() == 4) { out_err[i] = phase_error; out_phase[i] = d_phase; diff --git a/gr-digital/lib/digital_constellation_receiver_cb.h b/gr-digital/lib/digital_constellation_receiver_cb.h index 5e414dfed2..d33be89585 100644 --- a/gr-digital/lib/digital_constellation_receiver_cb.h +++ b/gr-digital/lib/digital_constellation_receiver_cb.h @@ -26,6 +26,7 @@ #include <digital_api.h> #include <gr_block.h> #include <digital_constellation.h> +#include <gri_control_loop.h> #include <gr_complex.h> #include <math.h> #include <fstream> @@ -64,134 +65,14 @@ digital_make_constellation_receiver_cb (digital_constellation_sptr constellation * */ -class DIGITAL_API digital_constellation_receiver_cb : public gr_block +class DIGITAL_API digital_constellation_receiver_cb : public gr_block, public gri_control_loop { - public: +public: int general_work (int noutput_items, gr_vector_int &ninput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); - - /******************************************************************* - SET FUNCTIONS - *******************************************************************/ - - /*! - * \brief Set the loop bandwidth - * - * Set the loop filter's bandwidth to \p bw. This should be between - * 2*pi/200 and 2*pi/100 (in rads/samp). It must also be a positive - * number. - * - * When a new damping factor is set, the gains, alpha and beta, of the loop - * are recalculated by a call to update_gains(). - * - * \param bw (float) new bandwidth - * - */ - void set_loop_bandwidth(float bw); - - /*! - * \brief Set the loop damping factor - * - * Set the loop filter's damping factor to \p df. The damping factor - * should be sqrt(2)/2.0 for critically damped systems. - * Set it to anything else only if you know what you are doing. It must - * be a number between 0 and 1. - * - * When a new damping factor is set, the gains, alpha and beta, of the loop - * are recalculated by a call to update_gains(). - * - * \param df (float) new damping factor - * - */ - void set_damping_factor(float df); - - /*! - * \brief Set the loop gain alpha - * - * Set's the loop filter's alpha gain parameter. - * - * This value should really only be set by adjusting the loop bandwidth - * and damping factor. - * - * \param alpha (float) new alpha gain - * - */ - void set_alpha(float alpha); - - /*! - * \brief Set the loop gain beta - * - * Set's the loop filter's beta gain parameter. - * - * This value should really only be set by adjusting the loop bandwidth - * and damping factor. - * - * \param beta (float) new beta gain - * - */ - void set_beta(float beta); - - /*! - * \brief Set the phase/freq recovery loop's frequency. - * - * Set's the phase/freq recovery loop's frequency. While this is normally - * updated by the inner loop of the algorithm, it could be useful to - * manually initialize, set, or reset this under certain circumstances. - * - * \param freq (float) new frequency - * - */ - void set_frequency(float freq); - - /*! - * \brief Set the phase/freq recovery loop's phase. - * - * Set's the phase/freq recovery loop's phase. While this is normally - * updated by the inner loop of the algorithm, it could be useful to - * manually initialize, set, or reset this under certain circumstances. - * - * \param phase (float) new phase - * - */ - void set_phase(float phase); - - /******************************************************************* - GET FUNCTIONS - *******************************************************************/ - - /*! - * \brief Returns the loop bandwidth - */ - float get_loop_bandwidth() const; - - /*! - * \brief Returns the loop damping factor - */ - float get_damping_factor() const; - - /*! - * \brief Returns the loop gain alpha - */ - float get_alpha() const; - - /*! - * \brief Returns the loop gain beta - */ - float get_beta() const; - - /*! - * \brief Get the phase/freq recovery loop's frequency estimate - */ - float get_frequency() const; - - /*! - * \brief Get the phase/freq loop's phase estimate - */ - float get_phase() const; - protected: /*! @@ -211,18 +92,9 @@ protected: void phase_error_tracking(float phase_error); - private: +private: unsigned int d_M; - // Members related to carrier and phase tracking - float d_freq, d_max_freq, d_min_freq; - float d_phase; - - float d_loop_bw; - float d_damping; - float d_alpha; - float d_beta; - digital_constellation_sptr d_constellation; unsigned int d_current_const_point; @@ -230,20 +102,11 @@ protected: static const unsigned int DLLEN = 8; //! delay line plus some length for overflow protection - __GR_ATTR_ALIGNED(8) gr_complex d_dl[2*DLLEN]; + gr_complex d_dl[2*DLLEN] __attribute__ ((aligned(8))); //! index to delay line unsigned int d_dl_idx; - /*! \brief update the system gains from the loop bandwidth and damping factor - * - * This function updates the system gains based on the loop - * bandwidth and damping factor of the system. - * These two factors can be set separately through their own - * set functions. - */ - void update_gains(); - friend DIGITAL_API digital_constellation_receiver_cb_sptr digital_make_constellation_receiver_cb (digital_constellation_sptr constell, float loop_bw, float fmin, float fmax); diff --git a/gr-digital/lib/digital_fll_band_edge_cc.cc b/gr-digital/lib/digital_fll_band_edge_cc.cc index 70cb543519..05c092622a 100644 --- a/gr-digital/lib/digital_fll_band_edge_cc.cc +++ b/gr-digital/lib/digital_fll_band_edge_cc.cc @@ -55,6 +55,7 @@ digital_fll_band_edge_cc::digital_fll_band_edge_cc (float samps_per_sym, float r : gr_sync_block ("fll_band_edge_cc", gr_make_io_signature (1, 1, sizeof(gr_complex)), gr_make_io_signaturev (1, 4, iosig)), + gri_control_loop(bandwidth, M_TWOPI*(2.0/samps_per_sym), -M_TWOPI*(2.0/samps_per_sym)), d_updated (false) { // Initialize samples per symbol @@ -75,22 +76,8 @@ digital_fll_band_edge_cc::digital_fll_band_edge_cc (float samps_per_sym, float r } d_filter_size = filter_size; - // base this on the number of samples per symbol - d_max_freq = M_TWOPI * (2.0/samps_per_sym); - d_min_freq = -M_TWOPI * (2.0/samps_per_sym); - - // Set the damping factor for a critically damped system - d_damping = sqrtf(2.0f)/2.0f; - - // Set the bandwidth, which will then call update_gains() - set_loop_bandwidth(bandwidth); - // Build the band edge filters design_filter(d_sps, d_rolloff, d_filter_size); - - // Initialize loop values - d_freq = 0; - d_phase = 0; } digital_fll_band_edge_cc::~digital_fll_band_edge_cc () @@ -102,47 +89,6 @@ digital_fll_band_edge_cc::~digital_fll_band_edge_cc () SET FUNCTIONS *******************************************************************/ - -void -digital_fll_band_edge_cc::set_loop_bandwidth(float bw) -{ - if(bw < 0) { - throw std::out_of_range ("digital_fll_band_edge_cc: invalid bandwidth. Must be >= 0."); - } - - d_loop_bw = bw; - update_gains(); -} - -void -digital_fll_band_edge_cc::set_damping_factor(float df) -{ - if(df < 0 || df > 1.0) { - throw std::out_of_range ("digital_fll_band_edge_cc: invalid damping factor. Must be in [0,1]."); - } - - d_damping = df; - update_gains(); -} - -void -digital_fll_band_edge_cc::set_alpha(float alpha) -{ - if(alpha < 0 || alpha > 1.0) { - throw std::out_of_range ("digital_fll_band_edge_cc: invalid alpha. Must be in [0,1]."); - } - d_alpha = alpha; -} - -void -digital_fll_band_edge_cc::set_beta(float beta) -{ - if(beta < 0 || beta > 1.0) { - throw std::out_of_range ("digital_fll_band_edge_cc: invalid beta. Must be in [0,1]."); - } - d_beta = beta; -} - void digital_fll_band_edge_cc::set_samples_per_symbol(float sps) { @@ -173,57 +119,10 @@ digital_fll_band_edge_cc::set_filter_size(int filter_size) design_filter(d_sps, d_rolloff, d_filter_size); } -void -digital_fll_band_edge_cc::set_frequency(float freq) -{ - if(freq > d_max_freq) - d_freq = d_min_freq; - else if(freq < d_min_freq) - d_freq = d_max_freq; - else - d_freq = freq; -} - -void -digital_fll_band_edge_cc::set_phase(float phase) -{ - d_phase = phase; - while(d_phase>M_TWOPI) - d_phase -= M_TWOPI; - while(d_phase<-M_TWOPI) - d_phase += M_TWOPI; -} - - /******************************************************************* GET FUNCTIONS *******************************************************************/ - -float -digital_fll_band_edge_cc::get_loop_bandwidth() const -{ - return d_loop_bw; -} - -float -digital_fll_band_edge_cc::get_damping_factor() const -{ - return d_damping; -} - -float -digital_fll_band_edge_cc::get_alpha() const -{ - return d_alpha; -} - -float -digital_fll_band_edge_cc::get_beta() const -{ - return d_beta; -} - float digital_fll_band_edge_cc::get_samples_per_symbol() const { @@ -242,31 +141,10 @@ digital_fll_band_edge_cc:: get_filter_size() const return d_filter_size; } -float -digital_fll_band_edge_cc::get_frequency() const -{ - return d_freq; -} - -float -digital_fll_band_edge_cc::get_phase() const -{ - return d_phase; -} - /******************************************************************* *******************************************************************/ - -void -digital_fll_band_edge_cc::update_gains() -{ - float denom = (1.0 + 2.0*d_damping*d_loop_bw + d_loop_bw*d_loop_bw); - d_alpha = (4*d_damping*d_loop_bw) / denom; - d_beta = (4*d_loop_bw*d_loop_bw) / denom; -} - void digital_fll_band_edge_cc::design_filter(float samps_per_sym, float rolloff, int filter_size) @@ -366,18 +244,9 @@ digital_fll_band_edge_cc::work (int noutput_items, } error = norm(out_lower) - norm(out_upper); - d_freq = d_freq + d_beta * error; - d_phase = d_phase + d_freq + d_alpha * error; - - if(d_phase > M_PI) - d_phase -= M_TWOPI; - else if(d_phase < -M_PI) - d_phase += M_TWOPI; - - if (d_freq > d_max_freq) - d_freq = d_max_freq; - else if (d_freq < d_min_freq) - d_freq = d_min_freq; + advance_loop(error); + phase_wrap(); + frequency_limit(); if(output_items.size() == 4) { frq[i] = d_freq; diff --git a/gr-digital/lib/digital_fll_band_edge_cc.h b/gr-digital/lib/digital_fll_band_edge_cc.h index c7a56a7c9c..6ef8376ace 100644 --- a/gr-digital/lib/digital_fll_band_edge_cc.h +++ b/gr-digital/lib/digital_fll_band_edge_cc.h @@ -26,6 +26,7 @@ #include <digital_api.h> #include <gr_sync_block.h> +#include <gri_control_loop.h> class digital_fll_band_edge_cc; typedef boost::shared_ptr<digital_fll_band_edge_cc> digital_fll_band_edge_cc_sptr; @@ -40,39 +41,51 @@ DIGITAL_API digital_fll_band_edge_cc_sptr digital_make_fll_band_edge_cc (float s * * \ingroup general * - * The frequency lock loop derives a band-edge filter that covers the upper and lower bandwidths - * of a digitally-modulated signal. The bandwidth range is determined by the excess bandwidth - * (e.g., rolloff factor) of the modulated signal. The placement in frequency of the band-edges - * is determined by the oversampling ratio (number of samples per symbol) and the excess bandwidth. - * The size of the filters should be fairly large so as to average over a number of symbols. + * The frequency lock loop derives a band-edge filter that covers the + * upper and lower bandwidths of a digitally-modulated signal. The + * bandwidth range is determined by the excess bandwidth (e.g., + * rolloff factor) of the modulated signal. The placement in frequency + * of the band-edges is determined by the oversampling ratio (number + * of samples per symbol) and the excess bandwidth. The size of the + * filters should be fairly large so as to average over a number of + * symbols. * - * The FLL works by filtering the upper and lower band edges into x_u(t) and x_l(t), respectively. - * These are combined to form cc(t) = x_u(t) + x_l(t) and ss(t) = x_u(t) - x_l(t). Combining - * these to form the signal e(t) = Re{cc(t) \\times ss(t)^*} (where ^* is the complex conjugate) - * provides an error signal at the DC term that is directly proportional to the carrier frequency. - * We then make a second-order loop using the error signal that is the running average of e(t). + * The FLL works by filtering the upper and lower band edges into + * x_u(t) and x_l(t), respectively. These are combined to form cc(t) + * = x_u(t) + x_l(t) and ss(t) = x_u(t) - x_l(t). Combining these to + * form the signal e(t) = Re{cc(t) \\times ss(t)^*} (where ^* is the + * complex conjugate) provides an error signal at the DC term that is + * directly proportional to the carrier frequency. We then make a + * second-order loop using the error signal that is the running + * average of e(t). * - * In practice, the above equation can be simplified by just comparing the absolute value squared - * of the output of both filters: abs(x_l(t))^2 - abs(x_u(t))^2 = norm(x_l(t)) - norm(x_u(t)). + * In practice, the above equation can be simplified by just comparing + * the absolute value squared of the output of both filters: + * abs(x_l(t))^2 - abs(x_u(t))^2 = norm(x_l(t)) - norm(x_u(t)). * - * In theory, the band-edge filter is the derivative of the matched filter in frequency, - * (H_be(f) = \\frac{H(f)}{df}. In practice, this comes down to a quarter sine wave at the point - * of the matched filter's rolloff (if it's a raised-cosine, the derivative of a cosine is a sine). - * Extend this sine by another quarter wave to make a half wave around the band-edges is equivalent - * in time to the sum of two sinc functions. The baseband filter fot the band edges is therefore - * derived from this sum of sincs. The band edge filters are then just the baseband signal - * modulated to the correct place in frequency. All of these calculations are done in the + * In theory, the band-edge filter is the derivative of the matched + * filter in frequency, (H_be(f) = \\frac{H(f)}{df}. In practice, this + * comes down to a quarter sine wave at the point of the matched + * filter's rolloff (if it's a raised-cosine, the derivative of a + * cosine is a sine). Extend this sine by another quarter wave to + * make a half wave around the band-edges is equivalent in time to the + * sum of two sinc functions. The baseband filter fot the band edges + * is therefore derived from this sum of sincs. The band edge filters + * are then just the baseband signal modulated to the correct place in + * frequency. All of these calculations are done in the * 'design_filter' function. * - * Note: We use FIR filters here because the filters have to have a flat phase response over the - * entire frequency range to allow their comparisons to be valid. + * Note: We use FIR filters here because the filters have to have a + * flat phase response over the entire frequency range to allow their + * comparisons to be valid. * - * It is very important that the band edge filters be the derivatives of the pulse shaping filter, - * and that they be linear phase. Otherwise, the variance of the error will be very large. + * It is very important that the band edge filters be the derivatives + * of the pulse shaping filter, and that they be linear + * phase. Otherwise, the variance of the error will be very large. * */ -class DIGITAL_API digital_fll_band_edge_cc : public gr_sync_block +class DIGITAL_API digital_fll_band_edge_cc : public gr_sync_block, public gri_control_loop { private: /*! @@ -90,21 +103,11 @@ class DIGITAL_API digital_fll_band_edge_cc : public gr_sync_block float d_sps; float d_rolloff; int d_filter_size; - float d_max_freq; - float d_min_freq; - - float d_loop_bw; - float d_damping; - float d_alpha; - float d_beta; std::vector<gr_complex> d_taps_lower; std::vector<gr_complex> d_taps_upper; bool d_updated; - float d_freq; - float d_phase; - /*! * Build the FLL * \param samps_per_sym (float) number of samples per symbol @@ -116,11 +119,6 @@ class DIGITAL_API digital_fll_band_edge_cc : public gr_sync_block int filter_size, float bandwidth); /*! - * \brief Update the gains, alpha and beta, of the loop filter. - */ - void update_gains(); - - /*! * Design the band-edge filter based on the number of samples per symbol, * filter rolloff factor, and the filter size * @@ -138,67 +136,11 @@ public: *******************************************************************/ /*! - * \brief Set the loop bandwidth - * - * Set the loop filter's bandwidth to \p bw. This should be between - * 2*pi/200 and 2*pi/100 (in rads/samp). It must also be a positive - * number. - * - * When a new damping factor is set, the gains, alpha and beta, of the loop - * are recalculated by a call to update_gains(). - * - * \param bw (float) new bandwidth - * - */ - void set_loop_bandwidth(float bw); - - /*! - * \brief Set the loop damping factor - * - * Set the loop filter's damping factor to \p df. The damping factor - * should be sqrt(2)/2.0 for critically damped systems. - * Set it to anything else only if you know what you are doing. It must - * be a number between 0 and 1. - * - * When a new damping factor is set, the gains, alpha and beta, of the loop - * are recalculated by a call to update_gains(). - * - * \param df (float) new damping factor - * - */ - void set_damping_factor(float df); - - /*! - * \brief Set the loop gain alpha - * - * Set's the loop filter's alpha gain parameter. - * - * This value should really only be set by adjusting the loop bandwidth - * and damping factor. - * - * \param alpha (float) new alpha gain - * - */ - void set_alpha(float alpha); - - /*! - * \brief Set the loop gain beta - * - * Set's the loop filter's beta gain parameter. - * - * This value should really only be set by adjusting the loop bandwidth - * and damping factor. - * - * \param beta (float) new beta gain - * - */ - void set_beta(float beta); - - /*! * \brief Set the number of samples per symbol * - * Set's the number of samples per symbol the system should use. This value - * is uesd to calculate the filter taps and will force a recalculation. + * Set's the number of samples per symbol the system should + * use. This value is uesd to calculate the filter taps and will + * force a recalculation. * * \param sps (float) new samples per symbol * @@ -208,13 +150,14 @@ public: /*! * \brief Set the rolloff factor of the shaping filter * - * This sets the rolloff factor that is used in the pulse shaping filter - * and is used to calculate the filter taps. Changing this will force a - * recalculation of the filter taps. + * This sets the rolloff factor that is used in the pulse shaping + * filter and is used to calculate the filter taps. Changing this + * will force a recalculation of the filter taps. * - * This should be the same value that is used in the transmitter's pulse - * shaping filter. It must be between 0 and 1 and is usually between - * 0.2 and 0.5 (where 0.22 and 0.35 are commonly used values). + * This should be the same value that is used in the transmitter's + * pulse shaping filter. It must be between 0 and 1 and is usually + * between 0.2 and 0.5 (where 0.22 and 0.35 are commonly used + * values). * * \param rolloff (float) new shaping filter rolloff factor [0,1] * @@ -224,68 +167,25 @@ public: /*! * \brief Set the number of taps in the filter * - * This sets the number of taps in the band-edge filters. Setting this will - * force a recalculation of the filter taps. + * This sets the number of taps in the band-edge filters. Setting + * this will force a recalculation of the filter taps. * - * This should be about the same number of taps used in the transmitter's - * shaping filter and also not very large. A large number of taps will - * result in a large delay between input and frequency estimation, and - * so will not be as accurate. Between 30 and 70 taps is usual. + * This should be about the same number of taps used in the + * transmitter's shaping filter and also not very large. A large + * number of taps will result in a large delay between input and + * frequency estimation, and so will not be as accurate. Between 30 + * and 70 taps is usual. * * \param filter_size (float) number of taps in the filters * */ void set_filter_size(int filter_size); - /*! - * \brief Set the FLL's frequency. - * - * Set's the FLL's frequency. While this is normally updated by the - * inner loop of the algorithm, it could be useful to manually initialize, - * set, or reset this under certain circumstances. - * - * \param freq (float) new frequency - * - */ - void set_frequency(float freq); - - /*! - * \brief Set the FLL's phase. - * - * Set's the FLL's phase. While this is normally updated by the - * inner loop of the algorithm, it could be useful to manually initialize, - * set, or reset this under certain circumstances. - * - * \param phase (float) new phase - * - */ - void set_phase(float phase); - /******************************************************************* GET FUNCTIONS *******************************************************************/ /*! - * \brief Returns the loop bandwidth - */ - float get_loop_bandwidth() const; - - /*! - * \brief Returns the loop damping factor - */ - float get_damping_factor() const; - - /*! - * \brief Returns the loop gain alpha - */ - float get_alpha() const; - - /*! - * \brief Returns the loop gain beta - */ - float get_beta() const; - - /*! * \brief Returns the number of sampler per symbol used for the filter */ float get_samples_per_symbol() const; @@ -301,16 +201,6 @@ public: int get_filter_size() const; /*! - * \brief Get the FLL's frequency estimate - */ - float get_frequency() const; - - /*! - * \brief Get the FLL's phase estimate - */ - float get_phase() const; - - /*! * Print the taps to screen. */ void print_taps(); diff --git a/gr-digital/lib/digital_mpsk_receiver_cc.cc b/gr-digital/lib/digital_mpsk_receiver_cc.cc index 3b2ea98404..363b86c9f2 100644 --- a/gr-digital/lib/digital_mpsk_receiver_cc.cc +++ b/gr-digital/lib/digital_mpsk_receiver_cc.cc @@ -41,28 +41,30 @@ digital_mpsk_receiver_cc_sptr digital_make_mpsk_receiver_cc(unsigned int M, float theta, - float alpha, float beta, + float loop_bw, float fmin, float fmax, float mu, float gain_mu, float omega, float gain_omega, float omega_rel) { return gnuradio::get_initial_sptr(new digital_mpsk_receiver_cc (M, theta, - alpha, beta, - fmin, fmax, - mu, gain_mu, - omega, gain_omega, omega_rel)); + loop_bw, + fmin, fmax, + mu, gain_mu, + omega, gain_omega, + omega_rel)); } digital_mpsk_receiver_cc::digital_mpsk_receiver_cc (unsigned int M, float theta, - float alpha, float beta, - float fmin, float fmax, - float mu, float gain_mu, - float omega, float gain_omega, float omega_rel) + float loop_bw, + float fmin, float fmax, + float mu, float gain_mu, + float omega, float gain_omega, + float omega_rel) : gr_block ("mpsk_receiver_cc", gr_make_io_signature (1, 1, sizeof (gr_complex)), gr_make_io_signature (1, 1, sizeof (gr_complex))), + gri_control_loop(loop_bw, fmax, fmin), d_M(M), d_theta(theta), - d_alpha(alpha), d_beta(beta), d_freq(0), d_max_freq(fmax), d_min_freq(fmin), d_phase(0), d_current_const_point(0), d_mu(mu), d_gain_mu(gain_mu), d_gain_omega(gain_omega), d_omega_rel(omega_rel), d_max_omega(0), d_min_omega(0), @@ -265,18 +267,10 @@ digital_mpsk_receiver_cc::phase_error_tracking(gr_complex sample) // Make phase and frequency corrections based on sampled value phase_error = (*this.*d_phase_error_detector)(sample); - - d_freq += d_beta*phase_error; // adjust frequency based on error - d_phase += d_freq + d_alpha*phase_error; // adjust phase based on error - - // Make sure we stay within +-2pi - while(d_phase > M_TWOPI) - d_phase -= M_TWOPI; - while(d_phase < -M_TWOPI) - d_phase += M_TWOPI; - // Limit the frequency range - d_freq = gr_branchless_clip(d_freq, d_max_freq); + advance_loop(phase_error); + phase_wrap(); + frequency_limit(); #if VERBOSE_COSTAS printf("cl: phase_error: %f phase: %f freq: %f sample: %f+j%f constellation: %f+j%f\n", diff --git a/gr-digital/lib/digital_mpsk_receiver_cc.h b/gr-digital/lib/digital_mpsk_receiver_cc.h index 8a6352ec7b..85cd81e992 100644 --- a/gr-digital/lib/digital_mpsk_receiver_cc.h +++ b/gr-digital/lib/digital_mpsk_receiver_cc.h @@ -25,6 +25,7 @@ #include <digital_api.h> #include <gruel/attributes.h> +#include <gri_control_loop.h> #include <gr_block.h> #include <gr_complex.h> #include <fstream> @@ -37,41 +38,48 @@ typedef boost::shared_ptr<digital_mpsk_receiver_cc> digital_mpsk_receiver_cc_spt // public constructor DIGITAL_API digital_mpsk_receiver_cc_sptr digital_make_mpsk_receiver_cc (unsigned int M, float theta, - float alpha, float beta, + float loop_bw, float fmin, float fmax, float mu, float gain_mu, float omega, float gain_omega, float omega_rel); /*! - * \brief This block takes care of receiving M-PSK modulated signals through phase, frequency, and symbol - * synchronization. + * \brief This block takes care of receiving M-PSK modulated signals + * through phase, frequency, and symbol synchronization. * \ingroup sync_blk * \ingroup demod_blk * - * This block takes care of receiving M-PSK modulated signals through phase, frequency, and symbol - * synchronization. It performs carrier frequency and phase locking as well as symbol timing recovery. - * It works with (D)BPSK, (D)QPSK, and (D)8PSK as tested currently. It should also work for OQPSK and - * PI/4 DQPSK. + * This block takes care of receiving M-PSK modulated signals through + * phase, frequency, and symbol synchronization. It performs carrier + * frequency and phase locking as well as symbol timing recovery. It + * works with (D)BPSK, (D)QPSK, and (D)8PSK as tested currently. It + * should also work for OQPSK and PI/4 DQPSK. * - * The phase and frequency synchronization are based on a Costas loop that finds the error of the incoming - * signal point compared to its nearest constellation point. The frequency and phase of the NCO are - * updated according to this error. There are optimized phase error detectors for BPSK and QPSK, but 8PSK - * is done using a brute-force computation of the constellation points to find the minimum. + * The phase and frequency synchronization are based on a Costas loop + * that finds the error of the incoming signal point compared to its + * nearest constellation point. The frequency and phase of the NCO are + * updated according to this error. There are optimized phase error + * detectors for BPSK and QPSK, but 8PSK is done using a brute-force + * computation of the constellation points to find the minimum. * - * The symbol synchronization is done using a modified Mueller and Muller circuit from the paper: + * The symbol synchronization is done using a modified Mueller and + * Muller circuit from the paper: * - * G. R. Danesfahani, T.G. Jeans, "Optimisation of modified Mueller and Muller - * algorithm," Electronics Letters, Vol. 31, no. 13, 22 June 1995, pp. 1032 - 1033. + * G. R. Danesfahani, T.G. Jeans, "Optimisation of modified Mueller + * and Muller algorithm," Electronics Letters, Vol. 31, no. 13, 22 + * June 1995, pp. 1032 - 1033. * - * This circuit interpolates the downconverted sample (using the NCO developed by the Costas loop) - * every mu samples, then it finds the sampling error based on this and the past symbols and the decision - * made on the samples. Like the phase error detector, there are optimized decision algorithms for BPSK - * and QPKS, but 8PSK uses another brute force computation against all possible symbols. The modifications - * to the M&M used here reduce self-noise. + * This circuit interpolates the downconverted sample (using the NCO + * developed by the Costas loop) every mu samples, then it finds the + * sampling error based on this and the past symbols and the decision + * made on the samples. Like the phase error detector, there are + * optimized decision algorithms for BPSK and QPKS, but 8PSK uses + * another brute force computation against all possible symbols. The + * modifications to the M&M used here reduce self-noise. * */ -class DIGITAL_API digital_mpsk_receiver_cc : public gr_block +class DIGITAL_API digital_mpsk_receiver_cc : public gr_block, public gri_control_loop { public: ~digital_mpsk_receiver_cc (); @@ -112,43 +120,14 @@ class DIGITAL_API digital_mpsk_receiver_cc : public gr_block //! (M&M) Sets value for omega gain factor void set_gain_omega (float gain_omega) { d_gain_omega = gain_omega; } - - - // Member function related to the phase/frequency tracking portion of the receiver - //! (CL) Returns the value for alpha (the phase gain term) - float alpha() const { return d_alpha; } - - //! (CL) Returns the value of beta (the frequency gain term) - float beta() const { return d_beta; } - - //! (CL) Returns the current value of the frequency of the NCO in the Costas loop - float freq() const { return d_freq; } - - //! (CL) Returns the current value of the phase of the NCO in the Costal loop - float phase() const { return d_phase; } - - //! (CL) Sets the value for alpha (the phase gain term) - void set_alpha(float alpha) { d_alpha = alpha; } - - //! (CL) Setss the value of beta (the frequency gain term) - void set_beta(float beta) { d_beta = beta; } - - //! (CL) Sets the current value of the frequency of the NCO in the Costas loop - void set_freq(float freq) { d_freq = freq; } - - //! (CL) Setss the current value of the phase of the NCO in the Costal loop - void set_phase(float phase) { d_phase = phase; } - - protected: - - /*! + + /*! * \brief Constructor to synchronize incoming M-PSK symbols * * \param M modulation order of the M-PSK modulation * \param theta any constant phase rotation from the real axis of the constellation - * \param alpha gain parameter to adjust the phase in the Costas loop (~0.01) - * \param beta gain parameter to adjust the frequency in the Costas loop (~alpha^2/4) + * \param loop_bw Loop bandwidth to set gains of phase/freq tracking loop * \param fmin minimum normalized frequency value the loop can achieve * \param fmax maximum normalized frequency value the loop can achieve * \param mu initial parameter for the interpolator [0,1] @@ -161,7 +140,7 @@ protected: * value of M. */ digital_mpsk_receiver_cc (unsigned int M, float theta, - float alpha, float beta, + float loop_bw, float fmin, float fmax, float mu, float gain_mu, float omega, float gain_omega, float omega_rel); @@ -172,54 +151,61 @@ protected: void phase_error_tracking(gr_complex sample); -/*! + /*! * \brief Phase error detector for MPSK modulations. * * \param sample the I&Q sample from which to determine the phase error * - * This function determines the phase error for any MPSK signal by creating a set of PSK constellation points - * and doing a brute-force search to see which point minimizes the Euclidean distance. This point is then used - * to derotate the sample to the real-axis and a atan (using the fast approximation function) to determine the - * phase difference between the incoming sample and the real constellation point + * This function determines the phase error for any MPSK signal by + * creating a set of PSK constellation points and doing a + * brute-force search to see which point minimizes the Euclidean + * distance. This point is then used to derotate the sample to the + * real-axis and a atan (using the fast approximation function) to + * determine the phase difference between the incoming sample and + * the real constellation point * * This should be cleaned up and made more efficient. * * \returns the approximated phase error. - */ + */ float phase_error_detector_generic(gr_complex sample) const; // generic for M but more costly - /*! + /*! * \brief Phase error detector for BPSK modulation. * * \param sample the I&Q sample from which to determine the phase error * - * This function determines the phase error using a simple BPSK phase error detector by multiplying the real - * and imaginary (the error signal) components together. As the imaginary part goes to 0, so does this error. + * This function determines the phase error using a simple BPSK + * phase error detector by multiplying the real and imaginary (the + * error signal) components together. As the imaginary part goes to + * 0, so does this error. * * \returns the approximated phase error. - */ + */ float phase_error_detector_bpsk(gr_complex sample) const; // optimized for BPSK - /*! + /*! * \brief Phase error detector for QPSK modulation. * * \param sample the I&Q sample from which to determine the phase error * - * This function determines the phase error using the limiter approach in a standard 4th order Costas loop + * This function determines the phase error using the limiter + * approach in a standard 4th order Costas loop * * \returns the approximated phase error. - */ + */ float phase_error_detector_qpsk(gr_complex sample) const; - /*! + /*! * \brief Decision maker for a generic MPSK constellation. * * \param sample the baseband I&Q sample from which to make the decision * - * This decision maker is a generic implementation that does a brute-force search - * for the constellation point that minimizes the error between it and the incoming signal. + * This decision maker is a generic implementation that does a + * brute-force search for the constellation point that minimizes the + * error between it and the incoming signal. * * \returns the index to d_constellation that minimizes the error/ */ @@ -231,46 +217,44 @@ protected: * * \param sample the baseband I&Q sample from which to make the decision * - * This decision maker is a simple slicer function that makes a decision on the symbol based on its - * placement on the real axis of greater than 0 or less than 0; the quadrature component is always 0. + * This decision maker is a simple slicer function that makes a + * decision on the symbol based on its placement on the real axis of + * greater than 0 or less than 0; the quadrature component is always + * 0. * * \returns the index to d_constellation that minimizes the error/ - */ + */ unsigned int decision_bpsk(gr_complex sample) const; - /*! + /*! * \brief Decision maker for QPSK constellation. * * \param sample the baseband I&Q sample from which to make the decision * - * This decision maker is a simple slicer function that makes a decision on the symbol based on its - * placement versus both axes and returns which quadrant the symbol is in. + * This decision maker is a simple slicer function that makes a + * decision on the symbol based on its placement versus both axes + * and returns which quadrant the symbol is in. * * \returns the index to d_constellation that minimizes the error/ - */ + */ unsigned int decision_qpsk(gr_complex sample) const; - private: +private: unsigned int d_M; float d_theta; - // Members related to carrier and phase tracking - float d_alpha; - float d_beta; - float d_freq, d_max_freq, d_min_freq; - float d_phase; - -/*! + /*! * \brief Decision maker function pointer * * \param sample the baseband I&Q sample from which to make the decision * - * This is a function pointer that is set in the constructor to point to the proper decision function - * for the specified constellation order. + * This is a function pointer that is set in the constructor to + * point to the proper decision function for the specified + * constellation order. * * \return index into d_constellation point that is the closest to the recieved sample - */ + */ unsigned int (digital_mpsk_receiver_cc::*d_decision)(gr_complex sample) const; // pointer to decision function @@ -283,14 +267,15 @@ protected: gr_complex d_p_2T, d_p_1T, d_p_0T; gr_complex d_c_2T, d_c_1T, d_c_0T; - /*! + /*! * \brief Phase error detector function pointer * * \param sample the I&Q sample from which to determine the phase error * - * This is a function pointer that is set in the constructor to point to the proper phase error detector - * function for the specified constellation order. - */ + * This is a function pointer that is set in the constructor to + * point to the proper phase error detector function for the + * specified constellation order. + */ float (digital_mpsk_receiver_cc::*d_phase_error_detector)(gr_complex sample) const; @@ -308,7 +293,7 @@ protected: friend DIGITAL_API digital_mpsk_receiver_cc_sptr digital_make_mpsk_receiver_cc (unsigned int M, float theta, - float alpha, float beta, + float loop_bw, float fmin, float fmax, float mu, float gain_mu, float omega, float gain_omega, float omega_rel); diff --git a/gr-digital/python/Makefile.am b/gr-digital/python/Makefile.am index 6c61002f16..a33e4963d6 100644 --- a/gr-digital/python/Makefile.am +++ b/gr-digital/python/Makefile.am @@ -35,6 +35,7 @@ noinst_PYTHON = \ qa_binary_slicer_fb.py \ qa_clock_recovery_mm.py \ qa_cma_equalizer.py \ + qa_cpm.py \ qa_constellation.py \ qa_constellation_receiver.py \ qa_constellation_decoder_cb.py \ diff --git a/gr-digital/python/__init__.py b/gr-digital/python/__init__.py index 4046f7fafe..7173fa09d8 100644 --- a/gr-digital/python/__init__.py +++ b/gr-digital/python/__init__.py @@ -26,6 +26,7 @@ from psk2 import * from bpsk import * from qpsk import * from qam import * +from gmsk import * from pkt import * from packet_utils import * from crc import * diff --git a/gr-digital/python/generic_mod_demod.py b/gr-digital/python/generic_mod_demod.py index da8e2cfd99..b1986512f0 100644 --- a/gr-digital/python/generic_mod_demod.py +++ b/gr-digital/python/generic_mod_demod.py @@ -54,7 +54,7 @@ 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]") + help="set the number of constellation points (must be a power of 2 for psk, power of 4 for QAM) [default=%default]") parser.add_option("", "--non-differential", action="store_true", dest="differential", default=False, help="do not use differential encoding [default=%default]") @@ -120,7 +120,7 @@ class generic_mod(gr.hier_block2): self.bytes2chunks = \ gr.packed_to_unpacked_bb(self.bits_per_symbol(), gr.GR_MSB_FIRST) - if self._constellation.apply_pre_diff_code(): + if gray_coded == True: self.symbol_mapper = gr.map_bb(self._constellation.pre_diff_code()) if differential: @@ -142,7 +142,7 @@ class generic_mod(gr.hier_block2): # Connect blocks = [self, self.bytes2chunks] - if self._constellation.apply_pre_diff_code(): + if gray_coded == True: blocks.append(self.symbol_mapper) if differential: blocks.append(self.diffenc) @@ -211,6 +211,7 @@ class generic_demod(gr.hier_block2): samples_per_symbol=_def_samples_per_symbol, differential=_def_differential, excess_bw=_def_excess_bw, + gray_coded=True, freq_bw=_def_freq_bw, timing_bw=_def_timing_bw, phase_bw=_def_phase_bw, @@ -228,9 +229,11 @@ class generic_demod(gr.hier_block2): @type samples_per_symbol: float @param excess_bw: Root-raised cosine filter excess bandwidth @type excess_bw: float + @param gray_coded: turn gray coding on/off + @type gray_coded: bool @param freq_bw: loop filter lock-in bandwidth @type freq_bw: float - @param timing_bw: timing recoery loop lock-in bandwidth + @param timing_bw: timing recovery loop lock-in bandwidth @type timing_bw: float @param phase_bw: phase recovery loop bandwidth @type phase_bw: float @@ -265,8 +268,9 @@ class generic_demod(gr.hier_block2): self.agc = gr.agc2_cc(0.6e-1, 1e-3, 1, 1, 100) # Frequency correction + fll_ntaps = 55 self.freq_recov = digital_swig.fll_band_edge_cc(self._samples_per_symbol, self._excess_bw, - ntaps, self._freq_bw) + fll_ntaps, self._freq_bw) # symbol timing recovery with RRC data filter taps = gr.firdes.root_raised_cosine(nfilts, nfilts*self._samples_per_symbol, @@ -280,12 +284,12 @@ class generic_demod(gr.hier_block2): self.receiver = digital_swig.constellation_receiver_cb( self._constellation, self._phase_bw, 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(): + if gray_coded: self.symbol_mapper = gr.map_bb( mod_codes.invert_code(self._constellation.pre_diff_code())) @@ -297,9 +301,10 @@ class generic_demod(gr.hier_block2): if log: self._setup_logging() - + # Connect and Initialize base class - blocks = [self, self.agc, self.freq_recov, self.time_recov, self.receiver] + 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(): diff --git a/gr-digital/python/gmsk.py b/gr-digital/python/gmsk.py index 3b6c016a0b..ba122821e1 100644 --- a/gr-digital/python/gmsk.py +++ b/gr-digital/python/gmsk.py @@ -25,7 +25,9 @@ # See gnuradio-examples/python/digital for examples from gnuradio import gr -from gnuradio import modulation_utils +import digital_swig +import modulation_utils2 + from math import pi import numpy from pprint import pprint @@ -37,11 +39,9 @@ _def_bt = 0.35 _def_verbose = False _def_log = False -_def_gain_mu = None -_def_mu = 0.5 -_def_freq_error = 0.0 -_def_omega_relative_limit = 0.005 - +# Symbol timing recovery +_def_timing_bw = 2*pi/100.0 +_def_timing_max_dev = 1.5 # ///////////////////////////////////////////////////////////////////////////// # GMSK modulator @@ -79,7 +79,8 @@ class gmsk_mod(gr.hier_block2): self._bt = bt if not isinstance(samples_per_symbol, int) or samples_per_symbol < 2: - raise TypeError, ("samples_per_symbol must be an integer >= 2, is %r" % (samples_per_symbol,)) + raise TypeError, ("samples_per_symbol must be an integer >= 2, is %r" % \ + (samples_per_symbol,)) ntaps = 4 * samples_per_symbol # up to 3 bits in filter at once sensitivity = (pi / 2) / samples_per_symbol # phase change per bit = pi / 2 @@ -128,11 +129,11 @@ class gmsk_mod(gr.hier_block2): def _setup_logging(self): print "Modulation logging turned on." self.connect(self.nrz, - gr.file_sink(gr.sizeof_float, "nrz.dat")) + gr.file_sink(gr.sizeof_float, "tx_gmsk_nrz.32f")) self.connect(self.gaussian_filter, - gr.file_sink(gr.sizeof_float, "gaussian_filter.dat")) + gr.file_sink(gr.sizeof_float, "tx_gmsk_gaussian_filter.32f")) self.connect(self.fmmod, - gr.file_sink(gr.sizeof_gr_complex, "fmmod.dat")) + gr.file_sink(gr.sizeof_gr_complex, "tx_gmsk_fmmod.32fc")) def add_options(parser): @@ -148,7 +149,7 @@ class gmsk_mod(gr.hier_block2): """ Given command line options, create dictionary suitable for passing to __init__ """ - return modulation_utils.extract_kwargs_from_options(gmsk_mod.__init__, + return modulation_utils2.extract_kwargs_from_options(gmsk_mod.__init__, ('self',), options) extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) @@ -162,10 +163,8 @@ class gmsk_demod(gr.hier_block2): def __init__(self, samples_per_symbol=_def_samples_per_symbol, - gain_mu=_def_gain_mu, - mu=_def_mu, - omega_relative_limit=_def_omega_relative_limit, - freq_error=_def_freq_error, + bt=_def_bt, + timing_bw=_def_timing_bw, verbose=_def_verbose, log=_def_log): """ @@ -177,21 +176,12 @@ class gmsk_demod(gr.hier_block2): @param samples_per_symbol: samples per baud @type samples_per_symbol: integer + @param timing_bw: timing recovery loop lock-in bandwidth + @type timing_bw: float @param verbose: Print information about modulator? @type verbose: bool @param log: Print modualtion data to files? @type log: bool - - Clock recovery parameters. These all have reasonble defaults. - - @param gain_mu: controls rate of mu adjustment - @type gain_mu: float - @param mu: fractional delay [0.0, 1.0] - @type mu: float - @param omega_relative_limit: sets max variation in omega - @type omega_relative_limit: float, typically 0.000200 (200 ppm) - @param freq_error: bit rate error as a fraction - @param float """ gr.hier_block2.__init__(self, "gmsk_demod", @@ -199,33 +189,31 @@ class gmsk_demod(gr.hier_block2): gr.io_signature(1, 1, gr.sizeof_char)) # Output signature self._samples_per_symbol = samples_per_symbol - self._gain_mu = gain_mu - self._mu = mu - self._omega_relative_limit = omega_relative_limit - self._freq_error = freq_error + self._bt = bt + self._timing_bw = timing_bw + self._timing_max_dev= _def_timing_max_dev if samples_per_symbol < 2: raise TypeError, "samples_per_symbol >= 2, is %f" % samples_per_symbol - self._omega = samples_per_symbol*(1+self._freq_error) - - if not self._gain_mu: - self._gain_mu = 0.175 - - self._gain_omega = .25 * self._gain_mu * self._gain_mu # critically damped - # Demodulate FM sensitivity = (pi / 2) / samples_per_symbol self.fmdemod = gr.quadrature_demod_cf(1.0 / sensitivity) # the clock recovery block tracks the symbol clock and resamples as needed. # the output of the block is a stream of soft symbols (float) - self.clock_recovery = gr.clock_recovery_mm_ff(self._omega, self._gain_omega, - self._mu, self._gain_mu, - self._omega_relative_limit) - + nfilts = 32 + ntaps = 11 * int(self._samples_per_symbol*nfilts) + taps = gr.firdes.gaussian(nfilts, + nfilts*self._samples_per_symbol, + self._bt, ntaps) + self.clock_recovery = \ + gr.pfb_clock_sync_fff(self._samples_per_symbol, + self._timing_bw, taps, + nfilts, nfilts//2, self._timing_max_dev) + # slice the floats at 0, outputting 1 bit (the LSB of the output byte) per sample - self.slicer = gr.binary_slicer_fb() + self.slicer = digital_swig.binary_slicer_fb() if verbose: self._print_verbage() @@ -245,48 +233,38 @@ class gmsk_demod(gr.hier_block2): def _print_verbage(self): - print "bits per symbol = %d" % self.bits_per_symbol() - print "M&M clock recovery omega = %f" % self._omega - print "M&M clock recovery gain mu = %f" % self._gain_mu - print "M&M clock recovery mu = %f" % self._mu - print "M&M clock recovery omega rel. limit = %f" % self._omega_relative_limit - print "frequency error = %f" % self._freq_error + print "bits per symbol: %d" % self.bits_per_symbol() + print "Bandwidth-Time Prod: %f" % self._bw + print "Timing bandwidth: %.2e" % self._timing_bw def _setup_logging(self): print "Demodulation logging turned on." self.connect(self.fmdemod, - gr.file_sink(gr.sizeof_float, "fmdemod.dat")) + gr.file_sink(gr.sizeof_float, "rx_gmsk_fmdemod.32f")) self.connect(self.clock_recovery, - gr.file_sink(gr.sizeof_float, "clock_recovery.dat")) + gr.file_sink(gr.sizeof_float, "rx_gmsk_clock_recovery.32f")) self.connect(self.slicer, - gr.file_sink(gr.sizeof_char, "slicer.dat")) + gr.file_sink(gr.sizeof_char, "rx_gmsk_slicer.8b")) def add_options(parser): """ Adds GMSK demodulation-specific options to the standard parser """ - parser.add_option("", "--gain-mu", type="float", default=_def_gain_mu, - help="M&M clock recovery gain mu [default=%default] (GMSK/PSK)") - parser.add_option("", "--mu", type="float", default=_def_mu, - help="M&M clock recovery mu [default=%default] (GMSK/PSK)") - parser.add_option("", "--omega-relative-limit", type="float", default=_def_omega_relative_limit, - help="M&M clock recovery omega relative limit [default=%default] (GMSK/PSK)") - parser.add_option("", "--freq-error", type="float", default=_def_freq_error, - help="M&M clock recovery frequency error [default=%default] (GMSK)") + parser.add_option("", "--timing-bw", type="float", default=_def_timing_bw, + help="set timing symbol sync loop gain lock-in bandwidth [default=%default]") add_options=staticmethod(add_options) def extract_kwargs_from_options(options): """ Given command line options, create dictionary suitable for passing to __init__ """ - return modulation_utils.extract_kwargs_from_options(gmsk_demod.__init__, - ('self',), options) + return modulation_utils2.extract_kwargs_from_options(gmsk_demod.__init__, + ('self',), options) extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) - # # Add these to the mod/demod registry # -modulation_utils.add_type_1_mod('gmsk', gmsk_mod) -modulation_utils.add_type_1_demod('gmsk', gmsk_demod) +modulation_utils2.add_type_1_mod('gmsk', gmsk_mod) +modulation_utils2.add_type_1_demod('gmsk', gmsk_demod) diff --git a/gr-digital/python/qa_constellation.py b/gr-digital/python/qa_constellation.py index 02afb8d2da..264ff7de64 100755 --- a/gr-digital/python/qa_constellation.py +++ b/gr-digital/python/qa_constellation.py @@ -70,10 +70,8 @@ tested_constellation_info = ( 'mod_code': tested_mod_codes, }, True, None), (digital_swig.constellation_bpsk, {}, 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. (digital_swig.constellation_qpsk, {}, False, None), + (digital_swig.constellation_dqpsk, {}, True, None), (digital_swig.constellation_8psk, {}, False, None), (twod_constell, {}, True, None), (threed_constell, {}, True, None), @@ -143,8 +141,8 @@ class test_constellation (gr_unittest.TestCase): 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. + # 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", @@ -174,7 +172,8 @@ class mod_demod(gr.hier_block2): 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())) + 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: diff --git a/gr-digital/python/qa_constellation_receiver.py b/gr-digital/python/qa_constellation_receiver.py index 79dded8ba3..25107e4a74 100755 --- a/gr-digital/python/qa_constellation_receiver.py +++ b/gr-digital/python/qa_constellation_receiver.py @@ -29,6 +29,7 @@ from generic_mod_demod import generic_mod, generic_demod from qa_constellation import tested_constellations, twod_constell +import math # Set a seed so that if errors turn up they are reproducible. # 1234 fails @@ -47,11 +48,8 @@ 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 +FREQ_BW = 2*math.pi/100.0 +PHASE_BW = 2*math.pi/100.0 class test_constellation_receiver (gr_unittest.TestCase): @@ -127,8 +125,8 @@ class rec_test_tb (gr.top_block): channel = gr.channel_model(NOISE_VOLTAGE, FREQUENCY_OFFSET, TIMING_OFFSET) # Receiver Blocks demod = generic_demod(constellation, differential=differential, - freq_alpha=FREQ_ALPHA, - phase_alpha=PHASE_ALPHA) + freq_bw=FREQ_BW, + phase_bw=PHASE_BW) self.dst = gr.vector_sink_b() self.connect(src, packer, mod, channel, demod, self.dst) diff --git a/gr-digital/python/qa_cpm.py b/gr-digital/python/qa_cpm.py new file mode 100755 index 0000000000..12a84c76c2 --- /dev/null +++ b/gr-digital/python/qa_cpm.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python +# +# Copyright 2010 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +import digital_swig +import numpy + +class test_cpm(gr_unittest.TestCase): + + def setUp (self): + self.tb = gr.top_block () + + def tearDown (self): + self.tb = None + + def do_check_phase_shift(self, type, name): + sps = 2 + L = 1 + in_bits = (1,) * 20 + src = gr.vector_source_b(in_bits, False) + cpm = digital_swig.cpmmod_bc(type, 0.5, sps, L) + arg = gr.complex_to_arg() + sink = gr.vector_sink_f() + + self.tb.connect(src, cpm, arg, sink) + self.tb.run() + + symbol_phases = numpy.array(sink.data()[sps*L-1::sps]) + phase_diff = numpy.mod(numpy.subtract(symbol_phases[1:], symbol_phases[:-1]), + (2*numpy.pi,) * (len(symbol_phases)-1)) + self.assertFloatTuplesAlmostEqual(tuple(phase_diff), (0.5 * numpy.pi,) * len(phase_diff), 5, + msg="Phase shift was not correct for CPM method " + name) + + def test_001_lrec(self): + self.do_check_phase_shift(gr.cpm.LRC, 'LREC') + + def test_001_lrc(self): + self.do_check_phase_shift(gr.cpm.LRC, 'LRC') + + def test_001_lsrc(self): + self.do_check_phase_shift(gr.cpm.LSRC, 'LSRC') + + def test_001_ltfm(self): + self.do_check_phase_shift(gr.cpm.TFM, 'TFM') + + def test_001_lgmsk(self): + sps = 2 + L = 5 + bt = 0.3 + in_bits = (1,) * 20 + src = gr.vector_source_b(in_bits, False) + gmsk = digital_swig.gmskmod_bc(sps, bt, L) + arg = gr.complex_to_arg() + sink = gr.vector_sink_f() + + self.tb.connect(src, gmsk, arg, sink) + self.tb.run() + + symbol_phases = numpy.array(sink.data()[sps*L-1::sps]) + phase_diff = numpy.mod(numpy.subtract(symbol_phases[1:], symbol_phases[:-1]), + (2*numpy.pi,) * (len(symbol_phases)-1)) + self.assertFloatTuplesAlmostEqual(tuple(phase_diff), (0.5 * numpy.pi,) * len(phase_diff), 5, + msg="Phase shift was not correct for GMSK") + + def test_phase_response(self): + phase_response = gr.cpm.phase_response(gr.cpm.LREC, 2, 4) + self.assertAlmostEqual(numpy.sum(phase_response), 1) + + +if __name__ == '__main__': + gr_unittest.run(test_cpm, "test_cpm.xml") + diff --git a/gr-digital/python/qa_crc32.py b/gr-digital/python/qa_crc32.py index f86813f3f3..f86813f3f3 100644..100755 --- a/gr-digital/python/qa_crc32.py +++ b/gr-digital/python/qa_crc32.py diff --git a/gr-digital/python/qa_fll_band_edge.py b/gr-digital/python/qa_fll_band_edge.py index 088eb2b680..088eb2b680 100644..100755 --- a/gr-digital/python/qa_fll_band_edge.py +++ b/gr-digital/python/qa_fll_band_edge.py diff --git a/gr-digital/python/qa_lms_equalizer.py b/gr-digital/python/qa_lms_equalizer.py index 025c785aa4..025c785aa4 100644..100755 --- a/gr-digital/python/qa_lms_equalizer.py +++ b/gr-digital/python/qa_lms_equalizer.py diff --git a/gr-digital/python/qa_mpsk_receiver.py b/gr-digital/python/qa_mpsk_receiver.py index 7e9a76e1f2..6531e59f7a 100644..100755 --- a/gr-digital/python/qa_mpsk_receiver.py +++ b/gr-digital/python/qa_mpsk_receiver.py @@ -36,8 +36,7 @@ class test_mpsk_receiver(gr_unittest.TestCase): # Test BPSK sync M = 2 theta = 0 - alpha = 0.1 - beta = 0.25*alpha*alpha + loop_bw = cmath.pi/100.0 fmin = -0.5 fmax = 0.5 mu = 0.25 @@ -46,7 +45,7 @@ class test_mpsk_receiver(gr_unittest.TestCase): gain_omega = 0.001 omega_rel = 0.001 - self.test = digital_swig.mpsk_receiver_cc(M, theta, alpha, beta, + self.test = digital_swig.mpsk_receiver_cc(M, theta, loop_bw, fmin, fmax, mu, gain_mu, omega, gain_omega, omega_rel) @@ -68,8 +67,8 @@ class test_mpsk_receiver(gr_unittest.TestCase): expected_result = expected_result[len_e - Ncmp:] dst_data = dst_data[len_d - Ncmp:] - #print expected_result - #print dst_data + #for e,d in zip(expected_result, dst_data): + # print e, d self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 1) @@ -78,8 +77,7 @@ class test_mpsk_receiver(gr_unittest.TestCase): # Test QPSK sync M = 4 theta = 0 - alpha = 0.1 - beta = 0.25*alpha*alpha + loop_bw = 2*cmath.pi/100.0 fmin = -0.5 fmax = 0.5 mu = 0.25 @@ -88,11 +86,11 @@ class test_mpsk_receiver(gr_unittest.TestCase): gain_omega = 0.001 omega_rel = 0.001 - self.test = digital_swig.mpsk_receiver_cc(M, theta, alpha, beta, + self.test = digital_swig.mpsk_receiver_cc(M, theta, loop_bw, fmin, fmax, mu, gain_mu, omega, gain_omega, omega_rel) - + data = 1000*[complex( 0.707, 0.707), complex( 0.707, 0.707), complex(-0.707, 0.707), complex(-0.707, 0.707), complex(-0.707, -0.707), complex(-0.707, -0.707), @@ -103,8 +101,8 @@ class test_mpsk_receiver(gr_unittest.TestCase): self.tb.connect(self.src, self.test, self.snk) self.tb.run() - expected_result = 1000*[complex(1.2, 0), complex(0, 1.2), - complex(-1.2, 0), complex(0, -1.2)] + expected_result = 1000*[complex(0, -1.0), complex(1.0, 0), + complex(0, 1.0), complex(-1.0, 0)] dst_data = self.snk.data() # Only compare last Ncmp samples @@ -114,9 +112,9 @@ class test_mpsk_receiver(gr_unittest.TestCase): expected_result = expected_result[len_e - Ncmp:] dst_data = dst_data[len_d - Ncmp:] - #print expected_result - #print dst_data - + #for e,d in zip(expected_result, dst_data): + # print e, d + self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 1) if __name__ == '__main__': diff --git a/gr-digital/python/qpsk.py b/gr-digital/python/qpsk.py index 76e5df2701..481b7cb5b7 100644 --- a/gr-digital/python/qpsk.py +++ b/gr-digital/python/qpsk.py @@ -100,10 +100,77 @@ class qpsk_demod(generic_demod): super(qpsk_demod, self).__init__(constellation=constellation, *args, **kwargs) + + +# ///////////////////////////////////////////////////////////////////////////// +# DQPSK constellation +# ///////////////////////////////////////////////////////////////////////////// + +def dqpsk_constellation(m=_def_constellation_points): + if m != _def_constellation_points: + raise ValueError("DQPSK can only have 4 constellation points.") + return digital_swig.constellation_dqpsk() + +# ///////////////////////////////////////////////////////////////////////////// +# DQPSK modulator +# ///////////////////////////////////////////////////////////////////////////// + +class dqpsk_mod(generic_mod): + + def __init__(self, constellation_points=_def_constellation_points, + gray_coded=_def_gray_coded, + differential=True, *args, **kwargs): + """ + Hierarchical block for RRC-filtered DQPSK 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_points = _def_constellation_points + constellation = digital_swig.constellation_dqpsk() + if constellation_points != 4: + raise ValueError('Number of constellation points must be 4 for DQPSK.') + super(dqpsk_mod, self).__init__(constellation=constellation, + gray_coded=gray_coded, + differential=True, + *args, **kwargs) + +# ///////////////////////////////////////////////////////////////////////////// +# DQPSK demodulator +# +# ///////////////////////////////////////////////////////////////////////////// + +class dqpsk_demod(generic_demod): + + def __init__(self, constellation_points=_def_constellation_points, + differential=True, *args, **kwargs): + + """ + Hierarchical block for RRC-filtered DQPSK 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_points = _def_constellation_points + constellation = digital_swig.constellation_dqpsk() + if constellation_points != 4: + raise ValueError('Number of constellation points must be 4 for DQPSK.') + super(dqpsk_demod, self).__init__(constellation=constellation, + differential=True, + *args, **kwargs) + # # Add these to the mod/demod registry # modulation_utils2.add_type_1_mod('qpsk', qpsk_mod) modulation_utils2.add_type_1_demod('qpsk', qpsk_demod) modulation_utils2.add_type_1_constellation('qpsk', qpsk_constellation) +modulation_utils2.add_type_1_mod('dqpsk', dqpsk_mod) +modulation_utils2.add_type_1_demod('dqpsk', dqpsk_demod) +modulation_utils2.add_type_1_constellation('dqpsk', dqpsk_constellation) diff --git a/gr-digital/swig/CMakeLists.txt b/gr-digital/swig/CMakeLists.txt index b62c024e06..5c6477aa18 100644 --- a/gr-digital/swig/CMakeLists.txt +++ b/gr-digital/swig/CMakeLists.txt @@ -30,10 +30,11 @@ SET(GR_SWIG_INCLUDE_DIRS SET(GR_SWIG_LIBRARIES gnuradio-digital) +GR_SWIG_MAKE(digital_hier digital_hier.i) GR_SWIG_MAKE(digital_swig digital_swig.i) GR_SWIG_INSTALL( - TARGETS digital_swig + TARGETS digital_hier digital_swig DESTINATION ${GR_PYTHON_DIR}/gnuradio/digital COMPONENT "digital_python" ) diff --git a/gr-digital/swig/Makefile.am b/gr-digital/swig/Makefile.am index c0d28c24c6..6604665b0f 100644 --- a/gr-digital/swig/Makefile.am +++ b/gr-digital/swig/Makefile.am @@ -26,8 +26,7 @@ TESTS = EXTRA_DIST += $(nobase_guile_DATA) AM_CPPFLAGS = \ - -I$(abs_top_srcdir)/gr-digital/lib \ - -I$(abs_top_builddir)/gr-digital/lib \ + $(GR_DIGITAL_INCLUDES) \ $(STD_DEFINES_AND_INCLUDES) \ $(PYTHON_CPPFLAGS) \ $(WITH_INCLUDES) @@ -43,6 +42,7 @@ noinst_GUILE = digital.test ############################## # SWIG interface and library TOP_SWIG_IFILES = \ + digital_hier.i \ digital_swig.i # Install so that they end up available as: @@ -73,9 +73,11 @@ digital_swig_swiginclude_headers = \ digital_kurtotic_equalizer_cc.i \ digital_mpsk_receiver_cc.i +digital_hier_swig_args = \ + $(GR_DIGITAL_INCLUDES) + digital_swig_swig_args = \ - -I$(abs_top_srcdir)/gr-digital/lib \ - -I$(abs_top_builddir)/gr-digital/lib + $(GR_DIGITAL_INCLUDES) if GUILE TESTS += run_guile_tests diff --git a/gr-digital/swig/Makefile.swig.gen b/gr-digital/swig/Makefile.swig.gen index bd9aabcea1..95d6aea899 100644 --- a/gr-digital/swig/Makefile.swig.gen +++ b/gr-digital/swig/Makefile.swig.gen @@ -101,8 +101,7 @@ _digital_swig_la_LDFLAGS = \ _digital_swig_la_CXXFLAGS = \ $(STD_SWIG_CXX_FLAGS) \ - -I$(top_builddir) \ - $(digital_swig_la_swig_cxxflags) + -I$(top_builddir) python/digital_swig.cc: digital_swig.py digital_swig.py: digital_swig.i @@ -143,3 +142,148 @@ gnuradio/digital_swig-primitive.scm: gnuradio/digital_swig.scm endif # end of GUILE +# -*- Makefile -*- +# +# Copyright 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. +# + +# Makefile.swig.gen for digital_hier.i + +## Default install locations for these files: +## +## Default location for the Python directory is: +## ${prefix}/lib/python${python_version}/site-packages/[category]/digital_hier +## Default location for the Python exec directory is: +## ${exec_prefix}/lib/python${python_version}/site-packages/[category]/digital_hier +## +## The following can be overloaded to change the install location, but +## this has to be done in the including Makefile.am -before- +## Makefile.swig is included. + +digital_hier_pythondir_category ?= gnuradio/digital_hier +digital_hier_pylibdir_category ?= $(digital_hier_pythondir_category) +digital_hier_pythondir = $(pythondir)/$(digital_hier_pythondir_category) +digital_hier_pylibdir = $(pyexecdir)/$(digital_hier_pylibdir_category) + +# The .so libraries for the guile modules get installed whereever guile +# is installed, usually /usr/lib/guile/gnuradio/ +# FIXME: determince whether these should be installed with gnuradio. +digital_hier_scmlibdir = $(libdir) + +# The scm files for the guile modules get installed where ever guile +# is installed, usually /usr/share/guile/site/digital_hier +# FIXME: determince whether these should be installed with gnuradio. +digital_hier_scmdir = $(guiledir) + +## SWIG headers are always installed into the same directory. + +digital_hier_swigincludedir = $(swigincludedir) + +## This is a template file for a "generated" Makefile addition (in +## this case, "Makefile.swig.gen"). By including the top-level +## Makefile.swig, this file will be used to generate the SWIG +## dependencies. Assign the variable TOP_SWIG_FILES to be the list of +## SWIG .i files to generated wrappings for; there can be more than 1 +## so long as the names are unique (no sorting is done on the +## TOP_SWIG_FILES list). This file explicitly assumes that a SWIG .i +## file will generate .cc, .py, and possibly .h files -- meaning that +## all of these files will have the same base name (that provided for +## the SWIG .i file). +## +## This code is setup to ensure parallel MAKE ("-j" or "-jN") does the +## right thing. For more info, see < +## http://sources.redhat.com/automake/automake.html#Multiple-Outputs > + +## Other cleaned files: dependency files generated by SWIG or this Makefile + +MOSTLYCLEANFILES += $(DEPDIR)/*.S* + +## Various SWIG variables. These can be overloaded in the including +## Makefile.am by setting the variable value there, then including +## Makefile.swig . + +digital_hier_swiginclude_HEADERS = \ + digital_hier.i \ + $(digital_hier_swiginclude_headers) + +if PYTHON +digital_hier_pylib_LTLIBRARIES = \ + _digital_hier.la + +_digital_hier_la_SOURCES = \ + python/digital_hier.cc \ + $(digital_hier_la_swig_sources) + +digital_hier_python_PYTHON = \ + digital_hier.py \ + $(digital_hier_python) + +_digital_hier_la_LIBADD = \ + $(STD_SWIG_LA_LIB_ADD) \ + $(digital_hier_la_swig_libadd) + +_digital_hier_la_LDFLAGS = \ + $(STD_SWIG_LA_LD_FLAGS) \ + $(digital_hier_la_swig_ldflags) + +_digital_hier_la_CXXFLAGS = \ + $(STD_SWIG_CXX_FLAGS) \ + -I$(top_builddir) \ + $(digital_hier_la_swig_cxxflags) + +python/digital_hier.cc: digital_hier.py +digital_hier.py: digital_hier.i + +# Include the python dependencies for this file +-include python/digital_hier.d + +endif # end of if python + +if GUILE + +digital_hier_scmlib_LTLIBRARIES = \ + libguile-gnuradio-digital_hier.la +libguile_gnuradio_digital_hier_la_SOURCES = \ + guile/digital_hier.cc \ + $(digital_hier_la_swig_sources) +nobase_digital_hier_scm_DATA = \ + gnuradio/digital_hier.scm \ + gnuradio/digital_hier-primitive.scm +libguile_gnuradio_digital_hier_la_LIBADD = \ + $(STD_SWIG_LA_LIB_ADD) \ + $(digital_hier_la_swig_libadd) +libguile_gnuradio_digital_hier_la_LDFLAGS = \ + $(STD_SWIG_LA_LD_FLAGS) \ + $(digital_hier_la_swig_ldflags) +libguile_gnuradio_digital_hier_la_CXXFLAGS = \ + $(STD_SWIG_CXX_FLAGS) \ + -I$(top_builddir) \ + $(digital_hier_la_swig_cxxflags) + +guile/digital_hier.cc: gnuradio/digital_hier.scm +gnuradio/digital_hier.scm: digital_hier.i +gnuradio/digital_hier-primitive.scm: gnuradio/digital_hier.scm + +# Include the guile dependencies for this file +-include guile/digital_hier.d + +endif # end of GUILE + + diff --git a/gr-digital/swig/digital_constellation.i b/gr-digital/swig/digital_constellation.i index ae31d443e6..7e0ad6afec 100644 --- a/gr-digital/swig/digital_constellation.i +++ b/gr-digital/swig/digital_constellation.i @@ -50,6 +50,7 @@ public: unsigned int arity (); digital_constellation_sptr base (); bool apply_pre_diff_code(); + void set_pre_diff_code(bool a); std::vector<unsigned int> pre_diff_code(); unsigned int rotational_symmetry(); unsigned int dimensionality(); @@ -152,6 +153,23 @@ public: digital_constellation_qpsk (); }; +/* + DQPSK Constellation +*/ + +class digital_constellation_dqpsk; +typedef boost::shared_ptr<digital_constellation_dqpsk> digital_constellation_dqpsk_sptr; +%template(digital_constellation_dqpsk_sptr) boost::shared_ptr<digital_constellation_dqpsk>; +%rename(constellation_dqpsk) digital_make_constellation_dqpsk; +digital_constellation_dqpsk_sptr digital_make_constellation_dqpsk(); +%ignore digital_constellation_dqpsk; + +class digital_constellation_dqpsk : public digital_constellation +{ +public: + digital_constellation_dqpsk (); +}; + /* 8PSK Constellation diff --git a/gr-digital/swig/digital_constellation_receiver_cb.i b/gr-digital/swig/digital_constellation_receiver_cb.i index e4be5f39f1..9c4ba645e0 100644 --- a/gr-digital/swig/digital_constellation_receiver_cb.i +++ b/gr-digital/swig/digital_constellation_receiver_cb.i @@ -28,23 +28,9 @@ digital_constellation_receiver_cb_sptr digital_make_constellation_receiver_cb (digital_constellation_sptr constellation, float loop_bw, float fmin, float fmax); -class digital_constellation_receiver_cb : public gr_block +class digital_constellation_receiver_cb : public gr_block, public gri_control_loop { private: digital_constellation_receiver_cb (digital_contellation_sptr constellation, float loop_bw, float fmin, float fmax); -public: - void set_loop_bandwidth(float bw); - void set_damping_factor(float df); - void set_alpha(float alpha); - void set_beta(float beta); - void set_frequency(float freq); - void set_phase(float phase); - - float get_loop_bandwidth() const; - float get_damping_factor() const; - float get_alpha() const; - float get_beta() const; - float get_frequency() const; - float get_phase() const; }; diff --git a/gr-digital/swig/digital_fll_band_edge_cc.i b/gr-digital/swig/digital_fll_band_edge_cc.i index f022bc7e18..3efcb89ed1 100644 --- a/gr-digital/swig/digital_fll_band_edge_cc.i +++ b/gr-digital/swig/digital_fll_band_edge_cc.i @@ -27,7 +27,7 @@ digital_fll_band_edge_cc_sptr digital_make_fll_band_edge_cc (float samps_per_sym int filter_size, float bandwidth); -class digital_fll_band_edge_cc : public gr_sync_block +class digital_fll_band_edge_cc : public gr_sync_block, public gri_control_loop { private: digital_fll_band_edge_cc (float samps_per_sym, float rolloff, diff --git a/gr-digital/swig/digital_hier.i b/gr-digital/swig/digital_hier.i new file mode 100644 index 0000000000..e004461d26 --- /dev/null +++ b/gr-digital/swig/digital_hier.i @@ -0,0 +1,40 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009,2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef SWIGIMPORTED +%module(directors="1") digital_hier +#endif + + //%feature("autodoc", "1"); // generate python docstrings + +%include "gnuradio.i" // the common stuff + +%include "_digital_hier.i" + +#if SWIGGUILE +%scheme %{ +(load-extension-global "libguile-gnuradio-digital_hier" "scm_init_gnuradio_digital_hier_module") +%} + +%goops %{ + (use-modules (gnuradio gnuradio_core_runtime)) +%} +#endif diff --git a/gr-digital/swig/digital_mpsk_receiver_cc.i b/gr-digital/swig/digital_mpsk_receiver_cc.i index cdc9f661b5..b51411f6fb 100644 --- a/gr-digital/swig/digital_mpsk_receiver_cc.i +++ b/gr-digital/swig/digital_mpsk_receiver_cc.i @@ -23,16 +23,16 @@ GR_SWIG_BLOCK_MAGIC(digital,mpsk_receiver_cc); digital_mpsk_receiver_cc_sptr digital_make_mpsk_receiver_cc (unsigned int M, float theta, - float alpha, float beta, + float loop_bw, float fmin, float fmax, float mu, float gain_mu, float omega, float gain_omega, float omega_rel); -class digital_mpsk_receiver_cc : public gr_block +class digital_mpsk_receiver_cc : public gr_block, public gri_control_loop { private: digital_mpsk_receiver_cc (unsigned int M,float theta, - float alpha, float beta, + float loop_bw, float fmin, float fmax, float mu, float gain_mu, float omega, float gain_omega, float omega_rel); @@ -49,12 +49,4 @@ public: } void set_gain_mu (float gain_mu) { d_gain_mu = gain_mu; } void set_gain_omega (float gain_omega) { d_gain_omega = gain_omega; } - float alpha() const { return d_alpha; } - float beta() const { return d_beta; } - float freq() const { return d_freq; } - float phase() const { return d_phase; } - void set_alpha(float alpha) { d_alpha = alpha; } - void set_beta(float beta) { d_beta = beta; } - void set_freq(float freq) { d_freq = freq; } - void set_phase(float phase) { d_phase = phase; } }; diff --git a/gr-digital/swig/digital_swig.i b/gr-digital/swig/digital_swig.i index 0328ca2fcc..ca50f9c501 100644 --- a/gr-digital/swig/digital_swig.i +++ b/gr-digital/swig/digital_swig.i @@ -40,6 +40,7 @@ #include "digital_mpsk_receiver_cc.h" %} +%include "digital_hier.i" %include "digital_binary_slicer_fb.i" %include "digital_clock_recovery_mm_cc.i" %include "digital_clock_recovery_mm_ff.i" |