legend-daq2lh5 1.0.2__py3-none-any.whl → 1.2.0a1__py3-none-any.whl

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.
daq2lh5/_version.py CHANGED
@@ -2,7 +2,15 @@
2
2
  # don't change, don't track in version control
3
3
  TYPE_CHECKING = False
4
4
  if TYPE_CHECKING:
5
- from typing import Tuple
5
+ from typing import Tuple, Union
6
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
7
+ else:
8
+ VERSION_TUPLE = object
6
9
 
7
- __version__ = version = '1.0.2' # type: str
8
- __version_tuple__ = version_tuple = (1, 0, 2) # type: Tuple[int | str, ...]
10
+ version: str
11
+ __version__: str
12
+ __version_tuple__: VERSION_TUPLE
13
+ version_tuple: VERSION_TUPLE
14
+
15
+ __version__ = version = '1.2.0a1'
16
+ __version_tuple__ = version_tuple = (1, 2, 0)
@@ -48,10 +48,17 @@ def buffer_processor(rb: RawBuffer) -> Table:
48
48
  ``"dtype_conv": {"lgdo": "dtype" [, ...]}`` `(dict)`
49
49
  Casts `lgdo` to the requested data type.
50
50
 
51
- ``"compression": { "lgdo": "codec_name" [, ...]}`` `(dict)`
51
+ ``"compression": {"lgdo": "codec_name" [, ...]}`` `(dict)`
52
52
  Updates the `compression` attribute of `lgdo` to `codec_name`. The
53
53
  attribute sets the compression algorithm applied by
54
- :func:`~.lgdo.lh5_store.LH5Store.read_object` before writing `lgdo` to
54
+ :func:`~lgdo.lh5.store.LH5Store.read` before writing `lgdo` to
55
+ disk. Can be used to apply custom waveform compression algorithms from
56
+ :mod:`lgdo.compression`.
57
+
58
+ ``"hdf5_settings": {"lgdo": { <HDF5 settings> }}`` `(dict)`
59
+ Updates the `hdf5_settings` attribute of `lgdo`. The attribute sets the
60
+ HDF5 dataset options applied by
61
+ :func:`~lgdo.lh5.store.LH5Store.read` before writing `lgdo` to
55
62
  disk.
56
63
 
57
64
  Parameters
@@ -102,7 +109,9 @@ def buffer_processor(rb: RawBuffer) -> Table:
102
109
  ,}
103
110
  "compression": {
104
111
  "windowed_waveform/values": RadwareSigcompress(codec_shift=-32768),
105
- "presummed_waveform/values": ULEB128ZigZagDiff(),
112
+ }
113
+ "hdf5_settings": {
114
+ "presummed_waveform/values": {"shuffle": True, "compression": "lzf"},
106
115
  }
107
116
  }
108
117
  },
@@ -143,7 +152,7 @@ def buffer_processor(rb: RawBuffer) -> Table:
143
152
  if "drop" in rb.proc_spec.keys():
144
153
  process_drop(rb, tmp_table)
145
154
 
146
- # at last, assign compression attributes
155
+ # assign compression attributes
147
156
  if "compression" in rb.proc_spec.keys():
148
157
  for name, codec in rb.proc_spec["compression"].items():
149
158
  ptr = tmp_table
@@ -154,6 +163,15 @@ def buffer_processor(rb: RawBuffer) -> Table:
154
163
  codec if isinstance(codec, WaveformCodec) else str2wfcodec(codec)
155
164
  )
156
165
 
166
+ # and HDF5 settings
167
+ if "hdf5_settings" in rb.proc_spec.keys():
168
+ for name, settings in rb.proc_spec["hdf5_settings"].items():
169
+ ptr = tmp_table
170
+ for word in name.split("/"):
171
+ ptr = ptr[word]
172
+
173
+ ptr.attrs["hdf5_settings"] = settings
174
+
157
175
  return tmp_table
158
176
 
159
177
 
@@ -277,7 +295,7 @@ def process_windowed_t0(t0s: Array, dts: Array, start_index: int) -> Array:
277
295
 
278
296
 
279
297
  def process_dsp(rb: RawBuffer, tmp_table: Table) -> None:
280
- r"""Run a DSP processing chain.
298
+ r"""Run a DSP processing chain with :mod:`dspeed`.
281
299
 
282
300
  Run a provided DSP config from `rb.proc_spec` using
283
301
  :func:`.dsp.build_processing_chain`, and add specified outputs to the
@@ -6,7 +6,7 @@ import os
6
6
 
7
7
  import h5py
8
8
  import lgdo
9
- from lgdo import LH5Store
9
+ from lgdo import lh5
10
10
 
11
11
  from ..buffer_processor.buffer_processor import buffer_processor
12
12
  from ..raw_buffer import RawBuffer, RawBufferLibrary
