diff options
Diffstat (limited to 'gr-digital/lib')
20 files changed, 1467 insertions, 1145 deletions
diff --git a/gr-digital/lib/Makefile.am b/gr-digital/lib/Makefile.am index 4f67614d8..17baf2101 100644 --- a/gr-digital/lib/Makefile.am +++ b/gr-digital/lib/Makefile.am @@ -21,29 +21,28 @@ include $(top_srcdir)/Makefile.common -AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(PYTHON_CPPFLAGS) $(WITH_INCLUDES) - -# These headers get installed in ${prefix}/include/gnuradio -grinclude_HEADERS = \ - digital_constellation.h \ - digital_constellation_receiver_cb.h \ - digital_constellation_decoder_cb.h \ - digital_costas_loop_cc.h \ - digital_cma_equalizer_cc.h \ - digital_lms_dd_equalizer_cc.h \ - digital_kurtotic_equalizer_cc.h \ - digital_metric_type.h +AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(PYTHON_CPPFLAGS) \ + $(GR_DIGITAL_INCLUDES) $(WITH_INCLUDES) lib_LTLIBRARIES = libgnuradio-digital.la libgnuradio_digital_la_SOURCES = \ + digital_binary_slicer_fb.cc \ + digital_clock_recovery_mm_cc.cc \ + digital_clock_recovery_mm_ff.cc \ digital_constellation.cc \ digital_constellation_receiver_cb.cc \ digital_constellation_decoder_cb.cc \ + digital_correlate_access_code_bb.cc \ digital_costas_loop_cc.cc \ digital_cma_equalizer_cc.cc \ + digital_crc32.cc \ + digital_fll_band_edge_cc.cc \ digital_lms_dd_equalizer_cc.cc \ - digital_kurtotic_equalizer_cc.cc + digital_kurtotic_equalizer_cc.cc \ + digital_mpsk_receiver_cc.cc \ + digital_gmskmod_bc.cc \ + digital_cpmmod_bc.cc libgnuradio_digital_la_LIBADD = \ $(GNURADIO_CORE_LA) diff --git a/gr-digital/lib/digital_binary_slicer_fb.cc b/gr-digital/lib/digital_binary_slicer_fb.cc new file mode 100644 index 000000000..fcdb4291f --- /dev/null +++ b/gr-digital/lib/digital_binary_slicer_fb.cc @@ -0,0 +1,59 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_binary_slicer_fb.h> +#include <gr_io_signature.h> +#include <gr_math.h> +#include <stdexcept> + +digital_binary_slicer_fb_sptr +digital_make_binary_slicer_fb () +{ + return gnuradio::get_initial_sptr(new digital_binary_slicer_fb ()); +} + +digital_binary_slicer_fb::digital_binary_slicer_fb () + : gr_sync_block ("binary_slicer_fb", + gr_make_io_signature (1, 1, sizeof (float)), + gr_make_io_signature (1, 1, sizeof (unsigned char))) +{ +} + +int +digital_binary_slicer_fb::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const float *in = (const float *) input_items[0]; + unsigned char *out = (unsigned char *) output_items[0]; + + + for (int i = 0; i < noutput_items; i++){ + out[i] = gr_binary_slicer(in[i]); + } + + return noutput_items; +} diff --git a/gr-digital/lib/digital_clock_recovery_mm_cc.cc b/gr-digital/lib/digital_clock_recovery_mm_cc.cc new file mode 100644 index 000000000..198eb4b89 --- /dev/null +++ b/gr-digital/lib/digital_clock_recovery_mm_cc.cc @@ -0,0 +1,217 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2006,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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_io_signature.h> +#include <gr_prefs.h> +#include <digital_clock_recovery_mm_cc.h> +#include <gri_mmse_fir_interpolator_cc.h> +#include <stdexcept> +#include <cstdio> + + +// Public constructor +static const int FUDGE = 16; + +digital_clock_recovery_mm_cc_sptr +digital_make_clock_recovery_mm_cc(float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit) +{ + return gnuradio::get_initial_sptr(new digital_clock_recovery_mm_cc (omega, + gain_omega, + mu, + gain_mu, + omega_relative_limit)); +} + +digital_clock_recovery_mm_cc::digital_clock_recovery_mm_cc (float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit) + : gr_block ("clock_recovery_mm_cc", + gr_make_io_signature (1, 1, sizeof (gr_complex)), + gr_make_io_signature2 (1, 2, sizeof (gr_complex), sizeof(float))), + d_mu (mu), d_omega(omega), d_gain_omega(gain_omega), + d_omega_relative_limit(omega_relative_limit), + d_gain_mu(gain_mu), d_last_sample(0), d_interp(new gri_mmse_fir_interpolator_cc()), + d_verbose(gr_prefs::singleton()->get_bool("clock_recovery_mm_cc", "verbose", false)), + d_p_2T(0), d_p_1T(0), d_p_0T(0), d_c_2T(0), d_c_1T(0), d_c_0T(0) +{ + if (omega <= 0.0) + throw std::out_of_range ("clock rate must be > 0"); + if (gain_mu < 0 || gain_omega < 0) + throw std::out_of_range ("Gains must be non-negative"); + + set_omega(omega); // also sets min and max omega + set_relative_rate (1.0 / omega); + set_history(3); // ensure 2 extra input sample is available +} + +digital_clock_recovery_mm_cc::~digital_clock_recovery_mm_cc () +{ + delete d_interp; +} + +void +digital_clock_recovery_mm_cc::forecast(int noutput_items, gr_vector_int &ninput_items_required) +{ + unsigned ninputs = ninput_items_required.size(); + for (unsigned i=0; i < ninputs; i++) + ninput_items_required[i] = + (int) ceil((noutput_items * d_omega) + d_interp->ntaps()) + FUDGE; +} + +gr_complex +digital_clock_recovery_mm_cc::slicer_0deg (gr_complex sample) +{ + float real=0, imag=0; + + if(sample.real() > 0) + real = 1; + if(sample.imag() > 0) + imag = 1; + return gr_complex(real,imag); +} + +gr_complex +digital_clock_recovery_mm_cc::slicer_45deg (gr_complex sample) +{ + float real= -1, imag = -1; + if(sample.real() > 0) + real=1; + if(sample.imag() > 0) + imag = 1; + return gr_complex(real,imag); +} + +/* + Modified Mueller and Muller clock recovery circuit + Based: + 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. +*/ + +int +digital_clock_recovery_mm_cc::general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const gr_complex *in = (const gr_complex *) input_items[0]; + gr_complex *out = (gr_complex *) output_items[0]; + float *foptr = (float *) output_items[1]; + + bool write_foptr = output_items.size() >= 2; + + int ii = 0; // input index + int oo = 0; // output index + int ni = ninput_items[0] - d_interp->ntaps() - FUDGE; // don't use more input than this + + assert(d_mu >= 0.0); + assert(d_mu <= 1.0); + + float mm_val=0; + gr_complex u, x, y; + + // This loop writes the error to the second output, if it exists + if (write_foptr) { + while(oo < noutput_items && ii < ni) { + d_p_2T = d_p_1T; + d_p_1T = d_p_0T; + d_p_0T = d_interp->interpolate (&in[ii], d_mu); + + d_c_2T = d_c_1T; + d_c_1T = d_c_0T; + d_c_0T = slicer_0deg(d_p_0T); + + x = (d_c_0T - d_c_2T) * conj(d_p_1T); + y = (d_p_0T - d_p_2T) * conj(d_c_1T); + u = y - x; + mm_val = u.real(); + out[oo++] = d_p_0T; + + // limit mm_val + mm_val = gr_branchless_clip(mm_val,4.0); + d_omega = d_omega + d_gain_omega * mm_val; + d_omega = d_omega_mid + gr_branchless_clip(d_omega-d_omega_mid, d_omega_relative_limit); // make sure we don't walk away + + d_mu = d_mu + d_omega + d_gain_mu * mm_val; + ii += (int)floor(d_mu); + d_mu -= floor(d_mu); + + // write the error signal to the second output + foptr[oo-1] = mm_val; + + if (ii < 0) // clamp it. This should only happen with bogus input + ii = 0; + } + } + // This loop does not write to the second output (ugly, but faster) + else { + while(oo < noutput_items && ii < ni) { + d_p_2T = d_p_1T; + d_p_1T = d_p_0T; + d_p_0T = d_interp->interpolate (&in[ii], d_mu); + + d_c_2T = d_c_1T; + d_c_1T = d_c_0T; + d_c_0T = slicer_0deg(d_p_0T); + + x = (d_c_0T - d_c_2T) * conj(d_p_1T); + y = (d_p_0T - d_p_2T) * conj(d_c_1T); + u = y - x; + mm_val = u.real(); + out[oo++] = d_p_0T; + + // limit mm_val + mm_val = gr_branchless_clip(mm_val,1.0); + + d_omega = d_omega + d_gain_omega * mm_val; + d_omega = d_omega_mid + gr_branchless_clip(d_omega-d_omega_mid, d_omega_relative_limit); // make sure we don't walk away + + d_mu = d_mu + d_omega + d_gain_mu * mm_val; + ii += (int)floor(d_mu); + d_mu -= floor(d_mu); + + if(d_verbose) { + printf("%f\t%f\n", d_omega, d_mu); + } + + if (ii < 0) // clamp it. This should only happen with bogus input + ii = 0; + } + } + + if (ii > 0){ + if (ii > ninput_items[0]){ + fprintf(stderr, "gr_clock_recovery_mm_cc: ii > ninput_items[0] (%d > %d)\n", + ii, ninput_items[0]); + assert(0); + } + consume_each (ii); + } + + return oo; +} diff --git a/gr-digital/lib/digital_clock_recovery_mm_ff.cc b/gr-digital/lib/digital_clock_recovery_mm_ff.cc new file mode 100644 index 000000000..04057f0e9 --- /dev/null +++ b/gr-digital/lib/digital_clock_recovery_mm_ff.cc @@ -0,0 +1,139 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_io_signature.h> +#include <digital_clock_recovery_mm_ff.h> +#include <gri_mmse_fir_interpolator.h> +#include <stdexcept> + +#define DEBUG_CR_MM_FF 0 // must be defined as 0 or 1 + +// Public constructor + +digital_clock_recovery_mm_ff_sptr +digital_make_clock_recovery_mm_ff(float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit) +{ + return gnuradio::get_initial_sptr(new digital_clock_recovery_mm_ff (omega, + gain_omega, + mu, + gain_mu, + omega_relative_limit)); +} + +digital_clock_recovery_mm_ff::digital_clock_recovery_mm_ff (float omega, float gain_omega, + float mu, float gain_mu, + float omega_relative_limit) + : gr_block ("clock_recovery_mm_ff", + gr_make_io_signature (1, 1, sizeof (float)), + gr_make_io_signature (1, 1, sizeof (float))), + d_mu (mu), d_gain_omega(gain_omega), d_gain_mu(gain_mu), + d_last_sample(0), d_interp(new gri_mmse_fir_interpolator()), + d_logfile(0), d_omega_relative_limit(omega_relative_limit) +{ + if (omega < 1) + throw std::out_of_range ("clock rate must be > 0"); + if (gain_mu < 0 || gain_omega < 0) + throw std::out_of_range ("Gains must be non-negative"); + + set_omega(omega); // also sets min and max omega + set_relative_rate (1.0 / omega); + + if (DEBUG_CR_MM_FF) + d_logfile = fopen("cr_mm_ff.dat", "wb"); +} + +digital_clock_recovery_mm_ff::~digital_clock_recovery_mm_ff () +{ + delete d_interp; + + if (DEBUG_CR_MM_FF && d_logfile){ + fclose(d_logfile); + d_logfile = 0; + } +} + +void +digital_clock_recovery_mm_ff::forecast(int noutput_items, gr_vector_int &ninput_items_required) +{ + unsigned ninputs = ninput_items_required.size(); + for (unsigned i=0; i < ninputs; i++) + ninput_items_required[i] = + (int) ceil((noutput_items * d_omega) + d_interp->ntaps()); +} + +static inline float +slice(float x) +{ + return x < 0 ? -1.0F : 1.0F; +} + +/* + * This implements the Mueller and Müller (M&M) discrete-time error-tracking synchronizer. + * + * See "Digital Communication Receivers: Synchronization, Channel + * Estimation and Signal Processing" by Heinrich Meyr, Marc Moeneclaey, & Stefan Fechtel. + * ISBN 0-471-50275-8. + */ +int +digital_clock_recovery_mm_ff::general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const float *in = (const float *) input_items[0]; + float *out = (float *) output_items[0]; + + int ii = 0; // input index + int oo = 0; // output index + int ni = ninput_items[0] - d_interp->ntaps(); // don't use more input than this + float mm_val; + + while (oo < noutput_items && ii < ni ){ + + // produce output sample + out[oo] = d_interp->interpolate (&in[ii], d_mu); + mm_val = slice(d_last_sample) * out[oo] - slice(out[oo]) * d_last_sample; + d_last_sample = out[oo]; + + d_omega = d_omega + d_gain_omega * mm_val; + d_omega = d_omega_mid + gr_branchless_clip(d_omega-d_omega_mid, d_omega_relative_limit); // make sure we don't walk away + d_mu = d_mu + d_omega + d_gain_mu * mm_val; + + ii += (int) floor(d_mu); + d_mu = d_mu - floor(d_mu); + oo++; + + if (DEBUG_CR_MM_FF && d_logfile){ + fwrite(&d_omega, sizeof(d_omega), 1, d_logfile); + } + } + + consume_each (ii); + + return oo; +} diff --git a/gr-digital/lib/digital_cma_equalizer_cc.h b/gr-digital/lib/digital_cma_equalizer_cc.h deleted file mode 100644 index 0dd99debd..000000000 --- a/gr-digital/lib/digital_cma_equalizer_cc.h +++ /dev/null @@ -1,101 +0,0 @@ -/* -*- 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. - */ - -#ifndef INCLUDED_DIGITAL_CMA_EQUALIZER_CC_H -#define INCLUDED_DIGITAL_CMA_EQUALIZER_CC_H - -#include <gr_adaptive_fir_ccc.h> -#include <gr_math.h> -#include <iostream> - -class digital_cma_equalizer_cc; -typedef boost::shared_ptr<digital_cma_equalizer_cc> digital_cma_equalizer_cc_sptr; - -digital_cma_equalizer_cc_sptr -digital_make_cma_equalizer_cc(int num_taps, float modulus, float mu, int sps); - -/*! - * \brief Implements constant modulus adaptive filter on complex stream - * \ingroup eq_blk - * - * The error value and tap update equations (for p=2) can be found in: - * - * D. Godard, "Self-Recovering Equalization and Carrier Tracking in - * Two-Dimensional Data Communication Systems," IEEE Transactions on - * Communications, Vol. 28, No. 11, pp. 1867 - 1875, 1980, - */ -class digital_cma_equalizer_cc : public gr_adaptive_fir_ccc -{ -private: - float d_modulus; - float d_mu; - - friend digital_cma_equalizer_cc_sptr digital_make_cma_equalizer_cc(int num_taps, - float modulus, - float mu, - int sps); - digital_cma_equalizer_cc(int num_taps, float modulus, float mu, int sps); - -protected: - - virtual gr_complex error(const gr_complex &out) - { - gr_complex error = out*(norm(out) - d_modulus); - float re = gr_clip(error.real(), 1.0); - float im = gr_clip(error.imag(), 1.0); - return gr_complex(re, im); - } - - virtual void update_tap(gr_complex &tap, const gr_complex &in) - { - // Hn+1 = Hn - mu*conj(Xn)*zn*(|zn|^2 - 1) - tap -= d_mu*conj(in)*d_error; - } - -public: - float get_gain() - { - return d_mu; - } - - void set_gain(float mu) - { - if(mu < 0.0f || mu > 1.0f) { - throw std::out_of_range("digital_cma_equalizer::set_gain: Gain value must be in [0,1]"); - } - d_mu = mu; - } - - float get_modulus() - { - return d_modulus; - } - - void set_modulus(float mod) - { - if(mod < 0) - throw std::out_of_range("digital_cma_equalizer::set_modulus: Modulus value must be >= 0"); - d_modulus = mod; - } -}; - -#endif diff --git a/gr-digital/lib/digital_constellation.cc b/gr-digital/lib/digital_constellation.cc index d1f218439..0c100f38e 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 deleted file mode 100644 index d345ebc10..000000000 --- a/gr-digital/lib/digital_constellation.h +++ /dev/null @@ -1,352 +0,0 @@ -/* -*- c++ -*- */ -/* - * 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. - */ - -#ifndef INCLUDED_DIGITAL_CONSTELLATION_H -#define INCLUDED_DIGITAL_CONSTELLATION_H - -#include <vector> -#include <math.h> -#include <gr_complex.h> -#include <boost/enable_shared_from_this.hpp> -#include <digital_metric_type.h> - -/************************************************************/ -/* digital_constellation */ -/* */ -/* Base class defining interface. */ -/************************************************************/ - -class digital_constellation; -typedef boost::shared_ptr<digital_constellation> digital_constellation_sptr; - -class 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 (); - - //! Returns the constellation points for a symbol value - void map_to_points(unsigned int value, gr_complex *points); - std::vector<gr_complex> map_to_points_v(unsigned int value); - - //! Returns the constellation point that matches best. - virtual unsigned int decision_maker (const gr_complex *sample) = 0; - //! Takes a vector rather than a pointer. Better for SWIG wrapping. - unsigned int decision_maker_v (std::vector<gr_complex> sample); - //! Also calculates the phase error. - unsigned int decision_maker_pe (const gr_complex *sample, float *phase_error); - //! Calculates distance. - unsigned int decision_maker_e (const gr_complex *sample, float *error); - - //! Calculates metrics for all points in the constellation. - //! For use with the viterbi algorithm. - virtual void calc_metric(const gr_complex *sample, float *metric, trellis_metric_type_t type); - virtual void calc_euclidean_metric(const gr_complex *sample, float *metric); - virtual void calc_hard_symbol_metric(const gr_complex *sample, float *metric); - - //! Returns the set of points in this constellation. - std::vector<gr_complex> points() { return d_constellation;} - //! Returns the vector of points in this constellation. - //! Raise error if dimensionality is not one. - std::vector<gr_complex> s_points(); - //! Returns a vector of vectors of points. - 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;} - //! 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. - unsigned int rotational_symmetry() { return d_rotational_symmetry;} - //! Returns the number of complex numbers in a single symbol. - unsigned int dimensionality() {return d_dimensionality;} - - unsigned int bits_per_symbol () { - return floor(log(double(d_constellation.size()))/d_dimensionality/log(2.0)); - } - - unsigned int arity () { - return d_arity; - } - - digital_constellation_sptr base() { - return shared_from_this(); - } - - protected: - - std::vector<gr_complex> d_constellation; - std::vector<unsigned int> d_pre_diff_code; - bool d_apply_pre_diff_code; - unsigned int d_rotational_symmetry; - unsigned int d_dimensionality; - unsigned int d_arity; - - float get_distance(unsigned int index, const gr_complex *sample); - unsigned int get_closest_point(const gr_complex *sample); - void calc_arity (); -}; - -/************************************************************/ -/* digital_constellation_calcdist */ -/* */ -/* Constellation which calculates the distance to each */ -/* point in the constellation for decision making. */ -/* Inefficient for large constellations. */ -/************************************************************/ - -class digital_constellation_calcdist; -typedef boost::shared_ptr<digital_constellation_calcdist> digital_constellation_calcdist_sptr; - -// public constructor -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); - - -class 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); - 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); - // void calc_hard_symbol_metric(gr_complex *sample, float *metric); - - private: - friend digital_constellation_calcdist_sptr - digital_make_constellation_calcdist (std::vector<gr_complex> constellation); -}; - -/************************************************************/ -/* digital_constellation_sector */ -/* */ -/* An abstract class. */ -/* Constellation space is divided into sectors. */ -/* Each sector is associated with the nearest constellation */ -/* point. */ -/************************************************************/ - -class 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); - - unsigned int decision_maker (const gr_complex *sample); - - protected: - - virtual unsigned int get_sector (const gr_complex *sample) = 0; - virtual unsigned int calc_sector_value (unsigned int sector) = 0; - void find_sector_values (); - - unsigned int n_sectors; - - private: - - std::vector<unsigned int> sector_values; - -}; - -/************************************************************/ -/* digital_constellation_rect */ -/* */ -/* Only implemented for 1-(complex)dimensional */ -/* constellation. */ -/* Constellation space is divided into rectangular sectors. */ -/* Each sector is associated with the nearest constellation */ -/* point. */ -/* Works well for square QAM. */ -/* Works for any generic constellation provided sectors are */ -/* not too large. */ -/************************************************************/ - -class digital_constellation_rect; -typedef boost::shared_ptr<digital_constellation_rect> digital_constellation_rect_sptr; - -// public constructor -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); - -class 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); - - protected: - - unsigned int get_sector (const gr_complex *sample); - - unsigned int calc_sector_value (unsigned int sector); - - private: - - unsigned int n_real_sectors; - unsigned int n_imag_sectors; - float d_width_real_sectors; - float d_width_imag_sectors; - - friend 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_constellation_psk */ -/* */ -/* Constellation space is divided into pie slices sectors. */ -/* Each slice is associated with the nearest constellation */ -/* point. */ -/* Works well for PSK but nothing else. */ -/* Assumes that there is a constellation point at 1. */ -/************************************************************/ - -class digital_constellation_psk; -typedef boost::shared_ptr<digital_constellation_psk> digital_constellation_psk_sptr; - -// public constructor -digital_constellation_psk_sptr -digital_make_constellation_psk (std::vector<gr_complex> constellation, - std::vector<unsigned int> pre_diff_code, - unsigned int n_sectors); - -class digital_constellation_psk : public digital_constellation_sector -{ - public: - - digital_constellation_psk (std::vector<gr_complex> constellation, - std::vector<unsigned int> pre_diff_code, - unsigned int n_sectors); - - protected: - - unsigned int get_sector (const gr_complex *sample); - - unsigned int calc_sector_value (unsigned int sector); - - private: - - friend digital_constellation_psk_sptr - digital_make_constellation_psk (std::vector<gr_complex> constellation, - std::vector<unsigned int> pre_diff_code, - unsigned int n_sectors); - -}; - -/************************************************************/ -/* digital_constellation_bpsk */ -/* */ -/* Only works for BPSK. */ -/* */ -/************************************************************/ - -class digital_constellation_bpsk; -typedef boost::shared_ptr<digital_constellation_bpsk> digital_constellation_bpsk_sptr; - -// public constructor -digital_constellation_bpsk_sptr -digital_make_constellation_bpsk (); - -class digital_constellation_bpsk : public digital_constellation -{ - public: - - digital_constellation_bpsk (); - unsigned int decision_maker (const gr_complex *sample); - - friend digital_constellation_bpsk_sptr - digital_make_constellation_bpsk (); - -}; - -/************************************************************/ -/* digital_constellation_qpsk */ -/* */ -/* Only works for QPSK. */ -/* */ -/************************************************************/ - -class digital_constellation_qpsk; -typedef boost::shared_ptr<digital_constellation_qpsk> digital_constellation_qpsk_sptr; - -// public constructor -digital_constellation_qpsk_sptr -digital_make_constellation_qpsk (); - -class digital_constellation_qpsk : public digital_constellation -{ - public: - - digital_constellation_qpsk (); - unsigned int decision_maker (const gr_complex *sample); - - friend digital_constellation_qpsk_sptr - digital_make_constellation_qpsk (); - -}; - - -/************************************************************/ -/* digital_constellation_8psk */ -/* */ -/* Only works for 8PSK. */ -/* */ -/************************************************************/ - -class digital_constellation_8psk; -typedef boost::shared_ptr<digital_constellation_8psk> digital_constellation_8psk_sptr; - -// public constructor -digital_constellation_8psk_sptr -digital_make_constellation_8psk (); - -class digital_constellation_8psk : public digital_constellation -{ - public: - - digital_constellation_8psk (); - unsigned int decision_maker (const gr_complex *sample); - - friend digital_constellation_8psk_sptr - digital_make_constellation_8psk (); - -}; - -#endif diff --git a/gr-digital/lib/digital_constellation_decoder_cb.h b/gr-digital/lib/digital_constellation_decoder_cb.h deleted file mode 100644 index 022456733..000000000 --- a/gr-digital/lib/digital_constellation_decoder_cb.h +++ /dev/null @@ -1,64 +0,0 @@ -/* -*- 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. - */ - -#ifndef INCLUDED_DIGITAL_CONSTELLATION_DECODER_CB_H -#define INCLUDED_DIGITAL_CONSTELLATION_DECODER_CB_H - -#include <gr_block.h> -#include <digital_constellation.h> -#include <vector> - -class digital_constellation_decoder_cb; -typedef boost::shared_ptr<digital_constellation_decoder_cb>digital_constellation_decoder_cb_sptr; - -digital_constellation_decoder_cb_sptr -digital_make_constellation_decoder_cb (digital_constellation_sptr constellation); - -/*! - * \brief Constellation Decoder - * \ingroup coding_blk - * - */ -class digital_constellation_decoder_cb : public gr_block -{ - - private: - digital_constellation_sptr d_constellation; - unsigned int d_dim; - - friend digital_constellation_decoder_cb_sptr - digital_make_constellation_decoder_cb (digital_constellation_sptr constellation); - - digital_constellation_decoder_cb (digital_constellation_sptr constellation); - - public: - - void forecast (int noutput_items, - gr_vector_int &ninput_items_required); - - int general_work (int noutput_items, - gr_vector_int &ninput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); -}; - -#endif diff --git a/gr-digital/lib/digital_constellation_receiver_cb.cc b/gr-digital/lib/digital_constellation_receiver_cb.cc index 573c4e855..b9239962a 100644 --- a/gr-digital/lib/digital_constellation_receiver_cb.cc +++ b/gr-digital/lib/digital_constellation_receiver_cb.cc @@ -40,23 +40,21 @@ digital_constellation_receiver_cb_sptr digital_make_constellation_receiver_cb(digital_constellation_sptr constell, - float alpha, float beta, - float fmin, float fmax) + float loop_bw, float fmin, float fmax) { return gnuradio::get_initial_sptr(new digital_constellation_receiver_cb (constell, - alpha, beta, + loop_bw, fmin, fmax)); } static int ios[] = {sizeof(char), sizeof(float), sizeof(float), sizeof(float)}; static std::vector<int> iosig(ios, ios+sizeof(ios)/sizeof(int)); digital_constellation_receiver_cb::digital_constellation_receiver_cb (digital_constellation_sptr constellation, - float alpha, float beta, - float fmin, float fmax) + float loop_bw, float fmin, float fmax) : gr_block ("constellation_receiver_cb", gr_make_io_signature (1, 1, sizeof (gr_complex)), gr_make_io_signaturev (1, 4, iosig)), - d_alpha(alpha), d_beta(beta), 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) { @@ -67,17 +65,9 @@ digital_constellation_receiver_cb::digital_constellation_receiver_cb (digital_co 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", @@ -113,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 deleted file mode 100644 index bf6fc51fa..000000000 --- a/gr-digital/lib/digital_constellation_receiver_cb.h +++ /dev/null @@ -1,149 +0,0 @@ -/* -*- 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. - */ - -#ifndef INCLUDED_DIGITAL_CONSTELLATION_RECEIVER_CB_H -#define INCLUDED_DIGITAL_CONSTELLATION_RECEIVER_CB_H - -#include <gr_block.h> -#include <digital_constellation.h> -#include <gr_complex.h> -#include <math.h> -#include <fstream> - -class digital_constellation_receiver_cb; -typedef boost::shared_ptr<digital_constellation_receiver_cb> digital_constellation_receiver_cb_sptr; - -// public constructor -digital_constellation_receiver_cb_sptr -digital_make_constellation_receiver_cb (digital_constellation_sptr constellation, - float alpha, float beta, - float fmin, float fmax); - -/*! - * \brief This block takes care of receiving generic modulated signals through phase, frequency, and symbol - * synchronization. - * \ingroup sync_blk - * \ingroup demod_blk - * - * This block takes care of receiving generic modulated signals through phase, frequency, and symbol - * synchronization. It performs carrier frequency and phase locking as well as symbol timing recovery. - * - * 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. - * - * 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. - * - * 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_constellation_receiver_cb : public gr_block -{ - 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); - - - // 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 constellation constellation of points for generic modulation - * \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 fmin minimum normalized frequency value the loop can achieve - * \param fmax maximum normalized frequency value the loop can achieve - * - * The constructor also chooses which phase detector and decision maker to use in the - * work loop based on the value of M. - */ - digital_constellation_receiver_cb (digital_constellation_sptr constellation, - float alpha, float beta, - float fmin, float fmax); - - void phase_error_tracking(float phase_error); - - private: - unsigned int d_M; - - // 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; - - digital_constellation_sptr d_constellation; - unsigned int d_current_const_point; - - //! delay line length. - static const unsigned int DLLEN = 8; - - //! delay line plus some length for overflow protection - gr_complex d_dl[2*DLLEN] __attribute__ ((aligned(8))); - - //! index to delay line - unsigned int d_dl_idx; - - friend digital_constellation_receiver_cb_sptr - digital_make_constellation_receiver_cb (digital_constellation_sptr constell, - float alpha, float beta, - float fmin, float fmax); -}; - -#endif diff --git a/gr-digital/lib/digital_correlate_access_code_bb.cc b/gr-digital/lib/digital_correlate_access_code_bb.cc new file mode 100644 index 000000000..f21b57d92 --- /dev/null +++ b/gr-digital/lib/digital_correlate_access_code_bb.cc @@ -0,0 +1,134 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2006,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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_correlate_access_code_bb.h> +#include <gr_io_signature.h> +#include <stdexcept> +#include <gr_count_bits.h> +#include <cstdio> + + +#define VERBOSE 0 + + +digital_correlate_access_code_bb_sptr +digital_make_correlate_access_code_bb (const std::string &access_code, int threshold) +{ + return gnuradio::get_initial_sptr(new digital_correlate_access_code_bb + (access_code, threshold)); +} + + +digital_correlate_access_code_bb::digital_correlate_access_code_bb ( + const std::string &access_code, int threshold) + : gr_sync_block ("correlate_access_code_bb", + gr_make_io_signature (1, 1, sizeof(char)), + gr_make_io_signature (1, 1, sizeof(char))), + d_data_reg(0), d_flag_reg(0), d_flag_bit(0), d_mask(0), + d_threshold(threshold) + +{ + if (!set_access_code(access_code)){ + fprintf(stderr, "digital_correlate_access_code_bb: access_code is > 64 bits\n"); + throw std::out_of_range ("access_code is > 64 bits"); + } +} + +digital_correlate_access_code_bb::~digital_correlate_access_code_bb () +{ +} + +bool +digital_correlate_access_code_bb::set_access_code( + const std::string &access_code) +{ + unsigned len = access_code.length(); // # of bytes in string + if (len > 64) + return false; + + // set len top bits to 1. + d_mask = ((~0ULL) >> (64 - len)) << (64 - len); + + d_flag_bit = 1LL << (64 - len); // Where we or-in new flag values. + // new data always goes in 0x0000000000000001 + d_access_code = 0; + for (unsigned i=0; i < 64; i++){ + d_access_code <<= 1; + if (i < len) + d_access_code |= access_code[i] & 1; // look at LSB only + } + + return true; +} + +int +digital_correlate_access_code_bb::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const unsigned char *in = (const unsigned char *) input_items[0]; + unsigned char *out = (unsigned char *) output_items[0]; + + for (int i = 0; i < noutput_items; i++){ + + // compute output value + unsigned int t = 0; + + t |= ((d_data_reg >> 63) & 0x1) << 0; + t |= ((d_flag_reg >> 63) & 0x1) << 1; // flag bit + out[i] = t; + + // compute hamming distance between desired access code and current data + unsigned long long wrong_bits = 0; + unsigned int nwrong = d_threshold+1; + int new_flag = 0; + + wrong_bits = (d_data_reg ^ d_access_code) & d_mask; + nwrong = gr_count_bits64(wrong_bits); + + // test for access code with up to threshold errors + new_flag = (nwrong <= d_threshold); + +#if VERBOSE + if(new_flag) { + fprintf(stderr, "access code found: %llx\n", d_access_code); + } + else { + fprintf(stderr, "%llx ==> %llx\n", d_access_code, d_data_reg); + } +#endif + + // shift in new data and new flag + d_data_reg = (d_data_reg << 1) | (in[i] & 0x1); + d_flag_reg = (d_flag_reg << 1); + if (new_flag) { + d_flag_reg |= d_flag_bit; + } + } + + return noutput_items; +} + diff --git a/gr-digital/lib/digital_costas_loop_cc.cc b/gr-digital/lib/digital_costas_loop_cc.cc index 5d98bde4c..370dc7e5c 100644 --- a/gr-digital/lib/digital_costas_loop_cc.cc +++ b/gr-digital/lib/digital_costas_loop_cc.cc @@ -30,31 +30,23 @@ #include <gr_sincos.h> #include <gr_math.h> -#define M_TWOPI (2*M_PI) - digital_costas_loop_cc_sptr -digital_make_costas_loop_cc (float damping, float nat_freq, - int order +digital_make_costas_loop_cc (float loop_bw, int order ) throw (std::invalid_argument) { - return gnuradio::get_initial_sptr(new digital_costas_loop_cc (damping, - nat_freq, - order)); + return gnuradio::get_initial_sptr(new digital_costas_loop_cc + (loop_bw, order)); } -digital_costas_loop_cc::digital_costas_loop_cc (float damping, float nat_freq, - int order +digital_costas_loop_cc::digital_costas_loop_cc (float loop_bw, int order ) throw (std::invalid_argument) : gr_sync_block ("costas_loop_cc", gr_make_io_signature (1, 1, sizeof (gr_complex)), gr_make_io_signature2 (1, 2, sizeof (gr_complex), sizeof(float))), - d_max_freq(1.0), d_min_freq(-1.0), d_phase(0), d_freq(0.0), - d_nat_freq(nat_freq), d_damping(damping), + gri_control_loop(loop_bw, 1.0, -1.0), d_order(order), d_phase_detector(NULL) { - // initialize gains from the natural freq and damping factors - update_gains(); - + // Set up the phase detector to use based on the constellation order switch(d_order) { case 2: d_phase_detector = &digital_costas_loop_cc::phase_detector_2; @@ -115,27 +107,6 @@ digital_costas_loop_cc::phase_detector_2(gr_complex sample) const return (sample.real()*sample.imag()); } -void -digital_costas_loop_cc::set_natural_freq(float w) -{ - d_nat_freq = w; - update_gains(); -} - -void -digital_costas_loop_cc::set_damping_factor(float eta) -{ - d_damping = eta; - update_gains(); -} - -void -digital_costas_loop_cc::update_gains() -{ - d_beta = d_nat_freq*d_nat_freq; - d_alpha = 2*d_damping*d_nat_freq; -} - int digital_costas_loop_cc::work (int noutput_items, gr_vector_const_void_star &input_items, @@ -159,18 +130,9 @@ digital_costas_loop_cc::work (int noutput_items, error = (*this.*d_phase_detector)(optr[i]); error = gr_branchless_clip(error, 1.0); - d_freq = d_freq + d_beta * error; - d_phase = d_phase + d_freq + d_alpha * error; - - while(d_phase>M_TWOPI) - d_phase -= M_TWOPI; - while(d_phase<-M_TWOPI) - d_phase += M_TWOPI; - - if (d_freq > d_max_freq) - d_freq = d_min_freq; - else if (d_freq < d_min_freq) - d_freq = d_max_freq; + advance_loop(error); + phase_wrap(); + frequency_limit(); foptr[i] = d_freq; } @@ -181,20 +143,10 @@ digital_costas_loop_cc::work (int noutput_items, error = (*this.*d_phase_detector)(optr[i]); error = gr_branchless_clip(error, 1.0); - - d_freq = d_freq + d_beta * error; - d_phase = d_phase + d_freq + d_alpha * error; - - while(d_phase>M_TWOPI) - d_phase -= M_TWOPI; - while(d_phase<-M_TWOPI) - d_phase += M_TWOPI; - - if (d_freq > d_max_freq) - d_freq = d_min_freq; - else if (d_freq < d_min_freq) - d_freq = d_max_freq; - + + advance_loop(error); + phase_wrap(); + frequency_limit(); } } return noutput_items; diff --git a/gr-digital/lib/digital_costas_loop_cc.h b/gr-digital/lib/digital_costas_loop_cc.h deleted file mode 100644 index 9c112d328..000000000 --- a/gr-digital/lib/digital_costas_loop_cc.h +++ /dev/null @@ -1,150 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2006,2011 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - - -#ifndef INCLUDED_DIGITAL_COSTAS_LOOP_CC_H -#define INCLUDED_DIGITAL_COSTAS_LOOP_CC_H - -#include <gr_sync_block.h> -#include <stdexcept> -#include <fstream> - - -/*! \brief A Costas loop carrier recovery module. - * \ingroup sync_blk - * - * The Costas loop locks to the center frequency of a signal and - * downconverts it to baseband. The second (order=2) order loop is - * used for BPSK where the real part of the output signal is the - * baseband BPSK signal and the imaginary part is the error - * signal. When order=4, it can be used for quadrature modulations - * where both I and Q (real and imaginary) are outputted. - * - * More details can be found online: - * - * J. Feigin, "Practical Costas loop design: Designing a simple and inexpensive - * BPSK Costas loop carrier recovery circuit," RF signal processing, pp. 20-36, - * 2002. - * - * http://rfdesign.com/images/archive/0102Feigin20.pdf - * - * \param alpha the loop gain used for phase adjustment - * \param beta the loop gain for frequency adjustments - * \param max_freq the maximum frequency deviation (radians/sample) the loop can handle - * \param min_freq the minimum frequency deviation (radians/sample) the loop can handle - * \param order the loop order, either 2 or 4 - */ -class digital_costas_loop_cc; -typedef boost::shared_ptr<digital_costas_loop_cc> digital_costas_loop_cc_sptr; - - -digital_costas_loop_cc_sptr -digital_make_costas_loop_cc (float damping, float nat_freq, - int order - ) throw (std::invalid_argument); - - -/*! - * \brief Carrier tracking PLL for QPSK - * \ingroup sync_blk - * input: complex; output: complex - * <br>The Costas loop can have two output streams: - * stream 1 is the baseband I and Q; - * stream 2 is the normalized frequency of the loop - * - * \p order must be 2 or 4. - */ -class digital_costas_loop_cc : public gr_sync_block -{ - friend digital_costas_loop_cc_sptr - digital_make_costas_loop_cc (float damping, float nat_freq, - int order - ) throw (std::invalid_argument); - - float d_alpha, d_beta, d_max_freq, d_min_freq, d_phase, d_freq; - float d_nat_freq, d_damping; - int d_order; - - digital_costas_loop_cc (float damping, float nat_freq, - int order - ) throw (std::invalid_argument); - - - /*! \brief update the system gains from omega and eta - * - * This function updates the system gains based on the natural - * frequency (omega) and damping factor (eta) of the system. - * These two factors can be set separately through their own - * set functions. - * - * These equations are summarized nicely in this paper from Berkeley: - * http://www.complextoreal.com/chapters/pll.pdf - */ - void update_gains(); - - /*! \brief the phase detector circuit for 8th-order PSK loops - * \param sample complex sample - * \return the phase error - */ - float phase_detector_8(gr_complex sample) const; // for 8PSK - - /*! \brief the phase detector circuit for fourth-order loops - * \param sample complex sample - * \return the phase error - */ - float phase_detector_4(gr_complex sample) const; // for QPSK - - /*! \brief the phase detector circuit for second-order loops - * \param sample a complex sample - * \return the phase error - */ - float phase_detector_2(gr_complex sample) const; // for BPSK - - - float (digital_costas_loop_cc::*d_phase_detector)(gr_complex sample) const; - -public: - - void set_natural_freq(float w); - void set_damping_factor(float eta); - - /*! \brief get the first order gain - * - */ - float alpha() const { return d_alpha; } - - /*! \brief get the second order gain - * - */ - float beta() const { return d_beta; } - - int work (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); - - /*! \brief returns the current NCO frequency in radians/sample - * - */ - float freq() const { return d_freq; } -}; - -#endif diff --git a/gr-digital/lib/digital_cpmmod_bc.cc b/gr-digital/lib/digital_cpmmod_bc.cc new file mode 100644 index 000000000..a95b604d1 --- /dev/null +++ b/gr-digital/lib/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/lib/digital_crc32.cc b/gr-digital/lib/digital_crc32.cc new file mode 100644 index 000000000..8806d6e9c --- /dev/null +++ b/gr-digital/lib/digital_crc32.cc @@ -0,0 +1,130 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* + * See also ISO 3309 [ISO-3309] or ITU-T V.42 [ITU-V42] for a formal specification. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <digital_crc32.h> + + +// Automatically generated CRC function +// polynomial: 0x104C11DB7 +unsigned int +digital_update_crc32(unsigned int crc, const unsigned char *data, size_t len) +{ + static const unsigned int table[256] = { + 0x00000000U,0x04C11DB7U,0x09823B6EU,0x0D4326D9U, + 0x130476DCU,0x17C56B6BU,0x1A864DB2U,0x1E475005U, + 0x2608EDB8U,0x22C9F00FU,0x2F8AD6D6U,0x2B4BCB61U, + 0x350C9B64U,0x31CD86D3U,0x3C8EA00AU,0x384FBDBDU, + 0x4C11DB70U,0x48D0C6C7U,0x4593E01EU,0x4152FDA9U, + 0x5F15ADACU,0x5BD4B01BU,0x569796C2U,0x52568B75U, + 0x6A1936C8U,0x6ED82B7FU,0x639B0DA6U,0x675A1011U, + 0x791D4014U,0x7DDC5DA3U,0x709F7B7AU,0x745E66CDU, + 0x9823B6E0U,0x9CE2AB57U,0x91A18D8EU,0x95609039U, + 0x8B27C03CU,0x8FE6DD8BU,0x82A5FB52U,0x8664E6E5U, + 0xBE2B5B58U,0xBAEA46EFU,0xB7A96036U,0xB3687D81U, + 0xAD2F2D84U,0xA9EE3033U,0xA4AD16EAU,0xA06C0B5DU, + 0xD4326D90U,0xD0F37027U,0xDDB056FEU,0xD9714B49U, + 0xC7361B4CU,0xC3F706FBU,0xCEB42022U,0xCA753D95U, + 0xF23A8028U,0xF6FB9D9FU,0xFBB8BB46U,0xFF79A6F1U, + 0xE13EF6F4U,0xE5FFEB43U,0xE8BCCD9AU,0xEC7DD02DU, + 0x34867077U,0x30476DC0U,0x3D044B19U,0x39C556AEU, + 0x278206ABU,0x23431B1CU,0x2E003DC5U,0x2AC12072U, + 0x128E9DCFU,0x164F8078U,0x1B0CA6A1U,0x1FCDBB16U, + 0x018AEB13U,0x054BF6A4U,0x0808D07DU,0x0CC9CDCAU, + 0x7897AB07U,0x7C56B6B0U,0x71159069U,0x75D48DDEU, + 0x6B93DDDBU,0x6F52C06CU,0x6211E6B5U,0x66D0FB02U, + 0x5E9F46BFU,0x5A5E5B08U,0x571D7DD1U,0x53DC6066U, + 0x4D9B3063U,0x495A2DD4U,0x44190B0DU,0x40D816BAU, + 0xACA5C697U,0xA864DB20U,0xA527FDF9U,0xA1E6E04EU, + 0xBFA1B04BU,0xBB60ADFCU,0xB6238B25U,0xB2E29692U, + 0x8AAD2B2FU,0x8E6C3698U,0x832F1041U,0x87EE0DF6U, + 0x99A95DF3U,0x9D684044U,0x902B669DU,0x94EA7B2AU, + 0xE0B41DE7U,0xE4750050U,0xE9362689U,0xEDF73B3EU, + 0xF3B06B3BU,0xF771768CU,0xFA325055U,0xFEF34DE2U, + 0xC6BCF05FU,0xC27DEDE8U,0xCF3ECB31U,0xCBFFD686U, + 0xD5B88683U,0xD1799B34U,0xDC3ABDEDU,0xD8FBA05AU, + 0x690CE0EEU,0x6DCDFD59U,0x608EDB80U,0x644FC637U, + 0x7A089632U,0x7EC98B85U,0x738AAD5CU,0x774BB0EBU, + 0x4F040D56U,0x4BC510E1U,0x46863638U,0x42472B8FU, + 0x5C007B8AU,0x58C1663DU,0x558240E4U,0x51435D53U, + 0x251D3B9EU,0x21DC2629U,0x2C9F00F0U,0x285E1D47U, + 0x36194D42U,0x32D850F5U,0x3F9B762CU,0x3B5A6B9BU, + 0x0315D626U,0x07D4CB91U,0x0A97ED48U,0x0E56F0FFU, + 0x1011A0FAU,0x14D0BD4DU,0x19939B94U,0x1D528623U, + 0xF12F560EU,0xF5EE4BB9U,0xF8AD6D60U,0xFC6C70D7U, + 0xE22B20D2U,0xE6EA3D65U,0xEBA91BBCU,0xEF68060BU, + 0xD727BBB6U,0xD3E6A601U,0xDEA580D8U,0xDA649D6FU, + 0xC423CD6AU,0xC0E2D0DDU,0xCDA1F604U,0xC960EBB3U, + 0xBD3E8D7EU,0xB9FF90C9U,0xB4BCB610U,0xB07DABA7U, + 0xAE3AFBA2U,0xAAFBE615U,0xA7B8C0CCU,0xA379DD7BU, + 0x9B3660C6U,0x9FF77D71U,0x92B45BA8U,0x9675461FU, + 0x8832161AU,0x8CF30BADU,0x81B02D74U,0x857130C3U, + 0x5D8A9099U,0x594B8D2EU,0x5408ABF7U,0x50C9B640U, + 0x4E8EE645U,0x4A4FFBF2U,0x470CDD2BU,0x43CDC09CU, + 0x7B827D21U,0x7F436096U,0x7200464FU,0x76C15BF8U, + 0x68860BFDU,0x6C47164AU,0x61043093U,0x65C52D24U, + 0x119B4BE9U,0x155A565EU,0x18197087U,0x1CD86D30U, + 0x029F3D35U,0x065E2082U,0x0B1D065BU,0x0FDC1BECU, + 0x3793A651U,0x3352BBE6U,0x3E119D3FU,0x3AD08088U, + 0x2497D08DU,0x2056CD3AU,0x2D15EBE3U,0x29D4F654U, + 0xC5A92679U,0xC1683BCEU,0xCC2B1D17U,0xC8EA00A0U, + 0xD6AD50A5U,0xD26C4D12U,0xDF2F6BCBU,0xDBEE767CU, + 0xE3A1CBC1U,0xE760D676U,0xEA23F0AFU,0xEEE2ED18U, + 0xF0A5BD1DU,0xF464A0AAU,0xF9278673U,0xFDE69BC4U, + 0x89B8FD09U,0x8D79E0BEU,0x803AC667U,0x84FBDBD0U, + 0x9ABC8BD5U,0x9E7D9662U,0x933EB0BBU,0x97FFAD0CU, + 0xAFB010B1U,0xAB710D06U,0xA6322BDFU,0xA2F33668U, + 0xBCB4666DU,0xB8757BDAU,0xB5365D03U,0xB1F740B4U, + }; + + while (len > 0) + { + crc = table[*data ^ ((crc >> 24) & 0xff)] ^ (crc << 8); + data++; + len--; + } + return crc; +} + +unsigned int +digital_update_crc32(unsigned int crc, const std::string s) +{ + return digital_update_crc32(crc, (const unsigned char *) s.data(), s.size()); +} + +unsigned int +digital_crc32(const unsigned char *buf, size_t len) +{ + return digital_update_crc32(0xffffffff, buf, len) ^ 0xffffffff; +} + +unsigned int +digital_crc32(const std::string s) +{ + return digital_crc32((const unsigned char *) s.data(), s.size()); +} diff --git a/gr-digital/lib/digital_fll_band_edge_cc.cc b/gr-digital/lib/digital_fll_band_edge_cc.cc new file mode 100644 index 000000000..05c092622 --- /dev/null +++ b/gr-digital/lib/digital_fll_band_edge_cc.cc @@ -0,0 +1,259 @@ +/* -*- c++ -*- */ +/* + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_fll_band_edge_cc.h> +#include <gr_io_signature.h> +#include <gr_expj.h> +#include <cstdio> + +#define M_TWOPI (2*M_PI) + +float sinc(float x) +{ + if(x == 0) + return 1; + else + return sin(M_PI*x)/(M_PI*x); +} + +digital_fll_band_edge_cc_sptr +digital_make_fll_band_edge_cc (float samps_per_sym, float rolloff, + int filter_size, float bandwidth) +{ + return gnuradio::get_initial_sptr(new digital_fll_band_edge_cc (samps_per_sym, rolloff, + filter_size, bandwidth)); +} + + +static int ios[] = {sizeof(gr_complex), sizeof(float), sizeof(float), sizeof(float)}; +static std::vector<int> iosig(ios, ios+sizeof(ios)/sizeof(int)); +digital_fll_band_edge_cc::digital_fll_band_edge_cc (float samps_per_sym, float rolloff, + int filter_size, float bandwidth) + : 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 + if(samps_per_sym <= 0) { + throw std::out_of_range ("digital_fll_band_edge_cc: invalid number of sps. Must be > 0."); + } + d_sps = samps_per_sym; + + // Initialize rolloff factor + if(rolloff < 0 || rolloff > 1.0) { + throw std::out_of_range ("digital_fll_band_edge_cc: invalid rolloff factor. Must be in [0,1]."); + } + d_rolloff = rolloff; + + // Initialize filter length + if(filter_size <= 0) { + throw std::out_of_range ("digital_fll_band_edge_cc: invalid filter size. Must be > 0."); + } + d_filter_size = filter_size; + + // Build the band edge filters + design_filter(d_sps, d_rolloff, d_filter_size); +} + +digital_fll_band_edge_cc::~digital_fll_band_edge_cc () +{ +} + + +/******************************************************************* + SET FUNCTIONS +*******************************************************************/ + +void +digital_fll_band_edge_cc::set_samples_per_symbol(float sps) +{ + if(sps <= 0) { + throw std::out_of_range ("digital_fll_band_edge_cc: invalid number of sps. Must be > 0."); + } + d_sps = sps; + design_filter(d_sps, d_rolloff, d_filter_size); +} + +void +digital_fll_band_edge_cc::set_rolloff(float rolloff) +{ + if(rolloff < 0 || rolloff > 1.0) { + throw std::out_of_range ("digital_fll_band_edge_cc: invalid rolloff factor. Must be in [0,1]."); + } + d_rolloff = rolloff; + design_filter(d_sps, d_rolloff, d_filter_size); +} + +void +digital_fll_band_edge_cc::set_filter_size(int filter_size) +{ + if(filter_size <= 0) { + throw std::out_of_range ("digital_fll_band_edge_cc: invalid filter size. Must be > 0."); + } + d_filter_size = filter_size; + design_filter(d_sps, d_rolloff, d_filter_size); +} + +/******************************************************************* + GET FUNCTIONS +*******************************************************************/ + +float +digital_fll_band_edge_cc::get_samples_per_symbol() const +{ + return d_sps; +} + +float +digital_fll_band_edge_cc::get_rolloff() const +{ + return d_rolloff; +} + +int +digital_fll_band_edge_cc:: get_filter_size() const +{ + return d_filter_size; +} + + +/******************************************************************* +*******************************************************************/ + +void +digital_fll_band_edge_cc::design_filter(float samps_per_sym, + float rolloff, int filter_size) +{ + int M = rint(filter_size / samps_per_sym); + float power = 0; + + // Create the baseband filter by adding two sincs together + std::vector<float> bb_taps; + for(int i = 0; i < filter_size; i++) { + float k = -M + i*2.0/samps_per_sym; + float tap = sinc(rolloff*k - 0.5) + sinc(rolloff*k + 0.5); + power += tap; + + bb_taps.push_back(tap); + } + + d_taps_lower.resize(filter_size); + d_taps_upper.resize(filter_size); + + // Create the band edge filters by spinning the baseband + // filter up and down to the right places in frequency. + // Also, normalize the power in the filters + int N = (bb_taps.size() - 1.0)/2.0; + for(int i = 0; i < filter_size; i++) { + float tap = bb_taps[i] / power; + + float k = (-N + (int)i)/(2.0*samps_per_sym); + + gr_complex t1 = tap * gr_expj(-M_TWOPI*(1+rolloff)*k); + gr_complex t2 = tap * gr_expj(M_TWOPI*(1+rolloff)*k); + + d_taps_lower[filter_size-i-1] = t1; + d_taps_upper[filter_size-i-1] = t2; + } + + d_updated = true; + + // Set the history to ensure enough input items for each filter + set_history(filter_size+1); +} + +void +digital_fll_band_edge_cc::print_taps() +{ + unsigned int i; + + printf("Upper Band-edge: ["); + for(i = 0; i < d_taps_upper.size(); i++) { + printf(" %.4e + %.4ej,", d_taps_upper[i].real(), d_taps_upper[i].imag()); + } + printf("]\n\n"); + + printf("Lower Band-edge: ["); + for(i = 0; i < d_taps_lower.size(); i++) { + printf(" %.4e + %.4ej,", d_taps_lower[i].real(), d_taps_lower[i].imag()); + } + printf("]\n\n"); +} + +int +digital_fll_band_edge_cc::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const gr_complex *in = (const gr_complex *) input_items[0]; + gr_complex *out = (gr_complex *) output_items[0]; + + float *frq = NULL; + float *phs = NULL; + float *err = NULL; + if(output_items.size() == 4) { + frq = (float *) output_items[1]; + phs = (float *) output_items[2]; + err = (float *) output_items[3]; + } + + if (d_updated) { + d_updated = false; + return 0; // history requirements may have changed. + } + + int i; + float error; + gr_complex nco_out; + gr_complex out_upper, out_lower; + for(i = 0; i < noutput_items; i++) { + nco_out = gr_expj(d_phase); + out[i+d_filter_size-1] = in[i] * nco_out; + + // Perform the dot product of the output with the filters + out_upper = 0; + out_lower = 0; + for(int k = 0; k < d_filter_size; k++) { + out_upper += d_taps_upper[k] * out[i+k]; + out_lower += d_taps_lower[k] * out[i+k]; + } + error = norm(out_lower) - norm(out_upper); + + advance_loop(error); + phase_wrap(); + frequency_limit(); + + if(output_items.size() == 4) { + frq[i] = d_freq; + phs[i] = d_phase; + err[i] = error; + } + } + + return noutput_items; +} diff --git a/gr-digital/lib/digital_metric_type.h b/gr-digital/lib/digital_gmskmod_bc.cc index 83de166f0..e53e90037 100644 --- a/gr-digital/lib/digital_metric_type.h +++ b/gr-digital/lib/digital_gmskmod_bc.cc @@ -1,31 +1,44 @@ /* -*- c++ -*- */ /* - * Copyright 2004 Free Software Foundation, Inc. - * + * 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_METRIC_TYPE_H -#define INCLUDED_DIGITAL_METRIC_TYPE_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif -typedef enum { - TRELLIS_EUCLIDEAN = 200, TRELLIS_HARD_SYMBOL, TRELLIS_HARD_BIT -} trellis_metric_type_t; +#include <digital_gmskmod_bc.h> +#include <gr_io_signature.h> -#endif +// 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/lib/digital_kurtotic_equalizer_cc.h b/gr-digital/lib/digital_kurtotic_equalizer_cc.h deleted file mode 100644 index e01cbd6e6..000000000 --- a/gr-digital/lib/digital_kurtotic_equalizer_cc.h +++ /dev/null @@ -1,111 +0,0 @@ -/* -*- 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. - */ - -#ifndef INCLUDED_DIGITAL_KURTOTIC_EQUALIZER_CC_H -#define INCLUDED_DIGITAL_KURTOTIC_EQUALIZER_CC_H - -#include <gr_adaptive_fir_ccc.h> -#include <gr_math.h> -#include <iostream> - -class digital_kurtotic_equalizer_cc; -typedef boost::shared_ptr<digital_kurtotic_equalizer_cc> digital_kurtotic_equalizer_cc_sptr; - -digital_kurtotic_equalizer_cc_sptr -digital_make_kurtotic_equalizer_cc(int num_taps, float mu); - -/*! - * \brief Implements a kurtosis-based adaptive equalizer on complex stream - * \ingroup eq_blk - * - * Y. Guo, J. Zhao, Y. Sun, "Sign kurtosis maximization based blind - * equalization algorithm," IEEE Conf. on Control, Automation, - * Robotics and Vision, Vol. 3, Dec. 2004, pp. 2052 - 2057. - */ -class digital_kurtotic_equalizer_cc : public gr_adaptive_fir_ccc -{ -private: - float d_mu; - float d_p, d_m; - gr_complex d_q, d_u; - float d_alpha_p, d_alpha_q, d_alpha_m; - - friend digital_kurtotic_equalizer_cc_sptr digital_make_kurtotic_equalizer_cc(int num_taps, - float mu); - digital_kurtotic_equalizer_cc(int num_taps, float mu); - - gr_complex sign(gr_complex x) - { - float re = (float)(x.real() >= 0.0f); - float im = (float)(x.imag() >= 0.0f); - return gr_complex(re, im); - } - -protected: - - virtual gr_complex error(const gr_complex &out) - { - - // p = E[|z|^2] - // q = E[z^2] - // m = E[|z|^4] - // u = E[kurtosis(z)] - - float nrm = norm(out); - gr_complex cnj = conj(out); - float epsilon_f = 1e-12; - gr_complex epsilon_c = gr_complex(1e-12, 1e-12); - - - d_p = (1-d_alpha_p)*d_p + (d_alpha_p)*nrm + epsilon_f; - d_q = (1-d_alpha_q)*d_q + (d_alpha_q)*out*out + epsilon_c; - d_m = (1-d_alpha_m)*d_m + (d_alpha_m)*nrm*nrm + epsilon_f; - d_u = d_m - 2.0f*(d_p*d_p) - d_q*d_q; - - gr_complex F = (1.0f / (d_p*d_p*d_p)) * - (sign(d_u) * (nrm*cnj - 2.0f*d_p*cnj - conj(d_q)*out) - - abs(d_u)*cnj); - - //std::cout << "out: " << out << " p: " << d_p << " q: " << d_q; - //std::cout << " m: " << d_m << " u: " << d_u << std::endl; - //std::cout << "error: " << F << std::endl; - - float re = gr_clip(F.real(), 1.0); - float im = gr_clip(F.imag(), 1.0); - return gr_complex(re, im); - } - - virtual void update_tap(gr_complex &tap, const gr_complex &in) - { - tap += d_mu*in*d_error; - } - -public: - void set_gain(float mu) - { - if(mu < 0) - throw std::out_of_range("digital_kurtotic_equalizer::set_gain: Gain value must be >= 0"); - d_mu = mu; - } -}; - -#endif diff --git a/gr-digital/lib/digital_lms_dd_equalizer_cc.h b/gr-digital/lib/digital_lms_dd_equalizer_cc.h deleted file mode 100644 index e3ad4bf4a..000000000 --- a/gr-digital/lib/digital_lms_dd_equalizer_cc.h +++ /dev/null @@ -1,116 +0,0 @@ -/* -*- 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. - */ - -#ifndef INCLUDED_DIGITAL_LMS_DD_EQUALIZER_CC_H -#define INCLUDED_DIGITAL_LMS_DD_EQUALIZER_CC_H - -#include <gr_adaptive_fir_ccc.h> -#include <digital_constellation.h> - -class digital_lms_dd_equalizer_cc; -typedef boost::shared_ptr<digital_lms_dd_equalizer_cc> digital_lms_dd_equalizer_cc_sptr; - -digital_lms_dd_equalizer_cc_sptr digital_make_lms_dd_equalizer_cc (int num_taps, - float mu, int sps, - digital_constellation_sptr cnst); - -/*! - * \brief Least-Mean-Square Decision Directed Equalizer (complex in/out) - * \ingroup eq_blk - * - * This block implements an LMS-based decision-directed equalizer. - * It uses a set of weights, w, to correlate against the inputs, u, - * and a decisions is then made from this output. The error - * in the decision is used to update teh weight vector. - * - * y[n] = conj(w[n]) u[n] - * d[n] = decision(y[n]) - * e[n] = d[n] - y[n] - * w[n+1] = w[n] + mu u[n] conj(e[n]) - * - * Where mu is a gain value (between 0 and 1 and usualy small, - * around 0.001 - 0.01. - * - * This block uses the digital_constellation object for making - * the decision from y[n]. Create the constellation object for - * whatever constellation is to be used and pass in the object. - * In Python, you can use something like: - * self.constellation = digital.constellation_qpsk() - * To create a QPSK constellation (see the digital_constellation - * block for more details as to what constellations are available - * or how to create your own). You then pass the object to this - * block as an sptr, or using "self.constellation.base()". - * - * The theory for this algorithm can be found in Chapter 9 of: - * S. Haykin, Adaptive Filter Theory, Upper Saddle River, NJ: - * Prentice Hall, 1996. - * - */ -class digital_lms_dd_equalizer_cc : public gr_adaptive_fir_ccc -{ -private: - friend digital_lms_dd_equalizer_cc_sptr digital_make_lms_dd_equalizer_cc (int num_taps, - float mu, int sps, - digital_constellation_sptr cnst); - - float d_mu; - std::vector<gr_complex> d_taps; - digital_constellation_sptr d_cnst; - - digital_lms_dd_equalizer_cc (int num_taps, - float mu, int sps, - digital_constellation_sptr cnst); - -protected: - - virtual gr_complex error(const gr_complex &out) - { - gr_complex decision, error; - d_cnst->map_to_points(d_cnst->decision_maker(&out), &decision); - error = decision - out; - return error; - } - - virtual void update_tap(gr_complex &tap, const gr_complex &in) - { - tap += d_mu*conj(in)*d_error; - } - -public: - float get_gain() - { - return d_mu; - } - - void set_gain(float mu) - { - if(mu < 0.0f || mu > 1.0f) { - throw std::out_of_range("digital_lms_dd_equalizer::set_mu: Gain value must in [0, 1]"); - } - else { - d_mu = mu; - } - } - -}; - -#endif diff --git a/gr-digital/lib/digital_mpsk_receiver_cc.cc b/gr-digital/lib/digital_mpsk_receiver_cc.cc new file mode 100644 index 000000000..363b86c9f --- /dev/null +++ b/gr-digital/lib/digital_mpsk_receiver_cc.cc @@ -0,0 +1,316 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2006,2007,2010,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_io_signature.h> +#include <gr_prefs.h> +#include <digital_mpsk_receiver_cc.h> +#include <stdexcept> +#include <gr_math.h> +#include <gr_expj.h> +#include <gri_mmse_fir_interpolator_cc.h> + + +#define M_TWOPI (2*M_PI) +#define VERBOSE_MM 0 // Used for debugging symbol timing loop +#define VERBOSE_COSTAS 0 // Used for debugging phase and frequency tracking + +// Public constructor + +digital_mpsk_receiver_cc_sptr +digital_make_mpsk_receiver_cc(unsigned int M, float theta, + 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, + 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 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_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), + d_p_2T(0), d_p_1T(0), d_p_0T(0), d_c_2T(0), d_c_1T(0), d_c_0T(0) +{ + d_interp = new gri_mmse_fir_interpolator_cc(); + d_dl_idx = 0; + + set_omega(omega); + + if (omega <= 0.0) + throw std::out_of_range ("clock rate must be > 0"); + if (gain_mu < 0 || gain_omega < 0) + throw std::out_of_range ("Gains must be non-negative"); + + assert(d_interp->ntaps() <= DLLEN); + + // zero double length delay line. + for (unsigned int i = 0; i < 2 * DLLEN; i++) + d_dl[i] = gr_complex(0.0,0.0); + + // build the constellation vector from M + make_constellation(); + + // Select a phase detector and a decision maker for the modulation order + switch(d_M) { + case 2: // optimized algorithms for BPSK + d_phase_error_detector = &digital_mpsk_receiver_cc::phase_error_detector_bpsk; //bpsk; + d_decision = &digital_mpsk_receiver_cc::decision_bpsk; + break; + + case 4: // optimized algorithms for QPSK + d_phase_error_detector = &digital_mpsk_receiver_cc::phase_error_detector_qpsk; //qpsk; + d_decision = &digital_mpsk_receiver_cc::decision_qpsk; + break; + + default: // generic algorithms for any M (power of 2?) but not pretty + d_phase_error_detector = &digital_mpsk_receiver_cc::phase_error_detector_generic; + d_decision = &digital_mpsk_receiver_cc::decision_generic; + break; + } +} + +digital_mpsk_receiver_cc::~digital_mpsk_receiver_cc () +{ + delete d_interp; +} + +void +digital_mpsk_receiver_cc::forecast(int noutput_items, gr_vector_int &ninput_items_required) +{ + unsigned ninputs = ninput_items_required.size(); + for (unsigned i=0; i < ninputs; i++) + ninput_items_required[i] = (int) ceil((noutput_items * d_omega) + d_interp->ntaps()); +} + +// FIXME add these back in an test difference in performance +float +digital_mpsk_receiver_cc::phase_error_detector_qpsk(gr_complex sample) const +{ + float phase_error = 0; + if(fabsf(sample.real()) > fabsf(sample.imag())) { + if(sample.real() > 0) + phase_error = -sample.imag(); + else + phase_error = sample.imag(); + } + else { + if(sample.imag() > 0) + phase_error = sample.real(); + else + phase_error = -sample.real(); + } + + return phase_error; +} + +float +digital_mpsk_receiver_cc::phase_error_detector_bpsk(gr_complex sample) const +{ + return -(sample.real()*sample.imag()); +} + +float digital_mpsk_receiver_cc::phase_error_detector_generic(gr_complex sample) const +{ + //return gr_fast_atan2f(sample*conj(d_constellation[d_current_const_point])); + return -arg(sample*conj(d_constellation[d_current_const_point])); +} + +unsigned int +digital_mpsk_receiver_cc::decision_bpsk(gr_complex sample) const +{ + return (gr_branchless_binary_slicer(sample.real()) ^ 1); + //return gr_binary_slicer(sample.real()) ^ 1; +} + +unsigned int +digital_mpsk_receiver_cc::decision_qpsk(gr_complex sample) const +{ + unsigned int index; + + //index = gr_branchless_quad_0deg_slicer(sample); + index = gr_quad_0deg_slicer(sample); + return index; +} + +unsigned int +digital_mpsk_receiver_cc::decision_generic(gr_complex sample) const +{ + unsigned int min_m = 0; + float min_s = 65535; + + // Develop all possible constellation points and find the one that minimizes + // the Euclidean distance (error) with the sample + for(unsigned int m=0; m < d_M; m++) { + gr_complex diff = norm(d_constellation[m] - sample); + + if(fabs(diff.real()) < min_s) { + min_s = fabs(diff.real()); + min_m = m; + } + } + // Return the index of the constellation point that minimizes the error + return min_m; +} + + +void +digital_mpsk_receiver_cc::make_constellation() +{ + for(unsigned int m=0; m < d_M; m++) { + d_constellation.push_back(gr_expj((M_TWOPI/d_M)*m)); + } +} + +void +digital_mpsk_receiver_cc::mm_sampler(const gr_complex symbol) +{ + gr_complex sample, nco; + + d_mu--; // skip a number of symbols between sampling + d_phase += d_freq; // increment the phase based on the frequency of the rotation + + // Keep phase clamped and not walk to infinity + while(d_phase > M_TWOPI) + d_phase -= M_TWOPI; + while(d_phase < -M_TWOPI) + d_phase += M_TWOPI; + + nco = gr_expj(d_phase+d_theta); // get the NCO value for derotating the current sample + sample = nco*symbol; // get the downconverted symbol + + // Fill up the delay line for the interpolator + d_dl[d_dl_idx] = sample; + d_dl[(d_dl_idx + DLLEN)] = sample; // put this in the second half of the buffer for overflows + d_dl_idx = (d_dl_idx+1) % DLLEN; // Keep the delay line index in bounds +} + +void +digital_mpsk_receiver_cc::mm_error_tracking(gr_complex sample) +{ + gr_complex u, x, y; + float mm_error = 0; + + // Make sample timing corrections + + // set the delayed samples + d_p_2T = d_p_1T; + d_p_1T = d_p_0T; + d_p_0T = sample; + d_c_2T = d_c_1T; + d_c_1T = d_c_0T; + + d_current_const_point = (*this.*d_decision)(d_p_0T); // make a decision on the sample value + d_c_0T = d_constellation[d_current_const_point]; + + x = (d_c_0T - d_c_2T) * conj(d_p_1T); + y = (d_p_0T - d_p_2T) * conj(d_c_1T); + u = y - x; + mm_error = u.real(); // the error signal is in the real part + mm_error = gr_branchless_clip(mm_error, 1.0); // limit mm_val + + d_omega = d_omega + d_gain_omega * mm_error; // update omega based on loop error + d_omega = d_omega_mid + gr_branchless_clip(d_omega-d_omega_mid, d_omega_rel); // make sure we don't walk away + + d_mu += d_omega + d_gain_mu * mm_error; // update mu based on loop error + +#if VERBOSE_MM + printf("mm: mu: %f omega: %f mm_error: %f sample: %f+j%f constellation: %f+j%f\n", + d_mu, d_omega, mm_error, sample.real(), sample.imag(), + d_constellation[d_current_const_point].real(), d_constellation[d_current_const_point].imag()); +#endif +} + + +void +digital_mpsk_receiver_cc::phase_error_tracking(gr_complex sample) +{ + float phase_error = 0; + + // Make phase and frequency corrections based on sampled value + phase_error = (*this.*d_phase_error_detector)(sample); + + 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", + phase_error, d_phase, d_freq, sample.real(), sample.imag(), + d_constellation[d_current_const_point].real(), d_constellation[d_current_const_point].imag()); +#endif +} + +int +digital_mpsk_receiver_cc::general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const gr_complex *in = (const gr_complex *) input_items[0]; + gr_complex *out = (gr_complex *) output_items[0]; + + int i=0, o=0; + + while((o < noutput_items) && (i < ninput_items[0])) { + while((d_mu > 1) && (i < ninput_items[0])) { + mm_sampler(in[i]); // puts symbols into a buffer and adjusts d_mu + i++; + } + + if(i < ninput_items[0]) { + gr_complex interp_sample = d_interp->interpolate(&d_dl[d_dl_idx], d_mu); + + mm_error_tracking(interp_sample); // corrects M&M sample time + phase_error_tracking(interp_sample); // corrects phase and frequency offsets + + out[o++] = interp_sample; + } + } + + #if 0 + printf("ninput_items: %d noutput_items: %d consuming: %d returning: %d\n", + ninput_items[0], noutput_items, i, o); + #endif + + consume_each(i); + return o; +} |