Page 1 of 2

Control Panogear from Stellarium?

PostPosted: Sat Mar 09, 2013 8:03 pm
by aaronpriest
I have a Panogear with bluetooth module and it works fine from my laptop via Papywizard. Is there anyway to use it with Stellarium over bluetooth? I'd like to track the Pan-STARRS comet with a telephoto this weekend and shoot it. I don't have the GOTO controller. I've been unsuccessful getting Stellarium to communicate with the Merlin/Panogear.

PostPosted: Sat Mar 09, 2013 8:28 pm
by mediavets
aaronpriest wrote:I have a Panogear with bluetooth module and it works fine from my laptop via Papywizard. Is there anyway to use it with Stellarium over bluetooth? I'd like to track the Pan-STARRS comet with a telephoto this weekend and shoot it. I don't have the GOTO controller. I've been unsuccessful getting Stellarium to communicate with the Merlin/Panogear.

I can't see that Stellarium has anything to do with controlling astrotelescope mounts.

Is this the software you are referring to?:

http://www.stellarium.org/wiki/index.php/Introduction

Perhaps you need something like this software?:

http://www.ursaminor.hu/ursaminor_introduction_en.html

PostPosted: Sat Mar 09, 2013 9:32 pm
by aaronpriest
Sure Stellarium does! Been a plugin for several versions now:
http://www.stellarium.org/wiki/index.php/Telescope_Control_plug-in

UrsaMinor does more if you have an autoguider of course, I was just looking for something quick and dirty using hardware I already had available for tonight. ;-)

PostPosted: Sat Mar 09, 2013 9:42 pm
by aaronpriest
BTW, that test program on the UrsaMinor page worked fine with my Panogear on COM4. Unfortunately telescope control is purposely disabled in their demo and there's no way to buy it online without contacting dealers that are not located in my country. No way I'd have something working by tonight with them. Haha

PostPosted: Sun Mar 10, 2013 8:04 pm
by fma38
Stellarium can natively drive a Panogear with the GOTO hand controller... Without it, it needs a little server. The protocole is described here:

https://stellarium.svn.sourceforge.net/svnroot/stellarium/trunk/telescope_server/stellarium_telescope_protocol.txt

PostPosted: Tue Mar 12, 2013 11:24 am
by fma38
Here is a python script which act as stellarium server to drive the Panogear head. I only tested it through the EthernetDriver, using the simulator which comes with Papywizard. Let me know if it works.

Note: I used the serial drive, so you first need to pair with the bluetooth module from the system, then give the correct virtual serial port at the top of the script (you can use Windows syntax on that OS: COM1, COM4...), and launch it. Then, connect from Stellarium (I used the port 9999).

Code: Select all
# -*- coding: utf-8 -*-

""" v0.1
"""

import struct
import SocketServer
import threading
import socket

import serial

# Stellarium server params
HOST, PORT = "localhost", 9999
PACKET_SIZE = 20

# Merlin/Orion params
ENCODER_ZERO = 0x800000

# Driver params
SERIAL_PORT = "/dev/ttyS0"
SERIAL_BAUDRATE = 9600


class AbstractDriver(object):
    """ Base (abstract) class for bus drivers.
    """
    def __init__(self):
        """ Init the driver.
        """
        self._lock = threading.RLock()

    def init(self):
        """ Init the connection.
        """
        raise NotImplementedError("AbstractDriver.init() must be overidden")

    def shutdown(self):
        """ Shutdown the connection.
        """
        raise NotImplementedError("AbstractDriver.shutdown() must be overidden")

    def acquireBus(self):
        """ Acquire and lock the bus.
        """
        #print "AbstractDriver.acquireBus()"
        self._lock.acquire()

    def releaseBus(self):
        """ Unlock and release the bus.
        """
        #print "AbstractDriver.releaseBus()"
        self._lock.release()

    def setTimeout(self, timeout):
        """ Set the timeout delay.

        @param timeout: timeout delay, in s
        @type timeout: float
        """
        raise NotImplementedError("AbstractDriver.setTimeout() must be overloaded")

    def empty(self):
        """ Empty buffer.
        """
        raise NotImplementedError("AbstractDriver.empty() must be overloaded")

    def write(self, data):
        """ Write data.

        @param data: data to write
        @type data: str

        @return: size of data written
        @rtype: int
        """
        raise NotImplementedError("AbstractDriver.write() must be overloaded")

    def read(self, size):
        """ Read data.

        @param size: size of the data to read
        @type size: int

        @return: read data
        @rtype: str
        """
        raise NotImplementedError("AbstractDriver.read() must be overloaded")


