30 Commits
dev ... master

Author SHA1 Message Date
14d37c3e67 Handle missing channels in data 2025-06-10 14:46:18 +02:00
14d0db7fad add SPECTRUM_X to Furka 2025-05-24 17:12:20 +02:00
2267992699 bugfix 2025-04-28 14:48:01 +02:00
26998e15dd change bscache args to new bstrd version 2025-04-27 13:39:42 +02:00
100be302e9 Merge branch 'fix_bscaquisition' into 'master'
Fix bscaquisition

See merge request slic/sfbd!6
2025-04-27 12:52:06 +02:00
1d933a66ce change argument passed to baseclass Acquisition 2025-04-27 12:33:56 +02:00
a5189537bb bugfix 2025-04-27 12:01:58 +02:00
b221a7f964 Merge branch 'fix_bscaquisition' into 'master'
Fix bscaquisition

See merge request slic/sfbd!5
2025-04-27 11:21:36 +02:00
127f3ef9db Merge branch 'master' into fix_bscaquisition 2025-04-11 14:57:30 +02:00
8de87ed1a9 merged and untested 2025-04-11 14:14:51 +02:00
423e724022 minor fix 2025-04-09 17:40:19 +02:00
fa67b2fab9 Adding individual pulse ID output for bscacquisiton 2025-02-27 17:20:33 +01:00
716d1cbf2a rewrite bsacquisition with help of Sven Augustin 2025-02-11 18:33:13 +01:00
2a0c5cf049 Added functionality to read also the seed laser spectrum 2025-02-07 17:29:59 +01:00
f5b6f29efc changed bsacquisition to avoid asigning an array to an element of an array with scalar elements 2025-02-07 15:23:40 +01:00
68cd4d5633 added pvcombination class but is no longer needed. can be deleted 2025-02-06 12:03:21 +01:00
52e126c1af Adding an adjustable instance which combines a set of normal pvadjustables to a new adjustable class PVCombiAdjustable 2025-02-05 17:36:28 +01:00
bca4098438 Corrected indexing when saving scans 2025-02-05 09:21:09 +01:00
964da0a16f add new class for simultaneous acquisition of PSSS and gas detector values 2025-01-24 18:41:31 +01:00
f2c07f0c98 Merge branch 'tmp' into 'master'
status 2024-12-03

See merge request slic/sfbd!4
2024-12-03 17:33:46 +01:00
36b7a2980a added new files 2024-12-03 17:33:04 +01:00
5e06e49c45 status 2024-12-03 2024-12-03 17:27:53 +01:00
01be4a5203 Merge branch 'philipp-dev' into 'master'
remove daq of gas detector pvs

See merge request slic/sfbd!3
2024-12-03 17:24:09 +01:00
e603069ffd remove daq of gas detector pvs 2024-12-03 17:17:03 +01:00
26eebc650b updating snapshot and loading datasets 2024-09-11 17:06:10 +02:00
24cbccb6a6 Added routines to get only the PVs from snapshot file and to get the data by these PVS. This avoids crashed of too many files open when used often 2024-06-27 14:49:05 +02:00
d1fb0b3462 Merge branch 'dev' 2024-06-27 09:27:16 +02:00
a07f1bcf42 Merge branch 'feature/acquire_gas_detector_with_spectra' into 'master'
new spectralanalysis with gas detector readings

See merge request slic/sfbd!2
2024-06-27 09:05:14 +02:00
2906c9cc44 rewrote spectralanalysis. Needs update of SpectralAnalysis repo 2024-06-26 18:34:15 +02:00
6fa15dd9d6 Merge branch 'dev' into 'master'
Update Master with a functional version of sfbd

See merge request slic/sfbd!1
2024-01-10 09:12:36 +01:00
17 changed files with 610 additions and 67 deletions

View File

