6.15. Filtriranje i spektrogrami

Filtriranje, izglađivanje i spektri magnitude poslovi su susjedni sirovom FFT-u: izglađivanje ili pojasno filtriranje toka uzoraka, izračunavanje spektara magnitude u petlji za tok podataka bez dodjele memorije te ponovno tumačenje sirovih međuspremnika periferije kao polja s pomičnim zarezom. Dostupni alati:

  • sosfilt() – digitalni filtar primijenjen putem kaskadno povezanih sekcija drugog reda.

  • spectrogram() – magnituda abs(fft(...)) bez međudodjela memorije.

  • from_int16_buffer() i ostali pomoćnici ulab.utils from_*_buffer – izvuku polje s pomičnim zarezom iz međuspremnika čiji dtype ugrađeni frombuffer() ne pokriva.

6.15.1. Filtriranje s sosfilt

sosfilt() primjenjuje digitalni filtar beskonačnog impulsnog odziva (IIR) kao kaskadu sekcija drugog reda (SOS) – numerički robustan oblik. sos je niz sekcija duljine 6; x je 1-D ulaz:

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)

Svaki redak polja sos sadrži šest koeficijenata [b0, b1, b2, a0, a1, a2] za jednu biquad sekciju. Polje sos obično se unaprijed izračunava na računalu pomoću scipy.signal.iirfilter(..., output='sos') i kopira u skriptu kamere kao Python literal.

Neobavezna ključna riječ zi= prenosi stanje filtra preko međuspremnika. Proslijedite početno stanje oblika (n_sections, 2) i funkcija vraća (y, zf) – filtrirani izlaz i konačno stanje – tako da konačno stanje jednog međuspremnika napaja početno stanje sljedećeg:

y0, zf0 = sp.signal.sosfilt(sos, buffer0, zi=zi)
y1, zf1 = sp.signal.sosfilt(sos, buffer1, zi=zf0)
# ...

Ovo je standardni obrazac za streaming filtar na međuspremljenim podacima – ulaz mikrofona čitan po 1024 uzorka odjednom, ADC uzorci akumulirani u DMA-pogonjenim dijelovima, IMU očitanja prikupljena tijekom prozora.

6.15.2. Spektrogrami

spectrogram() izračunava magnitudu Fourierove transformacije. Konceptualno je istovjetna s np.sqrt(real * real + imag * imag) nakon poziva fft(), ali sažima posao u jedan poziv – bez zadržavanja međurezultata real * real, imag * imag, zbroja ili eksplicitnog polja magnitude u RAM-u u bilo kojem trenutku. To je čini pravim alatom u svakoj petlji u kojoj se spektri izračunavaju iznova i iznova:

from ulab import numpy as np
from ulab import utils

x        = np.linspace(0, 10, num=1024)
spectrum = utils.spectrogram(x)

Oblik argumenata zrcali fft(): jedno realno polje ili par (real, imag) kada ulaz ima imaginarni dio.

Tri ključna argumenta pomažu kod dodjele memorije:

  • scratchpad=None – 1-D gusto polje s pomičnim zarezom duljine 2 * len(signal) koje spectrogram() koristi kao radni prostor.

  • out=None – 1-D polje s pomičnim zarezom u koje se zapisuje rezultat.

  • log=False – kada je True, prije vraćanja uzima log() magnitude, sažeto u isti poziv.

Obrazac za tok podataka jest sve dodijeliti jednom i nikada više ne dodjeljivati:

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 ...

Usporedite s očitom, ali rasipnom inačicom:

while True:
    signal   = read_samples(N)
    spectrum = np.log(utils.spectrogram(signal))   # two allocations

Obje proizvode iste brojeve, ali prva inačica ništa ne dodjeljuje unutar petlje – kamera u svakoj iteraciji koristi istu memoriju, a petlja se izvodi brže.

6.15.3. Međuspremnici periferije širi od 16 bita

frombuffer() obrađuje samo dtypeove koje numpy sam definira (uint8 / int8, uint16 / int16, float). Kada periferija proizvodi 32-bitne cjelobrojne uzorke – 24-bitni ili 32-bitni ADC, mikrofon visoke razlučivosti – ulab.utils izlaže eksplicitne pomoćnike za pretvorbu:

Svaki uzima međuspremnik nalik bajtovima i vraća ndarray s pomičnim zarezom:

from ulab import utils

buf = bytearray([1, 1, 0, 0, 0, 0, 0, 255])
utils.from_uint32_buffer(buf)
# array([257.0, 4278190080.0])

Funkcije prihvaćaju iste regulatore za uštedu memorije kao spectrogram():

  • count= i offset= za preskakanje zaglavlja ili ograničavanje čitanja.

  • out= za zapisivanje u unaprijed dodijeljeno polje s pomičnim zarezom.

  • byteswap=True kada se periferija ne slaže s MCU-om oko redoslijeda bajtova.

Kombinirani obrazac – jedan poziv from_int32_buffer() izravno u jedan poziv spectrogram(), oba s out= međuspremnicima izvan petlje – pravi je predložak za streaming analizator spektra koji radi na mikrofonu visoke razlučivosti.