Improve the Morse decoder block

The Morse decoding block has now the configuration parameter that
enables an automatic estimation of the dot duration. Based on this
estimation all other symbol durations are computed.
This commit is contained in:
Manolis Surligas 2016-02-28 00:45:41 +02:00
parent 07ff0054ef
commit e9c6f36f86
11 changed files with 2115 additions and 82 deletions

Binary file not shown.

View File

@ -81,69 +81,6 @@
<value></value>
</param>
</block>
<block>
<key>variable_qtgui_range</key>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>value</key>
<value>20e3</value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>_coordinate</key>
<value>(416, 10)</value>
</param>
<param>
<key>gui_hint</key>
<value></value>
</param>
<param>
<key>_rotation</key>
<value>180</value>
</param>
<param>
<key>id</key>
<value>act_thrld</value>
</param>
<param>
<key>label</key>
<value>Activation</value>
</param>
<param>
<key>min_len</key>
<value>200</value>
</param>
<param>
<key>orient</key>
<value>Qt.Horizontal</value>
</param>
<param>
<key>start</key>
<value>10e3</value>
</param>
<param>
<key>step</key>
<value>100</value>
</param>
<param>
<key>stop</key>
<value>80e3</value>
</param>
<param>
<key>rangeType</key>
<value>float</value>
</param>
<param>
<key>widget</key>
<value>counter_slider</value>
</param>
</block>
<block>
<key>variable_qtgui_range</key>
<param>
@ -160,7 +97,7 @@
</param>
<param>
<key>_coordinate</key>
<value>(168, 10)</value>
<value>(184, 9)</value>
</param>
<param>
<key>gui_hint</key>
@ -250,7 +187,7 @@
</param>
<param>
<key>_coordinate</key>
<value>(296, 10)</value>
<value>(328, 9)</value>
</param>
<param>
<key>gui_hint</key>
@ -383,7 +320,7 @@ of a full period of the CW signal with frequency freq.</value>
<key>analog_agc2_xx</key>
<param>
<key>attack_rate</key>
<value>6.25e-4</value>
<value>6.25e-3</value>
</param>
<param>
<key>alias</key>
@ -399,7 +336,7 @@ of a full period of the CW signal with frequency freq.</value>
</param>
<param>
<key>decay_rate</key>
<value>6.25e-4</value>
<value>6.25e-3</value>
</param>
<param>
<key>_enabled</key>
@ -501,6 +438,53 @@ of a full period of the CW signal with frequency freq.</value>
<value>8192</value>
</param>
</block>
<block>
<key>audio_sink</key>
<param>
<key>alias</key>
<value></value>
</param>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>affinity</key>
<value></value>
</param>
<param>
<key>device_name</key>
<value></value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>_coordinate</key>
<value>(960, 188)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>id</key>
<value>audio_sink_0</value>
</param>
<param>
<key>num_inputs</key>
<value>1</value>
</param>
<param>
<key>ok_to_block</key>
<value>True</value>
</param>
<param>
<key>samp_rate</key>
<value>samp_rate</value>
</param>
</block>
<block>
<key>blocks_add_xx</key>
<param>
@ -1654,7 +1638,11 @@ of a full period of the CW signal with frequency freq.</value>
<key>satnogs_cw_to_symbol</key>
<param>
<key>threshold</key>
<value>act_thrld</value>
<value>20e3</value>
</param>
<param>
<key>auto_config</key>
<value>True</value>
</param>
<param>
<key>alias</key>
@ -1784,6 +1772,12 @@ of a full period of the CW signal with frequency freq.</value>
<source_key>0</source_key>
<sink_key>0</sink_key>
</connection>
<connection>
<source_block_id>blocks_multiply_const_vxx_0</source_block_id>
<sink_block_id>audio_sink_0</sink_block_id>
<source_key>0</source_key>
<sink_key>0</sink_key>
</connection>
<connection>
<source_block_id>blocks_multiply_const_vxx_0</source_block_id>
<sink_block_id>blocks_throttle_0</sink_block_id>

File diff suppressed because it is too large Load Diff

View File

@ -187,7 +187,7 @@
</param>
<param>
<key>debug_seq</key>
<value>"HELLO EARTH WORLD 123456789"</value>
<value>"XPGOLIAT HELLO EARTH WORLD 123456789"</value>
</param>
</block>
<block>

View File

@ -4,7 +4,7 @@
<key>satnogs_cw_to_symbol</key>
<category>satnogs</category>
<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, $auto_config)</make>
<callback>set_act_threshold($threshold)</callback>
<param>
@ -33,6 +33,20 @@
<value>20</value>
<type>int</type>
</param>
<param>
<name>Automatic timing recovery</name>
<key>auto_config</key>
<type>enum</type>
<option>
<name>No</name>
<key>False</key>
</option>
<option>
<name>Yes</name>
<key>True</key>
</option>
</param>
<sink>
<name>act_threshold</name>
@ -40,6 +54,12 @@
<optional>1</optional>
</sink>
<sink>
<name>sync</name>
<type>message</type>
<optional>1</optional>
</sink>
<sink>
<name>in</name>
<type>float</type>

View File

@ -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
)

View File

@ -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

View File

@ -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
*/

50
include/satnogs/utils.h Normal file
View File

@ -0,0 +1,50 @@
/* -*- c++ -*- */
/*
* gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
*
* Copyright (C) 2016, Libre Space Foundation <http://librespacefoundation.org/>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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_ */

View File

@ -22,8 +22,10 @@
#include "config.h"
#endif
#include <limits>
#include <gnuradio/io_signature.h>
#include <satnogs/log.h>
#include <satnogs/utils.h>
#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 */

View File

@ -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