From 67ea02a248ff77478dc8712221bdeab07a762200 Mon Sep 17 00:00:00 2001 From: Manolis Surligas Date: Sun, 9 Dec 2018 00:57:15 +0200 Subject: [PATCH] Improve CW decoding * Introduce the hysteresis option, in order the CW demodulator to adjust properly the plateau length based on the WPM and any filtering that can be used before * Instead for a frame per word, now the CW decoder waits for 10 long spaces before it commits a frame. With this way many words are placed on the same frame telemetry decoding is easier --- grc/satnogs_cw_to_symbol.xml | 95 +++--- include/satnogs/cw_to_symbol.h | 14 +- include/satnogs/morse.h | 7 +- lib/cw_to_symbol_impl.cc | 572 +++++++++++++++++---------------- lib/cw_to_symbol_impl.h | 3 +- lib/morse_decoder_impl.cc | 165 +++++----- lib/morse_decoder_impl.h | 29 +- 7 files changed, 466 insertions(+), 419 deletions(-) diff --git a/grc/satnogs_cw_to_symbol.xml b/grc/satnogs_cw_to_symbol.xml index 984c241..89ea992 100644 --- a/grc/satnogs_cw_to_symbol.xml +++ b/grc/satnogs_cw_to_symbol.xml @@ -1,51 +1,58 @@ - CW to Symbols - satnogs_cw_to_symbol - import satnogs - satnogs.cw_to_symbol($sampling_rate, $threshold, $conf_level, $wpm) - set_act_threshold($threshold) - - - Sampling Rate - sampling_rate - samp_rate - real - + CW to Symbols + satnogs_cw_to_symbol + import satnogs + satnogs.cw_to_symbol($sampling_rate, $threshold, $conf_level, $wpm, $hysteresis) + set_act_threshold($threshold) - - Activation Threshold - threshold - real - - - - Confidence Level - conf_level - 0.9 - real - + + Sampling Rate + sampling_rate + samp_rate + real + - - Words per Minute - wpm - 20 - int - + + Activation Threshold + threshold + real + - - act_threshold - message - 1 - - - - in - float - + + Confidence Level + conf_level + 0.9 + real + - - out - message - + + Words per Minute + wpm + 20 + int + + + + Hysteresis + hysteresis + 0 + int + + + + act_threshold + message + 1 + + + + in + float + + + + out + message + diff --git a/include/satnogs/cw_to_symbol.h b/include/satnogs/cw_to_symbol.h index acf5aef..6a05bbf 100644 --- a/include/satnogs/cw_to_symbol.h +++ b/include/satnogs/cw_to_symbol.h @@ -24,6 +24,9 @@ #include #include +#define MIN_WPM 5 +#define MAX_WPM 50 + namespace gr { namespace satnogs @@ -65,10 +68,19 @@ namespace gr * symbols * * @param wpm Morse code Words per Minute + * + * @param hysteresis this value represents the hysteresis of a possible + * filter used before the CW to Symbol block. For example if there is + * a moving average filter with x taps, the full power of the signal + * will be available after x samples. The total length of samples with + * maximum power will be 2*x less. Because the block searches for plateaus + * with proper durations, this filtering hysteresis should be known. + * If no such a filter is used, the hysteresis value should be set to zero. */ static cw_to_symbol::sptr make (double sampling_rate, float threshold, float conf_level = 0.9, - size_t wpm = 20); + size_t wpm = 20, + size_t hysteresis = 0); virtual void set_act_threshold (float thrld) = 0; diff --git a/include/satnogs/morse.h b/include/satnogs/morse.h index af643b5..b7e96cf 100644 --- a/include/satnogs/morse.h +++ b/include/satnogs/morse.h @@ -2,7 +2,7 @@ /* * gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module * - * Copyright (C) 2016,2017 + * Copyright (C) 2016-2018 * Libre Space Foundation * * This program is free software: you can redistribute it and/or modify @@ -22,8 +22,6 @@ #ifndef INCLUDE_SATNOGS_MORSE_H_ #define INCLUDE_SATNOGS_MORSE_H_ -#define MIN_WPM 5 -#define MAX_WPM 30 /** * The different Morse symbols */ @@ -32,7 +30,8 @@ typedef enum { MORSE_DASH, //!< Morse dash (-) symbol MORSE_INTRA_SPACE, //!< Space between dot and dash symbols MORSE_S_SPACE, //!< Morse short space between characters - MORSE_L_SPACE //! #include #if BOOST_VERSION >= 106500 - #include +#include #else - #include +#include #endif #include namespace gr { - namespace satnogs - { +namespace satnogs +{ - cw_to_symbol::sptr - cw_to_symbol::make (double sampling_rate, float threshold, float conf_level, - size_t wpm) - { - return gnuradio::get_initial_sptr ( - new cw_to_symbol_impl (sampling_rate, threshold, conf_level, wpm)); - } +cw_to_symbol::sptr +cw_to_symbol::make (double sampling_rate, float threshold, float conf_level, + size_t wpm, size_t hysteresis) +{ + return gnuradio::get_initial_sptr ( + new cw_to_symbol_impl (sampling_rate, threshold, conf_level, wpm, + hysteresis)); +} - /* - * The private constructor - */ - cw_to_symbol_impl::cw_to_symbol_impl (double sampling_rate, float threshold, - float conf_level, size_t wpm) : - 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_dot_samples ((1.2 / wpm) * sampling_rate), - d_window_size(0), - d_window_cnt(0), - d_dot_windows_num(0), - d_dec_state (NO_SYNC), - d_prev_space_symbol (true) - { - if (wpm < MIN_WPM) { - throw std::invalid_argument ( - "Decoder can not handle such low WPM setting"); - } +/* + * The private constructor + */ +cw_to_symbol_impl::cw_to_symbol_impl (double sampling_rate, float threshold, + float conf_level, size_t wpm, + size_t hysteresis) : + 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_dot_samples ((1.2 / wpm) * sampling_rate), + d_window_size (0), + d_window_cnt (0), + d_idle_cnt (0), + d_dot_windows_num (0), + d_dec_state (NO_SYNC), + d_prev_space_symbol (true) +{ + if (wpm < MIN_WPM) { + throw std::invalid_argument ("Decoder can not handle such low WPM setting"); + } - if (wpm > MAX_WPM) { - throw std::invalid_argument ( - "Decoder can not handle such high WPM setting"); - } + if (wpm > MAX_WPM) { + throw std::invalid_argument ( + "Decoder can not handle such high WPM setting"); + } - if (conf_level > 1.0 || conf_level < 0.5) { - throw std::invalid_argument ( - "Confidence level should be in the range [0.5, 1.0]"); - } + if (conf_level > 1.0 || conf_level < 0.5) { + throw std::invalid_argument ( + "Confidence level should be in the range [0.5, 1.0]"); + } - message_port_register_in (pmt::mp ("act_threshold")); - message_port_register_out (pmt::mp ("out")); + if(hysteresis > d_dot_samples / 4) { + throw std::invalid_argument ("Too large hysteresis value"); + } - /* Register the message handlers */ - set_msg_handler ( - pmt::mp ("act_threshold"), - boost::bind (&cw_to_symbol_impl::set_act_threshold_msg_handler, this, - _1)); + message_port_register_in (pmt::mp ("act_threshold")); + message_port_register_out (pmt::mp ("out")); - /* - * Try to split the CW pulses in smaller windows for detecting faster - * a false alarm. As we use the window size for setting the history - * it should have a reasonably size. - */ - size_t i = 1; - d_window_size = d_dot_samples / i; - while(d_window_size > 64) { - i++; - d_window_size = d_dot_samples / i; - } + /* Register the message handlers */ + set_msg_handler ( + pmt::mp ("act_threshold"), + boost::bind (&cw_to_symbol_impl::set_act_threshold_msg_handler, this, + _1)); - /* NOTE: The dot duration should be a perfect multiple of the window */ - while(d_dot_samples % d_window_size != 0) { - d_window_size++; - } + /* + * Reconsider the dot oulse duration based on the confidence level + * and hysteresis + */ + d_dot_samples = d_dot_samples - 2 * (hysteresis * conf_level); - LOG_WARN("Dot symbol samples: %lu", d_dot_samples); - LOG_WARN("Window size: %lu", d_window_size); - /* Set the duration of each symbol in multiples of the window size */ - d_dot_windows_num = d_dot_samples / d_window_size; - d_dash_windows_num = 3 * d_dot_windows_num; - d_short_pause_windows_num = d_dash_windows_num; - d_long_pause_windows_num = 7 * d_dot_windows_num; + /* + * Try to split the CW pulses in smaller windows for detecting faster + * a false alarm. As we use the window size for setting the history + * it should have a reasonably size. + */ + size_t i = 1; + d_window_size = d_dot_samples / i; + while (d_window_size > 64) { + i++; + d_window_size = d_dot_samples / i; + } - const int alignment_multiple = volk_get_alignment () - / (d_window_size * sizeof(float)); - set_alignment (std::max (1, alignment_multiple)); + /* NOTE: The dot duration should be a perfect multiple of the window */ + while (d_dot_samples % d_window_size != 0) { + d_window_size++; + } - d_const_val = (float *) volk_malloc(d_window_size * sizeof(float), - volk_get_alignment ()); - d_tmp = (float *) volk_malloc(d_window_size * sizeof(float), - volk_get_alignment ()); - d_out = (int32_t *) volk_malloc (d_window_size * sizeof(int32_t), + LOG_DEBUG("Dot symbol samples: %lu", d_dot_samples); + LOG_DEBUG("Window size: %lu", d_window_size); + + /* Set the duration of each symbol in multiples of the window size */ + d_dot_windows_num = d_dot_samples / d_window_size; + d_dash_windows_num = 3 * d_dot_windows_num; + d_short_pause_windows_num = d_dash_windows_num; + d_long_pause_windows_num = 7 * d_dot_windows_num; + + const int alignment_multiple = volk_get_alignment () + / (d_window_size * sizeof(float)); + set_alignment (std::max (1, alignment_multiple)); + + d_const_val = (float *) volk_malloc (d_window_size * sizeof(float), volk_get_alignment ()); + d_tmp = (float *) volk_malloc (d_window_size * sizeof(float), + volk_get_alignment ()); + d_out = (int32_t *) volk_malloc (d_window_size * sizeof(int32_t), + volk_get_alignment ()); - if(!d_const_val || !d_tmp || !d_out) { - throw std::runtime_error("cw_to_symbol: Could not allocate memory"); - } + if (!d_const_val || !d_tmp || !d_out) { + throw std::runtime_error ("cw_to_symbol: Could not allocate memory"); + } - for(i = 0; i < d_window_size; i++) { - d_const_val[i] = threshold; - } - set_history(d_window_size); - } + for (i = 0; i < d_window_size; i++) { + d_const_val[i] = threshold; + } + set_history (d_window_size); +} - inline void - cw_to_symbol_impl::send_symbol_msg (morse_symbol_t s) - { - if(s == MORSE_S_SPACE || s == MORSE_L_SPACE) { - d_prev_space_symbol = true; - } - else{ - d_prev_space_symbol = false; - } - message_port_pub (pmt::mp ("out"), pmt::from_long (s)); - } +inline void +cw_to_symbol_impl::send_symbol_msg (morse_symbol_t s) +{ + if (s == MORSE_S_SPACE || s == MORSE_L_SPACE) { + d_prev_space_symbol = true; + } + else { + d_prev_space_symbol = false; + } + message_port_pub (pmt::mp ("out"), pmt::from_long (s)); +} - inline bool - cw_to_symbol_impl::check_conf_level(size_t cnt, size_t target) - { - return ((float)cnt > target * d_confidence_level); - } +inline bool +cw_to_symbol_impl::check_conf_level (size_t cnt, size_t target) +{ + return ((float) cnt > target * d_confidence_level); +} - /* - * Our virtual destructor. - */ - cw_to_symbol_impl::~cw_to_symbol_impl () - { - volk_free (d_const_val); - volk_free (d_tmp); - volk_free (d_out); - } +/* + * Our virtual destructor. + */ +cw_to_symbol_impl::~cw_to_symbol_impl () +{ + volk_free (d_const_val); + volk_free (d_tmp); + volk_free (d_out); +} - inline void - cw_to_symbol_impl::set_idle () - { - d_dec_state = NO_SYNC; - d_window_cnt = 0; - } +inline void +cw_to_symbol_impl::set_idle () +{ + d_dec_state = NO_SYNC; + d_window_cnt = 0; + d_idle_cnt = 0; +} - inline void - cw_to_symbol_impl::set_short_on () - { - d_dec_state = SEARCH_DOT; - d_window_cnt = 1; - } +inline void +cw_to_symbol_impl::set_short_on () +{ + d_dec_state = SEARCH_DOT; + d_window_cnt = 1; +} - inline void - cw_to_symbol_impl::set_long_on () - { - d_dec_state = SEARCH_DASH; - } +inline void +cw_to_symbol_impl::set_long_on () +{ + d_dec_state = SEARCH_DASH; +} - inline void - cw_to_symbol_impl::set_search_space () - { - d_dec_state = SEARCH_SPACE; - d_window_cnt = 1; - } +inline void +cw_to_symbol_impl::set_search_space () +{ + d_dec_state = SEARCH_SPACE; + d_window_cnt = 1; +} - void - cw_to_symbol_impl::set_act_threshold_msg_handler (pmt::pmt_t msg) - { - if (pmt::is_pair (msg)) { - set_act_threshold (pmt::to_double (pmt::cdr (msg))); +void +cw_to_symbol_impl::set_act_threshold_msg_handler (pmt::pmt_t msg) +{ + if (pmt::is_pair (msg)) { + set_act_threshold (pmt::to_double (pmt::cdr (msg))); + } +} + +int +cw_to_symbol_impl::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + bool triggered; + size_t i; + const float *in_old = (const float *) input_items[0]; + const float *in = in_old + history () - 1; + + if (noutput_items < 0) { + return noutput_items; + } + + /* During idle state search for a possible trigger */ + if (d_dec_state == NO_SYNC) { + for (i = 0; i < (size_t) noutput_items; i++) { + /* + * Clamp the input so the window mean is not affected by strong spikes + * Good luck understanding this black magic shit! + */ + triggered = is_triggered (in_old + i, d_window_size); + if (triggered) { + LOG_DEBUG("Triggered!"); + set_short_on (); + return i + 1; } } + d_idle_cnt += noutput_items / d_window_size; + if(d_idle_cnt > 10 * d_long_pause_windows_num) { + send_symbol_msg (MORSE_END_MSG_SPACE); + d_idle_cnt = 0; + } + return noutput_items; + } - int - cw_to_symbol_impl::work (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) - { - bool triggered; - size_t i; - const float *in_old = (const float *) input_items[0]; - const float *in = in_old + history() - 1; - - if(noutput_items < 0) { - return noutput_items; - } - - /* During idle state search for a possible trigger */ - if(d_dec_state == NO_SYNC) { - for(i = 0; i < (size_t)noutput_items; i++) { - /* - * Clamp the input so the window mean is not affected by strong spikes - * Good luck understanding this black magic shit! - */ - triggered = is_triggered(in_old + i, d_window_size); - if(triggered) { - LOG_DEBUG("Triggered!"); - set_short_on(); - return i+1; + /* From now one, we handle the input in multiples of a window */ + for (i = 0; i < (size_t) noutput_items / d_window_size; i++) { + triggered = is_triggered (in + i * d_window_size, d_window_size); + switch (d_dec_state) + { + case SEARCH_DOT: + if (triggered) { + d_window_cnt++; + if (d_window_cnt > d_dot_windows_num) { + set_long_on (); + LOG_DEBUG("Going to search for long sequence"); } } - return noutput_items; - } - - /* From now one, we handle the input in multiples of a window */ - for (i = 0; i < (size_t)noutput_items / d_window_size; i++) { - triggered = is_triggered(in + i * d_window_size, d_window_size); - switch(d_dec_state) { - case SEARCH_DOT: - if(triggered) { - d_window_cnt++; - if(d_window_cnt > d_dot_windows_num) { - set_long_on(); - LOG_DEBUG("Going to search for long sequence"); - } - } - else { - if(check_conf_level(d_window_cnt, d_dot_windows_num)) { - LOG_DEBUG("DOT"); - send_symbol_msg(MORSE_DOT); - } - LOG_DEBUG("Going to search for space: win cnt %lu", d_window_cnt); - set_search_space (); - } - break; - case SEARCH_DASH: - if(triggered) { - d_window_cnt++; - } - else{ - if(check_conf_level(d_window_cnt, d_dash_windows_num)) { - LOG_DEBUG("DASH"); - send_symbol_msg(MORSE_DASH); - } - else{ - LOG_DEBUG("DOT"); - send_symbol_msg(MORSE_DOT); - } - set_search_space (); - LOG_DEBUG("Going to search for space"); - } - break; - case SEARCH_SPACE: - if (triggered) { - if(check_conf_level(d_window_cnt, d_long_pause_windows_num)) { - LOG_DEBUG("LONG SPACE"); - send_symbol_msg(MORSE_L_SPACE); - } - else if(check_conf_level(d_window_cnt, d_short_pause_windows_num)){ - LOG_DEBUG("SHORT SPACE"); - send_symbol_msg(MORSE_S_SPACE); - } - set_short_on(); - LOG_DEBUG("Going to search for dot"); - } - else{ - d_window_cnt++; - if(d_window_cnt > d_long_pause_windows_num) { - LOG_DEBUG("LONG SPACE"); - send_symbol_msg(MORSE_L_SPACE); - set_idle(); - LOG_DEBUG("Going to idle"); - return (i + 1) * d_window_size; - } - } - break; - default: - LOG_ERROR("Invalid decoder state"); + else { + if (check_conf_level (d_window_cnt, d_dot_windows_num)) { + LOG_DEBUG("DOT"); + send_symbol_msg (MORSE_DOT); + } + LOG_DEBUG("Going to search for space: win cnt %lu", d_window_cnt); + set_search_space (); } + break; + case SEARCH_DASH: + if (triggered) { + d_window_cnt++; + } + else { + if (check_conf_level (d_window_cnt, d_dash_windows_num)) { + LOG_DEBUG("DASH"); + send_symbol_msg (MORSE_DASH); + } + else { + LOG_DEBUG("DOT"); + send_symbol_msg (MORSE_DOT); + } + set_search_space (); + LOG_DEBUG("Going to search for space"); + } + break; + case SEARCH_SPACE: + if (triggered) { + if (check_conf_level (d_window_cnt, d_long_pause_windows_num)) { + LOG_DEBUG("LONG SPACE"); + send_symbol_msg (MORSE_L_SPACE); + } + else if (check_conf_level (d_window_cnt, d_short_pause_windows_num)) { + LOG_DEBUG("SHORT SPACE"); + send_symbol_msg (MORSE_S_SPACE); + } + set_short_on (); + LOG_DEBUG("Going to search for dot"); + } + else { + d_window_cnt++; + if (d_window_cnt > d_long_pause_windows_num) { + LOG_DEBUG("LONG SPACE"); + send_symbol_msg (MORSE_L_SPACE); + set_idle (); + LOG_DEBUG("Going to idle"); + return (i + 1) * d_window_size; + } + } + break; + default: + LOG_ERROR("Invalid decoder state"); } - return i * d_window_size; - } + } + return i * d_window_size; +} - /** - * Sets a new activation threshold. - * @param thrhld the new threshold. - */ - void - cw_to_symbol_impl::set_act_threshold (float thrhld) - { - d_act_thrshld = thrhld; - } +/** + * Sets a new activation threshold. + * @param thrhld the new threshold. + */ +void +cw_to_symbol_impl::set_act_threshold (float thrhld) +{ + d_act_thrshld = thrhld; +} - /** - * Clamps the input and performs at the same time binary slicing. - * With this way, a decision based on moving average is not affected - * by strong peaks. - * @param out the output buffer with the binary sliced output - * @param in the input signal - * @param len number of samples to process - */ - inline void - cw_to_symbol_impl::clamp_input (int32_t* out, const float* in, size_t len) - { - volk_32f_x2_subtract_32f(d_tmp, in, d_const_val, len); - volk_32f_binary_slicer_32i(d_out, d_tmp, len); - } +/** + * Clamps the input and performs at the same time binary slicing. + * With this way, a decision based on moving average is not affected + * by strong peaks. + * @param out the output buffer with the binary sliced output + * @param in the input signal + * @param len number of samples to process + */ +inline void +cw_to_symbol_impl::clamp_input (int32_t* out, const float* in, size_t len) +{ + volk_32f_x2_subtract_32f (d_tmp, in, d_const_val, len); + volk_32f_binary_slicer_32i (d_out, d_tmp, len); +} - static inline int32_t - hadd (const int32_t* in, size_t len) - { - size_t i; - int32_t cnt = 0; - for(i = 0; i < len; i++) { - cnt += in[i]; - } - return cnt; - } +static inline int32_t +hadd (const int32_t* in, size_t len) +{ + size_t i; + int32_t cnt = 0; + for (i = 0; i < len; i++) { + cnt += in[i]; + } + return cnt; +} - inline bool - cw_to_symbol_impl::is_triggered (const float* in, size_t len) - { - int32_t cnt; - clamp_input(d_out, in, len); - cnt = hadd(d_out, len); - return (cnt >= (int32_t)(d_window_size * d_confidence_level)) ? true : false; - } +inline bool +cw_to_symbol_impl::is_triggered (const float* in, size_t len) +{ + int32_t cnt; + clamp_input (d_out, in, len); + cnt = hadd (d_out, len); + return (cnt >= (int32_t) (d_window_size * d_confidence_level)) ? true : false; +} - } /* namespace satnogs */ +} /* namespace satnogs */ } /* namespace gr */ diff --git a/lib/cw_to_symbol_impl.h b/lib/cw_to_symbol_impl.h index 59fa4ed..e2fd13b 100644 --- a/lib/cw_to_symbol_impl.h +++ b/lib/cw_to_symbol_impl.h @@ -45,6 +45,7 @@ namespace gr size_t d_dot_samples; size_t d_window_size; size_t d_window_cnt; + size_t d_idle_cnt; size_t d_dot_windows_num; size_t d_dash_windows_num; size_t d_short_pause_windows_num; @@ -84,7 +85,7 @@ namespace gr public: cw_to_symbol_impl (double sampling_rate, float threshold, - float conf_level, size_t wpm); + float conf_level, size_t wpm, size_t hysteresis); ~cw_to_symbol_impl (); // Where all the action really happens diff --git a/lib/morse_decoder_impl.cc b/lib/morse_decoder_impl.cc index c86fb36..e54ddf8 100644 --- a/lib/morse_decoder_impl.cc +++ b/lib/morse_decoder_impl.cc @@ -28,94 +28,99 @@ #include namespace gr { - namespace satnogs - { +namespace satnogs +{ - morse_decoder::sptr - morse_decoder::make (char unrecognized_char, size_t min_frame_len) +morse_decoder::sptr +morse_decoder::make (char unrecognized_char, size_t min_frame_len) +{ + return gnuradio::get_initial_sptr ( + new morse_decoder_impl (unrecognized_char, min_frame_len)); +} + +void +morse_decoder_impl::symbol_msg_handler (pmt::pmt_t msg) +{ + bool res = false; + morse_symbol_t s; + s = (morse_symbol_t) pmt::to_long (msg); + + switch (s) { - return gnuradio::get_initial_sptr ( - new morse_decoder_impl (unrecognized_char, min_frame_len)); - } - - void - morse_decoder_impl::symbol_msg_handler (pmt::pmt_t msg) - { - bool res = false; - std::string str; - morse_symbol_t s; - s = (morse_symbol_t) pmt::to_long (msg); - - switch (s) - { - case MORSE_DOT: - case MORSE_DASH: - case MORSE_S_SPACE: - res = d_morse_tree.received_symbol (s); - break; - /* - * If a word separator occurs it is a good time to retrieve the decoded - * word - */ - case MORSE_L_SPACE: - /* - * Inject a character separator, for the morse decoder to commit - * the outstanding character - */ - res = d_morse_tree.received_symbol (MORSE_S_SPACE); - /* Just ignore the word separator if no word is yet decoded */ - if (d_morse_tree.get_word_len () == 0) { - res = true; - break; - } - str = d_morse_tree.get_word (); - if (str.length () > d_min_frame_len) { - d_morse_tree.reset (); - message_port_pub (pmt::mp ("out"), - pmt::make_blob (str.c_str (), str.length ())); - } - break; - case MORSE_INTRA_SPACE: - /*Ignore it */ - break; - default: - LOG_ERROR("Unknown Morse symbol"); - return; - } - + case MORSE_DOT: + case MORSE_DASH: + case MORSE_S_SPACE: + res = d_morse_tree.received_symbol (s); + break; /* - * If the decoding return false, it means that either an non decode-able - * character situation occurred or the maximum word limit reached + * If a word separator occurs it is a good time to retrieve the decoded + * word */ - if (!res) { - if (d_morse_tree.get_max_word_len () == d_morse_tree.get_word_len ()) { - str = d_morse_tree.get_word (); - d_morse_tree.reset (); - message_port_pub (pmt::mp ("out"), - pmt::make_blob (str.c_str (), str.length ())); - } + case MORSE_L_SPACE: + /* + * Inject a character separator, for the morse decoder to commit + * the outstanding character + */ + res = d_morse_tree.received_symbol (MORSE_S_SPACE); + /* Just ignore the word separator if no word is yet decoded */ + if (d_morse_tree.get_word_len () == 0) { + res = true; + break; } + d_str = d_str.append(d_morse_tree.get_word ()); + d_str = d_str.append(" "); + d_morse_tree.reset (); + break; + case MORSE_INTRA_SPACE: + /*Ignore it */ + break; + case MORSE_END_MSG_SPACE: + if (d_str.length () > d_min_frame_len) { + d_morse_tree.reset (); + message_port_pub (pmt::mp ("out"), + pmt::make_blob (d_str.c_str (), d_str.length ())); + d_str = ""; + } + break; + default: + LOG_ERROR("Unknown Morse symbol"); + return; } - /* - * The private constructor - */ - morse_decoder_impl::morse_decoder_impl (char unrecognized_char, - size_t min_frame_len) : - gr::block ("morse_decoder", gr::io_signature::make (0, 0, 0), - gr::io_signature::make (0, 0, 0)), - d_morse_tree (unrecognized_char), - d_min_frame_len (min_frame_len) - - { - /* Register the input and output msg handler */ - message_port_register_in (pmt::mp ("in")); - message_port_register_out (pmt::mp ("out")); - set_msg_handler ( - pmt::mp ("in"), - boost::bind (&morse_decoder_impl::symbol_msg_handler, this, _1)); + /* + * If the decoding return false, it means that either an non decode-able + * character situation occurred or the maximum word limit reached + */ + if (!res) { + if (d_morse_tree.get_max_word_len () == d_morse_tree.get_word_len ()) { + d_str = d_morse_tree.get_word (); + d_morse_tree.reset (); + message_port_pub (pmt::mp ("out"), + pmt::make_blob (d_str.c_str (), d_str.length ())); } + } +} - } /* namespace satnogs */ +/* + * The private constructor + */ +morse_decoder_impl::morse_decoder_impl (char unrecognized_char, + size_t min_frame_len) : + gr::block ("morse_decoder", gr::io_signature::make (0, 0, 0), + gr::io_signature::make (0, 0, 0)), + d_morse_tree (unrecognized_char), + d_min_frame_len (min_frame_len), + d_str("") + +{ + /* Register the input and output msg handler */ + message_port_register_in (pmt::mp ("in")); + message_port_register_out (pmt::mp ("out")); + set_msg_handler ( + pmt::mp ("in"), + boost::bind (&morse_decoder_impl::symbol_msg_handler, this, _1)); +} + +} /* namespace satnogs */ } /* namespace gr */ diff --git a/lib/morse_decoder_impl.h b/lib/morse_decoder_impl.h index 0094e46..0eceedc 100644 --- a/lib/morse_decoder_impl.h +++ b/lib/morse_decoder_impl.h @@ -24,24 +24,27 @@ #include #include -namespace gr { - namespace satnogs { +namespace gr +{ +namespace satnogs +{ - class morse_decoder_impl : public morse_decoder - { - private: - morse_tree d_morse_tree; - size_t d_min_frame_len; +class morse_decoder_impl : public morse_decoder +{ - void - symbol_msg_handler(pmt::pmt_t msg); +public: + morse_decoder_impl (char unrecognized_char, size_t min_frame_len); - public: - morse_decoder_impl(char unrecognized_char, size_t min_frame_len); +private: + morse_tree d_morse_tree; + size_t d_min_frame_len; + std::string d_str; - }; + void + symbol_msg_handler (pmt::pmt_t msg); +}; - } // namespace satnogs +} // namespace satnogs } // namespace gr #endif /* INCLUDED_SATNOGS_MORSE_DECODER_IMPL_H */