Z λ³ν(Z-Transform)
Z λ³ν(Z-Transform)¶
κ°μ¶
Z λ³νμ λΌνλΌμ€ λ³ν(Laplace Transform)μ μ΄μ° μκ°(discrete-time) λμ κ°λ μ λλ€. μ°¨λΆ λ°©μ μ(difference equation)μ λμ λ°©μ μμΌλ‘ λ³ννμ¬, 볡μ $z$ νλ©΄μμ μ΄μ° μκ° LTI μμ€ν μ λΆμν μ μκ² ν΄μ€λλ€. Z λ³νμ μμ€ν μμ μ±, μ£Όνμ μλ΅, μ λ¬ ν¨μ κ²°μ μ κ°λ ₯ν λꡬλ₯Ό μ 곡ν©λλ€. μ΄ λ μ¨μμλ Z λ³ν μ΄λ‘ , μ±μ§, μλ³ν λ°©λ², κ·Έλ¦¬κ³ λμ§νΈ μμ€ν λΆμ μμ©μ λ€λ£Ήλλ€.
νμ΅ λͺ©ν: - μλ°©ν₯(bilateral) λ° λ¨λ°©ν₯(unilateral) Z λ³νμ μ μνκ³ κ³μ°νκΈ° - μλ ΄ μμ(ROC, Region of Convergence)κ³Ό κ·Έ μλ―Έ μ΄ν΄νκΈ° - μμ€ν λΆμμ μν Z λ³ν μ±μ§ μ μ©νκΈ° - λ€μν λ°©λ²μΌλ‘ μ Z λ³ν κ³μ°νκΈ° - μ λ¬ ν¨μ, κ·Ήμ (poles), μμ (zeros)μ μ΄μ©νμ¬ LTI μμ€ν λΆμνκΈ° - Z λ³νκ³Ό DTFT λ° λΌνλΌμ€ λ³νμ κ΄κ³ μ΄ν΄νκΈ°
μ μ νμ΅: 06. μ΄μ° νΈλ¦¬μ λ³ν
1. Z λ³νμ μ μ¶
1.1 μλ°©ν₯ Z λ³ν(Bilateral Z-Transform)¶
μ΄μ° μκ° μ νΈ $x[n]$μ μλ°©ν₯(λ λ°©ν₯) Z λ³νμ λ€μκ³Ό κ°μ΅λλ€:
$$\boxed{X(z) = \mathcal{Z}\{x[n]\} = \sum_{n=-\infty}^{\infty} x[n] \, z^{-n}}$$
μ¬κΈ°μ $z$λ 볡μ λ³μλ‘, $z = r \, e^{j\omega}$μ λλ€.
1.2 λ¨λ°©ν₯ Z λ³ν(Unilateral Z-Transform)¶
λ¨λ°©ν₯(ν λ°©ν₯) Z λ³νμ μ΄κΈ° μ‘°κ±΄μ΄ μλ μΈκ³Ό μ νΈμ μμ€ν μ μ¬μ©λ©λλ€:
$$X(z) = \sum_{n=0}^{\infty} x[n] \, z^{-n}$$
μ΄ ννλ νΉν λΉμ(non-zero) μ΄κΈ° μ‘°κ±΄μ΄ μλ μ°¨λΆ λ°©μ μμ ν λ μ μ©ν©λλ€.
1.3 μ§κ΄μ μ΄ν΄: zλ 무μμΈκ°?¶
볡μ λ³μ $z = r \, e^{j\omega}$λ λ€μκ³Ό κ°μ΄ λΆν΄λ©λλ€: - $|z| = r$: μμ μΌλ‘λΆν°μ λ°κ²½(μ§μ κ°μ€μ ν΅ν΄ μλ ΄ μ μ΄) - $\angle z = \omega$: κ°λ(μ£Όνμμ λμ) - λ¨μμ(unit circle) μ($r = 1$, $z = e^{j\omega}$): Z λ³νμ DTFTλ‘ μΆμ½λ¨
1.4 μ£Όμ Z λ³ν μ(Common Z-Transform Pairs)¶
| μ νΈ $x[n]$ | Z λ³ν $X(z)$ | ROC |
|---|---|---|
| $\delta[n]$ | $1$ | λͺ¨λ $z$ |
| $u[n]$ (λ¨μ κ³λ¨) | $\frac{z}{z-1} = \frac{1}{1-z^{-1}}$ | $|z| > 1$ |
| $a^n u[n]$ | $\frac{z}{z-a} = \frac{1}{1-az^{-1}}$ | $|z| > |a|$ |
| $-a^n u[-n-1]$ | $\frac{z}{z-a} = \frac{1}{1-az^{-1}}$ | $|z| < |a|$ |
| $n a^n u[n]$ | $\frac{az}{(z-a)^2} = \frac{az^{-1}}{(1-az^{-1})^2}$ | $|z| > |a|$ |
| $\cos(\omega_0 n) u[n]$ | $\frac{z(z-\cos\omega_0)}{z^2 - 2z\cos\omega_0 + 1}$ | $|z| > 1$ |
| $r^n \cos(\omega_0 n) u[n]$ | $\frac{z(z - r\cos\omega_0)}{z^2 - 2rz\cos\omega_0 + r^2}$ | $|z| > r$ |
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
def compute_z_transform_examples():
"""Compute and verify common Z-transform pairs."""
# Example 1: x[n] = (0.8)^n * u[n]
a = 0.8
N = 50
n = np.arange(N)
x = a ** n # Causal exponential
# Z-transform: X(z) = 1 / (1 - 0.8 * z^{-1}), |z| > 0.8
# Evaluate on unit circle (should give DTFT)
omega = np.linspace(-np.pi, np.pi, 1024)
z_unit = np.exp(1j * omega)
# X(z) on unit circle
X_formula = 1.0 / (1.0 - a * z_unit ** (-1))
# DTFT (direct computation from samples)
X_dtft = np.zeros(len(omega), dtype=complex)
for k in range(N):
X_dtft += x[k] * np.exp(-1j * omega * k)
# Compare
fig, axes = plt.subplots(2, 1, figsize=(12, 8))
axes[0].plot(omega / np.pi, np.abs(X_formula), 'b-', linewidth=2,
label='Z-transform formula')
axes[0].plot(omega / np.pi, np.abs(X_dtft), 'r--', linewidth=1,
label=f'DTFT (N={N} terms)')
axes[0].set_title(r'$x[n] = 0.8^n u[n]$: Magnitude on Unit Circle')
axes[0].set_xlabel(r'$\omega / \pi$')
axes[0].set_ylabel(r'$|X(e^{j\omega})|$')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
axes[1].plot(omega / np.pi, np.angle(X_formula), 'b-', linewidth=2,
label='Z-transform formula')
axes[1].plot(omega / np.pi, np.angle(X_dtft), 'r--', linewidth=1,
label=f'DTFT (N={N} terms)')
axes[1].set_title('Phase on Unit Circle')
axes[1].set_xlabel(r'$\omega / \pi$')
axes[1].set_ylabel('Phase (radians)')
axes[1].legend()
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('ztransform_unit_circle.png', dpi=150)
plt.show()
compute_z_transform_examples()
2. μλ ΄ μμ(ROC, Region of Convergence)¶
2.1 μ μ¶
ROCλ Z λ³νμ ν©μ΄ μλ ΄νλ λͺ¨λ $z$ κ°μ μ§ν©μ λλ€:
$$\text{ROC} = \left\{ z \in \mathbb{C} : \sum_{n=-\infty}^{\infty} |x[n]| \, |z|^{-n} < \infty \right\}$$
ROCλ νμ z νλ©΄μμ νν(annular) μμμ λλ€(μμ μ μ€μ¬μΌλ‘ ν λ λμ¬μ μ¬μ΄μ κ³ λ¦¬):
$$R^{-} < |z| < R^{+}$$
2.2 ROC μ±μ§¶
- ROCμλ $X(z)$μ κ·Ήμ (poles)μ΄ ν¬ν¨λμ§ μμ
- μ ν μ§μ μ νΈ(finite-duration signals): ROCλ μ 체 z νλ©΄($z = 0$ λ°/λλ $z = \infty$ μ μΈ κ°λ₯)
- μ€λ₯Έμͺ½ μ νΈ(right-sided signals) ($x[n] = 0$, $n < N_1$): ROCλ μ μΈλΆ: $|z| > R^{-}$
- μΌμͺ½ μ νΈ(left-sided signals) ($x[n] = 0$, $n > N_2$): ROCλ μ λ΄λΆ: $|z| < R^{+}$
- μλ°©ν₯ μ νΈ(two-sided signals): ROCλ νν κ³ λ¦¬
- DTFT μ‘΄μ¬: ROCκ° λ¨μμ $|z| = 1$μ ν¬ν¨ν κ²½μ°μλ§ μ‘΄μ¬
- μΈκ³Όμ μ΄κ³ μμ μ μΈ μμ€ν : ROCκ° λ¨μμ μΈλΆκΉμ§ ν¬ν¨; λͺ¨λ κ·Ήμ μ λ¨μμ λ΄λΆμ μμΉ
2.3 ROCμ μ νΈ μ ν: λμΌν X(z), λ€λ₯Έ μ νΈ¶
Z λ³ν $X(z) = \frac{1}{1 - az^{-1}}$μ ROCμ λ°λΌ λ κ°μ§ λ€λ₯Έ μ νΈμ λμν μ μμ΅λλ€:
- ROC: $|z| > |a|$ $\implies$ $x[n] = a^n u[n]$ (μΈκ³Όμ , μ€λ₯Έμͺ½ μ νΈ)
- ROC: $|z| < |a|$ $\implies$ $x[n] = -a^n u[-n-1]$ (λ°μΈκ³Όμ , μΌμͺ½ μ νΈ)
μ΄ λλ¬Έμ ROCκ° νμμ μ λλ€: ROC μμ΄ $X(z)$ λ¨λ μΌλ‘λ $x[n]$μ μ μΌνκ² κ²°μ ν μ μμ΅λλ€.
def visualize_roc():
"""Visualize ROC for different signal types."""
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
theta = np.linspace(0, 2 * np.pi, 200)
# Case 1: Causal signal x[n] = 0.7^n u[n], ROC: |z| > 0.7
ax = axes[0]
# Unit circle
ax.plot(np.cos(theta), np.sin(theta), 'k-', linewidth=1)
# Pole at z = 0.7
ax.plot(0.7, 0, 'rx', markersize=12, markeredgewidth=2, label='Pole')
# ROC: |z| > 0.7 (shade exterior)
r_roc = 0.7
circle = plt.Circle((0, 0), r_roc, fill=True, color='lightblue',
alpha=0.5, label=f'ROC: |z| > {r_roc}')
ax.add_patch(circle)
ax.fill_between(np.cos(theta) * 2, np.sin(theta) * 2,
alpha=0.2, color='green')
ax.set_xlim(-2, 2)
ax.set_ylim(-2, 2)
ax.set_aspect('equal')
ax.set_title(r'Causal: $0.7^n u[n]$' + '\nROC: |z| > 0.7')
ax.axhline(0, color='gray', linewidth=0.5)
ax.axvline(0, color='gray', linewidth=0.5)
ax.legend(fontsize=8)
ax.grid(True, alpha=0.3)
# Case 2: Anti-causal signal, ROC: |z| < 0.7
ax = axes[1]
ax.plot(np.cos(theta), np.sin(theta), 'k-', linewidth=1)
ax.plot(0.7, 0, 'rx', markersize=12, markeredgewidth=2, label='Pole')
circle = plt.Circle((0, 0), r_roc, fill=True, color='lightgreen',
alpha=0.5, label=f'ROC: |z| < {r_roc}')
ax.add_patch(circle)
ax.set_xlim(-2, 2)
ax.set_ylim(-2, 2)
ax.set_aspect('equal')
ax.set_title(r'Anti-causal: $-0.7^n u[-n-1]$' + '\nROC: |z| < 0.7')
ax.axhline(0, color='gray', linewidth=0.5)
ax.axvline(0, color='gray', linewidth=0.5)
ax.legend(fontsize=8)
ax.grid(True, alpha=0.3)
# Case 3: Two-sided signal, ROC: annular ring
ax = axes[2]
ax.plot(np.cos(theta), np.sin(theta), 'k-', linewidth=1)
ax.plot(0.5, 0, 'rx', markersize=12, markeredgewidth=2, label='Poles')
ax.plot(1.5, 0, 'rx', markersize=12, markeredgewidth=2)
# ROC: 0.5 < |z| < 1.5
for r in [0.5, 1.5]:
ax.plot(r * np.cos(theta), r * np.sin(theta), 'b--', linewidth=1)
# Shade annular region
theta_fill = np.linspace(0, 2 * np.pi, 100)
r_inner, r_outer = 0.5, 1.5
ax.fill_between(
np.concatenate([r_inner * np.cos(theta_fill),
r_outer * np.cos(theta_fill[::-1])]),
np.concatenate([r_inner * np.sin(theta_fill),
r_outer * np.sin(theta_fill[::-1])]),
alpha=0.3, color='yellow', label='ROC: 0.5 < |z| < 1.5'
)
ax.set_xlim(-2, 2)
ax.set_ylim(-2, 2)
ax.set_aspect('equal')
ax.set_title('Two-sided signal\nROC: 0.5 < |z| < 1.5')
ax.axhline(0, color='gray', linewidth=0.5)
ax.axvline(0, color='gray', linewidth=0.5)
ax.legend(fontsize=8)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('roc_visualization.png', dpi=150)
plt.show()
visualize_roc()
3. Z λ³νμ μ±μ§(Properties of the Z-Transform)¶
3.1 μ νμ±(Linearity)¶
$$a \, x_1[n] + b \, x_2[n] \quad \xleftrightarrow{\mathcal{Z}} \quad a \, X_1(z) + b \, X_2(z)$$
ROC: μ μ΄λ $\text{ROC}_1 \cap \text{ROC}_2$ (κ·Ήμ -μμ μμκ° λ°μνλ©΄ λ ν΄ μ μμ).
3.2 μκ° μ΄λ(Time Shifting)¶
$$x[n - n_0] \quad \xleftrightarrow{\mathcal{Z}} \quad z^{-n_0} X(z)$$
ROC: $X(z)$μ λμΌ ($z = 0$ λλ $z = \infty$ μΆκ°/μ κ±° κ°λ₯).
μ§μ° μ°μ°μ $z^{-1}$μ λμ§νΈ μμ€ν μ κΈ°λ³Έ μμμ λλ€. ν μνμ μ§μ°μ $z^{-1}$ κ³±μ μΌλ‘ ννλ©λλ€.
3.3 z μμμμμ μ€μΌμΌλ§(Scaling in the z-Domain)¶
$$a^n x[n] \quad \xleftrightarrow{\mathcal{Z}} \quad X(z/a)$$
ROC: $|a| \cdot R^{-} < |z| < |a| \cdot R^{+}$ (ROCκ° $|a|$λ§νΌ μ€μΌμΌλ§λ¨).
3.4 μκ° μμ (Time Reversal)¶
$$x[-n] \quad \xleftrightarrow{\mathcal{Z}} \quad X(z^{-1})$$
ROC: $1/R^{+} < |z| < 1/R^{-}$ (ROCκ° μμ λ¨).
3.5 z μμμμμ λ―ΈλΆ(Differentiation in z-Domain)¶
$$n \, x[n] \quad \xleftrightarrow{\mathcal{Z}} \quad -z \frac{dX(z)}{dz}$$
$n \cdot a^n$ κ΄λ ¨ λ³ν λμΆμ μ μ©ν©λλ€.
3.6 컨볼루μ (Convolution)¶
$$x_1[n] * x_2[n] \quad \xleftrightarrow{\mathcal{Z}} \quad X_1(z) \cdot X_2(z)$$
ROC: μ μ΄λ $\text{ROC}_1 \cap \text{ROC}_2$.
μμ€ν λΆμμμ κ°μ₯ μ€μν μ±μ§μ λλ€: μκ° μμμ 컨볼루μ μ΄ z μμμμ κ³±μ μΌλ‘ λ³νλ©λλ€.
3.7 μ΄κΈ°κ° μ 리(Initial Value Theorem, μΈκ³Ό μ νΈ)¶
$$x[0] = \lim_{z \to \infty} X(z)$$
3.8 μ΅μ’ κ° μ 리(Final Value Theorem)¶
$(1 - z^{-1})X(z)$μ λͺ¨λ κ·Ήμ μ΄ λ¨μμ λ΄λΆμ μλ κ²½μ°:
$$\lim_{n \to \infty} x[n] = \lim_{z \to 1} (1 - z^{-1}) X(z)$$
3.9 μ±μ§ μμ½ν¶
| μ±μ§ | μκ° μμ | Z μμ | ROC |
|---|---|---|---|
| μ νμ± | $ax_1 + bx_2$ | $aX_1 + bX_2$ | $\supseteq R_1 \cap R_2$ |
| μκ° μ΄λ | $x[n-n_0]$ | $z^{-n_0}X(z)$ | $R$ (κ²½μ°μ λ°λΌ $\pm$ 0, $\infty$) |
| μ€μΌμΌλ§ | $a^n x[n]$ | $X(z/a)$ | $|a| \cdot R$ |
| μμ | $x[-n]$ | $X(1/z)$ | $1/R$ |
| λ―ΈλΆ | $nx[n]$ | $-z\frac{dX}{dz}$ | $R$ |
| 컨볼루μ | $x_1 * x_2$ | $X_1 X_2$ | $\supseteq R_1 \cap R_2$ |
| λμ | $\sum_{k=-\infty}^{n} x[k]$ | $\frac{X(z)}{1-z^{-1}}$ | $R \cap \{|z|>1\}$ |
def demonstrate_z_properties():
"""Numerically verify Z-transform properties."""
# Test signal: x[n] = 0.8^n * u[n], truncated to 100 samples
N = 100
a = 0.8
n = np.arange(N)
x = a ** n
# Evaluate Z-transforms on the unit circle
omega = np.linspace(-np.pi, np.pi, 512)
z = np.exp(1j * omega)
def zt_on_circle(signal, omega_vals):
"""Compute Z-transform on unit circle (= DTFT)."""
z_vals = np.exp(1j * omega_vals)
result = np.zeros(len(omega_vals), dtype=complex)
for k in range(len(signal)):
result += signal[k] * z_vals ** (-k)
return result
# Property 1: Time shift
m = 5
x_shifted = np.zeros(N + m)
x_shifted[m:m + N] = x
X_orig = zt_on_circle(x, omega)
X_shifted_direct = zt_on_circle(x_shifted, omega)
X_shifted_property = np.exp(-1j * omega * m) * X_orig
# Property 2: Convolution
h = 0.5 ** n # Another causal exponential
y_conv = np.convolve(x[:50], h[:50]) # Linear convolution
X_x = zt_on_circle(x[:50], omega)
H_z = zt_on_circle(h[:50], omega)
Y_product = X_x * H_z
Y_conv_direct = zt_on_circle(y_conv, omega)
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# Time shift verification
axes[0, 0].plot(omega / np.pi, np.abs(X_shifted_direct), 'b-',
linewidth=2, label='Direct')
axes[0, 0].plot(omega / np.pi, np.abs(X_shifted_property), 'r--',
linewidth=1, label='z^{-m} X(z)')
axes[0, 0].set_title(f'Time Shift Property (m={m}): Magnitude')
axes[0, 0].legend()
axes[0, 0].grid(True, alpha=0.3)
axes[0, 1].plot(omega / np.pi,
np.abs(X_shifted_direct - X_shifted_property), 'k-')
axes[0, 1].set_title(f'Time Shift Error')
axes[0, 1].set_ylabel('|Error|')
axes[0, 1].grid(True, alpha=0.3)
# Convolution verification
axes[1, 0].plot(omega / np.pi, np.abs(Y_conv_direct), 'b-',
linewidth=2, label='Z{x*h}')
axes[1, 0].plot(omega / np.pi, np.abs(Y_product), 'r--',
linewidth=1, label='X(z)H(z)')
axes[1, 0].set_title('Convolution Property: Magnitude')
axes[1, 0].legend()
axes[1, 0].grid(True, alpha=0.3)
axes[1, 1].plot(omega / np.pi, np.abs(Y_conv_direct - Y_product), 'k-')
axes[1, 1].set_title('Convolution Property Error')
axes[1, 1].set_ylabel('|Error|')
axes[1, 1].grid(True, alpha=0.3)
for ax in axes.flat:
ax.set_xlabel(r'$\omega / \pi$')
plt.tight_layout()
plt.savefig('z_properties.png', dpi=150)
plt.show()
demonstrate_z_properties()
4. μ Z λ³ν(Inverse Z-Transform)¶
4.1 νμμ μ μ¶
$$x[n] = \frac{1}{2\pi j} \oint_C X(z) \, z^{n-1} \, dz$$
μ¬κΈ°μ $C$λ ROC λ΄μμ μμ μ λλ¬μΈλ λ°μκ³ λ°©ν₯ κ²½λ‘μ λλ€.
μ€μ λ‘λ μΈ κ°μ§ λ°©λ²μ΄ μΌλ°μ μΌλ‘ μ¬μ©λ©λλ€:
4.2 λ°©λ² 1: λΆλΆ λΆμ μ κ°(Partial Fraction Expansion)¶
μ 리 ν¨μ $X(z) = B(z)/A(z)$μ λν΄, λ¨μ λΆμλ‘ λΆν΄ν©λλ€:
$$X(z) = \sum_i \frac{A_i}{1 - p_i z^{-1}} + \cdots$$
κ° νμ μλ €μ§ μ Z λ³νμ κ°μ§λ©°, ROCκ° κ° νμ΄ μΈκ³Όμ μΈμ§ λ°μΈκ³Όμ μΈμ§λ₯Ό κ²°μ ν©λλ€.
μμ:
$$X(z) = \frac{1}{(1 - 0.5z^{-1})(1 - 0.8z^{-1})}, \quad |z| > 0.8$$
λΆλΆ λΆμ μ κ°:
$$X(z) = \frac{A}{1 - 0.5z^{-1}} + \frac{B}{1 - 0.8z^{-1}}$$
νλ©΄: $A = \frac{-5}{3}$, $B = \frac{8}{3}$
ROCκ° $|z| > 0.8$μ΄λ―λ‘(λ κ·Ήμ λͺ¨λ ROC λ΄λΆ), λ ν λͺ¨λ μΈκ³Όμ :
$$x[n] = \left(-\frac{5}{3}(0.5)^n + \frac{8}{3}(0.8)^n\right) u[n]$$
def partial_fraction_inverse_z():
"""Inverse Z-transform via partial fraction expansion."""
# X(z) = 1 / ((1 - 0.5 z^{-1})(1 - 0.8 z^{-1}))
# Numerator: [1] (in z^{-1} form)
# Denominator: (1 - 0.5 z^{-1})(1 - 0.8 z^{-1})
# = 1 - 1.3 z^{-1} + 0.4 z^{-2}
# Using scipy.signal for partial fractions
# Express as H(z) = B(z)/A(z) in descending powers of z
# B(z) = 1
# A(z) = 1 - 1.3 z^{-1} + 0.4 z^{-2}
b = [1.0] # Numerator coefficients
a = [1.0, -1.3, 0.4] # Denominator coefficients
# Partial fraction expansion
# scipy uses z (not z^{-1}), so we need to be careful
# Convert to z-form: multiply num/den by z^2
b_z = [0, 0, 1] # z^0 (need to match length)
a_z = [1, -1.3, 0.4] # z^2 - 1.3z + 0.4
residues, poles, remainder = signal.residuez(b, a)
print("Partial Fraction Expansion")
print("=" * 50)
print(f"X(z) = 1 / ((1 - 0.5z^-1)(1 - 0.8z^-1))")
print(f"\nPoles: {poles}")
print(f"Residues: {residues}")
print(f"Remainder: {remainder}")
# Reconstruct x[n]
N = 30
n = np.arange(N)
# From partial fractions (causal, ROC: |z| > 0.8)
x_pf = np.zeros(N)
for r, p in zip(residues, poles):
x_pf += np.real(r * p ** n)
# Direct computation via scipy.signal (impulse response)
_, x_impulse = signal.dimpulse(signal.dlti(b, a, dt=1), n=N)
x_impulse = np.squeeze(x_impulse)
fig, ax = plt.subplots(figsize=(12, 5))
ax.stem(n, x_pf, linefmt='b-', markerfmt='bo', basefmt='k-',
label='Partial fractions')
ax.plot(n, x_impulse, 'rx', markersize=8, label='scipy dimpulse')
ax.set_title('Inverse Z-Transform via Partial Fractions')
ax.set_xlabel('n')
ax.set_ylabel('x[n]')
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('inverse_z_partial.png', dpi=150)
plt.show()
partial_fraction_inverse_z()
4.3 λ°©λ² 2: κΈ΄ λλμ (Long Division, λ©±κΈμ μ κ°)¶
$B(z^{-1})$μ $A(z^{-1})$λ‘ λλμ΄ $z^{-n}$μ κ³μλ₯Ό ꡬνλ©΄, μ΄ κ³μλ€μ΄ $x[n]$μ κ°μ΄ λ©λλ€.
μμ:
$$X(z) = \frac{1}{1 - 1.5z^{-1} + 0.5z^{-2}}$$
$z^{-1}$μμμ κΈ΄ λλμ :
$$\frac{1}{1 - 1.5z^{-1} + 0.5z^{-2}} = 1 + 1.5z^{-1} + 1.75z^{-2} + 1.875z^{-3} + \cdots$$
λ°λΌμ $x[0] = 1$, $x[1] = 1.5$, $x[2] = 1.75$, $x[3] = 1.875$, ...
def long_division_inverse_z():
"""Inverse Z-transform via long division."""
# X(z) = B(z^{-1}) / A(z^{-1})
b = np.array([1.0])
a = np.array([1.0, -1.5, 0.5])
N = 20
x = np.zeros(N)
# Long division algorithm
remainder = np.zeros(len(b) + N)
remainder[:len(b)] = b
for n in range(N):
x[n] = remainder[0] / a[0]
for k in range(len(a)):
if k < len(remainder):
remainder[k] -= x[n] * a[k]
remainder = np.roll(remainder, -1)
remainder[-1] = 0
# Verify with scipy
_, x_scipy = signal.dimpulse(signal.dlti(b, a, dt=1), n=N)
x_scipy = np.squeeze(x_scipy)
print("Long Division Inverse Z-Transform")
print("=" * 40)
print(f"X(z) = 1 / (1 - 1.5z^(-1) + 0.5z^(-2))")
print(f"\n{'n':>4s} | {'x[n] (long div)':>16s} | {'x[n] (scipy)':>14s}")
print("-" * 40)
for i in range(min(10, N)):
print(f"{i:4d} | {x[i]:16.6f} | {x_scipy[i]:14.6f}")
long_division_inverse_z()
4.4 λ°©λ² 3: κ²½λ‘ μ λΆ(Contour Integration, μ μ μ 리)¶
$$x[n] = \sum_{\text{poles } p_k \text{ inside } C} \text{Res}\left[X(z) z^{n-1}, p_k\right]$$
$z = p_k$μμμ λ¨μ κ·Ήμ μ λν΄:
$$\text{Res}\left[X(z)z^{n-1}, p_k\right] = \lim_{z \to p_k} (z - p_k) X(z) z^{n-1}$$
5. μ λ¬ ν¨μ H(z)¶
5.1 μ μ¶
μ°¨λΆ λ°©μ μμΌλ‘ κΈ°μ λλ LTI μμ€ν μ λν΄:
$$\sum_{k=0}^{N} a_k \, y[n-k] = \sum_{k=0}^{M} b_k \, x[n-k]$$
μ λ¬ ν¨μ(transfer function)λ:
$$\boxed{H(z) = \frac{Y(z)}{X(z)} = \frac{\sum_{k=0}^{M} b_k z^{-k}}{\sum_{k=0}^{N} a_k z^{-k}} = \frac{B(z)}{A(z)}}$$
z μμμμμ μΆλ ₯μ λ¨μν:
$$Y(z) = H(z) \cdot X(z)$$
5.2 μνμ€ μλ΅κ³Ό μ λ¬ ν¨μ¶
μνμ€ μλ΅(impulse response) $h[n]$μ $H(z)$μ μ Z λ³νμ λλ€:
$$h[n] = \mathcal{Z}^{-1}\{H(z)\}$$
$Y(z) = H(z) X(z)$μ΄κ³ z μμμ κ³±μ μ΄ μκ° μμμ 컨볼루μ μ λμνλ―λ‘:
$$y[n] = h[n] * x[n] = \sum_{k=-\infty}^{\infty} h[k] \, x[n-k]$$
5.3 μμ: 1μ°¨ μμ€ν ¶
$$y[n] = 0.9 \, y[n-1] + x[n]$$
μ λ¬ ν¨μ:
$$H(z) = \frac{1}{1 - 0.9z^{-1}} = \frac{z}{z - 0.9}$$
- $z = 0.9$μμ κ·Ήμ νλ
- $z = 0$μμ μμ νλ ($z^{-1}$ νμμμμ μλͺ ν μμ )
def transfer_function_example():
"""Analyze a first-order digital system."""
# y[n] = 0.9 * y[n-1] + x[n]
# H(z) = 1 / (1 - 0.9 z^{-1})
b = [1.0]
a = [1.0, -0.9]
# Create discrete-time system
sys = signal.dlti(b, a, dt=1)
# Impulse response
t_imp, h = signal.dimpulse(sys, n=40)
h = np.squeeze(h)
# Step response
t_step, s = signal.dstep(sys, n=40)
s = np.squeeze(s)
# Frequency response
w, H = signal.freqz(b, a, worN=1024)
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# Impulse response
axes[0, 0].stem(np.arange(len(h)), h, linefmt='b-', markerfmt='bo',
basefmt='k-')
axes[0, 0].set_title('Impulse Response h[n]')
axes[0, 0].set_xlabel('n')
axes[0, 0].set_ylabel('h[n]')
axes[0, 0].grid(True, alpha=0.3)
# Step response
axes[0, 1].stem(np.arange(len(s)), s, linefmt='r-', markerfmt='ro',
basefmt='k-')
axes[0, 1].set_title('Step Response')
axes[0, 1].set_xlabel('n')
axes[0, 1].set_ylabel('y[n]')
axes[0, 1].grid(True, alpha=0.3)
# Magnitude response
axes[1, 0].plot(w / np.pi, 20 * np.log10(np.abs(H)), 'b-', linewidth=2)
axes[1, 0].set_title('Magnitude Response |H(e^jw)|')
axes[1, 0].set_xlabel(r'$\omega / \pi$')
axes[1, 0].set_ylabel('Magnitude (dB)')
axes[1, 0].grid(True, alpha=0.3)
# Phase response
axes[1, 1].plot(w / np.pi, np.unwrap(np.angle(H)), 'r-', linewidth=2)
axes[1, 1].set_title('Phase Response')
axes[1, 1].set_xlabel(r'$\omega / \pi$')
axes[1, 1].set_ylabel('Phase (radians)')
axes[1, 1].grid(True, alpha=0.3)
plt.suptitle(r'System: $y[n] = 0.9\,y[n-1] + x[n]$', fontsize=14)
plt.tight_layout()
plt.savefig('transfer_function.png', dpi=150)
plt.show()
transfer_function_example()
6. z νλ©΄μ κ·Ήμ κ³Ό μμ (Poles and Zeros in the z-Plane)¶
6.1 μ μ¶
μ 리(rational) μ λ¬ ν¨μμ λν΄:
$$H(z) = \frac{b_0 + b_1 z^{-1} + \cdots + b_M z^{-M}}{a_0 + a_1 z^{-1} + \cdots + a_N z^{-N}} = G \cdot \frac{\prod_{k=1}^{M}(z - z_k)}{\prod_{k=1}^{N}(z - p_k)}$$
- μμ (zeros) ($z_k$): $H(z) = 0$μ΄ λλ $z$ κ° (λΆμμ κ·Ό)
- κ·Ήμ (poles) ($p_k$): $H(z) \to \infty$κ° λλ $z$ κ° (λΆλͺ¨μ κ·Ό)
- $G$: μ΄λ μΈμ(gain factor)
6.2 κ·Ήμ -μμ λμ(Pole-Zero Plot)¶
def pole_zero_analysis():
"""Analyze a system using pole-zero plots."""
# Second-order system (resonator)
# H(z) = 1 / (1 - 2r cos(w0) z^{-1} + r^2 z^{-2})
r = 0.9 # Pole radius (< 1 for stability)
w0 = np.pi / 4 # Resonant frequency (pi/4 = fs/8)
b = [1.0]
a = [1.0, -2 * r * np.cos(w0), r ** 2]
# Find poles and zeros
zeros = np.roots(b)
poles = np.roots(a)
print("Pole-Zero Analysis")
print("=" * 50)
print(f"Zeros: {zeros}")
print(f"Poles: {poles}")
print(f"Pole magnitudes: {np.abs(poles)}")
print(f"Pole angles: {np.angle(poles) / np.pi} * pi")
print(f"Stable: {all(np.abs(poles) < 1)}")
# Pole-zero plot and frequency response
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
# Pole-zero plot
ax = axes[0]
theta = np.linspace(0, 2 * np.pi, 200)
ax.plot(np.cos(theta), np.sin(theta), 'k-', linewidth=1,
label='Unit circle')
# Plot zeros
if len(zeros) > 0:
ax.plot(np.real(zeros), np.imag(zeros), 'bo', markersize=10,
label=f'Zeros ({len(zeros)})')
# Plot poles
ax.plot(np.real(poles), np.imag(poles), 'rx', markersize=12,
markeredgewidth=2, label=f'Poles ({len(poles)})')
ax.set_xlim(-1.5, 1.5)
ax.set_ylim(-1.5, 1.5)
ax.set_aspect('equal')
ax.set_title('Pole-Zero Plot')
ax.set_xlabel('Real')
ax.set_ylabel('Imaginary')
ax.axhline(0, color='gray', linewidth=0.5)
ax.axvline(0, color='gray', linewidth=0.5)
ax.legend()
ax.grid(True, alpha=0.3)
# Frequency response
ax = axes[1]
w, H = signal.freqz(b, a, worN=1024)
ax.plot(w / np.pi, 20 * np.log10(np.abs(H)), 'b-', linewidth=2)
ax.axvline(w0 / np.pi, color='red', linestyle='--', alpha=0.5,
label=f'Resonant freq = {w0/np.pi:.2f}pi')
ax.set_title('Magnitude Response')
ax.set_xlabel(r'$\omega / \pi$')
ax.set_ylabel('Magnitude (dB)')
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('pole_zero.png', dpi=150)
plt.show()
pole_zero_analysis()
6.3 κ·Ήμ κ³Ό μμ μμΉμ ν¨κ³Ό¶
| μμΉ | ν¨κ³Ό |
|---|---|
| λ¨μμ κ·Όμ²μ κ·Ήμ | κ·Ήμ κ°λμμμ μ£Όνμ μλ΅μ λ μΉ΄λ‘μ΄ νΌν¬ |
| λ¨μμ μμ μμ | μμ κ°λμμ μ(null) μ΄λ |
| λ¨μμ λ΄λΆμ κ·Ήμ | μμ μ , κ°μνλ μνμ€ μλ΅ |
| λ¨μμ μΈλΆμ κ·Ήμ | λΆμμ , μ¦κ°νλ μνμ€ μλ΅ |
| λ¨μμ μμ κ·Ήμ | κ²½κ³ μμ , μ§μμ μ§λ |
| μμ μ κ·Ήμ | μμ μ§μ° (FIR λμ) |
| μΌ€λ 볡μ κ·Ήμ | κ³΅μ§ (μ§λμ κ°μ) |
6.4 κ·Ήμ -μμ μνΈμμ© νμ¶
def pole_zero_effects():
"""Show how pole/zero locations affect frequency response."""
fig, axes = plt.subplots(3, 2, figsize=(14, 14))
configurations = [
{
'title': 'Lowpass (pole near z=1)',
'b': [1.0], 'a': [1.0, -0.9]
},
{
'title': 'Highpass (pole near z=-1)',
'b': [1.0, -1.0], 'a': [1.0, -0.9]
},
{
'title': 'Bandpass (complex conjugate poles)',
'b': [1.0, 0, -1.0],
'a': [1.0, -2 * 0.9 * np.cos(np.pi / 4), 0.81]
},
{
'title': 'Notch (zeros on unit circle)',
'b': [1.0, -2 * np.cos(np.pi / 4), 1.0],
'a': [1.0, -2 * 0.9 * np.cos(np.pi / 4), 0.81]
},
{
'title': 'All-pass (poles/zeros reciprocal)',
'b': [0.5, 1.0],
'a': [1.0, 0.5]
},
{
'title': 'Comb filter (pole at z^N=r^N)',
'b': [1.0],
'a': np.concatenate([[1.0], np.zeros(7), [-0.8]])
},
]
for ax_row, config in zip(axes.flat, configurations):
b, a = np.array(config['b']), np.array(config['a'])
w, H = signal.freqz(b, a, worN=1024)
ax_row.plot(w / np.pi, 20 * np.log10(np.abs(H) + 1e-12),
'b-', linewidth=2)
ax_row.set_title(config['title'])
ax_row.set_xlabel(r'$\omega / \pi$')
ax_row.set_ylabel('Magnitude (dB)')
ax_row.set_ylim(-40, 30)
ax_row.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('pole_zero_effects.png', dpi=150)
plt.show()
pole_zero_effects()
7. μμ μ± λΆμ(Stability Analysis)¶
7.1 BIBO μμ μ±(BIBO Stability)¶
μΈκ³Όμ LTI μμ€ν μ΄ μ κ³ μ λ ₯-μ κ³ μΆλ ₯(BIBO, Bounded-Input Bounded-Output) μμ μΈ νμμΆ©λΆμ‘°κ±΄:
$$\sum_{n=0}^{\infty} |h[n]| < \infty$$
Z λ³ν κ΄μ μμ, μΈκ³Όμ μμ€ν μ΄ BIBO μμ μΈ νμμΆ©λΆμ‘°κ±΄:
$$\boxed{\text{$H(z)$μ λͺ¨λ κ·Ήμ μ΄ λ¨μμ μ격ν λ΄λΆμ μμΉ: } |p_k| < 1 \, \forall k}$$
7.2 μμ μ± μ‘°κ±΄¶
| κ·Ήμ μμΉ | μμ μ± | μνμ€ μλ΅ |
|---|---|---|
| λͺ¨λ $|p_k| < 1$ | μμ | μμΌλ‘ μλ ΄ |
| μΌλΆ $|p_k| = 1$ (λ¨μ) | κ²½κ³ μμ | μ κ³, λΉμλ©Έ |
| μμ $|p_k| > 1$ | λΆμμ | 무ν μ¦κ° |
| $|p_k| = 1$ (μ€λ³΅) | λΆμμ | $n^{m-1}$μ²λΌ μ¦κ° |
7.3 μμ μ± κ²μ¬¶
def stability_analysis():
"""Analyze stability of several systems."""
systems = [
{
'name': 'Stable: y[n] = 0.5*y[n-1] + x[n]',
'b': [1.0], 'a': [1.0, -0.5]
},
{
'name': 'Marginally stable: y[n] = y[n-1] + x[n]',
'b': [1.0], 'a': [1.0, -1.0]
},
{
'name': 'Unstable: y[n] = 1.1*y[n-1] + x[n]',
'b': [1.0], 'a': [1.0, -1.1]
},
{
'name': 'Stable oscillator: r=0.9, w0=pi/4',
'b': [1.0],
'a': [1.0, -2 * 0.9 * np.cos(np.pi / 4), 0.81]
},
{
'name': 'Unstable oscillator: r=1.05, w0=pi/4',
'b': [1.0],
'a': [1.0, -2 * 1.05 * np.cos(np.pi / 4), 1.05**2]
},
]
fig, axes = plt.subplots(len(systems), 2, figsize=(14, 3 * len(systems)))
for i, sys_info in enumerate(systems):
b, a = np.array(sys_info['b']), np.array(sys_info['a'])
poles = np.roots(a)
max_pole_mag = np.max(np.abs(poles))
stability = "STABLE" if max_pole_mag < 1 else \
"MARGINALLY STABLE" if np.isclose(max_pole_mag, 1) else \
"UNSTABLE"
# Pole-zero plot
ax_pz = axes[i, 0]
theta = np.linspace(0, 2 * np.pi, 200)
ax_pz.plot(np.cos(theta), np.sin(theta), 'k-', linewidth=0.5)
ax_pz.plot(np.real(poles), np.imag(poles), 'rx', markersize=10,
markeredgewidth=2)
ax_pz.set_xlim(-1.5, 1.5)
ax_pz.set_ylim(-1.5, 1.5)
ax_pz.set_aspect('equal')
ax_pz.set_title(f'{sys_info["name"]}\n[{stability}] max|pole|={max_pole_mag:.3f}')
ax_pz.axhline(0, color='gray', linewidth=0.5)
ax_pz.axvline(0, color='gray', linewidth=0.5)
ax_pz.grid(True, alpha=0.3)
# Impulse response
ax_ir = axes[i, 1]
N = 40
h = np.zeros(N)
h[0] = b[0] / a[0]
for n in range(1, N):
h[n] = (b[n] if n < len(b) else 0)
for k in range(1, min(n + 1, len(a))):
h[n] -= a[k] * h[n - k]
h[n] /= a[0]
color = 'green' if stability == "STABLE" else \
'orange' if stability == "MARGINALLY STABLE" else 'red'
ax_ir.stem(np.arange(N), h, linefmt=f'{color[0]}-',
markerfmt=f'{color[0]}o', basefmt='k-')
ax_ir.set_title(f'Impulse Response')
ax_ir.set_xlabel('n')
ax_ir.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('stability.png', dpi=150)
plt.show()
stability_analysis()
7.4 주리 μμ μ± κ²μ (Jury Stability Test)¶
λ€νμ $A(z) = a_0 z^N + a_1 z^{N-1} + \cdots + a_N$μ λν΄, 주리 μμ μ± κ²μ μ λͺ μμ μΌλ‘ κ·Όμ κ³μ°νμ§ μκ³ λ λͺ¨λ κ·Όμ΄ λ¨μμ λ΄λΆμ μλμ§ νμμΆ©λΆμ‘°κ±΄μ μ 곡ν©λλ€.
νμ 쑰건 (λΉ λ₯Έ νμΈ): 1. $A(1) > 0$ 2. $(-1)^N A(-1) > 0$ 3. $|a_N| < |a_0|$
def jury_test(a):
"""Perform Jury stability test on polynomial coefficients."""
a = np.array(a, dtype=float)
N = len(a) - 1 # Polynomial order
print("Jury Stability Test")
print("=" * 50)
print(f"Polynomial order: {N}")
print(f"Coefficients: {a}")
# Necessary conditions
A_1 = np.polyval(a, 1)
A_neg1 = np.polyval(a, -1)
cond1 = A_1 > 0
cond2 = ((-1) ** N * A_neg1) > 0
cond3 = abs(a[-1]) < abs(a[0])
print(f"\nNecessary conditions:")
print(f" A(1) = {A_1:.4f} > 0 ? {cond1}")
print(f" (-1)^N * A(-1) = {(-1)**N * A_neg1:.4f} > 0 ? {cond2}")
print(f" |a_N| = {abs(a[-1]):.4f} < |a_0| = {abs(a[0]):.4f} ? {cond3}")
if not (cond1 and cond2 and cond3):
print("\n => UNSTABLE (necessary condition violated)")
return False
# Verify with actual roots
roots = np.roots(a)
max_mag = np.max(np.abs(roots))
stable = max_mag < 1
print(f"\nVerification: max|root| = {max_mag:.6f}")
print(f"System is {'STABLE' if stable else 'UNSTABLE'}")
return stable
# Test examples
print("System 1:")
jury_test([1, -1.3, 0.4]) # Stable (poles at 0.5, 0.8)
print("\nSystem 2:")
jury_test([1, -2.0, 1.1]) # Unstable
8. DTFT λ° λΌνλΌμ€ λ³νκ³Όμ κ΄κ³¶
8.1 Z λ³νκ³Ό DTFT¶
DTFTλ λ¨μμ μμμ νκ°ν Z λ³νμ λλ€:
$$\boxed{X(e^{j\omega}) = X(z)\big|_{z=e^{j\omega}}}$$
μ΄ κ΄κ³λ $X(z)$μ ROCκ° λ¨μμμ ν¬ν¨νλ κ²½μ°μ μ±λ¦½ν©λλ€.
κ²°κ³Ό: μ΄μ° μκ° μμ€ν μ μ£Όνμ μλ΅μ:
$$H(e^{j\omega}) = H(z)\big|_{z=e^{j\omega}}$$
8.2 Z λ³νκ³Ό λΌνλΌμ€ λ³ν¶
μνλ§λ μ νΈ $x_s(t) = \sum_n x(nT_s) \delta(t - nT_s)$μ λν΄, λΌνλΌμ€ λ³ν $X_s(s)$μ Z λ³ν $X(z)$ μ¬μ΄μ κ΄κ³λ:
$$\boxed{z = e^{sT_s}}$$
λλ λλ±νκ²:
$$s = \frac{1}{T_s} \ln z$$
μ΄λ λ€μκ³Ό κ°μ΄ λμν©λλ€: - s νλ©΄μ μ’λ°λ©΄ ($\text{Re}(s) < 0$) $\to$ λ¨μμ λ΄λΆ ($|z| < 1$) - νμμΆ ($\text{Re}(s) = 0$) $\to$ λ¨μμ ($|z| = 1$) - s νλ©΄μ μ°λ°λ©΄ ($\text{Re}(s) > 0$) $\to$ λ¨μμ μΈλΆ ($|z| > 1$)
8.3 H(z)λ‘λΆν°μ μ£Όνμ μλ΅¶
νΉμ 물리μ μ£Όνμ $f$ (Hz)μμμ μ£Όνμ μλ΅ κ³μ°:
$$\omega = 2\pi f / f_s \quad \text{(μ κ·νλ λμ§νΈ μ£Όνμ)}$$ $$H(e^{j\omega}) = H(z)\big|_{z = e^{j2\pi f/f_s}}$$
def frequency_response_from_hz():
"""Compute frequency response from H(z) by evaluating on unit circle."""
# System: H(z) = (1 + z^{-1}) / (1 - 0.5 z^{-1})
b = [1.0, 1.0]
a = [1.0, -0.5]
fs = 8000 # Hz
# Method 1: Using scipy.signal.freqz
w, H_scipy = signal.freqz(b, a, worN=1024)
f_hz = w * fs / (2 * np.pi)
# Method 2: Direct evaluation on unit circle
omega = np.linspace(0, np.pi, 1024)
z = np.exp(1j * omega)
H_direct = (b[0] + b[1] * z**(-1)) / (a[0] + a[1] * z**(-1))
fig, axes = plt.subplots(2, 1, figsize=(12, 8))
axes[0].plot(f_hz, 20 * np.log10(np.abs(H_scipy)), 'b-',
linewidth=2, label='scipy.signal.freqz')
axes[0].plot(f_hz, 20 * np.log10(np.abs(H_direct)), 'r--',
linewidth=1, label='Direct evaluation')
axes[0].set_title(r'Frequency Response: $H(z) = (1+z^{-1})/(1-0.5z^{-1})$')
axes[0].set_xlabel('Frequency (Hz)')
axes[0].set_ylabel('Magnitude (dB)')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
axes[1].plot(f_hz, np.unwrap(np.angle(H_scipy)) * 180 / np.pi, 'b-',
linewidth=2, label='scipy.signal.freqz')
axes[1].plot(f_hz, np.unwrap(np.angle(H_direct)) * 180 / np.pi, 'r--',
linewidth=1, label='Direct evaluation')
axes[1].set_xlabel('Frequency (Hz)')
axes[1].set_ylabel('Phase (degrees)')
axes[1].legend()
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('freq_response_hz.png', dpi=150)
plt.show()
frequency_response_from_hz()
8.4 s νλ©΄μμ z νλ©΄μΌλ‘μ μ¬μ¶
def s_to_z_mapping():
"""Visualize the mapping from s-plane to z-plane."""
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
Ts = 1.0 # Normalized sampling period
# s-plane
ax = axes[0]
# Stability boundary (imaginary axis)
sigma = np.linspace(-3, 3, 100)
omega_s = np.linspace(-np.pi / Ts, np.pi / Ts, 100)
ax.axvline(0, color='red', linewidth=2, label='Stability boundary')
ax.fill_betweenx([-4, 4], -4, 0, alpha=0.1, color='green',
label='Stable region')
ax.fill_betweenx([-4, 4], 0, 4, alpha=0.1, color='red',
label='Unstable region')
# Constant sigma lines
for sig in [-2, -1, -0.5, 0.5, 1, 2]:
ax.axvline(sig, color='gray', linestyle=':', alpha=0.3)
# Constant omega lines
for om in np.linspace(-3, 3, 7):
ax.axhline(om, color='gray', linestyle=':', alpha=0.3)
ax.set_xlim(-3, 3)
ax.set_ylim(-4, 4)
ax.set_title('s-Plane')
ax.set_xlabel(r'$\sigma$ (Real)')
ax.set_ylabel(r'$j\omega$ (Imaginary)')
ax.legend(fontsize=8)
ax.grid(True, alpha=0.3)
# z-plane
ax = axes[1]
theta = np.linspace(0, 2 * np.pi, 200)
ax.plot(np.cos(theta), np.sin(theta), 'r-', linewidth=2,
label='Unit circle (stability)')
# Map constant sigma lines
for sig in [-2, -1, -0.5, 0, 0.5, 1, 2]:
r = np.exp(sig * Ts)
ax.plot(r * np.cos(theta), r * np.sin(theta), '--',
alpha=0.4, label=f'sigma={sig}' if sig in [-1, 0, 1] else '')
ax.fill_between(np.cos(theta), np.sin(theta), alpha=0.1, color='green')
ax.set_xlim(-3, 3)
ax.set_ylim(-3, 3)
ax.set_aspect('equal')
ax.set_title(r'z-Plane ($z = e^{sT_s}$)')
ax.set_xlabel('Real')
ax.set_ylabel('Imaginary')
ax.axhline(0, color='gray', linewidth=0.5)
ax.axvline(0, color='gray', linewidth=0.5)
ax.legend(fontsize=8)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('s_to_z_mapping.png', dpi=150)
plt.show()
s_to_z_mapping()
9. μ£Όνμ μλ΅μ κΈ°ννμ ν΄μ¶
9.1 λ²‘ν° κ³±μΌλ‘μμ μ£Όνμ μλ΅¶
μ£Όνμ $\omega$μμμ μ£Όνμ μλ΅μ κΈ°ννμ μΌλ‘ κ³μ°ν μ μμ΅λλ€:
$$|H(e^{j\omega})| = |G| \cdot \frac{\prod_{k=1}^{M} |e^{j\omega} - z_k|}{\prod_{k=1}^{N} |e^{j\omega} - p_k|}$$
$$\angle H(e^{j\omega}) = \angle G + \sum_{k=1}^{M} \angle(e^{j\omega} - z_k) - \sum_{k=1}^{N} \angle(e^{j\omega} - p_k)$$
$\omega$κ° $0$μμ $\pi$κΉμ§ λ³νν λ, μ $e^{j\omega}$λ λ¨μμμ μλ°λΆλ₯Ό λ°λΌ μμ§μ λλ€. ν¬κΈ°λ μμ κΉμ§μ 거리μ κ³±μ κ·Ήμ κΉμ§μ 거리μ κ³±μΌλ‘ λλ κ°μ λλ€.
def geometric_frequency_response():
"""Visualize the geometric interpretation of frequency response."""
# System with poles and zeros
zeros = [0.5 + 0.5j, 0.5 - 0.5j]
poles = [0.8 * np.exp(1j * np.pi / 3), 0.8 * np.exp(-1j * np.pi / 3)]
# Build transfer function from poles and zeros
b = np.real(np.poly(zeros))
a = np.real(np.poly(poles))
# Animate for a specific frequency
omega_target = np.pi / 3 # Target frequency
z_point = np.exp(1j * omega_target)
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
# Pole-zero plot with vectors
ax = axes[0]
theta = np.linspace(0, 2 * np.pi, 200)
ax.plot(np.cos(theta), np.sin(theta), 'k-', linewidth=0.5)
# Zeros
for z in zeros:
ax.plot(np.real(z), np.imag(z), 'bo', markersize=10)
# Vector from zero to point on unit circle
ax.annotate('', xy=(np.real(z_point), np.imag(z_point)),
xytext=(np.real(z), np.imag(z)),
arrowprops=dict(arrowstyle='->', color='blue', lw=1.5))
# Poles
for p in poles:
ax.plot(np.real(p), np.imag(p), 'rx', markersize=12, markeredgewidth=2)
ax.annotate('', xy=(np.real(z_point), np.imag(z_point)),
xytext=(np.real(p), np.imag(p)),
arrowprops=dict(arrowstyle='->', color='red', lw=1.5))
# Point on unit circle
ax.plot(np.real(z_point), np.imag(z_point), 'g*', markersize=15,
label=f'e^(j*{omega_target/np.pi:.2f}*pi)')
ax.set_xlim(-1.5, 1.5)
ax.set_ylim(-1.5, 1.5)
ax.set_aspect('equal')
ax.set_title(f'Geometric Vectors at omega = {omega_target/np.pi:.2f}*pi')
ax.legend()
ax.grid(True, alpha=0.3)
# Frequency response
ax = axes[1]
w, H = signal.freqz(b, a, worN=1024)
ax.plot(w / np.pi, 20 * np.log10(np.abs(H) + 1e-12), 'b-', linewidth=2)
ax.axvline(omega_target / np.pi, color='green', linestyle='--',
linewidth=2, label=f'omega = {omega_target/np.pi:.2f}*pi')
H_at_target = np.polyval(b, z_point) / np.polyval(a, z_point)
ax.plot(omega_target / np.pi, 20 * np.log10(np.abs(H_at_target)),
'g*', markersize=15)
ax.set_title('Magnitude Response')
ax.set_xlabel(r'$\omega / \pi$')
ax.set_ylabel('Magnitude (dB)')
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('geometric_freq_response.png', dpi=150)
plt.show()
geometric_frequency_response()
10. μ’ ν© μμ: μμ€ν λΆμ νμ΄νλΌμΈ¶
def complete_system_analysis():
"""Complete analysis of a digital system from difference equation to
frequency response."""
print("Complete System Analysis")
print("=" * 60)
# Difference equation:
# y[n] - 1.2y[n-1] + 0.72y[n-2] = x[n] - 0.5x[n-1]
b = [1.0, -0.5]
a = [1.0, -1.2, 0.72]
# 1. Transfer function
print("\n1. Transfer function:")
print(f" H(z) = ({b[0]} + {b[1]}z^-1) / "
f"({a[0]} + {a[1]}z^-1 + {a[2]}z^-2)")
# 2. Poles and zeros
zeros = np.roots(b)
poles = np.roots(a)
print(f"\n2. Zeros: {zeros}")
print(f" Poles: {poles}")
print(f" Pole magnitudes: {np.abs(poles)}")
print(f" Pole angles: {np.angle(poles) * 180 / np.pi} degrees")
# 3. Stability
stable = all(np.abs(poles) < 1)
print(f"\n3. Stability: {'STABLE' if stable else 'UNSTABLE'}")
print(f" Max |pole| = {np.max(np.abs(poles)):.4f}")
# 4. Partial fraction expansion
r, p, k = signal.residuez(b, a)
print(f"\n4. Partial fractions:")
print(f" Residues: {r}")
print(f" Poles: {p}")
print(f" Direct term: {k}")
# 5. Impulse response (first 50 samples)
_, h = signal.dimpulse(signal.dlti(b, a, dt=1), n=50)
h = np.squeeze(h)
# 6. Frequency response
w, H = signal.freqz(b, a, worN=1024)
# Plot everything
fig = plt.figure(figsize=(16, 12))
gs = fig.add_gridspec(3, 2)
# Pole-zero plot
ax1 = fig.add_subplot(gs[0, 0])
theta = np.linspace(0, 2 * np.pi, 200)
ax1.plot(np.cos(theta), np.sin(theta), 'k-', linewidth=0.5)
ax1.plot(np.real(zeros), np.imag(zeros), 'bo', markersize=10, label='Zeros')
ax1.plot(np.real(poles), np.imag(poles), 'rx', markersize=12,
markeredgewidth=2, label='Poles')
ax1.set_xlim(-1.5, 1.5)
ax1.set_ylim(-1.5, 1.5)
ax1.set_aspect('equal')
ax1.set_title('Pole-Zero Plot')
ax1.legend()
ax1.grid(True, alpha=0.3)
# Impulse response
ax2 = fig.add_subplot(gs[0, 1])
ax2.stem(np.arange(len(h)), h, linefmt='b-', markerfmt='bo', basefmt='k-')
ax2.set_title('Impulse Response h[n]')
ax2.set_xlabel('n')
ax2.grid(True, alpha=0.3)
# Magnitude response
ax3 = fig.add_subplot(gs[1, 0])
ax3.plot(w / np.pi, 20 * np.log10(np.abs(H) + 1e-12), 'b-', linewidth=2)
ax3.set_title('Magnitude Response')
ax3.set_xlabel(r'$\omega / \pi$')
ax3.set_ylabel('dB')
ax3.grid(True, alpha=0.3)
# Phase response
ax4 = fig.add_subplot(gs[1, 1])
ax4.plot(w / np.pi, np.unwrap(np.angle(H)) * 180 / np.pi, 'r-',
linewidth=2)
ax4.set_title('Phase Response')
ax4.set_xlabel(r'$\omega / \pi$')
ax4.set_ylabel('Degrees')
ax4.grid(True, alpha=0.3)
# Group delay
ax5 = fig.add_subplot(gs[2, 0])
_, gd = signal.group_delay((b, a), w=1024)
ax5.plot(w / np.pi, gd, 'g-', linewidth=2)
ax5.set_title('Group Delay')
ax5.set_xlabel(r'$\omega / \pi$')
ax5.set_ylabel('Samples')
ax5.grid(True, alpha=0.3)
# Step response
ax6 = fig.add_subplot(gs[2, 1])
_, s = signal.dstep(signal.dlti(b, a, dt=1), n=50)
s = np.squeeze(s)
ax6.stem(np.arange(len(s)), s, linefmt='m-', markerfmt='mo', basefmt='k-')
ax6.set_title('Step Response')
ax6.set_xlabel('n')
ax6.grid(True, alpha=0.3)
plt.suptitle(r'System: $y[n] - 1.2y[n-1] + 0.72y[n-2] = x[n] - 0.5x[n-1]$',
fontsize=14)
plt.tight_layout()
plt.savefig('complete_analysis.png', dpi=150)
plt.show()
complete_system_analysis()
11. μμ½¶
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Z λ³ν(Z-Transform) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β μ μ: β
β X(z) = Ξ£ x[n] z^{-n} z = r * e^{jΟ} β
β β
β ROC (μλ ΄ μμ): β
β - μ νΈλ₯Ό μ μΌνκ² κ²°μ (κ°μ X(z), λ€λ₯Έ ROC β β
β λ€λ₯Έ x[n]) β
β - μΈκ³Όμ : |z| > Rβ» (μ μΈλΆ) β
β - λ°μΈκ³Όμ : |z| < RβΊ (μ λ΄λΆ) β
β - μλ°©ν₯: Rβ» < |z| < RβΊ (νν κ³ λ¦¬) β
β β
β μ£Όμ μ±μ§: β
β - μκ° μ΄λ: x[n-m] β z^{-m} X(z) β
β - 컨볼루μ
: xβ*xβ β Xβ(z)Β·Xβ(z) β
β - z^{-1} = ν μν μ§μ° β
β β
β μ λ¬ ν¨μ: β
β H(z) = Y(z)/X(z) = B(z)/A(z) β
β κ·Ήμ β λΆλͺ¨μ κ·Ό β
β μμ β λΆμμ κ·Ό β
β β
β μμ μ± (μΈκ³Ό μμ€ν
): β
β λͺ¨λ κ·Ήμ μ΄ λ¨μμ λ΄λΆ β BIBO μμ β
β β
β κ΄κ³: β
β λ¨μμ μμ Z λ³ν = DTFT: X(e^{jΟ}) = X(z)|_{z=e^{jΟ}} β
β z = e^{sTs}λ‘ Z λ³νκ³Ό λΌνλΌμ€ λ³ν μ°κ²° β
β β
β μλ³ν λ°©λ²: β
β 1. λΆλΆ λΆμ β μλ €μ§ μ β
β 2. κΈ΄ λλμ
β λ©±κΈμ β
β 3. κ²½λ‘ μ λΆ β μ μ μ 리 β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
12. μ°μ΅ λ¬Έμ ¶
μ°μ΅ λ¬Έμ 1: Z λ³ν κ³μ°¶
κ°κ°μ λν΄ Z λ³νμ κ³μ°νκ³ ROCλ₯Ό λͺ μνμμ€:
(a) $x[n] = (0.5)^n u[n] + (0.8)^n u[n]$
(b) $x[n] = (0.6)^n u[n] - 2(0.6)^n u[n-3]$
(c) $x[n] = n(0.9)^n u[n]$
(d) $x[n] = (0.7)^{|n|}$ (μλ°©ν₯)
μ°μ΅ λ¬Έμ 2: μ Z λ³ν¶
λΆλΆ λΆμλ₯Ό μ¬μ©νμ¬ κ°κ°μ λν $x[n]$μ ꡬνμμ€:
(a) $X(z) = \frac{z}{(z-0.5)(z-0.8)}, \quad |z| > 0.8$
(b) $X(z) = \frac{z}{(z-0.5)(z-0.8)}, \quad |z| < 0.5$
(c) $X(z) = \frac{z^2}{(z-0.5)(z-0.8)}, \quad |z| > 0.8$
(d) $X(z) = \frac{1+2z^{-1}}{1-z^{-1}+0.5z^{-2}}, \quad |z| > 0.707$
μ°μ΅ λ¬Έμ 3: μμ€ν λΆμ¶
λ€μ μ°¨λΆ λ°©μ μμ΄ μ£Όμ΄μ‘μ λ: $y[n] = 0.8y[n-1] - 0.64y[n-2] + x[n] + x[n-1]$
(a) $H(z)$λ₯Ό ꡬνκ³ κ·Ήμ -μμ λμμ μ€μΌμΉνμμ€.
(b) μμ€ν μ΄ μμ μ μΈκ°? μ΄μ λ₯Ό μ€λͺ νμμ€.
(c) λΆλΆ λΆμλ₯Ό μ¬μ©νμ¬ μνμ€ μλ΅ $h[n]$μ ꡬνμμ€.
(d) μ£Όνμ μλ΅(ν¬κΈ° λ° μμ)μ κ³μ°νκ³ λμννμμ€.
(e) μ΄ μμ€ν μ μ μ ν΅κ³Ό, κ³ μ ν΅κ³Ό, λμ ν΅κ³Ό, λμ μ μ§ νν° μ€ μ΄λ κ²μΈκ°?
μ°μ΅ λ¬Έμ 4: μμ μ± νμ ¶
κ·Όμ μ§μ κ³μ°νμ§ μκ³ κ° μμ€ν μ μμ μ±μ κ²°μ νμμ€:
(a) $H(z) = \frac{1}{1 - 0.5z^{-1} + 0.06z^{-2}}$
(b) $H(z) = \frac{z^2}{z^2 - 1.4z + 0.85}$
(c) $H(z) = \frac{1}{1 - 1.8\cos(0.4\pi)z^{-1} + 0.81z^{-2}}$
주리 μμ μ± κ²μ μ μ¬μ©νκ³ PythonμΌλ‘ κ²μ¦νμμ€.
μ°μ΅ λ¬Έμ 5: ROCμ μ νΈ κ²°μ ¶
Z λ³νμ΄ $X(z) = \frac{2z^2 - 1.5z}{z^2 - 0.9z + 0.2}$μΌ λ:
(a) κ°λ₯ν λͺ¨λ ROCλ₯Ό ꡬνμμ€.
(b) κ° ROCμ λν΄ λμνλ μ νΈ $x[n]$ (μΈκ³Όμ , λ°μΈκ³Όμ , λλ μλ°©ν₯)μ κ²°μ νμμ€.
(c) μ΄λ ROCμμ DTFTκ° μ‘΄μ¬νλκ°?
μ°μ΅ λ¬Έμ 6: μ λ¬ ν¨μ μ€κ³¶
λ€μ μ¬μμ κ°μ§ 2μ°¨ λμ§νΈ μμ€ν μ μ€κ³νμμ€: - κ³΅μ§ μ£Όνμ: $\omega_0 = \pi/3$ rad/sample - λμν: $\Delta\omega \approx 0.1$ rad/sample - 곡μ§μμ λ¨μ μ΄λ
(a) $z = re^{\pm j\omega_0}$μ κ·Ήμ μ λκ³ μνλ λμνμ μν $r$μ μ ννμμ€. (ννΈ: $r$μ΄ 1μ κ°κΉμΈ λ λμν $\approx 2(1-r)$)
(b) 곡μ§μμ λ¨μ μ΄λμ μ»κΈ° μν μμ μ λμΌμμ€.
(c) PythonμΌλ‘ ꡬννκ³ μ£Όνμ μλ΅μ λμννμμ€.
(d) κ³΅μ§ μ£Όνμμ λ€λ₯Έ μ£Όνμλ₯Ό ν¬ν¨ν μ νΈλ‘ ν μ€νΈνμμ€.
μ°μ΅ λ¬Έμ 7: λμ§νΈ λ°μ§κΈ° ꡬν¶
λμ§νΈ λ°μ§κΈ°λ λ€μ μ°¨λΆ λ°©μ μμ μ¬μ©νμ¬ λ§λ€ μ μμ΅λλ€:
$$y[n] = 2\cos(\omega_0) y[n-1] - y[n-2]$$
(a) $H(z)$μ κ·Ήμ μ ꡬνμμ€. κ·Ήμ μ μ΄λμ μμΉνλκ°?
(b) μ μ΄ μμ€ν μ΄ κ²½κ³ μμ μΈκ°? μ ν μ λ°λ μ°μ μμ μ€μ λ‘ μ΄λ€ μΌμ΄ λ°μνλκ°?
(c) PythonμΌλ‘ λ°μ§κΈ°λ₯Ό ꡬννκ³ 8000 Hz μνλ§ λ μ΄νΈμμ 440 Hz μμ μμ±νμμ€. 10000 μν μ΄ν μ§μ μ¬μΈ κ³μ°κ³Ό λΉκ΅νμμ€.
13. λ μ½μ 거리¶
- Oppenheim, Schafer. Discrete-Time Signal Processing, 3rd ed. Chapters 3, 5.
- Proakis, Manolakis. Digital Signal Processing, 4th ed. Chapters 3-4.
- Haykin, Van Veen. Signals and Systems, 2nd ed. Chapter 8.
- Roberts, M. J. Signals and Systems, Chapter 10.
- Phillips, Parr, Riskin. Signals, Systems, and Transforms, 5th ed. Chapters 8-9.
μ΄μ : 06. μ΄μ° νΈλ¦¬μ λ³ν | λ€μ: 08. λμ§νΈ νν° κΈ°μ΄