Merge branch 'cw' into 'master'

CW decoder

See merge request librespacefoundation/satnogs/gr-satnogs!117
This commit is contained in:
Manolis Surligas 2017-11-01 20:18:11 +00:00
commit 05eacd4eec
36 changed files with 5530 additions and 1192 deletions

View File

@ -28,6 +28,7 @@ GR_PYTHON_INSTALL(
flowgraphs/satnogs_cw_demod.py
flowgraphs/satnogs_generic_iq_receiver.py
flowgraphs/satnogs_bpsk_demod.py
flowgraphs/satnogs_cw_decoder.py
flowgraphs/satnogs_apt_demod.py
flowgraphs/satnogs_fsk9600_ax25.py
flowgraphs/satnogs_fsk9600_g3ruh_ax25.py

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,426 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
##################################################
# GNU Radio Python Flow Graph
# Title: CW Decoder
# Author: Manolis Surligas (surligas@gmail.com)
# Description: A CW (Morse) Decoder
# Generated: Wed Nov 1 21:56:56 2017
##################################################
if __name__ == '__main__':
import ctypes
import sys
if sys.platform.startswith('linux'):
try:
x11 = ctypes.cdll.LoadLibrary('libX11.so')
x11.XInitThreads()
except:
print "Warning: failed to XInitThreads()"
from PyQt4 import Qt
from gnuradio import analog
from gnuradio import blocks
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 osmosdr
import satnogs
import sys
import time
from gnuradio import qtgui
class satnogs_cw_decoder(gr.top_block, Qt.QWidget):
def __init__(self, antenna=satnogs.not_set_antenna, bb_gain=satnogs.not_set_rx_bb_gain, cw_offset=700, 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', waterfall_file_path='/tmp/waterfall.dat', wpm=20):
gr.top_block.__init__(self, "CW Decoder")
Qt.QWidget.__init__(self)
self.setWindowTitle("CW Decoder")
qtgui.util.check_set_qss()
try:
self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc'))
except:
pass
self.top_scroll_layout = Qt.QVBoxLayout()
self.setLayout(self.top_scroll_layout)
self.top_scroll = Qt.QScrollArea()
self.top_scroll.setFrameStyle(Qt.QFrame.NoFrame)
self.top_scroll_layout.addWidget(self.top_scroll)
self.top_scroll.setWidgetResizable(True)
self.top_widget = Qt.QWidget()
self.top_scroll.setWidget(self.top_widget)
self.top_layout = Qt.QVBoxLayout(self.top_widget)
self.top_grid_layout = Qt.QGridLayout()
self.top_layout.addLayout(self.top_grid_layout)
self.settings = Qt.QSettings("GNU Radio", "satnogs_cw_decoder")
self.restoreGeometry(self.settings.value("geometry").toByteArray())
##################################################
# Parameters
##################################################
self.antenna = antenna
self.bb_gain = bb_gain
self.cw_offset = cw_offset
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
self.wpm = wpm
##################################################
# Variables
##################################################
self.samp_rate_rx = samp_rate_rx = satnogs.hw_rx_settings[rx_sdr_device]['samp_rate']
self.xlating_decimation = xlating_decimation = 5
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.ndelay = ndelay = 250
self.lpf_decimation = lpf_decimation = 5
self.audio_samp_rate = audio_samp_rate = 48000
##################################################
# Blocks
##################################################
self.satnogs_waterfall_sink_0 = satnogs.waterfall_sink((samp_rate_rx/xlating_decimation), 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, 1500)
self.satnogs_ogg_encoder_0 = satnogs.ogg_encoder(file_path, audio_samp_rate, 1.0)
self.satnogs_morse_decoder_0 = satnogs.morse_decoder(ord('#'))
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(int((samp_rate_rx/xlating_decimation)/lpf_decimation), 300000, 0.9, wpm)
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_ccc(
interpolation=audio_samp_rate,
decimation=int((samp_rate_rx/xlating_decimation)/lpf_decimation),
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 = filter.fir_filter_ccf(lpf_decimation, firdes.low_pass(
1, samp_rate_rx/xlating_decimation, 2e3, 500, firdes.WIN_HAMMING, 6.76))
self.freq_xlating_fir_filter_xxx_0 = filter.freq_xlating_fir_filter_ccc(xlating_decimation, (xlate_filter_taps), lo_offset - cw_offset, samp_rate_rx)
self.fir_filter_xxx_0 = filter.fir_filter_ccc(1, ([1,] * ndelay))
self.fir_filter_xxx_0.declare_sample_delay(0)
self.blocks_multiply_conjugate_cc_0 = blocks.multiply_conjugate_cc(1)
self.blocks_moving_average_xx_0 = blocks.moving_average_ff(ndelay, 1, 40)
self.blocks_delay_0 = blocks.delay(gr.sizeof_gr_complex*1, ndelay)
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.analog_agc2_xx_0 = analog.agc2_cc(0.1, 0.8, 0.6, 0.0)
self.analog_agc2_xx_0.set_max_gain(1e3)
##################################################
# Connections
##################################################
self.msg_connect((self.satnogs_cw_to_symbol_0, 'out'), (self.satnogs_morse_decoder_0, 'in'))
self.msg_connect((self.satnogs_morse_decoder_0, 'out'), (self.satnogs_frame_file_sink_0_0, 'frame'))
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.blocks_delay_0, 0))
self.connect((self.analog_agc2_xx_0, 0), (self.blocks_multiply_conjugate_cc_0, 0))
self.connect((self.blocks_complex_to_mag_squared_0, 0), (self.blocks_moving_average_xx_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_moving_average_xx_0, 0), (self.satnogs_cw_to_symbol_0, 0))
self.connect((self.blocks_multiply_conjugate_cc_0, 0), (self.fir_filter_xxx_0, 0))
self.connect((self.fir_filter_xxx_0, 0), (self.blocks_complex_to_mag_squared_0, 0))
self.connect((self.freq_xlating_fir_filter_xxx_0, 0), (self.low_pass_filter_0, 0))
self.connect((self.low_pass_filter_0, 0), (self.analog_agc2_xx_0, 0))
self.connect((self.low_pass_filter_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.blocks_complex_to_real_0, 0))
self.connect((self.rational_resampler_xxx_0, 0), (self.satnogs_iq_sink_0, 0))
self.connect((self.rational_resampler_xxx_0, 0), (self.satnogs_waterfall_sink_0, 0))
self.connect((self.satnogs_coarse_doppler_correction_cc_0, 0), (self.freq_xlating_fir_filter_xxx_0, 0))
def closeEvent(self, event):
self.settings = Qt.QSettings("GNU Radio", "satnogs_cw_decoder")
self.settings.setValue("geometry", self.saveGeometry())
event.accept()
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_cw_offset(self):
return self.cw_offset
def set_cw_offset(self, cw_offset):
self.cw_offset = cw_offset
self.freq_xlating_fir_filter_xxx_0.set_center_freq(self.lo_offset - self.cw_offset)
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 - self.cw_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_wpm(self):
return self.wpm
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.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)
self.low_pass_filter_0.set_taps(firdes.low_pass(1, self.samp_rate_rx/self.xlating_decimation, 2e3, 500, firdes.WIN_HAMMING, 6.76))
def get_xlating_decimation(self):
return self.xlating_decimation
def set_xlating_decimation(self, xlating_decimation):
self.xlating_decimation = xlating_decimation
self.low_pass_filter_0.set_taps(firdes.low_pass(1, self.samp_rate_rx/self.xlating_decimation, 2e3, 500, firdes.WIN_HAMMING, 6.76))
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_ndelay(self):
return self.ndelay
def set_ndelay(self, ndelay):
self.ndelay = ndelay
self.fir_filter_xxx_0.set_taps(([1,] * self.ndelay))
self.blocks_moving_average_xx_0.set_length_and_scale(self.ndelay, 1)
self.blocks_delay_0.set_dly(self.ndelay)
def get_lpf_decimation(self):
return self.lpf_decimation
def set_lpf_decimation(self, lpf_decimation):
self.lpf_decimation = lpf_decimation
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
def argument_parser():
description = 'A CW (Morse) Decoder'
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(
"", "--cw-offset", dest="cw_offset", type="eng_float", default=eng_notation.num_to_str(700),
help="Set cw_offset [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=1000,
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.txt',
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]")
parser.add_option(
"", "--wpm", dest="wpm", type="intx", default=20,
help="Set wpm [default=%default]")
return parser
def main(top_block_cls=satnogs_cw_decoder, options=None):
if options is None:
options, _ = argument_parser().parse_args()
from distutils.version import StrictVersion
if StrictVersion(Qt.qVersion()) >= StrictVersion("4.5.0"):
style = gr.prefs().get_string('qtgui', 'style', 'raster')
Qt.QApplication.setGraphicsSystem(style)
qapp = Qt.QApplication(sys.argv)
tb = top_block_cls(antenna=options.antenna, bb_gain=options.bb_gain, cw_offset=options.cw_offset, 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, wpm=options.wpm)
tb.start()
tb.show()
def quitting():
tb.stop()
tb.wait()
qapp.connect(qapp, Qt.SIGNAL("aboutToQuit()"), quitting)
qapp.exec_()
if __name__ == '__main__':
main()

View File

