6.15. Filtrare și spectrograme¶
Filtrarea, netezirea și spectrele de magnitudine sunt sarcinile adiacente unei FFT brute: netezirea sau filtrarea trece-bandă a unui flux de eșantioane, calcularea spectrelor de magnitudine într-o buclă de tip flux fără alocare și reinterpretarea tampoanelor (buffer) brute ale perifericelor ca tablouri de numere în virgulă mobilă. Instrumentele disponibile:
sosfilt()– filtru digital aplicat prin secțiuni de ordinul doi în cascadă.spectrogram()– magnitudineabs(fft(...))fără alocări intermediare.from_int16_buffer()și celelalte funcții ajutătoarefrom_*_bufferdinulab.utils– extrag un tablou de numere în virgulă mobilă dintr-un tampon (buffer) al cărui dtype nu este acoperit de funcția încorporatăfrombuffer().
6.15.1. Filtrare cu sosfilt¶
sosfilt() aplică un filtru digital cu răspuns infinit la impuls (IIR) sub forma unei cascade de secțiuni de ordinul doi (SOS) – o formă robustă numeric. sos este o secvență de secțiuni de lungime 6; x este intrarea 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)
Fiecare rând din sos conține șase coeficienți [b0, b1, b2, a0, a1, a2] pentru o secțiune biquad. Tabloul sos este de obicei precalculat pe un PC cu scipy.signal.iirfilter(..., output='sos') și copiat în scriptul camerei ca un literal Python.
Cuvântul-cheie opțional zi= transportă starea filtrului între tampoane (buffer). Transmite o stare inițială de forma (n_sections, 2) și funcția returnează (y, zf) – ieșirea filtrată și starea finală – astfel încât starea finală a unui tampon (buffer) alimentează starea inițială a celui următor:
y0, zf0 = sp.signal.sosfilt(sos, buffer0, zi=zi)
y1, zf1 = sp.signal.sosfilt(sos, buffer1, zi=zf0)
# ...
Acesta este modelul standard pentru un filtru de tip flux aplicat datelor tamponate – intrare de microfon citită câte 1024 de eșantioane odată, eșantioane ADC acumulate în fragmente acționate prin DMA, citiri IMU colectate pe o fereastră.
6.15.2. Spectrograme¶
spectrogram() calculează magnitudinea transformatei Fourier. Este conceptual echivalentă cu np.sqrt(real * real + imag * imag) după un apel la fft(), dar comprimă munca într-un singur apel – fără a păstra în RAM, în niciun moment, valorile intermediare real * real, imag * imag, suma sau tabloul de magnitudine explicit. Acest lucru o face instrumentul potrivit în orice buclă în care spectrele sunt calculate în mod repetat:
from ulab import numpy as np
from ulab import utils
x = np.linspace(0, 10, num=1024)
spectrum = utils.spectrogram(x)
Forma argumentelor reflectă fft(): un singur tablou real sau o pereche (real, imag) atunci când intrarea are o parte imaginară.
Trei argumente de tip cuvânt-cheie ajută la alocare:
scratchpad=None– un tablou dens 1-D de numere în virgulă mobilă, de lungime2 * len(signal), pe carespectrogram()îl folosește drept spațiu de lucru.out=None– un tablou 1-D de numere în virgulă mobilă în care se scrie rezultatul.log=False– când esteTrue, se aplicălog()magnitudinii înainte de returnare, comprimat în același apel.
Modelul de tip flux constă în a aloca totul o singură dată și a nu mai aloca niciodată:
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 ...
Comparați cu versiunea evidentă, dar risipitoare:
while True:
signal = read_samples(N)
spectrum = np.log(utils.spectrogram(signal)) # two allocations
Ambele produc aceleași numere, dar prima versiune nu alocă nimic în interiorul buclei – camera păstrează aceeași memorie în uz la fiecare iterație, iar bucla rulează mai repede.
6.15.3. Tampoane de periferice mai late de 16 biți¶
frombuffer() gestionează doar dtype-urile pe care numpy însuși le definește (uint8 / int8, uint16 / int16, float). Când un periferic produce eșantioane de numere întregi pe 32 de biți – un ADC pe 24 sau 32 de biți, un microfon de înaltă rezoluție – ulab.utils expune funcții ajutătoare explicite de conversie:
Fiecare primește un tampon (buffer) de tip bytes și returnează un ndarray de numere în virgulă mobilă:
from ulab import utils
buf = bytearray([1, 1, 0, 0, 0, 0, 0, 255])
utils.from_uint32_buffer(buf)
# array([257.0, 4278190080.0])
Funcțiile acceptă aceleași opțiuni de economisire a alocării ca spectrogram():
count=șioffset=pentru a sări un antet sau a limita citirea.out=pentru a scrie într-un tablou de numere în virgulă mobilă prealocat.byteswap=Truecând perifericul nu este de acord cu MCU asupra ordinii octeților.
Modelul combinat – un apel from_int32_buffer() direct într-un apel spectrogram(), ambele cu tampoane (buffer) out= din afara buclei – este șablonul potrivit pentru un analizor de spectru de tip flux care rulează pe un microfon de înaltă rezoluție.