@@ -183,52 +183,64 @@ DF2TFilter(coef::FilterCoefficients) = DF2TFilter(convert(SecondOrderSections, c
183183# filtfilt
184184#
185185
186- # Extrapolate the beginning of a signal for use by filtfilt. This
187- # computes:
188- #
189- # [(2 * x[1]) .- x[pad_length+1:-1:2],
190- # x,
191- # (2 * x[end]) .- x[end-1:-1:end-pad_length]]
192- #
193- # in place in output. The istart and n parameters determine the portion
194- # of the input signal x to extrapolate.
195- function extrapolate_signal! (out, ostart, sig, istart, n, pad_length)
196- length (out) >= n+ 2 * pad_length || error (" output is incorrectly sized" )
197- x = 2 * sig[istart]
186+ # Extrapolate the beginning of a signal for use by filtfilt. Computes:
187+ # [(2 * x[1]) .- x[pad_length+1:-1:2]]
188+ # in place in output.
189+ function extrapolate_start! (out, sig)
190+ x = 2 * sig[1 ]
191+ pad_length = length (out)
198192 for i = 1 : pad_length
199- out[ostart + i - 1 ] = x - sig[istart + pad_length+ 1 - i]
193+ out[i ] = x - sig[pad_length+ 2 - i]
200194 end
201- copy! (out, ostart+ pad_length, sig, istart, n)
202- x = 2 * sig[istart+ n- 1 ]
203- for i = 1 : pad_length
204- out[ostart+ n+ pad_length+ i- 1 ] = x - sig[istart+ n- 1 - i]
195+ out
196+ end
197+
198+ # Extrapolate the end of a signal for use by filtfilt. Computes:
199+ # (2 * x[end]) .- x[end-1:-1:end-pad_length]]
200+ # in place in output.
201+ function extrapolate_end! (out, sig)
202+ x = 2 * sig[end ]
203+ for i = 1 : length (out)
204+ out[i] = x - sig[end - i]
205205 end
206206 out
207207end
208208
209- # Zero phase digital filtering by processing data in forward and reverse direction
210- function iir_filtfilt (b :: AbstractVector , a :: AbstractVector , x :: AbstractArray )
211- zi = filt_stepstate (b, a )
212- zitmp = copy (zi )
213- pad_length = 3 * ( max ( length (a), length (b)) - 1 )
214- t = Base. promote_eltype (b, a , x)
215- extrapolated = Array (t, size (x, 1 ) + pad_length * 2 )
209+ padlength (coefs :: PolynomialRatio ) = 3 * ( max ( length (coefs . a), length (coefs . b)) - 1 )
210+ padlength (coefs :: SecondOrderSections ) = 6 * length (coefs . biquads )
211+ function filtfilt {T} (coefs :: Union(PolynomialRatio{T}, SecondOrderSections{T}) , x :: AbstractArray )
212+ zi = filt_stepstate (coefs )
213+ zitmp = similar (zi )
214+ t = Base. promote_eltype (T , x)
215+ extrapolated = Array (t, padlength (coefs) )
216216 out = similar (x, t)
217+ f = DF2TFilter (coefs, zitmp)
217218
218- istart = 1
219219 for i = 1 : Base. trailingsize (x, 2 )
220- extrapolate_signal! (extrapolated, 1 , x, istart, size (x, 1 ), pad_length)
221- reverse! (filt! (extrapolated, b, a, extrapolated, scale! (zitmp, zi, extrapolated[1 ])))
222- filt! (extrapolated, b, a, extrapolated, scale! (zitmp, zi, extrapolated[1 ]))
223- for j = 1 : size (x, 1 )
224- @inbounds out[j, i] = extrapolated[end - pad_length+ 1 - j]
225- end
226- istart += size (x, 1 )
220+ # Forward pass
221+ sigout = sub (out, :, i)
222+ sig = sub (x, :, i)
223+ extrapolate_start! (extrapolated, sig)
224+ scale! (zitmp, zi, extrapolated[1 ])
225+ filt! (extrapolated, f, extrapolated)
226+ filt! (sigout, f, sig)
227+ extrapolate_end! (extrapolated, sig)
228+ filt! (extrapolated, f, extrapolated)
229+
230+ # Reverse pass
231+ sigoutrev = sub (out, size (x, 1 ): - 1 : 1 , i)
232+ reverse! (extrapolated)
233+ scale! (zitmp, zi, extrapolated[1 ])
234+ filt! (extrapolated, f, extrapolated)
235+ filt! (sigoutrev, f, sigoutrev)
227236 end
228237
229238 out
230239end
231240
241+ # Support for other filter types
242+ filtfilt (f:: FilterCoefficients , x) = filtfilt (convert (SecondOrderSections, f), x)
243+
232244# Zero phase digital filtering with an FIR filter in a single pass
233245function fir_filtfilt (b:: AbstractVector , x:: AbstractArray )
234246 nb = length (b)
@@ -245,7 +257,10 @@ function fir_filtfilt(b::AbstractVector, x::AbstractArray)
245257
246258 # Extrapolate each column
247259 for i = 1 : size (extrapolated, 2 )
248- extrapolate_signal! (extrapolated, (i- 1 )* size (extrapolated, 1 )+ 1 , x, (i- 1 )* size (x, 1 )+ 1 , size (x, 1 ), nb- 1 )
260+ sig = sub (x, :, i)
261+ extrapolate_start! (sub (extrapolated, 1 : nb- 1 , i), sig)
262+ copy! (extrapolated, (i- 1 )* size (extrapolated, 1 )+ nb, x, (i- 1 )* size (x, 1 )+ 1 , size (x, 1 ))
263+ extrapolate_end! (sub (extrapolated, size (extrapolated, 1 )- nb+ 2 : size (extrapolated, 1 ), i), sig)
249264 end
250265
251266 # Filter
@@ -255,73 +270,45 @@ function fir_filtfilt(b::AbstractVector, x::AbstractArray)
255270 reshape (out[2 nb- 1 : end , :], size (x))
256271end
257272
258- # Choose whether to use fir_filtfilt or iir_filtfilt depending on
259- # length of a
260- function filtfilt (b:: AbstractVector , a:: AbstractVector , x:: AbstractArray )
273+ # Support for passing b and a
274+ function filtfilt (b:: AbstractVector , a:: AbstractVector , x)
261275 if length (a) == 1
262276 if a[1 ] != 1
263277 b /= a[1 ]
264278 end
265279 fir_filtfilt (b, x)
266280 else
267- iir_filtfilt (b, a, x )
268- end
269- end
281+ bs = length (b )
282+ as = length (a)
283+ sz = max (bs, as)
270284
271- # Zero phase digital filtering for second order sections
272- function filtfilt {T,G,S} (f:: SecondOrderSections{T,G} , x:: AbstractArray{S} )
273- zi = filt_stepstate (f)
274- zitmp = similar (zi)
275- pad_length = 6 * length (f. biquads)
276- t = Base. promote_type (T, G, S)
277- extrapolated = Array (t, size (x, 1 )+ pad_length* 2 )
278- out = similar (x, t)
285+ # Pad the coefficients with zeros if needed
286+ bs< sz && (b = copy! (zeros (eltype (b), sz), b))
287+ as< sz && (a = copy! (zeros (eltype (a), sz), a))
288+ sz > 0 || error (" a and b must have at least one element each" )
279289
280- istart = 1
281- for i = 1 : Base. trailingsize (x, 2 )
282- extrapolate_signal! (extrapolated, 1 , x, istart, size (x, 1 ), pad_length)
283- reverse! (filt! (extrapolated, f, extrapolated, scale! (zitmp, zi, extrapolated[1 ])))
284- filt! (extrapolated, f, extrapolated, scale! (zitmp, zi, extrapolated[1 ]))
285- for j = 1 : size (x, 1 )
286- @inbounds out[j, i] = extrapolated[end - pad_length+ 1 - j]
287- end
288- istart += size (x, 1 )
290+ filtfilt (PolynomialRatio (b, a), x)
289291 end
290-
291- out
292292end
293293
294- # Support for other filter types
295- filtfilt (f:: FilterCoefficients , x) = filtfilt (convert (SecondOrderSections, f), x)
296- filtfilt (f:: PolynomialRatio , x) = filtfilt (coefb (f), coefa (f), x)
297-
298294# # Initial filter state
299295
300296# Compute an initial state for filt with coefficients (b,a) such that its
301297# response to a step function is steady state.
302- function filt_stepstate {T<:Number} (b:: Union(AbstractVector{T}, T) , a:: Union(AbstractVector{T}, T) )
303- scale_factor = a[1 ]
304- if scale_factor != 1.0
305- a = a ./ scale_factor
306- b = b ./ scale_factor
307- end
308-
309- bs = length (b)
310- as = length (a)
311- sz = max (bs, as)
312- sz > 0 || error (" a and b must have at least one element each" )
313- sz == 1 && return T[]
314-
315- # Pad the coefficients with zeros if needed
316- bs< sz && (b = copy! (zeros (eltype (b), sz), b))
317- as< sz && (a = copy! (zeros (eltype (a), sz), a))
318-
319- # construct the companion matrix A and vector B:
298+ function filt_stepstate {T<:Number} (f:: PolynomialRatio{T} )
299+ b = coefb (f)
300+ a = coefa (f)
301+ sz = length (b)
302+ sz == length (a) || throw (ArgumentError (" a and b must have same length" ))
303+ a[1 ] == 1 || throw (ArgumentError (" first element of a must be 1" ))
304+ max (length (b), length (a)) == 1 && return T[]
305+
306+ # Construct the companion matrix A and vector B:
320307 A = [- a[2 : end ] [eye (T, sz- 2 ); zeros (T, 1 , sz- 2 )]]
321308 B = b[2 : end ] - a[2 : end ] * b[1 ]
322309 # Solve si = A*si + B
323310 # (I - A)*si = B
324- scale_factor \ (I - A) \ B
311+ (I - A) \ B
325312 end
326313
327314function filt_stepstate {T} (f:: SecondOrderSections{T} )
0 commit comments