×

INDI Library v2.0.6 is Released (02 Feb 2024)

Bi-monthly release with minor bug fixes and improvements

Take image with python script

  • Posts: 49
  • Thank you received: 20
Good idea! I wrote a tutorial for a simple timelapse client :)

In this tutorial we create a simple timelapse capture script with Python for an INDI CCD camera.

At first we need Python wrappers for INDI from code.google.com/p/pyindi-client/ . We need the package subversion to get the source code and swig, python-dev and libindi-dev to compile it.
If you run Ubuntu or Debian just install the packages with:
sudo apt-get install subversion libindi-dev python-dev swig
If you use a Raspberry Pi just install INDI Library for Raspberry PI: indilib.org/download/category/6-raspberry-pi.html

Then follow the instructions on the website to download, compile and install the warppers:
svn co http://pyindi-client.googlecode.com/svn/trunk/swig-indi/swig-indi-python 
mkdir libindipython
cd libindipython
cmake ../swig-indi-python
make
sudo make install

Now we can test the wrappers. Open a second terminal and start an indi server with the CCD Simulator:
indiserver -v indi_simulator_ccd

Then switch to the first terminal and go to the downloaded swig-indi-python directory.
cd ../swig-indi-python/

There is the script test-indiclient.py. Start it with:
python test-indiclient.py
The script will connect to your indiserver on localhost and then it looks for connected devices and their properties. The output for CCD Simulatoir should look like this:
2015-02-20 02:39:45,993 creating an instance of PyQtIndi.IndiClient
Connecting and waiting 2secs
Server connected (localhost:7624)
2015-02-20 02:39:45,993 new device CCD Simulator
2015-02-20 02:39:45,994 new property CONNECTION for device CCD Simulator
2015-02-20 02:39:45,994 new property DRIVER_INFO for device CCD Simulator
2015-02-20 02:39:45,994 new property DEBUG for device CCD Simulator
2015-02-20 02:39:45,994 new property CONFIG_PROCESS for device CCD Simulator
2015-02-20 02:39:45,994 new property SIMULATOR_SETTINGS for device CCD Simulator
2015-02-20 02:39:45,994 new property ON_TIME_FACTOR for device CCD Simulator
List of devices
CCD Simulator
List of Device Properties
-- CCD Simulator
   > CONNECTION
       CONNECT(Connect)= Off
       DISCONNECT(Disconnect)= On
   > DRIVER_INFO
       DRIVER_NAME(Name)= CCD Simulator
       DRIVER_EXEC(Exec)= indi_simulator_ccd
       DRIVER_VERSION(Version)= 1.0
       DRIVER_INTERFACE(Interface)= 22
   > DEBUG
       ENABLE(Enable)= Off
       DISABLE(Disable)= On
   > CONFIG_PROCESS
       CONFIG_LOAD(Load)= Off
       CONFIG_SAVE(Save)= Off
       CONFIG_DEFAULT(Default)= Off
   > SIMULATOR_SETTINGS
       SIM_XRES(CCD X resolution)= 1280.0
       SIM_YRES(CCD Y resolution)= 1024.0
       SIM_XSIZE(CCD X Pixel Size)= 5.2
       SIM_YSIZE(CCD Y Pixel Size)= 5.2
       SIM_MAXVAL(CCD Maximum ADU)= 65000.0
       SIM_BIAS(CCD Bias)= 10.0
       SIM_SATURATION(Saturation Mag)= 1.0
       SIM_LIMITINGMAG(Limiting Mag)= 17.0
       SIM_NOISE(CCD Noise)= 10.0
       SIM_SKYGLOW(Sky Glow (magnitudes))= 19.5
       SIM_OAGOFFSET(Oag Offset (arcminutes))= 0.0
       SIM_POLAR(PAE (arcminutes))= 0.0
       SIM_POLARDRIFT(PAE Drift (minutes))= 0.0
   > ON_TIME_FACTOR
       1X(Actual Time)= On
       10X(10x)= Off
       100X(100x)= Off
Disconnecting
INDI server localhost/7624 disconnected.
2015-02-20 02:39:46,995 Server disconnected (exit code = 0,localhost:7624)

