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 modify it
10 # under the terms of the GNU Lesser General Public License as
11 # published by the Free Software Foundation, either version 3 of the
12 # License, or (at your option) any later version.
14 # Hooke is distributed in the hope that it will be useful, but WITHOUT
15 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
17 # 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
39 from .. import curve as curve
40 from .. import experiment as experiment
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 os.path.isdir(path):
59 if not path.endswith('.ibw'):
61 targets = ['Version:', 'XOPVersion:', 'ForceNote:']
62 found = [False]*len(targets)
63 for line in open(path, 'rU'):
64 for i,ft in enumerate(zip(found, targets)):
66 if f == False and line.startswith(t):
68 if min(found) == True:
72 def read(self, path, info=None):
73 data,bin_info,wave_info = loadibw(path)
74 approach,retract = self._translate_ibw(data, bin_info, wave_info)
76 info = {'filetype':self.name, 'experiment':experiment.VelocityClamp}
77 return ([approach, retract], info)
79 def _translate_ibw(self, data, bin_info, wave_info):
80 if bin_info['version'] != 5:
81 raise NotImplementedError('IBW version %d (< 5) not supported'
82 % bin_info['version'])
83 # We need version 5 for multidimensional arrays.
85 # Parse the note into a dictionary
87 for line in bin_info['note'].split('\r'):
88 fields = [x.strip() for x in line.split(':', 1)]
95 bin_info['note'] = note
96 if note['VerDate'] not in ['80501.041', '80501.0207']:
97 raise Exception(note['VerDate'])
98 raise NotImplementedError(
99 '%s file version %s not supported (yet!)\n%s'
100 % (self.name, note['VerDate'], pprint.pformat(note)))
103 'raw info':{'bin':bin_info,
105 'time':wave_info['creationDate'],
106 'spring constant (N/m)':note['SpringConstant'],
108 # MFP3D's native data dimensions match Hooke's (<point>, <column>) layout.
109 approach = self._scale_block(data[:wave_info['npnts']/2,:], info, 'approach')
110 retract = self._scale_block(data[wave_info['npnts']/2:,:], info, 'retract')
111 return (approach, retract)
113 def _scale_block(self, data, info, name):
114 """Convert the block from its native format to a `numpy.float`
119 columns = info['raw info']['bin']['dimLabels'][1]
120 # Depending on your MFP3D version:
121 # VerDate 80501.0207: ['Raw', 'Defl', 'LVDT', 'Time']
122 # VerDate 80501.041: ['Raw', 'Defl', 'LVDT']
123 if 'Time' in columns:
128 shape=(data.shape[0], n_col),
130 info=copy.deepcopy(info)
132 ret.info['name'] = name
133 ret.info['raw data'] = data # store the raw data
135 z_rcol = columns.index('LVDT')
136 d_rcol = columns.index('Defl')
138 # scaled column indices
139 ret.info['columns'] = ['z piezo (m)', 'deflection (m)']
140 z_scol = ret.info['columns'].index('z piezo (m)')
141 d_scol = ret.info['columns'].index('deflection (m)')
143 # Leading '-' because increasing voltage extends the piezo,
144 # moving the tip towards the surface (positive indentation),
145 # but it makes more sense to me to have it increase away from
146 # the surface (positive separation).
147 ret[:,z_scol] = -data[:,z_rcol].astype(ret.dtype)
149 # Leading '-' because deflection voltage increases as the tip
150 # moves away from the surface, but it makes more sense to me
151 # to have it increase as it moves toward the surface (positive
152 # tension on the protein chain).
153 ret[:,d_scol] = -data[:,d_rcol]
155 if 'Time' in columns:
156 ret.info['columns'].append('time (s)')
157 t_rcol = columns.index('Time')
158 t_scol = ret.info['columns'].index('time (s)')
159 ret[:,t_scol] = data[:,t_rcol]