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 = wave_data[self.name]
464 for size in bin_header[self._size_field]:
467 dim_data = d[start:end]
469 for i in range(size//32):
470 chunks.append(dim_data[32*i:32*(i+1)])
473 labels[-1] = labels[-1] + b''.join(chunk)
480 dim_labels.append(labels)
481 wave_data[self.name] = dim_labels
484 class DynamicStringIndicesDataField (_DynamicField):
485 """String indices used for text waves only
487 def pre_pack(self, parents, data):
488 raise NotImplementedError()
490 def pre_unpack(self, parents, data):
491 wave_structure = parents[-1]
492 wave_data = self._get_structure_data(parents, data, wave_structure)
493 bin_header = wave_data['bin_header']
494 wave_header = wave_data['wave_header']
495 self.string_indices_size = bin_header['sIndicesSize']
496 self.count = self.string_indices_size // 4
497 if self.count: # make sure we're in a text wave
498 assert TYPE_TABLE[wave_header['type']] is None, wave_header
501 def post_unpack(self, parents, data):
504 wave_structure = parents[-1]
505 wave_data = self._get_structure_data(parents, data, wave_structure)
506 wave_header = wave_data['wave_header']
507 wdata = wave_data['wData']
510 for i,offset in enumerate(wave_data['sIndices']):
512 chars = wdata[start:offset]
513 strings.append(b''.join(chars))
515 elif offset == start:
518 raise ValueError((offset, wave_data['sIndices']))
519 wdata = _numpy.array(strings)
520 shape = [n for n in wave_header['nDim'] if n > 0] or (0,)
522 wdata = wdata.reshape(shape)
525 'could not reshape strings from {} to {}'.format(
528 wave_data['wData'] = wdata
531 class DynamicVersionField (_DynamicField):
532 def pre_pack(self, parents, byte_order):
533 raise NotImplementedError()
535 def post_unpack(self, parents, data):
536 wave_structure = parents[-1]
537 wave_data = self._get_structure_data(parents, data, wave_structure)
538 version = wave_data['version']
539 if wave_structure.byte_order in '@=':
540 need_to_reorder_bytes = _need_to_reorder_bytes(version)
541 wave_structure.byte_order = _byte_order(need_to_reorder_bytes)
543 'get byte order from version: {} (reorder? {})'.format(
544 wave_structure.byte_order, need_to_reorder_bytes))
546 need_to_reorder_bytes = False
548 old_format = wave_structure.fields[-1].format
550 wave_structure.fields[-1].format = Wave1
552 wave_structure.fields[-1].format = Wave2
554 wave_structure.fields[-1].format = Wave3
556 wave_structure.fields[-1].format = Wave5
557 elif not need_to_reorder_bytes:
559 'invalid binary wave version: {}'.format(version))
561 if wave_structure.fields[-1].format != old_format:
562 _LOG.debug('change wave headers from {} to {}'.format(
563 old_format, wave_structure.fields[-1].format))
564 wave_structure.setup()
565 elif need_to_reorder_bytes:
566 wave_structure.setup()
568 # we might need to unpack again with the new byte order
569 return need_to_reorder_bytes
572 class DynamicWaveField (_DynamicField):
573 def post_unpack(self, parents, data):
575 raise NotImplementedError() # TODO
576 checksum_size = bin.size + wave.size
577 wave_structure = parents[-1]
579 # Version 5 checksum does not include the wData field.
581 c = _checksum(b, parents[-1].byte_order, 0, checksum_size)
584 ('This does not appear to be a valid Igor binary wave file. '
585 'Error in checksum: should be 0, is {}.').format(c))
587 Wave1 = _DynamicStructure(
590 _Field(BinHeader1, 'bin_header', help='Binary wave header'),
591 _Field(WaveHeader2, 'wave_header', help='Wave header'),
592 DynamicWaveDataField1('f', 'wData', help='The start of the array of waveform data.', count=0, array=True),
595 Wave2 = _DynamicStructure(
598 _Field(BinHeader2, 'bin_header', help='Binary wave header'),
599 _Field(WaveHeader2, 'wave_header', help='Wave header'),
600 DynamicWaveDataField1('f', 'wData', help='The start of the array of waveform data.', count=0, array=True),
601 _Field('x', 'padding', help='16 bytes of padding in versions 2 and 3.', count=16, array=True),
602 DynamicWaveNoteField('c', 'note', help='Optional wave note data', count=0, array=True),
605 Wave3 = _DynamicStructure(
608 _Field(BinHeader3, 'bin_header', help='Binary wave header'),
609 _Field(WaveHeader2, 'wave_header', help='Wave header'),
610 DynamicWaveDataField1('f', 'wData', help='The start of the array of waveform data.', count=0, array=True),
611 _Field('x', 'padding', help='16 bytes of padding in versions 2 and 3.', count=16, array=True),
612 DynamicWaveNoteField('c', 'note', help='Optional wave note data', count=0, array=True),
613 DynamicDependencyFormulaField('c', 'formula', help='Optional wave dependency formula', count=0, array=True),
616 Wave5 = _DynamicStructure(
619 _Field(BinHeader5, 'bin_header', help='Binary wave header'),
620 _Field(WaveHeader5, 'wave_header', help='Wave header'),
621 DynamicWaveDataField5('f', 'wData', help='The start of the array of waveform data.', count=0, array=True),
622 DynamicDependencyFormulaField('c', 'formula', help='Optional wave dependency formula.', count=0, array=True),
623 DynamicWaveNoteField('c', 'note', help='Optional wave note data.', count=0, array=True),
624 DynamicDataUnitsField('c', 'data_units', help='Optional extended data units data.', count=0, array=True),
625 DynamicDimensionUnitsField('c', 'dimension_units', help='Optional dimension label data', count=0, array=True),
626 DynamicLabelsField('c', 'labels', help="Optional dimension label data", count=0, array=True),
627 DynamicStringIndicesDataField('P', 'sIndices', help='Dynamic string indices for text waves.', count=0, array=True),
630 Wave = _DynamicStructure(
633 DynamicVersionField('h', 'version', help='Version number for backwards compatibility.'),
634 DynamicWaveField(Wave1, 'wave', help='The rest of the wave data.'),
639 if hasattr(filename, 'read'):
640 f = filename # filename is actually a stream object
642 f = open(filename, 'rb')
644 Wave.byte_order = '='
646 data = Wave.unpack_stream(f)
648 if not hasattr(filename, 'read'):
655 raise NotImplementedError