diff --git a/CMakeLists.txt b/CMakeLists.txt index 14efacf..b6fe6d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,7 +96,7 @@ set(Boost_ADDITIONAL_VERSIONS "1.60.0" "1.60" "1.61.0" "1.61" "1.62.0" "1.62" "1.63.0" "1.63" "1.64.0" "1.64" "1.65.0" "1.65" "1.66.0" "1.66" "1.67.0" "1.67" "1.68.0" "1.68" "1.69.0" "1.69" ) -find_package(Boost "1.35" COMPONENTS filesystem system python numpy) +find_package(Boost "1.35" COMPONENTS filesystem system) if(NOT Boost_FOUND) message(FATAL_ERROR "Boost required to compile digitalhf") @@ -178,7 +178,6 @@ include_directories( ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/lib ${CMAKE_BINARY_DIR}/include - ${PYTHON_INCLUDE_DIR} ${Boost_INCLUDE_DIRS} ${CPPUNIT_INCLUDE_DIRS} ${GNURADIO_ALL_INCLUDE_DIRS} diff --git a/examples/test_188-110A.grc b/examples/test_188-110A.grc index be4d45b..4ecae36 100644 --- a/examples/test_188-110A.grc +++ b/examples/test_188-110A.grc @@ -109,7 +109,7 @@ _coordinate - (1141, 80) + (810, 16) gui_hint @@ -156,33 +156,6 @@ counter_slider - - variable - - comment - - - - _enabled - True - - - _coordinate - (320, 16) - - - _rotation - 0 - - - id - lpz - - - value - MIL_STD_188_110A.PhysicalLayer.get_preamble_z(sps).tolist() - - variable_qtgui_range @@ -199,7 +172,7 @@ _coordinate - (1024, 80) + (682, 16) gui_hint @@ -258,7 +231,7 @@ _coordinate - (533, 16) + (405, 16) _rotation @@ -285,7 +258,7 @@ _coordinate - (608, 16) + (480, 16) _rotation @@ -312,7 +285,7 @@ _coordinate - (682, 16) + (565, 16) _rotation @@ -327,49 +300,6 @@ 4 - - variable_rrc_filter_taps - - comment - - - - _enabled - 1 - - - alpha - 0.35 - - - _coordinate - (469, 352) - - - _rotation - 0 - - - gain - 1.0 - - - id - rrc_taps - - - ntaps - 11*sps - - - samp_rate - samp_rate - - - sym_rate - samp_rate/sps - - variable @@ -409,7 +339,7 @@ _coordinate - (458, 16) + (320, 16) _rotation @@ -424,6 +354,69 @@ 5 + + analog_agc2_xx + + attack_rate + 1e-1 + + + alias + + + + comment + + + + affinity + + + + decay_rate + 1e-2 + + + _enabled + 1 + + + _coordinate + (458, 154) + + + _rotation + 0 + + + gain + 5 + + + id + analog_agc2_xx_0 + + + max_gain + 8 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + reference + 1.0 + + + type + complex + + blocks_complex_to_mag @@ -444,11 +437,11 @@ _coordinate - (928, 314) + (426, 368) _rotation - 0 + 180 id @@ -483,7 +476,7 @@ _enabled - True + 1 _coordinate @@ -510,57 +503,6 @@ 1 - - blocks_multiply_const_vxx - - alias - - - - comment - - - - const - 5 - - - affinity - - - - _enabled - True - - - _coordinate - (426, 176) - - - _rotation - 0 - - - id - blocks_multiply_const_vxx_0 - - - type - complex - - - maxoutbuf - 0 - - - minoutbuf - 0 - - - vlen - 1 - - blocks_tag_debug @@ -585,7 +527,7 @@ _coordinate - (917, 133) + (1002, 282) _rotation @@ -636,7 +578,7 @@ _coordinate - (586, 176) + (672, 176) _rotation @@ -687,7 +629,7 @@ _enabled - True + 1 file @@ -719,11 +661,11 @@ repeat - False + True - digitalhf_adaptive_dfe + digitalhf_physical_layer_driver alias @@ -736,21 +678,25 @@ affinity + + description_name + MIL_STD_188_110A + _enabled - True + 1 _coordinate - (874, 389) + (661, 325) _rotation - 0 + 180 id - digitalhf_adaptive_dfe_0 + digitalhf_physical_layer_driver_0 maxoutbuf @@ -766,11 +712,11 @@ alpha - 0.0005 + 0.005 mode - '' + "" mu @@ -789,215 +735,8 @@ nW - py_obj_name - MIL_STD_188_110A - - - - fir_filter_xxx - - alias - - - - comment - - - - affinity - - - - decim - 1 - - - _enabled - 1 - - - _coordinate - (330, 293) - - - _rotation - 0 - - - id - fir_filter_xxx_0 - - - maxoutbuf - 0 - - - minoutbuf - 0 - - - samp_delay - 0 - - - taps - rrc_taps - - - type - ccc - - - - import - - alias - - - - comment - - - - _enabled - True - - - _coordinate - (1034, 21) - - - _rotation - 0 - - - id - import_0 - - - import - import digitalhf.physical_layer.MIL_STD_188_110A as MIL_STD_188_110A - - - - import - - alias - - - - comment - - - - _enabled - True - - - _coordinate - (960, 21) - - - _rotation - 0 - - - id - import_0_0 - - - import - from gnuradio import gr - - - - import - - alias - - - - comment - - - - _enabled - True - - - _coordinate - (810, 32) - - - _rotation - 0 - - - id - import_1 - - - import - import numpy as np - - - - digital_corr_est_cc - - alias - - - - comment - - - - affinity - - - - _enabled - 1 - - - _coordinate - (640, 272) - - - _rotation - 0 - - - id - preamble - - - maxoutbuf - 0 - - - minoutbuf - 0 - - - sps - sps - - - symbols - lpz - - - mark_delay - nF*sps-2 - - - threshold_method - digital.corr_est_cc.THRESHOLD_DYNAMIC - - - threshold - 0.35 + samp_rate + samp_rate @@ -1028,7 +767,7 @@ _coordinate - (1109, 410) + (394, 464) gui_hint @@ -1036,7 +775,7 @@ _rotation - 0 + 180 grid @@ -1383,7 +1122,7 @@ _coordinate - (1120, 304) + (181, 357) gui_hint @@ -1391,7 +1130,7 @@ _rotation - 0 + 180 grid @@ -1746,11 +1485,11 @@ _enabled - True + 1 _coordinate - (1002, 602) + (426, 293) gui_hint @@ -1758,7 +1497,7 @@ _rotation - 0 + 180 grid @@ -2117,7 +1856,7 @@ _coordinate - (1120, 218) + (938, 165) gui_hint @@ -2304,6 +2043,12 @@ firdes.WIN_BLACKMAN_hARRIS + + analog_agc2_xx_0 + blocks_throttle_0 + 0 + 0 + blocks_complex_to_mag_0 qtgui_time_sink_x_0 @@ -2312,19 +2057,19 @@ blocks_float_to_complex_0 - blocks_multiply_const_vxx_0 - 0 - 0 - - - blocks_multiply_const_vxx_0 - blocks_throttle_0 + analog_agc2_xx_0 0 0 blocks_throttle_0 - fir_filter_xxx_0 + digitalhf_physical_layer_driver_0 + 0 + 0 + + + blocks_throttle_0 + qtgui_waterfall_sink_x_0 0 0 @@ -2341,45 +2086,21 @@ 1 - digitalhf_adaptive_dfe_0 - qtgui_const_sink_x_0 - 0 - 0 - - - digitalhf_adaptive_dfe_0 - qtgui_time_sink_x_1 - soft_dec - in - - - fir_filter_xxx_0 - preamble - 0 - 0 - - - preamble + digitalhf_physical_layer_driver_0 blocks_complex_to_mag_0 1 0 - preamble - blocks_tag_debug_0 + digitalhf_physical_layer_driver_0 + qtgui_const_sink_x_0 0 0 - preamble - digitalhf_adaptive_dfe_0 - 0 - 0 - - - preamble - qtgui_waterfall_sink_x_0 - 0 - 0 + digitalhf_physical_layer_driver_0 + qtgui_time_sink_x_1 + soft_dec + in diff --git a/examples/test_188-110C.grc b/examples/test_188-110C.grc index 4bf8892..a687b37 100644 --- a/examples/test_188-110C.grc +++ b/examples/test_188-110C.grc @@ -109,7 +109,7 @@ _coordinate - (1141, 80) + (1141, 240) gui_hint @@ -156,33 +156,6 @@ counter_slider - - variable - - comment - - - - _enabled - True - - - _coordinate - (320, 16) - - - _rotation - 0 - - - id - lpz - - - value - MIL_STD_188_110C.PhysicalLayer.get_preamble_z(sps).tolist() - - variable_qtgui_range @@ -199,7 +172,7 @@ _coordinate - (1024, 80) + (629, 16) gui_hint @@ -258,7 +231,7 @@ _coordinate - (533, 16) + (416, 16) _rotation @@ -285,7 +258,7 @@ _coordinate - (608, 16) + (480, 16) _rotation @@ -312,7 +285,7 @@ _coordinate - (682, 16) + (554, 16) _rotation @@ -327,49 +300,6 @@ 4 - - variable_rrc_filter_taps - - comment - - - - _enabled - 1 - - - alpha - 0.35 - - - _coordinate - (469, 352) - - - _rotation - 0 - - - gain - 1.0 - - - id - rrc_taps - - - ntaps - 11*sps - - - samp_rate - samp_rate - - - sym_rate - samp_rate/sps - - variable @@ -409,7 +339,7 @@ _coordinate - (458, 16) + (330, 16) _rotation @@ -424,6 +354,69 @@ 5 + + analog_agc2_xx + + attack_rate + .1 + + + alias + + + + comment + + + + affinity + + + + decay_rate + .01 + + + _enabled + 1 + + + _coordinate + (448, 133) + + + _rotation + 0 + + + gain + 5 + + + id + analog_agc2_xx_0 + + + max_gain + 8 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + reference + 1.0 + + + type + complex + + blocks_complex_to_mag @@ -444,11 +437,11 @@ _coordinate - (928, 314) + (576, 453) _rotation - 0 + 180 id @@ -487,7 +480,7 @@ _enabled - True + 1 file @@ -495,11 +488,11 @@ _coordinate - (693, 677) + (277, 293) _rotation - 0 + 180 id @@ -534,11 +527,11 @@ _enabled - True + 1 _coordinate - (256, 154) + (256, 133) _rotation @@ -561,57 +554,6 @@ 1 - - blocks_multiply_const_vxx - - alias - - - - comment - - - - const - 5 - - - affinity - - - - _enabled - True - - - _coordinate - (426, 176) - - - _rotation - 0 - - - id - blocks_multiply_const_vxx_0 - - - type - complex - - - maxoutbuf - 0 - - - minoutbuf - 0 - - - vlen - 1 - - blocks_pdu_to_tagged_stream @@ -628,15 +570,15 @@ _enabled - True + 1 _coordinate - (458, 581) + (544, 368) _rotation - 0 + 180 id @@ -683,7 +625,7 @@ _coordinate - (917, 133) + (1162, 421) _rotation @@ -734,7 +676,7 @@ _coordinate - (586, 176) + (661, 154) _rotation @@ -785,7 +727,7 @@ _enabled - True + 1 file @@ -793,7 +735,7 @@ _coordinate - (10, 154) + (10, 133) _rotation @@ -817,11 +759,11 @@ repeat - True + False - digitalhf_adaptive_dfe + digitalhf_physical_layer_driver alias @@ -834,21 +776,25 @@ affinity + + description_name + MIL_STD_188_110C + _enabled True _coordinate - (874, 389) + (821, 400) _rotation - 0 + 180 id - digitalhf_adaptive_dfe_0 + digitalhf_physical_layer_driver_0 maxoutbuf @@ -864,11 +810,11 @@ alpha - 0.0001 + 0.005 mode - '' + "" mu @@ -887,215 +833,8 @@ nW - py_obj_name - MIL_STD_188_110C - - - - fir_filter_xxx - - alias - - - - comment - - - - affinity - - - - decim - 1 - - - _enabled - 1 - - - _coordinate - (330, 293) - - - _rotation - 0 - - - id - fir_filter_xxx_0 - - - maxoutbuf - 0 - - - minoutbuf - 0 - - - samp_delay - 0 - - - taps - rrc_taps - - - type - ccc - - - - import - - alias - - - - comment - - - - _enabled - True - - - _coordinate - (1034, 21) - - - _rotation - 0 - - - id - import_0 - - - import - import digitalhf.physical_layer.MIL_STD_188_110C as MIL_STD_188_110C - - - - import - - alias - - - - comment - - - - _enabled - True - - - _coordinate - (960, 21) - - - _rotation - 0 - - - id - import_0_0 - - - import - from gnuradio import gr - - - - import - - alias - - - - comment - - - - _enabled - True - - - _coordinate - (810, 32) - - - _rotation - 0 - - - id - import_1 - - - import - import numpy as np - - - - digital_corr_est_cc - - alias - - - - comment - - - - affinity - - - - _enabled - 1 - - - _coordinate - (640, 272) - - - _rotation - 0 - - - id - preamble - - - maxoutbuf - 0 - - - minoutbuf - 0 - - - sps - sps - - - symbols - lpz - - - mark_delay - nF*sps-2 - - - threshold_method - digital.corr_est_cc.THRESHOLD_DYNAMIC - - - threshold - 0.5 + samp_rate + samp_rate @@ -1126,7 +865,7 @@ _coordinate - (1109, 410) + (554, 538) gui_hint @@ -1134,7 +873,7 @@ _rotation - 0 + 180 grid @@ -1481,7 +1220,7 @@ _coordinate - (1120, 304) + (288, 474) gui_hint @@ -1489,7 +1228,7 @@ _rotation - 0 + 180 grid @@ -1844,11 +1583,11 @@ _enabled - True + 1 _coordinate - (1002, 602) + (298, 378) gui_hint @@ -1856,7 +1595,7 @@ _rotation - 0 + 180 grid @@ -2215,7 +1954,7 @@ _coordinate - (1120, 218) + (1066, 101) gui_hint @@ -2402,6 +2141,12 @@ firdes.WIN_BLACKMAN_hARRIS + + analog_agc2_xx_0 + blocks_throttle_0 + 0 + 0 + blocks_complex_to_mag_0 qtgui_time_sink_x_0 @@ -2410,13 +2155,7 @@ blocks_float_to_complex_0 - blocks_multiply_const_vxx_0 - 0 - 0 - - - blocks_multiply_const_vxx_0 - blocks_throttle_0 + analog_agc2_xx_0 0 0 @@ -2434,7 +2173,13 @@ blocks_throttle_0 - fir_filter_xxx_0 + digitalhf_physical_layer_driver_0 + 0 + 0 + + + blocks_throttle_0 + qtgui_waterfall_sink_x_0 0 0 @@ -2451,45 +2196,21 @@ 1 - digitalhf_adaptive_dfe_0 - qtgui_const_sink_x_0 - 0 - 0 - - - digitalhf_adaptive_dfe_0 - blocks_pdu_to_tagged_stream_0 - soft_dec - pdus - - - fir_filter_xxx_0 - preamble - 0 - 0 - - - preamble + digitalhf_physical_layer_driver_0 blocks_complex_to_mag_0 1 0 - preamble - blocks_tag_debug_0 + digitalhf_physical_layer_driver_0 + qtgui_const_sink_x_0 0 0 - preamble - digitalhf_adaptive_dfe_0 - 0 - 0 - - - preamble - qtgui_waterfall_sink_x_0 - 0 - 0 + digitalhf_physical_layer_driver_0 + blocks_pdu_to_tagged_stream_0 + soft_dec + pdus diff --git a/examples/test_s4285.grc b/examples/test_s4285.grc index 1548bf6..8899d89 100644 --- a/examples/test_s4285.grc +++ b/examples/test_s4285.grc @@ -93,33 +93,6 @@ (0,0) - - variable - - comment - - - - _enabled - True - - - _coordinate - (757, 16) - - - _rotation - 0 - - - id - dummy - - - value - [lpz[0].extend(x) for x in lpz[1:]] - - variable_qtgui_range @@ -136,7 +109,7 @@ _coordinate - (1141, 80) + (1141, 16) gui_hint @@ -183,33 +156,6 @@ counter_slider - - variable - - comment - - - - _enabled - True - - - _coordinate - (320, 16) - - - _rotation - 0 - - - id - lpz - - - value - [[x,x,x,x,x] for x in STANAG_4285.PhysicalLayer.get_preamble()['symb'][9:40].tolist()] - - variable_qtgui_chooser @@ -226,7 +172,7 @@ _coordinate - (1152, 490) + (1152, 277) gui_hint @@ -325,7 +271,7 @@ _coordinate - (1024, 80) + (1152, 144) gui_hint @@ -384,7 +330,7 @@ _coordinate - (533, 16) + (394, 16) _rotation @@ -411,7 +357,7 @@ _coordinate - (608, 16) + (469, 16) _rotation @@ -438,7 +384,7 @@ _coordinate - (682, 16) + (544, 16) _rotation @@ -453,49 +399,6 @@ 4 - - variable_rrc_filter_taps - - comment - - - - _enabled - True - - - alpha - 0.25 - - - _coordinate - (469, 352) - - - _rotation - 0 - - - gain - 1.0 - - - id - rrc_taps - - - ntaps - 11*sps - - - samp_rate - samp_rate - - - sym_rate - samp_rate/sps - - variable @@ -535,7 +438,7 @@ _coordinate - (458, 16) + (320, 16) _rotation @@ -550,6 +453,69 @@ 5 + + analog_agc2_xx + + attack_rate + 1e-1 + + + alias + + + + comment + + + + affinity + + + + decay_rate + 1e-2 + + + _enabled + 1 + + + _coordinate + (480, 154) + + + _rotation + 0 + + + gain + 5 + + + id + analog_agc2_xx_0 + + + max_gain + 8 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + reference + 1.0 + + + type + complex + + blocks_complex_to_mag @@ -570,11 +536,11 @@ _coordinate - (928, 314) + (533, 368) _rotation - 0 + 180 id @@ -613,7 +579,7 @@ _coordinate - (256, 154) + (277, 154) _rotation @@ -636,57 +602,6 @@ 1 - - blocks_multiply_const_vxx - - alias - - - - comment - - - - const - 5 - - - affinity - - - - _enabled - True - - - _coordinate - (426, 176) - - - _rotation - 0 - - - id - blocks_multiply_const_vxx_0 - - - type - complex - - - maxoutbuf - 0 - - - minoutbuf - 0 - - - vlen - 1 - - blocks_tag_debug @@ -711,7 +626,7 @@ _coordinate - (917, 133) + (1184, 464) _rotation @@ -762,7 +677,7 @@ _coordinate - (586, 176) + (682, 176) _rotation @@ -817,7 +732,7 @@ file - /Users/chm/Downloads/kiwi-sm2gct.mooo.com_2018-10-30T10_35_39Z_8700.00_iq.wav + /Users/chm/Software/signal-analysis/gnuradio/20181027T095433Z_6407800_SM2GCT_iq.wav _coordinate @@ -845,11 +760,11 @@ repeat - False + True - digitalhf_adaptive_dfe + digitalhf_physical_layer_driver alias @@ -862,21 +777,25 @@ affinity + + description_name + STANAG_4285 + _enabled True _coordinate - (874, 389) + (736, 314) _rotation - 0 + 180 id - digitalhf_adaptive_dfe_0 + digitalhf_physical_layer_driver_0 maxoutbuf @@ -915,235 +834,8 @@ nW - py_obj_name - STANAG_4285 - - - - fir_filter_xxx - - alias - - - - comment - - - - affinity - - - - decim - 1 - - - _enabled - True - - - _coordinate - (330, 293) - - - _rotation - 0 - - - id - fir_filter_xxx_0 - - - maxoutbuf - 0 - - - minoutbuf - 0 - - - samp_delay - 0 - - - taps - rrc_taps - - - type - ccc - - - - fractional_resampler_xx - - alias - - - - comment - - - - affinity - - - - _enabled - 1 - - - _coordinate - (96, 293) - - - _rotation - 0 - - - id - fractional_resampler_xx_0 - - - maxoutbuf - 0 - - - minoutbuf - 0 - - - phase_shift - 0 - - - resamp_ratio - 12001./11999.*0+1 - - - type - complex - - - - import - - alias - - - - comment - - - - _enabled - True - - - _coordinate - (1034, 21) - - - _rotation - 0 - - - id - import_0 - - - import - import digitalhf.physical_layer.STANAG_4285 as STANAG_4285 - - - - import - - alias - - - - comment - - - - _enabled - True - - - _coordinate - (960, 21) - - - _rotation - 0 - - - id - import_0_0 - - - import - from gnuradio import gr - - - - digital_corr_est_cc - - alias - - - - comment - - - - affinity - - - - _enabled - True - - - _coordinate - (640, 272) - - - _rotation - 0 - - - id - preamble - - - maxoutbuf - 0 - - - minoutbuf - 0 - - - sps - sps - - - symbols - lpz[0] - - - mark_delay - (nF-9)*sps+2 - - - threshold_method - digital.corr_est_cc.THRESHOLD_DYNAMIC - - - threshold - 0.5 + samp_rate + samp_rate @@ -1174,7 +866,7 @@ _coordinate - (1109, 410) + (320, 442) gui_hint @@ -1182,7 +874,7 @@ _rotation - 0 + 180 grid @@ -1529,7 +1221,7 @@ _coordinate - (1120, 304) + (320, 357) gui_hint @@ -1537,7 +1229,7 @@ _rotation - 0 + 180 grid @@ -1801,7 +1493,7 @@ size - 1024/2 + 80*sps srate @@ -1817,7 +1509,7 @@ tr_delay - 0.01 + 0.007 tr_level @@ -1896,7 +1588,7 @@ _coordinate - (1002, 602) + (320, 293) gui_hint @@ -1904,7 +1596,7 @@ _rotation - 0 + 180 grid @@ -2263,7 +1955,7 @@ _coordinate - (1120, 218) + (896, 101) gui_hint @@ -2450,6 +2142,12 @@ firdes.WIN_BLACKMAN_hARRIS + + analog_agc2_xx_0 + blocks_throttle_0 + 0 + 0 + blocks_complex_to_mag_0 qtgui_time_sink_x_0 @@ -2458,19 +2156,19 @@ blocks_float_to_complex_0 - blocks_multiply_const_vxx_0 - 0 - 0 - - - blocks_multiply_const_vxx_0 - fractional_resampler_xx_0 + analog_agc2_xx_0 0 0 blocks_throttle_0 - fir_filter_xxx_0 + digitalhf_physical_layer_driver_0 + 0 + 0 + + + blocks_throttle_0 + qtgui_waterfall_sink_x_0 0 0 @@ -2487,51 +2185,21 @@ 1 - digitalhf_adaptive_dfe_0 - qtgui_const_sink_x_0 - 0 - 0 - - - digitalhf_adaptive_dfe_0 - qtgui_time_sink_x_1 - soft_dec - in - - - fir_filter_xxx_0 - preamble - 0 - 0 - - - fractional_resampler_xx_0 - blocks_throttle_0 - 0 - 0 - - - preamble + digitalhf_physical_layer_driver_0 blocks_complex_to_mag_0 1 0 - preamble - blocks_tag_debug_0 + digitalhf_physical_layer_driver_0 + qtgui_const_sink_x_0 0 0 - preamble - digitalhf_adaptive_dfe_0 - 0 - 0 - - - preamble - qtgui_waterfall_sink_x_0 - 0 - 0 + digitalhf_physical_layer_driver_0 + qtgui_time_sink_x_1 + soft_dec + in diff --git a/grc/CMakeLists.txt b/grc/CMakeLists.txt index 2c23310..e01edda 100644 --- a/grc/CMakeLists.txt +++ b/grc/CMakeLists.txt @@ -18,5 +18,8 @@ # the Free Software Foundation, Inc., 51 Franklin Street, # Boston, MA 02110-1301, USA. install(FILES - digitalhf_adaptive_dfe.xml DESTINATION share/gnuradio/grc/blocks + digitalhf_adaptive_dfe.xml + digitalhf_physical_layer_driver.xml + digitalhf_doppler_correction_cc.xml + digitalhf_msg_proxy.xml DESTINATION share/gnuradio/grc/blocks ) diff --git a/grc/digitalhf_adaptive_dfe.xml b/grc/digitalhf_adaptive_dfe.xml index 1b3a9f3..9472ff5 100644 --- a/grc/digitalhf_adaptive_dfe.xml +++ b/grc/digitalhf_adaptive_dfe.xml @@ -4,9 +4,8 @@ digitalhf_adaptive_dfe [digitalhf] import digitalhf - digitalhf.adaptive_dfe($sps,$nB,$nF,$nW,$mu,$alpha,$py_obj_name) + digitalhf.adaptive_dfe($sps,$nB,$nF,$nW,$mu,$alpha) set_mu($mu) - set_mode($mode) + + ... + ... + ... + + + + + in + + + + + + out + + + diff --git a/grc/digitalhf_msg_proxy.xml b/grc/digitalhf_msg_proxy.xml new file mode 100644 index 0000000..82f0279 --- /dev/null +++ b/grc/digitalhf_msg_proxy.xml @@ -0,0 +1,38 @@ + + + msg_proxy + digitalhf_msg_proxy + [digitalhf] + import digitalhf + digitalhf.msg_proxy($physical_layer_object) + + + ... + ... + ... + + + + + in + + + + + + out + + + diff --git a/grc/digitalhf_physical_layer_driver.xml b/grc/digitalhf_physical_layer_driver.xml new file mode 100644 index 0000000..e4bcce5 --- /dev/null +++ b/grc/digitalhf_physical_layer_driver.xml @@ -0,0 +1,76 @@ + + + physical_layer_driver + digitalhf_physical_layer_driver + [digitalhf] + import digitalhf + digitalhf.physical_layer_driver($samp_rate, $sps, $alpha, $mu, $nB, $nF, $nW, $description_name, $mode) + set_mu($mu) + set_alpha($alpha) + set_mode($mode) + + Description + description_name + string + + + sample rate + samp_rate + int + + + SPS + sps + int + + + nB + nB + int + + + nF + nF + int + + + nW + nW + int + + + mu + mu + float + + + alpha + alpha + float + + + mode + mode + string + + + + in + complex + + + + out_symb + complex + + + out_cc + complex + + + + soft_dec + message + 1 + + diff --git a/include/digitalhf/CMakeLists.txt b/include/digitalhf/CMakeLists.txt index cac5763..90dc3cb 100644 --- a/include/digitalhf/CMakeLists.txt +++ b/include/digitalhf/CMakeLists.txt @@ -23,5 +23,6 @@ ######################################################################## install(FILES api.h - adaptive_dfe.h DESTINATION include/digitalhf + adaptive_dfe.h + doppler_correction_cc.h DESTINATION include/digitalhf ) diff --git a/include/digitalhf/adaptive_dfe.h b/include/digitalhf/adaptive_dfe.h index 84a24a3..23b553d 100644 --- a/include/digitalhf/adaptive_dfe.h +++ b/include/digitalhf/adaptive_dfe.h @@ -50,13 +50,12 @@ class DIGITALHF_API adaptive_dfe : virtual public gr::block static sptr make(int sps, // samples per symbol int nB, // number of forward FIR taps int nF, // number of backward FIR taps - int nW, // number of feedback taps - float mu, // - float alpha, // - std::string physical_layer_description); + int nW, // number of feedback symbol taps + float mu, // mu - decision-feedback equalizer + float alpha); // alpha - decision-feedback equalizer virtual void set_mu(float) = 0; - virtual void set_mode(std::string) = 0; + virtual void set_alpha(float) = 0; } ; } // namespace digitalhf diff --git a/include/digitalhf/doppler_correction_cc.h b/include/digitalhf/doppler_correction_cc.h new file mode 100644 index 0000000..281618d --- /dev/null +++ b/include/digitalhf/doppler_correction_cc.h @@ -0,0 +1,58 @@ +/* -*- c++ -*- */ +/* + * Copyright 2018 hcab14@gmail.com. + * + * This 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. + * + * This software 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 software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef INCLUDED_DIGITALHF_DOPPLER_CORRECTION_CC_H +#define INCLUDED_DIGITALHF_DOPPLER_CORRECTION_CC_H + +#include +#include + +namespace gr { +namespace digitalhf { + +/*! + * \brief <+description of block+> + * \ingroup digitalhf + * + */ +class DIGITALHF_API doppler_correction_cc : virtual public gr::sync_block +{ + public: + typedef boost::shared_ptr sptr; + + /*! + * \brief Return a shared_ptr to a new instance of digitalhf::doppler_correction_cc. + * + * To avoid accidental use of raw pointers, digitalhf::doppler_correction_cc's + * constructor is in a private implementation + * class. digitalhf::doppler_correction_cc::make is the public interface for + * creating new instances. + */ + static sptr make(unsigned int preamble_length, + unsigned int preamble_length_cc); + +}; + +} // namespace digitalhf +} // namespace gr + +#endif /* INCLUDED_DIGITALHF_DOPPLER_CORRECTION_CC_H */ + diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index e91d030..938caa3 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -26,7 +26,8 @@ include(GrPlatform) #define LIB_SUFFIX include_directories(${Boost_INCLUDE_DIR}) link_directories(${Boost_LIBRARY_DIRS}) list(APPEND digitalhf_sources - adaptive_dfe_impl.cc ) + adaptive_dfe_impl.cc + doppler_correction_cc_impl.cc ) set(digitalhf_sources "${digitalhf_sources}" PARENT_SCOPE) if(NOT digitalhf_sources) @@ -59,6 +60,7 @@ include_directories(${CPPUNIT_INCLUDE_DIRS}) list(APPEND test_digitalhf_sources ${CMAKE_CURRENT_SOURCE_DIR}/test_digitalhf.cc ${CMAKE_CURRENT_SOURCE_DIR}/qa_digitalhf.cc + ${CMAKE_CURRENT_SOURCE_DIR}/qa_doppler_correction_cc.cc ${CMAKE_CURRENT_SOURCE_DIR}/qa_adaptive_dfe.cc ) @@ -66,13 +68,11 @@ add_executable(test-digitalhf ${test_digitalhf_sources}) target_link_libraries( test-digitalhf - ${PYTHON_LIBRARIES} ${GNURADIO_RUNTIME_LIBRARIES} ${Boost_LIBRARIES} ${CPPUNIT_LIBRARIES} gnuradio-digitalhf ) -MESSAGE("XXX ${PYTHON_LIBRARIES}") GR_ADD_TEST(test_digitalhf test-digitalhf) ######################################################################## diff --git a/lib/adaptive_dfe_impl.cc b/lib/adaptive_dfe_impl.cc index a1f761d..98ce497 100644 --- a/lib/adaptive_dfe_impl.cc +++ b/lib/adaptive_dfe_impl.cc @@ -39,51 +39,24 @@ namespace gr { namespace digitalhf { -namespace { -class GILLock { - PyGILState_STATE _state; -public: - GILLock() - :_state(PyGILState_Ensure()) {} - ~GILLock() { - PyGILState_Release(_state); - } -} ; - -boost::python::numpy::ndarray -complex_vector_to_ndarray(std::vector const& v) { - return boost::python::numpy::from_data - (&v.front(), - boost::python::numpy::dtype::get_builtin(), - boost::python::make_tuple(v.size()), - boost::python::make_tuple(sizeof(gr_complex)), - boost::python::object()); -} -} // anonymous namespace - adaptive_dfe::sptr adaptive_dfe::make(int sps, // samples per symbol int nB, // number of forward FIR taps int nF, // number of backward FIR taps int nW, // number of feedback taps float mu, - float alpha, - std::string python_module_name) + float alpha) { return gnuradio::get_initial_sptr - (new adaptive_dfe_impl(sps, nB, nF, nW, mu, alpha, python_module_name)); + (new adaptive_dfe_impl(sps, nB, nF, nW, mu, alpha)); } -/* - * The private constructor - */ adaptive_dfe_impl::adaptive_dfe_impl(int sps, // samples per symbol int nB, // number of forward FIR taps int nF, // number of backward FIR taps int nW, // number of feedback taps float mu, - float alpha, - std::string python_module_name) + float alpha) : gr::block("adaptive_dfe", gr::io_signature::make(1, 1, sizeof(gr_complex)), gr::io_signature::make(1, 1, sizeof(gr_complex))) @@ -91,63 +64,61 @@ adaptive_dfe_impl::adaptive_dfe_impl(int sps, // samples per symbol , _nB(nB*sps) , _nF(nF*sps) , _nW(nW) + , _nGuard(2*sps) , _mu(mu) , _alpha(alpha) , _use_symbol_taps(true) - , _py_module_name(python_module_name) - , _physicalLayer() + // , _py_module_name(python_module_name) + // , _physicalLayer() , _taps_samples(nullptr) , _taps_symbols(nullptr) - , _hist_samples(nullptr) , _hist_symbols(nullptr) - , _hist_sample_index(0) , _hist_symbol_index(0) - , _ignore_filter_updates(0) - , _saved_samples() - , _sample_counter(0) , _constellations() , _npwr() - , _npwr_counter() , _npwr_max_time_constant(10) , _constellation_index() - , _samples() , _symbols() , _scramble() , _descrambled_symbols() , _symbol_counter(0) - , _need_samples(false) , _save_soft_decisions(false) , _vec_soft_decisions() - , _msg_port_name(pmt::mp("soft_dec")) + , _msg_ports{{"soft_dec", pmt::mp("soft_dec")}, + {"frame_info", pmt::mp("frame_info")}} , _msg_metadata(pmt::make_dict()) - , _df(0) - , _phase(0) - , _b{0.338187046465954, -0.288839024460507} - , _ud(0) , _state(WAIT_FOR_PREAMBLE) { GR_LOG_DECLARE_LOGPTR(d_logger); GR_LOG_ASSIGN_LOGPTR(d_logger, "adaptive_dfe"); - message_port_register_out(_msg_port_name); + + set_history(_nGuard+_nB+1); + + message_port_register_out(_msg_ports["soft_dec"]); + + pmt::pmt_t constellations_port = pmt::mp("constellations"); + message_port_register_in(constellations_port); + set_msg_handler(constellations_port, boost::bind(&adaptive_dfe_impl::update_constellations, this, _1)); + + pmt::pmt_t frame_info_port = _msg_ports["frame_info"]; + message_port_register_in(frame_info_port); + message_port_register_out(frame_info_port); + set_msg_handler(frame_info_port, boost::bind(&adaptive_dfe_impl::update_frame_info, this, _1)); } -/* - * Our virtual destructor. - */ adaptive_dfe_impl::~adaptive_dfe_impl() { - _msg_port_name = pmt::PMT_NIL; - _msg_metadata = pmt::PMT_NIL; + _msg_metadata = pmt::PMT_NIL; VOLK_SAFE_DELETE(_taps_samples); VOLK_SAFE_DELETE(_taps_symbols); - VOLK_SAFE_DELETE(_hist_samples); VOLK_SAFE_DELETE(_hist_symbols); } void -adaptive_dfe_impl::forecast (int noutput_items, gr_vector_int &ninput_items_required) +adaptive_dfe_impl::forecast(int noutput_items, gr_vector_int &ninput_items_required) { - ninput_items_required[0] = _sps*noutput_items; + // [guard | nB | 1 | nF | guard ] + ninput_items_required[0] = _sps*noutput_items + 2*_nGuard + _nB + _nF + 1; } int @@ -160,234 +131,102 @@ adaptive_dfe_impl::general_work(int noutput_items, gr_complex const* in = (gr_complex const *)input_items[0]; gr_complex *out = (gr_complex *)output_items[0]; + const int nin = ninput_items[0]; + + assert(ninput_items[0] >= 2*_nGuard + _nB + _nF + 1); + if (ninput_items[0] < 2*_nGuard + _nB + _nF + 1) + return 0; + int const ninput = ninput_items[0] - _nGuard - _nF; + int nout = 0; // counter for produced output items - int i = 0; // counter for consumed input items - for (; i INITIAL_DOPPLER_ESTIMATE"); - _state = INITIAL_DOPPLER_ESTIMATE; - _sample_counter = 0; + switch (_state) { + case WAIT_FOR_PREAMBLE: { + std::vector v; + get_tags_in_window(v, 0, history()-1, ninput, pmt::mp("preamble_start")); + if (v.empty()) { + consume(0, ninput - history()+1); + } else { + tag_t const& tag = v.front(); + // uint64_t const offset = tag.offset - nitems_read(0) + history() - 1; + // std::cout << "========= offset= " << offset + // << " tag.offset= " << tag.offset + // << " nitems_read(0)= " << nitems_read(0) + // << " tag.offset-nitems_read(0)= " << tag.offset - nitems_read(0) << " ==========" << std::endl; + // for (int k=0; k wait for frame info"); + } + break; + } // WAIT_FOR_PREAMBLE + case WAIT_FOR_FRAME_INFO: { + //update_frame_info(delete_head_blocking(_msg_ports["frame_info"])); + break; + } // WAIT_FOR_MESSAGE + case DO_FILTER: { + // std::cout << "========= offset (DO_FILTER) nitems_read(0)= " << nitems_read(0) << " ==========" << std::endl; + int ninput_processed = 0; + for (int i=history()-1; i const empty_vec; - // initial doppler estimate - if (!update_doppler_information(_physicalLayer.attr("get_doppler") - (complex_vector_to_ndarray(empty_vec), - complex_vector_to_ndarray(_samples)))) { - GR_LOG_DEBUG(d_logger, "next state > WAIT_FOR_PREAMBLE"); - _state = WAIT_FOR_PREAMBLE; - break; - } - } catch (boost::python::error_already_set const&) { - PyErr_Print(); - } - // (1) correct all samples in the circular buffer with the inital doppler estimate - for (int j=_nB+1; j<_nB+_nF+1; ++j) { - assert(_hist_sample_index+j < 2*(_nB+_nF+1)); - _hist_samples[_hist_sample_index+j] *= gr_expj(-_phase); - update_local_oscillator(); - } - // (2) insert all buffered samples and run the adaptive filter for them - // instead of pop_front() we first reverse _samples and then insert back() + pop_back() - // O(N) instead of O(N^2) - std::reverse(_samples.begin(), _samples.end()); - while (!_samples.empty() && nout < noutput_items) { - insert_sample(_samples.back()); - _sample_counter += 1; - _samples.pop_back(); - if ((_sample_counter%_sps) == 0) - out[nout++] = filter(); - } - if (_samples.empty()) { - GR_LOG_DEBUG(d_logger,"next state > DO_FILTER"); - _state = DO_FILTER; - break; - } else { - GR_LOG_DEBUG(d_logger, "next state > INITIAL_DOPPLER_ESTIMATE_CONTINUE"); - _state = INITIAL_DOPPLER_ESTIMATE_CONTINUE; - break; - } - } - } // INITIAL_DOPPLER_ESTIMATE_CONTINUE + int const shift = recenter_filter_taps(); + if (shift != 0) + ninput_processed += shift; - case INITIAL_DOPPLER_ESTIMATE_CONTINUE: { - GR_LOG_DEBUG(d_logger, "INITIAL_DOPPLER_ESTIMATE_CONTINUE"); - while (!_samples.empty() && nout < noutput_items) { - insert_sample(_samples.back()); - _sample_counter += 1; - _samples.pop_back(); - if ((_sample_counter%_sps) == 0) - out[nout++] = filter(); + _state = WAIT_FOR_FRAME_INFO; + break; } - if (_samples.empty()) { - GR_LOG_DEBUG(d_logger, "next state > DO_FILTER"); - _state = DO_FILTER; - } else { - GR_LOG_DEBUG(d_logger, "next state > INITIAL_DOPPLER_ESTIMATE_CONTINUE"); - _state = INITIAL_DOPPLER_ESTIMATE_CONTINUE; - } - break; - } // INITIAL_DOPPLER_ESTIMATE_CONTINUE - - case DO_FILTER: { - if ((_sample_counter%_sps) == 0) { - if (_symbol_counter == _symbols.size()) { // frame is ready - _symbol_counter = 0; - GILLock gil_lock; - try { - // update doppler estimate - if (!update_doppler_information(_physicalLayer.attr("get_doppler") - (complex_vector_to_ndarray(_descrambled_symbols), - complex_vector_to_ndarray(_samples)))) { - GR_LOG_DEBUG(d_logger, "next state > WAIT_FOR_PREAMBLE"); - _state = WAIT_FOR_PREAMBLE; - break; - } - // publish soft decisions - if (!_vec_soft_decisions.empty()) { - std::cout << "soft_dec " << _vec_soft_decisions.size() << "\n"; - unsigned int const bits_per_symbol = _constellations[_constellation_index]->bits_per_symbol(); - _msg_metadata = pmt::dict_add(_msg_metadata, pmt::mp("bits_per_symbol"), pmt::from_long(bits_per_symbol)); - _msg_metadata = pmt::dict_add(_msg_metadata, pmt::mp("packet_len"), pmt::mp(_vec_soft_decisions.size())); - message_port_pub(_msg_port_name, - pmt::cons(_msg_metadata, - pmt::init_f32vector(_vec_soft_decisions.size(), _vec_soft_decisions))); - _vec_soft_decisions.clear(); - } - _samples.clear(); - // get information about the following frame - update_frame_information(_physicalLayer.attr("get_frame")()); - } catch (boost::python::error_already_set const&) { - PyErr_Print(); - } - } // frame is ready - if (_ignore_filter_updates == 0) { - out[nout++] = filter(); - if (_symbol_counter+1 == _symbols.size()) - recenter_filter_taps(); - } else { - _ignore_filter_updates -= 1; - } - } // (_sample_counter%_sps) == 0 - if (_need_samples) { - _samples.push_back(_hist_samples[_hist_sample_index+_nB+1]); - } - if (_saved_samples.empty()) { - insert_sample(in[i++]); - } else { - insert_sample(_saved_samples.back()); - _saved_samples.pop_back(); - } - _sample_counter += 1; - } // DO_FILTER - } // switch _state - } // next input sample - - consume(0, i); - - // Tell runtime system how many output items we produced. + // std::cout << "FILTER_CHECK: " << i << " " << i-1-_nB << " " << i+_nF << " " << in[i] << std::endl; + assert(i+_nF < nin && i-1-_nB >= 0); + out[nout++] = filter(in + i - _nB, in + i + _nF+1); + } // next sample + consume(0, ninput_processed); + break; + } // DO_FILTER + } return nout; } bool adaptive_dfe_impl::start() { gr::thread::scoped_lock lock(d_setlock); - // make sure python is ready for threading - if( Py_IsInitialized() ){ - GILLock gil_lock; - if(PyEval_ThreadsInitialized() != 1 ){ - PyEval_InitThreads(); - } - boost::python::numpy::initialize(); - } else { - throw std::runtime_error("dont use adaptive_dfe without python!"); - } - _taps_samples = (gr_complex*)(volk_malloc( (_nB+_nF+1)*sizeof(gr_complex), volk_get_alignment())); - _taps_symbols = (gr_complex*)(volk_malloc( _nW*sizeof(gr_complex), volk_get_alignment())); - _hist_samples = (gr_complex*)(volk_malloc(2*(_nB+_nF+1)*sizeof(gr_complex), volk_get_alignment())); - _hist_symbols = (gr_complex*)(volk_malloc( 2*_nW*sizeof(gr_complex), volk_get_alignment())); - - _samples.clear(); - std::fill_n(_hist_samples, 2*(_nB+_nF+1), gr_complex(0)); - std::fill_n(_hist_symbols, 2*_nW, gr_complex(0)); - std::fill_n(_taps_samples, (_nB+_nF+1), gr_complex(0)); - std::fill_n(_taps_symbols, _nW, gr_complex(0)); - - _taps_samples[_nB+1] = 0.01; - _taps_symbols[0] = 1; + _taps_samples = (gr_complex*)(volk_malloc((_nB+_nF+1)*sizeof(gr_complex), volk_get_alignment())); + _taps_symbols = (gr_complex*)(volk_malloc( _nW*sizeof(gr_complex), volk_get_alignment())); + _hist_symbols = (gr_complex*)(volk_malloc( 2*_nW*sizeof(gr_complex), volk_get_alignment())); + reset_filter(); GR_LOG_DEBUG(d_logger,str(boost::format("adaptive_dfe_impl::start() nB=%d nF=%d mu=%f alpha=%f") % _nB % _nF % _mu % _alpha)); - GILLock gil_lock; - try { - boost::python::object module = boost::python::import(boost::python::str("digitalhf.physical_layer." + _py_module_name)); - boost::python::object PhysicalLayer = module.attr("PhysicalLayer"); - _physicalLayer = PhysicalLayer(_sps); - update_constellations(_physicalLayer.attr("get_constellations")()); - } catch (boost::python::error_already_set const&) { - PyErr_Print(); - return false; - } return true; } bool adaptive_dfe_impl::stop() { gr::thread::scoped_lock lock(d_setlock); GR_LOG_DEBUG(d_logger, "adaptive_dfe_impl::stop()"); - GILLock gil_lock; - _physicalLayer = boost::python::object(); VOLK_SAFE_DELETE(_taps_samples); VOLK_SAFE_DELETE(_taps_symbols); - VOLK_SAFE_DELETE(_hist_samples); VOLK_SAFE_DELETE(_hist_symbols); return true; } -gr_complex adaptive_dfe_impl::filter() { +gr_complex adaptive_dfe_impl::filter(gr_complex const* start, gr_complex const* end) { + assert(end-start == _nB + _nF + 1); + gr_complex filter_output = 0; volk_32fc_x2_dot_prod_32fc(&filter_output, - _hist_samples+_hist_sample_index, + start, _taps_samples, _nB+_nF+1); gr_complex dot_symbols=0; gr::digital::constellation_sptr constell = _constellations[_constellation_index]; - bool const update_taps = constell->bits_per_symbol() <= 3; + bool const update_taps = true;//constell->bits_per_symbol() <= 3; if (constell->bits_per_symbol() > 3) _use_symbol_taps = false; if (_use_symbol_taps) { @@ -397,30 +236,28 @@ gr_complex adaptive_dfe_impl::filter() { } filter_output += dot_symbols; } + assert(_symbol_counter < _symbols.size()); gr_complex known_symbol = _symbols[_symbol_counter]; bool const is_known = std::abs(known_symbol) > 1e-5; - if (not is_known) { // not known + if (not is_known) { gr_complex const descrambled_filter_output = std::conj(_scramble[_symbol_counter]) * filter_output; - unsigned int jc = constell->decision_maker(&descrambled_filter_output); + unsigned int const jc = constell->decision_maker(&descrambled_filter_output); gr_complex descrambled_symbol = 0; constell->map_to_points(jc, &descrambled_symbol); - if (_save_soft_decisions) { float const err = std::abs(descrambled_filter_output - descrambled_symbol); - _npwr_counter[_constellation_index] += (_npwr_counter[_constellation_index] < _npwr_max_time_constant); - float const alpha = 1.0f/_npwr_counter[_constellation_index]; - _npwr[_constellation_index] = (1-alpha)*_npwr[_constellation_index] + alpha*err; - std::vector const soft_dec = constell->calc_soft_dec(descrambled_filter_output, _npwr[_constellation_index]); + std::vector const soft_dec = constell->calc_soft_dec + (descrambled_filter_output, _npwr[_constellation_index].filter(err)); std::copy(soft_dec.begin(), soft_dec.end(), std::back_inserter >(_vec_soft_decisions)); } known_symbol = _scramble[_symbol_counter] * descrambled_symbol; } + // std::cout << "FILTER: " << filter_output <<" " << known_symbol << " " << start[_nB+1] << std::endl; if (is_known || update_taps) { gr_complex const err = filter_output - known_symbol; - for (int j=0; j<_nB+_nF+1; ++j) { - assert(_hist_sample_index+j < 2*(_nB+_nF+1)); - _taps_samples[j] -= _mu*err*std::conj(_hist_samples[_hist_sample_index+j]); - } + for (int j=0; j<_nB+_nF+1; ++j) + _taps_samples[j] -= _mu*err*std::conj(start[j]); + if (_use_symbol_taps) { for (int j=0; j<_nW; ++j) { assert(_hist_symbol_index+j < 2*_nW); @@ -435,165 +272,98 @@ gr_complex adaptive_dfe_impl::filter() { return filter_output*std::conj(_scramble[_symbol_counter++]); } -void adaptive_dfe_impl::recenter_filter_taps() { +int +adaptive_dfe_impl::recenter_filter_taps() { // get max(abs(taps)) ssize_t const idx_max = std::distance(_taps_samples, std::max_element(_taps_samples+_nB+1-3*_sps, _taps_samples+_nB+1+3*_sps, [](gr_complex a, gr_complex b) { return std::norm(a) < std::norm(b); })); - GR_LOG_DEBUG(d_logger, str(boost::format("idx_max=%2d abs(tap_max)=%f") % idx_max % std::abs(_taps_samples[idx_max]))); - - if (idx_max-_nB-1 >= 2*_sps && _saved_samples.empty() && _ignore_filter_updates==0) { + // GR_LOG_DEBUG(d_logger, str(boost::format("idx_max=%2d abs(tap_max)=%f") % idx_max % std::abs(_taps_samples[idx_max]))); + if (idx_max-_nB-1 > +2*_sps) { // maximum is right of the center tap // -> shift taps to the left left GR_LOG_DEBUG(d_logger, "shift left"); std::copy(_taps_samples+2*_sps, _taps_samples+_nB+_nF+1, _taps_samples); std::fill_n(_taps_samples+_nB+_nF+1-2*_sps, 2*_sps, gr_complex(0)); - // and omit the next two calls to filter in order to keep the alignment between samples and taps - _ignore_filter_updates = 2; - - } else if (idx_max-_nB-1 <= -2*_sps && _saved_samples.empty() && _ignore_filter_updates==0) { + return +2*_sps; + } + if (idx_max-_nB-1 < -2*_sps) { // maximum is left of the center tap // -> shift taps to the right GR_LOG_DEBUG(d_logger, "shift right"); std::copy_backward(_taps_samples, _taps_samples+_nB+_nF+1-2*_sps, _taps_samples+_nB+_nF+1); std::fill_n(_taps_samples, 2*_sps, gr_complex(0)); - // save the last 2*_sps samples (will be reinserted) - _saved_samples.resize(2*_sps); - std::reverse_copy(_hist_samples+_hist_sample_index+(_nB+_nF+1)-2*_sps, - _hist_samples+_hist_sample_index+(_nB+_nF+1), - _saved_samples.begin()); - // shift samples index - _hist_sample_index += (_nB+_nF+1)-2*_sps; - _hist_sample_index %= (_nB+_nF+1); - // set the 1st 2*_sps unknown old samples to zero - for (int l=_hist_sample_index; l<_hist_sample_index+2*_sps; ++l) { - int const k = (l+_nB+_nF+1)%(2*(_nB+_nF+1)); - _hist_samples[l] = _hist_samples[k] = gr_complex(0); - } - } -} -void adaptive_dfe_impl::set_mode(std::string mode) { - gr::thread::scoped_lock lock(d_setlock); - GR_LOG_DEBUG(d_logger, "adaptive_dfe_impl::set_mode "+ mode); - GILLock gil_lock; - try { - _physicalLayer.attr("set_mode")(mode); - } catch (boost::python::error_already_set const&) { - PyErr_Print(); - return; + return -2*_sps; } + return 0; } -void adaptive_dfe_impl::update_constellations(boost::python::object obj) +void adaptive_dfe_impl::reset_filter() { - int const n = boost::python::extract(obj.attr("__len__")()); + std::fill_n(_taps_samples, _nB+_nF+1, gr_complex(0)); + std::fill_n(_taps_symbols, _nW, gr_complex(0)); + std::fill_n(_hist_symbols, 2*_nW, gr_complex(0)); + _taps_samples[_nB+1] = 0.01; + _taps_symbols[0] = 1; + _hist_symbol_index = 0; +} + +void adaptive_dfe_impl::publish_frame_info() +{ + pmt::pmt_t data = pmt::make_dict(); + GR_LOG_DEBUG(d_logger, str(boost::format("publish_frame_info %d == %d") % _descrambled_symbols.size() % _symbols.size())); + data = pmt::dict_add(data, pmt::mp("symbols"), pmt::init_c32vector(_descrambled_symbols.size(), &_descrambled_symbols.front())); + message_port_pub(_msg_ports["frame_info"], data); + _descrambled_symbols.clear(); +} + +void adaptive_dfe_impl::publish_soft_dec() +{ + if (_vec_soft_decisions.empty()) + return; + message_port_pub(_msg_ports["soft_dec"], + pmt::cons(pmt::dict_add(_msg_metadata, pmt::mp("packet_len"), pmt::mp(_vec_soft_decisions.size())), + pmt::init_f32vector(_vec_soft_decisions.size(), _vec_soft_decisions))); + _vec_soft_decisions.clear(); +} + +void adaptive_dfe_impl::update_constellations(pmt::pmt_t data) { + int const n = pmt::length(data); _constellations.resize(n); _npwr.resize(n); - _npwr_counter.resize(n); + std::cout << "adaptive_dfe_impl::update_constellations " << data << std::endl; + std::cout << "adaptive_dfe_impl::update_constellations n=" << n << std::endl; + unsigned int const rotational_symmetry = 0; + unsigned int const dimensionality = 1; + for (int i=0; i constell(m); - std::vector pre_diff_code(m); - for (int j=0; j=0 && idx < n); + _constellations[idx] = gr::digital::constellation_calcdist::make + (pmt::c32vector_elements(pmt::dict_ref(c, pmt::mp("points"), pmt::PMT_NIL)), + pmt::s32vector_elements(pmt::dict_ref(c, pmt::mp("symbols"), pmt::PMT_NIL)), + rotational_symmetry, dimensionality); + _npwr[i].reset(_npwr_max_time_constant); } } -bool adaptive_dfe_impl::update_frame_information(boost::python::object obj) -{ - int const n = boost::python::extract(obj.attr("__len__")()); - assert(n==4); - boost::python::numpy::ndarray array = boost::python::numpy::array(obj[0]); - char const* data = array.get_data(); - int const m = array.shape(0); - _symbols.resize(m); - _scramble.resize(m); - _descrambled_symbols.resize(m); - _samples.clear(); - for (int i=0; i (obj[1]); - _need_samples = boost::python::extract(obj[2]); - _save_soft_decisions = boost::python::extract(obj[3]); - return true; -} -bool adaptive_dfe_impl::update_doppler_information(boost::python::object obj) -{ - int const n = boost::python::extract(obj.attr("__len__")()); - assert(n==2); - bool const do_continue = boost::python::extract(obj[0]); - if (!do_continue) { - _phase = 0; - _df = 0; - std::fill_n(_hist_samples, 2*(_nB+_nF+1), gr_complex(0)); - _hist_sample_index = 0; - _sample_counter = 0; - return false; - } - float const doppler = boost::python::extract(obj[1]); - update_pll(doppler); - return true; -} -void adaptive_dfe_impl::update_pll(float doppler) { - if (doppler == 0) - return; - float const delta_f = doppler/_sps; - if (_df == 0) { // init - _ud = _df = delta_f; - } else { - float const ud_old = _ud; - _ud = delta_f; - _df +=_b[0]*_ud + _b[1]*ud_old; - } - GR_LOG_DEBUG(d_logger, str(boost::format("PLL: df=%f delta_f=%f (rad/sample)") % _df % delta_f)); -} -void adaptive_dfe_impl::insert_sample(gr_complex z) { - // insert sample into the circular buffer - _hist_samples[_hist_sample_index] = _hist_samples[_hist_sample_index+_nB+_nF+1] = z * gr_expj(-_phase); - if (++_hist_sample_index == _nB+_nF+1) - _hist_sample_index = 0; - if (z != gr_complex(0)) - update_local_oscillator(); -} -void adaptive_dfe_impl:: update_local_oscillator() { - _phase += _df; - if (_phase > M_PI) - _phase -= 2*M_PI; - if (_phase < -M_PI) - _phase += 2*M_PI; -} -bool adaptive_dfe_impl::get_correlation_tag(uint64_t i, uint64_t& offset, float& phase_est) { - std::vector v; - get_tags_in_window(v, 0, i,i+1); - for (int j=0; j 10e3) - return true; - } - } - return false; +void adaptive_dfe_impl::update_frame_info(pmt::pmt_t data) +{ + //GR_LOG_DEBUG(d_logger,str(boost::format("adaptive_dfe_impl::update_frame_info() %s") % data)); + _symbols = pmt::c32vector_elements(pmt::dict_ref(data, pmt::mp("symb"), pmt::PMT_NIL)); + _scramble = pmt::c32vector_elements(pmt::dict_ref(data, pmt::mp("scramble"), pmt::PMT_NIL)); + _constellation_index = pmt::to_long(pmt::dict_ref(data, pmt::mp("constellation_idx"), pmt::PMT_NIL)); + _save_soft_decisions = pmt::to_bool(pmt::dict_ref(data, pmt::mp("save_soft_dec"), pmt::PMT_F)); + bool const do_continue = pmt::to_bool(pmt::dict_ref(data, pmt::mp("do_continue"), pmt::PMT_F)); + assert(_symbols.size() == _scramble.size()); + _descrambled_symbols.resize(_symbols.size()); + _vec_soft_decisions.clear(); + _symbol_counter = 0; + _state = (do_continue ? DO_FILTER : WAIT_FOR_PREAMBLE); } } /* namespace digitalhf */ diff --git a/lib/adaptive_dfe_impl.h b/lib/adaptive_dfe_impl.h index 8c2a20b..d7b2464 100644 --- a/lib/adaptive_dfe_impl.h +++ b/lib/adaptive_dfe_impl.h @@ -21,18 +21,42 @@ #ifndef INCLUDED_DIGITALHF_ADAPTIVE_DFE_IMPL_H #define INCLUDED_DIGITALHF_ADAPTIVE_DFE_IMPL_H -#include -#include #include #include namespace gr { namespace digitalhf { +class constellation_distance_filter { +public: + constellation_distance_filter(int max_time_constant=10) + : _max_time_constant(max_time_constant) + , _counter(0) + , _pwr(0) {} + + void reset(int max_time_constant) { + _max_time_constant = max_time_constant; + _counter = 0; + _pwr = 0; + } + float filter(float x) { + _counter += (_counter < _max_time_constant); + float const alpha = 1.0f/_counter; + _pwr = (1-alpha)*_pwr + alpha*x; + return _pwr; + } +protected: +private: + int _max_time_constant; + int _counter; + float _pwr; // filtered distance to constellation point +} ; + class adaptive_dfe_impl : public adaptive_dfe { private: int _sps; int _nB, _nF, _nW; + int _nGuard; float _mu; float _alpha; @@ -40,64 +64,45 @@ private: bool _use_symbol_taps; // module name w.r.t. digitalhf.physical_layer containing a PhysicalLayer class - std::string _py_module_name; - boost::python::object _physicalLayer; // class instance of physical layer description + // std::string _py_module_name; + // boost::python::object _physicalLayer; // class instance of physical layer description gr_complex* _taps_samples; gr_complex* _taps_symbols; - gr_complex* _hist_samples; gr_complex* _hist_symbols; - - int _hist_sample_index; int _hist_symbol_index; - int _ignore_filter_updates; - std::vector _saved_samples; - - uint64_t _sample_counter; - std::vector _constellations; - std::vector _npwr; - std::vector _npwr_counter; + std::vector _npwr; int _npwr_max_time_constant; int _constellation_index; - std::vector _samples; std::vector _symbols; std::vector _scramble; std::vector _descrambled_symbols; int _symbol_counter; - bool _need_samples; bool _save_soft_decisions; std::vector _vec_soft_decisions; - pmt::pmt_t _msg_port_name; - pmt::pmt_t _msg_metadata; - - // PLL for doppler tracking - float _df; // frequency offset in radians per sample - float _phase; // accumulated phase for frequency correction - const float _b[2]; - float _ud; + std::map _msg_ports; + pmt::pmt_t _msg_metadata; enum state { WAIT_FOR_PREAMBLE, - INITIAL_DOPPLER_ESTIMATE, - INITIAL_DOPPLER_ESTIMATE_CONTINUE, + WAIT_FOR_FRAME_INFO, DO_FILTER } _state; - void update_constellations(boost::python::object obj); - bool update_frame_information(boost::python::object obj); - bool update_doppler_information(boost::python::object obj); +// void update_constellations(boost::python::object obj); + void update_constellations(pmt::pmt_t ); + void update_frame_info(pmt::pmt_t ); - void update_local_oscillator(); - gr_complex filter(); - void recenter_filter_taps(); + gr_complex filter(gr_complex const* start, gr_complex const* end); + int recenter_filter_taps(); + void reset_filter(); - void insert_sample(gr_complex z); - void update_pll(float doppler); - bool get_correlation_tag(uint64_t i, uint64_t& offset, float& phase_est); + void publish_frame_info(); + void publish_soft_dec(); public: adaptive_dfe_impl(int sps, // samples per symbol @@ -105,8 +110,7 @@ public: int nF, // number of backward FIR taps int nW, // number of symbol taps float mu, - float alpha, - std::string physical_layer_description); + float alpha); virtual ~adaptive_dfe_impl(); void forecast (int noutput_items, gr_vector_int &ninput_items_required); @@ -120,8 +124,7 @@ public: gr_vector_void_star &output_items); virtual void set_mu(float mu) { _mu = mu; } - virtual void set_mode(std::string); - + virtual void set_alpha(float alpha) { _alpha = alpha; } } ; } // namespace digitalhf diff --git a/lib/doppler_correction_cc_impl.cc b/lib/doppler_correction_cc_impl.cc new file mode 100644 index 0000000..3d77467 --- /dev/null +++ b/lib/doppler_correction_cc_impl.cc @@ -0,0 +1,153 @@ +/* -*- c++ -*- */ +/* + * Copyright 2018 hcab14@gmail.com. + * + * This 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. + * + * This software 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 software; 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 +#include +#include + +#include "doppler_correction_cc_impl.h" + +namespace gr { +namespace digitalhf { + +doppler_correction_cc::sptr +doppler_correction_cc::make(unsigned int preamble_length, unsigned int preamble_length_cc) +{ + return gnuradio::get_initial_sptr + (new doppler_correction_cc_impl(preamble_length, preamble_length_cc)); +} + +/* + * The private constructor + */ +doppler_correction_cc_impl::doppler_correction_cc_impl(unsigned int preamble_length, + unsigned int preamble_length_cc) + : gr::sync_block("doppler_correction_cc", + gr::io_signature::make(1, 1, sizeof(gr_complex)), + gr::io_signature::make(1, 1, sizeof(gr_complex))) + , _preamble_length(preamble_length) + , _preamble_length_cc(preamble_length_cc) + , _rotator() + , _state(WAIT_FOR_PHASE_EST_TAG) + , _msg_metadata(pmt::make_dict()) + , _port_name(pmt::mp("doppler")) + , _phase_est(0) +{ + GR_LOG_DECLARE_LOGPTR(d_logger); + GR_LOG_ASSIGN_LOGPTR(d_logger, "doppler_correction_cc"); + message_port_register_out(_port_name); + message_port_register_in (_port_name); +// set_msg_handler(_port_name, boost::bind(&doppler_correction_cc_impl::handle_message, this, _1)); + set_tag_propagation_policy(TPP_DONT); + set_output_multiple(2*_preamble_length); // without this the CPU usage goes up to 100% +} + +doppler_correction_cc_impl::~doppler_correction_cc_impl() +{ +} + +void +doppler_correction_cc_impl::forecast(int noutput_items, gr_vector_int &ninput_items_required) +{ + ninput_items_required[0] = noutput_items + _preamble_length; +} + +void +doppler_correction_cc_impl::handle_message(pmt::pmt_t msg) +{ + bool const success = pmt::to_bool(pmt::dict_ref(msg, pmt::mp("success"), pmt::get_PMT_F())); + if (!success) { + // GR_LOG_DEBUG(d_logger, "next state > CONSUME_AND_SKIP success=false"); + _state = CONSUME_AND_SKIP; + return; + } + float const doppler = pmt::to_float(pmt::dict_ref(msg, pmt::mp("doppler"), pmt::from_float(0))); + _rotator.set_phase(gr_expj(-_phase_est + 0.5*doppler*_preamble_length_cc)); + _rotator.set_phase_incr(gr_expj(-doppler)); + // GR_LOG_DEBUG(d_logger, str(boost::format("next state > CONSUME_AND_INSERT_PREAMBLE_TAG phase_est=%f doppler=%f") + // % (_phase_est - 0.5*doppler*_preamble_length_cc) + // % doppler)); + _state = CONSUME_AND_INSERT_PREAMBLE_TAG; +} + +int +doppler_correction_cc_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + gr_complex const* in = (gr_complex const*)input_items[0]; + gr_complex *out = (gr_complex *) output_items[0]; + +// assert(noutput_items >= _preamble_length); + noutput_items -= _preamble_length; + if (noutput_items < 0) + return 0; + int nout = 0; + switch (_state) { + case WAIT_FOR_PHASE_EST_TAG: { + std::vector v; + get_tags_in_window(v, 0, 0, noutput_items, pmt::mp("phase_est")); + if (v.empty()) { + nout = noutput_items; + } else { + tag_t const& tag = v.front(); + uint64_t const offset = tag.offset - nitems_read(0); + nout = offset; + _phase_est = pmt::to_double(tag.value); + _msg_metadata = pmt::dict_add(_msg_metadata, pmt::mp("packet_len"), pmt::from_long(_preamble_length)); + message_port_pub(_port_name, + pmt::cons(_msg_metadata, + pmt::init_c32vector(_preamble_length, in+nout))); + // GR_LOG_DEBUG(d_logger, str(boost::format("next state > WAIT_FOR_MSG %lld phase_est=%f") % tag.offset % _phase_est)); + _state = WAIT_FOR_MSG; + } + break; + } // WAIT_FOR_PHASE_EST_TAG + case WAIT_FOR_MSG: { + // GR_LOG_DEBUG(d_logger, "WAIT_FOR_MSG"); + handle_message(delete_head_blocking(_port_name)); + break; + } // WAIT_FOR_MSG + case CONSUME_AND_INSERT_PREAMBLE_TAG: { + add_item_tag(0, nitems_read(0), pmt::mp("preamble_start"), pmt::from_long(0)); + nout = _preamble_length; + // GR_LOG_DEBUG(d_logger, str(boost::format("next state > WAIT_FOR_PHASE_EST_TAG %lld") % nitems_read(0))); + _state = WAIT_FOR_PHASE_EST_TAG; + break; + } // CONSUME_AND_INSERT_PREAMBLE_TAG + case CONSUME_AND_SKIP: { + nout = _preamble_length; + // GR_LOG_DEBUG(d_logger, str(boost::format("next state > WAIT_FOR_PHASE_EST_TAG %lld") % nitems_read(0))); + _state = WAIT_FOR_PHASE_EST_TAG; + break; + } // CONSUME_AND_SKIP + } + // apply current doppler correction to all produced samples + _rotator.rotateN(out, in, nout); + // Tell runtime system how many output items we produced. + return nout; +} + +} /* namespace digitalhf */ +} /* namespace gr */ diff --git a/lib/doppler_correction_cc_impl.h b/lib/doppler_correction_cc_impl.h new file mode 100644 index 0000000..fdab73c --- /dev/null +++ b/lib/doppler_correction_cc_impl.h @@ -0,0 +1,65 @@ +/* -*- c++ -*- */ +/* + * Copyright 2018 hcab14@gmail.com. + * + * This 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. + * + * This software 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 software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITALHF_DOPPLER_CORRECTION_CC_IMPL_H +#define INCLUDED_DIGITALHF_DOPPLER_CORRECTION_CC_IMPL_H + +#include + +#include + +namespace gr { +namespace digitalhf { + +class doppler_correction_cc_impl : public doppler_correction_cc +{ +private: + unsigned int _preamble_length; // length of preamble (in samples) + unsigned int _preamble_length_cc; // length of the part of the preamble used for cross correlation (in samples) + blocks::rotator _rotator; + enum { + WAIT_FOR_PHASE_EST_TAG, // wait for a tag from corr_est_cc + WAIT_FOR_MSG, // wait for response from msg_proxy + CONSUME_AND_INSERT_PREAMBLE_TAG, // insert a preamble tag (=doppler calculation was successful) + CONSUME_AND_SKIP // do not intsert a tag and skip the samples (=doppler calculation was not successful) + } _state; + pmt::pmt_t _msg_metadata; + pmt::pmt_t _port_name; + + float _phase_est; + +public: + doppler_correction_cc_impl(unsigned int preamble_length, unsigned int preamble_length_cc); + virtual ~doppler_correction_cc_impl(); + + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +protected: + void handle_message(pmt::pmt_t msg); + +}; + +} // namespace digitalhf +} // namespace gr + +#endif /* INCLUDED_DIGITALHF_DOPPLER_CORRECTION_CC_IMPL_H */ diff --git a/lib/qa_digitalhf.cc b/lib/qa_digitalhf.cc index 8384713..b6992c6 100644 --- a/lib/qa_digitalhf.cc +++ b/lib/qa_digitalhf.cc @@ -28,12 +28,14 @@ #include "qa_digitalhf.h" #include "qa_adaptive_dfe.h" +#include "qa_doppler_correction_cc.h" CppUnit::TestSuite * qa_digitalhf::suite() { CppUnit::TestSuite *s = new CppUnit::TestSuite("digitalhf"); s->addTest(gr::digitalhf::qa_adaptive_dfe::suite()); + s->addTest(gr::digitalhf::qa_doppler_correction_cc::suite()); return s; } diff --git a/lib/qa_doppler_correction_cc.cc b/lib/qa_doppler_correction_cc.cc new file mode 100644 index 0000000..e7945c9 --- /dev/null +++ b/lib/qa_doppler_correction_cc.cc @@ -0,0 +1,37 @@ +/* -*- c++ -*- */ +/* + * Copyright 2018 hcab14@gmail.com. + * + * This 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. + * + * This software 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 software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#include +#include +#include "qa_doppler_correction_cc.h" +#include + +namespace gr { +namespace digitalhf { + +void +qa_doppler_correction_cc::t1() +{ + // Put test here +} + +} /* namespace digitalhf */ +} /* namespace gr */ diff --git a/lib/qa_doppler_correction_cc.h b/lib/qa_doppler_correction_cc.h new file mode 100644 index 0000000..620c238 --- /dev/null +++ b/lib/qa_doppler_correction_cc.h @@ -0,0 +1,46 @@ +/* -*- c++ -*- */ +/* + * Copyright 2018 hcab14@gmail.com. + * + * This 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. + * + * This software 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 software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef _QA_DOPPLER_CORRECTION_CC_H_ +#define _QA_DOPPLER_CORRECTION_CC_H_ + +#include +#include + +namespace gr { +namespace digitalhf { + +class qa_doppler_correction_cc : public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(qa_doppler_correction_cc); + CPPUNIT_TEST(t1); + CPPUNIT_TEST_SUITE_END(); + +private: + void t1(); +}; + +} /* namespace digitalhf */ +} /* namespace gr */ + +#endif /* _QA_DOPPLER_CORRECTION_CC_H_ */ + diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 4d5bf2e..ede36a1 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -32,7 +32,8 @@ endif() GR_PYTHON_INSTALL( FILES __init__.py - DESTINATION ${GR_PYTHON_DIR}/digitalhf + physical_layer_driver.py + msg_proxy.py DESTINATION ${GR_PYTHON_DIR}/digitalhf ) ######################################################################## @@ -45,3 +46,6 @@ set(GR_TEST_PYTHON_DIRS ${CMAKE_BINARY_DIR}/swig) GR_ADD_TEST(qa_adaptive_dfe ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_adaptive_dfe.py) add_subdirectory(physical_layer) +GR_ADD_TEST(qa_physical_layer_driver_driver ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_physical_layer_driver_driver.py) +GR_ADD_TEST(qa_doppler_correction_cc ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_doppler_correction_cc.py) +GR_ADD_TEST(qa_msg_proxy ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_msg_proxy.py) diff --git a/python/__init__.py b/python/__init__.py index c63d72a..61f3827 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -31,4 +31,6 @@ except ImportError: pass # import any pure python here +from physical_layer_driver import physical_layer_driver +from msg_proxy import msg_proxy # diff --git a/python/msg_proxy.py b/python/msg_proxy.py new file mode 100644 index 0000000..f3fc253 --- /dev/null +++ b/python/msg_proxy.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2018 hcab14@gmail.com. +# +# This 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. +# +# This software 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 software; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import numpy as np +from gnuradio import gr +import pmt + +class msg_proxy(gr.basic_block): + """ + docstring for block msg_proxy + """ + def __init__(self, physical_layer_object): + gr.basic_block.__init__(self, + name="msg_proxy", + in_sig=[], + out_sig=[]) + self._obj = physical_layer_object + + self._port_doppler = pmt.intern("doppler") + self.message_port_register_in(self._port_doppler) + self.message_port_register_out(self._port_doppler) + self.set_msg_handler(self._port_doppler, self.msg_handler_doppler) + + self._port_frame_info = pmt.intern("frame_info") + self.message_port_register_in(self._port_frame_info) + self.message_port_register_out(self._port_frame_info) + self.set_msg_handler(self._port_frame_info, self.msg_handler_frame) + + def msg_handler_doppler(self, msg_in): + ## print('-------------------- msg_handler_doppler --------------------') + iq_samples = pmt.to_python(pmt.cdr(msg_in)) + success,doppler = self._obj.get_doppler(iq_samples) + msg_out = pmt.make_dict() + msg_out = pmt.dict_add(msg_out, pmt.intern('success'), pmt.to_pmt(success)) + msg_out = pmt.dict_add(msg_out, pmt.intern('doppler'), pmt.to_pmt(doppler)) + ## print(msg_out) + self.message_port_pub(self._port_doppler, msg_out) + + def msg_handler_frame(self, msg_in): + ## print('-------------------- msg_handler_frame --------------------') + ## print(msg_in) + symbols = pmt.to_python(pmt.dict_ref(msg_in, pmt.intern('symbols'), pmt.PMT_NIL)) + symb,constellation_idx,do_continue,save_soft_dec = self._obj.get_next_frame(symbols) + ##print('symb=', symb, symb['symb'], symb['scramble']) + msg_out = pmt.make_dict() + msg_out = pmt.dict_add(msg_out, pmt.intern('symb'), pmt.to_pmt(symb['symb'])) + msg_out = pmt.dict_add(msg_out, pmt.intern('scramble'), pmt.to_pmt(symb['scramble'])) + msg_out = pmt.dict_add(msg_out, pmt.intern('constellation_idx'), pmt.to_pmt(constellation_idx)) + msg_out = pmt.dict_add(msg_out, pmt.intern('do_continue'), pmt.to_pmt(do_continue)) + msg_out = pmt.dict_add(msg_out, pmt.intern('save_soft_dec'), pmt.to_pmt(save_soft_dec)) + ## print(msg_out) + self.message_port_pub(self._port_frame_info, msg_out) diff --git a/python/physical_layer/MIL_STD_188_110A.py b/python/physical_layer/MIL_STD_188_110A.py index 382e7ac..a16028a 100644 --- a/python/physical_layer/MIL_STD_188_110A.py +++ b/python/physical_layer/MIL_STD_188_110A.py @@ -114,20 +114,35 @@ class PhysicalLayer(object): def get_constellations(self): return self._constellations - def get_frame(self): + def get_next_frame(self, symbols): """returns a tuple describing the frame: [0] ... known+unknown symbols and scrambling [1] ... modulation type after descrambling - [2] ... a boolean indicating whethere or not raw IQ samples needed + [2] ... a boolean indicating if the processing should continue [3] ... a boolean indicating if the soft decision for the unknown symbols are saved""" print('-------------------- get_frame --------------------', self._pre_counter, self._frame_counter) - ## --- preamble frame ---- - if self._pre_counter != 0: - self._scr_data.reset() - return [self._preamble,MODE_BPSK,True,False] - ## ----- data frame ------ + success = True + if self._frame_counter == -1: ## preamble mode + if len(symbols) == 0: + return [self._preamble,MODE_BPSK,success,False] + else: + success = self.decode_preamble(symbols) + if self._pre_counter != 0: + return [self._preamble,MODE_BPSK,success,False] + else: + self._frame_counter = 0 + self._scr_data.reset() + return [self.get_next_data_frame(success),self._mode['ci'],success,success] + else: ## data mode + self._frame_counter += 1 + print('test:', symbols[self._mode['unknown']:], np.mean(np.real(symbols[self._mode['unknown']:]))) + if self._frame_counter < self._num_frames_per_block-2: + success = np.bool(np.mean(np.real(symbols[self._mode['unknown']:])) > 0.7) + return [self.get_next_data_frame(success),self._mode['ci'],success,success] + + def get_next_data_frame(self, success): if self._frame_counter == self._num_frames_per_block: self._frame_counter = 0 scramble_for_frame = n_psk(8, np.array([self._scr_data.next() @@ -142,34 +157,12 @@ class PhysicalLayer(object): idx_d1d2 = self._frame_counter - self._num_frames_per_block + 2; a['symb'][n_unknown :n_unknown+ 8] *= n_psk(2, WALSH[self._d1d2[idx_d1d2]][:]) a['symb'][n_unknown+8:n_unknown+16] *= n_psk(2, WALSH[self._d1d2[idx_d1d2]][:]) + if not success: + self._frame_counter = -1 + self._pre_counter = -1 + return a - self._frame_counter += 1 - return [a, self._mode['ci'],False,True] - - def get_doppler(self, symbols, iq_samples): - """returns a tuple - [0] ... quality flag - [1] ... doppler estimate (rad/symbol) if available""" - print('-------------------- get_doppler --------------------', - self._frame_counter,len(symbols),len(iq_samples)) - success,doppler = False,0 - if self._frame_counter == -1: ## -- preamble ---- - success,doppler = self.get_doppler_from_preamble(symbols, iq_samples) - if len(symbols) != 0: - success = self.decode_preamble(symbols) - if self._pre_counter == 0: - self._frame_counter = 0 - print('pre_counter', self._pre_counter, - 'mode', self._mode) - else: ## ------------------------ data frame ---- - print(self._frame_counter,symbols, np.mean(np.abs(symbols))) - success = np.mean(np.abs(symbols[0:20])) > 0.5 - if not success: - self._frame_counter = -1 - self._pre_counter = -1 - return success,doppler - - def get_doppler_from_preamble(self, symbols, iq_samples): + def get_doppler(self, iq_samples): """quality check and doppler estimation for preamble""" success,doppler = True,0 if len(iq_samples) != 0: @@ -177,21 +170,25 @@ class PhysicalLayer(object): zp = np.array([z for z in PhysicalLayer.get_preamble()['symb'] for _ in range(sps)], dtype=np.complex64) ## find starting point - cc = np.correlate(iq_samples, zp[0:3*32*sps]) + _,_zp = self.get_preamble_z() + cc = np.correlate(iq_samples, _zp) ##zp[0:3*32*sps]) imax = np.argmax(np.abs(cc[0:2*32*sps])) - pks = cc[(imax, imax+3*32*sps),] - tpks = cc[imax+3*16*sps:imax+5*16*sps] - print('imax=', imax, 'apks=',np.abs(pks), - np.mean(np.abs(pks)), np.mean(np.abs(tpks)), np.abs(tpks)) - success = np.mean(np.abs(pks)) > 2*np.mean(np.abs(tpks)) - doppler = np.diff(np.unwrap(np.angle(pks)))[0]/(3*32) if success else 0 + apks = np.abs(cc[(imax, imax+3*32*sps),]) + tpks = np.abs(cc[imax+3*16*sps:imax+5*16*sps]) + print('imax=', imax, 'apks=',apks, + np.mean(apks), np.mean(tpks)) + success = np.bool(np.mean(apks) > 5*np.mean(tpks) and + apks[0]/apks[1] > 0.5 and + apks[0]/apks[1] < 2.0) if success: idx = np.arange(32*sps) pks = [np.correlate(iq_samples[imax+i*32*sps+idx], - zp[i*32*sps+idx])[0] + zp[ i*32*sps+idx])[0] for i in range(9)] - doppler = freq_est(pks)/32 - print('success=', success, 'doppler=', doppler) + doppler = freq_est(pks)/(32*sps) + print('success=', success, 'doppler=', doppler, + np.abs(np.array(pks)), + np.angle(np.array(pks))) return success,doppler def decode_preamble(self, symbols): @@ -209,6 +206,9 @@ class PhysicalLayer(object): self._num_frames_per_block = self._block_len/self._frame_len; return True + def set_mode(self, _): + pass + @staticmethod def get_preamble(): """preamble symbols + scrambler""" @@ -217,12 +217,11 @@ class PhysicalLayer(object): dtype=[('symb', np.complex64), ('scramble', np.complex64)]) - @staticmethod - def get_preamble_z(sps): + def get_preamble_z(self): """preamble symbols for preamble correlation""" a = PhysicalLayer.get_preamble() - return np.array([z for z in a['symb'][0:32*3] - for _ in range(sps)]) + return 0,np.array([z for z in a['symb'][0:3*32] + for _ in range(self._sps)]) if __name__ == '__main__': def gen_data_scramble(): @@ -238,20 +237,23 @@ if __name__ == '__main__': a[i] = s&7; return a - p=PhysicalLayer(5) - z1=np.array([x for x in PRE_SYMBOLS for _ in range(5)]) - z2=np.array([x for x in PRE_SCRAMBLE for _ in range(5)]) - z=z1*z2 - + sps = 5; + p=PhysicalLayer(sps) + z1=np.array([x for x in PRE_SYMBOLS for _ in range(sps)]) + z2=np.array([x for x in PRE_SCRAMBLE for _ in range(sps)]) + z=z1*z2; + _,_z=p.get_preamble_z() + print(all(z[0:3*32*sps]==_z[0:3*32*sps])) for i in range(3): - print(i, all(z[32*5*i:32*5*(i+1)] == z[32*5*(3+i):32*5*(3+i+1)])) + print(i, all(z[32*sps*i:32*sps*(i+1)] == z[32*sps*(3+i):32*sps*(3+i+1)])) - print(np.sum(np.sum(z[0:32*5] * np.conj(z[32*5*3:32*5*4])))) - print(WALSH[1][:]) - print(sum(WALSH[1][:]*(1<= 1: - self.a = self.make_data_frame() - return [self.a, self._constellation_index,False,True] - - def get_doppler(self, symbols, iq_samples): - """returns a tuple - [0] ... quality flag - [1] ... doppler estimate (rad/symbol) if available""" - print('-------------------- get_doppler --------------------', - self._frame_counter,len(symbols),len(iq_samples)) - #if len(symbols)!=0: - # print('symb=', symbols) - success,doppler = False,0 - if self._frame_counter == -1: ## -- preamble ---- - success,doppler = self.get_doppler_from_preamble(symbols, iq_samples) - if len(symbols) != 0: - for s in symbols: - print(s) - self._frame_counter = 0 - elif self._frame_counter == 0: ## -- reinserted preamble ---- - ## decode D0,D1,D2 - idx = np.arange(13) - z = np.array([np.mean(symbols[32+13*i+idx]) for i in range(3)]) - d0d1d2 = map(np.uint8, np.mod(np.round(np.angle(z)/np.pi*2),4)) - dibits = [TO_DIBIT[idx] for idx in d0d1d2] - self._mode = {'rate': tuple([x[0] for x in dibits]), - 'interleaver': tuple([x[1] for x in dibits])} - print('======== rate,interleaver:', - TO_RATE[self._mode['rate']], - TO_INTERLEAVER[self._mode['interleaver']]) - rate_info = TO_RATE[self._mode['rate']] - self._constellation_index = rate_info['ci'] - print('constellation index', self._constellation_index) - scr = ScrambleData() - iscr = [scr.next(rate_info['bits_per_symbol']) for _ in range(256)] - if rate_info['ci'] > MODE_8PSK: - self._data_scramble = np.ones(256, dtype=np.complex64) - else: - constell = self._constellations[rate_info['ci']] - self._data_scramble = constell[iscr] self._frame_counter += 1 - success = True - elif self._frame_counter <=72: ## -- data ------------------- - print(np.abs(symbols[:16]), symbols[-31:], np.mean(symbols[-31:])) - success = np.abs(np.mean(symbols[-31:])) > 0.5 - if success: - self._frame_counter += 1 - else: - self._frame_counter = -1 - else: ## ------------------------ re-inserted preamble ------- - ## TODO - for s in symbols: - print(s) - success = False - self._frame_counter = -1 - return success,doppler + return [self._preamble,MODE_BPSK,success,False] - def get_doppler_from_preamble(self, symbols, iq_samples): + frame_counter_mod = self._frame_counter%72 + if frame_counter_mod == 0: ## --- re-inserted preamble + self._frame_counter += 1 + success = self.get_preamble_quality(symbols) + return [self.make_reinserted_preamble(self._preamble_offset,success),MODE_QPSK,success,False] + + if frame_counter_mod >= 1: ## ---- data frames + got_reinserted_preamble = frame_counter_mod == 1 + self._frame_counter += 1 + if got_reinserted_preamble: + success = self.decode_reinserted_preamble(symbols) + else: + success = self.get_data_frame_quality(symbols) + return [self.make_data_frame(success),self._constellation_index,success,not got_reinserted_preamble] + + def get_doppler(self, iq_samples): """quality check and doppler estimation for preamble""" success,doppler = True,0 if len(iq_samples) != 0: sps = self._sps idx = np.arange(23*sps) - zp = self.get_preamble_z(self._sps) + _,zp = self.get_preamble_z() cc = np.correlate(iq_samples, zp[idx]) imax = np.argmax(np.abs(cc[0:23*sps])) pks = [np.correlate(iq_samples[imax+i*23*sps+idx], zp[i*23*sps+idx])[0] - for i in range(7)] - success = np.mean(np.abs(pks)) > 2*np.mean(np.abs(cc[imax+11*sps+range(-sps,sps)])) + for i in range(8)] + success = np.bool(np.mean(np.abs(pks)) > 2*np.mean(np.abs(cc[imax+11*sps+range(-sps,sps)]))) print('test:',imax, np.mean(np.abs(pks)), np.mean(np.abs(cc[imax+11*sps+range(-sps,sps)]))) if success: print('doppler apks', np.abs(pks)) print('doppler ppks', np.angle(pks), np.diff(np.unwrap(np.angle(pks)))/23, np.mean(np.diff(np.unwrap(np.angle(pks)))/23)) - doppler = freq_est(pks[1:])/23; + doppler = freq_est(pks)/(23*sps); print('success=', success, 'doppler=', doppler) return success,doppler - def make_reinserted_preamble(self, offset): + def set_mode(self, mode): + pass + + def get_preamble_quality(self, symbols): + return np.bool(np.abs(np.mean(symbols[-40:])) > 0.5) + + def get_data_frame_quality(self, symbols): + return np.bool(np.abs(np.mean(symbols[-31:])) > 0.5) + + def decode_reinserted_preamble(self, symbols): + ## decode D0,D1,D2 + idx = np.arange(13) + z = np.array([np.mean(symbols[32+13*i+idx]) for i in range(3)]) + d0d1d2 = map(np.uint8, np.mod(np.round(np.angle(z)/np.pi*2),4)) + dibits = [TO_DIBIT[idx] for idx in d0d1d2] + self._mode = {'rate': tuple([x[0] for x in dibits]), + 'interleaver': tuple([x[1] for x in dibits])} + print('======== rate,interleaver:', + TO_RATE[self._mode['rate']], + TO_INTERLEAVER[self._mode['interleaver']]) + rate_info = TO_RATE[self._mode['rate']] + print('rate_info', rate_info) + self._constellation_index = rate_info['ci'] + print('constellation index', self._constellation_index) + scr = ScrambleData() + iscr = [scr.next(rate_info['bits_per_symbol']) for _ in range(256)] + if rate_info['ci'] > MODE_8PSK: + self._data_scramble = np.ones(256, dtype=np.complex64) + else: + constell = self._constellations[rate_info['ci']] + self._data_scramble = constell[iscr]['points'] + success = True ## TODO + return success + + def make_reinserted_preamble(self, offset, success): """ offset= 0 -> 1st reinserted preamble offset=-72 -> all following reinserted preambles""" + print('make_reinserted_preamble', offset, success) a=np.array(zip(REINSERTED_PREAMBLE[offset:], REINSERTED_PREAMBLE[offset:]), dtype=[('symb', np.complex64), ('scramble', np.complex64)]) a['symb'][-72:-72+3*13] = 0 ## D0,D1,D2 + if not success: + sefl._frame_counter = -1 return a - def make_data_frame(self): + + def make_data_frame(self, success): self._preamble_offset = -72 ## all following reinserted preambles start at index -72 a=np.zeros(256+31, dtype=[('symb', np.complex64), ('scramble', np.complex64)]) a['scramble'][:256] = self._data_scramble - n = self._frame_counter -1 + n = (self._frame_counter-2)%72 m = n%18 if m == 0: cnt = n//18 @@ -261,6 +254,8 @@ class PhysicalLayer(object): print('new mini-probe signs n=',n,'m=',m,self._mp) a['symb'][256:] = MINI_PROBE[self._mp[m]] a['scramble'][256:] = MINI_PROBE[self._mp[m]] + if not success: + self._frame_counter = -1 return a @staticmethod @@ -270,10 +265,10 @@ class PhysicalLayer(object): PREAMBLE), dtype=[('symb', np.complex64), ('scramble', np.complex64)]) - @staticmethod - def get_preamble_z(sps): + + def get_preamble_z(self): """preamble symbols for preamble correlation""" - return np.array([z for z in PREAMBLE for _ in range(sps)]) + return 2,np.array([z for z in PREAMBLE for _ in range(self._sps)]) if __name__ == '__main__': print(PREAMBLE) diff --git a/python/physical_layer/STANAG_4285.py b/python/physical_layer/STANAG_4285.py index 881e2de..134bb5c 100644 --- a/python/physical_layer/STANAG_4285.py +++ b/python/physical_layer/STANAG_4285.py @@ -12,7 +12,7 @@ class PhysicalLayer(object): def __init__(self, sps): """intialization""" self._sps = sps - self._mode = self.MODE_BPSK + self._mode = self.MODE_QPSK self._frame_counter = 0 self._is_first_frame = True self._constellations = [self.make_psk(2, [0,1]), @@ -29,50 +29,53 @@ class PhysicalLayer(object): def get_constellations(self): return self._constellations - def get_frame(self): + def get_next_frame(self, symbols): """returns a tuple describing the frame: [0] ... known+unknown symbols and scrambling [1] ... modulation type after descrambling - [2] ... a boolean indicating whethere or not raw IQ samples needed + [2] ... a boolean indicating if the processing should continue [3] ... a boolean indicating if the soft decision for the unknown symbols are saved""" - print('-------------------- get_frame --------------------', self._frame_counter) - return [self._preamble,self.MODE_BPSK,True,False] if self.is_preamble() else [self._data,self._mode,False,True] + ## print('-------------------- get_frame --------------------', self._frame_counter, len(symbols)) + if len(symbols) == 0: ## 1st preamble + self._frame_counter = 0 - def get_doppler(self, symbols, iq_samples): + success,frame_description = True,[] + if (self._frame_counter%2) == 0: + frame_description = [self._preamble,self.MODE_BPSK,success,False] + else: + idx = range(30,80) + z = symbols[idx]*np.conj(self._preamble['symb'][idx]) + ## print('quality_preamble',np.sum(np.real(z)<0), symbols[idx]) + success = np.bool(np.sum(np.real(z)<0) < 30) + frame_description = [self._data,self._mode,success,True] + + self._frame_counter += 1 + return frame_description + + def get_doppler(self, iq_samples): """returns a tuple [0] ... quality flag [1] ... doppler estimate (rad/symbol) if available""" - print('-------------------- get_doppler --------------------', self._frame_counter,len(symbols),len(iq_samples)) - success,doppler = self.quality_preamble(symbols,iq_samples) if self.is_preamble() else self.quality_data(symbols) - if len(symbols) != 0: - self._frame_counter = (self._frame_counter+1)&1 if success else 0 - self._is_first_frame = not success + ## print('-------------------- get_doppler --------------------', self._frame_counter,len(iq_samples)) + success,doppler = False,0 + if len(iq_samples) == 0: + return success,doppler + + sps = self._sps + zp = np.array([x for x in self._preamble['symb'][9:40] + for _ in range(sps)], dtype=np.complex64) + cc = np.correlate(iq_samples, zp) + imax = np.argmax(np.abs(cc[0:18*sps])) + pks = cc[(imax,imax+31*sps),] + tpks = cc[imax+15*sps:imax+16*sps] + ## print('doppler: ', np.abs(pks), np.abs(tpks)) + success = np.bool(np.mean(np.abs(pks)) > 5*np.mean(np.abs(tpks))) + doppler = np.diff(np.unwrap(np.angle(pks)))[0]/31/self._sps if success else 0 return success,doppler def is_preamble(self): return self._frame_counter == 0 - def quality_preamble(self, symbols, iq_samples): - """quality check and doppler estimation for preamble""" - success = True - doppler = 0 - if len(iq_samples) != 0: - sps = self._sps - zp = np.array([x for x in self._preamble['symb'][9:40] - for _ in range(sps)], dtype=np.complex64) - cc = np.correlate(iq_samples, zp) - imax = np.argmax(np.abs(cc[0:18*sps])) - pks = cc[(imax,imax+31*sps),] - tpks = cc[imax+15*sps:imax+16*sps] - success = np.mean(np.abs(pks)) > 2*np.mean(np.abs(tpks)) - doppler = np.diff(np.unwrap(np.angle(pks)))[0]/31 if success else 0 - if len(symbols) != 0: - idx = range(30,80) if self._is_first_frame else range(80) - z = symbols[idx]*np.conj(self._preamble['symb'][idx]) - print('quality_preamble',np.sum(np.real(z)<0)) - success = np.sum(np.real(z)<0) < 30 - return success,doppler - def quality_data(self, s): """quality check for the data frame""" known_symbols = np.mod(range(176),48)>=32 @@ -80,6 +83,11 @@ class PhysicalLayer(object): success = np.sum(np.real(s[known_symbols])<0) < 20 return success,0 ## no doppler estimate for data frames + def get_preamble_z(self): + """preamble symbols for preamble correlation""" + a = PhysicalLayer.get_preamble() + return 2,np.array([z for z in a['symb'][0:31] for _ in range(self._sps)]) + @staticmethod def get_preamble(): """preamble symbols + scrambler(=1)""" @@ -117,7 +125,7 @@ class PhysicalLayer(object): @staticmethod def make_psk(n, gray_code): """generates n-PSK constellation data""" - c = np.zeros(n, dtype=[('points', np.complex64), ('symbols', np.uint8)]) + c = np.zeros(n, dtype=[('points', np.complex64), ('symbols', np.int32)]) c['points'] = np.exp(2*np.pi*1j*np.arange(n)/n) c['symbols'] = gray_code return c diff --git a/python/physical_layer/common.py b/python/physical_layer/common.py index 67a406f..375aacb 100644 --- a/python/physical_layer/common.py +++ b/python/physical_layer/common.py @@ -3,7 +3,7 @@ import numpy as np CONST_DTYPE=np.dtype([('points', np.complex64), - ('symbols', np.uint8)]) + ('symbols', np.int32)]) def n_psk(n,x): """n-ary PSK constellation""" diff --git a/python/physical_layer_driver.py b/python/physical_layer_driver.py new file mode 100644 index 0000000..368de65 --- /dev/null +++ b/python/physical_layer_driver.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2018 hcab14@gmail.com. +# +# This 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. +# +# This software 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 software; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import importlib +from gnuradio import blocks +from gnuradio import digital +from gnuradio import filter +from gnuradio import gr +import pmt +import digitalhf +import digitalhf.physical_layer + +class physical_layer_driver(gr.hier_block2): + """ + docstring for block physical_layer_driver + """ + def __init__(self, samp_rate,sps,alpha,mu,nB,nF,nW,description_name,mode): + gr.hier_block2.__init__(self, + "physical_layer_driver", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature(2, 2, gr.sizeof_gr_complex)) # Output signature + + self._sps = sps + self._alpha = alpha + self._mu = mu + self._nB = nB + self._nF = nF + self._nW = nW + + m = importlib.import_module('digitalhf.physical_layer.'+description_name) + self._physical_layer_driver_description = m.PhysicalLayer(sps) + self._physical_layer_driver_description.set_mode(mode) + + ## TODO: get rrc tap information from physical layer description + self._rrc_taps = filter.firdes.root_raised_cosine(1.0, samp_rate, samp_rate/sps, 0.35, 11*sps) + preamble_offset,preamble_samples = self._physical_layer_driver_description.get_preamble_z() + preamble_length = sps*len(self._physical_layer_driver_description.get_preamble()) + self._rrc_filter = filter.fir_filter_ccc(1, (self._rrc_taps)) + self._corr_est = digital.corr_est_cc((preamble_samples.tolist()), sps, preamble_offset, 0.5) + self._doppler_correction = digitalhf.doppler_correction_cc(preamble_length, len(preamble_samples)) + self._adaptive_filter = digitalhf.adaptive_dfe(sps, nB, nF, nW, mu, alpha) + self._msg_proxy = digitalhf.msg_proxy(self._physical_layer_driver_description) + self.connect((self, 0), + (self._rrc_filter, 0), + (self._corr_est, 0), + (self._doppler_correction, 0), + (self._adaptive_filter, 0), + (self, 0)) + self.connect((self._corr_est, 1), ## correlation + (self, 1)) + + self.msg_connect((self._doppler_correction, 'doppler'), (self._msg_proxy, 'doppler')) + self.msg_connect((self._msg_proxy, 'doppler'), (self._doppler_correction, 'doppler')) + + self.msg_connect((self._adaptive_filter, 'frame_info'), (self._msg_proxy, 'frame_info')) + self.msg_connect((self._msg_proxy, 'frame_info'), (self._adaptive_filter, 'frame_info')) + + constellations_data = self._physical_layer_driver_description.get_constellations() + constellations_msg = pmt.to_pmt([{'idx': idx, 'points': c['points'], 'symbols': c['symbols']} + for (idx,c) in enumerate(constellations_data)]) + self._adaptive_filter.to_basic_block()._post(pmt.intern('constellations'), constellations_msg) + + self.message_port_register_hier_out('soft_dec') + self.msg_connect((self._adaptive_filter, 'soft_dec'), (self, 'soft_dec')) + + def set_mu(self, mu): + self._adaptive_filter.set_mu(mu) + + def set_alpha(self, alpha): + self._adaptive_filter.set_alpha(alpha) + + def set_mode(self, mode): + self._physical_layer_driver_description.set_mode(mode) diff --git a/python/qa_doppler_correction_cc.py b/python/qa_doppler_correction_cc.py new file mode 100755 index 0000000..023456f --- /dev/null +++ b/python/qa_doppler_correction_cc.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2018 hcab14@gmail.com. +# +# This 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. +# +# This software 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 software; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +from gnuradio import blocks +import digitalhf.digitalhf_swig as digitalhf + + +class qa_doppler_correction_cc(gr_unittest.TestCase): + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def test_001_t(self): + # set up fg + self.tb.run() + # check data + + +if __name__ == '__main__': + gr_unittest.run(qa_doppler_correction_cc, "qa_doppler_correction_cc.xml") diff --git a/python/qa_msg_proxy.py b/python/qa_msg_proxy.py new file mode 100755 index 0000000..b42c05b --- /dev/null +++ b/python/qa_msg_proxy.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2018 hcab14@gmail.com. +# +# This 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. +# +# This software 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 software; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +from gnuradio import blocks +from physical_layer_driver import physical_layer_driver + + +class qa_msg_proxy(gr_unittest.TestCase): + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def test_001_t(self): + # set up fg + self.tb.run() + # check data + + +if __name__ == '__main__': + gr_unittest.run(qa_msg_proxy, "qa_msg_proxy.xml") diff --git a/python/qa_physical_layer_driver.py b/python/qa_physical_layer_driver.py new file mode 100755 index 0000000..ccc670a --- /dev/null +++ b/python/qa_physical_layer_driver.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2018 hcab14@gmail.com. +# +# This 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. +# +# This software 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 software; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +from gnuradio import blocks +from physical_layer import physical_layer + + +class qa_physical_layer_driver(gr_unittest.TestCase): + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def test_001_t(self): + # set up fg + self.tb.run() + # check data + + +if __name__ == '__main__': + gr_unittest.run(qa_physical_layer_driver, "qa_physical_layer_driver.xml") diff --git a/swig/digitalhf_swig.i b/swig/digitalhf_swig.i index b37c464..cb5c2ad 100644 --- a/swig/digitalhf_swig.i +++ b/swig/digitalhf_swig.i @@ -9,7 +9,10 @@ %{ #include "digitalhf/adaptive_dfe.h" +#include "digitalhf/doppler_correction_cc.h" %} %include "digitalhf/adaptive_dfe.h" GR_SWIG_BLOCK_MAGIC2(digitalhf, adaptive_dfe); +%include "digitalhf/doppler_correction_cc.h" +GR_SWIG_BLOCK_MAGIC2(digitalhf, doppler_correction_cc);