# measuring sine wave signal with adc of PIC16F

Discussion in 'General Electronics Chat' started by aamirali, Jul 6, 2012.

1. ### aamirali Thread Starter Member

Feb 2, 2012
415
1
Hi when i measure sine wave of 60hz from adc pin, it gives different values everytime. Obviously its amplitude is varying. But how to measure average value of it.

2. ### wmodavis Well-Known Member

Oct 23, 2010
737
150
At what rate are you sampling the 60Hz signal with your ADC? Hopefully the sampling rate is above the Nyquist rate.

3. ### MrChips Moderator

Oct 2, 2009
12,648
3,458
You need to rectify and integrate (with a low pass filter) the 60Hz AC signal.

4. ### THE_RB AAC Fanatic!

Feb 11, 2008
5,435
1,305
You need to sample much higher than the nyquist rate (ie sample at 10x or 20x the sine freq) then record the highest and lowest samples, and calc the sine peak to peak amplitude as the distance between the high and low samples.

If you are sampling at a lower frequency it's still possible but you need to sample enough times to capture by chance the highest and lowest samples. You risk missing the high and low peaks because of aliasing so it would be good to add a random component to your sampling period.

5. ### ErnieM AAC Fanatic!

Apr 24, 2011
7,442
1,628
If it varies with every measurement you are probably doing it right.

The *average* value of a sine wave is zero: are you sure that is what you want to measure?

If you want the peak value, how often do you want to measure it? If the answer is 60 times a second (or less) the simplest method (measured in parts) would be to also use the 60 Hz signal to trigger a zero-crossing detector (just a resistor to an input pin). Set it up to trip on the + to - edge, wait 1/2 * 1/60 seconds (83 mS) and make a fast group of measurements.

You should get the peak (or very close to the peak) as a sine wave has the least rate of change at the peak. Sort the array of readings to find that largest and work backwards to whatever value you desire.

There's a ton of assumptions built into this measurement, such as assuming it really is a sine, assuming the DC is zero, and assuming the frequency is 60 Hz.

6. ### MrChips Moderator

Oct 2, 2009
12,648
3,458
You don't have to record the highs and lows of the sine wave. You can perform a least-squares fit to a sine wave and estimate the amplitude.

7. ### crutschow Expert

Mar 14, 2008
13,508
3,385
Is your sine-wave going positive and negative (centered around zero volts)? If so then one approach is to run the sine-wave through an op amp precision full-wave rectifier to invert the negative half of the signal. Run this rectified signal through an RC low pass filter. That will give the average sine-wave value with good noise immunity which you can sample at a slow rate with the PIC. From that you can calculate the RMS or Peak value as needed.

8. ### THE_RB AAC Fanatic!

Feb 11, 2008
5,435
1,305
I haven't used that method MrChips and would like to see your procedure if you don't mind showing juts an outline of what is involved.

Recording the highest and lowest values is very trivial in math and fast to execute, small code etc. It also gives p/p amplitude of non-sine waveforms.

Maybe doing a least squares method would be faster by requiring less samples? How many samples are needed?

9. ### MrChips Moderator

Oct 2, 2009
12,648
3,458
I have to dig out that code on my computer at work.

10. ### THE_RB AAC Fanatic!

Feb 11, 2008
5,435
1,305
Sorry MrChips I didn't expect you to go to any trouble, I just thought you might have a simple solution handy using least squares to get the amplitude.

I went and did my own homework (as we often advise people to do here ) and read the Wiki and a couple of other pages, and now I'm not much wiser but I do have a nice headache.

11. ### MrChips Moderator

Oct 2, 2009
12,648
3,458
I got this MATLAB code off the internet.
Supply it with the digitized waveform and it returns
DC offset, amplitude, frequency and phase angle.

