Author Topic: How does this code snippet "low pass filter" ADC value?  (Read 3691 times)

0 Members and 1 Guest are viewing this topic.

Offline MagicSmokerTopic starter

  • Super Contributor
  • ***
  • Posts: 1408
  • Country: us
How does this code snippet "low pass filter" ADC value?
« on: June 29, 2016, 03:20:28 pm »
I am trying to understand a snippet of code that is supposed to reduce the effect of noise in ADC results:

Code: [Select]
// Save LP-filtered value.
  adc_res_lp[adc_chan] -=
    (adc_res_lp[adc_chan] + 0x20) >> 6;
  adc_res_lp[adc_chan] += adc_res[adc_chan] >> 6;

Our programmer's explanation (which went right over my head) is that this adds hex 20 (decimal 32) to the old ADC reading, bit shifts that result 6 steps to the right to effectively divide by 64, then adds that result to the new ADC reading which has also been divided by 64.

Try as I might, I just can't seem to figure out how this bit of mathematical sorcery averages 2 ADC readings together... Anyone care to explain?
 

Offline tszaboo

  • Super Contributor
  • ***
  • Posts: 7377
  • Country: nl
  • Current job: ATEX product design
Re: How does this code snippet "low pass filter" ADC value?
« Reply #1 on: June 29, 2016, 03:24:19 pm »
It is an IIR filter. You always divide the previous value with x and add it to the current one. So you have:
S=S1 + S2/x+S3/x/x+S4/x/x/x and so on. Or something like that. I think you get the idea.
 
The following users thanked this post: MagicSmoker

Offline bktemp

  • Super Contributor
  • ***
  • Posts: 1616
  • Country: de
Re: How does this code snippet "low pass filter" ADC value?
« Reply #2 on: June 29, 2016, 03:29:53 pm »
It replicates an analogue RC filter.
The 0x20 is only for rounding, by adding 0.5 (32/64=0.5). If you don't care about this small rounding error, it can be omitted without affecting the filter function.
 
The following users thanked this post: MagicSmoker

Offline MagicSmokerTopic starter

  • Super Contributor
  • ***
  • Posts: 1408
  • Country: us
Re: How does this code snippet "low pass filter" ADC value?
« Reply #3 on: June 29, 2016, 03:56:42 pm »
I get it now; what I failed to see is that this function keeps operating infinitely. Pretty clever, though not exactly what I wanted. Thanks for the help!

 

Offline mikerj

  • Super Contributor
  • ***
  • Posts: 3240
  • Country: gb
Re: How does this code snippet "low pass filter" ADC value?
« Reply #4 on: June 29, 2016, 07:19:02 pm »
I am trying to understand a snippet of code that is supposed to reduce the effect of noise in ADC results:

Code: [Select]
// Save LP-filtered value.
  adc_res_lp[adc_chan] -=
    (adc_res_lp[adc_chan] + 0x20) >> 6;
  adc_res_lp[adc_chan] += adc_res[adc_chan] >> 6;

Our programmer's explanation (which went right over my head) is that this adds hex 20 (decimal 32) to the old ADC reading, bit shifts that result 6 steps to the right to effectively divide by 64, then adds that result to the new ADC reading which has also been divided by 64.

Try as I might, I just can't seem to figure out how this bit of mathematical sorcery averages 2 ADC readings together... Anyone care to explain?

That appears to be a properly broken bit of code.  I suppose it's possible you actually want to discard the lower 6 bits of the ADC value, but usualy you low pass filter an ADC result to reduce noise in the low order bits, not throw them away entirely.

The usual format is something like this:
Code: [Select]
lp_acc = (lp_acc - lp_out) + new_sample;
lp_out = lp_acc / x;

Where x determines the cut off frequency of the filter.
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: How does this code snippet "low pass filter" ADC value?
« Reply #5 on: June 29, 2016, 07:45:53 pm »
Quote
I am trying to understand a snippet of code that is supposed to reduce the effect of noise in ADC results:

exponential smoothing.

It has infinite memory but weighs distant measurements less than recent measurements. So it is a little bit like a windowed average.

