Текст
                    ИЗМЕРЕНИЯ
Елена Бадло, Сергей Бадло
г. Запорожье
E-mail: raxp@radioliga.com
Данный материал является заключительным в цикле статей по
виртуальным приборам, в практической части которого мы узнаем
об аспектах создания анализатора спектра и декодера DTMF сигна-
лов, а также научим наше приложение “понимать” команды с пульта.
Виртуальные приборы.
DTMF. Спектроанализатор своими руками
Используя тональный набор (DTMF), можно легко управ-
лять как приложениями компьютерной телефонии, отвечая
на запросы системы нажатием клавиш на телефоне в режи-
ме тонального набора, так и организовать помехоустойчи-
вый канал связи-управления между цехами на основе имею-
щихся телефонных или городских сетей, в частности - све-
тофорами.
Краткий экскурс...
Что же такое DTMF? DTMF (Dual Tone Multi Frequency) -
это термин тонального набора. DTMF сигнал является ком-
бинацией двух частот, высокой и низкой. Система сигналов
DTMF включает восемь тонов, подобранных так, чтобы пе-
редаваться через ТФоП (телефонную сеть общего пользо-
вания) с минимальным влиянием друг на друга.
Недостатка в готовых аппаратных реализациях кодеров-
декодеров DTMF сегодня не наблюдается. Достаточно вспом-
нить отечественные микросхемы ВЖ18/ВЖ19 (MV8870) или
множество вариантов как на обычных, так и DSP контролле-
рах [1 , 2]. А вот что касается программных визуальных реше-
ний для ПК, то дальше “демо” версий с закрытыми алгорит-
мами, исключая специализированные военные применения,
дело не дошло. Как быть? Наиболее целесообразным реше-
нием в данной ситуации видится разработка своего анали-
затора спектра. Ведь зная характер сигнала и распределе-
ние частотных гармоник по спектру, ничто не мешает осуще-
ствить его декодирование. Кроме того, область применения
подобного спектроанализатора не ограничивается одним
лишь DTMF. С его помощью можно анализировать ЛЧМ,
MFSK, фазовую модуляцию, определять интенсивность гар-
моник, параметры и вид модуляции и т.д. и т.п.
Основные требования к ПО
Следовательно, ПО спектроанализатора должно как ми-
нимум позволять оценить уровень шумов (SNR), частоты и
динамический диапазон сигналов, полученных как от аудио-
карты ПК, так и стороннего оборудования, например, с фай-
лов (дампов). Для удобства работы желательно продублиро-
вать управление программой с пульта ДУ.
Таким образом, уже можем определить основные требо-
вания к нашему анализатору спектра:
-	поддержка текущих аудиоустройств в системе;
-	режим осциллографа и построение спектра в реальном
времени;
-	загрузка дампов *.dat “сырых” данных с внешних уст-
ройств, например с АЦП;
-	анализ файлов формата WAV/MP3 в off-time;
-	управление основными функциями с пульта ДУ;
-	возможность распечатки графика.
Рис. 1. Комплекс “виртуальный спектроанализатор”
Практика. Разработка ПО и средства отладки
Итак, приступим к основной задаче. Для работы нам сле-
дует запастись следующим:
*	среда разработки Borland Delphi 5-7
*	аудио-редактор Sound Forge 8.0
1. Работа с DTMF[ 1 -3] или Для чего мы учим математику
Прежде всего, в редакторе SoundForge на вкладке "Tools/
Synthesis/DTMF-MF Tones” создадим тестовые DTMF сигна-
лы, что значительно облегчит нам процесс отладки. Соглас-
но таблице 1 сразу же введем набор массивов тонов (врез-
ка 1) и разделим "проблему” на подзадачи:
-	выделить набор частот (спектр) из исходного сигнала;
-	идентифицировать по двум частотам, согласно табли-
це, символ DTMF.
Вспомним курс математики [3]. Как вы знаете, перио-
дическим сигналом называют такой вид воздействия, ког-
да форма сигнала повторяется через некоторый интервал
времени Т, который называется периодом. Простейшей
Таблица 1. Набор массивов тонов DTMF
F1/F2	1209 Гц	1336 Гц	1477 Гц	1633 Гц
697 Гц	1	2	3	А
770 Гц	4	5	6	В
852 Гц	7	8	9	
941 Гц	*	0	#	D
32
Радиолюбитель - 03/2009

