Один из подходов, который я считаю полезным, состоит в том, чтобы сгенерировать две эталонные волны, отстоящие друг от друга на 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];
}
Код здесь использует «двойной» для всех математических операций, кроме индексации массива. На практике почти наверняка было бы быстрее заменить некоторые математические операции целочисленными. Я ожидаю, что на машинах с плавающей запятой лучшим подходом будет сохранение фазы в виде 32-битного целого числа и использование таблицы ~ 4096 «одиночных» синусоидальных значений (чем меньше размер таблицы в ОЗУ, тем лучше когерентность кеша). представление). Я с большим успехом использовал код, очень похожий на приведенный выше, на ЦСП с фиксированной точкой (целочисленный); вычисления синуса и косинуса в process_some_waves выполнялись в отдельных «циклах», при этом каждый «цикл» реализовывался как отдельная инструкция с префиксом «повторить».
person
supercat
schedule
17.11.2010