IIR νν° μ€κ³
IIR νν° μ€κ³¶
νμ΅ λͺ©ν¶
- κ³ μ μ μΈ μλ λ‘κ·Έ μν νν°(λ²ν°μμ€(Butterworth), 체λΉμΌν(Chebyshev), νμν(Elliptic))μ νΉμ± μ΄ν΄
- μλ λ‘κ·Έ-λμ§νΈ λ³νμ μν μμΌμ°¨ λ³ν(bilinear transform)κ³Ό μνμ€ λΆλ³ λ°©λ²(impulse invariance) λ§μ€ν°
- μ£Όνμ μμ κ·κ²©μΌλ‘λΆν° νν° μ°¨μ κ²°μ λ°©λ² νμ΅
- Pythonμ
scipy.signalλͺ¨λμ μ¬μ©ν IIR λμ§νΈ νν° μ€κ³ - κ·Ή-μμ λΆμ(pole-zero analysis)μ ν΅ν νν° μμ μ± κ²μ¦
- νν° μ ν λΉκ΅ λ° νΉμ μμ©μ μ ν©ν μ€κ³ μ ν
λͺ©μ°¨¶
- IIR νν° μ€κ³ μκ°
- μλ λ‘κ·Έ μν νν°
- λ²ν°μμ€ νν°
- 체λΉμΌν Type I νν°
- 체λΉμΌν Type II νν°
- νμν(μ½μ΄) νν°
- μλ λ‘κ·Έ-λμ§νΈ λ³ν
- μμΌμ°¨ λ³ν
- μνμ€ λΆλ³ λ°©λ²
- μμ ν IIR μ€κ³ μ μ°¨
- μμ μ± λΆμ
- νν° μ ν λΉκ΅
- Python ꡬν
- μ°μ΅ λ¬Έμ
1. IIR νν° μ€κ³ μκ°¶
1.1 IIR νν° κ΅¬μ‘°¶
IIR(무ν μνμ€ μλ΅(Infinite Impulse Response)) νν°λ μλ°©ν₯(feedforward)κ³Ό νΌλλ°±(feedback) κ²½λ‘λ₯Ό λͺ¨λ κ°μ΅λλ€. μ λ¬ ν¨μλ λ€νμμ λΉμ¨λ‘ λνλ λλ€:
$$H(z) = \frac{B(z)}{A(z)} = \frac{\sum_{k=0}^{M} b_k z^{-k}}{1 + \sum_{k=1}^{N} a_k z^{-k}}$$
μ°¨λΆ λ°©μ μ(difference equation)μ λ€μκ³Ό κ°μ΅λλ€:
$$y[n] = \sum_{k=0}^{M} b_k x[n-k] - \sum_{k=1}^{N} a_k y[n-k]$$
1.2 μ€κ³ μ κ·Όλ²¶
FIR νν°(μ΄μ° μμμμ μ§μ μ€κ³)μ λ¬λ¦¬, IIR νν°λ μΌλ°μ μΌλ‘ λ€μκ³Ό κ°μ΄ μ€κ³ν©λλ€:
- μ μλ €μ§ νΉμ±μ κ°λ μλ λ‘κ·Έ μν $H_a(s)$μμ μμ
- μλ λ‘κ·Έ νν°λ₯Ό λμ§νΈ νν° $H(z)$λ‘ λ³ν
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β IIR Filter Design Pipeline β
β β
β Digital Specs Analog Specs Analog Filter Digital β
β ββββββββββββ ββββββββββββ ββββββββββββ ββββββββββ β
β β Οp, Οs β βββΆ β Ξ©p, Ξ©s β βββΆ β Ha(s) β βββΆβ H(z) β β
β β Ξ΄β, Ξ΄β β β Ξ΄β, Ξ΄β β β β β β β
β ββββββββββββ ββββββββββββ ββββββββββββ ββββββββββ β
β β
β Step 1: Step 2: Step 3: Step 4: β
β Specify digital Pre-warp to Design analog Apply β
β requirements analog specs prototype BLT/IIM β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
1.3 μλ λ‘κ·Έ μνμμ μμνλ μ΄μ ¶
- μμ λ μ μ΄λ‘ : λ²ν°μμ€(Butterworth), 체λΉμΌν(Chebyshev), νμν νν° μ€κ³λ μ μ 립λμ΄ μμ
- λ«ν ννμ ν΄: κ·Ήμ (pole)/μμ (zero) μμΉλ₯Ό ν΄μμ μΌλ‘ κ³μ° κ°λ₯
- μ΅μ νΉμ±: κ° μ νμ νΉμ μλ―Έμμ μ΅μ (ννλ, λ±λ¦¬ν, μ΅μ μ°¨μ)
- μ£Όνμ λ³ν: μ μ ν΅κ³Ό(lowpass) μνμ κ³ μ ν΅κ³Ό(highpass), λμ ν΅κ³Ό(bandpass), λμ μ μ§(bandstop)λ‘ λ³ν κ°λ₯
2. μλ λ‘κ·Έ μν νν°¶
2.1 μλ λ‘κ·Έ νν° κ·κ²©¶
μλ λ‘κ·Έ μ μ ν΅κ³Ό νν° κ·κ²©μ λ€μμΌλ‘ ꡬμ±λ©λλ€:
- ν΅κ³Όλμ μ£μ§ μ£Όνμ(passband edge frequency) $\Omega_p$
- μ μ§λμ μ£μ§ μ£Όνμ(stopband edge frequency) $\Omega_s$
- ν΅κ³Όλμ 리ν(passband ripple) $R_p$ (dB) λλ νμ©μ€μ°¨ $\epsilon$
- μ μ§λμ κ°μ (stopband attenuation) $A_s$ (dB)
리ν νλΌλ―Έν° $\epsilon$κ³Ό ν΅κ³Όλμ 리νμ κ΄κ³:
$$R_p = 10\log_{10}(1 + \epsilon^2) \quad \text{dB}$$
2.2 μ κ·νλ μ μ ν΅κ³Ό μν¶
λͺ¨λ κ³ μ μ μ€κ³λ ν΅κ³Όλμ μ£μ§κ° $\Omega_p = 1$ rad/sμΈ μ κ·νλ μ μ ν΅κ³Ό μν(normalized lowpass prototype)μμ μμν©λλ€. μ΄ν μνμ μ£Όνμ μ€μΌμΌλ§νκ³ μνλ νν° μ νμΌλ‘ λ³νν©λλ€.
μ νλ μΈμ(selectivity factor):
$$k = \frac{\Omega_p}{\Omega_s} < 1$$
$k$κ° μμμλ‘ μ μ΄λμ(transition band)μ΄ ν΅κ³Όλμμ λΉν΄ λμ΄μ Έ μ€κ³κ° μ¬μμ§λλ€.
3. λ²ν°μμ€ νν°¶
3.1 ν¬κΈ° μλ΅¶
λ²ν°μμ€(Butterworth) νν°λ ν΅κ³Όλμμμ μ΅λ νν ν¬κΈ° μλ΅(maximally flat magnitude response)μ μ 곡ν©λλ€. μ κ³± ν¬κΈ° μλ΅μ:
$$|H_a(j\Omega)|^2 = \frac{1}{1 + \left(\Omega / \Omega_c\right)^{2N}}$$
μ¬κΈ°μ $N$μ νν° μ°¨μ, $\Omega_c$λ $-3$ dB μ°¨λ¨ μ£Όνμμ λλ€.
νΉμ±: - $|H_a(j\Omega)|^2$μ $2N-1$μ°¨κΉμ§ λͺ¨λ λ―ΈλΆκ°μ΄ $\Omega = 0$μμ 0 - λ¨μ‘° κ°μ(monotonically decreasing) ν¬κΈ° - μ°¨λ¨ μ£Όνμμμ $|H_a(j\Omega_c)|^2 = 1/2$ ($-3$ dB) - λ‘€μ€ν(rolloff) μλ: μ μ§λμμμ $-20N$ dB/decade
3.2 κ·Ήμ μμΉ¶
λ²ν°μμ€ νν°μ κ·Ήμ μ $s$-νλ©΄μμ λ°μ§λ¦ $\Omega_c$μΈ μ μμ λ±κ°κ²©μΌλ‘ μμΉν©λλ€:
$$s_k = \Omega_c \exp\left[j\frac{\pi}{2N}(2k + N - 1)\right], \quad k = 0, 1, \ldots, 2N-1$$
μμ μ (μΈκ³Όμ ) νν°λ₯Ό μν΄ μ’λ°νλ©΄(left-half-plane) κ·Ήμ ($\text{Re}(s_k) < 0$)λ§ μ νν©λλ€.
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
def butterworth_poles(N, Omega_c=1.0):
"""Compute Butterworth filter poles in the s-plane."""
poles = []
for k in range(2 * N):
s_k = Omega_c * np.exp(1j * np.pi * (2 * k + N - 1) / (2 * N))
if np.real(s_k) < 0: # Left-half plane only
poles.append(s_k)
return np.array(poles)
# Visualize pole locations for different orders
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
for ax, N in zip(axes, [2, 4, 8]):
poles = butterworth_poles(N)
theta = np.linspace(0, 2 * np.pi, 200)
# Unit circle
ax.plot(np.cos(theta), np.sin(theta), 'k--', alpha=0.3)
# Poles
ax.plot(np.real(poles), np.imag(poles), 'rx', markersize=12, markeredgewidth=2)
ax.axhline(0, color='k', linewidth=0.5)
ax.axvline(0, color='k', linewidth=0.5)
ax.set_xlabel('Real')
ax.set_ylabel('Imaginary')
ax.set_title(f'Butterworth N={N} Poles')
ax.set_aspect('equal')
ax.grid(True, alpha=0.3)
ax.set_xlim(-1.5, 0.5)
ax.set_ylim(-1.5, 1.5)
plt.tight_layout()
plt.savefig('butterworth_poles.png', dpi=150)
plt.close()
3.3 μ°¨μ κ²°μ ¶
κ·κ²© $(\Omega_p, \Omega_s, R_p, A_s)$κ° μ£Όμ΄μ§λ©΄ μ΅μ λ²ν°μμ€ μ°¨μλ:
$$N \geq \frac{\log\left(\frac{10^{A_s/10} - 1}{10^{R_p/10} - 1}\right)}{2\log(\Omega_s / \Omega_p)}$$
def butterworth_order(Omega_p, Omega_s, Rp_dB, As_dB):
"""Compute minimum Butterworth filter order."""
numerator = np.log10((10**(As_dB / 10) - 1) / (10**(Rp_dB / 10) - 1))
denominator = 2 * np.log10(Omega_s / Omega_p)
N = int(np.ceil(numerator / denominator))
return N
# Example
N = butterworth_order(1.0, 2.0, 1.0, 40)
print(f"Minimum Butterworth order: N = {N}")
# Compare with scipy
N_scipy, Wn = signal.buttord(1.0, 2.0, 1.0, 40, analog=True)
print(f"scipy.signal.buttord: N = {N_scipy}, Wn = {Wn:.4f}")
3.4 ν¬κΈ° μλ΅ μκ°ν¶
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# Different orders
orders = [1, 2, 4, 8, 16]
for N in orders:
b, a = signal.butter(N, 1.0, analog=True)
w, H = signal.freqs(b, a, worN=np.logspace(-1, 1, 1000))
H_dB = 20 * np.log10(np.abs(H))
axes[0].plot(w, H_dB, linewidth=1.5, label=f'N={N}')
axes[1].plot(w, np.abs(H), linewidth=1.5, label=f'N={N}')
axes[0].set_xlabel('Frequency (rad/s)')
axes[0].set_ylabel('Magnitude (dB)')
axes[0].set_title('Butterworth: Magnitude (dB)')
axes[0].set_xlim(0.1, 10)
axes[0].set_ylim(-80, 5)
axes[0].axhline(-3, color='gray', linestyle=':', alpha=0.5, label='-3 dB')
axes[0].axvline(1.0, color='gray', linestyle=':', alpha=0.5)
axes[0].set_xscale('log')
axes[0].legend()
axes[0].grid(True, which='both', alpha=0.3)
axes[1].set_xlabel('Frequency (rad/s)')
axes[1].set_ylabel('|H(jΞ©)|')
axes[1].set_title('Butterworth: Magnitude (Linear)')
axes[1].set_xlim(0, 3)
axes[1].axhline(1/np.sqrt(2), color='gray', linestyle=':', alpha=0.5, label='-3 dB')
axes[1].legend()
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('butterworth_responses.png', dpi=150)
plt.close()
4. 체λΉμΌν Type I νν°¶
4.1 ν¬κΈ° μλ΅¶
체λΉμΌν(Chebyshev) Type I νν°λ λ±λ¦¬ν ν΅κ³Όλμ(equiripple passband)κ³Ό λ¨μ‘° μ μ§λμμ κ°μ΅λλ€:
$$|H_a(j\Omega)|^2 = \frac{1}{1 + \epsilon^2 T_N^2(\Omega / \Omega_p)}$$
μ¬κΈ°μ $T_N(\cdot)$λ $N$μ°¨ μ 1μ’ μ²΄λΉμΌν λ€νμ(Chebyshev polynomial of the first kind)μ λλ€:
$$T_N(x) = \begin{cases} \cos(N \cos^{-1}(x)), & |x| \leq 1 \\ \cosh(N \cosh^{-1}(x)), & |x| > 1 \end{cases}$$
νΉμ±: - 리ν $\epsilon$μ λ±λ¦¬ν ν΅κ³Όλμ - ν΅κ³Όλμ 리ν: $R_p = 10\log_{10}(1 + \epsilon^2)$ dB - λμΌ μ°¨μμμ λ²ν°μμ€λ³΄λ€ κΈκ²©ν μ μ΄ - λ¨μ‘° κ°μ μ μ§λμ
4.2 체λΉμΌν λ€νμ¶
μ²μ λͺ κ°μ 체λΉμΌν λ€νμ:
$$T_0(x) = 1, \quad T_1(x) = x, \quad T_2(x) = 2x^2 - 1$$
$$T_3(x) = 4x^3 - 3x, \quad T_4(x) = 8x^4 - 8x^2 + 1$$
μ νμ(recurrence relation): $T_{N+1}(x) = 2x \cdot T_N(x) - T_{N-1}(x)$
4.3 μ°¨μ κ²°μ ¶
$$N \geq \frac{\cosh^{-1}\left(\sqrt{\frac{10^{A_s/10} - 1}{10^{R_p/10} - 1}}\right)}{\cosh^{-1}(\Omega_s / \Omega_p)}$$
4.4 κ·Ήμ μμΉ¶
체λΉμΌν Type I νν°μ κ·Ήμ μ $s$-νλ©΄μμ νμ μμ μμΉν©λλ€:
$$s_k = \sigma_k + j\omega_k$$
μ¬κΈ°μ:
$$\sigma_k = -\sinh\left(\frac{1}{N}\sinh^{-1}\left(\frac{1}{\epsilon}\right)\right) \sin\left(\frac{(2k-1)\pi}{2N}\right)$$
$$\omega_k = \cosh\left(\frac{1}{N}\sinh^{-1}\left(\frac{1}{\epsilon}\right)\right) \cos\left(\frac{(2k-1)\pi}{2N}\right)$$
$k = 1, 2, \ldots, N$μ λν΄.
def chebyshev1_analysis(N, Rp_dB=1.0):
"""Analyze Chebyshev Type I filter."""
epsilon = np.sqrt(10**(Rp_dB / 10) - 1)
# Pole locations
poles = []
for k in range(1, N + 1):
theta_k = (2 * k - 1) * np.pi / (2 * N)
sigma_k = -np.sinh(np.arcsinh(1 / epsilon) / N) * np.sin(theta_k)
omega_k = np.cosh(np.arcsinh(1 / epsilon) / N) * np.cos(theta_k)
poles.append(sigma_k + 1j * omega_k)
return np.array(poles), epsilon
# Compare Butterworth and Chebyshev
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
for N in [2, 4, 6, 8]:
# Chebyshev Type I (1 dB ripple)
b, a = signal.cheby1(N, 1.0, 1.0, analog=True)
w, H = signal.freqs(b, a, worN=np.logspace(-1, 1, 1000))
axes[0].plot(w, 20 * np.log10(np.abs(H)), linewidth=1.5, label=f'N={N}')
axes[0].set_xlabel('Frequency (rad/s)')
axes[0].set_ylabel('Magnitude (dB)')
axes[0].set_title('Chebyshev Type I (Rp = 1 dB)')
axes[0].set_xlim(0.1, 10)
axes[0].set_ylim(-80, 5)
axes[0].set_xscale('log')
axes[0].legend()
axes[0].grid(True, which='both', alpha=0.3)
# Pole-zero plot for N=4
poles_butter = butterworth_poles(4)
poles_cheby, _ = chebyshev1_analysis(4, 1.0)
theta = np.linspace(0, 2 * np.pi, 200)
axes[1].plot(np.cos(theta), np.sin(theta), 'k--', alpha=0.3, label='Unit circle')
axes[1].plot(np.real(poles_butter), np.imag(poles_butter), 'bx',
markersize=12, markeredgewidth=2, label='Butterworth')
axes[1].plot(np.real(poles_cheby), np.imag(poles_cheby), 'r+',
markersize=12, markeredgewidth=2, label='Chebyshev I')
axes[1].set_xlabel('Real')
axes[1].set_ylabel('Imaginary')
axes[1].set_title('Pole Locations: Butterworth vs Chebyshev I (N=4)')
axes[1].set_aspect('equal')
axes[1].legend()
axes[1].grid(True, alpha=0.3)
axes[1].set_xlim(-1.5, 0.5)
axes[1].set_ylim(-1.5, 1.5)
plt.tight_layout()
plt.savefig('chebyshev1_analysis.png', dpi=150)
plt.close()
5. 체λΉμΌν Type II νν°¶
5.1 ν¬κΈ° μλ΅¶
체λΉμΌν Type II(μ 체λΉμΌν(inverse Chebyshev)) νν°λ ννν ν΅κ³Όλμκ³Ό λ±λ¦¬ν μ μ§λμμ κ°μ΅λλ€:
$$|H_a(j\Omega)|^2 = \frac{1}{1 + \frac{1}{\epsilon^2 T_N^2(\Omega_s / \Omega)}}$$
νΉμ±: - λ¨μ‘° κ°μ(νν) ν΅κ³Όλμ - μ μ§λμμμ λ±λ¦¬ν λμ - κ·Ήμ κ³Ό μμ λͺ¨λ μ‘΄μ¬ (Type Iμ κ·Ήμ λ§) - μμ μ΄ $j\Omega$ μΆμ μμΉνμ¬ μ μ§λμμ λ ΈμΉ(null) μμ±
5.2 Type Iκ³Όμ λΉκ΅¶
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
N = 5
# Chebyshev Type I (1 dB ripple)
b1, a1 = signal.cheby1(N, 1.0, 1.0, analog=True)
w1, H1 = signal.freqs(b1, a1, worN=np.logspace(-1, 1, 2000))
# Chebyshev Type II (40 dB stopband attenuation)
b2, a2 = signal.cheby2(N, 40, 1.0, analog=True)
w2, H2 = signal.freqs(b2, a2, worN=np.logspace(-1, 1, 2000))
# Butterworth (same order)
b_bw, a_bw = signal.butter(N, 1.0, analog=True)
w_bw, H_bw = signal.freqs(b_bw, a_bw, worN=np.logspace(-1, 1, 2000))
# dB plot
for w, H, name, style in [(w_bw, H_bw, 'Butterworth', 'b-'),
(w1, H1, 'Chebyshev I', 'r-'),
(w2, H2, 'Chebyshev II', 'g-')]:
axes[0].plot(w, 20 * np.log10(np.abs(H) + 1e-15), style,
linewidth=1.5, label=name)
axes[0].set_xlabel('Frequency (rad/s)')
axes[0].set_ylabel('Magnitude (dB)')
axes[0].set_title(f'Filter Comparison (N={N})')
axes[0].set_xlim(0.1, 10)
axes[0].set_ylim(-80, 5)
axes[0].set_xscale('log')
axes[0].legend()
axes[0].grid(True, which='both', alpha=0.3)
# Passband detail (linear scale)
for w, H, name, style in [(w_bw, H_bw, 'Butterworth', 'b-'),
(w1, H1, 'Chebyshev I', 'r-'),
(w2, H2, 'Chebyshev II', 'g-')]:
axes[1].plot(w, np.abs(H), style, linewidth=1.5, label=name)
axes[1].set_xlabel('Frequency (rad/s)')
axes[1].set_ylabel('|H(jΞ©)|')
axes[1].set_title('Passband Detail')
axes[1].set_xlim(0, 2)
axes[1].set_ylim(0, 1.2)
axes[1].legend()
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('chebyshev2_comparison.png', dpi=150)
plt.close()
6. νμν(μ½μ΄) νν°¶
6.1 ν¬κΈ° μλ΅¶
νμν(Elliptic, Cauer) νν°λ ν΅κ³Όλμκ³Ό μ μ§λμ λͺ¨λμ 리νμ λΆμ°μμΌ μ£Όμ΄μ§ κ·κ²©μ λν΄ μ΅μ μ°¨μλ₯Ό λ¬μ±ν©λλ€:
$$|H_a(j\Omega)|^2 = \frac{1}{1 + \epsilon^2 R_N^2(\Omega / \Omega_p)}$$
μ¬κΈ°μ $R_N(\cdot)$μ μ 리 체λΉμΌν(μΌμ½λΉ νμ(Jacobian elliptic)) ν¨μμ λλ€.
νΉμ±: - ν΅κ³Όλμκ³Ό μ μ§λμ λͺ¨λ λ±λ¦¬ν - μ£Όμ΄μ§ μ°¨μμ λν΄ κ°μ₯ κΈκ²©ν μ μ΄ - μ°¨μ λ κ·κ²© λ©΄μμ κ°μ₯ ν¨μ¨μ - κ·Ήμ κ³Ό μμ λͺ¨λ μ‘΄μ¬
6.2 μ°¨μ μ°μ¶
λμΌν κ·κ²©μ λν΄ νμν νν° μ°¨μλ:
$$N_\text{Elliptic} \leq N_\text{Chebyshev} \leq N_\text{Butterworth}$$
def compare_filter_orders(Rp_dB, As_dB, wp, ws):
"""Compare required orders for different filter types."""
# Butterworth
N_butter, Wn_butter = signal.buttord(wp, ws, Rp_dB, As_dB, analog=True)
# Chebyshev Type I
N_cheby1, Wn_cheby1 = signal.cheb1ord(wp, ws, Rp_dB, As_dB, analog=True)
# Chebyshev Type II
N_cheby2, Wn_cheby2 = signal.cheb2ord(wp, ws, Rp_dB, As_dB, analog=True)
# Elliptic
N_ellip, Wn_ellip = signal.ellipord(wp, ws, Rp_dB, As_dB, analog=True)
print(f"Specifications: Rp={Rp_dB} dB, As={As_dB} dB, wp={wp}, ws={ws}")
print(f"{'Filter Type':<20} {'Order N':<10} {'Natural Freq':>12}")
print("-" * 45)
print(f"{'Butterworth':<20} {N_butter:<10} {Wn_butter:>12.4f}")
print(f"{'Chebyshev I':<20} {N_cheby1:<10} {Wn_cheby1:>12.4f}")
print(f"{'Chebyshev II':<20} {N_cheby2:<10} {Wn_cheby2:>12.4f}")
print(f"{'Elliptic':<20} {N_ellip:<10} {Wn_ellip:>12.4f}")
return N_butter, N_cheby1, N_cheby2, N_ellip
orders = compare_filter_orders(1.0, 60, 1.0, 1.5)
6.3 νμν νν° μκ°ν¶
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# Same specifications, different filter types
Rp = 1.0 # dB
As = 60 # dB
wp = 2 * np.pi * 1000 # rad/s
ws = 2 * np.pi * 1300 # rad/s
filters = [
('Butterworth', signal.buttord, signal.butter),
('Chebyshev I', signal.cheb1ord, signal.cheby1),
('Chebyshev II', signal.cheb2ord, signal.cheby2),
('Elliptic', signal.ellipord, signal.ellip),
]
for ax, (name, ord_func, design_func) in zip(axes.flat, filters):
N, Wn = ord_func(wp, ws, Rp, As, analog=True)
if 'Chebyshev I' in name:
b, a = design_func(N, Rp, Wn, analog=True)
elif 'Chebyshev II' in name:
b, a = design_func(N, As, Wn, analog=True)
elif 'Elliptic' in name:
b, a = design_func(N, Rp, As, Wn, analog=True)
else:
b, a = design_func(N, Wn, analog=True)
w, H = signal.freqs(b, a, worN=np.linspace(0, 3 * ws, 5000))
H_dB = 20 * np.log10(np.abs(H) + 1e-15)
ax.plot(w / (2 * np.pi), H_dB, 'b-', linewidth=1.5)
ax.axhline(-Rp, color='g', linestyle='--', alpha=0.5, label=f'-{Rp} dB')
ax.axhline(-As, color='r', linestyle='--', alpha=0.5, label=f'-{As} dB')
ax.axvline(wp / (2 * np.pi), color='g', linestyle=':', alpha=0.5)
ax.axvline(ws / (2 * np.pi), color='r', linestyle=':', alpha=0.5)
ax.set_xlabel('Frequency (Hz)')
ax.set_ylabel('Magnitude (dB)')
ax.set_title(f'{name} (N={N})')
ax.set_ylim(-80, 5)
ax.legend(fontsize=8)
ax.grid(True, alpha=0.3)
plt.suptitle(f'Filter Type Comparison (Rp={Rp} dB, As={As} dB)', fontsize=13)
plt.tight_layout()
plt.savefig('filter_type_comparison.png', dpi=150)
plt.close()
7. μλ λ‘κ·Έ-λμ§νΈ λ³ν¶
7.1 λ°©λ² κ°μ¶
μλ λ‘κ·Έ νν° $H_a(s)$λ₯Ό λμ§νΈ νν° $H(z)$λ‘ λ³ννλ μΈ κ°μ§ μ£Όμ λ°©λ²:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Analog-to-Digital Conversion Methods β
ββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββββ€
β Method β Mapping s β z β
ββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββ€
β Bilinear β s = (2/T)(z-1)/(z+1) β
β Transform β - No aliasing β
β β - Frequency warping (correctable) β
β β - Most widely used β
ββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββ€
β Impulse β h[n] = T Β· h_a(nT) β
β Invariance β - Preserves impulse response shape β
β β - Aliasing in frequency domain β
β β - Only for bandlimited filters (LP, BP) β
ββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββ€
β Matched β Map poles: s_k β z_k = e^(s_k T) β
β Z-Transform β Map zeros: same mapping β
β β - Simple but no formal optimality β
ββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββββββββββ
8. μμΌμ°¨ λ³ν¶
8.1 λ§€ν¶
μμΌμ°¨ λ³ν(bilinear transform, BLT)μ $s$-νλ©΄ μ 체λ₯Ό $z$-νλ©΄μΌλ‘ λ€μκ³Ό κ°μ΄ λ§€νν©λλ€:
$$s = \frac{2}{T} \cdot \frac{z - 1}{z + 1}$$
λλ λλ±νκ²:
$$z = \frac{1 + (T/2)s}{1 - (T/2)s}$$
μ¬κΈ°μ $T$λ μνλ§ μ£ΌκΈ°μ λλ€.
8.2 μ£Όμ νΉμ±¶
μμ μ± λ³΄μ‘΄: $s$-νλ©΄μ μ’λ°νλ©΄μ΄ $z$-νλ©΄μ λ¨μμ λ΄λΆλ‘ λ§€νλ©λλ€. μ΄λ₯Ό ν΅ν΄ μμ μ μΈ μλ λ‘κ·Έ νν°λ νμ μμ μ μΈ λμ§νΈ νν°λ₯Ό μμ±ν©λλ€.
μ£Όνμ λ§€ν: νμμΆ($s = j\Omega$)μ λΉμ ν μ£Όνμ μν(frequency warping)μΌλ‘ λ¨μμ($z = e^{j\omega}$)μΌλ‘ λ§€νλ©λλ€:
$$\omega = 2\arctan\left(\frac{\Omega T}{2}\right) \quad \Leftrightarrow \quad \Omega = \frac{2}{T}\tan\left(\frac{\omega}{2}\right)$$
8.3 μ£Όνμ μν¶
Bilinear Transform Frequency Warping:
Ξ© (analog) Ο (digital)
β β
β β± Οβββββββββββββββββββ±
β β± β β±
β β± β β±
β β± β β± Nonlinear
ββ± β β± compression
0 βββββ Ο 0 βββββ±βββββββββββ Ξ©
0 Ο 0
- Low frequencies: Ξ© β Ο (nearly linear)
- High frequencies: compressed into [0, Ο]
- Ξ© = β maps to Ο = Ο
# Visualize frequency warping
T = 1.0 # Sampling period
omega = np.linspace(0, np.pi - 0.01, 1000)
Omega = (2 / T) * np.tan(omega / 2)
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
axes[0].plot(Omega, omega / np.pi, 'b-', linewidth=2, label='BLT mapping')
axes[0].plot(Omega, Omega * T / np.pi, 'r--', linewidth=1.5, label='Linear (ideal)')
axes[0].set_xlabel('Analog Frequency Ξ© (rad/s)')
axes[0].set_ylabel('Digital Frequency Ο/Ο')
axes[0].set_title('Bilinear Transform: Frequency Warping')
axes[0].set_xlim(0, 20)
axes[0].set_ylim(0, 1.1)
axes[0].legend()
axes[0].grid(True, alpha=0.3)
# Warping effect at different frequencies
freq_analog = np.array([0.1, 0.5, 1.0, 2.0, 5.0, 10.0])
freq_digital = 2 * np.arctan(freq_analog * T / 2)
axes[1].bar(range(len(freq_analog)),
freq_digital / (freq_analog * T) * 100 - 100,
tick_label=[f'{f:.1f}' for f in freq_analog])
axes[1].set_xlabel('Analog Frequency Ξ© (rad/s)')
axes[1].set_ylabel('Warping Error (%)')
axes[1].set_title('Frequency Warping Error')
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('bilinear_warping.png', dpi=150)
plt.close()
8.4 μ¬μ μν(Pre-warping)¶
μκ³ μ£Όνμκ° μ¬λ°λ₯΄κ² λ§€νλλλ‘ νκΈ° μν΄ μλ λ‘κ·Έ κ·κ²©μ μ¬μ μν(pre-warp)ν©λλ€:
$$\Omega_p' = \frac{2}{T}\tan\left(\frac{\omega_p}{2}\right), \quad \Omega_s' = \frac{2}{T}\tan\left(\frac{\omega_s}{2}\right)$$
κ·Έλ° λ€μ μ¬μ μνλ μ£Όνμλ‘ μλ λ‘κ·Έ νν°λ₯Ό μ€κ³νκ³ μμΌμ°¨ λ³νμ μ μ©ν©λλ€.
8.5 λ¨κ³λ³ BLT μ€κ³¶
def iir_design_blt(wp_digital, ws_digital, Rp_dB, As_dB, fs, ftype='butter'):
"""
Complete IIR design using bilinear transform with pre-warping.
Parameters:
wp_digital: digital passband edge (Hz)
ws_digital: digital stopband edge (Hz)
Rp_dB: passband ripple (dB)
As_dB: stopband attenuation (dB)
fs: sampling frequency (Hz)
ftype: 'butter', 'cheby1', 'cheby2', or 'ellip'
Returns:
b, a: digital filter coefficients
N: filter order
"""
T = 1 / fs
# Step 1: Pre-warp digital frequencies to analog
Omega_p = 2 * fs * np.tan(np.pi * wp_digital / fs)
Omega_s = 2 * fs * np.tan(np.pi * ws_digital / fs)
print(f"Pre-warped frequencies: Ξ©p = {Omega_p:.2f}, Ξ©s = {Omega_s:.2f} rad/s")
# Step 2: Determine order
if ftype == 'butter':
N, Wn = signal.buttord(Omega_p, Omega_s, Rp_dB, As_dB, analog=True)
# Step 3: Design analog prototype
ba, aa = signal.butter(N, Wn, analog=True)
elif ftype == 'cheby1':
N, Wn = signal.cheb1ord(Omega_p, Omega_s, Rp_dB, As_dB, analog=True)
ba, aa = signal.cheby1(N, Rp_dB, Wn, analog=True)
elif ftype == 'cheby2':
N, Wn = signal.cheb2ord(Omega_p, Omega_s, Rp_dB, As_dB, analog=True)
ba, aa = signal.cheby2(N, As_dB, Wn, analog=True)
elif ftype == 'ellip':
N, Wn = signal.ellipord(Omega_p, Omega_s, Rp_dB, As_dB, analog=True)
ba, aa = signal.ellip(N, Rp_dB, As_dB, Wn, analog=True)
print(f"Analog filter order: N = {N}")
# Step 4: Bilinear transform
b, a = signal.bilinear(ba, aa, fs)
return b, a, N
# Example
fs = 8000
wp = 1000 # Hz
ws = 1500 # Hz
Rp = 1.0 # dB
As = 60 # dB
b, a, N = iir_design_blt(wp, ws, Rp, As, fs, ftype='ellip')
print(f"\nDigital filter coefficients:")
print(f"b = {b}")
print(f"a = {a}")
# Verify
w, H = signal.freqz(b, a, worN=4096, fs=fs)
H_dB = 20 * np.log10(np.abs(H) + 1e-15)
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# Magnitude
axes[0, 0].plot(w, H_dB, 'b-', linewidth=1.5)
axes[0, 0].axhline(-Rp, color='g', linestyle='--', alpha=0.5, label=f'-{Rp} dB')
axes[0, 0].axhline(-As, color='r', linestyle='--', alpha=0.5, label=f'-{As} dB')
axes[0, 0].axvline(wp, color='g', linestyle=':', alpha=0.5)
axes[0, 0].axvline(ws, color='r', linestyle=':', alpha=0.5)
axes[0, 0].set_xlabel('Frequency (Hz)')
axes[0, 0].set_ylabel('Magnitude (dB)')
axes[0, 0].set_title(f'Elliptic IIR (N={N}): Magnitude')
axes[0, 0].set_ylim(-80, 5)
axes[0, 0].legend()
axes[0, 0].grid(True, alpha=0.3)
# Phase
axes[0, 1].plot(w, np.unwrap(np.angle(H)) * 180 / np.pi, 'r-', linewidth=1.5)
axes[0, 1].set_xlabel('Frequency (Hz)')
axes[0, 1].set_ylabel('Phase (degrees)')
axes[0, 1].set_title('Phase Response')
axes[0, 1].grid(True, alpha=0.3)
# Group delay
w_gd, gd = signal.group_delay((b, a), w=4096, fs=fs)
axes[1, 0].plot(w_gd, gd, 'g-', linewidth=1.5)
axes[1, 0].set_xlabel('Frequency (Hz)')
axes[1, 0].set_ylabel('Group Delay (samples)')
axes[1, 0].set_title('Group Delay (Non-constant!)')
axes[1, 0].grid(True, alpha=0.3)
# Pole-zero plot
z_zeros, z_poles, k = signal.tf2zpk(b, a)
theta = np.linspace(0, 2 * np.pi, 200)
axes[1, 1].plot(np.cos(theta), np.sin(theta), 'k--', alpha=0.3)
axes[1, 1].plot(np.real(z_zeros), np.imag(z_zeros), 'bo', markersize=10, label='Zeros')
axes[1, 1].plot(np.real(z_poles), np.imag(z_poles), 'rx', markersize=10,
markeredgewidth=2, label='Poles')
axes[1, 1].set_xlabel('Real')
axes[1, 1].set_ylabel('Imaginary')
axes[1, 1].set_title('Pole-Zero Plot')
axes[1, 1].set_aspect('equal')
axes[1, 1].legend()
axes[1, 1].grid(True, alpha=0.3)
plt.suptitle('IIR Filter Design via Bilinear Transform', fontsize=13)
plt.tight_layout()
plt.savefig('iir_blt_design.png', dpi=150)
plt.close()
9. μνμ€ λΆλ³ λ°©λ²¶
9.1 μ리¶
μνμ€ λΆλ³(impulse invariance) λ°©λ²μ λμ§νΈ νν°μ μνμ€ μλ΅μ μλ λ‘κ·Έ μνμ€ μλ΅μ μνκ³Ό λμΌνκ² μ€μ ν©λλ€:
$$h[n] = T \cdot h_a(nT)$$
μ¬κΈ°μ $h_a(t)$λ μλ λ‘κ·Έ μνμ€ μλ΅, $T$λ μνλ§ μ£ΌκΈ°μ λλ€.
9.2 λ§€ν¶
μλ λ‘κ·Έ νν°κ° λΆλΆ λΆμ μ κ°(partial fraction expansion)λ₯Ό κ°μ§ λ:
$$H_a(s) = \sum_{k=1}^{N} \frac{A_k}{s - s_k}$$
λμ§νΈ νν°λ:
$$H(z) = T \sum_{k=1}^{N} \frac{A_k}{1 - e^{s_k T} z^{-1}}$$
κ° μλ λ‘κ·Έ κ·Ήμ $s_k$λ λμ§νΈ κ·Ήμ $z_k = e^{s_k T}$λ‘ λ§€νλ©λλ€.
9.3 μμΌλ¦¬μ΄μ± λ¬Έμ ¶
λμ§νΈ νν°μ μ£Όνμ μλ΅μ μλ λ‘κ·Έ μλ΅μ μ΄λλ 볡μ¬λ³Έλ€μ ν©μ λλ€:
$$H(e^{j\omega}) = \frac{1}{T} \sum_{k=-\infty}^{\infty} H_a\left(j\frac{\omega - 2\pi k}{T}\right)$$
μ΄λ‘ μΈν΄ $H_a(j\Omega)$κ° λμ μ ν(bandlimited)λμ§ μμΌλ©΄ μμΌλ¦¬μ΄μ±(aliasing)μ΄ λ°μνλ―λ‘, μνμ€ λΆλ³ λ°©λ²μ κ³ μ ν΅κ³Ό λ° λμ μ μ§ νν°μ μ ν©νμ§ μμ΅λλ€.
9.4 ꡬν μμ ¶
def impulse_invariance(ba, aa, fs):
"""
Apply impulse invariance method to convert analog filter to digital.
Parameters:
ba, aa: analog filter coefficients (transfer function form)
fs: sampling frequency
Returns:
bd, ad: digital filter coefficients
"""
T = 1 / fs
# Convert to zeros, poles, gain form
z_a, p_a, k_a = signal.tf2zpk(ba, aa)
# Partial fraction expansion
residues, poles, direct = signal.residue(ba, aa)
# Map poles: z_k = exp(s_k * T)
z_poles = np.exp(poles * T)
# Construct digital transfer function
# H(z) = T * sum(A_k / (1 - e^(s_k*T) * z^-1))
bd = np.array([0.0])
ad = np.array([1.0])
for A_k, z_k in zip(residues, z_poles):
# Each term: T * A_k / (1 - z_k * z^-1)
bd_k = np.array([T * A_k])
ad_k = np.array([1, -z_k])
# Combine fractions
bd_new = np.convolve(bd, ad_k) + np.convolve(bd_k, ad)
ad_new = np.convolve(ad, ad_k)
bd = bd_new
ad = ad_new
# Ensure real coefficients
bd = np.real(bd)
ad = np.real(ad)
return bd, ad
# Compare BLT and impulse invariance for a Butterworth lowpass
fs = 8000
N = 4
fc = 1000 # Cutoff frequency (Hz)
# Analog prototype
Omega_c = 2 * np.pi * fc
ba, aa = signal.butter(N, Omega_c, analog=True)
# Method 1: Bilinear transform
bd_blt, ad_blt = signal.bilinear(ba, aa, fs)
# Method 2: Impulse invariance
bd_ii, ad_ii = impulse_invariance(ba, aa, fs)
# Compare frequency responses
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
w_blt, H_blt = signal.freqz(bd_blt, ad_blt, worN=4096, fs=fs)
w_ii, H_ii = signal.freqz(bd_ii, ad_ii, worN=4096, fs=fs)
# Also plot the analog response for reference
w_a, H_a = signal.freqs(ba, aa, worN=np.linspace(0, 2 * np.pi * fs / 2, 4096))
axes[0].plot(w_a / (2 * np.pi), 20 * np.log10(np.abs(H_a) + 1e-15),
'k--', linewidth=1.5, label='Analog', alpha=0.5)
axes[0].plot(w_blt, 20 * np.log10(np.abs(H_blt) + 1e-15),
'b-', linewidth=1.5, label='Bilinear Transform')
axes[0].plot(w_ii, 20 * np.log10(np.abs(H_ii) + 1e-15),
'r-', linewidth=1.5, label='Impulse Invariance')
axes[0].set_xlabel('Frequency (Hz)')
axes[0].set_ylabel('Magnitude (dB)')
axes[0].set_title('BLT vs Impulse Invariance')
axes[0].set_ylim(-80, 5)
axes[0].legend()
axes[0].grid(True, alpha=0.3)
# Near Nyquist detail
axes[1].plot(w_blt, 20 * np.log10(np.abs(H_blt) + 1e-15),
'b-', linewidth=1.5, label='BLT')
axes[1].plot(w_ii, 20 * np.log10(np.abs(H_ii) + 1e-15),
'r-', linewidth=1.5, label='Impulse Invariance')
axes[1].set_xlabel('Frequency (Hz)')
axes[1].set_ylabel('Magnitude (dB)')
axes[1].set_title('Detail Near Nyquist (aliasing visible)')
axes[1].set_xlim(2000, 4000)
axes[1].set_ylim(-60, -20)
axes[1].legend()
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('impulse_invariance.png', dpi=150)
plt.close()
10. μμ ν IIR μ€κ³ μ μ°¨¶
10.1 λ¨κ³λ³ μ μ°¨¶
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β IIR Filter Design Steps β
β β
β 1. Specify digital requirements: β
β - Passband/stopband edges (Οp, Οs or fp, fs) β
β - Passband ripple Rp (dB) β
β - Stopband attenuation As (dB) β
β β
β 2. Choose filter type: β
β - Butterworth: maximally flat β
β - Chebyshev I: equiripple passband β
β - Chebyshev II: equiripple stopband β
β - Elliptic: minimum order β
β β
β 3. Choose conversion method (usually BLT) β
β β
β 4. Pre-warp critical frequencies β
β β
β 5. Determine minimum analog filter order β
β β
β 6. Design analog prototype β
β β
β 7. Apply analog-to-digital transformation β
β β
β 8. Verify specifications β
β - Check passband ripple β
β - Check stopband attenuation β
β - Check stability (all poles inside unit circle) β
β β
β 9. Implement (Direct Form II, SOS cascade, etc.) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
10.2 scipy.signal μ§μ μ€κ³ ν¨μ μ¬μ©¶
SciPyλ μ¬μ μνμ λ΄λΆμ μΌλ‘ μ²λ¦¬νλ κ³ μμ€ ν¨μλ₯Ό μ 곡ν©λλ€:
def design_all_types(fs, f_pass, f_stop, Rp, As):
"""Design IIR lowpass filter using all four types."""
results = {}
# Direct digital design (scipy handles pre-warping internally)
# Butterworth
N_b, Wn_b = signal.buttord(f_pass, f_stop, Rp, As, fs=fs)
b_b, a_b = signal.butter(N_b, Wn_b, fs=fs)
results['Butterworth'] = (b_b, a_b, N_b)
# Chebyshev Type I
N_c1, Wn_c1 = signal.cheb1ord(f_pass, f_stop, Rp, As, fs=fs)
b_c1, a_c1 = signal.cheby1(N_c1, Rp, Wn_c1, fs=fs)
results['Chebyshev I'] = (b_c1, a_c1, N_c1)
# Chebyshev Type II
N_c2, Wn_c2 = signal.cheb2ord(f_pass, f_stop, Rp, As, fs=fs)
b_c2, a_c2 = signal.cheby2(N_c2, As, Wn_c2, fs=fs)
results['Chebyshev II'] = (b_c2, a_c2, N_c2)
# Elliptic
N_e, Wn_e = signal.ellipord(f_pass, f_stop, Rp, As, fs=fs)
b_e, a_e = signal.ellip(N_e, Rp, As, Wn_e, fs=fs)
results['Elliptic'] = (b_e, a_e, N_e)
return results
# Design and compare
fs = 16000
f_pass = 2000
f_stop = 2500
Rp = 0.5
As = 60
results = design_all_types(fs, f_pass, f_stop, Rp, As)
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
for ax, (name, (b, a, N)) in zip(axes.flat, results.items()):
w, H = signal.freqz(b, a, worN=4096, fs=fs)
H_dB = 20 * np.log10(np.abs(H) + 1e-15)
ax.plot(w, H_dB, 'b-', linewidth=1.5)
ax.axhline(-Rp, color='g', linestyle='--', alpha=0.5, label=f'Rp = -{Rp} dB')
ax.axhline(-As, color='r', linestyle='--', alpha=0.5, label=f'As = -{As} dB')
ax.axvline(f_pass, color='g', linestyle=':', alpha=0.5)
ax.axvline(f_stop, color='r', linestyle=':', alpha=0.5)
ax.set_xlabel('Frequency (Hz)')
ax.set_ylabel('Magnitude (dB)')
ax.set_title(f'{name} (N={N})')
ax.set_ylim(-80, 5)
ax.legend(fontsize=8)
ax.grid(True, alpha=0.3)
plt.suptitle(f'IIR Filter Comparison (fp={f_pass} Hz, fs_stop={f_stop} Hz)', fontsize=13)
plt.tight_layout()
plt.savefig('iir_all_types.png', dpi=150)
plt.close()
# Print summary
print(f"\n{'Filter Type':<15} {'Order':<8} {'Actual Rp (dB)':<16} {'Actual As (dB)':<16}")
print("-" * 55)
for name, (b, a, N) in results.items():
w, H = signal.freqz(b, a, worN=4096, fs=fs)
H_mag = np.abs(H)
f = w
# Passband ripple
pass_idx = f <= f_pass
Rp_actual = -20 * np.log10(np.min(H_mag[pass_idx]))
# Stopband attenuation
stop_idx = f >= f_stop
As_actual = -20 * np.log10(np.max(H_mag[stop_idx]))
print(f"{name:<15} {N:<8} {Rp_actual:<16.4f} {As_actual:<16.4f}")
11. μμ μ± λΆμ¶
11.1 μμ μ± κΈ°μ€¶
λμ§νΈ IIR νν°λ $H(z)$μ λͺ¨λ κ·Ήμ μ΄ λ¨μμ λ΄λΆμ μ격ν μμΉν λλ§ μμ (stable)ν©λλ€:
$$|z_k| < 1, \quad \forall k$$
11.2 μμΉμ μμ μ±μ μν 2μ°¨ μΉμ (SOS)¶
μ§μ νμ(direct form)μΌλ‘ ꡬνλ κ³ μ°¨ IIR νν°λ κ³μ μμν(coefficient quantization) λ° μμΉ μ λ°λ λ¬Έμ κ° λ°μν μ μμ΅λλ€. ν΄κ²°μ± μ 2μ°¨ μΉμ (second-order sections, biquads)μ μ’ μ μ°κ²°(cascade)λ‘ κ΅¬ννλ κ²μ λλ€:
$$H(z) = \prod_{i=1}^{L} \frac{b_{0i} + b_{1i}z^{-1} + b_{2i}z^{-2}}{1 + a_{1i}z^{-1} + a_{2i}z^{-2}}$$
def stability_analysis(b, a, title=""):
"""Analyze IIR filter stability."""
# Poles and zeros
zeros, poles, gain = signal.tf2zpk(b, a)
# Check stability
pole_mags = np.abs(poles)
is_stable = np.all(pole_mags < 1.0)
max_pole_mag = np.max(pole_mags) if len(poles) > 0 else 0
print(f"Filter: {title}")
print(f" Number of poles: {len(poles)}")
print(f" Number of zeros: {len(zeros)}")
print(f" Maximum pole magnitude: {max_pole_mag:.6f}")
print(f" Stable: {is_stable}")
print(f" Stability margin: {1.0 - max_pole_mag:.6f}")
# Pole-zero plot
fig, ax = plt.subplots(figsize=(8, 8))
theta = np.linspace(0, 2 * np.pi, 200)
ax.plot(np.cos(theta), np.sin(theta), 'k-', alpha=0.3, label='Unit circle')
ax.plot(np.real(zeros), np.imag(zeros), 'bo', markersize=10,
label=f'Zeros ({len(zeros)})')
ax.plot(np.real(poles), np.imag(poles), 'rx', markersize=10,
markeredgewidth=2, label=f'Poles ({len(poles)})')
# Color poles by stability
for p in poles:
color = 'green' if np.abs(p) < 1 else 'red'
circle = plt.Circle((np.real(p), np.imag(p)), 0.03,
fill=True, color=color, alpha=0.3)
ax.add_patch(circle)
ax.set_xlabel('Real')
ax.set_ylabel('Imaginary')
ax.set_title(f'Pole-Zero Plot: {title}\n(Stable: {is_stable}, '
f'Max |pole| = {max_pole_mag:.4f})')
ax.set_aspect('equal')
ax.legend()
ax.grid(True, alpha=0.3)
ax.set_xlim(-1.5, 1.5)
ax.set_ylim(-1.5, 1.5)
plt.tight_layout()
return fig, is_stable
# Design a high-order filter and check stability
N = 12
b_high, a_high = signal.butter(N, 0.3) # 12th-order Butterworth
fig, stable = stability_analysis(b_high, a_high, f"Butterworth N={N}")
plt.savefig('stability_analysis.png', dpi=150)
plt.close()
# Convert to SOS form for better numerical stability
sos = signal.tf2sos(b_high, a_high)
print(f"\nSOS representation: {sos.shape[0]} second-order sections")
print(f"SOS coefficients:\n{sos}")
11.3 μμΉμ λΉκ΅: tf vs SOS¶
def compare_tf_vs_sos(order=20):
"""Compare direct form vs SOS implementation for high-order filter."""
# Design high-order filter
b, a = signal.butter(order, 0.1)
sos = signal.butter(order, 0.1, output='sos')
# Test signal
np.random.seed(42)
x = np.random.randn(1000)
# Filter using tf form
y_tf = signal.lfilter(b, a, x)
# Filter using SOS form
y_sos = signal.sosfilt(sos, x)
# Compare
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# Frequency response comparison
w_tf, H_tf = signal.freqz(b, a, worN=4096)
w_sos, H_sos = signal.sosfreqz(sos, worN=4096)
axes[0, 0].plot(w_tf / np.pi, 20 * np.log10(np.abs(H_tf) + 1e-15),
'b-', linewidth=1.5, label='tf form')
axes[0, 0].plot(w_sos / np.pi, 20 * np.log10(np.abs(H_sos) + 1e-15),
'r--', linewidth=1.5, label='SOS form')
axes[0, 0].set_xlabel('Normalized Frequency (ΓΟ)')
axes[0, 0].set_ylabel('Magnitude (dB)')
axes[0, 0].set_title('Frequency Response')
axes[0, 0].legend()
axes[0, 0].grid(True, alpha=0.3)
# Output comparison
axes[0, 1].plot(y_tf[:200], 'b-', alpha=0.7, label='tf form')
axes[0, 1].plot(y_sos[:200], 'r--', alpha=0.7, label='SOS form')
axes[0, 1].set_xlabel('Sample')
axes[0, 1].set_ylabel('Output')
axes[0, 1].set_title('Output Signal')
axes[0, 1].legend()
axes[0, 1].grid(True, alpha=0.3)
# Difference
diff = np.abs(y_tf - y_sos)
axes[1, 0].semilogy(diff, 'k-')
axes[1, 0].set_xlabel('Sample')
axes[1, 0].set_ylabel('|y_tf - y_sos|')
axes[1, 0].set_title(f'Numerical Difference (max: {np.max(diff):.2e})')
axes[1, 0].grid(True, alpha=0.3)
# Pole magnitudes
zeros_tf, poles_tf, _ = signal.tf2zpk(b, a)
axes[1, 1].plot(np.abs(poles_tf), 'rx', markersize=10, markeredgewidth=2)
axes[1, 1].axhline(1.0, color='k', linestyle='--', alpha=0.5, label='Unit circle')
axes[1, 1].set_xlabel('Pole Index')
axes[1, 1].set_ylabel('|pole|')
axes[1, 1].set_title(f'Pole Magnitudes (N={order})')
axes[1, 1].legend()
axes[1, 1].grid(True, alpha=0.3)
plt.suptitle(f'Direct Form vs SOS (Order={order})', fontsize=13)
plt.tight_layout()
plt.savefig('tf_vs_sos.png', dpi=150)
plt.close()
compare_tf_vs_sos(order=20)
12. νν° μ ν λΉκ΅¶
12.1 μμ½ ν¶
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β IIR Filter Type Comparison β
ββββββββββββββββ¬ββββββββββββββ¬βββββββββββββ¬βββββββββββββ¬βββββββββββββββββ€
β β Butterworth β Chebyshev Iβ Chebyshev IIβ Elliptic β
ββββββββββββββββΌββββββββββββββΌβββββββββββββΌβββββββββββββΌβββββββββββββββββ€
β Passband β Maximally β Equiripple β Monotonic β Equiripple β
β β flat β β β β
ββββββββββββββββΌββββββββββββββΌβββββββββββββΌβββββββββββββΌβββββββββββββββββ€
β Stopband β Monotonic β Monotonic β Equiripple β Equiripple β
ββββββββββββββββΌββββββββββββββΌβββββββββββββΌβββββββββββββΌβββββββββββββββββ€
β Transition β Widest β Medium β Medium β Sharpest β
β band β β β β β
ββββββββββββββββΌββββββββββββββΌβββββββββββββΌβββββββββββββΌβββββββββββββββββ€
β Order for β Highest β Medium β Medium β Lowest β
β given specs β β β β β
ββββββββββββββββΌββββββββββββββΌβββββββββββββΌβββββββββββββΌβββββββββββββββββ€
β Group delay β Most β Less β Better thanβ Most β
β β uniform β uniform β Type I β non-uniform β
ββββββββββββββββΌββββββββββββββΌβββββββββββββΌβββββββββββββΌβββββββββββββββββ€
β Use case β General, β Sharp β Flat β Minimum order,β
β β smooth β cutoff, β passband, β tight specs β
β β response β ok ripple β ok stopbandβ β
ββββββββββββββββ΄ββββββββββββββ΄βββββββββββββ΄βββββββββββββ΄βββββββββββββββββ
12.2 μ’ ν© μκ°μ λΉκ΅¶
def comprehensive_comparison():
"""Complete comparison of all four IIR filter types."""
fs = 16000
fp = 2000
fstop = 3000
Rp = 1.0
As = 50
filters = {}
# Butterworth
N, Wn = signal.buttord(fp, fstop, Rp, As, fs=fs)
sos = signal.butter(N, Wn, fs=fs, output='sos')
filters['Butterworth'] = (sos, N)
# Chebyshev I
N, Wn = signal.cheb1ord(fp, fstop, Rp, As, fs=fs)
sos = signal.cheby1(N, Rp, Wn, fs=fs, output='sos')
filters['Chebyshev I'] = (sos, N)
# Chebyshev II
N, Wn = signal.cheb2ord(fp, fstop, Rp, As, fs=fs)
sos = signal.cheby2(N, As, Wn, fs=fs, output='sos')
filters['Chebyshev II'] = (sos, N)
# Elliptic
N, Wn = signal.ellipord(fp, fstop, Rp, As, fs=fs)
sos = signal.ellip(N, Rp, As, Wn, fs=fs, output='sos')
filters['Elliptic'] = (sos, N)
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
colors = {'Butterworth': 'blue', 'Chebyshev I': 'red',
'Chebyshev II': 'green', 'Elliptic': 'purple'}
# Magnitude response overlay
ax = axes[0, 0]
for name, (sos, N) in filters.items():
w, H = signal.sosfreqz(sos, worN=4096, fs=fs)
ax.plot(w, 20 * np.log10(np.abs(H) + 1e-15),
color=colors[name], linewidth=1.5, label=f'{name} (N={N})')
ax.axhline(-Rp, color='gray', linestyle='--', alpha=0.5)
ax.axhline(-As, color='gray', linestyle='--', alpha=0.5)
ax.set_xlabel('Frequency (Hz)')
ax.set_ylabel('Magnitude (dB)')
ax.set_title('Magnitude Response')
ax.set_ylim(-70, 5)
ax.legend(fontsize=8)
ax.grid(True, alpha=0.3)
# Passband detail
ax = axes[0, 1]
for name, (sos, N) in filters.items():
w, H = signal.sosfreqz(sos, worN=4096, fs=fs)
ax.plot(w, 20 * np.log10(np.abs(H) + 1e-15),
color=colors[name], linewidth=1.5, label=f'{name} (N={N})')
ax.set_xlabel('Frequency (Hz)')
ax.set_ylabel('Magnitude (dB)')
ax.set_title('Passband Detail')
ax.set_xlim(0, fp * 1.2)
ax.set_ylim(-2, 0.5)
ax.legend(fontsize=8)
ax.grid(True, alpha=0.3)
# Group delay
ax = axes[1, 0]
for name, (sos, N) in filters.items():
b, a = signal.sos2tf(sos)
w_gd, gd = signal.group_delay((b, a), w=4096, fs=fs)
ax.plot(w_gd, gd, color=colors[name], linewidth=1.5, label=f'{name}')
ax.set_xlabel('Frequency (Hz)')
ax.set_ylabel('Group Delay (samples)')
ax.set_title('Group Delay')
ax.set_xlim(0, fs / 2)
ax.legend(fontsize=8)
ax.grid(True, alpha=0.3)
# Step response
ax = axes[1, 1]
step = np.ones(200)
for name, (sos, N) in filters.items():
y = signal.sosfilt(sos, step)
ax.plot(y, color=colors[name], linewidth=1.5, label=f'{name}')
ax.set_xlabel('Sample')
ax.set_ylabel('Amplitude')
ax.set_title('Step Response')
ax.legend(fontsize=8)
ax.grid(True, alpha=0.3)
plt.suptitle('Comprehensive IIR Filter Comparison', fontsize=14)
plt.tight_layout()
plt.savefig('comprehensive_comparison.png', dpi=150)
plt.close()
comprehensive_comparison()
13. Python ꡬν¶
13.1 μμ ν IIR μ€κ³ μν¬νλ‘μ°¶
def complete_iir_workflow(fs, filter_type, band_type, freqs, Rp, As):
"""
Complete IIR filter design and analysis workflow.
Parameters:
fs: sampling frequency (Hz)
filter_type: 'butter', 'cheby1', 'cheby2', 'ellip'
band_type: 'lowpass', 'highpass', 'bandpass', 'bandstop'
freqs: (f_pass, f_stop) or ((fp1, fp2), (fs1, fs2)) for bandpass/stop
Rp: passband ripple (dB)
As: stopband attenuation (dB)
Returns:
sos: second-order sections
info: design information
"""
f_pass, f_stop = freqs
# Order determination
ord_funcs = {
'butter': signal.buttord,
'cheby1': signal.cheb1ord,
'cheby2': signal.cheb2ord,
'ellip': signal.ellipord,
}
N, Wn = ord_funcs[filter_type](f_pass, f_stop, Rp, As, fs=fs)
# Design
design_funcs = {
'butter': lambda: signal.butter(N, Wn, btype=band_type, fs=fs, output='sos'),
'cheby1': lambda: signal.cheby1(N, Rp, Wn, btype=band_type, fs=fs, output='sos'),
'cheby2': lambda: signal.cheby2(N, As, Wn, btype=band_type, fs=fs, output='sos'),
'ellip': lambda: signal.ellip(N, Rp, As, Wn, btype=band_type, fs=fs, output='sos'),
}
sos = design_funcs[filter_type]()
info = {
'filter_type': filter_type,
'band_type': band_type,
'order': N,
'Wn': Wn,
'Rp': Rp,
'As': As,
}
return sos, info
# Example: Bandpass filter
fs = 44100
sos_bp, info_bp = complete_iir_workflow(
fs=fs,
filter_type='ellip',
band_type='bandpass',
freqs=([800, 3000], [500, 3500]),
Rp=0.5,
As=50
)
print(f"Bandpass filter: {info_bp}")
# Apply to signal
t = np.arange(0, 0.5, 1/fs)
# Speech-like signal: mix of tones + noise
x = (0.5 * np.sin(2*np.pi*200*t) + # Low freq
np.sin(2*np.pi*1000*t) + # Mid freq (passband)
0.8 * np.sin(2*np.pi*2000*t) + # Mid freq (passband)
0.3 * np.sin(2*np.pi*5000*t) + # High freq
0.2 * np.random.randn(len(t))) # Noise
y = signal.sosfilt(sos_bp, x)
fig, axes = plt.subplots(3, 1, figsize=(14, 10))
# Time domain
axes[0].plot(t[:2000] * 1000, x[:2000], 'b-', alpha=0.7, label='Input')
axes[0].plot(t[:2000] * 1000, y[:2000], 'r-', linewidth=1.5, label='Filtered')
axes[0].set_xlabel('Time (ms)')
axes[0].set_ylabel('Amplitude')
axes[0].set_title('Time Domain: Bandpass Filtering')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
# Spectrum
N_fft = len(x)
freqs_fft = np.fft.rfftfreq(N_fft, 1/fs)
X = np.abs(np.fft.rfft(x)) / N_fft
Y = np.abs(np.fft.rfft(y)) / N_fft
axes[1].plot(freqs_fft, 20*np.log10(X + 1e-15), 'b-', alpha=0.7, label='Input')
axes[1].plot(freqs_fft, 20*np.log10(Y + 1e-15), 'r-', linewidth=1.5, label='Filtered')
axes[1].axvspan(800, 3000, alpha=0.1, color='green', label='Passband')
axes[1].set_xlabel('Frequency (Hz)')
axes[1].set_ylabel('Magnitude (dB)')
axes[1].set_title('Frequency Domain')
axes[1].set_xlim(0, 8000)
axes[1].legend()
axes[1].grid(True, alpha=0.3)
# Filter frequency response
w, H = signal.sosfreqz(sos_bp, worN=4096, fs=fs)
axes[2].plot(w, 20*np.log10(np.abs(H) + 1e-15), 'b-', linewidth=1.5)
axes[2].axvspan(800, 3000, alpha=0.1, color='green', label='Passband')
axes[2].set_xlabel('Frequency (Hz)')
axes[2].set_ylabel('Magnitude (dB)')
axes[2].set_title(f'Filter Response (Elliptic N={info_bp["order"]})')
axes[2].set_xlim(0, 8000)
axes[2].set_ylim(-70, 5)
axes[2].legend()
axes[2].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('iir_bandpass_demo.png', dpi=150)
plt.close()
13.2 SOSλ₯Ό μ¬μ©ν μ€μκ° IIR νν°λ§¶
class RealtimeIIRFilter:
"""Real-time IIR filter using second-order sections."""
def __init__(self, sos):
"""
Initialize with SOS coefficients.
Parameters:
sos: second-order sections array (L x 6)
"""
self.sos = np.array(sos, dtype=np.float64)
self.n_sections = self.sos.shape[0]
# State variables: 2 delay elements per section
self.state = np.zeros((self.n_sections, 2))
def process_sample(self, x):
"""Process a single input sample."""
for i in range(self.n_sections):
b0, b1, b2, a0, a1, a2 = self.sos[i]
# Direct Form II Transposed
y = b0 * x + self.state[i, 0]
self.state[i, 0] = b1 * x - a1 * y + self.state[i, 1]
self.state[i, 1] = b2 * x - a2 * y
x = y # Output of this section is input to next
return y
def process_block(self, x_block):
"""Process a block of samples."""
y = np.zeros_like(x_block)
for n in range(len(x_block)):
y[n] = self.process_sample(x_block[n])
return y
def reset(self):
"""Reset filter state."""
self.state = np.zeros((self.n_sections, 2))
# Demonstration
sos = signal.butter(6, 1000, fs=8000, output='sos')
filt = RealtimeIIRFilter(sos)
# Process in blocks (simulating real-time)
np.random.seed(42)
x = np.random.randn(1000)
block_size = 64
y_realtime = np.zeros_like(x)
for i in range(0, len(x), block_size):
block = x[i:i + block_size]
y_realtime[i:i + len(block)] = filt.process_block(block)
# Compare with scipy batch processing
y_batch = signal.sosfilt(sos, x)
error = np.max(np.abs(y_realtime - y_batch))
print(f"Maximum error between real-time and batch: {error:.2e}")
14. μ°μ΅ λ¬Έμ ¶
μ°μ΅ 1: λ²ν°μμ€ νν° μ€κ³¶
λ€μ κ·κ²©μ λμ§νΈ λ²ν°μμ€ μ μ ν΅κ³Ό νν°λ₯Ό μ€κ³νμΈμ: - μνλ§ μ£Όνμ: $f_s = 10000$ Hz - ν΅κ³Όλμ μ£μ§: $f_p = 1500$ Hz, 리ν $\leq 0.5$ dB - μ μ§λμ μ£μ§: $f_s = 2000$ Hz, κ°μ $\geq 40$ dB
(a) λ²ν°μμ€ μ°¨μ 곡μμ μ¬μ©νμ¬ νμν νν° μ°¨μλ₯Ό κ³μ°νμΈμ.
(b) λμ§νΈ μ£Όνμλ₯Ό μλ λ‘κ·Έ μ£Όνμλ‘ μ¬μ μννμΈμ.
(c) μλ λ‘κ·Έ λ²ν°μμ€ μνμ μ€κ³νκ³ μμΌμ°¨ λ³νμ μ μ©νμΈμ.
(d) μ€κ³κ° κ·κ²©μ μΆ©μ‘±νλμ§ κ²μ¦νμΈμ. ν¬κΈ° μλ΅, μμ μλ΅, κ΅° μ§μ°(group delay), κ·Ή-μμ λ€μ΄μ΄κ·Έλ¨μ 그리μΈμ.
(e) μλ μ€κ³λ₯Ό scipy.signal.butterμ λΉκ΅νμΈμ. κ²°κ³Όκ° λμΌνκ°μ?
μ°μ΅ 2: 체λΉμΌν νν° λΉκ΅¶
κ·κ²©: $f_s = 8000$ Hz, $f_p = 1000$ Hz, $f_\text{stop} = 1200$ Hz, $R_p = 1$ dB, $A_s = 50$ dBμ λν΄:
(a) 체λΉμΌν Type Iκ³Ό Type II νν°λ₯Ό λͺ¨λ μ€κ³νμΈμ.
(b) λ¨μΌ κ·Έλνμμ μ£Όνμ μλ΅μ λΉκ΅νμΈμ.
(c) ν΅κ³Όλμμμ μ΄λ κ²μ΄ κ΅° μ§μ° ννλκ° λ μ’μκ°μ? μ λμ μΌλ‘ 보μ¬μ£ΌμΈμ.
(d) λ νν°λ₯Ό μ²ν(chirp) μ νΈ(100 Hzμμ 4000 Hz μ€μ)μ μ μ©νκ³ μΆλ ₯μ λΉκ΅νμΈμ.
μ°μ΅ 3: μ€λμ€μ© νμν νν°¶
μ€λμ€ μν°μμΌλ¦¬μ΄μ±(anti-aliasing)μ μν νμν μ μ ν΅κ³Ό νν°λ₯Ό μ€κ³νμΈμ: - μ λ ₯ μνλ§ μλ: 96 kHz (48 kHzλ‘ λ€μ΄μνλ§ μμ ) - ν΅κ³Όλμ: 0~20 kHz, 리ν $\leq 0.01$ dB - μ μ§λμ: 24 kHz~48 kHz, κ°μ $\geq 96$ dB (16λΉνΈ μ λ°λ)
(a) μ΅μ νν° μ°¨μλ₯Ό κ²°μ νμΈμ.
(b) SOS νμμΌλ‘ ꡬννμΈμ.
(c) μ ν λ° dB μ€μΌμΌ λͺ¨λμμ ν¬κΈ° μλ΅μ 그리μΈμ.
(d) κ΅° μ§μ°μ κ³μ°νκ³ κ·Έλ¦¬μΈμ. μ€λμ€μμ κ΅° μ§μ° λ³λμ΄ νμ© κ°λ₯νκ°μ?
μ°μ΅ 4: μμμ μμΌμ°¨ λ³ν¶
1μ°¨ μλ λ‘κ·Έ μ μ ν΅κ³Ό νν°κ° μ£Όμ΄μ‘μ λ:
$$H_a(s) = \frac{\Omega_c}{s + \Omega_c}$$
(a) μμΌμ°¨ λ³ν $s = \frac{2}{T}\frac{z-1}{z+1}$μ μ μ©νμ¬ $H(z)$λ₯Ό μ λνμΈμ.
(b) $\Omega_c = 2\pi \times 1000$ rad/s, $f_s = 8000$ Hzμ λν΄ λμ§νΈ νν° κ³μ $b_0, b_1, a_0, a_1$μ κ³μ°νμΈμ.
(c) $H(z)$μ μ£Όνμ μλ΅κ³Ό μνλ μλ λ‘κ·Έ μλ΅μ λΉκ΅νμ¬ κ²μ¦νμΈμ.
(d) λμΌν μ°¨λ¨ μ£Όνμμ 2μ°¨ λ²ν°μμ€ νν°μ λν΄ λ°λ³΅νμΈμ.
μ°μ΅ 5: μμ μ± μ‘°μ¬¶
(a) 16μ°¨ 체λΉμΌν Type I νν°($R_p = 3$ dB, $\omega_c = 0.1\pi$)λ₯Ό tfμ sos νμμΌλ‘ μ€κ³νμΈμ.
(b) λ ννμ λν΄ κ·Ήμ ν¬κΈ°λ₯Ό κ³μ°νκ³ νμνμΈμ.
(c) λ ννμ μ¬μ©νμ¬ λ°±μ μ‘μ μ νΈλ₯Ό νν°λ§νμΈμ. tf νμ μΆλ ₯μ΄ μ ν¨νκ°μ?
(d) 체λΉμΌν Type I νν°μμ tf νμμ΄ μΌλ°μ μΌλ‘ μ΄λ μ°¨μμμ 무λμ§κΈ° μμνλμ? μ°¨μ 4, 8, 12, 16, 20, 24λ‘ μ€νν΄λ³΄μΈμ.
μ°μ΅ 6: IIR vs FIR λΉκ΅¶
κ·κ²©: $f_s = 16000$ Hz, $f_p = 3000$ Hz, $f_\text{stop} = 3500$ Hz, $A_s = 60$ dBμ λν΄:
(a) IIR(νμν)κ³Ό FIR(Parks-McClellan) νν° λͺ¨λ μ€κ³νμΈμ.
(b) λ€μμ λΉκ΅νμΈμ: νν° μ°¨μ, μνλΉ κ³μ° λΉμ©, κ΅° μ§μ°, ν¬κΈ° μλ΅.
(c) μμ±κ³Ό μ μ¬ν μ νΈ(100, 200, ..., 5000 Hzμ κ³ μ‘°ν ν©μ°)λ₯Ό νν°λ§νμΈμ. μκ° μμ μΆλ ₯μ μκ°μ μΌλ‘ λΉκ΅νκ³ SNR κ°μ μ κ³μ°νμΈμ.
(d) IIRμ FIRλ³΄λ€ μ νν΄μΌ νλ μμ© μλ리μ€λ 무μμΈμ§, κ·Έ λ°λλ 무μμΈμ§ μ€λͺ νμΈμ?
μ°μ΅ 7: λμ μ μ§(λ ΈμΉ) νν°¶
μ¬μ λ(ECG) μ νΈμμ 60 Hz μ μμ κ°μ(powerline interference)μ μ κ±°νκΈ° μν μ’μ λμ μ μ§ νν°λ₯Ό μ€κ³νμΈμ: - μνλ§ μ£Όνμ: 500 Hz - μ κ±° λμ: 59-61 Hz - ν΅κ³Όλμ: 0-55 Hz λ° 65-250 Hz - ν΅κ³Όλμ 리ν: $\leq 0.1$ dB - μ μ§λμ κ°μ : $\geq 30$ dB
(a) νμν μνμ μ¬μ©νμ¬ μ€κ³νμΈμ. μ΄λ€ μ°¨μκ° νμνκ°μ?
(b) 60 Hz μ£Όλ³μ νλν λ·°λ‘ ν¬κΈ° μλ΅μ 그리μΈμ.
(c) 60 Hz μ€μΌμ΄ μλ ν©μ± ECG μ νΈλ₯Ό μμ±νκ³ νν°μ ν¨κ³Όλ₯Ό μμ°νμΈμ.
(d) λ²ν°μμ€ λ νμν ꡬνμ κ΅° μ§μ°μ κ³μ°νκ³ λΉκ΅νμΈμ.
μ°Έκ³ λ¬Έν¶
- Oppenheim, A. V., & Schafer, R. W. (2010). Discrete-Time Signal Processing (3rd ed.). Pearson. Chapter 7.
- Proakis, J. G., & Manolakis, D. G. (2007). Digital Signal Processing (4th ed.). Pearson. Chapter 11.
- Parks, T. W., & Burrus, C. S. (1987). Digital Filter Design. Wiley.
- Antoniou, A. (2006). Digital Signal Processing: Signals, Systems, and Filters. McGraw-Hill.
- SciPy Documentation -- Filter design functions: https://docs.scipy.org/doc/scipy/reference/signal.html
- Smith, S. W. (1997). The Scientist and Engineer's Guide to Digital Signal Processing. Available free at http://www.dspguide.com/
νμ¶
- μ΄μ : 09. FIR νν° μ€κ³
- λ€μ: 11. λ€μ€λ₯ μ νΈ μ²λ¦¬
- λͺ©μ°¨λ‘ λμκ°κΈ°