DBUS is a very powerful and versatile inter-process communication system used in Linux and other OSes. With KStars (>v.2.3.0), it exposes a new INDI DBUS interface in addition to the existing KStars's DBUS interface giving you a fine control over most of KStars functionality.

In this tutorial, we will learn how to perform basic control of a telescope and CCD using DBUS and Python. We will utilize an external FITS viewer fv, you can install it in Ubuntu:

sudo apt-get install ftools-fv

The complete Python script can be downloaded, to run it, simply run KStars first, then execute the script:

python indidbus.py

We begin by starting a session bus and obtaining a remote object instance to KStars/INDI interface. DBUS can introspect the interface and prints all available methods as shown below.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# You must initialize the gobject/dbus support for threading
# before doing anything.
import gobject
import os
import time

gobject.threads_init()

from dbus import glib
glib.init_threads()

# Create a session bus.
import dbus
bus = dbus.SessionBus()

# Create an object that will proxy for a particular remote object.
remote_object = bus.get_object("org.kde.kstars", # Connection name
                               "/KStars/INDI" # Object's path
                              )

# Introspection returns an XML document containing information
# about the methods supported by an interface.
print ("Introspection data:\n")
print remote_object.Introspect()

Next, we obtain a handle to the INDI interface that we will use thorough out the script to call methods and get results back.

# Get INDI interface
iface = dbus.Interface(remote_object, 'org.kde.kstars.INDI')

We define our target devices as we plan to run the Telescope and CCD simulators

myDevices = [ "indi_simulator_telescope", "indi_simulator_ccd" ]

We make our first call to the INDI interface, and we ask it to start an INDI server on port 7624 with our devices

# Start INDI devices
iface.start("7624", myDevices)

print "Waiting for INDI devices..."

# Create array for received devices
devices = []

while True:
    devices = iface.getDevices()
    if (len(devices) < len(myDevices)):
        time.sleep(1)
    else:
        break;

print "We received the following devices:"
for device in devices:
    print device

Above, we invoke the getDevices() method which returns an array of devices, and we wait until all the devices are received. Please note that devices are not necessarily received in order, i.e. telescope first followed by CCD. It can be in any order. Furthermore, I didn't implement any timeout or error checking above, please do not forget to check for errors when building DBUS scripts.

Next, we establish a connection to the telescope and CCD. Note that we utilize methods to set the value of a property, but the value is not sent to the INDI server unless we use the sendProperty method. You must be familiar with INDI properties, and in particular INDI Standard Properties before you can write any useful script.

print "Establishing connection to Telescope and CCD..."

# Set connect switch to ON to connect the devices
iface.setSwitch("Telescope Simulator", "CONNECTION", "CONNECT", "On")
# Send the switch to INDI server so that it gets processed by the driver
iface.sendProperty("Telescope Simulator", "CONNECTION")
# Same thing for CCD Simulator
iface.setSwitch("CCD Simulator", "CONNECTION", "CONNECT", "On")
iface.sendProperty("CCD Simulator", "CONNECTION")

telescopeState = "Busy"
ccdState       = "Busy"

# Wait until devices are connected
while True:
    telescopeState = iface.getPropertyState("Telescope Simulator", "CONNECTION")
    ccdState       = iface.getPropertyState("CCD Simulator", "CONNECTION")
    if (telescopeState != "Ok" or ccdState != "Ok"):
        time.sleep(1)
    else:
        break

print "Connected to Telescope and CCD is established."

Above, after we connected, we inquired about the state of the CONNECTION property. Once we established it is Ok, we can proceed.

When we command the telescope, we need to send the Epoch of Date (EOD) coordinates or JNow. The following coordinates are the approximate coordinate for Caph at the time of writing this tutorial.

print "Commanding telescope to slew to coordinates of star Caph..."

# Set Telescope RA,DEC coords in JNOW
iface.setNumber("Telescope Simulator", "EQUATORIAL_EOD_COORD", "RA", 0.166)
iface.setNumber("Telescope Simulator", "EQUATORIAL_EOD_COORD", "DEC", 59.239)
iface.sendProperty("Telescope Simulator", "EQUATORIAL_EOD_COORD")

# Wait until slew is done
telescopeState = "Busy"
while True:
    telescopeState = iface.getPropertyState("Telescope Simulator", "EQUATORIAL_EOD_COORD")
    if (telescopeState != "Ok"):
        time.sleep(1)
    else:
        break

print "Telescope slew is complete, tracking..."

As with before, we wait until the property state turns to Ok

Next, we take a 5 second exposure

print "Taking a 5 second CCD exposure..."

# Take 5 second exposure
iface.setNumber("CCD Simulator", "CCD_EXPOSURE", "CCD_EXPOSURE_VALUE", 5.0)
iface.sendProperty("CCD Simulator", "CCD_EXPOSURE")

# Wait until exposure is done
ccdState       = "Busy"
while True:
    ccdState = iface.getPropertyState("CCD Simulator", "CCD_EXPOSURE")
    if (ccdState != "Ok"):
        time.sleep(1)
    else:
        break

print "Exposure complete"

After exposure is complete, KStars stores the captured FITS image in a temporary file. We have two methods to access BLOB data. The first method is to obtain the filename where the BLOB data is currently stored. Please note that not all BLOB data are stored by KStars into files (such as video streams) and for BLOBs that are updated very frequently (> 1 Hz), the filename might already point to the next BLOB segment and might not be current. But for regular FITS images, the filename method is more than sufficient. The other DBUS method getBLOBData returns the BLOB raw data. This might be inefficient for large blobs as DBUS is not optimized to transfer large amounts of data.

Next we obtain the filename of the blob (CCD1.CCD1 is the primary CCD whereas CCD2.CCD2 is the guide CCD chip if available). We pass the filename to the external fv tool.

# Get image file name and open it in external fv tool
fileinfo = iface.getBLOBFile("CCD Simulator", "CCD1", "CCD1")
print "We received file: ", fileinfo[0], " with format ", fileinfo[1], " and size ", fileinfo[2]

print "Invoking fv tool to view the received FITS file..."
# run external fits viewer
command = "fv " + fileinfo[0]
os.system(command)

After closing the fv tool, the script continues execution and we finally send a command to shutdown the INDI server running on port 7624

print "Shutting down INDI server..."
# Stop INDI server
iface.stop("7624")

That's a very basic tutorial illustrating the capabilities of DBUS scripting with Python in KStars. If you have any questions or suggestions or scripts you'd like to share, please let us know in INDI forums!