from collections import deque import random import wx import numpy as np import epics from datetime import datetime #from epics.wx import MotorPanel from slic.gui.widgets import LabeledMathEntry from slic.gui.widgets import make_filled_hbox, make_filled_vbox, EXPANDING from slic.gui.widgets.plotting import PlotPanel from bstrd import BS, bsstream # config nshots = 1000 mm2fs = 6671.2 # light speed and delay stages ttThreshold = 5 iZeroThreshold = 0.5 # spectrometer axis in time timeAx = -1.91*(np.arange(0,2048)-1024) # channels STAGEpv = epics.PV('SLAAR11-LMOT-M452:MOTOR_1.VAL') # global globi chname_amplitude = "SARES11-SPEC125-M1.edge_amplitude" chname_jitter = "SARES11-SPEC125-M1.edge_position" chname_deriv = "SARES11-SPEC125-M1.edge_derivative" chname_events = "SAR-CVME-TIFALL4:EvtSet" chname_iZero = "SAROP11-PBPS110:INTENSITY" # create channels ch_amp = BS(chname_amplitude) ch_jitter = BS(chname_jitter) ch_events = BS(chname_events) ch_iZero = BS(chname_iZero) ch_deriv = BS(chname_deriv) iso_format = "%Y-%m-%d %H:%M:%S" def goodshots(events, iZero, amps, *arrays): fel = events[:, 13] laser = events[:, 18] darkShot = events[:, 21] pulsePicking = events[:,200] iZero = iZero amplitudes = amps pumped_shots = np.logical_and.reduce((fel, laser, np.logical_not(darkShot), pulsePicking, iZero > iZeroThreshold, amps > ttThreshold)) # pumped_shots = np.logical_and.reduce((fel, laser, np.logical_not(darkShot))) return [a[pumped_shots] for a in arrays] class MainPanel(wx.Panel): def __init__(self, parent): super().__init__(parent) self.evts = np.empty((nshots,256)) self.jitter = np.empty(nshots) self.amp = np.empty(nshots) self.iZero = np.empty(nshots) self.deriv = np.empty((nshots,2048)) self.edge = np.empty((nshots,2048)) self.stagePos = deque(maxlen=1000) self.jitters = deque(maxlen=1000) self.jstds = deque(maxlen=1000) self.amps = deque(maxlen=1000) self.astds = deque(maxlen=1000) self.plot_jitters = plot_jitters = PlotPanel(self, figsize=(2,3)) self.plot_amps = plot_amps = PlotPanel(self, figsize=(2,3)) self.plot_edges = plot_edges = PlotPanel(self, figsize=(6,5)) plots1 = (plot_edges,) hb_plot1 = make_filled_hbox(plots1) plots2 = (plot_jitters, plot_amps) hb_plot2 = make_filled_hbox(plots2) btn_clearQ = wx.Button(self, label="Clear plots") btns = (btn_clearQ,) hb_btns = make_filled_hbox(btns) self.chkbx_feedback = chkbx_feedback = wx.CheckBox(self, label="feedback") chkbxs = (btn_clearQ, chkbx_feedback) hb_chkbx = make_filled_hbox(chkbxs) self.plot_stage = plot_stage = PlotPanel(self, figsize=(6,2)) self.plot_timeseries_jitters = plot_timeseries_jitters = PlotPanel(self, figsize=(6,2)) self.plot_timeseries_amps = plot_timeseries_amps = PlotPanel(self, figsize=(6,2)) plots3 = (plot_timeseries_jitters,) plots4 = (plot_timeseries_amps,) plots5 = (plot_stage,) hb_plot3 = make_filled_hbox(plots3) hb_plot4 = make_filled_hbox(plots4) hb_plot5 = make_filled_hbox(plots5) widgets = (hb_plot1, hb_plot2, hb_plot3, hb_plot4, hb_plot5, hb_chkbx) box = make_filled_vbox(widgets, border=1) self.SetSizerAndFit(box) btn_clearQ.Bind(wx.EVT_BUTTON, self.on_click_clearQ) self.timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.on_update, self.timer) self.timer.Start(100) def on_update(self, _event): self.time = datetime.now().strftime(iso_format) self.evts = np.empty((nshots,256)) self.jitter = np.empty(nshots) self.amp = np.empty(nshots) self.iZero = np.empty(nshots) self.deriv = np.empty((nshots,2048)) for i in range(nshots): tempa = ch_events.get() self.evts[i] = tempa tempb = ch_jitter.get() self.jitter[i] = tempb tempc = ch_amp.get() self.amp[i] = tempc tempd = ch_iZero.get() self.iZero[i] = tempd tempe = ch_deriv.get() self.deriv[i] = tempe next(bsstream) wx.GetApp().Yield() self.deriv, self.amp, self.jitter = goodshots(self.evts, self.iZero, self.amp, self.deriv, self.amp, self.jitter) if (len(np.asarray(self.jitter)) > 1): self.jitters.append(np.mean(self.jitter)) self.jstds.append(np.std(self.jitter)) self.amps.append(np.mean(self.amp)) self.astds.append(np.std(self.amp)) if (self.chkbx_feedback.IsChecked()) & (np.abs(np.mean(self.jitter)) > 50) & (np.abs(np.mean(self.jitter)) < 1000): moveTo = STAGEpv.get()*mm2fs - np.mean(self.jitter) STAGEpv.put(moveTo/mm2fs) self.stagePos.append(STAGEpv.get()) else: print("{} Where are my X-rays?".format(self.time)) self.jitters.append(np.nan) self.jstds.append(np.nan) self.amps.append(np.nan) self.astds.append(np.nan) self.stagePos.append(np.nan) self.draw_plot() def draw_plot(self): self.plot_jitters.clear() self.plot_amps.clear() self.plot_edges.clear() self.plot_timeseries_jitters.clear() self.plot_timeseries_amps.clear() self.plot_stage.clear() self.plot_edges.set_xlabel('relative arrival time, fs') self.plot_edges.set_xlim(-1500,1500) self.plot_edges.set_ylabel('transmission change, %') self.plot_timeseries_jitters.set_xlabel('time ago, a.u.') self.plot_timeseries_jitters.set_ylabel('arrival time, fs') self.plot_timeseries_amps.set_xlabel('time ago, a.u.') self.plot_timeseries_amps.set_ylabel('edge amplitude, %') self.plot_stage.set_xlabel('time ago, a.u.') self.plot_stage.set_ylabel('m452 position, mm') self.plot_edges.set_xlabel('relative arrival time, fs') self.plot_edges.set_xlim(-1500,1500) self.plot_edges.set_ylabel('transmission change, %') self.plot_jitters.set_xlabel('relative arrival time, fs') self.plot_jitters.set_ylabel('counts') self.plot_amps.set_xlabel('edge amplitudes, %') self.plot_amps.set_ylabel('counts') if (len(self.deriv) > 0): self.plot_edges.plot(timeAx, self.deriv[random.randrange(0, len(self.deriv))], color='grey') self.plot_edges.plot(timeAx, self.deriv[random.randrange(0, len(self.deriv))], color='grey') self.plot_edges.plot(timeAx, self.deriv[random.randrange(0, len(self.deriv))], color='grey') self.plot_edges.plot(timeAx, self.deriv[random.randrange(0, len(self.deriv))], color='grey') self.plot_edges.plot(timeAx, self.deriv[random.randrange(0, len(self.deriv))], color='grey') self.plot_edges.plot(timeAx, np.mean(self.deriv, axis=0), color='orangered') self.plot_edges.set_title("{}, offset of {}$\,$fs, jitter of {}$\,$fs rms".format(self.time,np.round(np.mean(self.jitter),1), np.round((np.std(self.jitter)),1))) self.plot_edges.axvline(x=np.mean(self.jitter), color='k', linestyle="--") self.plot_jitters.hist(self.jitter, facecolor='orangered', edgecolor='black') self.plot_amps.hist(self.amp, facecolor='orangered', edgecolor='black') self.plot_stage.plot(self.stagePos, 'o-') self.plot_timeseries_jitters.fill_between(np.arange(0, len(self.jitters)), np.asarray(self.jstds)+np.asarray(self.jitters), -np.asarray(self.jstds)+np.asarray(self.jitters), color='gold') self.plot_timeseries_jitters.plot(np.arange(0, len(self.jitters)), self.jitters, 'o-', color='orangered') self.plot_timeseries_amps.plot(np.arange(0, len(self.amps)), self.amps, 'o-', color='orangered') self.plot_timeseries_amps.fill_between(np.arange(0, len(self.amps)), np.asarray(self.astds)+np.asarray(self.amps), -np.asarray(self.astds)+np.asarray(self.amps), color='gold') self.plot_edges.draw() self.plot_jitters.draw() self.plot_amps.draw() self.plot_timeseries_jitters.draw() self.plot_timeseries_amps.draw() self.plot_stage.draw() def on_click_clearQ(self, _event): self.jitters.clear() self.jstds.clear() self.amps.clear() self.astds.clear() self.stagePos.clear()