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.
Files changed (89) hide show
  1. {dbdreader-0.6.1 → dbdreader-0.6.2}/PKG-INFO +13 -5
  2. {dbdreader-0.6.1 → dbdreader-0.6.2}/README.md +12 -4
  3. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/_dbdreader.py +78 -60
  4. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/dbdreader.py +1027 -771
  5. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/decompress.py +110 -89
  6. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader.egg-info/PKG-INFO +13 -5
  7. {dbdreader-0.6.1 → dbdreader-0.6.2}/extension/decompress.c +13 -5
  8. {dbdreader-0.6.1 → dbdreader-0.6.2}/pyproject.toml +4 -4
  9. {dbdreader-0.6.1 → dbdreader-0.6.2}/tests/test_dbdreader.py +0 -7
  10. {dbdreader-0.6.1 → dbdreader-0.6.2}/tests/test_decompress.py +35 -20
  11. {dbdreader-0.6.1 → dbdreader-0.6.2}/COPYING +0 -0
  12. {dbdreader-0.6.1 → dbdreader-0.6.2}/COPYRIGHT +0 -0
  13. {dbdreader-0.6.1 → dbdreader-0.6.2}/INSTALL.rst +0 -0
  14. {dbdreader-0.6.1 → dbdreader-0.6.2}/LICENSE +0 -0
  15. {dbdreader-0.6.1 → dbdreader-0.6.2}/MANIFEST.in +0 -0
  16. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/__init__.py +0 -0
  17. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/01600000.dcd +0 -0
  18. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/01600000.ebd +0 -0
  19. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/01600000.ecd +0 -0
  20. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/01600000.mcg +0 -0
  21. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/01600000.mlg +0 -0
  22. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/01600001.dbd +0 -0
  23. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/01600001.dcd +0 -0
  24. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/01600001.ebd +0 -0
  25. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/01600001.ecd +0 -0
  26. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/02380107.ecd +0 -0
  27. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/02380108.ecd +0 -0
  28. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/02450133.tcd +0 -0
  29. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/02450137.tcd +0 -0
  30. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/amadeus-2014-203-00-000.SBD +0 -0
  31. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/amadeus-2014-203-00-000.TBD +0 -0
  32. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/amadeus-2014-204-05-000.dbd +0 -0
  33. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/amadeus-2014-204-05-000.ebd +0 -0
  34. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/amadeus-2014-204-05-000.sbd +0 -0
  35. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/amadeus-2014-204-05-000.tbd +0 -0
  36. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/amadeus-2014-204-05-001.sbd +0 -0
  37. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/amadeus-2014-204-05-001.tbd +0 -0
  38. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/amadeus-2014-204-05-002.sbd +0 -0
  39. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/amadeus-2014-204-05-002.tbd +0 -0
  40. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/ammonite-2008-028-01-000.mbd +0 -0
  41. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/06a36d4e.cac +0 -0
  42. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/06a36d4e.ccc +0 -0
  43. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/093bd5ed.cac +0 -0
  44. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/0f682cb2.cac +0 -0
  45. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/39b2c12e.cac +0 -0
  46. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/468fd1be.cac +0 -0
  47. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/5fbf5947.cac +0 -0
  48. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/61b1780f.cac +0 -0
  49. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/6f9a08d3.cac +0 -0
  50. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/813b137d.cac +0 -0
  51. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/8d31d0c8.cac +0 -0
  52. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/92610b65.cac +0 -0
  53. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/c4ec741e.cac +0 -0
  54. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/d6f44165.cac +0 -0
  55. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/daad1b20.cac +0 -0
  56. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/daad1b20.ccc +0 -0
  57. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac/dc76ebd5.cac +0 -0
  58. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/cac_missing/d6f44165.cac +0 -0
  59. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/dbd2asc_output.txt +0 -0
  60. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/electa-2023-143-00-050.sbd +0 -0
  61. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/electa-2023-143-00-050.tbd +0 -0
  62. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/empty-2014-204-05-000.dbd +0 -0
  63. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/hal_1002-2024-183-4-4.sbd +0 -0
  64. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/hal_1002-2024-183-4-4.tbd +0 -0
  65. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/hal_1002-2024-183-4-6.tbd +0 -0
  66. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/invalid_encoding-2014-204-05-000.dbd +0 -0
  67. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/sebastian-2014-204-05-000.dbd +0 -0
  68. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/sebastian-2014-204-05-000.ebd +0 -0
  69. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/sebastian-2014-204-05-001.dbd +0 -0
  70. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/sebastian-2014-204-05-001.ebd +0 -0
  71. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/unit_887-2021-321-3-0.sbd +0 -0
  72. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/data/unit_887-2021-321-3-0.tbd +0 -0
  73. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader/scripts.py +0 -0
  74. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader.egg-info/SOURCES.txt +0 -0
  75. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader.egg-info/dependency_links.txt +0 -0
  76. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader.egg-info/entry_points.txt +0 -0
  77. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader.egg-info/requires.txt +0 -0
  78. {dbdreader-0.6.1 → dbdreader-0.6.2}/dbdreader.egg-info/top_level.txt +0 -0
  79. {dbdreader-0.6.1 → dbdreader-0.6.2}/examples/read_multiple_dbd_files.py +0 -0
  80. {dbdreader-0.6.1 → dbdreader-0.6.2}/examples/read_single_dbd_file.py +0 -0
  81. {dbdreader-0.6.1 → dbdreader-0.6.2}/extension/dbdreader.c +0 -0
  82. {dbdreader-0.6.1 → dbdreader-0.6.2}/extension/include/dbdreader.h +0 -0
  83. {dbdreader-0.6.1 → dbdreader-0.6.2}/extension/include/decompress.h +0 -0
  84. {dbdreader-0.6.1 → dbdreader-0.6.2}/extension/py_dbdreader.c +0 -0
  85. {dbdreader-0.6.1 → dbdreader-0.6.2}/lz4/include/lz4.h +0 -0
  86. {dbdreader-0.6.1 → dbdreader-0.6.2}/lz4/lz4.c +0 -0
  87. {dbdreader-0.6.1 → dbdreader-0.6.2}/requirements.txt +0 -0
  88. {dbdreader-0.6.1 → dbdreader-0.6.2}/setup.cfg +0 -0
  89. {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.1
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.0.1
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.0
47
+ ### Version 0.6.0
41
48
 
42
- * Offers a python only implementation, alongside the C-extension to
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.0.1
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.0
28
+ ### Version 0.6.0
22
29
 
23
- * Offers a python only implementation, alongside the C-extension to
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
- import numpy as np
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 = 2
51
- SAME = 1
52
- NOTSET = 0
53
- _FMT_CHAR = {2: 'h', 4: 'f', 8: 'd'} # byte-size → struct char
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, 'rb') as fh:
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 = 0
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(n_state_bytes: int, n_sensors: int, bs_list: list) -> list:
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 # 6, 4, 2, 0
108
- states = (byte_vals >> shift) & 3 # (256,)
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() # nested Python list for fast indexing
115
+ return lut.tolist() # nested Python list for fast indexing
111
116
 
