I'm trying to calculate RMS of an AC signal which input frequency may vary from 40 to 70 Hz. I would like to use a window function because it accounts for frequency variations and also my ADC sampling frequency is not synchronized with the input frequency range. I found the code below which fires an interrupt every 200 samples are taken. But I don't quite understand what happens there. First, the window function is calculated and stored in a static array. This is done once during micro initialization. Then, every time the RMS function is called (every 200 samples interrupt) the array of ADC values is multiplied by the window, but why two For cycles before RMS computation?
#define NO_SAMPLES 200u // number of samples to acquire (1/4 buffer)
int16 Buffer[4 * NO_SAMPLES]; // RAM buffer to hold acquired data (signed), double buffered
uint8 Window[NO_SAMPLES]; // RAM buffer holding weighting function coefficients (8-bit)
uint16 RMS16; // RMS scaled to 16-bit
static uint32 Wght32; // Total Gaussian weight (sum of weighting coefficients
static uint8 BufferSection = 0; // ..0,..1,..2,..3
//==============================================================================
// Prepare window function and calculate total weight
// Hanning window:
// Hanning = a0 + a1 * Cos (2*PI*i/N); -N/2 <= i <= N/2
// a0 = 0.5 and a1 = 0.5
//==============================================================================
void ConfigRMS16()
{
const double a0 = 0.5; // Hanning (standard deviation (0.3-0.4)E-4)
const double a1 = 0.5;
const double scale = 255.0; // 8-bit window
uint16 i=0;
Wght32 = 0; // Total window weight
// Hanning 8-bit->
for( i=0; i<NO_SAMPLES; i++ ) // calculate 1/2 window weight
{
double Y = a0 + a1 * cos( M_PI * ((double) i) / ((double) (NO_SAMPLES-1)) );
//uint8 Y8 = (uint8) (scale * Y);
uint8 Y8 = round (scale * Y); // convert to 8-bit
Window[i] = Y8; // fill weighting function buffer
Wght32 += Y8; // calculate 1/2 window total weight
}
Wght32 <<=1; // total window weight
}
//==============================================================================
// Calculate RMS in half-buffer with 16-bit precision
//==============================================================================
double GetRMS16()
{
uint64 AccMS64 = 0; // Multiply-Accumulate for mean square
uint16 offset1=0, offset2=0; // starting offsets for left and right half of Window
uint16 i;
int16 v;
switch (BufferSection)
{
case 0: // -->0..1..2--3
offset1 = 3 * NO_SAMPLES;
offset2 = 0;
BufferSection=1; // next section
break;
case 1: // --0-->1..2..3
offset1 = 0;
offset2 = NO_SAMPLES;
BufferSection=2; // next section
break;
case 2: // ..0--1-->2..3
offset1 = NO_SAMPLES;
offset2 = 2 * NO_SAMPLES;
BufferSection=3; // next section
break;
case 3: // ..0..1--2-->3
offset1 = 2 * NO_SAMPLES;
offset2 = 3 * NO_SAMPLES;
BufferSection=0; // next section
break;
default: // should not come here
BufferSection=0;
}
for( i=0; i<NO_SAMPLES; i++) { // calculate RMS
v = Buffer[i+offset1]; // Buffer data are signed
AccMS64 += (uint64)(v*v) * Window[(NO_SAMPLES-1)-i]; // accumulate
}
for( i=0; i<NO_SAMPLES; i++) { // calculate RMS
v = Buffer[i+offset2]; // Buffer data are signed
AccMS64 += (uint64)(v*v) * Window[i]; // accumulate
}
RMS16 = fsqrt( AccMS64 / Wght32 ); // RMS rounded to 16-bit scale //106us/68us (using fast lib)
}
Source of code: https://community.cypress.com/thread/51426