Skip to content

Commit 19ba34c

Browse files
committed
Be smarter about the initial filter state
Compute an initial state for filt such that its response to a step function is steady state.
1 parent 14aa2fc commit 19ba34c

File tree

2 files changed

+42
-27
lines changed

2 files changed

+42
-27
lines changed

base/dsp.jl

+36-25
Original file line numberDiff line numberDiff line change
@@ -15,46 +15,39 @@ filt{T<:Number}(b::Union(AbstractVector{T}, T), a::Union(AbstractVector{T}, T),
1515

1616
# in-place filtering; both the input and filter state are modified in-place
1717
function filt!{T<:Number}(b::Union(AbstractVector{T}, T), a::Union(AbstractVector{T}, T),
18-
x::AbstractVector{T}, si::AbstractVector{T}=zeros(T, max(length(a), length(b))-1))
18+
x::AbstractVector{T}, si::Union(AbstractVector{T}, Nothing)=nothing)
1919
if isempty(b); error("b must be non-empty"); end
2020
if isempty(a); error("a must be non-empty"); end
2121
if a[1]==0; error("a[1] must be nonzero"); end
2222

2323
as = length(a)
2424
bs = length(b)
2525
sz = max(as, bs)
26+
xs = size(x,1)
2627

27-
if sz == 1
28-
# Simple scaling without memory; quick exit
29-
return scale!(x, b[1]/a[1])
30-
end
31-
32-
if bs<sz
33-
# Ensure b has at least as many elements as a
34-
newb = zeros(T,sz)
35-
newb[1:bs] = b
36-
b = newb
37-
end
28+
# Quick exits
29+
sz == 1 && return scale!(x, b[1]/a[1]) # Simple scaling; no filter state
30+
xs == 0 && return x # No data; return the same empty array
3831

39-
xs = size(x,1)
40-
silen = sz-1
41-
size(si) == (silen,) || error("the vector of initial conditions must have exactly max(length(a),length(b))-1 elements")
32+
has_denominator = (as > 1)
33+
# Make the filter coefficients the same length
34+
a = copy!(zeros(T, sz), a)
35+
b = copy!(zeros(T, sz), b)
4236

4337
if a[1] != 1
4438
# Normalize the coefficients such that a[1] == 1
45-
norml = a[1]
46-
a ./= norml
47-
b ./= norml
39+
norml = one(T) ./ a[1]
40+
scale!(a, norml)
41+
scale!(b, norml)
4842
end
4943

50-
if as > 1
51-
if as<sz
52-
# Pad a to be the same length as b
53-
newa = zeros(T,sz)
54-
newa[1:as] = a
55-
a = newa
56-
end
44+
silen = sz-1
45+
if si == nothing
46+
si = scale!(filt_stepstate(b, a), x[1])
47+
end
48+
size(si) == (silen,) || error("the vector of initial conditions must have exactly max(length(a),length(b))-1 elements")
5749

50+
if has_denominator
5851
@inbounds begin
5952
for i=1:xs
6053
val = si[1] + b[1]*x[i]
@@ -80,6 +73,24 @@ function filt!{T<:Number}(b::Union(AbstractVector{T}, T), a::Union(AbstractVecto
8073
return x
8174
end
8275

76+
# Compute an initial state for filt with coefficients (b,a) such that its
77+
# response to a step function is steady state.
78+
function filt_stepstate{T<:Number}(b::Union(AbstractVector{T}, T), a::Union(AbstractVector{T}, T))
79+
sz = length(a)
80+
sz == length(b) || error("a and b must be the same length")
81+
sz > 0 || error("a and b must have at least one element each")
82+
a[1] == 1 || error("a and b must be normalized such that a[1] == 1")
83+
84+
sz == 1 && return T[]
85+
86+
# construct the companion matrix A and vector B:
87+
A = [-a[2:end] [eye(T, sz-2); zeros(T, 1, sz-2)]]
88+
B = b[2:end] - a[2:end] * b[1]
89+
# Solve si = A*si + B
90+
# (I - A)*si = B
91+
return (I - A) \ B
92+
end
93+
8394
function deconv{T}(b::StridedVector{T}, a::StridedVector{T})
8495
lb = size(b,1)
8596
la = size(a,1)

test/dsp.jl

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
# Filter
22
b = [1., 2., 3., 4.]
33
x = [1., 1., 0., 1., 1., 0., 0., 0.]
4-
@test filt(b, 1., x) == [1., 3., 5., 8., 7., 5., 7., 4.]
5-
@test filt(b, [1., -0.5], x) == [1., 3.5, 6.75, 11.375, 12.6875, 11.34375, 12.671875, 10.3359375]
4+
@test filt(b, 1., x, [0.,0.,0.]) == [1., 3., 5., 8., 7., 5., 7., 4.]
5+
@test filt(b, [1., -0.5], x, [0.,0.,0.]) == [1., 3.5, 6.75, 11.375, 12.6875, 11.34375, 12.671875, 10.3359375]
6+
# A low-pass 5-pole butterworth filter with W_n = 0.25
7+
b = [0.003279216306360201,0.016396081531801006,0.03279216306360201,0.03279216306360201,0.016396081531801006,0.003279216306360201]
8+
a = [1.0,-2.4744161749781606,2.8110063119115782,-1.703772240915465,0.5444326948885326,-0.07231566910295834]
9+
@test_approx_eq filt(b,a,ones(10)) ones(10) # Should not affect a DC offset
610

711
# Convolution
812
a = [1., 2., 1., 2.]

0 commit comments

Comments
 (0)