6 Driver for MFP-1D files.
8 Copyright 2010 by Dr. Rolf Schmidt (Concordia University, Canada)
9 This driver is based on the work of R. Naud and A. Seeholzer (see below)
10 to read Igor binary waves. Code used with permission.
12 This program is released under the GNU General Public License version 2.
16 # Reads Igor's (Wavemetric) binary wave format, .ibw, files.
19 # Parsing proper to version 2, 3, or version 5 (see Technical notes TN003.ifn:
20 # http://mirror.optus.net.au/pub/wavemetrics/IgorPro/Technical_Notes/) and data
21 # type 2 or 4 (non complex, single or double precision vector, real values).
24 # Matlab version: R. Naud August 2008 (http://lcn.epfl.ch/~naud/Home.html)
25 # Python port: A. Seeholzer October 2008
30 # Only tested for version 2 Igor files for now, testing for 3 and 5 remains to be done.
31 # More header data could be passed back if wished. For significance of ignored bytes see
32 # the technical notes linked above.
42 __version__='0.0.0.20100225'
44 class mfp1dDriver(lib.driver.Driver):
46 def __init__(self, filename):
48 This is a driver to import Asylum Research MFP-1D data.
53 self.retract_velocity = None
54 self.spring_constant = None
55 self.filename = filename
57 self.filedata = open(filename,'rU')
58 self.lines = list(self.filedata.readlines())
61 self.filetype = 'mfp1d'
62 self.experiment = 'smfs'
64 def _load_from_file(self, filename, extract_note=False):
66 f = open(filename, 'rb')
67 ####################### ORDERING
68 # machine format for IEEE floating point with big-endian
70 # MacIgor use the Motorola big-endian 'b'
71 # WinIgor use Intel little-endian 'l'
72 # If the first byte in the file is non-zero, then the file is a WinIgor
73 firstbyte = struct.unpack('b', f.read(1))[0]
78 ####################### CHECK VERSION
80 version = struct.unpack(format+'h', f.read(2))[0]
81 ####################### READ DATA AND ACCOMPANYING INFO
82 if version == 2 or version == 3:
84 wfmSize = struct.unpack(format+'i', f.read(4))[0] # The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.
85 noteSize = struct.unpack(format+'i', f.read(4))[0] # The size of the note text.
87 formulaSize = struct.unpack(format+'i', f.read(4))[0]
88 pictSize = struct.unpack(format+'i', f.read(4))[0] # Reserved. Write zero. Ignore on read.
89 checksum = struct.unpack(format+'H', f.read(2))[0] # Checksum over this header and the wave header.
91 dtype = struct.unpack(format+'h', f.read(2))[0]
93 dtype = numpy.float32(.0).dtype
95 dtype = numpy.double(.0).dtype
97 assert False, "Wave is of type '%i', not supported" % dtype
98 dtype = dtype.newbyteorder(format)
100 ignore = f.read(4) # 1 uint32
101 bname = self._flatten(struct.unpack(format+'20c', f.read(20)))
102 ignore = f.read(4) # 2 int16
103 ignore = f.read(4) # 1 uint32
104 dUnits = self._flatten(struct.unpack(format+'4c', f.read(4)))
105 xUnits = self._flatten(struct.unpack(format+'4c', f.read(4)))
106 npnts = struct.unpack(format+'i', f.read(4))[0]
107 amod = struct.unpack(format+'h', f.read(2))[0]
108 dx = struct.unpack(format+'d', f.read(8))[0]
109 x0 = struct.unpack(format+'d', f.read(8))[0]
110 ignore = f.read(4) # 2 int16
111 fsValid = struct.unpack(format+'h', f.read(2))[0]
112 topFullScale = struct.unpack(format+'d', f.read(8))[0]
113 botFullScale = struct.unpack(format+'d', f.read(8))[0]
114 ignore = f.read(16) # 16 int8
115 modDate = struct.unpack(format+'I', f.read(4))[0]
116 ignore = f.read(4) # 1 uint32
117 # Numpy algorithm works a lot faster than struct.unpack
118 data = numpy.fromfile(f, dtype, npnts)
122 checksum = struct.unpack(format+'H', f.read(2))[0] # Checksum over this header and the wave header.
123 wfmSize = struct.unpack(format+'i', f.read(4))[0] # The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.
124 formulaSize = struct.unpack(format+'i', f.read(4))[0]
125 noteSize = struct.unpack(format+'i', f.read(4))[0] # The size of the note text.
126 dataEUnitsSize = struct.unpack(format+'i', f.read(4))[0]
127 dimEUnitsSize = struct.unpack(format+'4i', f.read(16))
128 dimLabelsSize = struct.unpack(format+'4i', f.read(16))
129 sIndicesSize = struct.unpack(format+'i', f.read(4))[0]
130 optionSize1 = struct.unpack(format+'i', f.read(4))[0]
131 optionSize2 = struct.unpack(format+'i', f.read(4))[0]
135 CreationDate = struct.unpack(format+'I',f.read(4))[0]
136 modData = struct.unpack(format+'I',f.read(4))[0]
137 npnts = struct.unpack(format+'i',f.read(4))[0]
139 dtype = struct.unpack(format+'h',f.read(2))[0]
141 dtype = numpy.float32(.0).dtype
143 dtype = numpy.double(.0).dtype
145 assert False, "Wave is of type '%i', not supported" % dtype
146 dtype = dtype.newbyteorder(format)
148 ignore = f.read(2) # 1 int16
149 ignore = f.read(6) # 6 schar, SCHAR = SIGNED CHAR? ignore = fread(fid,6,'schar'); #
150 ignore = f.read(2) # 1 int16
151 bname = self._flatten(struct.unpack(format+'32c',f.read(32)))
152 ignore = f.read(4) # 1 int32
153 ignore = f.read(4) # 1 int32
154 ndims = struct.unpack(format+'4i',f.read(16)) # Number of of items in a dimension -- 0 means no data.
155 sfA = struct.unpack(format+'4d',f.read(32))
156 sfB = struct.unpack(format+'4d',f.read(32))
157 dUnits = self._flatten(struct.unpack(format+'4c',f.read(4)))
158 xUnits = self._flatten(struct.unpack(format+'16c',f.read(16)))
159 fsValid = struct.unpack(format+'h',f.read(2))
160 whpad3 = struct.unpack(format+'h',f.read(2))
161 ignore = f.read(16) # 2 double
162 ignore = f.read(40) # 10 int32
163 ignore = f.read(64) # 16 int32
164 ignore = f.read(6) # 3 int16
165 ignore = f.read(2) # 2 char
166 ignore = f.read(4) # 1 int32
167 ignore = f.read(4) # 2 int16
168 ignore = f.read(4) # 1 int32
169 ignore = f.read(8) # 2 int32
171 data = numpy.fromfile(f, dtype, npnts)
172 note_str = f.read(noteSize)
174 note_lines = note_str.split('\r')
176 for line in note_lines:
178 key, value = line.split(':', 1)
179 self.note[key] = value
180 self.retract_velocity = float(self.note['RetractVelocity'])
181 self.spring_constant = float(self.note['SpringC'])
183 assert False, "Fileversion is of type '%i', not supported" % dtype
188 data_list = data.tolist()
189 count = len(data_list) / 2
190 return data_list[:count - 1], data_list[count:]
194 def _flatten(self, tup):
200 def _read_columns(self):
201 extension = lib.curve.Data()
202 retraction = lib.curve.Data()
204 extension.y, retraction.y = self._load_from_file(self.filename, extract_note=True)
205 filename = self.filename.replace('deflection', 'LVDT', 1)
206 extension.x, retraction.x = self._load_from_file(filename, extract_note=False)
207 return [[extension.x, extension.y], [retraction.x, retraction.y]]
210 self.filedata.close()
213 if os.path.isdir(path):
215 if len(self.lines) < 34:
218 name, extension = os.path.splitext(self.filename)
219 #the following only exist in MFP1D files, not MFP-3D
220 #PullDist, PullDistSign, FastSamplingFrequency, SlowSamplingFrequency, FastDecimationFactor
221 #SlowDecimationFactor, IsDualPull, InitRetDist, RelaxDist, SlowTrigger, RelativeTrigger,
223 if extension == '.ibw' and 'deflection' in name:
224 #check if the corresponding LVDT file exists
225 filename = self.filename.replace('deflection', 'LVDT', 1)
226 if os.path.isfile(filename) and 'EndOfNote' in self.lines:
233 def default_plots(self):
237 defl_ext, defl_ret = self.deflection()
239 extension = lib.curve.Curve()
240 retraction = lib.curve.Curve()
242 extension.color = 'red'
243 extension.label = 'extension'
244 extension.style = 'plot'
245 extension.title = 'Force curve'
246 extension.units.x = 'm'
247 extension.units.y = 'N'
248 extension.x = self.data[0][0]
249 extension.y = [i * self.spring_constant for i in defl_ext]
250 retraction.color = 'blue'
251 retraction.label = 'retraction'
252 retraction.style = 'plot'
253 retraction.title = 'Force curve'
254 retraction.units.x = 'm'
255 retraction.units.y = 'N'
256 retraction.x = self.data[1][0]
257 retraction.y = [i * self.spring_constant for i in defl_ret]
259 plot = lib.plot.Plot()
260 plot.title = os.path.basename(self.filename)
261 plot.curves.append(extension)
262 plot.curves.append(retraction)
267 def deflection(self):
269 self.data = self._read_columns()
270 return self.data[0][1], self.data[1][1]