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 post_unpack(self, parents, data):
67 wave_structure = parents[-1]
68 wave_data = self._get_structure_data(parents, data, wave_structure)
69 d = self._normalize_string(wave_data[self.name])
70 wave_data[self.name] = d
72 def _normalize_string(self, d):
73 if hasattr(d, 'tostring'):
77 if self._array_size_field:
80 for count in self.counts:
83 strings.append(d[start:end])
84 if self._null_terminated:
85 strings[-1] = strings[-1].split(b'\x00', 1)[0]
87 elif self._null_terminated:
88 d = d.split(b'\x00', 1)[0]
92 class NullStaticStringField (StaticStringField):
93 _null_terminated = True
96 # Begin IGOR constants and typedefs from IgorBin.h
99 TYPE_TABLE = { # (key: integer flag, value: numpy dtype)
100 0:None, # Text wave, not handled in ReadWave.c
101 1:_numpy.complex, # NT_CMPLX, makes number complex.
102 2:_numpy.float32, # NT_FP32, 32 bit fp numbers.
104 4:_numpy.float64, # NT_FP64, 64 bit fp numbers.
106 8:_numpy.int8, # NT_I8, 8 bit signed integer. Requires Igor Pro
109 0x10:_numpy.int16,# NT_I16, 16 bit integer numbers. Requires Igor
112 0x20:_numpy.int32,# NT_I32, 32 bit integer numbers. Requires Igor
115 # 0x40:None, # NT_UNSIGNED, Makes above signed integers
116 # # unsigned. Requires Igor Pro 3.0 or later.
129 BinHeader1 = _Structure( # `version` field pulled out into Wave
132 _Field('l', 'wfmSize', help='The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.'),
133 _Field('h', 'checksum', help='Checksum over this header and the wave header.'),
136 BinHeader2 = _Structure( # `version` field pulled out into Wave
139 _Field('l', 'wfmSize', help='The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.'),
140 _Field('l', 'noteSize', help='The size of the note text.'),
141 _Field('l', 'pictSize', default=0, help='Reserved. Write zero. Ignore on read.'),
142 _Field('h', 'checksum', help='Checksum over this header and the wave header.'),
145 BinHeader3 = _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', 'formulaSize', help='The size of the dependency formula, if any.'),
151 _Field('l', 'pictSize', default=0, help='Reserved. Write zero. Ignore on read.'),
152 _Field('h', 'checksum', help='Checksum over this header and the wave header.'),
155 BinHeader5 = _Structure( # `version` field pulled out into Wave
158 _Field('h', 'checksum', help='Checksum over this header and the wave header.'),
159 _Field('l', 'wfmSize', help='The size of the WaveHeader5 data structure plus the wave data.'),
160 _Field('l', 'formulaSize', help='The size of the dependency formula, if any.'),
161 _Field('l', 'noteSize', help='The size of the note text.'),
162 _Field('l', 'dataEUnitsSize', help='The size of optional extended data units.'),
163 _Field('l', 'dimEUnitsSize', help='The size of optional extended dimension units.', count=MAXDIMS),
164 _Field('l', 'dimLabelsSize', help='The size of optional dimension labels.', count=MAXDIMS),
165 _Field('l', 'sIndicesSize', help='The size of string indicies if this is a text wave.'),
166 _Field('l', 'optionsSize1', default=0, help='Reserved. Write zero. Ignore on read.'),
167 _Field('l', 'optionsSize2', default=0, help='Reserved. Write zero. Ignore on read.'),
172 MAX_WAVE_NAME2 = 18 # Maximum length of wave name in version 1 and 2
173 # files. Does not include the trailing null.
174 MAX_WAVE_NAME5 = 31 # Maximum length of wave name in version 5
175 # files. Does not include the trailing null.
178 # Header to an array of waveform data.
180 # `wData` field pulled out into DynamicWaveDataField1
181 WaveHeader2 = _DynamicStructure(
184 _Field('h', 'type', help='See types (e.g. NT_FP64) above. Zero for text waves.'),
185 _Field('P', 'next', default=0, help='Used in memory only. Write zero. Ignore on read.'),
186 NullStaticStringField('c', 'bname', help='Name of wave plus trailing null.', count=MAX_WAVE_NAME2+2),
187 _Field('h', 'whVersion', default=0, help='Write 0. Ignore on read.'),
188 _Field('h', 'srcFldr', default=0, help='Used in memory only. Write zero. Ignore on read.'),
189 _Field('P', 'fileName', default=0, help='Used in memory only. Write zero. Ignore on read.'),
190 _Field('c', 'dataUnits', default=0, help='Natural data units go here - null if none.', count=MAX_UNIT_CHARS+1),
191 _Field('c', 'xUnits', default=0, help='Natural x-axis units go here - null if none.', count=MAX_UNIT_CHARS+1),
192 _Field('l', 'npnts', help='Number of data points in wave.'),
193 _Field('h', 'aModified', default=0, help='Used in memory only. Write zero. Ignore on read.'),
194 _Field('d', 'hsA', help='X value for point p = hsA*p + hsB'),
195 _Field('d', 'hsB', help='X value for point p = hsA*p + hsB'),
196 _Field('h', 'wModified', default=0, help='Used in memory only. Write zero. Ignore on read.'),
197 _Field('h', 'swModified', default=0, help='Used in memory only. Write zero. Ignore on read.'),
198 _Field('h', 'fsValid', help='True if full scale values have meaning.'),
199 _Field('d', 'topFullScale', help='The min full scale value for wave.'), # sic, 'min' should probably be 'max'
200 _Field('d', 'botFullScale', help='The min full scale value for wave.'),
201 _Field('c', 'useBits', default=0, help='Used in memory only. Write zero. Ignore on read.'),
202 _Field('c', 'kindBits', default=0, help='Reserved. Write zero. Ignore on read.'),
203 _Field('P', 'formula', default=0, help='Used in memory only. Write zero. Ignore on read.'),
204 _Field('l', 'depID', default=0, help='Used in memory only. Write zero. Ignore on read.'),
205 _Field('L', 'creationDate', help='DateTime of creation. Not used in version 1 files.'),
206 _Field('c', 'wUnused', default=0, help='Reserved. Write zero. Ignore on read.', count=2),
207 _Field('L', 'modDate', help='DateTime of last modification.'),
208 _Field('P', 'waveNoteH', help='Used in memory only. Write zero. Ignore on read.'),
211 # `sIndices` pointer unset (use Wave5_data['sIndices'] instead). This
212 # field is filled in by DynamicStringIndicesDataField.
213 # `wData` field pulled out into DynamicWaveDataField5
214 WaveHeader5 = _DynamicStructure(
217 _Field('P', 'next', help='link to next wave in linked list.'),
218 _Field('L', 'creationDate', help='DateTime of creation.'),
219 _Field('L', 'modDate', help='DateTime of last modification.'),
220 _Field('l', 'npnts', help='Total number of points (multiply dimensions up to first zero).'),
221 _Field('h', 'type', help='See types (e.g. NT_FP64) above. Zero for text waves.'),
222 _Field('h', 'dLock', default=0, help='Reserved. Write zero. Ignore on read.'),
223 _Field('c', 'whpad1', default=0, help='Reserved. Write zero. Ignore on read.', count=6),
224 _Field('h', 'whVersion', default=1, help='Write 1. Ignore on read.'),
225 NullStaticStringField('c', 'bname', help='Name of wave plus trailing null.', count=MAX_WAVE_NAME5+1),
226 _Field('l', 'whpad2', default=0, help='Reserved. Write zero. Ignore on read.'),
227 _Field('P', 'dFolder', default=0, help='Used in memory only. Write zero. Ignore on read.'),
228 # Dimensioning info. [0] == rows, [1] == cols etc
229 _Field('l', 'nDim', help='Number of of items in a dimension -- 0 means no data.', count=MAXDIMS),
230 _Field('d', 'sfA', help='Index value for element e of dimension d = sfA[d]*e + sfB[d].', count=MAXDIMS),
231 _Field('d', 'sfB', help='Index value for element e of dimension d = sfA[d]*e + sfB[d].', count=MAXDIMS),
233 _Field('c', 'dataUnits', default=0, help='Natural data units go here - null if none.', count=MAX_UNIT_CHARS+1),
234 _Field('c', 'dimUnits', default=0, help='Natural dimension units go here - null if none.', count=(MAXDIMS, MAX_UNIT_CHARS+1)),
235 _Field('h', 'fsValid', help='TRUE if full scale values have meaning.'),
236 _Field('h', 'whpad3', default=0, help='Reserved. Write zero. Ignore on read.'),
237 _Field('d', 'topFullScale', help='The max and max full scale value for wave'), # sic, probably "max and min"
238 _Field('d', 'botFullScale', help='The max and max full scale value for wave.'), # sic, probably "max and min"
239 _Field('P', 'dataEUnits', default=0, help='Used in memory only. Write zero. Ignore on read.'),
240 _Field('P', 'dimEUnits', default=0, help='Used in memory only. Write zero. Ignore on read.', count=MAXDIMS),
241 _Field('P', 'dimLabels', default=0, help='Used in memory only. Write zero. Ignore on read.', count=MAXDIMS),
242 _Field('P', 'waveNoteH', default=0, help='Used in memory only. Write zero. Ignore on read.'),
243 _Field('l', 'whUnused', default=0, help='Reserved. Write zero. Ignore on read.', count=16),
244 # The following stuff is considered private to Igor.
245 _Field('h', 'aModified', default=0, help='Used in memory only. Write zero. Ignore on read.'),
246 _Field('h', 'wModified', default=0, help='Used in memory only. Write zero. Ignore on read.'),
247 _Field('h', 'swModified', default=0, help='Used in memory only. Write zero. Ignore on read.'),
248 _Field('c', 'useBits', default=0, help='Used in memory only. Write zero. Ignore on read.'),
249 _Field('c', 'kindBits', default=0, help='Reserved. Write zero. Ignore on read.'),
250 _Field('P', 'formula', default=0, help='Used in memory only. Write zero. Ignore on read.'),
251 _Field('l', 'depID', default=0, help='Used in memory only. Write zero. Ignore on read.'),
252 _Field('h', 'whpad4', default=0, help='Reserved. Write zero. Ignore on read.'),
253 _Field('h', 'srcFldr', default=0, help='Used in memory only. Write zero. Ignore on read.'),
254 _Field('P', 'fileName', default=0, help='Used in memory only. Write zero. Ignore on read.'),
255 _Field('P', 'sIndices', default=0, help='Used in memory only. Write zero. Ignore on read.'),
259 class DynamicWaveDataField1 (_DynamicField):
260 def pre_pack(self, parents, data):
261 raise NotImplementedError()
263 def pre_unpack(self, parents, data):
264 full_structure = parents[0]
265 wave_structure = parents[-1]
266 wave_header_structure = wave_structure.fields[1].format
267 wave_data = self._get_structure_data(parents, data, wave_structure)
268 version = data['version']
269 bin_header = wave_data['bin_header']
270 wave_header = wave_data['wave_header']
272 self.count = wave_header['npnts']
273 self.data_size = self._get_size(bin_header, wave_header_structure.size)
275 type_ = TYPE_TABLE.get(wave_header['type'], None)
277 self.shape = self._get_shape(bin_header, wave_header)
279 type_ = _numpy.dtype('S1')
280 self.shape = (self.data_size,)
281 # dtype() wrapping to avoid numpy.generic and
282 # getset_descriptor issues with the builtin numpy types
283 # (e.g. int32). It has no effect on our local complex
285 self.dtype = _numpy.dtype(type_).newbyteorder(
286 wave_structure.byte_order)
289 bin_header['formulaSize'] > 0 and
290 self.data_size == 0):
293 Igor Pro 2.00 included support for dependency formulae. If
294 a wave was governed by a dependency formula then the
295 actual wave data was not written to disk for that wave,
296 because on loading the wave Igor could recalculate the
297 data. However,this prevented the wave from being loaded
298 into an experiment other than the original
299 experiment. Consequently, in a version of Igor Pro 3.0x,
300 we changed it so that the wave data was written even if
301 the wave was governed by a dependency formula. When
302 reading a binary wave file, you can detect that the wave
303 file does not contain the wave data by examining the
304 wfmSize, formulaSize and npnts fields. If npnts is greater
305 than zero and formulaSize is greater than zero and
306 the waveDataSize as calculated above is zero, then this is
307 a file governed by a dependency formula that was written
308 without the actual wave data.
311 elif TYPE_TABLE.get(wave_header['type'], None) is not None:
312 assert self.data_size == self.count * self.dtype.itemsize, (
313 self.data_size, self.count, self.dtype.itemsize, self.dtype)
315 assert self.data_size >= 0, (
316 bin_header['wfmSize'], wave_header_structure.size)
318 def _get_size(self, bin_header, wave_header_size):
319 return bin_header['wfmSize'] - wave_header_size - 16
321 def _get_shape(self, bin_header, wave_header):
324 def unpack(self, stream):
325 data_b = stream.read(self.data_size)
327 data = _numpy.ndarray(
335 'could not reshape data from {} to {}'.format(
341 class DynamicWaveDataField5 (DynamicWaveDataField1):
342 "Adds support for multidimensional data."
343 def _get_size(self, bin_header, wave_header_size):
344 return bin_header['wfmSize'] - wave_header_size
346 def _get_shape(self, bin_header, wave_header):
347 return [n for n in wave_header['nDim'] if n > 0] or (0,)
350 # End IGOR constants and typedefs from IgorBin.h
353 class DynamicStringField (StaticStringField):
356 def pre_unpack(self, parents, data):
357 size = self._get_size_data(parents, data)
358 if self._array_size_field:
360 self.count = sum(self.counts)
365 def _get_size_data(self, parents, data):
366 wave_structure = parents[-1]
367 wave_data = self._get_structure_data(parents, data, wave_structure)
368 bin_header = wave_data['bin_header']
369 return bin_header[self._size_field]
372 class DynamicWaveNoteField (DynamicStringField):
373 _size_field = 'noteSize'
376 class DynamicDependencyFormulaField (DynamicStringField):
377 """Optional wave dependency formula
379 Excerpted from TN003:
381 A wave has a dependency formula if it has been bound by a
382 statement such as "wave0 := sin(x)". In this example, the
383 dependency formula is "sin(x)". The formula is stored with
384 no trailing null byte.
386 _size_field = 'formulaSize'
387 # Except when it is stored with a trailing null byte :p. See, for
388 # example, test/data/mac-version3Dependent.ibw.
389 _null_terminated = True
392 class DynamicDataUnitsField (DynamicStringField):
393 """Optional extended data units data
395 Excerpted from TN003:
397 dataUnits - Present in versions 1, 2, 3, 5. The dataUnits field
398 stores the units for the data represented by the wave. It is a C
399 string terminated with a null character. This field supports
400 units of 0 to 3 bytes. In version 1, 2 and 3 files, longer units
401 can not be represented. In version 5 files, longer units can be
402 stored using the optional extended data units section of the
405 _size_field = 'dataEUnitsSize'
408 class DynamicDimensionUnitsField (DynamicStringField):
409 """Optional extended dimension units data
411 Excerpted from TN003:
413 xUnits - Present in versions 1, 2, 3. The xUnits field stores the
414 X units for a wave. It is a C string terminated with a null
415 character. This field supports units of 0 to 3 bytes. In
416 version 1, 2 and 3 files, longer units can not be represented.
418 dimUnits - Present in version 5 only. This field is an array of 4
419 strings, one for each possible wave dimension. Each string
420 supports units of 0 to 3 bytes. Longer units can be stored using
421 the optional extended dimension units section of the file.
423 _size_field = 'dimEUnitsSize'
424 _array_size_field = True
427 class DynamicLabelsField (DynamicStringField):
428 """Optional dimension label data
432 If the wave has dimension labels for dimension d then the
433 dimLabelsSize[d] field of the BinHeader5 structure will be
436 A wave will have dimension labels if a SetDimLabel command has
439 A 3 point 1D wave has 4 dimension labels. The first dimension
440 label is the label for the dimension as a whole. The next three
441 dimension labels are the labels for rows 0, 1, and 2. When Igor
442 writes dimension labels to disk, it writes each dimension label as
443 a C string (null-terminated) in a field of 32 bytes.
445 _size_field = 'dimLabelsSize'
446 _array_size_field = True
448 def post_unpack(self, parents, data):
449 wave_structure = parents[-1]
450 wave_data = self._get_structure_data(parents, data, wave_structure)
451 bin_header = wave_data['bin_header']
452 d = ''.join(wave_data[self.name])
455 for size in bin_header[self._size_field]:
458 dim_data = d[start:end]
459 # split null-delimited strings
460 labels = dim_data.split(chr(0))
464 dim_labels.append(labels)
465 wave_data[self.name] = dim_labels
468 class DynamicStringIndicesDataField (_DynamicField):
469 """String indices used for text waves only
471 def pre_pack(self, parents, data):
472 raise NotImplementedError()
474 def pre_unpack(self, parents, data):
475 wave_structure = parents[-1]
476 wave_data = self._get_structure_data(parents, data, wave_structure)
477 bin_header = wave_data['bin_header']
478 wave_header = wave_data['wave_header']
479 self.string_indices_size = bin_header['sIndicesSize']
480 self.count = self.string_indices_size // 4
481 if self.count: # make sure we're in a text wave
482 assert TYPE_TABLE[wave_header['type']] is None, wave_header
485 def post_unpack(self, parents, data):
488 wave_structure = parents[-1]
489 wave_data = self._get_structure_data(parents, data, wave_structure)
490 wave_header = wave_data['wave_header']
491 wdata = wave_data['wData']
494 for i,offset in enumerate(wave_data['sIndices']):
496 chars = wdata[start:offset]
497 strings.append(''.join(chars))
499 elif offset == start:
502 raise ValueError((offset, wave_data['sIndices']))
503 wdata = _numpy.array(strings)
504 shape = [n for n in wave_header['nDim'] if n > 0] or (0,)
506 wdata = wdata.reshape(shape)
509 'could not reshape strings from {} to {}'.format(
512 wave_data['wData'] = wdata
515 class DynamicVersionField (_DynamicField):
516 def pre_pack(self, parents, byte_order):
517 raise NotImplementedError()
519 def post_unpack(self, parents, data):
520 wave_structure = parents[-1]
521 wave_data = self._get_structure_data(parents, data, wave_structure)
522 version = wave_data['version']
523 if wave_structure.byte_order in '@=':
524 need_to_reorder_bytes = _need_to_reorder_bytes(version)
525 wave_structure.byte_order = _byte_order(need_to_reorder_bytes)
527 'get byte order from version: {} (reorder? {})'.format(
528 wave_structure.byte_order, need_to_reorder_bytes))
530 need_to_reorder_bytes = False
532 old_format = wave_structure.fields[-1].format
534 wave_structure.fields[-1].format = Wave1
536 wave_structure.fields[-1].format = Wave2
538 wave_structure.fields[-1].format = Wave3
540 wave_structure.fields[-1].format = Wave5
541 elif not need_to_reorder_bytes:
543 'invalid binary wave version: {}'.format(version))
545 if wave_structure.fields[-1].format != old_format:
546 _LOG.debug('change wave headers from {} to {}'.format(
547 old_format, wave_structure.fields[-1].format))
548 wave_structure.setup()
549 elif need_to_reorder_bytes:
550 wave_structure.setup()
552 # we might need to unpack again with the new byte order
553 return need_to_reorder_bytes
556 class DynamicWaveField (_DynamicField):
557 def post_unpack(self, parents, data):
559 raise NotImplementedError() # TODO
560 checksum_size = bin.size + wave.size
561 wave_structure = parents[-1]
563 # Version 5 checksum does not include the wData field.
565 c = _checksum(b, parents[-1].byte_order, 0, checksum_size)
568 ('This does not appear to be a valid Igor binary wave file. '
569 'Error in checksum: should be 0, is {}.').format(c))
571 Wave1 = _DynamicStructure(
574 _Field(BinHeader1, 'bin_header', help='Binary wave header'),
575 _Field(WaveHeader2, 'wave_header', help='Wave header'),
576 DynamicWaveDataField1('f', 'wData', help='The start of the array of waveform data.', count=0),
579 Wave2 = _DynamicStructure(
582 _Field(BinHeader2, 'bin_header', help='Binary wave header'),
583 _Field(WaveHeader2, 'wave_header', help='Wave header'),
584 DynamicWaveDataField1('f', 'wData', help='The start of the array of waveform data.', count=0),
585 _Field('x', 'padding', help='16 bytes of padding in versions 2 and 3.', count=16),
586 DynamicWaveNoteField('c', 'note', help='Optional wave note data', count=0),
589 Wave3 = _DynamicStructure(
592 _Field(BinHeader3, 'bin_header', help='Binary wave header'),
593 _Field(WaveHeader2, 'wave_header', help='Wave header'),
594 DynamicWaveDataField1('f', 'wData', help='The start of the array of waveform data.', count=0),
595 _Field('x', 'padding', help='16 bytes of padding in versions 2 and 3.', count=16),
596 DynamicWaveNoteField('c', 'note', help='Optional wave note data', count=0),
597 DynamicDependencyFormulaField('c', 'formula', help='Optional wave dependency formula', count=0),
600 Wave5 = _DynamicStructure(
603 _Field(BinHeader5, 'bin_header', help='Binary wave header'),
604 _Field(WaveHeader5, 'wave_header', help='Wave header'),
605 DynamicWaveDataField5('f', 'wData', help='The start of the array of waveform data.', count=0),
606 DynamicDependencyFormulaField('c', 'formula', help='Optional wave dependency formula.', count=0),
607 DynamicWaveNoteField('c', 'note', help='Optional wave note data.', count=0),
608 DynamicDataUnitsField('c', 'data_units', help='Optional extended data units data.', count=0),
609 DynamicDimensionUnitsField('c', 'dimension_units', help='Optional dimension label data', count=0),
610 DynamicLabelsField('c', 'labels', help="Optional dimension label data", count=0),
611 DynamicStringIndicesDataField('P', 'sIndices', help='Dynamic string indices for text waves.', count=0),
614 Wave = _DynamicStructure(
617 DynamicVersionField('h', 'version', help='Version number for backwards compatibility.'),
618 DynamicWaveField(Wave1, 'wave', help='The rest of the wave data.'),
623 if hasattr(filename, 'read'):
624 f = filename # filename is actually a stream object
626 f = open(filename, 'rb')
628 Wave.byte_order = '='
630 data = Wave.unpack_stream(f)
632 if not hasattr(filename, 'read'):
639 raise NotImplementedError