6.15. Filtering dan spektrogram#
Filtering, penghalusan, dan spektrum magnitudo adalah pekerjaan yang berdekatan dengan FFT mentah: menghaluskan atau melakukan band-pass-filtering pada aliran sampel, menghitung spektrum magnitudo dalam loop streaming tanpa alokasi, dan menginterpretasi ulang buffer periferal mentah sebagai array float. Alat-alat yang tersedia:
sosfilt()-- filter digital yang diterapkan melalui seksi orde kedua bertingkat.spectrogram()-- magnitudoabs(fft(...))tanpa alokasi perantara.from_int16_buffer()dan pembantuulab.utilsfrom_*_bufferlainnya -- mengambil array float dari buffer yang dtypenya tidak dicakup olehfrombuffer()bawaan.
6.15.1. Filtering dengan sosfilt#
sosfilt() menerapkan filter digital infinite impulse response (IIR) sebagai kaskade second-order sections (SOS) -- bentuk yang stabil secara numerik. sos adalah urutan seksi dengan panjang 6; x adalah masukan 1-D:
from ulab import numpy as np
from ulab import scipy as sp
x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
sos = [[1, 2, 3, 1, 5, 6],
[1, 2, 3, 1, 5, 6]]
y = sp.signal.sosfilt(sos, x)
Setiap baris dari sos menyimpan enam koefisien [b0, b1, b2, a0, a1, a2] untuk satu seksi biquad. Array sos biasanya dihitung terlebih dahulu di PC dengan scipy.signal.iirfilter(..., output='sos') dan disalin ke skrip kamera sebagai literal Python.
Kata kunci opsional zi= membawa status filter antar buffer. Berikan status awal dengan bentuk (n_sections, 2) dan fungsi mengembalikan (y, zf) -- keluaran yang difilter dan status akhir -- sehingga status akhir satu buffer menjadi status awal buffer berikutnya:
y0, zf0 = sp.signal.sosfilt(sos, buffer0, zi=zi)
y1, zf1 = sp.signal.sosfilt(sos, buffer1, zi=zf0)
# ...
Ini adalah pola standar untuk filter streaming pada data yang di-buffer -- masukan mikrofon dibaca 1024 sampel sekaligus, sampel ADC dikumpulkan dalam potongan yang didorong DMA, pembacaan IMU dikumpulkan selama satu jendela.
6.15.2. Spektrogram#
spectrogram() menghitung magnitudo dari transformasi Fourier. Secara konseptual setara dengan np.sqrt(real * real + imag * imag) setelah panggilan ke fft(), tetapi menggabungkan pekerjaan dalam satu panggilan -- tanpa menyimpan real * real, imag * imag, jumlah, atau array magnitudo eksplisit di RAM pada titik mana pun. Itu menjadikannya alat yang tepat dalam loop apa pun di mana spektrum dihitung berulang kali:
from ulab import numpy as np
from ulab import utils
x = np.linspace(0, 10, num=1024)
spectrum = utils.spectrogram(x)
Bentuk argumen mencerminkan fft(): satu array real, atau pasangan (real, imag) ketika masukan memiliki bagian imajiner.
Tiga argumen kata kunci membantu dengan alokasi:
scratchpad=None-- array float padat 1-D dengan panjang2 * len(signal)yang digunakanspectrogram()untuk ruang kerja.out=None-- array float 1-D untuk menulis hasil ke dalamnya.log=False-- ketikaTrue, ambillog()dari magnitudo sebelum dikembalikan, dilipat ke dalam panggilan yang sama.
Pola streaming adalah mengalokasikan semuanya sekali dan tidak pernah mengalokasikan lagi:
from ulab import numpy as np
from ulab import utils
N = 1024
scratch = np.zeros(2 * N)
out = np.zeros(N)
while True:
signal = read_samples(N)
utils.spectrogram(signal, out=out, scratchpad=scratch,
log=True)
# out now holds the log-magnitude spectrum for this window ...
Bandingkan dengan versi yang jelas namun boros:
while True:
signal = read_samples(N)
spectrum = np.log(utils.spectrogram(signal)) # two allocations
Keduanya menghasilkan angka yang sama, tetapi versi pertama tidak mengalokasikan apa pun di dalam loop -- kamera mempertahankan memori yang sama digunakan setiap iterasi, dan loop berjalan lebih cepat.
6.15.3. Buffer periferal lebih lebar dari 16-bit#
frombuffer() hanya menangani dtype yang didefinisikan numpy itu sendiri (uint8 / int8, uint16 / int16, float). Ketika periferal menghasilkan sampel integer 32-bit -- ADC 24- atau 32-bit, mikrofon resolusi tinggi -- ulab.utils mengekspos pembantu konversi eksplisit:
Masing-masing menerima buffer seperti bytes dan mengembalikan ndarray float:
from ulab import utils
buf = bytearray([1, 1, 0, 0, 0, 0, 0, 255])
utils.from_uint32_buffer(buf)
# array([257.0, 4278190080.0])
Fungsi-fungsi tersebut menerima knop penghemat alokasi yang sama seperti spectrogram():
count=danoffset=untuk melewati header atau membatasi pembacaan.out=untuk menulis ke array float yang sudah dialokasikan.byteswap=Trueketika periferal tidak setuju dengan MCU tentang urutan byte.
Pola kombinasi -- satu panggilan from_int32_buffer() langsung ke satu panggilan spectrogram(), keduanya dengan buffer out= dari luar loop -- adalah template yang tepat untuk analis spektrum streaming yang berjalan pada mikrofon resolusi tinggi.