Skip to content

Commit 41877e7

Browse files
authored
FIX: Lo L1c pointing sets need to handle pivot_angle (#2637)
The DPS elevation angles (off-angles) need to be relative to the pivot angle.
1 parent e1263b8 commit 41877e7

2 files changed

Lines changed: 46 additions & 4 deletions

File tree

imap_processing/lo/l1c/lo_l1c.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ def lo_l1c(sci_dependencies: dict, anc_dependencies: list) -> list[xr.Dataset]:
190190
)
191191

192192
pset["hae_longitude"], pset["hae_latitude"] = set_pointing_directions(
193-
pset["epoch"].item(), attr_mgr
193+
pset["epoch"].item(), attr_mgr, pset["pivot_angle"].values[0].item()
194194
)
195195

196196
pset.attrs = attr_mgr.get_global_attributes(logical_source)
@@ -1109,7 +1109,9 @@ def set_background_rates(
11091109

11101110

11111111
def set_pointing_directions(
1112-
epoch: float, attr_mgr: ImapCdfAttributes
1112+
epoch: float,
1113+
attr_mgr: ImapCdfAttributes,
1114+
pivot_angle: float,
11131115
) -> tuple[xr.DataArray, xr.DataArray]:
11141116
"""
11151117
Set the pointing directions for the given epoch.
@@ -1124,6 +1126,9 @@ def set_pointing_directions(
11241126
The epoch time in TTJ2000ns.
11251127
attr_mgr : ImapCdfAttributes
11261128
Attribute manager used to get the L1C attributes.
1129+
pivot_angle : float
1130+
The pivot angle in degrees.
1131+
Off-angles are adjusted relative to this pivot angle before transformation.
11271132
11281133
Returns
11291134
-------
@@ -1137,6 +1142,8 @@ def set_pointing_directions(
11371142
spin, off = np.meshgrid(
11381143
SPIN_ANGLE_BIN_CENTERS, OFF_ANGLE_BIN_CENTERS, indexing="ij"
11391144
)
1145+
# off_angles need to account for the pivot_angle
1146+
off += 90 - pivot_angle
11401147
dps_az_el = np.stack([spin, off], axis=-1)
11411148

11421149
# Transform from DPS Az/El to HAE lon/lat

imap_processing/tests/lo/test_lo_l1c.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
N_OFF_ANGLE_BINS,
1313
N_SAMPLES_PER_SPIN,
1414
N_SPIN_ANGLE_BINS,
15+
OFF_ANGLE_BIN_CENTERS,
1516
PSET_SHAPE,
1617
FilterType,
1718
calculate_bin_weights,
@@ -701,7 +702,7 @@ def test_set_pointing_directions(attr_mgr):
701702
test_epoch = 1000000000.0
702703

703704
# Call the function
704-
hae_longitude, hae_latitude = set_pointing_directions(test_epoch, attr_mgr)
705+
hae_longitude, hae_latitude = set_pointing_directions(test_epoch, attr_mgr, 90)
705706

706707
# Verify ttj2000ns_to_et was called correctly
707708
mock_ttj2000ns_to_et.assert_called_once_with(test_epoch)
@@ -752,7 +753,7 @@ def test_set_pointing_directions_meshgrid(attr_mgr):
752753
) # spin_angle x off_angle x 2
753754
mock_frame_transform.return_value = mock_hae_az_el
754755

755-
set_pointing_directions(1000000000.0, attr_mgr)
756+
set_pointing_directions(1000000000.0, attr_mgr, 90)
756757

757758
# Get the dps_az_el array that was passed to frame_transform_az_el
758759
call_args = mock_frame_transform.call_args
@@ -771,3 +772,37 @@ def test_set_pointing_directions_meshgrid(attr_mgr):
771772

772773
# Check that off angles vary along the second dimension
773774
assert not np.allclose(dps_az_el[0, 0, 1], dps_az_el[0, 1, 1])
775+
776+
777+
@pytest.mark.parametrize("pivot_angle", [75, 90, 105])
778+
def test_set_pointing_directions_pivot_angle(attr_mgr, pivot_angle):
779+
"""Test that pivot_angle correctly adjusts off_angles before transformation."""
780+
with (
781+
patch("imap_processing.lo.l1c.lo_l1c.ttj2000ns_to_et") as mock_ttj2000ns_to_et,
782+
patch(
783+
"imap_processing.lo.l1c.lo_l1c.frame_transform_az_el"
784+
) as mock_frame_transform,
785+
):
786+
mock_ttj2000ns_to_et.return_value = 123456789.0
787+
mock_hae_az_el = np.stack(
788+
np.meshgrid(np.arange(3600), np.arange(40), indexing="ij"), axis=-1
789+
)
790+
mock_frame_transform.return_value = mock_hae_az_el
791+
792+
set_pointing_directions(1000000000.0, attr_mgr, pivot_angle=pivot_angle)
793+
794+
# Get the dps_az_el array that was passed to frame_transform_az_el
795+
call_args = mock_frame_transform.call_args
796+
dps_az_el = call_args[0][1]
797+
798+
# Calculate expected offset: off_angles should be adjusted by (90 - pivot_angle)
799+
offset = 90 - pivot_angle
800+
801+
# OFF_ANGLE_BIN_CENTERS range from -1.95 to 1.95 (40 bins from -2 to 2)
802+
# After offset, they should be shifted by the offset amount
803+
expected_off_angles = OFF_ANGLE_BIN_CENTERS + offset
804+
805+
# Check that the off_angle component (index 1) was adjusted correctly
806+
# dps_az_el[:, :, 1] should have the adjusted off angles repeated across spin
807+
actual_off_angles = dps_az_el[0, :, 1] # Take first spin angle
808+
np.testing.assert_allclose(actual_off_angles, expected_off_angles, rtol=1e-10)

0 commit comments

Comments
 (0)