Все про Интернет
       (N 384) 2011 - 2012
        << Архив передач >>
 Логин:  Пароль:
[регистрация]     запомнить 
 Поиск по сайту:
[Сайты зрителей] [Рейтинг] [Статьи] [Форум] [Блог] [Киберспорт] [Конкурсы] [О передаче]
 

DTMF – FFT декодер

raxp@mail.zp.ua

Данный материал является продолжением цикла статей по VoIP телефонии [1].

F1/F2

1209 Гц

1336 Гц

1477 Гц

1633 Гц

697 Гц

1

2

3

A

770 Гц

4

5

6

B

852 Гц

7

8

9

C

941 Гц

*

0

#

D


В быту и промышленности…

Используя тональный набор, можно легко управлять приложениями компьютерной телефонии, отвечая на запросы системы нажатием клавиш на телефоне в режиме тонального набора, так и организовать помехоустойчивый канал связи-управления между цехами на основе имеющихся телефонных сетей или городскими сетями, в частности – светофоров (что уже существует)…

Немножко теории

DTMF (Dual Tone Multi Frequency) - термин тонального набора. DTMF сигнал является комбинацией двух частот, высокой и низкой. Система сигналов DTMF включает восемь тонов, подобранных так, чтобы передаваться через ТФоП с минимальным влиянием друг на друга.

 

Проблема и решение

Недостатка в готовых аппаратных реализациях кодеров-декодеров DTMF не наблюдается. Достаточно вспомнить отечественные микросхемы BЖ19/ВЖ19 (MV8870) или множество вариантов на обычных так и DSP контроллерах [2, 3]. А вот что касается программных визуальных решений для ПК, то дальше “демо” версий с закрытыми алгоритмами, исключая специализированные военные применения, дело не дошло. Как быть?

Сразу же введем таблицу массивов тонов:

     keys = '1234567890*#abcd';
     dtmf1: array[1..16]of integer =(697,697,697,770,770,770,852,852,852,941,941,941,697,770,852,941);
     dtmf2: array[1..16]of integer =(1209,1336,1477,1209,1336,1477,1209,1336,1477,1336,1209,1477,1633,1633,1633,1633)

и разделим “проблему” на подзадачи:

  • выделить набор частот (спектр) из исходного сигнала
  • идентифицировать по двум частотам, согласно таблице, символ DTMF

Вспомним курс математики [4]. Для выделения спектра как правило используется быстрое преобразование Фурье (БПФ). В нашем случае реализация БПФ будет следующей:

 procedure FFTQuad(number: dword; InData, OutData: Parraysingle); 

 function inv_diskret(a: Integer): Integer;
  var  b: Integer;
  begin
    Result:= 0;
    for b:= 0 to Base - 1 do
      if a and (1 shl b) <> 0 then
    Result:= Result or (1 shl (Base - b - 1))
  end;

begin
  maxbase:= 0;
  repeat  inc(Base);
  until (Number shr Base)=1;

  ZeroMemory(OutData, Number * SizeOf(Single));
  for i := 1 to Number - 1 do
  begin
    j := inv_diskret(i);
    if i <= j then begin
      C := InData[i];
      InData[i] := InData[j];
      InData[j] := C
    end
  end;

  for i := 1 to maxBase do begin
    lp := 1 shl i;
    lp2 := Number div lp;
    lp3 := lp div 2;
    StepAng := -pi / lp3;
    Ang := 0;
    for j:=0 to lp3-1 do begin
      C := Cos(Ang);
      S := Sin(Ang);
      Ang := Ang + StepAng;
      k1 := j;
      for k := 0 to lp2 - 1 do  begin
        k2 := k1 + lp3;
        //наши квадратуры-
        Re := C * InData[k2] + S * OutData[k2];
        Im := C * OutData[k2] - S * InData[k2];

        InData[k2] := InData[k1] - Re;
        OutData[k2] := OutData[k1] - Im;
        InData[k1] := InData[k1] + Re;
        OutData[k1] := OutData[k1] + Im;
        k1 := k1 + lp
      end
    end
  end;

  for i := 0 to Number - 1 do  begin
    InData[i] := InData[i] / Number;
    OutData[i] := OutData[i] / Number
  end
end;

Для идентификации полученного символа DTMF из вычисленных наборов частот применяется алгоритм декодирования. На практике нам достаточно найти 2 максимальных амплитуды из набора и сопоставить их частоты – частотам DTMF. Достоверность этого сопоставления определяется двумя критериями:

  • оба максимальных значения должны превышать уровень шума
  • незначительность отличия этих амплитуд друг от друга

 

Практика

Для работы нам следует запастись следующим:

  • среда разработки Borland Delphi 5-7
  • аудио - редактор Sound Forge 8.0

