Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve decision boundary between time domain and frequency filtering #273

Merged
merged 6 commits into from
May 3, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 19 additions & 20 deletions src/Filters/filt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -386,11 +386,13 @@ function filt_stepstate(f::SecondOrderSections{T}) where T
si
end

const SMALL_FILT_CUTOFF = 54

#
# filt implementation for FIR filters (faster than Base)
#

for n = 2:15
for n = 2:SMALL_FILT_CUTOFF
silen = n-1
si = [Symbol("si$i") for i = 1:silen]
@eval function filt!(out, b::NTuple{$n,T}, x) where T
Expand All @@ -412,7 +414,7 @@ for n = 2:15
end

let chain = :(throw(ArgumentError("invalid tuple size")))
for n = 15:-1:2
for n = SMALL_FILT_CUTOFF:-1:2
chain = quote
if length(h) == $n
filt!(out, ($([:(h[$i]) for i = 1:n]...),), x)
Expand Down Expand Up @@ -452,7 +454,7 @@ end
function _tdfilt!(out::AbstractArray, h::AbstractVector, x::AbstractArray)
if length(h) == 1
return mul!(out, h[1], x)
elseif length(h) <= 15
elseif length(h) <= SMALL_FILT_CUTOFF
return small_filt!(out, h, x)
end

Expand Down Expand Up @@ -484,21 +486,22 @@ filt(h::AbstractArray, x::AbstractArray) =
# fftfilt and filt
#

# Number of real operations required for overlap-save with nfft = 2^pow2 and filter
# length nb
os_fft_complexity(pow2, nb) = 4 * (2 ^ pow2 * (pow2 + 1)) / (2 ^ pow2 - nb + 1)
# Rough estimate of number of multiplications per output sample
os_fft_complexity(nfft, nb) = (nfft * log2(nfft) + nfft) / (nfft - nb + 1)

# Determine optimal length of the FFT for fftfilt
function optimalfftfiltlength(nb, nx)
first_pow2 = ceil(Int, log2(nb))
last_pow2 = ceil(Int, log2(nx + nb - 1))
complexities = os_fft_complexity.(first_pow2:last_pow2, nb)

# Find power of 2 with least complexity relative to the first power of 2
relative_ind_best_pow2 = argmin(complexities)

best_pow2 = first_pow2 + relative_ind_best_pow2 - 1
nfft = 2 ^ best_pow2
last_complexity = os_fft_complexity(2 ^ first_pow2, nb)
pow2 = first_pow2 + 1
while pow2 <= last_pow2
new_complexity = os_fft_complexity(2 ^ pow2, nb)
new_complexity > last_complexity && break
last_complexity = new_complexity
pow2 += 1
end
nfft = pow2 > last_pow2 ? 2 ^ last_pow2 : 2 ^ (pow2 - 1)

L = nfft - nb + 1
if L > nx
Expand Down Expand Up @@ -605,14 +608,10 @@ function filt_choose_alg!(
x::AbstractArray{<:Real}
)
nb = length(b)
nx = size(x, 1)

filtops_per_sample = min(nx, nb)

nfft = optimalfftfiltlength(nb, nx)
fftops_per_sample = os_fft_complexity(log2(nfft), nb)

if filtops_per_sample > fftops_per_sample
if nb > SMALL_FILT_CUTOFF
nx = size(x, 1)
nfft = optimalfftfiltlength(nb, nx)
_fftfilt!(out, b, x, nfft)
else
_tdfilt!(out, b, x)
Expand Down