From c4483edff052922f04064140c2f5832477a268c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Izzo?= Date: Thu, 21 Apr 2022 08:53:21 +0200 Subject: [PATCH] Improve quantization strategy Now quantization is performed by averaging syncword samples and dividing the resulting threshold by a constant. TG-81 --- .../include/protocols/M17/M17Demodulator.h | 4 +- openrtx/src/protocols/M17/M17Demodulator.cpp | 94 +++++++++++-------- scripts/plot_m17_demod_csv.py | 2 +- 3 files changed, 58 insertions(+), 42 deletions(-) diff --git a/openrtx/include/protocols/M17/M17Demodulator.h b/openrtx/include/protocols/M17/M17Demodulator.h index 38229a6d..d98be7f1 100644 --- a/openrtx/include/protocols/M17/M17Demodulator.h +++ b/openrtx/include/protocols/M17/M17Demodulator.h @@ -125,7 +125,7 @@ private: static constexpr float CONV_STATS_ALPHA = 0.001f; static constexpr float CONV_THRESHOLD_FACTOR = 3.40; - static constexpr int16_t QNT_SMA_WINDOW = 40; + static constexpr int16_t QNT_SMA_WINDOW = 8; /** * M17 syncwords; @@ -142,7 +142,7 @@ private: int16_t *baseband_buffer; ///< Buffer for baseband audio handling. dataBlock_t baseband; ///< Half buffer, free to be processed. uint16_t *rawFrame; ///< Analog values to be quantized. - uint16_t frameIndex; ///< Index for filling the raw frame. + uint16_t frame_index; ///< Index for filling the raw frame. frame_t *activeFrame; ///< Half frame, in demodulation. frame_t *idleFrame; ///< Half frame, free to be processed. bool isLSF; ///< Indicates that we demodualated an LSF. diff --git a/openrtx/src/protocols/M17/M17Demodulator.cpp b/openrtx/src/protocols/M17/M17Demodulator.cpp index f10e8c56..910d32a5 100644 --- a/openrtx/src/protocols/M17/M17Demodulator.cpp +++ b/openrtx/src/protocols/M17/M17Demodulator.cpp @@ -60,7 +60,7 @@ void M17Demodulator::init() activeFrame = new frame_t; rawFrame = new uint16_t[M17_FRAME_SYMBOLS]; idleFrame = new frame_t; - frameIndex = 0; + frame_index = 0; phase = 0; locked = false; newFrame = false; @@ -90,6 +90,7 @@ void M17Demodulator::startBasebandSampling() M17_RX_SAMPLE_RATE); // Clean start of the demodulation statistics resetCorrelationStats(); + resetQuantizationStats(); // DC removal filter reset dsp_resetFilterState(&dsp_state); } @@ -135,25 +136,39 @@ void M17Demodulator::updateQuantizationStats(int32_t offset) sample = baseband.data[offset]; if (sample > 0) { - // If the FIFO is not full just push a new sample - if (qnt_pos_fifo.size() >= QNT_SMA_WINDOW) + qnt_pos_fifo.push_front(sample); + // FIFO not full, compute traditional average + if (qnt_pos_fifo.size() <= QNT_SMA_WINDOW) + { + int32_t acc = 0; + for(auto e : qnt_pos_fifo) + acc += e; + qnt_pos_avg = acc / static_cast(qnt_pos_fifo.size()); + } + else { qnt_pos_avg += 1 / static_cast(QNT_SMA_WINDOW) * (sample - qnt_pos_fifo.back()); qnt_pos_fifo.pop_back(); } - qnt_pos_fifo.push_front(sample); } else { - // If the FIFO is not full just push a new sample - if (qnt_neg_fifo.size() >= QNT_SMA_WINDOW) + qnt_neg_fifo.push_front(sample); + // FIFO not full, compute traditional average + if (qnt_neg_fifo.size() <= QNT_SMA_WINDOW) + { + int32_t acc = 0; + for(auto e : qnt_neg_fifo) + acc += e; + qnt_neg_avg = acc / static_cast(qnt_neg_fifo.size()); + } + else { qnt_neg_avg += 1 / static_cast(QNT_SMA_WINDOW) * (sample - qnt_neg_fifo.back()); qnt_neg_fifo.pop_back(); } - qnt_neg_fifo.push_front(sample); } } @@ -194,7 +209,6 @@ sync_t M17Demodulator::nextFrameSync(int32_t offset) // If we are not locked search for a syncword int32_t conv = convolution(i, stream_syncword, M17_SYNCWORD_SYMBOLS); updateCorrelationStats(conv); - updateQuantizationStats(i); #ifdef PLATFORM_LINUX int16_t sample = 0; @@ -239,9 +253,9 @@ int8_t M17Demodulator::quantize(int32_t offset) sample = basebandBridge[M17_BRIDGE_SIZE + offset]; else // Otherwise use regular data buffer sample = baseband.data[offset]; - if (sample > static_cast< int16_t >(qnt_pos_avg * 0.067)) + if (sample > static_cast< int16_t >(qnt_pos_avg / 1.7)) return +3; - else if (sample < static_cast< int16_t >(qnt_neg_avg * 0.067)) + else if (sample < static_cast< int16_t >(qnt_neg_avg / 1.7)) return -3; else if (sample > 0) return +1; @@ -312,7 +326,7 @@ bool M17Demodulator::update() isLSF = syncword.lsf; offset = syncword.index + 4; phase = 0; - frameIndex = 0; + frame_index = 0; } } // While we are locked, demodulate available samples @@ -322,8 +336,9 @@ bool M17Demodulator::update() int32_t symbol_index = offset + phase + (M17_SAMPLES_PER_SYMBOL * decoded_syms); - - updateQuantizationStats(symbol_index); + // Update quantization stats only on syncwords + if (frame_index < M17_SYNCWORD_SYMBOLS) + updateQuantizationStats(symbol_index); int8_t symbol = quantize(symbol_index); #ifdef PLATFORM_LINUX @@ -333,63 +348,64 @@ bool M17Demodulator::update() fprintf(csv_log, "%" PRId16 ",%d,%f,%d,%" PRId16 ",%f,%f,%d,%d\n", 0,0,0.0,symbol_index - 4 + i, baseband.data[symbol_index - 4 + i], - qnt_pos_avg, - qnt_neg_avg, + qnt_pos_avg / 1.7, + qnt_neg_avg / 1.7, 0, - frameIndex); + frame_index); } fprintf(csv_log, "%" PRId16 ",%d,%f,%d,%" PRId16 ",%f,%f,%d,%d\n", 0,0,0.0,symbol_index, baseband.data[symbol_index], - qnt_pos_avg, - qnt_neg_avg, + qnt_pos_avg / 1.7, + qnt_neg_avg / 1.7, symbol * 666, - frameIndex); + frame_index); for (int i = 0; i < 5; i++) { if ((symbol_index + i + 1) < static_cast (baseband.len)) fprintf(csv_log, "%" PRId16 ",%d,%f,%d,%" PRId16 ",%f,%f,%d,%d\n", 0,0,0.0,symbol_index + i + 1, baseband.data[symbol_index + i + 1], - qnt_pos_avg, - qnt_neg_avg, + qnt_pos_avg / 1.7, + qnt_neg_avg / 1.7, 0, - frameIndex); + frame_index); } fflush(csv_log); #endif - setSymbol(*activeFrame, frameIndex, symbol); + setSymbol(*activeFrame, frame_index, symbol); decoded_syms++; - frameIndex++; + frame_index++; // If the frame buffer is full switch active and idle frame - if (frameIndex == M17_FRAME_SYMBOLS) + if (frame_index == M17_FRAME_SYMBOLS) { std::swap(activeFrame, idleFrame); - frameIndex = 0; + frame_index = 0; newFrame = true; } - // If syncword is not valid, lock is lost, accept 2 bit errors - uint8_t hammingSync = hammingDistance((*activeFrame)[0], - stream_syncword_bytes[0]) - + hammingDistance((*activeFrame)[1], - stream_syncword_bytes[1]); - - uint8_t hammingLsf = hammingDistance((*activeFrame)[0], - lsf_syncword_bytes[0]) - + hammingDistance((*activeFrame)[1], - lsf_syncword_bytes[1]); - - if (frameIndex == M17_SYNCWORD_SYMBOLS) + if (frame_index == M17_SYNCWORD_SYMBOLS) { + + // If syncword is not valid, lock is lost, accept 2 bit errors + uint8_t hammingSync = hammingDistance((*activeFrame)[0], + stream_syncword_bytes[0]) + + hammingDistance((*activeFrame)[1], + stream_syncword_bytes[1]); + + uint8_t hammingLsf = hammingDistance((*activeFrame)[0], + lsf_syncword_bytes[0]) + + hammingDistance((*activeFrame)[1], + lsf_syncword_bytes[1]); + // Too many errors in the syncword, lock is lost if ((hammingSync > 1) && (hammingLsf > 1)) { locked = false; std::swap(activeFrame, idleFrame); - frameIndex = 0; + frame_index = 0; newFrame = true; #ifdef PLATFORM_MOD17 diff --git a/scripts/plot_m17_demod_csv.py b/scripts/plot_m17_demod_csv.py index 89d0e9f7..8f60e350 100755 --- a/scripts/plot_m17_demod_csv.py +++ b/scripts/plot_m17_demod_csv.py @@ -15,6 +15,6 @@ plt.plot(df.index, df.Offset) plt.plot(df.index, df.Sample) plt.plot(df.index, df.Max) plt.plot(df.index, df.Min) -#plt.plot(df.index, df.Symbol) +plt.plot(df.index, df.Symbol) plt.plot(df.index, df.I) plt.show()