Skip to content

Commit

Permalink
Merge pull request #291 from sot/fix-spm-eclipse-enable
Browse files Browse the repository at this point in the history
Improve SPM eclipse enable state
  • Loading branch information
taldcroft authored Aug 24, 2023
2 parents 12fe7e1 + b310778 commit eb58686
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 27 deletions.
113 changes: 89 additions & 24 deletions kadi/commands/states.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
+ ["auto_npnt", "pcad_mode", "pitch", "off_nom_roll"]
)

# State keys for SPM-related transitions.
SPM_STATE_KEYS = ["sun_pos_mon", "battery_connect", "eclipse_enable_spm"]

# Default state keys (mostly matches classic command states list)
DEFAULT_STATE_KEYS = (
"ccd_count",
Expand Down Expand Up @@ -711,7 +714,7 @@ class SPMEnableTransition(FixedTransition):

command_attributes = {"tlmsid": "AOFUNCEN"}
command_params = {"aopcadse": 30}
state_keys = ["sun_pos_mon"]
state_keys = SPM_STATE_KEYS
transition_key = "sun_pos_mon"
transition_val = "ENAB"

Expand All @@ -721,7 +724,7 @@ class SPMDisableTransition(FixedTransition):

command_attributes = {"tlmsid": "AOFUNCDS"}
command_params = {"aopcadsd": 30}
state_keys = ["sun_pos_mon"]
state_keys = SPM_STATE_KEYS
transition_key = "sun_pos_mon"
transition_val = "DISA"

Expand All @@ -736,11 +739,51 @@ class SPMEclipseEnableTransition(BaseTransition):
Eclipse exit is event type ORBPOINT with TYPE=PEXIT or TYPE=LSPEXIT
"""

# Command attributes and params are just for docstring, but actual transition
# command filtering in set_transitions is more complicated.
command_attributes = {"type": "ORBPOINT"}
command_params = {"event_type": ["PEXIT", "LSPEXIT"]}
state_keys = ["sun_pos_mon"]
state_keys = SPM_STATE_KEYS
default_value = False

@classmethod
def set_transitions(cls, transitions_dict, cmds, start, stop):
"""
Set transitions for a Table of commands ``cmds``.
:param transitions_dict: global dict of transitions (updated in-place)
:param cmds: commands (CmdList)
:param start: start time for states
:param stop: stop time for states
:returns: None
"""
# Preselect only commands that might have an impact here.
state_cmds = cls.get_state_changing_commands(cmds)

for cmd in state_cmds:
transitions_dict[cmd["date"]]["sun_pos_mon"] = cls.callback

@classmethod
def callback(cls, date, transitions, state, idx):
if state["eclipse_enable_spm"]:
transition = {
"date": secs2date(date2secs(date) + 11 * 60),
"sun_pos_mon": "ENAB",
}
add_transition(transitions, idx, transition)


class EclipseEnableSPM(BaseTransition):
"""Flag to indicate whether SPM will be enabled 11 minutes after eclipse exit.
This is evaluated at the time of eclipse entry and checks that the most recent
battery connect command (via the ``battery_connect`` state) was within 2:05 minutes
of eclipse entry.
"""

command_attributes = {"type": "ORBPOINT"}
command_params = {"event_type": ["PENTRY", "LSPENTRY"]}
state_keys = SPM_STATE_KEYS
default_value = False

@classmethod
def set_transitions(cls, transitions_dict, cmds, start, stop):
Expand All @@ -755,27 +798,49 @@ def set_transitions(cls, transitions_dict, cmds, start, stop):
:returns: None
"""
# Preselect only commands that might have an impact here.
ok = (cmds["tlmsid"] == "EOESTECN") | (cmds["type"] == "ORBPOINT")
cmds = cmds[ok]
state_cmds = cls.get_state_changing_commands(cmds)

for cmd in state_cmds:
transitions_dict[cmd["date"]]["eclipse_enable_spm"] = cls.callback

@classmethod
def callback(cls, date, transitions, state, idx):
"""Set flag if SPM will be enabled 11 minutes after eclipse exit.
``battery_connect_time`` is the time of the battery connect EOESTECN command,
which must occur prior to this command which is eclipse entry.
"""
battery_connect_time = date2secs(state["battery_connect"])
eclipse_entry_time = date2secs(date)
enable_spm = eclipse_entry_time - battery_connect_time < 125
transition = {"date": date, "eclipse_enable_spm": enable_spm}
add_transition(transitions, idx, transition)

connect_time = 0
connect_flag = False

for cmd in cmds:
if cmd["tlmsid"] == "EOESTECN":
connect_time = DateTime(cmd["date"]).secs
class BatteryConnect(BaseTransition):
"""Most recent battery connect time (type=COMMAND_SW and tlmsid=EOESTECN)"""

elif cmd["type"] == "ORBPOINT":
if cmd["event_type"] in ("PENTRY", "LSPENTRY"):
entry_time = DateTime(cmd["date"]).secs
connect_flag = entry_time - connect_time < 125
command_attributes = {"tlmsid": "EOESTECN"}
state_keys = SPM_STATE_KEYS

elif cmd["event_type"] in ("PEXIT", "LSPEXIT") and connect_flag:
scs33 = (
DateTime(cmd["date"]) + 11 * 60 / 86400
) # 11 minutes in days
transitions_dict[scs33.date]["sun_pos_mon"] = "ENAB"
connect_flag = False
default_value = "1999:001:00:00:00.000"

@classmethod
def set_transitions(cls, transitions, cmds, start, stop):
"""
Set transitions for a Table of commands ``cmds``.
:param transitions_dict: global dict of transitions (updated in-place)
:param cmds: commands (CmdList)
:param start: start time for states
:param stop: stop time for states
:returns: None
"""
state_cmds = cls.get_state_changing_commands(cmds)

for cmd in state_cmds:
transitions[cmd["date"]]["battery_connect"] = cmd["date"]


class SCS84EnableTransition(FixedTransition):
Expand Down Expand Up @@ -1573,7 +1638,7 @@ def get_states(
if "did not find transitions" in str(exc):
raise ValueError(
f"no continuity found for {start=}. Need to have state "
f'transitions following first command at {cmds[0]["date"]} '
f"transitions following first command at {cmds[0]['date']} "
"so use a later start date."
)
else:
Expand Down Expand Up @@ -1946,7 +2011,7 @@ def get_chandra_states(main_args=None):
"--merge-identical",
default=False,
action="store_true",
help="Merge adjacent states that have identical values " "(default=False)",
help="Merge adjacent states that have identical values (default=False)",
)
parser.add_argument("--outfile", help="Output file (default=stdout)")

Expand Down
79 changes: 76 additions & 3 deletions kadi/commands/tests/test_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from astropy.io import ascii
from astropy.table import Table
from Chandra.Time import DateTime
from cxotime import CxoTime
from Ska.engarchive import fetch
from testr.test_helper import has_internet

Expand All @@ -19,8 +20,8 @@
message="kadi commands v1 is deprecated, use v2 instead",
)

