diff --git a/starcheck/pcad_att_check.py b/starcheck/pcad_att_check.py index 75b1dfbb..56a1cbd0 100755 --- a/starcheck/pcad_att_check.py +++ b/starcheck/pcad_att_check.py @@ -1,7 +1,10 @@ # 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 +import math from parse_cm import read_backstop, read_or_list from Chandra.Time import DateTime @@ -71,23 +74,20 @@ def recent_attitude_history(time, file): return greta_time, float(q1), float(q2), float(q3), float(q4) -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 +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): - """ all_ok = True - lines = [] # output report lines + lines = [] 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) @@ -139,7 +139,46 @@ 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 = Quat(q=Quaternion.normalize( + [m['initial']['q1'], m['initial']['q2'], + m['initial']['q3'], m['initial']['q4']])) + q2 = Quat(q=Quaternion.normalize( + [m['final']['q1'], m['final']['q2'], + m['final']['q3'], m['final']['q4']])) + + # 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 + 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]: @@ -153,8 +192,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 attitude 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/lib/Ska/Starcheck/Obsid.pm b/starcheck/src/lib/Ska/Starcheck/Obsid.pm index e5bb3810..145750a9 100644 --- a/starcheck/src/lib/Ska/Starcheck/Obsid.pm +++ b/starcheck/src/lib/Ska/Starcheck/Obsid.pm @@ -328,20 +328,19 @@ 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 # of a segmented maneuver @@ -359,9 +358,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 @@ -372,6 +368,7 @@ 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, @@ -379,7 +376,8 @@ sub set_maneuver { $q4_obc / $norm ); - # Get quat from MANEUVER summary file. This is correct to high precision + # 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} }; @@ -400,11 +398,12 @@ sub set_maneuver { $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); } @@ -1869,14 +1868,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}); diff --git a/starcheck/src/starcheck.pl b/starcheck/src/starcheck.pl index 8157b8da..47599ef6 100755 --- a/starcheck/src/starcheck.pl +++ b/starcheck/src/starcheck.pl @@ -254,11 +254,23 @@ 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 $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, + } +); -# Read momentum management (maneuvers + SIM move) summary file -my %mm = Ska::Parse_CM_File::MM({ file => $mm_file, ret_type => 'hash' }) - if ($mm_file); +my $mm = $att_check->{mm}; # Read maneuver management summary for handy obsid time checks print "Reading process summary $ps_file\n"; @@ -416,7 +428,7 @@ $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, @@ -744,21 +756,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 { @@ -1051,7 +1049,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) = @_; 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)