@ -27,8 +27,8 @@ class AdaptiveOrbit:
idx = '070'
if bpm == 5 or bpm ==14:
idx='410'
self.ATchx.append('SATUN%2.2d-DBPM%s:X2' % (bpm,idx))
self.ATchy.append('SATUN%2.2d-DBPM%s:Y2' % (bpm,idx))
self.ATchx.append('SATUN%2.2d-DBPM%s:X1' % (bpm,idx))
self.ATchy.append('SATUN%2.2d-DBPM%s:Y1' % (bpm,idx))
self.bsAT = self.initBSStream([self.ATch0]+self.ATchx+self.ATchy)
self.pvAT = self.initPV(self.ATchx)
self.kickerAT = self.initPV(['SATMA01-MCRX610:I-SET','SATMA01-MCRY610:I-SET','SATUN05-MCRX420:I-SET','SATUN05-MCRY420:I-SET','SFB_ORBIT_SAT:ONOFF1'])
@ -39,7 +39,7 @@ class AdaptiveOrbit:
def initBSStream(self,channels):
print("Initializing BSstream")
bs = BSCache(100000,100000) # 1000 second time out, capazity for 1000 second.
bs = BSCache(100000,receive_timeout=100000) # 1000 second time out, capazity for 1000 second.
bs.get_vars(channels)
return bs

View File

@ -79,7 +79,7 @@ class Dispersion:
self.scale = scl
# define stream
print('Getting BSCache')
self.bsc = BSCache(100000,10000) # 100 second timeout, size for 100 second data taken
self.bsc = BSCache(100000, receive_timeout=10000) # 1000 second timeout, size for 100 second data taken
self.bsc.get_vars(self.sensor) # this starts the stream into the cache
print('Getting BSCache done')

View File

@ -1,23 +1,34 @@
import time
import numpy as np
from bstrd import BSCache
from epics import PV
class SpectralAnalysis:
"""
Wrapper class to bundle all daq/io needed for adaptive orbit feedback.
"""
def __init__(self):
athos_uncalibrated = 'SATFE10-PEPG046-EVR0:CALCI'
athos_calibrated = 'SATFE10-PEPG046:FCUP-INTENSITY-CAL'
aramis_uncalibrated = 'SARFE10-PBIG050-EVR0:CALCI'
aramis_calibrated = 'SARFE10-PBPG050:HAMP-INTENSITY-CAL'
eehg_spectrometer = 'SSL2-CPCW-SPEC01:SPECTRUM'
channel_dict = {
'PSSS': ['SARFE10-PSSS059:SPECTRUM_Y'],
'PMOS Maloja': ['SATOP21-PMOS127-2D:SPECTRUM_Y', athos_uncalibrated, athos_calibrated],
'PMOS Furka': ['SATOP31-PMOS132-2D:SPECTRUM_Y', 'SATOP31-PMOS132-2D:SPECTRUM_X', athos_uncalibrated, athos_calibrated],
'PSSS LB': ['SARFE10-PSSS059-LB:SPECTRUM_Y'],
'PSSS incl gasd': ['SARFE10-PSSS059:SPECTRUM_Y', aramis_uncalibrated, aramis_calibrated],
'PSSS LB incl gasd': ['SARFE10-PSSS059-LB:SPECTRUM_Y', aramis_uncalibrated, aramis_calibrated],
'PMOS Maloja EEHG': ['SATOP21-PMOS127-2D:SPECTRUM_Y', eehg_spectrometer],
'PMOS Furka EEHG': ['SATOP31-PMOS132-2D:SPECTRUM_Y', eehg_spectrometer],
}
names = ['PSSS', 'PMOS Maloja', 'PMOS Furka', 'PSSS LB', 'PSSS incl gasd', 'PSSS LB incl gasd', 'PMOS Maloja EEHG','PMOS Furka EEHG']
self.bs = BSCache(100000,10000) # 100 second timeout, size for 100 second data taken
class SpectralAnalysis:
def __init__(self):
self.bs = BSCache(100000,receive_timeout=10000) # 100 second timeout, size for 10 second data taken
self.bs.stop()
self.channel = ''
self.channels = ['SARFE10-PSSS059:SPECTRUM_Y',
'SATOP21-PMOS127-2D:SPECTRUM_Y',
'SATOP31-PMOS132-2D:SPECTRUM_Y']
self.isConnected = False
self.channel = None
self.channels = [channel_dict[x] for x in names]
self.hasBStream=False
def connect_name(self, name):
index = names.index(name)
self.connect(index)
def connect(self,ich):
if ich < 0 or ich >= len(self.channels):
@ -25,25 +36,71 @@ class SpectralAnalysis:
self.channel = self.channels[ich]
print('Connecting to BS-Channel:',self.channel)
self.bs.channels.clear()
self.bs.get_var(self.channel) # this starts the stream into the cache
self.pv = PV(self.channel.replace('_Y','_X'))
self.hasBStream=True
try:
self.bs.get_vars(self.channel) # this starts the stream into the cache
except ValueError:
print('Cannot find requested channels in BS stream')
self.hasBStream=False
self.pv = PV(self.channel[0].replace('_Y','_X'))
def terminate(self):
print('Stopping BSStream Thread...')
print('Stopping BSStream Thread...')
self.bs.stop()
self.bs.pt.running.clear() # for some reason I have to
self.pv.disconnect()
def flush(self):
self.bs.flush()
def read(self):
data=self.bs.__next__()
return data['pid'],data[self.channel]
return next(self.bs)
def readPV(self):
def read_spectrum_axis(self):
return self.pv.value
def getSpectrometerName(self):
return self.channel
class SpectralAnalysis2:
def __init__(self):
self.bs1 = BSCache(100000, receive_timeout=10000)
self.bs2 = BSCache(100000, receive_timeout=10000)
self.bs1.stop()
self.bs2.stop()
self.hasBStream=False
def connect_name(self, name):
channels = channel_dict[name]
self.bs1.channels.clear()
self.hasBStream = True
try:
self.bs1.get_vars(channels[:1]) # this starts the stream into the cache
except ValueError:
print('Cannot find requested channel %s in BS stream' % channels[0])
self.hasBStream=False
if len(channels) > 1:
try:
self.bs2.get_vars(channels[1:]) # this starts the stream into the cache
except ValueError:
print('Cannot find requested channel %s in BS stream' % channels[1:])
self.hasBStream=False
self.pv = PV(channels[0].replace('_Y','_X'))
def read_spectrum_axis(self):
return self.pv.value
def flush(self):
for _bs in self.bs1, self.bs2:
_bs.flush()
def read(self):
return next(self.bs1), next(self.bs2)
def terminate(self):
print('Stopping BSStream Thread...')
for _bs in self.bs1, self.bs2:
_bs.stop()
_bs.pt.running.clear() # for some reason I have to
self.pv.disconnect()

