summaryrefslogtreecommitdiff
path: root/gr-digital/lib
diff options
context:
space:
mode:
Diffstat (limited to 'gr-digital/lib')
-rw-r--r--gr-digital/lib/.gitignore4
-rw-r--r--gr-digital/lib/CMakeLists.txt73
-rw-r--r--gr-digital/lib/Makefile.am56
-rw-r--r--gr-digital/lib/digital_binary_slicer_fb.cc59
-rw-r--r--gr-digital/lib/digital_clock_recovery_mm_cc.cc217
-rw-r--r--gr-digital/lib/digital_clock_recovery_mm_ff.cc139
-rw-r--r--gr-digital/lib/digital_cma_equalizer_cc.cc46
-rw-r--r--gr-digital/lib/digital_constellation.cc554
-rw-r--r--gr-digital/lib/digital_constellation_decoder_cb.cc77
-rw-r--r--gr-digital/lib/digital_constellation_receiver_cb.cc123
-rw-r--r--gr-digital/lib/digital_correlate_access_code_bb.cc134
-rw-r--r--gr-digital/lib/digital_costas_loop_cc.cc153
-rw-r--r--gr-digital/lib/digital_cpmmod_bc.cc69
-rw-r--r--gr-digital/lib/digital_crc32.cc130
-rw-r--r--gr-digital/lib/digital_fll_band_edge_cc.cc259
-rw-r--r--gr-digital/lib/digital_gmskmod_bc.cc44
-rw-r--r--gr-digital/lib/digital_kurtotic_equalizer_cc.cc51
-rw-r--r--gr-digital/lib/digital_lms_dd_equalizer_cc.cc85
-rw-r--r--gr-digital/lib/digital_mpsk_receiver_cc.cc316
-rw-r--r--gr-digital/lib/digital_ofdm_cyclic_prefixer.cc70
-rw-r--r--gr-digital/lib/digital_ofdm_frame_acquisition.cc210
-rw-r--r--gr-digital/lib/digital_ofdm_frame_sink.cc405
-rw-r--r--gr-digital/lib/digital_ofdm_insert_preamble.cc187
-rw-r--r--gr-digital/lib/digital_ofdm_mapper_bcv.cc241
-rw-r--r--gr-digital/lib/digital_ofdm_sampler.cc133
25 files changed, 3835 insertions, 0 deletions
diff --git a/gr-digital/lib/.gitignore b/gr-digital/lib/.gitignore
new file mode 100644
index 000000000..1b6114c39
--- /dev/null
+++ b/gr-digital/lib/.gitignore
@@ -0,0 +1,4 @@
+/.libs
+/.deps
+/Makefile
+/Makefile.in
diff --git a/gr-digital/lib/CMakeLists.txt b/gr-digital/lib/CMakeLists.txt
new file mode 100644
index 000000000..9417dc355
--- /dev/null
+++ b/gr-digital/lib/CMakeLists.txt
@@ -0,0 +1,73 @@
+# 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.
+
+########################################################################
+# Setup the include and linker paths
+########################################################################
+include_directories(
+ ${GNURADIO_CORE_INCLUDE_DIRS}
+ ${GR_DIGITAL_INCLUDE_DIRS}
+)
+
+include_directories(${Boost_INCLUDE_DIRS})
+link_directories(${Boost_LIBRARY_DIRS})
+
+########################################################################
+# Setup library
+########################################################################
+list(APPEND gr_digital_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_mpsk_receiver_cc.cc
+ digital_ofdm_cyclic_prefixer.cc
+ digital_ofdm_frame_acquisition.cc
+ digital_ofdm_frame_sink.cc
+ digital_ofdm_insert_preamble.cc
+ digital_ofdm_mapper_bcv.cc
+ digital_ofdm_sampler.cc
+ digital_gmskmod_bc.cc
+ digital_cpmmod_bc.cc
+)
+
+list(APPEND digital_libs
+ gnuradio-core
+ ${Boost_LIBRARIES}
+)
+
+add_library(gnuradio-digital SHARED ${gr_digital_sources})
+target_link_libraries(gnuradio-digital ${digital_libs})
+set_target_properties(gnuradio-digital PROPERTIES DEFINE_SYMBOL "gnuradio_digital_EXPORTS")
+set_target_properties(gnuradio-digital PROPERTIES SOVERSION ${LIBVER})
+
+install(TARGETS gnuradio-digital
+ LIBRARY DESTINATION ${GR_LIBRARY_DIR} COMPONENT "digital_runtime" # .so/.dylib file
+ ARCHIVE DESTINATION ${GR_LIBRARY_DIR} COMPONENT "digital_devel" # .lib file
+ RUNTIME DESTINATION ${GR_RUNTIME_DIR} COMPONENT "digital_runtime" # .dll file
+)
diff --git a/gr-digital/lib/Makefile.am b/gr-digital/lib/Makefile.am
new file mode 100644
index 000000000..2860974ca
--- /dev/null
+++ b/gr-digital/lib/Makefile.am
@@ -0,0 +1,56 @@
+#
+# 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) $(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_mpsk_receiver_cc.cc \
+ digital_ofdm_cyclic_prefixer.cc \
+ digital_ofdm_frame_acquisition.cc \
+ digital_ofdm_frame_sink.cc \
+ digital_ofdm_insert_preamble.cc \
+ digital_ofdm_mapper_bcv.cc \
+ digital_ofdm_sampler.cc \
+ digital_gmskmod_bc.cc \
+ digital_cpmmod_bc.cc
+
+libgnuradio_digital_la_LIBADD = \
+ $(GNURADIO_CORE_LA)
+
+libgnuradio_digital_la_LDFLAGS = $(NO_UNDEFINED) $(LTVERSIONFLAGS)
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.cc b/gr-digital/lib/digital_cma_equalizer_cc.cc
new file mode 100644
index 000000000..c6c46c2d8
--- /dev/null
+++ b/gr-digital/lib/digital_cma_equalizer_cc.cc
@@ -0,0 +1,46 @@
+/* -*- 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_cma_equalizer_cc.h>
+#include <cstdio>
+
+digital_cma_equalizer_cc_sptr
+digital_make_cma_equalizer_cc(int num_taps, float modulus, float mu, int sps)
+{
+ return gnuradio::get_initial_sptr(new digital_cma_equalizer_cc(num_taps, modulus,
+ mu, sps));
+}
+
+digital_cma_equalizer_cc::digital_cma_equalizer_cc(int num_taps, float modulus,
+ float mu, int sps)
+ : gr_adaptive_fir_ccc("cma_equalizer_cc", sps,
+ std::vector<gr_complex>(num_taps, gr_complex(0,0)))
+{
+ set_modulus(modulus);
+ set_gain(mu);
+ if (num_taps > 0)
+ d_taps[0] = 1.0;
+}
diff --git a/gr-digital/lib/digital_constellation.cc b/gr-digital/lib/digital_constellation.cc
new file mode 100644
index 000000000..0c100f38e
--- /dev/null
+++ b/gr-digital/lib/digital_constellation.cc
@@ -0,0 +1,554 @@
+/* -*- 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gr_io_signature.h>
+#include <digital_constellation.h>
+#include <digital_metric_type.h>
+#include <gr_math.h>
+#include <gr_complex.h>
+#include <math.h>
+#include <iostream>
+#include <stdlib.h>
+#include <float.h>
+#include <stdexcept>
+
+#define M_TWOPI (2*M_PI)
+#define SQRT_TWO 0.707107
+
+// Base Constellation Class
+
+digital_constellation::digital_constellation (std::vector<gr_complex> constellation,
+ std::vector<unsigned int> pre_diff_code,
+ unsigned int rotational_symmetry,
+ unsigned int dimensionality) :
+ d_constellation(constellation),
+ d_pre_diff_code(pre_diff_code),
+ d_rotational_symmetry(rotational_symmetry),
+ d_dimensionality(dimensionality)
+{
+ if (pre_diff_code.size() == 0)
+ d_apply_pre_diff_code = false;
+ else if (pre_diff_code.size() != constellation.size())
+ throw std::runtime_error ("The constellation and pre-diff code must be of the same length.");
+ else
+ d_apply_pre_diff_code = true;
+ calc_arity();
+}
+
+digital_constellation::digital_constellation () :
+ d_apply_pre_diff_code(false),
+ d_rotational_symmetry(0),
+ d_dimensionality(1)
+{
+ calc_arity();
+}
+
+//! Returns the constellation points for a symbol value
+void
+digital_constellation::map_to_points(unsigned int value, gr_complex *points)
+{
+ for (unsigned int i=0; i<d_dimensionality; i++)
+ points[i] = d_constellation[value*d_dimensionality + i];
+}
+
+std::vector<gr_complex>
+digital_constellation::map_to_points_v(unsigned int value)
+{
+ std::vector<gr_complex> points_v;
+ points_v.resize(d_dimensionality);
+ map_to_points(value, &(points_v[0]));
+ return points_v;
+}
+
+float
+digital_constellation::get_distance(unsigned int index, const gr_complex *sample)
+{
+ float dist = 0;
+ for (unsigned int i=0; i<d_dimensionality; i++) {
+ dist += norm(sample[i] - d_constellation[index*d_dimensionality + i]);
+ }
+ return dist;
+}
+
+unsigned int
+digital_constellation::get_closest_point(const gr_complex *sample)
+{
+ unsigned int min_index = 0;
+ float min_euclid_dist;
+ float euclid_dist;
+
+ min_euclid_dist = get_distance(0, sample);
+ min_index = 0;
+ for (unsigned int j = 1; j < d_arity; j++){
+ euclid_dist = get_distance(j, sample);
+ if (euclid_dist < min_euclid_dist){
+ min_euclid_dist = euclid_dist;
+ min_index = j;
+ }
+ }
+ return min_index;
+}
+
+unsigned int
+digital_constellation::decision_maker_pe(const gr_complex *sample, float *phase_error)
+{
+ unsigned int index = decision_maker(sample);
+ *phase_error = 0;
+ for (unsigned int d=0; d<d_dimensionality; d++)
+ *phase_error += -arg(sample[d]*conj(d_constellation[index+d]));
+ return index;
+}
+
+/*
+unsigned int digital_constellation::decision_maker_e(const gr_complex *sample, float *error)
+{
+ unsigned int index = decision_maker(sample);
+ *error = 0;
+ for (unsigned int d=0; d<d_dimensionality; d++)
+ *error += sample[d]*conj(d_constellation[index+d]);
+ return index;
+}
+*/
+
+std::vector<gr_complex> digital_constellation::s_points () {
+ if (d_dimensionality != 1)
+ throw std::runtime_error ("s_points only works for dimensionality 1 constellations.");
+ else
+ return d_constellation;
+}
+
+std::vector<std::vector<gr_complex> >
+digital_constellation::v_points ()
+{
+ std::vector<std::vector<gr_complex> > vv_const;
+ vv_const.resize(d_arity);
+ for (unsigned int p=0; p<d_arity; p++) {
+ std::vector<gr_complex> v_const;
+ v_const.resize(d_dimensionality);
+ for (unsigned int d=0; d<d_dimensionality; d++) {
+ v_const[d] = d_constellation[p*d_dimensionality+d];
+ }
+ vv_const[p] = v_const;
+ }
+ return vv_const;
+}
+
+void
+digital_constellation::calc_metric(const gr_complex *sample, float *metric,
+ trellis_metric_type_t type)
+{
+ switch (type){
+ case TRELLIS_EUCLIDEAN:
+ calc_euclidean_metric(sample, metric);
+ break;
+ case TRELLIS_HARD_SYMBOL:
+ calc_hard_symbol_metric(sample, metric);
+ break;
+ case TRELLIS_HARD_BIT:
+ throw std::runtime_error ("Invalid metric type (not yet implemented).");
+ break;
+ default:
+ throw std::runtime_error ("Invalid metric type.");
+ }
+}
+
+void
+digital_constellation::calc_euclidean_metric(const gr_complex *sample, float *metric)
+{
+ for (unsigned int o=0; o<d_arity; o++) {
+ metric[o] = get_distance(o, sample);
+ }
+}
+
+void
+digital_constellation::calc_hard_symbol_metric(const gr_complex *sample, float *metric)
+{
+ float minm = FLT_MAX;
+ unsigned int minmi = 0;
+ for (unsigned int o=0; o<d_arity; o++) {
+ float dist = get_distance(o, sample);
+ if (dist < minm) {
+ minm = dist;
+ minmi = o;
+ }
+ }
+ for(unsigned int o=0; o<d_arity; o++) {
+ metric[o] = (o==minmi?0.0:1.0);
+ }
+}
+
+void
+digital_constellation::calc_arity ()
+{
+ if (d_constellation.size() % d_dimensionality != 0)
+ throw std::runtime_error ("Constellation vector size must be a multiple of the dimensionality.");
+ d_arity = d_constellation.size()/d_dimensionality;
+}
+
+unsigned int
+digital_constellation::decision_maker_v (std::vector<gr_complex> sample)
+{
+ assert(sample.size() == d_dimensionality);
+ return decision_maker (&(sample[0]));
+}
+
+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)
+{
+ return digital_constellation_calcdist_sptr(new digital_constellation_calcdist
+ (constellation, pre_diff_code,
+ rotational_symmetry, dimensionality));
+}
+
+digital_constellation_calcdist::digital_constellation_calcdist(std::vector<gr_complex> constellation,
+ std::vector<unsigned int> pre_diff_code,
+ unsigned int rotational_symmetry,
+ unsigned int dimensionality) :
+ digital_constellation(constellation, pre_diff_code, rotational_symmetry, dimensionality)
+{}
+
+// Chooses points base on shortest distance.
+// Inefficient.
+unsigned int
+digital_constellation_calcdist::decision_maker(const gr_complex *sample)
+{
+ return get_closest_point(sample);
+}
+
+digital_constellation_sector::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) :
+ digital_constellation(constellation, pre_diff_code, rotational_symmetry, dimensionality),
+ n_sectors(n_sectors)
+{
+}
+
+unsigned int
+digital_constellation_sector::decision_maker (const gr_complex *sample)
+{
+ unsigned int sector;
+ sector = get_sector(sample);
+ return sector_values[sector];
+}
+
+void
+digital_constellation_sector::find_sector_values ()
+{
+ unsigned int i;
+ sector_values.clear();
+ for (i=0; i<n_sectors; i++) {
+ sector_values.push_back(calc_sector_value(i));
+ }
+}
+
+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)
+{
+ return digital_constellation_rect_sptr(new digital_constellation_rect
+ (constellation, pre_diff_code,
+ rotational_symmetry,
+ real_sectors, imag_sectors,
+ width_real_sectors,
+ width_imag_sectors));
+ }
+
+digital_constellation_rect::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_sector(constellation, pre_diff_code, rotational_symmetry, 1, real_sectors * imag_sectors),
+ n_real_sectors(real_sectors), n_imag_sectors(imag_sectors),
+ d_width_real_sectors(width_real_sectors), d_width_imag_sectors(width_imag_sectors)
+{
+ find_sector_values();
+}
+
+unsigned int
+digital_constellation_rect::get_sector (const gr_complex *sample)
+{
+ int real_sector, imag_sector;
+ unsigned int sector;
+
+ real_sector = int(real(*sample)/d_width_real_sectors + n_real_sectors/2.0);
+ if(real_sector < 0)
+ real_sector = 0;
+ if(real_sector >= (int)n_real_sectors)
+ real_sector = n_real_sectors-1;
+
+ imag_sector = int(imag(*sample)/d_width_imag_sectors + n_imag_sectors/2.0);
+ if(imag_sector < 0)
+ imag_sector = 0;
+ if(imag_sector >= (int)n_imag_sectors)
+ imag_sector = n_imag_sectors-1;
+
+ sector = real_sector * n_imag_sectors + imag_sector;
+ return sector;
+}
+
+unsigned int
+digital_constellation_rect::calc_sector_value (unsigned int sector)
+{
+ unsigned int real_sector, imag_sector;
+ gr_complex sector_center;
+ unsigned int closest_point;
+ real_sector = float(sector)/n_imag_sectors;
+ imag_sector = sector - real_sector * n_imag_sectors;
+ sector_center = gr_complex((real_sector + 0.5 - n_real_sectors/2.0) * d_width_real_sectors,
+ (imag_sector + 0.5 - n_imag_sectors/2.0) * d_width_imag_sectors);
+ closest_point = get_closest_point(&sector_center);
+ return closest_point;
+}
+
+
+digital_constellation_psk_sptr
+digital_make_constellation_psk(std::vector<gr_complex> constellation,
+ std::vector<unsigned int> pre_diff_code,
+ unsigned int n_sectors)
+{
+ return digital_constellation_psk_sptr(new digital_constellation_psk
+ (constellation, pre_diff_code,
+ n_sectors));
+}
+
+digital_constellation_psk::digital_constellation_psk (std::vector<gr_complex> constellation,
+ std::vector<unsigned int> pre_diff_code,
+ unsigned int n_sectors) :
+ digital_constellation_sector(constellation, pre_diff_code, constellation.size(), 1, n_sectors)
+{
+ find_sector_values();
+}
+
+unsigned int
+digital_constellation_psk::get_sector (const gr_complex *sample)
+{
+ float phase = arg(*sample);
+ float width = M_TWOPI / n_sectors;
+ int sector = floor(phase/width + 0.5);
+ unsigned int u_sector;
+ if (sector < 0)
+ sector += n_sectors;
+ u_sector = sector;
+ return sector;
+}
+
+unsigned int
+digital_constellation_psk::calc_sector_value (unsigned int sector)
+{
+ float phase = sector * M_TWOPI / n_sectors;
+ gr_complex sector_center = gr_complex(cos(phase), sin(phase));
+ unsigned int closest_point = get_closest_point(&sector_center);
+ return closest_point;
+}
+
+
+digital_constellation_bpsk_sptr
+digital_make_constellation_bpsk()
+{
+ return digital_constellation_bpsk_sptr(new digital_constellation_bpsk ());
+}
+
+digital_constellation_bpsk::digital_constellation_bpsk ()
+{
+ d_constellation.resize(2);
+ d_constellation[0] = gr_complex(-1, 0);
+ d_constellation[1] = gr_complex(1, 0);
+ d_rotational_symmetry = 2;
+ d_dimensionality = 1;
+ calc_arity();
+}
+
+unsigned int
+digital_constellation_bpsk::decision_maker(const gr_complex *sample)
+{
+ return (real(*sample) > 0);
+}
+
+
+digital_constellation_qpsk_sptr
+digital_make_constellation_qpsk()
+{
+ return digital_constellation_qpsk_sptr(new digital_constellation_qpsk ());
+}
+
+digital_constellation_qpsk::digital_constellation_qpsk ()
+{
+ d_constellation.resize(4);
+ // Gray-coded
+ 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_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();
+}
+
+unsigned int
+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()
+{
+ return digital_constellation_8psk_sptr(new digital_constellation_8psk ());
+}
+
+digital_constellation_8psk::digital_constellation_8psk ()
+{
+ float angle = M_PI/8.0;
+ d_constellation.resize(8);
+ // Gray-coded
+ d_constellation[0] = gr_complex(cos( 1*angle), sin( 1*angle));
+ d_constellation[1] = gr_complex(cos( 7*angle), sin( 7*angle));
+ d_constellation[2] = gr_complex(cos(15*angle), sin(15*angle));
+ d_constellation[3] = gr_complex(cos( 9*angle), sin( 9*angle));
+ d_constellation[4] = gr_complex(cos( 3*angle), sin( 3*angle));
+ d_constellation[5] = gr_complex(cos( 5*angle), sin( 5*angle));
+ d_constellation[6] = gr_complex(cos(13*angle), sin(13*angle));
+ d_constellation[7] = gr_complex(cos(11*angle), sin(11*angle));
+ d_rotational_symmetry = 8;
+ d_dimensionality = 1;
+ calc_arity();
+}
+
+unsigned int
+digital_constellation_8psk::decision_maker(const gr_complex *sample)
+{
+ unsigned int ret = 0;
+
+ float re = sample->real();
+ float im = sample->imag();
+
+ if(fabsf(re) <= fabsf(im))
+ ret = 4;
+ if(re <= 0)
+ ret |= 1;
+ if(im <= 0)
+ ret |= 2;
+
+ return ret;
+}
diff --git a/gr-digital/lib/digital_constellation_decoder_cb.cc b/gr-digital/lib/digital_constellation_decoder_cb.cc
new file mode 100644
index 000000000..4638790f6
--- /dev/null
+++ b/gr-digital/lib/digital_constellation_decoder_cb.cc
@@ -0,0 +1,77 @@
+/* -*- 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_constellation_decoder_cb.h>
+#include <digital_constellation.h>
+#include <gr_io_signature.h>
+#include <iostream>
+
+digital_constellation_decoder_cb_sptr
+digital_make_constellation_decoder_cb (digital_constellation_sptr constellation)
+{
+ return gnuradio::get_initial_sptr
+ (new digital_constellation_decoder_cb(constellation));
+}
+
+digital_constellation_decoder_cb::
+digital_constellation_decoder_cb (digital_constellation_sptr constellation)
+ : gr_block ("constellation_decoder_cb",
+ gr_make_io_signature (1, 1, sizeof (gr_complex)),
+ gr_make_io_signature (1, 1, sizeof (unsigned char))),
+ d_constellation(constellation),
+ d_dim(constellation->dimensionality())
+{
+ set_relative_rate (1.0 / ((double) d_dim));
+}
+
+void
+digital_constellation_decoder_cb::forecast (int noutput_items,
+ gr_vector_int &ninput_items_required)
+{
+ unsigned int input_required = noutput_items * d_dim;
+
+ unsigned ninputs = ninput_items_required.size();
+ for (unsigned int i = 0; i < ninputs; i++)
+ ninput_items_required[i] = input_required;
+}
+
+
+int
+digital_constellation_decoder_cb::general_work (int noutput_items,
+ gr_vector_int &ninput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+{
+ gr_complex const *in = (const gr_complex *) input_items[0];
+ unsigned char *out = (unsigned char *) output_items[0];
+
+ for(int i = 0; i < noutput_items; i++){
+ out[i] = d_constellation->decision_maker(&(in[i*d_dim]));
+ }
+
+ consume_each (noutput_items * d_dim);
+ return noutput_items;
+}
diff --git a/gr-digital/lib/digital_constellation_receiver_cb.cc b/gr-digital/lib/digital_constellation_receiver_cb.cc
new file mode 100644
index 000000000..b9239962a
--- /dev/null
+++ b/gr-digital/lib/digital_constellation_receiver_cb.cc
@@ -0,0 +1,123 @@
+/* -*- 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 <gr_io_signature.h>
+#include <gr_prefs.h>
+#include <digital_constellation_receiver_cb.h>
+#include <stdexcept>
+#include <gr_math.h>
+#include <gr_expj.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_constellation_receiver_cb_sptr
+digital_make_constellation_receiver_cb(digital_constellation_sptr constell,
+ float loop_bw, float fmin, float fmax)
+{
+ return gnuradio::get_initial_sptr(new digital_constellation_receiver_cb (constell,
+ 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 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)),
+ 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.");
+}
+
+void
+digital_constellation_receiver_cb::phase_error_tracking(float phase_error)
+{
+ 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->points()[d_current_const_point].real(),
+ d_constellation->points()[d_current_const_point].imag());
+#endif
+}
+
+int
+digital_constellation_receiver_cb::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];
+ unsigned char *out = (unsigned char *) output_items[0];
+
+ int i=0;
+
+ float phase_error;
+ unsigned int sym_value;
+ gr_complex sample, nco;
+
+ float *out_err = 0, *out_phase = 0, *out_freq = 0;
+ if(output_items.size() == 4) {
+ out_err = (float *) output_items[1];
+ out_phase = (float *) output_items[2];
+ out_freq = (float *) output_items[3];
+ }
+
+ while((i < noutput_items) && (i < ninput_items[0])) {
+ 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_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;
+ out_freq[i] = d_freq;
+ }
+ i++;
+ }
+
+ consume_each(i);
+ return i;
+}
+
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
new file mode 100644
index 000000000..370dc7e5c
--- /dev/null
+++ b/gr-digital/lib/digital_costas_loop_cc.cc
@@ -0,0 +1,153 @@
+/* -*- 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_costas_loop_cc.h>
+#include <gr_io_signature.h>
+#include <gr_expj.h>
+#include <gr_sincos.h>
+#include <gr_math.h>
+
+digital_costas_loop_cc_sptr
+digital_make_costas_loop_cc (float loop_bw, int order
+ ) throw (std::invalid_argument)
+{
+ return gnuradio::get_initial_sptr(new digital_costas_loop_cc
+ (loop_bw, 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))),
+ gri_control_loop(loop_bw, 1.0, -1.0),
+ d_order(order), d_phase_detector(NULL)
+{
+ // 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;
+ break;
+
+ case 4:
+ d_phase_detector = &digital_costas_loop_cc::phase_detector_4;
+ break;
+
+ case 8:
+ d_phase_detector = &digital_costas_loop_cc::phase_detector_8;
+ break;
+
+ default:
+ throw std::invalid_argument("order must be 2, 4, or 8");
+ break;
+ }
+}
+
+float
+digital_costas_loop_cc::phase_detector_8(gr_complex sample) const
+{
+ /* This technique splits the 8PSK constellation into 2 squashed
+ QPSK constellations, one when I is larger than Q and one where
+ Q is larger than I. The error is then calculated proportionally
+ to these squashed constellations by the const K = sqrt(2)-1.
+
+ The signal magnitude must be > 1 or K will incorrectly bias
+ the error value.
+
+ Ref: Z. Huang, Z. Yi, M. Zhang, K. Wang, "8PSK demodulation for
+ new generation DVB-S2", IEEE Proc. Int. Conf. Communications,
+ Circuits and Systems, Vol. 2, pp. 1447 - 1450, 2004.
+ */
+
+ float K = (sqrt(2.0) - 1);
+ if(fabsf(sample.real()) >= fabsf(sample.imag())) {
+ return ((sample.real()>0 ? 1.0 : -1.0) * sample.imag() -
+ (sample.imag()>0 ? 1.0 : -1.0) * sample.real() * K);
+ }
+ else {
+ return ((sample.real()>0 ? 1.0 : -1.0) * sample.imag() * K -
+ (sample.imag()>0 ? 1.0 : -1.0) * sample.real());
+ }
+}
+
+float
+digital_costas_loop_cc::phase_detector_4(gr_complex sample) const
+{
+
+ return ((sample.real()>0 ? 1.0 : -1.0) * sample.imag() -
+ (sample.imag()>0 ? 1.0 : -1.0) * sample.real());
+}
+
+float
+digital_costas_loop_cc::phase_detector_2(gr_complex sample) const
+{
+ return (sample.real()*sample.imag());
+}
+
+int
+digital_costas_loop_cc::work (int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+{
+ const gr_complex *iptr = (gr_complex *) input_items[0];
+ gr_complex *optr = (gr_complex *) output_items[0];
+ float *foptr = (float *) output_items[1];
+
+ bool write_foptr = output_items.size() >= 2;
+
+ float error;
+ gr_complex nco_out;
+
+ if (write_foptr) {
+
+ for (int i = 0; i < noutput_items; i++){
+ nco_out = gr_expj(-d_phase);
+ optr[i] = iptr[i] * nco_out;
+
+ error = (*this.*d_phase_detector)(optr[i]);
+ error = gr_branchless_clip(error, 1.0);
+
+ advance_loop(error);
+ phase_wrap();
+ frequency_limit();
+
+ foptr[i] = d_freq;
+ }
+ } else {
+ for (int i = 0; i < noutput_items; i++){
+ nco_out = gr_expj(-d_phase);
+ optr[i] = iptr[i] * nco_out;
+
+ error = (*this.*d_phase_detector)(optr[i]);
+ error = gr_branchless_clip(error, 1.0);
+
+ advance_loop(error);
+ phase_wrap();
+ frequency_limit();
+ }
+ }
+ return noutput_items;
+}
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_gmskmod_bc.cc b/gr-digital/lib/digital_gmskmod_bc.cc
new file mode 100644
index 000000000..e53e90037
--- /dev/null
+++ b/gr-digital/lib/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/lib/digital_kurtotic_equalizer_cc.cc b/gr-digital/lib/digital_kurtotic_equalizer_cc.cc
new file mode 100644
index 000000000..c95b56021
--- /dev/null
+++ b/gr-digital/lib/digital_kurtotic_equalizer_cc.cc
@@ -0,0 +1,51 @@
+/* -*- 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_kurtotic_equalizer_cc.h>
+
+digital_kurtotic_equalizer_cc_sptr
+digital_make_kurtotic_equalizer_cc(int num_taps, float mu)
+{
+ return gnuradio::get_initial_sptr(new digital_kurtotic_equalizer_cc(num_taps, mu));
+}
+
+digital_kurtotic_equalizer_cc::digital_kurtotic_equalizer_cc(int num_taps, float mu)
+ : gr_adaptive_fir_ccc("kurtotic_equalizer_cc", 1, std::vector<gr_complex>(num_taps))
+{
+ set_gain(mu);
+ if (num_taps > 0)
+ d_taps[0] = 1.0;
+
+ d_alpha_p = 0.01;
+ d_alpha_q = 0.01;
+ d_alpha_m = 0.01;
+
+ d_p = 0.0f;
+ d_m = 0.0f;
+ d_q = gr_complex(0,0);
+ d_u = gr_complex(0,0);
+}
+
diff --git a/gr-digital/lib/digital_lms_dd_equalizer_cc.cc b/gr-digital/lib/digital_lms_dd_equalizer_cc.cc
new file mode 100644
index 000000000..e2c2f16f2
--- /dev/null
+++ b/gr-digital/lib/digital_lms_dd_equalizer_cc.cc
@@ -0,0 +1,85 @@
+/* -*- 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_lms_dd_equalizer_cc.h>
+#include <gr_io_signature.h>
+#include <gr_misc.h>
+#include <iostream>
+
+digital_lms_dd_equalizer_cc_sptr
+digital_make_lms_dd_equalizer_cc(int num_taps, float mu, int sps,
+ digital_constellation_sptr cnst)
+{
+ return gnuradio::get_initial_sptr(new digital_lms_dd_equalizer_cc(num_taps, mu,
+ sps, cnst));
+}
+
+digital_lms_dd_equalizer_cc::digital_lms_dd_equalizer_cc(int num_taps, float mu,
+ int sps,
+ digital_constellation_sptr cnst)
+ : gr_adaptive_fir_ccc("lms_dd_equalizer_cc", sps,
+ std::vector<gr_complex>(num_taps, gr_complex(0,0))),
+ d_taps(num_taps), d_cnst(cnst)
+{
+ set_gain(mu);
+ if (num_taps > 0)
+ d_taps[num_taps/2] = 1.0;
+}
+
+
+
+
+/*
+int
+digital_lms_dd_equalizer_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];
+
+ gr_complex acc, decision, error;
+
+ for(int i = 0; i < noutput_items; i++) {
+ acc = 0;
+
+ // Compute output
+ for (size_t j=0; j < d_taps.size(); j++)
+ acc += in[i+j] * conj(d_taps[j]);
+
+ d_cnst->map_to_points(d_cnst->decision_maker(&acc), &decision);
+ error = decision - acc;
+
+ // Update taps
+ for (size_t j=0; j < d_taps.size(); j++)
+ d_taps[j] += d_mu * conj(error) * in[i+j];
+
+ out[i] = acc;
+ }
+
+ return noutput_items;
+}
+*/
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;
+}
diff --git a/gr-digital/lib/digital_ofdm_cyclic_prefixer.cc b/gr-digital/lib/digital_ofdm_cyclic_prefixer.cc
new file mode 100644
index 000000000..192af2591
--- /dev/null
+++ b/gr-digital/lib/digital_ofdm_cyclic_prefixer.cc
@@ -0,0 +1,70 @@
+/* -*- 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_ofdm_cyclic_prefixer.h>
+#include <gr_io_signature.h>
+
+digital_ofdm_cyclic_prefixer_sptr
+digital_make_ofdm_cyclic_prefixer (size_t input_size, size_t output_size)
+{
+ return gnuradio::get_initial_sptr(new digital_ofdm_cyclic_prefixer (input_size,
+ output_size));
+}
+
+digital_ofdm_cyclic_prefixer::digital_ofdm_cyclic_prefixer (size_t input_size,
+ size_t output_size)
+ : gr_sync_interpolator ("ofdm_cyclic_prefixer",
+ gr_make_io_signature (1, 1, input_size*sizeof(gr_complex)),
+ gr_make_io_signature (1, 1, sizeof(gr_complex)),
+ output_size),
+ d_input_size(input_size),
+ d_output_size(output_size)
+
+{
+}
+
+int
+digital_ofdm_cyclic_prefixer::work (int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+{
+ gr_complex *in = (gr_complex *) input_items[0];
+ gr_complex *out = (gr_complex *) output_items[0];
+ size_t cp_size = d_output_size - d_input_size;
+ unsigned int i=0, j=0;
+
+ j = cp_size;
+ for(i=0; i < d_input_size; i++,j++) {
+ out[j] = in[i];
+ }
+
+ j = d_input_size - cp_size;
+ for(i=0; i < cp_size; i++, j++) {
+ out[i] = in[j];
+ }
+
+ return d_output_size;
+}
diff --git a/gr-digital/lib/digital_ofdm_frame_acquisition.cc b/gr-digital/lib/digital_ofdm_frame_acquisition.cc
new file mode 100644
index 000000000..93b58aeca
--- /dev/null
+++ b/gr-digital/lib/digital_ofdm_frame_acquisition.cc
@@ -0,0 +1,210 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2006-2008,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_ofdm_frame_acquisition.h>
+#include <gr_io_signature.h>
+#include <gr_expj.h>
+#include <gr_math.h>
+#include <cstdio>
+
+#define VERBOSE 0
+#define M_TWOPI (2*M_PI)
+#define MAX_NUM_SYMBOLS 1000
+
+digital_ofdm_frame_acquisition_sptr
+digital_make_ofdm_frame_acquisition (unsigned int occupied_carriers,
+ unsigned int fft_length,
+ unsigned int cplen,
+ const std::vector<gr_complex> &known_symbol,
+ unsigned int max_fft_shift_len)
+{
+ return gnuradio::get_initial_sptr(new digital_ofdm_frame_acquisition (occupied_carriers, fft_length, cplen,
+ known_symbol, max_fft_shift_len));
+}
+
+digital_ofdm_frame_acquisition::digital_ofdm_frame_acquisition (unsigned occupied_carriers,
+ unsigned int fft_length,
+ unsigned int cplen,
+ const std::vector<gr_complex> &known_symbol,
+ unsigned int max_fft_shift_len)
+ : gr_block ("ofdm_frame_acquisition",
+ gr_make_io_signature2 (2, 2, sizeof(gr_complex)*fft_length, sizeof(char)*fft_length),
+ gr_make_io_signature2 (2, 2, sizeof(gr_complex)*occupied_carriers, sizeof(char))),
+ d_occupied_carriers(occupied_carriers),
+ d_fft_length(fft_length),
+ d_cplen(cplen),
+ d_freq_shift_len(max_fft_shift_len),
+ d_known_symbol(known_symbol),
+ d_coarse_freq(0),
+ d_phase_count(0)
+{
+ d_symbol_phase_diff.resize(d_fft_length);
+ d_known_phase_diff.resize(d_occupied_carriers);
+ d_hestimate.resize(d_occupied_carriers);
+
+ unsigned int i = 0, j = 0;
+
+ std::fill(d_known_phase_diff.begin(), d_known_phase_diff.end(), 0);
+ for(i = 0; i < d_known_symbol.size()-2; i+=2) {
+ d_known_phase_diff[i] = norm(d_known_symbol[i] - d_known_symbol[i+2]);
+ }
+
+ d_phase_lut = new gr_complex[(2*d_freq_shift_len+1) * MAX_NUM_SYMBOLS];
+ for(i = 0; i <= 2*d_freq_shift_len; i++) {
+ for(j = 0; j < MAX_NUM_SYMBOLS; j++) {
+ d_phase_lut[j + i*MAX_NUM_SYMBOLS] = gr_expj(-M_TWOPI*d_cplen/d_fft_length*(i-d_freq_shift_len)*j);
+ }
+ }
+}
+
+digital_ofdm_frame_acquisition::~digital_ofdm_frame_acquisition(void)
+{
+ delete [] d_phase_lut;
+}
+
+void
+digital_ofdm_frame_acquisition::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] = 1;
+}
+
+gr_complex
+digital_ofdm_frame_acquisition::coarse_freq_comp(int freq_delta, int symbol_count)
+{
+ // return gr_complex(cos(-M_TWOPI*freq_delta*d_cplen/d_fft_length*symbol_count),
+ // sin(-M_TWOPI*freq_delta*d_cplen/d_fft_length*symbol_count));
+
+ return gr_expj(-M_TWOPI*freq_delta*d_cplen/d_fft_length*symbol_count);
+
+ //return d_phase_lut[MAX_NUM_SYMBOLS * (d_freq_shift_len + freq_delta) + symbol_count];
+}
+
+void
+digital_ofdm_frame_acquisition::correlate(const gr_complex *symbol, int zeros_on_left)
+{
+ unsigned int i,j;
+
+ std::fill(d_symbol_phase_diff.begin(), d_symbol_phase_diff.end(), 0);
+ for(i = 0; i < d_fft_length-2; i++) {
+ d_symbol_phase_diff[i] = norm(symbol[i] - symbol[i+2]);
+ }
+
+ // sweep through all possible/allowed frequency offsets and select the best
+ int index = 0;
+ float max = 0, sum=0;
+ for(i = zeros_on_left - d_freq_shift_len; i < zeros_on_left + d_freq_shift_len; i++) {
+ sum = 0;
+ for(j = 0; j < d_occupied_carriers; j++) {
+ sum += (d_known_phase_diff[j] * d_symbol_phase_diff[i+j]);
+ }
+ if(sum > max) {
+ max = sum;
+ index = i;
+ }
+ }
+
+ // set the coarse frequency offset relative to the edge of the occupied tones
+ d_coarse_freq = index - zeros_on_left;
+}
+
+void
+digital_ofdm_frame_acquisition::calculate_equalizer(const gr_complex *symbol, int zeros_on_left)
+{
+ unsigned int i=0;
+
+ // Set first tap of equalizer
+ d_hestimate[0] = d_known_symbol[0] /
+ (coarse_freq_comp(d_coarse_freq,1)*symbol[zeros_on_left+d_coarse_freq]);
+
+ // set every even tap based on known symbol
+ // linearly interpolate between set carriers to set zero-filled carriers
+ // FIXME: is this the best way to set this?
+ for(i = 2; i < d_occupied_carriers; i+=2) {
+ d_hestimate[i] = d_known_symbol[i] /
+ (coarse_freq_comp(d_coarse_freq,1)*(symbol[i+zeros_on_left+d_coarse_freq]));
+ d_hestimate[i-1] = (d_hestimate[i] + d_hestimate[i-2]) / gr_complex(2.0, 0.0);
+ }
+
+ // with even number of carriers; last equalizer tap is wrong
+ if(!(d_occupied_carriers & 1)) {
+ d_hestimate[d_occupied_carriers-1] = d_hestimate[d_occupied_carriers-2];
+ }
+
+ if(VERBOSE) {
+ fprintf(stderr, "Equalizer setting:\n");
+ for(i = 0; i < d_occupied_carriers; i++) {
+ gr_complex sym = coarse_freq_comp(d_coarse_freq,1)*symbol[i+zeros_on_left+d_coarse_freq];
+ gr_complex output = sym * d_hestimate[i];
+ fprintf(stderr, "sym: %+.4f + j%+.4f ks: %+.4f + j%+.4f eq: %+.4f + j%+.4f ==> %+.4f + j%+.4f\n",
+ sym .real(), sym.imag(),
+ d_known_symbol[i].real(), d_known_symbol[i].imag(),
+ d_hestimate[i].real(), d_hestimate[i].imag(),
+ output.real(), output.imag());
+ }
+ fprintf(stderr, "\n");
+ }
+}
+
+int
+digital_ofdm_frame_acquisition::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 *symbol = (const gr_complex *)input_items[0];
+ const char *signal_in = (const char *)input_items[1];
+
+ gr_complex *out = (gr_complex *) output_items[0];
+ char *signal_out = (char *) output_items[1];
+
+ int unoccupied_carriers = d_fft_length - d_occupied_carriers;
+ int zeros_on_left = (int)ceil(unoccupied_carriers/2.0);
+
+ if(signal_in[0]) {
+ d_phase_count = 1;
+ correlate(symbol, zeros_on_left);
+ calculate_equalizer(symbol, zeros_on_left);
+ signal_out[0] = 1;
+ }
+ else {
+ signal_out[0] = 0;
+ }
+
+ for(unsigned int i = 0; i < d_occupied_carriers; i++) {
+ out[i] = d_hestimate[i]*coarse_freq_comp(d_coarse_freq,d_phase_count)
+ *symbol[i+zeros_on_left+d_coarse_freq];
+ }
+
+ d_phase_count++;
+ if(d_phase_count == MAX_NUM_SYMBOLS) {
+ d_phase_count = 1;
+ }
+
+ consume_each(1);
+ return 1;
+}
diff --git a/gr-digital/lib/digital_ofdm_frame_sink.cc b/gr-digital/lib/digital_ofdm_frame_sink.cc
new file mode 100644
index 000000000..f8fb1bbb1
--- /dev/null
+++ b/gr-digital/lib/digital_ofdm_frame_sink.cc
@@ -0,0 +1,405 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2007,2008,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_ofdm_frame_sink.h>
+#include <gr_io_signature.h>
+#include <gr_expj.h>
+#include <gr_math.h>
+#include <math.h>
+#include <cstdio>
+#include <stdexcept>
+#include <iostream>
+#include <string.h>
+
+#define VERBOSE 0
+
+inline void
+digital_ofdm_frame_sink::enter_search()
+{
+ if (VERBOSE)
+ fprintf(stderr, "@ enter_search\n");
+
+ d_state = STATE_SYNC_SEARCH;
+
+}
+
+inline void
+digital_ofdm_frame_sink::enter_have_sync()
+{
+ if (VERBOSE)
+ fprintf(stderr, "@ enter_have_sync\n");
+
+ d_state = STATE_HAVE_SYNC;
+
+ // clear state of demapper
+ d_byte_offset = 0;
+ d_partial_byte = 0;
+
+ d_header = 0;
+ d_headerbytelen_cnt = 0;
+
+ // Resetting PLL
+ d_freq = 0.0;
+ d_phase = 0.0;
+ fill(d_dfe.begin(), d_dfe.end(), gr_complex(1.0,0.0));
+}
+
+inline void
+digital_ofdm_frame_sink::enter_have_header()
+{
+ d_state = STATE_HAVE_HEADER;
+
+ // header consists of two 16-bit shorts in network byte order
+ // payload length is lower 12 bits
+ // whitener offset is upper 4 bits
+ d_packetlen = (d_header >> 16) & 0x0fff;
+ d_packet_whitener_offset = (d_header >> 28) & 0x000f;
+ d_packetlen_cnt = 0;
+
+ if (VERBOSE)
+ fprintf(stderr, "@ enter_have_header (payload_len = %d) (offset = %d)\n",
+ d_packetlen, d_packet_whitener_offset);
+}
+
+
+unsigned char digital_ofdm_frame_sink::slicer(const gr_complex x)
+{
+ unsigned int table_size = d_sym_value_out.size();
+ unsigned int min_index = 0;
+ float min_euclid_dist = norm(x - d_sym_position[0]);
+ float euclid_dist = 0;
+
+ for (unsigned int j = 1; j < table_size; j++){
+ euclid_dist = norm(x - d_sym_position[j]);
+ if (euclid_dist < min_euclid_dist){
+ min_euclid_dist = euclid_dist;
+ min_index = j;
+ }
+ }
+ return d_sym_value_out[min_index];
+}
+
+unsigned int digital_ofdm_frame_sink::demapper(const gr_complex *in,
+ unsigned char *out)
+{
+ unsigned int i=0, bytes_produced=0;
+ gr_complex carrier;
+
+ carrier=gr_expj(d_phase);
+
+ gr_complex accum_error = 0.0;
+ //while(i < d_occupied_carriers) {
+ while(i < d_subcarrier_map.size()) {
+ if(d_nresid > 0) {
+ d_partial_byte |= d_resid;
+ d_byte_offset += d_nresid;
+ d_nresid = 0;
+ d_resid = 0;
+ }
+
+ //while((d_byte_offset < 8) && (i < d_occupied_carriers)) {
+ while((d_byte_offset < 8) && (i < d_subcarrier_map.size())) {
+ //gr_complex sigrot = in[i]*carrier*d_dfe[i];
+ gr_complex sigrot = in[d_subcarrier_map[i]]*carrier*d_dfe[i];
+
+ if(d_derotated_output != NULL){
+ d_derotated_output[i] = sigrot;
+ }
+
+ unsigned char bits = slicer(sigrot);
+
+ gr_complex closest_sym = d_sym_position[bits];
+
+ accum_error += sigrot * conj(closest_sym);
+
+ // FIX THE FOLLOWING STATEMENT
+ if (norm(sigrot)> 0.001) d_dfe[i] += d_eq_gain*(closest_sym/sigrot-d_dfe[i]);
+
+ i++;
+
+ if((8 - d_byte_offset) >= d_nbits) {
+ d_partial_byte |= bits << (d_byte_offset);
+ d_byte_offset += d_nbits;
+ }
+ else {
+ d_nresid = d_nbits-(8-d_byte_offset);
+ int mask = ((1<<(8-d_byte_offset))-1);
+ d_partial_byte |= (bits & mask) << d_byte_offset;
+ d_resid = bits >> (8-d_byte_offset);
+ d_byte_offset += (d_nbits - d_nresid);
+ }
+ //printf("demod symbol: %.4f + j%.4f bits: %x partial_byte: %x byte_offset: %d resid: %x nresid: %d\n",
+ // in[i-1].real(), in[i-1].imag(), bits, d_partial_byte, d_byte_offset, d_resid, d_nresid);
+ }
+
+ if(d_byte_offset == 8) {
+ //printf("demod byte: %x \n\n", d_partial_byte);
+ out[bytes_produced++] = d_partial_byte;
+ d_byte_offset = 0;
+ d_partial_byte = 0;
+ }
+ }
+ //std::cerr << "accum_error " << accum_error << std::endl;
+
+ float angle = arg(accum_error);
+
+ d_freq = d_freq - d_freq_gain*angle;
+ d_phase = d_phase + d_freq - d_phase_gain*angle;
+ if (d_phase >= 2*M_PI) d_phase -= 2*M_PI;
+ if (d_phase <0) d_phase += 2*M_PI;
+
+ //if(VERBOSE)
+ // std::cerr << angle << "\t" << d_freq << "\t" << d_phase << "\t" << std::endl;
+
+ return bytes_produced;
+}
+
+
+digital_ofdm_frame_sink_sptr
+digital_make_ofdm_frame_sink(const std::vector<gr_complex> &sym_position,
+ const std::vector<unsigned char> &sym_value_out,
+ gr_msg_queue_sptr target_queue, unsigned int occupied_carriers,
+ float phase_gain, float freq_gain)
+{
+ return gnuradio::get_initial_sptr(new digital_ofdm_frame_sink(sym_position, sym_value_out,
+ target_queue, occupied_carriers,
+ phase_gain, freq_gain));
+}
+
+
+digital_ofdm_frame_sink::digital_ofdm_frame_sink(const std::vector<gr_complex> &sym_position,
+ const std::vector<unsigned char> &sym_value_out,
+ gr_msg_queue_sptr target_queue, unsigned int occupied_carriers,
+ float phase_gain, float freq_gain)
+ : gr_sync_block ("ofdm_frame_sink",
+ gr_make_io_signature2 (2, 2, sizeof(gr_complex)*occupied_carriers, sizeof(char)),
+ gr_make_io_signature (1, 1, sizeof(gr_complex)*occupied_carriers)),
+ d_target_queue(target_queue), d_occupied_carriers(occupied_carriers),
+ d_byte_offset(0), d_partial_byte(0),
+ d_resid(0), d_nresid(0),d_phase(0),d_freq(0),d_phase_gain(phase_gain),d_freq_gain(freq_gain),
+ d_eq_gain(0.05)
+{
+ std::string carriers = "FE7F";
+
+ // A bit hacky to fill out carriers to occupied_carriers length
+ int diff = (d_occupied_carriers - 4*carriers.length());
+ while(diff > 7) {
+ carriers.insert(0, "f");
+ carriers.insert(carriers.length(), "f");
+ diff -= 8;
+ }
+
+ // if there's extras left to be processed
+ // divide remaining to put on either side of current map
+ // all of this is done to stick with the concept of a carrier map string that
+ // can be later passed by the user, even though it'd be cleaner to just do this
+ // on the carrier map itself
+ int diff_left=0;
+ int diff_right=0;
+
+ // dictionary to convert from integers to ascii hex representation
+ char abc[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ if(diff > 0) {
+ char c[2] = {0,0};
+
+ diff_left = (int)ceil((float)diff/2.0f); // number of carriers to put on the left side
+ c[0] = abc[(1 << diff_left) - 1]; // convert to bits and move to ASCI integer
+ carriers.insert(0, c);
+
+ diff_right = diff - diff_left; // number of carriers to put on the right side
+ c[0] = abc[0xF^((1 << diff_right) - 1)]; // convert to bits and move to ASCI integer
+ carriers.insert(carriers.length(), c);
+ }
+
+ // It seemed like such a good idea at the time...
+ // because we are only dealing with the occupied_carriers
+ // at this point, the diff_left in the following compensates
+ // for any offset from the 0th carrier introduced
+ unsigned int i,j,k;
+ for(i = 0; i < (d_occupied_carriers/4)+diff_left; i++) {
+ char c = carriers[i];
+ for(j = 0; j < 4; j++) {
+ k = (strtol(&c, NULL, 16) >> (3-j)) & 0x1;
+ if(k) {
+ d_subcarrier_map.push_back(4*i + j - diff_left);
+ }
+ }
+ }
+
+ // make sure we stay in the limit currently imposed by the occupied_carriers
+ if(d_subcarrier_map.size() > d_occupied_carriers) {
+ throw std::invalid_argument("digital_ofdm_mapper_bcv: subcarriers allocated exceeds size of occupied carriers");
+ }
+
+ d_bytes_out = new unsigned char[d_occupied_carriers];
+ d_dfe.resize(occupied_carriers);
+ fill(d_dfe.begin(), d_dfe.end(), gr_complex(1.0,0.0));
+
+ set_sym_value_out(sym_position, sym_value_out);
+
+ enter_search();
+}
+
+digital_ofdm_frame_sink::~digital_ofdm_frame_sink ()
+{
+ delete [] d_bytes_out;
+}
+
+bool
+digital_ofdm_frame_sink::set_sym_value_out(const std::vector<gr_complex> &sym_position,
+ const std::vector<unsigned char> &sym_value_out)
+{
+ if (sym_position.size() != sym_value_out.size())
+ return false;
+
+ if (sym_position.size()<1)
+ return false;
+
+ d_sym_position = sym_position;
+ d_sym_value_out = sym_value_out;
+ d_nbits = (unsigned long)ceil(log10(float(d_sym_value_out.size())) / log10(2.0));
+
+ return true;
+}
+
+
+int
+digital_ofdm_frame_sink::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];
+ const char *sig = (const char *) input_items[1];
+ unsigned int j = 0;
+ unsigned int bytes=0;
+
+ // If the output is connected, send it the derotated symbols
+ if(output_items.size() >= 1)
+ d_derotated_output = (gr_complex *)output_items[0];
+ else
+ d_derotated_output = NULL;
+
+ if (VERBOSE)
+ fprintf(stderr,">>> Entering state machine\n");
+
+ switch(d_state) {
+
+ case STATE_SYNC_SEARCH: // Look for flag indicating beginning of pkt
+ if (VERBOSE)
+ fprintf(stderr,"SYNC Search, noutput=%d\n", noutput_items);
+
+ if (sig[0]) { // Found it, set up for header decode
+ enter_have_sync();
+ }
+ break;
+
+ case STATE_HAVE_SYNC:
+ // only demod after getting the preamble signal; otherwise, the
+ // equalizer taps will screw with the PLL performance
+ bytes = demapper(&in[0], d_bytes_out);
+
+ if (VERBOSE) {
+ if(sig[0])
+ printf("ERROR -- Found SYNC in HAVE_SYNC\n");
+ fprintf(stderr,"Header Search bitcnt=%d, header=0x%08x\n",
+ d_headerbytelen_cnt, d_header);
+ }
+
+ j = 0;
+ while(j < bytes) {
+ d_header = (d_header << 8) | (d_bytes_out[j] & 0xFF);
+ j++;
+
+ if (++d_headerbytelen_cnt == HEADERBYTELEN) {
+
+ if (VERBOSE)
+ fprintf(stderr, "got header: 0x%08x\n", d_header);
+
+ // we have a full header, check to see if it has been received properly
+ if (header_ok()){
+ enter_have_header();
+
+ if (VERBOSE)
+ printf("\nPacket Length: %d\n", d_packetlen);
+
+ while((j < bytes) && (d_packetlen_cnt < d_packetlen)) {
+ d_packet[d_packetlen_cnt++] = d_bytes_out[j++];
+ }
+
+ if(d_packetlen_cnt == d_packetlen) {
+ gr_message_sptr msg =
+ gr_make_message(0, d_packet_whitener_offset, 0, d_packetlen);
+ memcpy(msg->msg(), d_packet, d_packetlen_cnt);
+ d_target_queue->insert_tail(msg); // send it
+ msg.reset(); // free it up
+
+ enter_search();
+ }
+ }
+ else {
+ enter_search(); // bad header
+ }
+ }
+ }
+ break;
+
+ case STATE_HAVE_HEADER:
+ bytes = demapper(&in[0], d_bytes_out);
+
+ if (VERBOSE) {
+ if(sig[0])
+ printf("ERROR -- Found SYNC in HAVE_HEADER at %d, length of %d\n", d_packetlen_cnt, d_packetlen);
+ fprintf(stderr,"Packet Build\n");
+ }
+
+ j = 0;
+ while(j < bytes) {
+ d_packet[d_packetlen_cnt++] = d_bytes_out[j++];
+
+ if (d_packetlen_cnt == d_packetlen){ // packet is filled
+ // build a message
+ // NOTE: passing header field as arg1 is not scalable
+ gr_message_sptr msg =
+ gr_make_message(0, d_packet_whitener_offset, 0, d_packetlen_cnt);
+ memcpy(msg->msg(), d_packet, d_packetlen_cnt);
+
+ d_target_queue->insert_tail(msg); // send it
+ msg.reset(); // free it up
+
+ enter_search();
+ break;
+ }
+ }
+ break;
+
+ default:
+ assert(0);
+
+ } // switch
+
+ return 1;
+}
diff --git a/gr-digital/lib/digital_ofdm_insert_preamble.cc b/gr-digital/lib/digital_ofdm_insert_preamble.cc
new file mode 100644
index 000000000..a46133643
--- /dev/null
+++ b/gr-digital/lib/digital_ofdm_insert_preamble.cc
@@ -0,0 +1,187 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 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 this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <digital_ofdm_insert_preamble.h>
+#include <gr_io_signature.h>
+#include <stdexcept>
+#include <iostream>
+#include <string.h>
+
+digital_ofdm_insert_preamble_sptr
+digital_make_ofdm_insert_preamble(int fft_length,
+ const std::vector<std::vector<gr_complex> > &preamble)
+{
+ return gnuradio::get_initial_sptr(new digital_ofdm_insert_preamble(fft_length,
+ preamble));
+}
+
+digital_ofdm_insert_preamble::digital_ofdm_insert_preamble
+ (int fft_length,
+ const std::vector<std::vector<gr_complex> > &preamble)
+ : gr_block("ofdm_insert_preamble",
+ gr_make_io_signature2(2, 2,
+ sizeof(gr_complex)*fft_length,
+ sizeof(char)),
+ gr_make_io_signature2(1, 2,
+ sizeof(gr_complex)*fft_length,
+ sizeof(char))),
+ d_fft_length(fft_length),
+ d_preamble(preamble),
+ d_state(ST_IDLE),
+ d_nsymbols_output(0),
+ d_pending_flag(0)
+{
+ // sanity check preamble symbols
+ for (size_t i = 0; i < d_preamble.size(); i++){
+ if (d_preamble[i].size() != (size_t) d_fft_length)
+ throw std::invalid_argument("digital_ofdm_insert_preamble: invalid length for preamble symbol");
+ }
+
+ enter_idle();
+}
+
+
+digital_ofdm_insert_preamble::~digital_ofdm_insert_preamble()
+{
+}
+
+int
+digital_ofdm_insert_preamble::general_work (int noutput_items,
+ gr_vector_int &ninput_items_v,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+{
+ int ninput_items = std::min(ninput_items_v[0], ninput_items_v[1]);
+ const gr_complex *in_sym = (const gr_complex *) input_items[0];
+ const unsigned char *in_flag = (const unsigned char *) input_items[1];
+
+ gr_complex *out_sym = (gr_complex *) output_items[0];
+ unsigned char *out_flag = 0;
+ if (output_items.size() == 2)
+ out_flag = (unsigned char *) output_items[1];
+
+
+ int no = 0; // number items output
+ int ni = 0; // number items read from input
+
+
+#define write_out_flag() \
+ do { if (out_flag) \
+ out_flag[no] = d_pending_flag; \
+ d_pending_flag = 0; \
+ } while(0)
+
+
+ while (no < noutput_items && ni < ninput_items){
+ switch(d_state){
+ case ST_IDLE:
+ if (in_flag[ni] & 0x1) // this is first symbol of new payload
+ enter_preamble();
+ else
+ ni++; // eat one input symbol
+ break;
+
+ case ST_PREAMBLE:
+ assert(in_flag[ni] & 0x1);
+ if (d_nsymbols_output >= (int) d_preamble.size()){
+ // we've output all the preamble
+ enter_first_payload();
+ }
+ else {
+ memcpy(&out_sym[no * d_fft_length],
+ &d_preamble[d_nsymbols_output][0],
+ d_fft_length*sizeof(gr_complex));
+
+ write_out_flag();
+ no++;
+ d_nsymbols_output++;
+ }
+ break;
+
+ case ST_FIRST_PAYLOAD:
+ // copy first payload symbol from input to output
+ memcpy(&out_sym[no * d_fft_length],
+ &in_sym[ni * d_fft_length],
+ d_fft_length * sizeof(gr_complex));
+
+ write_out_flag();
+ no++;
+ ni++;
+ enter_payload();
+ break;
+
+ case ST_PAYLOAD:
+ if (in_flag[ni] & 0x1){ // this is first symbol of a new payload
+ enter_preamble();
+ break;
+ }
+
+ // copy a symbol from input to output
+ memcpy(&out_sym[no * d_fft_length],
+ &in_sym[ni * d_fft_length],
+ d_fft_length * sizeof(gr_complex));
+
+ write_out_flag();
+ no++;
+ ni++;
+ break;
+
+ default:
+ std::cerr << "digital_ofdm_insert_preamble: (can't happen) invalid state, resetting\n";
+ enter_idle();
+ }
+ }
+
+ consume_each(ni);
+ return no;
+}
+
+void
+digital_ofdm_insert_preamble::enter_idle()
+{
+ d_state = ST_IDLE;
+ d_nsymbols_output = 0;
+ d_pending_flag = 0;
+}
+
+void
+digital_ofdm_insert_preamble::enter_preamble()
+{
+ d_state = ST_PREAMBLE;
+ d_nsymbols_output = 0;
+ d_pending_flag = 1;
+}
+
+void
+digital_ofdm_insert_preamble::enter_first_payload()
+{
+ d_state = ST_FIRST_PAYLOAD;
+}
+
+void
+digital_ofdm_insert_preamble::enter_payload()
+{
+ d_state = ST_PAYLOAD;
+}
diff --git a/gr-digital/lib/digital_ofdm_mapper_bcv.cc b/gr-digital/lib/digital_ofdm_mapper_bcv.cc
new file mode 100644
index 000000000..cf3d08703
--- /dev/null
+++ b/gr-digital/lib/digital_ofdm_mapper_bcv.cc
@@ -0,0 +1,241 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2006-2008,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_ofdm_mapper_bcv.h>
+#include <gr_io_signature.h>
+#include <stdexcept>
+#include <string.h>
+
+digital_ofdm_mapper_bcv_sptr
+digital_make_ofdm_mapper_bcv (const std::vector<gr_complex> &constellation, unsigned int msgq_limit,
+ unsigned int occupied_carriers, unsigned int fft_length)
+{
+ return gnuradio::get_initial_sptr(new digital_ofdm_mapper_bcv (constellation, msgq_limit,
+ occupied_carriers, fft_length));
+}
+
+// Consumes 1 packet and produces as many OFDM symbols of fft_length to hold the full packet
+digital_ofdm_mapper_bcv::digital_ofdm_mapper_bcv (const std::vector<gr_complex> &constellation, unsigned int msgq_limit,
+ unsigned int occupied_carriers, unsigned int fft_length)
+ : gr_sync_block ("ofdm_mapper_bcv",
+ gr_make_io_signature (0, 0, 0),
+ gr_make_io_signature2 (1, 2, sizeof(gr_complex)*fft_length, sizeof(char))),
+ d_constellation(constellation),
+ d_msgq(gr_make_msg_queue(msgq_limit)), d_msg_offset(0), d_eof(false),
+ d_occupied_carriers(occupied_carriers),
+ d_fft_length(fft_length),
+ d_bit_offset(0),
+ d_pending_flag(0),
+ d_resid(0),
+ d_nresid(0)
+{
+ if (!(d_occupied_carriers <= d_fft_length))
+ throw std::invalid_argument("digital_ofdm_mapper_bcv: occupied carriers must be <= fft_length");
+
+ // this is not the final form of this solution since we still use the occupied_tones concept,
+ // which would get us into trouble if the number of carriers we seek is greater than the occupied carriers.
+ // Eventually, we will get rid of the occupied_carriers concept.
+ std::string carriers = "FE7F";
+
+ // A bit hacky to fill out carriers to occupied_carriers length
+ int diff = (d_occupied_carriers - 4*carriers.length());
+ while(diff > 7) {
+ carriers.insert(0, "f");
+ carriers.insert(carriers.length(), "f");
+ diff -= 8;
+ }
+
+ // if there's extras left to be processed
+ // divide remaining to put on either side of current map
+ // all of this is done to stick with the concept of a carrier map string that
+ // can be later passed by the user, even though it'd be cleaner to just do this
+ // on the carrier map itself
+ int diff_left=0;
+ int diff_right=0;
+
+ // dictionary to convert from integers to ascii hex representation
+ char abc[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ if(diff > 0) {
+ char c[2] = {0,0};
+
+ diff_left = (int)ceil((float)diff/2.0f); // number of carriers to put on the left side
+ c[0] = abc[(1 << diff_left) - 1]; // convert to bits and move to ASCI integer
+ carriers.insert(0, c);
+
+ diff_right = diff - diff_left; // number of carriers to put on the right side
+ c[0] = abc[0xF^((1 << diff_right) - 1)]; // convert to bits and move to ASCI integer
+ carriers.insert(carriers.length(), c);
+ }
+
+ // find out how many zeros to pad on the sides; the difference between the fft length and the subcarrier
+ // mapping size in chunks of four. This is the number to pack on the left and this number plus any
+ // residual nulls (if odd) will be packed on the right.
+ diff = (d_fft_length/4 - carriers.length())/2;
+
+ unsigned int i,j,k;
+ for(i = 0; i < carriers.length(); i++) {
+ char c = carriers[i]; // get the current hex character from the string
+ for(j = 0; j < 4; j++) { // walk through all four bits
+ k = (strtol(&c, NULL, 16) >> (3-j)) & 0x1; // convert to int and extract next bit
+ if(k) { // if bit is a 1,
+ d_subcarrier_map.push_back(4*(i+diff) + j); // use this subcarrier
+ }
+ }
+ }
+
+ // make sure we stay in the limit currently imposed by the occupied_carriers
+ if(d_subcarrier_map.size() > d_occupied_carriers) {
+ throw std::invalid_argument("digital_ofdm_mapper_bcv: subcarriers allocated exceeds size of occupied carriers");
+ }
+
+ d_nbits = (unsigned long)ceil(log10(float(d_constellation.size())) / log10(2.0));
+}
+
+digital_ofdm_mapper_bcv::~digital_ofdm_mapper_bcv(void)
+{
+}
+
+int digital_ofdm_mapper_bcv::randsym()
+{
+ return (rand() % d_constellation.size());
+}
+
+int
+digital_ofdm_mapper_bcv::work(int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+{
+ gr_complex *out = (gr_complex *)output_items[0];
+
+ unsigned int i=0;
+
+ //printf("OFDM BPSK Mapper: ninput_items: %d noutput_items: %d\n", ninput_items[0], noutput_items);
+
+ if(d_eof) {
+ return -1;
+ }
+
+ if(!d_msg) {
+ d_msg = d_msgq->delete_head(); // block, waiting for a message
+ d_msg_offset = 0;
+ d_bit_offset = 0;
+ d_pending_flag = 1; // new packet, write start of packet flag
+
+ if((d_msg->length() == 0) && (d_msg->type() == 1)) {
+ d_msg.reset();
+ return -1; // We're done; no more messages coming.
+ }
+ }
+
+ char *out_flag = 0;
+ if(output_items.size() == 2)
+ out_flag = (char *) output_items[1];
+
+
+ // Build a single symbol:
+ // Initialize all bins to 0 to set unused carriers
+ memset(out, 0, d_fft_length*sizeof(gr_complex));
+
+ i = 0;
+ unsigned char bits = 0;
+ //while((d_msg_offset < d_msg->length()) && (i < d_occupied_carriers)) {
+ while((d_msg_offset < d_msg->length()) && (i < d_subcarrier_map.size())) {
+
+ // need new data to process
+ if(d_bit_offset == 0) {
+ d_msgbytes = d_msg->msg()[d_msg_offset];
+ //printf("mod message byte: %x\n", d_msgbytes);
+ }
+
+ if(d_nresid > 0) {
+ // take the residual bits, fill out nbits with info from the new byte, and put them in the symbol
+ d_resid |= (((1 << d_nresid)-1) & d_msgbytes) << (d_nbits - d_nresid);
+ bits = d_resid;
+
+ out[d_subcarrier_map[i]] = d_constellation[bits];
+ i++;
+
+ d_bit_offset += d_nresid;
+ d_nresid = 0;
+ d_resid = 0;
+ //printf("mod bit(r): %x resid: %x nresid: %d bit_offset: %d\n",
+ // bits, d_resid, d_nresid, d_bit_offset);
+ }
+ else {
+ if((8 - d_bit_offset) >= d_nbits) { // test to make sure we can fit nbits
+ // take the nbits number of bits at a time from the byte to add to the symbol
+ bits = ((1 << d_nbits)-1) & (d_msgbytes >> d_bit_offset);
+ d_bit_offset += d_nbits;
+
+ out[d_subcarrier_map[i]] = d_constellation[bits];
+ i++;
+ }
+ else { // if we can't fit nbits, store them for the next
+ // saves d_nresid bits of this message where d_nresid < d_nbits
+ unsigned int extra = 8-d_bit_offset;
+ d_resid = ((1 << extra)-1) & (d_msgbytes >> d_bit_offset);
+ d_bit_offset += extra;
+ d_nresid = d_nbits - extra;
+ }
+
+ }
+
+ if(d_bit_offset == 8) {
+ d_bit_offset = 0;
+ d_msg_offset++;
+ }
+ }
+
+ // Ran out of data to put in symbol
+ if (d_msg_offset == d_msg->length()) {
+ if(d_nresid > 0) {
+ d_resid |= 0x00;
+ bits = d_resid;
+ d_nresid = 0;
+ d_resid = 0;
+ }
+
+ //while(i < d_occupied_carriers) { // finish filling out the symbol
+ while(i < d_subcarrier_map.size()) { // finish filling out the symbol
+ out[d_subcarrier_map[i]] = d_constellation[randsym()];
+
+ i++;
+ }
+
+ if (d_msg->type() == 1) // type == 1 sets EOF
+ d_eof = true;
+ d_msg.reset(); // finished packet, free message
+ assert(d_bit_offset == 0);
+ }
+
+ if (out_flag)
+ out_flag[0] = d_pending_flag;
+ d_pending_flag = 0;
+
+ return 1; // produced symbol
+}
diff --git a/gr-digital/lib/digital_ofdm_sampler.cc b/gr-digital/lib/digital_ofdm_sampler.cc
new file mode 100644
index 000000000..cab8c2ba9
--- /dev/null
+++ b/gr-digital/lib/digital_ofdm_sampler.cc
@@ -0,0 +1,133 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2007,2008,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_ofdm_sampler.h>
+#include <gr_io_signature.h>
+#include <gr_expj.h>
+#include <cstdio>
+
+digital_ofdm_sampler_sptr
+digital_make_ofdm_sampler (unsigned int fft_length,
+ unsigned int symbol_length,
+ unsigned int timeout)
+{
+ return gnuradio::get_initial_sptr(new digital_ofdm_sampler (fft_length, symbol_length, timeout));
+}
+
+digital_ofdm_sampler::digital_ofdm_sampler (unsigned int fft_length,
+ unsigned int symbol_length,
+ unsigned int timeout)
+ : gr_block ("ofdm_sampler",
+ gr_make_io_signature2 (2, 2, sizeof (gr_complex), sizeof(char)),
+ gr_make_io_signature2 (2, 2, sizeof (gr_complex)*fft_length, sizeof(char)*fft_length)),
+ d_state(STATE_NO_SIG), d_timeout_max(timeout), d_fft_length(fft_length), d_symbol_length(symbol_length)
+{
+ set_relative_rate(1.0/(double) fft_length); // buffer allocator hint
+}
+
+void
+digital_ofdm_sampler::forecast (int noutput_items, gr_vector_int &ninput_items_required)
+{
+ // FIXME do we need more
+ //int nreqd = (noutput_items-1) * d_symbol_length + d_fft_length;
+ int nreqd = d_symbol_length + d_fft_length;
+ unsigned ninputs = ninput_items_required.size ();
+ for (unsigned i = 0; i < ninputs; i++)
+ ninput_items_required[i] = nreqd;
+}
+
+
+int
+digital_ofdm_sampler::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 *iptr = (const gr_complex *) input_items[0];
+ const char *trigger = (const char *) input_items[1];
+
+ gr_complex *optr = (gr_complex *) output_items[0];
+ char *outsig = (char *) output_items[1];
+
+ //FIXME: we only process a single OFDM symbol at a time; after the preamble, we can
+ // process a few at a time as long as we always look out for the next preamble.
+
+ unsigned int index=d_fft_length; // start one fft length into the input so we can always look back this far
+
+ outsig[0] = 0; // set output to no signal by default
+
+ // Search for a preamble trigger signal during the next symbol length
+ while((d_state != STATE_PREAMBLE) && (index <= (d_symbol_length+d_fft_length))) {
+ if(trigger[index]) {
+ outsig[0] = 1; // tell the next block there is a preamble coming
+ d_state = STATE_PREAMBLE;
+ }
+ else
+ index++;
+ }
+
+ unsigned int i, pos, ret;
+ switch(d_state) {
+ case(STATE_PREAMBLE):
+ // When we found a preamble trigger, get it and set the symbol boundary here
+ for(i = (index - d_fft_length + 1); i <= index; i++) {
+ *optr++ = iptr[i];
+ }
+
+ d_timeout = d_timeout_max; // tell the system to expect at least this many symbols for a frame
+ d_state = STATE_FRAME;
+ consume_each(index - d_fft_length + 1); // consume up to one fft_length away to keep the history
+ ret = 1;
+ break;
+
+ case(STATE_FRAME):
+ // use this state when we have processed a preamble and are getting the rest of the frames
+ //FIXME: we could also have a power squelch system here to enter STATE_NO_SIG if no power is received
+
+ // skip over fft length history and cyclic prefix
+ pos = d_symbol_length; // keeps track of where we are in the input buffer
+ while(pos < d_symbol_length + d_fft_length) {
+ *optr++ = iptr[pos++];
+ }
+
+ if(d_timeout-- == 0) {
+ printf("TIMEOUT\n");
+ d_state = STATE_NO_SIG;
+ }
+
+ consume_each(d_symbol_length); // jump up by 1 fft length and the cyclic prefix length
+ ret = 1;
+ break;
+
+ case(STATE_NO_SIG):
+ default:
+ consume_each(index-d_fft_length); // consume everything we've gone through so far leaving the fft length history
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}