class SerialDriver(AbstractDriver):
    """ Driver for serial connection.
    """
    def init(self):
        try:
            self._serial = serial.Serial(port=SERIAL_PORT, baudrate=SERIAL_BAUDRATE, timeout=3)
            self.empty()
        except:
            print "SerialDriver._init()"
            raise

    def shutdown(self):
        self._serial.close()

    def setRTS(self, level):
        """ Set RTS signal to specified level.

        @param level: level to set to RTS signal
        @type level: int
        """
        self._serial.setRTS(level)

    def setDTR(self, level):
        """ Set DTR signal to specified level.

        @param level: level to set to DTR signal
        @type level: int
        """
        self._serial.setDTR(level)

    def setTimeout(self, timeout):
        self._serial.timeout = timeout

    def empty(self):
        if hasattr(self._serial, "inWaiting"):
            self.read(self._serial.inWaiting())

    def write(self, data):
        #print "SerialDriver.write(): data=%s" % repr(data)
        size = self._serial.write(data)
        return size

    def read(self, size):
        data = self._serial.read(size)
        #print "SerialDriver.read(): data=%s" % repr(data)
        if size and not data:
            raise IOError("Timeout while reading on serial bus (size=%d, data=%s)" % (size, repr(data)))
        else:
            return data


class EthernetDriver(AbstractDriver):
    """ Driver for TCP ethernet connection.
    """
    def init(self):
        print "EthernetDriver._init(): trying to connect to %s:%d..." % ("localhost", 7165)
        try:
            #import time
            #time.sleep(3)
            self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self._sock.connect(("localhost", 7165))
            self._sock.settimeout(3)
            self.empty()
        except Exception, msg:
            print "EthernetDriver.init()"
            raise Exception(msg)
        else:
            print "EthernetDriver._init(): successfully connected to %s:%d" % ("localhost", 7165)

    def shutdown(self):
        self._sock.close()

    def setTimeout(self, timeout):
        self._sock.settimeout(timeout)

    def empty(self):
        pass

    def write(self, data):
        #Logger().debug("EthernetDriver.write(): data=%s" % repr(data))
        try:
            size = self._sock.send(data)
            #if size != len(data) + 2:
                #raise IOError("Failed to send data on ethernet")
            #else:
                #return size
            return size
        except socket.timeout:
            raise IOError("Timeout while writing on ethernet")
        except socket.error, msg:
            raise IOError(msg)

    def read(self, size):
        try:
            data = self._sock.recv(size)
            #print "EthernetDriver.read(): data=%s" % repr(data)
            if not data:
                raise IOError("Connection lost")
            else:
                return data
        except socket.timeout:
            raise IOError("Timeout while reading on ethernet bus")
        except socket.error, msg:
            raise IOError(msg)