@@ -54,14 +54,14 @@ def lh5_buffer_processor(
54
54
  """
55
55
 
56
56
  # Initialize the input raw file
57
- raw_store = LH5Store()
57
+ raw_store = lh5.LH5Store()
58
58
  lh5_file = raw_store.gimme_file(lh5_raw_file_in, "r")
59
59
  if lh5_file is None:
60
60
  raise ValueError(f"input file not found: {lh5_raw_file_in}")
61
61
  return
62
62
 
63
63
  # List the groups in the raw file
64
- lh5_groups = lgdo.ls(lh5_raw_file_in)
64
+ lh5_groups = lh5.ls(lh5_raw_file_in)
65
65
  lh5_tables = []
66
66
 
67
67
  # check if group points to raw data; sometimes 'raw' is nested, e.g g024/raw
@@ -69,21 +69,19 @@ def lh5_buffer_processor(
69
69
  # Make sure that the upper level key isn't a dataset
70
70
  if isinstance(lh5_file[tb], h5py.Dataset):
71
71
  lh5_tables.append(f"{tb}")
72
- elif "raw" not in tb and lgdo.ls(lh5_file, f"{tb}/raw"):
72
+ elif "raw" not in tb and lh5.ls(lh5_file, f"{tb}/raw"):
73
73
  lh5_tables.append(f"{tb}/raw")
74
74
  # Look one layer deeper for a :meth:`lgdo.Table` if necessary
75
- elif lgdo.ls(lh5_file, f"{tb}"):
75
+ elif lh5.ls(lh5_file, f"{tb}"):
76
76
  # Check to make sure that this isn't a table itself
77
- maybe_table, _ = raw_store.read_object(f"{tb}", lh5_file)
77
+ maybe_table, _ = raw_store.read(f"{tb}", lh5_file)
78
78
  if isinstance(maybe_table, lgdo.Table):
79
79
  lh5_tables.append(f"{tb}")
80
80
  del maybe_table
81
81
  # otherwise, go deeper
82
82
  else:
83
- for sub_table in lgdo.ls(lh5_file, f"{tb}"):
84
- maybe_table, _ = raw_store.read_object(
85
- f"{tb}/{sub_table}", lh5_file
86
- )
83
+ for sub_table in lh5.ls(lh5_file, f"{tb}"):
84
+ maybe_table, _ = raw_store.read(f"{tb}/{sub_table}", lh5_file)
87
85
  if isinstance(maybe_table, lgdo.Table):
88
86
  lh5_tables.append(f"{tb}/{sub_table}")
89
87
  del maybe_table
@@ -114,7 +112,7 @@ def lh5_buffer_processor(
114
112
 
115
113
  # Write everything in the raw file to the new file, check for proc_spec under either the group name, out_name, or the name
116
114
  for tb in lh5_tables:
117
- lgdo_obj, _ = raw_store.read_object(f"{tb}", lh5_file)
115
+ lgdo_obj, _ = raw_store.read(f"{tb}", lh5_file)
118
116
 
119
117
  # Find the out_name.
120
118
  # If the top level group has an lgdo table in it, then the out_name is group
@@ -198,6 +196,4 @@ def lh5_buffer_processor(
198
196
  pass
199
197
 
200
198
  # Write the (possibly processed) lgdo_obj to a file
201
- raw_store.write_object(
202
- lgdo_obj, out_name, lh5_file=proc_file_name, group=group_name
203
- )
199
+ raw_store.write(lgdo_obj, out_name, lh5_file=proc_file_name, group=group_name)
daq2lh5/build_raw.py CHANGED
@@ -6,10 +6,8 @@ import logging
6
6
  import os
7
7
  import time
8
8
 
9
- import hdf5plugin
10
- import lgdo
11
9
  import numpy as np
12
- from lgdo.lh5_store import DEFAULT_HDF5_COMPRESSION
10
+ from lgdo import lh5
13
11
  from tqdm.auto import tqdm
14
12
 
15
13
  from .compass.compass_streamer import CompassStreamer
@@ -28,7 +26,7 @@ def build_raw(
28
26
  n_max: int = np.inf,
29
27
  overwrite: bool = False,
30
28
  compass_config_file: str = None,
31
- hdf5_compression: str | dict | hdf5plugin.filters.Filter = DEFAULT_HDF5_COMPRESSION,
29
+ hdf5_settings: dict[str, ...] = None,
32
30
  **kwargs,
33
31
  ) -> None:
34
32
  """Convert data into LEGEND HDF5 raw-tier format.
@@ -77,12 +75,16 @@ def build_raw(
77
75
  json-shorthand for the output specification (see
78
76
  :mod:`.compass.compass_event_decoder`).
79
77
 
80
- hdf5_compression
81
- forwarded to :meth:`~.lgdo.lh5_store.LH5Store.write_object`.
78
+ hdf5_settings
79
+ keyword arguments (as a dict) forwarded to
80
+ :meth:`lgdo.lh5.store.LH5Store.write`.
82
81
 
83
82
  **kwargs
84
83
  sent to :class:`.RawBufferLibrary` generation as `kw_dict` argument.
85
84
  """
85
+ if hdf5_settings is None:
86
+ hdf5_settings = {}
87
+
86
88
  # convert any environment variables in in_stream so that we can check for readability
87
89
  in_stream = os.path.expandvars(in_stream)
88
90
  # later: fix if in_stream is not a file
@@ -222,8 +224,8 @@ def build_raw(
222
224
  os.remove(out_file_glob[0])
223
225
 
224
226
  # Write header data
225
- lh5_store = lgdo.LH5Store(keep_open=True)
226
- write_to_lh5_and_clear(header_data, lh5_store, hdf5_compression=hdf5_compression)
227
+ lh5_store = lh5.LH5Store(keep_open=True)
228
+ write_to_lh5_and_clear(header_data, lh5_store, **hdf5_settings)
227
229
 
228
230
  # Now loop through the data
229
231
  n_bytes_last = streamer.n_bytes_read
@@ -248,7 +250,7 @@ def build_raw(
248
250
  if log.getEffectiveLevel() <= logging.INFO and n_max < np.inf:
249
251
  progress_bar.update(n_read)
250
252
 
251
- write_to_lh5_and_clear(chunk_list, lh5_store, hdf5_compression=hdf5_compression)
253
+ write_to_lh5_and_clear(chunk_list, lh5_store, **hdf5_settings)
252
254
 
253
255
  if n_max <= 0:
254
256
  log.info(f"Wrote {n_max} rows, exiting...")
daq2lh5/data_decoder.py CHANGED
@@ -3,13 +3,10 @@ Base classes for decoding data into raw LGDO Tables or files
3
3
  """
4
4
  from __future__ import annotations
5
5
 
6
- from typing import Union
7
-
8
6
  import lgdo
9
7
  import numpy as np
10
- from lgdo import LH5Store
11
-
12
- LGDO = Union[lgdo.Scalar, lgdo.Struct, lgdo.Array, lgdo.VectorOfVectors]
8
+ from lgdo import LGDO
9
+ from lgdo.lh5 import LH5Store
13
10
 
14
11
 
15
12
  class DataDecoder:
@@ -18,37 +15,39 @@ class DataDecoder:
18
15
  Most decoders will repeatedly decode the same set of values from each
19
16
  packet. The values that get decoded need to be described by a dict stored
20
17
  in `self.decoded_values` that helps determine how to set up the buffers and
21
- write them to file as :class:`~.lgdo.LGDO`\ s. :class:`~.lgdo.table.Table`\ s
22
- are made whose columns correspond to the elements of `decoded_values`, and
23
- packet data gets pushed to the end of the table one row at a time.
18
+ write them to file as :class:`~lgdo.types.lgdo.LGDO`\ s.
19
+ :class:`~lgdo.types.table.Table`\ s are made whose columns correspond to
20
+ the elements of `decoded_values`, and packet data gets pushed to the end of
21
+ the table one row at a time.
24
22
 
25
23
  Any key-value entry in a configuration dictionary attached to an element
26
24
  of `decoded_values` is typically interpreted as an attribute to be attached
27
25
  to the corresponding LGDO. This feature can be for example exploited to
28
- specify the data compression algorithm used by
29
- :meth:`~.lgdo.lh5_store.LH5Store.write_object` to write LGDOs to disk.
26
+ specify HDF5 dataset settings used by
27
+ :meth:`~lgdo.lh5.store.LH5Store.write` to write LGDOs to disk.
30
28
 
31
29
  For example ::
32
30
 
33
31
  from lgdo.compression import RadwareSigcompress
34
32
 
35
33
  FCEventDecoder.decoded_values = {
36
- "packet_id": {"dtype": "uint32", "compression": "gzip"},
34
+ "packet_id": {"dtype": "uint32", "hdf5_settings": {"compression": "gzip"}},
37
35
  # ...
38
36
  "waveform": {
39
37
  "dtype": "uint16",
40
38
  "datatype": "waveform",
41
39
  # ...
42
40
  "compression": {"values": RadwareSigcompress(codec_shift=-32768)},
41
+ "hdf5_settings": {"t0": {"compression": "lzf", shuffle: True}},
43
42
  }
44
43
  }
45
44
 
46
- LGDOs corresponding to ``packet_id`` and ``waveform`` will have their
47
- `compression` attribute set as ``"gzip"`` and
48
- ``RadwareSigcompress(codec_shift=-32768)``, respectively. Before being
49
- written to disk, they will compressed with the HDF5 built-in Gzip filter
50
- and with the :class:`~.lgdo.compression.radware.RadwareSigcompress`
51
- waveform compressor.
45
+ The LGDO corresponding to ``packet_id`` will have its `hdf5_settings`
46
+ attribute set as ``{"compression": "gzip"}``, while ``waveform.values``
47
+ will have its `compression` attribute set to
48
+ ``RadwareSigcompress(codec_shift=-32768)``. Before being written to disk,
49
+ they will be compressed with the HDF5 built-in Gzip filter and with the
50
+ :class:`~lgdo.compression.radware.RadwareSigcompress` waveform compressor.
52
51
 
53
52
  Examples
54
53
  --------
@@ -118,7 +117,7 @@ class DataDecoder:
118
117
  """Make an LGDO for this :class:`DataDecoder` to fill.
119
118
 
120
119
  This default version of this function allocates a
121
- :class:`~.lgdo.table.Table` using the `decoded_values` for key. If a
120
+ :class:`~lgdo.types.table.Table` using the `decoded_values` for key. If a
122
121
  different type of LGDO object is required for this decoder, overload
123
122
  this function.
124
123
 
@@ -178,7 +177,10 @@ class DataDecoder:
178
177
  dt = attrs.pop("dt")
179
178
  dt_units = attrs.pop("dt_units")
180
179
  wf_len = attrs.pop("wf_len")
181
- compression = attrs.pop("compression", None)
180
+ settings = {
181
+ "compression": attrs.pop("compression", {}),
182
+ "hdf5_settings": attrs.pop("hdf5_settings", {}),
183
+ }
182
184
 
183
185
  wf_table = lgdo.WaveformTable(
184
186
  size=size,
@@ -190,24 +192,20 @@ class DataDecoder:
190
192
  dtype=dtype,
191
193
  attrs=attrs,
192
194
  )
193
- if compression is not None:
194
- if not isinstance(compression, dict):
195
- raise RuntimeError(
196
- "waveform/compression attribute must be a dictionary"
197
- )
198
-
199
- if "values" in compression:
200
- wf_table.values.attrs["compression"] = compression["values"]
201
- if "t0" in compression:
202
- wf_table.t0.attrs["compression"] = compression["t0"]
203
- if "dt" in compression:
204
- wf_table.dt.attrs["compression"] = compression["dt"]
195
+
196
+ # attach compression/hdf5_settings to sub-fields
197
+ for el in ["values", "t0", "dt"]:
198
+ for settings_name in ("hdf5_settings", "compression"):
199
+ if el in settings[settings_name]:
200
+ wf_table[el].attrs[settings_name] = settings[settings_name][
201
+ el
202
+ ]
205
203
 
206
204
  data_obj.add_field(field, wf_table)
207
205
  continue
208
206
 
209
207
  # Parse datatype for remaining lgdos
210
- datatype, shape, elements = lgdo.lgdo_utils.parse_datatype(datatype)
208
+ datatype, shape, elements = lgdo.lh5.utils.parse_datatype(datatype)
211
209
 
212
210
  # ArrayOfEqualSizedArrays
213
211
  if datatype == "array_of_equalsized_arrays":
@@ -258,7 +256,7 @@ class DataDecoder:
258
256
  n_rows = self.garbage_table.loc
259
257
  if n_rows == 0:
260
258
  return
261
- lh5_store.write_object(
259
+ lh5_store.write(
262
260
  self.garbage_table, "garbage", filename, group, n_rows=n_rows, append=True
263
261
  )
264
262
  self.garbage_table.clear()
@@ -28,7 +28,7 @@ class FCConfigDecoder(DataDecoder):
28
28
  >>> decoder = FCConfigDecoder()
29
29
  >>> config = decoder.decode_config(fc)
30
30
  >>> type(config)
31
- lgdo.struct.Struct
31
+ lgdo.types.struct.Struct
32
32
  """
33
33
 
34
34
  def __init__(self, *args, **kwargs) -> None:
@@ -98,11 +98,11 @@ class ORSIS3302DecoderForEnergy(OrcaDecoder):
98
98
  sys.exit()
99
99
  self.decoded_values[ccc]["waveform"]["wf_len"] = trace_length
100
100
 
101
- def get_key_lists(self) -> list[list[str]]:
101
+ def get_key_lists(self) -> list[list[int]]:
102
102
  key_lists = []
103
103
  for key in self.decoded_values.keys():
104
104
  key_lists.append([key])
105
- return [key_lists]
105
+ return key_lists
106
106
 
107
107
  def get_decoded_values(self, key: int = None) -> dict[str, Any]:
108
108
  if key is None:
@@ -326,6 +326,7 @@ class ORFlashCamListenerStatusDecoder(OrcaDecoder):
326
326
  def decode_packet(
327
327
  self, packet: OrcaPacket, packet_id: int, rbl: RawBufferLibrary
328
328
  ) -> bool:
329
+ return False # FIXME: skip decoding until pyfcutils is updated
329
330
  """Decode the ORCA FlashCam Status packet."""
330
331
  # aliases for brevity
331
332
  if len(rbl) != 1:
@@ -47,11 +47,9 @@ def hex_dump(
47
47
  as_short: bool = False,
48
48
  id_dict: dict = None,
49
49
  use_logging: bool = True,
50
+ return_output=False,
50
51
  ) -> None:
51
- dump_cmd = print # noqa: T202
52
- if use_logging:
53
- dump_cmd = log.debug
54
-
52
+ output = []
55
53
  data_id = get_data_id(packet, shift=shift_data_id)
56
54
  n_words = get_n_words(packet)
57
55
  if id_dict is not None:
@@ -62,9 +60,9 @@ def hex_dump(
62
60
  else:
63
61
  heading = f"data ID = {data_id}"
64
62
  if print_n_words:
65
- dump_cmd(f"{heading}: {n_words} words")
63
+ output.append(f"{heading}: {n_words} words")
66
64
  else:
67
- dump_cmd(f"{heading}:")
65
+ output.append(f"{heading}:")
68
66
  n_to_print = int(np.minimum(n_words, max_words))
69
67
  pad = int(np.ceil(np.log10(n_to_print)))
70
68
  for i in range(n_to_print):
@@ -76,4 +74,13 @@ def hex_dump(
76
74
  line += f" {packet[i]}"
77
75
  if as_short:
78
76
  line += f" {np.frombuffer(packet[i:i+1].tobytes(), dtype='uint16')}"
79
- dump_cmd(line)
77
+ output.append(line)
78
+
79
+ dump_cmd = print # noqa: T202
80
+ if use_logging:
81
+ dump_cmd = log.debug
82
+ for line in output:
83
+ dump_cmd(line)
84
+
85
+ if return_output:
86
+ return output
@@ -32,30 +32,138 @@ class OrcaStreamer(DataStreamer):
32
32
  def __init__(self) -> None:
33
33
  super().__init__()
34
34
  self.in_stream = None
35
+ self.packet_locs = []
35
36
  self.buffer = np.empty(1024, dtype="uint32") # start with a 4 kB packet buffer
36
37
  self.header = None
37
38
  self.header_decoder = OrcaHeaderDecoder()
38
39
  self.decoder_id_dict = {} # dict of data_id to decoder object
39
40
  self.rbl_id_dict = {} # dict of RawBufferLists for each data_id
41
+ self.missing_decoders = []
42
+
43
+ def load_packet_header(self) -> np.uint32 | None:
44
+ """Loads the packet header at the current read location into the buffer
45
+
46
+ and updates internal variables.
47
+ """
48
+ pkt_hdr = self.buffer[:1]
49
+ n_bytes_read = self.in_stream.readinto(pkt_hdr) # buffer is at least 4 kB long
50
+ self.n_bytes_read += n_bytes_read
51
+ if n_bytes_read == 0: # EOF
52
+ return None
53
+ if n_bytes_read != 4:
54
+ raise RuntimeError(f"only got {n_bytes_read} bytes for packet header")
55
+
56
+ # packet is valid. Can set the packet_id and log its location
57
+ self.packet_id += 1
58
+ filepos = self.in_stream.tell() - n_bytes_read
59
+ if self.packet_id < len(self.packet_locs):
60
+ if self.packet_locs[self.packet_id] != filepos:
61
+ raise RuntimeError(
62
+ f"filepos for packet {self.packet_id} was {filepos} but {self.packet_locs[self.packet_id]} was expected"
63
+ )
64
+ else:
65
+ if len(self.packet_locs) != self.packet_id:
66
+ raise RuntimeError(
67
+ f"loaded packet {self.packet_id} after packet {len(self.packet_locs)-1}"
68
+ )
69
+ self.packet_locs.append(filepos)
70
+
71
+ return pkt_hdr
72
+
73
+ def skip_packet(self, n: int = 1) -> bool:
74
+ """Skip a packets without loading it into the internal buffer.
75
+
76
+ Requires loading the header. Optionally skips n packets.
77
+
78
+ Returns
79
+ ----------
80
+ succeeded
81
+ returns False if reached EOF, otherwise returns true
82
+ """
83
+ if self.in_stream is None:
84
+ raise RuntimeError("self.in_stream is None")
85
+ if not int(n) >= 0:
86
+ raise ValueError(f"n must be a non-negative int, can't be {n}")
87
+ n = int(n)
88
+ while n > 0:
89
+ pkt_hdr = self.load_packet_header()
90
+ if pkt_hdr is None:
91
+ return False
92
+ self.in_stream.seek((orca_packet.get_n_words(pkt_hdr) - 1) * 4, 1)
93
+ n -= 1
94
+ return True
95
+
96
+ def build_packet_locs(self, saveloc=True) -> None:
97
+ loc = self.in_stream.tell()
98
+ pid = self.packet_id
99
+ if len(self.packet_locs) > 0:
100
+ self.in_stream.seek(self.packet_locs[-1])
101
+ self.packet_id = len(self.packet_locs) - 2
102
+ while self.skip_packet():
103
+ pass # builds the rest of the packet_locs list
104
+ if saveloc:
105
+ self.in_stream.seek(loc)
106
+ self.packet_id = pid
107
+
108
+ def count_packets(self, saveloc=True) -> None:
109
+ self.build_packet_locs(saveloc=saveloc)
110
+ return len(self.packet_locs)
40
111
 
41
112
  # TODO: need to correct for endianness?
42
- def load_packet(self, skip_unknown_ids: bool = False) -> np.uint32 | None:
113
+ def load_packet(
114
+ self, index: int = None, whence: int = 0, skip_unknown_ids: bool = False
115
+ ) -> np.uint32 | None:
43
116
  """Loads the next packet into the internal buffer.
44
117
 
45
118
  Returns packet as a :class:`numpy.uint32` view of the buffer (a slice),
46
119
  returns ``None`` at EOF.
120
+
121
+ Parameters
122
+ ----------
123
+ index
124
+ Optionally give an index of packet to skip to, relative to the
125
+ "whence" location. Can be positive or negative. If out-of-range for
126
+ the file, None will be returned.
127
+ whence
128
+ used when an index is supplied. Follows the file.seek() convention:
129
+ whence = 0 (default) means index is relative to the beginning of the
130
+ file; whence = 1 means index is relative to the current position in
131
+ the file; whence = 2 means relative to the end of the file.
132
+
133
+ Returns
134
+ ----------
135
+ packet
136
+ a view of the internal buffer spanning the packet data (uint32
137
+ ndarray). If you want to hold on to the packet data while you load
138
+ more packets, you can call copy() on the view to make a copy.
47
139
  """
48
140
  if self.in_stream is None:
49
141
  raise RuntimeError("self.in_stream is None")
50
142
 
51
- # read packet header
52
- pkt_hdr = self.buffer[:1]
53
- n_bytes_read = self.in_stream.readinto(pkt_hdr) # buffer is at least 4 kB long
54
- self.n_bytes_read += n_bytes_read
55
- if n_bytes_read == 0:
143
+ if index is not None:
144
+ if whence not in [0, 1, 2]:
145
+ raise ValueError(f"whence can't be {whence}")
146
+ index = int(index)
147
+ # convert whence 1 or 2 to whence = 0
148
+ if whence == 1: # index is relative to current position
149
+ index += self.packet_id - 1
150
+ elif whence == 2: # index is relative to end of file
151
+ self.build_packet_locs(saveloc=False)
152
+ index += len(self.packet_locs) - 2
153
+ if index < 0:
154
+ self.in_stream.seek(0)
155
+ self.packet_id = -1
156
+ return None
157
+ while index >= len(self.packet_locs):
158
+ if not self.skip_packet():
159
+ return None
160
+ self.in_stream.seek(self.packet_locs[index])
161
+ self.packet_id = index - 1
162
+
163
+ # load packet header
164
+ pkt_hdr = self.load_packet_header()
165
+ if pkt_hdr is None:
56
166
  return None
57
- if n_bytes_read != 4:
58
- raise RuntimeError(f"only got {n_bytes_read} bytes for packet header")
59
167
 
60
168
  # if it's a short packet, we are done
61
169
  if orca_packet.is_short(pkt_hdr):
@@ -69,7 +177,6 @@ class OrcaStreamer(DataStreamer):
69
177
  not in self.decoder_id_dict
70
178
  ):
71
179
  self.in_stream.seek((n_words - 1) * 4, 1)
72
- self.n_bytes_read += (n_words - 1) * 4 # well, we didn't really read it...
73
180
  return pkt_hdr
74
181
 
75
182
  # load into buffer, resizing as necessary
@@ -204,15 +311,17 @@ class OrcaStreamer(DataStreamer):
204
311
  """
205
312
 
206
313
  self.set_in_stream(stream_name)
314
+ self.packet_id = -1
207
315
 
208
316
  # read in the header
209
317
  packet = self.load_packet()
318
+ if packet is None:
319
+ raise RuntimeError(f"no orca data in file {stream_name}")
210
320
  if orca_packet.get_data_id(packet) != 0:
211
321
  raise RuntimeError(
212
322
  f"got data id {orca_packet.get_data_id(packet)} for header"
213
323
  )
214
324
 
215
- self.packet_id = 0
216
325
  self.any_full |= self.header_decoder.decode_packet(packet, self.packet_id)
217
326
  self.header = self.header_decoder.header
218
327
 
@@ -240,9 +349,7 @@ class OrcaStreamer(DataStreamer):
240
349
  name = id_to_dec_name_dict[data_id]
241
350
  if name not in instantiated_decoders:
242
351
  if name not in globals():
243
- log.warning(
244
- f"no implementation of {name}, corresponding packets will be skipped"
245
- )
352
+ self.missing_decoders.append(data_id)
246
353
  continue
247
354
  decoder = globals()[name]
248
355
  instantiated_decoders[name] = decoder(header=self.header)
@@ -296,13 +403,18 @@ class OrcaStreamer(DataStreamer):
296
403
  packet = self.load_packet(skip_unknown_ids=True)
297
404
  if packet is None:
298
405
  return False
299
- self.packet_id += 1
300
406
 
301
407
  # look up the data id, decoder, and rbl
302
408
  data_id = orca_packet.get_data_id(packet, shift=False)
303
409
  log.debug(
304
410
  f"packet {self.packet_id}: data_id = {data_id}, decoder = {'None' if data_id not in self.decoder_id_dict else type(self.decoder_id_dict[data_id]).__name__}"
305
411
  )
412
+ if data_id in self.missing_decoders:
413
+ name = self.header.get_id_to_decoder_name_dict(shift_data_id=False)[
414
+ data_id
415
+ ]
416
+ log.warning(f"no implementation of {name}, packets were skipped")
417
+ continue
306
418
  if data_id in self.rbl_id_dict:
307
419
  break
308
420
 
daq2lh5/raw_buffer.py CHANGED
@@ -65,21 +65,19 @@ keys.
65
65
  from __future__ import annotations
66
66
 
67
67
  import os
68
- from typing import Union
69
68
 
70
69
  import lgdo
71
- from lgdo import LH5Store
70
+ from lgdo import LGDO
71
+ from lgdo.lh5 import LH5Store
72
72
 
73
73
  from .buffer_processor.buffer_processor import buffer_processor
74
74
 
75
- LGDO = Union[lgdo.Scalar, lgdo.Struct, lgdo.Array, lgdo.VectorOfVectors]
76
-
77
75
 
78
76
  class RawBuffer:
79
77
  r"""Base class to represent a buffer of raw data.
80
78
 
81
79
  A :class:`RawBuffer` is in essence a an LGDO object (typically a
82
- :class:`~.lgdo.table.Table`) to which decoded data will be written, along
80
+ :class:`~lgdo.types.table.Table`) to which decoded data will be written, along
83
81
  with some meta-data distinguishing what data goes into it, and where the
84
82
  LGDO gets written out. Also holds on to the current location in the buffer
85
83
  for writing.
@@ -88,7 +86,7 @@ class RawBuffer:
88
86
  ----------
89
87
  lgdo
90
88
  the LGDO used as the actual buffer. Typically a
91
- :class:`~.lgdo.table.Table`. Set to ``None`` upon creation so that the
89
+ :class:`~lgdo.types.table.Table`. Set to ``None`` upon creation so that the
92
90
  user or a decoder can initialize it later.
93
91
  key_list
94
92
  a list of keys (e.g. channel numbers) identifying data to be written
@@ -107,7 +105,7 @@ class RawBuffer:
107
105
  proc_spec
108
106
  a dictionary containing the following:
109
107
  - a DSP config file, passed as a dictionary, or as a path to a JSON file
110
- - an array containing: the name of an :class:`~.lgdo` object stored in the :class:`.RawBuffer` to be sliced,
108
+ - an array containing: the name of an LGDO object stored in the :class:`.RawBuffer` to be sliced,
111
109
  the start and end indices of the slice, and the new name for the sliced object
112
110
  - a dictionary of fields to drop
113
111
  - a dictionary of new fields and their return datatype
@@ -440,11 +438,11 @@ def write_to_lh5_and_clear(
440
438
  files (saves some time opening / closing files).
441
439
  **kwargs
442
440
  keyword-arguments forwarded to
443
- :meth:`.lgdo.lh5_store.LH5Store.write_object`.
441
+ :meth:`lgdo.lh5.store.LH5Store.write`.
444
442
 
445
443
  See Also
446
444
  --------
447
- .lgdo.lh5_store.LH5Store.write_object
445
+ lgdo.lh5.store.LH5Store.write
448
446
  """
449
447
  if lh5_store is None:
450
448
  lh5_store = lgdo.LH5Store()
@@ -470,7 +468,7 @@ def write_to_lh5_and_clear(
470
468
 
471
469
  # write if requested...
472
470
  if filename != "":
473
- lh5_store.write_object(
471
+ lh5_store.write(
474
472
  lgdo_to_write,
475
473
  rb.out_name,
476
474
  filename,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
- Name: legend-daq2lh5
3
- Version: 1.0.2
2
+ Name: legend_daq2lh5
3
+ Version: 1.2.0a1
4
4
  Summary: Convert digitizer data to LH5
5
5
  Home-page: https://github.com/legend-exp/legend-daq2lh5
6
6
  Author: Jason Detwiler
@@ -26,10 +26,10 @@ Classifier: Topic :: Software Development
26
26
  Requires-Python: >=3.9
27
27
  Description-Content-Type: text/markdown
28
28
  License-File: LICENSE
29
- Requires-Dist: dspeed ~=1.1
29
+ Requires-Dist: dspeed >=1.3.0a4
30
30
  Requires-Dist: h5py >=3.2.0
31
31
  Requires-Dist: hdf5plugin
32
- Requires-Dist: legend-pydataobj ~=1.1
32
+ Requires-Dist: legend-pydataobj >=1.5.0a1
33
33
  Requires-Dist: numpy >=1.21
34
34
  Requires-Dist: pyfcutils
35
35
  Requires-Dist: tqdm >=4.27
@@ -60,3 +60,16 @@ Requires-Dist: pytest-cov ; extra == 'test'
60
60
  ![GitHub pull requests](https://img.shields.io/github/issues-pr/legend-exp/legend-daq2lh5?logo=github)
61
61
  ![License](https://img.shields.io/github/license/legend-exp/legend-daq2lh5)
62
62
  [![Read the Docs](https://img.shields.io/readthedocs/legend-daq2lh5?logo=readthedocs)](https://legend-daq2lh5.readthedocs.io)
63
+
64
+ JSON-configurable conversion of digitized data into
65
+ [LEGEND HDF5](https://legend-exp.github.io/legend-data-format-specs/dev/hdf5/),
66
+ with optional data pre-processing via [dspeed](https://dspeed.readthedocs.io)
67
+ and data compression via [legend-pydataobj](https://legend-pydataobj.readthedocs.io).
68
+
69
+ Currently supported DAQ data formats:
70
+ * [FlashCam](https://www.mizzi-computer.de/home)
71
+ * [CoMPASS](https://www.caen.it/products/compass)
72
+ * [ORCA](https://github.com/unc-enap/Orca), reading out:
73
+ - FlashCam
74
+ - [Struck SIS3302](https://www.struck.de/sis3302.htm)
75
+ - [Struck SIS3316](https://www.struck.de/sis3316.html)
@@ -1,36 +1,36 @@
1
1
  daq2lh5/__init__.py,sha256=VPmwKuZSA0icpce05ojhnsKWhR4_QUgD0oVXUoN9wks,975
2
- daq2lh5/_version.py,sha256=37405taMQ5GWqqWhBXb1DerL4bDz062TZx0QfP2a0bw,274
3
- daq2lh5/build_raw.py,sha256=6uMuDp2N9Rjqh3JCt2oHNFMMWlrQuiYozAvHx-si6oI,10585
2
+ daq2lh5/_version.py,sha256=k5PS9p0a5Ey36DDxagN4mnTZow7bHSa0Oh_ycx0FrX4,413
3
+ daq2lh5/build_raw.py,sha256=JFXC5ln9u353TUZMksY3zydLiV2HlxqdI6_Y2_ZMCIE,10524
4
4
  daq2lh5/cli.py,sha256=HCZ9Vyg-gqvairN9zJIpBjw5vLpp9ZUOOQYLFxloLL8,2912
5
- daq2lh5/data_decoder.py,sha256=hl4kebdH22K3hImvWFeNf6C43YC03DCK7HlWCdwHhhw,10670
5
+ daq2lh5/data_decoder.py,sha256=ka2WIJuPvsG892__HCW1SagCEzyiZJ2kQP6zGDMtlr0,10641
6
6
  daq2lh5/data_streamer.py,sha256=6SEAekOHyfC4k3E0df0lW37ap6ZemVFbH8PYMl6UvCU,14130
7
7
  daq2lh5/logging.py,sha256=Nu3wgIoWN7cyUxuzPom5rMwFvTlBu8p8d9uONHDquRg,965
8
- daq2lh5/raw_buffer.py,sha256=yVLUYhxLe6KOjwxq_k91MAekx3ZUYmWL32GAhu3ahls,17784
8
+ daq2lh5/raw_buffer.py,sha256=dyPUok0N3MP41oP9F8sO_PrH7-SWs9UdPh7dqCF729g,17687
9
9
  daq2lh5/buffer_processor/__init__.py,sha256=7k6v_KPximtv7805QnX4-xp_S3vqvqwDfdV3q95oZJo,84
10
- daq2lh5/buffer_processor/buffer_processor.py,sha256=Ka4NbYOBCYdqyg3C0VZEJRbCVcjPEX9xlRbclhQjJVw,13758
11
- daq2lh5/buffer_processor/lh5_buffer_processor.py,sha256=Jf-lVM6t9Ui-6-0fr9tXkMsX6ButHZB7I0gyJcRQqw8,8323
10
+ daq2lh5/buffer_processor/buffer_processor.py,sha256=GUxpNDbqGLuUEZmXjeratipbzmki12RFNYZkxgMtesg,14483
11
+ daq2lh5/buffer_processor/lh5_buffer_processor.py,sha256=yL1ru0_GTsZx099oi45sXL-FxPfdChtStd_IFtZNI_Q,8222
12
12
  daq2lh5/compass/__init__.py,sha256=mOXHWp7kRDgNTPQty3E8k2KPSy_vAzjneKfAcCVaPyE,132
13
13
  daq2lh5/compass/compass_config_parser.py,sha256=zeAsOo1dOJPGLL8-zkAcdYRkqt8BodtOPi96n7fWsl4,12300
14
14
  daq2lh5/compass/compass_event_decoder.py,sha256=kiPOaEu8SgLD2wbSPbBahcbTBBRAIw35wtVLBcwPcXY,7386
15
15
  daq2lh5/compass/compass_header_decoder.py,sha256=AA-Md2FIT3nD4mXX9CrWvbbfmKiA436-BTmzcU3_XOY,2823
16
16
  daq2lh5/compass/compass_streamer.py,sha256=zSl7IqO0ID0wcixkLE9QVEG3bF9hfGVITVPomCeOFTM,8841
17
17
  daq2lh5/fc/__init__.py,sha256=bB1j6r-bDmylNi0iutQeAJGjsDSjLSoXMqFfXWwfb8I,141
18
- daq2lh5/fc/fc_config_decoder.py,sha256=6PA4AGxfoI9S22lfoMFgb4L_tMloT1TF7HWi2r7OJaM,1990
18
+ daq2lh5/fc/fc_config_decoder.py,sha256=RLRfUOZN0vYbAprqTymP7TGg641IiP9rgCGIOwWVKzU,1996
19
19
  daq2lh5/fc/fc_event_decoder.py,sha256=JIRsySnxeuY3wmxjJOrTXo6wpelVup8WIvxU-fkPL-A,8131
20
20
  daq2lh5/fc/fc_status_decoder.py,sha256=o_3vTAgYXelZxIsreCYioVYid2mY-wqloYKlxoCqX5Q,3390
21
21
  daq2lh5/fc/fc_streamer.py,sha256=S0imXdVsiyolPvxI1uiBngpC58DporSNZPqx1HeVi5o,5737
22
22
  daq2lh5/orca/__init__.py,sha256=Xf6uOIOzk_QkKH_7VizGlCo3iuiAgLtUE3A07x_HXC0,175
23
23
  daq2lh5/orca/orca_base.py,sha256=-XIolXsHj-1EdewaGxyvJTZvRGZsDyZe-5PzVOd-LFY,1333
24
- daq2lh5/orca/orca_digitizers.py,sha256=rpk2SSDQgE681FB_iaewAuTXCVEqhUTlhLAm0RGJDfo,20869
25
- daq2lh5/orca/orca_flashcam.py,sha256=2zJUkuhR7MsZ14stW3VI67DE2UIwN6LSZ80K0_7EpuI,33016
24
+ daq2lh5/orca/orca_digitizers.py,sha256=BsAA3OgQ13YIirDM8pd_xDY3F5FqEY4YjSHviflmov8,20867
25
+ daq2lh5/orca/orca_flashcam.py,sha256=gsvPorUXk1Jn-U93GsxXJ5z6pbTK2yjsYDqZFVCm57U,33088
26
26
  daq2lh5/orca/orca_header.py,sha256=1tDRG8l9Gqu4c0K4BjXBSC5eiLTzY_HaCsgNBiv5EgI,4283
27
27
  daq2lh5/orca/orca_header_decoder.py,sha256=ORIIyfx22ybyKc-uyWy5ER49-dl3BGpHdfV8OCDmjIw,1632
28
- daq2lh5/orca/orca_packet.py,sha256=FCB-toCjqQUwJmcYRDjnHoxo8m_xra9jr6rEfuawnh0,2340
28
+ daq2lh5/orca/orca_packet.py,sha256=TcdfuYN8_gcug_Xdjz98KqjHw1MqJ4J98zc7WI2xtf4,2488
29
29
  daq2lh5/orca/orca_run_decoder.py,sha256=3atKXC6mDi8_PK6ICUBBJ-LyaTM8OU31kKWIpmttRr4,2065
30
- daq2lh5/orca/orca_streamer.py,sha256=lU2ZkxKyxgk650Zdw9aanFthW93P-FIzraPmlv-nwIA,11307
31
- legend_daq2lh5-1.0.2.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
32
- legend_daq2lh5-1.0.2.dist-info/METADATA,sha256=OPBk1lZxJjOJby-YlH72YWABmuCKDQygRh4ItS5VzJw,3127
33
- legend_daq2lh5-1.0.2.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
34
- legend_daq2lh5-1.0.2.dist-info/entry_points.txt,sha256=R08R4NrHi0ab5MJN_qKqzePVzrLSsw5WpmbiwwduYjw,59
35
- legend_daq2lh5-1.0.2.dist-info/top_level.txt,sha256=MJQVLyLqMgMKBdVfNXFaCKCjHKakAs19VLbC9ctXZ7A,8
36
- legend_daq2lh5-1.0.2.dist-info/RECORD,,
30
+ daq2lh5/orca/orca_streamer.py,sha256=VbD9PF-rx_Rk-rEy7XECPmgxr6kZSUf0tC7Qbol3Qeg,15693
31
+ legend_daq2lh5-1.2.0a1.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
32
+ legend_daq2lh5-1.2.0a1.dist-info/METADATA,sha256=QiBKAO0ycatdNK5W8HlhHXA28pUnqFr2iPnjyjr2RAE,3755
33
+ legend_daq2lh5-1.2.0a1.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
34
+ legend_daq2lh5-1.2.0a1.dist-info/entry_points.txt,sha256=R08R4NrHi0ab5MJN_qKqzePVzrLSsw5WpmbiwwduYjw,59
35
+ legend_daq2lh5-1.2.0a1.dist-info/top_level.txt,sha256=MJQVLyLqMgMKBdVfNXFaCKCjHKakAs19VLbC9ctXZ7A,8
36
+ legend_daq2lh5-1.2.0a1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.41.2)
2
+ Generator: bdist_wheel (0.42.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5