Прежде всего в SoundForge на вкладке Tools/Synthesis/DTMF-MF Tones создадим тестовые DTMF сигналы, что значительно облегчит нам процесс отладки. Для загрузки данных из WAV- файла PCM формата

//wav-
 r:= ReadWave(s);
r.Data.seek(0, soFromBeginning);
 for i := 1 to r.Data.Size div 2 do begin
  r.Data.readBuffer(W1,1);
  r.Data.readBuffer(W2,1);
  series3.Add(W1); //1- канал
  series4.Add(W2)  //2-
 end;

понадобится его заголовок

function ReadWave(FileName: AnsiString) : TWaveResult;
var  f             : TFileStream;
  wFileSize     : Cardinal;
  wChankSize    : Cardinal;
  ID            : array[0..3] of Char;
  Header        : TWaveHeaderChank;
  RealFileSize  : Cardinal;
Begin
  FillChar(Result, SizeOf(Result), 0);
  Try
    f := TFileStream.Create(FileName, fmOpenRead);
    f.Seek(0, soFromBeginning);
    f.ReadBuffer(ID[0], 4);  
    if String(ID) <> 'RIFF' then Begin      //
        Result.ERROR := IncorectFileFormat;
        f.Free;
        exit;
    end;
    f.ReadBuffer(wFileSize, 4);
    if f.size <> (wFileSize + 8) then begin       
        Result.ERROR := FileCorrupt;
        f.Free;
        exit;
      end;
    f.ReadBuffer(ID[0], 4);
    if String(ID) <> 'WAVE' then begin   //проверяем-
        Result.ERROR := IncorectFileFormat;
        f.Free;
        exit;
      end;
 
    wChankSize := 0;
    repeat                              
      f.Seek(wChankSize, soFromCurrent);
      f.ReadBuffer(ID[0], 4);           
      f.ReadBuffer(wChankSize, 4);  
      if wChankSize > High(integer)  then Begin                        
          Result.ERROR := DataError;
          f.Free;
          exit;
        end;
    until  (String(ID)='fmt ') or (String(ID)='data');
    if String(ID)='data'  then  Begin
        Result.ERROR := HeaderError;
        f.Free;
        exit;
      end;
    f.ReadBuffer(Header, Min(wChankSize, SizeOf(TWaveHeaderChank)));
    if wChankSize > SizeOf(TWaveHeaderChank)  
	  then  f.Seek(wChankSize - SizeOf(TWaveHeaderChank), soFromCurrent);

    wChankSize := 0;
    repeat          
      f.Seek(wChankSize, soFromCurrent);
      f.ReadBuffer(ID[0], 4);   
      f.ReadBuffer(wChankSize, 4);
    until  String(ID)='data';

    Result.ERROR:= noError;  
    Result.wAvgBytesPerSec := Header.wAvgBytesPerSec;
    Result.wBitsPerSample  := Header.wBitsPerSample;
    Result.wChannels       := Header.wChannels;
    Result.Data := TMemoryStream.Create;
    Result.Data.Seek(0, soFromBeginning);
    Result.Data.Size := wChankSize;  
    f.ReadBuffer(Result.Data.Memory^, wChankSize);
  Except
    Result.ERROR := ReadError;
  end;
  f.Free
end;
//===============

далее передаем набор данных в процедуру БПФ:

 FFTQuad(series4,ser2,1024); //набор отсчетов- на 1024 точки 

Переходим ко второй подзадаче – поиск и идентификация 2-х частот:

//2-х проходной поиск-
 a1:= -1000;
 for i:= 0 to (ser2.YValues.Count)-1 do begin //
  a:= ser2.YValues[i];
  f:= i / (ser2.YValues.Count/2) * (r.wSamplePerSec / 2);
  Series1.AddXY(f,a);
  if a > a1 then begin a1:= a; f1:= f end           
 end;
 a2:= -1000;
 for i:= (ser2.YValues.Count)-1 downto 0 do begin
  a:= ser2.YValues[i];
  f:= i / (ser2.YValues.Count/2) * (r.wSamplePerSec / 2);
  if (a > a2)and(a<>a1) then begin a2:= a; f2:= f end  
 end;

По нашей таблице массивов делаем выборку:

 //идентификация-
 dtmf_sig:= 'not';
 for i:= 1 to 16 do begin
  if (dtmf2[i]*0.98f1)and  //1 амплитуда >2       
  // 0.98 и 1.02 это своего рода доверительный интервал
   (dtmf1[i]*0.98f2)then begin  // определяющий помехоустойчивость
     dtmf_sig:= keys[i];  // и учитывающий разброс в параметрах генераторов DTMF
     break
    end;

  if (dtmf1[i]*0.98f1)and  //1 амплитуда >2
   (dtmf2[i]*0.98f2)then begin
     dtmf_sig:= keys[i];
     break
    end;
 end;