class MerlinOrionHardware(object):
    """ Merlin/Orion low-level hardware.
    """
    def __init__(self, axis, driver):
        """
        """
        self._axis = axis
        self._driver = driver

        self._encoderFullCircle = None

    def _decodeAxisValue(self, strValue):
        """ Decode value from axis.

        Values (position, speed...) returned by axis are
        32bits-encoded strings, low byte first.

        @param strValue: value returned by axis
        @type strValue: str

        @return: value
        @rtype: int
        """
        value = 0
        for i in xrange(3):
            value += eval("0x%s" % strValue[i*2:i*2+2]) * 2 ** (i * 8)

        return value

    def _encodeAxisValue(self, value):
        """ Encode value for axis.

        Values (position, speed...) to send to axis must be
        32bits-encoded strings, low byte first.

        @param value: value
        @type value: int

        @return: value to send to axis
        @rtype: str
        """
        strHexValue = "000000%s" % hex(value)[2:]
        strValue = strHexValue[-2:] + strHexValue[-4:-2] + strHexValue[-6:-4]

        return strValue.upper()

    def _encoderToAngle(self, codPos):
        """ Convert encoder value to degres.

        @param codPos: encoder position
        @type codPos: int

        @return: position, in °
        @rtype: float
        """
        return (codPos - ENCODER_ZERO) * 360. / self._encoderFullCircle

    def _angleToEncoder(self, pos):
        """ Convert degres to encoder value.

        @param pos: position, in °
        @type pos: float

        @return: encoder position
        @rtype: int
        """
        return int(pos * self._encoderFullCircle / 360. + ENCODER_ZERO)

    def _sendCmd(self, cmd, param=""):
        """ Send a command to the axis.

        @param cmd: command to send
        @type cmd: str

        @return: answer
        @rtype: str
        """
        cmd = ":%s%d%s\r" % (cmd, self._axis, param)
        #print "MerlinOrionHardware._sendCmd(): axis %d cmd=%s" % (self._axis, repr(cmd))
        for nbTry in xrange(3):
            try:
                answer = ""
                self._driver.empty()
                self._driver.write(cmd)
                c = ''
                while c not in ('=', '!'):
                    c = self._driver.read(1)
                    #print "MerlinOrionHardware._sendCmd(): c=%s" % repr(c)
                if c == '!':
                    c = self._driver.read(1) # Get error code
                    raise IOError("Error in command %s (err=%s)" % (repr(cmd), c))
                answer = ""
                while True:
                    c = self._driver.read(1)
                    #print "MerlinOrionHardware._sendCmd(): c=%s" % repr(c)
                    if c == '\r':
                        break
                    answer += c

            except IOError:
                print "MerlinOrionHardware._sendCmd(): axis %d can't sent command %s. Retrying..." % (self._axis, repr(cmd))
            else:
                break
        else:
            raise Exception("axis %d can't send command %s" % (self._axis, repr(cmd)))
        #print "MerlinOrionHardware._sendCmd(): axis %d ans=%s" % (self._axis, repr(answer))

        return answer

    def init(self):
        """ Init the MerlinOrion hardware.

        Done only once per axis.
        """
        self._driver.acquireBus()
        try:

            # Stop motor
            self._sendCmd("L")

            # Check motor?
            self._sendCmd("F")

            # Get firmeware version
            value = self._sendCmd("e")
            print "MerlinOrionHardware.init(): firmeware version=%s" % value

            # Get encoder full circle
            value = self._sendCmd("a")
            self._encoderFullCircle = self._encoderFullCircle_ = self._decodeAxisValue(value)
            print "MerlinOrionHardware.init(): encoder full circle=%s" % hex(self._encoderFullCircle)

            # Get sidereal rate
            value = self._sendCmd("D")
            print "MerlinOrionHardware.init(): sidereal rate=%s" % hex(self._decodeAxisValue(value))

        finally:
            self._driver.releaseBus()

    def overwriteEncoderFullCircle(self, encoderFullCircle):
        """ Overwrite firmware value.
        """
        print "MerlinOrionHardware.overwriteEncoderFullCircle(): encoderFullCircle=%x" % encoderFullCircle
        self._encoderFullCircle = encoderFullCircle

    def useFirmwareEncoderFullCircle(self):
        """ Use value from firmware.
        """
        self._encoderFullCircle = self._encoderFullCircle_
        print "MerlinOrionHardware.useFirmwareEncoderFullCircle(): encoderFullCircle=%x" % self._encoderFullCircle

    def read(self):
        """ Read the axis position.

        @return: axis position, in °
        @rtype: float
        """
        self._driver.acquireBus()
        try:
            value = self._sendCmd("j")
        finally:
            self._driver.releaseBus()
        pos = self._encoderToAngle(self._decodeAxisValue(value))
        return pos

    def drive(self, pos):
        """ Drive the axis.

        @param pos: position to reach, in °
        @type pos: float
        """
        strValue = self._encodeAxisValue(self._angleToEncoder(pos))
        self._driver.acquireBus()
        try:
            self._sendCmd("L")
            self._sendCmd("G", "00")
            self._sendCmd("S", strValue)
            self._sendCmd("J")
        finally:
            self._driver.releaseBus()

    def stop(self):
        """ Stop the axis.
        """
        self._driver.acquireBus()
        try:
            self._sendCmd("L")
        finally:
            self._driver.releaseBus()

    def startJog(self, dir_, speed):
        """ Start the axis.

        @param dir_: direction ('+', '-')
        @type dir_: str

        @param speed: speed
        @type speed: int
        """
        self._driver.acquireBus()
        try:
            self._sendCmd("L")
            if dir_ == '+':
                self._sendCmd("G", "30")
            elif dir_ == '-':
                self._sendCmd("G", "31")
            else:
                raise ValueError("Axis %d dir. must be in ('+', '-')" % self._axis)
            self._sendCmd("I", self._encodeAxisValue(speed))
            self._sendCmd("J")
        finally:
            self._driver.releaseBus()

    def getStatus(self):
        """ Get axis status.

        @return: axis status
        @rtype: str
        """
        self._driver.acquireBus()
        try:
            return self._sendCmd("f")
        finally:
            self._driver.releaseBus()

    def setOutput(self, state):
        """ Set output state.

        @param state: new state of the the output
        @type state: bool
        """
        self._driver.acquireBus()
        try:
            self._sendCmd("O", str(int(state)))
        finally:
            self._driver.releaseBus()