@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<?grc format='1' created='3.7.9'?>
<?grc format='1' created='3.7.11'?>
<flow_graph>
<timestamp>Sun Jan 17 23:03:00 2016</timestamp>
<block>
@ -10,7 +10,7 @@
</param>
<param>
<key>window_size</key>
<value></value>
<value>2048,1080</value>
</param>
<param>
<key>category</key>
@ -148,15 +148,16 @@
<key>variable</key>
<param>
<key>comment</key>
<value>The frequency of the CW signal</value>
<value>The number of taps depends on the number of samples
of a full period of the CW signal with frequency freq.</value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
<value>1</value>
</param>
<param>
<key>_coordinate</key>
<value>(840, 13)</value>
<value>(552, 13)</value>
</param>
<param>
<key>_rotation</key>
@ -164,11 +165,11 @@
</param>
<param>
<key>id</key>
<value>freq</value>
<value>ndelay</value>
</param>
<param>
<key>value</key>
<value>700.0</value>
<value>100</value>
</param>
</block>
<block>
@ -261,34 +262,6 @@
<value>8000</value>
</param>
</block>
<block>
<key>variable</key>
<param>
<key>comment</key>
<value>The number of taps depends on the number of samples
of a full period of the CW signal with frequency freq.</value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>_coordinate</key>
<value>(552, 13)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>id</key>
<value>taps</value>
</param>
<param>
<key>value</key>
<value>int(math.ceil(samp_rate/freq))</value>
</param>
</block>
<block>
<key>variable</key>
<param>
@ -320,7 +293,7 @@ of a full period of the CW signal with frequency freq.</value>
<key>analog_agc2_xx</key>
<param>
<key>attack_rate</key>
<value>6.25e-3</value>
<value>0.01</value>
</param>
<param>
<key>alias</key>
@ -336,7 +309,7 @@ of a full period of the CW signal with frequency freq.</value>
</param>
<param>
<key>decay_rate</key>
<value>6.25e-3</value>
<value>0.001</value>
</param>
<param>
<key>_enabled</key>
@ -344,7 +317,7 @@ of a full period of the CW signal with frequency freq.</value>
</param>
<param>
<key>_coordinate</key>
<value>(608, 336)</value>
<value>(736, 376)</value>
</param>
<param>
<key>_rotation</key>
@ -462,7 +435,7 @@ of a full period of the CW signal with frequency freq.</value>
</param>
<param>
<key>_coordinate</key>
<value>(960, 188)</value>
<value>(711, 291)</value>
</param>
<param>
<key>_rotation</key>
@ -485,6 +458,85 @@ of a full period of the CW signal with frequency freq.</value>
<value>samp_rate</value>
</param>
</block>
<block>
<key>band_pass_filter</key>
<param>
<key>beta</key>
<value>6.76</value>
</param>
<param>
<key>alias</key>
<value></value>
</param>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>affinity</key>
<value></value>
</param>
<param>
<key>decim</key>
<value>1</value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>type</key>
<value>fir_filter_fff</value>
</param>
<param>
<key>_coordinate</key>
<value>(511, 355)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>gain</key>
<value>1</value>
</param>
<param>
<key>high_cutoff_freq</key>
<value>1000</value>
</param>
<param>
<key>id</key>
<value>band_pass_filter_0</value>
</param>
<param>
<key>interp</key>
<value>1</value>
</param>
<param>
<key>low_cutoff_freq</key>
<value>200</value>
</param>
<param>
<key>maxoutbuf</key>
<value>0</value>
</param>
<param>
<key>minoutbuf</key>
<value>0</value>
</param>
<param>
<key>samp_rate</key>
<value>samp_rate</value>
</param>
<param>
<key>width</key>
<value>100</value>
</param>
<param>
<key>win</key>
<value>firdes.WIN_HAMMING</value>
</param>
</block>
<block>
<key>blocks_add_xx</key>
<param>
@ -536,6 +588,61 @@ of a full period of the CW signal with frequency freq.</value>
<value>1</value>
</param>
</block>
<block>
<key>blocks_delay</key>
<param>
<key>alias</key>
<value></value>
</param>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>affinity</key>
<value></value>
</param>
<param>
<key>delay</key>
<value>ndelay</value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>_coordinate</key>
<value>(1000, 428)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>id</key>
<value>blocks_delay_0</value>
</param>
<param>
<key>maxoutbuf</key>
<value>0</value>
</param>
<param>
<key>minoutbuf</key>
<value>0</value>
</param>
<param>
<key>num_ports</key>
<value>1</value>
</param>
<param>
<key>type</key>
<value>float</value>
</param>
<param>
<key>vlen</key>
<value>1</value>
</param>
</block>
<block>
<key>blocks_moving_average_xx</key>
<param>
@ -556,7 +663,7 @@ of a full period of the CW signal with frequency freq.</value>
</param>
<param>
<key>_coordinate</key>
<value>(1000, 438)</value>
<value>(1304, 556)</value>
</param>
<param>
<key>_rotation</key>
@ -568,7 +675,7 @@ of a full period of the CW signal with frequency freq.</value>
</param>
<param>
<key>length</key>
<value>taps</value>
<value>ndelay</value>
</param>
<param>
<key>max_iter</key>
@ -615,7 +722,7 @@ of a full period of the CW signal with frequency freq.</value>
</param>
<param>
<key>_coordinate</key>
<value>(304, 364)</value>
<value>(296, 268)</value>
</param>
<param>
<key>_rotation</key>
@ -666,7 +773,7 @@ of a full period of the CW signal with frequency freq.</value>
</param>
<param>
<key>_coordinate</key>
<value>(336, 220)</value>
<value>(296, 212)</value>
</param>
<param>
<key>_rotation</key>
@ -693,6 +800,108 @@ of a full period of the CW signal with frequency freq.</value>
<value>1</value>
</param>
</block>
<block>
<key>blocks_multiply_xx</key>
<param>
<key>alias</key>
<value></value>
</param>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>affinity</key>
<value></value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>_coordinate</key>
<value>(1152, 408)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>id</key>
<value>blocks_multiply_xx_0</value>
</param>
<param>
<key>type</key>
<value>float</value>
</param>
<param>
<key>maxoutbuf</key>
<value>0</value>
</param>
<param>
<key>minoutbuf</key>
<value>0</value>
</param>
<param>
<key>num_inputs</key>
<value>2</value>
</param>
<param>
<key>vlen</key>
<value>1</value>
</param>
</block>
<block>
<key>blocks_multiply_xx</key>
<param>
<key>alias</key>
<value></value>
</param>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>affinity</key>
<value></value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>_coordinate</key>
<value>(1502, 407)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>id</key>
<value>blocks_multiply_xx_0_0</value>
</param>
<param>
<key>type</key>
<value>float</value>
</param>
<param>
<key>maxoutbuf</key>
<value>0</value>
</param>
<param>
<key>minoutbuf</key>
<value>0</value>
</param>
<param>
<key>num_inputs</key>
<value>2</value>
</param>
<param>
<key>vlen</key>
<value>1</value>
</param>
</block>
<block>
<key>blocks_throttle</key>
<param>
@ -713,7 +922,7 @@ of a full period of the CW signal with frequency freq.</value>
</param>
<param>
<key>_coordinate</key>
<value>(456, 364)</value>
<value>(335, 403)</value>
</param>
<param>
<key>_rotation</key>
@ -768,7 +977,7 @@ of a full period of the CW signal with frequency freq.</value>
</param>
<param>
<key>file</key>
<value>/home/surligas/workspace/gr-satnogs/examples/morse_ref.wav</value>
<value>./morse_ref.wav</value>
</param>
<param>
<key>_coordinate</key>
@ -799,6 +1008,61 @@ of a full period of the CW signal with frequency freq.</value>
<value>True</value>
</param>
</block>
<block>
<key>fir_filter_xxx</key>
<param>
<key>alias</key>
<value></value>
</param>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>affinity</key>
<value></value>
</param>
<param>
<key>decim</key>
<value>1</value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>_coordinate</key>
<value>(1296, 412)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>id</key>
<value>fir_filter_xxx_0</value>
</param>
<param>
<key>maxoutbuf</key>
<value>0</value>
</param>
<param>
<key>minoutbuf</key>
<value>0</value>
</param>
<param>
<key>samp_delay</key>
<value>0</value>
</param>
<param>
<key>taps</key>
<value>[1.0,]*ndelay</value>
</param>
<param>
<key>type</key>
<value>fff</value>
</param>
</block>
<block>
<key>import</key>
<param>
@ -836,6 +1100,10 @@ of a full period of the CW signal with frequency freq.</value>
<key>autoscale</key>
<value>False</value>
</param>
<param>
<key>axislabels</key>
<value>True</value>
</param>
<param>
<key>alias</key>
<value></value>
@ -862,7 +1130,7 @@ of a full period of the CW signal with frequency freq.</value>
</param>
<param>
<key>_coordinate</key>
<value>(704, 486)</value>
<value>(1032, 612)</value>
</param>
<param>
<key>gui_hint</key>
@ -1134,7 +1402,7 @@ of a full period of the CW signal with frequency freq.</value>
</param>
<param>
<key>size</key>
<value>1024</value>
<value>samp_rate</value>
</param>
<param>
<key>srate</key>
@ -1182,7 +1450,7 @@ of a full period of the CW signal with frequency freq.</value>
</param>
<param>
<key>ymax</key>
<value>150000</value>
<value>1</value>
</param>
<param>
<key>ymin</key>
@ -1195,6 +1463,10 @@ of a full period of the CW signal with frequency freq.</value>
<key>autoscale</key>
<value>False</value>
</param>
<param>
<key>axislabels</key>
<value>True</value>
</param>
<param>
<key>alias</key>
<value></value>
@ -1221,7 +1493,7 @@ of a full period of the CW signal with frequency freq.</value>
</param>
<param>
<key>_coordinate</key>
<value>(512, 222)</value>
<value>(936, 216)</value>
</param>
<param>
<key>gui_hint</key>
@ -1548,70 +1820,15 @@ of a full period of the CW signal with frequency freq.</value>
<value>-10</value>
</param>
</block>
<block>
<key>satnogs_cw_matched_filter_ff</key>
<param>
<key>carrier_freq</key>
<value>freq</value>
</param>
<param>
<key>alias</key>
<value></value>
</param>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>energy</key>
<value>True</value>
</param>
<param>
<key>affinity</key>
<value></value>
</param>
<param>
<key>_enabled</key>
<value>1</value>
</param>
<param>
<key>_coordinate</key>
<value>(808, 351)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>id</key>
<value>satnogs_cw_matched_filter_ff_0</value>
</param>
<param>
<key>maxoutbuf</key>
<value>0</value>
</param>
<param>
<key>minoutbuf</key>
<value>0</value>
</param>
<param>
<key>sampling_rate</key>
<value>samp_rate</value>
</param>
<param>
<key>wpm</key>
<value>wpm</value>
</param>
</block>
<block>
<key>satnogs_cw_to_symbol</key>
<param>
<key>threshold</key>
<value>20e3</value>
<value>2.0</value>
</param>
<param>
<key>auto_config</key>
<value>True</value>
<value>False</value>
</param>
<param>
<key>alias</key>
@ -1635,11 +1852,11 @@ of a full period of the CW signal with frequency freq.</value>
</param>
<param>
<key>_coordinate</key>
<value>(272, 559)</value>
<value>(816, 576)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
<value>180</value>
</param>
<param>
<key>id</key>
@ -1682,11 +1899,11 @@ of a full period of the CW signal with frequency freq.</value>
</param>
<param>
<key>_coordinate</key>
<value>(664, 580)</value>
<value>(551, 603)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
<value>180</value>
</param>
<param>
<key>id</key>
@ -1723,18 +1940,30 @@ of a full period of the CW signal with frequency freq.</value>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>filename</key>
<value></value>
</param>
<param>
<key>_coordinate</key>
<value>(864, 580)</value>
<value>(191, 587)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
<value>180</value>
</param>
<param>
<key>id</key>
<value>satnogs_multi_format_msg_sink_0</value>
</param>
<param>
<key>outstream</key>
<value>True</value>
</param>
<param>
<key>timestamp</key>
<value>False</value>
</param>
<param>
<key>format</key>
<value>0</value>
@ -1742,28 +1971,52 @@ of a full period of the CW signal with frequency freq.</value>
</block>
<connection>
<source_block_id>analog_agc2_xx_0_0</source_block_id>
<sink_block_id>qtgui_time_sink_x_0_0</sink_block_id>
<sink_block_id>blocks_delay_0</sink_block_id>
<source_key>0</source_key>
<sink_key>2</sink_key>
<sink_key>0</sink_key>
</connection>
<connection>
<source_block_id>analog_agc2_xx_0_0</source_block_id>
<sink_block_id>satnogs_cw_matched_filter_ff_0</sink_block_id>
<sink_block_id>blocks_multiply_xx_0</sink_block_id>
<source_key>0</source_key>
<sink_key>0</sink_key>
</connection>
<connection>
<source_block_id>analog_agc2_xx_0_0</source_block_id>
<sink_block_id>qtgui_time_sink_x_0_0</sink_block_id>
<source_key>0</source_key>
<sink_key>2</sink_key>
</connection>
<connection>
<source_block_id>analog_fastnoise_source_x_0</source_block_id>
<sink_block_id>blocks_add_xx_0</sink_block_id>
<source_key>0</source_key>
<sink_key>1</sink_key>
</connection>
<connection>
<source_block_id>band_pass_filter_0</source_block_id>
<sink_block_id>analog_agc2_xx_0_0</sink_block_id>
<source_key>0</source_key>
<sink_key>0</sink_key>
</connection>
<connection>
<source_block_id>band_pass_filter_0</source_block_id>
<sink_block_id>audio_sink_0</sink_block_id>
<source_key>0</source_key>
<sink_key>0</sink_key>
</connection>
<connection>
<source_block_id>blocks_add_xx_0</source_block_id>
<sink_block_id>blocks_multiply_const_vxx_0</sink_block_id>
<source_key>0</source_key>
<sink_key>0</sink_key>
</connection>
<connection>
<source_block_id>blocks_delay_0</source_block_id>
<sink_block_id>blocks_multiply_xx_0</sink_block_id>
<source_key>0</source_key>
<sink_key>1</sink_key>
</connection>
<connection>
<source_block_id>blocks_moving_average_xx_0</source_block_id>
<sink_block_id>qtgui_time_sink_x_0</sink_block_id>
@ -1776,12 +2029,6 @@ of a full period of the CW signal with frequency freq.</value>
<source_key>0</source_key>
<sink_key>0</sink_key>
</connection>
<connection>
<source_block_id>blocks_multiply_const_vxx_0</source_block_id>
<sink_block_id>audio_sink_0</sink_block_id>
<source_key>0</source_key>
<sink_key>0</sink_key>
</connection>
<connection>
<source_block_id>blocks_multiply_const_vxx_0</source_block_id>
<sink_block_id>blocks_throttle_0</sink_block_id>
@ -1800,9 +2047,21 @@ of a full period of the CW signal with frequency freq.</value>
<source_key>0</source_key>
<sink_key>0</sink_key>
</connection>
<connection>
<source_block_id>blocks_multiply_xx_0</source_block_id>
<sink_block_id>fir_filter_xxx_0</sink_block_id>
<source_key>0</source_key>
<sink_key>0</sink_key>
</connection>
<connection>
<source_block_id>blocks_multiply_xx_0_0</source_block_id>
<sink_block_id>blocks_moving_average_xx_0</sink_block_id>
<source_key>0</source_key>
<sink_key>0</sink_key>
</connection>
<connection>
<source_block_id>blocks_throttle_0</source_block_id>
<sink_block_id>analog_agc2_xx_0_0</sink_block_id>
<sink_block_id>band_pass_filter_0</sink_block_id>
<source_key>0</source_key>
<sink_key>0</sink_key>
</connection>
@ -1819,11 +2078,17 @@ of a full period of the CW signal with frequency freq.</value>
<sink_key>0</sink_key>
</connection>
<connection>
<source_block_id>satnogs_cw_matched_filter_ff_0</source_block_id>
<sink_block_id>blocks_moving_average_xx_0</sink_block_id>
<source_block_id>fir_filter_xxx_0</source_block_id>
<sink_block_id>blocks_multiply_xx_0_0</sink_block_id>
<source_key>0</source_key>
<sink_key>0</sink_key>
</connection>
<connection>
<source_block_id>fir_filter_xxx_0</source_block_id>
<sink_block_id>blocks_multiply_xx_0_0</sink_block_id>
<source_key>0</source_key>
<sink_key>1</sink_key>
</connection>
<connection>
<source_block_id>satnogs_cw_to_symbol_0</source_block_id>
<sink_block_id>satnogs_morse_decoder_0</sink_block_id>

