View previous topic :: View next topic |
Author |
Message |
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
Radical olympic++ ADC averaging , de-noising routine |
Posted: Sat Apr 20, 2013 2:19 pm |
|
|
I've had very good luck with this HL adc averaging routine that i wrote some time ago, for use under high noise ADC reading conditions.
Was particularly good for a high power AC heater system with hashy/trashy EMI TRIAC modulators.
a typical use was to call it 4 times /second and use a circular buffer for the results of this routine at a higher level ( main() )
Then to average sets of 16 of the values this returns for process decison making.
that yielded 256 ADC readings , summarized to very low noise,
and time weighted as well.
I am always grateful for tips on how to do this better.
Code: |
// olympic++ adc de-noising routine for 10 or 12 bit ADC values
// NOTE: you must have selected the desired ADC channel before calling
// reads 16x , sorts and tosses low4 && high 4 readings
// then averages the middle 8
unsigned int16 adchlx(void){ // read 16 - sort, keep middle 8 average
unsigned int8 i; unsigned int16 accum=0;
unsigned int16 s, b[16]; int1 didswap=1;
for ( i = 0 ; i < 16; i++ ) {
b[i]= read_adc(ADC_start_and_read); // ADC set for 10 or 12 bits
delay_us(8);
} // end of for loop for multi sample
while(didswap){ // bubble sort
didswap=0;
for (i=0; i<15; i++){ // i 0-15
if(b[(i)]>b[(i+1)]){ // if low element greater than next -do swap
s=b[i]; // hold upper
b[i]=b[(1+i)];
b[(1+i)]=s;
didswap=1;
} // ~if
} // ~for
} // ~while
// now sort and keep middle 8 values
for (i=4; i<12; i++){ accum +=b[i]; }
return(accum>>3);
}
|
Last edited by asmboy on Mon Apr 22, 2013 1:39 pm; edited 1 time in total |
|
|
Gabriel
Joined: 03 Aug 2009 Posts: 1067 Location: Panama
|
|
Posted: Mon Apr 22, 2013 8:35 am |
|
|
Awesome work man...
Thanks for sharing. _________________ CCS PCM 5.078 & CCS PCH 5.093 |
|
|
a_abdoli
Joined: 18 Dec 2010 Posts: 1
|
|
Posted: Tue Oct 22, 2013 9:01 am |
|
|
Thank you, It was great. |
|
|
muratmert4
Joined: 19 May 2011 Posts: 3
|
|
Posted: Wed Oct 23, 2013 11:17 am |
|
|
asmboy
this code very nice thankyou |
|
|
dorinm
Joined: 07 Jan 2006 Posts: 38
|
|
Posted: Thu Oct 24, 2013 7:11 pm |
|
|
Yes it is a good idea indeed ... I've also used a similar method for slow changing variations where "almost" realtime was not necessary;
....just for completeness sake, I would add that if anyone need that "almost" realtime reading, even if not that accurate, I would use some sort of predictive filtering (a simple Kalman would do that ) |
|
|
andrewg
Joined: 17 Aug 2005 Posts: 316 Location: Perth, Western Australia
|
|
Posted: Fri Oct 25, 2013 10:04 pm |
|
|
Just a comment that if I was going to do this, then I would use an insertion sort where the delay_us is (for a somewhat random delay), and change the array size to 12 to save a bit of RAM. The extreme min or max values would just drop off (it doesn't matter which), then take the average of 8 values left after the other 4 extreme values are ignored. _________________ Andrew |
|
|
andrewg
Joined: 17 Aug 2005 Posts: 316 Location: Perth, Western Australia
|
|
Posted: Tue Oct 29, 2013 9:33 am |
|
|
To clarify "somewhat random", the time taken to perform the insertion sort will depend on the data it is sorting - the ADC readings. It will vary slightly, but probably not too much. Given there are just a few readings it will be quite quick, but almost certainly longer than 8us. _________________ Andrew |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Tue Oct 29, 2013 5:43 pm |
|
|
to be clear: -
the 8uS delay is not part of the sorting algorithm at all -
It is inserted only in the acquisition phase of the function as an arbitrary delay to space out the individual ADC readings so as to better allow for trending changes in the string of averaged values returned
( as in when the true value really is slowly increasing or decreasing
over the course of the raw reading set)
It is a delay that can be:
1) eliminated
2) made longer
3) made shorter
The example code is based on the experimentally determined integral T.C. of the readings being taken in my particular application, from whence it was excerpted. It is up to the user who borrows this code to find a delay value which works optimally in their own application of this concept. |
|
|
Gabriel
Joined: 03 Aug 2009 Posts: 1067 Location: Panama
|
|
Posted: Sun Dec 15, 2013 8:19 pm |
|
|
I used this routine today for a project i have ongoing.
It worked perfectly...
Thank you Asmboy.
G. _________________ CCS PCM 5.078 & CCS PCH 5.093 |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1346
|
|
Posted: Wed Mar 19, 2014 10:12 am |
|
|
I made use of it as well! Thanks!
I did change the sorting algorithm to an insertion sort, which ended up being a bit quicker for the data sets I typically get.
Code: |
#define DEV_ADC_SAMPLES 16
//insertion sort the values. This is usually a fast enough sort for small
//sets of values. This algorithm divides the array into a sorted section
//on the left side and an unsorted section on the right side. Elements
//are placed one at a time into the sorted section, which scoots over
//existing sorted elements as needed to make space for the new element
// 5 | 4 3 2 1 0
// Pull 4 out
// 5 | X 3 2 1 0
// Shift values higher than 4 one space to the right
// X 5 | 3 2 1 0
// Place 4 in left over space
// 4 5 | 3 2 1 0
// 4 5 | X 2 1 0
// 4 X 5 | 2 1 0
// X 4 5 | 2 1 0
// 3 4 5 | 2 1 0
// Continue until all are sorted
for(i=1; i<DEV_ADC_SAMPLES; i++){
s = b[i]; //Get current sample to place in the sorted section
//Move left down the array, scooting all values higher than 's' one
//place to the right. This loop always stops after j=1, so that j=0
//doesn't cause j-1 to give an out of bounds index in the array
for(j=i; j && (b[j-1] > temp); j--){
b[j] = b[j-1]; //shift to the right one location
}
//At this point, no values left of current position are larger than
//'s', so place 's' here
b[j] = s;
}
|
|
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Wed Mar 19, 2014 7:49 pm |
|
|
i considered the use of the insertion sort when i created the routine,
BUT in my case there is a very special consideration!
i did choose the bubble on purpose based on the histogram of collected data
during development of the precision heater it was used in.
when the noise is low in a given block of readings - frequently there are multiple identical ADC values in the array. I leave it to the reader to apprehend the implications of that - though i believe
"res ipsa loquitur" in this instance
|
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1346
|
|
Posted: Thu Mar 20, 2014 5:37 pm |
|
|
Curiosity question:
In this part:
Code: |
for ( i = 0 ; i < 16; i++ ) {
b[i]= read_adc(ADC_start_and_read); // ADC set for 10 or 12 bits
delay_us(8);
} // end of for loop for multi sample
|
What purpose does the delay serve exactly? I assume to allow for some part of the process to charge/settle, but up till recently, I have (probably naively) assumed that read_adc(ADC_start_and_read) blocked appropriately. I am familiar with adding a delay between selecting a port and doing a reading, but was wondering about the delay inbetween (in this case, the 8us delay). I have yet to run into any problems not having delays between successive readings, but I understand that doesn't mean that it will always work that way. |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Fri Mar 21, 2014 11:57 am |
|
|
the above question is answered a few posts earlier in this thread ......
|
|
|
akay
Joined: 29 Sep 2020 Posts: 17
|
Re: Radical olympic++ ADC averaging , de-noising routine |
Posted: Tue Sep 29, 2020 1:04 pm |
|
|
The value we need to print on the LCD is int B? asmboy wrote: | I've had very good luck with this HL adc averaging routine that i wrote some time ago, for use under high noise ADC reading conditions.
Was particularly good for a high power AC heater system with hashy/trashy EMI TRIAC modulators.
a typical use was to call it 4 times /second and use a circular buffer for the results of this routine at a higher level ( main() )
Then to average sets of 16 of the values this returns for process decison making.
that yielded 256 ADC readings , summarized to very low noise,
and time weighted as well.
I am always grateful for tips on how to do this better.
Code: |
// olympic++ adc de-noising routine for 10 or 12 bit ADC values
// NOTE: you must have selected the desired ADC channel before calling
// reads 16x , sorts and tosses low4 && high 4 readings
// then averages the middle 8
unsigned int16 adchlx(void){ // read 16 - sort, keep middle 8 average
unsigned int8 i; unsigned int16 accum=0;
unsigned int16 s, b[16]; int1 didswap=1;
for ( i = 0 ; i < 16; i++ ) {
b[i]= read_adc(ADC_start_and_read); // ADC set for 10 or 12 bits
delay_us(8);
} // end of for loop for multi sample
while(didswap){ // bubble sort
didswap=0;
for (i=0; i<15; i++){ // i 0-15
if(b[(i)]>b[(i+1)]){ // if low element greater than next -do swap
s=b[i]; // hold upper
b[i]=b[(1+i)];
b[(1+i)]=s;
didswap=1;
} // ~if
} // ~for
} // ~while
// now sort and keep middle 8 values
for (i=4; i<12; i++){ accum +=b[i]; }
return(accum>>3);
}
|
|
|
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Tue Sep 29, 2020 1:23 pm |
|
|
Not sure what you are getting at.
The result is implied in the function call.
I did not mention an LCD display -
unsigned int16 adchlx(void)
It returns an unsigned int16 on exit as in
Code: |
unsigned int16 myanswer,
myanswer=adchlx(); // myanswer has the function result
|
|
|
|
|