root/trunk/papywizard/hardware/merlinOrionHardware.py

Revision 2519, 9.0 KB (checked in by fma, 11 months ago)

Added 'restoreEncoderFullCircle()' method

  • Property svn:keywords set to Id
Line 
1# -*- coding: utf-8 -*-
2
3""" Panohead remote control.
4
5License
6=======
7
8 - B{Papywizard} (U{http://www.papywizard.org}) is Copyright:
9  - (C) 2007-2011 Frédéric Mantegazza
10
11This software is governed by the B{CeCILL} license under French law and
12abiding by the rules of distribution of free software.  You can  use,
13modify and/or redistribute the software under the terms of the CeCILL
14license as circulated by CEA, CNRS and INRIA at the following URL
15U{http://www.cecill.info}.
16
17As a counterpart to the access to the source code and  rights to copy,
18modify and redistribute granted by the license, users are provided only
19with a limited warranty  and the software's author,  the holder of the
20economic rights,  and the successive licensors  have only  limited
21liability.
22
23In this respect, the user's attention is drawn to the risks associated
24with loading,  using,  modifying and/or developing or reproducing the
25software by the user in light of its specific status of free software,
26that may mean  that it is complicated to manipulate,  and  that  also
27therefore means  that it is reserved for developers  and  experienced
28professionals having in-depth computer knowledge. Users are therefore
29encouraged to load and test the software's suitability as regards their
30requirements in conditions enabling the security of their systems and/or
31data to be ensured and,  more generally, to use and operate it in the
32same conditions as regards security.
33
34The fact that you are presently reading this means that you have had
35knowledge of the CeCILL license and that you accept its terms.
36
37Module purpose
38==============
39
40Hardware
41
42Implements
43==========
44
45 - MerlinOrionHardware
46
47@author: Frédéric Mantegazza
48@copyright: (C) 2007-2011 Frédéric Mantegazza
49@license: CeCILL
50"""
51
52__revision__ = "$Id$"
53
54from PyQt4 import QtCore
55
56from papywizard.common.exception import HardwareError
57from papywizard.common.loggingServices import Logger
58from papywizard.hardware.abstractHardware import AbstractHardware
59
60ENCODER_ZERO = 0x800000
61
62
63class MerlinOrionHardware(AbstractHardware):
64    """ Merlin/Orion low-level hardware.
65    """
66    def _init(self):
67        AbstractHardware._init(self)
68        self.__encoderFullCircle = None
69
70    def __decodeAxisValue(self, strValue):
71        """ Decode value from axis.
72
73        Values (position, speed...) returned by axis are
74        32bits-encoded strings, low byte first.
75
76        @param strValue: value returned by axis
77        @type strValue: str
78
79        @return: value
80        @rtype: int
81        """
82        value = 0
83        for i in xrange(3):
84            value += eval("0x%s" % strValue[i*2:i*2+2]) * 2 ** (i * 8)
85
86        return value
87
88    def __encodeAxisValue(self, value):
89        """ Encode value for axis.
90
91        Values (position, speed...) to send to axis must be
92        32bits-encoded strings, low byte first.
93
94        @param value: value
95        @type value: int
96
97        @return: value to send to axis
98        @rtype: str
99        """
100        strHexValue = "000000%s" % hex(value)[2:]
101        strValue = strHexValue[-2:] + strHexValue[-4:-2] + strHexValue[-6:-4]
102
103        return strValue.upper()
104
105    def __encoderToAngle(self, codPos):
106        """ Convert encoder value to degres.
107
108        @param codPos: encoder position
109        @type codPos: int
110
111        @return: position, in °
112        @rtype: float
113        """
114        return (codPos - ENCODER_ZERO) * 360. / self.__encoderFullCircle
115
116    def __angleToEncoder(self, pos):
117        """ Convert degres to encoder value.
118
119        @param pos: position, in °
120        @type pos: float
121
122        @return: encoder position
123        @rtype: int
124        """
125        return int(pos * self.__encoderFullCircle / 360. + ENCODER_ZERO)
126
127    def __sendCmd(self, cmd, param=""):
128        """ Send a command to the axis.
129
130        @param cmd: command to send
131        @type cmd: str
132
133        @return: answer
134        @rtype: str
135        """
136        cmd = ":%s%d%s\r" % (cmd, self._axis, param)
137        #Logger().debug("MerlinOrionHardware.__sendCmd(): axis %d cmd=%s" % (self._axis, repr(cmd)))
138        for nbTry in xrange(self._nbRetry):
139            try:
140                answer = ""
141                self._driver.empty()
142                self._driver.write(cmd)
143                c = ''
144                while c not in ('=', '!'):
145                    c = self._driver.read(1)
146                    #Logger().debug("MerlinOrionHardware.__sendCmd(): c=%s" % repr(c))
147                if c == '!':
148                    c = self._driver.read(1) # Get error code
149                    raise IOError("Error in command %s (err=%s)" % (repr(cmd), c))
150                answer = ""
151                while True:
152                    c = self._driver.read(1)
153                    #Logger().debug("MerlinOrionHardware.__sendCmd(): c=%s" % repr(c))
154                    if c == '\r':
155                        break
156                    answer += c
157
158            except IOError:
159                Logger().exception("MerlinOrionHardware.__sendCmd")
160                Logger().warning("MerlinOrionHardware.__sendCmd(): axis %d can't sent command %s. Retrying..." % (self._axis, repr(cmd)))
161            else:
162                break
163        else:
164            raise HardwareError("axis %d can't send command %s" % (self._axis, repr(cmd)))
165        #Logger().debug("MerlinOrionHardware.__sendCmd(): axis %d ans=%s" % (self._axis, repr(answer)))
166
167        return answer
168
169    def init(self):
170        """ Init the MerlinOrion hardware.
171
172        Done only once per axis.
173        """
174        self._driver.acquireBus()
175        try:
176
177            # Stop motor
178            self.__sendCmd("L")
179
180            # Check motor?
181            self.__sendCmd("F")
182
183            # Get firmeware version
184            value = self.__sendCmd("e")
185            Logger().debug("MerlinOrionHardware.init(): firmeware version=%s" % value)
186
187            # Get encoder full circle
188            value = self.__sendCmd("a")
189            self.__encoderFullCircle = self.__encoderFullCircle_ = self.__decodeAxisValue(value)
190            Logger().debug("MerlinOrionHardware.init(): encoder full circle=%s" % hex(self.__encoderFullCircle))
191
192            # Get sidereal rate
193            value = self.__sendCmd("D")
194            Logger().debug("MerlinOrionHardware.init(): sidereal rate=%s" % hex(self.__decodeAxisValue(value)))
195
196        finally:
197            self._driver.releaseBus()
198
199    def overwriteEncoderFullCircle(self, encoderFullCircle):
200        """ Overwrite firmware value.
201        """
202        Logger().debug("MerlinOrionHardware.overwriteEncoderFullCircle(): encoderFullCircle=%x" % encoderFullCircle)
203        self.__encoderFullCircle = encoderFullCircle
204
205    def useFirmwareEncoderFullCircle(self):
206        """ Use value from firmware.
207        """
208        self.__encoderFullCircle = self.__encoderFullCircle_
209        Logger().debug("MerlinOrionHardware.useFirmwareEncoderFullCircle(): encoderFullCircle=%x" % self.__encoderFullCircle)
210
211    def read(self):
212        """ Read the axis position.
213
214        @return: axis position, in °
215        @rtype: float
216        """
217        self._driver.acquireBus()
218        try:
219            value = self.__sendCmd("j")
220        finally:
221            self._driver.releaseBus()
222        pos = self.__encoderToAngle(self.__decodeAxisValue(value))
223        return pos
224
225    def drive(self, pos):
226        """ Drive the axis.
227
228        @param pos: position to reach, in °
229        @type pos: float
230        """
231        strValue = self.__encodeAxisValue(self.__angleToEncoder(pos))
232        self._driver.acquireBus()
233        try:
234            self.__sendCmd("L")
235            self.__sendCmd("G", "00")
236            self.__sendCmd("S", strValue)
237            self.__sendCmd("J")
238        finally:
239            self._driver.releaseBus()
240
241    def stop(self):
242        """ Stop the axis.
243        """
244        self._driver.acquireBus()
245        try:
246            self.__sendCmd("L")
247        finally:
248            self._driver.releaseBus()
249
250    def startJog(self, dir_, speed):
251        """ Start the axis.
252
253        @param dir_: direction ('+', '-')
254        @type dir_: str
255
256        @param speed: speed
257        @type speed: int
258        """
259        self._driver.acquireBus()
260        try:
261            self.__sendCmd("L")
262            if dir_ == '+':
263                self.__sendCmd("G", "30")
264            elif dir_ == '-':
265                self.__sendCmd("G", "31")
266            else:
267                raise ValueError("Axis %d dir. must be in ('+', '-')" % self._axis)
268            self.__sendCmd("I", self.__encodeAxisValue(speed))
269            self.__sendCmd("J")
270        finally:
271            self._driver.releaseBus()
272
273    def getStatus(self):
274        """ Get axis status.
275
276        @return: axis status
277        @rtype: str
278        """
279        self._driver.acquireBus()
280        try:
281            return self.__sendCmd("f")
282        finally:
283            self._driver.releaseBus()
284
285    def setOutput(self, state):
286        """ Set output state.
287
288        @param state: new state of the the output
289        @type state: bool
290        """
291        self._driver.acquireBus()
292        try:
293            self.__sendCmd("O", str(int(state)))
294        finally:
295            self._driver.releaseBus()
Note: See TracBrowser for help on using the browser.