dbdreader 0.6.1__tar.gz → 0.6.2__tar.gz
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.
- {dbdreader-0.6.1 → dbdreader-0.6.2}/PKG-INFO +13 -5
- {dbdreader-0.6.1 → dbdreader-0.6.2}/README.md +12 -4
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/_dbdreader.py +78 -60
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/dbdreader.py +1027 -771
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/decompress.py +110 -89
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader.egg-info/PKG-INFO +13 -5
- {dbdreader-0.6.1 → dbdreader-0.6.2}/extension/decompress.c +13 -5
- {dbdreader-0.6.1 → dbdreader-0.6.2}/pyproject.toml +4 -4
- {dbdreader-0.6.1 → dbdreader-0.6.2}/tests/test_dbdreader.py +0 -7
- {dbdreader-0.6.1 → dbdreader-0.6.2}/tests/test_decompress.py +35 -20
- {dbdreader-0.6.1 → dbdreader-0.6.2}/COPYING +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/COPYRIGHT +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/INSTALL.rst +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/LICENSE +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/MANIFEST.in +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/__init__.py +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/01600000.dcd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/01600000.ebd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/01600000.ecd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/01600000.mcg +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/01600000.mlg +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/01600001.dbd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/01600001.dcd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/01600001.ebd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/01600001.ecd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/02380107.ecd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/02380108.ecd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/02450133.tcd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/02450137.tcd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/amadeus-2014-203-00-000.SBD +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/amadeus-2014-203-00-000.TBD +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/amadeus-2014-204-05-000.dbd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/amadeus-2014-204-05-000.ebd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/amadeus-2014-204-05-000.sbd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/amadeus-2014-204-05-000.tbd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/amadeus-2014-204-05-001.sbd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/amadeus-2014-204-05-001.tbd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/amadeus-2014-204-05-002.sbd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/amadeus-2014-204-05-002.tbd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/ammonite-2008-028-01-000.mbd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/06a36d4e.cac +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/06a36d4e.ccc +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/093bd5ed.cac +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/0f682cb2.cac +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/39b2c12e.cac +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/468fd1be.cac +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/5fbf5947.cac +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/61b1780f.cac +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/6f9a08d3.cac +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/813b137d.cac +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/8d31d0c8.cac +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/92610b65.cac +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/c4ec741e.cac +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/d6f44165.cac +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/daad1b20.cac +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/daad1b20.ccc +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/dc76ebd5.cac +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac_missing/d6f44165.cac +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/dbd2asc_output.txt +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/electa-2023-143-00-050.sbd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/electa-2023-143-00-050.tbd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/empty-2014-204-05-000.dbd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/hal_1002-2024-183-4-4.sbd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/hal_1002-2024-183-4-4.tbd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/hal_1002-2024-183-4-6.tbd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/invalid_encoding-2014-204-05-000.dbd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/sebastian-2014-204-05-000.dbd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/sebastian-2014-204-05-000.ebd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/sebastian-2014-204-05-001.dbd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/sebastian-2014-204-05-001.ebd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/unit_887-2021-321-3-0.sbd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/unit_887-2021-321-3-0.tbd +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/scripts.py +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader.egg-info/SOURCES.txt +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader.egg-info/dependency_links.txt +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader.egg-info/entry_points.txt +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader.egg-info/requires.txt +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader.egg-info/top_level.txt +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/examples/read_multiple_dbd_files.py +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/examples/read_single_dbd_file.py +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/extension/dbdreader.c +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/extension/include/dbdreader.h +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/extension/include/decompress.h +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/extension/py_dbdreader.c +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/lz4/include/lz4.h +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/lz4/lz4.c +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/requirements.txt +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/setup.cfg +0 -0
- {dbdreader-0.6.1 → dbdreader-0.6.2}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dbdreader
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.2
|
|
4
4
|
Summary: A python module to access binary data files generated by Teledyne WebbResearch gliders
|
|
5
5
|
Author-email: Lucas Merckelbach <lucas.merckelbach@hereon.de>
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -33,15 +33,23 @@ binary files, so that they can be further analysed.
|
|
|
33
33
|
|
|
34
34
|
## Change log
|
|
35
35
|
|
|
36
|
-
### Version 6.
|
|
36
|
+
### Version 0.6.2
|
|
37
|
+
|
|
38
|
+
* Builds wheels for python versions 3.10 -- 3.14, including macos
|
|
39
|
+
* Removes the get_list() method from the DBD class
|
|
40
|
+
* Adds CI tests for all platforms (contributed by jklymak).
|
|
41
|
+
* Typehinting introduced
|
|
42
|
+
|
|
43
|
+
### Version 0.6.1
|
|
37
44
|
|
|
38
45
|
* Drops dependency on scipy.
|
|
39
46
|
|
|
40
|
-
### Version 6.0
|
|
47
|
+
### Version 0.6.0
|
|
41
48
|
|
|
42
|
-
* Offers a python
|
|
49
|
+
* Offers a python-only implementation, alongside the C-extension to
|
|
43
50
|
read the binary files. At the expense of processing speed, no
|
|
44
|
-
compiler is required to use this package.
|
|
51
|
+
compiler is required to use this package. Python-only code
|
|
52
|
+
contributed by jklymak.
|
|
45
53
|
|
|
46
54
|
* A github workflow is implemented that generates pre-compiled wheels
|
|
47
55
|
for linux and windows platforms for python version 3.10, 3.11, 3.12
|
|
@@ -14,15 +14,23 @@ binary files, so that they can be further analysed.
|
|
|
14
14
|
|
|
15
15
|
## Change log
|
|
16
16
|
|
|
17
|
-
### Version 6.
|
|
17
|
+
### Version 0.6.2
|
|
18
|
+
|
|
19
|
+
* Builds wheels for python versions 3.10 -- 3.14, including macos
|
|
20
|
+
* Removes the get_list() method from the DBD class
|
|
21
|
+
* Adds CI tests for all platforms (contributed by jklymak).
|
|
22
|
+
* Typehinting introduced
|
|
23
|
+
|
|
24
|
+
### Version 0.6.1
|
|
18
25
|
|
|
19
26
|
* Drops dependency on scipy.
|
|
20
27
|
|
|
21
|
-
### Version 6.0
|
|
28
|
+
### Version 0.6.0
|
|
22
29
|
|
|
23
|
-
* Offers a python
|
|
30
|
+
* Offers a python-only implementation, alongside the C-extension to
|
|
24
31
|
read the binary files. At the expense of processing speed, no
|
|
25
|
-
compiler is required to use this package.
|
|
32
|
+
compiler is required to use this package. Python-only code
|
|
33
|
+
contributed by jklymak.
|
|
26
34
|
|
|
27
35
|
* A github workflow is implemented that generates pre-compiled wheels
|
|
28
36
|
for linux and windows platforms for python version 3.10, 3.11, 3.12
|
|
@@ -42,15 +42,16 @@ inside a Python loop, which dominated runtime for files with many cycles.
|
|
|
42
42
|
"""
|
|
43
43
|
|
|
44
44
|
import struct
|
|
45
|
-
|
|
45
|
+
from typing import Any
|
|
46
46
|
|
|
47
|
+
import numpy as np
|
|
47
48
|
|
|
48
49
|
# ── constants (mirror dbdreader.h) ───────────────────────────────────────────
|
|
49
50
|
FILLVALUE = 1e9
|
|
50
|
-
UPDATED
|
|
51
|
-
SAME
|
|
52
|
-
NOTSET
|
|
53
|
-
_FMT_CHAR = {2:
|
|
51
|
+
UPDATED = 2
|
|
52
|
+
SAME = 1
|
|
53
|
+
NOTSET = 0
|
|
54
|
+
_FMT_CHAR = {2: "h", 4: "f", 8: "d"} # byte-size → struct char
|
|
54
55
|
|
|
55
56
|
# Shift amounts to extract 4 × 2-bit fields from one byte (MSB first)
|
|
56
57
|
_SHIFTS = np.array([6, 4, 2, 0], dtype=np.int32)
|
|
@@ -58,17 +59,19 @@ _SHIFTS = np.array([6, 4, 2, 0], dtype=np.int32)
|
|
|
58
59
|
|
|
59
60
|
# ── helpers ──────────────────────────────────────────────────────────────────
|
|
60
61
|
|
|
62
|
+
|
|
61
63
|
def _read_file(filename: str) -> bytes:
|
|
62
64
|
"""Return raw decompressed bytes for *filename* (handles .?cd/.?cg files)."""
|
|
63
65
|
from dbdreader.decompress import is_compressed, Decompressor
|
|
66
|
+
|
|
64
67
|
if is_compressed(filename):
|
|
65
68
|
with Decompressor(filename) as d:
|
|
66
69
|
return d.decompress()
|
|
67
|
-
with open(filename,
|
|
70
|
+
with open(filename, "rb") as fh:
|
|
68
71
|
return fh.read()
|
|
69
72
|
|
|
70
73
|
|
|
71
|
-
def _insert_ti(vi_list: list, ti: int):
|
|
74
|
+
def _insert_ti(vi_list: list[int], ti: int) -> tuple[list[int], int]:
|
|
72
75
|
"""
|
|
73
76
|
Insert *ti* into sorted *vi_list* before the first element > ti,
|
|
74
77
|
replicating the C code in get_variable().
|
|
@@ -77,7 +80,7 @@ def _insert_ti(vi_list: list, ti: int):
|
|
|
77
80
|
of ti within vit.
|
|
78
81
|
"""
|
|
79
82
|
vit = []
|
|
80
|
-
i
|
|
83
|
+
i = 0
|
|
81
84
|
while i < len(vi_list):
|
|
82
85
|
if vi_list[i] > ti:
|
|
83
86
|
break
|
|
@@ -90,7 +93,9 @@ def _insert_ti(vi_list: list, ti: int):
|
|
|
90
93
|
return vit, nti
|
|
91
94
|
|
|
92
95
|
|
|
93
|
-
def _build_chunk_lut(
|
|
96
|
+
def _build_chunk_lut(
|
|
97
|
+
n_state_bytes: int, n_sensors: int, bs_list: list[int]
|
|
98
|
+
) -> list[list[int]]:
|
|
94
99
|
"""
|
|
95
100
|
Build a lookup table: lut[byte_pos][byte_value] = total data bytes
|
|
96
101
|
contributed to the chunk by the four sensors encoded in that state byte.
|
|
@@ -104,16 +109,27 @@ def _build_chunk_lut(n_state_bytes: int, n_sensors: int, bs_list: list) -> list:
|
|
|
104
109
|
sidx = bpos * 4 + slot
|
|
105
110
|
if sidx >= n_sensors:
|
|
106
111
|
break
|
|
107
|
-
shift = 6 - slot * 2
|
|
108
|
-
states = (byte_vals >> shift) & 3
|
|
112
|
+
shift = 6 - slot * 2 # 6, 4, 2, 0
|
|
113
|
+
states = (byte_vals >> shift) & 3 # (256,)
|
|
109
114
|
lut[bpos] += (states == UPDATED) * bs_list[sidx]
|
|
110
|
-
return lut.tolist()
|
|
115
|
+
return lut.tolist() # nested Python list for fast indexing
|
|
111
116
|
|
|
112
117
|
|
|
113
118
|
# ── public API ───────────────────────────────────────────────────────────────
|
|
114
119
|
|
|
115
|
-
|
|
116
|
-
|
|
120
|
+
|
|
121
|
+
def get(
|
|
122
|
+
n_state_bytes: int,
|
|
123
|
+
n_sensors: int,
|
|
124
|
+
bin_offset: int,
|
|
125
|
+
byte_sizes: tuple[int, ...],
|
|
126
|
+
filename: str,
|
|
127
|
+
ti: int,
|
|
128
|
+
vi: tuple[int, ...],
|
|
129
|
+
return_nans: int,
|
|
130
|
+
skip_initial_line: int,
|
|
131
|
+
max_values_to_read: int,
|
|
132
|
+
) -> tuple[int, list[Any]]:
|
|
117
133
|
"""
|
|
118
134
|
Read one or more sensor time-series from a glider binary data file.
|
|
119
135
|
|
|
@@ -141,20 +157,20 @@ def get(n_state_bytes, n_sensors, bin_offset, byte_sizes,
|
|
|
141
157
|
try:
|
|
142
158
|
data = _read_file(filename)
|
|
143
159
|
except FileNotFoundError:
|
|
144
|
-
return 2, []
|
|
160
|
+
return 2, [] # ERROR_FILE_NOT_FOUND
|
|
145
161
|
except Exception:
|
|
146
|
-
return 1, []
|
|
147
|
-
nv
|
|
148
|
-
bs_list
|
|
162
|
+
return 1, [] # ERROR_UNEXPECTED_END_OF_FILE
|
|
163
|
+
nv = len(vi)
|
|
164
|
+
bs_list = list(byte_sizes)
|
|
149
165
|
vit, nti = _insert_ti(list(vi), ti)
|
|
150
|
-
nvt
|
|
166
|
+
nvt = len(vit) # = nv + 1
|
|
151
167
|
|
|
152
168
|
# ── byte-order detection ─────────────────────────────────────────────────
|
|
153
|
-
endian =
|
|
169
|
+
endian = "<" if struct.unpack_from("<H", data, bin_offset + 2)[0] == 4660 else ">"
|
|
154
170
|
|
|
155
171
|
# ── numpy array of byte sizes (used in vectorised pass 2) ────────────────
|
|
156
|
-
bs_arr = np.array(bs_list, dtype=np.int32)
|
|
157
|
-
vit_arr = np.array(vit,
|
|
172
|
+
bs_arr = np.array(bs_list, dtype=np.int32) # (n_sensors,)
|
|
173
|
+
vit_arr = np.array(vit, dtype=np.intp) # (nvt,)
|
|
158
174
|
|
|
159
175
|
# ── chunk-size lookup table (for pass 1) ─────────────────────────────────
|
|
160
176
|
chunk_lut = _build_chunk_lut(n_state_bytes, n_sensors, bs_list)
|
|
@@ -166,13 +182,13 @@ def get(n_state_bytes, n_sensors, bin_offset, byte_sizes,
|
|
|
166
182
|
# Cost: O(n_cycles × n_state_bytes) pure-Python ops — cheap.
|
|
167
183
|
|
|
168
184
|
min_offset_value = -2 if return_nans else -1
|
|
169
|
-
file_size
|
|
170
|
-
pos
|
|
171
|
-
write_data
|
|
185
|
+
file_size = len(data)
|
|
186
|
+
pos = bin_offset + 17 # skip known cycle
|
|
187
|
+
write_data = not bool(skip_initial_line)
|
|
172
188
|
|
|
173
|
-
state_positions = []
|
|
174
|
-
chunk_sizes
|
|
175
|
-
write_flags
|
|
189
|
+
state_positions = [] # file pos of state bytes for each cycle
|
|
190
|
+
chunk_sizes = [] # data-chunk byte count for each cycle
|
|
191
|
+
write_flags = [] # whether to emit output for each cycle
|
|
176
192
|
|
|
177
193
|
while pos < file_size:
|
|
178
194
|
if pos + n_state_bytes > file_size:
|
|
@@ -187,51 +203,53 @@ def get(n_state_bytes, n_sensors, bin_offset, byte_sizes,
|
|
|
187
203
|
chunk_sizes.append(chunksize)
|
|
188
204
|
write_flags.append(write_data)
|
|
189
205
|
|
|
190
|
-
pos
|
|
191
|
-
write_data
|
|
206
|
+
pos += n_state_bytes + chunksize + 1
|
|
207
|
+
write_data = True
|
|
192
208
|
|
|
193
209
|
n_cycles = len(state_positions)
|
|
194
210
|
if n_cycles == 0:
|
|
195
211
|
return 0, [[] for _ in range(nv)] + [[] for _ in range(nv)]
|
|
196
212
|
|
|
197
213
|
# ── PASS 2: vectorised processing of all cycles at once ──────────────────
|
|
198
|
-
data_u8
|
|
199
|
-
sp_arr
|
|
200
|
-
cstart
|
|
214
|
+
data_u8 = np.frombuffer(data, dtype=np.uint8)
|
|
215
|
+
sp_arr = np.array(state_positions, dtype=np.intp) # (n_cycles,)
|
|
216
|
+
cstart = sp_arr + n_state_bytes # chunk start positions
|
|
201
217
|
|
|
202
218
|
# Extract state bytes for ALL cycles: shape (n_cycles, n_state_bytes)
|
|
203
|
-
sb_idx
|
|
204
|
-
all_sb
|
|
219
|
+
sb_idx = sp_arr[:, np.newaxis] + np.arange(n_state_bytes, dtype=np.intp)
|
|
220
|
+
all_sb = data_u8[sb_idx] # uint8, (n_cycles, n_state_bytes)
|
|
205
221
|
|
|
206
222
|
# Decode 2-bit fields: (n_cycles, n_state_bytes, 4) → (n_cycles, n_sensors)
|
|
207
|
-
all_fields = (
|
|
223
|
+
all_fields = (
|
|
224
|
+
all_sb[:, :, np.newaxis] >> _SHIFTS
|
|
225
|
+
) & 3 # int, (n_cycles, n_state_bytes, 4)
|
|
208
226
|
all_fields = all_fields.reshape(n_cycles, n_state_bytes * 4)[:, :n_sensors]
|
|
209
227
|
|
|
210
228
|
# Compute byte offsets within the data chunk for every sensor in every cycle.
|
|
211
229
|
# UPDATED → cumulative offset; SAME → -1; NOTSET → -2.
|
|
212
|
-
upd_mask
|
|
213
|
-
w_bs
|
|
214
|
-
cum_bs
|
|
215
|
-
all_off
|
|
216
|
-
all_off
|
|
230
|
+
upd_mask = all_fields == UPDATED # bool, (n_cycles, n_sensors)
|
|
231
|
+
w_bs = np.where(upd_mask, bs_arr[np.newaxis, :], 0) # int32, (n_cycles, n_sensors)
|
|
232
|
+
cum_bs = np.cumsum(w_bs, axis=1) # int32, (n_cycles, n_sensors)
|
|
233
|
+
all_off = np.where(upd_mask, cum_bs - w_bs, np.int32(-1)) # (n_cycles, n_sensors)
|
|
234
|
+
all_off = np.where(all_fields == NOTSET, np.int32(-2), all_off)
|
|
217
235
|
|
|
218
236
|
# Offsets for only the wanted sensors: (n_cycles, nvt)
|
|
219
|
-
wanted_off = all_off[:, vit_arr]
|
|
237
|
+
wanted_off = all_off[:, vit_arr] # int32, (n_cycles, nvt)
|
|
220
238
|
|
|
221
239
|
# ── helper: read all values for one column of wanted_off ─────────────────
|
|
222
|
-
def _read_col(col: int) -> np.ndarray:
|
|
240
|
+
def _read_col(col: int) -> np.ndarray[Any, Any]:
|
|
223
241
|
"""
|
|
224
242
|
Return float64 array (n_cycles,) for wanted sensor at column *col*.
|
|
225
243
|
UPDATED cycles → actual data value.
|
|
226
244
|
SAME cycles → carry-forward of last UPDATED value (numpy ffill).
|
|
227
245
|
NOTSET cycles → NaN (or FILLVALUE if return_nans).
|
|
228
246
|
"""
|
|
229
|
-
sidx
|
|
230
|
-
bs
|
|
231
|
-
offsets = wanted_off[:, col]
|
|
232
|
-
vals
|
|
247
|
+
sidx = vit[col]
|
|
248
|
+
bs = bs_list[sidx]
|
|
249
|
+
offsets = wanted_off[:, col] # int32, (n_cycles,)
|
|
250
|
+
vals = np.full(n_cycles, np.nan, dtype=np.float64)
|
|
233
251
|
|
|
234
|
-
upd = np.where(offsets >= 0)[0]
|
|
252
|
+
upd = np.where(offsets >= 0)[0] # indices of UPDATED cycles
|
|
235
253
|
if upd.size:
|
|
236
254
|
abs_pos = cstart[upd] + offsets[upd].astype(np.intp)
|
|
237
255
|
|
|
@@ -242,15 +260,15 @@ def get(n_state_bytes, n_sensors, bin_offset, byte_sizes,
|
|
|
242
260
|
# Gather bs consecutive bytes for each updated cycle,
|
|
243
261
|
# reinterpret as the sensor's native type.
|
|
244
262
|
bidx = abs_pos[:, np.newaxis] + np.arange(bs, dtype=np.intp)
|
|
245
|
-
raw
|
|
246
|
-
dt
|
|
263
|
+
raw = data_u8[bidx].tobytes() # flat byte buffer
|
|
264
|
+
dt = np.dtype(endian + _FMT_CHAR[bs])
|
|
247
265
|
vals[upd] = np.frombuffer(raw, dtype=dt)
|
|
248
266
|
|
|
249
267
|
# Forward-fill: propagate each UPDATED value into subsequent SAME slots.
|
|
250
268
|
# Classic numpy trick: build an index array that "sticks" at the last
|
|
251
269
|
# non-NaN position, then use it to index vals.
|
|
252
270
|
has_val = ~np.isnan(vals)
|
|
253
|
-
ff_idx
|
|
271
|
+
ff_idx = np.where(has_val, np.arange(n_cycles, dtype=np.intp), np.intp(0))
|
|
254
272
|
np.maximum.accumulate(ff_idx, out=ff_idx)
|
|
255
273
|
vals = vals[ff_idx]
|
|
256
274
|
|
|
@@ -262,30 +280,30 @@ def get(n_state_bytes, n_sensors, bin_offset, byte_sizes,
|
|
|
262
280
|
return vals
|
|
263
281
|
|
|
264
282
|
# ── read time and sensor values ───────────────────────────────────────────
|
|
265
|
-
t_vals = _read_col(nti)
|
|
283
|
+
t_vals = _read_col(nti) # (n_cycles,) float64
|
|
266
284
|
write_arr = np.array(write_flags, dtype=bool)
|
|
267
285
|
|
|
268
|
-
result_t = [None] * nv
|
|
269
|
-
result_v = [None] * nv
|
|
286
|
+
result_t: list[Any] = [None] * nv
|
|
287
|
+
result_v: list[Any] = [None] * nv
|
|
270
288
|
|
|
271
289
|
for col in range(nvt):
|
|
272
290
|
if col == nti:
|
|
273
291
|
continue
|
|
274
|
-
j = col - (1 if col > nti else 0)
|
|
292
|
+
j = col - (1 if col > nti else 0) # output index in result_t/v
|
|
275
293
|
|
|
276
|
-
v_vals
|
|
277
|
-
v_off
|
|
294
|
+
v_vals = _read_col(col)
|
|
295
|
+
v_off = wanted_off[:, col]
|
|
278
296
|
|
|
279
297
|
# Include this cycle in output if:
|
|
280
298
|
# • write_flags says so (skip_initial_line handling)
|
|
281
299
|
# • sensor has a valid offset (UPDATED or SAME; also NOTSET if return_nans)
|
|
282
|
-
include
|
|
283
|
-
mask
|
|
300
|
+
include = v_off >= min_offset_value
|
|
301
|
+
mask = write_arr & include
|
|
284
302
|
|
|
285
303
|
if max_values_to_read > 0:
|
|
286
304
|
keep = np.where(mask)[0]
|
|
287
305
|
if keep.size > max_values_to_read:
|
|
288
|
-
mask
|
|
306
|
+
mask = np.zeros(n_cycles, dtype=bool)
|
|
289
307
|
mask[keep[:max_values_to_read]] = True
|
|
290
308
|
|
|
291
309
|
result_t[j] = t_vals[mask].tolist()
|