Weird Print/Array Error & Peak Signal Detection

Hello All,
I'm fairly new to Arduino but I do have at least some experience programming in general. That being said, I am working on a peak detection program based on the algorithm provided here. A search of this forum seems to indicate a similar library has already been created but I still would like to work on it from scratch. The actual issue that I'm currently dealing with is while going through the code to debug, I've noticed that some of the values the Arduino prints are incorrect. Specifically, when I'm trying to print the value of an array (to see what is actually inside) using the following code:

 avgFilter[lag] = getMean(filteredY, lag);

  stdFilter[lag] = getStdDev(filteredY, lag); 

  for (int x = 0; x <= lag; x++)
  {
     Serial.println(avgFilter[5]);
     Serial.println(x);
     Serial.println(avgFilter[x]);
  }

The output is as follows:

If you notice on the last few lines of the output it is essentially saying that the value of the array avgFilter[5] is equal to 1, which is correct. It then goes on to say that the counter x is equal to 5 but for some reason, when that counter is 5, the value of avgFilter[x] is equal to 0???

I'm sure it is a simple error on my part, but I'm at my wits end trying to determine the cause. Any and all help is certainly appreciated. Also, here is the rest of my code for reference:

#include <Arduino.h>

float getMean(float val[], int arrayCount) 
{
  float total = 0;
  for (int i = 0; i < arrayCount; i++) 
  {
    total = total + val[i];
  }
  float avg = total/(float)arrayCount;
  return avg;
}

float getStdDev(float val[], int arrayCount) 
{
  float avg = getMean(val, arrayCount);
  float total = 0;
  for (int i = 0; i < arrayCount; i++) 
  {
    total = total + (val[i] - avg) * (val[i] - avg);
  }

  float variance = total/(float)arrayCount;
  float stdDev = sqrt(variance);
  return stdDev;
}

void setup() 
{
  Serial.begin(9600);
  int sizeOfSignal = 74;
  float y[sizeOfSignal] = {1,1,1.1,1,0.9,1,1,1.1,1,0.9,1,1.1,1,1,0.9,1,1,1.1,1,1,1,1,1.1,0.9,1,1.1,1,1,0.9,
       1,1.1,1,1,1.1,1,0.8,0.9,1,1.2,0.9,1,1,1.1,1.2,1,1.5,1,3,2,5,3,2,1,1,1,0.9,1,1,3,
       2.6,4,3,3.2,2,1,1,0.8,4,4,2,2.5,1,1,1};
  int lag = 5;
  float threshold = 3.5;
  float influence = 0.5;
  float signal[sizeOfSignal];
  float filteredY[lag];
  for (int i = 0; i <= lag; i = i + 1)
  {
    filteredY[i] = y[i]; 
  }
  float avgFilter[lag] = {0};                          
  float stdFilter[lag] = {0};
  
  avgFilter[lag] = getMean(filteredY, lag);
  stdFilter[lag] = getStdDev(filteredY, lag); 
  for (int x = 0; x <= lag; x++)
  {
     Serial.println(avgFilter[5]);
     Serial.println(x);
     Serial.println(avgFilter[x]);
  }
  for (int x = lag+1; x <= sizeOfSignal; x = x + 1)
    {
      if(abs(y[x] - avgFilter[x-1]) > threshold*stdFilter[x-1])
      {
        if (y[x] > avgFilter[x-1])
        {
           signal[x] = 1; 
        }
        else
        {
          signal[x] = -1;
        }
      }
      else
      {
        signal[x] = 0;
        filteredY[x] = y[x];
      }
      filteredY[x] = influence*y[x] + (1-influence)*filteredY[x-1];

      float avgHolderArray[lag-1];
      for (int k = 0; k <= lag-1; k = k + 1)
      {
        avgHolderArray[k] = filteredY[x-lag+1]; 
      }
      float stdHolderArray[lag-1];
      for (int m = 0; m <= lag-1; m = m + 1)
      {
        stdHolderArray[m] = filteredY[m-lag+1]; 
      }
      avgFilter[x] = getMean(avgHolderArray,(lag-1));
      stdFilter[x] = getStdDev(stdHolderArray,(lag-1));

    }
}

void loop() {
  // put your main code here, to run repeatedly:
}

Welcome to the forum

The lag variable has a value of 5 but the array value starts at zero so when you do

for (int x = 0; x <= lag; x++)

x will have a value of 0 to 5, ie 6 values, but the stdFilter array is declared as an array of only 5 values. There is no sixth value

Change the test in the for loop from <= to just < so that the for loop iterates only 5 times to match the number of elements in teh array

Can you tell what your project is ? Please give us a broader view.
For example, measuring the peak dB with an Arduino Uno and a microphone is not really possible.

Do you need the peak or do you want the standard deviation ?

If you have a sensor, then usually a few samples are taken and the average/oversampling is used. Just a few samples is enough to get rid of most electronic noise. There is no need to use a delay.

for(int i=0; i<10; i++)
  total += analogRead(A0);
result = total / 10;

This works very well with pressure sensors, analog temperature sensors and measuring the voltage of a battery.
It works better than a moving average. The moving average is a theoretical solution, which assumes that the samples are not too noisy. The moving average can be used after this average/oversampling, if the project requires a moving average.

To find a peak, remember the first value or set a impossible value and check if the new sample is higher.

// initial value:
  value = -1;  // set a low impossible value or do value = analogRead(A0);

// do many times:
  newValue = analogRead(A0);
  if (newValue > value)     // two lines of code to detect the peak
    value = newValue;  

Thank you for the greetings and replies everyone!

