1 # Copyright (C) 2010 W. Trevor King <wking@drexel.edu>
3 # This file is part of Hooke.
5 # Hooke is free software: you can redistribute it and/or modify it
6 # under the terms of the GNU Lesser General Public License as
7 # published by the Free Software Foundation, either version 3 of the
8 # License, or (at your option) any later version.
10 # Hooke is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
13 # Public License for more details.
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with Hooke. If not, see
17 # <http://www.gnu.org/licenses/>.
19 "Read IGOR Binary Wave files into Numpy arrays."
21 # Based on WaveMetric's Technical Note 003, "Igor Binary Format"
22 # ftp://ftp.wavemetrics.net/IgorPro/Technical_Notes/TN003.zip
23 # From ftp://ftp.wavemetrics.net/IgorPro/Technical_Notes/TN000.txt
24 # We place no restrictions on copying Technical Notes, with the
25 # exception that you cannot resell them. So read, enjoy, and
26 # share. We hope IGOR Technical Notes will provide you with lots of
27 # valuable information while you are developing IGOR applications.
29 from __future__ import absolute_import
30 import array as _array
31 import struct as _struct
33 import types as _types
35 import numpy as _numpy
37 from . import LOG as _LOG
38 from .struct import Structure as _Structure
39 from .struct import DynamicStructure as _DynamicStructure
40 from .struct import Field as _Field
41 from .struct import DynamicField as _DynamicField
42 from .util import assert_null as _assert_null
43 from .util import byte_order as _byte_order
44 from .util import need_to_reorder_bytes as _need_to_reorder_bytes
45 from .util import checksum as _checksum
48 # Numpy doesn't support complex integers by default, see
49 # http://mail.python.org/pipermail/python-dev/2002-April/022408.html
50 # http://mail.scipy.org/pipermail/numpy-discussion/2007-October/029447.html
51 # So we roll our own types. See
52 # http://docs.scipy.org/doc/numpy/user/basics.rec.html
53 # http://docs.scipy.org/doc/numpy/reference/generated/numpy.dtype.html
54 complexInt8 = _numpy.dtype([('real', _numpy.int8), ('imag', _numpy.int8)])
55 complexInt16 = _numpy.dtype([('real', _numpy.int16), ('imag', _numpy.int16)])
56 complexInt32 = _numpy.dtype([('real', _numpy.int32), ('imag', _numpy.int32)])
57 complexUInt8 = _numpy.dtype([('real', _numpy.uint8), ('imag', _numpy.uint8)])
58 complexUInt16 = _numpy.dtype(
59 [('real', _numpy.uint16), ('imag', _numpy.uint16)])
60 complexUInt32 = _numpy.dtype(
61 [('real', _numpy.uint32), ('imag', _numpy.uint32)])
64 class StaticStringField (_DynamicField):
65 _null_terminated = False
66 _array_size_field = None
67 def post_unpack(self, parents, data):
68 wave_structure = parents[-1]
69 wave_data = self._get_structure_data(parents, data, wave_structure)
70 d = self._normalize_string(wave_data[self.name])
71 wave_data[self.name] = d
73 def _normalize_string(self, d):
74 if hasattr(d, 'tostring'):
78 if self._array_size_field:
81 for count in self.counts:
84 strings.append(d[start:end])
85 if self._null_terminated:
86 strings[-1] = strings[-1].split('\x00', 1)[0]
88 elif self._null_terminated:
89 d = d.split('\x00', 1)[0]
93 class NullStaticStringField (StaticStringField):
94 _null_terminated = True
97 # Begin IGOR constants and typedefs from IgorBin.h
100 TYPE_TABLE = { # (key: integer flag, value: numpy dtype)
101 0:None, # Text wave, not handled in ReadWave.c
102 1:_numpy.complex, # NT_CMPLX, makes number complex.
103 2:_numpy.float32, # NT_FP32, 32 bit fp numbers.
105 4:_numpy.float64, # NT_FP64, 64 bit fp numbers.
107 8:_numpy.int8, # NT_I8, 8 bit signed integer. Requires Igor Pro
110 0x10:_numpy.int16,# NT_I16, 16 bit integer numbers. Requires Igor
113 0x20:_numpy.int32,# NT_I32, 32 bit integer numbers. Requires Igor
116 # 0x40:None, # NT_UNSIGNED, Makes above signed integers
117 # # unsigned. Requires Igor Pro 3.0 or later.
130 BinHeader1 = _Structure( # `version` field pulled out into Wave
133 _Field('l', 'wfmSize', help='The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.'),
134 _Field('h', 'checksum', help='Checksum over this header and the wave header.'),
137 BinHeader2 = _Structure( # `version` field pulled out into Wave
140 _Field('l', 'wfmSize', help='The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.'),
141 _Field('l', 'noteSize', help='The size of the note text.'),
142 _Field('l', 'pictSize', default=0, help='Reserved. Write zero. Ignore on read.'),
143 _Field('h', 'checksum', help='Checksum over this header and the wave header.'),
146 BinHeader3 = _Structure( # `version` field pulled out into Wave
149 _Field('l', 'wfmSize', help='The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.'),
150 _Field('l', 'noteSize', help='The size of the note text.'),
151 _Field('l', 'formulaSize', help='The size of the dependency formula, if any.'),
152 _Field('l', 'pictSize', default=0, help='Reserved. Write zero. Ignore on read.'),
153 _Field('h', 'checksum', help='Checksum over this header and the wave header.'),
156 BinHeader5 = _Structure( # `version` field pulled out into Wave
159 _Field('h', 'checksum', help='Checksum over this header and the wave header.'),
160 _Field('l', 'wfmSize', help='The size of the WaveHeader5 data structure plus the wave data.'),
161 _Field('l', 'formulaSize', help='The size of the dependency formula, if any.'),
162 _Field('l', 'noteSize', help='The size of the note text.'),
163 _Field('l', 'dataEUnitsSize', help='The size of optional extended data units.'),
164 _Field('l', 'dimEUnitsSize', help='The size of optional extended dimension units.', count=MAXDIMS),
165 _Field('l', 'dimLabelsSize', help='The size of optional dimension labels.', count=MAXDIMS),
166 _Field('l', 'sIndicesSize', help='The size of string indicies if this is a text wave.'),
167 _Field('l', 'optionsSize1', default=0, help='Reserved. Write zero. Ignore on read.'),
168 _Field('l', 'optionsSize2', default=0, help='Reserved. Write zero. Ignore on read.'),
173 MAX_WAVE_NAME2 = 18 # Maximum length of wave name in version 1 and 2
174 # files. Does not include the trailing null.
175 MAX_WAVE_NAME5 = 31 # Maximum length of wave name in version 5
176 # files. Does not include the trailing null.
179 # Header to an array of waveform data.
181 # `wData` field pulled out into DynamicWaveDataField1
182 WaveHeader2 = _DynamicStructure(
185 _Field('h', 'type', help='See types (e.g. NT_FP64) above. Zero for text waves.'),
186 _Field('P', 'next', default=0, help='Used in memory only. Write zero. Ignore on read.'),
187 NullStaticStringField('c', 'bname', help='Name of wave plus trailing null.', count=MAX_WAVE_NAME2+2),
188 _Field('h', 'whVersion', default=0, help='Write 0. Ignore on read.'),
189 _Field('h', 'srcFldr', default=0, help='Used in memory only. Write zero. Ignore on read.'),
190 _Field('P', 'fileName', default=0, help='Used in memory only. Write zero. Ignore on read.'),
191 _Field('c', 'dataUnits', default=0, help='Natural data units go here - null if none.', count=MAX_UNIT_CHARS+1),
192 _Field('c', 'xUnits', default=0, help='Natural x-axis units go here - null if none.', count=MAX_UNIT_CHARS+1),
193 _Field('l', 'npnts', help='Number of data points in wave.'),
194 _Field('h', 'aModified', default=0, help='Used in memory only. Write zero. Ignore on read.'),
195 _Field('d', 'hsA', help='X value for point p = hsA*p + hsB'),
196 _Field('d', 'hsB', help='X value for point p = hsA*p + hsB'),
197 _Field('h', 'wModified', default=0, help='Used in memory only. Write zero. Ignore on read.'),
198 _Field('h', 'swModified', default=0, help='Used in memory only. Write zero. Ignore on read.'),
199 _Field('h', 'fsValid', help='True if full scale values have meaning.'),
200 _Field('d', 'topFullScale', help='The min full scale value for wave.'), # sic, 'min' should probably be 'max'
201 _Field('d', 'botFullScale', help='The min full scale value for wave.'),
202 _Field('c', 'useBits', default=0, help='Used in memory only. Write zero. Ignore on read.'),
203 _Field('c', 'kindBits', default=0, help='Reserved. Write zero. Ignore on read.'),
204 _Field('P', 'formula', default=0, help='Used in memory only. Write zero. Ignore on read.'),
205 _Field('l', 'depID', default=0, help='Used in memory only. Write zero. Ignore on read.'),
206 _Field('L', 'creationDate', help='DateTime of creation. Not used in version 1 files.'),
207 _Field('c', 'wUnused', default=0, help='Reserved. Write zero. Ignore on read.', count=2),
208 _Field('L', 'modDate', help='DateTime of last modification.'),
209 _Field('P', 'waveNoteH', help='Used in memory only. Write zero. Ignore on read.'),
212 # `sIndices` pointer unset (use Wave5_data['sIndices'] instead). This
213 # field is filled in by DynamicStringIndicesDataField.
214 # `wData` field pulled out into DynamicWaveDataField5
215 WaveHeader5 = _DynamicStructure(
218 _Field('P', 'next', help='link to next wave in linked list.'),
219 _Field('L', 'creationDate', help='DateTime of creation.'),
220 _Field('L', 'modDate', help='DateTime of last modification.'),
221 _Field('l', 'npnts', help='Total number of points (multiply dimensions up to first zero).'),
222 _Field('h', 'type', help='See types (e.g. NT_FP64) above. Zero for text waves.'),
223 _Field('h', 'dLock', default=0, help='Reserved. Write zero. Ignore on read.'),
224 _Field('c', 'whpad1', default=0, help='Reserved. Write zero. Ignore on read.', count=6),
225 _Field('h', 'whVersion', default=1, help='Write 1. Ignore on read.'),
226 NullStaticStringField('c', 'bname', help='Name of wave plus trailing null.', count=MAX_WAVE_NAME5+1),
227 _Field('l', 'whpad2', default=0, help='Reserved. Write zero. Ignore on read.'),
228 _Field('P', 'dFolder', default=0, help='Used in memory only. Write zero. Ignore on read.'),
229 # Dimensioning info. [0] == rows, [1] == cols etc
230 _Field('l', 'nDim', help='Number of of items in a dimension -- 0 means no data.', count=MAXDIMS),
231 _Field('d', 'sfA', help='Index value for element e of dimension d = sfA[d]*e + sfB[d].', count=MAXDIMS),
232 _Field('d', 'sfB', help='Index value for element e of dimension d = sfA[d]*e + sfB[d].', count=MAXDIMS),
234 _Field('c', 'dataUnits', default=0, help='Natural data units go here - null if none.', count=MAX_UNIT_CHARS+1),
235 _Field('c', 'dimUnits', default=0, help='Natural dimension units go here - null if none.', count=(MAXDIMS, MAX_UNIT_CHARS+1)),
236 _Field('h', 'fsValid', help='TRUE if full scale values have meaning.'),
237 _Field('h', 'whpad3', default=0, help='Reserved. Write zero. Ignore on read.'),
238 _Field('d', 'topFullScale', help='The max and max full scale value for wave'), # sic, probably "max and min"
239 _Field('d', 'botFullScale', help='The max and max full scale value for wave.'), # sic, probably "max and min"
240 _Field('P', 'dataEUnits', default=0, help='Used in memory only. Write zero. Ignore on read.'),
241 _Field('P', 'dimEUnits', default=0, help='Used in memory only. Write zero. Ignore on read.', count=MAXDIMS),
242 _Field('P', 'dimLabels', default=0, help='Used in memory only. Write zero. Ignore on read.', count=MAXDIMS),
243 _Field('P', 'waveNoteH', default=0, help='Used in memory only. Write zero. Ignore on read.'),
244 _Field('l', 'whUnused', default=0, help='Reserved. Write zero. Ignore on read.', count=16),
245 # The following stuff is considered private to Igor.
246 _Field('h', 'aModified', default=0, help='Used in memory only. Write zero. Ignore on read.'),
247 _Field('h', 'wModified', default=0, help='Used in memory only. Write zero. Ignore on read.'),
248 _Field('h', 'swModified', default=0, help='Used in memory only. Write zero. Ignore on read.'),
249 _Field('c', 'useBits', default=0, help='Used in memory only. Write zero. Ignore on read.'),
250 _Field('c', 'kindBits', default=0, help='Reserved. Write zero. Ignore on read.'),
251 _Field('P', 'formula', default=0, help='Used in memory only. Write zero. Ignore on read.'),
252 _Field('l', 'depID', default=0, help='Used in memory only. Write zero. Ignore on read.'),
253 _Field('h', 'whpad4', default=0, help='Reserved. Write zero. Ignore on read.'),
254 _Field('h', 'srcFldr', default=0, help='Used in memory only. Write zero. Ignore on read.'),
255 _Field('P', 'fileName', default=0, help='Used in memory only. Write zero. Ignore on read.'),
256 _Field('P', 'sIndices', default=0, help='Used in memory only. Write zero. Ignore on read.'),
260 class DynamicWaveDataField1 (_DynamicField):
261 def pre_pack(self, parents, data):
262 raise NotImplementedError()
264 def pre_unpack(self, parents, data):
265 full_structure = parents[0]
266 wave_structure = parents[-1]
267 wave_header_structure = wave_structure.fields[1].format
268 wave_data = self._get_structure_data(parents, data, wave_structure)
269 version = data['version']
270 bin_header = wave_data['bin_header']
271 wave_header = wave_data['wave_header']
273 self.count = wave_header['npnts']
274 self.data_size = self._get_size(bin_header, wave_header_structure.size)
276 type_ = TYPE_TABLE.get(wave_header['type'], None)
278 self.shape = self._get_shape(bin_header, wave_header)
280 type_ = _numpy.dtype('S1')
281 self.shape = (self.data_size,)
282 # dtype() wrapping to avoid numpy.generic and
283 # getset_descriptor issues with the builtin numpy types
284 # (e.g. int32). It has no effect on our local complex
286 self.dtype = _numpy.dtype(type_).newbyteorder(
287 wave_structure.byte_order)
290 bin_header['formulaSize'] > 0 and
291 self.data_size == 0):
294 Igor Pro 2.00 included support for dependency formulae. If
295 a wave was governed by a dependency formula then the
296 actual wave data was not written to disk for that wave,
297 because on loading the wave Igor could recalculate the
298 data. However,this prevented the wave from being loaded
299 into an experiment other than the original
300 experiment. Consequently, in a version of Igor Pro 3.0x,
301 we changed it so that the wave data was written even if
302 the wave was governed by a dependency formula. When
303 reading a binary wave file, you can detect that the wave
304 file does not contain the wave data by examining the
305 wfmSize, formulaSize and npnts fields. If npnts is greater
306 than zero and formulaSize is greater than zero and
307 the waveDataSize as calculated above is zero, then this is
308 a file governed by a dependency formula that was written
309 without the actual wave data.
312 elif TYPE_TABLE.get(wave_header['type'], None) is not None:
313 assert self.data_size == self.count * self.dtype.itemsize, (
314 self.data_size, self.count, self.dtype.itemsize, self.dtype)
316 assert self.data_size >= 0, (
317 bin_header['wfmSize'], wave_header_structure.size)
319 def _get_size(self, bin_header, wave_header_size):
320 return bin_header['wfmSize'] - wave_header_size - 16
322 def _get_shape(self, bin_header, wave_header):
325 def unpack(self, stream):
326 data_b = stream.read(self.data_size)
328 data = _numpy.ndarray(
336 'could not reshape data from {} to {}'.format(
342 class DynamicWaveDataField5 (DynamicWaveDataField1):
343 "Adds support for multidimensional data."
344 def _get_size(self, bin_header, wave_header_size):
345 return bin_header['wfmSize'] - wave_header_size
347 def _get_shape(self, bin_header, wave_header):
348 return [n for n in wave_header['nDim'] if n > 0] or (0,)
351 # End IGOR constants and typedefs from IgorBin.h
354 class DynamicStringField (StaticStringField):
357 def pre_unpack(self, parents, data):
358 size = self._get_size_data(parents, data)
359 if self._array_size_field:
361 self.count = sum(self.counts)
366 def _get_size_data(self, parents, data):
367 wave_structure = parents[-1]
368 wave_data = self._get_structure_data(parents, data, wave_structure)
369 bin_header = wave_data['bin_header']
370 return bin_header[self._size_field]
373 class DynamicWaveNoteField (DynamicStringField):
374 _size_field = 'noteSize'
377 class DynamicDependencyFormulaField (DynamicStringField):
378 """Optional wave dependency formula
380 Excerpted from TN003:
382 A wave has a dependency formula if it has been bound by a
383 statement such as "wave0 := sin(x)". In this example, the
384 dependency formula is "sin(x)". The formula is stored with
385 no trailing null byte.
387 _size_field = 'formulaSize'
388 # Except when it is stored with a trailing null byte :p. See, for
389 # example, test/data/mac-version3Dependent.ibw.
390 _null_terminated = True
393 class DynamicDataUnitsField (DynamicStringField):
394 """Optional extended data units data
396 Excerpted from TN003:
398 dataUnits - Present in versions 1, 2, 3, 5. The dataUnits field
399 stores the units for the data represented by the wave. It is a C
400 string terminated with a null character. This field supports
401 units of 0 to 3 bytes. In version 1, 2 and 3 files, longer units
402 can not be represented. In version 5 files, longer units can be
403 stored using the optional extended data units section of the
406 _size_field = 'dataEUnitsSize'
409 class DynamicDimensionUnitsField (DynamicStringField):
410 """Optional extended dimension units data
412 Excerpted from TN003:
414 xUnits - Present in versions 1, 2, 3. The xUnits field stores the
415 X units for a wave. It is a C string terminated with a null
416 character. This field supports units of 0 to 3 bytes. In
417 version 1, 2 and 3 files, longer units can not be represented.
419 dimUnits - Present in version 5 only. This field is an array of 4
420 strings, one for each possible wave dimension. Each string
421 supports units of 0 to 3 bytes. Longer units can be stored using
422 the optional extended dimension units section of the file.
424 _size_field = 'dimEUnitsSize'
425 _array_size_field = True
428 class DynamicLabelsField (DynamicStringField):
429 """Optional dimension label data
433 If the wave has dimension labels for dimension d then the
434 dimLabelsSize[d] field of the BinHeader5 structure will be
437 A wave will have dimension labels if a SetDimLabel command has
440 A 3 point 1D wave has 4 dimension labels. The first dimension
441 label is the label for the dimension as a whole. The next three
442 dimension labels are the labels for rows 0, 1, and 2. When Igor
443 writes dimension labels to disk, it writes each dimension label as
444 a C string (null-terminated) in a field of 32 bytes.
446 _size_field = 'dimLabelsSize'
447 _array_size_field = True
449 def post_unpack(self, parents, data):
450 wave_structure = parents[-1]
451 wave_data = self._get_structure_data(parents, data, wave_structure)
452 bin_header = wave_data['bin_header']
453 d = ''.join(wave_data[self.name])
456 for size in bin_header[self._size_field]:
459 dim_data = d[start:end]
460 # split null-delimited strings
461 labels = dim_data.split(chr(0))
465 dim_labels.append(labels)
466 wave_data[self.name] = dim_labels
469 class DynamicStringIndicesDataField (_DynamicField):
470 """String indices used for text waves only
472 def pre_pack(self, parents, data):
473 raise NotImplementedError()
475 def pre_unpack(self, parents, data):
476 wave_structure = parents[-1]
477 wave_data = self._get_structure_data(parents, data, wave_structure)
478 bin_header = wave_data['bin_header']
479 wave_header = wave_data['wave_header']
480 self.string_indices_size = bin_header['sIndicesSize']
481 self.count = self.string_indices_size / 4
482 if self.count: # make sure we're in a text wave
483 assert TYPE_TABLE[wave_header['type']] is None, wave_header
486 def post_unpack(self, parents, data):
489 wave_structure = parents[-1]
490 wave_data = self._get_structure_data(parents, data, wave_structure)
491 wave_header = wave_data['wave_header']
492 wdata = wave_data['wData']
495 for i,offset in enumerate(wave_data['sIndices']):
497 chars = wdata[start:offset]
498 strings.append(''.join(chars))
500 elif offset == start:
503 raise ValueError((offset, wave_data['sIndices']))
504 wdata = _numpy.array(strings)
505 shape = [n for n in wave_header['nDim'] if n > 0] or (0,)
507 wdata = wdata.reshape(shape)
510 'could not reshape strings from {} to {}'.format(
513 wave_data['wData'] = wdata
516 class DynamicVersionField (_DynamicField):
517 def pre_pack(self, parents, byte_order):
518 raise NotImplementedError()
520 def post_unpack(self, parents, data):
521 wave_structure = parents[-1]
522 wave_data = self._get_structure_data(parents, data, wave_structure)
523 version = wave_data['version']
524 if wave_structure.byte_order in '@=':
525 need_to_reorder_bytes = _need_to_reorder_bytes(version)
526 wave_structure.byte_order = _byte_order(need_to_reorder_bytes)
528 'get byte order from version: {} (reorder? {})'.format(
529 wave_structure.byte_order, need_to_reorder_bytes))
531 need_to_reorder_bytes = False
533 old_format = wave_structure.fields[-1].format
535 wave_structure.fields[-1].format = Wave1
537 wave_structure.fields[-1].format = Wave2
539 wave_structure.fields[-1].format = Wave3
541 wave_structure.fields[-1].format = Wave5
542 elif not need_to_reorder_bytes:
544 'invalid binary wave version: {}'.format(version))
546 if wave_structure.fields[-1].format != old_format:
547 _LOG.debug('change wave headers from {} to {}'.format(
548 old_format, wave_structure.fields[-1].format))
549 wave_structure.setup()
550 elif need_to_reorder_bytes:
551 wave_structure.setup()
553 # we might need to unpack again with the new byte order
554 return need_to_reorder_bytes
557 class DynamicWaveField (_DynamicField):
558 def post_unpack(self, parents, data):
560 raise NotImplementedError() # TODO
561 checksum_size = bin.size + wave.size
562 wave_structure = parents[-1]
564 # Version 5 checksum does not include the wData field.
566 c = _checksum(b, parents[-1].byte_order, 0, checksum_size)
569 ('This does not appear to be a valid Igor binary wave file. '
570 'Error in checksum: should be 0, is {}.').format(c))
572 Wave1 = _DynamicStructure(
575 _Field(BinHeader1, 'bin_header', help='Binary wave header'),
576 _Field(WaveHeader2, 'wave_header', help='Wave header'),
577 DynamicWaveDataField1('f', 'wData', help='The start of the array of waveform data.', count=0),
580 Wave2 = _DynamicStructure(
583 _Field(BinHeader2, '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),
586 _Field('x', 'padding', help='16 bytes of padding in versions 2 and 3.', count=16),
587 DynamicWaveNoteField('c', 'note', help='Optional wave note data', count=0),
590 Wave3 = _DynamicStructure(
593 _Field(BinHeader3, 'bin_header', help='Binary wave header'),
594 _Field(WaveHeader2, 'wave_header', help='Wave header'),
595 DynamicWaveDataField1('f', 'wData', help='The start of the array of waveform data.', count=0),
596 _Field('x', 'padding', help='16 bytes of padding in versions 2 and 3.', count=16),
597 DynamicWaveNoteField('c', 'note', help='Optional wave note data', count=0),
598 DynamicDependencyFormulaField('c', 'formula', help='Optional wave dependency formula', count=0),
601 Wave5 = _DynamicStructure(
604 _Field(BinHeader5, 'bin_header', help='Binary wave header'),
605 _Field(WaveHeader5, 'wave_header', help='Wave header'),
606 DynamicWaveDataField5('f', 'wData', help='The start of the array of waveform data.', count=0),
607 DynamicDependencyFormulaField('c', 'formula', help='Optional wave dependency formula.', count=0),
608 DynamicWaveNoteField('c', 'note', help='Optional wave note data.', count=0),
609 DynamicDataUnitsField('c', 'data_units', help='Optional extended data units data.', count=0),
610 DynamicDimensionUnitsField('c', 'dimension_units', help='Optional dimension label data', count=0),
611 DynamicLabelsField('c', 'labels', help="Optional dimension label data", count=0),
612 DynamicStringIndicesDataField('P', 'sIndices', help='Dynamic string indices for text waves.', count=0),
615 Wave = _DynamicStructure(
618 DynamicVersionField('h', 'version', help='Version number for backwards compatibility.'),
619 DynamicWaveField(Wave1, 'wave', help='The rest of the wave data.'),
624 if hasattr(filename, 'read'):
625 f = filename # filename is actually a stream object
627 f = open(filename, 'rb')
629 Wave.byte_order = '='
631 data = Wave.unpack_stream(f)
633 if not hasattr(filename, 'read'):
640 raise NotImplementedError