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