Введение Целью является описать подход к проблеме с точки зрения инженера, без излишней наукообразности, так чтобы, было понятно, как использовать достижения отцов математиков нам простым инженерам. Для начала напомним некоторые базовые понятия. Сигнал Уровень, энергия, мощность сигнала – суть одно и то же. Считается, что сигнал имеет мощность (уровень) 0 dB, если он выделяет 1 mW тепла на резисторе 600 Om. Синусоидальный сигнал такой мощности будет иметь эффективное значение напряжения: Vrms = P • R = 0.001W • 600Om = 0.7745966[V ] Амплитуда его будет равна: Vp = 2 • Vrms = 1.0954449[V ] Уровень, в dB, синусоидального сигнала, имеющего амплитуду V, определяется по формуле: V ⎛ ⎞ L = 20 • Log10⎜ ⎟ ⎝ 1.0954449 ⎠ Если говорят, что сигнал «А» отличается от сигнала «Б» на «L» dB, то это значит, что L А = К * Б, где K = 10 20 Если разность сигналов равна 6 dB, то первый сигнал больше второго примерно в 2 раза, 6 K = 10 20 = 1.995262315 Мощность (P) сигнала произвольной формы определяется как корень из суммы квадратов выборок (измерений) сигнала (S), деленный на количество выборок (N), чем больше N, тем точнее получится вычисление: N −1 P= ∑S 2 n 0 N [1] Амплитуда (Vp) синусоидального сигнала мощностью P равна: N −1 Vp = 2 * P = 2 * ∑ S2n 0 N [2] Выборка сигнала (результат единичного измерения) представляется двоичным числом определенной разрядности. На практике широко распространено 12 битное представление в формате 1.12, один знаковый разряд и 12 значащих разрядов. Согласно кодированию по закону A-law (G.711) максимальный уровень (амплитуда) сигнала составляет 3.14 dB. Число 4096 (0х1000) соответствует уровню 3.14 dB, однако Урусов А.В. www.butovo.com/~uav 1 оно не достижимо, реальный диапазон положительных значений 0…4095 (0хFFF). Легко определить число соответствующее уровню 0 dB, оно будет на 3.14 dB меньше чем 4096: S = 10 −3.14 20 • 4096 = 0.696626514 • 4096 = 2853.382202 ≈ 2853 , что соответствует напряжению 1.0954449 V. Если мы сформируем внутри DSP синус амплитудой 2853(0.696626514) по A-law и выведем его наружу, используя стандартный кодек, то на выходе получим аналоговый сигнал с уровнем 0 dB. Однако DSP как правило имеют 16 битное представление чисел в формате 1.15, что обеспечивает числовой диапазон вещественных чисел: от -1.0 до 0.999969482421875 с шагом 0.000030518509476 (от 0x8000 до 0x7FFF с шагом 0x0001). Однако, если число в таком формате (fract16 для ANALOG DEVICE) передается из DSP в HOST компьютер (PC), то для правильного перевода в формат FLOAT или DOUBLE, необходимо это число поделить на 32768 (2^15): volatile short int Lev; double dLevel = Lev/32768.0; где, Lev – это число (fract16), пришедшее из DSP. Ниже приведен код программы для вычисления N выборок синусоидального сигнала произвольного уровня L, с частотой квантования 8000 kHz и разрядностью представления 1.12 (A-law): #define #define #define #define #define M_PI D2F(x) DB2K(dB) K2DB(k) KDB0 3.141592653589793 ((short int)((x>0)?(x+0.5):(x-0.5))) (pow(10.0,(dB)/20.0)) (20*log10(k)) (DB2K(-3.14)) //pSin - pointer to output buffer //N - number of samples //freq - freq in Hz //L - level in dB //nPhase - start PHASE in degrees (0..360) void GetSinusArb(short int* pSin, int N, int freq, double L, int nPhase){ double dDeltaPhase = 2.0*M_PI*freq/8000.0; double dK = 4096.0*KDB0*DB2K(L);//convert dB to coefficient double dPh = nPhase*M_PI/180.0; double dCurrentPhase = dPh; for(int i=0;i<N;i++){ *pSin++ = D2F(dK*sin(dCurrentPhase)); dCurrentPhase += dDeltaPhase; } } Переменная dDeltaPhase хранит приращение фазы сигнала, соответствующее переходу от одной выборке к другой. Во избежание перегрузок L не должно превышать 3.14 dB, вычисленный таким образом сигнал можно выводить на “Sound Blaster” и далее по назначению. Метод Goertzel Данный метод позволяет вычислить магнитуду (вес) искомой частоты в анализируемом сигнале. Алгоритм вычислений подобен IIR фильтру второго порядка, при чем выходное зачение вычисляется один раз для всех N выборок сигнала. Метод требует Урусов А.В. www.butovo.com/~uav 2 существенно меньшего количества вычислений (по сравнению с DFT) и не требует предварительного накопления всех N выборок входного сигнала. Вычисления разбиваются на два этапа: промежуточные вычисления (Q1,Q2) на N выборках и однократное вычисление магнитуды (M). В вычислениях используется коэффициент (Coeff), определяемый по формуле: K = D2F(N*Freq/8000.0); //K – integer Coeff = 2.0*cos(2*M_PI*K/N); Q1 = Q2 = 0; //-- get Q1 Q2 -for(int i = 0;i<N;i++){ Q = Coeff*Q1 - Q2 + Sample[i]/Kdiv; Q2 = Q1; Q1 = Q; }//sample loop //-- get MNSQR ==== M = Q2*Q2 + Q1*Q1 - Q1*Q2*Coeff; Где: • • • • • • • • Freq – искомая частота (Hz) N – количество выборок Sample – выборка (мгновенное значение) сигнала 8000 – частота выборки (квантования) в Hz Kdiv – коэффициент ослабления входного сигнала Coeff – коэффициент Гертцеля K – число периодов искомой частоты, укладывающихся в N периодов частоты квантования (должно быть близко к целому числу) M – магнитуда частоты Freq Необходимо выбрать такое N, чтобы K было как можно ближе к целому значению. В Таблица 1 приводятся значения N хорошо подходящие для частоты 1000Гц, как нетрудно заметить все значения кратны 8 (8000/1000). N 16 24 32 40 48 56 64 72 80 88 96 104 112 120 128 136 144 152 160 Урусов А.В. K 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Таблица 1 Coeff F[Hz] 1.414214 1000 1.414214 1000 1.414214 1000 1.414214 1000 1.414214 1000 1.414214 1000 1.414214 1000 1.414214 1000 1.414214 1000 1.414214 1000 1.414214 1000 1.414214 1000 1.414214 1000 1.414214 1000 1.414214 1000 1.414214 1000 1.414214 1000 1.414214 1000 1.414214 1000 www.butovo.com/~uav Ferr[%] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Time[mS] 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 3 Ослабление (деление на Kdiv) входного сигнала необходмимо для избежания переполненния разрядной сетки DSP, Kdiv выбирается исходя из динамического диапазона входного сигнала. На Рисунок 1 представлена АЧХ фильтра для частоты 1000 Гц, количество выборок 200, уровень сигнала 0 дБ и без ослабления входного сигнала. Ширина лепестков фильтра определяется формулой: ΔF = Где: • • Fq [3] N Fq – частота квантования N – количество выборок 8000 Hz = 40 Hz . 200 Значение магнитуды частоты синусоидального сигнала амплитудой Vp можно предсказать как : Для случая на Рисунок 1 имеем ΔF = 2 ⎛ Vp * N ⎞ M =⎜ ⎟ [4] ⎝ 2 * Kdiv ⎠ Где: • • • Vp – амплитуда синуса N – количество выборок Kdiv – коэффициент ослабления входного сигнала F=1000Hz N=200 Level=0dB Kdiv=1 Магнитуда 80 70 60 50 40 30 20 10 0 800 840 880 920 960 1000 1040 1080 1120 1160 1200 F[Hz] Рисунок 1 На практике требуется детектирование сразу нескольких частот, при этом не всегда удается подобрать N одинаковое для всех частот и приходится использовать различные N. При этом значения магнитуд разных частот ( при одинаковых их мощностях) будут Урусов А.В. www.butovo.com/~uav 4 отличаться друг от друга. Однако можно выравнять магнитуды, например по меньшему N, для удобства их последующего сравнения, используя корректирующий коэффициент: 2 ⎛ N min ⎞ = M N *⎜ ⎟ [5] ⎝ N ⎠ M norm Где: • • • • Mnorm – нормализованная магнитуда искомой частоты Mn – исходная магнитуда искомой частоты Nmin – минимальное число выборок среди всех частот N – число выборок искомой частоты Для сравнения уровней двух частот нет необходимости переводить магнитуды в уровни (Vp), можно обойтись сравнением магнитуд, учитывая, что их отношение будет равно отношению квадратов уровней: ⎛ V1 ⎜⎜ ⎝ V2 2 ⎞ M ⎟⎟ = 1 [6] M2 ⎠ Для определения соотношения сигнал/шум можно сравнить мощность сигнала из выражения [2] и мощность магнитуды: VM = 2 * Kdiv * M [7] N Во избежание извлечения корня квадратного, можно сравнивать квадраты мощностей, тогда нормализованные магнитуды нескольких частот можно суммировать. VM2 1+ M 2 = 4 * Kdiv 2 * (M 1 + M 2 ) [8] 2 N min Прецизионное детектирование частоты На практике, вычисленная магнитуда будет отличаться от эталонной, и невозможно догадаться, вызвано это отклонением частоты или уровня сигнала. Чтобы гарантировано детектировать частоту в некотором диапазоне (по частоте и уровню) можно использовать 3 фильтра с частотами меньшей, равной и большей. Например, DTMF(Q.24 NTT) предписывает детектировать частоты с отклонением до +-1.8% и не детектировать с отклонениями более +-3%, а также допускает отличие уровней в паре частот до 5dB. На Рисунок 2 представлены характеристики трех фильтров для частоты 1336Гц (боковые частоты имеют смещение 4%). Коричневым цветом обозначена характеристика результирующего фильтра, который имеет узкую полосу (примерно 4%) и плоскую характеристику. Выходы 3 фильтров сравниваются для получения результирующей магнитуды. Ниже : • M – магнитуда центра фильтра (1336Гц), а также выходная • Mm – магнитуда фильтра -4% (1276 Гц) • Mp – магнитуда фильтра +4%( 1396 Гц) Урусов А.В. www.butovo.com/~uav 5 1283 1285 1287 1289 1291 1293 1295 1297 1299 1301 1303 1305 1307 1309 1311 1313 1315 1317 1319 1321 1323 1325 1327 1329 1331 1333 1335 1337 1339 1341 1343 1345 1347 1349 1351 1353 1355 1357 1359 1361 1363 1365 1367 1369 1371 1373 1375 1377 1379 1381 1383 1385 1387 Mnsqr if ((M >= Mm) && (M >= Mp)){ if (Mm > Mp) M = M + (Mm + 2.25*Mp)*1.17; else M = M + (2.25*Mm + Mp)*1.17; } else M = 0; 6000 5000 4000 3000 2000 1000 0 Freq 1336-4% Урусов А.В. 1336 1336+4% www.butovo.com/~uav Corrected Mnsqr Рисунок 2 6 Реализация для ADSP2181 Наиболее затратные фрагменты программы целесообразно писать на ассемблере, в то время как, основная часть обычно реализуется на языке C/C++. Такой подход обеспечивает приемлемую производительность при разумном время разработки. Ниже приведены две подпрограммы, написанные на ассемблере с учетом соглашений о регистрах и передаче парметров , принятых в IDE VDSP. I0 scratch I1 scratch I2 preserved I3 preserved I4 dedicated: SP I5 preserved I6 scratch I7 preserved M0 preserved M1 dedicated: +1 M2 dedicated: 0 M3 scratch M4 dedicated: FP M5 scratch M6 dedicated: 0 M7 dedicated: -1 L7..0 = 0 Первая подпрограмма _getQ вычисляет значения промежуточных значений Q1,Q2, вторая (_getM) – собственно магнитуду. Основные вычисления производятся исключительно на MAC, что позволяет избежать переполнений в промежуточных операциях. Для минимизации времени выполнения надо стараться использовать мультиинструкции DSP, совмещающие например операцию в MAC и пересылки с памятью. #define KDIV 20 #define KMULT 32768/KDIV #define CHANNEL_STRIDE 1 .section/code program; .global _getQ,_getM; _getQ: //======== Get Q1,Q2 ========== // Q = K*X + COEFF*Q1 - Q2 // I0 - pointer to sample (DM) // I1 - pointer to Q1.Q2 (DM) ALIGN 2 // AX0 - save M0 // CNTR = amount of samples // AR = constant -1.0 // MX0 = coeff // MX1 = KMULT // MY0 = sample/Q2 // MY1 = Q1 //---- GET PARAMETRS ----------M5 = 4; MODIFY(I4+=M5);//point to first parameter AX0 = DM(I4+=M7); CNTR = AX0;//N MX0 = DM(I4+=M7); //coef AX0 = DM(I4+=M7); I1 = AX0;//pointer to Q1Q2 AX0 = DM(I4+=M7); I0 = AX0;//pointer to sample Урусов А.В. www.butovo.com/~uav 7 //--- INIT SOME REGS -----------AX0 = M0; M0 = CHANNEL_STRIDE; // save M0 M3 = -1; L1 = 2; MX1 = KMULT; AR = -1.0r;// 0x8000 (fract -1.0) DIS M_MODE;//set fractional MAC mode //---- MAIN LOOP --------------DO __end1 UNTIL CE; MY0 = DM(I0,M0) ; //MY0 = sample MR = MX1 * MY0 (US), MY1 = DM(I1,M1); // KMULT*SAMPLE, MY1= Q1 MR = MR + MX0 * MY1(US), MY0 = DM(I1,M2); // +coeff*Q1, MY0 = Q2 MR = MR + AR * MY0 (RND), DM(I1,M3) = MY1; // + (-1.0)*Q2, Q2 = Q1 __end1: DM(I1,M2) = MR1; //Q1 = Q ENA M_MODE; //---- RESTORE ...--------L1 = 0; M0 = AX0;//restore M0 RTS; _getQ.end: //========= Get MNSQR ======================== _getM: //---- GET PARAMETRS ----------I0 = AR; //pointer to Q1Q2 MY0 = AY1;//coeff MX0 = DM(I0,M1);//Q1 MY1 = DM(I0,M1);//Q2 DIS M_MODE; MR = MX0 * MY1(RND); AR = MR1; MR = MX0 * MX0(SS); MX1 = MY1; MR = MR + MX1 * MX1(SS); MR = MR - AR * MY0(SU); MR = MR (RND); IF MV SAT MR; ENA M_MODE; //set fractional MAC mode //Q1*Q2 //save Q1*Q2 //Q1*Q1 //copy Q2 //Q1*Q1 + Q2*Q2 //Q1*Q1 + Q2*Q2 - Q1*Q2*coeff AX1 = MR1; //return value RTS; _getM.end: Пример вызова подпрограмм из кода на С приведен ниже. #pragma regs_clobbered "I0,I1,M3,M5,AX0,AR,MX0,MX1,MY0,MY1,MR,CNTR,ASTAT" extern void getQ(int* pSample, int* pQ, unsigned int coeff, unsigned int N); #pragma regs_clobbered "I0,M5,AX0,AR,MX0,MX1,MY0,MY1,MR,ASTAT" extern int getM(int* pQ, unsigned int coeff); #include "697_1209.h" //set Sample[320] signal data (int Sample[] = {0,10,…}) #pragma align 2 int Q1Q2[2]; int mnsqr; main (){ memset(Q1Q2,0,2); getQ(Sample,Q1Q2,55960,264);//697 hZ (1352 DSP cycles) mnsqr = getM(Q1Q2,55960); } Урусов А.В. www.butovo.com/~uav 8 //1352*12.5ns/264 = 64ns/sample //125us/(64ns*8*3) = 81 (dtmf recievers) При тактовой частоте DSP 80 мГц (время такта 12.5 нс) работающих приемников DTMF может достигать 80. число одновременно На моей страничке Урусов А.В. можно найти программу GoerTest (Рисунок 4) детектирования DTMF (эмулирующую арифметику DSP AnalogDevice) демонстрирующую описанный выше подход. Программа может работать как с файлами семплов, так и напрямую от звукового устройства PC (требуется настройка режима записи). Для создания тестовых файлов сигналов можно использовать мою программу «Генератора многочастотных сигналов» MfsGen (Рисунок 3). Рисунок 3 Генерируются две DTMF цифры (1 и 8) с наложенными сигналами Dial Tone (425Гц и 350&440Гц), DTMF частоты и уровни смещены в допустимых пределах, длительность цифр 40 и 60 млсек. Урусов А.В. www.butovo.com/~uav 9 • • • • • • • • • • Рисунок 4 N – номер Dg – цифра (digit) Lev – уровень [dB] сигнала Ovf – переполнения разрядной сетки DSP M0-M7 – магнитуды в формате INT V – мощность сигнала M – мощность полезной составляющей (DTMF + DIALTONE) MVR – отношение M/V DgL – примерная длительность цифры [млсек] Time – время от предыдущего события 25.05.2011 Урусов А.В. www.butovo.com/~uav 10