added 'write to file' support for ni 611x boards, and calibration
authorFrank Mori Hess <fmhess@speakeasy.net>
Fri, 2 May 2003 21:58:23 +0000 (21:58 +0000)
committerFrank Mori Hess <fmhess@speakeasy.net>
Fri, 2 May 2003 21:58:23 +0000 (21:58 +0000)
of all their input ranges.

comedi_calibrate/Makefile.am
comedi_calibrate/cal_common.c [new file with mode: 0644]
comedi_calibrate/calib.h
comedi_calibrate/cb.c
comedi_calibrate/cb64.c
comedi_calibrate/comedi_calibrate.c
comedi_calibrate/ni.c

index 1e7e4fec706af802ab60dd7b5aa5576746451f29..0acc161606b37e7e0fbeb8d3d3468fac82c697f3 100644 (file)
@@ -4,7 +4,7 @@ bin_PROGRAMS = comedi_calibrate
 noinst_HEADERS = calib.h
 
 comedi_calibrate_SOURCES = \
-       comedi_calibrate.c ni.c cb.c cb64.c other.c save_cal.c
+       comedi_calibrate.c ni.c cb.c cb64.c other.c save_cal.c cal_common.c
 comedi_calibrate_CFLAGS = $(COMEDILIB_CFLAGS)
 comedi_calibrate_LDADD = $(COMEDILIB_LIBS)
 
