Codestyle
This commit is contained in:
parent
c7867a910f
commit
3a7dd4e3cf
@ -4,11 +4,11 @@ from ophyd import PVPositioner, Component, EpicsSignal, EpicsSignalRO, Kind
|
||||
class InsertionDevice(PVPositioner):
|
||||
"""Python wrapper for the CSAXS insertion device control
|
||||
|
||||
This wrapper provides a positioner interface for the ID control.
|
||||
is completely custom XBPM with templates directly in the
|
||||
VME repo. Thus it needs a custom ophyd template as well...
|
||||
This wrapper provides a positioner interface for the ID control.
|
||||
is completely custom XBPM with templates directly in the
|
||||
VME repo. Thus it needs a custom ophyd template as well...
|
||||
|
||||
WARN: The x and y are not updated by the IOC
|
||||
WARN: The x and y are not updated by the IOC
|
||||
"""
|
||||
status = Component(EpicsSignalRO, "-USER:STATUS", auto_monitor=True)
|
||||
errorSource = Component(EpicsSignalRO, "-USER:ERROR-SOURCE", auto_monitor=True)
|
||||
@ -23,4 +23,4 @@ class InsertionDevice(PVPositioner):
|
||||
# Automatically start simulation if directly invoked
|
||||
# (NA for important devices)
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
pass
|
||||
|
@ -6,12 +6,12 @@ import matplotlib.pyplot as plt
|
||||
class SpmBase(Device):
|
||||
"""Python wrapper for the Staggered Blade Pair Monitors
|
||||
|
||||
SPM's consist of a set of four horizontal tungsten blades and are
|
||||
used to monitor the beam height (only Y) for the bending magnet
|
||||
beamlines of SLS.
|
||||
SPM's consist of a set of four horizontal tungsten blades and are
|
||||
used to monitor the beam height (only Y) for the bending magnet
|
||||
beamlines of SLS.
|
||||
|
||||
Note: EPICS provided signals are read only, but the user can
|
||||
change the beam position offset.
|
||||
Note: EPICS provided signals are read only, but the user can
|
||||
change the beam position offset.
|
||||
"""
|
||||
# Motor interface
|
||||
s1 = Component(EpicsSignalRO, "Current1", auto_monitor=True)
|
||||
@ -27,12 +27,12 @@ class SpmBase(Device):
|
||||
class SpmSim(SpmBase):
|
||||
"""Python wrapper for simulated Staggered Blade Pair Monitors
|
||||
|
||||
SPM's consist of a set of four horizontal tungsten blades and are
|
||||
used to monitor the beam height (only Y) for the bending magnet
|
||||
beamlines of SLS.
|
||||
SPM's consist of a set of four horizontal tungsten blades and are
|
||||
used to monitor the beam height (only Y) for the bending magnet
|
||||
beamlines of SLS.
|
||||
|
||||
This simulation device extends the basic proxy with a script that
|
||||
fills signals with quasi-randomized values.
|
||||
This simulation device extends the basic proxy with a script that
|
||||
fills signals with quasi-randomized values.
|
||||
"""
|
||||
# Motor interface
|
||||
s1w = Component(EpicsSignal, "Current1:RAW.VAL", auto_monitor=False)
|
||||
@ -53,13 +53,13 @@ class SpmSim(SpmBase):
|
||||
|
||||
def _simFrame(self):
|
||||
"""Generator to simulate a jumping gaussian"""
|
||||
# define normalized 2D gaussian
|
||||
# Define normalized 2D gaussian
|
||||
def gaus2d(x=0, y=0, mx=0, my=0, sx=1, sy=1):
|
||||
return np.exp(-((x - mx)**2. / (2. * sx**2.) + (y - my)**2. / (2. * sy**2.)))
|
||||
|
||||
#Generator for dynamic values
|
||||
self._MX = 0.75 * self._MX + 0.25 * (10.0 * np.random.random()-5.0)
|
||||
self._MY = 0.75 * self._MY + 0.25 * (10.0 * np.random.random()-5.0)
|
||||
# Generator for dynamic values
|
||||
self._MX = 0.75 * self._MX + 0.25 * (10.0 * np.random.random() - 5.0)
|
||||
self._MY = 0.75 * self._MY + 0.25 * (10.0 * np.random.random() - 5.0)
|
||||
self._I0 = 0.75 * self._I0 + 0.25 * (255.0 * np.random.random())
|
||||
|
||||
arr = self._I0 * gaus2d(self._x, self._y, self._MX, self._MY)
|
||||
@ -68,12 +68,12 @@ class SpmSim(SpmBase):
|
||||
def sim(self):
|
||||
# Get next frame
|
||||
beam = self._simFrame()
|
||||
total = np.sum(beam) - np.sum(beam[24:48,:])
|
||||
rnge = np.floor(np.log10(total) - 0.0 )
|
||||
s1 = np.sum(beam[0:16,:]) / 10**rnge
|
||||
s2 = np.sum(beam[16:24,:]) / 10**rnge
|
||||
s3 = np.sum(beam[40:48,:]) / 10**rnge
|
||||
s4 = np.sum(beam[48:64,:]) / 10**rnge
|
||||
total = np.sum(beam) - np.sum(beam[24:48, :])
|
||||
rnge = np.floor(np.log10(total) - 0.0)
|
||||
s1 = np.sum(beam[0:16, :]) / 10**rnge
|
||||
s2 = np.sum(beam[16:24, :]) / 10**rnge
|
||||
s3 = np.sum(beam[40:48, :]) / 10**rnge
|
||||
s4 = np.sum(beam[48:64, :]) / 10**rnge
|
||||
|
||||
self.s1w.set(s1).wait()
|
||||
self.s2w.set(s2).wait()
|
||||
@ -82,23 +82,23 @@ class SpmSim(SpmBase):
|
||||
self.rangew.set(rnge).wait()
|
||||
# Print debug info
|
||||
print(f"Raw signals: R={rnge}\t{s1}\t{s2}\t{s3}\t{s4}")
|
||||
#plt.imshow(beam)
|
||||
#plt.show(block=False)
|
||||
# plt.imshow(beam)
|
||||
# plt.show(block=False)
|
||||
plt.pause(0.5)
|
||||
|
||||
|
||||
# Automatically start simulation if directly invoked
|
||||
if __name__ == "__main__":
|
||||
spm1 = SpmSim("X06D-FE-BM1:", name="spm1")
|
||||
spm2 = SpmSim("X06D-FE-BM2:", name="spm2")
|
||||
spm1 = SpmSim("X06D-FE-BM1:", name="spm1")
|
||||
spm2 = SpmSim("X06D-FE-BM2:", name="spm2")
|
||||
|
||||
spm1.wait_for_connection(timeout=5)
|
||||
spm2.wait_for_connection(timeout=5)
|
||||
spm1.wait_for_connection(timeout=5)
|
||||
spm2.wait_for_connection(timeout=5)
|
||||
|
||||
spm1.rangew.set(1).wait()
|
||||
spm2.rangew.set(1).wait()
|
||||
spm1.rangew.set(1).wait()
|
||||
spm2.rangew.set(1).wait()
|
||||
|
||||
while True:
|
||||
print("---")
|
||||
spm1.sim()
|
||||
spm2.sim()
|
||||
while True:
|
||||
print("---")
|
||||
spm1.sim()
|
||||
spm2.sim()
|
||||
|
@ -6,10 +6,10 @@ import matplotlib.pyplot as plt
|
||||
class XbpmCsaxsOp(Device):
|
||||
"""Python wrapper for custom XBPMs in the cSAXS optics hutch
|
||||
|
||||
This is completely custom XBPM with templates directly in the
|
||||
VME repo. Thus it needs a custom ophyd template as well...
|
||||
This is completely custom XBPM with templates directly in the
|
||||
VME repo. Thus it needs a custom ophyd template as well...
|
||||
|
||||
WARN: The x and y are not updated by the IOC
|
||||
WARN: The x and y are not updated by the IOC
|
||||
"""
|
||||
sum = Component(EpicsSignalRO, "SUM", auto_monitor=True)
|
||||
x = Component(EpicsSignalRO, "POSH", auto_monitor=True)
|
||||
@ -23,14 +23,14 @@ class XbpmCsaxsOp(Device):
|
||||
class XbpmBase(Device):
|
||||
"""Python wrapper for X-ray Beam Position Monitors
|
||||
|
||||
XBPM's consist of a metal-coated diamond window that ejects
|
||||
photoelectrons from the incoming X-ray beam. These electons
|
||||
are collected and their current is measured. Effectively
|
||||
they act as four quadrant photodiodes and are used as BPMs
|
||||
at the undulator beamlines of SLS.
|
||||
XBPM's consist of a metal-coated diamond window that ejects
|
||||
photoelectrons from the incoming X-ray beam. These electons
|
||||
are collected and their current is measured. Effectively
|
||||
they act as four quadrant photodiodes and are used as BPMs
|
||||
at the undulator beamlines of SLS.
|
||||
|
||||
Note: EPICS provided signals are read only, but the user can
|
||||
change the beam position offset.
|
||||
Note: EPICS provided signals are read only, but the user can
|
||||
change the beam position offset.
|
||||
"""
|
||||
# Motor interface
|
||||
s1 = Component(EpicsSignalRO, "Current1", auto_monitor=True)
|
||||
@ -51,17 +51,17 @@ class XbpmBase(Device):
|
||||
class XbpmSim(XbpmBase):
|
||||
"""Python wrapper for simulated X-ray Beam Position Monitors
|
||||
|
||||
XBPM's consist of a metal-coated diamond window that ejects
|
||||
photoelectrons from the incoming X-ray beam. These electons
|
||||
are collected and their current is measured. Effectively
|
||||
they act as four quadrant photodiodes and are used as BPMs
|
||||
at the undulator beamlines of SLS.
|
||||
XBPM's consist of a metal-coated diamond window that ejects
|
||||
photoelectrons from the incoming X-ray beam. These electons
|
||||
are collected and their current is measured. Effectively
|
||||
they act as four quadrant photodiodes and are used as BPMs
|
||||
at the undulator beamlines of SLS.
|
||||
|
||||
Note: EPICS provided signals are read only, but the user can
|
||||
Note: EPICS provided signals are read only, but the user can
|
||||
change the beam position offset.
|
||||
|
||||
This simulation device extends the basic proxy with a script that
|
||||
fills signals with quasi-randomized values.
|
||||
This simulation device extends the basic proxy with a script that
|
||||
fills signals with quasi-randomized values.
|
||||
"""
|
||||
# Motor interface
|
||||
s1w = Component(EpicsSignal, "Current1:RAW.VAL", auto_monitor=False)
|
||||
@ -118,16 +118,16 @@ class XbpmSim(XbpmBase):
|
||||
|
||||
# Automatically start simulation if directly invoked
|
||||
if __name__ == "__main__":
|
||||
xbpm1 = XbpmSim("X01DA-FE-XBPM1:", name="xbpm1")
|
||||
xbpm2 = XbpmSim("X01DA-FE-XBPM2:", name="xbpm2")
|
||||
xbpm1 = XbpmSim("X01DA-FE-XBPM1:", name="xbpm1")
|
||||
xbpm2 = XbpmSim("X01DA-FE-XBPM2:", name="xbpm2")
|
||||
|
||||
xbpm1.wait_for_connection(timeout=5)
|
||||
xbpm2.wait_for_connection(timeout=5)
|
||||
xbpm1.wait_for_connection(timeout=5)
|
||||
xbpm2.wait_for_connection(timeout=5)
|
||||
|
||||
xbpm1.rangew.set(1).wait()
|
||||
xbpm2.rangew.set(1).wait()
|
||||
xbpm1.rangew.set(1).wait()
|
||||
xbpm2.rangew.set(1).wait()
|
||||
|
||||
while True:
|
||||
print("---")
|
||||
xbpm1.sim()
|
||||
xbpm2.sim()
|
||||
while True:
|
||||
print("---")
|
||||
xbpm1.sim()
|
||||
xbpm2.sim()
|
||||
|
@ -9,14 +9,15 @@ IMPORTANT: Virtual monochromator axes should be implemented already in EPICS!!!
|
||||
|
||||
import numpy as np
|
||||
from math import isclose
|
||||
from ophyd import EpicsSignal, EpicsSignalRO, EpicsMotor, PseudoPositioner, PseudoSingle, Device, Component, Kind
|
||||
from ophyd import (EpicsSignal, EpicsSignalRO, EpicsMotor, PseudoPositioner,
|
||||
PseudoSingle, Device, Component, Kind)
|
||||
from ophyd.pseudopos import pseudo_position_argument, real_position_argument
|
||||
from ophyd.sim import SynAxis, Syn2DGauss
|
||||
|
||||
LN_CORR = 2e-4
|
||||
|
||||
|
||||
def a2e(angle, hkl=[1,1,1], lnc=False, bent=False, deg=False):
|
||||
def a2e(angle, hkl=[1, 1, 1], lnc=False, bent=False, deg=False):
|
||||
"""Convert between angle and energy for Si monchromators
|
||||
ATTENTION: 'angle' must be in radians, not degrees!
|
||||
"""
|
||||
@ -24,7 +25,7 @@ def a2e(angle, hkl=[1,1,1], lnc=False, bent=False, deg=False):
|
||||
angle = angle*np.pi/180 if deg else angle
|
||||
|
||||
# Lattice constant along direction
|
||||
d0 = 5.43102 * (1.0-lncorr) / np.linalg.norm(hkl)
|
||||
d0 = 5.43102 * (1.0 - lncorr) / np.linalg.norm(hkl)
|
||||
energy = 12.39842 / (2.0 * d0 * np.sin(angle))
|
||||
return energy
|
||||
|
||||
@ -41,22 +42,22 @@ def w2e(wwl):
|
||||
return 12398.42 * 0.1 / wwl
|
||||
|
||||
|
||||
def e2a(energy, hkl=[1,1,1], lnc=False, bent=False):
|
||||
def e2a(energy, hkl=[1, 1, 1], lnc=False, bent=False):
|
||||
"""Convert between energy and angle for Si monchromators
|
||||
ATTENTION: 'angle' must be in radians, not degrees!
|
||||
"""
|
||||
lncorr = LN_CORR if lnc else 0.0
|
||||
|
||||
# Lattice constant along direction
|
||||
d0 = 2*5.43102 * (1.0-lncorr) / np.linalg.norm(hkl)
|
||||
angle = np.arcsin(12.39842/d0/energy)
|
||||
d0 = 2 * 5.43102 * (1.0 - lncorr) / np.linalg.norm(hkl)
|
||||
angle = np.arcsin(12.39842 / d0 / energy)
|
||||
|
||||
# Rfine for bent mirror
|
||||
if bent:
|
||||
rho = 2 * 19.65 * 8.35 / 28 * np.sin(angle)
|
||||
dt = 0.2e-3 / rho * 0.279
|
||||
d0 = 2 * 5.43102 * (1.0+dt) / np.linalg.norm(hkl)
|
||||
angle = np.arcsin(12.39842/d0/energy)
|
||||
d0 = 2 * 5.43102 * (1.0 + dt) / np.linalg.norm(hkl)
|
||||
angle = np.arcsin(12.39842 / d0 / energy)
|
||||
|
||||
return angle
|
||||
|
||||
@ -64,8 +65,8 @@ def e2a(energy, hkl=[1,1,1], lnc=False, bent=False):
|
||||
class MonoMotor(PseudoPositioner):
|
||||
"""Monochromator axis
|
||||
|
||||
Small wrapper to combine a real angular axis with the corresponding energy.
|
||||
ATTENTION: 'angle' is in degrees, at least for PXIII
|
||||
Small wrapper to combine a real angular axis with the corresponding energy.
|
||||
ATTENTION: 'angle' is in degrees, at least for PXIII
|
||||
"""
|
||||
# Real axis (in degrees)
|
||||
angle = Component(EpicsMotor, "", name='angle')
|
||||
@ -86,10 +87,10 @@ class MonoMotor(PseudoPositioner):
|
||||
class MonoDccm(PseudoPositioner):
|
||||
"""Combined DCCM monochromator
|
||||
|
||||
The first crystal selects the energy, the second one is only following.
|
||||
DCCMs are quite simple in terms that they can't crash and we don't
|
||||
have a beam offset.
|
||||
ATTENTION: 'angle' is in degrees, at least for PXIII
|
||||
The first crystal selects the energy, the second one is only following.
|
||||
DCCMs are quite simple in terms that they can't crash and we don't
|
||||
have a beam offset.
|
||||
ATTENTION: 'angle' is in degrees, at least for PXIII
|
||||
"""
|
||||
|
||||
# Real axis (in degrees)
|
||||
@ -102,22 +103,30 @@ class MonoDccm(PseudoPositioner):
|
||||
energy = Component(PseudoSingle, name='energy', kind=Kind.hinted)
|
||||
|
||||
# Other parameters
|
||||
#feedback = Component(EpicsSignal, "MONOBEAM", name="feedback")
|
||||
#enc1 = Component(EpicsSignalRO, "1:EXC1", name="enc1")
|
||||
#enc2 = Component(EpicsSignalRO, "1:EXC2", name="enc2")
|
||||
# feedback = Component(EpicsSignal, "MONOBEAM", name="feedback")
|
||||
# enc1 = Component(EpicsSignalRO, "1:EXC1", name="enc1")
|
||||
# enc2 = Component(EpicsSignalRO, "1:EXC2", name="enc2")
|
||||
|
||||
@pseudo_position_argument
|
||||
def forward(self, pseudo_pos):
|
||||
"""WARNING: We have an overdefined system! Not sure if common crystal movement is reliable without retuning"""
|
||||
if abs(pseudo_pos.energy-self.energy.position) > 0.0001 and abs(pseudo_pos.en1-self.en1.position) < 0.0001 and abs(pseudo_pos.en2-self.en2.position) < 0.0001:
|
||||
# Probably the common energy was changed
|
||||
return self.RealPosition(th1=-180.0*e2a(pseudo_pos.energy)/3.141592, th2=180.0*e2a(pseudo_pos.energy)/3.141592)
|
||||
return self.RealPosition(
|
||||
th1=-180.0 * e2a(pseudo_pos.energy) / 3.141592,
|
||||
th2=180.0 * e2a(pseudo_pos.energy) / 3.141592
|
||||
)
|
||||
else:
|
||||
# Probably the individual axes was changes
|
||||
return self.RealPosition(th1=-180.0*e2a(pseudo_pos.en1)/3.141592, th2=180.0*e2a(pseudo_pos.en2)/3.141592)
|
||||
return self.RealPosition(
|
||||
th1=-180.0 * e2a(pseudo_pos.en1 / 3.141592,
|
||||
th2=180.0 * e2a(pseudo_pos.en2) / 3.141592
|
||||
)
|
||||
|
||||
@real_position_argument
|
||||
def inverse(self, real_pos):
|
||||
return self.PseudoPosition(en1=-a2e(3.141592*real_pos.th1/180.0),
|
||||
en2=a2e(3.141592*real_pos.th2/180.0),
|
||||
energy=-a2e(3.141592*real_pos.th1/180.0))
|
||||
return self.PseudoPosition(
|
||||
en1=-a2e(3.141592 * real_pos.th1 / 180.0),
|
||||
en2=a2e(3.141592 * real_pos.th2 / 180.0),
|
||||
energy=-a2e(3.141592 * real_pos.th1 / 180.0)
|
||||
)
|
||||
|
@ -1,15 +1,15 @@
|
||||
from ophyd import Device, Component, EpicsMotor, PseudoPositioner, PseudoSingle
|
||||
from ophyd.pseudopos import pseudo_position_argument,real_position_argument
|
||||
from ophyd.pseudopos import pseudo_position_argument, real_position_argument
|
||||
|
||||
|
||||
class SlitH(PseudoPositioner):
|
||||
"""Python wrapper for virtual slits
|
||||
|
||||
These devices should be implemented as an EPICS SoftMotor IOC,
|
||||
but thats not the case for all slits. So here is a pure ophyd
|
||||
implementation. Uses standard naming convention!
|
||||
These devices should be implemented as an EPICS SoftMotor IOC,
|
||||
but thats not the case for all slits. So here is a pure ophyd
|
||||
implementation. Uses standard naming convention!
|
||||
|
||||
NOTE: The real and virtual axes are wrapped together.
|
||||
NOTE: The real and virtual axes are wrapped together.
|
||||
"""
|
||||
# Motor interface
|
||||
x1 = Component(EpicsMotor, "TRX1")
|
||||
@ -20,13 +20,13 @@ class SlitH(PseudoPositioner):
|
||||
|
||||
@pseudo_position_argument
|
||||
def forward(self, pseudo_pos):
|
||||
'''Run a forward (pseudo -> real) calculation'''
|
||||
"""Run a forward (pseudo -> real) calculation"""
|
||||
return self.RealPosition(x1=pseudo_pos.cenx-pseudo_pos.gapx/2,
|
||||
x2=pseudo_pos.cenx+pseudo_pos.gapx/2)
|
||||
|
||||
@real_position_argument
|
||||
def inverse(self, real_pos):
|
||||
'''Run an inverse (real -> pseudo) calculation'''
|
||||
"""Run an inverse (real -> pseudo) calculation"""
|
||||
return self.PseudoPosition(cenx=(real_pos.x1+real_pos.x2)/2,
|
||||
gapx=real_pos.x2-real_pos.x1)
|
||||
|
||||
@ -34,11 +34,11 @@ class SlitH(PseudoPositioner):
|
||||
class SlitV(PseudoPositioner):
|
||||
"""Python wrapper for virtual slits
|
||||
|
||||
These devices should be implemented as an EPICS SoftMotor IOC,
|
||||
but thats not the case for all slits. So here is a pure ophyd
|
||||
implementation. Uses standard naming convention!
|
||||
These devices should be implemented as an EPICS SoftMotor IOC,
|
||||
but thats not the case for all slits. So here is a pure ophyd
|
||||
implementation. Uses standard naming convention!
|
||||
|
||||
NOTE: The real and virtual axes are wrapped together.
|
||||
NOTE: The real and virtual axes are wrapped together.
|
||||
"""
|
||||
# Motor interface
|
||||
y1 = Component(EpicsMotor, "TRY1")
|
||||
|
Loading…
x
Reference in New Issue
Block a user