View File

@ -18,7 +18,7 @@ class XTCAVStabilizer:
self.PVPhase = PV('SATMA02-RSYS:SET-BEAM-PHASE')
# the BS channels
self.bs = BSCache(100000,10000) # 100 second timeout, size for 100 second data taken
self.bs = BSCache(100000, receive_timeout=10000) # 100 second timeout, size for 10 second data taken
self.channels = ['SATBD02-DBPM040:X2','SATMA02-RLLE-DSP:PHASE-VS','SATBD02-DBPM040:X2-VALID']
self.validation = self.channels[2]
self.bs.get_vars(self.channels) # this starts the stream into the cache

View File

@ -2,3 +2,4 @@ from .magnet import Magnet
from .camacquisition import CamAcquisition
from .counteradjustable import CounterAdjustable
from .bscacquisition import BSCAcquisition
from .pvcombiadjustable import PVCombiAdjustable

View File

@ -1,6 +1,5 @@
import h5py
import numpy as np
#import numpy as np
from slic.core.acquisition.acquisition import Acquisition
@ -8,33 +7,38 @@ from slic.core.acquisition.acquisition import Acquisition
# class using the BSQueue to avoid to reestablish a stream for each step.
class BSCAcquisition(Acquisition):
def __init__(self, bscache, *args, use_channels=None, **kwargs):
self.bscache = bscache
self.use_channels = use_channels
self.grp = 0
super().__init__(*args, default_channels= self.use_channels or self.bscache.channels.keys(), **kwargs)
def setGroup(self,idx):
self.grp = idx
def _acquire(self, filename, channels=None, data_base_dir=None, scan_info=None, n_pulses=100, **kwargs):
queue =channels[0] # abusing interface since BSAcquisition assume a list of channels
# allocating space
data={}
chns = queue.channels
for chn in chns:
data[chn]={'data':np.zeros((n_pulses))}
data['pulse_id']=np.zeros((n_pulses))
# clear the queue
queue.flush()
self.bscache.flush()
data = []
for i in range(n_pulses):
msg = queue.__next__() # pull data from cache
for chn in chns:
data[chn]['data'][i] = msg[chn]
data['pulse_id'][i]=msg['pid']
data.append(next(self.bscache))
# write out the data file
hid = h5py.File(filename,'w')
hid.create_dataset('pulse_id', data = data['pulse_id'])
for chn in chns:
gid = hid.create_group(chn)
for key in data[chn].keys():
gid.create_dataset(key, data = data[chn][key])
hid.close()
use_channels = self.use_channels or self.bscache.channels.keys()
with h5py.File(filename,'a') as hid:
# save the pulse ID
singledata = [ele['pid'] for ele in data]
pidname = 'pulse_id/group%d' % self.grp
hid.create_dataset(pidname, data=singledata)
for chn in use_channels:
singledata = []
for ele in data:
if chn in ele:
singledata.append(ele[chn])
else:
print('%s not in data!' % chn)
if not chn == 'pid':
dname = chn.replace(':','/')+'/data'
hid.create_dataset(dname, data=singledata)
dpid = dname.replace('/data','/pid')
hid[dpid] = hid[pidname]

