Расшифровка FFT библиотек.
- Войдите на сайт для отправки комментариев
Сб, 02/05/2015 - 17:41
Форумчане, взявшись за повторение работы смысл которой заключается в создании анализатора спектра звука и преобразовании его в ряд сигналов, подоваемых, например, на светодиодную матрицу (типа эквалайзер) столкнулся с вопросом, а что вообще содержит код fft библиотек. В итоге (после усвоения информации о быстром преобразованнии Фурье, написания кода и сборки рабочего устройства) хочу создать корректно работающюю систему. Кто может "разложить всё по полочкам" - пояснить содержание fft библиотек?
/*Основная fix_fft.h библиотека*/ #ifndef FIXFFT_H #define FIXFFT_H #include <WProgram.h> /* fix_fft() - perform forward/inverse fast Fourier transform. fr[n],fi[n] are real and imaginary arrays, both INPUT AND RESULT (in-place FFT), with 0 <= n < 2**m; set inverse to 0 for forward transform (FFT), or 1 for iFFT. */ int fix_fft(char fr[], char fi[], int m, int inverse); /* fix_fftr() - forward/inverse FFT on array of real numbers. Real FFT/iFFT using half-size complex FFT by distributing even/odd samples into real/imaginary arrays respectively. In order to save data space (i.e. to avoid two arrays, one for real, one for imaginary samples), we proceed in the following two steps: a) samples are rearranged in the real array so that all even samples are in places 0-(N/2-1) and all imaginary samples in places (N/2)-(N-1), and b) fix_fft is called with fr and fi pointing to index 0 and index N/2 respectively in the original array. The above guarantees that fix_fft "sees" consecutive real samples as alternating real and imaginary samples in the complex array. */ int fix_fftr(char f[], int m, int inverse); #endif
/*Ещё fix_fft.cpp библиотека*/ #include <avr/pgmspace.h> #include "fix_fft.h" #include <WProgram.h> /* fix_fft.c - Fixed-point in-place Fast Fourier Transform */ /* All data are fixed-point short integers, in which -32768 to +32768 represent -1.0 to +1.0 respectively. Integer arithmetic is used for speed, instead of the more natural floating-point. For the forward FFT (time -> freq), fixed scaling is performed to prevent arithmetic overflow, and to map a 0dB sine/cosine wave (i.e. amplitude = 32767) to two -6dB freq coefficients. The return value is always 0. For the inverse FFT (freq -> time), fixed scaling cannot be done, as two 0dB coefficients would sum to a peak amplitude of 64K, overflowing the 32k range of the fixed-point integers. Thus, the fix_fft() routine performs variable scaling, and returns a value which is the number of bits LEFT by which the output must be shifted to get the actual amplitude (i.e. if fix_fft() returns 3, each value of fr[] and fi[] must be multiplied by 8 (2**3) for proper scaling. Clearly, this cannot be done within fixed-point short integers. In practice, if the result is to be used as a filter, the scale_shift can usually be ignored, as the result will be approximately correctly normalized as is. Written by: Tom Roberts 11/8/89 Made portable: Malcolm Slaney 12/15/94 malcolm@interval.com Enhanced: Dimitrios P. Bouras 14 Jun 2006 dbouras@ieee.org Modified for 8bit values David Keller 10.10.2010 */ #define N_WAVE 256 /* full length of Sinewave[] */ #define LOG2_N_WAVE 8 /* log2(N_WAVE) */ /* Since we only use 3/4 of N_WAVE, we define only this many samples, in order to conserve data space. */ const prog_int8_t Sinewave[N_WAVE-N_WAVE/4] PROGMEM = { 0, 3, 6, 9, 12, 15, 18, 21, 24, 28, 31, 34, 37, 40, 43, 46, 48, 51, 54, 57, 60, 63, 65, 68, 71, 73, 76, 78, 81, 83, 85, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 109, 111, 112, 114, 115, 117, 118, 119, 120, 121, 122, 123, 124, 124, 125, 126, 126, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 126, 126, 125, 124, 124, 123, 122, 121, 120, 119, 118, 117, 115, 114, 112, 111, 109, 108, 106, 104, 102, 100, 98, 96, 94, 92, 90, 88, 85, 83, 81, 78, 76, 73, 71, 68, 65, 63, 60, 57, 54, 51, 48, 46, 43, 40, 37, 34, 31, 28, 24, 21, 18, 15, 12, 9, 6, 3, 0, -3, -6, -9, -12, -15, -18, -21, -24, -28, -31, -34, -37, -40, -43, -46, -48, -51, -54, -57, -60, -63, -65, -68, -71, -73, -76, -78, -81, -83, -85, -88, -90, -92, -94, -96, -98, -100, -102, -104, -106, -108, -109, -111, -112, -114, -115, -117, -118, -119, -120, -121, -122, -123, -124, -124, -125, -126, -126, -127, -127, -127, -127, -127, /*-127, -127, -127, -127, -127, -127, -126, -126, -125, -124, -124, -123, -122, -121, -120, -119, -118, -117, -115, -114, -112, -111, -109, -108, -106, -104, -102, -100, -98, -96, -94, -92, -90, -88, -85, -83, -81, -78, -76, -73, -71, -68, -65, -63, -60, -57, -54, -51, -48, -46, -43, -40, -37, -34, -31, -28, -24, -21, -18, -15, -12, -9, -6, -3, */ }; /* FIX_MPY() - fixed-point multiplication & scaling. Substitute inline assembly for hardware-specific optimization suited to a particluar DSP processor. Scaling ensures that result remains 16-bit. */ inline char FIX_MPY(char a, char b) { //Serial.println(a); //Serial.println(b); /* shift right one less bit (i.e. 15-1) */ int c = ((int)a * (int)b) >> 6; /* last bit shifted out = rounding-bit */ b = c & 0x01; /* last shift + rounding bit */ a = (c >> 1) + b; /* Serial.println(Sinewave[3]); Serial.println(c); Serial.println(a); while(1);*/ return a; } /* fix_fft() - perform forward/inverse fast Fourier transform. fr[n],fi[n] are real and imaginary arrays, both INPUT AND RESULT (in-place FFT), with 0 <= n < 2**m; set inverse to 0 for forward transform (FFT), or 1 for iFFT. */ int fix_fft(char fr[], char fi[], int m, int inverse) { int mr, nn, i, j, l, k, istep, n, scale, shift; char qr, qi, tr, ti, wr, wi; n = 1 << m; /* max FFT size = N_WAVE */ if (n > N_WAVE) return -1; mr = 0; nn = n - 1; scale = 0; /* decimation in time - re-order data */ for (m=1; m<=nn; ++m) { l = n; do { l >>= 1; } while (mr+l > nn); mr = (mr & (l-1)) + l; if (mr <= m) continue; tr = fr[m]; fr[m] = fr[mr]; fr[mr] = tr; ti = fi[m]; fi[m] = fi[mr]; fi[mr] = ti; } l = 1; k = LOG2_N_WAVE-1; while (l < n) { if (inverse) { /* variable scaling, depending upon data */ shift = 0; for (i=0; i<n; ++i) { j = fr[i]; if (j < 0) j = -j; m = fi[i]; if (m < 0) m = -m; if (j > 16383 || m > 16383) { shift = 1; break; } } if (shift) ++scale; } else { /* fixed scaling, for proper normalization -- there will be log2(n) passes, so this results in an overall factor of 1/n, distributed to maximize arithmetic accuracy. */ shift = 1; } /* it may not be obvious, but the shift will be performed on each data point exactly once, during this pass. */ istep = l << 1; for (m=0; m<l; ++m) { j = m << k; /* 0 <= j < N_WAVE/2 */ wr = pgm_read_word_near(Sinewave + j+N_WAVE/4); /*Serial.println("asdfasdf"); Serial.println(wr); Serial.println(j+N_WAVE/4); Serial.println(Sinewave[256]); Serial.println("");*/ wi = -pgm_read_word_near(Sinewave + j); if (inverse) wi = -wi; if (shift) { wr >>= 1; wi >>= 1; } for (i=m; i<n; i+=istep) { j = i + l; tr = FIX_MPY(wr,fr[j]) - FIX_MPY(wi,fi[j]); ti = FIX_MPY(wr,fi[j]) + FIX_MPY(wi,fr[j]); qr = fr[i]; qi = fi[i]; if (shift) { qr >>= 1; qi >>= 1; } fr[j] = qr - tr; fi[j] = qi - ti; fr[i] = qr + tr; fi[i] = qi + ti; } } --k; l = istep; } return scale; } /* fix_fftr() - forward/inverse FFT on array of real numbers. Real FFT/iFFT using half-size complex FFT by distributing even/odd samples into real/imaginary arrays respectively. In order to save data space (i.e. to avoid two arrays, one for real, one for imaginary samples), we proceed in the following two steps: a) samples are rearranged in the real array so that all even samples are in places 0-(N/2-1) and all imaginary samples in places (N/2)-(N-1), and b) fix_fft is called with fr and fi pointing to index 0 and index N/2 respectively in the original array. The above guarantees that fix_fft "sees" consecutive real samples as alternating real and imaginary samples in the complex array. */ int fix_fftr(char f[], int m, int inverse) { int i, N = 1<<(m-1), scale = 0; char tt, *fr=f, *fi=&f[N]; if (inverse) scale = fix_fft(fi, fr, m-1, inverse); for (i=1; i<N; i+=2) { tt = f[N+i-1]; f[N+i-1] = f[i]; f[i] = tt; } if (! inverse) scale = fix_fft(fi, fr, m-1, inverse); return scale; }
Интересно было бы вспомнить этот раздел матана))). Но что конкретно требуется не понял. В 2-х словах ты скармливаешь библиотеке семпл сигнала (определенного размера желательно, хотя и не обязательно) и получаешь на выходе 2 - массив с АЧХ и ФЧХ сигнала . На фазу забиваешь,частоту отображаешь. В аннотации к библиотеке вроде все расписано очень подробно.
Есть нюансы, конечно))), например: апмлитуды и фазы частот спектра, отношение с/ш, еще очень критичен сам процесс семплирования, фильтрация сигнала, но в бытовых приложениях на все это можно положить болт.
Да, в коментах пояснение написано, но , к моему сожалению, по-английски и так что даже работа с переводчик не даёт картинки происходящего процесса, а пытаться врать самому себе в том чего не знаю - не хочу.
Конкретно (для начала) - мне нужно собрать на макетке простенькую схему, допустим из четырёх светодиодов, подключить её к ардуинке включить на компе какой-нибудь музон (в это время ардуинка с компом не связана, ну только разве что питается от него) и вместе с этим ардуинка начнёт анализировать (разуммется микрофон входит в вышеупомянутую схемку) поступающий сигнал и передавать на светодиоды согласованно с той частотой звука которая в данный момент играет. Скажем так. В данный помент из динамиков вылетает звук низочастотный - горит светодиод №1. Повышаем частоту колебаний звуковых волн загорается светодиод №2. И т.д. до 4-ого.
Понятно. Задачу разбиваем на 3 этапа.
1. АЦП. Ардуинка формирует массив из 256 значений сигнала с частотой дискретизации порядка 32кГц (получим в итоге спектр сигнала в диапазоне 0-16кгц). Я бы это делал с линейного входа источника сигнала, а не с микрофона...
2. Этот массив обрабатываем FFT
3. Полученный спектр сигнала разбиваем на 4 подгруппы и определяем вес каждой (по амплитуде гармоник). Модулируем шимом ваши 4 светодиода в зависимости от веса подгруппы или зажигаем только 1 светодиод, соответствующий подгруппе с максимальным весом.
4. возвращаемся к п.1.
-----------
На истину не претендую, просто есть интерес к этой теме, с удовольставием послушаю оппонентов.
Действительно, лучше будет установить jack 3,5. Можете подсказать с чего начать (с чего начать писать код)?
Начните с АЦП
Отличнейшая статья-исследование с кодом, показывающая как сделать АЦП на ардуино и какие подводные камни стоит ожидать.
Для вашего проекта с 4-я диодами годится на все 100.
http://robotosha.ru/arduino/analog-measurements-arduino.html
Из которой следует что при оцифровке на 38 кГц (полоса сигнала до 19 кГц) вы получите удовлетворительный для вашей задачи результат. Конечно, есть нюансы с самим сигналом, он должен быть в определенном диапазоне, чтобы не спалить АЦП ардуинки. Я бы для поиграться снимал сигнал с простейшего делителя, предварительно проверив осциллографом, что он находится в требуемом диапазоне до 5В. Но Вам никто не запрещает собрать входной каскад на ОУ.
Вот нашел готовый проект как раз по вашей теме.
https://learn.adafruit.com/piccolo