3 """Driver for MFP-3D files.
5 This driver reads Igor binary waves.
8 Matlab version: Richard Naud August 2008 (http://lcn.epfl.ch/~naud/)
9 Python port: A. Seeholzer October 2008
10 Hooke submission: Rolf Schmidt, Alberto Gomez-Casado 2009
14 # Reads Igor's (Wavemetric) binary wave format, .ibw, files.
17 # Parsing proper to version 2, 3, or version 5 (see Technical notes TN003.ifn:
18 # http://mirror.optus.net.au/pub/wavemetrics/IgorPro/Technical_Notes/) and data
19 # type 2 or 4 (non complex, single or double precision vector, real values).
24 # Only tested for version 2 Igor files for now, testing for 3 and 5 remains to be done.
25 # More header data could be passed back if wished. For significance of ignored bytes see
26 # the technical notes linked above.
32 from .. import curve as lhc
35 __version__='0.0.0.20100310'
38 class DataChunk(list):
39 #Dummy class to provide ext and ret methods to the data list.
43 return self[0:halflen]
49 class mfp3dDriver(lhc.Driver):
51 #Construction and other special methods
53 def __init__(self,filename):
58 self.textfile =file(filename)
59 self.binfile=file(filename,'rb')
60 #unnecesary, but some other part of the program expects these to be open
64 #TODO eliminate the need to set chunk numbers
66 self.filepath=filename
71 self.retract_velocity = None
72 self.spring_constant = None
73 self.filename = filename
75 self.filedata = open(filename,'rU')
76 self.lines = list(self.filedata.readlines())
79 self.filetype = 'mfp3d'
80 self.experiment = 'smfs'
83 def _get_data_chunk(self,whichchunk):
86 f = open(self.filename, 'rb')
87 ####################### ORDERING
88 # machine format for IEEE floating point with big-endian
90 # MacIgor use the Motorola big-endian 'b'
91 # WinIgor use Intel little-endian 'l'
92 # If the first byte in the file is non-zero, then the file is a WinIgor
93 firstbyte = struct.unpack('b', f.read(1))[0]
98 ####################### CHECK VERSION
100 version = struct.unpack(format+'h', f.read(2))[0]
101 ####################### READ DATA AND ACCOMPANYING INFO
102 if version == 2 or version == 3:
104 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.
105 noteSize = struct.unpack(format+'i', f.read(4))[0] # The size of the note text.
107 formulaSize = struct.unpack(format+'i', f.read(4))[0]
108 pictSize = struct.unpack(format+'i', f.read(4))[0] # Reserved. Write zero. Ignore on read.
109 checksum = struct.unpack(format+'H', f.read(2))[0] # Checksum over this header and the wave header.
111 dtype = struct.unpack(format+'h', f.read(2))[0]
113 dtype = numpy.float32(.0).dtype
115 dtype = numpy.double(.0).dtype
117 assert False, "Wave is of type '%i', not supported" % dtype
118 dtype = dtype.newbyteorder(format)
120 ignore = f.read(4) # 1 uint32
121 bname = self._flatten(struct.unpack(format+'20c', f.read(20)))
122 ignore = f.read(4) # 2 int16
123 ignore = f.read(4) # 1 uint32
124 dUnits = self._flatten(struct.unpack(format+'4c', f.read(4)))
125 xUnits = self._flatten(struct.unpack(format+'4c', f.read(4)))
126 npnts = struct.unpack(format+'i', f.read(4))[0]
127 amod = struct.unpack(format+'h', f.read(2))[0]
128 dx = struct.unpack(format+'d', f.read(8))[0]
129 x0 = struct.unpack(format+'d', f.read(8))[0]
130 ignore = f.read(4) # 2 int16
131 fsValid = struct.unpack(format+'h', f.read(2))[0]
132 topFullScale = struct.unpack(format+'d', f.read(8))[0]
133 botFullScale = struct.unpack(format+'d', f.read(8))[0]
134 ignore = f.read(16) # 16 int8
135 modDate = struct.unpack(format+'I', f.read(4))[0]
136 ignore = f.read(4) # 1 uint32
137 # Numpy algorithm works a lot faster than struct.unpack
138 data = numpy.fromfile(f, dtype, npnts)
142 checksum = struct.unpack(format+'H', f.read(2))[0] # Checksum over this header and the wave header.
143 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.
144 formulaSize = struct.unpack(format+'i', f.read(4))[0]
145 noteSize = struct.unpack(format+'i', f.read(4))[0] # The size of the note text.
146 dataEUnitsSize = struct.unpack(format+'i', f.read(4))[0]
147 dimEUnitsSize = struct.unpack(format+'4i', f.read(16))
148 dimLabelsSize = struct.unpack(format+'4i', f.read(16))
149 sIndicesSize = struct.unpack(format+'i', f.read(4))[0]
150 optionSize1 = struct.unpack(format+'i', f.read(4))[0]
151 optionSize2 = struct.unpack(format+'i', f.read(4))[0]
155 CreationDate = struct.unpack(format+'I',f.read(4))[0]
156 modData = struct.unpack(format+'I',f.read(4))[0]
157 npnts = struct.unpack(format+'i',f.read(4))[0]
159 dtype = struct.unpack(format+'h',f.read(2))[0]
161 dtype = numpy.float32(.0).dtype
163 dtype = numpy.double(.0).dtype
165 assert False, "Wave is of type '%i', not supported" % dtype
166 dtype = dtype.newbyteorder(format)
168 ignore = f.read(2) # 1 int16
169 ignore = f.read(6) # 6 schar, SCHAR = SIGNED CHAR? ignore = fread(fid,6,'schar'); #
170 ignore = f.read(2) # 1 int16
171 bname = self._flatten(struct.unpack(format+'32c',f.read(32)))
172 ignore = f.read(4) # 1 int32
173 ignore = f.read(4) # 1 int32
174 ndims = struct.unpack(format+'4i',f.read(16)) # Number of of items in a dimension -- 0 means no data.
175 sfA = struct.unpack(format+'4d',f.read(32))
176 sfB = struct.unpack(format+'4d',f.read(32))
177 dUnits = self._flatten(struct.unpack(format+'4c',f.read(4)))
178 xUnits = self._flatten(struct.unpack(format+'16c',f.read(16)))
179 fsValid = struct.unpack(format+'h',f.read(2))
180 whpad3 = struct.unpack(format+'h',f.read(2))
181 ignore = f.read(16) # 2 double
182 ignore = f.read(40) # 10 int32
183 ignore = f.read(64) # 16 int32
184 ignore = f.read(6) # 3 int16
185 ignore = f.read(2) # 2 char
186 ignore = f.read(4) # 1 int32
187 ignore = f.read(4) # 2 int16
188 ignore = f.read(4) # 1 int32
189 ignore = f.read(8) # 2 int32
191 data = numpy.fromfile(f, dtype, npnts)
192 note_str = f.read(noteSize)
193 note_lines = note_str.split('\r')
195 for line in note_lines:
197 key, value = line.split(':', 1)
198 self.note[key] = value
199 self.retract_velocity = float(self.note['Velocity'])
200 self.spring_constant = float(self.note['SpringConstant'])
202 assert False, "Fileversion is of type '%i', not supported" % dtype
207 #we have 3 columns: deflection, LVDT, raw
208 #TODO detect which is each one
211 deflection = data[count:2 * count]
212 #every column contains data for extension and retraction
213 #we assume the same number of points for each
214 #we could possibly extract this info from the note
217 forcechunk=deflection*self.spring_constant
220 if whichchunk==self.forcechunk:
222 if whichchunk==self.distancechunk:
228 #returns force vector
229 Kspring=self.spring_constant
230 return DataChunk([(meter*Kspring) for meter in self._deflection()])
232 def _deflection(self):
233 #for internal use (feeds _force)
234 deflect=self.data_chunks[self.forcechunk]/self.spring_constant
237 def _flatten(self, tup):
244 return DataChunk(self.data_chunks[self.distancechunk])
247 if len(self.lines) < 34:
250 name, extension = os.path.splitext(self.filename)
251 if extension == '.ibw':
252 for line in self.lines:
253 if line.startswith('ForceNote:'):
254 self.data_chunks=[self._get_data_chunk(num) for num in [0,1,2]]
263 Explicitly closes all files
265 self.textfile.close()
268 def default_plots(self):
270 creates the default PlotObject
274 main_plot=lhc.PlotObject()
275 main_plot.vectors=[[zdomain.ext(), force.ext()],[zdomain.ret(), force.ret()]]
276 main_plot.normalize_vectors()
277 main_plot.units=['meters','newton']
278 main_plot.destination=0
279 main_plot.title=self.filepath
284 def deflection(self):
285 #interface for correct plotmanip and others
286 deflectionchunk=DataChunk(self._deflection())
287 return deflectionchunk.ext(),deflectionchunk.ret()