42
ext/pvcombiadjustable.py Normal file
View File

@ -0,0 +1,42 @@
from slic.core.adjustable import Adjustable,DummyAdjustable,PVAdjustable
import numpy as np
class PVCombiAdjustable(Adjustable):
def __init__(self, adjustables,values):
self.adjustables = adjustables
self.values = values # array or indiviual array values
def set_target_value(self, value):
print('##### Setvalue:',value)
# check if values are in range
for i,adj in enumerate(self.adjustables):
idx = int(value[i])
print('### Index',i, idx)
val=self.values[i][idx]
print('### Setting',adj.name,'to',val)
adj.set_target_value(val)
@property
def units(self):
return ['m' for adj in self.adjustables]
# return [adj.units() for adj in self.adjustables if adj is not None]
def get_current_value(self, readback=True):
return [adj.get_current_value() for adj in self.adjustables] # this is different for dummy elements
def stop(self):
for adj in self.adjustbale:
adj.stop()
def is_moving(self):
return any([adj.is_moving() for adj in self.adjustables])

View File

@ -1,4 +1,6 @@
from .snap import getSnap
from .snap import getSnapPV
from .snap import getSnapVal
from .snap import saveSnap
from .snap import loadSnap
from .snap import parseSnapShotReqYAML
@ -7,5 +9,7 @@ from .save import getDatasetFileName
from .load import loadDataset
from .elog import writeElog
from .slic import SlicScan
from .slic import importSlicScan
from .doocs import doocsread
from .doocs import doocswrite

33
interface/doocs.py Normal file
View File

@ -0,0 +1,33 @@
import subprocess
def doocsread(mystr):
""" mystr input can be a string or a list """
if isinstance(mystr, list): # it's a list
readCmd = ' '.join(mystr)
else:
readCmd = mystr
cmd = ['doocsget', '-c', readCmd]
MyOut = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, stderr = MyOut.communicate()
if b'result->error()' in stdout.split():
print('ERROR: reading error')
print(mystr)
return
result = [element.decode('utf-8') for element in stdout.split()]
if len(result) == 1: # one element only
try:
return float(result[0])
except Exception as e:
return result[0]
else:
try:
return [float(x) for x in result]
except Exception as e:
return result
def doocswrite(mystr, target):
cmd = ['doocsput', '-c', mystr, '-d', str(target)]
MyOut = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, stderr = MyOut.communicate()

View File

@ -3,11 +3,26 @@ import h5py
def loadDataset(filename):
hid = h5py.File(filename, "r")
icount = 0
snap = loadSnap(hid)
data = loadData(hid)
act = loadActuator(hid)
data={}
act= {}
maxID = 0
for key in hid.keys():
if 'scan' in key:
ID = int(key.split('_')[1])
if ID > maxID:
maxID = ID
print('Reading scan_%d' % ID)
data[ID]=loadData(hid,ID)
act[ID]=loadActuator(hid,ID)
icount +=1
hid.close()
return data,act,snap
if icount == 0:
return None, None, None
elif icount == 1:
return data[0], act[0],snap
return [data[j] for j in range(maxID+1)],[act[j] for j in range(maxID+1)],snap
def loadSnap(hid):
snap={}

