adc2.c -- Determine scale and offset calibration for Analog to Digital converter input.
/* adc2.c */

/* Copyright(c) 1991-2002 by Motion Engineering, Inc.  All rights reserved.
 *
 * This software  contains proprietary and  confidential information  of
 * Motion Engineering Inc., and its suppliers.  Except as may be set forth
 * in the license agreement under which  this software is supplied, use,
 * disclosure, or  reproduction is prohibited without the prior express
 * written consent of Motion Engineering, Inc.
 */

#if defined(MEI_RCS)
static const char MEIAppRCS[] =
    "$Header: /MainTree/XMPLib/XMP/app/adc2.c 5     8/01/01 2:08p Kevinh $";
#endif

#if defined(ARG_MAIN_RENAME)
#define main    adc2Main

argMainRENAME(main, adc2)
#endif

/*

:Determine scale and offset calibration for Analog to Digital converter input.

This sample code demonstrates how to determine calibration factors for
 an ADC input channel.  This sample requires an external source to generate
 the analog voltage into an ADC channel.

The calibration factors are determined by sampling two known voltages.
 The default voltages are 5 volts and -5 volts.  Other voltages can be set by
 changing the definitions of the #define's LINE_VOLTAGE_1 and LINE_VOLTAGE_2.
 This default input lines are Analog_IN_0 and Analog_IN_1.  These can be changed
 by changing the definitions of the #define's LINE_INPUT_1 and LINE_INPUT_2.

ATTENTION: The uncertanties of the input voltage lines should also be specified
 for an accurate analysis.  The smaller these uncertainties, the better analysis
 you will receive.  The default values are 0.01 Volts and can be specified by
 changing the #define's LINE_VOLTAGE_UNCERTAINTY_1 and
 LINE_VOLTAGE_UNCERTAINTY_2.

Using the ADC values, voltage values, and uncertainties, displayAdcAnalysis()
 determines the linear "scale" factor (m) and the "offset" (b).  These
 calibration factors can be applied to future ADC value conversions in
 application code, to improve the ADC accuracy and analysis of the values read.

Warning!  This is a sample program to assist in the integration of the
 XMP motion controller with your application.  It may not contain all
 of the logic and safety features that your application requires.
*/

#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#include "stdmpi.h"
#include "stdmei.h"

#include "apputil.h"


#define ADC_COUNT       (8)

#define ADC_RANGE                   (10.0)  /* 10.0, 5.0, 2.5, or 1.25 volts */
#define LINE_VOLTAGE_1              (5.00)
#define LINE_VOLTAGE_2              (-5.00)
#define LINE_VOLTAGE_UNCERTAINTY_1  (0.01)
#define LINE_VOLTAGE_UNCERTAINTY_2  (0.01)

#define LINE_INPUT_1        (MEIAdcMuxANALOG_IN_0)
#define LINE_INPUT_2        (MEIAdcMuxANALOG_IN_1)

#define SAMPLES     (1000)  /* Samples per input line */


/* Perform basic command line parsing. (-control -server -port -trace) */
void basicParsing(int                    argc,
                  char                  *argv[],
                  MPIControlType        *controlType,
                  MPIControlAddress     *controlAddress)
{
    long argIndex;

    /* Parse command line for Control type and address */
    argIndex = argControl(argc, argv, controlType, controlAddress);

    /* Check for unknown/invalid command line arguments */
    if (argIndex < argc) {
        fprintf(stderr,"usage: %s %s\n", argv[0], ArgUSAGE);
        exit(MPIMessageARG_INVALID);
    }
}


/* Create and initialize MPI objects */
void programInit(MPIControl         *control,
                 MPIControlType      controlType,
                 MPIControlAddress  *controlAddress,
                 MPIAdc             *adc,
                 long                adcCount)
{
    long index;
    long returnValue;


    /* Create motion controller object */
    *control =
        mpiControlCreate(controlType,
                         controlAddress);
    msgCHECK(mpiControlValidate(*control));

    /* Initialize motion controller */
    returnValue =
        mpiControlInit(*control);
    msgCHECK(returnValue);

    /* Create adc objects */
    for (index = 0; index < adcCount; index++) {
        adc[index] =
            mpiAdcCreate(*control,
                         index);
        msgCHECK(mpiAdcValidate(adc[index]));
    }
}


/* Perform certain cleanup actions and delete MPI objects */
void programCleanup(MPIControl  *control,
                    MPIAdc      *adc,
                    long         adcCount)
{
    long index;
    long returnValue;


    /* Delete adc objects */
    for (index = 0; index < adcCount; index++) {
        returnValue =
            mpiAdcDelete(adc[index]);
        msgCHECK(returnValue);
    }

    /* Delete motion controller object */
    returnValue =
        mpiControlDelete(*control);
    msgCHECK(returnValue);
}


/* Enable adcs */
void enableAdcs(MPIControl  control,
                long        adcCount)
{
    MPIControlConfig    controlConfig;
    long                returnValue;


    /* Read controller configuration */
    returnValue =
        mpiControlConfigGet(control,
                            &controlConfig,
                            NULL);
    msgCHECK(returnValue);

    controlConfig.adcCount = adcCount;

    /* Write controller configuration */
    returnValue =
        mpiControlConfigSet(control,
                            &controlConfig,
                            NULL);
    msgCHECK(returnValue);
}


