#ifndef LOMB_SCARGLE_SPECTRUM #define LOMB_SCARGLE_SPECTRUM #ifndef PI #define PI 3.141592653589793238462643 #endif #ifndef EPSILON #define EPSILON 1e-15 #endif // Calculates the Lomb-Scargle spectrum (LSS) and Lomb-Scargle periodogram LSP (aka LS power spectrum) of a real (!) time series (an estimate for the spectrum of the generating process) // Sampling times can be arbitrary, but must be given in increasing order. // Consider this as a (typically slight) improvement over leastSquaresPowerSpectrum() defined above. // In the case of evenly sampled data, the Lomb-Scargle spectrum reduces to the discrete spectrum returned by discretePowerSpectrum(). // See: // [Scargle (1982) - Studies in astronomical time series analysis. II-Statistical aspects of spectral analysis of unevenly spaced data, eq. (10)] // [Scargle (1989) - Studies in astronomical time series analysis. III. Fourier transforms, autocorrelation functions, and cross-correlation functions of unevenly spaced data, eq. (I.1)] // [Rehfeld (2011) - Comparison of correlation analysis techniques for irregularly sampled time series, §2.2] // Note that the normalization used here is for continuous-time stochastic process and is different than in the original articles by Scargle (you need to multiply power spectrum by N/T to get the original Lomb-Scargle normalization). // To get the power spectrum normalization used for stochastic processes, multiply the obtained spectrum by the (total signal count N)/(total time span T) (for discrete sum version, same as original Lomb-Scargle version). // Also, df is in frequency units. To get angular frequencies, replace df by df*2PI. // Also note that here the spectrum is the complex conjugate of the spectrum defined by Lomb & Scangler (for consistency with the conventional Fourier transform). // Returns false on error template bool LombScargleSpectrum( const vector &signal, const vector ×, long start, // start index in data series (ignore anything prior) long end, // end index in data series (ignore anything after) double maxFrequency, // (input) limit the considered spectrum band. Set to non-positive for "average" Nyquist–Shannon bound. vector* spectrumReal, // (output) either pre-allocated, or NULL to skip this part vector* spectrumImag, // (output) either pre-allocated, or NULL to skip this part vector* powerSpectrum, // (output) either pre-allocated, or NULL to skip this part double &df){ //(output) fundamental frequency if((times.size()<=end) || (signal.size() <=end)) return false; double T = times[end] - times[start]; //data time span if(T<=0) return false; long n,m, N = end - start + 1; if(N<2) return false; double S, C, phi, tau, f, mindt, maxdt, meandt=T/(N-1); TYPE FC, FS, FRe, FIm, Xmean, zero=signal[start]*0; for(n=start, mindt=maxdt=times[start+1]-times[start]; n0 ? min((long)(maxFrequency/df), N/2 - 1) : N/2 - 1); //upper limit given by Nyquist frequency if(powerSpectrum!=NULL){ (*powerSpectrum).clear(); (*powerSpectrum).reserve(maxMode+1); } if(spectrumReal!=NULL){ (*spectrumReal).clear(); (*spectrumReal).reserve(maxMode+1); } if(spectrumImag!=NULL){ (*spectrumImag).clear(); (*spectrumImag).reserve(maxMode+1); } for(m=0; m<=maxMode; ++m){ if(uniform){ //use classical DFT formula for(n=0, FRe=FIm=zero; n bool averagedLombScargleSpectrum( const vector &signal, const vector ×, long start, // start index in data series (ignore anything prior) long end, // end index in data series (ignore anything after) long segmentsAim, // (input) number of segments to split the time series into (if possible) long minNumberOfSegments,// (input) if segmentsAchieved ends up lower than this, abort and return false long maxPointsToOmit, // (input) maximum number of data points allowed to be omitted in order to make time series points divisible by segmentsAim. If zero, it might happen that only one segment is achieved. double maxFrequency, // (input) limit the considered spectrum band. Set to non-positive for "average" Nyquist–Shannon bound. long &segmentsAchieved, // (output) number of segments the time series was actually split into vector &powerSpectrum, // (output) double &df, // (output) double &dtPerSegment, // (output) long &NperSegment){ if(endN-maxPointsToOmit){ --Nreduced; } else if(segmentsAchieved>minNumberOfSegments){ --segmentsAchieved; Nreduced=N; } else{ return false; } } if(segmentsAchieved0 ? min((long)(maxFrequency/df), NperSegment/2 - 1) : NperSegment/2 - 1); //upper limit given by Nyquist frequency double S, C, phi, tau, f; TYPE FC, FS, FRe, FIm, Xmean, zero=signal[start]*0; long segmentStart, segmentEnd, segmentStep, n; powerSpectrum.clear(); powerSpectrum.assign(maxMode+1,zero); for(long s=0; s