153
interface/magnet.py Normal file
View File

@ -0,0 +1,153 @@
from time import sleep
from epics import PV,caget_many, caput_many
import numpy as np
from threading import Thread
from time import sleep
#from onlinemodel.interface import SwissFELMagnet
class Magnet:
def __init__(self, beamline = 'ARAMIS',debug=False, signal = None, cal = None):
self.beamline = beamline
self.debug=debug
self.signal = signal
self.progress = None
# self.cal = SwissFELMagnet()
# getting the array
temp = np.sort(np.array(list(range(0, 11)) + [0.5, 1.5]))[::-1]
temp2 = -1. * temp
cyclingCurrents = np.ones(len(temp) * 2) * 0.
cyclingCurrents[::2] = temp
cyclingCurrents[1::2] = temp2
self.currents = cyclingCurrents[:-1]
self.cyclingPause = 3
self.pvcor = []
self.abort = False
def doAbort(self):
self.abort = True
def setMagnetI(self,maglist,vals):
if self.debug:
for i,ele in enumerate(maglist):
print('DEBUG: Setting %s to: %f A' % (ele,vals[i]))
return
caput_many(maglist,vals)
def setMagnets(self,maglist,magbd,erg):
for mag in maglist.keys():
Itar = self.cal.QuadrupoleKL2I(magbd[mag],maglist[mag],erg*1e6)
pv = PV('%s:I-SET' % mag)
if not Itar is None:
if self.debug:
self.signal.emit('DEBUG: Setting %s to %f' % (pv.pvname,Itar))
else:
pv.value = Itar
def scaleMagnets(self,maglist,magbg,EProfCur,EProfTar):
# get current from magnets
for i,mag in enumerate(maglist):
if 'SAR' in mag:
ecur = EProfCur[-1]
etar = EProfTar[-1]
elif 'S30' in mag:
idx = int(mag[5:7])
if idx >= len(EProfCur):
ecur = EProfCur[-1]
etar = EProfTar[-1]
else:
ecur = EProfCur[idx]
etar = EProfTar[idx]
if 'MQUA' in mag:
pv = PV('%s:I-SET' % mag)
valmag = pv.value
strength = self.cal.QuadrupoleI2KL(magbg[i],valmag,ecur*1e6)
Itar = self.cal.QuadrupoleKL2I(magbg[i],strength,etar*1e6)
elif 'MSEX' in mag:
pv = PV('%s:I-SET' % mag)
valmag = pv.value
strength = self.cal.SextupoleI2KL(magbg[i],valmag,ecur*1e6)
Itar = self.cal.SextupoleKL2I(magbg[i],strength,etar*1e6)
elif 'MBND' in mag:
pv = PV('%s:P-SET' % mag)
valmag=pv.value
strength = pv.value
Itar = valmag-EProfCur[-1]+etar
else:
strength = 0
if not Itar is None:
if self.debug:
self.signal.emit('DEBUG: Setting %s to %f' % (pv.pvname,Itar))
else:
pv.value = Itar
def cycleCorrector(self,QuadList=[],progress=None,done = None):
self.progress = progress
self.done = done # signal to indicate end of thread
self.pvcor = [PV(ele.replace('MQUA','MCRX')) for ele in QuadList]+[PV(ele.replace('MQUA','MCRY')) for ele in QuadList]
self.abort = False
Thread(name='CycleCor',target=self.TcycleCorrector).start() # starting thread which starts and monitors actual thread
def TcycleCorrector(self):
self.running = True
istep = 0
nstep = len(self.currents)
while self.running:
if self.progress:
self.progress.emit(istep,nstep)
if self.signal:
self.signal.emit('Setting corrector current to %5.1f A' % self.currents[istep])
if self.debug:
print('DEBUG: Setting Corrector Magnets to:',self.currents[istep])
else:
for pv in self.pvcor:
pv.value = cyclingCurrents[istep]
sleep(self.cyclingPause)
istep+=1
if istep == nstep or self.abort:
self.running = False
if self.progress:
self.progress.emit(nstep,nstep)
if self.done:
self.done.emit(not self.abort)
def cycleMagnets(self,maglist, progress = None, done = None):
if len(maglist) == 0:
done.emit(True)
return -1
self.progress = progress
self.done = done # signal to indicate end of thread
state = caget_many(['%s:CYCLE-STATE' % mag for mag in maglist])
magcyc = [mag for i,mag in enumerate(maglist) if not state[i] == 2]
time = caget_many(['%s:CYCLE-PROGFULL' % mag for mag in magcyc])
if len(time) == 0:
done.emit(True)
return -1 # no cycling needed
imax = np.argmax(time)
self.tcyc = time[imax]
print('Cycling Time:',self.tcyc,'sec for magnet',magcyc[imax])
if self.debug:
print('DEBUG: Cycle-SET magnets')
else:
caput_many(['%s:CYCLE' % mag for mag in magcyc],[2]*len(magcyc))
self.abort = False
Thread(name='CycleMag',target=self.TcycleMagnets).start()
return self.tcyc
def TcycleMagnets(self):
self.running = True
tcur = 0
while self.running:
sleep(1)
tcur += 1
if self.progress:
self.progress.emit(tcur,self.tcyc)
if tcur >= self.tcyc or self.abort:
self.running = False
if self.progress:
self.progress.emit(self.tcyc,self.tcyc)
if self.done:
self.done.emit(not self.abort)

