From b8c542b29a9b3f72686a7dc2486fdc9b6a375a67 Mon Sep 17 00:00:00 2001 From: Luisa Date: Tue, 3 Feb 2026 15:57:48 -0700 Subject: [PATCH] idex fix event time for science data --- imap_processing/idex/idex_l1a.py | 64 +++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/imap_processing/idex/idex_l1a.py b/imap_processing/idex/idex_l1a.py index 66a41799d8..234fe34c41 100644 --- a/imap_processing/idex/idex_l1a.py +++ b/imap_processing/idex/idex_l1a.py @@ -95,8 +95,8 @@ def __init__(self, packet_file: str | Path) -> None: data.attrs = self.idex_attrs.get_global_attributes( f"imap_idex_{level}_evt" ) - data["epoch"] = calculate_idex_epoch_time( - data["shcoarse"], data["shfine"] + data["epoch"] = calculate_idex_event_time( + data["shfine"].data, data["shcoarse"].data ) data["epoch"].attrs = epoch_attrs self.data.append(data) @@ -107,8 +107,8 @@ def __init__(self, packet_file: str | Path) -> None: data.attrs = self.idex_attrs.get_global_attributes( f"imap_idex_{level}_catlst" ) - data["epoch"] = calculate_idex_epoch_time( - data["shcoarse"], data["shfine"] + data["epoch"] = calculate_idex_event_time( + data["shfine"].data, data["shcoarse"].data ) data["epoch"].attrs = epoch_attrs self.data.append(data) @@ -248,25 +248,37 @@ def _read_waveform_bits(waveform_raw: str, high_sample: bool = True) -> list[int return ints -def calculate_idex_epoch_time( - shcoarse_time: float | np.ndarray, shfine_time: float | np.ndarray +def calculate_idex_event_time( + fine_time_subs: np.ndarray, + coarse_time_sec1: np.ndarray, + coarse_time_sec2: np.ndarray | None = None, ) -> npt.NDArray[np.int64]: """ Calculate the epoch time from the FPGA header time variables. - We are given the MET seconds, we need to convert it to nanoseconds in j2000. IDEX - epoch is calculated with shcoarse and shfine time values. The shcoarse time counts - the number of whole seconds elapsed since the epoch (Jan 1st 2010), while shfine - time counts the number of additional 20-microsecond intervals beyond the whole - seconds. Together, these time measurements establish when a dust event took place. + For science packets, we are given the MET seconds, we need to convert it to + nanoseconds in j2000. The idx__txhdrtimesec1 and idx__txhdrtimesec2 variables count + the number of whole seconds elapsed since the epoch (Jan 1st 2010), while + idx__txhdrtimesubs time counts the number of additional 20-microsecond intervals + beyond the whole seconds. Together, these time measurements establish when a dust + event took place. + + The elapsed seconds are stored as a 32-bit unsigned integer that is split across + two 16-bit words for packetization. As a result, idx__txhdrtimesec1 represents + multiples of 2^16 seconds, while idx_txhdrtimesec2 represents the remaining seconds + within that range. This necessitates scaling the upper word by 2^16 = 65,536 when + reconstructing the full seconds counter. + + For housekeeping packets, use the shcoarse and shfine variables instead. Parameters ---------- - shcoarse_time : float, numpy.ndarray - The coarse time value from the FPGA header. Number of seconds since epoch. - shfine_time : float, numpy.ndarray - The fine time value from the FPGA header. Number of 20 microsecond "ticks" since - the last second. + fine_time_subs : numpy.ndarray, optional + The lower 16 bits of the coarse event time. + coarse_time_sec1 : numpy.ndarray + The fine event time in 20-microsecond intervals. + coarse_time_sec2 : numpy.ndarray + The upper 16 bits of the coarse event time. Returns ------- @@ -274,9 +286,17 @@ def calculate_idex_epoch_time( The mission elapsed time converted to nanoseconds since the J2000 epoch in the terrestrial time (TT) timescale. """ - # Get met time in seconds including shfine (number of 20 microsecond ticks) - met = shcoarse_time + shfine_time * 20e-6 - return met_to_ttj2000ns(met) + if coarse_time_sec2 is not None: + # Reconstruct the total seconds from the two 16-bit words + coarse_event_time = 65536 * coarse_time_sec1 + coarse_time_sec2 + else: + # Use coarse_time_sec1 as the full coarse time (e.g., for housekeeping) + coarse_event_time = coarse_time_sec1 + + # Calculate the fine event time in seconds + fine_event_time = fine_time_subs * 20e-6 + + return met_to_ttj2000ns(coarse_event_time + fine_event_time) class RawDustEvent: @@ -357,8 +377,10 @@ def __init__(self, header_packet: space_packet_parser.SpacePacket) -> None: """ # Calculate the impact time in seconds since epoch self.impact_time = 0 - self.impact_time = calculate_idex_epoch_time( - header_packet["SHCOARSE"], header_packet["SHFINE"] + self.impact_time = calculate_idex_event_time( + header_packet["IDX__TXHDRTIMESUBS"], + header_packet["IDX__TXHDRTIMESEC1"], + header_packet["IDX__TXHDRTIMESEC2"], ) self.event_number = header_packet["IDX__SCI0EVTNUM"]