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 def _load_from_file(self, filename, extract_note=False):
63 f = open(filename, 'rb')
64 ####################### ORDERING
65 # machine format for IEEE floating point with big-endian
67 # MacIgor use the Motorola big-endian 'b'
68 # WinIgor use Intel little-endian 'l'
69 # If the first byte in the file is non-zero, then the file is a WinIgor
70 firstbyte = struct.unpack('b', f.read(1))[0]
75 ####################### CHECK VERSION
77 version = struct.unpack(format+'h', f.read(2))[0]
78 ####################### READ DATA AND ACCOMPANYING INFO
79 if version == 2 or version == 3:
81 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.
82 noteSize = struct.unpack(format+'i', f.read(4))[0] # The size of the note text.
84 formulaSize = struct.unpack(format+'i', f.read(4))[0]
85 pictSize = struct.unpack(format+'i', f.read(4))[0] # Reserved. Write zero. Ignore on read.
86 checksum = struct.unpack(format+'H', f.read(2))[0] # Checksum over this header and the wave header.
88 dtype = struct.unpack(format+'h', f.read(2))[0]
90 dtype = numpy.float32(.0).dtype
92 dtype = numpy.double(.0).dtype
94 assert False, "Wave is of type '%i', not supported" % dtype
95 dtype = dtype.newbyteorder(format)
97 ignore = f.read(4) # 1 uint32
98 bname = self._flatten(struct.unpack(format+'20c', f.read(20)))
99 ignore = f.read(4) # 2 int16
100 ignore = f.read(4) # 1 uint32
101 dUnits = self._flatten(struct.unpack(format+'4c', f.read(4)))
102 xUnits = self._flatten(struct.unpack(format+'4c', f.read(4)))
103 npnts = struct.unpack(format+'i', f.read(4))[0]
104 amod = struct.unpack(format+'h', f.read(2))[0]
105 dx = struct.unpack(format+'d', f.read(8))[0]
106 x0 = struct.unpack(format+'d', f.read(8))[0]
107 ignore = f.read(4) # 2 int16
108 fsValid = struct.unpack(format+'h', f.read(2))[0]
109 topFullScale = struct.unpack(format+'d', f.read(8))[0]
110 botFullScale = struct.unpack(format+'d', f.read(8))[0]
111 ignore = f.read(16) # 16 int8
112 modDate = struct.unpack(format+'I', f.read(4))[0]
113 ignore = f.read(4) # 1 uint32
114 # Numpy algorithm works a lot faster than struct.unpack
115 data = numpy.fromfile(f, dtype, npnts)
119 checksum = struct.unpack(format+'H', f.read(2))[0] # Checksum over this header and the wave header.
120 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.
121 formulaSize = struct.unpack(format+'i', f.read(4))[0]
122 noteSize = struct.unpack(format+'i', f.read(4))[0] # The size of the note text.
123 dataEUnitsSize = struct.unpack(format+'i', f.read(4))[0]
124 dimEUnitsSize = struct.unpack(format+'4i', f.read(16))
125 dimLabelsSize = struct.unpack(format+'4i', f.read(16))
126 sIndicesSize = struct.unpack(format+'i', f.read(4))[0]
127 optionSize1 = struct.unpack(format+'i', f.read(4))[0]
128 optionSize2 = struct.unpack(format+'i', f.read(4))[0]
132 CreationDate = struct.unpack(format+'I',f.read(4))[0]
133 modData = struct.unpack(format+'I',f.read(4))[0]
134 npnts = struct.unpack(format+'i',f.read(4))[0]
136 dtype = struct.unpack(format+'h',f.read(2))[0]
138 dtype = numpy.float32(.0).dtype
140 dtype = numpy.double(.0).dtype
142 assert False, "Wave is of type '%i', not supported" % dtype
143 dtype = dtype.newbyteorder(format)
145 ignore = f.read(2) # 1 int16
146 ignore = f.read(6) # 6 schar, SCHAR = SIGNED CHAR? ignore = fread(fid,6,'schar'); #
147 ignore = f.read(2) # 1 int16
148 bname = self._flatten(struct.unpack(format+'32c',f.read(32)))
149 ignore = f.read(4) # 1 int32
150 ignore = f.read(4) # 1 int32
151 ndims = struct.unpack(format+'4i',f.read(16)) # Number of of items in a dimension -- 0 means no data.
152 sfA = struct.unpack(format+'4d',f.read(32))
153 sfB = struct.unpack(format+'4d',f.read(32))
154 dUnits = self._flatten(struct.unpack(format+'4c',f.read(4)))
155 xUnits = self._flatten(struct.unpack(format+'16c',f.read(16)))
156 fsValid = struct.unpack(format+'h',f.read(2))
157 whpad3 = struct.unpack(format+'h',f.read(2))
158 ignore = f.read(16) # 2 double
159 ignore = f.read(40) # 10 int32
160 ignore = f.read(64) # 16 int32
161 ignore = f.read(6) # 3 int16
162 ignore = f.read(2) # 2 char
163 ignore = f.read(4) # 1 int32
164 ignore = f.read(4) # 2 int16
165 ignore = f.read(4) # 1 int32
166 ignore = f.read(8) # 2 int32
168 data = numpy.fromfile(f, dtype, npnts)
169 note_str = f.read(noteSize)
171 note_lines = note_str.split('\r')
173 for line in note_lines:
175 key, value = line.split(':', 1)
176 self.note[key] = value
177 self.retract_velocity = float(self.note['RetractVelocity'])
178 self.spring_constant = float(self.note['SpringC'])
180 assert False, "Fileversion is of type '%i', not supported" % dtype
185 data_list = data.tolist()
186 count = len(data_list) / 2
187 return data_list[:count - 1], data_list[count:]
191 def _flatten(self, tup):
197 def _read_columns(self):
198 extension = lib.curve.Data()
199 retraction = lib.curve.Data()
201 extension.y, retraction.y = self._load_from_file(self.filename, extract_note=True)
202 filename = self.filename.replace('deflection', 'LVDT', 1)
203 extension.x, retraction.x = self._load_from_file(filename, extract_note=False)
204 return [[extension.x, extension.y], [retraction.x, retraction.y]]
207 self.filedata.close()
210 if os.path.isdir(path):
212 if len(self.lines) < 34:
215 name, extension = os.path.splitext(self.filename)
216 #the following only exist in MFP1D files, not MFP-3D
217 #PullDist, PullDistSign, FastSamplingFrequency, SlowSamplingFrequency, FastDecimationFactor
218 #SlowDecimationFactor, IsDualPull, InitRetDist, RelaxDist, SlowTrigger, RelativeTrigger,
220 if extension == '.ibw' and 'deflection' in name:
221 #check if the corresponding LVDT file exists
222 filename = self.filename.replace('deflection', 'LVDT', 1)
223 if os.path.isfile(filename) and 'EndOfNote' in self.lines:
230 def default_plots(self):
234 defl_ext, defl_ret = self.deflection()
236 extension = lib.curve.Curve()
237 retraction = lib.curve.Curve()
239 extension.color = 'red'
240 extension.label = 'extension'
241 extension.style = 'plot'
242 extension.title = 'Force curve'
243 extension.units.x = 'm'
244 extension.units.y = 'N'
245 extension.x = self.data[0][0]
246 extension.y = [i * self.spring_constant for i in defl_ext]
247 retraction.color = 'blue'
248 retraction.label = 'retraction'
249 retraction.style = 'plot'
250 retraction.title = 'Force curve'
251 retraction.units.x = 'm'
252 retraction.units.y = 'N'
253 retraction.x = self.data[1][0]
254 retraction.y = [i * self.spring_constant for i in defl_ret]
256 plot = lib.plot.Plot()
257 plot.title = os.path.basename(self.filename)
258 plot.curves.append(extension)
259 plot.curves.append(retraction)
264 def deflection(self):
266 self.data = self._read_columns()
267 return self.data[0][1], self.data[1][1]