External enable works, trigger input seems dead

This commit is contained in:
gac-x05la
2024-11-12 12:26:16 +01:00
committed by mohacsi_i
parent 9a143e9ac2
commit 19b57642b5
2 changed files with 139 additions and 108 deletions
@@ -545,14 +545,29 @@ class GigaFrostCamera(PSIDetectorBase):
self.cfgCntNum.set(num_images).wait()
self.cfgCorrMode.set(correction_mode).wait()
# if trigger_mode is not None:
# self.set_trigger_mode(str(trigger_mode))
if trigger_mode is not None:
self.set_trigger_mode(str(trigger_mode))
# Commit parameter
self.cmdSetParam.set(1).wait()
def set_trigger_mode(self, trigger_mode):
if trigger_mode == "soft":
"""
NOTE: The trigger input appears to be dead, it completely ignores the
supplied signal. Use external enable instead, that works!
"""
if trigger_mode == "default":
# trigger modes
self.cfgCntStartBit.set(1).wait()
self.cfgCntEndBit.set(0).wait()
# set modes
self.enable_mode = "soft"
self.trigger_mode = "auto"
self.exposure_mode = "timer"
elif trigger_mode == "soft":
# Switch to physical enable signal
self.cfgEnableScheme.set(0).wait()
# Set enable signal to always
@@ -590,6 +605,16 @@ class GigaFrostCamera(PSIDetectorBase):
# Set trigger edge to fixed frames on posedge
self.cfgCntStartBit.set(1).wait()
self.cfgCntEndBit.set(0).wait()
elif trigger_mode in ["ext_enable", "external_enable"]:
# Switch to physical enable signal
self.cfgEnableScheme.set(0).wait()
# Trigger modes
self.cfgCntStartBit.set(1).wait()
self.cfgCntEndBit.set(0).wait()
# Set modes
self.enable_mode = "external"
self.trigger_mode = "auto"
self.exposure_mode = "timer"
else:
raise RuntimeError(f"Unsupported trigger mode: {trigger_mode}")
+111 -105
View File
@@ -2,7 +2,12 @@
""" Tomcat scan base class examples
A collection of example scan base classes using Automation1 rotation stage,
GigaFrost camera and the StandardDAQ pipeline.
GigaFrost camera and the StandardDAQ pipeline.
NOTE: As an explicit request from Tomcat, all devices must be freely
interchangeable, including the simultaneous use of multiple devices. Therefore
the devices were prepared in such a way, that the scans need to address
devices.
Created on Mon Sep 16 16:45:11 2024
@@ -18,23 +23,23 @@ from bec_server.scan_server.scans import AsyncFlyScanBase, ScanBase
logger = bec_logger.logger
class TomcatStepScan(ScanBase):
"""Simple software step scan forTomcat
Example class for simple BEC-based step scans using the low-level API. All it does is
translate conventional kwargs to tomcat specific naming scheme.
Example class for simple BEC-based step scan using the low-level API.
All it does is translate conventional kwargs to tomcat specific device
pareameters and launches the standard step scan.
Example
-------
>>> scans.gigastep(scan_start=-25, scan_end=155, steps=180, exp_time=0.005, exp_burst=5)
>>> scans.tomcatstepscan(scan_start=-25, scan_end=155, steps=180, exp_time=0.005, exp_burst=5)
"""
scan_name = "tomcatstepscan"
required_kwargs = ["scan_start", "scan_end", "steps"]
gui_config = {
"Movement parameters": ["steps"],
"Acquisition parameters": ["exp_time", "burst_at_each_point", "roix", "roiy"],
"Acquisition parameters": ["exp_time", "exp_burst", "roix", "roiy"],
}
def _get_scan_motors(self):
@@ -47,7 +52,7 @@ class TomcatStepScan(ScanBase):
steps: int,
exp_time=0.005,
settling_time=0.2,
burst_at_each_point=1,
exp_burst=1,
roix=2016,
roiy=2016,
sync="event",
@@ -55,18 +60,18 @@ class TomcatStepScan(ScanBase):
):
# Converting generic kwargs to tomcat device configuration parameters
# Used by gigafrost
kwargs['parameter']['kwargs']["exposure_time_ms"] = 1000 * exp_time
kwargs['parameter']['kwargs']["exposure_period_ms"] = 2 * 1000 * exp_time
kwargs['parameter']['kwargs']["exposure_num_burst"] = burst_at_each_point
kwargs['parameter']['kwargs']["image_width"] = roix
kwargs['parameter']['kwargs']["image_height"] = roiy
kwargs["parameter"]["kwargs"]["exposure_time_ms"] = 1000 * exp_time
kwargs["parameter"]["kwargs"]["exposure_period_ms"] = 2 * 1000 * exp_time
kwargs["parameter"]["kwargs"]["exposure_num_burst"] = exp_burst
kwargs["parameter"]["kwargs"]["image_width"] = roix
kwargs["parameter"]["kwargs"]["image_height"] = roiy
# Used by stdDAQ and DDC
kwargs['parameter']['kwargs']["num_points_total"] = burst_at_each_point * (steps + 1)
kwargs["parameter"]["kwargs"]["num_points_total"] = exp_burst * (steps + 1)
t_modes = {"pso": 0, "event": 1, "inp0": 2, "inp1": 4}
ddc_trigger = t_modes[sync]
kwargs['parameter']['kwargs']["ddc_trigger"] = ddc_trigger
kwargs["parameter"]["kwargs"]["ddc_trigger"] = ddc_trigger
# Use PSO trigger
kwargs['parameter']['kwargs']["pso_wavemode"] = "pulsed"
kwargs["parameter"]["kwargs"]["pso_wavemode"] = "pulsed"
super().__init__(
exp_time=exp_time,
@@ -84,21 +89,17 @@ class TomcatStepScan(ScanBase):
self.scan_steps = steps
self.scan_stepsize = (scan_end - scan_start) / steps
def _calculate_positions(self) -> None:
"""Pre-calculate scan positions"""
for ii in range(self.scan_steps + 1):
self.positions.append(self.scan_start + ii * self.scan_stepsize)
class TomcatSnapNStep(AsyncFlyScanBase):
"""Simple software step scan forTomcat
Example class for simple BEC-based step scans using the low-level API. All it does is
translate conventional kwargs to tomcat specific naming scheme.
Example class for simple BEC-based step scans using the low-level API. All it does is
translate conventional kwargs to tomcat specific naming scheme.
Example
-------
@@ -109,7 +110,7 @@ class TomcatSnapNStep(AsyncFlyScanBase):
required_kwargs = ["scan_start", "scan_end", "steps"]
gui_config = {
"Movement parameters": ["steps"],
"Acquisition parameters": ["exp_time", "burst_at_each_point", "roix", "roiy"],
"Acquisition parameters": ["exp_time", "exp_burst", "roix", "roiy"],
}
def _get_scan_motors(self):
@@ -122,7 +123,7 @@ class TomcatSnapNStep(AsyncFlyScanBase):
steps: int,
exp_time=0.005,
settling_time=0.2,
burst_at_each_point=1,
exp_burst=1,
roix=2016,
roiy=2016,
sync="event",
@@ -130,44 +131,35 @@ class TomcatSnapNStep(AsyncFlyScanBase):
):
# Converting generic kwargs to tomcat device configuration parameters
# Used by gigafrost
kwargs['parameter']['kwargs']["exposure_time_ms"] = 1000 * exp_time
kwargs['parameter']['kwargs']["exposure_period_ms"] = 2 * 1000 * exp_time
kwargs['parameter']['kwargs']["exposure_num_burst"] = burst_at_each_point
kwargs['parameter']['kwargs']["image_width"] = roix
kwargs['parameter']['kwargs']["image_height"] = roiy
kwargs["parameter"]["kwargs"]["exposure_time_ms"] = 1000 * exp_time
kwargs["parameter"]["kwargs"]["exposure_period_ms"] = 2 * 1000 * exp_time
kwargs["parameter"]["kwargs"]["exposure_num_burst"] = exp_burst
kwargs["parameter"]["kwargs"]["image_width"] = roix
kwargs["parameter"]["kwargs"]["image_height"] = roiy
# Used by stdDAQ and DDC
kwargs['parameter']['kwargs']["num_points_total"] = burst_at_each_point * (steps + 1)
kwargs["parameter"]["kwargs"]["num_points_total"] = exp_burst * (steps + 1)
t_modes = {"pso": 0, "event": 1, "inp0": 2, "inp1": 4}
ddc_trigger = t_modes[sync]
kwargs['parameter']['kwargs']["ddc_trigger"] = ddc_trigger
kwargs["parameter"]["kwargs"]["ddc_trigger"] = ddc_trigger
# Use PSO trigger (disable triggering)
kwargs['parameter']['kwargs']["pso_distance"] = 0
kwargs["parameter"]["kwargs"]["pso_distance"] = 0
# For position calculation
self.scan_start = scan_start
self.scan_end = scan_end
self.scan_steps = steps
self.scan_stepsize = (scan_end - scan_start) / steps
self.scan_ntotal = burst_at_each_point * (steps + 1)
self.scan_ntotal = exp_burst * (steps + 1)
self.exp_burst = exp_burst
self.settling_time = settling_time
self.scan_sync = sync
# Aerotech DDC settings: Internal event trigger: PsoEvent = 1
p_modes = {"pso": "PsoOutput", "event": "PsoEvent", "inp0": "HighSpeedInput0RisingEdge", "inp1": "HighSpeedInput1RisingEdge",}
# Used for Aeroscript file substitutions for the task interface
filename = "AerotechSnapAndStepTemplate.ascript"
filesubs = {
"startpos": self.scan_start,
"stepsize": self.scan_stepsize,
"numsteps": self.scan_steps,
"exptime": 2 * exp_time * burst_at_each_point,
"settling": settling_time,
"psotrigger": p_modes[sync],
"npoints": self.scan_ntotal,
}
filesubs = self.get_filesubs()
filetext = self.render_file(filename, filesubs)
# Task inteface
kwargs['parameter']['kwargs']["script_text"] = filetext
kwargs['parameter']['kwargs']["script_file"] = "bec.ascript"
kwargs["parameter"]["kwargs"]["script_text"] = filetext
kwargs["parameter"]["kwargs"]["script_file"] = "bec.ascript"
super().__init__(
exp_time=exp_time,
@@ -178,13 +170,30 @@ class TomcatSnapNStep(AsyncFlyScanBase):
**kwargs,
)
def get_filesubs(self) -> dict:
"""Generate jinja template substitutions."""
p_modes = {
"pso": "PsoOutput",
"event": "PsoEvent",
"inp0": "HighSpeedInput0RisingEdge",
"inp1": "HighSpeedInput1RisingEdge",
}
filesubs = {
"startpos": self.scan_start,
"stepsize": self.scan_stepsize,
"numsteps": self.scan_steps,
"exptime": 2 * self.exp_time * self.exp_burst,
"settling": self.settling_time,
"psotrigger": p_modes[self.scan_sync],
"npoints": self.scan_ntotal,
}
return filesubs
def render_file(self, filename, filesubs):
"""Prepare action: render AeroScript file"""
# Load the test file
if filename is not None:
filename = os.path.join(
os.path.dirname(__file__), "../devices/aerotech/" + filename
)
filename = os.path.join(os.path.dirname(__file__), "../devices/aerotech/" + filename)
logger.info(f"Attempting to load file {filename}")
with open(filename) as f:
templatetext = f.read()
@@ -219,26 +228,21 @@ class TomcatSnapNStep(AsyncFlyScanBase):
# positions = yield from self.stubs.send_rpc_and_wait(self.daqname, "collect")
# logger.info(f"Finished scan with collected positions: {positions}")
def cleanup(self):
"""Set scan progress to 1 to finish the scan"""
self.num_pos = 1
return super().cleanup()
class TomcatSimpleSequence(AsyncFlyScanBase):
"""Simple software step scan forTomcat
Example class for simple BEC-based step scans using the low-level API. All it does is
translate conventional kwargs to tomcat specific naming scheme.
Example class for simple AeroScript-based step scan using the low-level API. All it does is
translate meaningful kwargs to device specific naming scheme.
Example
-------
>>> scans.tomcatsimplesequencescan(33, 180, 180, exp_time=0.005, exp_frames=1800, repeats=10)
>>> scans.tomcatsimplesequencescan(33, 180, 180, exp_time=0.005, exp_burst=1800, repeats=10)
"""
scan_name = "tomcatsimplesequencescan"
@@ -250,7 +254,7 @@ class TomcatSimpleSequence(AsyncFlyScanBase):
"gate_high",
"gate_low",
"exp_time",
"exp_frames",
"exp_burst",
"roix",
"roiy",
"sync",
@@ -265,13 +269,13 @@ class TomcatSimpleSequence(AsyncFlyScanBase):
scan_start: float,
gate_high: float,
gate_low: float,
repeats: int=1,
repmode: str="PosNeg",
exp_time: float=0.005,
exp_frames: float=180,
roix: int=2016,
roiy: int=2016,
sync: str="pso",
repeats: int = 1,
repmode: str = "PosNeg",
exp_time: float = 0.005,
exp_burst: float = 180,
roix: int = 2016,
roiy: int = 2016,
sync: str = "pso",
**kwargs,
):
# Auto-setup configuration parameters from input
@@ -281,63 +285,47 @@ class TomcatSimpleSequence(AsyncFlyScanBase):
self.scan_repnum = repeats
self.scan_repmode = repmode.upper()
self.exp_time = exp_time
self.exp_frames = exp_frames
self.exp_burst = exp_burst
self.scan_sync = sync
# Synthetic values
self.scan_velocity = gate_high / (exp_time * exp_frames)
self.scan_ntotal = exp_burst * repeats
self.scan_velocity = gate_high / (exp_time * exp_burst)
self.scan_acceleration = 500
self.scan_safedistance = 10
self.scan_accdistance = (
self.scan_safedistance
+ 0.5 * self.scan_velocity * self.scan_velocity / self.scan_acceleration
)
self.scan_ntotal = exp_frames * repeats
if self.scan_repmode in ("POS", "NEG"):
self.scan_range = repeats * (gate_high + gate_low)
elif self.scan_repmode in ("POSNEG", "NEGPOS"):
self.scan_range = gate_high + gate_low
else:
raise RuntimeError(f"Unsupported repetition mode: {repmode}")
raise RuntimeError(f"Unsupported repetition mode: {self.scan_repmode}")
# Converting generic kwargs to tomcat device configuration parameters
# Used by gigafrost
kwargs['parameter']['kwargs']["exposure_time_ms"] = 1000 * exp_time
kwargs['parameter']['kwargs']["exposure_period_ms"] = 2 * 1000 * exp_time
kwargs['parameter']['kwargs']["exposure_num_burst"] = exp_frames
kwargs['parameter']['kwargs']["image_width"] = roix
kwargs['parameter']['kwargs']["image_height"] = roiy
kwargs["parameter"]["kwargs"]["exposure_time_ms"] = 1000 * exp_time
kwargs["parameter"]["kwargs"]["exposure_period_ms"] = 2 * 1000 * exp_time
kwargs["parameter"]["kwargs"]["exposure_num_burst"] = exp_burst
kwargs["parameter"]["kwargs"]["image_width"] = roix
kwargs["parameter"]["kwargs"]["image_height"] = roiy
# Used by stdDAQ and DDC
kwargs['parameter']['kwargs']["num_points_total"] = exp_frames * (self.scan_repnum + 1)
kwargs["parameter"]["kwargs"]["num_points_total"] = exp_burst * (self.scan_repnum + 1)
t_modes = {"pso": 0, "event": 1, "inp0": 2, "inp1": 4}
ddc_trigger = t_modes[sync]
kwargs['parameter']['kwargs']["ddc_trigger"] = ddc_trigger
kwargs["parameter"]["kwargs"]["ddc_trigger"] = ddc_trigger
# Use PSO trigger (disable triggering)
kwargs['parameter']['kwargs']["pso_distance"] = 0
kwargs["parameter"]["kwargs"]["pso_distance"] = 0
# Aerotech DDC settings: Internal event trigger: PsoEvent = 1
p_modes = {"pso": "PsoOutput", "event": "PsoEvent", "inp0": "HighSpeedInput0RisingEdge", "inp1": "HighSpeedInput1RisingEdge",}
# Used for Aeroscript file substitutions for the task interface
filename = "AerotechSimpleSequenceTemplate.ascript"
filesubs = {
"startpos": self.scan_start,
"scanrange": self.scan_range,
"nrepeat": self.scan_repnum,
"scanvel": self.scan_velocity,
"scanacc": self.scan_acceleration,
"accdist": self.scan_accdistance,
"npoints": self.scan_ntotal,
"scandir": self.scan_repmode.upper(),
"psotrigger": p_modes[sync],
}
filesubs = self.get_positions(filesubs)
filesubs = self.get_filesubs()
filetext = self.render_file(filename, filesubs)
# Task inteface
kwargs['parameter']['kwargs']["script_text"] = filetext
kwargs['parameter']['kwargs']["script_file"] = "bec.ascript"
kwargs["parameter"]["kwargs"]["script_text"] = filetext
kwargs["parameter"]["kwargs"]["script_file"] = "bec.ascript"
super().__init__(
exp_time=exp_time,
@@ -352,9 +340,7 @@ class TomcatSimpleSequence(AsyncFlyScanBase):
"""Prepare action: render AeroScript file"""
# Load the test file
if filename is not None:
filename = os.path.join(
os.path.dirname(__file__), "../devices/aerotech/" + filename
)
filename = os.path.join(os.path.dirname(__file__), "../devices/aerotech/" + filename)
logger.info(f"Attempting to load file {filename}")
with open(filename) as f:
templatetext = f.read()
@@ -389,7 +375,27 @@ class TomcatSimpleSequence(AsyncFlyScanBase):
# positions = yield from self.stubs.send_rpc_and_wait(self.daqname, "collect")
# logger.info(f"Finished scan with collected positions: {positions}")
def get_positions(self, filesubs: dict={}):
def get_filesubs(self):
"""Generate jinja template substitutions."""
# Aerotech DDC settings: Internal event trigger: PsoEvent = 1
p_modes = {
"pso": "PsoOutput",
"event": "PsoEvent",
"inp0": "HighSpeedInput0RisingEdge",
"inp1": "HighSpeedInput1RisingEdge",
}
filesubs = {
"startpos": self.scan_start,
"scanrange": self.scan_range,
"nrepeat": self.scan_repnum,
"scanvel": self.scan_velocity,
"scanacc": self.scan_acceleration,
"accdist": self.scan_accdistance,
"npoints": self.scan_ntotal,
"scandir": self.scan_repmode,
"psotrigger": p_modes[self.scan_sync],
}
# Fill PSO vectors according to scan direction
# NOTE: Distance counter is bidirectional
pso_bounds_pos = []
@@ -412,4 +418,4 @@ class TomcatSimpleSequence(AsyncFlyScanBase):
def cleanup(self):
"""Set scan progress to 1 to finish the scan"""
self.num_pos = 1
return super().cleanup()
return super().cleanup()