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

Sign up to get free protection for your applications and to get access to all the features.
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