Commit 1994da6e authored by reiche's avatar reiche

Initial commit

parent da07aaaf
Pipeline #931 failed with stages
This diff is collapsed.
This diff is collapsed.
# server to check status of bunch compressor and synchronize the values
from datetime import datetime
import sched, time
# modules for interface with epics and the softIOC package
from pcaspy import Driver, SimpleServer, Alarm, Severity
import sys
from epics import caget,caput,PV
# program specific modules - transformation between moto position etc.
from BCcal import BCcal
# the channels for the server
prefix = 'SF-BC-SERVER:'
pvdb = {
'SINBC-TOF' : {'prec': 3, 'scan' : 2, 'unit' : 'ps'},
'SINBC-R56' : {'prec': 3, 'scan' : 2, 'unit' : 'mm'},
'SINBC-ANGLE' : {'prec': 3, 'scan' : 2, 'unit' : 'degree'},
'SINBC-OFFSET' : {'prec': 3, 'scan' : 2, 'unit' : 'mm'},
'S10BC-TOF' : {'prec': 3, 'scan' : 2, 'unit' : 'ps'},
'S10BC-R56' : {'prec': 3, 'scan' : 2, 'unit' : 'mm'},
'S10BC-ANGLE' : {'prec': 3, 'scan' : 2, 'unit' : 'degree'},
'S10BC-OFFSET' : {'prec': 3, 'scan' : 2, 'unit' : 'mm'},
'STATUS' : {'type': 'enum', 'scan' : 2,
'enums': ['Running','Paused','Reset'],
'states' : [Severity.NO_ALARM,Severity.MINOR_ALARM,Severity.No_ALARM]},
'ACTIVITY' : {'type': 'string', 'scan' : 2}
}
class BDServerBunchCompressor(Driver):
def __init__(self):
super(BDServerBunchCompressor, self).__init__()
self.config={}
self.config['LH'] =BCcal('LH')
self.config['BC1']=BCcal('BC1')
self.config['BC2']=BCcal('BC2')
# conncet to the PV of the mover and add eventhandler for changes
self.pvBC1Mot=PV('SINBC02-BC1:MOTOR_X1.VAL')
self.pvBC1Mot.add_callback(self.BCMotorChanged)
self.pvBC2Mot=PV('S10BC02-BC2:MOTOR_X1.VAL')
self.pvBC2Mot.add_callback(self.BCMotorChanged)
self.pvBC1Tof=PV('SINBC:TOF')
self.pvBC1R56=PV('SINBC:R56')
self.pvBC2Tof=PV('S10BC:TOF')
self.pvBC2R56=PV('S10BC:R56')
# initialize to the current values
self.resetServer()
self.running=False # to keep the timer thread running
self.setParam('STATUS',1)
# routine to update the EPICS channels of this server
def updateEPICS(self,tag,alarm=False):
if 'BC1' in tag:
self.setParam('SINBC-TOF',self.config['BC1'].delay)
self.setParam('SINBC-R56',self.config['BC1'].r56)
self.setParam('SINBC-ANGLE', self.config['BC1'].angle)
self.setParam('SINBC-OFFSET',self.config['BC1'].offset)
elif 'BC2' in tag:
self.setParam('S10BC-TOF',self.config['BC2'].delay)
self.setParam('S10BC-R56',self.config['BC2'].r56)
self.setParam('S10BC-ANGLE', self.config['BC2'].angle)
self.setParam('S10BC-OFFSET',self.config['BC2'].offset)
st = datetime.now().strftime('Last update: %Y-%m-%d %H:%M:%S')
self.setParam('ACTIVITY',st)
# event handler if motor position has changed
def BCMotorChanged(self,pvname=None,char_value=None,**kw):
value=float(char_value)
if 'SINBC02' in pvname:
self.config['BC1'].setByOffset(value)
self.updateEPICS('BC1')
self.FollowMotorChange('BC1')
elif 'S10BC02' in pvname:
self.config['BC2'].setByOffset(value)
self.updateEPICS('BC2')
self.FollowMotorChange('BC2')
def FollowMotorChange(self,tag):
if self.running is False: # don't do anything if server is not actively running
return
if 'BC1'is tag:
self.pvBC1Tof.put(self.config['BC1'].delay)
self.pvBC1R56.put(self.config['BC1'].r56)
elif 'BC2' is tag:
self.pvBC2Tof.put(self.config['BC2'].delay)
self.pvBC2R56.put(self.config['BC2'].r56)
def FollowServerChange(self):
if self.running is False:
return
self.pvBC1Mot.put(self.config['BC1'].offset)
self.pvBC2Mot.put(self.config['BC2'].offset)
# routine BCMotorChanged will be call since the offset has been changed
def resetServer(self):
self.config['BC1'].setByOffset(self.pvBC1Mot.get())
self.updateEPICS('BC1')
self.FollowMotorChange('BC1')
self.config['BC2'].setByOffset(self.pvBC2Mot.get())
self.updateEPICS('BC2')
self.FollowMotorChange('BC1')
# event haendler for read and write
def read(self, reason):
value = self.getParam(reason)
return value
def write(self,reason,value):
if reason == 'SINBC-TOF':
self.config['BC1'].setByDelay(value)
if reason == 'SINBC-R56':
self.config['BC1'].setByR56(value)
if reason == 'SINBC-ANGLE':
self.config['BC1'].setByAngle(value)
if reason == 'SINBC-OFFSET':
self.config['BC1'].setByOffset(value)
if reason == 'S10BC-TOF':
self.config['BC2'].setByDelay(value)
if reason == 'S10BC-R56':
self.config['BC2'].setByR56(value)
if reason == 'S10BC-ANGLE':
self.config['BC2'].setByAngle(value)
if reason == 'S10BC-OFFSET':
self.config['BC2'].setByOffset(value)
if 'SINBC' in reason:
self.updateEpics('BC1')
self.FollowServerChange()
return
if 'S10BC' in reason:
self.updateEpics('B21')
self.FollowServerChange()
return
if reason == 'STATUS':
if value == 0 : # start the server
self.running=True
self.FollowServerChange()
elif value == 1: # stop the server
self.running=False
elif value == 2: # reset the server
value == 1
if self.running:
value==0
self.resetServer()
self.setParam(reason,value)
if updateEpics:
self.
def disconnect(self):
print('Good Bye')
def __del__(self):
self.disconnect()
if __name__ == '__main__':
server = SimpleServer()
server.createPV(prefix, pvdb)
driver = BDServerBunchCompressor()
# process CA transactions
while True:
try:
server.process(0.1)
except KeyboardInterrupt:
driver.disconnect()
print('Gracefully exiting...')
sys.exit()
import sys
from math import sqrt
sys.path.append('/sf/bd/applications/OnlineModel/current')
from OMMagnetCalibration import *
class BCcal:
def __init__(self,BC='BC1'):
self.magcal=SwissFELMagnet()
# variables
self.cal=False;
self.refEnergy=1000.
self.magtype='AFBC1'
if (BC=='BC1'): ## calibration data
self.magtype='AFBC1'
self.off2del=[5.257774e-04,-4.768006e-04,2.639313e-02]
self.ang2del=[6.284876e+00,-4.890560e-02,1.422479e-02]
self.ang2r56=[3.784936e+00,-5.876918e-02,1.709704e-02]
self.off2cur=[-5.757576e-06,-2.906394e-01,1.963636e-01]
self.refEnergy=335.1;
self.Energy=self.refEnergy;
self.cal=True;
if (BC=='LH'): ## calibration data
self.magtype='AFL'
self.off2del=[ -0.000000e+00, -1.885013e-01, -1.569589e-16]
self.ang2del=[2.444974e-01, -1.728179e-03 , -0.000000e+00]
self.ang2r56=[ 1.471816e-01, -2.076526e-03, -5.625735e-19]
self.off2cur=[ -8.616596e-18, 1.340000e+00, -1.115774e-15]
self.refEnergy=137.04;
self.Energy=self.refEnergy;
self.cal=True;
if (BC=='BC2'): ## calibration data
self.magtype='AFBC3'
self.off2del=[4.398750e-04, 2.720996e-04,-9.118231e-02]
self.ang2del=[7.457803e+00,-1.559434e-02,2.609833e-03]
self.ang2r56=[4.481649e+00, -1.872441e-02, 3.133944e-03]
self.off2cur=[-4.523810e-06,-2.989405e-01,1.208333e-01]
self.refEnergy=2296.0;
self.Energy=self.refEnergy;
self.cal=True;
self.Energy=self.refEnergy
self.reset()
# conversion function
def reset(self):
self.offset=0 # in mm
self.angle=0 # in degree
self.r56=0 # in mm
self.delay=0 # in ps
self.refI=0
def quadratic(self,x,pol):
res=x*x*pol[0]+x*pol[1]+pol[2]
return res
def invertquadratic(self,y,pol):
a=pol[1]/pol[0]
b=(pol[2]-y)/pol[0]
arg=0.25*a*a-b
if (arg<0):
self.reset()
return [0, 0]
arg=sqrt(arg)
sol=[0.5*a+arg, 0.5*a-arg]
return sol
def getCurrent(self):
ang=self.magcal.BendI2Angle(self.magtype,self.refI,EM=self.refEnergy*1e6)
cur=self.magcal.BendAngle2I(self.magtype,ang,EM=self.Energy*1e6)
return cur
# access function
def setByOffset(self,x):
if x==0 or not self.cal:
self.reset()
return
self.offset=x
self.delay=self.quadratic(x,self.off2del)
self.refI =self.quadratic(x,self.off2cur)
# delay -> angle
self.angle=max(self.invertquadratic(self.delay,self.ang2del))
if (self.angle==0):
self.reset()
return
self.r56 =self.quadratic(self.angle,self.ang2r56)
def setByAngle(self,x):
if x==0 or not self.cal:
self.reset()
return
self.angle=x;
self.delay=self.quadratic(x,self.ang2del)
self.r56 = self.quadratic(x,self.ang2r56)
self.offset=-abs(min(self.invertquadratic(self.delay,self.off2del)))
if self.offset==0:
self.reset()
return
self.refI=self.quadratic(self.offset,self.off2cur)
def setByR56(self,x):
if x==0 or not self.cal:
self.reset()
return
self.r56=x
self.angle=max(self.invertquadratic(self.r56,self.ang2r56))
if self.angle==0:
self.reset()
return
self.delay=self.quadratic(self.angle,self.ang2del)
self.offset=-abs(min(self.invertquadratic(self.delay,self.off2del)))
if self.offset==0:
self.reset()
return
self.refI=self.quadratic(self.offset,self.off2cur)
def setByDelay(self,x):
if x==0 or not self.cal:
self.reset()
return
self.delay=x
self.angle=max(self.invertquadratic(self.delay,self.ang2del))
if (self.angle==0):
self.reset()
return
self.r56 = self.quadratic(self.angle,self.ang2r56)
self.offset=-abs(min(self.invertquadratic(self.delay,self.off2del)))
if self.offset==0:
self.reset()
return
self.refI=self.quadratic(self.offset,self.off2cur)
def setByCur(self,y):
ang=self.magcal.BendI2Angle(self.magtype,y,EM=self.Energy*1e6)
x=self.magcal.BendAngle2I(self.magtype,ang,EM=self.refEnergy*1e6)
if x==0 or not self.cal:
self.reset()
return
self.refI=x
self.offset=-abs(min(self.invertquadratic(x,self.off2cur)))
if self.offset==0:
self.reset()
return
self.delay=self.quadratic(self.offset,self.off2del)
self.angle=max(self.invertquadratic(self.delay,self.ang2del))
if (self.angle==0):
self.reset()
return
self.r56 =self.quadratic(self.angle,self.ang2r56)
def setByEnergy(self,x):
self.Energy=x
import sys
from math import sqrt
sys.path.append('/afs/psi.ch/intranet/SF/Applications/on-line_model/v2.0.0/PythonModule')
from OMMagnetCalibration import *
class BCcal:
def __init__(self,BC='BC1'):
self.magcal=SwissFELMagnet()
# variables
self.cal=False;
self.refEnergy=1000.
self.magtype='AFBC1'
if (BC=='BC1'): ## calibration data
self.magtype='AFBC1'
self.off2del=[5.257774e-04,-4.768006e-04,2.639313e-02]
self.ang2del=[6.284876e+00,-4.890560e-02,1.422479e-02]
self.ang2r56=[3.784936e+00,-5.876918e-02,1.709704e-02]
self.off2cur=[-5.757576e-06,-2.906394e-01,1.963636e-01]
self.refEnergy=335.1;
self.Energy=self.refEnergy;
self.cal=True;
if (BC=='LH'): ## calibration data
self.magtype='AFL'
self.off2del=[ -0.000000e+00, 1.885013e-01, -1.569589e-16]
self.ang2del=[2.444974e-01, -1.728179e-03 , -0.000000e+00]
self.ang2r56=[ 1.471816e-01, -2.076526e-03, -5.625735e-19]
self.off2cur=[ -8.616596e-18, 1.340000e+00, -1.115774e-15]
self.refEnergy=137.04;
self.Energy=self.refEnergy;
self.cal=True;
if (BC=='BC2'): ## calibration data
self.magtype='AFBC3'
self.off2del=[4.398750e-04, 2.720996e-04,-9.118231e-02]
self.ang2del=[7.457803e+00,-1.559434e-02,2.609833e-03]
self.ang2r56=[4.481649e+00, -1.872441e-02, 3.133944e-03]
self.off2cur=[-4.523810e-06,-2.989405e-01,1.208333e-01]
self.refEnergy=2296.0;
self.Energy=self.refEnergy;
self.cal=True;
self.Energy=self.refEnergy
self.reset()
# conversion function
def reset(self):
self.offset=0 # in mm
self.angle=0 # in degree
self.r56=0 # in mm
self.delay=0 # in ps
self.refI=0
def quadratic(self,x,pol):
res=x*x*pol[0]+x*pol[1]+pol[2]
return res
def invertquadratic(self,y,pol):
a=pol[1]/pol[0]
b=(pol[2]-y)/pol[0]
arg=0.25*a*a-b
if (arg<0):
self.reset()
return [0, 0]
arg=sqrt(arg)
sol=[0.5*a+arg, 0.5*a-arg]
return sol
def getCurrent(self):
ang=self.magcal.BendI2Angle(self.magtype,self.refI,EM=self.refEnergy*1e6)
cur=self.magcal.BendAngle2I(self.magtype,ang,EM=self.Energy*1e6)
return cur
# access function
def setByOffset(self,x):
if x==0 or not self.cal:
self.reset()
return
self.offset=x
self.delay=self.quadratic(x,self.off2del)
self.refI =self.quadratic(x,self.off2cur)
# delay -> angle
self.angle=max(self.invertquadratic(self.delay,self.ang2del))
if (self.angle==0):
self.reset()
return
self.r56 =self.quadratic(self.angle,self.ang2r56)
def setByAngle(self,x):
if x==0 or not self.cal:
self.reset()
return
self.angle=x;
self.delay=self.quadratic(x,self.ang2del)
self.r56 = self.quadratic(x,self.ang2r56)
self.offset=-abs(min(self.invertquadratic(self.delay,self.off2del)))
if self.offset==0:
self.reset()
return
self.refI=self.quadratic(self.offset,self.off2cur)
def setByR56(self,x):
if x==0 or not self.cal:
self.reset()
return
self.r56=x
self.angle=max(self.invertquadratic(self.r56,self.ang2r56))
if self.angle==0:
self.reset()
return
self.delay=self.quadratic(self.angle,self.ang2del)
self.offset=-abs(min(self.invertquadratic(self.delay,self.off2del)))
if self.offset==0:
self.reset()
return
self.refI=self.quadratic(self.offset,self.off2cur)
def setByDelay(self,x):
if x==0 or not self.cal:
self.reset()
return
self.delay=x
self.angle=max(self.invertquadratic(self.delay,self.ang2del))
if (self.angle==0):
self.reset()
return
self.r56 = self.quadratic(self.angle,self.ang2r56)
self.offset=-abs(min(self.invertquadratic(self.delay,self.off2del)))
if self.offset==0:
self.reset()
return
self.refI=self.quadratic(self.offset,self.off2cur)
def setByCur(self,y):
ang=self.magcal.BendI2Angle(self.magtype,y,EM=self.Energy*1e6)
x=self.magcal.BendAngle2I(self.magtype,ang,EM=self.refEnergy*1e6)
if x==0 or not self.cal:
self.reset()
return
self.refI=x
self.offset=-abs(min(self.invertquadratic(x,self.off2cur)))
if self.offset==0:
self.reset()
return
self.delay=self.quadratic(self.offset,self.off2del)
self.angle=max(self.invertquadratic(self.delay,self.ang2del))
if (self.angle==0):
self.reset()
return
self.r56 =self.quadratic(self.angle,self.ang2r56)
def setByEnergy(self,x):
self.Energy=x
function CalibrateBC
% measurement BC1
Offset = -[0:1:9]*50; % encode reading in mm
Phi=[29.91 27.06 18.93 5.36 -13.64 -37.89 -67.71 -102.99 -143.42 (170.41-360)];
I1=[0 14.9 29.3 43.7 58.1 72.4 86.8 101.2 115.5 129.9];
E1=335.1;
I2=[0 13.4 26.4 39.4 52.4 65.4 78.4 91.3 104.2 117.1];
E2=300.3;
freq=5712e6; % C-band frequency
% measurement BC2
Offset=-[0 50 100 150 200 250 300 350];
I1=[0 15.2 30.0 44.9 59.7 74.5 89.3 104.3];
Phi=[246.6 244.8 237.6 227.1 210.4 190.1 165.6 136.3];
E1=2296;
freq=5712e6; % C-band frequency
% measurement LH - artifially do a line
Offset=-[0 10 20];
Phi = [4.07 2.035 0];
I1 = [ 0 13.4 26.8];
E1 = 137.04;
freq= 2998.8e6;
%note that in the routine the chicane specification has to be selected
%according to the chicane!!!!!!
dPhi=-(Phi-Phi(1));
% Step one: Delay
T0=1/freq;
dT=dPhi*T0/360*1e12; %delay in ps
fob=fit(Offset',dT','poly2');
coeff=coeffvalues(fob);
fprintf('Polynomial fit: a2: %e a1: %e a0: %e\n',coeff(1), coeff(2), coeff(3))
figure(1)
plot(Offset,dT,'o')
hold on
plot(fob,'r')
hold off
xlabel('Encoder Position (mm)')
ylabel('Downstream RF Delay (ps)')
%step two: Calculate angle
angle=Phi*0;
for i = 1:3
fun= @(x) PL(x,dT(i));
angle(i)=fzero(fun, [0 0.1]);
end
angle=180/pi*angle;
fob=fit(angle',dT','poly2');
coeff=coeffvalues(fob);
fprintf('Polynomial fit: a2: %e a1: %e a0: %e\n',coeff(1), coeff(2), coeff(3))
figure(2)
plot(angle,dT,'o')
hold on
plot(fob,'r')
hold off
xlabel('angle (degree)')
ylabel('Downstream RF Delay (ps)')
% step 3 - calculate R56 from angle
rr=R56(angle);
rr(1)=0;
fob=fit(angle',rr','poly2');
coeff=coeffvalues(fob);
fprintf('Polynomial fit: a2: %e a1: %e a0: %e\n',coeff(1), coeff(2), coeff(3))
figure(3)
plot(angle,rr,'o')
hold on
plot(fob,'r')
hold off
xlabel('angle (degree)')
ylabel('R_{56} (mm)')
% step 4 - callibrate current with offset
fob=fit(Offset',I1','poly2');
coeff=coeffvalues(fob);
fprintf('Polynomial fit: a2: %e a1: %e a0: %e\n',coeff(1), coeff(2), coeff(3))
figure(4)
plot(Offset,I1,'o')
hold on
plot(fob,'r')
hold off
xlabel('Encoder Position (mm)')
ylabel('Dipole Current at Reference Energy (A)')
% % step 5 - cross check delay and R56
%
% figure(5)
% plot(angle,1e-12*3e8*dT*2*1e3-rr,'o')
%
% xlabel('angle (degree)')
% ylabel('2c{\delta}T - R_{56} (mm)')
%
% % step 6 - cross check current
% ddI=I1/E1-I2/E2
% figure(6)
% plot(ddI)
end
function [val] =R56(angle)
% BC 1
Lb=0.25;
Ld=6;