- 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
This commit is contained in:
2022-06-21 19:39:40 +00:00
parent 4f9efa88bb
commit eec12c9393
2 changed files with 174 additions and 162 deletions
+171 -161
View File
@@ -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);
}
}
}
+3 -1
View File
@@ -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;
}
};