summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gr-digital/examples/CMakeLists.txt2
-rw-r--r--gr-digital/examples/demod/symbol_sync_test_complex.grc2809
-rw-r--r--gr-digital/examples/demod/symbol_sync_test_float.grc2636
-rw-r--r--gr-digital/grc/digital_block_tree.xml3
-rw-r--r--gr-digital/grc/digital_symbol_sync_xx.xml200
-rw-r--r--gr-digital/include/gnuradio/digital/CMakeLists.txt6
-rw-r--r--gr-digital/include/gnuradio/digital/interpolating_resampler_type.h40
-rw-r--r--gr-digital/include/gnuradio/digital/symbol_sync_cc.h278
-rw-r--r--gr-digital/include/gnuradio/digital/symbol_sync_ff.h278
-rw-r--r--gr-digital/include/gnuradio/digital/timing_error_detector_type.h46
-rw-r--r--gr-digital/lib/CMakeLists.txt7
-rw-r--r--gr-digital/lib/clock_tracking_loop.cc307
-rw-r--r--gr-digital/lib/clock_tracking_loop.h719
-rw-r--r--gr-digital/lib/interpolating_resampler.cc794
-rw-r--r--gr-digital/lib/interpolating_resampler.h599
-rw-r--r--gr-digital/lib/symbol_sync_cc_impl.cc650
-rw-r--r--gr-digital/lib/symbol_sync_cc_impl.h153
-rw-r--r--gr-digital/lib/symbol_sync_ff_impl.cc650
-rw-r--r--gr-digital/lib/symbol_sync_ff_impl.h153
-rw-r--r--gr-digital/lib/timing_error_detector.cc443
-rw-r--r--gr-digital/lib/timing_error_detector.h513
-rw-r--r--gr-digital/swig/digital_swig2.i17
-rw-r--r--gr-filter/include/gnuradio/filter/CMakeLists.txt4
-rw-r--r--gr-filter/include/gnuradio/filter/interp_differentiator_taps.h162
-rw-r--r--gr-filter/include/gnuradio/filter/mmse_interp_differentiator_cc.h80
-rw-r--r--gr-filter/include/gnuradio/filter/mmse_interp_differentiator_ff.h79
-rw-r--r--gr-filter/lib/CMakeLists.txt6
-rw-r--r--gr-filter/lib/gen_interpolator_taps/diff_objective_fct.c214
-rw-r--r--gr-filter/lib/gen_interpolator_taps/gen_interp_differentiator_taps.c182
-rwxr-xr-xgr-filter/lib/gen_interpolator_taps/generate.sh43
-rw-r--r--gr-filter/lib/mmse_interp_differentiator_cc.cc78
-rw-r--r--gr-filter/lib/mmse_interp_differentiator_ff.cc78
-rw-r--r--gr-filter/lib/qa_filter.cc6
-rw-r--r--gr-filter/lib/qa_mmse_interp_differentiator_cc.cc130
-rw-r--r--gr-filter/lib/qa_mmse_interp_differentiator_cc.h45
-rw-r--r--gr-filter/lib/qa_mmse_interp_differentiator_ff.cc99
-rw-r--r--gr-filter/lib/qa_mmse_interp_differentiator_ff.h45
37 files changed, 12547 insertions, 7 deletions
diff --git a/gr-digital/examples/CMakeLists.txt b/gr-digital/examples/CMakeLists.txt
index 3725fa55ce..99c0712722 100644
--- a/gr-digital/examples/CMakeLists.txt
+++ b/gr-digital/examples/CMakeLists.txt
@@ -76,6 +76,8 @@ install(
demod/mpsk_demod.grc
demod/pam_sync.grc
demod/pam_timing.grc
+ demod/symbol_sync_test_complex.grc
+ demod/symbol_sync_test_float.grc
demod/test_corr_est.grc
DESTINATION ${GR_PKG_DIGITAL_EXAMPLES_DIR}/demod
)
diff --git a/gr-digital/examples/demod/symbol_sync_test_complex.grc b/gr-digital/examples/demod/symbol_sync_test_complex.grc
new file mode 100644
index 0000000000..1b7ac50f8d
--- /dev/null
+++ b/gr-digital/examples/demod/symbol_sync_test_complex.grc
@@ -0,0 +1,2809 @@
+<?xml version='1.0' encoding='utf-8'?>
+<?grc format='1' created='3.7.12'?>
+<!--
+ Copyright (C) 2017 Free Software Foundation
+
+ 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.
+-->
+<flow_graph>
+ <timestamp>Mon Jan 12 16:38:01 2015</timestamp>
+ <block>
+ <key>options</key>
+ <param>
+ <key>author</key>
+ <value>Andy Walls</value>
+ </param>
+ <param>
+ <key>window_size</key>
+ <value>3200, 700</value>
+ </param>
+ <param>
+ <key>category</key>
+ <value>Custom</value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>description</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(10, 10)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>generate_options</key>
+ <value>qt_gui</value>
+ </param>
+ <param>
+ <key>hier_block_src_path</key>
+ <value>.:</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>symbol_sync_test_complex</value>
+ </param>
+ <param>
+ <key>max_nouts</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>qt_qss_theme</key>
+ <value></value>
+ </param>
+ <param>
+ <key>realtime_scheduling</key>
+ <value></value>
+ </param>
+ <param>
+ <key>run_command</key>
+ <value>{python} -u {filename}</value>
+ </param>
+ <param>
+ <key>run_options</key>
+ <value>prompt</value>
+ </param>
+ <param>
+ <key>run</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>thread_safe_setters</key>
+ <value></value>
+ </param>
+ <param>
+ <key>title</key>
+ <value>Symbol Sync Test (Complex)</value>
+ </param>
+ </block>
+ <block>
+ <key>variable</key>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(552, 244)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>baud_rate</value>
+ </param>
+ <param>
+ <key>value</key>
+ <value>1200.0</value>
+ </param>
+ </block>
+ <block>
+ <key>variable_qtgui_chooser</key>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>value</key>
+ <value>4</value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(392, 48)</value>
+ </param>
+ <param>
+ <key>gui_hint</key>
+ <value>0,0,1,1</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>data_src</value>
+ </param>
+ <param>
+ <key>label0</key>
+ <value>Random</value>
+ </param>
+ <param>
+ <key>label1</key>
+ <value>Low freq</value>
+ </param>
+ <param>
+ <key>label2</key>
+ <value>Dot Pattern</value>
+ </param>
+ <param>
+ <key>label3</key>
+ <value>Pulse</value>
+ </param>
+ <param>
+ <key>label4</key>
+ <value>Packets</value>
+ </param>
+ <param>
+ <key>label</key>
+ <value></value>
+ </param>
+ <param>
+ <key>labels</key>
+ <value>[]</value>
+ </param>
+ <param>
+ <key>num_opts</key>
+ <value>5</value>
+ </param>
+ <param>
+ <key>option0</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>option1</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>option2</key>
+ <value>2</value>
+ </param>
+ <param>
+ <key>option3</key>
+ <value>3</value>
+ </param>
+ <param>
+ <key>option4</key>
+ <value>4</value>
+ </param>
+ <param>
+ <key>options</key>
+ <value>[0, 1, 2]</value>
+ </param>
+ <param>
+ <key>orient</key>
+ <value>Qt.QVBoxLayout</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>int</value>
+ </param>
+ <param>
+ <key>widget</key>
+ <value>combo_box</value>
+ </param>
+ </block>
+ <block>
+ <key>variable</key>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(2304, 572)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>gain_mu</value>
+ </param>
+ <param>
+ <key>value</key>
+ <value>2.0*math.exp(-zeta*omega_n_norm)*math.sinh(zeta*omega_n_norm)</value>
+ </param>
+ </block>
+ <block>
+ <key>variable_qtgui_label</key>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>value</key>
+ <value>"%8.6f" % gain_mu</value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>formatter</key>
+ <value>None</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(2808, 572)</value>
+ </param>
+ <param>
+ <key>gui_hint</key>
+ <value>1,2,1,1</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>gain_mu_label</value>
+ </param>
+ <param>
+ <key>label</key>
+ <value>Gain Mu</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>string</value>
+ </param>
+ </block>
+ <block>
+ <key>variable</key>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(2304, 636)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>gain_omega</value>
+ </param>
+ <param>
+ <key>value</key>
+ <value>2.0-gain_mu-2.0*math.exp(-zeta*omega_n_norm)*(math.cosh(omega_d_norm) if zeta &gt; 1.0 else math.cos(omega_d_norm))</value>
+ </param>
+ </block>
+ <block>
+ <key>variable_qtgui_label</key>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>value</key>
+ <value>"%8.6f" % gain_omega</value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>formatter</key>
+ <value>None</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(2640, 572)</value>
+ </param>
+ <param>
+ <key>gui_hint</key>
+ <value>0,2,1,1</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>gain_omega_label</value>
+ </param>
+ <param>
+ <key>label</key>
+ <value>Gain Omega</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>string</value>
+ </param>
+ </block>
+ <block>
+ <key>variable</key>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(2304, 508)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>omega_d_norm</value>
+ </param>
+ <param>
+ <key>value</key>
+ <value>omega_n_norm*math.sqrt((zeta*zeta-1.0) if zeta &gt; 1.0 else (1.0-zeta*zeta))</value>
+ </param>
+ </block>
+ <block>
+ <key>variable_qtgui_range</key>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>value</key>
+ <value>0.54</value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(2440, 568)</value>
+ </param>
+ <param>
+ <key>gui_hint</key>
+ <value>1,1,1,1</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>omega_n_norm</value>
+ </param>
+ <param>
+ <key>label</key>
+ <value>Normalized Bandwidth</value>
+ </param>
+ <param>
+ <key>min_len</key>
+ <value>200</value>
+ </param>
+ <param>
+ <key>orient</key>
+ <value>Qt.Horizontal</value>
+ </param>
+ <param>
+ <key>start</key>
+ <value>0.0</value>
+ </param>
+ <param>
+ <key>step</key>
+ <value>0.001</value>
+ </param>
+ <param>
+ <key>stop</key>
+ <value>2.0*math.pi*0.25</value>
+ </param>
+ <param>
+ <key>rangeType</key>
+ <value>float</value>
+ </param>
+ <param>
+ <key>widget</key>
+ <value>counter_slider</value>
+ </param>
+ </block>
+ <block>
+ <key>variable</key>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(2608, 212)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>osps</value>
+ </param>
+ <param>
+ <key>value</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>variable_tag_object</key>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(8, 600)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>packet_time_est_tag</value>
+ </param>
+ <param>
+ <key>key</key>
+ <value>pmt.intern("test")</value>
+ </param>
+ <param>
+ <key>offset</key>
+ <value>9</value>
+ </param>
+ <param>
+ <key>src</key>
+ <value>pmt.intern("packet_vector_source")</value>
+ </param>
+ <param>
+ <key>value</key>
+ <value>pmt.from_double(0.0)</value>
+ </param>
+ </block>
+ <block>
+ <key>variable</key>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(1336, 236)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>sps</value>
+ </param>
+ <param>
+ <key>value</key>
+ <value>7</value>
+ </param>
+ </block>
+ <block>
+ <key>variable_qtgui_range</key>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>value</key>
+ <value>1.0/math.sqrt(2.0)*0+1.3</value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(2440, 448)</value>
+ </param>
+ <param>
+ <key>gui_hint</key>
+ <value>0,1,1,1</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>zeta</value>
+ </param>
+ <param>
+ <key>label</key>
+ <value>Damping Factor</value>
+ </param>
+ <param>
+ <key>min_len</key>
+ <value>200</value>
+ </param>
+ <param>
+ <key>orient</key>
+ <value>Qt.Horizontal</value>
+ </param>
+ <param>
+ <key>start</key>
+ <value>0.1</value>
+ </param>
+ <param>
+ <key>step</key>
+ <value>0.1</value>
+ </param>
+ <param>
+ <key>stop</key>
+ <value>5.0</value>
+ </param>
+ <param>
+ <key>rangeType</key>
+ <value>float</value>
+ </param>
+ <param>
+ <key>widget</key>
+ <value>counter_slider</value>
+ </param>
+ </block>
+ <block>
+ <key>analog_random_source_x</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(8, 192)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>analog_random_source_x_0</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>max</key>
+ <value>2</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>min</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>num_samps</key>
+ <value>16384</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>short</value>
+ </param>
+ <param>
+ <key>repeat</key>
+ <value>True</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_add_xx</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(448, 264)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_add_xx_0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>short</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>num_inputs</key>
+ <value>5</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_char_to_float</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(1160, 324)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_char_to_float_2</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>scale</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_float_to_char</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(872, 324)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_float_to_char_0</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>scale</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_float_to_complex</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(2752, 320)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_float_to_complex_0_0</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_float_to_complex</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(2752, 392)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_float_to_complex_1_0</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_float_to_complex</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(1936, 328)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_float_to_complex_3</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_multiply_const_vxx</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>const</key>
+ <value>1 if data_src == 4 else 0</value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(224, 540)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_multiply_const_vxx_0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>short</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_multiply_const_vxx</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>const</key>
+ <value>1 if data_src == 3 else 0</value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(216, 460)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_multiply_const_vxx_0_0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>short</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_multiply_const_vxx</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>const</key>
+ <value>1 if data_src == 2 else 0</value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(216, 380)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_multiply_const_vxx_0_0_0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>short</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_multiply_const_vxx</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>const</key>
+ <value>1 if data_src == 1 else 0</value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(216, 308)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_multiply_const_vxx_0_0_0_0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>short</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_multiply_const_vxx</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>const</key>
+ <value>1 if data_src == 0 else 0</value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(208, 212)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_multiply_const_vxx_0_0_0_0_0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>short</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_multiply_const_vxx</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>const</key>
+ <value>0.707+0.707j</value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(2128, 340)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_multiply_const_vxx_1</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>complex</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_repeat</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(1312, 324)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_repeat_0</value>
+ </param>
+ <param>
+ <key>interp</key>
+ <value>sps*2</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>float</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_short_to_float</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(712, 324)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_short_to_float_0</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>scale</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_throttle</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(544, 324)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_throttle_0</value>
+ </param>
+ <param>
+ <key>ignoretag</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>samples_per_second</key>
+ <value>baud_rate*10</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>short</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_vector_source_x</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(8, 364)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_vector_source_x_0_0</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>short</value>
+ </param>
+ <param>
+ <key>repeat</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>tags</key>
+ <value>[]</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>vector</key>
+ <value>(0, 1, 0, 1, 0, 1, 0, 1)</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_vector_source_x</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(8, 284)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_vector_source_x_0_0_0</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>short</value>
+ </param>
+ <param>
+ <key>repeat</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>tags</key>
+ <value>[]</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>vector</key>
+ <value>(0,0,0,0,1,1,1,1)</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_vector_source_x</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(8, 444)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_vector_source_x_0_0_1</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>short</value>
+ </param>
+ <param>
+ <key>repeat</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>tags</key>
+ <value>[]</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>vector</key>
+ <value>[1]+[0]*7</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_vector_source_x</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(8, 524)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_vector_source_x_0_0_1_0</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>short</value>
+ </param>
+ <param>
+ <key>repeat</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>tags</key>
+ <value>[packet_time_est_tag]</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>vector</key>
+ <value>[1,0]*(4*12*0)+[1,1,0,1,0,1,0,1]*12+[1,0,1,1,1,1,1,0,0,1]+[1,1,1,1,0,1,1,0,0,1]+[1,0,1,1,1,1,1,0,0,1]+[0,1,1,1,0,1,1,0,1,0]+[0,0,0,0,0,1,0,1,0,1,1,0,0,1,1,1,0,0,0,0]+[2]*128</value>
+ </param>
+ </block>
+ <block>
+ <key>digital_map_bb</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(1024, 324)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>digital_map_bb_0</value>
+ </param>
+ <param>
+ <key>map</key>
+ <value>[-1, 1,0]</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ </block>
+ <block>
+ <key>digital_symbol_sync_xx</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>damping</key>
+ <value>zeta</value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>nfilters</key>
+ <value>128</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(2376, 296)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>cc</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>digital_symbol_sync_xx_0</value>
+ </param>
+ <param>
+ <key>resamp_type</key>
+ <value>digital.IR_MMSE_8TAP</value>
+ </param>
+ <param>
+ <key>loop_bw</key>
+ <value>omega_n_norm</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>max_dev</key>
+ <value>1.5</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>osps</key>
+ <value>osps</value>
+ </param>
+ <param>
+ <key>pfb_mf_taps</key>
+ <value>[]</value>
+ </param>
+ <param>
+ <key>sps</key>
+ <value>sps</value>
+ </param>
+ <param>
+ <key>constellation</key>
+ <value>digital.constellation_qpsk().base()</value>
+ </param>
+ <param>
+ <key>ted_type</key>
+ <value>digital.TED_MUELLER_AND_MULLER</value>
+ </param>
+ </block>
+ <block>
+ <key>fir_filter_xxx</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>decim</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(1688, 316)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>fir_filter_xxx_0_1_0_0_0</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>samp_delay</key>
+ <value>int((sps-1.0)/2.0)+4</value>
+ </param>
+ <param>
+ <key>taps</key>
+ <value>[1.0/float(sps)]*sps</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>fff</value>
+ </param>
+ </block>
+ <block>
+ <key>import</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(184, 12)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>import_0</value>
+ </param>
+ <param>
+ <key>import</key>
+ <value>import math</value>
+ </param>
+ </block>
+ <block>
+ <key>qtgui_time_sink_x</key>
+ <param>
+ <key>autoscale</key>
+ <value>False</value>
+ </param>
+ <param>
+ <key>axislabels</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>ctrlpanel</key>
+ <value>False</value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>entags</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(2992, 296)</value>
+ </param>
+ <param>
+ <key>gui_hint</key>
+ <value>3,1,1,2</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>grid</key>
+ <value>False</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>qtgui_time_sink_x_0_0_0_0_0</value>
+ </param>
+ <param>
+ <key>legend</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>alpha1</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color1</key>
+ <value>"blue"</value>
+ </param>
+ <param>
+ <key>label1</key>
+ <value>Soft Bits Re</value>
+ </param>
+ <param>
+ <key>marker1</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>style1</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width1</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha10</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color10</key>
+ <value>"blue"</value>
+ </param>
+ <param>
+ <key>label10</key>
+ <value></value>
+ </param>
+ <param>
+ <key>marker10</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style10</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width10</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha2</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color2</key>
+ <value>"magenta"</value>
+ </param>
+ <param>
+ <key>label2</key>
+ <value>Soft Bits Im</value>
+ </param>
+ <param>
+ <key>marker2</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>style2</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width2</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha3</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color3</key>
+ <value>"red"</value>
+ </param>
+ <param>
+ <key>label3</key>
+ <value>Error</value>
+ </param>
+ <param>
+ <key>marker3</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style3</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width3</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha4</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color4</key>
+ <value>"green"</value>
+ </param>
+ <param>
+ <key>label4</key>
+ <value>Instantaneous Period</value>
+ </param>
+ <param>
+ <key>marker4</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style4</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width4</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha5</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color5</key>
+ <value>"black"</value>
+ </param>
+ <param>
+ <key>label5</key>
+ <value>Average Period</value>
+ </param>
+ <param>
+ <key>marker5</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style5</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width5</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha6</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color6</key>
+ <value>"yellow"</value>
+ </param>
+ <param>
+ <key>label6</key>
+ <value>(unused)</value>
+ </param>
+ <param>
+ <key>marker6</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style6</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>width6</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha7</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color7</key>
+ <value>"black"</value>
+ </param>
+ <param>
+ <key>label7</key>
+ <value></value>
+ </param>
+ <param>
+ <key>marker7</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style7</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width7</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha8</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color8</key>
+ <value>"black"</value>
+ </param>
+ <param>
+ <key>label8</key>
+ <value></value>
+ </param>
+ <param>
+ <key>marker8</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style8</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>width8</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha9</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color9</key>
+ <value>"dark green"</value>
+ </param>
+ <param>
+ <key>label9</key>
+ <value></value>
+ </param>
+ <param>
+ <key>marker9</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style9</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width9</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>name</key>
+ <value>Symbol Synched Output and Debug</value>
+ </param>
+ <param>
+ <key>nconnections</key>
+ <value>3</value>
+ </param>
+ <param>
+ <key>size</key>
+ <value>256*osps</value>
+ </param>
+ <param>
+ <key>srate</key>
+ <value>baud_rate*osps</value>
+ </param>
+ <param>
+ <key>tr_chan</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>tr_delay</key>
+ <value>0.01</value>
+ </param>
+ <param>
+ <key>tr_level</key>
+ <value>0.1</value>
+ </param>
+ <param>
+ <key>tr_mode</key>
+ <value>qtgui.TRIG_MODE_NORM</value>
+ </param>
+ <param>
+ <key>tr_slope</key>
+ <value>qtgui.TRIG_SLOPE_POS</value>
+ </param>
+ <param>
+ <key>tr_tag</key>
+ <value>"time_est"</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>complex</value>
+ </param>
+ <param>
+ <key>update_time</key>
+ <value>0.1</value>
+ </param>
+ <param>
+ <key>ylabel</key>
+ <value>Amplitude</value>
+ </param>
+ <param>
+ <key>yunit</key>
+ <value>""</value>
+ </param>
+ <param>
+ <key>ymax</key>
+ <value>sps+2</value>
+ </param>
+ <param>
+ <key>ymin</key>
+ <value>-1.5</value>
+ </param>
+ </block>
+ <block>
+ <key>qtgui_time_sink_x</key>
+ <param>
+ <key>autoscale</key>
+ <value>False</value>
+ </param>
+ <param>
+ <key>axislabels</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>ctrlpanel</key>
+ <value>False</value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>entags</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(2376, 204)</value>
+ </param>
+ <param>
+ <key>gui_hint</key>
+ <value>3,0,1,1</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>grid</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>qtgui_time_sink_x_0_1_0</value>
+ </param>
+ <param>
+ <key>legend</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>alpha1</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color1</key>
+ <value>"blue"</value>
+ </param>
+ <param>
+ <key>label1</key>
+ <value>Re</value>
+ </param>
+ <param>
+ <key>marker1</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style1</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width1</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha10</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color10</key>
+ <value>"blue"</value>
+ </param>
+ <param>
+ <key>label10</key>
+ <value></value>
+ </param>
+ <param>
+ <key>marker10</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style10</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width10</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha2</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color2</key>
+ <value>"red"</value>
+ </param>
+ <param>
+ <key>label2</key>
+ <value>Im</value>
+ </param>
+ <param>
+ <key>marker2</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style2</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width2</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha3</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color3</key>
+ <value>"green"</value>
+ </param>
+ <param>
+ <key>label3</key>
+ <value></value>
+ </param>
+ <param>
+ <key>marker3</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style3</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width3</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha4</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color4</key>
+ <value>"black"</value>
+ </param>
+ <param>
+ <key>label4</key>
+ <value></value>
+ </param>
+ <param>
+ <key>marker4</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style4</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width4</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha5</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color5</key>
+ <value>"dark green"</value>
+ </param>
+ <param>
+ <key>label5</key>
+ <value>Baseband</value>
+ </param>
+ <param>
+ <key>marker5</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style5</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width5</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha6</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color6</key>
+ <value>"magenta"</value>
+ </param>
+ <param>
+ <key>label6</key>
+ <value>Abs(Corr)</value>
+ </param>
+ <param>
+ <key>marker6</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style6</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width6</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha7</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color7</key>
+ <value>"yellow"</value>
+ </param>
+ <param>
+ <key>label7</key>
+ <value></value>
+ </param>
+ <param>
+ <key>marker7</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style7</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width7</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha8</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color8</key>
+ <value>"dark red"</value>
+ </param>
+ <param>
+ <key>label8</key>
+ <value></value>
+ </param>
+ <param>
+ <key>marker8</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style8</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width8</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha9</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color9</key>
+ <value>"dark green"</value>
+ </param>
+ <param>
+ <key>label9</key>
+ <value></value>
+ </param>
+ <param>
+ <key>marker9</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style9</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width9</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>name</key>
+ <value>""</value>
+ </param>
+ <param>
+ <key>nconnections</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>size</key>
+ <value>1024*3</value>
+ </param>
+ <param>
+ <key>srate</key>
+ <value>baud_rate*sps</value>
+ </param>
+ <param>
+ <key>tr_chan</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>tr_delay</key>
+ <value>0.01</value>
+ </param>
+ <param>
+ <key>tr_level</key>
+ <value>0.1</value>
+ </param>
+ <param>
+ <key>tr_mode</key>
+ <value>qtgui.TRIG_MODE_NORM</value>
+ </param>
+ <param>
+ <key>tr_slope</key>
+ <value>qtgui.TRIG_SLOPE_POS</value>
+ </param>
+ <param>
+ <key>tr_tag</key>
+ <value></value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>complex</value>
+ </param>
+ <param>
+ <key>update_time</key>
+ <value>0.10</value>
+ </param>
+ <param>
+ <key>ylabel</key>
+ <value>Amplitude</value>
+ </param>
+ <param>
+ <key>yunit</key>
+ <value>""</value>
+ </param>
+ <param>
+ <key>ymax</key>
+ <value>1.5</value>
+ </param>
+ <param>
+ <key>ymin</key>
+ <value>-1.5</value>
+ </param>
+ </block>
+ <block>
+ <key>rational_resampler_xxx</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>decim</key>
+ <value>21</value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>fbw</key>
+ <value>0.45</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(1480, 304)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>rational_resampler_xxx_0_0</value>
+ </param>
+ <param>
+ <key>interp</key>
+ <value>10</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>taps</key>
+ <value></value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>fff</value>
+ </param>
+ </block>
+ <connection>
+ <source_block_id>analog_random_source_x_0</source_block_id>
+ <sink_block_id>blocks_multiply_const_vxx_0_0_0_0_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_add_xx_0</source_block_id>
+ <sink_block_id>blocks_throttle_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_char_to_float_2</source_block_id>
+ <sink_block_id>blocks_repeat_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_float_to_char_0</source_block_id>
+ <sink_block_id>digital_map_bb_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_float_to_complex_0_0</source_block_id>
+ <sink_block_id>qtgui_time_sink_x_0_0_0_0_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>1</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_float_to_complex_1_0</source_block_id>
+ <sink_block_id>qtgui_time_sink_x_0_0_0_0_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>2</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_float_to_complex_3</source_block_id>
+ <sink_block_id>blocks_multiply_const_vxx_1</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_multiply_const_vxx_0</source_block_id>
+ <sink_block_id>blocks_add_xx_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>4</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_multiply_const_vxx_0_0</source_block_id>
+ <sink_block_id>blocks_add_xx_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>3</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_multiply_const_vxx_0_0_0</source_block_id>
+ <sink_block_id>blocks_add_xx_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>2</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_multiply_const_vxx_0_0_0_0</source_block_id>
+ <sink_block_id>blocks_add_xx_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>1</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_multiply_const_vxx_0_0_0_0_0</source_block_id>
+ <sink_block_id>blocks_add_xx_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_multiply_const_vxx_1</source_block_id>
+ <sink_block_id>digital_symbol_sync_xx_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_multiply_const_vxx_1</source_block_id>
+ <sink_block_id>qtgui_time_sink_x_0_1_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_repeat_0</source_block_id>
+ <sink_block_id>rational_resampler_xxx_0_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_short_to_float_0</source_block_id>
+ <sink_block_id>blocks_float_to_char_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_throttle_0</source_block_id>
+ <sink_block_id>blocks_short_to_float_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_vector_source_x_0_0</source_block_id>
+ <sink_block_id>blocks_multiply_const_vxx_0_0_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_vector_source_x_0_0_0</source_block_id>
+ <sink_block_id>blocks_multiply_const_vxx_0_0_0_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_vector_source_x_0_0_1</source_block_id>
+ <sink_block_id>blocks_multiply_const_vxx_0_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_vector_source_x_0_0_1_0</source_block_id>
+ <sink_block_id>blocks_multiply_const_vxx_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>digital_map_bb_0</source_block_id>
+ <sink_block_id>blocks_char_to_float_2</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>digital_symbol_sync_xx_0</source_block_id>
+ <sink_block_id>blocks_float_to_complex_1_0</sink_block_id>
+ <source_key>3</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>digital_symbol_sync_xx_0</source_block_id>
+ <sink_block_id>blocks_float_to_complex_0_0</sink_block_id>
+ <source_key>2</source_key>
+ <sink_key>1</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>digital_symbol_sync_xx_0</source_block_id>
+ <sink_block_id>blocks_float_to_complex_0_0</sink_block_id>
+ <source_key>1</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>digital_symbol_sync_xx_0</source_block_id>
+ <sink_block_id>qtgui_time_sink_x_0_0_0_0_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>fir_filter_xxx_0_1_0_0_0</source_block_id>
+ <sink_block_id>blocks_float_to_complex_3</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>rational_resampler_xxx_0_0</source_block_id>
+ <sink_block_id>fir_filter_xxx_0_1_0_0_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+</flow_graph>
diff --git a/gr-digital/examples/demod/symbol_sync_test_float.grc b/gr-digital/examples/demod/symbol_sync_test_float.grc
new file mode 100644
index 0000000000..f81111b957
--- /dev/null
+++ b/gr-digital/examples/demod/symbol_sync_test_float.grc
@@ -0,0 +1,2636 @@
+<?xml version='1.0' encoding='utf-8'?>
+<?grc format='1' created='3.7.12'?>
+<!--
+ Copyright (C) 2017 Free Software Foundation
+
+ 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.
+-->
+<flow_graph>
+ <timestamp>Mon Jan 12 16:38:01 2015</timestamp>
+ <block>
+ <key>options</key>
+ <param>
+ <key>author</key>
+ <value>Andy Walls</value>
+ </param>
+ <param>
+ <key>window_size</key>
+ <value>2600, 700</value>
+ </param>
+ <param>
+ <key>category</key>
+ <value>Custom</value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>description</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(10, 10)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>generate_options</key>
+ <value>qt_gui</value>
+ </param>
+ <param>
+ <key>hier_block_src_path</key>
+ <value>.:</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>symbol_sync_test_float</value>
+ </param>
+ <param>
+ <key>max_nouts</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>qt_qss_theme</key>
+ <value></value>
+ </param>
+ <param>
+ <key>realtime_scheduling</key>
+ <value></value>
+ </param>
+ <param>
+ <key>run_command</key>
+ <value>{python} -u {filename}</value>
+ </param>
+ <param>
+ <key>run_options</key>
+ <value>prompt</value>
+ </param>
+ <param>
+ <key>run</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>thread_safe_setters</key>
+ <value></value>
+ </param>
+ <param>
+ <key>title</key>
+ <value>Symbol Sync Test (Float)</value>
+ </param>
+ </block>
+ <block>
+ <key>variable</key>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(552, 244)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>baud_rate</value>
+ </param>
+ <param>
+ <key>value</key>
+ <value>1200.0</value>
+ </param>
+ </block>
+ <block>
+ <key>variable_qtgui_chooser</key>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>value</key>
+ <value>4</value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(392, 48)</value>
+ </param>
+ <param>
+ <key>gui_hint</key>
+ <value>0,0,1,1</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>data_src</value>
+ </param>
+ <param>
+ <key>label0</key>
+ <value>Random</value>
+ </param>
+ <param>
+ <key>label1</key>
+ <value>Low freq</value>
+ </param>
+ <param>
+ <key>label2</key>
+ <value>Dot Pattern</value>
+ </param>
+ <param>
+ <key>label3</key>
+ <value>Pulse</value>
+ </param>
+ <param>
+ <key>label4</key>
+ <value>Packets</value>
+ </param>
+ <param>
+ <key>label</key>
+ <value></value>
+ </param>
+ <param>
+ <key>labels</key>
+ <value>[]</value>
+ </param>
+ <param>
+ <key>num_opts</key>
+ <value>5</value>
+ </param>
+ <param>
+ <key>option0</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>option1</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>option2</key>
+ <value>2</value>
+ </param>
+ <param>
+ <key>option3</key>
+ <value>3</value>
+ </param>
+ <param>
+ <key>option4</key>
+ <value>4</value>
+ </param>
+ <param>
+ <key>options</key>
+ <value>[0, 1, 2]</value>
+ </param>
+ <param>
+ <key>orient</key>
+ <value>Qt.QVBoxLayout</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>int</value>
+ </param>
+ <param>
+ <key>widget</key>
+ <value>combo_box</value>
+ </param>
+ </block>
+ <block>
+ <key>variable</key>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(1800, 484)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>gain_mu</value>
+ </param>
+ <param>
+ <key>value</key>
+ <value>2.0*math.exp(-zeta*omega_n_norm)*math.sinh(zeta*omega_n_norm)</value>
+ </param>
+ </block>
+ <block>
+ <key>variable_qtgui_label</key>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>value</key>
+ <value>"%8.6f" % gain_mu</value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>formatter</key>
+ <value>None</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(2168, 500)</value>
+ </param>
+ <param>
+ <key>gui_hint</key>
+ <value>1,2,1,1</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>gain_mu_label</value>
+ </param>
+ <param>
+ <key>label</key>
+ <value>Gain Mu</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>string</value>
+ </param>
+ </block>
+ <block>
+ <key>variable</key>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(1800, 548)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>gain_omega</value>
+ </param>
+ <param>
+ <key>value</key>
+ <value>2.0-gain_mu-2.0*math.exp(-zeta*omega_n_norm)*(math.cosh(omega_d_norm) if zeta &gt; 1.0 else math.cos(omega_d_norm))</value>
+ </param>
+ </block>
+ <block>
+ <key>variable_qtgui_label</key>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>value</key>
+ <value>"%8.6f" % gain_omega</value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>formatter</key>
+ <value>None</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(2168, 420)</value>
+ </param>
+ <param>
+ <key>gui_hint</key>
+ <value>0,2,1,1</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>gain_omega_label</value>
+ </param>
+ <param>
+ <key>label</key>
+ <value>Gain Omega</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>string</value>
+ </param>
+ </block>
+ <block>
+ <key>variable</key>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(1800, 420)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>omega_d_norm</value>
+ </param>
+ <param>
+ <key>value</key>
+ <value>omega_n_norm*math.sqrt((zeta*zeta-1.0) if zeta &gt; 1.0 else (1.0-zeta*zeta))</value>
+ </param>
+ </block>
+ <block>
+ <key>variable_qtgui_range</key>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>value</key>
+ <value>0.58</value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(1944, 416)</value>
+ </param>
+ <param>
+ <key>gui_hint</key>
+ <value>1,1,1,1</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>omega_n_norm</value>
+ </param>
+ <param>
+ <key>label</key>
+ <value>Normalized Bandwidth</value>
+ </param>
+ <param>
+ <key>min_len</key>
+ <value>200</value>
+ </param>
+ <param>
+ <key>orient</key>
+ <value>Qt.Horizontal</value>
+ </param>
+ <param>
+ <key>start</key>
+ <value>0.0</value>
+ </param>
+ <param>
+ <key>step</key>
+ <value>0.001</value>
+ </param>
+ <param>
+ <key>stop</key>
+ <value>2.0*math.pi*0.25</value>
+ </param>
+ <param>
+ <key>rangeType</key>
+ <value>float</value>
+ </param>
+ <param>
+ <key>widget</key>
+ <value>counter_slider</value>
+ </param>
+ </block>
+ <block>
+ <key>variable</key>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(2192, 196)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>osps</value>
+ </param>
+ <param>
+ <key>value</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>variable_tag_object</key>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(8, 600)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>packet_time_est_tag</value>
+ </param>
+ <param>
+ <key>key</key>
+ <value>pmt.intern("test")</value>
+ </param>
+ <param>
+ <key>offset</key>
+ <value>9</value>
+ </param>
+ <param>
+ <key>src</key>
+ <value>pmt.intern("packet_vector_source")</value>
+ </param>
+ <param>
+ <key>value</key>
+ <value>pmt.from_double(0.0)</value>
+ </param>
+ </block>
+ <block>
+ <key>variable</key>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(1328, 228)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>sps</value>
+ </param>
+ <param>
+ <key>value</key>
+ <value>7</value>
+ </param>
+ </block>
+ <block>
+ <key>variable_qtgui_range</key>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>value</key>
+ <value>1.0/math.sqrt(2.0)*0+1.5</value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(1944, 536)</value>
+ </param>
+ <param>
+ <key>gui_hint</key>
+ <value>0,1,1,1</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>zeta</value>
+ </param>
+ <param>
+ <key>label</key>
+ <value>Damping Factor</value>
+ </param>
+ <param>
+ <key>min_len</key>
+ <value>200</value>
+ </param>
+ <param>
+ <key>orient</key>
+ <value>Qt.Horizontal</value>
+ </param>
+ <param>
+ <key>start</key>
+ <value>0.1</value>
+ </param>
+ <param>
+ <key>step</key>
+ <value>0.1</value>
+ </param>
+ <param>
+ <key>stop</key>
+ <value>5.0</value>
+ </param>
+ <param>
+ <key>rangeType</key>
+ <value>float</value>
+ </param>
+ <param>
+ <key>widget</key>
+ <value>counter_slider</value>
+ </param>
+ </block>
+ <block>
+ <key>analog_random_source_x</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(8, 192)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>analog_random_source_x_0</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>max</key>
+ <value>2</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>min</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>num_samps</key>
+ <value>16384</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>short</value>
+ </param>
+ <param>
+ <key>repeat</key>
+ <value>True</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_add_xx</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(448, 264)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_add_xx_0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>short</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>num_inputs</key>
+ <value>5</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_char_to_float</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(1160, 324)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_char_to_float_2</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>scale</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_float_to_char</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(872, 324)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_float_to_char_0</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>scale</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_multiply_const_vxx</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>const</key>
+ <value>1 if data_src == 4 else 0</value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(224, 540)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_multiply_const_vxx_0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>short</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_multiply_const_vxx</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>const</key>
+ <value>1 if data_src == 3 else 0</value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(216, 460)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_multiply_const_vxx_0_0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>short</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_multiply_const_vxx</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>const</key>
+ <value>1 if data_src == 2 else 0</value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(216, 380)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_multiply_const_vxx_0_0_0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>short</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_multiply_const_vxx</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>const</key>
+ <value>1 if data_src == 1 else 0</value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(216, 308)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_multiply_const_vxx_0_0_0_0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>short</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_multiply_const_vxx</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>const</key>
+ <value>1 if data_src == 0 else 0</value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(208, 212)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_multiply_const_vxx_0_0_0_0_0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>short</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_repeat</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(1312, 324)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_repeat_0</value>
+ </param>
+ <param>
+ <key>interp</key>
+ <value>sps*2</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>float</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_short_to_float</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(712, 324)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_short_to_float_0</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>scale</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_throttle</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(544, 324)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_throttle_0</value>
+ </param>
+ <param>
+ <key>ignoretag</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>samples_per_second</key>
+ <value>baud_rate*10</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>short</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_vector_source_x</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(8, 364)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_vector_source_x_0_0</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>short</value>
+ </param>
+ <param>
+ <key>repeat</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>tags</key>
+ <value>[]</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>vector</key>
+ <value>(0, 1, 0, 1, 0, 1, 0, 1)</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_vector_source_x</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(8, 284)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_vector_source_x_0_0_0</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>short</value>
+ </param>
+ <param>
+ <key>repeat</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>tags</key>
+ <value>[]</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>vector</key>
+ <value>(0,0,0,0,1,1,1,1)</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_vector_source_x</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(8, 444)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_vector_source_x_0_0_1</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>short</value>
+ </param>
+ <param>
+ <key>repeat</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>tags</key>
+ <value>[]</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>vector</key>
+ <value>[1]+[0]*7</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_vector_source_x</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(8, 524)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_vector_source_x_0_0_1_0</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>short</value>
+ </param>
+ <param>
+ <key>repeat</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>tags</key>
+ <value>[packet_time_est_tag]</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>vector</key>
+ <value>[1,0]*(4*12*0)+[1,1,0,1,0,1,0,1]*12+[1,0,1,1,1,1,1,0,0,1]+[1,1,1,1,0,1,1,0,0,1]+[1,0,1,1,1,1,1,0,0,1]+[0,1,1,1,0,1,1,0,1,0]+[0,0,0,0,0,1,0,1,0,1,1,0,0,1,1,1,0,0,0,0]+[2]*128</value>
+ </param>
+ </block>
+ <block>
+ <key>digital_map_bb</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(1024, 324)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>digital_map_bb_0</value>
+ </param>
+ <param>
+ <key>map</key>
+ <value>[-1, 1,0]</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ </block>
+ <block>
+ <key>digital_symbol_sync_xx</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>damping</key>
+ <value>zeta</value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>nfilters</key>
+ <value>128</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(1944, 280)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>ff</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>digital_symbol_sync_xx_0</value>
+ </param>
+ <param>
+ <key>resamp_type</key>
+ <value>digital.IR_MMSE_8TAP</value>
+ </param>
+ <param>
+ <key>loop_bw</key>
+ <value>omega_n_norm</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>max_dev</key>
+ <value>1.5</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>osps</key>
+ <value>osps</value>
+ </param>
+ <param>
+ <key>pfb_mf_taps</key>
+ <value>[]</value>
+ </param>
+ <param>
+ <key>sps</key>
+ <value>sps</value>
+ </param>
+ <param>
+ <key>constellation</key>
+ <value>digital.constellation_bpsk().base()</value>
+ </param>
+ <param>
+ <key>ted_type</key>
+ <value>digital.TED_MUELLER_AND_MULLER</value>
+ </param>
+ </block>
+ <block>
+ <key>fir_filter_xxx</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>decim</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(1672, 316)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>fir_filter_xxx_0_1_0_0_0</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>samp_delay</key>
+ <value>int((sps-1.0)/2.0)+4</value>
+ </param>
+ <param>
+ <key>taps</key>
+ <value>[1.0/float(sps)]*sps</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>fff</value>
+ </param>
+ </block>
+ <block>
+ <key>import</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(184, 12)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>import_0</value>
+ </param>
+ <param>
+ <key>import</key>
+ <value>import math</value>
+ </param>
+ </block>
+ <block>
+ <key>import</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(184, 60)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>import_0_0</value>
+ </param>
+ <param>
+ <key>import</key>
+ <value>from gnuradio import digital</value>
+ </param>
+ </block>
+ <block>
+ <key>qtgui_time_sink_x</key>
+ <param>
+ <key>autoscale</key>
+ <value>False</value>
+ </param>
+ <param>
+ <key>axislabels</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>ctrlpanel</key>
+ <value>False</value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>entags</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(2296, 280)</value>
+ </param>
+ <param>
+ <key>gui_hint</key>
+ <value>3,1,1,2</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>grid</key>
+ <value>False</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>qtgui_time_sink_x_0_0_0_0_0</value>
+ </param>
+ <param>
+ <key>legend</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>alpha1</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color1</key>
+ <value>"blue"</value>
+ </param>
+ <param>
+ <key>label1</key>
+ <value>Soft Bits</value>
+ </param>
+ <param>
+ <key>marker1</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>style1</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width1</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha10</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color10</key>
+ <value>"blue"</value>
+ </param>
+ <param>
+ <key>label10</key>
+ <value></value>
+ </param>
+ <param>
+ <key>marker10</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style10</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width10</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha2</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color2</key>
+ <value>"red"</value>
+ </param>
+ <param>
+ <key>label2</key>
+ <value>Error</value>
+ </param>
+ <param>
+ <key>marker2</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style2</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width2</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha3</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color3</key>
+ <value>"green"</value>
+ </param>
+ <param>
+ <key>label3</key>
+ <value>Instantaneous Period</value>
+ </param>
+ <param>
+ <key>marker3</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style3</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width3</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha4</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color4</key>
+ <value>"black"</value>
+ </param>
+ <param>
+ <key>label4</key>
+ <value>Average Period</value>
+ </param>
+ <param>
+ <key>marker4</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style4</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width4</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha5</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color5</key>
+ <value>"cyan"</value>
+ </param>
+ <param>
+ <key>label5</key>
+ <value></value>
+ </param>
+ <param>
+ <key>marker5</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style5</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width5</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha6</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color6</key>
+ <value>"magenta"</value>
+ </param>
+ <param>
+ <key>label6</key>
+ <value></value>
+ </param>
+ <param>
+ <key>marker6</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style6</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width6</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha7</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color7</key>
+ <value>"yellow"</value>
+ </param>
+ <param>
+ <key>label7</key>
+ <value></value>
+ </param>
+ <param>
+ <key>marker7</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style7</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width7</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha8</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color8</key>
+ <value>"dark red"</value>
+ </param>
+ <param>
+ <key>label8</key>
+ <value></value>
+ </param>
+ <param>
+ <key>marker8</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style8</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width8</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha9</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color9</key>
+ <value>"dark green"</value>
+ </param>
+ <param>
+ <key>label9</key>
+ <value></value>
+ </param>
+ <param>
+ <key>marker9</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style9</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width9</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>name</key>
+ <value>Symbol Synched Output and Debug</value>
+ </param>
+ <param>
+ <key>nconnections</key>
+ <value>4</value>
+ </param>
+ <param>
+ <key>size</key>
+ <value>256*osps</value>
+ </param>
+ <param>
+ <key>srate</key>
+ <value>baud_rate*osps</value>
+ </param>
+ <param>
+ <key>tr_chan</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>tr_delay</key>
+ <value>0.01</value>
+ </param>
+ <param>
+ <key>tr_level</key>
+ <value>0.1</value>
+ </param>
+ <param>
+ <key>tr_mode</key>
+ <value>qtgui.TRIG_MODE_NORM</value>
+ </param>
+ <param>
+ <key>tr_slope</key>
+ <value>qtgui.TRIG_SLOPE_POS</value>
+ </param>
+ <param>
+ <key>tr_tag</key>
+ <value>"time_est"</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>float</value>
+ </param>
+ <param>
+ <key>update_time</key>
+ <value>0.1</value>
+ </param>
+ <param>
+ <key>ylabel</key>
+ <value>Amplitude</value>
+ </param>
+ <param>
+ <key>yunit</key>
+ <value>""</value>
+ </param>
+ <param>
+ <key>ymax</key>
+ <value>sps+2</value>
+ </param>
+ <param>
+ <key>ymin</key>
+ <value>-1.5</value>
+ </param>
+ </block>
+ <block>
+ <key>qtgui_time_sink_x</key>
+ <param>
+ <key>autoscale</key>
+ <value>False</value>
+ </param>
+ <param>
+ <key>axislabels</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>ctrlpanel</key>
+ <value>False</value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>entags</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(1976, 172)</value>
+ </param>
+ <param>
+ <key>gui_hint</key>
+ <value>3,0,1,1</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>grid</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>qtgui_time_sink_x_0_1_0</value>
+ </param>
+ <param>
+ <key>legend</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>alpha1</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color1</key>
+ <value>"blue"</value>
+ </param>
+ <param>
+ <key>label1</key>
+ <value></value>
+ </param>
+ <param>
+ <key>marker1</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style1</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width1</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha10</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color10</key>
+ <value>"blue"</value>
+ </param>
+ <param>
+ <key>label10</key>
+ <value></value>
+ </param>
+ <param>
+ <key>marker10</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style10</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width10</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha2</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color2</key>
+ <value>"red"</value>
+ </param>
+ <param>
+ <key>label2</key>
+ <value></value>
+ </param>
+ <param>
+ <key>marker2</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style2</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width2</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha3</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color3</key>
+ <value>"green"</value>
+ </param>
+ <param>
+ <key>label3</key>
+ <value></value>
+ </param>
+ <param>
+ <key>marker3</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style3</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width3</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha4</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color4</key>
+ <value>"black"</value>
+ </param>
+ <param>
+ <key>label4</key>
+ <value></value>
+ </param>
+ <param>
+ <key>marker4</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style4</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width4</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha5</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color5</key>
+ <value>"dark green"</value>
+ </param>
+ <param>
+ <key>label5</key>
+ <value>Baseband</value>
+ </param>
+ <param>
+ <key>marker5</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style5</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width5</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha6</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color6</key>
+ <value>"magenta"</value>
+ </param>
+ <param>
+ <key>label6</key>
+ <value>Abs(Corr)</value>
+ </param>
+ <param>
+ <key>marker6</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style6</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width6</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha7</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color7</key>
+ <value>"yellow"</value>
+ </param>
+ <param>
+ <key>label7</key>
+ <value></value>
+ </param>
+ <param>
+ <key>marker7</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style7</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width7</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha8</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color8</key>
+ <value>"dark red"</value>
+ </param>
+ <param>
+ <key>label8</key>
+ <value></value>
+ </param>
+ <param>
+ <key>marker8</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style8</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width8</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>alpha9</key>
+ <value>1.0</value>
+ </param>
+ <param>
+ <key>color9</key>
+ <value>"dark green"</value>
+ </param>
+ <param>
+ <key>label9</key>
+ <value></value>
+ </param>
+ <param>
+ <key>marker9</key>
+ <value>-1</value>
+ </param>
+ <param>
+ <key>style9</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>width9</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>name</key>
+ <value>""</value>
+ </param>
+ <param>
+ <key>nconnections</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>size</key>
+ <value>1024*3</value>
+ </param>
+ <param>
+ <key>srate</key>
+ <value>baud_rate*sps</value>
+ </param>
+ <param>
+ <key>tr_chan</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>tr_delay</key>
+ <value>0.01</value>
+ </param>
+ <param>
+ <key>tr_level</key>
+ <value>0.1</value>
+ </param>
+ <param>
+ <key>tr_mode</key>
+ <value>qtgui.TRIG_MODE_NORM</value>
+ </param>
+ <param>
+ <key>tr_slope</key>
+ <value>qtgui.TRIG_SLOPE_POS</value>
+ </param>
+ <param>
+ <key>tr_tag</key>
+ <value></value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>float</value>
+ </param>
+ <param>
+ <key>update_time</key>
+ <value>0.10</value>
+ </param>
+ <param>
+ <key>ylabel</key>
+ <value>Amplitude</value>
+ </param>
+ <param>
+ <key>yunit</key>
+ <value>""</value>
+ </param>
+ <param>
+ <key>ymax</key>
+ <value>1.5</value>
+ </param>
+ <param>
+ <key>ymin</key>
+ <value>-1.5</value>
+ </param>
+ </block>
+ <block>
+ <key>rational_resampler_xxx</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>decim</key>
+ <value>21</value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>fbw</key>
+ <value>0.45</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(1472, 304)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>rational_resampler_xxx_0</value>
+ </param>
+ <param>
+ <key>interp</key>
+ <value>10</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>taps</key>
+ <value></value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>fff</value>
+ </param>
+ </block>
+ <connection>
+ <source_block_id>analog_random_source_x_0</source_block_id>
+ <sink_block_id>blocks_multiply_const_vxx_0_0_0_0_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_add_xx_0</source_block_id>
+ <sink_block_id>blocks_throttle_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_char_to_float_2</source_block_id>
+ <sink_block_id>blocks_repeat_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_float_to_char_0</source_block_id>
+ <sink_block_id>digital_map_bb_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_multiply_const_vxx_0</source_block_id>
+ <sink_block_id>blocks_add_xx_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>4</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_multiply_const_vxx_0_0</source_block_id>
+ <sink_block_id>blocks_add_xx_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>3</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_multiply_const_vxx_0_0_0</source_block_id>
+ <sink_block_id>blocks_add_xx_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>2</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_multiply_const_vxx_0_0_0_0</source_block_id>
+ <sink_block_id>blocks_add_xx_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>1</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_multiply_const_vxx_0_0_0_0_0</source_block_id>
+ <sink_block_id>blocks_add_xx_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_repeat_0</source_block_id>
+ <sink_block_id>rational_resampler_xxx_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_short_to_float_0</source_block_id>
+ <sink_block_id>blocks_float_to_char_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_throttle_0</source_block_id>
+ <sink_block_id>blocks_short_to_float_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_vector_source_x_0_0</source_block_id>
+ <sink_block_id>blocks_multiply_const_vxx_0_0_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_vector_source_x_0_0_0</source_block_id>
+ <sink_block_id>blocks_multiply_const_vxx_0_0_0_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_vector_source_x_0_0_1</source_block_id>
+ <sink_block_id>blocks_multiply_const_vxx_0_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_vector_source_x_0_0_1_0</source_block_id>
+ <sink_block_id>blocks_multiply_const_vxx_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>digital_map_bb_0</source_block_id>
+ <sink_block_id>blocks_char_to_float_2</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>digital_symbol_sync_xx_0</source_block_id>
+ <sink_block_id>qtgui_time_sink_x_0_0_0_0_0</sink_block_id>
+ <source_key>3</source_key>
+ <sink_key>3</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>digital_symbol_sync_xx_0</source_block_id>
+ <sink_block_id>qtgui_time_sink_x_0_0_0_0_0</sink_block_id>
+ <source_key>2</source_key>
+ <sink_key>2</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>digital_symbol_sync_xx_0</source_block_id>
+ <sink_block_id>qtgui_time_sink_x_0_0_0_0_0</sink_block_id>
+ <source_key>1</source_key>
+ <sink_key>1</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>digital_symbol_sync_xx_0</source_block_id>
+ <sink_block_id>qtgui_time_sink_x_0_0_0_0_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>fir_filter_xxx_0_1_0_0_0</source_block_id>
+ <sink_block_id>digital_symbol_sync_xx_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>fir_filter_xxx_0_1_0_0_0</source_block_id>
+ <sink_block_id>qtgui_time_sink_x_0_1_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>rational_resampler_xxx_0</source_block_id>
+ <sink_block_id>fir_filter_xxx_0_1_0_0_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+</flow_graph>
diff --git a/gr-digital/grc/digital_block_tree.xml b/gr-digital/grc/digital_block_tree.xml
index 88da4b7621..10346cbcd0 100644
--- a/gr-digital/grc/digital_block_tree.xml
+++ b/gr-digital/grc/digital_block_tree.xml
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<!--
- Copyright 2011-2016 Free Software Foundation, Inc.
+ Copyright (C) 2011-2017 Free Software Foundation, Inc.
This file is part of GNU Radio
@@ -118,6 +118,7 @@
<block>digital_fll_band_edge_cc</block>
<block>digital_pfb_clock_sync_xxx</block>
<block>digital_pn_correlator_cc</block>
+ <block>digital_symbol_sync_xx</block>
<block>digital_corr_est_cc</block>
</cat>
<cat>
diff --git a/gr-digital/grc/digital_symbol_sync_xx.xml b/gr-digital/grc/digital_symbol_sync_xx.xml
new file mode 100644
index 0000000000..1f597ef8ce
--- /dev/null
+++ b/gr-digital/grc/digital_symbol_sync_xx.xml
@@ -0,0 +1,200 @@
+<?xml version="1.0"?>
+<!--
+ Copyright (C) 2016-2017 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.
+ -->
+
+<block>
+ <name>Symbol Sync</name>
+ <key>digital_symbol_sync_xx</key>
+ <category>[Core]/Synchronizers</category>
+ <import>from gnuradio import digital</import>
+ <import>from gnuradio import filter</import>
+ <make>digital.symbol_sync_$(type)($ted_type, $sps, $loop_bw, $damping, $max_dev, $osps, $constellation, $resamp_type, $nfilters, $pfb_mf_taps)</make>
+
+ <callback>set_loop_bandwidth($loop_bw)</callback>
+ <callback>set_damping_factor($damping)</callback>
+
+ <param>
+ <name>I/O Type</name>
+ <key>type</key>
+ <type>enum</type>
+ <option>
+ <name>Complex</name>
+ <key>cc</key>
+ <opt>input:complex</opt>
+ <opt>output:complex</opt>
+ </option>
+ <option>
+ <name>Float</name>
+ <key>ff</key>
+ <opt>input:float</opt>
+ <opt>output:float</opt>
+ </option>
+ </param>
+ <param>
+ <name>Timing Error Detector</name>
+ <key>ted_type</key>
+ <type>enum</type>
+ <option>
+ <name>Mueller and Müller</name>
+ <key>digital.TED_MUELLER_AND_MULLER</key>
+ <opt>hide_constellation:part</opt>
+ </option>
+ <option>
+ <name>Modified Mueller and Müller</name>
+ <key>digital.TED_MOD_MUELLER_AND_MULLER</key>
+ <opt>hide_constellation:part</opt>
+ </option>
+ <option>
+ <name>Zero Crossing</name>
+ <key>digital.TED_ZERO_CROSSING</key>
+ <opt>hide_constellation:part</opt>
+ </option>
+ <option>
+ <name>Gardner</name>
+ <key>digital.TED_GARDNER</key>
+ <opt>hide_constellation:all</opt>
+ </option>
+ <option>
+ <name>Early-Late</name>
+ <key>digital.TED_EARLY_LATE</key>
+ <opt>hide_constellation:all</opt>
+ </option>
+ <option>
+ <name>D'Andrea and Mengali Gen MSK</name>
+ <key>digital.TED_DANDREA_AND_MENGALI_GEN_MSK</key>
+ <opt>hide_constellation:all</opt>
+ </option>
+ <option>
+ <name>Mengali and D'Andrea GMSK</name>
+ <key>digital.TED_MENGALI_AND_DANDREA_GMSK</key>
+ <opt>hide_constellation:all</opt>
+ </option>
+ <option>
+ <name>y[n]y'[n] Maximum Likelyhood</name>
+ <key>digital.TED_SIGNAL_TIMES_SLOPE_ML</key>
+ <opt>hide_constellation:all</opt>
+ </option>
+ <option>
+ <name>sgn(y[n])y'[n] Maximum Likelyhood</name>
+ <key>digital.TED_SIGNUM_TIMES_SLOPE_ML</key>
+ <opt>hide_constellation:all</opt>
+ </option>
+ </param>
+ <param>
+ <name>TED Slicer Constellation</name>
+ <key>constellation</key>
+ <value>digital.constellation_bpsk().base()</value>
+ <type>raw</type>
+ <hide>$ted_type.hide_constellation</hide>
+ </param>
+ <param>
+ <name>Samples per Symbol</name>
+ <key>sps</key>
+ <value>sps</value>
+ <type>real</type>
+ </param>
+ <param>
+ <name>Loop Bandwidth</name>
+ <key>loop_bw</key>
+ <value>0.045</value>
+ <type>real</type>
+ </param>
+ <param>
+ <name>Damping Factor</name>
+ <key>damping</key>
+ <value>1.0/math.sqrt(2.0)</value>
+ <type>real</type>
+ </param>
+ <param>
+ <name>Maximum Deviation</name>
+ <key>max_dev</key>
+ <value>1.5</value>
+ <type>real</type>
+ </param>
+ <param>
+ <name>Output Samples/Symbol</name>
+ <key>osps</key>
+ <value>1</value>
+ <type>int</type>
+ </param>
+ <param>
+ <name>Interpolating Resampler</name>
+ <key>resamp_type</key>
+ <type>enum</type>
+ <option>
+ <name>MMSE, 8 tap FIR</name>
+ <key>digital.IR_MMSE_8TAP</key>
+ <opt>hide_nfilters:all</opt>
+ <opt>hide_pfb_mf_taps:all</opt>
+ </option>
+ <option>
+ <name>Polyphase Filterbank, MMSE</name>
+ <key>digital.IR_PFB_NO_MF</key>
+ <opt>hide_nfilters:</opt>
+ <opt>hide_pfb_mf_taps:all</opt>
+ </option>
+ <option>
+ <name>Polyphase Filterbank, MF</name>
+ <key>digital.IR_PFB_MF</key>
+ <opt>hide_nfilters:</opt>
+ <opt>hide_pfb_mf_taps:</opt>
+ </option>
+ </param>
+ <param>
+ <name>Filterbank Arms</name>
+ <key>nfilters</key>
+ <value>128</value>
+ <type>int</type>
+ <hide>$resamp_type.hide_nfilters</hide>
+ </param>
+ <param>
+ <name>PFB MF Taps</name>
+ <key>pfb_mf_taps</key>
+ <value>[]</value>
+ <type>real_vector</type>
+ <hide>$resamp_type.hide_pfb_mf_taps</hide>
+ </param>
+
+ <sink>
+ <name>in</name>
+ <type>$type.input</type>
+ </sink>
+
+ <source>
+ <name>out</name>
+ <type>$type.output</type>
+ </source>
+ <source>
+ <name>error</name>
+ <type>float</type>
+ <optional>1</optional>
+ </source>
+ <source>
+ <name>T_inst</name>
+ <type>float</type>
+ <optional>1</optional>
+ </source>
+ <source>
+ <name>T_avg</name>
+ <type>float</type>
+ <optional>1</optional>
+ </source>
+</block>
diff --git a/gr-digital/include/gnuradio/digital/CMakeLists.txt b/gr-digital/include/gnuradio/digital/CMakeLists.txt
index 9074df47c8..cdaf48080b 100644
--- a/gr-digital/include/gnuradio/digital/CMakeLists.txt
+++ b/gr-digital/include/gnuradio/digital/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright 2011-2016 Free Software Foundation, Inc.
+# Copyright (C) 2011-2017 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
@@ -107,6 +107,10 @@ install(FILES
simple_correlator.h
simple_framer.h
simple_framer_sync.h
+ interpolating_resampler_type.h
+ symbol_sync_cc.h
+ symbol_sync_ff.h
+ timing_error_detector_type.h
header_payload_demux.h
DESTINATION ${GR_INCLUDE_DIR}/gnuradio/digital
)
diff --git a/gr-digital/include/gnuradio/digital/interpolating_resampler_type.h b/gr-digital/include/gnuradio/digital/interpolating_resampler_type.h
new file mode 100644
index 0000000000..78687a545a
--- /dev/null
+++ b/gr-digital/include/gnuradio/digital/interpolating_resampler_type.h
@@ -0,0 +1,40 @@
+/* -*- c++ -*- */
+/*
+ * Copyright (C) 2017 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 file; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_DIGITAL_INTERPOLATING_RESAMPLER_TYPE_H
+#define INCLUDED_DIGITAL_INTERPOLATING_RESAMPLER_TYPE_H
+
+namespace gr {
+ namespace digital {
+
+ // Interpolating Resampler type
+ enum ir_type {
+ IR_NONE = -1,
+ IR_MMSE_8TAP = 0, // Valid for [-Fs/4, Fs/4] bandlimited input
+ IR_PFB_NO_MF = 1, // No matched filtering, just interpolation
+ IR_PFB_MF = 2,
+ };
+
+ } /* namespace digital */
+} /* namespace gr */
+
+#endif /* INCLUDED_DIGITAL_INTERPOLATING_RESAMPLER_TYPE_H */
diff --git a/gr-digital/include/gnuradio/digital/symbol_sync_cc.h b/gr-digital/include/gnuradio/digital/symbol_sync_cc.h
new file mode 100644
index 0000000000..5e51c3cdf1
--- /dev/null
+++ b/gr-digital/include/gnuradio/digital/symbol_sync_cc.h
@@ -0,0 +1,278 @@
+/* -*- c++ -*- */
+/*
+ * Copyright (C) 2017 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_DIGITAL_SYMBOL_SYNC_CC_H
+#define INCLUDED_DIGITAL_SYMBOL_SYNC_CC_H
+
+#include <gnuradio/digital/api.h>
+#include <gnuradio/block.h>
+#include <gnuradio/digital/timing_error_detector_type.h>
+#include <gnuradio/digital/constellation.h>
+#include <gnuradio/digital/interpolating_resampler_type.h>
+
+namespace gr {
+ namespace digital {
+
+ /*!
+ * \brief Symbol Synchronizer block with complex input, complex output.
+ * \ingroup synchronizers_blk
+ *
+ * \details
+ * This implements a discrete-time error-tracking synchronizer.
+ *
+ * For this block to work properly, the input stream must meet the
+ * following requirements:
+ *
+ * 1. if not using the PFB Matched Filter interpolator, and using
+ * a non-CPM timing error detector, the input pulses must have peaks
+ * (not flat), which usually can be implemented by using a matched
+ * filter before this block.
+ *
+ * 2. for decision directed timing error detectors, the input pulses
+ * should nominally match the normalized slicer constellation, which
+ * is normalized to an average symbol magnitude of 1.0 over the entire
+ * constellation.
+ */
+ class DIGITAL_API symbol_sync_cc : virtual public block
+ {
+ public:
+ // gr::digital::symbol_sync_cc::sptr
+ typedef boost::shared_ptr<symbol_sync_cc> sptr;
+
+ /*!
+ * Make a Symbol Synchronizer block.
+ *
+ * \details
+ * This implements a discrete-time error-tracking synchronizer.
+ *
+ * For this block to work properly, the input stream must meet the
+ * following requirements:
+ *
+ * 1. if not using the PFB Matched Filter interpolator, and using
+ * a non-CPM timing error detector, the input pulses must have peaks
+ * (not flat), which usually can be implemented by using a matched
+ * filter before this block.
+ *
+ * 2. for decision directed timing error detectors, the input pulses
+ * should nominally match the normalized slicer constellation, which
+ * is normalized to an average symbol magnitude of 1.0 over the entire
+ * constellation.
+ *
+ * \param detector_type
+ * The enumerated type of timing error detector to use.
+ * See enum ted_type for a list of possible types.
+ *
+ * \param sps
+ * User specified nominal clock period in samples per symbol.
+ *
+ * \param loop_bw
+ * Approximate normailzed loop bandwidth of the symbol clock tracking
+ * loop. It should nominally be close to 0, but greater than 0. If
+ * unsure, start with a number around 2*pi*0.040, and experiment to find
+ * the value that works best for your situation.
+ *
+ * \param damping_factor
+ * Damping factor of the symbol clock tracking loop.
+ * Damping < 1.0f is an under-damped loop.
+ * Damping = 1.0f/sqrt(2.0f) is a maximally flat loop response.
+ * Damping = 1.0f is a critically-damped loop.
+ * Damping > 1.0f is an over-damped loop.
+ *
+ * \param max_deviation
+ * Maximum absolute deviation of the average clock period estimate
+ * from the user specified nominal clock period in samples per symbol.
+ *
+ * \param osps
+ * The number of output samples per symbol (default=1).
+ *
+ * \param slicer
+ * A constellation obj shared pointer that will be used by
+ * decision directed timing error detectors to make decisions.
+ * I.e. the timing error detector will use this constellation
+ * as a slicer, if the particular algorithm needs sliced
+ * symbols.
+ *
+ * \param interp_type
+ * The enumerated type of interpolating resampler to use.
+ * See the interpolating resampler type enum for a list of possible types.
+ *
+ * \param n_filters
+ * The number of arms in the polyphase filterbank of the interpolating
+ * resampler, if using an interpolating resampler that uses a PFB.
+ *
+ * \param taps
+ * The prototype filter for the polyphase filterbank of the interpolating
+ * resampler, if using an interpolating resampler that uses a PFB.
+ */
+ static sptr make(enum ted_type detector_type,
+ float sps,
+ float loop_bw,
+ float damping_factor = 2.0f,
+ float max_deviation = 1.5f,
+ int osps = 1,
+ constellation_sptr slicer = constellation_sptr(),
+ ir_type interp_type = IR_MMSE_8TAP,
+ int n_filters = 128,
+ const std::vector<float> &taps = std::vector<float>());
+
+ /*!
+ * \brief Returns the normalized approximate loop bandwidth.
+ *
+ * \details
+ * See the documenation for set_loop_bandwidth() for more details.
+ *
+ * Note that if set_alpha() or set_beta() were called to directly
+ * set gains, the value returned by this method will be inaccurate/stale.
+ */
+ virtual float loop_bandwidth() const = 0;
+
+ /*!
+ * \brief Returns the loop damping factor.
+ *
+ * \details
+ * See the documenation for set_damping_factor() for more details.
+ *
+ * Note that if set_alpha() or set_beta() were called to directly
+ * set gains, the value returned by this method will be inaccurate/stale.
+ */
+ virtual float damping_factor() const = 0;
+
+ /*!
+ * \brief Returns the PI filter proportional gain, alpha.
+ *
+ * \details
+ * See the documenation for set_alpha() for more details.
+ */
+ virtual float alpha() const = 0;
+
+ /*!
+ * \brief Returns the PI filter integral gain, beta.
+ *
+ * \details
+ * See the documenation for set_beta() for more details.
+ */
+ virtual float beta() const = 0;
+
+ /*!
+ * \brief Set the normalized approximate loop bandwidth.
+ *
+ * \details
+ * Set the normalized approximate loop bandwidth.
+ * Useful values are usually close to 0.0, e.g. 2*pi*0.045.
+ *
+ * It should be a small positive number, corresponding to the normalized
+ * natural radian frequency of the loop as digital low-pass filter that is
+ * filtering the clock phase/timing error.
+ *
+ * Technically this parameter corresponds to the natural radian frequency
+ * of the 2nd order loop transfer function (scaled by Fs),
+ * which is the radius of the pole locations in the s-plane of an
+ * underdamped analog 2nd order system.
+ *
+ * The input parameter corresponds to omega_n_norm in the following
+ * relation:
+ *
+ * omega_n_norm = omega_n*T = 2*pi*f_n*T = 2*pi*f_n_norm
+ *
+ * where T is the period of the clock being estimated by this
+ * clock tracking loop, and omega_n is the natural radian frequency
+ * of the 2nd order loop transfer function.
+ *
+ * When a new loop bandwidth is set, the gains, alpha and beta,
+ * of the loop are automatically recalculated.
+ *
+ * \param omega_n_norm normalized approximate loop bandwidth
+ */
+ virtual void set_loop_bandwidth (float omega_n_norm) = 0;
+
+ /*!
+ * \brief Set the loop damping factor.
+ *
+ * \details
+ * Set the damping factor of the loop.
+ * Damping in the range (0.0, 1.0) yields an under-damped loop.
+ * Damping in the range (1.0, Inf) yields an over-damped loop.
+ * Damping equal to 1.0 yields a crtically-damped loop.
+ * Damping equal to 1.0/sqrt(2.0) yields a maximally flat
+ * loop filter response.
+ *
+ * Damping factor of the 2nd order loop transfer function.
+ * When a new damping factor is set, the gains, alpha and beta,
+ * of the loop are automatcally recalculated.
+ *
+ * \param zeta loop damping factor
+ */
+ virtual void set_damping_factor (float zeta) = 0;
+
+ /*!
+ * \brief Set the PI filter proportional gain, alpha.
+ *
+ * \details
+ * Sets the PI filter proportional gain, alpha.
+ * This gain directly mutliplies the clock phase/timing error
+ * term in the PI filter when advancing the loop.
+ * It most directly affects the instantaneous clock period estimate,
+ * T_inst, and instantaneous clock phase estimate, tau.
+ *
+ * This value would normally be adjusted by setting the loop
+ * bandwidth and damping factor. However,
+ * it can be set here directly if desired.
+ *
+ * Setting this parameter directly is probably only feasible if
+ * the user is directly observing the estimates of average clock
+ * period and instantaneous clock period over time in response to
+ * an impulsive change in the input stream (i.e. watching the loop
+ * transient behavior at the start of a data burst).
+ *
+ * \param alpha PI filter proportional gain
+ */
+ virtual void set_alpha (float alpha) = 0;
+
+ /*!
+ * \brief Set the PI filter integral gain, beta.
+ *
+ * \details
+ * Sets the PI filter integral gain, beta.
+ * This gain is used when integrating the clock phase/timing error
+ * term in the PI filter when advancing the loop.
+ * It most directly affects the average clock period estimate,
+ * T_avg.
+ *
+ * This value would normally be adjusted by setting the loop
+ * bandwidth and damping factor. However,
+ * it can be set here directly if desired.
+ *
+ * Setting this parameter directly is probably only feasible if
+ * the user is directly observing the estimates of average clock
+ * period and instantaneous clock period over time in response to
+ * an impulsive change in the input stream (i.e. watching the loop
+ * transient behavior at the start of a data burst).
+ *
+ * \param beta PI filter integral gain
+ */
+ virtual void set_beta (float beta) = 0;
+ };
+
+ } /* namespace digital */
+} /* namespace gr */
+
+#endif /* INCLUDED_DIGITAL_SYMBOL_SYNC_CC_H */
diff --git a/gr-digital/include/gnuradio/digital/symbol_sync_ff.h b/gr-digital/include/gnuradio/digital/symbol_sync_ff.h
new file mode 100644
index 0000000000..2fdac08e2d
--- /dev/null
+++ b/gr-digital/include/gnuradio/digital/symbol_sync_ff.h
@@ -0,0 +1,278 @@
+/* -*- c++ -*- */
+/*
+ * Copyright (C) 2017 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_DIGITAL_SYMBOL_SYNC_FF_H
+#define INCLUDED_DIGITAL_SYMBOL_SYNC_FF_H
+
+#include <gnuradio/digital/api.h>
+#include <gnuradio/block.h>
+#include <gnuradio/digital/timing_error_detector_type.h>
+#include <gnuradio/digital/constellation.h>
+#include <gnuradio/digital/interpolating_resampler_type.h>
+
+namespace gr {
+ namespace digital {
+
+ /*!
+ * \brief Symbol Synchronizer block with float input, float output.
+ * \ingroup synchronizers_blk
+ *
+ * \details
+ * This implements a discrete-time error-tracking synchronizer.
+ *
+ * For this block to work properly, the input stream must meet the
+ * following requirements:
+ *
+ * 1. if not using the PFB Matched Filter interpolator, and using
+ * a non-CPM timing error detector, the input pulses must have peaks
+ * (not flat), which usually can be implemented by using a matched
+ * filter before this block.
+ *
+ * 2. for decision directed timing error detectors, the input pulses
+ * should nominally match the normalized slicer constellation, which
+ * is normalized to an average symbol magnitude of 1.0 over the entire
+ * constellation.
+ */
+ class DIGITAL_API symbol_sync_ff : virtual public block
+ {
+ public:
+ // gr::digital::symbol_sync_ff::sptr
+ typedef boost::shared_ptr<symbol_sync_ff> sptr;
+
+ /*!
+ * Make a Symbol Synchronizer block.
+ *
+ * \details
+ * This implements a discrete-time error-tracking synchronizer.
+ *
+ * For this block to work properly, the input stream must meet the
+ * following requirements:
+ *
+ * 1. if not using the PFB Matched Filter interpolator, and using
+ * a non-CPM timing error detector, the input pulses must have peaks
+ * (not flat), which usually can be implemented by using a matched
+ * filter before this block.
+ *
+ * 2. for decision directed timing error detectors, the input pulses
+ * should nominally match the normalized slicer constellation, which
+ * is normalized to an average symbol magnitude of 1.0 over the entire
+ * constellation.
+ *
+ * \param detector_type
+ * The enumerated type of timing error detector to use.
+ * See enum ted_type for a list of possible types.
+ *
+ * \param sps
+ * User specified nominal clock period in samples per symbol.
+ *
+ * \param loop_bw
+ * Approximate normailzed loop bandwidth of the symbol clock tracking
+ * loop. It should nominally be close to 0, but greater than 0. If
+ * unsure, start with a number around 2*pi*0.040, and experiment to find
+ * the value that works best for your situation.
+ *
+ * \param damping_factor
+ * Damping factor of the symbol clock tracking loop.
+ * Damping < 1.0f is an under-damped loop.
+ * Damping = 1.0f/sqrt(2.0f) is a maximally flat loop response.
+ * Damping = 1.0f is a critically-damped loop.
+ * Damping > 1.0f is an over-damped loop.
+ *
+ * \param max_deviation
+ * Maximum absolute deviation of the average clock period estimate
+ * from the user specified nominal clock period in samples per symbol.
+ *
+ * \param osps
+ * The number of output samples per symbol (default=1).
+ *
+ * \param slicer
+ * A constellation obj shared pointer that will be used by
+ * decision directed timing error detectors to make decisions.
+ * I.e. the timing error detector will use this constellation
+ * as a slicer, if the particular algorithm needs sliced
+ * symbols.
+ *
+ * \param interp_type
+ * The enumerated type of interpolating resampler to use.
+ * See the interpolating resampler type enum for a list of possible types.
+ *
+ * \param n_filters
+ * The number of arms in the polyphase filterbank of the interpolating
+ * resampler, if using an interpolating resampler that uses a PFB.
+ *
+ * \param taps
+ * The prototype filter for the polyphase filterbank of the interpolating
+ * resampler, if using an interpolating resampler that uses a PFB.
+ */
+ static sptr make(enum ted_type detector_type,
+ float sps,
+ float loop_bw,
+ float damping_factor = 2.0f,
+ float max_deviation = 1.5f,
+ int osps = 1,
+ constellation_sptr slicer = constellation_sptr(),
+ ir_type interp_type = IR_MMSE_8TAP,
+ int n_filters = 128,
+ const std::vector<float> &taps = std::vector<float>());
+
+ /*!
+ * \brief Returns the normalized approximate loop bandwidth.
+ *
+ * \details
+ * See the documenation for set_loop_bandwidth() for more details.
+ *
+ * Note that if set_alpha() or set_beta() were called to directly
+ * set gains, the value returned by this method will be inaccurate/stale.
+ */
+ virtual float loop_bandwidth() const = 0;
+
+ /*!
+ * \brief Returns the loop damping factor.
+ *
+ * \details
+ * See the documenation for set_damping_factor() for more details.
+ *
+ * Note that if set_alpha() or set_beta() were called to directly
+ * set gains, the value returned by this method will be inaccurate/stale.
+ */
+ virtual float damping_factor() const = 0;
+
+ /*!
+ * \brief Returns the PI filter proportional gain, alpha.
+ *
+ * \details
+ * See the documenation for set_alpha() for more details.
+ */
+ virtual float alpha() const = 0;
+
+ /*!
+ * \brief Returns the PI filter integral gain, beta.
+ *
+ * \details
+ * See the documenation for set_beta() for more details.
+ */
+ virtual float beta() const = 0;
+
+ /*!
+ * \brief Set the normalized approximate loop bandwidth.
+ *
+ * \details
+ * Set the normalized approximate loop bandwidth.
+ * Useful values are usually close to 0.0, e.g. 2*pi*0.045.
+ *
+ * It should be a small positive number, corresponding to the normalized
+ * natural radian frequency of the loop as digital low-pass filter that is
+ * filtering the clock phase/timing error.
+ *
+ * Technically this parameter corresponds to the natural radian frequency
+ * of the 2nd order loop transfer function (scaled by Fs),
+ * which is the radius of the pole locations in the s-plane of an
+ * underdamped analog 2nd order system.
+ *
+ * The input parameter corresponds to omega_n_norm in the following
+ * relation:
+ *
+ * omega_n_norm = omega_n*T = 2*pi*f_n*T = 2*pi*f_n_norm
+ *
+ * where T is the period of the clock being estimated by this
+ * clock tracking loop, and omega_n is the natural radian frequency
+ * of the 2nd order loop transfer function.
+ *
+ * When a new loop bandwidth is set, the gains, alpha and beta,
+ * of the loop are automatically recalculated.
+ *
+ * \param omega_n_norm normalized approximate loop bandwidth
+ */
+ virtual void set_loop_bandwidth (float omega_n_norm) = 0;
+
+ /*!
+ * \brief Set the loop damping factor.
+ *
+ * \details
+ * Set the damping factor of the loop.
+ * Damping in the range (0.0, 1.0) yields an under-damped loop.
+ * Damping in the range (1.0, Inf) yields an over-damped loop.
+ * Damping equal to 1.0 yields a crtically-damped loop.
+ * Damping equal to 1.0/sqrt(2.0) yields a maximally flat
+ * loop filter response.
+ *
+ * Damping factor of the 2nd order loop transfer function.
+ * When a new damping factor is set, the gains, alpha and beta,
+ * of the loop are automatcally recalculated.
+ *
+ * \param zeta loop damping factor
+ */
+ virtual void set_damping_factor (float zeta) = 0;
+
+ /*!
+ * \brief Set the PI filter proportional gain, alpha.
+ *
+ * \details
+ * Sets the PI filter proportional gain, alpha.
+ * This gain directly mutliplies the clock phase/timing error
+ * term in the PI filter when advancing the loop.
+ * It most directly affects the instantaneous clock period estimate,
+ * T_inst, and instantaneous clock phase estimate, tau.
+ *
+ * This value would normally be adjusted by setting the loop
+ * bandwidth and damping factor. However,
+ * it can be set here directly if desired.
+ *
+ * Setting this parameter directly is probably only feasible if
+ * the user is directly observing the estimates of average clock
+ * period and instantaneous clock period over time in response to
+ * an impulsive change in the input stream (i.e. watching the loop
+ * transient behavior at the start of a data burst).
+ *
+ * \param alpha PI filter proportional gain
+ */
+ virtual void set_alpha (float alpha) = 0;
+
+ /*!
+ * \brief Set the PI filter integral gain, beta.
+ *
+ * \details
+ * Sets the PI filter integral gain, beta.
+ * This gain is used when integrating the clock phase/timing error
+ * term in the PI filter when advancing the loop.
+ * It most directly affects the average clock period estimate,
+ * T_avg.
+ *
+ * This value would normally be adjusted by setting the loop
+ * bandwidth and damping factor. However,
+ * it can be set here directly if desired.
+ *
+ * Setting this parameter directly is probably only feasible if
+ * the user is directly observing the estimates of average clock
+ * period and instantaneous clock period over time in response to
+ * an impulsive change in the input stream (i.e. watching the loop
+ * transient behavior at the start of a data burst).
+ *
+ * \param beta PI filter integral gain
+ */
+ virtual void set_beta (float beta) = 0;
+ };
+
+ } /* namespace digital */
+} /* namespace gr */
+
+#endif /* INCLUDED_DIGITAL_SYMBOL_SYNC_FF_H */
diff --git a/gr-digital/include/gnuradio/digital/timing_error_detector_type.h b/gr-digital/include/gnuradio/digital/timing_error_detector_type.h
new file mode 100644
index 0000000000..e2bc51e80e
--- /dev/null
+++ b/gr-digital/include/gnuradio/digital/timing_error_detector_type.h
@@ -0,0 +1,46 @@
+/* -*- c++ -*- */
+/*
+ * Copyright (C) 2017 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 file; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_DIGITAL_TIMING_ERROR_DETECTOR_TYPE_H
+#define INCLUDED_DIGITAL_TIMING_ERROR_DETECTOR_TYPE_H
+
+namespace gr {
+ namespace digital {
+
+ // Timing Error Detector types.
+ enum ted_type {
+ TED_NONE = -1,
+ TED_MUELLER_AND_MULLER = 0, // Decision directed
+ TED_MOD_MUELLER_AND_MULLER = 1, // Decision directed
+ TED_ZERO_CROSSING = 2, // Decision directed
+ TED_GARDNER = 4,
+ TED_EARLY_LATE = 5,
+ TED_DANDREA_AND_MENGALI_GEN_MSK = 6, // Operates on the CPM signal
+ TED_SIGNAL_TIMES_SLOPE_ML = 7, // ML approx. for small signal
+ TED_SIGNUM_TIMES_SLOPE_ML = 8, // ML approx. for large signal
+ TED_MENGALI_AND_DANDREA_GMSK = 9, // Operates on the CPM signal
+ };
+
+ } /* namespace digital */
+} /* namespace gr */
+
+#endif /* INCLUDED_DIGITAL_TIMING_ERROR_DETECTOR_TYPE_H */
diff --git a/gr-digital/lib/CMakeLists.txt b/gr-digital/lib/CMakeLists.txt
index 838a14ac99..f19d037171 100644
--- a/gr-digital/lib/CMakeLists.txt
+++ b/gr-digital/lib/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright 2011-2016 Free Software Foundation, Inc.
+# Copyright (C) 2011-2016,2017 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
@@ -54,6 +54,7 @@ list(APPEND digital_sources
binary_slicer_fb_impl.cc
clock_recovery_mm_cc_impl.cc
clock_recovery_mm_ff_impl.cc
+ clock_tracking_loop.cc
cma_equalizer_cc_impl.cc
constellation.cc
constellation_decoder_cb_impl.cc
@@ -120,6 +121,10 @@ list(APPEND digital_sources
scrambler_bb_impl.cc
simple_correlator_impl.cc
simple_framer_impl.cc
+ interpolating_resampler.cc
+ symbol_sync_cc_impl.cc
+ symbol_sync_ff_impl.cc
+ timing_error_detector.cc
)
#Add Windows DLL resource file if using MSVC
diff --git a/gr-digital/lib/clock_tracking_loop.cc b/gr-digital/lib/clock_tracking_loop.cc
new file mode 100644
index 0000000000..d74e4b2120
--- /dev/null
+++ b/gr-digital/lib/clock_tracking_loop.cc
@@ -0,0 +1,307 @@
+/* -*- c++ -*- */
+/*
+ * Copyright (C) 2011,2013,2016-2017 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 "clock_tracking_loop.h"
+#include <gnuradio/math.h>
+#include <stdexcept>
+
+namespace gr {
+ namespace digital {
+
+ clock_tracking_loop::clock_tracking_loop(float loop_bw,
+ float max_period, float min_period,
+ float nominal_period,
+ float damping)
+ : d_avg_period(nominal_period),
+ d_max_avg_period(max_period),
+ d_min_avg_period(min_period),
+ d_nom_avg_period(nominal_period),
+ d_inst_period(nominal_period),
+ d_phase(0.0f),
+ d_zeta(damping),
+ d_omega_n_norm(loop_bw),
+ d_alpha(0.0f),
+ d_beta(0.0f),
+ d_prev_avg_period(nominal_period),
+ d_prev_inst_period(nominal_period),
+ d_prev_phase(0.0f)
+ {
+ set_max_avg_period(max_period);
+ set_min_avg_period(min_period);
+ set_nom_avg_period(nominal_period);
+
+ set_avg_period(d_nom_avg_period);
+ set_inst_period(d_nom_avg_period);
+
+ if (d_zeta < 0.0f)
+ throw std::out_of_range (
+ "clock_tracking_loop: damping factor must be > 0.0");
+
+ if (d_omega_n_norm < 0.0f)
+ throw std::out_of_range (
+ "clock_tracking_loop: loop bandwidth must be greater than 0.0");
+
+ update_gains();
+ }
+
+ clock_tracking_loop::~clock_tracking_loop()
+ {
+ }
+
+ void
+ clock_tracking_loop::update_gains()
+ {
+ float omega_n_T, omega_d_T, zeta_omega_n_T, k1, cosx_omega_d_T;
+ float alpha, beta;
+
+ omega_n_T = d_omega_n_norm;
+ zeta_omega_n_T = d_zeta * omega_n_T;
+ k1 = 2.0f * expf(-zeta_omega_n_T);
+
+ if (d_zeta > 1.0f) { // Over-damped (or critically-damped too)
+
+ omega_d_T = omega_n_T * sqrtf(d_zeta * d_zeta - 1.0f);
+ cosx_omega_d_T = coshf(omega_d_T);
+ // cosh ---------^^^^
+
+ } else if (d_zeta == 1.0f) { // Critically-damped
+
+ omega_d_T = 0.0f;
+ cosx_omega_d_T = 1.0f;
+ // cosh(omega_d_T) & cos(omega_d_T) are both 1 for omega_d_T == 0
+
+ } else { // Under-damped (or critically-damped too)
+
+ omega_d_T = omega_n_T * sqrtf(1.0 - d_zeta * d_zeta);
+ cosx_omega_d_T = cosf(omega_d_T);
+ // cos ----------^^^
+ }
+
+ alpha = k1 * sinhf(zeta_omega_n_T);
+ beta = 2.0f - (alpha + k1 * cosx_omega_d_T);
+
+ set_alpha(alpha);
+ set_beta(beta);
+ }
+
+ void
+ clock_tracking_loop::advance_loop(float error)
+ {
+ // So the loop can be reverted one step, if needed.
+ d_prev_avg_period = d_avg_period;
+ d_prev_inst_period = d_inst_period;
+ d_prev_phase = d_phase;
+
+ // Integral arm of PI filter
+ d_avg_period = d_avg_period + d_beta * error;
+ // Proportional arm of PI filter and final sum of PI filter arms
+ d_inst_period = d_avg_period + d_alpha * error;
+ // Compute the new, unwrapped clock phase
+ d_phase = d_phase + d_inst_period;
+ }
+
+ void
+ clock_tracking_loop::revert_loop()
+ {
+ d_avg_period = d_prev_avg_period;
+ d_inst_period = d_prev_inst_period;
+ d_phase = d_prev_phase;
+ }
+
+ void
+ clock_tracking_loop::phase_wrap()
+ {
+ float period = d_avg_period; // One could argue d_inst_period instead
+ float limit = period/2.0f;
+
+ while (d_phase > limit)
+ d_phase -= period;
+
+ while (d_phase <= -limit)
+ d_phase += period;
+ }
+
+ void
+ clock_tracking_loop::period_limit()
+ {
+ if (d_avg_period > d_max_avg_period)
+ d_avg_period = d_max_avg_period;
+ else if (d_avg_period < d_min_avg_period)
+ d_avg_period = d_min_avg_period;
+ }
+
+ /*******************************************************************
+ * SET FUNCTIONS
+ *******************************************************************/
+
+ void
+ clock_tracking_loop::set_loop_bandwidth(float bw)
+ {
+ if (bw < 0.0f)
+ throw std::out_of_range (
+ "clock_tracking_loop: loop bandwidth must be greater than 0.0");
+
+ d_omega_n_norm = bw;
+ update_gains();
+ }
+
+ void
+ clock_tracking_loop::set_damping_factor(float df)
+ {
+ if (df < 0.0f)
+ throw std::out_of_range (
+ "clock_tracking_loop: damping factor must be > 0.0");
+
+ d_zeta = df;
+ update_gains();
+ }
+
+ void
+ clock_tracking_loop::set_alpha(float alpha)
+ {
+ d_alpha = alpha;
+ }
+
+ void
+ clock_tracking_loop::set_beta(float beta)
+ {
+ d_beta = beta;
+ }
+
+ void
+ clock_tracking_loop::set_avg_period(float period)
+ {
+ d_avg_period = period;
+ d_prev_avg_period = period;
+ }
+
+ void
+ clock_tracking_loop::set_inst_period(float period)
+ {
+ d_inst_period = period;
+ d_prev_inst_period = period;
+ }
+
+ void
+ clock_tracking_loop::set_phase(float phase)
+ {
+ // This previous phase is likely inconsistent with the tracking,
+ // but if the caller is setting the phase, the odds of
+ // revert_loop() being called are slim.
+ d_prev_phase = phase;
+
+ d_phase = phase;
+ }
+
+ void
+ clock_tracking_loop::set_max_avg_period(float period)
+ {
+ d_max_avg_period = period;
+ }
+
+ void
+ clock_tracking_loop::set_min_avg_period(float period)
+ {
+ d_min_avg_period = period;
+ }
+
+ void
+ clock_tracking_loop::set_nom_avg_period(float period)
+ {
+ if (period < d_min_avg_period or
+ period > d_max_avg_period ) {
+ d_nom_avg_period = (d_max_avg_period + d_min_avg_period)/2.0f;
+ } else {
+ d_nom_avg_period = period;
+ }
+ }
+
+ /*******************************************************************
+ * GET FUNCTIONS
+ *******************************************************************/
+
+ float
+ clock_tracking_loop::get_loop_bandwidth() const
+ {
+ return d_omega_n_norm;
+ }
+
+ float
+ clock_tracking_loop::get_damping_factor() const
+ {
+ return d_zeta;
+ }
+
+ float
+ clock_tracking_loop::get_alpha() const
+ {
+ return d_alpha;
+ }
+
+ float
+ clock_tracking_loop::get_beta() const
+ {
+ return d_beta;
+ }
+
+ float
+ clock_tracking_loop::get_avg_period() const
+ {
+ return d_avg_period;
+ }
+
+ float
+ clock_tracking_loop::get_inst_period() const
+ {
+ return d_inst_period;
+ }
+
+ float
+ clock_tracking_loop::get_phase() const
+ {
+ return d_phase;
+ }
+
+ float
+ clock_tracking_loop::get_max_avg_period() const
+ {
+ return d_max_avg_period;
+ }
+
+ float
+ clock_tracking_loop::get_min_avg_period() const
+ {
+ return d_min_avg_period;
+ }
+
+ float
+ clock_tracking_loop::get_nom_avg_period() const
+ {
+ return d_nom_avg_period;
+ }
+
+ } /* namespace digital */
+} /* namespace gr */
diff --git a/gr-digital/lib/clock_tracking_loop.h b/gr-digital/lib/clock_tracking_loop.h
new file mode 100644
index 0000000000..84c77e197a
--- /dev/null
+++ b/gr-digital/lib/clock_tracking_loop.h
@@ -0,0 +1,719 @@
+/* -*- c++ -*- */
+/*
+ * Copyright (C) 2011,2013,2016-2017 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_DIGITAL_CLOCK_TRACKING_LOOP_H
+#define INCLUDED_DIGITAL_CLOCK_TRACKING_LOOP_H
+
+namespace gr {
+ namespace digital {
+
+ /*!
+ * \brief A second-order control loop implementation class.
+ *
+ * \details
+ * This class implements most of a second order symbol clock
+ * tracking loop and is inteded to act as a parent class to blocks
+ * which need a symbol clock tracking loop to determine the optimal
+ * instant to sample a received symbol from an input sample
+ * stream (i.e. *_clock_recovery* and *_clock_sync* blocks).
+ * It takes in a normalized loop bandwidth and damping factor
+ * as well as clock period bounds and provides the functions that
+ * control the update of the loop.
+ *
+ * This control loop runs at the rate of the output clock, so
+ * each step of the loop produces estimates about the output clock,
+ * and the clock phase/timing error input must come at a rate of
+ * once per output clock.
+ *
+ * This class does not include a timing error detector, and the
+ * caller is expected to provide the clock phase/timing error input
+ * for each step of the loop.
+ *
+ * The loop's low pass filter is a Proportional-Integral (PI) filter.
+ * The proportional and integral gains of the filter are termed alpha
+ * and beta respectively. These gains are calculated using the input
+ * loop bandwidth and damping factor. If needed, the alpha and beta
+ * gain values can be set using their respective #set_alpha or #set_beta
+ * functions.
+ *
+ * The class estimates the average clock period, T_avg; the instantaneous
+ * clock period, T_inst; and the instantaneous clock phase, tau; of a
+ * symbol clock based on an error signal from an external clock phase/
+ * timing error detector which provides one error signal sample per
+ * clock (one error sample at the end of every T_inst clock cycle).
+ * The error calculation is unique for each TED algorithm and is
+ * calculated externally and passed to the advance_loop function,
+ * which uses this to update its estimates.
+ *
+ * This class also provides the functions #phase_wrap and
+ * #period_limit to easily keep the clock phase estimate, tau, and the
+ * average clock period estimate, T_avg, within set bounds (phase_wrap
+ * keeps the phase within +/-T_avg/2).
+ *
+ * The clock tracking loop, with its PI filter, when properly implemented, has
+ * a digital loop phase-transfer function, in terms of the proportional gain
+ * \f$\alpha\f$ and the integral gain \f$\beta\f$ as follows:
+ *
+ * \f{align*}
+ * H(z) &= \dfrac {\Theta_o(z)}{\Theta_i(z)}
+ * = (\alpha + \beta)z^{-1} \cdot
+ * \dfrac{
+ * 1
+ * - \dfrac{\alpha}{\alpha + \beta} z^{-1}
+ * }
+ * {
+ * 1
+ * - 2 \left(1 - \dfrac{\alpha + \beta}{2}\right) z^{-1}
+ * + (1 - \alpha) z^{-2}
+ * } \\
+ * \f}
+ *
+ * Mapping the above phase-transfer function to the standard form of a transfer
+ * function for an analog second order control loop mapped to the digital domain
+ * with the mapping \f$z = e^{sT}\f$ applied to the s-plane poles,
+ * \f$s_{1,2} = -\zeta\omega_{n} \pm \omega_{n}\sqrt{\zeta^{2}-1}\f$, one obtains an
+ * alternate form of the transfer function, directly related to the damping factor
+ * \f$\zeta\f$, the natural radian frequency \f$\omega_{n}\f$, the damped radian frequency
+ * of oscillation \f$\omega_{d}\f$, and the symbol clock period \f$T\f$:
+ *
+ * \f{align*}
+ * H(z) &=
+ * \begin{cases}
+ * \dfrac{
+ * [2 -2\cos(\omega_{d}T)e^{-\zeta\omega_{n}T}] z
+ * -2\sinh(\zeta\omega_{n}T)e^{-\zeta\omega_{n}T}
+ * }
+ * {
+ * z^{2}
+ * - 2 \cos(\omega_{d}T) e^{-\zeta\omega_{n}T} z
+ * + e^{-2\zeta\omega_{n}T}
+ * }
+ * & \quad \text{for} \quad \zeta < 1 \quad \text{with}
+ * \quad \omega_{d}T = \omega_{n}T \sqrt{1 - \zeta^{2}}
+ * \\
+ * \\
+ * \dfrac{
+ * [2 -2(1)e^{-\zeta\omega_{n}T}] z
+ * -2\sinh(\zeta\omega_{n}T)e^{-\zeta\omega_{n}T}
+ * }
+ * {
+ * z^{2}
+ * - 2(1)e^{-\zeta\omega_{n}T} z
+ * + e^{-2\zeta\omega_{n}T}
+ * }
+ * & \quad \text{for} \quad \zeta = 1 \quad \text{with}
+ * \quad \omega_{d}T = 0
+ * \\
+ * \\
+ * \dfrac{
+ * [2 -2\cosh(\omega_{d}T)e^{-\zeta\omega_{n}T}] z
+ * -2\sinh(\zeta\omega_{n}T)e^{-\zeta\omega_{n}T}
+ * }
+ * {
+ * z^{2}
+ * - 2 \cosh(\omega_{d}T) e^{-\zeta\omega_{n}T} z
+ * + e^{-2\zeta\omega_{n}T}
+ * }
+ * & \quad \text{for} \quad \zeta > 1 \quad \text{with}
+ * \quad \omega_{d}T = \omega_{n}T \sqrt{\zeta^{2} - 1}
+ * \\
+ * \end{cases}
+ * \\
+ * \f}
+ *
+ * The PI filter gains, expressed in terms of the damping factor \f$\zeta\f$,
+ * the natural radian frequency \f$\omega_{n}\f$, the damped radian frequency of
+ * oscillation \f$\omega_{d}\f$, and the clock period \f$T\f$ are:
+ *
+ * \f{align*}
+ * \alpha &= 2e^{-\zeta\omega_{n}T} \sinh(\zeta\omega_{n}T) \\
+ * \\
+ * \beta &=
+ * \begin{cases}
+ * 2
+ * -2e^{-\zeta\omega_{n}T} [\sinh(\zeta\omega_{n}T) + \cos(\omega_{d}T)] &
+ * \text{for} \quad \zeta < 1 \quad (under \: damped)\\
+ * 2
+ * -2e^{-\zeta\omega_{n}T} [\sinh(\zeta\omega_{n}T) + 1] &
+ * \text{for} \quad \zeta = 1 \quad (critically \: damped)\\
+ * 2
+ * -2e^{-\zeta\omega_{n}T} [\sinh(\zeta\omega_{n}T) +\cosh(\omega_{d}T)] &
+ * \text{for} \quad \zeta > 1 \quad (over \: damped)\\
+ * \end{cases} \\
+ * \\
+ * \f}
+ *
+ * It should be noted that the clock period \f$T\f$ is being estimated by the clock
+ * tracking loop and can vary over time, so that setting the loop bandwidth
+ * directly can be a problem. However, we specify loop bandwidth in terms
+ * of the normalized digital natural radian frequency \f$\omega_{n\_norm}\f$ of the loop.
+ * \f$\omega_{n\_norm}\f$ can only usefully be a small positive number, close to
+ * zero. The damping factor, \f$\zeta\f$, dictates the maximum value
+ * \f$\omega_{n\_norm}\f$ can practically take on. In the extreme a case of
+ * \f$\zeta = 0.0\f$, \f$\omega_{n\_norm}\f$ is practically limited to the range
+ * \f$(0, \pi)\f$, as \f$\pi\f$ then corresponds to the Nyquist frequency
+ * of the clock. However, whatever the damping factor, large values of
+ * \f$\omega_{n\_norm}\f$ are usually not useful and yield poor results.
+ *
+ * \f{align*}
+ * \omega_{n}T = \omega_{n\_norm} = 2 \pi f_{n\_norm} = 2 \pi f_{n} T =
+ * \pi \dfrac{f_{n}}{\left(\dfrac{F_{c}}{2}\right)}
+ * \f}
+ *
+ * * A note on symbol clock phase vs. interpolating resampler sample phase,
+ * since most GNURadio symbol synchronization blocks seem to have the same
+ * implementation error:
+ *
+ * In general, the symbol clock phase, that the symbol clock tracking loop
+ * estimates and tracks, cannot be used alone to derive the interpolating resampler
+ * sample phase used in symbol synchronization, except in the very special case of
+ * the symbol clock period being exactly divisible by the input sample stream
+ * sample period. Since this is never guaranteed in tracking real symbol clocks,
+ * one should not use the symbol clock phase alone to compute the interpolating
+ * resampler sample phase.
+ *
+ * Consider, in the analog time domain, the optimum symbol sampling instants
+ * \f$t_{k}\f$, of an analog input signal \f$x(t)\f$, at an optimal symbol clock
+ * phase \f$\tau_{0}\f$ and the symbol clock period \f$T_{c}\f$:
+ *
+ * \f{align*}
+ * t_{k} &= \tau_{0} + k T_{c} \\
+ * y_{k} &= x(t_{k}) = x(\tau_{0} + k T_{c}) \\
+ * \f}
+ *
+ * If one divides the \f$t_{k}\f$ times by the input sample stream sample period
+ * \f$T_{i}\f$, the correct interpolating resampler sample phase \f$\tau_{0\_i}\f$ will
+ * get a contribution from the term \f$T_{c\_remainder}\f$ (which is an error) as shown
+ * below:
+ *
+ * \f{align*}
+ * \dfrac{t_{k}}{T_{i}} &= \dfrac{\tau_{0}}{T_{i}} + \dfrac{k T_{c}}{T_{i}} \\
+ * &= (m + \tau_{0\_remainder}) + (n + T_{c\_remainder}) \\
+ * &= \tau_{0\_remainder} + T_{c\_remainder} + (m + n) \\
+ * &= \tau_{0\_i} + k^{\prime}
+ * \f}
+ *
+ * So instead of using the symbol clock sample phase alone to obtain the
+ * interpolating resampler sample phase, one should use the previous interpolating
+ * resampler sample phase and the instantaneous clock period estimate provided by
+ * the symbol clock tracking loop.
+ *
+ */
+ class clock_tracking_loop
+ {
+ protected:
+ // Estimate of the average clock period, T_avg, in units of
+ // input sample clocks (so this is the average number of
+ // input samples per output symbol, aka samples/symbol).
+ // To convert to seconds, divide by the input sample rate: F_s_input.
+ float d_avg_period;
+
+ // Limits on how far the average clock period estimate can wander,
+ // and the nominal average clock period, in units of input sample clocks.
+ // To convert to seconds, divide by the input sample rate: F_s_input.
+ float d_max_avg_period, d_min_avg_period;
+ float d_nom_avg_period;
+
+ // Instantaneous clock period estimate, T_inst, in units of
+ // input sample clocks (so this is the intantaneous number of
+ // input samples per output symbol, aka instantaneous samples/symbol).
+ // To convert to seconds, divide by the input sample rate: F_s_input.
+ float d_inst_period;
+
+ // Instantaneous clock phase estimate, tau, in units of
+ // input sample clocks.
+ // To convert to seconds, divide by the input sample rate: F_s_input.
+ // To wrap, add or subtract a multiple of the estimate of the
+ // average clock period, T_avg.
+ // To convert to a normalized (but not wrapped) clock phase estimate,
+ // divide by the estimate of the average clock period, T_avg.
+ // To further convert the normalized clock phase estimate to radians,
+ // multiply the normalized clock phase estimate by 2*pi.
+ float d_phase;
+
+ // Damping factor of the 2nd order loop transfer function.
+ // Zeta in the range (0.0, 1.0) yields an under-damped loop.
+ // Zeta in the range (1.0, Inf) yields an over-damped loop.
+ // Zeta equal to 1.0 yields a crtically-damped loop.
+ float d_zeta;
+
+ // Normalized natural radian frequency of the 2nd order loop transfer
+ // function. It should be a small positive number, corresponding to
+ // the normalized natural radian frequency of the loop as digital
+ // low-pass filter that is filtering the clock phase/timing error signal.
+ // omega_n_norm = omega_n*T = 2*pi*f_n*T = 2*pi*f_n_norm
+ float d_omega_n_norm;
+
+ // Proportional gain of the PI loop filter (aka gain_mu)
+ // (aka gain_mu in some clock recovery blocks)
+ float d_alpha;
+
+ // Integral gain of the PI loop filter
+ // (aka gain_omega in some clock recovery blocks)
+ float d_beta;
+
+ // For reverting the loop state one iteration (only)
+ float d_prev_avg_period;
+ float d_prev_inst_period;
+ float d_prev_phase;
+
+ public:
+ clock_tracking_loop(void) {}
+
+ /*! \brief Construct a clock_tracking_loop object.
+ *
+ * \details
+ * This function instantiates a clock_tracking_loop object.
+ *
+ * \param loop_bw
+ * Normalized approximate loop bandwidth.
+ * It should be a small positive number, corresponding to the normalized
+ * natural radian frequency of the loop as digital low-pass filter that is
+ * filtering the clock phase/timing error.
+ *
+ * Technically this parameter corresponds to the natural radian frequency
+ * of the 2nd order loop transfer function (scaled by Fs),
+ * which is the radius of the pole locations in the s-plane of an
+ * underdamped analog 2nd order system.
+ *
+ * \param max_period
+ * Maximum limit for the estimated clock period, in units of
+ * input stream sample periods. (i.e. maximum samples/symbol)
+ *
+ * \param min_period
+ * Minimum limit for the estimated clock period, in units of
+ * input stream sample periods. (i.e. minimum samples/symbol)
+ *
+ * \param nominal_period
+ * Nominal value for the estimated clock period, in units of
+ * input stream sample periods. (i.e. nominal samples/symbol)
+ * If not specified, this value will be set to the average of
+ * min_period and max_period,
+ *
+ * \param damping
+ * Damping factor of the 2nd order loop transfer function.
+ * Damping in the range (0.0, 1.0) yields an under-damped loop.
+ * Damping in the range (1.0, Inf) yields an over-damped loop.
+ * Damping equal to 1.0 yields a crtically-damped loop.
+ * Under-damped loops are not generally useful for clock tracking.
+ * This parameter defaults to 2.0, if not specified.
+ */
+ clock_tracking_loop(float loop_bw,
+ float max_period, float min_period,
+ float nominal_period = 0.0f,
+ float damping = 2.0f);
+
+ virtual ~clock_tracking_loop();
+
+ /*! \brief Update the gains from the loop bandwidth and damping factor.
+ *
+ * \details
+ * This function updates the gains based on the loop
+ * bandwidth and damping factor of the system. These two
+ * factors can be set separately through their own set
+ * functions.
+ */
+ void update_gains();
+
+ /*! \brief Advance the loop based on the current gain
+ * settings and the input error signal.
+ */
+ void advance_loop(float error);
+
+ /*! \brief Undo a single, prior advance_loop() call.
+ *
+ * \details
+ * Reverts a single, prior call to advance_loop().
+ * It cannot usefully called again, until after the next call
+ * to advance_loop().
+ *
+ * This method is needed so clock recovery/sync blocks can
+ * perform correctly given the constraints of GNURadio's streaming
+ * engine, interpolation filtering, and tag propagation.
+ */
+ void revert_loop();
+
+ /*! \brief
+ * Keep the clock phase estimate, tau, between -T_avg/2 and T_avg/2.
+ *
+ * \details
+ * This function keeps the clock phase estimate, tau, between
+ * -T_avg/2 and T_avg/2, by wrapping it modulo the estimated
+ * average clock period, T_avg. (N.B. Wrapping an estimated phase
+ * by an *estimated*, *average* period.)
+ *
+ * This function can be called after advance_loop to keep the
+ * phase value small. It is set as a separate method in case
+ * another way is desired as this is fairly heavy-handed.
+ * Clock recovery/sync blocks usually do not need the phase of the
+ * clock, and this class doesn't actually use the phase at all,
+ * so calling this is optional.
+ */
+ void phase_wrap();
+
+ /*! \brief
+ * Keep the estimated average clock period, T_avg, between T_avg_min
+ * and T_avg_max.
+ *
+ * \details
+ * This function keeps the estimated average clock period, T_avg,
+ * between T_avg_min and T_avg_max. It accomplishes this by hard limiting.
+ * This is needed because T_avg is essentially computed by the
+ * integrator portion of an IIR filter, so T_avg could potentially
+ * wander very far during periods of noise/nonsense input.
+ *
+ * This function should be called after advance_loop to keep the
+ * estimated average clock period, T_avg, in the specified range.
+ * It is set as a separate method in case another way is desired as
+ * this is fairly heavy-handed.
+ */
+ void period_limit();
+
+ /*******************************************************************
+ * SET FUNCTIONS
+ *******************************************************************/
+
+ /*!
+ * \brief Set the normalized approximate loop bandwidth.
+ *
+ * \details
+ * Set the normalized approximate loop bandwidth.
+ * Useful values are usually close to 0.0, e.g. 2*pi*0.045.
+ *
+ * It should be a small positive number, corresponding to the normalized
+ * natural radian frequency of the loop as digital low-pass filter that is
+ * filtering the clock phase/timing error.
+ *
+ * Technically this parameter corresponds to the natural radian frequency
+ * of the 2nd order loop transfer function (scaled by Fs),
+ * which is the radius of the pole locations in the s-plane of an
+ * underdamped analog 2nd order system.
+ *
+ * The input parameter corresponds to omega_n_norm in the following
+ * relation:
+ *
+ * omega_n_norm = omega_n*T = 2*pi*f_n*T = 2*pi*f_n_norm
+ *
+ * where T is the period of the clock being estimated by this
+ * clock tracking loop, and omega_n is the natural radian frequency
+ * of the 2nd order loop transfer function.
+ *
+ * When a new loop bandwidth is set, the gains, alpha and beta,
+ * of the loop are recalculated by a call to update_gains().
+ *
+ * \param bw normalized approximate loop bandwidth
+ */
+ virtual void set_loop_bandwidth(float bw);
+
+ /*!
+ * \brief Set the loop damping factor.
+ *
+ * \details
+ * Set the damping factor of the loop.
+ * Damping in the range (0.0, 1.0) yields an under-damped loop.
+ * Damping in the range (1.0, Inf) yields an over-damped loop.
+ * Damping equal to 1.0 yields a crtically-damped loop.
+ * Under-damped loops are not generally useful for clock tracking.
+ * For clock tracking, as a first guess, set the damping factor to 2.0,
+ * or 1.5 or 1.0.
+ *
+ * Damping factor of the 2nd order loop transfer function.
+ * When a new damping factor is set, the gains, alpha and beta,
+ * of the loop are recalculated by a call to update_gains().
+ *
+ * \param df loop damping factor
+ */
+ void set_damping_factor(float df);
+
+ /*!
+ * \brief Set the PI filter proportional gain, alpha.
+ *
+ * \details
+ * Sets the PI filter proportional gain, alpha.
+ * This gain directly mutliplies the clock phase/timing error
+ * term in the PI filter when advancing the loop.
+ * It most directly affects the instantaneous clock period estimate,
+ * T_inst, and instantaneous clock phase estimate, tau.
+ *
+ * This value would normally be adjusted by setting the loop
+ * bandwidth and damping factor and calling update_gains(). However,
+ * it can be set here directly if desired.
+ *
+ * Setting this parameter directly is probably only feasible if
+ * the user is directly observing the estimates of average clock
+ * period and instantaneous clock period over time in response to
+ * an impulsive change in the input stream (i.e. watching the loop
+ * transient behavior at the start of a data burst).
+ *
+ * \param alpha PI filter proportional gain
+ */
+ void set_alpha(float alpha);
+
+ /*!
+ * \brief Set the PI filter integral gain, beta.
+ *
+ * \details
+ * Sets the PI filter integral gain, beta.
+ * This gain is used when integrating the clock phase/timing error
+ * term in the PI filter when advancing the loop.
+ * It most directly affects the average clock period estimate,
+ * T_avg.
+ *
+ * This value would normally be adjusted by setting the loop
+ * bandwidth and damping factor and calling update_gains(). However,
+ * it can be set here directly if desired.
+ *
+ * Setting this parameter directly is probably only feasible if
+ * the user is directly observing the estimates of average clock
+ * period and instantaneous clock period over time in response to
+ * an impulsive change in the input stream (i.e. watching the loop
+ * transient behavior at the start of a data burst).
+ *
+ * \param beta PI filter integral gain
+ */
+ void set_beta(float beta);
+
+ /*!
+ * \brief Set the average clock period estimate, T_avg.
+ *
+ * \details
+ * Directly sets the average clock period estimate, T_avg,
+ * in units of input stream sample clocks (so the average number of
+ * input samples per output symbol, aka samples/symbol).
+ *
+ * The average clock period estimate, T_avg, is normally updated by
+ * the advance_loop() and period_limit() calls. This method is used
+ * manually reset the estimate when needed.
+ *
+ * \param period
+ * Average clock period, T_avg, in units of input stream sample clocks.
+ */
+ void set_avg_period(float period);
+
+ /*!
+ * \brief Set the instantaneous clock period estimate, T_inst.
+ *
+ * \details
+ * Directly sets the instantaneous clock period estimate, T_inst,
+ * in units of input stream sample clocks (so the instantaneous number of
+ * input samples per output symbol, aka instantaneous samples/symbol).
+ *
+ * The instantaneous clock period estimate, T_inst, is normally updated by
+ * the advance_loop() call. This method is used manually reset the
+ * estimate when needed.
+ *
+ * \param period
+ * Instantaneous clock period, T_inst, in units of input stream sample
+ * clocks.
+ */
+ void set_inst_period(float period);
+
+ /*!
+ * \brief Set the instantaneous clock phase estimate, tau.
+ *
+ * \details
+ * Directly sets the instantaneous clock phase estimate, tau,
+ * in units of input stream sample clocks.
+ *
+ * The instantaneous clock phase estimate, tau, is normally updated by
+ * the advance_loop() call. This method is used manually reset the
+ * estimate when needed.
+ *
+ * \param phase
+ * Instantaneous clock phase, tau, in units of input stream sample clocks.
+ *
+ */
+ void set_phase(float phase);
+
+ /*!
+ * \brief Set the maximum average clock period estimate limit, T_avg_max.
+ *
+ * \details
+ * Sets the maximum average clock period estimate limit, T_avg_max
+ * in units of input stream sample clocks (so the maximum average number
+ * of input samples per output symbol, aka maximum samples/symbol).
+ *
+ * This limit is needed because T_avg is essentially computed by the
+ * integrator portion of an IIR filter, so T_avg could potentially
+ * wander very far during periods of noise/nonsense input.
+ *
+ * \param period
+ * Maximum average clock period, T_avg_max, in units of input stream
+ * sample clocks.
+ */
+ void set_max_avg_period(float period);
+
+ /*!
+ * \brief Set the minimum average clock period estimate limit, T_avg_min.
+ *
+ * \details
+ * Sets the minimum average clock period estimate limit, T_avg_min
+ * in units of input stream sample clocks (so the minimum average number
+ * of input samples per output symbol, aka minimum samples/symbol).
+ *
+ * This limit is needed because T_avg is essentially computed by the
+ * integrator portion of an IIR filter, so T_avg could potentially
+ * wander very far during periods of noise/nonsense input.
+ *
+ * \param period
+ * Minimum average clock period, T_avg_min, in units of input stream
+ * sample clocks.
+ */
+ void set_min_avg_period(float period);
+
+ /*!
+ * \brief Set the nominal average clock period estimate limit, T_avg_nom.
+ *
+ * \details
+ * Sets the nominal average clock period estimate limit, T_avg_nom
+ * in units of input stream sample clocks (so the nominal average number
+ * of input samples per output symbol, aka minimum samples/symbol).
+ *
+ * \param period
+ * Nominal average clock period, T_avg_nom, in units of input stream
+ * sample clocks.
+ */
+ void set_nom_avg_period(float period);
+
+ /*******************************************************************
+ * GET FUNCTIONS
+ *******************************************************************/
+
+ /*!
+ * \brief Returns the normalized approximate loop bandwidth.
+ *
+ * \details
+ * See the documenation for set_loop_bandwidth() for more details.
+ *
+ * Note that if set_alpha() or set_beta() were called to directly
+ * set gains, the value returned by this method will be inaccurate/stale.
+ */
+ float get_loop_bandwidth() const;
+
+ /*!
+ * \brief Returns the loop damping factor.
+ *
+ * \details
+ * See the documenation for set_damping_factor() for more details.
+ *
+ * Note that if set_alpha() or set_beta() were called to directly
+ * set gains, the value returned by this method will be inaccurate/stale.
+ */
+ float get_damping_factor() const;
+
+ /*!
+ * \brief Returns the PI filter proportional gain, alpha.
+ *
+ * \details
+ * See the documenation for set_alpha() for more details.
+ */
+ float get_alpha() const;
+
+ /*!
+ * \brief Returns the PI filter integral gain, beta.
+ *
+ * \details
+ * See the documenation for set_beta() for more details.
+ */
+ float get_beta() const;
+
+ /*!
+ * \brief Get the average clock period estimate, T_avg.
+ *
+ * \details
+ * Gets the average clock period estimate, T_avg,
+ * in units of input stream sample clocks (so the average number of
+ * input samples per output symbol, aka samples/symbol).
+ *
+ * To convert to seconds, divide by the input stream sample rate:
+ * F_s_input.
+ */
+ float get_avg_period() const;
+
+ /*!
+ * \brief Get the instantaneous clock period estimate, T_inst.
+ *
+ * \details
+ * Gets the instantaneous clock period estimate, T_inst,
+ * in units of input stream sample clocks (so the instantaneous number of
+ * input samples per output symbol, aka instantaneous samples/symbol).
+ *
+ * To convert to seconds, divide by the input stream sample rate:
+ * F_s_input.
+ */
+ float get_inst_period() const;
+
+ /*!
+ * \brief Get the instantaneous clock phase estimate, tau.
+ *
+ * \details
+ * Gets the instantaneous clock phase estimate, tau, in units of
+ * input stream sample clocks.
+ *
+ * To convert to seconds, divide by the input stream sample rate:
+ * F_s_input.
+ *
+ * To manually wrap, add or subtract a multiple of the estimate of the
+ * average clock period, T_avg.
+ *
+ * To convert to a normalized (but not wrapped) clock phase estimate,
+ * divide by the estimate of the average clock period, T_avg.
+ * To further convert the normalized clock phase estimate to radians,
+ * multiply the normalized clock phase estimate by 2*pi.
+ */
+ float get_phase() const;
+
+ /*!
+ * \brief Get the maximum average clock period estimate limit, T_avg_max.
+ *
+ * \details
+ * See the documenation for set_max_avg_period() for more details.
+ */
+ float get_max_avg_period() const;
+
+ /*!
+ * \brief Get the minimum average clock period estimate limit, T_avg_min.
+ *
+ * \details
+ * See the documenation for set_min_avg_period() for more details.
+ */
+ float get_min_avg_period() const;
+
+ /*!
+ * \brief Get the nominal average clock period, T_avg_nom.
+ *
+ * \details
+ * Gets the nominal average clock period, T_avg_nom,
+ * in units of input stream sample clocks (so the nominal average
+ * number of input samples per output symbol, aka nominal samples/symbol).
+ *
+ * To convert to seconds, divide by the input stream sample rate:
+ * F_s_input.
+ */
+ float get_nom_avg_period() const;
+
+ };
+
+ } /* namespace digital */
+} /* namespace gr */
+
+#endif /* INCLUDED_DIGITAL_CLOCK_TRACKING_LOOP_H */
diff --git a/gr-digital/lib/interpolating_resampler.cc b/gr-digital/lib/interpolating_resampler.cc
new file mode 100644
index 0000000000..3ec27b05de
--- /dev/null
+++ b/gr-digital/lib/interpolating_resampler.cc
@@ -0,0 +1,794 @@
+/* -*- c++ -*- */
+/*
+ * Copyright (C) 2017 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 file; 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 "interpolating_resampler.h"
+#include <gnuradio/math.h>
+#include <stdexcept>
+#include <deque>
+
+namespace gr {
+ namespace digital {
+
+ interpolating_resampler::interpolating_resampler(enum ir_type type,
+ bool derivative)
+ : d_type(type),
+ d_derivative(derivative),
+ d_phase(0.0f),
+ d_phase_wrapped(0.0f),
+ d_phase_n(0),
+ d_prev_phase(0.0f),
+ d_prev_phase_wrapped(0.0f),
+ d_prev_phase_n(0)
+ {
+ switch (d_type) {
+ case IR_MMSE_8TAP:
+ break;
+ case IR_PFB_NO_MF:
+ break;
+ case IR_PFB_MF:
+ break;
+ case IR_NONE:
+ default:
+ throw std::invalid_argument(
+ "interpolating_resampler: invalid interpolating resampler type.");
+ break;
+ }
+
+ sync_reset(0.0f);
+ }
+
+ void
+ interpolating_resampler::next_phase(float increment,
+ float &phase,
+ int &phase_n,
+ float &phase_wrapped)
+ {
+ float n;
+
+ phase = d_phase_wrapped + increment;
+ n = floorf(phase);
+ phase_wrapped = phase - n;
+ phase_n = static_cast<int>(n);
+ }
+
+ void
+ interpolating_resampler::advance_phase(float increment)
+ {
+ d_prev_phase = d_phase;
+ d_prev_phase_wrapped = d_phase_wrapped;
+ d_prev_phase_n = d_phase_n;
+
+ next_phase(increment, d_phase, d_phase_n, d_phase_wrapped);
+ }
+
+ void
+ interpolating_resampler::revert_phase()
+ {
+ d_phase = d_prev_phase;
+ d_phase_wrapped = d_prev_phase_wrapped;
+ d_phase_n = d_prev_phase_n;
+ }
+
+ void
+ interpolating_resampler::sync_reset(float phase)
+ {
+ float n;
+
+ d_phase = phase;
+ n = floorf(d_phase);
+ d_phase_wrapped = d_phase - n;
+ d_phase_n = static_cast<int>(n);
+
+ d_prev_phase = d_phase;
+ d_prev_phase_wrapped = d_phase_wrapped;
+ d_prev_phase_n = d_phase_n;
+ }
+
+ /*************************************************************************/
+
+ interpolating_resampler_ccf *
+ interpolating_resampler_ccf::make(enum ir_type type,
+ bool derivative,
+ int nfilts,
+ const std::vector<float> &taps)
+ {
+ interpolating_resampler_ccf *ret = NULL;
+ switch (type) {
+ case IR_MMSE_8TAP:
+ ret = new interp_resampler_mmse_8tap_cc(derivative);
+ break;
+ case IR_PFB_NO_MF:
+ ret = new interp_resampler_pfb_no_mf_cc(derivative, nfilts);
+ break;
+ case IR_PFB_MF:
+ ret = new interp_resampler_pfb_mf_ccf(taps, nfilts, derivative);
+ break;
+ case IR_NONE:
+ default:
+ throw std::invalid_argument("interpolating_resampler_ccf: invalid "
+ "interpolating resampler type.");
+ break;
+ }
+ return ret;
+ }
+
+ /*************************************************************************/
+
+ interpolating_resampler_fff *
+ interpolating_resampler_fff::make(enum ir_type type,
+ bool derivative,
+ int nfilts,
+ const std::vector<float> &taps)
+ {
+ interpolating_resampler_fff *ret = NULL;
+ switch (type) {
+ case IR_MMSE_8TAP:
+ ret = new interp_resampler_mmse_8tap_ff(derivative);
+ break;
+ case IR_PFB_NO_MF:
+ ret = new interp_resampler_pfb_no_mf_ff(derivative, nfilts);
+ break;
+ case IR_PFB_MF:
+ ret = new interp_resampler_pfb_mf_fff(taps, nfilts, derivative);
+ break;
+ case IR_NONE:
+ default:
+ throw std::invalid_argument("interpolating_resampler_fff: invalid "
+ "interpolating resampler type.");
+ break;
+ }
+ return ret;
+ }
+
+ /*************************************************************************/
+
+ interp_resampler_mmse_8tap_cc::interp_resampler_mmse_8tap_cc(
+ bool derivative)
+ : interpolating_resampler_ccf(IR_MMSE_8TAP, derivative),
+ d_interp(NULL),
+ d_interp_diff(NULL)
+ {
+ d_interp = new filter::mmse_fir_interpolator_cc();
+ if (d_interp == NULL)
+ throw std::runtime_error("unable to create mmse_fir_interpolator_cc");
+
+ if (d_derivative) {
+ d_interp_diff = new filter::mmse_interp_differentiator_cc();
+ if (d_interp_diff == NULL)
+ throw std::runtime_error("unable to create "
+ "mmse_interp_differentiator_cc");
+ }
+ }
+
+ interp_resampler_mmse_8tap_cc::~interp_resampler_mmse_8tap_cc()
+ {
+ delete d_interp;
+ if (d_derivative)
+ delete d_interp_diff;
+ }
+
+ gr_complex
+ interp_resampler_mmse_8tap_cc::interpolate(const gr_complex input[],
+ float mu) const
+ {
+ return d_interp->interpolate(input, mu);
+ }
+
+ gr_complex
+ interp_resampler_mmse_8tap_cc::differentiate(const gr_complex input[],
+ float mu) const
+ {
+ return d_interp_diff->differentiate(input, mu);
+ }
+
+ unsigned int
+ interp_resampler_mmse_8tap_cc::ntaps() const
+ {
+ return d_interp->ntaps();
+ }
+
+ /*************************************************************************/
+
+ interp_resampler_mmse_8tap_ff::interp_resampler_mmse_8tap_ff(
+ bool derivative)
+ : interpolating_resampler_fff(IR_MMSE_8TAP, derivative),
+ d_interp(NULL),
+ d_interp_diff(NULL)
+ {
+ d_interp = new filter::mmse_fir_interpolator_ff();
+ if (d_interp == NULL)
+ throw std::runtime_error("unable to create mmse_fir_interpolator_ff");
+
+ if (d_derivative) {
+ d_interp_diff = new filter::mmse_interp_differentiator_ff();
+ if (d_interp_diff == NULL)
+ throw std::runtime_error("unable to create "
+ "mmse_interp_differentiator_ff");
+ }
+ }
+
+ interp_resampler_mmse_8tap_ff::~interp_resampler_mmse_8tap_ff()
+ {
+ delete d_interp;
+ if (d_derivative)
+ delete d_interp_diff;
+ }
+
+ float
+ interp_resampler_mmse_8tap_ff::interpolate(const float input[],
+ float mu) const
+ {
+ return d_interp->interpolate(input, mu);
+ }
+
+ float
+ interp_resampler_mmse_8tap_ff::differentiate(const float input[],
+ float mu) const
+ {
+ return d_interp_diff->differentiate(input, mu);
+ }
+
+ unsigned int
+ interp_resampler_mmse_8tap_ff::ntaps() const
+ {
+ return d_interp->ntaps();
+ }
+
+ /*************************************************************************/
+
+#include "gnuradio/filter/interpolator_taps.h"
+#include "gnuradio/filter/interp_differentiator_taps.h"
+
+ interp_resampler_pfb_no_mf_cc::interp_resampler_pfb_no_mf_cc(
+ bool derivative,
+ int nfilts)
+ : interpolating_resampler_ccf(IR_PFB_NO_MF, derivative),
+ d_nfilters(0),
+ d_filters(),
+ d_diff_filters()
+ {
+ if (nfilts <= 1)
+ throw std::invalid_argument("interpolating_resampler_pfb_no_mf_cc: "
+ "number of polyphase filter arms "
+ "must be greater than 1");
+
+ // Round up the number of filter arms to the current or next power of 2
+ d_nfilters =
+ 1 << (static_cast<int>(log2f(static_cast<float>(nfilts-1))) + 1);
+
+ // N.B. We assume in this class: NSTEPS == DNSTEPS and NTAPS == DNTAPS
+
+ // Limit to the maximum number of precomputed MMSE tap sets
+ if (d_nfilters <= 0 or d_nfilters > NSTEPS)
+ d_nfilters = NSTEPS;
+
+ // Create our polyphase filter arms for the steps from 0.0 to 1.0 from
+ // the MMSE interpolating filter and MMSE interpolating differentiator
+ // taps rows.
+ // N.B. We create an extra final row for an offset of 1.0, because it's
+ // easier than dealing with wrap around from 0.99... to 0.0 shifted
+ // by 1 tap.
+ d_filters = std::vector<filter::kernel::fir_filter_ccf*>(d_nfilters + 1,
+ NULL);
+ d_diff_filters = std::vector<filter::kernel::fir_filter_ccf*>(
+ d_nfilters + 1,
+ NULL);
+
+ std::vector<float> t(NTAPS, 0);
+ int incr = NSTEPS/d_nfilters;
+ int src, dst;
+ for (src = 0, dst = 0; src <= NSTEPS; src += incr, dst++) {
+
+ t.assign(&taps[src][0], &taps[src][NTAPS]);
+ d_filters[dst] = new filter::kernel::fir_filter_ccf(1, t);
+ if (d_filters[dst] == NULL)
+ throw std::runtime_error("unable to create fir_filter_ccf");
+
+ if (d_derivative) {
+ t.assign(&Dtaps[src][0], &Dtaps[src][DNTAPS]);
+ d_diff_filters[dst] = new filter::kernel::fir_filter_ccf(1, t);
+ if (d_diff_filters[dst] == NULL)
+ throw std::runtime_error("unable to create fir_filter_ccf");
+ }
+ }
+ }
+
+ interp_resampler_pfb_no_mf_cc::~interp_resampler_pfb_no_mf_cc()
+ {
+ for (int i = 0; i <= d_nfilters; i++) {
+ delete d_filters[i];
+ if (d_derivative)
+ delete d_diff_filters[i];
+ }
+ }
+
+ gr_complex
+ interp_resampler_pfb_no_mf_cc::interpolate(const gr_complex input[],
+ float mu) const
+ {
+ int arm = static_cast<int>(rint(mu * d_nfilters));
+
+ if (arm < 0 or arm > d_nfilters)
+ throw std::runtime_error("interp_resampler_pfb_no_mf_cc: mu is not "
+ "in the range [0.0, 1.0]");
+
+ return d_filters[arm]->filter(input);
+ }
+
+ gr_complex
+ interp_resampler_pfb_no_mf_cc::differentiate(const gr_complex input[],
+ float mu) const
+ {
+ int arm = static_cast<int>(rint(mu * d_nfilters));
+
+ if (arm < 0 or arm > d_nfilters)
+ throw std::runtime_error("interp_resampler_pfb_no_mf_cc: mu is not "
+ "in the range [0.0, 1.0]");
+
+ return d_diff_filters[arm]->filter(input);
+ }
+
+ unsigned int
+ interp_resampler_pfb_no_mf_cc::ntaps() const
+ {
+ return NTAPS;
+ }
+
+ /*************************************************************************/
+
+ interp_resampler_pfb_no_mf_ff::interp_resampler_pfb_no_mf_ff(
+ bool derivative,
+ int nfilts)
+ : interpolating_resampler_fff(IR_PFB_NO_MF, derivative),
+ d_nfilters(0),
+ d_filters(),
+ d_diff_filters()
+ {
+ if (nfilts <= 1)
+ throw std::invalid_argument("interpolating_resampler_pfb_no_mf_ff: "
+ "number of polyphase filter arms "
+ "must be greater than 1");
+
+ // Round up the number of filter arms to the current or next power of 2
+ d_nfilters =
+ 1 << (static_cast<int>(log2f(static_cast<float>(nfilts-1))) + 1);
+
+ // N.B. We assume in this class: NSTEPS == DNSTEPS and NTAPS == DNTAPS
+
+ // Limit to the maximum number of precomputed MMSE tap sets
+ if (d_nfilters <= 0 or d_nfilters > NSTEPS)
+ d_nfilters = NSTEPS;
+
+ // Create our polyphase filter arms for the steps from 0.0 to 1.0 from
+ // the MMSE interpolating filter and MMSE interpolating differentiator
+ // taps rows.
+ // N.B. We create an extra final row for an offset of 1.0, because it's
+ // easier than dealing with wrap around from 0.99... to 0.0 shifted
+ // by 1 tap.
+ d_filters = std::vector<filter::kernel::fir_filter_fff*>(d_nfilters + 1,
+ NULL);
+ d_diff_filters = std::vector<filter::kernel::fir_filter_fff*>(
+ d_nfilters + 1,
+ NULL);
+
+ std::vector<float> t(NTAPS, 0);
+ int incr = NSTEPS/d_nfilters;
+ int src, dst;
+ for (src = 0, dst = 0; src <= NSTEPS; src += incr, dst++) {
+
+ t.assign(&taps[src][0], &taps[src][NTAPS]);
+ d_filters[dst] = new filter::kernel::fir_filter_fff(1, t);
+ if (d_filters[dst] == NULL)
+ throw std::runtime_error("unable to create fir_filter_fff");
+
+ if (d_derivative) {
+ t.assign(&Dtaps[src][0], &Dtaps[src][DNTAPS]);
+ d_diff_filters[dst] = new filter::kernel::fir_filter_fff(1, t);
+ if (d_diff_filters[dst] == NULL)
+ throw std::runtime_error("unable to create fir_filter_fff");
+ }
+ }
+ }
+
+ interp_resampler_pfb_no_mf_ff::~interp_resampler_pfb_no_mf_ff()
+ {
+ for (int i = 0; i <= d_nfilters; i++) {
+ delete d_filters[i];
+ if (d_derivative)
+ delete d_diff_filters[i];
+ }
+ }
+
+ float
+ interp_resampler_pfb_no_mf_ff::interpolate(const float input[],
+ float mu) const
+ {
+ int arm = static_cast<int>(rint(mu * d_nfilters));
+
+ if (arm < 0 or arm > d_nfilters)
+ throw std::runtime_error("interp_resampler_pfb_no_mf_ff: mu is not "
+ "in the range [0.0, 1.0]");
+
+ return d_filters[arm]->filter(input);
+ }
+
+ float
+ interp_resampler_pfb_no_mf_ff::differentiate(const float input[],
+ float mu) const
+ {
+ int arm = static_cast<int>(rint(mu * d_nfilters));
+
+ if (arm < 0 or arm > d_nfilters)
+ throw std::runtime_error("interp_resampler_pfb_no_mf_ff: mu is not "
+ "in the range [0.0, 1.0]");
+
+ return d_diff_filters[arm]->filter(input);
+ }
+
+ unsigned int
+ interp_resampler_pfb_no_mf_ff::ntaps() const
+ {
+ return NTAPS;
+ }
+
+ /*************************************************************************/
+
+ interp_resampler_pfb_mf_ccf::interp_resampler_pfb_mf_ccf(
+ const std::vector<float> &taps,
+ int nfilts,
+ bool derivative)
+ : interpolating_resampler_ccf(IR_PFB_MF, derivative),
+ d_nfilters(nfilts),
+ d_taps_per_filter(static_cast<unsigned int>(
+ ceil( static_cast<double>(taps.size())
+ / static_cast<double>(nfilts )))),
+ d_filters(),
+ d_diff_filters(),
+ d_taps(),
+ d_diff_taps()
+ {
+ if (d_nfilters <= 1)
+ throw std::invalid_argument("interpolating_resampler_pfb_mf_ccf: "
+ "number of polyphase filter arms "
+ "must be greater than 1");
+ if (taps.size() < static_cast<unsigned int>(d_nfilters))
+ throw std::invalid_argument("interpolating_resampler_pfb_mf_ccf: "
+ "length of the prototype filter taps"
+ " must be greater than or equal to "
+ "the number of polyphase filter arms.");
+
+ // Create a derivative filter from the provided taps
+
+ // First create a truncated ideal differentiator filter
+ int ideal_diff_filt_len = 3; // Must be odd; rest of init assumes odd.
+ std::vector<float> ideal_diff_taps(ideal_diff_filt_len, 0.0f);
+ int i, n;
+ n = ideal_diff_taps.size()/2;
+ for (i = -n; i < 0; i++) {
+ ideal_diff_taps[i+n] = (-i & 1) == 1 ? -1.0f/i : 1.0f/i;
+ ideal_diff_taps[n-i] = -ideal_diff_taps[i+n];
+ }
+ ideal_diff_taps[n] = 0.0f;
+
+ // Perform linear convolution of prototype filter taps and the truncated
+ // ideal differentiator taps to generate a derivative matched filter.
+ // N.B. the truncated ideal differentiator taps must have an odd length
+ int j, k, l, m;
+ m = ideal_diff_taps.size();
+ n = taps.size();
+ l = m + n - 1; // length of convolution
+ std::deque<float> diff_taps(l, 0.0f);
+ for (i = 0; i < l; i++) {
+ for (j = 0; j < m; j++) {
+ k = i + j - (m - 1);
+ if (k < 0 or k >= n)
+ continue;
+ diff_taps[i] += ideal_diff_taps[(m - 1) - j] * taps[k];
+ }
+ }
+
+ // Trim the convolution so the prototype derivative filter is the same
+ // length as the passed in prototype filter taps.
+ n = ideal_diff_taps.size()/2;
+ for (i = 0; i < n; i++) {
+ diff_taps.pop_back();
+ diff_taps.pop_front();
+ }
+
+ // Squash the differentiation noise spikes at the filter ends.
+ diff_taps[0] = 0.0f;
+ diff_taps[diff_taps.size()-1] = 0.0f;
+
+ // Normalize the prototype derviative filter gain to the number of
+ // filter arms
+ n = diff_taps.size();
+ float mag = 0.0f;
+ for (i = 0; i < n; i++)
+ mag += fabsf(diff_taps[i]);
+ for (i = 0; i < n; i++) {
+ diff_taps[i] *= d_nfilters/mag;
+ if (d_derivative and std::isnan(diff_taps[i]))
+ throw std::runtime_error("interpolating_resampler_pfb_mf_ccf: "
+ "NaN error creating derivative filter."
+ );
+ }
+
+ // Create our polyphase filter arms for the steps from 0.0 to 1.0 from
+ // the prototype matched filter.
+ // N.B. We create an extra final row for an offset of 1.0, because it's
+ // easier than dealing with wrap around from 0.99... to 0.0 shifted
+ // by 1 tap.
+ d_filters = std::vector<filter::kernel::fir_filter_ccf*>(d_nfilters + 1,
+ NULL);
+ d_diff_filters = std::vector<filter::kernel::fir_filter_ccf*>(
+ d_nfilters + 1,
+ NULL);
+
+ m = taps.size();
+ n = diff_taps.size();
+ d_taps.resize(d_nfilters+1);
+ d_diff_taps.resize(d_nfilters+1);
+ signed int taps_per_filter = static_cast<signed int>(d_taps_per_filter);
+
+ for (i = 0; i <= d_nfilters; i++) {
+ d_taps[i] = std::vector<float>(d_taps_per_filter, 0.0f);
+ for (j = 0; j < taps_per_filter; j++) {
+ k = i+j*d_nfilters;
+ if (k < m)
+ d_taps[i][j] = taps[k];
+ }
+ d_filters[i] = new filter::kernel::fir_filter_ccf(1, d_taps[i]);
+ if (d_filters[i] == NULL)
+ throw std::runtime_error("unable to create fir_filter_ccf");
+
+ if (not d_derivative)
+ continue;
+
+ d_diff_taps[i] = std::vector<float>(d_taps_per_filter, 0.0f);
+ for (j = 0; j < taps_per_filter; j++) {
+ k = i+j*d_nfilters;
+ if (k < n)
+ d_diff_taps[i][j] = diff_taps[k];
+ }
+ d_diff_filters[i] = new filter::kernel::fir_filter_ccf(
+ 1,
+ d_diff_taps[i]);
+ if (d_diff_filters[i] == NULL)
+ throw std::runtime_error("unable to create fir_filter_ccf");
+ }
+ }
+
+ interp_resampler_pfb_mf_ccf::~interp_resampler_pfb_mf_ccf()
+ {
+ for (int i = 0; i <= d_nfilters; i++) {
+ delete d_filters[i];
+ if (d_derivative)
+ delete d_diff_filters[i];
+ }
+ }
+
+ gr_complex
+ interp_resampler_pfb_mf_ccf::interpolate(const gr_complex input[],
+ float mu) const
+ {
+ int arm = static_cast<int>(rint(mu * d_nfilters));
+
+ if (arm < 0 or arm > d_nfilters)
+ throw std::runtime_error("interp_resampler_pfb_mf_ccf: mu is not "
+ "in the range [0.0, 1.0]");
+
+ return d_filters[arm]->filter(input);
+ }
+
+ gr_complex
+ interp_resampler_pfb_mf_ccf::differentiate(const gr_complex input[],
+ float mu) const
+ {
+ int arm = static_cast<int>(rint(mu * d_nfilters));
+
+ if (arm < 0 or arm > d_nfilters)
+ throw std::runtime_error("interp_resampler_pfb_mf_ccf: mu is not "
+ "in the range [0.0, 1.0]");
+
+ return d_diff_filters[arm]->filter(input);
+ }
+
+ unsigned int
+ interp_resampler_pfb_mf_ccf::ntaps() const
+ {
+ return d_taps_per_filter;
+ }
+
+ /*************************************************************************/
+
+ interp_resampler_pfb_mf_fff::interp_resampler_pfb_mf_fff(
+ const std::vector<float> &taps,
+ int nfilts,
+ bool derivative)
+ : interpolating_resampler_fff(IR_PFB_MF, derivative),
+ d_nfilters(nfilts),
+ d_taps_per_filter(static_cast<unsigned int>(
+ ceil( static_cast<double>(taps.size())
+ / static_cast<double>(nfilts )))),
+ d_filters(),
+ d_diff_filters(),
+ d_taps(),
+ d_diff_taps()
+ {
+ if (d_nfilters <= 1)
+ throw std::invalid_argument("interpolating_resampler_pfb_mf_fff: "
+ "number of polyphase filter arms "
+ "must be greater than 1");
+ if (taps.size() < static_cast<unsigned int>(d_nfilters))
+ throw std::invalid_argument("interpolating_resampler_pfb_mf_fff: "
+ "length of the prototype filter taps"
+ " must be greater than or equal to "
+ "the number of polyphase filter arms.");
+
+ // Create a derivative filter from the provided taps
+
+ // First create a truncated ideal differentiator filter
+ int ideal_diff_filt_len = 3; // Must be odd; rest of init assumes odd.
+ std::vector<float> ideal_diff_taps(ideal_diff_filt_len, 0.0f);
+ int i, n;
+ n = ideal_diff_taps.size()/2;
+ for (i = -n; i < 0; i++) {
+ ideal_diff_taps[i+n] = (-i & 1) == 1 ? -1.0f/i : 1.0f/i;
+ ideal_diff_taps[n-i] = -ideal_diff_taps[i+n];
+ }
+ ideal_diff_taps[n] = 0.0f;
+
+ // Perform linear convolution of prototype filter taps and the truncated
+ // ideal differentiator taps to generate a derivative matched filter.
+ // N.B. the truncated ideal differentiator taps must have an odd length
+ int j, k, l, m;
+ m = ideal_diff_taps.size();
+ n = taps.size();
+ l = m + n - 1; // length of convolution
+ std::deque<float> diff_taps(l, 0.0f);
+ for (i = 0; i < l; i++) {
+ for (j = 0; j < m; j++) {
+ k = i + j - (m - 1);
+ if (k < 0 or k >= n)
+ continue;
+ diff_taps[i] += ideal_diff_taps[(m - 1) - j] * taps[k];
+ }
+ }
+
+ // Trim the convolution so the prototype derivative filter is the same
+ // length as the passed in prototype filter taps.
+ n = ideal_diff_taps.size()/2;
+ for (i = 0; i < n; i++) {
+ diff_taps.pop_back();
+ diff_taps.pop_front();
+ }
+
+ // Squash the differentiation noise spikes at the filter ends.
+ diff_taps[0] = 0.0f;
+ diff_taps[diff_taps.size()-1] = 0.0f;
+
+ // Normalize the prototype derviative filter gain to the number of
+ // filter arms
+ n = diff_taps.size();
+ float mag = 0.0f;
+ for (i = 0; i < n; i++)
+ mag += fabsf(diff_taps[i]);
+ for (i = 0; i < n; i++) {
+ diff_taps[i] *= d_nfilters/mag;
+ if (d_derivative and std::isnan(diff_taps[i]))
+ throw std::runtime_error("interpolating_resampler_pfb_mf_fff: "
+ "NaN error creating derivative filter."
+ );
+ }
+
+ // Create our polyphase filter arms for the steps from 0.0 to 1.0 from
+ // the prototype matched filter.
+ // N.B. We create an extra final row for an offset of 1.0, because it's
+ // easier than dealing with wrap around from 0.99... to 0.0 shifted
+ // by 1 tap.
+ d_filters = std::vector<filter::kernel::fir_filter_fff*>(d_nfilters + 1,
+ NULL);
+ d_diff_filters = std::vector<filter::kernel::fir_filter_fff*>(
+ d_nfilters + 1,
+ NULL);
+
+ m = taps.size();
+ n = diff_taps.size();
+ d_taps.resize(d_nfilters+1);
+ d_diff_taps.resize(d_nfilters+1);
+ signed int taps_per_filter = static_cast<signed int>(d_taps_per_filter);
+
+ for (i = 0; i <= d_nfilters; i++) {
+ d_taps[i] = std::vector<float>(d_taps_per_filter, 0.0f);
+ for (j = 0; j < taps_per_filter; j++) {
+ k = i+j*d_nfilters;
+ if (k < m)
+ d_taps[i][j] = taps[k];
+ }
+ d_filters[i] = new filter::kernel::fir_filter_fff(1, d_taps[i]);
+ if (d_filters[i] == NULL)
+ throw std::runtime_error("unable to create fir_filter_fff");
+
+ if (not d_derivative)
+ continue;
+
+ d_diff_taps[i] = std::vector<float>(d_taps_per_filter, 0.0f);
+ for (j = 0; j < taps_per_filter; j++) {
+ k = i+j*d_nfilters;
+ if (k < n)
+ d_diff_taps[i][j] = diff_taps[k];
+ }
+ d_diff_filters[i] = new filter::kernel::fir_filter_fff(
+ 1,
+ d_diff_taps[i]);
+ if (d_diff_filters[i] == NULL)
+ throw std::runtime_error("unable to create fir_filter_fff");
+ }
+ }
+
+ interp_resampler_pfb_mf_fff::~interp_resampler_pfb_mf_fff()
+ {
+ for (int i = 0; i <= d_nfilters; i++) {
+ delete d_filters[i];
+ if (d_derivative)
+ delete d_diff_filters[i];
+ }
+ }
+
+ float
+ interp_resampler_pfb_mf_fff::interpolate(const float input[],
+ float mu) const
+ {
+ int arm = static_cast<int>(rint(mu * d_nfilters));
+
+ if (arm < 0 or arm > d_nfilters)
+ throw std::runtime_error("interp_resampler_pfb_mf_fff: mu is not "
+ "in the range [0.0, 1.0]");
+
+ return d_filters[arm]->filter(input);
+ }
+
+ float
+ interp_resampler_pfb_mf_fff::differentiate(const float input[],
+ float mu) const
+ {
+ int arm = static_cast<int>(rint(mu * d_nfilters));
+
+ if (arm < 0 or arm > d_nfilters)
+ throw std::runtime_error("interp_resampler_pfb_mf_fff: mu is not "
+ "in the range [0.0, 1.0]");
+
+ return d_diff_filters[arm]->filter(input);
+ }
+
+ unsigned int
+ interp_resampler_pfb_mf_fff::ntaps() const
+ {
+ return d_taps_per_filter;
+ }
+
+ } /* namespace digital */
+} /* namespace gr */
diff --git a/gr-digital/lib/interpolating_resampler.h b/gr-digital/lib/interpolating_resampler.h
new file mode 100644
index 0000000000..cdd0d976ea
--- /dev/null
+++ b/gr-digital/lib/interpolating_resampler.h
@@ -0,0 +1,599 @@
+/* -*- c++ -*- */
+/*
+ * Copyright (C) 2017 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 file; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_DIGITAL_INTERPOLATING_RESAMPLER_H
+#define INCLUDED_DIGITAL_INTERPOLATING_RESAMPLER_H
+
+#include <gnuradio/gr_complex.h>
+#include <gnuradio/digital/interpolating_resampler_type.h>
+#include <vector>
+#include <gnuradio/filter/mmse_fir_interpolator_cc.h>
+#include <gnuradio/filter/mmse_fir_interpolator_ff.h>
+#include <gnuradio/filter/mmse_interp_differentiator_cc.h>
+#include <gnuradio/filter/mmse_interp_differentiator_ff.h>
+
+namespace gr {
+ namespace digital {
+
+ /*!
+ * \brief Base class for the composite interpolating resampler objects
+ * used by the symbol_synchronizer_xx blocks.
+ * \ingroup internal
+ *
+ * \details
+ * This is the base class for the composite interpolating resampler
+ * objects used by the symbol_synchronizer_xx blocks to provide a
+ * user selectable interpolating resampler type.
+ *
+ * This base class provides the enumeration type for the available
+ * types of interpolating resamplers.
+ *
+ * This base class also provides methods to to update, manage, and
+ * store the sample phase state of the interpolating resampler.
+ * The sample phase increments and phase state are assumed to be in
+ * units of samples, and a complete sample phase cycle is one sample.
+ */
+ class interpolating_resampler
+ {
+ public:
+
+ virtual ~interpolating_resampler() {}
+
+ /*!
+ * \brief Return the number of taps used in any single FIR filtering
+ * operation
+ */
+ virtual unsigned int ntaps() const = 0;
+
+ /*!
+ * \brief Return the current unwrapped interpolator samples phase in
+ * units of samples
+ */
+ virtual float phase() { return d_phase; }
+
+ /*!
+ * \brief Return the integral part of the current unwrapped
+ * interpolator sample phase in units of (whole) samples
+ */
+ virtual int phase_n() { return d_phase_n; }
+
+ /*!
+ * \brief Returns the fractional part of the current wrapped
+ * interpolator sample phase in units of samples from [0.0, 1.0).
+ */
+ virtual float phase_wrapped() { return d_phase_wrapped; }
+
+ /*!
+ * \brief Return the fractional part of the previous wrapped
+ * interpolator sample phase in units of samples from [0.0, 1.0).
+ */
+ virtual float prev_phase_wrapped() { return d_prev_phase_wrapped; }
+
+ /*!
+ * \brief Compute the next interpolator sample phase.
+ * \param increment The sample phase increment to the next interpolation
+ * point, in units of samples.
+ * \param phase The new interpolator sample phase, in units of
+ * samples.
+ * \param phase_n The integral part of the new interpolator sample
+ * phase, in units of samples.
+ * \param phase_wrapped The new wrapped interpolator sample phase,
+ * in units of samples.
+ */
+ virtual void next_phase(float increment,
+ float &phase,
+ int &phase_n,
+ float &phase_wrapped);
+
+ /*!
+ * \brief Advance the phase state to the next interpolator sample phase.
+ * \param increment The sample phase increment to the next interpolation
+ * point, in units of samples.
+ */
+ virtual void advance_phase(float increment);
+
+ /*!
+ * \brief Revert the phase state to the previous interpolator sample
+ * phase.
+ */
+ virtual void revert_phase();
+
+ /*!
+ * \brief Reset the phase state to the specified interpolator sample
+ * phase.
+ * \param phase The interpolator sample phase to which to reset,
+ * in units of samples.
+ */
+ virtual void sync_reset(float phase);
+
+ private:
+ enum ir_type d_type;
+
+ protected:
+ interpolating_resampler(enum ir_type type, bool derivative = false);
+
+ bool d_derivative;
+
+ float d_phase;
+ float d_phase_wrapped;
+ int d_phase_n;
+ float d_prev_phase;
+ float d_prev_phase_wrapped;
+ int d_prev_phase_n;
+ };
+
+ /*************************************************************************/
+
+ /*!
+ * \brief A complex input, complex output, composite interpolating resampler
+ * object used by the symbol_synchronizer_cc block.
+ * \ingroup internal
+ *
+ * \details
+ * This is the complex input, complex output composite interpolating
+ * resampler object used by the symbol_synchronizer_cc block to provide a
+ * user selectable interpolating resampler type.
+ *
+ * This class abstracts away the underlying interpolating resampler
+ * type implementation, so the the symbol_synchronizer_cc block need not
+ * be bothered with the underlying implementation after the object is
+ * instantiated.
+ */
+ class interpolating_resampler_ccf : public interpolating_resampler
+ {
+ public:
+ /*!
+ * \brief Create a complex input, complex output interpolating
+ * resampler object.
+ * \param type The underlying type implementation of the interpolating
+ * resampler.
+ * \param derivative True if an interpolating differentitator is
+ * requested to be initialized to obtain interpolated
+ * derivative samples as well.
+ * \param nfilts The number of polyphase filter bank arms. Only needed
+ * for some types.
+ * \param taps Prototype filter for the polyphase filter bank. Only
+ * needed for some types.
+ */
+ static interpolating_resampler_ccf *make(enum ir_type type,
+ bool derivative = false,
+ int nfilts = 32,
+ const std::vector<float> &taps
+ = std::vector<float>());
+
+ virtual ~interpolating_resampler_ccf() {};
+
+ /*!
+ * \brief Return an interpolated sample.
+ * \param input Array of input samples of length ntaps().
+ * \param mu Intersample phase in the range [0.0, 1.0] samples.
+ */
+ virtual gr_complex interpolate(const gr_complex input[],
+ float mu) const = 0;
+
+ /*!
+ * \brief Return an interpolated derivative sample.
+ * \param input Array of input samples of length ntaps().
+ * \param mu Intersample phase in the range [0.0, 1.0] samples.
+ */
+ virtual gr_complex differentiate(const gr_complex input[],
+ float mu) const = 0;
+
+ protected:
+ interpolating_resampler_ccf(enum ir_type type,
+ bool derivative = false)
+ : interpolating_resampler(type, derivative) {}
+ };
+
+ /*************************************************************************/
+
+ /*!
+ * \brief A float input, float output, composite interpolating resampler
+ * object used by the symbol_synchronizer_ff block.
+ * \ingroup internal
+ *
+ * \details
+ * This is the float input, float output composite interpolating
+ * resampler object used by the symbol_synchronizer_ff block to provide a
+ * user selectable interpolating resampler type.
+ *
+ * This class abstracts away the underlying interpolating resampler
+ * type implementation, so the the symbol_synchronizer_ff block need not
+ * be bothered with the underlying implementation after the object is
+ * instantiated.
+ */
+ class interpolating_resampler_fff : public interpolating_resampler
+ {
+ public:
+ /*!
+ * \brief Create a float input, float output interpolating
+ * resampler object.
+ * \param type The underlying type implementation of the interpolating
+ * resampler.
+ * \param derivative True if an interpolating differentitator is
+ * requested to be initialized to obtain interpolated
+ * derivative samples as well.
+ * \param nfilts The number of polyphase filter bank arms. Only needed
+ * for some types.
+ * \param taps Prototype filter for the polyphase filter bank. Only
+ * needed for some types.
+ */
+ static interpolating_resampler_fff *make(enum ir_type type,
+ bool derivative = false,
+ int nfilts = 32,
+ const std::vector<float> &taps
+ = std::vector<float>());
+
+ virtual ~interpolating_resampler_fff() {};
+
+ /*!
+ * \brief Return an interpolated sample.
+ * \param input Array of input samples of length ntaps().
+ * \param mu Intersample phase in the range [0.0, 1.0] samples.
+ */
+ virtual float interpolate(const float input[], float mu) const = 0;
+
+ /*!
+ * \brief Return an interpolated derivative sample.
+ * \param input Array of input samples of length ntaps().
+ * \param mu Intersample phase in the range [0.0, 1.0] samples.
+ */
+ virtual float differentiate(const float input[], float mu) const = 0;
+
+ protected:
+ interpolating_resampler_fff(enum ir_type type,
+ bool derivative = false)
+ : interpolating_resampler(type, derivative) {}
+ };
+
+ /*************************************************************************/
+
+ /*!
+ * \brief A complex input, complex output, interpolating resampler
+ * object using the MMSE interpolator filter bank.
+ * \ingroup internal
+ *
+ * \details
+ * This is the complex input, complex output, interpolating resampler
+ * object using the MMSE interpolator filter bank as its underlying
+ * polyphase filterbank interpolator.
+ */
+ class interp_resampler_mmse_8tap_cc : public interpolating_resampler_ccf
+ {
+ public:
+ /*!
+ * \brief Create a complex input, complex output, polyphase filter bank
+ * using the MMSE filter bank, interpolating resampler object.
+ * \param derivative True if an interpolating differentitator is
+ * requested to be initialized to obtain interpolated
+ * derivative samples as well.
+ */
+ interp_resampler_mmse_8tap_cc(bool derivative = false);
+ ~interp_resampler_mmse_8tap_cc();
+
+ /*!
+ * \brief Return the number of taps used in any single FIR filtering
+ * operation
+ */
+ unsigned int ntaps() const;
+
+ /*!
+ * \brief Return an interpolated sample.
+ * \param input Array of input samples of length ntaps().
+ * \param mu Intersample phase in the range [0.0, 1.0] samples.
+ */
+ gr_complex interpolate(const gr_complex input[], float mu) const;
+
+ /*!
+ * \brief Return an interpolated derivative sample.
+ * \param input Array of input samples of length ntaps().
+ * \param mu Intersample phase in the range [0.0, 1.0] samples.
+ */
+ gr_complex differentiate(const gr_complex input[], float mu) const;
+
+ private:
+ filter::mmse_fir_interpolator_cc *d_interp;
+ filter::mmse_interp_differentiator_cc *d_interp_diff;
+ };
+
+ /*!
+ * \brief A float input, float output, interpolating resampler
+ * object using the MMSE interpolator filter bank.
+ * \ingroup internal
+ *
+ * \details
+ * This is the float input, float output, interpolating resampler
+ * object using the MMSE interpolator filter bank as its underlying
+ * polyphase filterbank interpolator.
+ */
+ class interp_resampler_mmse_8tap_ff : public interpolating_resampler_fff
+ {
+ public:
+ /*!
+ * \brief Create a float input, float output, polyphase filter bank
+ * using the MMSE filter bank, interpolating resampler object.
+ * \param derivative True if an interpolating differentitator is
+ * requested to be initialized to obtain interpolated
+ * derivative samples as well.
+ */
+ interp_resampler_mmse_8tap_ff(bool derivative = false);
+ ~interp_resampler_mmse_8tap_ff();
+
+ /*!
+ * \brief Return the number of taps used in any single FIR filtering
+ * operation
+ */
+ unsigned int ntaps() const;
+
+ /*!
+ * \brief Return an interpolated sample.
+ * \param input Array of input samples of length ntaps().
+ * \param mu Intersample phase in the range [0.0, 1.0] samples.
+ */
+ float interpolate(const float input[], float mu) const;
+
+ /*!
+ * \brief Return an interpolated derivative sample.
+ * \param input Array of input samples of length ntaps().
+ * \param mu Intersample phase in the range [0.0, 1.0] samples.
+ */
+ float differentiate(const float input[], float mu) const;
+
+ private:
+ filter::mmse_fir_interpolator_ff *d_interp;
+ filter::mmse_interp_differentiator_ff *d_interp_diff;
+ };
+
+ /*************************************************************************/
+
+ /*!
+ * \brief A complex input, complex output, interpolating resampler
+ * object with a polyphase filter bank which uses the MMSE interpolator
+ * filter arms.
+ * \ingroup internal
+ *
+ * \details
+ * This is the complex input, complex output, interpolating resampler
+ * object with a polyphase filter bank which uses the MMSE interpolator
+ * filter arms. This class has the "advantage" that the number of arms
+ * used can be reduced from 128 (default) to 64, 32, 16, 8, 4, or 2.
+ */
+ class interp_resampler_pfb_no_mf_cc : public interpolating_resampler_ccf
+ {
+ public:
+ /*!
+ * \brief Create a complex input, complex output, polyphase filter bank
+ * using the MMSE filter bank, interpolating resampler object.
+ * \param nfilts The number of polyphase filter bank arms. Must be in
+ * {2, 4, 8, 16, 32, 64, 128 (default)}
+ * \param derivative True if an interpolating differentitator is
+ * requested to be initialized to obtain interpolated
+ * derivative samples as well.
+ */
+ interp_resampler_pfb_no_mf_cc(bool derivative = false,
+ int nfilts = 128);
+ ~interp_resampler_pfb_no_mf_cc();
+
+ /*!
+ * \brief Return the number of taps used in any single FIR filtering
+ * operation
+ */
+ unsigned int ntaps() const;
+
+ /*!
+ * \brief Return an interpolated sample.
+ * \param input Array of input samples of length ntaps().
+ * \param mu Intersample phase in the range [0.0, 1.0] samples.
+ */
+ gr_complex interpolate(const gr_complex input[], float mu) const;
+
+ /*!
+ * \brief Return an interpolated derivative sample.
+ * \param input Array of input samples of length ntaps().
+ * \param mu Intersample phase in the range [0.0, 1.0] samples.
+ */
+ gr_complex differentiate(const gr_complex input[], float mu) const;
+
+ private:
+ int d_nfilters;
+ std::vector<filter::kernel::fir_filter_ccf*> d_filters;
+ std::vector<filter::kernel::fir_filter_ccf*> d_diff_filters;
+ };
+
+ /*!
+ * \brief A float input, float output, interpolating resampler
+ * object with a polyphase filter bank which uses the MMSE interpolator
+ * filter arms.
+ * \ingroup internal
+ *
+ * \details
+ * This is the float input, float output, interpolating resampler
+ * object with a polyphase filter bank which uses the MMSE interpolator
+ * filter arms. This class has the "advantage" that the number of arms
+ * used can be reduced from 128 (default) to 64, 32, 16, 8, 4, or 2.
+ */
+ class interp_resampler_pfb_no_mf_ff : public interpolating_resampler_fff
+ {
+ public:
+ /*!
+ * \brief Create a float input, float output, polyphase filter bank
+ * using the MMSE filter bank, interpolating resampler object.
+ * \param nfilts The number of polyphase filter bank arms. Must be in
+ * {2, 4, 8, 16, 32, 64, 128 (default)}
+ * \param derivative True if an interpolating differentitator is
+ * requested to be initialized to obtain interpolated
+ * derivative samples as well.
+ */
+ interp_resampler_pfb_no_mf_ff(bool derivative = false,
+ int nfilts = 128);
+ ~interp_resampler_pfb_no_mf_ff();
+
+ /*!
+ * \brief Return the number of taps used in any single FIR filtering
+ * operation
+ */
+ unsigned int ntaps() const;
+
+ /*!
+ * \brief Return an interpolated sample.
+ * \param input Array of input samples of length ntaps().
+ * \param mu Intersample phase in the range [0.0, 1.0] samples.
+ */
+ float interpolate(const float input[], float mu) const;
+
+ /*!
+ * \brief Return an interpolated derivative sample.
+ * \param input Array of input samples of length ntaps().
+ * \param mu Intersample phase in the range [0.0, 1.0] samples.
+ */
+ float differentiate(const float input[], float mu) const;
+
+ private:
+ int d_nfilters;
+ std::vector<filter::kernel::fir_filter_fff*> d_filters;
+ std::vector<filter::kernel::fir_filter_fff*> d_diff_filters;
+ };
+
+ /*************************************************************************/
+
+ /*!
+ * \brief A complex input, complex output, interpolating resampler
+ * object with a polyphase filter bank with a user provided prototype
+ * matched filter.
+ * \ingroup internal
+ *
+ * \details
+ * This is the complex input, complex output, interpolating resampler
+ * object with a polyphase filter bank with a user provided protoype
+ * matched filter. The prototype matched filter must be designed at a
+ * rate of nfilts times the output rate.
+ */
+ class interp_resampler_pfb_mf_ccf : public interpolating_resampler_ccf
+ {
+ public:
+ /*!
+ * \brief Create a complex input, complex output, polyphase filter bank,
+ * with matched filter, interpolating resampler object.
+ * \param taps Prototype filter for the polyphase filter bank.
+ * \param nfilts The number of polyphase filter bank arms.
+ * \param derivative True if an interpolating differentitator is
+ * requested to be initialized to obtain interpolated
+ * derivative samples as well.
+ */
+ interp_resampler_pfb_mf_ccf(const std::vector<float> &taps,
+ int nfilts = 32,
+ bool derivative = false);
+ ~interp_resampler_pfb_mf_ccf();
+
+ /*!
+ * \brief Return the number of taps used in any single FIR filtering
+ * operation
+ */
+ unsigned int ntaps() const;
+
+ /*!
+ * \brief Return an interpolated sample.
+ * \param input Array of input samples of length ntaps().
+ * \param mu Intersample phase in the range [0.0, 1.0] samples.
+ */
+ gr_complex interpolate(const gr_complex input[], float mu) const;
+
+ /*!
+ * \brief Return an interpolated derivative sample.
+ * \param input Array of input samples of length ntaps().
+ * \param mu Intersample phase in the range [0.0, 1.0] samples.
+ */
+ gr_complex differentiate(const gr_complex input[], float mu) const;
+
+ private:
+ int d_nfilters;
+ const unsigned int d_taps_per_filter;
+ std::vector<filter::kernel::fir_filter_ccf*> d_filters;
+ std::vector<filter::kernel::fir_filter_ccf*> d_diff_filters;
+
+ std::vector< std::vector<float> > d_taps;
+ std::vector< std::vector<float> > d_diff_taps;
+ };
+
+ /*!
+ * \brief A float input, float output, interpolating resampler
+ * object with a polyphase filter bank with a user provided prototype
+ * matched filter.
+ * \ingroup internal
+ *
+ * \details
+ * This is the float input, float output, interpolating resampler
+ * object with a polyphase filter bank with a user provided protoype
+ * matched filter. The prototype matched filter must be designed at a
+ * rate of nfilts times the output rate.
+ */
+ class interp_resampler_pfb_mf_fff : public interpolating_resampler_fff
+ {
+ public:
+ /*!
+ * \brief Create a float input, float output, polyphase filter bank,
+ * with matched filter, interpolating resampler object.
+ * \param taps Prototype filter for the polyphase filter bank.
+ * \param nfilts The number of polyphase filter bank arms.
+ * \param derivative True if an interpolating differentitator is
+ * requested to be initialized to obtain interpolated
+ * derivative samples as well.
+ */
+ interp_resampler_pfb_mf_fff(const std::vector<float> &taps,
+ int nfilts = 32,
+ bool derivative = false);
+ ~interp_resampler_pfb_mf_fff();
+
+ /*!
+ * \brief Return the number of taps used in any single FIR filtering
+ * operation
+ */
+ unsigned int ntaps() const;
+
+ /*!
+ * \brief Return an interpolated sample.
+ * \param input Array of input samples of length ntaps().
+ * \param mu Intersample phase in the range [0.0, 1.0] samples.
+ */
+ float interpolate(const float input[], float mu) const;
+
+ /*!
+ * \brief Return an interpolated derivative sample.
+ * \param input Array of input samples of length ntaps().
+ * \param mu Intersample phase in the range [0.0, 1.0] samples.
+ */
+ float differentiate(const float input[], float mu) const;
+
+ private:
+ int d_nfilters;
+ const unsigned int d_taps_per_filter;
+ std::vector<filter::kernel::fir_filter_fff*> d_filters;
+ std::vector<filter::kernel::fir_filter_fff*> d_diff_filters;
+
+ std::vector< std::vector<float> > d_taps;
+ std::vector< std::vector<float> > d_diff_taps;
+ };
+
+ } /* namespace digital */
+} /* namespace gr */
+
+#endif /* INCLUDED_DIGITAL_INTERPOLATING_RESAMPLER_H */
diff --git a/gr-digital/lib/symbol_sync_cc_impl.cc b/gr-digital/lib/symbol_sync_cc_impl.cc
new file mode 100644
index 0000000000..3df84be134
--- /dev/null
+++ b/gr-digital/lib/symbol_sync_cc_impl.cc
@@ -0,0 +1,650 @@
+/* -*- c++ -*- */
+/*
+ * Copyright (C) 2017 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 "symbol_sync_cc_impl.h"
+#include <boost/math/common_factor.hpp>
+#include <gnuradio/io_signature.h>
+#include <gnuradio/math.h>
+#include <stdexcept>
+
+namespace gr {
+ namespace digital {
+
+ symbol_sync_cc::sptr
+ symbol_sync_cc::make(enum ted_type detector_type,
+ float sps,
+ float loop_bw,
+ float damping_factor,
+ float max_deviation,
+ int osps,
+ constellation_sptr slicer,
+ ir_type interp_type,
+ int n_filters,
+ const std::vector<float> &taps)
+ {
+ return gnuradio::get_initial_sptr
+ (new symbol_sync_cc_impl(detector_type,
+ sps,
+ loop_bw,
+ damping_factor,
+ max_deviation,
+ osps,
+ slicer,
+ interp_type,
+ n_filters,
+ taps));
+ }
+
+ symbol_sync_cc_impl::symbol_sync_cc_impl(
+ enum ted_type detector_type,
+ float sps,
+ float loop_bw,
+ float damping_factor,
+ float max_deviation,
+ int osps,
+ constellation_sptr slicer,
+ ir_type interp_type,
+ int n_filters,
+ const std::vector<float> &taps)
+ : block("symbol_sync_cc",
+ io_signature::make(1, 1, sizeof(gr_complex)),
+ io_signature::makev(1, 4, std::vector<int>(4, sizeof(float)))),
+ d_ted(NULL),
+ d_interp(NULL),
+ d_inst_output_period(sps / static_cast<float>(osps)),
+ d_inst_clock_period(sps),
+ d_avg_clock_period(sps),
+ d_osps(static_cast<float>(osps)),
+ d_osps_n(osps),
+ d_tags(),
+ d_new_tags(),
+ d_time_est_key(pmt::intern("time_est")),
+ d_clock_est_key(pmt::intern("clock_est")),
+ d_noutputs(1),
+ d_out_error(NULL),
+ d_out_instantaneous_clock_period(NULL),
+ d_out_average_clock_period(NULL)
+ {
+ // Brute force fix of the output io_signature, because I can't get
+ // an anonymous std::vector<int>() rvalue, with a const expression
+ // initializing the vector, to work. Lvalues seem to make everything
+ // better.
+ int output_io_sizes[4] = {
+ sizeof(gr_complex),
+ sizeof(float), sizeof(float), sizeof(float)
+ };
+ std::vector<int> output_io_sizes_vector(&output_io_sizes[0],
+ &output_io_sizes[4]);
+ set_output_signature(io_signature::makev(1, 4, output_io_sizes_vector));
+
+ if (sps <= 1.0f)
+ throw std::out_of_range("nominal samples per symbol must be > 1");
+
+ if (osps < 1)
+ throw std::out_of_range("output samples per symbol must be > 0");
+
+ // Timing Error Detector
+ d_ted = timing_error_detector::make(detector_type, slicer);
+ if (d_ted == NULL)
+ throw std::runtime_error("unable to create timing_error_detector");
+
+ // Interpolating Resampler
+ d_interp = interpolating_resampler_ccf::make(interp_type,
+ d_ted->needs_derivative(),
+ n_filters, taps);
+ if (d_interp == NULL)
+ throw std::runtime_error("unable to create interpolating_resampler_ccf");
+
+ // Block Internal Clocks
+ d_interps_per_symbol_n = boost::math::lcm(d_ted->inputs_per_symbol(),
+ d_osps_n);
+ d_interps_per_ted_input_n =
+ d_interps_per_symbol_n / d_ted->inputs_per_symbol();
+ d_interps_per_output_sample_n =
+ d_interps_per_symbol_n / d_osps_n;
+
+ d_interps_per_symbol = static_cast<float>(d_interps_per_symbol_n);
+ d_interps_per_ted_input = static_cast<float>(d_interps_per_ted_input_n);
+
+ d_interp_clock = d_interps_per_symbol_n - 1;
+ sync_reset_internal_clocks();
+ d_inst_interp_period = d_inst_clock_period / d_interps_per_symbol;
+
+ if (d_interps_per_symbol > sps)
+ GR_LOG_WARN(d_logger,
+ boost::format("block performing more interopolations per "
+ "symbol (%3f) than input samples per symbol"
+ "(%3f). Consider reducing osps or "
+ "increasing sps") % d_interps_per_symbol
+ % sps);
+
+ // Symbol Clock Tracking and Estimation
+ d_clock = new clock_tracking_loop(loop_bw,
+ sps + max_deviation,
+ sps - max_deviation,
+ sps,
+ damping_factor);
+
+ // Timing Error Detector
+ d_ted->sync_reset();
+
+ // Interpolating Resampler
+ d_interp->sync_reset(sps);
+
+ // Tag Propagation and Clock Tracking Reset/Resync
+ set_relative_rate (d_osps / sps);
+ set_tag_propagation_policy(TPP_DONT);
+ d_filter_delay = (d_interp->ntaps() + 1) / 2;
+
+ set_output_multiple(d_osps_n);
+ }
+
+ symbol_sync_cc_impl::~symbol_sync_cc_impl()
+ {
+ delete d_ted;
+ delete d_interp;
+ delete d_clock;
+ }
+
+ //
+ // Block Internal Clocks
+ //
+ void
+ symbol_sync_cc_impl::update_internal_clock_outputs()
+ {
+ // a d_interp_clock boolean output would always be true.
+ d_ted_input_clock = (d_interp_clock % d_interps_per_ted_input_n == 0);
+ d_output_sample_clock =
+ (d_interp_clock % d_interps_per_output_sample_n == 0);
+ d_symbol_clock = (d_interp_clock % d_interps_per_symbol_n == 0);
+ }
+
+ void
+ symbol_sync_cc_impl::advance_internal_clocks()
+ {
+ d_interp_clock = (d_interp_clock + 1) % d_interps_per_symbol_n;
+ update_internal_clock_outputs();
+ }
+
+ void
+ symbol_sync_cc_impl::revert_internal_clocks()
+ {
+ if (d_interp_clock == 0)
+ d_interp_clock = d_interps_per_symbol_n - 1;
+ else
+ d_interp_clock--;
+ update_internal_clock_outputs();
+ }
+
+ void
+ symbol_sync_cc_impl::sync_reset_internal_clocks()
+ {
+ d_interp_clock = d_interps_per_symbol_n - 1;
+ update_internal_clock_outputs();
+ }
+
+ //
+ // Tag Propagation and Clock Tracking Reset/Resync
+ //
+ void
+ symbol_sync_cc_impl::collect_tags(uint64_t nitems_rd, int count)
+ {
+ // Get all the tags in offset order
+ // d_new_tags is used to look for time_est and clock_est tags.
+ // d_tags is used for manual tag propagation.
+ d_new_tags.clear();
+ get_tags_in_range(d_new_tags, 0, nitems_rd, nitems_rd + count);
+ std::sort(d_new_tags.begin(), d_new_tags.end(), tag_t::offset_compare);
+ d_tags.insert(d_tags.end(), d_new_tags.begin(), d_new_tags.end());
+ std::sort(d_tags.begin(), d_tags.end(), tag_t::offset_compare);
+ }
+
+ bool
+ symbol_sync_cc_impl::find_sync_tag(uint64_t nitems_rd, int iidx,
+ int distance,
+ uint64_t &tag_offset,
+ float &timing_offset,
+ float &clock_period)
+ {
+ bool found;
+ uint64_t soffset, eoffset;
+ std::vector<tag_t>::iterator t;
+ std::vector<tag_t>::iterator t2;
+
+ // PLL Reset/Resynchronization to time_est & clock_est tags
+ //
+ // Look for a time_est tag between the current interpolated input sample
+ // and the next predicted interpolated input sample. (both rounded up)
+ soffset = nitems_rd + d_filter_delay + static_cast<uint64_t>(iidx + 1);
+ eoffset = soffset + distance;
+ found = false;
+ for (t = d_new_tags.begin();
+ t != d_new_tags.end();
+ t = d_new_tags.erase(t)) {
+
+ if (t->offset > eoffset) // search finished
+ break;
+
+ if (t->offset < soffset) // tag is in the past of what we care about
+ continue;
+
+ if (not pmt::eq(t->key, d_time_est_key) and // not a time_est tag
+ not pmt::eq(t->key, d_clock_est_key) ) // not a clock_est tag
+ continue;
+
+ found = true;
+ tag_offset = t->offset;
+ if (pmt::eq(t->key, d_time_est_key)) {
+ // got a time_est tag
+ timing_offset = static_cast<float>(pmt::to_double(t->value));
+ // next instantaneous clock period estimate will be nominal
+ clock_period = d_clock->get_nom_avg_period();
+
+ // Look for a clock_est tag at the same offset,
+ // as we prefer clock_est tags
+ for (t2 = ++t; t2 != d_new_tags.end(); ++t2) {
+ if (t2->offset > t->offset) // search finished
+ break;
+ if (not pmt::eq(t->key, d_clock_est_key)) // not a clock_est
+ continue;
+ // Found a clock_est tag at the same offset
+ tag_offset = t2->offset;
+ timing_offset = static_cast<float>(
+ pmt::to_double(pmt::tuple_ref(t2->value, 0)));
+ clock_period = static_cast<float>(
+ pmt::to_double(pmt::tuple_ref(t2->value, 1)));
+ break;
+ }
+ } else {
+ // got a clock_est tag
+ timing_offset = static_cast<float>(
+ pmt::to_double(pmt::tuple_ref(t->value, 0)));
+ clock_period = static_cast<float>(
+ pmt::to_double(pmt::tuple_ref(t->value, 1)));
+ }
+
+ if (not(timing_offset >= -1.0f and timing_offset <= 1.0f)) {
+ // the time_est/clock_est tag's payload is invalid
+ GR_LOG_WARN(d_logger,
+ boost::format("ignoring time_est/clock_est tag with"
+ " value %.2f, outside of allowed "
+ "range [-1.0, 1.0]") % timing_offset);
+ found = false;
+ continue;
+ }
+
+ if (t->offset == soffset and timing_offset < 0.0f) {
+ // already handled times earlier than this previously
+ found = false;
+ continue;
+ }
+
+ if (t->offset == eoffset and timing_offset >= 0.0f) {
+ // handle times greater than this later
+ found = false;
+ break;
+ }
+
+ if (found == true)
+ break;
+ }
+ return found;
+ }
+
+ void
+ symbol_sync_cc_impl::propagate_tags(uint64_t nitems_rd, int iidx,
+ float iidx_fraction,
+ float inst_output_period,
+ uint64_t nitems_wr, int oidx)
+ {
+ // Tag Propagation
+ //
+ // Onto this output sample, place all the remaining tags that
+ // came before the interpolated input sample, and all the tags
+ // on and after the interpolated input sample, up to half way to
+ // the next output sample.
+
+ uint64_t mid_period_offset = nitems_rd + d_filter_delay
+ + static_cast<uint64_t>(iidx)
+ + static_cast<uint64_t>(llroundf(iidx_fraction
+ + inst_output_period/2.0f));
+
+ uint64_t output_offset = nitems_wr + static_cast<uint64_t>(oidx);
+
+ int i;
+ std::vector<tag_t>::iterator t;
+ for (t = d_tags.begin();
+ t != d_tags.end() and t->offset <= mid_period_offset;
+ t = d_tags.erase(t)) {
+ t->offset = output_offset;
+ for (i = 0; i < d_noutputs; i++)
+ add_item_tag(i, *t);
+ }
+ }
+
+ void
+ symbol_sync_cc_impl::save_expiring_tags(uint64_t nitems_rd,
+ int consumed)
+ {
+ // Deferred Tag Propagation
+ //
+ // Only save away input tags that will not be available
+ // in the next call to general_work(). Otherwise we would
+ // create duplicate tags next time around.
+ // Tags that have already been propagated, have already been erased
+ // from d_tags.
+
+ uint64_t consumed_offset = nitems_rd + static_cast<uint64_t>(consumed);
+ std::vector<tag_t>::iterator t;
+
+ for (t = d_tags.begin(); t != d_tags.end(); ) {
+ if (t->offset < consumed_offset)
+ ++t;
+ else
+ t = d_tags.erase(t);
+ }
+ }
+
+ //
+ // Optional Diagnostic Outputs
+ //
+ void
+ symbol_sync_cc_impl::setup_optional_outputs(
+ gr_vector_void_star &output_items)
+ {
+ d_noutputs = output_items.size();
+ d_out_error = NULL;
+ d_out_instantaneous_clock_period = NULL;
+ d_out_average_clock_period = NULL;
+
+ if (d_noutputs < 2)
+ return;
+ d_out_error = (float *) output_items[1];
+
+ if (d_noutputs < 3)
+ return;
+ d_out_instantaneous_clock_period = (float *) output_items[2];
+
+ if (d_noutputs < 4)
+ return;
+ d_out_average_clock_period = (float *) output_items[3];
+ }
+
+ void
+ symbol_sync_cc_impl::emit_optional_output(int oidx,
+ float error,
+ float inst_clock_period,
+ float avg_clock_period)
+ {
+ if (d_noutputs < 2)
+ return;
+ d_out_error[oidx] = error;
+
+ if (d_noutputs < 3)
+ return;
+ d_out_instantaneous_clock_period[oidx] = inst_clock_period;
+
+ if (d_noutputs < 4)
+ return;
+ d_out_average_clock_period[oidx] = avg_clock_period;
+ }
+
+ void
+ symbol_sync_cc_impl::forecast(int noutput_items,
+ gr_vector_int &ninput_items_required)
+ {
+ unsigned ninputs = ninput_items_required.size();
+
+ // The '+ 2' in the expression below is an effort to always have at
+ // least one output sample, even if the main loop decides it has to
+ // revert one computed sample and wait for the next call to
+ // general_work().
+ // The d_clock->get_max_avg_period() is also an effort to do the same,
+ // in case we have the worst case allowable clock timing deviation on
+ // input.
+ int answer = static_cast<int>(
+ ceilf(static_cast<float>(noutput_items + 2)
+ * d_clock->get_max_avg_period() / d_osps))
+ + static_cast<int>(d_interp->ntaps());
+
+ for(unsigned i = 0; i < ninputs; i++)
+ ninput_items_required[i] = answer;
+ }
+
+ int
+ symbol_sync_cc_impl::general_work(int noutput_items,
+ gr_vector_int &ninput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+ {
+ // max input to consume
+ int ni = ninput_items[0] - static_cast<int>(d_interp->ntaps());
+ if (ni <= 0)
+ return 0;
+
+ const gr_complex *in = (const gr_complex *)input_items[0];
+ gr_complex *out = (gr_complex *)output_items[0];
+
+ setup_optional_outputs(output_items);
+
+ int ii = 0; // input index
+ int oo = 0; // output index
+ gr_complex interp_output;
+ gr_complex interp_derivative = gr_complex(0.0f, 0.0f);
+ float error;
+ float look_ahead_phase = 0.0f;
+ int look_ahead_phase_n = 0;
+ float look_ahead_phase_wrapped = 0.0f;
+
+ uint64_t nitems_rd = nitems_read(0);
+ uint64_t nitems_wr = nitems_written(0);
+ uint64_t sync_tag_offset;
+ float sync_timing_offset;
+ float sync_clock_period;
+
+ // Tag Propagation and Symbol Clock Tracking Reset/Resync
+ collect_tags(nitems_rd, ni);
+
+ while (oo < noutput_items) {
+ // Block Internal Clocks
+ advance_internal_clocks();
+
+ // Symbol Clock and Interpolator Positioning & Alignment
+ interp_output = d_interp->interpolate(&in[ii],
+ d_interp->phase_wrapped());
+ if (output_sample_clock())
+ out[oo] = interp_output;
+
+ // Timing Error Detector
+ if (ted_input_clock()) {
+ if (d_ted->needs_derivative())
+ interp_derivative =
+ d_interp->differentiate(&in[ii], d_interp->phase_wrapped());
+ d_ted->input(interp_output, interp_derivative);
+ }
+ if (symbol_clock() and d_ted->needs_lookahead()) {
+ // N.B. symbol_clock() == true implies ted_input_clock() == true
+ // N.B. symbol_clock() == true implies output_sample_clock() == true
+
+ // We must interpolate ahead to the *next* ted_input_clock, so the
+ // ted will compute the error for *this* symbol.
+ d_interp->next_phase(d_interps_per_ted_input
+ * (d_clock->get_inst_period()
+ / d_interps_per_symbol),
+ look_ahead_phase,
+ look_ahead_phase_n,
+ look_ahead_phase_wrapped);
+
+ if (ii + look_ahead_phase_n >= ni) {
+ // We can't compute the look ahead interpolated output
+ // because there's not enough input.
+ // Revert the actions we took in the loop so far, and bail out.
+
+ // Symbol Clock Tracking and Estimation
+ // We haven't advanced the clock loop yet; no revert needed.
+
+ // Timing Error Detector
+ d_ted->revert(true); // keep the error value; it's still good
+
+ // Symbol Clock and Interpolator Positioning & Alignment
+ // Nothing to do
+
+ // Block Internal Clocks
+ revert_internal_clocks();
+ break;
+ }
+ // Give the ted the look ahead input that it needs to compute
+ // the error for *this* symbol.
+ interp_output = d_interp->interpolate(&in[ii + look_ahead_phase_n],
+ look_ahead_phase_wrapped);
+ if (d_ted->needs_derivative())
+ interp_derivative =
+ d_interp->differentiate(&in[ii + look_ahead_phase_n],
+ look_ahead_phase_wrapped);
+ d_ted->input_lookahead(interp_output, interp_derivative);
+ }
+ error = d_ted->error();
+
+ if (symbol_clock()) {
+ // Symbol Clock Tracking and Estimation
+ d_clock->advance_loop(error);
+ d_inst_clock_period = d_clock->get_inst_period();
+ d_avg_clock_period = d_clock->get_avg_period();
+ d_clock->phase_wrap();
+ d_clock->period_limit();
+
+ // Symbol Clock and Interpolator Positioning & Alignment
+ d_inst_interp_period = d_inst_clock_period / d_interps_per_symbol;
+
+ // Tag Propagation
+ d_inst_output_period = d_inst_clock_period / d_osps;
+ }
+
+ // Symbol Clock, Interpolator Positioning & Alignment, and
+ // Tag Propagation
+ if (symbol_clock()) {
+ // N.B. symbol_clock() == true implies ted_input_clock() == true
+ // N.B. symbol_clock() == true implies output_sample_clock() == true
+
+ // This check and revert is needed either when
+ // a) the samples per symbol to get to the next symbol is greater
+ // than d_interp->ntaps() (normally 8); thus we would consume()
+ // more input than we were given to get there.
+ // b) we can't get to the next output so we can't do tag
+ // propagation properly.
+ d_interp->next_phase(d_inst_clock_period,
+ look_ahead_phase,
+ look_ahead_phase_n,
+ look_ahead_phase_wrapped);
+
+ if (ii + look_ahead_phase_n >= ni) {
+ // We can't move forward because there's not enough input.
+ // Revert the actions we took in the loop so far, and bail out.
+
+ // Symbol Clock Tracking and Estimation
+ d_clock->revert_loop();
+
+ // Timing Error Detector
+ d_ted->revert();
+
+ // Symbol Clock and Interpolator Positioning & Alignment
+ // Nothing to do;
+
+ // Block Internal Clocks
+ revert_internal_clocks();
+ break;
+ }
+ }
+
+ // Symbol Clock and Interpolator Positioning & Alignment
+ d_interp->advance_phase(d_inst_interp_period);
+
+ // Symbol Clock Tracking Reset/Resync to time_est and clock_est tags
+ if (find_sync_tag(nitems_rd, ii, d_interp->phase_n(), sync_tag_offset,
+ sync_timing_offset, sync_clock_period) == true ) {
+
+ // Block Internal Clocks
+ sync_reset_internal_clocks();
+
+ // Timing Error Detector
+ d_ted->sync_reset();
+ error = d_ted->error();
+
+ // Symbol Clock Tracking and Estimation
+
+ // NOTE: the + 1 below was determined empirically, but doesn't
+ // seem right on paper (maybe rounding in the computation of
+ // d_filter_delay is the culprit). Anyway, experiment trumps
+ // theory *every* time; so + 1 it is.
+ d_inst_clock_period = static_cast<float>(
+ static_cast<int>(sync_tag_offset - nitems_rd - d_filter_delay)
+ - ii + 1) + sync_timing_offset - d_interp->phase_wrapped();
+
+ d_clock->set_inst_period(d_inst_clock_period);
+ d_clock->set_avg_period(sync_clock_period);
+ d_avg_clock_period = d_clock->get_avg_period();
+ d_clock->set_phase(0.0f);
+
+ // Symbol Clock and Interpolator Positioning & Alignment
+ d_inst_interp_period = d_inst_clock_period;
+ d_interp->revert_phase();
+ d_interp->advance_phase(d_inst_interp_period);
+
+ // Tag Propagation
+ // Only needed if we reverted back to an output_sample_clock()
+ // as this is only used for tag propagation. Note that the
+ // next output will also be both an output_sample_clock() and a
+ // symbol_clock().
+ d_inst_output_period = d_inst_clock_period;
+ }
+
+ if (output_sample_clock()) {
+ // Diagnostic Output of Symbol Clock Tracking cycle results
+ emit_optional_output(oo, error, d_inst_clock_period,
+ d_avg_clock_period);
+ // Tag Propagation
+ propagate_tags(nitems_rd, ii,
+ d_interp->prev_phase_wrapped(), d_inst_output_period,
+ nitems_wr, oo);
+
+ // Symbol Clock and Interpolator Positioning & Alignment
+ oo++;
+ }
+
+ // Symbol Clock and Interpolator Positioning & Alignment
+ ii += d_interp->phase_n();
+ }
+
+ // Deferred Tag Propagation
+ save_expiring_tags(nitems_rd, ii);
+
+ // Symbol Clock and Interpolator Positioning & Alignment
+ consume_each(ii);
+ return oo;
+ }
+
+ } /* namespace digital */
+} /* namespace gr */
diff --git a/gr-digital/lib/symbol_sync_cc_impl.h b/gr-digital/lib/symbol_sync_cc_impl.h
new file mode 100644
index 0000000000..e0b85069d2
--- /dev/null
+++ b/gr-digital/lib/symbol_sync_cc_impl.h
@@ -0,0 +1,153 @@
+/* -*- c++ -*- */
+/*
+ * Copyright (C) 2017 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_DIGITAL_SYMBOL_SYNC_CC_IMPL_H
+#define INCLUDED_DIGITAL_SYMBOL_SYNC_CC_IMPL_H
+
+#include <gnuradio/digital/symbol_sync_cc.h>
+#include "clock_tracking_loop.h"
+#include "timing_error_detector.h"
+#include "interpolating_resampler.h"
+
+namespace gr {
+ namespace digital {
+
+ class symbol_sync_cc_impl : public symbol_sync_cc
+ {
+ public:
+ symbol_sync_cc_impl(enum ted_type detector_type,
+ float sps,
+ float loop_bw,
+ float damping_factor,
+ float max_deviation,
+ int osps,
+ constellation_sptr slicer,
+ ir_type interp_type,
+ int n_filters,
+ const std::vector<float> &taps);
+ ~symbol_sync_cc_impl();
+
+ void forecast(int noutput_items, gr_vector_int &ninput_items_required);
+ int general_work(int noutput_items,
+ gr_vector_int &ninput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items);
+
+ // Symbol Clock Tracking and Estimation
+ float loop_bandwidth() const { return d_clock->get_loop_bandwidth(); }
+ float damping_factor() const { return d_clock->get_damping_factor(); }
+ float alpha() const { return d_clock->get_alpha(); }
+ float beta() const { return d_clock->get_beta(); }
+
+ void set_loop_bandwidth (float omega_n_norm) {
+ d_clock->set_loop_bandwidth(omega_n_norm);
+ }
+ void set_damping_factor (float zeta) {
+ d_clock->set_damping_factor(zeta);
+ }
+ void set_alpha (float alpha) { d_clock->set_alpha(alpha); }
+ void set_beta (float beta) { d_clock->set_beta(beta); }
+
+ private:
+ // Timing Error Detector
+ timing_error_detector *d_ted;
+
+ // Symbol Clock Tracking and Estimation
+ clock_tracking_loop *d_clock;
+
+ // Interpolator and Interpolator Positioning and Alignment
+ interpolating_resampler_ccf *d_interp;
+
+ // Block Internal Clocks
+ // 4 clocks that run synchronously, aligned to the Symbol Clock:
+ // Interpolator Clock (always highest rate)
+ // Timing Error Detector Input Clock
+ // Output Sample Clock
+ // Symbol Clock (always lowest rate)
+ int d_interp_clock;
+ float d_inst_interp_period;
+
+ float d_interps_per_ted_input;
+ int d_interps_per_ted_input_n;
+ bool d_ted_input_clock;
+
+ int d_interps_per_output_sample_n;
+ bool d_output_sample_clock;
+ float d_inst_output_period;
+
+ float d_interps_per_symbol;
+ int d_interps_per_symbol_n;
+ bool d_symbol_clock;
+ float d_inst_clock_period;
+ float d_avg_clock_period;
+
+ // Block output
+ float d_osps;
+ int d_osps_n;
+
+ // Tag Propagation and Symbol Clock Tracking Reset/Resync
+ uint64_t d_filter_delay; // interpolator filter delay
+ std::vector<tag_t> d_tags;
+ std::vector<tag_t> d_new_tags;
+ pmt::pmt_t d_time_est_key;
+ pmt::pmt_t d_clock_est_key;
+
+ // Optional Diagnostic Outputs
+ int d_noutputs;
+ float *d_out_error;
+ float *d_out_instantaneous_clock_period;
+ float *d_out_average_clock_period;
+
+ // Block Internal Clocks
+ void update_internal_clock_outputs();
+ void advance_internal_clocks();
+ void revert_internal_clocks();
+ void sync_reset_internal_clocks();
+ bool ted_input_clock() { return d_ted_input_clock; }
+ bool output_sample_clock() { return d_output_sample_clock; }
+ bool symbol_clock() { return d_symbol_clock; }
+
+ // Tag Propagation and Clock Tracking Reset/Resync
+ void collect_tags(uint64_t nitems_rd, int count);
+ bool find_sync_tag(uint64_t nitems_rd, int iidx,
+ int distance,
+ uint64_t &tag_offset,
+ float &timing_offset,
+ float &clock_period);
+ void propagate_tags(uint64_t nitems_rd, int iidx,
+ float iidx_fraction,
+ float inst_output_period,
+ uint64_t nitems_wr, int oidx);
+ void save_expiring_tags(uint64_t nitems_rd, int consumed);
+
+ // Optional Diagnostic Outputs
+ void setup_optional_outputs(gr_vector_void_star &output_items);
+ void emit_optional_output(int oidx,
+ float error,
+ float inst_clock_period,
+ float avg_clock_period);
+ };
+
+ } /* namespace digital */
+} /* namespace gr */
+
+#endif /* INCLUDED_DIGITAL_SYMBOL_SYNC_CC_IMPL_H */
diff --git a/gr-digital/lib/symbol_sync_ff_impl.cc b/gr-digital/lib/symbol_sync_ff_impl.cc
new file mode 100644
index 0000000000..48509e4ba9
--- /dev/null
+++ b/gr-digital/lib/symbol_sync_ff_impl.cc
@@ -0,0 +1,650 @@
+/* -*- c++ -*- */
+/*
+ * Copyright (C) 2017 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 "symbol_sync_ff_impl.h"
+#include <boost/math/common_factor.hpp>
+#include <gnuradio/io_signature.h>
+#include <gnuradio/math.h>
+#include <stdexcept>
+
+namespace gr {
+ namespace digital {
+
+ symbol_sync_ff::sptr
+ symbol_sync_ff::make(enum ted_type detector_type,
+ float sps,
+ float loop_bw,
+ float damping_factor,
+ float max_deviation,
+ int osps,
+ constellation_sptr slicer,
+ ir_type interp_type,
+ int n_filters,
+ const std::vector<float> &taps)
+ {
+ return gnuradio::get_initial_sptr
+ (new symbol_sync_ff_impl(detector_type,
+ sps,
+ loop_bw,
+ damping_factor,
+ max_deviation,
+ osps,
+ slicer,
+ interp_type,
+ n_filters,
+ taps));
+ }
+
+ symbol_sync_ff_impl::symbol_sync_ff_impl(
+ enum ted_type detector_type,
+ float sps,
+ float loop_bw,
+ float damping_factor,
+ float max_deviation,
+ int osps,
+ constellation_sptr slicer,
+ ir_type interp_type,
+ int n_filters,
+ const std::vector<float> &taps)
+ : block("symbol_sync_ff",
+ io_signature::make(1, 1, sizeof(float)),
+ io_signature::makev(1, 4, std::vector<int>(4, sizeof(float)))),
+ d_ted(NULL),
+ d_interp(NULL),
+ d_inst_output_period(sps / static_cast<float>(osps)),
+ d_inst_clock_period(sps),
+ d_avg_clock_period(sps),
+ d_osps(static_cast<float>(osps)),
+ d_osps_n(osps),
+ d_tags(),
+ d_new_tags(),
+ d_time_est_key(pmt::intern("time_est")),
+ d_clock_est_key(pmt::intern("clock_est")),
+ d_noutputs(1),
+ d_out_error(NULL),
+ d_out_instantaneous_clock_period(NULL),
+ d_out_average_clock_period(NULL)
+ {
+ // Brute force fix of the output io_signature, because I can't get
+ // an anonymous std::vector<int>() rvalue, with a const expression
+ // initializing the vector, to work. Lvalues seem to make everything
+ // better.
+ int output_io_sizes[4] = {
+ sizeof(float),
+ sizeof(float), sizeof(float), sizeof(float)
+ };
+ std::vector<int> output_io_sizes_vector(&output_io_sizes[0],
+ &output_io_sizes[4]);
+ set_output_signature(io_signature::makev(1, 4, output_io_sizes_vector));
+
+ if (sps <= 1.0f)
+ throw std::out_of_range("nominal samples per symbol must be > 1");
+
+ if (osps < 1)
+ throw std::out_of_range("output samples per symbol must be > 0");
+
+ // Timing Error Detector
+ d_ted = timing_error_detector::make(detector_type, slicer);
+ if (d_ted == NULL)
+ throw std::runtime_error("unable to create timing_error_detector");
+
+ // Interpolating Resampler
+ d_interp = interpolating_resampler_fff::make(interp_type,
+ d_ted->needs_derivative(),
+ n_filters, taps);
+ if (d_interp == NULL)
+ throw std::runtime_error("unable to create interpolating_resampler_fff");
+
+ // Block Internal Clocks
+ d_interps_per_symbol_n = boost::math::lcm(d_ted->inputs_per_symbol(),
+ d_osps_n);
+ d_interps_per_ted_input_n =
+ d_interps_per_symbol_n / d_ted->inputs_per_symbol();
+ d_interps_per_output_sample_n =
+ d_interps_per_symbol_n / d_osps_n;
+
+ d_interps_per_symbol = static_cast<float>(d_interps_per_symbol_n);
+ d_interps_per_ted_input = static_cast<float>(d_interps_per_ted_input_n);
+
+ d_interp_clock = d_interps_per_symbol_n - 1;
+ sync_reset_internal_clocks();
+ d_inst_interp_period = d_inst_clock_period / d_interps_per_symbol;
+
+ if (d_interps_per_symbol > sps)
+ GR_LOG_WARN(d_logger,
+ boost::format("block performing more interopolations per "
+ "symbol (%3f) than input samples per symbol"
+ "(%3f). Consider reducing osps or "
+ "increasing sps") % d_interps_per_symbol
+ % sps);
+
+ // Symbol Clock Tracking and Estimation
+ d_clock = new clock_tracking_loop(loop_bw,
+ sps + max_deviation,
+ sps - max_deviation,
+ sps,
+ damping_factor);
+
+ // Timing Error Detector
+ d_ted->sync_reset();
+
+ // Interpolating Resampler
+ d_interp->sync_reset(sps);
+
+ // Tag Propagation and Clock Tracking Reset/Resync
+ set_relative_rate (d_osps / sps);
+ set_tag_propagation_policy(TPP_DONT);
+ d_filter_delay = (d_interp->ntaps() + 1) / 2;
+
+ set_output_multiple(d_osps_n);
+ }
+
+ symbol_sync_ff_impl::~symbol_sync_ff_impl()
+ {
+ delete d_ted;
+ delete d_interp;
+ delete d_clock;
+ }
+
+ //
+ // Block Internal Clocks
+ //
+ void
+ symbol_sync_ff_impl::update_internal_clock_outputs()
+ {
+ // a d_interp_clock boolean output would always be true.
+ d_ted_input_clock = (d_interp_clock % d_interps_per_ted_input_n == 0);
+ d_output_sample_clock =
+ (d_interp_clock % d_interps_per_output_sample_n == 0);
+ d_symbol_clock = (d_interp_clock % d_interps_per_symbol_n == 0);
+ }
+
+ void
+ symbol_sync_ff_impl::advance_internal_clocks()
+ {
+ d_interp_clock = (d_interp_clock + 1) % d_interps_per_symbol_n;
+ update_internal_clock_outputs();
+ }
+
+ void
+ symbol_sync_ff_impl::revert_internal_clocks()
+ {
+ if (d_interp_clock == 0)
+ d_interp_clock = d_interps_per_symbol_n - 1;
+ else
+ d_interp_clock--;
+ update_internal_clock_outputs();
+ }
+
+ void
+ symbol_sync_ff_impl::sync_reset_internal_clocks()
+ {
+ d_interp_clock = d_interps_per_symbol_n - 1;
+ update_internal_clock_outputs();
+ }
+
+ //
+ // Tag Propagation and Clock Tracking Reset/Resync
+ //
+ void
+ symbol_sync_ff_impl::collect_tags(uint64_t nitems_rd, int count)
+ {
+ // Get all the tags in offset order
+ // d_new_tags is used to look for time_est and clock_est tags.
+ // d_tags is used for manual tag propagation.
+ d_new_tags.clear();
+ get_tags_in_range(d_new_tags, 0, nitems_rd, nitems_rd + count);
+ std::sort(d_new_tags.begin(), d_new_tags.end(), tag_t::offset_compare);
+ d_tags.insert(d_tags.end(), d_new_tags.begin(), d_new_tags.end());
+ std::sort(d_tags.begin(), d_tags.end(), tag_t::offset_compare);
+ }
+
+ bool
+ symbol_sync_ff_impl::find_sync_tag(uint64_t nitems_rd, int iidx,
+ int distance,
+ uint64_t &tag_offset,
+ float &timing_offset,
+ float &clock_period)
+ {
+ bool found;
+ uint64_t soffset, eoffset;
+ std::vector<tag_t>::iterator t;
+ std::vector<tag_t>::iterator t2;
+
+ // PLL Reset/Resynchronization to time_est & clock_est tags
+ //
+ // Look for a time_est tag between the current interpolated input sample
+ // and the next predicted interpolated input sample. (both rounded up)
+ soffset = nitems_rd + d_filter_delay + static_cast<uint64_t>(iidx + 1);
+ eoffset = soffset + distance;
+ found = false;
+ for (t = d_new_tags.begin();
+ t != d_new_tags.end();
+ t = d_new_tags.erase(t)) {
+
+ if (t->offset > eoffset) // search finished
+ break;
+
+ if (t->offset < soffset) // tag is in the past of what we care about
+ continue;
+
+ if (not pmt::eq(t->key, d_time_est_key) and // not a time_est tag
+ not pmt::eq(t->key, d_clock_est_key) ) // not a clock_est tag
+ continue;
+
+ found = true;
+ tag_offset = t->offset;
+ if (pmt::eq(t->key, d_time_est_key)) {
+ // got a time_est tag
+ timing_offset = static_cast<float>(pmt::to_double(t->value));
+ // next instantaneous clock period estimate will be nominal
+ clock_period = d_clock->get_nom_avg_period();
+
+ // Look for a clock_est tag at the same offset,
+ // as we prefer clock_est tags
+ for (t2 = ++t; t2 != d_new_tags.end(); ++t2) {
+ if (t2->offset > t->offset) // search finished
+ break;
+ if (not pmt::eq(t->key, d_clock_est_key)) // not a clock_est
+ continue;
+ // Found a clock_est tag at the same offset
+ tag_offset = t2->offset;
+ timing_offset = static_cast<float>(
+ pmt::to_double(pmt::tuple_ref(t2->value, 0)));
+ clock_period = static_cast<float>(
+ pmt::to_double(pmt::tuple_ref(t2->value, 1)));
+ break;
+ }
+ } else {
+ // got a clock_est tag
+ timing_offset = static_cast<float>(
+ pmt::to_double(pmt::tuple_ref(t->value, 0)));
+ clock_period = static_cast<float>(
+ pmt::to_double(pmt::tuple_ref(t->value, 1)));
+ }
+
+ if (not(timing_offset >= -1.0f and timing_offset <= 1.0f)) {
+ // the time_est/clock_est tag's payload is invalid
+ GR_LOG_WARN(d_logger,
+ boost::format("ignoring time_est/clock_est tag with"
+ " value %.2f, outside of allowed "
+ "range [-1.0, 1.0]") % timing_offset);
+ found = false;
+ continue;
+ }
+
+ if (t->offset == soffset and timing_offset < 0.0f) {
+ // already handled times earlier than this previously
+ found = false;
+ continue;
+ }
+
+ if (t->offset == eoffset and timing_offset >= 0.0f) {
+ // handle times greater than this later
+ found = false;
+ break;
+ }
+
+ if (found == true)
+ break;
+ }
+ return found;
+ }
+
+ void
+ symbol_sync_ff_impl::propagate_tags(uint64_t nitems_rd, int iidx,
+ float iidx_fraction,
+ float inst_output_period,
+ uint64_t nitems_wr, int oidx)
+ {
+ // Tag Propagation
+ //
+ // Onto this output sample, place all the remaining tags that
+ // came before the interpolated input sample, and all the tags
+ // on and after the interpolated input sample, up to half way to
+ // the next output sample.
+
+ uint64_t mid_period_offset = nitems_rd + d_filter_delay
+ + static_cast<uint64_t>(iidx)
+ + static_cast<uint64_t>(llroundf(iidx_fraction
+ + inst_output_period/2.0f));
+
+ uint64_t output_offset = nitems_wr + static_cast<uint64_t>(oidx);
+
+ int i;
+ std::vector<tag_t>::iterator t;
+ for (t = d_tags.begin();
+ t != d_tags.end() and t->offset <= mid_period_offset;
+ t = d_tags.erase(t)) {
+ t->offset = output_offset;
+ for (i = 0; i < d_noutputs; i++)
+ add_item_tag(i, *t);
+ }
+ }
+
+ void
+ symbol_sync_ff_impl::save_expiring_tags(uint64_t nitems_rd,
+ int consumed)
+ {
+ // Deferred Tag Propagation
+ //
+ // Only save away input tags that will not be available
+ // in the next call to general_work(). Otherwise we would
+ // create duplicate tags next time around.
+ // Tags that have already been propagated, have already been erased
+ // from d_tags.
+
+ uint64_t consumed_offset = nitems_rd + static_cast<uint64_t>(consumed);
+ std::vector<tag_t>::iterator t;
+
+ for (t = d_tags.begin(); t != d_tags.end(); ) {
+ if (t->offset < consumed_offset)
+ ++t;
+ else
+ t = d_tags.erase(t);
+ }
+ }
+
+ //
+ // Optional Diagnostic Outputs
+ //
+ void
+ symbol_sync_ff_impl::setup_optional_outputs(
+ gr_vector_void_star &output_items)
+ {
+ d_noutputs = output_items.size();
+ d_out_error = NULL;
+ d_out_instantaneous_clock_period = NULL;
+ d_out_average_clock_period = NULL;
+
+ if (d_noutputs < 2)
+ return;
+ d_out_error = (float *) output_items[1];
+
+ if (d_noutputs < 3)
+ return;
+ d_out_instantaneous_clock_period = (float *) output_items[2];
+
+ if (d_noutputs < 4)
+ return;
+ d_out_average_clock_period = (float *) output_items[3];
+ }
+
+ void
+ symbol_sync_ff_impl::emit_optional_output(int oidx,
+ float error,
+ float inst_clock_period,
+ float avg_clock_period)
+ {
+ if (d_noutputs < 2)
+ return;
+ d_out_error[oidx] = error;
+
+ if (d_noutputs < 3)
+ return;
+ d_out_instantaneous_clock_period[oidx] = inst_clock_period;
+
+ if (d_noutputs < 4)
+ return;
+ d_out_average_clock_period[oidx] = avg_clock_period;
+ }
+
+ void
+ symbol_sync_ff_impl::forecast(int noutput_items,
+ gr_vector_int &ninput_items_required)
+ {
+ unsigned ninputs = ninput_items_required.size();
+
+ // The '+ 2' in the expression below is an effort to always have at
+ // least one output sample, even if the main loop decides it has to
+ // revert one computed sample and wait for the next call to
+ // general_work().
+ // The d_clock->get_max_avg_period() is also an effort to do the same,
+ // in case we have the worst case allowable clock timing deviation on
+ // input.
+ int answer = static_cast<int>(
+ ceilf(static_cast<float>(noutput_items + 2)
+ * d_clock->get_max_avg_period() / d_osps))
+ + static_cast<int>(d_interp->ntaps());
+
+ for(unsigned i = 0; i < ninputs; i++)
+ ninput_items_required[i] = answer;
+ }
+
+ int
+ symbol_sync_ff_impl::general_work(int noutput_items,
+ gr_vector_int &ninput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+ {
+ // max input to consume
+ int ni = ninput_items[0] - static_cast<int>(d_interp->ntaps());
+ if (ni <= 0)
+ return 0;
+
+ const float *in = (const float *)input_items[0];
+ float *out = (float *)output_items[0];
+
+ setup_optional_outputs(output_items);
+
+ int ii = 0; // input index
+ int oo = 0; // output index
+ float interp_output;
+ float interp_derivative = 0.0f;
+ float error;
+ float look_ahead_phase = 0.0f;
+ int look_ahead_phase_n = 0;
+ float look_ahead_phase_wrapped = 0.0f;
+
+ uint64_t nitems_rd = nitems_read(0);
+ uint64_t nitems_wr = nitems_written(0);
+ uint64_t sync_tag_offset;
+ float sync_timing_offset;
+ float sync_clock_period;
+
+ // Tag Propagation and Symbol Clock Tracking Reset/Resync
+ collect_tags(nitems_rd, ni);
+
+ while (oo < noutput_items) {
+ // Block Internal Clocks
+ advance_internal_clocks();
+
+ // Symbol Clock and Interpolator Positioning & Alignment
+ interp_output = d_interp->interpolate(&in[ii],
+ d_interp->phase_wrapped());
+ if (output_sample_clock())
+ out[oo] = interp_output;
+
+ // Timing Error Detector
+ if (ted_input_clock()) {
+ if (d_ted->needs_derivative())
+ interp_derivative =
+ d_interp->differentiate(&in[ii], d_interp->phase_wrapped());
+ d_ted->input(interp_output, interp_derivative);
+ }
+ if (symbol_clock() and d_ted->needs_lookahead()) {
+ // N.B. symbol_clock() == true implies ted_input_clock() == true
+ // N.B. symbol_clock() == true implies output_sample_clock() == true
+
+ // We must interpolate ahead to the *next* ted_input_clock, so the
+ // ted will compute the error for *this* symbol.
+ d_interp->next_phase(d_interps_per_ted_input
+ * (d_clock->get_inst_period()
+ / d_interps_per_symbol),
+ look_ahead_phase,
+ look_ahead_phase_n,
+ look_ahead_phase_wrapped);
+
+ if (ii + look_ahead_phase_n >= ni) {
+ // We can't compute the look ahead interpolated output
+ // because there's not enough input.
+ // Revert the actions we took in the loop so far, and bail out.
+
+ // Symbol Clock Tracking and Estimation
+ // We haven't advanced the clock loop yet; no revert needed.
+
+ // Timing Error Detector
+ d_ted->revert(true); // keep the error value; it's still good
+
+ // Symbol Clock and Interpolator Positioning & Alignment
+ // Nothing to do
+
+ // Block Internal Clocks
+ revert_internal_clocks();
+ break;
+ }
+ // Give the ted the look ahead input that it needs to compute
+ // the error for *this* symbol.
+ interp_output = d_interp->interpolate(&in[ii + look_ahead_phase_n],
+ look_ahead_phase_wrapped);
+ if (d_ted->needs_derivative())
+ interp_derivative =
+ d_interp->differentiate(&in[ii + look_ahead_phase_n],
+ look_ahead_phase_wrapped);
+ d_ted->input_lookahead(interp_output, interp_derivative);
+ }
+ error = d_ted->error();
+
+ if (symbol_clock()) {
+ // Symbol Clock Tracking and Estimation
+ d_clock->advance_loop(error);
+ d_inst_clock_period = d_clock->get_inst_period();
+ d_avg_clock_period = d_clock->get_avg_period();
+ d_clock->phase_wrap();
+ d_clock->period_limit();
+
+ // Symbol Clock and Interpolator Positioning & Alignment
+ d_inst_interp_period = d_inst_clock_period / d_interps_per_symbol;
+
+ // Tag Propagation
+ d_inst_output_period = d_inst_clock_period / d_osps;
+ }
+
+ // Symbol Clock, Interpolator Positioning & Alignment, and
+ // Tag Propagation
+ if (symbol_clock()) {
+ // N.B. symbol_clock() == true implies ted_input_clock() == true
+ // N.B. symbol_clock() == true implies output_sample_clock() == true
+
+ // This check and revert is needed either when
+ // a) the samples per symbol to get to the next symbol is greater
+ // than d_interp->ntaps() (normally 8); thus we would consume()
+ // more input than we were given to get there.
+ // b) we can't get to the next output so we can't do tag
+ // propagation properly.
+ d_interp->next_phase(d_inst_clock_period,
+ look_ahead_phase,
+ look_ahead_phase_n,
+ look_ahead_phase_wrapped);
+
+ if (ii + look_ahead_phase_n >= ni) {
+ // We can't move forward because there's not enough input.
+ // Revert the actions we took in the loop so far, and bail out.
+
+ // Symbol Clock Tracking and Estimation
+ d_clock->revert_loop();
+
+ // Timing Error Detector
+ d_ted->revert();
+
+ // Symbol Clock and Interpolator Positioning & Alignment
+ // Nothing to do;
+
+ // Block Internal Clocks
+ revert_internal_clocks();
+ break;
+ }
+ }
+
+ // Symbol Clock and Interpolator Positioning & Alignment
+ d_interp->advance_phase(d_inst_interp_period);
+
+ // Symbol Clock Tracking Reset/Resync to time_est and clock_est tags
+ if (find_sync_tag(nitems_rd, ii, d_interp->phase_n(), sync_tag_offset,
+ sync_timing_offset, sync_clock_period) == true ) {
+
+ // Block Internal Clocks
+ sync_reset_internal_clocks();
+
+ // Timing Error Detector
+ d_ted->sync_reset();
+ error = d_ted->error();
+
+ // Symbol Clock Tracking and Estimation
+
+ // NOTE: the + 1 below was determined empirically, but doesn't
+ // seem right on paper (maybe rounding in the computation of
+ // d_filter_delay is the culprit). Anyway, experiment trumps
+ // theory *every* time; so + 1 it is.
+ d_inst_clock_period = static_cast<float>(
+ static_cast<int>(sync_tag_offset - nitems_rd - d_filter_delay)
+ - ii + 1) + sync_timing_offset - d_interp->phase_wrapped();
+
+ d_clock->set_inst_period(d_inst_clock_period);
+ d_clock->set_avg_period(sync_clock_period);
+ d_avg_clock_period = d_clock->get_avg_period();
+ d_clock->set_phase(0.0f);
+
+ // Symbol Clock and Interpolator Positioning & Alignment
+ d_inst_interp_period = d_inst_clock_period;
+ d_interp->revert_phase();
+ d_interp->advance_phase(d_inst_interp_period);
+
+ // Tag Propagation
+ // Only needed if we reverted back to an output_sample_clock()
+ // as this is only used for tag propagation. Note that the
+ // next output will also be both an output_sample_clock() and a
+ // symbol_clock().
+ d_inst_output_period = d_inst_clock_period;
+ }
+
+ if (output_sample_clock()) {
+ // Diagnostic Output of Symbol Clock Tracking cycle results
+ emit_optional_output(oo, error, d_inst_clock_period,
+ d_avg_clock_period);
+ // Tag Propagation
+ propagate_tags(nitems_rd, ii,
+ d_interp->prev_phase_wrapped(), d_inst_output_period,
+ nitems_wr, oo);
+
+ // Symbol Clock and Interpolator Positioning & Alignment
+ oo++;
+ }
+
+ // Symbol Clock and Interpolator Positioning & Alignment
+ ii += d_interp->phase_n();
+ }
+
+ // Deferred Tag Propagation
+ save_expiring_tags(nitems_rd, ii);
+
+ // Symbol Clock and Interpolator Positioning & Alignment
+ consume_each(ii);
+ return oo;
+ }
+
+ } /* namespace digital */
+} /* namespace gr */
diff --git a/gr-digital/lib/symbol_sync_ff_impl.h b/gr-digital/lib/symbol_sync_ff_impl.h
new file mode 100644
index 0000000000..f4bd6658b3
--- /dev/null
+++ b/gr-digital/lib/symbol_sync_ff_impl.h
@@ -0,0 +1,153 @@
+/* -*- c++ -*- */
+/*
+ * Copyright (C) 2017 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_DIGITAL_SYMBOL_SYNC_FF_IMPL_H
+#define INCLUDED_DIGITAL_SYMBOL_SYNC_FF_IMPL_H
+
+#include <gnuradio/digital/symbol_sync_ff.h>
+#include "clock_tracking_loop.h"
+#include "timing_error_detector.h"
+#include "interpolating_resampler.h"
+
+namespace gr {
+ namespace digital {
+
+ class symbol_sync_ff_impl : public symbol_sync_ff
+ {
+ public:
+ symbol_sync_ff_impl(enum ted_type detector_type,
+ float sps,
+ float loop_bw,
+ float damping_factor,
+ float max_deviation,
+ int osps,
+ constellation_sptr slicer,
+ ir_type interp_type,
+ int n_filters,
+ const std::vector<float> &taps);
+ ~symbol_sync_ff_impl();
+
+ void forecast(int noutput_items, gr_vector_int &ninput_items_required);
+ int general_work(int noutput_items,
+ gr_vector_int &ninput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items);
+
+ // Symbol Clock Tracking and Estimation
+ float loop_bandwidth() const { return d_clock->get_loop_bandwidth(); }
+ float damping_factor() const { return d_clock->get_damping_factor(); }
+ float alpha() const { return d_clock->get_alpha(); }
+ float beta() const { return d_clock->get_beta(); }
+
+ void set_loop_bandwidth (float omega_n_norm) {
+ d_clock->set_loop_bandwidth(omega_n_norm);
+ }
+ void set_damping_factor (float zeta) {
+ d_clock->set_damping_factor(zeta);
+ }
+ void set_alpha (float alpha) { d_clock->set_alpha(alpha); }
+ void set_beta (float beta) { d_clock->set_beta(beta); }
+
+ private:
+ // Timing Error Detector
+ timing_error_detector *d_ted;
+
+ // Symbol Clock Tracking and Estimation
+ clock_tracking_loop *d_clock;
+
+ // Interpolator and Interpolator Positioning and Alignment
+ interpolating_resampler_fff *d_interp;
+
+ // Block Internal Clocks
+ // 4 clocks that run synchronously, aligned to the Symbol Clock:
+ // Interpolator Clock (always highest rate)
+ // Timing Error Detector Input Clock
+ // Output Sample Clock
+ // Symbol Clock (always lowest rate)
+ int d_interp_clock;
+ float d_inst_interp_period;
+
+ float d_interps_per_ted_input;
+ int d_interps_per_ted_input_n;
+ bool d_ted_input_clock;
+
+ int d_interps_per_output_sample_n;
+ bool d_output_sample_clock;
+ float d_inst_output_period;
+
+ float d_interps_per_symbol;
+ int d_interps_per_symbol_n;
+ bool d_symbol_clock;
+ float d_inst_clock_period;
+ float d_avg_clock_period;
+
+ // Block output
+ float d_osps;
+ int d_osps_n;
+
+ // Tag Propagation and Symbol Clock Tracking Reset/Resync
+ uint64_t d_filter_delay; // interpolator filter delay
+ std::vector<tag_t> d_tags;
+ std::vector<tag_t> d_new_tags;
+ pmt::pmt_t d_time_est_key;
+ pmt::pmt_t d_clock_est_key;
+
+ // Optional Diagnostic Outputs
+ int d_noutputs;
+ float *d_out_error;
+ float *d_out_instantaneous_clock_period;
+ float *d_out_average_clock_period;
+
+ // Block Internal Clocks
+ void update_internal_clock_outputs();
+ void advance_internal_clocks();
+ void revert_internal_clocks();
+ void sync_reset_internal_clocks();
+ bool ted_input_clock() { return d_ted_input_clock; }
+ bool output_sample_clock() { return d_output_sample_clock; }
+ bool symbol_clock() { return d_symbol_clock; }
+
+ // Tag Propagation and Clock Tracking Reset/Resync
+ void collect_tags(uint64_t nitems_rd, int count);
+ bool find_sync_tag(uint64_t nitems_rd, int iidx,
+ int distance,
+ uint64_t &tag_offset,
+ float &timing_offset,
+ float &clock_period);
+ void propagate_tags(uint64_t nitems_rd, int iidx,
+ float iidx_fraction,
+ float inst_output_period,
+ uint64_t nitems_wr, int oidx);
+ void save_expiring_tags(uint64_t nitems_rd, int consumed);
+
+ // Optional Diagnostic Outputs
+ void setup_optional_outputs(gr_vector_void_star &output_items);
+ void emit_optional_output(int oidx,
+ float error,
+ float inst_clock_period,
+ float avg_clock_period);
+ };
+
+ } /* namespace digital */
+} /* namespace gr */
+
+#endif /* INCLUDED_DIGITAL_SYMBOL_SYNC_FF_IMPL_H */
diff --git a/gr-digital/lib/timing_error_detector.cc b/gr-digital/lib/timing_error_detector.cc
new file mode 100644
index 0000000000..f3794888e8
--- /dev/null
+++ b/gr-digital/lib/timing_error_detector.cc
@@ -0,0 +1,443 @@
+/* -*- c++ -*- */
+/*
+ * Copyright (C) 2017 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 file; 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 "timing_error_detector.h"
+#include <gnuradio/math.h>
+#include <stdexcept>
+
+namespace gr {
+ namespace digital {
+
+ timing_error_detector *
+ timing_error_detector::make(enum ted_type type,
+ constellation_sptr constellation)
+ {
+ timing_error_detector *ret = NULL;
+ switch (type) {
+ case TED_NONE:
+ break;
+ case TED_MUELLER_AND_MULLER:
+ ret = new ted_mueller_and_muller(constellation);
+ break;
+ case TED_MOD_MUELLER_AND_MULLER:
+ ret = new ted_mod_mueller_and_muller(constellation);
+ break;
+ case TED_ZERO_CROSSING:
+ ret = new ted_zero_crossing(constellation);
+ break;
+ case TED_GARDNER:
+ ret = new ted_gardner();
+ break;
+ case TED_EARLY_LATE:
+ ret = new ted_early_late();
+ break;
+ case TED_DANDREA_AND_MENGALI_GEN_MSK:
+ ret = new ted_generalized_msk();
+ break;
+ case TED_SIGNAL_TIMES_SLOPE_ML:
+ ret = new ted_signal_times_slope_ml();
+ break;
+ case TED_SIGNUM_TIMES_SLOPE_ML:
+ ret = new ted_signum_times_slope_ml();
+ break;
+ case TED_MENGALI_AND_DANDREA_GMSK:
+ ret = new ted_gaussian_msk();
+ break;
+ default:
+ break;
+ }
+ return ret;
+ }
+
+ timing_error_detector::timing_error_detector(
+ enum ted_type type,
+ int inputs_per_symbol,
+ int error_computation_depth,
+ bool needs_lookahead,
+ bool needs_derivative,
+ constellation_sptr constellation)
+ : d_type(type),
+ d_constellation(constellation),
+ d_error(0.0f),
+ d_prev_error(0.0f),
+ d_inputs_per_symbol(inputs_per_symbol),
+ d_error_depth(error_computation_depth),
+ d_input(),
+ d_decision(),
+ d_input_derivative(),
+ d_needs_lookahead(needs_lookahead),
+ d_needs_derivative(needs_derivative)
+ {
+ if (d_constellation && d_constellation->dimensionality() != 1)
+ throw std::invalid_argument(
+ "timing_error_detector: constellation dimensionality "
+ "(complex numbers per symbol) must be 1.");
+
+ switch (type) {
+ case TED_MUELLER_AND_MULLER:
+ case TED_MOD_MUELLER_AND_MULLER:
+ case TED_ZERO_CROSSING:
+ if (!d_constellation)
+ throw std::invalid_argument(
+ "timing_error_detector: slicer constellation required.");
+ break;
+ case TED_GARDNER:
+ case TED_EARLY_LATE:
+ case TED_DANDREA_AND_MENGALI_GEN_MSK:
+ case TED_SIGNAL_TIMES_SLOPE_ML:
+ case TED_SIGNUM_TIMES_SLOPE_ML:
+ case TED_MENGALI_AND_DANDREA_GMSK:
+ break;
+ case TED_NONE:
+ default:
+ throw std::invalid_argument(
+ "timing_error_detector: invalid timing error detector type.");
+ break;
+ }
+
+ sync_reset();
+ }
+
+ void
+ timing_error_detector::input(const gr_complex &x, const gr_complex &dx)
+ {
+ d_input.push_front(x);
+ d_input.pop_back();
+
+ if (d_constellation) {
+ d_decision.push_front(slice(d_input[0]));
+ d_decision.pop_back();
+ }
+
+ if (d_needs_derivative) {
+ d_input_derivative.push_front(dx);
+ d_input_derivative.pop_back();
+ }
+
+ advance_input_clock();
+ if (d_input_clock == 0 && d_needs_lookahead == false) {
+ d_prev_error = d_error;
+ d_error = compute_error_cf();
+ }
+ }
+
+ void
+ timing_error_detector::input(float x, float dx)
+ {
+ d_input.push_front(gr_complex(x, 0.0f));
+ d_input.pop_back();
+
+ if (d_constellation) {
+ d_decision.push_front(slice(d_input[0]));
+ d_decision.pop_back();
+ }
+
+ if (d_needs_derivative) {
+ d_input_derivative.push_front(gr_complex(dx, 0.0f));
+ d_input_derivative.pop_back();
+ }
+
+ advance_input_clock();
+ if (d_input_clock == 0 && d_needs_lookahead == false) {
+ d_prev_error = d_error;
+ d_error = compute_error_ff();
+ }
+ }
+
+ void
+ timing_error_detector::input_lookahead(const gr_complex &x,
+ const gr_complex &dx)
+ {
+ if (d_input_clock != 0 || d_needs_lookahead == false)
+ return;
+
+ d_input.push_front(x);
+ if (d_constellation)
+ d_decision.push_front(slice(d_input[0]));
+ if (d_needs_derivative)
+ d_input_derivative.push_front(dx);
+
+ d_prev_error = d_error;
+ d_error = compute_error_cf();
+
+ if (d_needs_derivative)
+ d_input_derivative.pop_front();
+ if (d_constellation)
+ d_decision.pop_front();
+ d_input.pop_front();
+ }
+
+ void
+ timing_error_detector::input_lookahead(float x, float dx)
+ {
+ if (d_input_clock != 0 || d_needs_lookahead == false)
+ return;
+
+ d_input.push_front(gr_complex(x, 0.0f));
+ if (d_constellation)
+ d_decision.push_front(slice(d_input[0]));
+ if (d_needs_derivative)
+ d_input_derivative.push_front(gr_complex(dx, 0.0f));
+
+ d_prev_error = d_error;
+ d_error = compute_error_ff();
+
+ if (d_needs_derivative)
+ d_input_derivative.pop_front();
+ if (d_constellation)
+ d_decision.pop_front();
+ d_input.pop_front();
+ }
+
+ void
+ timing_error_detector::revert(bool preserve_error)
+ {
+ if (d_input_clock == 0 and preserve_error != true)
+ d_error = d_prev_error;
+ revert_input_clock();
+
+ if (d_needs_derivative) {
+ d_input_derivative.push_back(d_input_derivative.back());
+ d_input_derivative.pop_front();
+ }
+
+ if (d_constellation) {
+ d_decision.push_back(d_decision.back());
+ d_decision.pop_front();
+ }
+
+ d_input.push_back(d_input.back());
+ d_input.pop_front();
+ }
+
+ void
+ timing_error_detector::sync_reset()
+ {
+ d_error = 0.0f;
+ d_prev_error = 0.0f;
+
+ d_input.assign(d_error_depth, gr_complex(0.0f, 0.0f));
+ d_input_derivative.assign(d_error_depth, gr_complex(0.0f, 0.0f));
+
+ if (d_constellation) {
+ std::deque<gr_complex>::iterator it;
+ d_decision.clear();
+ for (it = d_input.begin(); it != d_input.end(); ++it)
+ d_decision.push_back(gr_complex(0.0f, 0.0f));
+ //d_decision.push_back(slice(*it));
+ }
+
+ sync_reset_input_clock();
+ }
+
+ gr_complex
+ timing_error_detector::slice(const gr_complex &x)
+ {
+ unsigned int index;
+ gr_complex z(0.0f, 0.0f);
+
+ index = d_constellation->decision_maker(&x);
+ d_constellation->map_to_points(index, &z);
+ return z;
+ }
+
+ /*************************************************************************/
+
+ float
+ ted_mueller_and_muller::compute_error_cf()
+ {
+ return ( d_decision[1].real() * d_input[0].real()
+ - d_decision[0].real() * d_input[1].real())
+ + ( d_decision[1].imag() * d_input[0].imag()
+ - d_decision[0].imag() * d_input[1].imag());
+ }
+
+ float
+ ted_mueller_and_muller::compute_error_ff()
+ {
+ return ( d_decision[1].real() * d_input[0].real()
+ - d_decision[0].real() * d_input[1].real());
+ }
+
+ /*************************************************************************/
+
+ float
+ ted_mod_mueller_and_muller::compute_error_cf()
+ {
+ gr_complex u;
+
+ u = ((d_input[0] - d_input[2] ) * conj(d_decision[1]))
+ -((d_decision[0] - d_decision[2]) * conj(d_input[1] ));
+
+ return gr::branchless_clip(u.real(), 1.0f);
+ }
+
+ float
+ ted_mod_mueller_and_muller::compute_error_ff()
+ {
+ float u;
+
+ u = ((d_input[0].real() - d_input[2].real()) * d_decision[1].real())
+ -((d_decision[0].real() - d_decision[2].real()) * d_input[1].real());
+
+ return gr::branchless_clip(u/2.0f, 1.0f);
+ }
+
+ /*************************************************************************/
+
+ float
+ ted_zero_crossing::compute_error_cf()
+ {
+ return
+ ((d_decision[2].real() - d_decision[0].real()) * d_input[1].real())
+ + ((d_decision[2].imag() - d_decision[0].imag()) * d_input[1].imag());
+ }
+
+ float
+ ted_zero_crossing::compute_error_ff()
+ {
+ return
+ ((d_decision[2].real() - d_decision[0].real()) * d_input[1].real());
+ }
+
+ /*************************************************************************/
+
+ float
+ ted_gardner::compute_error_cf()
+ {
+ return ((d_input[2].real() - d_input[0].real()) * d_input[1].real())
+ + ((d_input[2].imag() - d_input[0].imag()) * d_input[1].imag());
+ }
+
+ float
+ ted_gardner::compute_error_ff()
+ {
+ return ((d_input[2].real() - d_input[0].real()) * d_input[1].real());
+ }
+
+ /*************************************************************************/
+
+ float
+ ted_early_late::compute_error_cf()
+ {
+ return ((d_input[0].real() - d_input[2].real()) * d_input[1].real())
+ + ((d_input[0].imag() - d_input[2].imag()) * d_input[1].imag());
+ }
+
+ float
+ ted_early_late::compute_error_ff()
+ {
+ return ((d_input[0].real() - d_input[2].real()) * d_input[1].real());
+ }
+
+ /*************************************************************************/
+
+ float
+ ted_generalized_msk::compute_error_cf()
+ {
+ gr_complex u;
+
+ u = (d_input[0] * d_input[0] * conj(d_input[2] * d_input[2]))
+ - (d_input[1] * d_input[1] * conj(d_input[3] * d_input[3]));
+
+ return gr::branchless_clip(u.real(), 3.0f);
+ }
+
+ float
+ ted_generalized_msk::compute_error_ff()
+ {
+ float u;
+
+ u = ( d_input[0].real() * d_input[0].real()
+ * d_input[2].real() * d_input[2].real())
+ - ( d_input[1].real() * d_input[1].real()
+ * d_input[3].real() * d_input[3].real());
+
+ return gr::branchless_clip(u, 3.0f);
+ }
+
+ /*************************************************************************/
+
+ float
+ ted_gaussian_msk::compute_error_cf()
+ {
+ gr_complex u;
+
+ u = -(d_input[0] * d_input[0] * conj(d_input[2] * d_input[2]))
+ + (d_input[1] * d_input[1] * conj(d_input[3] * d_input[3]));
+
+ return gr::branchless_clip(u.real(), 3.0f);
+ }
+
+ float
+ ted_gaussian_msk::compute_error_ff()
+ {
+ float u;
+
+ u = -( d_input[0].real() * d_input[0].real()
+ * d_input[2].real() * d_input[2].real())
+ + ( d_input[1].real() * d_input[1].real()
+ * d_input[3].real() * d_input[3].real());
+
+ return gr::branchless_clip(u, 3.0f);
+ }
+
+ /*************************************************************************/
+
+ float
+ ted_signal_times_slope_ml::compute_error_cf()
+ {
+ return ( d_input[0].real() * d_input_derivative[0].real()
+ + d_input[0].imag() * d_input_derivative[0].imag()) / 2.0f;
+ }
+
+ float
+ ted_signal_times_slope_ml::compute_error_ff()
+ {
+ return ( d_input[0].real() * d_input_derivative[0].real());
+ }
+
+ /*************************************************************************/
+
+ float
+ ted_signum_times_slope_ml::compute_error_cf()
+ {
+ return ( (d_input[0].real() < 0.0f ? -d_input_derivative[0].real()
+ : d_input_derivative[0].real())
+ + (d_input[0].imag() < 0.0f ? -d_input_derivative[0].imag()
+ : d_input_derivative[0].imag()))
+ / 2.0f;
+ }
+
+ float
+ ted_signum_times_slope_ml::compute_error_ff()
+ {
+ return (d_input[0].real() < 0.0f ? -d_input_derivative[0].real()
+ : d_input_derivative[0].real());
+ }
+
+ } /* namespace digital */
+} /* namespace gr */
diff --git a/gr-digital/lib/timing_error_detector.h b/gr-digital/lib/timing_error_detector.h
new file mode 100644
index 0000000000..fd569ba360
--- /dev/null
+++ b/gr-digital/lib/timing_error_detector.h
@@ -0,0 +1,513 @@
+/* -*- c++ -*- */
+/*
+ * Copyright (C) 2017 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 file; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_DIGITAL_TIMING_ERROR_DETECTOR_H
+#define INCLUDED_DIGITAL_TIMING_ERROR_DETECTOR_H
+
+#include <gnuradio/gr_complex.h>
+#include <gnuradio/digital/timing_error_detector_type.h>
+#include <gnuradio/digital/constellation.h>
+#include <deque>
+
+namespace gr {
+ namespace digital {
+
+ /*!
+ * \brief Base class for the composite symbol clock timing error
+ * detector object used by the symbol_synchronizer_xx blocks.
+ * \ingroup internal
+ *
+ * \details
+ * This is the base class for the symbol clock timing error detector
+ * object used by the symbol_synchronizer_xx blocks to provide a
+ * user selectable timing error detector type.
+ *
+ * This base class provides the enumeration type for the available
+ * types of timing error detectors.
+ *
+ * This base class also provides most of the methods update, manage, and
+ * store the data points required to compute the symbol clock timing error,
+ * and the timing error result itself.
+ *
+ * The derived classes only have to provide the simple functions
+ * to actually compute the timing error.
+ */
+ class timing_error_detector
+ {
+ public:
+
+ /*!
+ * \brief Create a timing error detector object of the specified
+ * type
+ * \param type The type/algorithm to use for the timing error detector
+ * \param constellation A constellation to be used as a decision slicer
+ * for decision directed timing error detector
+ * algorithms
+ */
+ static timing_error_detector *make(enum ted_type type,
+ constellation_sptr constellation =
+ constellation_sptr());
+
+ virtual ~timing_error_detector() {};
+
+ /*!
+ * \brief Return the number of input samples per symbol this timing
+ * error detector algorithm requires.
+ */
+ virtual int inputs_per_symbol() { return d_inputs_per_symbol; }
+
+ /*!
+ * \brief Provide a complex input sample to the TED algorithm
+ * \param x the input sample
+ * \param dx the derivative at the input sample, if required by the
+ * timinig error detector algorithm
+ */
+ virtual void input(const gr_complex &x,
+ const gr_complex &dx = gr_complex(0.0f, 0.0f));
+
+ /*!
+ * \brief Provide a float input sample to the TED algorithm
+ * \param x the input sample
+ * \param dx the derivative at the input sample, if required by the
+ * timinig error detector algorithm
+ */
+ virtual void input(float x,
+ float dx = 0.0f);
+
+ /*!
+ * \brief Return if this timing error detector algorithm requires a
+ * look-ahead input sample to be input to produce a new error estimate
+ * for the symbol sampling instant.
+ */
+ virtual bool needs_lookahead() { return d_needs_lookahead; }
+
+ /*!
+ * \brief Provide a complex input lookahead sample to the TED algorithm
+ * \param x the input lookahead sample
+ * \param dx the derivative at the input lookahead sample, if required
+ * by the timinig error detector algorithm
+ */
+ virtual void input_lookahead(const gr_complex &x,
+ const gr_complex &dx =
+ gr_complex(0.0f, 0.0f));
+
+ /*!
+ * \brief Provide a float input lookahead sample to the TED algorithm
+ * \param x the input lookahead sample
+ * \param dx the derivative at the input lookahead sample, if required
+ * by the timinig error detector algorithm
+ */
+ virtual void input_lookahead(float x,
+ float dx = 0.0f);
+
+ /*!
+ * \brief Return if this timing error detector algorithm requires
+ * derivative input samples in addition to normal input samples.
+ */
+ virtual bool needs_derivative() { return d_needs_derivative; }
+
+ /*!
+ * \brief Return the current symbol timing error estimate
+ */
+ virtual float error() { return d_error; }
+
+ /*!
+ * \brief Revert the timing error detector processing state back one
+ * step.
+ * \param preserve_error If true, don't revert the error estimate.
+ */
+ virtual void revert(bool preserve_error = false);
+
+ /*!
+ * \brief Reset the timing error detector
+ */
+ virtual void sync_reset();
+
+ private:
+ enum ted_type d_type;
+
+ protected:
+ /*!
+ * \brief Create a timing error detector abstract base class object
+ * object
+ * \param type The type/algorithm to use for the timing error detector
+ * \param inputs_per_symbol The input samples per symbol this TED
+ * algorithm requires
+ * \param error_computation_depth The number of input samples this TED
+ * algorithm needs to compute the error
+ * \param needs_lookahead True if this TED algorithm needs a lookahead
+ * input sample to compute the error for the
+ * symbol sampling instant
+ * \param needs_derivative True if this TED algorithm needs a derivative
+ * input sample in addition to the normal input
+ * sample
+ * \param constellation A constellation to be used as a decision slicer
+ * for decision directed timing error detector
+ * algorithms
+ */
+ timing_error_detector(enum ted_type type,
+ int inputs_per_symbol,
+ int error_computation_depth,
+ bool needs_lookahead = false,
+ bool needs_derivative = false,
+ constellation_sptr constellation =
+ constellation_sptr());
+
+ /*!
+ * \brief Advance the TED input clock, so the input() methods will
+ * compute the TED error term at the proper symbol sampling instant.
+ */
+ void advance_input_clock() {
+ d_input_clock = (d_input_clock + 1) % d_inputs_per_symbol;
+ }
+
+ /*!
+ * \brief Revert the TED input clock one step
+ */
+ void revert_input_clock()
+ {
+ if (d_input_clock == 0)
+ d_input_clock = d_inputs_per_symbol - 1;
+ else
+ d_input_clock--;
+ }
+
+ /*!
+ * \brief Reset the TED input clock, so the next input clock advance
+ * corresponds to a symbol sampling instant.
+ */
+ void sync_reset_input_clock() {
+ d_input_clock = d_inputs_per_symbol - 1;
+ }
+
+ /*!
+ * \brief Convert the soft symbol sample into a hard symbol decision.
+ * \param x the soft input symbol sample
+ */
+ gr_complex slice(const gr_complex &x);
+
+ /*!
+ * \brief Compute the TED error term for complex input samples
+ */
+ virtual float compute_error_cf() = 0;
+
+ /*!
+ * \brief Compute the TED error term for float input samples
+ */
+ virtual float compute_error_ff() = 0;
+
+ constellation_sptr d_constellation;
+ float d_error;
+ float d_prev_error;
+ int d_inputs_per_symbol;
+ int d_input_clock;
+ int d_error_depth;
+ std::deque<gr_complex> d_input;
+ std::deque<gr_complex> d_decision;
+ std::deque<gr_complex> d_input_derivative;
+ bool d_needs_lookahead;
+ bool d_needs_derivative;
+ };
+
+ /*!
+ * \brief Mueller and Müller (M&M) timing error detector algorithm class
+ * \ingroup internal
+ *
+ * \details
+ * This is the Mueller and Müller (M&M) timing error detector algorithm
+ * class. It is a decision directed timing error detector, and requires
+ * an input slicer constellation to make decisions.
+ *
+ * See equation (49) of:
+ * Mueller, Kurt H., Müller, Markus, "Timing Recovery in Digital
+ * Synchronous Data Receivers", _IEEE_Transactions_on_Communications_,
+ * Vol. COM-24, No. 5, May 1976, pp. 516-531
+ */
+ class ted_mueller_and_muller : public timing_error_detector
+ {
+ public:
+ /*!
+ * \brief Create a Mueller and Müller (M&M) timing error detector
+ * \param constellation A constellation to be used as a decision slicer
+ */
+ ted_mueller_and_muller(constellation_sptr constellation)
+ : timing_error_detector(TED_MUELLER_AND_MULLER,
+ 1, 2, false, false, constellation)
+ {}
+ ~ted_mueller_and_muller() {};
+
+ private:
+ float compute_error_cf();
+ float compute_error_ff();
+ };
+
+ /*!
+ * \brief Modified Mueller and Müller (M&M) timing error detector algorithm
+ * class
+ * \ingroup internal
+ *
+ * \details
+ * This is the modified Mueller and Müller (M&M) timing error detector
+ * algorithm class. It is a decision directed timing error detector, and
+ * requires an input slicer constellation to make decisions.
+ *
+ * The modification of the standard M&M TED is done to remove
+ * self-noise that is present in the standard M&M TED.
+ *
+ * See:
+ * 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.
+ */
+ class ted_mod_mueller_and_muller : public timing_error_detector
+ {
+ public:
+ /*!
+ * \brief Create a modified Mueller and Müller (M&M) timing error
+ * detector
+ * \param constellation A constellation to be used as a decision slicer
+ */
+ ted_mod_mueller_and_muller(constellation_sptr constellation)
+ : timing_error_detector(TED_MOD_MUELLER_AND_MULLER,
+ 1, 3, false, false, constellation)
+ {}
+ ~ted_mod_mueller_and_muller() {};
+
+ private:
+ float compute_error_cf();
+ float compute_error_ff();
+ };
+
+ /*!
+ * \brief Zero Crossing timing error detector algorithm class
+ * \ingroup internal
+ *
+ * \details
+ * This is the Zero Crossing timing error detector algorithm class.
+ * It is a decision directed timing error detector, and
+ * requires an input slicer constellation to make decisions.
+ */
+ class ted_zero_crossing : public timing_error_detector
+ {
+ public:
+ /*!
+ * \brief Create a Zero Crossing timing error detector
+ * \param constellation A constellation to be used as a decision slicer
+ */
+ ted_zero_crossing(constellation_sptr constellation)
+ : timing_error_detector(TED_ZERO_CROSSING,
+ 2, 3, false, false, constellation)
+ {}
+ ~ted_zero_crossing() {};
+
+ private:
+ float compute_error_cf();
+ float compute_error_ff();
+ };
+
+ /*!
+ * \brief Gardner timing error detector algorithm class
+ * \ingroup internal
+ *
+ * \details
+ * This is the Gardner timing error detector algorithm class.
+ *
+ * See:
+ * F.M. Gardner, “A BPSK/QPSK Timing Error Detector for Sampled Receivers”,
+ * IEEE Trans., COM-34, (5), 1988, pp. 423 – 429.
+ */
+ class ted_gardner : public timing_error_detector
+ {
+ public:
+ /*!
+ * \brief Create a Gardner timing error detector
+ */
+ ted_gardner()
+ : timing_error_detector(TED_GARDNER,
+ 2, 3, false, false,
+ constellation_sptr())
+ {}
+ ~ted_gardner() {};
+
+ private:
+ float compute_error_cf();
+ float compute_error_ff();
+ };
+
+ /*!
+ * \brief Early-Late timing error detector algorithm class
+ * \ingroup internal
+ *
+ * \details
+ * This is the Early-Late timing error detector algorithm class.
+ * It requires a lookahead sample to compute the symbol timing error
+ * at the symbol sampling instant.
+ */
+ class ted_early_late : public timing_error_detector
+ {
+ public:
+ /*!
+ * \brief Create a Early-Late timing error detector
+ */
+ ted_early_late()
+ : timing_error_detector(TED_EARLY_LATE,
+ 2, 2, true, false,
+ constellation_sptr())
+ {}
+ ~ted_early_late() {};
+
+ private:
+ float compute_error_cf();
+ float compute_error_ff();
+ };
+
+ /*!
+ * \brief Generalized MSK timing error detector algorithm class
+ * \ingroup internal
+ *
+ * \details
+ * This is the Generalized MSK timing error detector algorithm class.
+ * It operates on the CPM MSK signal directly and not the FM pulses.
+ *
+ * See:
+ * A.N. D'Andrea, U. Mengali, R. Reggiannini, "A digital approach to clock
+ * recovery in generalized minimum shift keying", IEEE Transactions on
+ * Vehicular Technology, Vol. 39, Issue 3, August 1990, pp. 227-234
+ */
+ class ted_generalized_msk : public timing_error_detector
+ {
+ public:
+ /*!
+ * \brief Create a Generalized MSK timing error detector
+ */
+ ted_generalized_msk()
+ : timing_error_detector(TED_DANDREA_AND_MENGALI_GEN_MSK,
+ 2, 4, false, false, constellation_sptr())
+ {}
+ ~ted_generalized_msk() {};
+
+ private:
+ float compute_error_cf();
+ float compute_error_ff();
+ };
+
+ /*!
+ * \brief Gaussian MSK timing error detector algorithm class
+ * \ingroup internal
+ *
+ * \details
+ * This is the Gaussian MSK timing error detector algorithm class.
+ * It operates on the CPM MSK signal directly and not the FM pulses.
+ *
+ * See:
+ * U. Mengali, A.N. D'Andrea, _Synchronization_Techniques_for_Digital_
+ * Receviers_, New York, Plenum Press 1997
+ */
+ class ted_gaussian_msk : public timing_error_detector
+ {
+ public:
+ /*!
+ * \brief Create a Generalized MSK timing error detector
+ */
+ ted_gaussian_msk()
+ : timing_error_detector(TED_MENGALI_AND_DANDREA_GMSK,
+ 2, 4, false, false, constellation_sptr())
+ {}
+ ~ted_gaussian_msk() {};
+
+ private:
+ float compute_error_cf();
+ float compute_error_ff();
+ };
+
+ /*!
+ * \brief Signal times Slope Maximum Likelyhood solution approximation
+ * timing error detector algorithm class
+ * \ingroup internal
+ *
+ * \details
+ * This is the Signal times Slope Maximum Likelyhood solution approximation
+ * timing error detector algorithm class. This approximation is good for
+ * small signals (< 1.0) and/or situations with low SNR.
+ *
+ * See equation (5) and the 2 following paragraphs of:
+ * Fredric J. Harris, Michael Rice, "Multirate Digital Filters for
+ * Symbol Timing Synchronization in Software Defined Radios",
+ * _IEEE_Journal_on_Selected_Areas_in_Communications_, Vol. 19, No. 12,
+ * December 2001, pp. 2346 - 2357
+ */
+ class ted_signal_times_slope_ml : public timing_error_detector
+ {
+ public:
+ /*!
+ * \brief Create a Signal times Slope Maximum Likelyhood solution
+ * approximation timing error detector
+ */
+ ted_signal_times_slope_ml()
+ : timing_error_detector(TED_SIGNAL_TIMES_SLOPE_ML,
+ 1, 1, false, true, constellation_sptr())
+ {}
+ ~ted_signal_times_slope_ml() {};
+
+ private:
+ float compute_error_cf();
+ float compute_error_ff();
+ };
+
+ /*!
+ * \brief Signum times Slope Maximum Likelyhood solution approximation
+ * timing error detector algorithm class
+ * \ingroup internal
+ *
+ * \details
+ * This is the Signum times Slope Maximum Likelyhood solution approximation
+ * timing error detector algorithm class. This approximation is good for
+ * large signals (> 1.0) and/or situations with high SNR.
+ *
+ * See equation (5) and the 2 following paragraphs of:
+ * Fredric J. Harris, Michael Rice, "Multirate Digital Filters for
+ * Symbol Timing Synchronization in Software Defined Radios",
+ * _IEEE_Journal_on_Selected_Areas_in_Communications_, Vol. 19, No. 12,
+ * December 2001, pp. 2346 - 2357
+ */
+ class ted_signum_times_slope_ml : public timing_error_detector
+ {
+ public:
+ /*!
+ * \brief Create a Signum times Slope Maximum Likelyhood solution
+ * approximation timing error detector
+ */
+ ted_signum_times_slope_ml()
+ : timing_error_detector(TED_SIGNUM_TIMES_SLOPE_ML,
+ 1, 1, false, true, constellation_sptr())
+ {}
+ ~ted_signum_times_slope_ml() {};
+
+ private:
+ float compute_error_cf();
+ float compute_error_ff();
+ };
+
+ } /* namespace digital */
+} /* namespace gr */
+
+#endif /* INCLUDED_DIGITAL_TIMING_ERROR_DETECTOR_H */
diff --git a/gr-digital/swig/digital_swig2.i b/gr-digital/swig/digital_swig2.i
index 1836ad576c..781d74b749 100644
--- a/gr-digital/swig/digital_swig2.i
+++ b/gr-digital/swig/digital_swig2.i
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Free Software Foundation, Inc.
+ * Copyright 2016-2017 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
@@ -22,6 +22,7 @@
#define DIGITAL_API
#define ANALOG_API
#define BLOCKS_API
+#define FILTER_API
%include "gnuradio.i"
%include "stdint.i"
@@ -33,8 +34,15 @@
%{
#include <gnuradio/blocks/control_loop.h>
+#include "gnuradio/digital/constellation.h"
+#include "gnuradio/digital/timing_error_detector_type.h"
+#include "gnuradio/digital/interpolating_resampler_type.h"
%}
+
%include <gnuradio/blocks/control_loop.h>
+%include "gnuradio/digital/constellation.h"
+%include "gnuradio/digital/timing_error_detector_type.h"
+%include "gnuradio/digital/interpolating_resampler_type.h"
%{
#include "gnuradio/digital/mpsk_snr_est.h"
@@ -60,6 +68,8 @@
#include "gnuradio/digital/scrambler_bb.h"
#include "gnuradio/digital/simple_correlator.h"
#include "gnuradio/digital/simple_framer.h"
+#include "gnuradio/digital/symbol_sync_cc.h"
+#include "gnuradio/digital/symbol_sync_ff.h"
#include "gnuradio/digital/ofdm_serializer_vcc.h"
#include "gnuradio/digital/packet_headerparser_b.h"
#include "gnuradio/digital/header_payload_demux.h"
@@ -88,6 +98,8 @@
%include "gnuradio/digital/scrambler_bb.h"
%include "gnuradio/digital/simple_correlator.h"
%include "gnuradio/digital/simple_framer.h"
+%include "gnuradio/digital/symbol_sync_cc.h"
+%include "gnuradio/digital/symbol_sync_ff.h"
GR_SWIG_BLOCK_MAGIC2(digital, mpsk_snr_est_cc);
GR_SWIG_BLOCK_MAGIC2(digital, protocol_formatter_async);
@@ -104,6 +116,9 @@ GR_SWIG_BLOCK_MAGIC2(digital, probe_mpsk_snr_est_c);
GR_SWIG_BLOCK_MAGIC2(digital, scrambler_bb);
GR_SWIG_BLOCK_MAGIC2(digital, simple_correlator);
GR_SWIG_BLOCK_MAGIC2(digital, simple_framer);
+GR_SWIG_BLOCK_MAGIC2(digital, symbol_sync_cc);
+GR_SWIG_BLOCK_MAGIC2(digital, symbol_sync_ff);
// Properly package up non-block objects
%include "packet_header.i"
+%include "constellation.i"
diff --git a/gr-filter/include/gnuradio/filter/CMakeLists.txt b/gr-filter/include/gnuradio/filter/CMakeLists.txt
index 7ad692743a..c22c7995c0 100644
--- a/gr-filter/include/gnuradio/filter/CMakeLists.txt
+++ b/gr-filter/include/gnuradio/filter/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright 2012,2014 Free Software Foundation, Inc.
+# Copyright (C) 2012,2014,2017 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
@@ -44,6 +44,8 @@ install(FILES
interpolator_taps.h
mmse_fir_interpolator_cc.h
mmse_fir_interpolator_ff.h
+ mmse_interp_differentiator_cc.h
+ mmse_interp_differentiator_ff.h
pm_remez.h
polyphase_filterbank.h
filterbank.h
diff --git a/gr-filter/include/gnuradio/filter/interp_differentiator_taps.h b/gr-filter/include/gnuradio/filter/interp_differentiator_taps.h
new file mode 100644
index 0000000000..cc63a93a74
--- /dev/null
+++ b/gr-filter/include/gnuradio/filter/interp_differentiator_taps.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2017 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 file; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+/*
+ * This file was machine generated by gen_interp_differentiator_taps.
+ * DO NOT EDIT BY HAND.
+ */
+
+static const int DNTAPS = 8;
+static const int DNSTEPS = 128;
+static const double DBANDWIDTH = 0.25;
+
+static const float Dtaps[DNSTEPS+1][DNTAPS] = {
+ // -4 -3 -2 -1 0 1 2 3 mu
+ { -1.97975e-02, 1.09180e-01, -3.53697e-01, 1.00394e+00, -1.76425e-01, -6.98940e-01, 1.60378e-01, -2.55693e-02 }, // 0/128
+ { -1.98041e-02, 1.09378e-01, -3.55314e-01, 1.01662e+00, -1.98983e-01, -6.86147e-01, 1.58694e-01, -2.53710e-02 }, // 1/128
+ { -1.98003e-02, 1.09519e-01, -3.56761e-01, 1.02906e+00, -2.21442e-01, -6.73286e-01, 1.56945e-01, -2.51608e-02 }, // 2/128
+ { -1.97862e-02, 1.09604e-01, -3.58039e-01, 1.04126e+00, -2.43798e-01, -6.60361e-01, 1.55133e-01, -2.49389e-02 }, // 3/128
+ { -1.97616e-02, 1.09633e-01, -3.59146e-01, 1.05322e+00, -2.66043e-01, -6.47378e-01, 1.53260e-01, -2.47055e-02 }, // 4/128
+ { -1.97267e-02, 1.09605e-01, -3.60080e-01, 1.06494e+00, -2.88174e-01, -6.34339e-01, 1.51327e-01, -2.44607e-02 }, // 5/128
+ { -1.96814e-02, 1.09520e-01, -3.60840e-01, 1.07640e+00, -3.10183e-01, -6.21250e-01, 1.49335e-01, -2.42049e-02 }, // 6/128
+ { -1.96258e-02, 1.09379e-01, -3.61425e-01, 1.08760e+00, -3.32066e-01, -6.08114e-01, 1.47287e-01, -2.39381e-02 }, // 7/128
+ { -1.95598e-02, 1.09180e-01, -3.61833e-01, 1.09855e+00, -3.53816e-01, -5.94935e-01, 1.45183e-01, -2.36607e-02 }, // 8/128
+ { -1.94834e-02, 1.08925e-01, -3.62064e-01, 1.10923e+00, -3.75429e-01, -5.81717e-01, 1.43025e-01, -2.33727e-02 }, // 9/128
+ { -1.93968e-02, 1.08612e-01, -3.62117e-01, 1.11965e+00, -3.96899e-01, -5.68465e-01, 1.40814e-01, -2.30745e-02 }, // 10/128
+ { -1.92998e-02, 1.08243e-01, -3.61990e-01, 1.12979e+00, -4.18221e-01, -5.55183e-01, 1.38553e-01, -2.27662e-02 }, // 11/128
+ { -1.91927e-02, 1.07816e-01, -3.61683e-01, 1.13966e+00, -4.39388e-01, -5.41875e-01, 1.36242e-01, -2.24481e-02 }, // 12/128
+ { -1.90753e-02, 1.07333e-01, -3.61195e-01, 1.14926e+00, -4.60397e-01, -5.28544e-01, 1.33883e-01, -2.21203e-02 }, // 13/128
+ { -1.89477e-02, 1.06793e-01, -3.60525e-01, 1.15857e+00, -4.81241e-01, -5.15195e-01, 1.31477e-01, -2.17832e-02 }, // 14/128
+ { -1.88101e-02, 1.06195e-01, -3.59672e-01, 1.16761e+00, -5.01915e-01, -5.01832e-01, 1.29027e-01, -2.14369e-02 }, // 15/128
+ { -1.86623e-02, 1.05541e-01, -3.58635e-01, 1.17635e+00, -5.22415e-01, -4.88459e-01, 1.26533e-01, -2.10816e-02 }, // 16/128
+ { -1.85046e-02, 1.04831e-01, -3.57414e-01, 1.18481e+00, -5.42735e-01, -4.75079e-01, 1.23997e-01, -2.07177e-02 }, // 17/128
+ { -1.83369e-02, 1.04064e-01, -3.56008e-01, 1.19297e+00, -5.62871e-01, -4.61698e-01, 1.21421e-01, -2.03452e-02 }, // 18/128
+ { -1.81593e-02, 1.03240e-01, -3.54416e-01, 1.20083e+00, -5.82817e-01, -4.48318e-01, 1.18806e-01, -1.99646e-02 }, // 19/128
+ { -1.79718e-02, 1.02361e-01, -3.52639e-01, 1.20840e+00, -6.02568e-01, -4.34944e-01, 1.16154e-01, -1.95759e-02 }, // 20/128
+ { -1.77746e-02, 1.01425e-01, -3.50676e-01, 1.21567e+00, -6.22120e-01, -4.21579e-01, 1.13466e-01, -1.91794e-02 }, // 21/128
+ { -1.75678e-02, 1.00434e-01, -3.48526e-01, 1.22263e+00, -6.41467e-01, -4.08229e-01, 1.10744e-01, -1.87754e-02 }, // 22/128
+ { -1.73513e-02, 9.93878e-02, -3.46188e-01, 1.22928e+00, -6.60606e-01, -3.94895e-01, 1.07989e-01, -1.83641e-02 }, // 23/128
+ { -1.71253e-02, 9.82862e-02, -3.43664e-01, 1.23563e+00, -6.79531e-01, -3.81583e-01, 1.05203e-01, -1.79458e-02 }, // 24/128
+ { -1.68899e-02, 9.71297e-02, -3.40952e-01, 1.24166e+00, -6.98238e-01, -3.68296e-01, 1.02387e-01, -1.75206e-02 }, // 25/128
+ { -1.66451e-02, 9.59188e-02, -3.38052e-01, 1.24738e+00, -7.16722e-01, -3.55037e-01, 9.95428e-02, -1.70889e-02 }, // 26/128
+ { -1.63911e-02, 9.46536e-02, -3.34965e-01, 1.25278e+00, -7.34980e-01, -3.41811e-01, 9.66721e-02, -1.66509e-02 }, // 27/128
+ { -1.61280e-02, 9.33347e-02, -3.31690e-01, 1.25786e+00, -7.53006e-01, -3.28622e-01, 9.37762e-02, -1.62068e-02 }, // 28/128
+ { -1.58558e-02, 9.19624e-02, -3.28227e-01, 1.26263e+00, -7.70796e-01, -3.15472e-01, 9.08568e-02, -1.57569e-02 }, // 29/128
+ { -1.55747e-02, 9.05371e-02, -3.24577e-01, 1.26707e+00, -7.88347e-01, -3.02366e-01, 8.79152e-02, -1.53014e-02 }, // 30/128
+ { -1.52848e-02, 8.90593e-02, -3.20739e-01, 1.27118e+00, -8.05653e-01, -2.89308e-01, 8.49529e-02, -1.48405e-02 }, // 31/128
+ { -1.49862e-02, 8.75294e-02, -3.16714e-01, 1.27497e+00, -8.22711e-01, -2.76300e-01, 8.19717e-02, -1.43747e-02 }, // 32/128
+ { -1.46789e-02, 8.59479e-02, -3.12502e-01, 1.27843e+00, -8.39517e-01, -2.63347e-01, 7.89727e-02, -1.39039e-02 }, // 33/128
+ { -1.43633e-02, 8.43153e-02, -3.08103e-01, 1.28156e+00, -8.56067e-01, -2.50452e-01, 7.59576e-02, -1.34286e-02 }, // 34/128
+ { -1.40393e-02, 8.26321e-02, -3.03518e-01, 1.28435e+00, -8.72357e-01, -2.37619e-01, 7.29278e-02, -1.29490e-02 }, // 35/128
+ { -1.37071e-02, 8.08990e-02, -2.98747e-01, 1.28682e+00, -8.88384e-01, -2.24851e-01, 6.98849e-02, -1.24653e-02 }, // 36/128
+ { -1.33668e-02, 7.91163e-02, -2.93791e-01, 1.28895e+00, -9.04143e-01, -2.12151e-01, 6.68302e-02, -1.19778e-02 }, // 37/128
+ { -1.30187e-02, 7.72849e-02, -2.88650e-01, 1.29074e+00, -9.19630e-01, -1.99523e-01, 6.37653e-02, -1.14867e-02 }, // 38/128
+ { -1.26627e-02, 7.54052e-02, -2.83325e-01, 1.29220e+00, -9.34844e-01, -1.86970e-01, 6.06916e-02, -1.09923e-02 }, // 39/128
+ { -1.22991e-02, 7.34779e-02, -2.77817e-01, 1.29332e+00, -9.49779e-01, -1.74496e-01, 5.76105e-02, -1.04948e-02 }, // 40/128
+ { -1.19280e-02, 7.15036e-02, -2.72125e-01, 1.29410e+00, -9.64433e-01, -1.62104e-01, 5.45235e-02, -9.99449e-03 }, // 41/128
+ { -1.15496e-02, 6.94831e-02, -2.66252e-01, 1.29453e+00, -9.78802e-01, -1.49797e-01, 5.14320e-02, -9.49163e-03 }, // 42/128
+ { -1.11640e-02, 6.74171e-02, -2.60198e-01, 1.29463e+00, -9.92883e-01, -1.37578e-01, 4.83374e-02, -8.98645e-03 }, // 43/128
+ { -1.07714e-02, 6.53061e-02, -2.53964e-01, 1.29439e+00, -1.00667e+00, -1.25450e-01, 4.52412e-02, -8.47921e-03 }, // 44/128
+ { -1.03719e-02, 6.31511e-02, -2.47551e-01, 1.29380e+00, -1.02017e+00, -1.13418e-01, 4.21447e-02, -7.97016e-03 }, // 45/128
+ { -9.96577e-03, 6.09527e-02, -2.40960e-01, 1.29287e+00, -1.03337e+00, -1.01483e-01, 3.90493e-02, -7.45954e-03 }, // 46/128
+ { -9.55311e-03, 5.87118e-02, -2.34191e-01, 1.29159e+00, -1.04627e+00, -8.96486e-02, 3.59565e-02, -6.94760e-03 }, // 47/128
+ { -9.13410e-03, 5.64291e-02, -2.27247e-01, 1.28997e+00, -1.05886e+00, -7.79181e-02, 3.28675e-02, -6.43460e-03 }, // 48/128
+ { -8.70893e-03, 5.41055e-02, -2.20128e-01, 1.28801e+00, -1.07115e+00, -6.62943e-02, 2.97837e-02, -5.92077e-03 }, // 49/128
+ { -8.27777e-03, 5.17418e-02, -2.12836e-01, 1.28570e+00, -1.08314e+00, -5.47801e-02, 2.67066e-02, -5.40637e-03 }, // 50/128
+ { -7.84082e-03, 4.93389e-02, -2.05372e-01, 1.28305e+00, -1.09481e+00, -4.33784e-02, 2.36373e-02, -4.89163e-03 }, // 51/128
+ { -7.39824e-03, 4.68976e-02, -1.97737e-01, 1.28005e+00, -1.10617e+00, -3.20920e-02, 2.05773e-02, -4.37681e-03 }, // 52/128
+ { -6.95024e-03, 4.44189e-02, -1.89933e-01, 1.27670e+00, -1.11721e+00, -2.09236e-02, 1.75279e-02, -3.86214e-03 }, // 53/128
+ { -6.49701e-03, 4.19036e-02, -1.81961e-01, 1.27302e+00, -1.12794e+00, -9.87606e-03, 1.44903e-02, -3.34787e-03 }, // 54/128
+ { -6.03873e-03, 3.93528e-02, -1.73823e-01, 1.26898e+00, -1.13834e+00, 1.04807e-03, 1.14658e-02, -2.83424e-03 }, // 55/128
+ { -5.57561e-03, 3.67674e-02, -1.65521e-01, 1.26461e+00, -1.14843e+00, 1.18461e-02, 8.45574e-03, -2.32148e-03 }, // 56/128
+ { -5.10786e-03, 3.41483e-02, -1.57056e-01, 1.25989e+00, -1.15819e+00, 2.25155e-02, 5.46133e-03, -1.80983e-03 }, // 57/128
+ { -4.63566e-03, 3.14966e-02, -1.48430e-01, 1.25482e+00, -1.16762e+00, 3.30537e-02, 2.48381e-03, -1.29954e-03 }, // 58/128
+ { -4.15923e-03, 2.88132e-02, -1.39644e-01, 1.24942e+00, -1.17672e+00, 4.34583e-02, -4.75572e-04, -7.90819e-04 }, // 59/128
+ { -3.67878e-03, 2.60992e-02, -1.30701e-01, 1.24367e+00, -1.18550e+00, 5.37267e-02, -3.41562e-03, -2.83916e-04 }, // 60/128
+ { -3.19451e-03, 2.33557e-02, -1.21603e-01, 1.23758e+00, -1.19395e+00, 6.38567e-02, -6.33513e-03, 2.20942e-04 }, // 61/128
+ { -2.70665e-03, 2.05837e-02, -1.12350e-01, 1.23115e+00, -1.20206e+00, 7.38460e-02, -9.23291e-03, 7.23528e-04 }, // 62/128
+ { -2.21539e-03, 1.77843e-02, -1.02946e-01, 1.22439e+00, -1.20984e+00, 8.36921e-02, -1.21078e-02, 1.22361e-03 }, // 63/128
+ { -1.72098e-03, 1.49587e-02, -9.33930e-02, 1.21728e+00, -1.21728e+00, 9.33930e-02, -1.49587e-02, 1.72098e-03 }, // 64/128
+ { -1.22361e-03, 1.21078e-02, -8.36921e-02, 1.20984e+00, -1.22439e+00, 1.02946e-01, -1.77843e-02, 2.21539e-03 }, // 65/128
+ { -7.23528e-04, 9.23291e-03, -7.38460e-02, 1.20206e+00, -1.23115e+00, 1.12350e-01, -2.05837e-02, 2.70665e-03 }, // 66/128
+ { -2.20942e-04, 6.33513e-03, -6.38567e-02, 1.19395e+00, -1.23758e+00, 1.21603e-01, -2.33557e-02, 3.19451e-03 }, // 67/128
+ { 2.83916e-04, 3.41562e-03, -5.37267e-02, 1.18550e+00, -1.24367e+00, 1.30701e-01, -2.60992e-02, 3.67878e-03 }, // 68/128
+ { 7.90819e-04, 4.75572e-04, -4.34583e-02, 1.17672e+00, -1.24942e+00, 1.39644e-01, -2.88132e-02, 4.15923e-03 }, // 69/128
+ { 1.29954e-03, -2.48381e-03, -3.30537e-02, 1.16762e+00, -1.25482e+00, 1.48430e-01, -3.14966e-02, 4.63566e-03 }, // 70/128
+ { 1.80983e-03, -5.46133e-03, -2.25155e-02, 1.15819e+00, -1.25989e+00, 1.57056e-01, -3.41483e-02, 5.10786e-03 }, // 71/128
+ { 2.32148e-03, -8.45574e-03, -1.18461e-02, 1.14843e+00, -1.26461e+00, 1.65521e-01, -3.67674e-02, 5.57561e-03 }, // 72/128
+ { 2.83424e-03, -1.14658e-02, -1.04807e-03, 1.13834e+00, -1.26898e+00, 1.73823e-01, -3.93528e-02, 6.03873e-03 }, // 73/128
+ { 3.34787e-03, -1.44903e-02, 9.87606e-03, 1.12794e+00, -1.27302e+00, 1.81961e-01, -4.19036e-02, 6.49701e-03 }, // 74/128
+ { 3.86214e-03, -1.75279e-02, 2.09236e-02, 1.11721e+00, -1.27670e+00, 1.89933e-01, -4.44189e-02, 6.95024e-03 }, // 75/128
+ { 4.37681e-03, -2.05773e-02, 3.20920e-02, 1.10617e+00, -1.28005e+00, 1.97737e-01, -4.68976e-02, 7.39824e-03 }, // 76/128
+ { 4.89163e-03, -2.36373e-02, 4.33784e-02, 1.09481e+00, -1.28305e+00, 2.05372e-01, -4.93389e-02, 7.84082e-03 }, // 77/128
+ { 5.40637e-03, -2.67066e-02, 5.47801e-02, 1.08314e+00, -1.28570e+00, 2.12836e-01, -5.17418e-02, 8.27777e-03 }, // 78/128
+ { 5.92077e-03, -2.97837e-02, 6.62943e-02, 1.07115e+00, -1.28801e+00, 2.20128e-01, -5.41055e-02, 8.70893e-03 }, // 79/128
+ { 6.43460e-03, -3.28675e-02, 7.79181e-02, 1.05886e+00, -1.28997e+00, 2.27247e-01, -5.64291e-02, 9.13410e-03 }, // 80/128
+ { 6.94760e-03, -3.59565e-02, 8.96486e-02, 1.04627e+00, -1.29159e+00, 2.34191e-01, -5.87118e-02, 9.55311e-03 }, // 81/128
+ { 7.45954e-03, -3.90493e-02, 1.01483e-01, 1.03337e+00, -1.29287e+00, 2.40960e-01, -6.09527e-02, 9.96577e-03 }, // 82/128
+ { 7.97016e-03, -4.21447e-02, 1.13418e-01, 1.02017e+00, -1.29380e+00, 2.47551e-01, -6.31511e-02, 1.03719e-02 }, // 83/128
+ { 8.47921e-03, -4.52412e-02, 1.25450e-01, 1.00667e+00, -1.29439e+00, 2.53964e-01, -6.53061e-02, 1.07714e-02 }, // 84/128
+ { 8.98645e-03, -4.83374e-02, 1.37578e-01, 9.92883e-01, -1.29463e+00, 2.60198e-01, -6.74171e-02, 1.11640e-02 }, // 85/128
+ { 9.49163e-03, -5.14320e-02, 1.49797e-01, 9.78802e-01, -1.29453e+00, 2.66252e-01, -6.94831e-02, 1.15496e-02 }, // 86/128
+ { 9.99449e-03, -5.45235e-02, 1.62104e-01, 9.64433e-01, -1.29410e+00, 2.72125e-01, -7.15036e-02, 1.19280e-02 }, // 87/128
+ { 1.04948e-02, -5.76105e-02, 1.74496e-01, 9.49779e-01, -1.29332e+00, 2.77817e-01, -7.34779e-02, 1.22991e-02 }, // 88/128
+ { 1.09923e-02, -6.06916e-02, 1.86970e-01, 9.34844e-01, -1.29220e+00, 2.83325e-01, -7.54052e-02, 1.26627e-02 }, // 89/128
+ { 1.14867e-02, -6.37653e-02, 1.99523e-01, 9.19630e-01, -1.29074e+00, 2.88650e-01, -7.72849e-02, 1.30187e-02 }, // 90/128
+ { 1.19778e-02, -6.68302e-02, 2.12151e-01, 9.04143e-01, -1.28895e+00, 2.93791e-01, -7.91163e-02, 1.33668e-02 }, // 91/128
+ { 1.24653e-02, -6.98849e-02, 2.24851e-01, 8.88384e-01, -1.28682e+00, 2.98747e-01, -8.08990e-02, 1.37071e-02 }, // 92/128
+ { 1.29490e-02, -7.29278e-02, 2.37619e-01, 8.72357e-01, -1.28435e+00, 3.03518e-01, -8.26321e-02, 1.40393e-02 }, // 93/128
+ { 1.34286e-02, -7.59576e-02, 2.50452e-01, 8.56067e-01, -1.28156e+00, 3.08103e-01, -8.43153e-02, 1.43633e-02 }, // 94/128
+ { 1.39039e-02, -7.89727e-02, 2.63347e-01, 8.39517e-01, -1.27843e+00, 3.12502e-01, -8.59479e-02, 1.46789e-02 }, // 95/128
+ { 1.43747e-02, -8.19717e-02, 2.76300e-01, 8.22711e-01, -1.27497e+00, 3.16714e-01, -8.75294e-02, 1.49862e-02 }, // 96/128
+ { 1.48406e-02, -8.49530e-02, 2.89308e-01, 8.05653e-01, -1.27118e+00, 3.20739e-01, -8.90593e-02, 1.52848e-02 }, // 97/128
+ { 1.53014e-02, -8.79152e-02, 3.02366e-01, 7.88347e-01, -1.26707e+00, 3.24577e-01, -9.05371e-02, 1.55747e-02 }, // 98/128
+ { 1.57569e-02, -9.08568e-02, 3.15472e-01, 7.70796e-01, -1.26263e+00, 3.28227e-01, -9.19624e-02, 1.58558e-02 }, // 99/128
+ { 1.62068e-02, -9.37762e-02, 3.28622e-01, 7.53006e-01, -1.25786e+00, 3.31690e-01, -9.33347e-02, 1.61280e-02 }, // 100/128
+ { 1.66509e-02, -9.66721e-02, 3.41811e-01, 7.34980e-01, -1.25278e+00, 3.34965e-01, -9.46536e-02, 1.63911e-02 }, // 101/128
+ { 1.70889e-02, -9.95428e-02, 3.55037e-01, 7.16722e-01, -1.24738e+00, 3.38052e-01, -9.59188e-02, 1.66451e-02 }, // 102/128
+ { 1.75206e-02, -1.02387e-01, 3.68296e-01, 6.98238e-01, -1.24166e+00, 3.40952e-01, -9.71297e-02, 1.68899e-02 }, // 103/128
+ { 1.79458e-02, -1.05203e-01, 3.81583e-01, 6.79531e-01, -1.23563e+00, 3.43664e-01, -9.82862e-02, 1.71253e-02 }, // 104/128
+ { 1.83641e-02, -1.07989e-01, 3.94895e-01, 6.60606e-01, -1.22928e+00, 3.46188e-01, -9.93878e-02, 1.73513e-02 }, // 105/128
+ { 1.87754e-02, -1.10744e-01, 4.08229e-01, 6.41467e-01, -1.22263e+00, 3.48526e-01, -1.00434e-01, 1.75678e-02 }, // 106/128
+ { 1.91794e-02, -1.13466e-01, 4.21579e-01, 6.22120e-01, -1.21567e+00, 3.50676e-01, -1.01425e-01, 1.77746e-02 }, // 107/128
+ { 1.95759e-02, -1.16154e-01, 4.34944e-01, 6.02568e-01, -1.20840e+00, 3.52639e-01, -1.02361e-01, 1.79718e-02 }, // 108/128
+ { 1.99646e-02, -1.18806e-01, 4.48318e-01, 5.82817e-01, -1.20083e+00, 3.54416e-01, -1.03240e-01, 1.81593e-02 }, // 109/128
+ { 2.03452e-02, -1.21421e-01, 4.61698e-01, 5.62871e-01, -1.19297e+00, 3.56008e-01, -1.04064e-01, 1.83369e-02 }, // 110/128
+ { 2.07177e-02, -1.23997e-01, 4.75079e-01, 5.42735e-01, -1.18481e+00, 3.57414e-01, -1.04831e-01, 1.85046e-02 }, // 111/128
+ { 2.10816e-02, -1.26533e-01, 4.88459e-01, 5.22415e-01, -1.17635e+00, 3.58635e-01, -1.05541e-01, 1.86623e-02 }, // 112/128
+ { 2.14369e-02, -1.29027e-01, 5.01832e-01, 5.01915e-01, -1.16761e+00, 3.59672e-01, -1.06195e-01, 1.88101e-02 }, // 113/128
+ { 2.17832e-02, -1.31477e-01, 5.15195e-01, 4.81241e-01, -1.15857e+00, 3.60525e-01, -1.06793e-01, 1.89477e-02 }, // 114/128
+ { 2.21203e-02, -1.33883e-01, 5.28544e-01, 4.60397e-01, -1.14926e+00, 3.61195e-01, -1.07333e-01, 1.90753e-02 }, // 115/128
+ { 2.24481e-02, -1.36242e-01, 5.41875e-01, 4.39388e-01, -1.13966e+00, 3.61683e-01, -1.07816e-01, 1.91927e-02 }, // 116/128
+ { 2.27662e-02, -1.38553e-01, 5.55183e-01, 4.18221e-01, -1.12979e+00, 3.61990e-01, -1.08243e-01, 1.92998e-02 }, // 117/128
+ { 2.30745e-02, -1.40814e-01, 5.68465e-01, 3.96899e-01, -1.11965e+00, 3.62117e-01, -1.08612e-01, 1.93968e-02 }, // 118/128
+ { 2.33727e-02, -1.43025e-01, 5.81717e-01, 3.75429e-01, -1.10923e+00, 3.62064e-01, -1.08925e-01, 1.94834e-02 }, // 119/128
+ { 2.36607e-02, -1.45183e-01, 5.94935e-01, 3.53816e-01, -1.09855e+00, 3.61833e-01, -1.09180e-01, 1.95598e-02 }, // 120/128
+ { 2.39381e-02, -1.47287e-01, 6.08114e-01, 3.32066e-01, -1.08760e+00, 3.61425e-01, -1.09379e-01, 1.96258e-02 }, // 121/128
+ { 2.42049e-02, -1.49335e-01, 6.21250e-01, 3.10183e-01, -1.07640e+00, 3.60840e-01, -1.09520e-01, 1.96814e-02 }, // 122/128
+ { 2.44607e-02, -1.51327e-01, 6.34339e-01, 2.88174e-01, -1.06494e+00, 3.60080e-01, -1.09605e-01, 1.97267e-02 }, // 123/128
+ { 2.47055e-02, -1.53260e-01, 6.47378e-01, 2.66043e-01, -1.05322e+00, 3.59146e-01, -1.09633e-01, 1.97616e-02 }, // 124/128
+ { 2.49389e-02, -1.55133e-01, 6.60361e-01, 2.43798e-01, -1.04126e+00, 3.58039e-01, -1.09604e-01, 1.97862e-02 }, // 125/128
+ { 2.51608e-02, -1.56945e-01, 6.73286e-01, 2.21442e-01, -1.02906e+00, 3.56761e-01, -1.09519e-01, 1.98003e-02 }, // 126/128
+ { 2.53710e-02, -1.58694e-01, 6.86147e-01, 1.98983e-01, -1.01662e+00, 3.55314e-01, -1.09378e-01, 1.98041e-02 }, // 127/128
+ { 2.55693e-02, -1.60378e-01, 6.98940e-01, 1.76425e-01, -1.00394e+00, 3.53697e-01, -1.09180e-01, 1.97975e-02 }, // 128/128
+};
+
diff --git a/gr-filter/include/gnuradio/filter/mmse_interp_differentiator_cc.h b/gr-filter/include/gnuradio/filter/mmse_interp_differentiator_cc.h
new file mode 100644
index 0000000000..5170264b2f
--- /dev/null
+++ b/gr-filter/include/gnuradio/filter/mmse_interp_differentiator_cc.h
@@ -0,0 +1,80 @@
+/* -*- c++ -*- */
+/*
+ * Copyright (C) 2002,2007,2012,2017 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 file; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef _MMSE_INTERP_DIFFERENTIATOR_CC_H_
+#define _MMSE_INTERP_DIFFERENTIATOR_CC_H_
+
+#include <gnuradio/filter/api.h>
+#include <gnuradio/filter/fir_filter.h>
+#include <gnuradio/gr_complex.h>
+#include <vector>
+
+namespace gr {
+ namespace filter {
+
+ /*!
+ * \brief Compute intermediate samples of the derivative of a signal
+ * between signal samples x(k*Ts)
+ * \ingroup filter_primitive
+ *
+ * \details
+ * This implements a Mininum Mean Squared Error interpolating
+ * differentiator with 8 taps. It is suitable for signals where the
+ * derivative of a signal has a bandwidth of interest in the range
+ * (-Fs/4, Fs/4), where Fs is the samples rate.
+ *
+ * Although mu, the fractional delay, is specified as a float, in
+ * the range [0.0, 1.0], it is actually quantized. That is, mu is
+ * quantized in the differentiate method to 128th's of a sample.
+ *
+ */
+ class FILTER_API mmse_interp_differentiator_cc
+ {
+ public:
+ mmse_interp_differentiator_cc();
+ ~mmse_interp_differentiator_cc();
+
+ unsigned ntaps() const;
+ unsigned nsteps() const;
+
+ /*!
+ * \brief compute a single interpolated differentiated output value.
+ *
+ * \p input must have ntaps() valid entries.
+ * input[0] .. input[ntaps() - 1] are referenced to compute the output
+ * value.
+ *
+ * \p mu must be in the range [0, 1] and specifies the fractional delay.
+ *
+ * \throws std::runtime_error if mu is not in the range [0, 1].
+ *
+ * \returns the interpolated differentiated output value.
+ */
+ gr_complex differentiate(const gr_complex input[], float mu) const;
+
+ protected:
+ std::vector<kernel::fir_filter_ccf *> filters;
+ };
+
+ } /* namespace filter */
+} /* namespace gr */
+
+#endif /* _MMSE_INTERP_DIFFERENTIATOR_CC_H_ */
diff --git a/gr-filter/include/gnuradio/filter/mmse_interp_differentiator_ff.h b/gr-filter/include/gnuradio/filter/mmse_interp_differentiator_ff.h
new file mode 100644
index 0000000000..eb176cd753
--- /dev/null
+++ b/gr-filter/include/gnuradio/filter/mmse_interp_differentiator_ff.h
@@ -0,0 +1,79 @@
+/* -*- c++ -*- */
+/*
+ * Copyright (C) 2002,2007,2012,2017 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 file; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef _MMSE_INTERP_DIFFERENTIATOR_FF_H_
+#define _MMSE_INTERP_DIFFERENTIATOR_FF_H_
+
+#include <gnuradio/filter/api.h>
+#include <gnuradio/filter/fir_filter.h>
+#include <vector>
+
+namespace gr {
+ namespace filter {
+
+ /*!
+ * \brief Compute intermediate samples of the derivative of a signal
+ * between signal samples x(k*Ts)
+ * \ingroup filter_primitive
+ *
+ * \details
+ * This implements a Mininum Mean Squared Error interpolating
+ * differentiator with 8 taps. It is suitable for signals where the
+ * derivative of a signal has a bandwidth of interest in the range
+ * (-Fs/4, Fs/4), where Fs is the samples rate.
+ *
+ * Although mu, the fractional delay, is specified as a float, in
+ * the range [0.0, 1.0], it is actually quantized. That is, mu is
+ * quantized in the differentiate method to 128th's of a sample.
+ *
+ */
+ class FILTER_API mmse_interp_differentiator_ff
+ {
+ public:
+ mmse_interp_differentiator_ff();
+ ~mmse_interp_differentiator_ff();
+
+ unsigned ntaps() const;
+ unsigned nsteps() const;
+
+ /*!
+ * \brief compute a single interpolated differentiated output value.
+ *
+ * \p input must have ntaps() valid entries.
+ * input[0] .. input[ntaps() - 1] are referenced to compute the output
+ * value.
+ *
+ * \p mu must be in the range [0, 1] and specifies the fractional delay.
+ *
+ * \throws std::runtime_error if mu is not in the range [0, 1].
+ *
+ * \returns the interpolated differentiated output value.
+ */
+ float differentiate(const float input[], float mu) const;
+
+ protected:
+ std::vector<kernel::fir_filter_fff *> filters;
+ };
+
+ } /* namespace filter */
+} /* namespace gr */
+
+#endif /* _MMSE_INTERP_DIFFERENTIATOR_FF_H_ */
diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt
index d04e60370c..ebc5381785 100644
--- a/gr-filter/lib/CMakeLists.txt
+++ b/gr-filter/lib/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright 2012-2014 Free Software Foundation, Inc.
+# Copyright (C) 2012-2014,2017 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
@@ -59,6 +59,8 @@ list(APPEND filter_sources
iir_filter.cc
mmse_fir_interpolator_cc.cc
mmse_fir_interpolator_ff.cc
+ mmse_interp_differentiator_cc.cc
+ mmse_interp_differentiator_ff.cc
pm_remez.cc
polyphase_filterbank.cc
${generated_sources}
@@ -167,6 +169,8 @@ if(ENABLE_TESTING)
${CMAKE_CURRENT_SOURCE_DIR}/qa_fir_filter_with_buffer.cc
${CMAKE_CURRENT_SOURCE_DIR}/qa_mmse_fir_interpolator_cc.cc
${CMAKE_CURRENT_SOURCE_DIR}/qa_mmse_fir_interpolator_ff.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/qa_mmse_interp_differentiator_cc.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/qa_mmse_interp_differentiator_ff.cc
)
add_executable(test-gr-filter ${test_gr_filter_sources})
diff --git a/gr-filter/lib/gen_interpolator_taps/diff_objective_fct.c b/gr-filter/lib/gen_interpolator_taps/diff_objective_fct.c
new file mode 100644
index 0000000000..367aeeb356
--- /dev/null
+++ b/gr-filter/lib/gen_interpolator_taps/diff_objective_fct.c
@@ -0,0 +1,214 @@
+/* -*- c -*- */
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/*
+ * generate MMSE interpolating differentiator FIR table values
+ */
+
+#include <math.h>
+#include <assert.h>
+#include <gsl/gsl_integration.h>
+
+#define MU 0.5 /* the MU for which we're computing coeffs */
+
+#define Ts (1.0) /* sampling period */
+#define B (1.0/(4*Ts)) /* one-sided signal bandwidth */
+//#define B (1.0/(8./3*Ts)) /* one-sided signal bandwidth */
+
+static unsigned global_n;
+static double *global_h;
+double global_mu = MU;
+double global_B = B;
+
+gsl_integration_workspace *global_gsl_int_workspace = NULL;
+
+/*
+ * This function computes the difference squared between the ideal
+ * interpolating differentiator frequency response at frequency OMEGA and the
+ * approximation defined by the FIR coefficients in global_h[]
+ *
+ * See eqn (9-7), "Digital Communication Receivers", Meyr, Moeneclaey
+ * and Fechtel, Wiley, 1998. for the jist of what going on here.
+ *
+ * See eqns (7.99) and (7.100), "Discrete Time Signal Processing",
+ * Oppenheim, Alan V., Schafer, Ronald W., Prentice Hall 1989. for the
+ * (non-bandlimited) ideal differentiator.
+ *
+ * Andy Walls derived the expression for the h[n] for the bandlimited
+ * differentiator by performing an Inverse DTFT on the bandlimited ideal
+ * response. (Integration by parts.)
+ */
+
+static double
+integrand (double omega, void *params)
+{
+ double real_ideal;
+ double real_approx;
+ double real_diff;
+ double imag_ideal;
+ double imag_approx;
+ double imag_diff;
+
+ int i, n;
+ int I1;
+ double *h;
+
+ /*
+ * Ideal differentiator frequency response,
+ * with freq modulation to effect a time shift left (advance),
+ * and band limited to (-fc, fc).
+ * N.B. fc is global_B, mu is global_mu, Fs is 1/Ts, 2*pi*f is omega.
+ * fc and Fs are in Hz, and omega is in radians/second.
+ *
+ * H(f) = j*2*pi*f*Ts * exp(j*2*pi*f*Ts*mu) for |f| <= fc <= Fs/2
+ */
+ real_ideal = -omega * Ts * sin (omega * Ts * global_mu);
+ imag_ideal = omega * Ts * cos (omega * Ts * global_mu);
+
+ n = global_n;
+ h = global_h;
+ I1 = -(n / 2);
+
+ real_approx = 0;
+ imag_approx = 0;
+
+ for (i = 0; i < n; i++){
+ real_approx += h[i] * cos (-omega * Ts * (i + I1));
+ imag_approx += h[i] * sin (-omega * Ts * (i + I1));
+ }
+
+ real_diff = real_ideal - real_approx;
+ imag_diff = imag_ideal - imag_approx;
+
+ return real_diff * real_diff + imag_diff * imag_diff;
+}
+
+/*
+ * Integrate the difference squared over all frequencies of interest.
+ */
+double
+c_fcn (double *x, int n)
+{
+ gsl_function F;
+ double result, error;
+
+ F.function = integrand;
+ F.params = NULL;
+
+ assert ((n & 1) == 0); /* assert n is even */
+ global_n = n;
+ global_h = x;
+ // global_B is fc, the cutoff frequency in Hz. It defaults to Fs/4.0.
+ gsl_integration_qag(&F, -2 * M_PI * global_B, 2 * M_PI * global_B,
+ 0.0, 1e-12, 1000, GSL_INTEG_GAUSS61,
+ global_gsl_int_workspace, &result, &error);
+ return result;
+}
+
+/* this is the interface expected by the calling fortran code */
+
+double
+objective (double x[], int *ndim)
+{
+ return c_fcn (x, *ndim);
+}
+
+/*
+ * starting guess for optimization
+ */
+void initpt (double x[], int ndim)
+{
+ int i;
+ double w_c;
+ double start;
+ double t;
+ double arg;
+ double denom;
+
+ /* Bandlimited, time shifted, differentiator filter (infinitely long)
+ *
+ * Andy Walls derived the expression for the h[n] for the bandlimited
+ * differentiator by performing an Inverse DTFT on the bandlimited ideal
+ * response. (Integration by parts.)
+ *
+ * Ideal differentiator frequency response,
+ * with freq modulation to effect a time shift left (advance),
+ * and band limited to (-fc, fc).
+ * N.B. fc is global_B, mu is global_mu, Fs is 1/Ts.
+ * fc and Fs are in Hz, and omega is in radians/second.
+ *
+ * H(f) = j*2*pi*f*Ts * exp(j*2*pi*f*Ts*mu) for |f| <= fc <= Fs/2
+ * H(f) = 0 for |f| > fc <= Fs/2
+ *
+ * Substituting normalized radian frequency, w, for 2*pi*f*Ts, we get:
+ *
+ * H(w) = j*w * exp(j*w*mu) for |w| <= w_c = 2*pi*fc*Ts <= pi
+ * H(w) = 0 for |w| > w_c = 2*pi*fc*Ts <= pi
+ *
+ * and the Inverse Discrete Time Fourier Transform is:
+ *
+ * h[n] = (1/(2*pi)) * integral(-pi,pi)[ H(w)*exp(j*w*n)*dw ]
+ *
+ * Performing integration by parts of the above expression, using these hints:
+ * integral [u*dv] = u*v - integral [v*du]
+ * u = j*w
+ * dv = exp(j*w*(n+mu))*dw
+ *
+ * We eventually get an infinitely long, but decaying impulse response:
+ *
+ * h[n] = (w_c/pi)/(n+mu) * [cos(pi*(w_c/pi)*(n+mu)) - sinc((w_c/pi)*(n+mu))]
+ *
+ * = [ w_c*(n+mu)*cos(w_c*(n+mu)) - sin(w_c*(n+mu)) ] / (pi*(n+mu)^2)
+ *
+ * w_c is the normalized radian cutoff frequency:
+ * 2*pi*fc*Ts, with fc <= Fs/2 and Fs = 1/Ts, so w_c <= pi
+ *
+ * For the case of w_c = pi and mu = 0, the cos() term alternates
+ * between +/- 1 and the sinc() term goes to 0 except at n == 0, so
+ * we get the following:
+ *
+ * h[n] = (-1)^n / n for n != 0
+ * = 0 for n == 0
+ * or
+ *
+ * h[n] = ..., 1/5, -1/4, 1/3, -1/2, 1, 0, -1, 1/2, -1/3, 1/4, -1/5, ...
+ *
+ * Compare with:
+ * Eqns (7.99) and (7.100), "Discrete Time Signal Processing",
+ * Oppenheim, Alan V., Schafer, Ronald W., Prentice Hall 1989. for the
+ * non-bandlimited (w_c = pi) ideal differentiator.
+ */
+
+ /* Truncated, bandlimited, time shifted, differentiator filter */
+ w_c = 2.0 * M_PI * global_B * Ts;
+ start = (double)(-ndim/2) + global_mu;
+
+ for (i = 0; i < ndim; i++){
+ t = (double)(i) + start;
+ arg = w_c * t;
+ denom = M_PI*t*t; /* always >= 0.0 */
+ if (denom < 1e-9)
+ x[i] = 0.0;
+ else
+ x[i] = (arg * cos(arg) - sin(arg))/denom;
+ }
+}
diff --git a/gr-filter/lib/gen_interpolator_taps/gen_interp_differentiator_taps.c b/gr-filter/lib/gen_interpolator_taps/gen_interp_differentiator_taps.c
new file mode 100644
index 0000000000..d34ccea6c6
--- /dev/null
+++ b/gr-filter/lib/gen_interpolator_taps/gen_interp_differentiator_taps.c
@@ -0,0 +1,182 @@
+/* -*- c++ -*- */
+/*
+ * Copyright (C) 2017 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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <gsl/gsl_integration.h>
+
+#define NSTEPS 10 // how many steps of mu are in the generated table
+#define MAX_NSTEPS 256
+#define NTAPS 8 // # of taps in the interpolator
+#define MAX_NTAPS 128
+
+extern void initpt (double x[], int ntaps);
+extern double objective (double x[], int *ntaps);
+extern double global_mu;
+extern double global_B;
+extern gsl_integration_workspace *global_gsl_int_workspace;
+
+// fortran
+extern double prax2_ (double (fct)(double x[], int *ntaps),
+ double initv[], int *ntaps, double result[]);
+
+static void
+usage (char *name)
+{
+ fprintf (stderr, "usage: %s [-v] [-n <nsteps>] [-t <ntaps>] [-B <bw>]\n", name);
+ exit (1);
+}
+
+static void
+printline (double x[], int ntaps, int imu, int nsteps)
+{
+ int i;
+
+ printf (" { ");
+ for (i = 0; i < ntaps; i++){
+ printf ("%12.5e", x[i]);
+ if (i != ntaps - 1)
+ printf (", ");
+ else
+ printf (" }, // %3d/%d\n", imu, nsteps);
+ }
+}
+
+int
+main (int argc, char **argv)
+{
+ double xx[MAX_NSTEPS+1][MAX_NTAPS];
+ int ntaps = NTAPS;
+ int nsteps = NSTEPS;
+ int i, j;
+ double result;
+ double step_size;
+ int c;
+ int verbose = 0;
+
+ global_B = 0.25;
+
+ while ((c = getopt (argc, argv, "n:t:B:v")) != EOF){
+ switch (c){
+ case 'n':
+ nsteps = strtol (optarg, 0, 0);
+ break;
+
+ case 't':
+ ntaps = strtol (optarg, 0, 0);
+ break;
+
+ case 'B':
+ global_B = strtod (optarg, 0);
+ break;
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ default:
+ usage (argv[0]);
+ break;
+ }
+ }
+
+ if ((nsteps & 1) != 0){
+ fprintf (stderr, "%s: nsteps must be even\n", argv[0]);
+ exit (1);
+ }
+
+ if (nsteps > MAX_NSTEPS){
+ fprintf (stderr, "%s: nsteps must be < %d\n", argv[0], MAX_NSTEPS);
+ exit (1);
+ }
+
+ if ((ntaps & 1) != 0){
+ fprintf (stderr, "%s: ntaps must be even\n", argv[0]);
+ exit (1);
+ }
+
+ if (nsteps > MAX_NTAPS){
+ fprintf (stderr, "%s: ntaps must be < %d\n", argv[0], MAX_NTAPS);
+ exit (1);
+ }
+
+ if (global_B < 0 || global_B > 0.5){
+ fprintf (stderr, "%s: bandwidth must be in the range (0, 0.5)\n", argv[0]);
+ exit (1);
+ }
+
+ global_gsl_int_workspace = gsl_integration_workspace_alloc(4000);
+ if (global_gsl_int_workspace == NULL) {
+ fprintf (stderr, "%s: unable to allocate GSL integration work space\n",
+ argv[0]);
+ exit (1);
+ }
+
+ step_size = 1.0/nsteps;
+
+ // compute optimal values for mu <= 1.0
+
+ for (j = 0; j <= nsteps; j++){
+
+ global_mu = j * step_size; // this determines the MU for which we're computing the taps
+
+ // initialize X to a reasonable starting value
+
+ initpt (&xx[j][0], ntaps);
+
+ // find the value of X that minimizes the value of OBJECTIVE
+
+ result = prax2_ (objective, &xx[j][0], &ntaps, &xx[j][0]);
+
+ if (verbose){
+ fprintf (stderr, "Mu: %10.8f\t", global_mu);
+ fprintf (stderr, "Objective: %g\n", result);
+ }
+ }
+
+ gsl_integration_workspace_free(global_gsl_int_workspace);
+
+ // now print out the table
+
+ printf ("\
+/*\n\
+ * This file was machine generated by gen_interp_differentiator_taps.\n\
+ * DO NOT EDIT BY HAND.\n\
+ */\n\n");
+
+
+ printf ("static const int DNTAPS = %4d;\n", ntaps);
+ printf ("static const int DNSTEPS = %4d;\n", nsteps);
+ printf ("static const double DBANDWIDTH = %g;\n\n", global_B);
+
+ printf ("static const float Dtaps[DNSTEPS+1][DNTAPS] = {\n");
+ printf (" // -4 -3 -2 -1 0 1 2 3 mu\n");
+
+
+ for (i = 0; i <= nsteps; i++)
+ printline (xx[i], ntaps, i, nsteps);
+
+ printf ("};\n\n");
+
+ return 0;
+}
diff --git a/gr-filter/lib/gen_interpolator_taps/generate.sh b/gr-filter/lib/gen_interpolator_taps/generate.sh
new file mode 100755
index 0000000000..a7a6c6cafe
--- /dev/null
+++ b/gr-filter/lib/gen_interpolator_taps/generate.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+#
+# Copyright (C) 2017 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 file; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+gcc -c -o diff_objective_fct.o diff_objective_fct.c
+gfortran -c -o praxis.o praxis.f
+gcc -c -o gen_interp_differentiator_taps.o gen_interp_differentiator_taps.c
+
+gfortran -o gen_interp_differentiator_taps \
+ gen_interp_differentiator_taps.o praxis.o diff_objective_fct.o \
+ -lgsl -lgslcblas
+
+./gen_interp_differentiator_taps -n 128 -t 8 -B 0.25 \
+ > interp_differentiator_taps.h
+
+gcc -c -o simpson.o simpson.c
+gcc -c -o objective_fct.o objective_fct.c
+gcc -c -o gen_interpolator_taps.o gen_interpolator_taps.c
+
+gfortran -o gen_interpolator_taps \
+ gen_interpolator_taps.o praxis.o objective_fct.o simpson.o
+
+./gen_interpolator_taps -n 128 -t 8 -B 0.25 \
+ > interpolator_taps.h
+
diff --git a/gr-filter/lib/mmse_interp_differentiator_cc.cc b/gr-filter/lib/mmse_interp_differentiator_cc.cc
new file mode 100644
index 0000000000..2cdd390774
--- /dev/null
+++ b/gr-filter/lib/mmse_interp_differentiator_cc.cc
@@ -0,0 +1,78 @@
+/* -*- c++ -*- */
+/*
+ * Copyright (C) 2002,2012,2017 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 file; 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 <gnuradio/filter/mmse_interp_differentiator_cc.h>
+#include "gnuradio/filter/interp_differentiator_taps.h"
+#include <stdexcept>
+
+namespace gr {
+ namespace filter {
+
+ mmse_interp_differentiator_cc::mmse_interp_differentiator_cc()
+ {
+ filters.resize (DNSTEPS + 1);
+
+ for(int i = 0; i < DNSTEPS + 1; i++) {
+ std::vector<float> t (&Dtaps[i][0], &Dtaps[i][DNTAPS]);
+ filters[i] = new kernel::fir_filter_ccf(1, t);
+ }
+ }
+
+ mmse_interp_differentiator_cc::~mmse_interp_differentiator_cc()
+ {
+ for(int i = 0; i < DNSTEPS + 1; i++)
+ delete filters[i];
+ }
+
+ unsigned
+ mmse_interp_differentiator_cc::ntaps() const
+ {
+ return DNTAPS;
+ }
+
+ unsigned
+ mmse_interp_differentiator_cc::nsteps() const
+ {
+ return DNSTEPS;
+ }
+
+ gr_complex
+ mmse_interp_differentiator_cc::differentiate(const gr_complex input[],
+ float mu) const
+ {
+ int imu = (int)rint(mu * DNSTEPS);
+
+ if((imu < 0) || (imu > DNSTEPS)) {
+ throw std::runtime_error(
+ "mmse_interp_differentiator_cc: imu out of bounds.\n");
+ }
+
+ gr_complex r = filters[imu]->filter(input);
+ return r;
+ }
+
+ } /* namespace filter */
+} /* namespace gr */
diff --git a/gr-filter/lib/mmse_interp_differentiator_ff.cc b/gr-filter/lib/mmse_interp_differentiator_ff.cc
new file mode 100644
index 0000000000..247169fdba
--- /dev/null
+++ b/gr-filter/lib/mmse_interp_differentiator_ff.cc
@@ -0,0 +1,78 @@
+/* -*- c++ -*- */
+/*
+ * Copyright (C) 2002,2012,2017 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 file; 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 <gnuradio/filter/mmse_interp_differentiator_ff.h>
+#include "gnuradio/filter/interp_differentiator_taps.h"
+#include <stdexcept>
+
+namespace gr {
+ namespace filter {
+
+ mmse_interp_differentiator_ff::mmse_interp_differentiator_ff()
+ {
+ filters.resize (DNSTEPS + 1);
+
+ for(int i = 0; i < DNSTEPS + 1; i++) {
+ std::vector<float> t (&Dtaps[i][0], &Dtaps[i][DNTAPS]);
+ filters[i] = new kernel::fir_filter_fff(1, t);
+ }
+ }
+
+ mmse_interp_differentiator_ff::~mmse_interp_differentiator_ff()
+ {
+ for(int i = 0; i < DNSTEPS + 1; i++)
+ delete filters[i];
+ }
+
+ unsigned
+ mmse_interp_differentiator_ff::ntaps() const
+ {
+ return DNTAPS;
+ }
+
+ unsigned
+ mmse_interp_differentiator_ff::nsteps() const
+ {
+ return DNSTEPS;
+ }
+
+ float
+ mmse_interp_differentiator_ff::differentiate(const float input[],
+ float mu) const
+ {
+ int imu = (int)rint(mu * DNSTEPS);
+
+ if((imu < 0) || (imu > DNSTEPS)) {
+ throw std::runtime_error(
+ "mmse_interp_differentiator_ff: imu out of bounds.\n");
+ }
+
+ float r = filters[imu]->filter(input);
+ return r;
+ }
+
+ } /* namespace filter */
+} /* namespace gr */
diff --git a/gr-filter/lib/qa_filter.cc b/gr-filter/lib/qa_filter.cc
index b7760b19ea..e1cfefdbed 100644
--- a/gr-filter/lib/qa_filter.cc
+++ b/gr-filter/lib/qa_filter.cc
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 Free Software Foundation, Inc.
+ * Copyright (C) 2012,2017 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
@@ -30,6 +30,8 @@
#include <qa_fir_filter_with_buffer.h>
#include <qa_mmse_fir_interpolator_cc.h>
#include <qa_mmse_fir_interpolator_ff.h>
+#include <qa_mmse_interp_differentiator_cc.h>
+#include <qa_mmse_interp_differentiator_ff.h>
CppUnit::TestSuite *
qa_gr_filter::suite ()
@@ -42,6 +44,8 @@ qa_gr_filter::suite ()
s->addTest(gr::filter::ccf::qa_fir_filter_with_buffer_ccf::suite());
s->addTest(gr::filter::qa_mmse_fir_interpolator_cc::suite());
s->addTest(gr::filter::qa_mmse_fir_interpolator_ff::suite());
+ s->addTest(gr::filter::qa_mmse_interp_differentiator_cc::suite());
+ s->addTest(gr::filter::qa_mmse_interp_differentiator_ff::suite());
return s;
}
diff --git a/gr-filter/lib/qa_mmse_interp_differentiator_cc.cc b/gr-filter/lib/qa_mmse_interp_differentiator_cc.cc
new file mode 100644
index 0000000000..55bb4e045b
--- /dev/null
+++ b/gr-filter/lib/qa_mmse_interp_differentiator_cc.cc
@@ -0,0 +1,130 @@
+/* -*- c++ -*- */
+/*
+ * Copyright (C) 2002,2007,2012,2017 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 file; 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 <cppunit/TestAssert.h>
+#include <qa_mmse_interp_differentiator_cc.h>
+#include <gnuradio/filter/mmse_interp_differentiator_cc.h>
+#include <gnuradio/fft/fft.h>
+#include <volk/volk.h>
+#include <cstdio>
+#include <cmath>
+#include <stdexcept>
+#include <stdint.h>
+
+namespace gr {
+ namespace filter {
+
+ static const double k1 = 0.25 * 2 * M_PI;
+ static const double k2 = 0.125 * M_PI;
+ static const double k3 = 0.077 * 2 * M_PI;
+ static const double k4 = 0.3 * M_PI;
+
+ static double
+ phase_wrap(double arg)
+ {
+ while (arg >= 2.0*M_PI)
+ arg -= 2.0*M_PI;
+ while (arg <= 2.0*M_PI)
+ arg += 2.0*M_PI;
+ return arg;
+ }
+
+ static float
+ test_fcn_sin(double index)
+ {
+ double arg1 = phase_wrap(index * k1 + k2);
+ double arg2 = phase_wrap(index * k3 + k4);
+
+ return (2 * sin (arg1) + 3 * sin (arg2));
+ }
+
+ static float
+ test_fcn_cos(double index)
+ {
+ double arg1 = phase_wrap(index * k1 + k2);
+ double arg2 = phase_wrap(index * k3 + k4);
+
+ return (2 * cos (arg1) + 3 * cos (arg2));
+ }
+
+ static float
+ test_fcn_dsin(double index)
+ {
+ double arg1 = phase_wrap(index * k1 + k2);
+ double arg2 = phase_wrap(index * k3 + k4);
+
+ return (k1 * 2 * cos (arg1) + k3 * 3 * cos (arg2));
+ }
+
+ static float
+ test_fcn_dcos(double index)
+ {
+ double arg1 = phase_wrap(index * k1 + k2);
+ double arg2 = phase_wrap(index * k3 + k4);
+
+ return (-k1 * 2 * sin (arg1) - k3 * 3 * sin (arg2));
+ }
+
+ static gr_complex
+ test_fcn(double index)
+ {
+ return gr_complex(test_fcn_cos(index), test_fcn_sin(index));
+ }
+
+ static gr_complex
+ test_fcn_d(double index)
+ {
+ return gr_complex(test_fcn_dcos(index), test_fcn_dsin(index));
+ }
+
+ void
+ qa_mmse_interp_differentiator_cc::t1()
+ {
+ static const unsigned N = 100;
+ gr_complex *input = (gr_complex*)volk_malloc((N + 10)*sizeof(gr_complex),
+ volk_get_alignment());
+
+ for(unsigned i = 0; i < N+10; i++)
+ input[i] = test_fcn((double) i);
+
+ mmse_interp_differentiator_cc diffr;
+ float inv_nsteps = 1.0 / diffr.nsteps();
+
+ for(unsigned i = 0; i < N; i++) {
+ for(unsigned imu = 0; imu <= diffr.nsteps (); imu += 1) {
+ gr_complex expected = test_fcn_d((i + 3) + imu * inv_nsteps);
+ gr_complex actual = diffr.differentiate(&input[i], imu * inv_nsteps);
+
+ CPPUNIT_ASSERT_COMPLEXES_EQUAL(expected, actual, 0.0103);
+ // printf ("%9.6f %9.6f %9.6f \n", expected.real(), actual.real(), expected.real() - actual.real());
+ // printf ("%9.6f %9.6f %9.6f \n", expected.imag(), actual.imag(), expected.imag() - actual.imag());
+ }
+ }
+ volk_free(input);
+ }
+
+ } /* namespace filter */
+} /* namespace gr */
diff --git a/gr-filter/lib/qa_mmse_interp_differentiator_cc.h b/gr-filter/lib/qa_mmse_interp_differentiator_cc.h
new file mode 100644
index 0000000000..5074852933
--- /dev/null
+++ b/gr-filter/lib/qa_mmse_interp_differentiator_cc.h
@@ -0,0 +1,45 @@
+/* -*- c++ -*- */
+/*
+ * Copyright (C) 2002,2007,2012,2017 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _QA_MMSE_INTERP_DIFFERENTIATOR_CC_H_
+#define _QA_MMSE_INTERP_DIFFERENTIATOR_CC_H_
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/TestCase.h>
+
+namespace gr {
+ namespace filter {
+
+ class qa_mmse_interp_differentiator_cc : public CppUnit::TestCase
+ {
+ CPPUNIT_TEST_SUITE(qa_mmse_interp_differentiator_cc);
+ CPPUNIT_TEST(t1);
+ CPPUNIT_TEST_SUITE_END();
+
+ private:
+ void t1();
+ };
+
+ } /* namespace filter */
+} /* namespace gr */
+
+#endif /* _QA_MMSE_INTERP_DIFFERENTIATOR_CC_H_ */
diff --git a/gr-filter/lib/qa_mmse_interp_differentiator_ff.cc b/gr-filter/lib/qa_mmse_interp_differentiator_ff.cc
new file mode 100644
index 0000000000..6736d11baa
--- /dev/null
+++ b/gr-filter/lib/qa_mmse_interp_differentiator_ff.cc
@@ -0,0 +1,99 @@
+/* -*- c++ -*- */
+/*
+ * Copyright (C) 2002,2007,2012,2017 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 file; 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 <cppunit/TestAssert.h>
+#include <qa_mmse_interp_differentiator_ff.h>
+#include <gnuradio/filter/mmse_interp_differentiator_ff.h>
+#include <gnuradio/fft/fft.h>
+#include <volk/volk.h>
+#include <cstdio>
+#include <cmath>
+#include <stdexcept>
+#include <stdint.h>
+
+namespace gr {
+ namespace filter {
+
+ static const double k1 = 0.25 * 2 * M_PI;
+ static const double k2 = 0.125 * M_PI;
+ static const double k3 = 0.077 * 2 * M_PI;
+ static const double k4 = 0.3 * M_PI;
+
+ static double
+ phase_wrap(double arg)
+ {
+ while (arg >= 2.0*M_PI)
+ arg -= 2.0*M_PI;
+ while (arg <= 2.0*M_PI)
+ arg += 2.0*M_PI;
+ return arg;
+ }
+
+ static float
+ test_fcn(double index)
+ {
+ double arg1 = phase_wrap(index * k1 + k2);
+ double arg2 = phase_wrap(index * k3 + k4);
+
+ return (2 * sin (arg1) + 3 * sin (arg2));
+ }
+
+ static float
+ test_fcn_d(double index)
+ {
+ double arg1 = phase_wrap(index * k1 + k2);
+ double arg2 = phase_wrap(index * k3 + k4);
+
+ return (k1 * 2 * cos (arg1) + k3 * 3 * cos (arg2));
+ }
+
+ void
+ qa_mmse_interp_differentiator_ff::t1()
+ {
+ static const unsigned N = 100;
+ float *input = (float*)volk_malloc((N + 10)*sizeof(float),
+ volk_get_alignment());
+
+ for(unsigned i = 0; i < N+10; i++)
+ input[i] = test_fcn((double) i);
+
+ mmse_interp_differentiator_ff diffr;
+ float inv_nsteps = 1.0 / diffr.nsteps();
+
+ for(unsigned i = 0; i < N; i++) {
+ for(unsigned imu = 0; imu <= diffr.nsteps (); imu += 1) {
+ float expected = test_fcn_d((i + 3) + imu * inv_nsteps);
+ float actual = diffr.differentiate(&input[i], imu * inv_nsteps);
+
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, actual, 0.0103);
+ // printf ("%9.6f %9.6f %9.6f\n", expected, actual, expected - actual);
+ }
+ }
+ volk_free(input);
+ }
+
+ } /* namespace filter */
+} /* namespace gr */
diff --git a/gr-filter/lib/qa_mmse_interp_differentiator_ff.h b/gr-filter/lib/qa_mmse_interp_differentiator_ff.h
new file mode 100644
index 0000000000..5f11c3955c
--- /dev/null
+++ b/gr-filter/lib/qa_mmse_interp_differentiator_ff.h
@@ -0,0 +1,45 @@
+/* -*- c++ -*- */
+/*
+ * Copyright (C) 2002,2012,2017 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _QA_MMSE_INTERP_DIFFERENTIATOR_FF_H_
+#define _QA_MMSE_INTERP_DIFFERENTIATOR_FF_H_
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/TestCase.h>
+
+namespace gr {
+ namespace filter {
+
+ class qa_mmse_interp_differentiator_ff : public CppUnit::TestCase
+ {
+ CPPUNIT_TEST_SUITE(qa_mmse_interp_differentiator_ff);
+ CPPUNIT_TEST(t1);
+ CPPUNIT_TEST_SUITE_END();
+
+ private:
+ void t1();
+ };
+
+ } /* namespace filter */
+} /* namespace gr */
+
+#endif /* _QA_MMSE_INTERP_DIFFERENTIATOR_FF_H_ */