Files
tomcat_bec/tomcat_bec/scripts/demoscans.py
2024-10-21 15:57:26 +02:00

202 lines
7.1 KiB
Python

""" Demo scans for Tomcat at the microXAS test bench
"""
def bl_check_beam():
""" Checks beamline status"""
motor_enabled = bool(dev.es1_roty.motor_enable.get())
result = motor_enabled
return result
def demostepscan(scan_start, scan_end, steps, exp_time=0.005, exp_burst=1, settling_time=0, roix=2016, roiy=2016, sync='event'):
""" Demo step scan with GigaFrost
This is a small BEC user-space demo step scan at the microXAS testbench
using the gigafrost in software triggering mode. It tries to be a
standard BEC scan, while still setting up the environment.
Example:
--------
demostepscan(scan_start=-32, scan_end=148, steps=180, exp_time=0.005, exp_burst=5)
"""
if not bl_check_beam():
raise RuntimeError("Beamline is not in ready state")
scan_ntotal = exp_burst * (steps + 1)
# Move to start position
t_modes = {'pso': 0, 'event': 1, 'inp0': 2, 'inp1': 4}
cfg = {'ntotal': scan_ntotal*10, 'trigger': t_modes[sync]}
dev.es1_ddaq.configure(d=cfg)
# Configure gigafrost
cfg = {
"ntotal": scan_ntotal, "nimages": exp_burst,
"exposure": 1000*exp_time, "period": 2000*exp_time,
"pixel_width": roix, "pixel_height": roiy, "trigger_mode": "soft"
}
dev.gfclient.configure(d=cfg)
# Configure PSO trigger (trigger is normally disabled in fly mode)
# dev.es1_psod.configure(d={})
# dev.es1_psod.software_trigger = True
# Explicitly arm trigger (and detector in future)
# dev.es1_psod.prepare()
# dev.es1_roty.move(scan_start).wait()
print("Handing over to 'scans.line_scan'")
wait_time = 2*exp_time * exp_burst
scans.line_scan(
dev.es1_roty, scan_start, scan_end,
steps=steps, exp_time=wait_time, relative=False, settling_time=settling_time)
# Cleanup: disable SW trigger
dev.es1_psod.software_trigger = False
def demosequencescan(scan_start, gate_high, gate_low, repeats=1, repmode="PosNeg",
exp_time=0.005, exp_frames=180, roix=2016, roiy=2016, sync='pso'):
""" Demo sequence scan with GigaFrost
This is a small BEC user-space sequence scan at the microXAS testbench
triggering a customized scripted scan on the controller. The scan uses
a pre-written custom low-level sequence scan, so it is really minimal.
NOTE: It calls the AeroScript template version.
Example:
--------
>>> demosequencescan(33, 180, 180, exp_time=0.005, exp_frames=1800, repeats=10)
"""
if not bl_check_beam():
raise RuntimeError("Beamline is not in ready state")
print("Handing over to 'scans.sequencescan'")
scans.sequencescan(scan_start, gate_high, gate_low, exp_time=exp_time, exp_frames=exp_frames, repeats=repeats, roix=roix, roiy=roiy, repmode=repmode, sync=sync)
def becsequencescan(start, gate_high, gate_low, repeats=1, repmode="PosNeg",
exp_time=0.005, exp_frames=180, roix=2016, roiy=2016, sync='pso'):
""" Demo sequence scan with GigaFrost via BEC
This is a small BEC user-space sequence scan at the microXAS testbench
doing every scan logic from BEC, i.e. there's no AeroScript involved.
Example:
--------
>>> demosequencescan(33, 180, 180, exp_time=0.005, exp_frames=1800, repeats=10)
"""
if not bl_check_beam():
raise RuntimeError("Beamline is not in ready state")
# Parameter validation before scan
if exp_frames <=0:
raise RuntimeError(f"Requires at least one frame per cycle, got {self.exp_frames}")
# Synthetic values
scan_roix = roix
scan_roiy = roiy
scan_repnum = repeats
scan_repmode = repmode.upper()
scan_velocity = gate_high / (exp_time * exp_frames)
scan_acceleration = 500
scan_safedistance = 10
scan_accdistance = scan_safedistance + 0.5* scan_velocity * scan_velocity / scan_acceleration
scan_ntotal = exp_frames * repeats
if scan_repmode in ("POS", "NEG"):
scan_range = repeats *(gate_high + gate_low)
if scan_repmode=="POS":
scan_start = start - scan_accdistance
scan_end = start + scan_range + scan_accdistance
if scan_repmode=="NEG":
scan_start = start + scan_accdistance
scan_end = start - scan_range - scan_accdistance
elif scan_repmode in ("POSNEG", "NEGPOS"):
scan_range = gate_high + gate_low
if scan_repmode=="POSNEG":
scan_start = start - scan_accdistance
scan_end = start + scan_range + scan_accdistance
if scan_repmode=="NEGPOS":
scan_start = start + scan_accdistance
scan_end = start - scan_range - scan_accdistance
else:
raise RuntimeError(f"Unsupported repetition mode: {repmode}")
# Aerotech DDC settings: Internal event trigger: PsoEvent = 1
t_modes = {'pso': 0, 'event': 1, 'inp0': 2, 'inp1': 4}
daq_trigger = t_modes[sync]
# Drice data collection config
daqcfg = {'ntotal': scan_ntotal, 'trigger': daq_trigger}
# Gigafrost config
camcfg = {
"ntotal": scan_ntotal,
"nimages": exp_frames,
"exposure": 1000 * exp_time,
"period": 2000*exp_time,
"pixel_width": scan_roix,
"pixel_height": scan_roiy
}
# PSO config
# Fill PSO vectors according to scan direction
# NOTE: Distance counter is bidirectional
psoBoundsPos = []
psoBoundsNeg = []
if scan_repmode in ("POS", "NEG"):
psoBoundsPos.append(scan_accdistance)
for ii in range(scan_repnum):
psoBoundsPos += [gate_high, gate_low]
if scan_repmode in ("POSNEG", "NEGPOS"):
psoBoundsPos = [scan_accdistance, gate_high]
psoBoundsNeg = [scan_accdistance + gate_low, gate_high]
psocfg = {"distance": psoBoundsPos, "wmode": "toggle"}
# Move to start position
dev.es1_roty.move(scan_start).wait()
# Configure all devices
dev.gfclient.configure(camcfg)
dev.es1_ddaq.configure(daqcfg)
dev.es1_psod.configure(psocfg)
try:
# Stage all devices
dev.gfclient.stage()
dev.es1_ddaq.stage()
dev.es1_psod.stage()
dev.daq_stream1.stage()
# Kick off devices
dev.gfclient.kickoff()
dev.es1_ddaq.kickoff()
dev.es1_psod.kickoff()
print("Manual motor scan")
if scan_repmode in ["POS", "NEG"]:
dev.es1_psod.prepare(psoBoundsPos)
dev.es1_roty.move(scan_end).wait()
elif scan_repmode in ["POSNEG", "NEGPOS"]:
for ii in range(scan_repnum):
if ii%2==0:
dev.es1_psod.prepare(psoBoundsPos)
# FIXME : Temporary trigger
dev.gfclient.trigger()
dev.es1_roty.move(scan_end).wait()
if ii%2==1:
dev.es1_psod.prepare(psoBoundsNeg)
# FIXME : Temporary trigger
dev.gfclient.trigger()
dev.es1_roty.move(scan_start).wait()
finally:
# Stage all devices
dev.gfclient.unstage()
dev.es1_ddaq.unstage()
dev.es1_psod.unstage()
dev.daq_stream1.unstage()
# Move back to start
dev.es1_roty.move(scan_start).wait()