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
This commit is contained in:
parent
5291e18032
commit
67ea02a248
|
@ -3,7 +3,7 @@
|
|||
<name>CW to Symbols</name>
|
||||
<key>satnogs_cw_to_symbol</key>
|
||||
<import>import satnogs</import>
|
||||
<make>satnogs.cw_to_symbol($sampling_rate, $threshold, $conf_level, $wpm)</make>
|
||||
<make>satnogs.cw_to_symbol($sampling_rate, $threshold, $conf_level, $wpm, $hysteresis)</make>
|
||||
<callback>set_act_threshold($threshold)</callback>
|
||||
|
||||
<param>
|
||||
|
@ -33,6 +33,13 @@
|
|||
<type>int</type>
|
||||
</param>
|
||||
|
||||
<param>
|
||||
<name>Hysteresis</name>
|
||||
<key>hysteresis</key>
|
||||
<value>0</value>
|
||||
<type>int</type>
|
||||
</param>
|
||||
|
||||
<sink>
|
||||
<name>act_threshold</name>
|
||||
<type>message</type>
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
#include <satnogs/api.h>
|
||||
#include <gnuradio/sync_block.h>
|
||||
|
||||
#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;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/*
|
||||
* gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
|
||||
*
|
||||
* Copyright (C) 2016,2017
|
||||
* Copyright (C) 2016-2018
|
||||
* Libre Space Foundation <http://librespacefoundation.org/>
|
||||
*
|
||||
* 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 //!<Morse long space between words
|
||||
MORSE_L_SPACE, //!<Morse long space between words
|
||||
MORSE_END_MSG_SPACE //!< End of message
|
||||
} morse_symbol_t;
|
||||
|
||||
|
||||
|
|
|
@ -44,17 +44,19 @@ namespace gr
|
|||
|
||||
cw_to_symbol::sptr
|
||||
cw_to_symbol::make (double sampling_rate, float threshold, float conf_level,
|
||||
size_t wpm)
|
||||
size_t wpm, size_t hysteresis)
|
||||
{
|
||||
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,
|
||||
hysteresis));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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,
|
||||
size_t hysteresis) :
|
||||
gr::sync_block ("cw_to_symbol",
|
||||
gr::io_signature::make (1, 1, sizeof(float)),
|
||||
gr::io_signature::make (0, 0, 0)),
|
||||
|
@ -64,13 +66,13 @@ namespace gr
|
|||
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");
|
||||
throw std::invalid_argument ("Decoder can not handle such low WPM setting");
|
||||
}
|
||||
|
||||
if (wpm > MAX_WPM) {
|
||||
|
@ -83,6 +85,10 @@ namespace gr
|
|||
"Confidence level should be in the range [0.5, 1.0]");
|
||||
}
|
||||
|
||||
if(hysteresis > d_dot_samples / 4) {
|
||||
throw std::invalid_argument ("Too large hysteresis value");
|
||||
}
|
||||
|
||||
message_port_register_in (pmt::mp ("act_threshold"));
|
||||
message_port_register_out (pmt::mp ("out"));
|
||||
|
||||
|
@ -92,6 +98,13 @@ namespace gr
|
|||
boost::bind (&cw_to_symbol_impl::set_act_threshold_msg_handler, this,
|
||||
_1));
|
||||
|
||||
/*
|
||||
* Reconsider the dot oulse duration based on the confidence level
|
||||
* and hysteresis
|
||||
*/
|
||||
d_dot_samples = d_dot_samples - 2 * (hysteresis * conf_level);
|
||||
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
@ -109,8 +122,8 @@ namespace gr
|
|||
d_window_size++;
|
||||
}
|
||||
|
||||
LOG_WARN("Dot symbol samples: %lu", d_dot_samples);
|
||||
LOG_WARN("Window size: %lu", d_window_size);
|
||||
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;
|
||||
|
@ -172,6 +185,7 @@ namespace gr
|
|||
{
|
||||
d_dec_state = NO_SYNC;
|
||||
d_window_cnt = 0;
|
||||
d_idle_cnt = 0;
|
||||
}
|
||||
|
||||
inline void
|
||||
|
@ -230,13 +244,19 @@ namespace gr
|
|||
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;
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
switch (d_dec_state)
|
||||
{
|
||||
case SEARCH_DOT:
|
||||
if (triggered) {
|
||||
d_window_cnt++;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -42,7 +42,6 @@ namespace gr
|
|||
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);
|
||||
|
||||
|
@ -68,16 +67,21 @@ namespace gr
|
|||
res = true;
|
||||
break;
|
||||
}
|
||||
str = d_morse_tree.get_word ();
|
||||
if (str.length () > d_min_frame_len) {
|
||||
d_str = d_str.append(d_morse_tree.get_word ());
|
||||
d_str = d_str.append(" ");
|
||||
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;
|
||||
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;
|
||||
|
@ -89,10 +93,10 @@ namespace gr
|
|||
*/
|
||||
if (!res) {
|
||||
if (d_morse_tree.get_max_word_len () == d_morse_tree.get_word_len ()) {
|
||||
str = d_morse_tree.get_word ();
|
||||
d_str = d_morse_tree.get_word ();
|
||||
d_morse_tree.reset ();
|
||||
message_port_pub (pmt::mp ("out"),
|
||||
pmt::make_blob (str.c_str (), str.length ()));
|
||||
pmt::make_blob (d_str.c_str (), d_str.length ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -105,7 +109,8 @@ namespace gr
|
|||
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_min_frame_len (min_frame_len),
|
||||
d_str("")
|
||||
|
||||
{
|
||||
/* Register the input and output msg handler */
|
||||
|
|
|
@ -24,21 +24,24 @@
|
|||
#include <satnogs/morse_decoder.h>
|
||||
#include <satnogs/morse_tree.h>
|
||||
|
||||
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;
|
||||
|
||||
void
|
||||
symbol_msg_handler(pmt::pmt_t msg);
|
||||
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue