Fix CW decoder issue

The CW decoder was wrongly producing a Short Pause symbol instead of a
dot symbol. With this fix the decoder can now reconstruct the initial
text sequence sent.

Also the CW matched filter now can directly produce the power of the
filtered samples. This is very handy in order to get rid off an
additional multiply block, saving vital resources especially for
embedded platforms.

For testing and demonstration purposes the morse_decoding_flowgraph can
be used. The word sequence is the `HELLO WORLD`.
This commit is contained in:
Manolis Surligas 2016-02-01 22:04:27 +02:00
parent 43bf277442
commit d07fd19285
12 changed files with 568 additions and 236 deletions

View File

@ -10,4 +10,10 @@ Words per Minute (WPM) is about 20.
## Flowgraphs
* `test_matched_filter.grc`: Demonstrates the performance of the implemented
matched filter for CW decoding.
matched filter for CW decoding.
* `morse_decoding_flowgraph.grc`: This flowgraph decodes a CW signal and
prints the corresponding message at the `stdout`. To demonstrate the
capabilities of the decoder, signal and noise power GUI sliders are provided
for easy testing and experimenting.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -4,7 +4,7 @@
<key>satnogs_cw_matched_filter_ff</key>
<category>satnogs</category>
<import>import satnogs</import>
<make>satnogs.cw_matched_filter_ff($sampling_rate, $carrier_freq, $wpm)
<make>satnogs.cw_matched_filter_ff($sampling_rate, $carrier_freq, $wpm, $energy)
</make>
<param>
@ -25,6 +25,20 @@
<value>20</value>
<type>int</type>
</param>
<param>
<name>Compute Energy</name>
<key>energy</key>
<type>enum</type>
<option>
<name>No</name>
<key>False</key>
</option>
<option>
<name>Yes</name>
<key>True</key>
</option>
</param>
<sink>
<name>in</name>

View File

@ -21,6 +21,11 @@
#ifndef INCLUDE_SATNOGS_CONFIG_H_
#define INCLUDE_SATNOGS_CONFIG_H_
/*!
* Enable debug messages for the module
*/
#define ENABLE_DEBUG_MSG 0
/*!
* Enable debug messages for the CW decoding mechanism
*/

View File

@ -45,9 +45,14 @@ namespace gr {
* @param sampling_rate the sampling rate of the signal
* @param carrier_freq the audio frequency of the CW signal
* @param wpm Morse code Words per Minute
* @param energy_out if true, the filter produces the energy of the
* resulting filtered samples, otherwise the raw filter samples are
* produced. This is handy, in order to save the flowgraph from
* am additional signal energy block.
*/
static sptr make(double sampling_rate, double carrier_freq = 500,
size_t wpm = 20);
size_t wpm = 20,
bool energy_out = false);
};
} // namespace satnogs

View File

@ -29,7 +29,7 @@
#include <sys/syscall.h>
#if CW_DEBUG
#if ENABLE_DEBUG_MSG
#define LOG_INFO(M, ...) \
fprintf(stderr, "[INFO]: " M " \n", ##__VA_ARGS__)
@ -43,7 +43,7 @@
#define LOG_WARN(M, ...) \
fprintf(stderr, "[WARNING] %s:%d: " M "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#if CW_DEBUG
#if ENABLE_DEBUG_MSG
#define LOG_DEBUG(M, ...) \
fprintf(stderr, "[DEBUG]: " M "\n", ##__VA_ARGS__)
#else

View File

@ -39,7 +39,7 @@ namespace gr {
clear_text_msg_sink_impl::msg_handler (pmt::pmt_t msg)
{
std::string s((const char *)pmt::blob_data(msg), pmt::blob_length(msg));
std::cout << s << " " << std::endl;
std::cout << "Received text sequence:" << s << " " << std::endl;
}
/*

View File

@ -32,10 +32,12 @@ namespace gr {
namespace satnogs {
cw_matched_filter_ff::sptr
cw_matched_filter_ff::make(double sampling_rate, double carrier_freq, size_t wpm)
cw_matched_filter_ff::make(double sampling_rate, double carrier_freq,
size_t wpm, bool energy_out)
{
return gnuradio::get_initial_sptr
(new cw_matched_filter_ff_impl(sampling_rate, carrier_freq, wpm));
(new cw_matched_filter_ff_impl(sampling_rate, carrier_freq,
wpm, energy_out));
}
/*
@ -43,11 +45,13 @@ namespace gr {
*/
cw_matched_filter_ff_impl::cw_matched_filter_ff_impl (double sampling_rate,
double carrier_freq,
size_t wpm) :
size_t wpm,
bool energy_out) :
gr::sync_block ("cw_matched_filter_ff",
gr::io_signature::make (1, 1, sizeof(float)),
gr::io_signature::make (1, 1, sizeof(float))),
d_dot_duration(1.2/wpm),
d_produce_enrg(energy_out),
d_dot_samples(d_dot_duration / (1.0 / sampling_rate))
{
const int alignment_multiple = volk_get_alignment() / sizeof(float);
@ -85,6 +89,9 @@ namespace gr {
volk_32f_x2_dot_prod_32f(out + i, in + i, d_sin_wave,
d_dot_samples);
}
if(d_produce_enrg){
volk_32f_s32f_power_32f(out, out, 2, noutput_items);
}
return noutput_items;
}

View File

@ -34,6 +34,11 @@ namespace gr {
* The duration of the dot in seconds
*/
const double d_dot_duration;
/**
* If set to true, this block produces the energy of the filtered
* samples, rather the samples themselves
*/
const bool d_produce_enrg;
/**
* The duration of the dot in number of samples
*/
@ -43,7 +48,7 @@ namespace gr {
public:
cw_matched_filter_ff_impl(double sampling_rate, double carrier_freq,
size_t wpm);
size_t wpm, bool energy_out);
~cw_matched_filter_ff_impl();
// Where all the action really happens

View File

@ -78,7 +78,6 @@ namespace gr
inline void
cw_to_symbol_impl::set_idle ()
{
LOG_WARN("Enter IDLE");
d_state = IDLE;
d_state_cnt = 0;
d_pause_cnt = 0;
@ -87,7 +86,6 @@ namespace gr
inline void
cw_to_symbol_impl::set_short_on ()
{
LOG_WARN("Enter SHORT ON");
d_state = SHORT_ON_PERIOD;
d_state_cnt = 1;
d_pause_cnt = 0;
@ -96,14 +94,12 @@ namespace gr
inline void
cw_to_symbol_impl::set_long_on ()
{
LOG_WARN("Enter LONG ON");
d_state = LONG_ON_PERIOD;
}
inline void
cw_to_symbol_impl::set_short_off ()
{
LOG_WARN("Enter SHORT OFF");
d_state = SHORT_OFF_PERIOD;
d_state_cnt = 0;
d_pause_cnt = 1;
@ -112,7 +108,6 @@ namespace gr
inline void
cw_to_symbol_impl::set_long_off ()
{
LOG_WARN("Enter LONG OFF");
d_state = LONG_OFF_PERIOD;
}
@ -150,8 +145,8 @@ namespace gr
*/
conf_lvl = ((float) d_state_cnt) / d_dot_samples;
if(conf_lvl > d_confidence_level){
LOG_DEBUG("Short space");
send_symbol_msg(MORSE_S_SPACE);
LOG_DEBUG("DOT");
send_symbol_msg(MORSE_DOT);
}
/* Go find a possible short pause symbol */

View File

@ -56,6 +56,11 @@ namespace gr
* 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;