diff --git a/comedi_calibrate/cal_common.c b/comedi_calibrate/cal_common.c
new file mode 100644 (file)
index 0000000..c0454f1
--- /dev/null
@@ -0,0 +1,206 @@
+/***************************************************************************
+       cal_common.c  -  shared calibration routines
+                             -------------------
+
+    begin                : Fri May 2, 2003
+    copyright            : (C) 2003 by Frank Mori Hess
+    email                : fmhess@users.sourceforge.net
+
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License as        *
+ *   published by                                                          *
+ *   the Free Software Foundation; either version 2.1 of the License, or   *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#include "calib.h"
+#include <assert.h>
+#include <stdlib.h>
+
+void generic_do_cal( calibration_setup_t *setup,
+       saved_calibration_t *saved_cal, int observable, int caldac )
+{
+       if( caldac < 0 || observable < 0 ) return;
+
+       cal_binary( setup, observable, caldac );
+       sc_push_caldac( saved_cal, setup->caldacs[ caldac ] );
+}
+
+void generic_do_relative( calibration_setup_t *setup,
+       saved_calibration_t *saved_cal, int observable1, int observable2, int caldac )
+{
+       if( caldac < 0 || observable1 < 0 || observable2 < 0 ) return;
+
+       cal_relative_binary( setup, observable1, observable2, caldac );
+       sc_push_caldac( saved_cal, setup->caldacs[ caldac ] );
+}
+
+void generic_do_linearity( calibration_setup_t *setup,
+       saved_calibration_t *saved_cal, int observable1, int observable2,
+       int observable3, int caldac )
+{
+       if( caldac < 0 || observable1 < 0 || observable2 < 0 || observable3 < 0 )
+               return;
+
+       cal_linearity_binary( setup, observable1, observable2, observable3, caldac );
+       sc_push_caldac( saved_cal, setup->caldacs[ caldac ] );
+}
+
+void generic_prep_adc_caldacs( calibration_setup_t *setup,
+       const generic_layout_t *layout, unsigned int channel, unsigned int range )
+{
+       int retval;
+
+       if( setup->ad_subdev < 0 ) return;
+
+       if( setup->do_reset )
+       {
+               reset_caldac( setup, layout->adc_offset( channel ) );
+               reset_caldac( setup, layout->adc_gain( channel ) );
+       }else
+       {
+               retval = comedi_apply_calibration( setup->dev, setup->ad_subdev,
+                       channel, range, AREF_GROUND, setup->cal_save_file_path);
+               if( retval < 0 )
+               {
+                       DPRINT( 0, "Failed to apply existing calibration, reseting dac caldacs.\n" );
+                       reset_caldac( setup, layout->adc_offset( channel ) );
+                       reset_caldac( setup, layout->adc_gain( channel ) );
+               }
+       }
+}
+
+void generic_prep_dac_caldacs( calibration_setup_t *setup,
+       const generic_layout_t *layout, unsigned int channel, unsigned int range )
+{
+       int retval;
+
+       if( setup->da_subdev < 0 ) return;
+
+       if( setup->do_reset )
+       {
+               reset_caldac( setup, layout->dac_offset( channel ) );
+               reset_caldac( setup, layout->dac_gain( channel ) );
+       }else
+       {
+               retval = comedi_apply_calibration( setup->dev, setup->da_subdev,
+                       channel, range, AREF_GROUND, setup->cal_save_file_path);
+               if( retval < 0 )
+               {
+                       DPRINT( 0, "Failed to apply existing calibration, reseting dac caldacs.\n" );
+                       reset_caldac( setup, layout->dac_offset( channel ) );
+                       reset_caldac( setup, layout->dac_gain( channel ) );
+               }
+       }
+}
+
+int generic_cal_by_channel_and_range( calibration_setup_t *setup,
+       const generic_layout_t *layout  )
+{
+       int range, channel, num_ai_ranges, num_ai_channels, num_ao_ranges,
+               num_ao_channels, retval, num_calibrations, i;
+       saved_calibration_t *saved_cals, *current_cal;
+
+       assert( comedi_range_is_chan_specific( setup->dev, setup->ad_subdev ) == 0 );
+
+       num_ai_ranges = comedi_get_n_ranges( setup->dev, setup->ad_subdev, 0 );
+       if( num_ai_ranges < 0 ) return -1;
+
+       num_ai_channels = comedi_get_n_channels( setup->dev, setup->ad_subdev );
+       if( num_ai_channels < 0 ) return -1;
+
+       if( setup->da_subdev && setup->do_output )
+       {
+               assert( comedi_range_is_chan_specific( setup->dev, setup->da_subdev ) == 0 );
+
+               num_ao_ranges = comedi_get_n_ranges( setup->dev, setup->da_subdev, 0 );
+               if( num_ao_ranges < 0 ) return -1;
+
+               num_ao_channels = comedi_get_n_channels( setup->dev, setup->da_subdev );
+               if( num_ao_channels < 0 ) return -1;
+       }else
+               num_ao_ranges = num_ao_channels = 0;
+
+       num_calibrations = num_ai_ranges * num_ai_channels + num_ao_ranges * num_ao_channels;
+
+       saved_cals = malloc( num_calibrations * sizeof( saved_calibration_t ) );
+       if( saved_cals == NULL ) return -1;
+
+       current_cal = saved_cals;
+
+       for( channel = 0; channel < num_ai_channels; channel++ )
+       {
+               for( range = 0; range < num_ai_ranges; range++ )
+               {
+                       generic_prep_adc_caldacs( setup, layout, channel, range );
+
+                       generic_do_cal( setup, current_cal, layout->adc_ground_observable( channel, range ),
+                               layout->adc_offset( channel ) );
+
+                       generic_do_cal( setup, current_cal, layout->adc_high_observable( channel, range ),
+                               layout->adc_gain( channel ) );
+
+                       current_cal->subdevice = setup->ad_subdev;
+                       sc_push_caldac( current_cal, setup->caldacs[ layout->adc_gain( channel ) ] );
+                       sc_push_caldac( current_cal, setup->caldacs[ layout->adc_offset( channel ) ] );
+                       sc_push_channel( current_cal, channel );
+                       sc_push_range( current_cal, range );
+                       sc_push_aref( current_cal, SC_ALL_AREFS );
+
+                       current_cal++;
+               }
+       }
+       for( channel = 0; channel < num_ao_channels; channel++ )
+       {
+               for( range = 0; range < num_ao_ranges; range++ )
+               {
+                       generic_prep_dac_caldacs( setup, layout, channel, range );
+
+                       generic_do_cal( setup, current_cal, layout->dac_ground_observable( channel, range ),
+                               layout->dac_offset( channel ) );
+
+                       generic_do_cal( setup, current_cal, layout->dac_high_observable( channel, range ),
+                               layout->dac_gain( channel ) );
+
+                       current_cal->subdevice = setup->da_subdev;
+                       sc_push_caldac( current_cal, setup->caldacs[ layout->dac_gain( channel ) ] );
+                       sc_push_caldac( current_cal, setup->caldacs[ layout->dac_offset( channel ) ] );
+                       sc_push_channel( current_cal, channel );
+                       sc_push_range( current_cal, range );
+                       sc_push_aref( current_cal, SC_ALL_AREFS );
+
+                       current_cal++;
+               }
+       }
+
+       retval = write_calibration_file( setup, saved_cals, num_calibrations );
+       for( i = 0; i < num_calibrations; i++ )
+               clear_saved_calibration( &saved_cals[ i ] );
+       free( saved_cals );
+       return retval;
+}
+
+static int dummy_caldac( unsigned int channel )
+{
+       return -1;
+}
+static int dummy_observable( unsigned int channel, unsigned int range )
+{
+       return -1;
+}
+void init_generic_layout( generic_layout_t *layout )
+{
+       layout->adc_offset = dummy_caldac;
+       layout->adc_gain = dummy_caldac;
+       layout->dac_offset = dummy_caldac;
+       layout->dac_gain = dummy_caldac;
+       layout->adc_high_observable = dummy_observable;
+       layout->adc_ground_observable = dummy_observable;
+       layout->dac_high_observable = dummy_observable;
+       layout->dac_ground_observable = dummy_observable;
+}
index 5de512ae1dd8550d99a68abd6fbb617b726ff782..0038982bd873cddb8494519ab7db3238032c849e 100644 (file)
@@ -11,7 +11,7 @@
 #ifndef __CALIB_H_
 #define __CALIB_H_
 
-#include <comedilib.h>
+#include "comedilib.h"
 #if 0
 #include <stdio.h>
 #include <fcntl.h>
@@ -93,7 +93,7 @@ void observe( calibration_setup_t *setup );
 int preobserve( calibration_setup_t *setup, int obs);
 void observable_dependence( calibration_setup_t *setup, int obs);
 void measure_observable( calibration_setup_t *setup, int obs);
-void reset_caldac( calibration_setup_t *setup, unsigned int caldac_index );
+void reset_caldac( calibration_setup_t *setup, int caldac_index );
 void reset_caldacs( calibration_setup_t *setup);
 
 /* drivers */
@@ -246,5 +246,32 @@ void sc_push_range( saved_calibration_t *saved_cal, int range );
 void sc_push_aref( saved_calibration_t *saved_cal, int aref );
 void clear_saved_calibration( saved_calibration_t *saved_cal );
 