class IncomingPacket(object):
    """ Incoming packet from Stellarium.
    """
    def __init__(self, message):
        """ Create the packet
        """
        self.message = message

        length, type_, time_, ra, dec =  struct.unpack("<HHQIi", message)
        self.length = length
        self.type_ = type_
        self.time_ = time_
        self.ra = ra * 90. / 0x40000000
        self.dec = dec * 90. / 0x40000000


class MyTCPHandler(SocketServer.BaseRequestHandler):
    """ The RequestHandler class for our server.
    """
    def handle(self):

        # self.request is the TCP socket connected to the client
        self.packet = IncomingPacket(self.request.recv(PACKET_SIZE).strip())
        print "MyTCPHandler.handle(): {} wrote:".format(self.client_address[0])
        print "MyTCPHandler.handle(): %s" % repr(self.packet.message)
        print "MyTCPHandler.handle(): time=%d, RA=%.2f, DEC=%.2f" % (self.packet.time_, self.packet.ra, self.packet.dec)
        yaw.drive(self.packet.dec)
        pitch.drive(self.packet.ra)
        while yaw.getStatus()[1] != '0' and pitch.getStatus()[1] != '0':
            time.sleep(0.1)
        print "MyTCPHandler.handle(): done"

        # just send back the same data
        #self.request.sendall(struct.pack("<HHQIii", length+4, type_, time_, ra+50000, dec-50000, 0))


class MyServer(SocketServer.TCPServer):
    allow_reuse_address = True


#driver = EthernetDriver()
driver = SerialDriver()
driver.init()

yaw = MerlinOrionHardware(1, driver)
yaw.init()
pitch = MerlinOrionHardware(2, driver)
pitch.init()

if __name__ == "__main__":

    # Create the server, binding to localhost on port 9999
    server = MyServer((HOST, PORT), MyTCPHandler)

    # Activate the server; this will keep running until you
    # interrupt the program with Ctrl-C
    server.serve_forever()

PostPosted: Tue Mar 12, 2013 2:37 pm
by fma38
Here is a new version, with a few fixes:

Code: Select all
# -*- coding: utf-8 -*-

""" v0.2
"""

import struct
import SocketServer
import threading
import socket
import time

import serial

# Stellarium server params
HOST, PORT = "localhost", 9999
PACKET_SIZE = 20

# Merlin/Orion params
ENCODER_ZERO = 0x800000

# Driver params
SERIAL_PORT = "/dev/ttyS0"
SERIAL_BAUDRATE = 9600


class AbstractDriver(object):
    """ Base (abstract) class for bus drivers.
    """
    def __init__(self):
        """ Init the driver.
        """
        self._lock = threading.RLock()

    def init(self):
        """ Init the connection.
        """
        raise NotImplementedError("AbstractDriver.init() must be overidden")

    def shutdown(self):
        """ Shutdown the connection.
        """
        raise NotImplementedError("AbstractDriver.shutdown() must be overidden")

    def acquireBus(self):
        """ Acquire and lock the bus.
        """
        #print "AbstractDriver.acquireBus()"
        self._lock.acquire()

    def releaseBus(self):
        """ Unlock and release the bus.
        """
        #print "AbstractDriver.releaseBus()"
        self._lock.release()

    def setTimeout(self, timeout):
        """ Set the timeout delay.

        @param timeout: timeout delay, in s
        @type timeout: float
        """
        raise NotImplementedError("AbstractDriver.setTimeout() must be overloaded")

    def empty(self):
        """ Empty buffer.
        """
        raise NotImplementedError("AbstractDriver.empty() must be overloaded")

    def write(self, data):
        """ Write data.

        @param data: data to write
        @type data: str

        @return: size of data written
        @rtype: int
        """
        raise NotImplementedError("AbstractDriver.write() must be overloaded")

    def read(self, size):
        """ Read data.

        @param size: size of the data to read
        @type size: int

        @return: read data
        @rtype: str
        """
        raise NotImplementedError("AbstractDriver.read() must be overloaded")


