From eec12c93937ba0476dfaf3d695b24389cbc7face Mon Sep 17 00:00:00 2001 From: Jens Ahrensfeld Date: Tue, 21 Jun 2022 19:39:40 +0000 Subject: [PATCH] - CPR/STR: no automatic change of ACQ and TRK states base on SNR - No use for clock stuff, since buffer_ip is decimated - use Costas Loop for acquisition git-svn-id: http://moon:8086/svn/software/trunk/projects/mpsk_rx_gui@1052 b431acfa-c32f-4a4a-93f1-934dc6c82436 --- Source/Receiver.cpp | 332 +++++++++++++++++++++++--------------------- Source/Receiver.hpp | 4 +- 2 files changed, 174 insertions(+), 162 deletions(-) diff --git a/Source/Receiver.cpp b/Source/Receiver.cpp index 535dee4..c92d762 100644 --- a/Source/Receiver.cpp +++ b/Source/Receiver.cpp @@ -345,14 +345,14 @@ void Receiver::timerCallback() } // Control - m_params.cprState = cpr_state_acquisition; - m_params.strState = str_state_acquisition; - if (getStatus().snrCurrent_dB > 30) - { +// m_params.cprState = cpr_state_acquisition; +// m_params.strState = str_state_acquisition; +// if (getStatus().snrCurrent_dB > 30) +// { // statusHasChanged = (m_params.cprState != cpr_state_track) || (m_params.strState != str_state_track); - m_params.cprState = cpr_state_track; - m_params.strState = str_state_track; - } +// m_params.cprState = cpr_state_track; +// m_params.strState = str_state_track; +// } // Announce status changed if (statusHasChanged) @@ -477,184 +477,194 @@ void Receiver::processBaseband(CVec const &iq, uint32_t len) m_polyPhase.process(); } + // ----------------------------------------------------- + // Big and ugly processing loop // Further BB processing - // @2 x symbolrate + // @1 x symbolrate + // ----------------------------------------------------- while(m_buffer_ip.len()) { + // Buffer contains every 2nd sample from interpolator ComplexScalar iq_mf = m_buffer_ip.readAt(0); +#ifndef DISABLE_UNUSED_STATISTICS + minMaxProcess(&m_statistics.ddcMinMax, iq_mf); +#endif + // ----------------------------------------------------- + // AGC Tracker based training + // @1 x symbolrate + // ----------------------------------------------------- + if (m_params.agc_mode != agc_mode_disabled) + { + if (m_params.agcMu_index == agc_state_acquisition) + { + e_agc = 1.0 - CpxMagS(getTracker(0)); + AGC_Train(&agcBlind, e_agc, m_params.agcMu[m_params.agcMu_index]); + } + if (m_params.agcMu_index == agc_state_track) + { + e_agc = 1.0 - CpxMagS(getTracker(0)); + AGC_Train(&agcBlind, e_agc, m_params.agcMu[m_params.agcMu_index]); + } + } + // ----------------------------------------------------- // Carrier Derotator - // @2 x symbolrate + // @1 x symbolrate // ----------------------------------------------------- IQ_cpr = toCpx(m_nco_cpr.mixComplexS(iq_mf, ComplexScalar(1,1))); m_nco_cpr.process(m_dOmega_vco, m_params.CPR_phase); + // Carrier Phase Recovery + if (m_params.cprState == cpr_state_acquisition) + { + // Costas loop + vPfdCpr = PhaseErrQPSK(IQ_cpr); + } + IQ_eq_in = IQ_cpr; - + // ----------------------------------------------------- - // Big and ugly processing loop + // Equalizer Process samples // @1 x symbolrate // ----------------------------------------------------- - if (ClockIsTick(&m_symClock, 1)) - { -#ifndef DISABLE_UNUSED_STATISTICS - minMaxProcess(&m_statistics.ddcMinMax, IQ_eq_in); -#endif - // ----------------------------------------------------- - // AGC Blind process samples - // @1 x symbolrate - // ----------------------------------------------------- - if (m_params.agc_mode != agc_mode_disabled) - { - if (m_params.agcMu_index == agc_state_acquisition) - { - e_agc = 1.0 - CpxMagS(getTracker(0)); - AGC_Train(&agcBlind, e_agc, m_params.agcMu[m_params.agcMu_index]); - } - if (m_params.agcMu_index == agc_state_track) - { - e_agc = 1.0 - CpxMagS(getTracker(0)); - AGC_Train(&agcBlind, e_agc, m_params.agcMu[m_params.agcMu_index]); - } - } - - // ----------------------------------------------------- - // Equalizer Process samples - // @1 x symbolrate - // ----------------------------------------------------- - 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 - // @1 x symbolrate - // ----------------------------------------------------- - 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 - // @1 x symbolrate - // ----------------------------------------------------- - 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); - } + ComplexScalar __IQ_cma; + ComplexScalar __IQ_dfeOn; - // Calculate current error - // CMA - e_cma = CpxMagS(CpxSubS(sym_cma.rect, IQ_cma)); + 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; + } - // DFE-Offline - e_dfe_off = CpxMagS(m_dfe_e_cpx); + 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; + } - // DFE-Online - e_dfe_on = CpxMagS(CpxSubS(IQ_hard, IQ_dfeOn)); - // ----------------------------------------------------- + IQ_soft = IQ_eq; - // Update IQ_H - IQ_hard_last = IQ_hard; + // ----------------------------------------------------- + // Map sympol + // @1 x symbolrate + // ----------------------------------------------------- + sym = SymMapDemap(m_pSymMapper, IQ_soft); + sym_err = SymMapGetError(m_pSymMapper, IQ_soft, sym); + IQ_hard = sym_err.hard_sym; - // ----------------------------------------------------- - // Lock-detector development - // @1 x symbolrate - // ----------------------------------------------------- - m_tracker.process(toComplexScalar(IQ_soft)); + // Carrier Phase Recovery + if (m_params.cprState == cpr_state_track) + { + // state-based PFD + vPfdCpr = PfdProcess(&m_pfdCpr, sym_err.err_phi, dabs(sym_err.mag)); + } - // 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); + if (m_params.cpr_mode == cpr_mode_enabled) + { + // Update frequency from Carrier Derotator + // vPfdCpr comes from PhaseErrQPSK forming a Costas Loop when + // cprState == cpr_state_acquisition + // vPfdCpr comes from SymMapper forming a decision directed phase correction loop when + // cprState == cpr_state_track + m_dOmega_vco = LeadLagProcess(&m_loop_filter_cpr, &m_params.cpr_loopfilter_coeff[m_params.cpr_loopfilter_coeff_index], vPfdCpr); + } - // ----------------------------------------------------- - m_pSymbolBuffer[m_numSymsInBuffer++] = sym_err; - if ((m_params.strState == str_state_track) && (m_params.cprState == cpr_state_track)) + // ----------------------------------------------------- + // Equalizer Training + // @1 x symbolrate + // ----------------------------------------------------- + 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) { - // Update per-symbol statistic - SymStatUpDate(&m_sym_stat, sym, &sym_err); - m_frameReceiver.process((symbol_t)sym); + // CMA: Train + m_cma2.trainGodard(__IQ_cma, CpxMagS(sym_cma.rect), m_pSymMapper->R2_cma, m_params.eqMuCma); } - if (m_pDataListener) - m_pDataListener->receiverDataChanged(this); + 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 + // @1 x symbolrate + // ----------------------------------------------------- + m_tracker.process(toComplexScalar(IQ_soft)); + + // 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; + // Update per-symbol statistic + SymStatUpDate(&m_sym_stat, sym, &sym_err); + m_frameReceiver.process((symbol_t)sym); + if (m_pDataListener) + { + m_pDataListener->receiverDataChanged(this); } } } diff --git a/Source/Receiver.hpp b/Source/Receiver.hpp index dfffa23..7c2bde3 100644 --- a/Source/Receiver.hpp +++ b/Source/Receiver.hpp @@ -306,7 +306,7 @@ class TimingGeneratorGardner : public Interpolation::TimingGenerator coeff = pCoeff; } - void process(ComplexScalar const &iq) + bool process(ComplexScalar const &iq) { // @2 x symbolrate // Symbol timing recovery @@ -320,6 +320,8 @@ class TimingGeneratorGardner : public Interpolation::TimingGenerator is_time = not is_time; update(m_omega); + + return not is_time; } };