/*************************************************************************** PFourier.cpp Author: Andreas Suter e-mail: andreas.suter@psi.ch ***************************************************************************/ /*************************************************************************** * Copyright (C) 2007-2016 by Andreas Suter * * andreas.suter@psi.ch * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include #include using namespace std; #include "TH1F.h" #include "TF1.h" #include "TAxis.h" //#include "TFile.h" #include "PMusr.h" #include "PFourier.h" #define PI 3.14159265358979312 #define PI_HALF 1.57079632679489656 //-------------------------------------------------------------------------- // Constructor //-------------------------------------------------------------------------- /** *

Constructor. * * \param data data histogram * \param unitTag tag telling in which units the Fourier transform shall be represented. Possible tags are: * FOURIER_UNIT_GAUSS, FOURIER_UNIT_TESLA, FOURIER_UNIT_FREQ, FOURIER_UNIT_CYCLES * \param startTime start time of the data time window * \param endTime end time of the data time window * \param dcCorrected if true, removed DC offset from signal before Fourier transformation, otherwise not * \param zeroPaddingPower if set to values > 0, there will be zero padding up to 2^zeroPaddingPower */ PFourier::PFourier(TH1F *data, Int_t unitTag, Double_t startTime, Double_t endTime, Bool_t dcCorrected, UInt_t zeroPaddingPower) : fData(data), fUnitTag(unitTag), fStartTime(startTime), fEndTime(endTime), fDCCorrected(dcCorrected), fZeroPaddingPower(zeroPaddingPower) { // some necessary checks and initialization if (fData == 0) { cerr << endl << "**ERROR** PFourier::PFourier: no valid data" << endl << endl; fValid = false; return; } fValid = true; fIn = 0; fOut = 0; fApodization = F_APODIZATION_NONE; // calculate time resolution in (us) fTimeResolution = fData->GetBinWidth(1); // if endTime == 0 set it to the last time slot if (fEndTime == 0.0) { Int_t last = fData->GetNbinsX()-1; fEndTime = fData->GetBinCenter(last); } // swap start and end time if necessary if (fStartTime > fEndTime) { Double_t keep = fStartTime; fStartTime = fEndTime; fEndTime = keep; } // calculate start and end bin fNoOfData = (UInt_t)((fEndTime-fStartTime)/fTimeResolution); // check if zero padding is whished if (fZeroPaddingPower > 0) { UInt_t noOfBins = static_cast(pow(2.0, static_cast(fZeroPaddingPower))); if (noOfBins > fNoOfData) fNoOfBins = noOfBins; else fNoOfBins = fNoOfData; } else { fNoOfBins = fNoOfData; } // calculate fourier resolution, depending on the units Double_t resolution = 1.0/(fTimeResolution*fNoOfBins); // in MHz switch (fUnitTag) { case FOURIER_UNIT_GAUSS: fResolution = resolution/GAMMA_BAR_MUON; break; case FOURIER_UNIT_TESLA: fResolution = 1e-4*resolution/GAMMA_BAR_MUON; break; case FOURIER_UNIT_FREQ: fResolution = resolution; break; case FOURIER_UNIT_CYCLES: fResolution = 2.0*PI*resolution; break; default: fValid = false; return; break; } // allocate necessary memory fIn = (fftw_complex *)fftw_malloc(sizeof(fftw_complex)*fNoOfBins); fOut = (fftw_complex *)fftw_malloc(sizeof(fftw_complex)*fNoOfBins); // check if memory allocation has been successful if ((fIn == 0) || (fOut == 0)) { fValid = false; return; } // get the FFTW3 plan (see FFTW3 manual) fFFTwPlan = fftw_plan_dft_1d(fNoOfBins, fIn, fOut, FFTW_FORWARD, FFTW_ESTIMATE); // check if a valid plan has been generated if (!fFFTwPlan) { fValid = false; } } //-------------------------------------------------------------------------- // Destructor //-------------------------------------------------------------------------- /** *

Destructor */ PFourier::~PFourier() { if (fFFTwPlan) fftw_destroy_plan(fFFTwPlan); if (fIn) fftw_free(fIn); if (fOut) fftw_free(fOut); } //-------------------------------------------------------------------------- // Transform (public) //-------------------------------------------------------------------------- /** *

Carries out the Fourier transform. It is assumed that fStartTime is the time zero * for the Fourier frame. Hence if fStartTime != 0.0 the phase shift will be corrected. * * \param apodizationTag 0=no apod., 1=weak apod., 2=medium apod., 3=strong apod. */ void PFourier::Transform(UInt_t apodizationTag) { if (!fValid) return; PrepareFFTwInputData(apodizationTag); fftw_execute(fFFTwPlan); // correct the phase for tstart != 0.0 // find the first bin >= fStartTime Double_t shiftTime = 0.0; for (Int_t i=1; iGetXaxis()->GetNbins(); i++) { if (fData->GetXaxis()->GetBinCenter(i) >= fStartTime) { shiftTime = fData->GetXaxis()->GetBinCenter(i); break; } } Double_t phase, re, im; for (UInt_t i=0; ireturns the maximal frequency in units choosen, i.e. Gauss, Tesla, MHz, Mc/s */ Double_t PFourier::GetMaxFreq() { UInt_t noOfFourierBins = 0; if (fNoOfBins % 2 == 0) noOfFourierBins = fNoOfBins/2; else noOfFourierBins = (fNoOfBins+1)/2; return fResolution*noOfFourierBins; } //-------------------------------------------------------------------------- // GetRealFourier (public) //-------------------------------------------------------------------------- /** *

returns the real part Fourier as a histogram. * * \param scale normalisation factor */ TH1F* PFourier::GetRealFourier(const Double_t scale) { // check if valid flag is set if (!fValid) return 0; // invoke realFourier Char_t name[256]; Char_t title[256]; snprintf(name, sizeof(name), "%s_Fourier_Re", fData->GetName()); snprintf(title, sizeof(title), "%s_Fourier_Re", fData->GetTitle()); UInt_t noOfFourierBins = 0; if (fNoOfBins % 2 == 0) noOfFourierBins = fNoOfBins/2; else noOfFourierBins = (fNoOfBins+1)/2; TH1F *realFourier = new TH1F(name, title, noOfFourierBins, -fResolution/2.0, (Double_t)(noOfFourierBins-1)*fResolution+fResolution/2.0); if (realFourier == 0) { fValid = false; cerr << endl << "**SEVERE ERROR** couldn't allocate memory for the real part of the Fourier transform." << endl; return 0; } // fill realFourier vector for (UInt_t i=0; iSetBinContent(i+1, scale*fOut[i][0]); realFourier->SetBinError(i+1, 0.0); } return realFourier; } //-------------------------------------------------------------------------- // GetPhaseOptRealFourier (public) //-------------------------------------------------------------------------- /** *

returns the phase corrected real Fourier transform. The correction angle is * past back as well. *

Currently it simply does the following thing: (i) rotate the complex Fourier * transform through all angles in 1/2° steps, i.e. * \f$ f_{\rm rot} = (f_{\rm real} + i f_{\rm imag}) \exp(- \alpha)\f$, * hence \f$ f_{\rm rot} = f_{\rm real} \cos(\alpha) + f_{\rm imag} \sin(\alpha)\f$. * (ii) search the maximum of \f$ sum_{\alpha} {\cal R}\{f_{\rm rot}\}\f$ for all * \f$\alpha\f$. From this one gets \f$\alpha_{\rm opt}\f$. (iii) The 'optimal' * real Fourier transform is \f$f_{\rm opt} = (f_{\rm real} + i f_{\rm imag}) * \exp(- \alpha_{\rm opt})\f$. * * \return the TH1F histo of the phase 'optimzed' real Fourier transform. * * \param phase return value of the optimal phase * \param scale normalisation factor * \param min minimal freq / field from which to optimise. Given in the choosen unit. * \param max maximal freq / field up to which to optimise. Given in the choosen unit. */ TH1F* PFourier::GetPhaseOptRealFourier(Double_t &phase, const Double_t scale, const Double_t min, const Double_t max) { UInt_t noOfFourierBins = 0; if (fNoOfBins % 2 == 0) noOfFourierBins = fNoOfBins/2; else noOfFourierBins = (fNoOfBins+1)/2; UInt_t minBin = 0; UInt_t maxBin = noOfFourierBins; // check if minimum frequency is given. If yes, get the proper minBin if (min != -1.0) { minBin = (UInt_t)(min/fResolution); } // check if maximum frequency is given. If yes, get the proper maxBin if (max != -1.0) { maxBin = (UInt_t)(max/fResolution); if (maxBin >= noOfFourierBins) { maxBin = noOfFourierBins; cerr << "**WARNING** maximum frequency/field out of range. Will adopted it." << endl; } } // copy the real/imag Fourier from min to max vector realF, imagF; for (UInt_t i=minBin; i<=maxBin; i++) { realF.push_back(fOut[i][0]); imagF.push_back(fOut[i][1]); } // rotate trough real/imag Fourier through 360° with a 1/2° resolution and keep the integral Double_t rotRealIntegral[720]; Double_t sum = 0.0; Double_t da = 8.72664625997164774e-03; // pi / 360 for (UInt_t i=0; i<720; i++) { sum = 0.0; for (UInt_t j=0; jGetName()); snprintf(title, sizeof(title), "%s_Fourier_PhOptRe", fData->GetTitle()); TH1F *realPhaseOptFourier = new TH1F(name, title, noOfFourierBins, -fResolution/2.0, (Double_t)(noOfFourierBins-1)*fResolution+fResolution/2.0); if (realPhaseOptFourier == 0) { fValid = false; cerr << endl << "**SEVERE ERROR** couldn't allocate memory for the real part of the Fourier transform." << endl; return 0; } // fill realFourier vector for (UInt_t i=0; iSetBinContent(i+1, scale*(fOut[i][0]*cos(phase) + fOut[i][1]*sin(phase))); realPhaseOptFourier->SetBinError(i+1, 0.0); } return realPhaseOptFourier; } //-------------------------------------------------------------------------- // GetImaginaryFourier (public) //-------------------------------------------------------------------------- /** *

returns the imaginary part Fourier as a histogram. * * \param scale normalisation factor */ TH1F* PFourier::GetImaginaryFourier(const Double_t scale) { // check if valid flag is set if (!fValid) return 0; // invoke imaginaryFourier Char_t name[256]; Char_t title[256]; snprintf(name, sizeof(name), "%s_Fourier_Im", fData->GetName()); snprintf(title, sizeof(title), "%s_Fourier_Im", fData->GetTitle()); UInt_t noOfFourierBins = 0; if (fNoOfBins % 2 == 0) noOfFourierBins = fNoOfBins/2; else noOfFourierBins = (fNoOfBins+1)/2; TH1F* imaginaryFourier = new TH1F(name, title, noOfFourierBins, -fResolution/2.0, (Double_t)(noOfFourierBins-1)*fResolution+fResolution/2.0); if (imaginaryFourier == 0) { fValid = false; cerr << endl << "**SEVERE ERROR** couldn't allocate memory for the imaginary part of the Fourier transform." << endl; return 0; } // fill imaginaryFourier vector for (UInt_t i=0; iSetBinContent(i+1, scale*fOut[i][1]); imaginaryFourier->SetBinError(i+1, 0.0); } return imaginaryFourier; } //-------------------------------------------------------------------------- // GetPowerFourier (public) //-------------------------------------------------------------------------- /** *

returns the Fourier power spectrum as a histogram. * * \param scale normalisation factor */ TH1F* PFourier::GetPowerFourier(const Double_t scale) { // check if valid flag is set if (!fValid) return 0; // invoke powerFourier Char_t name[256]; Char_t title[256]; snprintf(name, sizeof(name), "%s_Fourier_Pwr", fData->GetName()); snprintf(title, sizeof(title), "%s_Fourier_Pwr", fData->GetTitle()); UInt_t noOfFourierBins = 0; if (fNoOfBins % 2 == 0) noOfFourierBins = fNoOfBins/2; else noOfFourierBins = (fNoOfBins+1)/2; TH1F* pwrFourier = new TH1F(name, title, noOfFourierBins, -fResolution/2.0, (Double_t)(noOfFourierBins-1)*fResolution+fResolution/2.0); if (pwrFourier == 0) { fValid = false; cerr << endl << "**SEVERE ERROR** couldn't allocate memory for the power part of the Fourier transform." << endl; return 0; } // fill powerFourier vector for (UInt_t i=0; iSetBinContent(i+1, scale*sqrt(fOut[i][0]*fOut[i][0]+fOut[i][1]*fOut[i][1])); pwrFourier->SetBinError(i+1, 0.0); } return pwrFourier; } //-------------------------------------------------------------------------- // GetPhaseFourier (public) //-------------------------------------------------------------------------- /** *

returns the Fourier phase spectrum as a histogram. * * \param scale normalisation factor */ TH1F* PFourier::GetPhaseFourier(const Double_t scale) { // check if valid flag is set if (!fValid) return 0; // invoke phaseFourier Char_t name[256]; Char_t title[256]; snprintf(name, sizeof(name), "%s_Fourier_Phase", fData->GetName()); snprintf(title, sizeof(title), "%s_Fourier_Phase", fData->GetTitle()); UInt_t noOfFourierBins = 0; if (fNoOfBins % 2 == 0) noOfFourierBins = fNoOfBins/2; else noOfFourierBins = (fNoOfBins+1)/2; TH1F* phaseFourier = new TH1F(name, title, noOfFourierBins, -fResolution/2.0, (Double_t)(noOfFourierBins-1)*fResolution+fResolution/2.0); if (phaseFourier == 0) { fValid = false; cerr << endl << "**SEVERE ERROR** couldn't allocate memory for the phase part of the Fourier transform." << endl; return 0; } // fill phaseFourier vector Double_t value = 0.0; for (UInt_t i=0; i= 0.0) value = PI_HALF; else value = -PI_HALF; } else { value = atan(fOut[i][1]/fOut[i][0]); // check sector if (fOut[i][0] < 0.0) { if (fOut[i][1] > 0.0) value = PI + value; else value = PI - value; } } phaseFourier->SetBinContent(i+1, scale*value); phaseFourier->SetBinError(i+1, 0.0); } return phaseFourier; } //-------------------------------------------------------------------------- // PrepareFFTwInputData (private) //-------------------------------------------------------------------------- /** *

Feeds the Fourier data and apply the apodization. * * \param apodizationTag apodization tag. Possible are currently: F_APODIZATION_NONE = no apodization, * F_APODIZATION_WEAK = weak apodization, F_APODIZATION_MEDIUM = intermediate apodization, * F_APODIZATION_STRONG = strong apodization */ void PFourier::PrepareFFTwInputData(UInt_t apodizationTag) { // 1st find t==0. fData start at times t<0!! Int_t t0bin = -1; for (Int_t i=1; iGetNbinsX(); i++) { if (fData->GetBinCenter(i) >= 0.0) { t0bin = i; break; } } Int_t ival = static_cast(fStartTime/fTimeResolution) + t0bin; UInt_t start = 0; if (ival >= 0) { start = static_cast(ival); } Double_t mean = 0.0; if (fDCCorrected) { for (UInt_t i=0; iGetBinContent(i+start); } mean /= (Double_t)fNoOfData; } // 2nd fill fIn for (UInt_t i=0; iGetBinContent(i+start) - mean; fIn[i][1] = 0.0; } for (UInt_t i=fNoOfData; iCarries out the appodization of the data. * * \param apodizationTag apodization tag. Possible are currently: F_APODIZATION_NONE = no apodization, * F_APODIZATION_WEAK = weak apodization, F_APODIZATION_MEDIUM = intermediate apodization, * F_APODIZATION_STRONG = strong apodization */ void PFourier::ApodizeData(Int_t apodizationTag) { const Double_t cweak[3] = { 0.384093, -0.087577, 0.703484 }; const Double_t cmedium[3] = { 0.152442, -0.136176, 0.983734 }; const Double_t cstrong[3] = { 0.045335, 0.554883, 0.399782 }; Double_t c[5]; for (UInt_t i=0; i<5; i++) { c[i] = 0.0; } switch (apodizationTag) { case F_APODIZATION_NONE: return; break; case F_APODIZATION_WEAK: c[0] = cweak[0]+cweak[1]+cweak[2]; c[1] = -(cweak[1]+2.0*cweak[2]); c[2] = cweak[2]; break; case F_APODIZATION_MEDIUM: c[0] = cmedium[0]+cmedium[1]+cmedium[2]; c[1] = -(cmedium[1]+2.0*cmedium[2]); c[2] = cmedium[2]; break; case F_APODIZATION_STRONG: c[0] = cstrong[0]+cstrong[1]+cstrong[2]; c[1] = -2.0*(cstrong[1]+2.0*cstrong[2]); c[2] = cstrong[1]+6.0*cstrong[2]; c[3] = -4.0*cstrong[2]; c[4] = cstrong[2]; break; default: cerr << endl << ">> **ERROR** User Apodization tag " << apodizationTag << " unknown, sorry ..." << endl; break; } Double_t q; for (UInt_t i=0; i