# What is the warm-up period of a Butterworth filter?

I am not sure if I use the correct term which may be the reason why I was not able to find the answer, but what I mean by the warm-up period is the number of samples required for the filter to produce valid values in the output. Visually I am guessing that it depends on the order and the cut-off frequency, and is roughly a division of the former by latter, but it does not seem to be a strict measure, because it holds better for higher cut-off frequency than lower, but I may be wrong as my judgement is based on the looks of the plots like in this toy example in Python:

import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import butter, lfilter

l = 256
x = np.arange(l)
#y = (1/8)*np.sin(8*x + np.pi/8) + (1/4)*np.sin(4*x + np.pi/4) + (1/2)*np.sin(2*x + np.pi/2)
#y = 8*np.sin(x/8 + np.pi/8) + 4*np.sin(x/4 + np.pi/4) + 2*np.sin(x/2 + np.pi/2)
y = (1/128)*np.sin(x/128 + np.pi/128) + (1/64)*np.sin(x/64 + np.pi/64) + (1/32)*np.sin(x/32 + np.pi/32) + 1/2
fig, axs = plt.subplots(2, sharex=True)
axs[0].plot(y, c='k')
axs[1].plot(y, c='k')
for order in (1, 2, 3):
b, a = butter(order, 1/8, 'lowpass')
axs[0].plot(lfilter(b, a, y))
b, a = butter(order, 1/64, 'lowpass')
axs[1].plot(lfilter(b, a, y))
fig.show()


which produces a figure:

where the black curve is the original signal y and the colored lines are its filtered equivalents: blue - 1st order, orange - 2nd, green - 3rd. After zooming in on the top plot one can see that the blue line picks up around x = 8, orange around x = 16 and green around x = 24, so one would expect to see on the bottom plot the blue line picking up around x = 64, orange around x = 128 and green around x = 196 which kind of looks like that for blue and orange, but not quite for the green one. Of course there is a lag in play that makes it even more difficult to judge, so my question is: how to calculate the number of samples needed for a Butterworth filter to produce valid results?

There is likely a closed form expression but what I do in the general case for any IIR filter including butterworth filters is to evaluate the unit sample response for the filter on a dB scale (see Python example below). The “unit sample response” is the discrete-time equivalent of the “impulse response”. I then decide how good is “good enough” based on a distortion requirement (such as -80 or -100 dB) to then determine from that the number of samples to meet a given accuracy. The accuracy of the result is the inverse of the distortion which we can get from: $$20Log(1+10^{-d/20})$$ where d is the distortion level in dB.

I commonly use this with any causal IIR filter to decide how many samples to truncate from the start of the output to avoid filter transients. The logic to doing this is very intuitive: from the impulse response we see how long the response from any particular slice from the input takes to decay to an insignificant value (based on our distortion criteria), which also is the time from when the first input and all previous inputs would be "valid" within our allowable error.

Below shows examples of this for the OP's butterworth 2nd and 3rd order filters and associated Python code:

import scipy.signal as sig
import matplotlib.pyplot as plt
plt.figure(figsize=(6,8))
plt.subplot(2,1,1)
imp = sig.unit_impulse(50)
for order in (2, 3):
b, a = sig.butter(order, 1/8, 'lowpass')
r = sig.lfilter(b,a,imp)
plt.plot(20*np.log10(np.abs(r)), label = f"order {order}")
plt.title("Unit Sample Response, $$\omega_n$$ = 1/8")
plt.grid()
plt.xlabel("Sample Number")
plt.ylabel("dB")
plt.legend()

plt.subplot(2,1,2)
imp = sig.unit_impulse(400)
for order in (2, 3):
b, a = sig.butter(order, 1/64, 'lowpass')
r = sig.lfilter(b,a,imp)
plt.plot(20*np.log10(np.abs(r)), label = f"order {order}")

plt.title("Unit Sample Response, $$\omega_n$$ = 1/64")
plt.grid()
plt.xlabel("Sample Number")
plt.ylabel("dB")
plt.legend()
plt.tight_layout()