6713 is completely failing
[comedilib.git] / comedi_calibrate / ni.c
index 1cc88a96435f9ca31519cc344f77c2901acdf723..c028fbd0603bfc161d48124ba4c39eac6df7233d 100644 (file)
@@ -47,13 +47,16 @@ struct board_struct{
 static int ni_setup_board( calibration_setup_t *setup , const char *device_name );
 static void ni_setup_observables( calibration_setup_t *setup );
 static void ni_setup_observables_611x( calibration_setup_t *setup );
+static void ni67xx_setup_observables( calibration_setup_t *setup );
 
 static int cal_ni_at_mio_16e_2(calibration_setup_t *setup);
 static int cal_ni_daqcard_ai_16xe_50(calibration_setup_t *setup);
 static int cal_ni_at_mio_16e_1(calibration_setup_t *setup);
 static int cal_ni_pci_mio_16e_1(calibration_setup_t *setup);
+static int cal_ni_pci_6014(calibration_setup_t *setup);
 static int cal_ni_pci_6024e(calibration_setup_t *setup);
 static int cal_ni_pci_6025e(calibration_setup_t *setup);
+static int cal_ni_pci_6032e(calibration_setup_t *setup);
 static int cal_ni_pci_6035e(calibration_setup_t *setup);
 static int cal_ni_pci_6036e(calibration_setup_t *setup);
 static int cal_ni_pci_6071e(calibration_setup_t *setup);
@@ -64,60 +67,69 @@ static int cal_ni_pci_6023e(calibration_setup_t *setup);
 static int cal_ni_at_mio_16xe_50(calibration_setup_t *setup);
 static int cal_ni_pci_mio_16xe_10(calibration_setup_t *setup);
 static int cal_ni_pci_6052e(calibration_setup_t *setup);
-static int cal_ni_pci_6032e(calibration_setup_t *setup);
 static int cal_ni_daqcard_ai_16e_4(calibration_setup_t *setup);
 static int cal_ni_pci_611x(calibration_setup_t *setup);
 static int cal_ni_pci_mio_16e_4(calibration_setup_t *setup);
 static int cal_ni_daqcard_6062e(calibration_setup_t *setup);
 static int cal_ni_daqcard_6024e(calibration_setup_t *setup);
+static int cal_ni_daqcard_6036e(calibration_setup_t *setup);
+static int cal_ni_pci_6711(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-ai-16xe-10", STATUS_UNKNOWN, NULL, ni_setup_observables, 0x1b7, 0x1b8 },
+       { "at-mio-16de-10", STATUS_UNKNOWN, NULL, ni_setup_observables, 0x1a7, 0x1a8 },
+       { "at-mio-16e-1", STATUS_DONE, cal_ni_at_mio_16e_1, ni_setup_observables, 0x1a9, 0x1aa },
        { "at-mio-16e-2", STATUS_DONE, cal_ni_at_mio_16e_2, ni_setup_observables, 0x1a9, 0x1aa },
-       { "DAQCard-ai-16xe-50", STATUS_DONE, cal_ni_daqcard_ai_16xe_50, ni_setup_observables, 0x1be, 0x1bf },
+       { "at-mio-16e-10", STATUS_DONE, cal_ni_at_mio_16e_10, ni_setup_observables, 0x1a7, 0x1a8 },
+       { "at-mio-16xe-10", STATUS_UNKNOWN, NULL, ni_setup_observables, 0x1b7, 0x1b8 },
        { "at-mio-16xe-50", STATUS_SOME, cal_ni_at_mio_16xe_50, ni_setup_observables, 0x1b5, 0x1b6 },
-       { "at-mio-16e-1", STATUS_SOME, cal_ni_at_mio_16e_1, ni_setup_observables, 0x1a9, 0x1aa },
+       { "DAQCard-ai-16e-4", STATUS_DONE, cal_ni_daqcard_ai_16e_4, ni_setup_observables, 0x1b5, 0x1b6 },
+       { "DAQCard-ai-16xe-50", STATUS_DONE, cal_ni_daqcard_ai_16xe_50, ni_setup_observables, 0x1be, 0x1bf },
+       { "DAQCard-6024E", STATUS_SOME, cal_ni_daqcard_6024e, ni_setup_observables, -1, -1 },
+       { "DAQCard-6036E", STATUS_DONE, cal_ni_daqcard_6036e, ni_setup_observables, 0x1ab, 0x1ac },
+       { "DAQCard-6062E", STATUS_DONE, cal_ni_daqcard_6062e, ni_setup_observables, 0x1a9, 0x1aa },
        { "pci-mio-16e-1", STATUS_DONE, cal_ni_pci_mio_16e_1, ni_setup_observables, 0x1a9, 0x1aa },
-       { "pci-6025e", STATUS_SOME, cal_ni_pci_6025e, ni_setup_observables, 0x1af, 0x1b0 },
-       { "pci-6034e", STATUS_UNKNOWN, NULL, ni_setup_observables, -1, -1 },
-       { "pci-6035e", STATUS_DONE, cal_ni_pci_6035e, ni_setup_observables, 0x1af, 0x1b0 },
-       { "pci-6036e", STATUS_GUESS, cal_ni_pci_6036e, ni_setup_observables, -1, -1 },
-       { "pci-6071e", STATUS_SOME, cal_ni_pci_6071e, ni_setup_observables, 0x1a9, 0x1aa },
-       { "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, 0x1a7, 0x1a8 },
+       { "pci-mio-16e-4", STATUS_SOME, cal_ni_pci_mio_16e_4, ni_setup_observables, 0x1a9, 0x1aa },
+       { "pci-mio-16xe-10", STATUS_DONE,       cal_ni_pci_mio_16xe_10, ni_setup_observables, 0x1ae, 0x1af },
        { "pci-mio-16xe-50", STATUS_SOME, cal_ni_pci_mio_16xe_50, ni_setup_observables, 0x1b5, 0x1b6 },
+       { "pci-6014", STATUS_SOME, cal_ni_pci_6014, ni_setup_observables, 0x1ab, 0x1ac },
        { "pci-6023e", STATUS_DONE, cal_ni_pci_6023e, ni_setup_observables, 0x1bb, 0x1bc },
-       { "pci-mio-16xe-10", STATUS_DONE,       cal_ni_pci_mio_16xe_10, ni_setup_observables, 0x1ae, 0x1af },
-       { "pci-6052e", STATUS_DONE, cal_ni_pci_6052e, ni_setup_observables, 0x19f, 0x1a0 },
        { "pci-6024e", STATUS_SOME, cal_ni_pci_6024e, ni_setup_observables, 0x1af, 0x1b0 },
-       { "pci-mio-16e-4", STATUS_SOME, cal_ni_pci_mio_16e_4, ni_setup_observables, 0x1a9, 0x1aa },
+       { "pci-6025e", STATUS_SOME, cal_ni_pci_6025e, ni_setup_observables, 0x1af, 0x1b0 },
+       { "pci-6031e", STATUS_DONE, cal_ni_pci_mio_16xe_10, ni_setup_observables, 0x1ae, 0x1af },
        { "pci-6032e", STATUS_DONE, cal_ni_pci_6032e, ni_setup_observables, 0x1ae, 0x1af },
-       { "DAQCard-ai-16e-4", STATUS_DONE, cal_ni_daqcard_ai_16e_4, ni_setup_observables, 0x1b5, 0x1b6 },
+       { "pci-6033e", STATUS_DONE, cal_ni_pci_6032e, ni_setup_observables, 0x1b7, 0x1b8 },
+       { "pci-6034e", STATUS_UNKNOWN, NULL, ni_setup_observables, -1, -1 },
+       { "pci-6035e", STATUS_DONE, cal_ni_pci_6035e, ni_setup_observables, 0x1af, 0x1b0 },
+       { "pci-6036e", STATUS_DONE, cal_ni_pci_6036e, ni_setup_observables, 0x1ab, 0x1ac },
+       { "pci-6052e", STATUS_DONE, cal_ni_pci_6052e, ni_setup_observables, 0x19f, 0x1a0 },
+       { "pci-6071e", STATUS_DONE, cal_ni_pci_6071e, ni_setup_observables, 0x1a9, 0x1aa },
        { "pci-6110", STATUS_DONE, cal_ni_pci_611x, ni_setup_observables_611x, 0x1d4, 0x1d5 },
        { "pci-6111", STATUS_DONE, cal_ni_pci_611x, ni_setup_observables_611x, 0x1d4, 0x1d5 },
-       { "DAQCard-6062E", STATUS_DONE, cal_ni_daqcard_6062e, ni_setup_observables, 0x1a9, 0x1aa },
-       { "DAQCard-6024E", STATUS_SOME, cal_ni_daqcard_6024e, ni_setup_observables, -1, -1 },
-       { "at-mio-16de-10", STATUS_UNKNOWN, NULL, ni_setup_observables, 0x1a7, 0x1a8 },
-       { "at-mio-16xe-10", STATUS_UNKNOWN, NULL, ni_setup_observables, 0x1b7, 0x1b8 },
-       { "at-ai-16xe-10", STATUS_UNKNOWN, NULL, ni_setup_observables, 0x1b7, 0x1b8 },
-       { "pci-6031e", STATUS_UNKNOWN, NULL, ni_setup_observables, 0x1ae, 0x1af },
-       { "pci-6033e", STATUS_UNKNOWN, NULL, ni_setup_observables, 0x1ae, 0x1af },
+       { "pxi-6025e", STATUS_UNKNOWN, NULL, ni_setup_observables, -1, -1 },
+       { "pxi-6030e", STATUS_UNKNOWN, NULL, ni_setup_observables, -1, -1 },
+       { "pxi-6031e", STATUS_UNKNOWN, NULL, ni_setup_observables, -1, -1 },
+       { "pxi-6040e", STATUS_UNKNOWN, NULL, ni_setup_observables, -1, -1 },
+       { "pxi-6052e", STATUS_UNKNOWN, NULL, ni_setup_observables, -1, -1 },
+       { "pxi-6070e", STATUS_UNKNOWN, NULL, ni_setup_observables, -1, -1 },
+       { "pxi-6071e", STATUS_GUESS, cal_ni_pxi_6071e, ni_setup_observables, -1, -1 },
+       { "pci-6711", STATUS_DONE, cal_ni_pci_6711, ni67xx_setup_observables, 0x1d4, 0x1d5},
+       { "pci-6713", STATUS_GUESS, cal_ni_pci_6711, ni67xx_setup_observables, -1, -1},
+       { "pci-6731", STATUS_GUESS, cal_ni_pci_6711, ni67xx_setup_observables, -1, -1},
+       { "pci-6733", STATUS_GUESS, cal_ni_pci_6711, ni67xx_setup_observables, -1, -1},
+       { "pxi-6711", STATUS_GUESS, cal_ni_pci_6711, ni67xx_setup_observables, -1, -1},
+       { "pxi-6713", STATUS_GUESS, cal_ni_pci_6711, ni67xx_setup_observables, -1, -1},
+       { "pxi-6731", STATUS_GUESS, cal_ni_pci_6711, ni67xx_setup_observables, -1, -1},
+       { "pxi-6733", STATUS_GUESS, cal_ni_pci_6711, ni67xx_setup_observables, -1, -1},
 #if 0
        { "at-mio-64e-3",       cal_ni_16e_1 },
-//     { "at-mio-16xe-50",     cal_ni_unknown },
-//     { "pxi-6030e",          cal_ni_unknown },
-//     { "pxi-6040e",          cal_ni_unknown },
-       { "pxi-6025e",          cal_ni_6023e }, // guess
-//     { "pci-6711",           cal_ni_unknown },
-//     { "pci-6713",           cal_ni_unknown },
-//     { "pxi-6070e",          cal_ni_unknown },
-//     { "pxi-6052e",          cal_ni_unknown },
 #endif
 };
 #define n_boards (sizeof(boards)/sizeof(boards[0]))
 
-static const int ni_num_observables = 18;
+static const int ni_num_observables = 20;
 enum observables{
        ni_zero_offset_low = 0,
        ni_zero_offset_high,
@@ -133,10 +145,12 @@ enum observables{
        ni_ao1_linearity,
        ni_ao0_unip_zero_offset,
        ni_ao0_unip_reference,
-       ni_ao0_unip_linearity,
+       ni_ao0_unip_low_linearity,
+       ni_ao0_unip_mid_linearity,
        ni_ao1_unip_zero_offset,
        ni_ao1_unip_reference,
-       ni_ao1_unip_linearity,
+       ni_ao1_unip_low_linearity,
+       ni_ao1_unip_mid_linearity,
 };
 static inline unsigned int ni_ao_zero_offset( unsigned int channel )
 {
@@ -148,7 +162,7 @@ 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 )
+static inline unsigned int ni_ao_mid_linearity( unsigned int channel )
 {
        if( channel ) return ni_ao1_linearity;
        else return ni_ao0_linearity;
@@ -163,10 +177,15 @@ static inline unsigned int ni_ao_unip_reference( unsigned int channel )
        if( channel ) return ni_ao1_unip_reference;
        else return ni_ao0_unip_reference;
 }
-static inline unsigned int ni_ao_unip_linearity( unsigned int channel )
+static inline unsigned int ni_ao_unip_low_linearity( unsigned int channel )
+{
+       if( channel ) return ni_ao1_unip_low_linearity;
+       else return ni_ao0_unip_low_linearity;
+}
+static inline unsigned int ni_ao_unip_mid_linearity( unsigned int channel )
 {
-       if( channel ) return ni_ao1_unip_linearity;
-       else return ni_ao0_unip_linearity;
+       if( channel ) return ni_ao1_unip_mid_linearity;
+       else return ni_ao0_unip_mid_linearity;
 }
 
 static const int num_ao_observables_611x = 4;
@@ -224,7 +243,9 @@ typedef struct
        int adc_postgain_offset_fine;
        int adc_gain_fine;
        int adc_unip_offset;
+       int adc_unip_offset_fine;
        int dac_offset[ 2 ];
+       int dac_offset_fine[ 2 ];
        int dac_gain[ 2 ];
        int dac_gain_fine[ 2 ];
        int dac_linearity[ 2 ];
@@ -241,12 +262,14 @@ static inline void init_ni_caldac_layout( ni_caldac_layout_t *layout )
        layout->adc_postgain_offset = -1;
        layout->adc_gain = -1;
        layout->adc_unip_offset = -1;
+       layout->adc_unip_offset_fine = -1;
        layout->adc_pregain_offset_fine = -1;
        layout->adc_postgain_offset_fine = -1;
        layout->adc_gain_fine = -1;
        for( i = 0; i < 2; i++ )
        {
                layout->dac_offset[ i ] = -1;
+               layout->dac_offset_fine[ i ] = -1;
                layout->dac_gain[ i ] = -1;
                layout->dac_gain_fine[ i ] = -1;
                layout->dac_linearity[ i ] = -1;
@@ -291,7 +314,9 @@ static void ni_setup_ao_observables( calibration_setup_t *setup )
        int ao_unipolar_lowgain;
 
        ai_bipolar_lowgain = get_bipolar_lowgain( setup->dev, setup->ad_subdev);
+       assert(ai_bipolar_lowgain >= 0);
        ao_bipolar_lowgain = get_bipolar_lowgain( setup->dev, setup->da_subdev);
+       assert(ao_bipolar_lowgain >= 0);
        ao_unipolar_lowgain = get_unipolar_lowgain( setup->dev, setup->da_subdev);
 
        memset(&tmpl,0,sizeof(tmpl));
@@ -332,12 +357,12 @@ static void ni_setup_ao_observables( calibration_setup_t *setup )
                        CR_PACK(REF_DAC_GND( channel ),ai_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);
+               set_target( setup, ni_ao_reference( channel ),8.0);
 
-               /* ao linearity, negative */
-               o = setup->observables + ni_ao_linearity( channel );
+               /* ao linearity, mid */
+               o = setup->observables + ni_ao_mid_linearity( channel );
                assert( o->name == NULL );
-               asprintf( &o->name, "ao %i, linearity (negative), low gain", channel );
+               asprintf( &o->name, "ao %i, linearity (mid), low gain", channel );
                o->preobserve_insn = po_tmpl;
                o->preobserve_insn.chanspec = CR_PACK(channel,ao_bipolar_lowgain,0);
                o->preobserve_insn.data = o->preobserve_data;
@@ -346,7 +371,7 @@ static void ni_setup_ao_observables( calibration_setup_t *setup )
                        CR_PACK(REF_DAC_GND( channel ),ai_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);
+               set_target( setup, ni_ao_mid_linearity( channel ),4.0);
 
                if( ao_unipolar_lowgain >= 0 )
                {
@@ -362,7 +387,7 @@ static void ni_setup_ao_observables( calibration_setup_t *setup )
                                CR_PACK(REF_DAC_GND( channel ),ai_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);
+                       set_target( setup, ni_ao_unip_zero_offset( channel ),0.0);
 
                        /* ao unipolar gain */
                        o = setup->observables + ni_ao_unip_reference( channel );
@@ -376,10 +401,10 @@ static void ni_setup_ao_observables( calibration_setup_t *setup )
                                CR_PACK(REF_DAC_GND( channel ),ai_bipolar_lowgain,AREF_OTHER)
                                | CR_ALT_SOURCE | CR_ALT_FILTER;
                        o->reference_source = REF_DAC_GND( channel );
-                       set_target( setup, ni_ao_reference( channel ),8.0);
+                       set_target( setup, ni_ao_unip_reference( channel ), 9.0);
 
-                       /* ao unipolar linearity, negative */
-                       o = setup->observables + ni_ao_unip_linearity( channel );
+                       /* ao unipolar linearity, mid */
+                       o = setup->observables + ni_ao_unip_mid_linearity( channel );
                        assert( o->name == NULL );
                        asprintf( &o->name, "ao %i, unipolar linearity (mid), low gain", channel );
                        o->preobserve_insn = po_tmpl;
@@ -390,7 +415,21 @@ static void ni_setup_ao_observables( calibration_setup_t *setup )
                                CR_PACK(REF_DAC_GND( channel ),ai_bipolar_lowgain,AREF_OTHER)
                                | CR_ALT_SOURCE | CR_ALT_FILTER;
                        o->reference_source = REF_DAC_GND( channel );
-                       set_target( setup, ni_ao_linearity( channel ),4.0);
+                       set_target( setup, ni_ao_unip_mid_linearity( channel ), 5.0);
+
+                       /* ao unipolar linearity, low */
+                       o = setup->observables + ni_ao_unip_low_linearity( channel );
+                       assert( o->name == NULL );
+                       asprintf( &o->name, "ao %i, unipolar linearity (low), low gain", channel );
+                       o->preobserve_insn = po_tmpl;
+                       o->preobserve_insn.chanspec = CR_PACK(channel,ao_unipolar_lowgain,0);
+                       o->preobserve_insn.data = o->preobserve_data;
+                       o->observe_insn = tmpl;
+                       o->observe_insn.chanspec =
+                               CR_PACK(REF_DAC_GND( channel ),ai_bipolar_lowgain,AREF_OTHER)
+                               | CR_ALT_SOURCE | CR_ALT_FILTER;
+                       o->reference_source = REF_DAC_GND( channel );
+                       set_target( setup, ni_ao_unip_low_linearity( channel ), 1.0);
                }
        }
 }
@@ -624,23 +663,6 @@ static void ni_setup_observables_611x( calibration_setup_t *setup )
        setup->n_observables = num_ao_observables_611x + 2 * num_ai_ranges * num_ai_channels;
 }
 
-static int cal_ni_at_mio_16e_2(calibration_setup_t *setup)
-{
-       ni_caldac_layout_t layout;
-
-       init_ni_caldac_layout( &layout );
-       layout.adc_pregain_offset = 0;
-       layout.adc_postgain_offset = 1;
-       layout.adc_gain = 3;
-       layout.adc_unip_offset = 2;
-       layout.dac_offset[ 0 ] = 5;
-       layout.dac_gain[ 0 ] = 6;
-       layout.dac_offset[ 1 ] = 8;
-       layout.dac_gain[ 1 ] = 9;
-
-       return cal_ni_generic( setup, &layout );
-}
-
 static int cal_ni_daqcard_ai_16xe_50(calibration_setup_t *setup)
 {
        ni_caldac_layout_t layout;
@@ -691,7 +713,26 @@ static int cal_ni_pci_mio_16xe_10(calibration_setup_t *setup)
 
 static int cal_ni_at_mio_16e_1(calibration_setup_t *setup)
 {
-       return cal_ni_at_mio_16e_2( setup );
+       ni_caldac_layout_t layout;
+
+       init_ni_caldac_layout( &layout );
+       layout.adc_pregain_offset = 0;
+       layout.adc_postgain_offset = 1;
+       layout.adc_gain = 3;
+       layout.adc_unip_offset = 2;
+       layout.dac_offset[0] = 5;
+       layout.dac_gain[0] = 6;
+       layout.dac_linearity[0] = 4;
+       layout.dac_offset[1] = 8;
+       layout.dac_gain[1] = 9;
+       layout.dac_linearity[1] = 7;
+
+       return cal_ni_generic( setup, &layout );
+}
+
+static int cal_ni_at_mio_16e_2(calibration_setup_t *setup)
+{
+       return cal_ni_at_mio_16e_1(setup);
 }
 
 static int cal_ni_pci_mio_16e_1(calibration_setup_t *setup)
@@ -713,6 +754,26 @@ static int cal_ni_pci_mio_16e_1(calibration_setup_t *setup)
        return cal_ni_generic( setup, &layout );
 }
 
+static int cal_ni_pci_6014(calibration_setup_t *setup)
+{
+       ni_caldac_layout_t layout;
+
+       init_ni_caldac_layout( &layout );
+       layout.adc_pregain_offset = 0;
+       layout.adc_postgain_offset = 4;
+       layout.adc_pregain_offset_fine = 8;
+       layout.adc_gain = 2;
+       layout.dac_offset[0] = 6;
+       layout.dac_offset_fine[0] = 10;
+       layout.dac_gain[0] = 7;
+       layout.dac_gain_fine[0] = 11;
+       layout.dac_offset[1] = 9;
+       layout.dac_offset_fine[1] = 1;
+       layout.dac_gain[1] = 3;
+       layout.dac_gain_fine[1] = 5;
+       return cal_ni_generic( setup, &layout );
+}
+
 static int cal_ni_pci_6032e(calibration_setup_t *setup)
 {
        ni_caldac_layout_t layout;
@@ -786,9 +847,9 @@ static int cal_ni_pci_6071e(calibration_setup_t *setup)
        }
 
        init_ni_caldac_layout( &layout );
-       layout.adc_pregain_offset = 0;
-       layout.adc_pregain_offset_fine = 8;
+       layout.adc_pregain_offset = 8;
        layout.adc_postgain_offset = 4;
+       layout.adc_unip_offset = 7;
        layout.adc_gain = 2;
        layout.dac_offset[ 0 ] = 6;
        layout.dac_gain[ 0 ] = 11;
@@ -825,20 +886,25 @@ static int cal_ni_pxi_6071e(calibration_setup_t *setup)
 
 static int cal_ni_at_mio_16e_10(calibration_setup_t *setup)
 {
-       // 16e-10 (old)
        ni_caldac_layout_t layout;
 
+       if(comedi_get_version_code(setup->dev) <= COMEDI_VERSION_CODE(0, 7, 68))
+       {
+               DPRINT(0, "WARNING: you need comedi driver version 0.7.69 or later\n"
+                "for this calibration to work properly\n" );
+       }
        init_ni_caldac_layout( &layout );
-       layout.adc_pregain_offset = 10;
-       layout.adc_pregain_offset_fine = 0;
-       layout.adc_postgain_offset = 1;
-       layout.adc_gain = 3;
-       layout.adc_unip_offset = 2;
-       layout.dac_offset[ 0 ] = 5; /* guess */
-       layout.dac_gain[ 0 ] = 6; /* guess */
-       layout.dac_offset[ 1 ] = 8; /* guess */
-       layout.dac_gain[ 1 ] = 9; /* guess */
-
+       layout.adc_pregain_offset = 0;
+       layout.adc_pregain_offset_fine = 8;
+       layout.adc_postgain_offset = 4;
+       layout.adc_gain = 2;
+       layout.adc_unip_offset = 7;
+       layout.dac_offset[ 0 ] = 6;
+       layout.dac_gain[ 0 ] = 11; 
+       layout.dac_linearity[ 0 ] = 10;
+       layout.dac_offset[ 1 ] = 9;
+       layout.dac_gain[ 1 ] = 5;
+       layout.dac_linearity[1] = 1;
        return cal_ni_generic( setup, &layout );
 }
 
@@ -879,9 +945,9 @@ static int cal_ni_pci_6024e(calibration_setup_t *setup)
        ni_caldac_layout_t layout;
 
        init_ni_caldac_layout( &layout );
-       layout.adc_pregain_offset = 8;
+       layout.adc_pregain_offset = 0;
        layout.adc_postgain_offset = 4;
-       layout.adc_pregain_offset_fine = 0;
+       layout.adc_pregain_offset_fine = 8;
        layout.adc_gain = 2;
        layout.dac_offset[ 0 ] = 6;
        layout.dac_gain[ 0 ] = 11;
@@ -898,9 +964,9 @@ static int cal_ni_pci_6025e(calibration_setup_t *setup)
        ni_caldac_layout_t layout;
 
        init_ni_caldac_layout( &layout );
-       layout.adc_pregain_offset = 8;
+       layout.adc_pregain_offset = 0;
        layout.adc_postgain_offset = 4;
-       layout.adc_pregain_offset_fine = 0;
+       layout.adc_pregain_offset_fine = 8;
        layout.adc_gain = 2;
        layout.dac_offset[ 0 ] = 6;
        layout.dac_gain[ 0 ] = 11;
@@ -961,20 +1027,14 @@ static int cal_ni_pci_6052e(calibration_setup_t *setup)
        layout.adc_postgain_offset = 2;
        layout.adc_gain = 4;
        layout.adc_unip_offset = 6;
+       layout.adc_unip_offset_fine = 7;
        layout.adc_pregain_offset_fine = 1;
        layout.adc_postgain_offset_fine = 3;
        layout.adc_gain_fine = 5;
-#if 1
-/* this seems broken, i think we need to change
- * second caldac in driver to ad8804_debug */
-       layout.dac_offset[ 0 ] = 12 + 11;
-       layout.dac_gain[ 0 ] = 12 + 7;
-       layout.dac_gain_fine[ 0 ] = 12 + 3;
-       layout.dac_offset[ 1 ] = 12 + 1;
-       layout.dac_gain[ 1 ] = 12 + 9;
-       layout.dac_gain_fine[ 1 ] = 12 + 5;
-#else
-/* this should work if the first two caldacs were ad8804_debug */
+
+       DPRINT(0, "WARNING: you need comedi driver version 0.7.67 or later\n"
+        "for this calibration to work properly\n" );
+/* this works when the first two caldacs are ad8804_debug */
        layout.dac_offset[ 0 ] = 16 + 3;
        layout.dac_gain[ 0 ] = 16 + 1;
        layout.dac_gain_fine[ 0 ] = 16 + 2;
@@ -983,7 +1043,7 @@ static int cal_ni_pci_6052e(calibration_setup_t *setup)
        layout.dac_gain[ 1 ] = 16 + 5;
        layout.dac_gain_fine[ 1 ] = 16 + 6;
        layout.dac_linearity[ 1 ] = 16 + 4;
-#endif
+
        return cal_ni_generic( setup, &layout );
 }
 
