/******************************************************************************\ * OFDM-demodulation * \******************************************************************************/ void COFDMDemodulation::ProcessDataInternal(CParameter& ReceiverParam) { int i; /* Copy data from CVector in CMatlib vector */ for (i = 0; i < iInputBlockSize; i++) veccFFTInput[i] = (*pvecInputData)[i]; /* Calculate Fourier transformation (actual OFDM demodulation) */ veccFFTOutput = Fft(veccFFTInput, FftPlan); /* Use only useful carriers and normalize with the block-size ("N"). Check if spectrum can be cut in one step or two steps */ if (iShiftedKmin < 0) { /* Spectrum must be cut in two steps since some parts are on the left side of the DC frequency */ for (i = iShiftedKmin; i < 0; i++) { (*pvecOutputData)[i - iShiftedKmin] = veccFFTOutput[iDFTSize + i] / (CReal) iDFTSize; } for (i = 0; i < iShiftedKmax + 1; i++) { (*pvecOutputData)[i - iShiftedKmin] = veccFFTOutput[i] / (CReal) iDFTSize; } } else { /* DRM spectrum is completely on the right side of the DC carrier and can be cut in one step */ for (i = iShiftedKmin; i < iShiftedKmax + 1; i++) { (*pvecOutputData)[i - iShiftedKmin] = veccFFTOutput[i] / (CReal) iDFTSize; } } /* Save averaged spectrum for plotting ---------------------------------- */ /* Average power (using power of this tap) (first order IIR filter) */ for (i = 0; i < iLenPowSpec; i++) IIR1(vecrPowSpec[i], SqMag(veccFFTOutput[i]), rLamPSD); }
void CNoiseReduction::UpdateNoiseEst(CRealVector& vecrNoisePSD, const CRealVector& vecrSqMagSigFreq, const ENoiRedDegree eNoiRedDegree) { /* Implements a mimium statistic proposed by R. Martin */ /* Set weightning factor for minimum statistic */ CReal rWeiFact; switch (eNoiRedDegree) { case NR_LOW: rWeiFact = MIN_STAT_WEIGTH_FACTOR_LOW; break; case NR_MEDIUM: rWeiFact = MIN_STAT_WEIGTH_FACTOR_MED; break; case NR_HIGH: rWeiFact = MIN_STAT_WEIGTH_FACTOR_HIGH; break; } /* Update signal PSD estimation (first order IIR filter) */ IIR1(vecrSigPSD, vecrSqMagSigFreq, rLamPSD); for (int i = 0; i < iFreqBlLen; i++) { // TODO: Update of minimum statistic can be done much more efficient /* Update history */ matrMinimumStatHist[i].Merge( matrMinimumStatHist[i](2, iMinStatHistLen), vecrSigPSD[i]); /* Minimum values in history are taken for noise estimation */ vecrNoisePSD[i] = Min(matrMinimumStatHist[i]) * rWeiFact; } }
/* Implementation *************************************************************/ void CTimeSyncTrack::Process(CParameter& Parameter, CComplexVector& veccChanEst, int iNewTiCorr, FXP& rLenPDS_fxp, FXP& rOffsPDS_fxp) { int i, j; int iIntShiftVal; int iFirstPathDelay; CFReal rPeakBound_fxp; CFReal rPropGain_fxp; long long rCurEnergy_fxp; long long rWinEnergy_fxp; long long rMaxWinEnergy_fxp; _BOOLEAN bDelayFound; //ok _BOOLEAN bPDSResultFound; //ok /* Rotate the averaged PDP to follow the time shifts -------------------- */ /* Update timing correction history (shift register) */ vecTiCorrHist.AddEnd(iNewTiCorr); /* Calculate the actual shift of the timing correction. Since we do the timing correction at the sound card sample rate (48 kHz) and the estimated impulse response has a different sample rate (since the spectrum is only one little part of the sound card frequency range) we have to correct the timing correction by a certain bandwidth factor */ const CFReal rActShiftTiCor_fxp = rFracPartTiCor_fxp - (_FREAL) vecTiCorrHist[0] * iNumCarrier / iDFTSize; /* Integer part of shift */ const int iIntPartTiCorr = (int) Round(rActShiftTiCor_fxp); /* Extract the fractional part since we can only correct integer timing shifts */ rFracPartTiCor_fxp = rActShiftTiCor_fxp - iIntPartTiCorr; /* Shift the values in the vector storing the averaged impulse response. We have to consider two cases for shifting (left and right shift) */ if (rActShiftTiCor_fxp < 0) iIntShiftVal = iIntPartTiCorr + iNumIntpFreqPil; else iIntShiftVal = iIntPartTiCorr; /* If new correction is out of range, do not apply rotation */ if ((iIntShiftVal > 0) && (iIntShiftVal < iNumIntpFreqPil)) { /* Actual rotation of vector */ vecrAvPoDeSp.Merge(vecrAvPoDeSp(iIntShiftVal + 1, iNumIntpFreqPil), vecrAvPoDeSp(1, iIntShiftVal)); } /* New estimate for impulse response ------------------------------------ */ /* Apply hamming window, Eq (15) */ veccPilots = veccChanEst * vecrHammingWindow; /* Transform in time-domain to get an estimate for the delay power profile, Eq (15) */ veccPilots = Ifft(veccPilots, FftPlan); /* Average result, Eq (16) (Should be a moving average function, for simplicity we have chosen an IIR filter here) */ IIR1(vecrAvPoDeSp, SqMag(veccPilots), rLamAvPDS); /* Rotate the averaged result vector to put the earlier peaks (which can also detected in a certain amount) at the beginning of the vector */ vecrAvPoDeSpRot.Merge(vecrAvPoDeSp(iStPoRot, iNumIntpFreqPil), vecrAvPoDeSp(1, iStPoRot - 1)); /* Different timing algorithms ------------------------------------------ */ switch (TypeTiSyncTrac) { case TSFIRSTPEAK: /* Detect first peak algorithm proposed by Baoguo Yang */ /* Lower and higher bound */ rBoundHigher = Max(vecrAvPoDeSpRot) * rConst1; //ok rBoundLower = Min(vecrAvPoDeSpRot) * rConst2; //ok /* Calculate the peak bound, Eq (19) */ rPeakBound_fxp = FXP (Max(rBoundHigher, rBoundLower)); /* Get final estimate, Eq (18) */ bDelayFound = FALSE; /* Init flag */ for (i = 0; i < iNumIntpFreqPil - 1; i++) { /* We are only interested in the first peak */ if (bDelayFound == FALSE) { if ((vecrAvPoDeSpRot[i] > vecrAvPoDeSpRot[i + 1]) && (FXP (vecrAvPoDeSpRot[i]) > rPeakBound_fxp)) { /* The first peak was found, store index */ iFirstPathDelay = i; /* Set the flag */ bDelayFound = TRUE; } } } break; case TSENERGY: /* Determin position of window with maximum energy in guard-interval. A window with the size of the guard-interval is moved over the entire profile and the energy inside this window is calculated. The window position which maximises this energy is taken as the new timing position */ rMaxWinEnergy_fxp = 0; iFirstPathDelay = 0; for (i = 0; i < iNumIntpFreqPil - 1 - rGuardSizeFFT; i++) { rWinEnergy_fxp = 0; /* Energy IN the guard-interval */ for (j = 0; j < rGuardSizeFFT; j++) rWinEnergy_fxp += (long long)(vecrAvPoDeSpRot[i + j]*(1<<FXP_TIME_SYNC_TRACK_SCALE)); /* Get maximum */ if (rWinEnergy_fxp > rMaxWinEnergy_fxp) { rMaxWinEnergy_fxp = rWinEnergy_fxp; iFirstPathDelay = i; } } /* We always have a valid measurement, set flag */ bDelayFound = TRUE; break; } /* Only apply timing correction if search was successful and tracking is activated */ if ((bDelayFound == TRUE) && (bTiSyncTracking == TRUE)) { /* Consider the rotation introduced for earlier peaks in path delay. Since the "iStPoRot" is the position of the beginning of the block at the end for cutting out, "iNumIntpFreqPil" must be substracted. (Actually, a part of the following line should be look like this: "iStPoRot - 1 - iNumIntpFreqPil + 1" but the "- 1 + 1" compensate each other) */ iFirstPathDelay += iStPoRot - iNumIntpFreqPil - iTargetTimingPos - 1; /* Correct timing offset -------------------------------------------- */ /* Final offset is target position in comparision to the estimated first path delay. Since we have a delay from the channel estimation, the previous correction is subtracted "- vecrNewMeasHist[0]". If the "real" correction arrives after the delay, this correction is compensated. The length of the history buffer (vecrNewMeasHist) must be equal to the delay of the channel estimation. The corrections must be quantized to the upsampled output sample rate ("* iDFTSize / iNumCarrier") */ iDFTSize / iNumCarrier - veciNewMeasHist[0]; const CFReal rTiOffset_fxp = (CFReal) -iFirstPathDelay * iDFTSize / iNumCarrier - veciNewMeasHist[0]; /* Different controlling parameters for different types of tracking */ switch (TypeTiSyncTrac) { case TSFIRSTPEAK: /* Adapt the linear control parameter to the region, where the peak was found. The region left of the desired timing position is critical, because we immediately get ISI if a peak appers here. Therefore we apply fast correction here. At the other positions, we smooth the controlling to improve the immunity against false peaks */ if (rTiOffset_fxp > 0) rPropGain_fxp = CONT_PROP_BEFORE_GUARD_INT; else rPropGain_fxp = CONT_PROP_IN_GUARD_INT; break; case TSENERGY: rPropGain_fxp = CONT_PROP_ENERGY_METHOD; break; } /* In case of sample rate offset acquisition phase, use faster timing corrections */ if (bSamRaOffsAcqu == TRUE) rPropGain_fxp *= 2; /* Apply proportional control and fix result to sample grid */ const CFReal rCurCorrValue_fxp = rTiOffset_fxp * rPropGain_fxp + rFracPartContr_fxp; const int iContrTiOffs = (int) Fix(rCurCorrValue_fxp); /* Calculate new fractional part of controlling */ rFracPartContr_fxp = rCurCorrValue_fxp - iContrTiOffs; /* Manage correction history */ veciNewMeasHist.AddEnd(0); for (i = 0; i < iSymDelay - 1; i++) veciNewMeasHist[i] += iContrTiOffs; /* Apply correction */ Parameter.iTimingOffsTrack = -iContrTiOffs; } /* Sample rate offset estimation ---------------------------------------- */ /* This sample rate offset estimation is based on the movement of the estimated PDS with time. The movement per symbol (or a number of symbols) is proportional to the sample rate offset. It has to be considered the PDS shiftings of the timing correction unit ("rActShiftTiCor" can be used for that). The procedere is to detect the maximum peak in the PDS and use this as a reference, assuming tha delay of this peak is not changing. The problem is when another peak get higher than this due to fading. In this case we must adjust the history to this new peak (the new reference) */ int iMaxInd; CReal rMax; //int rMax_fxp; /* Find index of maximum peak in PDS estimation. This is our reference for this estimation method */ Max(rMax, iMaxInd, vecrAvPoDeSpRot); /* Integration of timing corrections FIXME: Check for overrun of "iIntegTiCorrections" variable! */ iIntegTiCorrections += (long long) iIntPartTiCorr; /* We need to consider the timing corrections done by the timing unit. What we want to estimate is only the real movement of the detected maximum peak */ const long long iCurRes = iIntegTiCorrections + iMaxInd; veciSRTiCorrHist.AddEnd(iCurRes); /* We assumed that the detected peak is always the same peak in the actual PDS. But due to fading the maximum can change to a different peak. In this case the estimation would be wrong. We try to detect the detection of a different peak by defining a maximum sample rate change. The sample rate offset is very likely to be very constant since usually crystal oscialltors are used. Thus, if a larger change of sample rate offset happens, we assume that the maximum peak has changed */ const long long iNewDiff = veciSRTiCorrHist[iLenCorrectionHist - 2] - iCurRes; /* If change is larger than 2, it is most likely that a new peak was chosen by the maximum function. Also, if the sign has changed of the difference (and it is not zero), we also say that a new peak was selected */ if ((llabs(iNewDiff) > 2) || ((Sign(iOldNonZeroDiff) != Sign(iNewDiff)) && (iNewDiff != 0))) { /* Correct the complete history to the new reference peak. Reference peak was already added, therefore do not use last element */ for (i = 0; i < iLenCorrectionHist - 1; i++) veciSRTiCorrHist[i] -= iNewDiff; } /* Store old difference if it is not zero */ if (iNewDiff != 0) iOldNonZeroDiff = iNewDiff; /* Check, if we are in acquisition phase */ if (iResOffsetAcquCnt > 0) { /* Acquisition phase */ iResOffsetAcquCnt--; } else { /* Apply the result from acquisition only once */ if (bSamRaOffsAcqu == TRUE) { /* End of acquisition phase */ bSamRaOffsAcqu = FALSE; /* Set sample rate offset to initial estimate. We consider the initialization phase of channel estimation by "iSymDelay" */ /*rInitSamOffset = FXP GetSamOffHz(iCurRes - veciSRTiCorrHist[ iLenCorrectionHist - (iResOffAcqCntMax - iSymDelay)], iResOffAcqCntMax - iSymDelay - 1); */ CFReal rInitSamOffset = FXP (GetSamOffHz(iCurRes - veciSRTiCorrHist[ iLenCorrectionHist - (iResOffAcqCntMax - iSymDelay)], iResOffAcqCntMax - iSymDelay - 1)); #ifndef USE_SAMOFFS_TRACK_FRE_PIL /* Apply initial sample rate offset estimation */ // (Parameter.rResampleOffset) -= rInitSamOffset; FXP (Parameter.rResampleOffset) -= rInitSamOffset; #endif /* Reset estimation history (init with zeros) since the sample rate offset was changed */ veciSRTiCorrHist.Init(iLenCorrectionHist, 0); iIntegTiCorrections = 0; } else { /* Tracking phase */ /* Get actual sample rate offset in Hertz */ /* const CReal rSamOffset = GetSamOffHz(iCurRes - veciSRTiCorrHist[0], iLenCorrectionHist - 1); */ const CFReal rSamOffset = FXP (GetSamOffHz(iCurRes - veciSRTiCorrHist[0], iLenCorrectionHist - 1)); #ifndef USE_SAMOFFS_TRACK_FRE_PIL /* Apply result from sample rate offset estimation */ //Parameter.rResampleOffset -= CONTR_SAMP_OFF_INT_FTI * rSamOffset; FXP (Parameter.rResampleOffset) -= CONTR_SAMP_OFF_INT_FTI * rSamOffset; #endif } } /* Delay spread length estimation --------------------------------------- */ /* Estimate the noise energy using the minimum statistic. We assume that the noise variance is equal on all samples of the impulse response. Therefore we subtract the variance on each sample. The total estimated signal energy is the total energy minus the noise energy */ /* Calculate total energy */ const CFReal rTotEgy = Sum(vecrAvPoDeSpRot); /* Sort the values of the PDS to get the smallest values */ CRealVector rSortAvPoDeSpRot(Sort(vecrAvPoDeSpRot)); /* Average the result of smallest values and overestimate result */ const long long rSigmaNoise_fxp = (long long)(Sum(rSortAvPoDeSpRot(1, NUM_SAM_IR_FOR_MIN_STAT - 1)) / NUM_SAM_IR_FOR_MIN_STAT * OVER_EST_FACT_MIN_STAT*(1<<FXP_TIME_SYNC_TRACK_SCALE)); /* Calculate signal energy by subtracting the noise energy from total energy (energy cannot by negative -> bound at zero) */ const long long rSigEnergyBound_fxp = (long long)((double)Max(rTotEgy - ((double)rSigmaNoise_fxp/(1<<FXP_TIME_SYNC_TRACK_SCALE)) * iNumIntpFreqPil, (FXP)0)*(1<<FXP_TIME_SYNC_TRACK_SCALE)); /* From left to the right -> search for end of PDS */ rEstPDSEnd_fxp = (FXP) (iNumIntpFreqPil - 1); rCurEnergy_fxp = 0; bPDSResultFound = FALSE; for (i = 0; i < iNumIntpFreqPil; i++) { if (bPDSResultFound == FALSE) { if (rCurEnergy_fxp > rSigEnergyBound_fxp) { /* Delay index */ rEstPDSEnd_fxp = (CReal) i; bPDSResultFound = TRUE; } /* Accumulate signal energy, subtract noise on each sample */ rCurEnergy_fxp += (long long)(vecrAvPoDeSpRot[i]*(1<<FXP_TIME_SYNC_TRACK_SCALE)) - rSigmaNoise_fxp; //slu2 change } } /* From right to the left -> search for beginning of PDS */ rEstPDSBegin_fxp = 0; rCurEnergy_fxp = 0; bPDSResultFound = FALSE; for (i = iNumIntpFreqPil - 1; i >= 0; i--) { if (bPDSResultFound == FALSE) { if (rCurEnergy_fxp > rSigEnergyBound_fxp) { /* Delay index */ rEstPDSBegin_fxp = (CFReal) i; bPDSResultFound = TRUE; } /* Accumulate signal energy, subtract noise on each sample */ rCurEnergy_fxp += (long long)(vecrAvPoDeSpRot[i]*(1<<FXP_TIME_SYNC_TRACK_SCALE)) - rSigmaNoise_fxp; //slu2 change } } /* If the signal energy is too low it can happen that the estimated beginning of the impulse response is before the end -> correct */ if (rEstPDSBegin_fxp > rEstPDSEnd_fxp) { /* Set beginning and end to their maximum (minimum) value */ //rEstPDSBegin = (CReal) 0.0; rEstPDSBegin_fxp = 0; rEstPDSEnd_fxp = (CFReal) (iNumIntpFreqPil - 1); } /* Correct estimates of begin and end of PDS by the rotation */ const CReal rPDSLenCorrection = iNumIntpFreqPil - iStPoRot + 1; /* slu2: dont' have to change here */ rEstPDSBegin_fxp -= FXP (rPDSLenCorrection); rEstPDSEnd_fxp -= FXP (rPDSLenCorrection); /* Set return parameters */ rLenPDS_fxp = rEstPDSEnd_fxp - rEstPDSBegin_fxp; rOffsPDS_fxp= rEstPDSBegin_fxp; }
/* 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; }
/* Implementation *************************************************************/ void CSyncUsingPil::ProcessDataInternal(CParameter& ReceiverParam) { int i; /**************************************************************************\ * Frame synchronization detection * \**************************************************************************/ _BOOLEAN bSymbolIDHasChanged = FALSE; if ((bSyncInput == FALSE) && (bAquisition == TRUE)) { #ifdef USE_DRM_FRAME_SYNC_IR_BASED /* DRM frame synchronization using impulse response ----------------- */ /* We assume that the current received OFDM symbol is the first symbol in a DRM frame and estimate the channel transfer function at the pilot positions (the positions of pilots in the first OFDM symbol in a DRM frame). Then we calculate an FFT to get the impulse response of the channel. If the assumption was correct and this really was the correct OFDM symbol, we will get something which looks like an impulse response (contains peaks -> peak-to-average ratio is high). If not, we will certainly get only noise -> no peaks -> peak to average ratio is small. This is because the transmitted values at the pilot positions are different from the values at the pilot cells when transmitting the correct OFDM symbol (which we assumed) */ /* Pick pilot positions and calculate "test" channel estimation */ int iCurIndex = 0; for (i = 0; i < iNumCarrier; i++) { if (_IsScatPil(ReceiverParam.matiMapTab[0][i])) { /* Get channel estimate */ veccChan[iCurIndex] = (*pvecInputData)[i] / ReceiverParam.matcPilotCells[0][i]; /* We have to introduce a new index because not on all carriers is a pilot */ iCurIndex++; } } /* Calculate abs(IFFT) for getting estimate of impulse response */ vecrTestImpResp = Abs(Ifft(veccChan, FftPlan)); /* Calculate peak to average */ const CReal rResultIREst = Max(vecrTestImpResp) / Sum(vecrTestImpResp); /* Store correlation results in a shift register for finding the peak */ vecrCorrHistory.AddEnd(rResultIREst); #else /* DRM frame synchronization based on time pilots ------------------- */ /* Calculate correlation of received cells with pilot pairs */ CReal rResultPilPairCorr = (CReal) 0.0; for (i = 0; i < iNumPilPairs; i++) { /* Actual correlation */ const CComplex cCorrRes = (*pvecInputData)[vecPilCorr[i].iIdx1] * Conj(vecPilCorr[i].cPil1) * Conj((*pvecInputData)[vecPilCorr[i].iIdx2]) * vecPilCorr[i].cPil2 * cR_HH; rResultPilPairCorr += Real(cCorrRes); } /* Store correlation results in a shift register for finding the peak */ vecrCorrHistory.AddEnd(rResultPilPairCorr); #endif /* Finding beginning of DRM frame in results ------------------------ */ /* Wait until history is filled completly */ if (iInitCntFraSy > 0) iInitCntFraSy--; else { /* Search for maximum */ int iMaxIndex = 0; CReal rMaxValue = -_MAXREAL; for (i = 0; i < iNumSymPerFrame; i++) { if (vecrCorrHistory[i] > rMaxValue) { rMaxValue = vecrCorrHistory[i]; iMaxIndex = i; } } /* For initial frame synchronization, use maximum directly */ if (bInitFrameSync == TRUE) { /* Reset init flag */ bInitFrameSync = FALSE; /* Set symbol ID index according to received data */ iSymbCntFraSy = iNumSymPerFrame - iMaxIndex - 1; } else { /* If maximum is in the middle of the interval (check frame sync) */ if (iMaxIndex == iMiddleOfInterval) { if (iSymbCntFraSy == iNumSymPerFrame - iMiddleOfInterval - 1) { /* Reset flags */ bBadFrameSync = FALSE; bFrameSyncWasOK = TRUE; /* Post Message for GUI (Good frame sync) */ PostWinMessage(MS_FRAME_SYNC, 0); /* green */ } else { if (bBadFrameSync == TRUE) { /* Reset symbol ID index according to received data */ iSymbCntFraSy = iNumSymPerFrame - iMiddleOfInterval - 1; /* Inform that symbol ID has changed */ bSymbolIDHasChanged = TRUE; /* Reset flag */ bBadFrameSync = FALSE; PostWinMessage(MS_FRAME_SYNC, 2); /* red */ } else { /* One false detected frame sync should not reset the actual frame sync because the measurement could be wrong. Sometimes the frame sync detection gets false results. If the next time the frame sync is still unequal to the measurement, then correct it */ bBadFrameSync = TRUE; if (bFrameSyncWasOK == TRUE) { /* Post Message that frame sync was wrong but was not yet corrected (yellow light) */ PostWinMessage(MS_FRAME_SYNC, 1); /* yellow */ } else PostWinMessage(MS_FRAME_SYNC, 2); /* red */ } /* Set flag for bad sync */ bFrameSyncWasOK = FALSE; } } } } } else { /* Frame synchronization has successfully finished, show always green light */ PostWinMessage(MS_FRAME_SYNC, 0); } /* Set current symbol ID and flag in extended data of output vector */ (*pvecOutputData).GetExData().iSymbolID = iSymbCntFraSy; (*pvecOutputData).GetExData().bSymbolIDHasChanged = bSymbolIDHasChanged; /* Increase symbol counter and take care of wrap around */ iSymbCntFraSy++; if (iSymbCntFraSy >= iNumSymPerFrame) iSymbCntFraSy = 0; /**************************************************************************\ * Using Frequency pilot information * \**************************************************************************/ if ((bSyncInput == FALSE) && (bTrackPil == TRUE)) { CComplex cFreqOffEstVecSym = CComplex((CReal) 0.0, (CReal) 0.0); for (i = 0; i < NUM_FREQ_PILOTS; i++) { /* The old pilots must be rotated due to timing corrections */ const CComplex cOldFreqPilCorr = Rotate(cOldFreqPil[i], iPosFreqPil[i], (*pvecInputData).GetExData().iCurTimeCorr); /* Calculate the inner product of the sum */ const CComplex cCurPilMult = (*pvecInputData)[iPosFreqPil[i]] * Conj(cOldFreqPilCorr); /* Save "old" frequency pilots for next symbol. Special treatment for robustness mode D (carriers 7 and 21) necessary (See 8.4.2.2) */ if ((ReceiverParam.GetWaveMode() == RM_ROBUSTNESS_MODE_E) && (i < 2)) { cOldFreqPil[i] = -(*pvecInputData)[iPosFreqPil[i]]; } else cOldFreqPil[i] = (*pvecInputData)[iPosFreqPil[i]]; #ifdef USE_SAMOFFS_TRACK_FRE_PIL /* Get phase difference for sample rate offset estimation. Average the vector, real and imaginary part separately */ IIR1(cFreqPilotPhDiff[i], cCurPilMult, rLamSamRaOff); #endif /* Calculate estimation of frequency offset */ cFreqOffEstVecSym += cCurPilMult; } /* Frequency offset ------------------------------------------------- */ /* Correct frequency offset estimation for resample offset corrections. When a sample rate offset correction was applied, the frequency offset is shifted proportional to this correction. The correction is mandatory if large sample rate offsets occur */ /* Get sample rate offset change */ const CReal rDiffSamOffset = rPrevSamRateOffset - ReceiverParam.rResampleOffset; /* Save current resample offset for next symbol */ rPrevSamRateOffset = ReceiverParam.rResampleOffset; /* Correct sample-rate offset correction according to the proportional rule. Use relative DC frequency offset plus relative average offset of frequency pilots to the DC frequency. Normalize this offset so that it can be used as a phase correction for frequency offset estimation */ CReal rPhaseCorr = (ReceiverParam.rFreqOffsetAcqui + ReceiverParam.rFreqOffsetTrack + rAvFreqPilDistToDC) * rDiffSamOffset / SOUNDCRD_SAMPLE_RATE / rNormConstFOE; /* Actual correction (rotate vector) */ cFreqOffVec *= CComplex(Cos(rPhaseCorr), Sin(rPhaseCorr)); /* Average vector, real and imaginary part separately */ IIR1(cFreqOffVec, cFreqOffEstVecSym, rLamFreqOff); /* Calculate argument */ const CReal rFreqOffsetEst = Angle(cFreqOffVec); /* Correct measurement average for actually applied frequency correction */ cFreqOffVec *= CComplex(Cos(-rFreqOffsetEst), Sin(-rFreqOffsetEst)); #ifndef USE_FRQOFFS_TRACK_GUARDCORR /* Integrate the result for controling the frequency offset, normalize estimate */ ReceiverParam.rFreqOffsetTrack += rFreqOffsetEst * rNormConstFOE; #endif #ifdef USE_SAMOFFS_TRACK_FRE_PIL /* Sample rate offset ----------------------------------------------- */ /* Calculate estimation of sample frequency offset. We use the different frequency offset estimations of the frequency pilots. We normalize them with the distance between them and average the result (/ 2.0) */ CReal rSampFreqOffsetEst = ((Angle(cFreqPilotPhDiff[1]) - Angle(cFreqPilotPhDiff[0])) / (iPosFreqPil[1] - iPosFreqPil[0]) + (Angle(cFreqPilotPhDiff[2]) - Angle(cFreqPilotPhDiff[0])) / (iPosFreqPil[2] - iPosFreqPil[0])) / (CReal) 2.0; /* Integrate the result for controling the resampling */ ReceiverParam.rResampleOffset += CONTR_SAMP_OFF_INTEGRATION * rSampFreqOffsetEst; #endif #ifdef _DEBUG_ /* Save frequency and sample rate tracking */ static FILE* pFile = fopen("test/freqtrack.dat", "w"); fprintf(pFile, "%e %e\n", SOUNDCRD_SAMPLE_RATE * ReceiverParam.rFreqOffsetTrack, ReceiverParam.rResampleOffset); fflush(pFile); #endif } /* If synchronized DRM input stream is used, overwrite the detected frequency offest estimate by "0", because we know this value */ if (bSyncInput == TRUE) ReceiverParam.rFreqOffsetTrack = (CReal) 0.0; /* Do not ship data before first frame synchronization was done. The flag "bAquisition" must not be set to FALSE since in that case we would run into an infinite loop since we would not ever ship any data. But since the flag is set after this module, we should be fine with that. */ if ((bInitFrameSync == TRUE) && (bSyncInput == FALSE)) iOutputBlockSize = 0; else { iOutputBlockSize = iNumCarrier; /* Copy data from input to the output. Data is not modified in this module */ for (i = 0; i < iOutputBlockSize; i++) (*pvecOutputData)[i] = (*pvecInputData)[i]; } }
/* 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; } } }