Rework igor.struct to support dynamic structures.
authorW. Trevor King <wking@tremily.us>
Fri, 20 Jul 2012 18:50:09 +0000 (14:50 -0400)
committerW. Trevor King <wking@tremily.us>
Fri, 20 Jul 2012 18:50:09 +0000 (14:50 -0400)
Between binarywave and record.variables, there was a good deal of
parsing that was conditional on previously parsed data.  Rather than
continue writing spaghetti code to handle each specific case, I've
taken a stab at a general framework for updating structures during
parsing (DynamicStructure and DynamicField).  The docstrings should
explain how they work.  The implementation still has a few holes, but
it works on each of the files in my test suite.

igor/binarywave.py
igor/packed.py
igor/record/variables.py
igor/record/wave.py
igor/struct.py
test/test.py

index e7f1282989650fffbe36fe8bde139a52b6e1daaa..ee70be2e73e20130a3c67e518aedfbcdf594437d 100644 (file)
 #   share. We hope IGOR Technical Notes will provide you with lots of
 #   valuable information while you are developing IGOR applications.
 
+from __future__ import absolute_import
 import array as _array
+import struct as _struct
 import sys as _sys
 import types as _types
 
 import numpy as _numpy
 
+from . import LOG as _LOG
 from .struct import Structure as _Structure
+from .struct import DynamicStructure as _DynamicStructure
 from .struct import Field as _Field
+from .struct import DynamicField as _DynamicField
 from .util import assert_null as _assert_null
 from .util import byte_order as _byte_order
 from .util import need_to_reorder_bytes as _need_to_reorder_bytes
