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,415
    3,354
    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,386
    1,605
    If it varies with every measurement you are probably doing it right. :D

    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,415
    3,354
    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
    12,977
    3,220
    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,415
    3,354
    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,415
    3,354
    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:
    31. %                     offset+amplitude*cos(2*pi*frequency_Hz*t+angle_rad
    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
    78.     std_addnoi = 1e-1;  %standard deviation of  noise added to signal
    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;
    84.     add_noise = std_addnoi*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
    91.     yin = offset+amplitude*yin+A2*yin.*yin+A3*yin.*yin.*yin+add_noise;
    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))
    98.     fprintf('\nPhase:\t\t%3.4g*pi\t\t%3.4g*pi [rad]\n',phase/pi,...
    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
    17,715
    4,788
    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,415
    3,354
    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,415
    3,354
    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.
     
Loading...