Mantid
Loading...
Searching...
No Matches
LoadANSTOEventFile.h
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2020 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
7#pragma once
8
9#include <cstdio>
10#include <stdexcept>
11#include <stdint.h>
12
13namespace Mantid {
14namespace DataHandling {
15namespace ANSTO {
16
17// The function reads a binary ANSTO event file,
18// It opens the file and returns the data through the callbacks.
19
20constexpr int32_t EVENTFILEHEADER_BASE_MAGIC_NUMBER = 0x0DAE0DAE;
21constexpr int32_t EVENTFILEHEADER_BASE_FORMAT_NUMBER = 0x00010002;
22
23// all events contain some or all of these fields
24constexpr int32_t NVAL = 5; // x, y, v, w, wa
25
26#pragma pack(push, 1) // otherwise may get 8 byte aligned, no good for us
27
28struct EventFileHeader_Base { // total content should be 16*int (64 bytes)
29 int32_t magic_number; // must equal EVENTFILEHEADER_BASE_MAGIC_NUMBER (DAE data)
30 int32_t format_number; // must equal EVENTFILEHEADER_BASE_FORMAT_NUMBER,
31 // identifies this header format
32 // cppcheck-suppress unusedStructMember
33 int32_t anstohm_version; // ANSTOHM_VERSION server/filler version number that
34 // generated the file
35 int32_t pack_format; // typically 0 if packed binary, 1 if unpacked binary.
36 int32_t oob_enabled; // if set, OOB events can be present in the data,
37 // otherwise only neutron and t0 events are stored
38 int32_t clock_scale; // the CLOCK_SCALE setting, ns per timestamp unit
39 // cppcheck-suppress unusedStructMember
40 int32_t spares[16 - 6]; // spares (padding)
41};
42
43struct EventFileHeader_Packed { // total content should be 16*int (64 bytes)
44 int32_t evt_stg_nbits_x; // number of bits in x datum
45 int32_t evt_stg_nbits_y; // number of bits in y datum
46 int32_t evt_stg_nbits_v; // number of bits in v datum
47 int32_t evt_stg_nbits_w; // number of bits in w datum
48 int32_t evt_stg_nbits_wa; // number of bits in wa datum // MJL added 5/15 for
49 // format 0x00010002
50 int32_t evt_stg_xy_signed; // 0 if x and y are unsigned, 1 if x and y are
51 // signed ints
52 // cppcheck-suppress unusedStructMember
53 int32_t spares[16 - 6]; // spares (padding)
54};
55
56#pragma pack(pop)
57
58// event decoding state machine
60 // for all events
61 DECODE_START, // initial state - then DECODE_VAL_BITFIELDS (for neutron
62 // events) or DECODE_OOB_BYTE_1 (for OOB events) for OOB events
63 // only
66 // for all events
68 DECODE_DT // final state - then output data and return to DECODE_START
69};
70
71/*
72Types of OOB events, and 'NEUTRON' event. Not all are used for all
73instruments, or supported yet.
74
75NEUTRON = 0 = a neutron detected, FRAME_START = -2 = T0 pulse (e.g. from
76chopper, or from Doppler on Emu). For most instruments, these are the only
77types used.
78
79FRAME_AUX_START = -3 (e.g. from reflecting chopper on Emu), VETO = -6 (e.g.
80veto signal from ancillary)
81
82BEAM_MONITOR = -7 (e.g. if beam monitors connected direct to Mesytec MCPD8 DAE)
83
84RAW = -8 = pass-through, non-decoded raw event directly from the DAE (e.g.
85Mesytec MCPD8). Used to access special features of DAE.
86
87Other types are not used in general (DATASIZES = -1 TBD in future, FLUSH = -4
88deprecated, FRAME_DEASSERT = -5 only on Fastcomtec P7888 DAE).
89*/
90
91template <class IReader, class IEventHandler, class IProgress>
92void ReadEventFile(IReader &loader, IEventHandler &handler, IProgress &progress, int32_t def_clock_scale,
93 bool use_tx_chopper) {
94 // read file headers (base header then packed-format header)
95 EventFileHeader_Base hdr_base;
96 if (!loader.read(reinterpret_cast<char *>(&hdr_base), sizeof(hdr_base)))
97 throw std::runtime_error("unable to load EventFileHeader-Base");
98
99 EventFileHeader_Packed hdr_packed;
100 if (!loader.read(reinterpret_cast<char *>(&hdr_packed), sizeof(hdr_packed)))
101 throw std::runtime_error("unable to load EventFileHeader-Packed");
102
104 throw std::runtime_error("bad magic number");
105
107 char txtBuffer[255] = {};
108 snprintf(txtBuffer, sizeof(txtBuffer), "invalid file (only format_number=%08Xh or lower)",
110 throw std::runtime_error(txtBuffer);
111 }
112
113 if (hdr_base.pack_format != 0)
114 throw std::runtime_error("only packed binary format is supported");
115
116 if (hdr_base.clock_scale == 0)
117 throw std::runtime_error("clock scale cannot be zero");
118
119 // note: in the old format 0x00010001, the evt_stg_nbits_wa did not exist and
120 // it contained evt_stg_xy_signed
121 if (hdr_base.format_number <= 0x00010001) {
122 hdr_packed.evt_stg_xy_signed = hdr_packed.evt_stg_nbits_wa;
123 hdr_packed.evt_stg_nbits_wa = 0;
124 }
125
126 // Setup the clock_scale. In format 0x00010001 this was not part of the
127 // headers, hence a function argument is provided to allow it to be
128 // specified manually. In the current format 0x00010002, clock_scale is
129 // written to the header and need not be specified, unless some alternate
130 // scale is needed.
131 double scale_microsec = hdr_base.clock_scale / 1000.0;
132 if (!hdr_base.clock_scale) {
133 // old eventfile format did not have clock_scale...
134 scale_microsec = def_clock_scale / 1000.0;
135 }
136
137 // the initial time is not set correctly so wait until primary and auxillary
138 // time have been reset before sending events
139 int64_t primary_time = 0;
140 int64_t auxillary_time = 0;
141 bool primary_ok = false;
142 bool auxillary_ok = false;
143
144 // main loop
145 uint32_t x = 0, y = 0, v = 0, w = 0,
146 wa = 0; // storage for event data fields
147 uint32_t *ptr_val[NVAL] = {&x, &y, &v, &w, &wa}; // used to store data into fields
148
149 // All events are also timestamped. The differential timestamp dt stored in
150 // each event is summed to recover the event timestamp t. All timestamps are
151 // frame-relative, i.e. FRAME_START event represents T0 (e.g. from a chopper)
152 // and t is reset to 0. In OOB mode and for certain DAE types only (e.g.
153 // Mesytec MCPD8), the FRAME_START event is timestamped relative to the last
154 // FRAME_START. The timestamp t on the FRAME_START event is therefore the
155 // total frame duration, and this can be used to recover the absolute
156 // timestamp of all events in the DAQ, if desired (e.g. for accurate timing
157 // during long term kinematic experiments).
158 int32_t dt; // , t = 0 dt may be negative occasionally for some DAE types,
159 // therefore dt and t are signed ints.
160
161 int32_t nbits_val_oob[NVAL] = {};
162
163 int32_t nbits_val_neutron[NVAL] = {hdr_packed.evt_stg_nbits_x, hdr_packed.evt_stg_nbits_y, hdr_packed.evt_stg_nbits_v,
164 hdr_packed.evt_stg_nbits_w, hdr_packed.evt_stg_nbits_wa};
165
166 int32_t ind_val = 0;
167 int32_t nbits_val = 0;
168 int32_t nbits_val_filled = 0;
169 int32_t nbits_dt_filled = 0;
170
171 int32_t oob_en = hdr_base.oob_enabled; // will be 1 if we are reading a new OOB
172 // event file (format 0x00010002 only).
173 int32_t oob_event = 0,
174 c = 0; // For neutron events, oob_event = 0, and for OOB
175 // events, oob_event = 1 and c indicates the OOB
176 // event type. c<0 for all OOB events currently.
177
178 event_decode_state state = DECODE_START; // event decoding state machine
179 bool event_ended = false;
180
181 while (true) {
182
183 // read next byte
184 uint8_t ch;
185 if (!loader.read(reinterpret_cast<char *>(&ch), 1))
186 break;
187
188 int32_t nbits_ch_used = 0; // no bits used initially, 8 to go
189
190 // start of event processing
191 if (state == DECODE_START) {
192
193 // if OOB event mode is enabled, the leading Bit 0 of the first byte
194 // indicates whether the event is a neutron event or an OOB event
195 if (!oob_en)
196 state = DECODE_VAL_BITFIELDS;
197 else {
198 oob_event = (ch & 1);
199 nbits_ch_used = 1; // leading bit used as OOB bit
200
201 if (!oob_event)
202 state = DECODE_VAL_BITFIELDS;
203 else
204 state = DECODE_OOB_BYTE_1;
205 }
206
207 // setup to decode new event bitfields (for both neutron and OOB events)
208 for (ind_val = 0; ind_val < NVAL; ind_val++)
209 *ptr_val[ind_val] = 0;
210
211 ind_val = 0;
212 nbits_val_filled = 0;
213
214 dt = 0;
215 nbits_dt_filled = 0;
216 }
217
218 // state machine for event decoding
219 switch (state) {
220 case DECODE_START: // Should never get here
221 throw std::runtime_error("Failure in event decoding");
222 case DECODE_OOB_BYTE_1: // first OOB header byte
223 // OOB event Byte 1: Bit 0 = 1 = OOB event, Bit 1 =
224 // mode (only mode=0 suported currently), Bits 2-5 =
225 // c (OOB event type), Bits 6-7 = bitfieldsize_x
226 // / 8. bitfieldsize_x and following 2-bit
227 // bitfieldsizes are the number of bytes used to
228 // store the OOB parameter. All of x,y,v,w,wa are
229 // short integers (16 bits maximum) and so
230 // bitfieldsizes = 0, 1 or 2 only.
231 c = (ch >> 2) & 0xF; // Bits 2-5 = c
232
233 if (c & 0x8)
234 c |= 0xFFFFFFF0; // c is a signed parameter so sign extend - OOB events
235 // are negative values
236 nbits_val_oob[0] = (ch & 0xC0) >> 3; // Bits 6-7 * 8 = bitfieldsize_x
237
238 state = DECODE_OOB_BYTE_2; // Proceed to process second OOB event header
239 // byte next time
240 break;
241
242 case DECODE_OOB_BYTE_2: // second OOB header byte
243 // bitfieldsizes for y, v, w and wa, as for
244 // bitfieldsize_x above.
245 nbits_val_oob[1] = (ch & 0x03) << 3; // Bits 0-1 * 8 = bitfieldsize_y
246 nbits_val_oob[2] = (ch & 0x0C) << 1; // Bits 2-3 * 8 = bitfieldsize_v
247 nbits_val_oob[3] = (ch & 0x30) >> 1; // Bits 4-5 * 8 = bitfieldsize_w
248 nbits_val_oob[4] = (ch & 0xC0) >> 3; // Bits 6-7 * 8 = bitfieldsize_wa
249
250 state = DECODE_VAL_BITFIELDS; // Proceed to read and store x,y,v,w,wa for
251 // the OOB event
252 break;
253
255 // fill bits of the incoming ch to the event's bitfields.
256 // stop when we've filled them all, or all bits of ch are used.
257 do {
258 nbits_val = (oob_event ? nbits_val_oob[ind_val] : nbits_val_neutron[ind_val]);
259 if (!nbits_val) {
260 nbits_val_filled = 0;
261 ind_val++;
262 } else {
263 int32_t nbits_val_to_fill = (nbits_val - nbits_val_filled);
264 if ((8 - nbits_ch_used) >= nbits_val_to_fill) {
265 *ptr_val[ind_val] |= ((ch >> nbits_ch_used) & ((1 << nbits_val_to_fill) - 1)) << nbits_val_filled;
266 nbits_val_filled = 0;
267 nbits_ch_used += nbits_val_to_fill;
268 ind_val++;
269 } else {
270 *ptr_val[ind_val] |= (ch >> nbits_ch_used) << nbits_val_filled;
271 nbits_val_filled += (8 - nbits_ch_used);
272 nbits_ch_used = 8;
273 }
274 }
275 } while ((ind_val < NVAL) && (nbits_ch_used < 8));
276
277 //
278 if (ind_val == NVAL)
279 state = DECODE_DT; // and fall through for dt processing
280
281 if (nbits_ch_used == 8) // read next byte
282 break;
283
284 case DECODE_DT:
285 if ((8 - nbits_ch_used) <= 2) {
286 dt |= (ch >> nbits_ch_used) << nbits_dt_filled;
287 nbits_dt_filled += (8 - nbits_ch_used);
288 } else if ((ch & 0xC0) == 0xC0) {
289 dt |= ((ch & 0x3F) >> nbits_ch_used) << nbits_dt_filled;
290 nbits_dt_filled += (6 - nbits_ch_used);
291 } else {
292 dt |= (ch >> nbits_ch_used) << nbits_dt_filled;
293 nbits_dt_filled += (8 - nbits_ch_used);
294 event_ended = true;
295 }
296
297 break;
298 }
299
300 if (event_ended) {
301 state = DECODE_START; // start on new event next time
302
303 // update times
304 primary_time += dt;
305 auxillary_time += dt;
306
307 // is this event a frame_start? // FRAME_START is an OOB event when oob
308 // mode enabled
309 bool frame_start_event = (oob_en ? (oob_event && c == -2) : (x == 0 && y == 0 && dt == -1));
310
311 if (oob_en || !frame_start_event) {
312 if (oob_event) {
313 if (c == -3) { // FRAME_AUX_START = -3
314 // 0 is the reflecting chopper and 1 is the transmission chopper
315 if (!use_tx_chopper && x == 0) {
316 auxillary_time = 0;
317 auxillary_ok = true;
318 }
319 if (use_tx_chopper && x == 1) {
320 auxillary_time = 0;
321 auxillary_ok = true;
322 }
323 }
324 } else {
325 // if times are ok pass the event trhough the call back, time units in
326 // nsec
327 if (primary_ok && auxillary_ok)
328 handler.addEvent(x, y, static_cast<double>(primary_time) * scale_microsec,
329 static_cast<double>(auxillary_time) * scale_microsec);
330 }
331 }
332
333 if (frame_start_event) {
334 // reset timestamp at start of a new frame
335 // the auxillary time is only available in OOB mode
336 // otherwise, auxillary time = primary time
337 primary_time = 0;
338 primary_ok = true;
339 if (!oob_en) {
340 auxillary_time = 0;
341 auxillary_ok = true;
342 }
343 handler.newFrame();
344 }
345
346 progress.update(loader.selected_position());
347
348 event_ended = false;
349 }
350 }
351}
352} // namespace ANSTO
353} // namespace DataHandling
354} // namespace Mantid
constexpr int32_t EVENTFILEHEADER_BASE_MAGIC_NUMBER
constexpr int32_t EVENTFILEHEADER_BASE_FORMAT_NUMBER
void ReadEventFile(IReader &loader, IEventHandler &handler, IProgress &progress, int32_t def_clock_scale, bool use_tx_chopper)
Helper class which provides the Collimation Length for SANS instruments.