| 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: 1074
 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: 1074
 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: 1401
 
 
 
			    
 
 | 
			
				|  |  
				|  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: 1401
 
 
 
			    
 
 | 
			
				|  |  
				|  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
 
 
 | 
 |  | 
	
		|  | 
	
		|  |