diff --git a/ctapipe_io_lst/__init__.py b/ctapipe_io_lst/__init__.py index 31ec6a9b..2fe2e7f1 100644 --- a/ctapipe_io_lst/__init__.py +++ b/ctapipe_io_lst/__init__.py @@ -20,7 +20,7 @@ from ctapipe.io import EventSource from ctapipe.io.datalevels import DataLevel -from ctapipe.core.traits import Int, Bool, List, Float +from ctapipe.core.traits import Int, Bool, List, Float, Enum from ctapipe.containers import PixelStatusContainer, EventType from .containers import LSTArrayEventContainer, LSTServiceContainer @@ -164,6 +164,17 @@ class LSTEventSource(EventSource): ), ).tag(config=True) + default_trigger_type = Enum( + ['ucts', 'tib'], default_value='ucts', + help=( + 'Default source for trigger type information.' + ' For older data, tib might be the better choice but for data newer' + ' than 2020-06-25, ucts is the preferred option. The source will still' + ' fallback to the other device if the chosen default device is not ' + ' available' + ) + ).tag(config=True) + classes = [PointingSource, EventTimeCalculator, LSTR0Corrections] def __init__(self, input_url=None, **kwargs): @@ -314,7 +325,7 @@ def _generator(self): self.fill_lst_event_container(array_event, zfits_event) # fill trigger info (needs r0 and lst specifics) - self.fill_trigger_info(array_event, zfits_event) + self.fill_trigger_info(array_event) # fill general monitoring data self.fill_mon_container(array_event, zfits_event) @@ -469,7 +480,7 @@ def fill_lst_event_container(self, array_event, zfits_event): lst_evt.drs_tag_status = zfits_event.lstcam.drs_tag_status lst_evt.drs_tag = zfits_event.lstcam.drs_tag - def fill_trigger_info(self, array_event, zfits_event): + def fill_trigger_info(self, array_event): tel_id = self.tel_id trigger = array_event.trigger @@ -477,7 +488,36 @@ def fill_trigger_info(self, array_event, zfits_event): trigger.tels_with_trigger = [tel_id] trigger.tel[tel_id].time = trigger.time - trigger_bits = array_event.lst.tel[tel_id].evt.ucts_trigger_type + lst = array_event.lst.tel[tel_id] + tib_available = lst.evt.extdevices_presence & 1 + ucts_available = lst.evt.extdevices_presence & 2 + + # decide which source to use, if both are available, + # the option decides, if not, fallback to the avilable source + # if no source available, warn and do not fill trigger info + if tib_available and ucts_available: + if self.default_trigger_type == 'ucts': + trigger_bits = lst.evt.ucts_trigger_type + else: + trigger_bits = lst.evt.tib_masked_trigger + + elif tib_available: + trigger_bits = lst.evt.tib_masked_trigger + + elif ucts_available: + trigger_bits = lst.evt.ucts_trigger_type + + else: + self.log.warning('No trigger info available.') + return + + if ucts_available and lst.evt.ucts_trigger_type == 42: + self.log.warning( + 'Event with UCTS trigger_type 42 found.' + ' Probably means unreliable or shifted UCTS data.' + ' Consider switching to TIB using `default_trigger_type="tib"`' + ) + # first bit mono trigger, second stereo. # If *only* those two are set, we assume it's a physics event # for all other we only check if the flag is present diff --git a/ctapipe_io_lst/event_time.py b/ctapipe_io_lst/event_time.py index 7158a692..85c1942c 100644 --- a/ctapipe_io_lst/event_time.py +++ b/ctapipe_io_lst/event_time.py @@ -156,6 +156,9 @@ def __call__(self, tel_id, event): # data comes in random module order, svc contains actual order central_module_index = np.where(lst.svc.module_ids == CENTRAL_MODULE)[0][0] + tib_available = lst.evt.extdevices_presence & 1 + ucts_available = lst.evt.extdevices_presence & 2 + if self._has_reference[tel_id]: # Dragon/TIB timestamps based on a valid absolute reference UCTS timestamp dragon_time = calc_dragon_time( @@ -168,8 +171,7 @@ def __call__(self, tel_id, event): reference=1e-9 * (self._ucts_t0_tib[tel_id] - self._tib_counter0[tel_id]) ) - if lst.evt.extdevices_presence & 2: - # UCTS presence flag is OK + if ucts_available: ucts_timestamp = lst.evt.ucts_timestamp ucts_time = ucts_timestamp * 1e-9 else: @@ -178,7 +180,7 @@ def __call__(self, tel_id, event): # first event and values not passed else: - if not lst.evt.extdevices_presence & 2: + if not ucts_available: raise ValueError( 'Timestamp reference should be extracted from first event' ' but UCTS not available' @@ -198,7 +200,7 @@ def __call__(self, tel_id, event): f' dragon_counter: {initial_dragon_counter}' ) - if not lst.evt.extdevices_presence & 1 and self.timestamp == 'tib': + if not tib_available and self.timestamp == 'tib': raise ValueError( 'TIB is selected for timestamp, no external reference given' ' and first event has not TIB info' @@ -254,7 +256,6 @@ def __call__(self, tel_id, event): ucts_trigger_type = self.previous_ucts_trigger_types[tel_id].popleft() ucts_time = ucts_timestamp * 1e-9 - lst.evt.ucts_trigger_type = ucts_trigger_type lst.evt.ucts_timestamp = ucts_timestamp diff --git a/ctapipe_io_lst/tests/test_stage1.py b/ctapipe_io_lst/tests/test_stage1.py index 5eaeac20..0853e490 100644 --- a/ctapipe_io_lst/tests/test_stage1.py +++ b/ctapipe_io_lst/tests/test_stage1.py @@ -43,7 +43,7 @@ def test_stage1(tmpdir): 'dragon_counter0': int(run_info['dragon_counter0']), 'ucts_t0_tib': int(run_info['ucts_t0_tib']), 'tib_counter0': int(run_info['tib_counter0']), - } + }, }, "CameraCalibrator": { "image_extractor_type": "LocalPeakWindowSum",