def get_fitting_data(self, start, stop): tstart = date2secs(start) tstop = date2secs(stop) times = self._eng_match_times(tstart, tstop, 328.0) msids = [self.msid] + [ data_map[input] for input in self.inputs if input not in pwr_states ] msids += ['solarephem0_{}'.format(ax) for ax in "xyz"] data = fetch.MSIDset(msids, start, stop, stat='5min', filter_bad=True) data.interpolate(times=times) msid_vals = Ska.Numpy.smooth(data[self.msid].vals, 10) sun_eci = np.array([ data['solarephem0_x'].vals, data['solarephem0_y'].vals, data['solarephem0_z'].vals ]) d_sun = np.sqrt((sun_eci**2).sum(axis=0)) states = self.get_cmd_states(data.datestart, data.datestop, times) combined_dict = { 'msid_times': times, 'msid_vals': msid_vals, 'd_sun': d_sun } for input in self.inputs: if input in data_map: combined_dict[input] = data[data_map[input]].vals elif input in states.dtype.names: combined_dict[input] = states[input] return pd.DataFrame(combined_dict)
def _fix_comm_times(self, lines, line_times, comm_durations): new_lines = [] new_times = [] for i, line in enumerate(lines): if not "REAL-TIME COMM" in line and not "COMM DURATION" in line: new_lines.append(line) new_times.append(line_times[i]) for time in self.events["comm_begins"]["times"]: local_time = datetime.strptime(time, "%Y:%j:%H:%M:%S.%f").replace( tzinfo=timezone.utc).astimezone(tz=None) t = date2secs(time) idx = bisect.bisect_right(new_times, t) new_times.insert(idx, t) new_lines.insert( idx, "%s REAL-TIME COMM BEGINS %s EDT" % (time, local_time.strftime("%Y:%j:%H:%M:%S"))) for i, time in enumerate(self.events["comm_ends"]["times"]): local_time = datetime.strptime(time, "%Y:%j:%H:%M:%S.%f").replace( tzinfo=timezone.utc).astimezone(tz=None) t = date2secs(time) idx = bisect.bisect_right(new_times, t) new_times.insert(idx, t) new_lines.insert( idx, "%s REAL-TIME COMM ENDS %s EDT" % (time, local_time.strftime("%Y:%j:%H:%M:%S"))) new_times.insert(idx + 1, t) new_lines.insert( idx + 1, "==> COMM DURATION: %g mins." % comm_durations[i]) return new_lines, new_times
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 get_msid_attrs(self, tstart, tstop, msid, msid_args): """Get attributes for computed MSID: ``vals``, ``bads``, ``times`` :param tstart: start time (CXC secs) :param tstop: stop time (CXC secs) :param msid: full MSID name e.g. cmd_state_pitch_clean :param msid_args: tuple of regex match groups: (state_key, dt) :returns: dict of MSID attributes """ from kadi.commands.states import get_states from Chandra.Time import date2secs state_key = msid_args[0] dt = 1.025 * int(msid_args[1]) states = get_states(tstart, tstop, state_keys=[state_key]) tstart = date2secs(states['datestart'][0]) tstops = date2secs(states['datestop']) times = np.arange(tstart, tstops[-1], dt) vals = states[state_key].view(np.ndarray) indexes = np.searchsorted(tstops, times) out = { 'vals': vals[indexes], 'times': times, 'bads': np.zeros(len(times), dtype=bool), 'unit': None } return out
def calc_model_wrapper(self, model_spec, states, tstart, tstop, state0=None): """ This method sets up the model and runs it. "calc_model" is provided by the specific model instances. Parameters ---------- model_spec : string Path to the JSON file containing the model specification. states : NumPy record array Commanded states tstart : float The start time of the model run. tstop : float The end time of the model run. state0 : initial state dictionary, optional This state is used to set the initial temperature. """ if state0 is None: start_msid = None dh_heater = None dh_heater_times = None else: start_msid = state0[self.msid] htrbfn = os.path.join(self.bsdir, 'dahtbon_history.rdb') mylog.info('Reading file of dahtrb commands from file %s' % htrbfn) htrb = ascii.read(htrbfn, format='rdb') dh_heater_times = date2secs(htrb['time']) dh_heater = htrb['dahtbon'].astype(bool) return self.calc_model(model_spec, states, tstart, tstop, start_msid, dh_heater=dh_heater, dh_heater_times=dh_heater_times)
def append_mask_time(self, new_times, bad=False): t0, t1 = DateTime(new_times).secs i0, i1 = np.searchsorted(self.times, [t0, t1]) if i1 > i0: self.mask_times_indices.append((i0, i1)) self.mask_times.append(new_times) self.mask_times_bad = np.append(self.mask_times_bad, bad) self.mask_time_secs = date2secs(self.mask_times)
def _plot_bands(self, tbegin, tend, plot, events, color, alpha=1.0): tc_start = list(self.events[events[0]]["times"]) tc_end = list(self.events[events[1]]["times"]) if tc_end[0] < tc_start[0]: tc_start.insert(0, self.first_time) if tc_start[-1] > tc_end[-1]: tc_end.append(self.last_time) assert len(tc_start) == len(tc_end) tc_start = date2secs(tc_start) tc_end = date2secs(tc_end) ybot, ytop = plot.ax.get_ylim() t = np.linspace(tbegin, tend, 500) tplot = cxctime2plotdate(t) for tcs, tce in zip(tc_start, tc_end): in_evt = (t >= tcs) & (t <= tce) plot.ax.fill_between(tplot, ybot, ytop, where=in_evt, color=color, alpha=alpha)
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 __init__(self, table): import numpy.lib.recfunctions as rf new_table = OrderedDict() if isinstance(table, np.ndarray): state_names = list(table.dtype.names) if "tstart" not in state_names: table = rf.append_fields(table, ["tstart", "tstop"], [ date2secs(table["datestart"]), date2secs(table["datestop"]) ], usemask=False) state_names += ["tstart", "tstop"] else: state_names = list(table.keys()) if "tstart" not in state_names: table["tstart"] = date2secs(table["datestart"]) table["tstop"] = date2secs(table["datestop"]) state_names += ["tstart", "tstop"] times = Quantity([table["tstart"], table["tstop"]], "s") for k in state_names: v = np.asarray(table[k]) if k == "trans_keys" and v.dtype.char == "O": new_table[k] = APStringArray( np.array([",".join(d) for d in v]), times) elif v.dtype.char in ['S', 'U', 'O']: new_table[k] = APStringArray(v, times) else: new_table[k] = APQuantity(v, times, get_units("states", k), dtype=v.dtype) if "off_nom_roll" not in state_names: v = calc_off_nom_rolls(new_table) new_table["off_nom_roll"] = APQuantity(v, times, "deg", dtype=v.dtype) super(States, self).__init__(table=new_table)
def from_tracelog(cls, filename, tbegin=None, tend=None): if tbegin is None: tbegin = -1.0e22 else: if isinstance(tbegin, str): tbegin = date2secs(tbegin) if tend is None: tend = 1.0e22 else: if isinstance(tend, str): tend = date2secs(tend) f = open(filename, "r") header = f.readline().split() dtype = [] state_codes = {} for msid in header: state_code = get_state_codes(msid.lower()) if msid.lower() != "time": state_codes[msid.lower()] = state_code if state_code is not None: dtype.append((msid.lower(), "|U4")) else: dtype.append((msid.lower(), '<f8')) data = [] for line in f: words = line.split() if len(words) == len(header): data.append(tuple(words)) f.close() data = np.array(data, dtype=dtype) # Convert times in the TIME column to Chandra 1998 time data['time'] -= 410227200. idxs = np.logical_and(data['time'] >= tbegin, data['time'] <= tend) table = dict((k.lower(), data[k][idxs]) for k in data.dtype.names if k != "time") times = dict((k.lower(), data["time"][idxs]) for k in header if k != "time") derived_msids = ["dpa_a_power", "dpa_b_power", "dea_a_power", "dea_b_power"] return cls(table, times, state_codes=state_codes, derived_msids=derived_msids)
def make_dashboard_plots(self, msid, tstart=None, tstop=None, yplotlimits=None, errorplotlimits=None, fig=None, figfile=None, bad_times=None, mask_radzones=False, plot_limits=True, mask_fmt1=False): """ Make dashboard plots for the particular thermal model. Parameters ---------- msid : string The MSID name to plot in the dashboard. tstart : string, optional The start time of the data for the dashboard plot. If not specified, the beginning of the thermal model run is used. tstop : string, optional The stop time of the data for the dashboard plot. If not specified, the end of the thermal model run is used. yplotlimits : two-element array_like, optional The (min, max) bounds on the temperature to use for the temperature vs. time plot. Default: Determine the min/max bounds from the telemetry and model prediction and decrease/increase by degrees to determine the plot limits. errorplotlimits : two-element array_like, optional The (min, max) error bounds to use for the error plot. Default: [-15, 15] fig : :class:`~matplotlib.figure.Figure`, optional A Figure instance to plot in. Default: None, one will be created if not provided. figfile : string, optional The file to write the dashboard plot to. One will be created if not provided. bad_times : list of tuples, optional Provide a set of times to exclude from the creation of the dashboard plot. mask_radzones : boolean, optional If True, mask out radzone periods for dashboard plots of the focal plane model. Default: False plot_limits : boolean, optional If True, plot the yellow caution and planning limits on the dashboard plots. Default: True """ from xijafit import dashboard as dash if fig is None: fig = plt.figure(figsize=(20,10)) if ("msids", msid) not in self.field_list: raise RuntimeError("You must include the real data if you want to make a " "dashboard plot! Set get_msids=True when creating the" "thermal model!") telem = self["msids", msid] pred = self["model", msid] mask = np.logical_and(telem.mask, pred.mask) if tstart is not None: tstart = DateTime(tstart).secs mask[telem.times.value < tstart] = False if tstop is not None: tstop = DateTime(tstop).secs mask[telem.times.value > tstop] = False if bad_times is not None: for (left, right) in bad_times: idxs = np.logical_and(telem.times.value >= date2secs(left), telem.times.value <= date2secs(right)) mask[idxs] = False if msid == "fptemp_11" and mask_radzones: rad_zones = events.rad_zones.filter(start=telem.dates[0], stop=telem.dates[-1]) for rz in rad_zones: idxs = np.logical_and(telem.times.value >= rz.tstart, telem.times.value <= rz.tstop) mask[idxs] = False if mask_fmt1: which = self["msids", "ccsdstmf"] == "FMT1" mask[which] = False times = telem.times.value[mask] if yplotlimits is None: ymin = min(telem.value[mask].min(), pred.value[mask].min())-2 ymax = min(telem.value[mask].max(), pred.value[mask].max())+2 yplotlimits = [ymin, ymax] if errorplotlimits is None: errorplotlimits = [-5, 5] mylimits = {"units": "C"} if plot_limits: if msid == "fptemp_11": mylimits["acisi_limit"] = -112.0 mylimits["aciss_limit"] = -111.0 mylimits["fp_sens_limit"] = -118.7 else: mylimits["caution_high"] = limits[msid]+margins[msid] mylimits["planning_limit"] = limits[msid] dash.dashboard(pred.value[mask], telem.value[mask], times, mylimits, msid=msid, modelname=full_name.get(msid, msid), errorplotlimits=errorplotlimits, yplotlimits=yplotlimits, fig=fig, savefig=False) if figfile is not None: fig.savefig(figfile) return fig
def from_mit_file(cls, filename, tbegin=None, tend=None): if tbegin is None: tbegin = -1.0e22 else: if isinstance(tbegin, str): tbegin = date2secs(tbegin) if tend is None: tend = 1.0e22 else: if isinstance(tend, str): tend = date2secs(tend) f = open(filename, 'r') line = f.readline() f.close() if "," in line: delimiter = "," elif "\t" in line: delimiter = "\t" else: delimiter = " " if line.startswith("#"): year = "#YEAR" else: year = "YEAR" data = Table(ascii.read(filename, guess=False, format='csv', delimiter=delimiter), masked=True) mins, hours = np.modf(data["SEC"].data/3600.) secs, mins = np.modf(mins*60.) secs *= 60.0 time_arr = ["%04d:%03d:%02d:%02d:%06.3f" % (y, d, h, m, s) for y, d, h, m, s in zip(data[year].data, data["DOY"].data, hours, mins, secs)] tsecs = date2secs(time_arr) idxs = np.logical_and(tsecs >= tbegin, tsecs <= tend) table = {} times = {} masks = {} state_codes = {} for k in data.keys(): if k not in [year, "DOY", "SEC"]: if k in mit_trans_table: key = mit_trans_table[k] else: key = k.lower() table[key] = np.array(data[k].data[idxs]) times[key] = tsecs[idxs] if key == "bilevels": masks[key] = np.array(table[key] != "0") else: masks[key] = ~data[k].data[idxs].mask state_codes[key] = get_state_codes(key) # Now we split the bilevel into its components bmask = masks["bilevels"] bilevels = np.char.strip(table["bilevels"], "b")[bmask] for i in range(8): key = "1stat%dst" % (7-i) table[key] = np.array(["BAD"]*bmask.size) table[key][bmask] = np.array([b[i] for b in bilevels]) times[key] = times["bilevels"] masks[key] = bmask state_codes[key] = get_state_codes(key) return cls(table, times, masks=masks, state_codes=state_codes)
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 reset_mask_times(self): self.mask_times = self.bad_times.copy() self.mask_times_indices = self.bad_times_indices.copy() self.mask_time_secs = date2secs(self.mask_times) self.mask_times_bad = np.ones(self.mask_time_secs.shape[0], dtype='bool')
def find_text_time(time, hours=1.0): return secs2date(date2secs(time)+hours*3600.0)
def test_date2secs(self): vals = DateTime(['2012:001', '2000:001']) self.assertTrue(np.all(date2secs(vals.date) == vals.secs))
def test_date2secs(self): vals = DateTime(['2012:001', '2000:001']) self.assertTrue(np.all(date2secs(vals.date) == vals.secs))