View File

@ -78,65 +78,7 @@
</param>
<param>
<key>title</key>
<value></value>
</param>
</block>
<block>
<key>variable</key>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>_coordinate</key>
<value>(8, 160)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>id</key>
<value>samp_rate</value>
</param>
<param>
<key>value</key>
<value>32000</value>
</param>
</block>
<block>
<key>satnogs_clear_text_msg_sink</key>
<param>
<key>alias</key>
<value></value>
</param>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>affinity</key>
<value></value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>_coordinate</key>
<value>(960, 168)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>id</key>
<value>satnogs_clear_text_msg_sink_0</value>
<value>Morse Decoder Simple Example</value>
</param>
</block>
<block>
@ -187,7 +129,7 @@
</param>
<param>
<key>debug_seq</key>
<value>"XPGOLIAT HELLO EARTH WORLD 123456789"</value>
<value>"SATNOGS HELLO EARTH WORLD 123456789"</value>
</param>
</block>
<block>
@ -233,6 +175,53 @@
<value>ord('#')</value>
</param>
</block>
<block>
<key>satnogs_multi_format_msg_sink</key>
<param>
<key>alias</key>
<value></value>
</param>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>affinity</key>
<value></value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>filename</key>
<value></value>
</param>
<param>
<key>_coordinate</key>
<value>(968, 148)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>id</key>
<value>satnogs_multi_format_msg_sink_0</value>
</param>
<param>
<key>outstream</key>
<value>True</value>
</param>
<param>
<key>timestamp</key>
<value>False</value>
</param>
<param>
<key>format</key>
<value>0</value>
</param>
</block>
<connection>
<source_block_id>satnogs_morse_debug_source_0</source_block_id>
<sink_block_id>satnogs_morse_decoder_0</sink_block_id>
@ -241,7 +230,7 @@
</connection>
<connection>
<source_block_id>satnogs_morse_decoder_0</source_block_id>
<sink_block_id>satnogs_clear_text_msg_sink_0</sink_block_id>
<sink_block_id>satnogs_multi_format_msg_sink_0</sink_block_id>
<source_key>out</source_key>
<sink_key>in</sink_key>
</connection>

View File

@ -24,11 +24,11 @@ list(APPEND debug_blocks
satnogs_debug_msg_source.xml
satnogs_debug_msg_source_raw.xml
satnogs_leo_channel.xml
satnogs_cw_encoder.xml
)
list(APPEND enabled_blocks
satnogs_block_tree.xml
satnogs_cw_matched_filter_ff.xml
satnogs_morse_decoder.xml
satnogs_multi_format_msg_sink.xml
satnogs_ogg_encoder.xml
@ -41,16 +41,17 @@ list(APPEND enabled_blocks
satnogs_coarse_doppler_correction_cc.xml
satnogs_ax25_encoder_mb.xml
satnogs_ax25_decoder_bm.xml
satnogs_waterfall_sink.xml
satnogs_waterfall_sink.xml
satnogs_ogg_source.xml
satnogs_noaa_apt_sink.xml
satnogs_frame_file_sink.xml
satnogs_iq_sink.xml
)
if(${INCLUDE_DEBUG_BLOCKS})
list(APPEND enabled_blocks ${debug_blocks})
endif()
install(FILES
${enabled_blocks}
satnogs_ogg_source.xml
satnogs_noaa_apt_sink.xml
satnogs_frame_file_sink.xml
satnogs_iq_sink.xml DESTINATION share/gnuradio/grc/blocks
${enabled_blocks}
DESTINATION share/gnuradio/grc/blocks
)

View File

@ -0,0 +1,39 @@
<?xml version="1.0"?>
<block>
<name>CW Encoder</name>
<key>satnogs_cw_encoder</key>
<category>[SatNOGS]/Debug</category>
<import>import satnogs</import>
<make>satnogs.cw_encoder($samp_rate, $cw_freq, $wpm)</make>
<param>
<name>Sample Rate</name>
<key>samp_rate</key>
<value>samp_rate</value>
<type>real</type>
</param>
<param>
<name>CW Frequency</name>
<key>cw_freq</key>
<value>700</value>
<type>real</type>
</param>
<param>
<name>Words per minute</name>
<key>wpm</key>
<value>20</value>
<type>int</type>
</param>
<sink>
<name>symbol</name>
<type>message</type>
</sink>
<source>
<name>out</name>
<type>complex</type>
</source>
</block>

View File

@ -1,57 +0,0 @@
<?xml version="1.0"?>
<block>
<name>CW Matched Filter</name>
<key>satnogs_cw_matched_filter_ff</key>
<import>import satnogs</import>
<make>satnogs.cw_matched_filter_ff($sampling_rate, $carrier_freq, $wpm, $energy)</make>
<callback>set_new_freq_locked($carrier_freq)</callback>
<param>
<name>Sampling Rate</name>
<key>sampling_rate</key>
<type>real</type>
</param>
<param>
<name>Audio Frequency (Hz)</name>
<key>carrier_freq</key>
<type>real</type>
</param>
<param>
<name>Words per Minute</name>
<key>wpm</key>
<value>20</value>
<type>int</type>
</param>
<param>
<name>Compute Energy</name>
<key>energy</key>
<type>enum</type>
<option>
<name>No</name>
<key>False</key>
</option>
<option>
<name>Yes</name>
<key>True</key>
</option>
</param>
<sink>
<name>freq</name>
<type>message</type>
<optional>1</optional>
</sink>
<sink>
<name>in</name>
<type>float</type>
</sink>
<source>
<name>out</name>
<type>float</type>
</source>
</block>

View File

@ -3,7 +3,7 @@
<name>CW to Symbols</name>
<key>satnogs_cw_to_symbol</key>
<import>import satnogs</import>
<make>satnogs.cw_to_symbol($sampling_rate, $threshold, $conf_level, $wpm, $auto_config)</make>
<make>satnogs.cw_to_symbol($sampling_rate, $threshold, $conf_level, $wpm)</make>
<callback>set_act_threshold($threshold)</callback>
<param>
@ -32,20 +32,6 @@
<value>20</value>
<type>int</type>
</param>
<param>
<name>Automatic timing recovery</name>
<key>auto_config</key>
<type>enum</type>
<option>
<name>No</name>
<key>False</key>
</option>
<option>
<name>Yes</name>
<key>True</key>
</option>
</param>
<sink>
<name>act_threshold</name>
@ -53,12 +39,6 @@
<optional>1</optional>
</sink>
<sink>
<name>sync</name>
<type>message</type>
<optional>1</optional>
</sink>
<sink>
<name>in</name>
<type>float</type>

View File

@ -4,7 +4,14 @@
<key>satnogs_morse_debug_source</key>
<category>[SatNOGS]/Debug</category>
<import>import satnogs</import>
<make>satnogs.morse_debug_source($debug_seq, $errors, $p)</make>
<make>satnogs.morse_debug_source($wpm, $debug_seq, $errors, $p, $seq_pause_ms)</make>
<param>
<name>WPM</name>
<key>wpm</key>
<value>20</value>
<type>int</type>
</param>
<param>
<name>Sentence</name>
@ -33,6 +40,13 @@
<value>0.1</value>
<type>real</type>
</param>
<param>
<name>Sequence Pause (millis)</name>
<key>seq_pause_ms</key>
<value>1000</value>
<type>int</type>
</param>
<source>
<name>out</name>

View File

@ -3,7 +3,7 @@
<name>Multi Format Message Sink</name>
<key>satnogs_multi_format_msg_sink</key>
<import>import satnogs</import>
<make>satnogs.multi_format_msg_sink($format)</make>
<make>satnogs.multi_format_msg_sink($format, $timestamp, $outstream, $filename)</make>
<param>
<name>Output format</name>
@ -22,6 +22,41 @@
<key>2</key>
</option>
</param>
<param>
<name>Output Timestamp</name>
<key>timestamp</key>
<type>enum</type>
<option>
<name>No</name>
<key>False</key>
</option>
<option>
<name>Yes</name>
<key>True</key>
</option>
</param>
<param>
<name>Output Result</name>
<key>outstream</key>
<type>enum</type>
<option>
<name>STDOUT</name>
<key>True</key>
<opt>t:stdout</opt>
</option>
<option>
<name>File</name>
<key>False</key>
<opt>t:file</opt>
</option>
</param>
<param>
<name>File</name>
<key>filename</key>
<value></value>
<type>file_save</type>
<hide>#if $outstream.t == "file" then 'none' else 'all'#</hide>
</param>
<sink>
<name>in</name>

View File

@ -4,7 +4,7 @@
<key>satnogs_ogg_source</key>
<category>[satnogs]</category>
<import>import satnogs</import>
<make>satnogs.ogg_source($filename, $channels)</make>
<make>satnogs.ogg_source($filename, $channels, $repeat)</make>
<param>
<name>File</name>
@ -19,6 +19,20 @@
<value>1</value>
<type>int</type>
</param>
<param>
<name>Repeat</name>
<key>repeat</key>
<type>enum</type>
<option>
<name>No</name>
<key>False</key>
</option>
<option>
<name>Yes</name>
<key>True</key>
</option>
</param>
<source>
<name>out</name>

View File

@ -21,17 +21,17 @@
# Install public header files
########################################################################
list(APPEND DEBUG_HEADER_FILES
morse_debug_source.h
debug_msg_source_raw.h
debug_msg_source.h
leo_channel.h
morse_debug_source.h
debug_msg_source_raw.h
debug_msg_source.h
leo_channel.h
cw_encoder.h
)
list(APPEND HEADER_FILES
api.h
ax25.h
config.h
cw_matched_filter_ff.h
log.h
morse_tree.h
morse.h
@ -55,16 +55,18 @@ list(APPEND HEADER_FILES
ax25_encoder_mb.h
ax25_decoder_bm.h
qb50_deframer.h
waterfall_sink.h
waterfall_sink.h
ogg_source.h
noaa_apt_sink.h
frame_file_sink.h
iq_sink.h
)
if(${INCLUDE_DEBUG_BLOCKS})
list(APPEND HEADER_FILES ${DEBUG_HEADER_FILES})
endif()
install(FILES
${HEADER_FILES}
ogg_source.h
noaa_apt_sink.h
frame_file_sink.h
iq_sink.h DESTINATION include/satnogs
DESTINATION include/satnogs
)

View File

@ -0,0 +1,59 @@
/* -*- c++ -*- */
/*
* gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
*
* Copyright (C) 2017
* Libre Space Foundation <http://librespacefoundation.org/>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_SATNOGS_CW_ENCODER_H
#define INCLUDED_SATNOGS_CW_ENCODER_H
#include <satnogs/api.h>
#include <gnuradio/sync_block.h>
namespace gr
{
namespace satnogs
{
/*!
* \brief CW encoder block, mainly for debugging and testing purposes.
* It accepts a CW word via a message source port and transmits the
* corresponding CW symbols.
* \ingroup satnogs
*
*/
class SATNOGS_API cw_encoder : virtual public gr::sync_block
{
public:
typedef boost::shared_ptr<cw_encoder> sptr;
/*!
* \brief Return a shared_ptr to a new instance of satnogs::cw_encoder.
* @param samp_rate the sampling rate
* @param cw_freq the CW tone frequency
* @param wpm words per minute (WPM)
*/
static sptr
make (double samp_rate, double cw_freq = 700, size_t wpm = 20);
};
} // namespace satnogs
} // namespace gr
#endif /* INCLUDED_SATNOGS_CW_ENCODER_H */

