From c23c0aaf4cc5d2de17149eb7212096fb0afb58d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Mon, 28 Aug 2023 18:11:18 +0200 Subject: [PATCH 01/29] Update Debian changelog (cherry picked from commit 0fbccddd53a20bb22315ce499315a17846933a57) --- debian/changelog | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/debian/changelog b/debian/changelog index 4458e307..d3545993 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,15 @@ +gr-satellites (5.4.0-1) kinetic; urgency=medium + + * Mainstream release v5.4.0 + + -- Mon, 28 Aug 2023 16:10:00 +0000 + +gr-satellites (5.4.0-0) jammy; urgency=medium + + * Mainstream release v5.4.0 + + -- Mon, 28 Aug 2023 16:10:00 +0000 + gr-satellites (5.3.0-1) kinetic; urgency=medium * Mainstream release v5.3.0 From 6134e116c7c47d1ddc8e64d5944a6a8173896812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Mon, 28 Aug 2023 18:17:02 +0200 Subject: [PATCH 02/29] Up to v5.5.0-git --- CMakeLists.txt | 2 +- docs/source/conf.py | 2 +- python/__init__.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3799f135..f778ea59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake/Modules) # Set the version information here set(VERSION_MAJOR 5) -set(VERSION_API 4) +set(VERSION_API 5) set(VERSION_ABI 0) set(VERSION_PATCH git) diff --git a/docs/source/conf.py b/docs/source/conf.py index 39d30f64..13961ab0 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -26,7 +26,7 @@ # The short X.Y version version = '' # The full version, including alpha/beta/rc tags -release = '5.4.0-git' +release = '5.5.0-git' # -- General configuration --------------------------------------------------- diff --git a/python/__init__.py b/python/__init__.py index 4b724d78..bb178d23 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -19,7 +19,7 @@ ad-hoc protocols used in other satellites. """ -__version__ = 'v5.4.0-git' +__version__ = 'v5.5.0-git' __author__ = 'Daniel Estevez' __copyright__ = 'Copyright 2016-2023 Daniel Estevez' __email__ = 'daniel@destevez.net' From c265ce6a5b8e33ee1c2db0fe39c3e5085d3dc22f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Mon, 28 Aug 2023 22:54:55 +0200 Subject: [PATCH 03/29] Update readthedocs.yml to include build os The docs build was failing because this parameter was missing. --- readthedocs.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/readthedocs.yml b/readthedocs.yml index f7ec88bc..152c5c60 100644 --- a/readthedocs.yml +++ b/readthedocs.yml @@ -5,6 +5,12 @@ # Required version: 2 +# Set the OS, Python version and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.11" + # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/source/conf.py From 40e3ad3f8465f0fb6a592bd94800051ba85b7fc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Mon, 4 Sep 2023 11:40:14 +0200 Subject: [PATCH 04/29] Port Manchester Sync to C++ This ports the Manchester Sync block to C++, and changes the algorithm that is used to detect the phase of the Manchester clock. The new algorithm is based on maximum likelihood and works by blocks. The metrics for a block of N samples are computed, and the output is formed for that block is formed based on the comparison of those metrics. --- grc/satellites_manchester_sync.block.yml | 17 ++- include/satellites/CMakeLists.txt | 1 + include/satellites/manchester_sync.h | 55 ++++++++++ lib/CMakeLists.txt | 1 + lib/manchester_sync_impl.cc | 100 ++++++++++++++++++ lib/manchester_sync_impl.h | 42 ++++++++ python/CMakeLists.txt | 2 +- python/__init__.py | 1 - python/bindings/CMakeLists.txt | 1 + .../manchester_sync_pydoc_template.h | 18 ++++ python/bindings/manchester_sync_python.cc | 37 +++++++ python/bindings/python_bindings.cc | 2 + .../demodulators/bpsk_demodulator.py | 14 +-- python/manchester_sync.py | 59 ----------- python/qa_manchester_sync.py | 74 +++++++++++++ 15 files changed, 352 insertions(+), 72 deletions(-) create mode 100644 include/satellites/manchester_sync.h create mode 100644 lib/manchester_sync_impl.cc create mode 100644 lib/manchester_sync_impl.h create mode 100644 python/bindings/docstrings/manchester_sync_pydoc_template.h create mode 100644 python/bindings/manchester_sync_python.cc delete mode 100644 python/manchester_sync.py create mode 100755 python/qa_manchester_sync.py diff --git a/grc/satellites_manchester_sync.block.yml b/grc/satellites_manchester_sync.block.yml index fd0333c7..3c4c22c5 100644 --- a/grc/satellites_manchester_sync.block.yml +++ b/grc/satellites_manchester_sync.block.yml @@ -3,21 +3,28 @@ label: Manchester Sync category: '[Satellites]/Coding' parameters: -- id: history - label: History +- id: type + label: IO Type + dtype: enum + options: [complex, float] + option_attributes: + fcn: [cc, ff] + hide: part +- id: block_size + label: Block Size dtype: int default: 0 inputs: - domain: stream - dtype: complex + dtype: ${ type } outputs: - domain: stream - dtype: complex + dtype: ${ type } templates: imports: import satellites - make: satellites.manchester_sync(history = ${history}) + make: satellites.manchester_sync_${type.fcn}(${block_size}) file_format: 1 diff --git a/include/satellites/CMakeLists.txt b/include/satellites/CMakeLists.txt index 18641d75..369f82ab 100644 --- a/include/satellites/CMakeLists.txt +++ b/include/satellites/CMakeLists.txt @@ -37,6 +37,7 @@ install(FILES encode_rs.h fixedlen_to_pdu.h lilacsat1_demux.h + manchester_sync.h matrix_deinterleaver_soft.h nrzi_decode.h nrzi_encode.h diff --git a/include/satellites/manchester_sync.h b/include/satellites/manchester_sync.h new file mode 100644 index 00000000..02bac82e --- /dev/null +++ b/include/satellites/manchester_sync.h @@ -0,0 +1,55 @@ +/* -*- c++ -*- */ +/* + * Copyright 2023 Daniel Estevez . + * + * This file is part of gr-satellites + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#ifndef INCLUDED_SATELLITES_MANCHESTER_SYNC_H +#define INCLUDED_SATELLITES_MANCHESTER_SYNC_H + +#include +#include + +namespace gr { +namespace satellites { + +/*! + * \brief Detects phase of a Manchester clock and integrates symbols + * \ingroup satellites + * + * \details + * The Manchester Sync block operates on a Manchester coded signal at + * two samples per symbol (one sample per each half of the Manchester + * pulse). The block detects which is the phase of the Manchester clock + * (i.e., what are the symbol boundaries), integrates symbols by + * subtracting the appropriate halves, and outputs the symbols at one + * sample per symbol. + * + * The detection of the Manchester clock phase is done using a high + * SNR approximation to the maximum likelihood metric. Detection is + * done blockwise, with the size of the block indicated in the constructor. + */ +template +class SATELLITES_API manchester_sync : virtual public gr::sync_decimator +{ +public: + typedef std::shared_ptr> sptr; + + /*! + * \brief Constructs a Manchester Sync block. + * + * \param block_size Size of the block for metric evaluation (in symbols). + */ + static sptr make(int block_size); +}; + +typedef manchester_sync manchester_sync_cc; +typedef manchester_sync manchester_sync_ff; + +} // namespace satellites +} // namespace gr + +#endif /* INCLUDED_SATELLITES_MANCHESTER_SYNC_H */ diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 63db46a4..8e35792a 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -58,6 +58,7 @@ list(APPEND satellites_sources fixedlen_to_pdu_impl.cc golay24.c lilacsat1_demux_impl.cc + manchester_sync_impl.cc matrix_deinterleaver_soft_impl.cc nrzi_decode_impl.cc nrzi_encode_impl.cc diff --git a/lib/manchester_sync_impl.cc b/lib/manchester_sync_impl.cc new file mode 100644 index 00000000..7f0da4bf --- /dev/null +++ b/lib/manchester_sync_impl.cc @@ -0,0 +1,100 @@ +/* -*- c++ -*- */ +/* + * Copyright 2023 Daniel Estevez . + * + * This file is part of gr-satellites + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "manchester_sync_impl.h" +#include +#include +#include +#include + +namespace gr { +namespace satellites { + +template +typename manchester_sync::sptr manchester_sync::make(int block_size) +{ + return gnuradio::make_block_sptr>(block_size); +} + + +template +manchester_sync_impl::manchester_sync_impl(int block_size) + : gr::sync_decimator("manchester_sync", + gr::io_signature::make(1, 1, sizeof(T)), + gr::io_signature::make(1, 1, sizeof(T)), + 2) +{ + this->d_diffs_0.resize(block_size); + this->d_diffs_1.resize(block_size); + this->d_abs.resize(block_size); + this->set_history(2); + this->set_output_multiple(block_size); +} + +template +manchester_sync_impl::~manchester_sync_impl() +{ +} + +template +int manchester_sync_impl::work(int noutput_items, + gr_vector_const_void_star& input_items, + gr_vector_void_star& output_items) +{ + auto in = static_cast(input_items[0]); + auto out = static_cast(output_items[0]); + + const int block_size = d_diffs_0.size(); + + for (int j = 0; j < noutput_items; j += block_size) { + compute_diff(&d_diffs_0[0], &in[2 * j], block_size); + compute_diff(&d_diffs_1[0], &in[2 * j + 1], block_size); + compute_abs(&d_abs[0], &d_diffs_0[0], block_size); + float metric0; + volk_32f_accumulator_s32f(&metric0, &d_abs[0], block_size); + compute_abs(&d_abs[0], &d_diffs_1[0], block_size); + float metric1; + volk_32f_accumulator_s32f(&metric1, &d_abs[0], block_size); + const auto diff = metric0 > metric1 ? &d_diffs_0[0] : &d_diffs_1[0]; + std::memcpy(&out[j], diff, block_size * sizeof(T)); + } + + return noutput_items; +} + +template +void manchester_sync_impl::compute_diff(T* out, const T* in, int block_size) +{ + for (int j = 0; j < block_size; ++j) { + out[j] = 0.5f * (in[2 * j] - in[2 * j + 1]); + } +} + +template <> +void manchester_sync_impl::compute_abs(float* out, + const gr_complex* in, + int block_size) +{ + volk_32fc_magnitude_32f(out, in, block_size); +} + +template <> +void manchester_sync_impl::compute_abs(float* out, const float* in, int block_size) +{ + // There is no Volk kernel for abs of 32f + for (int j = 0; j < block_size; ++j) { + out[j] = fabsf(in[j]); + } +} + +template class manchester_sync; +template class manchester_sync; + +} /* namespace satellites */ +} /* namespace gr */ diff --git a/lib/manchester_sync_impl.h b/lib/manchester_sync_impl.h new file mode 100644 index 00000000..9ea8ba77 --- /dev/null +++ b/lib/manchester_sync_impl.h @@ -0,0 +1,42 @@ +/* -*- c++ -*- */ +/* + * Copyright 2023 Daniel Estevez . + * + * This file is part of gr-satellites + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#ifndef INCLUDED_SATELLITES_MANCHESTER_SYNC_IMPL_H +#define INCLUDED_SATELLITES_MANCHESTER_SYNC_IMPL_H + +#include +#include + +namespace gr { +namespace satellites { + +template +class manchester_sync_impl : public manchester_sync +{ +private: + volk::vector d_diffs_0; + volk::vector d_diffs_1; + volk::vector d_abs; + + static inline void compute_diff(T* out, const T* in, int block_size); + static inline void compute_abs(float* out, const T* in, int block_size); + +public: + manchester_sync_impl(int block_size); + ~manchester_sync_impl() override; + + int work(int noutput_items, + gr_vector_const_void_star& input_items, + gr_vector_void_star& output_items) override; +}; + +} // namespace satellites +} // namespace gr + +#endif /* INCLUDED_SATELLITES_MANCHESTER_SYNC_IMPL_H */ diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 0807f447..ef0aede3 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -73,7 +73,6 @@ GR_PYTHON_INSTALL( kiss_to_pdu.py ks1q_header_remover.py lilacsat1_gps_kml.py - manchester_sync.py ngham_check_crc.py ngham_packet_crop.py ngham_remove_padding.py @@ -118,6 +117,7 @@ GR_ADD_TEST(qa_fixedlen_tagger ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/ GR_ADD_TEST(qa_fixedlen_to_pdu ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_fixedlen_to_pdu.py) GR_ADD_TEST(qa_hdlc ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_hdlc.py) GR_ADD_TEST(qa_kiss ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_kiss.py) +GR_ADD_TEST(qa_manchester_sync ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_manchester_sync.py) GR_ADD_TEST(qa_nrzi ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_nrzi.py) GR_ADD_TEST(qa_pdu_add_meta ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_pdu_add_meta.py) GR_ADD_TEST(qa_pdu_head_tail ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_pdu_head_tail.py) diff --git a/python/__init__.py b/python/__init__.py index bb178d23..bcbe0332 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -77,7 +77,6 @@ from .kiss_to_pdu import kiss_to_pdu from .ks1q_header_remover import ks1q_header_remover from .lilacsat1_gps_kml import lilacsat1_gps_kml -from .manchester_sync import manchester_sync from .ngham_check_crc import ngham_check_crc from .ngham_packet_crop import ngham_packet_crop from .ngham_remove_padding import ngham_remove_padding diff --git a/python/bindings/CMakeLists.txt b/python/bindings/CMakeLists.txt index cf2a3ce0..38802ad5 100644 --- a/python/bindings/CMakeLists.txt +++ b/python/bindings/CMakeLists.txt @@ -43,6 +43,7 @@ list(APPEND satellites_python_files encode_rs_python.cc fixedlen_to_pdu_python.cc lilacsat1_demux_python.cc + manchester_sync_python.cc matrix_deinterleaver_soft_python.cc nrzi_decode_python.cc nrzi_encode_python.cc diff --git a/python/bindings/docstrings/manchester_sync_pydoc_template.h b/python/bindings/docstrings/manchester_sync_pydoc_template.h new file mode 100644 index 00000000..707fbf3a --- /dev/null +++ b/python/bindings/docstrings/manchester_sync_pydoc_template.h @@ -0,0 +1,18 @@ +/* + * Copyright 2023 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ +#include "pydoc_macros.h" +#define D(...) DOC(gr, satellites, __VA_ARGS__) +/* + This file contains placeholders for docstrings for the Python bindings. + Do not edit! These were automatically extracted during the binding process + and will be overwritten during the build process + */ +static const char* __doc_gr_satellites_manchester_sync = R"doc()doc"; +static const char* __doc_gr_satellites_manchester_sync_manchester_sync = R"doc()doc"; +static const char* __doc_gr_satellites_manchester_sync_make = R"doc()doc"; diff --git a/python/bindings/manchester_sync_python.cc b/python/bindings/manchester_sync_python.cc new file mode 100644 index 00000000..0e17b7f2 --- /dev/null +++ b/python/bindings/manchester_sync_python.cc @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#include +#include +#include + +namespace py = pybind11; + +#include +// pydoc.h is automatically generated in the build directory +#include + +template +void bind_manchester_sync_template(py::module& m, const char* classname) +{ + using manchester_sync = ::gr::satellites::manchester_sync; + + py::class_>(m, classname, D(manchester_sync)) + .def(py::init(&manchester_sync::make), py::arg("block_size")); +} + +void bind_manchester_sync(py::module& m) +{ + bind_manchester_sync_template(m, "manchester_sync_cc"); + bind_manchester_sync_template(m, "manchester_sync_ff"); +} diff --git a/python/bindings/python_bindings.cc b/python/bindings/python_bindings.cc index cbcc6208..e796a37c 100644 --- a/python/bindings/python_bindings.cc +++ b/python/bindings/python_bindings.cc @@ -35,6 +35,7 @@ void bind_doppler_correction(py::module& m); void bind_encode_rs(py::module& m); void bind_fixedlen_to_pdu(py::module& m); void bind_lilacsat1_demux(py::module& m); +void bind_manchester_sync(py::module& m); void bind_matrix_deinterleaver_soft(py::module& m); void bind_nrzi_decode(py::module& m); void bind_nrzi_encode(py::module& m); @@ -91,6 +92,7 @@ PYBIND11_MODULE(satellites_python, m) bind_encode_rs(m); bind_fixedlen_to_pdu(m); bind_lilacsat1_demux(m); + bind_manchester_sync(m); bind_matrix_deinterleaver_soft(m); bind_nrzi_decode(m); bind_nrzi_encode(m); diff --git a/python/components/demodulators/bpsk_demodulator.py b/python/components/demodulators/bpsk_demodulator.py index fdaf7c22..2df72061 100644 --- a/python/components/demodulators/bpsk_demodulator.py +++ b/python/components/demodulators/bpsk_demodulator.py @@ -15,7 +15,7 @@ from gnuradio import gr, analog, blocks, digital, filter from gnuradio.filter import firdes -from ... import manchester_sync +from ... import manchester_sync_cc from ...hier.rms_agc import rms_agc from ...utils.options_block import options_block @@ -155,7 +155,8 @@ def __init__(self, baudrate, samp_rate, iq, f_offset=None, self.complex_to_real = blocks.complex_to_real(1) if manchester: - self.manchester = manchester_sync(self.options.manchester_history) + self.manchester = manchester_sync_cc( + self.options.manchester_block_size) self.connect(self.clock_recovery, self.manchester) else: self.manchester = self.clock_recovery @@ -200,7 +201,7 @@ def __init__(self, baudrate, samp_rate, iq, f_offset=None, _default_clk_rel_bw = 0.06 _default_clk_limit = 0.004 _default_costas_bw = 50 - _default_manchester_history = 32 + _default_manchester_block_size = 32 @classmethod def add_options(cls, parser): @@ -228,6 +229,7 @@ def add_options(cls, parser): '--costas_bw', type=float, default=cls._default_costas_bw, help='Costas loop bandwidth (Hz) [default=%(default)r]') parser.add_argument( - '--manchester_history', type=int, - default=cls._default_manchester_history, - help='Manchester recovery history (symbols) [default=%(default)r]') + '--manchester_block_size', type=int, + default=cls._default_manchester_block_size, + help=('Manchester recovery block size (symbols) ' + '[default=%(default)r]')) diff --git a/python/manchester_sync.py b/python/manchester_sync.py deleted file mode 100644 index 58e69c0a..00000000 --- a/python/manchester_sync.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -# Copyright 2019 Daniel Estevez -# -# This file is part of gr-satellites -# -# SPDX-License-Identifier: GPL-3.0-or-later -# - -import collections - -import numpy as np -from gnuradio import gr - - -def branch_weight(x): - return -np.sum(np.real(x[::2] * np.conjugate(x[1::2]))) - - -class manchester_sync(gr.decim_block): - """ - Synchronize to a Manchester-coded signal and output symbols - - The input to this block is a BPSK Manchester-coded signal at - 2 samples per symbol. The block finds the Manchester clock phase, - wipes the Manchester clock and outputs the symbols at 1 samples per - symbol. - - Args: - history: The number of bits to look back to find the clock phase - """ - def __init__(self, history): - gr.decim_block.__init__( - self, - name='manchester_sync', - in_sig=[np.complex64], - out_sig=[np.complex64], - decim=2) - size = 2 * (history + 1) - self.samples = collections.deque(np.zeros(size, dtype=np.complex64), - maxlen=size) - - def work(self, input_items, output_items): - inp = input_items[0] - out = output_items[0] - - for i, x in enumerate(zip(inp[::2], inp[1::2])): - self.samples.extend(x) - z = np.array(self.samples, dtype=np.complex64) - b0 = branch_weight(z[2:]) - b1 = branch_weight(z[1:-1]) - if b0 >= b1: - output = 0.5*(z[-2] - z[-1]) - else: - output = 0.5*(z[-3] - z[-2]) - out[i] = output - - return len(out) diff --git a/python/qa_manchester_sync.py b/python/qa_manchester_sync.py new file mode 100755 index 00000000..19e7ad4c --- /dev/null +++ b/python/qa_manchester_sync.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Copyright 2023 Daniel Estevez +# +# This file is part of gr-satellites +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +from gnuradio import gr, blocks, gr_unittest +import numpy as np + +# bootstrap satellites module, even from build dir +try: + import python as satellites +except ImportError: + pass +else: + import sys + sys.modules['satellites'] = satellites + +from satellites import manchester_sync_cc, manchester_sync_ff + + +class qa_manchester_sync(gr_unittest.TestCase): + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def test_manchester_sync_cc_no_offset(self): + self.manchester_sync_test('c', False) + + def test_manchester_sync_cc_offset(self): + self.manchester_sync_test('c', True) + + def test_manchester_sync_ff_no_offset(self): + self.manchester_sync_test('f', False) + + def test_manchester_sync_ff_offset(self): + self.manchester_sync_test('f', True) + + def manchester_sync_test(self, type_, offset): + bits = 2 * np.random.randint(2, size=4096) - 1 + manchester_bits = np.repeat(bits, 2) * np.tile([1, -1], bits.size) + if offset: + manchester_bits[:-1] = manchester_bits[1:] + + source_block = {'f': blocks.vector_source_f, + 'c': blocks.vector_source_c}[type_] + self.source = source_block(manchester_bits) + sink_block = {'f': blocks.vector_sink_f, + 'c': blocks.vector_sink_c}[type_] + self.sink = sink_block(1, bits.size) + block_size = 32 + manchester_sync_block = {'f': manchester_sync_ff, + 'c': manchester_sync_cc}[type_] + self.manchester_sync = manchester_sync_block(block_size) + + self.tb.connect(self.source, self.manchester_sync, self.sink) + self.tb.run() + + sink_data = self.sink.data() + print(type_, offset) + if offset: + np.testing.assert_equal(sink_data[1:], bits[1:]) + else: + np.testing.assert_equal(sink_data, bits) + + +if __name__ == '__main__': + gr_unittest.run(qa_manchester_sync) From 984db499cd587696dd343ee3efb07823e781b989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Thu, 7 Sep 2023 08:31:16 +0200 Subject: [PATCH 05/29] Fix BME Telemetry Submitter (WebSocket) GRC The wrong block was instantiated by the GRC file. --- grc/satellites_bme_ws_submitter.block.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grc/satellites_bme_ws_submitter.block.yml b/grc/satellites_bme_ws_submitter.block.yml index b360ede3..e23534cd 100644 --- a/grc/satellites_bme_ws_submitter.block.yml +++ b/grc/satellites_bme_ws_submitter.block.yml @@ -8,6 +8,6 @@ inputs: templates: imports: import satellites - make: satellites.bme_submitter() + make: satellites.bme_ws_submitter() file_format: 1 From e0657f2c5aae850f9c86a0f1fb52c3c74b1440ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Wed, 13 Sep 2023 12:11:08 +0200 Subject: [PATCH 06/29] CPU usage improvement for Selector block The Selector block has a tricky implementation of the forecast() function, because we want to be able to dump any items present on inactive inputs, but forecast() does not have enough information for us to decide if this is the case or to communication this need to the runtime. This limitation causes that under some conditions the block is called repeated with no items on any input, consuming high CPU. This adds handling for two special cases to limit the CPU usage. If there is only one input, then we do not need to lie to the runtime, because we do not need to dump items on other inputs. If there are exactly two inputs and we are requested to produce one sample, then we lie and say that we need exactly one sample in the inactive input. This is not exactly right, because there might be samples in the active input which we might process normally, however, the hope is that if so, then there are multiple samples, in which case a previous forecast() call requesting more output samples would have "succeeded". --- lib/selector_impl.cc | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/selector_impl.cc b/lib/selector_impl.cc index 94e46047..6b4c53e7 100644 --- a/lib/selector_impl.cc +++ b/lib/selector_impl.cc @@ -143,15 +143,22 @@ void selector_impl::forecast(int noutput_items, gr_vector_int& ninput_items_requ for (unsigned i = 0; i < ninputs; i++) { ninput_items_required[i] = 0; } - // When we are requested to produce only one item, we lie and say that we - // can produce it for free. This hack is required in order to ensure that - // general_work() gets called when there is only input in some of the - // inactive inputs. This achieves having general_work() consume that input, - // which may unblock the production of items for the active input elsewhere - // in the flowgraph. - if (noutput_items > 1) { + // When we have multiple inputs and are requested to produce only one item, + // we lie and say that we can produce it for free. This hack is required in + // order to ensure that general_work() gets called when there is only input + // in some of the inactive inputs. This achieves having general_work() + // consume that input, which may unblock the production of items for the + // active input elsewhere in the flowgraph. + if ((ninputs == 1) || (noutput_items > 1)) { gr::thread::scoped_lock l(d_mutex); ninput_items_required[d_input_index] = noutput_items; + } else if (ninputs == 2) { + // If we lie and have exactly 2 inputs, tell the runtime that we need to + // have some items (to dump them) on the inactive input. If we have more + // inputs we cannot do this trick because we don't know in which + // inactive input the items might be present. + gr::thread::scoped_lock l(d_mutex); + ninput_items_required[1 - d_input_index] = 1; } } From 7de329b253bbdcd76f0af2d3ec608f964d6e42b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Mon, 9 Oct 2023 17:58:03 +0200 Subject: [PATCH 07/29] Fix SNET Deframer GRC block typo This fixes a typo in the SNET Deframer GRC block. The fix is extracted from #508. --- grc/satellites_snet_deframer.block.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grc/satellites_snet_deframer.block.yml b/grc/satellites_snet_deframer.block.yml index 7808bdf2..5e106c62 100644 --- a/grc/satellites_snet_deframer.block.yml +++ b/grc/satellites_snet_deframer.block.yml @@ -9,7 +9,7 @@ parameters: options: ['True', 'False'] option_labels: ['Yes', 'No'] - id: buggy_crc - label: Verbose + label: Buggy CRC dtype: bool default: True From aa89f6b39d1b0649d81f8502e72a40667cbbceab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Mon, 9 Oct 2023 18:05:15 +0200 Subject: [PATCH 08/29] Add support for GALASSIA-2 --- python/satyaml/GALASSIA-2.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 python/satyaml/GALASSIA-2.yml diff --git a/python/satyaml/GALASSIA-2.yml b/python/satyaml/GALASSIA-2.yml new file mode 100644 index 00000000..6c4aa00d --- /dev/null +++ b/python/satyaml/GALASSIA-2.yml @@ -0,0 +1,13 @@ +name: GALASSIA-2 +norad: 57486 +data: + &tlm Telemetry: + unknown +transmitters: + 1k2 FSK downlink: + frequency: 436.400e+6 + modulation: FSK + baudrate: 1200 + framing: AX100 ASM+Golay + data: + - *tlm From 76183b779cc4b0e7008ce859db27844ba4a25fac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Mon, 9 Oct 2023 18:07:38 +0200 Subject: [PATCH 09/29] Add support for VELOX-AM --- python/satyaml/VELOX-AM.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 python/satyaml/VELOX-AM.yml diff --git a/python/satyaml/VELOX-AM.yml b/python/satyaml/VELOX-AM.yml new file mode 100644 index 00000000..e44058e0 --- /dev/null +++ b/python/satyaml/VELOX-AM.yml @@ -0,0 +1,13 @@ +name: VELOX-AM +norad: 57482 +data: + &tlm Telemetry: + unknown +transmitters: + 4k8 FSK downlink: + frequency: 437.125e+6 + modulation: FSK + baudrate: 4800 + framing: AX100 ASM+Golay + data: + - *tlm From 2b15b3ee46e6d649a5de2b939b909072b4787958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Mon, 9 Oct 2023 18:10:25 +0200 Subject: [PATCH 10/29] Add support for SCOOB-II --- python/satyaml/SCOOB-II.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 python/satyaml/SCOOB-II.yml diff --git a/python/satyaml/SCOOB-II.yml b/python/satyaml/SCOOB-II.yml new file mode 100644 index 00000000..b585ceb7 --- /dev/null +++ b/python/satyaml/SCOOB-II.yml @@ -0,0 +1,13 @@ +name: SCOOB-II +norad: 99057 +data: + &tlm Telemetry: + telemetry: ax25 +transmitters: + 9k6 FSK downlink: + frequency: 437.500e+6 + modulation: FSK + baudrate: 9600 + framing: AX.25 G3RUH + data: + - *tlm From 0160e77d0e474db3acd913660682b9064cb6e875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Thu, 26 Oct 2023 18:19:29 +0200 Subject: [PATCH 11/29] Fix AF deviation in SALSAT yml file See https://github.com/daniestevez/gr-satellites/issues/463#issuecomment-1781439116 After measurement, it turns out that the AFSK tone deviation used by SALSAT is 300 Hz instead of 600 Hz. In other words, the AF signal is 1k2 MSK with a carrier of 1500 Hz. It is clear that the lower tone is at 1200 Hz, since the packets start with a long lower tone. The value of 600 Hz seems inherited from the SNET yml files, since these satellites are similar to SALSAT. Maybe the value is also wrong for them. --- python/satyaml/SALSAT.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/satyaml/SALSAT.yml b/python/satyaml/SALSAT.yml index 2b275d09..032ed029 100644 --- a/python/satyaml/SALSAT.yml +++ b/python/satyaml/SALSAT.yml @@ -9,7 +9,7 @@ transmitters: modulation: AFSK baudrate: 1200 af_carrier: 1500 - deviation: -600 + deviation: -300 framing: SALSAT data: - *tlm From 0d024f3545d2eda4ed6c73dd745e8b168486d29c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Thu, 30 Nov 2023 19:34:52 +0100 Subject: [PATCH 12/29] Add support for VERONIKA Tested with https://network.satnogs.org/observations/8624777/ --- python/satyaml/VERONIKA.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 python/satyaml/VERONIKA.yml diff --git a/python/satyaml/VERONIKA.yml b/python/satyaml/VERONIKA.yml new file mode 100644 index 00000000..19808519 --- /dev/null +++ b/python/satyaml/VERONIKA.yml @@ -0,0 +1,13 @@ +name: VERONIKA +norad: 58261 +data: + &tlm Telemetry: + telemetry: ax25 +transmitters: + 9k6 FSK downlink: + frequency: 436.680e+6 + modulation: FSK + baudrate: 9600 + framing: AX.25 G3RUH + data: + - *tlm From b814e810f96221c73f5d419d2c58b469f23c8691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Thu, 30 Nov 2023 19:46:08 +0100 Subject: [PATCH 13/29] Add support for KAFASAT Tested with https://network.satnogs.org/observations/8616810/ and the options --disable_dc_block --clk_bw 0.03 --- python/satyaml/KAFASAT.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 python/satyaml/KAFASAT.yml diff --git a/python/satyaml/KAFASAT.yml b/python/satyaml/KAFASAT.yml new file mode 100644 index 00000000..f8a9e565 --- /dev/null +++ b/python/satyaml/KAFASAT.yml @@ -0,0 +1,13 @@ +name: KAFASAT +norad: 58317 +data: + &tlm Telemetry: + telemetry: ax25 +transmitters: + 1k2 FSK downlink: + frequency: 435.835e+6 + modulation: FSK + baudrate: 1200 + framing: AX.25 G3RUH + data: + - *tlm From 8ef766676431ab55d7da1265c267833e602f5ce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Thu, 30 Nov 2023 19:50:31 +0100 Subject: [PATCH 14/29] Add support for PEARL-1C and PEARL-1H Tested with https://network.satnogs.org/observations/8618982/ --- python/satyaml/PEARL-1C.yml | 13 +++++++++++++ python/satyaml/PEARL-1H.yml | 13 +++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 python/satyaml/PEARL-1C.yml create mode 100644 python/satyaml/PEARL-1H.yml diff --git a/python/satyaml/PEARL-1C.yml b/python/satyaml/PEARL-1C.yml new file mode 100644 index 00000000..30322240 --- /dev/null +++ b/python/satyaml/PEARL-1C.yml @@ -0,0 +1,13 @@ +name: PEARL-1C +norad: 58342 +data: + &tlm Telemetry: + telemetry: ax25 +transmitters: + 9k6 FSK downlink: + frequency: 435.310e+6 + modulation: FSK + baudrate: 9600 + framing: AX.25 G3RUH + data: + - *tlm diff --git a/python/satyaml/PEARL-1H.yml b/python/satyaml/PEARL-1H.yml new file mode 100644 index 00000000..f5ee4710 --- /dev/null +++ b/python/satyaml/PEARL-1H.yml @@ -0,0 +1,13 @@ +name: PEARL-1H +norad: 58265 +data: + &tlm Telemetry: + telemetry: ax25 +transmitters: + 9k6 FSK downlink: + frequency: 435.390e+6 + modulation: FSK + baudrate: 9600 + framing: AX.25 G3RUH + data: + - *tlm From 60d503da23d5d316f25b057b5922b2bc82002ba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Sun, 3 Dec 2023 14:56:08 +0100 Subject: [PATCH 15/29] Add support for ENSO This SatYAML file is untested, since all the SatNOGS OGG recordings I have tested are badly clipped. --- python/satyaml/ENSO.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 python/satyaml/ENSO.yml diff --git a/python/satyaml/ENSO.yml b/python/satyaml/ENSO.yml new file mode 100644 index 00000000..00b15cb3 --- /dev/null +++ b/python/satyaml/ENSO.yml @@ -0,0 +1,15 @@ +name: ENSO +alternative_names: + - ROBUSTA 1E +norad: 58470 +data: + &tlm Telemetry: + telemetry: ax25 +transmitters: + 2k4 FSK downlink: + frequency: 436.500e+6 + modulation: FSK + baudrate: 2400 + framing: AX.25 G3RUH + data: + - *tlm From 32073af696622f38ab38c87b54576fe19757c268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Sun, 3 Dec 2023 15:11:36 +0100 Subject: [PATCH 16/29] Add support for Hayasat Decoder tested with https://network.satnogs.org/observations/8632379/ --- python/satyaml/Hayasat.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 python/satyaml/Hayasat.yml diff --git a/python/satyaml/Hayasat.yml b/python/satyaml/Hayasat.yml new file mode 100644 index 00000000..21db9713 --- /dev/null +++ b/python/satyaml/Hayasat.yml @@ -0,0 +1,14 @@ +name: Hayasat +alternative_names: +norad: 99032 +data: + &tlm Telemetry: + telemetry: ax25 +transmitters: + 9k6 FSK downlink: + frequency: 437.020e+6 + modulation: FSK + baudrate: 9600 + framing: AX.25 G3RUH + data: + - *tlm From b49887eb76ecd0be3eaf4f1d082497fe6b760578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Sun, 3 Dec 2023 15:16:03 +0100 Subject: [PATCH 17/29] Fix ENSO SatYAML ENSO does not use G3RUH scrambling. See https://community.libre.space/t/spacex-f9-425-project-eo-ir-vsfb-slc-4e-2023-12-01-18-19-utc/10997/35 Decoder now tested successfully with https://network.satnogs.org/observations/8633379/ --- python/satyaml/ENSO.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/satyaml/ENSO.yml b/python/satyaml/ENSO.yml index 00b15cb3..bbbe1373 100644 --- a/python/satyaml/ENSO.yml +++ b/python/satyaml/ENSO.yml @@ -10,6 +10,6 @@ transmitters: frequency: 436.500e+6 modulation: FSK baudrate: 2400 - framing: AX.25 G3RUH + framing: AX.25 data: - *tlm From 2fb45c2219627517ea046756b5a35fe9860ce554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Sun, 3 Dec 2023 15:54:00 +0100 Subject: [PATCH 18/29] Add --fm-deviation option to AFSK demodulator This adds an --fm-deviation command line option to the AFSK demodulator. It can be used in cases when the default of 3kHz is very different from the true deviation (the deviation is only used to compute the bandwidth for a lowpass filter using Carson's rule). An optional field fm_deviation has been added to SatYAML. This field can be used to force the FM deviation in case it is known. If the SatYAML file specifies fm_deviation, then the --fm-deviation command line option has no effect. --- docs/source/satyaml.rst | 4 +++- .../demodulators/afsk_demodulator.py | 22 +++++++++++++------ python/core/gr_satellites_flowgraph.py | 4 ++-- python/satyaml/SALSAT.yml | 1 + python/satyaml/satyaml.py | 7 +++++- 5 files changed, 27 insertions(+), 11 deletions(-) diff --git a/docs/source/satyaml.rst b/docs/source/satyaml.rst index 042d2e08..b0c12251 100644 --- a/docs/source/satyaml.rst +++ b/docs/source/satyaml.rst @@ -109,7 +109,9 @@ The modulations allowed in the ``modulation`` field are the following: ``True`` is used to perform non-coherent demodulation The ``AFSK`` modulation also needs the ``deviation`` and ``af_carrier`` fields -that indicate the AFSK tone frequencies in Hz, as in the AFSK demodulator. +that indicate the AFSK tone frequencies in Hz, as in the AFSK demodulator. Optionally, +it is possible to indicate the deviation of the FM modulation using the ``fm_deviation`` +field. By default, an FM deviation of 3 kHz is assumed. The framings allowed in the ``framing`` field are the following: diff --git a/python/components/demodulators/afsk_demodulator.py b/python/components/demodulators/afsk_demodulator.py index dcc41630..7207a3f2 100644 --- a/python/components/demodulators/afsk_demodulator.py +++ b/python/components/demodulators/afsk_demodulator.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -# Copyright 2019 Daniel Estevez +# Copyright 2019-2023 Daniel Estevez # # This file is part of gr-satellites # @@ -34,7 +34,8 @@ class afsk_demodulator(gr.hier_block2, options_block): options: Options from argparse """ def __init__(self, baudrate, samp_rate, iq, af_carrier, - deviation, dump_path=None, options=None): + deviation, dump_path=None, options=None, + fm_deviation=None): gr.hier_block2.__init__( self, 'afsk_demodulator', @@ -43,11 +44,12 @@ def __init__(self, baudrate, samp_rate, iq, af_carrier, gr.io_signature(1, 1, gr.sizeof_float)) options_block.__init__(self, options) + # The FM deviation is used to apply Carson's rule in a low-pass filter + # in the IQ case. In the FM demodulated case it is ignored. + if fm_deviation is None: + fm_deviation = self.options.fm_deviation + if iq: - # Cut to Carson's bandwidth rule before quadrature demod. - # Assume that the FM modulator used a deviation of 3 kHz, - # which is typical. - fm_deviation = 3000 # Note that deviation can be negative to encode that the # low tone corresponds to the symbol 1 and the high tone # corresponds to the symbol 0. @@ -79,7 +81,13 @@ def __init__(self, baudrate, samp_rate, iq, af_carrier, self.connect(self.demod, self.xlating, self.fsk, self) + _default_fm_deviation_hz = 3000 + @classmethod def add_options(cls, parser): - """Adds CCSDS concatenated deframer options to the argparse parser""" + """Adds AFSK demodulator options to the argparse parser""" fsk_demodulator.add_options(parser) + parser.add_argument( + '--fm-deviation', type=float, + default=cls._default_fm_deviation_hz, + help='FM deviation (Hz) [default=%(default)r]') diff --git a/python/core/gr_satellites_flowgraph.py b/python/core/gr_satellites_flowgraph.py index b056bcec..ac301b48 100644 --- a/python/core/gr_satellites_flowgraph.py +++ b/python/core/gr_satellites_flowgraph.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -# Copyright 2019 Daniel Estevez +# Copyright 2019-2023 Daniel Estevez # # This file is part of gr-satellites # @@ -253,7 +253,7 @@ def _init_demodulator_deframer(self, key, transmitter): transmitter: transmitter entry in the SatYAML """ baudrate = transmitter['baudrate'] - demod_options = ['deviation', 'af_carrier'] + demod_options = ['deviation', 'fm_deviation', 'af_carrier'] demod_options = {k: k for k in demod_options} demodulator_additional_options = filter_translate_dict(transmitter, demod_options) diff --git a/python/satyaml/SALSAT.yml b/python/satyaml/SALSAT.yml index 032ed029..210c7f44 100644 --- a/python/satyaml/SALSAT.yml +++ b/python/satyaml/SALSAT.yml @@ -10,6 +10,7 @@ transmitters: baudrate: 1200 af_carrier: 1500 deviation: -300 + fm_deviation: 10000 framing: SALSAT data: - *tlm diff --git a/python/satyaml/satyaml.py b/python/satyaml/satyaml.py index 549c64b6..5bb1be61 100644 --- a/python/satyaml/satyaml.py +++ b/python/satyaml/satyaml.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -# Copyright 2019 Daniel Estevez +# Copyright 2019-2023 Daniel Estevez # # This file is part of gr-satellites # @@ -116,6 +116,11 @@ def check_yaml(self, yml): raise YAMLError( 'Deviation field does not contain a float ' f'in {key} in {yml}') + if ('fm_deviation' in transmitter + and type(transmitter['fm_deviation']) not in [float, int]): + raise YAMLError( + 'FM deviation field does not contain a float ' + f'in {key} in {yml}') if 'framing' not in transmitter: raise YAMLError(f'Missing framing field in {key} in {yml}') if transmitter['framing'] not in self.framings: From a69004305e89c9da7f3f7af410844a07e4091852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Sun, 3 Dec 2023 16:24:32 +0100 Subject: [PATCH 19/29] Add Auto-Polarization block This adds the Auto-Polarization Python block that formerly was being copied over as an Embedded Python Block in several of my deep space flowgraphs. The code has been taken from https://github.com/daniestevez/jupyter_notebooks/blob/master/JWST/jwst_40kbps.grc This closes #444 --- grc/CMakeLists.txt | 1 + grc/satellites_autopolarization.block.yml | 37 ++++++++++ python/CMakeLists.txt | 1 + python/__init__.py | 1 + python/autopolarization.py | 85 +++++++++++++++++++++++ 5 files changed, 125 insertions(+) create mode 100644 grc/satellites_autopolarization.block.yml create mode 100644 python/autopolarization.py diff --git a/grc/CMakeLists.txt b/grc/CMakeLists.txt index daf1625d..f5d3b711 100644 --- a/grc/CMakeLists.txt +++ b/grc/CMakeLists.txt @@ -22,6 +22,7 @@ install(FILES satellites_aausat4_remove_fsm.block.yml satellites_adsb_kml.block.yml satellites_append_crc32c.block.yml + satellites_autopolarization.block.yml satellites_ax100_decode.block.yml satellites_beesat_classifier.block.yml satellites_bme_submitter.block.yml diff --git a/grc/satellites_autopolarization.block.yml b/grc/satellites_autopolarization.block.yml new file mode 100644 index 00000000..f355e064 --- /dev/null +++ b/grc/satellites_autopolarization.block.yml @@ -0,0 +1,37 @@ +id: satellites_autopolarization +label: Auto-Polarization +category: '[Satellites]/Polarization' + +parameters: +- id: fft_size + label: FFT size + dtype: int + default: 2048 +- id: fft_avg + label: FFT average + dtype: int + default: round(0.1 * samp_rate / 2048) +- id: iir_weight + label: IIR weight + dtype: float + default: 0.1 + +inputs: +- domain: stream + dtype: complex +- domain: stream + dtype: complex + +outputs: +- label: signal + domain: stream + dtype: complex +- label: orthogonal + domain: stream + dtype: complex + +templates: + imports: import satellites + make: satellites.autopolarization(fft_size=${fft_size}, fft_avg=${fft_avg}, iir_weight=${iir_weight}) + +file_format: 1 diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index ef0aede3..92fd8657 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -39,6 +39,7 @@ GR_PYTHON_INSTALL( adsb_kml.py ao40_uncoded_crc.py append_crc32c.py + autopolarization.py bch15.py beesat_classifier.py bme_submitter.py diff --git a/python/__init__.py b/python/__init__.py index bcbe0332..b09f4df9 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -53,6 +53,7 @@ from .aausat4_remove_fsm import aausat4_remove_fsm from .adsb_kml import adsb_kml from .append_crc32c import append_crc32c +from .autopolarization import autopolarization from .beesat_classifier import beesat_classifier from .bme_submitter import bme_submitter from .bme_ws_submitter import bme_ws_submitter diff --git a/python/autopolarization.py b/python/autopolarization.py new file mode 100644 index 00000000..772ec114 --- /dev/null +++ b/python/autopolarization.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Copyright 2020-2023 Daniel Estevez +# +# This file is part of gr-satellites +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +import numpy as np +from gnuradio import gr + + +class autopolarization(gr.sync_block): + def __init__(self, fft_size=2048, fft_avg=1, iir_weight=0.1): + gr.sync_block.__init__( + self, + name='autopolarization', + in_sig=[np.complex64]*2, + out_sig=[np.complex64]*2 + ) + self.fft_size = fft_size + self.fft_avg = fft_avg + self.set_output_multiple(fft_size * fft_avg) + self.iir_weight = iir_weight + self.noise_ampl = np.ones(2) + self.sig_ampl = np.ones(2) + self.phase = 0 + + def work(self, input_items, output_items): + x = np.array([inp.reshape((-1, self.fft_avg, self.fft_size)) + for inp in input_items]) + # axes for x are: + # (channel: 2, block, fft_num: fft_avg, fft_bin: fft_size) + + fx = np.fft.fft(x) + fx_sq_avg = np.average(np.abs(fx)**2, axis=2) + noise_ampl = np.sqrt(np.median(fx_sq_avg, axis=2)) + + fx_sq_avg_ch_max = np.max(fx_sq_avg/noise_ampl[..., np.newaxis]**2, + axis=0) + peak_idx = np.argmax(fx_sq_avg_ch_max, axis=1) + + f_cross = np.average(fx[0] * np.conjugate(fx[1]), axis=1) + phase = np.angle(f_cross[np.arange(f_cross.shape[0]), + peak_idx]) + # technically this should have - noise_ampl + sig_ampl = fx_sq_avg[:, np.arange(fx_sq_avg.shape[1]), peak_idx] + sig_ampl = np.sqrt(np.clip(sig_ampl, 0, np.inf)) + + # update IIR filters + for j in range(x.shape[1]): + noise_ampl[:, j] = (1-self.iir_weight)*self.noise_ampl \ + + self.iir_weight*noise_ampl[:, j] + self.noise_ampl = noise_ampl[:, j] + sig_ampl[:, j] = (1-self.iir_weight)*self.sig_ampl \ + + self.iir_weight*sig_ampl[:, j] + self.sig_ampl = sig_ampl[:, j] + phase_diff = phase[j] - self.phase + phase_diff = (phase_diff + np.pi) % (2*np.pi) - np.pi + phase[j] = self.phase + self.iir_weight*phase_diff + self.phase = (phase[j] + np.pi) % (2*np.pi) - np.pi + + tau = np.log10(sig_ampl[0]/noise_ampl[0]+1e-6) \ + - np.log10(sig_ampl[1]/noise_ampl[1]+1e-6) + tau = 20*tau/6*0.5 + 0.5 + tau = np.clip(tau, 0, 1) + # note tau = 1 if sig[0] is 6dB stronger than sig[1] and + # tau = 0 if sig[0] is 6dB weaker than sig[1] + a_phase = (tau - 1) * phase + b_phase = tau * phase + # note -a_phase + b_phase = phase + + alpha = (np.exp(1j*a_phase) + * sig_ampl[0] / noise_ampl[0])[:, np.newaxis] + beta = (np.exp(1j*b_phase) + * sig_ampl[1] / noise_ampl[1])[:, np.newaxis] + + y = x.reshape((x.shape[0], x.shape[1], self.fft_avg * self.fft_size)) + y = y / noise_ampl[..., np.newaxis] * np.sqrt(self.fft_size) + output_items[0][:] = (alpha * y[0] + beta * y[1]).ravel() + output_items[1][:] = ( + np.conjugate(beta) * y[0] - np.conjugate(alpha) * y[1]).ravel() + return len(output_items[0]) From cda7bbb783aeb5dd639eff758e7ba8c46ba9aeb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Mon, 4 Dec 2023 08:55:28 +0100 Subject: [PATCH 20/29] Add SanoSat-1 Deframer GRC file I forgot to create a GRC file when I added the SanoSat deframer in 6ab82fa2474f74724f449724e5369d8fee9ac8e1 --- grc/components/deframers/CMakeLists.txt | 1 + .../satellites_sanosat_deframer.block.yml | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 grc/components/deframers/satellites_sanosat_deframer.block.yml diff --git a/grc/components/deframers/CMakeLists.txt b/grc/components/deframers/CMakeLists.txt index 8a3bbe5a..28aa861f 100644 --- a/grc/components/deframers/CMakeLists.txt +++ b/grc/components/deframers/CMakeLists.txt @@ -49,6 +49,7 @@ install(FILES satellites_nusat_deframer.block.yml satellites_ops_sat_deframer.block.yml satellites_reaktor_hello_world_deframer.block.yml + satellites_sanosat_deframer.block.yml satellites_sat_3cat_1_deframer.block.yml satellites_smogp_ra_deframer.block.yml satellites_smogp_signalling_deframer.block.yml diff --git a/grc/components/deframers/satellites_sanosat_deframer.block.yml b/grc/components/deframers/satellites_sanosat_deframer.block.yml new file mode 100644 index 00000000..04fe9755 --- /dev/null +++ b/grc/components/deframers/satellites_sanosat_deframer.block.yml @@ -0,0 +1,43 @@ +id: satellites_sanosat_deframer +label: SanoSat-1 Deframer +category: '[Satellites]/Deframers' + +parameters: +- id: threshold + label: Syncword threshold + dtype: int + default: 0 +- id: options + label: Command line options + dtype: string + default: '""' + hide: part + +inputs: +- domain: stream + dtype: float + +outputs: +- domain: message + id: out + +templates: + imports: import satellites.components.deframers + make: satellites.components.deframers.sanosat_deframer(syncword_threshold=${threshold}, options=${options}) + +documentation: |- + Deframes SanoSat-1 packets + + See https://amsat-np.org/transmission-protocol-for-sanosat-1/ for the specifications + + Input: + A stream of soft symbols containing SanoSat-1 packets + + Output: + PDUs with the deframed SanoSat-1 packets + + Parameters: + Syncword threshold: number of bit errors to allow in syncword detection + Command line options: options to pass down to the block, following the syntax of the gr_satellites command line tool + +file_format: 1 From 4eb94e87f8c673499d825147096e3f8eb30924b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Sat, 9 Dec 2023 14:59:47 +0100 Subject: [PATCH 21/29] Add tle_to_doppler_file.py This adds an example script that uses Skyfield to generate a Doppler correction file from a TLE. --- .../doppler_correction/tle_to_doppler_file.py | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100755 examples/doppler_correction/tle_to_doppler_file.py diff --git a/examples/doppler_correction/tle_to_doppler_file.py b/examples/doppler_correction/tle_to_doppler_file.py new file mode 100755 index 00000000..89ce17a9 --- /dev/null +++ b/examples/doppler_correction/tle_to_doppler_file.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 + +# Copyright 2023 Daniel Estevez +# +# This file is part of gr-satellites +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +import argparse +import datetime + +import numpy as np +import scipy.constants +import skyfield.api +from skyfield.api import wgs84, EarthSatellite + + +DAY_S = 24 * 3600 + + +ts = skyfield.api.load.timescale() + + +def parse_args(): + parser = argparse.ArgumentParser( + description='Generates a Doppler file from a TLE') + parser.add_argument( + '--tle-file', required=True, + help='Input TLE file') + parser.add_argument( + '--output-file', required=True, + help='Ouptut text file') + parser.add_argument( + '--unix-timestamp', required=True, type=float, + help='Initial UNIX timestamp') + parser.add_argument( + '--time-step', default=0.1, type=float, + help='Time step (seconds) [default=%(default)r]') + parser.add_argument( + '--duration', default=15*60, type=float, + help='Doppler file duration (seconds) [default=%(default)r]') + parser.add_argument( + '--f-carrier', required=True, type=float, + help='Carrier frequency (Hz)') + parser.add_argument( + '--lat', required=True, type=float, + help='Groundstation latitude (degrees)') + parser.add_argument( + '--lon', required=True, type=float, + help='Groundstation longitude (degrees)') + parser.add_argument( + '--alt', default=0.0, type=float, + help='Groundstation altitude (meters) [default=%(default)r]') + return parser.parse_args() + + +def main(): + args = parse_args() + with open(args.tle_file) as f: + lines = f.readlines() + if len(lines) == 3: + # drop first line (contains the name of the satellite) + lines = lines[1:] + if len(lines) != 2: + raise RuntimeError('TLE file must have either 2 or 3 lines') + unix_epoch = datetime.datetime(1970, 1, 1, tzinfo=skyfield.api.utc) + satellite = EarthSatellite(lines[0], lines[1], 'satellite', ts) + t0 = unix_epoch + datetime.timedelta(seconds=args.unix_timestamp) + t0 = ts.from_datetime(t0.replace(tzinfo=skyfield.api.utc)) + t = t0 + np.arange(0, (args.duration + args.time_step) / DAY_S, + args.time_step / DAY_S) + t = ts.tai_jd([s.tai for s in t]) + groundstation = wgs84.latlon( + args.lat, args.lon, args.alt) + topocentric = (satellite - groundstation).at(t) + range_rate = topocentric.frame_latlon_and_rates( + groundstation)[5].km_per_s * 1e3 + doppler = - range_rate / scipy.constants.c * args.f_carrier + with open(args.output_file, 'w') as output_file: + for s, f in zip(t, doppler): + s = (s.utc_datetime() - unix_epoch).total_seconds() + print(f'{s}\t{f}', file=output_file) + + +if __name__ == '__main__': + main() From aee5e5932f546f0bb194fc02a326a1cb5cc23d24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Sat, 9 Dec 2023 18:42:47 +0100 Subject: [PATCH 22/29] Add HADES-D deframer This adds a deframer for HADES-D. This satellite uses 50 baud FSK with a deviation of approximately 550 Hz. Since the deviation is much larger than the baudrate, the usual FSK demodulator in gr-satellites won't work well for this signal. A custom demodulator flowgraph based on the difference of the power detection of each FSK tone will be added as an example. --- grc/components/deframers/CMakeLists.txt | 1 + .../satellites_hades_deframer.block.yml | 45 +++++ python/components/deframers/CMakeLists.txt | 1 + python/components/deframers/__init__.py | 1 + python/components/deframers/hades_deframer.py | 160 ++++++++++++++++++ 5 files changed, 208 insertions(+) create mode 100644 grc/components/deframers/satellites_hades_deframer.block.yml create mode 100644 python/components/deframers/hades_deframer.py diff --git a/grc/components/deframers/CMakeLists.txt b/grc/components/deframers/CMakeLists.txt index 28aa861f..92daaa02 100644 --- a/grc/components/deframers/CMakeLists.txt +++ b/grc/components/deframers/CMakeLists.txt @@ -38,6 +38,7 @@ install(FILES satellites_fossasat_deframer.block.yml satellites_geoscan_deframer.block.yml satellites_grizu263a_deframer.block.yml + satellites_hades_deframer.block.yml satellites_hsu_sat1_deframer.block.yml satellites_ideassat_deframer.block.yml satellites_k2sat_deframer_component.block.yml diff --git a/grc/components/deframers/satellites_hades_deframer.block.yml b/grc/components/deframers/satellites_hades_deframer.block.yml new file mode 100644 index 00000000..8dda9010 --- /dev/null +++ b/grc/components/deframers/satellites_hades_deframer.block.yml @@ -0,0 +1,45 @@ +id: satellites_hades_deframer +label: HADES-D Deframer +category: '[Satellites]/Deframers' + +parameters: +- id: threshold + label: Syncword threshold + dtype: int + default: 0 +- id: options + label: Command line options + dtype: string + default: '""' + hide: part + +inputs: +- domain: stream + dtype: float + +outputs: +- domain: message + id: out + +templates: + imports: import satellites.components.deframers + make: satellites.components.deframers.hades_deframer(syncword_threshold=${threshold}, options=${options}) + +documentation: |- + Deframes HADES-D packets + + See the following documents for the specifications + https://www.amsat-ea.org/app/download/13595424/AMSAT+EA+-+Descripci%C3%B3n+de+transmisiones+de+HADES-D.pdf + https://www.amsat-ea.org/app/download/13595777/AMSAT+EA+-+HADES-D+Transmissions+description.pdf + + Input: + A stream of soft symbols containing SanoSat-1 packets + + Output: + PDUs with the deframed HADES-D packets + + Parameters: + Syncword threshold: number of bit errors to allow in syncword detection + Command line options: options to pass down to the block, following the syntax of the gr_satellites command line tool + +file_format: 1 diff --git a/python/components/deframers/CMakeLists.txt b/python/components/deframers/CMakeLists.txt index 8a8049a2..065bae7a 100644 --- a/python/components/deframers/CMakeLists.txt +++ b/python/components/deframers/CMakeLists.txt @@ -50,6 +50,7 @@ GR_PYTHON_INSTALL( fossasat_deframer.py geoscan_deframer.py grizu263a_deframer.py + hades_deframer.py hsu_sat1_deframer.py ideassat_deframer.py k2sat_deframer.py diff --git a/python/components/deframers/__init__.py b/python/components/deframers/__init__.py index 745ab0ce..6924c27c 100644 --- a/python/components/deframers/__init__.py +++ b/python/components/deframers/__init__.py @@ -36,6 +36,7 @@ from .fossasat_deframer import fossasat_deframer from .geoscan_deframer import geoscan_deframer from .grizu263a_deframer import grizu263a_deframer +from .hades_deframer import hades_deframer from .hsu_sat1_deframer import hsu_sat1_deframer from .ideassat_deframer import ideassat_deframer from .k2sat_deframer import k2sat_deframer diff --git a/python/components/deframers/hades_deframer.py b/python/components/deframers/hades_deframer.py new file mode 100644 index 00000000..9473e131 --- /dev/null +++ b/python/components/deframers/hades_deframer.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Copyright 2023 Daniel Estevez +# +# This file is part of gr-satellites +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +from gnuradio import gr, digital +import pmt + +from ... import decode_rs, pdu_head_tail +from ...crcs import crc16_ccitt_false +from .ccsds_rs_deframer import _syncword +from ...hier.sync_to_pdu_packed import sync_to_pdu_packed +from ...utils.options_block import options_block + + +# 0xBF35 in MSB first +_syncword = '1011111100110101' + + +class hades_packet_crop(gr.basic_block): + """Crop HADES-D packet""" + def __init__(self): + gr.basic_block.__init__( + self, + name='hades_packet_crop', + in_sig=[], + out_sig=[]) + self.message_port_register_in(pmt.intern('in')) + self.set_msg_handler(pmt.intern('in'), self.handle_msg) + self.message_port_register_out(pmt.intern('out')) + + def handle_msg(self, msg_pmt): + msg = pmt.cdr(msg_pmt) + if not pmt.is_u8vector(msg): + print('[ERROR] Received invalid message type. Expected u8vector') + return + packet = bytes(pmt.u8vector_elements(msg)) + packet_type = packet[0] >> 4 + lengths = {1: 26, 2: 13, 3: 26, 4: 54, 5: 33, 6: 135, 7: 67, 8: 28, + 9: 123, 12: 64} + try: + packet_length = lengths[packet_type] + except KeyError: + # invalid packet_type + return + packet = packet[:packet_length] + self.message_port_pub( + pmt.intern('out'), + pmt.cons(pmt.car(msg_pmt), + pmt.init_u8vector(len(packet), list(packet)))) + + +class hades_descrambler(gr.basic_block): + """Descramble HADES-D packet""" + def __init__(self): + gr.basic_block.__init__( + self, + name='hades_packet_crop', + in_sig=[], + out_sig=[]) + self.message_port_register_in(pmt.intern('in')) + self.set_msg_handler(pmt.intern('in'), self.handle_msg) + self.message_port_register_out(pmt.intern('out')) + + def handle_msg(self, msg_pmt): + msg = pmt.cdr(msg_pmt) + if not pmt.is_u8vector(msg): + print('[ERROR] Received invalid message type. Expected u8vector') + return + packet = bytearray(bytes(pmt.u8vector_elements(msg))) + # The algorithm deviates from a typical scrambler (and in fact the + # implementation has some bugs), so it is impossible to get it right + # just by looking at the documentation. + # + # See + # https://github.com/AMSAT-EA/URESAT-1-decoder/blob/main/bit-version/linux/genesis_scrambler.c + # for the AMSAT-EA implementation. + + # In the AMSAT-EA code the state is initialized to 0x2C350000, but + # since the shift register is supposed to have 17 bits, most of the + # non-zero MSBs do not matter. + state = 1 << 16 + # The first byte is not scrambled (the CRC is not scrambled either, but + # it has been removed at this point) + for j in range(1, len(packet)): + out = 0 + # There is a bug in how the scrambler is written. The LSB is each + # byte is not scrambler, and the scrambler state is not advanced. + for k in range(7): + b = (packet[j] >> (7 - k)) & 1 + b0 = b + b ^= ((state >> 16) ^ (state >> 11)) & 1 + out = (out << 1) | b + state = ((state << 1) | b0) & 0x1ffff + # Take LSB directly without scrambling + out = (out << 1) | (packet[j] & 1) + packet[j] = out + self.message_port_pub( + pmt.intern('out'), + pmt.cons(pmt.car(msg_pmt), + pmt.init_u8vector(len(packet), list(packet)))) + + +class hades_deframer(gr.hier_block2, options_block): + """ + Hierarchical block to deframe HADES-D custom frames + + https://www.amsat-ea.org/app/download/13595424/AMSAT+EA+-+Descripci%C3%B3n+de+transmisiones+de+HADES-D.pdf + https://www.amsat-ea.org/app/download/13595777/AMSAT+EA+-+HADES-D+Transmissions+description.pdf + + The input is a float stream of soft symbols. The output are PDUs + with frames. + + Args: + syncword_threshold: number of bit errors allowed in syncword (int) + options: Options from argparse + """ + def __init__(self, syncword_threshold=None, options=None): + gr.hier_block2.__init__( + self, + 'hades_deframer', + gr.io_signature(1, 1, gr.sizeof_float), + gr.io_signature(0, 0, 0)) + options_block.__init__(self, options) + + self.message_port_register_hier_out('out') + + if syncword_threshold is None: + syncword_threshold = self.options.syncword_threshold + + self.slicer = digital.binary_slicer_fb() + self.deframer = sync_to_pdu_packed( + packlen=135, sync=_syncword, + threshold=syncword_threshold) + self.crop = hades_packet_crop() + self.crc = crc16_ccitt_false() + self.descrambler = hades_descrambler() + + self.connect(self, self.slicer, self.deframer) + self.msg_connect((self.deframer, 'out'), (self.crop, 'in')) + self.msg_connect((self.crop, 'out'), (self.crc, 'in')) + self.msg_connect((self.crc, 'ok'), (self.descrambler, 'in')) + self.msg_connect((self.descrambler, 'out'), (self, 'out')) + + _default_sync_threshold = 0 + + @classmethod + def add_options(cls, parser): + """ + Adds HADES-D deframer specific options to the argparse parser + """ + parser.add_argument( + '--syncword_threshold', type=int, + default=cls._default_sync_threshold, + help='Syncword bit errors [default=%(default)r]') From eb77b3b0799174309a21081005c43327fae747b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Sun, 10 Dec 2023 21:52:38 +0100 Subject: [PATCH 23/29] Add example HADES-D decoder This decoder contains a custom FSK demodulator for HADES-D. --- examples/satellites/hades_d.grc | 563 ++++++++++++++++++++++++++++++++ 1 file changed, 563 insertions(+) create mode 100644 examples/satellites/hades_d.grc diff --git a/examples/satellites/hades_d.grc b/examples/satellites/hades_d.grc new file mode 100644 index 00000000..b7e6f90a --- /dev/null +++ b/examples/satellites/hades_d.grc @@ -0,0 +1,563 @@ +options: + parameters: + author: Daniel Estevez + catch_exceptions: 'True' + category: '[GRC Hier Blocks]' + cmake_opt: '' + comment: '' + copyright: '' + description: '' + gen_cmake: 'On' + gen_linking: dynamic + generate_options: qt_gui + hier_block_src_path: '.:' + id: hades_d + max_nouts: '0' + output_language: python + placement: (0,0) + qt_qss_theme: '' + realtime_scheduling: '' + run: 'True' + run_command: '{python} -u {filename}' + run_options: run + sizing_mode: fixed + thread_safe_setters: '' + title: HADES-D FSK decoder + window_size: (1000,1000) + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [8, 8] + rotation: 0 + state: enabled + +blocks: +- name: baudrate + id: variable + parameters: + comment: '' + value: '50' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [752, 12.0] + rotation: 0 + state: true +- name: dec + id: variable + parameters: + comment: '' + value: samp_rate // (baudrate * sps) + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [568, 84.0] + rotation: 0 + state: true +- name: deviation + id: variable + parameters: + comment: '' + value: '550' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [448, 12.0] + rotation: 0 + state: true +- name: freq_error + id: variable + parameters: + comment: '' + value: -1.3e3 + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [328, 12.0] + rotation: 0 + state: true +- name: samp_rate + id: variable + parameters: + comment: '' + value: '32000' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [200, 12.0] + rotation: 0 + state: enabled +- name: sideband_filter + id: variable + parameters: + comment: '' + value: firdes.low_pass(1, samp_rate, 200, 25) + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [568, 12.0] + rotation: 0 + state: true +- name: sps + id: variable + parameters: + comment: '' + value: '8' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [752, 84.0] + rotation: 0 + state: true +- name: blocks_complex_to_mag_squared_0 + id: blocks_complex_to_mag_squared + parameters: + affinity: '' + alias: '' + comment: '' + maxoutbuf: '0' + minoutbuf: '0' + vlen: '1' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [888, 224.0] + rotation: 0 + state: true +- name: blocks_complex_to_mag_squared_0_0 + id: blocks_complex_to_mag_squared + parameters: + affinity: '' + alias: '' + comment: '' + maxoutbuf: '0' + minoutbuf: '0' + vlen: '1' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [888, 312.0] + rotation: 0 + state: true +- name: blocks_complex_to_real_0 + id: blocks_complex_to_real + parameters: + affinity: '' + alias: '' + comment: '' + maxoutbuf: '0' + minoutbuf: '0' + vlen: '1' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [576, 504.0] + rotation: 0 + state: true +- name: blocks_file_source_0 + id: blocks_file_source + parameters: + affinity: '' + alias: '' + begin_tag: pmt.PMT_NIL + comment: '' + file: HADES-D_2023-12-09T11_20_03_32ksps.sigmf-data + length: '0' + maxoutbuf: '0' + minoutbuf: '0' + offset: '0' + repeat: 'False' + type: complex + vlen: '1' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [32, 172.0] + rotation: 0 + state: true +- name: blocks_float_to_complex_0 + id: blocks_float_to_complex + parameters: + affinity: '' + alias: '' + comment: '' + maxoutbuf: '0' + minoutbuf: '0' + vlen: '1' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [240, 488.0] + rotation: 0 + state: true +- name: blocks_null_source_0 + id: blocks_null_source + parameters: + affinity: '' + alias: '' + bus_structure_source: '[[0,],]' + comment: '' + maxoutbuf: '0' + minoutbuf: '0' + num_outputs: '1' + type: float + vlen: '1' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [72, 520.0] + rotation: 0 + state: true +- name: blocks_rotator_cc_0 + id: blocks_rotator_cc + parameters: + affinity: '' + alias: '' + comment: Low tone + maxoutbuf: '0' + minoutbuf: '0' + phase_inc: -2 * np.pi * (freq_error - deviation) / samp_rate + tag_inc_update: 'False' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [464, 208.0] + rotation: 0 + state: true +- name: blocks_rotator_cc_0_0 + id: blocks_rotator_cc + parameters: + affinity: '' + alias: '' + comment: High tone + maxoutbuf: '0' + minoutbuf: '0' + phase_inc: -2 * np.pi * (freq_error + deviation) / samp_rate + tag_inc_update: 'False' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [464, 296.0] + rotation: 0 + state: true +- name: blocks_sub_xx_0 + id: blocks_sub_xx + parameters: + affinity: '' + alias: '' + comment: '' + maxoutbuf: '0' + minoutbuf: '0' + num_inputs: '2' + type: float + vlen: '1' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [1088, 256.0] + rotation: 0 + state: true +- name: blocks_throttle2_0 + id: blocks_throttle2 + parameters: + affinity: '' + alias: '' + comment: '' + ignoretag: 'True' + limit: auto + maximum: '0.1' + maxoutbuf: '0' + minoutbuf: '0' + samples_per_second: samp_rate * 30 + type: complex + vlen: '1' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [280, 196.0] + rotation: 0 + state: true +- name: digital_symbol_sync_xx_0 + id: digital_symbol_sync_xx + parameters: + affinity: '' + alias: '' + comment: '' + constellation: digital.constellation_bpsk().base() + damping: '1.0' + loop_bw: '0.01' + max_dev: '0.01' + maxoutbuf: '0' + minoutbuf: '0' + nfilters: '128' + osps: '1' + pfb_mf_taps: '[]' + resamp_type: digital.IR_MMSE_8TAP + sps: sps + ted_gain: '1.0' + ted_type: digital.TED_GARDNER + type: ff + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [744, 444.0] + rotation: 0 + state: true +- name: fir_filter_xxx_0 + id: fir_filter_xxx + parameters: + affinity: '' + alias: '' + comment: '' + decim: dec + maxoutbuf: '0' + minoutbuf: '0' + samp_delay: '0' + taps: sideband_filter + type: ccc + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [680, 212.0] + rotation: 0 + state: true +- name: fir_filter_xxx_0_0 + id: fir_filter_xxx + parameters: + affinity: '' + alias: '' + comment: '' + decim: dec + maxoutbuf: '0' + minoutbuf: '0' + samp_delay: '0' + taps: sideband_filter + type: ccc + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [680, 300.0] + rotation: 0 + state: true +- name: fir_filter_xxx_1 + id: fir_filter_xxx + parameters: + affinity: '' + alias: '' + comment: '' + decim: '1' + maxoutbuf: '0' + minoutbuf: '0' + samp_delay: '0' + taps: '[1] * sps' + type: fff + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [1208, 260.0] + rotation: 0 + state: true +- name: import_0 + id: import + parameters: + alias: '' + comment: '' + imports: import numpy as np + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [208, 108.0] + rotation: 0 + state: true +- name: qtgui_time_sink_x_0 + id: qtgui_time_sink_x + parameters: + affinity: '' + alias: '' + alpha1: '1.0' + alpha10: '1.0' + alpha2: '1.0' + alpha3: '1.0' + alpha4: '1.0' + alpha5: '1.0' + alpha6: '1.0' + alpha7: '1.0' + alpha8: '1.0' + alpha9: '1.0' + autoscale: 'False' + axislabels: 'True' + color1: blue + color10: dark blue + color2: red + color3: green + color4: black + color5: cyan + color6: magenta + color7: yellow + color8: dark red + color9: dark green + comment: '' + ctrlpanel: 'False' + entags: 'True' + grid: 'False' + gui_hint: '' + label1: Signal 1 + label10: Signal 10 + label2: Signal 2 + label3: Signal 3 + label4: Signal 4 + label5: Signal 5 + label6: Signal 6 + label7: Signal 7 + label8: Signal 8 + label9: Signal 9 + legend: 'True' + marker1: '0' + marker10: '-1' + marker2: '-1' + marker3: '-1' + marker4: '-1' + marker5: '-1' + marker6: '-1' + marker7: '-1' + marker8: '-1' + marker9: '-1' + name: '""' + nconnections: '1' + size: 60 * baudrate + srate: samp_rate + stemplot: 'False' + style1: '0' + style10: '1' + style2: '1' + style3: '1' + style4: '1' + style5: '1' + style6: '1' + style7: '1' + style8: '1' + style9: '1' + tr_chan: '0' + tr_delay: '0' + tr_level: '0.0' + tr_mode: qtgui.TRIG_MODE_FREE + tr_slope: qtgui.TRIG_SLOPE_POS + tr_tag: '""' + type: float + update_time: '0.10' + width1: '1' + width10: '1' + width2: '1' + width3: '1' + width4: '1' + width5: '1' + width6: '1' + width7: '1' + width8: '1' + width9: '1' + ylabel: Amplitude + ymax: '3' + ymin: '-3' + yunit: '""' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [1064, 436.0] + rotation: 0 + state: true +- name: satellites_hades_deframer_0 + id: satellites_hades_deframer + parameters: + affinity: '' + alias: '' + comment: '' + maxoutbuf: '0' + minoutbuf: '0' + options: '""' + threshold: '0' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [256, 644.0] + rotation: 0 + state: true +- name: satellites_hexdump_sink_0 + id: satellites_hexdump_sink + parameters: + affinity: '' + alias: '' + comment: '' + options: '""' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [656, 648.0] + rotation: 0 + state: true +- name: satellites_rms_agc_0 + id: satellites_rms_agc + parameters: + affinity: '' + alias: '' + alpha: 3e-2 + comment: '' + maxoutbuf: '0' + minoutbuf: '0' + reference: '1.0' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [424, 492.0] + rotation: 0 + state: true + +connections: +- [blocks_complex_to_mag_squared_0, '0', blocks_sub_xx_0, '0'] +- [blocks_complex_to_mag_squared_0_0, '0', blocks_sub_xx_0, '1'] +- [blocks_complex_to_real_0, '0', digital_symbol_sync_xx_0, '0'] +- [blocks_file_source_0, '0', blocks_rotator_cc_0_0, '0'] +- [blocks_file_source_0, '0', blocks_throttle2_0, '0'] +- [blocks_float_to_complex_0, '0', satellites_rms_agc_0, '0'] +- [blocks_null_source_0, '0', blocks_float_to_complex_0, '1'] +- [blocks_rotator_cc_0, '0', fir_filter_xxx_0, '0'] +- [blocks_rotator_cc_0_0, '0', fir_filter_xxx_0_0, '0'] +- [blocks_sub_xx_0, '0', fir_filter_xxx_1, '0'] +- [blocks_throttle2_0, '0', blocks_rotator_cc_0, '0'] +- [digital_symbol_sync_xx_0, '0', qtgui_time_sink_x_0, '0'] +- [digital_symbol_sync_xx_0, '0', satellites_hades_deframer_0, '0'] +- [fir_filter_xxx_0, '0', blocks_complex_to_mag_squared_0, '0'] +- [fir_filter_xxx_0_0, '0', blocks_complex_to_mag_squared_0_0, '0'] +- [fir_filter_xxx_1, '0', blocks_float_to_complex_0, '0'] +- [satellites_hades_deframer_0, out, satellites_hexdump_sink_0, in] +- [satellites_rms_agc_0, '0', blocks_complex_to_real_0, '0'] + +metadata: + file_format: 1 + grc_version: 3.10.8.0 From 4a55dfccf5c085c8afe7271c61576e983b72be47 Mon Sep 17 00:00:00 2001 From: Jan - PE0SAT Date: Thu, 14 Dec 2023 17:02:46 +0100 Subject: [PATCH 24/29] Update EIRSAT-1.yml norad identification update --- python/satyaml/EIRSAT-1.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/satyaml/EIRSAT-1.yml b/python/satyaml/EIRSAT-1.yml index a632b383..49c0166b 100644 --- a/python/satyaml/EIRSAT-1.yml +++ b/python/satyaml/EIRSAT-1.yml @@ -1,5 +1,5 @@ name: EIRSAT-1 -norad: 99320 +norad: 58472 data: &tlm Telemetry: unknown @@ -26,4 +26,4 @@ transmitters: RS interleaving: 4 deviation: -2400 data: - - *tlm \ No newline at end of file + - *tlm From a440419b9a27bbb17f84f3fa64dc8a36bea57d3d Mon Sep 17 00:00:00 2001 From: Jan - PE0SAT Date: Sat, 16 Dec 2023 00:05:47 +0100 Subject: [PATCH 25/29] Create IRIS-C.yml Based on the following observation also added 1k4 and 2k4 --- python/satyaml/IRIS-C.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 python/satyaml/IRIS-C.yml diff --git a/python/satyaml/IRIS-C.yml b/python/satyaml/IRIS-C.yml new file mode 100644 index 00000000..816c3b84 --- /dev/null +++ b/python/satyaml/IRIS-C.yml @@ -0,0 +1,27 @@ +name: IRIS-C +norad: 56221 +data: + &tlm Telemetry: + telemetry: ax25 +transmitters: + 1k2 BPSK downlink: + frequency: 436.915e+6 + modulation: BPSK + baudrate: 1200 + framing: AX.25 G3RUH + data: + - *tlm + 2k4 BPSK downlink: + frequency: 436.915e+6 + modulation: BPSK + baudrate: 2400 + framing: AX.25 G3RUH + data: + - *tlm + 9k6 BPSK downlink: + frequency: 436.915e+6 + modulation: BPSK + baudrate: 9600 + framing: AX.25 G3RUH + data: + - *tlm From 552e58e62d4b25ab97c43a0e11e04df6af8a9774 Mon Sep 17 00:00:00 2001 From: Jan - PE0SAT Date: Wed, 20 Dec 2023 17:49:28 +0100 Subject: [PATCH 26/29] Create CLARKSAT-1.yml New satellite released from Kibo and identified https://twitter.com/HAMSATNL/status/1737451855645450594 --- python/satyaml/CLARKSAT-1.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 python/satyaml/CLARKSAT-1.yml diff --git a/python/satyaml/CLARKSAT-1.yml b/python/satyaml/CLARKSAT-1.yml new file mode 100644 index 00000000..a9a6ba14 --- /dev/null +++ b/python/satyaml/CLARKSAT-1.yml @@ -0,0 +1,13 @@ +name: CLARKSAT-1 +norad: 58613 +data: + &tlm unknown telemetry: + unknown +transmitters: + 4k8 G3RUH FSK downlink: + frequency: 435.130e+6 + modulation: FSK + baudrate: 4800 + framing: AX.25 G3RUH + data: + - *tlm From b924ac94a84ec596b7a7120d5b4d225dbbf0c183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Thu, 28 Dec 2023 15:52:58 +0100 Subject: [PATCH 27/29] Update supported satellites list --- docs/source/supported_satellites.rst | 83 +++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/docs/source/supported_satellites.rst b/docs/source/supported_satellites.rst index b11710cd..abd86f04 100644 --- a/docs/source/supported_satellites.rst +++ b/docs/source/supported_satellites.rst @@ -495,6 +495,13 @@ The list is auto-generated by reading the SatYAML files and using the script ``d * **9k6 FSK downlink** (437.250 MHz): FSK modulation with AX.25 G3RUH framing * **19k2 FSK downlink** (437.250 MHz): FSK modulation with AX.25 G3RUH framing +**CLARKSAT-1** + NORAD ID: 58613 + + Transmitters: + + * **4k8 G3RUH FSK downlink** (435.130 MHz): FSK modulation with AX.25 G3RUH framing + **COLUMBIA** Alternative names: US04, ON04US @@ -744,7 +751,7 @@ The list is auto-generated by reading the SatYAML files and using the script ``d * **1k2 AFSK downlink** (437.095 MHz): AFSK modulation with AX.25 framing **EIRSAT-1** - NORAD ID: 99320 + NORAD ID: 58472 Transmitters: @@ -780,6 +787,15 @@ The list is auto-generated by reading the SatYAML files and using the script ``d * **9k6 FSK downlink** (437.050 MHz): FSK modulation with AX.25 G3RUH framing +**ENSO** + Alternative names: ROBUSTA 1E + + NORAD ID: 58470 + + Transmitters: + + * **2k4 FSK downlink** (436.500 MHz): FSK modulation with AX.25 framing + **EntrySat** NORAD ID: 44429 @@ -887,6 +903,13 @@ The list is auto-generated by reading the SatYAML files and using the script ``d * **4k8 AFSK downlink** (436.400 MHz): AFSK modulation with U482C framing +**GALASSIA-2** + NORAD ID: 57486 + + Transmitters: + + * **1k2 FSK downlink** (436.400 MHz): FSK modulation with AX100 ASM+Golay framing + **GASPACS** NORAD ID: 51439 @@ -978,6 +1001,13 @@ The list is auto-generated by reading the SatYAML files and using the script ``d * **9k6 FSK downlink** (437.175 MHz): FSK modulation with AX.25 G3RUH framing +**Hayasat** + NORAD ID: 99032 + + Transmitters: + + * **9k6 FSK downlink** (437.020 MHz): FSK modulation with AX.25 G3RUH framing + **HSKSAT** NORAD ID: 55182 @@ -1101,6 +1131,15 @@ The list is auto-generated by reading the SatYAML files and using the script ``d * **9k6 FSK downlink** (436.915 MHz): FSK modulation with AX.25 G3RUH framing +**IRIS-C** + NORAD ID: 56221 + + Transmitters: + + * **1k2 BPSK downlink** (436.915 MHz): BPSK modulation with AX.25 G3RUH framing + * **2k4 BPSK downlink** (436.915 MHz): BPSK modulation with AX.25 G3RUH framing + * **9k6 BPSK downlink** (436.915 MHz): BPSK modulation with AX.25 G3RUH framing + **IRVINE-01** NORAD ID: 43693 @@ -1157,6 +1196,13 @@ The list is auto-generated by reading the SatYAML files and using the script ``d * **1k2 BPSK downlink** (145.840 MHz): DBPSK modulation with AO-40 FEC framing +**KAFASAT** + NORAD ID: 58317 + + Transmitters: + + * **1k2 FSK downlink** (435.835 MHz): FSK modulation with AX.25 G3RUH framing + **KAI-1** Alternative names: KNRTU-KAI, KNITU-KAI, RS26S @@ -1659,6 +1705,20 @@ The list is auto-generated by reading the SatYAML files and using the script ``d * **9k6 FSK downlink** (437.475 MHz): FSK modulation with AX.25 G3RUH framing +**PEARL-1C** + NORAD ID: 58342 + + Transmitters: + + * **9k6 FSK downlink** (435.310 MHz): FSK modulation with AX.25 G3RUH framing + +**PEARL-1H** + NORAD ID: 58265 + + Transmitters: + + * **9k6 FSK downlink** (435.390 MHz): FSK modulation with AX.25 G3RUH framing + **PHOENIX** Alternative names: TW01, ON01TW @@ -1852,6 +1912,13 @@ The list is auto-generated by reading the SatYAML files and using the script ``d * **500baud FSK downlink** (436.235 MHz): FSK modulation with SanoSat framing +**SCOOB-II** + NORAD ID: 99057 + + Transmitters: + + * **9k6 FSK downlink** (437.500 MHz): FSK modulation with AX.25 G3RUH framing + **SelfieSat** NORAD ID: 53951 @@ -2605,6 +2672,20 @@ The list is auto-generated by reading the SatYAML files and using the script ``d * **9k6 FSK downlink** (435.600 MHz): FSK modulation with AX.25 G3RUH framing +**VELOX-AM** + NORAD ID: 57482 + + Transmitters: + + * **4k8 FSK downlink** (437.125 MHz): FSK modulation with AX100 ASM+Golay framing + +**VERONIKA** + NORAD ID: 58261 + + Transmitters: + + * **9k6 FSK downlink** (436.680 MHz): FSK modulation with AX.25 G3RUH framing + **VIZARD** Alternative names: SXC3-215, RS33S From de10984014e5d3c0b82648062011a961306e21a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Thu, 28 Dec 2023 16:04:55 +0100 Subject: [PATCH 28/29] Update CHANGELOG for release --- CHANGELOG.md | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7442215e..1416774d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## [5.5.0], [4.12.0], [3.19.0] - 2023-12-28 + +### Added +- Auto-Polarization Python block +- --fm-deviation option to AFSK Demodulator +- TLE to Doppler file Python script +- HADES-D deframer and example decoder +- Support for CLARKSAT-1 +- Support for ENSO +- Support for GALASSIA-2 +- Support for Hayasat +- Support for IRIS-C +- Support for KAFASAT +- Support for PEARL-1C and PEARL-1H +- Support for SCOOB-II +- Support for VELOX-AM +- Support for VERONIKA + +### Fixed +- BME Telemetry Submitter GRC file +- SNET Deframer GRC file typo +- AF deviation in SALSAT SatYAML file +- Added missing SanoSat Deframer GRC file + +### Changed +- Ported Manchester Sync block to C++ +- CPU usage improvement for Selector block +- Final NORAD ID for EIRSAT-1 + ## [5.4.0], [4.11.0], [3.18.0] - 2023-08-28 ### Added @@ -627,13 +656,15 @@ Large refactor release bringing new functionality and improvements. This is an o ## [1.0.0] - 2018-08-02 First gr-satellites release using semantic versioning -[Unreleased]: https://github.com/daniestevez/gr-satellites/compare/v5.4.0...main +[Unreleased]: https://github.com/daniestevez/gr-satellites/compare/v5.5.0...main +[5.5.0]: https://github.com/daniestevez/gr-satellites/compare/v5.4.0...v5.5.0 [5.4.0]: https://github.com/daniestevez/gr-satellites/compare/v5.3.0...v5.4.0 [5.3.0]: https://github.com/daniestevez/gr-satellites/compare/v5.2.0...v5.3.0 [5.2.0]: https://github.com/daniestevez/gr-satellites/compare/v5.1.1...v5.2.0 [5.1.1]: https://github.com/daniestevez/gr-satellites/compare/v5.1.0...v5.1.1 [5.1.0]: https://github.com/daniestevez/gr-satellites/compare/v5.0.0...v5.1.0 [5.0.0]: https://github.com/daniestevez/gr-satellites/compare/v4.6.0...v5.0.0 +[4.12.0]: https://github.com/daniestevez/gr-satellites/compare/v4.11.0...v4.12.0 [4.11.0]: https://github.com/daniestevez/gr-satellites/compare/v4.10.0...v4.11.0 [4.10.0]: https://github.com/daniestevez/gr-satellites/compare/v4.9.0...v4.10.0 [4.9.0]: https://github.com/daniestevez/gr-satellites/compare/v4.8.1...v4.9.0 @@ -649,6 +680,7 @@ First gr-satellites release using semantic versioning [4.1.0]: https://github.com/daniestevez/gr-satellites/compare/v4.0.0...v4.1.0 [4.0.0]: https://github.com/daniestevez/gr-satellites/compare/v4.0.0-rc1...v4.0.0 [4.0.0-rc1]: https://github.com/daniestevez/gr-satellites/compare/v3.7.0...v4.0.0-rc1 +[3.19.0]: https://github.com/daniestevez/gr-satellites/compare/v3.18.0...v3.19.0 [3.18.0]: https://github.com/daniestevez/gr-satellites/compare/v3.17.0...v3.18.0 [3.17.0]: https://github.com/daniestevez/gr-satellites/compare/v3.16.0...v3.17.0 [3.16.0]: https://github.com/daniestevez/gr-satellites/compare/v3.15.1...v3.16.0 From 4861b803e9e92aedd5669210fc9cf0eba330cfe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Est=C3=A9vez?= Date: Thu, 28 Dec 2023 16:07:43 +0100 Subject: [PATCH 29/29] Update Debian changelog --- debian/changelog | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/debian/changelog b/debian/changelog index d3545993..5e1ed1dc 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,15 @@ +gr-satellites (5.5.0-1) mantic; urgency=medium + + * Mainstream release v5.5.0 + + -- Thu, 28 Dec 2023 15:00:00 +0000 + +gr-satellites (5.5.0-0) jammy; urgency=medium + + * Mainstream release v5.5.0 + + -- Thu, 28 Dec 2023 15:00:00 +0000 + gr-satellites (5.4.0-1) kinetic; urgency=medium * Mainstream release v5.4.0