Skip to content

Commit c6663ab

Browse files
Merge pull request #566 from JuliaDSP/mh/backport-539
Backport `filt` fix (#516 and #539) and bump version to 0.7.10
2 parents 26ca32e + dd9fffc commit c6663ab

14 files changed

+281
-294
lines changed

Project.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "DSP"
22
uuid = "717857b8-e6f2-59f4-9121-6e50c889abd2"
3-
version = "0.7.9"
3+
version = "0.7.10"
44

55
[deps]
66
Compat = "34da2185-b29b-5c13-b0c7-acf172513d20"

src/DSP.jl

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module DSP
33
using FFTW
44
using LinearAlgebra: mul!, rmul!
55
using IterTools: subsets
6+
using Compat: Compat
67

78
export conv, deconv, filt, filt!, xcorr
89

src/Filters/design.jl

+9-11
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,7 @@ function Chebyshev2(::Type{T}, n::Integer, ripple::Real) where {T<:Real}
8686

8787
ε = 1/sqrt(10^(convert(T, ripple)/10)-1)
8888
p = chebyshev_poles(T, n, ε)
89-
for i = 1:length(p)
90-
p[i] = inv(p[i])
91-
end
89+
map!(inv, p, p)
9290

9391
z = zeros(Complex{T}, n-isodd(n))
9492
k = one(T)
@@ -155,7 +153,7 @@ function asne(w::Number, k::Real)
155153
# Eq. (50)
156154
k = abs2(k/(1+sqrt(1-abs2(k))))
157155
# Eq. (56)
158-
w = 2*w/((1+k)*(1+sqrt(1-abs2(kold)*w^2)))
156+
w = 2w / ((1 + k) * (1 + sqrt(muladd(-abs2(kold), w^2, 1))))
159157
end
160158
2*asin(w)/π
161159
end
@@ -355,9 +353,9 @@ function transform_prototype(ftype::Bandpass, proto::ZeroPoleGain{:s})
355353
newz = zeros(TR, 2*nz+np-ncommon)
356354
newp = zeros(TR, 2*np+nz-ncommon)
357355
for (oldc, newc) in ((p, newp), (z, newz))
358-
for i = 1:length(oldc)
356+
for i in eachindex(oldc)
359357
b = oldc[i] * ((ftype.w2 - ftype.w1)/2)
360-
pm = sqrt(b^2 - ftype.w2 * ftype.w1)
358+
pm = sqrt(muladd(-ftype.w2, ftype.w1, b^2))
361359
newc[2i-1] = b + pm
362360
newc[2i] = b - pm
363361
end
@@ -372,25 +370,25 @@ function transform_prototype(ftype::Bandstop, proto::ZeroPoleGain{:s})
372370
k = proto.k
373371
nz = length(z)
374372
np = length(p)
375-
npairs = nz+np-min(nz, np)
373+
npairs = max(nz, np)
376374
TR = Base.promote_eltype(z, p)
377375
newz = Vector{TR}(undef, 2*npairs)
378376
newp = Vector{TR}(undef, 2*npairs)
379377

380378
num = one(eltype(z))
381379
for i = 1:nz
382380
num *= -z[i]
383-
b = (ftype.w2 - ftype.w1)/2/z[i]
384-
pm = sqrt(b^2 - ftype.w2 * ftype.w1)
381+
b = (ftype.w2 - ftype.w1)/2z[i]
382+
pm = sqrt(muladd(-ftype.w2, ftype.w1, b^2))
385383
newz[2i-1] = b - pm
386384
newz[2i] = b + pm
387385
end
388386

389387
den = one(eltype(p))
390388
for i = 1:np
391389
den *= -p[i]
392-
b = (ftype.w2 - ftype.w1)/2/p[i]
393-
pm = sqrt(b^2 - ftype.w2 * ftype.w1)
390+
b = (ftype.w2 - ftype.w1)/2p[i]
391+
pm = sqrt(muladd(-ftype.w2, ftype.w1, b^2))
394392
newp[2i-1] = b - pm
395393
newp[2i] = b + pm
396394
end

src/Filters/filt.jl

+54-59
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77

88
## PolynomialRatio
9-
_zerosi(f::PolynomialRatio{:z,T}, x::AbstractArray{S}) where {T,S} =
9+
_zerosi(f::PolynomialRatio{:z,T}, ::AbstractArray{S}) where {T,S} =
1010
zeros(promote_type(T, S), max(-firstindex(f.a), -firstindex(f.b)))
1111

1212
"""
@@ -35,7 +35,7 @@ selected based on the data and filter length.
3535
filt(f::PolynomialRatio{:z}, x, si=_zerosi(f, x)) = filt(coefb(f), coefa(f), x, si)
3636

3737
## SecondOrderSections
38-
_zerosi(f::SecondOrderSections{:z,T,G}, x::AbstractArray{S}) where {T,G,S} =
38+
_zerosi(f::SecondOrderSections{:z,T,G}, ::AbstractArray{S}) where {T,G,S} =
3939
zeros(promote_type(T, G, S), 2, length(f.biquads))
4040

4141
# filt! algorithm (no checking, returns si)
@@ -44,14 +44,14 @@ function _filt!(out::AbstractArray, si::AbstractArray{S,N}, f::SecondOrderSectio
4444
g = f.g
4545
biquads = f.biquads
4646
n = length(biquads)
47-
@inbounds for i = 1:size(x, 1)
47+
@inbounds for i in axes(x, 1)
4848
yi = x[i, col]
4949
for fi = 1:n
5050
biquad = biquads[fi]
5151
xi = yi
52-
yi = si[1, fi] + biquad.b0*xi
53-
si[1, fi] = si[2, fi] + biquad.b1*xi - biquad.a1*yi
54-
si[2, fi] = biquad.b2*xi - biquad.a2*yi
52+
yi = muladd(biquad.b0, xi, si[1, fi])
53+
si[1, fi] = muladd(biquad.a1, -yi, muladd(biquad.b1, xi, si[2, fi]))
54+
si[2, fi] = muladd(biquad.b2, xi, -biquad.a2 * yi)
5555
end
5656
out[i, col] = yi*g
5757
end
@@ -80,17 +80,17 @@ filt(f::SecondOrderSections{:z,T,G}, x::AbstractArray{S}, si=_zerosi(f, x)) wher
8080
filt!(Array{promote_type(T, G, S)}(undef, size(x)), f, x, si)
8181

8282
## Biquad
83-
_zerosi(f::Biquad{:z,T}, x::AbstractArray{S}) where {T,S} =
83+
_zerosi(::Biquad{:z,T}, ::AbstractArray{S}) where {T,S} =
8484
zeros(promote_type(T, S), 2)
8585

8686
# filt! algorithm (no checking, returns si)
8787
function _filt!(out::AbstractArray, si1::Number, si2::Number, f::Biquad{:z},
8888
x::AbstractArray, col::Int)
89-
@inbounds for i = 1:size(x, 1)
89+
@inbounds for i in axes(x, 1)
9090
xi = x[i, col]
91-
yi = si1 + f.b0*xi
92-
si1 = si2 + f.b1*xi - f.a1*yi
93-
si2 = f.b2*xi - f.a2*yi
91+
yi = muladd(f.b0, xi, si1)
92+
si1 = muladd(f.a1, -yi, muladd(f.b1, xi, si2))
93+
si2 = muladd(f.b2, xi, -f.a2 * yi)
9494
out[i, col] = yi
9595
end
9696
(si1, si2)
@@ -105,9 +105,8 @@ function filt!(out::AbstractArray, f::Biquad{:z}, x::AbstractArray,
105105
(size(si, 1) != 2 || (N > 1 && Base.trailingsize(si, 2) != ncols)) &&
106106
error("si must have two rows and 1 or nsignals columns")
107107

108-
initial_si = si
109108
for col = 1:ncols
110-
_filt!(out, initial_si[1, N > 1 ? col : 1], initial_si[2, N > 1 ? col : 1], f, x, col)
109+
_filt!(out, si[1, N > 1 ? col : 1], si[2, N > 1 ? col : 1], f, x, col)
111110
end
112111
out
113112
end
@@ -170,13 +169,13 @@ function filt!(out::AbstractVector, f::DF2TFilter{<:PolynomialRatio,<:Vector}, x
170169
if n == 1
171170
mul!(out, x, b[1])
172171
else
173-
@inbounds for i=1:length(x)
172+
@inbounds for i in eachindex(x, out)
174173
xi = x[i]
175-
val = si[1] + b[1]*xi
174+
val = muladd(b[1], xi, si[1])
176175
for j=2:n-1
177-
si[j-1] = si[j] + b[j]*xi - a[j]*val
176+
si[j-1] = muladd(a[j], -val, muladd(b[j], xi, si[j]))
178177
end
179-
si[n-1] = b[n]*xi - a[n]*val
178+
si[n-1] = muladd(b[n], xi, -a[n] * val)
180179
out[i] = val
181180
end
182181
end
@@ -200,13 +199,13 @@ DF2TFilter(coef::Biquad{:z,T}, state::Vector{S}=zeros(T, 2)) where {T,S} =
200199
function filt!(out::AbstractVector, f::DF2TFilter{<:Biquad,<:Vector}, x::AbstractVector)
201200
length(x) != length(out) && throw(ArgumentError("out size must match x"))
202201
si = f.state
203-
(si[1], si[2]) =_filt!(out, si[1], si[2], f.coef, x, 1)
202+
(si[1], si[2]) = _filt!(out, si[1], si[2], f.coef, x, 1)
204203
out
205204
end
206205

207206
# Variant that allocates the output
208-
filt(f::DF2TFilter{<:FilterCoefficients{:z},S}, x::AbstractVector) where {S<:Array} =
209-
filt!(Vector{eltype(S)}(undef, length(x)), f, x)
207+
filt(f::DF2TFilter{<:FilterCoefficients{:z},<:Array{T}}, x::AbstractVector) where {T} =
208+
filt!(Vector{T}(undef, length(x)), f, x)
210209

211210
# Fall back to SecondOrderSections
212211
DF2TFilter(coef::FilterCoefficients{:z}) = DF2TFilter(convert(SecondOrderSections, coef))
@@ -346,7 +345,7 @@ filtfilt(f::PolynomialRatio{:z}, x) = filtfilt(coefb(f), coefa(f), x)
346345
# response to a step function is steady state.
347346
function filt_stepstate(b::Union{AbstractVector{T}, T}, a::Union{AbstractVector{T}, T}) where T<:Number
348347
scale_factor = a[1]
349-
if scale_factor != 1.0
348+
if !isone(scale_factor)
350349
a = a ./ scale_factor
351350
b = b ./ scale_factor
352351
end
@@ -362,8 +361,8 @@ function filt_stepstate(b::Union{AbstractVector{T}, T}, a::Union{AbstractVector{
362361
as<sz && (a = copyto!(zeros(eltype(a), sz), a))
363362

364363
# construct the companion matrix A and vector B:
365-
A = [-a[2:end] [I; zeros(T, 1, sz-2)]]
366-
B = b[2:end] - a[2:end] * b[1]
364+
A = [-a[2:end] Matrix{T}(I, sz-1, sz-2)]
365+
B = @views @. muladd(a[2:end], -b[1], b[2:end])
367366
# Solve si = A*si + B
368367
# (I - A)*si = B
369368
scale_factor \ (I - A) \ B
@@ -375,18 +374,18 @@ function filt_stepstate(f::SecondOrderSections{:z,T}) where T
375374
y = one(T)
376375
for i = 1:length(biquads)
377376
biquad = biquads[i]
377+
a1, a2, b0, b1, b2 = biquad.a1, biquad.a2, biquad.b0, biquad.b1, biquad.b2
378378

379379
# At steady state, we have:
380380
# y = s1 + b0*x
381381
# s1 = s2 + b1*x - a1*y
382382
# s2 = b2*x - a2*y
383383
# where x is the input and y is the output. Solving these
384384
# equations yields the following.
385-
si[1, i] = (-(biquad.a1 + biquad.a2)*biquad.b0 + biquad.b1 + biquad.b2)/
386-
(1 + biquad.a1 + biquad.a2)*y
387-
si[2, i] = (biquad.a1*biquad.b2 - biquad.a2*(biquad.b0 + biquad.b1) + biquad.b2)/
388-
(1 + biquad.a1 + biquad.a2)*y
389-
y *= (biquad.b0 + biquad.b1 + biquad.b2)/(1 + biquad.a1 + biquad.a2)
385+
den = (1 + a1 + a2)
386+
si[1, i] = muladd((a1 + a2), -b0, b1 + b2) / den * y
387+
si[2, i] = muladd(a1, b2, muladd(-a2, (b0 + b1), b2)) / den * y
388+
y *= (b0 + b1 + b2) / den
390389
end
391390
si
392391
end
@@ -397,8 +396,8 @@ end
397396
Apply filter or filter coefficients `h` along the first dimension
398397
of array `x` using a naïve time-domain algorithm
399398
"""
400-
function tdfilt(h::AbstractVector, x::AbstractArray{T}) where T<:Real
401-
filt!(Array{T}(undef, size(x)), h, ones(eltype(h), 1), x)
399+
function tdfilt(h::AbstractVector{H}, x::AbstractArray{T}) where {H,T<:Real}
400+
filt(h, one(H), x)
402401
end
403402

404403
"""
@@ -407,12 +406,12 @@ end
407406
Like `tdfilt`, but writes the result into array `out`. Output array `out` may
408407
not be an alias of `x`, i.e. filtering may not be done in place.
409408
"""
410-
function tdfilt!(out::AbstractArray, h::AbstractVector, x::AbstractArray)
411-
filt!(out, h, ones(eltype(h), 1), x)
409+
function tdfilt!(out::AbstractArray, h::AbstractVector{H}, x::AbstractArray) where H
410+
filt!(out, h, one(H), x)
412411
end
413412

414-
filt(h::AbstractArray, x::AbstractArray) =
415-
filt!(Array{eltype(x)}(undef, size(x)), h, x)
413+
filt(h::AbstractVector{H}, x::AbstractArray{T}) where {H,T} =
414+
filt!(Array{promote_type(H, T)}(undef, size(x)), h, x)
416415

417416
#
418417
# fftfilt and filt
@@ -447,48 +446,44 @@ end
447446
# Like fftfilt! but does not check if out and x are the same size
448447
function _fftfilt!(
449448
out::AbstractArray{<:Real},
450-
b::AbstractVector{<:Real},
449+
b::AbstractVector{H},
451450
x::AbstractArray{T},
452451
nfft::Integer
453-
) where T<:Real
452+
) where {T<:Real,H<:Real}
454453
nb = length(b)
455454
nx = size(x, 1)
456-
normfactor = 1/nfft
455+
normfactor = nfft
456+
W = promote_type(H, T)
457457

