Files
mpsk_rx_gui/Source/Receiver.cpp
T
2014-09-24 17:21:29 +00:00

1667 lines
44 KiB
C++

#include <cmath>
#include "Receiver.h"
#include "MinMaxLemire.h"
#include <radio/Frame.hpp>
#if 0
#include <radio/ComplexVector.hpp>
#include <radio/RealVector.hpp>
#endif
/***************************************************************/
const uint32_t LPF_OVERSAMPLING = 32; // Arm-Filter: oversampling
const radio_float_t LPF_OMEGA = (radio_float_t)0.48; // Arm-Filter: RC Roll-off
const uint32_t RCF_OVERSAMPLING = 16; // RCF_TYPE_POLYPHASE_DISCRETE: oversampling
const radio_float_t RCF_ROLLOFF = (radio_float_t)0.35; // RCF_TYPE_POLYPHASE_DISCRETE: RC Roll-off
const uint32_t RCF_NUM_PHASES = 256; // RCF_TYPE_POLYPHASE_DISCRETE: number of discrete phases
const uint32_t LAGRANGE_IP_ORDER = 16; // RCF_TYPE_LAGRANGE: degree of polynomial
const radio_float_t CPR_GAIN_LEAD_AQU = (radio_float_t)5.0E-5; // 3E-5
const radio_float_t CPR_GAIN_LAG_AQU = (radio_float_t)0.4E-6; // 1E-6
const radio_float_t CPR_GAIN_LEAD_TRK = (radio_float_t)1.0E-6; // 1E-5
const radio_float_t CPR_GAIN_LAG_TRK = (radio_float_t)2.0E-8; // 5E-7
const radio_float_t STR_GAIN_LEAD_AQU = (radio_float_t)4.00E-4; // 2E-3
const radio_float_t STR_GAIN_LAG_AQU = (radio_float_t)0.50E-6; // 5E-7
const radio_float_t STR_GAIN_LEAD_TRK = (radio_float_t)4.00E-6; // 8E-4
const radio_float_t STR_GAIN_LAG_TRK = (radio_float_t)0.02E-6; // 1E-6
const radio_float_t AGC_INITIAL_VALUE = (radio_float_t)2.0;
const radio_float_t AGC_ADAPTION_RATE = (radio_float_t)0.001;
const uint32_t EQ_UPD_INTERVAL = 2000; // 1000
const uint32_t EQ_DFE_K = 63; // 63
const uint32_t EQ_GROUP_DELAY = 16; // 16
const radio_float_t DFE_MU = (radio_float_t)1E-3; // 1E-3
const radio_float_t CMA_MU = (radio_float_t)8E-2; // 8E-2
const radio_float_t TRACKER_MU = (radio_float_t)2E-2;
const radio_float_t TRACKER_EPS = (radio_float_t)1E-3;
/***************************************************************/
Receiver::Receiver(LogHandler *pLogHandler)
: m_log(pLogHandler)
, m_pPassbandBuffer(0)
, m_pBasebandBuffer(0)
, m_pSymbolBuffer(0)
, m_ReceiverEnable(false)
, m_pDataListener(0)
, m_frameReceiver(this)
, m_pFirArm(0)
, m_pFirDownArm(0)
, m_pFirRcf(0)
, m_pSymMapper(0)
, m_pPolyphaseInterpolator(0)
, m_pLagrangeInterpolator(0)
, m_pFarrowInterpolator(0)
{
m_stateArm_I = nullptr;
m_stateArm_Q = nullptr;
m_coefArm = nullptr;
m_coefRCF = nullptr;
m_params.numBitsPerSymbol = 2;
m_params.samplerate = 192000;
m_params.symbolrate = m_params.samplerate/4;
m_params.ddc_freq = m_params.samplerate/4;
m_params.CPR_phase = 0;
startTimer (1000 / 10);
}
Receiver::~Receiver(void)
{
stopTimer();
free();
}
void Receiver::addStatusListener(ReceiverStatusListener *pListener)
{
m_statusListeners.add(pListener);
}
void Receiver::addDataListener(ReceiverDataListener *pListener)
{
m_pDataListener = pListener;
}
void Receiver::setBufSize(uint32 size)
{
const ScopedLock sl (m_lock);
if (m_bufsize != size)
{
if (m_pPassbandBuffer)
delete m_pPassbandBuffer;
if (m_pBasebandBuffer)
delete m_pBasebandBuffer;
if (m_pSymbolBuffer)
delete m_pSymbolBuffer;
m_pPassbandBuffer = new cpx_t[size];
m_pBasebandBuffer = new cpx_t[size];
m_pSymbolBuffer = new sym_err_t[size];
}
m_bufsize = size;
init();
}
uint32 Receiver::getNumSoftSym()
{
return m_numSymsInBuffer;
}
sym_err_t Receiver::getSoftSym(uint32 index)
{
return m_pSymbolBuffer[index];
}
sym_err_t* Receiver::getSoftSyms()
{
return m_pSymbolBuffer;
}
cpx_t& Receiver::getTracker(uint32 index)
{
return m_trackers[index];
}
void Receiver::initDefaultParams()
{
m_params.CPR_phase = 0;
m_params.agc_state = agc_state_acquisition;
m_params.agc_mode = agc_mode_disabled;
m_params.agcMu[0] = AGC_ADAPTION_RATE;
m_params.agcMu[1] = AGC_ADAPTION_RATE/10;
m_params.agcMu_index = 0;
m_params.strState = str_state_acquisition;
m_params.str_mode = str_mode_enabled;
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_params.cprState = cpr_state_acquisition;
m_params.cpr_mode = cpr_mode_enabled;
LeadLagSetCoeff(&m_params.cpr_loopfilter_coeff[0], CPR_GAIN_LEAD_AQU, CPR_GAIN_LAG_AQU);
LeadLagSetCoeff(&m_params.cpr_loopfilter_coeff[1], CPR_GAIN_LEAD_TRK, CPR_GAIN_LAG_TRK);
m_params.cpr_loopfilter_coeff_index = 0;
m_params.eq_mode = eq_mode_disabled;
m_params.cmaType = cma_type_cma;
m_params.cmaMode = cma_mode_training_enabled;
m_params.dfeMode = dfe_mode_training_enabled;
m_params.dfeAutoUpdateEnable = true;
m_params.eqMuCma = CMA_MU;
m_params.eqMuDfe = DFE_MU;
}
#if 0
void UpdateWeigths(ComplexVector &x, ComplexVector &e, ComplexVector &w, ComplexVector &w_conj, radio_float_t mu)
{
e *= ComplexVector(mu, 0);
e.print("e * mu");
w += x * e.conj();
w.print("x * e*");
w_conj = w.conj();
w_conj.print("w*");
}
#endif
void Receiver::init()
{
const ScopedLock sl (m_lock);
m_trackers[0] = Cpx(1,1);
m_trackers[1] = Cpx(-1,1);
m_trackers[2] = Cpx(-1,-1);
m_trackers[3] = Cpx(1,-1);
m_trackers_[0] = Cpx(1,1);
m_trackers_[1] = Cpx(-1,1);
m_trackers_[2] = Cpx(-1,-1);
m_trackers_[3] = Cpx(1,-1);
initDefaultParams();
m_ReceiverEnable = false;
ClockInit(&m_symClock, NUM_BASEBAND_SAMPLES_PER_SYM);
ClockSetPhase(&m_symClock, 1);
// FIR Arm filters
initFilterArm();
// FIR RCF filters
initFilterRcf();
// NCOs
initDDC();
initCPR();
// Gardner Symbol Timing Recovery
initSTR();
// Symbol demapper
initSymbolMapper();
// AGC
AGC_Init(&agcBlind, EQ_UPD_INTERVAL, AGC_INITIAL_VALUE);
// Channel estimation filter
DFEComplexInit(&m_dfe_off, EQ_DFE_K);
DFEComplexInit(&m_dfe_on, EQ_DFE_K);
CMAInit(&m_cma, 2*EQ_DFE_K+1);
m_dfe_e_cpx.real = m_dfe_e_cpx.imag = 0.f;
cmaReset();
dfeReset();
// Power detectors
SlidingVarInit(&m_statistics.RF, EQ_UPD_INTERVAL, EQ_UPD_INTERVAL);
SlidingVarInit(&m_statistics.I_DDC, EQ_UPD_INTERVAL, EQ_UPD_INTERVAL);
SlidingVarInit(&m_statistics.Q_DDC, EQ_UPD_INTERVAL, EQ_UPD_INTERVAL);
SlidingVarInit(&m_statistics.I_MF, EQ_UPD_INTERVAL, EQ_UPD_INTERVAL);
SlidingVarInit(&m_statistics.Q_MF, EQ_UPD_INTERVAL, EQ_UPD_INTERVAL);
SlidingVarInit(&m_statistics.I_CPR, EQ_UPD_INTERVAL, EQ_UPD_INTERVAL);
SlidingVarInit(&m_statistics.Q_CPR, EQ_UPD_INTERVAL, EQ_UPD_INTERVAL);
SlidingVarInit(&m_statistics.I_AGC, EQ_UPD_INTERVAL, EQ_UPD_INTERVAL);
SlidingVarInit(&m_statistics.Q_AGC, EQ_UPD_INTERVAL, EQ_UPD_INTERVAL);
SlidingVarInit(&m_statistics.I_EQ, EQ_UPD_INTERVAL, EQ_UPD_INTERVAL);
SlidingVarInit(&m_statistics.Q_EQ, EQ_UPD_INTERVAL, EQ_UPD_INTERVAL);
SlidingVarInit(&m_statistics.I_decision, EQ_UPD_INTERVAL, EQ_UPD_INTERVAL);
SlidingVarInit(&m_statistics.Q_decision, EQ_UPD_INTERVAL, EQ_UPD_INTERVAL);
SlidingVarInit(&m_statistics.MagDecision, EQ_UPD_INTERVAL, EQ_UPD_INTERVAL);
SlidingVarInit(&m_statistics.PhiDecision, EQ_UPD_INTERVAL, EQ_UPD_INTERVAL);
SlidingVarInit(&m_statistics.noise, EQ_UPD_INTERVAL, EQ_UPD_INTERVAL);
SlidingVarInit(&m_statistics.noise_str, EQ_UPD_INTERVAL, EQ_UPD_INTERVAL);
SlidingVarInit(&m_statistics.noise_cpr, EQ_UPD_INTERVAL, EQ_UPD_INTERVAL);
SlidingVarInit(&m_statistics.noise_cma, EQ_UPD_INTERVAL, EQ_UPD_INTERVAL);
SlidingVarInit(&m_statistics.noise_dfe_on, EQ_UPD_INTERVAL, EQ_UPD_INTERVAL);
SlidingVarInit(&m_statistics.noise_dfe_off, EQ_UPD_INTERVAL, EQ_UPD_INTERVAL);
SlidingVarInit(&m_statistics.sym_err_mag, EQ_UPD_INTERVAL, EQ_UPD_INTERVAL);
SlidingVarInit(&m_statistics.sym_err_phi, EQ_UPD_INTERVAL, EQ_UPD_INTERVAL);
SlidingMinMaxInit(&m_statistics.sl_min_I, EQ_UPD_INTERVAL, (radio_float_t)1E12, -1);
SlidingMinMaxInit(&m_statistics.sl_max_I, EQ_UPD_INTERVAL, (radio_float_t)1E12, +1);
SlidingMinMaxInit(&m_statistics.sl_min_Q, EQ_UPD_INTERVAL, (radio_float_t)1E12, -1);
SlidingMinMaxInit(&m_statistics.sl_max_Q, EQ_UPD_INTERVAL, (radio_float_t)1E12, +1);
SlidingMinMaxInit(&m_statistics.sl_min_mag, EQ_UPD_INTERVAL, (radio_float_t)1E12, -1);
SlidingMinMaxInit(&m_statistics.sl_max_mag, EQ_UPD_INTERVAL, (radio_float_t)1E12, +1);
m_ReceiverEnable = true;
// --------------------------------------
// Eval
#if 0
sl_mean_t slMean;
sl_minmax_t sl_min;
sl_minmax_t sl_max;
fifo_t fifo;
uint32_t y;
radio_float_t mean;
radio_float_t min;
radio_float_t max;
radio_float_t soll_min;
radio_float_t soll_max;
radio_float_t x;
SlidingMeanInit(&slMean, 4);
SlidingMinMaxInit(&sl_min, 1000, (radio_float_t)1E12, -1);
SlidingMinMaxInit(&sl_max, 1000, (radio_float_t)1E12, +1);
for (int k=0; k < 4; k++)
{
mean = SlidingMeanProcess(&slMean, 2);
}
for (int k=0; k < 4; k++)
{
mean = SlidingMeanProcess(&slMean, 4);
}
for (int k=0; k < 4; k++)
{
mean = SlidingMeanProcess(&slMean, 0);
}
FifoInit(&fifo, 10);
for (int k=0; k < 10; k++)
{
FifoPushBack(&fifo, (uint32_t)k);
}
for (int k=0; k < 5; k++)
{
y = FifoFront(&fifo);
FifoPopFront(&fifo);
}
for (int k=0; k < 8; k++)
{
FifoPushBack(&fifo, (uint32_t)k+10);
}
FifoPushFront(&fifo, (uint32_t)123);
for (int k=0; k < 10; k++)
{
y = FifoFront(&fifo);
FifoPopFront(&fifo);
}
FifoPushBack(&fifo, (uint32_t)456);
y = FifoFront(&fifo);
// Sliding min
soll_min = 1000;
for (int k=0; k < 1000; k++)
{
x = 2*(0.5-(float)rand()/RAND_MAX);
if (x < soll_min)
soll_min = x;
SlidingMinMaxProcess(&sl_min, x);
}
min = SlidingMinMaxGet(&sl_min);
// Sliding max
soll_max = -1000;
for (int k=0; k < 1000; k++)
{
x = 2*(0.5-(float)rand()/RAND_MAX);
if (x > soll_max)
soll_max = x;
SlidingMinMaxProcess(&sl_max, x);
}
max = SlidingMinMaxGet(&sl_max);
SlidingMeanFree(&slMean);
FifoFree(&fifo);
SlidingMinMaxFree(&sl_min);
SlidingMinMaxFree(&sl_max);
MinMaxLemire minMax(100);
std::vector<float>data(100);
std::vector<float>minValues;
std::vector<float>maxValues;
for (int k=0; k < data.size(); k++)
{
data[k] = (float)k+1;
}
minMax.process(data);
minValues = minMax.getminvalues();
maxValues = minMax.getmaxvalues();
for (int k=0; k < data.size(); k++)
{
data[k] = (float)k+1+50;
}
minMax.process(data);
minValues = minMax.getminvalues();
maxValues = minMax.getmaxvalues();
// --------------------------------------
// Eval
UnitFrameReceiver rx(10);
UnitFrameTransmitter tx(10, &rx);
tx.process();
//---------------------------
// ComplexVector
//---------------------------
ComplexVector cv1(4);
ComplexVector cv2(4);
ComplexVector cv3(4);
cv1.at(0) = ComplexVector(1,2);
cv1.at(1) = ComplexVector(3,4);
cv1.at(2) = ComplexVector(5,6);
cv1.at(3) = ComplexVector(7,8);
cv2.at(0) = ComplexVector(11,12);
cv2.at(1) = ComplexVector(13,14);
cv2.at(2) = ComplexVector(15,16);
cv2.at(3) = ComplexVector(17,18);
const ComplexVector &c = -cv1;
cv3 = c;
//---------------------------
// cv3 = cv1 <op> cv2
//---------------------------
cv3 = cv1 + cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = cv1 - cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = cv1 * cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = cv1 / cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
//---------------------------
// cv3 = -cv1 <op> cv2
//---------------------------
cv3 = -cv1 + cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = -cv1 - cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = -cv1 * cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = -cv1 / cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
//---------------------------
// cv3 = cv1 <op> -cv2
//---------------------------
cv3 = cv1 + -cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = cv1 - -cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = cv1 * -cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = cv1 / -cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
//---------------------------
// cv3 = -cv1 <op> -cv2
//---------------------------
cv3 = -cv1 + -cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = -cv1 - -cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = -cv1 * -cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = -cv1 / -cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
//---------------------------
// cv3 = cv1* <op> cv2
//---------------------------
cv3 = cv1.conj() + cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = cv1.conj() - cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = cv1.conj() * cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = cv1.conj() / cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
//---------------------------
// cv3 = cv1 <op> cv2*
//---------------------------
cv3 = cv1 + cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = cv1 - cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = cv1 * cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = cv1 / cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
//---------------------------
// cv3 = cv1* <op> cv2*
//---------------------------
cv3 = cv1.conj() + cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = cv1.conj() - cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = cv1.conj() * cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = cv1.conj() / cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
//---------------------------
// cv3 = -cv1* <op> cv2
//---------------------------
cv3 = -cv1.conj() + cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = -cv1.conj() - cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = -cv1.conj() * cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = -cv1.conj() / cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
//---------------------------
// cv3 = cv1* <op> -cv2
//---------------------------
cv3 = cv1.conj() + -cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = cv1.conj() - -cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = cv1.conj() * -cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = cv1.conj() / -cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
//---------------------------
// cv3 = -cv1* <op> -cv2
//---------------------------
cv3 = -cv1.conj() + -cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = -cv1.conj() - -cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = -cv1.conj() * -cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = -cv1.conj() / -cv2;
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
//---------------------------
// cv3 = -cv1 <op> cv2*
//---------------------------
cv3 = -cv1 + cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = -cv1 - cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = -cv1 * cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = -cv1 / cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
//---------------------------
// cv3 = cv1 <op> -cv2*
//---------------------------
cv3 = cv1 + -cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = cv1 - -cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = cv1 * -cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = cv1 / -cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
//---------------------------
// cv3 = -cv1 <op> -cv2*
//---------------------------
cv3 = -cv1 + -cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = -cv1 - -cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = -cv1 * -cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = -cv1 / -cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
//---------------------------
// cv3 = -cv1* <op> cv2*
//---------------------------
cv3 = -cv1.conj() + cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = -cv1.conj() - cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = -cv1.conj() * cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = -cv1.conj() / cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
//---------------------------
// cv3 = cv1* <op> -cv2*
//---------------------------
cv3 = cv1.conj() + -cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = cv1.conj() - -cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = cv1.conj() * -cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = cv1.conj() / -cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
//---------------------------
// cv3 = -cv1* <op> -cv2*
//---------------------------
cv3 = -cv1.conj() + -cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = -cv1.conj() - -cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = -cv1.conj() * -cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
cv3 = -cv1.conj() / -cv2.conj();
cv1.print("cv1");
cv2.print("cv2");
cv3.print("cv3");
//---------------------------
// Test
//---------------------------
ComplexVector w(32);
ComplexVector w_conj(32);
ComplexVector e(32);
ComplexVector x(32);
w = ComplexVector(1, 1);
w_conj = ComplexVector(0.f, 0.f);
e = ComplexVector (0.5f, 0.4f);
x = ComplexVector(2.f, 2.f);
x *= ComplexVector(10.f, 0);
x += ComplexVector(0.5f, 0);
x -= ComplexVector(5.f, 0);
x += w + w_conj;
w.print("w");
x.print("x");
e.print("e");
UpdateWeigths(x, e, w, w_conj, 0.08);
//---------------------------
// RealVector
//---------------------------
RealVector rv1(4, 0);
RealVector rv2(4, 0);
RealVector rv3(4, 0);
rv1 = 10.f;
rv2 = 20.f;
const RealVector &r = -rv1;
rv3 = r;
rv3 = -rv1;
rv3 = -rv1 * rv2 * rv2;
rv3 = (rv1 * rv2) / (rv1 + rv2) * (rv1 - rv2);
rv1.print("v1");
rv2.print("v2");
rv3.print("v3");
rv3 = (rv1 * -rv2) / (rv1 + rv2);
rv1.print("v1");
rv2.print("v2");
rv3.print("v3");
rv3 *= -rv2;
rv1.print("v1");
rv2.print("v2");
rv3.print("v3");
rv3 += -rv2;
rv1.print("v1");
rv2.print("v2");
rv3.print("v3");
rv3 -= -rv2;
rv1.print("v1");
rv2.print("v2");
rv3.print("v3");
rv3 *= -(rv1 + rv2);
rv1.print("v1");
rv2.print("v2");
rv3.print("v3");
rv3 += -(rv1 + rv2);
rv1.print("v1");
rv2.print("v2");
rv3.print("v3");
rv3 -= -(rv1 + rv2);
rv1.print("v1");
rv2.print("v2");
rv3.print("v3");
rv3 = (rv1 + -rv2);
rv1.print("v1");
rv2.print("v2");
rv3.print("v3");
rv3 = (-rv1 + rv2);
rv1.print("v1");
rv2.print("v2");
rv3.print("v3");
rv3 = (-rv1 + -rv2);
rv1.print("v1");
rv2.print("v2");
rv3.print("v3");
rv3 = (-rv1 - rv2);
rv1.print("v1");
rv2.print("v2");
rv3.print("v3");
rv3 = (rv1 - -rv2);
rv1.print("v1");
rv2.print("v2");
rv3.print("v3");
rv3 = (-rv1 - -rv2);
rv1.print("v1");
rv2.print("v2");
rv3.print("v3");
rv3 = (-(rv1 * -rv2) * (1.f + 3.f))/(-rv3) + -6.f;
rv1.print("v1");
rv2.print("v2");
rv3.print("v3");
rv3 += -(rv1 * -rv2);
rv1.print("v1");
rv2.print("v2");
rv3.print("v3");
#endif
TraceOpen(&m_trace, "D:\\home\\jens\\Dokumente\\trace.txt");
}
void Receiver::free()
{
const ScopedLock sl (m_lock);
TraceClose(&m_trace);
m_ReceiverEnable = false;
if (m_stateArm_I != nullptr)
delete m_stateArm_I;
m_stateArm_I = nullptr;
if (m_stateArm_Q != nullptr)
delete m_stateArm_Q;
m_stateArm_Q = nullptr;
if (m_coefArm != nullptr)
delete m_coefArm;
m_coefArm = nullptr;
if (m_coefRCF != nullptr)
delete m_coefRCF;
m_coefRCF = nullptr;
if (m_pFirArm)
{
FirCpxFree(m_pFirArm);
delete m_pFirArm;
}
m_pFirArm = nullptr;
if (m_pFirDownArm)
{
FirCpxMultirateFree(m_pFirDownArm);
delete m_pFirDownArm;
}
m_pFirDownArm = nullptr;
if (m_pFirRcf)
{
FirCpxFree(m_pFirRcf);
delete m_pFirRcf;
}
m_pFirRcf = nullptr;
if (m_pPolyphaseInterpolator)
{
PolyPhaseCpxIpFree(m_pPolyphaseInterpolator);
delete m_pPolyphaseInterpolator;
}
m_pPolyphaseInterpolator = nullptr;
if (m_pLagrangeInterpolator)
{
LGCpxIpFree(m_pLagrangeInterpolator);
delete m_pLagrangeInterpolator;
}
m_pLagrangeInterpolator = nullptr;
if (m_pFarrowInterpolator)
{
FarrowPPIPCpxFree(m_pFarrowInterpolator);
delete m_pFarrowInterpolator;
}
m_pFarrowInterpolator = nullptr;
NCO_Free(&m_nco_ddc);
NCO_Free(&m_nco_cpr);
// Symbol demapper
if (m_pSymMapper)
{
SymMapFree(m_pSymMapper);
delete m_pSymMapper;
}
m_pSymMapper = nullptr;
SymStatFree(&m_sym_stat);
// Power detectors
SlidingVarFree(&m_statistics.RF);
SlidingVarFree(&m_statistics.I_DDC);
SlidingVarFree(&m_statistics.Q_DDC);
SlidingVarFree(&m_statistics.I_MF);
SlidingVarFree(&m_statistics.Q_MF);
SlidingVarFree(&m_statistics.I_CPR);
SlidingVarFree(&m_statistics.Q_CPR);
SlidingVarFree(&m_statistics.I_AGC);
SlidingVarFree(&m_statistics.Q_AGC);
SlidingVarFree(&m_statistics.I_EQ);
SlidingVarFree(&m_statistics.Q_EQ);
SlidingVarFree(&m_statistics.I_decision);
SlidingVarFree(&m_statistics.Q_decision);
SlidingVarFree(&m_statistics.MagDecision);
SlidingVarFree(&m_statistics.PhiDecision);
SlidingVarFree(&m_statistics.noise);
SlidingVarFree(&m_statistics.noise_str);
SlidingVarFree(&m_statistics.noise_cpr);
SlidingVarFree(&m_statistics.noise_cma);
SlidingVarFree(&m_statistics.noise_dfe_on);
SlidingVarFree(&m_statistics.noise_dfe_off);
SlidingVarFree(&m_statistics.sym_err_mag);
SlidingVarFree(&m_statistics.sym_err_phi);
SlidingMinMaxFree(&m_statistics.sl_min_I);
SlidingMinMaxFree(&m_statistics.sl_max_I);
SlidingMinMaxFree(&m_statistics.sl_min_Q);
SlidingMinMaxFree(&m_statistics.sl_max_Q);
SlidingMinMaxFree(&m_statistics.sl_min_mag);
SlidingMinMaxFree(&m_statistics.sl_max_mag);
// AGC
AGC_Free(&agcBlind);
// Channel estimation filter
DFEComplexFree(&m_dfe_off);
DFEComplexFree(&m_dfe_on);
CMAFree(&m_cma);
}
// The Receiver Controller
void Receiver::timerCallback()
{
static uint32_t forceStatusChangedCounter;
const ScopedLock sl (m_lock);
bool statusHasChanged = false;
if (!m_ReceiverEnable)
return;
if (!forceStatusChangedCounter)
{
statusHasChanged = true;
forceStatusChangedCounter = 10;
}
forceStatusChangedCounter--;
// DFE Auto update
if (m_params.dfeAutoUpdateEnable && (m_params.dfeMode == dfe_mode_training_enabled))
{
if ((getStatus().snrDfeOff_dB - getStatus().snrDfeOn_dB) > (radio_float_t)1.5)
{
dfeOnUpdateFromDfeOff();
statusHasChanged = true;
}
}
// Control
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;
}
// Announce status changed
if (statusHasChanged)
{
m_statusListeners.call(&ReceiverStatusListener::receiverStatusChanged, this);
}
}
void Receiver::processPassband(float *pRF, uint32_t len)
{
const ScopedLock sl (m_lock);
uint32_t len_down;
float test4normal;
if (!m_ReceiverEnable)
{
return;
}
test4normal = 0;
for (uint32_t i=0; i < len; i++)
{
test4normal += pRF[i];
}
if (isnan(test4normal))
{
return;
}
if (dabs(test4normal/len) > 10)
{
return;
}
SlidingVarProcessV(&m_statistics.RF, pRF, len);
// Digital Down Converter
// @samplerate
NCO_MixRealComplexV(&m_nco_ddc, m_pPassbandBuffer, pRF, 2/*m_params.agcGain[0]*/, len);
// Arm Filtering and downsampling
// len_down = FirCpxProcessReal(m_pFirArm, m_coefArm, m_pPassbandBuffer, m_pBasebandBuffer, len);
len_down = FirCpxDownProcess(m_pFirDownArm, m_pPassbandBuffer, m_pBasebandBuffer, len);
processBaseband(m_pBasebandBuffer, len_down);
}
void Receiver::processBaseband(float *pI, float *pQ, uint32_t len)
{
for (uint32_t i=0; i < len/2; i++)
{
m_pBasebandBuffer[i].real = pI[2*i];
m_pBasebandBuffer[i].imag = pQ[2*i];
}
processBaseband(m_pBasebandBuffer, len/2);
}
// Baseband processing
void Receiver::processBaseband(cpx_t *pIF, uint32_t len)
{
const ScopedLock sl (m_lock);
static cpx_t IQ_hard_last;
uint32_t n;
static cpx_t IQ_mf;
cpx_t IQ_agc;
cpx_t IQ_cpr;
cpx_t IQ_cma;
cpx_t IQ_dfeOn;
cpx_t IQ_dfeOff;
cpx_t IQ_eq;
cpx_t IQ_soft;
cpx_t IQ_hard;
radio_float_t e_cma;
radio_float_t e_dfe_on;
radio_float_t e_dfe_off;
radio_float_t e_agc;
sym_err_t sym_err;
radio_float_t Vd;
radio_float_t vPfdCpr;
symbol_t sym;
map_t sym_cma;
static map_t sym_dfe_off;
m_numSymsInBuffer = 0;
if (!m_ReceiverEnable)
return;
// @2 x symbolrate
for (n = 0; n < len; n++)
{
SlidingVarProcess(&m_statistics.I_DDC, pIF[n].real);
SlidingVarProcess(&m_statistics.Q_DDC, pIF[n].imag);
}
for (n = 0; n < len; n++)
{
if (RCF_TYPE == RCF_TYPE_POLYPHASE_DISCRETE)
{
// Matched filtering and interpolation
PolyPhaseCpxIpFeed(m_pPolyphaseInterpolator, 1, pIF[n]);
}
if (RCF_TYPE == RCF_TYPE_POLYPHASE_FARROW)
{
// Matched filtering and interpolation
FarrowPPIPCpxFeed(m_pFarrowInterpolator, 1, pIF[n]);
}
do
{
// Timing corrector
TimingCorrectorProcess(&m_timing_corrector, m_Vc);
#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
if (RCF_TYPE == RCF_TYPE_POLYPHASE_DISCRETE)
{
IQ_mf = PolyPhaseCpxIpInterpolate(m_pPolyphaseInterpolator, !TimingCorrectorIsStuff(&m_timing_corrector), TimingCorrectorGetMu(&m_timing_corrector));
}
if (RCF_TYPE == RCF_TYPE_POLYPHASE_FARROW)
{
IQ_mf = FarrowPPIPCpxInterpolate(m_pFarrowInterpolator, !TimingCorrectorIsStuff(&m_timing_corrector), TimingCorrectorGetMu(&m_timing_corrector));
}
// 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 = NCO_MixComplexS(&m_nco_cpr, IQ_agc, Cpx(1,1));
NCO_Process(&m_nco_cpr, m_dOmega_vco, m_params.CPR_phase);
if (TimingCorrectorIsSkip(&m_timing_corrector))
{
if (!TimingCorrectorIsStuff(&m_timing_corrector))
break;
}
// 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_cpr), m_params.agcMu[m_params.agcMu_index], 1.0);
}
if (m_params.agcMu_index == agc_state_track)
{
e_agc = CpxMagS(IQ_hard_last) - CpxMagS(IQ_cpr);
AGC_Train(&agcBlind, e_agc, m_params.agcMu[m_params.agcMu_index]);
}
}
minMaxProcess(&m_statistics.ddcMinMax, IQ_cpr);
// -----------------------------------------------------
// Equalizer Process samples
// -----------------------------------------------------
SlidingVarGet(&m_statistics.I_CPR);
IQ_cma = CMAProcess(&m_cma, IQ_cpr);
IQ_dfeOn = DFEComplexProcess(&m_dfe_on, IQ_cpr, IQ_hard_last);
IQ_eq = IQ_cpr;
if (m_params.eq_mode == eq_mode_cma)
{
IQ_eq = IQ_cma;
}
if (m_params.eq_mode == eq_mode_dfe)
{
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
CMATrainGodard(&m_cma, IQ_cma, CpxMagS(sym_cma.rect), m_pSymMapper->R2_cma, m_params.eqMuCma);
}
if (m_params.cmaType == cma_type_mma)
{
// MMA: Train
MMATrain(&m_cma, IQ_cma, m_pSymMapper->R_mma, m_params.eqMuCma);
}
if (m_params.cmaType == cma_type_smma)
{
// S-MMA: Train
SMMATrain(&m_cma, IQ_cma, CpxMagS(sym_cma.rect), m_pSymMapper->R_smma, m_params.eqMuCma);
}
}
if (m_params.dfeMode == dfe_mode_training_enabled)
{
// DFE: LMS Update coefficients
DFEAdaptLMS(&m_dfe_off, m_dfe_e_cpx, m_params.eqMuDfe); // last dfe_e_cpx
// DFE: Calc offline response
IQ_dfeOff = DFEComplexProcess(&m_dfe_off, IQ_cpr, sym_dfe_off.rect);
sym_dfe_off = SymMapGetSymbolInfo(m_pSymMapper, SymMapDemap(m_pSymMapper, IQ_soft));
// DFE: Calculate current error
m_dfe_e_cpx = CpxSubS(sym_dfe_off.rect, IQ_dfeOff);
}
// 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);
float winner_d = 1000;
int winner_i = 0;
cpx_t diff;
float 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.995f), CpxScaleRealS(m_trackers_[i], 0.005f));
}
// 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_EQ, IQ_eq.real);
SlidingVarProcess(&m_statistics.Q_EQ, IQ_eq.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);
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));
}
}
cpx_t Receiver::processMatchedFilter(cpx_t x, int32_t m, radio_float_t mu)
{
cpx_t y = x;
cpx_t z;
// Matched filtering and interpolation
if (RCF_TYPE == RCF_TYPE_POLYPHASE_DISCRETE)
{
// Polyphase matched filter
// y = PolyPhaseCpxIpProcess(m_pPolyphaseInterpolator, x, m, mu);
}
if (RCF_TYPE == RCF_TYPE_POLYPHASE_FARROW)
{
// Polyphase matched farraow filter
// y = FarrowPPIPCpxProcess2(m_pFarrowInterpolator, x, 0, 0, mu);
}
if (RCF_TYPE == RCF_TYPE_LAGRANGE)
{
// Matched filter followed by Lagrange interpolator
FirCpxProcessReal(m_pFirRcf, m_coefRCF, &x, &z, 1);
y = LGCpxIpProcess(m_pLagrangeInterpolator, z, m, mu);
}
return y;
}
int Receiver::Farrow_open_coeff(ppip_farrow_cpx_t *pObj, const char *filename)
{
int i;
FILE *pFile;
ml_farrow_coef_hdr_t hdr;
radio_float_t *pCoeff;
pFile = fopen(filename, "rb");
if (!pFile)
{
m_log.log("Can't open %s\n", filename);
return -1;
}
fread(&hdr, sizeof(hdr), 1, pFile);
FarrowPPIPCpxInit(pObj, hdr.M, hdr.N);
// pCoeff = (radio_float_t*)malloc(hdr.M*hdr.N*sizeof(radio_float_t));
for (i=0; i < (int)hdr.M; i++)
{
fread(pObj->ppCoeff[i], sizeof(radio_float_t), hdr.N, pFile);
pCoeff = pObj->ppCoeff[i];
}
return 0;
}
void Receiver::initDDC()
{
NCO_Init(&m_nco_ddc, m_params.ddc_freq/m_params.samplerate, 0);
}
void Receiver::initCPR()
{
PfdInit(&m_pfdCpr);
NCO_Init(&m_nco_cpr, 0, 0);
LeadLagInit(&m_loop_filter_cpr, 0.0);
m_dOmega_vco = 0.0;
}
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;
}
void Receiver::initSymbolMapper()
{
if (m_pSymMapper)
{
SymMapFree(m_pSymMapper);
SymStatFree(&m_sym_stat);
}
else
{
m_pSymMapper = new sym_map_t();
}
SymMapInit(m_pSymMapper, m_params.numBitsPerSymbol, MODULATION_TYPE);
SymStatInit(&m_sym_stat, m_params.numBitsPerSymbol);
m_frameReceiver.setNumBitsPerSymbol(m_params.numBitsPerSymbol);
}
void Receiver::initFilterRcf()
{
m_nrcf = (uint32_t)(RCF_OVERSAMPLING*m_params.samplerate/m_params.symbolrate)+1;
if (m_coefRCF)
delete m_coefRCF;
m_coefRCF = new radio_float_t[m_nrcf];
// Polyphase matched filter
if (RCF_TYPE == RCF_TYPE_LAGRANGE)
{
m_log.log("Calculating %d-tap SRRC matched Filter",m_nrcf, m_nrcf);
CalcFirSRRC(m_coefRCF, NUM_BASEBAND_SAMPLES_PER_SYM, (radio_float_t)NUM_BASEBAND_SAMPLES_PER_SYM, RCF_ROLLOFF, m_nrcf);
// Calc Raised Cosine Matched Filter
if (m_pFirRcf)
{
FirCpxFree(m_pFirRcf);
}
else
{
m_pFirRcf = new fir_cpx_t();
}
FirCpxInit(m_pFirRcf, m_nrcf, 1, 1);
if (m_pLagrangeInterpolator)
{
LGCpxIpFree(m_pLagrangeInterpolator);
}
else
{
m_pLagrangeInterpolator = new lgip_cpx_t();
}
LGCpxIpInit(m_pLagrangeInterpolator, LAGRANGE_IP_ORDER);
}
if (RCF_TYPE == RCF_TYPE_POLYPHASE_DISCRETE)
{
m_log.log("Calculating %d-tap %d-phase SRRC matched Filter (total %d coefficients)",m_nrcf, RCF_NUM_PHASES, m_nrcf*RCF_NUM_PHASES);
if (m_pPolyphaseInterpolator)
{
PolyPhaseCpxIpFree(m_pPolyphaseInterpolator);
}
else
{
m_pPolyphaseInterpolator = new ppip_cpx_t();
}
PolyPhaseCpxIpInit(m_pPolyphaseInterpolator, NUM_BASEBAND_SAMPLES_PER_SYM, (radio_float_t)NUM_BASEBAND_SAMPLES_PER_SYM, RCF_ROLLOFF, m_nrcf, RCF_NUM_PHASES);
}
// SRRC poly-phase matched filter with Lagrange interpolator (Farrow)
if (RCF_TYPE == RCF_TYPE_POLYPHASE_FARROW)
{
// Fixed at symbol duration 2/fs
// -> RCF_ROLLOFF and symbol rate parameter have no influence
if (m_pFarrowInterpolator)
{
FarrowPPIPCpxFree(m_pFarrowInterpolator);
delete m_pFarrowInterpolator;
}
m_pFarrowInterpolator = new ppip_farrow_cpx_t();
Farrow_open_coeff(m_pFarrowInterpolator, "/home/jens/farrow_coeff.dat");
m_log.log("Farrow filter coefficients loaded (L=%d, M=%d, N=%d)", m_pFarrowInterpolator->L, m_pFarrowInterpolator->M, m_pFarrowInterpolator->N);
}
}
void Receiver::initFilterArm()
{
uint32_t m_down;
m_narm = (uint32_t)(LPF_OVERSAMPLING*m_params.samplerate/m_params.symbolrate)+1;
if (m_stateArm_I)
delete m_stateArm_I;
if (m_stateArm_Q)
delete m_stateArm_Q;
if (m_coefArm)
delete m_coefArm;
m_stateArm_I = new radio_float_t[m_narm];
m_stateArm_Q = new radio_float_t[m_narm];
m_coefArm = new radio_float_t[m_narm];
memset(m_stateArm_I, 0, sizeof (m_narm*sizeof(radio_float_t)));
memset(m_stateArm_Q, 0, sizeof (m_narm*sizeof(radio_float_t)));
if (m_pFirArm)
{
FirCpxFree(m_pFirArm);
delete m_pFirArm;
}
m_pFirArm = new fir_cpx_t();
FirCpxInit(m_pFirArm, m_narm, 1, (uint32_t)(m_params.samplerate/(2*m_params.symbolrate)));
if (m_pFirDownArm)
{
FirCpxMultirateFree(m_pFirDownArm);
delete m_pFirDownArm;
}
m_pFirDownArm = new fir_cpx_multirate_t();
// FIR Arm filters
m_log.log("Calculating %d-tap Arm LP-Filter fc = %g Hz",m_narm, NUM_PASSBAND_SAMPLES_PER_SYM*LPF_OMEGA*m_params.symbolrate);
FIRCalcLowpass((radio_float_t)(NUM_PASSBAND_SAMPLES_PER_SYM*LPF_OMEGA*m_params.symbolrate/m_params.samplerate), m_coefArm, m_narm);
// m_log.log("Calculating %d-tap Arm BP-Filter fc = %g Hz, bw=%g Hz",m_narm, m_params.symbolrate, 4*m_params.symbolrate/3);
// FIRCalcBandpass((radio_float_t)(m_params.symbolrate/m_params.samplerate), 4*(m_params.symbolrate/m_params.samplerate)/3, m_coefArm, m_narm);
m_down = (uint32_t)(m_params.samplerate/(2*m_params.symbolrate) + 0.5);
FirCpxMultirateDownInit(m_pFirDownArm, m_coefArm, m_narm, m_down);
}
// Interface
void Receiver::setSamplerate(float samplerate_hz)
{
const ScopedLock sl (m_lock);
m_params.samplerate = samplerate_hz;
m_params.symbolrate = samplerate_hz/4;
m_params.ddc_freq = samplerate_hz/4;
initDDC();
initFilterArm();
initFilterRcf();
}
void Receiver::setParams(const params_t &params)
{
const ScopedLock sl (m_lock);
params_t lastParams = m_params;
m_params = params;
// DDC frequency has changed
if (lastParams.ddc_freq != m_params.ddc_freq)
{
initDDC();
}
// Symbol rate has changed
if (lastParams.symbolrate != m_params.symbolrate)
{
initFilterArm();
initFilterRcf();
}
// Number of bits per symbol has changed
if (MAX_NUMBITS_PERSYM < m_params.numBitsPerSymbol)
{
m_params.numBitsPerSymbol = MAX_NUMBITS_PERSYM;
}
if (lastParams.numBitsPerSymbol != m_params.numBitsPerSymbol)
{
initSymbolMapper();
}
m_statusListeners.call(&ReceiverStatusListener::receiverStatusChanged, this);
}
params_t& Receiver::getParams()
{
return m_params;
}
status_t& Receiver::getStatus()
{
static status_t status;
radio_float_t powerSoft_dB;
radio_float_t powerHard_dB;
status.frameStatRx = m_frameReceiver.getStats();
status.numSymbolsReceived = m_sym_stat.sym_cnt;
status.noiseStr = powerDB(SlidingVarGet(&m_statistics.noise_str), 1.0f);
status.noiseCpr = powerDB(SlidingVarGet(&m_statistics.noise_cpr), 1.0f);
status.agcGain[0] = AGC_GetWeight(&agcBlind);
status.powerRF_dB = powerDB(SlidingVarGet(&m_statistics.RF), 1.0f);
status.powerDDC_dB = cpxPowerDB(Cpx(SlidingVarGet(&m_statistics.I_DDC), SlidingVarGet(&m_statistics.Q_DDC)), 1.0f);
status.powerMF_dB = cpxPowerDB(Cpx(SlidingVarGet(&m_statistics.I_MF), SlidingVarGet(&m_statistics.Q_MF)), 1.0f);
status.powerCPR_dB = cpxPowerDB(Cpx(SlidingVarGet(&m_statistics.I_CPR), SlidingVarGet(&m_statistics.Q_CPR)), 1.0f);
status.powerAGC_dB = cpxPowerDB(Cpx(SlidingVarGet(&m_statistics.I_AGC), SlidingVarGet(&m_statistics.Q_AGC)), 1.0f);
status.powerEQ_dB = cpxPowerDB(Cpx(SlidingVarGet(&m_statistics.I_EQ), SlidingVarGet(&m_statistics.Q_EQ)), 1.0f);
status.powerDecison_dB = cpxPowerDB(Cpx(SlidingVarGet(&m_statistics.I_decision), SlidingVarGet(&m_statistics.Q_decision)), 1.0f);
status.powerMagDecision_dB = powerDB(SlidingVarGet(&m_statistics.MagDecision), 1);
status.powerPhiDecision_dB = powerDB(SlidingVarGet(&m_statistics.PhiDecision), 1);
powerSoft_dB = status.powerEQ_dB;
powerHard_dB = status.powerDecison_dB;
status.snrCurrent_dB = -(powerDB(SlidingVarGet(&m_statistics.noise), 1.0f) + powerSoft_dB);
status.snrCma_dB = -(powerDB(SlidingVarGet(&m_statistics.noise_cma), 1.0f) + powerSoft_dB);
status.snrDfeOn_dB = -(powerDB(SlidingVarGet(&m_statistics.noise_dfe_on), 1.0f) + powerSoft_dB);
status.snrDfeOff_dB = -(powerDB(SlidingVarGet(&m_statistics.noise_dfe_off), 1.0f) + powerSoft_dB);
status.snrSymbolMagnitude_dB = -(powerDB(SlidingVarGet(&m_statistics.sym_err_mag), 1.0f));
status.snrSymbolPhase_dB = -(powerDB(SlidingVarGet(&m_statistics.sym_err_phi), 1));
status.EB_N0 = (powerDB(m_pSymMapper->Eb, 2) - powerDB((SlidingVarGet(&m_statistics.sym_err_mag) + SlidingVarGet(&m_statistics.sym_err_phi)), 1));
status.deltaFrequencyCPR = m_params.ddc_freq + NUM_BASEBAND_SAMPLES_PER_SYM*m_params.symbolrate*LeadLagGetState(&m_loop_filter_cpr);
status.deltaFrequencySTR = m_params.symbolrate * (1-LeadLagGetState(&m_loop_filter_str));
status.ddcMinMax = m_statistics.ddcMinMax;
return status;
}
void Receiver::reset()
{
free();
init();
m_statusListeners.call(&ReceiverStatusListener::receiverStatusChanged, this);
}
void Receiver::statisticsReset()
{
m_frameReceiver.resetStats();
SymStatReset(&m_sym_stat);
m_statusListeners.call(&ReceiverStatusListener::receiverStatusChanged, this);
}
void Receiver::strReset()
{
LeadLagSetState(&m_loop_filter_str, 0);
STRGardnerInit(&m_SymbolTimingRevovery);
m_statusListeners.call(&ReceiverStatusListener::receiverStatusChanged, this);
}
void Receiver::cprReset()
{
LeadLagSetState(&m_loop_filter_cpr, 0);
m_statusListeners.call(&ReceiverStatusListener::receiverStatusChanged, this);
}
void Receiver::dfeReset()
{
CoeffComplexUnitAt(m_dfe_off.eq.pW, m_dfe_off.N, EQ_GROUP_DELAY);
CoeffComplexUnitAt(m_dfe_on.eq.pW, m_dfe_on.N, EQ_GROUP_DELAY);
CoeffComplexUnitAt(m_dfe_off.eq.pW_conj, m_dfe_off.N, EQ_GROUP_DELAY);
CoeffComplexUnitAt(m_dfe_on.eq.pW_conj, m_dfe_on.N, EQ_GROUP_DELAY);
m_statusListeners.call(&ReceiverStatusListener::receiverStatusChanged, this);
}
void Receiver::cmaReset()
{
CoeffComplexUnitAt(m_cma.eq.pW, m_cma.N, EQ_GROUP_DELAY);
m_statusListeners.call(&ReceiverStatusListener::receiverStatusChanged, this);
}
void Receiver::dfeOffUpdateFromCma()
{
memcpy(m_dfe_off.eq.pW, m_cma.eq.pW, m_cma.N*sizeof(cpx_t));
m_statusListeners.call(&ReceiverStatusListener::receiverStatusChanged, this);
}
void Receiver::dfeOnUpdateFromDfeOff()
{
memcpy(m_dfe_on.eq.pW, m_dfe_off.eq.pW, m_dfe_on.eq.N*sizeof(cpx_t));
memcpy(m_dfe_on.eq.pW_conj, m_dfe_off.eq.pW_conj, m_dfe_on.eq.N*sizeof(cpx_t));
m_statusListeners.call(&ReceiverStatusListener::receiverStatusChanged, this);
}