diff --git a/imap_processing/cdf/config/imap_idex_l1b_variable_attrs.yaml b/imap_processing/cdf/config/imap_idex_l1b_variable_attrs.yaml index e65b69da0..d6fe824e3 100644 --- a/imap_processing/cdf/config/imap_idex_l1b_variable_attrs.yaml +++ b/imap_processing/cdf/config/imap_idex_l1b_variable_attrs.yaml @@ -6,6 +6,8 @@ data_max: &data_max 4095 spice_data_min: &spice_data_min -2.0e7 spice_data_max: &spice_data_max 2.0e7 +dead_max: &dead_max 4.065248492307693 + # <=== Base Attributes ===> string_base_attrs: &string_base CATDESC: " " @@ -392,6 +394,20 @@ current_neg2p5v_bus: LABLAXIS: Current UNITS: A +# <=== Dead Time Attributes ===> +dead_time: + <<: *l1b_data_base + CATDESC: Event dead time + VALIDMIN: *data_min + VALIDMAX: *dead_max + DEPEND_0: epoch + FORMAT: F12.6 + UNITS: s + VAR_TYPE: data + FIELDNAM: event_dead_time + FILLVAL: *double_fillval + LABLAXIS: event_dead_time + # <=== Spice Data Attributes ===> ephemeris_position_x: <<: *spice_base diff --git a/imap_processing/idex/idex_constants.py b/imap_processing/idex/idex_constants.py index 176971aea..67550d687 100644 --- a/imap_processing/idex/idex_constants.py +++ b/imap_processing/idex/idex_constants.py @@ -46,6 +46,11 @@ class IdexConstants: # Microseconds to seconds conversion US_TO_S = 1e-6 +# Low-rate timing constants +LOW_SAMPLE_RATE_HZ: float = 4.0625e6 +SAMPLES_PER_BLOCK: int = 8 +DT_BLOCK: float = SAMPLES_PER_BLOCK / LOW_SAMPLE_RATE_HZ + # Seconds in a day SECONDS_IN_DAY = 86400 # Nanoseconds in day diff --git a/imap_processing/idex/idex_l1b.py b/imap_processing/idex/idex_l1b.py index 2b71a570a..dbbcd79c0 100644 --- a/imap_processing/idex/idex_l1b.py +++ b/imap_processing/idex/idex_l1b.py @@ -28,6 +28,7 @@ from imap_processing.idex.idex_constants import ( IDEX_EVENT_REFERENCE_FRAME, ConversionFactors, + DT_BLOCK, ) from imap_processing.idex.idex_utils import get_idex_attrs, setup_dataset from imap_processing.spice.geometry import ( @@ -245,6 +246,8 @@ def idex_l1b_science(l1a_dataset: xr.Dataset) -> xr.Dataset: l1a_dataset, var_information_df, idex_attrs ) + dead_time = get_event_dead_time(l1a_dataset, idex_attrs) + waveforms_converted = convert_waveforms(l1a_dataset, idex_attrs) # Get spice data and save them as xr.DataArrays in the output. Spice data is not @@ -259,6 +262,7 @@ def idex_l1b_science(l1a_dataset: xr.Dataset) -> xr.Dataset: prefixes = ["shcoarse", "shfine", "time_high_sample", "time_low_sample", "aid"] data_vars = ( processed_vars + | dead_time | waveforms_converted | trigger_settings | spice_data @@ -498,6 +502,60 @@ def get_trigger_origin( } +def get_event_dead_time( + l1a_dataset: xr.Dataset, + idex_attrs: ImapCdfAttributes, +) -> dict[str, xr.DataArray]: + """ + Compute event dead time (in seconds) from packed txhdrblocks. + + The dead time is encoded via two bitfields: + - dead_blocks_base (6 bits) + - dead_blocks_shift (4 bits) + + Dead time is computed as: + dead_time = dead_blocks_base * 2**dead_blocks_shift * DT_BLOCK + + where DT_BLOCK is the duration of a single low-rate block. + + Parameters + ---------- + l1a_dataset : xarray.Dataset + IDEX L1A dataset containing the packed `idx__txhdrblocks` variable. + idex_attrs : ImapCdfAttributes + CDF attribute manager object. + + Returns + ------- + dict[str, xarray.DataArray] + Dictionary containing the `dead_time` DataArray (seconds). + """ + txhdrblocks = l1a_dataset["idx__txhdrblocks"].data + + # Extract bitfields + dead_blocks_shift = (txhdrblocks >> 20) & 0b1111 + dead_blocks_base = (txhdrblocks >> 24) & 0b111111 + + print(f"txhdrblocks = {l1a_dataset['idx__txhdrblocks']}, Dead shift = {dead_blocks_shift}, dead base = {dead_blocks_base}") + + # Convert to float once + base = dead_blocks_base.astype(np.float64) + shift = dead_blocks_shift.astype(np.float64) + + # Compute dead time + dead_time_array: NDArray[np.float64] = ( + base * np.power(2.0, shift) * DT_BLOCK + ) + + return { + "dead_time": xr.DataArray( + name="dead_time", + data=dead_time_array, + dims="epoch", + attrs=idex_attrs.get_variable_attributes("dead_time"), + ) + } + def get_spice_data( l1a_dataset: xr.Dataset, idex_attrs: ImapCdfAttributes ) -> dict[str, xr.DataArray]: