Нравится? Делимся информацией!

понедельник, 14 января 2013 г.

Вычисление модуля вектора ( sqrt( I^2 + Q^2 ) ). Результаты




В статье производится тестирование алгоритма на производительность как на компьютере ,так и на целевой платформе (TMS320F28335).

Статья 3. Вычисление модуля вектора (работа над ошибками)



Часть 5.

Результаты профилирования на компьютере

Для получения результатов были написаны тестовые программы в Microsoft Visual C++ 2010 Express.
Для исследования времени выполнения алгоритма, за эталон была взята функция sqrt() из <cmath>. Счет времени производился при помощи rdtsc инструкции - для этих целей был создан класс CProfiler для профилирования кода.

  1. /* sqrt_profile.cpp */
  2. //объявление профилируемого алгоритма
  3. #include "sqrtIQ.hpp"
  4. #include "profiler.hpp"
  5. #include <iostream>
  6. #include <cmath>
  7. #include <fstream>
  8. int main()
  9. {
  10. std::fstream file;
  11. file.open("sqrt_profile_log.txt", std::ios::out | std::ios::trunc);
  12. float i = 0.0125f;
  13. float q = 0.567f;
  14. float temp = 0.0f;
  15. float resultLib = 0.f, resultMy = 0.f;
  16. DWORD clocksLib = 0, clocksMy = 0;
  17. CProfiler profile;
  18. profile.prefix();
  19. //профилируемый код
  20. temp = i*i + q*q;
  21. resultLib = std::sqrt( temp );
  22. // конец профилируемого кода
  23. clocksLib = profile.postfix();
  24. profile.clear();
  25. profile.prefix();
  26. resultMy = sqrtIQ( i, q );
  27. clocksMy = profile.postfix();
  28. file << "<cmath> : Result of sqrt( ( "<< i <<" )^2 + ( "<< q <<" )^2 ) = ";
  29. file << resultLib << "\t Elapsed Time, in clocks: " << clocksLib << std::endl;
  30. file << "sqrtIQ: Result of sqrtIQ( "<< i <<", "<< q <<" ) = " << resultMy ;
  31. file << "\t\t\t Elapsed Time, in clocks: " << clocksMy ;
  32. file << "\t Clock Ratio, stdLib/myFunc : " << (float) clocksLib / clocksMy <<std::endl;
  33. std::cout << " Profiling is complete. Refer to log file in project folder" << std::endl;
  34. file.close();
  35. std::cin.get();
  36. return 0;
  37. }

Результат в файле 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:

  1. /* sqrt_iterations.cpp */
  2. #include <iostream>
  3. #include <cmath>
  4. #include <fstream>
  5. #define ITERATIONS
  6. #include "sqrtIQ.hpp"
  7. int main() {
  8. std::fstream file;
  9. //ios::out - открыть для записи с начала файл
  10. //ios::trunc - очистить файл если он существует
  11. file.open( "sqrt_iterations_log.txt", std::ios::out | std::ios::trunc );
  12. float i, q, temp;
  13. //variables for sqrt's result
  14. float sqrtLib, sqrtMy;
  15. float error, errorPer;
  16. file << " " << std::endl;
  17. file << "/**********************************************************************************************/" << std::endl;
  18. file << "Опыт №1: Определяем количество итераций вычисления значения корня квадратного по методу Ньютона." << std::endl;
  19. file << "Это количество прохода цикла while( firstEstimation - nextEstimation != 0 ) { <..> } " << std::endl;
  20. file << "error = std::fabs( sqrtLib - sqrtMy);" << std::endl;
  21. file << "error, % вычисляется как: 100 * error/ sqrtLib; " << std::endl;
  22. file << " " << std::endl;
  23. for( int k = 0, g = 100; k < 100; ++k, --g) {
  24. i = ( float )k/10;
  25. q = ( float )g/10;
  26. temp = i*i + q*q;
  27. sqrtLib = sqrt( temp );
  28. sqrtMy = sqrtIQ(i, q);
  29. error = std::fabs( sqrtLib - sqrtMy);
  30. errorPer = 100 * error/ sqrtLib;
  31. file << "<cmath> sqrt("<< temp <<"): " << sqrtLib << "\t my sqrt("<< i <<","<< q <<"): " << sqrtMy;
  32. file << "\t perform in " << iterationCnt << " iterations" ;
  33. file << "\t error: " << error << "\t error, %: " << errorPer <<std::endl;
  34. } // for
  35. file.close();
  36. std::cin.get();
  37. return 0;
  38. }

