Skip to content

Commit ac582fe

Browse files
committed
FIX: Lo update spin time calculation
- use universal spin table to get true onboard timing The Lo spin packet is not accurate enough or has different time information in it, so we need to reference DE events to the onboard timekeeping (spin table), which the Lo instrument references to reset its counter. - Change formula for DE time tag counter. It is a fixed length pulse of 4.096ms, not varying with spin period. This means we need to multiply each DE tick by that length of time.
1 parent 176a6df commit ac582fe

2 files changed

Lines changed: 116 additions & 125 deletions

File tree

imap_processing/lo/l1b/lo_l1b.py

Lines changed: 36 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939

4040
logger = logging.getLogger(__name__)
4141

42+
DE_CLOCK_TICK_S = 4.096e-3 # seconds per DE clock tick
43+
4244

4345
def lo_l1b(
4446
sci_dependencies: dict, anc_dependencies: list, descriptor: str
@@ -149,13 +151,9 @@ def l1b_de(
149151
avg_spin_durations_per_cycle = get_avg_spin_durations_per_cycle(spin_data)
150152
# set the spin cycle for each direct event
151153
l1b_de = set_spin_cycle(pointing_start_met, l1a_de, l1b_de)
152-
# get spin start times for each event
153-
spin_start_time = get_spin_start_times(l1a_de, l1b_de, spin_data)
154154

155155
# get the absolute met for each event
156-
l1b_de = set_event_met(
157-
l1a_de, l1b_de, spin_start_time, avg_spin_durations_per_cycle
158-
)
156+
l1b_de = set_event_met(l1a_de, l1b_de)
159157
# set the epoch for each event
160158
l1b_de = set_each_event_epoch(l1b_de)
161159
# Set the ESA mode for each direct event
@@ -662,7 +660,7 @@ def _check_sufficient_spins(spin_data: xr.Dataset) -> np.ndarray:
662660

663661

664662
def get_spin_start_times(
665-
l1a_de: xr.Dataset, l1b_de: xr.Dataset, spin_data: xr.Dataset
663+
l1a_de: xr.Dataset,
666664
) -> xr.DataArray:
667665
"""
668666
Get the start time for the spin that each direct event is in.
@@ -675,59 +673,51 @@ def get_spin_start_times(
675673
----------
676674
l1a_de : xr.Dataset
677675
The L1A DE dataset.
678-
l1b_de : xr.Dataset
679-
The L1B DE dataset.
680-
spin_data : xr.Dataset
681-
The L1A Spin dataset.
682676
683677
Returns
684678
-------
685679
spin_start_time : xr.DataArray
686680
The start time for the spin that each direct event is in.
687681
"""
688-
# align l1a_de packets with spin_data packets
689-
de_to_spin_indices = match_science_to_spin_asc(
690-
l1a_de["epoch"].values, spin_data["epoch"].values
682+
# Get the actual spin start times from the spin data
683+
# Use the individual spin start times rather than calculating from ASC averages
684+
spin_df = get_spin_data()
685+
686+
# For each direct event, find the closest spin start time
687+
# based on the DE's shcoarse time
688+
de_shcoarse = l1a_de["shcoarse"].values
689+
spin_start_sec = spin_df["spin_start_sec_sclk"].values
690+
691+
# Find the index of the closest spin start for each direct event
692+
# We want the closest prior spin (spin that has already started)
693+
closest_spin_indices = (
694+
np.searchsorted(spin_start_sec, de_shcoarse, side="right") - 1
691695
)
692-
# Repeat this for each direct event based on the de_count
693-
de_to_spin_indices = np.repeat(de_to_spin_indices, l1a_de["de_count"])
694696

695-
# There are 28 spins per epoch (1 aggregated science cycle)
696-
# Set the spin_cycle_num to the spin number relative to the
697-
# start of the ASC
698-
spin_cycle_num = l1b_de["spin_cycle"] % 28
699-
asc_starts = spin_data["acq_start_sec"] + spin_data["acq_start_subsec"] * 1e-6
700-
avg_spin_durations = get_avg_spin_durations_per_cycle(spin_data)
697+
# Clip to valid range to handle edge cases
698+
closest_spin_indices = np.clip(closest_spin_indices, 0, len(spin_df) - 1)
701699

702-
# Calculate the time based off of the start of the acquisition period
703-
# then using an average spin duration to calculate the offset within the ASC
704-
# NOTE: We don't want to use the spin start times directly from the ASC spin packet
705-
# because we are using an average spin_cycle for the ASC and there are
706-
# times when only half the spins were completed in an ASC. This allows us
707-
# to still get a valid spin_cycle start time for each direct event, even
708-
# if the average spin_cycle was after the end of the acquisition period.
709-
# TODO: Can we do even better by knowing how many esa_steps and spins were complete?
710-
# i.e. change the spin_cycle calculation
711-
spin_start_time = (
712-
asc_starts[de_to_spin_indices]
713-
+ spin_cycle_num * avg_spin_durations[de_to_spin_indices]
700+
spin_start_times = (
701+
spin_df["spin_start_sec_sclk"].values[closest_spin_indices]
702+
+ spin_df["spin_start_subsec_sclk"].values[closest_spin_indices] * 1e-6
714703
)
715-
return spin_start_time
704+
705+
# repeat out to number of direct events
706+
spin_start_times = np.repeat(spin_start_times, l1a_de["de_count"].values)
707+
708+
return spin_start_times
716709

717710

718711
def set_event_met(
719712
l1a_de: xr.Dataset,
720713
l1b_de: xr.Dataset,
721-
spin_start_time: xr.DataArray,
722-
avg_spin_durations: xr.DataArray,
723714
) -> xr.Dataset:
724715
"""
725716
Get the event MET for each direct event.
726717
727718
Each direct event is converted from a data number to engineering unit in seconds.
728-
de_eu_time de_dn_time / 4096 * avg_spin_duration
729-
where de_time is the direct event time Data Number (DN) and avg_spin_duration
730-
is the average spin duration for the ASC that the event was measured in.
719+
time_from_start_of_spin = de_time * DE_CLOCK_TICK_S
720+
where de_time is the direct event time Data Number (DN).
731721
732722
The direct event time is the time of direct event relative to the start of the spin.
733723
The event MET is the sum of the start time of the spin and the
@@ -739,26 +729,18 @@ def set_event_met(
739729
The L1A DE dataset.
740730
l1b_de : xr.Dataset
741731
The L1B DE dataset.
742-
spin_start_time : np.ndarray
743-
The start time for the spin that each direct event is in.
744-
avg_spin_durations : xr.DataArray
745-
The average spin duration for each epoch.
746732
747733
Returns
748734
-------
749735
l1b_de : xr.Dataset
750736
The L1B DE dataset with the event MET.
751737
"""
752-
counts = l1a_de["de_count"].values
753-
de_time_asc_groups = np.split(l1a_de["de_time"].values, np.cumsum(counts)[:-1])
754-
de_times_eu = []
755-
for i, de_time_asc in enumerate(de_time_asc_groups):
756-
# DE Time is 12 bit DN. The max possible value is 4095
757-
# divide by 4096 to get fraction of a spin duration
758-
de_times_eu.extend(de_time_asc / 4096 * avg_spin_durations[i].values)
738+
# get spin start times for each event
739+
spin_start_times = get_spin_start_times(l1a_de)
759740

741+
# spin start + offset based on de_time ticks
760742
l1b_de["event_met"] = xr.DataArray(
761-
spin_start_time + de_times_eu,
743+
spin_start_times + l1a_de["de_time"].values * DE_CLOCK_TICK_S,
762744
dims=["epoch"],
763745
# attrs=attr_mgr.get_variable_attributes("epoch")
764746
)
@@ -1212,12 +1194,14 @@ def set_pointing_bin(l1b_de: xr.Dataset) -> xr.Dataset:
12121194
# first column: radius (Not needed)
12131195
# second column: longitude
12141196
lons = direction[:, 1]
1197+
# shift to 0-360 range (spin-phase 0 should be in bin 0)
1198+
lons = (lons + 360) % 360
12151199
# third column: latitude
12161200
lats = direction[:, 2]
12171201

12181202
# Define bin edges
12191203
# 3600 bins, 0.1° each
1220-
lon_bins = np.linspace(-180, 180, 3601)
1204+
lon_bins = np.linspace(0, 360, 3601)
12211205
# 40 bins, 0.1° each
12221206
lat_bins = np.linspace(-2, 2, 41)
12231207

0 commit comments

Comments
 (0)