6.15. الترشيح والأطياف الطيفية#
الترشيح والتنعيم وأطياف المقدار هي المهام المجاورة لتحويلة الـ FFT الخام: تنعيم تدفق من العينات أو ترشيحه بمرشح تمرير نطاقي، وحساب أطياف المقدار في حلقة بثّية دون تخصيص، وإعادة تفسير المخازن المؤقتة الخام للطرفيات بوصفها مصفوفات عشرية. والأدوات المتاحة:
sosfilt()-- مرشح رقمي يُطبَّق عبر مقاطع متتالية من الرتبة الثانية.spectrogram()-- المقدارabs(fft(...))دون تخصيصات وسيطة.from_int16_buffer()وسائر الدوال المساعدةfrom_*_bufferفيulab.utils-- تستخلص مصفوفة عشرية من مخزن مؤقت لا تغطي الدالة المدمجةfrombuffer()نوعَ بياناته.
6.15.1. الترشيح باستخدام sosfilt#
تُطبّق sosfilt() مرشحًا رقميًا لاستجابة نبضية لانهائية (IIR) على هيئة سلسلة من مقاطع الرتبة الثانية (SOS) -- وهي صيغة متينة عدديًا. والـ sos هو متتالية من المقاطع ذات الطول 6؛ والـ x هو الدخل أحادي البُعد:
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 ستة معاملات [b0, b1, b2, a0, a1, a2] لمقطع biquad واحد. وعادةً ما تُحسب مصفوفة sos مسبقًا على حاسوب شخصي باستخدام scipy.signal.iirfilter(..., output='sos') ثم تُنسخ إلى برنامج الكاميرا النصي على هيئة قيمة Python حرفية.
تحمل الكلمة المفتاحية الاختيارية zi= حالةَ المرشح عبر المخازن المؤقتة. مرِّر حالةً ابتدائيةً بالشكل (n_sections, 2) فتُعيد الدالة (y, zf) -- الخرج المُرشَّح والحالة النهائية -- بحيث تُغذّي الحالةُ النهائيةُ لمخزن مؤقت الحالةَ الابتدائيةَ للمخزن التالي:
y0, zf0 = sp.signal.sosfilt(sos, buffer0, zi=zi)
y1, zf1 = sp.signal.sosfilt(sos, buffer1, zi=zf0)
# ...
هذا هو النمط القياسي لمرشح بثّي يعمل على بيانات مُخزّنة مؤقتًا -- دخل ميكروفون يُقرأ بمعدل 1024 عينة في المرة، وعينات ADC مُجمّعة في قطع مدفوعة بالـ DMA، وقراءات IMU مجموعة عبر نافذة.
6.15.2. الأطياف الطيفية#
تحسب spectrogram() مقدار تحويلة فورييه. وهي مكافئة مفاهيميًا لـ np.sqrt(real * real + imag * imag) بعد استدعاء fft()، لكنها تطوي العمل في استدعاء واحد -- دون الاحتفاظ بـ real * real الوسيط، أو imag * imag، أو المجموع، أو مصفوفة المقدار الصريحة في الـ RAM في أي لحظة. وهذا يجعلها الأداة الصحيحة في أي حلقة تُحسب فيها الأطياف بشكل متكرر:
from ulab import numpy as np
from ulab import utils
x = np.linspace(0, 10, num=1024)
spectrum = utils.spectrogram(x)
تعكس صيغة الوسيط صيغة fft(): مصفوفة حقيقية واحدة، أو زوج (real, imag) عندما يكون للدخل جزء تخيلي.
تساعد ثلاث كلمات مفتاحية في التخصيص:
scratchpad=None-- مصفوفة عشرية كثيفة أحادية البُعد بطول2 * len(signal)تستخدمهاspectrogram()كمساحة عمل.out=None-- مصفوفة عشرية أحادية البُعد تُكتب النتيجة فيها.log=False-- عندTrue، تأخذlog()للمقدار قبل الإعادة، مطويةً في الاستدعاء نفسه.
النمط البثّي هو تخصيص كل شيء مرةً واحدةً وعدم التخصيص مجددًا أبدًا:
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 ...
قارن بالنسخة البديهية لكن المُسرِفة:
while True:
signal = read_samples(N)
spectrum = np.log(utils.spectrogram(signal)) # two allocations
تنتج النسختان الأرقامَ نفسها، لكن النسخة الأولى لا تخصص أي شيء داخل الحلقة -- إذ تُبقي الكاميرا الذاكرةَ نفسَها قيد الاستخدام في كل تكرار، وتعمل الحلقة بسرعة أكبر.
6.15.3. المخازن المؤقتة للطرفيات الأعرض من 16 بت#
لا تتعامل frombuffer() إلا مع أنواع البيانات التي تعرّفها numpy نفسها (uint8 / int8، و uint16 / int16، و float). وعندما تنتج طرفيةٌ عيناتٍ صحيحةً بطول 32 بت -- مثل ADC بطول 24 أو 32 بت، أو ميكروفون عالي الدقة -- تكشف ulab.utils دوالًا مساعدةً صريحةً للتحويل:
تأخذ كلٌّ منها مخزنًا مؤقتًا شبيهًا بالبايتات وتُعيد ndarray عشرية:
from ulab import utils
buf = bytearray([1, 1, 0, 0, 0, 0, 0, 255])
utils.from_uint32_buffer(buf)
# array([257.0, 4278190080.0])
تقبل الدوال مقابض توفير التخصيص نفسها التي تقبلها spectrogram():
count=وoffset=لتخطّي ترويسة أو لتحديد القراءة.out=للكتابة في مصفوفة عشرية مُخصّصة مسبقًا.byteswap=Trueعندما تختلف الطرفية عن الـ MCU في ترتيب البايتات.
النمط المُدمج -- استدعاء from_int32_buffer() واحد يصبّ مباشرةً في استدعاء spectrogram() واحد، وكلاهما بمخازن out= مأخوذة من خارج الحلقة -- هو القالب الصحيح لمحلل طيفي بثّي يعمل على ميكروفون عالي الدقة.