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
+
+
+
+
+
+
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
+
+
+
+
+
+
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
+
+
+
+
+
+
+
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);