From ea6a2279cbd3e318aa48af47e488e87b4e30e3a2 Mon Sep 17 00:00:00 2001 From: Jens Ahrensfeld Date: Thu, 16 Jun 2022 15:50:47 +0000 Subject: [PATCH] - moved Interpolator out of big loop into tight processing loop git-svn-id: http://moon:8086/svn/software/trunk/projects/mpsk_rx_gui@1000 b431acfa-c32f-4a4a-93f1-934dc6c82436 --- Source/Receiver.cpp | 430 +++++++++++++++++++++----------------------- Source/Receiver.hpp | 61 ++++++- 2 files changed, 261 insertions(+), 230 deletions(-) diff --git a/Source/Receiver.cpp b/Source/Receiver.cpp index 0a4003b..9db09bf 100644 --- a/Source/Receiver.cpp +++ b/Source/Receiver.cpp @@ -138,6 +138,7 @@ void Receiver::initDefaultParams() LeadLagSetCoeff(&m_params.str_loopfilter_coeff[0], STR_GAIN_LEAD_AQU, STR_GAIN_LAG_AQU); LeadLagSetCoeff(&m_params.str_loopfilter_coeff[1], STR_GAIN_LEAD_TRK, STR_GAIN_LAG_TRK); m_params.str_loopfilter_coeff_index = 0; + m_timingGenerator.loopFilterSetup(&m_loop_filter_str, &m_params.str_loopfilter_coeff[m_params.str_loopfilter_coeff_index]); m_params.cprState = cpr_state_acquisition; m_params.cpr_mode = cpr_mode_enabled; @@ -450,241 +451,218 @@ void Receiver::processBaseband(CVec const &iq, uint32_t len) SlidingVarProcess(&m_statistics.Q_DDC, iq[n].imag()); } #endif - - for (n = 0; n < len; n++) + + // Matched filtering and symbol timing recovery + // @2 x symbolrate + + Processor::Buffer *buf_ip = nullptr; + if (RCF_TYPE == RCF_TYPE_POLYPHASE_FARROW) { + // Farrow interpolation + m_farrow.feed(iq, len); + m_farrow.process(m_timingGenerator); + buf_ip = &m_farrow.getOutputBuffer(); + } + if (RCF_TYPE == RCF_TYPE_POLYPHASE_DISCRETE) + { + // Polyphase interpolation +// m_polyPhase.feed(iq, len); +// m_polyPhase.process(m_timingGenerator); +// buf_ip = &m_polyPhase.getOutputBuffer(); + } - if (RCF_TYPE == RCF_TYPE_POLYPHASE_DISCRETE) - { - // Matched filtering and interpolation - m_polyPhase.feed(iq[n], 1); - } + if (buf_ip == nullptr) + { + return; + } - if (RCF_TYPE == RCF_TYPE_POLYPHASE_FARROW) - { - // Matched filtering and interpolation - m_farrow.feed(&iq[n], 1); - } + // Further BB processing + // @2 x symbolrate + while(buf_ip->len()) + { + IQ_mf = toCpx(buf_ip->readAt(0)); - do - { - // Timing corrector - TimingCorrectorProcess(&m_timing_corrector, m_Vc); + // AGC Blind process samples + IQ_agc = CpxScaleRealS(IQ_mf, AGC_GetWeight(&agcBlind)); -#ifndef _DEBUG -// TracePrint(&m_trace, "%d\t%8f\t%.8f\t%.8f\n", TimingCorrectorIsSkip(&m_timing_corrector)-TimingCorrectorIsStuff(&m_timing_corrector), TimingCorrectorGetMu(&m_timing_corrector), TimingCorrectorGetMu(&m_timing_corrector), (powerDB(m_pSymMapper->Eb, 2) - powerDB((SlidingVarGet(&m_statistics.sym_err_mag) + SlidingVarGet(&m_statistics.sym_err_phi)), 1))); -#endif + // Carrier Derotator + IQ_cpr = toCpx(m_nco_cpr.mixComplexS(toComplexScalar(IQ_agc), ComplexScalar(1,1))); + m_nco_cpr.process(m_dOmega_vco, m_params.CPR_phase); - if (RCF_TYPE == RCF_TYPE_POLYPHASE_DISCRETE) - { - ComplexScalar __IQ_mf = m_polyPhase.process(TimingCorrectorGetMu(&m_timing_corrector), !TimingCorrectorIsStuff(&m_timing_corrector)); - IQ_mf = toCpx(__IQ_mf); - } - - if (RCF_TYPE == RCF_TYPE_POLYPHASE_FARROW) - { - ComplexScalar __IQ_mf = m_farrow.process(TimingCorrectorGetMu(&m_timing_corrector), !TimingCorrectorIsStuff(&m_timing_corrector)); - IQ_mf = toCpx(__IQ_mf); - } - - // AGC Blind process samples - IQ_agc = CpxScaleRealS(IQ_mf, AGC_GetWeight(&agcBlind)); - - // Symbol timing recovery - Vd = STRGardnerProcess(&m_SymbolTimingRevovery, IQ_agc); - - // Carrier Derotator - IQ_cpr = toCpx(m_nco_cpr.mixComplexS(toComplexScalar(IQ_agc), ComplexScalar(1,1))); - m_nco_cpr.process(m_dOmega_vco, m_params.CPR_phase); - - if (TimingCorrectorIsSkip(&m_timing_corrector)) - { - if (!TimingCorrectorIsStuff(&m_timing_corrector)) - break; - } - - IQ_eq_in = IQ_cpr; - - // Operate at symbol clock - // @1 x symbolrate - if (ClockIsTick(&m_symClock, 1)) - { - // AGC Blind Training - if (m_params.agc_mode != agc_mode_disabled) - { - if (m_params.agcMu_index == agc_state_acquisition) - { -// AGC_Process(&agcBlind, CpxMagS(IQ_eq_in), m_params.agcMu[m_params.agcMu_index], 1.0); - e_agc = m_pSymMapper->R2_cma - CpxMagS(m_trackers[0]); - AGC_Train(&agcBlind, e_agc, m_params.agcMu[m_params.agcMu_index]); - } - if (m_params.agcMu_index == agc_state_track) - { - e_agc = m_pSymMapper->R2_cma - CpxMagS(m_trackers[0]); -// e_agc = CpxMagS(IQ_hard_last) - CpxMagS(IQ_eq_in); - AGC_Train(&agcBlind, e_agc, m_params.agcMu[m_params.agcMu_index]); - } - } -#ifndef DISABLE_UNUSED_STATISTICS - minMaxProcess(&m_statistics.ddcMinMax, IQ_eq_in); -#endif - // ----------------------------------------------------- - // Equalizer Process samples - // ----------------------------------------------------- - ComplexScalar __IQ_cma; - ComplexScalar __IQ_dfeOn; - - IQ_eq = IQ_eq_in; - if (m_params.eq_mode == eq_mode_cma) - { - __IQ_cma = m_cma2.process(toComplexScalar(IQ_eq_in)); - IQ_cma = toCpx(__IQ_cma); - IQ_eq = IQ_cma; - } - - if (m_params.eq_mode == eq_mode_dfe) - { - __IQ_dfeOn = m_dfe_on2.process(toComplexScalar(IQ_eq_in), toComplexScalar(IQ_hard_last)); - IQ_dfeOn = toCpx(__IQ_dfeOn); - IQ_eq = IQ_dfeOn; - } - - IQ_soft = IQ_eq; - - // Map sympol - sym = SymMapDemap(m_pSymMapper, IQ_soft); - sym_err = SymMapGetError(m_pSymMapper, IQ_soft, sym); - IQ_hard = sym_err.hard_sym; - - // Timing detector - if (m_params.str_mode == str_mode_enabled) - { - m_Vc = LeadLagProcess(&m_loop_filter_str, &m_params.str_loopfilter_coeff[m_params.str_loopfilter_coeff_index], Vd); - } - - // Carrier Phase Recovery - if (m_params.cpr_mode == cpr_mode_enabled) - { - // state-based PFD - vPfdCpr = PfdProcess(&m_pfdCpr, sym_err.err_phi, dabs(sym_err.mag)); - - // use phase error from symbol mapper - m_dOmega_vco = LeadLagProcess(&m_loop_filter_cpr, &m_params.cpr_loopfilter_coeff[m_params.cpr_loopfilter_coeff_index], vPfdCpr); - } - - // ----------------------------------------------------- - // Equalizer Training - // ----------------------------------------------------- - sym_cma = SymMapGetSymbolInfo(m_pSymMapper, SymMapDemap(m_pSymMapper, IQ_cma)); - if (m_params.cmaMode == cma_mode_training_enabled) - { - if (m_params.cmaType == cma_type_cma) - { - // CMA: Train - m_cma2.trainGodard(__IQ_cma, CpxMagS(sym_cma.rect), m_pSymMapper->R2_cma, m_params.eqMuCma); - } - if (m_params.cmaType == cma_type_mma) - { - // MMA: Train - m_cma2.trainMma(__IQ_cma, toComplexScalar(m_pSymMapper->R_mma), m_params.eqMuCma); - } - if (m_params.cmaType == cma_type_smma) - { - // S-MMA: Train - m_cma2.trainSmma(__IQ_cma, CpxMagS(sym_cma.rect), toComplexScalar(m_pSymMapper->R_smma), m_params.eqMuCma); - } - } - - if (m_params.dfeMode == dfe_mode_training_enabled) - { - // DFE: LMS Update coefficients - m_dfe_off2.train(toComplexScalar(m_dfe_e_cpx), m_params.eqMuDfe); + IQ_eq_in = IQ_cpr; - // DFE: Calc offline response - ComplexScalar __IQ_dfeOff = m_dfe_off2.process(toComplexScalar(IQ_eq_in), toComplexScalar(sym_dfe_off.rect)); - IQ_dfeOff = toCpx(__IQ_dfeOff); - - sym_dfe_off = SymMapGetSymbolInfo(m_pSymMapper, SymMapDemap(m_pSymMapper, IQ_soft)); - - // DFE: Calculate current error - ComplexScalar __dfe_e_cpx = toComplexScalar(sym_dfe_off.rect) - __IQ_dfeOff; - m_dfe_e_cpx = toCpx(__dfe_e_cpx); - } - - // Calculate current error - // CMA - e_cma = CpxMagS(CpxSubS(sym_cma.rect, IQ_cma)); - - // DFE-Offline - e_dfe_off = CpxMagS(m_dfe_e_cpx); - - // DFE-Online - e_dfe_on = CpxMagS(CpxSubS(IQ_hard, IQ_dfeOn)); - // ----------------------------------------------------- - - // Update IQ_H - IQ_hard_last = IQ_hard; - - // Lock-detector development - cpx_t winner_diff = Cpx(0,0); - radio_float_t winner_d = 1000; - int winner_i = 0; - cpx_t diff; - radio_float_t d; - for (int i=0; i < 4; i++) + // Operate at symbol clock + // @1 x symbolrate + if (ClockIsTick(&m_symClock, 1)) + { + // AGC Blind Training + if (m_params.agc_mode != agc_mode_disabled) + { + if (m_params.agcMu_index == agc_state_acquisition) { - diff = CpxSubS(IQ_soft, m_trackers_[i]); - d = CpxMagS(diff); - if (d < winner_d) - { - winner_d = d; - winner_i = i; - winner_diff = diff; - } +// AGC_Process(&agcBlind, CpxMagS(IQ_eq_in), m_params.agcMu[m_params.agcMu_index], 1.0); + e_agc = m_pSymMapper->R2_cma - CpxMagS(m_trackers[0]); + AGC_Train(&agcBlind, e_agc, m_params.agcMu[m_params.agcMu_index]); } - m_trackers_[winner_i] = CpxAddS(m_trackers_[winner_i], CpxScaleRealS(winner_diff, winner_d*TRACKER_MU+TRACKER_EPS)); - for (int i=0; i < 4; i++) + if (m_params.agcMu_index == agc_state_track) { - m_trackers[i] = CpxAddS(CpxScaleRealS(m_trackers[i], 0.998f), CpxScaleRealS(m_trackers_[i], 0.002f)); + e_agc = m_pSymMapper->R2_cma - CpxMagS(m_trackers[0]); +// e_agc = CpxMagS(IQ_hard_last) - CpxMagS(IQ_eq_in); + AGC_Train(&agcBlind, e_agc, m_params.agcMu[m_params.agcMu_index]); } - - // Statistics -#ifndef DISABLE_UNUSED_STATISTICS - SlidingVarProcess(&m_statistics.I_MF, IQ_mf.real); - SlidingVarProcess(&m_statistics.Q_MF, IQ_mf.imag); - SlidingVarProcess(&m_statistics.I_CPR, IQ_cpr.real); - SlidingVarProcess(&m_statistics.Q_CPR, IQ_cpr.imag); - SlidingVarProcess(&m_statistics.I_AGC, IQ_agc.real); - SlidingVarProcess(&m_statistics.Q_AGC, IQ_agc.imag); - SlidingVarProcess(&m_statistics.I_decision, IQ_hard.real); - SlidingVarProcess(&m_statistics.Q_decision, IQ_hard.imag); - SlidingVarProcess(&m_statistics.MagDecision, sym_err.hard_mag); - SlidingVarProcess(&m_statistics.PhiDecision, sym_err.hard_phi); -#endif - SlidingVarProcess(&m_statistics.I_EQ, IQ_eq.real); - SlidingVarProcess(&m_statistics.Q_EQ, IQ_eq.imag); - SlidingVarProcess(&m_statistics.sym_err_mag, sym_err.err_mag); - SlidingVarProcess(&m_statistics.sym_err_phi, sym_err.err_phi*(radio_float_t)(1.0/PI)); - SlidingVarProcess(&m_statistics.noise, CpxMagS(CpxSubS(IQ_hard, IQ_soft))); - SlidingVarProcess(&m_statistics.noise_str, LeadLagGetState(&m_loop_filter_str)); - SlidingVarProcess(&m_statistics.noise_cpr, LeadLagGetState(&m_loop_filter_cpr)); - SlidingVarProcess(&m_statistics.noise_cma, e_cma); - SlidingVarProcess(&m_statistics.noise_dfe_on, e_dfe_on); - SlidingVarProcess(&m_statistics.noise_dfe_off, e_dfe_off); - - // ----------------------------------------------------- - m_pSymbolBuffer[m_numSymsInBuffer++] = sym_err; - if ((m_params.strState == str_state_track) && (m_params.cprState == cpr_state_track)) - { - // Update per-symbol statistic - SymStatUpDate(&m_sym_stat, sym, &sym_err); - m_frameReceiver.process((symbol_t)sym); - } - if (m_pDataListener) - m_pDataListener->receiverDataChanged(this); } - } while (TimingCorrectorIsStuff(&m_timing_corrector)); +#ifndef DISABLE_UNUSED_STATISTICS + minMaxProcess(&m_statistics.ddcMinMax, IQ_eq_in); +#endif + // ----------------------------------------------------- + // Equalizer Process samples + // ----------------------------------------------------- + ComplexScalar __IQ_cma; + ComplexScalar __IQ_dfeOn; + + IQ_eq = IQ_eq_in; + if (m_params.eq_mode == eq_mode_cma) + { + __IQ_cma = m_cma2.process(toComplexScalar(IQ_eq_in)); + IQ_cma = toCpx(__IQ_cma); + IQ_eq = IQ_cma; + } + if (m_params.eq_mode == eq_mode_dfe) + { + __IQ_dfeOn = m_dfe_on2.process(toComplexScalar(IQ_eq_in), toComplexScalar(IQ_hard_last)); + IQ_dfeOn = toCpx(__IQ_dfeOn); + IQ_eq = IQ_dfeOn; + } + + IQ_soft = IQ_eq; + + // Map sympol + sym = SymMapDemap(m_pSymMapper, IQ_soft); + sym_err = SymMapGetError(m_pSymMapper, IQ_soft, sym); + IQ_hard = sym_err.hard_sym; + + // Carrier Phase Recovery + if (m_params.cpr_mode == cpr_mode_enabled) + { + // state-based PFD + vPfdCpr = PfdProcess(&m_pfdCpr, sym_err.err_phi, dabs(sym_err.mag)); + + // use phase error from symbol mapper + m_dOmega_vco = LeadLagProcess(&m_loop_filter_cpr, &m_params.cpr_loopfilter_coeff[m_params.cpr_loopfilter_coeff_index], vPfdCpr); + } + + // ----------------------------------------------------- + // Equalizer Training + // ----------------------------------------------------- + sym_cma = SymMapGetSymbolInfo(m_pSymMapper, SymMapDemap(m_pSymMapper, IQ_cma)); + if (m_params.cmaMode == cma_mode_training_enabled) + { + if (m_params.cmaType == cma_type_cma) + { + // CMA: Train + m_cma2.trainGodard(__IQ_cma, CpxMagS(sym_cma.rect), m_pSymMapper->R2_cma, m_params.eqMuCma); + } + if (m_params.cmaType == cma_type_mma) + { + // MMA: Train + m_cma2.trainMma(__IQ_cma, toComplexScalar(m_pSymMapper->R_mma), m_params.eqMuCma); + } + if (m_params.cmaType == cma_type_smma) + { + // S-MMA: Train + m_cma2.trainSmma(__IQ_cma, CpxMagS(sym_cma.rect), toComplexScalar(m_pSymMapper->R_smma), m_params.eqMuCma); + } + } + + if (m_params.dfeMode == dfe_mode_training_enabled) + { + // DFE: LMS Update coefficients + m_dfe_off2.train(toComplexScalar(m_dfe_e_cpx), m_params.eqMuDfe); + + // DFE: Calc offline response + ComplexScalar __IQ_dfeOff = m_dfe_off2.process(toComplexScalar(IQ_eq_in), toComplexScalar(sym_dfe_off.rect)); + IQ_dfeOff = toCpx(__IQ_dfeOff); + + sym_dfe_off = SymMapGetSymbolInfo(m_pSymMapper, SymMapDemap(m_pSymMapper, IQ_soft)); + + // DFE: Calculate current error + ComplexScalar __dfe_e_cpx = toComplexScalar(sym_dfe_off.rect) - __IQ_dfeOff; + m_dfe_e_cpx = toCpx(__dfe_e_cpx); + } + + // Calculate current error + // CMA + e_cma = CpxMagS(CpxSubS(sym_cma.rect, IQ_cma)); + + // DFE-Offline + e_dfe_off = CpxMagS(m_dfe_e_cpx); + + // DFE-Online + e_dfe_on = CpxMagS(CpxSubS(IQ_hard, IQ_dfeOn)); + // ----------------------------------------------------- + + // Update IQ_H + IQ_hard_last = IQ_hard; + + // Lock-detector development + cpx_t winner_diff = Cpx(0,0); + radio_float_t winner_d = 1000; + int winner_i = 0; + cpx_t diff; + radio_float_t d; + for (int i=0; i < 4; i++) + { + diff = CpxSubS(IQ_soft, m_trackers_[i]); + d = CpxMagS(diff); + if (d < winner_d) + { + winner_d = d; + winner_i = i; + winner_diff = diff; + } + } + m_trackers_[winner_i] = CpxAddS(m_trackers_[winner_i], CpxScaleRealS(winner_diff, winner_d*TRACKER_MU+TRACKER_EPS)); + for (int i=0; i < 4; i++) + { + m_trackers[i] = CpxAddS(CpxScaleRealS(m_trackers[i], 0.998f), CpxScaleRealS(m_trackers_[i], 0.002f)); + } + + // Statistics +#ifndef DISABLE_UNUSED_STATISTICS + SlidingVarProcess(&m_statistics.I_MF, IQ_mf.real); + SlidingVarProcess(&m_statistics.Q_MF, IQ_mf.imag); + SlidingVarProcess(&m_statistics.I_CPR, IQ_cpr.real); + SlidingVarProcess(&m_statistics.Q_CPR, IQ_cpr.imag); + SlidingVarProcess(&m_statistics.I_AGC, IQ_agc.real); + SlidingVarProcess(&m_statistics.Q_AGC, IQ_agc.imag); + SlidingVarProcess(&m_statistics.I_decision, IQ_hard.real); + SlidingVarProcess(&m_statistics.Q_decision, IQ_hard.imag); + SlidingVarProcess(&m_statistics.MagDecision, sym_err.hard_mag); + SlidingVarProcess(&m_statistics.PhiDecision, sym_err.hard_phi); +#endif + SlidingVarProcess(&m_statistics.I_EQ, IQ_eq.real); + SlidingVarProcess(&m_statistics.Q_EQ, IQ_eq.imag); + SlidingVarProcess(&m_statistics.sym_err_mag, sym_err.err_mag); + SlidingVarProcess(&m_statistics.sym_err_phi, sym_err.err_phi*(radio_float_t)(1.0/PI)); + SlidingVarProcess(&m_statistics.noise, CpxMagS(CpxSubS(IQ_hard, IQ_soft))); + SlidingVarProcess(&m_statistics.noise_str, LeadLagGetState(&m_loop_filter_str)); + SlidingVarProcess(&m_statistics.noise_cpr, LeadLagGetState(&m_loop_filter_cpr)); + SlidingVarProcess(&m_statistics.noise_cma, e_cma); + SlidingVarProcess(&m_statistics.noise_dfe_on, e_dfe_on); + SlidingVarProcess(&m_statistics.noise_dfe_off, e_dfe_off); + + // ----------------------------------------------------- + m_pSymbolBuffer[m_numSymsInBuffer++] = sym_err; + if ((m_params.strState == str_state_track) && (m_params.cprState == cpr_state_track)) + { + // Update per-symbol statistic + SymStatUpDate(&m_sym_stat, sym, &sym_err); + m_frameReceiver.process((symbol_t)sym); + } + if (m_pDataListener) + m_pDataListener->receiverDataChanged(this); + } } } @@ -706,10 +684,9 @@ void Receiver::initCPR() void Receiver::initSTR() { // Gardner Symbol Timing Recovery - STRGardnerInit(&m_SymbolTimingRevovery); LeadLagInit(&m_loop_filter_str, 0.0); - TimingCorrectorInit(&m_timing_corrector, (radio_float_t)0.1); - m_Vc = 0; + m_timingGenerator.setOmega(1.0); + } void Receiver::initSymbolMapper() @@ -886,8 +863,7 @@ void Receiver::statisticsReset() void Receiver::strReset() { - LeadLagSetState(&m_loop_filter_str, 0); - STRGardnerInit(&m_SymbolTimingRevovery); + m_timingGenerator.reset(); m_statusListeners.call(&ReceiverStatusListener::receiverStatusChanged, this); } diff --git a/Source/Receiver.hpp b/Source/Receiver.hpp index 4f7941e..ba497ec 100644 --- a/Source/Receiver.hpp +++ b/Source/Receiver.hpp @@ -271,6 +271,63 @@ public: virtual void receiverDataChanged(Receiver *pObj) = 0; }; +// Symbol timing recovery +class TimingGeneratorGardner : public Interpolation::TimingGenerator +{ + // Loop-Filter + lead_lag_filter_t *filter; + lead_lag_coeff_t *coeff; + + // Gardner Symbol Timing Recovery + str_t m_SymbolTimingRevovery; + bool is_time; + radio_float_t m_omega; + + public: + TimingGeneratorGardner() + : Interpolation::TimingGenerator() + , filter(nullptr) + , coeff(nullptr) + { + STRGardnerInit(&m_SymbolTimingRevovery); + is_time = true; + } + virtual ~TimingGeneratorGardner() = default; + + void reset() + { + LeadLagSetState(filter, 0); + } + + void loopFilterSetup(lead_lag_filter_t *pFilter, lead_lag_coeff_t *pCoeff) + { + filter = pFilter; + coeff = pCoeff; + } + + void setOmega(radio_float_t omega) + { + LeadLagInit(filter, omega); + m_omega = omega; + } + + void process(ComplexScalar const &iq) + { + // @2 x symbolrate + // Symbol timing recovery + radio_float_t vd = STRGardnerProcess(&m_SymbolTimingRevovery, toCpx(iq)); + + if (is_time) + { + // @1 x symbolrate + m_omega = LeadLagProcess(filter, coeff, vd); + } + is_time = not is_time; + + update(m_omega); + } +}; + class Receiver : public ReceiverInterface, public IDeFormatter, public Timer { public: @@ -322,9 +379,7 @@ private: lead_lag_filter_t m_loop_filter_cpr; // Gardner Symbol Timing Recovery - str_t m_SymbolTimingRevovery; - timing_corrector_t m_timing_corrector; - radio_float_t m_Vc; + TimingGeneratorGardner m_timingGenerator; // AGC // Blind