diff --git a/backstop_history/BackstopHistory.py b/backstop_history/BackstopHistory.py index 6c0c19b..3789323 100644 --- a/backstop_history/BackstopHistory.py +++ b/backstop_history/BackstopHistory.py @@ -27,23 +27,43 @@ # Tom Aldcroft # - Accomodate shiny # +# +# Update: June 2021 +# Gregg Germain +# - Modify SCS-107 commands for WSPOW0002A +# - Move history assembly functions (e.g. the big loop) +# out of ATC and into BSC +# - New power commands were added to account for recently +# added power commands/SI modes +# - Use CR backstop files directly +# - Create model-capable output CR*.backstop.hist backstop file +# of the assembled history +# - Eliminate methods not used +# - Consolidated template command definitions into one file for easy update +# - Eliminated files +# - Eliminated any classes that didn't have to be classes +# - Created local logger +# - Logger verbosity level transmitted from model invocation command line +# ################################################################################ from __future__ import print_function + +from astropy.io import ascii +from astropy.table import Table, vstack +from astropy.time import Time + import copy import glob +import json import logging import numpy as np import pickle import os from pathlib import Path +import sys from backstop_history import LTCTI_RTS -import kadi.commands - -from cxotime import CxoTime - - # ------------------------------------------------------------------------------- # # globfile @@ -80,51 +100,83 @@ def emit(self, record): pass rootlogger = logging.getLogger() rootlogger.addHandler(NullHandler()) + + # MODS NEEDED - Now create a localized handler logger = logging.getLogger("backstop_history") - logger.setLevel(logging.DEBUG) + + # Create handler which sends output to stdout + bh_handler = logging.StreamHandler(stream = sys.stdout) # Set numerical values for the different log levels loglevel = {0: logging.CRITICAL, 1: logging.INFO, - 2: logging.DEBUG}.get(verbose, logging.INFO) + 2: logging.DEBUG}.get(verbose, logging.DEBUG) - formatter = logging.Formatter('%(message)s') + # Set the logging level at the LOGGER instance + logger.setLevel(loglevel) - console = logging.StreamHandler() - console.setFormatter(formatter) - console.setLevel(loglevel) - logger.addHandler(console) + # Create format... + formatter = logging.Formatter('%(name)-3s: [%(levelname)-9s] %(message)s') + # ... and add it to handlers + bh_handler.setFormatter(formatter) + + # Add the updated backstop history handler to the logger + logger.addHandler(bh_handler) return logger -class BackstopHistory(object): +class Backstop_History_Class(object): def __init__(self, cont_file_name='ACIS-Continuity.txt', NLET_tracking_file_path='/data/acis/LoadReviews/NonLoadTrackedEvents.txt', - logger=None, verbose=1): - if logger is None: - logger = config_logger(verbose) + verbose=2): + + logger = config_logger(verbose) + self.logger = logger - self.legal_events = ['LTCTI', 'MAN', 'TOO', 'S107', 'GO'] + + self.logger.debug('LOGGER ************************* BHC Init') + + self.review_file_name = None + self.review_file_tstart = None + self.review_file_tstop = None + + # This is the master list which will contain the Assembled Command History self.master_list = [] - self.rev_to_take = [] - self.load_list = [] - self.backstop_list = [] + self.master_ToFC = None + + self.backstop_file_dir = None + self.backstop_file_list = [] self.backstop_file_path_list = [] - self.load_type_list = [] + self.continuity_file_name = cont_file_name + self.NLET_tracking_file_path = NLET_tracking_file_path - self.Review_ToFC = None + self.STOP_time = None self.S107_time = None self.TOO_ToFC = None self.trim_time = None - self.power_cmd_list = ['WSPOW00000', 'WSPOW0002A', 'WSVIDALLDN'] self.end_event_time = None # End time to use for event searches - # Full path to RTS files - self.RTS = LTCTI_RTS.LTCTI_RTS(os.path.dirname(__file__)) + # Path to various data files such as command sequences + self.cmd_seq_file_path = os.path.dirname(__file__) + + # Collection of specialized command sequences such as the sequence + # the sequence performed for an SCS-107 + + self.raw_scs107_cmd_list = [ 'SIMTRANS', 'AA00000000', 'AA00000000', 'WSPOW0002A'] + self.raw_man_cmd_list = ['MP_TARGQUAT', 'AOMANUVR'] + self.raw_ltcti_cmd_list = [] + self.power_cmd_list = ['WSPOW00000', 'WSPOW0002A', 'WSVIDALLDN'] + + # Attribute specifying the full path to where the assembled history file is + # written out + self.assembled_hist_file_path = None + + # CR Command Array DTYPE + self.CR_DTYPE = [('commands', '|U3000'), ('time', ' tbegin_time: + # Obtain the Continuity information of the present ofls directory + cont_load_path, present_load_type, scs107_date = self.get_continuity_file_info(present_ofls_dir) + + self.logger.debug(' Cont load path: %s, load type: %s, scs107 date: %s' % (cont_load_path, present_load_type, scs107_date)) + + + # Read the commands from the Continuity file + cont_cr_cmds, cont_file_name = self.get_CR_bs_cmds(cont_load_path) + + #---------------------- NORMAL ---------------------------------------- + # If the PRESENT (i.e. NOT Continuity) load type is "normal" then grab + # the continuity command set and concatenate those commands to the start of + # the Master List. + if present_load_type.upper() == 'NORMAL': + + self.logger.debug('Processing Normal: %s' % (self.master_list[-1]['date'])) + + # Next step is to set the Master List equal to the concatenation of + # the continuity load commands and the review load commands + # commands with no trimming, since this is a Normal load + self.master_list = np.append(cont_cr_cmds, self.master_list, axis=0) + + # Sort the master list based upon the time column + self.master_list.sort(order='time') + + # Adjust the Master List Time of First Command (ToFC) + self.master_ToFC = round(self.master_list[0]['time'], 1) + + # Now point the "present" ofls directory to the Continuity directory + # This instigates the back chaining. + present_ofls_dir = cont_load_path + + + #---------------------- TOO ---------------------------------------- + # If the PRESENT (i.e. NOT Continuity) load type is "TOO" then grab + # the continuity command set, trim the command set to exclude those + # Commands discarded by the TOO cut, concatenate the remaining + # continuity commands to the start of the Master List. + elif present_load_type.upper() == 'TOO': + self.logger.debug('Processing TOO at date: %s' % (scs107_date)) + + # Convert the TOO cut time found in the ACIS-Continuity.txt file to + # cxcsec + too_cut_time = round(Time(scs107_date, format = 'yday', scale = 'utc').cxcsec, 1) + + # Trim off any continuity command which occurs on or after the + # TOO cut time + cont_cr_cmds = self.Trim_bs_cmds_After_Time(too_cut_time, cont_cr_cmds) + + # Combine the trimmed continuity commands with the master list + self.master_list = np.append(cont_cr_cmds, self.master_list, axis=0) + + # Sort the master list based upon the time column + self.master_list.sort(order='time') + + # Adjust the Master List Time of First Command (ToFC) + self.master_ToFC = round(self.master_list[0]['time'], 1) + + # Now point the operative ofls directory to the Continuity directory + present_ofls_dir = cont_load_path + + + #---------------------- STOP ---------------------------------------- + # If the PRESENT (i.e. NOT Continuity) load type is "STOP" then: + # 1) trim the continuity command set to include only those continuity + # commands prior to the Cut + # 2) Add on the SCS-107 commands that were executed at the Cut time + # 3) Look for and insert any events (power commands, maneuvers, LTCTI's) + # 4) append all these commands to the start of the Master List. + elif present_load_type.upper() == 'STOP': + + self.logger.debug('Processing FULL STOP at date: %s' % (scs107_date)) + + # Convert the SCS-107 cut time found in the ACIS-Continuity.txt file to + # cxcsec + scs107_time = round(Time(scs107_date, format = 'yday', scale = 'utc').cxcsec, 1) + + # Trim off any continuity command which occurs on or after the + # SCS-107 cut time + cont_cr_cmds = self.Trim_bs_cmds_After_Time(scs107_time, cont_cr_cmds) + + # Time stamp the raw SCS-107 commands using the STOP_date + processed_scs107_cmds = self.Process_Cmds(self.raw_scs107_cmd_list, scs107_date) + + # Append the processed SCS-107 commands to the continuity + # command list + cont_cr_cmds = np.append(processed_scs107_cmds, cont_cr_cmds, axis=0) + + # Sort the continuity list based upon the time column + cont_cr_cmds.sort(order='time') + + # Continuity = Trimmed Continuity plus time stamped SCS-107 commands + + # Combine the trimmed continuity commands with the master list + self.master_list = np.append(cont_cr_cmds, self.master_list, axis=0) + + # Sort the master list based upon the time column + self.master_list.sort(order='time') + + # Adjust the Master List Time of First Command (ToFC) + self.master_ToFC = round(self.master_list[0]['time'], 1) + + # Now point the operative ofls directory to the Continuity directory + present_ofls_dir = cont_load_path + + #---------------------- SCS-107 ---------------------------------------- + # The load type is "SCS-107". master_list contains the review load + # The Continuity load was read in at the top of the loop + # So perform these functions for the 107: + # 1) Get an updated set of SCS-107 commands + # - Concat to master_list and sort + # 2) Trim the Continuity Commands removing all those after the SCS-107 time + # - Concat to master_list and sort + # 3) Obtain the Vehicle Only (VO) commands + # - Trim those before the SCS-107 time and any after the Review Load start time + # - Concat to master_list and sort + # + elif present_load_type.upper() == 'SCS-107': + # Inform coder that you are processing an SCS-107 + self.logger.debug('Processing SCS-107 at date: %s' % (scs107_date)) + + # Convert the TOO cut time found in the ACIS-Continuity.txt file to + # cxcsec + scs107_time = round(Time(scs107_date, format = 'yday', scale = 'utc').cxcsec, 1) + + # Trim off any continuity command which occurs on or after the + # SCS-107 cut time + cont_cr_cmds = self.Trim_bs_cmds_After_Time(scs107_time, cont_cr_cmds) + + # Time stamp the raw SCS-107 commands using the STOP_date + processed_scs107_cmds = self.Process_Cmds(self.raw_scs107_cmd_list, scs107_date) + + # Append the processed SCS-107 commands to the continuity + # command list + cont_cr_cmds = np.append(processed_scs107_cmds, cont_cr_cmds, axis=0) + + # Sort the continuity list based upon the time column + cont_cr_cmds.sort(order='time') + + # Continuity = Trimmed Continuity + time stamped SCS-107 commands + + # Combine the trimmed continuity commands with the master list + self.master_list = np.append(cont_cr_cmds, self.master_list, axis=0) + + # Master = Continuity + SCS-107 cmds + + # Sort the master list based upon the time column + self.master_list.sort(order='time') + + # Obtain the CONTINUITY load Vehicle-Only file + + vo_cr_cmds, vo_cr_name = self.get_VR_bs_cmds(cont_load_path) + + self.logger.debug(' Got the VO load from: %s %s'% (cont_load_path, vo_cr_name)) + self.logger.debug(' VO START: %s' % (vo_cr_cmds[0]['date'])) + self.logger.debug(' VO STOP: %s' % (vo_cr_cmds[-1]['date'])) + + # Trim off all VO commands prior to scs-107 because they already ran + vo_cr_cmds = self.Trim_bs_cmds_Before_Time(scs107_time, vo_cr_cmds) + + # Trim off all VO commands AFTER the start of the master_ list + # because they will not run + vo_cr_cmds = self.Trim_bs_cmds_After_Time(self.master_ToFC, vo_cr_cmds) + + self.logger.debug(' After VO TRIM: %s %s' % (scs107_date, self.master_ToFC)) + self.logger.debug(' VO START: %s' % (vo_cr_cmds[0]['date'])) + self.logger.debug(' VO STOP: %s' % (vo_cr_cmds[-1]['date'])) + + # Combine the trimmed VO commands with the master list + self.master_list = np.append(vo_cr_cmds, self.master_list, axis=0) + + # Sort the master list based upon the time column + self.master_list.sort(order='time') + + # Master = Continuity + SCS-107 cmds + trimmed VO cmds + + + # Adjust the Master List Time of First Command (ToFC) + self.master_ToFC = round(self.master_list[0]['time'], 1) + + # Now point the "present" ofls directory to the Continuity directory + # This instigates the back chaining. + present_ofls_dir = cont_load_path + + + # At this point you have processed the continuity load, a VO load if required, + # and an SCS107 command set if required and appended all that + # to the master list. + + # self.master_ToFC is the time of the start of the assembled history section + # self.end_event_time points to the last time of the Review load + + # Continuity start Continuity end Review start Review end + # | | | | + # self.master_ToFC self.end_event_time + + # or, if you've been backchaining, the time of th4e first command of the + # previous backchain. + + # New Continuity start Old C-start Old-Review end + # | | + # self.master_ToFC self.end_event_time + + # The task now is to look for any NLET events that occurred between the + # present values of self.master_ToFC and self.end_event_time + + # Now scan the NLET file for any Event that occurs between the + # start of the newly updated master_list and the previously recorded + # end_event_time value. You are tacking them on as you go and using + # + # So search the NLET file for events.... + event_list = self.Find_Events_Between_Dates(self.master_ToFC, self.end_event_time) + + # ....and if there are events to process....... + if len(event_list) > 0: + + # There are, so process them all + for eachevent in event_list: + # split the string on spaces + splitline = eachevent.split() + + # LTCTI event - Process and add to the master list + if splitline[1] == 'LTCTI': + # Process_LTCTI appends the event commands to self.master_list + ltcti_cmds = self.Process_LTCTI(eachevent, self.master_ToFC) + # Append the processed LTCTI commands + self.master_list = np.append(ltcti_cmds, self.master_list, axis=0) + # Sort the resultant master list based upon the time column + self.master_list.sort(order='time') + + # MANEUVER, event, process it and add it to the Master List + elif splitline[1] == 'MAN': + processed_man_cmds = self.Process_MAN(eachevent) + + # Append the processed maneuver commands + self.master_list = np.append(processed_man_cmds, self.master_list, axis=0) + + # Sort the master list based upon the time column + self.master_list.sort(order='time') + + # POWER COMMAND + elif splitline[1] in self.power_cmd_list: + # We probably ran a CAP to execute a power command such as WSPOW0002A + # So insert the power command into the historical Backstop file you are building. + power_cmds = self.Process_Power_Cmd(eachevent) + + # Append the processed POWER commands + self.master_list = np.append(power_cmds, self.master_list, axis=0) + + # Sort the resultant master list - DO NOT REVERSE + self.master_list.sort(order='time') + + else: # NOT an LTCTI nor a Maneuver nor a power command + self.logger.info('NLET Processing, Event Detected: %s ' % (eachevent)) + + # Whether or not there were events tacked onto this assembly, + # move the self.end_event_time to the START of the assembly. + # If and when you append another continuity load, you will then + # check for events within the newly added section of the assembly + self.end_event_time = self.master_ToFC + + # The history has been assembled. Write it out to a + # file in the OFLS directory whose naming convention is: + # CR*.backstop.hist + filespec = os.path.join(ofls_dir, self.review_file_name+'.hist') + + self.logger.debug('Writing assembled history to: %s' % (filespec) ) + + # Set the History File Path attribute + self.assembled_hist_file_path = filespec + + self.Write_Commands_to_File(self.master_list, filespec, 'w', comment = None) + + # Return the final ASSEMBLED history which is contained in Master List + return self.master_list #------------------------------------------------------------------------------- # -# CombineTOO - Combine the Continuity backstop commands with the review load -# backstop commands, without overlap. -# -# Call this when you are reviewing a TOO load +# Process_MAN - process the submitted maneuver line from the NLET file # #------------------------------------------------------------------------------- - def CombineTOO(self, cont_bs_cmds, rev_bs_cmds): + def Process_MAN(self, nlet_event): """ - Combine the Continuity backstop commands with the review load - backstop commands, without overlap. The Continuity load is - cut at the time of the first command in the TOO load. If there - are commands whose execution time is precisely the cut time, they - will be kept and executed. The possible AOACRSTD pre-TOFC command - in the TOO load is placed appropriately. - - NOTE: By "review" load we mean the load being reviewed this week OR - a combination of one or more continuity loads with the load - being reviewed this week. This routine can be called multiple - times - tacking continuity loads to the start of the "master list" + Inputs: man_event - Event line from the NLET file, split on spaces, indicating + a maneuver + """ + # Split, on spaces, the maneuver event line read from the NLET Tracking file + man_event_list = nlet_event.split() + + # Extract pertinent data to meaningful names + MAN_date = man_event_list[0] + pitch = man_event_list[2] + roll = man_event_list[3] + q1 = man_event_list[4] + q2 = man_event_list[5] + q3 = man_event_list[6] + q4 = man_event_list[7] + + # If this is a legal maneuver, process it. + # Other LR code (history-files.pl) explicitly sets the pitch to 0.0 in + # the NLET file if the user did not or could not specify the 4 + # quaternions. The 4 Quaternion entries are set to a bogus value as + # well. Since we know the pitch ought not get below 45, and 0.0 is + # not possible then explicitly setting will work. It is converted + # from a string value - not a calculation. + if pitch != 0.0: - Call this when you are reviewing a TOO load or if a - continuity load was broken by a TOO load. + self.logger.info("NEW MANEUVER FOUND %s pitch: %s" % (MAN_date, str(pitch))) - INPUTS: Continuity load backstop file commands - Review Load Backstop file + # Create the time stamped scs-107 command set using MAN_date as the start time + processed_maneuver_cmds = self.Process_Cmds(self.raw_man_cmd_list, MAN_date) - NOTE: end_event_time is initialized by ATC to the end of the review load. + # Now insert the Quaternion values into the MP_TARGQUAT params section. + # The MP_TARGQUAT is the first of the two commands + # make a deep copy of the params section + x = processed_maneuver_cmds[0]['commands'] - OUTPUTS: Backstop commands of the combined Continuity and Review - loads. - """ - # First get the start and stop dates and times for the Review Load. - # Capture the Time of First Command from the rev_bs_cmds - Time_of_First_Command = rev_bs_cmds[0]['time'] + splitx = x.split('-0') + scs_step = x.split('00000004')[-1] - # Record the TOO Load Time of First Command (ToFC) - self.TOO_ToFC = Time_of_First_Command + splitx[1] = str(q1) + ',' + splitx[1].split(',')[1] + splitx[2] = str(q2) + ',' + splitx[2].split(',')[1] + splitx[3] = str(q3) + ',' + splitx[3].split(',')[1] + splitx[4] = str(q4) + ',' + splitx[4].split(',')[1] + scs_step - # Get all the Continuity commands up to and including the time of first command of - # the Review load - # NOTE: This will automatically take care of the fact that one or more of the first commands - # in the new load will come before the end of commands in the continuity load. - # - self.master_list = [cmd for cmd in cont_bs_cmds if cmd['time'] <= rev_bs_cmds[0]['time']] - - # Now concatenate the review load taking all the commands - # to the master list - self.master_list = self.master_list + rev_bs_cmds - # Sort the master list - self.master_list = sorted(self.master_list, key=lambda k: k['time']) - - # MASTER = Trimmed Continuity + Review Load. - - # Now scan the NLET file for any Event that occurs between the - # start of the continuity load and the end of the review load. - # For now we won't make this inclusive but subsequent - # new and exciting ideas on how to operate may make that necessary. - # So first search the NLET file for events - event_list = self.Find_Events_Between_Dates(self.master_list[0]['time'], self.end_event_time) - - # If there are events to process....... - if event_list != []: - # There are, so process them all - for eachevent in event_list: - # split the string on spaces - splitline = eachevent.split() - # If the event found is a LTCTI measurement... - if splitline[1] == 'LTCTI': - # ......process it feeding the routine the - # Review Load Time of First Command - self.Process_LTCTI(splitline, self.TOO_ToFC) - elif splitline[1] in self.power_cmd_list: - # We probably ran a CAP to execute a power command such as WSPOW0002A - # So insert the power command into the historical Backstop file you are building. - self.Process_Power_Cmd( splitline) - else: # Neither an LTCTI nor a power command - print('Detected:\n ', eachevent) - - # Sort the master list based upon time - self.master_list = sorted(self.master_list, key=lambda k: k['time']) - - # Move the end event time back to the beginning of the assembled history - self.end_event_time = self.master_list[0]['time'] + processed_maneuver_cmds[0]['commands'] = ''.join(splitx) + + else: # It's a bogus maneuver entry - the user didn't specify good Q's + self.logger.warning("Bogus Maneuver Entry! Quaternions badly specified: \n" + "Bad Q's: %g, %g, %g, %g " % (q1, q2, q3, q4) + + "...therefore bogus pitch and roll: %g, %g" % (pitch, roll)) - return self.master_list + # Return the list of processed maneuver commands + return processed_maneuver_cmds #------------------------------------------------------------------------------- # -# Process_LTCTI - process the submitted LTCTIline from the NLET file +# Process_LTCTI - process the submitted LTCTI line from the NLET file # #------------------------------------------------------------------------------- - def Process_LTCTI(self, ltcti_event, trim_time): + def Process_LTCTI(self, ltcti_event, trim_date): """ Inputs: ltcti_event - Event line from the NLET file, split on spaces, indicating a LTCTI entry - format: 2020:147:02:08:00 LTCTI 1527 1_4_CTI 000:16:00:00 + event start date type CAP # CLD file Length of LTCTI trim_date - date/time after which the continuity load has to be trimmed. This could be: NORMAL Review Load ToFC - which results in NO trimming @@ -666,148 +839,116 @@ def Process_LTCTI(self, ltcti_event, trim_time): to look for the first Stop Science command (AA00000000) that occurs AFTER the start of the LTCTI. """ - RTS_start_date = ltcti_event[0] - self.RTS.CAP_num = ltcti_event[2] - self.RTS.RTS_name = ltcti_event[3] - self.RTS.NUM_HOURS = ltcti_event[4] + # Split the NLET event line on spaces + ltcti_event_split = ltcti_event.split() + + RTS_start_date = ltcti_event_split[0] + CAP_num = ltcti_event_split[2] + RTS_name = ltcti_event_split[3] + NUM_HOURS = ltcti_event_split[4] - self.logger.info("\nLTCTI Measurement FOUND %s CAP # %s RTS file %s\n" % (RTS_start_date, str(self.RTS.CAP_num),self.RTS.RTS_name )) + # SCS NUm is the SCS slot specified in the FOR request. If you are not processing + # FOT requests then set it to 135 + SCS_NUM = 135 - # Process the specified RTS file and get a time-stamped numpy array of the data - ltcti_cmd_list = self.RTS.processRTS(self.RTS.RTS_name, self.RTS.SCS_NUM, self.RTS.NUM_HOURS, RTS_start_date) + self.logger.info("LTCTI Measurement Detected: %s \n" % (ltcti_event)) + + # Process the specified RTS file and get a time-stamped numpy array of the commands + ltcti_cmd_list = LTCTI_RTS.processRTS(RTS_name, SCS_NUM, NUM_HOURS, RTS_start_date) + + # Make a list of the needed commands from the mnemonic column of ltcti_cmd_list + raw_ltcti_cmd_list = [eachcmd for eachcmd in ltcti_cmd_list['mnemonic'] ] + + # Create the raw command array + LTCTI_cr_cmds = self.Process_Cmds(raw_ltcti_cmd_list, RTS_start_date) + + # Now Process_Cmds has a rudimentary numbering scheme - it increments the time + # by a dt and each successive command is dt seconds later. + # + # For Processing LTCTI this isn't good enough. However the array + # Returned by RTS.processRTS does have the correct timing so we execute + # a simple substitution + for eachRTScmd, eachCRcmd in zip(ltcti_cmd_list, LTCTI_cr_cmds): + # Modify the date and time columns + eachCRcmd['date'] = Time(eachRTScmd['date'], format = 'yday', scale = 'utc').yday + eachCRcmd['time'] = eachRTScmd['time'] + # Now modify the CR command string + eachCRcmd['commands'] = eachCRcmd['date'].ljust(22) + eachCRcmd['commands'][22:] - # Now convert the numpy array into SKA command format which is a list of dicts - LTCTI_bs_cmds = self.RTS.convert_ACIS_RTS_to_ska_parse(ltcti_cmd_list) + self.logger.debug('Nominal LTCTI start and stop dates: %s %s' % (LTCTI_cr_cmds[0]['date'], LTCTI_cr_cmds[-1]['date']) ) - # We need to find the first ACISPKT command in the review load that + self.logger.debug(LTCTI_cr_cmds) + + + # We need to find the first ACISPKT command in the master list that # comes after the start of the LTCTI first command, and is ALSO # a Stop Science ('AA00000000'). # IMPORTANT: The LTCTI run may have started in the Continuity load # but it will end either because it runs to completion with - # it's own Stop Science commands, OR a Stop Sciecne in the review load - # To do that we obtain the start time of the timed LTCTI command set - ltcti_cmd_list_start_time = CxoTime(RTS_start_date).secs + # it's own Stop Science commands, OR a Stop Science in the review load + # Obtain the start and end times of the timed LTCTI command set + + ltcti_cmd_list_start_time = LTCTI_cr_cmds[0]['time'] + ltcti_cmd_list_stop_time = LTCTI_cr_cmds[-1]['time'] - # Initialize the ACIS_specific_cmds as an empty array of DTYPE self.ACISPKT_dtype - ACIS_specific_cmds = np.array( [], dtype = self.ACIS_specific_dtype) - - # Next, collect all commands in the review backstop file which + # Next, make a list all ACISPKTcommands in the master list which # are between the LTCTI start time and the LTCTI stop time, inclusive - # - # Start by getting a copy of all the backstop file commands, directly - # from the official backstop file, which are pertinent to ACIS and - # this operation: ACISPKT, ORBPOINTS etc. - # - # Work your way backwards through self.backstop_file_path_list, except - # the first file which is the review load. Trim off any commands that - # occur after the STOP time.processing - # - for eachCR_file in self.backstop_file_path_list[:0:-1]: - # Read all the Continuity commands pertinent to ACIS filtered by get_ACIS_backstop_cmds - all_file_cmds = self.get_ACIS_backstop_cmds(eachCR_file) - # Trim the commands that occur on or after the trim date - filter_arr = all_file_cmds['event_time'] <= trim_time - ACIS_specific_cmds = np.append(ACIS_specific_cmds, all_file_cmds[filter_arr], axis=0) - - # At this point you have all the Continuity pertinent commands assembled. - # Now get the pertinent commands coming from the Review Load - all_file_cmds = self.get_ACIS_backstop_cmds(self.backstop_file_path_list[0]) - #....and append them - ACIS_specific_cmds = np.append(ACIS_specific_cmds, all_file_cmds, axis = 0) - - # Now find all review backstop commands which are a Stop Science and - # which occurs AFTER the start of the LTCTI run. - cut_cmd_indices = np.where( (ACIS_specific_cmds[:]['event_time'] >= ltcti_cmd_list_start_time) & \ - (ACIS_specific_cmds[:]['packet_or_cmd'] == 'AA00000000') ) - - # A maneuver-only load may NOT have any Stop Science commands in it. - # For example the May2620 Maneuver-Only load - # So check to see if you got a result for cut_cmd_indices. - # if you did then use it to determine whether or not you need to trim the - # LTCTI commands. If not, then the LTCI ran to completion - # If no stop sciences in the (maneruver-only) review load... - if cut_cmd_indices[0].size == 0: - print(' NO STOP SCIENCES IN REVIEW LOAD') - #...then the KTCTI runs to completion - trimmed_LTCTI_bs_cmds = LTCTI_bs_cmds - else: # Else there was a stop science found in the review load... - # Get the first AA00 from the REVIEW load - bs_load_stop_science = ACIS_specific_cmds[cut_cmd_indices[0][0]] - - # There was a stop science in the review load so trim any - # LTCTI CLD commands that occurred ON or AFTER the - # Return to Science Time of First Command. - # If the first AA00 in the Review load comes AFTER the AA00 in the LTCTI - # Command list, the LTCTI AA00 is preserved. - trimmed_LTCTI_bs_cmds = self.Trim_bs_cmds_After_Date(bs_load_stop_science['event_time'], LTCTI_bs_cmds) - - # Append the LTCTI commands to the Master list - # They are out of time order but will be sorted in the calling routine. - self.master_list += trimmed_LTCTI_bs_cmds + + # Make a copy of the master list + x = self.master_list -#------------------------------------------------------------------------------- -# -# Process_MAN - process the submitted maneuver line from the NLET file -# -#------------------------------------------------------------------------------- - def Process_MAN(self, man_event): - """ - Inputs: man_event - Event line from the NLET file, split on spaces, indicating - a maneuver - """ - MAN_date = man_event[0] - pitch = man_event[2] - roll = man_event[3] - q1 = man_event[4] - q2 = man_event[5] - q3 = man_event[6] - q4 = man_event[7] - - # If this is a legal maneuver, process it - if pitch != 0.0: + # Trim all commands prior to ltcti_cmd_list_start_time + x = self.Trim_bs_cmds_Before_Time(ltcti_cmd_list_start_time, x) - self.logger.info("NEW MANEUVER FOUND %s pitch: %s" % (MAN_date, str(pitch))) + # Trim all commands AFTER ltcti_cmd_list_stop_time + x = self.Trim_bs_cmds_After_Time(ltcti_cmd_list_stop_time, x) - # Now form and add the command (in SKA.Parse format - i.e. dict) which - # specifies the MP_TARGQUAT - new_maneuver = copy.deepcopy(self.MAN_bs_cmds) + self.logger.debug('Number of commands left in x after trim: %d' % ( len(x) ) ) - # Set the dates, times and Q's - new_maneuver['date'] = MAN_date - new_maneuver['params']['q1'] = float(q1) - new_maneuver['params']['q2'] = float(q2) - new_maneuver['params']['q3'] = float(q3) - new_maneuver['params']['q4'] = float(q4) - new_maneuver['time'] = CxoTime(MAN_date).secs + # x now contains any load commands that occured during the LTCTI run + # Find out if there are any Stop Sciences in the resultant trimmed list - # Tack the maneuver to the Master List - self.master_list.append(new_maneuver) + # If there are no commands left after the trim then you can use the entire + # LTCTI command sequence you've built. + if x.size == 0: + self.logger.info("LTCTI Measurement runs to completion\n" ) + else: + # There are load commands within the start and stop time of the LTCTI. + # They may or may not terminate the LTCTI before it's nominal run. + # Find those instances (if any) where a load command, within the trimmed + # set, is a Stop Science (AA0000000). If you find one then chop any + # LTCTI commands which come on or after that Stop Science. + # + # Collect any stop science commands within the trimmed + aa_instances = [cmd_index for cmd_index, eachcmd in enumerate(x) if 'AA00000000' in eachcmd['commands']] + + # If there are no Stop Science commands within the start and stop of the + # LTCTI run then th4e LTCTI runs to completion + if len(aa_instances) == 0: + self.logger.info("Load commands exist within the LTCTI but LTCTI Measurement runs to completion\n" ) + else: + self.logger.debug('LTCTI Interrupted at: %s with load command:\n %s' % (x[aa_instances[0]]['date'], x[aa_instances[0]]['commands'])) + # The LTCTI was terminated before completion. Remove all LTCTI commands + # on or before the Stop Science. Get th eindex of the terminating Stop Science. + cut_index = aa_instances[0] - else: # It's a bogus maneuver entry - the user didn't specify good Q's - self.logger.warning("Bogus Maneuver Entry! Quaternions badly specified: \n" - "Bad Q's: %g, %g, %g, %g " % (q1, q2, q3, q4) + - "...therefore bogus pitch and roll: %g, %g" % (pitch, roll)) + # record the cut time + ltcti_cut_time = x[cut_index]['time'] + ltcti_cut_date = x[cut_index]['date'] - # Create the AOMANUVR command and add it to the Master List. - # This command actually kicks the maneuver off. - # NOTE: This is done whether or not the maneuver data was good. - # It allows the loop to continue looking for maneuvers - aoman = copy.deepcopy(self.AOMANUVR_bs_cmd) - aoman_time = CxoTime(MAN_date).secs + 1 - aoman_date = CxoTime(aoman_time).date - aoman['date'] = aoman_date - aoman['time'] = aoman_time - # Tack the maneuver to the Master List - # Tacking the command to the Master list doesn't really do much if there - # is no AOUPTARQ command. But it allows you to search for subsequent maneuver - # commands - - # Append the commands for this maneuver to the Master List - self.master_list.append(aoman) + # Now trim the sequence of ltcti commands + LTCTI_cr_cmds = self.Trim_bs_cmds_After_Time(ltcti_cut_time, LTCTI_cr_cmds) - #------------------------------------------------------------------------------- + # Tell the user that the LTCTI was cut short + self.logger.info("LTCTI Measurement Cut Short at %s %s\n" % (ltcti_cut_date, ltcti_cut_time)) + + # They are out of time order but will be sorted in the calling routine. + return LTCTI_cr_cmds + + + #-------------------------------------------------------------------------------eachcmd['date'] # - # Process_MAN - process the submitted maneuver line from the NLET file + # Process_Power_Cmd # #------------------------------------------------------------------------------- def Process_Power_Cmd(self, power_cmd_event): @@ -823,339 +964,111 @@ def Process_Power_Cmd(self, power_cmd_event): NOTE: If you get to this method, the command has already been checked for existence. So you can rest assured that the necessary data structurs exist """ + # split the command on spaces + power_cmd_list = power_cmd_event.split() + # Log that you see it - self.logger.info("\nNEW Power Command Found %s power cmd: %s" % (CxoTime(power_cmd_event[0]).date, power_cmd_event[1]) ) - # Process the power command. Make a copy of the appropriate power command attribute - if power_cmd_event[1] == 'WSVIDALLDN': - new_pwr_cmd = copy.deepcopy(self.WSVIDALLDN_bs_cmd) - elif power_cmd_event[1] == 'WSPOW00000': - new_pwr_cmd = copy.deepcopy(self.WSPOW00000_bs_cmd) - else: # It can only be a POW2a if you get here - new_pwr_cmd = copy.deepcopy(self.WSPOW0002A_bs_cmd) + self.logger.info("Power Command Detected %s power cmd: %s" % (power_cmd_list[0], power_cmd_list[1]) ) - # Next insert the date and tie which comes from the NLET line - new_pwr_cmd['date'] = CxoTime(power_cmd_event[0]).date - new_pwr_cmd['time'] = CxoTime(power_cmd_event[0]).secs + # Create the time stamped power command set using date in the NLET line as the start time + new_power_cmd = self.Process_Cmds([power_cmd_list[1]], power_cmd_list[0]) - # Now append this power command to the Master List. - self.master_list.append(new_pwr_cmd) + return new_power_cmd + #------------------------------------------------------------------------------- + # + # Process_Cmds + # + #------------------------------------------------------------------------------- + def Process_Cmds(self, cmd_list, BEGIN_date): + """ + This method will take a list of command names and a starting time stamp and generate + an array of commands with the date string of the command, the date and the time + columns all updated + Input: cmd_list - list of strings showing the command sequence + e.g. [ 'SIMTRANS', 'AA00000000', 'AA00000000', 'WSPOW0002A'] -#------------------------------------------------------------------------------- -# -# CombineSTOP - Combine the Continuity backstop commands with the review load -# backstop commands, when both the Vehicle and Science loads have -# been stopped. -# -#------------------------------------------------------------------------------- - def CombineSTOP(self, cont_bs_cmds, rev_bs_cmds, shutdown_date): + BEGIN_date - SCS-107/STOP or sequence start date(s) in Chandra DOY format - # Convert and record shutdown date to seconds: - self.STOP_time = CxoTime(shutdown_date).secs + Output: processed_cmd_array + - array of commands adjusted to the given BEGIN date + """ + # Make a list of the actual command strings, in raw form, + # for each command in the cmd_list using the CR_cmds dictionary + raw_cmds = [self.CR_cmds[eachcmd] for eachcmd in cmd_list] - # Trim the Continuity commands list to include only those - # commands whose excution time is before the shutdown time - self.master_list = [cmd for cmd in cont_bs_cmds if (cmd['time'] < self.STOP_time) ] + # Calculate the times and the dates: split the command string on spaces, calculate the time and add the date + cmd_times = [round(Time(eachdate.split()[0], format = 'yday', scale = 'utc').cxcsec,1) for eachdate in raw_cmds] + cmd_dates = [eachdate.split()[0] for eachdate in raw_cmds] - # - # IMPORTANT: At this point, self.master_list should consist ONLY of - # the continuity load TRIMMED to the STOP time + # Now make a numpy array with two columns: cmd strings and times + cmd_array = np.array(list(zip(raw_cmds, cmd_times, cmd_dates)) , dtype = self.CR_DTYPE) - # When the Spacecraft does a Full Stop, an SCS-107 is excuted. - # So make a copy of the SCS-107 commands and populate the times. These - # are: SIMTRANS, AA00, AA00, WSPOW. Then concatenate the 107 commands - # to the master list - scs107_bs_cmds = copy.deepcopy(self.scs107_bs_cmds) + # Now substitute the dates and times in the command portion and the + # time column of the array # The starting time for the first scs107 command will be at the stop time + # obtained from the ACIS-Continuity.dat file. Run Time on the date string + # to arrange for 3 places to the right of seconds. + base_date = Time(BEGIN_date, format = 'yday', scale = 'utc').yday + base_time = Time(BEGIN_date, format = 'yday', scale = 'utc').cxcsec - # 1 second after the last - # command in the TRIMMED master list - base_time = self.STOP_time + # Each subsequent time will be 1 second after the last + dt = 1.0 - # populate the date and time slots of each command incrementing the times + # Populate the date and time slots of each command incrementing the times # by one second - for eachcmd in scs107_bs_cmds: + for eachcmd in cmd_array: + eachcmd['date'] = base_date eachcmd['time'] = base_time - eachcmd['date'] = CxoTime(base_time).date - - # Increment the base time by one second - base_time += 1.0 - - # Now concatenate scs107 commands specific to this event to the master list - # which has been trimmed to include only those commands that executed prior to - # the shutdown. No need to sort these at this point - self.master_list += scs107_bs_cmds - - # MASTER LIST = Trimmed Continuity + SCS-107 commands - - # Now we need to process any events that appear in the NonLoadEventTracker.txt - # file whose times are after the continuity load start time, but before the subsequent - # STOP time of the review load. These events include: - # - # "MAN" includes: - # NSM - pitch change to 90 degrees: ALL Stop - # BSH - stuck at some pitch - ALL Stop - # OCC Pitch Maneuver - Move to a new pitch. - # - # Long Term CTI events (LTCTI) - # - # So first search the NLET file for events between the start of the Continuity load - # and the end of the Review Load. - event_list = self.Find_Events_Between_Dates(self.master_list[0]['time'], self.end_event_time) - - # If there are events to process....... - if event_list != []: - # There are, so process them all - for eachevent in event_list: - # split the string on spaces - splitline = eachevent.split() - # If this is a MANEUVER, event, process it and add it to the Master List - if splitline[1] == 'MAN': - self.Process_MAN(splitline) - elif splitline[1] == 'LTCTI': - # Since this is a LTCTI, process it feeding the routine the - # Review Load Time of First Command - self.Process_LTCTI(splitline, self.STOP_time) - elif splitline[1] in self.power_cmd_list: - # We probably ran a CAP to execute a power command such as WSPOW0002A - # So insert the power command into the historical Backstop file you are building. - self.Process_Power_Cmd( splitline) - else: - print('Detected:\n ', eachevent) - - # MASTER LIST = Trimmed Continuity + - # SCS-107 commands + - # Any Maneuvers and/or LTCTI's that occurred - - # Finally, concatenate the review load tacking all the commands - # to the master list. - # NOTE: In subsequent calls to this method the TOFC of rev_bs_cmds will be - # the start of the *assembled* load history. Only in the case of the first - # call is the TOFC of rev_bs_cmds also the TOFC of the actual Review Load. - newlist = self.master_list + rev_bs_cmds - - # MASTER LIST = Trimmed Continuity + - # SCS-107 commands + - # Any Maneuvers and/or LTCTI's that occurred + - # Review Load - - # sort the master list based on time so tht events occur at the correct - # moment of time. - self.master_list = sorted(newlist, key=lambda k: k['time']) - - # Move the end event time back to the beginning of the assembled history - self.end_event_time = self.master_list[0]['time'] - - # Now return the sorted master list which contains - return self.master_list - -#------------------------------------------------------------------------------- -# -# Combine107 - Combine the Continuity backstop commands with the review load -# backstop commands, without overlap and without including any -# command after the specified SCS-107 time -# -# Call this when you are reviewing an SCS-107 load -# -#------------------------------------------------------------------------------- - def Combine107(self, cont_bs_cmds, vo_bs_cmds, rev_bs_cmds, shutdown_date): - """ - Combine the Continuity backstop commands with the review load - backstop commands, without overlap IN THE SCIENCE LOAD and without including any - SCIENCE command after the specified SCS-107 time. The Continuity load is - cut at the time of shutdown. - - THe SCS 107 commands are inserted after the time of shutdown - LTCTI is looked for in the NLET file and if it exists, it is inserted - into the history + # Substitute the updated date into the command string + eachcmd['commands'] = base_date.ljust(22) + eachcmd['commands'][22:] - There is a check for the AOACRSTD command in the TOO load and it's - placed accordingly via a sort. - - Steps: - - 1) Trim all commands from the continuity load on and after the - SCS-107 interrupt time. - - 2) Add in the SCS-107 Shutdown commands (SIMTRANS, AA, AA, WSPOW) - - 3) Check for any LTCTI's. If they exist, add them to the master list. - - 4) Trim the beginning of the VO load to eliminate that part of the - continuity load you are keeping. - - 5) Trim the back end of the VR file to the Review load start time - - 6) Concatenate the VO remnant to the assembled continuity. - - 7) Concatenate the Review Load - - NOTE: By "review" load we mean the load being reviewed this week OR - a combination of one or more continuity loads with the load - being reviewed this week. This routine can be called multiple - times - tacking continuity loads to the start of the "master list" - - Call this when you are reviewing a normal week-to-week load - - INPUTS: Continuity load backstop file commands - Review Load Backstop file - Time of Shutdown - - OUTPUTS: Backstop commands of the combined Continuity and Review - loads. - """ - # Convert shutdown date to seconds: - self.S107_time = CxoTime(shutdown_date).secs - - # STEP 1 - - # Trim the Continuity commands list to include only those commands whose excution - # time is less than the time of first command of the Review load (or assembled bs list) - self.master_list = [cmd for cmd in cont_bs_cmds if (cmd['time'] < self.S107_time)] - - # MASTER LIST = Trimmed Continuity - - # Capture the end of the master list (trimmed continuity ) - # so that you can use it later for the Verhicle Only Load Cut. You want it to - # be right after the SCS-107 time. But we are adding in any LTCTI runs first. - # That would move the end time of master list too far. The same thing would happen - # to the LTCTI if we swapped them in time. - vo_cut_date = self.master_list[-1]['date'] - - # STEP 2 SCS-107 SIMTRANS AND STOP - - # Now make a copy of the SCS-107 commands and populate the times. These - # are: SIMTRANS, AA00, AA00, WSPOW. Then concatenate the 107 commands - # to the master list - scs107_bs_cmds = copy.deepcopy(self.scs107_bs_cmds) - # The starting time for the first scs107 command will be 1 second after the last - # command in the TRIMMED master list - base_time = CxoTime(self.S107_time).secs + 1 - - # populate the date and time slots of each command incrementing the times by one second - for eachcmd in scs107_bs_cmds: - eachcmd['time'] = base_time - eachcmd['date'] = CxoTime(base_time).date # Increment the base time by one second - base_time += 1.0 - - # Now concatenate scs107 commands specific to this event to the master list - # which has been trimmed to include only those commands that executed prior to - # the shutdown. No need to sort these at this point, because the base time was - # the last time of the trimmed continuity load plus 1 second. - self.master_list += scs107_bs_cmds - - # MASTER LIST = Trimmed Continuity + SCS-107 COMMANDS + base_time += dt - # STEP 3 - self.logger.info("STEP - Process any and all events between: %s - %s" % (self.master_list[-1]['date'],rev_bs_cmds[0]['date'])) + # Convert the new time to yday format + base_date = Time(base_time, format = 'cxcsec', scale = 'utc').yday - # Now we need to process any events that appear in the NonLoadEventTracker.txt - # file whose times are after the stop time, but before the subsequent - # start science of the review load. The only events non-load events - # that canoccur are Long Term ECS measurements - # - # So first search the NLET file for any LTECS events between the start - # of the Continuity load and the end of the REview Load - event_list = self.Find_Events_Between_Dates(self.master_list[0]['time'], self.end_event_time) - - # If there are events to process....... - if event_list != []: - # There are, so process them all - for eachevent in event_list: - # split the string on spaces - splitline = eachevent.split() - # If the event found is a LTCTI measurement... - if splitline[1] == 'LTCTI': - # .....process it feeding the routine the - # Review Load Time of First Command - self.Process_LTCTI(splitline, self.S107_time) - elif splitline[1] in self.power_cmd_list: - # We probably ran a CAP to execute a power command such as WSPOW0002A - # So insert the power command into the historical Backstop file you are building. - self.Process_Power_Cmd( splitline) - else: - print('Detected:\n ', eachevent) - - - # MASTER LIST = Trimmed Continuity + SCS-107 COMMANDS + ANY LTCTI's + ANY INDIVIDUAL POWER COMMANDS. - - - # Trim the Vehicle Only command list by removing vo commands occuring - # prior to the stop date. - vo_bs_cmds_trimmed = self.Trim_bs_cmds_Before_Date(vo_cut_date, vo_bs_cmds) - - # STEP 5 - - # Trim all commands in the VO list that occur AFTER the first command - # of the return to science load - vo_bs_cmds_trimmed = self.Trim_bs_cmds_After_Date(rev_bs_cmds[0]['time'], vo_bs_cmds_trimmed) - - # STEP 6 - - # Concatenate the trimmed VO list - self.master_list += vo_bs_cmds_trimmed - - # MASTER LIST = Trimmed Continuity + - # SCS-107 COMMANDS + - # ANY LTCTI's + - # ANY POWER CMDS + - # VEHICLE ONLY CMDS - - # Finally, concatenate the review load tacking all the commands - # to the master list. - # NOTE: In subsequent calls to this method the TOFC of rev_bs_cmds will be - # the start of the *assembled* load history. Only in the case of the first - # call is the TOFC of rev_bs_cmds also the TOFC of the actual Review Load. - newlist = self.master_list + rev_bs_cmds - - - # MASTER LIST = Trimmed Continuity + - # SCS-107 COMMANDS + - # ANY LTCTI's + - # VEHICLE ONLY CMDS + - # Review Load - - # sort the master list based on time so that events occur at the correct - # moment of time. - self.master_list = sorted(newlist, key=lambda k: k['time']) - - # Move the end event time back to the beginning of the assembled history - self.end_event_time = self.master_list[0]['time'] - - # Now return the sorted master list which contains - return self.master_list + # Return the time stamped scs-107 command set + return cmd_array - #------------------------------------------------------------------------------- + #-------------------------------------------------------------------------- # - # WriteCombinedCommands - Write the combined commands to a file + # WriteCommands to file - Write the specified commands to a file # - #------------------------------------------------------------------------------- - def WriteCombinedCommands(self, cmd_list, outfile_path, comment = ''): + #-------------------------------------------------------------------------- + def Write_Commands_to_File(self, cmd_list, outfile_path, outfile_mode, comment = None): """ - This method will write the command list out into a file whose path is specified - in outfile_path. Whether or not this is an original command list or a combined - one it immaterial. + This method will write the specified command list out to a file whose path is specified + in outfile_path. + + The commands are either written to a new file or appended to an existing file as + indicated by outfile_mode - INPUTS: command list - full output file specification + Whether or not this is an original command list or a combined one it immaterial. + + INPUTS: command list - table fo commands + outfile_path - full output file specification + outfile_mode - "w" or "a" + comment - Comment to be written out to the file OUTPUTS: Nothing returned; file written. """ # Open up the file for writing - combofile = open(outfile_path, "w") + combofile = open(outfile_path, outfile_mode) - # Now output pertinent info from the command list. + # Now output pertinent info from the command list. Make it look like the + # original CR backstop command file but add the extra info such as time + # at the end after the double bar for eachcmd in cmd_list: - if eachcmd['cmd'] != 'GET_PITCH': - cmd_line = eachcmd['date'] + ' | '+ eachcmd['cmd']+ ' | '+ eachcmd['paramstr'] - combofile.write(cmd_line+'\n') + combofile.write(eachcmd['commands']+ '\n') - # Output the comment whatever it is - combofile.write('\nComment: '+comment+'\n') + # Output the comment if one was given + if comment is not None: + combofile.write('\nComment: '+comment+'\n') # Done with the file; close it. combofile.close() @@ -1165,80 +1078,80 @@ def WriteCombinedCommands(self, cmd_list, outfile_path, comment = ''): # Trim_bs_commands_After_Date - Given a list of backstop commands, remove any command # prior to the specified time # - # INPUTS: Chandra date or time (either is ok) + # INPUTS: Chandra time # List of backstop commands. # # OUTPUTS: Trimmed list of backstop commands #------------------------------------------------------------------------------- - def Trim_bs_cmds_After_Date(self, trim_date, cmd_list): + def Trim_bs_cmds_After_Time(self, trim_time, cmd_list): """ - Given a list of backstop commands, remove any command ON or *AFTER* the specified time - - This can be confusing - you want to throw away all those commands that occur - ON or AFTER the specified time. You want to keep everything that occurs BEFORE the - cutoff time. + Given an astropy table of backstop commands, remove any command on + or *AFTER* the specified time - INPUTS: Chandra date or time (either is ok) + INPUTS: Trim time in Chandra time (Chandra Seconds) List of backstop commands. - OUTPUTS: Trimmed list of backstop commands + OUTPUTS: The trimmed input table - NOTE: John Scott assures me that they try to schedule the first - command of the new load to be either between loads or in between two - commands in the old load. Also, they will not lengthen an observation - by having the stop science come much later than it did in the broken load. - They tried that once and there were myriad complaints. + NOTE: The input table is modified """ - # Convert the date into seconds. If it was already seconds this will not hurt. - trim_time = CxoTime(trim_date).secs + # Can only trim if the specified trim time is within the cmd_list + # first time and last time + if (trim_time >= cmd_list[0]['time']) and \ + (trim_time <= cmd_list[-1]['time']): + # Get the index of the first row whose time value is greater than trim time + cut_index = np.where(cmd_list['time'] > trim_time)[0][0] - # Now trim the list by including only those backstop commands that occur after the time. - trimmed_list = [cmd for cmd in cmd_list if cmd['time'] < trim_time] + # Remove all rows starting from that index to the end of the list + cmd_list = np.delete(cmd_list, slice(cut_index, len(cmd_list)), axis = 0) + # ....otherwise you will return the untouched command list - # Return the trimmed list - return trimmed_list + # Return the possibly trimmed command list + return cmd_list #------------------------------------------------------------------------------- # - # Trim_bs_cmds_Before_Date - Given a list of backstop commands, remove any command + # Trim_bs_cmds_Before_Date - Given a list of backstop commands, + # remove any command on or # prior to the specified time # - # INPUTS: Chandra date or time (either is ok) + # INPUTS: Chandra time in seconds # List of backstop commands. # # OUTPUTS: Trimmed list of backstop commands #------------------------------------------------------------------------------- - def Trim_bs_cmds_Before_Date(self, trim_date, cmd_list): + def Trim_bs_cmds_Before_Time(self, trim_time, cmd_list): """ - Given a list of backstop commands, remove any command *PRIOR* to the specified time + Given an astropy table of backstop commands, remove any command + *PRIOR* to or on the specified time - This can be confusing - you want to throw away all those commands that occur BEFORE - the specified time. You want to keep everything that occurs ON or AFTER the - cutoff time. + INPUTS: trim time in Chandra time (Chandra Seconds) - INPUTS: Chandra date or time (either is ok) List of backstop commands. - OUTPUTS: Trimmed list of backstop commands + OUTPUTS: The trimmed input table - NOTE: John Scott assures me that you want to do a "less than" comparison - on times and not less than or equal. Also they try to schedule the first - command of the new load to be either between loads or in between two - commands in the old load. Also, they will not lengthen an observation - by having the stop science come much later than it did in the broken load. - They tried that once and there were myriad complaints. + NOTE: The input table is modified, so unless you want the master + list trimmed, don't send that one in. """ - # Convert the date into seconds. If it was already seconds this will not hurt. - trim_time = CxoTime(trim_date).secs + # Can only trim if the specified trim time is within the cmd_list + # first time and last time + if (trim_time >= cmd_list[0]['time']) and \ + (trim_time <= cmd_list[-1]['time']): + + # Get the index of the first row whose time value is greater than trim time + cut_index = np.where(cmd_list['time'] < trim_time )[0][-1] + + # Remove all rows starting from index 0 to the specified index + cmd_list = np.delete(cmd_list, slice(0, cut_index+1), axis = 0) - # Now trim the list by KEEPING only those backstop commands that occur AFTER the time. - trimmed_list = [cmd for cmd in cmd_list if cmd['time'] >= trim_time] + # ....otherwise you will return the untouched command list # Return the trimmed list - return trimmed_list + return cmd_list #------------------------------------------------------------------------------- @@ -1262,7 +1175,7 @@ def Trim_bs_cmds_Before_Date(self, trim_date, cmd_list): # The January 30th load. # #------------------------------------------------------------------------------- - def BackChain(self, base_load_dir, chain_length): + def get_BackChain_List(self, base_load_dir, chain_length): """ Given a full base load directory, and a chain length, this method will successively backtrack through the @@ -1297,7 +1210,8 @@ def BackChain(self, base_load_dir, chain_length): # Extract the base load week from the full path # This is entered in the first column of the resultant array - base_load_week = os.path.split(base_load_dir)[-2] + base_load_week = os.path.split(base_load_dir)[0].split('/')[-1] + # Extract the continuity info from the Load week. # REMEMBER: this information is with regard to the # input load. It's the Continuity file that leads to the @@ -1324,9 +1238,9 @@ def BackChain(self, base_load_dir, chain_length): # Load up an entry in the array for this load load_chain = np.r_[load_chain, np.array( [ (base_load_week, - continuity_load_path, - continuity_info[1], - continuity_info[2]) ], + continuity_info[1], + continuity_info[2], + continuity_load_path) ], dtype = self.cont_dtype) ] # Try to do it again @@ -1346,8 +1260,8 @@ def BackChain(self, base_load_dir, chain_length): # # Find_Events_Between_Dates - Given a path to a Non Load Event Tracking file, # a start time and a stop time, search the Tracking - # file for any NLET event that - # occurred between the start and stop times. + # file for any NLET event that occurred between the + # start and stop times. # #------------------------------------------------------------------------------- def Find_Events_Between_Dates(self, tstart, tstop): @@ -1362,14 +1276,15 @@ def Find_Events_Between_Dates(self, tstart, tstop): The path to the Non Load Event Tracking file (NLET) is a constructor argument so that users can have their own version of the file. However the format of the file is fixed and this method expects a certain format. + + tstart and tstop are expected to be in Chandra seconds """ # Initialize and empty Event List event_list = [] # Convert the input tstart and tstop to seconds - this allows the # user to input either seconds or DOY format - whichever is more # convenient. - tstart = CxoTime(tstart).secs - tstop = CxoTime(tstop).secs + # The Non Load Event Tracking file is an input so that different # users of this module can have different NLET files. nletfile = open(self.NLET_tracking_file_path, 'r') @@ -1394,9 +1309,10 @@ def Find_Events_Between_Dates(self, tstart, tstop): # # Split the line splitline = nletline.split() + if (splitline[0] != 'GO') and \ - (CxoTime(splitline[0]).secs > tstart) and \ - (CxoTime(splitline[0]).secs < tstop): + (Time(splitline[0], format='yday', scale='utc').cxcsec > tstart) and \ + (Time(splitline[0], format='yday', scale='utc').cxcsec < tstop): # We have found an event. append it to the list while # removing the \n at the end of the string @@ -1492,7 +1408,7 @@ def get_ACIS_backstop_cmds(self, infile): # the time in seconds, insert the command type, and the mnemonic ACIS_specific_cmds = np.r_[ACIS_specific_cmds, np.array( [ ( packet_time, - CxoTime(packet_time).secs, + Time(packet_time, format = 'yday', scale = 'utc').cxcsec, cmd_type, cmd) ], dtype = self.ACIS_specific_dtype) ] @@ -1503,187 +1419,31 @@ def get_ACIS_backstop_cmds(self, infile): # Return the backstop command array return ACIS_specific_cmds - - - #------------------------------------------------------------------------------- + ############################################################################ # - # write_back_chain_to_pickle - Given a backchain, write the contents of the - # backchain to a pickle file. If this is ACIS format - # the full path to the OFLS directory is written out. + # Attribute Sets and Gets # - #------------------------------------------------------------------------------- - def write_back_chain_to_pickle(self, inst_format='ACIS', - file_path='/home/gregg/DPAMODEL/dpa_check/Chain.p', chain=None): - """ - Given a backchain, and a file path, write the contents of the backchain to a pickle file. - If the "inst_format" argument is "ACIS' then the full path to the ACIS ofls directory is - written out. If the inst_format is anything except ACIS, then the load week - (e.g. JAN0917) is extracted from the second column in the backchain array and only that is - written out. - - The latter option allows users with their own backstop tarball directory structure to - utilize the backchain and obtain backstop command files. - - The inst_format of the backchain must be a numpy array whose DTYPE is self.cont_dtype + ############################################################################ - Note that the results of Backchain can be an enpty array. If so, the resultant pickle - file will be empty. - """ - # If the inst_format is ACIS then write out the array as is - meaning that the - # 'cont_file' column will specify a full path such as: - # '/data/acis/LoadReviews/2017/OCT0917/ofls' - if inst_format == 'ACIS': - with open(file_path, 'w') as outfile: - chain.dump(outfile) - outfile.close() - else: # Just write out the continuity week name from the second column (e.g. OCT0917) - # Create an empty general array of detype self.cont_dtype - gen_chain = np.array( [], dtype = self.cont_dtype) - - # Run through the chain, modify the second column, and append it to the general - # array - for eachload in chain: - # Extract just the load week from the full continuity path - continuity_week = eachload[1].split('/')[-2] - - # Append the entry in the array for this load - gen_chain = np.r_[gen_chain, - np.array( [ (eachload[0], # Base load - continuity_week, # Continuity load name - eachload[2], # Base load type - eachload[3]) ], # Interrupt time or None - dtype = self.cont_dtype) ] - # Now you've created the generalized load array so dump that - with open(file_path, 'w') as outfile: - gen_chain.dump(outfile) - outfile.close() - - #------------------------------------------------------------------------------- + #--------------------------------------------------------------------------- # - # write_back_chain_to_txt - Given a backchain, write the contents of the - # backchain to a text file. If this is ACIS inst_format - # the full path to the OFLS directory is written out. + # get_review_tstart # - #------------------------------------------------------------------------------- - def write_back_chain_to_txt(self, inst_format='ACIS', - file_path='/home/gregg/DPAMODEL/dpa_check/Chain.txt', chain=None): + #--------------------------------------------------------------------------- + def get_review_tstart(self,): """ - Given a backchain, and a file path, write the contents of the backchain to a text file. - If the "inst_format" argumen is "ACIS' then the full path to the ACIS ofls directory is - written out. If the inst_format is anything except ACIS, then the load week - (e.g. JAN0917) is extracted from the second column in the backchain array and only that is - written out. - - The latter option allows users with their own backstop tarball directory structure to - utilize the backchain and obtain backstop command files. - - The inst_format of the backchain must be a numpy array whose DTYPE is self.cont_dtype - - Note that the results of Backchain can be an enpty array. If so, the resultant pickle - file will be empty. + Return the contents of attribute: review_file_tstart """ - # If the inst_format is ACIS then write out the array as is - meaning that the - # 'cont_file' column will specify a full path such as: - # '/data/acis/LoadReviews/2017/OCT0917/ofls' - if inst_format == 'ACIS': - np.savetxt(file_path, chain, fmt = '%s', newline = '\n') - - else: # Just write out the continuity week name from the second column (e.g. OCT0917) - # Create an empty general array of detype self.cont_dtype - gen_chain = np.array( [], dtype = self.cont_dtype) - - # Run through the chain, modify the second column, and append it to the general - # array - for eachload in chain: - # Extract just the load week from the full continuity path - continuity_week = eachload[1].split('/')[-2] - - # Append the entry in the array for this load - gen_chain = np.r_[gen_chain, - np.array( [ (eachload[0], # Base load - continuity_week, # Continuity load name - eachload[2], # Base load type - eachload[3]) ], # Interrupt time or None - dtype = self.cont_dtype) ] - # Now you've created the generalized load array so dump that - np.savetxt(file_path, gen_chain, fmt = '%s', newline = '\n') + return self.review_file_tstart - #------------------------------------------------------------------------------- + #--------------------------------------------------------------------------- # - # read_back_chain_from_txt - Given a path to a txt file written by - # write_back_chain_to_txt, read the contents of the - # text file and store it in the specified array - # variable. + # get_review_tstop # - #------------------------------------------------------------------------------- - def read_back_chain_from_txt(self, file_path='/home/gregg/DPAMODEL/dpa_check/Chain.txt'): - """ - Given a path to a txt file written by write_back_chain_to_txt, - read the contents of the text file and store it in an array - using DTYPE: self.cont_dtype - """ - # Read the file - chain = np.loadtxt(file_path, self.cont_dtype) - # Return the array - return chain - - #------------------------------------------------------------------------------- - # - # method set_backstop_lists - Given the name of a weekly load (e.g. MAR2717A) - # and the name of the backstop file for that load, - # insert the load name into the beginning of the class - # attribute list: self.load_list and the backstop file - # name at the beginning of the self.backstop_list - # - #------------------------------------------------------------------------------- - def set_backstop_lists(self, load_week, backstop_name, load_type): - """ - Given the name of a weekly load (e.g. MAR2717A) - and the name of the backstop file for that load, - insert the load name into the beginning of the class - attribute list: self.load_list and the backstop file - name at the beginning of the self.backstop_list - - The idea here is to maintain a history of the files and directories - you used to create the set of backstop commands. - - After being used once, the lists should be cleared by calling - self.clear_backstop_lists if you are running two or more histories - in one program - """ - if load_week is not None: - self.load_list.insert(0, load_week) - - if backstop_name is not None: - self.backstop_list.insert(0, backstop_name) - - if load_type is not None: - self.load_type_list.insert(0, load_type) - - #------------------------------------------------------------------------------- - # - # method clear_backstop_lists - Clear out the load and backstop file history lists - # - #------------------------------------------------------------------------------- - def clear_backstop_lists(self): - """ - Clearing out self.load_list and self.backstop_list - """ - del self.load_list[:] - del self.backstop_list[:] - del self.load_type_list[:] - - #------------------------------------------------------------------------------- - # - # method print_backstop_lists - Print the load week, load type and backstop file - # name history lists - # - #------------------------------------------------------------------------------- - def print_backstop_lists(self): + #--------------------------------------------------------------------------- + def get_review_tstop(self,): """ - Print out self.load_list and self.backstop_list + Return the contents of attribute: review_file_tstop """ - print(self.load_list) - print(self.backstop_list) - print(self.load_type_list) + return self.review_file_tstop diff --git a/backstop_history/CR_Commands.dat b/backstop_history/CR_Commands.dat new file mode 100644 index 0000000..96c78f8 --- /dev/null +++ b/backstop_history/CR_Commands.dat @@ -0,0 +1,43 @@ + +{"SIMTRANS": "1999:001:00:00:00.000 | 20 0 | SIMTRANS | POS= 75624, SCS= 107, STEP= 1", + "MP_TARGQUAT":"1999:001:00:00:00.000 | 20 0 | MP_TARGQUAT | TLMSID= AOUPTARQ, CMDS= 8, Q1= -0.00000001, Q2= -0.00000002, Q3= -0.00000003, Q4= -0.00000004, SCS= 107, STEP= 12", + "AOMANUVR": "1999:001:00:00:00.000 | 20 0 | COMMAND_SW | TLMSID= AOMANUVR, HEX= 8034101, MSID= AOMANUVR, SCS= 107, STEP= 2", + + "XTZ0000005": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= XTZ0000005, CMDS= 4, WORDS= 4, PACKET(40)= D8000040004003A000E000400000 , SCS= 107, STEP= 1", + "XCZ0000005": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= XCZ0000005, CMDS= 4, WORDS= 4, PACKET(40)= D800004000400270010000400000 , SCS= 107, STEP= 1", + "RS_0000001": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= RS_0000001, CMDS= 3, WORDS= 3, PACKET(40)= D80000300030042002100 , SCS= 131, STEP= 1", + "RH_0000001": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= RH_0000001, CMDS= 3, WORDS= 3, PACKET(40)= D80000300030044002300 , SCS= 107, STEP= 1", + "AA00000000": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= AA00000000, CMDS= 3, WORDS= 3, PACKET(40)= D80000300030603001300 , SCS= 107, STEP= 1", + + "WSVIDALLDN": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= WSVIDALLDN, CMDS= 4, WORDS= 5, PACKET(40)= D800005000506050020000000000 , SCS= 107, STEP= 1", + "WT007AC024": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= WT007AC024, CMDS= 87, WORDS= 150, PACKET(40)= D80009600962D7C00090004308AC024007A10730 , SCS= 107, STEP= 1", + "WT007AE024": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= WT007AE024, CMDS= 87, WORDS= 150, PACKET(40)= D80009600962D80000900046541E024007A65790 , SCS= 107, STEP= 1", + "WT00B26014": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= WT00B26014, CMDS= 87, WORDS= 150, PACKET(40)= D80009600963194000900049271601400B210730 , SCS= 107, STEP= 1", + "WT00C60014": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= WT00C60014, CMDS= 87, WORDS= 150, PACKET(40)= D8000960096381600090004C357001400C6237A0 , SCS= 107, STEP= 1", + "WT00C62014": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= WT00C62014, CMDS= 87, WORDS= 150, PACKET(40)= D8000960096381A00090004E3C1201400C6237A0 , SCS= 107, STEP= 1", + "WT00CA8014": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= WT00CA8014, CMDS= 87, WORDS= 150, PACKET(40)= D800096009639CF0009000405F5801400CA657A0 , SCS= 107, STEP= 1", + + "WT00D7C014": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= WT00D7C014, CMDS= 87, WORDS= 150, PACKET(40)= D000000000000000009000000000000000000000 , SCS= 107, STEP= 1", + "WT00D7E014": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= WT00D7E014, CMDS= 87, WORDS= 150, PACKET(40)= D000000000000000009000000000000000000000 , SCS= 107, STEP= 1", + "WT00D78014": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= WT00D78014, CMDS= 87, WORDS= 150, PACKET(40)= D000000000000000009000000000000000000000 , SCS= 107, STEP= 1", + "WT00D7A014": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= WT00D7A014, CMDS= 87, WORDS= 150, PACKET(40)= D000000000000000009000000000000000000000 , SCS= 107, STEP= 1", + + "WSPOW00000": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= WSPOW00000, CMDS= 5, WORDS= 7, PACKET(40)= D8000070007030500200000000000010000 , SCS= 107, STEP= 1", + "WSPOW0002A": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= WSPOW0002A, CMDS= 5, WORDS= 7, PACKET(40)= D80000700073E800020000000000001002A , SCS= 107, STEP= 1", + "WSPOW08E1E": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= WSPOW08E1E, CMDS= 5, WORDS= 7, PACKET(40)= D8000070007381F00200000008E0001001E , SCS= 107, STEP= 1", + "WSPOW0CF3F": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= WSPOW0CF3F, CMDS= 5, WORDS= 7, PACKET(40)= D800007000701670020000000CF0001003F , SCS= 107, STEP= 1", + "WSPOW08F3E": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= WSPOW08F3E, CMDS= 5, WORDS= 7, PACKET(40)= D80000700071A9E00200000008F0001003E , SCS= 107, STEP= 1", + "WSPOW3E03E": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= WSPOW08F3E, CMDS= 5, WORDS= 7, PACKET(40)= D80000700071AD50020000003E00001003E , SCS= 107, STEP= 1", + + "WSPOW1E33F": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= WSPOW1E33F, CMDS= 5, WORDS= 7, PACKET(40)= D0000000000000000000000000000000000 , SCS= 107, STEP= 1", + "WSPOW1EC3F": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= WSPOW1EC3F, CMDS= 5, WORDS= 7, PACKET(40)= D0000000000000000000000000000000000 , SCS= 107, STEP= 1", + "WSPOW00F1E": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= WSPOW00F1E, CMDS= 5, WORDS= 7, PACKET(40)= D0000000000000000000000000000000000 , SCS= 107, STEP= 1", + "WSPOW1E01E": "1999:001:00:00:00.000 | 20 0 | ACISPKT | TLMSID= WSPOW1E01E, CMDS= 5, WORDS= 7, PACKET(40)= D0000000000000000000000000000000000 , SCS= 107, STEP= 1", + + "OORMPDS": "1999:001:00:00:00.000 | 20 0 | COMMAND_SW | TLMSID= OORMPDS, HEX= 800030C, MSID= OORMPDS, SCS= 107, STEP= 1", + "OORMPEN": "1999:001:00:00:00.000 | 20 0 | COMMAND_SW | TLMSID= OORMPEN, HEX= 800020C, MSID= OORMPEN, SCS= 107, STEP= 1", + + "2S2STHV": "1999:001:00:00:00.000 | 20 0 | COMMAND_HW | TLMSID= 2S2STHV, HEX= 641C000, MSID= 2S2STHV, 2S2STHV2=0 , SCS= 107, STEP= 1", + "2S2HVON": "1999:001:00:00:00.000 | 20 0 | COMMAND_HW | TLMSID= 2S2HVON, HEX= 641A020, MSID= 2S2HVON, SCS= 107, STEP= 1", + "2S2HVOF": "1999:001:00:00:00.000 | 20 0 | COMMAND_HW | TLMSID= 2S2HVOF, HEX= 641A000, MSID= 2S2HVOF, SCS= 107, STEP= 1" +} diff --git a/backstop_history/LTCTI_ACIS_commands.py b/backstop_history/LTCTI_ACIS_commands.py deleted file mode 100644 index 1e42838..0000000 --- a/backstop_history/LTCTI_ACIS_commands.py +++ /dev/null @@ -1,162 +0,0 @@ -################################################################################ -# -# CTI_RTS_Backstop_Commands - Base Class Definition -# -# Update: November 24, 2020 -# Gregg Germain -# V2.1 - change command format to new shiny forat -# -################################################################################ - -class LTCTI_ACIS_commands(object): - """ - The Backstop_Commands class is used when translating an ACIS - command line found in such files as a LTCTI_CLD.txt file - or an FOT LTCTI RTS file, into another format such as SKA.Parse - or FOT CR*.backstop format. - - Usage: - import Backstop_Commands - BC = Backstop_commands() - expanded_cmds = PCLD.ProcessCLD(CLD_file_path, CLD_START_TIME, CAP_NUMBER) - - Sample output, contents of expanded_cmds: - - - NOTES: - 1) Each ACIS Ops LTCTI CLD file starts with at least a 1.025 second wait. - Therefore the first command in the array will occur 1.025 seconds - after the start time argument of the call to ProcessCLD. - - 2) Every ACIS Ops LTCTI CLD file contains the SCS slot in which the CLD will be run - """ - - # Constructor - def __init__(self): - # Create the rec array dtype for the mnemonics.hrd file read - self.mnemonics_DTYPE = [('packet_id', '|S23'), - ('cmd_id', '|S23'), - ('acis_table_hdr', '|S23')] - - # Initialize the recognized command list - self.cmd_list = ['WSVIDALLDN', - 'XTZ0000005', - 'RS_0000001', - 'RH_0000001', - 'AA00000000', - 'WSPOW00000', - 'WSPOW0002A', - 'WSPOW08002', - 'WSPOW08F3E', - 'WT00C60014', - 'WSPOW08E1E', - 'WT00C62014', - 'WSPOW3E03E', - 'WT00CA8014'] - - self.cmd_vals = {'XTZ0000005': {'CMDS': '4', - 'WORDS': '4', - 'PACKET': 'PACKET(40)= D8000040004003A000E000400000'}, - - 'XCZ0000005': {'CMDS': '4', - 'WORDS': '4', - 'PACKET': 'PACKET{40)= D800004000400270010000400000'}, - - 'RS_0000001': {'CMDS': '3', - 'WORDS': '3', - 'PACKET': 'PACKET{40)= D80000300030042002100'}, - - 'RH_0000001': {'CMDS': '3', - 'WORDS': '3', - 'PACKET': 'PACKET{40)= D80000300030044002300'}, - - 'AA00000000': {'CMDS': '3', - 'WORDS': '3', - 'PACKET': 'PACKET{40)= D80000300030603001300'}, - - 'WSVIDALLDN': {'CMDS': '4', - 'WORDS': '5', - 'PACKET': 'PACKET{40)= D800005000506050020000000000'}, - - 'WT007AC024': {'CMDS': '87', - 'WORDS': '150', - 'PACKET': 'PACKET{40)= D80009600962D7C00090004308AC024007A10730'}, - - 'WT007AE024': {'CMDS': '87', - 'WORDS': '150', - 'PACKET': 'PACKET{40)= D80009600962D80000900046541E024007A65790'}, - - 'WT00B26014': {'CMDS': '87', - 'WORDS': '150', - 'PACKET': 'PACKET{40)= D80009600963194000900049271601400B210730'}, - - 'WSPOW0CF3F': {'CMDS': '5', - 'WORDS': '7', - 'PACKET': 'PACKET(40)= D800007000701670020000000CF0001003F'}, - - 'WSPOW00000': {'CMDS': '5', - 'WORDS': '7', - 'PACKET': 'PACKET(40)= D8000070007030500200000000000010000'}, - - 'WSPOW0002A': {'CMDS': '5', - 'WORDS': '7', - 'PACKET': 'PACKET(40)= D80000700073E800020000000000001002A'}, - - - - 'WSPOW08F3E': {'CMDS': '5', - 'WORDS': '7', - 'PACKET': 'PACKET(40)= D80000700071A9E00200000008F0001003E'}, - - 'WT00C60014': {'CMDS': '87', - 'WORDS': '150', - 'PACKET': 'PACKET(40)= D8000960096381600090004C357001400C6237A0'}, - - - - - 'WSPOW08E1E': {'CMDS': '5', - 'WORDS': '7', - 'PACKET': 'PACKET(40)= D8000070007381F00200000008E0001001E'}, - - 'WT00C62014': {'CMDS': '87', - 'WORDS': '150', - 'PACKET': 'PACKET(40)= D8000960096381A00090004E3C1201400C6237A0'}, - - - - - 'WSPOW3E03E': {'CMDS': '5', - 'WORDS': '7', - 'PACKET': 'PACKET(40)= D80000700071AD50020000003E00001003E'}, - - 'WT00CA8014': {'CMDS': '87', - 'WORDS': '150', - 'PACKET': 'PACKET(40)= D800096009639CF0009000405F5801400CA657A0'} - - - - - - } # END Command Constants Dictionary - - - # Now the LTCTI CLD files are run as an RTS so the times and - # VCDU's in this data structure are bogus. Meaningful times - # will be entered in a copy of this data structure when the commands - # are integrated into a backstop history - # NOTE: Never write into this attribute. Always make a copy - self.general_bs_cmds = [ - # --------------------- SIMTRANS ----------------------------- - {'idx': -1, - 'date': '1900:001', - 'type': 'SIMTRANS', - 'tlmsid': 'None', - 'scs': 132, - 'step': 451, - 'time': -1, - 'timeline_id': 0, - 'vcdu': 0000000, - 'params': {'pos': -99616}}, - - ] # End of definition of self.LTCTI_bs_cmds diff --git a/backstop_history/LTCTI_RTS.py b/backstop_history/LTCTI_RTS.py index 7604f2a..180d38e 100644 --- a/backstop_history/LTCTI_RTS.py +++ b/backstop_history/LTCTI_RTS.py @@ -2,19 +2,12 @@ import os import re -# Import the class that contains individual command data for all -# the commands which are relevant to LTCTI runs -from backstop_history import LTCTI_ACIS_commands +from astropy.time import Time -from cxotime import CxoTime - - -class LTCTI_RTS(object): - """ - This class allows the user to convert a FOT, ACIS, LTCTI, RTS file, along with - information found in a FOT Request, and generate a list of dicts where each line in the RTS file is - processed and converted to a dict in the SKA.parse format. Each dict is appended - to an output list. +""" + These functions allow the user to convert a FOT, ACIS, LTCTI, RTS file, along with + information found in a FOT Request, and generate an array where each line in the RTS file is + processed. The format of the RTS file and the FOT request are described in OP-19. @@ -56,7 +49,7 @@ class LTCTI_RTS(object): Yuo can skip this step and proceed directly to Step 2 if you have obtained the necessary information in some other way. - Second, the processRTS() method is used to open the file, read + Second, the processRTS() method is used to open the file, readImport and process each line and write the results into a numpy array. The array DTYPE is: dtype=[('date', 'S20'), ('time', '