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