From a4112ca628680c891a9d6b6e65110694272e28bd Mon Sep 17 00:00:00 2001 From: Jean Connelly Date: Wed, 31 May 2023 12:46:52 -0400 Subject: [PATCH 01/12] Calculate maneuvers from cmds, replace maneuver summary file --- starcheck/pcad_att_check.py | 67 ++++++++++++++++++++++++ starcheck/src/lib/Ska/Starcheck/Obsid.pm | 46 +++------------- starcheck/src/starcheck.pl | 18 +++---- 3 files changed, 82 insertions(+), 49 deletions(-) diff --git a/starcheck/pcad_att_check.py b/starcheck/pcad_att_check.py index 75b1dfbb..ba1d8471 100755 --- a/starcheck/pcad_att_check.py +++ b/starcheck/pcad_att_check.py @@ -2,9 +2,11 @@ import re from astropy.table import Table import Quaternion +from Quaternion import Quat from parse_cm import read_backstop, read_or_list from Chandra.Time import DateTime +from agasc import sphere_dist import hopper @@ -71,6 +73,71 @@ def recent_attitude_history(time, file): return greta_time, float(q1), float(q2), float(q3), float(q4) +def get_maneuvers(backstop_file, attitude_file=None): + """ + Use the hopper state machine to make a maneuver structure from initial + conditions and the backstop file. + + This duplicates some of make_pcad_attitude_check_report. + + :param backstop_file: backstop file + :param attitude_file: attitude history file + :returns: list of maneuvers + """ + + bs = read_backstop(backstop_file) + + # Get initial state attitude and sim position from history + att_time, q1, q2, q3, q4 = recent_attitude_history( + DateTime(bs[0]['date']).secs, + attitude_file) + q = Quaternion.normalize([q1, q2, q3, q4]) + + initial_state = {'q1': q[0], + 'q2': q[1], + 'q3': q[2], + 'q4': q[3]} + + sc = hopper.run_cmds(backstop_file, or_list=None, ofls_characteristics_file=None, + initial_state=initial_state, starcheck=True) + + mm = [] + for m in sc.maneuvers: + q1 = Quaternion.normalize([m['initial']['q1'], + m['initial']['q2'], + m['initial']['q3'], + m['initial']['q4']]) + q1 = Quat(q=q1) + q2 = Quaternion.normalize([m['final']['q1'], + m['final']['q2'], + m['final']['q3'], + m['final']['q4']]) + q2 = Quat(q=q2) + angle = sphere_dist(q1.ra, q1.dec, q2.ra, q2.dec) + + # Re-arrange the hopper maneuever structure to match the structure previously used + # from Parse_CM_File.pm + man = {'initial_obsid': m['initial']['obsid'], + 'final_obsid': m['final']['obsid'], + 'start_date': m['initial']['date'], + 'stop_date': m['final']['date'], + 'ra': q2.ra, + 'dec': q2.dec, + 'roll': q2.roll, + 'dur': m['dur'], + 'angle': angle, + 'q1': m['final']['q1'], + 'q2': m['final']['q2'], + 'q3': m['final']['q3'], + 'q4': m['final']['q4'], + 'tstart': DateTime(m['initial']['date']).secs, + 'tstop': DateTime(m['final']['date']).secs, + } + mm.append(man) + + return mm + + def make_pcad_attitude_check_report(backstop_file, or_list_file=None, attitude_file=None, simtrans_file=None, simfocus_file=None, ofls_characteristics_file=None, out=None, diff --git a/starcheck/src/lib/Ska/Starcheck/Obsid.pm b/starcheck/src/lib/Ska/Starcheck/Obsid.pm index e5bb3810..3cc96f83 100644 --- a/starcheck/src/lib/Ska/Starcheck/Obsid.pm +++ b/starcheck/src/lib/Ska/Starcheck/Obsid.pm @@ -269,7 +269,6 @@ sub set_files { $self->{backstop}, $self->{guide_summ}, $self->{or_file}, - $self->{mm_file}, $self->{dot_file}, $self->{tlr_file} ) = @_; @@ -328,22 +327,21 @@ sub find_command { ################################################################################## sub set_maneuver { # - # Find the right obsid for each maneuver. Note that obsids in mm_file don't - # always match those in DOT, etc + # Find the maneuver for each dot obsid. # ################################################################################## my $self = shift; - my %mm = @_; + my $mm = shift; my $n = 1; my $c; my $found; while ($c = find_command($self, "MP_TARGQUAT", $n++)) { $found = 0; - foreach my $m (values %mm) { - my $manvr_obsid = $m->{manvr_dest}; + foreach my $m (@{$mm}) { + my $manvr_obsid = $m->{final_obsid}; -# where manvr_dest is either the final_obsid of a maneuver or the eventual destination obsid + # where manvr_dest is either the final_obsid of a maneuver or the eventual destination obsid # of a segmented maneuver if ( ($manvr_obsid eq $self->{dot_obsid}) && abs($m->{q1} - $c->{Q1}) < 1e-7 @@ -372,39 +370,12 @@ sub set_maneuver { sprintf( "Uplink quaternion norm value $norm is too far from 1.0\n"); } - my @c_quat_norm = ( - $c->{Q1} / $norm, - $c->{Q2} / $norm, - $c->{Q3} / $norm, - $q4_obc / $norm - ); - - # Get quat from MANEUVER summary file. This is correct to high precision - my $q_man = Quat->new($m->{ra}, $m->{dec}, $m->{roll}); - my $q_obc = Quat->new(@c_quat_norm); - my @q_man = @{ $q_man->{q} }; - my $q_diff = $q_man->divide($q_obc); - if ( abs($q_diff->{ra0} * 3600) > 1.0 - || abs($q_diff->{dec} * 3600) > 1.0 - || abs($q_diff->{roll0} * 3600) > 10.0) - { - push @{ $self->{warn} }, - sprintf( -"Target uplink precision problem for MP_TARGQUAT at $c->{date}\n" - . " Error is yaw, pitch, roll (arcsec) = %.2f %.2f %.2f\n" - . " Use Q1,Q2,Q3,Q4 = %.12f %.12f %.12f %.12f\n", - $q_diff->{ra0} * 3600, - $q_diff->{dec} * 3600, - $q_diff->{roll0} * 3600, - $q_man[0], $q_man[1], $q_man[2], $q_man[3] - ); - } } } push @{ $self->{yellow_warn} }, - sprintf("Did not find match in MAN summary for MP_TARGQUAT at $c->{date}\n") + sprintf("Did not find match in maneuvers for MP_TARGQUAT at $c->{date}\n") unless ($found); } @@ -2089,11 +2060,6 @@ sub print_report { $self->{STARCHECK}, basename($self->{or_file}), $self->{obsid} ) if ($self->{or_file}); - $o .= sprintf( - "MANVR ", - $self->{STARCHECK}, basename($self->{mm_file}), - $self->{dot_obsid} - ); $o .= sprintf( "DOT ", $self->{STARCHECK}, basename($self->{dot_file}), diff --git a/starcheck/src/starcheck.pl b/starcheck/src/starcheck.pl index 8157b8da..cbc40e11 100755 --- a/starcheck/src/starcheck.pl +++ b/starcheck/src/starcheck.pl @@ -146,7 +146,6 @@ get_file("$par{dir}/${sosa_dir_slash}*.backstop", 'backstop', 'required'); my $guide_summ = get_file("$par{dir}/mps/mg*.sum", 'guide summary'); my $or_file = get_file("$par{dir}/mps/or/*.or", 'OR'); -my $mm_file = get_file("$par{dir}/mps/mm*.sum", 'maneuver'); my $dot_file = get_file("$par{dir}/mps/md*.dot", 'DOT', 'required'); my $mech_file = get_file("$par{dir}/${sosa_dir_slash}output/${sosa_prefix}TEST_mechcheck.txt*", @@ -254,11 +253,15 @@ print "Reading TLR file $tlr_file\n"; my @load_segments = Ska::Parse_CM_File::TLR_load_segments($tlr_file); -print "Reading MM file $mm_file\n"; +my $mm = call_python( + "pcad_att_check.get_maneuvers", + [], + { + backstop_file => $backstop, + attitude_file => $attitude_file, + } + ); -# Read momentum management (maneuvers + SIM move) summary file -my %mm = Ska::Parse_CM_File::MM({ file => $mm_file, ret_type => 'hash' }) - if ($mm_file); # Read maneuver management summary for handy obsid time checks print "Reading process summary $ps_file\n"; @@ -416,14 +419,13 @@ $obs{$obsid}->set_obsid(\%guidesumm); # Commanded obsid $obs{$obsid}->set_target(); $obs{$obsid}->set_star_catalog(); - $obs{$obsid}->set_maneuver(%mm) if ($mm_file); + $obs{$obsid}->set_maneuver($mm); $obs{$obsid}->set_manerr(@manerr) if (@manerr); $obs{$obsid}->set_files( $STARCHECK, $backstop, $guide_summ, $or_file, - $mm_file, $dot_file, $tlr_file ); @@ -941,7 +943,6 @@ sub json_obsids { make_annotated_file('', ' ID=\s+', ', ', $backstop); make_annotated_file($guide_summ_start, '^\s+ID:\s+', '\S\S', $guide_summ); make_annotated_file('', '^ ID=', ', ', $or_file) if ($or_file); - make_annotated_file('', ' ID:\s+', '\S\S', $mm_file); make_annotated_file('', 'OBSID,ID=', ',', $dot_file); my $tlr_lines = add_obsid_to_tlr(\@bs, $tlr_file); make_annotated_file('', 'OBSERVATION ID\s*', '\s*\(', $tlr_file, $tlr_lines); @@ -1051,7 +1052,6 @@ sub make_annotated_file { # $backstop = get_file("$par{dir}/*.backstop",'backstop', 'required'); # $guide_summ = get_file("$par{dir}/mg*.sum", 'guide summary'); # $or_file = get_file("$par{dir}/*.or", 'OR'); - # $mm_file = get_file("$par{dir}/*/mm*.sum", 'maneuver'); # $dot_file = get_file("$par{dir}/*.dot", 'DOT', 'required'); my ($start_rexp, $id_pre, $id_post, $file_in, $lines) = @_; From c38a52b1f1034ab93bb3e1fd499d7316b34a6a02 Mon Sep 17 00:00:00 2001 From: Jean Connelly Date: Sun, 25 Jun 2023 23:53:02 -0400 Subject: [PATCH 02/12] Reorganize to only run hopper cmds once --- starcheck/pcad_att_check.py | 125 ++++++++++++++---------------------- starcheck/src/starcheck.pl | 41 ++++++------ starcheck/utils.py | 5 -- 3 files changed, 65 insertions(+), 106 deletions(-) diff --git a/starcheck/pcad_att_check.py b/starcheck/pcad_att_check.py index ba1d8471..877763a1 100755 --- a/starcheck/pcad_att_check.py +++ b/starcheck/pcad_att_check.py @@ -73,82 +73,13 @@ def recent_attitude_history(time, file): return greta_time, float(q1), float(q2), float(q3), float(q4) -def get_maneuvers(backstop_file, attitude_file=None): - """ - Use the hopper state machine to make a maneuver structure from initial - conditions and the backstop file. - - This duplicates some of make_pcad_attitude_check_report. - - :param backstop_file: backstop file - :param attitude_file: attitude history file - :returns: list of maneuvers - """ - - bs = read_backstop(backstop_file) +def run(backstop_file, or_list_file=None, attitude_file=None, + simtrans_file=None, simfocus_file=None, + ofls_characteristics_file=None, out=None, + dynamic_offsets_file=None): - # Get initial state attitude and sim position from history - att_time, q1, q2, q3, q4 = recent_attitude_history( - DateTime(bs[0]['date']).secs, - attitude_file) - q = Quaternion.normalize([q1, q2, q3, q4]) - - initial_state = {'q1': q[0], - 'q2': q[1], - 'q3': q[2], - 'q4': q[3]} - - sc = hopper.run_cmds(backstop_file, or_list=None, ofls_characteristics_file=None, - initial_state=initial_state, starcheck=True) - - mm = [] - for m in sc.maneuvers: - q1 = Quaternion.normalize([m['initial']['q1'], - m['initial']['q2'], - m['initial']['q3'], - m['initial']['q4']]) - q1 = Quat(q=q1) - q2 = Quaternion.normalize([m['final']['q1'], - m['final']['q2'], - m['final']['q3'], - m['final']['q4']]) - q2 = Quat(q=q2) - angle = sphere_dist(q1.ra, q1.dec, q2.ra, q2.dec) - - # Re-arrange the hopper maneuever structure to match the structure previously used - # from Parse_CM_File.pm - man = {'initial_obsid': m['initial']['obsid'], - 'final_obsid': m['final']['obsid'], - 'start_date': m['initial']['date'], - 'stop_date': m['final']['date'], - 'ra': q2.ra, - 'dec': q2.dec, - 'roll': q2.roll, - 'dur': m['dur'], - 'angle': angle, - 'q1': m['final']['q1'], - 'q2': m['final']['q2'], - 'q3': m['final']['q3'], - 'q4': m['final']['q4'], - 'tstart': DateTime(m['initial']['date']).secs, - 'tstop': DateTime(m['final']['date']).secs, - } - mm.append(man) - - return mm - - -def make_pcad_attitude_check_report(backstop_file, or_list_file=None, attitude_file=None, - simtrans_file=None, simfocus_file=None, - ofls_characteristics_file=None, out=None, - dynamic_offsets_file=None, - ): - """ - Make a report for checking PCAD attitudes - - """ all_ok = True - lines = [] # output report lines + lines = [] bs = read_backstop(backstop_file) @@ -170,7 +101,7 @@ def make_pcad_attitude_check_report(backstop_file, or_list_file=None, attitude_f or_list = None if or_list_file is None else read_or_list(or_list_file) if or_list is None: - lines.append('ERROR: No OR list provided, cannot check attitudes') + err_lines.append('ERROR: No OR list provided, cannot check attitudes') all_ok = False # If dynamical offsets file is available then load was planned using @@ -197,7 +128,7 @@ def make_pcad_attitude_check_report(backstop_file, or_list_file=None, attitude_f if not set(doffs['obsid']).issubset(set(or_map)): all_ok = False obsid_mismatch = set(doffs['obsid']) - set(or_map) - lines.append('WARNING: Obsid in dynamic offsets table but missing in OR list {}' + err_lines.append('WARNING: Obsid in dynamic offsets table but missing in OR list {}' .format(list(obsid_mismatch))) # Run the commands and populate attributes in `sc`, the spacecraft state. @@ -206,7 +137,43 @@ def make_pcad_attitude_check_report(backstop_file, or_list_file=None, attitude_f # gives the history of updates as a dict with a `value` and `date` key. sc = hopper.run_cmds(backstop_file, or_list, ofls_characteristics_file, initial_state=initial_state, starcheck=True) - # Iterate through checks by obsid to print status + + # Make maneuver structure + mm = [] + for m in sc.maneuvers: + q1 = Quaternion.normalize([m['initial']['q1'], + m['initial']['q2'], + m['initial']['q3'], + m['initial']['q4']]) + q1 = Quat(q=q1) + q2 = Quaternion.normalize([m['final']['q1'], + m['final']['q2'], + m['final']['q3'], + m['final']['q4']]) + q2 = Quat(q=q2) + angle = sphere_dist(q1.ra, q1.dec, q2.ra, q2.dec) + + # Re-arrange the hopper maneuever structure to match the structure previously used + # from Parse_CM_File.pm + man = {'initial_obsid': m['initial']['obsid'], + 'final_obsid': m['final']['obsid'], + 'start_date': m['initial']['date'], + 'stop_date': m['final']['date'], + 'ra': q2.ra, + 'dec': q2.dec, + 'roll': q2.roll, + 'dur': m['dur'], + 'angle': angle, + 'q1': m['final']['q1'], + 'q2': m['final']['q2'], + 'q3': m['final']['q3'], + 'q4': m['final']['q4'], + 'tstart': DateTime(m['initial']['date']).secs, + 'tstop': DateTime(m['final']['date']).secs, + } + mm.append(man) + + # Do the attitude checks checks = sc.get_checks_by_obsid() for obsid in sc.obsids: for check in checks[obsid]: @@ -220,8 +187,10 @@ def make_pcad_attitude_check_report(backstop_file, or_list_file=None, attitude_f line = '{:5d}: {}'.format(obsid, message) lines.append(line) + # Write the attitute report if out is not None: with open(out, 'w') as fh: fh.writelines("\n".join(lines)) - return all_ok + # Return the attitude status check and the maneuver structure + return {'mm': mm, 'att_check_ok': all_ok} diff --git a/starcheck/src/starcheck.pl b/starcheck/src/starcheck.pl index cbc40e11..0b3271a4 100755 --- a/starcheck/src/starcheck.pl +++ b/starcheck/src/starcheck.pl @@ -253,15 +253,24 @@ print "Reading TLR file $tlr_file\n"; my @load_segments = Ska::Parse_CM_File::TLR_load_segments($tlr_file); -my $mm = call_python( - "pcad_att_check.get_maneuvers", - [], - { - backstop_file => $backstop, - attitude_file => $attitude_file, - } - ); +my $att_report = "${STARCHECK}/pcad_att_check.txt"; +my $att_check = call_python( + "pcad_att_check.run", + [], + { + backstop_file => $backstop, + or_list_file => $or_file, + simtrans_file => $simtrans_file, + simfocus_file => $simfocus_file, + attitude_file => $attitude_file, + ofls_characteristics_file => $char_file, + dynamic_offsets_file => $aimpoint_file, + out => $att_report, + } + ); + +my $mm = $att_check->{mm}; # Read maneuver management summary for handy obsid time checks print "Reading process summary $ps_file\n"; @@ -746,21 +755,7 @@ sub json_obsids { } else { my $att_report = "${STARCHECK}/pcad_att_check.txt"; - my $att_ok = call_python( - "utils._make_pcad_attitude_check_report", - [], - { - backstop_file => $backstop, - or_list_file => $or_file, - simtrans_file => $simtrans_file, - simfocus_file => $simfocus_file, - attitude_file => $attitude_file, - ofls_characteristics_file => $char_file, - out => $att_report, - dynamic_offsets_file => $aimpoint_file - } - ); - if ($att_ok) { + if ($att_check->{att_check_ok} == 1) { $out .= "[OK] Coordinates as expected.\n"; } else { diff --git a/starcheck/utils.py b/starcheck/utils.py index 0a6276a5..d257f128 100644 --- a/starcheck/utils.py +++ b/starcheck/utils.py @@ -26,7 +26,6 @@ from starcheck import __version__ as version from starcheck.calc_ccd_temps import get_ccd_temps from starcheck.check_ir_zone import ir_zone_ok -from starcheck.pcad_att_check import make_pcad_attitude_check_report from starcheck.plot import make_plots_for_obsid ACQS = mica.stats.acq_stats.get_stats() @@ -104,10 +103,6 @@ def get_data_dir(): return sc_data if os.path.exists(sc_data) else "" -def _make_pcad_attitude_check_report(**kwargs): - return make_pcad_attitude_check_report(**kwargs) - - def make_ir_check_report(**kwargs): return ir_zone_ok(**kwargs) From a9b0f6e3e60a456077854e18e5fe4d8c6ccdf3d5 Mon Sep 17 00:00:00 2001 From: Jean Connelly Date: Mon, 26 Jun 2023 00:04:37 -0400 Subject: [PATCH 03/12] Tidy --- starcheck/pcad_att_check.py | 49 ++++++++++++------------ starcheck/src/lib/Ska/Starcheck/Obsid.pm | 2 +- starcheck/src/starcheck.pl | 37 ++++++++---------- 3 files changed, 40 insertions(+), 48 deletions(-) diff --git a/starcheck/pcad_att_check.py b/starcheck/pcad_att_check.py index 877763a1..4a43012a 100755 --- a/starcheck/pcad_att_check.py +++ b/starcheck/pcad_att_check.py @@ -84,8 +84,9 @@ def run(backstop_file, or_list_file=None, attitude_file=None, bs = read_backstop(backstop_file) # Get initial state attitude and sim position from history - att_time, q1, q2, q3, q4 = recent_attitude_history(DateTime(bs[0]['date']).secs, - attitude_file) + att_time, q1, q2, q3, q4 = recent_attitude_history( + DateTime(bs[0]['date']).secs, + attitude_file) q = Quaternion.normalize([q1, q2, q3, q4]) simfa_time, simfa = recent_sim_history(DateTime(bs[0]['date']).secs, simfocus_file) @@ -141,36 +142,34 @@ def run(backstop_file, or_list_file=None, attitude_file=None, # Make maneuver structure mm = [] for m in sc.maneuvers: - q1 = Quaternion.normalize([m['initial']['q1'], - m['initial']['q2'], - m['initial']['q3'], - m['initial']['q4']]) + q1 = Quaternion.normalize( + [m['initial']['q1'], m['initial']['q2'], + m['initial']['q3'], m['initial']['q4']]) q1 = Quat(q=q1) - q2 = Quaternion.normalize([m['final']['q1'], - m['final']['q2'], - m['final']['q3'], - m['final']['q4']]) + q2 = Quaternion.normalize( + [m['final']['q1'], m['final']['q2'], + m['final']['q3'], m['final']['q4']]) q2 = Quat(q=q2) angle = sphere_dist(q1.ra, q1.dec, q2.ra, q2.dec) # Re-arrange the hopper maneuever structure to match the structure previously used # from Parse_CM_File.pm man = {'initial_obsid': m['initial']['obsid'], - 'final_obsid': m['final']['obsid'], - 'start_date': m['initial']['date'], - 'stop_date': m['final']['date'], - 'ra': q2.ra, - 'dec': q2.dec, - 'roll': q2.roll, - 'dur': m['dur'], - 'angle': angle, - 'q1': m['final']['q1'], - 'q2': m['final']['q2'], - 'q3': m['final']['q3'], - 'q4': m['final']['q4'], - 'tstart': DateTime(m['initial']['date']).secs, - 'tstop': DateTime(m['final']['date']).secs, - } + 'final_obsid': m['final']['obsid'], + 'start_date': m['initial']['date'], + 'stop_date': m['final']['date'], + 'ra': q2.ra, + 'dec': q2.dec, + 'roll': q2.roll, + 'dur': m['dur'], + 'angle': angle, + 'q1': m['final']['q1'], + 'q2': m['final']['q2'], + 'q3': m['final']['q3'], + 'q4': m['final']['q4'], + 'tstart': DateTime(m['initial']['date']).secs, + 'tstop': DateTime(m['final']['date']).secs, + } mm.append(man) # Do the attitude checks diff --git a/starcheck/src/lib/Ska/Starcheck/Obsid.pm b/starcheck/src/lib/Ska/Starcheck/Obsid.pm index 3cc96f83..4f64d1e1 100644 --- a/starcheck/src/lib/Ska/Starcheck/Obsid.pm +++ b/starcheck/src/lib/Ska/Starcheck/Obsid.pm @@ -341,7 +341,7 @@ sub set_maneuver { foreach my $m (@{$mm}) { my $manvr_obsid = $m->{final_obsid}; - # where manvr_dest is either the final_obsid of a maneuver or the eventual destination obsid +# where manvr_dest is either the final_obsid of a maneuver or the eventual destination obsid # of a segmented maneuver if ( ($manvr_obsid eq $self->{dot_obsid}) && abs($m->{q1} - $c->{Q1}) < 1e-7 diff --git a/starcheck/src/starcheck.pl b/starcheck/src/starcheck.pl index 0b3271a4..1c3dd87d 100755 --- a/starcheck/src/starcheck.pl +++ b/starcheck/src/starcheck.pl @@ -253,22 +253,21 @@ print "Reading TLR file $tlr_file\n"; my @load_segments = Ska::Parse_CM_File::TLR_load_segments($tlr_file); - my $att_report = "${STARCHECK}/pcad_att_check.txt"; my $att_check = call_python( - "pcad_att_check.run", - [], - { - backstop_file => $backstop, - or_list_file => $or_file, - simtrans_file => $simtrans_file, - simfocus_file => $simfocus_file, - attitude_file => $attitude_file, - ofls_characteristics_file => $char_file, - dynamic_offsets_file => $aimpoint_file, - out => $att_report, - } - ); + "pcad_att_check.run", + [], + { + backstop_file => $backstop, + or_list_file => $or_file, + simtrans_file => $simtrans_file, + simfocus_file => $simfocus_file, + attitude_file => $attitude_file, + ofls_characteristics_file => $char_file, + dynamic_offsets_file => $aimpoint_file, + out => $att_report, + } +); my $mm = $att_check->{mm}; @@ -430,14 +429,8 @@ $obs{$obsid}->set_star_catalog(); $obs{$obsid}->set_maneuver($mm); $obs{$obsid}->set_manerr(@manerr) if (@manerr); - $obs{$obsid}->set_files( - $STARCHECK, - $backstop, - $guide_summ, - $or_file, - $dot_file, - $tlr_file - ); + $obs{$obsid} + ->set_files($STARCHECK, $backstop, $guide_summ, $or_file, $dot_file, $tlr_file); $obs{$obsid}->set_fids($fidsel); $obs{$obsid}->set_ps_times(@ps) if ($ps_file); map { $obs{$obsid}->{$_} = $or{$obsid}{$_} } keys %{ $or{$obsid} } From 6d76843bb0d835210f4e39f05d860066e26bd5dd Mon Sep 17 00:00:00 2001 From: Jean Connelly Date: Mon, 26 Jun 2023 00:04:52 -0400 Subject: [PATCH 04/12] Fix incorrectly named lines array --- starcheck/pcad_att_check.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/starcheck/pcad_att_check.py b/starcheck/pcad_att_check.py index 4a43012a..caa59afd 100755 --- a/starcheck/pcad_att_check.py +++ b/starcheck/pcad_att_check.py @@ -102,7 +102,7 @@ def run(backstop_file, or_list_file=None, attitude_file=None, or_list = None if or_list_file is None else read_or_list(or_list_file) if or_list is None: - err_lines.append('ERROR: No OR list provided, cannot check attitudes') + lines.append('ERROR: No OR list provided, cannot check attitudes') all_ok = False # If dynamical offsets file is available then load was planned using @@ -129,7 +129,7 @@ def run(backstop_file, or_list_file=None, attitude_file=None, if not set(doffs['obsid']).issubset(set(or_map)): all_ok = False obsid_mismatch = set(doffs['obsid']) - set(or_map) - err_lines.append('WARNING: Obsid in dynamic offsets table but missing in OR list {}' + lines.append('WARNING: Obsid in dynamic offsets table but missing in OR list {}' .format(list(obsid_mismatch))) # Run the commands and populate attributes in `sc`, the spacecraft state. From 3c9dca27aca1d29c4be96bbad1b58b4490f9ffb4 Mon Sep 17 00:00:00 2001 From: Jean Connelly Date: Mon, 26 Jun 2023 16:46:07 -0400 Subject: [PATCH 05/12] Put back Quat precision check as benign for now --- starcheck/src/lib/Ska/Starcheck/Obsid.pm | 33 +++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/starcheck/src/lib/Ska/Starcheck/Obsid.pm b/starcheck/src/lib/Ska/Starcheck/Obsid.pm index 4f64d1e1..1a30ef90 100644 --- a/starcheck/src/lib/Ska/Starcheck/Obsid.pm +++ b/starcheck/src/lib/Ska/Starcheck/Obsid.pm @@ -357,9 +357,6 @@ sub set_maneuver { $c->{man_err} = (exists $c->{angle}) ? 35 + $c->{angle} / 2. : 85; $c->{man_err} = 85 if ($c->{man_err} > 85); - # Now check for consistency between quaternion from MANUEVER summary - # file and the quat from backstop (MP_TARGQUAT cmd) - # Get quat from MP_TARGQUAT (backstop) command. # Compute 4th component (as only first 3 are uplinked) and renormalize. # Intent is to match OBC Target Reference subfunction @@ -371,6 +368,36 @@ sub set_maneuver { "Uplink quaternion norm value $norm is too far from 1.0\n"); } + my @c_quat_norm = ( + $c->{Q1} / $norm, + $c->{Q2} / $norm, + $c->{Q3} / $norm, + $q4_obc / $norm + ); + + # Compare to quaternion used in $m (which provides {ra} {dec} {roll}) which was built + # directly from the 4 components in Backstop + my $q_man = Quat->new($m->{ra}, $m->{dec}, $m->{roll}); + my $q_obc = Quat->new(@c_quat_norm); + my @q_man = @{ $q_man->{q} }; + my $q_diff = $q_man->divide($q_obc); + + if ( abs($q_diff->{ra0} * 3600) > 1.0 + || abs($q_diff->{dec} * 3600) > 1.0 + || abs($q_diff->{roll0} * 3600) > 10.0) + { + push @{ $self->{warn} }, + sprintf( +"Target uplink precision problem for MP_TARGQUAT at $c->{date}\n" + . " Error is yaw, pitch, roll (arcsec) = %.2f %.2f %.2f\n" + . " Use Q1,Q2,Q3,Q4 = %.12f %.12f %.12f %.12f\n", + $q_diff->{ra0} * 3600, + $q_diff->{dec} * 3600, + $q_diff->{roll0} * 3600, + $q_man[0], $q_man[1], $q_man[2], $q_man[3] + ); + } + } } From 0264b112b356c0d3c0b79f4d23297dfcd8dee552 Mon Sep 17 00:00:00 2001 From: Jean Connelly Date: Mon, 26 Jun 2023 23:29:10 -0400 Subject: [PATCH 06/12] Update calc for maneuver angle --- starcheck/pcad_att_check.py | 9 +++++++-- starcheck/src/lib/Ska/Starcheck/Obsid.pm | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/starcheck/pcad_att_check.py b/starcheck/pcad_att_check.py index caa59afd..67533fea 100755 --- a/starcheck/pcad_att_check.py +++ b/starcheck/pcad_att_check.py @@ -1,6 +1,7 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst import re from astropy.table import Table +import numpy as np import Quaternion from Quaternion import Quat @@ -150,7 +151,10 @@ def run(backstop_file, or_list_file=None, attitude_file=None, [m['final']['q1'], m['final']['q2'], m['final']['q3'], m['final']['q4']]) q2 = Quat(q=q2) - angle = sphere_dist(q1.ra, q1.dec, q2.ra, q2.dec) + angle1 = sphere_dist(q1.ra, q1.dec, q2.ra, q2.dec) + angle2 = np.degrees(2 * np.arccos(q1.q.dot(q2.q))) + if angle2 > 180: + angle2 = 360 - angle2 # Re-arrange the hopper maneuever structure to match the structure previously used # from Parse_CM_File.pm @@ -162,7 +166,8 @@ def run(backstop_file, or_list_file=None, attitude_file=None, 'dec': q2.dec, 'roll': q2.roll, 'dur': m['dur'], - 'angle': angle, + 'angle': angle2, + 'sphere_dist': angle1, 'q1': m['final']['q1'], 'q2': m['final']['q2'], 'q3': m['final']['q3'], diff --git a/starcheck/src/lib/Ska/Starcheck/Obsid.pm b/starcheck/src/lib/Ska/Starcheck/Obsid.pm index 1a30ef90..68c3f76d 100644 --- a/starcheck/src/lib/Ska/Starcheck/Obsid.pm +++ b/starcheck/src/lib/Ska/Starcheck/Obsid.pm @@ -2115,6 +2115,7 @@ sub print_report { " MANVR: Angle= %6.2f deg Duration= %.0f sec Slew err= %.1f arcsec End= %s\n", $c->{angle}, $c->{dur}, $c->{man_err}, substr(time2date($c->{tstop}), 0, 17)); + $o .= sprintf(" MANVR: sphere_dist= %.2f deg\n", $c->{sphere_dist}); } $o .= "\n"; } From 8e239a8f75eb21d07f626ddfd085524a8011075a Mon Sep 17 00:00:00 2001 From: Jean Connelly Date: Tue, 27 Jun 2023 08:46:19 -0400 Subject: [PATCH 07/12] Remove sphere_dist print and extra q assign --- starcheck/pcad_att_check.py | 21 +++++++++------------ starcheck/src/lib/Ska/Starcheck/Obsid.pm | 1 - 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/starcheck/pcad_att_check.py b/starcheck/pcad_att_check.py index 67533fea..59295e0a 100755 --- a/starcheck/pcad_att_check.py +++ b/starcheck/pcad_att_check.py @@ -143,18 +143,16 @@ def run(backstop_file, or_list_file=None, attitude_file=None, # Make maneuver structure mm = [] for m in sc.maneuvers: - q1 = Quaternion.normalize( + q1 = Quat(q=Quaternion.normalize( [m['initial']['q1'], m['initial']['q2'], - m['initial']['q3'], m['initial']['q4']]) - q1 = Quat(q=q1) - q2 = Quaternion.normalize( + m['initial']['q3'], m['initial']['q4']])) + q2 = Quat(q=Quaternion.normalize( [m['final']['q1'], m['final']['q2'], - m['final']['q3'], m['final']['q4']]) - q2 = Quat(q=q2) - angle1 = sphere_dist(q1.ra, q1.dec, q2.ra, q2.dec) - angle2 = np.degrees(2 * np.arccos(q1.q.dot(q2.q))) - if angle2 > 180: - angle2 = 360 - angle2 + m['final']['q3'], m['final']['q4']])) + + angle = np.degrees(2 * np.arccos(q1.q.dot(q2.q))) + if angle > 180: + angle = 360 - angle # Re-arrange the hopper maneuever structure to match the structure previously used # from Parse_CM_File.pm @@ -166,8 +164,7 @@ def run(backstop_file, or_list_file=None, attitude_file=None, 'dec': q2.dec, 'roll': q2.roll, 'dur': m['dur'], - 'angle': angle2, - 'sphere_dist': angle1, + 'angle': angle, 'q1': m['final']['q1'], 'q2': m['final']['q2'], 'q3': m['final']['q3'], diff --git a/starcheck/src/lib/Ska/Starcheck/Obsid.pm b/starcheck/src/lib/Ska/Starcheck/Obsid.pm index 68c3f76d..1a30ef90 100644 --- a/starcheck/src/lib/Ska/Starcheck/Obsid.pm +++ b/starcheck/src/lib/Ska/Starcheck/Obsid.pm @@ -2115,7 +2115,6 @@ sub print_report { " MANVR: Angle= %6.2f deg Duration= %.0f sec Slew err= %.1f arcsec End= %s\n", $c->{angle}, $c->{dur}, $c->{man_err}, substr(time2date($c->{tstop}), 0, 17)); - $o .= sprintf(" MANVR: sphere_dist= %.2f deg\n", $c->{sphere_dist}); } $o .= "\n"; } From 92065ea8ad91229ea5429cb5a04467466496fe98 Mon Sep 17 00:00:00 2001 From: Jean Connelly Date: Tue, 27 Jun 2023 17:44:14 -0400 Subject: [PATCH 08/12] Remove unused import --- starcheck/pcad_att_check.py | 1 - 1 file changed, 1 deletion(-) diff --git a/starcheck/pcad_att_check.py b/starcheck/pcad_att_check.py index 59295e0a..b638cbc9 100755 --- a/starcheck/pcad_att_check.py +++ b/starcheck/pcad_att_check.py @@ -7,7 +7,6 @@ from parse_cm import read_backstop, read_or_list from Chandra.Time import DateTime -from agasc import sphere_dist import hopper From 771e3fd1d01a6ad542798a5a93371e2f898f2525 Mon Sep 17 00:00:00 2001 From: Jean Connelly Date: Tue, 27 Jun 2023 17:48:04 -0400 Subject: [PATCH 09/12] Simplify angle determination --- starcheck/pcad_att_check.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/starcheck/pcad_att_check.py b/starcheck/pcad_att_check.py index b638cbc9..328bf192 100755 --- a/starcheck/pcad_att_check.py +++ b/starcheck/pcad_att_check.py @@ -4,6 +4,7 @@ import numpy as np import Quaternion from Quaternion import Quat +import math from parse_cm import read_backstop, read_or_list from Chandra.Time import DateTime @@ -149,9 +150,13 @@ def run(backstop_file, or_list_file=None, attitude_file=None, [m['final']['q1'], m['final']['q2'], m['final']['q3'], m['final']['q4']])) - angle = np.degrees(2 * np.arccos(q1.q.dot(q2.q))) - if angle > 180: - angle = 360 - angle + # Calculate maneuver angle using code borrowed from kadi + q_manvr_3 = np.abs(-q2.q[0] * q1.q[0] - q2.q[1] * q1.q[1] + - q2.q[2] * q1.q[2] + q2.q[3] * -q1.q[3]) + # 4th component is cos(theta/2) + if q_manvr_3 > 1: + q_manvr_3 = 1 + angle = np.degrees(2 * math.acos(q_manvr_3)) # Re-arrange the hopper maneuever structure to match the structure previously used # from Parse_CM_File.pm From cae2215df58bff764b560f30a5325cbc853b9497 Mon Sep 17 00:00:00 2001 From: Jean Connelly Date: Tue, 27 Jun 2023 19:03:19 -0400 Subject: [PATCH 10/12] Update monitor window checks for new manvr end times --- starcheck/src/lib/Ska/Starcheck/Obsid.pm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/starcheck/src/lib/Ska/Starcheck/Obsid.pm b/starcheck/src/lib/Ska/Starcheck/Obsid.pm index 1a30ef90..fe5d44d2 100644 --- a/starcheck/src/lib/Ska/Starcheck/Obsid.pm +++ b/starcheck/src/lib/Ska/Starcheck/Obsid.pm @@ -1867,14 +1867,14 @@ sub check_monitor_commanding { } # Now check in backstop commands for : - # Dither is disabled (AODSDITH) 1 min prior to the end of the maneuver (EOM) + # Dither is disabled (AODSDITH) 1 min - 10s prior to the end of the maneuver (EOM) # to the target attitude. - # The OFP Aspect Camera Process is restarted (AOACRSET) 3 minutes after EOM. - # Dither is enabled (AOENDITH) 5 min after EOM + # The OFP Aspect Camera Process is restarted (AOACRSET) 3 minutes - 10s after EOM. + # Dither is enabled (AOENDITH) 5 min - 10s after EOM # ACA-040 my $t_manv = $manv->{tstop}; - my %dt = (AODSDITH => -60, AOACRSET => 180, AOENDITH => 300); + my %dt = (AODSDITH => -70, AOACRSET => 170, AOENDITH => 290); my %cnt = map { $_ => 0 } keys %dt; foreach $bs (grep { $_->{cmd} eq 'COMMAND_SW' } @{$backstop}) { my %param = Ska::Parse_CM_File::parse_params($bs->{params}); From 25d960a2b5efe39a7d6538e71616eab67190ade3 Mon Sep 17 00:00:00 2001 From: Jean Connelly Date: Wed, 28 Jun 2023 10:09:09 -0400 Subject: [PATCH 11/12] Put back the annotated manvr summary --- starcheck/src/lib/Ska/Starcheck/Obsid.pm | 6 ++++++ starcheck/src/starcheck.pl | 13 +++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/starcheck/src/lib/Ska/Starcheck/Obsid.pm b/starcheck/src/lib/Ska/Starcheck/Obsid.pm index fe5d44d2..145750a9 100644 --- a/starcheck/src/lib/Ska/Starcheck/Obsid.pm +++ b/starcheck/src/lib/Ska/Starcheck/Obsid.pm @@ -269,6 +269,7 @@ sub set_files { $self->{backstop}, $self->{guide_summ}, $self->{or_file}, + $self->{mm_file}, $self->{dot_file}, $self->{tlr_file} ) = @_; @@ -2087,6 +2088,11 @@ sub print_report { $self->{STARCHECK}, basename($self->{or_file}), $self->{obsid} ) if ($self->{or_file}); + $o .= sprintf( + "MANVR ", + $self->{STARCHECK}, basename($self->{mm_file}), + $self->{dot_obsid} + ); $o .= sprintf( "DOT ", $self->{STARCHECK}, basename($self->{dot_file}), diff --git a/starcheck/src/starcheck.pl b/starcheck/src/starcheck.pl index 1c3dd87d..47599ef6 100755 --- a/starcheck/src/starcheck.pl +++ b/starcheck/src/starcheck.pl @@ -146,6 +146,7 @@ get_file("$par{dir}/${sosa_dir_slash}*.backstop", 'backstop', 'required'); my $guide_summ = get_file("$par{dir}/mps/mg*.sum", 'guide summary'); my $or_file = get_file("$par{dir}/mps/or/*.or", 'OR'); +my $mm_file = get_file("$par{dir}/mps/mm*.sum", 'maneuver'); my $dot_file = get_file("$par{dir}/mps/md*.dot", 'DOT', 'required'); my $mech_file = get_file("$par{dir}/${sosa_dir_slash}output/${sosa_prefix}TEST_mechcheck.txt*", @@ -429,8 +430,15 @@ $obs{$obsid}->set_star_catalog(); $obs{$obsid}->set_maneuver($mm); $obs{$obsid}->set_manerr(@manerr) if (@manerr); - $obs{$obsid} - ->set_files($STARCHECK, $backstop, $guide_summ, $or_file, $dot_file, $tlr_file); + $obs{$obsid}->set_files( + $STARCHECK, + $backstop, + $guide_summ, + $or_file, + $mm_file, + $dot_file, + $tlr_file + ); $obs{$obsid}->set_fids($fidsel); $obs{$obsid}->set_ps_times(@ps) if ($ps_file); map { $obs{$obsid}->{$_} = $or{$obsid}{$_} } keys %{ $or{$obsid} } @@ -931,6 +939,7 @@ sub json_obsids { make_annotated_file('', ' ID=\s+', ', ', $backstop); make_annotated_file($guide_summ_start, '^\s+ID:\s+', '\S\S', $guide_summ); make_annotated_file('', '^ ID=', ', ', $or_file) if ($or_file); + make_annotated_file('', ' ID:\s+', '\S\S', $mm_file); make_annotated_file('', 'OBSID,ID=', ',', $dot_file); my $tlr_lines = add_obsid_to_tlr(\@bs, $tlr_file); make_annotated_file('', 'OBSERVATION ID\s*', '\s*\(', $tlr_file, $tlr_lines); From eb6127b0e0883cd53453eff7a16d5a6d1e4f7d8d Mon Sep 17 00:00:00 2001 From: Jean Connelly Date: Wed, 28 Jun 2023 10:09:26 -0400 Subject: [PATCH 12/12] Fix a typo in a comment --- starcheck/pcad_att_check.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starcheck/pcad_att_check.py b/starcheck/pcad_att_check.py index 328bf192..56a1cbd0 100755 --- a/starcheck/pcad_att_check.py +++ b/starcheck/pcad_att_check.py @@ -192,7 +192,7 @@ def run(backstop_file, or_list_file=None, attitude_file=None, line = '{:5d}: {}'.format(obsid, message) lines.append(line) - # Write the attitute report + # Write the attitude report if out is not None: with open(out, 'w') as fh: fh.writelines("\n".join(lines))