added support for calibrating analog output linearity
authorFrank Mori Hess <fmhess@speakeasy.net>
Tue, 29 Apr 2003 01:00:22 +0000 (01:00 +0000)
committerFrank Mori Hess <fmhess@speakeasy.net>
Tue, 29 Apr 2003 01:00:22 +0000 (01:00 +0000)
comedi_calibrate/calib.h
comedi_calibrate/cb.c
comedi_calibrate/cb64.c
comedi_calibrate/comedi_calibrate.c
comedi_calibrate/ni.c

index bdec910b7801d404619b4c1de15f36dca86cace1..e97b2ada09c490c816212badb72f55dd1cd46651 100644 (file)
@@ -115,6 +115,8 @@ void cal1( calibration_setup_t *setup, int obs, int dac);
 void cal1_fine( calibration_setup_t *setup, int obs, int dac);
 void cal_binary( calibration_setup_t *setup, int obs, int dac);
 void cal_postgain_binary( calibration_setup_t *setup, int obs1, int obs2, int dac);
+void cal_relative_binary( calibration_setup_t *setup, int obs1, int obs2, int dac);
+void cal_linearity_binary( calibration_setup_t *setup, int obs1, int obs2, int obs3, int dac);
 
 /* misc and temp */
 
index faa81dfc69f6a613c6586d57bb44169bfbffca35..ea76138f8abff09ded6f9ba2c7d010db16b088a3 100644 (file)
@@ -1,7 +1,6 @@
 /***************************************************************************
        cb.c  -  calibration support for some Measurement computing boards.
-       Based on ni.c by David Schleef.
-                             -------------------
+                           -------------------
 
     begin                : Sat Apr 27 2002
     copyright            : (C) 2002,2003 by Frank Mori Hess
index d7948f77d2b75aead2ce1a140782cbf89524df6e..8d87121c68c1fa31ad8a474a5629662e33f7b53d 100644 (file)
@@ -1,6 +1,5 @@
 /***************************************************************************
        cb64.c  -  calibration support for some Measurement computing boards.
-       Based on ni.c by David Schleef.
                              -------------------
 
     begin                : Sat Apr 27 2002
@@ -67,7 +66,7 @@ static struct board_struct boards[]={
        { "pci-das6025",        STATUS_DONE,    setup_cb_pci_60xx },
        { "pci-das6034",        STATUS_GUESS,   setup_cb_pci_60xx },
        { "pci-das6035",        STATUS_GUESS,   setup_cb_pci_60xx },
-       { "pci-das4020/12",     STATUS_SOME,    setup_cb_pci_4020 },
+       { "pci-das4020/12",     STATUS_DONE,    setup_cb_pci_4020 },
 };
 
 static const int num_boards = ( sizeof(boards) / sizeof(boards[0]) );
index eca9f51d00631ede14412597957fd7820dbe0410..f9fef0b99548524ad9e09684a35ac849beb1b32b 100644 (file)
@@ -2,18 +2,17 @@
    A little auto-calibration utility, for boards
    that support it.
 
+   copyright (C) 1999,2000,2001,2002 by David Schleef
+   copyright (C) 2003 by Frank Mori Hess
+
    A few things need improvement here:
     - current system gets "close", but doesn't
       do any fine-tuning
-    - no pre/post gain discrimination for the
-      A/D zero offset.
     - should read (and use) the actual reference
       voltage value from eeprom
     - statistics would be nice, to show how good
       the calibration is.
     - doesn't check unipolar ranges
-    - "alternate calibrations" would be cool--to
-      accurately measure 0 in a unipolar range
     - more portable
  */
 
@@ -525,6 +524,11 @@ void cal_binary( calibration_setup_t *setup, int obs, int dac)
 }
 
 void cal_postgain_binary( calibration_setup_t *setup, int obs1, int obs2, int dac)
