Files
tomcat_bec/tomcat_bec/scripts/demoscans.py
gac-x05la 4ecb6d0f1d Flaked
2024-10-25 14:29:44 +02:00

252 lines
7.4 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()