![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Декодирование DTFM [D3, D5, Win95/98, WinXP]
Вадим © (2004-05-22 17:34:00)Здравствуйте. Задача стоит такая. На линейный вход приходит последовательность цифр в виде DTFM. Необходимо в реальном времени преобразовывать это снова в цифры. DTFM представляет собой смесь двух частот (одной нижней и одной верхней) из следующего ряда: 697, 770, 852, 941 (нижние), 1209, 1336, 1477 и 1633 Гц (верхние). Что позволяет закодировать 16 символов: 1, 2, 3, A, 4, 5, 6, B, 7, 8, 9, C, *, 0, #, D. Таким образом, проблема сводится к тому, чтобы обнаружить наличие или отсутствие данных частот во входном сигнале, но как это сделать не представляю… В обсуждении темы fft.dll ( http://www.delphimaster.ru/cgi-bin/forum.pl?id=1082816087&n=7) видел процедуру, делающую БПФ, и по этому ( http://www.delphikingdom.com/asp/answer.asp?IDAnswer=5489) адресу было что-то подобное, но как этим воспользоваться до меня не доходит. Либо есть более простой выход?
MegaVoltik (2004-05-31 10:26:00) [21]А чем вас аппаратный декодер не устраивает то? Сигнал всё равно от куда то братся должен? Если качественно определять то жедаетельно АЦП ставить. А если АЦП не ставить а просто компаратор как в АОНах то я думаю для такого входного сигнала есть свои специальные методы. Сходи на http://www.telesys.ru/teleconf.shtmlтам помогут :)
Evgeny V (2004-05-29 05:27:00) [22]Извини Вадим, я и правда ввел тебя в заблуждение,
давно этим уже не занимался и подзабыл. Сорри. И так нам надо
посчитать энергию при выбранных частотах. En=Acos^2+ASin^2,
где Acos - косинусные и Asin -синусные коэффициенты (Re и Im).
Acos=Sum(buf[i])*Cos(2*pi*Freq*i/N) и Asin=Sum(buf[i])*Sin(2*pi*Freq*i/N) где i меняется oт 0 до N-1,
вот исправленный код с прорисовкой графиков сигнала и двух значений энергий для частот 1000 и 750
Графики рисую в Tchart, Series выбран Line или FastLine.
procedure TForm1.Button3Click(Sender: TObject);
var
i,j: integer;
EnX: array [0..63] of double;
EnY: array [0..63] of double;
EnX1: array [0..63] of double;
EnY1: array [0..63] of double;
Acos1000,Asin1000,Acos750,Asin750:double;
Freq1000: double;
Freq750,t1,t2,t3,t4:double;
En1000,En750:double;
buf: array [0..3999] of double;
begin
Freq1000:=1000;
Freq750:=750;
Chart1.Series[0].Clear;
Chart1.Series[1].Clear;
Chart1.Series[2].Clear;
j:=0;
for i:=0 to 63 do
begin
EnX[i]:=0;
EnY[i]:=0;
EnX1[i]:=0;
EnY1[i]:=0;
end;
for i:=0 to 999 do
buf [i]:= 0;// сигнала нет
for i:=0 to 2000 do
buf [i+1000]:= sin (2*pi*i*Freq1000/8000); //с 1000 точки сигнал 1000
for i:=0 to 999 do
buf [i+3000]:= sin (2*pi*i*Freq750/8000); //с 3000 тысячной точки сигнал 750
ACos1000:=0;
ASin1000:=0;
ACos750:=0;
ASin750:=0;
for i:=0 to 3999 do// внутри цикла у нас неявный подцикл по массивам En из 64 итераций
begin
j:=j mod 8000;
ACos1000:=ACos1000-EnX[i mod 64]; // вычитаем из значения коэффициента косинуса значения прошлого подцикла
ASin1000:=ASin1000-EnY[i mod 64];// // вычитаем из значения коэффициента синуса значения прошлого подцикла
EnX[i mod 64]:=buf[i]*Cos(2*pi*Freq1000*j/8000.0);// вычисляем значение в текущей точке
EnY[i mod 64]:=buf[i]*Sin(2*pi*Freq1000*j/8000.0);
ACos1000:=ACos1000+EnX[i mod 64];// cуммируем значения для получения значения коэффициента косинуса
ASin1000:=ASin1000+EnY[i mod 64];// cуммируем значения для получения значения коэффициента синуса
t1:=ACos1000*ACos1000;// квадрат
t2:=ASin1000*ASin1000;// квадрат
En1000:=Sqrt((t1*t1+t2*t2))/64;// находим значение энергии для заданной частоты
//корень квадратный и деление на 64 не нужно, сделано только для удобства отображения на графике,
//большие значения получаются
/ все то же для другой частоты
ACos750:=ACos750-EnX1[i mod 64];
ASin750:=ASin750-EnY1[i mod 64];
EnX1[i mod 64]:=buf[i]*Cos(2*pi*Freq750*j/8000.0);
EnY1[i mod 64]:=buf[i]*Sin(2*pi*Freq750*j/8000.0);
ACos750:=ACos750+EnX1[i mod 64];
ASin750:=ASin750+EnY1[i mod 64];
t3:=ACos750*ACos750;
t4:=ASin750*ASin750;
En750:=Sqrt((t3*t3+t4*t4))/64;
Inc(j);
Chart1.Series[0].AddY( buf[i],'',clRed);// показываем сигнал
Chart1.Series[1].AddY( En1000/50,'',clGreen);// значения энергии частоты 1000
// на 50 делю тоже только для смотрибельности на грфике
Chart1.Series[2].AddY( En750/50,'',clBlue);// значения энергии частоты 750
Edit1.Text:=IntToStr(i);// просто показываю сколько точек пробежал
if (i mod 1000) =0 then
AppLication.ProcessMessages;// даю возможность прорисоваться графику и эдиту
end;
end;
Тебе прийдется еще решать проблему с определением сигнала вообще, помнишь porog?
В линии все равно шумы есть и энергии сигнала будут присутствовать, но меньшими значениями,
поэтому их надо сравнивать с пороговым значением, его я подбирал экспериментально.
Для радио у меня получилась формула Sum(Abs(buf[i])*0.5 > Max(sqrt(En)/N) - i от 0 до N-1,
Может и тебе она подойдет, поиграйся с коэффициентом, не 0.5, а может другой.
Потом при смене частотной посылки возможны кратковременные появления энергий
"ложных" частот, так что суди о наличии частоты по нескольким опредлениям энергии,
например за 5 подциклов по 64 вычисления. Будут вопросы пиши, лучше наверное на почту.
evgeny_v@rambler.ru. Удачи, извини еще раз за ошибку в предыдущих постах.
Вадим (2004-05-28 12:33:00) [23]To KilkennyCat: Не такой уж и проостой. Ведь внешние устройства нужно програмно обслуживать. Я с COM-портом кое-как разобрался, да и то не до конца, а тут ещё LPT! Так зачем городить огород, если можно всё решить толькопрограммным путём.
To Evgeny V. Сделал так:
{$N+}
Uses
CRT;
var
i,j: integer;
EnX: array [0..63] of double;
EnY: array [0..63] of double;
An, Freq: double;
buf: array [0..3999] of double;
begin
ClrScr;
j:=0;
An:=0;
for i:=0 to 63 do
begin
EnX[i]:=0;
EnY[i]:=0;
end;
for i:=0 to 3999 do
buf [i]:= sin (2*pi*i*500/4000); //- опять же 1000 Гц
for i:=0 to 999 do
buf [i+3000]:= sin (2*pi*i*93.75/1000); //- как бы в конце появилось 750 Гц
begin
writeln;
Freq:=750;
for i:=0 to 3999 do
begin
j:=j mod 8000;
An:=An-EnX[i mod 64]*EnX[i mod 64]-EnY[i mod 64]*EnY[i mod 64];
EnX[i mod 64]:=buf[i]*Cos(2*pi*Freq*j/8000.0);
EnY[i mod 64]:=buf[i]*Sin(2*pi*Freq*j/8000.0);
An:=An+EnX[i mod 64]*EnX[i mod 64]+EnY[i mod 64]*EnY[i mod 64];
Inc(j);
end;
writeln;
writeln (An:5:3);
end;
end.
Всё равно An у меня принимает одно и то же значение (32.000) при любых раскладах. Оно меняется толькоесли последние 64 элемента "буфера" заполнить нулями - тогда An=0, если последние 32, то An=16.000. "Нужную" частоту вставлял не только в конце, но и в середине (с 2000 элемента) - эффект тот-же. ???
А почитат исходники по нужной теме всегда поучительно, тем более разобраться в них! Ещё раз спасибо.
Evgeny V (2004-05-28 05:46:00) [24]Ошибки нет, дело в том что энергия не появится сразу, и в начале у тебя все инициализировано 0 и с первого буфера An будет расти медленно, ты сделай буфер на секунду, и в нем только на 100 миллисикунд заполни значения частотой, например в середине, тогда будет видно изменения. Про j когда будешь бежать по буферу более чем 64 выборки, 12000 например, то j не будет равно i
for i:=0 to N-1 do
begin
j:=j mod 8000;
An:=An-EnX[i mod 64]*EnX[i mod 64]-EnY[i mod 64]*EnY[i mod 64];
EnX[i mod 64]:=buf[i]*Cos(2*pi*Freq*j/8000.0);
EnY[i mod 64]:=buf[i]*Sin(2*pi*Freq*j/8000.0);
An:=An+EnX[i mod 64]*EnX[i mod 64]+EnY[i mod 64]*EnY[i mod 64];
write (i,' ',EnX[i mod 64]:5:3,' '); - это кстати не очень наглядный коэффициент, энергия должна сильно меняться
write (i,' ',EnX[i mod 64]:5:3,' '); - аналогично
Inc(j);
end;
writeln;
writeln (An:5:3);
end;
N -cделай побольше,ну пусть 4000, и тысячу значений в нем например заполни синусом нужной частоты. Посмотри результат тогда, если не получится, пришлю тебе прогу и исходник, где я делаю похожие вещи. Удачи, пиши, можешь на мыло в моей анкете.
KilkennyCat (2004-05-28 05:10:00) [25]Наиболее простой способ обойдется в 50 рублей, час работы паяльником.... микруха декодера DTMF -> LPT
Вадим (2004-05-27 22:30:00) [26]To Evgeny V. Извиняюсь, что долго думал, но так уж получилось. Вот что я делал, чтобы "поиграться":
{$N+}
Uses
CRT;
var
i,j: integer;
EnX: array [0..63] of double;
EnY: array [0..63] of double;
An, Freq: double;
buf: array [0..63] of double;
begin
ClrScr;
j:=0;
for i:=0 to 63 do
begin
Enx[i]:=0;
Eny[i]:=0;
buf [i]:= sin (2*pi*i*8/64);//- в ручную заполняю буфер из расчета - входящяя 1000 и дескридитация 8000 Герц
write (i,' ',buf[i]:5:3,' ');//- посмотреть, чем заполнил
end;
begin
writeln;
An:=0;
Freq:=1000;
for i:=0 to 63 do
begin
j:=j mod 8000;
An:=An-EnX[i]*EnX[i]-EnY[i]*EnY[i];
EnX[i]:=buf[i]*Cos(2*pi*Freq*j/8000.0);
EnY[i]:=buf[i]*Sin(2*pi*Freq*j/8000.0);
An:=An+EnX[i]*EnX[i]+EnY[i]*EnY[i];
write (i,' ',EnX[i]:5:3,' ');
Inc(j);
end;
writeln;
writeln (An:5:3);
end;
end.
Делалось это в Борланде. Так вот, какое бы значение Freq я не задавал, An остается одно и то же! Где здесь ошибка? В алгоритме ДПФ или я сделал что-то не так? И зачем былаа введена переменная j, ведь она в цикле принимает те же значения, что и i? А вообще спасибо, если это сработает, то будет то, что надо!
Jeer (2004-05-24 15:38:00) [27]Алгоритм Герцеля (Goerzel)
http://factaee.elfak.ni.ac.yu/fu2k33/9mp.html
Evgeny V (2004-05-24 06:21:00) [28]В данном случае лучше дискретное преобразование фурье во скользящему окну(это точнее позволит определить момент смены сигнала), приведу пример для нахождения одной заданной частоты по N значениям, например частота дискретизации 8000, берем выборки из 64 значений( это цифра должна быть степенью 2 и достаточно большая, что бы обеспечить точность измерения частоты) и по ним делаем преобразование, шаг между частотами по спектру в этом случае 8000/64 125 герц (фактически это ширина полосы определяемой частоты, спектральной палки, точность), делаем массив выборок для мнимой и действительно части из 64 элеменотов, тогда делаем следующее (пример для одной частоты, посчитаешь сам )
EnX:array [0..63] double - сумма коэффициентов реальной части для заданной частоты
EnУ:[0..63] double - сумма коэффициентов мнимой части для заданной частоты
An:double - Энергия заданной частоты
buf:array [0..63] - твои выборки сигнала с саунда.
porog;double - величина, с которой ты сравниваешь на наличие частоты
j:integer - циклический счетчик, местонахождение фазы измерения, свяжем его с частотой дискретизации
Freq - заданная частота
Вначале все переменные, массивы раны нулю, кроме porog и Freq, это уже задается;
Предварительно заполни массив buf - очередной порцией выборок саунда - 64 выборки, можно и больше взять число, 128, 256 и т.д, влияет на точность определения и временной интевал, за который определяем
for i:=0 to 63 do
begin
j:=j mod 8000;
An:=An-EnX[i]*EnX[i]-EnY[i]*EnY[i];// - вычитаем из суммы коэффициентов самое старое значение, на его место запишем следом новое
EnX[i]:=buf[i]*Cos(2*pi*Freq*j/8000.0);// вычислим реальную часть
EnY[i]:=buf[i]*Sin(2*pi*Freq*j/8000.0);// вычислим мнимую часть
An:=An+EnX[i]*EnX[i]+EnY[i]*EnY[i];// значение энергии в заданной точке по результату 64 выборок
Inc(j);
if An >= porog then частота есть в сигнале
end;
Получим новые 64 выборки и повторим вычисления и так до .....
Вот весьма упрощенный алгоритм определения частоты в сигнале. Если ты не поленишься и построишь реальные графики(Tchart например весьма для этого удобен) сигнала и этих вычислений (можешь провести их для каждой частоты DTMF), то наглядно увидишь что и как, это лучше чем рассказывать, поиграйся с размером массива, можно и не 64, это дело твое, насколько помню, в DTMF частоты могут лежать довольно близко в строках или стобцах, и тебе лучше будет брать размер 128 точек или больше.
Вадим (2004-05-23 21:54:00) [29]Я, конечно, извиняюсь, но неужели не найдется человека, знающего как мне помочь?
Вадим (2004-05-22 23:16:00) [30]
> Ты хотел простой выход - я тебе его подсказал
Спасибо, но давай не устраивать полемику. Я же в вопросе даже дал ссылки на сравнительно несложные алгоритмы для преобразования БПФ. Мне нужна хотя бы подсказка как ихприменить для решения моей проблемы. Либо что-нибудь болеепростое. Ещё раз повторю, буду признатален за конкретнуюпомощь или подсказу
Jel (2004-05-22 22:16:00) [31]
> А ты сам-то видел, то, что советуешь?!
Вообще-то видел. Зря шумишь. Ты не говорил что тебе нужны бесплатные решения не весящие мегабайты. Ты хотел простой выход - я тебе его подсказал. Подходит - отлично, нет - ищи другой.
:)
Вадим (2004-05-22 21:07:00) [32]To Jel. А ты сам-то видел, то, что советуешь?! Там либо генераторы DTFM, либо компоненты, которые "весят" мегабайты и стоят совсем нехило! Нужен дельный совет, расчитанный на мою пока не высокую квалификацию.
Jel (2004-05-22 17:58:00) [33]Посмотри готовые компоненты тут
http://www.torry.net/quicksearchd.php?SID=f8370ce5e349ab4b6be3a0a0bce1fd4b&String=DTMF&Title=No