Merge branch 'sgalil_flyscan' into 'master'

Sgalil flyscan

See merge request bec/ophyd_devices!32
This commit is contained in:
appel_c 2023-08-17 18:08:44 +02:00
commit 284737163a
8 changed files with 889 additions and 69 deletions

View File

@ -6,7 +6,7 @@ Created on Tue Nov 9 16:12:47 2021
"""
from ophyd import Device, Component, EpicsSignal, EpicsSignalRO, Kind
from ophyd import PositionerBase, PVPositioner, Signal
from ophyd import PVPositioner, Signal
from ophyd.pseudopos import (
pseudo_position_argument,
real_position_argument,
@ -35,7 +35,7 @@ class DelayStatic(Device):
amplitude = Component(
EpicsSignal, "OutputAmpAI", write_pv="OutputAmpAO", name="amplitude", kind=Kind.config
)
polarity = Component(
offset = Component(
EpicsSignal, "OutputOffsetAI", write_pv="OutputOffsetAO", name="offset", kind=Kind.config
)
@ -44,6 +44,7 @@ class DummyPositioner(PVPositioner):
setpoint = Component(EpicsSignal, "DelayAO", put_complete=True, kind=Kind.config)
readback = Component(EpicsSignalRO, "DelayAI", kind=Kind.config)
done = Component(Signal, value=1)
reference = Component(EpicsSignal, "ReferenceMO", put_complete=True, kind=Kind.config)
class DelayPair(PseudoPositioner):
@ -61,11 +62,14 @@ class DelayPair(PseudoPositioner):
# ch2 = Component(EpicsSignal, "DelayAI", write_pv="DelayAO", name="ch2", put_complete=True, kind=Kind.config)
ch1 = Component(DummyPositioner, name="ch1")
ch2 = Component(DummyPositioner, name="ch2")
io = Component(DelayStatic, name="io")
def __init__(self, *args, **kwargs):
# Change suffix names before connecting (a bit of dynamic connections)
self.__class__.__dict__["ch1"].suffix = kwargs["channel"][0]
self.__class__.__dict__["ch2"].suffix = kwargs["channel"][1]
self.__class__.__dict__["io"].suffix = kwargs["channel"]
del kwargs["channel"]
# Call parent to start the connections
super().__init__(*args, **kwargs)
@ -153,7 +157,7 @@ class DelayGeneratorDG645(Device):
)
# Command PVs
arm = Component(EpicsSignal, "TriggerDelayBO", name="arm", kind=Kind.omitted)
# arm = Component(EpicsSignal, "TriggerDelayBO", name="arm", kind=Kind.omitted)
# Burst mode
burstMode = Component(
@ -174,18 +178,21 @@ class DelayGeneratorDG645(Device):
def stage(self):
"""Trigger the generator by arming to accept triggers"""
self.arm.write(1).wait()
# TODO check PV TriggerDelayBO, seems to be a bug in the IOC
# self.arm.write(1).wait()
super().stage()
def unstage(self):
"""Stop the trigger generator from accepting triggers"""
self.arm.write(0).wait()
# self.arm.write(0).wait()
super().stage()
def burstEnable(self, count, delay, period, config="all"):
"""Enable the burst mode"""
# Validate inputs
count = int(count)
assert count > 0, "Number of bursts must be positive"
assert delay > 0, "Burst delay must be positive"
assert delay >= 0, "Burst delay must be larger than 0"
assert period > 0, "Burst period must be positive"
assert config in ["all", "first"], "Supported bust configs are 'all' and 'first'"
@ -199,7 +206,7 @@ class DelayGeneratorDG645(Device):
elif config == "first":
self.burstConfig.set(1).wait()
def busrtDisable(self):
def burstDisable(self):
"""Disable the burst mode"""
self.burstMode.set(0).wait()

View File

@ -0,0 +1,43 @@
# Documentation for Stanford Research DG645 device and ophyd class
This device allows to create TTL pulse for hardware trigger control of stages, detectors and more.
It can be operate on external and/or internal triggering with up to 5 signals (T0, AB, CD, EF, GH) that can be generated for single or burst triggers.
A detailed description of all functionality can be found in the [manual](https://www.thinksrs.com/downloads/pdfs/manuals/DG645m.pdf) of the device online.
The DG645 is for instance used for hardware triggering for fly scans at cSAXS.
As for all EPIC devices, you need to ensure that the EPICS IOC is running to be able to control the device.
This readme relates to the implementation of the devices as an ophyd class done in DelayGeneratorDG645.py.
## Integration of device in IPython kernel
To add the device within IPython, proceed with the following steps:
```Python
from ophyd_devices.epics.devices.DelayGeneratorDG645 import DelayGeneratorDG645
ddg4 = DelayGeneratorDG645(prefix='delaygen:DG1:',name='DDG4')
```
with prefix and name matching the settings within the EPIC's IOC.
## TODO Integration of device in BEC device config!
to be tested too
## DDG645 ophyd class wrapper
***Control trigger source***
```Python
ddg4.source.set(value:int)
# int between 0 and 6
```
with *int*
- 0: internal, 1: ext rising edge, 2: ext falling edge, 3: SS ext rise edge, 4: SS ext falling edge, 5: Single shot, 6: line
See manual for more details, most commonly used 0,1,2.
***Control width and delay of for instance channel AB***
```Python
ddg4.channelAB.width.set(1e-3) # setpoint in s
ddg4.channelAB.width.read()
ddg4.channelAB.delay.set(1e-3) # setpoint in s
```
***Burst mode***
Check page 26 of manual for details.
```Python
ddg4.burstEnable(count:int, delay:float, period:float, config="first")
# with
# count: number of burst triggers on all channels: integer>0
# delay: initial delay before first trigger in s:>0
# period: repetition rate for triggers in s:>0, e.g. exposure time + readout time
# config Whether T0 fires off for all or only first trigger in burst sequence: "all" or "first"
# for example
ddg4.burstEnable(50, 0.015, 0.05, config="first")
```

View File

@ -0,0 +1,201 @@
<mxfile host="app.diagrams.net" modified="2023-07-31T14:55:08.470Z" agent="Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0" etag="hKC99drhJ4x6bY93jFGH" version="21.6.6" type="device">
<diagram name="Page-1" id="b520641d-4fe3-3701-9064-5fc419738815">
<mxGraphModel dx="2049" dy="1206" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="850" background="none" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="21ea969265ad0168-6" value="SGalil stages" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=1;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=12" parent="1" vertex="1">
<mxGeometry x="12.5" y="24" width="160" height="110" as="geometry" />
</mxCell>
<mxCell id="21ea969265ad0168-7" value="grid fly scan (2D), TTL signal at the beginning of each line" style="text;html=1;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="21ea969265ad0168-6" vertex="1">
<mxGeometry y="26" width="160" height="54" as="geometry" />
</mxCell>
<mxCell id="21ea969265ad0168-14" value="DelayGenerator 4 - ddg4" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=1;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=12" parent="1" vertex="1">
<mxGeometry x="270" y="40" width="160" height="160" as="geometry" />
</mxCell>
<mxCell id="21ea969265ad0168-15" value="ext. trigger" style="text;html=1;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="21ea969265ad0168-14" vertex="1">
<mxGeometry y="26" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="21ea969265ad0168-16" value="operated in normal mode, controls fast shutter and triggers second DDG" style="text;html=1;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="21ea969265ad0168-14" vertex="1">
<mxGeometry y="52" width="160" height="48" as="geometry" />
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-19" value="&lt;div&gt;channel AB&lt;/div&gt;" style="text;html=1;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="21ea969265ad0168-14">
<mxGeometry y="100" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-21" value="&lt;div&gt;channel T0&lt;br&gt;&lt;/div&gt;" style="text;html=1;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="21ea969265ad0168-14">
<mxGeometry y="130" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="21ea969265ad0168-22" value="Logical Card" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=1;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=12" parent="1" vertex="1">
<mxGeometry x="710" y="250" width="160" height="104" as="geometry" />
</mxCell>
<mxCell id="21ea969265ad0168-24" value="&lt;div&gt;both signals low -&amp;gt; low&lt;br&gt;either on signal high -&amp;gt; high&lt;br&gt;both signals high -&amp;gt; low&lt;br&gt;&lt;br&gt;&lt;/div&gt;" style="text;html=1;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="21ea969265ad0168-22" vertex="1">
<mxGeometry y="26" width="160" height="54" as="geometry" />
</mxCell>
<mxCell id="21ea969265ad0168-26" value="MCS readout card" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=1;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=12" parent="1" vertex="1">
<mxGeometry x="920" y="360" width="160" height="110" as="geometry" />
</mxCell>
<mxCell id="21ea969265ad0168-27" value="falling edge trigger&lt;br&gt;readout is triggered between to falling edges, thus logical board required" style="text;html=1;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="21ea969265ad0168-26" vertex="1">
<mxGeometry y="26" width="160" height="74" as="geometry" />
</mxCell>
<mxCell id="21ea969265ad0168-36" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;labelBackgroundColor=none;startFill=0;startSize=8;endFill=1;endSize=8;fontFamily=Verdana;fontSize=12;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="21ea969265ad0168-6" target="21ea969265ad0168-15" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="21ea969265ad0168-38" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;labelBackgroundColor=none;startFill=0;startSize=8;endFill=1;endSize=8;fontFamily=Verdana;fontSize=12;align=center;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="O8qpyw_Cq4v1m74naMs6-19" target="O8qpyw_Cq4v1m74naMs6-3" edge="1">
<mxGeometry x="-0.0026" relative="1" as="geometry">
<mxPoint x="430" y="150" as="sourcePoint" />
<mxPoint x="680" y="60" as="targetPoint" />
<Array as="points">
<mxPoint x="660" y="155" />
<mxPoint x="660" y="43" />
</Array>
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="21ea969265ad0168-42" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.5;labelBackgroundColor=none;startFill=0;startSize=8;endFill=1;endSize=8;fontFamily=Verdana;fontSize=12;exitDx=0;exitDy=0;" parent="1" source="O8qpyw_Cq4v1m74naMs6-19" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="660" y="155" />
<mxPoint x="660" y="290" />
</Array>
<mxPoint x="710" y="290" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="21ea969265ad0168-43" style="edgeStyle=orthogonalEdgeStyle;html=1;labelBackgroundColor=none;startFill=0;startSize=8;endFill=1;endSize=8;fontFamily=Verdana;fontSize=12;entryX=0;entryY=0.378;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="21ea969265ad0168-24" edge="1" target="21ea969265ad0168-27">
<mxGeometry relative="1" as="geometry">
<mxPoint x="880" y="331" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-2" value="Fast shutter: fsh" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=1;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=12" vertex="1" parent="1">
<mxGeometry x="750" width="160" height="104" as="geometry" />
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-3" value="FSH opening time, e.g. 20ms" style="text;html=1;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="O8qpyw_Cq4v1m74naMs6-2">
<mxGeometry y="26" width="160" height="34" as="geometry" />
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-11" value="TTL" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=default;" vertex="1" parent="1">
<mxGeometry x="200" y="60" width="40" height="30" as="geometry" />
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-16" value="DelayGenerator 4 - ddg4" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=1;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=12" vertex="1" parent="1">
<mxGeometry x="270" y="350" width="230" height="190" as="geometry" />
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-17" value="ext. trigger" style="text;html=1;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="O8qpyw_Cq4v1m74naMs6-16">
<mxGeometry y="26" width="230" height="24" as="geometry" />
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-18" value="operated in burst mode:&lt;br&gt;burstCount: N_points&lt;br&gt;burstPeriod: (exp_time + readout time)&lt;br&gt;burstDelay: fsh opening (20ms)" style="text;html=1;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="O8qpyw_Cq4v1m74naMs6-16">
<mxGeometry y="50" width="230" height="68" as="geometry" />
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-29" value="Channel AB" style="text;html=1;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="O8qpyw_Cq4v1m74naMs6-16">
<mxGeometry y="118" width="230" height="24" as="geometry" />
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-28" value="Channel CD " style="text;html=1;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="O8qpyw_Cq4v1m74naMs6-16">
<mxGeometry y="142" width="230" height="24" as="geometry" />
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-30" value="Channel EF" style="text;html=1;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="O8qpyw_Cq4v1m74naMs6-16">
<mxGeometry y="166" width="230" height="24" as="geometry" />
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-27" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;labelBackgroundColor=none;startFill=0;startSize=8;endFill=1;endSize=8;fontFamily=Verdana;fontSize=12;align=center;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="O8qpyw_Cq4v1m74naMs6-21" target="O8qpyw_Cq4v1m74naMs6-17">
<mxGeometry x="-0.0026" relative="1" as="geometry">
<mxPoint x="220" y="262" as="sourcePoint" />
<mxPoint x="240" y="390" as="targetPoint" />
<Array as="points">
<mxPoint x="510" y="185" />
<mxPoint x="510" y="270" />
<mxPoint x="200" y="270" />
<mxPoint x="200" y="390" />
</Array>
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-31" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.5;labelBackgroundColor=none;startFill=0;startSize=8;endFill=1;endSize=8;fontFamily=Verdana;fontSize=12;exitDx=0;exitDy=0;" edge="1" parent="1" source="O8qpyw_Cq4v1m74naMs6-28" target="O8qpyw_Cq4v1m74naMs6-36">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="640" y="504" />
<mxPoint x="640" y="610" />
</Array>
<mxPoint x="500" y="480" as="sourcePoint" />
<mxPoint x="910" y="610" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-32" style="edgeStyle=orthogonalEdgeStyle;html=1;labelBackgroundColor=none;startFill=0;startSize=8;endFill=1;endSize=8;fontFamily=Verdana;fontSize=12;entryX=0;entryY=0.25;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="O8qpyw_Cq4v1m74naMs6-28" target="O8qpyw_Cq4v1m74naMs6-34">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="640" y="504" />
<mxPoint x="640" y="610" />
<mxPoint x="760" y="610" />
<mxPoint x="760" y="528" />
</Array>
<mxPoint x="500" y="500" as="sourcePoint" />
<mxPoint x="880" y="540" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-33" style="edgeStyle=orthogonalEdgeStyle;html=1;labelBackgroundColor=none;startFill=0;startSize=8;endFill=1;endSize=8;fontFamily=Verdana;fontSize=12;entryX=0.008;entryY=0.154;entryDx=0;entryDy=0;entryPerimeter=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="O8qpyw_Cq4v1m74naMs6-28" target="O8qpyw_Cq4v1m74naMs6-38">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="640" y="504" />
<mxPoint x="640" y="610" />
<mxPoint x="760" y="610" />
<mxPoint x="760" y="701" />
</Array>
<mxPoint x="500" y="480" as="sourcePoint" />
<mxPoint x="880" y="700" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-34" value="eiger" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=1;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=12" vertex="1" parent="1">
<mxGeometry x="920" y="510" width="160" height="70" as="geometry" />
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-35" value="rising edge&lt;br&gt;set exp_time on device" style="text;html=1;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="O8qpyw_Cq4v1m74naMs6-34">
<mxGeometry y="26" width="160" height="44" as="geometry" />
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-36" value="falcon" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=1;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=12" vertex="1" parent="1">
<mxGeometry x="920" y="600" width="160" height="70" as="geometry" />
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-37" value="rising edge &lt;br&gt;set exp_time on device" style="text;html=1;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="O8qpyw_Cq4v1m74naMs6-36">
<mxGeometry y="26" width="160" height="44" as="geometry" />
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-38" value="Pilatus 300k" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=1;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=12" vertex="1" parent="1">
<mxGeometry x="920" y="690" width="160" height="70" as="geometry" />
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-39" value="rising edge &lt;br&gt;set exp_time on device" style="text;html=1;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="O8qpyw_Cq4v1m74naMs6-38">
<mxGeometry y="26" width="160" height="44" as="geometry" />
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-40" style="edgeStyle=orthogonalEdgeStyle;html=1;labelBackgroundColor=none;startFill=0;startSize=8;endFill=1;endSize=8;fontFamily=Verdana;fontSize=12;entryX=-0.005;entryY=1.002;entryDx=0;entryDy=0;entryPerimeter=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="O8qpyw_Cq4v1m74naMs6-29" target="21ea969265ad0168-24">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="560" y="480" />
<mxPoint x="560" y="330" />
</Array>
<mxPoint x="510" y="520" as="sourcePoint" />
<mxPoint x="700" y="330" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-41" style="edgeStyle=orthogonalEdgeStyle;html=1;labelBackgroundColor=none;startFill=0;startSize=8;endFill=1;endSize=8;fontFamily=Verdana;fontSize=12;exitX=1;exitY=0.167;exitDx=0;exitDy=0;exitPerimeter=0;" edge="1" parent="1" source="O8qpyw_Cq4v1m74naMs6-30">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="530" y="520" />
<mxPoint x="530" y="780" />
</Array>
<mxPoint x="500" y="530" as="sourcePoint" />
<mxPoint x="610" y="780" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-42" value="SGalil positions encoder" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=1;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=12" vertex="1" parent="1">
<mxGeometry x="610" y="740" width="160" height="70" as="geometry" />
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-43" value="triggered on falling edge" style="text;html=1;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="O8qpyw_Cq4v1m74naMs6-42">
<mxGeometry y="26" width="160" height="44" as="geometry" />
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-44" value="&lt;div align=&quot;justify&quot;&gt;&lt;span style=&quot;background-color: rgb(255, 255, 255);&quot;&gt;channel AB&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;justify&quot;&gt;&lt;div&gt;&lt;span style=&quot;background-color: rgb(255, 255, 255);&quot;&gt;delay:0&lt;br&gt;&lt;/span&gt;&lt;/div&gt;&lt;span style=&quot;background-color: rgb(255, 255, 255);&quot;&gt;width: fsh opening (20ms)&lt;br&gt;+ N_points * exp_time&lt;br&gt;+ (N_points-1) * readout time&lt;br&gt;&lt;/span&gt;&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=default;" vertex="1" parent="1">
<mxGeometry x="450" y="44" width="180" height="90" as="geometry" />
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-45" value="&lt;div align=&quot;justify&quot;&gt;&lt;span style=&quot;background-color: rgb(255, 255, 255);&quot;&gt;channel AB&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;justify&quot;&gt;&lt;div&gt;&lt;span style=&quot;background-color: rgb(255, 255, 255);&quot;&gt;delay:0&lt;br&gt;&lt;/span&gt;&lt;/div&gt;&lt;span style=&quot;background-color: rgb(255, 255, 255);&quot;&gt;width: exp_time &lt;br&gt;or less&lt;br&gt;&lt;/span&gt;&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=default;" vertex="1" parent="1">
<mxGeometry x="530" y="300" width="110" height="70" as="geometry" />
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-46" value="&lt;div align=&quot;justify&quot;&gt;&lt;span style=&quot;background-color: rgb(255, 255, 255);&quot;&gt;channel CD&lt;br&gt;split into 3 signals&lt;br&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;justify&quot;&gt;&lt;div&gt;&lt;span style=&quot;background-color: rgb(255, 255, 255);&quot;&gt;delay:0&lt;br&gt;&lt;/span&gt;&lt;/div&gt;&lt;span style=&quot;background-color: rgb(255, 255, 255);&quot;&gt;width: exp_time &lt;br&gt;or less&lt;br&gt;&lt;/span&gt;&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=default;" vertex="1" parent="1">
<mxGeometry x="575" y="450" width="120" height="90" as="geometry" />
</mxCell>
<mxCell id="O8qpyw_Cq4v1m74naMs6-47" value="&lt;div align=&quot;justify&quot;&gt;&lt;span style=&quot;background-color: rgb(255, 255, 255);&quot;&gt;channel EF&lt;br&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;justify&quot;&gt;&lt;div&gt;&lt;span style=&quot;background-color: rgb(255, 255, 255);&quot;&gt;delay:0&lt;br&gt;&lt;/span&gt;&lt;/div&gt;&lt;span style=&quot;background-color: rgb(255, 255, 255);&quot;&gt;width: exp_time/2 &lt;br&gt;&lt;/span&gt;&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=default;" vertex="1" parent="1">
<mxGeometry x="480" y="610" width="120" height="60" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

View File

@ -1,22 +0,0 @@
# Summary on communication commands for SGalilMotor
## sgalil_y - vertical axis (samy)
- Axis 2, C
- in motion: "MG _BG{axis_char}", e.g. "MG _BGC" , 0 or 1
- limit switch not pressed: "MG _LR{axis_char}, _LF{axis_char}" , 0 or 1
- position: "MG _TP{axis_char}/mm" , position in mm
- Axis referenced: "MG allaxref", 0 or 1
- stop all axis: "XQ#STOP,1"
- is motor on: "MG _MO{axis_char}", 0 or 1
- is thread active: "MG _XQ{thread_id}", 0 or 1
**Specific for sgalil_y**
- set_motion_speed: "SP{axis_char}=2*mm", 2mm/s is max speed
- set_final_pos: "PA{axis_char}={val:04f}*mm", target pos in mm
- start motion: "BG{axis_char}", start motion
## sgalil_y - horizontal axis (samx) - due to hardware modifications a bit more complicated
- initiate with Axis 4, E
**Specific for sgalil_x**
- set_final_pos: "targ{axis_char}={val:04f}", e.g. "targE=2.0000"
- start motion: "XQ#POSE,{axis_char}"
- For *in motion* and *limit switch not pressed* commands,
the key changes to AXIS 5 || F, e.g. "MG _BGF"
- For *position* switch to Axis 0 || A, e.g. "MG _TPA/mm"

View File

@ -0,0 +1,415 @@
'******************************************************************************
' scanning saxs stages controller code
' version 0.1 20160113, holler, based on example code
' version 0.2 20160321, holler, small adjustments
' version 0.3 20160323, position sampling
' version 0.4 grid scan implemented
' version 0.5 20160426, shutter control from controller
' version 0.6 20160614, code for manual stage tuning added
' version 0.7 20170609, prefact added
' version 0.8 20190327, stepper motor x axis E, encoder axis A
' DC motor y axis C
' version 0.9 20190403, Pos sampling averaging and ring buffered
' internal grid scan updated
' 20190507 various fixes during comm. at beamline
' off now in microns for higher resolution
' version 1.1 20191021, position samples were off compared to xrays
' use AL and RL commands for position latch
' to reduce delay for axis C (continuous)
' switch to DI3 required, averaging removed
' version 1.2 20200500, switch stepper motor to axis F
' because motor driver axis E defective
' version 2.0 20230816, adjustments in premove with BEC
' DO8 controls the shutter
' DI1 1 during exposure for pos sampling
' Thread overview
'******************************************************************************
#AUTO
DA*,*[];'DEALLOCATE ARRAYS
ssaxs_v=1.3
IA129,129,122,26
'acctim determines pre motion
acctim=2.5
prvspeed=0
posest=0
'prefact increases the distance for pre acceleration
'if in acctim limits
prefact=2.5
off=0
DM aposavg[2000]
DM cposavg[2000]
nums=1
JS#INIT
JS#SETPLAT
EN
#CALIBC
'ACC=1000000
'DCC=1000000
'SPC=1*mm
PAC=2*mm
BGC;AMC
WT 1000
PAC=5*mm
BGC;AMC
WT 1000
JP#CALIBC
EN
'default settings
#SETTOMO
IF(allaxref=1)
EN
ENDIF
KPC=100
PLC=0.3
KIC=10
KDC=30
ILC=9
FAC=10
FVC=240
EN
'set tuning parameters for scanning saxs plate
#SETPLAT
IF(allaxref=1)
EN
ENDIF
KPC=100
PLC=0.3
KIC=10
KDC=30
ILC=9
FAC=10
FVC=240
EN
'called with AB before execution
#SAMPLE
posct=0
sposct=0
IF(_XQ2=-1)
'arm position latch rising axis C
'set latch direction rising
'we do this prior the loop, to do the switching later asap
CN ,,1
ALC
XQ#SAMPLEL,2
ELSE
runerr=1
ENDIF
EN
#SAMPLEL
'wait for latch
AI3
'WT1
''#WAITLT1
''JP#WAITLT1,(_ALC=1);'WAIT UNTIL CAPTURED'
'write encoder position to a array
aposstrt=_TPA
'write latched position to c array
cposstrt=_RLC
'change latch direction falling axis C
CN ,,-1
ALC
'wait for latch
AI-3
'WT1
''#WAITLT2
''JP#WAITLT2,(_ALC=1);'WAIT UNTIL CAPTURED
'write encoder position to a array
aposend=_TPA
'write latched position to c array
cposend=_RLC
'arm position latch rising axis C for next cycle
'set latch direction rising
CN ,,1
ALC
aposavg[posct]=((aposstrt+aposend)/2)
cposavg[posct]=((cposstrt+cposend)/2)
posct=posct+1
sposct=sposct+1
IF(posct>1999)
posct=0
ENDIF
JP #SAMPLEL,(posct<nums)
EN
#SCANG
'a_start, a_end, speed is line axis
'b_start, gridmax, b_step is grid axis
gridct=0
'nums=1000
'wait for detector
WT350
IF(_XQ3=(-1))
XQ#SCANGL,3
ENDIF
EN
#SCANGL
gridpos=b_start+(gridct*b_step)
targE=gridpos
JS#POSE
WT10
IF(@INT[(gridct/2)]<>(gridct/2))
start=a_start
end=a_end
ELSE
start=a_end
end=a_start
ENDIF
'XQ#SCANL,5
'scanstat=-1
'#lineact
'WT2
'JP#lineact,(scanstat<>0)
JS#SCANL
gridct=gridct+1
JP #SCANGL,(gridct<=gridmax)
'close shutter
CB8
EN
#TEMP1
EN
'
#SCANL
'based on acceleration of 500 mm/s^2
'and a max speed of 2 mm/s
'the max distance needed for acceleration is 4 microns
'so we pre-move 10 microns
'variables to set in mm, mm/s
'start = start position
'end = end position
'speed = velocity
'the scan axis is defined in the init section
IF(allaxref=0)
EN
ENDIF
'
IF(end>start)
dir=1
ELSE
dir=-1
ENDIF
'measure required premove
IF((@ABS[(prvspeed-speed)])>0.001)
premv=speed*mm*5
IF(premv>(3*mm))
premv=3*mm
ENDIF
measpre=1
ELSE
measpre=0
ENDIF
'for internal grid scans reduce overshoot
'IF(_XQ3<>-1)
'redpremv=0.1 ;'case for int grid scan
'ELSE
'redpremv=1 ;'case for line based scans
'ENDIF
'we are doing an internal grid
'reduce overshoot
prepos=(start*mm)-(dir*redpremv*(premv+(off/1000*mm)))
'move to pre-start position if needed
'prepos=(start*mm)-(dir*speed*acctim*mm)
'IF(@ABS[(speed*acctim)]<0.01)
'prepos=(start*mm)-(dir*0.01*mm*prefact)
'ENDIF
'IF(@ABS[(speed*acctim)]>0.1)
'ENDIF
IF((@ABS[(_TDC-prepos)])>(0.002*mm))
scanstat=1
SPC=2*mm
PAC=prepos
BGC
AMC
'open the shutter
SB8
WT10
ENDIF
IF((_LFC<>0)&(_LRC=<>0))
scanstat=2
SPC=@RND[speed*mm]
'arm trigger
trigpos=(start*mm)+(dir*off/1000*mm)
IF(dir=1)
OCC=trigpos,0
ENDIF
IF(dir=-1)
OCC=trigpos,-65536;
ENDIF
'PAC=((end*mm)+(dir*premv))
PAC=((end*mm)+(dir*redpremv*(premv+(off/1000*mm))))
BGC
IF(measpre=1)
calstart=_TDC
WT25
#CALIBV
prevvel=_TVC
WT10
JP#CALIBV,((@ABS[(prevvel-(_TVC/mm))])<15)
calend=_TDC
premv=((@ABS[(calend-calstart)])*3)
'case of grid scan
prvspeed=speed
ENDIF
'
AMC
ENDIF
scanstat=0
'close the shutter
'#SHUTWT
'JP#SHUTWT,(@IN[1]=1)
'WT10
'JP#SHUTWT,(@IN[1]=1)
'
'we are doing an internal grid
IF(_XQ3=-1)
CB8
ENDIF
EN
'
#POSE
posctr=0
posest=1
sttime=TIME
PTF=1;' Position Tracking aktiv
errE=(targE-(_TPA/mm));' Fehler in mm
IF((@ABS[errE])>0.2)
SPF=12*stpmm
ENDIF
#CORRE
posest=2
errE=(targE-(_TPA/mm));' Fehler in mm
PAF=_TDF+(errE*stpmm)
IF((@ABS[errE])<0.1)
SPF=5*stpmm
ENDIF
IF((@ABS[errE])<0.0001)
posctr=posctr+1
IF(posctr>5)
STF
'MG TIME-sttime, (targE-(_TPA/mm))*1000
EN
ENDIF
ELSE
posctr=0
ENDIF
WT5
JP#CORRE
posest=3
MCF
EN
'
#ZZ
targE=120;XQ#POSE
WT12000
targE=20;XQ#POSE
WT12000
JP#ZZ
EN
#FINDREF
SB1;' Bremse C-Achse loesen (nur in Verbindung mit SHC)
SHC
SHF
JS#LIMSWI
JS#REFE
JS#REFC
allaxref=1
targE=0
EN
'
#REFE
SHF
JGF=-2*stpmm
BGF
'MG "suche negativen Endschalter E"
AMF
WT100
'step counter zero
DPF=0
'encoder zero
DPA=0
EN
'
#REFC
SB1;' Bremse loesen (nur in Verbindung mit SHC)
SHC
JGC=-2*mm
BGC
'MG "suche negativen Endschalter C"
AMC
WT100
DPC=0
EN
'
#LIMSWI
scanstat=0
JS#LFF,_LFF=0;' +LIMIT E
JS#LRF,_LRF=0;' -LIMIT E
JS#LFC,_LFC=0;' +LIMIT C
JS#LRC,_LRC=0;' -LIMIT C
RE;' RETURN FROM ERROR INTERUPT
'
#LRF
'MG "- LIMIT E-ACHSE "
STF
AMF
JGF=stpmm
BGF
#LOOPA1
JP#LOOPA1,_LRF=0
STF
MCF
EN
'
#LFF
'MG "+ LIMIT E-ACHSE "
STF
AMF
JGF=-stpmm
BGF
#LOOPA2
JP#LOOPA2,_LFF=0
STF
MCF
EN
'
#LRC
'MG "- LIMIT C-ACHSE "
STC
AMC
JGC=mm
BGC
#LOOPC1
JP#LOOPC1,_LRC=0
STC
AMC
EN
'
#LFC
'MG "- LIMIT C-ACHSE "
STC
AMC
JGC=-mm
BGC
#LOOPC2
JP#LOOPC2,_LFC=0
STC
AMC
EN
'
#INIT
'define fast scan axis redefined by spec
scanstat=0
mm=10000;' 100nm Aufloesung im encoder
stpmm=50000;' microsteps axis E/mm
ratio=5;'
allaxref=0
'acceleration rates
ACF=3000000
DCF=3000000
SPF=2*mm
OEF=0;' OF ON ERROR axis E ausgeschaltet
MTF=2
KSF=0.5;' Smoothing ausgeschaltet
ACC=10*mm
DCC=10*mm
DVC=1;' Dual loop deaktiviert
EN
'

View File

@ -152,7 +152,7 @@ class GalilController(Controller):
def stop_all_axes(self) -> str:
return self.socket_put_and_receive(f"XQ#STOP,1")
def axis_is_referenced(self, axis_Id_numeric) -> bool:
def axis_is_referenced(self) -> bool:
return bool(float(self.socket_put_and_receive(f"MG allaxref").strip()))
def show_running_threads(self) -> None:
@ -172,10 +172,10 @@ class GalilController(Controller):
def get_motor_limit_switch(self, axis_Id) -> list:
# SGalil specific
if axis_id == 2:
if axis_Id == "C":
ret = self.socket_put_and_receive(f"MG _LF{axis_Id}, _LR{axis_Id}")
high, low = ret.strip().split(" ")
elif axis_id == 4:
elif axis_Id == "E":
ret = self.socket_put_and_receive(f"MG _LF{'F'}, _LR{'F'}")
high, low = ret.strip().split(" ")
return [not bool(float(low)), not bool(float(high))]
@ -200,7 +200,7 @@ class GalilController(Controller):
f"{axis.axis_Id_numeric}/{axis.axis_Id}",
axis.name,
axis.connected,
self.axis_is_referenced(axis.axis_Id_numeric),
self.axis_is_referenced(),
self.is_motor_on(axis.axis_Id),
self.get_motor_limit_switch(axis.axis_Id),
axis.readback.read().get(axis.name).get("value"),
@ -217,29 +217,125 @@ class GalilController(Controller):
if isinstance(controller, GalilController):
controller.describe()
# @threadlocked
# def fly_grid_scan(self, start_y:float, end_y:float, y_interval:int, start_x:float, end_x:float, x_interval:int, ctime:float, readtime:float) -> None:
# """_summary_
def sgalil_reference(self) -> None:
"""Reference all axes of the controller"""
if self.axis_is_referenced():
print("All axes are already referenced.\n")
return
# Make sure no axes are moving, is this necessary?
self.stop_all_axes()
self.socket_put_and_receive(f"XQ#FINDREF")
print("Referencing. Please wait, timeout after 100s...\n")
# Args:
# start_y (float): _description_
# end_y (float): _description_
# y_interval (int): _description_
# start_x (float): _description_
# end_x (float): _description_
# x_interval (int): _description_
# ctime (float): _description_
# readtime (float): _description_
# """
# #toDo Checking limits, checking logic for speed. SGALIL do 101 points when 100 are given
# # Check sign of motors, and offsets!
# speed = np.abs(end_y-start_y)/(y_interval*ctime+ (y_interval-1)*readtime)
# self.socket_put_and_receive(f"a_start={start_y:.04f};a_end={end_y:.04f};speed={speed:.04f}")
# step_grid = (end_x-start_x)/x_interval
# gridmax = (end_x-start_x)/step_grid +1
# self.socket_put_and_receive(f"b_start={start_x:.04f};gridmax={gridmax:.04f};step={step_grid:.04f}")
# self.socket_put_and_receive('XQ#SAMPLE')
# self.socket_put_and_receive('XQ#SCANG')
timeout = time.time() + 100
while not self.axis_is_referenced():
if time.time() > timeout:
print("Abort reference sequence, timeout reached\n")
break
time.sleep(0.5)
# @threadlocked
def fly_grid_scan(
self,
start_y: float,
end_y: float,
interval_y: int,
start_x: float,
end_x: float,
interval_x: int,
exp_time: float,
readtime: float,
) -> tuple:
"""_summary_
Args:
start_y (float): start position of y axis (fast axis)
end_y (float): end position of y axis (fast axis)
interval_y (int): number of points in y axis
start_x (float): start position of x axis (slow axis)
end_x (float): end position of x axis (slow axis)
interval_x (int): number of points in x axis
exp_time (float): exposure time in seconds
readtime (float): readout time in seconds, minimum of .5e-3s (0.5ms)
Raises:
LimitError: Raised if any position of motion is outside of the limits
LimitError: Raised if the speed is above 2mm/s or below 0.02mm/s
"""
# time.sleep(0.2)
# Check limits
# TODO check sign of stage, or not necessary
check_values = [start_y, end_y, start_x, end_x]
for val in check_values:
self.check_value(val)
speed = np.abs(end_y - start_y) / ((interval_y) * exp_time + (interval_y - 1) * readtime)
if speed > 2.00 or speed < 0.02:
raise LimitError(
f"Speed of {speed:.03f}mm/s is outside of acceptable range of 0.02 to 2 mm/s"
)
gridmax = int(interval_x - 1)
step_grid = (end_x - start_x) / interval_x
n_samples = int(interval_y * interval_x)
# Hard coded to maximum offset of 0.1mm to avoid long motions.
self.socket_put_and_receive(f"off={(0*0.1/2*1000):f}")
self.socket_put_and_receive(f"a_start={start_y:.04f};a_end={end_y:.04f};speed={speed:.04f}")
self.socket_put_and_receive(
f"b_start={start_x:.04f};gridmax={gridmax:d};b_step={step_grid:.04f}"
)
self.socket_put_and_receive(f"nums={n_samples}")
self.socket_put_and_receive("XQ#SAMPLE")
# sleep 50ms to avoid controller running into
time.sleep(0.1)
self.socket_put_and_receive("XQ#SCANG")
# time.sleep(0.1)
# threading.Thread(target=_while_in_motion(3, n_samples), daemon=True).start()
# self._while_in_motion(3, n_samples)
def _while_in_motion(self, thread_id: int, n_samples: int) -> tuple:
last_readout = 0
val_axis2 = [] # y axis
val_axis4 = [] # x axis
while self.is_thread_active(thread_id):
posct = int(self.socket_put_and_receive(f"MGposct").strip().split(".")[0])
logger.info(f"SGalil is scanning - latest enconder position {posct+1} from {n_samples}")
time.sleep(1)
if posct > last_readout:
positions = self.read_encoder_position(last_readout, posct)
val_axis4.extend(positions[0])
val_axis2.extend(positions[1])
last_readout = posct + 1
logger.info(len(val_axis2))
time.sleep(1)
# Readout of last positions after scan finished
posct = int(self.socket_put_and_receive(f"MGposct").strip().split(".")[0])
logger.info(f"SGalil is scanning - latest enconder position {posct} from {n_samples}")
if posct > last_readout:
positions = self.read_encoder_position(last_readout, posct)
val_axis4.extend(positions[0])
val_axis2.extend(positions[1])
return val_axis4, val_axis2
def read_encoder_position(self, fromval: int, toval: int) -> tuple:
val_axis2 = [] # y axis
val_axis4 = [] # x axis
for ii in range(fromval, toval + 1):
rts = self.socket_put_and_receive(f"MGaposavg[{ii%2000}]*10,cposavg[{ii%2000}]*10")
if rts == ":":
val_axis4.append(rts)
val_axis2.append(rts)
continue
val_axis4.append(float(rts.strip().split(" ")[0]) / 100000)
val_axis2.append(float(rts.strip().split(" ")[1]) / 100000)
return val_axis4, val_axis2
class GalilSignalBase(SocketSignal):
@ -311,21 +407,22 @@ class GalilSetpointSignal(GalilSignalBase):
"""
target_val = val * self.parent.sign
self.setpoint = target_val
axes_referenced = float(self.controller.socket_put_and_receive("MG allaxref"))
if axes_referenced:
while self.controller.is_thread_active(0):
time.sleep(0.1)
axes_referenced = self.controller.axis_is_referenced()
if not axes_referenced:
raise GalilError(
"Not all axes are referenced. Please use controller.sgalil_reference(). BE AWARE that axes start moving, potentially beyond limits, make sure full range of motion is safe"
)
while self.controller.is_thread_active(0):
time.sleep(0.1)
if self.parent.axis_Id_numeric == 2:
self.controller.socket_put_confirmed(f"PA{self.parent.axis_Id}={target_val:.4f}*mm")
self.controller.socket_put_and_receive(f"BG{self.parent.axis_Id}")
elif self.parent.axis_Id_numeric == 4:
self.controller.socket_put_confirmed(f"targ{self.parent.axis_Id}={target_val:.4f}")
self.controller.socket_put_and_receive(f"XQ#POSE,{self.parent.axis_Id_numeric}")
while self.controller.is_thread_active(0):
time.sleep(0.005)
else:
raise GalilError("Not all axes are referenced.")
if self.parent.axis_Id_numeric == 2:
self.controller.socket_put_confirmed(f"PA{self.parent.axis_Id}={target_val:.4f}*mm")
self.controller.socket_put_and_receive(f"BG{self.parent.axis_Id}")
elif self.parent.axis_Id_numeric == 4:
self.controller.socket_put_confirmed(f"targ{self.parent.axis_Id}={target_val:.4f}")
self.controller.socket_put_and_receive(f"XQ#POSE,{self.parent.axis_Id_numeric}")
while self.controller.is_thread_active(0):
time.sleep(0.005)
class GalilMotorIsMoving(GalilSignalRO):

View File

@ -0,0 +1,79 @@
# Documentation SGalil ophyd wrapper
Ophyd wrapper for the SGalil controller and stages.
## TODO tests and evaluate whether its good to combine common functionaltiy with galil lamni/omny/flomni controller
## Integration of the device in IPython kernel
BEC needs to be able to reach the host TCP to initiate a connection to the device.
```Python
from ophyd_devices.galil.sgalil_ophyd import SGalilMotor
samx = SGalilMotor("E", name="samx", host="129.129.122.26", port=23, sign=-1)
samy = SGalilMotor("C", name="samy", host="129.129.122.26", port=23, sign=-1)
# connect to the controller
samx.controller.on()
samx.read()
samx.move(5)
dir(samx)# for full printout of commands
# useful for development, check below socket communication with sgalil controller
samx.controller.socket_put_and_receive('#string: message_to_controller')
```
## TODO Integration of device in BEC device config!
to be tested too
## Fly scans
2D grid fly scan as implemented on the controller.
TTL triggers are sent for the start of each line.
The scan on the controller needs to be matched with an appropriate triggering scheme, as for instance shown in the attached scheme together with the Stanford Research DG645 device at cSAXS.
![image info](./csaxs_sgalil_triggering.png)
```Python
samx.controller.(start_y, end_y, interval_y, start_x, end_x, interval_x, exp_time, readtime)
# for example
samx.controller.fly_grid_scan(start_y= 16, end_y= 24, interval_y= 100, start_x= 18, end_x= 17.6, interval_x= 2, exp_time= 0.08, readtime= 0.005)
```
## TODO implement line scan
Check SPEC implementation for line scans with sgalil controller, and complement it with a suitable triggering scheme of the DG645.
## TODO readout of positions in encoder
Should this be integrated in the flyscan or not.
To be explored where this is most suitable.
## Socket communication with sgalil controller
### vertical axis (samy)
- initiate with axis 2, C
- in motion: "MG _BG{axis_char}", e.g. "MG _BGC" , 0 or 1
- limit switch not pressed: "MG _LR{axis_char}, _LF{axis_char}" , 0 or 1
- position: "MG _TP{axis_char}/mm" , position in mm
- Axis referenced: "MG allaxref", 0 or 1
- stop all axis: "XQ#STOP,1"
- is motor on: "MG _MO{axis_char}", 0 or 1
- is thread active: "MG _XQ{thread_id}", 0 or 1
**Specific for sgalil_y**
- set_motion_speed: "SP{axis_char}=2*mm", 2mm/s is max speed
- set_final_pos: "PA{axis_char}={val:04f}*mm", target pos in mm
- start motion: "BG{axis_char}", start motion
### horizontal axis (samx)
note: some hardware modifications were done that require access to different channels in the encoder. Encoder, motor and limit switches are not controlled by the same endpoint/axis of the controller... see below
- initiate with axis 4, E
**Specific for sgalil_x**
- set_final_pos: "targ{axis_char}={val:04f}", e.g. "targE=2.0000"
- start motion: "XQ#POSE,{axis_char}"
- For *in motion* and *limit switch not pressed* commands,
the key changes to AXIS 5 || F, e.g. "MG _BGF"
- For *position* switch to Axis 0 || A, e.g. "MG _TPA/mm"
### flyscan 2D grid commanes:
Last command ('XQ#SCANG') has to come with sufficient delay, important for setting up dedicated scans
f***ast axis***
- self.socket_put_and_receive(f'a_start={start_y:.04f};a_end={end_y:.04f};speed={speed:.04f}')
***slow axis***
- self.socket_put_and_receive(f'b_start={start_x:.04f};gridmax={gridmax:d};b_step={step_grid:.04f}')
- self.socket_put_and_receive(f'nums={n_samples}') # Declare number of triggers for encoder
- self.socket_put_and_receive('XQ#SAMPLE') # Reset encoder counting --> sampling starts with 0
Start scan (be aware, needs some waiting from before)
- self.socket_put_and_receive('XQ#SCANG')
### Encoder readings!
The encoder readout is triggered by an TTL pulse.
Unfortunately, TTL triggers to the encoder can only be accepted with at least 12.5ms time between rising/falling edges. Therefore, maximum readout has to be ~25Hz, rather 30Hz (experimentally determined).
Socket commands for the readout:
- self.socket_put_and_receive('MGsposct') # get current position counter
- self.socket_put_and_receive('MGaposavg[{ii%2000}]*10, cposavg[{ii%2000}]*10,') # loop over ii