View File

@ -1,65 +0,0 @@
/* -*- c++ -*- */
/*
* gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
*
* Copyright (C) 2016, Libre Space Foundation <http://librespacefoundation.org/>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_SATNOGS_CW_MATCHED_FILTER_FF_H
#define INCLUDED_SATNOGS_CW_MATCHED_FILTER_FF_H
#include <satnogs/api.h>
#include <gnuradio/sync_block.h>
namespace gr {
namespace satnogs {
/*!
* \brief This block implements a matched filter to reduce the noise
* level of the received CW signal.
*
* \ingroup satnogs
*
*/
class SATNOGS_API cw_matched_filter_ff : virtual public gr::sync_block
{
public:
typedef boost::shared_ptr<cw_matched_filter_ff> sptr;
/*!
* \brief Matched filter for CW noise reduction
*
* @param sampling_rate the sampling rate of the signal
* @param carrier_freq the audio frequency of the CW signal
* @param wpm Morse code Words per Minute
* @param energy_out if true, the filter produces the energy of the
* resulting filtered samples, otherwise the raw filter samples are
* produced. This is handy, in order to save the flowgraph from
* am additional signal energy block.
*/
static sptr make(double sampling_rate, double carrier_freq = 500,
size_t wpm = 20,
bool energy_out = false);
virtual void set_new_freq_locked(double freq) = 0;
virtual void set_new_freq(double freq) = 0;
};
} // namespace satnogs
} // namespace gr
#endif /* INCLUDED_SATNOGS_CW_MATCHED_FILTER_FF_H */

View File

@ -24,17 +24,24 @@
#include <satnogs/api.h>
#include <gnuradio/sync_block.h>
namespace gr {
namespace satnogs {
namespace gr
{
namespace satnogs
{
/*!
* \brief The CW to Symbol block tries to translate the received signal
* power time-series into Morse symbols.
* \brief The CW to Symbol block tries to translate the input signal
* into Morse symbols. The input signal should have been already properly
* filtered and processed. A possible DSP on the input signal may be the
* squared magnitude or the amplitude of the autocorrelation. Proper filtering
* that take cares possible spikes may drastically increase the performance
* of this block.
*
* \ingroup satnogs
*/
class SATNOGS_API cw_to_symbol : virtual public gr::sync_block
{
public:
public:
typedef boost::shared_ptr<cw_to_symbol> sptr;
/*!
@ -58,18 +65,13 @@ namespace gr {
* symbols
*
* @param wpm Morse code Words per Minute
*
* @param auto_config if set to true, the block will try first to
* automatically adjust the WPM in order to extract the dot and dash
* durations. If set to false, the given WPM is used.
*/
static cw_to_symbol::sptr
make (double sampling_rate, float threshold, float conf_level = 0.9,
size_t wpm = 20, bool auto_config = false);
size_t wpm = 20);
virtual void set_act_threshold(float thrld) = 0;
virtual void start_timing_recovery() = 0;
virtual void
set_act_threshold (float thrld) = 0;
};
} // namespace satnogs

View File

@ -2,7 +2,8 @@
/*
* gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
*
* Copyright (C) 2016, Libre Space Foundation <http://librespacefoundation.org/>
* Copyright (C) 2016,2017
* Libre Space Foundation <http://librespacefoundation.org/>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -27,10 +28,11 @@
* The different Morse symbols
*/
typedef enum {
MORSE_DOT = 0,//!< MORSE_DOT Morse dot (.) symbol
MORSE_DASH, //!< MORSE_DASH Morse dash (-) symbol
MORSE_S_SPACE,//!< MORSE_S_SPACE Morse short space between characters
MORSE_L_SPACE //!< MORSE_L_SPACE Morse long space between words
MORSE_DOT = 0, //!< Morse dot (.) symbol
MORSE_DASH, //!< Morse dash (-) symbol
MORSE_INTRA_SPACE, //!< Space between dot and dash symbols
MORSE_S_SPACE, //!< Morse short space between characters
MORSE_L_SPACE //!<Morse long space between words
} morse_symbol_t;

View File

@ -2,7 +2,8 @@
/*
* gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
*
* Copyright (C) 2016, Libre Space Foundation <http://librespacefoundation.org/>
* Copyright (C) 2016,2017
* Libre Space Foundation <http://librespacefoundation.org/>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -24,8 +25,10 @@
#include <satnogs/api.h>
#include <gnuradio/block.h>
namespace gr {
namespace satnogs {
namespace gr
{
namespace satnogs
{
/*!
* \brief A Morse debug source block that supports injection of random
@ -36,7 +39,7 @@ namespace gr {
*/
class SATNOGS_API morse_debug_source : virtual public gr::block
{
public:
public:
typedef boost::shared_ptr<morse_debug_source> sptr;
/*!
@ -45,13 +48,21 @@ namespace gr {
* This block can also inject random errors, based on a Bernoulli
* distribution.
*
* @param wpm words per minute
* @param debug_seq A string containing the debug sentence
* @param inject_errors if set the debug source block will produce
* errors that follow a Bernoulli distribution
* @param error_prob the probability p of error for the Bernulli distribution
* @param seq_pause_ms pause in milliseconds between concecutive debug
* sequences. For simplicity the pause duration will be scaled to multiple
* dot durations.
*/
static sptr make(const std::string& debug_seq, bool inject_errors = false,
float error_prob = 0.1);
static sptr
make (const size_t wpm,
const std::string& debug_seq,
bool inject_errors = false,
float error_prob = 0.1,
size_t seq_pause_ms = 1000);
};
} // namespace satnogs

View File

@ -2,7 +2,7 @@
/*
* gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
*
* Copyright (C) 2016, Libre Space Foundation <http://librespacefoundation.org/>
* Copyright (C) 2016,2017, Libre Space Foundation <http://librespacefoundation.org/>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -44,12 +44,24 @@ namespace gr
/*!
* \brief Block accepting clear text messages from various decoders.
* Its purpose is to forward these messages at other services, programs,
* stdout, etc,
* Its purpose is to either print these messages to stdout or save them
* in text format in a file.
*
* Depending on format parameter, the contents of each message are
* converted to hexademical, binary or ASCII format.
*
* @param format the format that will used to display the messages.
* 0: Clear Text 1: Hexademical 2: Binary
* @param timestamp if set, a ISO 8601 timestamp is inserted in front of
* each message
* @param out_stdout if set, the messages are displayed in the stdout.
* Otherwise messages are saved in a text file
* @param filepath specifies the file path of the text file
*/
static sptr
make (size_t format);
make (size_t format, bool timestamp = true,
bool out_stdout = true,
const std::string& filepath = "");
};
} // namespace satnogs