/* Configure adc object */
void adcConfigure(MPIAdc    adc,
                  MEIAdcMux input,
                  double    range)
{
    MPIAdcConfig    adcConfig;
    MEIAdcConfig    adcConfigXmp;
    long            returnValue;


    /* Read adc configuration */
    returnValue =
        mpiAdcConfigGet(adc,
                        &adcConfig,
                        &adcConfigXmp);
    msgCHECK(returnValue);

    /* Set voltage range */
    adcConfig.range = range;

    /* Set voltage input */
    adcConfigXmp.mux = input;

    /* Write adc configuration */
    returnValue =
        mpiAdcConfigSet(adc,
                        &adcConfig,
                        &adcConfigXmp);
    msgCHECK(returnValue);
}


/* Calculate stastical input information */
void getAdcInputInfo(MPIAdc *adc,
                     long    adcCount,
                     long    samples,
                     double *inputMean,
                     double *inputVar)
{
    MPIAdcConfig    adcConfig;

    double sumInput   = 0.0;
    double sumInputSq = 0.0;

    long input;
    long index;
    long returnValue;


    /* Read adc configuration */
    returnValue =
        mpiAdcConfigGet(adc[0],
                        &adcConfig,
                        NULL);
    msgCHECK(returnValue);

    for (index=0; index<samples; index++) {

        /* Read ADC input value */
        returnValue =
            mpiAdcInput(adc[index%adcCount],
                        (unsigned long*)&input);
        msgCHECK(returnValue);

        /* Record input information */
        sumInput+=input;
        sumInputSq+=input * 1.0 * input;

        if (index%adcCount == 0) {
            /* Wait one controller sample */
            meiControlSampleWait(mpiAdcControl(adc[0]),
                                 1);
        }
    }

    /* Calculate stastical input information */
    *inputMean = sumInput / samples;
    *inputVar = (sumInputSq - (sumInput*sumInput)/samples)/(samples-1);
}


/* Display ADC analysis */
void displayAdcAnalysis(double *lineVoltage,
                        double *lineUncertainty,
                        double *inputMean,
                        double *inputVar)
{
    double b;
    double bStdDev;
    double m;
    double mStdDev;
    double deltaInput;
    double deltaInputSq;


    deltaInput = inputMean[1] - inputMean[0];
    deltaInputSq = deltaInput * deltaInput;

    b = (lineVoltage[0]*inputMean[1] - lineVoltage[1]*inputMean[0]) /
            deltaInput;

    m = (lineVoltage[1] - lineVoltage[0]) / deltaInput;

    bStdDev = sqrt(
        ((b - lineVoltage[1])*(b - lineVoltage[1])*inputVar[0] +
            (b - lineVoltage[0])*(b - lineVoltage[0])*inputVar[1] +
            inputMean[0]*inputMean[0]*lineUncertainty[1]*lineUncertainty[1] +
            inputMean[1]*inputMean[1]*lineUncertainty[0]*lineUncertainty[0]) /
        deltaInputSq);

    mStdDev = sqrt(
        (m*m*(inputVar[0] + inputVar[1]) +
            lineUncertainty[0]*lineUncertainty[0] +
            lineUncertainty[1]*lineUncertainty[1]) /
        deltaInputSq);

    printf("Voltage = m * (ADC input) + b\n\n"
           " m = %le +/- %le \t(Volts/ADC_UNIT)\n"
           " b = %le +/- %le \t(Volts)\n\n",
           m, mStdDev,
           b, bStdDev);
}


int main(int     argc,
         char   *argv[])
{
    MPIControl          control;
    MPIControlType      controlType;
    MPIControlAddress   controlAddress;
    MPIAdc              adc[ADC_COUNT];

    double  inputMean[2];
    double  inputVar[2];
    double  lineVoltage[2]  =
        {LINE_VOLTAGE_1, LINE_VOLTAGE_2};
    double  lineUncertainty[2] =
        {LINE_VOLTAGE_UNCERTAINTY_1, LINE_VOLTAGE_UNCERTAINTY_2};

    long    index;

    /* Perform basic command line parsing. (-control -server -port -trace) */
    basicParsing(argc,
                 argv,
                 &controlType,
                 &controlAddress);

    /* Create and initialize MPI objects */
    programInit(&control,
                controlType,
                &controlAddress,
                adc,
                ADC_COUNT);

    /* Enable adcs */
    enableAdcs(control,
               ADC_COUNT);

    /* Configure adc objects to read LINE_INPUT_1 */
    for (index = 0; index < ADC_COUNT; index++) {
        adcConfigure(adc[index],
                     LINE_INPUT_1,
                     ADC_RANGE);
    }

    /* Delay so that adc object can read values from  LINE_INPUT_1 */
    meiPlatformSleep(1);

    /* Calculate stastical input information for LINE_INPUT_1 */
    getAdcInputInfo(adc,
                    ADC_COUNT,
                    SAMPLES,
                    &inputMean[0],
                    &inputVar[0]);

    /* Configure adc objects to read LINE_INPUT_2 */
    for (index = 0; index < ADC_COUNT; index++) {
        adcConfigure(adc[index],
                     LINE_INPUT_2,
                     ADC_RANGE);
    }

    /* Delay so that adc object can read values from  LINE_INPUT_1 */
    meiPlatformSleep(1);

    /* Calculate stastical input information for LINE_INPUT_2 */
    getAdcInputInfo(adc,
                    ADC_COUNT,
                    SAMPLES,
                    &inputMean[1],
                    &inputVar[1]);

    /* Display ADC analysis */
    displayAdcAnalysis(lineVoltage,
                       lineUncertainty,
                       inputMean,
                       inputVar);

    /* Perform certain cleanup actions and delete MPI objects */
    programCleanup(&control,
                   adc,
                   ADC_COUNT);

	return (MPIMessageOK);
}