Compute Linear Calibration Constants

Compute Linear Calibration Constants

Cals computes the Slope and Y Intercept (M and b) constants to solve the "Y = M * X = b" equation of a straight line. Virtually all Analog to Digital Converters perform a linear conversion of inputs to their raw converter values. The value that the computer reads is a unitless number that then must be converted into engineering units. The value of Y, in engineering units, is directly proportional to the raw reading of X.

For example, a Rabbit BL2100 Smartcat, A/D input values of 0 to 4095 correspond to voltages of -10.0 volts to +10.0 volts. A reading of 123 is returned when the input voltage is -9,39927 volts if everything is calibrated correctly. The reading, 123 times 0.00488401 (the slope) plus 10.0 (the Y-Intercept) returns a value of -9,39927 volts.

Cals.h

/*********************************************************************************************
* Copyright (c) 2003 - 2011 David Cass Tyler, PO Box 1026, Willard, NM 87063, (505) 384-5342 *
*********************************************************************************************/

#if !defined(AFX_CAL_H__37363CE4_6108_4AED_A99A_F24CDF506083__INCLUDED_)
#define AFX_CAL_H__37363CE4_6108_4AED_A99A_F24CDF506083__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#ifndef __AFXWIN_H__
   #error include 'StdAfx.h' before including this file for PCH
#endif

#include "resource.h"      // main symbols

/////////////////////////////////////////////////////////////////////////////
// CCalApp:
// See Cals.cpp for the implementation of this class
//

class CCalApp : public CWinApp
{
public:
   CCalApp();

// Overrides
   // ClassWizard generated virtual function overrides
   //{{AFX_VIRTUAL(CCalApp)
   public:
   virtual BOOL InitInstance();
   //}}AFX_VIRTUAL

// Implementation

   //{{AFX_MSG(CCalApp)
      // NOTE - the ClassWizard will add and remove member functions here.
      //    DO NOT EDIT what you see in these blocks of generated code !
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};


/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_CAL_H__37363CE4_6108_4AED_A99A_F24CDF506083__INCLUDED_)

CalDlg.h

/*********************************************************************************************
* Copyright (c) 2003 - 2011 David Cass Tyler, PO Box 1026, Willard, NM 87063, (505) 384-5342 *
*********************************************************************************************/

#if !defined(AFX_CALDLG_H__F8CB7C67_1287_4929_A160_268B53A13E43__INCLUDED_)
#define AFX_CALDLG_H__F8CB7C67_1287_4929_A160_268B53A13E43__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

/////////////////////////////////////////////////////////////////////////////
// CCalDlg dialog

class CCalDlg : public CDialog
{
// Construction
public:
   CCalDlg(CWnd* pParent = NULL);   // standard constructor

// Dialog Data
   //{{AFX_DATA(CCalDlg)
   enum { IDD = IDD_CAL_DIALOG };
   float m_fltX1;
   float m_fltX2;
   float m_fltY1;
   float m_fltY2;
   CString  m_strSlope;
   CString  m_strIntercept;
   float m_fltRaw;
   CString  m_strProc;
   //}}AFX_DATA

   // ClassWizard generated virtual function overrides
   //{{AFX_VIRTUAL(CCalDlg)
   protected:
   virtual void DoDataExchange(CDataExchange* pDX);   // DDX/DDV support
   //}}AFX_VIRTUAL

// Implementation

   void Recompute ( void );

protected:
   HICON m_hIcon;

   // Generated message map functions
   //{{AFX_MSG(CCalDlg)
   virtual BOOL OnInitDialog();
   afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
   afx_msg void OnPaint();
   afx_msg HCURSOR OnQueryDragIcon();
   virtual void OnOK();
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_CALDLG_H__F8CB7C67_1287_4929_A160_268B53A13E43__INCLUDED_)

Cals.cpp

/************************************************************************************************
*                                                                                               *
*  Copyright (c) 2003 - 2011 David Cass Tyler, PO Box 1026, Willard, NM 87063, (505) 384-5342   *
*                                                                                               *
*  Compute the Linear Calibration Constants (i.e. M and b) for the equation Y = M * X = b       *
*                                                                                               *
*  Virtually all of the modern A/D sensors (and for that matter, the D/A converters) are have   *
*  a linear response.  Thus, the equation displayed above governs the conversion from the       *
*  observed A/D reading to the actual value in engineering units.                               *
*                                                                                               *
************************************************************************************************/

