Split View: 공업수학 완전 정복 2편: 푸리에 급수/변환과 편미분방정식(PDE)
공업수학 완전 정복 2편: 푸리에 급수/변환과 편미분방정식(PDE)
공업수학 완전 정복 2편: 푸리에 급수/변환과 편미분방정식(PDE)
신호 처리, 통신 시스템, 전자기학의 수학적 기반은 단연 **푸리에 해석(Fourier Analysis)**입니다. 어떤 주기 신호도 사인·코사인의 합으로 표현할 수 있다는 놀라운 사실이 현대 공학의 핵심입니다. 이번 글에서는 푸리에 급수부터 FFT, 그리고 편미분방정식까지 체계적으로 정리합니다.
1. 푸리에 급수 (Fourier Series)
1.1 핵심 아이디어
1807년 조제프 푸리에(Joseph Fourier)는 열방정식을 풀면서 놀라운 주장을 했습니다: 어떤 주기 함수든 삼각함수의 무한급수로 표현할 수 있다는 것입니다.
주기 인 함수 의 푸리에 급수:
푸리에 계수:
직교성(Orthogonality): 이 공식이 성립하는 이유는 삼각함수의 직교성 때문입니다.
1.2 사각파(Square Wave) 전개
문제: 주기 인 사각파
계산:
는 기함수(odd function)이므로 ().
결과:
처음 몇 항만 더해도 사각파에 점점 가까워집니다. 이것이 푸리에 급수의 위력입니다.
라이프니츠 공식: 를 대입하면:
1.3 삼각파(Triangle Wave) 전개
우함수(even function)이므로 .
결과:
사각파와 달리 계수가 으로 빨리 감소하여 더 빨리 수렴합니다.
1.4 수렴과 깁스 현상 (Gibbs Phenomenon)
디리클레 조건(Dirichlet Conditions):
함수 가 다음 조건을 만족하면 푸리에 급수는 수렴합니다:
- 한 주기 내에서 절대 적분 가능:
- 한 주기 내에서 극값의 수가 유한
- 한 주기 내에서 불연속점의 수가 유한
불연속점에서 급수는 좌극한과 우극한의 평균으로 수렴합니다:
깁스 현상: 불연속점 근처에서 유한 항 합산 시 오버슈트가 발생합니다. 항수를 아무리 늘려도 불연속점 근처에서 약 **8.9%**의 오버슈트가 사라지지 않습니다.
이는 사각파 필터의 링잉(ringing) 현상의 수학적 원인입니다.
1.5 복소 푸리에 급수
오일러 공식을 이용하면:
복소 계수:
, 과의 관계:
파시발(Parseval) 정리 (에너지 보존):
신호의 시간 영역 에너지와 주파수 영역 에너지가 같다는 의미입니다.
바젤 문제 응용: 삼각파의 파시발 정리 적용:
2. 푸리에 변환 (Fourier Transform)
2.1 주기 함수에서 비주기 함수로
주기 을 로 보내면 푸리에 급수가 푸리에 변환으로 발전합니다.
푸리에 변환 (시간 -> 주파수):
역 푸리에 변환 (주파수 -> 시간):
주파수 를 쓰는 표현 (공학에서 자주 사용):
2.2 주요 변환쌍
구형파 펄스:
시간 영역에서 유한한 펄스가 주파수 영역에서 sinc 함수가 됩니다.
불확정성 원리: 시간 폭 와 대역폭 의 곱은 하한이 있습니다.
펄스가 짧을수록(시간 분해능 높음) 대역폭이 넓어집니다(주파수 분해능 낮음).
가우시안 함수: 자기 쌍대(self-dual) 변환쌍
가우시안의 푸리에 변환은 다시 가우시안입니다.
디락 델타 함수(Dirac Delta):
임펄스의 주파수 스펙트럼은 모든 주파수에서 균일합니다 (백색 잡음과 유사).
순수 DC 신호는 에서만 에너지를 가집니다.
2.3 주요 성질
선형성:
시간 이동:
지연은 크기를 바꾸지 않고 위상만 변화시킵니다.
주파수 이동 (변조):
시간 스케일링:
신호를 시간 압축하면 대역폭이 넓어집니다.
미분:
미분이 주파수 영역에서 곱셈으로 변환됩니다. ODE를 대수 방정식으로!
적분:
합성곱 정리 (신호 처리의 핵심):
선형 시불변(LTI) 시스템에서 출력 = 입력과 임펄스 응답의 합성곱 → 주파수 영역에서는 곱셈!
파시발 정리 (에너지 보존):
2.4 응용: 필터 설계
이상적인 저역통과 필터(LPF):
임펄스 응답:
인과성이 없어(미래 샘플 필요) 실제 구현 불가 → 실용 필터(버터워스, 체비쇼프 등) 사용.
대역통과 필터:
임펄스 응답:
주파수 응답(Frequency Response):
LTI 시스템에 를 입력하면:
는 복소 크기(gain)와 위상 이동을 동시에 나타냅니다:
3. 이산 푸리에 변환 (DFT)과 FFT
3.1 DFT 정의
개의 이산 샘플 에 대해:
회전 인수(twiddle factor):
주파수 분해능: (샘플링 주파수를 으로 나눔)
나이퀴스트 한계: (앨리어싱 방지)
3.2 FFT 알고리즘 (쿨리-튜키 알고리즘)
직접 DFT 계산: 복잡도
FFT (Fast Fourier Transform): 복잡도
일 때:
- DFT: 연산
- FFT: 연산 → 100배 빠름
분할 정복 원리 (데시메이션-인-타임):
짝수/홀수 인덱스로 분리:
이므로 와 는 각각 점 DFT입니다.
이를 재귀적으로 적용하면 이 됩니다.
3.3 Python으로 FFT 구현과 신호 분석
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal as sig
# 복합 신호 생성 (50 Hz + 120 Hz 혼합)
fs = 1000 # 샘플링 주파수 (Hz)
T = 1.0 # 신호 길이 (초)
N = int(fs * T)
t = np.linspace(0, T, N, endpoint=False)
# 신호: 50 Hz (진폭 1.0) + 120 Hz (진폭 0.5) + 노이즈
np.random.seed(42)
signal_clean = (np.sin(2 * np.pi * 50 * t) +
0.5 * np.sin(2 * np.pi * 120 * t))
noise = 0.2 * np.random.randn(N)
signal_noisy = signal_clean + noise
# FFT 수행
fft_result = np.fft.fft(signal_noisy)
freqs = np.fft.fftfreq(N, 1/fs)
# 양측 스펙트럼 -> 단측 스펙트럼
magnitude = (2.0 / N) * np.abs(fft_result[:N//2])
freqs_pos = freqs[:N//2]
# 시각화
fig, axes = plt.subplots(3, 1, figsize=(12, 10))
axes[0].plot(t[:200], signal_noisy[:200], 'b-', alpha=0.7)
axes[0].plot(t[:200], signal_clean[:200], 'r-', linewidth=2)
axes[0].set_xlabel('시간 (s)')
axes[0].set_ylabel('진폭')
axes[0].set_title('시간 영역 신호 (파랑: 노이즈 포함, 빨강: 원 신호)')
axes[0].legend(['노이즈 포함', '원 신호'])
axes[0].grid(True, alpha=0.3)
axes[1].plot(freqs_pos, magnitude, 'g-')
axes[1].set_xlabel('주파수 (Hz)')
axes[1].set_ylabel('진폭 스펙트럼')
axes[1].set_title('주파수 영역 (FFT 크기)')
axes[1].set_xlim([0, 200])
axes[1].grid(True, alpha=0.3)
# 스펙트로그램
f_spec, t_spec, Sxx = sig.spectrogram(signal_noisy, fs, nperseg=64)
axes[2].pcolormesh(t_spec, f_spec, 10*np.log10(Sxx + 1e-10), shading='gouraud')
axes[2].set_ylabel('주파수 (Hz)')
axes[2].set_xlabel('시간 (s)')
axes[2].set_title('스펙트로그램')
axes[2].set_ylim([0, 200])
plt.tight_layout()
plt.savefig('fft_analysis.png', dpi=150)
plt.show()
# 주요 주파수 성분 출력
peak_indices = np.where(magnitude > 0.1)[0]
print("주요 주파수 성분:")
for idx in peak_indices:
print(f" {freqs_pos[idx]:.1f} Hz: 진폭 {magnitude[idx]:.3f}")
3.4 FFT를 이용한 신호 필터링
import numpy as np
import matplotlib.pyplot as plt
def fft_lowpass_filter(signal_in, fs, cutoff_freq):
"""FFT 기반 이상적 저역통과 필터"""
N = len(signal_in)
fft_result = np.fft.fft(signal_in)
freqs = np.fft.fftfreq(N, 1/fs)
# 차단 주파수 이상 제거
fft_filtered = fft_result.copy()
fft_filtered[np.abs(freqs) > cutoff_freq] = 0
# 역변환
return np.real(np.fft.ifft(fft_filtered))
# 필터링 예제
fs = 1000
t = np.linspace(0, 1, fs, endpoint=False)
signal_in = (np.sin(2 * np.pi * 50 * t) +
0.3 * np.sin(2 * np.pi * 200 * t) +
0.1 * np.random.randn(fs))
filtered = fft_lowpass_filter(signal_in, fs, cutoff_freq=80)
plt.figure(figsize=(10, 5))
plt.plot(t[:200], signal_in[:200], 'b-', alpha=0.5, label='원 신호')
plt.plot(t[:200], filtered[:200], 'r-', linewidth=2, label='필터링된 신호 (80Hz LPF)')
plt.xlabel('시간 (s)')
plt.ylabel('진폭')
plt.title('FFT 기반 저역통과 필터링')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
4. 편미분방정식 (PDE)
4.1 PDE 분류
2차 선형 PDE:
판별식 에 따라:
| 조건 | 분류 | 대표 방정식 |
|---|---|---|
| 타원형(Elliptic) | 라플라스 방정식 | |
| 포물선형(Parabolic) | 열방정식 | |
| 쌍곡선형(Hyperbolic) | 파동방정식 |
4.2 변수분리법 (Separation of Variables)
기본 아이디어: 로 가정하여 PDE를 두 개의 ODE로 분리합니다.
4.3 열방정식 (Heat Equation)
물리적 배경: 막대의 열전도
경계조건: , (양 끝이 온도 0 고정)
초기조건:
풀이 과정:
대입:
분리 상수 는 경계조건에서 결정됩니다.
X 방정식: ,
고유값 문제: , 고유함수:
T 방정식:
일반해 (중첩 원리):
계수 결정 ( 적용):
이는 푸리에 사인 급수이므로:
전자 소자 열 해석 응용:
CPU 히트싱크, 파워 트랜지스터 열 분포, PCB 열 관리 등에서 열방정식이 사용됩니다.
물리적 해석:
- 고주파 모드(이 큰 항): 빠르게 감쇠 (지수 항의 의존성)
- 저주파 모드(): 가장 천천히 감쇠
- 시간이 지나면 초기 분포에 무관하게 열이 균일하게 분포
4.4 파동방정식 (Wave Equation)
물리적 배경: 팽팽한 줄의 횡진동
파속 (장력/선밀도)
경계조건: (양 끝 고정)
초기조건: ,
풀이 (변수분리):
계수 결정:
달랑베르 해(d'Alembert Solution):
초기 변위만 있고 초기 속도가 없는 경우:
이는 진행파의 중첩입니다. 오른쪽으로 만큼 이동한 파와 왼쪽으로 이동한 파의 합.
정재파(Standing Wave):
마디(node): 인 점 →
배(antinode): 인 점
전자기파 전파: 전송선(transmission line)에서 파동방정식:
여기서 (단위 길이당 인덕턴스/캐패시턴스)
4.5 라플라스 방정식 (Laplace Equation)
물리적 의미: 정상 상태(시간 불변) 열분포, 정전기 전위, 비압축성 유동의 속도 퍼텐셜
직사각형 영역 (, ):
경계조건:
풀이:
분리:
X 방정식: ,
Y 방정식: ,
일반해:
에서:
정전기 응용: 두 도체 평판 사이의 전위 분포, PCB 도선 사이의 전기장 해석
4.6 Python으로 PDE 수치 풀기
유한 차분법(Finite Difference Method)으로 열방정식 풀기:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
def solve_heat_equation_fd(L, T_total, Nx, Nt, c, initial_func):
"""
유한 차분법으로 열방정식 수치 풀이
u_t = c^2 * u_xx
경계조건: u(0,t) = u(L,t) = 0
"""
dx = L / (Nx - 1)
dt = T_total / (Nt - 1)
# 안정성 조건 확인 (CFL 조건)
r = c**2 * dt / dx**2
if r > 0.5:
print(f"경고: r = {r:.3f} > 0.5, 불안정할 수 있습니다!")
else:
print(f"r = {r:.3f} (안정 조건 만족)")
x = np.linspace(0, L, Nx)
u = initial_func(x)
u[0] = u[-1] = 0 # 경계조건
u_history = [u.copy()]
for _ in range(Nt - 1):
u_new = u.copy()
u_new[1:-1] = u[1:-1] + r * (u[2:] - 2*u[1:-1] + u[:-2])
u_new[0] = u_new[-1] = 0
u = u_new
u_history.append(u.copy())
return x, np.array(u_history)
# 파라미터 설정
L = 1.0 # 막대 길이 (m)
T_total = 0.1 # 시뮬레이션 시간 (s)
c = 1.0 # 열확산 계수
def initial_temp(x):
"""초기 온도 분포 (사인 반파)"""
return np.sin(np.pi * x / L)
Nx, Nt = 50, 500
x, u_hist = solve_heat_equation_fd(L, T_total, Nx, Nt, c, initial_temp)
# 시각화
t_points = np.linspace(0, T_total, Nt)
t_indices = [0, Nt//10, Nt//4, Nt//2, Nt-1]
plt.figure(figsize=(10, 6))
for idx in t_indices:
plt.plot(x, u_hist[idx],
label=f't = {t_points[idx]:.3f} s')
plt.xlabel('위치 x (m)')
plt.ylabel('온도 u(x,t)')
plt.title('열방정식 수치 풀이 - 유한 차분법')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('heat_equation.png', dpi=150)
plt.show()
# 해석해와 비교
print("\n해석해 vs 수치해 비교 (t=0.05, n=1항):")
t_check = 0.05
idx_check = int(t_check / T_total * (Nt - 1))
u_analytical = np.sin(np.pi * x / L) * np.exp(-c**2 * (np.pi/L)**2 * t_check)
max_error = np.max(np.abs(u_hist[idx_check] - u_analytical))
print(f"최대 오차: {max_error:.6f}")
유한 차분법으로 라플라스 방정식 (정적 전위) 풀기:
import numpy as np
import matplotlib.pyplot as plt
def solve_laplace_fd(Nx, Ny, boundary_func, tol=1e-6, max_iter=10000):
"""
가우스-자이델 반복법으로 라플라스 방정식 풀이
경계 조건: boundary_func(x, y, side) 반환
"""
u = np.zeros((Ny, Nx))
x = np.linspace(0, 1, Nx)
y = np.linspace(0, 1, Ny)
# 경계조건 설정
for j in range(Nx):
u[0, j] = 0 # 아래 경계
u[-1, j] = np.sin(np.pi * x[j]) # 위 경계
for i in range(Ny):
u[i, 0] = 0 # 왼쪽 경계
u[i, -1] = 0 # 오른쪽 경계
# 반복법
for iteration in range(max_iter):
u_old = u.copy()
# 내부 점 갱신
u[1:-1, 1:-1] = 0.25 * (u[2:, 1:-1] + u[:-2, 1:-1] +
u[1:-1, 2:] + u[1:-1, :-2])
# 경계조건 재설정
u[0, :] = 0
u[-1, :] = np.sin(np.pi * x)
u[:, 0] = 0
u[:, -1] = 0
# 수렴 확인
residual = np.max(np.abs(u - u_old))
if residual < tol:
print(f"수렴: {iteration+1}번 반복 후 잔차 {residual:.2e}")
break
return x, y, u
x, y, u = solve_laplace_fd(50, 50, None)
X, Y = np.meshgrid(x, y)
plt.figure(figsize=(10, 8))
plt.subplot(1, 2, 1)
plt.contourf(X, Y, u, 20, cmap='hot')
plt.colorbar(label='전위 (V)')
plt.xlabel('x')
plt.ylabel('y')
plt.title('라플라스 방정식 - 등전위선')
plt.subplot(1, 2, 2)
plt.contour(X, Y, u, 20)
# 전기장 벡터 (전위의 음의 기울기)
Ey, Ex = np.gradient(-u)
plt.quiver(X[::4, ::4], Y[::4, ::4],
Ex[::4, ::4], Ey[::4, ::4],
alpha=0.7)
plt.xlabel('x')
plt.ylabel('y')
plt.title('전기장 벡터')
plt.tight_layout()
plt.savefig('laplace_solution.png', dpi=150)
plt.show()
5. 라플라스 변환과 PDE, Z-변환 소개
5.1 라플라스 변환으로 PDE 풀기
열방정식에서 시간 변수에 대해 라플라스 변환을 적용합니다:
이는 에 대한 2차 ODE가 됩니다. 경계조건과 함께 풀면:
역 라플라스 변환으로 시간 응답을 구할 수 있습니다.
5.2 Z-변환 소개 (디지털 시스템)
아날로그 라플라스 변환의 디지털 대응물이 Z-변환입니다.
정의:
핵심 관계: (: 샘플링 주기)
| 아날로그 (라플라스) | 디지털 (Z-변환) | | ------------------------ | --------------- | --- | --- | | 평면 | 평면 | | 축 (안정 경계) | 단위원 | | 좌반평면 (안정 영역) | 단위원 내부 | | (적분기) | |
주요 변환쌍:
다음 편(3편: 복소해석학과 Z-변환)에서 Z-변환을 완전히 다루겠습니다.
6. 공학 응용 종합: 신호 처리 파이프라인
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal as sig
# 완전한 디지털 신호 처리 파이프라인 예시
# 1. 신호 생성
fs = 8000 # 오디오 샘플링 주파수
duration = 0.5
t = np.linspace(0, duration, int(fs * duration), endpoint=False)
# 440 Hz (A4 음) + 880 Hz (A5 음) + 고주파 노이즈
audio = (np.sin(2 * np.pi * 440 * t) +
0.5 * np.sin(2 * np.pi * 880 * t) +
0.3 * np.random.randn(len(t)))
# 2. FFT로 스펙트럼 분석
N = len(audio)
fft_audio = np.fft.rfft(audio)
freqs = np.fft.rfftfreq(N, 1/fs)
magnitude = np.abs(fft_audio)
# 3. 버터워스 필터 설계 (1000 Hz 이하 통과)
sos = sig.butter(8, 1000, fs=fs, btype='low', output='sos')
# 4. 필터 적용
audio_filtered = sig.sosfilt(sos, audio)
# 5. 주파수 응답 확인
w, h = sig.sosfreqz(sos, worN=2000, fs=fs)
# 시각화
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
axes[0, 0].plot(t[:400], audio[:400], 'b-', alpha=0.7)
axes[0, 0].set_title('원 신호 (시간 영역)')
axes[0, 0].set_xlabel('시간 (s)')
axes[0, 0].grid(True, alpha=0.3)
axes[0, 1].semilogy(freqs, magnitude)
axes[0, 1].set_title('원 신호 FFT 크기 스펙트럼')
axes[0, 1].set_xlabel('주파수 (Hz)')
axes[0, 1].set_xlim([0, 2000])
axes[0, 1].grid(True, alpha=0.3)
axes[1, 0].plot(t[:400], audio_filtered[:400], 'r-', linewidth=1.5)
axes[1, 0].set_title('필터링된 신호 (1000Hz LPF)')
axes[1, 0].set_xlabel('시간 (s)')
axes[1, 0].grid(True, alpha=0.3)
axes[1, 1].plot(w, 20 * np.log10(abs(h) + 1e-10), 'g-', linewidth=2)
axes[1, 1].axvline(x=1000, color='r', linestyle='--', label='차단 주파수')
axes[1, 1].set_title('버터워스 필터 주파수 응답')
axes[1, 1].set_xlabel('주파수 (Hz)')
axes[1, 1].set_ylabel('크기 (dB)')
axes[1, 1].set_xlim([0, 3000])
axes[1, 1].set_ylim([-80, 5])
axes[1, 1].legend()
axes[1, 1].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('signal_processing_pipeline.png', dpi=150)
plt.show()
정리 및 다음 단계
이번 글에서 다룬 내용:
- 푸리에 급수: 사각파/삼각파 전개, 수렴, 깁스 현상, 복소 표현, 파시발 정리
- 푸리에 변환: 정의, 주요 변환쌍(구형파, 가우시안, 델타함수), 9가지 성질
- DFT와 FFT: 정의, 쿨리-튜키 알고리즘, 복잡도, Python 구현
- PDE 분류: 타원형/포물선형/쌍곡선형
- 열방정식: 변수분리법, 푸리에 급수 풀이, 유한 차분법
- 파동방정식: 정재파, 달랑베르 해, 전자기파
- 라플라스 방정식: 직사각형 영역 풀이, 가우스-자이델 수치 풀이
- Z-변환 맛보기: 아날로그-디지털 대응 관계
다음 편에서는 복소해석학과 Z-변환을 완전히 다루며, 유수 정리로 어려운 실수 적분을 풀고 Z-변환으로 디지털 필터를 설계하는 방법을 배웁니다.
참고 자료
- Oppenheim, A. & Willsky, A. "Signals and Systems", 2nd Edition, Prentice Hall
- Kreyszig, E. "Advanced Engineering Mathematics", 10th Edition, Wiley
- Proakis, J. & Manolakis, D. "Digital Signal Processing", 4th Edition
- NumPy FFT 공식 문서
- SciPy Signal Processing 문서
Engineering Mathematics 2: Fourier Series, Fourier Transform & PDE
Engineering Mathematics 2: Fourier Series/Transform and Partial Differential Equations (PDE)
The mathematical foundation of signal processing, communication systems, and electromagnetics is undoubtedly Fourier Analysis. The remarkable fact that any periodic signal can be expressed as a sum of sines and cosines is the core of modern engineering. This article systematically covers Fourier series through FFT and partial differential equations.
1. Fourier Series
1.1 The Core Idea
In 1807, Joseph Fourier made a remarkable claim while solving the heat equation: any periodic function can be expressed as an infinite series of trigonometric functions.
Fourier series of a function with period :
Fourier coefficients:
Orthogonality: This formula works because of the orthogonality of trigonometric functions.
1.2 Square Wave Expansion
Problem: A square wave with period
Calculation:
Since is an odd function, for .
Result:
Even with just the first few terms, the sum approaches the square wave increasingly closely. This is the power of the Fourier series.
Leibniz formula: Substituting :
1.3 Triangle Wave Expansion
Since it is an even function, .
Result:
Unlike the square wave, the coefficients decrease rapidly as , giving faster convergence.
1.4 Convergence and the Gibbs Phenomenon
Dirichlet Conditions:
A function converges in its Fourier series if:
- Absolutely integrable over one period:
- Finite number of extrema within one period
- Finite number of discontinuities within one period
At a point of discontinuity, the series converges to the average of the left and right limits:
Gibbs Phenomenon: Near discontinuities, overshoot occurs when summing a finite number of terms. No matter how many terms are added, approximately 8.9% overshoot near discontinuities persists.
This is the mathematical cause of the ringing phenomenon in square-wave filters.
1.5 Complex Fourier Series
Using Euler's formula:
Complex coefficients:
Relationship with , :
Parseval's Theorem (energy conservation):
The energy in the time domain equals the energy in the frequency domain.
Basel problem application: Applying Parseval's theorem to the triangle wave:
2. Fourier Transform
2.1 From Periodic to Aperiodic Functions
Taking the period to , the Fourier series develops into the Fourier transform.
Fourier Transform (time to frequency):
Inverse Fourier Transform (frequency to time):
Expression using frequency (commonly used in engineering):
2.2 Key Transform Pairs
Rectangular pulse:
A finite pulse in the time domain becomes a sinc function in the frequency domain.
Uncertainty principle: The product of the time width and bandwidth has a lower bound.
A shorter pulse (higher time resolution) has wider bandwidth (lower frequency resolution).
Gaussian function: Self-dual transform pair
The Fourier transform of a Gaussian is again a Gaussian.
Dirac Delta Function:
The frequency spectrum of an impulse is uniform at all frequencies (similar to white noise).
A pure DC signal has energy only at .
2.3 Key Properties
Linearity:
Time shift:
Delay changes only the phase, not the magnitude.
Frequency shift (modulation):
Time scaling:
Compressing a signal in time widens its bandwidth.
Differentiation:
Differentiation becomes multiplication in the frequency domain. ODEs become algebraic equations!
Integration:
Convolution theorem (core of signal processing):
In linear time-invariant (LTI) systems: output = convolution of input with impulse response. In the frequency domain, this is just multiplication!
Parseval's theorem (energy conservation):
2.4 Application: Filter Design
Ideal Low-Pass Filter (LPF):
Impulse response:
Non-causal (requires future samples), so cannot be implemented in practice. Practical filters (Butterworth, Chebyshev, etc.) are used.
Bandpass filter:
Impulse response:
Frequency Response:
When is input to an LTI system:
simultaneously represents complex magnitude (gain) and phase shift:
3. Discrete Fourier Transform (DFT) and FFT
3.1 DFT Definition
For discrete samples :
Twiddle factor:
Frequency resolution: (sampling frequency divided by N)
Nyquist limit: (to prevent aliasing)
3.2 FFT Algorithm (Cooley-Tukey Algorithm)
Direct DFT computation: complexity
FFT (Fast Fourier Transform): complexity
For :
- DFT: operations
- FFT: operations — 100x faster
Divide and conquer principle (decimation-in-time):
Separate into even/odd indices:
Since , and are each -point DFTs.
Applying this recursively gives .
3.3 FFT Implementation and Signal Analysis with Python
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal as sig
# Generate composite signal (50 Hz + 120 Hz mixed)
fs = 1000 # Sampling frequency (Hz)
T = 1.0 # Signal length (seconds)
N = int(fs * T)
t = np.linspace(0, T, N, endpoint=False)
# Signal: 50 Hz (amplitude 1.0) + 120 Hz (amplitude 0.5) + noise
np.random.seed(42)
signal_clean = (np.sin(2 * np.pi * 50 * t) +
0.5 * np.sin(2 * np.pi * 120 * t))
noise = 0.2 * np.random.randn(N)
signal_noisy = signal_clean + noise
# Perform FFT
fft_result = np.fft.fft(signal_noisy)
freqs = np.fft.fftfreq(N, 1/fs)
# Two-sided spectrum -> one-sided spectrum
magnitude = (2.0 / N) * np.abs(fft_result[:N//2])
freqs_pos = freqs[:N//2]
# Visualization
fig, axes = plt.subplots(3, 1, figsize=(12, 10))
axes[0].plot(t[:200], signal_noisy[:200], 'b-', alpha=0.7)
axes[0].plot(t[:200], signal_clean[:200], 'r-', linewidth=2)
axes[0].set_xlabel('Time (s)')
axes[0].set_ylabel('Amplitude')
axes[0].set_title('Time Domain Signal (blue: with noise, red: clean signal)')
axes[0].legend(['Noisy', 'Clean'])
axes[0].grid(True, alpha=0.3)
axes[1].plot(freqs_pos, magnitude, 'g-')
axes[1].set_xlabel('Frequency (Hz)')
axes[1].set_ylabel('Amplitude Spectrum')
axes[1].set_title('Frequency Domain (FFT Magnitude)')
axes[1].set_xlim([0, 200])
axes[1].grid(True, alpha=0.3)
# Spectrogram
f_spec, t_spec, Sxx = sig.spectrogram(signal_noisy, fs, nperseg=64)
axes[2].pcolormesh(t_spec, f_spec, 10*np.log10(Sxx + 1e-10), shading='gouraud')
axes[2].set_ylabel('Frequency (Hz)')
axes[2].set_xlabel('Time (s)')
axes[2].set_title('Spectrogram')
axes[2].set_ylim([0, 200])
plt.tight_layout()
plt.savefig('fft_analysis.png', dpi=150)
plt.show()
# Print dominant frequency components
peak_indices = np.where(magnitude > 0.1)[0]
print("Dominant frequency components:")
for idx in peak_indices:
print(f" {freqs_pos[idx]:.1f} Hz: amplitude {magnitude[idx]:.3f}")
3.4 Signal Filtering with FFT
import numpy as np
import matplotlib.pyplot as plt
def fft_lowpass_filter(signal_in, fs, cutoff_freq):
"""FFT-based ideal low-pass filter"""
N = len(signal_in)
fft_result = np.fft.fft(signal_in)
freqs = np.fft.fftfreq(N, 1/fs)
# Zero out components above cutoff frequency
fft_filtered = fft_result.copy()
fft_filtered[np.abs(freqs) > cutoff_freq] = 0
# Inverse transform
return np.real(np.fft.ifft(fft_filtered))
# Filtering example
fs = 1000
t = np.linspace(0, 1, fs, endpoint=False)
signal_in = (np.sin(2 * np.pi * 50 * t) +
0.3 * np.sin(2 * np.pi * 200 * t) +
0.1 * np.random.randn(fs))
filtered = fft_lowpass_filter(signal_in, fs, cutoff_freq=80)
plt.figure(figsize=(10, 5))
plt.plot(t[:200], signal_in[:200], 'b-', alpha=0.5, label='Original signal')
plt.plot(t[:200], filtered[:200], 'r-', linewidth=2, label='Filtered signal (80Hz LPF)')
plt.xlabel('Time (s)')
plt.ylabel('Amplitude')
plt.title('FFT-Based Low-Pass Filtering')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
4. Partial Differential Equations (PDE)
4.1 PDE Classification
Second-order linear PDE:
Based on discriminant :
| Condition | Classification | Representative Equation |
|---|---|---|
| Elliptic | Laplace equation | |
| Parabolic | Heat equation | |
| Hyperbolic | Wave equation |
4.2 Separation of Variables
Basic idea: Assume to separate the PDE into two ODEs.
4.3 Heat Equation
Physical background: Heat conduction in a rod
Boundary conditions: , (both ends fixed at temperature 0)
Initial condition:
Solution procedure:
Substituting :
The separation constant is determined by the boundary conditions.
X equation: ,
Eigenvalue problem: , eigenfunctions:
T equation:
General solution (superposition principle):
Coefficient determination (applying ):
This is a Fourier sine series, so:
Application to electronic component thermal analysis:
The heat equation is used in CPU heatsink design, power transistor heat distribution, and PCB thermal management.
Physical interpretation:
- High-frequency modes (large ): decay rapidly (the dependence in the exponential term)
- Low-frequency mode (): decays most slowly
- Over time, heat distributes uniformly regardless of the initial distribution
4.4 Wave Equation
Physical background: Transverse vibration of a taut string
Wave speed (tension/linear mass density)
Boundary conditions: (both ends fixed)
Initial conditions: ,
Solution (separation of variables):
Coefficient determination:
d'Alembert Solution:
For the case of initial displacement only and zero initial velocity:
This is the superposition of traveling waves — the sum of a wave moving to the right by and a wave moving to the left.
Standing Wave:
Nodes: points where — at
Antinodes: points where
Electromagnetic wave propagation: Wave equation on a transmission line:
Where (per-unit-length inductance/capacitance).
4.5 Laplace Equation
Physical meaning: Steady-state (time-invariant) heat distribution, electrostatic potential, velocity potential in incompressible flow.
Rectangular domain (, ):
Boundary conditions:
Solution:
Separating :
X equation: ,
Y equation: ,
General solution:
At :
Electrostatic application: Potential distribution between two conducting plates, electric field analysis between PCB traces.
4.6 Numerical PDE Solutions with Python
Solving the heat equation with the Finite Difference Method:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
def solve_heat_equation_fd(L, T_total, Nx, Nt, c, initial_func):
"""
Numerical solution of heat equation using finite difference method
u_t = c^2 * u_xx
Boundary conditions: u(0,t) = u(L,t) = 0
"""
dx = L / (Nx - 1)
dt = T_total / (Nt - 1)
# Check stability condition (CFL condition)
r = c**2 * dt / dx**2
if r > 0.5:
print(f"Warning: r = {r:.3f} > 0.5, may be unstable!")
else:
print(f"r = {r:.3f} (stability condition satisfied)")
x = np.linspace(0, L, Nx)
u = initial_func(x)
u[0] = u[-1] = 0 # Boundary conditions
u_history = [u.copy()]
for _ in range(Nt - 1):
u_new = u.copy()
u_new[1:-1] = u[1:-1] + r * (u[2:] - 2*u[1:-1] + u[:-2])
u_new[0] = u_new[-1] = 0
u = u_new
u_history.append(u.copy())
return x, np.array(u_history)
# Parameter setup
L = 1.0 # Rod length (m)
T_total = 0.1 # Simulation time (s)
c = 1.0 # Thermal diffusivity
def initial_temp(x):
"""Initial temperature distribution (half sine wave)"""
return np.sin(np.pi * x / L)
Nx, Nt = 50, 500
x, u_hist = solve_heat_equation_fd(L, T_total, Nx, Nt, c, initial_temp)
# Visualization
t_points = np.linspace(0, T_total, Nt)
t_indices = [0, Nt//10, Nt//4, Nt//2, Nt-1]
plt.figure(figsize=(10, 6))
for idx in t_indices:
plt.plot(x, u_hist[idx],
label=f't = {t_points[idx]:.3f} s')
plt.xlabel('Position x (m)')
plt.ylabel('Temperature u(x,t)')
plt.title('Heat Equation Numerical Solution - Finite Difference Method')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('heat_equation.png', dpi=150)
plt.show()
# Compare with analytical solution
print("\nAnalytical vs numerical comparison (t=0.05, n=1 term):")
t_check = 0.05
idx_check = int(t_check / T_total * (Nt - 1))
u_analytical = np.sin(np.pi * x / L) * np.exp(-c**2 * (np.pi/L)**2 * t_check)
max_error = np.max(np.abs(u_hist[idx_check] - u_analytical))
print(f"Maximum error: {max_error:.6f}")
Solving the Laplace equation (static potential) with finite difference:
import numpy as np
import matplotlib.pyplot as plt
def solve_laplace_fd(Nx, Ny, boundary_func, tol=1e-6, max_iter=10000):
"""
Solving Laplace equation using Gauss-Seidel iteration
"""
u = np.zeros((Ny, Nx))
x = np.linspace(0, 1, Nx)
y = np.linspace(0, 1, Ny)
# Set boundary conditions
for j in range(Nx):
u[0, j] = 0 # Bottom boundary
u[-1, j] = np.sin(np.pi * x[j]) # Top boundary
for i in range(Ny):
u[i, 0] = 0 # Left boundary
u[i, -1] = 0 # Right boundary
# Iteration
for iteration in range(max_iter):
u_old = u.copy()
# Update interior points
u[1:-1, 1:-1] = 0.25 * (u[2:, 1:-1] + u[:-2, 1:-1] +
u[1:-1, 2:] + u[1:-1, :-2])
# Re-apply boundary conditions
u[0, :] = 0
u[-1, :] = np.sin(np.pi * x)
u[:, 0] = 0
u[:, -1] = 0
# Check convergence
residual = np.max(np.abs(u - u_old))
if residual < tol:
print(f"Converged: residual {residual:.2e} after {iteration+1} iterations")
break
return x, y, u
x, y, u = solve_laplace_fd(50, 50, None)
X, Y = np.meshgrid(x, y)
plt.figure(figsize=(10, 8))
plt.subplot(1, 2, 1)
plt.contourf(X, Y, u, 20, cmap='hot')
plt.colorbar(label='Potential (V)')
plt.xlabel('x')
plt.ylabel('y')
plt.title('Laplace Equation - Equipotential Lines')
plt.subplot(1, 2, 2)
plt.contour(X, Y, u, 20)
# Electric field vector (negative gradient of potential)
Ey, Ex = np.gradient(-u)
plt.quiver(X[::4, ::4], Y[::4, ::4],
Ex[::4, ::4], Ey[::4, ::4],
alpha=0.7)
plt.xlabel('x')
plt.ylabel('y')
plt.title('Electric Field Vectors')
plt.tight_layout()
plt.savefig('laplace_solution.png', dpi=150)
plt.show()
5. Laplace Transform and PDE, Introduction to Z-Transform
5.1 Solving PDE with the Laplace Transform
Applying the Laplace transform to the heat equation with respect to the time variable:
This becomes a second-order ODE in . Solving with boundary conditions:
The time response can be obtained by inverse Laplace transform.
5.2 Introduction to Z-Transform (Digital Systems)
The digital counterpart of the analog Laplace transform is the Z-transform.
Definition:
Key relationship: (: sampling period)
| Analog (Laplace) | Digital (Z-transform) | | ----------------------------------- | --------------------- | --- | --- | | plane | plane | | axis (stability boundary) | Unit circle | | Left half-plane (stable region) | Inside unit circle | | (integrator) | |
Key transform pairs:
The Z-transform will be fully covered in the next article (Part 3: Complex Analysis and Z-Transform).
6. Engineering Application: Signal Processing Pipeline
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal as sig
# Complete digital signal processing pipeline example
# 1. Signal generation
fs = 8000 # Audio sampling frequency
duration = 0.5
t = np.linspace(0, duration, int(fs * duration), endpoint=False)
# 440 Hz (A4 note) + 880 Hz (A5 note) + high-frequency noise
audio = (np.sin(2 * np.pi * 440 * t) +
0.5 * np.sin(2 * np.pi * 880 * t) +
0.3 * np.random.randn(len(t)))
# 2. Spectrum analysis with FFT
N = len(audio)
fft_audio = np.fft.rfft(audio)
freqs = np.fft.rfftfreq(N, 1/fs)
magnitude = np.abs(fft_audio)
# 3. Butterworth filter design (pass below 1000 Hz)
sos = sig.butter(8, 1000, fs=fs, btype='low', output='sos')
# 4. Apply filter
audio_filtered = sig.sosfilt(sos, audio)
# 5. Check frequency response
w, h = sig.sosfreqz(sos, worN=2000, fs=fs)
# Visualization
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
axes[0, 0].plot(t[:400], audio[:400], 'b-', alpha=0.7)
axes[0, 0].set_title('Original Signal (Time Domain)')
axes[0, 0].set_xlabel('Time (s)')
axes[0, 0].grid(True, alpha=0.3)
axes[0, 1].semilogy(freqs, magnitude)
axes[0, 1].set_title('Original Signal FFT Magnitude Spectrum')
axes[0, 1].set_xlabel('Frequency (Hz)')
axes[0, 1].set_xlim([0, 2000])
axes[0, 1].grid(True, alpha=0.3)
axes[1, 0].plot(t[:400], audio_filtered[:400], 'r-', linewidth=1.5)
axes[1, 0].set_title('Filtered Signal (1000Hz LPF)')
axes[1, 0].set_xlabel('Time (s)')
axes[1, 0].grid(True, alpha=0.3)
axes[1, 1].plot(w, 20 * np.log10(abs(h) + 1e-10), 'g-', linewidth=2)
axes[1, 1].axvline(x=1000, color='r', linestyle='--', label='Cutoff frequency')
axes[1, 1].set_title('Butterworth Filter Frequency Response')
axes[1, 1].set_xlabel('Frequency (Hz)')
axes[1, 1].set_ylabel('Magnitude (dB)')
axes[1, 1].set_xlim([0, 3000])
axes[1, 1].set_ylim([-80, 5])
axes[1, 1].legend()
axes[1, 1].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('signal_processing_pipeline.png', dpi=150)
plt.show()
Summary and Next Steps
Topics covered in this article:
- Fourier series: Square/triangle wave expansion, convergence, Gibbs phenomenon, complex representation, Parseval's theorem
- Fourier transform: Definition, key transform pairs (rectangular pulse, Gaussian, delta function), 9 properties
- DFT and FFT: Definition, Cooley-Tukey algorithm, complexity, Python implementation
- PDE classification: Elliptic/Parabolic/Hyperbolic
- Heat equation: Separation of variables, Fourier series solution, finite difference method
- Wave equation: Standing waves, d'Alembert solution, electromagnetic waves
- Laplace equation: Rectangular domain solution, Gauss-Seidel numerical solution
- Introduction to Z-transform: Analog-digital correspondence
The next article will fully cover Complex Analysis and Z-Transform — using the residue theorem to solve difficult real integrals and designing digital filters with the Z-transform.
References
- Oppenheim, A. & Willsky, A. "Signals and Systems", 2nd Edition, Prentice Hall
- Kreyszig, E. "Advanced Engineering Mathematics", 10th Edition, Wiley
- Proakis, J. & Manolakis, D. "Digital Signal Processing", 4th Edition
- NumPy FFT Documentation
- SciPy Signal Processing Documentation
Quiz
Q1. What is the Gibbs phenomenon and why does it occur?
Answer: The Gibbs phenomenon is an overshoot of approximately 8.9% that occurs near discontinuities when a Fourier series is truncated to a finite number of terms. Increasing the number of terms does not eliminate this overshoot — it merely moves closer to the discontinuity.
Explanation: At a jump discontinuity, the Fourier series converges to the average of the left and right limits. The finite partial sum exhibits oscillations near the discontinuity due to the slow convergence of the series at that point. This causes ringing artifacts in square-wave signal filters, which is why windowing functions (Hamming, Hanning, etc.) are used in practice to reduce the Gibbs effect.
Q2. What is the convolution theorem in the context of Fourier transforms and why is it fundamental to signal processing?
Answer: The Fourier convolution theorem states that the Fourier transform of the convolution of two functions equals the pointwise product of their individual Fourier transforms: F{(f*g)(t)} = F(omega) * G(omega).
Explanation: In linear time-invariant (LTI) systems, the output signal is the convolution of the input with the system's impulse response in the time domain. In the frequency domain, this becomes simple multiplication H(omega) * X(omega) = Y(omega). This transforms a computationally expensive convolution operation into a simple multiplication, which is why frequency-domain filtering is used in practice: compute FFT of input, multiply by filter response, compute inverse FFT.
Q3. How is the method of separation of variables applied to solve the heat equation?
Answer: Assume u(x,t) = X(x)T(t). Substituting into the PDE and dividing by XT gives T'/(c^2T) = X''/X = -lambda (a constant). This separates into two ODEs: X'' + lambdaX = 0 (eigenvalue problem) and T' + c^2lambdaT = 0. Applying boundary conditions gives eigenvalues lambda_n = (npi/L)^2 and eigenfunctions X_n = sin(npix/L). The general solution is the superposition u(x,t) = sum of b_n * sin(npix/L) _ exp(-c^2_(npi/L)^2 * t).
Explanation: The Fourier coefficients b_n are determined from the initial condition u(x,0) = f(x) using the Fourier sine series formula. The solution shows that higher-frequency modes decay much faster (exponential factor has n^2), so the temperature distribution smooths out over time.
Q4. What is the Nyquist sampling theorem and what happens if it is violated?
Answer: The Nyquist theorem states that to faithfully reconstruct a signal, the sampling frequency must be at least twice the maximum frequency in the signal: f_s greater than or equal to 2*f_max. The frequency f_max is called the Nyquist frequency.
Explanation: If the sampling frequency is too low (f_s less than 2*f_max), aliasing occurs. High-frequency components fold back into the lower frequency range and appear as false low-frequency signals, making it impossible to recover the original signal. In practice, anti-aliasing low-pass filters are applied before sampling to remove frequency components above f_s/2.
Q5. What are the three classifications of second-order linear PDEs and what type of physical phenomena does each represent?
Answer: Second-order linear PDEs are classified by the discriminant B^2 - 4AC:
- Elliptic (B^2 - 4AC less than 0): Laplace and Poisson equations — represent steady-state distributions such as static electric potential, steady-state temperature, and incompressible fluid flow.
- Parabolic (B^2 - 4AC equals 0): Heat equation — represents diffusion processes that evolve over time toward a steady state.
- Hyperbolic (B^2 - 4AC greater than 0): Wave equation — represents propagating wave phenomena such as vibrating strings, sound waves, and electromagnetic waves.
Explanation: The classification determines the appropriate numerical methods and boundary/initial condition requirements. Elliptic equations need boundary conditions on all sides; parabolic equations need initial conditions plus boundary conditions; hyperbolic equations need two initial conditions (displacement and velocity) plus boundary conditions.