def get_updated_dsn_comms(self): dsnfile = "/data/acis/dsn_summary.dat" if os.path.getsize(dsnfile) == 0: mylog.warning("DSN summary file is empty. Ignoring.") return tstart = date2secs(self.first_time) tstop = date2secs(self.last_time) bots = [] eots = [] new_durations = [] with open(dsnfile) as f: for line in f.readlines()[2:]: words = line.strip().split() bot = datetime.strptime( "%s:%s:00:00:00" % (words[-4], words[-3].split(".")[0]), "%Y:%j:%H:%M:%S") eot = datetime.strptime( "%s:%s:00:00:00" % (words[-2], words[-1].split(".")[0]), "%Y:%j:%H:%M:%S") time_bot = date2secs(bot.strftime( "%Y:%j:%H:%M:%S")) + 86400.0 * (float(words[-3]) % 1) time_eot = date2secs(eot.strftime( "%Y:%j:%H:%M:%S")) + 86400.0 * (float(words[-1]) % 1) new_durations.append((time_eot - time_bot) / 60.0) if tstart <= time_bot <= tstop: bots.append(time_bot) if tstart <= time_eot <= tstop: eots.append(time_eot) self.events["comm_begins"]["times"] = secs2date(bots) self.events["comm_ends"]["times"] = secs2date(eots) self.lines, self.line_times = self._fix_comm_times( self.lines, self.line_times, new_durations)
def argmin(self, dates=False): idx = np.argmin(self.value) times = self.times[idx] if dates: return secs2date(times) else: return times
def write_temps(self, outdir, times, temps): """ Write the states record array to the file "temperatures.dat" and the Earth solid angles to "earth_solid_angles.dat". Parameters ---------- outdir : string The directory the file will be written to. times : NumPy array Times in seconds from the start of the mission temps : NumPy array Temperatures in Celsius """ super(ACISFPCheck, self).write_temps(outdir, times, temps) outfile = os.path.join(outdir, 'earth_solid_angles.dat') mylog.info('Writing Earth solid angles to %s' % outfile) e = self.predict_model.comp['earthheat__fptemp'].dvals efov_table = Table([times, secs2date(times), e], names=['time', 'date', 'earth_solid_angle'], copy=False) efov_table['time'].format = '%.2f' efov_table['earth_solid_angle'].format = '%.3e' efov_table.write(outfile, format='ascii', delimiter='\t', overwrite=True)
def _get_msids(self, model, comps, tl_file): comps = [comp.lower() for comp in comps] times = model[comps[0]].times.value tstart = secs2date(times[0] - 700.0) tstop = secs2date(times[-1] + 700.0) if tl_file is not None: msids = MSIDs.from_tracelog(tl_file, tbegin=tstart, tend=tstop) else: if "earth_solid_angle" in comps: comps.remove("earth_solid_angle") comps.append("ccsdstmf") msids = MSIDs.from_database(comps, tstart, tstop=tstop, filter_bad=True, interpolate='nearest', interpolate_times=times) if msids[comps[0]].times.size != times.size: raise RuntimeError("Lengths of time arrays for model data and MSIDs " "do not match. You probably ran a model past the " "end date in the engineering archive!") return msids
def write_prediction(self, filename, predict_times, predict_data): from astropy.table import Table temp_table = Table( [predict_times, secs2date(predict_times), predict_data], names=['time', 'date', self.msid.lower()], copy=False) temp_table['time'].format = '%.2f' temp_table[self.msid.lower()].format = '%.2f' temp_table.write(filename, format='ascii', delimiter='\t', overwrite=True)
def _add_annotations(self, plot, annotations, tbegin, tend): for i, line in enumerate(plot.ax.lines): line.set_zorder(100 - i) plot_comms = False plot_belts = False if "cti_runs" in annotations: annotations.append("start_cti") annotations.append("end_cti") annotations.remove("cti_runs") for key in annotations: if key == "comms": plot_comms = True continue if key == "belts": plot_belts = True continue color = colors[key] ls = styles[key] for i, t in enumerate(self.events[key]["times"]): tt = date2secs(t) if tt < tbegin or tt > tend: continue plot.add_vline(t, color=color, ls=ls) if "state" in self.events[key] and key in offsets: text = self.events[key]["state"][i] if isinstance(text, tuple): text = text[-1] tdt = secs2date(tt + 1800.0) ymin, ymax = plot.ax.get_ylim() y = (1.0 - offsets[key]) * ymin + offsets[key] * ymax plot.add_text(tdt, y, text, fontsize=15, rotation='vertical', color=color, zorder=100) if plot_belts: self._plot_bands(tbegin, tend, plot, ["radmon_disable", "radmon_enable"], "mediumpurple", alpha=0.333333) if plot_comms: self._plot_bands(tbegin, tend, plot, ["comm_begins", "comm_ends"], "pink", alpha=1.0)
def write_temps(self, outdir, times, temps): """ Write the states record array to the file "temperatures.dat". Parameters ---------- outdir : string The directory the file will be written to. times : NumPy array Times in seconds from the start of the mission temps : NumPy array Temperatures in Celsius """ outfile = os.path.join(outdir, 'temperatures.dat') mylog.info('Writing temperatures to %s' % outfile) T = temps[self.name] temp_table = Table([times, secs2date(times), T], names=['time', 'date', self.msid], copy=False) temp_table['time'].format = '%.2f' temp_table[self.msid].format = '%.2f' temp_table.write(outfile, format='ascii', delimiter='\t')
def write_temps(self, outdir, times, temps): """ Write the states record array to the file "temperatures.dat" and the Earth solid angles to "earth_solid_angles.dat". Parameters ---------- outdir : string The directory the file will be written to. times : NumPy array Times in seconds from the start of the mission temps : NumPy array Temperatures in Celsius """ super(ACISFPCheck, self).write_temps(outdir, times, temps) outfile = os.path.join(outdir, 'earth_solid_angles.dat') mylog.info('Writing Earth solid angles to %s' % outfile) e = self.predict_model.comp['earthheat__fptemp'].dvals efov_table = Table([times, secs2date(times), e], names=['time', 'date', 'earth_solid_angle'], copy=False) efov_table['time'].format = '%.2f' efov_table['earth_solid_angle'].format = '%.3e' efov_table.write(outfile, format='ascii', delimiter='\t')
def plot(self, fields, field2=None, lw=1.5, fontsize=18, color=None, color2='magenta', figsize=(10, 8), plot=None, tbegin=None, tend=None, annotations=None, ymin=None, ymax=None, ymin2=None, ymax2=None): """ Plot temperature and state data from a load review. Parameters ---------- fields : tuple of strings or list of tuples of strings A single field or list of fields to plot on the left y-axis. field2 : tuple of strings, optional A single field to plot on the right y-axis. Default: None lw : float, optional The width of the lines in the plots. Default: 1.5 px. fontsize : integer, optional The font size for the labels in the plot. Default: 18 pt. color : list of strings, optional The color for the lines plotted on the left y-axis. Default: ["blue", "red", "green", "black"] color2 : string, optional The color for the line plotted on the right y-axis. Default: "magenta" figsize : tuple of integers, optional The size of the plot in (width, height) in inches. Default: (10, 8) plot : :class:`~acispy.plots.DatePlot` or :class:`~acispy.plots.CustomDatePlot`, optional An existing DatePlot to add this plot to. Default: None, one will be created if not provided. tbegin : string, float, or DateTime object, optional The start time of the plot. Default is to plot from the beginning of the load. tend : string, float, or DateTime object, optional The end time of the plot. Default is to plot to the ending of the load. annotations : list of strings, optional Additional annotations to add to the plot. Available options are "cti_runs", "comms", "belts", "perigee", "sim_trans", and "apogee". Default: None ymin : float, optional Set the minimum value of the y-axis on the left side of the plot. ymax : float, optional Set the maximum value of the y-axis on the left side of the plot. ymin2 : float, optional Set the minimum value of the y-axis on the right side of the plot. ymax2 : float, optional Set the maximum value of the y-axis on the right side of the plot. """ dp = DatePlot(self.ds, fields, field2=field2, lw=lw, fontsize=fontsize, color=color, color2=color2, figsize=figsize, plot=plot) ylimits = dp.ax.get_ylim() if ymin is None: ymin = ylimits[0] if ymax is None: ymax = ylimits[1] dp.set_ylim(ymin, ymax) if field2 is not None: ylimits2 = dp.ax2.get_ylim() if ymin2 is None: ymin2 = ylimits2[0] if ymax2 is None: ymax2 = ylimits2[1] dp.set_ylim2(ymin2, ymax2) if tbegin is None: tbegin = self.first_time if tend is None: tend = self.last_time tbegin = get_time(tbegin, 'secs') tend = get_time(tend, 'secs') if annotations is not None: self._add_annotations(dp, annotations.copy(), tbegin, tend) dp.set_xlim(secs2date(tbegin), secs2date(tend)) return dp
def _populate_event_times(self): lines = [] line_times = [] time = self.first_time comm_durations = [] with open(self.load_file, "r") as f: for i, line in enumerate(f.readlines()): words = line.strip().split() if len(words) > 0: event = None state = None if line.startswith(self.load_year) or \ line.startswith(self.next_year): time = words[0] if "MP_OBSID" in line: event = "obsid_change" state = words[-1] if "SIMTRANS" in line: event = "sim_trans" state = (int(words[-2]), words[-1].strip("()")) if "HETGIN" in line: event = "hetg_in" if "HETGRE" in line: event = "hetg_out" if "LETGIN" in line: event = "letg_in" if "LETGRE" in line: event = "letg_out" if "CSELFMT" in line and "COMMAND_HW" in line: event = "fmt_change" state = int(words[-1][-1]) if "EPERIGEE" in line and "ORBPOINT" in line: event = "perigee" if "APOGEE" in line and "ORBPOINT" in line: event = "apogee" if "COMM BEGINS" in line: event = "comm_begins" if "COMM ENDS" in line: event = "comm_ends" if "EEF1000" in line and "ORBPOINT" in line: event = "enter_belts" if "XEF1000" in line and "ORBPOINT" in line: event = "exit_belts" if "OORMPDS" in line and "COMMAND_SW" in line: event = "radmon_disable" if "OORMPEN" in line and "COMMAND_SW" in line: event = "radmon_enable" if event is not None: if event not in self.events: self.events[event] = {"times": []} if event == "comm_ends": time = secs2date(date2secs(words[0]) - 1800.0) self.events[event]["times"].append(time) if state is not None: if "state" not in self.events[event]: self.events[event]["state"] = [] self.events[event]["state"].append(state) if "REAL-TIME COMM" in line: continue if "COMM DURATION" in line: comm_durations.append(float(words[-2]) - 30.0) continue if line.startswith(self.load_year) or \ line.startswith(self.next_year) or \ "WSPOW COMMAND LOADS" in line or \ "CHANDRA STATUS ARRAY" in line or \ "ACIS integration time" in line or \ "requested time" in line or \ "ObsID change" in line or \ "THERE IS A Z-SIM" in line or \ "==> DITHER" in line: lines.append(line) line_times.append(time) line_times = date2secs(line_times) if len(self.events["comm_begins"]) > 0: lines, line_times = self._fix_comm_times(lines, line_times, comm_durations) return lines, line_times
def test_secs2date(self): vals = DateTime(['2012:001', '2000:001']) self.assertTrue(np.all(secs2date(vals.secs) == vals.date))
def dates(self): if self._dates is None: self._dates = secs2date(self.times.value) return self._dates
def dates(self): return secs2date(self.times.value)
def __init__(self, name, tstart, hours, T_init, pitch, ccd_count, vehicle_load=None, simpos=-99616.0, off_nom_roll=0.0, dh_heater=0, fep_count=None, clocking=1, q=None, instrument=None, model_spec=None, no_limit=False, no_earth_heat=False): if name in short_name_rev: name = short_name_rev[name] if name == "fptemp_11" and instrument is None: raise RuntimeError("Must specify either 'ACIS-I' or 'ACIS-S' in " "'instrument' if you want to test a focal plane " "temperature prediction!") if fep_count is None: fep_count = ccd_count if q is None and name == "fptemp_11": raise RuntimeError("Please supply an attitude quaternion for the focal plane model!") self.vehicle_load = vehicle_load self.no_limit = no_limit tstart = get_time(tstart) datestart = tstart tstart = DateTime(tstart).secs tstop = tstart+hours*3600.0+10012.0 datestop = secs2date(tstop) tend = tstop+0.5*(tstop-tstart) dateend = secs2date(tend) self.datestart = datestart self.datestop = datestop self.hours = hours self.tstart = Quantity(tstart, "s") self.tstop = Quantity(tstop, "s") self.dateend = dateend self.T_init = Quantity(T_init, "deg_C") self.instrument = instrument self.no_earth_heat = no_earth_heat if vehicle_load is None: states = {"ccd_count": np.array([ccd_count], dtype='int'), "fep_count": np.array([fep_count], dtype='int'), "clocking": np.array([clocking], dtype='int'), 'vid_board': np.array([ccd_count > 0], dtype='int'), "pitch": np.array([pitch]), "simpos": np.array([simpos]), "datestart": np.array([self.datestart]), "datestop": np.array([self.dateend]), "tstart": np.array([self.tstart.value]), "tstop": np.array([tend]), "hetg": np.array(["RETR"]), "letg": np.array(["RETR"]), "off_nom_roll": np.array([off_nom_roll]), "dh_heater": np.array([dh_heater], dtype='int')} # For the focal plane model we need a quaternion. if name == "fptemp_11": for i in range(4): states["q%d" % (i+1)] = np.array([q[i]]) else: mylog.info("Modeling a %d-chip observation concurrent with " % ccd_count + "the %s vehicle loads." % vehicle_load) states = dict((k, state.value) for (k, state) in States.from_load_page(vehicle_load).table.items()) ecs_run_idxs = states["tstart"] < tstop states["ccd_count"][ecs_run_idxs] = ccd_count states["fep_count"][ecs_run_idxs] = fep_count states["clocking"][ecs_run_idxs] = clocking states["vid_board"][ecs_run_idxs] = ccd_count > 0 super(SimulateSingleObs, self).__init__(name, datestart, dateend, states, T_init, model_spec=model_spec, get_msids=False, no_eclipse=True) mylog.info("Run Parameters") mylog.info("--------------") mylog.info("Start Datestring: %s" % datestart) mylog.info("Length of ECS run in hours: %s" % hours) mylog.info("Stop Datestring: %s" % datestop) mylog.info("Initial Temperature: %g degrees C" % T_init) mylog.info("CCD Count: %d" % ccd_count) mylog.info("FEP Count: %d" % fep_count) if vehicle_load is None: disp_pitch = pitch disp_roll = off_nom_roll else: pitches = states["pitch"][ecs_run_idxs] rolls = states["off_nom_roll"][ecs_run_idxs] disp_pitch = "Min: %g, Max: %g" % (pitches.min(), pitches.max()) disp_roll = "Min: %g, Max: %g" % (rolls.min(), rolls.max()) mylog.info("Pitch: %s" % disp_pitch) mylog.info("SIM Position: %g" % simpos) mylog.info("Off-nominal Roll: %s" % disp_roll) mylog.info("Detector Housing Heater: %s" % {0: "OFF", 1: "ON"}[dh_heater]) mylog.info("Model Result") mylog.info("------------") if self.name == "fptemp_11": limit = limits[self.name][instrument] margin = 0.0 else: limit = limits[self.name] margin = margins[self.name] if self.name in low_limits: self.low_limit = Quantity(low_limits[self.name], "deg_C") else: self.low_limit = None self.limit = Quantity(limit, "deg_C") self.margin = Quantity(margin, 'deg_C') self.limit_time = None self.limit_date = None self.duration = None self.violate = False if self.no_limit: return viols = self.mvals.value > self.limit.value if np.any(viols): idx = np.where(viols)[0][0] self.limit_time = self.times('model', self.name)[idx] self.limit_date = secs2date(self.limit_time) self.duration = Quantity((self.limit_time.value-tstart)*0.001, "ks") msg = "The limit of %g degrees C will be reached at %s, " % (self.limit.value, self.limit_date) msg += "after %g ksec." % self.duration.value mylog.info(msg) if self.limit_time < self.tstop: self.violate = True viol_time = "before" else: self.violate = False viol_time = "after" mylog.info("The limit is reached %s the end of the observation." % viol_time) else: mylog.info("The limit of %g degrees C is never reached." % self.limit.value) if self.violate: mylog.warning("This observation is NOT safe from a thermal perspective.") else: mylog.info("This observation is safe from a thermal perspective.")
def find_text_time(time, hours=1.0): return secs2date(date2secs(time)+hours*3600.0)