112
117
 
113
118
  # ── public API ───────────────────────────────────────────────────────────────
114
119
 
115
- def get(n_state_bytes, n_sensors, bin_offset, byte_sizes,
116
- filename, ti, vi, return_nans, skip_initial_line, max_values_to_read):
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, [] # ERROR_FILE_NOT_FOUND
160
+ return 2, [] # ERROR_FILE_NOT_FOUND
145
161
  except Exception:
146
- return 1, [] # ERROR_UNEXPECTED_END_OF_FILE
147
- nv = len(vi)
148
- bs_list = list(byte_sizes)
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 = len(vit) # = nv + 1
166
+ nvt = len(vit) # = nv + 1
151
167
 
152
168
  # ── byte-order detection ─────────────────────────────────────────────────
153
- endian = '<' if struct.unpack_from('<H', data, bin_offset + 2)[0] == 4660 else '>'
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) # (n_sensors,)
157
- vit_arr = np.array(vit, dtype=np.intp) # (nvt,)
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 = len(data)
170
- pos = bin_offset + 17 # skip known cycle
171
- write_data = not bool(skip_initial_line)
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 = [] # file pos of state bytes for each cycle
174
- chunk_sizes = [] # data-chunk byte count for each cycle
175
- write_flags = [] # whether to emit output for each cycle
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 += n_state_bytes + chunksize + 1
191
- write_data = True
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 = np.frombuffer(data, dtype=np.uint8)
199
- sp_arr = np.array(state_positions, dtype=np.intp) # (n_cycles,)
200
- cstart = sp_arr + n_state_bytes # chunk start positions
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 = sp_arr[:, np.newaxis] + np.arange(n_state_bytes, dtype=np.intp)
204
- all_sb = data_u8[sb_idx] # uint8, (n_cycles, n_state_bytes)
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 = ((all_sb[:, :, np.newaxis] >> _SHIFTS) & 3) # int, (n_cycles, n_state_bytes, 4)
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 = (all_fields == UPDATED) # bool, (n_cycles, n_sensors)
213
- w_bs = np.where(upd_mask, bs_arr[np.newaxis, :], 0) # int32, (n_cycles, n_sensors)
214
- cum_bs = np.cumsum(w_bs, axis=1) # int32, (n_cycles, n_sensors)
215
- all_off = np.where(upd_mask, cum_bs - w_bs, np.int32(-1)) # (n_cycles, n_sensors)
216
- all_off = np.where(all_fields == NOTSET, np.int32(-2), 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] # int32, (n_cycles, nvt)
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 = vit[col]
230
- bs = bs_list[sidx]
231
- offsets = wanted_off[:, col] # int32, (n_cycles,)
232
- vals = np.full(n_cycles, np.nan, dtype=np.float64)
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] # indices of UPDATED cycles
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 = data_u8[bidx].tobytes() # flat byte buffer
246
- dt = np.dtype(endian + _FMT_CHAR[bs])
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 = np.where(has_val, np.arange(n_cycles, dtype=np.intp), np.intp(0))
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) # (n_cycles,) float64
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) # output index in result_t/v
292
+ j = col - (1 if col > nti else 0) # output index in result_t/v
275
293
 
276
- v_vals = _read_col(col)
277
- v_off = wanted_off[:, col]
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 = (v_off >= min_offset_value)
283
- mask = write_arr & include
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 = np.zeros(n_cycles, dtype=bool)
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()