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:

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 panjang 2 * len(signal) yang digunakan spectrogram() untuk ruang kerja.

  • out=None -- array float 1-D untuk menulis hasil ke dalamnya.

  • log=False -- ketika True, ambil log() 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= dan offset= untuk melewati header atau membatasi pembacaan.

  • out= untuk menulis ke array float yang sudah dialokasikan.

  • byteswap=True ketika 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.