Один подход, который я нашел полезным, состоит в том, чтобы сгенерировать две опорные волны с разницей в 90 градусов (я называю их «синус» и «косинус») и взять точечное произведение входной формы волны с этими опорными волнами на некоторых довольно коротких(скажем, 1/60 секунды) растягивает ввод.Это даст вам несколько зашумленный индикатор того, сколько входной частоты у вас в фазе или против фазы относительно ваших опорных волн (квадратный корень из суммы квадратов значений, сгенерированных с использованием двух опорных волн, будетбыть амплитудой).При небольшом размере окна вы заметите, что выходные данные довольно шумные, но если вы фильтруете выходные данные с помощью чего-то вроде простого КИХ или БИХ-фильтра, вы, вероятно, должны получить что-то довольно разумное.
Один хороший трюкчтобы сгенерировать два числа амплитуд: для первого прогоните амплитуды синуса и косинуса через два цикла фильтрации, а затем вычислите сумму квадратов.Для второго прогоните амплитуды через один раунд фильтрации, затем вычислите сумму квадратов, а затем прогоните ее через еще один раунд фильтрации.
Оба измерения амплитуды будут испытывать одинаковую задержку, но первоебудет гораздо более избирательным, чем второй;таким образом, вы можете очень четко сказать, является ли частота «правильной» или немного выключенной.Используя этот подход, можно быстро обнаруживать тональные сигналы DTMF, отклоняя тоны, которые даже на несколько Гц отключены (тоны вне тона будут восприниматься на «свободном» детекторе гораздо сильнее, чем на жестком).
Пример кода:
double sine_phase,sine_freq;
void process_some_waves(double *input, int16 len,
double *sine_phase, double sine_freq,
double *sine_result, double *cosine_result)
{
int i;
double phase, sin_tot,cos_tot;
phase = *sine_phase;
sin_tot = cos_tot = 0;
for (i=0; len > i; i++)
{
sin_tot += input[i] * sin(phase);
cos_tot += input[i] * cos(phase);
phase += sine_freq;
}
*sine_result = sin_tot;
*cosine_result = cos_tot;
*sine_phase = phase;
}
/* Takes first element in buffer and 'smears' it through buffer with simple Gaussian resp. */
void simple_fir_filter(double *buff, int buffsize)
{
int i;
for (i=buffsize-1; i>=2; i--)
buff[i] = (buff[i-1] + buff[i-2])/2;
}
#define FILTER_SIZE1 10
#define FILTER_SIZE2 8
#define SECTION_LENGTH 128
#define FREQ whatever
double sine_buff1[FILTER_SIZE1], sine_buff2[FILTER_SIZE2];
double cos_buff1[FILTER_SIZE1], cos_buff2[FILTER_SIZE2];
double combined_buff[FILTER_SIZE2];
double tight_amplitude, loose_amplitude;
double ref_phase;
void handle_some_data(double *input)
{
/* Put results in first element of filter buffers */
process_some_waves(input, SECTION_LENGTH, &ref_phase, FREQ, sine_buff1, cos_buff1);
/* Run first stage of filtering */
simple_fir_filter(sine_buff1, FILTER_SIZE1);
simple_fir_filter(cosine_buff1, FILTER_SIZE1);
/* Last element of each array will hold results of filtering. */
/* Now do second stage */
sine_buff2[0] = sine_buff1[FILTER_SIZE1-1];
cosine_buff2[0] = cosine_buff1[FILTER_SIZE1-1];
combined_buff[0] = sine_buff2[0]*sine_buff2[0] + cosine_buff2[0]*cosine_buff2[0];
simple_fir_filter(sine_buff2, FILTER_SIZE2);
simple_fir_filter(cosine_buff2, FILTER_SIZE2);
simple_fir_filter(combined_buff, FILTER_SIZE2);
tight_amplitude = sine_buff2[FILTER_SIZE2-1]*sine_buff2[FILTER_SIZE2-1] +
cosine_buff2[FILTER_SIZE2-1]*cosine_buff2[FILTER_SIZE2-1];
loose_amplitude = combined_buff2[FILTER_SIZE2-1];
}
Код здесь использует 'double' для всей математики, кроме подписки на массив.На практике почти наверняка было бы быстрее заменить некоторые математические вычисления на целочисленные.На машинах с плавающей запятой, я ожидаю, что лучшим подходом было бы сохранить фазу в виде 32-разрядного целого числа и использовать таблицу с ~ 4096 «одиночными» значениями синуса (чем меньше размер таблицы в ОЗУ, тем лучше когерентность кэшаспектакль).Я использовал код, очень похожий на приведенный выше, для DSP с фиксированной (целочисленной) точкой с большим успехом;вычисления синуса и косинуса в process_some_waves выполнялись в отдельных «циклах», причем каждый «цикл» реализовывался как отдельная инструкция с префиксом «repeat».