112
interface/reprate.py Normal file
View File

@ -0,0 +1,112 @@
from time import sleep
from epics import PV
import numpy as np
class RepRate:
def __init__(self, beamline = 'ARAMIS',debug=False):
self.beamline = beamline
self.debug=debug
self.pv={}
self.pv['BeamDelayAR'] = PV('SIN-TIMAST-TMA:Bunch-1-OnDelay-Sel')
self.pv['BeamDelayAT'] = PV('SIN-TIMAST-TMA:Bunch-2-OnDelay-Sel')
self.pv['RFDelay'] = PV('SIN-TIMAST-TMA:Beam-RF-OnDelay-Sel')
self.pv['StopperAR'] = PV('SARMA02-MBNP100:REQUEST')
self.pv['StopperAT'] = PV('SATMA01-MBNP100:REQUEST')
self.pv['StopperAROut'] = PV('SARMA02-MBNP100:PLC_MOV_OUT')
self.pv['StopperATOut'] = PV('SATMA01-MBNP100:PLC_NOV_OUT')
self.pv['BeamRRAR'] = PV('SIN-TIMAST-TMA:Bunch-1-Freq-Sel')
self.pv['BeamRRAT'] = PV('SIN-TIMAST-TMA:Bunch-2-Freq-Sel')
self.pv['applyTiming'] = PV('SIN-TIMAST-TMA:Beam-Apply-Cmd.PROC')
self.rrReal = np.array([100., 50., 33.3, 25., 16.6, 12.5, 10., 8.3, 5., 2.5, 1.])
self.rrId = np.arange(0, 11)
def setStopper(self,doinsert):
if self.beamline == 'ARAMIS':
pv = self.pv['StopperAR']
else:
pv = self.pv['StopperAT']
if self.debug:
print('DEBUG: Setting beam stopper to:',doinsert)
return
if doinsert:
pv.value = 1
else:
pv.value = 0
def isStopperOpen(self):
if self.beamline == 'ARAMIS':
return self.pv['StopperAROut']
else:
return self.pv['StopperATOut']
def stopSF(self):
if self.debug:
print('DEBUG: Stopping Machine disabled')
return
if self.beamline == 'ARAMIS':
self.pv['BeamDelayAR'].value = 1
self.pv['BeamDelayAT'].value = 1
sleep(0.1)
self.pv['RFDelay'].value =1
sleep(0.1)
self.pv['applyTiming'].value = 1
def getRR(self):
if self.beamline == 'ARAMIS':
return self.rrReal[self.pv['BeamRRAR'].value]
else:
return self.rrReal[self.pv['BeamRRAT'].value]
def setRR(self,rr=1.):
"""
Possible RR as of 14/01/2020
0 100.00 Hz
1 50.00 Hz
2 33.33 Hz
3 25.00 Hz
4 16.66 Hz
5 12.50 Hz
6 10.00 Hz
7 8.33 Hz
8 5.00 Hz
9 2.50 Hz
10 1.00 Hz
11 Bunch Off
"""
findRR = abs(self.rrReal - rr) < 0.1
if findRR.sum(): # at least one rr is matching
rrSel = self.rrId[np.argmax(findRR)]
else:
print('Requested rep. rate %.3f is not available' % rr)
return
if self.debug:
print('DEBUG: Setting beam rate to %d (%f Hz)' % (rrSel,rr))
return
if self.beamline == 'ARAMIS':
self.pv['BeamRRAR'].value = rrSel
sleep(0.1)
self.pv['BeamDelayAR'].value = 0 # 0 on beam, 1 on delay
rrAT = self.pv['BeamRRAT'].value
if rrAT < rrSel: # reduce athos at least to same rep rate as Aramis
self.pv['BeamRRAT'].value = rrSel
sleep(0.1)
self.pv['BeamDelayAT'].value = 0 # 0 on beam, 1 on delay
else:
self.pv['BeamRRAT'].value = rrSel
sleep(0.1)
self.pv['BeamDelayAT'].value = 0 # 0 on beam, 1 on delay
sleep(0.1)
if rrSel == 11:
self.pv['RFDelay'].value =1
else:
self.pv['RFDelay'].value = 0
sleep(0.1)
self.pv['applyTiming'].value = 1

