git-svn-id: http://moon:8086/svn/software/trunk/projects/mpsk_rx_gui@1086 b431acfa-c32f-4a4a-93f1-934dc6c82436
486 lines
11 KiB
C++
486 lines
11 KiB
C++
#ifndef _RECEIVER_H_
|
|
#define _RECEIVER_H_
|
|
|
|
#include <cstdint>
|
|
#include <radio/symbol.h>
|
|
#include <radio/synchronization.h>
|
|
#include <radio/interpolation.h>
|
|
#include <radio/statistics.h>
|
|
#include <radio/agc.h>
|
|
|
|
#include <JuceHeader.h>
|
|
#include "LogComponent.h"
|
|
#include "FrameReceiver.hpp"
|
|
|
|
#include <cpp/radio/Vector.hpp>
|
|
#include <cpp/radio/FirComplex.hpp>
|
|
#include <cpp/radio/Equalizer.hpp>
|
|
#include <cpp/radio/interpolation/Farrow.hpp>
|
|
#include <cpp/radio/interpolation/DownSampler.hpp>
|
|
#include <cpp/radio/interpolation/PolyPhase.hpp>
|
|
#include <cpp/radio/metrics/Tracker.hpp>
|
|
|
|
#include <cpp/radio/Nco.hpp>
|
|
|
|
#ifdef _WINDOWS
|
|
#include <float.h>
|
|
#define isnan _isnan
|
|
#define isinf(x) (!_finite(x))
|
|
#endif
|
|
|
|
using namespace Radio;
|
|
|
|
// ---------------------------------------------------
|
|
#define NUM_PASSBAND_SAMPLES_PER_SYM 4
|
|
#define NUM_BASEBAND_SAMPLES_PER_SYM 2
|
|
#define MODE_PROCESS_PASSBAND 0
|
|
#define MODE_PROCESS_BASEBAND 1
|
|
#define RCF_TYPE_POLYPHASE_DISCRETE 0
|
|
#define RCF_TYPE_POLYPHASE_FARROW 1
|
|
|
|
#define EQ_ENABLE 1
|
|
#define MODULATION_TYPE MAP_MODE_QAM
|
|
#define RCF_TYPE RCF_TYPE_POLYPHASE_FARROW
|
|
#define MAX_NUMBITS_PERSYM 16
|
|
// ---------------------------------------------------
|
|
enum eq_mode
|
|
{
|
|
eq_mode_disabled,
|
|
eq_mode_cma,
|
|
eq_mode_dfe
|
|
};
|
|
|
|
enum cma_type
|
|
{
|
|
cma_type_cma = 0,
|
|
cma_type_mma,
|
|
cma_type_smma
|
|
};
|
|
|
|
enum cma_mode
|
|
{
|
|
cma_mode_training_disabled = 0,
|
|
cma_mode_training_enabled
|
|
};
|
|
|
|
enum dfe_mode
|
|
{
|
|
dfe_mode_training_disabled = 0,
|
|
dfe_mode_training_enabled
|
|
};
|
|
|
|
enum agc_mode
|
|
{
|
|
agc_mode_disabled = 0,
|
|
agc_mode_enabled
|
|
};
|
|
|
|
enum agc_state
|
|
{
|
|
agc_state_acquisition = 0,
|
|
agc_state_track
|
|
};
|
|
|
|
enum cpr_mode
|
|
{
|
|
cpr_mode_disabled = 0,
|
|
cpr_mode_enabled
|
|
};
|
|
|
|
enum cpr_state
|
|
{
|
|
cpr_state_acquisition = 0,
|
|
cpr_state_track
|
|
};
|
|
|
|
enum str_mode
|
|
{
|
|
str_mode_disabled = 0,
|
|
str_mode_enabled
|
|
};
|
|
|
|
enum str_state
|
|
{
|
|
str_state_acquisition = 0,
|
|
str_state_track
|
|
};
|
|
|
|
typedef struct _minmax_t
|
|
{
|
|
radio_float_t min, max;
|
|
|
|
} minmax_t;
|
|
|
|
typedef struct _cpx_minmax_t
|
|
{
|
|
cpx_t min, max;
|
|
radio_float_t magMin, magMax;
|
|
radio_float_t phiAtMinI;
|
|
radio_float_t phiAtMaxI;
|
|
radio_float_t phiAtMinQ;
|
|
radio_float_t phiAtMaxQ;
|
|
|
|
} cpx_minmax_t;
|
|
|
|
typedef struct _statistics_t
|
|
{
|
|
sl_var_t RF;
|
|
sl_var_t I_DDC;
|
|
sl_var_t Q_DDC;
|
|
sl_var_t I_MF;
|
|
sl_var_t Q_MF;
|
|
sl_var_t I_CPR;
|
|
sl_var_t Q_CPR;
|
|
sl_var_t I_AGC;
|
|
sl_var_t Q_AGC;
|
|
sl_var_t I_EQ;
|
|
sl_var_t Q_EQ;
|
|
sl_var_t I_decision;
|
|
sl_var_t Q_decision;
|
|
sl_var_t MagDecision;
|
|
sl_var_t PhiDecision;
|
|
sl_var_t sym_err_mag;
|
|
sl_var_t sym_err_phi;
|
|
sl_var_t noise;
|
|
sl_var_t noise_cma;
|
|
sl_var_t noise_str;
|
|
sl_var_t noise_cpr;
|
|
sl_var_t noise_dfe_on;
|
|
sl_var_t noise_dfe_off;
|
|
sl_minmax_t sl_min_I;
|
|
sl_minmax_t sl_max_I;
|
|
sl_minmax_t sl_min_Q;
|
|
sl_minmax_t sl_max_Q;
|
|
sl_minmax_t sl_min_mag;
|
|
sl_minmax_t sl_max_mag;
|
|
|
|
cpx_minmax_t ddcMinMax;
|
|
|
|
} statistics_t;
|
|
|
|
typedef struct _params_t
|
|
{
|
|
uint32_t numBitsPerSymbol;
|
|
radio_float_t symbolrate;
|
|
radio_float_t samplerate;
|
|
radio_float_t ddc_freq;
|
|
radio_float_t CPR_phase;
|
|
bool dfeAutoUpdateEnable;
|
|
bool strAutoControlEnable;
|
|
bool cprAutoControlEnable;
|
|
bool agcAutoControlEnable;
|
|
enum str_mode str_mode;
|
|
enum cpr_mode cpr_mode;
|
|
enum agc_mode agc_mode;
|
|
enum eq_mode eq_mode;
|
|
enum cma_type cmaType;
|
|
enum cma_mode cmaMode;
|
|
enum dfe_mode dfeMode;
|
|
lead_lag_coeff_t str_loopfilter_coeff[2];
|
|
uint32_t str_loopfilter_coeff_index;
|
|
lead_lag_coeff_t cpr_loopfilter_coeff[2];
|
|
uint32_t cpr_loopfilter_coeff_index;
|
|
|
|
radio_float_t eqMuCma;
|
|
radio_float_t eqMuDfe;
|
|
radio_float_t agcMu[2];
|
|
uint32_t agcMu_index;
|
|
|
|
enum str_state strState;
|
|
enum cpr_state cprState;
|
|
enum agc_state agc_state;
|
|
|
|
} params_t;
|
|
|
|
typedef struct _status_t
|
|
{
|
|
uint32 numSymbolsReceived;
|
|
frame_statistics_t frameStatRx;
|
|
|
|
radio_float_t noiseStr;
|
|
radio_float_t noiseCpr;
|
|
|
|
radio_float_t EB_N0;
|
|
radio_float_t powerRF_dB;
|
|
radio_float_t powerDDC_dB;
|
|
radio_float_t powerMF_dB;
|
|
radio_float_t powerCPR_dB;
|
|
radio_float_t powerAGC_dB;
|
|
radio_float_t powerEQ_dB;
|
|
radio_float_t powerDecison_dB;
|
|
radio_float_t powerMagDecision_dB;
|
|
radio_float_t powerPhiDecision_dB;
|
|
|
|
radio_float_t snrCurrent_dB;
|
|
radio_float_t snrCma_dB;
|
|
radio_float_t snrDfeOn_dB;
|
|
radio_float_t snrDfeOff_dB;
|
|
radio_float_t snrSymbolMagnitude_dB;
|
|
radio_float_t snrSymbolPhase_dB;
|
|
|
|
radio_float_t deltaFrequencyCPR;
|
|
radio_float_t deltaFrequencySTR;
|
|
|
|
radio_float_t agcGain[2];
|
|
|
|
minmax_t rfMinMax;
|
|
cpx_minmax_t ddcMinMax;
|
|
|
|
} status_t;
|
|
|
|
class Receiver;
|
|
// ---------------------------------------------------
|
|
class ReceiverInterface
|
|
{
|
|
public:
|
|
ReceiverInterface() {}
|
|
virtual ~ReceiverInterface() {}
|
|
|
|
virtual void setSamplerate(radio_float_t samplerate_hz) = 0;
|
|
|
|
virtual void setParams(const params_t ¶ms) = 0;
|
|
virtual const params_t& getParams() = 0;
|
|
virtual const status_t& getStatus() = 0;
|
|
virtual cpx_t getTracker(uint32 index) = 0;
|
|
virtual void reset() = 0;
|
|
virtual void statisticsReset() = 0;
|
|
virtual void strReset() = 0;
|
|
virtual void cprReset() = 0;
|
|
virtual void cmaReset() = 0;
|
|
virtual void dfeReset() = 0;
|
|
virtual void dfeOffUpdateFromCma() = 0;
|
|
virtual void dfeOnUpdateFromDfeOff() = 0;
|
|
virtual const CVec& getWeightsCMA() = 0;
|
|
virtual const CVec& getWeightsDFE() = 0;
|
|
|
|
};
|
|
|
|
class ReceiverStatusListener
|
|
{
|
|
public:
|
|
ReceiverStatusListener() {}
|
|
virtual ~ReceiverStatusListener() {}
|
|
|
|
virtual void receiverStatusChanged(ReceiverInterface *pReceiver) = 0;
|
|
};
|
|
|
|
class ReceiverDataListener
|
|
{
|
|
public:
|
|
ReceiverDataListener() {}
|
|
virtual ~ReceiverDataListener() {}
|
|
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()
|
|
{
|
|
LeadLagInit(filter, 0.00);
|
|
}
|
|
|
|
void loopFilterSetup(lead_lag_filter_t *pFilter, lead_lag_coeff_t *pCoeff)
|
|
{
|
|
filter = pFilter;
|
|
coeff = pCoeff;
|
|
}
|
|
|
|
bool 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);
|
|
|
|
return not is_time;
|
|
}
|
|
};
|
|
|
|
class Receiver : public ReceiverInterface, public Timer
|
|
{
|
|
public:
|
|
Receiver(LogHandler *pLogHandler);
|
|
~Receiver(void);
|
|
void init();
|
|
void free();
|
|
void processBaseband(radio_float_t *pI, radio_float_t *pQ, uint32_t len);
|
|
void processPassband(RVec const &rf, uint32_t len);
|
|
void setBufSize(uint32 size);
|
|
Processor::Buffer<sym_err_t>& getSoftSyms();
|
|
void initDefaultParams();
|
|
void addStatusListener(ReceiverStatusListener *pListener);
|
|
void addDataListener(ReceiverDataListener *pListener);
|
|
private:
|
|
ListenerList<ReceiverStatusListener> m_statusListeners;
|
|
LogComponent m_log;
|
|
ReceiverDataListener *m_pDataListener;
|
|
CriticalSection m_lock;
|
|
cpx_t *m_pPassbandBuffer;
|
|
cpx_t *m_pBasebandBuffer;
|
|
CVec m_passbandBuffer;
|
|
CVec m_basebandBuffer;
|
|
|
|
Processor::Buffer<sym_err_t> m_buf_symerr;
|
|
Processor::Buffer<symbol_t> m_buf_sym;
|
|
|
|
uint32_t m_bufsize;
|
|
bool m_ReceiverEnable;
|
|
pfd_t m_pfdCpr;
|
|
|
|
// Filter
|
|
Interpolation::DownSampler m_firArmDown;
|
|
|
|
// Gardner Symbol Timing Recovery
|
|
TimingGeneratorGardner m_timingGenerator;
|
|
|
|
// Interpolators
|
|
Interpolation::PolyPhase m_polyPhase;
|
|
Interpolation::Farrow m_farrow;
|
|
|
|
// NCOs
|
|
Nco m_nco_ddc, m_nco_cpr;
|
|
radio_float_t m_dOmega_vco;
|
|
|
|
// Loop-Filter
|
|
lead_lag_filter_t m_loop_filter_str;
|
|
lead_lag_filter_t m_loop_filter_cpr;
|
|
|
|
// AGC
|
|
// Blind
|
|
agc_t agcBlind;
|
|
|
|
// Equalizer
|
|
// Decision directed EQ
|
|
Equalizer::Dfe m_dfe_on2;
|
|
Equalizer::Dfe m_dfe_off2;
|
|
|
|
cpx_t m_dfe_e_cpx;
|
|
|
|
// Blind EQ
|
|
Equalizer::Cma m_cma2;
|
|
|
|
// Symbol demapper
|
|
sym_map_t *m_pSymMapper;
|
|
|
|
// Staticstics
|
|
sym_stat_t m_sym_stat;
|
|
statistics_t m_statistics;
|
|
|
|
// Metrics
|
|
Metrics::Tracker m_tracker;
|
|
|
|
FrameReceiver m_frameReceiver;
|
|
params_t m_params;
|
|
|
|
// Processor Buffers
|
|
Processor::Buffer<ComplexScalar> m_buffer_agc;
|
|
Processor::Buffer<ComplexScalar> m_buffer_ip;
|
|
|
|
|
|
void timerCallback();
|
|
void processBaseband(CVec const &iq, uint32_t len);
|
|
void minMaxInit(minmax_t *pObj, radio_float_t min_initial, radio_float_t max_initial)
|
|
{
|
|
pObj->min = min_initial;
|
|
pObj->max = max_initial;
|
|
}
|
|
void minMaxInit(cpx_minmax_t *pObj, cpx_t min_initial, cpx_t max_initial)
|
|
{
|
|
pObj->min = min_initial;
|
|
pObj->max = max_initial;
|
|
pObj->magMin = 1000;
|
|
pObj->magMax = -1000;
|
|
}
|
|
void minMaxProcess(minmax_t *pObj, radio_float_t x)
|
|
{
|
|
pObj->min = dmin(pObj->min, x);
|
|
pObj->max = dmax(pObj->max, x);
|
|
}
|
|
void minMaxProcess(cpx_minmax_t *pObj, cpx_t x)
|
|
{
|
|
if (CpxMagS(x) >= pObj->magMax)
|
|
{
|
|
if (x.real > 0 && x.imag > 0)
|
|
pObj->phiAtMaxI = CpxPhiS(x);
|
|
if (x.real > 0 && x.imag < 0)
|
|
pObj->phiAtMaxQ = CpxPhiS(x);
|
|
if (x.real < 0 && x.imag < 0)
|
|
pObj->phiAtMinQ = CpxPhiS(x);
|
|
if (x.real < 0 && x.imag > 0)
|
|
pObj->phiAtMinI = CpxPhiS(x);
|
|
}
|
|
pObj->min.real = SlidingMinMaxProcess(&m_statistics.sl_min_I, x.real);
|
|
pObj->max.real = SlidingMinMaxProcess(&m_statistics.sl_max_I, x.real);
|
|
pObj->min.imag = SlidingMinMaxProcess(&m_statistics.sl_min_Q, x.imag);
|
|
pObj->max.imag = SlidingMinMaxProcess(&m_statistics.sl_max_Q, x.imag);
|
|
pObj->magMin = SlidingMinMaxProcess(&m_statistics.sl_min_mag, CpxMagS(x));
|
|
pObj->magMax = SlidingMinMaxProcess(&m_statistics.sl_max_mag, CpxMagS(x));
|
|
|
|
}
|
|
|
|
void initFilterArm();
|
|
void initFilterRcf();
|
|
void initSymbolMapper();
|
|
void initSTR();
|
|
void initCPR();
|
|
void initDDC();
|
|
|
|
// Interface
|
|
void setSamplerate(radio_float_t samplerate_hz) override;
|
|
void setParams(const params_t ¶ms) override;
|
|
params_t& getParams() override;
|
|
status_t& getStatus() override;
|
|
void reset() override;
|
|
void strReset() override;
|
|
void cprReset() override;
|
|
void dfeReset() override;
|
|
void cmaReset() override;
|
|
void statisticsReset() override;
|
|
void dfeOffUpdateFromCma() override;
|
|
void dfeOnUpdateFromDfeOff() override;
|
|
cpx_t getTracker(uint32 index) override;
|
|
|
|
const CVec& getWeightsCMA() override
|
|
{
|
|
return m_cma2.getWeights();
|
|
}
|
|
|
|
const CVec& getWeightsDFE() override
|
|
{
|
|
return m_dfe_off2.getWeights();
|
|
}
|
|
|
|
trace_t m_trace;
|
|
};
|
|
|
|
#endif // _RECEIVER_H_
|