@@ -1096,8 +1156,38 @@ static int cal_ni_daqcard_6024e( calibration_setup_t *setup )
        return cal_ni_generic( setup, &layout );
 }
 
+static int cal_ni_daqcard_6036e( calibration_setup_t *setup )
+{
+       ni_caldac_layout_t layout;
+
+       if( comedi_get_version_code( setup->dev ) <= COMEDI_VERSION_CODE( 0, 7, 68 ) )
+       {
+               DPRINT(0, "WARNING: you need comedi driver version 0.7.69 or later\n"
+                "for this calibration to work properly\n" );
+       }
+
+       init_ni_caldac_layout( &layout );
+
+       layout.adc_pregain_offset = 0;
+       layout.adc_pregain_offset_fine = 8;
+       layout.adc_postgain_offset = 4;
+       layout.adc_gain = 2;
+
+       layout.dac_offset[0] = 6;
+       layout.dac_gain[0] = 7;
+       layout.dac_gain_fine[ 0 ] = 11;
+       layout.dac_linearity[0] = 10;
+
+       layout.dac_offset[ 1 ] = 9;
+       layout.dac_gain[ 1 ] = 3;
+       layout.dac_gain_fine[ 1 ] = 5;
+       layout.dac_linearity[1] = 1;
+
+       return cal_ni_generic( setup, &layout );
+}
+
 static void prep_adc_caldacs_generic( calibration_setup_t *setup,
-       const ni_caldac_layout_t *layout )
+       const ni_caldac_layout_t *layout, unsigned int range )
 {
        int retval;
 
@@ -1110,10 +1200,11 @@ static void prep_adc_caldacs_generic( calibration_setup_t *setup,
                reset_caldac( setup, layout->adc_postgain_offset_fine );
                reset_caldac( setup, layout->adc_gain_fine );
                reset_caldac( setup, layout->adc_unip_offset );
+               reset_caldac( setup, layout->adc_unip_offset_fine );
        }else
        {
                retval = comedi_apply_parsed_calibration( setup->dev, setup->ad_subdev,
-                       0, 0, AREF_GROUND, setup->old_calibration );
+                       0, range, AREF_GROUND, setup->old_calibration );
                if( retval < 0 )
                {
                        DPRINT( 0, "Failed to apply existing calibration, reseting adc caldacs.\n" );
@@ -1124,6 +1215,7 @@ static void prep_adc_caldacs_generic( calibration_setup_t *setup,
                        reset_caldac( setup, layout->adc_postgain_offset_fine );
                        reset_caldac( setup, layout->adc_gain_fine );
                        reset_caldac( setup, layout->adc_unip_offset );
+                       reset_caldac( setup, layout->adc_unip_offset_fine );
                }
        }
 }
