Mở đầu: filter đầu tiên ai cũng viết, và vì sao nó "tồi"
Bài 4 định nghĩa: một hệ LTI được đặc trưng hoá TRỌN VẸN bởi đáp ứng xung $h[n]$, và đầu ra là phép tích chập $y[n] = x[n] * h[n]$. Đó chính xác là những gì một filter FIR (Finite Impulse Response) làm — bài này trả đúng cổ tức đã hứa từ Bài 4.
Filter đầu tiên hầu như ai học DSP cũng tự viết: trung bình trượt (moving average) — cộng vài mẫu gần nhau rồi chia trung bình. Nó mượt tín hiệu thật, nhưng đáp ứng tần số của nó lại có hình dạng sinc lởm chởm, chặn dải kém — chính là bộ lọc âm thầm chạy phía sau Series 13 Bài 10 (ADC) mà không ai giải thích vì sao nó lại có hình dạng phổ kỳ lạ đó. Bài này giải thích, rồi xây dựng một filter thiết kế CÓ CHỦ ĐÍCH, đúng đắn hơn hẳn.
1. Filter = hệ LTI có chủ đích
FIR nghĩa đen: đáp ứng xung $h[n]$ có ĐỘ DÀI HỮU HẠN (finite). Trung bình trượt $N$ điểm chính là 1 FIR đặc biệt: $h[n] = 1/N$ cho $N$ mẫu đầu, còn lại 0. Verify bằng self-test: đưa nhiễu trắng qua bộ lọc này (convolve với $h$), phương sai giảm rõ rệt so với tín hiệu gốc — nó THỰC SỰ làm mượt. Nhưng đáp ứng tần số của trung bình trượt có hình sinc với các thuỳ phụ lởm chởm (giống Bài 7!) — chặn dải kém, không kiểm soát được chính xác tần số cắt. Mục 2 thiết kế filter ĐÚNG CHỦ ĐÍCH hơn.
2. Thiết kế từ "giấc mơ": low-pass lý tưởng → sinc vô hạn
Low-pass "trong mơ" hoàn hảo: giữ NGUYÊN VẸN mọi tần số dưới cutoff, TRIỆT TIÊU HOÀN TOÀN mọi tần số trên
cutoff — về mặt hình học, đáp ứng tần số là một GẠCH CHỮ NHẬT hoàn hảo. Biến đổi Fourier ngược của một
gạch chữ nhật (Bài 6 quen thuộc) chính là hàm sinc — nhưng trải dài VÔ HẠN về cả 2 hướng thời
gian, nghĩa là filter này cần biết trước cả tương lai lẫn quá khứ vô hạn —
không thể cài đặt được (không nhân quả).
Đường ra thực dụng — windowed-sinc — gồm đúng 3 bước:
- Cắt sinc vô hạn về một số tap hữu hạn ($N$ hệ số).
- Dịch về bên phải $((N-1)/2$ mẫu) để phần "quan trọng nhất" (đỉnh sinc) rơi vào vùng có thể tính được — biến filter thành NHÂN QUẢ.
- Nhân với cửa sổ (Bài 7 tái xuất đúng vai) — cắt "cứng" (như cửa sổ chữ nhật) tạo gợn sóng dải thông xấu (hiện tượng Gibbs), cửa sổ mượt (Hann/Hamming/Blackman) giảm gợn đáng kể.
function firLowpassDesign(numTaps, cutoffNorm, windowFn) {
const M = (numTaps - 1) / 2; // dich de nhan qua
const win = windowFn(numTaps); // cua so Bai 7 - KHONG viet lai
const h = new Array(numTaps);
for (let n = 0; n < numTaps; n++) {
const k = n - M;
const ideal = 2 * cutoffNorm * sinc(2 * cutoffNorm * k); // sinc ly tuong, DA CAT + DICH
h[n] = ideal * win[n]; // NHAN voi cua so
}
return h;
}
Số tap càng NHIỀU, sinc bị cắt càng gần với bản vô hạn lý tưởng → dải chuyển tiếp (transition band) càng DỐC (gần lý tưởng hơn) — nhưng filter càng dài (Mục 4 pitfall).
3. Linear phase — món quà của đối xứng
Vì đáp ứng xung windowed-sinc luôn ĐỐI XỨNG quanh tâm ($h[n] = h[N-1-n]$ — verify bằng self-test: sai số dưới $10^{-9}$, thực chất là 0 tuyệt đối, chỉ giới hạn bởi làm tròn dấu phẩy động), FIR loại này có một tính chất TOÁN HỌC quý giá: linear phase — MỌI tần số đi qua filter đều bị trễ ĐÚNG BẰNG NHAU một lượng cố định, gọi là group delay, luôn bằng $(N{-}1)/2$ mẫu (verify: $N=51$ tap cho group delay đúng 25 mẫu).
Vì sao quý giá: nếu các tần số bị trễ KHÔNG ĐỀU nhau (như hầu hết filter IIR ở Bài 11), dạng sóng gốc bị MÉO — các thành phần tần số "đến nơi" ở thời điểm khác nhau, làm hỏng hình dạng ban đầu. Với linear phase, dạng sóng CHỈ bị trễ, không hề méo — đây là lý do FIR (không phải IIR) được chọn cho mastering âm thanh chuyên nghiệp, thiết bị đo lường chính xác, và filter bank — bất kỳ nơi nào pha "là vàng".
4. Biến hình từ low-pass: high-pass & band-pass
Không cần thiết kế lại từ đầu — 2 phép biến hình đơn giản tái sử dụng NGUYÊN low-pass vừa xây:
| Loại filter | Cách biến hình | Verified |
|---|---|---|
| High-pass | Spectral inversion: xung đơn vị (tại tâm) TRỪ ĐI low-pass cùng cutoff | DC ≈ 0 (chặn), gần Nyquist ≈ 1 (thông) — vẫn đối xứng hoàn hảo |
| Band-pass | Dịch tần: low-pass (băng thông mong muốn) NHÂN với cosin tại tần số trung tâm | DC ≈ 0, tại tần số trung tâm ≈ 1, gần Nyquist ≈ 0 — cả 2 đầu đều bị chặn |
5. Số đo thật từ self-test
Verify bằng node dsp-core.js, với low-pass $N=51$ tap, cửa sổ Hann, cutoff chuẩn hoá 0,1:
| Đại lượng | Giá trị verified |
|---|---|
| Đối xứng $h[i] = h[N-1-i]$ | Sai số < $10^{-9}$ (thực chất là 0 tuyệt đối) |
| Group delay | Đúng 25 mẫu ($(51{-}1)/2$) |
| Đáp ứng tại DC (f=0) | ≈ 1,00 (dải thông không suy hao) |
| Đáp ứng tại tần số cắt (f=0,1) | ≈ 0,50 (điểm -6dB kinh điển của windowed-sinc) |
| Đáp ứng dải chặn (f=0,2) | < 0,001 (chặn tốt) |
| Đáp ứng gần Nyquist (f=0,4) | < 0,0001 (gần như triệt tiêu hoàn toàn) |
6. Thực hành: FIR Designer
Kéo tần số cắt và số tap để xem $h[n]$ (đáp ứng xung) và $|H(f)|$ (đáp ứng tần số, thang dB) thay đổi trực
tiếp — cả hai đều tính bằng đúng firLowpassDesign() vừa verify ở trên:
Đáp ứng xung h[n]:
Đáp ứng tần số |H(f)| (dB):
import { firLowpassDesign, hannWindow, firFrequencyResponse, dftMagnitude, magnitudeToDb, firGroupDelay } from './dsp-core.js';
function updateFilter(cutoffNorm, numTaps) {
const h = firLowpassDesign(numTaps, cutoffNorm, hannWindow); // ve h[n]
const H = firFrequencyResponse(h, 2048);
const magDb = magnitudeToDb(dftMagnitude(H), -80); // ve |H(f)| theo dB
const groupDelay = firGroupDelay(numTaps); // luon (numTaps-1)/2
return { h, magDb, groupDelay };
}
Tóm lược
- ✅ FIR = $y[n] = x[n]*h[n]$ với $h[n]$ hữu hạn — trung bình trượt là 1 trường hợp đặc biệt (verified: giảm phương sai tín hiệu nhiễu rõ rệt), nhưng đáp ứng tần số kiểu sinc lởm chởm, chặn dải kém.
- ✅ Low-pass lý tưởng = gạch chữ nhật tần số → sinc vô hạn không nhân quả. Thực dụng: cắt + dịch + cửa sổ (Bài 7) = windowed-sinc.
- ✅ Verified: đối xứng $h[i]=h[N{-}1{-}i]$ sai số <$10^{-9}$ → linear phase → group delay HẰNG SỐ $(N{-}1)/2$ mẫu cho MỌI tần số — dạng sóng không méo, chỉ trễ.
- ✅ Verified: DC≈1, cutoff≈0,5 (-6dB), dải chặn <0,001 — đúng hình dạng windowed-sinc lý thuyết.
- ✅ High-pass (spectral inversion) và band-pass (dịch tần) đều verified, vẫn giữ linear phase.
- ✅ Pitfall: filter càng gắt càng dài → group delay càng lớn — gieo mầm IIR (Bài 11).
Trắc nghiệm ôn tập
Câu 1
Vì sao low-pass "lý tưởng" (gạch chữ nhật trong miền tần số) không thể cài đặt trực tiếp?
Câu 2
Verified: h[i] = h[N-1-i] (đối xứng hoàn hảo) trong windowed-sinc dẫn tới tính chất nào?
Câu 3
Cách thiết kế 1 filter high-pass bằng "spectral inversion" từ 1 low-pass đã có là gì?
Câu 4
Vì sao thiết kế 1 filter FIR có dải chuyển tiếp CÀNG DỐC (chặn dải "gắt" hơn) lại là một đánh đổi?
Tải file code thực hành minh họa bài học
File JavaScript DSPJS (dsp-core.js) — thư viện DSP tự viết dùng xuyên suốt cả
15 bài, Bài 9 vừa thêm firLowpassDesign()/firHighpassDesign()/firBandpassDesign()
(windowed-sinc, tái sử dụng cửa sổ Bài 7), kèm self-test đối chiếu đúng mọi hành vi trong bài (chạy
node dsp-core.js, không cần cài thêm gì):
Bình luận