From 22cb16f831e4a504d16b8b75c1ef5948fdefced2 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 10 Jun 2020 16:09:45 +0100 Subject: [PATCH] Add more frame processing. --- AX25Demodulator.cpp | 120 ++++++++++++++++++++++++++++++++++++++++++-- AX25Demodulator.h | 18 +++++-- 2 files changed, 129 insertions(+), 9 deletions(-) diff --git a/AX25Demodulator.cpp b/AX25Demodulator.cpp index e824cc1..f1bcddf 100644 --- a/AX25Demodulator.cpp +++ b/AX25Demodulator.cpp @@ -20,22 +20,102 @@ #include "Globals.h" #include "AX25Demodulator.h" -const float DELAY = 0.000448F; -const float SAMPLE_RATE = 24000.0F; +const float32_t DELAY = 0.000448F; +const float32_t SAMPLE_RATE = 24000.0F; +const float32_t SYMBOL_RATE = 1200.0F; const uint16_t DELAY_LEN = uint16_t((DELAY / (1.0F / SAMPLE_RATE)) + 0.5F); -CAX25Demodulator::CAX25Demodulator() : +const float32_t SAMPLES_PER_SYMBOL = SAMPLE_RATE / SYMBOL_RATE; +const float32_t PLL_LIMIT = SAMPLES_PER_SYMBOL / 2.0F; + +// XXX This is for the wrong sample rate +const uint32_t LPF_FILTER_LEN = 96; + +q15_t lpfCoeffs[] = { + 0, 1, 3, 5, 8, 11, 14, 17, 19, 20, 18, 14, + 7, -2, -16, -33, -53, -76, -101, -126, -151, -174, -194, -208, + -215, -212, -199, -173, -133, -79, -10, 74, 173, 287, 413, 549, + 693, 842, 993, 1142, 1287, 1423, 1547, 1656, 1747, 1817, 1865, 1889, + 1889, 1865, 1817, 1747, 1656, 1547, 1423, 1287, 1142, 993, 842, 693, + 549, 413, 287, 173, 74, -10, -79, -133, -173, -199, -212, -215, + -208, -194, -174, -151, -126, -101, -76, -53, -33, -16, -2, 7, + 14, 18, 20, 19, 17, 14, 11, 8, 5, 3, 1, 0, +}; + +// 64 Hz loop filter. +// scipy.signal: +// loop_coeffs = firwin(9, [64.0/(1200/2)], width = None, +// pass_zero = True, scale = True, window='hann') +// +float32_t pllLoopCoeffs[] = {3.196252e-02F, 1.204223e-01F, 2.176819e-01F, 2.598666e-01F, 2.176819e-01F, 1.204223e-01F, 3.196252e-02F}; + +CAX25Demodulator::CAX25Demodulator(float32_t* coeffs, uint16_t length) : +m_audioFilter(), +m_audioState(), +m_lpfFilter(), +m_lpfState(), m_delayLine(NULL), m_delayPos(0U), -m_nrziState(false) +m_nrziState(false), +m_pllFilter(), +m_pllState(), +m_pllLast(false), +m_pllBits(1U), +m_pllCount(0.0F) { m_delayLine = new bool[2U * DELAY_LEN]; + + m_audioFilter.numTaps = length; + m_audioFilter.pState = m_audioState; + m_audioFilter.pCoeffs = coeffs; + + m_lpfFilter.numTaps = LPF_FILTER_LEN; + m_lpfFilter.pState = m_lpfState; + m_lpfFilter.pCoeffs = lpfCoeffs; + + m_pllFilter.numTaps = 7U; + m_pllFilter.pState = m_pllState; + m_pllFilter.pCoeffs = pllLoopCoeffs; } bool CAX25Demodulator::process(const q15_t* samples, uint8_t length, CAX25Frame& frame) { - return false; + bool result = false; + + float32_t input[RX_BLOCK_SIZE]; + for (size_t i = 0; i < length; i++) + input[i] = float(samples[i]); + + float32_t fa[RX_BLOCK_SIZE]; + ::arm_fir_f32(&m_audioFilter, input, fa, RX_BLOCK_SIZE); + + int16_t buffer[RX_BLOCK_SIZE]; + for (uint8_t i = 0; i < length; i++) { + int16_t sample = int16_t(fa[i]); + bool level = (sample >= 0); + bool delayed = delay(level); + buffer[i] = (int16_t(level ^ delayed) << 1) - 1; + } + + q15_t fc[RX_BLOCK_SIZE]; + ::arm_fir_fast_q15(&m_lpfFilter, buffer, fc, RX_BLOCK_SIZE); + + for (uint8_t i = 0; i < length; i++) { + bool bit = fc[i] >= 0; + bool sample = PLL(bit); + + if (sample) { + // We will only ever get one frame because there are + // not enough bits in a block for more than one. + if (result) + hdlc_decoder_(NRZI(bit), true); + else + result = hdlc_decoder_(NRZI(bit), true); + } + } + + return result; } bool CAX25Demodulator::delay(bool b) @@ -59,3 +139,33 @@ bool CAX25Demodulator::NRZI(bool b) return result; } +bool CAX25Demodulator::PLL(bool input) +{ + bool sample = false; + + if (input != m_pllLast or m_pllBits > 16U) { + // Record transition. + m_pllLast = input; + + if (m_pllCount > PLL_LIMIT) + m_pllCount -= SAMPLES_PER_SYMBOL; + + float32_t offset = m_pllCount / float32_t(m_pllBits); + float32_t jitter; + ::arm_fir_f32(&m_pllFilter, &offset, &jitter, 1U); + + m_pllCount -= jitter / 2.0F; + m_pllBits = 1U; + } else { + if (m_pllCount > PLL_LIMIT) { + sample = true; + m_pllCount -= SAMPLES_PER_SYMBOL; + m_pllBits++; + } + } + + m_pllCount += 1.0F; + + return sample; +} + diff --git a/AX25Demodulator.h b/AX25Demodulator.h index 550ec8b..c897c24 100644 --- a/AX25Demodulator.h +++ b/AX25Demodulator.h @@ -26,17 +26,27 @@ class CAX25Demodulator { public: - CAX25Demodulator(); + CAX25Demodulator(float32_t* coeffs, uint16_t length); bool process(const q15_t* samples, uint8_t length, CAX25Frame& frame); private: - bool* m_delayLine; - uint16_t m_delayPos; - bool m_nrziState; + arm_fir_instance_f32 m_audioFilter; + float32_t m_audioState[20U]; + arm_fir_instance_q15 m_lpfFilter; + q15_t m_lpfState[120U]; // NoTaps + BlockSize - 1, 96 + 20 - 1 plus some spare + bool* m_delayLine; + uint16_t m_delayPos; + bool m_nrziState; + arm_fir_instance_f32 m_pllFilter; + float32_t m_pllState[7U]; + bool m_pllLast; + uint8_t m_pllBits; + float32_t m_pllCount; bool delay(bool b); bool NRZI(bool b); + bool PLL(bool b); }; #endif