Статья 3. Вычисление модуля вектора (работа над ошибками)
Часть 5.
Результаты профилирования на компьютере
Для получения результатов были написаны тестовые программы в Microsoft Visual C++ 2010 Express.
Для исследования времени выполнения алгоритма, за эталон была взята функция sqrt() из <cmath>. Счет времени производился при помощи rdtsc инструкции - для этих целей был создан класс CProfiler для профилирования кода.
- /* sqrt_profile.cpp */
- //объявление профилируемого алгоритма
- #include "sqrtIQ.hpp"
- #include "profiler.hpp"
- #include <iostream>
- #include <cmath>
- #include <fstream>
- int main()
- {
- std::fstream file;
- file.open("sqrt_profile_log.txt", std::ios::out | std::ios::trunc);
- float i = 0.0125f;
- float q = 0.567f;
- float temp = 0.0f;
- float resultLib = 0.f, resultMy = 0.f;
- DWORD clocksLib = 0, clocksMy = 0;
- CProfiler profile;
- profile.prefix();
- //профилируемый код
- temp = i*i + q*q;
- resultLib = std::sqrt( temp );
- // конец профилируемого кода
- clocksLib = profile.postfix();
- profile.clear();
- profile.prefix();
- resultMy = sqrtIQ( i, q );
- clocksMy = profile.postfix();
- file << "<cmath> : Result of sqrt( ( "<< i <<" )^2 + ( "<< q <<" )^2 ) = ";
- file << resultLib << "\t Elapsed Time, in clocks: " << clocksLib << std::endl;
- file << "sqrtIQ: Result of sqrtIQ( "<< i <<", "<< q <<" ) = " << resultMy ;
- file << "\t\t\t Elapsed Time, in clocks: " << clocksMy ;
- file << "\t Clock Ratio, stdLib/myFunc : " << (float) clocksLib / clocksMy <<std::endl;
- std::cout << " Profiling is complete. Refer to log file in project folder" << std::endl;
- file.close();
- std::cin.get();
- return 0;
- }
Результат в файле sqrt_profile_log.txt :
<cmath> : Result of sqrt( ( 0.0125 )^2 + ( 0.567 )^2 ) = 0.567138 Elapsed Time, in clocks: 5820
sqrtIQ: Result of sqrtIQ( 0.0125, 0.567 ) = 0.567138 Elapsed Time, in clocks: 1704 Clock Ratio, stdLib/myFunc : 3.41549
Интересно было сравнить абсолютную и относительную ( в % ) точность измерения, а так же количество заходов алгоритма в цикл while() (sqrtIQ.hpp, 43 строка). Все измерения производятся в файле sqrt_iterations.cpp:
- /* sqrt_iterations.cpp */
- #include <iostream>
- #include <cmath>
- #include <fstream>
- #define ITERATIONS
- #include "sqrtIQ.hpp"
- int main() {
- std::fstream file;
- //ios::out - открыть для записи с начала файл
- //ios::trunc - очистить файл если он существует
- file.open( "sqrt_iterations_log.txt", std::ios::out | std::ios::trunc );
- float i, q, temp;
- //variables for sqrt's result
- float sqrtLib, sqrtMy;
- float error, errorPer;
- file << " " << std::endl;
- file << "/**********************************************************************************************/" << std::endl;
- file << "Опыт №1: Определяем количество итераций вычисления значения корня квадратного по методу Ньютона." << std::endl;
- file << "Это количество прохода цикла while( firstEstimation - nextEstimation != 0 ) { <..> } " << std::endl;
- file << "error = std::fabs( sqrtLib - sqrtMy);" << std::endl;
- file << "error, % вычисляется как: 100 * error/ sqrtLib; " << std::endl;
- file << " " << std::endl;
- for( int k = 0, g = 100; k < 100; ++k, --g) {
- i = ( float )k/10;
- q = ( float )g/10;
- temp = i*i + q*q;
- sqrtLib = sqrt( temp );
- sqrtMy = sqrtIQ(i, q);
- error = std::fabs( sqrtLib - sqrtMy);
- errorPer = 100 * error/ sqrtLib;
- file << "<cmath> sqrt("<< temp <<"): " << sqrtLib << "\t my sqrt("<< i <<","<< q <<"): " << sqrtMy;
- file << "\t perform in " << iterationCnt << " iterations" ;
- file << "\t error: " << error << "\t error, %: " << errorPer <<std::endl;
- } // for
- file.close();
- std::cin.get();
- return 0;
- }
Результаты в файле sqrt_iterations_log.txt (приведу неполный файл):
/**********************************************************************************************/
Опыт №1: Определяем количество итераций вычисления значения корня квадратного по методу Ньютона.
Это количество прохода цикла while( firstEstimation - nextEstimation != 0 ) { <..> }
error = std::fabs( sqrtLib - sqrtMy);
error, % вычисляется как: 100 * error/ sqrtLib;
<cmath> sqrt(100): 10 my sqrt(0,10): 10 perform in 1 iterations error: 0 error, %: 0 MIN
<cmath> sqrt(98.02): 9.9005 my sqrt(0.1,9.9): 9.90051 perform in 26 iterations error: 6.67572e-006 error, %: 6.74281e-005
<cmath> sqrt(96.08): 9.80204 my sqrt(0.2,9.8): 9.80205 perform in 26 iterations error: 1.14441e-005 error, %: 0.000116752
<cmath> sqrt(94.18): 9.70464 my sqrt(0.3,9.7): 9.70465 perform in 26 iterations error: 1.71661e-005 error, %: 0.000176886
<cmath> sqrt(92.32): 9.60833 my sqrt(0.4,9.6): 9.60833 perform in 34 iterations error: 1.90735e-006 error, %: 1.9851e-005
<cmath> sqrt(90.5): 9.51315 my sqrt(0.5,9.5): 9.51318 perform in 26 iterations error: 2.86102e-005 error, %: 0.000300744
<cmath> sqrt(88.72): 9.41913 my sqrt(0.6,9.4): 9.41916 perform in 26 iterations error: 3.33786e-005 error, %: 0.00035437
<cmath> sqrt(86.98): 9.32631 my sqrt(0.7,9.3): 9.32635 perform in 26 iterations error: 3.8147e-005 error, %: 0.000409025
<cmath> sqrt(85.28): 9.23472 my sqrt(0.8,9.2): 9.23476 perform in 26 iterations error: 4.29153e-005 error, %: 0.000464717
<cmath> sqrt(83.62): 9.1444 my sqrt(0.9,9.1): 9.14445 perform in 26 iterations error: 4.76837e-005 error, %: 0.000521453
<cmath> sqrt(82): 9.05539 my sqrt(1,9): 9.05544 perform in 26 iterations error: 5.14984e-005 error, %: 0.000568705
<cmath> sqrt(80.42): 8.96772 my sqrt(1.1,8.9): 8.96778 perform in 26 iterations error: 5.62668e-005 error, %: 0.000627437
<cmath> sqrt(78.88): 8.88144 my sqrt(1.2,8.8): 8.8815 perform in 26 iterations error: 6.00815e-005 error, %: 0.000676483
<cmath> sqrt(77.38): 8.79659 my sqrt(1.3,8.7): 8.79665 perform in 26 iterations error: 6.38962e-005 error, %: 0.000726374
<cmath> sqrt(75.92): 8.71321 my sqrt(1.4,8.6): 8.71328 perform in 26 iterations error: 6.67572e-005 error, %: 0.000766161 MAX
<cmath> sqrt(74.5): 8.63134 my sqrt(1.5,8.5): 8.63134 perform in 40 iterations error: 0 error, %: 0 MIN
<cmath> sqrt(73.12): 8.55102 my sqrt(1.6,8.4): 8.55102 perform in 39 iterations error: 1.90735e-006 error, %: 2.23055e-005
<cmath> sqrt(71.78): 8.47231 my sqrt(1.7,8.3): 8.47231 perform in 39 iterations error: 9.53674e-007 error, %: 1.12564e-005
<cmath> sqrt(70.48): 8.39524 my sqrt(1.8,8.2): 8.39524 perform in 39 iterations error: 9.53674e-007 error, %: 1.13597e-005
<cmath> sqrt(69.22): 8.31986 my sqrt(1.9,8.1): 8.31985 perform in 39 iterations error: 1.90735e-006 error, %: 2.29253e-005
<cmath> sqrt(68): 8.24621 my sqrt(2,8): 8.24621 perform in 39 iterations error: 9.53674e-007 error, %: 1.1565e-005
<cmath> sqrt(66.82): 8.17435 my sqrt(2.1,7.9): 8.17435 perform in 39 iterations error: 9.53674e-007 error, %: 1.16667e-005
<cmath> sqrt(65.68): 8.10432 my sqrt(2.2,7.8): 8.10432 perform in 41 iterations error: 9.53674e-007 error, %: 1.17675e-005
<cmath> sqrt(64.58): 8.03617 my sqrt(2.3,7.7): 8.03617 perform in 39 iterations error: 9.53674e-007 error, %: 1.18673e-005
<cmath> sqrt(63.52): 7.96994 my sqrt(2.4,7.6): 7.96994 perform in 39 iterations error: 1.43051e-006 error, %: 1.79488e-005
<cmath> sqrt(62.5): 7.90569 my sqrt(2.5,7.5): 7.90569 perform in 39 iterations error: 9.53674e-007 error, %: 1.20631e-005
<cmath> sqrt(61.52): 7.84347 my sqrt(2.6,7.4): 7.84347 perform in 39 iterations error: 9.53674e-007 error, %: 1.21588e-005
<cmath> sqrt(60.58): 7.78332 my sqrt(2.7,7.3): 7.78331 perform in 39 iterations error: 9.53674e-007 error, %: 1.22528e-005
<cmath> sqrt(59.68): 7.72528 my sqrt(2.8,7.2): 7.72528 perform in 39 iterations error: 1.43051e-006 error, %: 1.85173e-005
<cmath> sqrt(58.82): 7.66942 my sqrt(2.9,7.1): 7.66942 perform in 39 iterations error: 1.43051e-006 error, %: 1.86521e-005
<cmath> sqrt(58): 7.61577 my sqrt(3,7): 7.61577 perform in 39 iterations error: 9.53674e-007 error, %: 1.25224e-005
<cmath> sqrt(57.22): 7.56439 my sqrt(3.1,6.9): 7.56439 perform in 39 iterations error: 9.53674e-007 error, %: 1.26074e-005
<cmath> sqrt(56.48): 7.51532 my sqrt(3.2,6.8): 7.51532 perform in 39 iterations error: 9.53674e-007 error, %: 1.26897e-005
<cmath> sqrt(55.78): 7.4686 my sqrt(3.3,6.7): 7.4686 perform in 39 iterations error: 1.43051e-006 error, %: 1.91537e-005
<cmath> sqrt(55.12): 7.42428 my sqrt(3.4,6.6): 7.42428 perform in 42 iterations error: 4.76837e-007 error, %: 6.42267e-006
<cmath> sqrt(54.5): 7.38241 my sqrt(3.5,6.5): 7.38241 perform in 42 iterations error: 4.76837e-007 error, %: 6.4591e-006
<cmath> sqrt(53.92): 7.34302 my sqrt(3.6,6.4): 7.34302 perform in 39 iterations error: 9.53674e-007 error, %: 1.29875e-005
<cmath> sqrt(53.38): 7.30616 my sqrt(3.7,6.3): 7.30616 perform in 39 iterations error: 9.53674e-007 error, %: 1.3053e-005
<cmath> sqrt(52.88): 7.27186 my sqrt(3.8,6.2): 7.27186 perform in 42 iterations error: 4.76837e-007 error, %: 6.55729e-006
<cmath> sqrt(52.42): 7.24017 my sqrt(3.9,6.1): 7.24016 perform in 39 iterations error: 9.53674e-007 error, %: 1.3172e-005
< не полный файл sqrt_iterations_log.txt >
Графическая интерпретация полученных результатов по поводу неточности вычислений функции sqrtIQ() представлена ниже. Первый график (первые два рисунка) показывает зависимость ошибки от отношения синфазной и квадратурной составляющих цифрового сигнала. Синфазная составляющая - это вектор от 0 до 10 с шагом 0,1; квадратурная составляющая - это вектор от 10 до 0 с шагом 0,1. На втором графике равномерно по оси абсцисс просто выводятся результаты вычисленной ошибки.
Вывод: полученный алгоритм быстрее библиотечной функции, примерно, в 3 раза, за что мы расплачиваемся ухудшением точности, но это ухудшение незначительное для наших целей (максимум погрешности 0,00076 % ). Если же сравнивать с аппроксимацией 4 порядка (видимо, по ряду Тейлора) по этой ссылке , то точность sqrtIQ() в 10 раз лучше.
Примечание 1. Если в алгоритме, что записан в файле sqrtIQ.hpp в строках 42, 46 заменить операцию деления на 2 умножением на 0,5, то по скорости получится еще больший выигрыш (до замены соотношение было около 3.3):
<cmath> : Result of sqrt( ( 0.0125 )^2 + ( 0.567 )^2 ) = 0.567138 Elapsed Time, in clocks: 5792
sqrtIQ: Result of sqrtIQ( 0.0125, 0.567 ) = 0.567138 Elapsed Time, in clocks: 1176 Clock Ratio, stdLib/myFunc : 4.92517
<cmath> : Result of sqrt( ( 0.0125 )^2 + ( 0.567 )^2 ) = 0.567138 Elapsed Time, in clocks: 4915
sqrtIQ: Result of sqrtIQ( 0.0125, 0.567 ) = 0.567138 Elapsed Time, in clocks: 1172 Clock Ratio, stdLib/myFunc : 4.19369
Примечание 2. Графики строились в среде МатЛаб при помощи шаблонного скрипта, который требовалось лишь немного подправить. Сам скрипт:
- %This script is used for quick plotting the experimental data from file
- %specify the name of file with data that must be plotted
- file = 'sqrt_iterations_error.dat';
- fid = fopen( file, 'r');
- if fid == -1
- error('file is not opened');
- end
- %read the data
- data = fscanf( fid,'%f', inf );
- %DELETE - example specified
- %
- %form additional data for plotting data
- % in-phase and quadrature samples that used for calculating sqrt()
- I = 0:0.1:10;
- Q = sort( I, 'descend'); %contains 10 9.9 9.8 ...
- %preallocate 1D column vector
- quotient = zeros( 1, 100, 'single');
- for i=1:100
- quotient(i) = I(i) / Q(i);
- end
- display( quotient );
- %
- %DELETE
- %print into screen
- fprintf(1, '%.11fn', data );
- plot( quotient, data );
- %MODIFY - example specified
- %the first graph
- title('Error of sqrtIQ() function (with respect to <cmath> sqrt())');
- xlabel('Ratio of the in-phase to quadrature: I/Q');
- ylabel('Error, %');
- %the second graph
- %plot( data );
- %title('Error of sqrtIQ() function (with respect to <cmath> sqrt())');
- %xlabel('Uniform scale');
- %
- %MODIFY
- fclose ( fid );
Часть 6.
Результаты работы на целевой платформе TMS320F28335.
Проверка работы алгоритма производилась в программе, которая является частью цифрового детектора. В этой программе была реализована первая ступень фильтрации-децимации цифрового детектора как в синфазном, так и квадратурном канале, и как логическое завершение - находился модуль вектора по I-Q отсчетам при помощи шаблонной функции sqrtIQ():- #include "fircoeff.h" //from matlab
- #include "filter.hpp"
- #include "adcmodel.hpp"
- #include "multiplication.hpp"
- #include "sqrtIQ.hpp"
- #define WDCR *((volatile int *)0x7029) /* WD Control reg */
- #define DISABLE_WD 0x0068
- void Disable_WD(void)
- {
- asm(" eallow");
- WDCR |= DISABLE_WD;
- asm(" edis");
- }
- //this is the samples amount in the *.dat file
- const int kLength = 5208;
- float inPhaseSamples[ kLength ];
- float quadratureSamples[ kLength ];
- static void dataIO();
- float g_sampleI = 0, g_sampleQ = 0;
- float g_firOutI = 0, g_firOutQ = 0;
- float g_sqrtOut = 0;
- int main() {
- Disable_WD();
- //function dataIO() to catch the breakPoint and read
- //all massive of samples into inPhaseSamples buffer
- dataIO();
- //load samples into quadratureSamples
- dataIO();
- CAdcModel adcI( inPhaseSamples, kLength );
- CAdcModel adcQ( quadratureSamples, kLength);
- //filter for in-phase channel
- dsp::CFirFilter firFilterI( FIR_coeff, BL, 10 );
- //filter for quadrature channel
- dsp::CFirFilter firFilterQ( FIR_coeff, BL, 10 );
- bool isReadyFirI, isReadyFirQ;
- while(1) {
- //reading ADC
- adcI.getSample( g_sampleI );
- adcQ.getSample( g_sampleQ );
- isReadyFirI = firFilterI.filtrate( g_sampleI, g_firOutI );
- isReadyFirQ = firFilterQ.filtrate( g_sampleQ, g_firOutQ );
- if( ( isReadyFirI == true ) &&
- ( isReadyFirQ == true ) ) {
- //here "g_out"/"g_firOutI/Q " contains proof FIR-decimator-output value.
- g_sqrtOut = dsp::sqrtIQ( g_firOutI, g_firOutQ );
- //Function to view the output of sqrt()
- dataIO();
- }
- }// while
- return 0;
- }
- /**
- @function dataIO
- The dataIO function acts as a placeholder.It is a convenient place to connect
- a Probe Point (or BreakPoint in the new CCS) that injects data from a PC file.
- @detailed - for details see spru301, 4.3 Adding a Probe Point for File I/O
- */
- static void dataIO() {
- /* do data I/O */
- return;
- }
Результаты работы алгоритма, оказались неожиданными:
Что то не так! Должна была быть на графике низкочастотная огибающая:
Первая идея - что-то не так с алгоритмом sqrtIQ(). Действительно. Оказалось, что отрицательные значения на выходе sqrtIQ(), появляются тогда, когда значения ВЧ-заполнения входного сигнала становятся отрицательными (когда ВЧ несущая меньше нуля). В этом случае алгоритм “альфа максимум + бета минимум” выдает отрицательное начальное приближение, и уже начиная с этого отрицательного значения, запускается расчет корня по алгоритму Ньютона, который, естественно, сходится к отрицательному числу.
Для преодоления проблемы написана шаблонная функция вычисления модуля (вычисляет по отдельности модуль для I и Q):
- template< class T >
- void absIQ( T& i, T& q ) {
- i = ( i >= (T)0 ) ? i : -i;
- q = ( q >= (T)0 ) ? q : -q;
- }
absIQ() была добавлена в начало функции sqrtIQ() (до начала "alpha max plus beta min"). Результат не заставил долго себя ждать:
Комментариев нет:
Отправить комментарий