+/* generic calibration support */
+typedef struct
+{
+       int (*adc_offset)( unsigned int channel );
+       int (*adc_gain)( unsigned int channel );
+       int (*dac_offset)( unsigned int channel );
+       int (*dac_gain)( unsigned int channel );
+       int (*adc_high_observable)( unsigned int channel, unsigned int range );
+       int (*adc_ground_observable)( unsigned int channel, unsigned int range );
+       int (*dac_high_observable)( unsigned int channel, unsigned int range );
+       int (*dac_ground_observable)( unsigned int channel, unsigned int range );
+} generic_layout_t;
+void init_generic_layout( generic_layout_t *layout );
+int generic_cal_by_channel_and_range( calibration_setup_t *setup,
+       const generic_layout_t *layout  );
+void generic_do_cal( calibration_setup_t *setup,
+       saved_calibration_t *saved_cal, int observable, int caldac );
+void generic_do_relative( calibration_setup_t *setup,
+       saved_calibration_t *saved_cal, int observable1, int observable2, int caldac );
+void generic_do_linearity( calibration_setup_t *setup,
+       saved_calibration_t *saved_cal, int observable1, int observable2,
+       int observable3, int caldac );
+void generic_prep_adc_caldacs( calibration_setup_t *setup,
+       const generic_layout_t *layout, unsigned int channel, unsigned int range );
+void generic_prep_dac_caldacs( calibration_setup_t *setup,
+       const generic_layout_t *layout, unsigned int channel, unsigned int range );
+
 #endif
 
index 78bd5a4a51b99484a9900b3da6f667a0be3e9662..15dcb49a6e5fa55db6c0a1062f3e716c5223059b 100644 (file)
@@ -21,7 +21,6 @@
 #define _GNU_SOURCE
 
 #include <stdio.h>
-#include <comedilib.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
index e64f9d710b05f9eb99c9da2eaa08c0f99ecfc746..7a17896dc2a436feaab07752b6ecce9071effb92 100644 (file)
@@ -21,7 +21,6 @@
 #define _GNU_SOURCE
 
 #include <stdio.h>
-#include "comedilib.h"
 #include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
@@ -128,15 +127,6 @@ enum cal_knobs_64xx
        ADC_OFFSET_64XX = 9,
 };
 
-static inline unsigned int ADC_OFFSET_4020( unsigned int channel )
-{
-       return channel;
-}
-static inline unsigned int ADC_GAIN_4020( unsigned int channel )
-{
-       return 4 + channel;
-}
-
 int cb64_setup( calibration_setup_t *setup, const char *device_name )
 {
        unsigned int i;
@@ -615,13 +605,13 @@ static int init_observables_60xx( calibration_setup_t *setup )
        return 0;
 }
 
-static unsigned int ai_low_observable_index_4020( unsigned int channel,
+static int ai_low_observable_index_4020( unsigned int channel,
        unsigned int ai_range )
 {
        return 4 * channel + 2 * ai_range;
 }
 
-static unsigned int ai_high_observable_index_4020( unsigned int channel,
+static int ai_high_observable_index_4020( unsigned int channel,
        unsigned int ai_range )
 {
        return ai_low_observable_index_4020( channel, ai_range ) + 1;
@@ -1032,73 +1022,24 @@ static int cal_cb_pci_60xx( calibration_setup_t *setup )
        return retval;
 }
 
-static void prep_adc_caldacs_4020( calibration_setup_t *setup,
-       unsigned int channel, unsigned int range )
+static int adc_offset_4020( unsigned int channel )
 {
-       int retval;
-
-       if( setup->do_reset )
-       {
-               reset_caldac( setup, ADC_OFFSET_4020( channel ) );
-               reset_caldac( setup, ADC_GAIN_4020( channel ) );
-       }else
-       {
-               retval = comedi_apply_calibration( setup->dev, setup->ad_subdev,
-                       channel, range, AREF_GROUND, setup->cal_save_file_path);
-               if( retval < 0 )
-               {
-                       reset_caldac( setup, ADC_OFFSET_4020( channel ) );
-                       reset_caldac( setup, ADC_GAIN_4020( channel ) );
-               }
-       }
+       return channel;
+}
+static int adc_gain_4020( unsigned int channel )
+{
+       return 4 + channel;
 }
 
 static int cal_cb_pci_4020( calibration_setup_t *setup )
 {
-       int range, channel, num_ranges, num_channels, retval,
-               num_calibrations, i;
-       saved_calibration_t *saved_cals, *current_cal;
-
-       num_ranges = comedi_get_n_ranges( setup->dev, setup->ad_subdev, 0 );
-       if( num_ranges < 0 ) return -1;
-
-       num_channels = comedi_get_n_channels( setup->dev, setup->ad_subdev );
-       if( num_channels < 0 ) return -1;
-
-       num_calibrations = num_ranges * num_channels;
-
-       saved_cals = malloc( num_calibrations * sizeof( saved_calibration_t ) );
-       if( saved_cals == NULL ) return -1;
-
-       current_cal = saved_cals;
-
-       for( channel = 0; channel < num_channels; channel++ )
-       {
-               for( range = 0; range < num_ranges; range++ )
-               {
-                       prep_adc_caldacs_4020( setup, channel, range );
-
-                       cal_binary( setup, ai_low_observable_index_4020( channel, range ),
-                               ADC_OFFSET_4020( channel ) );
-
-                       cal_binary( setup, ai_high_observable_index_4020( channel, range ),
-                               ADC_GAIN_4020( channel ) );
-
-                       current_cal->subdevice = setup->ad_subdev;
-                       sc_push_caldac( current_cal, setup->caldacs[ ADC_GAIN_4020( channel ) ] );
-                       sc_push_caldac( current_cal, setup->caldacs[ ADC_OFFSET_4020( channel ) ] );
-                       sc_push_channel( current_cal, channel );
-                       sc_push_range( current_cal, range );
-                       sc_push_aref( current_cal, SC_ALL_AREFS );
-
-                       current_cal++;
-               }
-       }
-
-       retval = write_calibration_file( setup, saved_cals, num_calibrations );
-       for( i = 0; i < num_calibrations; i++ )
-               clear_saved_calibration( &saved_cals[ i ] );
-       free( saved_cals );
-       return retval;
+       generic_layout_t layout;
+
+       init_generic_layout( &layout );
+       layout.adc_offset = adc_offset_4020;
+       layout.adc_gain = adc_gain_4020;
+       layout.adc_high_observable = ai_high_observable_index_4020;
+       layout.adc_ground_observable = ai_low_observable_index_4020;
+       return generic_cal_by_channel_and_range( setup, &layout );
 }
 
index c913ab54ba68d226f254cd0f2b54ad986726aeae..5b3d858ee976e7ed656046eba6da07ac531f5ff3 100644 (file)
@@ -786,9 +786,10 @@ void setup_caldacs( calibration_setup_t *setup, int caldac_subdev )
        setup->n_caldacs += n_chan;
 }
 
-void reset_caldac( calibration_setup_t *setup, unsigned int caldac_index )
+void reset_caldac( calibration_setup_t *setup, int caldac_index )
 {
        assert( caldac_index < setup->n_caldacs );
+       if( caldac_index < 0 ) return;
        update_caldac( setup, caldac_index, setup->caldacs[ caldac_index ].maxdata / 2 );
 }
 
@@ -1442,3 +1443,4 @@ double very_low_target( comedi_t *dev, unsigned int subdevice,
        return comedi_to_phys( 1, range_ptr, max_data ) / 2.0;
 }
 
+
index aa390585b5f7800444cf77b70ad77b9fbc10ee82..03728de60e7a97e59ae09603739126d694e5b77b 100644 (file)
@@ -26,7 +26,6 @@
 #define _GNU_SOURCE
 
 #include <stdio.h>
-#include "comedilib.h"
 #include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
@@ -173,17 +172,20 @@ static inline unsigned int ni_ao_unip_linearity( unsigned int channel )
        else return ni_ao0_unip_linearity;
 }
 
