From 3956ec60edae62d4fd4b09a3b3639b6c1f6f7127 Mon Sep 17 00:00:00 2001 From: Silvano Seva Date: Mon, 27 Oct 2025 21:16:04 +0100 Subject: [PATCH] M17: add clock recovery module Signed-off-by: Silvano Seva --- .../include/protocols/M17/ClockRecovery.hpp | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 openrtx/include/protocols/M17/ClockRecovery.hpp diff --git a/openrtx/include/protocols/M17/ClockRecovery.hpp b/openrtx/include/protocols/M17/ClockRecovery.hpp new file mode 100644 index 00000000..f83d8810 --- /dev/null +++ b/openrtx/include/protocols/M17/ClockRecovery.hpp @@ -0,0 +1,136 @@ +/*************************************************************************** + * Copyright (C) 2025 by Federico Amedeo Izzo IU2NUO, * + * Niccolò Izzo IU2KIN * + * 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 CLOCK_RECOVERY_H +#define CLOCK_RECOVERY_H + +#include +#include +#include + +/** + * Class to construct clock recovery objects. + * The clock recovery algorithm estimates the best sampling point by finding, + * within a symbol, the point with maximum energy. The algorithm will work + * properly only if correctly synchronized with the baseband stream. + */ +template +class ClockRecovery +{ +public: + /** + * Constructor + */ + ClockRecovery() + { + reset(); + } + + /** + * Destructor + */ + ~ClockRecovery() + { + } + + /** + * Reset the internal state. + */ + void reset() + { + curIdx = 0; + prevSample = 0; + numSamples = 0; + updateReq = false; + energy.fill(0); + } + + /** + * Process a new sample. + * + * @param sample: baseband sample. + */ + void sample(int16_t &sample) + { + int32_t delta = static_cast(sample) + - static_cast(prevSample); + + if ((sample + prevSample) < 0) + delta = -delta; + + energy[curIdx] += delta; + prevSample = sample; + curIdx = (curIdx + 1) % SAMPLES_PER_SYMBOL; + numSamples += 1; + } + + /** + * Update the best sampling point estimate. + */ + void update() + { + if (numSamples == 0) + return; + + uint8_t index = 0; + bool is_positive = false; + + for (size_t i = 0; i < SAMPLES_PER_SYMBOL; i++) { + int32_t phase = energy[i]; + + if (!is_positive && phase > 0) { + is_positive = true; + } else if (is_positive && phase < 0) { + index = i; + break; + } + } + + if (index == 0) + sp = SAMPLES_PER_SYMBOL - 1; + else + sp = index - 1; + + energy.fill(0); + numSamples = 0; + } + + /** + * Get the best sampling point estimate. + * The returned value is within the space of a simbol, that is in the + * range [0 SAMPLES_PER_SYMBOL - 1]. + * + * @return sampling point. + */ + uint8_t samplingPoint() + { + return sp; + } + +private: + std::array energy; + size_t curIdx; + size_t numSamples; + int16_t prevSample; + uint8_t sp; + bool updateReq; +}; + +#endif