Результаты в файле 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. Графики строились в среде МатЛаб при помощи шаблонного скрипта, который требовалось лишь немного подправить. Сам скрипт:

  1. %This script is used for quick plotting the experimental data from file
  2. %specify the name of file with data that must be plotted
  3. file = 'sqrt_iterations_error.dat';
  4. fid = fopen( file, 'r');
  5. if fid == -1
  6.   error('file is not opened');
  7. end
  8. %read the data
  9. data = fscanf( fid,'%f', inf );
  10. %DELETE - example specified
  11. %
  12. %form additional data for plotting data
  13. % in-phase and quadrature samples that used for calculating sqrt()
  14. I = 0:0.1:10;
  15. Q = sort( I, 'descend');    %contains 10 9.9 9.8 ...
  16. %preallocate 1D column vector
  17. quotient = zeros( 1, 100, 'single');
  18. for i=1:100
  19.   quotient(i) = I(i) / Q(i);
  20. end
  21. display( quotient );
  22. %
  23. %DELETE
  24. %print into screen
  25. fprintf(1, '%.11fn', data );
  26. plot( quotient, data );
  27. %MODIFY - example specified
  28. %the first graph
  29. title('Error of sqrtIQ() function (with respect to <cmath> sqrt())');
  30. xlabel('Ratio of the in-phase to quadrature: I/Q');
  31. ylabel('Error, %');
  32. %the second graph
  33. %plot( data );
  34. %title('Error of sqrtIQ() function (with respect to <cmath> sqrt())');
  35. %xlabel('Uniform scale');
  36. %
  37. %MODIFY
  38. fclose ( fid );

Часть 6.
Результаты работы на целевой платформе TMS320F28335.
Проверка работы алгоритма производилась в программе, которая является частью цифрового детектора. В этой программе была реализована первая ступень фильтрации-децимации цифрового детектора как в синфазном, так и квадратурном канале, и как логическое завершение - находился модуль вектора по I-Q отсчетам при помощи шаблонной функции sqrtIQ():


  1. #include "fircoeff.h" //from matlab
  2. #include "filter.hpp"
  3. #include "adcmodel.hpp"
  4. #include "multiplication.hpp"
  5. #include "sqrtIQ.hpp"
  6. #define WDCR        *((volatile int *)0x7029)    /* WD Control reg */
  7. #define DISABLE_WD  0x0068
  8. void Disable_WD(void)
  9. {
  10.   asm(" eallow");
  11.   WDCR |= DISABLE_WD;
  12.   asm(" edis");
  13. }
  14. //this is the samples amount in the *.dat file
  15. const int kLength = 5208;
  16. float inPhaseSamples[ kLength ];
  17. float quadratureSamples[ kLength ];
  18. static void dataIO();
  19. float g_sampleI = 0, g_sampleQ = 0;
  20. float g_firOutI = 0, g_firOutQ = 0;
  21. float g_sqrtOut = 0;
  22. int main() {
  23. Disable_WD();
  24. //function dataIO() to catch the breakPoint and read
  25. //all massive of samples into inPhaseSamples buffer
  26. dataIO();
  27. //load samples into quadratureSamples
  28. dataIO();
  29. CAdcModel adcI( inPhaseSamples, kLength );
  30. CAdcModel adcQ( quadratureSamples, kLength);
  31. //filter for in-phase channel
  32. dsp::CFirFilter firFilterI( FIR_coeff, BL, 10 );
  33. //filter for quadrature channel
  34. dsp::CFirFilter firFilterQ( FIR_coeff, BL, 10 );
  35. bool isReadyFirI, isReadyFirQ;
  36. while(1) {
  37. //reading ADC
  38. adcI.getSample( g_sampleI );
  39. adcQ.getSample( g_sampleQ );
  40. isReadyFirI = firFilterI.filtrate( g_sampleI, g_firOutI );
  41. isReadyFirQ = firFilterQ.filtrate( g_sampleQ, g_firOutQ );
  42. if( ( isReadyFirI == true ) &&
  43. ( isReadyFirQ  == true ) ) {
  44. //here "g_out"/"g_firOutI/Q " contains proof FIR-decimator-output value.
  45. g_sqrtOut = dsp::sqrtIQ( g_firOutI, g_firOutQ );
  46. //Function to view the output of sqrt()
  47. dataIO();
  48. }
  49. }// while
  50. return 0;
  51. }
  52. /**
  53. @function dataIO
  54. The dataIO function acts as a placeholder.It is a convenient place to connect
  55. a Probe Point (or BreakPoint in the new CCS) that injects data from a PC file.
  56. @detailed - for details see spru301, 4.3 Adding a Probe Point for File I/O
  57. */
  58. static void dataIO() {
  59. /* do data I/O */
  60. return;
  61. }

Результаты работы алгоритма, оказались неожиданными:


Что то не так! Должна была быть на графике низкочастотная огибающая:


Первая идея - что-то не так с алгоритмом sqrtIQ(). Действительно. Оказалось, что отрицательные значения на выходе sqrtIQ(), появляются тогда, когда значения ВЧ-заполнения входного сигнала становятся отрицательными (когда ВЧ несущая меньше нуля). В этом случае алгоритм “альфа максимум + бета минимум” выдает отрицательное начальное приближение, и уже начиная с этого отрицательного значения, запускается расчет корня по алгоритму Ньютона, который, естественно, сходится к отрицательному числу.
Для преодоления проблемы написана шаблонная функция вычисления модуля (вычисляет по отдельности модуль для I и Q):

  1. template< class T >
  2. void absIQ( T& i, T& q ) {
  3. i = ( i >= (T)0 ) ? i : -i;
  4. q = ( q >= (T)0 ) ? q : -q;
  5. }

absIQ() была добавлена в начало функции sqrtIQ() (до начала "alpha max plus beta min"). Результат не заставил долго себя ждать:

Комментариев нет:

Отправить комментарий