diff --git a/kadi/commands/command_sets.py b/kadi/commands/command_sets.py index 05686a4d..3627d5ab 100644 --- a/kadi/commands/command_sets.py +++ b/kadi/commands/command_sets.py @@ -139,9 +139,9 @@ def cmd_set_maneuver_sun_pitch(pitch, date=None): return (cmd,) -def cmd_set_maneuver_roll_sun(roll, date=None): +def cmd_set_maneuver_sun_rasl(rasl, date=None): """Maneuver by ``angle`` degrees in roll about Sun line (relative to current).""" - cmd = dict(type="LOAD_EVENT", tlmsid="ROLL_SUN", params={"ROLL": roll}) + cmd = dict(type="LOAD_EVENT", tlmsid="SUN_RASL", params={"RASL": rasl}) return (cmd,) diff --git a/kadi/commands/states.py b/kadi/commands/states.py index 4e24fd19..5fb4fac8 100644 --- a/kadi/commands/states.py +++ b/kadi/commands/states.py @@ -1394,7 +1394,7 @@ def callback(cls, date, transitions, state, idx): cls.add_manvr_transitions(date, transitions, state, idx) -class ManeuverToPitchTransition(ManeuverTransition): +class ManeuverSunPitchTransition(ManeuverTransition): """ Like ``ManeuverTransition`` except perform a pure-pitch maneuver from attitude. @@ -1415,13 +1415,6 @@ def set_transitions(cls, transitions, cmds, start, stop): @staticmethod def callback(pitch, date, transitions, state, idx): - """ - This is a transition function callback. - - It directly sets the state pcad_mode to NSUN and target quaternion to - the expected sun pointed attitude. It then calls the parent method to - add the actual maneuver. - """ # Setup for maneuver to sun-pointed attitude from current att curr_att = [state[qc] for qc in QUAT_COMPS] @@ -1446,6 +1439,48 @@ def callback(pitch, date, transitions, state, idx): ManeuverTransition.add_manvr_transitions(date, transitions, state, idx) +class ManeuverSunRaslTransition(ManeuverTransition): + """ + Like ``ManeuverTransition`` except roll about the sun line. + + This does not change the PCAD mode. + """ + + command_attributes = {"type": "LOAD_EVENT", "tlmsid": "SUN_RASL"} + state_keys = PCAD_STATE_KEYS + + @classmethod + def set_transitions(cls, transitions, cmds, start, stop): + state_cmds = cls.get_state_changing_commands(cmds) + + for cmd in state_cmds: + transitions[cmd["date"]]["maneuver_transition"] = functools.partial( + cls.callback, cmd["params"]["rasl"] + ) + + @staticmethod + def callback(rasl, date, transitions, state, idx): + # Setup for maneuver to sun-pointed attitude from current att + curr_att = [state[qc] for qc in QUAT_COMPS] + + # If current attitude is not defined then just drop the NSM maneuver on + # the floor. The state will start getting defined when the first normal + # maneuver happens. + if None in curr_att: + return + + curr_att = Quat(curr_att) + sun_ra, sun_dec = ska_sun.position(date) + targ_att = ska_sun.apply_sun_pitch_yaw( + curr_att, yaw=rasl, sun_ra=sun_ra, sun_dec=sun_dec + ) + for qc, targ_q in zip(QUAT_COMPS, targ_att.q): + state["targ_" + qc] = targ_q + + # Do the maneuver + ManeuverTransition.add_manvr_transitions(date, transitions, state, idx) + + ################################################################### # ACIS transitions ################################################################### diff --git a/kadi/commands/tests/test_commands.py b/kadi/commands/tests/test_commands.py index 92ece3b0..ab29949b 100644 --- a/kadi/commands/tests/test_commands.py +++ b/kadi/commands/tests/test_commands.py @@ -9,6 +9,7 @@ import parse_cm.paths import parse_cm.tests import pytest +import ska_sun from astropy.table import Table, vstack from chandra_time import secs2date from cxotime import CxoTime @@ -560,7 +561,7 @@ def test_cmds_scenario(stop_date_2020_12_03): # noqa: ARG001 @pytest.mark.skipif(not HAS_INTERNET, reason="No internet connection") -def test_nsm_offset_pitch_command_event(stop_date_2024_01_30): # noqa: ARG001 +def test_nsm_offset_pitch_rasl_command_events(stop_date_2024_01_30): # noqa: ARG001 """Test custom scenario with NSM offset pitch load event command""" # First make the cmd_events.csv file for the scenario scenario = "test_nsm_offset_pitch" @@ -569,8 +570,9 @@ def test_nsm_offset_pitch_command_event(stop_date_2024_01_30): # noqa: ARG001 # Note variation in format of date, since this comes from humans. cmd_evts_text = """\ State,Date,Event,Params,Author,Reviewer,Comment -Definitive,2024:025:00:00:00.000,Maneuver sun pitch,160,Tom Aldcroft,John Scott, -Definitive,2024:024:09:44:06.000,NSM,,John Scott,Julia Zachary, +Definitive,2024:025:04:00:00,Maneuver sun rasl,90,,, +Definitive,2024:025:00:00:00,Maneuver sun pitch,160,,, +Definitive,2024:024:09:44:06,NSM,,,, """ (cmds_dir / "cmd_events.csv").write_text(cmd_evts_text) @@ -588,7 +590,7 @@ def test_nsm_offset_pitch_command_event(stop_date_2024_01_30): # noqa: ARG001 states = kcs.get_states( "2024:024:09:00:00", - "2024:025:12:00:00", + "2024:025:02:00:00", state_keys=["pitch", "pcad_mode"], scenario=scenario, ) @@ -619,6 +621,25 @@ def test_nsm_offset_pitch_command_event(stop_date_2024_01_30): # noqa: ARG001 out["pitch"].format = ".1f" assert out.pformat_all() == exp + states = kcs.get_states( + "2024:024:09:00:00", + "2024:025:08:00:00", + state_keys=["q1", "q2", "q3", "q4"], + scenario=scenario, + ) + + # Interpolate states at two times just after the pitch maneuver and just after the + # roll about sun line (rasl) maneuver. + dates = ["2024:025:00:30:00", "2024:025:05:00:00"] + sts = kcs.interpolate_states(states, dates) + q1 = Quat([sts["q1"][0], sts["q2"][0], sts["q3"][0], sts["q4"][0]]) + q2 = Quat([sts["q1"][1], sts["q2"][1], sts["q3"][1], sts["q4"][1]]) + pitch1, rasl1 = ska_sun.get_sun_pitch_yaw(q1.ra, q1.dec, dates[0]) + pitch2, rasl2 = ska_sun.get_sun_pitch_yaw(q2.ra, q2.dec, dates[1]) + assert np.isclose(pitch1, 160, atol=0.1) + assert np.isclose(pitch2, 160, atol=0.5) + assert np.isclose(rasl2 - rasl1, 90, atol=0.5) + commands_v2.clear_caches()