But how to get images from the camera? It's easy! There is already a BaseClient in test-indiclient.py script:
import sys, time, logging
import PyIndi
 
class IndiClient(PyIndi.BaseClient):
    def __init__(self):
        super(IndiClient, self).__init__()
        self.logger = logging.getLogger('PyQtIndi.IndiClient')
        self.logger.info('creating an instance of PyQtIndi.IndiClient')
    def newDevice(self, d):
        self.logger.info("new device " + d.getDeviceName())
        #self.logger.info("new device ")
    def newProperty(self, p):
        self.logger.info("new property "+ p.getName() + " for device "+ p.getDeviceName())
        #self.logger.info("new property ")
    def removeProperty(self, p):
        self.logger.info("remove property "+ p.getName() + " for device "+ p.getDeviceName())
    def newBLOB(self, bp):
        self.logger.info("new BLOB "+ bp.name.decode())
    def newSwitch(self, svp):
        self.logger.info ("new Switch "+ svp.name.decode() + " for device "+ svp.device.decode())
    def newNumber(self, nvp):
        self.logger.info("new Number "+ nvp.name.decode() + " for device "+ nvp.device.decode())
    def newText(self, tvp):
        self.logger.info("new Text "+ tvp.name.decode() + " for device "+ tvp.device.decode())
    def newLight(self, lvp):
        self.logger.info("new Light "+ lvp.name.decode() + " for device "+ lvp.device.decode())
    def newMessage(self, d, m):
        self.logger.info("new Message "+ d.messageQueue(m).decode())
    def serverConnected(self):
        print("Server connected ("+self.getHost()+":"+str(self.getPort())+")")
    def serverDisconnected(self, code):
        self.logger.info("Server disconnected (exit code = "+str(code)+","+str(self.getHost())+":"+str(self.getPort())+")")
 
logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO)

Just copy the code into a new script timelapse.py.
The client inherits from INDI BaseClient and implements the virtual functions. These functions are called when the server sends information like new numbers (newNumber), new devices (newDevice) and so on. At the moment the new values are just printed to the terminal.

To test our client, we have to connect to the server. Just add the following code after your client:
# instantiate the client
indiclient=IndiClient()
# set indi server localhost and port 7624
indiclient.setServer("localhost",7624)
# connect to indi server
print("Connecting and waiting 2secs")
if (not(indiclient.connectServer())):
     print("No indiserver running on "+indiclient.getHost()+":"+str(indiclient.getPort())+" - Try to run")
     print("  indiserver indi_simulator_telescope indi_simulator_ccd")
     sys.exit(1)
time.sleep(1)
 
# start endless loop, client works asynchron in background
while True:
	time.sleep(1)
The code connects to the server and then it prevents the program from terminating with an endless loop (the client runs in background). If the connection was successfull, you will see some new properties. Now you can connect from another client for example Ekos to your INDI server. Connect Ekos to remote (localhost, 7624) and then connect to the CCD Simulator and change some settings (set exposure, gain etc.). You will see the server answers in Ekos and in your Python client.

To start an exposure with the Python client we have to connect our CCD camera and then set the "CCD_EXPOSURE" to a value > 0.

At first we just add a member variable "device" to our client:
class IndiClient(PyIndi.BaseClient):
    device = None
Then we edit the newDevice() function. This function is called when a new device is detected. We just test if the device is the CCD Simulator and then we save a reference in our member variable "device":
    def newDevice(self, d):
        self.logger.info("new device " + d.getDeviceName())
        if d.getDeviceName() == "CCD Simulator":
		self.logger.info("Set new device CCD Simulator!")
		# save reference to the device in member variable
		self.device = d
