Fix '@Sens. ZSensorSens' KeyError loading picoforce 0x07200000 curves.
[hooke.git] / hooke / driver / picoforce.py
index e664a951726ff3b6402adeaa0c0e45894ec69b19..94339d438964b76f97b29e1ee132e5998a5f388f 100644 (file)
@@ -4,15 +4,15 @@
 #
 # This file is part of Hooke.
 #
-# Hooke 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 3 of the License, or (at your option) any later version.
+# Hooke 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 3 of the
+# License, or (at your option) any later version.
 #
-# Hooke 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 Lesser General Public License for more details.
+# Hooke 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 Lesser General
+# Public License for more details.
 #
 # You should have received a copy of the GNU Lesser General Public
 # License along with Hooke.  If not, see
@@ -29,7 +29,6 @@ import numpy
 
 from .. import curve as curve # this module defines data containers.
 from .. import experiment as experiment # this module defines expt. types
-from ..config import Setting # configurable setting class
 from . import Driver as Driver # this is the Driver base class
 
 
@@ -48,7 +47,7 @@ class PicoForceDriver (Driver):
 
         return header[2:17] == 'Force file list'
 
-    def read(self, path):
+    def read(self, path, info=None):
         info = self._read_header_path(path)
         self._check_version(info)
         data = self._read_data_path(path, info)
@@ -171,8 +170,6 @@ class PicoForceDriver (Driver):
     def _read_data_file(self, file, info):
         file.seek(0)
         traces = self._extract_traces(buffer(file.read()), info)
-        for k,t in traces.items():
-            t.tofile('t-%s' % k, sep='\n')
         self._validate_traces(
             traces['Z sensor'], traces['Deflection'])
         L = len(traces['Deflection'])
@@ -215,8 +212,12 @@ class PicoForceDriver (Driver):
                 match = type_re.match(image['@4:Image Data'])
                 assert match != None, 'Bad regexp for %s, %s' \
                     % ('@4:Image Data', image['@4:Image Data'])
-                assert match.group(1).lower() == match.group(2).replace(' ','').lower(), \
-                    'Name missmatch: "%s", "%s"' % (match.group(1), match.group(2))
+                if version == '0x06130001' and match.group(1) == 'ZLowVoltage':
+                    assert match.group(2) == 'Low Voltage Z', \
+                        'Name missmatch: "%s", "%s"' % (match.group(1), match.group(2))
+                else:
+                    assert match.group(1).lower() == match.group(2).replace(' ','').lower(), \
+                        'Name missmatch: "%s", "%s"' % (match.group(1), match.group(2))
                 tname = match.group(2)
             else:
                 assert version == '0x07200000', version
@@ -234,28 +235,13 @@ class PicoForceDriver (Driver):
                 elif tname == 'Deflection Error':
                     tname = 'Deflection'
             if tname in traces:
-                continue
-                msg = None
-                if (traces[tname].info != image):
-                    ik = set(image.keys())
-                    ok = set(traces[tname].info.keys())
-                    if ik != ok:
-                        mmsg = 'extra keys: %s, missing keys %s' % (ik-ok, ok-ik)
-                    else:
-                        mmsg = []
-                        for key in image.keys():
-                            if image[key] != traces[tname].info[key]:
-                                mmsg.append(
-                                    '%s (%s != %s)'
-                                    % (key, image[key], traces[tname].info[key]))
-                        mmsg = ', '.join(mmsg)
-                    msg = 'info difference: %s' % mmsg
-                elif not (traces[tname] == d).all():
-                    msg = 'data difference'
-                if msg != None:
-                    raise NotImplementedError(
-                        'Missmatched duplicate traces for %s: %s'
-                        % (tname, msg))
+                #d.tofile('%s-2.dat' % tname, sep='\n')
+                tname = self._replace_name(tname, d, traces, info)
+                if tname == None:
+                    continue  # Don't replace anything
+            else:
+                #d.tofile('%s.dat' % tname, sep='\n')
+                pass
             traces[tname] = d
         return traces
 
@@ -276,6 +262,49 @@ class PicoForceDriver (Driver):
         block = self._scale_block(block)
         return block
 
+    def _replace_name(self, trace_name, trace, traces, info):
+        """Determine if a duplicate trace name should replace an earlier trace.
+
+        Return the target trace name if it should be replaced by the
+        new trace, or `None` if the new trace should be dropped.
+        """
+        #msg = []
+        #target = traces[trace_name]
+        #
+        ## Compare the info dictionaries for each trace
+        #ik = set(trace.info.keys())
+        #ok = set(traces[trace_name].info.keys())
+        #if ik != ok:  # Ensure we have the same set of keys for both traces
+        #    msg.append('extra keys: %s, missing keys %s' % (ik-ok, ok-ik))
+        #else:
+        #    # List keys we *require* to change between traces
+        #    variable_keys = ['Data offset', 'X data type']  # TODO: What is X data type?
+        #    for key in trace.info.keys():
+        #        if key in variable_keys:
+        #            if target.info[key] == trace.info[key]:
+        #                msg.append('constant %s (%s == %s)'
+        #                           % (key, target.info[key], trace.info[key]))
+        #        else:
+        #            if target.info[key] != trace.info[key]:
+        #                msg.append('variable %s (%s != %s)'
+        #                           % (key, target.info[key], trace.info[key]))
+        # Compare the data
+        #if not (traces[trace_name] == trace).all():
+        #    msg.append('data difference')
+        #if len(msg) > 0:
+        #    raise NotImplementedError(
+        #        'Missmatched duplicate traces for %s: %s'
+        #        % (trace_name, ', '.join(msg)))
+        import logging
+        log = logging.getLogger('hooke')
+        for name,t in traces.items():
+            if (t == trace).all():
+                log.debug('replace %s with %s-2' % (name, trace_name))
+                return name  # Replace this identical dataset.
+        log.debug('store %s-2 as Other' % (trace_name))
+        return 'Other'
+        # return None
+
     def _translate_block_info(self, info, z_piezo_info, deflection_info, name):
         version = info['Force file list']['Version']
         ret = {
@@ -319,9 +348,17 @@ class PicoForceDriver (Driver):
         # offset assumed if raw data is signed...
 
         nm_sens_re = re.compile('V ([.0-9]*) nm/V')
-        match = nm_sens_re.match(info['Scanner list']['@Sens. Zsens'])
-        assert match != None, 'Bad regexp for %s/%s, %s' \
-            % ('Scanner list', '@Sens. Zsens', info['Scanner list']['@4:Z scale'])
+        if version in ['0x06120002', '0x06130001']:        
+            match = nm_sens_re.match(info['Ciao scan list']['@Sens. ZSensorSens'])
+            assert match != None, 'Bad regexp for %s/%s, %s' \
+                % ('Ciao scan list', '@Sens. ZSensorSens',
+                   info['Ciao scan list']['@Sens. ZSensorSens'])
+        else:
+            assert version == '0x07200000', version
+            match = nm_sens_re.match(info['Ciao scan list']['@Sens. ZsensSens'])
+            assert match != None, 'Bad regexp for %s/%s, %s' \
+                % ('Ciao scan list', '@Sens. ZsensSens',
+                   info['Ciao scan list']['@Sens. ZsensSens'])
         ret['z piezo sensitivity (m/V)'] = float(match.group(1))*1e-9
 
         match = nm_sens_re.match(info['Ciao scan list']['@Sens. DeflSens'])