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