+ (see `calib_acquire()`)
+
+ Outputs:
+ k cantilever spring constant (in N/m, or equivalently nN/nm)
+ k_s standard deviation in our estimate of k
+
+ >>> import os
+ >>> from pprint import pprint
+ >>> import tempfile
+ >>> from pycomedi.device import Device
+ >>> from pycomedi.subdevice import StreamingSubdevice
+ >>> from pycomedi.channel import AnalogChannel, DigitalChannel
+ >>> from pycomedi.constant import AREF, IO_DIRECTION, SUBDEVICE_TYPE, UNIT
+ >>> from pypiezo.afm import AFMPiezo
+ >>> from pypiezo.base import PiezoAxis, InputChannel
+ >>> from pypiezo.config import (HDF5_ChannelConfig, HDF5_AxisConfig,
+ ... pprint_HDF5)
+ >>> from stepper import Stepper
+ >>> from pyafm import AFM
+ >>> from .config import (HDF5_CalibrationConfig, HDF5_BumpConfig,
+ ... HDF5_TemperatureConfig, HDF5_VibrationConfig)
+ >>> from .analyze import calib_load_all
+
+ >>> fd,filename = tempfile.mkstemp(suffix='.h5', prefix='calibcant-')
+ >>> os.close(fd)
+
+ >>> d = Device('/dev/comedi0')
+ >>> d.open()
+
+ Setup an `AFMPiezo` instance.
+
+ >>> s_in = d.find_subdevice_by_type(SUBDEVICE_TYPE.ai,
+ ... factory=StreamingSubdevice)
+ >>> s_out = d.find_subdevice_by_type(SUBDEVICE_TYPE.ao,
+ ... factory=StreamingSubdevice)
+
+ >>> axis_channel = s_out.channel(
+ ... 0, factory=AnalogChannel, aref=AREF.ground)
+ >>> input_channel = s_in.channel(0, factory=AnalogChannel, aref=AREF.diff)
+ >>> for chan in [axis_channel, input_channel]:
+ ... chan.range = chan.find_range(unit=UNIT.volt, min=-10, max=10)
+
+ We set the minimum voltage for the `z` axis to -9 (a volt above
+ the minimum possible voltage) to help with testing
+ `.get_surface_position`. Without this minimum voltage, small
+ calibration errors could lead to a railed -10 V input for the
+ first few surface approaching steps, which could lead to an
+ `EdgeKink` error instead of a `FlatFit` error.
+
+ >>> axis_config = HDF5_AxisConfig(filename, '/bump/config/z/axis')
+ >>> axis_config.update(
+ ... {'gain':20, 'sensitivity':8e-9, 'minimum':-9})
+ >>> axis_channel_config = HDF5_ChannelConfig(
+ ... filename, '/bump/config/z/channel')
+ >>> input_channel_config = HDF5_ChannelConfig(
+ ... filename, '/bump/config/deflection/channel')
+
+ >>> a = PiezoAxis(axis_config=axis_config,
+ ... axis_channel_config=axis_channel_config,
+ ... axis_channel=axis_channel, name='z')
+ >>> a.setup_config()
+
+ >>> c = InputChannel(
+ ... channel_config=input_channel_config, channel=input_channel,
+ ... name='deflection')
+ >>> c.setup_config()
+
+ >>> piezo = AFMPiezo(axes=[a], input_channels=[c])
+
+ Setup a `stepper` instance.
+
+ >>> s_d = d.find_subdevice_by_type(SUBDEVICE_TYPE.dio)
+ >>> d_channels = [s_d.channel(i, factory=DigitalChannel)
+ ... for i in (0, 1, 2, 3)]
+ >>> for chan in d_channels:
+ ... chan.dio_config(IO_DIRECTION.output)
+
+ >>> def write(value):
+ ... s_d.dio_bitfield(bits=value, write_mask=2**4-1)
+
+ >>> stepper = Stepper(write=write)
+
+ Setup an `AFM` instance.
+
+ >>> afm = AFM(piezo, stepper)
+
+ Test calibration:
+
+ >>> calibration_config = HDF5_CalibrationConfig(
+ ... filename=filename, group='/bump/config/calibration/')
+ >>> bump_config = HDF5_BumpConfig(
+ ... filename=filename, group='/bump/config/bump/')
+ >>> temperature_config = HDF5_TemperatureConfig(
+ ... filename=filename, group='/bump/config/temperature/')
+ >>> vibration_config = HDF5_VibrationConfig(
+ ... filename=filename, group='/bump/config/vibration')
+ >>> calib(afm, calibration_config, bump_config, temperature_config,
+ ... vibration_config, filename=filename, group='/')
+ TODO: replace skipped example data with real-world values
+ >>> pprint_HDF5(filename) # doctest: +ELLIPSIS, +REPORT_UDIFF
+ >>> everything = calib_load_all(filename, '/')
+ >>> pprint(everything)
+
+ Close the Comedi device.
+
+ >>> d.close()
+
+ Cleanup our temporary config file.
+
+ os.remove(filename)