- 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
This commit is contained in:
+203
-227
@@ -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[0], STR_GAIN_LEAD_AQU, STR_GAIN_LAG_AQU);
|
||||||
LeadLagSetCoeff(&m_params.str_loopfilter_coeff[1], STR_GAIN_LEAD_TRK, STR_GAIN_LAG_TRK);
|
LeadLagSetCoeff(&m_params.str_loopfilter_coeff[1], STR_GAIN_LEAD_TRK, STR_GAIN_LAG_TRK);
|
||||||
m_params.str_loopfilter_coeff_index = 0;
|
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.cprState = cpr_state_acquisition;
|
||||||
m_params.cpr_mode = cpr_mode_enabled;
|
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());
|
SlidingVarProcess(&m_statistics.Q_DDC, iq[n].imag());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (n = 0; n < len; n++)
|
// Matched filtering and symbol timing recovery
|
||||||
|
// @2 x symbolrate
|
||||||
|
|
||||||
|
Processor::Buffer<ComplexScalar> *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)
|
if (buf_ip == nullptr)
|
||||||
{
|
{
|
||||||
// Matched filtering and interpolation
|
return;
|
||||||
m_polyPhase.feed(iq[n], 1);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (RCF_TYPE == RCF_TYPE_POLYPHASE_FARROW)
|
// Further BB processing
|
||||||
{
|
// @2 x symbolrate
|
||||||
// Matched filtering and interpolation
|
while(buf_ip->len())
|
||||||
m_farrow.feed(&iq[n], 1);
|
{
|
||||||
}
|
IQ_mf = toCpx(buf_ip->readAt(0));
|
||||||
|
|
||||||
do
|
// AGC Blind process samples
|
||||||
{
|
IQ_agc = CpxScaleRealS(IQ_mf, AGC_GetWeight(&agcBlind));
|
||||||
// Timing corrector
|
|
||||||
TimingCorrectorProcess(&m_timing_corrector, m_Vc);
|
|
||||||
|
|
||||||
#ifndef _DEBUG
|
// Carrier Derotator
|
||||||
// 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)));
|
IQ_cpr = toCpx(m_nco_cpr.mixComplexS(toComplexScalar(IQ_agc), ComplexScalar(1,1)));
|
||||||
#endif
|
m_nco_cpr.process(m_dOmega_vco, m_params.CPR_phase);
|
||||||
|
|
||||||
if (RCF_TYPE == RCF_TYPE_POLYPHASE_DISCRETE)
|
IQ_eq_in = IQ_cpr;
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
// DFE: Calc offline response
|
// Operate at symbol clock
|
||||||
ComplexScalar __IQ_dfeOff = m_dfe_off2.process(toComplexScalar(IQ_eq_in), toComplexScalar(sym_dfe_off.rect));
|
// @1 x symbolrate
|
||||||
IQ_dfeOff = toCpx(__IQ_dfeOff);
|
if (ClockIsTick(&m_symClock, 1))
|
||||||
|
{
|
||||||
sym_dfe_off = SymMapGetSymbolInfo(m_pSymMapper, SymMapDemap(m_pSymMapper, IQ_soft));
|
// AGC Blind Training
|
||||||
|
if (m_params.agc_mode != agc_mode_disabled)
|
||||||
// DFE: Calculate current error
|
{
|
||||||
ComplexScalar __dfe_e_cpx = toComplexScalar(sym_dfe_off.rect) - __IQ_dfeOff;
|
if (m_params.agcMu_index == agc_state_acquisition)
|
||||||
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]);
|
// AGC_Process(&agcBlind, CpxMagS(IQ_eq_in), m_params.agcMu[m_params.agcMu_index], 1.0);
|
||||||
d = CpxMagS(diff);
|
e_agc = m_pSymMapper->R2_cma - CpxMagS(m_trackers[0]);
|
||||||
if (d < winner_d)
|
AGC_Train(&agcBlind, e_agc, m_params.agcMu[m_params.agcMu_index]);
|
||||||
{
|
|
||||||
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));
|
if (m_params.agcMu_index == agc_state_track)
|
||||||
for (int i=0; i < 4; i++)
|
|
||||||
{
|
{
|
||||||
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()
|
void Receiver::initSTR()
|
||||||
{
|
{
|
||||||
// Gardner Symbol Timing Recovery
|
// Gardner Symbol Timing Recovery
|
||||||
STRGardnerInit(&m_SymbolTimingRevovery);
|
|
||||||
LeadLagInit(&m_loop_filter_str, 0.0);
|
LeadLagInit(&m_loop_filter_str, 0.0);
|
||||||
TimingCorrectorInit(&m_timing_corrector, (radio_float_t)0.1);
|
m_timingGenerator.setOmega(1.0);
|
||||||
m_Vc = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Receiver::initSymbolMapper()
|
void Receiver::initSymbolMapper()
|
||||||
@@ -886,8 +863,7 @@ void Receiver::statisticsReset()
|
|||||||
|
|
||||||
void Receiver::strReset()
|
void Receiver::strReset()
|
||||||
{
|
{
|
||||||
LeadLagSetState(&m_loop_filter_str, 0);
|
m_timingGenerator.reset();
|
||||||
STRGardnerInit(&m_SymbolTimingRevovery);
|
|
||||||
m_statusListeners.call(&ReceiverStatusListener::receiverStatusChanged, this);
|
m_statusListeners.call(&ReceiverStatusListener::receiverStatusChanged, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+58
-3
@@ -271,6 +271,63 @@ public:
|
|||||||
virtual void receiverDataChanged(Receiver *pObj) = 0;
|
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
|
class Receiver : public ReceiverInterface, public IDeFormatter, public Timer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -322,9 +379,7 @@ private:
|
|||||||
lead_lag_filter_t m_loop_filter_cpr;
|
lead_lag_filter_t m_loop_filter_cpr;
|
||||||
|
|
||||||
// Gardner Symbol Timing Recovery
|
// Gardner Symbol Timing Recovery
|
||||||
str_t m_SymbolTimingRevovery;
|
TimingGeneratorGardner m_timingGenerator;
|
||||||
timing_corrector_t m_timing_corrector;
|
|
||||||
radio_float_t m_Vc;
|
|
||||||
|
|
||||||
// AGC
|
// AGC
|
||||||
// Blind
|
// Blind
|
||||||
|
|||||||
Reference in New Issue
Block a user