Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions imap_processing/lo/l1c/lo_l1c.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ def lo_l1c(sci_dependencies: dict, anc_dependencies: list) -> list[xr.Dataset]:
)

pset["hae_longitude"], pset["hae_latitude"] = set_pointing_directions(
pset["epoch"].item(), attr_mgr
pset["epoch"].item(), attr_mgr, pset["pivot_angle"].values[0].item()
)

pset.attrs = attr_mgr.get_global_attributes(logical_source)
Expand Down Expand Up @@ -1109,7 +1109,9 @@ def set_background_rates(


def set_pointing_directions(
epoch: float, attr_mgr: ImapCdfAttributes
epoch: float,
attr_mgr: ImapCdfAttributes,
pivot_angle: float,
) -> tuple[xr.DataArray, xr.DataArray]:
"""
Set the pointing directions for the given epoch.
Expand All @@ -1124,6 +1126,9 @@ def set_pointing_directions(
The epoch time in TTJ2000ns.
attr_mgr : ImapCdfAttributes
Attribute manager used to get the L1C attributes.
pivot_angle : float
The pivot angle in degrees.
Off-angles are adjusted relative to this pivot angle before transformation.

Returns
-------
Expand All @@ -1137,6 +1142,8 @@ def set_pointing_directions(
spin, off = np.meshgrid(
SPIN_ANGLE_BIN_CENTERS, OFF_ANGLE_BIN_CENTERS, indexing="ij"
)
# off_angles need to account for the pivot_angle
off += 90 - pivot_angle
dps_az_el = np.stack([spin, off], axis=-1)

# Transform from DPS Az/El to HAE lon/lat
Expand Down
39 changes: 37 additions & 2 deletions imap_processing/tests/lo/test_lo_l1c.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
N_OFF_ANGLE_BINS,
N_SAMPLES_PER_SPIN,
N_SPIN_ANGLE_BINS,
OFF_ANGLE_BIN_CENTERS,
PSET_SHAPE,
FilterType,
calculate_bin_weights,
Expand Down Expand Up @@ -701,7 +702,7 @@ def test_set_pointing_directions(attr_mgr):
test_epoch = 1000000000.0

# Call the function
hae_longitude, hae_latitude = set_pointing_directions(test_epoch, attr_mgr)
hae_longitude, hae_latitude = set_pointing_directions(test_epoch, attr_mgr, 90)

# Verify ttj2000ns_to_et was called correctly
mock_ttj2000ns_to_et.assert_called_once_with(test_epoch)
Expand Down Expand Up @@ -752,7 +753,7 @@ def test_set_pointing_directions_meshgrid(attr_mgr):
) # spin_angle x off_angle x 2
mock_frame_transform.return_value = mock_hae_az_el

set_pointing_directions(1000000000.0, attr_mgr)
set_pointing_directions(1000000000.0, attr_mgr, 90)

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

# Check that off angles vary along the second dimension
assert not np.allclose(dps_az_el[0, 0, 1], dps_az_el[0, 1, 1])


@pytest.mark.parametrize("pivot_angle", [75, 90, 105])
def test_set_pointing_directions_pivot_angle(attr_mgr, pivot_angle):
"""Test that pivot_angle correctly adjusts off_angles before transformation."""
with (
patch("imap_processing.lo.l1c.lo_l1c.ttj2000ns_to_et") as mock_ttj2000ns_to_et,
patch(
"imap_processing.lo.l1c.lo_l1c.frame_transform_az_el"
) as mock_frame_transform,
):
mock_ttj2000ns_to_et.return_value = 123456789.0
mock_hae_az_el = np.stack(
np.meshgrid(np.arange(3600), np.arange(40), indexing="ij"), axis=-1
)
mock_frame_transform.return_value = mock_hae_az_el

set_pointing_directions(1000000000.0, attr_mgr, pivot_angle=pivot_angle)

# Get the dps_az_el array that was passed to frame_transform_az_el
call_args = mock_frame_transform.call_args
dps_az_el = call_args[0][1]

# Calculate expected offset: off_angles should be adjusted by (90 - pivot_angle)
offset = 90 - pivot_angle

# OFF_ANGLE_BIN_CENTERS range from -1.95 to 1.95 (40 bins from -2 to 2)
# After offset, they should be shifted by the offset amount
expected_off_angles = OFF_ANGLE_BIN_CENTERS + offset

# Check that the off_angle component (index 1) was adjusted correctly
# dps_az_el[:, :, 1] should have the adjusted off angles repeated across spin
actual_off_angles = dps_az_el[0, :, 1] # Take first spin angle
np.testing.assert_allclose(actual_off_angles, expected_off_angles, rtol=1e-10)