I need to smooth a noisy signal. I think that the moving average algorithm would be fine. However, I want the following conditions to be satisfied:
- The total integral (the sum of all values) before and after the moving average filtering is kept unchanged
- The number of points before and after the moving average filtering is kept unchanged
Implementation
The implementation is based on the answer to this post. The main question here is how to process the edges of a signal. The idea is to "reflect" the signal at the edges:
$$a_{-i} = a_{i-1},\, i > 0\quad \text{at the beginning of a signal},$$ $$a_{L+i} = a_{L-i-1},\, i\geq 0\quad \text{at the end of a signal},$$ where L denotes the length of the signal.
Note
To produce more or less appropriate results this algorithm should be used with the signals which start and end with relatively flat waveform (the length of "flatness" depends on the size of the window used).
Algorithm
#include <vector>
#include <iostream>
//Input :
// in : the original signal
// w : the size of the window of the moving average
//Output :
// out : the filtered signal
void MovingAverage( const std::vector<float>& in, std::vector<float>& out, int w )
{
//****** PREPARATION ******/
out.clear();
//Make the window size odd
w += (w % 2) ? 0 : 1;
if( (w < 1) or (in.size() < w) )
{
std::cerr << "Bad input!" << std::endl;
return;
}
//****** ALGORITHM ******/
int i = 0; //the current position
//Firstly, average the head
for( ; i < w/2; i++ )
{
float averagedValue = 0.;
for( int j = -w/2; j < (w/2 + 1); j++ )
{
averagedValue += (i + j >= 0) ? in[i + j] :
in[-(i + j) - 1];//(i + j) is negative here
}
out.push_back( averagedValue / w );
}
//The middle is averaged normally
for( ; i + w/2 < in.size(); i++ )
{
float averagedValue = 0.;
for( int j = -w/2; j < (w/2 + 1); j++ )
{
averagedValue += in[i + j];
}
out.push_back( averagedValue / w );
}
//Finally, average the tail
for( ; i < in.size(); i++ )
{
float averagedValue = 0.;
for( int j = -w/2; j < (w/2 + 1); j++ )
{
//(i + j) is not negative here
averagedValue += (i + j < in.size()) ? in[i + j] :
in[in.size() - (i + j - in.size()) - 1];//(i + j - in.size()) is not negative here
}
out.push_back( averagedValue / w );
}
}