class SerialDriver(AbstractDriver):
    """ Driver for serial connection.
    """
    def init(self):
        try:
            self._serial = serial.Serial(port=SERIAL_PORT, baudrate=SERIAL_BAUDRATE, timeout=3)
            self.empty()
        except:
            print "SerialDriver._init()"
            raise

    def shutdown(self):
        self._serial.close()

    def setRTS(self, level):
        """ Set RTS signal to specified level.

        @param level: level to set to RTS signal
        @type level: int
        """
        self._serial.setRTS(level)

    def setDTR(self, level):
        """ Set DTR signal to specified level.

        @param level: level to set to DTR signal
        @type level: int
        """
        self._serial.setDTR(level)

    def setTimeout(self, timeout):
        self._serial.timeout = timeout

    def empty(self):
        if hasattr(self._serial, "inWaiting"):
            self.read(self._serial.inWaiting())

    def write(self, data):
        #print "SerialDriver.write(): data=%s" % repr(data)
        size = self._serial.write(data)
        return size

    def read(self, size):
        data = self._serial.read(size)
        #print "SerialDriver.read(): data=%s" % repr(data)
        if size and not data:
            raise IOError("Timeout while reading on serial bus (size=%d, data=%s)" % (size, repr(data)))
        else:
            return data


class EthernetDriver(AbstractDriver):
    """ Driver for TCP ethernet connection.
    """
    def init(self):
        print "EthernetDriver._init(): trying to connect to %s:%d..." % ("localhost", 7165)
        try:
            #import time
            #time.sleep(3)
            self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self._sock.connect(("localhost", 7165))
            self._sock.settimeout(3)
            self.empty()
        except Exception, msg:
            print "EthernetDriver.init()"
            raise Exception(msg)
        else:
            print "EthernetDriver._init(): successfully connected to %s:%d" % ("localhost", 7165)

    def shutdown(self):
        self._sock.close()

    def setTimeout(self, timeout):
        self._sock.settimeout(timeout)

    def empty(self):
        pass

    def write(self, data):
        #Logger().debug("EthernetDriver.write(): data=%s" % repr(data))
        try:
            size = self._sock.send(data)
            #if size != len(data) + 2:
                #raise IOError("Failed to send data on ethernet")
            #else:
                #return size
            return size
        except socket.timeout:
            raise IOError("Timeout while writing on ethernet")
        except socket.error, msg:
            raise IOError(msg)

    def read(self, size):
        try:
            data = self._sock.recv(size)
            #print "EthernetDriver.read(): data=%s" % repr(data)
            if not data:
                raise IOError("Connection lost")
            else:
                return data
        except socket.timeout:
            raise IOError("Timeout while reading on ethernet bus")
        except socket.error, msg:
            raise IOError(msg)


