import ch.psi.pshell.epics.Positioner as Positioner from mathutils import fit_polynomial #Parameters dry_run = False do_elog = True USE_SCREEN_PANEL = True MO_FREQ = 142.8e6 # MO = 142.8 MHz FW_THRESHOLD = 0.20 is_panel = get_exec_pars().source != CommandSource.ui # run from panel if is_panel: start = args[0] stop = args[1] + 0.001 # needed to get the last point step = args[2] nb = int(args[3]) lat = args[4] thr = args[5] tds = args[6] bunch = args[7] screen = args[8] user_calib_enabled = args[9] user_calib = args[10] plt11 = args[11] plt12 = args[12] plt21 = args[13] plt22 = args[14] else: start = -0.1 stop = 0.1 + 0.001 step = 0.05 nb = 10 lat = 0.4 thr = 20 tds = "SATMA02" bunch = "Bunch2" screen = "SATBD02-DSCR050" user_calib_enabled = False user_calib = 1.0 plt11 = plot(None, title="Output")[0] plt12 = plot(None, title="Output")[0] plt21 = plot(None, title="Output")[0] plt22 = plot(None, title="Output")[0] run("Tools/BunchLengthTDSdata.py") #Record callback: update of output plot def update_plot(record, scan, plt): com_mean, com_stdev = record[0].mean, record[0].stdev rms_mean, rms_stdev = record[1].mean, record[1].stdev fw_mean, fw_stdev = record[2].mean, record[2].stdev fit_mean, fit_stdev = record[3].mean, record[3].stdev phase = record.positions[0] plt.getSeries(0).appendData(phase, com_mean, com_stdev) plt.getSeries(1).appendData(phase, rms_mean, rms_stdev) plt.getSeries(2).appendData(phase, fw_mean, fw_stdev) plt.getSeries(3).appendData(phase, fit_mean, fit_stdev) def update_plot_1(record, scan): global plt11 update_plot(record, scan, plt11) def update_plot_2(record, scan): global plt12 update_plot(record, scan, plt12) #Plot setup def init_plots(plt1, plt2): plt1.clear() plt1.removeMarker(None) plt1.setStyle(plt1.Style.ErrorY) plt1.addSeries(LinePlotErrorSeries("Bunch center of mass", Color.red)) plt1.addSeries(LinePlotErrorSeries("Bunch length rms", Color.yellow, 2)) plt1.addSeries(LinePlotErrorSeries("Bunch length fw", Color.green, 2)) plt1.addSeries(LinePlotErrorSeries("Bunch length gauss fit", Color.blue, 2)) plt1.getAxis(plt1.AxisId.X).setLabel("TDS Phase (deg)") plt1.getAxis(plt1.AxisId.Y).setLabel("Bunch center of mass (um)") plt1.getAxis(plt1.AxisId.Y2).setLabel("Bunch length (um)") plt1.setLegendVisible(True) plt2.clear() plt2.removeMarker(None) plt2.setStyle(plt2.Style.Normal) plt2.getAxis(plt2.AxisId.X).setLabel("Time (fs)") plt2.getAxis(plt2.AxisId.Y).setLabel("Current (kA)") plt2.setLegendVisible(False) init_plots(plt11, plt21) init_plots(plt12, plt22) #Creating Phase positioner if dry_run: phase = DummyPositioner("TDS Phase") screen = "simulation" else: phase = Positioner("TDS Phase", tds + "-RSYS:SET-BEAM-PHASE", tds + "-RSYS:SET-BEAM-PHASE") camera_name = screen phase.config.minValue = -90.0 phase.config.maxValue = 360.0 phase.config.precision = 4 phase.config.deadband = 0.05 #phase.config.rotation = True # this may cause trouble when scanning around zero phase.config.save() phase.initialize() phase0 = phase.read() % 360 #Camera setup cam_server.start(camera_name +"_sp", USE_SCREEN_PANEL) cam_server.setFunction("bunch_length_op") cam_server.setInstanceConfigValue("replace", True) cam_server.setInstanceConfigValue("fw_threshold", FW_THRESHOLD) cam_server.setThreshold(thr) cam_server.setBackgroundSubtraction(False) cam_server.setGoodRegion(0.05, 5.0) print "Waiting for fw and fit" xy = tds_data[tds]["DEFAULTS"]["plane"] timeout = 1.0 start_time = time.time() while (True): wait_cam_server_message() fw = cam_server.stream.getChild(xy + "_fw") gr_fit = cam_server.stream.getChild("gr_" + xy + "_fit_standard_deviation") if (fw is not None) and (gr_fit is not None): break if time.time() - start_time > timeout: if fw is None: raise Exception(xy + "_fw not in camera stream") if gr_fit is None: raise Exception(xy + "_fit not in camera stream") com = cam_server.stream.getChild(xy + "_center_of_mass") rms = cam_server.stream.getChild(xy + "_rms") profile = cam_server.stream.getChild(xy + "_profile") axis = cam_server.stream.getChild(xy + "_axis") image = cam_server.getDataMatrix() set_device_alias(image, "image") #Creating averagers com_averager = create_averager(com, nb, -1) # -1 event based, waits for the next value rms_averager = create_averager(rms, nb, -1) fw_averager = create_averager(fw, nb, -1) fit_averager = create_averager(gr_fit, nb, -1) rms_averager.monitored = True # not blocking, will return last nb values fw_averager.monitored = True fit_averager.monitored = True #The scan loop try: bunch1_charge = caget("SINEG01-DBPM340:Q1") bunch2_charge = caget("SINEG01-DBPM340:Q2") bunch_charge = bunch1_charge if bunch == "Bunch1" else bunch2_charge sensor_list = [com_averager, rms_averager, fw_averager, fit_averager, axis, profile, com, image] phase.write(start) time.sleep(3.0) # get bs phase-jitter / screen position data for SATMA02 calib r1 = lscan(phase, sensor_list, start, stop, step , latency=lat, after_read = update_plot_1) start, stop = start + 180.0, stop + 180.0 phase.write(start) time.sleep(3.0) r2 = lscan(phase, sensor_list, start, stop, step , latency=lat, after_read = update_plot_2) finally: phase.write(0.001) # 0.0 seems to give 360 deg?! phase.close() cam_server.stop() # stops cam_server but does not close it cam_server is a global object # stop the beam if not dry_run: if bunch == "Bunch1": caput("SIN-TIMAST-TMA:Bunch-1-OnDelay-Sel", 1) else: caput("SIN-TIMAST-TMA:Bunch-2-OnDelay-Sel", 1) caput("SIN-TIMAST-TMA:Beam-Apply-Cmd.PROC", 1) #output def write_output(r, plt): phase_pos = r.getPositions(0) rf_period = 1 / MO_FREQ / tds_data[tds]["harm"] time_pos = [val / 360.0 * rf_period * 1e15 for val in phase_pos] # in fs bunch_center = [val.mean for val in r.getReadable(0)] bunch_center_stdev = [val.stdev for val in r.getReadable(0)] a0, a1 = fit_polynomial(time_pos, bunch_center, 1) calib = abs(a1) # in fs/um if user_calib_enabled: calib = rf_period * 1e15 / 360 / user_calib / 1000 # in fs/um if dry_run: calib = 1 bunch_length_rms = [val.mean * calib for val in r.getReadable(1)] bunch_length_rms_average = sum(bunch_length_rms) / len(bunch_length_rms) bunch_length_fw = [val.mean * calib for val in r.getReadable(2)] bunch_length_fw_average = sum(bunch_length_fw) / len(bunch_length_fw) bunch_length_fit = [val.mean * calib for val in r.getReadable(3)] bunch_length_fit_average = sum(bunch_length_fit) / len(bunch_length_fit) profiles_um = r[axis] profiles_dig = r[profile] profiles_com = r[com] com_avg = sum(profiles_com) / len(profiles_com) profiles_fs = [] profiles_kA = [] for i in range(len(r[axis])): delta_um = profiles_com[i] - com_avg profiles_fs.append([(val - delta_um) * calib for val in profiles_um[i]]) step_fs = (max(profiles_fs[i]) - min(profiles_fs[i])) / (len(profiles_fs[i]) - 1) total_dig = float(sum(profiles_dig[i])) profiles_kA.append([val / total_dig * bunch_charge / step_fs for val in profiles_dig[i]]) # pC / fs = kA for i in range(len(profiles_um)): plt.addSeries(LinePlotSeries("Bunch current " + str(i), Color.red)) plt.getSeries(i).setData(profiles_fs[i], profiles_kA[i]) return bunch_length_rms_average, bunch_length_fw_average, bunch_length_fit_average, calib bunch_length_rms_average1, bunch_length_fw_average1, bunch_length_fit_average1, calib1 = write_output(r1, plt21) bunch_length_rms_average2, bunch_length_fw_average2, bunch_length_fit_average2, calib2 = write_output(r2, plt22) bunch_length_rms = (bunch_length_rms_average1 + bunch_length_rms_average2) / 2 bunch_length_fw = (bunch_length_fw_average1 + bunch_length_fw_average2) / 2 bunch_length_fit = (bunch_length_fit_average1 + bunch_length_fit_average2) / 2 calib = (calib1 + calib2) / 2 #archiver channels if do_elog: if bunch == "Bunch1": caput(tds + "-RTDS100:BD-BUNCH1-DURATION", bunch_length_fit) caput(tds + "-RTDS100:BD-BUNCH1-CALIBRATION", 1 / calib) else: caput(tds + "-RTDS100:BD-BUNCH2-DURATION", bunch_length_fit) caput(tds + "-RTDS100:BD-BUNCH2-CALIBRATION", 1 / calib) #Elog entry if do_elog: if get_option("Generated data file:\n" + get_exec_pars().path +"\n\n" + "Save to ELOG?", "YesNo") == "Yes": title = "Bunch length Scan" log_msg = "Data file: " + get_exec_pars().path + "\n\n" log_msg = log_msg + "TDS: " + tds + "\n" log_msg = log_msg + "Bunch: " + bunch + "\n\n" log_msg = log_msg + "#### 0 deg ####\n" log_msg = log_msg + "Bunch length rms: %0.1f" % bunch_length_rms_average1 + " fs\n" log_msg = log_msg + "Bunch length fw: %0.1f" % bunch_length_fw_average1 + " fs\n" log_msg = log_msg + "Bunch length fit: %0.1f" % bunch_length_fit_average1 + " fs\n" log_msg = log_msg + "Calibration: %0.4f" % calib1 + " fs/um\n\n" log_msg = log_msg + "#### 180 deg ####\n" log_msg = log_msg + "Bunch length rms: %0.1f" % bunch_length_rms_average2 + " fs\n" log_msg = log_msg + "Bunch length fw: %0.1f" % bunch_length_fw_average2 + " fs\n" log_msg = log_msg + "Bunch length fit: %0.1f" % bunch_length_fit_average2 + " fs\n" log_msg = log_msg + "Calibration: %0.4f" % calib2 + " fs/um\n\n" log_msg = log_msg + "#### mean ####\n" log_msg = log_msg + "Bunch length rms: %0.1f" % bunch_length_rms + " fs\n" log_msg = log_msg + "Bunch length fw: %0.1f" % bunch_length_fw + " fs\n" log_msg = log_msg + "Bunch length fit: %0.1f" % bunch_length_fit + " fs\n" log_msg = log_msg + "Calibration: %0.4f" % calib + " fs/um" sleep(0.1) #Give some time to plot to be finished - it is not sync with acquisition file_name1 = os.path.abspath(Setup.getContextPath() + "/BunchLengthScanPlot1.png") plt11.saveSnapshot(file_name1, "png", Dimension(400, 300)) file_name2 = os.path.abspath(Setup.getContextPath() + "/BunchLengthCurrent1.png") plt21.saveSnapshot(file_name2, "png", Dimension(400, 300)) file_name3 = os.path.abspath(Setup.getContextPath() + "/BunchLengthScanPlot2.png") plt12.saveSnapshot(file_name3, "png", Dimension(400, 300)) file_name4 = os.path.abspath(Setup.getContextPath() + "/BunchLengthCurrent2.png") plt22.saveSnapshot(file_name4, "png", Dimension(400, 300)) elog(title, log_msg, [file_name1, file_name2, file_name3, file_name4]) set_return([bunch_length_rms_average1, bunch_length_fw_average1, bunch_length_fit_average1, calib1, bunch_length_rms_average2, bunch_length_fw_average2, bunch_length_fit_average2, calib2, bunch_length_rms, bunch_length_fw, bunch_length_fit, calib])