Skip to content

Commit 3ca5922

Browse files
authored
Update frame transforms (IMAP-Science-Operations-Center#2189)
* Update frame transforms * PR comments * Update test
1 parent 217a24f commit 3ca5922

5 files changed

Lines changed: 93 additions & 9 deletions

File tree

imap_processing/mag/l1d/mag_l1d_data.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# mypy: disable-error-code="unused-ignore"
22
"""Data classes for MAG L1D processing."""
33

4+
import logging
45
from dataclasses import InitVar, dataclass
56

67
import numpy as np
@@ -14,7 +15,9 @@
1415
from imap_processing.mag.l2.mag_l2_data import MagL2L1dBase, ValidFrames
1516
from imap_processing.spice import spin
1617
from imap_processing.spice.geometry import frame_transform
17-
from imap_processing.spice.time import ttj2000ns_to_met
18+
from imap_processing.spice.time import ttj2000ns_to_et, ttj2000ns_to_met
19+
20+
logger = logging.getLogger(__name__)
1821

1922

2023
@dataclass
@@ -166,6 +169,9 @@ def __post_init__(self, day: np.datetime64) -> None:
166169
The day we are processing, in np.datetime64[D] format. This is used to
167170
truncate the data to exactly 24 hours.
168171
"""
172+
# The main data frame is MAGO, even though we have MAGI data included.
173+
self.frame = ValidFrames.MAGO
174+
169175
# set the magnitude before truncating
170176
self.magnitude = np.zeros(self.vectors.shape[0], dtype=np.float64) # type: ignore[has-type]
171177
self.truncate_to_24h(day)
@@ -272,15 +278,42 @@ def rotate_frame(self, end_frame: ValidFrames) -> None:
272278
end_frame : ValidFrames
273279
The frame to rotate to. Should be one of the ValidFrames enum.
274280
"""
281+
# Self.frame should refer to the main data in self.vectors, which is MAGO
282+
# data. For most frames, MAGO and MAGI are in the same frame, except the
283+
# instrument reference frame.
284+
if ValidFrames.MAGI in (self.frame, end_frame):
285+
raise ValueError(
286+
"MAGL1d.frame should never be equal to MAGI frame. If the "
287+
"data is in the instrument frame, use MAGO."
288+
)
289+
275290
start_frame = self.frame
276-
super().rotate_frame(end_frame)
291+
292+
if self.epoch_et is None:
293+
self.epoch_et: np.ndarray = ttj2000ns_to_et(self.epoch)
294+
self.magi_epoch_et: np.ndarray = ttj2000ns_to_et(self.magi_epoch)
295+
296+
self.vectors = frame_transform(
297+
self.epoch_et,
298+
self.vectors,
299+
from_frame=start_frame.value,
300+
to_frame=end_frame.value,
301+
)
302+
303+
# If we were in MAGO frame, we need to rotate MAGI vectors from MAGI to
304+
# end_frame
305+
if start_frame == ValidFrames.MAGO:
306+
start_frame = ValidFrames.MAGI
307+
277308
self.magi_vectors = frame_transform(
278-
self.magi_epoch,
309+
self.magi_epoch_et,
279310
self.magi_vectors,
280311
from_frame=start_frame.value,
281312
to_frame=end_frame.value,
282313
)
283314

315+
self.frame = end_frame
316+
284317
def _calibrate_and_offset_vectors(
285318
self,
286319
mago_calibration: np.ndarray,

imap_processing/mag/l2/mag_l2.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ def mag_l2(
9090
)
9191
# level 2 vectors don't include range
9292
vectors = cal_vectors[:, :3]
93+
instrument_frame = ValidFrames.MAGO if always_output_mago else ValidFrames.MAGI
9394

9495
l2_data = MagL2(
9596
vectors=vectors,
@@ -101,6 +102,7 @@ def mag_l2(
101102
data_mode=mode,
102103
offsets=offsets_dataset["offsets"].data,
103104
timedelta=offsets_dataset["timedeltas"].data,
105+
frame=instrument_frame,
104106
)
105107

106108
attributes = ImapCdfAttributes()

imap_processing/mag/l2/mag_l2_data.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
class ValidFrames(Enum):
2121
"""SPICE reference frames for output."""
2222

23-
# TODO: Use correct IMAP_MAG_I or IMAP_MAG_O frame here
24-
MAG = SpiceFrame.IMAP_MAG
23+
MAGO = SpiceFrame.IMAP_MAG_O
24+
MAGI = SpiceFrame.IMAP_MAG_I
2525
DSRF = SpiceFrame.IMAP_DPS
2626
SRF = SpiceFrame.IMAP_SPACECRAFT
2727
GSE = SpiceFrame.IMAP_GSE
@@ -57,7 +57,7 @@ class MagL2L1dBase:
5757
Quality bitmask for each vector. Should be of length n. Copied from offset
5858
file in L2, marked as good always in L1D.
5959
frame:
60-
The reference frame of the input vectors. Starts as the MAG instrument frame.
60+
The reference frame of the input vectors. Defaults to the MAGO instrument frame.
6161
epoch_et: np.ndarray
6262
The epoch timestamps converted to ET format. Used for frame transformations.
6363
Calculated on first use and then saved. Should not be passed in.
@@ -71,7 +71,7 @@ class MagL2L1dBase:
7171
quality_bitmask: np.ndarray
7272
data_mode: DataMode
7373
magnitude: np.ndarray = field(init=False)
74-
frame: ValidFrames = ValidFrames.MAG
74+
frame: ValidFrames = ValidFrames.MAGO
7575
epoch_et: np.ndarray | None = field(init=False, default=None)
7676

7777
def generate_dataset(

imap_processing/tests/mag/test_mag_l1d.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,3 +451,53 @@ def test_enhanced_gradiometry_with_quality_flags_detailed():
451451
grad_ds["gradiometer_offset_magnitude"].data, expected_magnitudes, rtol=1e-10
452452
)
453453
assert np.array_equal(grad_ds["quality_flags"].data, expected_flags)
454+
455+
456+
def test_rotate_frames(mag_l1d_test_class):
457+
# Reset to initial MAGO frame for this test
458+
mag_l1d_test_class.frame = ValidFrames.MAGO
459+
460+
initial_vectors = mag_l1d_test_class.vectors.copy()
461+
initial_magi_vectors = mag_l1d_test_class.magi_vectors.copy()
462+
463+
# Mock frame_transform to return identifiable transformed vectors
464+
def mock_frame_transform(epoch_et, vectors, from_frame, to_frame):
465+
if from_frame == ValidFrames.MAGO.value:
466+
return vectors + 100
467+
elif from_frame == ValidFrames.MAGI.value:
468+
return vectors + 200
469+
else:
470+
return vectors + 300
471+
472+
with patch(
473+
"imap_processing.mag.l1d.mag_l1d_data.frame_transform",
474+
side_effect=mock_frame_transform,
475+
) as mock_transform_l1d:
476+
target_frame = ValidFrames.SRF
477+
mag_l1d_test_class.rotate_frame(target_frame)
478+
479+
# Verify frame_transform was called twice (once for MAGO, once for MAGI)
480+
assert mock_transform_l1d.call_count == 2
481+
482+
# First call should be for MAGO vectors
483+
first_call_args = mock_transform_l1d.call_args_list[0]
484+
assert first_call_args[1]["from_frame"] == ValidFrames.MAGO.value
485+
assert first_call_args[1]["to_frame"] == ValidFrames.SRF.value
486+
487+
# Second call should be for MAGI vectors
488+
second_call_args = mock_transform_l1d.call_args_list[1]
489+
assert second_call_args[1]["from_frame"] == ValidFrames.MAGI.value
490+
assert second_call_args[1]["to_frame"] == ValidFrames.SRF.value
491+
492+
# Check that MAGO vectors were transformed from MAGO frame (+100)
493+
expected_mago_vectors = initial_vectors + 100
494+
np.testing.assert_array_equal(mag_l1d_test_class.vectors, expected_mago_vectors)
495+
496+
# Check that MAGI vectors were transformed from MAGI frame (+200)
497+
expected_magi_vectors = initial_magi_vectors + 200
498+
np.testing.assert_array_equal(
499+
mag_l1d_test_class.magi_vectors, expected_magi_vectors
500+
)
501+
502+
# Check that frame was updated
503+
assert mag_l1d_test_class.frame == ValidFrames.SRF

imap_processing/tests/mag/test_mag_l2.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ def test_offset_application(norm_dataset, mag_test_l2_data):
9191
assert np.allclose(output.epoch, expected_timeshift, atol=1e-9)
9292

9393

94-
@pytest.mark.xfail(reason="Error is too strict during testing")
9594
def test_error_raises(mag_test_l2_data):
9695
dataset = mag_l1a_dataset_generator(3504)
9796
with pytest.raises(ValueError, match="same timestamps"):
@@ -331,7 +330,7 @@ def test_spice_returns(norm_dataset):
331330
timedelta=np.zeros(len(norm_dataset["epoch"].data)),
332331
)
333332

334-
assert l2.frame.name == "MAG"
333+
assert l2.frame.name == "MAGO"
335334

336335
with patch(
337336
"imap_processing.mag.l2.mag_l2_data.frame_transform",

0 commit comments

Comments
 (0)