Next step is to connect to CCD Simulator. We just wait for the CONNECTION property of CCD Simulator, connect to the device, set BLOB mode so we can get BLOBs (image data) and then we wait for the new property CCD_EXPOSURE by editing the newProperty() function:
    def newProperty(self, p):
        self.logger.info("new property "+ p.getName() + " for device "+ p.getDeviceName())
        if self.device is not None and p.getName() == "CONNECTION" and p.getDeviceName() == self.device.getDeviceName():
		self.logger.info("Got property CONNECTION for CCD Simulator!")
		# connect to device
		self.connectDevice(self.device.getDeviceName())
		# set BLOB mode to BLOB_ALSO
		self.setBLOBMode(1, self.device.getDeviceName(), None)
        if p.getName() == "CCD_EXPOSURE":
		# take first exposure
		self.takeExposure()
If CCD_EXPOSURE property was found, we call takeExposure(). in takeExposure() we just get the CCD_EXPOSURE property, set a new exposure time and send it back to the server/client:
    def takeExposure(self):
        self.logger.info("<<<<<<<< Exposure >>>>>>>>>")
	# get current exposure time
        exp = self.device.getNumber("CCD_EXPOSURE")
	# set exposure time to 5 seconds
        exp[0].value = 5
	# send new exposure time to server/device
        self.sendNewNumber(exp)
Now the CCD starts a new exposure and the newNumber() function is called every second. When the exposure finished, the newBlob() function with our image data is called. We just extract the image data from the BLOB and save it to a fits file ("frame.fit") on our harddisk. Then we start a new exposure:
    def newBLOB(self, bp):
        self.logger.info("new BLOB "+ bp.name.decode())
	# get image data
	img = bp.getblobdata()
	import cStringIO
	# write image data to StringIO buffer
	blobfile = cStringIO.StringIO(img)
	# open a file and save buffer to disk
	with open("frame.fit", "wb") as f:
		f.write(blobfile.getvalue())
	# start new exposure for timelapse images!
	self.takeExposure()

That's it!
At the moment we overwrite the fits file with every new image but you can change the name to current datetime or whatever to create a timelapse sequence.
The following user(s) said Thank You: Jasem Mutlaq, Gonzothegreat, dolguldur, surena
9 years 1 month ago #3235

Please Log in or Create an account to join the conversation.

  • Posts: 15
  • Thank you received: 0
Sorry, my question might reveal some ignorance in this topic, please be patient with me :) :

So, do I understand it correctly, if all the above python code goes into the same script (timelapse.py)? And when running it, it will generate a new image-file continously until I kill it? The only thing I need to do is to specify my type of ccd device?

It then would be fairly easy to change the script into just taking one frame? I am looking for a way of having a cronjob running a script that takes one frame of say 5 sec exposure and ftp'ing it to my server once or twice each minute...
8 years 11 months ago #3792

Please Log in or Create an account to join the conversation.

  • Posts: 49
  • Thank you received: 20
Yes, that's right!

I modified the timelapse.py to a singleframe.py. Instead of starting a new capture it disconnects from the server and stops the script after the first image (look at newBLOB method)

Edit: Attachment doesn't work, so here is the python-code for singleframe.py:
import sys, time, logging
import PyIndi
 
DEVICE_NAME = "QHY CCD QHY5-0-M-"
 
