6.15. Filtreleme ve spektrogramlar

Filtreleme, yumuşatma ve büyüklük spektrumları, ham bir FFT’nin yanı başındaki işlerdir: bir örnek akışını yumuşatmak veya bant geçiren filtreden geçirmek, tahsis yapmadan akışlı bir döngüde büyüklük spektrumlarını hesaplamak ve ham çevre birimi arabelleklerini float dizileri olarak yeniden yorumlamak. Mevcut araçlar şunlardır:

  • sosfilt() – art arda bağlı ikinci derece bölümler aracılığıyla uygulanan dijital filtre.

  • spectrogram() – ara tahsisler olmadan büyüklük abs(fft(...)).

  • from_int16_buffer() ve diğer ulab.utils from_*_buffer yardımcıları – dtype’ını yerleşik frombuffer() işlevinin kapsamadığı bir arabellekten float dizi çekmek için.

6.15.1. sosfilt ile filtreleme

sosfilt(), dijital bir sonsuz dürtü yanıtı (IIR) filtresini, ikinci derece bölümlerin (SOS) bir art arda dizisi olarak – sayısal açıdan sağlam bir biçimde – uygular. sos, 6 uzunluğunda bölümlerden oluşan bir dizidir; x ise 1 boyutlu giriştir:

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)

sos dizisinin her satırı, bir biquad bölümü için altı katsayı [b0, b1, b2, a0, a1, a2] içerir. sos dizisi genellikle bir PC üzerinde scipy.signal.iirfilter(..., output='sos') ile önceden hesaplanır ve bir Python değişmezi olarak kamera betiğine kopyalanır.

İsteğe bağlı zi= anahtar sözcüğü, filtre durumunu arabellekler arasında taşır. (n_sections, 2) şeklinde bir başlangıç durumu geçirin; işlev (y, zf) döndürür – filtrelenmiş çıkış ve son durum – böylece bir arabelleğin son durumu bir sonrakinin başlangıç durumunu besler:

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

Bu, arabelleğe alınmış veriler üzerinde akışlı bir filtre için standart kalıptır – bir seferde 1024 örnek okunan mikrofon girişi, DMA tabanlı parçalarda biriktirilen ADC örnekleri, bir pencere boyunca toplanan IMU okumaları.

6.15.2. Spektrogramlar

spectrogram(), Fourier dönüşümünün büyüklüğünü hesaplar. Kavramsal olarak, bir fft() çağrısından sonra np.sqrt(real * real + imag * imag) ifadesine eşdeğerdir, ancak işi tek bir çağrıda toplar – ara real * real, imag * imag, toplamı veya açık büyüklük dizisini herhangi bir anda RAM’de tutmadan. Bu da onu, spektrumların tekrar tekrar hesaplandığı herhangi bir döngüde doğru araç yapar:

from ulab import numpy as np
from ulab import utils

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

Argüman biçimi fft() ile aynıdır: tek bir gerçel dizi veya giriş bir sanal kısma sahip olduğunda bir (real, imag) çifti.

Üç anahtar sözcük argümanı tahsiste yardımcı olur:

  • scratchpad=Nonespectrogram() işlevinin çalışma alanı olarak kullandığı, 2 * len(signal) uzunluğunda 1 boyutlu yoğun bir float dizi.

  • out=None – sonucun yazılacağı 1 boyutlu bir float dizi.

  • log=FalseTrue olduğunda, döndürmeden önce büyüklüğün log() değerini alır; bu aynı çağrıya katlanır.

Akış kalıbı, her şeyi bir kez tahsis etmek ve bir daha asla tahsis yapmamaktır:

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

Bariz ama savurgan sürümle karşılaştırın:

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

Her ikisi de aynı sayıları üretir, ancak ilk sürüm döngü içinde hiçbir şey tahsis etmez – kamera her yinelemede aynı belleği kullanmayı sürdürür ve döngü daha hızlı çalışır.

6.15.3. 16 bitten geniş çevre birimi arabellekleri

frombuffer(), yalnızca numpy modülünün kendisinin tanımladığı dtype’ları (uint8 / int8, uint16 / int16, float) işler. Bir çevre birimi 32 bitlik tamsayı örnekler ürettiğinde – 24 veya 32 bitlik bir ADC, yüksek çözünürlüklü bir mikrofon – ulab.utils açık dönüştürme yardımcıları sunar:

Her biri bytes benzeri bir arabellek alır ve bir float ndarray döndürür:

from ulab import utils

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

İşlevler, spectrogram() ile aynı tahsis tasarrufu ayarlarını kabul eder:

  • Bir başlığı atlamak veya okumayı sınırlamak için count= ve offset=.

  • Önceden tahsis edilmiş bir float diziye yazmak için out=.

  • Çevre birimi, bayt sırası konusunda MCU ile anlaşmazlığa düştüğünde byteswap=True.

Birleşik kalıp – döngünün dışından gelen out= arabellekleriyle, doğrudan bir spectrogram() çağrısına giden tek bir from_int32_buffer() çağrısı – yüksek çözünürlüklü bir mikrofon üzerinde çalışan akışlı bir spektrum çözümleyicisi için doğru şablondur.