#include "StdAfx.h"
#include "Cals.h"
#include "CalDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CCalApp

BEGIN_MESSAGE_MAP(CCalApp, CWinApp)
   //{{AFX_MSG_MAP(CCalApp)
      // NOTE - the ClassWizard will add and remove mapping macros here.
      //    DO NOT EDIT what you see in these blocks of generated code!
   //}}AFX_MSG
   ON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CCalApp construction

CCalApp::CCalApp()
{
   // TODO: add construction code here,
   // Place all significant initialization in InitInstance
}

/////////////////////////////////////////////////////////////////////////////
// The one and only CCalApp object

CCalApp theApp;

/////////////////////////////////////////////////////////////////////////////
// CCalApp initialization

BOOL CCalApp::InitInstance()
{
   AfxEnableControlContainer();

   // Standard initialization
   // If you are not using these features and wish to reduce the size
   //  of your final executable, you should remove from the following
   //  the specific initialization routines you do not need.

#ifdef _AFXDLL
   Enable3dControls();        // Call this when using MFC in a shared DLL
#else
   Enable3dControlsStatic();  // Call this when linking to MFC statically
#endif

   CCalDlg dlg;
   m_pMainWnd = &dlg;
   int nResponse = dlg.DoModal();
   if (nResponse == IDOK)
   {
      // TODO: Place code here to handle when the dialog is
      //  dismissed with OK
   }
   else if (nResponse == IDCANCEL)
   {
      // TODO: Place code here to handle when the dialog is
      //  dismissed with Cancel
   }

   // Since the dialog has been closed, return FALSE so that we exit the
   //  application, rather than start the application's message pump.
   return FALSE;
}

CalDlg.cpp

/*********************************************************************************************
* Copyright (c) 2003 - 2011 David Cass Tyler, PO Box 1026, Willard, NM 87063, (505) 384-5342 *
*********************************************************************************************/

#include "StdAfx.h"

#include <MATH.H>                      // fabs()

#include "Cals.h"
#include "CalDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
   CAboutDlg();

// Dialog Data
   //{{AFX_DATA(CAboutDlg)
   enum { IDD = IDD_ABOUTBOX };
   //}}AFX_DATA

   // ClassWizard generated virtual function overrides
   //{{AFX_VIRTUAL(CAboutDlg)
   protected:
   virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
   //}}AFX_VIRTUAL

// Implementation
protected:
   //{{AFX_MSG(CAboutDlg)
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
   //{{AFX_DATA_INIT(CAboutDlg)
   //}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
   CDialog::DoDataExchange(pDX);
   //{{AFX_DATA_MAP(CAboutDlg)
   //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
   //{{AFX_MSG_MAP(CAboutDlg)
      // No message handlers
   //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CCalDlg dialog

