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.