Skip to content

Commit cd2912c

Browse files
Expose resample_filter optional arguments in resample, FIRFilter (#621)
* expose resample_filter options in resample * document `FIRFilter`, `resample_filter` _resample_filter * const fields for `FIRKernel`s * misc, spacing remove inbounds, correct typo * fix `filtfilt` pad length * Apply suggestions from code review Co-authored-by: Martin Holters <martin.holters@hsu-hh.de> * add tests * add more tests * reuse `FIRFilter`s for arrays * interpolation * test resample with resample_filter * Specify Int64, use `@test` broken argument Co-authored-by: Martin Holters <martin.holters@hsu-hh.de> --------- Co-authored-by: Martin Holters <martin.holters@hsu-hh.de>
1 parent 06f8776 commit cd2912c

File tree

7 files changed

+199
-163
lines changed

7 files changed

+199
-163
lines changed

docs/src/filters.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ parameters.
5959

6060
```@docs
6161
FIRFilter
62+
resample_filter
6263
```
6364

6465
## Filter application
@@ -196,7 +197,7 @@ coefa
196197
## Examples
197198

198199
Construct a 4th order elliptic lowpass filter with normalized cutoff
199-
frequency 0.2, 0.5 dB of passband ripple, and 30 dB attentuation in
200+
frequency 0.2, 0.5 dB of passband ripple, and 30 dB attenuation in
200201
the stopband and extract the coefficients of the numerator and
201202
denominator of the transfer function:
202203

src/Filters/design.jl

Lines changed: 33 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ function landen(k::Real)
124124
kn = Vector{typeof(k)}(undef, niter)
125125
# Eq. (50)
126126
for i = 1:niter
127-
kn[i] = k = abs2(k/(1+sqrt(1-abs2(k))))
127+
kn[i] = k = abs2(k / (1 + sqrt(1 - abs2(k))))
128128
end
129129
kn
130130
end
@@ -223,7 +223,7 @@ end
223223
Elliptic(n::Integer, rp::Real, rs::Real)
224224
225225
`n` pole elliptic (Cauer) filter with `rp` dB ripple in the
226-
passband and `rs` dB attentuation in the stopband.
226+
passband and `rs` dB attenuation in the stopband.
227227
"""
228228
Elliptic(n::Integer, rp::Real, rs::Real) = Elliptic(Float64, n, rp, rs)
229229

@@ -473,25 +473,25 @@ Here, `m` and `n` are respectively the numbers of zeros and poles in the s-domai
473473
then additional `n-m` zeros are added at `z = -1`.
474474
"""
475475
function bilinear(f::ZeroPoleGain{:s,Z,P,K}, fs::Real) where {Z,P,K}
476-
ztype = typeof(0 + zero(Z)/fs)
476+
ztype = typeof(0 + zero(Z) / fs)
477477
z = fill(convert(ztype, -1), max(length(f.p), length(f.z)))
478478

479479
ptype = typeof(0 + zero(P) / fs)
480480
p = Vector{ptype}(undef, length(f.p))
481481

482482
num = one(one(fs) - one(Z))
483483
for i = 1:length(f.z)
484-
z[i] = (2 + f.z[i] / fs)/(2 - f.z[i] / fs)
484+
z[i] = (2 + f.z[i] / fs) / (2 - f.z[i] / fs)
485485
num *= (2 * fs - f.z[i])
486486
end
487487

488488
den = one(one(fs) - one(P))
489489
for i = 1:length(f.p)
490-
p[i] = (2 + f.p[i] / fs)/(2 - f.p[i]/fs)
490+
p[i] = (2 + f.p[i] / fs) / (2 - f.p[i] / fs)
491491
den *= (2 * fs - f.p[i])
492492
end
493493

494-
ZeroPoleGain{:z}(z, p, f.k * real(num)/real(den))
494+
ZeroPoleGain{:z}(z, p, f.k * real(num) / real(den))
495495
end
496496

497497
# Pre-warp filter frequencies for digital filtering
@@ -545,12 +545,12 @@ end
545545
# Get length and alpha for Kaiser window filter with specified
546546
# transition band width and stopband attenuation in dB
547547
function kaiserord(transitionwidth::Real, attenuation::Real=60)
548-
n = ceil(Int, (attenuation - 7.95)/*2.285*transitionwidth))+1
548+
n = ceil(Int, (attenuation - 7.95) /* 2.285 * transitionwidth)) + 1
549549

550550
if attenuation > 50
551-
β = 0.1102*(attenuation - 8.7)
551+
β = 0.1102 * (attenuation - 8.7)
552552
elseif attenuation >= 21
553-
β = 0.5842*(attenuation - 21)^0.4 + 0.07886*(attenuation - 21)
553+
β = 0.5842 * (attenuation - 21)^0.4 + 0.07886 * (attenuation - 21)
554554
else
555555
β = 0.0
556556
end
@@ -670,50 +670,47 @@ function digitalfilter(ftype::FilterType, proto::FIRWindow; fs::Real=2)
670670
coefs = firprototype(length(proto.window), ftype, fs)
671671
@assert length(proto.window) == length(coefs)
672672
out = (coefs .*= proto.window)
673-
proto.scale ? rmul!(out, 1/scalefactor(out, ftype, fs)) : out
673+
proto.scale ? rmul!(out, 1 / scalefactor(out, ftype, fs)) : out
674674
end
675675

676676

677-
# Compute FIR coefficients necessary for arbitrary rate resampling
678-
function resample_filter(rate::AbstractFloat, Nϕ = 32, rel_bw = 1.0, attenuation = 60)
679-
f_nyq = rate >= 1.0 ? 1.0/: rate/
680-
cutoff = f_nyq * rel_bw
681-
trans_width = cutoff * 0.2
682-
683-
# Determine resampling filter order
684-
hLen, α = kaiserord(trans_width, attenuation)
677+
"""
678+
resample_filter(rate::AbstractFloat, Nϕ::Integer=32, rel_bw::Real=1.0, att=60)
685679
686-
# Round the number of taps up to a multiple of Nϕ.
687-
# Otherwise the missing taps will be filled with 0.
688-
hLen =* ceil(Int, hLen/Nϕ)
680+
Compute FIR coefficients suitable for arbitrary rate resampling
681+
with `Nϕ` filters / phases, stop-band attenuation `att`, and relative bandwidth `rel_bw`.
682+
"""
683+
function resample_filter(rate::AbstractFloat, Nϕ::Integer=32, rel_bw::Real=1.0, attenuation=60)
684+
f_nyq = rate >= 1.0 ? 1.0 /: rate /
685+
return _resample_filter(f_nyq, Nϕ, rel_bw, attenuation)
686+
end
689687

690-
# Ensure that the filter is an odd length
691-
if (iseven(hLen))
692-
hLen += 1
693-
end
688+
"""
689+
resample_filter(rate::Union{Integer,Rational}, rel_bw::Real=1.0, attenuation=60)
694690
695-
# Design filter
696-
h = digitalfilter(Lowpass(cutoff), FIRWindow(kaiser(hLen, α)))
697-
rmul!(h, Nϕ)
691+
Compute FIR coefficients suitable for rational rate resampling,
692+
with stop-band attenuation `att`, and relative bandwidth `rel_bw`.
693+
"""
694+
function resample_filter(rate::Union{Integer,Rational}, rel_bw::Real=1.0, attenuation=60)
695+
= numerator(rate)
696+
decimation = denominator(rate)
697+
f_nyq = min(1 / Nϕ, 1 / decimation)
698+
return _resample_filter(f_nyq, Nϕ, rel_bw, attenuation)
698699
end
699700

700-
# Compute FIR coefficients necessary for rational rate resampling
701-
function resample_filter(rate::Union{Integer,Rational}, rel_bw = 1.0, attenuation = 60)
702-
= numerator(rate)
703-
decimation = denominator(rate)
704-
f_nyq = min(1/Nϕ, 1/decimation)
701+
function _resample_filter(f_nyq, Nϕ, rel_bw, attenuation)
705702
cutoff = f_nyq * rel_bw
706703
trans_width = cutoff * 0.2
707704

708705
# Determine resampling filter order
709706
hLen, α = kaiserord(trans_width, attenuation)
710707

711-
# Round the number of taps up to a multiple of Nϕ (same as interpolation factor).
708+
# Round the number of taps up to a multiple of Nϕ.
712709
# Otherwise the missing taps will be filled with 0.
713-
hLen =* ceil(Int, hLen/Nϕ)
710+
hLen =* ceil(Int, hLen / Nϕ)
714711

715712
# Ensure that the filter is an odd length
716-
if (iseven(hLen))
713+
if iseven(hLen)
717714
hLen += 1
718715
end
719716

src/Filters/filt.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,10 +340,10 @@ end
340340
# Zero phase digital filtering for second order sections
341341
function filtfilt(f::SecondOrderSections{:z,T,G}, x::AbstractArray{S}) where {T,G,S}
342342
zi = filt_stepstate(f)
343-
pad_length = 6 * length(f.biquads)
343+
pad_length = min(6 * length(f.biquads), size(x, 1) - 1)
344344
t = promote_type(T, G, S)
345345
zitmp = similar(zi, t)
346-
extrapolated = Vector{t}(undef, size(x, 1)+pad_length*2)
346+
extrapolated = Vector{t}(undef, size(x, 1) + pad_length * 2)
347347
out = similar(x, t)
348348

349349
for col in CartesianIndices(axes(x)[2:end])

0 commit comments

Comments
 (0)