class MerlinOrionHardware(object):
    """ Merlin/Orion low-level hardware.
    """
    def __init__(self, axis, driver):
        """
        """
        self._axis = axis
        self._driver = driver

        self._encoderFullCircle = None

    def _decodeAxisValue(self, strValue):
        """ Decode value from axis.

        Values (position, speed...) returned by axis are
        32bits-encoded strings, low byte first.

        @param strValue: value returned by axis
        @type strValue: str

        @return: value
        @rtype: int
        """
        value = 0
        for i in xrange(3):
            value += eval("0x%s" % strValue[i*2:i*2+2]) * 2 ** (i * 8)

        return value

    def _encodeAxisValue(self, value):
        """ Encode value for axis.

        Values (position, speed...) to send to axis must be
        32bits-encoded strings, low byte first.

        @param value: value
        @type value: int

        @return: value to send to axis
        @rtype: str
        """
        strHexValue = "000000%s" % hex(value)[2:]
        strValue = strHexValue[-2:] + strHexValue[-4:-2] + strHexValue[-6:-4]

        return strValue.upper()

    def _encoderToAngle(self, codPos):
        """ Convert encoder value to degres.

        @param codPos: encoder position
        @type codPos: int

        @return: position, in °
        @rtype: float
        """
        return (codPos - ENCODER_ZERO) * 360. / self._encoderFullCircle

    def _angleToEncoder(self, pos):
        """ Convert degres to encoder value.

        @param pos: position, in °
        @type pos: float

        @return: encoder position
        @rtype: int
        """
        return int(pos * self._encoderFullCircle / 360. + ENCODER_ZERO)

    def _sendCmd(self, cmd, param=""):
        """ Send a command to the axis.

        @param cmd: command to send
        @type cmd: str

        @return: answer
        @rtype: str
        """
        cmd = ":%s%d%s\r" % (cmd, self._axis, param)
        #print "MerlinOrionHardware._sendCmd(): axis %d cmd=%s" % (self._axis, repr(cmd))
        for nbTry in xrange(3):
            try:
                answer = ""
                self._driver.empty()
                self._driver.write(cmd)
                c = ''
                while c not in ('=', '!'):
                    c = self._driver.read(1)
                    #print "MerlinOrionHardware._sendCmd(): c=%s" % repr(c)
                if c == '!':
                    c = self._driver.read(1) # Get error code
                    raise IOError("Error in command %s (err=%s)" % (repr(cmd), c))
                answer = ""
                while True:
                    c = self._driver.read(1)
                    #print "MerlinOrionHardware._sendCmd(): c=%s" % repr(c)
                    if c == '\r':
                        break
                    answer += c

            except IOError:
                print "MerlinOrionHardware._sendCmd(): axis %d can't sent command %s. Retrying..." % (self._axis, repr(cmd))
            else:
                break
        else:
            raise Exception("axis %d can't send command %s" % (self._axis, repr(cmd)))
        #print "MerlinOrionHardware._sendCmd(): axis %d ans=%s" % (self._axis, repr(answer))

        return answer

    def init(self):
        """ Init the MerlinOrion hardware.

        Done only once per axis.
        """
        self._driver.acquireBus()
        try:

            # Stop motor
            self._sendCmd("L")

            # Check motor?
            self._sendCmd("F")

            # Get firmeware version
            value = self._sendCmd("e")
            print "MerlinOrionHardware.init(): firmeware version=%s" % value

            # Get encoder full circle
            value = self._sendCmd("a")
            self._encoderFullCircle = self._encoderFullCircle_ = self._decodeAxisValue(value)
            print "MerlinOrionHardware.init(): encoder full circle=%s" % hex(self._encoderFullCircle)

            # Get sidereal rate
            value = self._sendCmd("D")
            print "MerlinOrionHardware.init(): sidereal rate=%s" % hex(self._decodeAxisValue(value))

        finally:
            self._driver.releaseBus()

    def overwriteEncoderFullCircle(self, encoderFullCircle):
        """ Overwrite firmware value.
        """
        print "MerlinOrionHardware.overwriteEncoderFullCircle(): encoderFullCircle=%x" % encoderFullCircle
        self._encoderFullCircle = encoderFullCircle

    def useFirmwareEncoderFullCircle(self):
        """ Use value from firmware.
        """
        self._encoderFullCircle = self._encoderFullCircle_
        print "MerlinOrionHardware.useFirmwareEncoderFullCircle(): encoderFullCircle=%x" % self._encoderFullCircle

    def read(self):
        """ Read the axis position.

        @return: axis position, in °
        @rtype: float
        """
        self._driver.acquireBus()
        try:
            value = self._sendCmd("j")
        finally:
            self._driver.releaseBus()
        pos = self._encoderToAngle(self._decodeAxisValue(value))
        return pos

    def drive(self, pos):
        """ Drive the axis.

        @param pos: position to reach, in °
        @type pos: float
        """
        strValue = self._encodeAxisValue(self._angleToEncoder(pos))
        self._driver.acquireBus()
        try:
            self._sendCmd("L")
            self._sendCmd("G", "00")
            self._sendCmd("S", strValue)
            self._sendCmd("J")
        finally:
            self._driver.releaseBus()

    def stop(self):
        """ Stop the axis.
        """
        self._driver.acquireBus()
        try:
            self._sendCmd("L")
        finally:
            self._driver.releaseBus()

    def startJog(self, dir_, speed):
        """ Start the axis.

        @param dir_: direction ('+', '-')
        @type dir_: str

        @param speed: speed
        @type speed: int
        """
        self._driver.acquireBus()
        try:
            self._sendCmd("L")
            if dir_ == '+':
                self._sendCmd("G", "30")
            elif dir_ == '-':
                self._sendCmd("G", "31")
            else:
                raise ValueError("Axis %d dir. must be in ('+', '-')" % self._axis)
            self._sendCmd("I", self._encodeAxisValue(speed))
            self._sendCmd("J")
        finally:
            self._driver.releaseBus()

    def getStatus(self):
        """ Get axis status.

        @return: axis status
        @rtype: str
        """
        self._driver.acquireBus()
        try:
            return self._sendCmd("f")
        finally:
            self._driver.releaseBus()

    def setOutput(self, state):
        """ Set output state.

        @param state: new state of the the output
        @type state: bool
        """
        self._driver.acquireBus()
        try:
            self._sendCmd("O", str(int(state)))
        finally:
            self._driver.releaseBus()


