Finish the CW encoder

The CW encoder is a debug block that can be used to check the
performance of the CW decoder of gr-satnogs module under different RF
conditions. It can also serve as a perfect debug tool for sattelite
missions.
This commit is contained in:
Manolis Surligas 2017-08-29 02:05:43 +03:00
parent 8d34e937ee
commit 52efa4c8bd
9 changed files with 238 additions and 222 deletions

View File

@ -24,6 +24,7 @@ list(APPEND debug_blocks
satnogs_debug_msg_source.xml
satnogs_debug_msg_source_raw.xml
satnogs_leo_channel.xml
satnogs_cw_encoder.xml
)
list(APPEND enabled_blocks
@ -50,7 +51,6 @@ install(FILES
${enabled_blocks}
satnogs_ogg_source.xml
satnogs_noaa_apt_sink.xml
satnogs_frame_file_sink.xml
satnogs_frame_file_sink.xml DESTINATION share/gnuradio/grc/blocks
satnogs_iq_sink.xml
satnogs_cw_encoder.xml DESTINATION share/gnuradio/grc/blocks
)

View File

@ -1,38 +1,39 @@
<?xml version="1.0"?>
<block>
<name>cw_encoder</name>
<name>CW Encoder</name>
<key>satnogs_cw_encoder</key>
<category>[satnogs]</category>
<category>[SatNOGS]/Debug</category>
<import>import satnogs</import>
<make>satnogs.cw_encoder($samp_rate, $cw_freq, $wpm)</make>
<!-- Make one 'param' node for every Parameter you want settable from the GUI.
Sub-nodes:
* name
* key (makes the value accessible as $keyname, e.g. in the make node)
* type -->
<param>
<name>...</name>
<key>...</key>
<type>...</type>
<name>Sample Rate</name>
<key>samp_rate</key>
<value>samp_rate</value>
<type>real</type>
</param>
<param>
<name>CW Frequency</name>
<key>cw_freq</key>
<value>700</value>
<type>real</type>
</param>
<param>
<name>Words per minute</name>
<key>wpm</key>
<value>20</value>
<type>int</type>
</param>
<!-- Make one 'sink' node per input. Sub-nodes:
* name (an identifier for the GUI)
* type
* vlen
* optional (set to 1 for optional inputs) -->
<sink>
<name>in</name>
<type><!-- e.g. int, float, complex, byte, short, xxx_vector, ...--></type>
<name>symbol</name>
<type>message</type>
</sink>
<!-- Make one 'source' node per output. Sub-nodes:
* name (an identifier for the GUI)
* type
* vlen
* optional (set to 1 for optional inputs) -->
<source>
<name>out</name>
<type><!-- e.g. int, float, complex, byte, short, xxx_vector, ...--></type>
<type>complex</type>
</source>
</block>

View File

@ -21,10 +21,11 @@
# Install public header files
########################################################################
list(APPEND DEBUG_HEADER_FILES
morse_debug_source.h
debug_msg_source_raw.h
debug_msg_source.h
leo_channel.h
morse_debug_source.h
debug_msg_source_raw.h
debug_msg_source.h
leo_channel.h
cw_encoder.h
)
list(APPEND HEADER_FILES
@ -64,7 +65,6 @@ install(FILES
${HEADER_FILES}
ogg_source.h
noaa_apt_sink.h
frame_file_sink.h
frame_file_sink.h DESTINATION include/satnogs
iq_sink.h DESTINATION
cw_encoder.h DESTINATION include/satnogs
)

View File

@ -2,7 +2,8 @@
/*
* gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
*
* Copyright (C) 2016, Libre Space Foundation <http://librespacefoundation.org/>
* Copyright (C) 2016,2017
* 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
@ -27,10 +28,11 @@
* The different Morse symbols
*/
typedef enum {
MORSE_DOT = 0,//!< MORSE_DOT Morse dot (.) symbol
MORSE_DASH, //!< MORSE_DASH Morse dash (-) symbol
MORSE_S_SPACE,//!< MORSE_S_SPACE Morse short space between characters
MORSE_L_SPACE //!< MORSE_L_SPACE Morse long space between words
MORSE_DOT = 0, //!< Morse dot (.) symbol
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_symbol_t;

View File

@ -33,10 +33,11 @@ include_directories(
link_directories(${Boost_LIBRARY_DIRS})
list(APPEND satnogs_debug_sources
morse_debug_source_impl.cc
debug_msg_source_impl.cc
debug_msg_source_raw_impl.cc
leo_channel_impl.cc
morse_debug_source_impl.cc
debug_msg_source_impl.cc
debug_msg_source_raw_impl.cc
leo_channel_impl.cc
cw_encoder_impl.cc
)
list(APPEND satnogs_sources
morse_tree.cc
@ -62,9 +63,8 @@ list(APPEND satnogs_sources
waterfall_sink_impl.cc
ogg_source_impl.cc
noaa_apt_sink_impl.cc
frame_file_sink_impl.cc
frame_file_sink_impl.cc)
iq_sink_impl.cc
cw_encoder_impl.cc)
if(${INCLUDE_DEBUG_BLOCKS})
list(APPEND satnogs_sources ${satnogs_debug_sources})

View File

@ -24,22 +24,12 @@
#endif
#include <gnuradio/io_signature.h>
#include <satnogs/log.h>
#include "cw_encoder_impl.h"
namespace gr {
namespace satnogs {
const std::vector<char> cw_encoder_impl::cw_chars (
{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '1', '2',
'3', '4', '5', '6', '7', '8', '9', '0' });
const std::vector<std::string> cw_encoder_impl::cw_symbols (
{ ".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..",
".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.",
"...", "-", "..-", "...-", ".--", "-..-", "-.--", "--..", ".----",
"..---", "...--", "....-", ".....", "-....", "--...", "---..",
"----.", "-----" });
cw_encoder::sptr
cw_encoder::make(double samp_rate, double cw_freq, size_t wpm)
{
@ -60,11 +50,11 @@ namespace gr {
d_wpm (wpm),
d_dot_samples ((1.2 / wpm) / (1.0 / samp_rate)),
d_window_size (0),
d_nco (),
d_word (new uint8_t[2048]),
d_remaining (0)
d_windows_remaining (0),
d_cw_symbol (MORSE_L_SPACE),
d_nco ()
{
message_port_register_in(pmt::mp("word"));
message_port_register_in(pmt::mp("symbol"));
/*
* Try to split the CW pulses in smaller windows for dealing efficiently
@ -82,6 +72,7 @@ namespace gr {
d_window_size++;
}
set_output_multiple(d_window_size);
d_nco.set_freq ((2 * M_PI * cw_freq) / samp_rate);
}
@ -90,31 +81,6 @@ namespace gr {
*/
cw_encoder_impl::~cw_encoder_impl()
{
delete [] d_word;
}
static inline size_t
find_char_idx(const char* characters, size_t len, char c)
{
size_t i;
for(i = 0; i < len; i++) {
if(characters[i] == c){
return i;
}
}
return len;
}
std::string
cw_encoder_impl::get_cw_symbol (char c)
{
size_t i;
for(i = 0; i < cw_chars.size(); i++) {
if(cw_chars[i] == c) {
return cw_symbols[i];
}
}
return "";
}
int
@ -122,19 +88,56 @@ namespace gr {
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
gr_complex *out = (gr_complex *) output_items[0];
size_t available;
size_t i;
gr_complex *out = (gr_complex *) output_items[0];
if(d_remaining == 0) {
pmt::pmt_t w = delete_head_blocking(pmt::mp("word"));
if(pmt::blob_length(w) > 2048) {
return 0;
}
d_word = (uint8_t *) pmt::blob_data(w);
d_remaining = pmt::blob_length(w);
if(noutput_items < 0) {
return noutput_items;
}
return noutput_items;
if(d_windows_remaining == 0) {
pmt::pmt_t symbol = delete_head_blocking(pmt::mp("symbol"));
d_cw_symbol = (morse_symbol_t) pmt::to_long(symbol);
/* Reset NCO so the amplitude starts from zero */
d_nco.set_freq ((2 * M_PI * d_cw_freq) / d_samp_rate);
switch(d_cw_symbol) {
case MORSE_DOT:
case MORSE_INTRA_SPACE:
d_windows_remaining = d_dot_samples / d_window_size;
break;
case MORSE_DASH:
case MORSE_S_SPACE:
d_windows_remaining = (d_dot_samples / d_window_size) * 3;
break;
case MORSE_L_SPACE:
d_windows_remaining = (d_dot_samples / d_window_size) * 7;
break;
default:
LOG_WARN("Unrecognized CW symbol");
return 0;
}
}
for(i = 0; i < (size_t)noutput_items / d_window_size; i++) {
switch(d_cw_symbol){
case MORSE_S_SPACE:
case MORSE_L_SPACE:
case MORSE_INTRA_SPACE:
memset (out + i * d_window_size, 0,
d_window_size * sizeof(gr_complex));
break;
case MORSE_DOT:
case MORSE_DASH:
d_nco.sincos(out + i * d_window_size, d_window_size, 1.0);
break;
}
d_windows_remaining--;
if(d_windows_remaining == 0) {
return (i + 1) * d_window_size;
}
}
return i * d_window_size;
}
} /* namespace satnogs */

View File

@ -25,6 +25,7 @@
#include <vector>
#include <string>
#include <satnogs/cw_encoder.h>
#include <satnogs/morse.h>
#include <gnuradio/fxpt_nco.h>
namespace gr
@ -40,16 +41,16 @@ namespace gr
const size_t d_wpm;
const size_t d_dot_samples;
size_t d_window_size;
size_t d_windows_remaining;
morse_symbol_t d_cw_symbol;
gr::fxpt_nco d_nco;
uint8_t *d_word;
size_t d_remaining;
std::string
get_cw_symbol(char c);
public:
static const std::vector<char> cw_chars;
static const std::vector<std::string> cw_symbols;
cw_encoder_impl (double samp_rate, double cw_freq, size_t wpm);
~cw_encoder_impl ();

View File

@ -2,7 +2,8 @@
/*
* gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
*
* Copyright (C) 2016, Libre Space Foundation <http://librespacefoundation.org/>
* Copyright (C) 2016, 2017
* 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
@ -27,55 +28,55 @@
#include <satnogs/morse.h>
#include <random>
namespace gr {
namespace satnogs {
namespace gr
{
namespace satnogs
{
morse_debug_source::sptr
morse_debug_source::make(const std::string& debug_seq,
bool inject_errors,
float error_prob)
morse_debug_source::make (const std::string& debug_seq, bool inject_errors,
float error_prob)
{
return gnuradio::get_initial_sptr
(new morse_debug_source_impl(debug_seq, inject_errors, error_prob));
return gnuradio::get_initial_sptr (
new morse_debug_source_impl (debug_seq, inject_errors, error_prob));
}
/*
* The private constructor
*/
morse_debug_source_impl::morse_debug_source_impl(std::string debug_seq,
bool inject_errors,
float error_prob)
: gr::block("morse_debug_source",
gr::io_signature::make(0, 0, 0),
gr::io_signature::make(0, 0, 0)),
d_inject_errors(inject_errors),
d_p(error_prob),
d_run(true),
d_chars { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
'W', 'X', 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7',
'8', '9', '0' },
d_symbols { ".-", "-...", "-.-.", "-..", ".", "..-.", "--.",
"....", "..", ".---", "-.-", ".-..", "--", "-.",
"---", ".--.", "--.-", ".-.", "...", "-", "..-",
"...-", ".--", "-..-", "-.--", "--..", ".----",
"..---", "...--", "....-", ".....", "-....", "--...",
"---..", "----.", "-----"}
morse_debug_source_impl::morse_debug_source_impl (std::string debug_seq,
bool inject_errors,
float error_prob) :
gr::block ("morse_debug_source",
gr::io_signature::make (0, 0, 0),
gr::io_signature::make (0, 0, 0)),
d_inject_errors (inject_errors),
d_p (error_prob),
d_run (true),
d_chars
{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
'Z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' },
d_symbols
{ ".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..",
".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-",
".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--",
"--..", ".----", "..---", "...--", "....-", ".....", "-....",
"--...", "---..", "----.", "-----" }
{
message_port_register_out(pmt::mp("out"));
d_thread = std::thread(&morse_debug_source_impl::send_debug_msg,
this,
debug_seq);
message_port_register_out (pmt::mp ("out"));
d_thread = std::thread (&morse_debug_source_impl::send_debug_msg, this,
debug_seq);
}
static inline size_t
find_char_idx(const char* characters, size_t len, char c)
find_char_idx (const char* characters, size_t len, char c)
{
size_t i;
for(i = 0; i < len; i++) {
if(characters[i] == c){
return i;
}
for (i = 0; i < len; i++) {
if (characters[i] == c) {
return i;
}
}
return len;
}
@ -89,65 +90,69 @@ namespace gr {
std::string s;
char c;
std::random_device rd;
std::mt19937 gen(rd());
std::bernoulli_distribution error_distr(d_p);
std::mt19937 gen (rd ());
std::bernoulli_distribution error_distr (d_p);
bool inject_error;
size_t len = sentence.length();
pmt::pmt_t port = pmt::mp("out");
size_t len = sentence.length ();
pmt::pmt_t port = pmt::mp ("out");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
while(d_run) {
std::this_thread::sleep_for (std::chrono::milliseconds (1000));
while (d_run) {
/* Not the best approach, but hey, this is only for debug */
for(i = 0; i < len; i++){
c = std::toupper(sentence[i]);
if(c == ' '){
message_port_pub(port, pmt::from_long(MORSE_L_SPACE));
}
for (i = 0; i < len; i++) {
c = std::toupper (sentence[i]);
if (c == ' ') {
message_port_pub (port, pmt::from_long (MORSE_L_SPACE));
}
idx = find_char_idx(d_chars, sizeof(d_chars), c);
if(idx != sizeof(d_chars)){
idx = find_char_idx (d_chars, sizeof(d_chars), c);
if (idx != sizeof(d_chars)) {
s = d_symbols[idx];
/* Get from the random distribution if an error should be injected */
inject_error = d_inject_errors && error_distr(gen);
for(j = 0; j < s.length(); j++) {
if(s[j] == '.'){
if(inject_error){
message_port_pub(port, pmt::from_long(MORSE_DASH));
}
else{
message_port_pub(port, pmt::from_long(MORSE_DOT));
}
}
else{
if(inject_error){
message_port_pub(port, pmt::from_long(MORSE_DOT));
}
else{
message_port_pub(port, pmt::from_long(MORSE_DASH));
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
s = d_symbols[idx];
/* Get from the random distribution if an error should be injected */
inject_error = d_inject_errors && error_distr (gen);
for (j = 0; j < s.length (); j++) {
if (s[j] == '.') {
if (inject_error) {
message_port_pub (port, pmt::from_long (MORSE_DASH));
message_port_pub (port, pmt::from_long (MORSE_INTRA_SPACE));
/* Send also a character delimiter after waiting a little */
std::this_thread::sleep_for(std::chrono::milliseconds(200));
message_port_pub(port, pmt::from_long(MORSE_S_SPACE));
}
}
message_port_pub(port, pmt::from_long(MORSE_L_SPACE));
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
else {
message_port_pub (port, pmt::from_long (MORSE_DOT));
message_port_pub (port, pmt::from_long (MORSE_INTRA_SPACE));
}
}
else {
if (inject_error) {
message_port_pub (port, pmt::from_long (MORSE_DOT));
message_port_pub (port, pmt::from_long (MORSE_INTRA_SPACE));
}
else {
message_port_pub (port, pmt::from_long (MORSE_DASH));
message_port_pub (port, pmt::from_long (MORSE_INTRA_SPACE));
}
}
std::this_thread::sleep_for (std::chrono::milliseconds (100));
}
/* Send also a character delimiter */
message_port_pub (port, pmt::from_long (MORSE_S_SPACE));
}
}
message_port_pub (port, pmt::from_long (MORSE_L_SPACE));
std::this_thread::sleep_for (std::chrono::milliseconds (1000));
}
}
/*
* Our virtual destructor.
*/
morse_debug_source_impl::~morse_debug_source_impl()
morse_debug_source_impl::~morse_debug_source_impl ()
{
d_run = false;
d_thread.join();
d_thread.join ();
}
} /* namespace satnogs */

View File

@ -2,7 +2,8 @@
/*
* gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
*
* Copyright (C) 2016, Libre Space Foundation <http://librespacefoundation.org/>
* Copyright (C) 2016, 2017
* 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
@ -34,7 +35,7 @@ namespace gr
morse_decoder::make (char unrecognized_char)
{
return gnuradio::get_initial_sptr (
new morse_decoder_impl (unrecognized_char));
new morse_decoder_impl (unrecognized_char));
}
void
@ -45,50 +46,54 @@ namespace gr
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();
d_morse_tree.reset();
message_port_pub(pmt::mp("out"), pmt::make_blob(str.c_str(),
str.length()));
break;
default:
LOG_ERROR("Unknown Morse symbol");
return;
}
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 ();
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;
}
/*
* If the decoding return false, it means that either an non decode-able
* character situation occurred or the maximum word limit reached
*/
if (!s) {
if(d_morse_tree.get_max_word_len() == d_morse_tree.get_word_len()){
str = d_morse_tree.get_word();
d_morse_tree.reset();
std::cout << "Received word: " << str << std::endl;
}
if (d_morse_tree.get_max_word_len () == d_morse_tree.get_word_len ()) {
str = d_morse_tree.get_word ();
d_morse_tree.reset ();
std::cout << "Received word: " << str << std::endl;
}
}
else{
LOG_DEBUG("Something went wrong");
else {
LOG_DEBUG("Something went wrong");
}
}
@ -96,17 +101,16 @@ namespace gr
* The private constructor
*/
morse_decoder_impl::morse_decoder_impl (char unrecognized_char) :
gr::block ("morse_decoder",
gr::io_signature::make (0, 0, 0),
gr::io_signature::make (0, 0, 0)),
d_morse_tree (unrecognized_char)
gr::block ("morse_decoder", gr::io_signature::make (0, 0, 0),
gr::io_signature::make (0, 0, 0)),
d_morse_tree (unrecognized_char)
{
/* Register the input and output msg handler */
message_port_register_in (pmt::mp ("in"));
message_port_register_out(pmt::mp("out"));
message_port_register_out (pmt::mp ("out"));
set_msg_handler (
pmt::mp ("in"),
boost::bind (&morse_decoder_impl::symbol_msg_handler, this, _1));
pmt::mp ("in"),
boost::bind (&morse_decoder_impl::symbol_msg_handler, this, _1));
}
} /* namespace satnogs */