@@ -183,52 +183,113 @@ 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)
209+ function filtfilt {T} (coefs:: PolynomialRatio{T} , x:: AbstractArray )
210+ zi = filt_stepstate (coefb (coefs), coefa (coefs))
212211 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 )
212+ pad_length = 3 * (max (length (coefs . a), length (coefs . b)) - 1 )
213+ t = Base. promote_eltype (T , x)
214+ extrapolated = Array (t, pad_length)
216215 out = similar (x, t)
216+ f = DF2TFilter (coefs, zitmp)
217217
218- istart = 1
219218 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]
219+ # Forward pass
220+ sigout = sub (out, :, i)
221+ sig = sub (x, :, i)
222+ extrapolate_start! (extrapolated, sig)
223+ scale! (zitmp, zi, extrapolated[1 ])
224+ filt! (extrapolated, f, extrapolated)
225+ filt! (sigout, f, sig)
226+ extrapolate_end! (extrapolated, sig)
227+ filt! (extrapolated, f, extrapolated)
228+
229+ # Reverse pass
230+ sigoutrev = sub (out, size (x, 1 ): - 1 : 1 , i)
231+ reverse! (extrapolated)
232+ scale! (zitmp, zi, extrapolated[1 ])
233+ filt! (extrapolated, f, extrapolated)
234+ filt! (sigoutrev, f, sigoutrev)
235+ end
236+
237+ out
238+ end
239+
240+ # Extract si for a biquad, multiplied by a scaling factor
241+ function biquad_si! (zitmp, zi, i, scal)
242+ zitmp[1 ] = zi[1 , i]* scal
243+ zitmp[2 ] = zi[2 , i]* scal
244+ zitmp
245+ end
246+
247+ # Zero phase digital filtering for second order sections
248+ function filtfilt {T,G,S} (coefs:: SecondOrderSections{T,G} , x:: AbstractArray{S} )
249+ zi = filt_stepstate (coefs)
250+ zitmp = zeros (2 )
251+ pad_length = 3 * size (zi, 1 )
252+ t = Base. promote_type (T, G, S)
253+ extrapolated_start = Array (t, pad_length)
254+ extrapolated_end = Array (t, pad_length)
255+ out = similar (x, t)
256+
257+ f1 = DF2TFilter (coefs. biquads[1 ]* coefs. g, zitmp)
258+ f = Array (typeof (f1), length (coefs. biquads))
259+ f[1 ] = f1
260+ for i = 2 : length (coefs. biquads)
261+ f[i] = DF2TFilter (coefs. biquads[i], zitmp)
262+ end
263+
264+ istart = 1
265+ for i = 1 : size (x, 2 )
266+ sigout = sub (out, :, i)
267+ sig = sub (x, :, i)
268+ sigoutrev = sub (out, size (x, 1 ): - 1 : 1 , i)
269+
270+ for j = 1 : length (f)
271+ # Forward pass
272+ extrapolate_start! (extrapolated_start, j == 1 ? sig : sigout)
273+ extrapolate_end! (extrapolated_end, j == 1 ? sig : sigout)
274+ biquad_si! (zitmp, zi, j, extrapolated_start[1 ])
275+ filt! (extrapolated_start, f[j], extrapolated_start)
276+ filt! (sigout, f[j], j == 1 ? sig : sigout)
277+ filt! (extrapolated_end, f[j], extrapolated_end)
278+
279+ # Reverse pass
280+ reverse! (extrapolated_end)
281+ biquad_si! (zitmp, zi, j, extrapolated_end[1 ])
282+ filt! (extrapolated_end, f[j], extrapolated_end)
283+ filt! (sigoutrev, f[j], sigoutrev)
225284 end
226- istart += size (x, 1 )
227285 end
228286
229287 out
230288end
231289
290+ # Support for other filter types
291+ filtfilt (f:: FilterCoefficients , x) = filtfilt (convert (SecondOrderSections, f), x)
292+
232293# Zero phase digital filtering with an FIR filter in a single pass
233294function fir_filtfilt (b:: AbstractVector , x:: AbstractArray )
234295 nb = length (b)
@@ -245,7 +306,10 @@ function fir_filtfilt(b::AbstractVector, x::AbstractArray)
245306
246307 # Extrapolate each column
247308 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 )
309+ sig = sub (x, :, i)
310+ extrapolate_start! (sub (extrapolated, 1 : nb- 1 , i), sig)
311+ copy! (extrapolated, (i- 1 )* size (extrapolated, 1 )+ nb, x, (i- 1 )* size (x, 1 )+ 1 , size (x, 1 ))
312+ extrapolate_end! (sub (extrapolated, size (extrapolated, 1 )- nb+ 2 : size (extrapolated, 1 ), i), sig)
249313 end
250314
251315 # Filter
@@ -255,91 +319,43 @@ function fir_filtfilt(b::AbstractVector, x::AbstractArray)
255319 reshape (out[2 nb- 1 : end , :], size (x))
256320end
257321
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 )
322+ # Support for passing b and a
323+ function filtfilt (b:: AbstractVector , a:: AbstractVector , x)
261324 if length (a) == 1
262325 if a[1 ] != 1
263326 b /= a[1 ]
264327 end
265328 fir_filtfilt (b, x)
266329 else
267- iir_filtfilt (b, a, x )
268- end
269- end
330+ bs = length (b )
331+ as = length (a)
332+ sz = max (bs, as)
270333
271- # Extract si for a biquad, multiplied by a scaling factor
272- function biquad_si! (zitmp, zi, i, scal)
273- zitmp[1 ] = zi[1 , i]* scal
274- zitmp[2 ] = zi[2 , i]* scal
275- zitmp
276- end
334+ # Pad the coefficients with zeros if needed
335+ bs< sz && (b = copy! (zeros (eltype (b), sz), b))
336+ as< sz && (a = copy! (zeros (eltype (a), sz), a))
337+ sz > 0 || error (" a and b must have at least one element each" )
277338
278- # Zero phase digital filtering for second order sections
279- function filtfilt {T,G,S} (f:: SecondOrderSections{T,G} , x:: AbstractArray{S} )
280- zi = filt_stepstate (f)
281- zi2 = zeros (2 )
282- zitmp = zeros (2 )
283- pad_length = 3 * size (zi, 1 )
284- t = Base. promote_type (T, G, S)
285- extrapolated = Array (t, size (x, 1 )+ pad_length* 2 )
286- out = similar (x, t)
287-
288- istart = 1
289- for i = 1 : size (x, 2 )
290- # First biquad
291- extrapolate_signal! (extrapolated, 1 , x, istart, size (x, 1 ), pad_length)
292- f2 = f. biquads[1 ]* f. g
293- reverse! (filt! (extrapolated, f2, extrapolated, biquad_si! (zitmp, zi, 1 , extrapolated[1 ])))
294- reverse! (filt! (extrapolated, f2, extrapolated, biquad_si! (zitmp, zi, 1 , extrapolated[1 ])))
295-
296- # Subsequent biquads
297- for j = 2 : length (f. biquads)
298- f2 = f. biquads[j]
299- extrapolate_signal! (extrapolated, 1 , extrapolated, pad_length+ 1 , size (x, 1 ), pad_length)
300- reverse! (filt! (extrapolated, f2, extrapolated, biquad_si! (zitmp, zi, j, extrapolated[1 ])))
301- reverse! (filt! (extrapolated, f2, extrapolated, biquad_si! (zitmp, zi, j, extrapolated[1 ])))
302- end
303-
304- # Copy to output
305- copy! (out, istart, extrapolated, pad_length+ 1 , size (x, 1 ))
306- istart += size (x, 1 )
339+ filtfilt (PolynomialRatio (b, a), x)
307340 end
308-
309- out
310341end
311342
312- # Support for other filter types
313- filtfilt (f:: FilterCoefficients , x) = filtfilt (convert (SecondOrderSections, f), x)
314- filtfilt (f:: PolynomialRatio , x) = filtfilt (coefb (f), coefa (f), x)
315-
316343# # Initial filter state
317344
318345# Compute an initial state for filt with coefficients (b,a) such that its
319346# response to a step function is steady state.
320347function filt_stepstate {T<:Number} (b:: Union(AbstractVector{T}, T) , a:: Union(AbstractVector{T}, T) )
321- scale_factor = a[1 ]
322- if scale_factor != 1.0
323- a = a ./ scale_factor
324- b = b ./ scale_factor
325- end
326-
327- bs = length (b)
328- as = length (a)
329- sz = max (bs, as)
330- sz > 0 || error (" a and b must have at least one element each" )
331- sz == 1 && return T[]
332-
333- # Pad the coefficients with zeros if needed
334- bs< sz && (b = copy! (zeros (eltype (b), sz), b))
335- as< sz && (a = copy! (zeros (eltype (a), sz), a))
348+ sz = length (b)
349+ sz == length (a) || throw (ArgumentError (" a and b must have same length" ))
350+ a[1 ] == 1 || throw (ArgumentError (" first element of a must be 1" ))
351+ max (length (b), length (a)) == 1 && return T[]
336352
337- # construct the companion matrix A and vector B:
353+ # Construct the companion matrix A and vector B:
338354 A = [- a[2 : end ] [eye (T, sz- 2 ); zeros (T, 1 , sz- 2 )]]
339355 B = b[2 : end ] - a[2 : end ] * b[1 ]
340356 # Solve si = A*si + B
341357 # (I - A)*si = B
342- scale_factor \ (I - A) \ B
358+ (I - A) \ B
343359 end
344360
345361function filt_stepstate {T} (f:: SecondOrderSections{T} )
0 commit comments