diff --git a/examples/Goliat_Morse_8k.wav b/examples/Goliat_Morse_8k.wav
new file mode 100644
index 0000000..c87ae52
Binary files /dev/null and b/examples/Goliat_Morse_8k.wav differ
diff --git a/examples/morse_decoding_flowgraph.grc b/examples/morse_decoding_flowgraph.grc
index 48c1589..00e9f67 100644
--- a/examples/morse_decoding_flowgraph.grc
+++ b/examples/morse_decoding_flowgraph.grc
@@ -81,69 +81,6 @@
-
- variable_qtgui_range
-
- comment
-
-
-
- value
- 20e3
-
-
- _enabled
- True
-
-
- _coordinate
- (416, 10)
-
-
- gui_hint
-
-
-
- _rotation
- 180
-
-
- id
- act_thrld
-
-
- label
- Activation
-
-
- min_len
- 200
-
-
- orient
- Qt.Horizontal
-
-
- start
- 10e3
-
-
- step
- 100
-
-
- stop
- 80e3
-
-
- rangeType
- float
-
-
- widget
- counter_slider
-
-
variable_qtgui_range
@@ -160,7 +97,7 @@
_coordinate
- (168, 10)
+ (184, 9)
gui_hint
@@ -250,7 +187,7 @@
_coordinate
- (296, 10)
+ (328, 9)
gui_hint
@@ -383,7 +320,7 @@ of a full period of the CW signal with frequency freq.
analog_agc2_xx
attack_rate
- 6.25e-4
+ 6.25e-3
alias
@@ -399,7 +336,7 @@ of a full period of the CW signal with frequency freq.
decay_rate
- 6.25e-4
+ 6.25e-3
_enabled
@@ -501,6 +438,53 @@ of a full period of the CW signal with frequency freq.
8192
+
+ audio_sink
+
+ alias
+
+
+
+ comment
+
+
+
+ affinity
+
+
+
+ device_name
+
+
+
+ _enabled
+ True
+
+
+ _coordinate
+ (960, 188)
+
+
+ _rotation
+ 0
+
+
+ id
+ audio_sink_0
+
+
+ num_inputs
+ 1
+
+
+ ok_to_block
+ True
+
+
+ samp_rate
+ samp_rate
+
+
blocks_add_xx
@@ -1654,7 +1638,11 @@ of a full period of the CW signal with frequency freq.
satnogs_cw_to_symbol
threshold
- act_thrld
+ 20e3
+
+
+ auto_config
+ True
alias
@@ -1784,6 +1772,12 @@ of a full period of the CW signal with frequency freq.
0
0
+
+ blocks_multiply_const_vxx_0
+ audio_sink_0
+ 0
+ 0
+
blocks_multiply_const_vxx_0
blocks_throttle_0
diff --git a/examples/morse_decoding_goliat.grc b/examples/morse_decoding_goliat.grc
new file mode 100644
index 0000000..3644101
--- /dev/null
+++ b/examples/morse_decoding_goliat.grc
@@ -0,0 +1,1772 @@
+
+
+
+ Sun Jan 17 23:03:00 2016
+
+ options
+
+ author
+
+
+
+ window_size
+
+
+
+ category
+ Custom
+
+
+ comment
+
+
+
+ description
+
+
+
+ _enabled
+ True
+
+
+ _coordinate
+ (8, 8)
+
+
+ _rotation
+ 0
+
+
+ generate_options
+ qt_gui
+
+
+ hier_block_src_path
+ .:
+
+
+ id
+ morse_decoding_goliat
+
+
+ max_nouts
+ 0
+
+
+ qt_qss_theme
+
+
+
+ realtime_scheduling
+
+
+
+ run_command
+ {python} -u {filename}
+
+
+ run_options
+ prompt
+
+
+ run
+ True
+
+
+ thread_safe_setters
+
+
+
+ title
+
+
+
+
+ variable_qtgui_range
+
+ comment
+
+
+
+ value
+ 1
+
+
+ _enabled
+ True
+
+
+ _coordinate
+ (184, 9)
+
+
+ gui_hint
+
+
+
+ _rotation
+ 180
+
+
+ id
+ const
+
+
+ label
+ Signal Amplitude
+
+
+ min_len
+ 200
+
+
+ orient
+ Qt.Horizontal
+
+
+ start
+ 0
+
+
+ step
+ 0.01
+
+
+ stop
+ 20
+
+
+ rangeType
+ float
+
+
+ widget
+ counter_slider
+
+
+
+ variable
+
+ comment
+ The frequency of the CW signal
+
+
+ _enabled
+ True
+
+
+ _coordinate
+ (840, 13)
+
+
+ _rotation
+ 0
+
+
+ id
+ freq
+
+
+ value
+ 1200.0
+
+
+
+ variable_qtgui_range
+
+ comment
+
+
+
+ value
+ 0.4
+
+
+ _enabled
+ True
+
+
+ _coordinate
+ (328, 9)
+
+
+ gui_hint
+
+
+
+ _rotation
+ 180
+
+
+ id
+ noise_ratio
+
+
+ label
+ Noise Ratio
+
+
+ min_len
+ 200
+
+
+ orient
+ Qt.Horizontal
+
+
+ start
+ 0
+
+
+ step
+ 0.01
+
+
+ stop
+ 1
+
+
+ rangeType
+ float
+
+
+ widget
+ counter_slider
+
+
+
+ variable
+
+ comment
+ WAV or input sampling rate
+
+
+ _enabled
+ True
+
+
+ _coordinate
+ (1016, 13)
+
+
+ _rotation
+ 0
+
+
+ id
+ samp_rate
+
+
+ value
+ 8000
+
+
+
+ variable
+
+ comment
+ The number of taps depends on the number of samples
+of a full period of the CW signal with frequency freq.
+
+
+ _enabled
+ True
+
+
+ _coordinate
+ (552, 13)
+
+
+ _rotation
+ 0
+
+
+ id
+ taps
+
+
+ value
+ int(math.ceil(samp_rate/freq))
+
+
+
+ variable
+
+ comment
+ Words Per Minute. ITU standard specifies it to 20.
+
+
+ _enabled
+ True
+
+
+ _coordinate
+ (552, 117)
+
+
+ _rotation
+ 0
+
+
+ id
+ wpm
+
+
+ value
+ 20
+
+
+
+ analog_agc2_xx
+
+ attack_rate
+ 6.25e-3
+
+
+ alias
+
+
+
+ comment
+
+
+
+ affinity
+
+
+
+ decay_rate
+ 6.25e-3
+
+
+ _enabled
+ 1
+
+
+ _coordinate
+ (592, 360)
+
+
+ _rotation
+ 0
+
+
+ gain
+ 0.0
+
+
+ id
+ analog_agc2_xx_0_0
+
+
+ max_gain
+ 65536
+
+
+ maxoutbuf
+ 0
+
+
+ minoutbuf
+ 0
+
+
+ reference
+ 0.3
+
+
+ type
+ float
+
+
+
+ analog_fastnoise_source_x
+
+ amp
+ noise_ratio
+
+
+ alias
+
+
+
+ comment
+
+
+
+ affinity
+
+
+
+ _enabled
+ 1
+
+
+ _coordinate
+ (24, 423)
+
+
+ _rotation
+ 0
+
+
+ id
+ analog_fastnoise_source_x_0
+
+
+ maxoutbuf
+ 0
+
+
+ minoutbuf
+ 0
+
+
+ noise_type
+ analog.GR_GAUSSIAN
+
+
+ type
+ float
+
+
+ seed
+ 0
+
+
+ samples
+ 8192
+
+
+
+ audio_sink
+
+ alias
+
+
+
+ comment
+
+
+
+ affinity
+
+
+
+ device_name
+
+
+
+ _enabled
+ 0
+
+
+ _coordinate
+ (1152, 276)
+
+
+ _rotation
+ 0
+
+
+ id
+ audio_sink_0
+
+
+ num_inputs
+ 1
+
+
+ ok_to_block
+ True
+
+
+ samp_rate
+ samp_rate
+
+
+
+ blocks_add_xx
+
+ alias
+
+
+
+ comment
+
+
+
+ affinity
+
+
+
+ _enabled
+ 1
+
+
+ _coordinate
+ (192, 328)
+
+
+ _rotation
+ 0
+
+
+ id
+ blocks_add_xx_0
+
+
+ type
+ float
+
+
+ maxoutbuf
+ 0
+
+
+ minoutbuf
+ 0
+
+
+ num_inputs
+ 2
+
+
+ vlen
+ 1
+
+
+
+ blocks_moving_average_xx
+
+ alias
+
+
+
+ comment
+
+
+
+ affinity
+
+
+
+ _enabled
+ True
+
+
+ _coordinate
+ (1000, 438)
+
+
+ _rotation
+ 180
+
+
+ id
+ blocks_moving_average_xx_0
+
+
+ length
+ taps
+
+
+ max_iter
+ 4000
+
+
+ maxoutbuf
+ 0
+
+
+ minoutbuf
+ 0
+
+
+ scale
+ 1
+
+
+ type
+ float
+
+
+
+ blocks_multiply_const_vxx
+
+ alias
+
+
+
+ comment
+
+
+
+ const
+ const
+
+
+ affinity
+
+
+
+ _enabled
+ True
+
+
+ _coordinate
+ (248, 236)
+
+
+ _rotation
+ 0
+
+
+ id
+ blocks_multiply_const_vxx_0_0
+
+
+ type
+ float
+
+
+ maxoutbuf
+ 0
+
+
+ minoutbuf
+ 0
+
+
+ vlen
+ 1
+
+
+
+ blocks_throttle
+
+ alias
+
+
+
+ comment
+
+
+
+ affinity
+
+
+
+ _enabled
+ True
+
+
+ _coordinate
+ (432, 388)
+
+
+ _rotation
+ 0
+
+
+ id
+ blocks_throttle_0
+
+
+ ignoretag
+ True
+
+
+ maxoutbuf
+ 0
+
+
+ minoutbuf
+ 0
+
+
+ samples_per_second
+ samp_rate
+
+
+ type
+ float
+
+
+ vlen
+ 1
+
+
+
+ blocks_wavfile_source
+
+ alias
+
+
+
+ comment
+
+
+
+ affinity
+
+
+
+ _enabled
+ True
+
+
+ file
+ /home/surligas/workspace/gr-satnogs/examples/Goliat_Morse_8k.wav
+
+
+ _coordinate
+ (8, 229)
+
+
+ _rotation
+ 0
+
+
+ id
+ blocks_wavfile_source_0
+
+
+ maxoutbuf
+ 0
+
+
+ minoutbuf
+ 0
+
+
+ nchan
+ 1
+
+
+ repeat
+ True
+
+
+
+ import
+
+ alias
+
+
+
+ comment
+
+
+
+ _enabled
+ True
+
+
+ _coordinate
+ (8, 132)
+
+
+ _rotation
+ 0
+
+
+ id
+ import_0
+
+
+ import
+ import math
+
+
+
+ qtgui_time_sink_x
+
+ autoscale
+ False
+
+
+ alias
+
+
+
+ comment
+
+
+
+ ctrlpanel
+ True
+
+
+ affinity
+
+
+
+ entags
+ True
+
+
+ _enabled
+ True
+
+
+ _coordinate
+ (712, 510)
+
+
+ gui_hint
+
+
+
+ _rotation
+ 180
+
+
+ grid
+ False
+
+
+ id
+ qtgui_time_sink_x_0
+
+
+ legend
+ True
+
+
+ alpha1
+ 1.0
+
+
+ color1
+ "blue"
+
+
+ label1
+ Matched filter Energy
+
+
+ marker1
+ -1
+
+
+ style1
+ 1
+
+
+ width1
+ 1
+
+
+ alpha10
+ 1.0
+
+
+ color10
+ "blue"
+
+
+ label10
+
+
+
+ marker10
+ -1
+
+
+ style10
+ 1
+
+
+ width10
+ 1
+
+
+ alpha2
+ 1.0
+
+
+ color2
+ "red"
+
+
+ label2
+ Without AGC
+
+
+ marker2
+ -1
+
+
+ style2
+ 1
+
+
+ width2
+ 1
+
+
+ alpha3
+ 1.0
+
+
+ color3
+ "green"
+
+
+ label3
+
+
+
+ marker3
+ -1
+
+
+ style3
+ 1
+
+
+ width3
+ 1
+
+
+ alpha4
+ 1.0
+
+
+ color4
+ "black"
+
+
+ label4
+
+
+
+ marker4
+ -1
+
+
+ style4
+ 1
+
+
+ width4
+ 1
+
+
+ alpha5
+ 1.0
+
+
+ color5
+ "cyan"
+
+
+ label5
+
+
+
+ marker5
+ -1
+
+
+ style5
+ 1
+
+
+ width5
+ 1
+
+
+ alpha6
+ 1.0
+
+
+ color6
+ "magenta"
+
+
+ label6
+
+
+
+ marker6
+ -1
+
+
+ style6
+ 1
+
+
+ width6
+ 1
+
+
+ alpha7
+ 1.0
+
+
+ color7
+ "yellow"
+
+
+ label7
+
+
+
+ marker7
+ -1
+
+
+ style7
+ 1
+
+
+ width7
+ 1
+
+
+ alpha8
+ 1.0
+
+
+ color8
+ "dark red"
+
+
+ label8
+
+
+
+ marker8
+ -1
+
+
+ style8
+ 1
+
+
+ width8
+ 1
+
+
+ alpha9
+ 1.0
+
+
+ color9
+ "dark green"
+
+
+ label9
+
+
+
+ marker9
+ -1
+
+
+ style9
+ 1
+
+
+ width9
+ 1
+
+
+ name
+ ""
+
+
+ nconnections
+ 1
+
+
+ size
+ 1024
+
+
+ srate
+ samp_rate
+
+
+ tr_chan
+ 0
+
+
+ tr_delay
+ 0
+
+
+ tr_level
+ 0.0
+
+
+ tr_mode
+ qtgui.TRIG_MODE_FREE
+
+
+ tr_slope
+ qtgui.TRIG_SLOPE_POS
+
+
+ tr_tag
+ ""
+
+
+ type
+ float
+
+
+ update_time
+ 0.10
+
+
+ ylabel
+ Amplitude
+
+
+ yunit
+ ""
+
+
+ ymax
+ 150000
+
+
+ ymin
+ 0
+
+
+
+ qtgui_time_sink_x
+
+ autoscale
+ False
+
+
+ alias
+
+
+
+ comment
+
+
+
+ ctrlpanel
+ True
+
+
+ affinity
+
+
+
+ entags
+ True
+
+
+ _enabled
+ True
+
+
+ _coordinate
+ (632, 208)
+
+
+ gui_hint
+
+
+
+ _rotation
+ 0
+
+
+ grid
+ True
+
+
+ id
+ qtgui_time_sink_x_0_0
+
+
+ legend
+ True
+
+
+ alpha1
+ 1.0
+
+
+ color1
+ "blue"
+
+
+ label1
+ Input Signal
+
+
+ marker1
+ -1
+
+
+ style1
+ 1
+
+
+ width1
+ 1
+
+
+ alpha10
+ 1.0
+
+
+ color10
+ "blue"
+
+
+ label10
+
+
+
+ marker10
+ -1
+
+
+ style10
+ 1
+
+
+ width10
+ 1
+
+
+ alpha2
+ 1.0
+
+
+ color2
+ "red"
+
+
+ label2
+ Input Signal + Noise
+
+
+ marker2
+ -1
+
+
+ style2
+ 1
+
+
+ width2
+ 1
+
+
+ alpha3
+ 1.0
+
+
+ color3
+ "green"
+
+
+ label3
+ AGC Signal
+
+
+ marker3
+ -1
+
+
+ style3
+ 1
+
+
+ width3
+ 1
+
+
+ alpha4
+ 1.0
+
+
+ color4
+ "black"
+
+
+ label4
+
+
+
+ marker4
+ -1
+
+
+ style4
+ 1
+
+
+ width4
+ 1
+
+
+ alpha5
+ 1.0
+
+
+ color5
+ "cyan"
+
+
+ label5
+
+
+
+ marker5
+ -1
+
+
+ style5
+ 1
+
+
+ width5
+ 1
+
+
+ alpha6
+ 1.0
+
+
+ color6
+ "magenta"
+
+
+ label6
+
+
+
+ marker6
+ -1
+
+
+ style6
+ 1
+
+
+ width6
+ 1
+
+
+ alpha7
+ 1.0
+
+
+ color7
+ "yellow"
+
+
+ label7
+
+
+
+ marker7
+ -1
+
+
+ style7
+ 1
+
+
+ width7
+ 1
+
+
+ alpha8
+ 1.0
+
+
+ color8
+ "dark red"
+
+
+ label8
+
+
+
+ marker8
+ -1
+
+
+ style8
+ 1
+
+
+ width8
+ 1
+
+
+ alpha9
+ 1.0
+
+
+ color9
+ "dark green"
+
+
+ label9
+
+
+
+ marker9
+ -1
+
+
+ style9
+ 1
+
+
+ width9
+ 1
+
+
+ name
+ ""
+
+
+ nconnections
+ 3
+
+
+ size
+ 1024
+
+
+ srate
+ samp_rate
+
+
+ tr_chan
+ 0
+
+
+ tr_delay
+ 0
+
+
+ tr_level
+ 0.0
+
+
+ tr_mode
+ qtgui.TRIG_MODE_FREE
+
+
+ tr_slope
+ qtgui.TRIG_SLOPE_POS
+
+
+ tr_tag
+ ""
+
+
+ type
+ float
+
+
+ update_time
+ 0.10
+
+
+ ylabel
+ Amplitude
+
+
+ yunit
+ ""
+
+
+ ymax
+ 10
+
+
+ ymin
+ -10
+
+
+
+ satnogs_clear_text_msg_sink
+
+ alias
+
+
+
+ comment
+
+
+
+ affinity
+
+
+
+ _enabled
+ True
+
+
+ _coordinate
+ (984, 584)
+
+
+ _rotation
+ 0
+
+
+ id
+ satnogs_clear_text_msg_sink_0
+
+
+
+ satnogs_cw_matched_filter_ff
+
+ carrier_freq
+ freq
+
+
+ alias
+
+
+
+ comment
+
+
+
+ energy
+ True
+
+
+ affinity
+
+
+
+ _enabled
+ 1
+
+
+ _coordinate
+ (984, 335)
+
+
+ _rotation
+ 0
+
+
+ id
+ satnogs_cw_matched_filter_ff_0
+
+
+ maxoutbuf
+ 0
+
+
+ minoutbuf
+ 0
+
+
+ sampling_rate
+ samp_rate
+
+
+ wpm
+ wpm
+
+
+
+ satnogs_cw_to_symbol
+
+ threshold
+ 20e3
+
+
+ auto_config
+ True
+
+
+ alias
+
+
+
+ comment
+
+
+
+ conf_level
+ 0.85
+
+
+ affinity
+
+
+
+ _enabled
+ True
+
+
+ _coordinate
+ (272, 559)
+
+
+ _rotation
+ 0
+
+
+ id
+ satnogs_cw_to_symbol_0
+
+
+ maxoutbuf
+ 0
+
+
+ minoutbuf
+ 0
+
+
+ sampling_rate
+ samp_rate
+
+
+ wpm
+ 20
+
+
+
+ satnogs_morse_decoder
+
+ alias
+
+
+
+ comment
+
+
+
+ affinity
+
+
+
+ _enabled
+ True
+
+
+ _coordinate
+ (488, 580)
+
+
+ _rotation
+ 0
+
+
+ id
+ satnogs_morse_decoder_0
+
+
+ maxoutbuf
+ 0
+
+
+ minoutbuf
+ 0
+
+
+ unrecognized_char
+ ord('#')
+
+
+
+ analog_agc2_xx_0_0
+ qtgui_time_sink_x_0_0
+ 0
+ 2
+
+
+ analog_agc2_xx_0_0
+ satnogs_cw_matched_filter_ff_0
+ 0
+ 0
+
+
+ analog_fastnoise_source_x_0
+ blocks_add_xx_0
+ 0
+ 1
+
+
+ blocks_add_xx_0
+ blocks_throttle_0
+ 0
+ 0
+
+
+ blocks_add_xx_0
+ qtgui_time_sink_x_0_0
+ 0
+ 1
+
+
+ blocks_moving_average_xx_0
+ qtgui_time_sink_x_0
+ 0
+ 0
+
+
+ blocks_moving_average_xx_0
+ satnogs_cw_to_symbol_0
+ 0
+ 0
+
+
+ blocks_multiply_const_vxx_0_0
+ blocks_add_xx_0
+ 0
+ 0
+
+
+ blocks_multiply_const_vxx_0_0
+ qtgui_time_sink_x_0_0
+ 0
+ 0
+
+
+ blocks_throttle_0
+ analog_agc2_xx_0_0
+ 0
+ 0
+
+
+ blocks_wavfile_source_0
+ blocks_multiply_const_vxx_0_0
+ 0
+ 0
+
+
+ satnogs_cw_matched_filter_ff_0
+ blocks_moving_average_xx_0
+ 0
+ 0
+
+
+ satnogs_cw_to_symbol_0
+ satnogs_morse_decoder_0
+ out
+ in
+
+
+ satnogs_morse_decoder_0
+ satnogs_clear_text_msg_sink_0
+ out
+ in
+
+
diff --git a/examples/test_morse_decoder.grc b/examples/test_morse_decoder.grc
index 90b0191..a07e07e 100644
--- a/examples/test_morse_decoder.grc
+++ b/examples/test_morse_decoder.grc
@@ -187,7 +187,7 @@
debug_seq
- "HELLO EARTH WORLD 123456789"
+ "XPGOLIAT HELLO EARTH WORLD 123456789"
diff --git a/grc/satnogs_cw_to_symbol.xml b/grc/satnogs_cw_to_symbol.xml
index fc4a26d..a93f997 100644
--- a/grc/satnogs_cw_to_symbol.xml
+++ b/grc/satnogs_cw_to_symbol.xml
@@ -4,7 +4,7 @@
satnogs_cw_to_symbol
satnogs
import satnogs
- satnogs.cw_to_symbol($sampling_rate, $threshold, $conf_level, $wpm)
+ satnogs.cw_to_symbol($sampling_rate, $threshold, $conf_level, $wpm, $auto_config)
set_act_threshold($threshold)
@@ -33,6 +33,20 @@
20
int
+
+
+ Automatic timing recovery
+ auto_config
+ enum
+
+
+
act_threshold
@@ -40,6 +54,12 @@
1
+
+ sync
+ message
+ 1
+
+
in
float
diff --git a/include/satnogs/CMakeLists.txt b/include/satnogs/CMakeLists.txt
index 5404f81..813c2ca 100644
--- a/include/satnogs/CMakeLists.txt
+++ b/include/satnogs/CMakeLists.txt
@@ -32,5 +32,6 @@ install(FILES
clear_text_msg_sink.h
cw_to_symbol.h
afsk_decoder.h
- sine_matched_filter_ff.h DESTINATION include/satnogs
+ sine_matched_filter_ff.h
+ utils.h DESTINATION include/satnogs
)
diff --git a/include/satnogs/cw_to_symbol.h b/include/satnogs/cw_to_symbol.h
index 81eb47d..200ff28 100644
--- a/include/satnogs/cw_to_symbol.h
+++ b/include/satnogs/cw_to_symbol.h
@@ -56,12 +56,20 @@ namespace gr {
* conservative, whereas lower may help in noisy environments but may
* trigger false alarms too, especially for the case of short pauses
* symbols
+ *
* @param wpm Morse code Words per Minute
+ *
+ * @param auto_config if set to true, the block will try first to
+ * automatically adjust the WPM in order to extract the dot and dash
+ * durations. If set to false, the given WPM is used.
*/
- static sptr make(double sampling_rate, float threshold,
- float conf_level = 0.9, size_t wpm = 20);
+ static cw_to_symbol::sptr
+ make (double sampling_rate, float threshold, float conf_level = 0.9,
+ size_t wpm = 20, bool auto_config = false);
virtual void set_act_threshold(float thrld) = 0;
+
+ virtual void start_timing_recovery() = 0;
};
} // namespace satnogs
diff --git a/include/satnogs/morse.h b/include/satnogs/morse.h
index 18dff3d..e5e3044 100644
--- a/include/satnogs/morse.h
+++ b/include/satnogs/morse.h
@@ -21,6 +21,8 @@
#ifndef INCLUDE_SATNOGS_MORSE_H_
#define INCLUDE_SATNOGS_MORSE_H_
+#define MIN_WPM 5
+#define MAX_WPM 30
/**
* The different Morse symbols
*/
diff --git a/include/satnogs/utils.h b/include/satnogs/utils.h
new file mode 100644
index 0000000..06568de
--- /dev/null
+++ b/include/satnogs/utils.h
@@ -0,0 +1,50 @@
+/* -*- c++ -*- */
+/*
+ * gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
+ *
+ * Copyright (C) 2016, Libre Space Foundation
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef INCLUDE_SATNOGS_UTILS_H_
+#define INCLUDE_SATNOGS_UTILS_H_
+
+
+namespace gr
+{
+
+namespace satnogs
+{
+
+/**
+ * Computes the Mean Absolute Percentage Error
+ * @param ref the reference value
+ * @param estimation the estimated value
+ * @return the mean absolute percentage error
+ */
+static inline double
+mape(double ref, double estimation)
+{
+ return std::abs(ref - estimation) / ref;
+}
+
+} // namespace satnogs
+
+
+} // namespace gr
+
+
+
+#endif /* INCLUDE_SATNOGS_UTILS_H_ */
diff --git a/lib/cw_to_symbol_impl.cc b/lib/cw_to_symbol_impl.cc
index 8c5ce25..54be3dc 100644
--- a/lib/cw_to_symbol_impl.cc
+++ b/lib/cw_to_symbol_impl.cc
@@ -22,8 +22,10 @@
#include "config.h"
#endif
+#include
#include
#include
+#include
#include "cw_to_symbol_impl.h"
namespace gr
@@ -33,23 +35,27 @@ namespace gr
cw_to_symbol::sptr
cw_to_symbol::make (double sampling_rate, float threshold,
- float conf_level, size_t wpm)
+ float conf_level, size_t wpm, bool auto_config)
{
return gnuradio::get_initial_sptr (
- new cw_to_symbol_impl (sampling_rate, threshold, conf_level, wpm));
+ new cw_to_symbol_impl (sampling_rate, threshold, conf_level,
+ wpm, auto_config));
}
/*
* The private constructor
*/
cw_to_symbol_impl::cw_to_symbol_impl (double sampling_rate, float threshold,
- float conf_level, size_t wpm) :
+ float conf_level, size_t wpm,
+ bool auto_config) :
gr::sync_block ("cw_to_symbol",
gr::io_signature::make (1, 1, sizeof(float)),
gr::io_signature::make (0, 0, 0)),
d_sampling_rate(sampling_rate),
d_act_thrshld(threshold),
d_confidence_level(conf_level),
+ d_sync_limit(15),
+ d_auto_sync(auto_config),
d_dot_samples((1.2/wpm) / (1.0 / sampling_rate)),
d_dash_samples(3 * d_dot_samples),
d_short_pause_samples(3 * d_dot_samples),
@@ -57,13 +63,27 @@ namespace gr
d_state(IDLE),
d_state_cnt(0),
d_pause_cnt(0),
- d_seq_started(false)
+ d_est_cnt(0),
+ d_mean_cnt(0),
+ d_have_sync(!auto_config),
+ d_seq_started(false),
+ d_sync_state(SYNC_TRIGGER_OFF)
{
message_port_register_in(pmt::mp("act_threshold"));
+ message_port_register_in(pmt::mp("sync"));
message_port_register_out(pmt::mp("out"));
+
+ /* Register the message handlers */
set_msg_handler(pmt::mp("act_threshold"),
boost::bind(&cw_to_symbol_impl::set_act_threshold_msg_handler,
this, _1));
+ set_msg_handler(pmt::mp("sync"),
+ boost::bind(&cw_to_symbol_impl::sync_msg_handler,
+ this, _1));
+
+ if( auto_config ){
+ d_dot_samples = (1.2/MIN_WPM) / (1.0 / d_sampling_rate);
+ }
}
inline void
@@ -123,6 +143,16 @@ namespace gr
}
}
+ void
+ cw_to_symbol_impl::sync_msg_handler (pmt::pmt_t msg)
+ {
+ if(pmt::is_pair(msg)){
+ if(pmt::to_bool(pmt::cdr(msg))){
+ start_timing_recovery();
+ }
+ }
+ }
+
int
cw_to_symbol_impl::work (int noutput_items,
gr_vector_const_void_star &input_items,
@@ -132,6 +162,41 @@ namespace gr
float conf_lvl;
const float *in = (const float *) input_items[0];
+ /* The estimation for the WPM has not yet been computed */
+ if(!d_have_sync){
+ for(i = 0; i < noutput_items; i++) {
+ switch (d_sync_state) {
+ case SYNC_TRIGGER_OFF:
+ if(in[i] > d_act_thrshld) {
+ d_sync_state = SYNC_TRIGGER_ON;
+ }
+ break;
+ case SYNC_TRIGGER_ON:
+ if(in[i] > d_act_thrshld) {
+ d_state_cnt++;
+ }
+ else {
+ /* Trigger is OFF. Extract the best timing information available */
+ d_sync_state = SYNC_TRIGGER_OFF;
+ estimate_dot_duration(d_state_cnt);
+ d_state_cnt = 0;
+
+ /*
+ * If the sync process was over set the state to idle and
+ * return for proper decoding
+ */
+ if(d_have_sync) {
+ set_idle();
+ return i+1;
+ }
+ }
+ break;
+ }
+ }
+
+ return noutput_items;
+ }
+
for(i = 0; i < noutput_items; i++) {
switch(d_state){
case IDLE:
@@ -248,10 +313,106 @@ namespace gr
return noutput_items;
}
+ void
+ cw_to_symbol_impl::estimate_dot_duration (size_t estimate)
+ {
+ /*
+ * The estimations should be in a logical range of
+ * [MIN_WPM - MAX_WPM] WPM. Otherwise it is considered a false alarm.
+ */
+ if( estimate < (1.2/MAX_WPM) / (1.0 / d_sampling_rate)
+ || estimate >= (1.2/MIN_WPM) / (1.0 / d_sampling_rate)) {
+ return;
+ }
+
+ /*
+ * Keep the minimum dot duration. This is only for the sync process.
+ * At the end the resulting dot duration will be a mean value of
+ * possible dot durations
+ */
+ if(estimate < d_dot_samples) {
+ d_dot_samples = estimate;
+ d_mean_cnt += estimate;
+ d_est_cnt++;
+ }
+ else if(mape(d_dot_samples, estimate) < 1 - d_confidence_level) {
+ d_mean_cnt += estimate;
+ d_est_cnt++;
+ }
+ /*
+ * Perhaps the synchronization process was triggered by a dash.
+ * Check this possibility and use MAPE and confidence level to
+ * decide or not to take into consideration the new estimation
+ */
+ else if ( mape(d_dot_samples, estimate / 3.0) < 1 - d_confidence_level ) {
+ d_mean_cnt += estimate / 3.0;
+ d_est_cnt++;
+ }
+
+ /*
+ * If the synchronization process is over update the dot duration
+ * with an average estimation
+ */
+ if(d_est_cnt == d_sync_limit) {
+ d_have_sync = true;
+ d_dot_samples = d_mean_cnt / d_sync_limit;
+ set_symbols_duration();
+ std::cout << "Have sync" << d_dot_samples << std::endl;
+ }
+ }
+
+ /**
+ * Sets the dash, short and long pause durations based on the dot estimated
+ * duration.
+ */
+ void
+ cw_to_symbol_impl::set_symbols_duration ()
+ {
+ d_dash_samples = 3 * d_dot_samples;
+ d_short_pause_samples = d_dash_samples;
+ d_long_pause_samples = 7 * d_dot_samples;
+ }
+
+ /**
+ * Resets the estimated dot duration. After the call of this method
+ * the caller MUST initiate a timing recovery procedure.
+ */
+ void
+ cw_to_symbol_impl::reset_sync ()
+ {
+ d_sync_state = SYNC_TRIGGER_OFF;
+ d_est_cnt = 0;
+ set_idle ();
+ d_have_sync = false;
+ d_dot_samples = (1.2 / MIN_WPM) / (1.0 / d_sampling_rate);
+ }
+
+ /**
+ * Sets a new activation threshold. If auto timing recovery is enabled,
+ * the process of finding automatically the WPM is initiated after the
+ * configuration of the new threshold.
+ * @param thrhld the new threshold.
+ */
void
cw_to_symbol_impl::set_act_threshold (float thrhld)
{
d_act_thrshld = thrhld;
+ /* When a new activation threshold is set,
+ * if the automatic synchronization is set it should be again performed
+ */
+ if(d_auto_sync) {
+ reset_sync();
+ }
+ }
+
+ /**
+ * Starts the timing recovery process fro automatically retrieving the best
+ * WPM estimation.
+ */
+ void
+ cw_to_symbol_impl::start_timing_recovery ()
+ {
+ reset_sync();
}
} /* namespace satnogs */
diff --git a/lib/cw_to_symbol_impl.h b/lib/cw_to_symbol_impl.h
index 2f0a89b..5ed409c 100644
--- a/lib/cw_to_symbol_impl.h
+++ b/lib/cw_to_symbol_impl.h
@@ -38,18 +38,33 @@ namespace gr
SHORT_OFF_PERIOD,
LONG_OFF_PERIOD
} cw_state_t;
+
+ /**
+ * Different states during the WPM auto synchronization
+ */
+ typedef enum {
+ SYNC_TRIGGER_OFF,//!< SYNC_TRIGGER_OFF Signal is below threshold
+ SYNC_TRIGGER_ON //!< SYNC_TRIGGER_ON Signal is above threshold
+ } sync_state_t;
+
private:
const double d_sampling_rate;
float d_act_thrshld;
const float d_confidence_level;
- const size_t d_dot_samples;
- const size_t d_dash_samples;
- const size_t d_short_pause_samples;
- const size_t d_long_pause_samples;
+ const size_t d_sync_limit;
+ const bool d_auto_sync;
+ size_t d_dot_samples;
+ size_t d_dash_samples;
+ size_t d_short_pause_samples;
+ size_t d_long_pause_samples;
cw_state_t d_state;
size_t d_state_cnt;
size_t d_pause_cnt;
+ size_t d_est_cnt;
+ size_t d_mean_cnt;
+ bool d_have_sync;
bool d_seq_started;
+ sync_state_t d_sync_state;
inline void
set_idle ();
@@ -71,9 +86,17 @@ namespace gr
void set_act_threshold_msg_handler(pmt::pmt_t msg);
+ void sync_msg_handler(pmt::pmt_t msg);
+
+ void estimate_dot_duration(size_t estimate);
+
+ void set_symbols_duration();
+
+ void reset_sync();
+
public:
cw_to_symbol_impl (double sampling_rate, float threshold,
- float conf_level, size_t wpm);
+ float conf_level, size_t wpm, bool auto_config);
~cw_to_symbol_impl ();
// Where all the action really happens
@@ -82,6 +105,8 @@ namespace gr
gr_vector_void_star &output_items);
void set_act_threshold(float thrhld);
+
+ void start_timing_recovery();
};
} // namespace satnogs