Skip to content
Snippets Groups Projects
gunPhaseScan.py 15.7 KiB
Newer Older
Mauro Trovo's avatar
Mauro Trovo committed
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
Gun Phase scan
Created in quarantena 2020
@author: mauro 
"""
#import os
import sys
import numpy as np
import PyTango
import h5py
import time

from PyQt4.QtCore import *
from PyQt4.QtGui import *
from scipy.interpolate import interp1d
from scipy.optimize import minimize_scalar
from threading import Thread
#from matplotlib import pyplot as plt
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
#from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure

# variabili globali
# phase_offset = 0
treadOut = []
# un dictionary for i device:
t_dev = {}
try:
    t_dev['pil_sh'] = PyTango.DeviceProxy('srv-tango-srf-01:20000/usa/mps/shutter') # ->Close, Open State
    t_dev['inj_current'] = PyTango.DeviceProxy('srv-tango-srf-01:20000/inj/diagnostics/cm_inj.01')
    t_dev['charge_fb'] = PyTango.DeviceProxy('srv-tango-srf-01:20000/f/feedback/charge_fb.01')
    t_dev['timing'] = PyTango.DeviceProxy('srv-tango-srf-01:20000/f/timing/bunchnumber_f') #,'BunchNumberFrequency'
    t_dev['llrf_k1'] = PyTango.DeviceProxy('srv-tango-srf-01:20000/kg01/mod/llrf_kg01.01') #'cav_phase_set'
    t_dev['mod_k1'] = PyTango.DeviceProxy('srv-tango-srf-01:20000/kg01/mod/rfamp')
    t_dev['llrf_ks'] = PyTango.DeviceProxy('srv-tango-srf-01:20000/kgsp/mod/llrf_kgsp.01')
    t_dev['pil_en'] = PyTango.DeviceProxy('srv-tango-srf-01:20000/pil/energy_meter/eml_pil.01')
except PyTango.DevFailed:
    print('error defining device, DeviceProxys')

#####################
class PhaScan():
    def __init__(self, inputs):
    		"""dati scan"""
    		self.inputs = inputs # [startphase, stopphase, stepphase, n_shot]
    		self.phases = np.linspace(inputs[0], inputs[1], num=(int((inputs[1]-inputs[0])/inputs[2])+1), endpoint=True)
    		self.currents = np.zeros(len(self.phases))
    		self.curr_errors = np.zeros(len(self.phases))
              		
    def getscan(self):
        rnmCheck = False
        # % wave dei valori di fase:
        phase_wave = []
        for i in range(int(self.inputs[3])):
            phase_wave.extend(self.phases)
            phase_wave.sort()
            
        phaseWaveform = np.fmod(phase_wave, 360)
        # % set number of waveform cycles (WaveLoopMode must be set to 0) a = 1;
        t_dev['llrf_k1'].write_attribute('PhaseWaveformNumCycles', 1) 
        t_dev['llrf_k1'].write_attribute('PhaseWaveformAbsMode', True) # False
        # % load waveform
        t_dev['llrf_k1'].write_attribute('PhaseWaveform', phaseWaveform)
        # % set current waveform as absolute value, a=1;
        t_dev['llrf_k1'].write_attribute('PhaseWaveformAbsMode', True) # False
        if t_dev['llrf_k1'].read_attribute('RnmMode').value:
            rnmCheck = True
        t_dev['llrf_k1'].write_attribute('RnmMode', False) # False

        # % read current bunch number
        b_stemp = t_dev['timing'].read_attribute('BunchNumber')
        bunchstart =  b_stemp.value + 20
        #print(bunchstart)
        # % set bunch number start
        t_dev['llrf_k1'].write_attribute('PhaseWaveformBunchNumberStart', bunchstart)
        t_dev['llrf_k1'].command_inout('PhaseWaveformStart')
        
        #        aspetto che sia finita la wave frequenza pari
        try:
            freq = t_dev['timing'].read_attribute('BunchNumberFrequency')
            # print(freq.value)
            frequenza = freq.value
        except PyTango.DevFailed:
            print('error reading Frequency')
            frequenza = 10
        time.sleep(((len(phase_wave)) / frequenza) + 1)
        
        indice = [1, bunchstart+1, (bunchstart +1+ len(phase_wave))]
        bunch_shots = t_dev['inj_current'].command_inout('GetCharge', indice)
        for i in range(len(self.phases)):
        	self.currents[i] = np.mean(bunch_shots[int(self.inputs[3])*i : int(self.inputs[3])*(i+1)])
        	self.curr_errors[i] = np.std(bunch_shots[int(self.inputs[3])*i : int(self.inputs[3])*(i+1)])
        
        t_dev['llrf_k1'].command_inout('PhaseWaveformAbort')
        if rnmCheck:
            t_dev['llrf_k1'].write_attribute('RnmMode', True)

        output = [self.phases, self.currents, self.curr_errors]
        return output

#####################

class Lancio(Thread):
    def __init__(self, nome, inputs):
        Thread.__init__(self)
        self.nome = nome
        self.input = inputs
        
    def __del__(self):
        self.exiting = False
        #self.wait()
   
    def run(self):
        ph_scan = PhaScan(self.input)
        global treadOut
        treadOut = ph_scan.getscan()

###############################################################################

class MainWindow(QDialog):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setWindowTitle("Gun phase scan")
        
        self.dati_scan = []
        self.phase_offset = 0
        # input ###############################################################
        larghezzal = 90
        self.gruppo_input = QGroupBox('Inputs')
        #self.gruppo_input.setStyleSheet("border: 1px solid #E77200")
        self.gruppo_input.setStyleSheet("color : blue")
        self.label1 = QLabel("Start phase")
        self.label1.setMaximumWidth(larghezzal)
        self.startphase = QDoubleSpinBox()
        self.startphase.setSingleStep(5)
        self.startphase.setMinimum(0.0)
        self.startphase.setMaximum(500.0)
        self.startphase.setValue(250)
        self.label2 = QLabel("Stop phase")
        self.label2.setMaximumWidth(larghezzal)
        self.stopphase = QDoubleSpinBox()
        self.stopphase.setSingleStep(5)
        self.stopphase.setMinimum(0.0)
        self.stopphase.setMaximum(500.0)
        self.stopphase.setValue(400)
        self.label3 = QLabel("Step")
        self.label3.setMaximumWidth(larghezzal)
        self.stepphase = QDoubleSpinBox()
        self.stepphase.setSingleStep(1)
        self.stepphase.setMinimum(1.0)
        self.stepphase.setMaximum(15.0)
        self.stepphase.setValue(3)
        self.label4 = QLabel("n. shots")
        self.label4.setMaximumWidth(larghezzal)
        self.n_shot = QDoubleSpinBox()
        self.n_shot.setSingleStep(1)
        self.n_shot.setMinimum(2.0)
        self.n_shot.setMaximum(20.0)
        self.n_shot.setValue(5)
        self.combo = QComboBox()
        self.combo.addItem("No knee - 80 MV/m")
        self.combo.addItem("Low knee - 80 MV/m")
        self.combo.addItem("High knee - 80 MV/m")
        self.combo.addItem("No knee - 100 MV/m")
        self.combo.addItem("Low knee - 100 MV/m")
        self.combo.addItem("High knee - 100 MV/m")
        self.combo.addItem("Low knee - 5 ps")
        self.combo.addItem("High knee - 5 ps")
        self.combo.setCurrentIndex(7) # definisco un default

        layoutinput = QFormLayout()
        layoutinput.addRow(self.label1, self.startphase)
        layoutinput.addRow(self.label2, self.stopphase)
        layoutinput.addRow(self.label3, self.stepphase)
        layoutinput.addRow(self.label4, self.n_shot)
        layoutinput.addRow(self.combo)
        self.gruppo_input.setLayout(layoutinput)

        # actions ########################################################################################################
        self.gruppo_action = QGroupBox('Actions')
        #self.gruppo_action.setStyleSheet("border: 1px solid #009DC4")
        self.gruppo_action.setStyleSheet("color : navy")
        self.tasto_scan = QPushButton('Start SCAN')
        self.tasto_scan.setCheckable(True)
        self.tasto_scan.setStyleSheet("background-color : aqua")

        # self.tasto_scan.clicked.connect(self.btnstate)
        self.tasto_scan.clicked.connect(self.phasescanstart)
        self.tasto_save = QPushButton('SAVE Data')
        self.tasto_save.clicked.connect(self.savedata)
        self.tasto_save.setEnabled(False)
        self.tasto_fit = QPushButton('FIT Data')
        self.tasto_fit.clicked.connect(self.fitdataset)
        self.tasto_fit.setEnabled(False)
        self.delta_phase = QDoubleSpinBox()
        self.delta_phase.setSingleStep(0.2)
        self.delta_phase.setMinimum(-50.0)
        self.delta_phase.setMaximum(-10.0)
        self.delta_phase.setValue(-30)
        self.tasto_setph = QPushButton('SET Phase')
        self.tasto_setph.clicked.connect(self.phaseK1set)
        self.tasto_setph.setEnabled(False)

        layoutaction = QVBoxLayout()
        layoutaction.addWidget(self.tasto_scan)
        layoutaction.addWidget(self.tasto_fit)
        layoutaction.addWidget(self.delta_phase)
        layoutaction.addWidget(self.tasto_setph)
        layoutaction.addWidget(self.tasto_save)
        self.gruppo_action.setLayout(layoutaction)

		# a figure instance to plot on
        self.figure1 = Figure(figsize=(3.8, 3.8))
        
		# this is the Canvas Widget that displays the `figure`
		# it takes the `figure` instance as a parameter to __init__
        self.canvas1 = FigureCanvas(self.figure1)
        self.ax1 = self.figure1.add_subplot(211)
        self.ax2 = self.figure1.add_subplot(212)
         
		### layout generale ####################################
        loglobale = QGridLayout()
        loglobale.addWidget(self.gruppo_input, 0, 0, 1, 1)
        loglobale.addWidget(self.gruppo_action, 1, 0, 1, 1)
        loglobale.addWidget(self.canvas1, 0, 1, 2, 1)
        self.setLayout(loglobale)
        self.show()
        
# definiamo i metodi:
############fuinzione di servizio per il display di messaggio ###########

    def showdialog_e(self, s_testo):
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Warning)
        msg.setText(s_testo)
        # msg.setInformativeText("This is additional information")
        msg.setWindowTitle("Error Message")
        # msg.setDetailedText("The details are as follows:")
        msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
        # msg.buttonClicked.connect(msgbtn)
        msg.exec_()
        # print "value of pressed message box button:", retval

    def btnstate(self):
        if self.tasto_scan.isChecked():
            self.tasto_scan.setText('Scan done')
        else:
            self.tasto_scan.setText('SCAN done')

############fuinzioni che fanno qualcosa ###########

    def phasescanstart(self):
        # inserisco il controllo sullo stato del feedback di carica: DA VERIFICARE
        status_fb_q = t_dev['charge_fb'].read_attribute('State')
        if (status_fb_q.value) == 0:
            l1 = "Please, STOP the Charge feedback before scanning!"
            self.showdialog_e(l1)
            self.tasto_scan.toggle()
            return -1

        #status_pilsh = t_dev['pil_sh'].read_attribute('State') if 'CLOSE' in str(status_pilsh):
        status_pilsh = t_dev['pil_sh'].command_inout('State')
        if 'CLOSE' in str(status_pilsh):
            l2 = "PIL shutter is closed, OPEN before scanning!"
            self.showdialog_e(l2)
            self.tasto_scan.toggle()
            return -1

   		# leggo la fase iniziale:
        try:
            phase_start = t_dev['llrf_k1'].read_attribute('cav_phase_set')
            phase_in = phase_start.value
        except PyTango.DevFailed:
            print('error reading phase in, DeviceProxys')
        pass

        w_title = "Gun phase scan " + time.strftime("%H:%M")
        self.setWindowTitle(w_title)
        self.tasto_scan.setText('Scan running ...')
        self.show()

        # costruisco l'imput dello scan
        inputs = [self.startphase.value(), self.stopphase.value(), self.stepphase.value(), self.n_shot.value()]

        thread_scan = Lancio("scan1", inputs)
        thread_scan.start()
         
        # Return whether the thread is alive.
        # while thread_scan.is_alive():
        while thread_scan.isAlive():
            bunch_shot = t_dev['inj_current'].command_inout('GetCharge', [0,200])
            self.ax1.clear()
            self.ax1.plot(bunch_shot, 'b')
            # refresh canvas
            self.canvas1.repaint()
            self.canvas1.draw()
            time.sleep(0.3)

        self.dati_scan = treadOut
        # ripristino la fase
        t_dev['llrf_k1'].write_attribute('cav_phase_set', phase_in)
        # plot data
        self.ax1.clear()
        self.ax2.clear()
        # self.ax1.plot(dati_scan(1), dati_scan(2), '*-b')
        self.ax1.errorbar(self.dati_scan[0], self.dati_scan[1], yerr=self.dati_scan[2], xerr=None, fmt='.-g')
        # refresh canvas
        self.canvas1.draw()
        # abilito i tasti successivi
        self.tasto_save.setEnabled(True)
        self.tasto_fit.setEnabled(True)
        self.tasto_scan.setText('Re Start SCAN')
        self.tasto_scan.toggle()

        return 0
    
        
    def fitdataset(self):
        
        x_scan = self.dati_scan[0]
        y_scan = self.dati_scan[1]		
        y_scan_norm = np.divide(y_scan - min(y_scan),(max(y_scan) - min(y_scan))) #da provare
#        y_scan_norm = np.divide(y_scan, max(y_scan))
        # cerco il massimo
        range = x_scan[np.argmax(y_scan)] + 90.
        
        # dati delle simulazioni estratti dal file scans....
        f1 = h5py.File('PhaseScans.hdf5', 'r')
        dset = f1['phase_scans']
        x_sim = dset[0,:]
        y_sim = dset[(self.combo.currentIndex()+1),:]
        
        #creo la funzione di interpolazione necesaria poco dopo:
        f_int = interp1d(x_sim, y_sim, kind='cubic', bounds_error=False, fill_value=0)
        # definisco la funzione che parametrizza la distanza in fase tra le curve:
        def diffDataConOffset(shift):
           			punti = y_scan_norm * f_int(x_scan - shift)
           			return 1./np.sum(punti)
        		
        # a funzione che minimizza:
        res = minimize_scalar(diffDataConOffset, bounds=(range -30, range + 30), method='bounded')
        self.phase_offset = res.x
        offest_s = "%.1f" % self.phase_offset   # trasformo il numero in stringa per dopo
        
        # parte grafica di output
        self.ax2.clear()
        self.ax2.plot((x_scan - res.x), y_scan_norm, '.g', x_sim, y_sim, 'r')
        self.ax2.set_xlabel('Offset = ' + offest_s + ' deg')
        # refresh canvas
        self.canvas1.draw()
        f1.close()
        self.tasto_setph.setEnabled(True)


    def phaseK1set(self):
        phase_value = self.delta_phase.value() + self.phase_offset
        print(phase_value)
        # mi assicuro che il valore sia buono
        if phase_value > 360:
        	phase_value = phase_value - 360
        if phase_value < 0:
        	phase_value = phase_value + 360		
        t_dev['llrf_k1'].write_attribute('cav_phase_set', phase_value)
        # definisco la cresta di K1
        time.sleep(0.5)
        t_dev['mod_k1'].command_inout('ResetKlystronPhase')

    def savedata(self, dati):
        # recupero la PIL energy:
        pil_energy = t_dev['pil_en'].read_attribute('Corrected_energy')
        pil_energy_v = pil_energy.value
        # % compongo la stringa per il salvataggio:
        pathf = '/home/fermi/data/ShottkyScan/runXX/'
#        filename = 'ShScan_'+ time.strftime("%Y%m%d_%H%M%S")
        filename = 'ShScan_'+ time.strftime("%Y%m%d_%H%M")
#        print 'saving file : ', fn
        f_out = h5py.File('%s%s.hdf5'%(pathf, filename), 'w')
        f_out.create_dataset('PILenegy', data = pil_energy_v)
        f_out.create_dataset('Ph Offset', data = self.phase_offset)
        f_out.create_dataset('Scan', data = self.dati_scan)
        f_out.close()
#        return '%s%s.hdf5'%(dn, fn)

#####################
if __name__ == '__main__':
    app = QApplication([])
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())