1
$\begingroup$

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

$\endgroup$
0
0
$\begingroup$

I think the macro NO_SAMPLES is generating confusion here, if you look at the buffer you will see that it's bigger than the actual number of samples, and from the link the author mention that the routine istriggered when half of the buffer is filled with data, so the reason to have to for loops is because the windows is not being calculated completely, only half of thw windows is computed and stored, that is the reason why there are two for loops, and one of them is in reverse.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.