View File

@ -47,9 +47,12 @@ namespace gr
* @param number of channels of the OGG stream. If the actual OGG stream
* contains a different number of channels than specified an exception
* is raised
* @param repeat if set to true, when EOF is reached the block
* will continue to output samples from the beginning of the OGG file.
*/
static sptr
make (const std::string& filename, size_t channels = 1);
make (const std::string& filename, size_t channels = 1,
bool repeat = false);
};
} // namespace satnogs

View File

@ -33,13 +33,13 @@ include_directories(
link_directories(${Boost_LIBRARY_DIRS})
list(APPEND satnogs_debug_sources
morse_debug_source_impl.cc
debug_msg_source_impl.cc
debug_msg_source_raw_impl.cc
leo_channel_impl.cc
morse_debug_source_impl.cc
debug_msg_source_impl.cc
debug_msg_source_raw_impl.cc
leo_channel_impl.cc
cw_encoder_impl.cc
)
list(APPEND satnogs_sources
cw_matched_filter_ff_impl.cc
morse_tree.cc
morse_decoder_impl.cc
multi_format_msg_sink_impl.cc

145
lib/cw_encoder_impl.cc Normal file
View File

@ -0,0 +1,145 @@
/* -*- c++ -*- */
/*
* gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
*
* Copyright (C) 2017
* Libre Space Foundation <http://librespacefoundation.org/>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gnuradio/io_signature.h>
#include <satnogs/log.h>
#include "cw_encoder_impl.h"
namespace gr {
namespace satnogs {
cw_encoder::sptr
cw_encoder::make(double samp_rate, double cw_freq, size_t wpm)
{
return gnuradio::get_initial_sptr
(new cw_encoder_impl(samp_rate, cw_freq, wpm));
}
/*
* The private constructor
*/
cw_encoder_impl::cw_encoder_impl(double samp_rate, double cw_freq,
size_t wpm)
: gr::sync_block("cw_encoder",
gr::io_signature::make(0, 0, 0),
gr::io_signature::make(1, 1, sizeof(gr_complex))),
d_samp_rate (samp_rate),
d_cw_freq (cw_freq),
d_wpm (wpm),
d_dot_samples ((1.2 / wpm) / (1.0 / samp_rate)),
d_window_size (0),
d_windows_remaining (0),
d_cw_symbol (MORSE_L_SPACE),
d_nco ()
{
message_port_register_in(pmt::mp("symbol"));
/*
* Try to split the CW pulses in smaller windows for dealing efficiently
* with the available buffer size
*/
size_t i = 10;
d_window_size = d_dot_samples / i;
while(d_window_size > 200) {
i += 10;
d_window_size = d_dot_samples / i;
}
/* NOTE: The dot duration should be a perfect multiple of the window */
while(d_dot_samples % d_window_size != 0) {
d_window_size++;
}
set_output_multiple(d_window_size);
d_nco.set_freq ((2 * M_PI * cw_freq) / samp_rate);
}
/*
* Our virtual destructor.
*/
cw_encoder_impl::~cw_encoder_impl()
{
}
int
cw_encoder_impl::work(int noutput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
size_t available;
size_t i;
gr_complex *out = (gr_complex *) output_items[0];
if(noutput_items < 0) {
return noutput_items;
}
if(d_windows_remaining == 0) {
pmt::pmt_t symbol = delete_head_blocking(pmt::mp("symbol"));
d_cw_symbol = (morse_symbol_t) pmt::to_long(symbol);
/* Reset NCO so the amplitude starts from zero */
d_nco.set_freq ((2 * M_PI * d_cw_freq) / d_samp_rate);
switch(d_cw_symbol) {
case MORSE_DOT:
case MORSE_INTRA_SPACE:
d_windows_remaining = d_dot_samples / d_window_size;
break;
case MORSE_DASH:
case MORSE_S_SPACE:
d_windows_remaining = (d_dot_samples / d_window_size) * 3;
break;
case MORSE_L_SPACE:
d_windows_remaining = (d_dot_samples / d_window_size) * 7;
break;
default:
LOG_WARN("Unrecognized CW symbol");
return 0;
}
}
for(i = 0; i < (size_t)noutput_items / d_window_size; i++) {
switch(d_cw_symbol){
case MORSE_S_SPACE:
case MORSE_L_SPACE:
case MORSE_INTRA_SPACE:
memset (out + i * d_window_size, 0,
d_window_size * sizeof(gr_complex));
break;
case MORSE_DOT:
case MORSE_DASH:
d_nco.sincos(out + i * d_window_size, d_window_size, 1.0);
break;
}
d_windows_remaining--;
if(d_windows_remaining == 0) {
return (i + 1) * d_window_size;
}
}
return i * d_window_size;
}
} /* namespace satnogs */
} /* namespace gr */

67
lib/cw_encoder_impl.h Normal file
View File

@ -0,0 +1,67 @@
/* -*- c++ -*- */
/*
* gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
*
* Copyright (C) 2017
* Libre Space Foundation <http://librespacefoundation.org/>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_SATNOGS_CW_ENCODER_IMPL_H
#define INCLUDED_SATNOGS_CW_ENCODER_IMPL_H
#include <vector>
#include <string>
#include <satnogs/cw_encoder.h>
#include <satnogs/morse.h>
#include <gnuradio/fxpt_nco.h>
namespace gr
{
namespace satnogs
{
class cw_encoder_impl : public cw_encoder
{
private:
const double d_samp_rate;
const double d_cw_freq;
const size_t d_wpm;
const size_t d_dot_samples;
size_t d_window_size;
size_t d_windows_remaining;
morse_symbol_t d_cw_symbol;
gr::fxpt_nco d_nco;
std::string
get_cw_symbol(char c);
public:
cw_encoder_impl (double samp_rate, double cw_freq, size_t wpm);
~cw_encoder_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_CW_ENCODER_IMPL_H */

View File

@ -1,131 +0,0 @@
/* -*- c++ -*- */
/*
* gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
*
* Copyright (C) 2016, Libre Space Foundation <http://librespacefoundation.org/>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gnuradio/io_signature.h>
#include "cw_matched_filter_ff_impl.h"
#include <volk/volk.h>
#include <gnuradio/fxpt_nco.h>
namespace gr {
namespace satnogs {
cw_matched_filter_ff::sptr
cw_matched_filter_ff::make(double sampling_rate, double carrier_freq,
size_t wpm, bool energy_out)
{
return gnuradio::get_initial_sptr
(new cw_matched_filter_ff_impl(sampling_rate, carrier_freq,
wpm, energy_out));
}
/*
* The private constructor
*/
cw_matched_filter_ff_impl::cw_matched_filter_ff_impl (double sampling_rate,
double carrier_freq,
size_t wpm,
bool energy_out) :
gr::sync_block ("cw_matched_filter_ff",
gr::io_signature::make (1, 1, sizeof(float)),
gr::io_signature::make (1, 1, sizeof(float))),
d_samp_rate(sampling_rate),
d_dot_duration(1.2/wpm),
d_produce_enrg(energy_out),
d_dot_samples(d_dot_duration / (1.0 / sampling_rate))
{
const int alignment_multiple = volk_get_alignment() / sizeof(float);
set_alignment(std::max(1,alignment_multiple));
set_history(d_dot_samples);
d_sin_wave = (float *)volk_malloc(d_dot_samples * sizeof(float), 32);
if(!d_sin_wave){
throw std::runtime_error("Could not allocate sine wave buffer");
}
/* Register the input port for frequency change messages */
message_port_register_in(pmt::mp("freq"));
set_msg_handler(pmt::mp("freq"),
boost::bind(&cw_matched_filter_ff_impl::new_freq_msg_handler,
this, _1));
/* Now fill the buffer with the appropriate sine wave */
gr::fxpt_nco nco;
nco.set_freq(2 * M_PI * carrier_freq / sampling_rate);
nco.sin(d_sin_wave, d_dot_samples, 1.0);
}
/*
* Our virtual destructor.
*/
cw_matched_filter_ff_impl::~cw_matched_filter_ff_impl()
{
volk_free(d_sin_wave);
}
void
cw_matched_filter_ff_impl::new_freq_msg_handler (pmt::pmt_t msg)
{
if(pmt::is_pair(msg)){
set_new_freq(pmt::to_double(pmt::cdr(msg)));
}
}
int
cw_matched_filter_ff_impl::work (int noutput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
boost::mutex::scoped_lock lock (d_mutex);
const float *in = (const float *) input_items[0];
float *out = (float *) output_items[0];
for (int i = 0; i < noutput_items; i++) {
volk_32f_x2_dot_prod_32f (out + i, in + i, d_sin_wave, d_dot_samples);
}
if (d_produce_enrg) {
volk_32f_s32f_power_32f (out, out, 2, noutput_items);
}
return noutput_items;
}
void
cw_matched_filter_ff_impl::set_new_freq (double freq)
{
gr::fxpt_nco nco;
nco.set_freq(2 * M_PI * freq / d_samp_rate);
nco.sin(d_sin_wave, d_dot_samples, 1.0);
}
void
cw_matched_filter_ff_impl::set_new_freq_locked(double freq)
{
boost::mutex::scoped_lock lock(d_mutex);
set_new_freq(freq);
}
} /* namespace satnogs */
} /* namespace gr */

View File

@ -1,82 +0,0 @@
/* -*- c++ -*- */
/*
* gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
*
* Copyright (C) 2016, Libre Space Foundation <http://librespacefoundation.org/>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_SATNOGS_CW_MATCHED_FILTER_FF_IMPL_H
#define INCLUDED_SATNOGS_CW_MATCHED_FILTER_FF_IMPL_H
#include <satnogs/cw_matched_filter_ff.h>
#include <boost/thread/mutex.hpp>
namespace gr
{
namespace satnogs
{
class cw_matched_filter_ff_impl : public cw_matched_filter_ff
{
private:
/**
* The sampling rate of the signal
*/
const double d_samp_rate;
/**
* The duration of the dot in seconds
*/
const double d_dot_duration;
/**
* If set to true, this block produces the energy of the filtered
* samples, rather the samples themselves
*/
const bool d_produce_enrg;
/**
* The duration of the dot in number of samples
*/
const size_t d_dot_samples;
float *d_sin_wave;
boost::mutex d_mutex;
void
new_freq_msg_handler(pmt::pmt_t msg);
public:
cw_matched_filter_ff_impl (double sampling_rate, double carrier_freq,
size_t wpm, bool energy_out);
~cw_matched_filter_ff_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);
void
set_new_freq(double freq);
void
set_new_freq_locked(double freq);
};
} // namespace satnogs
} // namespace gr
#endif /* INCLUDED_SATNOGS_CW_MATCHED_FILTER_FF_IMPL_H */

View File

