/*************************************************************************** * Copyright (C) 2021 - 2025 by Federico Amedeo Izzo IU2NUO, * * Niccolò Izzo IU2KIN * * Wojciech Kaczmarski SP5WWP * * Frederik Saraci IU2NRO * * Silvano Seva IU2KWO * * * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see * ***************************************************************************/ #ifndef M17_DEMODULATOR_H #define M17_DEMODULATOR_H #ifndef __cplusplus #error This header is C++ only! #endif #include "core/iir.hpp" #include #include #include #include #include "core/dsp.h" #include #include "core/audio_path.h" #include "core/audio_stream.h" #include "protocols/M17/M17Datatypes.hpp" #include "protocols/M17/M17Constants.hpp" #include "protocols/M17/Correlator.hpp" #include "protocols/M17/Synchronizer.hpp" #include "protocols/M17/DevEstimator.hpp" namespace M17 { class M17Demodulator { public: /** * Constructor. */ M17Demodulator(); /** * Destructor. */ ~M17Demodulator(); /** * Allocate buffers for baseband signal sampling and initialise demodulator. */ void init(); /** * Shutdown modulator and deallocate data buffers. */ void terminate(); /** * Starts the sampling of the baseband signal in a double buffer. */ void startBasebandSampling(); /** * Stops the sampling of the baseband signal in a double buffer. */ void stopBasebandSampling(); /** * Returns the a frame decoded from the baseband signal. * * @return reference to the internal data structure containing the last * decoded frame. */ const frame_t& getFrame(); /** * Demodulates data from the ADC and fills the idle frame. * Everytime this function is called a whole ADC buffer is consumed. * * @param invertPhase: invert the phase of the baseband signal before decoding. * @return true if a new frame has been fully decoded. */ bool update(const bool invertPhase = false); /** * @return true if a demodulator is locked on an M17 stream. */ bool isLocked(); private: /** * Quantize a given sample to its corresponding symbol and append it to the * ongoing frame. When a frame is complete, it swaps the pointers and updates * newFrame variable. * * @param sample: baseband sample. * @return quantized symbol. */ void quantize(const int16_t sample); /** * Reset the demodulator state. */ void reset(); /** * State handler function for DemodState::UNLOCKED */ void unlockedState(); /** * State handler function for DemodState::SYNCED */ void syncedState(); /** * State handler function for DemodState::LOCKED * * @param sample: current baseband sample */ void lockedState(int16_t sample); /** * State handler function for DemodState::SYNC_UPDATE * * @param sample: current baseband sample */ void syncUpdateState(int16_t sample); /** * M17 baseband signal sampled at 24kHz, half of an M17 frame is processed * at each update of the demodulator. */ static constexpr size_t RX_SAMPLE_RATE = 24000; static constexpr size_t SAMPLES_PER_SYMBOL = RX_SAMPLE_RATE / M17_SYMBOL_RATE; static constexpr size_t FRAME_SAMPLES = M17_FRAME_SYMBOLS * SAMPLES_PER_SYMBOL; static constexpr size_t SAMPLE_BUF_SIZE = FRAME_SAMPLES / 2; static constexpr size_t SYNCWORD_SAMPLES = SAMPLES_PER_SYMBOL * M17_SYNCWORD_SYMBOLS; /** * Internal state of the demodulator. */ enum class DemodState { INIT, ///< Initializing UNLOCKED, ///< Not locked SYNCED, ///< Synchronized, validate syncword LOCKED, ///< Locked SYNC_UPDATE ///< Updating the sampling point }; /** * Cofficients of the sample filter */ static constexpr std::array < float, 3 > sfNum = {4.24433681e-05f, 8.48867363e-05f, 4.24433681e-05f}; static constexpr std::array < float, 3 > sfDen = {1.0f, -1.98148851f, 0.98165828f}; DemodState demodState; ///< Demodulator state std::unique_ptr< int16_t[] > baseband_buffer; ///< Buffer for baseband audio handling. streamId basebandId; ///< Id of the baseband input stream. pathId basebandPath; ///< Id of the baseband input path. std::unique_ptr demodFrame; ///< Frame being demodulated. std::unique_ptr readyFrame; ///< Fully demodulated frame to be returned. bool newFrame; ///< A new frame has been fully decoded. uint16_t frameIndex; ///< Index for filling the raw frame. uint32_t sampleIndex; ///< Sample index, from 0 to (SAMPLES_PER_SYMBOL - 1) uint32_t samplingPoint; ///< Symbol sampling point uint32_t sampleCount; ///< Free-running sample counter uint8_t missedSyncs; ///< Counter of missed synchronizations uint32_t initCount; ///< Downcounter for initialization uint32_t syncCount; ///< Downcounter for resynchronization float corrThreshold; ///< Correlation threshold struct dcBlock dcBlock; ///< State of the DC removal filter Correlator < M17_SYNCWORD_SYMBOLS, SAMPLES_PER_SYMBOL > correlator; Synchronizer < M17_SYNCWORD_SYMBOLS, SAMPLES_PER_SYMBOL > streamSync{{ -3, -3, -3, -3, +3, +3, -3, +3 }}; Iir < 3 > sampleFilter{sfNum, sfDen}; DevEstimator devEstimator; }; } /* M17 */ #endif /* M17_DEMODULATOR_H */