60
interface/rf.py Normal file
View File

@ -0,0 +1,60 @@
from epics import PV,caput_many
import numpy as np
class RF:
def __init__(self, beamline = 'ARAMIS',debug=False, signal = None):
self.beamline = beamline
self.debug=debug
self.signal = signal
if self.beamline == 'ARAMIS':
self.stations = ['S30CB%2.2d' % d for d in range(1,14)]
self.RFamp = [PV('%s-RSYS:SET-ACC-VOLT' % station) for station in self.stations]
self.RFphs = [PV('%s-RSYS:SET-BEAM-PHASE' % station) for station in self.stations]
self.RFon = [PV('%s-RSYS:REQUIRED-OP' % station) for station in self.stations]
self.ErgRange = [PV('SATSY01-MBND200:P-SET'),PV('SARCL02-MBND100:P-SET')]
self.EGain = PV('S30:SET-E-GAIN-OP')
self.EPhase= PV('S30:SET-BEAM-PHASE-OP')
self.Ns = 13
else:
self.EGain = ['S20:SET-E-GAIN-OP']
self.Ns = 0
def energyProfile(self):
if self.Ns == 0:
return np.array([0,0])
valAmp = [pv.value for pv in self.RFamp]
valPhs = [pv.value for pv in self.RFphs]
valOn = [pv.value for pv in self.RFon]
valErg = [pv.value for pv in self.ErgRange]
prof = np.zeros((self.Ns+1))
prof[0] = valErg[0]
for i in range(self.Ns):
dE = 0
if valOn[i] == 1:
dE = valAmp[i]*np.sin(np.pi*valPhs[i]/180.)
prof[i+1] = prof[i]+dE
prof = np.array(prof)
Egain = prof - prof[0]
scl = (valErg[1]-valErg[0])/np.max(Egain)
prof = Egain*scl+valErg[0]
return prof
def changeEnergyGain(self,dE):
if self.debug:
print('DEBUG: Changing energy gain of linac by:',dE)
return
self.EGain.value = dE
if dE > 0:
self.EPhase.value = 90.
else:
self.EPhase.value = -90.
def setRF(self,chns,vals):
if self.debug:
for i,ele in enumerate(chns):
print('DEBUG: RF-Settings %s: %f' % (ele,vals[i]))
return
caput_many(chns,vals)

View File