@ -2,7 +2,8 @@
/*
* gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
*
* Copyright (C) 2016, Libre Space Foundation <http://librespacefoundation.org/>
* Copyright (C) 2016,2017
* Libre Space Foundation <http://librespacefoundation.org/>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -26,7 +27,9 @@
#include <gnuradio/io_signature.h>
#include <satnogs/log.h>
#include <satnogs/utils.h>
#include <boost/math/common_factor.hpp>
#include "cw_to_symbol_impl.h"
#include <volk/volk.h>
namespace gr
{
@ -34,62 +37,118 @@ namespace gr
{
cw_to_symbol::sptr
cw_to_symbol::make (double sampling_rate, float threshold,
float conf_level, size_t wpm, bool auto_config)
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, auto_config));
new cw_to_symbol_impl (sampling_rate, threshold, conf_level, wpm));
}
/*
* The private constructor
*/
cw_to_symbol_impl::cw_to_symbol_impl (double sampling_rate, float threshold,
float conf_level, size_t wpm,
bool auto_config) :
gr::sync_block ("cw_to_symbol",
gr::io_signature::make (1, 1, sizeof(float)),
gr::io_signature::make (0, 0, 0)),
d_sampling_rate(sampling_rate),
d_act_thrshld(threshold),
d_confidence_level(conf_level),
d_sync_limit(15),
d_auto_sync(auto_config),
d_dot_samples((1.2/wpm) / (1.0 / sampling_rate)),
d_dash_samples(3 * d_dot_samples),
d_short_pause_samples(3 * d_dot_samples),
d_long_pause_samples(7 * d_dot_samples),
d_state(IDLE),
d_state_cnt(0),
d_pause_cnt(0),
d_est_cnt(0),
d_mean_cnt(0),
d_have_sync(!auto_config),
d_seq_started(false),
d_sync_state(SYNC_TRIGGER_OFF)
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)
{
message_port_register_in(pmt::mp("act_threshold"));
message_port_register_in(pmt::mp("sync"));
message_port_register_out(pmt::mp("out"));
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 (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"));
/* Register the message handlers */
set_msg_handler(pmt::mp("act_threshold"),
boost::bind(&cw_to_symbol_impl::set_act_threshold_msg_handler,
this, _1));
set_msg_handler(pmt::mp("sync"),
boost::bind(&cw_to_symbol_impl::sync_msg_handler,
this, _1));
set_msg_handler (
pmt::mp ("act_threshold"),
boost::bind (&cw_to_symbol_impl::set_act_threshold_msg_handler, this,
_1));
if( auto_config ){
d_dot_samples = (1.2/MIN_WPM) / (1.0 / d_sampling_rate);
/*
* 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;
}
/* NOTE: The dot duration should be a perfect multiple of the window */
while(d_dot_samples % d_window_size != 0) {
d_window_size++;
}
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;
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");
}
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)
{
message_port_pub(pmt::mp("out"), pmt::from_long(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);
}
/*
@ -97,322 +156,189 @@ namespace gr
*/
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_state = IDLE;
d_state_cnt = 0;
d_pause_cnt = 0;
d_dec_state = NO_SYNC;
d_window_cnt = 0;
}
inline void
cw_to_symbol_impl::set_short_on ()
{
d_state = SHORT_ON_PERIOD;
d_state_cnt = 1;
d_pause_cnt = 0;
d_dec_state = SEARCH_DOT;
d_window_cnt = 1;
}
inline void
cw_to_symbol_impl::set_long_on ()
{
d_state = LONG_ON_PERIOD;
d_dec_state = SEARCH_DASH;
}
inline void
cw_to_symbol_impl::set_short_off ()
cw_to_symbol_impl::set_search_space ()
{
d_state = SHORT_OFF_PERIOD;
d_state_cnt = 0;
d_pause_cnt = 1;
}
inline void
cw_to_symbol_impl::set_long_off ()
{
d_state = LONG_OFF_PERIOD;
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::sync_msg_handler (pmt::pmt_t msg)
{
if(pmt::is_pair(msg)){
if(pmt::to_bool(pmt::cdr(msg))){
start_timing_recovery();
}
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)
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
int i;
float conf_lvl;
const float *in = (const float *) input_items[0];
bool triggered;
size_t i;
const float *in_old = (const float *) input_items[0];
const float *in = in_old + history() - 1;
/* The estimation for the WPM has not yet been computed */
if(!d_have_sync){
for(i = 0; i < noutput_items; i++) {
switch (d_sync_state) {
case SYNC_TRIGGER_OFF:
if(in[i] > d_act_thrshld) {
d_sync_state = SYNC_TRIGGER_ON;
}
break;
case SYNC_TRIGGER_ON:
if(in[i] > d_act_thrshld) {
d_state_cnt++;
}
else {
/* Trigger is OFF. Extract the best timing information available */
d_sync_state = SYNC_TRIGGER_OFF;
estimate_dot_duration(d_state_cnt);
d_state_cnt = 0;
/*
* If the sync process was over set the state to idle and
* return for proper decoding
*/
if(d_have_sync) {
set_idle();
return i+1;
}
}
break;
}
}
return noutput_items;
if(noutput_items < 0) {
return noutput_items;
}
for(i = 0; i < noutput_items; i++) {
switch(d_state){
case IDLE:
if(in[i] > d_act_thrshld) {
set_short_on();
}
break;
case SHORT_ON_PERIOD:
if( in[i] > d_act_thrshld ) {
d_state_cnt++;
if(d_state_cnt > d_dot_samples){
set_long_on();
}
}
else{
/*
* Before going to short pause, check the confidence level.
* Perhaps a short symbol should be produced.
*
* Otherwise, it was a false alarm.
*/
conf_lvl = ((float) d_state_cnt) / d_dot_samples;
if(conf_lvl > d_confidence_level){
LOG_DEBUG("DOT");
send_symbol_msg(MORSE_DOT);
}
/* Go find a possible short pause symbol */
set_short_off();
}
break;
case LONG_ON_PERIOD:
if( in[i] > d_act_thrshld ) {
d_state_cnt++;
}
else {
conf_lvl = ((float) d_state_cnt) / d_dash_samples;
if(conf_lvl > d_confidence_level) {
LOG_DEBUG("DASH");
send_symbol_msg(MORSE_DASH);
set_short_off();
break;
}
/* Perhaps this was a short on symbol */
conf_lvl = ((float) d_state_cnt) / d_dot_samples;
if(conf_lvl > d_confidence_level) {
LOG_DEBUG("DOT");
send_symbol_msg(MORSE_DOT);
set_short_off();
}
}
break;
case SHORT_OFF_PERIOD:
if(in[i] > d_act_thrshld) {
/*
* Before going to ON state, again check if a short pause symbol
* should be produced
*/
conf_lvl = ((float) d_pause_cnt) / d_short_pause_samples;
if(conf_lvl > d_confidence_level) {
LOG_DEBUG("Short space");
send_symbol_msg(MORSE_S_SPACE);
}
set_short_on();
}
else {
d_pause_cnt++;
if(d_pause_cnt > d_short_pause_samples) {
set_long_off();
}
}
break;
case LONG_OFF_PERIOD:
if(in[i] > d_act_thrshld) {
conf_lvl = ((float) d_pause_cnt) / d_long_pause_samples;
if (conf_lvl > d_confidence_level) {
LOG_DEBUG("Long space");
send_symbol_msg (MORSE_L_SPACE);
set_idle();
break;
}
else {
LOG_DEBUG("Short space");
send_symbol_msg (MORSE_S_SPACE);
}
set_short_on();
}
else {
d_pause_cnt++;
/*
* If the pause duration is greater than the long pause symbol,
* definitely a long pause symbol should be produced
*/
if(d_pause_cnt > d_long_pause_samples) {
LOG_DEBUG("Long space");
send_symbol_msg(MORSE_L_SPACE);
d_seq_started = false;
set_idle();
}
}
break;
default:
LOG_ERROR("Invalid State.");
d_state = IDLE;
}
}
return noutput_items;
}
void
cw_to_symbol_impl::estimate_dot_duration (size_t estimate)
{
/*
* The estimations should be in a logical range of
* [MIN_WPM - MAX_WPM] WPM. Otherwise it is considered a false alarm.
*/
if( estimate < (1.2/MAX_WPM) / (1.0 / d_sampling_rate)
|| estimate >= (1.2/MIN_WPM) / (1.0 / d_sampling_rate)) {
return;
/* 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;
}
}
return noutput_items;
}
/*
* Keep the minimum dot duration. This is only for the sync process.
* At the end the resulting dot duration will be a mean value of
* possible dot durations
*/
if(estimate < d_dot_samples) {
d_dot_samples = estimate;
d_mean_cnt += estimate;
d_est_cnt++;
}
else if(mape(d_dot_samples, estimate) < 1 - d_confidence_level) {
d_mean_cnt += estimate;
d_est_cnt++;
}
/*
* Perhaps the synchronization process was triggered by a dash.
* Check this possibility and use MAPE and confidence level to
* decide or not to take into consideration the new estimation
*/
else if ( mape(d_dot_samples, estimate / 3.0) < 1 - d_confidence_level ) {
d_mean_cnt += estimate / 3.0;
d_est_cnt++;
}
/*
* If the synchronization process is over update the dot duration
* with an average estimation
*/
if(d_est_cnt == d_sync_limit) {
d_have_sync = true;
d_dot_samples = d_mean_cnt / d_sync_limit;
set_symbols_duration();
std::cout << "Have sync" << d_dot_samples << std::endl;
/* 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");
}
}
return i * d_window_size;
}
/**
* Sets the dash, short and long pause durations based on the dot estimated
* duration.
*/
void
cw_to_symbol_impl::set_symbols_duration ()
{
d_dash_samples = 3 * d_dot_samples;
d_short_pause_samples = d_dash_samples;
d_long_pause_samples = 7 * d_dot_samples;
}
/**
* Resets the estimated dot duration. After the call of this method
* the caller MUST initiate a timing recovery procedure.
*/
void
cw_to_symbol_impl::reset_sync ()
{
d_sync_state = SYNC_TRIGGER_OFF;
d_est_cnt = 0;
set_idle ();
d_have_sync = false;
d_dot_samples = (1.2 / MIN_WPM) / (1.0 / d_sampling_rate);
}
/**
* Sets a new activation threshold. If auto timing recovery is enabled,
* the process of finding automatically the WPM is initiated after the
* configuration of the new threshold.
* Sets a new activation threshold.
* @param thrhld the new threshold.
*/
void
cw_to_symbol_impl::set_act_threshold (float thrhld)
{
d_act_thrshld = thrhld;
/* When a new activation threshold is set,
* if the automatic synchronization is set it should be again performed
*/
if(d_auto_sync) {
reset_sync();
}
}
/**
* Starts the timing recovery process fro automatically retrieving the best
* WPM estimation.
* 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
*/
void
cw_to_symbol_impl::start_timing_recovery ()
inline void
cw_to_symbol_impl::clamp_input (int32_t* out, const float* in, size_t len)
{
reset_sync();
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;
}
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 */

View File

@ -2,7 +2,8 @@
/*
* gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
*
* Copyright (C) 2016, Libre Space Foundation <http://librespacefoundation.org/>
* Copyright (C) 2016,2017
* Libre Space Foundation <http://librespacefoundation.org/>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -31,40 +32,28 @@ namespace gr
class cw_to_symbol_impl : public cw_to_symbol
{
typedef enum {
IDLE,
SHORT_ON_PERIOD,
LONG_ON_PERIOD,
SHORT_OFF_PERIOD,
LONG_OFF_PERIOD
} cw_state_t;
/**
* Different states during the WPM auto synchronization
*/
typedef enum {
SYNC_TRIGGER_OFF,//!< SYNC_TRIGGER_OFF Signal is below threshold
SYNC_TRIGGER_ON //!< SYNC_TRIGGER_ON Signal is above threshold
} sync_state_t;
typedef enum
{
NO_SYNC, SEARCH_DOT, SEARCH_DASH, SEARCH_SPACE
} cw_dec_state_t;
private:
const double d_sampling_rate;
float d_act_thrshld;
const float d_confidence_level;
const size_t d_sync_limit;
const bool d_auto_sync;
size_t d_dot_samples;
size_t d_dash_samples;
size_t d_short_pause_samples;
size_t d_long_pause_samples;
cw_state_t d_state;
size_t d_state_cnt;
size_t d_pause_cnt;
size_t d_est_cnt;
size_t d_mean_cnt;
bool d_have_sync;
bool d_seq_started;
sync_state_t d_sync_state;
size_t d_window_size;
size_t d_window_cnt;
size_t d_dot_windows_num;
size_t d_dash_windows_num;
size_t d_short_pause_windows_num;
size_t d_long_pause_windows_num;
cw_dec_state_t d_dec_state;
bool d_prev_space_symbol;
float *d_const_val;
float *d_tmp;
int32_t *d_out;
inline void
set_idle ();
@ -76,37 +65,35 @@ namespace gr
set_long_on ();
inline void
set_short_off ();
set_search_space ();
inline void
set_long_off ();
clamp_input (int32_t *out, const float *in, size_t len);
inline bool
is_triggered (const float *in, size_t len);
inline void
send_symbol_msg (morse_symbol_t s);
void set_act_threshold_msg_handler(pmt::pmt_t msg);
inline bool
check_conf_level(size_t cnt, size_t target);
void sync_msg_handler(pmt::pmt_t msg);
void estimate_dot_duration(size_t estimate);
void set_symbols_duration();
void reset_sync();
void
set_act_threshold_msg_handler (pmt::pmt_t msg);
public:
cw_to_symbol_impl (double sampling_rate, float threshold,
float conf_level, size_t wpm, bool auto_config);
float conf_level, size_t wpm);
~cw_to_symbol_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);
gr_vector_void_star &output_items);
void set_act_threshold(float thrhld);
void start_timing_recovery();
void
set_act_threshold (float thrhld);
};
} // namespace satnogs

View File

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

View File

@ -2,7 +2,8 @@
/*
* gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
*
* Copyright (C) 2016, Libre Space Foundation <http://librespacefoundation.org/>
* Copyright (C) 2016, 2017
* Libre Space Foundation <http://librespacefoundation.org/>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -26,26 +27,31 @@
#include <algorithm>
#include <vector>
namespace gr {
namespace satnogs {
namespace gr
{
namespace satnogs
{
class morse_debug_source_impl : public morse_debug_source
{
private:
private:
const size_t d_wpm;
const bool d_inject_errors;
const float d_p;
const size_t d_seq_pause_ms;
bool d_run;
const char d_chars[36];
const std::vector<std::string> d_symbols;
std::thread d_thread;
void
send_debug_msg(std::string sentence);
send_debug_msg (std::string sentence);
public:
morse_debug_source_impl(std::string debug_seq, bool inject_errors,
float error_prob);
~morse_debug_source_impl();
public:
morse_debug_source_impl (const size_t wpm, std::string debug_seq,
bool inject_errors,
float error_prob, size_t seq_pause_ms);
~morse_debug_source_impl ();
};
} // namespace satnogs

View File

@ -2,7 +2,8 @@
/*
* gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
*
* Copyright (C) 2016, Libre Space Foundation <http://librespacefoundation.org/>
* Copyright (C) 2016, 2017
* Libre Space Foundation <http://librespacefoundation.org/>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -34,61 +35,63 @@ namespace gr
morse_decoder::make (char unrecognized_char)
{
return gnuradio::get_initial_sptr (
new morse_decoder_impl (unrecognized_char));
new morse_decoder_impl (unrecognized_char));
}
void
morse_decoder_impl::symbol_msg_handler (pmt::pmt_t msg)
{
bool res;
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();
d_morse_tree.reset();
message_port_pub(pmt::mp("out"), pmt::make_blob(str.c_str(),
str.length()));
break;
default:
LOG_ERROR("Unknown Morse symbol");
return;
}
switch (s)
{
case MORSE_DOT:
case MORSE_DASH:
case MORSE_S_SPACE:
res = d_morse_tree.received_symbol (s);
break;
/*
* If a word separator occurs it is a good time to retrieve the decoded
* word
*/
case MORSE_L_SPACE:
/*
* Inject a character separator, for the morse decoder to commit
* the outstanding character
*/
res = d_morse_tree.received_symbol (MORSE_S_SPACE);
/* Just ignore the word separator if no word is yet decoded */
if (d_morse_tree.get_word_len () == 0) {
res = true;
break;
}
str = d_morse_tree.get_word ();
d_morse_tree.reset ();
message_port_pub (pmt::mp ("out"),
pmt::make_blob (str.c_str (), str.length ()));
break;
case MORSE_INTRA_SPACE:
/*Ignore it */
break;
default:
LOG_ERROR("Unknown Morse symbol");
return;
}
/*
* If the decoding return false, it means that either an non decode-able
* character situation occurred or the maximum word limit reached
*/
if (!s) {
if(d_morse_tree.get_max_word_len() == d_morse_tree.get_word_len()){
str = d_morse_tree.get_word();
d_morse_tree.reset();
std::cout << "Received word: " << str << std::endl;
}
}
else{
LOG_DEBUG("Something went wrong");
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 ()));
}
}
}
@ -96,17 +99,16 @@ namespace gr
* The private constructor
*/
morse_decoder_impl::morse_decoder_impl (char unrecognized_char) :
gr::block ("morse_decoder",
gr::io_signature::make (0, 0, 0),
gr::io_signature::make (0, 0, 0)),
d_morse_tree (unrecognized_char)
gr::block ("morse_decoder", gr::io_signature::make (0, 0, 0),
gr::io_signature::make (0, 0, 0)),
d_morse_tree (unrecognized_char)
{
/* Register the input and output msg handler */
message_port_register_in (pmt::mp ("in"));
message_port_register_out(pmt::mp("out"));
message_port_register_out (pmt::mp ("out"));
set_msg_handler (
pmt::mp ("in"),
boost::bind (&morse_decoder_impl::symbol_msg_handler, this, _1));
pmt::mp ("in"),
boost::bind (&morse_decoder_impl::symbol_msg_handler, this, _1));
}
} /* namespace satnogs */