ИЗМЕРЕНИЯ Сразу же введем набор массивов тонов врезка 1 keys = "1234567890*#abcd"; dtmfl: array[1..16]of integer =(697,697,697,770,770,770,852,852,852,941,941,941,697,770,852,941); dtmf2: array[l..16]of integer =(1209,1336,1477,1209,1336,1477,1209,1336,1477,1336,1209,1477,1633,1633,1633,1633) формой периодического сигнала является гармонический сигнал или синусоида, которая характеризуется амплитудой, фазой и периодом. Все остальные сигналы будут негармо- ническими. Существует общая методика исследования периодичес- ких негармонических сигналов, основанная на разложении сигналов в ряд Фурье. Данная методика заключается в том, что всегда можно подобрать ряд гармонических сигналов с такими амплитудами, частотами и начальными фазами, ал- гебраическая сумма ординат которых в любой момент вре- мени равна ординате исследуемого несинусоидального сиг- нала. В общем случае, ряд Фурье записывают в виде суммы бесконечного числа гармонических составляющих разных частот (см. формула): U(t) = Uo + SUM (Um * sin (k * со * t + у)) где k - номер гармоники; kco - угловая частота k-ой гармоники; со=2*я/Т - угловая частота первой гармоники; V - начальная фаза сигнала; Uo - нулевая гармоника. Для выделения спектра в радиотехнике, как правило, используется быстрое преобразование Фурье (БПФ). В на- шем случае реализация БПФ будет следующей (врезка 2). Для идентификации полученного символа DTMF из вы- численных наборов частот нам достаточно найти 2 макси- мальных амплитуды и сопоставить их частоты частотам DTMF. Достоверность этого сопоставления определяется двумя критериями: - оба максимальных значения должны превышать уро- вень шума в сигнале; - незначительность отличия этих амплитуд друг от друга, иначе принять за недостоверность. После чего передаем набор данных в процедуру БПФ (врезка 3) и переходим ко второй подзадаче - поиск и иден- тификация 2-х частот в спектре (врезка 4). Как быть с обработкой в реальном времени? Как и в прошлой нашей статье [4], воспользуемся функцией WavelnOpen, чтобы получить доступ к текущему аудиоуст- ройству (врезка 5). Проверим, как работает. Запустим на воспроизведе- ние двутональный сигнал и осуществим декодировку DTMF (см. спектр рис. 2, рис. 3). 2. Работа с WAV Для отображения и декодировки звуковых файлов в off-time нужно осуществить чтение данных из WAV-файла РСМ формата (врезка 6). После чего передаем полученный набор данных в нашу процедуру БПФ и строим спектр как обычно. 3. Работа с MP3 Работа с MP3 файлами ничем не отличается от мето- дов, что мы использовали при работе с WAV, с одним лишь собственно разложение в ряд Фурье врезка 2 procedure FFTQuad(ent: 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 (ent shr Base)=1; Zer oMemorу(OutData, ent * SizeOf(Single)); for i := 1 to ent - 1 do begin j := inv_diskret(i) ; if i <= j then begin C := InData[i]; InData[i] := IriData[j]; InData[j] := C end end; for i := 1 to maxBase do begin Ip := 1 shl i; lp2 := ent div Ip; lp3 : = Ip div 2 ; StepAng := -pi / lp3; Ang := 0; for j:=0 to lp3-l do begin C := Cos(Ang); S := Sin (Ang) ; Ang := Ang + StepAng; kl := j; for k := 0 to lp2 - 1 do begin k2 := kl + lp3; //наши квадратуры- Re := С * InData[k2] + S * OutData [ k2 ] ; Im := C * OutData[k2] - S * InData[k2] ; InData[k2] := InData[kl] -Re; OutData [k2] := OutData [kl] - Im; InData[kl] := InData[kl] + Re; OutData [kl] := OutData [kl] + Im; kl := kl + Ip end end end; for i := 0 to ent - 1 do begin InData [i] := InData [i] / ent; OutData [i] := OutData [i] / ent end end; врезка 3 FFTQuad(series4,ser2, 2048) ; //набор отсчетов - на 2048 точек отличием - предварительно перед загрузкой их нужно кон- вертировать в РСМ формат. Для чего воспользуемся широ- ко известной утилитой <1ате.ехе>. Формат командной стро- ки для работы с ней будет следующий: lame -decode filename.mp3 lame -b320 -qO filename.wav 4. Работа с пультом ДУ Для реализации управления с помощью любого пуль- та ДУ используем утилиту-сервер <winlirc.exe> [5]. Прин- цип ее работы следующий. Сервер опрашивает выбран- ный СОМ порт на ПК и отсылает данные по протоколу TCP. 33 Радиолюбитель - 03/2009 [|
ИЗМЕРЕНИЯ 2-х проходной поиск- врезка 4 а1: = -1000; for i:= 0 to (ser2.YValues.Count) -1 do begin // a: = ser2 . YValues [i] ; f:= i / (ser2.YValues.Count/2) * (r.wSamplePerSec / 2); Seriesl.AddXY(f ,a) ; if a > al then begin al:= 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(aOal) then begin a2:= a; f2:= f end end; По нашим наборам массивов делаем выборку- / /ид ентификация- dtmf_sig: = "not" ; for i:= 1 to 16 do begin if (dtmf2 [i] *0.98<fl) and(dtmf2 [i] *1.02>fl) and //1 амплитуда >2 // 0.98 и 1.02 это своего рода доверительный интервал (dtmf 1 [i ] *0.98<f 2) and (dtmf 1 [i]*1.02>f2) then begin / / определяющий помехоустойчивость dtmf_sig: = keys[i]; // и учитывающий разброс в параметрах генераторов DTMF break end; if (dtrnfl [i] *0.98<fl) and(dtmfl [i] *1.02>fl) and //1 амплитуда <2 (dtmf2 [i] *0.98<f2) and(dtmf2 [i] *1.02>f2) then begin dtmf_si g: = keys [ i ] ; break end; end; Наша программа декодирует посыл- ки и через интерпретатор выполняет выбранный пользователем набор ко- манд *. Чтобы осуществить подключение к серверу Win Lire, воспользуемся компо- нентом TCIientSocket из стандартной по- ставки Delphi. Установив порт подклю- чения равным 8765, активируем соеди- нение и по событию приема данных OnClientSosketRead осуществим деко- дирование принятой команды (кнопки пульта) (врезка 7). Если у вас есть карточка PCI тюне- ра, то с нее тоже можно получать ко- манды пульта, для этого достаточно воспользоваться универсальным мо- дулем PCI I/O for DELPHI разработки автора [6], но это тема уже отдельной статьи... обработка в реальном времени врезка 5 Wave InOpen (Addr (hwi2) , WAVE_MAPPER, addr (header) Л integer (@ wave InProc2) , 0, CALLBACK_FUNCTION) ; //= systimer2 = procedure wave InProc2 (hwi: HWAVE IN; uMs g, dw Ins tance, dwParaml,dwParam2: DWORD);stdcall; var i: integer; da tai6: PDatai6; h: integer; XScale, YScale: single; temp: pWaveHdr; a,al,a2, //амплитуды- f,f1,f2: double; //частоты- begin if (uMsg=WIM_DATA) then begin temp:= adr2; if adr2= Qbufheadl then adr2:= @bufhead2 else adr2:= Qbufheadl; if stp2 then WavelnAddBuf fer (hwi, adr2 , SizeOf (TWaveHdr)) ; datal6:= PDatai6(temp.IpData); inwav.Clear; outwav.Clear; forml.series5.Clear; forml.series6.Clear; for i := 0 to BufSize - 1 do begin //набивка- forml.series6.add(datal6A[i]); inwav.add(datai6л[i]) end; FFTQuad (inwav, outwav, 512) ; end; * Настройка сервера на конкретный пульт следующая: на панели конфигурации WinLirc нажимаете кнопку “Learn” (обуче- ние), вводите имя файла настройки пульта. Далее жмете 2 раза ENTER. После надписи <Press a button> - любую кнопку на пуль- те. После надписи <Please wait a second and press it again> - эту же кнопку на пульте. После надписи <Baseline initialized> - дер- жите, пока счетчик с 10 не опустится до 1. Далее сервер запро- сит усреднение пакетов - нажмите и держите кнопку до получе- ния 64 пакетов (для разных пультов - отличается), после чего будет предложено ввести название кнопки (вводите и жмите ENTER)... Все остальные кнопки на пульте: нажимаете каждую кноп- ку до тех пор, пока не будет предложено ввести ее имя (если реакции на эту кнопку нет, нажмите другую), когда кнопки за- кончатся - “ENTER”, затем “Hide window”. Все, файл конфигу- рации на ваш пульт сформирован. осуществим чтение данных из WAV- файла РСМ формата //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(Wl); //1- канал series4.Add(W2) //2- end; врезка 6 TSOP48X DCD RTS GND Рис. 4. Схема электрическая принципиальная приемника ДУ 34 Радиолюбитель - 03/2009
ИЗМЕРЕНИЯ DTMF + FFT 9 x File: 5.wav/Fd [Hz]= 44100 Diskret DTMF(5): Д1 = -12,09 [1335 Hz] Д2= -12,27 [775 Hz] -50- o? -100- 6 10 000 20 000 30 000 40 000 Frequency, Hz |l234567890*#abcd | |2048 fc 1 |p.10 0 S □ T □ M Рис. 3. Отсчеты и спектр файла MP3 сигнала DTMF. Цифра “7’ Рис. 2. Отсчеты и спектр файла MP3 сигнала DTMF. Цифра “5” 5. Аппаратная часть или для тех, кто “дружит"’ с паяльником Если у вас отсутствует готовый приемник ДУ, то его всегда можно собрать самому (см. рис. 4). Вам понадобятся всего несколько деталей: коннектор DB9 с корпусом, 2 резистора, 1 диод типа 1N4148, 1 конденсатор и один 5-ти вольтовый ИК приемник TSOP любой марки или ILMS5360 от“дистанционок” телевизоров (последний можно свободно достать на местном радиорынке). Внешний вид и расположение выводов** наибо- лее распространенных ИК приемников приведены на рис. 5, рис. 6. Приемник ДУ собран навесным монтажом пря- мо в корпусе коннектора на контактах разъема DB9 (см. рис. 7). Длина соединительного шлейфа между датчи- ком и “девайсом” не критична, но по возможности должна быть минимальной, для исключений возмож- ных наводок. Интерфейс или наведем “марафет” Для того, чтобы наша программа выглядела профессионально, нужно “научить” ее менять свой вид. “Как это сделать?” - спросите вы. Нет ничего проще. Нужна скиновая система. Скин - это растровая картинка (или набор), содержа- щая графические элементы программы, т.е. ее внешний вид. Создать ее (нарисовать) можно в декодировка команд пульта врезка 7 //---------- winlirc function comm(var s:string):string; var i fk fkk:integer; begin //1- пробел for i:=length(s) downto 1 do begin if (s[i]=' ") then begin k:=i; break end end; //2- пробел for i:=k-l downto 1 do begin if (s[i]=' ") then begin kk:=i; break end end; //выделение команды- result:=copy(s,kk+l,k-kk-l) end; procedure tforml.ic(s: string); begin //зададимся кнопками пульта- if s=rl' then imagel.OnClick(nil) ; if s=f2’ then image2.OnClick(nil); if s='3T then image3.OnClick(nil) ; if s=4T then image4.OnClick(nil); if s='5T then image5.OnClick(nil) ; if s='6' then image6.OnClick(nil); if s='7 ' then f9.OnClick(nil); if s='8T then sel_channel end; procedure TForml.CSRead(Sender: TObject; Socket: TCustoiriWinSocket) ; var s:string; begin //передача на интерпретатор команд- if Socket.Connected then s:= Socket.ReceiveText; ic(comm(s)) end; procedure TForml.CSError(Sender: TObj ect; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); begin ErrorCode:= 0 //чтоб не выскакивало сообщение об ошибке, если winlirc не запущен end; //---------- winlirc ** Обратите внимание! Распиновка ИК модулей может отли- чаться. Рис. 5. Распиновка ИК приемника TSOP48X GND Рис. 6. Распиновка ИК приемника ILMS5360 Рис. 7. Внешний вид “девайса” 35 Радиолюбитель - 03/2009
ИЗМЕРЕНИЯ Рис. 8. Отсчеты файла MP3 сигнала DTMF. Буква “А” загрузка скина и свойств графических элементов врезка 8 -15 -25 т* -35 т -40 4 -454 proc edurе Т forml.ReadIni(IniFile: TFileName) ; var skin: TIniFile; i : integer; s: string; begin skin:= TIniFile.Create(IniFile); s:= extractfilepath(inifile); for i:=0 to high(im) do im[i].LoadFromFile(s+inttostr(i)+'.bmp'); //подгрузка скиновых элементов- imagel.Picture.Bitmap.Assign(im[l]); image2.Picture.Bitmap.Assign(im[3]); image3.Picture.Bitmap.Assign(im[5]); imaged.Picture.Bitmap.Assign(im[7]); image5.Picture.Bitmap.Assign(im[9]); image6.Picture.Bitmap.Assign(im[ll]); try IMAGE7.FileName:= s+1 logo.avi1; except end; //расположение- imagel.Left:= skin.Readinteger("KEY_ADC",'left',10); imagel.Top := skin.ReadInteger("KEY_ADC",'top',72); image2.Left:= skin.Readinteger("KEY_FILE",'left',10); image2.Top := skin.ReadInteger(vKEY_FILE",'top',164); image3.Left:= skin.Readinteger("KEY_FFT",'left',10); image3.Top := skin.Readinteger("KEY_FFT",'top',253); imaged.Left:= skin.ReadInteger("KEY_PRINT",'left',10); imaged.Top := skin.ReadInteger("KEY_PRINT",'top',343); image5.Left:= skin.Readinteger("KEY_HELP",'left',10); image5.Top := skin.ReadInteger("KEY_HELP",'top',433); image6.Left:= skin.Readinteger("KEY_EXIT",'left',10); image6.Top := skin.Readinteger("KEY_EXIT",'top',492); image7.Left:= skin.ReadInteger("KEY_LOGO",'left',85); image7.Top := skin.ReadInteger("KEY_LOGO",'top',13); finally skin.Free; Canvas.Draw(0, 0, im[0]) end end; -55 т1 dB -60-М -65^ -70-э -75ii -80 М -85-=-* 10 318,834 -59,976 20 000 kHz г 30 000 40 000 1234567890*#abcd «Play DTMF 95 71 -100-И -105^* [ТОЧКИ; 10 00 Параметры (авто) Рис. 9. Спектр сигнала DTMF. Буква “А” любом графическом редакторе, будь-то Photoshop или Corel. Кроме того, скин может содержать информацию об абсолютном положении каждого визуального элемен- та, что дает большую свободу пользователю по заданию внешнего вида программы и делает ее универсальной (врезка 8). В результате получаем следующий интерфейс спект- роанализатора (см. рис. 8, рис. 9). Заключение Итак, мы подошли к заключительной части нашей "виртуальной трилогии”, но на этом тема виртуальных при- боров не исчерпывается, ведь появление все новых и новых видов сложных сигналов обуславливает разви- тие алгоритмов и методов их измерения и декодирова- ния, а значит, многообразие виртуалок будет только уве- личиваться... И пару советов напоследок. Многие собирают прием- ники ДУ с использованием стабилизаторов КРЕН типа 78L05, забывая о том, что мощности СОМ порта недо- статочно, чтоб их тянуть, да и все чаще их питание RS-232 с "матери” не 5 В, а 3 В! Поэтому использование одного диода по цепи питания для исключения бросков отрицательного напряжения оправданно в большинстве случаев. Полные исходные тексты, ресурсы проекта, сервер winlirc, а также справочные данные по ИК приемникам (файл dtmf_res.zip) вы можете загрузить с сайта нашего журнала: http://www.radioliga.com (раздел "Программы”) а также с сайта автора: http://raxp.radioliga.com Если тема представляет для вас интерес - пишите, задавайте вопросы на форуме: http://raxp.radioliga.com/forum Ресурсы 1. AVR314: Двутональный DTMF генератор - http://www.gawTuZhtml.cgi/txVapp/micros/avi7index.htm 2. MSP430: DTMF-Controlled - http7/www.gaw.ru/html.cgi/txt/app/micros/msp430/index.htm 3. Practical Design Techniques for Sensor Signal Conditioning, Analog Devices, 1998 4. E. Бадло, С. Бадло. Виртуальные приборы. Генератор сигналов без паяльника. Радиолюбитель, 2009, №1, с. 29-31. 5. Скачать последние версии сервера - http://lirc.sourceforge.net 6. http7/raxp.radioliga.com/cnt/s.php?p=bt.zip 7. Ресурсы тестового модуля DTMF-FFT и ПО SPEKTRA - http://raxp.radioliga.com/cnt/s.php?p=dtmf_res.zip 36 Радиолюбитель - 03/2009