Pada chapter fftfreq dan Spektrum Frekuensi kita membaca spektrum sinyal yang frekuensinya jatuh tepat di sebuah bin, sehingga puncaknya rapi. Kenyataan pengukuran jarang sekali seberes itu: sensor getaran dengan laju sampling 100 Hz yang merekam 256 sampel hanya menjangkau 2,56 detik, sedangkan komponen getaran mesin bisa berada di 10,3 Hz — jumlah periode dalam jendela rekaman tidak bulat.
Akibatnya energi sinus tidak terkumpul di satu bin, melainkan menyebar ke bin-bin tetangganya: puncak melebar dan muncul ekor di sekitarnya. Fenomena ini disebut kebocoran spektral (spectral leakage). Chapter ini menunjukkan asal kebocoran pada sinus terpotong, lalu meredamnya dengan window function — deret penimbang yang memperhalus ujung sinyal sebelum FFT dihitung.
Yang kita kerjakan pada chapter ini:
- Mengapa sinus yang tidak genap periode dalam jendela bocor ke bin tetangga
- Membandingkan spektrum tanpa window vs dengan
np.hanningsecara kuantitatif - Mengenal
np.hanning,np.hamming,np.blackman, dannp.kaiserbeserta peran parameterbeta
Mengapa Sinus Terpotong Bocor #
np.fft.fft bekerja di atas satu asumsi diam-diam: N sampel yang diberikan adalah satu periode utuh dari sinyal yang berulang tanpa henti. Bila sinyal di dalam jendela menuntaskan jumlah periode bulat, ujung jendela bertemu dengan mulus saat “dijahit” ke sampel pertama. Bila tidak — 10,3 Hz selama 2,56 detik berarti 26,368 periode — sambungannya melompat, dan FFT membaca lompatan itu sebagai komponen frekuensi lain yang tersebar. Ingat pula pembagian binnya: bin ke-k mewakili frekuensi k × fs/N; pada fs = 100 dan N = 256 jarak antar bin 0,390625 Hz, sementara 10,3 Hz jatuh di sela bin 26 dan 27.
import numpy as np
fs, N = 100.0, 256
t = np.arange(N) / fs
sinyal = np.sin(2 * np.pi * 10.3 * t) # tidak genap periode dalam jendela
mag = np.abs(np.fft.fft(sinyal))
freq = np.fft.fftfreq(N, d=1/fs)
puncak = int(np.argmax(mag))
print("bin puncak :", puncak, "=", freq[puncak], "Hz")
print("jarak antar bin :", fs / N, "Hz")
print("mag sekitar puncak:", mag[puncak - 3:puncak + 4].round(1))Output yang Dihasilkan:
bin puncak : 26 = 10.15625 Hz
jarak antar bin : 0.390625 Hz
mag sekitar puncak: [ 11.2 15.8 27.3 101.4 58.9 22.8 14.1]Mari kita telaah baris demi baris:
- Baris 5 (
sinyal = np.sin(...)): sinus beramplitudo 1 pada 10,3 Hz; bentuk periodanya dari Fungsi Trigonometri. Dalam 2,56 detik sinyal menempuh 26,368 periode — bukan bilangan bulat. - Baris 8 (
puncak = int(np.argmax(mag))):np.argmaxmencari indeks magnitude terbesar; hasilnya bin 26, bin yang paling dekat dengan 10,3 Hz. - Baris 9–10: frekuensi bin 26 adalah 10,15625 Hz, hasil 26 × 0,390625.
Perhatikan angkanya: pada sinus yang jatuh tepat di bin, magnitude puncaknya 128 (N/2 × amplitudo 1, seperti di Dari DFT ke FFT). Di sini puncak hanya 101,4, dan enam bin tetangganya memuat 11,2 sampai 58,9 — energi sinus tunggal tumpah ke banyak bin. Inilah wajah kebocoran spektral: puncak mengecil sekaligus melebar.
Meredam Bocoran dengan np.hanning #
Window function mengalikan sinyal dengan deret penimbang yang kecil di ujung dan penuh di tengah. Ujung sinyal diredam mendekati nol sehingga “jahitan” periodiknya menjadi mulus; lompatan di tepi jendela hilang, dan energi terkumpul di sekitar puncak utama. np.hanning(N) menghasilkan jendela berbentuk setengah gelombang kosinus — bentuk yang akrab dari Fungsi Trigonometri.
Kita ukur efeknya dengan satu angka: proporsi magnitude di luar pita pengaman puncak — puncak beserta tiga bin tetangganya tiap sisi — pada separuh frekuensi positif.
import numpy as np
fs, N = 100.0, 256
t = np.arange(N) / fs
sinyal = np.sin(2 * np.pi * 10.3 * t)
freq = np.fft.fftfreq(N, d=1/fs)
pos = freq > 0
puncak = 26 # bin puncak dari blok sebelumnya
luar = pos.copy()
luar[puncak - 3:puncak + 4] = False # buang puncak + 3 tetangga tiap sisi
for nama, jendela in [("persegi", np.ones(N)), ("hanning", np.hanning(N))]:
mag = np.abs(np.fft.fft(sinyal * jendela))
rasio = mag[luar].sum() / mag[pos].sum()
print(f"{nama:>8}: mag puncak {mag[puncak]:.1f} | rasio luar {rasio:.4f}")Output yang Dihasilkan:
persegi: mag puncak 101.4 | rasio luar 0.4680
hanning: mag puncak 58.4 | rasio luar 0.0119Uraian blok di atas:
- Baris 7 (
pos = freq > 0): analisis dibatasi ke frekuensi positif agar puncak cermin pada frekuensi negatif tidak ikut terhitung. - Baris 9–10 (
luar = ...):luarmenyalin maskpos, lalu mematikan tujuh bin di sekitar puncak; rasio menjadi tolok ukur bocoran yang adil. - Baris 11–14: jendela
"persegi"(semua nilai 1, artinya tanpa window) dibandingkan dengannp.hanning(N)pada sinyal yang sama.
Hasilnya tegas: rasio bocoran turun dari 0,4680 menjadi 0,0119 — sekitar 39 kali lebih kecil. Harganya terlihat di kolom mag puncak: 101,4 menjadi 58,4, karena jendela mengecilkan rata-rata amplitudo.
Hanning, Hamming, Blackman, dan Kaiser #
NumPy menyediakan empat window function yang sering dipakai. Semuanya menerima panjang M dan mengembalikan array float64 berisi penimbang dari 0 sampai 1.
import numpy as np
M = 10
print("hanning :", np.hanning(M).round(3))
print("hamming :", np.hamming(M).round(3))
print("blackman:", np.blackman(M).round(3))
print("kaiser :", np.kaiser(M, 6).round(3))Output yang Dihasilkan:
hanning : [0. 0.117 0.413 0.75 0.97 0.97 0.75 0.413 0.117 0. ]
hamming : [0.08 0.188 0.46 0.77 0.972 0.972 0.77 0.46 0.188 0.08 ]
blackman: [-0. 0.051 0.258 0.63 0.951 0.951 0.63 0.258 0.051 -0. ]
kaiser : [0.015 0.138 0.401 0.732 0.967 0.967 0.732 0.401 0.138 0.015]Semua jendela menekan ujung, dengan gaya berbeda: np.hanning sampai nol persis, np.hamming berhenti di sekitar 0,08, np.blackman paling dalam, dan np.kaiser(M, beta) bentuknya diatur lewat parameter beta. Nilai -0. pada blackman hanyalah negatif-nol hasil pembulatan, artefak yang sudah kita kenal di Dari DFT ke FFT. Ringkasan karakternya (kualitatif; angka desibel adalah nilai buku teks untuk jendela panjang):
| Window | Mainlobe | Sidelobe | Catatan pemakaian |
|---|---|---|---|
| persegi (tanpa window) | paling sempit | tertinggi (≈ −13 dB) | pembanding awal |
np.hanning | sedang | rendah (≈ −31 dB) | pilihan umum serbaguna |
np.hamming | sedang | lebih rendah (≈ −42 dB) | sidelobe jauh lebih kecil |
np.blackman | lebar | sangat rendah (≈ −58 dB) | saat sidelobe kritis |
np.kaiser(M, beta) | diatur beta | diatur beta | fleksibel lewat satu parameter |
Tabel itu memperlihatkan pertukaran yang tidak bisa dihindari: jendela yang menekan sidelobe lebih dalam selalu memperlebar mainlobe (puncak utama), sehingga dua sinus berdekatan lebih sulit dipisahkan.
np.kaiser mengemas pertukaran itu ke dalam satu angka: beta = 0 menghasilkan jendela persegi, beta makin besar menekan sidelobe makin dalam dengan mainlobe yang ikut melebar. Kita uji pada sinyal 10,3 Hz yang sama:
import numpy as np
fs, N = 100.0, 256
t = np.arange(N) / fs
sinyal = np.sin(2 * np.pi * 10.3 * t)
freq = np.fft.fftfreq(N, d=1/fs)
pos = freq > 0
puncak = 26
luar = pos.copy()
luar[puncak - 3:puncak + 4] = False
for beta in [0, 4, 8, 14]:
mag = np.abs(np.fft.fft(sinyal * np.kaiser(N, beta)))
rasio = mag[luar].sum() / mag[pos].sum()
print(f"kaiser beta={beta:>2}: rasio = {rasio:.4f}")Output yang Dihasilkan:
kaiser beta= 0: rasio = 0.4680
kaiser beta= 4: rasio = 0.1255
kaiser beta= 8: rasio = 0.0031
kaiser beta=14: rasio = 0.0017Windowing adalah langkah pra-FFT yang murah: satu perkalian array sebelum np.fft.fft dipanggil. Pilih jendela sesuai kebutuhan — mainlobe sempit untuk memisahkan frekuensi berdekatan, sidelobe rendah untuk membaca komponen kecil dekat komponen besar.