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