From 9d87210e89127135482ac11e17669c37ce6b20ff Mon Sep 17 00:00:00 2001 From: Carsten Bauer Date: Sun, 12 May 2019 00:06:51 +0200 Subject: [PATCH 1/6] Moved Frequencies, fftfreq, rfftfreq from DSP.jl to AbstractFFTs.jl --- src/AbstractFFTs.jl | 4 ++-- src/definitions.jl | 45 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/AbstractFFTs.jl b/src/AbstractFFTs.jl index b4a29e8..c6e137f 100644 --- a/src/AbstractFFTs.jl +++ b/src/AbstractFFTs.jl @@ -3,8 +3,8 @@ module AbstractFFTs export fft, ifft, bfft, fft!, ifft!, bfft!, plan_fft, plan_ifft, plan_bfft, plan_fft!, plan_ifft!, plan_bfft!, rfft, irfft, brfft, plan_rfft, plan_irfft, plan_brfft, - fftshift, ifftshift + fftshift, ifftshift, Frequencies, fftfreq, rfftfreq include("definitions.jl") -end # module +end # module \ No newline at end of file diff --git a/src/definitions.jl b/src/definitions.jl index 07f2d79..d9e841e 100644 --- a/src/definitions.jl +++ b/src/definitions.jl @@ -3,7 +3,7 @@ using LinearAlgebra using LinearAlgebra: BlasReal import Base: show, summary, size, ndims, length, eltype, - *, inv, \ + *, inv, \, size, step, getindex, iterate # DFT plan where the inputs are an array of eltype T abstract type Plan{T} end @@ -396,6 +396,49 @@ function ifftshift(x,dim) circshift(x, s) end +############################################################################## + + +struct Frequencies{T<:Real} <: AbstractVector{T} + nreal::Int + n::Int + multiplier::T +end + +unsafe_getindex(x::Frequencies, i::Int) = + (i-1+ifelse(i <= x.nreal, 0, -x.n))*x.multiplier +function Base.getindex(x::Frequencies, i::Int) + (i >= 1 && i <= x.n) || throw(BoundsError()) + unsafe_getindex(x, i) +end + +function Base.iterate(x::Frequencies, i::Int=1) + i > x.n ? nothing : (unsafe_getindex(x,i), i + 1) +end +Base.size(x::Frequencies) = (x.n,) +Base.step(x::Frequencies) = x.multiplier + +""" + fftfreq(n, fs=1) +Return discrete fourier transform sample frequencies. The returned +Frequencies object is an AbstractVector containing the frequency +bin centers at every sample point. `fs` is the sample rate of the +input signal. +""" +fftfreq(n::Int, fs::Real=1) = Frequencies(((n-1) >> 1)+1, n, fs/n) + +""" + rfftfreq(n, fs=1) +Return discrete fourier transform sample frequencies for use with +`rfft`. The returned Frequencies object is an AbstractVector +containing the frequency bin centers at every sample point. `fs` +is the sample rate of the input signal. +""" +rfftfreq(n::Int, fs::Real=1) = Frequencies((n >> 1)+1, (n >> 1)+1, fs/n) + +fftshift(x::Frequencies) = (x.nreal-x.n:x.nreal-1)*x.multiplier + + ############################################################################## """ From abb4c51f5c25d7ef17632f828a733fcbf425bebd Mon Sep 17 00:00:00 2001 From: Carsten Bauer Date: Sun, 12 May 2019 00:19:32 +0200 Subject: [PATCH 2/6] add newline; <:Real -> <:Number --- src/AbstractFFTs.jl | 2 +- src/definitions.jl | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/AbstractFFTs.jl b/src/AbstractFFTs.jl index c6e137f..d31cde3 100644 --- a/src/AbstractFFTs.jl +++ b/src/AbstractFFTs.jl @@ -7,4 +7,4 @@ export fft, ifft, bfft, fft!, ifft!, bfft!, include("definitions.jl") -end # module \ No newline at end of file +end # module diff --git a/src/definitions.jl b/src/definitions.jl index d9e841e..0f7c4be 100644 --- a/src/definitions.jl +++ b/src/definitions.jl @@ -399,7 +399,7 @@ end ############################################################################## -struct Frequencies{T<:Real} <: AbstractVector{T} +struct Frequencies{T<:Number} <: AbstractVector{T} nreal::Int n::Int multiplier::T @@ -425,7 +425,7 @@ Frequencies object is an AbstractVector containing the frequency bin centers at every sample point. `fs` is the sample rate of the input signal. """ -fftfreq(n::Int, fs::Real=1) = Frequencies(((n-1) >> 1)+1, n, fs/n) +fftfreq(n::Int, fs::Number=1) = Frequencies(((n-1) >> 1)+1, n, fs/n) """ rfftfreq(n, fs=1) @@ -434,7 +434,7 @@ Return discrete fourier transform sample frequencies for use with containing the frequency bin centers at every sample point. `fs` is the sample rate of the input signal. """ -rfftfreq(n::Int, fs::Real=1) = Frequencies((n >> 1)+1, (n >> 1)+1, fs/n) +rfftfreq(n::Int, fs::Number=1) = Frequencies((n >> 1)+1, (n >> 1)+1, fs/n) fftshift(x::Frequencies) = (x.nreal-x.n:x.nreal-1)*x.multiplier From 0c3db06d3cfacca2b2f8db704d21f9cf0c2abc15 Mon Sep 17 00:00:00 2001 From: Carsten Bauer Date: Sun, 12 May 2019 00:35:55 +0200 Subject: [PATCH 3/6] tests --- test/runtests.jl | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/runtests.jl b/test/runtests.jl index 5acd839..f860537 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -81,6 +81,23 @@ end @test AbstractFFTs.ifftshift([1 2 3; 4 5 6], 1:2) == [5 6 4; 2 3 1] end +@testset "FFT Frequencies" begin + # N even + @test fftfreq(8) == [0.0, 0.125, 0.25, 0.375, -0.5, -0.375, -0.25, -0.125] + @test rfftfreq(8) == [0.0, 0.125, 0.25, 0.375, 0.5] + @test fftshift(fftfreq(8)) == -0.5:0.125:0.375 + + # N odd + @test fftfreq(5) == [0.0, 0.2, 0.4, -0.4, -0.2] + @test rfftfreq(5) == [0.0, 0.2, 0.4] + @test fftshift(fftfreq(5)) == -0.4:0.2:0.4 + + # Sampling Frequency + @test fftfreq(5, 2) == [0.0, 0.4, 0.8, -0.8, -0.4] + # <:Number type compatibility + @test eltype(fftfreq(5, ComplexF64(2))) == ComplexF64 +end + @testset "normalization" begin # normalization should be inferable even if region is only inferred as ::Any, # need to wrap in another function to test this (note that p.region::Any for From bf84d0d3da026277ee1b5f60971ca708dd8436f9 Mon Sep 17 00:00:00 2001 From: Carsten Bauer Date: Sun, 12 May 2019 00:40:21 +0200 Subject: [PATCH 4/6] added functions to docs/src/api.md --- docs/src/api.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/src/api.md b/docs/src/api.md index 2b87105..c9b7b98 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -21,4 +21,6 @@ AbstractFFTs.plan_brfft AbstractFFTs.plan_irfft AbstractFFTs.fftshift AbstractFFTs.ifftshift +AbstractFFTs.fftfreq +AbstractFFTs.rfftfreq ``` From c68231e94125f53c74abcb41d501f78196c7ad7a Mon Sep 17 00:00:00 2001 From: Carsten Bauer Date: Sun, 12 May 2019 18:20:14 +0200 Subject: [PATCH 5/6] implementing comments --- src/definitions.jl | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/definitions.jl b/src/definitions.jl index 0f7c4be..27f7a73 100644 --- a/src/definitions.jl +++ b/src/definitions.jl @@ -400,15 +400,20 @@ end struct Frequencies{T<:Number} <: AbstractVector{T} - nreal::Int + n_nonnegative::Int n::Int multiplier::T + + Frequencies(n_nonnegative::Int, n::Int, multiplier::T) where {T<:Number} = begin + 1 ≤ n_nonnegative ≤ n || error("Condition 1 ≤ n_nonnegative ≤ n isn't satisfied.") + return new{T}(n_nonnegative, n, multiplier) + end end unsafe_getindex(x::Frequencies, i::Int) = - (i-1+ifelse(i <= x.nreal, 0, -x.n))*x.multiplier -function Base.getindex(x::Frequencies, i::Int) - (i >= 1 && i <= x.n) || throw(BoundsError()) + (i-1+ifelse(i <= x.n_nonnegative, 0, -x.n))*x.multiplier +@inline function Base.getindex(x::Frequencies, i::Int) + @boundscheck Base.checkbounds(x, i) unsafe_getindex(x, i) end @@ -420,23 +425,23 @@ Base.step(x::Frequencies) = x.multiplier """ fftfreq(n, fs=1) -Return discrete fourier transform sample frequencies. The returned -Frequencies object is an AbstractVector containing the frequency +Return the discrete Fourier transform (DFT) sample frequencies for a DFT of length `n`. The returned +`Frequencies` object is an `AbstractVector` containing the frequency bin centers at every sample point. `fs` is the sample rate of the input signal. """ -fftfreq(n::Int, fs::Number=1) = Frequencies(((n-1) >> 1)+1, n, fs/n) +fftfreq(n::Int, fs::Number=1) = Frequencies((n+1) >> 1, n, fs/n) """ rfftfreq(n, fs=1) -Return discrete fourier transform sample frequencies for use with -`rfft`. The returned Frequencies object is an AbstractVector +Return the discrete Fourier transform (DFT) sample frequencies for a real DFT of length `n`. +The returned `Frequencies` object is an `AbstractVector` containing the frequency bin centers at every sample point. `fs` is the sample rate of the input signal. """ rfftfreq(n::Int, fs::Number=1) = Frequencies((n >> 1)+1, (n >> 1)+1, fs/n) -fftshift(x::Frequencies) = (x.nreal-x.n:x.nreal-1)*x.multiplier +fftshift(x::Frequencies) = (x.n_nonnegative-x.n:x.n_nonnegative-1)*x.multiplier ############################################################################## From 5f113099d93cba973de4c9534297a5310274fdce Mon Sep 17 00:00:00 2001 From: Carsten Bauer Date: Mon, 13 May 2019 21:04:47 +0200 Subject: [PATCH 6/6] throw ArgumentError instead of ErrorException --- src/definitions.jl | 2 +- test/runtests.jl | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/definitions.jl b/src/definitions.jl index 27f7a73..2f10d0c 100644 --- a/src/definitions.jl +++ b/src/definitions.jl @@ -405,7 +405,7 @@ struct Frequencies{T<:Number} <: AbstractVector{T} multiplier::T Frequencies(n_nonnegative::Int, n::Int, multiplier::T) where {T<:Number} = begin - 1 ≤ n_nonnegative ≤ n || error("Condition 1 ≤ n_nonnegative ≤ n isn't satisfied.") + 1 ≤ n_nonnegative ≤ n || throw(ArgumentError("Condition 1 ≤ n_nonnegative ≤ n isn't satisfied.")) return new{T}(n_nonnegative, n, multiplier) end end diff --git a/test/runtests.jl b/test/runtests.jl index f860537..020aa68 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -96,6 +96,8 @@ end @test fftfreq(5, 2) == [0.0, 0.4, 0.8, -0.8, -0.4] # <:Number type compatibility @test eltype(fftfreq(5, ComplexF64(2))) == ComplexF64 + + @test_throws ArgumentError Frequencies(12, 10, 1) end @testset "normalization" begin