The 0x20 is for rounding, as others have pointed out.

This particular implementation isn't terribly efficient, and is problematic for measurements less than 64. There is a short discussion (me involved) here a while back talking about ways to get around that and proved a more efficient calculation.
================================
https://dannyelectronics.wordpress.com/
 

Offline macboy

  • Super Contributor
  • ***
  • Posts: 2254
  • Country: ca
Re: How does this code snippet "low pass filter" ADC value?
« Reply #6 on: June 29, 2016, 09:18:02 pm »
Quote
I am trying to understand a snippet of code that is supposed to reduce the effect of noise in ADC results:

exponential smoothing.

It has infinite memory but weighs distant measurements less than recent measurements. So it is a little bit like a windowed average.

The 0x20 is for rounding, as others have pointed out.

This particular implementation isn't terribly efficient, and is problematic for measurements less than 64. There is a short discussion (me involved) here a while back talking about ways to get around that and proved a more efficient calculation.
Have you considered it is a 10 bit ADC, returning a left-justified value in a 16 bit register? Then it makes sense to shift right 6 bits to right justify the value.
 

Offline Dielectric

  • Regular Contributor
  • *
  • Posts: 127
  • Country: 00
Re: How does this code snippet "low pass filter" ADC value?
« Reply #7 on: June 30, 2016, 12:54:22 am »

Have you considered it is a 10 bit ADC, returning a left-justified value in a 16 bit register? Then it makes sense to shift right 6 bits to right justify the value.

Which architecture does that so I can avoid using it?

Here's how I throw down a stupid IIR:
result = (result + result_old*3)>>2;

Actually, newer ADCs have the filtering built in so who cares anymore?   :box:
 

Offline batteksystem

  • Regular Contributor
  • *
  • Posts: 167
  • Country: hk
    • My ebay store
Re: How does this code snippet "low pass filter" ADC value?
« Reply #8 on: June 30, 2016, 02:43:36 am »
I am trying to understand a snippet of code that is supposed to reduce the effect of noise in ADC results:

Code: [Select]
// Save LP-filtered value.
  adc_res_lp[adc_chan] -=
    (adc_res_lp[adc_chan] + 0x20) >> 6;
  adc_res_lp[adc_chan] += adc_res[adc_chan] >> 6;

Our programmer's explanation (which went right over my head) is that this adds hex 20 (decimal 32) to the old ADC reading, bit shifts that result 6 steps to the right to effectively divide by 64, then adds that result to the new ADC reading which has also been divided by 64.

Try as I might, I just can't seem to figure out how this bit of mathematical sorcery averages 2 ADC readings together... Anyone care to explain?

That appears to be a properly broken bit of code.  I suppose it's possible you actually want to discard the lower 6 bits of the ADC value, but usualy you low pass filter an ADC result to reduce noise in the low order bits, not throw them away entirely.

The usual format is something like this:
Code: [Select]
lp_acc = (lp_acc - lp_out) + new_sample;
lp_out = lp_acc / x;

Where x determines the cut off frequency of the filter.

Shifting is less accurate but much faster than multiplication and division.

Offline AndersJ

  • Frequent Contributor
  • **
  • Posts: 408
  • Country: se
Re: How does this code snippet "low pass filter" ADC value?
« Reply #9 on: June 30, 2016, 05:59:47 am »
Quote
Here's how I throw down a stupid IIR:
result = (result + result_old*3)>>2;

Nice simple filter, BUT small changes will be lost,
regardless of the number of succeeding new entries.

Example: result_old = 50. Next reading is 51.
The new reading of 51 will be lost every time.

I would like result_old to eventually become 51.
How can that be accomplished?
"It should work"
R.N.Naidoo
 

Offline bktemp

  • Super Contributor
  • ***
  • Posts: 1616
  • Country: de
Re: How does this code snippet "low pass filter" ADC value?
« Reply #10 on: June 30, 2016, 07:03:40 am »
You need to keep more bits when dividing/right shifting. One easy way to do it is to multiply/left shift the ADC input values.
This will reduce rounding errors and also increase the ADC resolution if there is enough noise (couple of LSBs) in the ADC data.
 
