Add surface-matplotlib config option to pypiezo.
[pypiezo.git] / pypiezo / surface.py
index 0145b23eff333d08db07fb3f5cfb1a696b1d488e..0534613d28ed8d13a02fa57c4a1c4fa7fb82dbed 100644 (file)
@@ -1,19 +1,18 @@
-# Copyright (C) 2008-2011 W. Trevor King <wking@drexel.edu>
+# Copyright (C) 2011-2012 W. Trevor King <wking@tremily.us>
 #
 # This file is part of pypiezo.
 #
-# pypiezo is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the
-# Free Software Foundation, either version 3 of the License, or (at your
-# option) any later version.
+# pypiezo is free software: you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
 #
-# pypiezo is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# General Public License for more details.
+# pypiezo is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 #
-# You should have received a copy of the GNU General Public License
-# along with pypiezo.  If not, see <http://www.gnu.org/licenses/>.
+# You should have received a copy of the GNU General Public License along with
+# pypiezo.  If not, see <http://www.gnu.org/licenses/>.
 
 """Utilities detecting the position of the sample surface.
 
@@ -35,6 +34,7 @@ except (ImportError, RuntimeError), e:
     _matplotlib_import_error = e
 
 from . import LOG as _LOG
+from . import package_config as _package_config
 from . import base as _base
 
 
@@ -59,6 +59,7 @@ class FlatFit (PoorFit):
             left_slope, right_slope)
         super(FlatFit, self).__init__(msg)
 
+
 class EdgeKink (PoorFit):
     def __init__(self, kink, edge, window):
         self.kink = kink
@@ -78,24 +79,24 @@ def _get_min_max_positions(piezo, axis_name, min_position=None,
     output_axis = piezo.axis_by_name(axis_name)
     if min_position is None:
         min_position = _base.convert_volts_to_bits(
-            output_axis.axis_channel_config,
-            output_axis.axis_config['minimum'])
+            output_axis.config['channel'],
+            output_axis.config['minimum'])
     if max_position is None:
         max_position = _base.convert_volts_to_bits(
-            output_axis.axis_channel_config,
-            output_axis.axis_config['maximum'])
+            output_axis.config['channel'],
+            output_axis.config['maximum'])
     return (min_position, max_position)
 
 def get_surface_position_data(piezo, axis_name, max_deflection, steps=2000,
                               frequency=10e3, min_position=None,
                               max_position=None):
     "Measure the distance to the surface"
-    _LOG.debug('get surface position')
+    _LOG.info('get surface position')
     orig_position = piezo.last_output[axis_name]
     # fully retract the piezo
     min_position,max_position = _get_min_max_positions(
         piezo, axis_name, min_position=min_position, max_position=max_position)
-    _LOG.debug('retract the piezo to %d' % min_position)
+    _LOG.info('retract the piezo to %d' % min_position)
     dtype = piezo.channel_dtype(axis_name, direction='output')
     out = _linspace(orig_position, min_position, steps, dtype=dtype)
     out = out.reshape((len(out), 1)).astype(
@@ -107,7 +108,7 @@ def get_surface_position_data(piezo, axis_name, max_deflection, steps=2000,
         }
     ret1 = piezo.named_ramp(data=out, **ramp_kwargs)
     # locate high deflection position
-    _LOG.debug('approach until there is dangerous deflection (> %d)'
+    _LOG.info('approach until there is dangerous deflection (> %d)'
                % max_deflection)
     if SLEEP_DURING_SURF_POS_AQUISITION == True:
         _sleep(.2) # sleeping briefly seems to reduce bounce?
@@ -116,13 +117,13 @@ def get_surface_position_data(piezo, axis_name, max_deflection, steps=2000,
         step=(max_position-min_position)/steps, return_data=True)
     high_contact_pos = piezo.last_output[axis_name]
     # fully retract the piezo again
-    _LOG.debug('retract the piezo to %d again' % min_position)
+    _LOG.info('retract the piezo to %d again' % min_position)
     if SLEEP_DURING_SURF_POS_AQUISITION == True:
         _sleep(.2)
     out = _linspace(high_contact_pos, min_position, steps, dtype=dtype)
     ret2 = piezo.named_ramp(data=out, **ramp_kwargs)
     # scan to the high contact position
-    _LOG.debug('ramp in to the deflected position %d' % high_contact_pos)
+    _LOG.info('ramp in to the deflected position %d' % high_contact_pos)
     if SLEEP_DURING_SURF_POS_AQUISITION == True:
         _sleep(.2)
     out = _linspace(min_position, high_contact_pos, steps, dtype=dtype)
@@ -130,7 +131,7 @@ def get_surface_position_data(piezo, axis_name, max_deflection, steps=2000,
     if SLEEP_DURING_SURF_POS_AQUISITION == True:
         _sleep(.2)
     # return to the original position
-    _LOG.debug('return to the original position %d' % orig_position)
+    _LOG.info('return to the original position %d' % orig_position)
     out = _linspace(high_contact_pos, orig_position, steps, dtype=dtype)
     ret3 = piezo.named_ramp(data=out, **ramp_kwargs)
     return {'ret1':ret1, 'mtpod':mtpod, 'ret2':ret2,
@@ -163,9 +164,9 @@ def analyze_surface_position_data(
         minimum or maximum `z` position during the approach.  If
         `None`, a default value of 2% of the approach range is used.
     """