@@ -1138,6 +1230,7 @@ static void prep_dac_caldacs_generic( calibration_setup_t *setup,
        if( setup->old_calibration == NULL )
        {
                reset_caldac( setup, layout->dac_offset[ channel ] );
+               reset_caldac( setup, layout->dac_offset_fine[ channel ] );
                reset_caldac( setup, layout->dac_gain[ channel ] );
                reset_caldac( setup, layout->dac_gain_fine[ channel ] );
                reset_caldac( setup, layout->dac_linearity[ channel ] );
@@ -1149,6 +1242,7 @@ static void prep_dac_caldacs_generic( calibration_setup_t *setup,
                {
                        DPRINT( 0, "Failed to apply existing calibration, reseting dac caldacs.\n" );
                        reset_caldac( setup, layout->dac_offset[ channel ] );
+                       reset_caldac( setup, layout->dac_offset_fine[ channel ] );
                        reset_caldac( setup, layout->dac_gain[ channel ] );
                        reset_caldac( setup, layout->dac_gain_fine[ channel ] );
                        reset_caldac( setup, layout->dac_linearity[ channel ] );
@@ -1156,31 +1250,108 @@ static void prep_dac_caldacs_generic( calibration_setup_t *setup,
        }
 }
 
+static void prep_adc_for_dac( calibration_setup_t *setup, int observable )
+{
+       unsigned int adc_range;
+       int chanspec;
+
+       if( observable < 0 ) return;
+
+       chanspec = setup->observables[ observable ].observe_insn.chanspec;
+       adc_range = CR_RANGE( chanspec );
+
+       comedi_apply_parsed_calibration( setup->dev, setup->ad_subdev,
+               0, adc_range, 0, setup->new_calibration );
+}
+
 static int cal_ni_generic( calibration_setup_t *setup, const ni_caldac_layout_t *layout )
 {
        comedi_calibration_setting_t *current_cal;
        int retval;
+       int num_ai_ranges;
+       int range;
+       int ai_unipolar_lowgain, ai_bipolar_lowgain;
+
+       num_ai_ranges = comedi_get_n_ranges( setup->dev, setup->ad_subdev, 0 );
+       assert( num_ai_ranges > 0 );
 
-       prep_adc_caldacs_generic( setup, layout );
+       ai_bipolar_lowgain = get_bipolar_lowgain( setup->dev, setup->ad_subdev );
+       ai_unipolar_lowgain = get_unipolar_lowgain( setup->dev, setup->ad_subdev );
+
+       prep_adc_caldacs_generic( setup, layout, ai_bipolar_lowgain );
 
        current_cal = sc_alloc_calibration_setting( setup );
        current_cal->subdevice = setup->ad_subdev;
+       reset_caldac( setup, layout->adc_gain_fine );
        generic_do_relative( setup, current_cal, ni_zero_offset_low,
                ni_reference_low, layout->adc_gain );
+       reset_caldac( setup, layout->adc_postgain_offset_fine );
        generic_do_relative( setup, current_cal, ni_zero_offset_low,
                ni_zero_offset_high, layout->adc_postgain_offset );
+       generic_do_relative( setup, current_cal, ni_zero_offset_low,
+               ni_zero_offset_high, layout->adc_postgain_offset_fine );
+       reset_caldac( setup, layout->adc_pregain_offset_fine );
        generic_do_cal( setup, current_cal, ni_zero_offset_high, layout->adc_pregain_offset );
        generic_do_relative( setup, current_cal, ni_zero_offset_low,
                ni_reference_low, layout->adc_gain_fine );
-       generic_do_relative( setup, current_cal, ni_zero_offset_low,
-               ni_zero_offset_high, layout->adc_postgain_offset_fine );
        generic_do_cal( setup, current_cal, ni_zero_offset_high,
                layout->adc_pregain_offset_fine );
-       generic_do_cal( setup, current_cal, ni_unip_zero_offset_high, layout->adc_unip_offset );
        sc_push_channel( current_cal, SC_ALL_CHANNELS );
-       sc_push_range( current_cal, SC_ALL_RANGES );
        sc_push_aref( current_cal, SC_ALL_AREFS );
+       if( layout->adc_unip_offset >= 0 )
+       {
+               sc_push_range( current_cal, SC_ALL_RANGES );
+       }else
+       {
+               for( range = 0; range < num_ai_ranges; range++ )
+               {
+                       if( is_bipolar( setup->dev, setup->ad_subdev, 0, range ) )
+                               sc_push_range( current_cal, range );
+               }
+       }
 
+       /* do seperate unipolar calibration if appropriate */
+       if( ai_unipolar_lowgain >= 0 )
+       {
+               current_cal = sc_alloc_calibration_setting( setup );
+               current_cal->subdevice = setup->ad_subdev;
+               if( layout->adc_unip_offset >= 0 )
+               {
+                       reset_caldac( setup, layout->adc_unip_offset_fine );
+                       generic_do_cal( setup, current_cal, ni_unip_zero_offset_high,
+                               layout->adc_unip_offset );
+                       generic_do_cal( setup, current_cal, ni_unip_zero_offset_high,
+                               layout->adc_unip_offset_fine );
+               /* if we don't have a unipolar offset caldac, do a fully
+                * independent calibration for unipolar ranges */
+               }else
+               {
+                       prep_adc_caldacs_generic( setup, layout, ai_unipolar_lowgain );
+                       generic_peg( setup, ni_unip_zero_offset_low,
+                               layout->adc_pregain_offset, 1 );
+                       generic_peg( setup, ni_unip_zero_offset_low,
+                               layout->adc_postgain_offset, 1 );
+                       generic_do_relative( setup, current_cal, ni_unip_zero_offset_low,
+                               ni_unip_reference_low, layout->adc_gain );
+                       generic_do_relative( setup, current_cal, ni_unip_zero_offset_low,
+                               ni_unip_zero_offset_high, layout->adc_postgain_offset );
+                       generic_do_relative( setup, current_cal, ni_unip_zero_offset_low,
+                               ni_unip_zero_offset_high, layout->adc_postgain_offset_fine );
+                       generic_do_cal( setup, current_cal, ni_unip_zero_offset_high,
+                               layout->adc_pregain_offset );
+                       generic_do_relative( setup, current_cal, ni_unip_zero_offset_low,
+                               ni_unip_reference_low, layout->adc_gain_fine );
+                       generic_do_cal( setup, current_cal, ni_unip_zero_offset_high,
+                               layout->adc_pregain_offset_fine );
+               }
+               for( range = 0; range < num_ai_ranges; range++ )
+               {
+                       if( is_unipolar( setup->dev, setup->ad_subdev, 0, range ) )
+                               sc_push_range( current_cal, range );
+               }
+               sc_push_channel( current_cal, SC_ALL_CHANNELS );
+               sc_push_aref( current_cal, SC_ALL_AREFS );
+       }
        if( setup->da_subdev >= 0 && setup->do_output )
        {
                unsigned int channel, range;
@@ -1192,14 +1363,19 @@ static int cal_ni_generic( calibration_setup_t *setup, const ni_caldac_layout_t
                {
                        num_ao_ranges = comedi_get_n_ranges( setup->dev, setup->da_subdev, channel );
                        prep_dac_caldacs_generic( setup, layout, channel, ao_bipolar_lowgain );
+                       prep_adc_for_dac( setup, ni_ao_reference( channel ) );
 
                        current_cal = sc_alloc_calibration_setting( setup );
                        current_cal->subdevice = setup->da_subdev;
-                       generic_do_linearity( setup, current_cal, ni_ao_linearity( channel ),
-                               ni_ao_zero_offset( channel ), ni_ao_reference( channel ),
+                       generic_do_linearity( setup, current_cal, ni_ao_zero_offset( channel ),
+                               ni_ao_mid_linearity( channel ), ni_ao_reference( channel ),
                                layout->dac_linearity[ channel ] );
+                       reset_caldac(setup, layout->dac_offset_fine[channel]);
                        generic_do_cal( setup, current_cal, ni_ao_zero_offset( channel ),
                                layout->dac_offset[ channel ] );
+                       generic_do_cal( setup, current_cal, ni_ao_zero_offset( channel ),
+                               layout->dac_offset_fine[ channel ] );
+                       reset_caldac( setup, layout->dac_gain_fine[ channel ] );
                        generic_do_cal( setup, current_cal, ni_ao_reference( channel ),
                                layout->dac_gain[ channel ] );
                        generic_do_cal( setup, current_cal, ni_ao_reference( channel ),
@@ -1218,11 +1394,15 @@ static int cal_ni_generic( calibration_setup_t *setup, const ni_caldac_layout_t
 
                                current_cal = sc_alloc_calibration_setting( setup );
                                current_cal->subdevice = setup->da_subdev;
-                               generic_do_linearity( setup, current_cal, ni_ao_unip_zero_offset( channel ),
-                                       ni_ao_unip_linearity( channel ), ni_ao_unip_reference( channel ),
+                               generic_do_linearity( setup, current_cal, ni_ao_unip_low_linearity( channel ),
+                                       ni_ao_unip_mid_linearity( channel ), ni_ao_unip_reference( channel ),
                                        layout->dac_linearity[ channel ] );
+                               reset_caldac( setup, layout->dac_offset_fine[ channel ] );
                                generic_do_cal( setup, current_cal, ni_ao_unip_zero_offset( channel),
                                        layout->dac_offset[ channel ] );
+                               generic_do_cal( setup, current_cal, ni_ao_unip_zero_offset( channel),
+                                       layout->dac_offset_fine[ channel ] );
+                               reset_caldac( setup, layout->dac_gain_fine[ channel ] );
                                generic_do_cal( setup, current_cal, ni_ao_unip_reference( channel ),
                                        layout->dac_gain[ channel ] );
                                generic_do_cal( setup, current_cal, ni_ao_unip_reference( channel ),
@@ -1264,3 +1444,199 @@ static double ni_get_reference( calibration_setup_t *setup, int lsb_loc,int msb_
        return ref;
 }
 
+/****************
+ NI 671x and 673x support
+ **************/
+
+static const int channels_per_ad8804 = 16;
+
+static inline int ni67xx_ao_gain_caldac(unsigned int ao_channel)
+{
+       int ad8804_gain_channels[4] = {8, 2, 11, 5};
+       int caldac_channel = ad8804_gain_channels[ao_channel % 4];
+       int caldac_index = ao_channel / 4;
+       /* just guessing that second ad8804 is works for ao channels 4-7
+        * the same as the first ad8804 works for ao channels 0-3 */
+       return caldac_index * channels_per_ad8804 + caldac_channel;
+}
+static inline int ni67xx_ao_linearity_caldac(unsigned int ao_channel)
+{
+       int ad8804_linearity_channels[4] = {4, 10, 1, 0};
+       int caldac_channel = ad8804_linearity_channels[ao_channel % 4];
+       int caldac_index = ao_channel / 4;
+
+       return caldac_index * channels_per_ad8804 + caldac_channel;
+}
+static inline int ni67xx_ao_offset_caldac(unsigned int ao_channel)
+{
+       int ad8804_offset_channels[4] = {7, 6, 9, 3};
+       int caldac_channel = ad8804_offset_channels[ao_channel % 4];
+       int caldac_index = ao_channel / 4;
+
+       return caldac_index * channels_per_ad8804 + caldac_channel;
+}
+
+static int ni67xx_ao_ground_observable_index( const calibration_setup_t *setup,
+       unsigned int channel, unsigned int ao_range )
+{
+       return 3 * channel + 0;
+}
+
+static int ni67xx_ao_mid_observable_index( const calibration_setup_t *setup,
+       unsigned int channel, unsigned int ao_range )
+{
+       return 3 * channel + 1;
+}
+
+static int ni67xx_ao_high_observable_index( const calibration_setup_t *setup,
+       unsigned int channel, unsigned int ao_range )
+{
+       return 3 * channel + 2;
+}
+
+static const double ni67xx_unitless_adc_offset = 0.5;
+
+/* determine conversion factor between actual voltage and
+ * interval [0,1) returned by reads from the calibration adc
+ * subdevice.
+ */
+static double ni67xx_unitless_adc_slope(calibration_setup_t *setup)
+{
+       double reference_in_volts;
+       double reference_unitless;
+       double slope;
+       comedi_insn insn;
+       lsampl_t data;
+       comedi_range *range;
+       static const int maxdata = 0x10000;
+       int retval;
+
+       if(ni_board(setup)->ref_eeprom_lsb >= 0 &&
+               ni_board(setup)->ref_eeprom_msb >= 0)
+       {
+               reference_in_volts = ni_get_reference(setup,
+                       ni_board(setup)->ref_eeprom_lsb, ni_board(setup)->ref_eeprom_msb );
+       }else
+       {
+               DPRINT( 0, "WARNING: unknown eeprom address for reference voltage\n"
+                       "correction.  This might be fixable if you send us an eeprom dump\n"
+                       "(see the demo/eeprom_dump program).\n");
+               reference_in_volts = 5.0;
+       }
+
+       memset(&insn, 0, sizeof(insn));
+       insn.insn = INSN_READ;
+       insn.n = 1;
+       insn.subdev = setup->ad_subdev;
+       insn.data = &data;
+       insn.chanspec = CR_PACK(0, 0, AREF_GROUND) | CR_ALT_SOURCE;
+       retval = comedi_do_insn(setup->dev, &insn);
+       assert(retval >= 0);
+
+       range = comedi_get_range(setup->dev, setup->ad_subdev, 0, 0);
+       assert( range );
+       reference_unitless = comedi_to_phys(data, range, maxdata);
+
+       slope = (reference_unitless - ni67xx_unitless_adc_offset) / reference_in_volts;
+
+       return slope;
+}
+
+/* calibration adc uses RANGE_UNKNOWN, so it will return a value from
+   0.0 to 1.0 instead of a voltage, so we need to renormalize. */
+static void ni67xx_set_target( calibration_setup_t *setup, int obs, double target, double slope)
+{
+       set_target(setup, obs, target);
+       /* convert target from volts to interval [0,1) which calibration
+        * adc returns */
+       setup->observables[obs].target *= slope;
+       setup->observables[obs].target += ni67xx_unitless_adc_offset;
+}
+
+static void ni67xx_setup_observables( calibration_setup_t *setup )
+{
+       comedi_insn tmpl, po_tmpl;
+       observable *o;
+       int num_ao_channels;
+       int i;
+       double slope;
+
+       slope = ni67xx_unitless_adc_slope(setup);
+
+       /* calibration adc is very slow (15HZ) but accurate, so only sample a few times */
+       setup->sv_order = 1;
+
+       num_ao_channels = comedi_get_n_channels(setup->dev, setup->da_subdev);
+       assert(num_ao_channels >= 0);
+
+       memset( &tmpl, 0, sizeof(tmpl) );
+       tmpl.insn = INSN_READ;
+       tmpl.n = 1;
+       tmpl.subdev = setup->ad_subdev;
+
+       memset( &po_tmpl, 0, sizeof(po_tmpl) );
+       po_tmpl.insn = INSN_WRITE;
+       po_tmpl.n = 1;
+       po_tmpl.subdev = setup->da_subdev;
+
+       setup->n_observables = 0;
+
+       for(i = 0; i < num_ao_channels; i++)
+       {
+               o = setup->observables + ni67xx_ao_ground_observable_index( setup,
+                       i, 0);
+               o->reference_source = -1;
+               assert( o->name == NULL );
+               asprintf(&o->name, "dac%i ground, ground referenced", i);
+               o->preobserve_insn = po_tmpl;
+               o->preobserve_insn.chanspec = CR_PACK(i, 0, AREF_GROUND);
+               o->preobserve_insn.data = o->preobserve_data;
+               o->observe_insn = tmpl;
+               o->observe_insn.chanspec = CR_PACK(i, 0, AREF_GROUND);
+               ni67xx_set_target(setup, ni67xx_ao_ground_observable_index(setup, i, 0), 0.0, slope);
+               setup->n_observables++;
+
+               o = setup->observables + ni67xx_ao_mid_observable_index( setup,
+                       i, 0);
+               o->reference_source = -1;
+               assert( o->name == NULL );
+               asprintf(&o->name, "dac%i mid, ground referenced", i);
+               o->preobserve_insn = po_tmpl;
+               o->preobserve_insn.chanspec = CR_PACK(i, 0, AREF_GROUND);
+               o->preobserve_insn.data = o->preobserve_data;
+               o->observe_insn = tmpl;
+               o->observe_insn.chanspec = CR_PACK(i, 0, AREF_GROUND);
+               ni67xx_set_target(setup, ni67xx_ao_mid_observable_index(setup, i, 0), 4.0, slope);
+               setup->n_observables++;
+
+               o = setup->observables + ni67xx_ao_high_observable_index( setup, i, 0);
+               o->reference_source = -1;
+               assert( o->name == NULL );
+               asprintf(&o->name, "dac%i high, ground referenced", i);
+               o->preobserve_insn = po_tmpl;
+               o->preobserve_insn.chanspec = CR_PACK( i, 0, AREF_GROUND );
+               o->preobserve_insn.data = o->preobserve_data;
+               o->observe_insn = tmpl;
+               o->observe_insn.chanspec = CR_PACK(i, 0, AREF_GROUND);
+               ni67xx_set_target(setup, ni67xx_ao_high_observable_index(setup, i, 0), 8.0, slope);
+               setup->n_observables++;
+       }
+
+       return;
+}
+
+static int cal_ni_pci_6711(calibration_setup_t *setup)
+{
+       generic_layout_t layout;
+
+       init_generic_layout( &layout );
+       layout.dac_gain = ni67xx_ao_gain_caldac;
+       layout.dac_linearity = ni67xx_ao_linearity_caldac;
+       layout.dac_offset = ni67xx_ao_offset_caldac;
+       layout.dac_high_observable = ni67xx_ao_high_observable_index;
+       layout.dac_mid_observable = ni67xx_ao_mid_observable_index;
+       layout.dac_ground_observable = ni67xx_ao_ground_observable_index;
+       layout.dac_fractional_tolerance = get_tolerance( setup, setup->da_subdev, 1.0 );
+       return generic_cal_ao(setup, &layout);
+}
+