The following users thanked this post: AndersJ

Offline Jeroen3

  • Super Contributor
  • ***
  • Posts: 4078
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
Re: How does this code snippet "low pass filter" ADC value?
« Reply #11 on: June 30, 2016, 08:26:52 am »
It's an exponential averaging filter. The generic formula looks like this:
y(k) = a * y(k-1) + (1-a) * x(k).
Where a is the filter factor. Source is below.
http://gregstanleyandassociates.com/whitepapers/FaultDiagnosis/Filtering/Exponential-Filter/exponential-filter.htm

It's a simple filter since you only need T-1 sample history and a multiplication. Compared to a FIR filter, which is better, but requires more memory and time to execute. Try it in excel  ;).

Left aligning ADC allows you to have full Q15 range (0.00f to 1.00f) and allows the filter to oversample and have the filtered output settle at a value that is not a direct step of the adc.
A 2 bit adc left aligned to 4 bits would be able to have the codes [0, 4, 8, 12]. Yet, filtered the output of a noisy sequence (eg: 4,8, which is LSB noise) would be 6. Which is an output the ADC hardware can't do, but your filter can. Without left alignment you would have 1.5 which is an invalid integer.
 
The following users thanked this post: MagicSmoker

Offline Kalvin

  • Super Contributor
  • ***
  • Posts: 2145
  • Country: fi
  • Embedded SW/HW.
Re: How does this code snippet "low pass filter" ADC value?
« Reply #12 on: June 30, 2016, 08:36:37 am »
If you have RAM to spare and want to make a windowed filter (FIR), just make a ring buffer and keep a separate running sum of the samples. When you get a new sample, pop the oldest sample from the buffer, subtract the value from the running sum, add the new sample to the running sum and push the new sample into the ring buffer and output the updated running sum as filtered value. Pretty simple and fast.
 

Offline MagicSmokerTopic starter

  • Super Contributor
  • ***
  • Posts: 1408
  • Country: us
Re: How does this code snippet "low pass filter" ADC value?
« Reply #13 on: June 30, 2016, 10:36:12 am »
Which architecture does that so I can avoid using it?

Here's how I throw down a stupid IIR:
result = (result + result_old*3)>>2;

Actually, newer ADCs have the filtering built in so who cares anymore?   :box:

As I was just trying to understand a piece of code, and not trying to fix a problem, per se, I didn't include any application details, but in case it helps, signal filtering for noise and to prevent aliasing is done in hardware; the filtering in the code here is strictly to render some of those same process variables "human readable".

The MCU is an atmega644p, so 8b, no division and yes, memory and CPU cycles are highly constrained. The ADC readings being averaged are parameters like switch duty cycle, supply voltage, output current, heatsink temp, etc. Some don't really require much averaging (e.g. - heatsink temp), some are all but rendered useless by averaging (e.g. - duty cycle) but they all get it anyway.
 

Offline MagicSmokerTopic starter

  • Super Contributor
  • ***
  • Posts: 1408
  • Country: us
Re: How does this code snippet "low pass filter" ADC value?
« Reply #14 on: June 30, 2016, 10:38:08 am »
exponential smoothing.
...
This particular implementation isn't terribly efficient, and is problematic for measurements less than 64. There is a short discussion (me involved) here a while back talking about ways to get around that and proved a more efficient calculation.

I don't mind searching for the thread myself, but a keyword to search on besides your username would be appreciated. I tried "exponential", confined to this forum and your username, and only got this post as a result.

 

Offline MagicSmokerTopic starter

  • Super Contributor
  • ***
  • Posts: 1408
  • Country: us
Re: How does this code snippet "low pass filter" ADC value?
« Reply #15 on: June 30, 2016, 10:43:27 am »
If you have RAM to spare and want to make a windowed filter (FIR), just make a ring buffer and keep a separate running sum of the samples. When you get a new sample, pop the oldest sample from the buffer, subtract the value from the running sum, add the new sample to the running sum and push the new sample into the ring buffer and output the updated running sum as filtered value. Pretty simple and fast.

