383 lines
13 KiB
Python
Executable File
383 lines
13 KiB
Python
Executable File
###
|
|
# Copyright 2008-2011 Diamond Light Source Ltd.
|
|
# This file is part of Diffcalc.
|
|
#
|
|
# Diffcalc is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# Diffcalc is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with Diffcalc. If not, see <http://www.gnu.org/licenses/>.
|
|
###
|
|
|
|
from __future__ import absolute_import
|
|
|
|
from diffcalc.util import DiffcalcException
|
|
from diffcalc import settings
|
|
|
|
SMALL = 1e-8
|
|
|
|
from diffcalc.util import command
|
|
|
|
__all__ = ['hardware', 'setcut', 'setmin', 'setmax']
|
|
|
|
|
|
def getNameFromScannableOrString(o):
|
|
try: # it may be a scannable
|
|
return o.getName()
|
|
except AttributeError:
|
|
return str(o)
|
|
|
|
|
|
|
|
@command
|
|
def hardware():
|
|
"""hardware -- show diffcalc limits and cuts"""
|
|
print settings.hardware.repr_sector_limits_and_cuts() # @UndefinedVariable
|
|
|
|
@command
|
|
def setcut(scannable_or_string=None, val=None):
|
|
"""setcut {name {val}} -- sets cut angle
|
|
"""
|
|
if scannable_or_string is None and val is None:
|
|
print settings.hardware.repr_sector_limits_and_cuts() # @UndefinedVariable
|
|
else:
|
|
name = getNameFromScannableOrString(scannable_or_string)
|
|
if val is None:
|
|
print '%s: %f' % (name, settings.hardware.get_cuts()[name]) # @UndefinedVariable
|
|
else:
|
|
oldcut = settings.hardware.get_cuts()[name] # @UndefinedVariable
|
|
settings.hardware.set_cut(name, float(val)) # @UndefinedVariable
|
|
newcut = settings.hardware.get_cuts()[name] # @UndefinedVariable
|
|
|
|
@command
|
|
def setmin(name=None, val=None):
|
|
"""setmin {axis {val}} -- set lower limits used by auto sector code (None to clear)""" #@IgnorePep8
|
|
_setMinOrMax(name, val, settings.hardware.set_lower_limit) # @UndefinedVariable
|
|
|
|
@command
|
|
def setmax(name=None, val=None):
|
|
"""setmax {name {val}} -- sets upper limits used by auto sector code (None to clear)""" #@IgnorePep8
|
|
_setMinOrMax(name, val, settings.hardware.set_upper_limit) # @UndefinedVariable
|
|
|
|
@command
|
|
def setrange(name=None, lower=None, upper=None):
|
|
"""setrange {axis {min} {max}} -- set lower and upper limits used by auto sector code (None to clear)""" #@IgnorePep8
|
|
_setMinOrMax(name, lower, settings.hardware.set_lower_limit) # @UndefinedVariable
|
|
_setMinOrMax(name, upper, settings.hardware.set_upper_limit) # @UndefinedVariable
|
|
|
|
def _setMinOrMax(name, val, setMethod):
|
|
if name is None:
|
|
print settings.hardware.repr_sector_limits_and_cuts() # @UndefinedVariable
|
|
else:
|
|
name = getNameFromScannableOrString(name)
|
|
if val is None:
|
|
print settings.hardware.repr_sector_limits_and_cuts(name) # @UndefinedVariable
|
|
else:
|
|
setMethod(name, float(val))
|
|
|
|
|
|
commands_for_help = ['Hardware',
|
|
hardware,
|
|
setcut,
|
|
setmin,
|
|
setmax]
|
|
|
|
|
|
class HardwareAdapter(object):
|
|
|
|
def __init__(self, diffractometerAngleNames, defaultCuts={},
|
|
energyScannableMultiplierToGetKeV=1):
|
|
|
|
self._diffractometerAngleNames = diffractometerAngleNames
|
|
self._upperLimitDict = {}
|
|
self._lowerLimitDict = {}
|
|
self._cut_angles = {}
|
|
self._configure_cuts(defaultCuts)
|
|
self.energyScannableMultiplierToGetKeV = \
|
|
energyScannableMultiplierToGetKeV
|
|
self._name = 'base'
|
|
|
|
@property
|
|
def name(self):
|
|
return self._name
|
|
|
|
def get_axes_names(self):
|
|
return tuple(self._diffractometerAngleNames)
|
|
|
|
def get_position(self):
|
|
"""pos = get_position() -- returns the current physical diffractometer
|
|
position as a diffcalc.util object in degrees
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def get_wavelength(self):
|
|
"""wavelength = get_wavelength() -- returns wavelength in Angstroms
|
|
"""
|
|
return 12.39842 / self.get_energy()
|
|
|
|
def get_energy(self):
|
|
"""energy = get_energy() -- returns energy in kEv """
|
|
raise NotImplementedError()
|
|
|
|
def __str__(self):
|
|
s = self.name + ":\n"
|
|
s += " energy : " + str(self.get_energy()) + " keV\n"
|
|
s += " wavelength : " + str(self.get_wavelength()) + " Angstrom\n"
|
|
names = self._diffractometerAngleNames
|
|
for name, pos in zip(names, self.get_position()):
|
|
s += " %s : %r deg\n" % (name, pos)
|
|
return s
|
|
|
|
def __repr__(self):
|
|
return self.__str__()
|
|
|
|
def get_position_by_name(self, angleName):
|
|
names = list(self._diffractometerAngleNames)
|
|
return self.get_position()[names.index(angleName)]
|
|
|
|
### Limits ###
|
|
|
|
def get_lower_limit(self, name):
|
|
'''returns lower limits by axis name. Limit may be None if not set
|
|
'''
|
|
if name not in self._diffractometerAngleNames:
|
|
raise ValueError("No angle called %s. Try one of: %s" %
|
|
(name, self._diffractometerAngleNames))
|
|
return self._lowerLimitDict.get(name)
|
|
|
|
def get_upper_limit(self, name):
|
|
'''returns upper limit by axis name. Limit may be None if not set
|
|
'''
|
|
if name not in self._diffractometerAngleNames:
|
|
raise ValueError("No angle called %s. Try one of: %s" %
|
|
name, self._diffractometerAngleNames)
|
|
return self._upperLimitDict.get(name)
|
|
|
|
def set_lower_limit(self, name, value):
|
|
"""value may be None to remove limit"""
|
|
if name not in self._diffractometerAngleNames:
|
|
raise ValueError(
|
|
"Cannot set lower Diffcalc limit: No angle called %s. Try one "
|
|
"of: %s" % (name, self._diffractometerAngleNames))
|
|
if value is None:
|
|
try:
|
|
del self._lowerLimitDict[name]
|
|
except KeyError:
|
|
print ("WARNING: There was no lower Diffcalc limit %s set to "
|
|
"clear" % name)
|
|
else:
|
|
self._lowerLimitDict[name] = value
|
|
|
|
def set_upper_limit(self, name, value):
|
|
"""value may be None to remove limit"""
|
|
if name not in self._diffractometerAngleNames:
|
|
raise ValueError(
|
|
"Cannot set upper Diffcalc limit: No angle called %s. Try one "
|
|
"of: %s" % (name, self._diffractometerAngleNames))
|
|
if value is None:
|
|
try:
|
|
del self._upperLimitDict[name]
|
|
except KeyError:
|
|
print ("WARNING: There was no upper Diffcalc limit %s set to "
|
|
"clear" % name)
|
|
else:
|
|
self._upperLimitDict[name] = value
|
|
|
|
def is_position_within_limits(self, positionArray):
|
|
"""
|
|
where position array is in degrees and cut to be between -180 and 180
|
|
"""
|
|
names = self._diffractometerAngleNames
|
|
for axis_name, value in zip(names, positionArray):
|
|
if not self.is_axis_value_within_limits(axis_name, value):
|
|
return False
|
|
return True
|
|
|
|
def is_axis_value_within_limits(self, axis_name, value):
|
|
if axis_name in self._upperLimitDict:
|
|
if value > self._upperLimitDict[axis_name]:
|
|
return False
|
|
if axis_name in self._lowerLimitDict:
|
|
if value < self._lowerLimitDict[axis_name]:
|
|
return False
|
|
return True
|
|
|
|
def repr_sector_limits_and_cuts(self, name=None):
|
|
if name is None:
|
|
s = ''
|
|
for name in self.get_axes_names():
|
|
s += self.repr_sector_limits_and_cuts(name) + '\n'
|
|
s += "Note: When auto sector/transforms are used,\n "
|
|
s += " cuts are applied before checking limits."
|
|
return s
|
|
# limits:
|
|
low = self.get_lower_limit(name)
|
|
high = self.get_upper_limit(name)
|
|
s = ' '
|
|
if low is not None:
|
|
s += "% 6.1f <= " % low
|
|
else:
|
|
s += ' ' * 10
|
|
s += '%5s' % name
|
|
if high is not None:
|
|
s += " <= % 6.1f" % high
|
|
else:
|
|
s += ' ' * 10
|
|
# cuts:
|
|
try:
|
|
if self.get_cuts()[name] is not None:
|
|
s += " (cut: % 6.1f)" % self.get_cuts()[name]
|
|
except KeyError:
|
|
pass
|
|
|
|
return s
|
|
|
|
### Cutting Stuff ###
|
|
|
|
def _configure_cuts(self, defaultCutsDict):
|
|
# 1. Set default cut angles
|
|
self._cut_angles = dict.fromkeys(self._diffractometerAngleNames, -180.)
|
|
if 'phi' in self._cut_angles:
|
|
self._cut_angles['phi'] = 0.
|
|
# 2. Overide with user-specified cuts
|
|
for name, val in defaultCutsDict.iteritems():
|
|
self.set_cut(name, val)
|
|
|
|
def set_cut(self, name, value):
|
|
if name in self._cut_angles:
|
|
self._cut_angles[name] = value
|
|
else:
|
|
raise KeyError("Diffractometer has no angle %s. Try: %s." %
|
|
(name, self._diffractometerAngleNames))
|
|
|
|
def get_cuts(self):
|
|
return self._cut_angles
|
|
|
|
def cut_angles(self, positionArray):
|
|
'''Assumes each angle in positionArray is between -360 and 360
|
|
'''
|
|
cutArray = []
|
|
names = self._diffractometerAngleNames
|
|
for axis_name, value in zip(names, positionArray):
|
|
cutArray.append(self.cut_angle(axis_name, value))
|
|
return tuple(cutArray)
|
|
|
|
def cut_angle(self, axis_name, value):
|
|
cut_angle = self._cut_angles[axis_name]
|
|
if cut_angle is None:
|
|
return value
|
|
return cut_angle_at(cut_angle, value)
|
|
|
|
|
|
def cut_angle_at(cut_angle, value):
|
|
if (cut_angle == 0 and (abs(value - 360) < SMALL) or
|
|
(abs(value + 360) < SMALL) or
|
|
(abs(value) < SMALL)):
|
|
value = 0.
|
|
if value < (cut_angle - SMALL):
|
|
return value + 360.
|
|
elif value >= cut_angle + 360. + SMALL:
|
|
return value - 360.
|
|
else:
|
|
return value
|
|
|
|
|
|
class DummyHardwareAdapter(HardwareAdapter):
|
|
|
|
def __init__(self, diffractometerAngleNames):
|
|
super(self.__class__, self).__init__(diffractometerAngleNames)
|
|
# HardwareAdapter.__init__(self, diffractometerAngleNames)
|
|
|
|
self._position = [0.] * len(diffractometerAngleNames)
|
|
self._wavelength = 1.
|
|
self.energyScannableMultiplierToGetKeV = 1
|
|
self._name = "Dummy"
|
|
|
|
# Required methods
|
|
|
|
def get_position(self):
|
|
"""
|
|
pos = getDiffractometerPosition() -- returns the current physical
|
|
diffractometer position as a list in degrees
|
|
"""
|
|
return self._position
|
|
|
|
def _set_position(self, pos):
|
|
assert len(pos) == len(self.get_axes_names()), \
|
|
"Wrong length of input list"
|
|
self._position = pos
|
|
|
|
position = property(get_position, _set_position)
|
|
|
|
def get_energy(self):
|
|
"""energy = get_energy() -- returns energy in kEv """
|
|
if self._wavelength is None:
|
|
raise DiffcalcException(
|
|
"Energy or wavelength have not been set")
|
|
return (12.39842 /
|
|
(self._wavelength * self.energyScannableMultiplierToGetKeV))
|
|
|
|
def _set_energy(self, energy):
|
|
self._wavelength = 12.39842 / energy
|
|
|
|
energy = property(get_energy, _set_energy)
|
|
|
|
def get_wavelength(self):
|
|
"""wavelength = get_wavelength() -- returns wavelength in Angstroms"""
|
|
if self._wavelength is None:
|
|
raise DiffcalcException(
|
|
"Energy or wavelength have not been set")
|
|
return self._wavelength
|
|
|
|
def _set_wavelength(self, wavelength):
|
|
self._wavelength = wavelength
|
|
|
|
wavelength = property(get_wavelength, _set_wavelength)
|
|
|
|
|
|
class ScannableHardwareAdapter(HardwareAdapter):
|
|
|
|
def __init__(self, diffractometerScannable, energyScannable,
|
|
energyScannableMultiplierToGetKeV=1):
|
|
input_names = diffractometerScannable.getInputNames()
|
|
super(self.__class__, self).__init__(input_names)
|
|
# HardwareAdapter.__init__(self, input_names)
|
|
self.diffhw = diffractometerScannable
|
|
self.energyhw = energyScannable
|
|
self.energyScannableMultiplierToGetKeV = \
|
|
energyScannableMultiplierToGetKeV
|
|
self._name = "ScannableHarwdareMonitor"
|
|
|
|
# Required methods
|
|
|
|
def get_position(self):
|
|
"""
|
|
pos = getDiffractometerPosition() -- returns the current physical
|
|
diffractometer position as a list in degrees
|
|
"""
|
|
return self.diffhw.getPosition()
|
|
|
|
def get_energy(self):
|
|
"""energy = get_energy() -- returns energy in kEv (NOT eV!) """
|
|
multiplier = self.energyScannableMultiplierToGetKeV
|
|
energy = self.energyhw.getPosition() * multiplier
|
|
if energy is None:
|
|
raise DiffcalcException("Energy has not been set")
|
|
return energy
|
|
|
|
def get_wavelength(self):
|
|
"""wavelength = get_wavelength() -- returns wavelength in Angstroms"""
|
|
energy = self.get_energy()
|
|
return 12.39842 / energy
|
|
|
|
@property
|
|
def name(self):
|
|
return self.diffhw.getName()
|