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.
@@ -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
- # put decoded values here where they can be used also by the orca decoder
15
- fc_decoded_values = {
16
- # packet index in file
17
- "packet_id": {"dtype": "uint32"},
18
- # index of event
19
- "eventnumber": {"dtype": "int32"},
20
- # time since epoch
21
- "timestamp": {"dtype": "float64", "units": "s"},
22
- # time since beginning of file
23
- "runtime": {"dtype": "float64", "units": "s"},
24
- # number of triggered adc channels
25
- "numtraces": {"dtype": "int32"},
26
- # list of triggered adc channels
27
- "tracelist": {
28
- "dtype": "int16",
29
- "datatype": "array<1>{array<1>{real}}", # vector of vectors
30
- "length_guess": 16,
31
- },
32
- # fpga baseline
33
- "baseline": {"dtype": "uint16"},
34
- # fpga energy
35
- "daqenergy": {"dtype": "uint16"},
36
- # right now, index of the trigger (trace)
37
- "channel": {"dtype": "uint32"},
38
- # PPS timestamp in sec
39
- "ts_pps": {"dtype": "int32"},
40
- # clock ticks
41
- "ts_ticks": {"dtype": "int32"},
42
- # max clock ticks
43
- "ts_maxticks": {"dtype": "int32"},
44
- # the offset in sec between the master and unix
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
- # these are read for every event (decode_event)
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
- def set_file_config(self, fc_config: lgdo.Struct) -> None:
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
- def get_key_lists(self) -> range:
114
- return [list(range(self.fc_config["nadcs"].value))]
78
+ self.key_list = []
79
+ n_traces = len(fcio_stream.config.tracemap)
80
+ self.max_rows_in_packet = n_traces
115
81
 
116
- def get_decoded_values(self, channel: int = None) -> dict[str, dict[str, Any]]:
117
- # FC uses the same values for all channels
118
- return self.decoded_values
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.fc_config["nadcs"].value
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: fcutils.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 channel number.
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
- n_bytes
147
- (estimated) number of bytes in the packet that was just decoded.
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 iwf in fcio.tracelist:
153
- if iwf not in evt_rbkd:
154
- if iwf not in self.skipped_channels:
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(f"skipping packets from channel {iwf}...")
157
- self.skipped_channels[iwf] = 0
158
- self.skipped_channels[iwf] += 1
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
- tbl = evt_rbkd[iwf].lgdo
161
- if fcio.nsamples != tbl["waveform"]["values"].nda.shape[1]:
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.nsamples,
195
+ fcio.config.eventsamples,
165
196
  "when",
166
197
  self.decoded_values["waveform"]["wf_len"],
167
198
  "were expected",
168
199
  )
169
- ii = evt_rbkd[iwf].loc
170
-
171
- # fill the table
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()