Beispiel #1
0
void COFDMModulation::InitInternal(CParameter& TransmParam)
{
	/* Get global parameters */
	iDFTSize = TransmParam.iFFTSizeN;
	iGuardSize = TransmParam.iGuardSize;
	iShiftedKmin = TransmParam.iShiftedKmin;

	/* Last index */
	iEndIndex = TransmParam.iShiftedKmax + 1;

	/* Normalized offset correction factor for IF shift. Subtract the
	   default IF frequency ("VIRTUAL_INTERMED_FREQ") */
	const _REAL rNormCurFreqOffset = (_REAL) -2.0 * crPi *
		(rDefCarOffset - VIRTUAL_INTERMED_FREQ) / SOUNDCRD_SAMPLE_RATE;

	/* Rotation vector for exp() calculation */
	cExpStep = _COMPLEX(cos(rNormCurFreqOffset), sin(rNormCurFreqOffset));

	/* Start with phase null (can be arbitrarily chosen) */
	cCurExp = (_REAL) 1.0;

	/* Init plans for FFT (faster processing of Fft and Ifft commands) */
	FftPlan.Init(iDFTSize);

	/* Allocate memory for intermediate result of fft. Zero out input vector
	   (because only a few bins are used, the rest has to be zero) */
	veccFFTInput.Init(iDFTSize, (CReal) 0.0);
	veccFFTOutput.Init(iDFTSize);

	/* Define block-sizes for input and output */
	iInputBlockSize = TransmParam.iNumCarrier;
	iOutputBlockSize = TransmParam.iSymbolBlockSize;
}
Beispiel #2
0
_COMPLEX CPilotModiClass::Rotate(const _COMPLEX cI, const int iCN, 
								 const int iTiDi) const
{
	/* If "iTiDi" equals "0", rArg is also "0", we need no cos or sin
	   function */
	if (iTiDi != 0)
	{
		/* First calculate the argument */
		const _REAL rArg = rArgMult * iTiDi * (iKminAbs + iCN);

		/* * exp(2 * pi * TimeDiff / norm) */
		return _COMPLEX(cos(rArg), sin(rArg)) * cI;
	}
	else
		return cI;
}
Beispiel #3
0
/******************************************************************************\
* OFDM-modulation                                                              *
\******************************************************************************/
void COFDMModulation::ProcessDataInternal(CParameter& TransmParam)
{
	int	i;

	/* Copy input vector in matlib vector and place bins at the correct
	   position */
	for (i = iShiftedKmin; i < iEndIndex; i++)
		veccFFTInput[i] = (*pvecInputData)[i - iShiftedKmin];

	/* Calculate inverse fast Fourier transformation */
	veccFFTOutput = Ifft(veccFFTInput, FftPlan);

	/* Copy complex FFT output in output buffer and scale */
	for (i = 0; i < iDFTSize; i++)
		(*pvecOutputData)[i + iGuardSize] = veccFFTOutput[i] * (CReal) iDFTSize;

	/* Copy data from the end to the guard-interval (Add guard-interval) */
	for (i = 0; i < iGuardSize; i++)
		(*pvecOutputData)[i] = (*pvecOutputData)[iDFTSize + i];


	/* Shift spectrum to desired IF ----------------------------------------- */
	/* Only apply shifting if phase is not zero */
	if (cExpStep != _COMPLEX((_REAL) 1.0, (_REAL) 0.0))
	{
		for (i = 0; i < iOutputBlockSize; i++)
		{
			(*pvecOutputData)[i] = (*pvecOutputData)[i] * Conj(cCurExp);
			
			/* Rotate exp-pointer on step further by complex multiplication
			   with precalculated rotation vector cExpStep. This saves us from
			   calling sin() and cos() functions all the time (iterative
			   calculation of these functions) */
			cCurExp *= cExpStep;
		}
	}
}
Beispiel #4
0
int CTimeLinear::Init(CParameter& Parameter)
{
	/* Init base class, must be at the beginning of this init! */
	CPilotModiClass::InitRot(Parameter);

	/* Get parameters from global struct */
	iNumCarrier = Parameter.iNumCarrier;
	iNumIntpFreqPil = Parameter.iNumIntpFreqPil;
	iScatPilFreqInt = Parameter.iScatPilFreqInt;

	/* Set length of history-buffer according to time-int-index */
	iLenHistBuff = Parameter.iScatPilTimeInt + 1;
	
	/* Init timing correction history with zeros */
	iLenTiCorrHist = iLenHistBuff - 1;
	vecTiCorrHist.Init(iLenTiCorrHist, 0);

	/* Allocate memory for channel estimation history and init with ones */
	matcChanEstHist.Init(iLenHistBuff, iNumIntpFreqPil, 
		_COMPLEX((_REAL) 1.0, (_REAL) 0.0));

	/* Return delay of channel estimation in time direction */
	return iLenHistBuff;
}
/******************************************************************************\
* OFDM cells mapping														   *
\******************************************************************************/
void COFDMCellMapping::ProcessDataInternal(CParameter& TransmParam)
{
	/* Mapping of the data and pilot cells on the OFDM symbol --------------- */
	/* Set absolute symbol position */
	int iSymbolCounterAbs =
		TransmParam.iFrameIDTransm * iNumSymPerFrame + iSymbolCounter;

	/* Init temporary counter */
	int iDummyCellCounter = 0;
	int iMSCCounter = 0;
	int iFACCounter = 0;
	int iSDCCounter = 0;
	for (int iCar = 0; iCar < iNumCarrier; iCar++)
	{
		/* MSC */
		if (_IsMSC(TransmParam.matiMapTab[iSymbolCounterAbs][iCar]))
		{
			if (iMSCCounter >= TransmParam.veciNumMSCSym[iSymbolCounterAbs])
			{
				/* Insert dummy cells */
				(*pvecOutputData)[iCar] = pcDummyCells[iDummyCellCounter];

				iDummyCellCounter++;
			}
			else
				(*pvecOutputData)[iCar] = (*pvecInputData)[iMSCCounter];
				
			iMSCCounter++;
		}

		/* FAC */
		if (_IsFAC(TransmParam.matiMapTab[iSymbolCounterAbs][iCar]))
		{
			(*pvecOutputData)[iCar] = (*pvecInputData2)[iFACCounter];
				
			iFACCounter++;
		}

		/* SDC */
		if (_IsSDC(TransmParam.matiMapTab[iSymbolCounterAbs][iCar]))
		{
			(*pvecOutputData)[iCar] = (*pvecInputData3)[iSDCCounter];
				
			iSDCCounter++;
		}

		/* Pilots */
		if (_IsPilot(TransmParam.matiMapTab[iSymbolCounterAbs][iCar]))
			(*pvecOutputData)[iCar] = 
				TransmParam.matcPilotCells[iSymbolCounterAbs][iCar];

		/* DC carrier */
		if (_IsDC(TransmParam.matiMapTab[iSymbolCounterAbs][iCar]))
			(*pvecOutputData)[iCar] = _COMPLEX((_REAL) 0.0, (_REAL) 0.0);
	}

	/* Increase symbol-counter and wrap if needed */
	iSymbolCounter++;
	if (iSymbolCounter == iNumSymPerFrame)
	{
		iSymbolCounter = 0;

		/* Increase frame-counter (ID) (Used also in FAC.cpp) */
		TransmParam.iFrameIDTransm++;
		if (TransmParam.iFrameIDTransm == NUM_FRAMES_IN_SUPERFRAME)
			TransmParam.iFrameIDTransm = 0;
	}

	/* Set absolute symbol position (for updated relative positions) */
	iSymbolCounterAbs =
		TransmParam.iFrameIDTransm * iNumSymPerFrame + iSymbolCounter;

	/* Set input block-sizes for next symbol */
	iInputBlockSize = TransmParam.veciNumMSCSym[iSymbolCounterAbs];
	iInputBlockSize2 = TransmParam.veciNumFACSym[iSymbolCounterAbs];
	iInputBlockSize3 = TransmParam.veciNumSDCSym[iSymbolCounterAbs];
}
Beispiel #6
0
/* Implementation *************************************************************/
_REAL CTimeWiener::Estimate(CVectorEx<_COMPLEX>* pvecInputData,
						    CComplexVector& veccOutputData,
						    CVector<int>& veciMapTab,
						    CVector<_COMPLEX>& veccPilotCells, _REAL rSNR)
{
	int			j, i;
	int			iPiHiIndex;
	int			iCurrFiltPhase;
	int			iTimeDiffNew;
	_COMPLEX	cNewPilot;

	/* Timing correction history -------------------------------------------- */
	/* Shift old vaules and add a "0" at the beginning of the vector */
	vecTiCorrHist.AddBegin(0);

	/* Add new one to all history values except of the current one */
	for (i = 1; i < iLenTiCorrHist; i++)
		vecTiCorrHist[i] += (*pvecInputData).GetExData().iCurTimeCorr;


	/* Update histories for channel estimates at the pilot positions -------- */
	for (i = 0; i < iNumCarrier; i++)
	{
		/* Identify and calculate transfer function at the pilot positions */
		if (_IsScatPil(veciMapTab[i]))
		{
			/* Pilots are only every "iScatPilFreqInt"'th carrier. It is not
			   possible just to increase the "iPiHiIndex" because not in all
			   cases a pilot is at position zero in "matiMapTab[]" */
			iPiHiIndex = i / iScatPilFreqInt;

			/* Save channel estimates at the pilot positions for each carrier
			   Move old estimates and put new value. Use reversed order to
			   prepare vector for convolution */
			for (j = iLengthWiener - 1; j > 0; j--)
				matcChanAtPilPos[j][iPiHiIndex] =
					matcChanAtPilPos[j - 1][iPiHiIndex];

			/* Add new channel estimate: h = r / s, h: transfer function of the
			   channel, r: received signal, s: transmitted signal */
			matcChanAtPilPos[0][iPiHiIndex] =
				(*pvecInputData)[i] / veccPilotCells[i];


			/* Estimation of the channel correlation function --------------- */
			/* We calcuate the estimation for one symbol first and average this
			   result */
			for (j = 0; j < iNumTapsSigEst; j++)
			{
				/* Correct pilot information for phase rotation */
				iTimeDiffNew = vecTiCorrHist[iScatPilTimeInt * j];
				cNewPilot = Rotate(matcChanAtPilPos[j][iPiHiIndex], i,
					iTimeDiffNew);

				/* Use IIR filtering for averaging */
				IIR1(veccTiCorrEst[j],
					Conj(matcChanAtPilPos[0][iPiHiIndex]) * cNewPilot,
					rLamTiCorrAv);
			}
		}
	}


	/* Update sigma estimation ---------------------------------------------- */
	if (bTracking == TRUE)
	{
		/* Update filter coefficients once in one DRM frame */
		if (iUpCntWienFilt > 0)
		{
			iUpCntWienFilt--;

			/* Average estimated SNR values */
			rAvSNR += rSNR;
			iAvSNRCnt++;
		}
		else
		{
			/* Actual estimation of sigma */
			rSigma = ModLinRegr(veccTiCorrEst);

			/* Use overestimated sigma for filter update */
			_REAL rSigOverEst = rSigma * SIGMA_OVERESTIMATION_FACT;

			/* Update the wiener filter, use averaged SNR */
			if (rSigOverEst < rSigmaMax)
				rMMSE = UpdateFilterCoef(rAvSNR / iAvSNRCnt, rSigOverEst);
			else
				rMMSE = UpdateFilterCoef(rAvSNR / iAvSNRCnt, rSigmaMax);

			/* If no SNR improvent is achieved by the optimal filter, use
			   SNR estimation for MMSE */
			_REAL rNewSNR = (_REAL) 1.0 / rMMSE;
			if (rNewSNR < rSNR)
				rMMSE = (_REAL) 1.0 / rSNR;

			/* Reset counter and sum (for SNR) */
			iUpCntWienFilt = iNumSymPerFrame;
			iAvSNRCnt = 0;
			rAvSNR = (_REAL) 0.0;
		}
	}


	/* Wiener interpolation, filtering and prediction ----------------------- */
	for (i = 0; i < iNumCarrier; i += iScatPilFreqInt)
	{
		/* This check is for robustness mode D since "iScatPilFreqInt" is "1"
		   in this case it would include the DC carrier in the for-loop */
		if (!_IsDC(veciMapTab[i]))
		{
			/* Pilots are only every "iScatPilFreqInt"'th carrier */
			iPiHiIndex = i / iScatPilFreqInt;

			/* Calculate current filter phase, use distance to next pilot */
			iCurrFiltPhase = (iScatPilTimeInt - DisToNextPil(iPiHiIndex,
				(*pvecInputData).GetExData().iSymbolID)) % iScatPilTimeInt;

			/* Convolution with one phase of the optimal filter */
			/* Init sum */
			_COMPLEX cCurChanEst = _COMPLEX((_REAL) 0.0, (_REAL) 0.0);
			for (j = 0; j < iLengthWiener; j++)
			{
				/* We need to correct pilots due to timing corrections ------ */
				/* Calculate timing difference */
				iTimeDiffNew =
					vecTiCorrHist[j * iScatPilTimeInt + iCurrFiltPhase] -
					vecTiCorrHist[iLenHistBuff - 1];

				/* Correct pilot information for phase rotation */
				cNewPilot = 
					Rotate(matcChanAtPilPos[j][iPiHiIndex], i, iTimeDiffNew);

				/* Actual convolution with filter phase */
				cCurChanEst +=
					cNewPilot * matrFiltTime[iCurrFiltPhase][j];
			}


			/* Copy channel estimation from current symbol in output buffer - */
			veccOutputData[iPiHiIndex] = cCurChanEst;
		}
	}

	/* Return the SNR improvement by wiener interpolation in time direction */
	return 1 / rMMSE;
}
Beispiel #7
0
int CTimeWiener::Init(CParameter& ReceiverParam)
{
	int		iNoPiFreqDirAll;
	int		iSymDelyChanEst;
	int		iNumPilOneOFDMSym;
	int		iNumIntpFreqPil;
	_REAL	rSNR;

	/* Init base class, must be at the beginning of this init! */
	CPilotModiClass::InitRot(ReceiverParam);

	/* Set local parameters */
	iNumCarrier = ReceiverParam.iNumCarrier;
	iScatPilTimeInt = ReceiverParam.iScatPilTimeInt;
	iScatPilFreqInt = ReceiverParam.iScatPilFreqInt;
	iNumSymPerFrame = ReceiverParam.iNumSymPerFrame;
	iNumIntpFreqPil = ReceiverParam.iNumIntpFreqPil;

	/* We have to consider the last pilot at the end of the symbol ("+ 1") */
	iNoPiFreqDirAll = iNumCarrier / iScatPilFreqInt + 1;

	/* Init length of filter and maximum value of sigma (doppler) */
	switch (ReceiverParam.GetWaveMode())
	{
	case RM_ROBUSTNESS_MODE_A:
		iLengthWiener = LEN_WIENER_FILT_TIME_RMA;
		rSigmaMax = MAX_SIGMA_RMA;
		break;

	case RM_ROBUSTNESS_MODE_B:
		iLengthWiener = LEN_WIENER_FILT_TIME_RMB;
		rSigmaMax = MAX_SIGMA_RMB;
		break;

	case RM_ROBUSTNESS_MODE_E:
		iLengthWiener = LEN_WIENER_FILT_TIME_RME;
		rSigmaMax = MAX_SIGMA_RME;
		break;
	}

	/* Set delay of this channel estimation type. The longer the delay is, the
	   more "acausal" pilots can be used for interpolation. We use the same
	   amount of causal and acausal filter taps here */
	iSymDelyChanEst = (iLengthWiener / 2) * iScatPilTimeInt - 1;

	/* Set number of phases for wiener filter */
	iNoFiltPhasTi = iScatPilTimeInt;

	/* Set length of history-buffer */
	iLenHistBuff = iSymDelyChanEst + 1;

	/* Duration of useful part plus-guard interval */
	Ts = (_REAL) ReceiverParam.iSymbolBlockSize / SOUNDCRD_SAMPLE_RATE;

	/* Allocate memory for Channel at pilot positions (matrix) and init with
	   ones */
	matcChanAtPilPos.Init(iLengthWiener, iNoPiFreqDirAll,
		_COMPLEX((_REAL) 1.0, (_REAL) 0.0));

	/* Set number of taps for sigma estimation */
	if (iLengthWiener < NO_TAPS_USED4SIGMA_EST)
		iNumTapsSigEst = iLengthWiener;
	else
		iNumTapsSigEst = NO_TAPS_USED4SIGMA_EST;

	/* Init vector for estimation of the correlation function in time direction
	   (IIR average) */
	veccTiCorrEst.Init(iNumTapsSigEst, (CReal) 0.0);

	/* Init time constant for IIR filter for averaging correlation estimation.
	   Consider averaging over frequency axis, too. Pilots in frequency
	   direction are "iScatPilTimeInt * iScatPilFreqInt" apart */
	iNumPilOneOFDMSym = iNumIntpFreqPil / iScatPilTimeInt;
	rLamTiCorrAv = IIR1Lam(TICONST_TI_CORREL_EST * iNumPilOneOFDMSym,
		(CReal) SOUNDCRD_SAMPLE_RATE / ReceiverParam.iSymbolBlockSize);

	/* Init Update counter for wiener filter update. We immediatly use the
	   filtered result although right at the beginning there is no averaging.
	   But sine the estimation usually starts with bigger values and goes down
	   to the correct one, this should be not critical */
	iUpCntWienFilt = iNumSymPerFrame;

	/* Init averaging of SNR values */
	rAvSNR = (_REAL) 0.0;
	iAvSNRCnt = 0;


	/* Allocate memory for filter phases (Matrix) */
	matrFiltTime.Init(iNoFiltPhasTi, iLengthWiener);

	/* Length of the timing correction history buffer */
	iLenTiCorrHist = iLengthWiener * iNoFiltPhasTi;

	/* Init timing correction history with zeros */
	vecTiCorrHist.Init(iLenTiCorrHist, 0);

	/* Get the index of first symbol in a super-frame on where the first cell
	   (carrier-index = 0) is a pilot. This is needed for determining the
	   correct filter phase for the convolution */
	iFirstSymbWithPi = 0;
	while (!_IsScatPil(ReceiverParam.matiMapTab[iFirstSymbWithPi][0]))
		iFirstSymbWithPi++;


	/* Calculate optimal filter --------------------------------------------- */
	/* Distinguish between simulation and regular receiver. When we run a
	   simulation, the parameters are taken from simulation init */
	{
		/* Init SNR value */
		rSNR = pow(10, INIT_VALUE_SNR_WIEN_TIME_DB / 10);

		/* Init sigma with a very large value. This make the acquisition more
		   robust in case of a large sample frequency offset. But we get more
		   aliasing in the time domain and this could make the timing unit
		   perform worse. Therefore, this is only a trade-off */
		rSigma = rSigmaMax * 4;
	}

	/* Calculate initialization wiener filter taps and init MMSE */
	rMMSE = UpdateFilterCoef(rSNR, rSigma);

	/* Return delay of channel equalization */
	return iLenHistBuff;
}
Beispiel #8
0
/* Implementation *************************************************************/
_REAL CTimeLinear::Estimate(CVectorEx<_COMPLEX>* pvecInputData, 
						    CComplexVector& veccOutputData, 
						    CVector<int>& veciMapTab, 
						    CVector<_COMPLEX>& veccPilotCells, _REAL rSNR)
{
	int			i, j;
	int			iPiHiIndex;
	int			iTimeDiffOld;
	int			iTimeDiffNew;
	_COMPLEX	cOldPilot;
	_COMPLEX	cNewPilot;
	_COMPLEX	cGrad;

	/* Channel estimation buffer -------------------------------------------- */
	/* Move data in channel estimation-buffer 
	   (from iLenHistBuff - 1 towards 0) */
	for (j = 0; j < iLenHistBuff - 1; j++)
	{
		for (i = 0; i < iNumIntpFreqPil; i++)
			matcChanEstHist[j][i] = matcChanEstHist[j + 1][i];
	}

	/* Clear current symbol for new channel estimates */
	for (i = 0; i < iNumIntpFreqPil; i++)
		matcChanEstHist[iLenHistBuff - 1][i] = 
			_COMPLEX((_REAL) 0.0, (_REAL) 0.0);


	/* Timing correction history -------------------------------------------- */
	/* Update timing correction history (shift register) */
	vecTiCorrHist.AddEnd(0);

	/* Add new one to all history values */
	for (i = 0; i < iLenTiCorrHist; i++)
		vecTiCorrHist[i] += (*pvecInputData).GetExData().iCurTimeCorr;


	/* Main loop ------------------------------------------------------------ */
	/* Identify and calculate transfer function at the pilot positions */
	for (i = 0; i < iNumCarrier; i++)
	{
		if (_IsScatPil(veciMapTab[i]))
		{
			/* Pilots are only every "iScatPilFreqInt"'th carrier */
			iPiHiIndex = i / iScatPilFreqInt;

			/* h = r / s, h: transfer function of channel, r: received signal, 
			   s: transmitted signal */
			matcChanEstHist[iLenHistBuff - 1][iPiHiIndex] = 
				(*pvecInputData)[i] / veccPilotCells[i];

			/* Linear interpolate in time direction from this current pilot to 
			   the corresponding pilot in the last symbol of the history */
			for (j = 1; j < iLenHistBuff - 1; j++)
			{
				/* We need to correct pilots due to timing corrections ------ */
				/* Calculate timing difference to old and new pilot */
				iTimeDiffOld = vecTiCorrHist[0]	- vecTiCorrHist[j];
				iTimeDiffNew = 0				- vecTiCorrHist[j];
				
				/* Correct pilot information for phase rotation */
				cOldPilot = Rotate(
					matcChanEstHist[0][iPiHiIndex], i, iTimeDiffOld);
				cNewPilot = Rotate(
					matcChanEstHist[iLenHistBuff - 1][iPiHiIndex], i,
					iTimeDiffNew);


				/* Linear interpolation ------------------------------------- */
				/* First, calculate gradient */
				cGrad = (cNewPilot - cOldPilot) / (_REAL) (iLenHistBuff - 1);

				/* Apply linear interpolation to cells in between */
				matcChanEstHist[j][iPiHiIndex] = cGrad * (_REAL) j + cOldPilot;
			}
		}
	}

	/* Copy channel estimation from current symbol in output buffer */
	for (i = 0; i < iNumIntpFreqPil; i++)
		veccOutputData[i] = matcChanEstHist[0][i];

	/* No SNR improvement by linear interpolation */
	return rSNR;
}
Beispiel #9
0
/* Implementation *************************************************************/
void CFreqSyncAcq::ProcessDataInternal(CParameter& ReceiverParam)
{
	int			i, j;
	int			iMaxIndex_fxp;
	FXP			rMaxValue_fxp;
	int			iNumDetPeaks_fxp;
	int			iDiffTemp;
	CReal		rLevDiff;
	_BOOLEAN	bNoPeaksLeft_fxp;
	CFRealVector vecrPSDPilPoin_fxp(3);
	FILE* pFile_fxp;

	if (bAquisition == TRUE)
	{
		/* Do not transfer any data to the next block if no frequency
		   acquisition was successfully done */
		iOutputBlockSize = 0;


		/* Add new symbol in history (shift register) */
		vecrFFTHistory_fxp.AddEnd(*pvecInputData, iInputBlockSize);


		/* Start algorithm when history memory is filled -------------------- */
		/* Wait until history memory is filled for the first FFT operation.
		   ("> 1" since, e.g., if we would have only one buffer, we can start
		   immediately) */
		if (iAquisitionCounter > 1)
		{
			/* Decrease counter */
			iAquisitionCounter--;
		}
		else
		{
			/* Copy vector to matlib vector and calculate real-valued FFTW */
			const int iStartIdx = iHistBufSize - iFrAcFFTSize;
			for (i = 0; i < iFrAcFFTSize; i++)
			{
				vecrFFTInput_fxp[i] = vecrFFTHistory_fxp[i + iStartIdx]*(((FXP) (1.0))/((FXP) (6535.0)));
			}

			/* Calculate power spectrum (X = real(F)^2 + imag(F)^2) */
			vecrSqMagFFTOut_fxp = SqMag(rfft(vecrFFTInput_fxp * vecrHammingWin_fxp, FftPlan));


			/* Calculate moving average for better estimate of PSD */
			vvrPSDMovAv_fxp.Add(vecrSqMagFFTOut_fxp);


			/* Wait until we have sufficient data for averaging */
			if (iAverageCounter > 1)
			{
				/* Decrease counter */
				iAverageCounter--;
			}
			else
			{
				/* Get PSD estimate */
				const CFRealVector vecrPSD_fxp(vvrPSDMovAv_fxp.GetAverage());


				/* -------------------------------------------------------------
				   Low pass filtering over frequency axis. We do the filtering
				   from both sides, once from right to left and then from left
				   to the right side. Afterwards, these results are averaged
				   This way, the noise floor is estimated */

// TODO: Introduce offset to debar peak at DC frequency (cause by DC offset of
// sound cards). Set "iStartFilt" in Init() routine!
const int iStartFilt = 0; // <- no offset right now

				/* Reset vectors for intermediate filtered result */
				vecrFiltResLR_fxp.Reset((CFReal) 0);
				vecrFiltResRL_fxp.Reset((CFReal) 0);

				/* From the left edge to the right edge */
				vecrFiltResLR_fxp[iStartFilt] = vecrPSD_fxp[iStartFilt];
				for (i = iStartFilt + 1; i < iHalfBuffer; i++)
				{
					vecrFiltResLR_fxp[i] = (vecrFiltResLR_fxp[i - 1] - vecrPSD_fxp[i]) *
						LAMBDA_FREQ_IIR_FILT_fxp + vecrPSD_fxp[i];
				}

				/* From the right edge to the left edge */
				vecrFiltResRL_fxp[iHalfBuffer - 1] =
					vecrPSD_fxp[iHalfBuffer - 1];

				for (i = iHalfBuffer - 2; i >= iStartFilt; i--)
				{
					vecrFiltResRL_fxp[i] = (vecrFiltResRL_fxp[i + 1] - vecrPSD_fxp[i]) *
						LAMBDA_FREQ_IIR_FILT_fxp + vecrPSD_fxp[i];
				}

				/* Average RL and LR filter outputs */
				vecrFiltRes_fxp = (vecrFiltResLR_fxp + vecrFiltResRL_fxp) * (((FXP) (1.0))/((FXP) (2.0)));



#ifdef _DEBUG_
#if 0
/* Stores curves for PSD estimation and filtering */
FILE* pFile2 = fopen("test/freqacqFilt.dat", "w");
for (i = 0; i < iHalfBuffer; i++)
	fprintf(pFile2, "%e %e\n", vecrPSD[i], vecrFiltRes[i]);
fclose(pFile2);
#endif
#endif

				/* Equalize PSD by "noise floor estimate" */
				for (i = 0; i < iHalfBuffer; i++)
				{
					/* Make sure we do not devide by zero */
					if (vecrFiltRes_fxp[i] != (FXP) 0)
						vecrPSD_fxp[i] *= (((FXP) (1.0))/(vecrFiltRes_fxp[i]));
					else
						vecrPSD_fxp[i] = 0.0;
				}

				/* Correlate known frequency-pilot structure with equalized
				   power spectrum */
				for (i = 0; i < iSearchWinSize; i++)
				{
					vecrPSDPilCor_fxp[i] =
						vecrPSD_fxp[i + veciTableFreqPilots[0]] +
						vecrPSD_fxp[i + veciTableFreqPilots[1]] +
						vecrPSD_fxp[i + veciTableFreqPilots[2]];
				}


				/* Detect peaks --------------------------------------------- */
				/* Get peak indices of detected peaks */
				iNumDetPeaks_fxp = 0;
				for (i = iStartDCSearch; i < iEndDCSearch; i++)
				{
					/* Test peaks against a bound */
					if (vecrPSDPilCor_fxp[i] > rPeakBoundFiltToSig_fxp)
					{
						veciPeakIndex_fxp[iNumDetPeaks_fxp] = i;
						iNumDetPeaks_fxp++;
					}
				}

				/* Check, if at least one peak was detected */
				if (iNumDetPeaks_fxp > 0)
					{
						/* ---------------------------------------------------------
						   The following test shall exclude sinusoid interferers in
						   the received spectrum */
						CVector<int> vecbFlagVec_fxp(iNumDetPeaks_fxp, 1);

						/* Check all detected peaks in the "PSD-domain" if there are
						   at least two peaks with approx the same power at the
						   correct places (positions of the desired pilots) */
						for (i = 0; i < iNumDetPeaks_fxp; i++)
						{
							/* Fill the vector with the values at the desired
							   pilot positions */
							vecrPSDPilPoin_fxp[0] =
								vecrPSD_fxp[veciPeakIndex_fxp[i] + veciTableFreqPilots[0]];
							vecrPSDPilPoin_fxp[1] =
								vecrPSD_fxp[veciPeakIndex_fxp[i] + veciTableFreqPilots[1]];
							vecrPSDPilPoin_fxp[2] =
								vecrPSD_fxp[veciPeakIndex_fxp[i] + veciTableFreqPilots[2]];
							/* Sort, to extract the highest and second highest
							   peak */
							vecrPSDPilPoin_fxp = Sort(vecrPSDPilPoin_fxp);

							/* Debar peak, if it is much higher than second highest
							   peak (most probably a sinusoid interferer). The
							   highest peak is stored at "vecrPSDPilPoin[2]". Also
							   test for lowest peak */
							if ((vecrPSDPilPoin_fxp[1] * (((FXP) (1.0))/vecrPSDPilPoin_fxp[2]) <
								MAX_RAT_PEAKS_AT_PIL_POS_HIGH_fxp) &&
								(vecrPSDPilPoin_fxp[0] * (((FXP) (1.0))/vecrPSDPilPoin_fxp[2]) <
								MAX_RAT_PEAKS_AT_PIL_POS_LOW_fxp))
							{
								/* Reset "good-flag" */
								vecbFlagVec_fxp[i] = 0;
							}
						}


					/* Get maximum ------------------------------------------ */
					/* First, get the first valid peak entry and init the
					   maximum with this value. We also detect, if a peak is
					   left */
					bNoPeaksLeft_fxp = TRUE;
					for (i = 0; i < iNumDetPeaks_fxp; i++)
					{
						if (vecbFlagVec_fxp[i] == 1)
						{
							/* At least one peak is left */
							bNoPeaksLeft_fxp = FALSE;

							/* Init max value */
							iMaxIndex_fxp = veciPeakIndex_fxp[i];
							rMaxValue_fxp = vecrPSDPilCor_fxp[veciPeakIndex_fxp[i]];
						}
					}


					if (bNoPeaksLeft_fxp == FALSE)
					{
						/* Actual maximum detection, take the remaining peak
						   which has the highest value */
						for (i = 0; i < iNumDetPeaks_fxp; i++)
						{
							if ((vecbFlagVec_fxp[i] == 1) &&
								(vecrPSDPilCor_fxp[veciPeakIndex_fxp[i]] >
								rMaxValue_fxp))
							{
								iMaxIndex_fxp = veciPeakIndex_fxp[i];
								rMaxValue_fxp = vecrPSDPilCor_fxp[veciPeakIndex_fxp[i]];
							}
						}




						/* -----------------------------------------------------
						   An acquisition frequency offest estimation was
						   found */
						/* Calculate frequency offset and set global parameter
						   for offset */
						ReceiverParam.rFreqOffsetAcqui =
							(_REAL) iMaxIndex_fxp / iFrAcFFTSize;

						/* Reset acquisition flag */
						bAquisition = FALSE;


						/* Send out the data stored for FFT calculation ----- */
						/* This does not work for bandpass filter. TODO: make
						   this possible for bandpass filter, too */
						if (bUseRecFilter == FALSE)
						{
							iOutputBlockSize = iHistBufSize;

							/* Frequency offset correction */
							const _REAL rNormCurFreqOffsFst = (_REAL) 2.0 * crPi *
								(ReceiverParam.rFreqOffsetAcqui - rInternIFNorm);

							for (i = 0; i < iHistBufSize; i++)
							{
								/* Multiply with exp(j omega t) */
								(*pvecOutputData)[i] = (_REAL) vecrFFTHistory_fxp[i] *
									_COMPLEX(Cos(i * rNormCurFreqOffsFst),
									Sin(-i * rNormCurFreqOffsFst));
							}

							/* Init "exp-step" for regular frequency shift which
							   is used in tracking mode to get contiuous mixing
							   signal */
							cCurExp =
								_COMPLEX(Cos(iHistBufSize * rNormCurFreqOffsFst),
								Sin(-iHistBufSize * rNormCurFreqOffsFst));
						}
					}
				}
			}
		}
	}
	else
	{
		/* If synchronized DRM input stream is used, overwrite the detected
		   frequency offest estimate by the desired frequency, because we know
		   this value */
		if (bSyncInput == TRUE)
		{
			ReceiverParam.rFreqOffsetAcqui =
				(_REAL) ReceiverParam.iIndexDCFreq / ReceiverParam.iFFTSizeN;
		}

		/* Use the same block size as input block size */
		iOutputBlockSize = iInputBlockSize;


		/* Frequency offset correction -------------------------------------- */
		/* Total frequency offset from acquisition and tracking (we calculate
		   the normalized frequency offset) */
		const _REAL rNormCurFreqOffset =
			(_REAL) 2.0 * crPi * (ReceiverParam.rFreqOffsetAcqui +
			ReceiverParam.rFreqOffsetTrack - rInternIFNorm);


		/* New rotation vector for exp() calculation */
		const _COMPLEX cExpStep =
			_COMPLEX(Cos(rNormCurFreqOffset), Sin(rNormCurFreqOffset));

		/* Input data is real, make complex and compensate for frequency
		   offset */
		for (i = 0; i < iOutputBlockSize; i++)
		{
		  (*pvecOutputData)[i] = (_REAL)(*pvecInputData)[i] * Conj(cCurExp);

			/* Rotate exp-pointer on step further by complex multiplication with
			   precalculated rotation vector cExpStep. This saves us from
			   calling sin() and cos() functions all the time (iterative
			   calculation of these functions) */
			cCurExp *= cExpStep;
		}


		/* Bandpass filter -------------------------------------------------- */
		if (bUseRecFilter == TRUE)
			BPFilter.Process(*pvecOutputData);
	}
}
void CChannelEstimation::InitInternal(CParameter& ReceiverParam)
{
	/* Get parameters from global struct */
	iScatPilTimeInt = ReceiverParam.iScatPilTimeInt;
	iScatPilFreqInt = ReceiverParam.iScatPilFreqInt;
	iNumIntpFreqPil = ReceiverParam.iNumIntpFreqPil;
	iNumCarrier = ReceiverParam.iNumCarrier;
	iFFTSizeN = ReceiverParam.iFFTSizeN;
	iNumSymPerFrame = ReceiverParam.iNumSymPerFrame;

	/* Length of guard-interval with respect to FFT-size! */
	rGuardSizeFFT = (_REAL) iNumCarrier *
		ReceiverParam.RatioTgTu.iEnum / ReceiverParam.RatioTgTu.iDenom;

	/* If robustness mode D is active, get DC position. This position cannot
	   be "0" since in mode D no 5 kHz mode is defined (see DRM-standard). 
	   Therefore we can also use this variable to get information whether
	   mode D is active or not (by simply write: "if (iDCPos != 0)") */
	iDCPos = 0;

	/* FFT must be longer than "iNumCarrier" because of zero padding effect (
	   not in robustness mode D! -> "iLongLenFreq = iNumCarrier") */
	iLongLenFreq = iNumCarrier + iScatPilFreqInt - 1;

	/* Init vector for received data at pilot positions */
	veccPilots.Init(iNumIntpFreqPil);

	/* Init vector for interpolated pilots */
	veccIntPil.Init(iLongLenFreq);

	/* Init plans for FFT (faster processing of Fft and Ifft commands) */
	FftPlanShort.Init(iNumIntpFreqPil);
	FftPlanLong.Init(iLongLenFreq);

	/* Choose time interpolation method and set pointer to correcponding 
	   object */
	switch (TypeIntTime)
	{
	case TLINEAR:
		pTimeInt = &TimeLinear;
		break;

	case TWIENER:
		pTimeInt = &TimeWiener;
		break;
	}

	/* Init time interpolation interface and set delay for interpolation */
	iLenHistBuff = pTimeInt->Init(ReceiverParam);

	/* Init time synchronization tracking unit */
	TimeSyncTrack.Init(ReceiverParam, iLenHistBuff);

	/* Set channel estimation delay in global struct. This is needed for 
	   simulation */
	ReceiverParam.iChanEstDelay = iLenHistBuff;


	/* Init window for DFT operation for frequency interpolation ------------ */
	/* Init memory */
	vecrDFTWindow.Init(iNumIntpFreqPil);
	vecrDFTwindowInv.Init(iNumCarrier);

	/* Set window coefficients */
	switch (eDFTWindowingMethod)
	{
	case DFT_WIN_RECT:
		vecrDFTWindow = Ones(iNumIntpFreqPil);
		vecrDFTwindowInv = Ones(iNumCarrier);
		break;

	case DFT_WIN_HAMM:
		vecrDFTWindow = Hamming(iNumIntpFreqPil);
		vecrDFTwindowInv = (CReal) 1.0 / Hamming(iNumCarrier);
		break;
	}


	/* Set start index for zero padding in time domain for DFT method */
	iStartZeroPadding = (int) rGuardSizeFFT;
	if (iStartZeroPadding > iNumIntpFreqPil)
		iStartZeroPadding = iNumIntpFreqPil;

	/* Allocate memory for channel estimation */
	veccChanEst.Init(iNumCarrier);

	/* Allocate memory for history buffer (Matrix) and zero out */
	matcHistory.Init(iLenHistBuff, iNumCarrier,
		_COMPLEX((_REAL) 0.0, (_REAL) 0.0));

	/* After an initialization we do not put out data befor the number symbols
	   of the channel estimation delay have been processed */
	iInitCnt = iLenHistBuff - 1;

	/* Inits for SNR estimation (noise and signal averages) */
	rSNREstimate = (_REAL) pow(10.0, INIT_VALUE_SNR_ESTIM_DB / 10);
	rSignalEst = (_REAL) 0.0;
	rNoiseEst = (_REAL) 0.0;

	/* For SNR estimation initialization */
	iNumCellsSNRInit = iNumSymPerFrame * iNumCarrier; /* 1 DRM frame */
	iSNREstInitCnt = iNumCellsSNRInit;
	bWasSNRInit = TRUE;

	/* Lambda for IIR filter */
	rLamSNREstFast = IIR1Lam(TICONST_SNREST_FAST, (CReal) SOUNDCRD_SAMPLE_RATE /
		ReceiverParam.iSymbolBlockSize);
	rLamSNREstSlow = IIR1Lam(TICONST_SNREST_SLOW, (CReal) SOUNDCRD_SAMPLE_RATE /
		ReceiverParam.iSymbolBlockSize);


	/* SNR correction factor. We need this factor since we evalute the 
	   signal-to-noise ratio only on the pilots and these have a higher power as
	   the other cells */
	rSNRCorrectFact = 
		ReceiverParam.rAvPilPowPerSym /	ReceiverParam.rAvPowPerSymbol;

	/* Init delay spread length estimation (index) */
	rLenPDSEst = (_REAL) 0.0;

	/* Init maximum estimated delay spread and offset in one DRM frame */
	rMaxLenPDSInFra = (_REAL) 0.0;
	rMinOffsPDSInFra = rGuardSizeFFT;

	/* Inits for Wiener interpolation in frequency direction ---------------- */
	/* Length of wiener filter */
	switch (ReceiverParam.GetWaveMode())
	{
	case RM_ROBUSTNESS_MODE_A:
		iLengthWiener = LEN_WIENER_FILT_FREQ_RMA;
		break;

	case RM_ROBUSTNESS_MODE_B:
		iLengthWiener = LEN_WIENER_FILT_FREQ_RMB;
		break;

	case RM_ROBUSTNESS_MODE_E:
		iLengthWiener = LEN_WIENER_FILT_FREQ_RME;
		break;
	}


	/* Inits for wiener filter ---------------------------------------------- */
	/* In frequency direction we can use pilots from both sides for 
	   interpolation */
	iPilOffset = iLengthWiener / 2;

	/* Allocate memory */
	matcFiltFreq.Init(iNumCarrier, iLengthWiener);

	/* Pilot offset table */
	veciPilOffTab.Init(iNumCarrier);

	/* Number of different wiener filters */
	iNoWienerFilt = (iLengthWiener - 1) * iScatPilFreqInt + 1;

	/* Allocate temporary matlib vector for filter coefficients */
	matcWienerFilter.Init(iNoWienerFilt, iLengthWiener);

#ifdef UPD_WIENER_FREQ_EACH_DRM_FRAME
	/* Init Update counter for wiener filter update */
	iUpCntWienFilt = iNumSymPerFrame;
#endif

	/* Initial Wiener filter. Use initial SNR definition and assume that the
	   PDS ranges from the beginning of the guard-intervall to the end */
	UpdateWienerFiltCoef(pow(10.0, INIT_VALUE_SNR_WIEN_FREQ_DB / 10),
		(_REAL) ReceiverParam.RatioTgTu.iEnum / 
		ReceiverParam.RatioTgTu.iDenom, (CReal) 0.0);


	/* Define block-sizes for input and output */
	iInputBlockSize = iNumCarrier;
	iMaxOutputBlockSize = iNumCarrier; 
}
/* Implementation *************************************************************/
void CChannelEstimation::ProcessDataInternal(CParameter& ReceiverParam)
{
	int			i, j, k;
	int			iModSymNum;
	_COMPLEX	cModChanEst;
	_REAL		rSNRAftTiInt;
	_REAL		rCurSNREst;
	_REAL		rOffsPDSEst;

	/* Check if symbol ID index has changed by the synchronization unit. If it
	   has changed, reinit this module */
	if ((*pvecInputData).GetExData().bSymbolIDHasChanged == TRUE)
	{
		SetInitFlag();
		return;
	}

	/* Move data in history-buffer (from iLenHistBuff - 1 towards 0) */
	for (j = 0; j < iLenHistBuff - 1; j++)
	{
		for (i = 0; i < iNumCarrier; i++)
			matcHistory[j][i] = matcHistory[j + 1][i];
	}

	/* Write new symbol in memory */
	for (i = 0; i < iNumCarrier; i++)
		matcHistory[iLenHistBuff - 1][i] = (*pvecInputData)[i];


	/* Time interpolation *****************************************************/
	/* Get symbol-counter for next symbol. Use the count from the frame 
	   synchronization (in OFDM.cpp). Call estimation routine */
	rSNRAftTiInt = 
		pTimeInt->Estimate(pvecInputData, veccPilots, 
						   ReceiverParam.matiMapTab[(*pvecInputData).
						   GetExData().iSymbolID],
						   ReceiverParam.matcPilotCells[(*pvecInputData).
						   GetExData().iSymbolID], rSNREstimate);

	/* Debar initialization of channel estimation in time direction */
	if (iInitCnt > 0)
	{
		iInitCnt--;

		/* Do not put out data in initialization phase */
		iOutputBlockSize = 0;

		/* Do not continue */
		return;
	}
	else
		iOutputBlockSize = iNumCarrier; 

	/* Define DC carrier for robustness mode D because there is not pilot */
	if (iDCPos != 0)
		veccPilots[iDCPos] = (CReal) 0.0;


	/* -------------------------------------------------------------------------
	   Use time-interpolated channel estimate for timing synchronization 
	   tracking */
	TimeSyncTrack.Process(ReceiverParam, veccPilots, 
		(*pvecInputData).GetExData().iCurTimeCorr, rLenPDSEst /* out */,
		rOffsPDSEst /* out */);


	/* Frequency-interploation ************************************************/
	switch (TypeIntFreq)
	{
	case FLINEAR:
		/**********************************************************************\
		 * Linear interpolation												  *
		\**********************************************************************/
		/* Set first pilot position */
		veccChanEst[0] = veccPilots[0];

		for (j = 0, k = 1; j < iNumCarrier - iScatPilFreqInt;
			j += iScatPilFreqInt, k++)
		{
			/* Set values at second time pilot position in cluster */
			veccChanEst[j + iScatPilFreqInt] = veccPilots[k];

			/* Interpolation cluster */
			for (i = 1; i < iScatPilFreqInt; i++)
			{
				/* E.g.: c(x) = (c_4 - c_0) / 4 * x + c_0 */
				veccChanEst[j + i] =
					(veccChanEst[j + iScatPilFreqInt] - veccChanEst[j]) /
					(_REAL) (iScatPilFreqInt) * (_REAL) i + veccChanEst[j];
			}
		}
		break;

	case FDFTFILTER:
		/**********************************************************************\
		 * DFT based algorithm												  *
		\**********************************************************************/
		/* ---------------------------------------------------------------------
		   Put all pilots at the beginning of the vector. The "real" length of
		   the vector "pcFFTWInput" is longer than the No of pilots, but we 
		   calculate the FFT only over "iNumCarrier / iScatPilFreqInt + 1"
		   values (this is the number of pilot positions) */
		/* Weighting pilots with window */
		veccPilots *= vecrDFTWindow;

		/* Transform in time-domain */
		veccPilots = Ifft(veccPilots, FftPlanShort);

		/* Set values outside a defined bound to zero, zero padding (noise
		   filtering). Copy second half of spectrum at the end of the new vector
		   length and zero out samples between the two parts of the spectrum */
		veccIntPil.Merge(
			/* First part of spectrum */
			veccPilots(1, iStartZeroPadding), 
			/* Zero padding in the middle, length: Total length minus length of
			   the two parts at the beginning and end */
			CComplexVector(Zeros(iLongLenFreq - 2 * iStartZeroPadding), 
			Zeros(iLongLenFreq - 2 * iStartZeroPadding)), 
			/* Set the second part of the actual spectrum at the end of the new
			   vector */
			veccPilots(iNumIntpFreqPil - iStartZeroPadding + 1, 
			iNumIntpFreqPil));

		/* Transform back in frequency-domain */
		veccIntPil = Fft(veccIntPil, FftPlanLong);

		/* Remove weighting with DFT window by inverse multiplication */
		veccChanEst = veccIntPil(1, iNumCarrier) * vecrDFTwindowInv;
		break;

	case FWIENER:
		/**********************************************************************\
		 * Wiener filter													   *
		\**********************************************************************/
#ifdef UPD_WIENER_FREQ_EACH_DRM_FRAME
		/* Update filter coefficients once in one DRM frame */
		if (iUpCntWienFilt > 0)
		{
			iUpCntWienFilt--;

			/* Get maximum delay spread and offset in one DRM frame */
			if (rLenPDSEst > rMaxLenPDSInFra)
				rMaxLenPDSInFra = rLenPDSEst;

			if (rOffsPDSEst < rMinOffsPDSInFra)
				rMinOffsPDSInFra = rOffsPDSEst;
		}
		else
		{
#else
		/* Update Wiener filter each OFDM symbol. Use current estimates */
		rMaxLenPDSInFra = rLenPDSEst;
		rMinOffsPDSInFra = rOffsPDSEst;
#endif
			/* Update filter taps */
			UpdateWienerFiltCoef(rSNRAftTiInt, rMaxLenPDSInFra / iNumCarrier,
				rMinOffsPDSInFra / iNumCarrier);

#ifdef UPD_WIENER_FREQ_EACH_DRM_FRAME
			/* Reset counter and maximum storage variable */
			iUpCntWienFilt = iNumSymPerFrame;
			rMaxLenPDSInFra = (_REAL) 0.0;
			rMinOffsPDSInFra = rGuardSizeFFT;
		}
#endif

		/* FIR filter of the pilots with filter taps. We need to filter the
		   pilot positions as well to improve the SNR estimation (which 
		   follows this procedure) */
		for (j = 0; j < iNumCarrier; j++)
		{
			/* Convolution */
			veccChanEst[j] = _COMPLEX((_REAL) 0.0, (_REAL) 0.0);

			for (i = 0; i < iLengthWiener; i++)
				veccChanEst[j] += 
					matcFiltFreq[j][i] * veccPilots[veciPilOffTab[j] + i];
		}
		break;
	}


	/* Equalize the output vector ------------------------------------------- */
	/* Write to output vector. Take oldest symbol of history for output. Also,
	   ship the channel state at a certain cell */
	for (i = 0; i < iNumCarrier; i++)
	{
		(*pvecOutputData)[i].cSig = matcHistory[0][i] / veccChanEst[i];
		(*pvecOutputData)[i].rChan = SqMag(veccChanEst[i]);
	}


	/* -------------------------------------------------------------------------
	   Calculate symbol ID of the current output block and set parameter */
	(*pvecOutputData).GetExData().iSymbolID = 
		(*pvecInputData).GetExData().iSymbolID - iLenHistBuff + 1;


	/* SNR estimation ------------------------------------------------------- */
	/* Modified symbol ID, check range {0, ..., iNumSymPerFrame} */
	iModSymNum = (*pvecOutputData).GetExData().iSymbolID;

	while (iModSymNum < 0)
		iModSymNum += iNumSymPerFrame;

	for (i = 0; i < iNumCarrier; i++)
	{
		switch (TypeSNREst)
		{
		case SNR_PIL:
			/* Use estimated channel and compare it to the received pilots. This
			   estimation works only if the channel estimation was successful */
			/* Identify pilot positions. Use MODIFIED "iSymbolID" (See lines
			   above) */
			if (_IsScatPil(ReceiverParam.matiMapTab[iModSymNum][i]))
			{
				/* We assume that the channel estimation in "veccChanEst" is
				   noise free (e.g., the wiener interpolation does noise
				   reduction). Thus, we have an estimate of the received signal
				   power \hat{r} = s * \hat{h}_{wiener} */
				cModChanEst = veccChanEst[i] *
					ReceiverParam.matcPilotCells[iModSymNum][i];


				/* Calculate and average noise and signal estimates --------- */
				/* The noise estimation is difference between the noise reduced
				   signal and the noisy received signal
				   \tilde{n} = \hat{r} - r */
				IIR1(rNoiseEst, SqMag(matcHistory[0][i] - cModChanEst),
					rLamSNREstFast);

				/* The received signal power estimation is just \hat{r} */
				IIR1(rSignalEst, SqMag(cModChanEst), rLamSNREstFast);

				/* Calculate final result (signal to noise ratio) */
				if (rNoiseEst != 0)
					rCurSNREst = rSignalEst / rNoiseEst;
				else
					rCurSNREst = (_REAL) 1.0;

				/* Bound the SNR at 0 dB */
				if (rCurSNREst < (_REAL) 1.0)
					rCurSNREst = (_REAL) 1.0;

				/* Average the SNR with a two sided recursion */
				IIR1TwoSided(rSNREstimate, rCurSNREst, rLamSNREstFast,
					rLamSNREstSlow);
			}
			break;

		case SNR_FAC:
			/* SNR estimation with initialization */
			if (iSNREstInitCnt > 0)
			{
				/* Initial signal estimate. Use channel estimation from all
				   cells. Apply averaging */
				rSignalEst += (*pvecOutputData)[i].rChan;

				iSNREstInitCnt--;
			}
			else
			{
				/* Only right after initialization phase apply initial SNR
				   value */
				if (bWasSNRInit == TRUE)
				{
					/* Normalize average */
					rSignalEst /= iNumCellsSNRInit;

					/* Apply initial SNR value */
					rNoiseEst = rSignalEst / rSNREstimate;

					bWasSNRInit = FALSE;
				}

				/* Only use FAC cells for this SNR estimation method */
				if (_IsFAC(ReceiverParam.matiMapTab[iModSymNum][i]))
				{
					/* Get tentative decision for current FAC cell (squared) */
					CReal rCurErrPow =
						TentativeFACDec((*pvecOutputData)[i].cSig);

					/* Use decision together with channel estimate to get
					   estimates for signal and noise */
					IIR1(rNoiseEst, rCurErrPow * (*pvecOutputData)[i].rChan,
						rLamSNREstFast);

					IIR1(rSignalEst, (*pvecOutputData)[i].rChan,
						rLamSNREstFast);

					/* Calculate final result (signal to noise ratio) */
					if (rNoiseEst != (_REAL) 0.0)
						rCurSNREst = rSignalEst / rNoiseEst;
					else
						rCurSNREst = (_REAL) 1.0;

					/* Bound the SNR at 0 dB */
					if (rCurSNREst < (_REAL) 1.0)
						rCurSNREst = (_REAL) 1.0;

					/* The channel estimation algorithms need the SNR normalized
					   to the energy of the pilots */
					rSNREstimate = rCurSNREst / rSNRCorrectFact;
				}
			}
			break;
		}
	}
}