@ -19,7 +19,7 @@ def getDatasetFileName(program='Unknown'):
path = '%s/%s' % (path,day)
if not os.path.exists(path):
os.makedirs(path)
datetag = datetime.datetime.now().strftime('%Y_%m_%d_%H_%M_%S_%f')
datetag = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
filename=('%s/%s_%s' % (path, program.replace(' ','_'), datetag))
return filename
@ -31,19 +31,19 @@ def saveDataset(program,data,actuator=None,snap=None,analysis=None,figures=None)
# check if scan is multiple instances of a scan
if isinstance(data,list):
for iscan,singledata in enumerate(data):
writeData(hid,singledata,iscan)
writeData(hid,singledata,iscan+1)
else:
writeData(hid,data,1)
# same for actuator
if isinstance(actuator,list):
for iscan,singleactuator in enumerate(actuator):
writeActuator(hid,singleactuator,iscan)
writeActuator(hid,singleactuator,iscan+1)
else:
writeActuator(hid,actuator,1)
# and same for analysis
if isinstance(analysis,list):
for iscan,singleana in enumerate(analysis):
writeAnalysis(hid,singleana,iscan)
writeAnalysis(hid,singleana,iscan+1)
else:
writeAnalysis(hid,analysis,1)
# write aux data
@ -94,9 +94,13 @@ def writeData(hid, data, scanrun=1):
dset.attrs['system'] = getDatasetSystem(name[0])
dset.attrs['units'] = 'unknown'
# this part is obsolete - dimension should be given from the individual datasets
if not 'pid' in data.keys():
shape = None
if 'pid' in data.keys():
shape = data['pid'].shape
if 'pulse_id' in data.keys():
shape = data['pulse_id'].shape
if shape is None:
return
shape = data['pid'].shape
ndim = len(shape)
nsam = shape[-1]
nrec = 0

View File

@ -62,7 +62,7 @@ class SlicScan(QObject):
Thread(name='Snap-Acquisition',target=self.Tsnap).start()
def Tsnap(self):
self.snap = getSnap()
self.snap = getSnap()
print('Acquired snap')
self.sigsnap.emit(True)

View File

@ -61,6 +61,26 @@ def getSnap(pvs=None):
ret[pv]=float(val[i])
return ret
def getSnapPV(pvs=None):
if not isinstance(pvs,list):
pvs = parseSnapShotReqYAML(pvs)
if not pvs:
return
return [epics.PV(pvname,auto_monitor = False) for pvname in pvs]
def getSnapVal(pvs=None):
if not pvs:
return None
ret={}
val = [pv.get(timeout=0.1) for pv in pvs]
for i,pv in enumerate(pvs):
if not val[i] is None: # filter out None values
ret[pv.pvname]=float(val[i])
else:
print('Timeout:',pv.pvname)
return ret
def saveSnap(pvs={},label="", comment = "generated by application",reqfile = "SF_settings.yaml"):
filename = datetime.datetime.now().strftime('/sf/data/applications/snapshot/SF_settings_%Y%m%d_%H%M%S.snap')
with open(filename,'w') as fid:
@ -85,7 +105,7 @@ def loadSnap(filename):
if '.' in val:
res[pv]=float(val)
else:
res[pv]= val
res[pv]= val # might be string!!!!
return res

38
util/simplecapture.py Normal file
View File

@ -0,0 +1,38 @@
import numpy as np
from bstrd import BSCache
class SimpleCapture:
def __init__(self):
self.chn=None
self.bs = BSCache(100000, receive_timeout=100000) # 1000 second time out, capazity for 1000 second.
self.abort=False
def terminate(self):
self.bs.stop()
self.bs.pt.running.clear()
def stop(self):
self.abort=True
def initChannels(self,channels):
self.chn=channels
self.bs.get_vars(channels)
def acquire(self,N=1,reprate=-1,stop=False):
self.abort=False
data = np.zeros((N,len(self.chn)))
pid = np.zeros((N))
self.bs.flush()
idx = 0
while idx<N and not self.abort:
rec=self.bs.__next__()
pid[idx] = rec['pid']
data[idx,:] = np.array([rec[ch] for ch in self.chn])
idx += 1
ret={}
for i,ch in enumerate(self.chn):
ret[ch]=data[:,i]
ret['pid'] = pid
return ret