Low-pass filtering bounces when signal doesn't start at 0

Say I have an array representing a signal, going from 30 to -30 in 1000 increments, with a little bit of noise on those values. If I apply any king of low pass filtering (Besset, Butterworth, Savitsky-Golay, etc), then the 1st point is now very near zero and the next few points reach progressively for +30 after some wild oscillations depending on the filter and its parameters.

It's been a while (ahem, decades) since I last delved into the maths of DSP, so I'm a bit rusty and looking to know what I should do about it. I thought that the solution was to window my data before filtering it, but it doesn't seem to change much.

Obviously what I want to end up with is a nice 30 to -30 ramp, with the noise removed.

• You can try padding the signal with extra $30$'s on the left and $-30$'s on the right and then trim the edges after filtering. – Atul Ingle Dec 5 '16 at 15:19
• Thanks. Any way to evaluate the size of the padding ? 2*FilterOrder seems OK for some filters, but not enough for others (namely Chebyshev and Elliptic). – dargaud Dec 5 '16 at 16:01
• The padding needs to be as long as the impulse response of the filter (the portion above the desired noise floor for an IIR filter). – hotpaw2 Dec 5 '16 at 16:37

The problem is due to the fact that initially, the filter memory is filled with zeros (if not otherwise stated). Since the filter output is a weighted average of the filter memory and the input, it will start its output near zero, when the initial state is set to zero.

As Atul suggested, you can overcome this by padding 30 to the beginning of the sequence (to fill the memory before you actually filter the sequence of interest). However, the amount of padding depends on the filter and how fast it adjusts to the steady state (depending on filter order and e.g. Q-factor)

As a more elegant solution, you can initialize the filter state such that its first sample already outputs 30. You can calculate the filter state as the steady state response as to a unit step input. E.g. python has this functionality already built-in:

signal = np.linspace(30, -30, 1000)
sigma = 1
signal = signal + sigma * np.random.randn(len(signal))

plt.plot(signal)
b, a = scipy.signal.butter(5, 0.1)
zi = scipy.signal.lfilter_zi(b, a)

filtered = scipy.signal.lfilter(b, a, signal, zi=30*zi)[0]
plt.plot(filtered, lw=2)