class IndiClient(PyIndi.BaseClient):
 
    device = None
 
    def __init__(self):
        super(IndiClient, self).__init__()
        self.logger = logging.getLogger('PyQtIndi.IndiClient')
        self.logger.info('creating an instance of PyQtIndi.IndiClient')
    def newDevice(self, d):
		#self.logger.info("new device " + d.getDeviceName())
		if d.getDeviceName() == DEVICE_NAME:
		    self.logger.info("Set new device %s!" % DEVICE_NAME)
		    # save reference to the device in member variable
		    self.device = d
    def newProperty(self, p):
		#self.logger.info("new property "+ p.getName() + " for device "+ p.getDeviceName())
		if self.device is not None and p.getName() == "CONNECTION" and p.getDeviceName() == self.device.getDeviceName():
		    self.logger.info("Got property CONNECTION for %s!" % DEVICE_NAME)
		    # connect to device
		    self.connectDevice(self.device.getDeviceName())
		    # set BLOB mode to BLOB_ALSO
		    self.setBLOBMode(1, self.device.getDeviceName(), None)
		if p.getName() == "CCD_EXPOSURE":
		    # take first exposure
		    self.takeExposure()
    def removeProperty(self, p):
        #self.logger.info("remove property "+ p.getName() + " for device "+ p.getDeviceName())
	pass
    def newBLOB(self, bp):
	self.logger.info("new BLOB "+ bp.name.decode())
	# get image data
	img = bp.getblobdata()
	import cStringIO
	# write image data to StringIO buffer
	blobfile = cStringIO.StringIO(img)
	# open a file and save buffer to disk
	with open("frame.fit", "wb") as f:
	    f.write(blobfile.getvalue())
	# start new exposure for timelapse images!
	# self.takeExposure()
	# disconnect from server
	self.disconnectServer()
    def newSwitch(self, svp):
        self.logger.info ("new Switch "+ svp.name.decode() + " for device "+ svp.device.decode())
    def newNumber(self, nvp):
        self.logger.info("new Number "+ nvp.name.decode() + " for device "+ nvp.device.decode())
    def newText(self, tvp):
        self.logger.info("new Text "+ tvp.name.decode() + " for device "+ tvp.device.decode())
    def newLight(self, lvp):
        self.logger.info("new Light "+ lvp.name.decode() + " for device "+ lvp.device.decode())
    def newMessage(self, d, m):
        #self.logger.info("new Message "+ d.messageQueue(m).decode())
	pass
    def serverConnected(self):
        print("Server connected ("+self.getHost()+":"+str(self.getPort())+")")
	self.connected = True
    def serverDisconnected(self, code):
        self.logger.info("Server disconnected (exit code = "+str(code)+","+str(self.getHost())+":"+str(self.getPort())+")")
	# set connected to False
	self.connected = False
    def takeExposure(self):
	self.logger.info("<<<<<<<< Exposure >>>>>>>>>")
	#get current exposure time
	exp = self.device.getNumber("CCD_EXPOSURE")
	# set exposure time to 5 seconds
	exp[0].value = 5
	# send new exposure time to server/device
	self.sendNewNumber(exp)
 
logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO)
 
if __name__ == '__main__':
	# instantiate the client
	indiclient=IndiClient()
	# set indi server localhost and port 7624
	indiclient.setServer("localhost",7624)
	# connect to indi server
	print("Connecting and waiting 2secs")
	if (not(indiclient.connectServer())):
		 print("No indiserver running on "+indiclient.getHost()+":"+str(indiclient.getPort())+" - Try to run")
		 print("  indiserver indi_simulator_telescope indi_simulator_ccd")
		 sys.exit(1)
	time.sleep(1)
 
	# start endless loop, client works asynchron in background, loop stops after disconnect
	while indiclient.connected:
		time.sleep(1)
Last edit: 8 years 11 months ago by fehlfarbe.
8 years 11 months ago #3802

Please Log in or Create an account to join the conversation.

  • Posts: 15
  • Thank you received: 0
Thank you! That helped me a little further.

However, it seams that the program stops when waiting for the "CCD_EXPOSURE" property.

I have chosen my Device_name = "ASI CCD"
The scripts just stops and nothing happens after:
...
new property SIMULATION for device ASI CCD
new property CONFIG PROCESS for device ASI CCD
new property Available Cams for device ASI CCD

Any suggestions? When running the test-indiclient.py script everything seems ok compared to the output shown above...
8 years 11 months ago #3811

Please Log in or Create an account to join the conversation.

  • Posts: 49
  • Thank you received: 20
I only tried the script with INDI CCD Simulator and a QHY5. Maybe the property CCD_EXPOSURE has another name for ASI CCD? Can you post the whole output of test-indiclient.py?
8 years 11 months ago #3813

Please Log in or Create an account to join the conversation.

  • Posts: 15
  • Thank you received: 0