458458
L = min(nx, nfft - (nb - 1))
459-
tmp1 = Vector{T}(undef, nfft)
460-
tmp2 = Vector{Complex{T}}(undef, nfft >> 1 + 1)
459+
tmp1 = Vector{W}(undef, nfft)
460+
tmp2 = Vector{Complex{W}}(undef, nfft >> 1 + 1)
461461

462462
p1 = plan_rfft(tmp1)
463463
p2 = plan_brfft(tmp2, nfft)
464464

465465
# FFT of filter
466466
filterft = similar(tmp2)
467-
tmp1[1:nb] .= b .* normfactor
468-
tmp1[nb+1:end] .= zero(T)
467+
tmp1[1:nb] .= b ./ normfactor
468+
tmp1[nb+1:end] .= zero(W)
469469
mul!(filterft, p1, tmp1)
470470

471471
# FFT of chunks
472-
for colstart = 0:nx:length(x)-1
473-
off = 1
474-
while off <= nx
475-
npadbefore = max(0, nb - off)
476-
xstart = off - nb + npadbefore + 1
477-
n = min(nfft - npadbefore, nx - xstart + 1)
472+
for colstart = 0:nx:length(x)-1, off = 1:L:nx
473+
npadbefore = max(0, nb - off)
474+
xstart = off - nb + npadbefore + 1
475+
n = min(nfft - npadbefore, nx - xstart + 1)
478476

