lfilter
Builtin index · Signal processing
result = filter_ba(b, a, x)
Input: b, a, x · Output: result
What it is
b, a: vectors (numerator/denominator coefficients); x: a length-N vector or Signal.
Overloads
One call shape — this function is not overloaded.
| Code | Input | Output | Description |
result = filter_ba(b, a, x) | b vectorb, a: vectors (numerator/denominator coefficients).a vector or Signalx: a length-N vector or Signal.x vector or Signalx: a length-N vector or Signal. | result vector or Signal | Returns a same-length vector or Signal. Apply a transfer function given as raw numerator and denominator coefficients, direct form II transposed. For a function you derived yourself, or a script ported from MATLAB (filter(b, a, x)) or scipy (lfilter) — filter itself is the higher-order predicate filter here, so the name is taken. Warns above order 8, where a direct form loses accuracy and second-order sections do not. |
Examples
From the documentation, and run by the build -- the output and the figure below are what the engine actually produced. Simplest first.
fs = 1000
ep = ellip(4, 1, 40, "low", 100, fs)
c1 = cheby1(4, 1, "low", 100, fs)
c2 = cheby2(4, 40, "low", 100, fs)
print("ellip stable: {is_stable(ep)}, cheby1 stable: {is_stable(c1)}, cheby2 stable: {is_stable(c2)}")
fir = fir1(6, 100, kind="low", window="hamming", fs=fs)
b = fir.b
a = [1]
n = 0:1:255
t = n / fs
x = sin(2*pi*40*t) + sin(2*pi*300*t)
y = filter_ba(b, a, x)
y2 = lfilter(b, a, x) # lfilter is the same builtin under another name
print("filter_ba/lfilter agree: {max(abs(y - y2)) < 1e-12}")
lsq = firls(20, [0, 0.2, 0.3, 1.0], [1, 1, 0, 0]) # low-pass band edges/response
print("firls taps: {length(lsq.b)}")
gd = group_delay(fir, 256)
print("FIR group delay (should be ~order/2=3): {gd[10]}")
print("ellip filter has {length(poles(ep))} poles")
ep.fvtool() # 2x2 magnitude/phase/group-delay/impulse-response figure
pzplot(ep) # pole-zero plot with the unit circle and stability verdict
ellip stable: true, cheby1 stable: true, cheby2 stable: true filter_ba/lfilter agree: true firls taps: 21 FIR group delay (should be ~order/2=3): 3 ellip filter has 4 poles
from signal-processing
How it works
sosfilt(filt, x) — causal (one-pass) filtering; has the usual IIR phase lag. See filtfilt for zero-phase. Branches on the filter's representation: sos (IIR) uses the biquad cascade; b (FIR) is exactly conv(x, b, "full") truncated to len(x) (see numeric::filter::fir_filt's doc comment). Filtering never changes sample count or sample rate, so a Signal input keeps its Fs on the way out — the same "still the same time domain" convention diff/cumsum already follow (§41.2); a plain Vec input still returns a plain Vec, unchanged. filter_ba(b, a, x) — apply a transfer function given as raw numerator and denominator coefficients. Qu's filters are normally OBJECTS from butter/cheby1/ fir1, applied with sosfilt, and that is the better API: second-order sections are numerically well behaved where a high-order direct form is not. But it left no way to apply a transfer function you derived yourself, or ported from MATLAB or scipy, where filter(b, a, x) and lfilter(b, a, x) are the first thing anyone writes. lfilter is accepted as a second name for exactly that reason; filter itself is taken by the higher-order predicate filter. Direct form II transposed, the same recurrence scipy uses: y[n] = b0*x[n] + z0[n-1] z0[n] = b1*x[n] + z1[n-1] - a1*y[n] z1[n] = b2*x[n] - a2*y[n] It needs one state per coefficient rather than two histories, and it is the form that stays accurate when the coefficients span a wide dynamic range. A warning, not a guard. A direct-form filter above about order 8 loses accuracy fast: the polynomial coefficients of a high-order design differ by many orders of magnitude and the recurrence cannot represent the poles precisely. That is why butter returns sections. This says so rather than refusing, because a ported script is usually right about what it wants.
From the note above this builtin in the interpreter, where the reasoning was written down as it was built.
See also
filter_ba — documented in the same entry.