-    # ususes ddict["approach"] for analysis
+    # uses ddict["approach"] for analysis
     # the others are just along to be plotted
-    _LOG.debug('snalyze surface position data')
+    _LOG.info('analyze surface position data')
 
     data = ddict['approach']
     # analyze data, using bilinear model
@@ -173,29 +174,30 @@ def analyze_surface_position_data(
     #   = p0 + p1 p2 + p3 (x-p2)   for x >= p2
     dump_before_index = 0 # 25 # HACK!!
     # Generate a reasonable guess...
-    start_pos = int(data['z'].min())
-    final_pos = int(data['z'].max())
-    start_def = int(data['deflection'].min())
-    final_def = int(data['deflection'].max())
+    start_pos = data['z'].min()
+    final_pos = data['z'].max()
+    if final_pos == start_pos:
+        raise SurfaceError('cannot fit a single approach step (too close?)')
+    start_def = data['deflection'].min()
+    final_def = data['deflection'].max()
     # start_def and start_pos are probably for 2 different points
-    _LOG.debug('min deflection %d, max deflection %d'
-               % (start_def, final_def))
-    _LOG.debug('min position %d, max position %d'
-               % (start_pos, final_pos))
+    _LOG.info('min deflection {}, max deflection {}'.format(
+            start_def, final_def))
+    _LOG.info('min position {}, max position {}'.format(start_pos, final_pos))
 
     left_offset   = start_def
     left_slope    = 0
     kink_position = (final_pos+start_pos)/2.0
     right_slope   = 2.0*(final_def-start_def)/(final_pos-start_pos)
     pstart = [left_offset, left_slope, kink_position, right_slope]
-    _LOG.debug('guessed params: %s' % pstart)
+    _LOG.info('guessed params: %s' % pstart)
 
     offset_scale = (final_pos - start_pos)/100
     left_slope_scale = right_slope/10
     kink_scale = (final_pos-start_pos)/100
     right_slope_scale = right_slope
     scale = [offset_scale, left_slope_scale, kink_scale, right_slope_scale]
-    _LOG.debug('guessed scale: %s' % scale)
+    _LOG.info('guessed scale: %s' % scale)
 
     def residual(p, y, x):
         Y = bilinear(x, p)
@@ -205,9 +207,9 @@ def analyze_surface_position_data(
         args=(data["deflection"][dump_before_index:],
               data["z"][dump_before_index:]),
         full_output=True, maxfev=10000)
-    _LOG.debug('best fit parameters: %s' % (params,))
+    _LOG.info('best fit parameters: %s' % (params,))
 
-    if _base_config['matplotlib']:
+    if _package_config['surface-matplotlib']:
         if not _matplotlib:
             raise _matplotlib_import_error
         figure = _matplotlib_pyplot.figure()
@@ -233,7 +235,11 @@ def analyze_surface_position_data(
                   [fit_fn(start_pos, params), fit_fn(params[2], params),
                    fit_fn(final_pos, params)], '-',label='fit')
         #_pylab.legend(loc='best')
-        figure.show()
+        figure.canvas.draw()
+        if hasattr(figure, 'show'):
+            figure.show()
+        if not _matplotlib.is_interactive():
+            _matplotlib_pyplot.show()
 
     # check that the fit is reasonable
     # params[1] is slope in non-contact region
@@ -248,7 +254,7 @@ def analyze_surface_position_data(
         raise EdgeKink(kink=params[2], edge=start_pos, window=kink_window)
     if params[2] > final_pos-kink_window:
         raise EdgeKink(kink=params[2], edge=final_pos, window=kink_window)
-    _LOG.debug('surface position %s' % params[2])
+    _LOG.info('surface position %s' % params[2])
     if return_all_parameters:
         return params
     return params[2]