class IncomingPacket(object):
    """ Incoming packet from Stellarium.
    """
    def __init__(self, message):
        """ Create the packet
        """
        self.message = message

        length, type_, time_, ra, dec =  struct.unpack("<HHQIi", message)
        self.length = length
        self.type_ = type_
        self.time_ = time_
        self.ra = ra * 90. / 0x40000000
        self.dec = dec * 90. / 0x40000000


class MyTCPHandler(SocketServer.BaseRequestHandler):
    """ The RequestHandler class for our server.
    """
    def handle(self):
        while True:

            # self.request is the TCP socket connected to the client
            self.packet = IncomingPacket(self.request.recv(PACKET_SIZE).strip())
            print "MyTCPHandler.handle(): {} wrote:".format(self.client_address[0])
            print "MyTCPHandler.handle(): %s" % repr(self.packet.message)
            print "MyTCPHandler.handle(): length=%d, time=%d, RA=%.2f, DEC=%.2f" % (self.packet.length, self.packet.time_, self.packet.ra, self.packet.dec)
            self.server.yaw.drive(self.packet.dec)
            self.server.pitch.drive(self.packet.ra)
            while self.server.yaw.getStatus()[1] != '0' and self.server.pitch.getStatus()[1] != '0':
                time.sleep(0.1)
            print "MyTCPHandler.handle(): done"

            # just send back the reached position
            self.request.sendall(struct.pack("<HHQIii", self.packet.length+4, self.packet.type_, time.time(), self.packet.ra, self.packet.dec, 0))


class MyServer(SocketServer.TCPServer):
    allow_reuse_address = True



def main():

    #driver = EthernetDriver()
    driver = SerialDriver()
    driver.init()

    # Create the server, binding to localhost on port 9999
    server = MyServer((HOST, PORT), MyTCPHandler)

    server.yaw = MerlinOrionHardware(1, driver)
    server.yaw.init()
    server.pitch = MerlinOrionHardware(2, driver)
    server.pitch.init()

    # Activate the server; this will keep running until you
    # interrupt the program with Ctrl-C
    server.serve_forever()


if __name__ == "__main__":
    main()

PostPosted: Thu Apr 25, 2013 8:06 am
by dim
I have also a Panogear & Merlin (thank you!) on a Macbook with ubuntu 12.04... It works perfectly! And i read this very interresting topic. So i tried to use the Panogear & bluetooth with Stellarium 0.12.1.. When i launch the server, it make screen copy!

PostPosted: Thu Apr 25, 2013 8:13 am
by fma38
!!!! Did you install a python interpreter?

Does anyone alse tried my script to drive a Merlin from Stellarium?

PostPosted: Thu Apr 25, 2013 8:24 am
by dim
I think so! Python & libpython (2.7, 3.2, 3.3) are installed.

I don't know! I discover last night the possibility to drive Merlin with Stellarium!

