6.15. Szűrés és spektrogramok¶
A szűrés, a simítás és a magnitúdóspektrumok a nyers FFT szomszédos feladatai: mintafolyam simítása vagy sáváteresztő szűrése, magnitúdóspektrumok kiszámítása egy folyamatos ciklusban lefoglalás nélkül, valamint nyers perifériapufferek újraértelmezése float tömbökként. Az elérhető eszközök:
sosfilt()– kaszkádba kapcsolt másodrendű szekciókon keresztül alkalmazott digitális szűrő.spectrogram()– magnitúdó (abs(fft(...))) közbenső lefoglalások nélkül.A
from_int16_buffer()és a többiulab.utilsfrom_*_buffersegédfüggvény – float tömböt nyer ki egy olyan pufferből, amelynek dtype-ját a beépítettfrombuffer()nem fedi le.
6.15.1. Szűrés sosfilt-tel¶
A sosfilt() egy digitális végtelen impulzusválaszú (IIR) szűrőt alkalmaz másodrendű szekciók (SOS) kaszkádjaként – ez egy numerikusan robusztus forma. A sos 6 hosszúságú szekciók sorozata; az x az 1-D bemenet:
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)
A sos minden sora egy biquad szekció hat együtthatóját tartalmazza: [b0, b1, b2, a0, a1, a2]. A sos tömböt általában egy PC-n előre kiszámítják a scipy.signal.iirfilter(..., output='sos') hívással, majd Python literálként bemásolják a kamera szkriptjébe.
Az opcionális zi= kulcsszó átviszi a szűrő állapotát a pufferek között. Adj át egy (n_sections, 2) alakú kezdeti állapotot, és a függvény visszaadja az (y, zf)-t – a szűrt kimenetet és a végső állapotot –, így az egyik puffer végső állapota a következő kezdeti állapotába kerül:
y0, zf0 = sp.signal.sosfilt(sos, buffer0, zi=zi)
y1, zf1 = sp.signal.sosfilt(sos, buffer1, zi=zf0)
# ...
Ez a szabványos minta egy pufferelt adatokon futó folyamatos szűrőhöz – mikrofonbemenet egyszerre 1024 mintával beolvasva, ADC-minták DMA-vezérelt darabokban felhalmozva, IMU-leolvasások egy ablakon át gyűjtve.
6.15.2. Spektrogramok¶
A spectrogram() kiszámítja a Fourier-transzformáció magnitúdóját. Fogalmilag egyenértékű a np.sqrt(real * real + imag * imag) kifejezéssel egy fft() hívás után, de a munkát egyetlen hívásba sűríti – anélkül, hogy a közbenső real * real, imag * imag, az összeg vagy az explicit magnitúdótömb bármikor is a RAM-ban lenne. Ez teszi a megfelelő eszközzé minden olyan ciklusban, ahol a spektrumokat ismételten számítják ki:
from ulab import numpy as np
from ulab import utils
x = np.linspace(0, 10, num=1024)
spectrum = utils.spectrogram(x)
Az argumentumforma a fft() formáját tükrözi: egy valós tömb, vagy egy (real, imag) pár, ha a bemenetnek van képzetes része.
Három kulcsszavas argumentum segít a lefoglalásban:
scratchpad=None– egy2 * len(signal)hosszúságú 1-D sűrű float tömb, amelyet aspectrogram()munkaterületként használ.out=None– egy 1-D float tömb, amelybe az eredményt írja.log=False– amikorTrue, a visszaadás előtt veszi a magnitúdólog()-ját, ugyanabba a hívásba sűrítve.
A folyamatos minta lényege, hogy mindent egyszer foglalsz le, és soha többé nem foglalsz:
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 ...
Vesd össze a nyilvánvaló, de pazarló változattal:
while True:
signal = read_samples(N)
spectrum = np.log(utils.spectrogram(signal)) # two allocations
Mindkettő ugyanazokat a számokat adja, de az első változat semmit sem foglal le a cikluson belül – a kamera minden iterációban ugyanazt a memóriát használja, és a ciklus gyorsabban fut.
6.15.3. 16 bitnél szélesebb perifériapufferek¶
A frombuffer() csak azokat a dtype-okat kezeli, amelyeket maga a numpy definiál (uint8 / int8, uint16 / int16, float). Amikor egy periféria 32 bites egész mintákat állít elő – egy 24 vagy 32 bites ADC, egy nagy felbontású mikrofon –, a ulab.utils explicit konverziós segédfüggvényeket tesz elérhetővé:
Mindegyik egy bytes-szerű puffert vesz és egy float ndarray-t ad vissza:
from ulab import utils
buf = bytearray([1, 1, 0, 0, 0, 0, 0, 255])
utils.from_uint32_buffer(buf)
# array([257.0, 4278190080.0])
A függvények ugyanazokat a lefoglalást megtakarító beállításokat fogadják el, mint a spectrogram():
count=ésoffset=egy fejléc kihagyásához vagy a beolvasás korlátozásához.out=egy előre lefoglalt float tömbbe való íráshoz.byteswap=Trueamikor a periféria nem egyezik az MCU-val a bájtsorrendben.
A kombinált minta – egyetlen from_int32_buffer() hívás közvetlenül egyetlen spectrogram() hívásba, mindkettő a cikluson kívülről származó out= pufferekkel – a megfelelő sablon egy nagy felbontású mikrofonon futó folyamatos spektrumanalizátorhoz.