CCalDlg::CCalDlg(CWnd* pParent /*=NULL*/)
   : CDialog(CCalDlg::IDD, pParent)
{
   //{{AFX_DATA_INIT(CCalDlg)
   m_fltX1 = 0.0f;
   m_fltX2 = 10.0f;
   m_fltY1 = 0.0f;
   m_fltY2 = 10.0f;
   m_strSlope = _T("");
   m_strIntercept = _T("");
   m_fltRaw = 0.0f;
   m_strProc = _T("");
   //}}AFX_DATA_INIT
   // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
   m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CCalDlg::DoDataExchange(CDataExchange* pDX)
{
   CDialog::DoDataExchange(pDX);
   //{{AFX_DATA_MAP(CCalDlg)
   DDX_Text(pDX, IDC_X1, m_fltX1);
   DDX_Text(pDX, IDC_X2, m_fltX2);
   DDX_Text(pDX, IDC_Y1, m_fltY1);
   DDX_Text(pDX, IDC_Y2, m_fltY2);
   DDX_Text(pDX, IDC_M, m_strSlope);
   DDX_Text(pDX, IDC_B, m_strIntercept);
   DDX_Text(pDX, IDC_RAW, m_fltRaw);
   DDX_Text(pDX, IDC_PROC, m_strProc);
   //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CCalDlg, CDialog)
   //{{AFX_MSG_MAP(CCalDlg)
   ON_WM_SYSCOMMAND()
   ON_WM_PAINT()
   ON_WM_QUERYDRAGICON()
   ON_EN_KILLFOCUS(IDC_X1,    Recompute)
   ON_EN_KILLFOCUS(IDC_X2,    Recompute)
   ON_EN_KILLFOCUS(IDC_Y1,    Recompute)
   ON_EN_KILLFOCUS(IDC_Y2,    Recompute)
   ON_EN_KILLFOCUS(IDC_RAW,   Recompute)
   //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CCalDlg message handlers

BOOL CCalDlg::OnInitDialog()
{
   CDialog::OnInitDialog();

   Recompute();

   // Add "About..." menu item to system menu.

   // IDM_ABOUTBOX must be in the system command range.
   ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
   ASSERT(IDM_ABOUTBOX < 0xF000);

   CMenu* pSysMenu = GetSystemMenu(FALSE);
   if (pSysMenu != NULL)
   {
      CString strAboutMenu;
      strAboutMenu.LoadString(IDS_ABOUTBOX);
      if (!strAboutMenu.IsEmpty())
      {
         pSysMenu->AppendMenu(MF_SEPARATOR);
         pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
      }
   }

   // Set the icon for this dialog.  The framework does this automatically
   //  when the application's main window is not a dialog
   SetIcon(m_hIcon, TRUE);       // Set big icon
   SetIcon(m_hIcon, FALSE);      // Set small icon
   
   // TODO: Add extra initialization here
   
   return TRUE;  // return TRUE  unless you set the focus to a control
}

void CCalDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
   if ((nID & 0xFFF0) == IDM_ABOUTBOX)
   {
      CAboutDlg dlgAbout;
      dlgAbout.DoModal();
   }
   else
   {
      CDialog::OnSysCommand(nID, lParam);
   }
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CCalDlg::OnPaint() 
{
   if (IsIconic())
   {
      CPaintDC dc(this); // device context for painting

      SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

      // Center icon in client rectangle
      int cxIcon = GetSystemMetrics(SM_CXICON);
      int cyIcon = GetSystemMetrics(SM_CYICON);
      CRect rect;
      GetClientRect(&rect);
      int x = (rect.Width() - cxIcon + 1) / 2;
      int y = (rect.Height() - cyIcon + 1) / 2;

      // Draw the icon
      dc.DrawIcon(x, y, m_hIcon);
   }
   else
   {
      CDialog::OnPaint();
   }
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CCalDlg::OnQueryDragIcon()
{
   return (HCURSOR) m_hIcon;
}

void CCalDlg::Recompute ( void )

   {
   float fltSlope       = 0.0f;
   float fltIntercept   = 0.0f;
   float fltProcessed   = 0.0f;

   // Get and Update the Data

   UpdateData ( TRUE );

   // Update the Slope

   if ( m_fltX2 - m_fltX1 )
      {
      fltSlope = ( m_fltY2 - m_fltY1 ) / ( m_fltX2 - m_fltX1 );
      }
   else
      {
      * ( ( DWORD * ) ( &fltSlope ) ) = 0xFFFFFFFF;   // Set to NaN
      }

   m_strSlope.Format ( "%g", fltSlope );

   // Update the Intercept

   fltIntercept = m_fltY2 - ( fltSlope * m_fltX2 );

   // Avoid Displaying Tiny (Essentially Zero) Intercept Values

   if ( fabs ( fltIntercept ) < fabs ( m_fltY2 - m_fltY1 ) / 100000.0 )
      fltIntercept = 0.0f;

   m_strIntercept.Format ( "%g", fltIntercept );

   // Update the Raw to Processed Value

   fltProcessed = fltSlope * m_fltRaw + fltIntercept;

   m_strProc.Format ( "%g", fltProcessed );

   // Display the Computed Values

   UpdateData ( FALSE );

   }  // void CCalDlg::Recompute ( void )

void CCalDlg::OnOK()

   {

   Recompute();

// CDialog::OnOK();

   }  // void CCalDlg::OnOK()