+{
+       cal_relative_binary( setup, obs1, obs2, dac );
+}
+
+void cal_relative_binary( calibration_setup_t *setup, int obs1, int obs2, int dac)
 {
        int x0, x1, x2, x, polarity;
        double y0, y1, y2;
@@ -544,12 +548,101 @@ void cal_postgain_binary( calibration_setup_t *setup, int obs1, int obs2, int da
        new_sv_init(&sv1, setup->dev, setup->ad_subdev,chanspec1);
        sv1.settling_time_ns = setup->settling_time_ns;
        new_sv_measure( setup->dev, &sv1);
-       y0 = sv1.average;
+
+       preobserve( setup, obs2);
+       new_sv_init(&sv2, setup->dev, setup->ad_subdev,chanspec2);
+       sv2.settling_time_ns = setup->settling_time_ns;
+       new_sv_measure( setup->dev, &sv2);
+       y0 = y1 = y2 = sv1.average - sv2.average;
+
+       bit = 1;
+       while( ( bit << 1 ) < setup->caldacs[dac].maxdata )
+               bit <<= 1;
+       for( ; bit; bit >>= 1 )
+       {
+               x2 = x1 | bit;
+
+               update_caldac( setup, dac, x2 );
+               usleep(100000);
+
+               preobserve( setup, obs1);
+               new_sv_init(&sv1, setup->dev, setup->ad_subdev,chanspec1);
+               sv1.settling_time_ns = setup->settling_time_ns;
+               new_sv_measure( setup->dev, &sv1);
+
+               preobserve( setup, obs2);
+               new_sv_init(&sv2, setup->dev, setup->ad_subdev,chanspec2);
+               sv2.settling_time_ns = setup->settling_time_ns;
+               new_sv_measure( setup->dev, &sv2);
+               y2 = sv1.average - sv2.average;
+
+               DPRINT(3,"trying %d, result %g, target %g\n",x2,y2,target);
+
+               if( (y2 - y0) > 0.0 ) polarity = 1;
+               else polarity = -1;
+
+               if( (y2 - target) * polarity < 0.0 ){
+                       x1 = x2;
+                       y1 = y2;
+               }
+
+               if(verbose>=3){
+                       preobserve( setup, obs1);
+                       measure_observable( setup, obs1);
+                       preobserve( setup, obs2);
+                       measure_observable( setup, obs2);
+               }
+       }
+
+       if( fabs( y1 - target ) < fabs( y2 - target ) )
+               x = x1;
+       else
+               x = x2;
+       update_caldac( setup, dac, x );
+       DPRINT(0,"caldac[%d] set to %d\n",dac,x);
+       if(verbose>=3){
+               preobserve( setup, obs1);
+               measure_observable( setup, obs1);
+               preobserve( setup, obs2);
+               measure_observable( setup, obs2);
+       }
+}
+
+void cal_linearity_binary( calibration_setup_t *setup, int obs1, int obs2, int obs3, int dac)
+{
+       int x0, x1, x2, x, polarity;
+       double y0, y1, y2;
+       new_sv_t sv1, sv2, sv3;
+       double target = ( setup->observables[obs3].target - setup->observables[obs2].target ) /
+               ( setup->observables[obs2].target - setup->observables[obs1].target );
+       unsigned int chanspec1 = setup->observables[obs1].observe_insn.chanspec;
+       unsigned int chanspec2 = setup->observables[obs2].observe_insn.chanspec;
+       unsigned int chanspec3 = setup->observables[obs3].observe_insn.chanspec;
+       unsigned int bit;
+
+       DPRINT(0,"postgain linearity: %s,\n%s,\n%s\n", setup->observables[obs1].name,
+               setup->observables[obs2].name,setup->observables[obs3].name);
+
+       x0 = x1 = x2 = 0;
+       update_caldac( setup, dac, x0 );
+       usleep(100000);
+
+       preobserve( setup, obs1);
+       new_sv_init(&sv1, setup->dev, setup->ad_subdev,chanspec1);
+       sv1.settling_time_ns = setup->settling_time_ns;
+       new_sv_measure( setup->dev, &sv1);
+
        preobserve( setup, obs2);
        new_sv_init(&sv2, setup->dev, setup->ad_subdev,chanspec2);
        sv2.settling_time_ns = setup->settling_time_ns;
        new_sv_measure( setup->dev, &sv2);
-       y0 -= sv2.average;
+
+       preobserve( setup, obs3);
+       new_sv_init(&sv3, setup->dev, setup->ad_subdev,chanspec3);
+       sv3.settling_time_ns = setup->settling_time_ns;
+       new_sv_measure( setup->dev, &sv3);
+
+       y0 = sv1.average - sv2.average;
        y1 = y2 = y0;
 
        bit = 1;
@@ -566,12 +659,18 @@ void cal_postgain_binary( calibration_setup_t *setup, int obs1, int obs2, int da
                new_sv_init(&sv1, setup->dev, setup->ad_subdev,chanspec1);
                sv1.settling_time_ns = setup->settling_time_ns;
                new_sv_measure( setup->dev, &sv1);
-               y2 = sv1.average;
+
                preobserve( setup, obs2);
                new_sv_init(&sv2, setup->dev, setup->ad_subdev,chanspec2);
                sv2.settling_time_ns = setup->settling_time_ns;
                new_sv_measure( setup->dev, &sv2);
-               y2 -= sv2.average;
+
+               preobserve( setup, obs3);
+               new_sv_init(&sv3, setup->dev, setup->ad_subdev,chanspec3);
+               sv3.settling_time_ns = setup->settling_time_ns;
+               new_sv_measure( setup->dev, &sv3);
+
+               y2 = ( sv3.average - sv2.average ) / ( sv2.average - sv1.average );
 
                DPRINT(3,"trying %d, result %g, target %g\n",x2,y2,target);
 
@@ -588,6 +687,8 @@ void cal_postgain_binary( calibration_setup_t *setup, int obs1, int obs2, int da
                        measure_observable( setup, obs1);
                        preobserve( setup, obs2);
                        measure_observable( setup, obs2);
+                       preobserve( setup, obs3);
+                       measure_observable( setup, obs3);
                }
        }
 
@@ -602,6 +703,8 @@ void cal_postgain_binary( calibration_setup_t *setup, int obs1, int obs2, int da
                measure_observable( setup, obs1);
                preobserve( setup, obs2);
                measure_observable( setup, obs2);
+               preobserve( setup, obs3);
+               measure_observable( setup, obs3);
        }
 }
 
index c76e654d8ad9aec1443e9d2ba43b402a04ec524d..a8c3ba9c24756ac6cb22f18a63359e09ac554a47 100644 (file)
@@ -2,21 +2,17 @@
    A little auto-calibration utility, for boards
    that support it.
 
-   Right now, it only supports NI E series boards,
-   but it should be easily portable.
+   copyright (C) 1999,2000,2001,2002 by David Schleef
+   copyright (C) 2003 by Frank Mori Hess
 
    A few things need improvement here:
     - current system gets "close", but doesn't
       do any fine-tuning
-    - no pre/post gain discrimination for the
-      A/D zero offset.
     - should read (and use) the actual reference
       voltage value from eeprom
     - statistics would be nice, to show how good
       the calibration is.
     - doesn't check unipolar ranges
-    - "alternate calibrations" would be cool--to
-      accurately measure 0 in a unipolar range
     - more portable
  */
 
@@ -42,6 +38,7 @@
 #include <math.h>
 #include <stdlib.h>
 #include <string.h>
+#include <assert.h>
 
 #include "calib.h"
 
@@ -53,6 +50,8 @@ struct board_struct{
        int status;
        int (*cal)( calibration_setup_t *setup);
        void (*setup_observables)( calibration_setup_t *setup );
+       int ref_eeprom_lsb;
+       int ref_eeprom_msb;
 };
 
 static int ni_setup_board( calibration_setup_t *setup , const char *device_name );
@@ -83,28 +82,28 @@ static int cal_ni_daqcard_6062e(calibration_setup_t *setup);
 static double ni_get_reference( calibration_setup_t *setup, int lsb_loc,int msb_loc);
 
 static struct board_struct boards[]={
-       { "at-mio-16e-2",       STATUS_DONE,    cal_ni_at_mio_16e_2,    ni_setup_observables  },
-       { "DAQCard-ai-16xe-50", STATUS_DONE,    cal_ni_daqcard_ai_16xe_50,      ni_setup_observables },
-       { "at-mio-16xe-50",     STATUS_SOME,    cal_ni_at_mio_16xe_50,  ni_setup_observables },
-       { "at-mio-16e-1",       STATUS_SOME,    cal_ni_at_mio_16e_1,    ni_setup_observables },
-       { "pci-mio-16e-1",      STATUS_DONE,    cal_ni_pci_mio_16e_1,   ni_setup_observables },
-       { "pci-6025e",          STATUS_SOME,    cal_ni_pci_6025e,       ni_setup_observables },
-       { "pci-6035e",          STATUS_DONE,    cal_ni_pci_6035e,       ni_setup_observables },
-       { "pci-6071e",          STATUS_SOME,    cal_ni_pci_6071e,       ni_setup_observables },
-       { "pxi-6071e",          STATUS_GUESS,   cal_ni_pxi_6071e,       ni_setup_observables },
-       { "at-mio-16e-10",      STATUS_GUESS,   cal_ni_at_mio_16e_10,   ni_setup_observables },
-       { "pci-mio-16xe-50",    STATUS_SOME,    cal_ni_pci_mio_16xe_50, ni_setup_observables },
-       { "pci-6023e",          STATUS_DONE,    cal_ni_pci_6023e,       ni_setup_observables },
-       { "pci-mio-16xe-10",    STATUS_DONE,    cal_ni_pci_mio_16xe_10, ni_setup_observables },
-       { "pci-6052e",          STATUS_DONE,    cal_ni_pci_6052e,       ni_setup_observables },
-       { "pci-6024e",          STATUS_SOME,    cal_ni_pci_6024e,       ni_setup_observables },
-       { "pci-mio-16e-4",      STATUS_SOME,    cal_ni_pci_mio_16e_4,   ni_setup_observables },
-       { "pci-6032e",          STATUS_DONE,    cal_ni_pci_6032e,       ni_setup_observables },
-       { "DAQCard-ai-16e-4",   STATUS_DONE,    cal_ni_daqcard_ai_16e_4,        ni_setup_observables },
-       { "pci-6110",   STATUS_DONE,    cal_ni_pci_611x,        ni_setup_observables_611x },
-       { "pci-6111",   STATUS_DONE,    cal_ni_pci_611x,        ni_setup_observables_611x },
-       { "DAQCard-6062E", STATUS_SOME, cal_ni_daqcard_6062e, ni_setup_observables },
-       { "DAQCard-6024E",      STATUS_UNKNOWN, NULL, ni_setup_observables },
+       { "at-mio-16e-2", STATUS_DONE, cal_ni_at_mio_16e_2, ni_setup_observables, -1, -1 },
+       { "DAQCard-ai-16xe-50", STATUS_DONE,    cal_ni_daqcard_ai_16xe_50,      ni_setup_observables, -1, -1 },
+       { "at-mio-16xe-50",     STATUS_SOME,    cal_ni_at_mio_16xe_50,  ni_setup_observables, -1, -1 },
+       { "at-mio-16e-1",       STATUS_SOME,    cal_ni_at_mio_16e_1,    ni_setup_observables, -1, -1 },
+       { "pci-mio-16e-1",      STATUS_DONE,    cal_ni_pci_mio_16e_1,   ni_setup_observables, -1, -1 },
+       { "pci-6025e",          STATUS_SOME,    cal_ni_pci_6025e,       ni_setup_observables, -1, -1 },
+       { "pci-6035e",          STATUS_DONE,    cal_ni_pci_6035e,       ni_setup_observables, -1, -1 },
+       { "pci-6071e",          STATUS_SOME,    cal_ni_pci_6071e,       ni_setup_observables, -1, -1 },
+       { "pxi-6071e",          STATUS_GUESS,   cal_ni_pxi_6071e,       ni_setup_observables, -1, -1 },
+       { "at-mio-16e-10",      STATUS_GUESS,   cal_ni_at_mio_16e_10,   ni_setup_observables, -1, -1 },
+       { "pci-mio-16xe-50",    STATUS_SOME,    cal_ni_pci_mio_16xe_50, ni_setup_observables, -1, -1 },
+       { "pci-6023e",          STATUS_DONE,    cal_ni_pci_6023e,       ni_setup_observables, -1, -1 },
+       { "pci-mio-16xe-10",    STATUS_DONE,    cal_ni_pci_mio_16xe_10, ni_setup_observables, -1, -1 },
+       { "pci-6052e",          STATUS_DONE,    cal_ni_pci_6052e,       ni_setup_observables, -1, -1 },
+       { "pci-6024e",          STATUS_SOME,    cal_ni_pci_6024e,       ni_setup_observables, -1, -1 },
+       { "pci-mio-16e-4",      STATUS_SOME,    cal_ni_pci_mio_16e_4,   ni_setup_observables, -1, -1 },
+       { "pci-6032e",          STATUS_DONE,    cal_ni_pci_6032e,       ni_setup_observables, -1, -1 },
+       { "DAQCard-ai-16e-4",   STATUS_DONE,    cal_ni_daqcard_ai_16e_4,        ni_setup_observables, -1, -1 },
+       { "pci-6110",   STATUS_DONE,    cal_ni_pci_611x,        ni_setup_observables_611x, -1, -1 },
+       { "pci-6111",   STATUS_DONE,    cal_ni_pci_611x,        ni_setup_observables_611x, -1, -1 },
+       { "DAQCard-6062E", STATUS_DONE, cal_ni_daqcard_6062e, ni_setup_observables, -1, -1 },
+       { "DAQCard-6024E",      STATUS_UNKNOWN, NULL, ni_setup_observables, -1, -1 },
 #if 0
 //     { "at-mio-16de-10",     cal_ni_unknown },
        { "at-mio-64e-3",       cal_ni_16e_1 },
@@ -125,6 +124,7 @@ static struct board_struct boards[]={
 };
 #define n_boards (sizeof(boards)/sizeof(boards[0]))
 
+static const int ni_num_observables = 12;
 enum observables{
        ni_zero_offset_low = 0,
        ni_zero_offset_high,
@@ -134,9 +134,26 @@ enum observables{
        ni_unip_reference_low,
        ni_ao0_zero_offset,
        ni_ao0_reference,
+       ni_ao0_linearity,
        ni_ao1_zero_offset,
        ni_ao1_reference,
+       ni_ao1_linearity,
 };
+static inline unsigned int ni_ao_zero_offset( unsigned int channel )
+{
+       if( channel ) return ni_ao1_zero_offset;
+       else return ni_ao0_zero_offset;
+}
+static inline unsigned int ni_ao_reference( unsigned int channel )
+{
+       if( channel ) return ni_ao1_reference;
+       else return ni_ao0_reference;
+}
+static inline unsigned int ni_ao_linearity( unsigned int channel )
+{
+       if( channel ) return ni_ao1_linearity;
+       else return ni_ao0_linearity;
+}
 
 enum observables_611x{
        ni_ao0_zero_offset_611x = 0,
@@ -144,10 +161,10 @@ enum observables_611x{
        ni_ao1_zero_offset_611x = 2,
        ni_ao1_reference_611x = 3,
 };
-inline static int ni_zero_offset_611x( int channel ) {
+static inline unsigned int ni_zero_offset_611x( unsigned int channel ) {
        return 4 + 2 * channel;
 };
-inline static int ni_reference_611x( int channel ) {
+static inline unsigned int ni_reference_611x( unsigned int channel ) {
        return 5 + 2 * channel;
 };
 
@@ -161,6 +178,16 @@ enum reference_sources {
        REF_DAC0_CALSRC = 6,
        REF_DAC1_CALSRC = 7,
 };
+static inline unsigned int REF_DAC_GND( unsigned int channel )
+{
+       if( channel ) return REF_DAC1_GND;
+       else return REF_DAC0_GND;
+}
+static inline unsigned int REF_DAC_CALSRC( unsigned int channel )
+{
+       if( channel ) return REF_DAC1_CALSRC;
+       else return REF_DAC0_CALSRC;
+}
 
 int ni_setup( calibration_setup_t *setup , const char *device_name )
 {
@@ -211,7 +238,7 @@ static void ni_setup_observables( calibration_setup_t *setup )
        tmpl.n = 1;
        tmpl.subdev = setup->ad_subdev;
 
-       setup->n_observables = 10;
+       setup->n_observables = ni_num_observables;
 
        /* 0 offset, low gain */
        o = setup->observables + ni_zero_offset_low;
@@ -274,65 +301,57 @@ static void ni_setup_observables( calibration_setup_t *setup )
 
        if(setup->da_subdev>=0){
                comedi_insn po_tmpl;
+               unsigned int channel;
 
                memset(&po_tmpl,0,sizeof(po_tmpl));
                po_tmpl.insn = INSN_WRITE;
                po_tmpl.n = 1;
                po_tmpl.subdev = setup->da_subdev;
 
-               /* ao 0, zero offset */
-               o = setup->observables + ni_ao0_zero_offset;
-               o->name = "ao 0, zero offset, low gain";
-               o->preobserve_insn = po_tmpl;
-               o->preobserve_insn.chanspec = CR_PACK(0,0,0);
-               o->preobserve_insn.data = o->preobserve_data;
-               o->observe_insn = tmpl;
-               o->observe_insn.chanspec =
-                       CR_PACK(REF_DAC0_GND,bipolar_lowgain,AREF_OTHER)
-                       | CR_ALT_SOURCE | CR_ALT_FILTER;
-               o->reference_source = REF_DAC0_GND;
-               set_target( setup, ni_ao0_zero_offset,0.0);
-
-               /* ao 0, gain */
-               o = setup->observables + ni_ao0_reference;
-               o->name = "ao 0, reference voltage, low gain";
-               o->preobserve_insn = po_tmpl;
-               o->preobserve_insn.chanspec = CR_PACK(0,0,0);
-               o->preobserve_insn.data = o->preobserve_data;
-               o->observe_insn = tmpl;
-               o->observe_insn.chanspec =
-                       CR_PACK(REF_DAC0_CALSRC,bipolar_lowgain,AREF_OTHER)
-                       | CR_ALT_SOURCE | CR_ALT_FILTER;
-               o->reference_source = REF_DAC0_CALSRC;
-               set_target( setup, ni_ao0_reference,5.0);
-               o->target -= voltage_reference;
-
-               /* ao 1, zero offset */
-               o = setup->observables + ni_ao1_zero_offset;
-               o->name = "ao 1, zero offset, low gain";
-               o->preobserve_insn = po_tmpl;
-               o->preobserve_insn.chanspec = CR_PACK(1,0,0);
-               o->preobserve_insn.data = o->preobserve_data;
-               o->observe_insn = tmpl;
-               o->observe_insn.chanspec =
-                       CR_PACK(REF_DAC1_GND,bipolar_lowgain,AREF_OTHER)
-                       | CR_ALT_SOURCE | CR_ALT_FILTER;
-               o->reference_source = REF_DAC1_GND;
-               set_target( setup, ni_ao1_zero_offset,0.0);
-
-               /* ao 1, gain */
-               o = setup->observables + ni_ao1_reference;
-               o->name = "ao 1, reference voltage, low gain";
-               o->preobserve_insn = po_tmpl;
-               o->preobserve_insn.chanspec = CR_PACK(1,0,0);
-               o->preobserve_insn.data = o->preobserve_data;
-               o->observe_insn = tmpl;
-               o->observe_insn.chanspec =
-                       CR_PACK(REF_DAC1_CALSRC,bipolar_lowgain,AREF_OTHER)
-                       | CR_ALT_SOURCE | CR_ALT_FILTER;
-               o->reference_source = REF_DAC1_CALSRC;
-               set_target( setup, ni_ao1_reference,5.0);
-               o->target -= voltage_reference;
+               for( channel = 0; channel < 2; channel++ )
+               {
+                       /* ao zero offset */
+                       o = setup->observables + ni_ao_zero_offset( channel );
+                       assert( o->name == NULL );
+                       asprintf( &o->name, "ao %i, zero offset, low gain", channel );
+                       o->preobserve_insn = po_tmpl;
+                       o->preobserve_insn.chanspec = CR_PACK(channel,0,0);
+                       o->preobserve_insn.data = o->preobserve_data;
+                       o->observe_insn = tmpl;
+                       o->observe_insn.chanspec =
+                               CR_PACK(REF_DAC_GND( channel ),bipolar_lowgain,AREF_OTHER)
+                               | CR_ALT_SOURCE | CR_ALT_FILTER;
+                       o->reference_source = REF_DAC_GND( channel );
+                       set_target( setup, ni_ao_zero_offset( channel ),0.0);
+
+                       /* ao gain */
+                       o = setup->observables + ni_ao_reference( channel );
+                       assert( o->name == NULL );
+                       asprintf( &o->name, "ao %i, refernce voltage, low gain", channel );
+                       o->preobserve_insn = po_tmpl;
+                       o->preobserve_insn.chanspec = CR_PACK(channel,0,0);
+                       o->preobserve_insn.data = o->preobserve_data;
+                       o->observe_insn = tmpl;
+                       o->observe_insn.chanspec =
+                               CR_PACK(REF_DAC_GND( channel ),bipolar_lowgain,AREF_OTHER)
+                               | CR_ALT_SOURCE | CR_ALT_FILTER;
+                       o->reference_source = REF_DAC_GND( channel );
+                       set_target( setup, ni_ao_reference( channel ),5.0);
+
+                       /* ao linearity, negative */
+                       o = setup->observables + ni_ao_linearity( channel );
+                       assert( o->name == NULL );
+                       asprintf( &o->name, "ao %i, linearity (negative), low gain", channel );
+                       o->preobserve_insn = po_tmpl;
+                       o->preobserve_insn.chanspec = CR_PACK(channel,0,0);
+                       o->preobserve_insn.data = o->preobserve_data;
+                       o->observe_insn = tmpl;
+                       o->observe_insn.chanspec =
+                               CR_PACK(REF_DAC_GND( channel ),bipolar_lowgain,AREF_OTHER)
+                               | CR_ALT_SOURCE | CR_ALT_FILTER;
+                       o->reference_source = REF_DAC_GND( channel );
+                       set_target( setup, ni_ao_linearity( channel ),-5.0);
+               }
        }
 }
 
@@ -840,24 +859,89 @@ static int cal_ni_pci_611x( calibration_setup_t *setup )
        return 0;
 }
 
+enum caldacs_dc6062e
+{
+       DAC1_LINEARITY_DC6062E = 1, /* not sure exactly what this does */
+       ADC_GAIN_DC6062E = 2,   /* couples strongly to offset in bipolar ranges */
+       ADC_POSTGAIN_OFFSET_DC6062E = 4,
+       DAC1_GAIN_DC6062E = 5,
+       DAC0_OFFSET_DC6062E = 6,
+       ADC_UNIPOLAR_OFFSET_DC6062E = 7,
+       ADC_PREGAIN_OFFSET_DC6062E = 8,
+       DAC1_OFFSET_DC6062E = 9,
+       DAC0_LINEARITY_DC6062E = 10,
+       DAC0_GAIN_DC6062E = 11,
+};
+static inline unsigned int DAC_OFFSET_DC6062E( unsigned int channel )
+{
+       if( channel ) return DAC1_OFFSET_DC6062E;
+       else return DAC0_OFFSET_DC6062E;
+}
+static inline unsigned int DAC_GAIN_DC6062E( unsigned int channel )
+{
+       if( channel ) return DAC1_GAIN_DC6062E;
+       else return DAC0_GAIN_DC6062E;
+}
+static inline unsigned int DAC_LINEARITY_DC6062E( unsigned int channel )
+{
+       if( channel ) return DAC1_LINEARITY_DC6062E;
+       else return DAC0_LINEARITY_DC6062E;
+}
+
+static void prep_adc_caldacs_dc6062e( calibration_setup_t *setup )
+{
+       int retval;
+
+       if( setup->do_reset )
+       {
+               reset_caldac( setup, ADC_PREGAIN_OFFSET_DC6062E );
+               reset_caldac( setup, ADC_POSTGAIN_OFFSET_DC6062E );
+               reset_caldac( setup, ADC_GAIN_DC6062E );
+               reset_caldac( setup, ADC_PREGAIN_OFFSET_DC6062E );
+       }else
+       {
+               retval = comedi_apply_calibration( setup->dev, setup->ad_subdev,
+                       0, 0, AREF_GROUND, setup->cal_save_file_path);
+               if( retval < 0 )
+               {
+                       DPRINT( 0, "Failed to apply existing calibration, reseting adc caldacs.\n" );
+                       reset_caldac( setup, ADC_PREGAIN_OFFSET_DC6062E );
+                       reset_caldac( setup, ADC_POSTGAIN_OFFSET_DC6062E );
+                       reset_caldac( setup, ADC_GAIN_DC6062E );
+                       reset_caldac( setup, ADC_PREGAIN_OFFSET_DC6062E );
+               }
+       }
+}
+
+static void prep_dac_caldacs_dc6062e( calibration_setup_t *setup,
+       unsigned int channel )
+{
+       int retval;
+
+       if( setup->do_reset )
+       {
+               reset_caldac( setup, DAC_OFFSET_DC6062E( channel ) );
+               reset_caldac( setup, DAC_GAIN_DC6062E( channel ) );
+               reset_caldac( setup, DAC_LINEARITY_DC6062E( channel ) );
+       }else
+       {
+               retval = comedi_apply_calibration( setup->dev, setup->da_subdev,
+                       channel, 0, AREF_GROUND, setup->cal_save_file_path);
+               if( retval < 0 )
+               {
+                       DPRINT( 0, "Failed to apply existing calibration, reseting dac caldacs.\n" );
+                       reset_caldac( setup, DAC_OFFSET_DC6062E( channel ) );
+                       reset_caldac( setup, DAC_GAIN_DC6062E( channel ) );
+                       reset_caldac( setup, DAC_LINEARITY_DC6062E( channel ) );
+               }
+       }
+}
+
 static int cal_ni_daqcard_6062e( calibration_setup_t *setup )
 {
        saved_calibration_t saved_cals[ 3 ], *current_cal;
        static const int num_calibrations = sizeof( saved_cals ) / sizeof( saved_cals[0] );
        int i, retval;
-       enum caldacs
-       {
-               DAC1_LINEARITY = 1, /* not sure exactly what this does */
-               ADC_GAIN = 2,   /* couples strongly to offset */
-               ADC_POSTGAIN_OFFSET = 4,
-               DAC1_GAIN = 5,
-               DAC0_OFFSET = 6,
-               ADC_UNIPOLAR_OFFSET = 7,
-               ADC_PREGAIN_OFFSET = 8,
-               DAC1_OFFSET = 9,
-               DAC0_LINEARITY = 10, /* not sure exactly what this does */
-               DAC0_GAIN = 11,
-       };
 
        comedi_set_global_oor_behavior( COMEDI_OOR_NUMBER );
 
@@ -865,45 +949,47 @@ static int cal_ni_daqcard_6062e( calibration_setup_t *setup )
 
        memset( saved_cals, 0, sizeof( saved_cals ) );
 
-       cal_postgain_binary( setup, ni_zero_offset_low, ni_reference_low, ADC_GAIN );
-       cal_postgain_binary( setup, ni_zero_offset_low, ni_zero_offset_high, ADC_POSTGAIN_OFFSET );
-       cal_binary( setup, ni_zero_offset_high, ADC_PREGAIN_OFFSET );
-       cal_binary( setup, ni_unip_zero_offset_high, ADC_UNIPOLAR_OFFSET );
+       prep_adc_caldacs_dc6062e( setup );
+
+       cal_postgain_binary( setup, ni_zero_offset_low, ni_reference_low, ADC_GAIN_DC6062E );
+       cal_postgain_binary( setup, ni_zero_offset_low, ni_zero_offset_high,
+               ADC_POSTGAIN_OFFSET_DC6062E );
+       cal_binary( setup, ni_zero_offset_high, ADC_PREGAIN_OFFSET_DC6062E );
+       cal_binary( setup, ni_unip_zero_offset_high, ADC_UNIPOLAR_OFFSET_DC6062E );
 
        current_cal->subdevice = setup->ad_subdev;
-       sc_push_caldac( current_cal, setup->caldacs[ ADC_PREGAIN_OFFSET ] );
-       sc_push_caldac( current_cal, setup->caldacs[ ADC_GAIN ] );
-       sc_push_caldac( current_cal, setup->caldacs[ ADC_POSTGAIN_OFFSET ] );
-       sc_push_caldac( current_cal, setup->caldacs[ ADC_UNIPOLAR_OFFSET ] );
+       sc_push_caldac( current_cal, setup->caldacs[ ADC_PREGAIN_OFFSET_DC6062E ] );
+       sc_push_caldac( current_cal, setup->caldacs[ ADC_GAIN_DC6062E ] );
+       sc_push_caldac( current_cal, setup->caldacs[ ADC_POSTGAIN_OFFSET_DC6062E ] );
+       sc_push_caldac( current_cal, setup->caldacs[ ADC_UNIPOLAR_OFFSET_DC6062E ] );
        sc_push_channel( current_cal, SC_ALL_CHANNELS );
        sc_push_range( current_cal, SC_ALL_RANGES );
        sc_push_aref( current_cal, SC_ALL_AREFS );
        current_cal++;
 
-       if(setup->do_output){
-               cal_binary( setup, ni_ao0_zero_offset, DAC0_OFFSET );
-               cal_binary( setup, ni_ao0_reference, DAC0_GAIN );
-
-               current_cal->subdevice = setup->da_subdev;
-               sc_push_caldac( current_cal, setup->caldacs[ DAC0_OFFSET ] );
-               sc_push_caldac( current_cal, setup->caldacs[ DAC0_GAIN ] );
-               sc_push_caldac( current_cal, setup->caldacs[ DAC0_LINEARITY ] );
-               sc_push_channel( current_cal, 0 );
-               sc_push_range( current_cal, SC_ALL_RANGES );
-               sc_push_aref( current_cal, SC_ALL_AREFS );
-               current_cal++;
-
-               cal_binary( setup, ni_ao1_zero_offset, DAC1_OFFSET );
-               cal_binary( setup, ni_ao1_reference, DAC1_GAIN );
-
-               current_cal->subdevice = setup->da_subdev;
-               sc_push_caldac( current_cal, setup->caldacs[ DAC1_OFFSET ] );
-               sc_push_caldac( current_cal, setup->caldacs[ DAC1_GAIN ] );
-               sc_push_caldac( current_cal, setup->caldacs[ DAC1_LINEARITY ] );
-               sc_push_channel( current_cal, 1 );
-               sc_push_range( current_cal, SC_ALL_RANGES );
-               sc_push_aref( current_cal, SC_ALL_AREFS );
-               current_cal++;
+       if(setup->do_output)
+       {
+               unsigned int channel;
+
+               for( channel = 0; channel < 2; channel++ )
+               {
+                       prep_dac_caldacs_dc6062e( setup, channel );
+
+                       cal_linearity_binary( setup, ni_ao_linearity( channel ),
+                               ni_ao_zero_offset( channel ), ni_ao_reference( channel ),
+                               DAC_LINEARITY_DC6062E( channel ) );
+                       cal_binary( setup, ni_ao_zero_offset( channel ), DAC_OFFSET_DC6062E( channel ) );
+                       cal_binary( setup, ni_ao_reference( channel ), DAC_GAIN_DC6062E( channel ) );
+
+                       current_cal->subdevice = setup->da_subdev;
+                       sc_push_caldac( current_cal, setup->caldacs[ DAC_OFFSET_DC6062E( channel ) ] );
+                       sc_push_caldac( current_cal, setup->caldacs[ DAC_GAIN_DC6062E( channel ) ] );
+                       sc_push_caldac( current_cal, setup->caldacs[ DAC_LINEARITY_DC6062E( channel ) ] );
+                       sc_push_channel( current_cal, channel );
+                       sc_push_range( current_cal, SC_ALL_RANGES );
+                       sc_push_aref( current_cal, SC_ALL_AREFS );
+                       current_cal++;
+               }
        }
 
        retval = write_calibration_file( setup, saved_cals, num_calibrations );
@@ -926,6 +1012,9 @@ static double ni_get_reference( calibration_setup_t *setup, int lsb_loc,int msb_
        uv = ( lsb & 0xff ) | ( ( msb << 8 ) & 0xff00 );
        ref=5.000+1.0e-6*uv;
        printf("ref=%g\n",ref);
+       if( fabs( ref - 5.0 ) > 0.005 )
+               printf( "WARNING: eeprom indicates reference is more than 5mV away\n"
+                       "from 5V.  Possible bad eeprom address?\n" );
 
        return ref;
 }