2
\$\begingroup\$

I want to read the voltage of a battery using an ADC connected to a PLC. The signal oscillates a bit and I want to smooth it. The ADC samples the signal at 860 samples per second and stores it in variable ADCValue. Then I take an ADCValue every 0.5 seconds to create an array of size four and finally every two seconds I do an average of these four values.

My understanding is that this averaging by software is equivalent to a low pass filter. Is this correct? If so, how do I calculate the cut-off frequency of it?.

Edit: find here the Arduino code related to this, which uses two timers that call the interrupt functions associated in the setup():

#include <TimerOne.h>
#include <TimerThree.h>
#include <Wire.h>

const int AVGNUMELEM = 4;
float  ADCValue ; // This value comes from an external ADC and it is configured at 860 SPS
float arrayAvg[AVGNUMELEM];
float avg = 0;
int counteravg = 0;

//2 seconds
const long TIMER1TIME = 2000000;
//0.5 seconds
const long TIMER3TIME = 500000;

// Timer called every 0.5 seconds
void ISR_Store4values() {

  if(counteravg < AVGNUMELEM){
    arrayAvg[counteravg] = ADCValue;
    counteravg ++;
  }
  else
  {  
    for(int i = 0; i < (AVGNUMELEM-1); i++){
      arrayAvg[i] = arrayAvg[i + 1];
    }
    arrayAvg[(AVGNUMELEM-1)] = ADCValue;
  }
}

// Timer called to compute the average every 2 seconds
void ISR_PrintEvery2s() {
 
  avg = 0;
  for(int i = 0; i < AVGNUMELEM; i++){
    avg = avg + arrayAvg[i];
  }
  avg = avg / AVGNUMELEM;

  Serial.print(avg,4);
}

void setup() {

  Timer1.initialize(TIMER1TIME);
  Timer1.attachInterrupt(ISR_PrintEvery2s); 

  Timer3.initialize(TIMER3TIME);
  Timer3.attachInterrupt(ISR_Store4values); 

}

void loop() {

}
\$\endgroup\$
5
  • 3
    \$\begingroup\$ Just to be sure, do you use all 860 samples, or 430 samples in 0.5 seconds for averaging, or just the last sample every 0.5 seconds, and take an average of the 4? It's not clear how the 860 samples per second are calculated or selected to end up being a single sample value per 0.5 seconds, even if you take four of these values to get a single sample per 2 seconds. So do you ignore 429 samples and then you take one? \$\endgroup\$
    – Justme
    Commented Mar 8, 2023 at 13:07
  • 1
    \$\begingroup\$ I store values in a variable every 0.5 seconds <-- 430 samples added together and divided by 430? \$\endgroup\$
    – Andy aka
    Commented Mar 8, 2023 at 13:24
  • 2
    \$\begingroup\$ It's best to write a bit of pseudocode in your question to clarify which ADC samples end up in which final values, as the filter function depends on those details. Is it the case that every ADC sample ends up in just one stored sample. Is the value that you store every 2 seconds just the scaled sum of the last 1720 readings? If so, you'll have a zero at every multiple of 0.5 Hz, a -3dB point in the ballpark of half of that (I'll compute exact later), and a lousy stopband at odd multiples of 0.25 Hz. \$\endgroup\$
    – Neil_UK
    Commented Mar 8, 2023 at 14:00
  • 1
    \$\begingroup\$ About 0.22 Hz -3dB \$\endgroup\$
    – Neil_UK
    Commented Mar 8, 2023 at 14:07
  • \$\begingroup\$ @Justme it is the last sample every 0.5 seconds, sorry for the lack of details. As Neil has proposed, I have posted te code so everybody can check it. \$\endgroup\$
    – bardulia
    Commented Mar 8, 2023 at 22:32

1 Answer 1

2
\$\begingroup\$

It sounds like you are doing straight up accumulate-and-dump, which is a downsampled moving average, which is a finite-impulse response (FIR) filter with equal tap weights. Some people call it a frequency-domain sinc filter.

From your description you have 4 taps and a sampling period of 0.5 second. It is straight forward to find an analytical solution for your problem, but a quick solution is to compute it numerically in e.g. Octave:

pkg load control % loading the control toolbox
H = tf([1 1 1 1],[4 0 0 0],0.5,'Variable','z^-1'); % set up transfer-function in z-domain for a 4 tap moving average with sampling-time 0.5 s
[MAG,PHASE,W] = bode(H); % compute magnitude and phase response, using automatically generated frequency vector W
W(min(find(MAG < 0.7079)))/(2*pi) % approx. bandwidth at -3 dB in Hz, found by selecting all values for the magnitude less than 0.709 (-3 dB) and then picking the lowest frequency value the fulfills the criteria
\$\endgroup\$
5
  • \$\begingroup\$ I have just updated the question with the code used so now it will be more clear. Do you think it is still an FIR? \$\endgroup\$
    – bardulia
    Commented Mar 8, 2023 at 22:34
  • 1
    \$\begingroup\$ Yes, it is a downsampled FIR filter. Given the low order and number of operations you are using, you might consider using an IIR filter instead for a bit more performance. \$\endgroup\$
    – Arnfinn
    Commented Mar 9, 2023 at 11:44
  • 1
    \$\begingroup\$ Note that there is a good chance you are folding down a fair bit of noise using such a low sampling rate, which limits the effectiveness of the averaging; unless you have a suitable anti-aliasing filter on the input of your ADC. But 0.5 Hz or less cut-off low-pass filter can be a bit tricky to implement in practice... \$\endgroup\$
    – Arnfinn
    Commented Mar 9, 2023 at 11:48
  • \$\begingroup\$ that makes sense!. I am using ADC ADS1115, 860 samples per second is the maximum sampling frequency, however I think it should not be a problem as it is a DC signal what I am measuring. The datasheet of it recommends only a simple RC filter at the input. One question: can you explain a bit the lines of code in Octave? I would like to understand what you are doing with it. I have put the code in the program and I get an answer of 0.2277, I guess that it is the bandwidth/cut-off freq in Hz? \$\endgroup\$
    – bardulia
    Commented Mar 11, 2023 at 10:45
  • 1
    \$\begingroup\$ Hi; I've added some comments to the little script. At any rate, if you do not bandwidth limit the input to the ADC according to (less than half) the sampling frequency you will typically alias noise, even if the measurand is almost constant; if no filter on the input the variance of the noise will be the same regardless of sampling time, hence it's limited what you can do using a digital filter... \$\endgroup\$
    – Arnfinn
    Commented Mar 13, 2023 at 8:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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