Skip to content

Commit 27710f2

Browse files
tmplummergreglucaspre-commit-ci[bot]
authored
2301 hi l2 early look spacecraft frame map (IMAP-Science-Operations-Center#2312)
* Add more informative message when naming frame has unexpected value * Fix some issues in L1C processing found when using flight data * Modify L1C test based on recent change * Fix tests add descriptive comment * Descriptive comment * Update imap_processing/hi/hi_l1c.py Co-authored-by: Greg Lucas <greg.m.lucas@gmail.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Greg Lucas <greg.m.lucas@gmail.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 54987fa commit 27710f2

3 files changed

Lines changed: 65 additions & 21 deletions

File tree

imap_processing/ena_maps/utils/naming.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,9 @@ def get_map_coord_frame(frame_str: _coord_frame_str_types) -> SpiceFrame:
342342
elif frame_str == "gcs":
343343
return SpiceFrame.IMAP_GCS
344344
else:
345-
raise NotImplementedError("Coordinate frame is not yet implemented.")
345+
raise NotImplementedError(
346+
f"Coordinate frame {frame_str} is not yet implemented."
347+
)
346348

347349
def to_empty_map(
348350
self,

imap_processing/hi/hi_l1c.py

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def generate_pset_dataset(
102102

103103
pset_dataset = empty_pset_dataset(
104104
de_dataset.epoch.data[0],
105-
de_dataset.esa_energy_step.data,
105+
de_dataset.esa_energy_step,
106106
config_df.cal_prod_config.number_of_products,
107107
logical_source_parts["sensor"],
108108
)
@@ -121,7 +121,7 @@ def generate_pset_dataset(
121121

122122

123123
def empty_pset_dataset(
124-
epoch_val: int, l1b_energy_steps: np.ndarray, n_cal_prods: int, sensor_str: str
124+
epoch_val: int, l1b_energy_steps: xr.DataArray, n_cal_prods: int, sensor_str: str
125125
) -> xr.Dataset:
126126
"""
127127
Allocate an empty xarray.Dataset with appropriate pset coordinates.
@@ -130,7 +130,7 @@ def empty_pset_dataset(
130130
----------
131131
epoch_val : int
132132
The starting epoch in J2000 TT nanoseconds for data in the PSET.
133-
l1b_energy_steps : np.ndarray
133+
l1b_energy_steps : xarray.DataArray
134134
The array of esa_energy_step data from the L1B DE product.
135135
n_cal_prods : int
136136
Number of calibration products to allocate.
@@ -164,8 +164,12 @@ def empty_pset_dataset(
164164
"hi_pset_esa_energy_step", check_schema=False
165165
).copy()
166166
dtype = attrs.pop("dtype")
167-
# Find the unique, non-zero esa_energy_steps from the L1B data
168-
esa_energy_steps = np.array(sorted(set(l1b_energy_steps) - {0}), dtype=dtype)
167+
# Find the unique esa_energy_steps from the L1B data
168+
# Exclude 0 and FILLVAL
169+
esa_energy_steps = np.array(
170+
sorted(set(l1b_energy_steps.values) - {0, l1b_energy_steps.attrs["FILLVAL"]}),
171+
dtype=dtype,
172+
)
169173
coords["esa_energy_step"] = xr.DataArray(
170174
esa_energy_steps,
171175
name="esa_energy_step",
@@ -571,11 +575,26 @@ def find_second_de_packet_data(l1b_dataset: xr.Dataset) -> xr.Dataset:
571575
# We should get two CCSDS packets per 8-spin ESA step.
572576
# Get the indices of the packet before each ESA change.
573577
esa_step = epoch_dataset["esa_step"].values
578+
esa_energy_step = epoch_dataset["esa_energy_step"].values
579+
# A change in esa_step should indicate the location of the second packet in
580+
# each pair of DE packets at an esa_energy_step. In practice, during some
581+
# calibration activities, it was observed that the esa_energy_step can change
582+
# when the esa_step did not. So, we look for either to change and use the
583+
# indices of those changes to identify the second packet in each pair. We
584+
# also need to add the last packet index and assume an energy step change
585+
# occurs after the last packet.
574586
second_esa_packet_idx = np.append(
575-
np.flatnonzero(np.diff(esa_step) != 0), len(esa_step) - 1
587+
np.flatnonzero((np.diff(esa_step) != 0) | (np.diff(esa_energy_step) != 0)),
588+
len(esa_step) - 1,
589+
)
590+
# Remove esa energy steps at 0 - these are calibrations
591+
keep_mask = esa_energy_step[second_esa_packet_idx] != 0
592+
# Remove esa energy steps at FILLVAL - these are unidentified
593+
keep_mask &= (
594+
esa_energy_step[second_esa_packet_idx]
595+
!= l1b_dataset["esa_energy_step"].attrs["FILLVAL"]
576596
)
577-
# Remove esa steps at 0 - these are calibrations
578-
second_esa_packet_idx = second_esa_packet_idx[esa_step[second_esa_packet_idx] != 0]
597+
second_esa_packet_idx = second_esa_packet_idx[keep_mask]
579598
# Remove indices where we don't have two consecutive packets at the same ESA
580599
if second_esa_packet_idx[0] == 0:
581600
logger.warning(
@@ -584,7 +603,8 @@ def find_second_de_packet_data(l1b_dataset: xr.Dataset) -> xr.Dataset:
584603
)
585604
second_esa_packet_idx = second_esa_packet_idx[1:]
586605
missing_esa_pair_mask = (
587-
esa_step[second_esa_packet_idx - 1] != esa_step[second_esa_packet_idx]
606+
esa_energy_step[second_esa_packet_idx - 1]
607+
!= esa_energy_step[second_esa_packet_idx]
588608
)
589609
if missing_esa_pair_mask.any():
590610
logger.warning(
@@ -629,9 +649,11 @@ def get_de_clock_ticks_for_esa_step(
629649
# ESA step group so this match is the end time. The start time is
630650
# 8-spins earlier.
631651
spin_start_mets = spin_df.spin_start_met.to_numpy()
632-
# CCSDS MET has one second resolution, add one to it to make sure it is
633-
# greater than the spin start time it ended on.
634-
end_time_ind = np.flatnonzero(ccsds_met + 1 >= spin_start_mets).max()
652+
# CCSDS MET has one second resolution, add two to it to make sure it is
653+
# greater than the spin start time it ended on. Theotretically, adding
654+
# one second should be sufficeint, but in practice, with flight data, adding
655+
# two seconds was found to be necessary.
656+
end_time_ind = np.flatnonzero(ccsds_met + 2 >= spin_start_mets).max()
635657

636658
# If the minimum absolute difference is greater than 1/2 the spin-phase
637659
# we have a problem.

imap_processing/tests/hi/test_hi_l1c.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,10 @@ def test_generate_pset_dataset(
6262
def test_empty_pset_dataset():
6363
"""Test coverage for empty_pset_dataset function"""
6464
n_energy_steps = 8
65-
l1b_esa_energy_steps = np.arange(n_energy_steps + 1).repeat(2)
65+
l1b_esa_energy_steps = xr.DataArray(
66+
data=np.concat((np.arange(n_energy_steps + 1).repeat(2), np.array([255, 255]))),
67+
attrs={"FILLVAL": 255},
68+
)
6669
n_calibration_prods = 5
6770
sensor_str = HIAPID.H90_SCI_DE.sensor
6871
dataset = hi_l1c.empty_pset_dataset(
@@ -133,7 +136,7 @@ def test_pset_counts(hi_l1_test_data_path, hi_test_cal_prod_config_path):
133136
)
134137
empty_pset = hi_l1c.empty_pset_dataset(
135138
100,
136-
l1b_dataset.esa_energy_step.data,
139+
l1b_dataset.esa_energy_step,
137140
cal_config_df.cal_prod_config.number_of_products,
138141
HIAPID.H90_SCI_DE.sensor,
139142
)
@@ -154,7 +157,7 @@ def test_pset_counts_empty_l1b(hi_l1_test_data_path, hi_test_cal_prod_config_pat
154157
)
155158
empty_pset = hi_l1c.empty_pset_dataset(
156159
100,
157-
l1b_dataset.esa_energy_step.data,
160+
l1b_dataset.esa_energy_step,
158161
cal_config_df.cal_prod_config.number_of_products,
159162
HIAPID.H90_SCI_DE.sensor,
160163
)
@@ -262,8 +265,12 @@ def test_pset_exposure(
262265
mock_spin_data,
263266
):
264267
"""Test coverage for pset_exposure function"""
268+
l1b_energy_steps = xr.DataArray(
269+
np.arange(2) + 1,
270+
attrs={"FILLVAL": 255},
271+
)
265272
empty_pset = hi_l1c.empty_pset_dataset(
266-
100, np.arange(2) + 1, 2, HIAPID.H90_SCI_DE.sensor
273+
100, l1b_energy_steps, 2, HIAPID.H90_SCI_DE.sensor
267274
)
268275
# Set the mock of find_second_de_packet_data to return a xr.Dataset
269276
# with some dummy data. ESA 1 will get binned data once, ESA 2 will get
@@ -316,10 +323,18 @@ def test_pset_exposure(
316323
def test_find_second_de_packet_data():
317324
"""Test coverage for find_second_de_packet_data function"""
318325
# Create a test l1b_dataset
319-
# Expect to remove index 0 and 5 due to missing esa_step pair
320-
# Expect to remove index 11 due to 0 being a calibration step
321-
# Expect to return indices 2, 4, 7, 9, 13
322-
esa_steps = np.array([1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 0, 0, 7, 7])
326+
# Indices represent CCSDS packets at various ESA steps
327+
# Index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13
328+
# esa_step: 1 2 2 2 2 4 5 5 6 6 0 0 7 7
329+
# esa_energy: 1 2 2 3 3 4 5 5 6 6 0 0 7 7
330+
#
331+
# Expected second packet indices from diff logic: [0, 2, 4, 5, 7, 9, 11, 13]
332+
# Remove index 0: missing pair (first packet in series)
333+
# Remove index 5: esa_energy_step 4 doesn't match previous packet's 3
334+
# Remove index 11: esa_energy_step is 0 (calibration)
335+
# Expected final indices: [2, 4, 7, 9, 13]
336+
esa_steps = np.array([1, 2, 2, 2, 2, 4, 5, 5, 6, 6, 0, 0, 7, 7])
337+
esa_energy_steps = np.array([1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 0, 0, 7, 7])
323338
l1b_dataset = xr.Dataset(
324339
coords={
325340
"epoch": xr.DataArray(
@@ -336,6 +351,11 @@ def test_find_second_de_packet_data():
336351
esa_steps,
337352
dims=["epoch"],
338353
),
354+
"esa_energy_step": xr.DataArray(
355+
esa_energy_steps,
356+
dims=["epoch"],
357+
attrs={"FILLVAL": 255},
358+
),
339359
"coincidence_type": xr.DataArray(
340360
np.ones(10),
341361
dims=["event_met"],

0 commit comments

Comments
 (0)