That is *exactly* how my non-programmer's brain envisioned doing this but the programmer said that required too much memory and instruction cycles. Let's just say that "feeping creaturism" has afflicted the poor 8b Atmel MCU in the product. We have since migrated to 32b ARM MCUs.
 

Offline tszaboo

  • Super Contributor
  • ***
  • Posts: 7377
  • Country: nl
  • Current job: ATEX product design
Re: How does this code snippet "low pass filter" ADC value?
« Reply #16 on: June 30, 2016, 10:48:39 am »
If you have RAM to spare and want to make a windowed filter (FIR), just make a ring buffer and keep a separate running sum of the samples. When you get a new sample, pop the oldest sample from the buffer, subtract the value from the running sum, add the new sample to the running sum and push the new sample into the ring buffer and output the updated running sum as filtered value. Pretty simple and fast.

That is *exactly* how my non-programmer's brain envisioned doing this but the programmer said that required too much memory and instruction cycles. Let's just say that "feeping creaturism" has afflicted the poor 8b Atmel MCU in the product. We have since migrated to 32b ARM MCUs.
IIR works well as written, only, you have less control over the impulse response, frequency response and other parameters. If it is not a metrology parameter, only for a simple actuator or self-check or anything like that, I would use it without even considering FIR. You just want to filter out the noise.
Of course when I say IIR, because it is a fixed point calculation, it will be FIR, but that is beside the point.
 

Offline danadak

  • Super Contributor
  • ***
  • Posts: 1875
  • Country: us
  • Reactor Operator SSN-583, Retired EE
Re: How does this code snippet "low pass filter" ADC value?
« Reply #17 on: June 30, 2016, 10:51:18 am »
Or use a processor, ARM Core, 20 bit DelSig, Digital Filter Block, DMA,
reference, other analog (OpAmps.....) that handle the conversion and filter
in HW and only SW burden is to get the answer......

Cypress PSOC http://www.cypress.com/products/32-bit-arm-cortex-m3-psoc-5lp


Regards, Dana.
Love Cypress PSOC, ATTiny, Bit Slice, OpAmps, Oscilloscopes, and Analog Gurus like Pease, Miller, Widlar, Dobkin, obsessed with being an engineer
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: How does this code snippet "low pass filter" ADC value?
« Reply #18 on: July 01, 2016, 12:57:31 am »
If you have RAM to spare and want to make a windowed filter (FIR), just make a ring buffer and keep a separate running sum of the samples. When you get a new sample, pop the oldest sample from the buffer, subtract the value from the running sum, add the new sample to the running sum and push the new sample into the ring buffer and output the updated running sum as filtered value. Pretty simple and fast.

That is *exactly* how my non-programmer's brain envisioned doing this but the programmer said that required too much memory and instruction cycles. Let's just say that "feeping creaturism" has afflicted the poor 8b Atmel MCU in the product. We have since migrated to 32b ARM MCUs.
IIR works well as written, only, you have less control over the impulse response, frequency response and other parameters. If it is not a metrology parameter, only for a simple actuator or self-check or anything like that, I would use it without even considering FIR. You just want to filter out the noise.
Of course when I say IIR, because it is a fixed point calculation, it will be FIR, but that is beside the point.
Also,
* FIR filters can have latency that IIR filters don't (1/2th the kernel length),

* Usually the phase delay of a IIR is all over the place, so if phase is important to you you may need to use a FIR

* IIR filters can be unstable as you try to push the performance too far.
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: How does this code snippet "low pass filter" ADC value?
« Reply #19 on: July 01, 2016, 09:57:07 am »
Quote
I don't mind searching for the thread myself,

The basic idea is to keep track of the (weighted) sum:

Code: [Select]
  adc_sum += (adc_current - (adc_sum / N));
  return (adc_sum / N);

where (adc_sum / N) can be stored so it is calculated only once; and "/ N" can be done via shifts.

1 / N is the weight you give to the current observation: Higher 1/ N means shorter "window" -> more distant observations are less influential on the output. It also provides noisier but faster-reacting output.
================================
https://dannyelectronics.wordpress.com/
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf