measuring sine wave signal with adc of PIC16F

Thread Starter

aamirali

Joined Feb 2, 2012
412
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.
 

THE_RB

Joined Feb 11, 2008
5,438
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.
 

ErnieM

Joined Apr 24, 2011
8,377
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.
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.
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.
 

MrChips

Joined Oct 2, 2009
30,806
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.
 

crutschow

Joined Mar 14, 2008
34,432
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.
 

THE_RB

Joined Feb 11, 2008
5,438
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.
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?
 

THE_RB

Joined Feb 11, 2008
5,438
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. ;)
 

MrChips

Joined Oct 2, 2009
30,806
I got this MATLAB code off the internet.
Supply it with the digitized waveform and it returns
DC offset, amplitude, frequency and phase angle.

Rich (BB code):
function [params,yest,yres,rmserr] = sinefit(yin,t,f,verbose,plotflag)
% params = sinefit(yin,t,f)
% [params,yest] = sinefit(yin,t,f)
% [params,yest] = sinefit(yin,t,f,verbose)
% [params,yest] = sinefit(yin,t,f,verbose,plotflag)
% [params,yest,yres] = sinefit(yin,t,...)
% [params,yest,yres,rmserr] = sinefit(yin,t,...)
% 
% Least squares sinusoid fit - optimization toolbox not needed
%
% IEEE Standard for Digitizing Waveform Recorders (IEEE Std 1057):
% Algorithm for three-parameter (known frequency) and four-parameter 
% (general use) least squares fit to sinewave data using matrix operations.
%
% INPUTS:   -yin:      Input signal to be fitted (a vector)
%           -t:        Time vector (same length as yin)
%           -f:        Signal frequency (actual or estimate) [Hz]
%           -verbose:  if 1, display information; else, don't. default: 0
%           -plotflag: If 1, plot; else, don't. default: 0. The plots are:
%                      time-domain plot, histograms and modulo-time plot.
%                      Modulo-time plot is copied from file ID: #22907
%
% -The time vector is not necessarily linearly spaced (hence required here)
% -If f is a guess, it has to be near true input signal frequency
%
% OUTPUTS:  -params:  A vector containing:
%                     [offset amplitude freq(Hz) angle(rad)].
%                     If not converged, params = [nan nan nan nan]
%           -yest:    Estimated sinusoid:
%                     offset+amplitude*cos(2*pi*frequency_Hz*t+angle_rad
%           -yres:    The residual
%           -rmserr:  Root mean square of the residual
%
% 4-parameter sinefit uses 3-parameter sinefit to create initial search 
% vector. For further information, consult IEEE Std 1057 and/or 
% IEEE Std 1241 documentation.
% 
% type sinefit for demo

% convergence related things you can tweak:
TOL = 2.2204e-016;    % Normalized initial tolerance (default: eps )
MTOL = 4;             % TOL relaxation multiplicand, MTOL > 1 
MAX_FUN= 16;          % MAX number of function calls
MAX_ITER = 32;        % MAX number of iterations in one function call

% Convergence related stuff: Vector x0 will be created: x0=[A B C dw], 
% where A & B are sin & cos multiplicands, C is the offset and dw is 
% the angular frequency increment in iterative search.
% x0 is, of course, normalized in convergence calculation
%(Fit for 1*yin converges as fast as for 100*yin)
% ****  if max(abs(x0(i)-x0(i-1)))<TOL, the fit is complete.
% ****  if not, multiply TOL with MTOL and retry maximum of MAX_FUN times.


%plotting related
if nargin == 5 
    if plotflag
        N_per=8; %number of periods plotted
        N_hist = 11; %number of bins in histogram
    end
else
    plotflag = 0;
end

if nargin == 0   %demo
    fprintf('\ndemo: creating sinusoid with\n\t- clock jitter,\n\t')
    fprintf('- phase noise,\n\t- additive noise and \n\t- harmonics\n ')
    N=pow2(11);              %vector length
    fs = pi*1e3;            %sampling freq
    ts = 1/fs;              %sampling interval
    freq = (N/128-1)*fs/N;  %freq (Hz)
    phase = -pi/8;          %phase (rad)
    offset = pi*4;          %offset (i.e mean)
    amplitude = pi/3;       %amplitude
    t = (0:(N-1))*ts;       %time vector
    std_jitter = 1e-2;  %standard deviation of jitter noise
    std_addnoi = 1e-1;  %standard deviation of  noise added to signal
    std_phase  = 1e-2;   %standard deviation of phase noise
    noise = randn(1,N);
    std1_noise = noise/std(noise);  % random vector with stdev = 1
    jit_noise = std_jitter*std1_noise;
    phase_noise = std_phase*std1_noise;
    add_noise = std_addnoi*std1_noise;
    w=2*pi*freq;
    t = t + ts*jit_noise;                          % add clock jitter
    A2 = amplitude*0.01;     % 2. harmonic ampl
    A3 = amplitude*0.02;     % 3. harmonic ampl
    yin = cos(w*t+phase+phase_noise);  % sinusoid with phase noise
    %add offset, noise & harmonics
    yin = offset+amplitude*yin+A2*yin.*yin+A3*yin.*yin.*yin+add_noise; 
    figure
    params = sinefit(yin,t,freq,1,1);
    fprintf('\n\t\t\tpure sinusoid\tnoisy sinusoid fit')
    fprintf('\nOffset:   \t%3.4g\t\t\t%3.4g',offset,params(1))
    fprintf('\nAmpl:   \t%3.4g\t\t\t%3.4g',amplitude,params(2))
    fprintf('\nFreq:   \t%3.4g\t\t\t%3.4g [Hz]',freq,params(3))
    fprintf('\nPhase:\t\t%3.4g*pi\t\t%3.4g*pi [rad]\n',phase/pi,...
        params(4)/pi)
    fprintf('\nend demo\n')
    clear params yest
    return
end   %end demo

if nargin < 4
    verbose = 0;
end

if verbose
    tic
end

%vector dimension preps
[rc]=size(yin);
if rc(1)==1
    yin = yin(:);
    N=rc(2);
else
    N=rc(1);
end
t=t(:);
onevec = ones(N,1);

%t does not have to be linearly spaced, so to estimate sampling time ts
ts = rms(diff(t),N-1); % ts needed in freq normalization of converg. calc

if MTOL<0
    error('MTOL is a positive number > 1')
elseif MTOL<1
    MTOL=1/MTOL;
    fprintf('warning: MTOL should be > 1,')
    fprintf('swiching to inverse value.\nXTOL = %i\n',MTOL)
end

% convergence related normalization 
rmsyin = rms(yin,N);
TOL=rmsyin*TOL;

%here we go
[x0,iter] = sinefit4par(yin,t,ts,2*pi*f,onevec,TOL,MAX_ITER);
iter_total=iter;
iter_sinefit4par = 1;  %first function call

%success?
if iter<=MAX_ITER
    success = 1;
    if verbose
        st1 = ['\n\tConverged after ' int2str(iter) ' iterations\n'];
        fprintf(st1)
    end
else
    if verbose
        st2 = '\n\tincreasing TOL...';
        fprintf(st2)
    end
    while iter>MAX_ITER && iter_sinefit4par<=MAX_FUN
        if verbose
            fprintf('.')
        end
        %while # of function calls is < MAX_FUN, do:
        TOL = TOL*MTOL;   %increase TOL
        [x0,iter] = sinefit4par(yin,t,ts,2*pi*f,onevec,TOL,MAX_ITER);
        iter_total = iter_total+iter;
        iter_sinefit4par=iter_sinefit4par+1;
    end
    if iter>MAX_ITER
        clc
        fprintf(['\nFailed to fit. The reasons could be:\n\t1. Bad ',...
            'initial guess for frequency OR\n\t2. ',...
            'the amplitude level is way below RMS noise floor OR',...
            '\n\t3. too small MAX_FUN, FUN_ITER, MTOL or TOL.\n\n'])
        fprintf(['%g function calls made, %g iterations allowed ',...
            'in each.\n\n'],iter_sinefit4par-1,MAX_ITER)
        success = 0;
        %return
    else
        success = 1;
        if verbose
            st1 = 'converged!\n';
            fprintf(st1)
            fprintf(['\t%g function calls made,\n\t%g iterations allowed ',...
            'in each.\n\n'],iter_sinefit4par,MAX_ITER)
        end
    end
end
%prep the output parameters
A0=x0(1);   B0=x0(2);
C0=x0(3);   w=x0(4);

A=sqrt(A0*A0+B0*B0);

phi=atan(-B0/A0);
if A0<0
    phi=phi+pi;
end

f_est=w/(2*pi);
params = [C0 A f_est phi];
yest = x0(3) + x0(1)*cos(x0(4)*t) + x0(2)*sin(x0(4)*t);
yres = yin-yest;
rmserr = rms(yres,N);

if verbose
    t_elapsed=toc;
    if t_elapsed<60
        fprintf('\tTime elapsed: %g seconds\n',t_elapsed)
    else
        % this is not likely to happen
        fprintf('\tTime elapsed: %g minutes and %g seconds\n',...
            floor(t_elapsed/60),rem(t_elapsed,60))
    end
    
    if plotflag
        fprintf('\nplotting...')
    end
    
end

%plot or not
if nargin == 5
    if plotflag == 1
        plotsinefit(N_hist,N_per,t,ts,yin,yest,yres,f_est,N,verbose)
    end
end

if not(success)
    params = [nan nan nan nan];
    yest=nan; yres=nan; rmserr=nan;
end

%preserve the original vector dimensions
if nargout > 1 
    if rc(1)==1
        yest = yest.';
        yres = yres.';
    end
end

function x0 = sinefit3par(yin,wt,onevec)
    %3-parameter fit is used to create an initiall guess in sinefit4par
    cosvec=cos(wt);
    sinvec=sin(wt);
    D0=[cosvec sinvec onevec];
    %x0=inv(D0.'*D0)*(D0.'*yin);
    [Q,R] = qr(D0,0);
    x0 = R\(Q.'*yin); 
    %x0=lscov(D0,yin);

function [x0,iter] = sinefit4par(yin,t,ts,w,onevec,TOL,MAX_ITER)
    x0 = sinefit3par(yin,w*t,onevec);
    x0 = [x0;0];
    iter = 0;success = 0;
    while success == 0
        w=w+x0(4);
        wt=w*t;
        cosvec=cos(wt);
        sinvec=sin(wt);
        D0=[cosvec sinvec onevec -x0(1)*t.*sinvec+x0(2)*t.*cosvec];
        x0old = x0;
        %x0=inv(D0.'*D0)*(D0.'*yin);
        [Q,R] = qr(D0,0);
        x0 = R\(Q.'*yin);
        %x0=lscov(D0,yin);
        iter = iter + 1;
        
        %error term with dw normalized
        temp=abs(x0-x0old).*[1 1 1 ts].';
        err=max(temp);
        
        if err<TOL || iter > MAX_ITER %if iter>MAX_ITER, increase TOL and
            success = 1;              %re-visit this function later.
        end
    end
    x0(end)=w;  %place w in the position if w's increment

function plotsinefit(N_hist,N_per,t,ts,yin,yest,yres,f,N,verbose)

% function deleted in order to keep post within character limits
 

WBahn

Joined Mar 31, 2012
30,058
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.
 

MrChips

Joined Oct 2, 2009
30,806
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.
 

MrChips

Joined Oct 2, 2009
30,806
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.
 
Top