Search code examples
caveragesensorsmoving-average

Moving Average of a variable frequency signal


I was using this code to get the moving average of a sinusoidal Realtime wave with a constant frequency. (test_ sin being a sin wave with a constant frequency). I was sampling 100 samples as it can be seen in LENGTH_X = 100 and Buffer_X[100].

static double test_sin = 0; 
static double sum_x = 0 , filter_out_X = 0 ;
static int LENGTH_X = 100 , counter_filter_X = 0 ;
static double Buffer_X[100]= {0} ;
.
.
.
sum_x = sum_x + test_sin - Buffer_X\[counter_filter_X\];
filter_out_X = sum_x / LENGTH_X ;
Buffer_X\[counter_filter_X\] = (test_sin);
counter_filter_X = counter_filter_X+1 ;
if (counter_filter_X == LENGTH_X)
{
counter_filter_X = 0 ;
}

Basically i followed this reference to make the algorithm here on Youtube: efficient Moving Average Filter

The question i want to ask is how can I alter this code such that i can change the samples.

My sin wave has a variable frequency so i want to keep the LENGTH_X and BUffer_X[] variable or changing as well not just 100 samples. i already know the samples how much to keep.

Once I set Length_X =100 and Buffer_X[100]= {0} it gets stuck to 100 samples even if later i try to re initialize it,it does not re initialize as well.

I tried simple things like initializing in a different manner:

if(freq>1000)
{
static int LENGTH_X = 100 , counter_filter_X = 0 ;
static double Buffer_X[100]= {0} ;
}
else
{
static int LENGTH_X = 10 , counter_filter_X = 0 ;
static double Buffer_X[10]= {0} ;
}

Solution

  • The posted code for changing number of samples won't work. The variables defined inside the if-else part will not be accessible after the if-else.

    If supported on your system you could use variable length arrays and memset. Like:

    int LENGTH_X;
    int counter_filter_X = 0;
    if(freq>1000)
    {
        LENGTH_X = 100;
    }
    else
    {
        LENGTH_X = 10;
    }
    double Buffer_X[LENGTH_X];  // variable length array
    memset(Buffer_X, 0, LENGTH_X * sizeof Buffer_X[0]); // zero initialize
    

    Or you can go for dynamic allocated memory. Like

    int LENGTH_X;
    int counter_filter_X = 0;
    if(freq>1000)
    {
        LENGTH_X = 100;
    }
    else
    {
        LENGTH_X = 10;
    }
    double* Buffer_X = calloc(LENGTH_X, sizeof Buffer_X[0]);  // dynamic zero-initialized memory
    

    That said I would consider if there is a reason for changing array size at run time. If you know a maximum for LENGTH_X just make the array that maximum size and only use the part of the array you currently want. Re-initializing can be done using memset as shown above.

    Maybe your code could look like:

    #define MAX_LENGTH 1000
    ...
    ...
    double test_sin = 0; 
    double sum_x = 0;
    double filter_out_X = 0 ;
    int LENGTH_X = 100;
    int counter_filter_X = 0;
    double Buffer_X[MAX_LENGTH]= {0} ;
    
    while(keep_running)
    {
        if (change_length)
        {
            sum_x = 0;
            counter_filter_X = 0;
            if(freq>1000)
            {
                 LENGTH_X = 100;
            }
            else
            {
                LENGTH_X = 10;
            }
            memset(Buffer_X, 0, LENGTH_X * sizeof Buffer_X[0]);
        }
    
        test_sin = get_sample();
    
        sum_x = sum_x + test_sin - Buffer_X[counter_filter_X];
        filter_out_X = sum_x / LENGTH_X ;
        Buffer_X[counter_filter_X] = test_sin;
        counter_filter_X = counter_filter_X+1 ;
        if (counter_filter_X == LENGTH_X)
        {
            counter_filter_X = 0 ;
        }
    
        ...
        ...
    
        freq = ...;
        change_length = ...;
    }
    

    Here you'll have add some code for updating freq and change_length