Get calibcant working with the new load_from_config-based pyafm.
[calibcant.git] / calibcant / bump.py
index d4d8a2b4ca3716cb8bbd89db04054df01df7febd..ea6cccc31dcca8ad4d0211d47c5fb5f79a3e3e0a 100644 (file)
@@ -39,29 +39,14 @@ well defined sizes, and the gain is set with a knob on our modified
 NanoScope.
 
 Photo-sensitivity is measured by bumping the cantilever against the
-surface, where `Zp = Zcant` (see the `bump_*()` family of functions).
-The measured slope Vphoto/Vout is converted to photo-sensitivity via
+surface, where `Zp = Zcant`.  The measured slope Vphoto/Vout is
+converted to photo-sensitivity via::
 
   Vphoto/Vzp_out * Vzp_out/Vzp  * Vzp/Zp   *    Zp/Zcant =    Vphoto/Zcant
    (measured)      (1/zp_gain) (1/zp_sensitivity)  (1)    (photo_sensitivity)
 
 We do all these measurements a few times to estimate statistical
 errors.
-
-The functions are layed out in the families:
-  bump_*()
-For each family, * can be any of:
-  acquire       get real-world data
-  save         store real-world data to disk
-  load         get real-world data from disk
-  analyze      interperate the real-world data.
-  plot         show a nice graphic to convince people we're working :p
-
-A family name without any _* extension (e.g. `bump()`), runs `*_acquire()`,
-`*_save()`, `*_analyze()`.
-
-If `package_config['matplotlib']` is `True`, `*_analyze()` will call
-`*_plot()` internally.
 """
 
 import numpy as _numpy
@@ -70,235 +55,130 @@ from pypiezo.base import convert_meters_to_bits as _convert_meters_to_bits
 from pypiezo.base import convert_bits_to_meters as _convert_bits_to_meters
 
 from . import LOG as _LOG
-from .bump_analyze import bump_analyze as _bump_analyze
-from .bump_analyze import bump_save as _bump_save
+from .bump_analyze import analyze as _analyze
+from .bump_analyze import save as _save
 
 
-def bump_acquire(afm, bump_config):
+def acquire(afm, config):
     """Ramps `push_depth` closer and returns to the original position.
 
     Inputs:
-      afm          a pyafm.AFM instance
-      bump_config  a .config._BumpConfig instance
+      afm     a pyafm.AFM instance
+      config  a .config._BumpConfig instance
 
     Returns the acquired ramp data dictionary, with data in DAC/ADC bits.
     """
     afm.move_just_onto_surface(
-        depth=bump_config['initial-position'], far=bump_config['far-steps'],
-        setpoint=bump_config['setpoint'],
-        min_slope_ratio=bump_config['min-slope-ratio'])
+        depth=config['initial-position'], far=config['far-steps'],
+        setpoint=config['setpoint'],
+        min_slope_ratio=config['min-slope-ratio'])
     #afm.piezo.jump('z', 32000)
 
-    _LOG.info('bump the surface to a depth of %g m with a setpoint of %g V'
-              % (bump_config['push-depth'], bump_config['setpoint']))
+    _LOG.info(
+        'bump the surface to a depth of {} m with a setpoint of {} V'.format(
+            config['push-depth'], config['setpoint']))
 
-    axis = afm.piezo.axis_by_name(afm.axis_name)
+    axis = afm.piezo.axis_by_name(afm.config['main-axis'])
 
-    start_pos = afm.piezo.last_output[afm.axis_name]
+    start_pos = afm.piezo.last_output[afm.config['main-axis']]
     start_pos_m = _convert_bits_to_meters(axis.config, start_pos)
-    close_pos_m = start_pos_m + bump_config['push-depth']
+    close_pos_m = start_pos_m + config['push-depth']
     close_pos = _convert_meters_to_bits(axis.config, close_pos_m)
 
-    dtype = afm.piezo.channel_dtype(afm.axis_name, direction='output')
+    dtype = afm.piezo.channel_dtype(
+        afm.config['main-axis'], direction='output')
     appr = _numpy.linspace(
-        start_pos, close_pos, bump_config['samples']).astype(dtype)
+        start_pos, close_pos, config['samples']).astype(dtype)
     # switch numpy.append to numpy.concatenate with version 2.0+
     out = _numpy.append(appr, appr[::-1])
     out = out.reshape((len(out), 1))
 
     # (samples) / (meters) * (meters/second) = (samples/second)
-    freq = (bump_config['samples'] / bump_config['push-depth']
-            * bump_config['push-speed'])
+    freq = (config['samples'] / config['push-depth']
+            * config['push-speed'])
 
-    data = afm.piezo.ramp(out, freq, output_names=[afm.axis_name],
+    data = afm.piezo.ramp(out, freq, output_names=[afm.config['main-axis']],
                           input_names=['deflection'])
 
     out = out.reshape((len(out),))
     data = data.reshape((data.size,))
-    return {afm.axis_name: out, 'deflection': data}
+    return {afm.config['main-axis']: out, 'deflection': data}
 
-def bump(afm, bump_config, filename, group='/'):
-    """Wrapper around bump_acquire(), bump_analyze(), bump_save().
+def run(afm, config, filename, group='/'):
+    """Wrapper around acquire(), analyze(), save().
 
     >>> import os
     >>> import tempfile
     >>> from h5config.storage.hdf5 import pprint_HDF5
-    >>> 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 ChannelConfig, AxisConfig
-    >>> from stepper import Stepper
-    >>> from pyafm.afm import AFM
+    >>> from pyafm.storage import load_afm
     >>> from .config import BumpConfig
 
     >>> 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 = AxisConfig()
-    >>> axis_config.update(
-    ...     {'gain':20, 'sensitivity':8e-9, 'minimum':-9})
-    >>> axis_channel_config = ChannelConfig()
-    >>> axis_channel_config['name'] = 'z'
-    >>> axis_config['channel'] = axis_channel_config
-    >>> input_channel_config = ChannelConfig()
-    >>> input_channel_config['name'] = 'deflection'
-
-    >>> a = PiezoAxis(config=axis_config, axis_channel=axis_channel)
-    >>> a.setup_config()
-
-    >>> c = InputChannel(config=input_channel_config, channel=input_channel)
-    >>> c.setup_config()
-
-    >>> piezo = AFMPiezo(axes=[a], inputs=[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)
+    >>> devices = []
+    >>> afm = load_afm()
+    >>> afm.load_from_config(devices=devices)
 
     Test a bump:
 
-    >>> bump_config = BumpConfig()
-    >>> bump(afm, bump_config, filename, group='/bump')
-    TODO: replace skipped example data with real-world values
+    >>> config = BumpConfig()
+    >>> output = run(afm=afm, config=config, filename=filename, group='/')
+    >>> output  # doctest: +SKIP
+    23265462.3047795
     >>> pprint_HDF5(filename)  # doctest: +ELLIPSIS, +REPORT_UDIFF
     /
-      /bump
-        /bump/config
-          /bump/config/bump
-            <HDF5 dataset "far-steps": shape (), type "<i4">
-              200
-            <HDF5 dataset "initial-position": shape (), type "<f8">
-              -5e-08
-            <HDF5 dataset "model": shape (), type "|S9">
-              quadratic
-            <HDF5 dataset "push-depth": shape (), type "<f8">
-              2e-07
-            <HDF5 dataset "push-speed": shape (), type "<f8">
-              1e-06
-            <HDF5 dataset "samples": shape (), type "<i4">
-              1024
-            <HDF5 dataset "setpoint": shape (), type "<f8">
-              2.0
-          /bump/config/deflection
-            /bump/config/deflection/channel
-              <HDF5 dataset "channel": shape (), type "<i4">
-                0
-              <HDF5 dataset "conversion-coefficients": shape (2,), type "<f8">
-                [ -1.00000000e+01   3.05180438e-04]
-              <HDF5 dataset "conversion-origin": shape (), type "<f8">
-                0.0
-              <HDF5 dataset "device": shape (), type "|S12">
-                /dev/comedi0
-              <HDF5 dataset "inverse-conversion-coefficients": shape (2,), type "<f8">
-                [    0.    3276.75]
-              <HDF5 dataset "inverse-conversion-origin": shape (), type "<f8">
-                -10.0
-              <HDF5 dataset "maxdata": shape (), type "<i8">
-                65535
-              <HDF5 dataset "name": shape (), type "|S10">
-                deflection
-              <HDF5 dataset "range": shape (), type "<i4">
-                0
-              <HDF5 dataset "subdevice": shape (), type "<i4">
-                0
-          /bump/config/z
-            /bump/config/z/axis
-              /bump/config/z/axis/channel
-                <HDF5 dataset "channel": shape (), type "<i4">
-                  0
-                <HDF5 dataset "conversion-coefficients": shape (2,), type "<f8">
-                  [ -1.00000000e+01   3.05180438e-04]
-                <HDF5 dataset "conversion-origin": shape (), type "<f8">
-                  0.0
-                <HDF5 dataset "device": shape (), type "|S12">
-                  /dev/comedi0
-                <HDF5 dataset "inverse-conversion-coefficients": shape (2,), type "<f8">
-                  [    0.    3276.75]
-                <HDF5 dataset "inverse-conversion-origin": shape (), type "<f8">
-                  -10.0
-                <HDF5 dataset "maxdata": shape (), type "<i8">
-                  65535
-                <HDF5 dataset "name": shape (), type "|S1">
-                  z
-                <HDF5 dataset "range": shape (), type "<i4">
-                  0
-                <HDF5 dataset "subdevice": shape (), type "<i4">
-                  1
-              <HDF5 dataset "gain": shape (), type "<i4">
-                20
-              <HDF5 dataset "maximum": shape (), type "<f8">
-                10.0
-              <HDF5 dataset "minimum": shape (), type "<i4">
-                -9
-              <HDF5 dataset "monitor": shape (), type "|S1">
-    <BLANKLINE>
-              <HDF5 dataset "sensitivity": shape (), type "<f8">
-                8e-09
-        <HDF5 dataset "processed": shape (), type "<f8">
+      /config
+        /config/bump
+          <HDF5 dataset "far-steps": shape (), type "<i4">
+            200
+          <HDF5 dataset "initial-position": shape (), type "<f8">
+            -5e-08
+          <HDF5 dataset "min-slope-ratio": shape (), type "<f8">
+            10.0
+          <HDF5 dataset "model": shape (), type "|S9">
+            quadratic
+          <HDF5 dataset "push-depth": shape (), type "<f8">
+            2e-07
+          <HDF5 dataset "push-speed": shape (), type "<f8">
+            1e-06
+          <HDF5 dataset "samples": shape (), type "<i4">
+            1024
+          <HDF5 dataset "setpoint": shape (), type "<f8">
+            2.0
+      /processed
+        <HDF5 dataset "data": shape (), type "<f8">
           ...
-        /bump/raw
-          <HDF5 dataset "deflection": shape (2048,), type "<u2">
+        <HDF5 dataset "units": shape (), type "|S3">
+          V/m
+      /raw
+        /raw/deflection
+          <HDF5 dataset "data": shape (2048,), type "<u2">
             [...]
-          <HDF5 dataset "z": shape (2048,), type "<u2">
+          <HDF5 dataset "units": shape (), type "|S4">
+            bits
+        /raw/z
+          <HDF5 dataset "data": shape (2048,), type "<u2">
             [...]
+          <HDF5 dataset "units": shape (), type "|S4">
+            bits
 
-    Close the Comedi device.
+    Close the Comedi devices.
 
-    >>> d.close()
+    >>> for device in devices:
+    ...     device.close()
 
     Cleanup our temporary config file.
 
     >>> os.remove(filename)
     """
     deflection_channel = afm.piezo.input_channel_by_name('deflection')
-    axis = afm.piezo.axis_by_name(afm.axis_name)
+    axis = afm.piezo.axis_by_name(afm.config['main-axis'])
 
-    data = bump_acquire(afm, bump_config)
-    photo_sensitivity = _bump_analyze(
-        data, bump_config, z_axis_config=axis.config,
+    raw = acquire(afm, config)
+    photo_sensitivity = _analyze(
+        config=config, data=raw, z_axis_config=axis.config,
         deflection_channel_config=deflection_channel.config)
-    _bump_save(
-        filename, group, data, bump_config,
-        z_axis_config=axis.config,
-        deflection_channel_config=deflection_channel.config,
-        processed_bump=photo_sensitivity)
+    _save(filename=filename, group=group, config=config,
+          raw=raw, processed=photo_sensitivity)
     return photo_sensitivity