Goniometer control for Marco Braun Probehead (AXYs) added
This commit is contained in:
parent
9202ad2297
commit
9f4a3d6b53
125
src/tools/goniometer.py
Normal file
125
src/tools/goniometer.py
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
__author__ = 'Markus Rosenstihl <markus.rosenstihl@physik.tu-darmstadt.de>'
|
||||||
|
|
||||||
|
import re
|
||||||
|
import serial
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
logfile = os.path.expanduser("~/.goniometer.log")
|
||||||
|
logging.basicConfig(filename=logfile,level=logging.INFO)
|
||||||
|
logger = logging.getLogger()
|
||||||
|
logger.name = "goniometer"
|
||||||
|
|
||||||
|
class goniometer:
|
||||||
|
"""
|
||||||
|
This class is meant to control the goniometer probe head from Marco Braun (AXYs)
|
||||||
|
"""
|
||||||
|
def __init__(self, dev="/dev/ttyUSB0",
|
||||||
|
baudrate=38400,
|
||||||
|
timeout=1,
|
||||||
|
return_to_origin=True,
|
||||||
|
loglevel=2,
|
||||||
|
logangle=False):
|
||||||
|
"""
|
||||||
|
Upon creating an instance the goniometer will be initialised and the current angle defined as being 0.
|
||||||
|
The goniometer log can be found in `~/.goniometer.log`.
|
||||||
|
|
||||||
|
:param return_to_origin: Set to true if you want the stepper to return to the beginning when
|
||||||
|
the class instance is deleted (for example with "del" ). Default: True
|
||||||
|
:type return_to_origin: bool
|
||||||
|
:param loglevel: This sets the log level 0=DEBUG,1=INFO,2=WARNING. Default: 2
|
||||||
|
:type loglevel: int
|
||||||
|
:param logangle: Should a history of the angles be kept? The log can be accessed in the `_angle_history`
|
||||||
|
variable. Default: False
|
||||||
|
:type logangle: bool
|
||||||
|
"""
|
||||||
|
self.serial = serial.Serial(port=dev, baudrate=baudrate, timeout=timeout)
|
||||||
|
logger.setLevel([logging.DEBUG,logging.INFO,logging.WARNING][loglevel])
|
||||||
|
self.serial.readlines() # empty serial buffer
|
||||||
|
self.current_angle = 0.0
|
||||||
|
self._angle_history = []
|
||||||
|
self.initialise()
|
||||||
|
self.return_to_origin = return_to_origin
|
||||||
|
self.logangel = logangle
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
if self.return_to_origin:
|
||||||
|
logging.info("Return to origin")
|
||||||
|
self._send("g1")
|
||||||
|
self._wait()
|
||||||
|
self.serial.close()
|
||||||
|
|
||||||
|
def _send(self, string_to_send):
|
||||||
|
formatted_string = "%s>\r\n"%string_to_send
|
||||||
|
logging.debug("send: %s"%(repr(formatted_string)))
|
||||||
|
self.serial.write(formatted_string)
|
||||||
|
|
||||||
|
def _recv(self):
|
||||||
|
retstr = self.serial.readline().strip().replace("\x00","")
|
||||||
|
search_result = re.search("<(\d+)>", retstr)
|
||||||
|
if search_result != None:
|
||||||
|
angle = float(search_result.group(1))/36.0
|
||||||
|
self.current_angle = angle
|
||||||
|
if self.logangle:
|
||||||
|
self._angle_history.append(angle)
|
||||||
|
logging.debug("%.2f"%angle)
|
||||||
|
return angle
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _wait(self):
|
||||||
|
logging.debug("Wait until rotation finnished (current: %.2f)"%(self.current_angle))
|
||||||
|
time.sleep(4)
|
||||||
|
while True:
|
||||||
|
retstr = self._recv()
|
||||||
|
if not retstr:
|
||||||
|
logging.debug("Rotation finnished (current: %.2f)"%(self.current_angle))
|
||||||
|
break
|
||||||
|
|
||||||
|
def initialise(self):
|
||||||
|
logging.info("Initialize goniometer")
|
||||||
|
self._send("R000")
|
||||||
|
self.current_angle = 0.0
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
"""
|
||||||
|
Stops the current movement, not possible while doing :func:`step` and :func:`angle`
|
||||||
|
"""
|
||||||
|
logging.info("Stop goniometer")
|
||||||
|
self._send("S456")
|
||||||
|
|
||||||
|
def step(self, sample_rotation_degree=1.0):
|
||||||
|
"""
|
||||||
|
How many degrees should we step further. Negative values are not allowed, the stepper would return to 0.
|
||||||
|
A ValueError exception is raised in this case.
|
||||||
|
:param sample_rotation_degree: How many degrees to rotate. Default: 1
|
||||||
|
:type sample_rotation_degree: float
|
||||||
|
"""
|
||||||
|
if sample_rotation_degree <= 0:
|
||||||
|
raise ValueError,"This does not what you expect, would move to the start position whatever the value"
|
||||||
|
logging.info("Step goniometer: %.2f"%sample_rotation_degree)
|
||||||
|
direction = "g" if (sample_rotation_degree < 0) else "G"
|
||||||
|
sample_rotation_degree = -1*sample_rotation_degree if sample_rotation_degree < 0 else sample_rotation_degree
|
||||||
|
steps = int(sample_rotation_degree%360)
|
||||||
|
self._send("%s%i"%(direction, steps ))
|
||||||
|
self._wait()
|
||||||
|
|
||||||
|
def angle(self, angle_degree):
|
||||||
|
"""
|
||||||
|
To what angle, in degrees, should we rotate. Mod 360 is calculated for `angle_degree`.
|
||||||
|
If angle is smaller than current angle an exception will be raised.
|
||||||
|
(From the underlying :func:`step` function)
|
||||||
|
|
||||||
|
TODO: rotate either back or over to the wanted position.
|
||||||
|
|
||||||
|
:param angle_degree: position in degree
|
||||||
|
:type angle_degree: float
|
||||||
|
"""
|
||||||
|
angle_degree %= 360
|
||||||
|
delta_degree = angle_degree - self.current_angle
|
||||||
|
logging.info("Rotate tp %.2f (from %.2f, i.e. delta=%.2f)"%(angle_degree, self.current_angle, delta_degree))
|
||||||
|
self.step(sample_rotation_degree=delta_degree)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user