479-
tmp1[1:npadbefore] .= zero(T)
480-
tmp1[npadbefore+n+1:end] .= zero(T)
477+
tmp1[1:npadbefore] .= zero(W)
478+
tmp1[npadbefore+n+1:end] .= zero(W)
481479

482-
copyto!(tmp1, npadbefore+1, x, colstart+xstart, n)
483-
mul!(tmp2, p1, tmp1)
484-
broadcast!(*, tmp2, tmp2, filterft)
485-
mul!(tmp1, p2, tmp2)
480+
copyto!(tmp1, npadbefore+1, x, colstart+xstart, n)
481+
mul!(tmp2, p1, tmp1)
482+
broadcast!(*, tmp2, tmp2, filterft)
483+
mul!(tmp1, p2, tmp2)
486484

487-
# Copy to output
488-
copyto!(out, colstart+off, tmp1, nb, min(L, nx - off + 1))
489-
490-
off += L
491-
end
485+
# Copy to output
486+
copyto!(out, colstart+off, tmp1, nb, min(L, nx - off + 1))
492487
end
493488

494489
out
@@ -524,6 +519,6 @@ function filt_choose_alg!(
524519
end
525520
end
526521

527-
function filt_choose_alg!(out::AbstractArray, b::AbstractArray, x::AbstractArray)
522+
function filt_choose_alg!(out::AbstractArray, b::AbstractVector, x::AbstractArray)
528523
tdfilt!(out, b, x)
529524
end

0 commit comments

Comments
 (0)