@@ -55,6 +60,40 @@ complexUInt16 = _numpy.dtype(
 complexUInt32 = _numpy.dtype(
     [('real', _numpy.uint32), ('imag', _numpy.uint32)])
 
+
+class StaticStringField (_DynamicField):
+    _null_terminated = False
+    _array_size_field = None
+    def post_unpack(self, parents, data):
+        wave_structure = parents[-1]
+        wave_data = self._get_structure_data(parents, data, wave_structure)
+        d = self._normalize_string(wave_data[self.name])
+        wave_data[self.name] = d
+
+    def _normalize_string(self, d):
+        if hasattr(d, 'tostring'):
+            d = d.tostring()
+        else:
+            d = ''.join(d)
+        if self._array_size_field:
+            start = 0
+            strings = []
+            for count in self.counts:
+                end = start + count
+                if end > start:
+                    strings.append(d[start:end])
+                    if self._null_terminated:
+                        strings[-1] = strings[-1].split('\x00', 1)[0]
+                    start = end
+        elif self._null_terminated:
+            d = d.split('\x00', 1)[0]
+        return d
+
+
+class NullStaticStringField (StaticStringField):
+    _null_terminated = True
+
+
 # Begin IGOR constants and typedefs from IgorBin.h
 
 # From IgorMath.h
@@ -88,45 +127,35 @@ TYPE_TABLE = {        # (key: integer flag, value: numpy dtype)
 MAXDIMS = 4
 
 # From binary.h
-BinHeaderCommon = _Structure(  # WTK: this one is mine.
-    name='BinHeaderCommon',
-    fields=[
-        _Field('h', 'version', help='Version number for backwards compatibility.'),
-        ])
-
-BinHeader1 = _Structure(
+BinHeader1 = _Structure(  # `version` field pulled out into Wave
     name='BinHeader1',
     fields=[
-        _Field('h', 'version', help='Version number for backwards compatibility.'),
         _Field('l', 'wfmSize', help='The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.'),
         _Field('h', 'checksum', help='Checksum over this header and the wave header.'),
         ])
 
-BinHeader2 = _Structure(
+BinHeader2 = _Structure(  # `version` field pulled out into Wave
     name='BinHeader2',
     fields=[
-        _Field('h', 'version', help='Version number for backwards compatibility.'),
         _Field('l', 'wfmSize', help='The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.'),
         _Field('l', 'noteSize', help='The size of the note text.'),
         _Field('l', 'pictSize', default=0, help='Reserved. Write zero. Ignore on read.'),
         _Field('h', 'checksum', help='Checksum over this header and the wave header.'),
         ])
 
-BinHeader3 = _Structure(
+BinHeader3 = _Structure(  # `version` field pulled out into Wave
     name='BinHeader3',
     fields=[
-        _Field('h', 'version', help='Version number for backwards compatibility.'),
-        _Field('h', 'wfmSize', help='The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.'),
+        _Field('l', 'wfmSize', help='The size of the WaveHeader2 data structure plus the wave data plus 16 bytes of padding.'),
         _Field('l', 'noteSize', help='The size of the note text.'),
         _Field('l', 'formulaSize', help='The size of the dependency formula, if any.'),
         _Field('l', 'pictSize', default=0, help='Reserved. Write zero. Ignore on read.'),
         _Field('h', 'checksum', help='Checksum over this header and the wave header.'),
         ])
 
-BinHeader5 = _Structure(
+BinHeader5 = _Structure(  # `version` field pulled out into Wave
     name='BinHeader5',
     fields=[
-        _Field('h', 'version', help='Version number for backwards compatibility.'),
         _Field('h', 'checksum', help='Checksum over this header and the wave header.'),
         _Field('l', 'wfmSize', help='The size of the WaveHeader5 data structure plus the wave data.'),
         _Field('l', 'formulaSize', help='The size of the dependency formula, if any.'),
@@ -149,12 +178,13 @@ MAX_UNIT_CHARS = 3
 
 # Header to an array of waveform data.
 
-WaveHeader2 = _Structure(
+# `wData` field pulled out into DynamicWaveDataField1
+WaveHeader2 = _DynamicStructure(
     name='WaveHeader2',
     fields=[
         _Field('h', 'type', help='See types (e.g. NT_FP64) above. Zero for text waves.'),
         _Field('P', 'next', default=0, help='Used in memory only. Write zero. Ignore on read.'),
-        _Field('c', 'bname', help='Name of wave plus trailing null.', count=MAX_WAVE_NAME2+2),
+        NullStaticStringField('c', 'bname', help='Name of wave plus trailing null.', count=MAX_WAVE_NAME2+2),
         _Field('h', 'whVersion', default=0, help='Write 0. Ignore on read.'),
         _Field('h', 'srcFldr', default=0, help='Used in memory only. Write zero. Ignore on read.'),
         _Field('P', 'fileName', default=0, help='Used in memory only. Write zero. Ignore on read.'),
@@ -177,10 +207,12 @@ WaveHeader2 = _Structure(
         _Field('c', 'wUnused', default=0, help='Reserved. Write zero. Ignore on read.', count=2),
         _Field('L', 'modDate', help='DateTime of last modification.'),
         _Field('P', 'waveNoteH', help='Used in memory only. Write zero. Ignore on read.'),
-        _Field('f', 'wData', help='The start of the array of waveform data.', count=4),
         ])
 
-WaveHeader5 = _Structure(
+# `sIndices` pointer unset (use Wave5_data['sIndices'] instead).  This
+# field is filled in by DynamicStringIndicesDataField.
+# `wData` field pulled out into DynamicWaveDataField5
+WaveHeader5 = _DynamicStructure(
     name='WaveHeader5',
     fields=[
         _Field('P', 'next', help='link to next wave in linked list.'),
@@ -191,7 +223,7 @@ WaveHeader5 = _Structure(
         _Field('h', 'dLock', default=0, help='Reserved. Write zero. Ignore on read.'),
         _Field('c', 'whpad1', default=0, help='Reserved. Write zero. Ignore on read.', count=6),
         _Field('h', 'whVersion', default=1, help='Write 1. Ignore on read.'),
-        _Field('c', 'bname', help='Name of wave plus trailing null.', count=MAX_WAVE_NAME5+1),
+        NullStaticStringField('c', 'bname', help='Name of wave plus trailing null.', count=MAX_WAVE_NAME5+1),
         _Field('l', 'whpad2', default=0, help='Reserved. Write zero. Ignore on read.'),
         _Field('P', 'dFolder', default=0, help='Used in memory only. Write zero. Ignore on read.'),
         # Dimensioning info. [0] == rows, [1] == cols etc
@@ -222,197 +254,386 @@ WaveHeader5 = _Structure(
         _Field('h', 'srcFldr', default=0, help='Used in memory only. Write zero. Ignore on read.'),
         _Field('P', 'fileName', default=0, help='Used in memory only. Write zero. Ignore on read.'),
         _Field('P', 'sIndices', default=0, help='Used in memory only. Write zero. Ignore on read.'),
-        _Field('f', 'wData', help='The start of the array of data.  Must be 64 bit aligned.', count=1),
         ])
 
-# End IGOR constants and typedefs from IgorBin.h
 
-def _version_structs(version, byte_order):
-    if version == 1:
-        bin = BinHeader1
-        wave = WaveHeader2
-    elif version == 2:
-        bin = BinHeader2
-        wave = WaveHeader2
-    elif version == 3:
-        bin = BinHeader3
-        wave = WaveHeader2
-    elif version == 5:
-        bin = BinHeader5
-        wave = WaveHeader5
-    else:
-        raise ValueError(
-            ('This does not appear to be a valid Igor binary wave file. '
-             'The version field = {}.\n').format(version))
-    checkSumSize = bin.size + wave.size
-    if version == 5:
-        checkSumSize -= 4  # Version 5 checksum does not include the wData field.
-    bin.set_byte_order(byte_order)
-    wave.set_byte_order(byte_order)
-    return (bin, wave, checkSumSize)
-
-# Translated from ReadWave()
-def load(filename, strict=True):
-    if hasattr(filename, 'read'):
-        f = filename  # filename is actually a stream object
-    else:
-        f = open(filename, 'rb')
-    try:
-        BinHeaderCommon.set_byte_order('=')
-        b = buffer(f.read(BinHeaderCommon.size))
-        version = BinHeaderCommon.unpack_from(b)['version']
-        needToReorderBytes = _need_to_reorder_bytes(version)
-        byteOrder = _byte_order(needToReorderBytes)
-
-        if needToReorderBytes:
-            BinHeaderCommon.set_byte_order(byteOrder)
-            version = BinHeaderCommon.unpack_from(b)['version']
-        bin_struct,wave_struct,checkSumSize = _version_structs(
-            version, byteOrder)
-
-        b = buffer(b + f.read(bin_struct.size + wave_struct.size - BinHeaderCommon.size))
-        c = _checksum(b, byteOrder, 0, checkSumSize)
-        if c != 0:
-            raise ValueError(
-                ('This does not appear to be a valid Igor binary wave file.  '
-                 'Error in checksum: should be 0, is {}.').format(c))
-        bin_info = bin_struct.unpack_from(b)
-        wave_info = wave_struct.unpack_from(b, offset=bin_struct.size)
-        if version in [1,2,3]:
-            tail = 16  # 16 = size of wData field in WaveHeader2 structure
-            waveDataSize = bin_info['wfmSize'] - wave_struct.size
-            # =  bin_info['wfmSize']-16 - (wave_struct.size - tail)
-        else:
-            assert version == 5, version
-            tail = 4  # 4 = size of wData field in WaveHeader5 structure
-            waveDataSize = bin_info['wfmSize'] - (wave_struct.size - tail)
+class DynamicWaveDataField1 (_DynamicField):
+    def pre_pack(self, parents, data):
+        raise NotImplementedError()
+
+    def pre_unpack(self, parents, data):
+        full_structure = parents[0]
+        wave_structure = parents[-1]
+        wave_header_structure = wave_structure.fields[1].format
+        wave_data = self._get_structure_data(parents, data, wave_structure)
+        version = data['version']
+        bin_header = wave_data['bin_header']
+        wave_header = wave_data['wave_header']
+
+        self.count = wave_header['npnts']
+        self.data_size = self._get_size(bin_header, wave_header_structure.size)
+
+        type_ = TYPE_TABLE.get(wave_header['type'], None)
+        if type_:
+            self.shape = self._get_shape(bin_header, wave_header)
+        else:  # text wave
+            type_ = _numpy.dtype('S1')
+            self.shape = (self.data_size,)
         # dtype() wrapping to avoid numpy.generic and
         # getset_descriptor issues with the builtin numpy types
         # (e.g. int32).  It has no effect on our local complex
         # integers.
-        if version == 5:
-            shape = [n for n in wave_info['nDim'] if n > 0] or (0,)
+        self.dtype = _numpy.dtype(type_).newbyteorder(
+            wave_structure.byte_order)
+        if (version == 3 and
+            self.count > 0 and
+            bin_header['formulaSize'] > 0 and
+            self.data_size == 0):
+            """From TN003:
+
+            Igor Pro 2.00 included support for dependency formulae. If
+            a wave was governed by a dependency formula then the
+            actual wave data was not written to disk for that wave,
+            because on loading the wave Igor could recalculate the
+            data. However,this prevented the wave from being loaded
+            into an experiment other than the original
+            experiment. Consequently, in a version of Igor Pro 3.0x,
+            we changed it so that the wave data was written even if
+            the wave was governed by a dependency formula. When
+            reading a binary wave file, you can detect that the wave
+            file does not contain the wave data by examining the
+            wfmSize, formulaSize and npnts fields. If npnts is greater
+            than zero and formulaSize is greater than zero and
+            the waveDataSize as calculated above is zero, then this is
+            a file governed by a dependency formula that was written
+            without the actual wave data.
+            """
+            self.shape = (0,)
+        elif TYPE_TABLE.get(wave_header['type'], None) is not None:
+            assert self.data_size == self.count * self.dtype.itemsize, (
+                self.data_size, self.count, self.dtype.itemsize, self.dtype)
         else:
-            shape = (wave_info['npnts'],)
-        t = _numpy.dtype(_numpy.int8)  # setup a safe default
-        if wave_info['type'] == 0:  # text wave
-            shape = (waveDataSize,)
-        elif wave_info['type'] in TYPE_TABLE or wave_info['npnts']:
-            t = _numpy.dtype(TYPE_TABLE[wave_info['type']])
-            assert waveDataSize == wave_info['npnts'] * t.itemsize, (
-                '{}, {}, {}, {}'.format(
-                    waveDataSize, wave_info['npnts'], t.itemsize, t))
+            assert self.data_size >= 0, (
+                bin_header['wfmSize'], wave_header_structure.size)
+
+    def _get_size(self, bin_header, wave_header_size):
+        return bin_header['wfmSize'] - wave_header_size - 16
+
+    def _get_shape(self, bin_header, wave_header):
+        return (self.count,)
+
+    def unpack(self, stream):
+        data_b = stream.read(self.data_size)
+        try:
+            data = _numpy.ndarray(
+                shape=self.shape,
+                dtype=self.dtype,
+                buffer=data_b,
+                order='F',
+                )
+        except:
+            _LOG.error(
+                'could not reshape data from {} to {}'.format(
+                    self.shape, data_b))
+            raise
+        return data
+
+
+class DynamicWaveDataField5 (DynamicWaveDataField1):
+    "Adds support for multidimensional data."
+    def _get_size(self, bin_header, wave_header_size):
+        return bin_header['wfmSize'] - wave_header_size
+
+    def _get_shape(self, bin_header, wave_header):
+        return [n for n in wave_header['nDim'] if n > 0] or (0,)
+
+
+# End IGOR constants and typedefs from IgorBin.h
+
+
+class DynamicStringField (StaticStringField):
+    _size_field = None
+
+    def pre_unpack(self, parents, data):
+        size = self._get_size_data(parents, data)
+        if self._array_size_field:
+            self.counts = size
+            self.count = sum(self.counts)
         else:
-            pass  # formula waves
-        if wave_info['npnts'] == 0:
-            data_b = buffer('')
+            self.count = size
+        self.setup()
+
+    def _get_size_data(self, parents, data):
+        wave_structure = parents[-1]
+        wave_data = self._get_structure_data(parents, data, wave_structure)
+        bin_header = wave_data['bin_header']
+        return bin_header[self._size_field]
+
+
+class DynamicWaveNoteField (DynamicStringField):
+    _size_field = 'noteSize'
+
+
+class DynamicDependencyFormulaField (DynamicStringField):
+    """Optional wave dependency formula
+
+    Excerpted from TN003:
+
+    A wave has a dependency formula if it has been bound by a
+    statement such as "wave0 := sin(x)". In this example, the
+    dependency formula is "sin(x)". The formula is stored with
+    no trailing null byte.
+    """
+    _size_field = 'formulaSize'
+    # Except when it is stored with a trailing null byte :p.  See, for
+    # example, test/data/mac-version3Dependent.ibw.
+    _null_terminated = True
+
+
+class DynamicDataUnitsField (DynamicStringField):
+    """Optional extended data units data
+
+    Excerpted from TN003:
+
+    dataUnits - Present in versions 1, 2, 3, 5. The dataUnits field
+      stores the units for the data represented by the wave. It is a C
+      string terminated with a null character. This field supports
+      units of 0 to 3 bytes. In version 1, 2 and 3 files, longer units
+      can not be represented. In version 5 files, longer units can be
+      stored using the optional extended data units section of the
+      file.
+    """
+    _size_field = 'dataEUnitsSize'
+
+
+class DynamicDimensionUnitsField (DynamicStringField):
+    """Optional extended dimension units data
+
+    Excerpted from TN003:
+
+    xUnits - Present in versions 1, 2, 3. The xUnits field stores the
+      X units for a wave. It is a C string terminated with a null
+      character.  This field supports units of 0 to 3 bytes. In
+      version 1, 2 and 3 files, longer units can not be represented.
+
+    dimUnits - Present in version 5 only. This field is an array of 4
+      strings, one for each possible wave dimension. Each string
+      supports units of 0 to 3 bytes. Longer units can be stored using
+      the optional extended dimension units section of the file.
+    """
+    _size_field = 'dimEUnitsSize'
+    _array_size_field = True
+
+
+class DynamicLabelsField (DynamicStringField):
+    """Optional dimension label data
+
+    From TN003:
+
+    If the wave has dimension labels for dimension d then the
+    dimLabelsSize[d] field of the BinHeader5 structure will be
+    non-zero.
+
+    A wave will have dimension labels if a SetDimLabel command has
+    been executed on it.
+
+    A 3 point 1D wave has 4 dimension labels. The first dimension
+    label is the label for the dimension as a whole. The next three
+    dimension labels are the labels for rows 0, 1, and 2. When Igor
+    writes dimension labels to disk, it writes each dimension label as
+    a C string (null-terminated) in a field of 32 bytes.
+    """
+    _size_field = 'dimLabelsSize'
+    _array_size_field = True
+
+    def post_unpack(self, parents, data):
+        wave_structure = parents[-1]
+        wave_data = self._get_structure_data(parents, data, wave_structure)
+        bin_header = wave_data['bin_header']
+        d = ''.join(wave_data[self.name])
+        dim_labels = []
+        start = 0
+        for size in bin_header[self._size_field]:
+            end = start + size
+            if end > start:
+                dim_data = d[start:end]
+                # split null-delimited strings
+                labels = dim_data.split(chr(0))
+                start = end
+            else:
+                labels = []
+            dim_labels.append(labels)
+        wave_data[self.name] = dim_labels
+
+
+class DynamicStringIndicesDataField (_DynamicField):
+    """String indices used for text waves only
+    """
+    def pre_pack(self, parents, data):
+        raise NotImplementedError()
+
+    def pre_unpack(self, parents, data):
+        wave_structure = parents[-1]
+        wave_data = self._get_structure_data(parents, data, wave_structure)
+        bin_header = wave_data['bin_header']
+        wave_header = wave_data['wave_header']
+        self.string_indices_size = bin_header['sIndicesSize']
+        self.count = self.string_indices_size / 4
+        if self.count:  # make sure we're in a text wave
+            assert TYPE_TABLE[wave_header['type']] is None, wave_header
+        self.setup()
+
+    def post_unpack(self, parents, data):
+        if not self.count:
+            return
+        wave_structure = parents[-1]
+        wave_data = self._get_structure_data(parents, data, wave_structure)
+        wave_header = wave_data['wave_header']
+        wdata = wave_data['wData']
+        strings = []
+        start = 0
+        for i,offset in enumerate(wave_data['sIndices']):
+            if offset > start:
+                chars = wdata[start:offset]
+                strings.append(''.join(chars))
+                start = offset
+            elif offset == start:
+                strings.append('')
+            else:
+                raise ValueError((offset, wave_data['sIndices']))
+        wdata = _numpy.array(strings)
+        shape = [n for n in wave_header['nDim'] if n > 0] or (0,)
+        try:
+            wdata = wdata.reshape(shape)
+        except ValueError:
+            _LOG.error(
+                'could not reshape strings from {} to {}'.format(
+                    shape, wdata.shape))
+            raise
+        wave_data['wData'] = wdata
+
+
+class DynamicVersionField (_DynamicField):
+    def pre_pack(self, parents, byte_order):
+        raise NotImplementedError()
+
+    def post_unpack(self, parents, data):
+        wave_structure = parents[-1]
+        wave_data = self._get_structure_data(parents, data, wave_structure)
+        version = wave_data['version']
+        if wave_structure.byte_order in '@=':
+            need_to_reorder_bytes = _need_to_reorder_bytes(version)
+            wave_structure.byte_order = _byte_order(need_to_reorder_bytes)
+            _LOG.debug(
+                'get byte order from version: {} (reorder? {})'.format(
+                    wave_structure.byte_order, need_to_reorder_bytes))
         else:
-            tail_data = _array.array('f', b[-tail:])
-            data_b = buffer(buffer(tail_data) + f.read(waveDataSize-tail))
-        data = _numpy.ndarray(
-            shape=shape,
-            dtype=t.newbyteorder(byteOrder),
-            buffer=data_b,
-            order='F',
-            )
+            need_to_reorder_bytes = False
 
+        old_format = wave_structure.fields[-1].format
         if version == 1:
-            pass  # No post-data information
+            wave_structure.fields[-1].format = Wave1
         elif version == 2:
-            # Post-data info:
-            #   * 16 bytes of padding
-            #   * Optional wave note data
-            pad_b = buffer(f.read(16))  # skip the padding
-            _assert_null(pad_b, strict=strict)
-            bin_info['note'] = str(f.read(bin_info['noteSize'])).strip()
+            wave_structure.fields[-1].format = Wave2
         elif version == 3:
-            # Post-data info:
-            #   * 16 bytes of padding
-            #   * Optional wave note data
-            #   * Optional wave dependency formula
-            """Excerpted from TN003:
-
-            A wave has a dependency formula if it has been bound by a
-            statement such as "wave0 := sin(x)". In this example, the
-            dependency formula is "sin(x)". The formula is stored with
-            no trailing null byte.
-            """
-            pad_b = buffer(f.read(16))  # skip the padding
-            _assert_null(pad_b, strict=strict)
-            bin_info['note'] = str(f.read(bin_info['noteSize'])).strip()
-            bin_info['formula'] = str(f.read(bin_info['formulaSize'])).strip()
+            wave_structure.fields[-1].format = Wave3
         elif version == 5:
-            # Post-data info:
-            #   * Optional wave dependency formula
-            #   * Optional wave note data
-            #   * Optional extended data units data
-            #   * Optional extended dimension units data
-            #   * Optional dimension label data
-            #   * String indices used for text waves only
-            """Excerpted from TN003:
-
-            dataUnits - Present in versions 1, 2, 3, 5. The dataUnits
-              field stores the units for the data represented by the
-              wave. It is a C string terminated with a null
-              character. This field supports units of 0 to 3 bytes. In
-              version 1, 2 and 3 files, longer units can not be
-              represented. In version 5 files, longer units can be
-              stored using the optional extended data units section of
-              the file.
-
-            xUnits - Present in versions 1, 2, 3. The xUnits field
-              stores the X units for a wave. It is a C string
-              terminated with a null character.  This field supports
-              units of 0 to 3 bytes. In version 1, 2 and 3 files,
-              longer units can not be represented.
-
-            dimUnits - Present in version 5 only. This field is an
-              array of 4 strings, one for each possible wave
-              dimension. Each string supports units of 0 to 3
-              bytes. Longer units can be stored using the optional
-              extended dimension units section of the file.
-            """
-            bin_info['formula'] = str(f.read(bin_info['formulaSize'])).strip()
-            bin_info['note'] = str(f.read(bin_info['noteSize'])).strip()
-            bin_info['dataEUnits'] = str(f.read(bin_info['dataEUnitsSize'])).strip()
-            bin_info['dimEUnits'] = [
-                str(f.read(size)).strip() for size in bin_info['dimEUnitsSize']]
-            bin_info['dimLabels'] = []
-            for size in bin_info['dimLabelsSize']:
-                labels = str(f.read(size)).split(chr(0)) # split null-delimited strings
-                bin_info['dimLabels'].append([L for L in labels if len(L) > 0])
-            if wave_info['type'] == 0:  # text wave
-                sIndices_data = f.read(bin_info['sIndicesSize'])
-                sIndices_count = bin_info['sIndicesSize']/4
-                bin_info['sIndices'] = _numpy.ndarray(
-                    shape=(sIndices_count,),
-                    dtype=_numpy.dtype(
-                        _numpy.uint32()).newbyteorder(byteOrder),
-                    buffer=sIndices_data,
-                    order='F',
-                    )
-
-        if wave_info['type'] == 0:  # text wave
-            # use sIndices to split data into strings
-            strings = []
-            start = 0
-            for i,offset in enumerate(bin_info['sIndices']):
-                if offset > start:
-                    string = data[start:offset]
-                    strings.append(''.join(chr(x) for x in string))
-                    start = offset
-                elif offset == start:
-                    strings.append('')
-                else:
-                    raise ValueError((offset, bin_info['sIndices']))
-            data = _numpy.array(strings)
-            shape = [n for n in wave_info['nDim'] if n > 0] or (0,)
-            data.reshape(shape)
+            wave_structure.fields[-1].format = Wave5
+        elif not need_to_reorder_bytes:
+            raise ValueError(
+                'invalid binary wave version: {}'.format(version))
+
+        if wave_structure.fields[-1].format != old_format:
+            _LOG.debug('change wave headers from {} to {}'.format(
+                    old_format, wave_structure.fields[-1].format))
+            wave_structure.setup()
+        elif need_to_reorder_bytes:
+            wave_structure.setup()
+
+        # we might need to unpack again with the new byte order
+        return need_to_reorder_bytes
+
+
+class DynamicWaveField (_DynamicField):
+    def post_unpack(self, parents, data):
+        return
+        raise NotImplementedError()  # TODO
+        checksum_size = bin.size + wave.size
+        wave_structure = parents[-1]
+        if version == 5:
+            # Version 5 checksum does not include the wData field.
+            checksum_size -= 4
+        c = _checksum(b, parents[-1].byte_order, 0, checksum_size)
+        if c != 0:
+            raise ValueError(
+                ('This does not appear to be a valid Igor binary wave file.  '
+                 'Error in checksum: should be 0, is {}.').format(c))
+
+Wave1 = _DynamicStructure(
+    name='Wave1',
+    fields=[
+        _Field(BinHeader1, 'bin_header', help='Binary wave header'),
+        _Field(WaveHeader2, 'wave_header', help='Wave header'),
+        DynamicWaveDataField1('f', 'wData', help='The start of the array of waveform data.', count=0),
+        ])
+
+Wave2 = _DynamicStructure(
+    name='Wave2',
+    fields=[
+        _Field(BinHeader2, 'bin_header', help='Binary wave header'),
+        _Field(WaveHeader2, 'wave_header', help='Wave header'),
+        DynamicWaveDataField1('f', 'wData', help='The start of the array of waveform data.', count=0),
+        _Field('x', 'padding', help='16 bytes of padding in versions 2 and 3.', count=16),
+        DynamicWaveNoteField('c', 'note', help='Optional wave note data', count=0),
+        ])
+
+Wave3 = _DynamicStructure(
+    name='Wave3',
+    fields=[
+        _Field(BinHeader3, 'bin_header', help='Binary wave header'),
+        _Field(WaveHeader2, 'wave_header', help='Wave header'),
+        DynamicWaveDataField1('f', 'wData', help='The start of the array of waveform data.', count=0),
+        _Field('x', 'padding', help='16 bytes of padding in versions 2 and 3.', count=16),
+        DynamicWaveNoteField('c', 'note', help='Optional wave note data', count=0),
+        DynamicDependencyFormulaField('c', 'formula', help='Optional wave dependency formula', count=0),
+        ])
+
+Wave5 = _DynamicStructure(
+    name='Wave5',
+    fields=[
+        _Field(BinHeader5, 'bin_header', help='Binary wave header'),
+        _Field(WaveHeader5, 'wave_header', help='Wave header'),
+        DynamicWaveDataField5('f', 'wData', help='The start of the array of waveform data.', count=0),
+        DynamicDependencyFormulaField('c', 'formula', help='Optional wave dependency formula.', count=0),
+        DynamicWaveNoteField('c', 'note', help='Optional wave note data.', count=0),
+        DynamicDataUnitsField('c', 'data_units', help='Optional extended data units data.', count=0),
+        DynamicDimensionUnitsField('c', 'dimension_units', help='Optional dimension label data', count=0),
+        DynamicLabelsField('c', 'labels', help="Optional dimension label data", count=0),
+        DynamicStringIndicesDataField('P', 'sIndices', help='Dynamic string indices for text waves.', count=0),
+        ])
+
+Wave = _DynamicStructure(
+    name='Wave',
+    fields=[
+        DynamicVersionField('h', 'version', help='Version number for backwards compatibility.'),
+        DynamicWaveField(Wave1, 'wave', help='The rest of the wave data.'),
+        ])
+
+
+def load(filename):
+    if hasattr(filename, 'read'):
+        f = filename  # filename is actually a stream object
+    else:
+        f = open(filename, 'rb')
+    try:
+        Wave.byte_order = '='
+        Wave.setup()
+        data = Wave.unpack_stream(f)
     finally:
         if not hasattr(filename, 'read'):
             f.close()
 
-    return data, bin_info, wave_info
+    return data
 
 
 def save(filename):
index baad25ed193fa804ac2acb8b478426a67f604b15..564725aabbb9df7177c02a31aaa6afea0474b50c 100644 (file)
@@ -2,6 +2,7 @@
 
 "Read IGOR Packed Experiment files files into records."
 
+from . import LOG as _LOG
 from .struct import Structure as _Structure
 from .struct import Field as _Field
 from .util import byte_order as _byte_order
@@ -39,6 +40,7 @@ SUPERCEDED_MASK = 0x8000  # Bit is set if the record is superceded by
 
 
 def load(filename, strict=True, ignore_unknown=True):
+    _LOG.debug('loading a packed experiment file from {}'.format(filename))
     records = []
     if hasattr(filename, 'read'):
         f = filename  # filename is actually a stream object
@@ -48,26 +50,38 @@ def load(filename, strict=True, ignore_unknown=True):
     initial_byte_order = '='
     try:
         while True:
+            PackedFileRecordHeader.byte_order = initial_byte_order
+            PackedFileRecordHeader.setup()
             b = buffer(f.read(PackedFileRecordHeader.size))
             if not b:
                 break
-            PackedFileRecordHeader.set_byte_order(initial_byte_order)
+            _LOG.debug('reading a new packed experiment file record')
             header = PackedFileRecordHeader.unpack_from(b)
             if header['version'] and not byte_order:
                 need_to_reorder = _need_to_reorder_bytes(header['version'])
                 byte_order = initial_byte_order = _byte_order(need_to_reorder)
+                _LOG.debug(
+                    'get byte order from version: {} (reorder? {})'.format(
+                        byte_order, need_to_reorder))
                 if need_to_reorder:
-                    PackedFileRecordHeader.set_byte_order(byte_order)
+                    PackedFileRecordHeader.byte_order = byte_order
+                    PackedFileRecordHeader.setup()
                     header = PackedFileRecordHeader.unpack_from(b)
+                    _LOG.debug(
+                        'reordered version: {}'.format(header['version']))
             data = buffer(f.read(header['numDataBytes']))
             record_type = _RECORD_TYPE.get(
                 header['recordType'] & PACKEDRECTYPE_MASK, _UnknownRecord)
+            _LOG.debug('the new record has type {} ({}).'.format(
+                    record_type, header['recordType']))
             if record_type in [_UnknownRecord, _UnusedRecord
                                ] and not ignore_unknown:
                 raise KeyError('unkown record type {}'.format(
                         header['recordType']))
             records.append(record_type(header, data, byte_order=byte_order))
     finally:
+        _LOG.debug('finished loading {} records from {}'.format(
+                len(records), filename))
         if not hasattr(filename, 'read'):
             f.close()
 
@@ -122,15 +136,19 @@ def _build_filesystem(records):
             dir_stack.pop()
         elif isinstance(record, (_VariablesRecord, _WaveRecord)):
             if isinstance(record, _VariablesRecord):
-                _add_variables(dir_stack, cwd, record)
-                # start with an invalid character to avoid collisions
-                # with folder names
-                #filename = ':variables'
-                #_check_filename(dir_stack, filename)
-                #cwd[filename] = record
+                sys_vars = record.variables['variables']['sysVars'].keys()
+                for filename,value in record.namespace.items():
+                    if len(dir_stack) > 1 and filename in sys_vars:
+                        # From PTN003:
+                        """When reading a packed file, any system
+                        variables encountered while the current data
+                        folder is not the root should be ignored.
+                        """
+                        continue
+                    _check_filename(dir_stack, filename)
+                    cwd[filename] = value
             else:  # WaveRecord
-                filename = ''.join(c for c in record.wave_info['bname']
-                                   ).split('\x00', 1)[0]
+                filename = record.wave['wave']['wave_header']['bname']
                 _check_filename(dir_stack, filename)
                 cwd[filename] = record
     return filesystem
@@ -140,22 +158,3 @@ def _check_filename(dir_stack, filename):
     if filename in cwd:
         raise ValueError('collision on name {} in {}'.format(
                 filename, ':'.join(d for d,cwd in dir_stack)))
-
-def _add_variables(dir_stack, cwd, record):
-    if len(dir_stack) == 1:
-        # From PTN003:
-        """When reading a packed file, any system variables
-        encountered while the current data folder is not the root
-        should be ignored.
-        """
-        for i,value in enumerate(record.variables['sysVars']):
-            name = 'K{}'.format(i)
-            _check_filename(dir_stack, name)
-            cwd[name] = value
-    for name,value in (
-        record.variables['userVars'].items() +
-        record.variables['userStrs'].items()):
-        _check_filename(dir_stack, name)
-        cwd[name] = value
-    if record.variables['header']['version'] == 2:
-        raise NotImplementedError('add dependent variables to filesystem')
index e55d80063b1f2ed999e34acc4158361fd7cd5283..d604bfd77efbbfe3d4236ced93bb2cded9090fcd 100644 (file)
 # Copyright
 
+import io as _io
+
+from .. import LOG as _LOG
 from ..binarywave import TYPE_TABLE as _TYPE_TABLE
+from ..binarywave import NullStaticStringField as _NullStaticStringField
+from ..binarywave import DynamicStringField as _DynamicStringField
 from ..struct import Structure as _Structure
+from ..struct import DynamicStructure as _DynamicStructure
 from ..struct import Field as _Field
+from ..struct import DynamicField as _DynamicField
 from ..util import byte_order as _byte_order
 from ..util import need_to_reorder_bytes as _need_to_reorder_bytes
 from .base import Record
 
 
-VarHeaderCommon = _Structure(
-    name='VarHeaderCommon',
-    fields=[
-        _Field('h', 'version', help='Version number for this header.'),
-        ])
+class ListedStaticStringField (_NullStaticStringField):
+    """Handle string conversions for multi-count dynamic parents.
+
+    If a field belongs to a multi-count dynamic parent, the parent is
+    called multiple times to parse each count, and the field's
+    post-unpack hook gets called after the field is unpacked during
+    each iteration.  This requires alternative logic for getting and
+    setting the string data.  The actual string formatting code is not
+    affected.
+    """
+    def post_unpack(self, parents, data):
+        parent_structure = parents[-1]
+        parent_data = self._get_structure_data(parents, data, parent_structure)
+        d = self._normalize_string(parent_data[-1][self.name])
+        parent_data[-1][self.name] = d
+
+
+class ListedStaticStringField (_NullStaticStringField):
+    """Handle string conversions for multi-count dynamic parents.
+
+    If a field belongs to a multi-count dynamic parent, the parent is
+    called multiple times to parse each count, and the field's
+    post-unpack hook gets called after the field is unpacked during
+    each iteration.  This requires alternative logic for getting and
+    setting the string data.  The actual string formatting code is not
+    affected.
+    """
+    def post_unpack(self, parents, data):
+        parent_structure = parents[-1]
+        parent_data = self._get_structure_data(parents, data, parent_structure)
+        d = self._normalize_string(parent_data[-1][self.name])
+        parent_data[-1][self.name] = d
+
+
+class ListedDynamicStrDataField (_DynamicStringField, ListedStaticStringField):
+    _size_field = 'strLen'
+    _null_terminated = False
+
+    def _get_size_data(self, parents, data):
+        parent_structure = parents[-1]
+        parent_data = self._get_structure_data(parents, data, parent_structure)
+        return parent_data[-1][self._size_field]
+
+
+class DynamicVarDataField (_DynamicField):
+    def pre_pack(self, parents, data):
+        raise NotImplementedError()
+
+    def post_unpack(self, parents, data):
+        var_structure = parents[-1]
+        var_data = self._get_structure_data(parents, data, var_structure)
+        data = var_data[self.name]
+        d = {}
+        for i,value in enumerate(data):
+            key,value = self._normalize_item(i, value)
+            d[key] = value
+        var_data[self.name] = d
+
+    def _normalize_item(self, index, value):
+        raise NotImplementedError()
+
+
+class DynamicSysVarField (DynamicVarDataField):
+    def _normalize_item(self, index, value):
+        name = 'K{}'.format(index)
+        return (name, value)
+
+
+class DynamicUserVarField (DynamicVarDataField):
+    def _normalize_item(self, index, value):
+        name = value['name']
+        value = value['num']
+        return (name, value)
+
+
+class DynamicUserStrField (DynamicVarDataField):
+    def _normalize_item(self, index, value):
+        name = value['name']
+        value = value['data']
+        return (name, value)
+
+
+class DynamicVarNumField (_DynamicField):
+    def post_unpack(self, parents, data):
+        parent_structure = parents[-1]
+        parent_data = self._get_structure_data(parents, data, parent_structure)
+        d = self._normalize_numeric_variable(parent_data[-1][self.name])
+        parent_data[-1][self.name] = d
+
+    def _normalize_numeric_variable(self, num_var):
+        t = _TYPE_TABLE[num_var['numType']]
+        if num_var['numType'] % 2:  # complex number
+            return t(complex(num_var['realPart'], num_var['imagPart']))
+        else:
+            return t(num_var['realPart'])
+
+
+class DynamicFormulaField (_DynamicStringField):
+    _size_field = 'formulaLen'
+    _null_terminated = True
+
 
 # From Variables.h
-VarHeader1 = _Structure(
+VarHeader1 = _Structure(  # `version` field pulled out into VariablesRecord
     name='VarHeader1',
     fields=[
-        _Field('h', 'version', help='Version number is 1 for this header.'),
         _Field('h', 'numSysVars', help='Number of system variables (K0, K1, ...).'),
         _Field('h', 'numUserVars', help='Number of user numeric variables -- may be zero.'),
         _Field('h', 'numUserStrs', help='Number of user string variables -- may be zero.'),
         ])
 
 # From Variables.h
-VarHeader2 = _Structure(
+VarHeader2 = _Structure(  # `version` field pulled out into VariablesRecord
     name='VarHeader2',
     fields=[
-        _Field('h', 'version', help='Version number is 2 for this header.'),
         _Field('h', 'numSysVars', help='Number of system variables (K0, K1, ...).'),
         _Field('h', 'numUserVars', help='Number of user numeric variables -- may be zero.'),
         _Field('h', 'numUserStrs', help='Number of user string variables -- may be zero.'),
@@ -37,19 +138,19 @@ VarHeader2 = _Structure(
         ])
 
 # From Variables.h
-UserStrVarRec1 = _Structure(
+UserStrVarRec1 = _DynamicStructure(
     name='UserStrVarRec1',
     fields=[
-        _Field('c', 'name', help='Name of the string variable.', count=32),
+        ListedStaticStringField('c', 'name', help='Name of the string variable.', count=32),
         _Field('h', 'strLen', help='The real size of the following array.'),
-        _Field('c', 'data'),
+        ListedDynamicStrDataField('c', 'data'),
         ])
 
 # From Variables.h
-UserStrVarRec2 = _Structure(
+UserStrVarRec2 = _DynamicStructure(
     name='UserStrVarRec2',
     fields=[
-        _Field('c', 'name', help='Name of the string variable.', count=32),
+        ListedStaticStringField('c', 'name', help='Name of the string variable.', count=32),
         _Field('l', 'strLen', help='The real size of the following array.'),
         _Field('c', 'data'),
         ])
@@ -65,146 +166,134 @@ VarNumRec = _Structure(
         ])
 
 # From Variables.h
-UserNumVarRec = _Structure(
+UserNumVarRec = _DynamicStructure(
     name='UserNumVarRec',
     fields=[
-        _Field('c', 'name', help='Name of the string variable.', count=32),
+        ListedStaticStringField('c', 'name', help='Name of the string variable.', count=32),
         _Field('h', 'type', help='0 = string, 1 = numeric.'),
-        _Field(VarNumRec, 'num', help='Type and value of the variable if it is numeric.  Not used for string.'),
+        DynamicVarNumField(VarNumRec, 'num', help='Type and value of the variable if it is numeric.  Not used for string.'),
         ])
 
 # From Variables.h
-UserDependentVarRec = _Structure(
+UserDependentVarRec = _DynamicStructure(
     name='UserDependentVarRec',
     fields=[
-        _Field('c', 'name', help='Name of the string variable.', count=32),
+        ListedStaticStringField('c', 'name', help='Name of the string variable.', count=32),
         _Field('h', 'type', help='0 = string, 1 = numeric.'),
         _Field(VarNumRec, 'num', help='Type and value of the variable if it is numeric.  Not used for string.'),
         _Field('h', 'formulaLen', help='The length of the dependency formula.'),
-        _Field('c', 'formula', help='Start of the dependency formula. A C string including null terminator.'),
+        DynamicFormulaField('c', 'formula', help='Start of the dependency formula. A C string including null terminator.'),
         ])
 
 
-class VariablesRecord (Record):
-    def __init__(self, *args, **kwargs):
-        super(VariablesRecord, self).__init__(*args, **kwargs)
-        # self.header['version']  # record version always 0?
-        version = self._set_byte_order_and_get_version()
-        self.structure = self._get_structure(version)
-        self.variables = self.structure.unpack_from(self.data)
-        self.variables.update(self._unpack_variable_length_structures(version))
-        self._normalize_variables()
-
-    def _set_byte_order_and_get_version(self):
-        if self.byte_order:
-            VarHeaderCommon.set_byte_order(self.byte_order)
-        else:
-            VarHeaderCommon.set_byte_order('=')            
-        version = VarHeaderCommon.unpack_from(self.data)['version']
-        if not self.byte_order:
-            need_to_reorder = _need_to_reorder_bytes(version)
-            self.byte_order = _byte_order(need_to_reorder)
-            if need_to_reorder:
-                VarHeaderCommon.set_byte_order(self.byte_order)
-                version = VarHeaderCommon.unpack_from(self.data)['version']
-        return version
-
-    def _get_structure(self, version):
-        if version == 1:
-            header_struct = VarHeader1
-        elif version == 2:
-            header_struct = VarHeader2
+class DynamicVarHeaderField (_DynamicField):
+    def pre_pack(self, parents, data):
+        raise NotImplementedError()
+
+    def post_unpack(self, parents, data):
+        var_structure = parents[-1]
+        var_data = self._get_structure_data(
+            parents, data, var_structure)
+        var_header_structure = self.format
+        data = var_data['var_header']
+        sys_vars_field = var_structure.get_field('sysVars')
+        sys_vars_field.count = data['numSysVars']
+        sys_vars_field.setup()
+        user_vars_field = var_structure.get_field('userVars')
+        user_vars_field.count = data['numUserVars']
+        user_vars_field.setup()
+        user_strs_field = var_structure.get_field('userStrs')
+        user_strs_field.count = data['numUserStrs']
+        user_strs_field.setup()
+        if 'numDependentVars' in data:
+            dependent_vars_field = var_structure.get_field('dependentVars')
+            dependent_vars_field.count = data['numDependentVars']
+            dependent_vars_field.setup()
+            dependent_strs_field = var_structure.get_field('dependentStrs')
+            dependent_strs_field.count = data['numDependentStrs']
+            dependent_strs_field.setup()
+        var_structure.setup()
+
+
+Variables1 = _DynamicStructure(
+    name='Variables1',
+    fields=[
+        DynamicVarHeaderField(VarHeader1, 'var_header', help='Variables header'),
+        DynamicSysVarField('f', 'sysVars', help='System variables', count=0),
+        DynamicUserVarField(UserNumVarRec, 'userVars', help='User numeric variables', count=0),
+        DynamicUserStrField(UserStrVarRec1, 'userStrs', help='User string variables', count=0),
+        ])
+
+
+Variables2 = _DynamicStructure(
+    name='Variables2',
+    fields=[
+        DynamicVarHeaderField(VarHeader2, 'var_header', help='Variables header'),
+        DynamicSysVarField('f', 'sysVars', help='System variables', count=0),
+        DynamicUserVarField(UserNumVarRec, 'userVars', help='User numeric variables', count=0),
+        DynamicUserStrField(UserStrVarRec2, 'userStrs', help='User string variables', count=0),
+        _Field(UserDependentVarRec, 'dependentVars', help='Dependent numeric variables.', count=0),
+        _Field(UserDependentVarRec, 'dependentStrs', help='Dependent string variables.', count=0),
+        ])
+
+
+class DynamicVersionField (_DynamicField):
+    def pre_pack(self, parents, byte_order):
+        raise NotImplementedError()
+
+    def post_unpack(self, parents, data):
+        variables_structure = parents[-1]
+        variables_data = self._get_structure_data(
+            parents, data, variables_structure)
+        version = variables_data['version']
+        if variables_structure.byte_order in '@=':
+            need_to_reorder_bytes = _need_to_reorder_bytes(version)
+            variables_structure.byte_order = _byte_order(need_to_reorder_bytes)
+            _LOG.debug(
+                'get byte order from version: {} (reorder? {})'.format(
+                    variables_structure.byte_order, need_to_reorder_bytes))
         else:
-            raise NotImplementedError(
-                'Variables record version {}'.format(version))
-        header = header_struct.unpack_from(self.data)
-        fields = [
-            _Field(header_struct, 'header', help='VarHeader'),
-            _Field('f', 'sysVars', help='system variables',
-                   count=header['numSysVars']),
-            _Field(UserNumVarRec, 'userVars', help='user variables',
-                   count=header['numUserVars']),
-            ]
-        return _Structure(name='variables', fields=fields)
-
-    def _unpack_variable_length_structures(self, version):
-        data = {'userStrs': []}
-        offset = self.structure.size
+            need_to_reorder_bytes = False
 
+        old_format = variables_structure.fields[-1].format
         if version == 1:
-            user_str_var_struct = UserStrVarRec1
+            variables_structure.fields[-1].format = Variables1
         elif version == 2:
-            user_str_var_struct = UserStrVarRec2
-        else:
-            raise NotImplementedError(
-                'Variables record version {}'.format(version))
-        user_str_var_struct.set_byte_order(self.byte_order)
-        for i in range(self.variables['header']['numUserStrs']):
-            d = user_str_var_struct.unpack_from(self.data, offset)
-            offset += user_str_var_struct.size
-            end = offset + d['strLen'] - 1  # one character already in struct
-            if d['strLen']:
-                d['data'] = d['data'] + self.data[offset:end]
-            else:
-                d['data'] = ''
-            offset = end
-            data['userStrs'].append(d)
-
-        if version == 2:
-            data.update({'dependentVars': [], 'dependentStrs': []})
-            UserDependentVarRec.set_byte_order(self.byte_order)
-            for i in range(self.variables['header']['numDependentVars']):
-                d,offset = self._unpack_dependent_variable(offset)
-                data['dependentVars'].append(d)
-            for i in range(self.variables['header']['numDependentStrs']):
-                d,offset = self._unpack_dependent_variable(offset)
-                data['dependentStrs'].append(d)
-
-        if offset != len(self.data):
-            raise ValueError('too much data ({} extra bytes)'.format(
-                    len(self.data)-offset))
-        return data
-
-    def _unpack_dependent_variable(self, offset):
-        d = UserDependentVarRec.unpack_from(self.data, offset)
-        offset += UserDependentVarRec.size
-        end = offset + d['formulaLen'] - 1  # one character already in struct
-        if d['formulaLen']:
-            d['formula'] = d['formula'] + self.data[offset:end]
-        else:
-            d['formula'] = ''
-        offset = end
-        return (d, offset)
-
-    def _normalize_variables(self):
-        user_vars = {}
-        for num_var in self.variables['userVars']:
-            key,value = self._normalize_user_numeric_variable(num_var)
-            user_vars[key] = value
-        self.variables['userVars'] = user_vars
-        user_strs = {}
-        for str_var in self.variables['userStrs']:
-            name = self._normalize_null_terminated_string(str_var['name'])
-            user_strs[name] = str_var['data']
-        if self.variables['header']['version'] == 2:
-            raise NotImplementedError('normalize dependent variables')
-        self.variables['userStrs'] = user_strs
-
-    def _normalize_null_terminated_string(self, string):
-        return string.tostring().split('\x00', 1)[0]
-
-    def _normalize_user_numeric_variable(self, user_num_var):
-        user_num_var['name'] = self._normalize_null_terminated_string(
-            user_num_var['name'])
-        if user_num_var['type']:  # numeric
-            value = self._normalize_numeric_variable(user_num_var['num'])
-        else:  # string
-            value = None
-        return (user_num_var['name'], value)
+            variables_structure.fields[-1].format = Variables2
+        elif not need_to_reorder_bytes:
+            raise ValueError(
+                'invalid variables record version: {}'.format(version))
 
-    def _normalize_numeric_variable(self, num_var):
-        t = _TYPE_TABLE[num_var['numType']]
-        if num_var['numType'] % 2:  # complex number
-            return t(complex(num_var['realPart'], num_var['imagPart']))
-        else:
-            return t(num_var['realPart'])
+        if variables_structure.fields[-1].format != old_format:
+            _LOG.debug('change variables record from {} to {}'.format(
+                    old_format, variables_structure.fields[-1].format))
+            variables_structure.setup()
+        elif need_to_reorder_bytes:
+            variables_structure.setup()
+
+        # we might need to unpack again with the new byte order
+        return need_to_reorder_bytes
+
+
+VariablesRecordStructure = _DynamicStructure(
+    name='VariablesRecord',
+    fields=[
+        DynamicVersionField('h', 'version', help='Version number for this header.'),
+        _Field(Variables1, 'variables', help='The rest of the variables data.'),
+        ])
+
+
+class VariablesRecord (Record):
+    def __init__(self, *args, **kwargs):
+        super(VariablesRecord, self).__init__(*args, **kwargs)
+        # self.header['version']  # record version always 0?
+        VariablesRecordStructure.byte_order = '='
+        VariablesRecordStructure.setup()
+        stream = _io.BytesIO(bytes(self.data))
+        self.variables = VariablesRecordStructure.unpack_stream(stream)
+        self.namespace = {}
+        for key,value in self.variables['variables'].items():
+            if key not in ['var_header']:
+                _LOG.debug('update namespace {} with {} for {}'.format(
+                        self.namespace, value, key))
+                self.namespace.update(value)
index 93de8a31a48619b97cba08a1fa781efff05103f8..db36cfab202c18be44a5cb1687d2bcebbfdc84d5 100644 (file)
@@ -9,8 +9,7 @@ from . import Record
 class WaveRecord (Record):
     def __init__(self, *args, **kwargs):
         super(WaveRecord, self).__init__(*args, **kwargs)
-        self.wave,self.bin_info,self.wave_info = _loadibw(
-            _BytesIO(bytes(self.data)), strict=False)
+        self.wave = _loadibw(_BytesIO(bytes(self.data)))
 
     def __str__(self):
         return str(self.wave)
index c9886a05c36f174ceeb0a5474a64621bb8938d72..fce93b56798f74e07dc14c1e999b411bf852c802 100644 (file)
@@ -9,10 +9,15 @@ with each field in a hierarchy of Python dictionaries.
 """
 
 from __future__ import absolute_import
+import io as _io
+import logging as _logging
+import pprint as _pprint
 import struct as _struct
 
 import numpy as _numpy
 
+from . import LOG as _LOG
+
 
 _buffer = buffer  # save builtin buffer for clobbered situations
 
@@ -34,7 +39,7 @@ class Field (object):
 
     >>> time = Field(
     ...     'I', 'time', default=0, help='POSIX time')
-    >>> time.total_count
+    >>> time.arg_count
     1
     >>> list(time.pack_data(1))
     [1]
@@ -49,7 +54,7 @@ class Field (object):
 
     >>> data = Field(
     ...     'f', 'data', help='example data', count=(2,3,4))
-    >>> data.total_count
+    >>> data.arg_count
     24
     >>> list(data.indexes())  # doctest: +ELLIPSIS
     [[0, 0, 0], [0, 0, 1], [0, 0, 2], [0, 0, 3], [0, 1, 0], ..., [1, 2, 3]]
@@ -60,7 +65,7 @@ class Field (object):
     [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ..., 19, 20, 21, 22, 23]
     >>> list(data.pack_item(3))
     [3]
-    >>> data.unpack_data(range(data.total_count))
+    >>> data.unpack_data(range(data.arg_count))
     array([[[ 0,  1,  2,  3],
             [ 4,  5,  6,  7],
             [ 8,  9, 10, 11]],
@@ -75,10 +80,10 @@ class Field (object):
 
     >>> run = Structure('run', fields=[time, data])
     >>> runs = Field(run, 'runs', help='pair of runs', count=2)
-    >>> runs.total_count  # = 2 * (1 + 24)
+    >>> runs.arg_count  # = 2 * (1 + 24)
     50
-    >>> data1 = numpy.arange(data.total_count).reshape(data.count)
-    >>> data2 = data1 + data.total_count
+    >>> data1 = numpy.arange(data.arg_count).reshape(data.count)
+    >>> data2 = data1 + data.arg_count
     >>> list(runs.pack_data(
     ...     [{'time': 100, 'data': data1},
     ...      {'time': 101, 'data': data2}])
@@ -87,7 +92,7 @@ class Field (object):
     >>> list(runs.pack_item({'time': 100, 'data': data1})
     ...     )  # doctest: +ELLIPSIS
     [100, 0, 1, 2, ..., 22, 23]
-    >>> pprint(runs.unpack_data(range(runs.total_count)))
+    >>> pprint(runs.unpack_data(range(runs.arg_count)))
     [{'data': array([[[ 1,  2,  3,  4],
             [ 5,  6,  7,  8],
             [ 9, 10, 11, 12]],
@@ -137,12 +142,24 @@ class Field (object):
         self.default = default
         self.help = help
         self.count = count
-        self.item_count = _numpy.prod(count)  # number of item repeats
+        self.setup()
+
+    def setup(self):
+        """Setup any dynamic properties of a field.
+
+        Use this method to recalculate dynamic properities after
+        changing the basic properties set during initialization.
+        """
+        _LOG.debug('setup {}'.format(self))
+        self.item_count = _numpy.prod(self.count)  # number of item repeats
         if isinstance(self.format, Structure):
-            self.structure_count = sum(f.total_count for f in format.fields)
-            self.total_count = self.item_count * self.structure_count
+            self.structure_count = sum(
+                f.arg_count for f in self.format.fields)
+            self.arg_count = self.item_count * self.structure_count
+        elif self.format == 'x':
+            self.arg_count = 0  # no data in padding bytes
         else:
-            self.total_count = self.item_count  # struct.Struct format chars
+            self.arg_count = self.item_count  # struct.Struct format args
 
     def __str__(self):
         return self.__repr__()
@@ -220,9 +237,10 @@ class Field (object):
 
     def unpack_data(self, data):
         """Inverse of .pack_data"""
+        _LOG.debug('unpack {} for {} {}'.format(data, self, self.format))
         iterator = iter(data)
         try:
-            items = [iterator.next() for i in range(self.total_count)]
+            items = [iterator.next() for i in range(self.arg_count)]
         except StopIteration:
             raise ValueError('not enough data to unpack {}'.format(self))
         try:
@@ -238,7 +256,11 @@ class Field (object):
         else:
             items = [[i] for i in items]
         unpacked = [self.unpack_item(i) for i in items]
-        if self.count == 1:
+        if self.arg_count:
+            count = self.count
+        else:
+            count = 0  # padding bytes, etc.
+        if count == 1:
             return unpacked[0]
         if isinstance(self.format, Structure):
             try:
@@ -249,10 +271,12 @@ class Field (object):
                 raise NotImplementedError('reshape Structure field')
         else:
             unpacked = _numpy.array(unpacked)
-            unpacked = unpacked.reshape(self.count)
+            _LOG.debug('reshape {} data from {} to {}'.format(
+                    self, unpacked.shape, count))
+            unpacked = unpacked.reshape(count)
         return unpacked
 
-    def unpack_item(self, item): 
+    def unpack_item(self, item):
         """Inverse of .unpack_item"""
         if isinstance(self.format, Structure):
             return self.format._unpack_item(item)
@@ -261,6 +285,54 @@ class Field (object):
             return item[0]
 
 
+class DynamicField (Field):
+    """Represent a DynamicStructure field with a dynamic definition.
+
+    Adds the methods ``.pre_pack``, ``pre_unpack``, and
+    ``post_unpack``, all of which are called when a ``DynamicField``
+    is used by a ``DynamicStructure``.  Each method takes the
+    arguments ``(parents, data)``, where ``parents`` is a list of
+    ``DynamicStructure``\s that own the field and ``data`` is a dict
+    hierarchy of the structure data.
+
+    See the ``DynamicStructure`` docstring for the exact timing of the
+    method calls.
+
+    See Also
+    --------
+    Field, DynamicStructure
+    """
+    def pre_pack(self, parents, data):
+        "Prepare to pack."
+        pass
+
+    def pre_unpack(self, parents, data):
+        "React to previously unpacked data"
+        pass
+
+    def post_unpack(self, parents, data):
+        "React to our own data"
+        pass
+
+    def _get_structure_data(self, parents, data, structure):
+        """Extract the data belonging to a particular ancestor structure.
+        """
+        d = data
+        s = parents[0]
+        if s == structure:
+            return d
+        for p in parents[1:]:
+            for f in s.fields:
+                if f.format == p:
+                    s = p
+                    d = d[f.name]
+                    break
+            assert s == p, (s, p)
+            if p == structure:
+                break
+        return d
+
+
 class Structure (_struct.Struct):
     r"""Represent a C structure.
 
@@ -289,7 +361,7 @@ class Structure (_struct.Struct):
           struct run runs[2];
         }
 
-    As
+    As:
 
     >>> time = Field('I', 'time', default=0, help='POSIX time')
     >>> data = Field(
@@ -303,19 +375,30 @@ class Structure (_struct.Struct):
     The structures automatically calculate the flattened data format:
 
     >>> run.format
-    '=Ihhhhhh'
+    '@Ihhhhhh'
     >>> run.size  # 4 + 2*3*2
     16
     >>> experiment.format
-    '=HIhhhhhhIhhhhhh'
-    >>> experiment.size  # 2 + 2*(4 + 2*3*2)
+    '@HIhhhhhhIhhhhhh'
+    >>> experiment.size  # 2 + 2 + 2*(4 + 2*3*2)
+    36
+
+    The first two elements in the above size calculation are 2 (for
+    the unsigned short, 'H') and 2 (padding so the unsigned int aligns
+    with a 4-byte block).  If you select a byte ordering that doesn't
+    mess with alignment and recalculate the format, the padding goes
+    away and you get:
+
+    >>> experiment.set_byte_order('>')
+    >>> experiment.get_format()
+    '>HIhhhhhhIhhhhhh'
+    >>> experiment.size
     34
 
     You can read data out of any object supporting the buffer
     interface:
 
     >>> b = array.array('B', range(experiment.size))
-    >>> experiment.set_byte_order('>')
     >>> d = experiment.unpack_from(buffer=b)
     >>> pprint(d)
     {'runs': [{'data': array([[1543, 2057, 2571],
@@ -375,12 +458,15 @@ class Structure (_struct.Struct):
     >>> b2 == b
     True
     """
-    def __init__(self, name, fields, byte_order='='):
+    _byte_order_symbols = '@=<>!'
+
+    def __init__(self, name, fields, byte_order='@'):
         # '=' for native byte order, standard size and alignment
         # See http://docs.python.org/library/struct for details
         self.name = name
         self.fields = fields
-        self.set_byte_order(byte_order)
+        self.byte_order = byte_order
+        self.setup()
 
     def __str__(self):
         return self.name
@@ -389,25 +475,46 @@ class Structure (_struct.Struct):
         return '<{} {} {}>'.format(
             self.__class__.__name__, self.name, id(self))
 
+    def setup(self):
+        """Setup any dynamic properties of a structure.
+
+        Use this method to recalculate dynamic properities after
+        changing the basic properties set during initialization.
+        """
+        _LOG.debug('setup {!r}'.format(self))
+        self.set_byte_order(self.byte_order)
+        self.get_format()
+
     def set_byte_order(self, byte_order):
         """Allow changing the format byte_order on the fly.
         """
-        if (hasattr(self, 'format') and self.format != None
-            and self.format.startswith(byte_order)):
-            return  # no need to change anything
-        format = []
+        _LOG.debug('set byte order for {!r} to {}'.format(self, byte_order))
+        self.byte_order = byte_order
         for field in self.fields:
             if isinstance(field.format, Structure):
-                field_format = field.format.sub_format(
-                    ) * field.item_count
-            else:
-                field_format = [field.format]*field.item_count
-            format.extend(field_format)
-        super(Structure, self).__init__(
-            format=byte_order+''.join(format).replace('P', 'L'))
+                field.format.set_byte_order(byte_order)
+
+    def get_format(self):
+        format = self.byte_order + ''.join(self.sub_format())
+        # P format only allowed for native byte ordering
+        # Convert P to I for ILP32 compatibility when running on a LP64.
+        format = format.replace('P', 'I')
+        try:
+            super(Structure, self).__init__(format=format)
+        except _struct.error as e:
+            raise ValueError((e, format))
+        return format
 
     def sub_format(self):
-        return self.format.lstrip('=<>')  # byte order handled by parent
+        _LOG.debug('calculate sub-format for {!r}'.format(self))
+        for field in self.fields:
+            if isinstance(field.format, Structure):
+                field_format = list(
+                    field.format.sub_format()) * field.item_count
+            else:
+                field_format = [field.format]*field.item_count
+            for fmt in field_format:
+                yield fmt
 
     def _pack_item(self, item=None):
         """Linearize a single count of the structure's data to a flat iterable
@@ -430,7 +537,7 @@ class Structure (_struct.Struct):
         iterator = iter(args)
         for f in self.fields:
             try:
-                items = [iterator.next() for i in range(f.total_count)]
+                items = [iterator.next() for i in range(f.arg_count)]
             except StopIteration:
                 raise ValueError('not enough data to unpack {}.{}'.format(
                         self, f))
@@ -445,7 +552,10 @@ class Structure (_struct.Struct):
 
     def pack(self, data):
         args = list(self._pack_item(data))
-        return super(Structure, self).pack(*args)
+        try:
+            return super(Structure, self).pack(*args)
+        except:
+            raise ValueError(self.format)
 
     def pack_into(self, buffer, offset=0, data={}):
         args = list(self._pack_item(data))
@@ -457,20 +567,247 @@ class Structure (_struct.Struct):
         return self._unpack_item(args)
 
     def unpack_from(self, buffer, offset=0, *args, **kwargs):
-        try:
-            args = super(Structure, self).unpack_from(
-                buffer, offset, *args, **kwargs)
-        except _struct.error as e:
-            if not self.name in ('WaveHeader2', 'WaveHeader5'):
-                raise
-            # HACK!  For WaveHeader5, when npnts is 0, wData is
-            # optional.  If we couldn't unpack the structure, fill in
-            # wData with zeros and try again, asserting that npnts is
-            # zero.
-            if len(buffer) - offset < self.size:
-                # missing wData?  Pad with zeros
-                buffer += _buffer('\x00'*(self.size + offset - len(buffer)))
-            args = super(Structure, self).unpack_from(buffer, offset)
-            data = self._unpack_item(args)
-            assert data['npnts'] == 0, data['npnts']
+        _LOG.debug(
+            'unpack {!r} for {!r} ({}, offset={}) with {} ({})'.format(
+                buffer, self, len(buffer), offset, self.format, self.size))
+        args = super(Structure, self).unpack_from(
+            buffer, offset, *args, **kwargs)
+        return self._unpack_item(args)
+
+    def get_field(self, name):
+        return [f for f in self.fields if f.name == name][0]
+
+
+class DebuggingStream (object):
+    def __init__(self, stream):
+        self.stream = stream
+
+    def read(self, size):
+        data = self.stream.read(size)
+        _LOG.debug('read {} from {}: ({}) {!r}'.format(
+                size, self.stream, len(data), data))
+        return data
+
+
+class DynamicStructure (Structure):
+    r"""Represent a C structure field with a dynamic definition.
+
+    Any dynamic fields have their ``.pre_pack`` called before any
+    structure packing is done.  ``.pre_unpack`` is called for a
+    particular field just before that field's ``.unpack_data`` call.
+    ``.post_unpack`` is called for a particular field just after
+    ``.unpack_data``.  If ``.post_unpack`` returns ``True``, the same
+    field is unpacked again.
+
+    Examples
+    --------
+
+    >>> from pprint import pprint
+
+    This allows you to define structures where some portion of the
+    global structure depends on earlier data.  For example, in the
+    quasi-C structure::
+
+        struct vector {
+          unsigned int length;
+          short data[length];
+        }
+
+    You can generate a Python version of this structure in two ways,
+    with a dynamic ``length``, or with a dynamic ``data``.  In both
+    cases, the required methods are the same, the only difference is
+    where you attach them.
+
+    >>> def packer(self, parents, data):
+    ...     vector_structure = parents[-1]
+    ...     vector_data = self._get_structure_data(
+    ...         parents, data, vector_structure)
+    ...     length = len(vector_data['data'])
+    ...     vector_data['length'] = length
+    ...     data_field = vector_structure.get_field('data')
+    ...     data_field.count = length
+    ...     data_field.setup()
+    >>> def unpacker(self, parents, data):
+    ...     vector_structure = parents[-1]
+    ...     vector_data = self._get_structure_data(
+    ...         parents, data, vector_structure)
+    ...     length = vector_data['length']
+    ...     data_field = vector_structure.get_field('data')
+    ...     data_field.count = length
+    ...     data_field.setup()
+
+    >>> class DynamicLengthField (DynamicField):
+    ...     def pre_pack(self, parents, data):
+    ...         packer(self, parents, data)
+    ...     def post_unpack(self, parents, data):
+    ...         unpacker(self, parents, data)
+    >>> dynamic_length_vector = DynamicStructure('vector',
+    ...     fields=[
+    ...         DynamicLengthField('I', 'length'),
+    ...         Field('h', 'data', count=0),
+    ...         ],
+    ...     byte_order='>')
+    >>> class DynamicDataField (DynamicField):
+    ...     def pre_pack(self, parents, data):
+    ...         packer(self, parents, data)
+    ...     def pre_unpack(self, parents, data):
+    ...         unpacker(self, parents, data)
+    >>> dynamic_data_vector = DynamicStructure('vector',
+    ...     fields=[
+    ...         Field('I', 'length'),
+    ...         DynamicDataField('h', 'data', count=0),
+    ...         ],
+    ...     byte_order='>')
+
+    >>> b = '\x00\x00\x00\x02\x01\x02\x03\x04'
+    >>> d = dynamic_length_vector.unpack(b)
+    >>> pprint(d)
+    {'data': array([258, 772]), 'length': 2}
+    >>> d = dynamic_data_vector.unpack(b)
+    >>> pprint(d)
+    {'data': array([258, 772]), 'length': 2}
+
+    >>> d['data'] = [1,2,3,4]
+    >>> dynamic_length_vector.pack(d)
+    '\x00\x00\x00\x04\x00\x01\x00\x02\x00\x03\x00\x04'
+    >>> dynamic_data_vector.pack(d)
+    '\x00\x00\x00\x04\x00\x01\x00\x02\x00\x03\x00\x04'
+
+    The implementation is a good deal more complicated than the one
+    for ``Structure``, because we must make multiple calls to
+    ``struct.Struct.unpack`` to unpack the data.
+    """
+    #def __init__(self, *args, **kwargs):
+    #     pass #self.parent = ..
+
+    def _pre_pack(self, parents=None, data=None):
+        if parents is None:
+            parents = [self]
+        else:
+            parents = parents + [self]
+        for f in self.fields:
+            if hasattr(f, 'pre_pack'):
+                _LOG.debug('pre-pack {}'.format(f))
+                f.pre_pack(parents=parents, data=data)
+            if isinstance(f.format, DynamicStructure):
+                _LOG.debug('pre-pack {!r}'.format(f.format))
+                f._pre_pack(parents=parents, data=data)
+
+    def pack(self, data):
+        self._pre_pack(data=data)
+        self.setup()
+        return super(DynamicStructure, self).pack(data)
+
+    def pack_into(self, buffer, offset=0, data={}):
+        self._pre_pack(data=data)
+        self.setup()
+        return super(DynamicStructure, self).pack_into(
+            buffer=buffer, offset=offset, data=data)
+
+    def unpack_stream(self, stream, parents=None, data=None, d=None):
+        # `d` is the working data directory
+        if data is None:
+            parents = [self]
+            data = d = {}
+            if _LOG.level == _logging.DEBUG:
+                stream = DebuggingStream(stream)
+        else:
+            parents = parents + [self]
+
+        for f in self.fields:
+            _LOG.debug('parsing {!r}.{} (count={}, item_count={})'.format(
+                    self, f, f.count, f.item_count))
+            _LOG.debug('data:\n{}'.format(_pprint.pformat(data)))
+            if hasattr(f, 'pre_unpack'):
+                _LOG.debug('pre-unpack {}'.format(f))
+                f.pre_unpack(parents=parents, data=data)
+
+            if hasattr(f, 'unpack'):  # override default unpacking
+                _LOG.debug('override unpack for {}'.format(f))
+                d[f.name] = f.unpack(stream)
+                continue
+
+            # setup for unpacking loop
+            if isinstance(f.format, Structure):
+                f.format.set_byte_order(self.byte_order)
+                f.setup()
+                f.format.setup()
+                if isinstance(f.format, DynamicStructure):
+                    if f.item_count == 1:
+                        # TODO, fix in case we *want* an array
+                        d[f.name] = {}
+                        f.format.unpack_stream(
+                            stream, parents=parents, data=data, d=d[f.name])
+                    else:
+                        d[f.name] = []
+                        for i in range(f.item_count):
+                            x = {}
+                            d[f.name].append(x)
+                            f.format.unpack_stream(
+                                stream, parents=parents, data=data, d=x)
+                    if hasattr(f, 'post_unpack'):
+                        _LOG.debug('post-unpack {}'.format(f))
+                        repeat = f.post_unpack(parents=parents, data=data)
+                        if repeat:
+                            raise NotImplementedError(
+                                'cannot repeat unpack for dynamic structures')
+                    continue
+            if isinstance(f.format, Structure):
+                _LOG.debug('parsing {} bytes for {}'.format(
+                        f.format.size, f.format.format))
+                bs = [stream.read(f.format.size) for i in range(f.item_count)]
+                def unpack():
+                    f.format.set_byte_order(self.byte_order)
+                    f.setup()
+                    f.format.setup()
+                    x = [f.format.unpack_from(b) for b in bs]
+                    if len(x) == 1:  # TODO, fix in case we *want* an array
+                        x = x[0]
+                    return x
+            else:
+                field_format = self.byte_order + f.format*f.item_count
+                field_format = field_format.replace('P', 'I')
+                try:
+                    size = _struct.calcsize(field_format)
+                except _struct.error as e:
+                    _LOG.error(e)
+                    _LOG.error('{}.{}: {}'.format(self, f, field_format))
+                    raise
+                _LOG.debug('parsing {} bytes for preliminary {}'.format(
+                        size, field_format))
+                raw = stream.read(size)
+                if len(raw) < size:
+                    raise ValueError(
+                        'not enough data to unpack {}.{} ({} < {})'.format(
+                            self, f, len(raw), size))
+                def unpack():
+                    field_format = self.byte_order + f.format*f.item_count
+                    field_format = field_format.replace('P', 'I')
+                    _LOG.debug('parse previous bytes using {}'.format(
+                            field_format))
+                    struct = _struct.Struct(field_format)
+                    items = struct.unpack(raw)
+                    return f.unpack_data(items)
+
+            # unpacking loop
+            repeat = True
+            while repeat:
+                d[f.name] = unpack()
+                if hasattr(f, 'post_unpack'):
+                    _LOG.debug('post-unpack {}'.format(f))
+                    repeat = f.post_unpack(parents=parents, data=data)
+                else:
+                    repeat = False
+                if repeat:
+                    _LOG.debug('repeat unpack for {}'.format(f))
+
+        return data
+
+    def unpack(self, string):
+        stream = _io.BytesIO(string)
+        return self.unpack_stream(stream)
+
+    def unpack_from(self, buffer, offset=0, *args, **kwargs):
+        args = super(Structure, self).unpack_from(
+            buffer, offset, *args, **kwargs)
         return self._unpack_item(args)
index af65c79c2fd7534c0b6b47dffa15d9514265ed9d..1567ff31e84297fc310861180bb1b9786b011259 100644 (file)
 
 r"""Test the igor module by loading sample files.
 
->>> dumpibw('mac-double.ibw', strict=False)  # doctest: +REPORT_UDIFF
-array([ 5.,  4.,  3.,  2.,  1.])
-{'checksum': 25137,
- 'note': '',
- 'noteSize': 0,
- 'pictSize': 0,
- 'version': 2,
- 'wfmSize': 166}
-{'aModified': 0,
- 'bname': array(['d', 'o', 'u', 'b', 'l', 'e', '', '', '', '', '', '', '', '', '',
-       '', '', '', '', ''],
-      dtype='|S1'),
- 'botFullScale': 0.0,
- 'creationDate': 3001587842,
- 'dataUnits': array(['', '', '', ''],
-      dtype='|S1'),
- 'depID': 0,
- 'fileName': 0,
- 'formula': 0,
- 'fsValid': 0,
- 'hsA': 1.0,
- 'hsB': 0.0,
- 'kindBits': '\x00',
- 'modDate': 3001587842,
- 'next': 0,
- 'npnts': 5,
- 'srcFldr': 0,
- 'swModified': 0,
- 'topFullScale': 0.0,
- 'type': 4,
- 'useBits': '\x00',
- 'wData': array([ 2.3125,  0.    ,  2.25  ,  0.    ]),
- 'wModified': 0,
- 'wUnused': array(['', ''],
-      dtype='|S1'),
- 'waveNoteH': 0,
- 'whVersion': 0,
- 'xUnits': array(['', '', '', ''],
-      dtype='|S1')}
+>>> dumpibw('mac-double.ibw')  # doctest: +REPORT_UDIFF
+{'version': 2,
+ 'wave': {'bin_header': {'checksum': 25137,
+                         'noteSize': 0,
+                         'pictSize': 0,
+                         'wfmSize': 166},
+          'note': '',
+          'padding': array([], dtype=float64),
+          'wData': array([ 5.,  4.,  3.,  2.,  1.]),
+          'wave_header': {'aModified': 0,
+                          'bname': 'double',
+                          'botFullScale': 0.0,
+                          'creationDate': 3001587842,
+                          'dataUnits': array(['', '', '', ''],
+      dtype='|S1'),
+                          'depID': 0,
+                          'fileName': 0,
+                          'formula': 0,
+                          'fsValid': 0,
+                          'hsA': 1.0,
+                          'hsB': 0.0,
+                          'kindBits': '\x00',
+                          'modDate': 3001587842,
+                          'next': 0,
+                          'npnts': 5,
+                          'srcFldr': 0,
+                          'swModified': 0,
+                          'topFullScale': 0.0,
+                          'type': 4,
+                          'useBits': '\x00',
+                          'wModified': 0,
+                          'wUnused': array(['', ''],
+      dtype='|S1'),
+                          'waveNoteH': 0,
+                          'whVersion': 0,
+                          'xUnits': array(['', '', '', ''],
+      dtype='|S1')}}}
 
 >>> dumpibw('mac-textWave.ibw')  # doctest: +REPORT_UDIFF
-array(['Mary', 'had', 'a', 'little', 'lamb'],
-      dtype='|S6')
-{'checksum': 5554,
- 'dataEUnits': '',
- 'dataEUnitsSize': 0,
- 'dimEUnits': ['', '', '', ''],
- 'dimEUnitsSize': array([0, 0, 0, 0]),
- 'dimLabels': [[], [], [], []],
- 'dimLabelsSize': array([0, 0, 0, 0]),
- 'formula': '',
- 'formulaSize': 0,
- 'note': '',
- 'noteSize': 0,
- 'optionsSize1': 0,
- 'optionsSize2': 0,
- 'sIndices': array([ 4,  7,  8, 14, 18], dtype=uint32),
- 'sIndicesSize': 20,
- 'version': 5,
- 'wfmSize': 338}
-{'aModified': 0,
- 'bname': array(['t', 'e', 'x', 't', '0', '', '', '', '', '', '', '', '', '', '', '',
-       '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''],
-      dtype='|S1'),
- 'botFullScale': 0.0,
- 'creationDate': 3001571199,
- 'dFolder': 69554896,
- 'dLock': 0,
- 'dataEUnits': 0,
- 'dataUnits': array(['', '', '', ''],
-      dtype='|S1'),
- 'depID': 22,
- 'dimEUnits': array([0, 0, 0, 0]),
- 'dimLabels': array([0, 0, 0, 0]),
- 'dimUnits': array([['', '', '', ''],
+{'version': 5,
+ 'wave': {'bin_header': {'checksum': 5554,
+                         'dataEUnitsSize': 0,
+                         'dimEUnitsSize': array([0, 0, 0, 0]),
+                         'dimLabelsSize': array([0, 0, 0, 0]),
+                         'formulaSize': 0,
+                         'noteSize': 0,
+                         'optionsSize1': 0,
+                         'optionsSize2': 0,
+                         'sIndicesSize': 20,
+                         'wfmSize': 338},
+          'data_units': '',
+          'dimension_units': '',
+          'formula': '',
+          'labels': [[], [], [], []],
+          'note': '',
+          'sIndices': array([ 4,  7,  8, 14, 18]),
+          'wData': array(['Mary', 'had', 'a', 'little', 'lamb'],
+      dtype='|S6'),
+          'wave_header': {'aModified': 0,
+                          'bname': 'text0',
+                          'botFullScale': 0.0,
+                          'creationDate': 3001571199,
+                          'dFolder': 69554896,
+                          'dLock': 0,
+                          'dataEUnits': 0,
+                          'dataUnits': array(['', '', '', ''],
+      dtype='|S1'),
+                          'depID': 22,
+                          'dimEUnits': array([0, 0, 0, 0]),
+                          'dimLabels': array([0, 0, 0, 0]),
+                          'dimUnits': array([['', '', '', ''],
        ['', '', '', ''],
        ['', '', '', ''],
        ['', '', '', '']],
       dtype='|S1'),
- 'fileName': 0,
- 'formula': 0,
- 'fsValid': 0,
- 'kindBits': '\x00',
- 'modDate': 3001571215,
- 'nDim': array([5, 0, 0, 0]),
- 'next': 0,
- 'npnts': 5,
- 'sIndices': 69557296,
- 'sfA': array([ 1.,  1.,  1.,  1.]),
- 'sfB': array([ 0.,  0.,  0.,  0.]),
- 'srcFldr': 0,
- 'swModified': 1,
- 'topFullScale': 0.0,
- 'type': 0,
- 'useBits': '\x00',
- 'wData': 236398480.0,
- 'wModified': 0,
- 'waveNoteH': 0,
- 'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
- 'whVersion': 1,
- 'whpad1': array(['', '', '', '', '', ''],
-      dtype='|S1'),
- 'whpad2': 0,
- 'whpad3': 0,
- 'whpad4': 0}
+                          'fileName': 0,
+                          'formula': 0,
+                          'fsValid': 0,
+                          'kindBits': '\x00',
+                          'modDate': 3001571215,
+                          'nDim': array([5, 0, 0, 0]),
+                          'next': 0,
+                          'npnts': 5,
+                          'sIndices': 69557296,
+                          'sfA': array([ 1.,  1.,  1.,  1.]),
+                          'sfB': array([ 0.,  0.,  0.,  0.]),
+                          'srcFldr': 0,
+                          'swModified': 1,
+                          'topFullScale': 0.0,
+                          'type': 0,
+                          'useBits': '\x00',
+                          'wModified': 0,
+                          'waveNoteH': 0,
+                          'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
+                          'whVersion': 1,
+                          'whpad1': array(['', '', '', '', '', ''],
+      dtype='|S1'),
+                          'whpad2': 0,
+                          'whpad3': 0,
+                          'whpad4': 0}}}
 
->>> dumpibw('mac-version2.ibw', strict=False)  # doctest: +REPORT_UDIFF
-array([ 5.,  4.,  3.,  2.,  1.], dtype=float32)
-{'checksum': -16803,
- 'note': 'This is a test.',
- 'noteSize': 15,
- 'pictSize': 0,
- 'version': 2,
- 'wfmSize': 146}
-{'aModified': 0,
- 'bname': array(['v', 'e', 'r', 's', 'i', 'o', 'n', '2', '', '', '', '', '', '', '',
-       '', '', '', '', ''],
-      dtype='|S1'),
- 'botFullScale': 0.0,
- 'creationDate': 3001251979,
- 'dataUnits': array(['', '', '', ''],
-      dtype='|S1'),
- 'depID': 0,
- 'fileName': 0,
- 'formula': 0,
- 'fsValid': 0,
- 'hsA': 1.0,
- 'hsB': 0.0,
- 'kindBits': '\x00',
- 'modDate': 3001573594,
- 'next': 0,
- 'npnts': 5,
- 'srcFldr': 0,
- 'swModified': 0,
- 'topFullScale': 0.0,
- 'type': 2,
- 'useBits': '\x00',
- 'wData': array([ 5.,  4.,  3.,  2.]),
- 'wModified': 0,
- 'wUnused': array(['', ''],
-      dtype='|S1'),
- 'waveNoteH': 0,
- 'whVersion': 0,
- 'xUnits': array(['', '', '', ''],
-      dtype='|S1')}
+>>> dumpibw('mac-version2.ibw')  # doctest: +REPORT_UDIFF
+{'version': 2,
+ 'wave': {'bin_header': {'checksum': -16803,
+                         'noteSize': 15,
+                         'pictSize': 0,
+                         'wfmSize': 146},
+          'note': 'This is a test.',
+          'padding': array([], dtype=float64),
+          'wData': array([ 5.,  4.,  3.,  2.,  1.], dtype=float32),
+          'wave_header': {'aModified': 0,
+                          'bname': 'version2',
+                          'botFullScale': 0.0,
+                          'creationDate': 3001251979,
+                          'dataUnits': array(['', '', '', ''],
+      dtype='|S1'),
+                          'depID': 0,
+                          'fileName': 0,
+                          'formula': 0,
+                          'fsValid': 0,
+                          'hsA': 1.0,
+                          'hsB': 0.0,
+                          'kindBits': '\x00',
+                          'modDate': 3001573594,
+                          'next': 0,
+                          'npnts': 5,
+                          'srcFldr': 0,
+                          'swModified': 0,
+                          'topFullScale': 0.0,
+                          'type': 2,
+                          'useBits': '\x00',
+                          'wModified': 0,
+                          'wUnused': array(['', ''],
+      dtype='|S1'),
+                          'waveNoteH': 0,
+                          'whVersion': 0,
+                          'xUnits': array(['', '', '', ''],
+      dtype='|S1')}}}
 
->>> dumpibw('mac-version3Dependent.ibw', strict=False)  # doctest: +REPORT_UDIFF
-array([], dtype=int8)
-{'checksum': 0,
- 'formula': '',
- 'formulaSize': 0,
- 'note': '',
- 'noteSize': 8257536,
- 'pictSize': 262144,
- 'version': 3,
- 'wfmSize': 0}
-{'aModified': 10,
- 'bname': array(['', '', 'v', 'e', 'r', 's', 'i', 'o', 'n', '3', 'D', 'e', 'p', 'e',
-       'n', 'd', 'e', 'n', 't', ''],
-      dtype='|S1'),
- 'botFullScale': 0.0,
- 'creationDate': 1507328,
- 'dataUnits': array(['', '', '', ''],
-      dtype='|S1'),
- 'depID': -487849984,
- 'fileName': 0,
- 'formula': 1577,
- 'fsValid': 1,
- 'hsA': 4.5193417557662e-309,
- 'hsB': 0.0,
- 'kindBits': '\x00',
- 'modDate': 45801,
- 'next': 131072,
- 'npnts': 0,
- 'srcFldr': 0,
- 'swModified': 1,
- 'topFullScale': 0.0,
- 'type': -32334,
- 'useBits': '\x00',
- 'wData': array([ 0.,  0.,  0.,  0.]),
- 'wModified': 0,
- 'wUnused': array(['', ''],
-      dtype='|S1'),
- 'waveNoteH': 3835494400,
- 'whVersion': 0,
- 'xUnits': array(['', '', '', ''],
-      dtype='|S1')}
+>>> dumpibw('mac-version3Dependent.ibw')  # doctest: +REPORT_UDIFF
+{'version': 3,
+ 'wave': {'bin_header': {'checksum': -32334,
+                         'formulaSize': 4,
+                         'noteSize': 0,
+                         'pictSize': 0,
+                         'wfmSize': 126},
+          'formula': ' K0',
+          'note': '',
+          'padding': array([], dtype=float64),
+          'wData': array([], dtype=float32),
+          'wave_header': {'aModified': 3,
+                          'bname': 'version3Dependent',
+                          'botFullScale': 0.0,
+                          'creationDate': 0,
+                          'dataUnits': array(['', '', '', ''],
+      dtype='|S1'),
+                          'depID': 23,
+                          'fileName': 0,
+                          'formula': 103408364,
+                          'fsValid': 0,
+                          'hsA': 1.0,
+                          'hsB': 0.0,
+                          'kindBits': '\x00',
+                          'modDate': 3001672861,
+                          'next': 0,
+                          'npnts': 10,
+                          'srcFldr': 0,
+                          'swModified': 1,
+                          'topFullScale': 0.0,
+                          'type': 2,
+                          'useBits': '\x00',
+                          'wModified': 1,
+                          'wUnused': array(['', ''],
+      dtype='|S1'),
+                          'waveNoteH': 0,
+                          'whVersion': 0,
+                          'xUnits': array(['', '', '', ''],
+      dtype='|S1')}}}
 
 >>> dumpibw('mac-version5.ibw')  # doctest: +REPORT_UDIFF
-array([ 5.,  4.,  3.,  2.,  1.], dtype=float32)
-{'checksum': -12033,
- 'dataEUnits': '',
- 'dataEUnitsSize': 0,
- 'dimEUnits': ['', '', '', ''],
- 'dimEUnitsSize': array([0, 0, 0, 0]),
- 'dimLabels': [['Column0'], [], [], []],
- 'dimLabelsSize': array([64,  0,  0,  0]),
- 'formula': '',
- 'formulaSize': 0,
- 'note': 'This is a test.',
- 'noteSize': 15,
- 'optionsSize1': 0,
- 'optionsSize2': 0,
- 'sIndicesSize': 0,
- 'version': 5,
- 'wfmSize': 340}
-{'aModified': 0,
- 'bname': array(['v', 'e', 'r', 's', 'i', 'o', 'n', '5', '', '', '', '', '', '', '',
-       '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''],
-      dtype='|S1'),
- 'botFullScale': 0.0,
- 'creationDate': 3001252180,
- 'dFolder': 69554896,
- 'dLock': 0,
- 'dataEUnits': 0,
- 'dataUnits': array(['', '', '', ''],
-      dtype='|S1'),
- 'depID': 27,
- 'dimEUnits': array([0, 0, 0, 0]),
- 'dimLabels': array([69554136,        0,        0,        0]),
- 'dimUnits': array([['', '', '', ''],
+{'version': 5,
+ 'wave': {'bin_header': {'checksum': -12033,
+                         'dataEUnitsSize': 0,
+                         'dimEUnitsSize': array([0, 0, 0, 0]),
+                         'dimLabelsSize': array([64,  0,  0,  0]),
+                         'formulaSize': 0,
+                         'noteSize': 15,
+                         'optionsSize1': 0,
+                         'optionsSize2': 0,
+                         'sIndicesSize': 0,
+                         'wfmSize': 340},
+          'data_units': '',
+          'dimension_units': '',
+          'formula': '',
+          'labels': [['Column0'], [], [], []],
+          'note': 'This is a test.',
+          'sIndices': array([], dtype=float64),
+          'wData': array([ 5.,  4.,  3.,  2.,  1.], dtype=float32),
+          'wave_header': {'aModified': 0,
+                          'bname': 'version5',
+                          'botFullScale': 0.0,
+                          'creationDate': 3001252180,
+                          'dFolder': 69554896,
+                          'dLock': 0,
+                          'dataEUnits': 0,
+                          'dataUnits': array(['', '', '', ''],
+      dtype='|S1'),
+                          'depID': 27,
+                          'dimEUnits': array([0, 0, 0, 0]),
+                          'dimLabels': array([69554136,        0,        0,        0]),
+                          'dimUnits': array([['', '', '', ''],
        ['', '', '', ''],
        ['', '', '', ''],
        ['', '', '', '']],
       dtype='|S1'),
- 'fileName': 69554292,
- 'formula': 0,
- 'fsValid': 0,
- 'kindBits': '\x00',
- 'modDate': 3001573601,
- 'nDim': array([5, 0, 0, 0]),
- 'next': 69555212,
- 'npnts': 5,
- 'sIndices': 0,
- 'sfA': array([ 1.,  1.,  1.,  1.]),
- 'sfB': array([ 0.,  0.,  0.,  0.]),
- 'srcFldr': -32349,
- 'swModified': 1,
- 'topFullScale': 0.0,
- 'type': 2,
- 'useBits': '\x00',
- 'wData': 5.0,
- 'wModified': 0,
- 'waveNoteH': 69554032,
- 'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
- 'whVersion': 1,
- 'whpad1': array(['', '', '', '', '', ''],
-      dtype='|S1'),
- 'whpad2': 0,
- 'whpad3': 0,
- 'whpad4': 0}
+                          'fileName': 69554292,
+                          'formula': 0,
+                          'fsValid': 0,
+                          'kindBits': '\x00',
+                          'modDate': 3001573601,
+                          'nDim': array([5, 0, 0, 0]),
+                          'next': 69555212,
+                          'npnts': 5,
+                          'sIndices': 0,
+                          'sfA': array([ 1.,  1.,  1.,  1.]),
+                          'sfB': array([ 0.,  0.,  0.,  0.]),
+                          'srcFldr': -32349,
+                          'swModified': 1,
+                          'topFullScale': 0.0,
+                          'type': 2,
+                          'useBits': '\x00',
+                          'wModified': 0,
+                          'waveNoteH': 69554032,
+                          'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
+                          'whVersion': 1,
+                          'whpad1': array(['', '', '', '', '', ''],
+      dtype='|S1'),
+                          'whpad2': 0,
+                          'whpad3': 0,
+                          'whpad4': 0}}}
 
 >>> dumpibw('mac-zeroPointWave.ibw')  # doctest: +REPORT_UDIFF
-array([], dtype=float32)
-{'checksum': -15649,
- 'dataEUnits': '',
- 'dataEUnitsSize': 0,
- 'dimEUnits': ['', '', '', ''],
- 'dimEUnitsSize': array([0, 0, 0, 0]),
- 'dimLabels': [[], [], [], []],
- 'dimLabelsSize': array([0, 0, 0, 0]),
- 'formula': '',
- 'formulaSize': 0,
- 'note': '',
- 'noteSize': 0,
- 'optionsSize1': 0,
- 'optionsSize2': 0,
- 'sIndicesSize': 0,
- 'version': 5,
- 'wfmSize': 320}
-{'aModified': 3,
- 'bname': array(['z', 'e', 'r', 'o', 'W', 'a', 'v', 'e', '', '', '', '', '', '', '',
-       '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''],
-      dtype='|S1'),
- 'botFullScale': 0.0,
- 'creationDate': 3001573964,
- 'dFolder': 69554896,
- 'dLock': 0,
- 'dataEUnits': 0,
- 'dataUnits': array(['', '', '', ''],
-      dtype='|S1'),
- 'depID': 29,
- 'dimEUnits': array([0, 0, 0, 0]),
- 'dimLabels': array([0, 0, 0, 0]),
- 'dimUnits': array([['', '', '', ''],
+{'version': 5,
+ 'wave': {'bin_header': {'checksum': -15649,
+                         'dataEUnitsSize': 0,
+                         'dimEUnitsSize': array([0, 0, 0, 0]),
+                         'dimLabelsSize': array([0, 0, 0, 0]),
+                         'formulaSize': 0,
+                         'noteSize': 0,
+                         'optionsSize1': 0,
+                         'optionsSize2': 0,
+                         'sIndicesSize': 0,
+                         'wfmSize': 320},
+          'data_units': '',
+          'dimension_units': '',
+          'formula': '',
+          'labels': [[], [], [], []],
+          'note': '',
+          'sIndices': array([], dtype=float64),
+          'wData': array([], dtype=float32),
+          'wave_header': {'aModified': 3,
+                          'bname': 'zeroWave',
+                          'botFullScale': 0.0,
+                          'creationDate': 3001573964,
+                          'dFolder': 69554896,
+                          'dLock': 0,
+                          'dataEUnits': 0,
+                          'dataUnits': array(['', '', '', ''],
+      dtype='|S1'),
+                          'depID': 29,
+                          'dimEUnits': array([0, 0, 0, 0]),
+                          'dimLabels': array([0, 0, 0, 0]),
+                          'dimUnits': array([['', '', '', ''],
        ['', '', '', ''],
        ['', '', '', ''],
        ['', '', '', '']],
       dtype='|S1'),
- 'fileName': 0,
- 'formula': 0,
- 'fsValid': 0,
- 'kindBits': '\x00',
- 'modDate': 3001573964,
- 'nDim': array([0, 0, 0, 0]),
- 'next': 0,
- 'npnts': 0,
- 'sIndices': 0,
- 'sfA': array([ 1.,  1.,  1.,  1.]),
- 'sfB': array([ 0.,  0.,  0.,  0.]),
- 'srcFldr': 0,
- 'swModified': 1,
- 'topFullScale': 0.0,
- 'type': 2,
- 'useBits': '\x00',
- 'wData': 0.0,
- 'wModified': 1,
- 'waveNoteH': 0,
- 'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
- 'whVersion': 1,
- 'whpad1': array(['', '', '', '', '', ''],
-      dtype='|S1'),
- 'whpad2': 0,
- 'whpad3': 0,
- 'whpad4': 0}
+                          'fileName': 0,
+                          'formula': 0,
+                          'fsValid': 0,
+                          'kindBits': '\x00',
+                          'modDate': 3001573964,
+                          'nDim': array([0, 0, 0, 0]),
+                          'next': 0,
+                          'npnts': 0,
+                          'sIndices': 0,
+                          'sfA': array([ 1.,  1.,  1.,  1.]),
+                          'sfB': array([ 0.,  0.,  0.,  0.]),
+                          'srcFldr': 0,
+                          'swModified': 1,
+                          'topFullScale': 0.0,
+                          'type': 2,
+                          'useBits': '\x00',
+                          'wModified': 1,
+                          'waveNoteH': 0,
+                          'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
+                          'whVersion': 1,
+                          'whpad1': array(['', '', '', '', '', ''],
+      dtype='|S1'),
+                          'whpad2': 0,
+                          'whpad3': 0,
+                          'whpad4': 0}}}
 
 >>> dumpibw('win-double.ibw')  # doctest: +REPORT_UDIFF
-array([ 5.,  4.,  3.,  2.,  1.])
-{'checksum': 28962,
- 'note': '',
- 'noteSize': 0,
- 'pictSize': 0,
- 'version': 2,
- 'wfmSize': 166}
-{'aModified': 0,
- 'bname': array(['d', 'o', 'u', 'b', 'l', 'e', '', '', '', '', '', '', '', '', '',
-       '', '', '', '', ''],
-      dtype='|S1'),
- 'botFullScale': 0.0,
- 'creationDate': 3001587842,
- 'dataUnits': array(['', '', '', ''],
-      dtype='|S1'),
- 'depID': 0,
- 'fileName': 0,
- 'formula': 0,
- 'fsValid': 0,
- 'hsA': 1.0,
- 'hsB': 0.0,
- 'kindBits': '\x00',
- 'modDate': 3001587842,
- 'next': 0,
- 'npnts': 5,
- 'srcFldr': 0,
- 'swModified': 0,
- 'topFullScale': 0.0,
- 'type': 4,
- 'useBits': '\x00',
- 'wData': array([ 0.    ,  2.3125,  0.    ,  2.25  ]),
- 'wModified': 0,
- 'wUnused': array(['', ''],
-      dtype='|S1'),
- 'waveNoteH': 0,
- 'whVersion': 0,
- 'xUnits': array(['', '', '', ''],
-      dtype='|S1')}
+{'version': 2,
+ 'wave': {'bin_header': {'checksum': 28962,
+                         'noteSize': 0,
+                         'pictSize': 0,
+                         'wfmSize': 166},
+          'note': '',
+          'padding': array([], dtype=float64),
+          'wData': array([ 5.,  4.,  3.,  2.,  1.]),
+          'wave_header': {'aModified': 0,
+                          'bname': 'double',
+                          'botFullScale': 0.0,
+                          'creationDate': 3001587842,
+                          'dataUnits': array(['', '', '', ''],
+      dtype='|S1'),
+                          'depID': 0,
+                          'fileName': 0,
+                          'formula': 0,
+                          'fsValid': 0,
+                          'hsA': 1.0,
+                          'hsB': 0.0,
+                          'kindBits': '\x00',
+                          'modDate': 3001587842,
+                          'next': 0,
+                          'npnts': 5,
+                          'srcFldr': 0,
+                          'swModified': 0,
+                          'topFullScale': 0.0,
+                          'type': 4,
+                          'useBits': '\x00',
+                          'wModified': 0,
+                          'wUnused': array(['', ''],
+      dtype='|S1'),
+                          'waveNoteH': 0,
+                          'whVersion': 0,
+                          'xUnits': array(['', '', '', ''],
+      dtype='|S1')}}}
 
 >>> dumpibw('win-textWave.ibw')  # doctest: +REPORT_UDIFF
-array(['Mary', 'had', 'a', 'little', 'lamb'],
-      dtype='|S6')
-{'checksum': 184,
- 'dataEUnits': '',
- 'dataEUnitsSize': 0,
- 'dimEUnits': ['', '', '', ''],
- 'dimEUnitsSize': array([0, 0, 0, 0]),
- 'dimLabels': [[], [], [], []],
- 'dimLabelsSize': array([0, 0, 0, 0]),
- 'formula': '',
- 'formulaSize': 0,
- 'note': '',
- 'noteSize': 0,
- 'optionsSize1': 0,
- 'optionsSize2': 0,
- 'sIndices': array([ 4,  7,  8, 14, 18], dtype=uint32),
- 'sIndicesSize': 20,
- 'version': 5,
- 'wfmSize': 338}
-{'aModified': 0,
- 'bname': array(['t', 'e', 'x', 't', '0', '', '', '', '', '', '', '', '', '', '', '',
-       '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''],
-      dtype='|S1'),
- 'botFullScale': 0.0,
- 'creationDate': 3001571199,
- 'dFolder': 8108612,
- 'dLock': 0,
- 'dataEUnits': 0,
- 'dataUnits': array(['', '', '', ''],
-      dtype='|S1'),
- 'depID': 32,
- 'dimEUnits': array([0, 0, 0, 0]),
- 'dimLabels': array([0, 0, 0, 0]),
- 'dimUnits': array([['', '', '', ''],
+{'version': 5,
+ 'wave': {'bin_header': {'checksum': 184,
+                         'dataEUnitsSize': 0,
+                         'dimEUnitsSize': array([0, 0, 0, 0]),
+                         'dimLabelsSize': array([0, 0, 0, 0]),
+                         'formulaSize': 0,
+                         'noteSize': 0,
+                         'optionsSize1': 0,
+                         'optionsSize2': 0,
+                         'sIndicesSize': 20,
+                         'wfmSize': 338},
+          'data_units': '',
+          'dimension_units': '',
+          'formula': '',
+          'labels': [[], [], [], []],
+          'note': '',
+          'sIndices': array([ 4,  7,  8, 14, 18]),
+          'wData': array(['Mary', 'had', 'a', 'little', 'lamb'],
+      dtype='|S6'),
+          'wave_header': {'aModified': 0,
+                          'bname': 'text0',
+                          'botFullScale': 0.0,
+                          'creationDate': 3001571199,
+                          'dFolder': 8108612,
+                          'dLock': 0,
+                          'dataEUnits': 0,
+                          'dataUnits': array(['', '', '', ''],
+      dtype='|S1'),
+                          'depID': 32,
+                          'dimEUnits': array([0, 0, 0, 0]),
+                          'dimLabels': array([0, 0, 0, 0]),
+                          'dimUnits': array([['', '', '', ''],
        ['', '', '', ''],
        ['', '', '', ''],
        ['', '', '', '']],
       dtype='|S1'),
- 'fileName': 7814472,
- 'formula': 0,
- 'fsValid': 0,
- 'kindBits': '\x00',
- 'modDate': 3001571215,
- 'nDim': array([5, 0, 0, 0]),
- 'next': 0,
- 'npnts': 5,
- 'sIndices': 8133100,
- 'sfA': array([ 1.,  1.,  1.,  1.]),
- 'sfB': array([ 0.,  0.,  0.,  0.]),
- 'srcFldr': -1007,
- 'swModified': 0,
- 'topFullScale': 0.0,
- 'type': 0,
- 'useBits': '\x00',
- 'wData': 7.865683337909351e+34,
- 'wModified': 1,
- 'waveNoteH': 0,
- 'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
- 'whVersion': 1,
- 'whpad1': array(['', '', '', '', '', ''],
-      dtype='|S1'),
- 'whpad2': 0,
- 'whpad3': 0,
- 'whpad4': 0}
+                          'fileName': 7814472,
+                          'formula': 0,
+                          'fsValid': 0,
+                          'kindBits': '\x00',
+                          'modDate': 3001571215,
+                          'nDim': array([5, 0, 0, 0]),
+                          'next': 0,
+                          'npnts': 5,
+                          'sIndices': 8133100,
+                          'sfA': array([ 1.,  1.,  1.,  1.]),
+                          'sfB': array([ 0.,  0.,  0.,  0.]),
+                          'srcFldr': -1007,
+                          'swModified': 0,
+                          'topFullScale': 0.0,
+                          'type': 0,
+                          'useBits': '\x00',
+                          'wModified': 1,
+                          'waveNoteH': 0,
+                          'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
+                          'whVersion': 1,
+                          'whpad1': array(['', '', '', '', '', ''],
+      dtype='|S1'),
+                          'whpad2': 0,
+                          'whpad3': 0,
+                          'whpad4': 0}}}
 
 >>> dumpibw('win-version2.ibw')  # doctest: +REPORT_UDIFF
-array([ 5.,  4.,  3.,  2.,  1.], dtype=float32)
-{'checksum': 1047,
- 'note': 'This is a test.',
- 'noteSize': 15,
- 'pictSize': 0,
- 'version': 2,
- 'wfmSize': 146}
-{'aModified': 0,
- 'bname': array(['v', 'e', 'r', 's', 'i', 'o', 'n', '2', '', '', '', '', '', '', '',
-       '', '', '', '', ''],
-      dtype='|S1'),
- 'botFullScale': 0.0,
- 'creationDate': 3001251979,
- 'dataUnits': array(['', '', '', ''],
-      dtype='|S1'),
- 'depID': 0,
- 'fileName': 0,
- 'formula': 0,
- 'fsValid': 0,
- 'hsA': 1.0,
- 'hsB': 0.0,
- 'kindBits': '\x00',
- 'modDate': 3001573594,
- 'next': 0,
- 'npnts': 5,
- 'srcFldr': 0,
- 'swModified': 0,
- 'topFullScale': 0.0,
- 'type': 2,
- 'useBits': '\x00',
- 'wData': array([ 5.,  4.,  3.,  2.]),
- 'wModified': 0,
- 'wUnused': array(['', ''],
-      dtype='|S1'),
- 'waveNoteH': 0,
- 'whVersion': 0,
- 'xUnits': array(['', '', '', ''],
-      dtype='|S1')}
+{'version': 2,
+ 'wave': {'bin_header': {'checksum': 1047,
+                         'noteSize': 15,
+                         'pictSize': 0,
+                         'wfmSize': 146},
+          'note': 'This is a test.',
+          'padding': array([], dtype=float64),
+          'wData': array([ 5.,  4.,  3.,  2.,  1.], dtype=float32),
+          'wave_header': {'aModified': 0,
+                          'bname': 'version2',
+                          'botFullScale': 0.0,
+                          'creationDate': 3001251979,
+                          'dataUnits': array(['', '', '', ''],
+      dtype='|S1'),
+                          'depID': 0,
+                          'fileName': 0,
+                          'formula': 0,
+                          'fsValid': 0,
+                          'hsA': 1.0,
+                          'hsB': 0.0,
+                          'kindBits': '\x00',
+                          'modDate': 3001573594,
+                          'next': 0,
+                          'npnts': 5,
+                          'srcFldr': 0,
+                          'swModified': 0,
+                          'topFullScale': 0.0,
+                          'type': 2,
+                          'useBits': '\x00',
+                          'wModified': 0,
+                          'wUnused': array(['', ''],
+      dtype='|S1'),
+                          'waveNoteH': 0,
+                          'whVersion': 0,
+                          'xUnits': array(['', '', '', ''],
+      dtype='|S1')}}}
 
 >>> dumpibw('win-version5.ibw')  # doctest: +REPORT_UDIFF
-array([ 5.,  4.,  3.,  2.,  1.], dtype=float32)
-{'checksum': 13214,
- 'dataEUnits': '',
- 'dataEUnitsSize': 0,
- 'dimEUnits': ['', '', '', ''],
- 'dimEUnitsSize': array([0, 0, 0, 0]),
- 'dimLabels': [['Column0'], [], [], []],
- 'dimLabelsSize': array([64,  0,  0,  0]),
- 'formula': '',
- 'formulaSize': 0,
- 'note': 'This is a test.',
- 'noteSize': 15,
- 'optionsSize1': 0,
- 'optionsSize2': 0,
- 'sIndicesSize': 0,
- 'version': 5,
- 'wfmSize': 340}
-{'aModified': 0,
- 'bname': array(['v', 'e', 'r', 's', 'i', 'o', 'n', '5', '', '', '', '', '', '', '',
-       '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''],
-      dtype='|S1'),
- 'botFullScale': 0.0,
- 'creationDate': 3001252180,
- 'dFolder': 8108612,
- 'dLock': 0,
- 'dataEUnits': 0,
- 'dataUnits': array(['', '', '', ''],
-      dtype='|S1'),
- 'depID': 30,
- 'dimEUnits': array([0, 0, 0, 0]),
- 'dimLabels': array([8138784,       0,       0,       0]),
- 'dimUnits': array([['', '', '', ''],
+{'version': 5,
+ 'wave': {'bin_header': {'checksum': 13214,
+                         'dataEUnitsSize': 0,
+                         'dimEUnitsSize': array([0, 0, 0, 0]),
+                         'dimLabelsSize': array([64,  0,  0,  0]),
+                         'formulaSize': 0,
+                         'noteSize': 15,
+                         'optionsSize1': 0,
+                         'optionsSize2': 0,
+                         'sIndicesSize': 0,
+                         'wfmSize': 340},
+          'data_units': '',
+          'dimension_units': '',
+          'formula': '',
+          'labels': [['Column0'], [], [], []],
+          'note': 'This is a test.',
+          'sIndices': array([], dtype=float64),
+          'wData': array([ 5.,  4.,  3.,  2.,  1.], dtype=float32),
+          'wave_header': {'aModified': 0,
+                          'bname': 'version5',
+                          'botFullScale': 0.0,
+                          'creationDate': 3001252180,
+                          'dFolder': 8108612,
+                          'dLock': 0,
+                          'dataEUnits': 0,
+                          'dataUnits': array(['', '', '', ''],
+      dtype='|S1'),
+                          'depID': 30,
+                          'dimEUnits': array([0, 0, 0, 0]),
+                          'dimLabels': array([8138784,       0,       0,       0]),
+                          'dimUnits': array([['', '', '', ''],
        ['', '', '', ''],
        ['', '', '', ''],
        ['', '', '', '']],
       dtype='|S1'),
- 'fileName': 8131824,
- 'formula': 0,
- 'fsValid': 0,
- 'kindBits': '\x00',
- 'modDate': 3001573601,
- 'nDim': array([5, 0, 0, 0]),
- 'next': 8125236,
- 'npnts': 5,
- 'sIndices': 0,
- 'sfA': array([ 1.,  1.,  1.,  1.]),
- 'sfB': array([ 0.,  0.,  0.,  0.]),
- 'srcFldr': -1007,
- 'swModified': 0,
- 'topFullScale': 0.0,
- 'type': 2,
- 'useBits': '\x00',
- 'wData': 5.0,
- 'wModified': 1,
- 'waveNoteH': 8131596,
- 'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
- 'whVersion': 1,
- 'whpad1': array(['', '', '', '', '', ''],
-      dtype='|S1'),
- 'whpad2': 0,
- 'whpad3': 0,
- 'whpad4': 0}
+                          'fileName': 8131824,
+                          'formula': 0,
+                          'fsValid': 0,
+                          'kindBits': '\x00',
+                          'modDate': 3001573601,
+                          'nDim': array([5, 0, 0, 0]),
+                          'next': 8125236,
+                          'npnts': 5,
+                          'sIndices': 0,
+                          'sfA': array([ 1.,  1.,  1.,  1.]),
+                          'sfB': array([ 0.,  0.,  0.,  0.]),
+                          'srcFldr': -1007,
+                          'swModified': 0,
+                          'topFullScale': 0.0,
+                          'type': 2,
+                          'useBits': '\x00',
+                          'wModified': 1,
+                          'waveNoteH': 8131596,
+                          'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
+                          'whVersion': 1,
+                          'whpad1': array(['', '', '', '', '', ''],
+      dtype='|S1'),
+                          'whpad2': 0,
+                          'whpad3': 0,
+                          'whpad4': 0}}}
 
 >>> dumpibw('win-zeroPointWave.ibw')  # doctest: +REPORT_UDIFF
-array([], dtype=float32)
-{'checksum': 27541,
- 'dataEUnits': '',
- 'dataEUnitsSize': 0,
- 'dimEUnits': ['', '', '', ''],
- 'dimEUnitsSize': array([0, 0, 0, 0]),
- 'dimLabels': [[], [], [], []],
- 'dimLabelsSize': array([0, 0, 0, 0]),
- 'formula': '',
- 'formulaSize': 0,
- 'note': '',
- 'noteSize': 0,
- 'optionsSize1': 0,
- 'optionsSize2': 0,
- 'sIndicesSize': 0,
- 'version': 5,
- 'wfmSize': 320}
-{'aModified': 3,
- 'bname': array(['z', 'e', 'r', 'o', 'W', 'a', 'v', 'e', '', '', '', '', '', '', '',
-       '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''],
-      dtype='|S1'),
- 'botFullScale': 0.0,
- 'creationDate': 3001573964,
- 'dFolder': 8108612,
- 'dLock': 0,
- 'dataEUnits': 0,
- 'dataUnits': array(['', '', '', ''],
-      dtype='|S1'),
- 'depID': 31,
- 'dimEUnits': array([0, 0, 0, 0]),
- 'dimLabels': array([0, 0, 0, 0]),
- 'dimUnits': array([['', '', '', ''],
+{'version': 5,
+ 'wave': {'bin_header': {'checksum': 27541,
+                         'dataEUnitsSize': 0,
+                         'dimEUnitsSize': array([0, 0, 0, 0]),
+                         'dimLabelsSize': array([0, 0, 0, 0]),
+                         'formulaSize': 0,
+                         'noteSize': 0,
+                         'optionsSize1': 0,
+                         'optionsSize2': 0,
+                         'sIndicesSize': 0,
+                         'wfmSize': 320},
+          'data_units': '',
+          'dimension_units': '',
+          'formula': '',
+          'labels': [[], [], [], []],
+          'note': '',
+          'sIndices': array([], dtype=float64),
+          'wData': array([], dtype=float32),
+          'wave_header': {'aModified': 3,
+                          'bname': 'zeroWave',
+                          'botFullScale': 0.0,
+                          'creationDate': 3001573964,
+                          'dFolder': 8108612,
+                          'dLock': 0,
+                          'dataEUnits': 0,
+                          'dataUnits': array(['', '', '', ''],
+      dtype='|S1'),
+                          'depID': 31,
+                          'dimEUnits': array([0, 0, 0, 0]),
+                          'dimLabels': array([0, 0, 0, 0]),
+                          'dimUnits': array([['', '', '', ''],
        ['', '', '', ''],
        ['', '', '', ''],
        ['', '', '', '']],
       dtype='|S1'),
- 'fileName': 8125252,
- 'formula': 0,
- 'fsValid': 0,
- 'kindBits': '\x00',
- 'modDate': 3001573964,
- 'nDim': array([0, 0, 0, 0]),
- 'next': 8133140,
- 'npnts': 0,
- 'sIndices': 0,
- 'sfA': array([ 1.,  1.,  1.,  1.]),
- 'sfB': array([ 0.,  0.,  0.,  0.]),
- 'srcFldr': -1007,
- 'swModified': 0,
- 'topFullScale': 0.0,
- 'type': 2,
- 'useBits': '\x00',
- 'wData': 0.0,
- 'wModified': 1,
- 'waveNoteH': 0,
- 'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
- 'whVersion': 1,
- 'whpad1': array(['', '', '', '', '', ''],
-      dtype='|S1'),
- 'whpad2': 0,
- 'whpad3': 0,
- 'whpad4': 0}
+                          'fileName': 8125252,
+                          'formula': 0,
+                          'fsValid': 0,
+                          'kindBits': '\x00',
+                          'modDate': 3001573964,
+                          'nDim': array([0, 0, 0, 0]),
+                          'next': 8133140,
+                          'npnts': 0,
+                          'sIndices': 0,
+                          'sfA': array([ 1.,  1.,  1.,  1.]),
+                          'sfB': array([ 0.,  0.,  0.,  0.]),
+                          'srcFldr': -1007,
+                          'swModified': 0,
+                          'topFullScale': 0.0,
+                          'type': 2,
+                          'useBits': '\x00',
+                          'wModified': 1,
+                          'waveNoteH': 0,
+                          'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
+                          'whVersion': 1,
+                          'whpad1': array(['', '', '', '', '', ''],
+      dtype='|S1'),
+                          'whpad2': 0,
+                          'whpad3': 0,
+                          'whpad4': 0}}}
 
 >>> dumppxp('polar-graphs-demo.pxp')    # doctest: +REPORT_UDIFF, +ELLIPSIS
 record 0:
@@ -654,19 +630,44 @@ record 28:
 record 29:
 <UnknownRecord-26 ...>
 record 30:
-{'header': {'numSysVars': 21,
-            'numUserStrs': 0,
-            'numUserVars': 0,
-            'version': 1},
- 'sysVars': array([   0.,    0.,    0.,    0.,    0.,    0.,    0.,    0.,    0.,
-          0.,    0.,    0.,    0.,    0.,    0.,    0.,    0.,    0.,
-          0.,    0.,  128.]),
- 'userStrs': {},
- 'userVars': {}}
+{'variables': {'sysVars': {'K0': 0.0,
+                           'K1': 0.0,
+                           'K10': 0.0,
+                           'K11': 0.0,
+                           'K12': 0.0,
+                           'K13': 0.0,
+                           'K14': 0.0,
+                           'K15': 0.0,
+                           'K16': 0.0,
+                           'K17': 0.0,
+                           'K18': 0.0,
+                           'K19': 0.0,
+                           'K2': 0.0,
+                           'K20': 128.0,
+                           'K3': 0.0,
+                           'K4': 0.0,
+                           'K5': 0.0,
+                           'K6': 0.0,
+                           'K7': 0.0,
+                           'K8': 0.0,
+                           'K9': 0.0},
+               'userStrs': {},
+               'userVars': {},
+               'var_header': {'numSysVars': 21,
+                              'numUserStrs': 0,
+                              'numUserVars': 0}},
+ 'version': 1}
 record 31:
 '\x95 Polar Graphs Demo, v3.01\n\n'
 record 32:
-array([ 0.30000001,  0.5448544 ,  0.77480197,  0.97584349,  1.13573945,
+{'version': 2,
+ 'wave': {'bin_header': {'checksum': -25004,
+                         'noteSize': 0,
+                         'pictSize': 0,
+                         'wfmSize': 638},
+          'note': '',
+          'padding': array([], dtype=float64),
+          'wData': array([ 0.30000001,  0.5448544 ,  0.77480197,  0.97584349,  1.13573945,
         1.24475539,  1.2962544 ,  1.28710103,  1.21785283,  1.09272552,
         0.91933674,  0.7082426 ,  0.47229454,  0.22585714, -0.01606643,
        -0.23874778, -0.42862982, -0.57415301, -0.6664573 , -0.69992352,
@@ -691,46 +692,44 @@ array([ 0.30000001,  0.5448544 ,  0.77480197,  0.97584349,  1.13573945,
         1.17415261,  1.0286293 ,  0.83874667,  0.61606491,  0.37414294,
         0.12770344, -0.1082412 , -0.31933719, -0.49272597, -0.61785328,
        -0.6871013 , -0.69625437, -0.64475471, -0.53574032, -0.37584305,
-       -0.17479956,  0.05514668,  0.30000135], dtype=float32)
-{'checksum': -25004,
- 'note': '',
- 'noteSize': 0,
- 'pictSize': 0,
- 'version': 2,
- 'wfmSize': 638}
-{'aModified': 0,
- 'bname': array(['r', 'a', 'd', 'i', 'u', 's', 'D', 'a', 't', 'a', '', '', '', '',
-       '', '', '', '', '', ''],
-      dtype='|S1'),
- 'botFullScale': 0.0,
- 'creationDate': 0,
- 'dataUnits': array(['', '', '', ''],
-      dtype='|S1'),
- 'depID': 0,
- 'fileName': 0,
- 'formula': 0,
- 'fsValid': 0,
- 'hsA': 0.04908738521234052,
- 'hsB': 0.0,
- 'kindBits': '\x00',
- 'modDate': 2845545774,
- 'next': 0,
- 'npnts': 128,
- 'srcFldr': 0,
- 'swModified': 0,
- 'topFullScale': 0.0,
- 'type': 2,
- 'useBits': '\x00',
- 'wData': array([ 0.30000001,  0.5448544 ,  0.77480197,  0.97584349]),
- 'wModified': 0,
- 'wUnused': array(['', ''],
-      dtype='|S1'),
- 'waveNoteH': 0,
- 'whVersion': 0,
- 'xUnits': array(['', '', '', ''],
-      dtype='|S1')}
+       -0.17479956,  0.05514668,  0.30000135], dtype=float32),
+          'wave_header': {'aModified': 0,
+                          'bname': 'radiusData',
+                          'botFullScale': 0.0,
+                          'creationDate': 0,
+                          'dataUnits': array(['', '', '', ''],
+      dtype='|S1'),
+                          'depID': 0,
+                          'fileName': 0,
+                          'formula': 0,
+                          'fsValid': 0,
+                          'hsA': 0.04908738521234052,
+                          'hsB': 0.0,
+                          'kindBits': '\x00',
+                          'modDate': 2845545774,
+                          'next': 0,
+                          'npnts': 128,
+                          'srcFldr': 0,
+                          'swModified': 0,
+                          'topFullScale': 0.0,
+                          'type': 2,
+                          'useBits': '\x00',
+                          'wModified': 0,
+                          'wUnused': array(['', ''],
+      dtype='|S1'),
+                          'waveNoteH': 0,
+                          'whVersion': 0,
+                          'xUnits': array(['', '', '', ''],
+      dtype='|S1')}}}
 record 33:
-array([ 0.        ,  0.0494739 ,  0.0989478 ,  0.1484217 ,  0.1978956 ,
+{'version': 2,
+ 'wave': {'bin_header': {'checksum': 28621,
+                         'noteSize': 0,
+                         'pictSize': 0,
+                         'wfmSize': 638},
+          'note': '',
+          'padding': array([], dtype=float64),
+          'wData': array([ 0.        ,  0.0494739 ,  0.0989478 ,  0.1484217 ,  0.1978956 ,
         0.24736951,  0.29684341,  0.34631732,  0.3957912 ,  0.44526511,
         0.49473903,  0.54421294,  0.59368682,  0.6431607 ,  0.69263464,
         0.74210852,  0.79158241,  0.84105635,  0.89053023,  0.94000411,
@@ -755,46 +754,54 @@ array([ 0.        ,  0.0494739 ,  0.0989478 ,  0.1484217 ,  0.1978956 ,
         5.44212914,  5.4916029 ,  5.54107714,  5.5905509 ,  5.64002466,
         5.6894989 ,  5.73897219,  5.78844643,  5.83792019,  5.88739443,
         5.93686819,  5.98634195,  6.03581619,  6.08528948,  6.13476372,
-        6.18423796,  6.23371172,  6.28318548], dtype=float32)
-{'checksum': 28621,
- 'note': '',
- 'noteSize': 0,
- 'pictSize': 0,
- 'version': 2,
- 'wfmSize': 638}
-{'aModified': 0,
- 'bname': array(['a', 'n', 'g', 'l', 'e', 'D', 'a', 't', 'a', '', '', '', '', '', '',
-       '', '', '', '', ''],
-      dtype='|S1'),
- 'botFullScale': 0.0,
- 'creationDate': 0,
- 'dataUnits': array(['', '', '', ''],
-      dtype='|S1'),
- 'depID': 0,
- 'fileName': 0,
- 'formula': 0,
- 'fsValid': 0,
- 'hsA': 0.04908738521234052,
- 'hsB': 0.0,
- 'kindBits': '\x00',
- 'modDate': 2845470039,
- 'next': 0,
- 'npnts': 128,
- 'srcFldr': 0,
- 'swModified': 0,
- 'topFullScale': 0.0,
- 'type': 2,
- 'useBits': '\x00',
- 'wData': array([ 0.       ,  0.0494739,  0.0989478,  0.1484217]),
- 'wModified': 0,
- 'wUnused': array(['', ''],
-      dtype='|S1'),
- 'waveNoteH': 0,
- 'whVersion': 0,
- 'xUnits': array(['', '', '', ''],
-      dtype='|S1')}
+        6.18423796,  6.23371172,  6.28318548], dtype=float32),
+          'wave_header': {'aModified': 0,
+                          'bname': 'angleData',
+                          'botFullScale': 0.0,
+                          'creationDate': 0,
+                          'dataUnits': array(['', '', '', ''],
+      dtype='|S1'),
+                          'depID': 0,
+                          'fileName': 0,
+                          'formula': 0,
+                          'fsValid': 0,
+                          'hsA': 0.04908738521234052,
+                          'hsB': 0.0,
+                          'kindBits': '\x00',
+                          'modDate': 2845470039,
+                          'next': 0,
+                          'npnts': 128,
+                          'srcFldr': 0,
+                          'swModified': 0,
+                          'topFullScale': 0.0,
+                          'type': 2,
+                          'useBits': '\x00',
+                          'wModified': 0,
+                          'wUnused': array(['', ''],
+      dtype='|S1'),
+                          'waveNoteH': 0,
+                          'whVersion': 0,
+                          'xUnits': array(['', '', '', ''],
+      dtype='|S1')}}}
 record 34:
-array([  1.83690956e-17,   2.69450769e-02,   7.65399113e-02,
+{'version': 5,
+ 'wave': {'bin_header': {'checksum': 23021,
+                         'dataEUnitsSize': 0,
+                         'dimEUnitsSize': array([0, 0, 0, 0]),
+                         'dimLabelsSize': array([0, 0, 0, 0]),
+                         'formulaSize': 80,
+                         'noteSize': 0,
+                         'optionsSize1': 0,
+                         'optionsSize2': 0,
+                         'sIndicesSize': 0,
+                         'wfmSize': 832},
+          'data_units': '',
+          'dimension_units': '',
+          'formula': ' PolarRadiusFunction(radiusData,1,0) * cos(PolarAngleFunction(angleData,3,1,2))',
+          'labels': [[], [], [], []],
+          'note': '',
+          'sIndices': array([], dtype=float64),
+          'wData': array([  1.83690956e-17,   2.69450769e-02,   7.65399113e-02,
          1.44305170e-01,   2.23293692e-01,   3.04783821e-01,
          3.79158467e-01,   4.36888516e-01,   4.69528973e-01,
          4.70633775e-01,   4.36502904e-01,   3.66688997e-01,
@@ -836,70 +843,68 @@ array([  1.83690956e-17,   2.69450769e-02,   7.65399113e-02,
          1.51621893e-01,   2.12215677e-01,   2.38205954e-01,
          2.33226836e-01,   2.03656554e-01,   1.57870770e-01,
          1.05330117e-01,   5.55786416e-02,   1.72677450e-02,
-        -2.72719120e-03,   5.24539061e-08], dtype=float32)
-{'checksum': 23021,
- 'dataEUnits': '',
- 'dataEUnitsSize': 0,
- 'dimEUnits': ['', '', '', ''],
- 'dimEUnitsSize': array([0, 0, 0, 0]),
- 'dimLabels': [[], [], [], []],
- 'dimLabelsSize': array([0, 0, 0, 0]),
- 'formula': 'PolarRadiusFunction(radiusData,1,0) * cos(PolarAngleFunction(angleData,3,1,2))\x00',
- 'formulaSize': 80,
- 'note': '',
- 'noteSize': 0,
- 'optionsSize1': 0,
- 'optionsSize2': 0,
- 'sIndicesSize': 0,
- 'version': 5,
- 'wfmSize': 832}
-{'aModified': 0,
- 'bname': array(['W', '_', 'p', 'l', 'r', 'X', '5', '', '', '', '', '', '', '', '',
-       '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''],
-      dtype='|S1'),
- 'botFullScale': 0.0,
- 'creationDate': 0,
- 'dFolder': 7848580,
- 'dLock': 0,
- 'dataEUnits': 0,
- 'dataUnits': array(['', '', '', ''],
-      dtype='|S1'),
- 'depID': 24,
- 'dimEUnits': array([0, 0, 0, 0]),
- 'dimLabels': array([0, 0, 0, 0]),
- 'dimUnits': array([['', '', '', ''],
+        -2.72719120e-03,   5.24539061e-08], dtype=float32),
+          'wave_header': {'aModified': 0,
+                          'bname': 'W_plrX5',
+                          'botFullScale': 0.0,
+                          'creationDate': 0,
+                          'dFolder': 7848580,
+                          'dLock': 0,
+                          'dataEUnits': 0,
+                          'dataUnits': array(['', '', '', ''],
+      dtype='|S1'),
+                          'depID': 24,
+                          'dimEUnits': array([0, 0, 0, 0]),
+                          'dimLabels': array([0, 0, 0, 0]),
+                          'dimUnits': array([['', '', '', ''],
        ['', '', '', ''],
        ['', '', '', ''],
        ['', '', '', '']],
       dtype='|S1'),
- 'fileName': 0,
- 'formula': 8054500,
- 'fsValid': 0,
- 'kindBits': '\x00',
- 'modDate': 2985072242,
- 'nDim': array([128,   0,   0,   0]),
- 'next': 8054516,
- 'npnts': 128,
- 'sIndices': 0,
- 'sfA': array([ 0.04908739,  1.        ,  1.        ,  1.        ]),
- 'sfB': array([ 0.,  0.,  0.,  0.]),
- 'srcFldr': 0,
- 'swModified': 0,
- 'topFullScale': 0.0,
- 'type': 2,
- 'useBits': '\x00',
- 'wData': 1.8369095638207904e-17,
- 'wModified': 0,
- 'waveNoteH': 0,
- 'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
- 'whVersion': 1,
- 'whpad1': array(['', '', '', '', '', ''],
-      dtype='|S1'),
- 'whpad2': 0,
- 'whpad3': 0,
- 'whpad4': 0}
+                          'fileName': 0,
+                          'formula': 8054500,
+                          'fsValid': 0,
+                          'kindBits': '\x00',
+                          'modDate': 2985072242,
+                          'nDim': array([128,   0,   0,   0]),
+                          'next': 8054516,
+                          'npnts': 128,
+                          'sIndices': 0,
+                          'sfA': array([ 0.04908739,  1.        ,  1.        ,  1.        ]),
+                          'sfB': array([ 0.,  0.,  0.,  0.]),
+                          'srcFldr': 0,
+                          'swModified': 0,
+                          'topFullScale': 0.0,
+                          'type': 2,
+                          'useBits': '\x00',
+                          'wModified': 0,
+                          'waveNoteH': 0,
+                          'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
+                          'whVersion': 1,
+                          'whpad1': array(['', '', '', '', '', ''],
+      dtype='|S1'),
+                          'whpad2': 0,
+                          'whpad3': 0,
+                          'whpad4': 0}}}
 record 35:
-array([ 0.30000001,  0.54418772,  0.77101213,  0.96511477,  1.1135726 ,
+{'version': 5,
+ 'wave': {'bin_header': {'checksum': -9146,
+                         'dataEUnitsSize': 0,
+                         'dimEUnitsSize': array([0, 0, 0, 0]),
+                         'dimLabelsSize': array([0, 0, 0, 0]),
+                         'formulaSize': 80,
+                         'noteSize': 82,
+                         'optionsSize1': 0,
+                         'optionsSize2': 0,
+                         'sIndicesSize': 0,
+                         'wfmSize': 832},
+          'data_units': '',
+          'dimension_units': '',
+          'formula': ' PolarRadiusFunction(radiusData,1,0) * sin(PolarAngleFunction(angleData,3,1,2))',
+          'labels': [[], [], [], []],
+          'note': 'shadowX=W_plrX5,appendRadius=radiusData,appendAngleData=angleData,angleDataUnits=2',
+          'sIndices': array([], dtype=float64),
+          'wData': array([ 0.30000001,  0.54418772,  0.77101213,  0.96511477,  1.1135726 ,
         1.20686483,  1.23956215,  1.21068466,  1.12370288,  0.98618096,
         0.80910152,  0.60592639,  0.39147732,  0.18073183, -0.01236418,
        -0.17596789, -0.30120692, -0.38277394, -0.41920158, -0.41280419,
@@ -924,70 +929,58 @@ array([ 0.30000001,  0.54418772,  0.77101213,  0.96511477,  1.1135726 ,
         0.78277934,  0.72283876,  0.6181944 ,  0.47410288,  0.29939076,
         0.10585135, -0.09260413, -0.28104633, -0.44468346, -0.57008827,
        -0.64630753, -0.66580337, -0.62512833, -0.52528399, -0.37171093,
-       -0.17394456,  0.0550792 ,  0.30000135], dtype=float32)
-{'checksum': -9146,
- 'dataEUnits': '',
- 'dataEUnitsSize': 0,
- 'dimEUnits': ['', '', '', ''],
- 'dimEUnitsSize': array([0, 0, 0, 0]),
- 'dimLabels': [[], [], [], []],
- 'dimLabelsSize': array([0, 0, 0, 0]),
- 'formula': 'PolarRadiusFunction(radiusData,1,0) * sin(PolarAngleFunction(angleData,3,1,2))\x00',
- 'formulaSize': 80,
- 'note': 'shadowX=W_plrX5,appendRadius=radiusData,appendAngleData=angleData,angleDataUnits=2',
- 'noteSize': 82,
- 'optionsSize1': 0,
- 'optionsSize2': 0,
- 'sIndicesSize': 0,
- 'version': 5,
- 'wfmSize': 832}
-{'aModified': 0,
- 'bname': array(['W', '_', 'p', 'l', 'r', 'Y', '5', '', '', '', '', '', '', '', '',
-       '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''],
-      dtype='|S1'),
- 'botFullScale': 0.0,
- 'creationDate': 0,
- 'dFolder': 7848580,
- 'dLock': 0,
- 'dataEUnits': 0,
- 'dataUnits': array(['', '', '', ''],
-      dtype='|S1'),
- 'depID': 26,
- 'dimEUnits': array([0, 0, 0, 0]),
- 'dimLabels': array([0, 0, 0, 0]),
- 'dimUnits': array([['', '', '', ''],
+       -0.17394456,  0.0550792 ,  0.30000135], dtype=float32),
+          'wave_header': {'aModified': 0,
+                          'bname': 'W_plrY5',
+                          'botFullScale': 0.0,
+                          'creationDate': 0,
+                          'dFolder': 7848580,
+                          'dLock': 0,
+                          'dataEUnits': 0,
+                          'dataUnits': array(['', '', '', ''],
+      dtype='|S1'),
+                          'depID': 26,
+                          'dimEUnits': array([0, 0, 0, 0]),
+                          'dimLabels': array([0, 0, 0, 0]),
+                          'dimUnits': array([['', '', '', ''],
        ['', '', '', ''],
        ['', '', '', ''],
        ['', '', '', '']],
       dtype='|S1'),
- 'fileName': 0,
- 'formula': 8054532,
- 'fsValid': 0,
- 'kindBits': '\x00',
- 'modDate': 2985072242,
- 'nDim': array([128,   0,   0,   0]),
- 'next': 8084972,
- 'npnts': 128,
- 'sIndices': 0,
- 'sfA': array([ 0.04908739,  1.        ,  1.        ,  1.        ]),
- 'sfB': array([ 0.,  0.,  0.,  0.]),
- 'srcFldr': 0,
- 'swModified': 0,
- 'topFullScale': 0.0,
- 'type': 2,
- 'useBits': '\x00',
- 'wData': 0.30000001192092896,
- 'wModified': 0,
- 'waveNoteH': 7996608,
- 'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
- 'whVersion': 1,
- 'whpad1': array(['', '', '', '', '', ''],
-      dtype='|S1'),
- 'whpad2': 0,
- 'whpad3': 0,
- 'whpad4': 0}
+                          'fileName': 0,
+                          'formula': 8054532,
+                          'fsValid': 0,
+                          'kindBits': '\x00',
+                          'modDate': 2985072242,
+                          'nDim': array([128,   0,   0,   0]),
+                          'next': 8084972,
+                          'npnts': 128,
+                          'sIndices': 0,
+                          'sfA': array([ 0.04908739,  1.        ,  1.        ,  1.        ]),
+                          'sfB': array([ 0.,  0.,  0.,  0.]),
+                          'srcFldr': 0,
+                          'swModified': 0,
+                          'topFullScale': 0.0,
+                          'type': 2,
+                          'useBits': '\x00',
+                          'wModified': 0,
+                          'waveNoteH': 7996608,
+                          'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
+                          'whVersion': 1,
+                          'whpad1': array(['', '', '', '', '', ''],
+      dtype='|S1'),
+                          'whpad2': 0,
+                          'whpad3': 0,
+                          'whpad4': 0}}}
 record 36:
-array([ 0.2617994 ,  0.27842158,  0.29504377,  0.31166595,  0.32828814,
+{'version': 2,
+ 'wave': {'bin_header': {'checksum': 14307,
+                         'noteSize': 0,
+                         'pictSize': 0,
+                         'wfmSize': 382},
+          'note': '',
+          'padding': array([], dtype=float64),
+          'wData': array([ 0.2617994 ,  0.27842158,  0.29504377,  0.31166595,  0.32828814,
         0.34491032,  0.36153251,  0.3781547 ,  0.39477688,  0.41139907,
         0.42802125,  0.44464344,  0.46126559,  0.47788778,  0.49450997,
         0.51113212,  0.52775431,  0.54437649,  0.56099868,  0.57762086,
@@ -999,46 +992,44 @@ array([ 0.2617994 ,  0.27842158,  0.29504377,  0.31166595,  0.32828814,
         1.00979757,  1.02641988,  1.04304194,  1.05966425,  1.07628632,
         1.09290862,  1.10953069,  1.12615299,  1.14277506,  1.15939736,
         1.17601943,  1.19264174,  1.2092638 ,  1.22588611,  1.24250817,
-        1.25913048,  1.27575254,  1.29237485,  1.30899692], dtype=float32)
-{'checksum': 14307,
- 'note': '',
- 'noteSize': 0,
- 'pictSize': 0,
- 'version': 2,
- 'wfmSize': 382}
-{'aModified': 0,
- 'bname': array(['a', 'n', 'g', 'l', 'e', 'Q', '1', '', '', '', '', '', '', '', '',
-       '', '', '', '', ''],
-      dtype='|S1'),
- 'botFullScale': 0.0,
- 'creationDate': 0,
- 'dataUnits': array(['', '', '', ''],
-      dtype='|S1'),
- 'depID': 0,
- 'fileName': 0,
- 'formula': 0,
- 'fsValid': 0,
- 'hsA': 1.0,
- 'hsB': 0.0,
- 'kindBits': '\x00',
- 'modDate': 2845473705,
- 'next': 0,
- 'npnts': 64,
- 'srcFldr': 0,
- 'swModified': 0,
- 'topFullScale': 0.0,
- 'type': 2,
- 'useBits': '\x00',
- 'wData': array([ 0.2617994 ,  0.27842158,  0.29504377,  0.31166595]),
- 'wModified': 0,
- 'wUnused': array(['', ''],
-      dtype='|S1'),
- 'waveNoteH': 0,
- 'whVersion': 0,
- 'xUnits': array(['', '', '', ''],
-      dtype='|S1')}
+        1.25913048,  1.27575254,  1.29237485,  1.30899692], dtype=float32),
+          'wave_header': {'aModified': 0,
+                          'bname': 'angleQ1',
+                          'botFullScale': 0.0,
+                          'creationDate': 0,
+                          'dataUnits': array(['', '', '', ''],
+      dtype='|S1'),
+                          'depID': 0,
+                          'fileName': 0,
+                          'formula': 0,
+                          'fsValid': 0,
+                          'hsA': 1.0,
+                          'hsB': 0.0,
+                          'kindBits': '\x00',
+                          'modDate': 2845473705,
+                          'next': 0,
+                          'npnts': 64,
+                          'srcFldr': 0,
+                          'swModified': 0,
+                          'topFullScale': 0.0,
+                          'type': 2,
+                          'useBits': '\x00',
+                          'wModified': 0,
+                          'wUnused': array(['', ''],
+      dtype='|S1'),
+                          'waveNoteH': 0,
+                          'whVersion': 0,
+                          'xUnits': array(['', '', '', ''],
+      dtype='|S1')}}}
 record 37:
-array([ -8.34064484,  -7.66960144,  -6.62294245,  -6.82878971,
+{'version': 2,
+ 'wave': {'bin_header': {'checksum': -12080,
+                         'noteSize': 0,
+                         'pictSize': 0,
+                         'wfmSize': 382},
+          'note': '',
+          'padding': array([], dtype=float64),
+          'wData': array([ -8.34064484,  -7.66960144,  -6.62294245,  -6.82878971,
         -8.6383152 , -11.20019722, -13.83398628, -15.95139503,
        -16.18096733, -13.58062267,  -9.26843071,  -5.34649038,
         -3.01010084,  -2.30953455,  -2.73682952,  -3.72112942,
@@ -1053,46 +1044,54 @@ array([ -8.34064484,  -7.66960144,  -6.62294245,  -6.82878971,
         -4.54975414,  -4.52917624,  -3.99160147,  -3.1971693 ,
         -2.93472862,  -3.47230864,  -4.7322526 ,  -6.80173016,
         -9.08601665, -10.00928402,  -8.87677383,  -6.88120317,
-        -5.61007977,  -5.6351161 ,  -6.41880989,  -6.8738699 ], dtype=float32)
-{'checksum': -12080,
- 'note': '',
- 'noteSize': 0,
- 'pictSize': 0,
- 'version': 2,
- 'wfmSize': 382}
-{'aModified': 0,
- 'bname': array(['r', 'a', 'd', 'i', 'u', 's', 'Q', '1', '', '', '', '', '', '', '',
-       '', '', '', '', ''],
-      dtype='|S1'),
- 'botFullScale': 0.0,
- 'creationDate': 0,
- 'dataUnits': array(['', '', '', ''],
-      dtype='|S1'),
- 'depID': 0,
- 'fileName': 0,
- 'formula': 0,
- 'fsValid': 0,
- 'hsA': 1.0,
- 'hsB': 0.0,
- 'kindBits': '\x00',
- 'modDate': 2845473634,
- 'next': 0,
- 'npnts': 64,
- 'srcFldr': 0,
- 'swModified': 0,
- 'topFullScale': 0.0,
- 'type': 2,
- 'useBits': '\x00',
- 'wData': array([-8.34064484, -7.66960144, -6.62294245, -6.82878971]),
- 'wModified': 0,
- 'wUnused': array(['', ''],
-      dtype='|S1'),
- 'waveNoteH': 0,
- 'whVersion': 0,
- 'xUnits': array(['', '', '', ''],
-      dtype='|S1')}
+        -5.61007977,  -5.6351161 ,  -6.41880989,  -6.8738699 ], dtype=float32),
+          'wave_header': {'aModified': 0,
+                          'bname': 'radiusQ1',
+                          'botFullScale': 0.0,
+                          'creationDate': 0,
+                          'dataUnits': array(['', '', '', ''],
+      dtype='|S1'),
+                          'depID': 0,
+                          'fileName': 0,
+                          'formula': 0,
+                          'fsValid': 0,
+                          'hsA': 1.0,
+                          'hsB': 0.0,
+                          'kindBits': '\x00',
+                          'modDate': 2845473634,
+                          'next': 0,
+                          'npnts': 64,
+                          'srcFldr': 0,
+                          'swModified': 0,
+                          'topFullScale': 0.0,
+                          'type': 2,
+                          'useBits': '\x00',
+                          'wModified': 0,
+                          'wUnused': array(['', ''],
+      dtype='|S1'),
+                          'waveNoteH': 0,
+                          'whVersion': 0,
+                          'xUnits': array(['', '', '', ''],
+      dtype='|S1')}}}
 record 38:
-array([ 30.58058929,  31.08536911,  31.93481636,  31.57315445,
+{'version': 5,
+ 'wave': {'bin_header': {'checksum': -5745,
+                         'dataEUnitsSize': 0,
+                         'dimEUnitsSize': array([0, 0, 0, 0]),
+                         'dimLabelsSize': array([0, 0, 0, 0]),
+                         'formulaSize': 78,
+                         'noteSize': 0,
+                         'optionsSize1': 0,
+                         'optionsSize2': 0,
+                         'sIndicesSize': 0,
+                         'wfmSize': 576},
+          'data_units': '',
+          'dimension_units': '',
+          'formula': ' PolarRadiusFunction(radiusQ1,1,-40) * cos(PolarAngleFunction(angleQ1,2,2,2))',
+          'labels': [[], [], [], []],
+          'note': '',
+          'sIndices': array([], dtype=float64),
+          'wData': array([ 30.58058929,  31.08536911,  31.93481636,  31.57315445,
         29.68683434,  27.10366058,  24.47453499,  22.3495121 ,
         21.98692894,  24.21500397,  27.95923996,  31.28394508,
         33.12408066,  33.46794128,  32.79909515,  31.64211464,
@@ -1107,70 +1106,68 @@ array([ 30.58058929,  31.08536911,  31.93481636,  31.57315445,
         17.34101677,  16.83446693,  16.56042671,  16.38027191,
         15.94310474,  15.16159916,  14.10328865,  12.76812935,
         11.41363049,  10.60795975,  10.52314186,  10.67826462,
-        10.5454855 ,   9.99268055,   9.22939587,   8.5736742 ], dtype=float32)
-{'checksum': -5745,
- 'dataEUnits': '',
- 'dataEUnitsSize': 0,
- 'dimEUnits': ['', '', '', ''],
- 'dimEUnitsSize': array([0, 0, 0, 0]),
- 'dimLabels': [[], [], [], []],
- 'dimLabelsSize': array([0, 0, 0, 0]),
- 'formula': 'PolarRadiusFunction(radiusQ1,1,-40) * cos(PolarAngleFunction(angleQ1,2,2,2))\x00',
- 'formulaSize': 78,
- 'note': '',
- 'noteSize': 0,
- 'optionsSize1': 0,
- 'optionsSize2': 0,
- 'sIndicesSize': 0,
- 'version': 5,
- 'wfmSize': 576}
-{'aModified': 0,
- 'bname': array(['W', '_', 'p', 'l', 'r', 'X', '6', '', '', '', '', '', '', '', '',
-       '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''],
-      dtype='|S1'),
- 'botFullScale': 0.0,
- 'creationDate': 0,
- 'dFolder': 7848580,
- 'dLock': 0,
- 'dataEUnits': 0,
- 'dataUnits': array(['', '', '', ''],
-      dtype='|S1'),
- 'depID': 30,
- 'dimEUnits': array([0, 0, 0, 0]),
- 'dimLabels': array([0, 0, 0, 0]),
- 'dimUnits': array([['', '', '', ''],
+        10.5454855 ,   9.99268055,   9.22939587,   8.5736742 ], dtype=float32),
+          'wave_header': {'aModified': 0,
+                          'bname': 'W_plrX6',
+                          'botFullScale': 0.0,
+                          'creationDate': 0,
+                          'dFolder': 7848580,
+                          'dLock': 0,
+                          'dataEUnits': 0,
+                          'dataUnits': array(['', '', '', ''],
+      dtype='|S1'),
+                          'depID': 30,
+                          'dimEUnits': array([0, 0, 0, 0]),
+                          'dimLabels': array([0, 0, 0, 0]),
+                          'dimUnits': array([['', '', '', ''],
        ['', '', '', ''],
        ['', '', '', ''],
        ['', '', '', '']],
       dtype='|S1'),
- 'fileName': 0,
- 'formula': 8052116,
- 'fsValid': 0,
- 'kindBits': '\x00',
- 'modDate': 2985072242,
- 'nDim': array([64,  0,  0,  0]),
- 'next': 8324392,
- 'npnts': 64,
- 'sIndices': 0,
- 'sfA': array([ 1.,  1.,  1.,  1.]),
- 'sfB': array([ 0.,  0.,  0.,  0.]),
- 'srcFldr': 0,
- 'swModified': 0,
- 'topFullScale': 0.0,
- 'type': 2,
- 'useBits': '\x00',
- 'wData': 30.580589294433594,
- 'wModified': 0,
- 'waveNoteH': 0,
- 'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
- 'whVersion': 1,
- 'whpad1': array(['', '', '', '', '', ''],
-      dtype='|S1'),
- 'whpad2': 0,
- 'whpad3': 0,
- 'whpad4': 0}
+                          'fileName': 0,
+                          'formula': 8052116,
+                          'fsValid': 0,
+                          'kindBits': '\x00',
+                          'modDate': 2985072242,
+                          'nDim': array([64,  0,  0,  0]),
+                          'next': 8324392,
+                          'npnts': 64,
+                          'sIndices': 0,
+                          'sfA': array([ 1.,  1.,  1.,  1.]),
+                          'sfB': array([ 0.,  0.,  0.,  0.]),
+                          'srcFldr': 0,
+                          'swModified': 0,
+                          'topFullScale': 0.0,
+                          'type': 2,
+                          'useBits': '\x00',
+                          'wModified': 0,
+                          'waveNoteH': 0,
+                          'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
+                          'whVersion': 1,
+                          'whpad1': array(['', '', '', '', '', ''],
+      dtype='|S1'),
+                          'whpad2': 0,
+                          'whpad3': 0,
+                          'whpad4': 0}}}
 record 39:
-array([  8.19404411,   8.88563347,   9.70543861,  10.17177773,
+{'version': 5,
+ 'wave': {'bin_header': {'checksum': -16604,
+                         'dataEUnitsSize': 0,
+                         'dimEUnitsSize': array([0, 0, 0, 0]),
+                         'dimLabelsSize': array([0, 0, 0, 0]),
+                         'formulaSize': 78,
+                         'noteSize': 78,
+                         'optionsSize1': 0,
+                         'optionsSize2': 0,
+                         'sIndicesSize': 0,
+                         'wfmSize': 576},
+          'data_units': '',
+          'dimension_units': '',
+          'formula': ' PolarRadiusFunction(radiusQ1,1,-40) * sin(PolarAngleFunction(angleQ1,2,2,2))',
+          'labels': [[], [], [], []],
+          'note': 'shadowX=W_plrX6,appendRadius=radiusQ1,appendAngleData=angleQ1,angleDataUnits=2',
+          'sIndices': array([], dtype=float64),
+          'wData': array([  8.19404411,   8.88563347,   9.70543861,  10.17177773,
         10.11173058,   9.73756695,   9.25513077,   8.8788929 ,
          9.16085339,  10.56489944,  12.75579453,  14.90572262,
         16.46352959,  17.33401871,  17.68511391,  17.74635315,
@@ -1185,137 +1182,154 @@ array([  8.19404411,   8.88563347,   9.70543861,  10.17177773,
         30.91939545,  31.22146797,  31.97431755,  32.95656204,
         33.4611969 ,  33.23248672,  32.3250885 ,  30.64473915,
         28.72983551,  28.05199242,  29.29024887,  31.3501091 ,
-        32.7331543 ,  32.87995529,  32.28799438,  31.99738503], dtype=float32)
-{'checksum': -16604,
- 'dataEUnits': '',
- 'dataEUnitsSize': 0,
- 'dimEUnits': ['', '', '', ''],
- 'dimEUnitsSize': array([0, 0, 0, 0]),
- 'dimLabels': [[], [], [], []],
- 'dimLabelsSize': array([0, 0, 0, 0]),
- 'formula': 'PolarRadiusFunction(radiusQ1,1,-40) * sin(PolarAngleFunction(angleQ1,2,2,2))\x00',
- 'formulaSize': 78,
- 'note': 'shadowX=W_plrX6,appendRadius=radiusQ1,appendAngleData=angleQ1,angleDataUnits=2',
- 'noteSize': 78,
- 'optionsSize1': 0,
- 'optionsSize2': 0,
- 'sIndicesSize': 0,
- 'version': 5,
- 'wfmSize': 576}
-{'aModified': 0,
- 'bname': array(['W', '_', 'p', 'l', 'r', 'Y', '6', '', '', '', '', '', '', '', '',
-       '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''],
-      dtype='|S1'),
- 'botFullScale': 0.0,
- 'creationDate': 0,
- 'dFolder': 7848580,
- 'dLock': 0,
- 'dataEUnits': 0,
- 'dataUnits': array(['', '', '', ''],
-      dtype='|S1'),
- 'depID': 32,
- 'dimEUnits': array([0, 0, 0, 0]),
- 'dimLabels': array([0, 0, 0, 0]),
- 'dimUnits': array([['', '', '', ''],
+        32.7331543 ,  32.87995529,  32.28799438,  31.99738503], dtype=float32),
+          'wave_header': {'aModified': 0,
+                          'bname': 'W_plrY6',
+                          'botFullScale': 0.0,
+                          'creationDate': 0,
+                          'dFolder': 7848580,
+                          'dLock': 0,
+                          'dataEUnits': 0,
+                          'dataUnits': array(['', '', '', ''],
+      dtype='|S1'),
+                          'depID': 32,
+                          'dimEUnits': array([0, 0, 0, 0]),
+                          'dimLabels': array([0, 0, 0, 0]),
+                          'dimUnits': array([['', '', '', ''],
        ['', '', '', ''],
        ['', '', '', ''],
        ['', '', '', '']],
       dtype='|S1'),
- 'fileName': 0,
- 'formula': 7995612,
- 'fsValid': 0,
- 'kindBits': '\x00',
- 'modDate': 2985072242,
- 'nDim': array([64,  0,  0,  0]),
- 'next': 0,
- 'npnts': 64,
- 'sIndices': 0,
- 'sfA': array([ 1.,  1.,  1.,  1.]),
- 'sfB': array([ 0.,  0.,  0.,  0.]),
- 'srcFldr': 0,
- 'swModified': 0,
- 'topFullScale': 0.0,
- 'type': 2,
- 'useBits': '\x00',
- 'wData': 8.19404411315918,
- 'wModified': 0,
- 'waveNoteH': 7998208,
- 'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
- 'whVersion': 1,
- 'whpad1': array(['', '', '', '', '', ''],
-      dtype='|S1'),
- 'whpad2': 0,
- 'whpad3': 0,
- 'whpad4': 0}
+                          'fileName': 0,
+                          'formula': 7995612,
+                          'fsValid': 0,
+                          'kindBits': '\x00',
+                          'modDate': 2985072242,
+                          'nDim': array([64,  0,  0,  0]),
+                          'next': 0,
+                          'npnts': 64,
+                          'sIndices': 0,
+                          'sfA': array([ 1.,  1.,  1.,  1.]),
+                          'sfB': array([ 0.,  0.,  0.,  0.]),
+                          'srcFldr': 0,
+                          'swModified': 0,
+                          'topFullScale': 0.0,
+                          'type': 2,
+                          'useBits': '\x00',
+                          'wModified': 0,
+                          'waveNoteH': 7998208,
+                          'whUnused': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
+                          'whVersion': 1,
+                          'whpad1': array(['', '', '', '', '', ''],
+      dtype='|S1'),
+                          'whpad2': 0,
+                          'whpad3': 0,
+                          'whpad4': 0}}}
 record 40:
 'Packages'
 record 41:
 'WMDataBase'
 record 42:
-{'header': {'numSysVars': 21,
-            'numUserStrs': 6,
-            'numUserVars': 0,
-            'version': 1},
- 'sysVars': array([   0.,    0.,    0.,    0.,    0.,    0.,    0.,    0.,    0.,
-          0.,    0.,    0.,    0.,    0.,    0.,    0.,    0.,    0.,
-          0.,    0.,  128.]),
- 'userStrs': {'u_dataBase': ';PolarGraph0:,...,useCircles=2,maxArcLine=6;',
-              'u_dbBadStringChars': ',;=:',
-              'u_dbCurrBag': 'PolarGraph1',
-              'u_dbCurrContents': ',appendRadius=radiusQ1,...,useCircles=2,maxArcLine=6;',
-              'u_dbReplaceBadChars': '\xa9\xae\x99\x9f',
-              'u_str': '2'},
- 'userVars': {}}
+{'variables': {'sysVars': {'K0': 0.0,
+                           'K1': 0.0,
+                           'K10': 0.0,
+                           'K11': 0.0,
+                           'K12': 0.0,
+                           'K13': 0.0,
+                           'K14': 0.0,
+                           'K15': 0.0,
+                           'K16': 0.0,
+                           'K17': 0.0,
+                           'K18': 0.0,
+                           'K19': 0.0,
+                           'K2': 0.0,
+                           'K20': 128.0,
+                           'K3': 0.0,
+                           'K4': 0.0,
+                           'K5': 0.0,
+                           'K6': 0.0,
+                           'K7': 0.0,
+                           'K8': 0.0,
+                           'K9': 0.0},
+               'userStrs': {'u_dataBase': ';PolarGraph0:,...,useCircles=2,maxArcLine=6;',
+                            'u_dbBadStringChars': ',;=:',
+                            'u_dbCurrBag': 'PolarGraph1',
+                            'u_dbCurrContents': ',appendRadius=radiusQ1,...,useCircles=2,maxArcLine=6;',
+                            'u_dbReplaceBadChars': '\xa9\xae\x99\x9f',
+                            'u_str': '2'},
+               'userVars': {},
+               'var_header': {'numSysVars': 21,
+                              'numUserStrs': 6,
+                              'numUserVars': 0}},
+ 'version': 1}
 record 43:
 ''
 record 44:
 'PolarGraphs'
 record 45:
-{'header': {'numSysVars': 21,
-            'numUserStrs': 10,
-            'numUserVars': 28,
-            'version': 1},
- 'sysVars': array([   0.,    0.,    0.,    0.,    0.,    0.,    0.,    0.,    0.,
-          0.,    0.,    0.,    0.,    0.,    0.,    0.,    0.,    0.,
-          0.,    0.,  128.]),
- 'userStrs': {'u_colorList': 'black;blue;green;cyan;red;magenta;yellow;white;special',
-              'u_debugStr': 'Turn Debugging On',
-              'u_polAngleAxesWherePop': 'Off;Radius Start;Radius End;Radius Start and End;All Major Radii;At Listed Radii',
-              'u_polAngleUnitsPop': 'deg;rad',
-              'u_polLineStylePop': 'solid;dash 1;dash 2;dash 3;dash 4;dash 5;dash 6;dash 7;dash 8;dash 9;dash 10;dash 11;dash 12;dash 13;dash 14;dash 15;dash 16;dash 17;',
-              'u_polOffOn': 'Off;On',
-              'u_polRadAxesWherePop': '  Off;  Angle Start;  Angle Middle;  Angle End;  Angle Start and End;  0;  90;  180; -90;  0, 90;  90, 180; -180, -90; -90, 0;  0, 180;  90, -90;  0, 90, 180, -90;  All Major Angles;  At Listed Angles',
-              'u_polRotPop': ' -90;  0; +90; +180',
-              'u_popup': '',
-              'u_prompt': ''},
- 'userVars': {'V_bottom': 232.0,
-              'V_left': 1.0,
-              'V_max': 2.4158518093414401,
-              'V_min': -2.1848498883412,
-              'V_right': 232.0,
-              'V_top': 1.0,
-              'u_UniqWaveNdx': 8.0,
-              'u_UniqWinNdx': 3.0,
-              'u_angle0': 0.0,
-              'u_angleRange': 6.2831853071795862,
-              'u_debug': 0.0,
-              'u_majorDelta': 0.0,
-              'u_numPlaces': 0.0,
-              'u_polAngle0': 0.26179938779914941,
-              'u_polAngleRange': 1.0471975511965976,
-              'u_polInnerRadius': -20.0,
-              'u_polMajorAngleInc': 0.26179938779914941,
-              'u_polMajorRadiusInc': 10.0,
-              'u_polMinorAngleTicks': 3.0,
-              'u_polMinorRadiusTicks': 1.0,
-              'u_polOuterRadius': 0.0,
-              'u_segsPerMinorArc': 3.0,
-              'u_tickDelta': 0.0,
-              'u_var': 0.0,
-              'u_x1': 11.450159535018935,
-              'u_x2': 12.079591517721363,
-              'u_y1': 42.732577139459856,
-              'u_y2': 45.081649278814126}}
+{'variables': {'sysVars': {'K0': 0.0,
+                           'K1': 0.0,
+                           'K10': 0.0,
+                           'K11': 0.0,
+                           'K12': 0.0,
+                           'K13': 0.0,
+                           'K14': 0.0,
+                           'K15': 0.0,
+                           'K16': 0.0,
+                           'K17': 0.0,
+                           'K18': 0.0,
+                           'K19': 0.0,
+                           'K2': 0.0,
+                           'K20': 128.0,
+                           'K3': 0.0,
+                           'K4': 0.0,
+                           'K5': 0.0,
+                           'K6': 0.0,
+                           'K7': 0.0,
+                           'K8': 0.0,
+                           'K9': 0.0},
+               'userStrs': {'u_colorList': 'black;blue;green;cyan;red;magenta;yellow;white;special',
+                            'u_debugStr': 'Turn Debugging On',
+                            'u_polAngleAxesWherePop': 'Off;Radius Start;Radius End;Radius Start and End;All Major Radii;At Listed Radii',
+                            'u_polAngleUnitsPop': 'deg;rad',
+                            'u_polLineStylePop': 'solid;dash 1;dash 2;dash 3;dash 4;dash 5;dash 6;dash 7;dash 8;dash 9;dash 10;dash 11;dash 12;dash 13;dash 14;dash 15;dash 16;dash 17;',
+                            'u_polOffOn': 'Off;On',
+                            'u_polRadAxesWherePop': '  Off;  Angle Start;  Angle Middle;  Angle End;  Angle Start and End;  0;  90;  180; -90;  0, 90;  90, 180; -180, -90; -90, 0;  0, 180;  90, -90;  0, 90, 180, -90;  All Major Angles;  At Listed Angles',
+                            'u_polRotPop': ' -90;  0; +90; +180',
+                            'u_popup': '',
+                            'u_prompt': ''},
+               'userVars': {'V_bottom': 232.0,
+                            'V_left': 1.0,
+                            'V_max': 2.4158518093414401,
+                            'V_min': -2.1848498883412,
+                            'V_right': 232.0,
+                            'V_top': 1.0,
+                            'u_UniqWaveNdx': 8.0,
+                            'u_UniqWinNdx': 3.0,
+                            'u_angle0': 0.0,
+                            'u_angleRange': 6.2831853071795862,
+                            'u_debug': 0.0,
+                            'u_majorDelta': 0.0,
+                            'u_numPlaces': 0.0,
+                            'u_polAngle0': 0.26179938779914941,
+                            'u_polAngleRange': 1.0471975511965976,
+                            'u_polInnerRadius': -20.0,
+                            'u_polMajorAngleInc': 0.26179938779914941,
+                            'u_polMajorRadiusInc': 10.0,
+                            'u_polMinorAngleTicks': 3.0,
+                            'u_polMinorRadiusTicks': 1.0,
+                            'u_polOuterRadius': 0.0,
+                            'u_segsPerMinorArc': 3.0,
+                            'u_tickDelta': 0.0,
+                            'u_var': 0.0,
+                            'u_x1': 11.450159535018935,
+                            'u_x2': 12.079591517721363,
+                            'u_y1': 42.732577139459856,
+                            'u_y2': 45.081649278814126},
+               'var_header': {'numSysVars': 21,
+                              'numUserStrs': 10,
+                              'numUserVars': 28}},
+ 'version': 1}
 record 46:
 ''
 record 47:
@@ -1418,18 +1432,16 @@ from igor.record.wave import WaveRecord
 _this_dir = os.path.dirname(__file__)
 _data_dir = os.path.join(_this_dir, 'data')
 
-def dumpibw(filename, strict=True):
+def dumpibw(filename):
     sys.stderr.write('Testing {}\n'.format(filename))
     path = os.path.join(_data_dir, filename)
-    data,bin_info,wave_info = loadibw(path, strict=strict)
+    data = loadibw(path)
     pprint(data)
-    pprint(bin_info)
-    pprint(wave_info)
 
-def dumppxp(filename, strict=True):
+def dumppxp(filename):
     sys.stderr.write('Testing {}\n'.format(filename))
     path = os.path.join(_data_dir, filename)
-    records,filesystem = loadpxp(path, strict=strict)
+    records,filesystem = loadpxp(path)
     for i,record in enumerate(records):
         print('record {}:'.format(i))
         if isinstance(record, (FolderStartRecord, FolderEndRecord)):
@@ -1440,8 +1452,6 @@ def dumppxp(filename, strict=True):
             pprint(record.variables)
         elif isinstance(record, WaveRecord):
             pprint(record.wave)
-            pprint(record.bin_info)
-            pprint(record.wave_info)
         else:
             pprint(record)
     print('\nfilesystem:')