View File

@ -2,7 +2,8 @@
/*
* gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
*
* Copyright (C) 2016, Libre Space Foundation <http://librespacefoundation.org/>
* Copyright (C) 2016,2017
* Libre Space Foundation <http://librespacefoundation.org/>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -36,12 +37,12 @@ namespace gr
* Constructs a Morse code tree for Morse code decoding
*/
morse_tree::morse_tree () :
d_unrecognized_symbol('#'),
d_root (new tree_node(0)),
d_current (d_root),
d_buff_len(4096),
d_word_len(0),
d_word_buffer(new char[d_buff_len])
d_unrecognized_symbol ('#'),
d_root (new tree_node (0)),
d_current (d_root),
d_buff_len (4096),
d_word_len (0),
d_word_buffer (new char[d_buff_len])
{
construct_tree ();
}
@ -52,12 +53,12 @@ namespace gr
* in the place of unrecognized symbols
*/
morse_tree::morse_tree (char unrecognized) :
d_unrecognized_symbol(unrecognized),
d_root (new tree_node(0)),
d_current (d_root),
d_buff_len(4096),
d_word_len(0),
d_word_buffer(new char[d_buff_len])
d_unrecognized_symbol (unrecognized),
d_root (new tree_node (0)),
d_current (d_root),
d_buff_len (4096),
d_word_len (0),
d_word_buffer (new char[d_buff_len])
{
construct_tree ();
}
@ -200,46 +201,46 @@ namespace gr
bool ret = false;
/* Check for overflow */
if (d_word_len == d_buff_len) {
return false;
return false;
}
switch (s)
{
case MORSE_DOT:
if (d_current->get_left_child ()) {
d_current = d_current->get_left_child ();
ret = true;
}
break;
case MORSE_DASH:
if (d_current->get_right_child ()) {
d_current = d_current->get_right_child ();
ret = true;
}
break;
case MORSE_S_SPACE:
/*
* A short space received, but the decoder is still at the root.
* This is not in general an error so we return true
*/
if(d_current == d_root){
return true;
}
c = d_current->get_char ();
d_current = d_root;
/*
* Some nodes are null transitions and do not correspond to
* a specific character
*/
if (c != 0) {
d_word_buffer[d_word_len] = c;
d_word_len++;
ret = true;
}
break;
default:
LOG_ERROR("Unsupported Morse symbol");
return false;
}
{
case MORSE_DOT:
if (d_current->get_left_child ()) {
d_current = d_current->get_left_child ();
ret = true;
}
break;
case MORSE_DASH:
if (d_current->get_right_child ()) {
d_current = d_current->get_right_child ();
ret = true;
}
break;
case MORSE_S_SPACE:
/*
* A short space received, but the decoder is still at the root.
* This is not in general an error so we return true
*/
if (d_current == d_root) {
return true;
}
c = d_current->get_char ();
d_current = d_root;
/*
* Some nodes are null transitions and do not correspond to
* a specific character
*/
if (c != 0) {
d_word_buffer[d_word_len] = c;
d_word_len++;
ret = true;
}
break;
default:
LOG_ERROR("Unsupported Morse symbol");
return false;
}
return ret;
}
@ -265,7 +266,7 @@ namespace gr
morse_tree::delete_tree (tree_node *node)
{
if (!node) {
return;
return;
}
delete_tree (node->get_left_child ());
delete_tree (node->get_right_child ());
@ -273,7 +274,9 @@ namespace gr
}
tree_node::tree_node (char c) :
d_char (c), d_left (NULL), d_right (NULL)
d_char (c),
d_left (NULL),
d_right (NULL)
{
}

View File

