record chip type, motion mode and laser delay in the JFJ header
- header_appendix: chip_type, motion_mode, motion_param, fel_per_ms, delay_pulses, laser_delay_ns and total_delay_ns, taken from the job that actually runs (Jungfrau.config), not from the GUI - laser delay read from SLAAR03-LTIM-PDLY:DELAYNS_SLOW (ns, negative: laser before the X-rays); total = laser - delay_pulses * fel_per; trigger_delay now holds the total delay - motion_delay_pulses(): trigger to X-ray pulses per mode, same rules as the CTA sequences (mode 6/7 nominal) - remove the unused manual laser_delay from the run parameters - fix laser_pulse_energy, which was written with the trigger delay Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a2873056c3
commit
4be0f03437
3 files changed
+79
-5
No files matched your search
+6
-2
@@ -177,7 +177,6 @@ class AppCfg(QSettings):
|
||||
'padding':10,
|
||||
'cell_name' : 'na',
|
||||
'triggered': False,
|
||||
'laser_delay': '',
|
||||
'laser_pulse_energy': '',
|
||||
'num_of_on_to_1_off': '1',
|
||||
'block_size':1000,
|
||||
@@ -239,6 +238,12 @@ class AppCfg(QSettings):
|
||||
_log.warning(f'post sample tube: absolute out positions created from the out deltas: {pst}')
|
||||
self.setValue(AppCfg.DFT_POS_PST, pst)
|
||||
|
||||
# laser_delay was a manual entry (never used): the delay is now read from the laser delay PV
|
||||
run=self.value(AppCfg.DAQ_RUN)
|
||||
if run and 'laser_delay' in run:
|
||||
del run['laser_delay']
|
||||
self.setValue(AppCfg.DAQ_RUN, run)
|
||||
|
||||
#if AppCfg.ACTIVATE_PULSE_PICKER not in keys:
|
||||
# self.setValue(AppCfg.ACTIVATE_PULSE_PICKER, False)
|
||||
#if AppCfg.SKIP_ESCAPE_TRANSITIONS_IF_SAFE not in keys:
|
||||
@@ -522,7 +527,6 @@ verbose bits:
|
||||
{'name':'prefix', 'value':daq_run['prefix'], 'type':'str'},
|
||||
{'name':'padding', 'value':daq_run['padding'], 'type':'int'}, #at some point move to old_daq_config
|
||||
{'name':'cell_name', 'value':daq_run['cell_name'], 'type':'str'}, #at some point move to old_daq_config
|
||||
{'name':'laser_delay', 'value':daq_run['laser_delay'], 'type':'str'},
|
||||
{'name':'laser_pulse_energy', 'value':daq_run['laser_pulse_energy'], 'type':'str'},
|
||||
{'name':'triggered', 'value':daq_run['triggered'], 'type':'bool'},
|
||||
{'name':'num_of_on_to_1_off', 'value':daq_run['num_of_on_to_1_off'], 'type':'int', 'limits':(1, 6)},
|
||||
|
||||
+71
-3
@@ -6,6 +6,44 @@ _log=logging.getLogger(__name__)
|
||||
class DataCollectionAbort(Exception):
|
||||
"""Raised when acquisition configuration is invalid and the current scan should abort."""
|
||||
|
||||
LASER_DELAY_PV='SLAAR03-LTIM-PDLY:DELAYNS_SLOW' # fine laser delay in ns, negative: laser before the X-rays
|
||||
MOTION_KEYS=('cnt', 'scale', 'dwell', 'tmove', 'twait', 'ssz', 'smv', 'sdelay', 'use_trf') # recorded motion parameters
|
||||
|
||||
def motion_delay_pulses(mode, param):
|
||||
'''
|
||||
FEL pulses from the trigger (laser/droplet) to the X-ray pulse on the same aperture, as defined by the
|
||||
CTA sequences in Jungfrau.config:
|
||||
mode 1,3,4: 0 (same pulse)
|
||||
mode 5: twait//tmove
|
||||
mode 6: ssz[0]*ssz[1] (nominal: probe pass in the same aperture order as the pump pass)
|
||||
mode 7: ssz[0]*ssz[1]*(twait//tmove+1)+twait//tmove (nominal, as mode 6)
|
||||
raises KeyError/ValueError/TypeError if a needed parameter is missing or invalid
|
||||
'''
|
||||
mode=int(mode)
|
||||
if mode in (5, 7):
|
||||
tmove=param['tmove']
|
||||
wait=int(param['twait']//tmove) if tmove else 0
|
||||
if mode==5:
|
||||
return wait
|
||||
sx, sy=param['ssz']
|
||||
return int(sx)*int(sy)*(wait+1)+wait
|
||||
if mode==6:
|
||||
sx, sy=param['ssz']
|
||||
return int(sx)*int(sy)
|
||||
return 0
|
||||
|
||||
def total_delay_ns(pulses, fel_per_ms, laser_delay_ns):
|
||||
'''laser to X-ray delay in ns (negative: laser before the X-rays)'''
|
||||
return laser_delay_ns-pulses*fel_per_ms*1e6
|
||||
|
||||
def _plain(v):
|
||||
'numpy values/arrays and tuples to plain python (json header)'
|
||||
if hasattr(v, 'tolist'):
|
||||
return v.tolist()
|
||||
if isinstance(v, (tuple, list)):
|
||||
return [_plain(x) for x in v]
|
||||
return v
|
||||
|
||||
logging.getLogger('matplotlib').setLevel(logging.INFO)
|
||||
|
||||
if socket.gethostname()=='ganymede':
|
||||
@@ -220,6 +258,16 @@ class Jungfrau:
|
||||
#print(kwargs)
|
||||
motion_mode = kwargs['mode']
|
||||
self.motion_mode = motion_mode
|
||||
# recorded in the JFJ header (acquire): what actually runs, not what the main page shows
|
||||
motion_param={k:_plain(kwargs[k]) for k in MOTION_KEYS if k in kwargs}
|
||||
try:
|
||||
delay_pulses=motion_delay_pulses(motion_mode, kwargs)
|
||||
except (KeyError, ValueError, TypeError) as e:
|
||||
_log.warning(f'delay pulses not defined for mode {motion_mode}: {e!r}')
|
||||
delay_pulses=None
|
||||
self.scan_meta={'chip_type':kwargs.get('chip_type', ''), 'motion_mode':int(motion_mode),
|
||||
'motion_param':motion_param, 'delay_pulses':delay_pulses}
|
||||
_log.info(f'scan meta: {self.scan_meta}')
|
||||
|
||||
if loc['jungfraujoch']:
|
||||
try:
|
||||
@@ -563,8 +611,21 @@ class Jungfrau:
|
||||
geometry_file_location = loc['geometry_file_location']
|
||||
trigger_event = loc['event_trigger']
|
||||
trigger_flag = run['triggered']
|
||||
trigger_delay = run['laser_delay']
|
||||
laser_energy = run['laser_pulse_energy']
|
||||
dt_misc = cfg.value(AppCfg.DT_MISC)
|
||||
fel_per = dt_misc['fel_per']
|
||||
try:
|
||||
laser_delay_ns = float(epics.caget(LASER_DELAY_PV, timeout=1.0))
|
||||
except (TypeError, ValueError):
|
||||
_log.warning(f'laser delay: no value from {LASER_DELAY_PV}')
|
||||
laser_delay_ns = None
|
||||
scan_meta = getattr(self, 'scan_meta', {})
|
||||
delay_pulses = scan_meta.get('delay_pulses')
|
||||
if delay_pulses is None or laser_delay_ns is None:
|
||||
trigger_delay = None
|
||||
else:
|
||||
trigger_delay = total_delay_ns(delay_pulses, fel_per, laser_delay_ns)
|
||||
_log.info(f'delay: {delay_pulses} pulses x {fel_per} ms + laser {laser_delay_ns} ns = {trigger_delay} ns')
|
||||
|
||||
transmission_ATT53 = epics.caget("SARFE10-OATT053:UsrRec.TD")
|
||||
transmission_ATT150 = epics.caget("SAROP31-OATA150:UsrRec.TD")
|
||||
@@ -590,9 +651,16 @@ class Jungfrau:
|
||||
|
||||
header_appendix['geometry_file_location'] = geometry_file_location
|
||||
header_appendix['trigger_flag'] = trigger_flag
|
||||
header_appendix['trigger_delay'] = trigger_delay
|
||||
header_appendix['trigger_delay'] = trigger_delay # total delay in ns (same as total_delay_ns)
|
||||
header_appendix['trigger_event'] = trigger_event
|
||||
header_appendix['laser_pulse_energy'] = trigger_delay
|
||||
header_appendix['laser_pulse_energy'] = laser_energy
|
||||
header_appendix['chip_type'] = scan_meta.get('chip_type', '')
|
||||
header_appendix['motion_mode'] = scan_meta.get('motion_mode')
|
||||
header_appendix['motion_param'] = scan_meta.get('motion_param', {})
|
||||
header_appendix['fel_per_ms'] = fel_per
|
||||
header_appendix['delay_pulses'] = delay_pulses
|
||||
header_appendix['laser_delay_ns'] = laser_delay_ns
|
||||
header_appendix['total_delay_ns'] = trigger_delay
|
||||
header_appendix['file_prefix'] = f'run{run_number:04}-{user_tag}'
|
||||
try:
|
||||
self.jfj.acquire(
|
||||
|
||||
@@ -1593,6 +1593,7 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
|
||||
except AttributeError as e:
|
||||
_log.warning(f'no scan parameters for object->skipped:{go}')
|
||||
continue
|
||||
param['chip_type']=getattr(go, '_chip_type', t.__name__) # recorded in the JFJ header
|
||||
vb=self.vb
|
||||
grp=self._goTracked
|
||||
mft=self._moduleFixTarget
|
||||
@@ -2262,6 +2263,7 @@ object settings:
|
||||
go=UsrGO.Path((fx-sz[0]/2, fy-sz[1]/2), sz, gp.points, fiducial, fidScl,**param)
|
||||
else:
|
||||
_log.error('index not handeled')
|
||||
go._chip_type=mft._cbType.currentText() # recorded in the JFJ header
|
||||
|
||||
grp=self._goTracked
|
||||
grp.addItem(go)
|
||||
|
||||
Reference in new issue
Block a user