Newer
Older
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2020 ISIS Rutherford Appleton Laboratory UKRI,
// NScD Oak Ridge National Laboratory, European Spallation Source,
// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
// SPDX - License - Identifier: GPL - 3.0 +
#include <cstdio>
#include <stdexcept>
namespace Mantid {
namespace DataHandling {
namespace ANSTO {
// The function reads a binary ANSTO event file,
// It opens the file and returns the data through the callbacks.
constexpr int32_t EVENTFILEHEADER_BASE_MAGIC_NUMBER = 0x0DAE0DAE;
constexpr int32_t EVENTFILEHEADER_BASE_FORMAT_NUMBER = 0x00010002;
// all events contain some or all of these fields
constexpr int32_t NVAL = 5; // x, y, v, w, wa
#pragma pack(push, 1) // otherwise may get 8 byte aligned, no good for us
struct EventFileHeader_Base { // total content should be 16*int (64 bytes)
int32_t
magic_number; // must equal EVENTFILEHEADER_BASE_MAGIC_NUMBER (DAE data)
int32_t format_number; // must equal EVENTFILEHEADER_BASE_FORMAT_NUMBER,
// cppcheck-suppress unusedStructMember
int32_t anstohm_version; // ANSTOHM_VERSION server/filler version number that
// generated the file
int32_t pack_format; // typically 0 if packed binary, 1 if unpacked binary.
int32_t oob_enabled; // if set, OOB events can be present in the data,
// otherwise only neutron and t0 events are stored
int32_t clock_scale; // the CLOCK_SCALE setting, ns per timestamp unit
// cppcheck-suppress unusedStructMember
int32_t spares[16 - 6]; // spares (padding)
};
struct EventFileHeader_Packed { // total content should be 16*int (64 bytes)
int32_t evt_stg_nbits_x; // number of bits in x datum
int32_t evt_stg_nbits_y; // number of bits in y datum
int32_t evt_stg_nbits_v; // number of bits in v datum
int32_t evt_stg_nbits_w; // number of bits in w datum
int32_t evt_stg_nbits_wa; // number of bits in wa datum // MJL added 5/15 for
// format 0x00010002
int32_t evt_stg_xy_signed; // 0 if x and y are unsigned, 1 if x and y are
// signed ints
// cppcheck-suppress unusedStructMember
int32_t spares[16 - 6]; // spares (padding)
// event decoding state machine
enum event_decode_state {
// for all events
DECODE_START, // initial state - then DECODE_VAL_BITFIELDS (for neutron
// events) or DECODE_OOB_BYTE_1 (for OOB events) for OOB events
// only
DECODE_OOB_BYTE_1,
DECODE_OOB_BYTE_2,
// for all events
DECODE_VAL_BITFIELDS,
DECODE_DT // final state - then output data and return to DECODE_START
};
/*
Types of OOB events, and 'NEUTRON' event. Not all are used for all
NEUTRON = 0 = a neutron detected, FRAME_START = -2 = T0 pulse (e.g. from
chopper, or from Doppler on Emu). For most instruments, these are the only
types used.
FRAME_AUX_START = -3 (e.g. from reflecting chopper on Emu), VETO = -6 (e.g.
BEAM_MONITOR = -7 (e.g. if beam monitors connected direct to Mesytec MCPD8 DAE)
RAW = -8 = pass-through, non-decoded raw event directly from the DAE (e.g.
Mesytec MCPD8). Used to access special features of DAE.
Other types are not used in general (DATASIZES = -1 TBD in future, FLUSH = -4
deprecated, FRAME_DEASSERT = -5 only on Fastcomtec P7888 DAE).
*/
template <class IReader, class IEventHandler, class IProgress>
void ReadEventFile(IReader &loader, IEventHandler &handler, IProgress &progress,
int32_t def_clock_scale, bool use_tx_chopper) {
// read file headers (base header then packed-format header)
EventFileHeader_Base hdr_base;
if (!loader.read(reinterpret_cast<char *>(&hdr_base), sizeof(hdr_base)))
throw std::runtime_error("unable to load EventFileHeader-Base");
if (!loader.read(reinterpret_cast<char *>(&hdr_packed), sizeof(hdr_packed)))
throw std::runtime_error("unable to load EventFileHeader-Packed");
if (hdr_base.magic_number != EVENTFILEHEADER_BASE_MAGIC_NUMBER)
throw std::runtime_error("bad magic number");
if (hdr_base.format_number > EVENTFILEHEADER_BASE_FORMAT_NUMBER) {
char txtBuffer[255] = {};
snprintf(txtBuffer, sizeof(txtBuffer),
"invalid file (only format_number=%08Xh or lower)",
EVENTFILEHEADER_BASE_FORMAT_NUMBER);
throw std::runtime_error(txtBuffer);
}
if (hdr_base.pack_format != 0)
throw std::runtime_error("only packed binary format is supported");
throw std::runtime_error("clock scale cannot be zero");
// note: in the old format 0x00010001, the evt_stg_nbits_wa did not exist and
// it contained evt_stg_xy_signed
if (hdr_base.format_number <= 0x00010001) {
hdr_packed.evt_stg_xy_signed = hdr_packed.evt_stg_nbits_wa;
hdr_packed.evt_stg_nbits_wa = 0;
}
// Setup the clock_scale. In format 0x00010001 this was not part of the
// headers, hence a function argument is provided to allow it to be
// specified manually. In the current format 0x00010002, clock_scale is
// written to the header and need not be specified, unless some alternate
// scale is needed.
double scale_microsec = hdr_base.clock_scale / 1000.0;
// old eventfile format did not have clock_scale...
scale_microsec = def_clock_scale / 1000.0;
}
// the initial time is not set correctly so wait until primary and auxillary
// time have been reset before sending events
int64_t primary_time = 0;
int64_t auxillary_time = 0;
bool primary_ok = false;
bool auxillary_ok = false;
// main loop
uint32_t x = 0, y = 0, v = 0, w = 0,
wa = 0; // storage for event data fields
uint32_t *ptr_val[NVAL] = {&x, &y, &v, &w,
// All events are also timestamped. The differential timestamp dt stored in
// each event is summed to recover the event timestamp t. All timestamps are
// frame-relative, i.e. FRAME_START event represents T0 (e.g. from a chopper)
// and t is reset to 0. In OOB mode and for certain DAE types only (e.g.
// Mesytec MCPD8), the FRAME_START event is timestamped relative to the last
// FRAME_START. The timestamp t on the FRAME_START event is therefore the
// total frame duration, and this can be used to recover the absolute
// timestamp of all events in the DAQ, if desired (e.g. for accurate timing
// during long term kinematic experiments).
int32_t dt; // , t = 0 dt may be negative occasionally for some DAE types,
int32_t nbits_val_oob[NVAL] = {};
int32_t nbits_val_neutron[NVAL] = {
hdr_packed.evt_stg_nbits_x, hdr_packed.evt_stg_nbits_y,
hdr_packed.evt_stg_nbits_v, hdr_packed.evt_stg_nbits_w,
hdr_packed.evt_stg_nbits_wa};
int32_t ind_val = 0;
int32_t nbits_val = 0;
int32_t nbits_val_filled = 0;
int32_t nbits_dt_filled = 0;
int32_t oob_en =
hdr_base.oob_enabled; // will be 1 if we are reading a new OOB
// event file (format 0x00010002 only).
int32_t oob_event = 0,
c = 0; // For neutron events, oob_event = 0, and for OOB
// events, oob_event = 1 and c indicates the OOB
// event type. c<0 for all OOB events currently.
event_decode_state state = DECODE_START; // event decoding state machine
bool event_ended = false;
while (true) {
// read next byte
if (!loader.read(reinterpret_cast<char *>(&ch), 1))
int32_t nbits_ch_used = 0; // no bits used initially, 8 to go
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
// start of event processing
if (state == DECODE_START) {
// if OOB event mode is enabled, the leading Bit 0 of the first byte
// indicates whether the event is a neutron event or an OOB event
if (!oob_en)
state = DECODE_VAL_BITFIELDS;
else {
oob_event = (ch & 1);
nbits_ch_used = 1; // leading bit used as OOB bit
if (!oob_event)
state = DECODE_VAL_BITFIELDS;
else
state = DECODE_OOB_BYTE_1;
}
// setup to decode new event bitfields (for both neutron and OOB events)
for (ind_val = 0; ind_val < NVAL; ind_val++)
*ptr_val[ind_val] = 0;
ind_val = 0;
nbits_val_filled = 0;
dt = 0;
nbits_dt_filled = 0;
}
// state machine for event decoding
switch (state) {
case DECODE_START: // Should never get here
throw std::runtime_error("Failure in event decoding");
case DECODE_OOB_BYTE_1: // first OOB header byte
// OOB event Byte 1: Bit 0 = 1 = OOB event, Bit 1 =
// mode (only mode=0 suported currently), Bits 2-5 =
// c (OOB event type), Bits 6-7 = bitfieldsize_x
// / 8. bitfieldsize_x and following 2-bit
// bitfieldsizes are the number of bytes used to
// store the OOB parameter. All of x,y,v,w,wa are
// short integers (16 bits maximum) and so
// bitfieldsizes = 0, 1 or 2 only.
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
if (c & 0x8)
c |= 0xFFFFFFF0; // c is a signed parameter so sign extend - OOB events
// are negative values
nbits_val_oob[0] = (ch & 0xC0) >> 3; // Bits 6-7 * 8 = bitfieldsize_x
state = DECODE_OOB_BYTE_2; // Proceed to process second OOB event header
// byte next time
break;
case DECODE_OOB_BYTE_2: // second OOB header byte
// bitfieldsizes for y, v, w and wa, as for
// bitfieldsize_x above.
nbits_val_oob[1] = (ch & 0x03) << 3; // Bits 0-1 * 8 = bitfieldsize_y
nbits_val_oob[2] = (ch & 0x0C) << 1; // Bits 2-3 * 8 = bitfieldsize_v
nbits_val_oob[3] = (ch & 0x30) >> 1; // Bits 4-5 * 8 = bitfieldsize_w
nbits_val_oob[4] = (ch & 0xC0) >> 3; // Bits 6-7 * 8 = bitfieldsize_wa
state = DECODE_VAL_BITFIELDS; // Proceed to read and store x,y,v,w,wa for
// the OOB event
break;
case DECODE_VAL_BITFIELDS:
// fill bits of the incoming ch to the event's bitfields.
// stop when we've filled them all, or all bits of ch are used.
do {
nbits_val =
(oob_event ? nbits_val_oob[ind_val] : nbits_val_neutron[ind_val]);
if (!nbits_val) {
nbits_val_filled = 0;
ind_val++;
} else {
int32_t nbits_val_to_fill = (nbits_val - nbits_val_filled);
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
if ((8 - nbits_ch_used) >= nbits_val_to_fill) {
*ptr_val[ind_val] |=
((ch >> nbits_ch_used) & ((1 << nbits_val_to_fill) - 1))
<< nbits_val_filled;
nbits_val_filled = 0;
nbits_ch_used += nbits_val_to_fill;
ind_val++;
} else {
*ptr_val[ind_val] |= (ch >> nbits_ch_used) << nbits_val_filled;
nbits_val_filled += (8 - nbits_ch_used);
nbits_ch_used = 8;
}
}
} while ((ind_val < NVAL) && (nbits_ch_used < 8));
//
if (ind_val == NVAL)
state = DECODE_DT; // and fall through for dt processing
if (nbits_ch_used == 8) // read next byte
break;
case DECODE_DT:
if ((8 - nbits_ch_used) <= 2) {
dt |= (ch >> nbits_ch_used) << nbits_dt_filled;
nbits_dt_filled += (8 - nbits_ch_used);
} else if ((ch & 0xC0) == 0xC0) {
dt |= ((ch & 0x3F) >> nbits_ch_used) << nbits_dt_filled;
nbits_dt_filled += (6 - nbits_ch_used);
} else {
dt |= (ch >> nbits_ch_used) << nbits_dt_filled;
nbits_dt_filled += (8 - nbits_ch_used);
event_ended = true;
}
break;
}
if (event_ended) {
state = DECODE_START; // start on new event next time
// update times
primary_time += dt;
auxillary_time += dt;
// is this event a frame_start? // FRAME_START is an OOB event when oob
// mode enabled
bool frame_start_event =
(oob_en ? (oob_event && c == -2) : (x == 0 && y == 0 && dt == -1));
if (oob_en || !frame_start_event) {
if (oob_event) {
if (c == -3) { // FRAME_AUX_START = -3
// 0 is the reflecting chopper and 1 is the transmission chopper
if (!use_tx_chopper && x == 0) {
auxillary_time = 0;
auxillary_ok = true;
}
if (use_tx_chopper && x == 1) {
auxillary_time = 0;
auxillary_ok = true;
}
}
} else {
// if times are ok pass the event trhough the call back, time units in
// nsec
if (primary_ok && auxillary_ok)
handler.addEvent(
x, y, static_cast<double>(primary_time) * scale_microsec,
static_cast<double>(auxillary_time) * scale_microsec);
}
}
if (frame_start_event) {
// reset timestamp at start of a new frame
// the auxillary time is only available in OOB mode
// otherwise, auxillary time = primary time
primary_time = 0;
primary_ok = true;
if (!oob_en) {
auxillary_time = 0;
auxillary_ok = true;
}
handler.newFrame();
}
progress.update(loader.selected_position());
event_ended = false;
}
}
} // namespace DataHandling
} // namespace Mantid