@ -2,7 +2,7 @@
/*
* gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
*
* Copyright (C) 2016, Libre Space Foundation <http://librespacefoundation.org/>
* Copyright (C) 2016,2017 Libre Space Foundation <http://librespacefoundation.org/>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -24,58 +24,136 @@
#include <gnuradio/io_signature.h>
#include "multi_format_msg_sink_impl.h"
#include <ctime>
#include <iostream>
#include <iomanip>
namespace gr {
namespace satnogs {
namespace gr
{
namespace satnogs
{
multi_format_msg_sink::sptr
multi_format_msg_sink::make(size_t format)
multi_format_msg_sink::make (size_t format,
bool timestamp,
bool out_stdout,
const std::string& filepath)
{
return gnuradio::get_initial_sptr
(new multi_format_msg_sink_impl(format));
return gnuradio::get_initial_sptr (
new multi_format_msg_sink_impl (format, timestamp,
out_stdout, filepath));
}
void
multi_format_msg_sink_impl::msg_handler (pmt::pmt_t msg)
multi_format_msg_sink_impl::msg_handler_file (pmt::pmt_t msg)
{
uint8_t *su;
std::string s((const char *)pmt::blob_data(msg), pmt::blob_length(msg));
switch(d_format){
std::string s ((const char *) pmt::blob_data (msg),
pmt::blob_length (msg));
if(d_timestamp) {
std::time_t t = std::time(nullptr);
std::tm tm = *std::localtime(&t);
d_fos << "[" << std::put_time(&tm, "%F %T %z") << "] ";
}
switch (d_format)
{
case 0:
std::cout << "Received text sequence:" << s << " " << std::endl;
d_fos << s << std::endl;
break;
case 1:
su = (uint8_t *)pmt::blob_data(msg);
for (size_t i = 0; i < pmt::blob_length (msg); i++) {
printf ("0x%02x ", su[i]);
}
std::cout << std::endl;
su = (uint8_t *) pmt::blob_data (msg);
for (size_t i = 0; i < pmt::blob_length (msg); i++) {
d_fos << std::hex << std::showbase << std::setw (4)
<< (uint32_t) su[i] << " ";
}
d_fos << std::endl;
break;
case 2:
su = (uint8_t *)pmt::blob_data(msg);
for (size_t i = 0; i < pmt::blob_length (msg); i++) {
std::cout << "0b" << std::bitset<8> (su[i]) << " ";
}
std::cout << std::endl;
su = (uint8_t *) pmt::blob_data (msg);
for (size_t i = 0; i < pmt::blob_length (msg); i++) {
d_fos << "0b" << std::bitset<8> (su[i]) << " ";
}
d_fos << std::endl;
break;
default:
printf("Invalid format");
throw std::invalid_argument("Invalid format");
}
}
void
multi_format_msg_sink_impl::msg_handler_stdout (pmt::pmt_t msg)
{
uint8_t *su;
std::string s ((const char *) pmt::blob_data (msg),
pmt::blob_length (msg));
if(d_timestamp) {
std::time_t t = std::time(nullptr);
std::tm tm = *std::localtime(&t);
std::cout << "[" << std::put_time(&tm, "%F %T %z") << "] ";
}
switch (d_format)
{
case 0:
std::cout << s << std::endl;
break;
case 1:
su = (uint8_t *) pmt::blob_data (msg);
for (size_t i = 0; i < pmt::blob_length (msg); i++) {
std::cout << std::hex << std::showbase << std::setw (4)
<< (uint32_t) su[i] << " ";
}
std::cout << std::endl;
break;
case 2:
su = (uint8_t *) pmt::blob_data (msg);
for (size_t i = 0; i < pmt::blob_length (msg); i++) {
std::cout << "0b" << std::bitset<8> (su[i]) << " ";
}
std::cout << std::endl;
break;
default:
throw std::invalid_argument("Invalid format");
}
}
/*
* The private constructor
*/
multi_format_msg_sink_impl::multi_format_msg_sink_impl(size_t format)
: gr::block("multi_format_msg_sink",
gr::io_signature::make(0, 0, 0),
gr::io_signature::make(0, 0, 0)),
d_format(format)
multi_format_msg_sink_impl::multi_format_msg_sink_impl (
size_t format, bool timestamp, bool out_stdout,
const std::string& filepath) :
gr::block ("multi_format_msg_sink",
gr::io_signature::make (0, 0, 0),
gr::io_signature::make (0, 0, 0)),
d_format (format),
d_timestamp (timestamp),
d_stdout (out_stdout)
{
message_port_register_in(pmt::mp("in"));
set_msg_handler (
pmt::mp ("in"),
boost::bind (&multi_format_msg_sink_impl::msg_handler, this, _1));
message_port_register_in (pmt::mp ("in"));
if(out_stdout) {
set_msg_handler (
pmt::mp ("in"),
boost::bind (&multi_format_msg_sink_impl::msg_handler_stdout,
this, _1));
}
else{
d_fos.open(filepath);
set_msg_handler (
pmt::mp ("in"),
boost::bind (&multi_format_msg_sink_impl::msg_handler_file,
this, _1));
}
}
multi_format_msg_sink_impl::~multi_format_msg_sink_impl ()
{
if(!d_stdout) {
d_fos.close();
}
}
} /* namespace satnogs */

View File

@ -2,7 +2,7 @@
/*
* gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
*
* Copyright (C) 2016, Libre Space Foundation <http://librespacefoundation.org/>
* Copyright (C) 2016,2017, Libre Space Foundation <http://librespacefoundation.org/>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -22,6 +22,7 @@
#define INCLUDED_SATNOGS_MULTI_FORMAT_MSG_SINK_IMPL_H
#include <satnogs/multi_format_msg_sink.h>
#include <fstream>
namespace gr
{
@ -32,12 +33,20 @@ namespace gr
{
private:
void
msg_handler(pmt::pmt_t msg);
msg_handler_stdout (pmt::pmt_t msg);
void
msg_handler_file (pmt::pmt_t msg);
size_t d_format;
const size_t d_format;
const bool d_timestamp;
const bool d_stdout;
std::ofstream d_fos;
public:
multi_format_msg_sink_impl (size_t format);
multi_format_msg_sink_impl (size_t format, bool timestamp,
bool out_stdout, const std::string& filepath);
~multi_format_msg_sink_impl ();
};

View File

@ -27,6 +27,7 @@
#include <vorbis/vorbisfile.h>
#include <volk/volk.h>
#include <satnogs/log.h>
#include "ogg_source_impl.h"
#define PCM_BUF_SIZE 4096
@ -35,21 +36,22 @@ namespace gr {
namespace satnogs {
ogg_source::sptr
ogg_source::make(const std::string& filename, size_t channels)
ogg_source::make (const std::string& filename, size_t channels, bool repeat)
{
return gnuradio::get_initial_sptr
(new ogg_source_impl(filename, channels));
return gnuradio::get_initial_sptr (
new ogg_source_impl (filename, channels, repeat));
}
/*
* The private constructor
*/
ogg_source_impl::ogg_source_impl (const std::string& filename,
size_t channels) :
size_t channels, bool repeat) :
gr::sync_block (
"ogg_source", gr::io_signature::make (0, 0, 0),
gr::io_signature::make (channels, channels, sizeof(float))),
d_channels (channels)
d_channels (channels),
d_repeat (repeat)
{
if (channels < 1) {
throw std::invalid_argument ("At least one output channels should"
@ -60,8 +62,8 @@ namespace gr {
throw std::invalid_argument ("Invalid .ogg file");
}
vorbis_info *vi = ov_info(&d_ogvorb_f,-1);
if(vi->channels != (int) channels) {
vorbis_info *vi = ov_info (&d_ogvorb_f, -1);
if (vi->channels != (int) channels) {
throw std::invalid_argument (
std::string ("Channels number specified (")
+ std::to_string (channels)
@ -69,14 +71,17 @@ namespace gr {
"the ogg stream (" + std::to_string (vi->channels) + ")");
}
const int alignment_multiple = volk_get_alignment() / sizeof(float);
set_alignment(std::max(1,alignment_multiple));
set_max_noutput_items(PCM_BUF_SIZE);
const int alignment_multiple = volk_get_alignment () / sizeof(float);
set_alignment (std::max (1, alignment_multiple));
set_max_noutput_items (PCM_BUF_SIZE);
d_in_buffer = (int16_t *)volk_malloc(PCM_BUF_SIZE * sizeof(int16_t),
volk_get_alignment());
d_out_buffer = (float *)volk_malloc(PCM_BUF_SIZE * sizeof(float),
volk_get_alignment());
d_in_buffer = (int16_t *) volk_malloc (PCM_BUF_SIZE * sizeof(int16_t),
volk_get_alignment ());
d_out_buffer = (float *) volk_malloc (PCM_BUF_SIZE * sizeof(float),
volk_get_alignment ());
if(!d_in_buffer || !d_out_buffer) {
throw std::runtime_error("Could not allocate memory");
}
}
/*
@ -103,6 +108,17 @@ namespace gr {
available * sizeof(int16_t),
0, sizeof(int16_t), 1, &section);
if(ret < sizeof(int16_t)) {
/*
* If return value is EOF and the repeat mode is set seek back to the
* start of the ogg stream
*/
if(ret == 0 && d_repeat) {
if(ov_seekable(&d_ogvorb_f)){
ov_time_seek(&d_ogvorb_f, 0);
return 0;
}
LOG_WARN("File is not seakable.");
}
return WORK_DONE;
}

View File

@ -33,14 +33,16 @@ namespace gr
class ogg_source_impl : public ogg_source
{
private:
const size_t d_channels;
OggVorbis_File d_ogvorb_f;
const size_t d_channels;
const bool d_repeat;
OggVorbis_File d_ogvorb_f;
int16_t *d_in_buffer;
float *d_out_buffer;
int16_t *d_in_buffer;
float *d_out_buffer;
public:
ogg_source_impl (const std::string& filename, size_t channels);
ogg_source_impl (const std::string& filename, size_t channels,
bool repeat);
~ogg_source_impl ();
// Where all the action really happens

View File

@ -9,7 +9,6 @@
%include "satnogs_swig_doc.i"
%{
#include "satnogs/cw_matched_filter_ff.h"
#include "satnogs/morse_tree.h"
#include "satnogs/morse_decoder.h"
#include "satnogs/morse_debug_source.h"
@ -36,12 +35,10 @@
#include "satnogs/noaa_apt_sink.h"
#include "satnogs/frame_file_sink.h"
#include "satnogs/iq_sink.h"
#include "satnogs/cw_encoder.h"
%}
%include "satnogs/cw_matched_filter_ff.h"
GR_SWIG_BLOCK_MAGIC2(satnogs, cw_matched_filter_ff);
%include "satnogs/morse_tree.h"
%include "satnogs/morse_decoder.h"
@ -112,3 +109,5 @@ GR_SWIG_BLOCK_MAGIC2(satnogs, noaa_apt_sink);
GR_SWIG_BLOCK_MAGIC2(satnogs, frame_file_sink);
%include "satnogs/iq_sink.h"
GR_SWIG_BLOCK_MAGIC2(satnogs, iq_sink);
%include "satnogs/cw_encoder.h"
GR_SWIG_BLOCK_MAGIC2(satnogs, cw_encoder);