legend-daq2lh5 1.5.0__py3-none-any.whl → 1.6.1__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 +2 -2
- daq2lh5/buffer_processor/buffer_processor.py +8 -5
- daq2lh5/buffer_processor/lh5_buffer_processor.py +8 -5
- daq2lh5/build_raw.py +4 -3
- daq2lh5/data_decoder.py +1 -3
- daq2lh5/data_streamer.py +73 -28
- daq2lh5/fc/fc_config_decoder.py +99 -28
- daq2lh5/fc/fc_event_decoder.py +127 -137
- daq2lh5/fc/fc_eventheader_decoder.py +311 -0
- daq2lh5/fc/fc_fsp_decoder.py +885 -0
- daq2lh5/fc/fc_status_decoder.py +231 -63
- daq2lh5/fc/fc_streamer.py +153 -76
- daq2lh5/llama/llama_header_decoder.py +1 -2
- daq2lh5/orca/orca_digitizers.py +1 -1
- daq2lh5/orca/orca_fcio.py +405 -0
- daq2lh5/orca/orca_flashcam.py +7 -4
- daq2lh5/orca/orca_header.py +7 -2
- daq2lh5/orca/orca_packet.py +10 -0
- daq2lh5/orca/orca_run_decoder.py +6 -6
- daq2lh5/orca/orca_streamer.py +28 -10
- daq2lh5/orca/skim_orca_file.py +54 -0
- daq2lh5/raw_buffer.py +15 -2
- {legend_daq2lh5-1.5.0.dist-info → legend_daq2lh5-1.6.1.dist-info}/METADATA +13 -14
- legend_daq2lh5-1.6.1.dist-info/RECORD +46 -0
- {legend_daq2lh5-1.5.0.dist-info → legend_daq2lh5-1.6.1.dist-info}/WHEEL +1 -1
- legend_daq2lh5-1.6.1.dist-info/entry_points.txt +3 -0
- legend_daq2lh5-1.5.0.dist-info/RECORD +0 -42
- legend_daq2lh5-1.5.0.dist-info/entry_points.txt +0 -2
- {legend_daq2lh5-1.5.0.dist-info → legend_daq2lh5-1.6.1.dist-info}/licenses/LICENSE +0 -0
- {legend_daq2lh5-1.5.0.dist-info → legend_daq2lh5-1.6.1.dist-info}/top_level.txt +0 -0
daq2lh5/fc/fc_event_decoder.py
CHANGED
@@ -4,79 +4,45 @@ import copy
|
|
4
4
|
import logging
|
5
5
|
from typing import Any
|
6
6
|
|
7
|
-
import fcutils
|
8
7
|
import lgdo
|
8
|
+
from fcio import FCIO, Limits
|
9
9
|
|
10
10
|
from ..data_decoder import DataDecoder
|
11
|
+
from .fc_eventheader_decoder import fc_eventheader_decoded_values, get_key
|
11
12
|
|
12
13
|
log = logging.getLogger(__name__)
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
"
|
20
|
-
|
21
|
-
"
|
22
|
-
|
23
|
-
"
|
24
|
-
|
25
|
-
"
|
26
|
-
|
27
|
-
"
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
"
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
"
|
40
|
-
|
41
|
-
"
|
42
|
-
#
|
43
|
-
"
|
44
|
-
|
45
|
-
"mu_offset_sec": {"dtype": "int32"},
|
46
|
-
# the offset in usec between master and unix
|
47
|
-
"mu_offset_usec": {"dtype": "int32"},
|
48
|
-
# the calculated sec which must be added to the master
|
49
|
-
"to_master_sec": {"dtype": "int32"},
|
50
|
-
# the delta time between master and unix in usec
|
51
|
-
"delta_mu_usec": {"dtype": "int32"},
|
52
|
-
# the abs(time) between master and unix in usec
|
53
|
-
"abs_delta_mu_usec": {"dtype": "int32"},
|
54
|
-
# startsec
|
55
|
-
"to_start_sec": {"dtype": "int32"},
|
56
|
-
# startusec
|
57
|
-
"to_start_usec": {"dtype": "int32"},
|
58
|
-
# start pps of the next dead window
|
59
|
-
"dr_start_pps": {"dtype": "int32"},
|
60
|
-
# start ticks of the next dead window
|
61
|
-
"dr_start_ticks": {"dtype": "int32"},
|
62
|
-
# stop pps of the next dead window
|
63
|
-
"dr_stop_pps": {"dtype": "int32"},
|
64
|
-
# stop ticks of the next dead window
|
65
|
-
"dr_stop_ticks": {"dtype": "int32"},
|
66
|
-
# maxticks of the dead window
|
67
|
-
"dr_maxticks": {"dtype": "int32"},
|
68
|
-
# current dead time calculated from deadregion (dr) fields.
|
69
|
-
# Give the total dead time if summed up.
|
70
|
-
"deadtime": {"dtype": "float64"},
|
71
|
-
# waveform data
|
72
|
-
"waveform": {
|
73
|
-
"dtype": "uint16",
|
74
|
-
"datatype": "waveform",
|
75
|
-
"wf_len": 65532, # max value. override this before initializing buffers to save RAM
|
76
|
-
"dt": 16, # override if a different clock rate is used
|
77
|
-
"dt_units": "ns",
|
78
|
-
"t0_units": "ns",
|
79
|
-
},
|
15
|
+
fc_event_decoded_values = copy.deepcopy(fc_eventheader_decoded_values)
|
16
|
+
|
17
|
+
# The event decoding splits all arrays in the header into scalars to support decoding per key
|
18
|
+
for key in [
|
19
|
+
"board_id",
|
20
|
+
"fc_input",
|
21
|
+
"runtime",
|
22
|
+
"lifetime",
|
23
|
+
"deadtime",
|
24
|
+
"deadinterval_nsec",
|
25
|
+
"baseline",
|
26
|
+
"daqenergy",
|
27
|
+
]:
|
28
|
+
fc_event_decoded_values[key].pop("datatype")
|
29
|
+
fc_event_decoded_values[key].pop("length_guess")
|
30
|
+
|
31
|
+
# tracelist contains contains the mapping for array indices to channel indices
|
32
|
+
fc_event_decoded_values.pop("tracelist")
|
33
|
+
fc_event_decoded_values["channel"] = {
|
34
|
+
"dtype": "uint16",
|
35
|
+
"description": "The index of each read out channel in this stream.",
|
36
|
+
}
|
37
|
+
|
38
|
+
# Event always carries a waveform
|
39
|
+
fc_event_decoded_values["waveform"] = {
|
40
|
+
"dtype": "uint16",
|
41
|
+
"datatype": "waveform",
|
42
|
+
"wf_len": Limits.MaxSamples, # max value. override this before initializing buffers to save RAM
|
43
|
+
"dt": 16, # override if a different clock rate is used
|
44
|
+
"dt_units": "ns",
|
45
|
+
"t0_units": "ns",
|
80
46
|
}
|
81
47
|
"""Default FlashCam Event decoded values.
|
82
48
|
|
@@ -92,13 +58,15 @@ class FCEventDecoder(DataDecoder):
|
|
92
58
|
"""Decode FlashCam digitizer event data."""
|
93
59
|
|
94
60
|
def __init__(self, *args, **kwargs) -> None:
|
95
|
-
|
96
|
-
self.decoded_values = copy.deepcopy(fc_decoded_values)
|
61
|
+
self.decoded_values = copy.deepcopy(fc_event_decoded_values)
|
97
62
|
super().__init__(*args, **kwargs)
|
98
63
|
self.skipped_channels = {}
|
99
|
-
self.fc_config = None
|
100
64
|
|
101
|
-
|
65
|
+
# lookup table for the key list, populated in `set_fcio_stream`.
|
66
|
+
self.key_list = []
|
67
|
+
self.max_rows_in_packet = 0
|
68
|
+
|
69
|
+
def set_fcio_stream(self, fcio_stream: FCIO) -> None:
|
102
70
|
"""Access ``FCIOConfig`` members once when each file is opened.
|
103
71
|
|
104
72
|
Parameters
|
@@ -106,23 +74,35 @@ class FCEventDecoder(DataDecoder):
|
|
106
74
|
fc_config
|
107
75
|
extracted via :meth:`~.fc_config_decoder.FCConfigDecoder.decode_config`.
|
108
76
|
"""
|
109
|
-
self.fc_config = fc_config
|
110
|
-
self.decoded_values["waveform"]["wf_len"] = self.fc_config["nsamples"].value
|
111
|
-
self.decoded_values["tracelist"]["length_guess"] = self.fc_config["nadcs"].value
|
112
77
|
|
113
|
-
|
114
|
-
|
78
|
+
self.key_list = []
|
79
|
+
n_traces = len(fcio_stream.config.tracemap)
|
80
|
+
self.max_rows_in_packet = n_traces
|
115
81
|
|
116
|
-
|
117
|
-
|
118
|
-
|
82
|
+
self.decoded_values["waveform"]["wf_len"] = fcio_stream.config.eventsamples
|
83
|
+
self.decoded_values["waveform"]["dt"] = fcio_stream.config.sampling_period_ns
|
84
|
+
|
85
|
+
for trace_info in fcio_stream.config.tracemap:
|
86
|
+
key = get_key(
|
87
|
+
fcio_stream.config.streamid, trace_info >> 16, trace_info & 0xFFFF
|
88
|
+
)
|
89
|
+
self.key_list.append(key)
|
90
|
+
log.debug(
|
91
|
+
f"set_file_config: n_traces {n_traces} max_rows {self.max_rows_in_packet} key_list {len(self.key_list)}"
|
92
|
+
)
|
119
93
|
|
120
94
|
def get_max_rows_in_packet(self) -> int:
|
121
|
-
return self.
|
95
|
+
return self.max_rows_in_packet
|
96
|
+
|
97
|
+
def get_key_lists(self) -> list[list[int | str]]:
|
98
|
+
return [copy.deepcopy(self.key_list)]
|
99
|
+
|
100
|
+
def get_decoded_values(self, key: int | str = None) -> dict[str, dict[str, Any]]:
|
101
|
+
return self.decoded_values
|
122
102
|
|
123
103
|
def decode_packet(
|
124
104
|
self,
|
125
|
-
fcio:
|
105
|
+
fcio: FCIO,
|
126
106
|
evt_rbkd: lgdo.Table | dict[int, lgdo.Table],
|
127
107
|
packet_id: int,
|
128
108
|
) -> bool:
|
@@ -136,79 +116,89 @@ class FCEventDecoder(DataDecoder):
|
|
136
116
|
be read out.
|
137
117
|
evt_rbkd
|
138
118
|
A single table for reading out all data, or a dictionary of tables
|
139
|
-
keyed by
|
119
|
+
keyed by rawid.
|
140
120
|
packet_id
|
141
121
|
The index of the packet in the `fcio` stream. Incremented by
|
142
122
|
:class:`~.fc.fc_streamer.FCStreamer`.
|
143
123
|
|
144
124
|
Returns
|
145
125
|
-------
|
146
|
-
|
147
|
-
|
126
|
+
any_full
|
127
|
+
TODO
|
148
128
|
"""
|
149
129
|
any_full = False
|
150
130
|
|
151
131
|
# a list of channels is read out simultaneously for each event
|
152
|
-
for
|
153
|
-
|
154
|
-
|
132
|
+
for ii, (trace_idx, addr, chan) in enumerate(
|
133
|
+
zip(fcio.event.trace_list, fcio.event.card_address, fcio.event.card_channel)
|
134
|
+
):
|
135
|
+
key = get_key(fcio.config.streamid, addr, chan)
|
136
|
+
if key not in evt_rbkd:
|
137
|
+
if key not in self.skipped_channels:
|
155
138
|
# TODO: should this be a warning instead?
|
156
|
-
log.debug(
|
157
|
-
|
158
|
-
|
139
|
+
log.debug(
|
140
|
+
f"skipping packets from channel {trace_idx} index {ii} / key {key}..."
|
141
|
+
)
|
142
|
+
self.skipped_channels[key] = 0
|
143
|
+
self.skipped_channels[key] += 1
|
159
144
|
continue
|
160
|
-
|
161
|
-
|
145
|
+
|
146
|
+
tbl = evt_rbkd[key].lgdo
|
147
|
+
loc = evt_rbkd[key].loc
|
148
|
+
|
149
|
+
tbl["channel"].nda[loc] = trace_idx
|
150
|
+
tbl["packet_id"].nda[loc] = packet_id
|
151
|
+
tbl["fcid"].nda[loc] = fcio.config.streamid & 0xFFFF
|
152
|
+
tbl["board_id"].nda[loc] = fcio.event.card_address[ii]
|
153
|
+
tbl["fc_input"].nda[loc] = fcio.event.card_channel[ii]
|
154
|
+
tbl["event_type"].nda[loc] = fcio.event.type
|
155
|
+
tbl["eventnumber"].nda[loc] = fcio.event.timestamp[0]
|
156
|
+
tbl["numtraces"].nda[loc] = fcio.event.num_traces
|
157
|
+
tbl["ts_pps"].nda[loc] = fcio.event.timestamp[1]
|
158
|
+
tbl["ts_ticks"].nda[loc] = fcio.event.timestamp[2]
|
159
|
+
tbl["ts_maxticks"].nda[loc] = fcio.event.timestamp[3]
|
160
|
+
tbl["mu_offset_sec"].nda[loc] = fcio.event.timeoffset[0]
|
161
|
+
tbl["mu_offset_usec"].nda[loc] = fcio.event.timeoffset[1]
|
162
|
+
tbl["to_master_sec"].nda[loc] = fcio.event.timeoffset[2]
|
163
|
+
tbl["delta_mu_usec"].nda[loc] = fcio.event.timeoffset[3]
|
164
|
+
tbl["abs_delta_mu_usec"].nda[loc] = fcio.event.timeoffset[4]
|
165
|
+
tbl["to_start_sec"].nda[loc] = fcio.event.timeoffset[5]
|
166
|
+
tbl["to_start_usec"].nda[loc] = fcio.event.timeoffset[6]
|
167
|
+
tbl["dr_start_pps"].nda[loc] = fcio.event.deadregion[0]
|
168
|
+
tbl["dr_start_ticks"].nda[loc] = fcio.event.deadregion[1]
|
169
|
+
tbl["dr_stop_pps"].nda[loc] = fcio.event.deadregion[2]
|
170
|
+
tbl["dr_stop_ticks"].nda[loc] = fcio.event.deadregion[3]
|
171
|
+
tbl["dr_maxticks"].nda[loc] = fcio.event.deadregion[4]
|
172
|
+
# if event_type == 11: provides the same check
|
173
|
+
# however, the usage of deadregion[5]/[6] must never change
|
174
|
+
# and it will always be present if deadregion[7..] is ever used
|
175
|
+
if fcio.event.deadregion_size >= 7:
|
176
|
+
tbl["dr_ch_idx"].nda[loc] = fcio.event.deadregion[5]
|
177
|
+
tbl["dr_ch_len"].nda[loc] = fcio.event.deadregion[6]
|
178
|
+
else:
|
179
|
+
tbl["dr_ch_idx"].nda[loc] = 0
|
180
|
+
tbl["dr_ch_len"].nda[loc] = fcio.config.adcs
|
181
|
+
|
182
|
+
# The following values are calculated values by fcio-py
|
183
|
+
tbl["timestamp"].nda[loc] = fcio.event.unix_time_utc_sec
|
184
|
+
tbl["deadinterval_nsec"].nda[loc] = fcio.event.dead_interval_nsec[ii]
|
185
|
+
tbl["deadtime"].nda[loc] = fcio.event.dead_time_sec[ii]
|
186
|
+
tbl["lifetime"].nda[loc] = fcio.event.life_time_sec[ii]
|
187
|
+
tbl["runtime"].nda[loc] = fcio.event.run_time_sec[ii]
|
188
|
+
tbl["baseline"].nda[loc] = fcio.event.fpga_baseline[ii]
|
189
|
+
tbl["daqenergy"].nda[loc] = fcio.event.fpga_energy[ii]
|
190
|
+
|
191
|
+
tbl["waveform"]["values"].nda[loc][:] = fcio.event.trace[ii]
|
192
|
+
if fcio.config.eventsamples != tbl["waveform"]["values"].nda.shape[1]:
|
162
193
|
log.warning(
|
163
194
|
"event wf length was",
|
164
|
-
fcio.
|
195
|
+
fcio.config.eventsamples,
|
165
196
|
"when",
|
166
197
|
self.decoded_values["waveform"]["wf_len"],
|
167
198
|
"were expected",
|
168
199
|
)
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
tbl["channel"].nda[ii] = iwf
|
173
|
-
tbl["packet_id"].nda[ii] = packet_id
|
174
|
-
tbl["eventnumber"].nda[
|
175
|
-
ii
|
176
|
-
] = fcio.eventnumber # the eventnumber since the beginning of the file
|
177
|
-
tbl["timestamp"].nda[ii] = fcio.eventtime # the time since epoch in seconds
|
178
|
-
tbl["runtime"].nda[
|
179
|
-
ii
|
180
|
-
] = fcio.runtime # the time since the beginning of the file in seconds
|
181
|
-
tbl["numtraces"].nda[ii] = fcio.numtraces # number of triggered adcs
|
182
|
-
tbl["tracelist"]._set_vector_unsafe(
|
183
|
-
ii, fcio.tracelist
|
184
|
-
) # list of triggered adcs
|
185
|
-
tbl["baseline"].nda[ii] = fcio.baseline[
|
186
|
-
iwf
|
187
|
-
] # the fpga baseline values for each channel in LSB
|
188
|
-
tbl["daqenergy"].nda[ii] = fcio.daqenergy[
|
189
|
-
iwf
|
190
|
-
] # the fpga energy values for each channel in LSB
|
191
|
-
tbl["ts_pps"].nda[ii] = fcio.timestamp_pps
|
192
|
-
tbl["ts_ticks"].nda[ii] = fcio.timestamp_ticks
|
193
|
-
tbl["ts_maxticks"].nda[ii] = fcio.timestamp_maxticks
|
194
|
-
tbl["mu_offset_sec"].nda[ii] = fcio.timeoffset_mu_sec
|
195
|
-
tbl["mu_offset_usec"].nda[ii] = fcio.timeoffset_mu_usec
|
196
|
-
tbl["to_master_sec"].nda[ii] = fcio.timeoffset_master_sec
|
197
|
-
tbl["delta_mu_usec"].nda[ii] = fcio.timeoffset_dt_mu_usec
|
198
|
-
tbl["abs_delta_mu_usec"].nda[ii] = fcio.timeoffset_abs_mu_usec
|
199
|
-
tbl["to_start_sec"].nda[ii] = fcio.timeoffset_start_sec
|
200
|
-
tbl["to_start_usec"].nda[ii] = fcio.timeoffset_start_usec
|
201
|
-
tbl["dr_start_pps"].nda[ii] = fcio.deadregion_start_pps
|
202
|
-
tbl["dr_start_ticks"].nda[ii] = fcio.deadregion_start_ticks
|
203
|
-
tbl["dr_stop_pps"].nda[ii] = fcio.deadregion_stop_pps
|
204
|
-
tbl["dr_stop_ticks"].nda[ii] = fcio.deadregion_stop_ticks
|
205
|
-
tbl["dr_maxticks"].nda[ii] = fcio.deadregion_maxticks
|
206
|
-
tbl["deadtime"].nda[ii] = fcio.deadtime
|
207
|
-
|
208
|
-
# if len(traces[iwf]) != fcio.nsamples: # number of sample per trace check
|
209
|
-
tbl["waveform"]["values"].nda[ii][:] = fcio.traces[iwf]
|
210
|
-
|
211
|
-
evt_rbkd[iwf].loc += 1
|
212
|
-
any_full |= evt_rbkd[iwf].is_full()
|
200
|
+
|
201
|
+
evt_rbkd[key].loc += 1
|
202
|
+
any_full |= evt_rbkd[key].is_full()
|
213
203
|
|
214
204
|
return bool(any_full)
|
@@ -0,0 +1,311 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import copy
|
4
|
+
import logging
|
5
|
+
from typing import Any
|
6
|
+
|
7
|
+
import lgdo
|
8
|
+
from fcio import FCIO, Limits
|
9
|
+
|
10
|
+
from ..data_decoder import DataDecoder
|
11
|
+
|
12
|
+
log = logging.getLogger(__name__)
|
13
|
+
|
14
|
+
|
15
|
+
def get_key(streamid: int, card_address: int, card_input: int, iwf: int = -1) -> int:
|
16
|
+
if streamid > 0 or iwf < 0:
|
17
|
+
# For backwards compatibility only the lower 16-bit of the streamid are
|
18
|
+
# used.
|
19
|
+
return (streamid & 0xFFFF) * 1000000 + card_address * 100 + card_input
|
20
|
+
else:
|
21
|
+
return iwf
|
22
|
+
|
23
|
+
|
24
|
+
def get_fcid(key: int) -> int:
|
25
|
+
return int(key // 1000000)
|
26
|
+
|
27
|
+
|
28
|
+
def get_card_address(key: int) -> int:
|
29
|
+
return int((key // 100) % 10000)
|
30
|
+
|
31
|
+
|
32
|
+
def get_card_input(key: int) -> int:
|
33
|
+
return int(key % 100)
|
34
|
+
|
35
|
+
|
36
|
+
# put decoded values here where they can be used also by the orca decoder
|
37
|
+
fc_eventheader_decoded_values = {
|
38
|
+
"packet_id": {
|
39
|
+
"dtype": "uint32",
|
40
|
+
"description": "The index of this decoded packet in the file.",
|
41
|
+
},
|
42
|
+
"fcid": {"dtype": "uint16", "description": "The ID of this data stream."},
|
43
|
+
"board_id": {
|
44
|
+
"dtype": "uint16",
|
45
|
+
"datatype": "array<1>{array<1>{real}}",
|
46
|
+
"length_guess": Limits.MaxChannels,
|
47
|
+
"description": "The MAC address of the FlashCam board which recorded this event.",
|
48
|
+
},
|
49
|
+
"fc_input": {
|
50
|
+
"dtype": "uint16",
|
51
|
+
"datatype": "array<1>{array<1>{real}}",
|
52
|
+
"length_guess": Limits.MaxChannels,
|
53
|
+
"description": "The per-board ADC channel. Corresponds to the input connector in 62.5MHz mode or the input wire in 250MHz mode.",
|
54
|
+
},
|
55
|
+
"eventnumber": {
|
56
|
+
"dtype": "int32",
|
57
|
+
"description": "Counts triggered events. Fetched from the master board in non-sparse, or from the ADC board in sparse.",
|
58
|
+
},
|
59
|
+
"event_type": {
|
60
|
+
"dtype": "int32",
|
61
|
+
"description": "Is 1 for shared trigger/readout, or 11 in true sparse mode where ADC boards trigger independently.",
|
62
|
+
},
|
63
|
+
"timestamp": {
|
64
|
+
"dtype": "float64",
|
65
|
+
"units": "s",
|
66
|
+
"description": "Time since epoch (unix time) for this event.",
|
67
|
+
},
|
68
|
+
"runtime": {
|
69
|
+
"dtype": "float64",
|
70
|
+
"datatype": "array<1>{array<1>{real}}",
|
71
|
+
"length_guess": Limits.MaxChannels,
|
72
|
+
"units": "s",
|
73
|
+
"description": "Time since trigger enable of this run. Recorded per channel.",
|
74
|
+
},
|
75
|
+
"lifetime": {
|
76
|
+
"dtype": "float64",
|
77
|
+
"datatype": "array<1>{array<1>{real}}",
|
78
|
+
"length_guess": Limits.MaxChannels,
|
79
|
+
"units": "s",
|
80
|
+
"description": "The accumulated lifetime since trigger enable, for which the hardware was not dead. Recorded per channel.",
|
81
|
+
},
|
82
|
+
"deadtime": {
|
83
|
+
"dtype": "float64",
|
84
|
+
"datatype": "array<1>{array<1>{real}}",
|
85
|
+
"length_guess": Limits.MaxChannels,
|
86
|
+
"units": "s",
|
87
|
+
"description": "The accumulated time for which the hardware buffers were full (dead). Recorded per channel.",
|
88
|
+
},
|
89
|
+
"deadinterval_nsec": {
|
90
|
+
"dtype": "int64",
|
91
|
+
"datatype": "array<1>{array<1>{real}}",
|
92
|
+
"length_guess": Limits.MaxChannels,
|
93
|
+
"units": "ns",
|
94
|
+
"description": "The interval the board was dead between previous event and this event in nanoseconds with 4ns precision (due to the internal clock speed of 250MHz).",
|
95
|
+
},
|
96
|
+
"numtraces": {"dtype": "int32"},
|
97
|
+
"tracelist": {
|
98
|
+
"dtype": "uint16",
|
99
|
+
"datatype": "array<1>{array<1>{real}}",
|
100
|
+
"length_guess": Limits.MaxChannels,
|
101
|
+
"description": "The number of read out adc channels in this event.",
|
102
|
+
},
|
103
|
+
"baseline": {
|
104
|
+
"dtype": "uint16",
|
105
|
+
"datatype": "array<1>{array<1>{real}}",
|
106
|
+
"length_guess": Limits.MaxChannels,
|
107
|
+
"description": "The baseline determined by the FPGA baseline algorithm.",
|
108
|
+
},
|
109
|
+
"daqenergy": {
|
110
|
+
"dtype": "uint16",
|
111
|
+
"datatype": "array<1>{array<1>{real}}",
|
112
|
+
"length_guess": Limits.MaxChannels,
|
113
|
+
"description": "The shaped pulse peak height above the trigger threshold for 62.5MHz mode. The waveform integral over `sumlength` samples in 250MHz mode.",
|
114
|
+
},
|
115
|
+
"ts_pps": {
|
116
|
+
"dtype": "int32",
|
117
|
+
"description": "The pulse-per-second (PPS) counter since DAQ reset.",
|
118
|
+
},
|
119
|
+
"ts_ticks": {
|
120
|
+
"dtype": "int32",
|
121
|
+
"description": "The clock ticks counter (250MHz) since last PPS.",
|
122
|
+
},
|
123
|
+
"ts_maxticks": {
|
124
|
+
"dtype": "int32",
|
125
|
+
"description": "The clock ticks counter value when the last PPS arrived.",
|
126
|
+
},
|
127
|
+
"mu_offset_sec": {
|
128
|
+
"dtype": "int32",
|
129
|
+
"description": "The seconds between server (unix) and fpga time. See `man 2 gettimeofday`.",
|
130
|
+
},
|
131
|
+
"mu_offset_usec": {
|
132
|
+
"dtype": "int32",
|
133
|
+
"description": "The microseconds between server (unix second) and fpga time (pps). See `man 2 gettimeofday`",
|
134
|
+
},
|
135
|
+
"to_master_sec": {
|
136
|
+
"dtype": "int32",
|
137
|
+
"description": "The timeoffset between server (unix) and fpga clocks, only valid when a gps clock/time server is used and the offset to epoch needs to be adjusted.",
|
138
|
+
},
|
139
|
+
"delta_mu_usec": {
|
140
|
+
"dtype": "int32",
|
141
|
+
"description": "The clock offset in microseconds between fpga and server for aligned clocks.",
|
142
|
+
},
|
143
|
+
"abs_delta_mu_usec": {
|
144
|
+
"dtype": "int32",
|
145
|
+
"description": "The absolute value of `delta_mu_usec`.",
|
146
|
+
},
|
147
|
+
"to_start_sec": {
|
148
|
+
"dtype": "int32",
|
149
|
+
"description": "The seconds of the start time of the run / trigger enable.",
|
150
|
+
},
|
151
|
+
"to_start_usec": {
|
152
|
+
"dtype": "int32",
|
153
|
+
"description": "The microseconds of the start time of the run / trigger enable.",
|
154
|
+
},
|
155
|
+
"dr_start_pps": {
|
156
|
+
"dtype": "int32",
|
157
|
+
"description": "The PPS counter value when the hardware buffers were full (dead), before this event. Only changes if a new value is acquired.",
|
158
|
+
},
|
159
|
+
"dr_start_ticks": {
|
160
|
+
"dtype": "int32",
|
161
|
+
"description": "The ticks counter value when the hardware buffers were full (dead), before this event. Only changes if a new value is acquired.",
|
162
|
+
},
|
163
|
+
"dr_stop_pps": {
|
164
|
+
"dtype": "int32",
|
165
|
+
"description": "The PPS counter value when the hardware buffers are free (life) again, before this event. Only changes if a new value is acquired.",
|
166
|
+
},
|
167
|
+
"dr_stop_ticks": {
|
168
|
+
"dtype": "int32",
|
169
|
+
"description": "The ticks counter value when the hardware buffers are free (life) again, before this event. Only changes if a new value is acquired.",
|
170
|
+
},
|
171
|
+
"dr_maxticks": {
|
172
|
+
"dtype": "int32",
|
173
|
+
"description": "The ticks counter value on PPS when the hardware buffers were freed, before this event. Only changes if a new value is acquired.",
|
174
|
+
},
|
175
|
+
"dr_ch_idx": {
|
176
|
+
"dtype": "uint16",
|
177
|
+
"description": "The start channel index of channels which are affected by the recorded dead interval.",
|
178
|
+
},
|
179
|
+
"dr_ch_len": {
|
180
|
+
"dtype": "uint16",
|
181
|
+
"description": "The number of channels which are affected by the dead interval, starts from `dr_ch_idx`. Can be larger than `num_traces`.",
|
182
|
+
},
|
183
|
+
}
|
184
|
+
|
185
|
+
|
186
|
+
class FCEventHeaderDecoder(DataDecoder):
|
187
|
+
def __init__(self, *args, **kwargs) -> None:
|
188
|
+
self.decoded_values = copy.deepcopy(fc_eventheader_decoded_values)
|
189
|
+
super().__init__(*args, **kwargs)
|
190
|
+
self.max_rows_in_packet = (
|
191
|
+
1 # only == 1 if len(array) in table is counted as one.
|
192
|
+
)
|
193
|
+
self.key_list = []
|
194
|
+
|
195
|
+
def set_fcio_stream(self, fcio_stream: FCIO) -> None:
|
196
|
+
"""Access ``FCIOConfig`` members once when each file is opened.
|
197
|
+
|
198
|
+
Parameters
|
199
|
+
----------
|
200
|
+
fc_config
|
201
|
+
extracted via :meth:`~.fc_config_decoder.FCConfigDecoder.decode_config`.
|
202
|
+
"""
|
203
|
+
n_traces = len(fcio_stream.config.tracemap)
|
204
|
+
|
205
|
+
self.decoded_values["tracelist"]["length_guess"] = n_traces
|
206
|
+
self.decoded_values["board_id"]["length_guess"] = n_traces
|
207
|
+
self.decoded_values["fc_input"]["length_guess"] = n_traces
|
208
|
+
self.decoded_values["deadinterval_nsec"]["length_guess"] = n_traces
|
209
|
+
self.decoded_values["deadtime"]["length_guess"] = n_traces
|
210
|
+
self.decoded_values["runtime"]["length_guess"] = n_traces
|
211
|
+
self.decoded_values["lifetime"]["length_guess"] = n_traces
|
212
|
+
self.decoded_values["baseline"]["length_guess"] = n_traces
|
213
|
+
self.decoded_values["daqenergy"]["length_guess"] = n_traces
|
214
|
+
|
215
|
+
self.key_list = [get_key(fcio_stream.config.streamid, 0, 0)]
|
216
|
+
|
217
|
+
def get_key_lists(self) -> list[list[int | str]]:
|
218
|
+
return [copy.deepcopy(self.key_list)]
|
219
|
+
|
220
|
+
def get_decoded_values(self, key: int | str = None) -> dict[str, dict[str, Any]]:
|
221
|
+
# FC uses the same values for all channels
|
222
|
+
return self.decoded_values
|
223
|
+
|
224
|
+
def get_max_rows_in_packet(self) -> int:
|
225
|
+
return self.max_rows_in_packet
|
226
|
+
|
227
|
+
def decode_packet(
|
228
|
+
self,
|
229
|
+
fcio: FCIO,
|
230
|
+
evt_hdr_rbkd: dict[int, lgdo.Table],
|
231
|
+
packet_id: int,
|
232
|
+
) -> bool:
|
233
|
+
|
234
|
+
# only one key available: this streamid
|
235
|
+
key = get_key(fcio.config.streamid, 0, 0)
|
236
|
+
if key not in evt_hdr_rbkd:
|
237
|
+
return False
|
238
|
+
|
239
|
+
evt_hdr_rb = evt_hdr_rbkd[key]
|
240
|
+
|
241
|
+
tbl = evt_hdr_rb.lgdo
|
242
|
+
loc = evt_hdr_rb.loc
|
243
|
+
|
244
|
+
tbl["packet_id"].nda[loc] = packet_id
|
245
|
+
tbl["fcid"].nda[loc] = fcio.config.streamid & 0xFFFF
|
246
|
+
tbl["event_type"].nda[loc] = fcio.event.type
|
247
|
+
tbl["eventnumber"].nda[loc] = fcio.event.timestamp[0]
|
248
|
+
tbl["ts_pps"].nda[loc] = fcio.event.timestamp[1]
|
249
|
+
tbl["ts_ticks"].nda[loc] = fcio.event.timestamp[2]
|
250
|
+
tbl["ts_maxticks"].nda[loc] = fcio.event.timestamp[3]
|
251
|
+
tbl["mu_offset_sec"].nda[loc] = fcio.event.timeoffset[0]
|
252
|
+
tbl["mu_offset_usec"].nda[loc] = fcio.event.timeoffset[1]
|
253
|
+
tbl["to_master_sec"].nda[loc] = fcio.event.timeoffset[2]
|
254
|
+
tbl["delta_mu_usec"].nda[loc] = fcio.event.timeoffset[3]
|
255
|
+
tbl["abs_delta_mu_usec"].nda[loc] = fcio.event.timeoffset[4]
|
256
|
+
tbl["to_start_sec"].nda[loc] = fcio.event.timeoffset[5]
|
257
|
+
tbl["to_start_usec"].nda[loc] = fcio.event.timeoffset[6]
|
258
|
+
tbl["dr_start_pps"].nda[loc] = fcio.event.deadregion[0]
|
259
|
+
tbl["dr_start_ticks"].nda[loc] = fcio.event.deadregion[1]
|
260
|
+
tbl["dr_stop_pps"].nda[loc] = fcio.event.deadregion[2]
|
261
|
+
tbl["dr_stop_ticks"].nda[loc] = fcio.event.deadregion[3]
|
262
|
+
tbl["dr_maxticks"].nda[loc] = fcio.event.deadregion[4]
|
263
|
+
# the dead-time affected channels
|
264
|
+
if fcio.event.deadregion_size >= 7:
|
265
|
+
tbl["dr_ch_idx"].nda[loc] = fcio.event.deadregion[5]
|
266
|
+
tbl["dr_ch_len"].nda[loc] = fcio.event.deadregion[6]
|
267
|
+
else:
|
268
|
+
tbl["dr_ch_idx"].nda[loc] = 0
|
269
|
+
tbl["dr_ch_len"].nda[loc] = fcio.config.adcs
|
270
|
+
|
271
|
+
# The following values are derived values by fcio-py
|
272
|
+
# the time since epoch in seconds
|
273
|
+
tbl["timestamp"].nda[loc] = fcio.event.unix_time_utc_sec
|
274
|
+
|
275
|
+
tbl["numtraces"].nda[loc] = fcio.event.num_traces
|
276
|
+
|
277
|
+
start = 0 if loc == 0 else tbl["tracelist"].cumulative_length[loc - 1]
|
278
|
+
end = start + fcio.event.num_traces
|
279
|
+
|
280
|
+
tbl["tracelist"].flattened_data.nda[start:end] = fcio.event.trace_list
|
281
|
+
tbl["tracelist"].cumulative_length[loc] = end
|
282
|
+
|
283
|
+
tbl["board_id"].flattened_data.nda[start:end] = fcio.event.card_address
|
284
|
+
tbl["board_id"].cumulative_length[loc] = end
|
285
|
+
|
286
|
+
tbl["fc_input"].flattened_data.nda[start:end] = fcio.event.card_channel
|
287
|
+
tbl["fc_input"].cumulative_length[loc] = end
|
288
|
+
|
289
|
+
tbl["deadinterval_nsec"].flattened_data.nda[
|
290
|
+
start:end
|
291
|
+
] = fcio.event.dead_interval_nsec
|
292
|
+
tbl["deadinterval_nsec"].cumulative_length[loc] = end
|
293
|
+
|
294
|
+
tbl["deadtime"].flattened_data.nda[start:end] = fcio.event.dead_time_sec
|
295
|
+
tbl["deadtime"].cumulative_length[loc] = end
|
296
|
+
|
297
|
+
tbl["lifetime"].flattened_data.nda[start:end] = fcio.event.life_time_sec
|
298
|
+
tbl["lifetime"].cumulative_length[loc] = end
|
299
|
+
|
300
|
+
tbl["runtime"].flattened_data.nda[start:end] = fcio.event.run_time_sec
|
301
|
+
tbl["runtime"].cumulative_length[loc] = end
|
302
|
+
|
303
|
+
tbl["baseline"].flattened_data.nda[start:end] = fcio.event.fpga_baseline
|
304
|
+
tbl["baseline"].cumulative_length[loc] = end
|
305
|
+
|
306
|
+
tbl["daqenergy"].flattened_data.nda[start:end] = fcio.event.fpga_energy
|
307
|
+
tbl["daqenergy"].cumulative_length[loc] = end
|
308
|
+
|
309
|
+
evt_hdr_rb.loc += 1
|
310
|
+
|
311
|
+
return evt_hdr_rb.is_full()
|