from kadi import commands
from kadi.commands import states
from kadi import commands # noqa: E402
from kadi.commands import states # noqa: E402

try:
fetch.get_time_range("dp_pitch")
Expand Down Expand Up @@ -1433,7 +1434,7 @@ def test_continuity_with_transitions_SPM():
to a list of transitions that are needed for correct continuity. Part of
fix for #125.
Start time is ne minute before auto-re-enable of SPM during eclipse handling
Start time is one minute before auto-re-enable of SPM during eclipse handling
This is in the middle of the transitions generated by SPMEnableTransition.
The test here is seeing the ENAB transition that is one minute after the
continuity start time. This will be used by get_states() to get things
Expand All @@ -1448,6 +1449,7 @@ def test_continuity_with_transitions_SPM():
"sun_pos_mon": "DISA",
}

# fmt: off
exp = [
" datestart datestop tstart tstop "
"sun_pos_mon trans_keys",
Expand All @@ -1460,6 +1462,7 @@ def test_continuity_with_transitions_SPM():
"2017:087:08:30:50.891 2017:087:10:20:35.838 607077120.075 "
"607083705.022 DISA sun_pos_mon",
]
# fmt: on
sts = states.get_states(start, stop, state_keys=["sun_pos_mon"])
assert sts.pformat(max_lines=-1, max_width=-1) == exp

Expand Down Expand Up @@ -1724,3 +1727,73 @@ def test_nsm_continuity():
"""
# Prior to the fix this raised an exception, so just check that it runs.
states.get_continuity("2022:301:12:42:00", scenario="flight")


def test_sun_pos_mon_within_eclipse():
"""Test for #289 where sun_pos_mon was not being set correctly within eclipse.
Relevant commands near an eclipse::
2022:109:21:27:27.034 | COMMAND_SW | EOESTECN | APR1822A |
2022:109:21:29:27.034 | ORBPOINT | None | APR1822A | PENTRY
2022:109:22:03:07.034 | ORBPOINT | None | APR1822A | PEXIT
Battery connect time is 2022:109:21:27:27.034
Expected sun_pos_mon ENAB is at 2022:109:22:14:07.034.
"""
starts = CxoTime(
[
"2022:109:21:24:00.000", # Early
"2022:109:21:27:27.033", # 1 ms before battery connect
"2022:109:21:27:27.035", # 1 ms after battery connect
"2022:109:21:29:27.033", # 1 ms before pentry
"2022:109:21:29:27.035", # 1 ms after pentry
"2022:109:22:03:07.033", # 1 ms before pexit
"2022:109:22:03:07.035", # 1 ms after pexit
"2022:109:22:14:07.033", # 1 ms before OBC autonomous sun_pos_mon ENAB
]
)

stop = "2022:110:00:00:00.000"
spm_state_keys = states.SPM_STATE_KEYS

for start in starts:
exp_start_date = max(start.date, "2022:109:21:27:27.034")
# fmt: off
exp = [
" datestart sun_pos_mon battery_connect eclipse_enable_spm",
"--------------------- ----------- --------------------- ------------------",
f"{exp_start_date } DISA 2022:109:21:27:27.034 True",
"2022:109:22:14:07.034 ENAB 2022:109:21:27:27.034 True",
]
# fmt: on

sts = states.get_states(
start, stop, state_keys=spm_state_keys, merge_identical=True
)

names = ["datestart"] + spm_state_keys
assert sts[names][-2:].pformat_all() == exp


def test_sun_pos_mon_within_eclipse_no_spm_enab():
"""
Test a case where battery connect is more than 125 sec before pentry.
2005:014:15:31:36.410 | COMMAND_SW | EOESTECN | JAN1005B
2005:014:15:33:49.164 | ORBPOINT | None | JAN1005B | PENTRY
2005:014:16:38:09.164 | ORBPOINT | None | JAN1005B | PEXIT
"""
sts = states.get_states(
"2005:014:16:38:10", # Just after pexit
"2005:014:17:00:00", # 22 min later
state_keys=states.SPM_STATE_KEYS,
)

exp = [
" datestart sun_pos_mon battery_connect eclipse_enable_spm",
"--------------------- ----------- --------------------- ------------------",
"2005:014:16:38:10.000 DISA 2005:014:15:31:36.410 False",
]
names = ["datestart"] + states.SPM_STATE_KEYS
assert sts[names].pformat_all() == exp

0 comments on commit eb58686

Please sign in to comment.