This is a good point. However, I've tried it both ways (i.e. <= and <) while troubleshooting and I get the same wrong value error regardless. To your point though it is interesting that the value of avgFilter[5] even exists (and spits out the correct value) since I initialized the array, as you said, with only 5 values! Also every other compiler would crash with an "out of bounds" error, why doesn't the Arduino do the same thing?

My project involves reading input from a photoresistor. The photoresistor is essentially sampled 36,000 times and those values are assigned to an array of the same length. My ultimate goal for this part of the project would be to pass this array to a peak signal detection function so that it will return the indices of the approximately 5-9 peaks present in that data. The baseline values do change a bit so the peaks are relative to the surrounding baseline data and there is a fair amount of noise so some smoothing needs to occur. If I simply just compared the current value to the previous, I would probably get a lot of false positives because of the noise in the signal. What I liked about this algorithm is that it smooths and compares based on how far from the standard deviation, and has values I can change to suit the sensitivity to my needs.

Turn on warnings in the compiler from the IDE Preferences dialogue and you should get a warning

It exists but the memory that it uses does not belong to the array

Thank you for the quick reply!
I have now set the "compiler warnings" preference to "all". Interestingly enough the "out of bounds" warning still doesn't come up, actually another warning pops up saying that the variable "signal" is initialized but not used. This is also a bizarre warning because from my original code you can see that it is indeed used in the same function (so not a local variable issue) about 23, 27 and 32 lines after its initiated! I really appreciate all the help but these issues especially without debugging capabilities can be quite frustrating.

Which Arduino board and version of the IDE are you using ?

Mega 2560 and Arduino IDE 2.2.1.

Actually your answer helped me out with my initial problem! I was setting avgFilter[lag] when it should have been avgFilter[lag - 1] since avgFilter[5] isn't part of that array!

Now we just got to squash this signal bug and we'll be moving :slight_smile:

Where in the code do you actually use the signal array apart from setting the value of its elements ?

I see, I wasn't actually DOING anything with it! Once I included it in a print statement the error went away! Thank you for all your help!

The code overall seems to be working well so I will include it in this response just in case someone else wants to use this peak detection algorithm on an Arduino.

#include <Arduino.h>

// Function to determine average of an array
float getMean(float val[], int arrayCount) 
{
  float total = 0;
  for (int i = 0; i < arrayCount; i++) 
  {
    total = total + val[i];
  }
  float avg = total/(float)arrayCount;
  return avg;
}

// Function to determine standard deviation of an array
float getStdDev(float val[], int arrayCount) 
{
  float avg = getMean(val, arrayCount);
  float total = 0;
  for (int i = 0; i < arrayCount; i++) 
  {
    total = total + (val[i] - avg) * (val[i] - avg);
  }
  float variance = total/(float)arrayCount;
  float stdDev = sqrt(variance);
  return stdDev;
}

void setup() 
{
  Serial.begin(9600);
  // Initial data
  int sizeOfSignal = 74;
  float y[sizeOfSignal] = {1,1,1.1,1,0.9,1,1,1.1,1,0.9,1,1.1,1,1,0.9,1,1,1.1,1,1,1,1,1.1,0.9,1,1.1,1,1,0.9,1,1.1,1,1,1.1,1,0.8,
  0.9,1,1.2,0.9,1,1,1.1,1.2,1,1.5,1,3,2,5,3,2,1,1,1,0.9,1,1,3,2.6,4,3,3.2,2,1,1,0.8,4,4,2,2.5,1,1,1};

  // Sensitivity modifiers
  int lag = 30;
  float threshold = 5;
  float influence = 0;

  // Initializing the array that will hold the peak information
  float signal[sizeOfSignal]={0};

  // Initializing an array from the initial data that will be analyzed
  float filteredY[sizeOfSignal]={0};
  for (int i = 0; i < sizeOfSignal; i = i + 1)
  {
    filteredY[i] = y[i]; 
  }

  // Intializing the average and standard deviation arrays and values
  float avgFilter[sizeOfSignal] = {0};                          
  float stdFilter[sizeOfSignal] = {0};
  float tempAvgFilter[lag] = {0};
  float tempStdFilter[lag] = {0};
  for (int i = 0; i < lag; i = i + 1)
  {
    tempAvgFilter[i] = y[i];
  }
  for (int i = 0; i < lag; i = i + 1)
  {
    tempStdFilter[i] = y[i];
  }
  avgFilter[lag-1] = getMean(tempAvgFilter, (lag));
  stdFilter[lag-1] = getStdDev(tempStdFilter, (lag));   

  // Beginning the peak detection algorithm
  for (int x = lag; x < sizeOfSignal; x = x + 1)
  {
    if(abs(y[x] - avgFilter[x-1]) > threshold*stdFilter[x-1])
    {
      if (y[x] > avgFilter[x-1])
      {
        signal[x] = 1; 
      }
      else
      {
        signal[x] = -1;
      }
      filteredY[x] = influence*y[x] + (1-influence)*filteredY[x-1];
    }
    else
    {
      signal[x] = 0;
      filteredY[x] = y[x];
    }
    
    // Temporary arrays to find the new average and stddev of a range in the filtered data
    float tempAvg[lag] = {0};
    float tempStd[lag] = {0};
    for (int i = 0; i < lag; i = i + 1)
    {
      tempAvg[i] = filteredY[(x-lag+1)+i];
    }
    for (int i = 0; i < lag; i = i + 1)
    {
      tempStd[i] = filteredY[(x-lag+1)+i];
    }

    // Assigning the new average and standard deviation
    avgFilter[x] = getMean(tempAvg, lag);
    stdFilter[x] = getStdDev(tempStd, lag);
  }
  for(int i = 0; i < sizeOfSignal; i = i + 1)
  {
    Serial.println(signal[i]);
  }
}

void loop() {
  // put your main code here, to run repeatedly:
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.