Здравствуйте, adontz, Вы писали:
A>Есть бесконечный массив семплов (ну грубо говоря, сильно старые меня конечно не волнуют).
A>Есть частота дискретизацц D, частота которую надо распознать F, минимальная распознаваемая длительность сигнала T.
A>Для каждого sample[i], частоты D я вычисляю
A>sum1[F, i] += sample[i] * sin(2*pi*i/D);
A>sum2[F, i] += sample[i] * sin(2*pi*i/D + pi/2);
A>energy[F, i] = sqrt(sum1[F, i]*sum1[F, i]) + sqrt(sum2[F, i]*sum2[F, i])
Мне больше импонирует потоковый вариант, т.е. алгоритм надо писать просто в расчете подачи на вход очередного sample, без ориентации на массивы.
A>Если есть такой промежуток [k — D*T, k], что на нём energy[F, i] всегда больше некоторого порогового значения, то считать, что сигнал распознан.
Нет, из-за шума и из-за второй частоты не все твои отсчеты будут больше некоторого порогового значения. Это все надо сглаживать. Если подобрать частоту среза ФНЧ 1/20мс, то достаточно обнаружить сам факт достижения порогового значения.
Затем надо не забыть обнаружить паузу
Сигнал распознан, если две и только две частоты из сетки обнаружены.
A>Насколько я понимаю, мне понадобиться считать и общую энергию сигнала.
Обязательно, порог — величина относительная. Громкость в канале не постоянная. Я тут давал уже как считать общую енергию. Ее тоже надо сглаживать точно таким же фильтром.
Короче, навскидку так:
// фильтр НЧ
public struct FNC {
public double k; // коэф
public double outp; // выход фильтра
public void Step(double inp) {
outp -= (outp - inp) * k;
}
public FNC(double k) {
this.k = k;
this.outp = 0;
}
}
// подсчитываем квадрат текущей мощности сигнала
public struct EnergySum {
FNC fnc;
public void Step(double inp) {
fnc.Step(inp * inp);
}
public EnergySum(double k) {
fnc = new FNC(k);
}
}
// генерируем отсчеты
public struct Generator {
public double outp, outp90;
double df, q;
/// <param name="f_discr">частота дискретизации</param>
/// <param name="f_generate">частота генератора</param>
public Generator(double f_discr, double f_generate) {
df = 2 * Math.PI * f_generate / f_discr;
q = 0;
outp = outp90 = 0;
}
public void Step() {
q += df;
if (q > 2 * Math.PI)
q -= 2 * Math.PI;
outp = Math.Sin(q);
outp90 = Math.Cos(q);
}
}
// целевой детектор частоты
public class Detector {
FNC fnc, fnc90;
Generator generator;
public double outp; // выход детектора
public bool signal; // обнаружен сигнал выше порога
const double reaction = 0.025; // 25 mc
const double threshold = 0.2;
public Detector(double f_discr, double f_detect) {
double k = 1 - Math.Pow(Math.Sqrt(2) / 2, 1 / (2 * f_discr * reaction));
fnc = new FNC(k);
fnc90 = new FNC(k);
generator = new Generator(f_discr, f_detect);
outp = 0;
signal = false;
}
public void Step(double inp, double energy) {
generator.Step();
fnc.Step(inp * generator.outp);
fnc90.Step(inp * generator.outp90);
outp = fnc.outp * fnc.outp + fnc90.outp * fnc90.outp;
signal = energy * threshold < outp;
}
}
// пересечение 2-х частот на клавиатуре
public class Analizer {
Detector horizontal;
Detector vertical;
public char symbol;
public bool signal = false;
public Analizer(Detector h, Detector v, char symb) {
horizontal = h;
vertical = v;
symbol = symb;
}
public void Step() {
signal = horizontal.signal && vertical.signal;
}
}
Могут быть всякие мелкие неточности.
С инкапсуляцией тоже не заморачивался.
Замечание.
Генератор вполне может генерить {0, 1}, а не просчитывать синусы/косинусы каждый раз. Если точности будет не хватать, то буквально в 3-х битах закодированая таблица синусоиды дает уже весьма точные результаты. Т.е. почти все здесь можно переводить на int.