-enum observables_611x{
-       ni_ao0_zero_offset_611x = 0,
-       ni_ao0_reference_611x = 1,
-       ni_ao1_zero_offset_611x = 2,
-       ni_ao1_reference_611x = 3,
+static const int num_ao_observables_611x = 4;
+static int ni_ao_zero_offset_611x( unsigned int channel, unsigned int range ) {
+       assert( range = 0 );
+       return 2 * channel;
 };
-static inline unsigned int ni_zero_offset_611x( unsigned int channel ) {
-       return 4 + 2 * channel;
+static int ni_ao_reference_611x( unsigned int channel, unsigned int range ) {
+       assert( range = 0 );
+       return 2 * channel + 1;
 };
-static inline unsigned int ni_reference_611x( unsigned int channel ) {
-       return 5 + 2 * channel;
+static int ni_zero_offset_611x( unsigned int channel, unsigned int range ) {
+       return num_ao_observables_611x + 8 * range + 2 * channel;
+};
+static int ni_reference_611x( unsigned int channel, unsigned int range ) {
+       return num_ao_observables_611x + 8 * range + 2 * channel + 1;
 };
 
 enum reference_sources {
@@ -490,7 +492,7 @@ static void ni_setup_observables( calibration_setup_t *setup )
                ni_setup_ao_observables( setup );
 }
 
-/* XXX for +-50V and +-20V ranges, the reference source goes 0V
+/* for +-50V and +-20V ranges, the reference source goes 0V
  * to 50V instead of 0V to 5V */
 static unsigned int cal_gain_register_bits_611x( double reference, double *voltage )
 {
@@ -508,52 +510,76 @@ static unsigned int ref_source_611x( unsigned int ref_source, unsigned int cal_g
        return ( ref_source & 0xf ) | ( ( cal_gain_bits << 4 ) & 0xff0 );
 }
 
+static void reference_target_611x( calibration_setup_t *setup,
+       observable *o, double master_reference, unsigned int range )
+{
+       int cal_gain_reg_bits;
+       double reference;
+       double target;
+       comedi_range *range_ptr;
+
+       range_ptr = comedi_get_range( setup->dev, setup->ad_subdev, 0, range );
+       assert( range_ptr != NULL );
+       if( range_ptr->max > 19.0 ) reference = 10 * master_reference;
+       else reference = master_reference;
+       target = range_ptr->max * 0.9;
+
+       cal_gain_reg_bits = cal_gain_register_bits_611x( reference, &target );
+
+       o->reference_source = ref_source_611x( REF_CALSRC_GND, cal_gain_reg_bits );
+       o->target = target;
+}
+
 static void ni_setup_observables_611x( calibration_setup_t *setup )
 {
        comedi_insn tmpl;
        comedi_insn po_tmpl;
-       int range, ai_range_for_ao;
-       double voltage_reference, master_reference;
+       int range, channel;
+       double master_reference;
        observable *o;
-       int ai_chan;
-       int num_chans;
-       int cal_gain_reg_bits;
+       int num_ai_channels, num_ai_ranges;
+       static const int num_ao_channels = 2;
 
        setup->settling_time_ns = 1000000;
 
-       range = 2;
-
        master_reference = ni_get_reference( setup,
                ni_board( setup )->ref_eeprom_lsb, ni_board( setup )->ref_eeprom_msb );
-       voltage_reference = 5.0;
-       cal_gain_reg_bits = cal_gain_register_bits_611x( master_reference, &voltage_reference );
 
        memset(&tmpl,0,sizeof(tmpl));
        tmpl.insn = INSN_READ;
        tmpl.n = 1;
        tmpl.subdev = setup->ad_subdev;
 
-       num_chans = comedi_get_n_channels( setup->dev, setup->ad_subdev );
+       num_ai_channels = comedi_get_n_channels( setup->dev, setup->ad_subdev );
+       assert( num_ai_channels >= 0 );
+       num_ai_ranges = comedi_get_n_ranges( setup->dev, setup->ad_subdev, 0 );
+       assert( num_ai_ranges >= 0 );
 
-       for( ai_chan = 0; ai_chan < num_chans; ai_chan++ )
+       for( channel = 0; channel < num_ai_channels; channel++ )
        {
-               /* 0 offset */
-               o = setup->observables + ni_zero_offset_611x( ai_chan );
-               o->name = "ai, bipolar zero offset";
-               o->observe_insn = tmpl;
-               o->observe_insn.chanspec = CR_PACK(ai_chan, range, AREF_DIFF)
-                       | CR_ALT_SOURCE | CR_ALT_FILTER;
-               o->reference_source = REF_GND_GND;
-               o->target = 0.0;
+               for( range = 0; range < num_ai_ranges; range++ )
+               {
+                       /* 0 offset */
+                       o = setup->observables + ni_zero_offset_611x( channel, range );
+                       assert( o->name == NULL );
+                       asprintf( &o->name, "ai, ch %i, range %i, zero offset",
+                               channel, range );
+                       o->observe_insn = tmpl;
+                       o->observe_insn.chanspec = CR_PACK( channel, range, AREF_DIFF )
+                               | CR_ALT_SOURCE | CR_ALT_FILTER;
+                       o->reference_source = REF_GND_GND;
+                       o->target = 0.0;
 
-               /* voltage reference */
-               o = setup->observables + ni_reference_611x( ai_chan );
-               o->name = "ai, bipolar voltage reference";
-               o->observe_insn = tmpl;
-               o->observe_insn.chanspec = CR_PACK(ai_chan, range, AREF_DIFF)
-                       | CR_ALT_SOURCE | CR_ALT_FILTER;
-               o->reference_source = ref_source_611x( REF_CALSRC_GND, cal_gain_reg_bits );
-               o->target = voltage_reference;
+                       /* voltage reference */
+                       o = setup->observables + ni_reference_611x( channel, range );
+                       assert( o->name == NULL );
+                       asprintf( &o->name, "ai, ch %i, range %i, voltage reference",
+                               channel, range );
+                       o->observe_insn = tmpl;
+                       o->observe_insn.chanspec = CR_PACK( channel, range, AREF_DIFF )
+                               | CR_ALT_SOURCE | CR_ALT_FILTER;
+                       reference_target_611x( setup, o, master_reference, range );
+               }
        }
 
        memset(&po_tmpl,0,sizeof(po_tmpl));
@@ -561,57 +587,38 @@ static void ni_setup_observables_611x( calibration_setup_t *setup )
        po_tmpl.n = 1;
        po_tmpl.subdev = setup->da_subdev;
 
-       ai_range_for_ao = 2;
+       for( channel = 0; channel < num_ao_channels; channel ++ )
+       {
+               static const int ai_range_for_ao = 2;
 
-       /* ao 0, zero offset */
-       o = setup->observables + ni_ao0_zero_offset_611x;
-       o->name = "ao 0, zero offset";
-       o->preobserve_insn = po_tmpl;
-       o->preobserve_insn.chanspec = CR_PACK( 0, 0, AREF_GROUND );
-       o->preobserve_insn.data = o->preobserve_data;
-       o->observe_insn = tmpl;
-       o->observe_insn.chanspec = CR_PACK( 0, ai_range_for_ao, AREF_DIFF )
-               | CR_ALT_SOURCE | CR_ALT_FILTER;
-       o->reference_source = REF_DAC0_GND;
-       set_target( setup, ni_ao0_zero_offset_611x, 0.0 );
-
-       /* ao 0, gain */
-       o = setup->observables + ni_ao0_reference_611x;
-       o->name = "ao 0, reference voltage";
-       o->preobserve_insn = po_tmpl;
-       o->preobserve_insn.chanspec = CR_PACK( 0, 0, AREF_GROUND );
-       o->preobserve_insn.data = o->preobserve_data;
-       o->observe_insn = tmpl;
-       o->observe_insn.chanspec = CR_PACK( 0, ai_range_for_ao, AREF_DIFF )
-               | CR_ALT_SOURCE | CR_ALT_FILTER;
-       o->reference_source = REF_DAC0_GND;
-       set_target( setup, ni_ao0_reference_611x, 5.0 );
-
-       /* ao 1, zero offset */
-       o = setup->observables + ni_ao1_zero_offset_611x;
-       o->name = "ao 1, zero offset";
-       o->preobserve_insn = po_tmpl;
-       o->preobserve_insn.chanspec = CR_PACK( 1, 0, AREF_GROUND );
-       o->preobserve_insn.data = o->preobserve_data;
-       o->observe_insn = tmpl;
-       o->observe_insn.chanspec = CR_PACK( 0, ai_range_for_ao, AREF_DIFF)
-               | CR_ALT_SOURCE | CR_ALT_FILTER;
-       o->reference_source = REF_DAC1_GND;
-       set_target( setup, ni_ao1_zero_offset_611x, 0.0 );
-
-       /* ao 1, gain */
-       o = setup->observables + ni_ao1_reference_611x;
-       o->name = "ao 1, reference voltage";
-       o->preobserve_insn = po_tmpl;
-       o->preobserve_insn.chanspec = CR_PACK( 1, 0, AREF_GROUND );
-       o->preobserve_insn.data = o->preobserve_data;
-       o->observe_insn = tmpl;
-       o->observe_insn.chanspec = CR_PACK( 0, ai_range_for_ao, AREF_DIFF )
-               | CR_ALT_SOURCE | CR_ALT_FILTER;
-       o->reference_source = REF_DAC1_GND;
-       set_target( setup, ni_ao1_reference_611x, 5.0 );
+               /* ao zero offset */
+               o = setup->observables + ni_ao_zero_offset_611x( channel, 0 );
+               assert( o->name == NULL );
+               asprintf( &o->name, "ao ch %i, zero offset", channel );
+               o->preobserve_insn = po_tmpl;
+               o->preobserve_insn.chanspec = CR_PACK( channel, 0, AREF_GROUND );
+               o->preobserve_insn.data = o->preobserve_data;
+               o->observe_insn = tmpl;
+               o->observe_insn.chanspec = CR_PACK( 0, ai_range_for_ao, AREF_DIFF )
+                       | CR_ALT_SOURCE | CR_ALT_FILTER;
+               o->reference_source = REF_DAC0_GND;
+               set_target( setup, ni_ao_zero_offset_611x( channel, 0 ), 0.0 );
 
-       setup->n_observables = 4 + 2 * num_chans;
+               /* ao gain */
+               o = setup->observables + ni_ao_reference_611x( channel, 0 );
+               assert( o->name == NULL );
+               asprintf( &o->name, "ao ch %i, reference voltage", channel );
+               o->preobserve_insn = po_tmpl;
+               o->preobserve_insn.chanspec = CR_PACK( channel, 0, AREF_GROUND );
+               o->preobserve_insn.data = o->preobserve_data;
+               o->observe_insn = tmpl;
+               o->observe_insn.chanspec = CR_PACK( 0, ai_range_for_ao, AREF_DIFF )
+                       | CR_ALT_SOURCE | CR_ALT_FILTER;
+               o->reference_source = REF_DAC0_GND;
+               set_target( setup, ni_ao_reference_611x( channel, 0 ), 5.0 );
+       }
+
+       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)
@@ -890,34 +897,41 @@ static int cal_ni_pci_6052e(calibration_setup_t *setup)
        /*
         * This board has noisy caldacs
         *
-        * The NI documentation says:
-        *   0, 8   AI pregain  (coarse, fine)          3, 11
-        *   4, 12  AI postgain                         15,7
-        *   2, 10  AI reference                        1, 9
-        *   14, 7  AI unipolar offset                  5, 13
+        * The NI documentation says (true mb88341 addressing):
+        *   0, 8   AI pregain  (coarse, fine)
+        *   4, 12  AI postgain
+        *   2, 10  AI reference
+        *   14, 7  AI unipolar offset
         *
         *   0      AO0 linearity
-        *   8, 4   AO0 reference               23, 19  7, 3
-        *   12     AO0 offset                  27      11
+        *   8, 4   AO0 reference
+        *   12     AO0 offset
         *   2      AO1 linearity
-        *   10, 6  AO1 reference               25, 21  9, 5
-        *   14     AO1 offset                  29, 17  13, 1
-        *
-        *   0  3       x       0011
-        *
-        *   2  1       x       0001
+        *   10, 6  AO1 reference
+        *   14     AO1 offset
         *
-        *   4  7       15 3    0111 0011
+        *  For us, these map to (ad8804 channels)
         *
-        *   6          17 5         0101
-        *   7  x
-        *   8  11      19 7    1011 0111
+        *   0, 1   AI pregain  (coarse, fine)
+        *   2, 3  AI postgain
+        *   4, 5  AI reference
+        *   7  AI unipolar offset
         *
-        *   10 9       21 9    1001 1001
+        *   0      AO0 linearity
+        *   1, 2   AO0 reference
+        *   3      AO0 offset
+        *   4      AO1 linearity
+        *   5, 6   AO1 reference
+        *   7      AO1 offset
         *
-        *   12 x       23 11        1011
+        *  or, with mb88341 channels
         *
-        *   14 5       13 1    0101 0001
+        *   xxx    AO0 linearity
+        *   7, 3   AO0 reference
+        *   11     AO0 offset
+        *   1      AO1 linearity
+        *   9, 5   AO1 reference
+        *   xxx    AO1 offset
         *
         */
        ni_caldac_layout_t layout;
@@ -966,26 +980,37 @@ static int cal_ni_daqcard_ai_16e_4(calibration_setup_t *setup)
        return cal_ni_generic( setup, &layout );
 }
 
+static int adc_offset_611x( unsigned int channel )
+{
+       return 2 * channel + 2;
+}
+static int adc_gain_611x( unsigned int channel )
+{
+       return 2 * channel + 1;
+}
+static int dac_offset_611x( unsigned int channel )
+{
+       return 12 + 2 + 2 * channel;
+}
+static int dac_gain_611x( unsigned int channel )
+{
+       return 12 + 1 + 2 * channel;
+}
 static int cal_ni_pci_611x( calibration_setup_t *setup )
 {
-       int i;
-       int num_chans;
-
-       num_chans = comedi_get_n_channels( setup->dev, setup->ad_subdev );
-
-       for( i = 0; i < num_chans; i++ ){
-               cal_binary( setup, ni_zero_offset_611x( i ), ( 2 * i + 2 ) );
-               cal_binary( setup, ni_reference_611x( i ), ( 2 * i + 1 ) );
-       }
-
-       if(setup->do_output){
-               cal_binary( setup, ni_ao0_zero_offset_611x, 14 );
-               cal_binary( setup, ni_ao0_reference_611x, 13 );
-               cal_binary( setup, ni_ao1_zero_offset_611x, 16 );
-               cal_binary( setup, ni_ao1_reference_611x, 15 );
-       }
-
-       return 0;
+       generic_layout_t layout;
+
+       init_generic_layout( &layout );
+       layout.adc_offset = adc_offset_611x;
+       layout.adc_gain = adc_gain_611x;
+       layout.dac_offset = dac_offset_611x;
+       layout.dac_gain = dac_gain_611x;
+       layout.adc_high_observable = ni_reference_611x;
+       layout.adc_ground_observable = ni_zero_offset_611x;
+       layout.dac_high_observable = ni_ao_reference_611x;
+       layout.dac_ground_observable = ni_ao_zero_offset_611x;
+
+       return generic_cal_by_channel_and_range( setup, &layout );
 }
 
 static int cal_ni_pci_mio_16e_4( calibration_setup_t *setup )
@@ -1026,13 +1051,6 @@ static int cal_ni_daqcard_6062e( calibration_setup_t *setup )
        return cal_ni_generic( setup, &layout );
 }
 
-static void ni_generic_reset_caldac( calibration_setup_t *setup,
-       int caldac )
-{
-       if( caldac < 0 ) return;
-       reset_caldac( setup, caldac );
-}
-
 static void prep_adc_caldacs_generic( calibration_setup_t *setup,
        const ni_caldac_layout_t *layout )
 {
@@ -1040,13 +1058,13 @@ static void prep_adc_caldacs_generic( calibration_setup_t *setup,
 
        if( setup->do_reset )
        {
-               ni_generic_reset_caldac( setup, layout->adc_pregain_offset );
-               ni_generic_reset_caldac( setup, layout->adc_postgain_offset );
-               ni_generic_reset_caldac( setup, layout->adc_gain );
-               ni_generic_reset_caldac( setup, layout->adc_pregain_offset_fine );
-               ni_generic_reset_caldac( setup, layout->adc_postgain_offset_fine );
-               ni_generic_reset_caldac( setup, layout->adc_gain_fine );
-               ni_generic_reset_caldac( setup, layout->adc_unip_offset );
+               reset_caldac( setup, layout->adc_pregain_offset );
+               reset_caldac( setup, layout->adc_postgain_offset );
+               reset_caldac( setup, layout->adc_gain );
+               reset_caldac( setup, layout->adc_pregain_offset_fine );
+               reset_caldac( setup, layout->adc_postgain_offset_fine );
+               reset_caldac( setup, layout->adc_gain_fine );
+               reset_caldac( setup, layout->adc_unip_offset );
        }else
        {
                retval = comedi_apply_calibration( setup->dev, setup->ad_subdev,
@@ -1054,19 +1072,19 @@ static void prep_adc_caldacs_generic( calibration_setup_t *setup,
                if( retval < 0 )
                {
                        DPRINT( 0, "Failed to apply existing calibration, reseting adc caldacs.\n" );
-                       ni_generic_reset_caldac( setup, layout->adc_pregain_offset );
-                       ni_generic_reset_caldac( setup, layout->adc_postgain_offset );
-                       ni_generic_reset_caldac( setup, layout->adc_gain );
-                       ni_generic_reset_caldac( setup, layout->adc_pregain_offset_fine );
-                       ni_generic_reset_caldac( setup, layout->adc_postgain_offset_fine );
-                       ni_generic_reset_caldac( setup, layout->adc_gain_fine );
-                       ni_generic_reset_caldac( setup, layout->adc_unip_offset );
+                       reset_caldac( setup, layout->adc_pregain_offset );
+                       reset_caldac( setup, layout->adc_postgain_offset );
+                       reset_caldac( setup, layout->adc_gain );
+                       reset_caldac( setup, layout->adc_pregain_offset_fine );
+                       reset_caldac( setup, layout->adc_postgain_offset_fine );
+                       reset_caldac( setup, layout->adc_gain_fine );
+                       reset_caldac( setup, layout->adc_unip_offset );
                }
        }
 }
 
 static void prep_dac_caldacs_generic( calibration_setup_t *setup,
-       const ni_caldac_layout_t *layout, unsigned int channel )
+       const ni_caldac_layout_t *layout, unsigned int channel, unsigned int range )
 {
        int retval;
 
@@ -1074,53 +1092,25 @@ static void prep_dac_caldacs_generic( calibration_setup_t *setup,
 
        if( setup->do_reset )
        {
-               ni_generic_reset_caldac( setup, layout->dac_offset[ channel ] );
-               ni_generic_reset_caldac( setup, layout->dac_gain[ channel ] );
-               ni_generic_reset_caldac( setup, layout->dac_gain_fine[ channel ] );
-               ni_generic_reset_caldac( setup, layout->dac_linearity[ channel ] );
+               reset_caldac( setup, layout->dac_offset[ channel ] );
+               reset_caldac( setup, layout->dac_gain[ channel ] );
+               reset_caldac( setup, layout->dac_gain_fine[ channel ] );
+               reset_caldac( setup, layout->dac_linearity[ channel ] );
        }else
        {
                retval = comedi_apply_calibration( setup->dev, setup->da_subdev,
-                       channel, 0, AREF_GROUND, setup->cal_save_file_path);
+                       channel, range, AREF_GROUND, setup->cal_save_file_path);
                if( retval < 0 )
                {
                        DPRINT( 0, "Failed to apply existing calibration, reseting dac caldacs.\n" );
-                       ni_generic_reset_caldac( setup, layout->dac_offset[ channel ] );
-                       ni_generic_reset_caldac( setup, layout->dac_gain[ channel ] );
-                       ni_generic_reset_caldac( setup, layout->dac_gain_fine[ channel ] );
-                       ni_generic_reset_caldac( setup, layout->dac_linearity[ channel ] );
+                       reset_caldac( setup, layout->dac_offset[ channel ] );
+                       reset_caldac( setup, layout->dac_gain[ channel ] );
+                       reset_caldac( setup, layout->dac_gain_fine[ channel ] );
+                       reset_caldac( setup, layout->dac_linearity[ channel ] );
                }
        }
 }
 
-static void ni_generic_binary( calibration_setup_t *setup,
-       saved_calibration_t *saved_cal, int observable, int caldac )
-{
-       if( caldac < 0 ) return;
-
-       cal_binary( setup, observable, caldac );
-       sc_push_caldac( saved_cal, setup->caldacs[ caldac ] );
-}
-
-static void ni_generic_relative( calibration_setup_t *setup,
-       saved_calibration_t *saved_cal, int observable1, int observable2, int caldac )
-{
-       if( caldac < 0 ) return;
-
-       cal_relative_binary( setup, observable1, observable2, caldac );
-       sc_push_caldac( saved_cal, setup->caldacs[ caldac ] );
-}
-
-static void ni_generic_linearity( calibration_setup_t *setup,
-       saved_calibration_t *saved_cal, int observable1, int observable2,
-       int observable3, int caldac )
-{
-       if( caldac < 0 ) return;
-
-       cal_linearity_binary( setup, observable1, observable2, observable3, caldac );
-       sc_push_caldac( saved_cal, setup->caldacs[ caldac ] );
-}
-
 static int cal_ni_generic( calibration_setup_t *setup, const ni_caldac_layout_t *layout )
 {
        saved_calibration_t saved_cals[ 5 ], *current_cal;
@@ -1134,18 +1124,18 @@ static int cal_ni_generic( calibration_setup_t *setup, const ni_caldac_layout_t
        prep_adc_caldacs_generic( setup, layout );
 
        current_cal->subdevice = setup->ad_subdev;
-       ni_generic_relative( setup, current_cal, ni_zero_offset_low,
+       generic_do_relative( setup, current_cal, ni_zero_offset_low,
                ni_reference_low, layout->adc_gain );
-       ni_generic_relative( setup, current_cal, ni_zero_offset_low,
+       generic_do_relative( setup, current_cal, ni_zero_offset_low,
                ni_zero_offset_high, layout->adc_postgain_offset );
-       ni_generic_binary( setup, current_cal, ni_zero_offset_high, layout->adc_pregain_offset );
-       ni_generic_relative( setup, current_cal, ni_zero_offset_low,
+       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 );
-       ni_generic_relative( setup, current_cal, ni_zero_offset_low,
+       generic_do_relative( setup, current_cal, ni_zero_offset_low,
                ni_zero_offset_high, layout->adc_postgain_offset_fine );
-       ni_generic_binary( setup, current_cal, ni_zero_offset_high,
+       generic_do_cal( setup, current_cal, ni_zero_offset_high,
                layout->adc_pregain_offset_fine );
-       ni_generic_binary( 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 );
        sc_push_channel( current_cal, SC_ALL_CHANNELS );
        sc_push_range( current_cal, SC_ALL_RANGES );
        sc_push_aref( current_cal, SC_ALL_AREFS );
@@ -1155,22 +1145,23 @@ static int cal_ni_generic( calibration_setup_t *setup, const ni_caldac_layout_t
        {
                unsigned int channel, range;
                int ao_unipolar_lowgain = get_unipolar_lowgain( setup->dev, setup->da_subdev );
+               int ao_bipolar_lowgain = get_bipolar_lowgain( setup->dev, setup->da_subdev );
                int num_ao_ranges;
 
                for( channel = 0; channel < 2; channel++ )
                {
                        num_ao_ranges = comedi_get_n_ranges( setup->dev, setup->da_subdev, channel );
-                       prep_dac_caldacs_generic( setup, layout, channel );
+                       prep_dac_caldacs_generic( setup, layout, channel, ao_bipolar_lowgain );
 
                        current_cal->subdevice = setup->da_subdev;
-                       ni_generic_linearity( setup, current_cal, ni_ao_linearity( channel ),
+                       generic_do_linearity( setup, current_cal, ni_ao_linearity( channel ),
                                ni_ao_zero_offset( channel ), ni_ao_reference( channel ),
                                layout->dac_linearity[ channel ] );
-                       ni_generic_binary( setup, current_cal, ni_ao_zero_offset( channel ),
+                       generic_do_cal( setup, current_cal, ni_ao_zero_offset( channel ),
                                layout->dac_offset[ channel ] );
-                       ni_generic_binary( setup, current_cal, ni_ao_reference( channel ),
+                       generic_do_cal( setup, current_cal, ni_ao_reference( channel ),
                                layout->dac_gain[ channel ] );
-                       ni_generic_binary( setup, current_cal, ni_ao_reference( channel ),
+                       generic_do_cal( setup, current_cal, ni_ao_reference( channel ),
                                layout->dac_gain_fine[ channel ] );
                        sc_push_channel( current_cal, channel );
                        for( range = 0; range < num_ao_ranges; range++ )
@@ -1183,17 +1174,17 @@ static int cal_ni_generic( calibration_setup_t *setup, const ni_caldac_layout_t
 
                        if( ao_unipolar_lowgain >= 0 )
                        {
-                               prep_dac_caldacs_generic( setup, layout, channel );
+                               prep_dac_caldacs_generic( setup, layout, channel, ao_unipolar_lowgain );
 
                                current_cal->subdevice = setup->da_subdev;
-                               ni_generic_linearity( setup, current_cal, ni_ao_unip_zero_offset( channel ),
+                               generic_do_linearity( setup, current_cal, ni_ao_unip_zero_offset( channel ),
                                        ni_ao_unip_linearity( channel ), ni_ao_unip_reference( channel ),
                                        layout->dac_linearity[ channel ] );
-                               ni_generic_binary( setup, current_cal, ni_ao_unip_zero_offset( channel),
+                               generic_do_cal( setup, current_cal, ni_ao_unip_zero_offset( channel),
                                        layout->dac_offset[ channel ] );
-                               ni_generic_binary( setup, current_cal, ni_ao_unip_reference( channel ),
+                               generic_do_cal( setup, current_cal, ni_ao_unip_reference( channel ),
                                        layout->dac_gain[ channel ] );
-                               ni_generic_binary( setup, current_cal, ni_ao_unip_reference( channel ),
+                               generic_do_cal( setup, current_cal, ni_ao_unip_reference( channel ),
                                        layout->dac_gain_fine[ channel ] );
                                sc_push_channel( current_cal, channel );
                                for( range = 0; range < num_ao_ranges; range++ )