Code ( (Unknown Language)):
1.
2. function [params,yest,yres,rmserr] = sinefit(yin,t,f,verbose,plotflag)
3. % params = sinefit(yin,t,f)
4. % [params,yest] = sinefit(yin,t,f)
5. % [params,yest] = sinefit(yin,t,f,verbose)
6. % [params,yest] = sinefit(yin,t,f,verbose,plotflag)
7. % [params,yest,yres] = sinefit(yin,t,...)
8. % [params,yest,yres,rmserr] = sinefit(yin,t,...)
9. %
10. % Least squares sinusoid fit - optimization toolbox not needed
11. %
12. % IEEE Standard for Digitizing Waveform Recorders (IEEE Std 1057):
13. % Algorithm for three-parameter (known frequency) and four-parameter
14. % (general use) least squares fit to sinewave data using matrix operations.
15. %
16. % INPUTS:   -yin:      Input signal to be fitted (a vector)
17. %           -t:        Time vector (same length as yin)
18. %           -f:        Signal frequency (actual or estimate) [Hz]
19. %           -verbose:  if 1, display information; else, don't. default: 0
20. %           -plotflag: If 1, plot; else, don't. default: 0. The plots are:
21. %                      time-domain plot, histograms and modulo-time plot.
22. %                      Modulo-time plot is copied from file ID: #22907
23. %
24. % -The time vector is not necessarily linearly spaced (hence required here)
25. % -If f is a guess, it has to be near true input signal frequency
26. %
27. % OUTPUTS:  -params:  A vector containing:
28. %                     [offset amplitude freq(Hz) angle(rad)].
29. %                     If not converged, params = [nan nan nan nan]
30. %           -yest:    Estimated sinusoid:
32. %           -yres:    The residual
33. %           -rmserr:  Root mean square of the residual
34. %
35. % 4-parameter sinefit uses 3-parameter sinefit to create initial search
36. % vector. For further information, consult IEEE Std 1057 and/or
37. % IEEE Std 1241 documentation.
38. %
39. % type sinefit for demo
40.
41. % convergence related things you can tweak:
42. TOL = 2.2204e-016;    % Normalized initial tolerance (default: eps )
43. MTOL = 4;             % TOL relaxation multiplicand, MTOL > 1
44. MAX_FUN= 16;          % MAX number of function calls
45. MAX_ITER = 32;        % MAX number of iterations in one function call
46.
47. % Convergence related stuff: Vector x0 will be created: x0=[A B C dw],
48. % where A & B are sin & cos multiplicands, C is the offset and dw is
49. % the angular frequency increment in iterative search.
50. % x0 is, of course, normalized in convergence calculation
51. %(Fit for 1*yin converges as fast as for 100*yin)
52. % ****  if max(abs(x0(i)-x0(i-1)))<TOL, the fit is complete.
53. % ****  if not, multiply TOL with MTOL and retry maximum of MAX_FUN times.
54.
55.
56. %plotting related
57. if nargin == 5
58.     if plotflag
59.         N_per=8; %number of periods plotted
60.         N_hist = 11; %number of bins in histogram
61.     end
62. else
63.     plotflag = 0;
64. end
65.
66. if nargin == 0   %demo
67.     fprintf('\ndemo: creating sinusoid with\n\t- clock jitter,\n\t')
68.     fprintf('- phase noise,\n\t- additive noise and \n\t- harmonics\n ')
69.     N=pow2(11);              %vector length
70.     fs = pi*1e3;            %sampling freq
71.     ts = 1/fs;              %sampling interval
72.     freq = (N/128-1)*fs/N;  %freq (Hz)
73.     phase = -pi/8;          %phase (rad)
74.     offset = pi*4;          %offset (i.e mean)
75.     amplitude = pi/3;       %amplitude
76.     t = (0:(N-1))*ts;       %time vector
77.     std_jitter = 1e-2;  %standard deviation of jitter noise
79.     std_phase  = 1e-2;   %standard deviation of phase noise
80.     noise = randn(1,N);
81.     std1_noise = noise/std(noise);  % random vector with stdev = 1
82.     jit_noise = std_jitter*std1_noise;
83.     phase_noise = std_phase*std1_noise;
85.     w=2*pi*freq;
86.     t = t + ts*jit_noise;                          % add clock jitter
87.     A2 = amplitude*0.01;     % 2. harmonic ampl
88.     A3 = amplitude*0.02;     % 3. harmonic ampl
89.     yin = cos(w*t+phase+phase_noise);  % sinusoid with phase noise
90.     %add offset, noise & harmonics
92.     figure
93.     params = sinefit(yin,t,freq,1,1);
94.     fprintf('\n\t\t\tpure sinusoid\tnoisy sinusoid fit')
95.     fprintf('\nOffset:   \t%3.4g\t\t\t%3.4g',offset,params(1))
96.     fprintf('\nAmpl:   \t%3.4g\t\t\t%3.4g',amplitude,params(2))
97.     fprintf('\nFreq:   \t%3.4g\t\t\t%3.4g [Hz]',freq,params(3))
99.         params(4)/pi)
100.     fprintf('\nend demo\n')
101.     clear params yest
102.     return
103. end   %end demo
104.
105. if nargin < 4
106.     verbose = 0;
107. end
108.
109. if verbose
110.     tic
111. end
112.
113. %vector dimension preps
114. [rc]=size(yin);
115. if rc(1)==1
116.     yin = yin(:);
117.     N=rc(2);
118. else
119.     N=rc(1);
120. end
121. t=t(:);
122. onevec = ones(N,1);
123.
124. %t does not have to be linearly spaced, so to estimate sampling time ts
125. ts = rms(diff(t),N-1); % ts needed in freq normalization of converg. calc
126.
127. if MTOL<0
128.     error('MTOL is a positive number > 1')
129. elseif MTOL<1
130.     MTOL=1/MTOL;
131.     fprintf('warning: MTOL should be > 1,')
132.     fprintf('swiching to inverse value.\nXTOL = %i\n',MTOL)
133. end
134.
135. % convergence related normalization
136. rmsyin = rms(yin,N);
137. TOL=rmsyin*TOL;
138.
139. %here we go
140. [x0,iter] = sinefit4par(yin,t,ts,2*pi*f,onevec,TOL,MAX_ITER);
141. iter_total=iter;
142. iter_sinefit4par = 1;  %first function call
143.
144. %success?
145. if iter<=MAX_ITER
146.     success = 1;
147.     if verbose
148.         st1 = ['\n\tConverged after ' int2str(iter) ' iterations\n'];
149.         fprintf(st1)
150.     end
151. else
152.     if verbose
153.         st2 = '\n\tincreasing TOL...';
154.         fprintf(st2)
155.     end
156.     while iter>MAX_ITER && iter_sinefit4par<=MAX_FUN
157.         if verbose
158.             fprintf('.')
159.         end
160.         %while # of function calls is < MAX_FUN, do:
161.         TOL = TOL*MTOL;   %increase TOL
162.         [x0,iter] = sinefit4par(yin,t,ts,2*pi*f,onevec,TOL,MAX_ITER);
163.         iter_total = iter_total+iter;
164.         iter_sinefit4par=iter_sinefit4par+1;
165.     end
166.     if iter>MAX_ITER
167.         clc
168.         fprintf(['\nFailed to fit. The reasons could be:\n\t1. Bad ',...
169.             'initial guess for frequency OR\n\t2. ',...
170.             'the amplitude level is way below RMS noise floor OR',...
171.             '\n\t3. too small MAX_FUN, FUN_ITER, MTOL or TOL.\n\n'])
172.         fprintf(['%g function calls made, %g iterations allowed ',...
173.             'in each.\n\n'],iter_sinefit4par-1,MAX_ITER)
174.         success = 0;
175.         %return
176.     else
177.         success = 1;
178.         if verbose
179.             st1 = 'converged!\n';
180.             fprintf(st1)
181.             fprintf(['\t%g function calls made,\n\t%g iterations allowed ',...
182.             'in each.\n\n'],iter_sinefit4par,MAX_ITER)
183.         end
184.     end
185. end
186. %prep the output parameters
187. A0=x0(1);   B0=x0(2);
188. C0=x0(3);   w=x0(4);
189.
190. A=sqrt(A0*A0+B0*B0);
191.
192. phi=atan(-B0/A0);
193. if A0<0
194.     phi=phi+pi;
195. end
196.
197. f_est=w/(2*pi);
198. params = [C0 A f_est phi];
199. yest = x0(3) + x0(1)*cos(x0(4)*t) + x0(2)*sin(x0(4)*t);
200. yres = yin-yest;
201. rmserr = rms(yres,N);
202.
203. if verbose
204.     t_elapsed=toc;
205.     if t_elapsed<60
206.         fprintf('\tTime elapsed: %g seconds\n',t_elapsed)
207.     else
208.         % this is not likely to happen
209.         fprintf('\tTime elapsed: %g minutes and %g seconds\n',...
210.             floor(t_elapsed/60),rem(t_elapsed,60))
211.     end
212.
213.     if plotflag
214.         fprintf('\nplotting...')
215.     end
216.
217. end
218.
219. %plot or not
220. if nargin == 5
221.     if plotflag == 1
222.         plotsinefit(N_hist,N_per,t,ts,yin,yest,yres,f_est,N,verbose)
223.     end
224. end
225.
226. if not(success)
227.     params = [nan nan nan nan];
228.     yest=nan; yres=nan; rmserr=nan;
229. end
230.
231. %preserve the original vector dimensions
232. if nargout > 1
233.     if rc(1)==1
234.         yest = yest.';
235.         yres = yres.';
236.     end
237. end
238.
239. function x0 = sinefit3par(yin,wt,onevec)
240.     %3-parameter fit is used to create an initiall guess in sinefit4par
241.     cosvec=cos(wt);
242.     sinvec=sin(wt);
243.     D0=[cosvec sinvec onevec];
244.     %x0=inv(D0.'*D0)*(D0.'*yin);
245.     [Q,R] = qr(D0,0);
246.     x0 = R\(Q.'*yin);
247.     %x0=lscov(D0,yin);
248.
249. function [x0,iter] = sinefit4par(yin,t,ts,w,onevec,TOL,MAX_ITER)
250.     x0 = sinefit3par(yin,w*t,onevec);
251.     x0 = [x0;0];
252.     iter = 0;success = 0;
253.     while success == 0
254.         w=w+x0(4);
255.         wt=w*t;
256.         cosvec=cos(wt);
257.         sinvec=sin(wt);
258.         D0=[cosvec sinvec onevec -x0(1)*t.*sinvec+x0(2)*t.*cosvec];
259.         x0old = x0;
260.         %x0=inv(D0.'*D0)*(D0.'*yin);
261.         [Q,R] = qr(D0,0);
262.         x0 = R\(Q.'*yin);
263.         %x0=lscov(D0,yin);
264.         iter = iter + 1;
265.
266.         %error term with dw normalized
267.         temp=abs(x0-x0old).*[1 1 1 ts].';
268.         err=max(temp);
269.
270.         if err<TOL || iter > MAX_ITER %if iter>MAX_ITER, increase TOL and
271.             success = 1;              %re-visit this function later.
272.         end
273.     end
274.     x0(end)=w;  %place w in the position if w's increment
275.
276. function plotsinefit(N_hist,N_per,t,ts,yin,yest,yres,f,N,verbose)
277.
278. % function deleted in order to keep post within character limits
279.
280.
281.
282.
283.
284.
285.
286.
287.
288.
289.
290.

12. ### WBahn Moderator

Mar 31, 2012
18,089
4,917
As has already been noted, you can take the average of a rectified version of the sine wave and measure that value and then scale it (either in analog or in code) to mean RMS or Vpp or whatever you want that has an analytical relationship to the average of the rectified signal assuming the original signal is a sine wave (don't forget that assumption, either).

Another alternative, if you are perhaps looking for the min/max of the signal and want to allow for the possibility that the waveform might not be sinusoidal, is to use a peak detector. Basically this is just a diode feeding an RC filter. You measure the voltage across the capacitor with your ADC. As the signal voltage rises above the capacitor voltage, it starts to turn on the diode and, as it continues to rise, the capacitor voltage is dragged up along with the signal voltage (staying one diode forward voltage drop below it). When the signal voltage drops, the diode is reverse biased and the capacitor holds the voltage associated with the peak signal voltage. The resistor, which is in parallel with the capacitor, slowly bleeds the voltage off the capacitor.

How you size the components depends on the frequency of the waveform and, even more importantly, exactly what you are interested in. If you want to detect cycle to cycle variations in amplitude, then you need to sample faster and bleed charge quicker. If you are satisfied to just know what the peak is doing over the course of many cycles, then you can sample slower and bleed slower.

An alternative to the resistor is a transistor switch. The let's you hold a better representation of the peak that isn't as dependent on the latency between when the peak occurred and when you happen to sample it, but it requires an additional output to stroke the switch to reset the sampling capacitor.

To capture the minimum, you simply reverse the polarity of the diode.

This approach may not be useful for what you are doing now, but put it in the back of your toolbag for future consideration.

13. ### THE_RB AAC Fanatic!

Feb 11, 2008
5,435
1,305
Thanks for posting that MrChips, there's some interesting code there.

14. ### MrChips Moderator

Oct 2, 2009
12,648
3,458
BTW don't let long complicated code stop you from implementing it on an mcu.

For my FFT code, I copied code off the internet but couldn't get it to work.
So I converted the code to MATLAB and analyzed where it was going wrong.
Then I converted it back to C on the mcu.

15. ### MrChips Moderator

Oct 2, 2009
12,648
3,458
Also doing a least-squares fit to a sine wave is a digital solution to the age-old synchronous detection problem which is traditionally implemented using lock-in amplifiers.

I designed and built an EIT (Electrical Impedance Tomography) system where I inject a sinusoid into the device and then detect μV signals and measure the amplitude and phase.