surfacetopography 0.91.2__py3.6-linux-x86_64.egg
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- EGG-INFO/PKG-INFO +11 -0
- EGG-INFO/SOURCES.txt +96 -0
- EGG-INFO/dependency_links.txt +1 -0
- EGG-INFO/native_libs.txt +1 -0
- EGG-INFO/not-zip-safe +1 -0
- EGG-INFO/requires.txt +6 -0
- EGG-INFO/top_level.txt +3 -0
- SurfaceTopography/ChangeLog.md +358 -0
- SurfaceTopography/Converters.py +197 -0
- SurfaceTopography/Generation.py +440 -0
- SurfaceTopography/HeightContainer.py +315 -0
- SurfaceTopography/IO/DI.py +261 -0
- SurfaceTopography/IO/FromFile.py +802 -0
- SurfaceTopography/IO/H5.py +79 -0
- SurfaceTopography/IO/IBW.py +176 -0
- SurfaceTopography/IO/MI.py +307 -0
- SurfaceTopography/IO/Matlab.py +106 -0
- SurfaceTopography/IO/NC.py +228 -0
- SurfaceTopography/IO/NPY.py +139 -0
- SurfaceTopography/IO/OPDx.py +885 -0
- SurfaceTopography/IO/Reader.py +389 -0
- SurfaceTopography/IO/__init__.py +241 -0
- SurfaceTopography/IO/__pycache__/DI.cpython-36.pyc +0 -0
- SurfaceTopography/IO/__pycache__/FromFile.cpython-36.pyc +0 -0
- SurfaceTopography/IO/__pycache__/H5.cpython-36.pyc +0 -0
- SurfaceTopography/IO/__pycache__/IBW.cpython-36.pyc +0 -0
- SurfaceTopography/IO/__pycache__/MI.cpython-36.pyc +0 -0
- SurfaceTopography/IO/__pycache__/Matlab.cpython-36.pyc +0 -0
- SurfaceTopography/IO/__pycache__/NC.cpython-36.pyc +0 -0
- SurfaceTopography/IO/__pycache__/NPY.cpython-36.pyc +0 -0
- SurfaceTopography/IO/__pycache__/OPDx.cpython-36.pyc +0 -0
- SurfaceTopography/IO/__pycache__/Reader.cpython-36.pyc +0 -0
- SurfaceTopography/IO/__pycache__/__init__.cpython-36.pyc +0 -0
- SurfaceTopography/Interpolation.py +30 -0
- SurfaceTopography/Nonuniform/Autocorrelation.py +161 -0
- SurfaceTopography/Nonuniform/Detrending.py +87 -0
- SurfaceTopography/Nonuniform/PowerSpectrum.py +178 -0
- SurfaceTopography/Nonuniform/ScalarParameters.py +140 -0
- SurfaceTopography/Nonuniform/VariableBandwidth.py +155 -0
- SurfaceTopography/Nonuniform/__init__.py +28 -0
- SurfaceTopography/Nonuniform/__pycache__/Autocorrelation.cpython-36.pyc +0 -0
- SurfaceTopography/Nonuniform/__pycache__/Detrending.cpython-36.pyc +0 -0
- SurfaceTopography/Nonuniform/__pycache__/PowerSpectrum.cpython-36.pyc +0 -0
- SurfaceTopography/Nonuniform/__pycache__/ScalarParameters.cpython-36.pyc +0 -0
- SurfaceTopography/Nonuniform/__pycache__/VariableBandwidth.cpython-36.pyc +0 -0
- SurfaceTopography/Nonuniform/__pycache__/__init__.cpython-36.pyc +0 -0
- SurfaceTopography/Nonuniform/__pycache__/common.cpython-36.pyc +0 -0
- SurfaceTopography/Nonuniform/common.py +95 -0
- SurfaceTopography/NonuniformLineScan.py +310 -0
- SurfaceTopography/Special.py +225 -0
- SurfaceTopography/Tools/__init__.py +28 -0
- SurfaceTopography/Tools/__pycache__/__init__.cpython-36.pyc +0 -0
- SurfaceTopography/Tools/__pycache__/common.cpython-36.pyc +0 -0
- SurfaceTopography/Tools/common.py +75 -0
- SurfaceTopography/Uniform/Autocorrelation.py +211 -0
- SurfaceTopography/Uniform/Detrending.py +155 -0
- SurfaceTopography/Uniform/Filtering.py +226 -0
- SurfaceTopography/Uniform/Interpolation.py +159 -0
- SurfaceTopography/Uniform/PowerSpectrum.py +197 -0
- SurfaceTopography/Uniform/ScalarParameters.py +173 -0
- SurfaceTopography/Uniform/VariableBandwidth.py +169 -0
- SurfaceTopography/Uniform/__init__.py +28 -0
- SurfaceTopography/Uniform/__pycache__/Autocorrelation.cpython-36.pyc +0 -0
- SurfaceTopography/Uniform/__pycache__/Detrending.cpython-36.pyc +0 -0
- SurfaceTopography/Uniform/__pycache__/Filtering.cpython-36.pyc +0 -0
- SurfaceTopography/Uniform/__pycache__/Interpolation.cpython-36.pyc +0 -0
- SurfaceTopography/Uniform/__pycache__/PowerSpectrum.cpython-36.pyc +0 -0
- SurfaceTopography/Uniform/__pycache__/ScalarParameters.cpython-36.pyc +0 -0
- SurfaceTopography/Uniform/__pycache__/VariableBandwidth.cpython-36.pyc +0 -0
- SurfaceTopography/Uniform/__pycache__/__init__.cpython-36.pyc +0 -0
- SurfaceTopography/Uniform/__pycache__/common.cpython-36.pyc +0 -0
- SurfaceTopography/Uniform/common.py +260 -0
- SurfaceTopography/UniformLineScanAndTopography.py +887 -0
- SurfaceTopography/__init__.py +62 -0
- SurfaceTopography/__pycache__/Converters.cpython-36.pyc +0 -0
- SurfaceTopography/__pycache__/Generation.cpython-36.pyc +0 -0
- SurfaceTopography/__pycache__/HeightContainer.cpython-36.pyc +0 -0
- SurfaceTopography/__pycache__/Interpolation.cpython-36.pyc +0 -0
- SurfaceTopography/__pycache__/NonuniformLineScan.cpython-36.pyc +0 -0
- SurfaceTopography/__pycache__/Special.cpython-36.pyc +0 -0
- SurfaceTopography/__pycache__/UniformLineScanAndTopography.cpython-36.pyc +0 -0
- SurfaceTopography/__pycache__/__init__.cpython-36.pyc +0 -0
- SurfaceTopography/__pycache__/common.cpython-36.pyc +0 -0
- SurfaceTopography/common.py +111 -0
- _SurfaceTopography.cpython-36m-x86_64-linux-gnu.so +0 -0
- _SurfaceTopography.py +8 -0
- __pycache__/_SurfaceTopography.cpython-36.pyc +0 -0
- test/IO/__init__.py +28 -0
- test/IO/__pycache__/__init__.cpython-36.pyc +0 -0
- test/IO/__pycache__/test_ibw.cpython-36.pyc +0 -0
- test/IO/__pycache__/test_io.cpython-36.pyc +0 -0
- test/IO/__pycache__/test_mi.cpython-36.pyc +0 -0
- test/IO/__pycache__/test_netcdf.cpython-36.pyc +0 -0
- test/IO/__pycache__/test_npy.cpython-36.pyc +0 -0
- test/IO/__pycache__/test_opdx.cpython-36.pyc +0 -0
- test/IO/test_ibw.py +178 -0
- test/IO/test_io.py +401 -0
- test/IO/test_mi.py +103 -0
- test/IO/test_netcdf.py +134 -0
- test/IO/test_npy.py +135 -0
- test/IO/test_opdx.py +441 -0
- test/__init__.py +26 -0
- test/__pycache__/__init__.cpython-36.pyc +0 -0
- test/__pycache__/compare-orientation-with-gwyddion.cpython-36.pyc +0 -0
- test/__pycache__/conftest.cpython-36.pyc +0 -0
- test/__pycache__/test_autocorrelation.cpython-36.pyc +0 -0
- test/__pycache__/test_bicubic.cpython-36.pyc +0 -0
- test/__pycache__/test_filtering.cpython-36.pyc +0 -0
- test/__pycache__/test_generation.cpython-36.pyc +0 -0
- test/__pycache__/test_import.cpython-36.pyc +0 -0
- test/__pycache__/test_make_sphere.cpython-36.pyc +0 -0
- test/__pycache__/test_power_spectrum.cpython-36.pyc +0 -0
- test/__pycache__/test_scalar_parameters.cpython-36.pyc +0 -0
- test/__pycache__/test_topography1.cpython-36.pyc +0 -0
- test/__pycache__/test_topography2.cpython-36.pyc +0 -0
- test/__pycache__/test_variable_bandwidth.cpython-36.pyc +0 -0
- test/compare-orientation-with-gwyddion.py +102 -0
- test/conftest.py +80 -0
- test/test_autocorrelation.py +311 -0
- test/test_bicubic.py +190 -0
- test/test_filtering.py +69 -0
- test/test_generation.py +259 -0
- test/test_import.py +54 -0
- test/test_make_sphere.py +108 -0
- test/test_power_spectrum.py +259 -0
- test/test_scalar_parameters.py +176 -0
- test/test_topography1.py +1287 -0
- test/test_topography2.py +328 -0
- test/test_variable_bandwidth.py +107 -0
|
@@ -0,0 +1,885 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2019-2020 Lars Pastewka
|
|
3
|
+
# 2019-2020 Michael Röttger
|
|
4
|
+
# 2019-2020 Antoine Sanner
|
|
5
|
+
# 2019-2020 Kai Haase
|
|
6
|
+
#
|
|
7
|
+
# ### MIT license
|
|
8
|
+
#
|
|
9
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
# in the Software without restriction, including without limitation the rights
|
|
12
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
# furnished to do so, subject to the following conditions:
|
|
15
|
+
#
|
|
16
|
+
# The above copyright notice and this permission notice shall be included in
|
|
17
|
+
# all copies or substantial portions of the Software.
|
|
18
|
+
#
|
|
19
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
# SOFTWARE.
|
|
26
|
+
#
|
|
27
|
+
|
|
28
|
+
from collections import OrderedDict
|
|
29
|
+
from io import TextIOBase
|
|
30
|
+
import numpy as np
|
|
31
|
+
|
|
32
|
+
from .. import Topography
|
|
33
|
+
from .Reader import ReaderBase, ChannelInfo
|
|
34
|
+
from .FromFile import get_unit_conversion_factor, mangle_height_unit
|
|
35
|
+
|
|
36
|
+
MAGIC = "VCA DATA\x01\x00\x00\x55"
|
|
37
|
+
MAGIC_SIZE = 12
|
|
38
|
+
|
|
39
|
+
DEKTAK_MATRIX = 0x00 # Too lazy to assign an actual type id?
|
|
40
|
+
DEKTAK_BOOLEAN = 0x01 # Takes value 0 and 1
|
|
41
|
+
DEKTAK_SINT32 = 0x06
|
|
42
|
+
DEKTAK_UINT32 = 0x07
|
|
43
|
+
DEKTAK_SINT64 = 0x0a
|
|
44
|
+
DEKTAK_UINT64 = 0x0b
|
|
45
|
+
DEKTAK_FLOAT = 0x0c # Single precision float
|
|
46
|
+
DEKTAK_DOUBLE = 0x0d # Double precision float
|
|
47
|
+
DEKTAK_TYPE_ID = 0x0e # Compound type holding some kind of type id
|
|
48
|
+
DEKTAK_STRING = 0x12 # Free-form string value
|
|
49
|
+
DEKTAK_QUANTITY = 0x13 # Value with units (compound type)
|
|
50
|
+
DEKTAK_TIME_STAMP = 0x15 # Datetime (string/9-byte binary)
|
|
51
|
+
DEKTAK_UNITS = 0x18 # Units (compound type)
|
|
52
|
+
DEKTAK_DOUBLE_ARRAY = 0x40 # Raw data array, in XML Base64-encoded
|
|
53
|
+
DEKTAK_STRING_LIST = 0x42 # List of Str
|
|
54
|
+
DEKTAK_RAW_DATA = 0x46 # Parent/wrapper tag of raw data
|
|
55
|
+
DEKTAK_RAW_DATA_2D = 0x47 # Parent/wrapper tag of raw data
|
|
56
|
+
# Base64-encoded positions, not sure how it differs from 64
|
|
57
|
+
DEKTAK_POS_RAW_DATA = 0x7c
|
|
58
|
+
DEKTAK_CONTAINER = 0x7d # General nested data structure
|
|
59
|
+
# Always the last item. Usually a couple of 0xff bytes inside.
|
|
60
|
+
DEKTAK_TERMINATOR = 0x7f
|
|
61
|
+
|
|
62
|
+
TIMESTAMP_SIZE = 9
|
|
63
|
+
UNIT_EXTRA = 12
|
|
64
|
+
DOUBLE_ARRAY_EXTRA = 5
|
|
65
|
+
|
|
66
|
+
MEAS_SETTINGS = "/MetaData/MeasurementSettings"
|
|
67
|
+
RAW_1D_DATA = "/1D_Data/Raw"
|
|
68
|
+
ANY_2D_DATA = "/2D_Data/"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class OPDxReader(ReaderBase):
|
|
72
|
+
_format = 'opdx'
|
|
73
|
+
_name = 'Dektak OPDx'
|
|
74
|
+
_description = '''
|
|
75
|
+
File format of the Bruker Dektak XT* series stylus profilometer.
|
|
76
|
+
'''
|
|
77
|
+
|
|
78
|
+
# Reads in the positions of all the data and metadata
|
|
79
|
+
def __init__(self, file_path):
|
|
80
|
+
|
|
81
|
+
# depending from where this function is called, file_path might already
|
|
82
|
+
# be a filestream
|
|
83
|
+
already_open = False
|
|
84
|
+
if not hasattr(file_path, 'read'):
|
|
85
|
+
f = open(file_path, "rb")
|
|
86
|
+
else:
|
|
87
|
+
already_open = True
|
|
88
|
+
if isinstance(file_path, TextIOBase):
|
|
89
|
+
# file was opened without the 'b' option, so read its buffer
|
|
90
|
+
# to get the binary data
|
|
91
|
+
f = file_path.buffer
|
|
92
|
+
else:
|
|
93
|
+
f = file_path
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
|
|
97
|
+
# read topography in file as hexadecimal
|
|
98
|
+
self.buffer = [chr(byte) for byte in f.read()]
|
|
99
|
+
|
|
100
|
+
# length of file
|
|
101
|
+
physical_sizes = len(self.buffer)
|
|
102
|
+
|
|
103
|
+
# check if correct header
|
|
104
|
+
if physical_sizes < MAGIC_SIZE or ''.join(
|
|
105
|
+
self.buffer[:MAGIC_SIZE]) != MAGIC:
|
|
106
|
+
raise ValueError('Invalid file format for Dektak OPDx.')
|
|
107
|
+
|
|
108
|
+
pos = MAGIC_SIZE
|
|
109
|
+
hash_table = OrderedDict()
|
|
110
|
+
while pos < physical_sizes:
|
|
111
|
+
buf, pos, hash_table, path = read_item(buf=self.buffer,
|
|
112
|
+
pos=pos,
|
|
113
|
+
hash_table=hash_table,
|
|
114
|
+
path="")
|
|
115
|
+
|
|
116
|
+
# Make a list of channels containing metadata about the
|
|
117
|
+
# topographies but not reading them in directly yet
|
|
118
|
+
all_channels_data = find_2d_data(hash_table, self.buffer)
|
|
119
|
+
|
|
120
|
+
# Reformat the resulting dicts
|
|
121
|
+
for channel_name in all_channels_data.keys():
|
|
122
|
+
all_channels_data[channel_name][
|
|
123
|
+
-1], default_channel_name = reformat_dict(
|
|
124
|
+
channel_name, all_channels_data[channel_name][-1])
|
|
125
|
+
|
|
126
|
+
self._default_channel_index = list(all_channels_data.keys()).index(
|
|
127
|
+
default_channel_name)
|
|
128
|
+
|
|
129
|
+
#
|
|
130
|
+
# Build channel info objects and additional metadata for extracting
|
|
131
|
+
# data
|
|
132
|
+
#
|
|
133
|
+
self._channels = []
|
|
134
|
+
self._channels_xres_yres_start_stop_q = []
|
|
135
|
+
for channel_index, channel_name in enumerate(all_channels_data):
|
|
136
|
+
|
|
137
|
+
channel_data = all_channels_data[channel_name]
|
|
138
|
+
*xres_yres_start_stop_q, metadata = channel_data
|
|
139
|
+
|
|
140
|
+
#
|
|
141
|
+
# find out physical sizes in a common unit
|
|
142
|
+
# without touching the meta data, scale heights accordingly
|
|
143
|
+
#
|
|
144
|
+
unit_x = mangle_height_unit(metadata['Width_unit'])
|
|
145
|
+
size_x = metadata['Width_value']
|
|
146
|
+
|
|
147
|
+
unit_y = mangle_height_unit(metadata['Height_unit'])
|
|
148
|
+
size_y = metadata['Height_value']
|
|
149
|
+
|
|
150
|
+
unit_z = mangle_height_unit(metadata['z_unit'])
|
|
151
|
+
|
|
152
|
+
# we want value in unit_x units
|
|
153
|
+
unit_factor_y = get_unit_conversion_factor(unit_y,
|
|
154
|
+
unit_x)
|
|
155
|
+
if unit_factor_y is None:
|
|
156
|
+
raise ValueError(
|
|
157
|
+
'Units for size in x ("{}") and y ("{}") direction '
|
|
158
|
+
'are incompatible.'.format(unit_x, unit_y))
|
|
159
|
+
size_y *= unit_factor_y
|
|
160
|
+
|
|
161
|
+
if unit_z is None:
|
|
162
|
+
# No unit given for heights. Since we can only return one
|
|
163
|
+
# common unit, no unit should be returned
|
|
164
|
+
try:
|
|
165
|
+
del metadata['unit']
|
|
166
|
+
except KeyError:
|
|
167
|
+
pass
|
|
168
|
+
else:
|
|
169
|
+
# we want value in unit_x units
|
|
170
|
+
unit_factor_z = get_unit_conversion_factor(unit_z,
|
|
171
|
+
unit_x)
|
|
172
|
+
if unit_factor_z is None:
|
|
173
|
+
raise ValueError(
|
|
174
|
+
'Units for width ("{}") and data units ("{}") '
|
|
175
|
+
'are incompatible.'.format(unit_x, unit_y))
|
|
176
|
+
|
|
177
|
+
# we have converted everything to this unit
|
|
178
|
+
metadata['unit'] = unit_x
|
|
179
|
+
|
|
180
|
+
ch_info = ChannelInfo(self, channel_index,
|
|
181
|
+
name=metadata['Name'], dim=2,
|
|
182
|
+
nb_grid_pts=(metadata['ImageWidth'],
|
|
183
|
+
metadata['ImageHeight']),
|
|
184
|
+
physical_sizes=(size_x, size_y),
|
|
185
|
+
info=metadata)
|
|
186
|
+
|
|
187
|
+
self._channels.append(ch_info)
|
|
188
|
+
self._channels_xres_yres_start_stop_q.append(
|
|
189
|
+
xres_yres_start_stop_q) # needed for building heights
|
|
190
|
+
|
|
191
|
+
finally:
|
|
192
|
+
if not already_open:
|
|
193
|
+
f.close()
|
|
194
|
+
|
|
195
|
+
def topography(self, channel_index=None, physical_sizes=None,
|
|
196
|
+
height_scale_factor=None, info={},
|
|
197
|
+
periodic=False, subdomain_locations=None,
|
|
198
|
+
nb_subdomain_grid_pts=None):
|
|
199
|
+
if channel_index is None:
|
|
200
|
+
channel_index = self._default_channel_index
|
|
201
|
+
|
|
202
|
+
if subdomain_locations is not None or \
|
|
203
|
+
nb_subdomain_grid_pts is not None:
|
|
204
|
+
raise RuntimeError('This reader does not support MPI '
|
|
205
|
+
'parallelization.')
|
|
206
|
+
|
|
207
|
+
channel_info = self._channels[channel_index]
|
|
208
|
+
res_x, res_y, start, end, q = self._channels_xres_yres_start_stop_q[
|
|
209
|
+
channel_index]
|
|
210
|
+
|
|
211
|
+
data = build_matrix(res_x, res_y, self.buffer[start:end], q).T
|
|
212
|
+
|
|
213
|
+
unit_z = channel_info.info['z_unit']
|
|
214
|
+
|
|
215
|
+
if 'unit' in channel_info.info:
|
|
216
|
+
common_unit = channel_info.info['unit']
|
|
217
|
+
|
|
218
|
+
if (common_unit is not None) and (unit_z != common_unit):
|
|
219
|
+
# There is a common unit, but the z-unit in the file differs
|
|
220
|
+
# from that common unit. So we need to scale the data
|
|
221
|
+
# accordingly, such that also the height are represented in
|
|
222
|
+
# common units.
|
|
223
|
+
unit_factor_z = get_unit_conversion_factor(unit_z, common_unit)
|
|
224
|
+
if unit_factor_z is None:
|
|
225
|
+
raise ValueError(
|
|
226
|
+
'Common unit ("{}") derived from lateral units '
|
|
227
|
+
'and data units ("{}") are incompatible.'.format(
|
|
228
|
+
common_unit, unit_z))
|
|
229
|
+
data *= unit_factor_z
|
|
230
|
+
|
|
231
|
+
physical_sizes = self._check_physical_sizes(
|
|
232
|
+
physical_sizes, channel_info.physical_sizes)
|
|
233
|
+
|
|
234
|
+
info = info.copy()
|
|
235
|
+
info.update(channel_info.info)
|
|
236
|
+
|
|
237
|
+
return Topography(heights=data, physical_sizes=physical_sizes,
|
|
238
|
+
info=info, periodic=periodic)
|
|
239
|
+
|
|
240
|
+
@property
|
|
241
|
+
def channels(self):
|
|
242
|
+
return self._channels
|
|
243
|
+
|
|
244
|
+
channels.__doc__ = ReaderBase.channels.__doc__
|
|
245
|
+
topography.__doc__ = ReaderBase.topography.__doc__
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def reformat_dict(name, metadata):
|
|
249
|
+
"""
|
|
250
|
+
Reformat the metadata dict from c convention to a more readable format and
|
|
251
|
+
remove artefacts. Also gets and returns the default channel.
|
|
252
|
+
:param name: The name of the current channel.
|
|
253
|
+
:param metadata: The metadata dict
|
|
254
|
+
:return:
|
|
255
|
+
new dict: The nicer dict
|
|
256
|
+
primary_channel_name: The name of the primary channel.
|
|
257
|
+
"""
|
|
258
|
+
new_dict = OrderedDict()
|
|
259
|
+
|
|
260
|
+
primary_channel_name = None
|
|
261
|
+
for key in metadata.keys():
|
|
262
|
+
|
|
263
|
+
if key == '::MetaData::PrimaryData2D':
|
|
264
|
+
primary_channel_name = metadata[key]
|
|
265
|
+
|
|
266
|
+
if key.startswith('::MetaData::'):
|
|
267
|
+
# These are in here for no reason and the value is None
|
|
268
|
+
if not key[12:].endswith('::'):
|
|
269
|
+
new_dict[key[12:].replace('::', '_').replace(' ', '_')] = \
|
|
270
|
+
metadata[key]
|
|
271
|
+
|
|
272
|
+
if key.startswith(str(name) + '::'):
|
|
273
|
+
name_len = 2 + len(name)
|
|
274
|
+
new_dict[key[name_len:].replace('::', '_').replace(' ', '_')] = \
|
|
275
|
+
metadata[key]
|
|
276
|
+
|
|
277
|
+
if '' in new_dict.keys():
|
|
278
|
+
new_dict.pop('')
|
|
279
|
+
|
|
280
|
+
if 'xres' in new_dict.keys():
|
|
281
|
+
new_dict.pop('xres')
|
|
282
|
+
|
|
283
|
+
if 'yres' in new_dict.keys():
|
|
284
|
+
new_dict.pop('yres')
|
|
285
|
+
|
|
286
|
+
# Correct weird characters in units
|
|
287
|
+
for unit_key in ['z_unit', 'Height_unit', 'Width_unit']:
|
|
288
|
+
if unit_key in new_dict.keys():
|
|
289
|
+
unit = new_dict.pop(unit_key)
|
|
290
|
+
if unit.startswith("Â"):
|
|
291
|
+
unit = unit[1:]
|
|
292
|
+
new_dict[unit_key] = unit
|
|
293
|
+
|
|
294
|
+
new_dict['Name'] = name
|
|
295
|
+
|
|
296
|
+
return new_dict, primary_channel_name
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
class DektakItemData:
|
|
300
|
+
def __init__(self):
|
|
301
|
+
self.b = None
|
|
302
|
+
self.ui = None
|
|
303
|
+
self.si = None
|
|
304
|
+
self.uq = None
|
|
305
|
+
self.sq = None
|
|
306
|
+
self.d = None
|
|
307
|
+
self.timestamp = []
|
|
308
|
+
self.buf = None
|
|
309
|
+
self.qun = None
|
|
310
|
+
self.rawpos1d = DektakRawPos1D()
|
|
311
|
+
self.rawpos2d = DektakRawPos2D()
|
|
312
|
+
self.matrix = DektakMatrix()
|
|
313
|
+
self.strlist = None
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
class DektakItem:
|
|
317
|
+
def __init__(self):
|
|
318
|
+
self.typename = None
|
|
319
|
+
self.typeid = None
|
|
320
|
+
self.data = DektakItemData()
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
class DektakRawPos1D:
|
|
324
|
+
def __init__(self):
|
|
325
|
+
self.unit = DektakQuantUnit()
|
|
326
|
+
self.divisor = None
|
|
327
|
+
self.count = None
|
|
328
|
+
self.buf = DektakBuf
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
class DektakRawPos2D:
|
|
332
|
+
def __init__(self):
|
|
333
|
+
self.unitx = DektakQuantUnit()
|
|
334
|
+
self.unity = DektakQuantUnit()
|
|
335
|
+
self.divisorx = None
|
|
336
|
+
self.divisory = None
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
class DektakQuantUnit:
|
|
340
|
+
def __init__(self):
|
|
341
|
+
self.name = None
|
|
342
|
+
self.symbol = None
|
|
343
|
+
self.value = None
|
|
344
|
+
self.extra = None
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
class DektakMatrix:
|
|
348
|
+
def __init__(self):
|
|
349
|
+
self.another_name = None
|
|
350
|
+
self.some_int = None
|
|
351
|
+
self.xres = None
|
|
352
|
+
self.yres = None
|
|
353
|
+
self.buf = DektakBuf()
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
class DektakBuf:
|
|
357
|
+
def __init__(self, position=None, length=None):
|
|
358
|
+
self.position = position
|
|
359
|
+
self.length = length
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def find_1d_data(hash_table, buf):
|
|
363
|
+
""" THIS HAS NOT BEEN TESTED DUE TO NO FILES WITH 1D DATA AVAILABLE."""
|
|
364
|
+
|
|
365
|
+
item = hash_table.pop("/MetaData/MeasurementSettings/SamplesToLog", None)
|
|
366
|
+
|
|
367
|
+
if item is None:
|
|
368
|
+
return None
|
|
369
|
+
else:
|
|
370
|
+
raise NotImplementedError
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def find_2d_data(hash_table, buf):
|
|
374
|
+
""" Get all the 2d data channels out of the previously filled hash table.
|
|
375
|
+
|
|
376
|
+
:param hash_table: The filled hash table
|
|
377
|
+
:param buf: The raw hex data
|
|
378
|
+
:return: Dictionary with all names, data and metadata of the different
|
|
379
|
+
channels
|
|
380
|
+
"""
|
|
381
|
+
output = OrderedDict()
|
|
382
|
+
channels = []
|
|
383
|
+
|
|
384
|
+
# Get a list of all channels containing 2d data matrices
|
|
385
|
+
for key in hash_table.keys():
|
|
386
|
+
found = find_2d_data_matrix(key, hash_table[key])
|
|
387
|
+
if found is not None:
|
|
388
|
+
channels.append(found)
|
|
389
|
+
|
|
390
|
+
for channel in channels:
|
|
391
|
+
meta_data = create_meta(hash_table)
|
|
392
|
+
|
|
393
|
+
string = ANY_2D_DATA
|
|
394
|
+
string += channel
|
|
395
|
+
|
|
396
|
+
length = len(string)
|
|
397
|
+
|
|
398
|
+
# Get position and res of data matrix
|
|
399
|
+
string += "/Matrix"
|
|
400
|
+
item = hash_table[string]
|
|
401
|
+
start = item.data.matrix.buf.p
|
|
402
|
+
|
|
403
|
+
end = start + item.data.matrix.buf.length
|
|
404
|
+
xres = item.data.matrix.xres
|
|
405
|
+
yres = item.data.matrix.yres
|
|
406
|
+
|
|
407
|
+
meta_data[channel + "::xres"] = xres
|
|
408
|
+
meta_data[channel + "::yres"] = yres
|
|
409
|
+
|
|
410
|
+
# TODO: multiply value by interpret(item.data.qun.symbol)
|
|
411
|
+
string = string[:length]
|
|
412
|
+
string += "/Dimension1Extent"
|
|
413
|
+
item = hash_table[string]
|
|
414
|
+
yreal = item.data.qun.value
|
|
415
|
+
yunit = item.data.qun.symbol
|
|
416
|
+
|
|
417
|
+
meta_data[channel + "::Height value"] = yreal
|
|
418
|
+
meta_data[channel + "::Height unit"] = yunit
|
|
419
|
+
|
|
420
|
+
string = string[:length]
|
|
421
|
+
string += "/Dimension2Extent"
|
|
422
|
+
item = hash_table[string]
|
|
423
|
+
xreal = item.data.qun.value
|
|
424
|
+
xunit = item.data.qun.symbol
|
|
425
|
+
|
|
426
|
+
meta_data[channel + "::Width value"] = xreal
|
|
427
|
+
meta_data[channel + "::Width unit"] = xunit
|
|
428
|
+
|
|
429
|
+
string = string[:length]
|
|
430
|
+
string += "/DataScale"
|
|
431
|
+
item = hash_table[string]
|
|
432
|
+
q = item.data.qun.value
|
|
433
|
+
zunit = item.data.qun.symbol
|
|
434
|
+
|
|
435
|
+
meta_data[channel + "::z scale"] = q
|
|
436
|
+
meta_data[channel + "::z unit"] = zunit
|
|
437
|
+
|
|
438
|
+
output[channel] = [xres, yres, start, end, q, meta_data]
|
|
439
|
+
return output
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def find_2d_data_matrix(name, item):
|
|
443
|
+
""" Checks if an item is a matrix and if it is, returns it's channel name.
|
|
444
|
+
:param name: The name (key) of a found item
|
|
445
|
+
:param item: The item itself
|
|
446
|
+
:return: The name of the matrix data channel
|
|
447
|
+
"""
|
|
448
|
+
if item.typeid != DEKTAK_MATRIX:
|
|
449
|
+
return
|
|
450
|
+
if name[:9] != ANY_2D_DATA:
|
|
451
|
+
return
|
|
452
|
+
s = 9 + name[9:].find('/')
|
|
453
|
+
if s == -1:
|
|
454
|
+
return
|
|
455
|
+
if not name[s + 1:] == "Matrix":
|
|
456
|
+
return
|
|
457
|
+
return name[9:s]
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def create_meta(hash_table):
|
|
461
|
+
"""
|
|
462
|
+
Gets all the metadata out of a hash table.
|
|
463
|
+
:param hash_table: The hash table
|
|
464
|
+
:return: Hash table with all metadata names and values
|
|
465
|
+
"""
|
|
466
|
+
container = OrderedDict()
|
|
467
|
+
for key in hash_table.keys():
|
|
468
|
+
if not key.startswith('/MetaData/'):
|
|
469
|
+
continue
|
|
470
|
+
item = hash_table[key]
|
|
471
|
+
|
|
472
|
+
if item.typeid == DEKTAK_BOOLEAN:
|
|
473
|
+
metavalue = item.data.b
|
|
474
|
+
elif item.typeid == DEKTAK_SINT32:
|
|
475
|
+
metavalue = item.data.si
|
|
476
|
+
elif item.typeid == DEKTAK_UINT32:
|
|
477
|
+
metavalue = item.data.ui
|
|
478
|
+
elif item.typeid == DEKTAK_SINT64:
|
|
479
|
+
metavalue = item.data.sq
|
|
480
|
+
elif item.typeid == DEKTAK_UINT64:
|
|
481
|
+
metavalue = item.data.uq
|
|
482
|
+
elif item.typeid == DEKTAK_DOUBLE or item.typeid == DEKTAK_FLOAT:
|
|
483
|
+
metavalue = item.data.d
|
|
484
|
+
elif item.typeid == DEKTAK_STRING:
|
|
485
|
+
metavalue = "".join(item.data.buf)
|
|
486
|
+
elif item.typeid == DEKTAK_QUANTITY:
|
|
487
|
+
metavalue = str(item.data.qun.value) + item.data.qun.symbol
|
|
488
|
+
elif item.typeid == DEKTAK_STRING_LIST:
|
|
489
|
+
metavalue = "; ".join(item.data.strlist)
|
|
490
|
+
elif item.typeid == DEKTAK_TERMINATOR:
|
|
491
|
+
metavalue = None
|
|
492
|
+
else:
|
|
493
|
+
# Not really meta data
|
|
494
|
+
continue
|
|
495
|
+
metakey = key.replace("/", "::")
|
|
496
|
+
container[metakey] = metavalue
|
|
497
|
+
return container
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
def read_item(buf, pos, hash_table, path, abspos=0):
|
|
501
|
+
"""
|
|
502
|
+
Reads in the next item out of the buffer and saves it in the hash table.
|
|
503
|
+
May recursively call itself for containers.
|
|
504
|
+
:param buf: The raw data buffer
|
|
505
|
+
:param pos: Current position in the buffer
|
|
506
|
+
:param hash_table: The output hash table
|
|
507
|
+
:param path: Current name to save
|
|
508
|
+
:param abspos: Absolute position in buffer to keep track when calling
|
|
509
|
+
itself
|
|
510
|
+
:return:
|
|
511
|
+
Buffer, new position, hash table with new item in it, new path
|
|
512
|
+
"""
|
|
513
|
+
orig_path_len = len(path)
|
|
514
|
+
item = DektakItem()
|
|
515
|
+
itempos = 0
|
|
516
|
+
name, pos = read_name(buf, pos)
|
|
517
|
+
|
|
518
|
+
path += '/'
|
|
519
|
+
path += name
|
|
520
|
+
|
|
521
|
+
item.typeid, pos = read_with_check(buf, pos, 1)
|
|
522
|
+
item.typeid = ord(item.typeid[0])
|
|
523
|
+
|
|
524
|
+
# simple types
|
|
525
|
+
if item.typeid == DEKTAK_BOOLEAN:
|
|
526
|
+
b8, pos = read_with_check(buf, pos, 1)
|
|
527
|
+
if b8 == '\x01':
|
|
528
|
+
item.data.b = True
|
|
529
|
+
elif b8 == '\x00':
|
|
530
|
+
item.data.b = False
|
|
531
|
+
else:
|
|
532
|
+
raise ValueError("Something went wrong.")
|
|
533
|
+
|
|
534
|
+
elif item.typeid == DEKTAK_SINT32:
|
|
535
|
+
item.data.si, pos = read_int32(buf, pos, signed=True)
|
|
536
|
+
|
|
537
|
+
elif item.typeid == DEKTAK_UINT32:
|
|
538
|
+
item.data.ui, pos = read_int32(buf, pos, signed=False)
|
|
539
|
+
|
|
540
|
+
elif item.typeid == DEKTAK_SINT64:
|
|
541
|
+
item.data.sq, pos = read_int64(buf, pos, signed=True)
|
|
542
|
+
|
|
543
|
+
elif item.typeid == DEKTAK_UINT64:
|
|
544
|
+
item.data.uq, pos = read_int64(buf, pos, signed=False)
|
|
545
|
+
|
|
546
|
+
elif item.typeid == DEKTAK_FLOAT:
|
|
547
|
+
item.data.d, pos = read_float(buf, pos)
|
|
548
|
+
|
|
549
|
+
elif item.typeid == DEKTAK_DOUBLE:
|
|
550
|
+
item.data.d, pos = read_double(buf, pos)
|
|
551
|
+
|
|
552
|
+
elif item.typeid == DEKTAK_TIME_STAMP:
|
|
553
|
+
time, pos = read_with_check(buf, pos, TIMESTAMP_SIZE)
|
|
554
|
+
item.data.timestamp.append(time)
|
|
555
|
+
|
|
556
|
+
elif item.typeid == DEKTAK_STRING:
|
|
557
|
+
item.data.buf, _, pos = read_structured(buf, pos)
|
|
558
|
+
|
|
559
|
+
elif item.typeid == DEKTAK_QUANTITY:
|
|
560
|
+
content, _, pos = read_structured(buf, pos)
|
|
561
|
+
item.data.qun, itempos = read_quantunit_content(content, itempos,
|
|
562
|
+
False)
|
|
563
|
+
|
|
564
|
+
elif item.typeid == DEKTAK_UNITS:
|
|
565
|
+
content, _, pos = read_structured(buf, pos)
|
|
566
|
+
item.data.qun, itempos = read_quantunit_content(content, itempos, True)
|
|
567
|
+
|
|
568
|
+
elif item.typeid == DEKTAK_TERMINATOR:
|
|
569
|
+
pos = len(buf)
|
|
570
|
+
|
|
571
|
+
# Container types.
|
|
572
|
+
elif item.typeid == DEKTAK_CONTAINER or \
|
|
573
|
+
item.typeid == DEKTAK_RAW_DATA or \
|
|
574
|
+
item.typeid == DEKTAK_RAW_DATA_2D:
|
|
575
|
+
# TODO find out if maybe better place somewhere else
|
|
576
|
+
content, start, pos = read_structured(buf, pos)
|
|
577
|
+
abspos += start
|
|
578
|
+
while itempos < len(content):
|
|
579
|
+
content, itempos, hash_table, path = read_item(
|
|
580
|
+
buf=content, pos=itempos, hash_table=hash_table,
|
|
581
|
+
path=path, abspos=abspos)
|
|
582
|
+
|
|
583
|
+
# Types with string type name
|
|
584
|
+
elif item.typeid == DEKTAK_DOUBLE_ARRAY:
|
|
585
|
+
item.typename, item.data.buf, _, pos = read_named_struct(buf, pos)
|
|
586
|
+
|
|
587
|
+
elif item.typeid == DEKTAK_STRING_LIST:
|
|
588
|
+
item.typename, content, start, pos = read_named_struct(buf, pos)
|
|
589
|
+
item.data.strlist = []
|
|
590
|
+
while itempos < len(content):
|
|
591
|
+
s, itempos = read_name(content, itempos)
|
|
592
|
+
item.data.strlist.append(s)
|
|
593
|
+
|
|
594
|
+
elif item.typeid == DEKTAK_TYPE_ID:
|
|
595
|
+
item.typename, item.data.buf, _, pos = read_named_struct(buf, pos)
|
|
596
|
+
|
|
597
|
+
elif item.typeid == DEKTAK_POS_RAW_DATA:
|
|
598
|
+
if path.startswith('/2D_Data'):
|
|
599
|
+
item.typename, content, _, pos = read_named_struct(buf, pos)
|
|
600
|
+
|
|
601
|
+
item.data.rawpos2d.unitx, item.data.rawpos2d.divisorx, itempos = \
|
|
602
|
+
read_dimension2d_content(content, itempos,
|
|
603
|
+
item.data.rawpos2d.unitx)
|
|
604
|
+
item.data.rawpos2d.unity, item.data.rawpos2d.divisory, itempos = \
|
|
605
|
+
read_dimension2d_content(content, itempos,
|
|
606
|
+
item.data.rawpos2d.unity)
|
|
607
|
+
|
|
608
|
+
elif path.startswith('/1_Data'):
|
|
609
|
+
item.typename, content, _, pos = read_named_struct(buf, pos)
|
|
610
|
+
content.position += buf.position
|
|
611
|
+
|
|
612
|
+
item.data.rawpos1d.unit, itempos = read_quantunit_content(
|
|
613
|
+
content, itempos, True)
|
|
614
|
+
item.data.rawpos1d.count, itempos = read_int64(content, itempos)
|
|
615
|
+
|
|
616
|
+
item.data.rawpos1d.buf = content
|
|
617
|
+
item.data.rawpos1d.buf.position += itempos
|
|
618
|
+
item.data.rawpos1d.buf.length -= itempos
|
|
619
|
+
|
|
620
|
+
else:
|
|
621
|
+
# TODO check if should assume 1D here like gwyddion
|
|
622
|
+
raise ValueError
|
|
623
|
+
|
|
624
|
+
elif item.typeid == DEKTAK_MATRIX:
|
|
625
|
+
item.typename, pos = read_name(buf, pos)
|
|
626
|
+
item.data.matrix.some_int, pos = read_int32(buf, pos)
|
|
627
|
+
item.data.matrix.another_name, pos = read_name(buf, pos)
|
|
628
|
+
item.data.matrix.buf.length, pos = read_varlen(buf, pos)
|
|
629
|
+
item.data.matrix.yres, pos = read_int32(buf, pos)
|
|
630
|
+
item.data.matrix.xres, pos = read_int32(buf, pos)
|
|
631
|
+
|
|
632
|
+
if item.data.matrix.buf.length < 8: # 2 * sizeof int32
|
|
633
|
+
raise ValueError
|
|
634
|
+
item.data.matrix.buf.length -= 8
|
|
635
|
+
item.data.matrix.buf.p = pos + abspos
|
|
636
|
+
|
|
637
|
+
if len(buf) - pos < item.data.matrix.buf.length:
|
|
638
|
+
raise ValueError
|
|
639
|
+
pos += item.data.matrix.buf.length
|
|
640
|
+
|
|
641
|
+
else:
|
|
642
|
+
raise ValueError
|
|
643
|
+
hash_table[path] = item
|
|
644
|
+
path = path[:orig_path_len]
|
|
645
|
+
return buf, pos, hash_table, path
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
def read_quantunit_content(buf, pos, is_unit):
|
|
649
|
+
"""
|
|
650
|
+
Reads in a quantity unit: Value, name and symbol.
|
|
651
|
+
:param buf: The buffer
|
|
652
|
+
:param pos: The position in the buffer
|
|
653
|
+
:param is_unit: Whether or not it is a unit
|
|
654
|
+
:return: A quantunit item, filled with value, name and symbol
|
|
655
|
+
"""
|
|
656
|
+
quantunit = DektakQuantUnit()
|
|
657
|
+
quantunit.extra = []
|
|
658
|
+
|
|
659
|
+
if not is_unit:
|
|
660
|
+
quantunit.value, pos = read_double(buf, pos)
|
|
661
|
+
|
|
662
|
+
quantunit.name, pos = read_name(buf, pos)
|
|
663
|
+
quantunit.symbol, pos = read_name(buf, pos)
|
|
664
|
+
|
|
665
|
+
if is_unit:
|
|
666
|
+
quantunit.value, pos = read_double(buf, pos)
|
|
667
|
+
res, pos = read_with_check(buf, pos, UNIT_EXTRA)
|
|
668
|
+
quantunit.extra += res
|
|
669
|
+
|
|
670
|
+
return quantunit, pos
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
def read_dimension2d_content(buf, pos, unit):
|
|
674
|
+
"""
|
|
675
|
+
Reads in information about a 2d dimension.
|
|
676
|
+
:param buf: The buffer
|
|
677
|
+
:param pos: The position in the buffer
|
|
678
|
+
:param unit: The unit
|
|
679
|
+
:return: The open_topography unit, divisor and new position in the buffer
|
|
680
|
+
"""
|
|
681
|
+
unit.value, pos = read_double(buf, pos)
|
|
682
|
+
unit.name, pos = read_name(buf, pos)
|
|
683
|
+
unit.symbol, pos = read_name(buf, pos)
|
|
684
|
+
divisor, pos = read_double(buf, pos)
|
|
685
|
+
unit.extra, pos = read_with_check(buf, pos, UNIT_EXTRA)
|
|
686
|
+
return unit, divisor, pos
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
def build_matrix(xres, yres, data, q=1):
|
|
690
|
+
"""
|
|
691
|
+
Reads a float matrix of given dimensions and multiplies with a scale.
|
|
692
|
+
:param xres: Resolution along x-axis
|
|
693
|
+
:param yres: Resolution along y-axis
|
|
694
|
+
:param data: The raw hex data
|
|
695
|
+
:param q: The scale of the data, a double
|
|
696
|
+
:return: A numpy array, now doubles aswell
|
|
697
|
+
"""
|
|
698
|
+
data = ''.join(data)
|
|
699
|
+
|
|
700
|
+
# build correct type: 4byte flat, little endian
|
|
701
|
+
dt = np.dtype('f4') # double
|
|
702
|
+
dt = dt.newbyteorder('<') # little-endian
|
|
703
|
+
|
|
704
|
+
data = np.frombuffer(str.encode(data, "raw_unicode_escape"), dt)
|
|
705
|
+
data = data.copy().reshape((yres, xres))
|
|
706
|
+
data *= q
|
|
707
|
+
return data
|
|
708
|
+
|
|
709
|
+
|
|
710
|
+
def read_name(buf, pos):
|
|
711
|
+
"""
|
|
712
|
+
Reads a name.
|
|
713
|
+
:param buf: The buffer
|
|
714
|
+
:param pos: Position in buffer
|
|
715
|
+
:return:
|
|
716
|
+
name, new position in buffer
|
|
717
|
+
"""
|
|
718
|
+
|
|
719
|
+
# Names always have a physical_sizes of 4 bytes
|
|
720
|
+
length, pos = read_int32(buf, pos)
|
|
721
|
+
if len(buf) < length or pos > len(buf) - length:
|
|
722
|
+
raise ValueError("Some sizes went wrong.")
|
|
723
|
+
position = pos
|
|
724
|
+
|
|
725
|
+
name = buf[position:position + length]
|
|
726
|
+
name = "".join(s for s in name)
|
|
727
|
+
pos += length
|
|
728
|
+
return name, pos
|
|
729
|
+
|
|
730
|
+
|
|
731
|
+
def read_structured(buf, pos):
|
|
732
|
+
"""
|
|
733
|
+
Reads a length and returns a part of the buffer that long.
|
|
734
|
+
:param buf: The buffer
|
|
735
|
+
:param pos: Position in buffer
|
|
736
|
+
:return:
|
|
737
|
+
The slice of buffer, where it starts and the new position in the buffer
|
|
738
|
+
"""
|
|
739
|
+
length, pos = read_varlen(buf, pos)
|
|
740
|
+
if len(buf) < length or pos > len(buf) - length:
|
|
741
|
+
raise ValueError("Some sizes went wrong.")
|
|
742
|
+
start = pos
|
|
743
|
+
pos += length
|
|
744
|
+
return buf[start:start + length], start, pos
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
def read_named_struct(buf, pos):
|
|
748
|
+
"""
|
|
749
|
+
Same as read_structured but there is a name to it.
|
|
750
|
+
:param buf: The buffer
|
|
751
|
+
:param pos: Position in buffer
|
|
752
|
+
:return:
|
|
753
|
+
Name of the buffer, that buffer, its start and the new position in the
|
|
754
|
+
buffer
|
|
755
|
+
"""
|
|
756
|
+
typename, pos = read_name(buf, pos)
|
|
757
|
+
content, start, pos = read_structured(buf, pos)
|
|
758
|
+
return typename, content, start, pos
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
def read_varlen(buf, pos):
|
|
762
|
+
"""
|
|
763
|
+
Reads a length of variable length itself
|
|
764
|
+
:param buf: The buffer
|
|
765
|
+
:param pos: Position in the buffer
|
|
766
|
+
:return:
|
|
767
|
+
The open_topography length and new position in the buffer
|
|
768
|
+
"""
|
|
769
|
+
lenlen, pos = read_with_check(buf, pos, 1)
|
|
770
|
+
lenlen = np.frombuffer(str.encode(lenlen, "raw_unicode_escape"), "<u1")[0]
|
|
771
|
+
if lenlen == 1:
|
|
772
|
+
length, pos = read_with_check(buf, pos, 1)
|
|
773
|
+
length = \
|
|
774
|
+
np.frombuffer(str.encode(length, "raw_unicode_escape"), "<u1")[0]
|
|
775
|
+
elif lenlen == 2:
|
|
776
|
+
length, pos = read_int16(buf, pos)
|
|
777
|
+
elif lenlen == 4:
|
|
778
|
+
length, pos = read_int32(buf, pos)
|
|
779
|
+
else:
|
|
780
|
+
raise ValueError
|
|
781
|
+
return length, pos
|
|
782
|
+
|
|
783
|
+
|
|
784
|
+
def read_int64(buf, pos, signed=False):
|
|
785
|
+
"""
|
|
786
|
+
Reads a 64bit int.
|
|
787
|
+
:param buf: The buffer
|
|
788
|
+
:param pos: Position in the buffer
|
|
789
|
+
:param signed: Whether of not the int is signed
|
|
790
|
+
:return:
|
|
791
|
+
The int and the new position in the buffer
|
|
792
|
+
"""
|
|
793
|
+
out, pos = read_with_check(buf=buf, pos=pos, nbytes=8)
|
|
794
|
+
out = ''.join(out)
|
|
795
|
+
dt = "<i8" if signed else "<u8"
|
|
796
|
+
out = np.frombuffer(str.encode(out, "raw_unicode_escape"), dt)[
|
|
797
|
+
0] # interpret hexadecimal -> int (little-endian)
|
|
798
|
+
return out, pos
|
|
799
|
+
|
|
800
|
+
|
|
801
|
+
def read_int32(buf, pos, signed=False):
|
|
802
|
+
"""
|
|
803
|
+
Reads a 32bit int.
|
|
804
|
+
:param buf: The buffer
|
|
805
|
+
:param pos: Position in the buffer
|
|
806
|
+
:param signed: Whether of not the int is signed
|
|
807
|
+
:return:
|
|
808
|
+
The int and the new position in the buffer
|
|
809
|
+
"""
|
|
810
|
+
out, pos = read_with_check(buf=buf, pos=pos, nbytes=4)
|
|
811
|
+
out = ''.join(out)
|
|
812
|
+
dt = "<i4" if signed else "<u4"
|
|
813
|
+
out = np.frombuffer(str.encode(out, "raw_unicode_escape"), dt)[
|
|
814
|
+
0] # interpret hexadecimal -> int (little-endian)
|
|
815
|
+
return out, pos
|
|
816
|
+
|
|
817
|
+
|
|
818
|
+
def read_int16(buf, pos, signed=False):
|
|
819
|
+
"""
|
|
820
|
+
Reads a 16bit int.
|
|
821
|
+
:param buf: The buffer
|
|
822
|
+
:param pos: Position in the buffer
|
|
823
|
+
:param signed: Whether of not the int is signed
|
|
824
|
+
:return:
|
|
825
|
+
The int and the new position in the buffer
|
|
826
|
+
"""
|
|
827
|
+
out, pos = read_with_check(buf=buf, pos=pos, nbytes=2)
|
|
828
|
+
out = ''.join(out)
|
|
829
|
+
dt = "<i2" if signed else "<u2"
|
|
830
|
+
out = np.frombuffer(str.encode(out, "raw_unicode_escape"), dt)[
|
|
831
|
+
0] # interpret hexadecimal -> int (little-endian)
|
|
832
|
+
return out, pos
|
|
833
|
+
|
|
834
|
+
|
|
835
|
+
def read_double(buf, pos):
|
|
836
|
+
"""
|
|
837
|
+
Reads a double (64bit)
|
|
838
|
+
:param buf: The buffer
|
|
839
|
+
:param pos: Position in the buffer
|
|
840
|
+
:return:
|
|
841
|
+
The double and the new position in the buffer
|
|
842
|
+
"""
|
|
843
|
+
out, pos = read_with_check(buf=buf, pos=pos, nbytes=8)
|
|
844
|
+
out = ''.join(out)
|
|
845
|
+
dt = np.dtype('d') # double
|
|
846
|
+
dt = dt.newbyteorder('<') # little-endian
|
|
847
|
+
out = np.frombuffer(str.encode(out, "raw_unicode_escape"), dt)[
|
|
848
|
+
0] # interpret hexadecimal -> int (little-endian)
|
|
849
|
+
return out, pos
|
|
850
|
+
|
|
851
|
+
|
|
852
|
+
def read_float(buf, pos):
|
|
853
|
+
"""
|
|
854
|
+
Reads a float (32bit)
|
|
855
|
+
:param buf: The buffer
|
|
856
|
+
:param pos: Position in the buffer
|
|
857
|
+
:return:
|
|
858
|
+
The float and the new position in the buffer
|
|
859
|
+
"""
|
|
860
|
+
out, pos = read_with_check(buf=buf, pos=pos, nbytes=4)
|
|
861
|
+
out = ''.join(out)
|
|
862
|
+
dt = np.dtype('f') # double
|
|
863
|
+
dt = dt.newbyteorder('<') # little-endian
|
|
864
|
+
out = np.frombuffer(str.encode(out, "raw_unicode_escape"), dt)[
|
|
865
|
+
0] # interpret hexadecimal -> int (little-endian)
|
|
866
|
+
return out, pos
|
|
867
|
+
|
|
868
|
+
|
|
869
|
+
def read_with_check(buf, pos, nbytes):
|
|
870
|
+
"""
|
|
871
|
+
Reads and returns n bytes.
|
|
872
|
+
:param buf: The input buffer
|
|
873
|
+
:param pos: The current position
|
|
874
|
+
:param nbytes: number of bytes to open_topography in
|
|
875
|
+
:return: The bytes and the new position in the buffer
|
|
876
|
+
"""
|
|
877
|
+
|
|
878
|
+
if len(buf) < nbytes or len(buf) - nbytes < pos:
|
|
879
|
+
raise ValueError("Some sizes went wrong.")
|
|
880
|
+
|
|
881
|
+
out = buf[pos:pos + nbytes]
|
|
882
|
+
pos += int(nbytes)
|
|
883
|
+
|
|
884
|
+
out = out[0] if nbytes == 1 else out
|
|
885
|
+
return out, pos
|