PostPosted: Thu Apr 25, 2013 8:32 am
by fma38
How do you start the server? Better try from a console...

PostPosted: Thu Apr 25, 2013 8:47 am
by dim
In the console & the message: "line 4 $ v0.2 \n': command not found"

PostPosted: Thu Apr 25, 2013 9:32 am
by fma38
I think you are trying to run the python script using the shell interpreter!!!

As I didn't put a shebang at the start of the file, you need to start it with:

$ python <script>

Or add this to the first line:

#!/usr/bin/python

and make the file executable, and launch it with:

$ ./<script>

PostPosted: Thu Apr 25, 2013 9:46 am
by dim
Thanks for patience! I'm new with python and not a programmer. I will try it this night! I will tell you about it.

dimitri

PostPosted: Thu Apr 25, 2013 9:54 am
by fma38
No problem. Thanks for your feedback.

PostPosted: Thu Apr 25, 2013 5:28 pm
by Mateusz Malinowski
fma38 wrote:Does anyone alse tried my script to drive a Merlin from Stellarium?

Hi
am I doing something wrong?
1
- installed python 3.3.1 64bit http://www.python.org/ftp/python/3.3.1/python-3.3.1.amd64.msi
- create s.py with code http://www.kolor.com/forum/p112973-2013-03-12-15-37-56#p112973
- run "Python (command line)" - drag and drop s.py -> enter
- Syntax error: invalid syntax

2
- installed python 3.3.1 64bit http://www.python.org/ftp/python/3.3.1/python-3.3.1.amd64.msi
- installed winpython http://code.google.com/p/winpython/downloads/detail?name=WinPython-64bit-3.3.0.0beta2.exe
- run "WinPython Interpreter.exe" - drag and drop s.py -> enter
- Syntax error: invalid syntax

PostPosted: Thu Apr 25, 2013 6:03 pm
by dim
So! I can't find the correct virtual serial port used by bluetooth... I create one (sudo rfcomm connect rfcomm4 00:12:6F:21:AE:9B, mac address to find with hcitool scan), set it in the script & launch the server...It works! Launch Stellarium & connect to the Merlin... Connected. Send a star position... No problem.
Great! Good job! thanks a lot! Frédéric


dimitri

PostPosted: Thu Apr 25, 2013 9:54 pm
by fma38
Good :)

PostPosted: Thu Apr 25, 2013 9:54 pm
by fma38
Mateusz, try with python 2.x...

PostPosted: Fri Apr 26, 2013 9:28 am
by Mateusz Malinowski
Same syntax error
(WinPython-64bit-2.7.3.3; python 64bit 2.7.4)

PostPosted: Fri Apr 26, 2013 9:45 am
by fma38
Please, give the entire traceback...

Also try to run the script from IDLE, the python IDE.

- Right-click on the script, and choose 'Edit with IDLE'
- Run the script by pressing F5

PostPosted: Fri Apr 26, 2013 3:19 pm
by Mateusz Malinowski
Done as you wrote. Now have
Code: Select all
Traceback (most recent call last):
  File "C:\Users\MT\Desktop\s.py", line 12, in <module>
    import serial
ImportError: No module named serial
>>>

PostPosted: Fri Apr 26, 2013 3:28 pm
by fma38
You need to install this module :

http://pyserial.sourceforge.net

;)

PostPosted: Fri Apr 26, 2013 3:54 pm
by Mateusz Malinowski
Sorry, it's hard to understand pythhon if never used it before. I'm not sure I succesfully installed it (http://pyserial.sourceforge.net/pyserial.html#installation)

Code: Select all
Traceback (most recent call last):
  File "C:\Users\MT\Desktop\s.py", line 15, in <module>
    HOST, PORT = "localhost", COM7
NameError: name 'COM7' is not defined

PostPosted: Fri Apr 26, 2013 4:56 pm
by fma38
Code: Select all
# Stellarium server params
HOST, PORT = "localhost", 9999  <<<<<<<<< do not change anything here...
PACKET_SIZE = 20

# Merlin/Orion params
ENCODER_ZERO = 0x800000

# Driver params
SERIAL_PORT = "/dev/ttyS0"  <<<<<<<<<<<<<< change your Merlin serial port here
SERIAL_BAUDRATE = 9600