1 # Copyright (C) 2008-2010 A. Seeholzer
3 # Richard Naud <richard.naud@epfl.ch>
4 # Rolf Schmidt <rschmidt@alcor.concordia.ca>
5 # W. Trevor King <wking@drexel.edu>
7 # This file is part of Hooke.
9 # Hooke is free software: you can redistribute it and/or
10 # modify it under the terms of the GNU Lesser General Public
11 # License as published by the Free Software Foundation, either
12 # version 3 of the License, or (at your option) any later version.
14 # Hooke is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU Lesser General Public License for more details.
19 # You should have received a copy of the GNU Lesser General Public
20 # License along with Hooke. If not, see
21 # <http://www.gnu.org/licenses/>.
23 """Driver for MFP-3D files.
25 This driver reads IGOR binary waves.
28 Matlab version: Richard Naud August 2008 (http://lcn.epfl.ch/~naud/)
29 Python port: A. Seeholzer October 2008
30 Hooke submission: Rolf Schmidt, Alberto Gomez-Casado 2009
38 from .. import curve as curve
39 from .. import experiment as experiment
40 from ..config import Setting
41 from . import Driver as Driver
42 from .igorbinarywave import loadibw
45 __version__='0.0.0.20100604'
48 class MFP3DDriver (Driver):
49 """Handle Asylum Research's MFP3D data format.
52 super(MFP3DDriver, self).__init__(name='mfp3d')
54 def is_me(self, path):
55 """Look for identifying fields in the IBW note.
57 if not path.endswith('.ibw'):
59 targets = ['Version:', 'XOPVersion:', 'ForceNote:']
60 found = [False]*len(targets)
61 for line in open(path, 'rU'):
62 for i,ft in enumerate(zip(found, targets)):
64 if f == False and line.startswith(t):
66 if min(found) == True:
71 data,bin_info,wave_info = loadibw(path)
72 approach,retract = self._translate_ibw(data, bin_info, wave_info)
74 info = {'filetype':self.name, 'experiment':experiment.VelocityClamp}
75 return ([approach, retract], info)
77 def _translate_ibw(self, data, bin_info, wave_info):
78 if bin_info['version'] != 5:
79 raise NotImplementedError('IBW version %d (< 5) not supported'
80 % bin_info['version'])
81 # We need version 5 for multidimensional arrays.
83 # Parse the note into a dictionary
85 for line in bin_info['note'].split('\r'):
86 fields = [x.strip() for x in line.split(':', 1)]
93 bin_info['note'] = note
94 if note['VerDate'] not in ['80501.041', '80501.0207']:
95 raise Exception(note['VerDate'])
96 raise NotImplementedError(
97 '%s file version %s not supported (yet!)\n%s'
98 % (self.name, note['VerDate'], pprint.pformat(note)))
101 'raw info':{'bin':bin_info,
103 'time':wave_info['creationDate'],
104 'spring constant (N/m)':note['SpringConstant'],
106 # MFP3D's native data dimensions match Hooke's (<point>, <column>) layout.
107 approach = self._scale_block(data[:wave_info['npnts']/2,:], info, 'approach')
108 retract = self._scale_block(data[wave_info['npnts']/2:,:], info, 'retract')
109 return (approach, retract)
111 def _scale_block(self, data, info, name):
112 """Convert the block from its native format to a `numpy.float`
117 columns = info['raw info']['bin']['dimLabels'][1]
118 # Depending on your MFP3D version:
119 # VerDate 80501.0207: ['Raw', 'Defl', 'LVDT', 'Time']
120 # VerDate 80501.041: ['Raw', 'Defl', 'LVDT']
121 if 'Time' in columns:
126 shape=(data.shape[0], n_col),
128 info=copy.deepcopy(info)
130 ret.info['name'] = name
131 ret.info['raw data'] = data # store the raw data
133 z_rcol = columns.index('LVDT')
134 d_rcol = columns.index('Defl')
136 # scaled column indices
137 ret.info['columns'] = ['z piezo (m)', 'deflection (m)']
138 z_scol = ret.info['columns'].index('z piezo (m)')
139 d_scol = ret.info['columns'].index('deflection (m)')
141 # Leading '-' because increasing voltage extends the piezo,
142 # moving the tip towards the surface (positive indentation),
143 # but it makes more sense to me to have it increase away from
144 # the surface (positive separation).
145 ret[:,z_scol] = -data[:,z_rcol].astype(ret.dtype)
147 # Leading '-' because deflection voltage increases as the tip
148 # moves away from the surface, but it makes more sense to me
149 # to have it increase as it moves toward the surface (positive
150 # tension on the protein chain).
151 ret[:,d_scol] = -data[:,d_rcol]
153 if 'Time' in columns:
154 ret.info['columns'].append('time (s)')
155 t_rcol = columns.index('Time')
156 t_scol = ret.info['columns'].index('time (s)')
157 ret[:,t_scol] = data[:,t_rcol]