From 553898f61b8f2f62cec81a94ded8a1f2bc0f53ea Mon Sep 17 00:00:00 2001 From: Manolis Surligas Date: Sun, 9 Dec 2018 00:57:15 +0200 Subject: [PATCH 01/12] 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 */ From 4104091066b944bd6c159efbdf06840bdd347829 Mon Sep 17 00:00:00 2001 From: Manolis Surligas Date: Fri, 4 Jan 2019 20:33:55 +0200 Subject: [PATCH 02/12] Increase CW sensitivity * Switch from amplitude based approach to auto-correlation * Limit the bandwidth of the PLL using a low pass filter * Reject noise of the PLL using a very steep low pass after it --- apps/flowgraphs/cw_decoder.grc | 516 +++++++++++++++++++++----- apps/flowgraphs/fsk_ax25.grc | 24 +- apps/flowgraphs/satnogs_cw_decoder.py | 102 +++-- apps/flowgraphs/satnogs_fsk_ax25.py | 13 +- 4 files changed, 503 insertions(+), 152 deletions(-) diff --git a/apps/flowgraphs/cw_decoder.grc b/apps/flowgraphs/cw_decoder.grc index 87eec52..b1a9146 100644 --- a/apps/flowgraphs/cw_decoder.grc +++ b/apps/flowgraphs/cw_decoder.grc @@ -116,6 +116,123 @@ 48000 + + variable + + comment + + + + _enabled + 1 + + + _coordinate + (455, 35) + + + _rotation + 0 + + + id + dec + + + value + 8 + + + + variable + + comment + + + + _enabled + 1 + + + _coordinate + (359, 35) + + + _rotation + 0 + + + id + dot_samples + + + value + int((1.2 / wpm) / (1.0 / (audio_samp_rate / 10.0))) + + + + analog_agc2_xx + + attack_rate + 1e-4 + + + alias + + + + comment + + + + affinity + + + + decay_rate + 1e-4 + + + _enabled + 1 + + + _coordinate + (567, 283) + + + _rotation + 0 + + + gain + 1.0 + + + id + analog_agc2_xx_0 + + + max_gain + 65536 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + reference + 1.0 + + + type + complex + + analog_agc2_xx @@ -179,65 +296,6 @@ complex - - analog_agc_xx - - alias - - - - comment - - - - affinity - - - - _enabled - 1 - - - _coordinate - (1372, 339) - - - _rotation - 0 - - - gain - 1.0 - - - id - analog_agc_xx_0 - - - max_gain - 65536 - - - maxoutbuf - 0 - - - minoutbuf - 0 - - - rate - 1e-3 - - - reference - 1.0 - - - type - float - - analog_pll_carriertracking_cc @@ -258,7 +316,7 @@ _coordinate - (960, 348) + (919, 299) _rotation @@ -270,11 +328,11 @@ w - 2*math.pi/400.0 + 2*math.pi/100 max_freq - 2*math.pi*2e3/audio_samp_rate + 2*math.pi*2500 maxoutbuf @@ -282,7 +340,7 @@ min_freq - -2*math.pi*2e3/audio_samp_rate + -2*math.pi*2500 minoutbuf @@ -496,7 +554,7 @@ tone. This tone is typically 1 kHz. - blocks_complex_to_mag_squared + blocks_complex_to_mag alias @@ -515,15 +573,15 @@ tone. This tone is typically 1 kHz. _coordinate - (1168, 368) + (1630, 479) _rotation - 0 + 180 id - blocks_complex_to_mag_squared_0 + blocks_complex_to_mag_0 maxoutbuf @@ -581,6 +639,104 @@ tone. This tone is typically 1 kHz. 1 + + blocks_delay + + alias + + + + comment + + + + affinity + + + + delay + int(dot_samples)/dec + + + _enabled + 1 + + + _coordinate + (1302, 403) + + + _rotation + 0 + + + id + blocks_delay_0 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + num_ports + 1 + + + type + complex + + + vlen + 1 + + + + blocks_multiply_conjugate_cc + + alias + + + + comment + + + + affinity + + + + _enabled + 1 + + + _coordinate + (1358, 311) + + + _rotation + 0 + + + id + blocks_multiply_conjugate_cc_0 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + vlen + 1 + + blocks_multiply_xx @@ -910,6 +1066,61 @@ tone. This tone is typically 1 kHz. test.txt + + fir_filter_xxx + + alias + + + + comment + + + + affinity + + + + decim + 1 + + + _enabled + True + + + _coordinate + (1574, 315) + + + _rotation + 0 + + + id + fir_filter_xxx_0 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + samp_delay + 0 + + + taps + [1.0] * (int(dot_samples)/dec) + + + type + ccc + + parameter @@ -1134,7 +1345,7 @@ we shift the LO a little further cutoff_freq - 2e3 + 200 decim @@ -1150,7 +1361,7 @@ we shift the LO a little further _coordinate - (1268, 179) + (1102, 267) _rotation @@ -1178,11 +1389,11 @@ we shift the LO a little further samp_rate - audio_samp_rate + audio_samp_rate/10 width - 1e3 + 0.1e3 win @@ -1201,7 +1412,7 @@ we shift the LO a little further comment - Output samplerate: 20k / 4 = 5k + affinity @@ -1209,11 +1420,11 @@ we shift the LO a little further cutoff_freq - 100 + 2500 decim - 4 + 10 _enabled @@ -1221,19 +1432,19 @@ we shift the LO a little further type - fir_filter_fff + fir_filter_ccf _coordinate - (1584, 420) + (743, 267) _rotation - 180 + 0 gain - 4 + 1 id @@ -1257,7 +1468,82 @@ we shift the LO a little further width - 100 + 0.4e3 + + + win + firdes.WIN_HAMMING + + + + low_pass_filter + + beta + 6.76 + + + alias + + + + comment + + + + affinity + + + + cutoff_freq + 2500 + + + decim + 1 + + + _enabled + True + + + type + fir_filter_ccf + + + _coordinate + (1310, 155) + + + _rotation + 0 + + + gain + 1 + + + id + low_pass_filter_0_0_0 + + + interp + 1 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + samp_rate + audio_samp_rate + + + width + 0.8e3 win @@ -3067,7 +3353,7 @@ we shift the LO a little further satnogs_cw_to_symbol threshold - 0.4 + 2.0 alias @@ -3079,7 +3365,7 @@ we shift the LO a little further conf_level - 0.75 + 0.90 affinity @@ -3091,12 +3377,16 @@ we shift the LO a little further _coordinate - (1336, 460) + (1206, 459) _rotation 180 + + hysteresis + dot_samples/dec + id satnogs_cw_to_symbol_0 @@ -3111,7 +3401,7 @@ we shift the LO a little further sampling_rate - audio_samp_rate/4 + audio_samp_rate/10.0 wpm @@ -3224,7 +3514,7 @@ we shift the LO a little further _coordinate - (952, 444) + (951, 483) _rotation @@ -3639,21 +3929,21 @@ we shift the LO a little further 20 + + analog_agc2_xx_0 + low_pass_filter_0_0 + 0 + 0 + analog_agc2_xx_0_0 blocks_multiply_xx_0 0 1 - - analog_agc_xx_0 - low_pass_filter_0_0 - 0 - 0 - analog_pll_carriertracking_cc_0 - blocks_complex_to_mag_squared_0 + low_pass_filter_0 0 0 @@ -3664,8 +3954,8 @@ we shift the LO a little further 0 - blocks_complex_to_mag_squared_0 - analog_agc_xx_0 + blocks_complex_to_mag_0 + satnogs_cw_to_symbol_0 0 0 @@ -3675,6 +3965,18 @@ we shift the LO a little further 0 0 + + blocks_delay_0 + blocks_multiply_conjugate_cc_0 + 0 + 1 + + + blocks_multiply_conjugate_cc_0 + fir_filter_xxx_0 + 0 + 0 + blocks_multiply_xx_0 blocks_complex_to_real_0 @@ -3688,20 +3990,32 @@ we shift the LO a little further 0 - low_pass_filter_0 - analog_agc2_xx_0_0 + fir_filter_xxx_0 + blocks_complex_to_mag_0 0 0 low_pass_filter_0 - analog_pll_carriertracking_cc_0 + blocks_delay_0 + 0 + 0 + + + low_pass_filter_0 + blocks_multiply_conjugate_cc_0 0 0 low_pass_filter_0_0 - satnogs_cw_to_symbol_0 + analog_pll_carriertracking_cc_0 + 0 + 0 + + + low_pass_filter_0_0_0 + analog_agc2_xx_0_0 0 0 @@ -3713,7 +4027,13 @@ we shift the LO a little further pfb_arb_resampler_xxx_0 - low_pass_filter_0 + analog_agc2_xx_0 + 0 + 0 + + + pfb_arb_resampler_xxx_0 + low_pass_filter_0_0_0 0 0 diff --git a/apps/flowgraphs/fsk_ax25.grc b/apps/flowgraphs/fsk_ax25.grc index e389e60..6de43c5 100644 --- a/apps/flowgraphs/fsk_ax25.grc +++ b/apps/flowgraphs/fsk_ax25.grc @@ -136,7 +136,7 @@ _coordinate - (1248, 196) + (975, 395) _rotation @@ -677,7 +677,7 @@ omega - 2 + 4 type @@ -2457,7 +2457,7 @@ we shift the LO a little further atten - 100 + 60 taps @@ -2488,7 +2488,7 @@ we shift the LO a little further _coordinate - (984, 172) + (727, 275) _rotation @@ -2512,7 +2512,7 @@ we shift the LO a little further rrate - (2.0*baudrate)/audio_samp_rate + (4.0*baudrate)/satnogs.handle_samp_rate_rx(rx_sdr_device, samp_rate_rx) samp_delay @@ -2520,7 +2520,7 @@ we shift the LO a little further atten - 100 + 60 taps @@ -3512,12 +3512,6 @@ we shift the LO a little further 0 0 - - pfb_arb_resampler_xxx_0 - pfb_arb_resampler_xxx_0_0 - 0 - 0 - pfb_arb_resampler_xxx_0 satnogs_iq_sink_0 @@ -3566,6 +3560,12 @@ we shift the LO a little further 0 0 + + satnogs_coarse_doppler_correction_cc_0 + pfb_arb_resampler_xxx_0_0 + 0 + 0 + satnogs_quad_demod_filter_ff_0 digital_binary_slicer_fb_0 diff --git a/apps/flowgraphs/satnogs_cw_decoder.py b/apps/flowgraphs/satnogs_cw_decoder.py index 630ba3c..4c3af5b 100755 --- a/apps/flowgraphs/satnogs_cw_decoder.py +++ b/apps/flowgraphs/satnogs_cw_decoder.py @@ -5,7 +5,7 @@ # Title: CW Decoder # Author: Manolis Surligas (surligas@gmail.com) # Description: A CW (Morse) Decoder -# Generated: Sun Aug 19 21:56:19 2018 +# Generated: Sun Jan 6 13:47:58 2019 ################################################## from gnuradio import analog @@ -25,7 +25,7 @@ import time class satnogs_cw_decoder(gr.top_block): - def __init__(self, antenna=satnogs.not_set_antenna, bb_gain=satnogs.not_set_rx_bb_gain, bfo_freq=1e3, decoded_data_file_path='/tmp/.satnogs/data/data', dev_args=satnogs.not_set_dev_args, doppler_correction_per_sec=1000, enable_iq_dump=0, file_path='test.txt', if_gain=satnogs.not_set_rx_if_gain, iq_file_path='/tmp/iq.dat', lo_offset=100e3, ppm=0, rf_gain=satnogs.not_set_rx_rf_gain, rigctl_port=4532, rx_freq=100e6, rx_sdr_device='usrpb200', udp_IP='127.0.0.1', udp_port=16887, waterfall_file_path='/tmp/waterfall.dat', wpm=20, samp_rate_rx=satnogs.not_set_samp_rate_rx): + def __init__(self, antenna=satnogs.not_set_antenna, bb_gain=satnogs.not_set_rx_bb_gain, bfo_freq=1e3, decoded_data_file_path='/tmp/.satnogs/data/data', dev_args=satnogs.not_set_dev_args, doppler_correction_per_sec=1000, enable_iq_dump=0, file_path='test.txt', if_gain=satnogs.not_set_rx_if_gain, iq_file_path='/tmp/iq.dat', lo_offset=100e3, ppm=0, rf_gain=satnogs.not_set_rx_rf_gain, rigctl_port=4532, rx_freq=100e6, rx_sdr_device='usrpb200', samp_rate_rx=satnogs.not_set_samp_rate_rx, udp_IP='127.0.0.1', udp_port=16887, waterfall_file_path='/tmp/waterfall.dat', wpm=20): gr.top_block.__init__(self, "CW Decoder") ################################################## @@ -47,16 +47,18 @@ class satnogs_cw_decoder(gr.top_block): self.rigctl_port = rigctl_port self.rx_freq = rx_freq self.rx_sdr_device = rx_sdr_device + self.samp_rate_rx = samp_rate_rx self.udp_IP = udp_IP self.udp_port = udp_port self.waterfall_file_path = waterfall_file_path self.wpm = wpm - self.samp_rate_rx = samp_rate_rx ################################################## # Variables ################################################## self.audio_samp_rate = audio_samp_rate = 48000 + self.dot_samples = dot_samples = int((1.2 / wpm) / (1.0 / (audio_samp_rate / 10.0))) + self.dec = dec = 8 ################################################## # Blocks @@ -68,7 +70,7 @@ class satnogs_cw_decoder(gr.top_block): self.satnogs_morse_decoder_0 = satnogs.morse_decoder(ord('#'), 3) self.satnogs_iq_sink_0 = satnogs.iq_sink(16768, iq_file_path, False, enable_iq_dump) self.satnogs_frame_file_sink_0_0 = satnogs.frame_file_sink(decoded_data_file_path, 0) - self.satnogs_cw_to_symbol_0 = satnogs.cw_to_symbol(audio_samp_rate/4, 0.4, 0.75, wpm) + self.satnogs_cw_to_symbol_0 = satnogs.cw_to_symbol(audio_samp_rate/10.0, 2.0, 0.90, wpm, dot_samples/dec) self.satnogs_coarse_doppler_correction_cc_0 = satnogs.coarse_doppler_correction_cc(rx_freq, satnogs.handle_samp_rate_rx(rx_sdr_device, samp_rate_rx)) self.pfb_arb_resampler_xxx_0 = pfb.arb_resampler_ccf( audio_samp_rate/satnogs.handle_samp_rate_rx(rx_sdr_device, samp_rate_rx), @@ -89,20 +91,26 @@ class satnogs_cw_decoder(gr.top_block): self.osmosdr_source_0.set_antenna(satnogs.handle_rx_antenna(rx_sdr_device, antenna), 0) self.osmosdr_source_0.set_bandwidth(satnogs.handle_samp_rate_rx(rx_sdr_device, samp_rate_rx), 0) - self.low_pass_filter_0_0 = filter.fir_filter_fff(4, firdes.low_pass( - 4, audio_samp_rate, 100, 100, firdes.WIN_HAMMING, 6.76)) + self.low_pass_filter_0_0_0 = filter.fir_filter_ccf(1, firdes.low_pass( + 1, audio_samp_rate, 2500, 0.8e3, firdes.WIN_HAMMING, 6.76)) + self.low_pass_filter_0_0 = filter.fir_filter_ccf(10, firdes.low_pass( + 1, audio_samp_rate, 2500, 0.4e3, firdes.WIN_HAMMING, 6.76)) self.low_pass_filter_0 = filter.fir_filter_ccf(1, firdes.low_pass( - 1, audio_samp_rate, 2e3, 1e3, firdes.WIN_HAMMING, 6.76)) + 1, audio_samp_rate/10, 200, 0.1e3, firdes.WIN_HAMMING, 6.76)) + self.fir_filter_xxx_0 = filter.fir_filter_ccc(1, ([1.0] * (int(dot_samples)/dec))) + self.fir_filter_xxx_0.declare_sample_delay(0) self.blocks_rotator_cc_0 = blocks.rotator_cc(-2.0 * math.pi * (lo_offset / satnogs.handle_samp_rate_rx(rx_sdr_device, samp_rate_rx))) self.blocks_multiply_xx_0 = blocks.multiply_vcc(1) + self.blocks_multiply_conjugate_cc_0 = blocks.multiply_conjugate_cc(1) + self.blocks_delay_0 = blocks.delay(gr.sizeof_gr_complex*1, int(dot_samples)/dec) self.blocks_complex_to_real_0 = blocks.complex_to_real(1) - self.blocks_complex_to_mag_squared_0 = blocks.complex_to_mag_squared(1) + self.blocks_complex_to_mag_0 = blocks.complex_to_mag(1) self.analog_sig_source_x_0 = analog.sig_source_c(audio_samp_rate, analog.GR_COS_WAVE, bfo_freq, 1, 0) - self.analog_pll_carriertracking_cc_0 = analog.pll_carriertracking_cc(2*math.pi/400.0, 2*math.pi*2e3/audio_samp_rate, -2*math.pi*2e3/audio_samp_rate) - self.analog_agc_xx_0 = analog.agc_ff(1e-3, 1.0, 1.0) - self.analog_agc_xx_0.set_max_gain(65536) + self.analog_pll_carriertracking_cc_0 = analog.pll_carriertracking_cc(2*math.pi/100, 2*math.pi*2500, -2*math.pi*2500) self.analog_agc2_xx_0_0 = analog.agc2_cc(0.01, 0.001, 0.015, 0.0) self.analog_agc2_xx_0_0.set_max_gain(65536) + self.analog_agc2_xx_0 = analog.agc2_cc(1e-4, 1e-4, 1.0, 1.0) + self.analog_agc2_xx_0.set_max_gain(65536) @@ -113,19 +121,24 @@ class satnogs_cw_decoder(gr.top_block): self.msg_connect((self.satnogs_morse_decoder_0, 'out'), (self.satnogs_frame_file_sink_0_0, 'frame')) self.msg_connect((self.satnogs_morse_decoder_0, 'out'), (self.satnogs_udp_msg_sink_0_0, 'in')) self.msg_connect((self.satnogs_tcp_rigctl_msg_source_0, 'freq'), (self.satnogs_coarse_doppler_correction_cc_0, 'freq')) + self.connect((self.analog_agc2_xx_0, 0), (self.low_pass_filter_0_0, 0)) self.connect((self.analog_agc2_xx_0_0, 0), (self.blocks_multiply_xx_0, 1)) - self.connect((self.analog_agc_xx_0, 0), (self.low_pass_filter_0_0, 0)) - self.connect((self.analog_pll_carriertracking_cc_0, 0), (self.blocks_complex_to_mag_squared_0, 0)) + self.connect((self.analog_pll_carriertracking_cc_0, 0), (self.low_pass_filter_0, 0)) self.connect((self.analog_sig_source_x_0, 0), (self.blocks_multiply_xx_0, 0)) - self.connect((self.blocks_complex_to_mag_squared_0, 0), (self.analog_agc_xx_0, 0)) + self.connect((self.blocks_complex_to_mag_0, 0), (self.satnogs_cw_to_symbol_0, 0)) self.connect((self.blocks_complex_to_real_0, 0), (self.satnogs_ogg_encoder_0, 0)) + self.connect((self.blocks_delay_0, 0), (self.blocks_multiply_conjugate_cc_0, 1)) + self.connect((self.blocks_multiply_conjugate_cc_0, 0), (self.fir_filter_xxx_0, 0)) self.connect((self.blocks_multiply_xx_0, 0), (self.blocks_complex_to_real_0, 0)) self.connect((self.blocks_rotator_cc_0, 0), (self.satnogs_coarse_doppler_correction_cc_0, 0)) - self.connect((self.low_pass_filter_0, 0), (self.analog_agc2_xx_0_0, 0)) - self.connect((self.low_pass_filter_0, 0), (self.analog_pll_carriertracking_cc_0, 0)) - self.connect((self.low_pass_filter_0_0, 0), (self.satnogs_cw_to_symbol_0, 0)) + self.connect((self.fir_filter_xxx_0, 0), (self.blocks_complex_to_mag_0, 0)) + self.connect((self.low_pass_filter_0, 0), (self.blocks_delay_0, 0)) + self.connect((self.low_pass_filter_0, 0), (self.blocks_multiply_conjugate_cc_0, 0)) + self.connect((self.low_pass_filter_0_0, 0), (self.analog_pll_carriertracking_cc_0, 0)) + self.connect((self.low_pass_filter_0_0_0, 0), (self.analog_agc2_xx_0_0, 0)) self.connect((self.osmosdr_source_0, 0), (self.blocks_rotator_cc_0, 0)) - self.connect((self.pfb_arb_resampler_xxx_0, 0), (self.low_pass_filter_0, 0)) + self.connect((self.pfb_arb_resampler_xxx_0, 0), (self.analog_agc2_xx_0, 0)) + self.connect((self.pfb_arb_resampler_xxx_0, 0), (self.low_pass_filter_0_0_0, 0)) self.connect((self.pfb_arb_resampler_xxx_0, 0), (self.satnogs_iq_sink_0, 0)) self.connect((self.pfb_arb_resampler_xxx_0, 0), (self.satnogs_waterfall_sink_0, 0)) self.connect((self.satnogs_coarse_doppler_correction_cc_0, 0), (self.pfb_arb_resampler_xxx_0, 0)) @@ -244,6 +257,16 @@ class satnogs_cw_decoder(gr.top_block): self.osmosdr_source_0.set_bandwidth(satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx), 0) self.blocks_rotator_cc_0.set_phase_inc(-2.0 * math.pi * (self.lo_offset / satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx))) + def get_samp_rate_rx(self): + return self.samp_rate_rx + + def set_samp_rate_rx(self, samp_rate_rx): + self.samp_rate_rx = samp_rate_rx + self.pfb_arb_resampler_xxx_0.set_rate(self.audio_samp_rate/satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx)) + self.osmosdr_source_0.set_sample_rate(satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx)) + self.osmosdr_source_0.set_bandwidth(satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx), 0) + self.blocks_rotator_cc_0.set_phase_inc(-2.0 * math.pi * (self.lo_offset / satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx))) + def get_udp_IP(self): return self.udp_IP @@ -267,28 +290,35 @@ class satnogs_cw_decoder(gr.top_block): def set_wpm(self, wpm): self.wpm = wpm - - def get_samp_rate_rx(self): - return self.samp_rate_rx - - def set_samp_rate_rx(self, samp_rate_rx): - self.samp_rate_rx = samp_rate_rx - self.pfb_arb_resampler_xxx_0.set_rate(self.audio_samp_rate/satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx)) - self.osmosdr_source_0.set_sample_rate(satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx)) - self.osmosdr_source_0.set_bandwidth(satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx), 0) - self.blocks_rotator_cc_0.set_phase_inc(-2.0 * math.pi * (self.lo_offset / satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx))) + self.set_dot_samples(int((1.2 / self.wpm) / (1.0 / (self.audio_samp_rate / 10.0)))) def get_audio_samp_rate(self): return self.audio_samp_rate def set_audio_samp_rate(self, audio_samp_rate): self.audio_samp_rate = audio_samp_rate + self.set_dot_samples(int((1.2 / self.wpm) / (1.0 / (self.audio_samp_rate / 10.0)))) self.pfb_arb_resampler_xxx_0.set_rate(self.audio_samp_rate/satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx)) - self.low_pass_filter_0_0.set_taps(firdes.low_pass(4, self.audio_samp_rate, 100, 100, firdes.WIN_HAMMING, 6.76)) - self.low_pass_filter_0.set_taps(firdes.low_pass(1, self.audio_samp_rate, 2e3, 1e3, firdes.WIN_HAMMING, 6.76)) + self.low_pass_filter_0_0_0.set_taps(firdes.low_pass(1, self.audio_samp_rate, 2500, 0.8e3, firdes.WIN_HAMMING, 6.76)) + self.low_pass_filter_0_0.set_taps(firdes.low_pass(1, self.audio_samp_rate, 2500, 0.4e3, firdes.WIN_HAMMING, 6.76)) + self.low_pass_filter_0.set_taps(firdes.low_pass(1, self.audio_samp_rate/10, 200, 0.1e3, firdes.WIN_HAMMING, 6.76)) self.analog_sig_source_x_0.set_sampling_freq(self.audio_samp_rate) - self.analog_pll_carriertracking_cc_0.set_max_freq(2*math.pi*2e3/self.audio_samp_rate) - self.analog_pll_carriertracking_cc_0.set_min_freq(-2*math.pi*2e3/self.audio_samp_rate) + + def get_dot_samples(self): + return self.dot_samples + + def set_dot_samples(self, dot_samples): + self.dot_samples = dot_samples + self.fir_filter_xxx_0.set_taps(([1.0] * (int(self.dot_samples)/self.dec))) + self.blocks_delay_0.set_dly(int(self.dot_samples)/self.dec) + + def get_dec(self): + return self.dec + + def set_dec(self, dec): + self.dec = dec + self.fir_filter_xxx_0.set_taps(([1.0] * (int(self.dot_samples)/self.dec))) + self.blocks_delay_0.set_dly(int(self.dot_samples)/self.dec) def argument_parser(): @@ -342,6 +372,9 @@ def argument_parser(): parser.add_option( "", "--rx-sdr-device", dest="rx_sdr_device", type="string", default='usrpb200', help="Set rx_sdr_device [default=%default]") + parser.add_option( + "", "--samp-rate-rx", dest="samp_rate_rx", type="eng_float", default=eng_notation.num_to_str(satnogs.not_set_samp_rate_rx), + help="Set samp_rate_rx [default=%default]") parser.add_option( "", "--udp-IP", dest="udp_IP", type="string", default='127.0.0.1', help="Set udp_IP [default=%default]") @@ -354,9 +387,6 @@ def argument_parser(): parser.add_option( "", "--wpm", dest="wpm", type="intx", default=20, help="Set wpm [default=%default]") - parser.add_option( - "", "--samp-rate-rx", dest="samp_rate_rx", type="eng_float", default=eng_notation.num_to_str(satnogs.not_set_samp_rate_rx), - help="Set samp_rate_rx [default=%default]") return parser @@ -364,7 +394,7 @@ def main(top_block_cls=satnogs_cw_decoder, options=None): if options is None: options, _ = argument_parser().parse_args() - tb = top_block_cls(antenna=options.antenna, bb_gain=options.bb_gain, bfo_freq=options.bfo_freq, decoded_data_file_path=options.decoded_data_file_path, dev_args=options.dev_args, doppler_correction_per_sec=options.doppler_correction_per_sec, enable_iq_dump=options.enable_iq_dump, file_path=options.file_path, if_gain=options.if_gain, iq_file_path=options.iq_file_path, lo_offset=options.lo_offset, ppm=options.ppm, rf_gain=options.rf_gain, rigctl_port=options.rigctl_port, rx_freq=options.rx_freq, rx_sdr_device=options.rx_sdr_device, udp_IP=options.udp_IP, udp_port=options.udp_port, waterfall_file_path=options.waterfall_file_path, wpm=options.wpm, samp_rate_rx=options.samp_rate_rx) + tb = top_block_cls(antenna=options.antenna, bb_gain=options.bb_gain, bfo_freq=options.bfo_freq, decoded_data_file_path=options.decoded_data_file_path, dev_args=options.dev_args, doppler_correction_per_sec=options.doppler_correction_per_sec, enable_iq_dump=options.enable_iq_dump, file_path=options.file_path, if_gain=options.if_gain, iq_file_path=options.iq_file_path, lo_offset=options.lo_offset, ppm=options.ppm, rf_gain=options.rf_gain, rigctl_port=options.rigctl_port, rx_freq=options.rx_freq, rx_sdr_device=options.rx_sdr_device, samp_rate_rx=options.samp_rate_rx, udp_IP=options.udp_IP, udp_port=options.udp_port, waterfall_file_path=options.waterfall_file_path, wpm=options.wpm) tb.start() tb.wait() diff --git a/apps/flowgraphs/satnogs_fsk_ax25.py b/apps/flowgraphs/satnogs_fsk_ax25.py index 780b1f4..363a8a1 100755 --- a/apps/flowgraphs/satnogs_fsk_ax25.py +++ b/apps/flowgraphs/satnogs_fsk_ax25.py @@ -5,7 +5,7 @@ # Title: satnogs_fsk_ax25 # Author: Manolis Surligas (surligas@gmail.com) # Description: Generic FSK AX.25 decoder -# Generated: Fri Aug 17 00:38:04 2018 +# Generated: Fri Jan 4 20:33:37 2019 ################################################## from gnuradio import analog @@ -72,7 +72,7 @@ class satnogs_fsk_ax25(gr.top_block): self.satnogs_ax25_decoder_bm_0_0 = satnogs.ax25_decoder_bm('GND', 0, True, False, 1024) self.satnogs_ax25_decoder_bm_0 = satnogs.ax25_decoder_bm('GND', 0, True, True, 1024) self.pfb_arb_resampler_xxx_0_0 = pfb.arb_resampler_ccf( - (2.0*baudrate)/audio_samp_rate, + (4.0*baudrate)/satnogs.handle_samp_rate_rx(rx_sdr_device, samp_rate_rx), taps=None, flt_size=32) self.pfb_arb_resampler_xxx_0_0.declare_sample_delay(0) @@ -96,7 +96,7 @@ class satnogs_fsk_ax25(gr.top_block): self.osmosdr_source_0.set_antenna(satnogs.handle_rx_antenna(rx_sdr_device, antenna), 0) self.osmosdr_source_0.set_bandwidth(satnogs.handle_samp_rate_rx(rx_sdr_device, samp_rate_rx), 0) - self.digital_clock_recovery_mm_xx_0 = digital.clock_recovery_mm_ff(2, 0.25*0.175*0.175, 0.5, 0.175, 0.005) + self.digital_clock_recovery_mm_xx_0 = digital.clock_recovery_mm_ff(4, 0.25*0.175*0.175, 0.5, 0.175, 0.005) self.digital_binary_slicer_fb_0 = digital.binary_slicer_fb() self.dc_blocker_xx_0_0 = filter.dc_blocker_ff(1024, True) self.dc_blocker_xx_0 = filter.dc_blocker_ff(1024, True) @@ -124,11 +124,11 @@ class satnogs_fsk_ax25(gr.top_block): self.connect((self.digital_clock_recovery_mm_xx_0, 0), (self.satnogs_quad_demod_filter_ff_0, 0)) self.connect((self.osmosdr_source_0, 0), (self.blocks_rotator_cc_0, 0)) self.connect((self.pfb_arb_resampler_xxx_0, 0), (self.analog_quadrature_demod_cf_0_0_0, 0)) - self.connect((self.pfb_arb_resampler_xxx_0, 0), (self.pfb_arb_resampler_xxx_0_0, 0)) self.connect((self.pfb_arb_resampler_xxx_0, 0), (self.satnogs_iq_sink_0, 0)) self.connect((self.pfb_arb_resampler_xxx_0, 0), (self.satnogs_waterfall_sink_0, 0)) self.connect((self.pfb_arb_resampler_xxx_0_0, 0), (self.analog_quadrature_demod_cf_0_0, 0)) self.connect((self.satnogs_coarse_doppler_correction_cc_0, 0), (self.pfb_arb_resampler_xxx_0, 0)) + self.connect((self.satnogs_coarse_doppler_correction_cc_0, 0), (self.pfb_arb_resampler_xxx_0_0, 0)) self.connect((self.satnogs_quad_demod_filter_ff_0, 0), (self.digital_binary_slicer_fb_0, 0)) def get_antenna(self): @@ -143,7 +143,7 @@ class satnogs_fsk_ax25(gr.top_block): def set_baudrate(self, baudrate): self.baudrate = baudrate - self.pfb_arb_resampler_xxx_0_0.set_rate((2.0*self.baudrate)/self.audio_samp_rate) + self.pfb_arb_resampler_xxx_0_0.set_rate((4.0*self.baudrate)/satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx)) def get_bb_gain(self): return self.bb_gain @@ -236,6 +236,7 @@ class satnogs_fsk_ax25(gr.top_block): def set_rx_sdr_device(self, rx_sdr_device): self.rx_sdr_device = rx_sdr_device + self.pfb_arb_resampler_xxx_0_0.set_rate((4.0*self.baudrate)/satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx)) self.pfb_arb_resampler_xxx_0.set_rate(self.audio_samp_rate/satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx)) self.osmosdr_source_0.set_sample_rate(satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx)) self.osmosdr_source_0.set_gain(satnogs.handle_rx_rf_gain(self.rx_sdr_device, self.rf_gain), 0) @@ -250,6 +251,7 @@ class satnogs_fsk_ax25(gr.top_block): def set_samp_rate_rx(self, samp_rate_rx): self.samp_rate_rx = samp_rate_rx + self.pfb_arb_resampler_xxx_0_0.set_rate((4.0*self.baudrate)/satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx)) self.pfb_arb_resampler_xxx_0.set_rate(self.audio_samp_rate/satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx)) self.osmosdr_source_0.set_sample_rate(satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx)) self.osmosdr_source_0.set_bandwidth(satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx), 0) @@ -278,7 +280,6 @@ class satnogs_fsk_ax25(gr.top_block): def set_audio_samp_rate(self, audio_samp_rate): self.audio_samp_rate = audio_samp_rate - self.pfb_arb_resampler_xxx_0_0.set_rate((2.0*self.baudrate)/self.audio_samp_rate) self.pfb_arb_resampler_xxx_0.set_rate(self.audio_samp_rate/satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx)) From 35149fc047dc98e66f3d5d03f382ef2762a5a1db Mon Sep 17 00:00:00 2001 From: Manolis Surligas Date: Sun, 6 Jan 2019 14:19:59 +0200 Subject: [PATCH 03/12] Improve sensitivity of FSK/MSK flowgraphs --- apps/flowgraphs/fsk_ax25.grc | 95 +++++++++++++++++++++-- apps/flowgraphs/msk_ax25.grc | 113 ++++++++++++++++++++++++---- apps/flowgraphs/satnogs_fsk_ax25.py | 8 +- apps/flowgraphs/satnogs_msk_ax25.py | 23 +++--- 4 files changed, 205 insertions(+), 34 deletions(-) diff --git a/apps/flowgraphs/fsk_ax25.grc b/apps/flowgraphs/fsk_ax25.grc index 6de43c5..f19499e 100644 --- a/apps/flowgraphs/fsk_ax25.grc +++ b/apps/flowgraphs/fsk_ax25.grc @@ -136,7 +136,7 @@ _coordinate - (975, 395) + (1190, 411) _rotation @@ -179,7 +179,7 @@ _coordinate - (992, 316) + (1030, 195) _rotation @@ -406,7 +406,7 @@ _coordinate - (1432, 188) + (1390, 403) _rotation @@ -457,7 +457,7 @@ _coordinate - (1208, 308) + (1206, 187) _rotation @@ -641,7 +641,7 @@ _coordinate - (1616, 164) + (1590, 379) _rotation @@ -998,6 +998,81 @@ we shift the LO a little further 100e3 + + low_pass_filter + + beta + 6.76 + + + alias + + + + comment + + + + affinity + + + + cutoff_freq + baudrate / 2 + 1500 + + + decim + 1 + + + _enabled + True + + + type + fir_filter_ccf + + + _coordinate + (1007, 363) + + + _rotation + 0 + + + gain + 1 + + + id + low_pass_filter_0 + + + interp + 1 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + samp_rate + 4.0 * baudrate + + + width + 1000 + + + win + firdes.WIN_HAMMING + + osmosdr_source @@ -3088,7 +3163,7 @@ we shift the LO a little further _coordinate - (1384, 300) + (1382, 179) _rotation @@ -3500,6 +3575,12 @@ we shift the LO a little further 0 0 + + low_pass_filter_0 + analog_quadrature_demod_cf_0_0 + 0 + 0 + osmosdr_source_0 blocks_rotator_cc_0 @@ -3526,7 +3607,7 @@ we shift the LO a little further pfb_arb_resampler_xxx_0_0 - analog_quadrature_demod_cf_0_0 + low_pass_filter_0 0 0 diff --git a/apps/flowgraphs/msk_ax25.grc b/apps/flowgraphs/msk_ax25.grc index 321727f..0db7bf8 100644 --- a/apps/flowgraphs/msk_ax25.grc +++ b/apps/flowgraphs/msk_ax25.grc @@ -136,7 +136,7 @@ _coordinate - (1248, 196) + (1214, 419) _rotation @@ -406,7 +406,7 @@ _coordinate - (1432, 188) + (1430, 411) _rotation @@ -641,7 +641,7 @@ _coordinate - (1616, 164) + (1614, 387) _rotation @@ -677,7 +677,7 @@ omega - 2 + 4 type @@ -998,6 +998,81 @@ we shift the LO a little further 100e3 + + low_pass_filter + + beta + 6.76 + + + alias + + + + comment + + + + affinity + + + + cutoff_freq + baudrate / 2 + 1500 + + + decim + 1 + + + _enabled + True + + + type + fir_filter_ccf + + + _coordinate + (1022, 371) + + + _rotation + 0 + + + gain + 1 + + + id + low_pass_filter_0 + + + interp + 1 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + samp_rate + 4.0 * baudrate + + + width + 1000 + + + win + firdes.WIN_HAMMING + + osmosdr_source @@ -2488,7 +2563,7 @@ we shift the LO a little further _coordinate - (984, 172) + (735, 395) _rotation @@ -2496,7 +2571,7 @@ we shift the LO a little further id - pfb_arb_resampler_xxx_0_0 + pfb_arb_resampler_xxx_0_0_0 maxoutbuf @@ -2512,7 +2587,7 @@ we shift the LO a little further rrate - (2.0*baudrate)/audio_samp_rate + (4.0*baudrate)/satnogs.handle_samp_rate_rx(rx_sdr_device, samp_rate_rx) samp_delay @@ -2520,7 +2595,7 @@ we shift the LO a little further atten - 100 + 60 taps @@ -3500,6 +3575,12 @@ we shift the LO a little further 0 0 + + low_pass_filter_0 + analog_quadrature_demod_cf_0_0 + 0 + 0 + osmosdr_source_0 blocks_rotator_cc_0 @@ -3512,12 +3593,6 @@ we shift the LO a little further 0 0 - - pfb_arb_resampler_xxx_0 - pfb_arb_resampler_xxx_0_0 - 0 - 0 - pfb_arb_resampler_xxx_0 satnogs_iq_sink_0 @@ -3531,8 +3606,8 @@ we shift the LO a little further 0 - pfb_arb_resampler_xxx_0_0 - analog_quadrature_demod_cf_0_0 + pfb_arb_resampler_xxx_0_0_0 + low_pass_filter_0 0 0 @@ -3566,6 +3641,12 @@ we shift the LO a little further 0 0 + + satnogs_coarse_doppler_correction_cc_0 + pfb_arb_resampler_xxx_0_0_0 + 0 + 0 + satnogs_quad_demod_filter_ff_0 digital_binary_slicer_fb_0 diff --git a/apps/flowgraphs/satnogs_fsk_ax25.py b/apps/flowgraphs/satnogs_fsk_ax25.py index 363a8a1..acdef68 100755 --- a/apps/flowgraphs/satnogs_fsk_ax25.py +++ b/apps/flowgraphs/satnogs_fsk_ax25.py @@ -5,7 +5,7 @@ # Title: satnogs_fsk_ax25 # Author: Manolis Surligas (surligas@gmail.com) # Description: Generic FSK AX.25 decoder -# Generated: Fri Jan 4 20:33:37 2019 +# Generated: Sun Jan 6 14:19:34 2019 ################################################## from gnuradio import analog @@ -96,6 +96,8 @@ class satnogs_fsk_ax25(gr.top_block): self.osmosdr_source_0.set_antenna(satnogs.handle_rx_antenna(rx_sdr_device, antenna), 0) self.osmosdr_source_0.set_bandwidth(satnogs.handle_samp_rate_rx(rx_sdr_device, samp_rate_rx), 0) + self.low_pass_filter_0 = filter.fir_filter_ccf(1, firdes.low_pass( + 1, 4.0 * baudrate, baudrate / 2 + 1500, 1000, firdes.WIN_HAMMING, 6.76)) self.digital_clock_recovery_mm_xx_0 = digital.clock_recovery_mm_ff(4, 0.25*0.175*0.175, 0.5, 0.175, 0.005) self.digital_binary_slicer_fb_0 = digital.binary_slicer_fb() self.dc_blocker_xx_0_0 = filter.dc_blocker_ff(1024, True) @@ -122,11 +124,12 @@ class satnogs_fsk_ax25(gr.top_block): self.connect((self.digital_binary_slicer_fb_0, 0), (self.satnogs_ax25_decoder_bm_0, 0)) self.connect((self.digital_binary_slicer_fb_0, 0), (self.satnogs_ax25_decoder_bm_0_0, 0)) self.connect((self.digital_clock_recovery_mm_xx_0, 0), (self.satnogs_quad_demod_filter_ff_0, 0)) + self.connect((self.low_pass_filter_0, 0), (self.analog_quadrature_demod_cf_0_0, 0)) self.connect((self.osmosdr_source_0, 0), (self.blocks_rotator_cc_0, 0)) self.connect((self.pfb_arb_resampler_xxx_0, 0), (self.analog_quadrature_demod_cf_0_0_0, 0)) self.connect((self.pfb_arb_resampler_xxx_0, 0), (self.satnogs_iq_sink_0, 0)) self.connect((self.pfb_arb_resampler_xxx_0, 0), (self.satnogs_waterfall_sink_0, 0)) - self.connect((self.pfb_arb_resampler_xxx_0_0, 0), (self.analog_quadrature_demod_cf_0_0, 0)) + self.connect((self.pfb_arb_resampler_xxx_0_0, 0), (self.low_pass_filter_0, 0)) self.connect((self.satnogs_coarse_doppler_correction_cc_0, 0), (self.pfb_arb_resampler_xxx_0, 0)) self.connect((self.satnogs_coarse_doppler_correction_cc_0, 0), (self.pfb_arb_resampler_xxx_0_0, 0)) self.connect((self.satnogs_quad_demod_filter_ff_0, 0), (self.digital_binary_slicer_fb_0, 0)) @@ -144,6 +147,7 @@ class satnogs_fsk_ax25(gr.top_block): def set_baudrate(self, baudrate): self.baudrate = baudrate self.pfb_arb_resampler_xxx_0_0.set_rate((4.0*self.baudrate)/satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx)) + self.low_pass_filter_0.set_taps(firdes.low_pass(1, 4.0 * self.baudrate, self.baudrate / 2 + 1500, 1000, firdes.WIN_HAMMING, 6.76)) def get_bb_gain(self): return self.bb_gain diff --git a/apps/flowgraphs/satnogs_msk_ax25.py b/apps/flowgraphs/satnogs_msk_ax25.py index 09fe10c..7d12bf6 100755 --- a/apps/flowgraphs/satnogs_msk_ax25.py +++ b/apps/flowgraphs/satnogs_msk_ax25.py @@ -5,7 +5,7 @@ # Title: satnogs_msk_ax25 # Author: Manolis Surligas (surligas@gmail.com) # Description: Generic MSK AX.25 decoder -# Generated: Fri Aug 17 00:38:15 2018 +# Generated: Sun Jan 6 14:17:55 2019 ################################################## from gnuradio import analog @@ -71,11 +71,11 @@ class satnogs_msk_ax25(gr.top_block): self.satnogs_coarse_doppler_correction_cc_0 = satnogs.coarse_doppler_correction_cc(rx_freq, satnogs.handle_samp_rate_rx(rx_sdr_device, samp_rate_rx)) self.satnogs_ax25_decoder_bm_0_0 = satnogs.ax25_decoder_bm('GND', 0, True, False, 1024) self.satnogs_ax25_decoder_bm_0 = satnogs.ax25_decoder_bm('GND', 0, True, True, 1024) - self.pfb_arb_resampler_xxx_0_0 = pfb.arb_resampler_ccf( - (2.0*baudrate)/audio_samp_rate, + self.pfb_arb_resampler_xxx_0_0_0 = pfb.arb_resampler_ccf( + (4.0*baudrate)/satnogs.handle_samp_rate_rx(rx_sdr_device, samp_rate_rx), taps=None, flt_size=32) - self.pfb_arb_resampler_xxx_0_0.declare_sample_delay(0) + self.pfb_arb_resampler_xxx_0_0_0.declare_sample_delay(0) self.pfb_arb_resampler_xxx_0 = pfb.arb_resampler_ccf( audio_samp_rate/satnogs.handle_samp_rate_rx(rx_sdr_device, samp_rate_rx), @@ -96,7 +96,9 @@ class satnogs_msk_ax25(gr.top_block): self.osmosdr_source_0.set_antenna(satnogs.handle_rx_antenna(rx_sdr_device, antenna), 0) self.osmosdr_source_0.set_bandwidth(satnogs.handle_samp_rate_rx(rx_sdr_device, samp_rate_rx), 0) - self.digital_clock_recovery_mm_xx_0 = digital.clock_recovery_mm_ff(2, 0.25*0.175*0.175, 0.5, 0.175, 0.005) + self.low_pass_filter_0 = filter.fir_filter_ccf(1, firdes.low_pass( + 1, 4.0 * baudrate, baudrate / 2 + 1500, 1000, firdes.WIN_HAMMING, 6.76)) + self.digital_clock_recovery_mm_xx_0 = digital.clock_recovery_mm_ff(4, 0.25*0.175*0.175, 0.5, 0.175, 0.005) self.digital_binary_slicer_fb_0 = digital.binary_slicer_fb() self.dc_blocker_xx_0_0 = filter.dc_blocker_ff(1024, True) self.dc_blocker_xx_0 = filter.dc_blocker_ff(1024, True) @@ -122,13 +124,14 @@ class satnogs_msk_ax25(gr.top_block): self.connect((self.digital_binary_slicer_fb_0, 0), (self.satnogs_ax25_decoder_bm_0, 0)) self.connect((self.digital_binary_slicer_fb_0, 0), (self.satnogs_ax25_decoder_bm_0_0, 0)) self.connect((self.digital_clock_recovery_mm_xx_0, 0), (self.satnogs_quad_demod_filter_ff_0, 0)) + self.connect((self.low_pass_filter_0, 0), (self.analog_quadrature_demod_cf_0_0, 0)) self.connect((self.osmosdr_source_0, 0), (self.blocks_rotator_cc_0, 0)) self.connect((self.pfb_arb_resampler_xxx_0, 0), (self.analog_quadrature_demod_cf_0_0_0, 0)) - self.connect((self.pfb_arb_resampler_xxx_0, 0), (self.pfb_arb_resampler_xxx_0_0, 0)) self.connect((self.pfb_arb_resampler_xxx_0, 0), (self.satnogs_iq_sink_0, 0)) self.connect((self.pfb_arb_resampler_xxx_0, 0), (self.satnogs_waterfall_sink_0, 0)) - self.connect((self.pfb_arb_resampler_xxx_0_0, 0), (self.analog_quadrature_demod_cf_0_0, 0)) + self.connect((self.pfb_arb_resampler_xxx_0_0_0, 0), (self.low_pass_filter_0, 0)) self.connect((self.satnogs_coarse_doppler_correction_cc_0, 0), (self.pfb_arb_resampler_xxx_0, 0)) + self.connect((self.satnogs_coarse_doppler_correction_cc_0, 0), (self.pfb_arb_resampler_xxx_0_0_0, 0)) self.connect((self.satnogs_quad_demod_filter_ff_0, 0), (self.digital_binary_slicer_fb_0, 0)) def get_antenna(self): @@ -143,7 +146,8 @@ class satnogs_msk_ax25(gr.top_block): def set_baudrate(self, baudrate): self.baudrate = baudrate - self.pfb_arb_resampler_xxx_0_0.set_rate((2.0*self.baudrate)/self.audio_samp_rate) + self.pfb_arb_resampler_xxx_0_0_0.set_rate((4.0*self.baudrate)/satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx)) + self.low_pass_filter_0.set_taps(firdes.low_pass(1, 4.0 * self.baudrate, self.baudrate / 2 + 1500, 1000, firdes.WIN_HAMMING, 6.76)) def get_bb_gain(self): return self.bb_gain @@ -236,6 +240,7 @@ class satnogs_msk_ax25(gr.top_block): def set_rx_sdr_device(self, rx_sdr_device): self.rx_sdr_device = rx_sdr_device + self.pfb_arb_resampler_xxx_0_0_0.set_rate((4.0*self.baudrate)/satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx)) self.pfb_arb_resampler_xxx_0.set_rate(self.audio_samp_rate/satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx)) self.osmosdr_source_0.set_sample_rate(satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx)) self.osmosdr_source_0.set_gain(satnogs.handle_rx_rf_gain(self.rx_sdr_device, self.rf_gain), 0) @@ -250,6 +255,7 @@ class satnogs_msk_ax25(gr.top_block): def set_samp_rate_rx(self, samp_rate_rx): self.samp_rate_rx = samp_rate_rx + self.pfb_arb_resampler_xxx_0_0_0.set_rate((4.0*self.baudrate)/satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx)) self.pfb_arb_resampler_xxx_0.set_rate(self.audio_samp_rate/satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx)) self.osmosdr_source_0.set_sample_rate(satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx)) self.osmosdr_source_0.set_bandwidth(satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx), 0) @@ -278,7 +284,6 @@ class satnogs_msk_ax25(gr.top_block): def set_audio_samp_rate(self, audio_samp_rate): self.audio_samp_rate = audio_samp_rate - self.pfb_arb_resampler_xxx_0_0.set_rate((2.0*self.baudrate)/self.audio_samp_rate) self.pfb_arb_resampler_xxx_0.set_rate(self.audio_samp_rate/satnogs.handle_samp_rate_rx(self.rx_sdr_device, self.samp_rate_rx)) From 4bc8c1cacab404a76ca93103c232e6fbdb2393e9 Mon Sep 17 00:00:00 2001 From: Manolis Surligas Date: Tue, 22 Jan 2019 13:25:39 +0200 Subject: [PATCH 04/12] Bump up version to 1.5.1 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 19bc950..e9849bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,7 +48,7 @@ list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake/Modules) # Set the version information here set(VERSION_INFO_MAJOR_VERSION 1) set(VERSION_INFO_API_COMPAT 5) -set(VERSION_INFO_MINOR_VERSION 0) +set(VERSION_INFO_MINOR_VERSION 1) set(VERSION_INFO_MAINT_VERSION git) From 4c2fec066d2d130ef7d7c3ed138b6a47a1791a23 Mon Sep 17 00:00:00 2001 From: Manolis Surligas Date: Wed, 18 Jul 2018 13:39:00 +0300 Subject: [PATCH 05/12] Add contributors list and update versioning scheme --- CONTRIBUTORS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index e95f764..602f7e8 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -8,11 +8,11 @@ # Contributors * Corey Shields (cshields@gmail.com) -* LongHairedHacker(sebastian@sebastians-site.de) +* Sebastian Schumb (sebastian@sebastians-site.de) * Nikos Karamolegkos (nkaramolegos@csd.uoc.g) * Mark Jessop (vk5qi@rfhead.net) * Fabian P. Schmidt (kerel-fs@gmx.de) * Thanos Gkiolias (agiolias@csd.uoc.gr) * Patrick Dohmen (dl4pd@darc.de) * Kostis Triantayllakis (ctriant@csd.uoc.gr) -* Alexander Jenke \ No newline at end of file +* Alexander Jenke From a6993f98b1c69d2aa0cb8a305322fc3cbd8955a5 Mon Sep 17 00:00:00 2001 From: LongHairedHacker Date: Sat, 30 Mar 2019 18:03:40 +0100 Subject: [PATCH 06/12] Added module Added sync detection to module --- grc/CMakeLists.txt | 9 ++- grc/satnogs_sstv_pd120_sink.xml | 31 ++++++++ include/satnogs/CMakeLists.txt | 7 +- include/satnogs/sstv_pd120_sink.h | 56 ++++++++++++++ lib/CMakeLists.txt | 3 +- lib/sstv_pd120_sink_impl.cc | 123 ++++++++++++++++++++++++++++++ lib/sstv_pd120_sink_impl.h | 55 +++++++++++++ swig/satnogs_swig0.i | 4 + 8 files changed, 280 insertions(+), 8 deletions(-) create mode 100644 grc/satnogs_sstv_pd120_sink.xml create mode 100644 include/satnogs/sstv_pd120_sink.h create mode 100644 lib/sstv_pd120_sink_impl.cc create mode 100644 lib/sstv_pd120_sink_impl.h diff --git a/grc/CMakeLists.txt b/grc/CMakeLists.txt index 01034c8..d56f76a 100644 --- a/grc/CMakeLists.txt +++ b/grc/CMakeLists.txt @@ -24,7 +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 + satnogs_cw_encoder.xml ) list(APPEND enabled_blocks @@ -36,11 +36,11 @@ list(APPEND enabled_blocks satnogs_fox_telem_mm.xml satnogs_frame_file_sink.xml satnogs_iq_sink.xml - satnogs_lrpt_decoder.xml - satnogs_lrpt_sync.xml + satnogs_lrpt_decoder.xml + satnogs_lrpt_sync.xml satnogs_morse_decoder.xml satnogs_multi_format_msg_sink.xml - satnogs_ogg_encoder.xml + satnogs_ogg_encoder.xml satnogs_cw_to_symbol.xml satnogs_udp_msg_source.xml satnogs_tcp_rigctl_msg_source.xml @@ -53,6 +53,7 @@ list(APPEND enabled_blocks satnogs_noaa_apt_sink.xml satnogs_whitening.xml satnogs_frame_acquisition.xml + satnogs_sstv_pd120_sink.xml ) if(${INCLUDE_DEBUG_BLOCKS}) diff --git a/grc/satnogs_sstv_pd120_sink.xml b/grc/satnogs_sstv_pd120_sink.xml new file mode 100644 index 0000000..95f830a --- /dev/null +++ b/grc/satnogs_sstv_pd120_sink.xml @@ -0,0 +1,31 @@ + + SSTV PD120 sink + satnogs_sstv_pd120_sink + [SATNOGS] + import satnogs + satnogs.sstv_pd120_sink($filename_png, $split) + + Output PNG Filename + filename_png + + file_save + + + Split + split + False + bool + + + + + in + float + + diff --git a/include/satnogs/CMakeLists.txt b/include/satnogs/CMakeLists.txt index 7af04b4..0ec4861 100644 --- a/include/satnogs/CMakeLists.txt +++ b/include/satnogs/CMakeLists.txt @@ -24,7 +24,7 @@ list(APPEND DEBUG_HEADER_FILES morse_debug_source.h debug_msg_source_raw.h debug_msg_source.h - cw_encoder.h + cw_encoder.h ) list(APPEND HEADER_FILES @@ -37,7 +37,7 @@ list(APPEND HEADER_FILES morse.h morse_decoder.h multi_format_msg_sink.h - ogg_encoder.h + ogg_encoder.h cw_to_symbol.h utils.h udp_msg_source.h @@ -59,6 +59,7 @@ list(APPEND HEADER_FILES noaa_apt_sink.h frame_file_sink.h iq_sink.h + sstv_pd120_sink.h ) if(${INCLUDE_DEBUG_BLOCKS}) @@ -76,4 +77,4 @@ install(FILES frame_acquisition.h shift_reg.h golay24.h DESTINATION include/satnogs -) \ No newline at end of file +) diff --git a/include/satnogs/sstv_pd120_sink.h b/include/satnogs/sstv_pd120_sink.h new file mode 100644 index 0000000..f1a47e4 --- /dev/null +++ b/include/satnogs/sstv_pd120_sink.h @@ -0,0 +1,56 @@ +/* -*- c++ -*- */ +/* + * gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module + * + * Copyright (C) 2017, Libre Space Foundation + * + * 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 . + */ + + + +#ifndef INCLUDED_SATNOGS_SSTV_PD120_SINK_H +#define INCLUDED_SATNOGS_SSTV_PD120_SINK_H + +#include +#include + +namespace gr { + namespace satnogs { + + /*! + * \brief <+description of block+> + * \ingroup satnogs + * + */ + class SATNOGS_API sstv_pd120_sink : virtual public gr::sync_block + { + public: + typedef boost::shared_ptr sptr; + + /*! + * \brief Return a shared_ptr to a new instance of satnogs::sstv_pd120_sink. + * + * To avoid accidental use of raw pointers, satnogs::sstv_pd120_sink's + * constructor is in a private implementation + * class. satnogs::sstv_pd120_sink::make is the public interface for + * creating new instances. + */ + static sptr make(const char *filename_png, bool split); + }; + + } // namespace satnogs +} // namespace gr + +#endif /* INCLUDED_SATNOGS_SSTV_PD120_SINK_H */ diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 998fa24..e71ce2d 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -74,7 +74,8 @@ list(APPEND satnogs_sources lrpt_decoder_impl.cc frame_acquisition_impl.cc shift_reg.cc - golay24.cc) + golay24.cc + sstv_pd120_sink_impl.cc) if(${INCLUDE_DEBUG_BLOCKS}) list(APPEND satnogs_sources ${satnogs_debug_sources}) diff --git a/lib/sstv_pd120_sink_impl.cc b/lib/sstv_pd120_sink_impl.cc new file mode 100644 index 0000000..8750ae5 --- /dev/null +++ b/lib/sstv_pd120_sink_impl.cc @@ -0,0 +1,123 @@ +/* -*- c++ -*- */ +/* + * gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module + * + * Copyright (C) 2018, Libre Space Foundation + * + * 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 . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "sstv_pd120_sink_impl.h" + +namespace gr { + namespace satnogs { + + const size_t image_width = 640; + const size_t sync_length = 105; + const size_t sync_thresh = 100; + const size_t porch_length = 10; + const size_t line_length = sync_length + porch_length + 4 * image_width; + + const float max_dev = 600; + const float center = 1750; + const float min_freq = 1200; + const float max_freq = 2300; + + const float color_low = 1500; + const float color_high = max_freq; + + + sstv_pd120_sink::sptr + sstv_pd120_sink::make(const char *filename_png, bool split) + { + return gnuradio::get_initial_sptr + (new sstv_pd120_sink_impl(filename_png, split)); + } + + /* + * The private constructor + */ + sstv_pd120_sink_impl::sstv_pd120_sink_impl(const char *filename_png, bool split) + : gr::sync_block("sstv_pd120_sink", + gr::io_signature::make (1, 1, sizeof(float)), + gr::io_signature::make (0, 0, 0)), + d_filename_png (filename_png), + d_split (split), + d_has_sync(false) + { + set_history(sync_length); + d_line = new float[line_length]; + } + + /* + * Our virtual destructor. + */ + sstv_pd120_sink_impl::~sstv_pd120_sink_impl() + { + delete[] d_line; + } + + float + sstv_pd120_sink_impl::to_frequency(float sample) { + float freq = center + sample * max_dev; + freq = std::max(min_freq, freq); + freq = std::min(max_freq, freq); + return freq; + } + + bool + sstv_pd120_sink_impl::is_sync(size_t pos, const float *samples) { + size_t count = 0; + for(size_t i = 0; i < sync_length; i++) { + float sample = to_frequency(samples[pos - (sync_length - 1) + i]); + if(sample < color_low) { + count += 1; + } + } + + bool res = count > sync_length && !d_has_sync; + d_has_sync = count > sync_length; + + std::cout << "Count: " << count << " " << d_has_sync << std::endl; + + return res; + } + + int + sstv_pd120_sink_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const float *in = (const float *) input_items[0]; + + std::cout << "foo" << std::endl; + for (size_t i = sync_length - 1; + i < noutput_items + sync_length - 1; i++) { + + if(is_sync(i, in)) { + std::cout << "Sync: " << i << std::endl; + } + } + + // Tell runtime system how many output items we produced. + return noutput_items; + } + + } /* namespace satnogs */ +} /* namespace gr */ diff --git a/lib/sstv_pd120_sink_impl.h b/lib/sstv_pd120_sink_impl.h new file mode 100644 index 0000000..b831563 --- /dev/null +++ b/lib/sstv_pd120_sink_impl.h @@ -0,0 +1,55 @@ +/* -*- c++ -*- */ +/* + * gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module + * + * Copyright (C) 2018, Libre Space Foundation + * + * 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 . + */ + + +#ifndef INCLUDED_SATNOGS_SSTV_PD120_SINK_IMPL_H +#define INCLUDED_SATNOGS_SSTV_PD120_SINK_IMPL_H + +#include + +namespace gr { + namespace satnogs { + + class sstv_pd120_sink_impl : public sstv_pd120_sink + { + private: + std::string d_filename_png; + bool d_split; + bool d_has_sync; + + float *d_line; + + float to_frequency(float sample); + bool is_sync(size_t pos, const float *samples); + + public: + sstv_pd120_sink_impl(const char *filename_png, bool split); + ~sstv_pd120_sink_impl(); + + // Where all the action really happens + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } // namespace satnogs +} // namespace gr + +#endif /* INCLUDED_SATNOGS_SSTV_PD120_SINK_IMPL_H */ diff --git a/swig/satnogs_swig0.i b/swig/satnogs_swig0.i index 40d4064..b9933b9 100644 --- a/swig/satnogs_swig0.i +++ b/swig/satnogs_swig0.i @@ -33,6 +33,7 @@ #include "satnogs/ogg_encoder.h" #include "satnogs/ogg_source.h" #include "satnogs/noaa_apt_sink.h" +#include "satnogs/sstv_pd120_sink.h" #include "satnogs/frame_file_sink.h" #include "satnogs/iq_sink.h" #include "satnogs/quad_demod_filter_ff.h" @@ -103,6 +104,9 @@ GR_SWIG_BLOCK_MAGIC2(satnogs, ogg_source); %include "satnogs/noaa_apt_sink.h" GR_SWIG_BLOCK_MAGIC2(satnogs, noaa_apt_sink); +%include "satnogs/sstv_pd120_sink.h" +GR_SWIG_BLOCK_MAGIC2(satnogs, sstv_pd120_sink); + %include "satnogs/frame_file_sink.h" GR_SWIG_BLOCK_MAGIC2(satnogs, frame_file_sink); From 37c94698c08df7d2da5d73e090e37426b9b88589 Mon Sep 17 00:00:00 2001 From: LongHairedHacker Date: Sun, 31 Mar 2019 17:35:28 +0200 Subject: [PATCH 07/12] Added color conversion and stub for rendering --- lib/sstv_pd120_sink_impl.cc | 82 +++++++++++++++++++++++++++++++++---- lib/sstv_pd120_sink_impl.h | 12 ++++++ 2 files changed, 87 insertions(+), 7 deletions(-) diff --git a/lib/sstv_pd120_sink_impl.cc b/lib/sstv_pd120_sink_impl.cc index 8750ae5..ba79f96 100644 --- a/lib/sstv_pd120_sink_impl.cc +++ b/lib/sstv_pd120_sink_impl.cc @@ -29,6 +29,7 @@ namespace gr { namespace satnogs { const size_t image_width = 640; + const size_t image_height = 496; const size_t sync_length = 105; const size_t sync_thresh = 100; const size_t porch_length = 10; @@ -59,7 +60,10 @@ namespace gr { gr::io_signature::make (0, 0, 0)), d_filename_png (filename_png), d_split (split), - d_has_sync(false) + d_has_sync(false), + d_initial_sync(true), + d_line_pos(0), + d_image_y (0) { set_history(sync_length); d_line = new float[line_length]; @@ -81,6 +85,33 @@ namespace gr { return freq; } + int + sstv_pd120_sink_impl::to_color(float sample) { + sample = (sample - color_low) / (color_high - color_low); + int color = int(sample * 255); + color = std::max(color, 0); + color = std::min(color, 255); + return color; + } + + void + ycbcr_to_rgb(int ycbcr[3], int rgb[3]) { + int y = ycbcr[0]; + int cb = ycbcr[1]; + int cr = ycbcr[2]; + + //https://stackoverflow.com/questions/4041840/function-to-convert-ycbcr-to-rgb/15333019#15333019 + cr = cr - 128; + cb = cb - 128; + int r = y + 45 * cr / 32; + int g = y - (11 * cb + 23 * cr) / 32; + int b = y + 113 * cb / 64; + + rgb[0] = r; + rgb[1] = g; + rgb[2] = b; + } + bool sstv_pd120_sink_impl::is_sync(size_t pos, const float *samples) { size_t count = 0; @@ -91,14 +122,42 @@ namespace gr { } } - bool res = count > sync_length && !d_has_sync; - d_has_sync = count > sync_length; + bool res = count > sync_thresh && !d_has_sync; + d_has_sync = count > sync_thresh; - std::cout << "Count: " << count << " " << d_has_sync << std::endl; + d_initial_sync = false; return res; } + + void + sstv_pd120_sink_impl::render_line() { + int start_pos = d_line_pos - sync_length - 4 * image_width; + if(start_pos < 0) { + return; + } + + for(size_t x = 0; x < image_width; x++) { + int y0 = to_color(d_line[start_pos + x]); + int cr = to_color(d_line[start_pos + image_width + x]); + int cb = to_color(d_line[start_pos + 2 * image_width + x]); + int y1 = to_color(d_line[start_pos + 3 * image_width + x]); + + int rgb0[3]; + int ycrcb0[] = {y0, cr, cb}; + ycbcr_to_rgb(ycrcb0, rgb0); + + int rgb1[3]; + int ycrcb1[] = {y1, cr, cb}; + ycbcr_to_rgb(ycrcb1, rgb1); + + //Todo: Write pixels + } + + d_image_y += 2; + } + int sstv_pd120_sink_impl::work(int noutput_items, gr_vector_const_void_star &input_items, @@ -106,12 +165,21 @@ namespace gr { { const float *in = (const float *) input_items[0]; - std::cout << "foo" << std::endl; for (size_t i = sync_length - 1; i < noutput_items + sync_length - 1; i++) { - if(is_sync(i, in)) { - std::cout << "Sync: " << i << std::endl; + float sample = to_frequency(in[i]); + d_line[d_line_pos] = sample; + d_line_pos++; + + if(is_sync(i, in) || d_line_pos >= line_length) { + if(d_initial_sync) { + d_image_y = 0; + } + else if(!d_initial_sync && d_line_pos > line_length - porch_length) { + std::cout << "Rendering after: " << d_line_pos << std::endl; + } + d_line_pos = 0; } } diff --git a/lib/sstv_pd120_sink_impl.h b/lib/sstv_pd120_sink_impl.h index b831563..ddb47d2 100644 --- a/lib/sstv_pd120_sink_impl.h +++ b/lib/sstv_pd120_sink_impl.h @@ -24,6 +24,9 @@ #include +#define PNG_DEBUG 3 +#include + namespace gr { namespace satnogs { @@ -33,12 +36,21 @@ namespace gr { std::string d_filename_png; bool d_split; bool d_has_sync; + bool d_initial_sync; float *d_line; + size_t d_line_pos; + size_t d_image_y; + + png::image d_image; float to_frequency(float sample); + int to_color(float sample); + void ycbcr_to_rgb(int ycbcr[3], int rgb[3]); bool is_sync(size_t pos, const float *samples); + void render_line(); + public: sstv_pd120_sink_impl(const char *filename_png, bool split); ~sstv_pd120_sink_impl(); From f0e1125b24843ff0e09460e518a59c63bd3cb590 Mon Sep 17 00:00:00 2001 From: LongHairedHacker Date: Mon, 1 Apr 2019 00:03:58 +0200 Subject: [PATCH 08/12] First version that renders images Dropped split parameter --- grc/satnogs_sstv_pd120_sink.xml | 16 +------------- include/satnogs/sstv_pd120_sink.h | 2 +- lib/sstv_pd120_sink_impl.cc | 35 ++++++++++++++++++------------- lib/sstv_pd120_sink_impl.h | 3 +-- 4 files changed, 23 insertions(+), 33 deletions(-) diff --git a/grc/satnogs_sstv_pd120_sink.xml b/grc/satnogs_sstv_pd120_sink.xml index 95f830a..a034929 100644 --- a/grc/satnogs_sstv_pd120_sink.xml +++ b/grc/satnogs_sstv_pd120_sink.xml @@ -3,27 +3,13 @@ satnogs_sstv_pd120_sink [SATNOGS] import satnogs - satnogs.sstv_pd120_sink($filename_png, $split) + satnogs.sstv_pd120_sink($filename_png) Output PNG Filename filename_png file_save - - Split - split - False - bool - - - in float diff --git a/include/satnogs/sstv_pd120_sink.h b/include/satnogs/sstv_pd120_sink.h index f1a47e4..891eb74 100644 --- a/include/satnogs/sstv_pd120_sink.h +++ b/include/satnogs/sstv_pd120_sink.h @@ -47,7 +47,7 @@ namespace gr { * class. satnogs::sstv_pd120_sink::make is the public interface for * creating new instances. */ - static sptr make(const char *filename_png, bool split); + static sptr make(const char *filename_png); }; } // namespace satnogs diff --git a/lib/sstv_pd120_sink_impl.cc b/lib/sstv_pd120_sink_impl.cc index ba79f96..5cb050c 100644 --- a/lib/sstv_pd120_sink_impl.cc +++ b/lib/sstv_pd120_sink_impl.cc @@ -45,21 +45,20 @@ namespace gr { sstv_pd120_sink::sptr - sstv_pd120_sink::make(const char *filename_png, bool split) + sstv_pd120_sink::make(const char *filename_png) { return gnuradio::get_initial_sptr - (new sstv_pd120_sink_impl(filename_png, split)); + (new sstv_pd120_sink_impl(filename_png)); } /* * The private constructor */ - sstv_pd120_sink_impl::sstv_pd120_sink_impl(const char *filename_png, bool split) + sstv_pd120_sink_impl::sstv_pd120_sink_impl(const char *filename_png) : gr::sync_block("sstv_pd120_sink", gr::io_signature::make (1, 1, sizeof(float)), gr::io_signature::make (0, 0, 0)), d_filename_png (filename_png), - d_split (split), d_has_sync(false), d_initial_sync(true), d_line_pos(0), @@ -67,6 +66,7 @@ namespace gr { { set_history(sync_length); d_line = new float[line_length]; + d_image = png::image(image_width, image_height); } /* @@ -88,14 +88,14 @@ namespace gr { int sstv_pd120_sink_impl::to_color(float sample) { sample = (sample - color_low) / (color_high - color_low); - int color = int(sample * 255); - color = std::max(color, 0); - color = std::min(color, 255); - return color; + sample = sample * 255.0f; + sample = std::max(sample, 0.0f); + sample = std::min(sample, 255.0f); + return int(sample); } void - ycbcr_to_rgb(int ycbcr[3], int rgb[3]) { + sstv_pd120_sink_impl::ycbcr_to_rgb(int ycbcr[3], int rgb[3]) { int y = ycbcr[0]; int cb = ycbcr[1]; int cr = ycbcr[2]; @@ -144,18 +144,22 @@ namespace gr { int cb = to_color(d_line[start_pos + 2 * image_width + x]); int y1 = to_color(d_line[start_pos + 3 * image_width + x]); - int rgb0[3]; - int ycrcb0[] = {y0, cr, cb}; + int rgb0[3] = {0, 0, 0}; + int ycrcb0[] = {y0, cb, cr}; ycbcr_to_rgb(ycrcb0, rgb0); - int rgb1[3]; - int ycrcb1[] = {y1, cr, cb}; - ycbcr_to_rgb(ycrcb1, rgb1); + d_image.set_pixel(x, d_image_y, png::rgb_pixel(rgb0[0], rgb0[1], rgb0[2])); - //Todo: Write pixels + int rgb1[] = {0, 0, 0}; + int ycrcb1[] = {y1, cb, cr}; + ycbcr_to_rgb(ycrcb1, rgb1); + d_image.set_pixel(x, d_image_y + 1, png::rgb_pixel(rgb1[0], rgb1[1], rgb1[2])); } d_image_y += 2; + + std::cout << "Writing " << d_filename_png << std::endl; + d_image.write(d_filename_png); } int @@ -178,6 +182,7 @@ namespace gr { } else if(!d_initial_sync && d_line_pos > line_length - porch_length) { std::cout << "Rendering after: " << d_line_pos << std::endl; + render_line(); } d_line_pos = 0; } diff --git a/lib/sstv_pd120_sink_impl.h b/lib/sstv_pd120_sink_impl.h index ddb47d2..329f4a1 100644 --- a/lib/sstv_pd120_sink_impl.h +++ b/lib/sstv_pd120_sink_impl.h @@ -34,7 +34,6 @@ namespace gr { { private: std::string d_filename_png; - bool d_split; bool d_has_sync; bool d_initial_sync; @@ -52,7 +51,7 @@ namespace gr { void render_line(); public: - sstv_pd120_sink_impl(const char *filename_png, bool split); + sstv_pd120_sink_impl(const char *filename_png); ~sstv_pd120_sink_impl(); // Where all the action really happens From dd44916d5424f53355f49a442e46e6633cdc093e Mon Sep 17 00:00:00 2001 From: LongHairedHacker Date: Mon, 1 Apr 2019 00:27:53 +0200 Subject: [PATCH 09/12] Fixed intial sync, clamped rgb values --- lib/.clang_complete | 1 + lib/sstv_pd120_sink_impl.cc | 11 +++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 lib/.clang_complete diff --git a/lib/.clang_complete b/lib/.clang_complete new file mode 100644 index 0000000..b817a53 --- /dev/null +++ b/lib/.clang_complete @@ -0,0 +1 @@ +-I.. diff --git a/lib/sstv_pd120_sink_impl.cc b/lib/sstv_pd120_sink_impl.cc index 5cb050c..a25bf84 100644 --- a/lib/sstv_pd120_sink_impl.cc +++ b/lib/sstv_pd120_sink_impl.cc @@ -107,9 +107,9 @@ namespace gr { int g = y - (11 * cb + 23 * cr) / 32; int b = y + 113 * cb / 64; - rgb[0] = r; - rgb[1] = g; - rgb[2] = b; + rgb[0] = std::min(255, std::max(r, 0)); + rgb[1] = std::min(255, std::max(g, 0)); + rgb[2] = std::min(255, std::max(b, 0)); } bool @@ -117,6 +117,7 @@ namespace gr { size_t count = 0; for(size_t i = 0; i < sync_length; i++) { float sample = to_frequency(samples[pos - (sync_length - 1) + i]); + //std::cout << sample << std::endl; if(sample < color_low) { count += 1; } @@ -125,7 +126,9 @@ namespace gr { bool res = count > sync_thresh && !d_has_sync; d_has_sync = count > sync_thresh; - d_initial_sync = false; + if(res) { + d_initial_sync = false; + } return res; } From 7901ee150ae9e640e31fee091f40d2b9e1c780cd Mon Sep 17 00:00:00 2001 From: LongHairedHacker Date: Thu, 4 Apr 2019 20:29:29 +0200 Subject: [PATCH 10/12] Splitting images works --- grc/satnogs_sstv_pd120_sink.xml | 2 +- lib/sstv_pd120_sink_impl.cc | 17 ++++++++++++++--- lib/sstv_pd120_sink_impl.h | 1 + 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/grc/satnogs_sstv_pd120_sink.xml b/grc/satnogs_sstv_pd120_sink.xml index a034929..077853c 100644 --- a/grc/satnogs_sstv_pd120_sink.xml +++ b/grc/satnogs_sstv_pd120_sink.xml @@ -1,7 +1,7 @@ SSTV PD120 sink satnogs_sstv_pd120_sink - [SATNOGS] + [satnogs] import satnogs satnogs.sstv_pd120_sink($filename_png) diff --git a/lib/sstv_pd120_sink_impl.cc b/lib/sstv_pd120_sink_impl.cc index a25bf84..d3674bd 100644 --- a/lib/sstv_pd120_sink_impl.cc +++ b/lib/sstv_pd120_sink_impl.cc @@ -62,7 +62,8 @@ namespace gr { d_has_sync(false), d_initial_sync(true), d_line_pos(0), - d_image_y (0) + d_image_y (0), + d_num_image(0) { set_history(sync_length); d_line = new float[line_length]; @@ -159,10 +160,20 @@ namespace gr { d_image.set_pixel(x, d_image_y + 1, png::rgb_pixel(rgb1[0], rgb1[1], rgb1[2])); } + std::string file_name = std::string(d_filename_png) + "_" + std::to_string(d_num_image) + ".png"; + std::cout << "Writing " << file_name << std::endl; + d_image.write(file_name.c_str()); + + std::cout << "Line number: " << d_image_y << std::endl; + d_image_y += 2; - std::cout << "Writing " << d_filename_png << std::endl; - d_image.write(d_filename_png); + if(d_image_y >= image_height){ + std::cout << "Finished image, resetting." << std::endl; + d_image_y = 0; + d_initial_sync = true; + d_num_image++; + } } int diff --git a/lib/sstv_pd120_sink_impl.h b/lib/sstv_pd120_sink_impl.h index 329f4a1..36d734a 100644 --- a/lib/sstv_pd120_sink_impl.h +++ b/lib/sstv_pd120_sink_impl.h @@ -40,6 +40,7 @@ namespace gr { float *d_line; size_t d_line_pos; size_t d_image_y; + size_t d_num_image; png::image d_image; From f3fd67f6f53d29eb22c9c2ab710b9d5daca42236 Mon Sep 17 00:00:00 2001 From: LongHairedHacker Date: Fri, 5 Apr 2019 23:33:45 +0200 Subject: [PATCH 11/12] Added flowgraph --- apps/CMakeLists.txt | 1 + apps/flowgraphs/satnogs_sstv_pd120_demod.py | 356 ++ apps/flowgraphs/sstv_pd120_demod.grc | 3319 +++++++++++++++++++ 3 files changed, 3676 insertions(+) create mode 100755 apps/flowgraphs/satnogs_sstv_pd120_demod.py create mode 100644 apps/flowgraphs/sstv_pd120_demod.grc diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index b54538f..332ac66 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -37,5 +37,6 @@ GR_PYTHON_INSTALL( flowgraphs/satnogs_fsk_ax25.py flowgraphs/satnogs_msk_ax25.py flowgraphs/satnogs_bpsk_ax25.py + flowgraphs/satnogs_sstv_pd120_demod.py DESTINATION bin ) diff --git a/apps/flowgraphs/satnogs_sstv_pd120_demod.py b/apps/flowgraphs/satnogs_sstv_pd120_demod.py new file mode 100755 index 0000000..6315b41 --- /dev/null +++ b/apps/flowgraphs/satnogs_sstv_pd120_demod.py @@ -0,0 +1,356 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- +################################################## +# GNU Radio Python Flow Graph +# Title: SSTV PD120 Demodulation +# Author: Sebastian Schumb (sebastian@sebastains-site.de) +# Description: A PD120 SSTV demodulator +# Generated: Fri Apr 5 23:30:50 2019 +################################################## + +from gnuradio import analog +from gnuradio import eng_notation +from gnuradio import filter +from gnuradio import gr +from gnuradio.eng_option import eng_option +from gnuradio.filter import firdes +from optparse import OptionParser +import math +import osmosdr +import satnogs +import time + + +class satnogs_sstv_pd120_demod(gr.top_block): + + def __init__(self, antenna=satnogs.not_set_antenna, bb_gain=satnogs.not_set_rx_bb_gain, decoded_data_file_path='/tmp/.satnogs/data/data', dev_args=satnogs.not_set_dev_args, doppler_correction_per_sec=20, enable_iq_dump=0, file_path='test.ogg', if_gain=satnogs.not_set_rx_if_gain, iq_file_path='/tmp/iq.dat', lo_offset=100e3, ppm=0, rf_gain=satnogs.not_set_rx_rf_gain, rigctl_port=4532, rx_freq=100e6, rx_sdr_device='usrpb200', waterfall_file_path='/tmp/waterfall.dat'): + gr.top_block.__init__(self, "SSTV PD120 Demodulation") + + ################################################## + # Parameters + ################################################## + self.antenna = antenna + self.bb_gain = bb_gain + self.decoded_data_file_path = decoded_data_file_path + self.dev_args = dev_args + self.doppler_correction_per_sec = doppler_correction_per_sec + self.enable_iq_dump = enable_iq_dump + self.file_path = file_path + self.if_gain = if_gain + self.iq_file_path = iq_file_path + self.lo_offset = lo_offset + self.ppm = ppm + self.rf_gain = rf_gain + self.rigctl_port = rigctl_port + self.rx_freq = rx_freq + self.rx_sdr_device = rx_sdr_device + self.waterfall_file_path = waterfall_file_path + + ################################################## + # Variables + ################################################## + self.samp_rate_rx = samp_rate_rx = satnogs.hw_rx_settings[rx_sdr_device]['samp_rate'] + self.xlate_filter_taps = xlate_filter_taps = firdes.low_pass(1, samp_rate_rx, 125000, 25000, firdes.WIN_HAMMING, 6.76) + + self.taps = taps = firdes.low_pass(12.0, samp_rate_rx, 100e3, 60000, firdes.WIN_HAMMING, 6.76) + + self.max_modulation_freq = max_modulation_freq = 3000 + self.filter_rate = filter_rate = 250000 + self.deviation = deviation = 5000 + self.audio_samp_rate = audio_samp_rate = 48000 + + ################################################## + # Blocks + ################################################## + self.satnogs_waterfall_sink_0 = satnogs.waterfall_sink(audio_samp_rate, 0.0, 10, 1024, waterfall_file_path, 1) + self.satnogs_tcp_rigctl_msg_source_0 = satnogs.tcp_rigctl_msg_source("127.0.0.1", rigctl_port, False, 1000/doppler_correction_per_sec, 1500) + self.satnogs_sstv_pd120_sink_0 = satnogs.sstv_pd120_sink(decoded_data_file_path) + self.satnogs_ogg_encoder_0 = satnogs.ogg_encoder(file_path, audio_samp_rate, 1.0) + self.satnogs_iq_sink_0 = satnogs.iq_sink(32767, iq_file_path, False, enable_iq_dump) + self.satnogs_coarse_doppler_correction_cc_0 = satnogs.coarse_doppler_correction_cc(rx_freq, samp_rate_rx) + self.rational_resampler_xxx_0 = filter.rational_resampler_fff( + interpolation=5263, + decimation=12000, + taps=None, + fractional_bw=None, + ) + self.osmosdr_source_0 = osmosdr.source( args="numchan=" + str(1) + " " + satnogs.handle_rx_dev_args(rx_sdr_device, dev_args) ) + self.osmosdr_source_0.set_sample_rate(samp_rate_rx) + self.osmosdr_source_0.set_center_freq(rx_freq - lo_offset, 0) + self.osmosdr_source_0.set_freq_corr(ppm, 0) + self.osmosdr_source_0.set_dc_offset_mode(2, 0) + self.osmosdr_source_0.set_iq_balance_mode(0, 0) + self.osmosdr_source_0.set_gain_mode(False, 0) + self.osmosdr_source_0.set_gain(satnogs.handle_rx_rf_gain(rx_sdr_device, rf_gain), 0) + self.osmosdr_source_0.set_if_gain(satnogs.handle_rx_if_gain(rx_sdr_device, if_gain), 0) + self.osmosdr_source_0.set_bb_gain(satnogs.handle_rx_bb_gain(rx_sdr_device, bb_gain), 0) + self.osmosdr_source_0.set_antenna(satnogs.handle_rx_antenna(rx_sdr_device, antenna), 0) + self.osmosdr_source_0.set_bandwidth(samp_rate_rx, 0) + + self.low_pass_filter_0_0 = filter.fir_filter_fff(1, firdes.low_pass( + 1, 12000, 1500, 1000, firdes.WIN_HAMMING, 6.76)) + self.low_pass_filter_0 = filter.fir_filter_ccf(1, firdes.low_pass( + 1, audio_samp_rate, deviation+max_modulation_freq, 3000, firdes.WIN_HAMMING, 6.76)) + self.freq_xlating_fir_filter_xxx_0_0 = filter.freq_xlating_fir_filter_ccc(4, (63, ), 1750, audio_samp_rate) + self.freq_xlating_fir_filter_xxx_0 = filter.freq_xlating_fir_filter_ccc(int(samp_rate_rx/filter_rate), (xlate_filter_taps), lo_offset, samp_rate_rx) + self.blks2_rational_resampler_xxx_1 = filter.rational_resampler_ccc( + interpolation=24, + decimation=125, + taps=None, + fractional_bw=None, + ) + self.analog_quadrature_demod_cf_0 = analog.quadrature_demod_cf((2*math.pi*deviation)/audio_samp_rate) + self.analog_nbfm_rx_0 = analog.nbfm_rx( + audio_rate=12000, + quad_rate=12000, + tau=75e-6, + max_dev=600, + ) + + + + ################################################## + # Connections + ################################################## + self.msg_connect((self.satnogs_tcp_rigctl_msg_source_0, 'freq'), (self.satnogs_coarse_doppler_correction_cc_0, 'freq')) + self.connect((self.analog_nbfm_rx_0, 0), (self.low_pass_filter_0_0, 0)) + self.connect((self.analog_quadrature_demod_cf_0, 0), (self.satnogs_ogg_encoder_0, 0)) + self.connect((self.blks2_rational_resampler_xxx_1, 0), (self.low_pass_filter_0, 0)) + self.connect((self.blks2_rational_resampler_xxx_1, 0), (self.satnogs_iq_sink_0, 0)) + self.connect((self.blks2_rational_resampler_xxx_1, 0), (self.satnogs_waterfall_sink_0, 0)) + self.connect((self.freq_xlating_fir_filter_xxx_0, 0), (self.blks2_rational_resampler_xxx_1, 0)) + self.connect((self.freq_xlating_fir_filter_xxx_0_0, 0), (self.analog_nbfm_rx_0, 0)) + self.connect((self.low_pass_filter_0, 0), (self.analog_quadrature_demod_cf_0, 0)) + self.connect((self.low_pass_filter_0, 0), (self.freq_xlating_fir_filter_xxx_0_0, 0)) + self.connect((self.low_pass_filter_0_0, 0), (self.rational_resampler_xxx_0, 0)) + self.connect((self.osmosdr_source_0, 0), (self.satnogs_coarse_doppler_correction_cc_0, 0)) + self.connect((self.rational_resampler_xxx_0, 0), (self.satnogs_sstv_pd120_sink_0, 0)) + self.connect((self.satnogs_coarse_doppler_correction_cc_0, 0), (self.freq_xlating_fir_filter_xxx_0, 0)) + + def get_antenna(self): + return self.antenna + + def set_antenna(self, antenna): + self.antenna = antenna + self.osmosdr_source_0.set_antenna(satnogs.handle_rx_antenna(self.rx_sdr_device, self.antenna), 0) + + def get_bb_gain(self): + return self.bb_gain + + def set_bb_gain(self, bb_gain): + self.bb_gain = bb_gain + self.osmosdr_source_0.set_bb_gain(satnogs.handle_rx_bb_gain(self.rx_sdr_device, self.bb_gain), 0) + + def get_decoded_data_file_path(self): + return self.decoded_data_file_path + + def set_decoded_data_file_path(self, decoded_data_file_path): + self.decoded_data_file_path = decoded_data_file_path + + def get_dev_args(self): + return self.dev_args + + def set_dev_args(self, dev_args): + self.dev_args = dev_args + + def get_doppler_correction_per_sec(self): + return self.doppler_correction_per_sec + + def set_doppler_correction_per_sec(self, doppler_correction_per_sec): + self.doppler_correction_per_sec = doppler_correction_per_sec + + def get_enable_iq_dump(self): + return self.enable_iq_dump + + def set_enable_iq_dump(self, enable_iq_dump): + self.enable_iq_dump = enable_iq_dump + + def get_file_path(self): + return self.file_path + + def set_file_path(self, file_path): + self.file_path = file_path + + def get_if_gain(self): + return self.if_gain + + def set_if_gain(self, if_gain): + self.if_gain = if_gain + self.osmosdr_source_0.set_if_gain(satnogs.handle_rx_if_gain(self.rx_sdr_device, self.if_gain), 0) + + def get_iq_file_path(self): + return self.iq_file_path + + def set_iq_file_path(self, iq_file_path): + self.iq_file_path = iq_file_path + + def get_lo_offset(self): + return self.lo_offset + + def set_lo_offset(self, lo_offset): + self.lo_offset = lo_offset + self.osmosdr_source_0.set_center_freq(self.rx_freq - self.lo_offset, 0) + self.freq_xlating_fir_filter_xxx_0.set_center_freq(self.lo_offset) + + def get_ppm(self): + return self.ppm + + def set_ppm(self, ppm): + self.ppm = ppm + self.osmosdr_source_0.set_freq_corr(self.ppm, 0) + + def get_rf_gain(self): + return self.rf_gain + + def set_rf_gain(self, rf_gain): + self.rf_gain = rf_gain + self.osmosdr_source_0.set_gain(satnogs.handle_rx_rf_gain(self.rx_sdr_device, self.rf_gain), 0) + + def get_rigctl_port(self): + return self.rigctl_port + + def set_rigctl_port(self, rigctl_port): + self.rigctl_port = rigctl_port + + def get_rx_freq(self): + return self.rx_freq + + def set_rx_freq(self, rx_freq): + self.rx_freq = rx_freq + self.satnogs_coarse_doppler_correction_cc_0.set_new_freq_locked(self.rx_freq) + self.osmosdr_source_0.set_center_freq(self.rx_freq - self.lo_offset, 0) + + def get_rx_sdr_device(self): + return self.rx_sdr_device + + def set_rx_sdr_device(self, rx_sdr_device): + self.rx_sdr_device = rx_sdr_device + self.set_samp_rate_rx(satnogs.hw_rx_settings[self.rx_sdr_device]['samp_rate']) + self.osmosdr_source_0.set_gain(satnogs.handle_rx_rf_gain(self.rx_sdr_device, self.rf_gain), 0) + self.osmosdr_source_0.set_if_gain(satnogs.handle_rx_if_gain(self.rx_sdr_device, self.if_gain), 0) + self.osmosdr_source_0.set_bb_gain(satnogs.handle_rx_bb_gain(self.rx_sdr_device, self.bb_gain), 0) + self.osmosdr_source_0.set_antenna(satnogs.handle_rx_antenna(self.rx_sdr_device, self.antenna), 0) + + def get_waterfall_file_path(self): + return self.waterfall_file_path + + def set_waterfall_file_path(self, waterfall_file_path): + self.waterfall_file_path = waterfall_file_path + + def get_samp_rate_rx(self): + return self.samp_rate_rx + + def set_samp_rate_rx(self, samp_rate_rx): + self.samp_rate_rx = samp_rate_rx + self.set_xlate_filter_taps(firdes.low_pass(1, self.samp_rate_rx, 125000, 25000, firdes.WIN_HAMMING, 6.76)) + self.osmosdr_source_0.set_sample_rate(self.samp_rate_rx) + self.osmosdr_source_0.set_bandwidth(self.samp_rate_rx, 0) + + def get_xlate_filter_taps(self): + return self.xlate_filter_taps + + def set_xlate_filter_taps(self, xlate_filter_taps): + self.xlate_filter_taps = xlate_filter_taps + self.freq_xlating_fir_filter_xxx_0.set_taps((self.xlate_filter_taps)) + + def get_taps(self): + return self.taps + + def set_taps(self, taps): + self.taps = taps + + def get_max_modulation_freq(self): + return self.max_modulation_freq + + def set_max_modulation_freq(self, max_modulation_freq): + self.max_modulation_freq = max_modulation_freq + self.low_pass_filter_0.set_taps(firdes.low_pass(1, self.audio_samp_rate, self.deviation+self.max_modulation_freq, 3000, firdes.WIN_HAMMING, 6.76)) + + def get_filter_rate(self): + return self.filter_rate + + def set_filter_rate(self, filter_rate): + self.filter_rate = filter_rate + + def get_deviation(self): + return self.deviation + + def set_deviation(self, deviation): + self.deviation = deviation + self.low_pass_filter_0.set_taps(firdes.low_pass(1, self.audio_samp_rate, self.deviation+self.max_modulation_freq, 3000, firdes.WIN_HAMMING, 6.76)) + self.analog_quadrature_demod_cf_0.set_gain((2*math.pi*self.deviation)/self.audio_samp_rate) + + def get_audio_samp_rate(self): + return self.audio_samp_rate + + def set_audio_samp_rate(self, audio_samp_rate): + self.audio_samp_rate = audio_samp_rate + self.low_pass_filter_0.set_taps(firdes.low_pass(1, self.audio_samp_rate, self.deviation+self.max_modulation_freq, 3000, firdes.WIN_HAMMING, 6.76)) + self.analog_quadrature_demod_cf_0.set_gain((2*math.pi*self.deviation)/self.audio_samp_rate) + + +def argument_parser(): + description = 'A PD120 SSTV demodulator' + parser = OptionParser(usage="%prog: [options]", option_class=eng_option, description=description) + parser.add_option( + "", "--antenna", dest="antenna", type="string", default=satnogs.not_set_antenna, + help="Set antenna [default=%default]") + parser.add_option( + "", "--bb-gain", dest="bb_gain", type="eng_float", default=eng_notation.num_to_str(satnogs.not_set_rx_bb_gain), + help="Set bb_gain [default=%default]") + parser.add_option( + "", "--decoded-data-file-path", dest="decoded_data_file_path", type="string", default='/tmp/.satnogs/data/data', + help="Set decoded_data_file_path [default=%default]") + parser.add_option( + "", "--dev-args", dest="dev_args", type="string", default=satnogs.not_set_dev_args, + help="Set dev_args [default=%default]") + parser.add_option( + "", "--doppler-correction-per-sec", dest="doppler_correction_per_sec", type="intx", default=20, + help="Set doppler_correction_per_sec [default=%default]") + parser.add_option( + "", "--enable-iq-dump", dest="enable_iq_dump", type="intx", default=0, + help="Set enable_iq_dump [default=%default]") + parser.add_option( + "", "--file-path", dest="file_path", type="string", default='test.ogg', + help="Set file_path [default=%default]") + parser.add_option( + "", "--if-gain", dest="if_gain", type="eng_float", default=eng_notation.num_to_str(satnogs.not_set_rx_if_gain), + help="Set if_gain [default=%default]") + parser.add_option( + "", "--iq-file-path", dest="iq_file_path", type="string", default='/tmp/iq.dat', + help="Set iq_file_path [default=%default]") + parser.add_option( + "", "--lo-offset", dest="lo_offset", type="eng_float", default=eng_notation.num_to_str(100e3), + help="Set lo_offset [default=%default]") + parser.add_option( + "", "--ppm", dest="ppm", type="intx", default=0, + help="Set ppm [default=%default]") + parser.add_option( + "", "--rf-gain", dest="rf_gain", type="eng_float", default=eng_notation.num_to_str(satnogs.not_set_rx_rf_gain), + help="Set rf_gain [default=%default]") + parser.add_option( + "", "--rigctl-port", dest="rigctl_port", type="intx", default=4532, + help="Set rigctl_port [default=%default]") + parser.add_option( + "", "--rx-freq", dest="rx_freq", type="eng_float", default=eng_notation.num_to_str(100e6), + help="Set rx_freq [default=%default]") + parser.add_option( + "", "--rx-sdr-device", dest="rx_sdr_device", type="string", default='usrpb200', + help="Set rx_sdr_device [default=%default]") + parser.add_option( + "", "--waterfall-file-path", dest="waterfall_file_path", type="string", default='/tmp/waterfall.dat', + help="Set waterfall_file_path [default=%default]") + return parser + + +def main(top_block_cls=satnogs_sstv_pd120_demod, options=None): + if options is None: + options, _ = argument_parser().parse_args() + + tb = top_block_cls(antenna=options.antenna, bb_gain=options.bb_gain, decoded_data_file_path=options.decoded_data_file_path, dev_args=options.dev_args, doppler_correction_per_sec=options.doppler_correction_per_sec, enable_iq_dump=options.enable_iq_dump, file_path=options.file_path, if_gain=options.if_gain, iq_file_path=options.iq_file_path, lo_offset=options.lo_offset, ppm=options.ppm, rf_gain=options.rf_gain, rigctl_port=options.rigctl_port, rx_freq=options.rx_freq, rx_sdr_device=options.rx_sdr_device, waterfall_file_path=options.waterfall_file_path) + tb.start() + tb.wait() + + +if __name__ == '__main__': + main() diff --git a/apps/flowgraphs/sstv_pd120_demod.grc b/apps/flowgraphs/sstv_pd120_demod.grc new file mode 100644 index 0000000..27e5998 --- /dev/null +++ b/apps/flowgraphs/sstv_pd120_demod.grc @@ -0,0 +1,3319 @@ + + + + Thu May 5 00:22:45 2016 + + options + + author + Sebastian Schumb (sebastian@sebastains-site.de) + + + window_size + 3000, 3000 + + + category + Custom + + + comment + + + + description + A PD120 SSTV demodulator + + + _enabled + True + + + _coordinate + (8, 8) + + + _rotation + 0 + + + generate_options + no_gui + + + hier_block_src_path + .: + + + id + satnogs_sstv_pd120_demod + + + max_nouts + 0 + + + qt_qss_theme + + + + realtime_scheduling + + + + run_command + {python} -u {filename} + + + run_options + run + + + run + True + + + sizing_mode + fixed + + + thread_safe_setters + + + + title + SSTV PD120 Demodulation + + + placement + (0,0) + + + + variable + + comment + + + + _enabled + True + + + _coordinate + (606, 785) + + + _rotation + 0 + + + id + audio_samp_rate + + + value + 48000 + + + + variable + + comment + + + + _enabled + True + + + _coordinate + (223, 793) + + + _rotation + 0 + + + id + deviation + + + value + 5000 + + + + variable + + comment + + + + _enabled + True + + + _coordinate + (741, 785) + + + _rotation + 0 + + + id + filter_rate + + + value + 250000 + + + + variable + + comment + + + + _enabled + True + + + _coordinate + (318, 793) + + + _rotation + 0 + + + id + max_modulation_freq + + + value + 3000 + + + + variable + + comment + SDR device +TX sampling rate + + + _enabled + True + + + _coordinate + (478, 745) + + + _rotation + 0 + + + id + samp_rate_rx + + + value + satnogs.hw_rx_settings[rx_sdr_device]['samp_rate'] + + + + variable_low_pass_filter_taps + + beta + 6.76 + + + comment + + + + cutoff_freq + 100e3 + + + _enabled + 1 + + + _coordinate + (48, 716) + + + _rotation + 0 + + + gain + 12.0 + + + id + taps + + + samp_rate + samp_rate_rx + + + width + 60000 + + + win + firdes.WIN_HAMMING + + + + variable + + comment + + + + _enabled + True + + + _coordinate + (837, 785) + + + _rotation + 0 + + + id + xlate_filter_taps + + + value + firdes.low_pass(1, samp_rate_rx, 125000, 25000, firdes.WIN_HAMMING, 6.76) + + + + analog_nbfm_rx + + audio_rate + 12000 + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (964, 562) + + + _rotation + 180 + + + id + analog_nbfm_rx_0 + + + max_dev + 600 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + quad_rate + 12000 + + + tau + 75e-6 + + + + analog_quadrature_demod_cf + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (1204, 474) + + + _rotation + 180 + + + gain + (2*math.pi*deviation)/audio_samp_rate + + + id + analog_quadrature_demod_cf_0 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + + parameter + + alias + + + + comment + + + + _enabled + True + + + _coordinate + (422, 945) + + + _rotation + 0 + + + id + antenna + + + label + + + + short_id + + + + hide + none + + + type + string + + + value + satnogs.not_set_antenna + + + + parameter + + alias + + + + comment + + + + _enabled + True + + + _coordinate + (334, 945) + + + _rotation + 0 + + + id + bb_gain + + + label + + + + short_id + + + + hide + none + + + type + eng_float + + + value + satnogs.not_set_rx_bb_gain + + + + rational_resampler_xxx + + alias + + + + comment + + + + affinity + + + + decim + 125 + + + _enabled + True + + + fbw + 0 + + + _coordinate + (837, 267) + + + _rotation + 0 + + + id + blks2_rational_resampler_xxx_1 + + + interp + 24 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + taps + [] + + + type + ccc + + + + parameter + + alias + + + + comment + + + + _enabled + True + + + _coordinate + (741, 945) + + + _rotation + 0 + + + id + decoded_data_file_path + + + label + + + + short_id + + + + hide + none + + + type + string + + + value + /tmp/.satnogs/data/data + + + + parameter + + alias + + + + comment + + + + _enabled + True + + + _coordinate + (47, 945) + + + _rotation + 0 + + + id + dev_args + + + label + + + + short_id + + + + hide + none + + + type + string + + + value + satnogs.not_set_dev_args + + + + parameter + + alias + + + + comment + + + + _enabled + True + + + _coordinate + (47, 865) + + + _rotation + 0 + + + id + doppler_correction_per_sec + + + label + + + + short_id + + + + hide + none + + + type + intx + + + value + 20 + + + + parameter + + alias + + + + comment + + + + _enabled + True + + + _coordinate + (510, 945) + + + _rotation + 0 + + + id + enable_iq_dump + + + label + + + + short_id + + + + hide + none + + + type + intx + + + value + 0 + + + + parameter + + alias + + + + comment + + + + _enabled + True + + + _coordinate + (534, 865) + + + _rotation + 180 + + + id + file_path + + + label + + + + short_id + + + + hide + none + + + type + string + + + value + test.ogg + + + + freq_xlating_fir_filter_xxx + + alias + + + + center_freq + lo_offset + + + comment + + + + affinity + + + + decim + int(samp_rate_rx/filter_rate) + + + _enabled + 1 + + + _coordinate + (566, 267) + + + _rotation + 0 + + + id + freq_xlating_fir_filter_xxx_0 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + samp_rate + samp_rate_rx + + + taps + xlate_filter_taps + + + type + ccc + + + + freq_xlating_fir_filter_xxx + + alias + + + + center_freq + 1750 + + + comment + + + + affinity + + + + decim + 4 + + + _enabled + True + + + _coordinate + (1164, 562) + + + _rotation + 180 + + + id + freq_xlating_fir_filter_xxx_0_0 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + samp_rate + audio_samp_rate + + + taps + 63 + + + type + ccc + + + + parameter + + alias + + + + comment + + + + _enabled + True + + + _coordinate + (247, 945) + + + _rotation + 0 + + + id + if_gain + + + label + + + + short_id + + + + hide + none + + + type + eng_float + + + value + satnogs.not_set_rx_if_gain + + + + parameter + + alias + + + + comment + + + + _enabled + True + + + _coordinate + (630, 945) + + + _rotation + 0 + + + id + iq_file_path + + + label + + + + short_id + + + + hide + none + + + type + string + + + value + /tmp/iq.dat + + + + parameter + + alias + + + + comment + To avoid the SDR carrier at the DC +we shift the LO a little further + + + _enabled + True + + + _coordinate + (909, 865) + + + _rotation + 0 + + + id + lo_offset + + + label + + + + short_id + + + + hide + none + + + type + eng_float + + + value + 100e3 + + + + low_pass_filter + + beta + 6.76 + + + alias + + + + comment + Using the Carson bandwidth rule for filter width: +width = 2*(deviation + max_modulation_freq). +For the majority of FM transmissions we can expect +max_modulation_freq = 3000 + + + affinity + + + + cutoff_freq + deviation+max_modulation_freq + + + decim + 1 + + + _enabled + True + + + type + fir_filter_ccf + + + _coordinate + (1060, 243) + + + _rotation + 0 + + + gain + 1 + + + id + low_pass_filter_0 + + + interp + 1 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + samp_rate + audio_samp_rate + + + width + 3000 + + + win + firdes.WIN_HAMMING + + + + low_pass_filter + + beta + 6.76 + + + alias + + + + comment + + + + affinity + + + + cutoff_freq + 1500 + + + decim + 1 + + + _enabled + True + + + type + fir_filter_fff + + + _coordinate + (781, 538) + + + _rotation + 180 + + + gain + 1 + + + id + low_pass_filter_0_0 + + + interp + 1 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + samp_rate + 12000 + + + width + 1000 + + + win + firdes.WIN_HAMMING + + + + osmosdr_source + + alias + + + + ant0 + satnogs.handle_rx_antenna(rx_sdr_device, antenna) + + + bb_gain0 + satnogs.handle_rx_bb_gain(rx_sdr_device, bb_gain) + + + bw0 + samp_rate_rx + + + dc_offset_mode0 + 2 + + + corr0 + ppm + + + freq0 + rx_freq - lo_offset + + + gain_mode0 + False + + + if_gain0 + satnogs.handle_rx_if_gain(rx_sdr_device, if_gain) + + + iq_balance_mode0 + 0 + + + gain0 + satnogs.handle_rx_rf_gain(rx_sdr_device, rf_gain) + + + ant10 + + + + bb_gain10 + 20 + + + bw10 + 0 + + + dc_offset_mode10 + 0 + + + corr10 + 0 + + + freq10 + 100e6 + + + gain_mode10 + False + + + if_gain10 + 20 + + + iq_balance_mode10 + 0 + + + gain10 + 10 + + + ant11 + + + + bb_gain11 + 20 + + + bw11 + 0 + + + dc_offset_mode11 + 0 + + + corr11 + 0 + + + freq11 + 100e6 + + + gain_mode11 + False + + + if_gain11 + 20 + + + iq_balance_mode11 + 0 + + + gain11 + 10 + + + ant12 + + + + bb_gain12 + 20 + + + bw12 + 0 + + + dc_offset_mode12 + 0 + + + corr12 + 0 + + + freq12 + 100e6 + + + gain_mode12 + False + + + if_gain12 + 20 + + + iq_balance_mode12 + 0 + + + gain12 + 10 + + + ant13 + + + + bb_gain13 + 20 + + + bw13 + 0 + + + dc_offset_mode13 + 0 + + + corr13 + 0 + + + freq13 + 100e6 + + + gain_mode13 + False + + + if_gain13 + 20 + + + iq_balance_mode13 + 0 + + + gain13 + 10 + + + ant14 + + + + bb_gain14 + 20 + + + bw14 + 0 + + + dc_offset_mode14 + 0 + + + corr14 + 0 + + + freq14 + 100e6 + + + gain_mode14 + False + + + if_gain14 + 20 + + + iq_balance_mode14 + 0 + + + gain14 + 10 + + + ant15 + + + + bb_gain15 + 20 + + + bw15 + 0 + + + dc_offset_mode15 + 0 + + + corr15 + 0 + + + freq15 + 100e6 + + + gain_mode15 + False + + + if_gain15 + 20 + + + iq_balance_mode15 + 0 + + + gain15 + 10 + + + ant16 + + + + bb_gain16 + 20 + + + bw16 + 0 + + + dc_offset_mode16 + 0 + + + corr16 + 0 + + + freq16 + 100e6 + + + gain_mode16 + False + + + if_gain16 + 20 + + + iq_balance_mode16 + 0 + + + gain16 + 10 + + + ant17 + + + + bb_gain17 + 20 + + + bw17 + 0 + + + dc_offset_mode17 + 0 + + + corr17 + 0 + + + freq17 + 100e6 + + + gain_mode17 + False + + + if_gain17 + 20 + + + iq_balance_mode17 + 0 + + + gain17 + 10 + + + ant18 + + + + bb_gain18 + 20 + + + bw18 + 0 + + + dc_offset_mode18 + 0 + + + corr18 + 0 + + + freq18 + 100e6 + + + gain_mode18 + False + + + if_gain18 + 20 + + + iq_balance_mode18 + 0 + + + gain18 + 10 + + + ant19 + + + + bb_gain19 + 20 + + + bw19 + 0 + + + dc_offset_mode19 + 0 + + + corr19 + 0 + + + freq19 + 100e6 + + + gain_mode19 + False + + + if_gain19 + 20 + + + iq_balance_mode19 + 0 + + + gain19 + 10 + + + ant1 + + + + bb_gain1 + 20 + + + bw1 + 0 + + + dc_offset_mode1 + 0 + + + corr1 + 0 + + + freq1 + 100e6 + + + gain_mode1 + False + + + if_gain1 + 20 + + + iq_balance_mode1 + 0 + + + gain1 + 10 + + + ant20 + + + + bb_gain20 + 20 + + + bw20 + 0 + + + dc_offset_mode20 + 0 + + + corr20 + 0 + + + freq20 + 100e6 + + + gain_mode20 + False + + + if_gain20 + 20 + + + iq_balance_mode20 + 0 + + + gain20 + 10 + + + ant21 + + + + bb_gain21 + 20 + + + bw21 + 0 + + + dc_offset_mode21 + 0 + + + corr21 + 0 + + + freq21 + 100e6 + + + gain_mode21 + False + + + if_gain21 + 20 + + + iq_balance_mode21 + 0 + + + gain21 + 10 + + + ant22 + + + + bb_gain22 + 20 + + + bw22 + 0 + + + dc_offset_mode22 + 0 + + + corr22 + 0 + + + freq22 + 100e6 + + + gain_mode22 + False + + + if_gain22 + 20 + + + iq_balance_mode22 + 0 + + + gain22 + 10 + + + ant23 + + + + bb_gain23 + 20 + + + bw23 + 0 + + + dc_offset_mode23 + 0 + + + corr23 + 0 + + + freq23 + 100e6 + + + gain_mode23 + False + + + if_gain23 + 20 + + + iq_balance_mode23 + 0 + + + gain23 + 10 + + + ant24 + + + + bb_gain24 + 20 + + + bw24 + 0 + + + dc_offset_mode24 + 0 + + + corr24 + 0 + + + freq24 + 100e6 + + + gain_mode24 + False + + + if_gain24 + 20 + + + iq_balance_mode24 + 0 + + + gain24 + 10 + + + ant25 + + + + bb_gain25 + 20 + + + bw25 + 0 + + + dc_offset_mode25 + 0 + + + corr25 + 0 + + + freq25 + 100e6 + + + gain_mode25 + False + + + if_gain25 + 20 + + + iq_balance_mode25 + 0 + + + gain25 + 10 + + + ant26 + + + + bb_gain26 + 20 + + + bw26 + 0 + + + dc_offset_mode26 + 0 + + + corr26 + 0 + + + freq26 + 100e6 + + + gain_mode26 + False + + + if_gain26 + 20 + + + iq_balance_mode26 + 0 + + + gain26 + 10 + + + ant27 + + + + bb_gain27 + 20 + + + bw27 + 0 + + + dc_offset_mode27 + 0 + + + corr27 + 0 + + + freq27 + 100e6 + + + gain_mode27 + False + + + if_gain27 + 20 + + + iq_balance_mode27 + 0 + + + gain27 + 10 + + + ant28 + + + + bb_gain28 + 20 + + + bw28 + 0 + + + dc_offset_mode28 + 0 + + + corr28 + 0 + + + freq28 + 100e6 + + + gain_mode28 + False + + + if_gain28 + 20 + + + iq_balance_mode28 + 0 + + + gain28 + 10 + + + ant29 + + + + bb_gain29 + 20 + + + bw29 + 0 + + + dc_offset_mode29 + 0 + + + corr29 + 0 + + + freq29 + 100e6 + + + gain_mode29 + False + + + if_gain29 + 20 + + + iq_balance_mode29 + 0 + + + gain29 + 10 + + + ant2 + + + + bb_gain2 + 20 + + + bw2 + 0 + + + dc_offset_mode2 + 0 + + + corr2 + 0 + + + freq2 + 100e6 + + + gain_mode2 + False + + + if_gain2 + 20 + + + iq_balance_mode2 + 0 + + + gain2 + 10 + + + ant30 + + + + bb_gain30 + 20 + + + bw30 + 0 + + + dc_offset_mode30 + 0 + + + corr30 + 0 + + + freq30 + 100e6 + + + gain_mode30 + False + + + if_gain30 + 20 + + + iq_balance_mode30 + 0 + + + gain30 + 10 + + + ant31 + + + + bb_gain31 + 20 + + + bw31 + 0 + + + dc_offset_mode31 + 0 + + + corr31 + 0 + + + freq31 + 100e6 + + + gain_mode31 + False + + + if_gain31 + 20 + + + iq_balance_mode31 + 0 + + + gain31 + 10 + + + ant3 + + + + bb_gain3 + 20 + + + bw3 + 0 + + + dc_offset_mode3 + 0 + + + corr3 + 0 + + + freq3 + 100e6 + + + gain_mode3 + False + + + if_gain3 + 20 + + + iq_balance_mode3 + 0 + + + gain3 + 10 + + + ant4 + + + + bb_gain4 + 20 + + + bw4 + 0 + + + dc_offset_mode4 + 0 + + + corr4 + 0 + + + freq4 + 100e6 + + + gain_mode4 + False + + + if_gain4 + 20 + + + iq_balance_mode4 + 0 + + + gain4 + 10 + + + ant5 + + + + bb_gain5 + 20 + + + bw5 + 0 + + + dc_offset_mode5 + 0 + + + corr5 + 0 + + + freq5 + 100e6 + + + gain_mode5 + False + + + if_gain5 + 20 + + + iq_balance_mode5 + 0 + + + gain5 + 10 + + + ant6 + + + + bb_gain6 + 20 + + + bw6 + 0 + + + dc_offset_mode6 + 0 + + + corr6 + 0 + + + freq6 + 100e6 + + + gain_mode6 + False + + + if_gain6 + 20 + + + iq_balance_mode6 + 0 + + + gain6 + 10 + + + ant7 + + + + bb_gain7 + 20 + + + bw7 + 0 + + + dc_offset_mode7 + 0 + + + corr7 + 0 + + + freq7 + 100e6 + + + gain_mode7 + False + + + if_gain7 + 20 + + + iq_balance_mode7 + 0 + + + gain7 + 10 + + + ant8 + + + + bb_gain8 + 20 + + + bw8 + 0 + + + dc_offset_mode8 + 0 + + + corr8 + 0 + + + freq8 + 100e6 + + + gain_mode8 + False + + + if_gain8 + 20 + + + iq_balance_mode8 + 0 + + + gain8 + 10 + + + ant9 + + + + bb_gain9 + 20 + + + bw9 + 0 + + + dc_offset_mode9 + 0 + + + corr9 + 0 + + + freq9 + 100e6 + + + gain_mode9 + False + + + if_gain9 + 20 + + + iq_balance_mode9 + 0 + + + gain9 + 10 + + + comment + + + + affinity + + + + args + satnogs.handle_rx_dev_args(rx_sdr_device, dev_args) + + + _enabled + 1 + + + _coordinate + (16, 376) + + + _rotation + 0 + + + id + osmosdr_source_0 + + + maxoutbuf + 0 + + + clock_source0 + + + + time_source0 + + + + clock_source1 + + + + time_source1 + + + + clock_source2 + + + + time_source2 + + + + clock_source3 + + + + time_source3 + + + + clock_source4 + + + + time_source4 + + + + clock_source5 + + + + time_source5 + + + + clock_source6 + + + + time_source6 + + + + clock_source7 + + + + time_source7 + + + + minoutbuf + 0 + + + nchan + 1 + + + num_mboards + 1 + + + type + fc32 + + + sample_rate + samp_rate_rx + + + sync + + + + + parameter + + alias + + + + comment + + + + _enabled + True + + + _coordinate + (805, 865) + + + _rotation + 0 + + + id + ppm + + + label + + + + short_id + + + + hide + none + + + type + intx + + + value + 0 + + + + rational_resampler_xxx + + alias + + + + comment + + + + affinity + + + + decim + 12000 + + + _enabled + True + + + fbw + 0 + + + _coordinate + (590, 562) + + + _rotation + 180 + + + id + rational_resampler_xxx_0 + + + interp + 5263 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + taps + + + + type + fff + + + + parameter + + alias + + + + comment + + + + _enabled + True + + + _coordinate + (151, 945) + + + _rotation + 0 + + + id + rf_gain + + + label + + + + short_id + + + + hide + none + + + type + eng_float + + + value + satnogs.not_set_rx_rf_gain + + + + parameter + + alias + + + + comment + + + + _enabled + True + + + _coordinate + (334, 865) + + + _rotation + 0 + + + id + rigctl_port + + + label + + + + short_id + + + + hide + none + + + type + intx + + + value + 4532 + + + + parameter + + alias + + + + comment + + + + _enabled + True + + + _coordinate + (438, 865) + + + _rotation + 0 + + + id + rx_freq + + + label + + + + short_id + + + + hide + none + + + type + eng_float + + + value + 100e6 + + + + parameter + + alias + + + + comment + + + + _enabled + True + + + _coordinate + (215, 865) + + + _rotation + 0 + + + id + rx_sdr_device + + + label + + + + short_id + + + + hide + none + + + type + string + + + value + usrpb200 + + + + satnogs_coarse_doppler_correction_cc + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (318, 263) + + + _rotation + 0 + + + id + satnogs_coarse_doppler_correction_cc_0 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + sampling_rate + samp_rate_rx + + + target_freq + rx_freq + + + + satnogs_iq_sink + + append + False + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + filename + iq_file_path + + + _coordinate + (1060, 155) + + + _rotation + 0 + + + id + satnogs_iq_sink_0 + + + scale + 32767 + + + status + enable_iq_dump + + + + satnogs_ogg_encoder + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + filename + file_path + + + _coordinate + (1004, 458) + + + _rotation + 180 + + + id + satnogs_ogg_encoder_0 + + + quality + 1.0 + + + samp_rate + audio_samp_rate + + + + satnogs_sstv_pd120_sink + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (342, 586) + + + _rotation + 180 + + + id + satnogs_sstv_pd120_sink_0 + + + filename_png + decoded_data_file_path + + + + satnogs_tcp_rigctl_msg_source + + alias + + + + comment + + + + affinity + + + + _enabled + 1 + + + _coordinate + (16, 204) + + + _rotation + 0 + + + id + satnogs_tcp_rigctl_msg_source_0 + + + addr + "127.0.0.1" + + + mtu + 1500 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + mode + False + + + port + rigctl_port + + + interval + 1000/doppler_correction_per_sec + + + + satnogs_waterfall_sink + + alias + + + + center_freq + 0.0 + + + comment + + + + affinity + + + + _enabled + True + + + fft_size + 1024 + + + filename + waterfall_file_path + + + _coordinate + (1060, 3) + + + _rotation + 0 + + + id + satnogs_waterfall_sink_0 + + + mode + 1 + + + pps + 10 + + + samp_rate + audio_samp_rate + + + + parameter + + alias + + + + comment + + + + _enabled + True + + + _coordinate + (645, 865) + + + _rotation + 0 + + + id + waterfall_file_path + + + label + + + + short_id + + + + hide + none + + + type + string + + + value + /tmp/waterfall.dat + + + + analog_nbfm_rx_0 + low_pass_filter_0_0 + 0 + 0 + + + analog_quadrature_demod_cf_0 + satnogs_ogg_encoder_0 + 0 + 0 + + + blks2_rational_resampler_xxx_1 + low_pass_filter_0 + 0 + 0 + + + blks2_rational_resampler_xxx_1 + satnogs_iq_sink_0 + 0 + 0 + + + blks2_rational_resampler_xxx_1 + satnogs_waterfall_sink_0 + 0 + 0 + + + freq_xlating_fir_filter_xxx_0 + blks2_rational_resampler_xxx_1 + 0 + 0 + + + freq_xlating_fir_filter_xxx_0_0 + analog_nbfm_rx_0 + 0 + 0 + + + low_pass_filter_0 + analog_quadrature_demod_cf_0 + 0 + 0 + + + low_pass_filter_0 + freq_xlating_fir_filter_xxx_0_0 + 0 + 0 + + + low_pass_filter_0_0 + rational_resampler_xxx_0 + 0 + 0 + + + osmosdr_source_0 + satnogs_coarse_doppler_correction_cc_0 + 0 + 0 + + + rational_resampler_xxx_0 + satnogs_sstv_pd120_sink_0 + 0 + 0 + + + satnogs_coarse_doppler_correction_cc_0 + freq_xlating_fir_filter_xxx_0 + 0 + 0 + + + satnogs_tcp_rigctl_msg_source_0 + satnogs_coarse_doppler_correction_cc_0 + freq + freq + + From 3c0eca695a5cd9f8535614800f4ffa79b4a38e98 Mon Sep 17 00:00:00 2001 From: LongHairedHacker Date: Wed, 5 Jun 2019 19:02:21 +0200 Subject: [PATCH 12/12] Fixed missing FM decoding stage --- apps/flowgraphs/satnogs_sstv_pd120_demod.py | 7 +- apps/flowgraphs/sstv_pd120_demod.grc | 81 ++++++++++++++++++--- 2 files changed, 74 insertions(+), 14 deletions(-) diff --git a/apps/flowgraphs/satnogs_sstv_pd120_demod.py b/apps/flowgraphs/satnogs_sstv_pd120_demod.py index 6315b41..30c17c0 100755 --- a/apps/flowgraphs/satnogs_sstv_pd120_demod.py +++ b/apps/flowgraphs/satnogs_sstv_pd120_demod.py @@ -5,9 +5,10 @@ # Title: SSTV PD120 Demodulation # Author: Sebastian Schumb (sebastian@sebastains-site.de) # Description: A PD120 SSTV demodulator -# Generated: Fri Apr 5 23:30:50 2019 +# GNU Radio version: 3.7.13.5 ################################################## + from gnuradio import analog from gnuradio import eng_notation from gnuradio import filter @@ -91,6 +92,7 @@ class satnogs_sstv_pd120_demod(gr.top_block): 1, 12000, 1500, 1000, firdes.WIN_HAMMING, 6.76)) self.low_pass_filter_0 = filter.fir_filter_ccf(1, firdes.low_pass( 1, audio_samp_rate, deviation+max_modulation_freq, 3000, firdes.WIN_HAMMING, 6.76)) + self.hilbert_fc_0 = filter.hilbert_fc(65, firdes.WIN_HAMMING, 6.76) self.freq_xlating_fir_filter_xxx_0_0 = filter.freq_xlating_fir_filter_ccc(4, (63, ), 1750, audio_samp_rate) self.freq_xlating_fir_filter_xxx_0 = filter.freq_xlating_fir_filter_ccc(int(samp_rate_rx/filter_rate), (xlate_filter_taps), lo_offset, samp_rate_rx) self.blks2_rational_resampler_xxx_1 = filter.rational_resampler_ccc( @@ -114,14 +116,15 @@ class satnogs_sstv_pd120_demod(gr.top_block): ################################################## self.msg_connect((self.satnogs_tcp_rigctl_msg_source_0, 'freq'), (self.satnogs_coarse_doppler_correction_cc_0, 'freq')) self.connect((self.analog_nbfm_rx_0, 0), (self.low_pass_filter_0_0, 0)) + self.connect((self.analog_quadrature_demod_cf_0, 0), (self.hilbert_fc_0, 0)) self.connect((self.analog_quadrature_demod_cf_0, 0), (self.satnogs_ogg_encoder_0, 0)) self.connect((self.blks2_rational_resampler_xxx_1, 0), (self.low_pass_filter_0, 0)) self.connect((self.blks2_rational_resampler_xxx_1, 0), (self.satnogs_iq_sink_0, 0)) self.connect((self.blks2_rational_resampler_xxx_1, 0), (self.satnogs_waterfall_sink_0, 0)) self.connect((self.freq_xlating_fir_filter_xxx_0, 0), (self.blks2_rational_resampler_xxx_1, 0)) self.connect((self.freq_xlating_fir_filter_xxx_0_0, 0), (self.analog_nbfm_rx_0, 0)) + self.connect((self.hilbert_fc_0, 0), (self.freq_xlating_fir_filter_xxx_0_0, 0)) self.connect((self.low_pass_filter_0, 0), (self.analog_quadrature_demod_cf_0, 0)) - self.connect((self.low_pass_filter_0, 0), (self.freq_xlating_fir_filter_xxx_0_0, 0)) self.connect((self.low_pass_filter_0_0, 0), (self.rational_resampler_xxx_0, 0)) self.connect((self.osmosdr_source_0, 0), (self.satnogs_coarse_doppler_correction_cc_0, 0)) self.connect((self.rational_resampler_xxx_0, 0), (self.satnogs_sstv_pd120_sink_0, 0)) diff --git a/apps/flowgraphs/sstv_pd120_demod.grc b/apps/flowgraphs/sstv_pd120_demod.grc index 27e5998..8228e84 100644 --- a/apps/flowgraphs/sstv_pd120_demod.grc +++ b/apps/flowgraphs/sstv_pd120_demod.grc @@ -323,7 +323,7 @@ TX sampling rate _coordinate - (964, 562) + (877, 562) _rotation @@ -374,11 +374,11 @@ TX sampling rate _coordinate - (1204, 474) + (1252, 291) _rotation - 180 + 0 gain @@ -872,7 +872,7 @@ TX sampling rate _coordinate - (1164, 562) + (1076, 562) _rotation @@ -903,6 +903,57 @@ TX sampling rate ccc + + hilbert_fc + + beta + 6.76 + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (1323, 602) + + + _rotation + 180 + + + id + hilbert_fc_0 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + num_taps + 65 + + + win + firdes.WIN_HAMMING + + parameter @@ -1159,7 +1210,7 @@ max_modulation_freq = 3000 _coordinate - (781, 538) + (693, 538) _rotation @@ -2680,7 +2731,7 @@ max_modulation_freq = 3000 _coordinate - (590, 562) + (494, 562) _rotation @@ -3017,11 +3068,11 @@ max_modulation_freq = 3000 _coordinate - (1004, 458) + (1531, 275) _rotation - 180 + 0 id @@ -3056,7 +3107,7 @@ max_modulation_freq = 3000 _coordinate - (342, 586) + (263, 586) _rotation @@ -3238,6 +3289,12 @@ max_modulation_freq = 3000 0 0 + + analog_quadrature_demod_cf_0 + hilbert_fc_0 + 0 + 0 + analog_quadrature_demod_cf_0 satnogs_ogg_encoder_0 @@ -3275,14 +3332,14 @@ max_modulation_freq = 3000 0 - low_pass_filter_0 - analog_quadrature_demod_cf_0 + hilbert_fc_0 + freq_xlating_fir_filter_xxx_0_0 0 0 low_pass_filter_0 - freq_xlating_fir_filter_xxx_0_0 + analog_quadrature_demod_cf_0 0 0