6 Driver for MFP-3D 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 Modified for usage with Hooke CLI by Alberto Gomez-Casado (University of Twente, The Netherlands)
14 This program is released under the GNU General Public License version 2.
18 # Reads Igor's (Wavemetric) binary wave format, .ibw, files.
21 # Parsing proper to version 2, 3, or version 5 (see Technical notes TN003.ifn:
22 # http://mirror.optus.net.au/pub/wavemetrics/IgorPro/Technical_Notes/) and data
23 # type 2 or 4 (non complex, single or double precision vector, real values).
26 # Matlab version: R. Naud August 2008 (http://lcn.epfl.ch/~naud/Home.html)
27 # Python port: A. Seeholzer October 2008
32 # Only tested for version 2 Igor files for now, testing for 3 and 5 remains to be done.
33 # More header data could be passed back if wished. For significance of ignored bytes see
34 # the technical notes linked above.
40 from .. import libhookecurve as lhc
43 __version__='0.0.0.20100310'
46 class DataChunk(list):
47 #Dummy class to provide ext and ret methods to the data list.
51 return self[0:halflen]
57 class mfp3dDriver(lhc.Driver):
59 #Construction and other special methods
61 def __init__(self,filename):
66 self.textfile =file(filename)
67 self.binfile=file(filename,'rb')
68 #unnecesary, but some other part of the program expects these to be open
72 #TODO eliminate the need to set chunk numbers
74 self.filepath=filename
79 self.retract_velocity = None
80 self.spring_constant = None
81 self.filename = filename
83 self.filedata = open(filename,'rU')
84 self.lines = list(self.filedata.readlines())
87 self.filetype = 'mfp3d'
88 self.experiment = 'smfs'
91 def _get_data_chunk(self,whichchunk):
94 f = open(self.filename, 'rb')
95 ####################### ORDERING
96 # machine format for IEEE floating point with big-endian
98 # MacIgor use the Motorola big-endian 'b'
99 # WinIgor use Intel little-endian 'l'
100 # If the first byte in the file is non-zero, then the file is a WinIgor
101 firstbyte = struct.unpack('b', f.read(1))[0]
106 ####################### CHECK VERSION
108 version = struct.unpack(format+'h', f.read(2))[0]
109 ####################### READ DATA AND ACCOMPANYING INFO
110 if version == 2 or version == 3:
112 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.
113 noteSize = struct.unpack(format+'i', f.read(4))[0] # The size of the note text.
115 formulaSize = struct.unpack(format+'i', f.read(4))[0]
116 pictSize = struct.unpack(format+'i', f.read(4))[0] # Reserved. Write zero. Ignore on read.
117 checksum = struct.unpack(format+'H', f.read(2))[0] # Checksum over this header and the wave header.
119 dtype = struct.unpack(format+'h', f.read(2))[0]
121 dtype = numpy.float32(.0).dtype
123 dtype = numpy.double(.0).dtype
125 assert False, "Wave is of type '%i', not supported" % dtype
126 dtype = dtype.newbyteorder(format)
128 ignore = f.read(4) # 1 uint32
129 bname = self._flatten(struct.unpack(format+'20c', f.read(20)))
130 ignore = f.read(4) # 2 int16
131 ignore = f.read(4) # 1 uint32
132 dUnits = self._flatten(struct.unpack(format+'4c', f.read(4)))
133 xUnits = self._flatten(struct.unpack(format+'4c', f.read(4)))
134 npnts = struct.unpack(format+'i', f.read(4))[0]
135 amod = struct.unpack(format+'h', f.read(2))[0]
136 dx = struct.unpack(format+'d', f.read(8))[0]
137 x0 = struct.unpack(format+'d', f.read(8))[0]
138 ignore = f.read(4) # 2 int16
139 fsValid = struct.unpack(format+'h', f.read(2))[0]
140 topFullScale = struct.unpack(format+'d', f.read(8))[0]
141 botFullScale = struct.unpack(format+'d', f.read(8))[0]
142 ignore = f.read(16) # 16 int8
143 modDate = struct.unpack(format+'I', f.read(4))[0]
144 ignore = f.read(4) # 1 uint32
145 # Numpy algorithm works a lot faster than struct.unpack
146 data = numpy.fromfile(f, dtype, npnts)
150 checksum = struct.unpack(format+'H', f.read(2))[0] # Checksum over this header and the wave header.
151 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.
152 formulaSize = struct.unpack(format+'i', f.read(4))[0]
153 noteSize = struct.unpack(format+'i', f.read(4))[0] # The size of the note text.
154 dataEUnitsSize = struct.unpack(format+'i', f.read(4))[0]
155 dimEUnitsSize = struct.unpack(format+'4i', f.read(16))
156 dimLabelsSize = struct.unpack(format+'4i', f.read(16))
157 sIndicesSize = struct.unpack(format+'i', f.read(4))[0]
158 optionSize1 = struct.unpack(format+'i', f.read(4))[0]
159 optionSize2 = struct.unpack(format+'i', f.read(4))[0]
163 CreationDate = struct.unpack(format+'I',f.read(4))[0]
164 modData = struct.unpack(format+'I',f.read(4))[0]
165 npnts = struct.unpack(format+'i',f.read(4))[0]
167 dtype = struct.unpack(format+'h',f.read(2))[0]
169 dtype = numpy.float32(.0).dtype
171 dtype = numpy.double(.0).dtype
173 assert False, "Wave is of type '%i', not supported" % dtype
174 dtype = dtype.newbyteorder(format)
176 ignore = f.read(2) # 1 int16
177 ignore = f.read(6) # 6 schar, SCHAR = SIGNED CHAR? ignore = fread(fid,6,'schar'); #
178 ignore = f.read(2) # 1 int16
179 bname = self._flatten(struct.unpack(format+'32c',f.read(32)))
180 ignore = f.read(4) # 1 int32
181 ignore = f.read(4) # 1 int32
182 ndims = struct.unpack(format+'4i',f.read(16)) # Number of of items in a dimension -- 0 means no data.
183 sfA = struct.unpack(format+'4d',f.read(32))
184 sfB = struct.unpack(format+'4d',f.read(32))
185 dUnits = self._flatten(struct.unpack(format+'4c',f.read(4)))
186 xUnits = self._flatten(struct.unpack(format+'16c',f.read(16)))
187 fsValid = struct.unpack(format+'h',f.read(2))
188 whpad3 = struct.unpack(format+'h',f.read(2))
189 ignore = f.read(16) # 2 double
190 ignore = f.read(40) # 10 int32
191 ignore = f.read(64) # 16 int32
192 ignore = f.read(6) # 3 int16
193 ignore = f.read(2) # 2 char
194 ignore = f.read(4) # 1 int32
195 ignore = f.read(4) # 2 int16
196 ignore = f.read(4) # 1 int32
197 ignore = f.read(8) # 2 int32
199 data = numpy.fromfile(f, dtype, npnts)
200 note_str = f.read(noteSize)
201 note_lines = note_str.split('\r')
203 for line in note_lines:
205 key, value = line.split(':', 1)
206 self.note[key] = value
207 self.retract_velocity = float(self.note['Velocity'])
208 self.spring_constant = float(self.note['SpringConstant'])
210 assert False, "Fileversion is of type '%i', not supported" % dtype
215 #we have 3 columns: deflection, LVDT, raw
216 #TODO detect which is each one
219 deflection = data[count:2 * count]
220 #every column contains data for extension and retraction
221 #we assume the same number of points for each
222 #we could possibly extract this info from the note
225 forcechunk=deflection*self.spring_constant
228 if whichchunk==self.forcechunk:
230 if whichchunk==self.distancechunk:
236 #returns force vector
237 Kspring=self.spring_constant
238 return DataChunk([(meter*Kspring) for meter in self._deflection()])
240 def _deflection(self):
241 #for internal use (feeds _force)
242 deflect=self.data_chunks[self.forcechunk]/self.spring_constant
245 def _flatten(self, tup):
252 return DataChunk(self.data_chunks[self.distancechunk])
255 if len(self.lines) < 34:
258 name, extension = os.path.splitext(self.filename)
259 if extension == '.ibw':
260 for line in self.lines:
261 if line.startswith('ForceNote:'):
262 self.data_chunks=[self._get_data_chunk(num) for num in [0,1,2]]
271 Explicitly closes all files
273 self.textfile.close()
276 def default_plots(self):
278 creates the default PlotObject
282 main_plot=lhc.PlotObject()
283 main_plot.vectors=[[zdomain.ext(), force.ext()],[zdomain.ret(), force.ret()]]
284 main_plot.normalize_vectors()
285 main_plot.units=['meters','newton']
286 main_plot.destination=0
287 main_plot.title=self.filepath
292 def deflection(self):
293 #interface for correct plotmanip and others
294 deflectionchunk=DataChunk(self._deflection())
295 return deflectionchunk.ext(),deflectionchunk.ret()