From 424d072f0cfe6fcb6cc7a92fdca628f370c72ccc Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 9 Jun 2022 20:12:35 +0200 Subject: [PATCH] M17 demod: removed FloatType template parameter --- .../channelrx/demodm17/m17/CarrierDetect.h | 15 +- .../channelrx/demodm17/m17/ClockRecovery.h | 54 +- plugins/channelrx/demodm17/m17/Correlator.cpp | 13 +- plugins/channelrx/demodm17/m17/Correlator.h | 27 +- .../demodm17/m17/DataCarrierDetect.h | 22 +- .../channelrx/demodm17/m17/DeviationError.h | 15 +- plugins/channelrx/demodm17/m17/FirFilter.h | 20 +- .../demodm17/m17/FreqDevEstimator.cpp | 13 +- .../channelrx/demodm17/m17/FreqDevEstimator.h | 47 +- .../channelrx/demodm17/m17/FrequencyError.h | 16 +- plugins/channelrx/demodm17/m17/Fsk4Demod.h | 18 +- plugins/channelrx/demodm17/m17/IirFilter.h | 34 +- .../channelrx/demodm17/m17/M17Demodulator.cpp | 492 ++++++++++++++++-- .../channelrx/demodm17/m17/M17Demodulator.h | 488 +---------------- plugins/channelrx/demodm17/m17/M17Modulator.h | 4 +- .../channelrx/demodm17/m17/PhaseEstimator.h | 19 +- plugins/channelrx/demodm17/m17/SlidingDFT.h | 32 +- plugins/channelrx/demodm17/m17/SymbolEvm.h | 28 +- plugins/channelrx/demodm17/m17/Util.h | 24 +- .../channelrx/demodm17/m17demodprocessor.h | 2 +- 20 files changed, 652 insertions(+), 731 deletions(-) diff --git a/plugins/channelrx/demodm17/m17/CarrierDetect.h b/plugins/channelrx/demodm17/m17/CarrierDetect.h index 1988fa1ed..2bc7dcf5c 100644 --- a/plugins/channelrx/demodm17/m17/CarrierDetect.h +++ b/plugins/channelrx/demodm17/m17/CarrierDetect.h @@ -13,22 +13,21 @@ namespace mobilinkd { -template struct CarrierDetect { - using result_t = std::tuple; + using result_t = std::tuple; - BaseIirFilter filter_; - FloatType lock_; - FloatType unlock_; + BaseIirFilter<3> filter_; + float lock_; + float unlock_; bool locked_ = false; - CarrierDetect(std::array const& b, std::array const& a, FloatType lock_level, FloatType unlock_level) + CarrierDetect(std::array const& b, std::array const& a, float lock_level, float unlock_level) : filter_(b, a), lock_(lock_level), unlock_(unlock_level) { } - - result_t operator()(FloatType value) + + result_t operator()(float value) { auto filtered = filter_(std::abs(value)); if (locked_ && (filtered > unlock_)) locked_ = false; diff --git a/plugins/channelrx/demodm17/m17/ClockRecovery.h b/plugins/channelrx/demodm17/m17/ClockRecovery.h index 37529e667..af002fe02 100644 --- a/plugins/channelrx/demodm17/m17/ClockRecovery.h +++ b/plugins/channelrx/demodm17/m17/ClockRecovery.h @@ -13,16 +13,16 @@ namespace mobilinkd /** * Calculate the phase estimates for each sample position. - * + * * This performs a running calculation of the phase of each bit position. * It is very noisy for individual samples, but quite accurate when * averaged over an entire M17 frame. - * + * * It is designed to be used to calculate the best bit position for each * frame of data. Samples are collected and averaged. When update() is * called, the best sample index and clock are estimated, and the counters * reset for the next frame. - * + * * It starts counting bit 0 as the first bit received after a reset. * * This is very efficient as it only uses addition and subtraction for @@ -37,24 +37,24 @@ namespace mobilinkd * @inv sample_index_ is in the interval [0, SAMPLES_PER_SYMBOL). * @inv clock_ is in the interval [0.9995, 1.0005] */ -template +template class ClockRecovery { static constexpr size_t SAMPLES_PER_SYMBOL = SampleRate / SymbolRate; static constexpr int8_t MAX_OFFSET = SAMPLES_PER_SYMBOL / 2; - static constexpr FloatType dx = 1.0 / SAMPLES_PER_SYMBOL; - static constexpr FloatType MAX_CLOCK = 1.0005; - static constexpr FloatType MIN_CLOCK = 0.9995; + static constexpr float dx = 1.0 / SAMPLES_PER_SYMBOL; + static constexpr float MAX_CLOCK = 1.0005; + static constexpr float MIN_CLOCK = 0.9995; - std::array estimates_; + std::array estimates_; size_t sample_count_ = 0; uint16_t frame_count_ = 0; uint8_t sample_index_ = 0; uint8_t prev_sample_index_ = 0; uint8_t index_ = 0; - FloatType offset_ = 0.0; - FloatType clock_ = 1.0; - FloatType prev_sample_ = 0.0; + float offset_ = 0.0; + float clock_ = 1.0; + float prev_sample_ = 0.0; /** * Find the sample index. @@ -78,7 +78,7 @@ class ClockRecovery bool is_positive = false; for (size_t i = 0; i != SAMPLES_PER_SYMBOL; ++i) { - FloatType phase = estimates_[i]; + float phase = estimates_[i]; if (!is_positive && phase > 0) { @@ -90,7 +90,7 @@ class ClockRecovery break; } } - + sample_index_ = index == 0 ? SAMPLES_PER_SYMBOL - 1 : index - 1; } @@ -99,7 +99,7 @@ class ClockRecovery * * This should never be greater than one. */ - FloatType calc_offset_() + float calc_offset_() { int8_t offset = sample_index_ - prev_sample_index_; @@ -139,14 +139,14 @@ public: { estimates_.fill(0); } - + /** * Update clock recovery with the given sample. This will advance the * current sample index by 1. */ - void operator()(FloatType sample) + void operator()(float sample) { - FloatType dy = (sample - prev_sample_); + float dy = (sample - prev_sample_); if (sample + prev_sample_ < 0) { @@ -155,7 +155,7 @@ public: } prev_sample_ = sample; - + estimates_[index_] += dy; index_ += 1; if (index_ == SAMPLES_PER_SYMBOL) @@ -189,18 +189,18 @@ public: /** * Return the estimated sample clock increment based on the last update. - * + * * The value is only valid after samples have been collected and update() * has been called. */ - FloatType clock_estimate() const + float clock_estimate() const { return clock_; } /** * Return the estimated "best sample index" based on the last update. - * + * * The value is only valid after samples have been collected and update() * has been called. */ @@ -208,20 +208,20 @@ public: { return sample_index_; } - + /** * Update the sample index and clock estimates, and reset the state for * the next frame of data. - * + * * @pre index_ = 0 * @pre sample_count_ > 0 - * + * * After this is called, sample_index() and clock_estimate() will have * valid, updated results. - * + * * The more samples between calls to update, the more accurate the * estimates will be. - * + * * @return true if the preconditions are met and the update has been * performed, otherwise false. */ @@ -231,7 +231,7 @@ public: update_sample_index_(); update_clock_(); - + frame_count_ = std::min(0x1000, 1 + frame_count_); sample_count_ = 0; estimates_.fill(0); diff --git a/plugins/channelrx/demodm17/m17/Correlator.cpp b/plugins/channelrx/demodm17/m17/Correlator.cpp index 863b1b3f3..579ba7e5a 100644 --- a/plugins/channelrx/demodm17/m17/Correlator.cpp +++ b/plugins/channelrx/demodm17/m17/Correlator.cpp @@ -3,16 +3,7 @@ namespace mobilinkd { // IIR with Nyquist of 1/240. -template<> -const std::array Correlator::b = {4.24433681e-05, 8.48867363e-05, 4.24433681e-05}; - -template<> -const std::array Correlator::a = {1.0, -1.98148851, 0.98165828}; - -template<> -const std::array Correlator::b = {4.24433681e-05, 8.48867363e-05, 4.24433681e-05}; - -template<> -const std::array Correlator::a = {1.0, -1.98148851, 0.98165828}; +const std::array Correlator::b = {4.24433681e-05, 8.48867363e-05, 4.24433681e-05}; +const std::array Correlator::a = {1.0, -1.98148851, 0.98165828}; } // namespace mobilinkd diff --git a/plugins/channelrx/demodm17/m17/Correlator.h b/plugins/channelrx/demodm17/m17/Correlator.h index 7822ecfa6..cefd5dfc9 100644 --- a/plugins/channelrx/demodm17/m17/Correlator.h +++ b/plugins/channelrx/demodm17/m17/Correlator.h @@ -15,32 +15,31 @@ namespace mobilinkd { -template struct Correlator { static constexpr size_t SYMBOLS = 8; static constexpr size_t SAMPLES_PER_SYMBOL = 10; - using value_type = FloatType; - using buffer_t = std::array; + using value_type = float; + using buffer_t = std::array; using sync_t = std::array; - using sample_filter_t = BaseIirFilter; + using sample_filter_t = BaseIirFilter<3>; buffer_t buffer_; - FloatType limit_ = 0.; + float limit_ = 0.; size_t symbol_pos_ = 0; size_t buffer_pos_ = 0; size_t prev_buffer_pos_ = 0; int code = -1; // IIR with Nyquist of 1/240. - static const std::array b; - static const std::array a; + static const std::array b; + static const std::array a; sample_filter_t sample_filter{b, a}; std::array tmp; - void sample(FloatType value) + void sample(float value) { limit_ = sample_filter(std::abs(value)); buffer_[buffer_pos_] = value; @@ -48,9 +47,9 @@ struct Correlator if (++buffer_pos_ == buffer_.size()) buffer_pos_ = 0; } - FloatType correlate(sync_t sync) + float correlate(sync_t sync) { - FloatType result = 0.; + float result = 0.; size_t pos = prev_buffer_pos_ + SAMPLES_PER_SYMBOL; for (size_t i = 0; i != sync.size(); ++i) @@ -63,7 +62,7 @@ struct Correlator return result; } - FloatType limit() const {return limit_;} + float limit() const {return limit_;} size_t index() const {return prev_buffer_pos_ % SAMPLES_PER_SYMBOL;} /** @@ -78,10 +77,10 @@ struct Correlator * second holds true for the sync words used for M17. The third will * hold true if passed the timing index from a triggered sync word. */ - std::tuple outer_symbol_levels(size_t sample_index) + std::tuple outer_symbol_levels(size_t sample_index) { - FloatType min_sum = 0; - FloatType max_sum = 0; + float min_sum = 0; + float max_sum = 0; size_t min_count = 0; size_t max_count = 0; size_t index = 0; diff --git a/plugins/channelrx/demodm17/m17/DataCarrierDetect.h b/plugins/channelrx/demodm17/m17/DataCarrierDetect.h index 31a89318d..fac4f218b 100644 --- a/plugins/channelrx/demodm17/m17/DataCarrierDetect.h +++ b/plugins/channelrx/demodm17/m17/DataCarrierDetect.h @@ -25,23 +25,23 @@ namespace mobilinkd { * * Note: the input to this DCD must be unfiltered (raw) baseband input. */ -template +template struct DataCarrierDetect { - using ComplexType = std::complex; - using NDFT = NSlidingDFT; + using ComplexType = std::complex; + using NDFT = NSlidingDFT; NDFT dft_; - FloatType ltrigger_; - FloatType htrigger_; - FloatType level_1 = 0.0; - FloatType level_2 = 0.0; - FloatType level_ = 0.0; + float ltrigger_; + float htrigger_; + float level_1 = 0.0; + float level_2 = 0.0; + float level_ = 0.0; bool triggered_ = false; DataCarrierDetect( size_t freq1, size_t freq2, - FloatType ltrigger = 2.0, FloatType htrigger = 5.0) + float ltrigger = 2.0, float htrigger = 5.0) : dft_({freq1, freq2}), ltrigger_(ltrigger), htrigger_(htrigger) { } @@ -50,7 +50,7 @@ struct DataCarrierDetect * Accept unfiltered baseband input and output a decision on whether * a carrier has been detected after every @tparam BlockSize inputs. */ - void operator()(FloatType sample) + void operator()(float sample) { auto result = dft_(sample); level_1 += std::norm(result[0]); @@ -69,7 +69,7 @@ struct DataCarrierDetect } - FloatType level() const { return level_; } + float level() const { return level_; } bool dcd() const { return triggered_; } }; diff --git a/plugins/channelrx/demodm17/m17/DeviationError.h b/plugins/channelrx/demodm17/m17/DeviationError.h index 51c475b02..a18301b7e 100644 --- a/plugins/channelrx/demodm17/m17/DeviationError.h +++ b/plugins/channelrx/demodm17/m17/DeviationError.h @@ -9,11 +9,10 @@ namespace mobilinkd { -template +template struct DeviationError { - using float_type = T; - using array_t = std::array; + using array_t = std::array; array_t minima_{0}; array_t maxima_{0}; @@ -23,18 +22,18 @@ struct DeviationError bool max_rolled_ = false; size_t min_count_ = 0; size_t max_count_ = 0; - float_type min_estimate_ = 0.0; - float_type max_estimate_ = 0.0; + float min_estimate_ = 0.0; + float max_estimate_ = 0.0; - const float_type ZERO = 0.0; + const float ZERO = 0.0; DeviationError() { minima_.fill(0.0); maxima_.fill(0.0); } - - float_type operator()(float_type sample) + + float operator()(float sample) { if (sample > ZERO) { diff --git a/plugins/channelrx/demodm17/m17/FirFilter.h b/plugins/channelrx/demodm17/m17/FirFilter.h index 93953b3b5..e33457755 100644 --- a/plugins/channelrx/demodm17/m17/FirFilter.h +++ b/plugins/channelrx/demodm17/m17/FirFilter.h @@ -10,27 +10,27 @@ namespace mobilinkd { -template -struct BaseFirFilter : FilterBase +template +struct BaseFirFilter : FilterBase { - using array_t = std::array; + using array_t = std::array; const array_t& taps_; array_t history_; size_t pos_ = 0; - + BaseFirFilter(const array_t& taps) : taps_(taps) { history_.fill(0.0); } - - FloatType operator()(FloatType input) override + + float operator()(float input) override { history_[pos_++] = input; if (pos_ == N) pos_ = 0; - FloatType result = 0.0; + float result = 0.0; size_t index = pos_; for (size_t i = 0; i != N; ++i) @@ -49,10 +49,10 @@ struct BaseFirFilter : FilterBase } }; -template -BaseFirFilter makeFirFilter(const std::array& taps) +template +BaseFirFilter makeFirFilter(const std::array& taps) { - return std::move(BaseFirFilter(taps)); + return std::move(BaseFirFilter(taps)); } diff --git a/plugins/channelrx/demodm17/m17/FreqDevEstimator.cpp b/plugins/channelrx/demodm17/m17/FreqDevEstimator.cpp index 0cb0ecb1f..ce248ce7f 100644 --- a/plugins/channelrx/demodm17/m17/FreqDevEstimator.cpp +++ b/plugins/channelrx/demodm17/m17/FreqDevEstimator.cpp @@ -2,16 +2,7 @@ namespace mobilinkd { -template<> -const std::array FreqDevEstimator::dc_b = { 0.09763107, 0.19526215, 0.09763107 }; - -template<> -const std::array FreqDevEstimator::dc_a = { 1. , -0.94280904, 0.33333333 }; - -template<> -const std::array FreqDevEstimator::dc_b = { 0.09763107, 0.19526215, 0.09763107 }; - -template<> -const std::array FreqDevEstimator::dc_a = { 1. , -0.94280904, 0.33333333 }; +const std::array FreqDevEstimator::dc_b = { 0.09763107, 0.19526215, 0.09763107 }; +const std::array FreqDevEstimator::dc_a = { 1. , -0.94280904, 0.33333333 }; } // namespace mobilinkd diff --git a/plugins/channelrx/demodm17/m17/FreqDevEstimator.h b/plugins/channelrx/demodm17/m17/FreqDevEstimator.h index 9f8d49fa7..f65c12759 100644 --- a/plugins/channelrx/demodm17/m17/FreqDevEstimator.h +++ b/plugins/channelrx/demodm17/m17/FreqDevEstimator.h @@ -24,29 +24,28 @@ namespace mobilinkd { * Estimates are expected to be updated at each sync word. But they can * be updated more frequently, such as during the preamble. */ -template class FreqDevEstimator { - using sample_filter_t = BaseIirFilter; + using sample_filter_t = BaseIirFilter<3>; // IIR with Nyquist of 1/4. - static const std::array dc_b; - static const std::array dc_a; + static const std::array dc_b; + static const std::array dc_a; - static constexpr FloatType MAX_DC_ERROR = 0.2; + static constexpr float MAX_DC_ERROR = 0.2; - FloatType min_est_ = 0.0; - FloatType max_est_ = 0.0; - FloatType min_cutoff_ = 0.0; - FloatType max_cutoff_ = 0.0; - FloatType min_var_ = 0.0; - FloatType max_var_ = 0.0; + float min_est_ = 0.0; + float max_est_ = 0.0; + float min_cutoff_ = 0.0; + float max_cutoff_ = 0.0; + float min_var_ = 0.0; + float max_var_ = 0.0; size_t min_count_ = 0; size_t max_count_ = 0; - FloatType deviation_ = 0.0; - FloatType offset_ = 0.0; - FloatType error_ = 0.0; - FloatType idev_ = 1.0; + float deviation_ = 0.0; + float offset_ = 0.0; + float error_ = 0.0; + float idev_ = 1.0; sample_filter_t dc_filter_{dc_b, dc_a}; public: @@ -63,7 +62,7 @@ public: max_cutoff_ = 0.0; } - void sample(FloatType sample) + void sample(float sample) { if (sample < 1.5 * min_est_) { @@ -76,7 +75,7 @@ public: { min_count_ += 1; min_est_ += sample; - FloatType var = (min_est_ / min_count_) - sample; + float var = (min_est_ / min_count_) - sample; min_var_ += var * var; } else if (sample > 1.5 * max_est_) @@ -90,7 +89,7 @@ public: { max_count_ += 1; max_est_ += sample; - FloatType var = (max_est_ / max_count_) - sample; + float var = (max_est_ / max_count_) - sample; max_var_ += var * var; } } @@ -104,8 +103,8 @@ public: void update() { if (max_count_ < 2 || min_count_ < 2) return; - FloatType max_ = max_est_ / max_count_; - FloatType min_ = min_est_ / min_count_; + float max_ = max_est_ / max_count_; + float min_ = min_est_ / min_count_; deviation_ = (max_ - min_) / 6.0; offset_ = dc_filter_(std::max(std::min(max_ + min_, deviation_ * MAX_DC_ERROR), deviation_ * -MAX_DC_ERROR)); error_ = (std::sqrt(max_var_ / (max_count_ - 1)) + std::sqrt(min_var_ / (min_count_ - 1))) * 0.5; @@ -120,10 +119,10 @@ public: min_var_ = 0.0; } - FloatType deviation() const { return deviation_; } - FloatType offset() const { return offset_; } - FloatType error() const { return error_; } - FloatType idev() const { return idev_; } + float deviation() const { return deviation_; } + float offset() const { return offset_; } + float error() const { return error_; } + float idev() const { return idev_; } }; } // mobilinkd diff --git a/plugins/channelrx/demodm17/m17/FrequencyError.h b/plugins/channelrx/demodm17/m17/FrequencyError.h index 3534ade8e..d65ede721 100644 --- a/plugins/channelrx/demodm17/m17/FrequencyError.h +++ b/plugins/channelrx/demodm17/m17/FrequencyError.h @@ -11,15 +11,15 @@ namespace mobilinkd { -template +template struct FrequencyError { - using float_type = FloatType; - using array_t = std::array; - using filter_type = BaseIirFilter; + using float_type = float; + using array_t = std::array; + using filter_type = BaseIirFilter<3>; - static constexpr std::array evm_b{0.02008337, 0.04016673, 0.02008337}; - static constexpr std::array evm_a{1.0, -1.56101808, 0.64135154}; + static constexpr std::array evm_b{0.02008337, 0.04016673, 0.02008337}; + static constexpr std::array evm_a{1.0, -1.56101808, 0.64135154}; array_t samples_{0}; size_t index_ = 0; @@ -33,10 +33,10 @@ struct FrequencyError { samples_.fill(0.0); } - + auto operator()(float_type sample) { - FloatType evm = 0; + float evm = 0; bool use = true; if (sample > 2) diff --git a/plugins/channelrx/demodm17/m17/Fsk4Demod.h b/plugins/channelrx/demodm17/m17/Fsk4Demod.h index 33fd42e3c..792fbf29a 100644 --- a/plugins/channelrx/demodm17/m17/Fsk4Demod.h +++ b/plugins/channelrx/demodm17/m17/Fsk4Demod.h @@ -16,7 +16,7 @@ namespace mobilinkd namespace detail { -static const auto rrc_taps = std::array{ +static const auto rrc_taps = std::array{ -0.009265784007800534, -0.006136551625729697, -0.001125978562075172, 0.004891777252042491, 0.01071805138282269, 0.01505751553351295, 0.01679337935001369, 0.015256245142156299, 0.01042830577908502, 0.003031522725559901, -0.0055333532968188165, -0.013403099825723372, @@ -39,8 +39,8 @@ static const auto rrc_taps = std::array{ -0.001125978562075172, -0.006136551625729697, -0.009265784007800534 }; -static const auto evm_b = std::array{0.02008337, 0.04016673, 0.02008337}; -static const auto evm_a = std::array{1.0, -1.56101808, 0.64135154}; +static const auto evm_b = std::array{0.02008337, 0.04016673, 0.02008337}; +static const auto evm_a = std::array{1.0, -1.56101808, 0.64135154}; } // detail struct Fsk4Demod @@ -48,17 +48,17 @@ struct Fsk4Demod using demod_result_t = std::tuple; using result_t = std::tuple; - BaseFirFilter::value> rrc = makeFirFilter(detail::rrc_taps); - PhaseEstimator phase = PhaseEstimator(48000, 4800); - DeviationError deviation; - FrequencyError frequency; - SymbolEvm::value> symbol_evm = makeSymbolEvm(makeIirFilter(detail::evm_b, detail::evm_a)); + BaseFirFilter::value> rrc = makeFirFilter(detail::rrc_taps); + PhaseEstimator phase = PhaseEstimator(48000, 4800); + DeviationError<10> deviation; + FrequencyError<32> frequency; + SymbolEvm::value> symbol_evm = makeSymbolEvm(makeIirFilter(detail::evm_b, detail::evm_a)); double sample_rate = 48000; double symbol_rate = 4800; double unlock_gain = 0.02; double lock_gain = 0.001; - std::array samples{0}; + std::array samples{0}; double t = 0; double dt = symbol_rate / sample_rate; double ideal_dt = dt; diff --git a/plugins/channelrx/demodm17/m17/IirFilter.h b/plugins/channelrx/demodm17/m17/IirFilter.h index 947c38d83..fc6251263 100644 --- a/plugins/channelrx/demodm17/m17/IirFilter.h +++ b/plugins/channelrx/demodm17/m17/IirFilter.h @@ -10,43 +10,43 @@ namespace mobilinkd { -template -struct BaseIirFilter : FilterBase +template +struct BaseIirFilter : FilterBase { - const std::array& numerator_; - const std::array denominator_; - std::array history_{0}; - - BaseIirFilter(const std::array& b, const std::array& a) + const std::array& numerator_; + const std::array denominator_; + std::array history_{0}; + + BaseIirFilter(const std::array& b, const std::array& a) : numerator_(b), denominator_(a) { history_.fill(0.0); } - - FloatType operator()(FloatType input) { + + float operator()(float input) { for (size_t i = N - 1; i != 0; i--) history_[i] = history_[i - 1]; - + history_[0] = input; for (size_t i = 1; i != N; i++) { history_[0] -= denominator_[i] * history_[i]; } - - FloatType result = 0; + + float result = 0; for (size_t i = 0; i != N; i++) { result += numerator_[i] * history_[i]; } - + return result; } }; -template -BaseIirFilter makeIirFilter( - const std::array& b, const std::array& a) +template +BaseIirFilter makeIirFilter( + const std::array& b, const std::array& a) { - return std::move(BaseIirFilter(b, a)); + return std::move(BaseIirFilter(b, a)); } } // mobilinkd diff --git a/plugins/channelrx/demodm17/m17/M17Demodulator.cpp b/plugins/channelrx/demodm17/m17/M17Demodulator.cpp index 1754df4f7..c43ac14df 100644 --- a/plugins/channelrx/demodm17/m17/M17Demodulator.cpp +++ b/plugins/channelrx/demodm17/m17/M17Demodulator.cpp @@ -2,8 +2,7 @@ namespace mobilinkd { -template <> -const std::array M17Demodulator::rrc_taps = std::array{ +const std::array M17Demodulator::rrc_taps = std::array{ 0.0029364388513841593, 0.0031468394550958484, 0.002699564567597445, 0.001661182944400927, 0.00023319405581230247, -0.0012851320781224025, -0.0025577136087664687, -0.0032843366522956313, -0.0032697038088887226, -0.0024733964729590865, -0.0010285696910973807, 0.0007766690889758685, @@ -44,46 +43,453 @@ const std::array M17Demodulator::rrc_taps = std::array -const std::array M17Demodulator::rrc_taps = std::array{ - 0.0029364388513841593, 0.0031468394550958484, 0.002699564567597445, 0.001661182944400927, - 0.00023319405581230247, -0.0012851320781224025, -0.0025577136087664687, -0.0032843366522956313, - -0.0032697038088887226, -0.0024733964729590865, -0.0010285696910973807, 0.0007766690889758685, - 0.002553421969211845, 0.0038920145144327816, 0.004451886520053017, 0.00404219185231544, - 0.002674727068399207, 0.0005756567993179152, -0.0018493784971116507, -0.004092346891623224, - -0.005648131453822014, -0.006126925416243605, -0.005349511529163396, -0.003403189203405097, - -0.0006430502751187517, 0.002365929161655135, 0.004957956568090113, 0.006506845894531803, - 0.006569574194782443, 0.0050017573119839134, 0.002017321931508163, -0.0018256054303579805, - -0.00571615173291049, -0.008746639552588416, -0.010105075751866371, -0.009265784007800534, - -0.006136551625729697, -0.001125978562075172, 0.004891777252042491, 0.01071805138282269, - 0.01505751553351295, 0.01679337935001369, 0.015256245142156299, 0.01042830577908502, - 0.003031522725559901, -0.0055333532968188165, -0.013403099825723372, -0.018598682349642525, - -0.01944761739590459, -0.015005271935951746, -0.0053887880354343935, 0.008056525910253532, - 0.022816244158307273, 0.035513467692208076, 0.04244131815783876, 0.04025481153629372, - 0.02671818654865632, 0.0013810216516704976, -0.03394615682795165, -0.07502635967975885, - -0.11540977897637611, -0.14703962203941534, -0.16119995609538576, -0.14969512896336504, - -0.10610329539459686, -0.026921412469634916, 0.08757875030779196, 0.23293327870303457, - 0.4006012210123992, 0.5786324696325503, 0.7528286479934068, 0.908262741447522, - 1.0309661131633199, 1.1095611856548013, 1.1366197723675815, 1.1095611856548013, - 1.0309661131633199, 0.908262741447522, 0.7528286479934068, 0.5786324696325503, - 0.4006012210123992, 0.23293327870303457, 0.08757875030779196, -0.026921412469634916, - -0.10610329539459686, -0.14969512896336504, -0.16119995609538576, -0.14703962203941534, - -0.11540977897637611, -0.07502635967975885, -0.03394615682795165, 0.0013810216516704976, - 0.02671818654865632, 0.04025481153629372, 0.04244131815783876, 0.035513467692208076, - 0.022816244158307273, 0.008056525910253532, -0.0053887880354343935, -0.015005271935951746, - -0.01944761739590459, -0.018598682349642525, -0.013403099825723372, -0.0055333532968188165, - 0.003031522725559901, 0.01042830577908502, 0.015256245142156299, 0.01679337935001369, - 0.01505751553351295, 0.01071805138282269, 0.004891777252042491, -0.001125978562075172, - -0.006136551625729697, -0.009265784007800534, -0.010105075751866371, -0.008746639552588416, - -0.00571615173291049, -0.0018256054303579805, 0.002017321931508163, 0.0050017573119839134, - 0.006569574194782443, 0.006506845894531803, 0.004957956568090113, 0.002365929161655135, - -0.0006430502751187517, -0.003403189203405097, -0.005349511529163396, -0.006126925416243605, - -0.005648131453822014, -0.004092346891623224, -0.0018493784971116507, 0.0005756567993179152, - 0.002674727068399207, 0.00404219185231544, 0.004451886520053017, 0.0038920145144327816, - 0.002553421969211845, 0.0007766690889758685, -0.0010285696910973807, -0.0024733964729590865, - -0.0032697038088887226, -0.0032843366522956313, -0.0025577136087664687, -0.0012851320781224025, - 0.00023319405581230247, 0.001661182944400927, 0.002699564567597445, 0.0031468394550958484, - 0.0029364388513841593, 0.0 -}; +void M17Demodulator::update_values(uint8_t index) +{ + correlator.apply([this,index](float t){dev.sample(t);}, index); + dev.update(); + sync_sample_index = index; +} + +void M17Demodulator::dcd_on() +{ + // Data carrier newly detected. + dcd_ = true; + sync_count = 0; + missing_sync_count = 0; + + dev.reset(); + framer.reset(); + decoder.reset(); +} + +void M17Demodulator::dcd_off() +{ + // Just lost data carrier. + dcd_ = false; + demodState = DemodState::UNLOCKED; + decoder.reset(); + + if (diagnostic_callback) + { + diagnostic_callback(int(dcd_), dev.error(), dev.deviation(), dev.offset(), (int) demodState, + clock_recovery.clock_estimate(), sample_index, sync_sample_index, clock_recovery.sample_index(), -1); + } +} + +void M17Demodulator::initialize(const float input) +{ + auto filtered_sample = demod_filter(input); + correlator.sample(filtered_sample); +} + +void M17Demodulator::update_dcd() +{ + if (!dcd_ && dcd.dcd()) + { + // fputs("\nAOS\n", stderr); + dcd_on(); + need_clock_reset_ = true; + } + else if (dcd_ && !dcd.dcd()) + { + // fputs("\nLOS\n", stderr); + dcd_off(); + } +} + +void M17Demodulator::do_unlocked() +{ + // We expect to find the preamble immediately after DCD. + if (missing_sync_count < 1920) + { + missing_sync_count += 1; + auto sync_index = preamble_sync(correlator); + auto sync_updated = preamble_sync.updated(); + if (sync_updated) + { + sync_count = 0; + missing_sync_count = 0; + need_clock_reset_ = true; + dev.reset(); + update_values(sync_index); + sample_index = sync_index; + demodState = DemodState::LSF_SYNC; + } + return; + } + auto sync_index = lsf_sync(correlator); + auto sync_updated = lsf_sync.updated(); + if (sync_updated) + { + sync_count = 0; + missing_sync_count = 0; + need_clock_reset_ = true; + dev.reset(); + update_values(sync_index); + sample_index = sync_index; + demodState = DemodState::FRAME; + if (sync_updated < 0) + { + sync_word_type = M17FrameDecoder::SyncWordType::STREAM; + } + else + { + sync_word_type = M17FrameDecoder::SyncWordType::LSF; + } + return; + } + sync_index = packet_sync(correlator); + sync_updated = packet_sync.updated(); + if (sync_updated < 0) + { + sync_count = 0; + missing_sync_count = 0; + need_clock_reset_ = true; + dev.reset(); + update_values(sync_index); + sample_index = sync_index; + demodState = DemodState::FRAME; + sync_word_type = M17FrameDecoder::SyncWordType::BERT; + } +} + +/** + * Check for LSF sync word. We only enter the DemodState::LSF_SYNC state + * if a preamble sync has been detected, which also means that sample_index + * has been initialized to a sane value for the baseband. + */ +void M17Demodulator::do_lsf_sync() +{ + float sync_triggered = 0.; + float bert_triggered = 0.; + + if (correlator.index() == sample_index) + { + sync_triggered = preamble_sync.triggered(correlator); + if (sync_triggered > 0.1) + { + return; + } + sync_triggered = lsf_sync.triggered(correlator); + bert_triggered = packet_sync.triggered(correlator); + if (bert_triggered < 0) + { + missing_sync_count = 0; + need_clock_update_ = true; + update_values(sample_index); + demodState = DemodState::FRAME; + sync_word_type = M17FrameDecoder::SyncWordType::BERT; + } + else if (std::abs(sync_triggered) > 0.1) + { + missing_sync_count = 0; + need_clock_update_ = true; + update_values(sample_index); + if (sync_triggered > 0) + { + demodState = DemodState::FRAME; + sync_word_type = M17FrameDecoder::SyncWordType::LSF; + } + else + { + demodState = DemodState::FRAME; + sync_word_type = M17FrameDecoder::SyncWordType::STREAM; + } + } + else if (++missing_sync_count > 192) + { + demodState = DemodState::UNLOCKED; + decoder.reset(); + missing_sync_count = 0; + } + else + { + update_values(sample_index); + } + } +} + +/** + * Check for a stream sync word (LSF sync word that is maximally negative). + * We can enter DemodState::STREAM_SYNC from either a valid LSF decode for + * an audio stream, or from a stream frame decode. + * + */ +void M17Demodulator::do_stream_sync() +{ + uint8_t sync_index = lsf_sync(correlator); + int8_t sync_updated = lsf_sync.updated(); + sync_count += 1; + if (sync_updated < 0) // Stream sync word + { + missing_sync_count = 0; + if (sync_count > 70) + { + update_values(sync_index); + sync_word_type = M17FrameDecoder::SyncWordType::STREAM; + demodState = DemodState::FRAME; + } + return; + } + else if (sync_count > 87) + { + update_values(sync_index); + missing_sync_count += 1; + if (missing_sync_count < MAX_MISSING_SYNC) + { + sync_word_type = M17FrameDecoder::SyncWordType::STREAM; + demodState = DemodState::FRAME; + } + else + { + // fputs("\n!SYNC\n", stderr); + demodState = DemodState::LSF_SYNC; + } + } +} + +/** + * Check for a packet sync word. DemodState::PACKET_SYNC can only be + * entered from a valid LSF frame decode with the data/packet type bit set. + */ +void M17Demodulator::do_packet_sync() +{ + auto sync_index = packet_sync(correlator); + auto sync_updated = packet_sync.updated(); + sync_count += 1; + if (sync_count > 70 && sync_updated) + { + missing_sync_count = 0; + update_values(sync_index); + sync_word_type = M17FrameDecoder::SyncWordType::PACKET; + demodState = DemodState::FRAME; + } + else if (sync_count > 87) + { + missing_sync_count += 1; + if (missing_sync_count < MAX_MISSING_SYNC) + { + sync_word_type = M17FrameDecoder::SyncWordType::PACKET; + demodState = DemodState::FRAME; + } + else + { + demodState = DemodState::UNLOCKED; + decoder.reset(); + } + } +} + +/** + * Check for a bert sync word. + */ +void M17Demodulator::do_bert_sync() +{ + auto sync_index = packet_sync(correlator); + auto sync_updated = packet_sync.updated(); + sync_count += 1; + if (sync_count > 70 && sync_updated < 0) + { + missing_sync_count = 0; + update_values(sync_index); + sync_word_type = M17FrameDecoder::SyncWordType::BERT; + demodState = DemodState::FRAME; + } + else if (sync_count > 87) + { + missing_sync_count += 1; + if (missing_sync_count < MAX_MISSING_SYNC) + { + sync_word_type = M17FrameDecoder::SyncWordType::BERT; + demodState = DemodState::FRAME; + } + else + { + demodState = DemodState::UNLOCKED; + decoder.reset(); + } + } +} + + +void M17Demodulator::do_frame(float filtered_sample) +{ + if (correlator.index() != sample_index) return; + + static uint8_t cost_count = 0; + + auto sample = filtered_sample - dev.offset(); + sample *= dev.idev(); + sample *= polarity; + + auto n = llr<4>(sample); + int8_t* tmp; + auto len = framer(n, &tmp); + if (len != 0) + { + need_clock_update_ = true; + + M17FrameDecoder::input_buffer_t buffer; + std::copy(tmp, tmp + len, buffer.begin()); + auto valid = decoder(sync_word_type, buffer, viterbi_cost); + + cost_count = viterbi_cost > 90 ? cost_count + 1 : 0; + cost_count = viterbi_cost > 100 ? cost_count + 1 : cost_count; + cost_count = viterbi_cost > 110 ? cost_count + 1 : cost_count; + + if (cost_count > 75) + { + cost_count = 0; + demodState = DemodState::UNLOCKED; + decoder.reset(); + // fputs("\nCOST\n", stderr); + return; + } + + switch (decoder.state()) + { + case M17FrameDecoder::State::STREAM: + demodState = DemodState::STREAM_SYNC; + break; + case M17FrameDecoder::State::LSF: + // If state == LSF, we need to recover LSF from LICH. + demodState = DemodState::STREAM_SYNC; + break; + case M17FrameDecoder::State::BERT: + demodState = DemodState::BERT_SYNC; + break; + default: + demodState = DemodState::PACKET_SYNC; + break; + } + + sync_count = 0; + + switch (valid) + { + case M17FrameDecoder::DecodeResult::FAIL: + break; + case M17FrameDecoder::DecodeResult::EOS: + demodState = DemodState::LSF_SYNC; + break; + case M17FrameDecoder::DecodeResult::OK: + break; + case M17FrameDecoder::DecodeResult::INCOMPLETE: + break; + case M17FrameDecoder::DecodeResult::PACKET_INCOMPLETE: + break; + } + } +} + +void M17Demodulator::operator()(const float input) +{ + static int16_t initializing = 1920; + + count_++; + + dcd(input); + + // We need to pump a few ms of data through on startup to initialize + // the demodulator. + if (initializing) [[unlikely]] + { + --initializing; + initialize(input); + count_ = 0; + return; + } + + if (!dcd_) + { + if (count_ % (BLOCK_SIZE * 2) == 0) + { + update_dcd(); + dcd.update(); + + if (diagnostic_callback) + { + diagnostic_callback(int(dcd_), dev.error(), dev.deviation(), dev.offset(), (int) demodState, + clock_recovery.clock_estimate(), sample_index, sync_sample_index, clock_recovery.sample_index(), viterbi_cost); + } + + count_ = 0; + } + + return; + } + + auto filtered_sample = demod_filter(input); + + correlator.sample(filtered_sample); + + if (correlator.index() == 0) + { + if (need_clock_reset_) + { + clock_recovery.reset(); + need_clock_reset_ = false; + } + else if (need_clock_update_) // must avoid update immediately after reset. + { + clock_recovery.update(); + uint8_t clock_index = clock_recovery.sample_index(); + uint8_t clock_diff = std::abs(sample_index - clock_index); + uint8_t sync_diff = std::abs(sample_index - sync_sample_index); + bool clock_diff_ok = clock_diff <= 1 || clock_diff == 9; + bool sync_diff_ok = sync_diff <= 1 || sync_diff == 9; + if (clock_diff_ok) sample_index = clock_index; + else if (sync_diff_ok) sample_index = sync_sample_index; + // else unchanged. + need_clock_update_ = false; + } + } + + clock_recovery(filtered_sample); + + if (demodState != DemodState::UNLOCKED && correlator.index() == sample_index) + { + dev.sample(filtered_sample); + } + + switch (demodState) + { + case DemodState::UNLOCKED: + // In this state, the sample_index is unknown. We need to find + // a sync word to find the proper sample_index. We only leave + // this state if we believe that we have a valid sample_index. + do_unlocked(); + break; + case DemodState::LSF_SYNC: + do_lsf_sync(); + break; + case DemodState::STREAM_SYNC: + do_stream_sync(); + break; + case DemodState::PACKET_SYNC: + do_packet_sync(); + break; + case DemodState::BERT_SYNC: + do_bert_sync(); + break; + case DemodState::FRAME: + do_frame(filtered_sample); + break; + } + + if (count_ % (BLOCK_SIZE * 5) == 0) + { + update_dcd(); + count_ = 0; + + if (diagnostic_callback) + { + diagnostic_callback(int(dcd_), dev.error(), dev.deviation(), dev.offset(), (int) demodState, + clock_recovery.clock_estimate(), sample_index, sync_sample_index, clock_recovery.sample_index(), viterbi_cost); + } + + dcd.update(); + } +} } // mobilinkd diff --git a/plugins/channelrx/demodm17/m17/M17Demodulator.h b/plugins/channelrx/demodm17/m17/M17Demodulator.h index cae74f8dc..7bcc604ba 100644 --- a/plugins/channelrx/demodm17/m17/M17Demodulator.h +++ b/plugins/channelrx/demodm17/m17/M17Demodulator.h @@ -26,7 +26,6 @@ namespace detail } // detail -template struct M17Demodulator { static const uint16_t SAMPLE_RATE = 48000; @@ -34,15 +33,15 @@ struct M17Demodulator static const uint16_t SAMPLES_PER_SYMBOL = SAMPLE_RATE / SYMBOL_RATE; static const uint16_t BLOCK_SIZE = 192; - static constexpr FloatType sample_rate = SAMPLE_RATE; - static constexpr FloatType symbol_rate = SYMBOL_RATE; + static constexpr float sample_rate = SAMPLE_RATE; + static constexpr float symbol_rate = SYMBOL_RATE; static const uint8_t MAX_MISSING_SYNC = 8; - using collelator_t = Correlator; + using collelator_t = Correlator; using sync_word_t = SyncWord; using callback_t = M17FrameDecoder::callback_t; - using diagnostic_callback_t = std::function; + using diagnostic_callback_t = std::function; enum class DemodState { UNLOCKED, @@ -53,16 +52,16 @@ struct M17Demodulator FRAME }; - DataCarrierDetect dcd{2500, 4000, 1.0, 4.0}; - ClockRecovery clock_recovery; + DataCarrierDetect dcd{2500, 4000, 1.0, 4.0}; + ClockRecovery clock_recovery; collelator_t correlator; sync_word_t preamble_sync{{+3,-3,+3,-3,+3,-3,+3,-3}, 29.f}; sync_word_t lsf_sync{{+3,+3,+3,+3,-3,-3,+3,-3}, 32.f, -31.f}; // LSF or STREAM (inverted) sync_word_t packet_sync{{3,-3,3,3,-3,-3,-3,-3}, 31.f, -31.f}; // PACKET or BERT (inverted) - FreqDevEstimator dev; - FloatType idev; + FreqDevEstimator dev; + float idev; size_t count_ = 0; int8_t polarity = 1; @@ -91,14 +90,14 @@ struct M17Demodulator void dcd_on(); void dcd_off(); - void initialize(const FloatType input); + void initialize(const float input); void update_dcd(); void do_unlocked(); void do_lsf_sync(); void do_packet_sync(); void do_stream_sync(); void do_bert_sync(); - void do_frame(FloatType filtered_sample); + void do_frame(float filtered_sample); bool locked() const { @@ -118,472 +117,11 @@ struct M17Demodulator void update_values(uint8_t index); - void operator()(const FloatType input); + void operator()(const float input); private: - static const std::array rrc_taps; - BaseFirFilter demod_filter{rrc_taps}; + static const std::array rrc_taps; + BaseFirFilter demod_filter{rrc_taps}; }; -template -void M17Demodulator::update_values(uint8_t index) -{ - correlator.apply([this,index](FloatType t){dev.sample(t);}, index); - dev.update(); - sync_sample_index = index; -} - -template -void M17Demodulator::dcd_on() -{ - // Data carrier newly detected. - dcd_ = true; - sync_count = 0; - missing_sync_count = 0; - - dev.reset(); - framer.reset(); - decoder.reset(); -} - -template -void M17Demodulator::dcd_off() -{ - // Just lost data carrier. - dcd_ = false; - demodState = DemodState::UNLOCKED; - decoder.reset(); - - if (diagnostic_callback) - { - diagnostic_callback(int(dcd_), dev.error(), dev.deviation(), dev.offset(), (int) demodState, - clock_recovery.clock_estimate(), sample_index, sync_sample_index, clock_recovery.sample_index(), -1); - } -} - -template -void M17Demodulator::initialize(const FloatType input) -{ - auto filtered_sample = demod_filter(input); - correlator.sample(filtered_sample); -} - -template -void M17Demodulator::update_dcd() -{ - if (!dcd_ && dcd.dcd()) - { - // fputs("\nAOS\n", stderr); - dcd_on(); - need_clock_reset_ = true; - } - else if (dcd_ && !dcd.dcd()) - { - // fputs("\nLOS\n", stderr); - dcd_off(); - } -} - -template -void M17Demodulator::do_unlocked() -{ - // We expect to find the preamble immediately after DCD. - if (missing_sync_count < 1920) - { - missing_sync_count += 1; - auto sync_index = preamble_sync(correlator); - auto sync_updated = preamble_sync.updated(); - if (sync_updated) - { - sync_count = 0; - missing_sync_count = 0; - need_clock_reset_ = true; - dev.reset(); - update_values(sync_index); - sample_index = sync_index; - demodState = DemodState::LSF_SYNC; - } - return; - } - auto sync_index = lsf_sync(correlator); - auto sync_updated = lsf_sync.updated(); - if (sync_updated) - { - sync_count = 0; - missing_sync_count = 0; - need_clock_reset_ = true; - dev.reset(); - update_values(sync_index); - sample_index = sync_index; - demodState = DemodState::FRAME; - if (sync_updated < 0) - { - sync_word_type = M17FrameDecoder::SyncWordType::STREAM; - } - else - { - sync_word_type = M17FrameDecoder::SyncWordType::LSF; - } - return; - } - sync_index = packet_sync(correlator); - sync_updated = packet_sync.updated(); - if (sync_updated < 0) - { - sync_count = 0; - missing_sync_count = 0; - need_clock_reset_ = true; - dev.reset(); - update_values(sync_index); - sample_index = sync_index; - demodState = DemodState::FRAME; - sync_word_type = M17FrameDecoder::SyncWordType::BERT; - } -} - -/** - * Check for LSF sync word. We only enter the DemodState::LSF_SYNC state - * if a preamble sync has been detected, which also means that sample_index - * has been initialized to a sane value for the baseband. - */ -template -void M17Demodulator::do_lsf_sync() -{ - FloatType sync_triggered = 0.; - FloatType bert_triggered = 0.; - - if (correlator.index() == sample_index) - { - sync_triggered = preamble_sync.triggered(correlator); - if (sync_triggered > 0.1) - { - return; - } - sync_triggered = lsf_sync.triggered(correlator); - bert_triggered = packet_sync.triggered(correlator); - if (bert_triggered < 0) - { - missing_sync_count = 0; - need_clock_update_ = true; - update_values(sample_index); - demodState = DemodState::FRAME; - sync_word_type = M17FrameDecoder::SyncWordType::BERT; - } - else if (std::abs(sync_triggered) > 0.1) - { - missing_sync_count = 0; - need_clock_update_ = true; - update_values(sample_index); - if (sync_triggered > 0) - { - demodState = DemodState::FRAME; - sync_word_type = M17FrameDecoder::SyncWordType::LSF; - } - else - { - demodState = DemodState::FRAME; - sync_word_type = M17FrameDecoder::SyncWordType::STREAM; - } - } - else if (++missing_sync_count > 192) - { - demodState = DemodState::UNLOCKED; - decoder.reset(); - missing_sync_count = 0; - } - else - { - update_values(sample_index); - } - } -} - -/** - * Check for a stream sync word (LSF sync word that is maximally negative). - * We can enter DemodState::STREAM_SYNC from either a valid LSF decode for - * an audio stream, or from a stream frame decode. - * - */ -template -void M17Demodulator::do_stream_sync() -{ - uint8_t sync_index = lsf_sync(correlator); - int8_t sync_updated = lsf_sync.updated(); - sync_count += 1; - if (sync_updated < 0) // Stream sync word - { - missing_sync_count = 0; - if (sync_count > 70) - { - update_values(sync_index); - sync_word_type = M17FrameDecoder::SyncWordType::STREAM; - demodState = DemodState::FRAME; - } - return; - } - else if (sync_count > 87) - { - update_values(sync_index); - missing_sync_count += 1; - if (missing_sync_count < MAX_MISSING_SYNC) - { - sync_word_type = M17FrameDecoder::SyncWordType::STREAM; - demodState = DemodState::FRAME; - } - else - { - // fputs("\n!SYNC\n", stderr); - demodState = DemodState::LSF_SYNC; - } - } -} - -/** - * Check for a packet sync word. DemodState::PACKET_SYNC can only be - * entered from a valid LSF frame decode with the data/packet type bit set. - */ -template -void M17Demodulator::do_packet_sync() -{ - auto sync_index = packet_sync(correlator); - auto sync_updated = packet_sync.updated(); - sync_count += 1; - if (sync_count > 70 && sync_updated) - { - missing_sync_count = 0; - update_values(sync_index); - sync_word_type = M17FrameDecoder::SyncWordType::PACKET; - demodState = DemodState::FRAME; - } - else if (sync_count > 87) - { - missing_sync_count += 1; - if (missing_sync_count < MAX_MISSING_SYNC) - { - sync_word_type = M17FrameDecoder::SyncWordType::PACKET; - demodState = DemodState::FRAME; - } - else - { - demodState = DemodState::UNLOCKED; - decoder.reset(); - } - } -} - -/** - * Check for a bert sync word. - */ -template -void M17Demodulator::do_bert_sync() -{ - auto sync_index = packet_sync(correlator); - auto sync_updated = packet_sync.updated(); - sync_count += 1; - if (sync_count > 70 && sync_updated < 0) - { - missing_sync_count = 0; - update_values(sync_index); - sync_word_type = M17FrameDecoder::SyncWordType::BERT; - demodState = DemodState::FRAME; - } - else if (sync_count > 87) - { - missing_sync_count += 1; - if (missing_sync_count < MAX_MISSING_SYNC) - { - sync_word_type = M17FrameDecoder::SyncWordType::BERT; - demodState = DemodState::FRAME; - } - else - { - demodState = DemodState::UNLOCKED; - decoder.reset(); - } - } -} - - -template -void M17Demodulator::do_frame(FloatType filtered_sample) -{ - if (correlator.index() != sample_index) return; - - static uint8_t cost_count = 0; - - auto sample = filtered_sample - dev.offset(); - sample *= dev.idev(); - sample *= polarity; - - auto n = llr(sample); - int8_t* tmp; - auto len = framer(n, &tmp); - if (len != 0) - { - need_clock_update_ = true; - - M17FrameDecoder::input_buffer_t buffer; - std::copy(tmp, tmp + len, buffer.begin()); - auto valid = decoder(sync_word_type, buffer, viterbi_cost); - - cost_count = viterbi_cost > 90 ? cost_count + 1 : 0; - cost_count = viterbi_cost > 100 ? cost_count + 1 : cost_count; - cost_count = viterbi_cost > 110 ? cost_count + 1 : cost_count; - - if (cost_count > 75) - { - cost_count = 0; - demodState = DemodState::UNLOCKED; - decoder.reset(); - // fputs("\nCOST\n", stderr); - return; - } - - switch (decoder.state()) - { - case M17FrameDecoder::State::STREAM: - demodState = DemodState::STREAM_SYNC; - break; - case M17FrameDecoder::State::LSF: - // If state == LSF, we need to recover LSF from LICH. - demodState = DemodState::STREAM_SYNC; - break; - case M17FrameDecoder::State::BERT: - demodState = DemodState::BERT_SYNC; - break; - default: - demodState = DemodState::PACKET_SYNC; - break; - } - - sync_count = 0; - - switch (valid) - { - case M17FrameDecoder::DecodeResult::FAIL: - break; - case M17FrameDecoder::DecodeResult::EOS: - demodState = DemodState::LSF_SYNC; - break; - case M17FrameDecoder::DecodeResult::OK: - break; - case M17FrameDecoder::DecodeResult::INCOMPLETE: - break; - case M17FrameDecoder::DecodeResult::PACKET_INCOMPLETE: - break; - } - } -} - -template -void M17Demodulator::operator()(const FloatType input) -{ - static int16_t initializing = 1920; - - count_++; - - dcd(input); - - // We need to pump a few ms of data through on startup to initialize - // the demodulator. - if (initializing) [[unlikely]] - { - --initializing; - initialize(input); - count_ = 0; - return; - } - - if (!dcd_) - { - if (count_ % (BLOCK_SIZE * 2) == 0) - { - update_dcd(); - dcd.update(); - - if (diagnostic_callback) - { - diagnostic_callback(int(dcd_), dev.error(), dev.deviation(), dev.offset(), (int) demodState, - clock_recovery.clock_estimate(), sample_index, sync_sample_index, clock_recovery.sample_index(), viterbi_cost); - } - - count_ = 0; - } - - return; - } - - auto filtered_sample = demod_filter(input); - - correlator.sample(filtered_sample); - - if (correlator.index() == 0) - { - if (need_clock_reset_) - { - clock_recovery.reset(); - need_clock_reset_ = false; - } - else if (need_clock_update_) // must avoid update immediately after reset. - { - clock_recovery.update(); - uint8_t clock_index = clock_recovery.sample_index(); - uint8_t clock_diff = std::abs(sample_index - clock_index); - uint8_t sync_diff = std::abs(sample_index - sync_sample_index); - bool clock_diff_ok = clock_diff <= 1 || clock_diff == 9; - bool sync_diff_ok = sync_diff <= 1 || sync_diff == 9; - if (clock_diff_ok) sample_index = clock_index; - else if (sync_diff_ok) sample_index = sync_sample_index; - // else unchanged. - need_clock_update_ = false; - } - } - - clock_recovery(filtered_sample); - - if (demodState != DemodState::UNLOCKED && correlator.index() == sample_index) - { - dev.sample(filtered_sample); - } - - switch (demodState) - { - case DemodState::UNLOCKED: - // In this state, the sample_index is unknown. We need to find - // a sync word to find the proper sample_index. We only leave - // this state if we believe that we have a valid sample_index. - do_unlocked(); - break; - case DemodState::LSF_SYNC: - do_lsf_sync(); - break; - case DemodState::STREAM_SYNC: - do_stream_sync(); - break; - case DemodState::PACKET_SYNC: - do_packet_sync(); - break; - case DemodState::BERT_SYNC: - do_bert_sync(); - break; - case DemodState::FRAME: - do_frame(filtered_sample); - break; - } - - if (count_ % (BLOCK_SIZE * 5) == 0) - { - update_dcd(); - count_ = 0; - - if (diagnostic_callback) - { - diagnostic_callback(int(dcd_), dev.error(), dev.deviation(), dev.offset(), (int) demodState, - clock_recovery.clock_estimate(), sample_index, sync_sample_index, clock_recovery.sample_index(), viterbi_cost); - } - - dcd.update(); - } -} - } // mobilinkd diff --git a/plugins/channelrx/demodm17/m17/M17Modulator.h b/plugins/channelrx/demodm17/m17/M17Modulator.h index a8d8ee851..5aa0b1df9 100644 --- a/plugins/channelrx/demodm17/m17/M17Modulator.h +++ b/plugins/channelrx/demodm17/m17/M17Modulator.h @@ -595,7 +595,7 @@ public: static baseband_t symbols_to_baseband(const symbols_t& symbols) { // Generated using scikit-commpy - static const auto rrc_taps = std::array{ + static const auto rrc_taps = std::array{ -0.009265784007800534, -0.006136551625729697, -0.001125978562075172, 0.004891777252042491, 0.01071805138282269, 0.01505751553351295, 0.01679337935001369, 0.015256245142156299, 0.01042830577908502, 0.003031522725559901, -0.0055333532968188165, -0.013403099825723372, @@ -617,7 +617,7 @@ public: 0.01679337935001369, 0.01505751553351295, 0.01071805138282269, 0.004891777252042491, -0.001125978562075172, -0.006136551625729697, -0.009265784007800534 }; - static BaseFirFilter::value> rrc = makeFirFilter(rrc_taps); + static BaseFirFilter::value> rrc = makeFirFilter(rrc_taps); std::array baseband; baseband.fill(0); diff --git a/plugins/channelrx/demodm17/m17/PhaseEstimator.h b/plugins/channelrx/demodm17/m17/PhaseEstimator.h index 9e50a05e7..d69df0371 100644 --- a/plugins/channelrx/demodm17/m17/PhaseEstimator.h +++ b/plugins/channelrx/demodm17/m17/PhaseEstimator.h @@ -17,33 +17,32 @@ namespace mobilinkd * these errors have not affected the performance of clock * recovery. */ -template struct PhaseEstimator { - using float_type = FloatType; - using samples_t = std::array; // 3 samples in length + using float_type = float; + using samples_t = std::array; // 3 samples in length float_type dx_; - PhaseEstimator(FloatType sample_rate, FloatType symbol_rate) + PhaseEstimator(float sample_rate, float symbol_rate) : dx_(2.0 * symbol_rate / sample_rate) {} /** * This performs a rolling estimate of the phase. - * + * * @param samples are three samples centered around the current sample point * (t-1, t, t+1). */ float_type operator()(const samples_t& samples) { assert(dx_ > 0.0); - - auto ratio = ((samples.at(2) - samples.at(0)) / 3.0) / dx_; + + float ratio = ((samples.at(2) - samples.at(0)) / 3.0) / dx_; // Clamp +/-5. - ratio = std::min(FloatType(5.0), ratio); - ratio = std::max(FloatType(-5.0), ratio); - + ratio = std::min(float(5.0), ratio); + ratio = std::max(float(-5.0), ratio); + return ratio; } }; diff --git a/plugins/channelrx/demodm17/m17/SlidingDFT.h b/plugins/channelrx/demodm17/m17/SlidingDFT.h index 294d88f2b..76c28a050 100644 --- a/plugins/channelrx/demodm17/m17/SlidingDFT.h +++ b/plugins/channelrx/demodm17/m17/SlidingDFT.h @@ -17,18 +17,18 @@ namespace mobilinkd * Eric Jacobsen, 2015-04-23 * https://www.dsprelated.com/showarticle/776.php */ -template +template class SlidingDFT { - using ComplexType = std::complex; + using ComplexType = std::complex; static constexpr size_t N = SampleRate / Accuracy; - static constexpr FloatType pi2 = M_PI * 2.0; - static constexpr FloatType kth = FloatType(Frequency) / FloatType(SampleRate); + static constexpr float pi2 = M_PI * 2.0; + static constexpr float kth = float(Frequency) / float(SampleRate); // We'd like this to be static constexpr, but std::exp is not a constexpr. const ComplexType coeff_; - std::array samples_; + std::array samples_; ComplexType result_{0,0}; size_t index_ = 0; size_t prev_index_ = N - 1; @@ -40,15 +40,15 @@ public: coeff_ = std::exp(-ComplexType{0, 1} * pi2 * kth); } - ComplexType operator()(FloatType sample) + ComplexType operator()(float sample) { auto index = index_; index_ += 1; if (index_ == N) index_ = 0; - FloatType delta = sample - samples_[index]; + float delta = sample - samples_[index]; ComplexType result = (result_ + delta) * coeff_; - result_ = result * FloatType(0.999999999999999); + result_ = result * float(0.999999999999999); samples_[index] = sample; prev_index_ = index; return result; @@ -62,21 +62,21 @@ public: * Eric Jacobsen, 2015-04-23 * https://www.dsprelated.com/showarticle/776.php * - * @tparam FloatType is the floating point type to use. + * @tparam float is the floating point type to use. * @tparam SampleRate is the sample rate of the incoming data. * @tparam N is the length of the DFT. Frequency resolution is SampleRate / N. * @tparam K is the number of frequencies whose DFT will be calculated. */ -template +template class NSlidingDFT { - using ComplexType = std::complex; + using ComplexType = std::complex; - static constexpr FloatType pi2 = M_PI * 2.0; + static constexpr float pi2 = M_PI * 2.0; // We'd like this to be static constexpr, but std::exp is not a constexpr. const std::array coeff_; - std::array samples_; + std::array samples_; std::array result_{0,0}; size_t index_ = 0; size_t prev_index_ = N - 1; @@ -88,7 +88,7 @@ class NSlidingDFT std::array result; for (size_t i = 0; i != K; ++i) { - FloatType k = FloatType(frequencies[i]) / FloatType(SampleRate); + float k = float(frequencies[i]) / float(SampleRate); result[i] = std::exp(-j * pi2 * k); } return result; @@ -115,13 +115,13 @@ public: * constructor. The result is only valid after at least N samples * have been cycled in. */ - result_type operator()(FloatType sample) + result_type operator()(float sample) { auto index = index_; index_ += 1; if (index_ == N) index_ = 0; - FloatType delta = sample - samples_[index]; + float delta = sample - samples_[index]; for (size_t i = 0; i != K; ++i) { diff --git a/plugins/channelrx/demodm17/m17/SymbolEvm.h b/plugins/channelrx/demodm17/m17/SymbolEvm.h index 78b87e04a..1dc9692af 100644 --- a/plugins/channelrx/demodm17/m17/SymbolEvm.h +++ b/plugins/channelrx/demodm17/m17/SymbolEvm.h @@ -13,23 +13,23 @@ namespace mobilinkd { -template +template struct SymbolEvm { - using filter_type = BaseIirFilter; + using filter_type = BaseIirFilter; using symbol_t = int; - using result_type = std::tuple; + using result_type = std::tuple; filter_type filter_; - FloatType erasure_limit_; - FloatType evm_ = 0.0; + float erasure_limit_; + float evm_ = 0.0; - SymbolEvm(filter_type&& filter, FloatType erasure_limit = 0.0) : + SymbolEvm(filter_type&& filter, float erasure_limit = 0.0) : filter_(std::forward(filter)), erasure_limit_(erasure_limit) {} - FloatType evm() const { return evm_; } + float evm() const { return evm_; } /** * Decode a normalized sample into a symbol. Symbols @@ -37,10 +37,10 @@ struct SymbolEvm * is set, symbols outside this limit are 'erased' and * returned as 0. */ - result_type operator()(FloatType sample) + result_type operator()(float sample) { symbol_t symbol; - FloatType evm; + float evm; sample = std::min(3.0, std::max(-3.0, sample)); @@ -73,13 +73,13 @@ struct SymbolEvm } }; -template -SymbolEvm makeSymbolEvm( - BaseIirFilter&& filter, - FloatType erasure_limit = 0.0f +template +SymbolEvm makeSymbolEvm( + BaseIirFilter&& filter, + float erasure_limit = 0.0f ) { - return std::move(SymbolEvm(std::move(filter), erasure_limit)); + return std::move(SymbolEvm(std::move(filter), erasure_limit)); } } // mobilinkd diff --git a/plugins/channelrx/demodm17/m17/Util.h b/plugins/channelrx/demodm17/m17/Util.h index 600be204c..0fb98ac79 100644 --- a/plugins/channelrx/demodm17/m17/Util.h +++ b/plugins/channelrx/demodm17/m17/Util.h @@ -59,19 +59,19 @@ constexpr size_t llr_size() return llr_limit() * 6 + 1; } -template -constexpr std::array>, llr_size()> make_llr_map() +template +constexpr std::array>, llr_size()> make_llr_map() { constexpr size_t size = llr_size(); - std::array>, size> result; + std::array>, size> result; constexpr int8_t limit = llr_limit(); - constexpr FloatType inc = 1.0 / FloatType(limit); + constexpr float inc = 1.0 / float(limit); int8_t i = limit; int8_t j = limit; // Output must be ordered by k, ascending. - FloatType k = -3.0 + inc; + float k = -3.0 + inc; for (size_t index = 0; index != size; ++index) { auto& a = result[index]; @@ -124,17 +124,17 @@ inline int from_4fsk(int symbol) } } -template -auto llr(FloatType sample) +template +auto llr(float sample) { - static auto symbol_map = detail::make_llr_map(); - static constexpr FloatType MAX_VALUE = 3.0; - static constexpr FloatType MIN_VALUE = -3.0; + static auto symbol_map = detail::make_llr_map(); + static constexpr float MAX_VALUE = 3.0; + static constexpr float MIN_VALUE = -3.0; - FloatType s = std::min(MAX_VALUE, std::max(MIN_VALUE, sample)); + float s = std::min(MAX_VALUE, std::max(MIN_VALUE, sample)); auto it = std::lower_bound(symbol_map.begin(), symbol_map.end(), s, - [](std::tuple> const& e, FloatType s){ + [](std::tuple> const& e, float s){ return std::get<0>(e) < s; }); diff --git a/plugins/channelrx/demodm17/m17demodprocessor.h b/plugins/channelrx/demodm17/m17demodprocessor.h index bba5a86f8..90a4e7f1d 100644 --- a/plugins/channelrx/demodm17/m17demodprocessor.h +++ b/plugins/channelrx/demodm17/m17demodprocessor.h @@ -96,7 +96,7 @@ private: bool m_noiseBlanker; struct CODEC2 *m_codec2; static M17DemodProcessor *m_this; - mobilinkd::M17Demodulator m_demod; + mobilinkd::M17Demodulator m_demod; AudioFifo *m_audioFifo; bool m_audioMute; AudioVector m_audioBuffer;