diff options
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 > 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 > 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 > 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 > 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_ */ |