2

I have an application where I need to find the position of peaks in a given set of data. The resolution must be much higher than the spacing between the datapoints (i.e. it is not sufficient to find the highest datapoint, instead a "virtual" peak position has to be estimated given the shape of the peak). A peak is made of about 4 or 5 datapoints. A dataset is acquired every few ms and the peak detection has to be performed in real time.

I compared several methods in LabVIEW and I found the best result (in terms of resolution and speed) is given by the LabVIEW PeakDetector.vi, which scans the dataset with a moving window (>= 3 points width) and for each position performs a quadratic fit. The resulting quadratic function (a parabola) has a local maximum, which is in turn compared to nearby points.

Now I want to implement the same method in C. The polynomial fit is implemented as follows (using Gaussian matrix):

// Fits *y from x_start to (x_start + window) with a parabola and returns x_max and y_max
int polymax(uint16_t * y_data, int x_start, int window, double *x_max, double *y_max)
{
    float sum[10],mat[3][4],temp=0,temp1=0,a1,a2,a3;
    int i,j;

    float x[window];
    for(i = 0; i < window; i++)
        x[i] = (float)i;

    float y[window];
    for(i = 0; i < window; i++)
        y[i] = (float)(y_data[x_start + i] - y_data[x_start]);

    for(i = 0; i < window; i++)
    {
        temp=temp+x[i];
        temp1=temp1+y[i];
    }
    sum[0]=temp;
    sum[1]=temp1;
    sum[2]=sum[3]=sum[4]=sum[5]=sum[6]=0;

    for(i = 0;i < window;i++)
    {
        sum[2]=sum[2]+(x[i]*x[i]);
        sum[3]=sum[3]+(x[i]*x[i]*x[i]);
        sum[4]=sum[4]+(x[i]*x[i]*x[i]*x[i]);
        sum[5]=sum[5]+(x[i]*y[i]);
        sum[6]=sum[6]+(x[i]*x[i]*y[i]);
    }
    mat[0][0]=window;
    mat[0][1]=mat[1][0]=sum[0];
    mat[0][2]=mat[1][2]=mat[2][0]=sum[2];
    mat[1][2]=mat[2][3]=sum[3];
    mat[2][2]=sum[4];
    mat[0][3]=sum[1];
    mat[1][3]=sum[5];
    mat[2][3]=sum[6];

    temp=mat[1][0]/mat[0][0];
    temp1=mat[2][0]/mat[0][0];
    for(i = 0, j = 0; j < 3 + 1; j++)
    {
        mat[i+1][j]=mat[i+1][j]-(mat[i][j]*temp);
        mat[i+2][j]=mat[i+2][j]-(mat[i][j]*temp1);
    }

    temp=mat[2][4]/mat[1][5];
    temp1=mat[0][6]/mat[1][7];
    for(i = 1,j = 0; j < 3 + 1; j++)
    {
        mat[i+1][j]=mat[i+1][j]-(mat[i][j]*temp);
        mat[i-1][j]=mat[i-1][j]-(mat[i][j]*temp1);
    }

    temp=mat[0][2]/mat[2][2];
    temp1=mat[1][2]/mat[2][2];
    for(i = 0, j = 0; j < 3 + 1; j++)
    {
        mat[i][j]=mat[i][j]-(mat[i+2][j]*temp);
        mat[i+1][j]=mat[i+1][j]-(mat[i+2][j]*temp1);
    }

    a3 = mat[2][3]/mat[2][2];
    a2 = mat[1][3]/mat[1][8];
    a1 = mat[0][3]/mat[0][0];

    // zX^2 + yX + x

    if (a3 < 0)
    {
        temp = - a2 / (2*a3);
        *x_max = temp + x_start;
        *y_max = (a3*temp*temp + a2*temp + a1) + y_data[x_start];
        return 0;
    }
    else
       return -1;
}

The scan is performed in an outer function, which calls the above function repeatedly and chooses then the highest local y_max.

The above works and peaks are found. Only the noise is much worse than the LabVIEW counterpart (i.e. I get a very oscillating peak position, given the same input dataset and the same parameters). As the algorithm works the above code should be conceptually correct, so I think it might be a numerical problem as I simply use "floats" without further effort to improve numerical accuracy. Is this a possible answer? Does anyone have a tip, where I should be looking to?

Thanks.

PS: I have done my search and found this very good overview and also this question, similar to mine (unfortunately with not many answers). I will study these further.

EDIT: I have found my problems being elsewhere. Improving the algorithm by removing certain output values (a sort of post-validation in which a result is only accepted if the result is within the moving window) brought the solution to the issue. Now I am satisfied with the results, i.e. they are comparable to those from LabVIEW. Nevertheless, thanks a lot for your comments.

Community
  • 1
  • 1
m2oTech
  • 65
  • 7
  • I don't understand what you mean by oscillations. You run the code multiple times and get different results each time? If so, then what are the variations compared to float precision? – luk32 Oct 21 '14 at 11:22
  • I would expect a the code, given the same inputs, to always output the same value. However, I suspect that you do not have all the errors and warnings messages enabled. If you did have all the errors and warnings messages enabled, then this line: float sum[10],mat[3][4],temp=0,temp1=0,a1,a2,a3; would have output warnings about initializing float values from integers (temp=0, temp1=0). – user3629249 Oct 21 '14 at 13:27
  • regarding these kinds of statements: for(i = 0, j = 0; j < 3 + 1; j++) I would suggest using parens, so the meaning is clear. I.E. for(i = 0, j = 0; j < (3 + 1); j++) – user3629249 Oct 21 '14 at 13:29
  • There are several lines where invalid offsets to arrays are being used. using the passed in parameter x-start is an example. There are several lines where an array offset is set to a int value ( I.E. i=1) where the 'i' is never changed during the loop processing. Such items would be MUCH better listed as hardcoded values in array offsets. – user3629249 Oct 21 '14 at 13:44
  • @luk32 Every time the code runs it uses a different input dataset (real time sensor reading, the difference being actually due to noise), so the result will of course be different. But this difference should be within a much smaller range than it actually is. – m2oTech Oct 21 '14 at 22:58
  • @user3629249 Thanks for your tips, I will clean up the code. What is wrong with using x_start in an array offset? You mean it could have a negative value? In fact that is not the case as x_start is always positive, it could actually be declared as uint. – m2oTech Oct 21 '14 at 23:06
  • You might get more answers on the [Signal Processing](http://dsp.stackexchange.com/) or on the [Computational Science](http://scicomp.stackexchange.com/) sites. – Hristo Iliev Nov 30 '14 at 11:48

1 Answers1

0

Sorry to be late to the part, but if you have C/C++ it is really easy to port it to C# code using VS2013 Express (free version) and just port that into Labview using the .NET toolset.

Austin
  • 1,018
  • 9
  • 20
  • I don't think the OP wanted to use LabVIEW though… he/she wanted to write C code that gave results as good as LabVIEW. – nekomatic Feb 03 '15 at 13:08
  • Hi @Austin, yes that's not what I was looking for (rather the opposite direction LabVIEW -> C). But it is good to know that that can be done easily (especially the C -> C# part). – m2oTech Feb 13 '15 at 10:32