1 # Copyright (C) 2010-2012 W. Trevor King <wking@tremily.us>
3 # This file is part of igor.
5 # igor is free software: you can redistribute it and/or modify it under the
6 # terms of the GNU Lesser General Public License as published by the Free
7 # Software Foundation, either version 3 of the License, or (at your option) any
10 # igor is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
15 # You should have received a copy of the GNU Lesser General Public License
16 # along with igor. If not, see <http://www.gnu.org/licenses/>.
18 "Read IGOR Binary Wave files into Numpy arrays."
20 # Based on WaveMetric's Technical Note 003, "Igor Binary Format"
21 # ftp://ftp.wavemetrics.net/IgorPro/Technical_Notes/TN003.zip
22 # From ftp://ftp.wavemetrics.net/IgorPro/Technical_Notes/TN000.txt
23 # We place no restrictions on copying Technical Notes, with the
24 # exception that you cannot resell them. So read, enjoy, and
25 # share. We hope IGOR Technical Notes will provide you with lots of
26 # valuable information while you are developing IGOR applications.
28 from __future__ import absolute_import
29 import array as _array
30 import struct as _struct
32 import types as _types
34 import numpy as _numpy
36 from . import LOG as _LOG
37 from .struct import Structure as _Structure
38 from .struct import DynamicStructure as _DynamicStructure
39 from .struct import Field as _Field
40 from .struct import DynamicField as _DynamicField
41 from .util import assert_null as _assert_null
42 from .util import byte_order as _byte_order
43 from .util import need_to_reorder_bytes as _need_to_reorder_bytes
44 from .util import checksum as _checksum
47 # Numpy doesn't support complex integers by default, see
48 # http://mail.python.org/pipermail/python-dev/2002-April/022408.html
49 # http://mail.scipy.org/pipermail/numpy-discussion/2007-October/029447.html
50 # So we roll our own types. See
51 # http://docs.scipy.org/doc/numpy/user/basics.rec.html
52 # http://docs.scipy.org/doc/numpy/reference/generated/numpy.dtype.html
53 complexInt8 = _numpy.dtype([('real', _numpy.int8), ('imag', _numpy.int8)])
54 complexInt16 = _numpy.dtype([('real', _numpy.int16), ('imag', _numpy.int16)])
55 complexInt32 = _numpy.dtype([('real', _numpy.int32), ('imag', _numpy.int32)])
56 complexUInt8 = _numpy.dtype([('real', _numpy.uint8), ('imag', _numpy.uint8)])
57 complexUInt16 = _numpy.dtype(
58 [('real', _numpy.uint16), ('imag', _numpy.uint16)])
59 complexUInt32 = _numpy.dtype(
60 [('real', _numpy.uint32), ('imag', _numpy.uint32)])
63 class StaticStringField (_DynamicField):
64 _null_terminated = False
65 _array_size_field = None
66 def __init__(self, *args, **kwargs):
67 if 'array' not in kwargs:
68 kwargs['array'] = True
69 super(StaticStringField, self).__init__(*args, **kwargs)
71 def post_unpack(self, parents, data):
72 wave_structure = parents[-1]
73 wave_data = self._get_structure_data(parents, data, wave_structure)
74 d = self._normalize_string(wave_data[self.name])
75 wave_data[self.name] = d
77 def _normalize_string(self, d):
78 if isinstance(d, bytes):
80 elif hasattr(d, 'tobytes'):
82 elif hasattr(d, 'tostring'): # Python 2 compatibility
86 if self._array_size_field:
89 for count in self.counts:
92 strings.append(d[start:end])
93 if self._null_terminated:
94 strings[-1] = strings[-1].split(b'\x00', 1)[0]
96 elif self._null_terminated:
97 d = d.split(b'\x00', 1)[0]
101 class NullStaticStringField (StaticStringField):
102 _null_terminated = True
105 # Begin IGOR constants and typedefs from IgorBin.h
108 TYPE_TABLE = { # (key: integer flag, value: numpy dtype)
109 0:None, # Text wave, not handled in ReadWave.c
110 1:_numpy.complex, # NT_CMPLX, makes number complex.
111 2:_numpy.float32, # NT_FP32, 32 bit fp numbers.
113 4:_numpy.float64, # NT_FP64, 64 bit fp numbers.
115 8:_numpy.int8, # NT_I8, 8 bit signed integer. Requires Igor Pro
118 0x10:_numpy.int16,# NT_I16, 16 bit integer numbers. Requires Igor
121 0x20:_numpy.int32,# NT_I32, 32 bit integer numbers. Requires Igor
124 # 0x40:None, # NT_UNSIGNED, Makes above signed integers
125 # # unsigned. Requires Igor Pro 3.0 or later.
138 BinHeader1 = _Structure( # `version` field pulled out into Wave
141 _Field('l', 'wfmSize', help='The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.'),
142 _Field('h', 'checksum', help='Checksum over this header and the wave header.'),
145 BinHeader2 = _Structure( # `version` field pulled out into Wave
148 _Field('l', 'wfmSize', help='The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.'),
149 _Field('l', 'noteSize', help='The size of the note text.'),
150 _Field('l', 'pictSize', default=0, help='Reserved. Write zero. Ignore on read.'),
151 _Field('h', 'checksum', help='Checksum over this header and the wave header.'),
154 BinHeader3 = _Structure( # `version` field pulled out into Wave
157 _Field('l', 'wfmSize', help='The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.'),
158 _Field('l', 'noteSize', help='The size of the note text.'),
159 _Field('l', 'formulaSize', help='The size of the dependency formula, if any.'),
160 _Field('l', 'pictSize', default=0, help='Reserved. Write zero. Ignore on read.'),
161 _Field('h', 'checksum', help='Checksum over this header and the wave header.'),
164 BinHeader5 = _Structure( # `version` field pulled out into Wave
167 _Field('h', 'checksum', help='Checksum over this header and the wave header.'),
168 _Field('l', 'wfmSize', help='The size of the WaveHeader5 data structure plus the wave data.'),
169 _Field('l', 'formulaSize', help='The size of the dependency formula, if any.'),
170 _Field('l', 'noteSize', help='The size of the note text.'),
171 _Field('l', 'dataEUnitsSize', help='The size of optional extended data units.'),
172 _Field('l', 'dimEUnitsSize', help='The size of optional extended dimension units.', count=MAXDIMS, array=True),
173 _Field('l', 'dimLabelsSize', help='The size of optional dimension labels.', count=MAXDIMS, array=True),
174 _Field('l', 'sIndicesSize', help='The size of string indicies if this is a text wave.'),
175 _Field('l', 'optionsSize1', default=0, help='Reserved. Write zero. Ignore on read.'),
176 _Field('l', 'optionsSize2', default=0, help='Reserved. Write zero. Ignore on read.'),
181 MAX_WAVE_NAME2 = 18 # Maximum length of wave name in version 1 and 2
182 # files. Does not include the trailing null.
183 MAX_WAVE_NAME5 = 31 # Maximum length of wave name in version 5
184 # files. Does not include the trailing null.
187 # Header to an array of waveform data.
189 # `wData` field pulled out into DynamicWaveDataField1
190 WaveHeader2 = _DynamicStructure(
193 _Field('h', 'type', help='See types (e.g. NT_FP64) above. Zero for text waves.'),
194 _Field('P', 'next', default=0, help='Used in memory only. Write zero. Ignore on read.'),
195 NullStaticStringField('c', 'bname', help='Name of wave plus trailing null.', count=MAX_WAVE_NAME2+2),
196 _Field('h', 'whVersion', default=0, help='Write 0. Ignore on read.'),
197 _Field('h', 'srcFldr', default=0, help='Used in memory only. Write zero. Ignore on read.'),
198 _Field('P', 'fileName', default=0, help='Used in memory only. Write zero. Ignore on read.'),
199 _Field('c', 'dataUnits', default=0, help='Natural data units go here - null if none.', count=MAX_UNIT_CHARS+1, array=True),
200 _Field('c', 'xUnits', default=0, help='Natural x-axis units go here - null if none.', count=MAX_UNIT_CHARS+1, array=True),
201 _Field('l', 'npnts', help='Number of data points in wave.'),
202 _Field('h', 'aModified', default=0, help='Used in memory only. Write zero. Ignore on read.'),
203 _Field('d', 'hsA', help='X value for point p = hsA*p + hsB'),
204 _Field('d', 'hsB', help='X value for point p = hsA*p + hsB'),
205 _Field('h', 'wModified', default=0, help='Used in memory only. Write zero. Ignore on read.'),
206 _Field('h', 'swModified', default=0, help='Used in memory only. Write zero. Ignore on read.'),
207 _Field('h', 'fsValid', help='True if full scale values have meaning.'),
208 _Field('d', 'topFullScale', help='The min full scale value for wave.'), # sic, 'min' should probably be 'max'
209 _Field('d', 'botFullScale', help='The min full scale value for wave.'),
210 _Field('c', 'useBits', default=0, help='Used in memory only. Write zero. Ignore on read.'),
211 _Field('c', 'kindBits', default=0, help='Reserved. Write zero. Ignore on read.'),
212 _Field('P', 'formula', default=0, help='Used in memory only. Write zero. Ignore on read.'),
213 _Field('l', 'depID', default=0, help='Used in memory only. Write zero. Ignore on read.'),
214 _Field('L', 'creationDate', help='DateTime of creation. Not used in version 1 files.'),
215 _Field('c', 'wUnused', default=0, help='Reserved. Write zero. Ignore on read.', count=2, array=True),
216 _Field('L', 'modDate', help='DateTime of last modification.'),
217 _Field('P', 'waveNoteH', help='Used in memory only. Write zero. Ignore on read.'),
220 # `sIndices` pointer unset (use Wave5_data['sIndices'] instead). This
221 # field is filled in by DynamicStringIndicesDataField.
222 # `wData` field pulled out into DynamicWaveDataField5
223 WaveHeader5 = _DynamicStructure(
226 _Field('P', 'next', help='link to next wave in linked list.'),
227 _Field('L', 'creationDate', help='DateTime of creation.'),
228 _Field('L', 'modDate', help='DateTime of last modification.'),
229 _Field('l', 'npnts', help='Total number of points (multiply dimensions up to first zero).'),
230 _Field('h', 'type', help='See types (e.g. NT_FP64) above. Zero for text waves.'),
231 _Field('h', 'dLock', default=0, help='Reserved. Write zero. Ignore on read.'),
232 _Field('c', 'whpad1', default=0, help='Reserved. Write zero. Ignore on read.', count=6, array=True),
233 _Field('h', 'whVersion', default=1, help='Write 1. Ignore on read.'),
234 NullStaticStringField('c', 'bname', help='Name of wave plus trailing null.', count=MAX_WAVE_NAME5+1),
235 _Field('l', 'whpad2', default=0, help='Reserved. Write zero. Ignore on read.'),
236 _Field('P', 'dFolder', default=0, help='Used in memory only. Write zero. Ignore on read.'),
237 # Dimensioning info. [0] == rows, [1] == cols etc
238 _Field('l', 'nDim', help='Number of of items in a dimension -- 0 means no data.', count=MAXDIMS, array=True),
239 _Field('d', 'sfA', help='Index value for element e of dimension d = sfA[d]*e + sfB[d].', count=MAXDIMS, array=True),
240 _Field('d', 'sfB', help='Index value for element e of dimension d = sfA[d]*e + sfB[d].', count=MAXDIMS, array=True),
242 _Field('c', 'dataUnits', default=0, help='Natural data units go here - null if none.', count=MAX_UNIT_CHARS+1, array=True),
243 _Field('c', 'dimUnits', default=0, help='Natural dimension units go here - null if none.', count=(MAXDIMS, MAX_UNIT_CHARS+1), array=True),
244 _Field('h', 'fsValid', help='TRUE if full scale values have meaning.'),
245 _Field('h', 'whpad3', default=0, help='Reserved. Write zero. Ignore on read.'),
246 _Field('d', 'topFullScale', help='The max and max full scale value for wave'), # sic, probably "max and min"
247 _Field('d', 'botFullScale', help='The max and max full scale value for wave.'), # sic, probably "max and min"
248 _Field('P', 'dataEUnits', default=0, help='Used in memory only. Write zero. Ignore on read.'),
249 _Field('P', 'dimEUnits', default=0, help='Used in memory only. Write zero. Ignore on read.', count=MAXDIMS, array=True),
250 _Field('P', 'dimLabels', default=0, help='Used in memory only. Write zero. Ignore on read.', count=MAXDIMS, array=True),
251 _Field('P', 'waveNoteH', default=0, help='Used in memory only. Write zero. Ignore on read.'),
252 _Field('l', 'whUnused', default=0, help='Reserved. Write zero. Ignore on read.', count=16, array=True),
253 # The following stuff is considered private to Igor.
254 _Field('h', 'aModified', default=0, help='Used in memory only. Write zero. Ignore on read.'),
255 _Field('h', 'wModified', default=0, help='Used in memory only. Write zero. Ignore on read.'),
256 _Field('h', 'swModified', default=0, help='Used in memory only. Write zero. Ignore on read.'),
257 _Field('c', 'useBits', default=0, help='Used in memory only. Write zero. Ignore on read.'),
258 _Field('c', 'kindBits', default=0, help='Reserved. Write zero. Ignore on read.'),
259 _Field('P', 'formula', default=0, help='Used in memory only. Write zero. Ignore on read.'),
260 _Field('l', 'depID', default=0, help='Used in memory only. Write zero. Ignore on read.'),
261 _Field('h', 'whpad4', default=0, help='Reserved. Write zero. Ignore on read.'),
262 _Field('h', 'srcFldr', default=0, help='Used in memory only. Write zero. Ignore on read.'),
263 _Field('P', 'fileName', default=0, help='Used in memory only. Write zero. Ignore on read.'),
264 _Field('P', 'sIndices', default=0, help='Used in memory only. Write zero. Ignore on read.'),
268 class DynamicWaveDataField1 (_DynamicField):
269 def pre_pack(self, parents, data):
270 raise NotImplementedError()
272 def pre_unpack(self, parents, data):
273 full_structure = parents[0]
274 wave_structure = parents[-1]
275 wave_header_structure = wave_structure.fields[1].format
276 wave_data = self._get_structure_data(parents, data, wave_structure)
277 version = data['version']
278 bin_header = wave_data['bin_header']
279 wave_header = wave_data['wave_header']
281 self.count = wave_header['npnts']
282 self.data_size = self._get_size(bin_header, wave_header_structure.size)
284 type_ = TYPE_TABLE.get(wave_header['type'], None)
286 self.shape = self._get_shape(bin_header, wave_header)
288 type_ = _numpy.dtype('S1')
289 self.shape = (self.data_size,)
290 # dtype() wrapping to avoid numpy.generic and
291 # getset_descriptor issues with the builtin numpy types
292 # (e.g. int32). It has no effect on our local complex
294 self.dtype = _numpy.dtype(type_).newbyteorder(
295 wave_structure.byte_order)
298 bin_header['formulaSize'] > 0 and
299 self.data_size == 0):
302 Igor Pro 2.00 included support for dependency formulae. If
303 a wave was governed by a dependency formula then the
304 actual wave data was not written to disk for that wave,
305 because on loading the wave Igor could recalculate the
306 data. However,this prevented the wave from being loaded
307 into an experiment other than the original
308 experiment. Consequently, in a version of Igor Pro 3.0x,
309 we changed it so that the wave data was written even if
310 the wave was governed by a dependency formula. When
311 reading a binary wave file, you can detect that the wave
312 file does not contain the wave data by examining the
313 wfmSize, formulaSize and npnts fields. If npnts is greater
314 than zero and formulaSize is greater than zero and
315 the waveDataSize as calculated above is zero, then this is
316 a file governed by a dependency formula that was written
317 without the actual wave data.
320 elif TYPE_TABLE.get(wave_header['type'], None) is not None:
321 assert self.data_size == self.count * self.dtype.itemsize, (
322 self.data_size, self.count, self.dtype.itemsize, self.dtype)
324 assert self.data_size >= 0, (
325 bin_header['wfmSize'], wave_header_structure.size)
327 def _get_size(self, bin_header, wave_header_size):
328 return bin_header['wfmSize'] - wave_header_size - 16
330 def _get_shape(self, bin_header, wave_header):
333 def unpack(self, stream):
334 data_b = stream.read(self.data_size)
336 data = _numpy.ndarray(
344 'could not reshape data from {} to {}'.format(
350 class DynamicWaveDataField5 (DynamicWaveDataField1):
351 "Adds support for multidimensional data."
352 def _get_size(self, bin_header, wave_header_size):
353 return bin_header['wfmSize'] - wave_header_size
355 def _get_shape(self, bin_header, wave_header):
356 return [n for n in wave_header['nDim'] if n > 0] or (0,)
359 # End IGOR constants and typedefs from IgorBin.h
362 class DynamicStringField (StaticStringField):
365 def pre_unpack(self, parents, data):
366 size = self._get_size_data(parents, data)
367 if self._array_size_field:
369 self.count = sum(self.counts)
374 def _get_size_data(self, parents, data):
375 wave_structure = parents[-1]
376 wave_data = self._get_structure_data(parents, data, wave_structure)
377 bin_header = wave_data['bin_header']
378 return bin_header[self._size_field]
381 class DynamicWaveNoteField (DynamicStringField):
382 _size_field = 'noteSize'
385 class DynamicDependencyFormulaField (DynamicStringField):
386 """Optional wave dependency formula
388 Excerpted from TN003:
390 A wave has a dependency formula if it has been bound by a
391 statement such as "wave0 := sin(x)". In this example, the
392 dependency formula is "sin(x)". The formula is stored with
393 no trailing null byte.
395 _size_field = 'formulaSize'
396 # Except when it is stored with a trailing null byte :p. See, for
397 # example, test/data/mac-version3Dependent.ibw.
398 _null_terminated = True
401 class DynamicDataUnitsField (DynamicStringField):
402 """Optional extended data units data
404 Excerpted from TN003:
406 dataUnits - Present in versions 1, 2, 3, 5. The dataUnits field
407 stores the units for the data represented by the wave. It is a C
408 string terminated with a null character. This field supports
409 units of 0 to 3 bytes. In version 1, 2 and 3 files, longer units
410 can not be represented. In version 5 files, longer units can be
411 stored using the optional extended data units section of the
414 _size_field = 'dataEUnitsSize'
417 class DynamicDimensionUnitsField (DynamicStringField):
418 """Optional extended dimension units data
420 Excerpted from TN003:
422 xUnits - Present in versions 1, 2, 3. The xUnits field stores the
423 X units for a wave. It is a C string terminated with a null
424 character. This field supports units of 0 to 3 bytes. In
425 version 1, 2 and 3 files, longer units can not be represented.
427 dimUnits - Present in version 5 only. This field is an array of 4
428 strings, one for each possible wave dimension. Each string
429 supports units of 0 to 3 bytes. Longer units can be stored using
430 the optional extended dimension units section of the file.
432 _size_field = 'dimEUnitsSize'
433 _array_size_field = True
436 class DynamicLabelsField (DynamicStringField):
437 """Optional dimension label data
441 If the wave has dimension labels for dimension d then the
442 dimLabelsSize[d] field of the BinHeader5 structure will be
445 A wave will have dimension labels if a SetDimLabel command has
448 A 3 point 1D wave has 4 dimension labels. The first dimension
449 label is the label for the dimension as a whole. The next three
450 dimension labels are the labels for rows 0, 1, and 2. When Igor
451 writes dimension labels to disk, it writes each dimension label as
452 a C string (null-terminated) in a field of 32 bytes.
454 _size_field = 'dimLabelsSize'
455 _array_size_field = True
457 def post_unpack(self, parents, data):
458 wave_structure = parents[-1]
459 wave_data = self._get_structure_data(parents, data, wave_structure)
460 bin_header = wave_data['bin_header']
461 d = b''.join(wave_data[self.name])
464 for size in bin_header[self._size_field]:
467 dim_data = d[start:end]
468 # split null-delimited strings
469 labels = dim_data.split(b'\x00')
473 dim_labels.append(labels)
474 wave_data[self.name] = dim_labels
477 class DynamicStringIndicesDataField (_DynamicField):
478 """String indices used for text waves only
480 def pre_pack(self, parents, data):
481 raise NotImplementedError()
483 def pre_unpack(self, parents, data):
484 wave_structure = parents[-1]
485 wave_data = self._get_structure_data(parents, data, wave_structure)
486 bin_header = wave_data['bin_header']
487 wave_header = wave_data['wave_header']
488 self.string_indices_size = bin_header['sIndicesSize']
489 self.count = self.string_indices_size // 4
490 if self.count: # make sure we're in a text wave
491 assert TYPE_TABLE[wave_header['type']] is None, wave_header
494 def post_unpack(self, parents, data):
497 wave_structure = parents[-1]
498 wave_data = self._get_structure_data(parents, data, wave_structure)
499 wave_header = wave_data['wave_header']
500 wdata = wave_data['wData']
503 for i,offset in enumerate(wave_data['sIndices']):
505 chars = wdata[start:offset]
506 strings.append(b''.join(chars))
508 elif offset == start:
511 raise ValueError((offset, wave_data['sIndices']))
512 wdata = _numpy.array(strings)
513 shape = [n for n in wave_header['nDim'] if n > 0] or (0,)
515 wdata = wdata.reshape(shape)
518 'could not reshape strings from {} to {}'.format(
521 wave_data['wData'] = wdata
524 class DynamicVersionField (_DynamicField):
525 def pre_pack(self, parents, byte_order):
526 raise NotImplementedError()
528 def post_unpack(self, parents, data):
529 wave_structure = parents[-1]
530 wave_data = self._get_structure_data(parents, data, wave_structure)
531 version = wave_data['version']
532 if wave_structure.byte_order in '@=':
533 need_to_reorder_bytes = _need_to_reorder_bytes(version)
534 wave_structure.byte_order = _byte_order(need_to_reorder_bytes)
536 'get byte order from version: {} (reorder? {})'.format(
537 wave_structure.byte_order, need_to_reorder_bytes))
539 need_to_reorder_bytes = False
541 old_format = wave_structure.fields[-1].format
543 wave_structure.fields[-1].format = Wave1
545 wave_structure.fields[-1].format = Wave2
547 wave_structure.fields[-1].format = Wave3
549 wave_structure.fields[-1].format = Wave5
550 elif not need_to_reorder_bytes:
552 'invalid binary wave version: {}'.format(version))
554 if wave_structure.fields[-1].format != old_format:
555 _LOG.debug('change wave headers from {} to {}'.format(
556 old_format, wave_structure.fields[-1].format))
557 wave_structure.setup()
558 elif need_to_reorder_bytes:
559 wave_structure.setup()
561 # we might need to unpack again with the new byte order
562 return need_to_reorder_bytes
565 class DynamicWaveField (_DynamicField):
566 def post_unpack(self, parents, data):
568 raise NotImplementedError() # TODO
569 checksum_size = bin.size + wave.size
570 wave_structure = parents[-1]
572 # Version 5 checksum does not include the wData field.
574 c = _checksum(b, parents[-1].byte_order, 0, checksum_size)
577 ('This does not appear to be a valid Igor binary wave file. '
578 'Error in checksum: should be 0, is {}.').format(c))
580 Wave1 = _DynamicStructure(
583 _Field(BinHeader1, 'bin_header', help='Binary wave header'),
584 _Field(WaveHeader2, 'wave_header', help='Wave header'),
585 DynamicWaveDataField1('f', 'wData', help='The start of the array of waveform data.', count=0, array=True),
588 Wave2 = _DynamicStructure(
591 _Field(BinHeader2, 'bin_header', help='Binary wave header'),
592 _Field(WaveHeader2, 'wave_header', help='Wave header'),
593 DynamicWaveDataField1('f', 'wData', help='The start of the array of waveform data.', count=0, array=True),
594 _Field('x', 'padding', help='16 bytes of padding in versions 2 and 3.', count=16, array=True),
595 DynamicWaveNoteField('c', 'note', help='Optional wave note data', count=0, array=True),
598 Wave3 = _DynamicStructure(
601 _Field(BinHeader3, 'bin_header', help='Binary wave header'),
602 _Field(WaveHeader2, 'wave_header', help='Wave header'),
603 DynamicWaveDataField1('f', 'wData', help='The start of the array of waveform data.', count=0, array=True),
604 _Field('x', 'padding', help='16 bytes of padding in versions 2 and 3.', count=16, array=True),
605 DynamicWaveNoteField('c', 'note', help='Optional wave note data', count=0, array=True),
606 DynamicDependencyFormulaField('c', 'formula', help='Optional wave dependency formula', count=0, array=True),
609 Wave5 = _DynamicStructure(
612 _Field(BinHeader5, 'bin_header', help='Binary wave header'),
613 _Field(WaveHeader5, 'wave_header', help='Wave header'),
614 DynamicWaveDataField5('f', 'wData', help='The start of the array of waveform data.', count=0, array=True),
615 DynamicDependencyFormulaField('c', 'formula', help='Optional wave dependency formula.', count=0, array=True),
616 DynamicWaveNoteField('c', 'note', help='Optional wave note data.', count=0, array=True),
617 DynamicDataUnitsField('c', 'data_units', help='Optional extended data units data.', count=0, array=True),
618 DynamicDimensionUnitsField('c', 'dimension_units', help='Optional dimension label data', count=0, array=True),
619 DynamicLabelsField('c', 'labels', help="Optional dimension label data", count=0, array=True),
620 DynamicStringIndicesDataField('P', 'sIndices', help='Dynamic string indices for text waves.', count=0, array=True),
623 Wave = _DynamicStructure(
626 DynamicVersionField('h', 'version', help='Version number for backwards compatibility.'),
627 DynamicWaveField(Wave1, 'wave', help='The rest of the wave data.'),
632 if hasattr(filename, 'read'):
633 f = filename # filename is actually a stream object
635 f = open(filename, 'rb')
637 Wave.byte_order = '='
639 data = Wave.unpack_stream(f)
641 if not hasattr(filename, 'read'):
648 raise NotImplementedError