Как быть с обработкой в реальном времени? Воспользуемся ф-цией WaveInOpen, чтобы получить доступ к *аудиоустройству:

 
WaveInOpen(Addr(hwi2), WAVE_MAPPER, addr(header),
   integer(@waveInProc2),  0,CALLBACK_FUNCTION);

//= systimer2 =
procedure waveInProc2(hwi: HWAVEIN; uMsg,dwInstance,
                     dwParam1,dwParam2: DWORD);stdcall;
var i: integer;
    data16: PData16;
    h: integer;
    XScale, YScale: single;
    temp: pWaveHdr;
    a,a1,a2,             //амплитуды-
    f,f1,f2: double;   //частоты-
begin
 if (uMsg=WIM_DATA) then begin
  temp:= adr2;
  if adr2= @bufhead1 then adr2:= @bufhead2
   else adr2:= @bufhead1;
  if stp2 then WaveInAddBuffer(hwi,adr2,SizeOf(TWaveHdr));
  data16:= PData16(temp.lpData);

  inwav.Clear; outwav.Clear;
  form1.series5.Clear;
  form1.series6.Clear;
  for i := 0 to BufSize - 1 do begin //набивка-
   form1.series6.add(data16^[i]);
   inwav.add(data16^[i])
  end;
  FFTQuad(inwav,outwav,512);

  //2-х проходной поиск-
  a1:= -1000;
  for i:= 0 to (outwav.YValues.Count)-1 do begin //
   a:= outwav.YValues[i];
   f:= i / (outwav.YValues.Count/2) * (header.nSamplesPerSec / 2);
   form1.series5.AddXY(f,a);
   if a > a1 then begin a1:= a; f1:= f end         
  end;
  a2:= -1000;
  for i:= (outwav.YValues.Count)-1 downto 0 do begin
   a:= outwav.YValues[i];
   f:= i / (outwav.YValues.Count/2) * (header.nSamplesPerSec / 2);
   if (a > a2)and(a<>a1) then begin a2:= a; f2:= f end  
  end;
  //идентификация-
  dtmf_sig:= 'not';
  for i:= 1 to 16 do begin
   if (dtmf2[i]*0.98f1)and  //1 амплитуда >2
    (dtmf1[i]*0.98f2)then begin
      dtmf_sig:= keys[i];
      break
     end;
   if (dtmf1[i]*0.98f1)and  //1 амплитуда <2
    (dtmf2[i]*0.98f2)then begin
      dtmf_sig:= keys[i];
      break
     end;
  end;
  //-----------------------------------
end else Exit
end;

* не забудьте в проекте подключить модуль mmsystem

Итак, в результате компиляции получаем тестовую версию декодера DTMF (см. рис.):

Рис. Окно декодера

 

Ссылки:

P>Контактная информация:

raxp@mail.zp.ua

01.03.2008


[Переход к списку статей]

 
 

[Видеоархив]

Передача создана дизайн-студией Conus Video
По вопросам размещения рекламы или информации в передаче Internet@Mania пишите imania@mail.zp.ua, или звоните
8(0612)63-80-84


Share |
 
 


Провайдеры Запорожья
zp-provider.narod.ru
providers.portall.zp.ua
Домовые сети
linet.zp.ua
setka.zp.ua
biscom.zp.ua
homenet.zp.ua
www.16x.zp.ua
www.ss.zp.ua
www.skynet.zp.ua
www.watson.zp.ua
www.link.zp.ua
www.kichkas.net
www.neuro.zp.ua
innkom.zp.ua
Каталоги Запорожья
www.otvet.zp.ua
www.portall.zp.ua
www.zaporozhye.org
www.spravka.zp.ua
Форумы
www.forumzone.zp.ua
forum.mail.zp.ua
forum.tinet.zp.ua
forum.nulled-warez.org
forum2.biscom.zp.ua
myforum.net.ua
forum.ru-board.com
forstud.org.ua
Чаты
chatzone.zp.ua
4at.zp.ua
chatinet.zp.ua
chatik.zp.ua
Компьютерные сайты
www.ixbt.com
www.techlabs.ru
www.thg.ru
www.overclockers.ru
Игровые сайты
www.games.zp.ua
www.ag.ru
www.gamemag.ru
www.uaplay.com
www.gameg.info
Журналы
www.seti.com.ua
www.cpp.com.ua
www.shpil.com
www.chip.ua
Блоги
revolver.ru
dirty.ru
photoblog.ru
www.autoblog.ru

 
    Передача создана дизайн-студией Conus Video
    По вопросам размещения рекламы или информации в передаче Internet@Mania пишите нам imania@mail.zp.ua, или звоните 8(0612)63-80-84