22# #### Multitapered periodogram
33# ####
44
5+ using Distributions
6+
57struct MTConfig{T,R1,F,P,T1,T2,W,R2}
68 n_samples:: Int
79 fs:: R1
@@ -13,11 +15,12 @@ struct MTConfig{T,R1,F,P,T1,T2,W,R2}
1315 fft_output_tmp:: T2
1416 window:: W
1517 onesided:: Bool
18+ ftest:: Bool
1619 r:: R2 # inverse normalization; e.g. equal to `fs*N` for an unwindowed/untapered periodogram of a signal of length `N`
1720 # e.g. equal to `fs*ntapers*ones(ntapers)` when tapered by properly normalized window functions (i.e. norm-2 equal to 1 for each taper)
1821 # can be adjusted to weight the tapers, e.g. by the eigenvalues of the DPSS windows
1922 function MTConfig {T} (n_samples, nfft, ntapers, freq:: F , fs:: R1 , plan:: P , fft_input_tmp:: T1 ,
20- fft_output_tmp:: T2 , window:: W , onesided, r:: R2 ) where {T,R1,F,P,T1,T2,W,R2}
23+ fft_output_tmp:: T2 , window:: W , onesided, ftest, r:: R2 ) where {T,R1,F,P,T1,T2,W,R2}
2124 n_samples > 0 || throw (ArgumentError (" `n_samples` must be positive" ))
2225 nfft >= n_samples || throw (ArgumentError (" Must have `nfft >= n_samples`" ))
2326 ntapers > 0 || throw (ArgumentError (" `ntapers` must be positive" ))
@@ -44,7 +47,7 @@ struct MTConfig{T,R1,F,P,T1,T2,W,R2}
4447 ntapers, freq, plan,
4548 fft_input_tmp,
4649 fft_output_tmp, window,
47- onesided, r)
50+ onesided, ftest, r)
4851 end
4952end
5053
8588 ntapers = 2 * nw - 1,
8689 taper_weights = fill(1/ntapers, ntapers),
8790 onesided::Bool=T<:Real,
91+ ftest::Bool=false,
8892 fft_flags = FFTW.MEASURE)
8993
9094Creates a config object which holds the configuration state
@@ -107,11 +111,12 @@ as none of the input arguments change.
107111 The default setting is to simply average them.
108112* `onesided`: whether or not to compute a "one-sided" FFT by using that real signal data
109113 yields conjugate-symmetry in Fourier space.
114+ * `ftest: whether or not to compute ...
110115* `fft_flags`: flags to control how the FFT plan is generated.
111116"""
112117function MTConfig {T} (n_samples; fs= 1 , nfft= nextpow (2 , n_samples), window= nothing , nw= 4 ,
113118 ntapers= 2 * nw - 1 , taper_weights = fill (1 / ntapers, ntapers),
114- onesided:: Bool = T <: Real ,
119+ onesided:: Bool = T <: Real , ftest :: Bool = false ,
115120 fft_flags= FFTW. MEASURE) where {T}
116121 if onesided && T <: Complex
117122 throw (ArgumentError (" cannot compute one-sided FFT of a complex signal" ))
@@ -131,7 +136,7 @@ function MTConfig{T}(n_samples; fs=1, nfft=nextpow(2, n_samples), window=nothing
131136 end
132137
133138 return MTConfig {T} (n_samples, nfft, ntapers, freq, fs, plan, fft_input_tmp,
134- fft_output_tmp, window, onesided, r)
139+ fft_output_tmp, window, onesided, ftest, r)
135140end
136141
137142function allocate_output (config:: MTConfig{T} ) where {T}
@@ -222,6 +227,27 @@ See also [`mt_pgram`](@ref) and [`MTConfig`](@ref).
222227"""
223228mt_pgram!
224229
230+ function ftest (J:: Array{ComplexF64,2} , tapers:: Matrix )
231+ K = size (tapers,2 )
232+ Kodd = 1 : 2 : K
233+ Keven = 2 : 2 : K
234+
235+ Jp = J[:,Kodd]
236+ H0 = sum (tapers[:,Kodd], dims= 1 )
237+ H0sq = sum (abs2, H0)
238+ JpH0 = dropdims (sum (Jp.* H0, dims= 2 ), dims= 2 )
239+ A = JpH0 ./ H0sq
240+ Jhat = A * H0
241+
242+ num = (K- 1 ) * abs2 .(A) .* H0sq
243+ den = dropdims (sum (abs2 .(Jp.- Jhat), dims= 2 ), dims= 2 ) +
244+ dropdims (sum (abs2 .(J[:,Keven,:]), dims= 2 ), dims= 2 )
245+ Fval = num ./ den
246+
247+ map! (x -> 1.0 - cdf (FDist (2 , 2 * Int64 (K)- 2 ), x), Fval, Fval)
248+ return Fval
249+ end
250+
225251function mt_pgram! (output:: AbstractVector , signal:: AbstractVector , config:: MTConfig )
226252 if length (output) != length (config. freq)
227253 throw (DimensionMismatch (lazy """ Expected `output` to be of length `length(config.freq)`;
@@ -233,12 +259,16 @@ function mt_pgram!(output::AbstractVector, signal::AbstractVector, config::MTCon
233259 end
234260 fft_output = config. fft_output_tmp
235261
262+ config. ftest && (ftest_scratch = similar (fft_output, (size (fft_output)... , config. ntapers)))
263+
236264 output .= 0
237265 for taper_index in 1 : (config. ntapers)
238266 mt_fft_tapered! (fft_output, signal, taper_index, config)
267+ config. ftest && (ftest_scratch[:,taper_index] .= fft_output)
239268 fft2pow! (output, fft_output, config. nfft, config. r[taper_index], config. onesided)
240269 end
241- return Periodogram (output, config. freq)
270+ Fval = ftest (ftest_scratch, config. window)
271+ return PeriodogramF (output, config. freq, Fval[:,1 ])
242272end
243273
244274# ####
0 commit comments