Here it is:
pi@raspberrypi ~/swig-indi-python $ python test-indiclient.py
2015-03-31 19:33:59,207 creating an instance of PyQtIndi.IndiClient
Connecting and waiting 2secs
Server connected (localhost:7624)
2015-03-31 19:33:59,227 new device ASI CCD
2015-03-31 19:33:59,236 new property CONNECTION for device ASI CCD
2015-03-31 19:33:59,258 new property DRIVER_INFO for device ASI CCD
2015-03-31 19:33:59,265 new property DEBUG for device ASI CCD
2015-03-31 19:33:59,270 new property SIMULATION for device ASI CCD
2015-03-31 19:33:59,277 new property CONFIG_PROCESS for device ASI CCD
2015-03-31 19:33:59,283 new property Available Cams for device ASI CCD
List of devices
ASI CCD
List of Device Properties
-- ASI CCD
> CONNECTION
CONNECT(Connect)= Off
DISCONNECT(Disconnect)= On
> DRIVER_INFO
DRIVER_NAME(Name)= ASI CCD
DRIVER_EXEC(Exec)= indi_asi_ccd
DRIVER_VERSION(Version)= 1.0
DRIVER_INTERFACE(Interface)= 6
> DEBUG
ENABLE(Enable)= Off
DISABLE(Disable)= On
> SIMULATION
ENABLE(Enable)= Off
DISABLE(Disable)= On
> CONFIG_PROCESS
CONFIG_LOAD(Load)= Off
CONFIG_SAVE(Save)= Off
CONFIG_DEFAULT(Default)= Off
> Available Cams
ZWO ASI120MC(ZWO ASI120MC)= On
Disconnecting
INDI server localhost/7624 disconnected.
2015-03-31 19:34:00,249 Server disconnected (exit code = 0,localhost:7624)
pi@raspberrypi ~/swig-indi-python $
Last edit: 8 years 11 months ago by Magnar G. Johnsen.
8 years 11 months ago #3814

Please Log in or Create an account to join the conversation.

  • Posts: 49
  • Thank you received: 20
Ok and the whole output of singleframe.py?
8 years 11 months ago #3816

Please Log in or Create an account to join the conversation.

  • Posts: 15
  • Thank you received: 0
pi@raspberrypi ~ $ python singleframe.py
2015-03-31 19:56:42,497 creating an instance of PyQtIndi.IndiClient
Connecting and waiting 2secs
Server connected (localhost:7624)
2015-03-31 19:56:42,516 Set new device ASI CCD!
2015-03-31 19:56:42,526 new property CONNECTION for device ASI CCD
2015-03-31 19:56:42,547 Got property CONNECTION for ASI CCD!
2015-03-31 19:56:42,556 new property DRIVER_INFO for device ASI CCD
2015-03-31 19:56:42,561 new property DEBUG for device ASI CCD
2015-03-31 19:56:42,567 new property SIMULATION for device ASI CCD
2015-03-31 19:56:42,577 new property CONFIG_PROCESS for device ASI CCD
2015-03-31 19:56:42,581 new property Available Cams for device ASI CCD
8 years 11 months ago #3817

Please Log in or Create an account to join the conversation.

  • Posts: 49
  • Thank you received: 20
Do you habe the latest drivers and does the camera work with KStars/Ekos? (maybe this problem: indilib.org/forum/ccds-dslrs/548-zwo-asi...on.html?limitstart=0)

I'm not really well versed in INDI but maybe you have to choose an avaiable camera after connecting to ASI CCD device.
Last edit: 8 years 11 months ago by fehlfarbe.
8 years 11 months ago #3818

Please Log in or Create an account to join the conversation.

  • Posts: 15
  • Thank you received: 0
I followed the tutorial on the indi web page for installing the driver, so I assume it is the newest one. I just installed Kstars, and did not get it to work there either, but that might have something to do with my lack of experience with astronomy software... I will investigate this further.

Thanks so far :-)
8 years 11 months ago #3819

Please Log in or Create an account to join the conversation.

magnargj, I think you're using INDI packages for Raspberry PI which are using older ASI CCD driver. Try to compile INDI on your PI from SVN to use the latest driver which is known to work OK on the Raspberry PI.
8 years 11 months ago #3822

Please Log in or Create an account to join the conversation.

  • Posts: 15
  • Thank you received: 0
8 years 11 months ago #3824

Please Log in or Create an account to join the conversation.

Time to create page: 1.608 seconds